webrtc/modules/audio_processing/aecm/aecm_core.cc
Alex Loiko 600bdb4adc Undefined shifts.
This change

* replaces a left shift with multiplication, because the shiftee can
  be negative.

* replaces a right shift (a >> b) with the expression (b >= 32 ? 0 : a >> b)
  because a is a 32-bit value, and b can be >= 32.

cppreference quote relating to the second change:
"In any case, if the value of the right operand is
negative or is greater or equal to the number of bits in the promoted
left operand, the behavior is undefined."


Bug: chromium:805832 chromium:803078
Change-Id: I67db0c3fedb0af197b2205d424414a84f8fde474
Reviewed-on: https://webrtc-review.googlesource.com/43761
Reviewed-by: Oskar Sundbom <ossu@webrtc.org>
Commit-Queue: Alex Loiko <aleloi@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#21760}
2018-01-25 12:26:51 +00:00

1238 lines
46 KiB
C++

/*
* Copyright (c) 2012 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 "modules/audio_processing/aecm/aecm_core.h"
#include <stddef.h>
#include <stdlib.h>
extern "C" {
#include "common_audio/ring_buffer.h"
#include "common_audio/signal_processing/include/real_fft.h"
}
#include "modules/audio_processing/aecm/echo_control_mobile.h"
#include "modules/audio_processing/utility/delay_estimator_wrapper.h"
extern "C" {
#include "system_wrappers/include/cpu_features_wrapper.h"
}
#include "rtc_base/checks.h"
#include "rtc_base/numerics/safe_conversions.h"
#include "typedefs.h" // NOLINT(build/include)
#ifdef AEC_DEBUG
FILE *dfile;
FILE *testfile;
#endif
const int16_t WebRtcAecm_kCosTable[] = {
8192, 8190, 8187, 8180, 8172, 8160, 8147, 8130, 8112,
8091, 8067, 8041, 8012, 7982, 7948, 7912, 7874, 7834,
7791, 7745, 7697, 7647, 7595, 7540, 7483, 7424, 7362,
7299, 7233, 7164, 7094, 7021, 6947, 6870, 6791, 6710,
6627, 6542, 6455, 6366, 6275, 6182, 6087, 5991, 5892,
5792, 5690, 5586, 5481, 5374, 5265, 5155, 5043, 4930,
4815, 4698, 4580, 4461, 4341, 4219, 4096, 3971, 3845,
3719, 3591, 3462, 3331, 3200, 3068, 2935, 2801, 2667,
2531, 2395, 2258, 2120, 1981, 1842, 1703, 1563, 1422,
1281, 1140, 998, 856, 713, 571, 428, 285, 142,
0, -142, -285, -428, -571, -713, -856, -998, -1140,
-1281, -1422, -1563, -1703, -1842, -1981, -2120, -2258, -2395,
-2531, -2667, -2801, -2935, -3068, -3200, -3331, -3462, -3591,
-3719, -3845, -3971, -4095, -4219, -4341, -4461, -4580, -4698,
-4815, -4930, -5043, -5155, -5265, -5374, -5481, -5586, -5690,
-5792, -5892, -5991, -6087, -6182, -6275, -6366, -6455, -6542,
-6627, -6710, -6791, -6870, -6947, -7021, -7094, -7164, -7233,
-7299, -7362, -7424, -7483, -7540, -7595, -7647, -7697, -7745,
-7791, -7834, -7874, -7912, -7948, -7982, -8012, -8041, -8067,
-8091, -8112, -8130, -8147, -8160, -8172, -8180, -8187, -8190,
-8191, -8190, -8187, -8180, -8172, -8160, -8147, -8130, -8112,
-8091, -8067, -8041, -8012, -7982, -7948, -7912, -7874, -7834,
-7791, -7745, -7697, -7647, -7595, -7540, -7483, -7424, -7362,
-7299, -7233, -7164, -7094, -7021, -6947, -6870, -6791, -6710,
-6627, -6542, -6455, -6366, -6275, -6182, -6087, -5991, -5892,
-5792, -5690, -5586, -5481, -5374, -5265, -5155, -5043, -4930,
-4815, -4698, -4580, -4461, -4341, -4219, -4096, -3971, -3845,
-3719, -3591, -3462, -3331, -3200, -3068, -2935, -2801, -2667,
-2531, -2395, -2258, -2120, -1981, -1842, -1703, -1563, -1422,
-1281, -1140, -998, -856, -713, -571, -428, -285, -142,
0, 142, 285, 428, 571, 713, 856, 998, 1140,
1281, 1422, 1563, 1703, 1842, 1981, 2120, 2258, 2395,
2531, 2667, 2801, 2935, 3068, 3200, 3331, 3462, 3591,
3719, 3845, 3971, 4095, 4219, 4341, 4461, 4580, 4698,
4815, 4930, 5043, 5155, 5265, 5374, 5481, 5586, 5690,
5792, 5892, 5991, 6087, 6182, 6275, 6366, 6455, 6542,
6627, 6710, 6791, 6870, 6947, 7021, 7094, 7164, 7233,
7299, 7362, 7424, 7483, 7540, 7595, 7647, 7697, 7745,
7791, 7834, 7874, 7912, 7948, 7982, 8012, 8041, 8067,
8091, 8112, 8130, 8147, 8160, 8172, 8180, 8187, 8190
};
const int16_t WebRtcAecm_kSinTable[] = {
0, 142, 285, 428, 571, 713, 856, 998,
1140, 1281, 1422, 1563, 1703, 1842, 1981, 2120,
2258, 2395, 2531, 2667, 2801, 2935, 3068, 3200,
3331, 3462, 3591, 3719, 3845, 3971, 4095, 4219,
4341, 4461, 4580, 4698, 4815, 4930, 5043, 5155,
5265, 5374, 5481, 5586, 5690, 5792, 5892, 5991,
6087, 6182, 6275, 6366, 6455, 6542, 6627, 6710,
6791, 6870, 6947, 7021, 7094, 7164, 7233, 7299,
7362, 7424, 7483, 7540, 7595, 7647, 7697, 7745,
7791, 7834, 7874, 7912, 7948, 7982, 8012, 8041,
8067, 8091, 8112, 8130, 8147, 8160, 8172, 8180,
8187, 8190, 8191, 8190, 8187, 8180, 8172, 8160,
8147, 8130, 8112, 8091, 8067, 8041, 8012, 7982,
7948, 7912, 7874, 7834, 7791, 7745, 7697, 7647,
7595, 7540, 7483, 7424, 7362, 7299, 7233, 7164,
7094, 7021, 6947, 6870, 6791, 6710, 6627, 6542,
6455, 6366, 6275, 6182, 6087, 5991, 5892, 5792,
5690, 5586, 5481, 5374, 5265, 5155, 5043, 4930,
4815, 4698, 4580, 4461, 4341, 4219, 4096, 3971,
3845, 3719, 3591, 3462, 3331, 3200, 3068, 2935,
2801, 2667, 2531, 2395, 2258, 2120, 1981, 1842,
1703, 1563, 1422, 1281, 1140, 998, 856, 713,
571, 428, 285, 142, 0, -142, -285, -428,
-571, -713, -856, -998, -1140, -1281, -1422, -1563,
-1703, -1842, -1981, -2120, -2258, -2395, -2531, -2667,
-2801, -2935, -3068, -3200, -3331, -3462, -3591, -3719,
-3845, -3971, -4095, -4219, -4341, -4461, -4580, -4698,
-4815, -4930, -5043, -5155, -5265, -5374, -5481, -5586,
-5690, -5792, -5892, -5991, -6087, -6182, -6275, -6366,
-6455, -6542, -6627, -6710, -6791, -6870, -6947, -7021,
-7094, -7164, -7233, -7299, -7362, -7424, -7483, -7540,
-7595, -7647, -7697, -7745, -7791, -7834, -7874, -7912,
-7948, -7982, -8012, -8041, -8067, -8091, -8112, -8130,
-8147, -8160, -8172, -8180, -8187, -8190, -8191, -8190,
-8187, -8180, -8172, -8160, -8147, -8130, -8112, -8091,
-8067, -8041, -8012, -7982, -7948, -7912, -7874, -7834,
-7791, -7745, -7697, -7647, -7595, -7540, -7483, -7424,
-7362, -7299, -7233, -7164, -7094, -7021, -6947, -6870,
-6791, -6710, -6627, -6542, -6455, -6366, -6275, -6182,
-6087, -5991, -5892, -5792, -5690, -5586, -5481, -5374,
-5265, -5155, -5043, -4930, -4815, -4698, -4580, -4461,
-4341, -4219, -4096, -3971, -3845, -3719, -3591, -3462,
-3331, -3200, -3068, -2935, -2801, -2667, -2531, -2395,
-2258, -2120, -1981, -1842, -1703, -1563, -1422, -1281,
-1140, -998, -856, -713, -571, -428, -285, -142
};
// Initialization table for echo channel in 8 kHz
static const int16_t kChannelStored8kHz[PART_LEN1] = {
2040, 1815, 1590, 1498, 1405, 1395, 1385, 1418,
1451, 1506, 1562, 1644, 1726, 1804, 1882, 1918,
1953, 1982, 2010, 2025, 2040, 2034, 2027, 2021,
2014, 1997, 1980, 1925, 1869, 1800, 1732, 1683,
1635, 1604, 1572, 1545, 1517, 1481, 1444, 1405,
1367, 1331, 1294, 1270, 1245, 1239, 1233, 1247,
1260, 1282, 1303, 1338, 1373, 1407, 1441, 1470,
1499, 1524, 1549, 1565, 1582, 1601, 1621, 1649,
1676
};
// Initialization table for echo channel in 16 kHz
static const int16_t kChannelStored16kHz[PART_LEN1] = {
2040, 1590, 1405, 1385, 1451, 1562, 1726, 1882,
1953, 2010, 2040, 2027, 2014, 1980, 1869, 1732,
1635, 1572, 1517, 1444, 1367, 1294, 1245, 1233,
1260, 1303, 1373, 1441, 1499, 1549, 1582, 1621,
1676, 1741, 1802, 1861, 1921, 1983, 2040, 2102,
2170, 2265, 2375, 2515, 2651, 2781, 2922, 3075,
3253, 3471, 3738, 3976, 4151, 4258, 4308, 4288,
4270, 4253, 4237, 4179, 4086, 3947, 3757, 3484,
3153
};
// Moves the pointer to the next entry and inserts |far_spectrum| and
// corresponding Q-domain in its buffer.
//
// Inputs:
// - self : Pointer to the delay estimation instance
// - far_spectrum : Pointer to the far end spectrum
// - far_q : Q-domain of far end spectrum
//
void WebRtcAecm_UpdateFarHistory(AecmCore* self,
uint16_t* far_spectrum,
int far_q) {
// Get new buffer position
self->far_history_pos++;
if (self->far_history_pos >= MAX_DELAY) {
self->far_history_pos = 0;
}
// Update Q-domain buffer
self->far_q_domains[self->far_history_pos] = far_q;
// Update far end spectrum buffer
memcpy(&(self->far_history[self->far_history_pos * PART_LEN1]),
far_spectrum,
sizeof(uint16_t) * PART_LEN1);
}
// Returns a pointer to the far end spectrum aligned to current near end
// spectrum. The function WebRtc_DelayEstimatorProcessFix(...) should have been
// called before AlignedFarend(...). Otherwise, you get the pointer to the
// previous frame. The memory is only valid until the next call of
// WebRtc_DelayEstimatorProcessFix(...).
//
// Inputs:
// - self : Pointer to the AECM instance.
// - delay : Current delay estimate.
//
// Output:
// - far_q : The Q-domain of the aligned far end spectrum
//
// Return value:
// - far_spectrum : Pointer to the aligned far end spectrum
// NULL - Error
//
const uint16_t* WebRtcAecm_AlignedFarend(AecmCore* self,
int* far_q,
int delay) {
int buffer_position = 0;
RTC_DCHECK(self);
buffer_position = self->far_history_pos - delay;
// Check buffer position
if (buffer_position < 0) {
buffer_position += MAX_DELAY;
}
// Get Q-domain
*far_q = self->far_q_domains[buffer_position];
// Return far end spectrum
return &(self->far_history[buffer_position * PART_LEN1]);
}
// Declare function pointers.
CalcLinearEnergies WebRtcAecm_CalcLinearEnergies;
StoreAdaptiveChannel WebRtcAecm_StoreAdaptiveChannel;
ResetAdaptiveChannel WebRtcAecm_ResetAdaptiveChannel;
AecmCore* WebRtcAecm_CreateCore() {
AecmCore* aecm = static_cast<AecmCore*>(malloc(sizeof(AecmCore)));
aecm->farFrameBuf = WebRtc_CreateBuffer(FRAME_LEN + PART_LEN,
sizeof(int16_t));
if (!aecm->farFrameBuf)
{
WebRtcAecm_FreeCore(aecm);
return NULL;
}
aecm->nearNoisyFrameBuf = WebRtc_CreateBuffer(FRAME_LEN + PART_LEN,
sizeof(int16_t));
if (!aecm->nearNoisyFrameBuf)
{
WebRtcAecm_FreeCore(aecm);
return NULL;
}
aecm->nearCleanFrameBuf = WebRtc_CreateBuffer(FRAME_LEN + PART_LEN,
sizeof(int16_t));
if (!aecm->nearCleanFrameBuf)
{
WebRtcAecm_FreeCore(aecm);
return NULL;
}
aecm->outFrameBuf = WebRtc_CreateBuffer(FRAME_LEN + PART_LEN,
sizeof(int16_t));
if (!aecm->outFrameBuf)
{
WebRtcAecm_FreeCore(aecm);
return NULL;
}
aecm->delay_estimator_farend = WebRtc_CreateDelayEstimatorFarend(PART_LEN1,
MAX_DELAY);
if (aecm->delay_estimator_farend == NULL) {
WebRtcAecm_FreeCore(aecm);
return NULL;
}
aecm->delay_estimator =
WebRtc_CreateDelayEstimator(aecm->delay_estimator_farend, 0);
if (aecm->delay_estimator == NULL) {
WebRtcAecm_FreeCore(aecm);
return NULL;
}
// TODO(bjornv): Explicitly disable robust delay validation until no
// performance regression has been established. Then remove the line.
WebRtc_enable_robust_validation(aecm->delay_estimator, 0);
aecm->real_fft = WebRtcSpl_CreateRealFFT(PART_LEN_SHIFT);
if (aecm->real_fft == NULL) {
WebRtcAecm_FreeCore(aecm);
return NULL;
}
// Init some aecm pointers. 16 and 32 byte alignment is only necessary
// for Neon code currently.
aecm->xBuf = (int16_t*) (((uintptr_t)aecm->xBuf_buf + 31) & ~ 31);
aecm->dBufClean = (int16_t*) (((uintptr_t)aecm->dBufClean_buf + 31) & ~ 31);
aecm->dBufNoisy = (int16_t*) (((uintptr_t)aecm->dBufNoisy_buf + 31) & ~ 31);
aecm->outBuf = (int16_t*) (((uintptr_t)aecm->outBuf_buf + 15) & ~ 15);
aecm->channelStored = (int16_t*) (((uintptr_t)
aecm->channelStored_buf + 15) & ~ 15);
aecm->channelAdapt16 = (int16_t*) (((uintptr_t)
aecm->channelAdapt16_buf + 15) & ~ 15);
aecm->channelAdapt32 = (int32_t*) (((uintptr_t)
aecm->channelAdapt32_buf + 31) & ~ 31);
return aecm;
}
void WebRtcAecm_InitEchoPathCore(AecmCore* aecm, const int16_t* echo_path) {
int i = 0;
// Reset the stored channel
memcpy(aecm->channelStored, echo_path, sizeof(int16_t) * PART_LEN1);
// Reset the adapted channels
memcpy(aecm->channelAdapt16, echo_path, sizeof(int16_t) * PART_LEN1);
for (i = 0; i < PART_LEN1; i++)
{
aecm->channelAdapt32[i] = (int32_t)aecm->channelAdapt16[i] << 16;
}
// Reset channel storing variables
aecm->mseAdaptOld = 1000;
aecm->mseStoredOld = 1000;
aecm->mseThreshold = WEBRTC_SPL_WORD32_MAX;
aecm->mseChannelCount = 0;
}
static void CalcLinearEnergiesC(AecmCore* aecm,
const uint16_t* far_spectrum,
int32_t* echo_est,
uint32_t* far_energy,
uint32_t* echo_energy_adapt,
uint32_t* echo_energy_stored) {
int i;
// Get energy for the delayed far end signal and estimated
// echo using both stored and adapted channels.
for (i = 0; i < PART_LEN1; i++)
{
echo_est[i] = WEBRTC_SPL_MUL_16_U16(aecm->channelStored[i],
far_spectrum[i]);
(*far_energy) += (uint32_t)(far_spectrum[i]);
*echo_energy_adapt += aecm->channelAdapt16[i] * far_spectrum[i];
(*echo_energy_stored) += (uint32_t)echo_est[i];
}
}
static void StoreAdaptiveChannelC(AecmCore* aecm,
const uint16_t* far_spectrum,
int32_t* echo_est) {
int i;
// During startup we store the channel every block.
memcpy(aecm->channelStored, aecm->channelAdapt16, sizeof(int16_t) * PART_LEN1);
// Recalculate echo estimate
for (i = 0; i < PART_LEN; i += 4)
{
echo_est[i] = WEBRTC_SPL_MUL_16_U16(aecm->channelStored[i],
far_spectrum[i]);
echo_est[i + 1] = WEBRTC_SPL_MUL_16_U16(aecm->channelStored[i + 1],
far_spectrum[i + 1]);
echo_est[i + 2] = WEBRTC_SPL_MUL_16_U16(aecm->channelStored[i + 2],
far_spectrum[i + 2]);
echo_est[i + 3] = WEBRTC_SPL_MUL_16_U16(aecm->channelStored[i + 3],
far_spectrum[i + 3]);
}
echo_est[i] = WEBRTC_SPL_MUL_16_U16(aecm->channelStored[i],
far_spectrum[i]);
}
static void ResetAdaptiveChannelC(AecmCore* aecm) {
int i;
// The stored channel has a significantly lower MSE than the adaptive one for
// two consecutive calculations. Reset the adaptive channel.
memcpy(aecm->channelAdapt16, aecm->channelStored,
sizeof(int16_t) * PART_LEN1);
// Restore the W32 channel
for (i = 0; i < PART_LEN; i += 4)
{
aecm->channelAdapt32[i] = (int32_t)aecm->channelStored[i] << 16;
aecm->channelAdapt32[i + 1] = (int32_t)aecm->channelStored[i + 1] << 16;
aecm->channelAdapt32[i + 2] = (int32_t)aecm->channelStored[i + 2] << 16;
aecm->channelAdapt32[i + 3] = (int32_t)aecm->channelStored[i + 3] << 16;
}
aecm->channelAdapt32[i] = (int32_t)aecm->channelStored[i] << 16;
}
// Initialize function pointers for ARM Neon platform.
#if defined(WEBRTC_HAS_NEON)
static void WebRtcAecm_InitNeon(void)
{
WebRtcAecm_StoreAdaptiveChannel = WebRtcAecm_StoreAdaptiveChannelNeon;
WebRtcAecm_ResetAdaptiveChannel = WebRtcAecm_ResetAdaptiveChannelNeon;
WebRtcAecm_CalcLinearEnergies = WebRtcAecm_CalcLinearEnergiesNeon;
}
#endif
// Initialize function pointers for MIPS platform.
#if defined(MIPS32_LE)
static void WebRtcAecm_InitMips(void)
{
#if defined(MIPS_DSP_R1_LE)
WebRtcAecm_StoreAdaptiveChannel = WebRtcAecm_StoreAdaptiveChannel_mips;
WebRtcAecm_ResetAdaptiveChannel = WebRtcAecm_ResetAdaptiveChannel_mips;
#endif
WebRtcAecm_CalcLinearEnergies = WebRtcAecm_CalcLinearEnergies_mips;
}
#endif
// WebRtcAecm_InitCore(...)
//
// This function initializes the AECM instant created with WebRtcAecm_CreateCore(...)
// Input:
// - aecm : Pointer to the Echo Suppression instance
// - samplingFreq : Sampling Frequency
//
// Output:
// - aecm : Initialized instance
//
// Return value : 0 - Ok
// -1 - Error
//
int WebRtcAecm_InitCore(AecmCore* const aecm, int samplingFreq) {
int i = 0;
int32_t tmp32 = PART_LEN1 * PART_LEN1;
int16_t tmp16 = PART_LEN1;
if (samplingFreq != 8000 && samplingFreq != 16000)
{
samplingFreq = 8000;
return -1;
}
// sanity check of sampling frequency
aecm->mult = (int16_t)samplingFreq / 8000;
aecm->farBufWritePos = 0;
aecm->farBufReadPos = 0;
aecm->knownDelay = 0;
aecm->lastKnownDelay = 0;
WebRtc_InitBuffer(aecm->farFrameBuf);
WebRtc_InitBuffer(aecm->nearNoisyFrameBuf);
WebRtc_InitBuffer(aecm->nearCleanFrameBuf);
WebRtc_InitBuffer(aecm->outFrameBuf);
memset(aecm->xBuf_buf, 0, sizeof(aecm->xBuf_buf));
memset(aecm->dBufClean_buf, 0, sizeof(aecm->dBufClean_buf));
memset(aecm->dBufNoisy_buf, 0, sizeof(aecm->dBufNoisy_buf));
memset(aecm->outBuf_buf, 0, sizeof(aecm->outBuf_buf));
aecm->seed = 666;
aecm->totCount = 0;
if (WebRtc_InitDelayEstimatorFarend(aecm->delay_estimator_farend) != 0) {
return -1;
}
if (WebRtc_InitDelayEstimator(aecm->delay_estimator) != 0) {
return -1;
}
// Set far end histories to zero
memset(aecm->far_history, 0, sizeof(uint16_t) * PART_LEN1 * MAX_DELAY);
memset(aecm->far_q_domains, 0, sizeof(int) * MAX_DELAY);
aecm->far_history_pos = MAX_DELAY;
aecm->nlpFlag = 1;
aecm->fixedDelay = -1;
aecm->dfaCleanQDomain = 0;
aecm->dfaCleanQDomainOld = 0;
aecm->dfaNoisyQDomain = 0;
aecm->dfaNoisyQDomainOld = 0;
memset(aecm->nearLogEnergy, 0, sizeof(aecm->nearLogEnergy));
aecm->farLogEnergy = 0;
memset(aecm->echoAdaptLogEnergy, 0, sizeof(aecm->echoAdaptLogEnergy));
memset(aecm->echoStoredLogEnergy, 0, sizeof(aecm->echoStoredLogEnergy));
// Initialize the echo channels with a stored shape.
if (samplingFreq == 8000)
{
WebRtcAecm_InitEchoPathCore(aecm, kChannelStored8kHz);
}
else
{
WebRtcAecm_InitEchoPathCore(aecm, kChannelStored16kHz);
}
memset(aecm->echoFilt, 0, sizeof(aecm->echoFilt));
memset(aecm->nearFilt, 0, sizeof(aecm->nearFilt));
aecm->noiseEstCtr = 0;
aecm->cngMode = AecmTrue;
memset(aecm->noiseEstTooLowCtr, 0, sizeof(aecm->noiseEstTooLowCtr));
memset(aecm->noiseEstTooHighCtr, 0, sizeof(aecm->noiseEstTooHighCtr));
// Shape the initial noise level to an approximate pink noise.
for (i = 0; i < (PART_LEN1 >> 1) - 1; i++)
{
aecm->noiseEst[i] = (tmp32 << 8);
tmp16--;
tmp32 -= (int32_t)((tmp16 << 1) + 1);
}
for (; i < PART_LEN1; i++)
{
aecm->noiseEst[i] = (tmp32 << 8);
}
aecm->farEnergyMin = WEBRTC_SPL_WORD16_MAX;
aecm->farEnergyMax = WEBRTC_SPL_WORD16_MIN;
aecm->farEnergyMaxMin = 0;
aecm->farEnergyVAD = FAR_ENERGY_MIN; // This prevents false speech detection at the
// beginning.
aecm->farEnergyMSE = 0;
aecm->currentVADValue = 0;
aecm->vadUpdateCount = 0;
aecm->firstVAD = 1;
aecm->startupState = 0;
aecm->supGain = SUPGAIN_DEFAULT;
aecm->supGainOld = SUPGAIN_DEFAULT;
aecm->supGainErrParamA = SUPGAIN_ERROR_PARAM_A;
aecm->supGainErrParamD = SUPGAIN_ERROR_PARAM_D;
aecm->supGainErrParamDiffAB = SUPGAIN_ERROR_PARAM_A - SUPGAIN_ERROR_PARAM_B;
aecm->supGainErrParamDiffBD = SUPGAIN_ERROR_PARAM_B - SUPGAIN_ERROR_PARAM_D;
// Assert a preprocessor definition at compile-time. It's an assumption
// used in assembly code, so check the assembly files before any change.
static_assert(PART_LEN % 16 == 0, "PART_LEN is not a multiple of 16");
// Initialize function pointers.
WebRtcAecm_CalcLinearEnergies = CalcLinearEnergiesC;
WebRtcAecm_StoreAdaptiveChannel = StoreAdaptiveChannelC;
WebRtcAecm_ResetAdaptiveChannel = ResetAdaptiveChannelC;
#if defined(WEBRTC_HAS_NEON)
WebRtcAecm_InitNeon();
#endif
#if defined(MIPS32_LE)
WebRtcAecm_InitMips();
#endif
return 0;
}
// TODO(bjornv): This function is currently not used. Add support for these
// parameters from a higher level
int WebRtcAecm_Control(AecmCore* aecm, int delay, int nlpFlag) {
aecm->nlpFlag = nlpFlag;
aecm->fixedDelay = delay;
return 0;
}
void WebRtcAecm_FreeCore(AecmCore* aecm) {
if (aecm == NULL) {
return;
}
WebRtc_FreeBuffer(aecm->farFrameBuf);
WebRtc_FreeBuffer(aecm->nearNoisyFrameBuf);
WebRtc_FreeBuffer(aecm->nearCleanFrameBuf);
WebRtc_FreeBuffer(aecm->outFrameBuf);
WebRtc_FreeDelayEstimator(aecm->delay_estimator);
WebRtc_FreeDelayEstimatorFarend(aecm->delay_estimator_farend);
WebRtcSpl_FreeRealFFT(aecm->real_fft);
free(aecm);
}
int WebRtcAecm_ProcessFrame(AecmCore* aecm,
const int16_t* farend,
const int16_t* nearendNoisy,
const int16_t* nearendClean,
int16_t* out) {
int16_t outBlock_buf[PART_LEN + 8]; // Align buffer to 8-byte boundary.
int16_t* outBlock = (int16_t*) (((uintptr_t) outBlock_buf + 15) & ~ 15);
int16_t farFrame[FRAME_LEN];
const int16_t* out_ptr = NULL;
int size = 0;
// Buffer the current frame.
// Fetch an older one corresponding to the delay.
WebRtcAecm_BufferFarFrame(aecm, farend, FRAME_LEN);
WebRtcAecm_FetchFarFrame(aecm, farFrame, FRAME_LEN, aecm->knownDelay);
// Buffer the synchronized far and near frames,
// to pass the smaller blocks individually.
WebRtc_WriteBuffer(aecm->farFrameBuf, farFrame, FRAME_LEN);
WebRtc_WriteBuffer(aecm->nearNoisyFrameBuf, nearendNoisy, FRAME_LEN);
if (nearendClean != NULL)
{
WebRtc_WriteBuffer(aecm->nearCleanFrameBuf, nearendClean, FRAME_LEN);
}
// Process as many blocks as possible.
while (WebRtc_available_read(aecm->farFrameBuf) >= PART_LEN)
{
int16_t far_block[PART_LEN];
const int16_t* far_block_ptr = NULL;
int16_t near_noisy_block[PART_LEN];
const int16_t* near_noisy_block_ptr = NULL;
WebRtc_ReadBuffer(aecm->farFrameBuf, (void**) &far_block_ptr, far_block,
PART_LEN);
WebRtc_ReadBuffer(aecm->nearNoisyFrameBuf,
(void**) &near_noisy_block_ptr,
near_noisy_block,
PART_LEN);
if (nearendClean != NULL)
{
int16_t near_clean_block[PART_LEN];
const int16_t* near_clean_block_ptr = NULL;
WebRtc_ReadBuffer(aecm->nearCleanFrameBuf,
(void**) &near_clean_block_ptr,
near_clean_block,
PART_LEN);
if (WebRtcAecm_ProcessBlock(aecm,
far_block_ptr,
near_noisy_block_ptr,
near_clean_block_ptr,
outBlock) == -1)
{
return -1;
}
} else
{
if (WebRtcAecm_ProcessBlock(aecm,
far_block_ptr,
near_noisy_block_ptr,
NULL,
outBlock) == -1)
{
return -1;
}
}
WebRtc_WriteBuffer(aecm->outFrameBuf, outBlock, PART_LEN);
}
// Stuff the out buffer if we have less than a frame to output.
// This should only happen for the first frame.
size = (int) WebRtc_available_read(aecm->outFrameBuf);
if (size < FRAME_LEN)
{
WebRtc_MoveReadPtr(aecm->outFrameBuf, size - FRAME_LEN);
}
// Obtain an output frame.
WebRtc_ReadBuffer(aecm->outFrameBuf, (void**) &out_ptr, out, FRAME_LEN);
if (out_ptr != out) {
// ReadBuffer() hasn't copied to |out| in this case.
memcpy(out, out_ptr, FRAME_LEN * sizeof(int16_t));
}
return 0;
}
// WebRtcAecm_AsymFilt(...)
//
// Performs asymmetric filtering.
//
// Inputs:
// - filtOld : Previous filtered value.
// - inVal : New input value.
// - stepSizePos : Step size when we have a positive contribution.
// - stepSizeNeg : Step size when we have a negative contribution.
//
// Output:
//
// Return: - Filtered value.
//
int16_t WebRtcAecm_AsymFilt(const int16_t filtOld, const int16_t inVal,
const int16_t stepSizePos,
const int16_t stepSizeNeg)
{
int16_t retVal;
if ((filtOld == WEBRTC_SPL_WORD16_MAX) | (filtOld == WEBRTC_SPL_WORD16_MIN))
{
return inVal;
}
retVal = filtOld;
if (filtOld > inVal)
{
retVal -= (filtOld - inVal) >> stepSizeNeg;
} else
{
retVal += (inVal - filtOld) >> stepSizePos;
}
return retVal;
}
// ExtractFractionPart(a, zeros)
//
// returns the fraction part of |a|, with |zeros| number of leading zeros, as an
// int16_t scaled to Q8. There is no sanity check of |a| in the sense that the
// number of zeros match.
static int16_t ExtractFractionPart(uint32_t a, int zeros) {
return (int16_t)(((a << zeros) & 0x7FFFFFFF) >> 23);
}
// Calculates and returns the log of |energy| in Q8. The input |energy| is
// supposed to be in Q(|q_domain|).
static int16_t LogOfEnergyInQ8(uint32_t energy, int q_domain) {
static const int16_t kLogLowValue = PART_LEN_SHIFT << 7;
int16_t log_energy_q8 = kLogLowValue;
if (energy > 0) {
int zeros = WebRtcSpl_NormU32(energy);
int16_t frac = ExtractFractionPart(energy, zeros);
// log2 of |energy| in Q8.
log_energy_q8 += ((31 - zeros) << 8) + frac - (q_domain << 8);
}
return log_energy_q8;
}
// WebRtcAecm_CalcEnergies(...)
//
// This function calculates the log of energies for nearend, farend and estimated
// echoes. There is also an update of energy decision levels, i.e. internal VAD.
//
//
// @param aecm [i/o] Handle of the AECM instance.
// @param far_spectrum [in] Pointer to farend spectrum.
// @param far_q [in] Q-domain of farend spectrum.
// @param nearEner [in] Near end energy for current block in
// Q(aecm->dfaQDomain).
// @param echoEst [out] Estimated echo in Q(xfa_q+RESOLUTION_CHANNEL16).
//
void WebRtcAecm_CalcEnergies(AecmCore* aecm,
const uint16_t* far_spectrum,
const int16_t far_q,
const uint32_t nearEner,
int32_t* echoEst) {
// Local variables
uint32_t tmpAdapt = 0;
uint32_t tmpStored = 0;
uint32_t tmpFar = 0;
int i;
int16_t tmp16;
int16_t increase_max_shifts = 4;
int16_t decrease_max_shifts = 11;
int16_t increase_min_shifts = 11;
int16_t decrease_min_shifts = 3;
// Get log of near end energy and store in buffer
// Shift buffer
memmove(aecm->nearLogEnergy + 1, aecm->nearLogEnergy,
sizeof(int16_t) * (MAX_BUF_LEN - 1));
// Logarithm of integrated magnitude spectrum (nearEner)
aecm->nearLogEnergy[0] = LogOfEnergyInQ8(nearEner, aecm->dfaNoisyQDomain);
WebRtcAecm_CalcLinearEnergies(aecm, far_spectrum, echoEst, &tmpFar, &tmpAdapt, &tmpStored);
// Shift buffers
memmove(aecm->echoAdaptLogEnergy + 1, aecm->echoAdaptLogEnergy,
sizeof(int16_t) * (MAX_BUF_LEN - 1));
memmove(aecm->echoStoredLogEnergy + 1, aecm->echoStoredLogEnergy,
sizeof(int16_t) * (MAX_BUF_LEN - 1));
// Logarithm of delayed far end energy
aecm->farLogEnergy = LogOfEnergyInQ8(tmpFar, far_q);
// Logarithm of estimated echo energy through adapted channel
aecm->echoAdaptLogEnergy[0] = LogOfEnergyInQ8(tmpAdapt,
RESOLUTION_CHANNEL16 + far_q);
// Logarithm of estimated echo energy through stored channel
aecm->echoStoredLogEnergy[0] =
LogOfEnergyInQ8(tmpStored, RESOLUTION_CHANNEL16 + far_q);
// Update farend energy levels (min, max, vad, mse)
if (aecm->farLogEnergy > FAR_ENERGY_MIN)
{
if (aecm->startupState == 0)
{
increase_max_shifts = 2;
decrease_min_shifts = 2;
increase_min_shifts = 8;
}
aecm->farEnergyMin = WebRtcAecm_AsymFilt(aecm->farEnergyMin, aecm->farLogEnergy,
increase_min_shifts, decrease_min_shifts);
aecm->farEnergyMax = WebRtcAecm_AsymFilt(aecm->farEnergyMax, aecm->farLogEnergy,
increase_max_shifts, decrease_max_shifts);
aecm->farEnergyMaxMin = (aecm->farEnergyMax - aecm->farEnergyMin);
// Dynamic VAD region size
tmp16 = 2560 - aecm->farEnergyMin;
if (tmp16 > 0)
{
tmp16 = (int16_t)((tmp16 * FAR_ENERGY_VAD_REGION) >> 9);
} else
{
tmp16 = 0;
}
tmp16 += FAR_ENERGY_VAD_REGION;
if ((aecm->startupState == 0) | (aecm->vadUpdateCount > 1024))
{
// In startup phase or VAD update halted
aecm->farEnergyVAD = aecm->farEnergyMin + tmp16;
} else
{
if (aecm->farEnergyVAD > aecm->farLogEnergy)
{
aecm->farEnergyVAD +=
(aecm->farLogEnergy + tmp16 - aecm->farEnergyVAD) >> 6;
aecm->vadUpdateCount = 0;
} else
{
aecm->vadUpdateCount++;
}
}
// Put MSE threshold higher than VAD
aecm->farEnergyMSE = aecm->farEnergyVAD + (1 << 8);
}
// Update VAD variables
if (aecm->farLogEnergy > aecm->farEnergyVAD)
{
if ((aecm->startupState == 0) | (aecm->farEnergyMaxMin > FAR_ENERGY_DIFF))
{
// We are in startup or have significant dynamics in input speech level
aecm->currentVADValue = 1;
}
} else
{
aecm->currentVADValue = 0;
}
if ((aecm->currentVADValue) && (aecm->firstVAD))
{
aecm->firstVAD = 0;
if (aecm->echoAdaptLogEnergy[0] > aecm->nearLogEnergy[0])
{
// The estimated echo has higher energy than the near end signal.
// This means that the initialization was too aggressive. Scale
// down by a factor 8
for (i = 0; i < PART_LEN1; i++)
{
aecm->channelAdapt16[i] >>= 3;
}
// Compensate the adapted echo energy level accordingly.
aecm->echoAdaptLogEnergy[0] -= (3 << 8);
aecm->firstVAD = 1;
}
}
}
// WebRtcAecm_CalcStepSize(...)
//
// This function calculates the step size used in channel estimation
//
//
// @param aecm [in] Handle of the AECM instance.
// @param mu [out] (Return value) Stepsize in log2(), i.e. number of shifts.
//
//
int16_t WebRtcAecm_CalcStepSize(AecmCore* const aecm) {
int32_t tmp32;
int16_t tmp16;
int16_t mu = MU_MAX;
// Here we calculate the step size mu used in the
// following NLMS based Channel estimation algorithm
if (!aecm->currentVADValue)
{
// Far end energy level too low, no channel update
mu = 0;
} else if (aecm->startupState > 0)
{
if (aecm->farEnergyMin >= aecm->farEnergyMax)
{
mu = MU_MIN;
} else
{
tmp16 = (aecm->farLogEnergy - aecm->farEnergyMin);
tmp32 = tmp16 * MU_DIFF;
tmp32 = WebRtcSpl_DivW32W16(tmp32, aecm->farEnergyMaxMin);
mu = MU_MIN - 1 - (int16_t)(tmp32);
// The -1 is an alternative to rounding. This way we get a larger
// stepsize, so we in some sense compensate for truncation in NLMS
}
if (mu < MU_MAX)
{
mu = MU_MAX; // Equivalent with maximum step size of 2^-MU_MAX
}
}
return mu;
}
// WebRtcAecm_UpdateChannel(...)
//
// This function performs channel estimation. NLMS and decision on channel storage.
//
//
// @param aecm [i/o] Handle of the AECM instance.
// @param far_spectrum [in] Absolute value of the farend signal in Q(far_q)
// @param far_q [in] Q-domain of the farend signal
// @param dfa [in] Absolute value of the nearend signal (Q[aecm->dfaQDomain])
// @param mu [in] NLMS step size.
// @param echoEst [i/o] Estimated echo in Q(far_q+RESOLUTION_CHANNEL16).
//
void WebRtcAecm_UpdateChannel(AecmCore* aecm,
const uint16_t* far_spectrum,
const int16_t far_q,
const uint16_t* const dfa,
const int16_t mu,
int32_t* echoEst) {
uint32_t tmpU32no1, tmpU32no2;
int32_t tmp32no1, tmp32no2;
int32_t mseStored;
int32_t mseAdapt;
int i;
int16_t zerosFar, zerosNum, zerosCh, zerosDfa;
int16_t shiftChFar, shiftNum, shift2ResChan;
int16_t tmp16no1;
int16_t xfaQ, dfaQ;
// This is the channel estimation algorithm. It is base on NLMS but has a variable step
// length, which was calculated above.
if (mu)
{
for (i = 0; i < PART_LEN1; i++)
{
// Determine norm of channel and farend to make sure we don't get overflow in
// multiplication
zerosCh = WebRtcSpl_NormU32(aecm->channelAdapt32[i]);
zerosFar = WebRtcSpl_NormU32((uint32_t)far_spectrum[i]);
if (zerosCh + zerosFar > 31)
{
// Multiplication is safe
tmpU32no1 = WEBRTC_SPL_UMUL_32_16(aecm->channelAdapt32[i],
far_spectrum[i]);
shiftChFar = 0;
} else
{
// We need to shift down before multiplication
shiftChFar = 32 - zerosCh - zerosFar;
// If zerosCh == zerosFar == 0, shiftChFar is 32. A
// right shift of 32 is undefined. To avoid that, we
// do this check.
tmpU32no1 = rtc::dchecked_cast<uint32_t>(
shiftChFar >= 32
? 0
: aecm->channelAdapt32[i] >> shiftChFar) *
far_spectrum[i];
}
// Determine Q-domain of numerator
zerosNum = WebRtcSpl_NormU32(tmpU32no1);
if (dfa[i])
{
zerosDfa = WebRtcSpl_NormU32((uint32_t)dfa[i]);
} else
{
zerosDfa = 32;
}
tmp16no1 = zerosDfa - 2 + aecm->dfaNoisyQDomain -
RESOLUTION_CHANNEL32 - far_q + shiftChFar;
if (zerosNum > tmp16no1 + 1)
{
xfaQ = tmp16no1;
dfaQ = zerosDfa - 2;
} else
{
xfaQ = zerosNum - 2;
dfaQ = RESOLUTION_CHANNEL32 + far_q - aecm->dfaNoisyQDomain -
shiftChFar + xfaQ;
}
// Add in the same Q-domain
tmpU32no1 = WEBRTC_SPL_SHIFT_W32(tmpU32no1, xfaQ);
tmpU32no2 = WEBRTC_SPL_SHIFT_W32((uint32_t)dfa[i], dfaQ);
tmp32no1 = (int32_t)tmpU32no2 - (int32_t)tmpU32no1;
zerosNum = WebRtcSpl_NormW32(tmp32no1);
if ((tmp32no1) && (far_spectrum[i] > (CHANNEL_VAD << far_q)))
{
//
// Update is needed
//
// This is what we would like to compute
//
// tmp32no1 = dfa[i] - (aecm->channelAdapt[i] * far_spectrum[i])
// tmp32norm = (i + 1)
// aecm->channelAdapt[i] += (2^mu) * tmp32no1
// / (tmp32norm * far_spectrum[i])
//
// Make sure we don't get overflow in multiplication.
if (zerosNum + zerosFar > 31)
{
if (tmp32no1 > 0)
{
tmp32no2 = (int32_t)WEBRTC_SPL_UMUL_32_16(tmp32no1,
far_spectrum[i]);
} else
{
tmp32no2 = -(int32_t)WEBRTC_SPL_UMUL_32_16(-tmp32no1,
far_spectrum[i]);
}
shiftNum = 0;
} else
{
shiftNum = 32 - (zerosNum + zerosFar);
if (tmp32no1 > 0)
{
tmp32no2 = (tmp32no1 >> shiftNum) * far_spectrum[i];
} else
{
tmp32no2 = -((-tmp32no1 >> shiftNum) * far_spectrum[i]);
}
}
// Normalize with respect to frequency bin
tmp32no2 = WebRtcSpl_DivW32W16(tmp32no2, i + 1);
// Make sure we are in the right Q-domain
shift2ResChan = shiftNum + shiftChFar - xfaQ - mu - ((30 - zerosFar) << 1);
if (WebRtcSpl_NormW32(tmp32no2) < shift2ResChan)
{
tmp32no2 = WEBRTC_SPL_WORD32_MAX;
} else
{
tmp32no2 = WEBRTC_SPL_SHIFT_W32(tmp32no2, shift2ResChan);
}
aecm->channelAdapt32[i] =
WebRtcSpl_AddSatW32(aecm->channelAdapt32[i], tmp32no2);
if (aecm->channelAdapt32[i] < 0)
{
// We can never have negative channel gain
aecm->channelAdapt32[i] = 0;
}
aecm->channelAdapt16[i] =
(int16_t)(aecm->channelAdapt32[i] >> 16);
}
}
}
// END: Adaptive channel update
// Determine if we should store or restore the channel
if ((aecm->startupState == 0) & (aecm->currentVADValue))
{
// During startup we store the channel every block,
// and we recalculate echo estimate
WebRtcAecm_StoreAdaptiveChannel(aecm, far_spectrum, echoEst);
} else
{
if (aecm->farLogEnergy < aecm->farEnergyMSE)
{
aecm->mseChannelCount = 0;
} else
{
aecm->mseChannelCount++;
}
// Enough data for validation. Store channel if we can.
if (aecm->mseChannelCount >= (MIN_MSE_COUNT + 10))
{
// We have enough data.
// Calculate MSE of "Adapt" and "Stored" versions.
// It is actually not MSE, but average absolute error.
mseStored = 0;
mseAdapt = 0;
for (i = 0; i < MIN_MSE_COUNT; i++)
{
tmp32no1 = ((int32_t)aecm->echoStoredLogEnergy[i]
- (int32_t)aecm->nearLogEnergy[i]);
tmp32no2 = WEBRTC_SPL_ABS_W32(tmp32no1);
mseStored += tmp32no2;
tmp32no1 = ((int32_t)aecm->echoAdaptLogEnergy[i]
- (int32_t)aecm->nearLogEnergy[i]);
tmp32no2 = WEBRTC_SPL_ABS_W32(tmp32no1);
mseAdapt += tmp32no2;
}
if (((mseStored << MSE_RESOLUTION) < (MIN_MSE_DIFF * mseAdapt))
& ((aecm->mseStoredOld << MSE_RESOLUTION) < (MIN_MSE_DIFF
* aecm->mseAdaptOld)))
{
// The stored channel has a significantly lower MSE than the adaptive one for
// two consecutive calculations. Reset the adaptive channel.
WebRtcAecm_ResetAdaptiveChannel(aecm);
} else if (((MIN_MSE_DIFF * mseStored) > (mseAdapt << MSE_RESOLUTION)) & (mseAdapt
< aecm->mseThreshold) & (aecm->mseAdaptOld < aecm->mseThreshold))
{
// The adaptive channel has a significantly lower MSE than the stored one.
// The MSE for the adaptive channel has also been low for two consecutive
// calculations. Store the adaptive channel.
WebRtcAecm_StoreAdaptiveChannel(aecm, far_spectrum, echoEst);
// Update threshold
if (aecm->mseThreshold == WEBRTC_SPL_WORD32_MAX)
{
aecm->mseThreshold = (mseAdapt + aecm->mseAdaptOld);
} else
{
int scaled_threshold = aecm->mseThreshold * 5 / 8;
aecm->mseThreshold +=
((mseAdapt - scaled_threshold) * 205) >> 8;
}
}
// Reset counter
aecm->mseChannelCount = 0;
// Store the MSE values.
aecm->mseStoredOld = mseStored;
aecm->mseAdaptOld = mseAdapt;
}
}
// END: Determine if we should store or reset channel estimate.
}
// CalcSuppressionGain(...)
//
// This function calculates the suppression gain that is used in the Wiener filter.
//
//
// @param aecm [i/n] Handle of the AECM instance.
// @param supGain [out] (Return value) Suppression gain with which to scale the noise
// level (Q14).
//
//
int16_t WebRtcAecm_CalcSuppressionGain(AecmCore* const aecm) {
int32_t tmp32no1;
int16_t supGain = SUPGAIN_DEFAULT;
int16_t tmp16no1;
int16_t dE = 0;
// Determine suppression gain used in the Wiener filter. The gain is based on a mix of far
// end energy and echo estimation error.
// Adjust for the far end signal level. A low signal level indicates no far end signal,
// hence we set the suppression gain to 0
if (!aecm->currentVADValue)
{
supGain = 0;
} else
{
// Adjust for possible double talk. If we have large variations in estimation error we
// likely have double talk (or poor channel).
tmp16no1 = (aecm->nearLogEnergy[0] - aecm->echoStoredLogEnergy[0] - ENERGY_DEV_OFFSET);
dE = WEBRTC_SPL_ABS_W16(tmp16no1);
if (dE < ENERGY_DEV_TOL)
{
// Likely no double talk. The better estimation, the more we can suppress signal.
// Update counters
if (dE < SUPGAIN_EPC_DT)
{
tmp32no1 = aecm->supGainErrParamDiffAB * dE;
tmp32no1 += (SUPGAIN_EPC_DT >> 1);
tmp16no1 = (int16_t)WebRtcSpl_DivW32W16(tmp32no1, SUPGAIN_EPC_DT);
supGain = aecm->supGainErrParamA - tmp16no1;
} else
{
tmp32no1 = aecm->supGainErrParamDiffBD * (ENERGY_DEV_TOL - dE);
tmp32no1 += ((ENERGY_DEV_TOL - SUPGAIN_EPC_DT) >> 1);
tmp16no1 = (int16_t)WebRtcSpl_DivW32W16(tmp32no1, (ENERGY_DEV_TOL
- SUPGAIN_EPC_DT));
supGain = aecm->supGainErrParamD + tmp16no1;
}
} else
{
// Likely in double talk. Use default value
supGain = aecm->supGainErrParamD;
}
}
if (supGain > aecm->supGainOld)
{
tmp16no1 = supGain;
} else
{
tmp16no1 = aecm->supGainOld;
}
aecm->supGainOld = supGain;
if (tmp16no1 < aecm->supGain)
{
aecm->supGain += (int16_t)((tmp16no1 - aecm->supGain) >> 4);
} else
{
aecm->supGain += (int16_t)((tmp16no1 - aecm->supGain) >> 4);
}
// END: Update suppression gain
return aecm->supGain;
}
void WebRtcAecm_BufferFarFrame(AecmCore* const aecm,
const int16_t* const farend,
const int farLen) {
int writeLen = farLen, writePos = 0;
// Check if the write position must be wrapped
while (aecm->farBufWritePos + writeLen > FAR_BUF_LEN)
{
// Write to remaining buffer space before wrapping
writeLen = FAR_BUF_LEN - aecm->farBufWritePos;
memcpy(aecm->farBuf + aecm->farBufWritePos, farend + writePos,
sizeof(int16_t) * writeLen);
aecm->farBufWritePos = 0;
writePos = writeLen;
writeLen = farLen - writeLen;
}
memcpy(aecm->farBuf + aecm->farBufWritePos, farend + writePos,
sizeof(int16_t) * writeLen);
aecm->farBufWritePos += writeLen;
}
void WebRtcAecm_FetchFarFrame(AecmCore* const aecm,
int16_t* const farend,
const int farLen,
const int knownDelay) {
int readLen = farLen;
int readPos = 0;
int delayChange = knownDelay - aecm->lastKnownDelay;
aecm->farBufReadPos -= delayChange;
// Check if delay forces a read position wrap
while (aecm->farBufReadPos < 0)
{
aecm->farBufReadPos += FAR_BUF_LEN;
}
while (aecm->farBufReadPos > FAR_BUF_LEN - 1)
{
aecm->farBufReadPos -= FAR_BUF_LEN;
}
aecm->lastKnownDelay = knownDelay;
// Check if read position must be wrapped
while (aecm->farBufReadPos + readLen > FAR_BUF_LEN)
{
// Read from remaining buffer space before wrapping
readLen = FAR_BUF_LEN - aecm->farBufReadPos;
memcpy(farend + readPos, aecm->farBuf + aecm->farBufReadPos,
sizeof(int16_t) * readLen);
aecm->farBufReadPos = 0;
readPos = readLen;
readLen = farLen - readLen;
}
memcpy(farend + readPos, aecm->farBuf + aecm->farBufReadPos,
sizeof(int16_t) * readLen);
aecm->farBufReadPos += readLen;
}