/* * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. * * Use of this source code is governed by a BSD-style license * that can be found in the LICENSE file in the root of the source * tree. An additional intellectual property rights grant can be found * in the file PATENTS. All contributing project authors may * be found in the AUTHORS file in the root of the source tree. */ /****************************************************************** iLBC Speech Coder ANSI-C Source Code WebRtcIlbcfix_EnhancerInterface.c ******************************************************************/ #include "defines.h" #include "constants.h" #include "xcorr_coef.h" #include "enhancer.h" #include "hp_output.h" /*----------------------------------------------------------------* * interface for enhancer *---------------------------------------------------------------*/ int WebRtcIlbcfix_EnhancerInterface( /* (o) Estimated lag in end of in[] */ WebRtc_Word16 *out, /* (o) enhanced signal */ WebRtc_Word16 *in, /* (i) unenhanced signal */ iLBC_Dec_Inst_t *iLBCdec_inst /* (i) buffers etc */ ){ int iblock; int lag=20, tlag=20; int inLen=iLBCdec_inst->blockl+120; WebRtc_Word16 scale, scale1, plc_blockl; WebRtc_Word16 *enh_buf, *enh_period; WebRtc_Word32 tmp1, tmp2, max, new_blocks; WebRtc_Word16 *enh_bufPtr1; int i, k; WebRtc_Word16 EnChange; WebRtc_Word16 SqrtEnChange; WebRtc_Word16 inc; WebRtc_Word16 win; WebRtc_Word16 *tmpW16ptr; WebRtc_Word16 startPos; WebRtc_Word16 *plc_pred; WebRtc_Word16 *target, *regressor; WebRtc_Word16 max16; int shifts; WebRtc_Word32 ener; WebRtc_Word16 enerSh; WebRtc_Word16 corrSh; WebRtc_Word16 ind, sh; WebRtc_Word16 start, stop; /* Stack based */ WebRtc_Word16 totsh[3]; WebRtc_Word16 downsampled[(BLOCKL_MAX+120)>>1]; /* length 180 */ WebRtc_Word32 corr32[50]; WebRtc_Word32 corrmax[3]; WebRtc_Word16 corr16[3]; WebRtc_Word16 en16[3]; WebRtc_Word16 lagmax[3]; plc_pred = downsampled; /* Reuse memory since plc_pred[ENH_BLOCKL] and downsampled are non overlapping */ enh_buf=iLBCdec_inst->enh_buf; enh_period=iLBCdec_inst->enh_period; /* Copy in the new data into the enhancer buffer */ WEBRTC_SPL_MEMMOVE_W16(enh_buf, &enh_buf[iLBCdec_inst->blockl], ENH_BUFL-iLBCdec_inst->blockl); WEBRTC_SPL_MEMCPY_W16(&enh_buf[ENH_BUFL-iLBCdec_inst->blockl], in, iLBCdec_inst->blockl); /* Set variables that are dependent on frame size */ if (iLBCdec_inst->mode==30) { plc_blockl=ENH_BLOCKL; new_blocks=3; startPos=320; /* Start position for enhancement (640-new_blocks*ENH_BLOCKL-80) */ } else { plc_blockl=40; new_blocks=2; startPos=440; /* Start position for enhancement (640-new_blocks*ENH_BLOCKL-40) */ } /* Update the pitch prediction for each enhancer block, move the old ones */ WEBRTC_SPL_MEMMOVE_W16(enh_period, &enh_period[new_blocks], (ENH_NBLOCKS_TOT-new_blocks)); k=WebRtcSpl_DownsampleFast( enh_buf+ENH_BUFL-inLen, /* Input samples */ (WebRtc_Word16)(inLen+ENH_BUFL_FILTEROVERHEAD), downsampled, (WebRtc_Word16)WEBRTC_SPL_RSHIFT_W16(inLen, 1), (WebRtc_Word16*)WebRtcIlbcfix_kLpFiltCoefs, /* Coefficients in Q12 */ FILTERORDER_DS_PLUS1, /* Length of filter (order-1) */ FACTOR_DS, DELAY_DS); /* Estimate the pitch in the down sampled domain. */ for(iblock = 0; iblock totsh[i]) { sh = WEBRTC_SPL_MIN(31, totsh[ind]-totsh[i]); if ( WEBRTC_SPL_MUL_16_16(corr16[ind], en16[i]) < WEBRTC_SPL_MUL_16_16_RSFT(corr16[i], en16[ind], sh)) { ind = i; } } else { sh = WEBRTC_SPL_MIN(31, totsh[i]-totsh[ind]); if (WEBRTC_SPL_MUL_16_16_RSFT(corr16[ind], en16[i], sh) < WEBRTC_SPL_MUL_16_16(corr16[i], en16[ind])) { ind = i; } } } lag = lagmax[ind] + 10; /* Store the estimated lag in the non-downsampled domain */ enh_period[ENH_NBLOCKS_TOT-new_blocks+iblock] = (WebRtc_Word16)WEBRTC_SPL_MUL_16_16(lag, 8); /* Store the estimated lag for backward PLC */ if (iLBCdec_inst->prev_enh_pl==1) { if (!iblock) { tlag = WEBRTC_SPL_MUL_16_16(lag, 2); } } else { if (iblock==1) { tlag = WEBRTC_SPL_MUL_16_16(lag, 2); } } lag = WEBRTC_SPL_MUL_16_16(lag, 2); } if ((iLBCdec_inst->prev_enh_pl==1)||(iLBCdec_inst->prev_enh_pl==2)) { /* Calculate the best lag of the new frame This is used to interpolate backwards and mix with the PLC'd data */ /* references */ target=in; regressor=in+tlag-1; /* scaling */ max16=WebRtcSpl_MaxAbsValueW16(regressor, (WebRtc_Word16)(plc_blockl+3-1)); if (max16>5000) shifts=2; else shifts=0; /* compute cross correlation */ WebRtcSpl_CrossCorrelation(corr32, target, regressor, plc_blockl, 3, (WebRtc_Word16)shifts, 1); /* find lag */ lag=WebRtcSpl_MaxIndexW32(corr32, 3); lag+=tlag-1; /* Copy the backward PLC to plc_pred */ if (iLBCdec_inst->prev_enh_pl==1) { if (lag>plc_blockl) { WEBRTC_SPL_MEMCPY_W16(plc_pred, &in[lag-plc_blockl], plc_blockl); } else { WEBRTC_SPL_MEMCPY_W16(&plc_pred[plc_blockl-lag], in, lag); WEBRTC_SPL_MEMCPY_W16(plc_pred, &enh_buf[ENH_BUFL-iLBCdec_inst->blockl-plc_blockl+lag], (plc_blockl-lag)); } } else { int pos; pos = plc_blockl; while (lagprev_enh_pl==1) { /* limit energy change if energy in backward PLC is more than 4 times higher than the forward PLC, then reduce the energy in the backward PLC vector: sample 1...len-16 set energy of the to 4 times forward PLC sample len-15..len interpolate between 4 times fw PLC and bw PLC energy Note: Compared to floating point code there is a slight change, the window is 16 samples long instead of 10 samples to simplify the calculations */ max=WebRtcSpl_MaxAbsValueW16(&enh_buf[ENH_BUFL-iLBCdec_inst->blockl-plc_blockl], plc_blockl); max16=WebRtcSpl_MaxAbsValueW16(plc_pred, plc_blockl); max = WEBRTC_SPL_MAX(max, max16); scale=22-(WebRtc_Word16)WebRtcSpl_NormW32(max); scale=WEBRTC_SPL_MAX(scale,0); tmp2 = WebRtcSpl_DotProductWithScale(&enh_buf[ENH_BUFL-iLBCdec_inst->blockl-plc_blockl], &enh_buf[ENH_BUFL-iLBCdec_inst->blockl-plc_blockl], plc_blockl, scale); tmp1 = WebRtcSpl_DotProductWithScale(plc_pred, plc_pred, plc_blockl, scale); /* Check the energy difference */ if ((tmp1>0)&&((tmp1>>2)>tmp2)) { /* EnChange is now guaranteed to be <0.5 Calculate EnChange=tmp2/tmp1 in Q16 */ scale1=(WebRtc_Word16)WebRtcSpl_NormW32(tmp1); tmp1=WEBRTC_SPL_SHIFT_W32(tmp1, (scale1-16)); /* using 15 bits */ tmp2=WEBRTC_SPL_SHIFT_W32(tmp2, (scale1)); EnChange = (WebRtc_Word16)WebRtcSpl_DivW32W16(tmp2, (WebRtc_Word16)tmp1); /* Calculate the Sqrt of the energy in Q15 ((14+16)/2) */ SqrtEnChange = (WebRtc_Word16)WebRtcSpl_Sqrt(WEBRTC_SPL_LSHIFT_W32((WebRtc_Word32)EnChange, 14)); /* Multiply first part of vector with 2*SqrtEnChange */ WebRtcSpl_ScaleVector(plc_pred, plc_pred, SqrtEnChange, (WebRtc_Word16)(plc_blockl-16), 14); /* Calculate increase parameter for window part (16 last samples) */ inc=(2048-WEBRTC_SPL_RSHIFT_W16(SqrtEnChange, 3)); /* (1-2*SqrtEnChange)/16 in Q15 */ win=0; tmpW16ptr=&plc_pred[plc_blockl-16]; for (i=16;i>0;i--) { (*tmpW16ptr)=(WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT((*tmpW16ptr), (SqrtEnChange+(win>>1)), 14); /* multiply by (2.0*SqrtEnChange+win) */ win += inc; tmpW16ptr++; } } /* Make the linear interpolation between the forward PLC'd data and the backward PLC'd data (from the new frame) */ if (plc_blockl==40) { inc=400; /* 1/41 in Q14 */ } else { /* plc_blockl==80 */ inc=202; /* 1/81 in Q14 */ } win=0; enh_bufPtr1=&enh_buf[ENH_BUFL-1-iLBCdec_inst->blockl]; for (i=0; iblockl-plc_blockl]; WEBRTC_SPL_MEMCPY_W16(enh_bufPtr1, plc_pred, plc_blockl); /* Clear fileter memory */ WebRtcSpl_MemSetW16(iLBCdec_inst->syntMem, 0, LPC_FILTERORDER); WebRtcSpl_MemSetW16(iLBCdec_inst->hpimemy, 0, 4); WebRtcSpl_MemSetW16(iLBCdec_inst->hpimemx, 0, 2); /* Initialize filter memory by filtering through 2 lags */ WEBRTC_SPL_MEMCPY_W16(&synt[-LPC_FILTERORDER], iLBCdec_inst->syntMem, LPC_FILTERORDER); WebRtcSpl_FilterARFastQ12( enh_bufPtr1, synt, &iLBCdec_inst->old_syntdenum[(iLBCdec_inst->nsub-1)*(LPC_FILTERORDER+1)], LPC_FILTERORDER+1, (WebRtc_Word16)lag); WEBRTC_SPL_MEMCPY_W16(&synt[-LPC_FILTERORDER], &synt[lag-LPC_FILTERORDER], LPC_FILTERORDER); WebRtcIlbcfix_HpOutput(synt, (WebRtc_Word16*)WebRtcIlbcfix_kHpOutCoefs, iLBCdec_inst->hpimemy, iLBCdec_inst->hpimemx, (WebRtc_Word16)lag); WebRtcSpl_FilterARFastQ12( enh_bufPtr1, synt, &iLBCdec_inst->old_syntdenum[(iLBCdec_inst->nsub-1)*(LPC_FILTERORDER+1)], LPC_FILTERORDER+1, (WebRtc_Word16)lag); WEBRTC_SPL_MEMCPY_W16(iLBCdec_inst->syntMem, &synt[lag-LPC_FILTERORDER], LPC_FILTERORDER); WebRtcIlbcfix_HpOutput(synt, (WebRtc_Word16*)WebRtcIlbcfix_kHpOutCoefs, iLBCdec_inst->hpimemy, iLBCdec_inst->hpimemx, (WebRtc_Word16)lag); } } /* Perform enhancement block by block */ for (iblock = 0; iblock