1 /*
2 * Copyright (c) 2011 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 <stdlib.h>
12 //#include <string.h>
13
14 #include "echo_control_mobile.h"
15 #include "aecm_core.h"
16 #include "ring_buffer.h"
17 #ifdef AEC_DEBUG
18 #include <stdio.h>
19 #endif
20 #ifdef MAC_IPHONE_PRINT
21 #include <time.h>
22 #include <stdio.h>
23 #elif defined ARM_WINM_LOG
24 #include "windows.h"
25 extern HANDLE logFile;
26 #endif
27
28 #define BUF_SIZE_FRAMES 50 // buffer size (frames)
29 // Maximum length of resampled signal. Must be an integer multiple of frames
30 // (ceil(1/(1 + MIN_SKEW)*2) + 1)*FRAME_LEN
31 // The factor of 2 handles wb, and the + 1 is as a safety margin
32 #define MAX_RESAMP_LEN (5 * FRAME_LEN)
33
34 static const size_t kBufSizeSamp = BUF_SIZE_FRAMES * FRAME_LEN; // buffer size (samples)
35 static const int kSampMsNb = 8; // samples per ms in nb
36 // Target suppression levels for nlp modes
37 // log{0.001, 0.00001, 0.00000001}
38 static const int kInitCheck = 42;
39
40 typedef struct
41 {
42 int sampFreq;
43 int scSampFreq;
44 short bufSizeStart;
45 int knownDelay;
46
47 // Stores the last frame added to the farend buffer
48 short farendOld[2][FRAME_LEN];
49 short initFlag; // indicates if AEC has been initialized
50
51 // Variables used for averaging far end buffer size
52 short counter;
53 short sum;
54 short firstVal;
55 short checkBufSizeCtr;
56
57 // Variables used for delay shifts
58 short msInSndCardBuf;
59 short filtDelay;
60 int timeForDelayChange;
61 int ECstartup;
62 int checkBuffSize;
63 int delayChange;
64 short lastDelayDiff;
65
66 WebRtc_Word16 echoMode;
67
68 #ifdef AEC_DEBUG
69 FILE *bufFile;
70 FILE *delayFile;
71 FILE *preCompFile;
72 FILE *postCompFile;
73 #endif // AEC_DEBUG
74 // Structures
75 void *farendBuf;
76
77 int lastError;
78
79 AecmCore_t *aecmCore;
80 } aecmob_t;
81
82 // Estimates delay to set the position of the farend buffer read pointer
83 // (controlled by knownDelay)
84 static int WebRtcAecm_EstBufDelay(aecmob_t *aecmInst, short msInSndCardBuf);
85
86 // Stuffs the farend buffer if the estimated delay is too large
87 static int WebRtcAecm_DelayComp(aecmob_t *aecmInst);
88
WebRtcAecm_Create(void ** aecmInst)89 WebRtc_Word32 WebRtcAecm_Create(void **aecmInst)
90 {
91 aecmob_t *aecm;
92 if (aecmInst == NULL)
93 {
94 return -1;
95 }
96
97 aecm = malloc(sizeof(aecmob_t));
98 *aecmInst = aecm;
99 if (aecm == NULL)
100 {
101 return -1;
102 }
103
104 if (WebRtcAecm_CreateCore(&aecm->aecmCore) == -1)
105 {
106 WebRtcAecm_Free(aecm);
107 aecm = NULL;
108 return -1;
109 }
110
111 if (WebRtc_CreateBuffer(&aecm->farendBuf, kBufSizeSamp,
112 sizeof(int16_t)) == -1)
113 {
114 WebRtcAecm_Free(aecm);
115 aecm = NULL;
116 return -1;
117 }
118
119 aecm->initFlag = 0;
120 aecm->lastError = 0;
121
122 #ifdef AEC_DEBUG
123 aecm->aecmCore->farFile = fopen("aecFar.pcm","wb");
124 aecm->aecmCore->nearFile = fopen("aecNear.pcm","wb");
125 aecm->aecmCore->outFile = fopen("aecOut.pcm","wb");
126 //aecm->aecmCore->outLpFile = fopen("aecOutLp.pcm","wb");
127
128 aecm->bufFile = fopen("aecBuf.dat", "wb");
129 aecm->delayFile = fopen("aecDelay.dat", "wb");
130 aecm->preCompFile = fopen("preComp.pcm", "wb");
131 aecm->postCompFile = fopen("postComp.pcm", "wb");
132 #endif // AEC_DEBUG
133 return 0;
134 }
135
WebRtcAecm_Free(void * aecmInst)136 WebRtc_Word32 WebRtcAecm_Free(void *aecmInst)
137 {
138 aecmob_t *aecm = aecmInst;
139
140 if (aecm == NULL)
141 {
142 return -1;
143 }
144
145 #ifdef AEC_DEBUG
146 fclose(aecm->aecmCore->farFile);
147 fclose(aecm->aecmCore->nearFile);
148 fclose(aecm->aecmCore->outFile);
149 //fclose(aecm->aecmCore->outLpFile);
150
151 fclose(aecm->bufFile);
152 fclose(aecm->delayFile);
153 fclose(aecm->preCompFile);
154 fclose(aecm->postCompFile);
155 #endif // AEC_DEBUG
156 WebRtcAecm_FreeCore(aecm->aecmCore);
157 WebRtc_FreeBuffer(aecm->farendBuf);
158 free(aecm);
159
160 return 0;
161 }
162
WebRtcAecm_Init(void * aecmInst,WebRtc_Word32 sampFreq)163 WebRtc_Word32 WebRtcAecm_Init(void *aecmInst, WebRtc_Word32 sampFreq)
164 {
165 aecmob_t *aecm = aecmInst;
166 AecmConfig aecConfig;
167
168 if (aecm == NULL)
169 {
170 return -1;
171 }
172
173 if (sampFreq != 8000 && sampFreq != 16000)
174 {
175 aecm->lastError = AECM_BAD_PARAMETER_ERROR;
176 return -1;
177 }
178 aecm->sampFreq = sampFreq;
179
180 // Initialize AECM core
181 if (WebRtcAecm_InitCore(aecm->aecmCore, aecm->sampFreq) == -1)
182 {
183 aecm->lastError = AECM_UNSPECIFIED_ERROR;
184 return -1;
185 }
186
187 // Initialize farend buffer
188 if (WebRtc_InitBuffer(aecm->farendBuf) == -1)
189 {
190 aecm->lastError = AECM_UNSPECIFIED_ERROR;
191 return -1;
192 }
193
194 aecm->initFlag = kInitCheck; // indicates that initialization has been done
195
196 aecm->delayChange = 1;
197
198 aecm->sum = 0;
199 aecm->counter = 0;
200 aecm->checkBuffSize = 1;
201 aecm->firstVal = 0;
202
203 aecm->ECstartup = 1;
204 aecm->bufSizeStart = 0;
205 aecm->checkBufSizeCtr = 0;
206 aecm->filtDelay = 0;
207 aecm->timeForDelayChange = 0;
208 aecm->knownDelay = 0;
209 aecm->lastDelayDiff = 0;
210
211 memset(&aecm->farendOld[0][0], 0, 160);
212
213 // Default settings.
214 aecConfig.cngMode = AecmTrue;
215 aecConfig.echoMode = 3;
216
217 if (WebRtcAecm_set_config(aecm, aecConfig) == -1)
218 {
219 aecm->lastError = AECM_UNSPECIFIED_ERROR;
220 return -1;
221 }
222
223 return 0;
224 }
225
WebRtcAecm_BufferFarend(void * aecmInst,const WebRtc_Word16 * farend,WebRtc_Word16 nrOfSamples)226 WebRtc_Word32 WebRtcAecm_BufferFarend(void *aecmInst, const WebRtc_Word16 *farend,
227 WebRtc_Word16 nrOfSamples)
228 {
229 aecmob_t *aecm = aecmInst;
230 WebRtc_Word32 retVal = 0;
231
232 if (aecm == NULL)
233 {
234 return -1;
235 }
236
237 if (farend == NULL)
238 {
239 aecm->lastError = AECM_NULL_POINTER_ERROR;
240 return -1;
241 }
242
243 if (aecm->initFlag != kInitCheck)
244 {
245 aecm->lastError = AECM_UNINITIALIZED_ERROR;
246 return -1;
247 }
248
249 if (nrOfSamples != 80 && nrOfSamples != 160)
250 {
251 aecm->lastError = AECM_BAD_PARAMETER_ERROR;
252 return -1;
253 }
254
255 // TODO: Is this really a good idea?
256 if (!aecm->ECstartup)
257 {
258 WebRtcAecm_DelayComp(aecm);
259 }
260
261 WebRtc_WriteBuffer(aecm->farendBuf, farend, (size_t) nrOfSamples);
262
263 return retVal;
264 }
265
WebRtcAecm_Process(void * aecmInst,const WebRtc_Word16 * nearendNoisy,const WebRtc_Word16 * nearendClean,WebRtc_Word16 * out,WebRtc_Word16 nrOfSamples,WebRtc_Word16 msInSndCardBuf)266 WebRtc_Word32 WebRtcAecm_Process(void *aecmInst, const WebRtc_Word16 *nearendNoisy,
267 const WebRtc_Word16 *nearendClean, WebRtc_Word16 *out,
268 WebRtc_Word16 nrOfSamples, WebRtc_Word16 msInSndCardBuf)
269 {
270 aecmob_t *aecm = aecmInst;
271 WebRtc_Word32 retVal = 0;
272 short i;
273 short nmbrOfFilledBuffers;
274 short nBlocks10ms;
275 short nFrames;
276 #ifdef AEC_DEBUG
277 short msInAECBuf;
278 #endif
279
280 #ifdef ARM_WINM_LOG
281 __int64 freq, start, end, diff;
282 unsigned int milliseconds;
283 DWORD temp;
284 #elif defined MAC_IPHONE_PRINT
285 // double endtime = 0, starttime = 0;
286 struct timeval starttime;
287 struct timeval endtime;
288 static long int timeused = 0;
289 static int timecount = 0;
290 #endif
291
292 if (aecm == NULL)
293 {
294 return -1;
295 }
296
297 if (nearendNoisy == NULL)
298 {
299 aecm->lastError = AECM_NULL_POINTER_ERROR;
300 return -1;
301 }
302
303 if (out == NULL)
304 {
305 aecm->lastError = AECM_NULL_POINTER_ERROR;
306 return -1;
307 }
308
309 if (aecm->initFlag != kInitCheck)
310 {
311 aecm->lastError = AECM_UNINITIALIZED_ERROR;
312 return -1;
313 }
314
315 if (nrOfSamples != 80 && nrOfSamples != 160)
316 {
317 aecm->lastError = AECM_BAD_PARAMETER_ERROR;
318 return -1;
319 }
320
321 if (msInSndCardBuf < 0)
322 {
323 msInSndCardBuf = 0;
324 aecm->lastError = AECM_BAD_PARAMETER_WARNING;
325 retVal = -1;
326 } else if (msInSndCardBuf > 500)
327 {
328 msInSndCardBuf = 500;
329 aecm->lastError = AECM_BAD_PARAMETER_WARNING;
330 retVal = -1;
331 }
332 msInSndCardBuf += 10;
333 aecm->msInSndCardBuf = msInSndCardBuf;
334
335 nFrames = nrOfSamples / FRAME_LEN;
336 nBlocks10ms = nFrames / aecm->aecmCore->mult;
337
338 if (aecm->ECstartup)
339 {
340 if (nearendClean == NULL)
341 {
342 memcpy(out, nearendNoisy, sizeof(short) * nrOfSamples);
343 } else
344 {
345 memcpy(out, nearendClean, sizeof(short) * nrOfSamples);
346 }
347
348 nmbrOfFilledBuffers =
349 (short) WebRtc_available_read(aecm->farendBuf) / FRAME_LEN;
350 // The AECM is in the start up mode
351 // AECM is disabled until the soundcard buffer and farend buffers are OK
352
353 // Mechanism to ensure that the soundcard buffer is reasonably stable.
354 if (aecm->checkBuffSize)
355 {
356 aecm->checkBufSizeCtr++;
357 // Before we fill up the far end buffer we require the amount of data on the
358 // sound card to be stable (+/-8 ms) compared to the first value. This
359 // comparison is made during the following 4 consecutive frames. If it seems
360 // to be stable then we start to fill up the far end buffer.
361
362 if (aecm->counter == 0)
363 {
364 aecm->firstVal = aecm->msInSndCardBuf;
365 aecm->sum = 0;
366 }
367
368 if (abs(aecm->firstVal - aecm->msInSndCardBuf)
369 < WEBRTC_SPL_MAX(0.2 * aecm->msInSndCardBuf, kSampMsNb))
370 {
371 aecm->sum += aecm->msInSndCardBuf;
372 aecm->counter++;
373 } else
374 {
375 aecm->counter = 0;
376 }
377
378 if (aecm->counter * nBlocks10ms >= 6)
379 {
380 // The farend buffer size is determined in blocks of 80 samples
381 // Use 75% of the average value of the soundcard buffer
382 aecm->bufSizeStart
383 = WEBRTC_SPL_MIN((3 * aecm->sum
384 * aecm->aecmCore->mult) / (aecm->counter * 40), BUF_SIZE_FRAMES);
385 // buffersize has now been determined
386 aecm->checkBuffSize = 0;
387 }
388
389 if (aecm->checkBufSizeCtr * nBlocks10ms > 50)
390 {
391 // for really bad sound cards, don't disable echocanceller for more than 0.5 sec
392 aecm->bufSizeStart = WEBRTC_SPL_MIN((3 * aecm->msInSndCardBuf
393 * aecm->aecmCore->mult) / 40, BUF_SIZE_FRAMES);
394 aecm->checkBuffSize = 0;
395 }
396 }
397
398 // if checkBuffSize changed in the if-statement above
399 if (!aecm->checkBuffSize)
400 {
401 // soundcard buffer is now reasonably stable
402 // When the far end buffer is filled with approximately the same amount of
403 // data as the amount on the sound card we end the start up phase and start
404 // to cancel echoes.
405
406 if (nmbrOfFilledBuffers == aecm->bufSizeStart)
407 {
408 aecm->ECstartup = 0; // Enable the AECM
409 } else if (nmbrOfFilledBuffers > aecm->bufSizeStart)
410 {
411 WebRtc_MoveReadPtr(aecm->farendBuf,
412 (int) WebRtc_available_read(aecm->farendBuf)
413 - (int) aecm->bufSizeStart * FRAME_LEN);
414 aecm->ECstartup = 0;
415 }
416 }
417
418 } else
419 {
420 // AECM is enabled
421
422 // Note only 1 block supported for nb and 2 blocks for wb
423 for (i = 0; i < nFrames; i++)
424 {
425 int16_t farend[FRAME_LEN];
426 const int16_t* farend_ptr = NULL;
427
428 nmbrOfFilledBuffers =
429 (short) WebRtc_available_read(aecm->farendBuf) / FRAME_LEN;
430
431 // Check that there is data in the far end buffer
432 if (nmbrOfFilledBuffers > 0)
433 {
434 // Get the next 80 samples from the farend buffer
435 WebRtc_ReadBuffer(aecm->farendBuf, (void**) &farend_ptr, farend,
436 FRAME_LEN);
437
438 // Always store the last frame for use when we run out of data
439 memcpy(&(aecm->farendOld[i][0]), farend_ptr,
440 FRAME_LEN * sizeof(short));
441 } else
442 {
443 // We have no data so we use the last played frame
444 memcpy(farend, &(aecm->farendOld[i][0]), FRAME_LEN * sizeof(short));
445 farend_ptr = farend;
446 }
447
448 // Call buffer delay estimator when all data is extracted,
449 // i,e. i = 0 for NB and i = 1 for WB
450 if ((i == 0 && aecm->sampFreq == 8000) || (i == 1 && aecm->sampFreq == 16000))
451 {
452 WebRtcAecm_EstBufDelay(aecm, aecm->msInSndCardBuf);
453 }
454
455 #ifdef ARM_WINM_LOG
456 // measure tick start
457 QueryPerformanceFrequency((LARGE_INTEGER*)&freq);
458 QueryPerformanceCounter((LARGE_INTEGER*)&start);
459 #elif defined MAC_IPHONE_PRINT
460 // starttime = clock()/(double)CLOCKS_PER_SEC;
461 gettimeofday(&starttime, NULL);
462 #endif
463 // Call the AECM
464 /*WebRtcAecm_ProcessFrame(aecm->aecmCore, farend, &nearend[FRAME_LEN * i],
465 &out[FRAME_LEN * i], aecm->knownDelay);*/
466 if (nearendClean == NULL)
467 {
468 if (WebRtcAecm_ProcessFrame(aecm->aecmCore,
469 farend_ptr,
470 &nearendNoisy[FRAME_LEN * i],
471 NULL,
472 &out[FRAME_LEN * i]) == -1)
473 {
474 return -1;
475 }
476 } else
477 {
478 if (WebRtcAecm_ProcessFrame(aecm->aecmCore,
479 farend_ptr,
480 &nearendNoisy[FRAME_LEN * i],
481 &nearendClean[FRAME_LEN * i],
482 &out[FRAME_LEN * i]) == -1)
483 {
484 return -1;
485 }
486 }
487
488 #ifdef ARM_WINM_LOG
489
490 // measure tick end
491 QueryPerformanceCounter((LARGE_INTEGER*)&end);
492
493 if(end > start)
494 {
495 diff = ((end - start) * 1000) / (freq/1000);
496 milliseconds = (unsigned int)(diff & 0xffffffff);
497 WriteFile (logFile, &milliseconds, sizeof(unsigned int), &temp, NULL);
498 }
499 #elif defined MAC_IPHONE_PRINT
500 // endtime = clock()/(double)CLOCKS_PER_SEC;
501 // printf("%f\n", endtime - starttime);
502
503 gettimeofday(&endtime, NULL);
504
505 if( endtime.tv_usec > starttime.tv_usec)
506 {
507 timeused += endtime.tv_usec - starttime.tv_usec;
508 } else
509 {
510 timeused += endtime.tv_usec + 1000000 - starttime.tv_usec;
511 }
512
513 if(++timecount == 1000)
514 {
515 timecount = 0;
516 printf("AEC: %ld\n", timeused);
517 timeused = 0;
518 }
519 #endif
520
521 }
522 }
523
524 #ifdef AEC_DEBUG
525 msInAECBuf = (short) WebRtc_available_read(aecm->farendBuf) /
526 (kSampMsNb * aecm->aecmCore->mult);
527 fwrite(&msInAECBuf, 2, 1, aecm->bufFile);
528 fwrite(&(aecm->knownDelay), sizeof(aecm->knownDelay), 1, aecm->delayFile);
529 #endif
530
531 return retVal;
532 }
533
WebRtcAecm_set_config(void * aecmInst,AecmConfig config)534 WebRtc_Word32 WebRtcAecm_set_config(void *aecmInst, AecmConfig config)
535 {
536 aecmob_t *aecm = aecmInst;
537
538 if (aecm == NULL)
539 {
540 return -1;
541 }
542
543 if (aecm->initFlag != kInitCheck)
544 {
545 aecm->lastError = AECM_UNINITIALIZED_ERROR;
546 return -1;
547 }
548
549 if (config.cngMode != AecmFalse && config.cngMode != AecmTrue)
550 {
551 aecm->lastError = AECM_BAD_PARAMETER_ERROR;
552 return -1;
553 }
554 aecm->aecmCore->cngMode = config.cngMode;
555
556 if (config.echoMode < 0 || config.echoMode > 4)
557 {
558 aecm->lastError = AECM_BAD_PARAMETER_ERROR;
559 return -1;
560 }
561 aecm->echoMode = config.echoMode;
562
563 if (aecm->echoMode == 0)
564 {
565 aecm->aecmCore->supGain = SUPGAIN_DEFAULT >> 3;
566 aecm->aecmCore->supGainOld = SUPGAIN_DEFAULT >> 3;
567 aecm->aecmCore->supGainErrParamA = SUPGAIN_ERROR_PARAM_A >> 3;
568 aecm->aecmCore->supGainErrParamD = SUPGAIN_ERROR_PARAM_D >> 3;
569 aecm->aecmCore->supGainErrParamDiffAB = (SUPGAIN_ERROR_PARAM_A >> 3)
570 - (SUPGAIN_ERROR_PARAM_B >> 3);
571 aecm->aecmCore->supGainErrParamDiffBD = (SUPGAIN_ERROR_PARAM_B >> 3)
572 - (SUPGAIN_ERROR_PARAM_D >> 3);
573 } else if (aecm->echoMode == 1)
574 {
575 aecm->aecmCore->supGain = SUPGAIN_DEFAULT >> 2;
576 aecm->aecmCore->supGainOld = SUPGAIN_DEFAULT >> 2;
577 aecm->aecmCore->supGainErrParamA = SUPGAIN_ERROR_PARAM_A >> 2;
578 aecm->aecmCore->supGainErrParamD = SUPGAIN_ERROR_PARAM_D >> 2;
579 aecm->aecmCore->supGainErrParamDiffAB = (SUPGAIN_ERROR_PARAM_A >> 2)
580 - (SUPGAIN_ERROR_PARAM_B >> 2);
581 aecm->aecmCore->supGainErrParamDiffBD = (SUPGAIN_ERROR_PARAM_B >> 2)
582 - (SUPGAIN_ERROR_PARAM_D >> 2);
583 } else if (aecm->echoMode == 2)
584 {
585 aecm->aecmCore->supGain = SUPGAIN_DEFAULT >> 1;
586 aecm->aecmCore->supGainOld = SUPGAIN_DEFAULT >> 1;
587 aecm->aecmCore->supGainErrParamA = SUPGAIN_ERROR_PARAM_A >> 1;
588 aecm->aecmCore->supGainErrParamD = SUPGAIN_ERROR_PARAM_D >> 1;
589 aecm->aecmCore->supGainErrParamDiffAB = (SUPGAIN_ERROR_PARAM_A >> 1)
590 - (SUPGAIN_ERROR_PARAM_B >> 1);
591 aecm->aecmCore->supGainErrParamDiffBD = (SUPGAIN_ERROR_PARAM_B >> 1)
592 - (SUPGAIN_ERROR_PARAM_D >> 1);
593 } else if (aecm->echoMode == 3)
594 {
595 aecm->aecmCore->supGain = SUPGAIN_DEFAULT;
596 aecm->aecmCore->supGainOld = SUPGAIN_DEFAULT;
597 aecm->aecmCore->supGainErrParamA = SUPGAIN_ERROR_PARAM_A;
598 aecm->aecmCore->supGainErrParamD = SUPGAIN_ERROR_PARAM_D;
599 aecm->aecmCore->supGainErrParamDiffAB = SUPGAIN_ERROR_PARAM_A - SUPGAIN_ERROR_PARAM_B;
600 aecm->aecmCore->supGainErrParamDiffBD = SUPGAIN_ERROR_PARAM_B - SUPGAIN_ERROR_PARAM_D;
601 } else if (aecm->echoMode == 4)
602 {
603 aecm->aecmCore->supGain = SUPGAIN_DEFAULT << 1;
604 aecm->aecmCore->supGainOld = SUPGAIN_DEFAULT << 1;
605 aecm->aecmCore->supGainErrParamA = SUPGAIN_ERROR_PARAM_A << 1;
606 aecm->aecmCore->supGainErrParamD = SUPGAIN_ERROR_PARAM_D << 1;
607 aecm->aecmCore->supGainErrParamDiffAB = (SUPGAIN_ERROR_PARAM_A << 1)
608 - (SUPGAIN_ERROR_PARAM_B << 1);
609 aecm->aecmCore->supGainErrParamDiffBD = (SUPGAIN_ERROR_PARAM_B << 1)
610 - (SUPGAIN_ERROR_PARAM_D << 1);
611 }
612
613 return 0;
614 }
615
WebRtcAecm_get_config(void * aecmInst,AecmConfig * config)616 WebRtc_Word32 WebRtcAecm_get_config(void *aecmInst, AecmConfig *config)
617 {
618 aecmob_t *aecm = aecmInst;
619
620 if (aecm == NULL)
621 {
622 return -1;
623 }
624
625 if (config == NULL)
626 {
627 aecm->lastError = AECM_NULL_POINTER_ERROR;
628 return -1;
629 }
630
631 if (aecm->initFlag != kInitCheck)
632 {
633 aecm->lastError = AECM_UNINITIALIZED_ERROR;
634 return -1;
635 }
636
637 config->cngMode = aecm->aecmCore->cngMode;
638 config->echoMode = aecm->echoMode;
639
640 return 0;
641 }
642
WebRtcAecm_InitEchoPath(void * aecmInst,const void * echo_path,size_t size_bytes)643 WebRtc_Word32 WebRtcAecm_InitEchoPath(void* aecmInst,
644 const void* echo_path,
645 size_t size_bytes)
646 {
647 aecmob_t *aecm = aecmInst;
648 const WebRtc_Word16* echo_path_ptr = echo_path;
649
650 if ((aecm == NULL) || (echo_path == NULL))
651 {
652 aecm->lastError = AECM_NULL_POINTER_ERROR;
653 return -1;
654 }
655 if (size_bytes != WebRtcAecm_echo_path_size_bytes())
656 {
657 // Input channel size does not match the size of AECM
658 aecm->lastError = AECM_BAD_PARAMETER_ERROR;
659 return -1;
660 }
661 if (aecm->initFlag != kInitCheck)
662 {
663 aecm->lastError = AECM_UNINITIALIZED_ERROR;
664 return -1;
665 }
666
667 WebRtcAecm_InitEchoPathCore(aecm->aecmCore, echo_path_ptr);
668
669 return 0;
670 }
671
WebRtcAecm_GetEchoPath(void * aecmInst,void * echo_path,size_t size_bytes)672 WebRtc_Word32 WebRtcAecm_GetEchoPath(void* aecmInst,
673 void* echo_path,
674 size_t size_bytes)
675 {
676 aecmob_t *aecm = aecmInst;
677 WebRtc_Word16* echo_path_ptr = echo_path;
678
679 if ((aecm == NULL) || (echo_path == NULL))
680 {
681 aecm->lastError = AECM_NULL_POINTER_ERROR;
682 return -1;
683 }
684 if (size_bytes != WebRtcAecm_echo_path_size_bytes())
685 {
686 // Input channel size does not match the size of AECM
687 aecm->lastError = AECM_BAD_PARAMETER_ERROR;
688 return -1;
689 }
690 if (aecm->initFlag != kInitCheck)
691 {
692 aecm->lastError = AECM_UNINITIALIZED_ERROR;
693 return -1;
694 }
695
696 memcpy(echo_path_ptr, aecm->aecmCore->channelStored, size_bytes);
697 return 0;
698 }
699
WebRtcAecm_echo_path_size_bytes()700 size_t WebRtcAecm_echo_path_size_bytes()
701 {
702 return (PART_LEN1 * sizeof(WebRtc_Word16));
703 }
704
WebRtcAecm_get_version(WebRtc_Word8 * versionStr,WebRtc_Word16 len)705 WebRtc_Word32 WebRtcAecm_get_version(WebRtc_Word8 *versionStr, WebRtc_Word16 len)
706 {
707 const char version[] = "AECM 1.2.0";
708 const short versionLen = (short)strlen(version) + 1; // +1 for null-termination
709
710 if (versionStr == NULL)
711 {
712 return -1;
713 }
714
715 if (versionLen > len)
716 {
717 return -1;
718 }
719
720 strncpy(versionStr, version, versionLen);
721 return 0;
722 }
723
WebRtcAecm_get_error_code(void * aecmInst)724 WebRtc_Word32 WebRtcAecm_get_error_code(void *aecmInst)
725 {
726 aecmob_t *aecm = aecmInst;
727
728 if (aecm == NULL)
729 {
730 return -1;
731 }
732
733 return aecm->lastError;
734 }
735
WebRtcAecm_EstBufDelay(aecmob_t * aecm,short msInSndCardBuf)736 static int WebRtcAecm_EstBufDelay(aecmob_t *aecm, short msInSndCardBuf)
737 {
738 short delayNew, nSampSndCard;
739 short nSampFar = (short) WebRtc_available_read(aecm->farendBuf);
740 short diff;
741
742 nSampSndCard = msInSndCardBuf * kSampMsNb * aecm->aecmCore->mult;
743
744 delayNew = nSampSndCard - nSampFar;
745
746 if (delayNew < FRAME_LEN)
747 {
748 WebRtc_MoveReadPtr(aecm->farendBuf, FRAME_LEN);
749 delayNew += FRAME_LEN;
750 }
751
752 aecm->filtDelay = WEBRTC_SPL_MAX(0, (8 * aecm->filtDelay + 2 * delayNew) / 10);
753
754 diff = aecm->filtDelay - aecm->knownDelay;
755 if (diff > 224)
756 {
757 if (aecm->lastDelayDiff < 96)
758 {
759 aecm->timeForDelayChange = 0;
760 } else
761 {
762 aecm->timeForDelayChange++;
763 }
764 } else if (diff < 96 && aecm->knownDelay > 0)
765 {
766 if (aecm->lastDelayDiff > 224)
767 {
768 aecm->timeForDelayChange = 0;
769 } else
770 {
771 aecm->timeForDelayChange++;
772 }
773 } else
774 {
775 aecm->timeForDelayChange = 0;
776 }
777 aecm->lastDelayDiff = diff;
778
779 if (aecm->timeForDelayChange > 25)
780 {
781 aecm->knownDelay = WEBRTC_SPL_MAX((int)aecm->filtDelay - 160, 0);
782 }
783 return 0;
784 }
785
WebRtcAecm_DelayComp(aecmob_t * aecm)786 static int WebRtcAecm_DelayComp(aecmob_t *aecm)
787 {
788 int nSampFar = (int) WebRtc_available_read(aecm->farendBuf);
789 int nSampSndCard, delayNew, nSampAdd;
790 const int maxStuffSamp = 10 * FRAME_LEN;
791
792 nSampSndCard = aecm->msInSndCardBuf * kSampMsNb * aecm->aecmCore->mult;
793 delayNew = nSampSndCard - nSampFar;
794
795 if (delayNew > FAR_BUF_LEN - FRAME_LEN * aecm->aecmCore->mult)
796 {
797 // The difference of the buffer sizes is larger than the maximum
798 // allowed known delay. Compensate by stuffing the buffer.
799 nSampAdd = (int)(WEBRTC_SPL_MAX(((nSampSndCard >> 1) - nSampFar),
800 FRAME_LEN));
801 nSampAdd = WEBRTC_SPL_MIN(nSampAdd, maxStuffSamp);
802
803 WebRtc_MoveReadPtr(aecm->farendBuf, -nSampAdd);
804 aecm->delayChange = 1; // the delay needs to be updated
805 }
806
807 return 0;
808 }
809