Home | History | Annotate | Download | only in source
      1 /*
      2  *  Copyright (c) 2011 The WebRTC project authors. All Rights Reserved.
      3  *
      4  *  Use of this source code is governed by a BSD-style license
      5  *  that can be found in the LICENSE file in the root of the source
      6  *  tree. An additional intellectual property rights grant can be found
      7  *  in the file PATENTS.  All contributing project authors may
      8  *  be found in the AUTHORS file in the root of the source tree.
      9  */
     10 
     11 #include <stdlib.h>
     12 //#include <string.h>
     13 
     14 #include "echo_control_mobile.h"
     15 #include "aecm_core.h"
     16 #include "ring_buffer.h"
     17 #ifdef AEC_DEBUG
     18 #include <stdio.h>
     19 #endif
     20 #ifdef MAC_IPHONE_PRINT
     21 #include <time.h>
     22 #include <stdio.h>
     23 #elif defined ARM_WINM_LOG
     24 #include "windows.h"
     25 extern HANDLE logFile;
     26 #endif
     27 
     28 #define BUF_SIZE_FRAMES 50 // buffer size (frames)
     29 // Maximum length of resampled signal. Must be an integer multiple of frames
     30 // (ceil(1/(1 + MIN_SKEW)*2) + 1)*FRAME_LEN
     31 // The factor of 2 handles wb, and the + 1 is as a safety margin
     32 #define MAX_RESAMP_LEN (5 * FRAME_LEN)
     33 
     34 static const int kBufSizeSamp = BUF_SIZE_FRAMES * FRAME_LEN; // buffer size (samples)
     35 static const int kSampMsNb = 8; // samples per ms in nb
     36 // Target suppression levels for nlp modes
     37 // log{0.001, 0.00001, 0.00000001}
     38 static const int kInitCheck = 42;
     39 
     40 typedef struct
     41 {
     42     int sampFreq;
     43     int scSampFreq;
     44     short bufSizeStart;
     45     int knownDelay;
     46 
     47     // Stores the last frame added to the farend buffer
     48     short farendOld[2][FRAME_LEN];
     49     short initFlag; // indicates if AEC has been initialized
     50 
     51     // Variables used for averaging far end buffer size
     52     short counter;
     53     short sum;
     54     short firstVal;
     55     short checkBufSizeCtr;
     56 
     57     // Variables used for delay shifts
     58     short msInSndCardBuf;
     59     short filtDelay;
     60     int timeForDelayChange;
     61     int ECstartup;
     62     int checkBuffSize;
     63     int delayChange;
     64     short lastDelayDiff;
     65 
     66     WebRtc_Word16 echoMode;
     67 
     68 #ifdef AEC_DEBUG
     69     FILE *bufFile;
     70     FILE *delayFile;
     71     FILE *preCompFile;
     72     FILE *postCompFile;
     73 #endif // AEC_DEBUG
     74     // Structures
     75     void *farendBuf;
     76 
     77     int lastError;
     78 
     79     AecmCore_t *aecmCore;
     80 } aecmob_t;
     81 
     82 // Estimates delay to set the position of the farend buffer read pointer
     83 // (controlled by knownDelay)
     84 static int WebRtcAecm_EstBufDelay(aecmob_t *aecmInst, short msInSndCardBuf);
     85 
     86 // Stuffs the farend buffer if the estimated delay is too large
     87 static int WebRtcAecm_DelayComp(aecmob_t *aecmInst);
     88 
     89 WebRtc_Word32 WebRtcAecm_Create(void **aecmInst)
     90 {
     91     aecmob_t *aecm;
     92     if (aecmInst == NULL)
     93     {
     94         return -1;
     95     }
     96 
     97     aecm = malloc(sizeof(aecmob_t));
     98     *aecmInst = aecm;
     99     if (aecm == NULL)
    100     {
    101         return -1;
    102     }
    103 
    104     if (WebRtcAecm_CreateCore(&aecm->aecmCore) == -1)
    105     {
    106         WebRtcAecm_Free(aecm);
    107         aecm = NULL;
    108         return -1;
    109     }
    110 
    111     if (WebRtcApm_CreateBuffer(&aecm->farendBuf, kBufSizeSamp) == -1)
    112     {
    113         WebRtcAecm_Free(aecm);
    114         aecm = NULL;
    115         return -1;
    116     }
    117 
    118     aecm->initFlag = 0;
    119     aecm->lastError = 0;
    120 
    121 #ifdef AEC_DEBUG
    122     aecm->aecmCore->farFile = fopen("aecFar.pcm","wb");
    123     aecm->aecmCore->nearFile = fopen("aecNear.pcm","wb");
    124     aecm->aecmCore->outFile = fopen("aecOut.pcm","wb");
    125     //aecm->aecmCore->outLpFile = fopen("aecOutLp.pcm","wb");
    126 
    127     aecm->bufFile = fopen("aecBuf.dat", "wb");
    128     aecm->delayFile = fopen("aecDelay.dat", "wb");
    129     aecm->preCompFile = fopen("preComp.pcm", "wb");
    130     aecm->postCompFile = fopen("postComp.pcm", "wb");
    131 #endif // AEC_DEBUG
    132     return 0;
    133 }
    134 
    135 WebRtc_Word32 WebRtcAecm_Free(void *aecmInst)
    136 {
    137     aecmob_t *aecm = aecmInst;
    138 
    139     if (aecm == NULL)
    140     {
    141         return -1;
    142     }
    143 
    144 #ifdef AEC_DEBUG
    145     fclose(aecm->aecmCore->farFile);
    146     fclose(aecm->aecmCore->nearFile);
    147     fclose(aecm->aecmCore->outFile);
    148     //fclose(aecm->aecmCore->outLpFile);
    149 
    150     fclose(aecm->bufFile);
    151     fclose(aecm->delayFile);
    152     fclose(aecm->preCompFile);
    153     fclose(aecm->postCompFile);
    154 #endif // AEC_DEBUG
    155     WebRtcAecm_FreeCore(aecm->aecmCore);
    156     WebRtcApm_FreeBuffer(aecm->farendBuf);
    157     free(aecm);
    158 
    159     return 0;
    160 }
    161 
    162 WebRtc_Word32 WebRtcAecm_Init(void *aecmInst, WebRtc_Word32 sampFreq, WebRtc_Word32 scSampFreq)
    163 {
    164     aecmob_t *aecm = aecmInst;
    165     AecmConfig aecConfig;
    166 
    167     if (aecm == NULL)
    168     {
    169         return -1;
    170     }
    171 
    172     if (sampFreq != 8000 && sampFreq != 16000)
    173     {
    174         aecm->lastError = AECM_BAD_PARAMETER_ERROR;
    175         return -1;
    176     }
    177     aecm->sampFreq = sampFreq;
    178 
    179     if (scSampFreq < 1 || scSampFreq > 96000)
    180     {
    181         aecm->lastError = AECM_BAD_PARAMETER_ERROR;
    182         return -1;
    183     }
    184     aecm->scSampFreq = scSampFreq;
    185 
    186     // Initialize AECM core
    187     if (WebRtcAecm_InitCore(aecm->aecmCore, aecm->sampFreq) == -1)
    188     {
    189         aecm->lastError = AECM_UNSPECIFIED_ERROR;
    190         return -1;
    191     }
    192 
    193     // Initialize farend buffer
    194     if (WebRtcApm_InitBuffer(aecm->farendBuf) == -1)
    195     {
    196         aecm->lastError = AECM_UNSPECIFIED_ERROR;
    197         return -1;
    198     }
    199 
    200     aecm->initFlag = kInitCheck; // indicates that initialization has been done
    201 
    202     aecm->delayChange = 1;
    203 
    204     aecm->sum = 0;
    205     aecm->counter = 0;
    206     aecm->checkBuffSize = 1;
    207     aecm->firstVal = 0;
    208 
    209     aecm->ECstartup = 1;
    210     aecm->bufSizeStart = 0;
    211     aecm->checkBufSizeCtr = 0;
    212     aecm->filtDelay = 0;
    213     aecm->timeForDelayChange = 0;
    214     aecm->knownDelay = 0;
    215     aecm->lastDelayDiff = 0;
    216 
    217     memset(&aecm->farendOld[0][0], 0, 160);
    218 
    219     // Default settings.
    220     aecConfig.cngMode = AecmTrue;
    221     aecConfig.echoMode = 3;
    222 
    223     if (WebRtcAecm_set_config(aecm, aecConfig) == -1)
    224     {
    225         aecm->lastError = AECM_UNSPECIFIED_ERROR;
    226         return -1;
    227     }
    228 
    229     return 0;
    230 }
    231 
    232 WebRtc_Word32 WebRtcAecm_BufferFarend(void *aecmInst, const WebRtc_Word16 *farend,
    233                                       WebRtc_Word16 nrOfSamples)
    234 {
    235     aecmob_t *aecm = aecmInst;
    236     WebRtc_Word32 retVal = 0;
    237 
    238     if (aecm == NULL)
    239     {
    240         return -1;
    241     }
    242 
    243     if (farend == NULL)
    244     {
    245         aecm->lastError = AECM_NULL_POINTER_ERROR;
    246         return -1;
    247     }
    248 
    249     if (aecm->initFlag != kInitCheck)
    250     {
    251         aecm->lastError = AECM_UNINITIALIZED_ERROR;
    252         return -1;
    253     }
    254 
    255     if (nrOfSamples != 80 && nrOfSamples != 160)
    256     {
    257         aecm->lastError = AECM_BAD_PARAMETER_ERROR;
    258         return -1;
    259     }
    260 
    261     // TODO: Is this really a good idea?
    262     if (!aecm->ECstartup)
    263     {
    264         WebRtcAecm_DelayComp(aecm);
    265     }
    266 
    267     WebRtcApm_WriteBuffer(aecm->farendBuf, farend, nrOfSamples);
    268 
    269     return retVal;
    270 }
    271 
    272 WebRtc_Word32 WebRtcAecm_Process(void *aecmInst, const WebRtc_Word16 *nearendNoisy,
    273                                  const WebRtc_Word16 *nearendClean, WebRtc_Word16 *out,
    274                                  WebRtc_Word16 nrOfSamples, WebRtc_Word16 msInSndCardBuf)
    275 {
    276     aecmob_t *aecm = aecmInst;
    277     WebRtc_Word32 retVal = 0;
    278     short i;
    279     short farend[FRAME_LEN];
    280     short nmbrOfFilledBuffers;
    281     short nBlocks10ms;
    282     short nFrames;
    283 #ifdef AEC_DEBUG
    284     short msInAECBuf;
    285 #endif
    286 
    287 #ifdef ARM_WINM_LOG
    288     __int64 freq, start, end, diff;
    289     unsigned int milliseconds;
    290     DWORD temp;
    291 #elif defined MAC_IPHONE_PRINT
    292     //       double endtime = 0, starttime = 0;
    293     struct timeval starttime;
    294     struct timeval endtime;
    295     static long int timeused = 0;
    296     static int timecount = 0;
    297 #endif
    298 
    299     if (aecm == NULL)
    300     {
    301         return -1;
    302     }
    303 
    304     if (nearendNoisy == NULL)
    305     {
    306         aecm->lastError = AECM_NULL_POINTER_ERROR;
    307         return -1;
    308     }
    309 
    310     if (out == NULL)
    311     {
    312         aecm->lastError = AECM_NULL_POINTER_ERROR;
    313         return -1;
    314     }
    315 
    316     if (aecm->initFlag != kInitCheck)
    317     {
    318         aecm->lastError = AECM_UNINITIALIZED_ERROR;
    319         return -1;
    320     }
    321 
    322     if (nrOfSamples != 80 && nrOfSamples != 160)
    323     {
    324         aecm->lastError = AECM_BAD_PARAMETER_ERROR;
    325         return -1;
    326     }
    327 
    328     if (msInSndCardBuf < 0)
    329     {
    330         msInSndCardBuf = 0;
    331         aecm->lastError = AECM_BAD_PARAMETER_WARNING;
    332         retVal = -1;
    333     } else if (msInSndCardBuf > 500)
    334     {
    335         msInSndCardBuf = 500;
    336         aecm->lastError = AECM_BAD_PARAMETER_WARNING;
    337         retVal = -1;
    338     }
    339     msInSndCardBuf += 10;
    340     aecm->msInSndCardBuf = msInSndCardBuf;
    341 
    342     nFrames = nrOfSamples / FRAME_LEN;
    343     nBlocks10ms = nFrames / aecm->aecmCore->mult;
    344 
    345     if (aecm->ECstartup)
    346     {
    347         if (nearendClean == NULL)
    348         {
    349             memcpy(out, nearendNoisy, sizeof(short) * nrOfSamples);
    350         } else
    351         {
    352             memcpy(out, nearendClean, sizeof(short) * nrOfSamples);
    353         }
    354 
    355         nmbrOfFilledBuffers = WebRtcApm_get_buffer_size(aecm->farendBuf) / FRAME_LEN;
    356         // The AECM is in the start up mode
    357         // AECM is disabled until the soundcard buffer and farend buffers are OK
    358 
    359         // Mechanism to ensure that the soundcard buffer is reasonably stable.
    360         if (aecm->checkBuffSize)
    361         {
    362             aecm->checkBufSizeCtr++;
    363             // Before we fill up the far end buffer we require the amount of data on the
    364             // sound card to be stable (+/-8 ms) compared to the first value. This
    365             // comparison is made during the following 4 consecutive frames. If it seems
    366             // to be stable then we start to fill up the far end buffer.
    367 
    368             if (aecm->counter == 0)
    369             {
    370                 aecm->firstVal = aecm->msInSndCardBuf;
    371                 aecm->sum = 0;
    372             }
    373 
    374             if (abs(aecm->firstVal - aecm->msInSndCardBuf)
    375                     < WEBRTC_SPL_MAX(0.2 * aecm->msInSndCardBuf, kSampMsNb))
    376             {
    377                 aecm->sum += aecm->msInSndCardBuf;
    378                 aecm->counter++;
    379             } else
    380             {
    381                 aecm->counter = 0;
    382             }
    383 
    384             if (aecm->counter * nBlocks10ms >= 6)
    385             {
    386                 // The farend buffer size is determined in blocks of 80 samples
    387                 // Use 75% of the average value of the soundcard buffer
    388                 aecm->bufSizeStart
    389                         = WEBRTC_SPL_MIN((3 * aecm->sum
    390                                         * aecm->aecmCore->mult) / (aecm->counter * 40), BUF_SIZE_FRAMES);
    391                 // buffersize has now been determined
    392                 aecm->checkBuffSize = 0;
    393             }
    394 
    395             if (aecm->checkBufSizeCtr * nBlocks10ms > 50)
    396             {
    397                 // for really bad sound cards, don't disable echocanceller for more than 0.5 sec
    398                 aecm->bufSizeStart = WEBRTC_SPL_MIN((3 * aecm->msInSndCardBuf
    399                                 * aecm->aecmCore->mult) / 40, BUF_SIZE_FRAMES);
    400                 aecm->checkBuffSize = 0;
    401             }
    402         }
    403 
    404         // if checkBuffSize changed in the if-statement above
    405         if (!aecm->checkBuffSize)
    406         {
    407             // soundcard buffer is now reasonably stable
    408             // When the far end buffer is filled with approximately the same amount of
    409             // data as the amount on the sound card we end the start up phase and start
    410             // to cancel echoes.
    411 
    412             if (nmbrOfFilledBuffers == aecm->bufSizeStart)
    413             {
    414                 aecm->ECstartup = 0; // Enable the AECM
    415             } else if (nmbrOfFilledBuffers > aecm->bufSizeStart)
    416             {
    417                 WebRtcApm_FlushBuffer(
    418                                        aecm->farendBuf,
    419                                        WebRtcApm_get_buffer_size(aecm->farendBuf)
    420                                                - aecm->bufSizeStart * FRAME_LEN);
    421                 aecm->ECstartup = 0;
    422             }
    423         }
    424 
    425     } else
    426     {
    427         // AECM is enabled
    428 
    429         // Note only 1 block supported for nb and 2 blocks for wb
    430         for (i = 0; i < nFrames; i++)
    431         {
    432             nmbrOfFilledBuffers = WebRtcApm_get_buffer_size(aecm->farendBuf) / FRAME_LEN;
    433 
    434             // Check that there is data in the far end buffer
    435             if (nmbrOfFilledBuffers > 0)
    436             {
    437                 // Get the next 80 samples from the farend buffer
    438                 WebRtcApm_ReadBuffer(aecm->farendBuf, farend, FRAME_LEN);
    439 
    440                 // Always store the last frame for use when we run out of data
    441                 memcpy(&(aecm->farendOld[i][0]), farend, FRAME_LEN * sizeof(short));
    442             } else
    443             {
    444                 // We have no data so we use the last played frame
    445                 memcpy(farend, &(aecm->farendOld[i][0]), FRAME_LEN * sizeof(short));
    446             }
    447 
    448             // Call buffer delay estimator when all data is extracted,
    449             // i,e. i = 0 for NB and i = 1 for WB
    450             if ((i == 0 && aecm->sampFreq == 8000) || (i == 1 && aecm->sampFreq == 16000))
    451             {
    452                 WebRtcAecm_EstBufDelay(aecm, aecm->msInSndCardBuf);
    453             }
    454 
    455 #ifdef ARM_WINM_LOG
    456             // measure tick start
    457             QueryPerformanceFrequency((LARGE_INTEGER*)&freq);
    458             QueryPerformanceCounter((LARGE_INTEGER*)&start);
    459 #elif defined MAC_IPHONE_PRINT
    460             //            starttime = clock()/(double)CLOCKS_PER_SEC;
    461             gettimeofday(&starttime, NULL);
    462 #endif
    463             // Call the AECM
    464             /*WebRtcAecm_ProcessFrame(aecm->aecmCore, farend, &nearend[FRAME_LEN * i],
    465              &out[FRAME_LEN * i], aecm->knownDelay);*/
    466             if (nearendClean == NULL)
    467             {
    468                 WebRtcAecm_ProcessFrame(aecm->aecmCore, farend, &nearendNoisy[FRAME_LEN * i],
    469                                         NULL, &out[FRAME_LEN * i]);
    470             } else
    471             {
    472                 WebRtcAecm_ProcessFrame(aecm->aecmCore, farend, &nearendNoisy[FRAME_LEN * i],
    473                                         &nearendClean[FRAME_LEN * i], &out[FRAME_LEN * i]);
    474             }
    475 
    476 #ifdef ARM_WINM_LOG
    477 
    478             // measure tick end
    479             QueryPerformanceCounter((LARGE_INTEGER*)&end);
    480 
    481             if(end > start)
    482             {
    483                 diff = ((end - start) * 1000) / (freq/1000);
    484                 milliseconds = (unsigned int)(diff & 0xffffffff);
    485                 WriteFile (logFile, &milliseconds, sizeof(unsigned int), &temp, NULL);
    486             }
    487 #elif defined MAC_IPHONE_PRINT
    488             //            endtime = clock()/(double)CLOCKS_PER_SEC;
    489             //            printf("%f\n", endtime - starttime);
    490 
    491             gettimeofday(&endtime, NULL);
    492 
    493             if( endtime.tv_usec > starttime.tv_usec)
    494             {
    495                 timeused += endtime.tv_usec - starttime.tv_usec;
    496             } else
    497             {
    498                 timeused += endtime.tv_usec + 1000000 - starttime.tv_usec;
    499             }
    500 
    501             if(++timecount == 1000)
    502             {
    503                 timecount = 0;
    504                 printf("AEC: %ld\n", timeused);
    505                 timeused = 0;
    506             }
    507 #endif
    508 
    509         }
    510     }
    511 
    512 #ifdef AEC_DEBUG
    513     msInAECBuf = WebRtcApm_get_buffer_size(aecm->farendBuf) / (kSampMsNb*aecm->aecmCore->mult);
    514     fwrite(&msInAECBuf, 2, 1, aecm->bufFile);
    515     fwrite(&(aecm->knownDelay), sizeof(aecm->knownDelay), 1, aecm->delayFile);
    516 #endif
    517 
    518     return retVal;
    519 }
    520 
    521 WebRtc_Word32 WebRtcAecm_set_config(void *aecmInst, AecmConfig config)
    522 {
    523     aecmob_t *aecm = aecmInst;
    524 
    525     if (aecm == NULL)
    526     {
    527         return -1;
    528     }
    529 
    530     if (aecm->initFlag != kInitCheck)
    531     {
    532         aecm->lastError = AECM_UNINITIALIZED_ERROR;
    533         return -1;
    534     }
    535 
    536     if (config.cngMode != AecmFalse && config.cngMode != AecmTrue)
    537     {
    538         aecm->lastError = AECM_BAD_PARAMETER_ERROR;
    539         return -1;
    540     }
    541     aecm->aecmCore->cngMode = config.cngMode;
    542 
    543     if (config.echoMode < 0 || config.echoMode > 4)
    544     {
    545         aecm->lastError = AECM_BAD_PARAMETER_ERROR;
    546         return -1;
    547     }
    548     aecm->echoMode = config.echoMode;
    549 
    550     if (aecm->echoMode == 0)
    551     {
    552         aecm->aecmCore->supGain = SUPGAIN_DEFAULT >> 3;
    553         aecm->aecmCore->supGainOld = SUPGAIN_DEFAULT >> 3;
    554         aecm->aecmCore->supGainErrParamA = SUPGAIN_ERROR_PARAM_A >> 3;
    555         aecm->aecmCore->supGainErrParamD = SUPGAIN_ERROR_PARAM_D >> 3;
    556         aecm->aecmCore->supGainErrParamDiffAB = (SUPGAIN_ERROR_PARAM_A >> 3)
    557                 - (SUPGAIN_ERROR_PARAM_B >> 3);
    558         aecm->aecmCore->supGainErrParamDiffBD = (SUPGAIN_ERROR_PARAM_B >> 3)
    559                 - (SUPGAIN_ERROR_PARAM_D >> 3);
    560     } else if (aecm->echoMode == 1)
    561     {
    562         aecm->aecmCore->supGain = SUPGAIN_DEFAULT >> 2;
    563         aecm->aecmCore->supGainOld = SUPGAIN_DEFAULT >> 2;
    564         aecm->aecmCore->supGainErrParamA = SUPGAIN_ERROR_PARAM_A >> 2;
    565         aecm->aecmCore->supGainErrParamD = SUPGAIN_ERROR_PARAM_D >> 2;
    566         aecm->aecmCore->supGainErrParamDiffAB = (SUPGAIN_ERROR_PARAM_A >> 2)
    567                 - (SUPGAIN_ERROR_PARAM_B >> 2);
    568         aecm->aecmCore->supGainErrParamDiffBD = (SUPGAIN_ERROR_PARAM_B >> 2)
    569                 - (SUPGAIN_ERROR_PARAM_D >> 2);
    570     } else if (aecm->echoMode == 2)
    571     {
    572         aecm->aecmCore->supGain = SUPGAIN_DEFAULT >> 1;
    573         aecm->aecmCore->supGainOld = SUPGAIN_DEFAULT >> 1;
    574         aecm->aecmCore->supGainErrParamA = SUPGAIN_ERROR_PARAM_A >> 1;
    575         aecm->aecmCore->supGainErrParamD = SUPGAIN_ERROR_PARAM_D >> 1;
    576         aecm->aecmCore->supGainErrParamDiffAB = (SUPGAIN_ERROR_PARAM_A >> 1)
    577                 - (SUPGAIN_ERROR_PARAM_B >> 1);
    578         aecm->aecmCore->supGainErrParamDiffBD = (SUPGAIN_ERROR_PARAM_B >> 1)
    579                 - (SUPGAIN_ERROR_PARAM_D >> 1);
    580     } else if (aecm->echoMode == 3)
    581     {
    582         aecm->aecmCore->supGain = SUPGAIN_DEFAULT;
    583         aecm->aecmCore->supGainOld = SUPGAIN_DEFAULT;
    584         aecm->aecmCore->supGainErrParamA = SUPGAIN_ERROR_PARAM_A;
    585         aecm->aecmCore->supGainErrParamD = SUPGAIN_ERROR_PARAM_D;
    586         aecm->aecmCore->supGainErrParamDiffAB = SUPGAIN_ERROR_PARAM_A - SUPGAIN_ERROR_PARAM_B;
    587         aecm->aecmCore->supGainErrParamDiffBD = SUPGAIN_ERROR_PARAM_B - SUPGAIN_ERROR_PARAM_D;
    588     } else if (aecm->echoMode == 4)
    589     {
    590         aecm->aecmCore->supGain = SUPGAIN_DEFAULT << 1;
    591         aecm->aecmCore->supGainOld = SUPGAIN_DEFAULT << 1;
    592         aecm->aecmCore->supGainErrParamA = SUPGAIN_ERROR_PARAM_A << 1;
    593         aecm->aecmCore->supGainErrParamD = SUPGAIN_ERROR_PARAM_D << 1;
    594         aecm->aecmCore->supGainErrParamDiffAB = (SUPGAIN_ERROR_PARAM_A << 1)
    595                 - (SUPGAIN_ERROR_PARAM_B << 1);
    596         aecm->aecmCore->supGainErrParamDiffBD = (SUPGAIN_ERROR_PARAM_B << 1)
    597                 - (SUPGAIN_ERROR_PARAM_D << 1);
    598     }
    599 
    600     return 0;
    601 }
    602 
    603 WebRtc_Word32 WebRtcAecm_get_config(void *aecmInst, AecmConfig *config)
    604 {
    605     aecmob_t *aecm = aecmInst;
    606 
    607     if (aecm == NULL)
    608     {
    609         return -1;
    610     }
    611 
    612     if (config == NULL)
    613     {
    614         aecm->lastError = AECM_NULL_POINTER_ERROR;
    615         return -1;
    616     }
    617 
    618     if (aecm->initFlag != kInitCheck)
    619     {
    620         aecm->lastError = AECM_UNINITIALIZED_ERROR;
    621         return -1;
    622     }
    623 
    624     config->cngMode = aecm->aecmCore->cngMode;
    625     config->echoMode = aecm->echoMode;
    626 
    627     return 0;
    628 }
    629 
    630 WebRtc_Word32 WebRtcAecm_get_version(WebRtc_Word8 *versionStr, WebRtc_Word16 len)
    631 {
    632     const char version[] = "AECM 1.2.0";
    633     const short versionLen = (short)strlen(version) + 1; // +1 for null-termination
    634 
    635     if (versionStr == NULL)
    636     {
    637         return -1;
    638     }
    639 
    640     if (versionLen > len)
    641     {
    642         return -1;
    643     }
    644 
    645     strncpy(versionStr, version, versionLen);
    646     return 0;
    647 }
    648 
    649 WebRtc_Word32 WebRtcAecm_get_error_code(void *aecmInst)
    650 {
    651     aecmob_t *aecm = aecmInst;
    652 
    653     if (aecm == NULL)
    654     {
    655         return -1;
    656     }
    657 
    658     return aecm->lastError;
    659 }
    660 
    661 static int WebRtcAecm_EstBufDelay(aecmob_t *aecm, short msInSndCardBuf)
    662 {
    663     short delayNew, nSampFar, nSampSndCard;
    664     short diff;
    665 
    666     nSampFar = WebRtcApm_get_buffer_size(aecm->farendBuf);
    667     nSampSndCard = msInSndCardBuf * kSampMsNb * aecm->aecmCore->mult;
    668 
    669     delayNew = nSampSndCard - nSampFar;
    670 
    671     if (delayNew < FRAME_LEN)
    672     {
    673         WebRtcApm_FlushBuffer(aecm->farendBuf, FRAME_LEN);
    674         delayNew += FRAME_LEN;
    675     }
    676 
    677     aecm->filtDelay = WEBRTC_SPL_MAX(0, (8 * aecm->filtDelay + 2 * delayNew) / 10);
    678 
    679     diff = aecm->filtDelay - aecm->knownDelay;
    680     if (diff > 224)
    681     {
    682         if (aecm->lastDelayDiff < 96)
    683         {
    684             aecm->timeForDelayChange = 0;
    685         } else
    686         {
    687             aecm->timeForDelayChange++;
    688         }
    689     } else if (diff < 96 && aecm->knownDelay > 0)
    690     {
    691         if (aecm->lastDelayDiff > 224)
    692         {
    693             aecm->timeForDelayChange = 0;
    694         } else
    695         {
    696             aecm->timeForDelayChange++;
    697         }
    698     } else
    699     {
    700         aecm->timeForDelayChange = 0;
    701     }
    702     aecm->lastDelayDiff = diff;
    703 
    704     if (aecm->timeForDelayChange > 25)
    705     {
    706         aecm->knownDelay = WEBRTC_SPL_MAX((int)aecm->filtDelay - 160, 0);
    707     }
    708     return 0;
    709 }
    710 
    711 static int WebRtcAecm_DelayComp(aecmob_t *aecm)
    712 {
    713     int nSampFar, nSampSndCard, delayNew, nSampAdd;
    714     const int maxStuffSamp = 10 * FRAME_LEN;
    715 
    716     nSampFar = WebRtcApm_get_buffer_size(aecm->farendBuf);
    717     nSampSndCard = aecm->msInSndCardBuf * kSampMsNb * aecm->aecmCore->mult;
    718     delayNew = nSampSndCard - nSampFar;
    719 
    720     if (delayNew > FAR_BUF_LEN - FRAME_LEN * aecm->aecmCore->mult)
    721     {
    722         // The difference of the buffer sizes is larger than the maximum
    723         // allowed known delay. Compensate by stuffing the buffer.
    724         nSampAdd = (int)(WEBRTC_SPL_MAX(((nSampSndCard >> 1) - nSampFar),
    725                 FRAME_LEN));
    726         nSampAdd = WEBRTC_SPL_MIN(nSampAdd, maxStuffSamp);
    727 
    728         WebRtcApm_StuffBuffer(aecm->farendBuf, nSampAdd);
    729         aecm->delayChange = 1; // the delay needs to be updated
    730     }
    731 
    732     return 0;
    733 }
    734