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