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