/* * 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 #include #include extern "C" { #include "common_audio/ring_buffer.h" #include "common_audio/signal_processing/include/real_fft.h" } #include "common_audio/signal_processing/include/signal_processing_library.h" #include "modules/audio_processing/aecm/echo_control_mobile.h" #include "modules/audio_processing/utility/delay_estimator_wrapper.h" #include "rtc_base/checks.h" #include "rtc_base/numerics/safe_conversions.h" namespace webrtc { namespace { #ifdef AEC_DEBUG FILE* dfile; FILE* testfile; #endif // 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}; } // namespace 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}; // 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() { // Allocate zero-filled memory. AecmCore* aecm = static_cast(calloc(1, 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( 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; } } // namespace webrtc