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