blob: c947563b2805e2e9771241f3bd1e543c6af2f848 [file] [log] [blame]
niklase@google.com470e71d2011-07-07 08:21:25 +00001/*
bjornv@webrtc.org0c6f9312012-01-30 09:39:08 +00002 * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
niklase@google.com470e71d2011-07-07 08:21:25 +00003 *
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
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020011#include "modules/audio_processing/aecm/echo_control_mobile.h"
niklase@google.com470e71d2011-07-07 08:21:25 +000012
niklase@google.com470e71d2011-07-07 08:21:25 +000013#ifdef AEC_DEBUG
14#include <stdio.h>
15#endif
andrew@webrtc.org9ae13542013-02-25 17:07:35 +000016#include <stdlib.h>
17
peah27045122016-04-10 22:38:14 -070018extern "C" {
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020019#include "common_audio/ring_buffer.h"
20#include "common_audio/signal_processing/include/signal_processing_library.h"
peah27045122016-04-10 22:38:14 -070021}
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020022#include "modules/audio_processing/aecm/aecm_core.h"
niklase@google.com470e71d2011-07-07 08:21:25 +000023
Yves Gerey665174f2018-06-19 15:03:05 +020024#define BUF_SIZE_FRAMES 50 // buffer size (frames)
niklase@google.com470e71d2011-07-07 08:21:25 +000025// Maximum length of resampled signal. Must be an integer multiple of frames
26// (ceil(1/(1 + MIN_SKEW)*2) + 1)*FRAME_LEN
27// The factor of 2 handles wb, and the + 1 is as a safety margin
28#define MAX_RESAMP_LEN (5 * FRAME_LEN)
29
Yves Gerey665174f2018-06-19 15:03:05 +020030static const size_t kBufSizeSamp =
31 BUF_SIZE_FRAMES * FRAME_LEN; // buffer size (samples)
32static const int kSampMsNb = 8; // samples per ms in nb
niklase@google.com470e71d2011-07-07 08:21:25 +000033// Target suppression levels for nlp modes
34// log{0.001, 0.00001, 0.00000001}
35static const int kInitCheck = 42;
36
Yves Gerey665174f2018-06-19 15:03:05 +020037typedef struct {
38 int sampFreq;
39 int scSampFreq;
40 short bufSizeStart;
41 int knownDelay;
niklase@google.com470e71d2011-07-07 08:21:25 +000042
Yves Gerey665174f2018-06-19 15:03:05 +020043 // Stores the last frame added to the farend buffer
44 short farendOld[2][FRAME_LEN];
45 short initFlag; // indicates if AEC has been initialized
niklase@google.com470e71d2011-07-07 08:21:25 +000046
Yves Gerey665174f2018-06-19 15:03:05 +020047 // Variables used for averaging far end buffer size
48 short counter;
49 short sum;
50 short firstVal;
51 short checkBufSizeCtr;
niklase@google.com470e71d2011-07-07 08:21:25 +000052
Yves Gerey665174f2018-06-19 15:03:05 +020053 // Variables used for delay shifts
54 short msInSndCardBuf;
55 short filtDelay;
56 int timeForDelayChange;
57 int ECstartup;
58 int checkBuffSize;
59 int delayChange;
60 short lastDelayDiff;
niklase@google.com470e71d2011-07-07 08:21:25 +000061
Yves Gerey665174f2018-06-19 15:03:05 +020062 int16_t echoMode;
niklase@google.com470e71d2011-07-07 08:21:25 +000063
64#ifdef AEC_DEBUG
Yves Gerey665174f2018-06-19 15:03:05 +020065 FILE* bufFile;
66 FILE* delayFile;
67 FILE* preCompFile;
68 FILE* postCompFile;
69#endif // AEC_DEBUG
70 // Structures
71 RingBuffer* farendBuf;
niklase@google.com470e71d2011-07-07 08:21:25 +000072
Yves Gerey665174f2018-06-19 15:03:05 +020073 AecmCore* aecmCore;
pbos@webrtc.orge468bc92014-12-18 09:11:33 +000074} AecMobile;
niklase@google.com470e71d2011-07-07 08:21:25 +000075
76// Estimates delay to set the position of the farend buffer read pointer
77// (controlled by knownDelay)
Alex Loiko890988c2017-08-31 10:25:48 +020078static int WebRtcAecm_EstBufDelay(AecMobile* aecm, short msInSndCardBuf);
niklase@google.com470e71d2011-07-07 08:21:25 +000079
80// Stuffs the farend buffer if the estimated delay is too large
Alex Loiko890988c2017-08-31 10:25:48 +020081static int WebRtcAecm_DelayComp(AecMobile* aecm);
niklase@google.com470e71d2011-07-07 08:21:25 +000082
Bjorn Volckera7437942015-05-28 15:58:42 +020083void* WebRtcAecm_Create() {
Yves Gerey665174f2018-06-19 15:03:05 +020084 AecMobile* aecm = static_cast<AecMobile*>(malloc(sizeof(AecMobile)));
niklase@google.com470e71d2011-07-07 08:21:25 +000085
Yves Gerey665174f2018-06-19 15:03:05 +020086 WebRtcSpl_Init();
kma@webrtc.org31829a72013-03-19 00:25:01 +000087
Yves Gerey665174f2018-06-19 15:03:05 +020088 aecm->aecmCore = WebRtcAecm_CreateCore();
89 if (!aecm->aecmCore) {
90 WebRtcAecm_Free(aecm);
91 return NULL;
92 }
niklase@google.com470e71d2011-07-07 08:21:25 +000093
Yves Gerey665174f2018-06-19 15:03:05 +020094 aecm->farendBuf = WebRtc_CreateBuffer(kBufSizeSamp, sizeof(int16_t));
95 if (!aecm->farendBuf) {
96 WebRtcAecm_Free(aecm);
97 return NULL;
98 }
niklase@google.com470e71d2011-07-07 08:21:25 +000099
Yves Gerey665174f2018-06-19 15:03:05 +0200100 aecm->initFlag = 0;
niklase@google.com470e71d2011-07-07 08:21:25 +0000101
102#ifdef AEC_DEBUG
Yves Gerey665174f2018-06-19 15:03:05 +0200103 aecm->aecmCore->farFile = fopen("aecFar.pcm", "wb");
104 aecm->aecmCore->nearFile = fopen("aecNear.pcm", "wb");
105 aecm->aecmCore->outFile = fopen("aecOut.pcm", "wb");
106 // aecm->aecmCore->outLpFile = fopen("aecOutLp.pcm","wb");
niklase@google.com470e71d2011-07-07 08:21:25 +0000107
Yves Gerey665174f2018-06-19 15:03:05 +0200108 aecm->bufFile = fopen("aecBuf.dat", "wb");
109 aecm->delayFile = fopen("aecDelay.dat", "wb");
110 aecm->preCompFile = fopen("preComp.pcm", "wb");
111 aecm->postCompFile = fopen("postComp.pcm", "wb");
112#endif // AEC_DEBUG
113 return aecm;
niklase@google.com470e71d2011-07-07 08:21:25 +0000114}
115
Bjorn Volckerf6a99e62015-04-10 07:56:57 +0200116void WebRtcAecm_Free(void* aecmInst) {
peah27045122016-04-10 22:38:14 -0700117 AecMobile* aecm = static_cast<AecMobile*>(aecmInst);
niklase@google.com470e71d2011-07-07 08:21:25 +0000118
Yves Gerey665174f2018-06-19 15:03:05 +0200119 if (aecm == NULL) {
120 return;
121 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000122
123#ifdef AEC_DEBUG
Yves Gerey665174f2018-06-19 15:03:05 +0200124 fclose(aecm->aecmCore->farFile);
125 fclose(aecm->aecmCore->nearFile);
126 fclose(aecm->aecmCore->outFile);
127 // fclose(aecm->aecmCore->outLpFile);
niklase@google.com470e71d2011-07-07 08:21:25 +0000128
Yves Gerey665174f2018-06-19 15:03:05 +0200129 fclose(aecm->bufFile);
130 fclose(aecm->delayFile);
131 fclose(aecm->preCompFile);
132 fclose(aecm->postCompFile);
133#endif // AEC_DEBUG
134 WebRtcAecm_FreeCore(aecm->aecmCore);
135 WebRtc_FreeBuffer(aecm->farendBuf);
136 free(aecm);
niklase@google.com470e71d2011-07-07 08:21:25 +0000137}
138
Yves Gerey665174f2018-06-19 15:03:05 +0200139int32_t WebRtcAecm_Init(void* aecmInst, int32_t sampFreq) {
140 AecMobile* aecm = static_cast<AecMobile*>(aecmInst);
141 AecmConfig aecConfig;
niklase@google.com470e71d2011-07-07 08:21:25 +0000142
Yves Gerey665174f2018-06-19 15:03:05 +0200143 if (aecm == NULL) {
144 return -1;
145 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000146
Yves Gerey665174f2018-06-19 15:03:05 +0200147 if (sampFreq != 8000 && sampFreq != 16000) {
148 return AECM_BAD_PARAMETER_ERROR;
149 }
150 aecm->sampFreq = sampFreq;
niklase@google.com470e71d2011-07-07 08:21:25 +0000151
Yves Gerey665174f2018-06-19 15:03:05 +0200152 // Initialize AECM core
153 if (WebRtcAecm_InitCore(aecm->aecmCore, aecm->sampFreq) == -1) {
154 return AECM_UNSPECIFIED_ERROR;
155 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000156
Yves Gerey665174f2018-06-19 15:03:05 +0200157 // Initialize farend buffer
158 WebRtc_InitBuffer(aecm->farendBuf);
niklase@google.com470e71d2011-07-07 08:21:25 +0000159
Yves Gerey665174f2018-06-19 15:03:05 +0200160 aecm->initFlag = kInitCheck; // indicates that initialization has been done
niklase@google.com470e71d2011-07-07 08:21:25 +0000161
Yves Gerey665174f2018-06-19 15:03:05 +0200162 aecm->delayChange = 1;
niklase@google.com470e71d2011-07-07 08:21:25 +0000163
Yves Gerey665174f2018-06-19 15:03:05 +0200164 aecm->sum = 0;
165 aecm->counter = 0;
166 aecm->checkBuffSize = 1;
167 aecm->firstVal = 0;
niklase@google.com470e71d2011-07-07 08:21:25 +0000168
Yves Gerey665174f2018-06-19 15:03:05 +0200169 aecm->ECstartup = 1;
170 aecm->bufSizeStart = 0;
171 aecm->checkBufSizeCtr = 0;
172 aecm->filtDelay = 0;
173 aecm->timeForDelayChange = 0;
174 aecm->knownDelay = 0;
175 aecm->lastDelayDiff = 0;
niklase@google.com470e71d2011-07-07 08:21:25 +0000176
Yves Gerey665174f2018-06-19 15:03:05 +0200177 memset(&aecm->farendOld, 0, sizeof(aecm->farendOld));
niklase@google.com470e71d2011-07-07 08:21:25 +0000178
Yves Gerey665174f2018-06-19 15:03:05 +0200179 // Default settings.
180 aecConfig.cngMode = AecmTrue;
181 aecConfig.echoMode = 3;
niklase@google.com470e71d2011-07-07 08:21:25 +0000182
Yves Gerey665174f2018-06-19 15:03:05 +0200183 if (WebRtcAecm_set_config(aecm, aecConfig) == -1) {
184 return AECM_UNSPECIFIED_ERROR;
185 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000186
Yves Gerey665174f2018-06-19 15:03:05 +0200187 return 0;
niklase@google.com470e71d2011-07-07 08:21:25 +0000188}
189
peahc12be392015-11-09 23:53:50 -0800190// Returns any error that is caused when buffering the
191// farend signal.
Yves Gerey665174f2018-06-19 15:03:05 +0200192int32_t WebRtcAecm_GetBufferFarendError(void* aecmInst,
193 const int16_t* farend,
194 size_t nrOfSamples) {
peah27045122016-04-10 22:38:14 -0700195 AecMobile* aecm = static_cast<AecMobile*>(aecmInst);
niklase@google.com470e71d2011-07-07 08:21:25 +0000196
peahc12be392015-11-09 23:53:50 -0800197 if (aecm == NULL)
198 return -1;
niklase@google.com470e71d2011-07-07 08:21:25 +0000199
peahc12be392015-11-09 23:53:50 -0800200 if (farend == NULL)
201 return AECM_NULL_POINTER_ERROR;
niklase@google.com470e71d2011-07-07 08:21:25 +0000202
peahc12be392015-11-09 23:53:50 -0800203 if (aecm->initFlag != kInitCheck)
204 return AECM_UNINITIALIZED_ERROR;
niklase@google.com470e71d2011-07-07 08:21:25 +0000205
peahc12be392015-11-09 23:53:50 -0800206 if (nrOfSamples != 80 && nrOfSamples != 160)
207 return AECM_BAD_PARAMETER_ERROR;
niklase@google.com470e71d2011-07-07 08:21:25 +0000208
peahc12be392015-11-09 23:53:50 -0800209 return 0;
210}
niklase@google.com470e71d2011-07-07 08:21:25 +0000211
Yves Gerey665174f2018-06-19 15:03:05 +0200212int32_t WebRtcAecm_BufferFarend(void* aecmInst,
213 const int16_t* farend,
peahc12be392015-11-09 23:53:50 -0800214 size_t nrOfSamples) {
peah27045122016-04-10 22:38:14 -0700215 AecMobile* aecm = static_cast<AecMobile*>(aecmInst);
peahc12be392015-11-09 23:53:50 -0800216
217 const int32_t err =
218 WebRtcAecm_GetBufferFarendError(aecmInst, farend, nrOfSamples);
219
220 if (err != 0)
221 return err;
222
223 // TODO(unknown): Is this really a good idea?
Yves Gerey665174f2018-06-19 15:03:05 +0200224 if (!aecm->ECstartup) {
peahc12be392015-11-09 23:53:50 -0800225 WebRtcAecm_DelayComp(aecm);
226 }
227
228 WebRtc_WriteBuffer(aecm->farendBuf, farend, nrOfSamples);
229
230 return 0;
niklase@google.com470e71d2011-07-07 08:21:25 +0000231}
232
Yves Gerey665174f2018-06-19 15:03:05 +0200233int32_t WebRtcAecm_Process(void* aecmInst,
234 const int16_t* nearendNoisy,
235 const int16_t* nearendClean,
236 int16_t* out,
237 size_t nrOfSamples,
238 int16_t msInSndCardBuf) {
239 AecMobile* aecm = static_cast<AecMobile*>(aecmInst);
240 int32_t retVal = 0;
241 size_t i;
242 short nmbrOfFilledBuffers;
243 size_t nBlocks10ms;
244 size_t nFrames;
niklase@google.com470e71d2011-07-07 08:21:25 +0000245#ifdef AEC_DEBUG
Yves Gerey665174f2018-06-19 15:03:05 +0200246 short msInAECBuf;
niklase@google.com470e71d2011-07-07 08:21:25 +0000247#endif
248
Yves Gerey665174f2018-06-19 15:03:05 +0200249 if (aecm == NULL) {
250 return -1;
251 }
252
253 if (nearendNoisy == NULL) {
254 return AECM_NULL_POINTER_ERROR;
255 }
256
257 if (out == NULL) {
258 return AECM_NULL_POINTER_ERROR;
259 }
260
261 if (aecm->initFlag != kInitCheck) {
262 return AECM_UNINITIALIZED_ERROR;
263 }
264
265 if (nrOfSamples != 80 && nrOfSamples != 160) {
266 return AECM_BAD_PARAMETER_ERROR;
267 }
268
269 if (msInSndCardBuf < 0) {
270 msInSndCardBuf = 0;
271 retVal = AECM_BAD_PARAMETER_WARNING;
272 } else if (msInSndCardBuf > 500) {
273 msInSndCardBuf = 500;
274 retVal = AECM_BAD_PARAMETER_WARNING;
275 }
276 msInSndCardBuf += 10;
277 aecm->msInSndCardBuf = msInSndCardBuf;
278
279 nFrames = nrOfSamples / FRAME_LEN;
280 nBlocks10ms = nFrames / aecm->aecmCore->mult;
281
282 if (aecm->ECstartup) {
283 if (nearendClean == NULL) {
284 if (out != nearendNoisy) {
285 memcpy(out, nearendNoisy, sizeof(short) * nrOfSamples);
286 }
287 } else if (out != nearendClean) {
288 memcpy(out, nearendClean, sizeof(short) * nrOfSamples);
289 }
290
291 nmbrOfFilledBuffers =
292 (short)WebRtc_available_read(aecm->farendBuf) / FRAME_LEN;
293 // The AECM is in the start up mode
294 // AECM is disabled until the soundcard buffer and farend buffers are OK
295
296 // Mechanism to ensure that the soundcard buffer is reasonably stable.
297 if (aecm->checkBuffSize) {
298 aecm->checkBufSizeCtr++;
299 // Before we fill up the far end buffer we require the amount of data on
300 // the sound card to be stable (+/-8 ms) compared to the first value. This
301 // comparison is made during the following 4 consecutive frames. If it
302 // seems to be stable then we start to fill up the far end buffer.
303
304 if (aecm->counter == 0) {
305 aecm->firstVal = aecm->msInSndCardBuf;
306 aecm->sum = 0;
307 }
308
309 if (abs(aecm->firstVal - aecm->msInSndCardBuf) <
310 WEBRTC_SPL_MAX(0.2 * aecm->msInSndCardBuf, kSampMsNb)) {
311 aecm->sum += aecm->msInSndCardBuf;
312 aecm->counter++;
313 } else {
314 aecm->counter = 0;
315 }
316
317 if (aecm->counter * nBlocks10ms >= 6) {
318 // The farend buffer size is determined in blocks of 80 samples
319 // Use 75% of the average value of the soundcard buffer
320 aecm->bufSizeStart = WEBRTC_SPL_MIN(
321 (3 * aecm->sum * aecm->aecmCore->mult) / (aecm->counter * 40),
322 BUF_SIZE_FRAMES);
323 // buffersize has now been determined
324 aecm->checkBuffSize = 0;
325 }
326
327 if (aecm->checkBufSizeCtr * nBlocks10ms > 50) {
328 // for really bad sound cards, don't disable echocanceller for more than
329 // 0.5 sec
330 aecm->bufSizeStart = WEBRTC_SPL_MIN(
331 (3 * aecm->msInSndCardBuf * aecm->aecmCore->mult) / 40,
332 BUF_SIZE_FRAMES);
333 aecm->checkBuffSize = 0;
334 }
335 }
336
337 // if checkBuffSize changed in the if-statement above
338 if (!aecm->checkBuffSize) {
339 // soundcard buffer is now reasonably stable
340 // When the far end buffer is filled with approximately the same amount of
341 // data as the amount on the sound card we end the start up phase and
342 // start to cancel echoes.
343
344 if (nmbrOfFilledBuffers == aecm->bufSizeStart) {
345 aecm->ECstartup = 0; // Enable the AECM
346 } else if (nmbrOfFilledBuffers > aecm->bufSizeStart) {
347 WebRtc_MoveReadPtr(aecm->farendBuf,
348 (int)WebRtc_available_read(aecm->farendBuf) -
349 (int)aecm->bufSizeStart * FRAME_LEN);
350 aecm->ECstartup = 0;
351 }
352 }
353
354 } else {
355 // AECM is enabled
356
357 // Note only 1 block supported for nb and 2 blocks for wb
358 for (i = 0; i < nFrames; i++) {
359 int16_t farend[FRAME_LEN];
360 const int16_t* farend_ptr = NULL;
361
362 nmbrOfFilledBuffers =
363 (short)WebRtc_available_read(aecm->farendBuf) / FRAME_LEN;
364
365 // Check that there is data in the far end buffer
366 if (nmbrOfFilledBuffers > 0) {
367 // Get the next 80 samples from the farend buffer
368 WebRtc_ReadBuffer(aecm->farendBuf, (void**)&farend_ptr, farend,
369 FRAME_LEN);
370
371 // Always store the last frame for use when we run out of data
372 memcpy(&(aecm->farendOld[i][0]), farend_ptr, FRAME_LEN * sizeof(short));
373 } else {
374 // We have no data so we use the last played frame
375 memcpy(farend, &(aecm->farendOld[i][0]), FRAME_LEN * sizeof(short));
376 farend_ptr = farend;
377 }
378
379 // Call buffer delay estimator when all data is extracted,
380 // i,e. i = 0 for NB and i = 1 for WB
381 if ((i == 0 && aecm->sampFreq == 8000) ||
382 (i == 1 && aecm->sampFreq == 16000)) {
383 WebRtcAecm_EstBufDelay(aecm, aecm->msInSndCardBuf);
384 }
385
386 // Call the AECM
387 /*WebRtcAecm_ProcessFrame(aecm->aecmCore, farend, &nearend[FRAME_LEN * i],
388 &out[FRAME_LEN * i], aecm->knownDelay);*/
389 if (WebRtcAecm_ProcessFrame(
390 aecm->aecmCore, farend_ptr, &nearendNoisy[FRAME_LEN * i],
391 (nearendClean ? &nearendClean[FRAME_LEN * i] : NULL),
392 &out[FRAME_LEN * i]) == -1)
niklase@google.com470e71d2011-07-07 08:21:25 +0000393 return -1;
394 }
Yves Gerey665174f2018-06-19 15:03:05 +0200395 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000396
397#ifdef AEC_DEBUG
Yves Gerey665174f2018-06-19 15:03:05 +0200398 msInAECBuf = (short)WebRtc_available_read(aecm->farendBuf) /
399 (kSampMsNb * aecm->aecmCore->mult);
400 fwrite(&msInAECBuf, 2, 1, aecm->bufFile);
401 fwrite(&(aecm->knownDelay), sizeof(aecm->knownDelay), 1, aecm->delayFile);
niklase@google.com470e71d2011-07-07 08:21:25 +0000402#endif
403
Yves Gerey665174f2018-06-19 15:03:05 +0200404 return retVal;
niklase@google.com470e71d2011-07-07 08:21:25 +0000405}
406
Yves Gerey665174f2018-06-19 15:03:05 +0200407int32_t WebRtcAecm_set_config(void* aecmInst, AecmConfig config) {
408 AecMobile* aecm = static_cast<AecMobile*>(aecmInst);
niklase@google.com470e71d2011-07-07 08:21:25 +0000409
Yves Gerey665174f2018-06-19 15:03:05 +0200410 if (aecm == NULL) {
411 return -1;
412 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000413
Yves Gerey665174f2018-06-19 15:03:05 +0200414 if (aecm->initFlag != kInitCheck) {
415 return AECM_UNINITIALIZED_ERROR;
416 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000417
Yves Gerey665174f2018-06-19 15:03:05 +0200418 if (config.cngMode != AecmFalse && config.cngMode != AecmTrue) {
419 return AECM_BAD_PARAMETER_ERROR;
420 }
421 aecm->aecmCore->cngMode = config.cngMode;
niklase@google.com470e71d2011-07-07 08:21:25 +0000422
Yves Gerey665174f2018-06-19 15:03:05 +0200423 if (config.echoMode < 0 || config.echoMode > 4) {
424 return AECM_BAD_PARAMETER_ERROR;
425 }
426 aecm->echoMode = config.echoMode;
niklase@google.com470e71d2011-07-07 08:21:25 +0000427
Yves Gerey665174f2018-06-19 15:03:05 +0200428 if (aecm->echoMode == 0) {
429 aecm->aecmCore->supGain = SUPGAIN_DEFAULT >> 3;
430 aecm->aecmCore->supGainOld = SUPGAIN_DEFAULT >> 3;
431 aecm->aecmCore->supGainErrParamA = SUPGAIN_ERROR_PARAM_A >> 3;
432 aecm->aecmCore->supGainErrParamD = SUPGAIN_ERROR_PARAM_D >> 3;
433 aecm->aecmCore->supGainErrParamDiffAB =
434 (SUPGAIN_ERROR_PARAM_A >> 3) - (SUPGAIN_ERROR_PARAM_B >> 3);
435 aecm->aecmCore->supGainErrParamDiffBD =
436 (SUPGAIN_ERROR_PARAM_B >> 3) - (SUPGAIN_ERROR_PARAM_D >> 3);
437 } else if (aecm->echoMode == 1) {
438 aecm->aecmCore->supGain = SUPGAIN_DEFAULT >> 2;
439 aecm->aecmCore->supGainOld = SUPGAIN_DEFAULT >> 2;
440 aecm->aecmCore->supGainErrParamA = SUPGAIN_ERROR_PARAM_A >> 2;
441 aecm->aecmCore->supGainErrParamD = SUPGAIN_ERROR_PARAM_D >> 2;
442 aecm->aecmCore->supGainErrParamDiffAB =
443 (SUPGAIN_ERROR_PARAM_A >> 2) - (SUPGAIN_ERROR_PARAM_B >> 2);
444 aecm->aecmCore->supGainErrParamDiffBD =
445 (SUPGAIN_ERROR_PARAM_B >> 2) - (SUPGAIN_ERROR_PARAM_D >> 2);
446 } else if (aecm->echoMode == 2) {
447 aecm->aecmCore->supGain = SUPGAIN_DEFAULT >> 1;
448 aecm->aecmCore->supGainOld = SUPGAIN_DEFAULT >> 1;
449 aecm->aecmCore->supGainErrParamA = SUPGAIN_ERROR_PARAM_A >> 1;
450 aecm->aecmCore->supGainErrParamD = SUPGAIN_ERROR_PARAM_D >> 1;
451 aecm->aecmCore->supGainErrParamDiffAB =
452 (SUPGAIN_ERROR_PARAM_A >> 1) - (SUPGAIN_ERROR_PARAM_B >> 1);
453 aecm->aecmCore->supGainErrParamDiffBD =
454 (SUPGAIN_ERROR_PARAM_B >> 1) - (SUPGAIN_ERROR_PARAM_D >> 1);
455 } else if (aecm->echoMode == 3) {
456 aecm->aecmCore->supGain = SUPGAIN_DEFAULT;
457 aecm->aecmCore->supGainOld = SUPGAIN_DEFAULT;
458 aecm->aecmCore->supGainErrParamA = SUPGAIN_ERROR_PARAM_A;
459 aecm->aecmCore->supGainErrParamD = SUPGAIN_ERROR_PARAM_D;
460 aecm->aecmCore->supGainErrParamDiffAB =
461 SUPGAIN_ERROR_PARAM_A - SUPGAIN_ERROR_PARAM_B;
462 aecm->aecmCore->supGainErrParamDiffBD =
463 SUPGAIN_ERROR_PARAM_B - SUPGAIN_ERROR_PARAM_D;
464 } else if (aecm->echoMode == 4) {
465 aecm->aecmCore->supGain = SUPGAIN_DEFAULT << 1;
466 aecm->aecmCore->supGainOld = SUPGAIN_DEFAULT << 1;
467 aecm->aecmCore->supGainErrParamA = SUPGAIN_ERROR_PARAM_A << 1;
468 aecm->aecmCore->supGainErrParamD = SUPGAIN_ERROR_PARAM_D << 1;
469 aecm->aecmCore->supGainErrParamDiffAB =
470 (SUPGAIN_ERROR_PARAM_A << 1) - (SUPGAIN_ERROR_PARAM_B << 1);
471 aecm->aecmCore->supGainErrParamDiffBD =
472 (SUPGAIN_ERROR_PARAM_B << 1) - (SUPGAIN_ERROR_PARAM_D << 1);
473 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000474
Yves Gerey665174f2018-06-19 15:03:05 +0200475 return 0;
niklase@google.com470e71d2011-07-07 08:21:25 +0000476}
477
pbos@webrtc.orgb7192b82013-04-10 07:50:54 +0000478int32_t WebRtcAecm_InitEchoPath(void* aecmInst,
479 const void* echo_path,
Yves Gerey665174f2018-06-19 15:03:05 +0200480 size_t size_bytes) {
481 AecMobile* aecm = static_cast<AecMobile*>(aecmInst);
482 const int16_t* echo_path_ptr = static_cast<const int16_t*>(echo_path);
bjornv@google.comc4b939c2011-07-13 08:09:56 +0000483
Yves Gerey665174f2018-06-19 15:03:05 +0200484 if (aecmInst == NULL) {
485 return -1;
486 }
487 if (echo_path == NULL) {
488 return AECM_NULL_POINTER_ERROR;
489 }
490 if (size_bytes != WebRtcAecm_echo_path_size_bytes()) {
491 // Input channel size does not match the size of AECM
492 return AECM_BAD_PARAMETER_ERROR;
493 }
494 if (aecm->initFlag != kInitCheck) {
495 return AECM_UNINITIALIZED_ERROR;
496 }
bjornv@google.comc4b939c2011-07-13 08:09:56 +0000497
Yves Gerey665174f2018-06-19 15:03:05 +0200498 WebRtcAecm_InitEchoPathCore(aecm->aecmCore, echo_path_ptr);
bjornv@google.comc4b939c2011-07-13 08:09:56 +0000499
Yves Gerey665174f2018-06-19 15:03:05 +0200500 return 0;
bjornv@google.comc4b939c2011-07-13 08:09:56 +0000501}
502
pbos@webrtc.orgb7192b82013-04-10 07:50:54 +0000503int32_t WebRtcAecm_GetEchoPath(void* aecmInst,
504 void* echo_path,
Yves Gerey665174f2018-06-19 15:03:05 +0200505 size_t size_bytes) {
506 AecMobile* aecm = static_cast<AecMobile*>(aecmInst);
507 int16_t* echo_path_ptr = static_cast<int16_t*>(echo_path);
bjornv@google.comc4b939c2011-07-13 08:09:56 +0000508
Yves Gerey665174f2018-06-19 15:03:05 +0200509 if (aecmInst == NULL) {
510 return -1;
511 }
512 if (echo_path == NULL) {
513 return AECM_NULL_POINTER_ERROR;
514 }
515 if (size_bytes != WebRtcAecm_echo_path_size_bytes()) {
516 // Input channel size does not match the size of AECM
517 return AECM_BAD_PARAMETER_ERROR;
518 }
519 if (aecm->initFlag != kInitCheck) {
520 return AECM_UNINITIALIZED_ERROR;
521 }
bjornv@google.comc4b939c2011-07-13 08:09:56 +0000522
Yves Gerey665174f2018-06-19 15:03:05 +0200523 memcpy(echo_path_ptr, aecm->aecmCore->channelStored, size_bytes);
524 return 0;
bjornv@google.comc4b939c2011-07-13 08:09:56 +0000525}
526
Yves Gerey665174f2018-06-19 15:03:05 +0200527size_t WebRtcAecm_echo_path_size_bytes() {
528 return (PART_LEN1 * sizeof(int16_t));
bjornv@google.comc4b939c2011-07-13 08:09:56 +0000529}
530
pbos@webrtc.orge468bc92014-12-18 09:11:33 +0000531static int WebRtcAecm_EstBufDelay(AecMobile* aecm, short msInSndCardBuf) {
Yves Gerey665174f2018-06-19 15:03:05 +0200532 short delayNew, nSampSndCard;
533 short nSampFar = (short)WebRtc_available_read(aecm->farendBuf);
534 short diff;
niklase@google.com470e71d2011-07-07 08:21:25 +0000535
Yves Gerey665174f2018-06-19 15:03:05 +0200536 nSampSndCard = msInSndCardBuf * kSampMsNb * aecm->aecmCore->mult;
niklase@google.com470e71d2011-07-07 08:21:25 +0000537
Yves Gerey665174f2018-06-19 15:03:05 +0200538 delayNew = nSampSndCard - nSampFar;
niklase@google.com470e71d2011-07-07 08:21:25 +0000539
Yves Gerey665174f2018-06-19 15:03:05 +0200540 if (delayNew < FRAME_LEN) {
541 WebRtc_MoveReadPtr(aecm->farendBuf, FRAME_LEN);
542 delayNew += FRAME_LEN;
543 }
544
545 aecm->filtDelay =
546 WEBRTC_SPL_MAX(0, (8 * aecm->filtDelay + 2 * delayNew) / 10);
547
548 diff = aecm->filtDelay - aecm->knownDelay;
549 if (diff > 224) {
550 if (aecm->lastDelayDiff < 96) {
551 aecm->timeForDelayChange = 0;
552 } else {
553 aecm->timeForDelayChange++;
niklase@google.com470e71d2011-07-07 08:21:25 +0000554 }
Yves Gerey665174f2018-06-19 15:03:05 +0200555 } else if (diff < 96 && aecm->knownDelay > 0) {
556 if (aecm->lastDelayDiff > 224) {
557 aecm->timeForDelayChange = 0;
558 } else {
559 aecm->timeForDelayChange++;
niklase@google.com470e71d2011-07-07 08:21:25 +0000560 }
Yves Gerey665174f2018-06-19 15:03:05 +0200561 } else {
562 aecm->timeForDelayChange = 0;
563 }
564 aecm->lastDelayDiff = diff;
niklase@google.com470e71d2011-07-07 08:21:25 +0000565
Yves Gerey665174f2018-06-19 15:03:05 +0200566 if (aecm->timeForDelayChange > 25) {
567 aecm->knownDelay = WEBRTC_SPL_MAX((int)aecm->filtDelay - 160, 0);
568 }
569 return 0;
niklase@google.com470e71d2011-07-07 08:21:25 +0000570}
571
pbos@webrtc.orge468bc92014-12-18 09:11:33 +0000572static int WebRtcAecm_DelayComp(AecMobile* aecm) {
Yves Gerey665174f2018-06-19 15:03:05 +0200573 int nSampFar = (int)WebRtc_available_read(aecm->farendBuf);
574 int nSampSndCard, delayNew, nSampAdd;
575 const int maxStuffSamp = 10 * FRAME_LEN;
niklase@google.com470e71d2011-07-07 08:21:25 +0000576
Yves Gerey665174f2018-06-19 15:03:05 +0200577 nSampSndCard = aecm->msInSndCardBuf * kSampMsNb * aecm->aecmCore->mult;
578 delayNew = nSampSndCard - nSampFar;
niklase@google.com470e71d2011-07-07 08:21:25 +0000579
Yves Gerey665174f2018-06-19 15:03:05 +0200580 if (delayNew > FAR_BUF_LEN - FRAME_LEN * aecm->aecmCore->mult) {
581 // The difference of the buffer sizes is larger than the maximum
582 // allowed known delay. Compensate by stuffing the buffer.
583 nSampAdd =
584 (int)(WEBRTC_SPL_MAX(((nSampSndCard >> 1) - nSampFar), FRAME_LEN));
585 nSampAdd = WEBRTC_SPL_MIN(nSampAdd, maxStuffSamp);
niklase@google.com470e71d2011-07-07 08:21:25 +0000586
Yves Gerey665174f2018-06-19 15:03:05 +0200587 WebRtc_MoveReadPtr(aecm->farendBuf, -nSampAdd);
588 aecm->delayChange = 1; // the delay needs to be updated
589 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000590
Yves Gerey665174f2018-06-19 15:03:05 +0200591 return 0;
niklase@google.com470e71d2011-07-07 08:21:25 +0000592}