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"
25extern 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
34static const size_t kBufSizeSamp = BUF_SIZE_FRAMES * FRAME_LEN; // buffer size (samples)
35static const int kSampMsNb = 8; // samples per ms in nb
36// Target suppression levels for nlp modes
37// log{0.001, 0.00001, 0.00000001}
38static const int kInitCheck = 42;
39
40typedef 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)
84static int WebRtcAecm_EstBufDelay(aecmob_t *aecmInst, short msInSndCardBuf);
85
86// Stuffs the farend buffer if the estimated delay is too large
87static int WebRtcAecm_DelayComp(aecmob_t *aecmInst);
88
89WebRtc_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
136WebRtc_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
163WebRtc_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
226WebRtc_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
266WebRtc_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
534WebRtc_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
616WebRtc_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
643WebRtc_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
672WebRtc_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
700size_t WebRtcAecm_echo_path_size_bytes()
701{
702    return (PART_LEN1 * sizeof(WebRtc_Word16));
703}
704
705WebRtc_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
724WebRtc_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
736static 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
786static 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