tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

echo_control_mobile.cc (18374B)


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