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