aboutsummaryrefslogtreecommitdiff
path: root/src/modules/audio_processing/aecm/echo_control_mobile.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/modules/audio_processing/aecm/echo_control_mobile.c')
-rw-r--r--src/modules/audio_processing/aecm/echo_control_mobile.c808
1 files changed, 808 insertions, 0 deletions
diff --git a/src/modules/audio_processing/aecm/echo_control_mobile.c b/src/modules/audio_processing/aecm/echo_control_mobile.c
new file mode 100644
index 0000000000..49798b74c6
--- /dev/null
+++ b/src/modules/audio_processing/aecm/echo_control_mobile.c
@@ -0,0 +1,808 @@
+/*
+ * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include <stdlib.h>
+//#include <string.h>
+
+#include "echo_control_mobile.h"
+#include "aecm_core.h"
+#include "ring_buffer.h"
+#ifdef AEC_DEBUG
+#include <stdio.h>
+#endif
+#ifdef MAC_IPHONE_PRINT
+#include <time.h>
+#include <stdio.h>
+#elif defined ARM_WINM_LOG
+#include "windows.h"
+extern HANDLE logFile;
+#endif
+
+#define BUF_SIZE_FRAMES 50 // buffer size (frames)
+// Maximum length of resampled signal. Must be an integer multiple of frames
+// (ceil(1/(1 + MIN_SKEW)*2) + 1)*FRAME_LEN
+// The factor of 2 handles wb, and the + 1 is as a safety margin
+#define MAX_RESAMP_LEN (5 * FRAME_LEN)
+
+static const size_t kBufSizeSamp = BUF_SIZE_FRAMES * FRAME_LEN; // buffer size (samples)
+static const int kSampMsNb = 8; // samples per ms in nb
+// Target suppression levels for nlp modes
+// log{0.001, 0.00001, 0.00000001}
+static const int kInitCheck = 42;
+
+typedef struct
+{
+ int sampFreq;
+ int scSampFreq;
+ short bufSizeStart;
+ int knownDelay;
+
+ // Stores the last frame added to the farend buffer
+ short farendOld[2][FRAME_LEN];
+ short initFlag; // indicates if AEC has been initialized
+
+ // Variables used for averaging far end buffer size
+ short counter;
+ short sum;
+ short firstVal;
+ short checkBufSizeCtr;
+
+ // Variables used for delay shifts
+ short msInSndCardBuf;
+ short filtDelay;
+ int timeForDelayChange;
+ int ECstartup;
+ int checkBuffSize;
+ int delayChange;
+ short lastDelayDiff;
+
+ WebRtc_Word16 echoMode;
+
+#ifdef AEC_DEBUG
+ FILE *bufFile;
+ FILE *delayFile;
+ FILE *preCompFile;
+ FILE *postCompFile;
+#endif // AEC_DEBUG
+ // Structures
+ void *farendBuf;
+
+ int lastError;
+
+ AecmCore_t *aecmCore;
+} aecmob_t;
+
+// Estimates delay to set the position of the farend buffer read pointer
+// (controlled by knownDelay)
+static int WebRtcAecm_EstBufDelay(aecmob_t *aecmInst, short msInSndCardBuf);
+
+// Stuffs the farend buffer if the estimated delay is too large
+static int WebRtcAecm_DelayComp(aecmob_t *aecmInst);
+
+WebRtc_Word32 WebRtcAecm_Create(void **aecmInst)
+{
+ aecmob_t *aecm;
+ if (aecmInst == NULL)
+ {
+ return -1;
+ }
+
+ aecm = malloc(sizeof(aecmob_t));
+ *aecmInst = aecm;
+ if (aecm == NULL)
+ {
+ return -1;
+ }
+
+ if (WebRtcAecm_CreateCore(&aecm->aecmCore) == -1)
+ {
+ WebRtcAecm_Free(aecm);
+ aecm = NULL;
+ return -1;
+ }
+
+ if (WebRtc_CreateBuffer(&aecm->farendBuf, kBufSizeSamp,
+ sizeof(int16_t)) == -1)
+ {
+ WebRtcAecm_Free(aecm);
+ aecm = NULL;
+ return -1;
+ }
+
+ aecm->initFlag = 0;
+ aecm->lastError = 0;
+
+#ifdef AEC_DEBUG
+ aecm->aecmCore->farFile = fopen("aecFar.pcm","wb");
+ aecm->aecmCore->nearFile = fopen("aecNear.pcm","wb");
+ aecm->aecmCore->outFile = fopen("aecOut.pcm","wb");
+ //aecm->aecmCore->outLpFile = fopen("aecOutLp.pcm","wb");
+
+ aecm->bufFile = fopen("aecBuf.dat", "wb");
+ aecm->delayFile = fopen("aecDelay.dat", "wb");
+ aecm->preCompFile = fopen("preComp.pcm", "wb");
+ aecm->postCompFile = fopen("postComp.pcm", "wb");
+#endif // AEC_DEBUG
+ return 0;
+}
+
+WebRtc_Word32 WebRtcAecm_Free(void *aecmInst)
+{
+ aecmob_t *aecm = aecmInst;
+
+ if (aecm == NULL)
+ {
+ return -1;
+ }
+
+#ifdef AEC_DEBUG
+ fclose(aecm->aecmCore->farFile);
+ fclose(aecm->aecmCore->nearFile);
+ fclose(aecm->aecmCore->outFile);
+ //fclose(aecm->aecmCore->outLpFile);
+
+ fclose(aecm->bufFile);
+ fclose(aecm->delayFile);
+ fclose(aecm->preCompFile);
+ fclose(aecm->postCompFile);
+#endif // AEC_DEBUG
+ WebRtcAecm_FreeCore(aecm->aecmCore);
+ WebRtc_FreeBuffer(aecm->farendBuf);
+ free(aecm);
+
+ return 0;
+}
+
+WebRtc_Word32 WebRtcAecm_Init(void *aecmInst, WebRtc_Word32 sampFreq)
+{
+ aecmob_t *aecm = aecmInst;
+ AecmConfig aecConfig;
+
+ if (aecm == NULL)
+ {
+ return -1;
+ }
+
+ if (sampFreq != 8000 && sampFreq != 16000)
+ {
+ aecm->lastError = AECM_BAD_PARAMETER_ERROR;
+ return -1;
+ }
+ aecm->sampFreq = sampFreq;
+
+ // Initialize AECM core
+ if (WebRtcAecm_InitCore(aecm->aecmCore, aecm->sampFreq) == -1)
+ {
+ aecm->lastError = AECM_UNSPECIFIED_ERROR;
+ return -1;
+ }
+
+ // Initialize farend buffer
+ if (WebRtc_InitBuffer(aecm->farendBuf) == -1)
+ {
+ aecm->lastError = AECM_UNSPECIFIED_ERROR;
+ return -1;
+ }
+
+ aecm->initFlag = kInitCheck; // indicates that initialization has been done
+
+ aecm->delayChange = 1;
+
+ aecm->sum = 0;
+ aecm->counter = 0;
+ aecm->checkBuffSize = 1;
+ aecm->firstVal = 0;
+
+ aecm->ECstartup = 1;
+ aecm->bufSizeStart = 0;
+ aecm->checkBufSizeCtr = 0;
+ aecm->filtDelay = 0;
+ aecm->timeForDelayChange = 0;
+ aecm->knownDelay = 0;
+ aecm->lastDelayDiff = 0;
+
+ memset(&aecm->farendOld[0][0], 0, 160);
+
+ // Default settings.
+ aecConfig.cngMode = AecmTrue;
+ aecConfig.echoMode = 3;
+
+ if (WebRtcAecm_set_config(aecm, aecConfig) == -1)
+ {
+ aecm->lastError = AECM_UNSPECIFIED_ERROR;
+ return -1;
+ }
+
+ return 0;
+}
+
+WebRtc_Word32 WebRtcAecm_BufferFarend(void *aecmInst, const WebRtc_Word16 *farend,
+ WebRtc_Word16 nrOfSamples)
+{
+ aecmob_t *aecm = aecmInst;
+ WebRtc_Word32 retVal = 0;
+
+ if (aecm == NULL)
+ {
+ return -1;
+ }
+
+ if (farend == NULL)
+ {
+ aecm->lastError = AECM_NULL_POINTER_ERROR;
+ return -1;
+ }
+
+ if (aecm->initFlag != kInitCheck)
+ {
+ aecm->lastError = AECM_UNINITIALIZED_ERROR;
+ return -1;
+ }
+
+ if (nrOfSamples != 80 && nrOfSamples != 160)
+ {
+ aecm->lastError = AECM_BAD_PARAMETER_ERROR;
+ return -1;
+ }
+
+ // TODO: Is this really a good idea?
+ if (!aecm->ECstartup)
+ {
+ WebRtcAecm_DelayComp(aecm);
+ }
+
+ WebRtc_WriteBuffer(aecm->farendBuf, farend, (size_t) nrOfSamples);
+
+ return retVal;
+}
+
+WebRtc_Word32 WebRtcAecm_Process(void *aecmInst, const WebRtc_Word16 *nearendNoisy,
+ const WebRtc_Word16 *nearendClean, WebRtc_Word16 *out,
+ WebRtc_Word16 nrOfSamples, WebRtc_Word16 msInSndCardBuf)
+{
+ aecmob_t *aecm = aecmInst;
+ WebRtc_Word32 retVal = 0;
+ short i;
+ short nmbrOfFilledBuffers;
+ short nBlocks10ms;
+ short nFrames;
+#ifdef AEC_DEBUG
+ short msInAECBuf;
+#endif
+
+#ifdef ARM_WINM_LOG
+ __int64 freq, start, end, diff;
+ unsigned int milliseconds;
+ DWORD temp;
+#elif defined MAC_IPHONE_PRINT
+ // double endtime = 0, starttime = 0;
+ struct timeval starttime;
+ struct timeval endtime;
+ static long int timeused = 0;
+ static int timecount = 0;
+#endif
+
+ if (aecm == NULL)
+ {
+ return -1;
+ }
+
+ if (nearendNoisy == NULL)
+ {
+ aecm->lastError = AECM_NULL_POINTER_ERROR;
+ return -1;
+ }
+
+ if (out == NULL)
+ {
+ aecm->lastError = AECM_NULL_POINTER_ERROR;
+ return -1;
+ }
+
+ if (aecm->initFlag != kInitCheck)
+ {
+ aecm->lastError = AECM_UNINITIALIZED_ERROR;
+ return -1;
+ }
+
+ if (nrOfSamples != 80 && nrOfSamples != 160)
+ {
+ aecm->lastError = AECM_BAD_PARAMETER_ERROR;
+ return -1;
+ }
+
+ if (msInSndCardBuf < 0)
+ {
+ msInSndCardBuf = 0;
+ aecm->lastError = AECM_BAD_PARAMETER_WARNING;
+ retVal = -1;
+ } else if (msInSndCardBuf > 500)
+ {
+ msInSndCardBuf = 500;
+ aecm->lastError = AECM_BAD_PARAMETER_WARNING;
+ retVal = -1;
+ }
+ msInSndCardBuf += 10;
+ aecm->msInSndCardBuf = msInSndCardBuf;
+
+ nFrames = nrOfSamples / FRAME_LEN;
+ nBlocks10ms = nFrames / aecm->aecmCore->mult;
+
+ if (aecm->ECstartup)
+ {
+ if (nearendClean == NULL)
+ {
+ memcpy(out, nearendNoisy, sizeof(short) * nrOfSamples);
+ } else
+ {
+ memcpy(out, nearendClean, sizeof(short) * nrOfSamples);
+ }
+
+ nmbrOfFilledBuffers =
+ (short) WebRtc_available_read(aecm->farendBuf) / FRAME_LEN;
+ // The AECM is in the start up mode
+ // AECM is disabled until the soundcard buffer and farend buffers are OK
+
+ // Mechanism to ensure that the soundcard buffer is reasonably stable.
+ if (aecm->checkBuffSize)
+ {
+ aecm->checkBufSizeCtr++;
+ // Before we fill up the far end buffer we require the amount of data on the
+ // sound card to be stable (+/-8 ms) compared to the first value. This
+ // comparison is made during the following 4 consecutive frames. If it seems
+ // to be stable then we start to fill up the far end buffer.
+
+ if (aecm->counter == 0)
+ {
+ aecm->firstVal = aecm->msInSndCardBuf;
+ aecm->sum = 0;
+ }
+
+ if (abs(aecm->firstVal - aecm->msInSndCardBuf)
+ < WEBRTC_SPL_MAX(0.2 * aecm->msInSndCardBuf, kSampMsNb))
+ {
+ aecm->sum += aecm->msInSndCardBuf;
+ aecm->counter++;
+ } else
+ {
+ aecm->counter = 0;
+ }
+
+ if (aecm->counter * nBlocks10ms >= 6)
+ {
+ // The farend buffer size is determined in blocks of 80 samples
+ // Use 75% of the average value of the soundcard buffer
+ aecm->bufSizeStart
+ = WEBRTC_SPL_MIN((3 * aecm->sum
+ * aecm->aecmCore->mult) / (aecm->counter * 40), BUF_SIZE_FRAMES);
+ // buffersize has now been determined
+ aecm->checkBuffSize = 0;
+ }
+
+ if (aecm->checkBufSizeCtr * nBlocks10ms > 50)
+ {
+ // for really bad sound cards, don't disable echocanceller for more than 0.5 sec
+ aecm->bufSizeStart = WEBRTC_SPL_MIN((3 * aecm->msInSndCardBuf
+ * aecm->aecmCore->mult) / 40, BUF_SIZE_FRAMES);
+ aecm->checkBuffSize = 0;
+ }
+ }
+
+ // if checkBuffSize changed in the if-statement above
+ if (!aecm->checkBuffSize)
+ {
+ // soundcard buffer is now reasonably stable
+ // When the far end buffer is filled with approximately the same amount of
+ // data as the amount on the sound card we end the start up phase and start
+ // to cancel echoes.
+
+ if (nmbrOfFilledBuffers == aecm->bufSizeStart)
+ {
+ aecm->ECstartup = 0; // Enable the AECM
+ } else if (nmbrOfFilledBuffers > aecm->bufSizeStart)
+ {
+ WebRtc_MoveReadPtr(aecm->farendBuf,
+ (int) WebRtc_available_read(aecm->farendBuf)
+ - (int) aecm->bufSizeStart * FRAME_LEN);
+ aecm->ECstartup = 0;
+ }
+ }
+
+ } else
+ {
+ // AECM is enabled
+
+ // Note only 1 block supported for nb and 2 blocks for wb
+ for (i = 0; i < nFrames; i++)
+ {
+ int16_t farend[FRAME_LEN];
+ const int16_t* farend_ptr = NULL;
+
+ nmbrOfFilledBuffers =
+ (short) WebRtc_available_read(aecm->farendBuf) / FRAME_LEN;
+
+ // Check that there is data in the far end buffer
+ if (nmbrOfFilledBuffers > 0)
+ {
+ // Get the next 80 samples from the farend buffer
+ WebRtc_ReadBuffer(aecm->farendBuf, (void**) &farend_ptr, farend,
+ FRAME_LEN);
+
+ // Always store the last frame for use when we run out of data
+ memcpy(&(aecm->farendOld[i][0]), farend_ptr,
+ FRAME_LEN * sizeof(short));
+ } else
+ {
+ // We have no data so we use the last played frame
+ memcpy(farend, &(aecm->farendOld[i][0]), FRAME_LEN * sizeof(short));
+ farend_ptr = farend;
+ }
+
+ // Call buffer delay estimator when all data is extracted,
+ // i,e. i = 0 for NB and i = 1 for WB
+ if ((i == 0 && aecm->sampFreq == 8000) || (i == 1 && aecm->sampFreq == 16000))
+ {
+ WebRtcAecm_EstBufDelay(aecm, aecm->msInSndCardBuf);
+ }
+
+#ifdef ARM_WINM_LOG
+ // measure tick start
+ QueryPerformanceFrequency((LARGE_INTEGER*)&freq);
+ QueryPerformanceCounter((LARGE_INTEGER*)&start);
+#elif defined MAC_IPHONE_PRINT
+ // starttime = clock()/(double)CLOCKS_PER_SEC;
+ gettimeofday(&starttime, NULL);
+#endif
+ // Call the AECM
+ /*WebRtcAecm_ProcessFrame(aecm->aecmCore, farend, &nearend[FRAME_LEN * i],
+ &out[FRAME_LEN * i], aecm->knownDelay);*/
+ if (nearendClean == NULL)
+ {
+ if (WebRtcAecm_ProcessFrame(aecm->aecmCore,
+ farend_ptr,
+ &nearendNoisy[FRAME_LEN * i],
+ NULL,
+ &out[FRAME_LEN * i]) == -1)
+ {
+ return -1;
+ }
+ } else
+ {
+ if (WebRtcAecm_ProcessFrame(aecm->aecmCore,
+ farend_ptr,
+ &nearendNoisy[FRAME_LEN * i],
+ &nearendClean[FRAME_LEN * i],
+ &out[FRAME_LEN * i]) == -1)
+ {
+ return -1;
+ }
+ }
+
+#ifdef ARM_WINM_LOG
+
+ // measure tick end
+ QueryPerformanceCounter((LARGE_INTEGER*)&end);
+
+ if(end > start)
+ {
+ diff = ((end - start) * 1000) / (freq/1000);
+ milliseconds = (unsigned int)(diff & 0xffffffff);
+ WriteFile (logFile, &milliseconds, sizeof(unsigned int), &temp, NULL);
+ }
+#elif defined MAC_IPHONE_PRINT
+ // endtime = clock()/(double)CLOCKS_PER_SEC;
+ // printf("%f\n", endtime - starttime);
+
+ gettimeofday(&endtime, NULL);
+
+ if( endtime.tv_usec > starttime.tv_usec)
+ {
+ timeused += endtime.tv_usec - starttime.tv_usec;
+ } else
+ {
+ timeused += endtime.tv_usec + 1000000 - starttime.tv_usec;
+ }
+
+ if(++timecount == 1000)
+ {
+ timecount = 0;
+ printf("AEC: %ld\n", timeused);
+ timeused = 0;
+ }
+#endif
+
+ }
+ }
+
+#ifdef AEC_DEBUG
+ msInAECBuf = (short) WebRtc_available_read(aecm->farendBuf) /
+ (kSampMsNb * aecm->aecmCore->mult);
+ fwrite(&msInAECBuf, 2, 1, aecm->bufFile);
+ fwrite(&(aecm->knownDelay), sizeof(aecm->knownDelay), 1, aecm->delayFile);
+#endif
+
+ return retVal;
+}
+
+WebRtc_Word32 WebRtcAecm_set_config(void *aecmInst, AecmConfig config)
+{
+ aecmob_t *aecm = aecmInst;
+
+ if (aecm == NULL)
+ {
+ return -1;
+ }
+
+ if (aecm->initFlag != kInitCheck)
+ {
+ aecm->lastError = AECM_UNINITIALIZED_ERROR;
+ return -1;
+ }
+
+ if (config.cngMode != AecmFalse && config.cngMode != AecmTrue)
+ {
+ aecm->lastError = AECM_BAD_PARAMETER_ERROR;
+ return -1;
+ }
+ aecm->aecmCore->cngMode = config.cngMode;
+
+ if (config.echoMode < 0 || config.echoMode > 4)
+ {
+ aecm->lastError = AECM_BAD_PARAMETER_ERROR;
+ return -1;
+ }
+ aecm->echoMode = config.echoMode;
+
+ if (aecm->echoMode == 0)
+ {
+ aecm->aecmCore->supGain = SUPGAIN_DEFAULT >> 3;
+ aecm->aecmCore->supGainOld = SUPGAIN_DEFAULT >> 3;
+ aecm->aecmCore->supGainErrParamA = SUPGAIN_ERROR_PARAM_A >> 3;
+ aecm->aecmCore->supGainErrParamD = SUPGAIN_ERROR_PARAM_D >> 3;
+ aecm->aecmCore->supGainErrParamDiffAB = (SUPGAIN_ERROR_PARAM_A >> 3)
+ - (SUPGAIN_ERROR_PARAM_B >> 3);
+ aecm->aecmCore->supGainErrParamDiffBD = (SUPGAIN_ERROR_PARAM_B >> 3)
+ - (SUPGAIN_ERROR_PARAM_D >> 3);
+ } else if (aecm->echoMode == 1)
+ {
+ aecm->aecmCore->supGain = SUPGAIN_DEFAULT >> 2;
+ aecm->aecmCore->supGainOld = SUPGAIN_DEFAULT >> 2;
+ aecm->aecmCore->supGainErrParamA = SUPGAIN_ERROR_PARAM_A >> 2;
+ aecm->aecmCore->supGainErrParamD = SUPGAIN_ERROR_PARAM_D >> 2;
+ aecm->aecmCore->supGainErrParamDiffAB = (SUPGAIN_ERROR_PARAM_A >> 2)
+ - (SUPGAIN_ERROR_PARAM_B >> 2);
+ aecm->aecmCore->supGainErrParamDiffBD = (SUPGAIN_ERROR_PARAM_B >> 2)
+ - (SUPGAIN_ERROR_PARAM_D >> 2);
+ } else if (aecm->echoMode == 2)
+ {
+ aecm->aecmCore->supGain = SUPGAIN_DEFAULT >> 1;
+ aecm->aecmCore->supGainOld = SUPGAIN_DEFAULT >> 1;
+ aecm->aecmCore->supGainErrParamA = SUPGAIN_ERROR_PARAM_A >> 1;
+ aecm->aecmCore->supGainErrParamD = SUPGAIN_ERROR_PARAM_D >> 1;
+ aecm->aecmCore->supGainErrParamDiffAB = (SUPGAIN_ERROR_PARAM_A >> 1)
+ - (SUPGAIN_ERROR_PARAM_B >> 1);
+ aecm->aecmCore->supGainErrParamDiffBD = (SUPGAIN_ERROR_PARAM_B >> 1)
+ - (SUPGAIN_ERROR_PARAM_D >> 1);
+ } else if (aecm->echoMode == 3)
+ {
+ aecm->aecmCore->supGain = SUPGAIN_DEFAULT;
+ aecm->aecmCore->supGainOld = SUPGAIN_DEFAULT;
+ aecm->aecmCore->supGainErrParamA = SUPGAIN_ERROR_PARAM_A;
+ aecm->aecmCore->supGainErrParamD = SUPGAIN_ERROR_PARAM_D;
+ aecm->aecmCore->supGainErrParamDiffAB = SUPGAIN_ERROR_PARAM_A - SUPGAIN_ERROR_PARAM_B;
+ aecm->aecmCore->supGainErrParamDiffBD = SUPGAIN_ERROR_PARAM_B - SUPGAIN_ERROR_PARAM_D;
+ } else if (aecm->echoMode == 4)
+ {
+ aecm->aecmCore->supGain = SUPGAIN_DEFAULT << 1;
+ aecm->aecmCore->supGainOld = SUPGAIN_DEFAULT << 1;
+ aecm->aecmCore->supGainErrParamA = SUPGAIN_ERROR_PARAM_A << 1;
+ aecm->aecmCore->supGainErrParamD = SUPGAIN_ERROR_PARAM_D << 1;
+ aecm->aecmCore->supGainErrParamDiffAB = (SUPGAIN_ERROR_PARAM_A << 1)
+ - (SUPGAIN_ERROR_PARAM_B << 1);
+ aecm->aecmCore->supGainErrParamDiffBD = (SUPGAIN_ERROR_PARAM_B << 1)
+ - (SUPGAIN_ERROR_PARAM_D << 1);
+ }
+
+ return 0;
+}
+
+WebRtc_Word32 WebRtcAecm_get_config(void *aecmInst, AecmConfig *config)
+{
+ aecmob_t *aecm = aecmInst;
+
+ if (aecm == NULL)
+ {
+ return -1;
+ }
+
+ if (config == NULL)
+ {
+ aecm->lastError = AECM_NULL_POINTER_ERROR;
+ return -1;
+ }
+
+ if (aecm->initFlag != kInitCheck)
+ {
+ aecm->lastError = AECM_UNINITIALIZED_ERROR;
+ return -1;
+ }
+
+ config->cngMode = aecm->aecmCore->cngMode;
+ config->echoMode = aecm->echoMode;
+
+ return 0;
+}
+
+WebRtc_Word32 WebRtcAecm_InitEchoPath(void* aecmInst,
+ const void* echo_path,
+ size_t size_bytes)
+{
+ aecmob_t *aecm = aecmInst;
+ const WebRtc_Word16* echo_path_ptr = echo_path;
+
+ if ((aecm == NULL) || (echo_path == NULL))
+ {
+ aecm->lastError = AECM_NULL_POINTER_ERROR;
+ return -1;
+ }
+ if (size_bytes != WebRtcAecm_echo_path_size_bytes())
+ {
+ // Input channel size does not match the size of AECM
+ aecm->lastError = AECM_BAD_PARAMETER_ERROR;
+ return -1;
+ }
+ if (aecm->initFlag != kInitCheck)
+ {
+ aecm->lastError = AECM_UNINITIALIZED_ERROR;
+ return -1;
+ }
+
+ WebRtcAecm_InitEchoPathCore(aecm->aecmCore, echo_path_ptr);
+
+ return 0;
+}
+
+WebRtc_Word32 WebRtcAecm_GetEchoPath(void* aecmInst,
+ void* echo_path,
+ size_t size_bytes)
+{
+ aecmob_t *aecm = aecmInst;
+ WebRtc_Word16* echo_path_ptr = echo_path;
+
+ if ((aecm == NULL) || (echo_path == NULL))
+ {
+ aecm->lastError = AECM_NULL_POINTER_ERROR;
+ return -1;
+ }
+ if (size_bytes != WebRtcAecm_echo_path_size_bytes())
+ {
+ // Input channel size does not match the size of AECM
+ aecm->lastError = AECM_BAD_PARAMETER_ERROR;
+ return -1;
+ }
+ if (aecm->initFlag != kInitCheck)
+ {
+ aecm->lastError = AECM_UNINITIALIZED_ERROR;
+ return -1;
+ }
+
+ memcpy(echo_path_ptr, aecm->aecmCore->channelStored, size_bytes);
+ return 0;
+}
+
+size_t WebRtcAecm_echo_path_size_bytes()
+{
+ return (PART_LEN1 * sizeof(WebRtc_Word16));
+}
+
+WebRtc_Word32 WebRtcAecm_get_version(WebRtc_Word8 *versionStr, WebRtc_Word16 len)
+{
+ const char version[] = "AECM 1.2.0";
+ const short versionLen = (short)strlen(version) + 1; // +1 for null-termination
+
+ if (versionStr == NULL)
+ {
+ return -1;
+ }
+
+ if (versionLen > len)
+ {
+ return -1;
+ }
+
+ strncpy(versionStr, version, versionLen);
+ return 0;
+}
+
+WebRtc_Word32 WebRtcAecm_get_error_code(void *aecmInst)
+{
+ aecmob_t *aecm = aecmInst;
+
+ if (aecm == NULL)
+ {
+ return -1;
+ }
+
+ return aecm->lastError;
+}
+
+static int WebRtcAecm_EstBufDelay(aecmob_t *aecm, short msInSndCardBuf)
+{
+ short delayNew, nSampSndCard;
+ short nSampFar = (short) WebRtc_available_read(aecm->farendBuf);
+ short diff;
+
+ nSampSndCard = msInSndCardBuf * kSampMsNb * aecm->aecmCore->mult;
+
+ delayNew = nSampSndCard - nSampFar;
+
+ if (delayNew < FRAME_LEN)
+ {
+ WebRtc_MoveReadPtr(aecm->farendBuf, FRAME_LEN);
+ delayNew += FRAME_LEN;
+ }
+
+ aecm->filtDelay = WEBRTC_SPL_MAX(0, (8 * aecm->filtDelay + 2 * delayNew) / 10);
+
+ diff = aecm->filtDelay - aecm->knownDelay;
+ if (diff > 224)
+ {
+ if (aecm->lastDelayDiff < 96)
+ {
+ aecm->timeForDelayChange = 0;
+ } else
+ {
+ aecm->timeForDelayChange++;
+ }
+ } else if (diff < 96 && aecm->knownDelay > 0)
+ {
+ if (aecm->lastDelayDiff > 224)
+ {
+ aecm->timeForDelayChange = 0;
+ } else
+ {
+ aecm->timeForDelayChange++;
+ }
+ } else
+ {
+ aecm->timeForDelayChange = 0;
+ }
+ aecm->lastDelayDiff = diff;
+
+ if (aecm->timeForDelayChange > 25)
+ {
+ aecm->knownDelay = WEBRTC_SPL_MAX((int)aecm->filtDelay - 160, 0);
+ }
+ return 0;
+}
+
+static int WebRtcAecm_DelayComp(aecmob_t *aecm)
+{
+ int nSampFar = (int) WebRtc_available_read(aecm->farendBuf);
+ int nSampSndCard, delayNew, nSampAdd;
+ const int maxStuffSamp = 10 * FRAME_LEN;
+
+ nSampSndCard = aecm->msInSndCardBuf * kSampMsNb * aecm->aecmCore->mult;
+ delayNew = nSampSndCard - nSampFar;
+
+ if (delayNew > FAR_BUF_LEN - FRAME_LEN * aecm->aecmCore->mult)
+ {
+ // The difference of the buffer sizes is larger than the maximum
+ // allowed known delay. Compensate by stuffing the buffer.
+ nSampAdd = (int)(WEBRTC_SPL_MAX(((nSampSndCard >> 1) - nSampFar),
+ FRAME_LEN));
+ nSampAdd = WEBRTC_SPL_MIN(nSampAdd, maxStuffSamp);
+
+ WebRtc_MoveReadPtr(aecm->farendBuf, -nSampAdd);
+ aecm->delayChange = 1; // the delay needs to be updated
+ }
+
+ return 0;
+}