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 
WebRtcAecm_Create()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 
WebRtcAecm_Free(void * aecmInst)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 
WebRtcAecm_Init(void * aecmInst,int32_t sampFreq)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.
WebRtcAecm_GetBufferFarendError(void * aecmInst,const int16_t * farend,size_t nrOfSamples)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 
WebRtcAecm_BufferFarend(void * aecmInst,const int16_t * farend,size_t nrOfSamples)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 
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, 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 
WebRtcAecm_set_config(void * aecmInst,AecmConfig config)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 
WebRtcAecm_InitEchoPath(void * aecmInst,const void * echo_path,size_t size_bytes)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 
WebRtcAecm_GetEchoPath(void * aecmInst,void * echo_path,size_t size_bytes)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 
WebRtcAecm_echo_path_size_bytes()570 size_t WebRtcAecm_echo_path_size_bytes()
571 {
572     return (PART_LEN1 * sizeof(int16_t));
573 }
574 
575 
WebRtcAecm_EstBufDelay(AecMobile * aecm,short msInSndCardBuf)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 
WebRtcAecm_DelayComp(AecMobile * aecm)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