Home | History | Annotate | Download | only in aecm
      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 size_t 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 (WebRtc_CreateBuffer(&aecm->farendBuf, kBufSizeSamp,
    112                             sizeof(int16_t)) == -1)
    113     {
    114         WebRtcAecm_Free(aecm);
    115         aecm = NULL;
    116         return -1;
    117     }
    118 
    119     aecm->initFlag = 0;
    120     aecm->lastError = 0;
    121 
    122 #ifdef AEC_DEBUG
    123     aecm->aecmCore->farFile = fopen("aecFar.pcm","wb");
    124     aecm->aecmCore->nearFile = fopen("aecNear.pcm","wb");
    125     aecm->aecmCore->outFile = fopen("aecOut.pcm","wb");
    126     //aecm->aecmCore->outLpFile = fopen("aecOutLp.pcm","wb");
    127 
    128     aecm->bufFile = fopen("aecBuf.dat", "wb");
    129     aecm->delayFile = fopen("aecDelay.dat", "wb");
    130     aecm->preCompFile = fopen("preComp.pcm", "wb");
    131     aecm->postCompFile = fopen("postComp.pcm", "wb");
    132 #endif // AEC_DEBUG
    133     return 0;
    134 }
    135 
    136 WebRtc_Word32 WebRtcAecm_Free(void *aecmInst)
    137 {
    138     aecmob_t *aecm = aecmInst;
    139 
    140     if (aecm == NULL)
    141     {
    142         return -1;
    143     }
    144 
    145 #ifdef AEC_DEBUG
    146     fclose(aecm->aecmCore->farFile);
    147     fclose(aecm->aecmCore->nearFile);
    148     fclose(aecm->aecmCore->outFile);
    149     //fclose(aecm->aecmCore->outLpFile);
    150 
    151     fclose(aecm->bufFile);
    152     fclose(aecm->delayFile);
    153     fclose(aecm->preCompFile);
    154     fclose(aecm->postCompFile);
    155 #endif // AEC_DEBUG
    156     WebRtcAecm_FreeCore(aecm->aecmCore);
    157     WebRtc_FreeBuffer(aecm->farendBuf);
    158     free(aecm);
    159 
    160     return 0;
    161 }
    162 
    163 WebRtc_Word32 WebRtcAecm_Init(void *aecmInst, WebRtc_Word32 sampFreq)
    164 {
    165     aecmob_t *aecm = aecmInst;
    166     AecmConfig aecConfig;
    167 
    168     if (aecm == NULL)
    169     {
    170         return -1;
    171     }
    172 
    173     if (sampFreq != 8000 && sampFreq != 16000)
    174     {
    175         aecm->lastError = AECM_BAD_PARAMETER_ERROR;
    176         return -1;
    177     }
    178     aecm->sampFreq = sampFreq;
    179 
    180     // Initialize AECM core
    181     if (WebRtcAecm_InitCore(aecm->aecmCore, aecm->sampFreq) == -1)
    182     {
    183         aecm->lastError = AECM_UNSPECIFIED_ERROR;
    184         return -1;
    185     }
    186 
    187     // Initialize farend buffer
    188     if (WebRtc_InitBuffer(aecm->farendBuf) == -1)
    189     {
    190         aecm->lastError = AECM_UNSPECIFIED_ERROR;
    191         return -1;
    192     }
    193 
    194     aecm->initFlag = kInitCheck; // indicates that initialization has been done
    195 
    196     aecm->delayChange = 1;
    197 
    198     aecm->sum = 0;
    199     aecm->counter = 0;
    200     aecm->checkBuffSize = 1;
    201     aecm->firstVal = 0;
    202 
    203     aecm->ECstartup = 1;
    204     aecm->bufSizeStart = 0;
    205     aecm->checkBufSizeCtr = 0;
    206     aecm->filtDelay = 0;
    207     aecm->timeForDelayChange = 0;
    208     aecm->knownDelay = 0;
    209     aecm->lastDelayDiff = 0;
    210 
    211     memset(&aecm->farendOld[0][0], 0, 160);
    212 
    213     // Default settings.
    214     aecConfig.cngMode = AecmTrue;
    215     aecConfig.echoMode = 3;
    216 
    217     if (WebRtcAecm_set_config(aecm, aecConfig) == -1)
    218     {
    219         aecm->lastError = AECM_UNSPECIFIED_ERROR;
    220         return -1;
    221     }
    222 
    223     return 0;
    224 }
    225 
    226 WebRtc_Word32 WebRtcAecm_BufferFarend(void *aecmInst, const WebRtc_Word16 *farend,
    227                                       WebRtc_Word16 nrOfSamples)
    228 {
    229     aecmob_t *aecm = aecmInst;
    230     WebRtc_Word32 retVal = 0;
    231 
    232     if (aecm == NULL)
    233     {
    234         return -1;
    235     }
    236 
    237     if (farend == NULL)
    238     {
    239         aecm->lastError = AECM_NULL_POINTER_ERROR;
    240         return -1;
    241     }
    242 
    243     if (aecm->initFlag != kInitCheck)
    244     {
    245         aecm->lastError = AECM_UNINITIALIZED_ERROR;
    246         return -1;
    247     }
    248 
    249     if (nrOfSamples != 80 && nrOfSamples != 160)
    250     {
    251         aecm->lastError = AECM_BAD_PARAMETER_ERROR;
    252         return -1;
    253     }
    254 
    255     // TODO: Is this really a good idea?
    256     if (!aecm->ECstartup)
    257     {
    258         WebRtcAecm_DelayComp(aecm);
    259     }
    260 
    261     WebRtc_WriteBuffer(aecm->farendBuf, farend, (size_t) nrOfSamples);
    262 
    263     return retVal;
    264 }
    265 
    266 WebRtc_Word32 WebRtcAecm_Process(void *aecmInst, const WebRtc_Word16 *nearendNoisy,
    267                                  const WebRtc_Word16 *nearendClean, WebRtc_Word16 *out,
    268                                  WebRtc_Word16 nrOfSamples, WebRtc_Word16 msInSndCardBuf)
    269 {
    270     aecmob_t *aecm = aecmInst;
    271     WebRtc_Word32 retVal = 0;
    272     short i;
    273     short nmbrOfFilledBuffers;
    274     short nBlocks10ms;
    275     short nFrames;
    276 #ifdef AEC_DEBUG
    277     short msInAECBuf;
    278 #endif
    279 
    280 #ifdef ARM_WINM_LOG
    281     __int64 freq, start, end, diff;
    282     unsigned int milliseconds;
    283     DWORD temp;
    284 #elif defined MAC_IPHONE_PRINT
    285     //       double endtime = 0, starttime = 0;
    286     struct timeval starttime;
    287     struct timeval endtime;
    288     static long int timeused = 0;
    289     static int timecount = 0;
    290 #endif
    291 
    292     if (aecm == NULL)
    293     {
    294         return -1;
    295     }
    296 
    297     if (nearendNoisy == NULL)
    298     {
    299         aecm->lastError = AECM_NULL_POINTER_ERROR;
    300         return -1;
    301     }
    302 
    303     if (out == NULL)
    304     {
    305         aecm->lastError = AECM_NULL_POINTER_ERROR;
    306         return -1;
    307     }
    308 
    309     if (aecm->initFlag != kInitCheck)
    310     {
    311         aecm->lastError = AECM_UNINITIALIZED_ERROR;
    312         return -1;
    313     }
    314 
    315     if (nrOfSamples != 80 && nrOfSamples != 160)
    316     {
    317         aecm->lastError = AECM_BAD_PARAMETER_ERROR;
    318         return -1;
    319     }
    320 
    321     if (msInSndCardBuf < 0)
    322     {
    323         msInSndCardBuf = 0;
    324         aecm->lastError = AECM_BAD_PARAMETER_WARNING;
    325         retVal = -1;
    326     } else if (msInSndCardBuf > 500)
    327     {
    328         msInSndCardBuf = 500;
    329         aecm->lastError = AECM_BAD_PARAMETER_WARNING;
    330         retVal = -1;
    331     }
    332     msInSndCardBuf += 10;
    333     aecm->msInSndCardBuf = msInSndCardBuf;
    334 
    335     nFrames = nrOfSamples / FRAME_LEN;
    336     nBlocks10ms = nFrames / aecm->aecmCore->mult;
    337 
    338     if (aecm->ECstartup)
    339     {
    340         if (nearendClean == NULL)
    341         {
    342             memcpy(out, nearendNoisy, sizeof(short) * nrOfSamples);
    343         } else
    344         {
    345             memcpy(out, nearendClean, sizeof(short) * nrOfSamples);
    346         }
    347 
    348         nmbrOfFilledBuffers =
    349             (short) WebRtc_available_read(aecm->farendBuf) / FRAME_LEN;
    350         // The AECM is in the start up mode
    351         // AECM is disabled until the soundcard buffer and farend buffers are OK
    352 
    353         // Mechanism to ensure that the soundcard buffer is reasonably stable.
    354         if (aecm->checkBuffSize)
    355         {
    356             aecm->checkBufSizeCtr++;
    357             // Before we fill up the far end buffer we require the amount of data on the
    358             // sound card to be stable (+/-8 ms) compared to the first value. This
    359             // comparison is made during the following 4 consecutive frames. If it seems
    360             // to be stable then we start to fill up the far end buffer.
    361 
    362             if (aecm->counter == 0)
    363             {
    364                 aecm->firstVal = aecm->msInSndCardBuf;
    365                 aecm->sum = 0;
    366             }
    367 
    368             if (abs(aecm->firstVal - aecm->msInSndCardBuf)
    369                     < WEBRTC_SPL_MAX(0.2 * aecm->msInSndCardBuf, kSampMsNb))
    370             {
    371                 aecm->sum += aecm->msInSndCardBuf;
    372                 aecm->counter++;
    373             } else
    374             {
    375                 aecm->counter = 0;
    376             }
    377 
    378             if (aecm->counter * nBlocks10ms >= 6)
    379             {
    380                 // The farend buffer size is determined in blocks of 80 samples
    381                 // Use 75% of the average value of the soundcard buffer
    382                 aecm->bufSizeStart
    383                         = WEBRTC_SPL_MIN((3 * aecm->sum
    384                                         * aecm->aecmCore->mult) / (aecm->counter * 40), BUF_SIZE_FRAMES);
    385                 // buffersize has now been determined
    386                 aecm->checkBuffSize = 0;
    387             }
    388 
    389             if (aecm->checkBufSizeCtr * nBlocks10ms > 50)
    390             {
    391                 // for really bad sound cards, don't disable echocanceller for more than 0.5 sec
    392                 aecm->bufSizeStart = WEBRTC_SPL_MIN((3 * aecm->msInSndCardBuf
    393                                 * aecm->aecmCore->mult) / 40, BUF_SIZE_FRAMES);
    394                 aecm->checkBuffSize = 0;
    395             }
    396         }
    397 
    398         // if checkBuffSize changed in the if-statement above
    399         if (!aecm->checkBuffSize)
    400         {
    401             // soundcard buffer is now reasonably stable
    402             // When the far end buffer is filled with approximately the same amount of
    403             // data as the amount on the sound card we end the start up phase and start
    404             // to cancel echoes.
    405 
    406             if (nmbrOfFilledBuffers == aecm->bufSizeStart)
    407             {
    408                 aecm->ECstartup = 0; // Enable the AECM
    409             } else if (nmbrOfFilledBuffers > aecm->bufSizeStart)
    410             {
    411                 WebRtc_MoveReadPtr(aecm->farendBuf,
    412                                    (int) WebRtc_available_read(aecm->farendBuf)
    413                                    - (int) aecm->bufSizeStart * FRAME_LEN);
    414                 aecm->ECstartup = 0;
    415             }
    416         }
    417 
    418     } else
    419     {
    420         // AECM is enabled
    421 
    422         // Note only 1 block supported for nb and 2 blocks for wb
    423         for (i = 0; i < nFrames; i++)
    424         {
    425             int16_t farend[FRAME_LEN];
    426             const int16_t* farend_ptr = NULL;
    427 
    428             nmbrOfFilledBuffers =
    429                 (short) WebRtc_available_read(aecm->farendBuf) / FRAME_LEN;
    430 
    431             // Check that there is data in the far end buffer
    432             if (nmbrOfFilledBuffers > 0)
    433             {
    434                 // Get the next 80 samples from the farend buffer
    435                 WebRtc_ReadBuffer(aecm->farendBuf, (void**) &farend_ptr, farend,
    436                                   FRAME_LEN);
    437 
    438                 // Always store the last frame for use when we run out of data
    439                 memcpy(&(aecm->farendOld[i][0]), farend_ptr,
    440                        FRAME_LEN * sizeof(short));
    441             } else
    442             {
    443                 // We have no data so we use the last played frame
    444                 memcpy(farend, &(aecm->farendOld[i][0]), FRAME_LEN * sizeof(short));
    445                 farend_ptr = farend;
    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                 if (WebRtcAecm_ProcessFrame(aecm->aecmCore,
    469                                             farend_ptr,
    470                                             &nearendNoisy[FRAME_LEN * i],
    471                                             NULL,
    472                                             &out[FRAME_LEN * i]) == -1)
    473                 {
    474                     return -1;
    475                 }
    476             } else
    477             {
    478                 if (WebRtcAecm_ProcessFrame(aecm->aecmCore,
    479                                             farend_ptr,
    480                                             &nearendNoisy[FRAME_LEN * i],
    481                                             &nearendClean[FRAME_LEN * i],
    482                                             &out[FRAME_LEN * i]) == -1)
    483                 {
    484                     return -1;
    485                 }
    486             }
    487 
    488 #ifdef ARM_WINM_LOG
    489 
    490             // measure tick end
    491             QueryPerformanceCounter((LARGE_INTEGER*)&end);
    492 
    493             if(end > start)
    494             {
    495                 diff = ((end - start) * 1000) / (freq/1000);
    496                 milliseconds = (unsigned int)(diff & 0xffffffff);
    497                 WriteFile (logFile, &milliseconds, sizeof(unsigned int), &temp, NULL);
    498             }
    499 #elif defined MAC_IPHONE_PRINT
    500             //            endtime = clock()/(double)CLOCKS_PER_SEC;
    501             //            printf("%f\n", endtime - starttime);
    502 
    503             gettimeofday(&endtime, NULL);
    504 
    505             if( endtime.tv_usec > starttime.tv_usec)
    506             {
    507                 timeused += endtime.tv_usec - starttime.tv_usec;
    508             } else
    509             {
    510                 timeused += endtime.tv_usec + 1000000 - starttime.tv_usec;
    511             }
    512 
    513             if(++timecount == 1000)
    514             {
    515                 timecount = 0;
    516                 printf("AEC: %ld\n", timeused);
    517                 timeused = 0;
    518             }
    519 #endif
    520 
    521         }
    522     }
    523 
    524 #ifdef AEC_DEBUG
    525     msInAECBuf = (short) WebRtc_available_read(aecm->farendBuf) /
    526         (kSampMsNb * aecm->aecmCore->mult);
    527     fwrite(&msInAECBuf, 2, 1, aecm->bufFile);
    528     fwrite(&(aecm->knownDelay), sizeof(aecm->knownDelay), 1, aecm->delayFile);
    529 #endif
    530 
    531     return retVal;
    532 }
    533 
    534 WebRtc_Word32 WebRtcAecm_set_config(void *aecmInst, AecmConfig config)
    535 {
    536     aecmob_t *aecm = aecmInst;
    537 
    538     if (aecm == NULL)
    539     {
    540         return -1;
    541     }
    542 
    543     if (aecm->initFlag != kInitCheck)
    544     {
    545         aecm->lastError = AECM_UNINITIALIZED_ERROR;
    546         return -1;
    547     }
    548 
    549     if (config.cngMode != AecmFalse && config.cngMode != AecmTrue)
    550     {
    551         aecm->lastError = AECM_BAD_PARAMETER_ERROR;
    552         return -1;
    553     }
    554     aecm->aecmCore->cngMode = config.cngMode;
    555 
    556     if (config.echoMode < 0 || config.echoMode > 4)
    557     {
    558         aecm->lastError = AECM_BAD_PARAMETER_ERROR;
    559         return -1;
    560     }
    561     aecm->echoMode = config.echoMode;
    562 
    563     if (aecm->echoMode == 0)
    564     {
    565         aecm->aecmCore->supGain = SUPGAIN_DEFAULT >> 3;
    566         aecm->aecmCore->supGainOld = SUPGAIN_DEFAULT >> 3;
    567         aecm->aecmCore->supGainErrParamA = SUPGAIN_ERROR_PARAM_A >> 3;
    568         aecm->aecmCore->supGainErrParamD = SUPGAIN_ERROR_PARAM_D >> 3;
    569         aecm->aecmCore->supGainErrParamDiffAB = (SUPGAIN_ERROR_PARAM_A >> 3)
    570                 - (SUPGAIN_ERROR_PARAM_B >> 3);
    571         aecm->aecmCore->supGainErrParamDiffBD = (SUPGAIN_ERROR_PARAM_B >> 3)
    572                 - (SUPGAIN_ERROR_PARAM_D >> 3);
    573     } else if (aecm->echoMode == 1)
    574     {
    575         aecm->aecmCore->supGain = SUPGAIN_DEFAULT >> 2;
    576         aecm->aecmCore->supGainOld = SUPGAIN_DEFAULT >> 2;
    577         aecm->aecmCore->supGainErrParamA = SUPGAIN_ERROR_PARAM_A >> 2;
    578         aecm->aecmCore->supGainErrParamD = SUPGAIN_ERROR_PARAM_D >> 2;
    579         aecm->aecmCore->supGainErrParamDiffAB = (SUPGAIN_ERROR_PARAM_A >> 2)
    580                 - (SUPGAIN_ERROR_PARAM_B >> 2);
    581         aecm->aecmCore->supGainErrParamDiffBD = (SUPGAIN_ERROR_PARAM_B >> 2)
    582                 - (SUPGAIN_ERROR_PARAM_D >> 2);
    583     } else if (aecm->echoMode == 2)
    584     {
    585         aecm->aecmCore->supGain = SUPGAIN_DEFAULT >> 1;
    586         aecm->aecmCore->supGainOld = SUPGAIN_DEFAULT >> 1;
    587         aecm->aecmCore->supGainErrParamA = SUPGAIN_ERROR_PARAM_A >> 1;
    588         aecm->aecmCore->supGainErrParamD = SUPGAIN_ERROR_PARAM_D >> 1;
    589         aecm->aecmCore->supGainErrParamDiffAB = (SUPGAIN_ERROR_PARAM_A >> 1)
    590                 - (SUPGAIN_ERROR_PARAM_B >> 1);
    591         aecm->aecmCore->supGainErrParamDiffBD = (SUPGAIN_ERROR_PARAM_B >> 1)
    592                 - (SUPGAIN_ERROR_PARAM_D >> 1);
    593     } else if (aecm->echoMode == 3)
    594     {
    595         aecm->aecmCore->supGain = SUPGAIN_DEFAULT;
    596         aecm->aecmCore->supGainOld = SUPGAIN_DEFAULT;
    597         aecm->aecmCore->supGainErrParamA = SUPGAIN_ERROR_PARAM_A;
    598         aecm->aecmCore->supGainErrParamD = SUPGAIN_ERROR_PARAM_D;
    599         aecm->aecmCore->supGainErrParamDiffAB = SUPGAIN_ERROR_PARAM_A - SUPGAIN_ERROR_PARAM_B;
    600         aecm->aecmCore->supGainErrParamDiffBD = SUPGAIN_ERROR_PARAM_B - SUPGAIN_ERROR_PARAM_D;
    601     } else if (aecm->echoMode == 4)
    602     {
    603         aecm->aecmCore->supGain = SUPGAIN_DEFAULT << 1;
    604         aecm->aecmCore->supGainOld = SUPGAIN_DEFAULT << 1;
    605         aecm->aecmCore->supGainErrParamA = SUPGAIN_ERROR_PARAM_A << 1;
    606         aecm->aecmCore->supGainErrParamD = SUPGAIN_ERROR_PARAM_D << 1;
    607         aecm->aecmCore->supGainErrParamDiffAB = (SUPGAIN_ERROR_PARAM_A << 1)
    608                 - (SUPGAIN_ERROR_PARAM_B << 1);
    609         aecm->aecmCore->supGainErrParamDiffBD = (SUPGAIN_ERROR_PARAM_B << 1)
    610                 - (SUPGAIN_ERROR_PARAM_D << 1);
    611     }
    612 
    613     return 0;
    614 }
    615 
    616 WebRtc_Word32 WebRtcAecm_get_config(void *aecmInst, AecmConfig *config)
    617 {
    618     aecmob_t *aecm = aecmInst;
    619 
    620     if (aecm == NULL)
    621     {
    622         return -1;
    623     }
    624 
    625     if (config == NULL)
    626     {
    627         aecm->lastError = AECM_NULL_POINTER_ERROR;
    628         return -1;
    629     }
    630 
    631     if (aecm->initFlag != kInitCheck)
    632     {
    633         aecm->lastError = AECM_UNINITIALIZED_ERROR;
    634         return -1;
    635     }
    636 
    637     config->cngMode = aecm->aecmCore->cngMode;
    638     config->echoMode = aecm->echoMode;
    639 
    640     return 0;
    641 }
    642 
    643 WebRtc_Word32 WebRtcAecm_InitEchoPath(void* aecmInst,
    644                                       const void* echo_path,
    645                                       size_t size_bytes)
    646 {
    647     aecmob_t *aecm = aecmInst;
    648     const WebRtc_Word16* echo_path_ptr = echo_path;
    649 
    650     if ((aecm == NULL) || (echo_path == NULL))
    651     {
    652         aecm->lastError = AECM_NULL_POINTER_ERROR;
    653         return -1;
    654     }
    655     if (size_bytes != WebRtcAecm_echo_path_size_bytes())
    656     {
    657         // Input channel size does not match the size of AECM
    658         aecm->lastError = AECM_BAD_PARAMETER_ERROR;
    659         return -1;
    660     }
    661     if (aecm->initFlag != kInitCheck)
    662     {
    663         aecm->lastError = AECM_UNINITIALIZED_ERROR;
    664         return -1;
    665     }
    666 
    667     WebRtcAecm_InitEchoPathCore(aecm->aecmCore, echo_path_ptr);
    668 
    669     return 0;
    670 }
    671 
    672 WebRtc_Word32 WebRtcAecm_GetEchoPath(void* aecmInst,
    673                                      void* echo_path,
    674                                      size_t size_bytes)
    675 {
    676     aecmob_t *aecm = aecmInst;
    677     WebRtc_Word16* echo_path_ptr = echo_path;
    678 
    679     if ((aecm == NULL) || (echo_path == NULL))
    680     {
    681         aecm->lastError = AECM_NULL_POINTER_ERROR;
    682         return -1;
    683     }
    684     if (size_bytes != WebRtcAecm_echo_path_size_bytes())
    685     {
    686         // Input channel size does not match the size of AECM
    687         aecm->lastError = AECM_BAD_PARAMETER_ERROR;
    688         return -1;
    689     }
    690     if (aecm->initFlag != kInitCheck)
    691     {
    692         aecm->lastError = AECM_UNINITIALIZED_ERROR;
    693         return -1;
    694     }
    695 
    696     memcpy(echo_path_ptr, aecm->aecmCore->channelStored, size_bytes);
    697     return 0;
    698 }
    699 
    700 size_t WebRtcAecm_echo_path_size_bytes()
    701 {
    702     return (PART_LEN1 * sizeof(WebRtc_Word16));
    703 }
    704 
    705 WebRtc_Word32 WebRtcAecm_get_version(WebRtc_Word8 *versionStr, WebRtc_Word16 len)
    706 {
    707     const char version[] = "AECM 1.2.0";
    708     const short versionLen = (short)strlen(version) + 1; // +1 for null-termination
    709 
    710     if (versionStr == NULL)
    711     {
    712         return -1;
    713     }
    714 
    715     if (versionLen > len)
    716     {
    717         return -1;
    718     }
    719 
    720     strncpy(versionStr, version, versionLen);
    721     return 0;
    722 }
    723 
    724 WebRtc_Word32 WebRtcAecm_get_error_code(void *aecmInst)
    725 {
    726     aecmob_t *aecm = aecmInst;
    727 
    728     if (aecm == NULL)
    729     {
    730         return -1;
    731     }
    732 
    733     return aecm->lastError;
    734 }
    735 
    736 static int WebRtcAecm_EstBufDelay(aecmob_t *aecm, short msInSndCardBuf)
    737 {
    738     short delayNew, nSampSndCard;
    739     short nSampFar = (short) WebRtc_available_read(aecm->farendBuf);
    740     short diff;
    741 
    742     nSampSndCard = msInSndCardBuf * kSampMsNb * aecm->aecmCore->mult;
    743 
    744     delayNew = nSampSndCard - nSampFar;
    745 
    746     if (delayNew < FRAME_LEN)
    747     {
    748         WebRtc_MoveReadPtr(aecm->farendBuf, FRAME_LEN);
    749         delayNew += FRAME_LEN;
    750     }
    751 
    752     aecm->filtDelay = WEBRTC_SPL_MAX(0, (8 * aecm->filtDelay + 2 * delayNew) / 10);
    753 
    754     diff = aecm->filtDelay - aecm->knownDelay;
    755     if (diff > 224)
    756     {
    757         if (aecm->lastDelayDiff < 96)
    758         {
    759             aecm->timeForDelayChange = 0;
    760         } else
    761         {
    762             aecm->timeForDelayChange++;
    763         }
    764     } else if (diff < 96 && aecm->knownDelay > 0)
    765     {
    766         if (aecm->lastDelayDiff > 224)
    767         {
    768             aecm->timeForDelayChange = 0;
    769         } else
    770         {
    771             aecm->timeForDelayChange++;
    772         }
    773     } else
    774     {
    775         aecm->timeForDelayChange = 0;
    776     }
    777     aecm->lastDelayDiff = diff;
    778 
    779     if (aecm->timeForDelayChange > 25)
    780     {
    781         aecm->knownDelay = WEBRTC_SPL_MAX((int)aecm->filtDelay - 160, 0);
    782     }
    783     return 0;
    784 }
    785 
    786 static int WebRtcAecm_DelayComp(aecmob_t *aecm)
    787 {
    788     int nSampFar = (int) WebRtc_available_read(aecm->farendBuf);
    789     int nSampSndCard, delayNew, nSampAdd;
    790     const int maxStuffSamp = 10 * FRAME_LEN;
    791 
    792     nSampSndCard = aecm->msInSndCardBuf * kSampMsNb * aecm->aecmCore->mult;
    793     delayNew = nSampSndCard - nSampFar;
    794 
    795     if (delayNew > FAR_BUF_LEN - FRAME_LEN * aecm->aecmCore->mult)
    796     {
    797         // The difference of the buffer sizes is larger than the maximum
    798         // allowed known delay. Compensate by stuffing the buffer.
    799         nSampAdd = (int)(WEBRTC_SPL_MAX(((nSampSndCard >> 1) - nSampFar),
    800                 FRAME_LEN));
    801         nSampAdd = WEBRTC_SPL_MIN(nSampAdd, maxStuffSamp);
    802 
    803         WebRtc_MoveReadPtr(aecm->farendBuf, -nSampAdd);
    804         aecm->delayChange = 1; // the delay needs to be updated
    805     }
    806 
    807     return 0;
    808 }
    809