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