/* * 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_DoThePlc.c ******************************************************************/ #include "modules/audio_coding/codecs/ilbc/do_plc.h" #include "modules/audio_coding/codecs/ilbc/bw_expand.h" #include "modules/audio_coding/codecs/ilbc/comp_corr.h" #include "modules/audio_coding/codecs/ilbc/constants.h" #include "modules/audio_coding/codecs/ilbc/defines.h" /*----------------------------------------------------------------* * Packet loss concealment routine. Conceals a residual signal * and LP parameters. If no packet loss, update state. *---------------------------------------------------------------*/ void WebRtcIlbcfix_DoThePlc( int16_t *PLCresidual, /* (o) concealed residual */ int16_t *PLClpc, /* (o) concealed LP parameters */ int16_t PLI, /* (i) packet loss indicator 0 - no PL, 1 = PL */ int16_t *decresidual, /* (i) decoded residual */ int16_t *lpc, /* (i) decoded LPC (only used for no PL) */ size_t inlag, /* (i) pitch lag */ IlbcDecoder *iLBCdec_inst /* (i/o) decoder instance */ ){ size_t i; int32_t cross, ener, cross_comp, ener_comp = 0; int32_t measure, maxMeasure, energy; int32_t noise_energy_threshold_30dB; int16_t max, crossSquareMax, crossSquare; size_t j, lag, randlag; int16_t tmp1, tmp2; int16_t shift1, shift2, shift3, shiftMax; int16_t scale3; size_t corrLen; int32_t tmpW32, tmp2W32; int16_t use_gain; int16_t tot_gain; int16_t max_perSquare; int16_t scale1, scale2; int16_t totscale; int32_t nom; int16_t denom; int16_t pitchfact; size_t use_lag; int ind; int16_t randvec[BLOCKL_MAX]; /* Packet Loss */ if (PLI == 1) { (*iLBCdec_inst).consPLICount += 1; /* if previous frame not lost, determine pitch pred. gain */ if (iLBCdec_inst->prevPLI != 1) { /* Maximum 60 samples are correlated, preserve as high accuracy as possible without getting overflow */ max = WebRtcSpl_MaxAbsValueW16((*iLBCdec_inst).prevResidual, iLBCdec_inst->blockl); scale3 = (WebRtcSpl_GetSizeInBits(max)<<1) - 25; if (scale3 < 0) { scale3 = 0; } /* Store scale for use when interpolating between the * concealment and the received packet */ iLBCdec_inst->prevScale = scale3; /* Search around the previous lag +/-3 to find the best pitch period */ lag = inlag - 3; /* Guard against getting outside the frame */ corrLen = (size_t)WEBRTC_SPL_MIN(60, iLBCdec_inst->blockl-(inlag+3)); WebRtcIlbcfix_CompCorr( &cross, &ener, iLBCdec_inst->prevResidual, lag, iLBCdec_inst->blockl, corrLen, scale3); /* Normalize and store cross^2 and the number of shifts */ shiftMax = WebRtcSpl_GetSizeInBits(WEBRTC_SPL_ABS_W32(cross))-15; crossSquareMax = (int16_t)(( (int16_t)WEBRTC_SPL_SHIFT_W32(cross, -shiftMax) * (int16_t)WEBRTC_SPL_SHIFT_W32(cross, -shiftMax)) >> 15); for (j=inlag-2;j<=inlag+3;j++) { WebRtcIlbcfix_CompCorr( &cross_comp, &ener_comp, iLBCdec_inst->prevResidual, j, iLBCdec_inst->blockl, corrLen, scale3); /* Use the criteria (corr*corr)/energy to compare if this lag is better or not. To avoid the division, do a cross multiplication */ shift1 = WebRtcSpl_GetSizeInBits(WEBRTC_SPL_ABS_W32(cross_comp))-15; crossSquare = (int16_t)(( (int16_t)WEBRTC_SPL_SHIFT_W32(cross_comp, -shift1) * (int16_t)WEBRTC_SPL_SHIFT_W32(cross_comp, -shift1)) >> 15); shift2 = WebRtcSpl_GetSizeInBits(ener)-15; measure = (int16_t)WEBRTC_SPL_SHIFT_W32(ener, -shift2) * crossSquare; shift3 = WebRtcSpl_GetSizeInBits(ener_comp)-15; maxMeasure = (int16_t)WEBRTC_SPL_SHIFT_W32(ener_comp, -shift3) * crossSquareMax; /* Calculate shift value, so that the two measures can be put in the same Q domain */ if(2 * shiftMax + shift3 > 2 * shift1 + shift2) { tmp1 = WEBRTC_SPL_MIN(31, 2 * shiftMax + shift3 - 2 * shift1 - shift2); tmp2 = 0; } else { tmp1 = 0; tmp2 = WEBRTC_SPL_MIN(31, 2 * shift1 + shift2 - 2 * shiftMax - shift3); } if ((measure>>tmp1) > (maxMeasure>>tmp2)) { /* New lag is better => record lag, measure and domain */ lag = j; crossSquareMax = crossSquare; cross = cross_comp; shiftMax = shift1; ener = ener_comp; } } /* Calculate the periodicity for the lag with the maximum correlation. Definition of the periodicity: abs(corr(vec1, vec2))/(sqrt(energy(vec1))*sqrt(energy(vec2))) Work in the Square domain to simplify the calculations max_perSquare is less than 1 (in Q15) */ tmp2W32=WebRtcSpl_DotProductWithScale(&iLBCdec_inst->prevResidual[iLBCdec_inst->blockl-corrLen], &iLBCdec_inst->prevResidual[iLBCdec_inst->blockl-corrLen], corrLen, scale3); if ((tmp2W32>0)&&(ener_comp>0)) { /* norm energies to int16_t, compute the product of the energies and use the upper int16_t as the denominator */ scale1=(int16_t)WebRtcSpl_NormW32(tmp2W32)-16; tmp1=(int16_t)WEBRTC_SPL_SHIFT_W32(tmp2W32, scale1); scale2=(int16_t)WebRtcSpl_NormW32(ener)-16; tmp2=(int16_t)WEBRTC_SPL_SHIFT_W32(ener, scale2); denom = (int16_t)((tmp1 * tmp2) >> 16); /* in Q(scale1+scale2-16) */ /* Square the cross correlation and norm it such that max_perSquare will be in Q15 after the division */ totscale = scale1+scale2-1; tmp1 = (int16_t)WEBRTC_SPL_SHIFT_W32(cross, (totscale>>1)); tmp2 = (int16_t)WEBRTC_SPL_SHIFT_W32(cross, totscale-(totscale>>1)); nom = tmp1 * tmp2; max_perSquare = (int16_t)WebRtcSpl_DivW32W16(nom, denom); } else { max_perSquare = 0; } } /* previous frame lost, use recorded lag and gain */ else { lag = iLBCdec_inst->prevLag; max_perSquare = iLBCdec_inst->perSquare; } /* Attenuate signal and scale down pitch pred gain if several frames lost consecutively */ use_gain = 32767; /* 1.0 in Q15 */ if (iLBCdec_inst->consPLICount*iLBCdec_inst->blockl>320) { use_gain = 29491; /* 0.9 in Q15 */ } else if (iLBCdec_inst->consPLICount*iLBCdec_inst->blockl>640) { use_gain = 22938; /* 0.7 in Q15 */ } else if (iLBCdec_inst->consPLICount*iLBCdec_inst->blockl>960) { use_gain = 16384; /* 0.5 in Q15 */ } else if (iLBCdec_inst->consPLICount*iLBCdec_inst->blockl>1280) { use_gain = 0; /* 0.0 in Q15 */ } /* Compute mixing factor of picth repeatition and noise: for max_per>0.7 set periodicity to 1.0 0.47868) { /* periodicity > 0.7 (0.7^4=0.2401 in Q15) */ pitchfact = 32767; } else if (max_perSquare>839) { /* 0.4 < periodicity < 0.7 (0.4^4=0.0256 in Q15) */ /* find best index and interpolate from that */ ind = 5; while ((max_perSquare0)) { ind--; } /* pitch fact is approximated by first order */ tmpW32 = (int32_t)WebRtcIlbcfix_kPlcPitchFact[ind] + ((WebRtcIlbcfix_kPlcPfSlope[ind] * (max_perSquare - WebRtcIlbcfix_kPlcPerSqr[ind])) >> 11); pitchfact = (int16_t)WEBRTC_SPL_MIN(tmpW32, 32767); /* guard against overflow */ } else { /* periodicity < 0.4 */ pitchfact = 0; } /* avoid repetition of same pitch cycle (buzzyness) */ use_lag = lag; if (lag<80) { use_lag = 2*lag; } /* compute concealed residual */ noise_energy_threshold_30dB = (int32_t)iLBCdec_inst->blockl * 900; energy = 0; for (i=0; iblockl; i++) { /* noise component - 52 < randlagFIX < 117 */ iLBCdec_inst->seed = (int16_t)(iLBCdec_inst->seed * 31821 + 13849); randlag = 53 + (iLBCdec_inst->seed & 63); if (randlag > i) { randvec[i] = iLBCdec_inst->prevResidual[iLBCdec_inst->blockl + i - randlag]; } else { randvec[i] = iLBCdec_inst->prevResidual[i - randlag]; } /* pitch repeatition component */ if (use_lag > i) { PLCresidual[i] = iLBCdec_inst->prevResidual[iLBCdec_inst->blockl + i - use_lag]; } else { PLCresidual[i] = PLCresidual[i - use_lag]; } /* Attinuate total gain for each 10 ms */ if (i<80) { tot_gain=use_gain; } else if (i<160) { tot_gain = (int16_t)((31130 * use_gain) >> 15); /* 0.95*use_gain */ } else { tot_gain = (int16_t)((29491 * use_gain) >> 15); /* 0.9*use_gain */ } /* mix noise and pitch repeatition */ PLCresidual[i] = (int16_t)((tot_gain * ((pitchfact * PLCresidual[i] + (32767 - pitchfact) * randvec[i] + 16384) >> 15)) >> 15); /* Compute energy until threshold for noise energy is reached */ if (energy < noise_energy_threshold_30dB) { energy += PLCresidual[i] * PLCresidual[i]; } } /* less than 30 dB, use only noise */ if (energy < noise_energy_threshold_30dB) { for (i=0; iblockl; i++) { PLCresidual[i] = randvec[i]; } } /* use the old LPC */ WEBRTC_SPL_MEMCPY_W16(PLClpc, (*iLBCdec_inst).prevLpc, LPC_FILTERORDER+1); /* Update state in case there are multiple frame losses */ iLBCdec_inst->prevLag = lag; iLBCdec_inst->perSquare = max_perSquare; } /* no packet loss, copy input */ else { WEBRTC_SPL_MEMCPY_W16(PLCresidual, decresidual, iLBCdec_inst->blockl); WEBRTC_SPL_MEMCPY_W16(PLClpc, lpc, (LPC_FILTERORDER+1)); iLBCdec_inst->consPLICount = 0; } /* update state */ iLBCdec_inst->prevPLI = PLI; WEBRTC_SPL_MEMCPY_W16(iLBCdec_inst->prevLpc, PLClpc, (LPC_FILTERORDER+1)); WEBRTC_SPL_MEMCPY_W16(iLBCdec_inst->prevResidual, PLCresidual, iLBCdec_inst->blockl); return; }