   
   /******************************************************************
   
       iLBC Speech Coder ANSI-C Source Code
   
       doCPLC.c 
   
       Copyright (C) The Internet Society (2004). 
       All Rights Reserved.
   
   ******************************************************************/
   
   #include <math.h>
   #include <string.h>
   #include <stdio.h>
   
   #include "iLBC_define.h"
   
   /*----------------------------------------------------------------*
    *  Compute cross correlation and pitch gain for pitch prediction
    *  of last subframe at given lag.
    *---------------------------------------------------------------*/
   
   void compCorr(
       float *cc,      /* (o) cross correlation coefficient */
       float *gc,      /* (o) gain */
       float *pm,
       float *buffer,  /* (i) signal buffer */
       int lag,    /* (i) pitch lag */
       int bLen,       /* (i) length of buffer */
       int sRange      /* (i) correlation search length */
   ){
   
   
       int i;
       float ftmp1, ftmp2, ftmp3;
   
       /* Guard against getting outside buffer */
       if ((bLen-sRange-lag)<0) {
           sRange=bLen-lag;
       }
   
       ftmp1 = 0.0;
       ftmp2 = 0.0;
       ftmp3 = 0.0;
       for (i=0; i<sRange; i++) {
           ftmp1 += buffer[bLen-sRange+i] *
               buffer[bLen-sRange+i-lag];
           ftmp2 += buffer[bLen-sRange+i-lag] * 
                   buffer[bLen-sRange+i-lag];
           ftmp3 += buffer[bLen-sRange+i] * 
                   buffer[bLen-sRange+i];
       }
   
       if (ftmp2 > 0.0) {
           *cc = ftmp1*ftmp1/ftmp2;
           *gc = (float)fabs(ftmp1/ftmp2);
           *pm=(float)fabs(ftmp1)/
               ((float)sqrt(ftmp2)*(float)sqrt(ftmp3));
       }
       else {
           *cc = 0.0;
           *gc = 0.0;
           *pm=0.0;
       }
   }
   
   /*----------------------------------------------------------------*
    *  Packet loss concealment routine. Conceals a residual signal
    *  and LP parameters. If no packet loss, update state.
    *---------------------------------------------------------------*/
   
   void doThePLC(
       float *PLCresidual, /* (o) concealed residual */ 
       float *PLClpc,      /* (o) concealed LP parameters */  
       int PLI,        /* (i) packet loss indicator 
                                  0 - no PL, 1 = PL */ 
       float *decresidual, /* (i) decoded residual */
       float *lpc,         /* (i) decoded LPC (only used for no PL) */
       int inlag,          /* (i) pitch lag */
       iLBC_Dec_Inst_t *iLBCdec_inst 
                           /* (i/o) decoder instance */
   ){
       int lag=20, randlag;
       float gain, maxcc;
       float use_gain;
       float gain_comp, maxcc_comp, per, max_per;
       int i, pick, use_lag;
   
   
       float ftmp, randvec[BLOCKL_MAX], pitchfact, energy;
               
       /* Packet Loss */
   
       if (PLI == 1) {
           
           iLBCdec_inst->consPLICount += 1;
           
           /* if previous frame not lost, 
              determine pitch pred. gain */
           
           if (iLBCdec_inst->prevPLI != 1) {
   
               /* Search around the previous lag to find the 
                  best pitch period */
               
               lag=inlag-3;
               compCorr(&maxcc, &gain, &max_per, 
                   iLBCdec_inst->prevResidual,
                   lag, iLBCdec_inst->blockl, 60);
               for (i=inlag-2;i<=inlag+3;i++) {
                   compCorr(&maxcc_comp, &gain_comp, &per,
                       iLBCdec_inst->prevResidual,
                       i, iLBCdec_inst->blockl, 60);
                   
                   if (maxcc_comp>maxcc) {
                       maxcc=maxcc_comp;
                       gain=gain_comp;
                       lag=i;
                       max_per=per;
                   }
               }
               
           }
   
           /* previous frame lost, use recorded lag and periodicity */
   
           else {
               lag=iLBCdec_inst->prevLag;
               max_per=iLBCdec_inst->per;
           }
           
           /* downscaling */
   
           use_gain=1.0;
           if (iLBCdec_inst->consPLICount*iLBCdec_inst->blockl>320)
               use_gain=(float)0.9;
           else if (iLBCdec_inst->consPLICount*
                           iLBCdec_inst->blockl>2*320)
               use_gain=(float)0.7;
           else if (iLBCdec_inst->consPLICount*
                           iLBCdec_inst->blockl>3*320)
               use_gain=(float)0.5;
           else if (iLBCdec_inst->consPLICount*
   
   
                           iLBCdec_inst->blockl>4*320)
               use_gain=(float)0.0;
   
           /* mix noise and pitch repeatition */
           ftmp=(float)sqrt(max_per);
           if (ftmp>(float)0.7)
               pitchfact=(float)1.0;
           else if (ftmp>(float)0.4)
               pitchfact=(ftmp-(float)0.4)/((float)0.7-(float)0.4);
           else
               pitchfact=0.0;
   
   
           /* avoid repetition of same pitch cycle */
           use_lag=lag;
           if (lag<80) {
               use_lag=2*lag;
           }
   
           /* compute concealed residual */
   
           energy = 0.0;
           for (i=0; i<iLBCdec_inst->blockl; i++) {
   
               /* noise component */
   
               iLBCdec_inst->seed=(iLBCdec_inst->seed*69069L+1) & 
                   (0x80000000L-1);
               randlag = 50 + ((signed long) iLBCdec_inst->seed)%70;
               pick = i - randlag;
               
               if (pick < 0) {
                   randvec[i] = 
                       iLBCdec_inst->prevResidual[
                                   iLBCdec_inst->blockl+pick];
               } else {
                   randvec[i] =  randvec[pick];
               }
   
               /* pitch repeatition component */
               pick = i - use_lag;
               
               if (pick < 0) {
                   PLCresidual[i] =  
                       iLBCdec_inst->prevResidual[
                                   iLBCdec_inst->blockl+pick];
               } else {
                   PLCresidual[i] = PLCresidual[pick];
               }
   
               /* mix random and periodicity component */
   
               if (i<80)
                   PLCresidual[i] = use_gain*(pitchfact * 
   
   
                               PLCresidual[i] +
                               ((float)1.0 - pitchfact) * randvec[i]);
               else if (i<160)
                   PLCresidual[i] = (float)0.95*use_gain*(pitchfact * 
                               PLCresidual[i] +
                               ((float)1.0 - pitchfact) * randvec[i]);
               else
                   PLCresidual[i] = (float)0.9*use_gain*(pitchfact * 
                               PLCresidual[i] +
                               ((float)1.0 - pitchfact) * randvec[i]);
   
               energy += PLCresidual[i] * PLCresidual[i];
           }
           
           /* less than 30 dB, use only noise */
           
           if (sqrt(energy/(float)iLBCdec_inst->blockl) < 30.0) { 
               gain=0.0;
               for (i=0; i<iLBCdec_inst->blockl; i++) {
                   PLCresidual[i] = randvec[i];
               }
           }
   
           /* use old LPC */
   
           memcpy(PLClpc,iLBCdec_inst->prevLpc,
               (LPC_FILTERORDER+1)*sizeof(float));
           
       }
   
       /* no packet loss, copy input */
   
       else {
           memcpy(PLCresidual, decresidual, 
               iLBCdec_inst->blockl*sizeof(float));
           memcpy(PLClpc, lpc, (LPC_FILTERORDER+1)*sizeof(float));
           iLBCdec_inst->consPLICount = 0;
       }
       
       /* update state */
   
       if (PLI) {
           iLBCdec_inst->prevLag = lag;
           iLBCdec_inst->per=max_per;
       }
   
       iLBCdec_inst->prevPLI = PLI;
       memcpy(iLBCdec_inst->prevLpc, PLClpc, 
           (LPC_FILTERORDER+1)*sizeof(float));
       memcpy(iLBCdec_inst->prevResidual, PLCresidual,
           iLBCdec_inst->blockl*sizeof(float));
   }
   
   
   
   
