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