diff --git a/media/engine/webrtc_voice_engine.cc b/media/engine/webrtc_voice_engine.cc index 97e12ba9c8..21a9bc0448 100644 --- a/media/engine/webrtc_voice_engine.cc +++ b/media/engine/webrtc_voice_engine.cc @@ -473,7 +473,6 @@ bool WebRtcVoiceEngine::ApplyOptions(const AudioOptions& options_in) { if (options.echo_cancellation) { apm_config.echo_canceller.enabled = *options.echo_cancellation; apm_config.echo_canceller.mobile_mode = use_mobile_software_aec; - apm_config.echo_canceller.legacy_moderate_suppression_level = false; } if (options.auto_gain_control) { diff --git a/modules/audio_processing/BUILD.gn b/modules/audio_processing/BUILD.gn index a0f6124eb0..f347b04279 100644 --- a/modules/audio_processing/BUILD.gn +++ b/modules/audio_processing/BUILD.gn @@ -11,13 +11,6 @@ if (rtc_enable_protobuf) { import("//third_party/protobuf/proto_library.gni") } -declare_args() { - # Disables the usual mode where we trust the reported system delay - # values the AEC receives. The corresponding define is set appropriately - # in the code, but it can be force-enabled here for testing. - aec_untrusted_delay_for_testing = false -} - config("apm_debug_dump") { if (apm_debug_dump) { defines = [ "WEBRTC_APM_DEBUG_DUMP=1" ] @@ -112,8 +105,6 @@ rtc_library("audio_processing") { "audio_processing_impl.cc", "audio_processing_impl.h", "common.h", - "echo_cancellation_impl.cc", - "echo_cancellation_impl.h", "echo_control_mobile_impl.cc", "echo_control_mobile_impl.h", "echo_detector/circular_buffer.cc", @@ -187,8 +178,6 @@ rtc_library("audio_processing") { "../../system_wrappers:cpu_features_api", "../../system_wrappers:field_trial", "../../system_wrappers:metrics", - "aec", - "aec:aec_core", "aec3", "aecm:aecm_core", "agc", @@ -202,10 +191,6 @@ rtc_library("audio_processing") { "//third_party/abseil-cpp/absl/types:optional", ] - if (aec_untrusted_delay_for_testing) { - defines += [ "WEBRTC_UNTRUSTED_DELAY" ] - } - if (rtc_prefer_fixed_point) { defines += [ "WEBRTC_NS_FIXED" ] } else { @@ -400,7 +385,6 @@ if (rtc_include_tests) { "audio_buffer_unittest.cc", "audio_frame_view_unittest.cc", "config_unittest.cc", - "echo_cancellation_impl_unittest.cc", "echo_control_mobile_unittest.cc", "gain_controller2_unittest.cc", "splitting_filter_unittest.cc", @@ -451,8 +435,6 @@ if (rtc_include_tests) { "../../test:rtc_expect_death", "../../test:test_support", "../audio_coding:neteq_input_audio_tools", - "aec:aec_core", - "aec:aec_unittests", "aec_dump:mock_aec_dump_unittests", "agc:agc_unittests", "agc2:adaptive_digital_unittests", @@ -463,7 +445,6 @@ if (rtc_include_tests) { "agc2:test_utils", "agc2/rnn_vad:unittests", "test/conversational_speech:unittest", - "utility:block_mean_calculator_unittest", "utility:legacy_delay_estimator_unittest", "utility:pffft_wrapper_unittest", "vad:vad_unittests", @@ -499,7 +480,6 @@ if (rtc_include_tests) { "audio_processing_impl_locking_unittest.cc", "audio_processing_impl_unittest.cc", "audio_processing_unittest.cc", - "echo_cancellation_bit_exact_unittest.cc", "echo_control_mobile_bit_exact_unittest.cc", "echo_detector/circular_buffer_unittest.cc", "echo_detector/mean_variance_estimator_unittest.cc", diff --git a/modules/audio_processing/aec/BUILD.gn b/modules/audio_processing/aec/BUILD.gn deleted file mode 100644 index 472ed1776c..0000000000 --- a/modules/audio_processing/aec/BUILD.gn +++ /dev/null @@ -1,91 +0,0 @@ -# Copyright (c) 2018 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. - -import("../../../webrtc.gni") - -rtc_library("aec") { - configs += [ "..:apm_debug_dump" ] - sources = [ - "aec_resampler.cc", - "aec_resampler.h", - "echo_cancellation.cc", - "echo_cancellation.h", - ] - deps = [ - ":aec_core", - "..:apm_logging", - "../../../common_audio:common_audio_c", - "../../../rtc_base:checks", - "../../../rtc_base:rtc_base_approved", - ] -} - -rtc_library("aec_core") { - configs += [ "..:apm_debug_dump" ] - sources = [ - "aec_common.h", - "aec_core.cc", - "aec_core.h", - "aec_core_optimized_methods.h", - ] - deps = [ - "..:apm_logging", - "../../../common_audio:common_audio_c", - "../../../rtc_base:checks", - "../../../rtc_base:rtc_base_approved", - "../../../rtc_base/system:arch", - "../../../system_wrappers:cpu_features_api", - "../../../system_wrappers:metrics", - "../utility:block_mean_calculator", - "../utility:legacy_delay_estimator", - "../utility:ooura_fft", - ] - cflags = [] - - if (current_cpu == "x86" || current_cpu == "x64") { - sources += [ "aec_core_sse2.cc" ] - if (is_posix || is_fuchsia) { - cflags += [ "-msse2" ] - } - } - - if (rtc_build_with_neon) { - sources += [ "aec_core_neon.cc" ] - - if (current_cpu != "arm64") { - # Enable compilation for the NEON instruction set. - suppressed_configs += [ "//build/config/compiler:compiler_arm_fpu" ] - cflags += [ "-mfpu=neon" ] - } - - deps += [ "../../../common_audio" ] - } - - if (current_cpu == "mipsel" && mips_float_abi == "hard") { - sources += [ "aec_core_mips.cc" ] - } -} - -if (rtc_include_tests) { - rtc_library("aec_unittests") { - testonly = true - - sources = [ - "echo_cancellation_unittest.cc", - "system_delay_unittest.cc", - ] - deps = [ - ":aec", - ":aec_core", - "../../../rtc_base:checks", - "../../../rtc_base:rtc_base_approved", - "../../../test:test_support", - "//testing/gtest", - ] - } -} diff --git a/modules/audio_processing/aec/aec_common.h b/modules/audio_processing/aec/aec_common.h deleted file mode 100644 index ac1f339456..0000000000 --- a/modules/audio_processing/aec/aec_common.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (c) 2014 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. - */ - -#ifndef MODULES_AUDIO_PROCESSING_AEC_AEC_COMMON_H_ -#define MODULES_AUDIO_PROCESSING_AEC_AEC_COMMON_H_ - -#ifdef _MSC_VER /* visual c++ */ -#define ALIGN16_BEG __declspec(align(16)) -#define ALIGN16_END -#else /* gcc or icc */ -#define ALIGN16_BEG -#define ALIGN16_END __attribute__((aligned(16))) -#endif - -#ifdef __cplusplus -namespace webrtc { -#endif - -extern ALIGN16_BEG const float ALIGN16_END WebRtcAec_sqrtHanning[65]; -extern ALIGN16_BEG const float ALIGN16_END WebRtcAec_weightCurve[65]; -extern ALIGN16_BEG const float ALIGN16_END WebRtcAec_overDriveCurve[65]; -extern const float WebRtcAec_kExtendedSmoothingCoefficients[2][2]; -extern const float WebRtcAec_kNormalSmoothingCoefficients[2][2]; -extern const float WebRtcAec_kMinFarendPSD; - -#ifdef __cplusplus -} // namespace webrtc -#endif - -#endif // MODULES_AUDIO_PROCESSING_AEC_AEC_COMMON_H_ diff --git a/modules/audio_processing/aec/aec_core.cc b/modules/audio_processing/aec/aec_core.cc deleted file mode 100644 index d8ba926a8a..0000000000 --- a/modules/audio_processing/aec/aec_core.cc +++ /dev/null @@ -1,2012 +0,0 @@ -/* - * 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. - */ - -/* - * The core AEC algorithm, which is presented with time-aligned signals. - */ - -#include "modules/audio_processing/aec/aec_core.h" - -#include -#include // size_t -#include -#include - -#include -#include - -#include "rtc_base/checks.h" - -extern "C" { -#include "common_audio/ring_buffer.h" -} -#include "common_audio/signal_processing/include/signal_processing_library.h" -#include "modules/audio_processing/aec/aec_common.h" -#include "modules/audio_processing/aec/aec_core_optimized_methods.h" -#include "modules/audio_processing/logging/apm_data_dumper.h" -#include "modules/audio_processing/utility/delay_estimator_wrapper.h" -#include "rtc_base/system/arch.h" -#include "system_wrappers/include/cpu_features_wrapper.h" -#include "system_wrappers/include/metrics.h" - -namespace webrtc { -// Buffer size (samples) -static const size_t kBufferSizeBlocks = 250; // 1 second of audio in 16 kHz. - -// Metrics -static const size_t kSubCountLen = 4; -static const size_t kCountLen = 50; -static const int kDelayMetricsAggregationWindow = 1250; // 5 seconds at 16 kHz. - -// Divergence metric is based on audio level, which gets updated every -// |kSubCountLen + 1| * PART_LEN samples. Divergence metric takes the statistics -// of |kDivergentFilterFractionAggregationWindowSize| audio levels. The -// following value corresponds to 1 second at 16 kHz. -static const int kDivergentFilterFractionAggregationWindowSize = 50; - -// Quantities to control H band scaling for SWB input -static const float cnScaleHband = 0.4f; // scale for comfort noise in H band. -// Initial bin for averaging nlp gain in low band -static const int freqAvgIc = PART_LEN / 2; - -// Matlab code to produce table: -// win = sqrt(hanning(63)); win = [0 ; win(1:32)]; -// fprintf(1, '\t%.14f, %.14f, %.14f,\n', win); -ALIGN16_BEG const float ALIGN16_END WebRtcAec_sqrtHanning[65] = { - 0.00000000000000f, 0.02454122852291f, 0.04906767432742f, 0.07356456359967f, - 0.09801714032956f, 0.12241067519922f, 0.14673047445536f, 0.17096188876030f, - 0.19509032201613f, 0.21910124015687f, 0.24298017990326f, 0.26671275747490f, - 0.29028467725446f, 0.31368174039889f, 0.33688985339222f, 0.35989503653499f, - 0.38268343236509f, 0.40524131400499f, 0.42755509343028f, 0.44961132965461f, - 0.47139673682600f, 0.49289819222978f, 0.51410274419322f, 0.53499761988710f, - 0.55557023301960f, 0.57580819141785f, 0.59569930449243f, 0.61523159058063f, - 0.63439328416365f, 0.65317284295378f, 0.67155895484702f, 0.68954054473707f, - 0.70710678118655f, 0.72424708295147f, 0.74095112535496f, 0.75720884650648f, - 0.77301045336274f, 0.78834642762661f, 0.80320753148064f, 0.81758481315158f, - 0.83146961230255f, 0.84485356524971f, 0.85772861000027f, 0.87008699110871f, - 0.88192126434835f, 0.89322430119552f, 0.90398929312344f, 0.91420975570353f, - 0.92387953251129f, 0.93299279883474f, 0.94154406518302f, 0.94952818059304f, - 0.95694033573221f, 0.96377606579544f, 0.97003125319454f, 0.97570213003853f, - 0.98078528040323f, 0.98527764238894f, 0.98917650996478f, 0.99247953459871f, - 0.99518472667220f, 0.99729045667869f, 0.99879545620517f, 0.99969881869620f, - 1.00000000000000f}; - -// Matlab code to produce table: -// weightCurve = [0 ; 0.3 * sqrt(linspace(0,1,64))' + 0.1]; -// fprintf(1, '\t%.4f, %.4f, %.4f, %.4f, %.4f, %.4f,\n', weightCurve); -ALIGN16_BEG const float ALIGN16_END WebRtcAec_weightCurve[65] = { - 0.0000f, 0.1000f, 0.1378f, 0.1535f, 0.1655f, 0.1756f, 0.1845f, 0.1926f, - 0.2000f, 0.2069f, 0.2134f, 0.2195f, 0.2254f, 0.2309f, 0.2363f, 0.2414f, - 0.2464f, 0.2512f, 0.2558f, 0.2604f, 0.2648f, 0.2690f, 0.2732f, 0.2773f, - 0.2813f, 0.2852f, 0.2890f, 0.2927f, 0.2964f, 0.3000f, 0.3035f, 0.3070f, - 0.3104f, 0.3138f, 0.3171f, 0.3204f, 0.3236f, 0.3268f, 0.3299f, 0.3330f, - 0.3360f, 0.3390f, 0.3420f, 0.3449f, 0.3478f, 0.3507f, 0.3535f, 0.3563f, - 0.3591f, 0.3619f, 0.3646f, 0.3673f, 0.3699f, 0.3726f, 0.3752f, 0.3777f, - 0.3803f, 0.3828f, 0.3854f, 0.3878f, 0.3903f, 0.3928f, 0.3952f, 0.3976f, - 0.4000f}; - -// Matlab code to produce table: -// overDriveCurve = [sqrt(linspace(0,1,65))' + 1]; -// fprintf(1, '\t%.4f, %.4f, %.4f, %.4f, %.4f, %.4f,\n', overDriveCurve); -ALIGN16_BEG const float ALIGN16_END WebRtcAec_overDriveCurve[65] = { - 1.0000f, 1.1250f, 1.1768f, 1.2165f, 1.2500f, 1.2795f, 1.3062f, 1.3307f, - 1.3536f, 1.3750f, 1.3953f, 1.4146f, 1.4330f, 1.4507f, 1.4677f, 1.4841f, - 1.5000f, 1.5154f, 1.5303f, 1.5449f, 1.5590f, 1.5728f, 1.5863f, 1.5995f, - 1.6124f, 1.6250f, 1.6374f, 1.6495f, 1.6614f, 1.6731f, 1.6847f, 1.6960f, - 1.7071f, 1.7181f, 1.7289f, 1.7395f, 1.7500f, 1.7603f, 1.7706f, 1.7806f, - 1.7906f, 1.8004f, 1.8101f, 1.8197f, 1.8292f, 1.8385f, 1.8478f, 1.8570f, - 1.8660f, 1.8750f, 1.8839f, 1.8927f, 1.9014f, 1.9100f, 1.9186f, 1.9270f, - 1.9354f, 1.9437f, 1.9520f, 1.9601f, 1.9682f, 1.9763f, 1.9843f, 1.9922f, - 2.0000f}; - -// Delay Agnostic AEC parameters, still under development and may change. -static const float kDelayQualityThresholdMax = 0.07f; -static const float kDelayQualityThresholdMin = 0.01f; -static const int kInitialShiftOffset = 5; -#if !defined(WEBRTC_ANDROID) -static const int kDelayCorrectionStart = 1500; // 10 ms chunks -#endif - -// Target suppression levels for nlp modes. -// log{0.001, 0.00001, 0.00000001} -static const float kTargetSupp[3] = {-6.9f, -11.5f, -18.4f}; - -// Two sets of parameters, one for the extended filter mode. -static const float kExtendedMinOverDrive[3] = {3.0f, 6.0f, 15.0f}; -static const float kNormalMinOverDrive[3] = {1.0f, 2.0f, 5.0f}; -const float WebRtcAec_kExtendedSmoothingCoefficients[2][2] = {{0.9f, 0.1f}, - {0.92f, 0.08f}}; -const float WebRtcAec_kNormalSmoothingCoefficients[2][2] = {{0.9f, 0.1f}, - {0.93f, 0.07f}}; - -// Number of partitions forming the NLP's "preferred" bands. -enum { kPrefBandSize = 24 }; - -WebRtcAecFilterFar WebRtcAec_FilterFar; -WebRtcAecScaleErrorSignal WebRtcAec_ScaleErrorSignal; -WebRtcAecFilterAdaptation WebRtcAec_FilterAdaptation; -WebRtcAecOverdrive WebRtcAec_Overdrive; -WebRtcAecSuppress WebRtcAec_Suppress; -WebRtcAecComputeCoherence WebRtcAec_ComputeCoherence; -WebRtcAecUpdateCoherenceSpectra WebRtcAec_UpdateCoherenceSpectra; -WebRtcAecStoreAsComplex WebRtcAec_StoreAsComplex; -WebRtcAecPartitionDelay WebRtcAec_PartitionDelay; -WebRtcAecWindowData WebRtcAec_WindowData; - -__inline static float MulRe(float aRe, float aIm, float bRe, float bIm) { - return aRe * bRe - aIm * bIm; -} - -__inline static float MulIm(float aRe, float aIm, float bRe, float bIm) { - return aRe * bIm + aIm * bRe; -} - -// TODO(minyue): Due to a legacy bug, |framelevel| and |averagelevel| use a -// window, of which the length is 1 unit longer than indicated. Remove "+1" when -// the code is refactored. -PowerLevel::PowerLevel() - : framelevel(kSubCountLen + 1), averagelevel(kCountLen + 1) {} - -Aec2BlockBuffer::Aec2BlockBuffer() { - buffer_ = WebRtc_CreateBuffer(kBufferSizeBlocks, sizeof(float) * PART_LEN); - RTC_CHECK(buffer_); - ReInit(); -} - -Aec2BlockBuffer::~Aec2BlockBuffer() { - WebRtc_FreeBuffer(buffer_); -} - -void Aec2BlockBuffer::ReInit() { - WebRtc_InitBuffer(buffer_); -} - -void Aec2BlockBuffer::Insert(const float block[PART_LEN]) { - WebRtc_WriteBuffer(buffer_, block, 1); -} - -void Aec2BlockBuffer::ExtractExtendedBlock(float extended_block[PART_LEN2]) { - float* block_ptr = NULL; - RTC_DCHECK_LT(0, AvaliableSpace()); - - // Extract the previous block. - WebRtc_MoveReadPtr(buffer_, -1); - size_t read_elements = WebRtc_ReadBuffer( - buffer_, reinterpret_cast(&block_ptr), &extended_block[0], 1); - if (read_elements == 0u) { - std::fill_n(&extended_block[0], PART_LEN, 0.0f); - } else if (block_ptr != &extended_block[0]) { - memcpy(&extended_block[0], block_ptr, PART_LEN * sizeof(float)); - } - - // Extract the current block. - read_elements = - WebRtc_ReadBuffer(buffer_, reinterpret_cast(&block_ptr), - &extended_block[PART_LEN], 1); - if (read_elements == 0u) { - std::fill_n(&extended_block[PART_LEN], PART_LEN, 0.0f); - } else if (block_ptr != &extended_block[PART_LEN]) { - memcpy(&extended_block[PART_LEN], block_ptr, PART_LEN * sizeof(float)); - } -} - -int Aec2BlockBuffer::AdjustSize(int buffer_size_decrease) { - return WebRtc_MoveReadPtr(buffer_, buffer_size_decrease); -} - -size_t Aec2BlockBuffer::Size() { - return static_cast(WebRtc_available_read(buffer_)); -} - -size_t Aec2BlockBuffer::AvaliableSpace() { - return WebRtc_available_write(buffer_); -} - -DivergentFilterFraction::DivergentFilterFraction() - : count_(0), occurrence_(0), fraction_(-1.0) {} - -void DivergentFilterFraction::Reset() { - Clear(); - fraction_ = -1.0; -} - -void DivergentFilterFraction::AddObservation(const PowerLevel& nearlevel, - const PowerLevel& linoutlevel, - const PowerLevel& nlpoutlevel) { - const float near_level = nearlevel.framelevel.GetLatestMean(); - const float level_increase = - linoutlevel.framelevel.GetLatestMean() - near_level; - const bool output_signal_active = - nlpoutlevel.framelevel.GetLatestMean() > 40.0 * nlpoutlevel.minlevel; - // Level increase should be, in principle, negative, when the filter - // does not diverge. Here we allow some margin (0.01 * near end level) and - // numerical error (1.0). We count divergence only when the AEC output - // signal is active. - if (output_signal_active && level_increase > std::max(0.01 * near_level, 1.0)) - occurrence_++; - ++count_; - if (count_ == kDivergentFilterFractionAggregationWindowSize) { - fraction_ = static_cast(occurrence_) / - kDivergentFilterFractionAggregationWindowSize; - Clear(); - } -} - -float DivergentFilterFraction::GetLatestFraction() const { - return fraction_; -} - -void DivergentFilterFraction::Clear() { - count_ = 0; - occurrence_ = 0; -} - -// TODO(minyue): Moving some initialization from WebRtcAec_CreateAec() to ctor. -AecCore::AecCore(int instance_index) - : data_dumper(new ApmDataDumper(instance_index)) {} - -AecCore::~AecCore() {} - -static int CmpFloat(const void* a, const void* b) { - const float* da = (const float*)a; - const float* db = (const float*)b; - - return (*da > *db) - (*da < *db); -} - -static void FilterFar(int num_partitions, - int x_fft_buf_block_pos, - float x_fft_buf[2][kExtendedNumPartitions * PART_LEN1], - float h_fft_buf[2][kExtendedNumPartitions * PART_LEN1], - float y_fft[2][PART_LEN1]) { - int i; - for (i = 0; i < num_partitions; i++) { - int j; - int xPos = (i + x_fft_buf_block_pos) * PART_LEN1; - int pos = i * PART_LEN1; - // Check for wrap - if (i + x_fft_buf_block_pos >= num_partitions) { - xPos -= num_partitions * (PART_LEN1); - } - - for (j = 0; j < PART_LEN1; j++) { - y_fft[0][j] += MulRe(x_fft_buf[0][xPos + j], x_fft_buf[1][xPos + j], - h_fft_buf[0][pos + j], h_fft_buf[1][pos + j]); - y_fft[1][j] += MulIm(x_fft_buf[0][xPos + j], x_fft_buf[1][xPos + j], - h_fft_buf[0][pos + j], h_fft_buf[1][pos + j]); - } - } -} - -static void ScaleErrorSignal(float mu, - float error_threshold, - float x_pow[PART_LEN1], - float ef[2][PART_LEN1]) { - int i; - float abs_ef; - for (i = 0; i < (PART_LEN1); i++) { - ef[0][i] /= (x_pow[i] + 1e-10f); - ef[1][i] /= (x_pow[i] + 1e-10f); - abs_ef = sqrtf(ef[0][i] * ef[0][i] + ef[1][i] * ef[1][i]); - - if (abs_ef > error_threshold) { - abs_ef = error_threshold / (abs_ef + 1e-10f); - ef[0][i] *= abs_ef; - ef[1][i] *= abs_ef; - } - - // Stepsize factor - ef[0][i] *= mu; - ef[1][i] *= mu; - } -} - -static void FilterAdaptation( - const OouraFft& ooura_fft, - int num_partitions, - int x_fft_buf_block_pos, - float x_fft_buf[2][kExtendedNumPartitions * PART_LEN1], - float e_fft[2][PART_LEN1], - float h_fft_buf[2][kExtendedNumPartitions * PART_LEN1]) { - int i, j; - float fft[PART_LEN2]; - for (i = 0; i < num_partitions; i++) { - int xPos = (i + x_fft_buf_block_pos) * (PART_LEN1); - int pos; - // Check for wrap - if (i + x_fft_buf_block_pos >= num_partitions) { - xPos -= num_partitions * PART_LEN1; - } - - pos = i * PART_LEN1; - - for (j = 0; j < PART_LEN; j++) { - fft[2 * j] = MulRe(x_fft_buf[0][xPos + j], -x_fft_buf[1][xPos + j], - e_fft[0][j], e_fft[1][j]); - fft[2 * j + 1] = MulIm(x_fft_buf[0][xPos + j], -x_fft_buf[1][xPos + j], - e_fft[0][j], e_fft[1][j]); - } - fft[1] = - MulRe(x_fft_buf[0][xPos + PART_LEN], -x_fft_buf[1][xPos + PART_LEN], - e_fft[0][PART_LEN], e_fft[1][PART_LEN]); - - ooura_fft.InverseFft(fft); - memset(fft + PART_LEN, 0, sizeof(float) * PART_LEN); - - // fft scaling - { - float scale = 2.0f / PART_LEN2; - for (j = 0; j < PART_LEN; j++) { - fft[j] *= scale; - } - } - ooura_fft.Fft(fft); - - h_fft_buf[0][pos] += fft[0]; - h_fft_buf[0][pos + PART_LEN] += fft[1]; - - for (j = 1; j < PART_LEN; j++) { - h_fft_buf[0][pos + j] += fft[2 * j]; - h_fft_buf[1][pos + j] += fft[2 * j + 1]; - } - } -} - -static void Overdrive(float overdrive_scaling, - const float hNlFb, - float hNl[PART_LEN1]) { - for (int i = 0; i < PART_LEN1; ++i) { - // Weight subbands - if (hNl[i] > hNlFb) { - hNl[i] = WebRtcAec_weightCurve[i] * hNlFb + - (1 - WebRtcAec_weightCurve[i]) * hNl[i]; - } - hNl[i] = powf(hNl[i], overdrive_scaling * WebRtcAec_overDriveCurve[i]); - } -} - -static void Suppress(const float hNl[PART_LEN1], float efw[2][PART_LEN1]) { - for (int i = 0; i < PART_LEN1; ++i) { - // Suppress error signal - efw[0][i] *= hNl[i]; - efw[1][i] *= hNl[i]; - - // Ooura fft returns incorrect sign on imaginary component. It matters here - // because we are making an additive change with comfort noise. - efw[1][i] *= -1; - } -} - -static int PartitionDelay( - int num_partitions, - float h_fft_buf[2][kExtendedNumPartitions * PART_LEN1]) { - // Measures the energy in each filter partition and returns the partition with - // highest energy. - // TODO(bjornv): Spread computational cost by computing one partition per - // block? - float wfEnMax = 0; - int i; - int delay = 0; - - for (i = 0; i < num_partitions; i++) { - int j; - int pos = i * PART_LEN1; - float wfEn = 0; - for (j = 0; j < PART_LEN1; j++) { - wfEn += h_fft_buf[0][pos + j] * h_fft_buf[0][pos + j] + - h_fft_buf[1][pos + j] * h_fft_buf[1][pos + j]; - } - - if (wfEn > wfEnMax) { - wfEnMax = wfEn; - delay = i; - } - } - return delay; -} - -// Update metric with 10 * log10(numerator / denominator). -static void UpdateLogRatioMetric(Stats* metric, - float numerator, - float denominator) { - RTC_DCHECK(metric); - RTC_CHECK(numerator >= 0); - RTC_CHECK(denominator >= 0); - - const float log_numerator = std::log10(numerator + 1e-10f); - const float log_denominator = std::log10(denominator + 1e-10f); - metric->instant = 10.0f * (log_numerator - log_denominator); - - // Max. - if (metric->instant > metric->max) - metric->max = metric->instant; - - // Min. - if (metric->instant < metric->min) - metric->min = metric->instant; - - // Average. - metric->counter++; - // This is to protect overflow, which should almost never happen. - RTC_CHECK_NE(0, metric->counter); - metric->sum += metric->instant; - metric->average = metric->sum / metric->counter; - - // Upper mean. - if (metric->instant > metric->average) { - metric->hicounter++; - // This is to protect overflow, which should almost never happen. - RTC_CHECK_NE(0, metric->hicounter); - metric->hisum += metric->instant; - metric->himean = metric->hisum / metric->hicounter; - } -} - -// Threshold to protect against the ill-effects of a zero far-end. -const float WebRtcAec_kMinFarendPSD = 15; - -// Updates the following smoothed Power Spectral Densities (PSD): -// - sd : near-end -// - se : residual echo -// - sx : far-end -// - sde : cross-PSD of near-end and residual echo -// - sxd : cross-PSD of near-end and far-end -// -// In addition to updating the PSDs, also the filter diverge state is -// determined. -static void UpdateCoherenceSpectra(int mult, - bool extended_filter_enabled, - float efw[2][PART_LEN1], - float dfw[2][PART_LEN1], - float xfw[2][PART_LEN1], - CoherenceState* coherence_state, - short* filter_divergence_state, - int* extreme_filter_divergence) { - // Power estimate smoothing coefficients. - const float* ptrGCoh = - extended_filter_enabled - ? WebRtcAec_kExtendedSmoothingCoefficients[mult - 1] - : WebRtcAec_kNormalSmoothingCoefficients[mult - 1]; - int i; - float sdSum = 0, seSum = 0; - - for (i = 0; i < PART_LEN1; i++) { - coherence_state->sd[i] = - ptrGCoh[0] * coherence_state->sd[i] + - ptrGCoh[1] * (dfw[0][i] * dfw[0][i] + dfw[1][i] * dfw[1][i]); - coherence_state->se[i] = - ptrGCoh[0] * coherence_state->se[i] + - ptrGCoh[1] * (efw[0][i] * efw[0][i] + efw[1][i] * efw[1][i]); - // We threshold here to protect against the ill-effects of a zero farend. - // The threshold is not arbitrarily chosen, but balances protection and - // adverse interaction with the algorithm's tuning. - // TODO(bjornv): investigate further why this is so sensitive. - coherence_state->sx[i] = - ptrGCoh[0] * coherence_state->sx[i] + - ptrGCoh[1] * - WEBRTC_SPL_MAX(xfw[0][i] * xfw[0][i] + xfw[1][i] * xfw[1][i], - WebRtcAec_kMinFarendPSD); - - coherence_state->sde[i][0] = - ptrGCoh[0] * coherence_state->sde[i][0] + - ptrGCoh[1] * (dfw[0][i] * efw[0][i] + dfw[1][i] * efw[1][i]); - coherence_state->sde[i][1] = - ptrGCoh[0] * coherence_state->sde[i][1] + - ptrGCoh[1] * (dfw[0][i] * efw[1][i] - dfw[1][i] * efw[0][i]); - - coherence_state->sxd[i][0] = - ptrGCoh[0] * coherence_state->sxd[i][0] + - ptrGCoh[1] * (dfw[0][i] * xfw[0][i] + dfw[1][i] * xfw[1][i]); - coherence_state->sxd[i][1] = - ptrGCoh[0] * coherence_state->sxd[i][1] + - ptrGCoh[1] * (dfw[0][i] * xfw[1][i] - dfw[1][i] * xfw[0][i]); - - sdSum += coherence_state->sd[i]; - seSum += coherence_state->se[i]; - } - - // Divergent filter safeguard update. - *filter_divergence_state = - (*filter_divergence_state ? 1.05f : 1.0f) * seSum > sdSum; - - // Signal extreme filter divergence if the error is significantly larger - // than the nearend (13 dB). - *extreme_filter_divergence = (seSum > (19.95f * sdSum)); -} - -// Window time domain data to be used by the fft. -__inline static void WindowData(float* x_windowed, const float* x) { - int i; - for (i = 0; i < PART_LEN; i++) { - x_windowed[i] = x[i] * WebRtcAec_sqrtHanning[i]; - x_windowed[PART_LEN + i] = - x[PART_LEN + i] * WebRtcAec_sqrtHanning[PART_LEN - i]; - } -} - -// Puts fft output data into a complex valued array. -__inline static void StoreAsComplex(const float* data, - float data_complex[2][PART_LEN1]) { - int i; - data_complex[0][0] = data[0]; - data_complex[1][0] = 0; - for (i = 1; i < PART_LEN; i++) { - data_complex[0][i] = data[2 * i]; - data_complex[1][i] = data[2 * i + 1]; - } - data_complex[0][PART_LEN] = data[1]; - data_complex[1][PART_LEN] = 0; -} - -static void ComputeCoherence(const CoherenceState* coherence_state, - float* cohde, - float* cohxd) { - // Subband coherence - for (int i = 0; i < PART_LEN1; i++) { - cohde[i] = (coherence_state->sde[i][0] * coherence_state->sde[i][0] + - coherence_state->sde[i][1] * coherence_state->sde[i][1]) / - (coherence_state->sd[i] * coherence_state->se[i] + 1e-10f); - cohxd[i] = (coherence_state->sxd[i][0] * coherence_state->sxd[i][0] + - coherence_state->sxd[i][1] * coherence_state->sxd[i][1]) / - (coherence_state->sx[i] * coherence_state->sd[i] + 1e-10f); - } -} - -static void GetHighbandGain(const float* lambda, float* nlpGainHband) { - int i; - - *nlpGainHband = 0.0f; - for (i = freqAvgIc; i < PART_LEN1 - 1; i++) { - *nlpGainHband += lambda[i]; - } - *nlpGainHband /= static_cast(PART_LEN1 - 1 - freqAvgIc); -} - -static void GenerateComplexNoise(uint32_t* seed, float noise[2][PART_LEN1]) { - const float kPi2 = 6.28318530717959f; - int16_t randW16[PART_LEN]; - WebRtcSpl_RandUArray(randW16, PART_LEN, seed); - - noise[0][0] = 0; - noise[1][0] = 0; - for (size_t i = 1; i < PART_LEN1; i++) { - float tmp = kPi2 * randW16[i - 1] / 32768.f; - noise[0][i] = cosf(tmp); - noise[1][i] = -sinf(tmp); - } - noise[1][PART_LEN] = 0; -} - -static void ComfortNoise(bool generate_high_frequency_noise, - uint32_t* seed, - float e_fft[2][PART_LEN1], - float high_frequency_comfort_noise[2][PART_LEN1], - const float* noise_spectrum, - const float* suppressor_gain) { - float complex_noise[2][PART_LEN1]; - - GenerateComplexNoise(seed, complex_noise); - - // Shape, scale and add comfort noise. - for (int i = 1; i < PART_LEN1; ++i) { - float noise_scaling = - sqrtf(WEBRTC_SPL_MAX(1 - suppressor_gain[i] * suppressor_gain[i], 0)) * - sqrtf(noise_spectrum[i]); - e_fft[0][i] += noise_scaling * complex_noise[0][i]; - e_fft[1][i] += noise_scaling * complex_noise[1][i]; - } - - // Form comfort noise for higher frequencies. - if (generate_high_frequency_noise) { - // Compute average noise power and nlp gain over the second half of freq - // spectrum (i.e., 4->8khz). - int start_avg_band = PART_LEN1 / 2; - float upper_bands_noise_power = 0.f; - float upper_bands_suppressor_gain = 0.f; - for (int i = start_avg_band; i < PART_LEN1; ++i) { - upper_bands_noise_power += sqrtf(noise_spectrum[i]); - upper_bands_suppressor_gain += - sqrtf(WEBRTC_SPL_MAX(1 - suppressor_gain[i] * suppressor_gain[i], 0)); - } - upper_bands_noise_power /= (PART_LEN1 - start_avg_band); - upper_bands_suppressor_gain /= (PART_LEN1 - start_avg_band); - - // Shape, scale and add comfort noise. - float noise_scaling = upper_bands_suppressor_gain * upper_bands_noise_power; - high_frequency_comfort_noise[0][0] = 0; - high_frequency_comfort_noise[1][0] = 0; - for (int i = 1; i < PART_LEN1; ++i) { - high_frequency_comfort_noise[0][i] = noise_scaling * complex_noise[0][i]; - high_frequency_comfort_noise[1][i] = noise_scaling * complex_noise[1][i]; - } - high_frequency_comfort_noise[1][PART_LEN] = 0; - } else { - memset(high_frequency_comfort_noise, 0, - 2 * PART_LEN1 * sizeof(high_frequency_comfort_noise[0][0])); - } -} - -static void InitLevel(PowerLevel* level) { - const float kBigFloat = 1E17f; - level->averagelevel.Reset(); - level->framelevel.Reset(); - level->minlevel = kBigFloat; -} - -static void InitStats(Stats* stats) { - stats->instant = kOffsetLevel; - stats->average = kOffsetLevel; - stats->max = kOffsetLevel; - stats->min = kOffsetLevel * (-1); - stats->sum = 0; - stats->hisum = 0; - stats->himean = kOffsetLevel; - stats->counter = 0; - stats->hicounter = 0; -} - -static void InitMetrics(AecCore* self) { - self->stateCounter = 0; - InitLevel(&self->farlevel); - InitLevel(&self->nearlevel); - InitLevel(&self->linoutlevel); - InitLevel(&self->nlpoutlevel); - - InitStats(&self->erl); - InitStats(&self->erle); - InitStats(&self->aNlp); - InitStats(&self->rerl); - - self->divergent_filter_fraction.Reset(); -} - -static float CalculatePower(const float* in, size_t num_samples) { - size_t k; - float energy = 0.0f; - - for (k = 0; k < num_samples; ++k) { - energy += in[k] * in[k]; - } - return energy / num_samples; -} - -static void UpdateLevel(PowerLevel* level, float power) { - level->framelevel.AddValue(power); - if (level->framelevel.EndOfBlock()) { - const float new_frame_level = level->framelevel.GetLatestMean(); - if (new_frame_level > 0) { - if (new_frame_level < level->minlevel) { - level->minlevel = new_frame_level; // New minimum. - } else { - level->minlevel *= (1 + 0.001f); // Small increase. - } - } - level->averagelevel.AddValue(new_frame_level); - } -} - -static void UpdateMetrics(AecCore* aec) { - const float actThresholdNoisy = 8.0f; - const float actThresholdClean = 40.0f; - - const float noisyPower = 300000.0f; - - float actThreshold; - - if (aec->echoState) { // Check if echo is likely present - aec->stateCounter++; - } - - if (aec->linoutlevel.framelevel.EndOfBlock()) { - aec->divergent_filter_fraction.AddObservation( - aec->nearlevel, aec->linoutlevel, aec->nlpoutlevel); - } - - if (aec->farlevel.averagelevel.EndOfBlock()) { - if (aec->farlevel.minlevel < noisyPower) { - actThreshold = actThresholdClean; - } else { - actThreshold = actThresholdNoisy; - } - - const float far_average_level = aec->farlevel.averagelevel.GetLatestMean(); - - // The last condition is to let estimation be made in active far-end - // segments only. - if ((aec->stateCounter > (0.5f * kCountLen * kSubCountLen)) && - (aec->farlevel.framelevel.EndOfBlock()) && - (far_average_level > (actThreshold * aec->farlevel.minlevel))) { - // ERL: error return loss. - const float near_average_level = - aec->nearlevel.averagelevel.GetLatestMean(); - UpdateLogRatioMetric(&aec->erl, far_average_level, near_average_level); - - // A_NLP: error return loss enhanced before the nonlinear suppression. - const float linout_average_level = - aec->linoutlevel.averagelevel.GetLatestMean(); - UpdateLogRatioMetric(&aec->aNlp, near_average_level, - linout_average_level); - - // ERLE: error return loss enhanced. - const float nlpout_average_level = - aec->nlpoutlevel.averagelevel.GetLatestMean(); - UpdateLogRatioMetric(&aec->erle, near_average_level, - nlpout_average_level); - } - - aec->stateCounter = 0; - } -} - -static void UpdateDelayMetrics(AecCore* self) { - int i = 0; - int delay_values = 0; - int median = 0; - int lookahead = WebRtc_lookahead(self->delay_estimator); - const int kMsPerBlock = PART_LEN / (self->mult * 8); - int64_t l1_norm = 0; - - if (self->num_delay_values == 0) { - // We have no new delay value data. Even though -1 is a valid |median| in - // the sense that we allow negative values, it will practically never be - // used since multiples of |kMsPerBlock| will always be returned. - // We therefore use -1 to indicate in the logs that the delay estimator was - // not able to estimate the delay. - self->delay_median = -1; - self->delay_std = -1; - self->fraction_poor_delays = -1; - return; - } - - // Start value for median count down. - delay_values = self->num_delay_values >> 1; - // Get median of delay values since last update. - for (i = 0; i < kHistorySizeBlocks; i++) { - delay_values -= self->delay_histogram[i]; - if (delay_values < 0) { - median = i; - break; - } - } - // Account for lookahead. - self->delay_median = (median - lookahead) * kMsPerBlock; - - // Calculate the L1 norm, with median value as central moment. - for (i = 0; i < kHistorySizeBlocks; i++) { - l1_norm += abs(i - median) * self->delay_histogram[i]; - } - self->delay_std = static_cast((l1_norm + self->num_delay_values / 2) / - self->num_delay_values) * - kMsPerBlock; - - // Determine fraction of delays that are out of bounds, that is, either - // negative (anti-causal system) or larger than the AEC filter length. - { - int num_delays_out_of_bounds = self->num_delay_values; - const int histogram_length = - sizeof(self->delay_histogram) / sizeof(self->delay_histogram[0]); - for (i = lookahead; i < lookahead + self->num_partitions; ++i) { - if (i < histogram_length) - num_delays_out_of_bounds -= self->delay_histogram[i]; - } - self->fraction_poor_delays = - static_cast(num_delays_out_of_bounds) / self->num_delay_values; - } - - // Reset histogram. - memset(self->delay_histogram, 0, sizeof(self->delay_histogram)); - self->num_delay_values = 0; -} - -static void ScaledInverseFft(const OouraFft& ooura_fft, - float freq_data[2][PART_LEN1], - float time_data[PART_LEN2], - float scale, - int conjugate) { - int i; - const float normalization = scale / static_cast(PART_LEN2); - const float sign = (conjugate ? -1 : 1); - time_data[0] = freq_data[0][0] * normalization; - time_data[1] = freq_data[0][PART_LEN] * normalization; - for (i = 1; i < PART_LEN; i++) { - time_data[2 * i] = freq_data[0][i] * normalization; - time_data[2 * i + 1] = sign * freq_data[1][i] * normalization; - } - ooura_fft.InverseFft(time_data); -} - -static void Fft(const OouraFft& ooura_fft, - float time_data[PART_LEN2], - float freq_data[2][PART_LEN1]) { - int i; - ooura_fft.Fft(time_data); - - // Reorder fft output data. - freq_data[1][0] = 0; - freq_data[1][PART_LEN] = 0; - freq_data[0][0] = time_data[0]; - freq_data[0][PART_LEN] = time_data[1]; - for (i = 1; i < PART_LEN; i++) { - freq_data[0][i] = time_data[2 * i]; - freq_data[1][i] = time_data[2 * i + 1]; - } -} - -static int SignalBasedDelayCorrection(AecCore* self) { - int delay_correction = 0; - int last_delay = -2; - RTC_DCHECK(self); -#if !defined(WEBRTC_ANDROID) - // On desktops, turn on correction after |kDelayCorrectionStart| frames. This - // is to let the delay estimation get a chance to converge. Also, if the - // playout audio volume is low (or even muted) the delay estimation can return - // a very large delay, which will break the AEC if it is applied. - if (self->frame_count < kDelayCorrectionStart) { - self->data_dumper->DumpRaw("aec_da_reported_delay", 1, &last_delay); - return 0; - } -#endif - - // 1. Check for non-negative delay estimate. Note that the estimates we get - // from the delay estimation are not compensated for lookahead. Hence, a - // negative |last_delay| is an invalid one. - // 2. Verify that there is a delay change. In addition, only allow a change - // if the delay is outside a certain region taking the AEC filter length - // into account. - // TODO(bjornv): Investigate if we can remove the non-zero delay change check. - // 3. Only allow delay correction if the delay estimation quality exceeds - // |delay_quality_threshold|. - // 4. Finally, verify that the proposed |delay_correction| is feasible by - // comparing with the size of the far-end buffer. - last_delay = WebRtc_last_delay(self->delay_estimator); - self->data_dumper->DumpRaw("aec_da_reported_delay", 1, &last_delay); - if ((last_delay >= 0) && (last_delay != self->previous_delay) && - (WebRtc_last_delay_quality(self->delay_estimator) > - self->delay_quality_threshold)) { - int delay = last_delay - WebRtc_lookahead(self->delay_estimator); - // Allow for a slack in the actual delay, defined by a |lower_bound| and an - // |upper_bound|. The adaptive echo cancellation filter is currently - // |num_partitions| (of 64 samples) long. If the delay estimate is negative - // or at least 3/4 of the filter length we open up for correction. - const int lower_bound = 0; - const int upper_bound = self->num_partitions * 3 / 4; - const int do_correction = delay <= lower_bound || delay > upper_bound; - if (do_correction == 1) { - int available_read = self->farend_block_buffer_.Size(); - // With |shift_offset| we gradually rely on the delay estimates. For - // positive delays we reduce the correction by |shift_offset| to lower the - // risk of pushing the AEC into a non causal state. For negative delays - // we rely on the values up to a rounding error, hence compensate by 1 - // element to make sure to push the delay into the causal region. - delay_correction = -delay; - delay_correction += delay > self->shift_offset ? self->shift_offset : 1; - self->shift_offset--; - self->shift_offset = (self->shift_offset <= 1 ? 1 : self->shift_offset); - if (delay_correction > available_read - self->mult - 1) { - // There is not enough data in the buffer to perform this shift. Hence, - // we do not rely on the delay estimate and do nothing. - delay_correction = 0; - } else { - self->previous_delay = last_delay; - ++self->delay_correction_count; - } - } - } - // Update the |delay_quality_threshold| once we have our first delay - // correction. - if (self->delay_correction_count > 0) { - float delay_quality = WebRtc_last_delay_quality(self->delay_estimator); - delay_quality = - (delay_quality > kDelayQualityThresholdMax ? kDelayQualityThresholdMax - : delay_quality); - self->delay_quality_threshold = - (delay_quality > self->delay_quality_threshold - ? delay_quality - : self->delay_quality_threshold); - } - self->data_dumper->DumpRaw("aec_da_delay_correction", 1, &delay_correction); - - return delay_correction; -} - -static void RegressorPower( - int num_partitions, - int latest_added_partition, - float x_fft_buf[2][kExtendedNumPartitions * PART_LEN1], - float x_pow[PART_LEN1]) { - RTC_DCHECK_LT(latest_added_partition, num_partitions); - memset(x_pow, 0, PART_LEN1 * sizeof(x_pow[0])); - - int partition = latest_added_partition; - int x_fft_buf_position = partition * PART_LEN1; - for (int i = 0; i < num_partitions; ++i) { - for (int bin = 0; bin < PART_LEN1; ++bin) { - float re = x_fft_buf[0][x_fft_buf_position]; - float im = x_fft_buf[1][x_fft_buf_position]; - x_pow[bin] += re * re + im * im; - ++x_fft_buf_position; - } - - ++partition; - if (partition == num_partitions) { - partition = 0; - RTC_DCHECK_EQ(num_partitions * PART_LEN1, x_fft_buf_position); - x_fft_buf_position = 0; - } - } -} - -static void EchoSubtraction( - const OouraFft& ooura_fft, - int num_partitions, - int extended_filter_enabled, - int* extreme_filter_divergence, - float filter_step_size, - float error_threshold, - float* x_fft, - int* x_fft_buf_block_pos, - float x_fft_buf[2][kExtendedNumPartitions * PART_LEN1], - float* const y, - float x_pow[PART_LEN1], - float h_fft_buf[2][kExtendedNumPartitions * PART_LEN1], - float echo_subtractor_output[PART_LEN]) { - float s_fft[2][PART_LEN1]; - float e_extended[PART_LEN2]; - float s_extended[PART_LEN2]; - float* s; - float e[PART_LEN]; - float e_fft[2][PART_LEN1]; - int i; - - // Update the x_fft_buf block position. - (*x_fft_buf_block_pos)--; - if ((*x_fft_buf_block_pos) == -1) { - *x_fft_buf_block_pos = num_partitions - 1; - } - - // Buffer x_fft. - memcpy(x_fft_buf[0] + (*x_fft_buf_block_pos) * PART_LEN1, x_fft, - sizeof(float) * PART_LEN1); - memcpy(x_fft_buf[1] + (*x_fft_buf_block_pos) * PART_LEN1, &x_fft[PART_LEN1], - sizeof(float) * PART_LEN1); - - memset(s_fft, 0, sizeof(s_fft)); - - // Conditionally reset the echo subtraction filter if the filter has diverged - // significantly. - if (!extended_filter_enabled && *extreme_filter_divergence) { - memset(h_fft_buf, 0, - 2 * kExtendedNumPartitions * PART_LEN1 * sizeof(h_fft_buf[0][0])); - *extreme_filter_divergence = 0; - } - - // Produce echo estimate s_fft. - WebRtcAec_FilterFar(num_partitions, *x_fft_buf_block_pos, x_fft_buf, - h_fft_buf, s_fft); - - // Compute the time-domain echo estimate s. - ScaledInverseFft(ooura_fft, s_fft, s_extended, 2.0f, 0); - s = &s_extended[PART_LEN]; - - // Compute the time-domain echo prediction error. - for (i = 0; i < PART_LEN; ++i) { - e[i] = y[i] - s[i]; - } - - // Compute the frequency domain echo prediction error. - memset(e_extended, 0, sizeof(float) * PART_LEN); - memcpy(e_extended + PART_LEN, e, sizeof(float) * PART_LEN); - Fft(ooura_fft, e_extended, e_fft); - - // Scale error signal inversely with far power. - WebRtcAec_ScaleErrorSignal(filter_step_size, error_threshold, x_pow, e_fft); - WebRtcAec_FilterAdaptation(ooura_fft, num_partitions, *x_fft_buf_block_pos, - x_fft_buf, e_fft, h_fft_buf); - memcpy(echo_subtractor_output, e, sizeof(float) * PART_LEN); -} - -static void FormSuppressionGain(AecCore* aec, - float cohde[PART_LEN1], - float cohxd[PART_LEN1], - float hNl[PART_LEN1]) { - float hNlDeAvg, hNlXdAvg; - float hNlPref[kPrefBandSize]; - float hNlFb = 0, hNlFbLow = 0; - const int prefBandSize = kPrefBandSize / aec->mult; - const float prefBandQuant = 0.75f, prefBandQuantLow = 0.5f; - const int minPrefBand = 4 / aec->mult; - // Power estimate smoothing coefficients. - const float* min_overdrive = aec->extended_filter_enabled - ? kExtendedMinOverDrive - : kNormalMinOverDrive; - - hNlXdAvg = 0; - for (int i = minPrefBand; i < prefBandSize + minPrefBand; ++i) { - hNlXdAvg += cohxd[i]; - } - hNlXdAvg /= prefBandSize; - hNlXdAvg = 1 - hNlXdAvg; - - hNlDeAvg = 0; - for (int i = minPrefBand; i < prefBandSize + minPrefBand; ++i) { - hNlDeAvg += cohde[i]; - } - hNlDeAvg /= prefBandSize; - - if (hNlXdAvg < 0.75f && hNlXdAvg < aec->hNlXdAvgMin) { - aec->hNlXdAvgMin = hNlXdAvg; - } - - if (hNlDeAvg > 0.98f && hNlXdAvg > 0.9f) { - aec->stNearState = 1; - } else if (hNlDeAvg < 0.95f || hNlXdAvg < 0.8f) { - aec->stNearState = 0; - } - - if (aec->hNlXdAvgMin == 1) { - aec->echoState = 0; - aec->overDrive = min_overdrive[aec->nlp_mode]; - - if (aec->stNearState == 1) { - memcpy(hNl, cohde, sizeof(hNl[0]) * PART_LEN1); - hNlFb = hNlDeAvg; - hNlFbLow = hNlDeAvg; - } else { - for (int i = 0; i < PART_LEN1; ++i) { - hNl[i] = 1 - cohxd[i]; - hNl[i] = std::max(hNl[i], 0.f); - } - hNlFb = hNlXdAvg; - hNlFbLow = hNlXdAvg; - } - } else { - if (aec->stNearState == 1) { - aec->echoState = 0; - memcpy(hNl, cohde, sizeof(hNl[0]) * PART_LEN1); - hNlFb = hNlDeAvg; - hNlFbLow = hNlDeAvg; - } else { - aec->echoState = 1; - for (int i = 0; i < PART_LEN1; ++i) { - hNl[i] = WEBRTC_SPL_MIN(cohde[i], 1 - cohxd[i]); - hNl[i] = std::max(hNl[i], 0.f); - } - - // Select an order statistic from the preferred bands. - // TODO(peah): Using quicksort now, but a selection algorithm may be - // preferred. - memcpy(hNlPref, &hNl[minPrefBand], sizeof(float) * prefBandSize); - qsort(hNlPref, prefBandSize, sizeof(float), CmpFloat); - hNlFb = hNlPref[static_cast( - std::floor(prefBandQuant * (prefBandSize - 1)))]; - hNlFbLow = hNlPref[static_cast( - std::floor(prefBandQuantLow * (prefBandSize - 1)))]; - } - } - - // Track the local filter minimum to determine suppression overdrive. - if (hNlFbLow < 0.6f && hNlFbLow < aec->hNlFbLocalMin) { - aec->hNlFbLocalMin = hNlFbLow; - aec->hNlFbMin = hNlFbLow; - aec->hNlNewMin = 1; - aec->hNlMinCtr = 0; - } - aec->hNlFbLocalMin = - WEBRTC_SPL_MIN(aec->hNlFbLocalMin + 0.0008f / aec->mult, 1); - aec->hNlXdAvgMin = WEBRTC_SPL_MIN(aec->hNlXdAvgMin + 0.0006f / aec->mult, 1); - - if (aec->hNlNewMin == 1) { - aec->hNlMinCtr++; - } - if (aec->hNlMinCtr == 2) { - aec->hNlNewMin = 0; - aec->hNlMinCtr = 0; - aec->overDrive = WEBRTC_SPL_MAX( - kTargetSupp[aec->nlp_mode] / - static_cast(std::log(aec->hNlFbMin + 1e-10f) + 1e-10f), - min_overdrive[aec->nlp_mode]); - } - - // Smooth the overdrive. - if (aec->overDrive < aec->overdrive_scaling) { - aec->overdrive_scaling = - 0.99f * aec->overdrive_scaling + 0.01f * aec->overDrive; - } else { - aec->overdrive_scaling = - 0.9f * aec->overdrive_scaling + 0.1f * aec->overDrive; - } - - // Apply the overdrive. - WebRtcAec_Overdrive(aec->overdrive_scaling, hNlFb, hNl); -} - -static void EchoSuppression(const OouraFft& ooura_fft, - AecCore* aec, - float* nearend_extended_block_lowest_band, - float farend_extended_block[PART_LEN2], - float* echo_subtractor_output, - float output[NUM_HIGH_BANDS_MAX + 1][PART_LEN]) { - float efw[2][PART_LEN1]; - float xfw[2][PART_LEN1]; - float dfw[2][PART_LEN1]; - float comfortNoiseHband[2][PART_LEN1]; - float fft[PART_LEN2]; - float nlpGainHband; - int i; - size_t j; - - // Coherence and non-linear filter - float cohde[PART_LEN1], cohxd[PART_LEN1]; - float hNl[PART_LEN1]; - - // Filter energy - const int delayEstInterval = 10 * aec->mult; - - float* xfw_ptr = NULL; - - // Update eBuf with echo subtractor output. - memcpy(aec->eBuf + PART_LEN, echo_subtractor_output, - sizeof(float) * PART_LEN); - - // Analysis filter banks for the echo suppressor. - // Windowed near-end ffts. - WindowData(fft, nearend_extended_block_lowest_band); - ooura_fft.Fft(fft); - StoreAsComplex(fft, dfw); - - // Windowed echo suppressor output ffts. - WindowData(fft, aec->eBuf); - ooura_fft.Fft(fft); - StoreAsComplex(fft, efw); - - // NLP - - // Convert far-end partition to the frequency domain with windowing. - WindowData(fft, farend_extended_block); - Fft(ooura_fft, fft, xfw); - xfw_ptr = &xfw[0][0]; - - // Buffer far. - memcpy(aec->xfwBuf, xfw_ptr, sizeof(float) * 2 * PART_LEN1); - - aec->delayEstCtr++; - if (aec->delayEstCtr == delayEstInterval) { - aec->delayEstCtr = 0; - aec->delayIdx = WebRtcAec_PartitionDelay(aec->num_partitions, aec->wfBuf); - } - - aec->data_dumper->DumpRaw("aec_nlp_delay", 1, &aec->delayIdx); - - // Use delayed far. - memcpy(xfw, aec->xfwBuf + aec->delayIdx * PART_LEN1, - sizeof(xfw[0][0]) * 2 * PART_LEN1); - - WebRtcAec_UpdateCoherenceSpectra(aec->mult, aec->extended_filter_enabled == 1, - efw, dfw, xfw, &aec->coherence_state, - &aec->divergeState, - &aec->extreme_filter_divergence); - - WebRtcAec_ComputeCoherence(&aec->coherence_state, cohde, cohxd); - - // Select the microphone signal as output if the filter is deemed to have - // diverged. - if (aec->divergeState) { - memcpy(efw, dfw, sizeof(efw[0][0]) * 2 * PART_LEN1); - } - - FormSuppressionGain(aec, cohde, cohxd, hNl); - - aec->data_dumper->DumpRaw("aec_nlp_gain", PART_LEN1, hNl); - - WebRtcAec_Suppress(hNl, efw); - - // Add comfort noise. - ComfortNoise(aec->num_bands > 1, &aec->seed, efw, comfortNoiseHband, - aec->noisePow, hNl); - - // Inverse error fft. - ScaledInverseFft(ooura_fft, efw, fft, 2.0f, 1); - - // Overlap and add to obtain output. - for (i = 0; i < PART_LEN; i++) { - output[0][i] = (fft[i] * WebRtcAec_sqrtHanning[i] + - aec->outBuf[i] * WebRtcAec_sqrtHanning[PART_LEN - i]); - - // Saturate output to keep it in the allowed range. - output[0][i] = WEBRTC_SPL_SAT(WEBRTC_SPL_WORD16_MAX, output[0][i], - WEBRTC_SPL_WORD16_MIN); - } - memcpy(aec->outBuf, &fft[PART_LEN], PART_LEN * sizeof(aec->outBuf[0])); - - // For H band - if (aec->num_bands > 1) { - // H band gain - // average nlp over low band: average over second half of freq spectrum - // (4->8khz) - GetHighbandGain(hNl, &nlpGainHband); - - // Inverse comfort_noise - ScaledInverseFft(ooura_fft, comfortNoiseHband, fft, 2.0f, 0); - - // compute gain factor - for (j = 1; j < aec->num_bands; ++j) { - for (i = 0; i < PART_LEN; i++) { - output[j][i] = aec->previous_nearend_block[j][i] * nlpGainHband; - } - } - - // Add some comfort noise where Hband is attenuated. - for (i = 0; i < PART_LEN; i++) { - output[1][i] += cnScaleHband * fft[i]; - } - - // Saturate output to keep it in the allowed range. - for (j = 1; j < aec->num_bands; ++j) { - for (i = 0; i < PART_LEN; i++) { - output[j][i] = WEBRTC_SPL_SAT(WEBRTC_SPL_WORD16_MAX, output[j][i], - WEBRTC_SPL_WORD16_MIN); - } - } - } - - // Copy the current block to the old position. - memcpy(aec->eBuf, aec->eBuf + PART_LEN, sizeof(float) * PART_LEN); - - memmove(aec->xfwBuf + PART_LEN1, aec->xfwBuf, - sizeof(aec->xfwBuf) - sizeof(complex_t) * PART_LEN1); -} - -static void ProcessNearendBlock( - AecCore* aec, - float farend_extended_block_lowest_band[PART_LEN2], - float nearend_block[NUM_HIGH_BANDS_MAX + 1][PART_LEN], - float output_block[NUM_HIGH_BANDS_MAX + 1][PART_LEN]) { - size_t i; - - float fft[PART_LEN2]; - float nearend_extended_block_lowest_band[PART_LEN2]; - float farend_fft[2][PART_LEN1]; - float nearend_fft[2][PART_LEN1]; - float far_spectrum = 0.0f; - float near_spectrum = 0.0f; - float abs_far_spectrum[PART_LEN1]; - float abs_near_spectrum[PART_LEN1]; - - const float gPow[2] = {0.9f, 0.1f}; - - // Noise estimate constants. - const int noiseInitBlocks = 500 * aec->mult; - const float step = 0.1f; - const float ramp = 1.0002f; - const float gInitNoise[2] = {0.999f, 0.001f}; - - float echo_subtractor_output[PART_LEN]; - - aec->data_dumper->DumpWav("aec_far", PART_LEN, - &farend_extended_block_lowest_band[PART_LEN], - std::min(aec->sampFreq, 16000), 1); - aec->data_dumper->DumpWav("aec_near", PART_LEN, &nearend_block[0][0], - std::min(aec->sampFreq, 16000), 1); - - if (aec->metricsMode == 1) { - // Update power levels - UpdateLevel( - &aec->farlevel, - CalculatePower(&farend_extended_block_lowest_band[PART_LEN], PART_LEN)); - UpdateLevel(&aec->nearlevel, - CalculatePower(&nearend_block[0][0], PART_LEN)); - } - - // Convert far-end signal to the frequency domain. - memcpy(fft, farend_extended_block_lowest_band, sizeof(float) * PART_LEN2); - Fft(aec->ooura_fft, fft, farend_fft); - - // Form extended nearend frame. - memcpy(&nearend_extended_block_lowest_band[0], - &aec->previous_nearend_block[0][0], sizeof(float) * PART_LEN); - memcpy(&nearend_extended_block_lowest_band[PART_LEN], &nearend_block[0][0], - sizeof(float) * PART_LEN); - - // Convert near-end signal to the frequency domain. - memcpy(fft, nearend_extended_block_lowest_band, sizeof(float) * PART_LEN2); - Fft(aec->ooura_fft, fft, nearend_fft); - - // Power smoothing. - if (aec->refined_adaptive_filter_enabled) { - for (i = 0; i < PART_LEN1; ++i) { - far_spectrum = farend_fft[0][i] * farend_fft[0][i] + - farend_fft[1][i] * farend_fft[1][i]; - // Calculate the magnitude spectrum. - abs_far_spectrum[i] = sqrtf(far_spectrum); - } - RegressorPower(aec->num_partitions, aec->xfBufBlockPos, aec->xfBuf, - aec->xPow); - } else { - for (i = 0; i < PART_LEN1; ++i) { - far_spectrum = farend_fft[0][i] * farend_fft[0][i] + - farend_fft[1][i] * farend_fft[1][i]; - aec->xPow[i] = - gPow[0] * aec->xPow[i] + gPow[1] * aec->num_partitions * far_spectrum; - // Calculate the magnitude spectrum. - abs_far_spectrum[i] = sqrtf(far_spectrum); - } - } - - for (i = 0; i < PART_LEN1; ++i) { - near_spectrum = nearend_fft[0][i] * nearend_fft[0][i] + - nearend_fft[1][i] * nearend_fft[1][i]; - aec->dPow[i] = gPow[0] * aec->dPow[i] + gPow[1] * near_spectrum; - // Calculate the magnitude spectrum. - abs_near_spectrum[i] = sqrtf(near_spectrum); - } - - // Estimate noise power. Wait until dPow is more stable. - if (aec->noiseEstCtr > 50) { - for (i = 0; i < PART_LEN1; i++) { - if (aec->dPow[i] < aec->dMinPow[i]) { - aec->dMinPow[i] = - (aec->dPow[i] + step * (aec->dMinPow[i] - aec->dPow[i])) * ramp; - } else { - aec->dMinPow[i] *= ramp; - } - } - } - - // Smooth increasing noise power from zero at the start, - // to avoid a sudden burst of comfort noise. - if (aec->noiseEstCtr < noiseInitBlocks) { - aec->noiseEstCtr++; - for (i = 0; i < PART_LEN1; i++) { - if (aec->dMinPow[i] > aec->dInitMinPow[i]) { - aec->dInitMinPow[i] = gInitNoise[0] * aec->dInitMinPow[i] + - gInitNoise[1] * aec->dMinPow[i]; - } else { - aec->dInitMinPow[i] = aec->dMinPow[i]; - } - } - aec->noisePow = aec->dInitMinPow; - } else { - aec->noisePow = aec->dMinPow; - } - - // Block wise delay estimation used for logging - if (aec->delay_logging_enabled) { - if (WebRtc_AddFarSpectrumFloat(aec->delay_estimator_farend, - abs_far_spectrum, PART_LEN1) == 0) { - int delay_estimate = WebRtc_DelayEstimatorProcessFloat( - aec->delay_estimator, abs_near_spectrum, PART_LEN1); - if (delay_estimate >= 0) { - // Update delay estimate buffer. - aec->delay_histogram[delay_estimate]++; - aec->num_delay_values++; - } - if (aec->delay_metrics_delivered == 1 && - aec->num_delay_values >= kDelayMetricsAggregationWindow) { - UpdateDelayMetrics(aec); - } - } - } - - // Perform echo subtraction. - EchoSubtraction( - aec->ooura_fft, aec->num_partitions, aec->extended_filter_enabled, - &aec->extreme_filter_divergence, aec->filter_step_size, - aec->error_threshold, &farend_fft[0][0], &aec->xfBufBlockPos, aec->xfBuf, - &nearend_block[0][0], aec->xPow, aec->wfBuf, echo_subtractor_output); - aec->data_dumper->DumpRaw("aec_h_fft", PART_LEN1 * aec->num_partitions, - &aec->wfBuf[0][0]); - aec->data_dumper->DumpRaw("aec_h_fft", PART_LEN1 * aec->num_partitions, - &aec->wfBuf[1][0]); - - aec->data_dumper->DumpWav("aec_out_linear", PART_LEN, echo_subtractor_output, - std::min(aec->sampFreq, 16000), 1); - - if (aec->metricsMode == 1) { - UpdateLevel(&aec->linoutlevel, - CalculatePower(echo_subtractor_output, PART_LEN)); - } - - // Perform echo suppression. - EchoSuppression(aec->ooura_fft, aec, nearend_extended_block_lowest_band, - farend_extended_block_lowest_band, echo_subtractor_output, - output_block); - - if (aec->metricsMode == 1) { - UpdateLevel(&aec->nlpoutlevel, - CalculatePower(&output_block[0][0], PART_LEN)); - UpdateMetrics(aec); - } - - // Store the nearend signal until the next frame. - for (i = 0; i < aec->num_bands; ++i) { - memcpy(&aec->previous_nearend_block[i][0], &nearend_block[i][0], - sizeof(float) * PART_LEN); - } - - aec->data_dumper->DumpWav("aec_out", PART_LEN, &output_block[0][0], - std::min(aec->sampFreq, 16000), 1); -} - -AecCore* WebRtcAec_CreateAec(int instance_count) { - AecCore* aec = new AecCore(instance_count); - - if (!aec) { - return NULL; - } - aec->nearend_buffer_size = 0; - memset(&aec->nearend_buffer[0], 0, sizeof(aec->nearend_buffer)); - // Start the output buffer with zeros to be able to produce - // a full output frame in the first frame. - aec->output_buffer_size = PART_LEN - (FRAME_LEN - PART_LEN); - memset(&aec->output_buffer[0], 0, sizeof(aec->output_buffer)); - - aec->delay_estimator_farend = - WebRtc_CreateDelayEstimatorFarend(PART_LEN1, kHistorySizeBlocks); - if (aec->delay_estimator_farend == NULL) { - WebRtcAec_FreeAec(aec); - return NULL; - } - // We create the delay_estimator with the same amount of maximum lookahead as - // the delay history size (kHistorySizeBlocks) for symmetry reasons. - aec->delay_estimator = WebRtc_CreateDelayEstimator( - aec->delay_estimator_farend, kHistorySizeBlocks); - if (aec->delay_estimator == NULL) { - WebRtcAec_FreeAec(aec); - return NULL; - } -#ifdef WEBRTC_ANDROID - aec->delay_agnostic_enabled = 1; // DA-AEC enabled by default. - // DA-AEC assumes the system is causal from the beginning and will self adjust - // the lookahead when shifting is required. - WebRtc_set_lookahead(aec->delay_estimator, 0); -#else - aec->delay_agnostic_enabled = 0; - WebRtc_set_lookahead(aec->delay_estimator, kLookaheadBlocks); -#endif - aec->extended_filter_enabled = 0; - aec->refined_adaptive_filter_enabled = false; - - // Assembly optimization - WebRtcAec_FilterFar = FilterFar; - WebRtcAec_ScaleErrorSignal = ScaleErrorSignal; - WebRtcAec_FilterAdaptation = FilterAdaptation; - WebRtcAec_Overdrive = Overdrive; - WebRtcAec_Suppress = Suppress; - WebRtcAec_ComputeCoherence = ComputeCoherence; - WebRtcAec_UpdateCoherenceSpectra = UpdateCoherenceSpectra; - WebRtcAec_StoreAsComplex = StoreAsComplex; - WebRtcAec_PartitionDelay = PartitionDelay; - WebRtcAec_WindowData = WindowData; - -#if defined(WEBRTC_ARCH_X86_FAMILY) - if (WebRtc_GetCPUInfo(kSSE2)) { - WebRtcAec_InitAec_SSE2(); - } -#endif - -#if defined(MIPS_FPU_LE) - WebRtcAec_InitAec_mips(); -#endif - -#if defined(WEBRTC_HAS_NEON) - WebRtcAec_InitAec_neon(); -#endif - - return aec; -} - -void WebRtcAec_FreeAec(AecCore* aec) { - if (aec == NULL) { - return; - } - - WebRtc_FreeDelayEstimator(aec->delay_estimator); - WebRtc_FreeDelayEstimatorFarend(aec->delay_estimator_farend); - - delete aec; -} - -static void SetAdaptiveFilterStepSize(AecCore* aec) { - // Extended filter adaptation parameter. - // TODO(ajm): No narrowband tuning yet. - const float kExtendedMu = 0.4f; - - if (aec->refined_adaptive_filter_enabled) { - aec->filter_step_size = 0.05f; - } else { - if (aec->extended_filter_enabled) { - aec->filter_step_size = kExtendedMu; - } else { - if (aec->sampFreq == 8000) { - aec->filter_step_size = 0.6f; - } else { - aec->filter_step_size = 0.5f; - } - } - } -} - -static void SetErrorThreshold(AecCore* aec) { - // Extended filter adaptation parameter. - // TODO(ajm): No narrowband tuning yet. - static const float kExtendedErrorThreshold = 1.0e-6f; - - if (aec->extended_filter_enabled) { - aec->error_threshold = kExtendedErrorThreshold; - } else { - if (aec->sampFreq == 8000) { - aec->error_threshold = 2e-6f; - } else { - aec->error_threshold = 1.5e-6f; - } - } -} - -int WebRtcAec_InitAec(AecCore* aec, int sampFreq) { - int i; - aec->data_dumper->InitiateNewSetOfRecordings(); - - aec->sampFreq = sampFreq; - - SetAdaptiveFilterStepSize(aec); - SetErrorThreshold(aec); - - if (sampFreq == 8000) { - aec->num_bands = 1; - } else { - aec->num_bands = (size_t)(sampFreq / 16000); - } - - // Start the output buffer with zeros to be able to produce - // a full output frame in the first frame. - aec->output_buffer_size = PART_LEN - (FRAME_LEN - PART_LEN); - memset(&aec->output_buffer[0], 0, sizeof(aec->output_buffer)); - aec->nearend_buffer_size = 0; - memset(&aec->nearend_buffer[0], 0, sizeof(aec->nearend_buffer)); - - // Initialize far-end buffer. - aec->farend_block_buffer_.ReInit(); - - aec->system_delay = 0; - - if (WebRtc_InitDelayEstimatorFarend(aec->delay_estimator_farend) != 0) { - return -1; - } - if (WebRtc_InitDelayEstimator(aec->delay_estimator) != 0) { - return -1; - } - aec->delay_logging_enabled = 0; - aec->delay_metrics_delivered = 0; - memset(aec->delay_histogram, 0, sizeof(aec->delay_histogram)); - aec->num_delay_values = 0; - aec->delay_median = -1; - aec->delay_std = -1; - aec->fraction_poor_delays = -1.0f; - - aec->previous_delay = -2; // (-2): Uninitialized. - aec->delay_correction_count = 0; - aec->shift_offset = kInitialShiftOffset; - aec->delay_quality_threshold = kDelayQualityThresholdMin; - - aec->num_partitions = kNormalNumPartitions; - - // Update the delay estimator with filter length. We use half the - // |num_partitions| to take the echo path into account. In practice we say - // that the echo has a duration of maximum half |num_partitions|, which is not - // true, but serves as a crude measure. - WebRtc_set_allowed_offset(aec->delay_estimator, aec->num_partitions / 2); - // TODO(bjornv): I currently hard coded the enable. Once we've established - // that AECM has no performance regression, robust_validation will be enabled - // all the time and the APIs to turn it on/off will be removed. Hence, remove - // this line then. - WebRtc_enable_robust_validation(aec->delay_estimator, 1); - aec->frame_count = 0; - - // Default target suppression mode. - aec->nlp_mode = 1; - - // Sampling frequency multiplier w.r.t. 8 kHz. - // In case of multiple bands we process the lower band in 16 kHz, hence the - // multiplier is always 2. - if (aec->num_bands > 1) { - aec->mult = 2; - } else { - aec->mult = static_cast(aec->sampFreq) / 8000; - } - - aec->farBufWritePos = 0; - aec->farBufReadPos = 0; - - aec->inSamples = 0; - aec->outSamples = 0; - aec->knownDelay = 0; - - // Initialize buffers - memset(aec->previous_nearend_block, 0, sizeof(aec->previous_nearend_block)); - memset(aec->eBuf, 0, sizeof(aec->eBuf)); - - memset(aec->xPow, 0, sizeof(aec->xPow)); - memset(aec->dPow, 0, sizeof(aec->dPow)); - memset(aec->dInitMinPow, 0, sizeof(aec->dInitMinPow)); - aec->noisePow = aec->dInitMinPow; - aec->noiseEstCtr = 0; - - // Initial comfort noise power - for (i = 0; i < PART_LEN1; i++) { - aec->dMinPow[i] = 1.0e6f; - } - - // Holds the last block written to - aec->xfBufBlockPos = 0; - // TODO(peah): Investigate need for these initializations. Deleting them - // doesn't change the output at all and yields 0.4% overall speedup. - memset(aec->xfBuf, 0, sizeof(complex_t) * kExtendedNumPartitions * PART_LEN1); - memset(aec->wfBuf, 0, sizeof(complex_t) * kExtendedNumPartitions * PART_LEN1); - memset(aec->coherence_state.sde, 0, sizeof(complex_t) * PART_LEN1); - memset(aec->coherence_state.sxd, 0, sizeof(complex_t) * PART_LEN1); - memset(aec->xfwBuf, 0, - sizeof(complex_t) * kExtendedNumPartitions * PART_LEN1); - memset(aec->coherence_state.se, 0, sizeof(float) * PART_LEN1); - - // To prevent numerical instability in the first block. - for (i = 0; i < PART_LEN1; i++) { - aec->coherence_state.sd[i] = 1; - } - for (i = 0; i < PART_LEN1; i++) { - aec->coherence_state.sx[i] = 1; - } - - memset(aec->hNs, 0, sizeof(aec->hNs)); - memset(aec->outBuf, 0, sizeof(float) * PART_LEN); - - aec->hNlFbMin = 1; - aec->hNlFbLocalMin = 1; - aec->hNlXdAvgMin = 1; - aec->hNlNewMin = 0; - aec->hNlMinCtr = 0; - aec->overDrive = 2; - aec->overdrive_scaling = 2; - aec->delayIdx = 0; - aec->stNearState = 0; - aec->echoState = 0; - aec->divergeState = 0; - - aec->seed = 777; - aec->delayEstCtr = 0; - - aec->extreme_filter_divergence = 0; - - // Metrics disabled by default - aec->metricsMode = 0; - InitMetrics(aec); - - return 0; -} - -void WebRtcAec_BufferFarendBlock(AecCore* aec, const float* farend) { - // Check if the buffer is full, and in that case flush the oldest data. - if (aec->farend_block_buffer_.AvaliableSpace() < 1) { - aec->farend_block_buffer_.AdjustSize(1); - } - aec->farend_block_buffer_.Insert(farend); -} - -int WebRtcAec_AdjustFarendBufferSizeAndSystemDelay(AecCore* aec, - int buffer_size_decrease) { - int achieved_buffer_size_decrease = - aec->farend_block_buffer_.AdjustSize(buffer_size_decrease); - aec->system_delay -= achieved_buffer_size_decrease * PART_LEN; - return achieved_buffer_size_decrease; -} - -void FormNearendBlock( - size_t nearend_start_index, - size_t num_bands, - const float* const* nearend_frame, - size_t num_samples_from_nearend_frame, - const float nearend_buffer[NUM_HIGH_BANDS_MAX + 1] - [PART_LEN - (FRAME_LEN - PART_LEN)], - float nearend_block[NUM_HIGH_BANDS_MAX + 1][PART_LEN]) { - RTC_DCHECK_LE(num_samples_from_nearend_frame, PART_LEN); - const int num_samples_from_buffer = PART_LEN - num_samples_from_nearend_frame; - - if (num_samples_from_buffer > 0) { - for (size_t i = 0; i < num_bands; ++i) { - memcpy(&nearend_block[i][0], &nearend_buffer[i][0], - num_samples_from_buffer * sizeof(float)); - } - } - - for (size_t i = 0; i < num_bands; ++i) { - memcpy(&nearend_block[i][num_samples_from_buffer], - &nearend_frame[i][nearend_start_index], - num_samples_from_nearend_frame * sizeof(float)); - } -} - -void BufferNearendFrame( - size_t nearend_start_index, - size_t num_bands, - const float* const* nearend_frame, - size_t num_samples_to_buffer, - float nearend_buffer[NUM_HIGH_BANDS_MAX + 1] - [PART_LEN - (FRAME_LEN - PART_LEN)]) { - for (size_t i = 0; i < num_bands; ++i) { - memcpy(&nearend_buffer[i][0], - &nearend_frame[i][nearend_start_index + FRAME_LEN - - num_samples_to_buffer], - num_samples_to_buffer * sizeof(float)); - } -} - -void BufferOutputBlock( - size_t num_bands, - const float output_block[NUM_HIGH_BANDS_MAX + 1][PART_LEN], - size_t* output_buffer_size, - float output_buffer[NUM_HIGH_BANDS_MAX + 1][2 * PART_LEN]) { - for (size_t i = 0; i < num_bands; ++i) { - memcpy(&output_buffer[i][*output_buffer_size], &output_block[i][0], - PART_LEN * sizeof(float)); - } - (*output_buffer_size) += PART_LEN; -} - -void FormOutputFrame(size_t output_start_index, - size_t num_bands, - size_t* output_buffer_size, - float output_buffer[NUM_HIGH_BANDS_MAX + 1][2 * PART_LEN], - float* const* output_frame) { - RTC_DCHECK_LE(FRAME_LEN, *output_buffer_size); - for (size_t i = 0; i < num_bands; ++i) { - memcpy(&output_frame[i][output_start_index], &output_buffer[i][0], - FRAME_LEN * sizeof(float)); - } - (*output_buffer_size) -= FRAME_LEN; - if (*output_buffer_size > 0) { - RTC_DCHECK_GE(2 * PART_LEN - FRAME_LEN, (*output_buffer_size)); - for (size_t i = 0; i < num_bands; ++i) { - memcpy(&output_buffer[i][0], &output_buffer[i][FRAME_LEN], - (*output_buffer_size) * sizeof(float)); - } - } -} - -void WebRtcAec_ProcessFrames(AecCore* aec, - const float* const* nearend, - size_t num_bands, - size_t num_samples, - int knownDelay, - float* const* out) { - RTC_DCHECK(num_samples == 80 || num_samples == 160); - - aec->frame_count++; - // For each frame the process is as follows: - // 1) If the system_delay indicates on being too small for processing a - // frame we stuff the buffer with enough data for 10 ms. - // 2 a) Adjust the buffer to the system delay, by moving the read pointer. - // b) Apply signal based delay correction, if we have detected poor AEC - // performance. - // 3) TODO(bjornv): Investigate if we need to add this: - // If we can't move read pointer due to buffer size limitations we - // flush/stuff the buffer. - // 4) Process as many partitions as possible. - // 5) Update the |system_delay| with respect to a full frame of FRAME_LEN - // samples. Even though we will have data left to process (we work with - // partitions) we consider updating a whole frame, since that's the - // amount of data we input and output in audio_processing. - // 6) Update the outputs. - - // The AEC has two different delay estimation algorithms built in. The - // first relies on delay input values from the user and the amount of - // shifted buffer elements is controlled by |knownDelay|. This delay will - // give a guess on how much we need to shift far-end buffers to align with - // the near-end signal. The other delay estimation algorithm uses the - // far- and near-end signals to find the offset between them. This one - // (called "signal delay") is then used to fine tune the alignment, or - // simply compensate for errors in the system based one. - // Note that the two algorithms operate independently. Currently, we only - // allow one algorithm to be turned on. - - RTC_DCHECK_EQ(aec->num_bands, num_bands); - - for (size_t j = 0; j < num_samples; j += FRAME_LEN) { - // 1) At most we process |aec->mult|+1 partitions in 10 ms. Make sure we - // have enough far-end data for that by stuffing the buffer if the - // |system_delay| indicates others. - if (aec->system_delay < FRAME_LEN) { - // We don't have enough data so we rewind 10 ms. - WebRtcAec_AdjustFarendBufferSizeAndSystemDelay(aec, -(aec->mult + 1)); - } - - if (!aec->delay_agnostic_enabled) { - // 2 a) Compensate for a possible change in the system delay. - - // TODO(bjornv): Investigate how we should round the delay difference; - // right now we know that incoming |knownDelay| is underestimated when - // it's less than |aec->knownDelay|. We therefore, round (-32) in that - // direction. In the other direction, we don't have this situation, but - // might flush one partition too little. This can cause non-causality, - // which should be investigated. Maybe, allow for a non-symmetric - // rounding, like -16. - int move_elements = (aec->knownDelay - knownDelay - 32) / PART_LEN; - int moved_elements = aec->farend_block_buffer_.AdjustSize(move_elements); - aec->knownDelay -= moved_elements * PART_LEN; - } else { - // 2 b) Apply signal based delay correction. - int move_elements = SignalBasedDelayCorrection(aec); - int moved_elements = aec->farend_block_buffer_.AdjustSize(move_elements); - int far_near_buffer_diff = - aec->farend_block_buffer_.Size() - - (aec->nearend_buffer_size + FRAME_LEN) / PART_LEN; - WebRtc_SoftResetDelayEstimator(aec->delay_estimator, moved_elements); - WebRtc_SoftResetDelayEstimatorFarend(aec->delay_estimator_farend, - moved_elements); - // If we rely on reported system delay values only, a buffer underrun here - // can never occur since we've taken care of that in 1) above. Here, we - // apply signal based delay correction and can therefore end up with - // buffer underruns since the delay estimation can be wrong. We therefore - // stuff the buffer with enough elements if needed. - if (far_near_buffer_diff < 0) { - WebRtcAec_AdjustFarendBufferSizeAndSystemDelay(aec, - far_near_buffer_diff); - } - } - - static_assert( - 16 == (FRAME_LEN - PART_LEN), - "These constants need to be properly related for this code to work"); - float output_block[NUM_HIGH_BANDS_MAX + 1][PART_LEN]; - float nearend_block[NUM_HIGH_BANDS_MAX + 1][PART_LEN]; - float farend_extended_block_lowest_band[PART_LEN2]; - - // Form and process a block of nearend samples, buffer the output block of - // samples. - aec->farend_block_buffer_.ExtractExtendedBlock( - farend_extended_block_lowest_band); - FormNearendBlock(j, num_bands, nearend, PART_LEN - aec->nearend_buffer_size, - aec->nearend_buffer, nearend_block); - ProcessNearendBlock(aec, farend_extended_block_lowest_band, nearend_block, - output_block); - BufferOutputBlock(num_bands, output_block, &aec->output_buffer_size, - aec->output_buffer); - - if ((FRAME_LEN - PART_LEN + aec->nearend_buffer_size) == PART_LEN) { - // When possible (every fourth frame) form and process a second block of - // nearend samples, buffer the output block of samples. - aec->farend_block_buffer_.ExtractExtendedBlock( - farend_extended_block_lowest_band); - FormNearendBlock(j + FRAME_LEN - PART_LEN, num_bands, nearend, PART_LEN, - aec->nearend_buffer, nearend_block); - ProcessNearendBlock(aec, farend_extended_block_lowest_band, nearend_block, - output_block); - BufferOutputBlock(num_bands, output_block, &aec->output_buffer_size, - aec->output_buffer); - - // Reset the buffer size as there are no samples left in the nearend input - // to buffer. - aec->nearend_buffer_size = 0; - } else { - // Buffer the remaining samples in the nearend input. - aec->nearend_buffer_size += FRAME_LEN - PART_LEN; - BufferNearendFrame(j, num_bands, nearend, aec->nearend_buffer_size, - aec->nearend_buffer); - } - - // 5) Update system delay with respect to the entire frame. - aec->system_delay -= FRAME_LEN; - - // 6) Form the output frame. - FormOutputFrame(j, num_bands, &aec->output_buffer_size, aec->output_buffer, - out); - } -} - -int WebRtcAec_GetDelayMetricsCore(AecCore* self, - int* median, - int* std, - float* fraction_poor_delays) { - RTC_DCHECK(self); - RTC_DCHECK(median); - RTC_DCHECK(std); - - if (self->delay_logging_enabled == 0) { - // Logging disabled. - return -1; - } - - if (self->delay_metrics_delivered == 0) { - UpdateDelayMetrics(self); - self->delay_metrics_delivered = 1; - } - *median = self->delay_median; - *std = self->delay_std; - *fraction_poor_delays = self->fraction_poor_delays; - - return 0; -} - -int WebRtcAec_echo_state(AecCore* self) { - return self->echoState; -} - -void WebRtcAec_GetEchoStats(AecCore* self, - Stats* erl, - Stats* erle, - Stats* a_nlp, - float* divergent_filter_fraction) { - RTC_DCHECK(erl); - RTC_DCHECK(erle); - RTC_DCHECK(a_nlp); - *erl = self->erl; - *erle = self->erle; - *a_nlp = self->aNlp; - *divergent_filter_fraction = - self->divergent_filter_fraction.GetLatestFraction(); -} - -void WebRtcAec_SetConfigCore(AecCore* self, - int nlp_mode, - int metrics_mode, - int delay_logging) { - RTC_DCHECK_GE(nlp_mode, 0); - RTC_DCHECK_LT(nlp_mode, 3); - self->nlp_mode = nlp_mode; - self->metricsMode = metrics_mode; - if (self->metricsMode) { - InitMetrics(self); - } - // Turn on delay logging if it is either set explicitly or if delay agnostic - // AEC is enabled (which requires delay estimates). - self->delay_logging_enabled = delay_logging || self->delay_agnostic_enabled; - if (self->delay_logging_enabled) { - memset(self->delay_histogram, 0, sizeof(self->delay_histogram)); - } -} - -void WebRtcAec_enable_delay_agnostic(AecCore* self, int enable) { - self->delay_agnostic_enabled = enable; -} - -int WebRtcAec_delay_agnostic_enabled(AecCore* self) { - return self->delay_agnostic_enabled; -} - -void WebRtcAec_enable_refined_adaptive_filter(AecCore* self, bool enable) { - self->refined_adaptive_filter_enabled = enable; - SetAdaptiveFilterStepSize(self); - SetErrorThreshold(self); -} - -bool WebRtcAec_refined_adaptive_filter_enabled(const AecCore* self) { - return self->refined_adaptive_filter_enabled; -} - -void WebRtcAec_enable_extended_filter(AecCore* self, int enable) { - self->extended_filter_enabled = enable; - SetAdaptiveFilterStepSize(self); - SetErrorThreshold(self); - self->num_partitions = enable ? kExtendedNumPartitions : kNormalNumPartitions; - // Update the delay estimator with filter length. See InitAEC() for details. - WebRtc_set_allowed_offset(self->delay_estimator, self->num_partitions / 2); -} - -int WebRtcAec_extended_filter_enabled(AecCore* self) { - return self->extended_filter_enabled; -} - -int WebRtcAec_system_delay(AecCore* self) { - return self->system_delay; -} - -void WebRtcAec_SetSystemDelay(AecCore* self, int delay) { - RTC_DCHECK_GE(delay, 0); - self->system_delay = delay; -} -} // namespace webrtc diff --git a/modules/audio_processing/aec/aec_core.h b/modules/audio_processing/aec/aec_core.h deleted file mode 100644 index 659b6a1de8..0000000000 --- a/modules/audio_processing/aec/aec_core.h +++ /dev/null @@ -1,333 +0,0 @@ -/* - * 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. - */ - -/* - * Specifies the interface for the AEC core. - */ - -#ifndef MODULES_AUDIO_PROCESSING_AEC_AEC_CORE_H_ -#define MODULES_AUDIO_PROCESSING_AEC_AEC_CORE_H_ - -#include - -#include - -extern "C" { -#include "common_audio/ring_buffer.h" -} -#include "modules/audio_processing/aec/aec_common.h" -#include "modules/audio_processing/utility/block_mean_calculator.h" -#include "modules/audio_processing/utility/ooura_fft.h" -#include "rtc_base/constructor_magic.h" - -namespace webrtc { - -#define FRAME_LEN 80 -#define PART_LEN 64 // Length of partition -#define PART_LEN1 (PART_LEN + 1) // Unique fft coefficients -#define PART_LEN2 (PART_LEN * 2) // Length of partition * 2 -#define NUM_HIGH_BANDS_MAX 2 // Max number of high bands - -class ApmDataDumper; - -typedef float complex_t[2]; -// For performance reasons, some arrays of complex numbers are replaced by twice -// as long arrays of float, all the real parts followed by all the imaginary -// ones (complex_t[SIZE] -> float[2][SIZE]). This allows SIMD optimizations and -// is better than two arrays (one for the real parts and one for the imaginary -// parts) as this other way would require two pointers instead of one and cause -// extra register spilling. This also allows the offsets to be calculated at -// compile time. - -// Metrics -enum { kOffsetLevel = -100 }; - -typedef struct Stats { - float instant; - float average; - float min; - float max; - float sum; - float hisum; - float himean; - size_t counter; - size_t hicounter; -} Stats; - -// Number of partitions for the extended filter mode. The first one is an enum -// to be used in array declarations, as it represents the maximum filter length. -enum { kExtendedNumPartitions = 32 }; -static const int kNormalNumPartitions = 12; - -// Delay estimator constants, used for logging and delay compensation if -// if reported delays are disabled. -enum { kLookaheadBlocks = 15 }; -enum { - // 500 ms for 16 kHz which is equivalent with the limit of reported delays. - kHistorySizeBlocks = 125 -}; - -typedef struct PowerLevel { - PowerLevel(); - - BlockMeanCalculator framelevel; - BlockMeanCalculator averagelevel; - float minlevel; -} PowerLevel; - -class Aec2BlockBuffer { - public: - Aec2BlockBuffer(); - ~Aec2BlockBuffer(); - void ReInit(); - void Insert(const float block[PART_LEN]); - void ExtractExtendedBlock(float extended_block[PART_LEN]); - int AdjustSize(int buffer_size_decrease); - size_t Size(); - size_t AvaliableSpace(); - - private: - RingBuffer* buffer_; -}; - -class DivergentFilterFraction { - public: - DivergentFilterFraction(); - - // Reset. - void Reset(); - - void AddObservation(const PowerLevel& nearlevel, - const PowerLevel& linoutlevel, - const PowerLevel& nlpoutlevel); - - // Return the latest fraction. - float GetLatestFraction() const; - - private: - // Clear all values added. - void Clear(); - - size_t count_; - size_t occurrence_; - float fraction_; - - RTC_DISALLOW_COPY_AND_ASSIGN(DivergentFilterFraction); -}; - -typedef struct CoherenceState { - complex_t sde[PART_LEN1]; // cross-psd of nearend and error - complex_t sxd[PART_LEN1]; // cross-psd of farend and nearend - float sx[PART_LEN1], sd[PART_LEN1], se[PART_LEN1]; // far, near, error psd -} CoherenceState; - -struct AecCore { - explicit AecCore(int instance_index); - ~AecCore(); - - std::unique_ptr data_dumper; - const OouraFft ooura_fft; - - CoherenceState coherence_state; - - int farBufWritePos, farBufReadPos; - - int knownDelay; - int inSamples, outSamples; - int delayEstCtr; - - // Nearend buffer used for changing from FRAME_LEN to PART_LEN sample block - // sizes. The buffer stores all the incoming bands and for each band a maximum - // of PART_LEN - (FRAME_LEN - PART_LEN) values need to be buffered in order to - // change the block size from FRAME_LEN to PART_LEN. - float nearend_buffer[NUM_HIGH_BANDS_MAX + 1] - [PART_LEN - (FRAME_LEN - PART_LEN)]; - size_t nearend_buffer_size; - float output_buffer[NUM_HIGH_BANDS_MAX + 1][2 * PART_LEN]; - size_t output_buffer_size; - - float eBuf[PART_LEN2]; // error - - float previous_nearend_block[NUM_HIGH_BANDS_MAX + 1][PART_LEN]; - - float xPow[PART_LEN1]; - float dPow[PART_LEN1]; - float dMinPow[PART_LEN1]; - float dInitMinPow[PART_LEN1]; - float* noisePow; - - float xfBuf[2][kExtendedNumPartitions * PART_LEN1]; // farend fft buffer - float wfBuf[2][kExtendedNumPartitions * PART_LEN1]; // filter fft - // Farend windowed fft buffer. - complex_t xfwBuf[kExtendedNumPartitions * PART_LEN1]; - - float hNs[PART_LEN1]; - float hNlFbMin, hNlFbLocalMin; - float hNlXdAvgMin; - int hNlNewMin, hNlMinCtr; - float overDrive; - float overdrive_scaling; - int nlp_mode; - float outBuf[PART_LEN]; - int delayIdx; - - short stNearState, echoState; - short divergeState; - - int xfBufBlockPos; - - Aec2BlockBuffer farend_block_buffer_; - - int system_delay; // Current system delay buffered in AEC. - - int mult; // sampling frequency multiple - int sampFreq = 16000; - size_t num_bands; - uint32_t seed; - - float filter_step_size; // stepsize - float error_threshold; // error threshold - - int noiseEstCtr; - - PowerLevel farlevel; - PowerLevel nearlevel; - PowerLevel linoutlevel; - PowerLevel nlpoutlevel; - - int metricsMode; - int stateCounter; - Stats erl; - Stats erle; - Stats aNlp; - Stats rerl; - DivergentFilterFraction divergent_filter_fraction; - - // Quantities to control H band scaling for SWB input - int freq_avg_ic; // initial bin for averaging nlp gain - int flag_Hband_cn; // for comfort noise - float cn_scale_Hband; // scale for comfort noise in H band - - int delay_metrics_delivered; - int delay_histogram[kHistorySizeBlocks]; - int num_delay_values; - int delay_median; - int delay_std; - float fraction_poor_delays; - int delay_logging_enabled; - void* delay_estimator_farend; - void* delay_estimator; - // Variables associated with delay correction through signal based delay - // estimation feedback. - int previous_delay; - int delay_correction_count; - int shift_offset; - float delay_quality_threshold; - int frame_count; - - // 0 = delay agnostic mode (signal based delay correction) disabled. - // Otherwise enabled. - int delay_agnostic_enabled; - // 1 = extended filter mode enabled, 0 = disabled. - int extended_filter_enabled; - // 1 = refined filter adaptation aec mode enabled, 0 = disabled. - bool refined_adaptive_filter_enabled; - - // Runtime selection of number of filter partitions. - int num_partitions; - - // Flag that extreme filter divergence has been detected by the Echo - // Suppressor. - int extreme_filter_divergence; -}; - -AecCore* WebRtcAec_CreateAec(int instance_count); // Returns NULL on error. -void WebRtcAec_FreeAec(AecCore* aec); -int WebRtcAec_InitAec(AecCore* aec, int sampFreq); -void WebRtcAec_InitAec_SSE2(void); -#if defined(MIPS_FPU_LE) -void WebRtcAec_InitAec_mips(void); -#endif -#if defined(WEBRTC_HAS_NEON) -void WebRtcAec_InitAec_neon(void); -#endif - -void WebRtcAec_BufferFarendBlock(AecCore* aec, const float* farend); -void WebRtcAec_ProcessFrames(AecCore* aec, - const float* const* nearend, - size_t num_bands, - size_t num_samples, - int knownDelay, - float* const* out); - -// A helper function to call adjust the farend buffer size. -// Returns the number of elements the size was decreased with, and adjusts -// |system_delay| by the corresponding amount in ms. -int WebRtcAec_AdjustFarendBufferSizeAndSystemDelay(AecCore* aec, - int size_decrease); - -// Calculates the median, standard deviation and amount of poor values among the -// delay estimates aggregated up to the first call to the function. After that -// first call the metrics are aggregated and updated every second. With poor -// values we mean values that most likely will cause the AEC to perform poorly. -// TODO(bjornv): Consider changing tests and tools to handle constant -// constant aggregation window throughout the session instead. -int WebRtcAec_GetDelayMetricsCore(AecCore* self, - int* median, - int* std, - float* fraction_poor_delays); - -// Returns the echo state (1: echo, 0: no echo). -int WebRtcAec_echo_state(AecCore* self); - -// Gets statistics of the echo metrics ERL, ERLE, A_NLP. -void WebRtcAec_GetEchoStats(AecCore* self, - Stats* erl, - Stats* erle, - Stats* a_nlp, - float* divergent_filter_fraction); - -// Sets local configuration modes. -void WebRtcAec_SetConfigCore(AecCore* self, - int nlp_mode, - int metrics_mode, - int delay_logging); - -// Non-zero enables, zero disables. -void WebRtcAec_enable_delay_agnostic(AecCore* self, int enable); - -// Returns non-zero if delay agnostic (i.e., signal based delay estimation) is -// enabled and zero if disabled. -int WebRtcAec_delay_agnostic_enabled(AecCore* self); - -// Turns on/off the refined adaptive filter feature. -void WebRtcAec_enable_refined_adaptive_filter(AecCore* self, bool enable); - -// Returns whether the refined adaptive filter is enabled. -bool WebRtcAec_refined_adaptive_filter(const AecCore* self); - -// Enables or disables extended filter mode. Non-zero enables, zero disables. -void WebRtcAec_enable_extended_filter(AecCore* self, int enable); - -// Returns non-zero if extended filter mode is enabled and zero if disabled. -int WebRtcAec_extended_filter_enabled(AecCore* self); - -// Returns the current |system_delay|, i.e., the buffered difference between -// far-end and near-end. -int WebRtcAec_system_delay(AecCore* self); - -// Sets the |system_delay| to |value|. Note that if the value is changed -// improperly, there can be a performance regression. So it should be used with -// care. -void WebRtcAec_SetSystemDelay(AecCore* self, int delay); - -} // namespace webrtc - -#endif // MODULES_AUDIO_PROCESSING_AEC_AEC_CORE_H_ diff --git a/modules/audio_processing/aec/aec_core_mips.cc b/modules/audio_processing/aec/aec_core_mips.cc deleted file mode 100644 index 2b388a7959..0000000000 --- a/modules/audio_processing/aec/aec_core_mips.cc +++ /dev/null @@ -1,485 +0,0 @@ -/* - * Copyright (c) 2013 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. - */ - -/* - * The core AEC algorithm, which is presented with time-aligned signals. - */ - -#include - -#include "modules/audio_processing/aec/aec_core.h" - -extern "C" { -#include "common_audio/signal_processing/include/signal_processing_library.h" -} -#include "modules/audio_processing/aec/aec_core_optimized_methods.h" -#include "modules/audio_processing/utility/ooura_fft.h" - -namespace webrtc { - -extern const float WebRtcAec_weightCurve[65]; -extern const float WebRtcAec_overDriveCurve[65]; - -void WebRtcAec_FilterFar_mips( - int num_partitions, - int x_fft_buf_block_pos, - float x_fft_buf[2][kExtendedNumPartitions * PART_LEN1], - float h_fft_buf[2][kExtendedNumPartitions * PART_LEN1], - float y_fft[2][PART_LEN1]) { - int i; - for (i = 0; i < num_partitions; i++) { - int xPos = (i + x_fft_buf_block_pos) * PART_LEN1; - int pos = i * PART_LEN1; - // Check for wrap - if (i + x_fft_buf_block_pos >= num_partitions) { - xPos -= num_partitions * (PART_LEN1); - } - float* yf0 = y_fft[0]; - float* yf1 = y_fft[1]; - float* aRe = x_fft_buf[0] + xPos; - float* aIm = x_fft_buf[1] + xPos; - float* bRe = h_fft_buf[0] + pos; - float* bIm = h_fft_buf[1] + pos; - float f0, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12, f13; - int len = PART_LEN1 >> 1; - - __asm __volatile( - ".set push \n\t" - ".set noreorder \n\t" - "1: \n\t" - "lwc1 %[f0], 0(%[aRe]) \n\t" - "lwc1 %[f1], 0(%[bRe]) \n\t" - "lwc1 %[f2], 0(%[bIm]) \n\t" - "lwc1 %[f3], 0(%[aIm]) \n\t" - "lwc1 %[f4], 4(%[aRe]) \n\t" - "lwc1 %[f5], 4(%[bRe]) \n\t" - "lwc1 %[f6], 4(%[bIm]) \n\t" - "mul.s %[f8], %[f0], %[f1] \n\t" - "mul.s %[f0], %[f0], %[f2] \n\t" - "mul.s %[f9], %[f4], %[f5] \n\t" - "mul.s %[f4], %[f4], %[f6] \n\t" - "lwc1 %[f7], 4(%[aIm]) \n\t" -#if !defined(MIPS32_R2_LE) - "mul.s %[f12], %[f2], %[f3] \n\t" - "mul.s %[f1], %[f3], %[f1] \n\t" - "mul.s %[f11], %[f6], %[f7] \n\t" - "addiu %[aRe], %[aRe], 8 \n\t" - "addiu %[aIm], %[aIm], 8 \n\t" - "addiu %[len], %[len], -1 \n\t" - "sub.s %[f8], %[f8], %[f12] \n\t" - "mul.s %[f12], %[f7], %[f5] \n\t" - "lwc1 %[f2], 0(%[yf0]) \n\t" - "add.s %[f1], %[f0], %[f1] \n\t" - "lwc1 %[f3], 0(%[yf1]) \n\t" - "sub.s %[f9], %[f9], %[f11] \n\t" - "lwc1 %[f6], 4(%[yf0]) \n\t" - "add.s %[f4], %[f4], %[f12] \n\t" -#else // #if !defined(MIPS32_R2_LE) - "addiu %[aRe], %[aRe], 8 \n\t" - "addiu %[aIm], %[aIm], 8 \n\t" - "addiu %[len], %[len], -1 \n\t" - "nmsub.s %[f8], %[f8], %[f2], %[f3] \n\t" - "lwc1 %[f2], 0(%[yf0]) \n\t" - "madd.s %[f1], %[f0], %[f3], %[f1] \n\t" - "lwc1 %[f3], 0(%[yf1]) \n\t" - "nmsub.s %[f9], %[f9], %[f6], %[f7] \n\t" - "lwc1 %[f6], 4(%[yf0]) \n\t" - "madd.s %[f4], %[f4], %[f7], %[f5] \n\t" -#endif // #if !defined(MIPS32_R2_LE) - "lwc1 %[f5], 4(%[yf1]) \n\t" - "add.s %[f2], %[f2], %[f8] \n\t" - "addiu %[bRe], %[bRe], 8 \n\t" - "addiu %[bIm], %[bIm], 8 \n\t" - "add.s %[f3], %[f3], %[f1] \n\t" - "add.s %[f6], %[f6], %[f9] \n\t" - "add.s %[f5], %[f5], %[f4] \n\t" - "swc1 %[f2], 0(%[yf0]) \n\t" - "swc1 %[f3], 0(%[yf1]) \n\t" - "swc1 %[f6], 4(%[yf0]) \n\t" - "swc1 %[f5], 4(%[yf1]) \n\t" - "addiu %[yf0], %[yf0], 8 \n\t" - "bgtz %[len], 1b \n\t" - " addiu %[yf1], %[yf1], 8 \n\t" - "lwc1 %[f0], 0(%[aRe]) \n\t" - "lwc1 %[f1], 0(%[bRe]) \n\t" - "lwc1 %[f2], 0(%[bIm]) \n\t" - "lwc1 %[f3], 0(%[aIm]) \n\t" - "mul.s %[f8], %[f0], %[f1] \n\t" - "mul.s %[f0], %[f0], %[f2] \n\t" -#if !defined(MIPS32_R2_LE) - "mul.s %[f12], %[f2], %[f3] \n\t" - "mul.s %[f1], %[f3], %[f1] \n\t" - "sub.s %[f8], %[f8], %[f12] \n\t" - "lwc1 %[f2], 0(%[yf0]) \n\t" - "add.s %[f1], %[f0], %[f1] \n\t" - "lwc1 %[f3], 0(%[yf1]) \n\t" -#else // #if !defined(MIPS32_R2_LE) - "nmsub.s %[f8], %[f8], %[f2], %[f3] \n\t" - "lwc1 %[f2], 0(%[yf0]) \n\t" - "madd.s %[f1], %[f0], %[f3], %[f1] \n\t" - "lwc1 %[f3], 0(%[yf1]) \n\t" -#endif // #if !defined(MIPS32_R2_LE) - "add.s %[f2], %[f2], %[f8] \n\t" - "add.s %[f3], %[f3], %[f1] \n\t" - "swc1 %[f2], 0(%[yf0]) \n\t" - "swc1 %[f3], 0(%[yf1]) \n\t" - ".set pop \n\t" - : [f0] "=&f"(f0), [f1] "=&f"(f1), [f2] "=&f"(f2), [f3] "=&f"(f3), - [f4] "=&f"(f4), [f5] "=&f"(f5), [f6] "=&f"(f6), [f7] "=&f"(f7), - [f8] "=&f"(f8), [f9] "=&f"(f9), [f10] "=&f"(f10), [f11] "=&f"(f11), - [f12] "=&f"(f12), [f13] "=&f"(f13), [aRe] "+r"(aRe), [aIm] "+r"(aIm), - [bRe] "+r"(bRe), [bIm] "+r"(bIm), [yf0] "+r"(yf0), [yf1] "+r"(yf1), - [len] "+r"(len) - : - : "memory"); - } -} - -void WebRtcAec_FilterAdaptation_mips( - const OouraFft& ooura_fft, - int num_partitions, - int x_fft_buf_block_pos, - float x_fft_buf[2][kExtendedNumPartitions * PART_LEN1], - float e_fft[2][PART_LEN1], - float h_fft_buf[2][kExtendedNumPartitions * PART_LEN1]) { - float fft[PART_LEN2]; - int i; - for (i = 0; i < num_partitions; i++) { - int xPos = (i + x_fft_buf_block_pos) * (PART_LEN1); - int pos; - // Check for wrap - if (i + x_fft_buf_block_pos >= num_partitions) { - xPos -= num_partitions * PART_LEN1; - } - - pos = i * PART_LEN1; - float* aRe = x_fft_buf[0] + xPos; - float* aIm = x_fft_buf[1] + xPos; - float* bRe = e_fft[0]; - float* bIm = e_fft[1]; - float* fft_tmp; - - float f0, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12; - int len = PART_LEN >> 1; - - __asm __volatile( - ".set push \n\t" - ".set noreorder \n\t" - "addiu %[fft_tmp], %[fft], 0 \n\t" - "1: \n\t" - "lwc1 %[f0], 0(%[aRe]) \n\t" - "lwc1 %[f1], 0(%[bRe]) \n\t" - "lwc1 %[f2], 0(%[bIm]) \n\t" - "lwc1 %[f4], 4(%[aRe]) \n\t" - "lwc1 %[f5], 4(%[bRe]) \n\t" - "lwc1 %[f6], 4(%[bIm]) \n\t" - "addiu %[aRe], %[aRe], 8 \n\t" - "addiu %[bRe], %[bRe], 8 \n\t" - "mul.s %[f8], %[f0], %[f1] \n\t" - "mul.s %[f0], %[f0], %[f2] \n\t" - "lwc1 %[f3], 0(%[aIm]) \n\t" - "mul.s %[f9], %[f4], %[f5] \n\t" - "lwc1 %[f7], 4(%[aIm]) \n\t" - "mul.s %[f4], %[f4], %[f6] \n\t" -#if !defined(MIPS32_R2_LE) - "mul.s %[f10], %[f3], %[f2] \n\t" - "mul.s %[f1], %[f3], %[f1] \n\t" - "mul.s %[f11], %[f7], %[f6] \n\t" - "mul.s %[f5], %[f7], %[f5] \n\t" - "addiu %[aIm], %[aIm], 8 \n\t" - "addiu %[bIm], %[bIm], 8 \n\t" - "addiu %[len], %[len], -1 \n\t" - "add.s %[f8], %[f8], %[f10] \n\t" - "sub.s %[f1], %[f0], %[f1] \n\t" - "add.s %[f9], %[f9], %[f11] \n\t" - "sub.s %[f5], %[f4], %[f5] \n\t" -#else // #if !defined(MIPS32_R2_LE) - "addiu %[aIm], %[aIm], 8 \n\t" - "addiu %[bIm], %[bIm], 8 \n\t" - "addiu %[len], %[len], -1 \n\t" - "madd.s %[f8], %[f8], %[f3], %[f2] \n\t" - "nmsub.s %[f1], %[f0], %[f3], %[f1] \n\t" - "madd.s %[f9], %[f9], %[f7], %[f6] \n\t" - "nmsub.s %[f5], %[f4], %[f7], %[f5] \n\t" -#endif // #if !defined(MIPS32_R2_LE) - "swc1 %[f8], 0(%[fft_tmp]) \n\t" - "swc1 %[f1], 4(%[fft_tmp]) \n\t" - "swc1 %[f9], 8(%[fft_tmp]) \n\t" - "swc1 %[f5], 12(%[fft_tmp]) \n\t" - "bgtz %[len], 1b \n\t" - " addiu %[fft_tmp], %[fft_tmp], 16 \n\t" - "lwc1 %[f0], 0(%[aRe]) \n\t" - "lwc1 %[f1], 0(%[bRe]) \n\t" - "lwc1 %[f2], 0(%[bIm]) \n\t" - "lwc1 %[f3], 0(%[aIm]) \n\t" - "mul.s %[f8], %[f0], %[f1] \n\t" -#if !defined(MIPS32_R2_LE) - "mul.s %[f10], %[f3], %[f2] \n\t" - "add.s %[f8], %[f8], %[f10] \n\t" -#else // #if !defined(MIPS32_R2_LE) - "madd.s %[f8], %[f8], %[f3], %[f2] \n\t" -#endif // #if !defined(MIPS32_R2_LE) - "swc1 %[f8], 4(%[fft]) \n\t" - ".set pop \n\t" - : [f0] "=&f"(f0), [f1] "=&f"(f1), [f2] "=&f"(f2), [f3] "=&f"(f3), - [f4] "=&f"(f4), [f5] "=&f"(f5), [f6] "=&f"(f6), [f7] "=&f"(f7), - [f8] "=&f"(f8), [f9] "=&f"(f9), [f10] "=&f"(f10), [f11] "=&f"(f11), - [f12] "=&f"(f12), [aRe] "+r"(aRe), [aIm] "+r"(aIm), [bRe] "+r"(bRe), - [bIm] "+r"(bIm), [fft_tmp] "=&r"(fft_tmp), [len] "+r"(len) - : [fft] "r"(fft) - : "memory"); - - ooura_fft.InverseFft(fft); - memset(fft + PART_LEN, 0, sizeof(float) * PART_LEN); - - // fft scaling - { - float scale = 2.0f / PART_LEN2; - __asm __volatile( - ".set push \n\t" - ".set noreorder \n\t" - "addiu %[fft_tmp], %[fft], 0 \n\t" - "addiu %[len], $zero, 8 \n\t" - "1: \n\t" - "addiu %[len], %[len], -1 \n\t" - "lwc1 %[f0], 0(%[fft_tmp]) \n\t" - "lwc1 %[f1], 4(%[fft_tmp]) \n\t" - "lwc1 %[f2], 8(%[fft_tmp]) \n\t" - "lwc1 %[f3], 12(%[fft_tmp]) \n\t" - "mul.s %[f0], %[f0], %[scale] \n\t" - "mul.s %[f1], %[f1], %[scale] \n\t" - "mul.s %[f2], %[f2], %[scale] \n\t" - "mul.s %[f3], %[f3], %[scale] \n\t" - "lwc1 %[f4], 16(%[fft_tmp]) \n\t" - "lwc1 %[f5], 20(%[fft_tmp]) \n\t" - "lwc1 %[f6], 24(%[fft_tmp]) \n\t" - "lwc1 %[f7], 28(%[fft_tmp]) \n\t" - "mul.s %[f4], %[f4], %[scale] \n\t" - "mul.s %[f5], %[f5], %[scale] \n\t" - "mul.s %[f6], %[f6], %[scale] \n\t" - "mul.s %[f7], %[f7], %[scale] \n\t" - "swc1 %[f0], 0(%[fft_tmp]) \n\t" - "swc1 %[f1], 4(%[fft_tmp]) \n\t" - "swc1 %[f2], 8(%[fft_tmp]) \n\t" - "swc1 %[f3], 12(%[fft_tmp]) \n\t" - "swc1 %[f4], 16(%[fft_tmp]) \n\t" - "swc1 %[f5], 20(%[fft_tmp]) \n\t" - "swc1 %[f6], 24(%[fft_tmp]) \n\t" - "swc1 %[f7], 28(%[fft_tmp]) \n\t" - "bgtz %[len], 1b \n\t" - " addiu %[fft_tmp], %[fft_tmp], 32 \n\t" - ".set pop \n\t" - : [f0] "=&f"(f0), [f1] "=&f"(f1), [f2] "=&f"(f2), [f3] "=&f"(f3), - [f4] "=&f"(f4), [f5] "=&f"(f5), [f6] "=&f"(f6), [f7] "=&f"(f7), - [len] "=&r"(len), [fft_tmp] "=&r"(fft_tmp) - : [scale] "f"(scale), [fft] "r"(fft) - : "memory"); - } - ooura_fft.Fft(fft); - aRe = h_fft_buf[0] + pos; - aIm = h_fft_buf[1] + pos; - __asm __volatile( - ".set push \n\t" - ".set noreorder \n\t" - "addiu %[fft_tmp], %[fft], 0 \n\t" - "addiu %[len], $zero, 31 \n\t" - "lwc1 %[f0], 0(%[aRe]) \n\t" - "lwc1 %[f1], 0(%[fft_tmp]) \n\t" - "lwc1 %[f2], 256(%[aRe]) \n\t" - "lwc1 %[f3], 4(%[fft_tmp]) \n\t" - "lwc1 %[f4], 4(%[aRe]) \n\t" - "lwc1 %[f5], 8(%[fft_tmp]) \n\t" - "lwc1 %[f6], 4(%[aIm]) \n\t" - "lwc1 %[f7], 12(%[fft_tmp]) \n\t" - "add.s %[f0], %[f0], %[f1] \n\t" - "add.s %[f2], %[f2], %[f3] \n\t" - "add.s %[f4], %[f4], %[f5] \n\t" - "add.s %[f6], %[f6], %[f7] \n\t" - "addiu %[fft_tmp], %[fft_tmp], 16 \n\t" - "swc1 %[f0], 0(%[aRe]) \n\t" - "swc1 %[f2], 256(%[aRe]) \n\t" - "swc1 %[f4], 4(%[aRe]) \n\t" - "addiu %[aRe], %[aRe], 8 \n\t" - "swc1 %[f6], 4(%[aIm]) \n\t" - "addiu %[aIm], %[aIm], 8 \n\t" - "1: \n\t" - "lwc1 %[f0], 0(%[aRe]) \n\t" - "lwc1 %[f1], 0(%[fft_tmp]) \n\t" - "lwc1 %[f2], 0(%[aIm]) \n\t" - "lwc1 %[f3], 4(%[fft_tmp]) \n\t" - "lwc1 %[f4], 4(%[aRe]) \n\t" - "lwc1 %[f5], 8(%[fft_tmp]) \n\t" - "lwc1 %[f6], 4(%[aIm]) \n\t" - "lwc1 %[f7], 12(%[fft_tmp]) \n\t" - "add.s %[f0], %[f0], %[f1] \n\t" - "add.s %[f2], %[f2], %[f3] \n\t" - "add.s %[f4], %[f4], %[f5] \n\t" - "add.s %[f6], %[f6], %[f7] \n\t" - "addiu %[len], %[len], -1 \n\t" - "addiu %[fft_tmp], %[fft_tmp], 16 \n\t" - "swc1 %[f0], 0(%[aRe]) \n\t" - "swc1 %[f2], 0(%[aIm]) \n\t" - "swc1 %[f4], 4(%[aRe]) \n\t" - "addiu %[aRe], %[aRe], 8 \n\t" - "swc1 %[f6], 4(%[aIm]) \n\t" - "bgtz %[len], 1b \n\t" - " addiu %[aIm], %[aIm], 8 \n\t" - ".set pop \n\t" - : [f0] "=&f"(f0), [f1] "=&f"(f1), [f2] "=&f"(f2), [f3] "=&f"(f3), - [f4] "=&f"(f4), [f5] "=&f"(f5), [f6] "=&f"(f6), [f7] "=&f"(f7), - [len] "=&r"(len), [fft_tmp] "=&r"(fft_tmp), [aRe] "+r"(aRe), - [aIm] "+r"(aIm) - : [fft] "r"(fft) - : "memory"); - } -} - -void WebRtcAec_Overdrive_mips(float overdrive_scaling, - float hNlFb, - float hNl[PART_LEN1]) { - const float one = 1.0; - float* p_hNl; - const float* p_WebRtcAec_wC; - float temp1, temp2, temp3, temp4; - - p_hNl = &hNl[0]; - p_WebRtcAec_wC = &WebRtcAec_weightCurve[0]; - - for (int i = 0; i < PART_LEN1; ++i) { - // Weight subbands - __asm __volatile( - ".set push \n\t" - ".set noreorder \n\t" - "lwc1 %[temp1], 0(%[p_hNl]) \n\t" - "lwc1 %[temp2], 0(%[p_wC]) \n\t" - "c.lt.s %[hNlFb], %[temp1] \n\t" - "bc1f 1f \n\t" - " mul.s %[temp3], %[temp2], %[hNlFb] \n\t" - "sub.s %[temp4], %[one], %[temp2] \n\t" -#if !defined(MIPS32_R2_LE) - "mul.s %[temp1], %[temp1], %[temp4] \n\t" - "add.s %[temp1], %[temp3], %[temp1] \n\t" -#else // #if !defined(MIPS32_R2_LE) - "madd.s %[temp1], %[temp3], %[temp1], %[temp4] \n\t" -#endif // #if !defined(MIPS32_R2_LE) - "swc1 %[temp1], 0(%[p_hNl]) \n\t" - "1: \n\t" - "addiu %[p_wC], %[p_wC], 4 \n\t" - ".set pop \n\t" - : [temp1] "=&f"(temp1), [temp2] "=&f"(temp2), [temp3] "=&f"(temp3), - [temp4] "=&f"(temp4), [p_wC] "+r"(p_WebRtcAec_wC) - : [hNlFb] "f"(hNlFb), [one] "f"(one), [p_hNl] "r"(p_hNl) - : "memory"); - - hNl[i] = powf(hNl[i], overdrive_scaling * WebRtcAec_overDriveCurve[i]); - } -} - -void WebRtcAec_Suppress_mips(const float hNl[PART_LEN1], - float efw[2][PART_LEN1]) { - const float* p_hNl; - float* p_efw0; - float* p_efw1; - float temp1, temp2, temp3, temp4; - - p_hNl = &hNl[0]; - p_efw0 = &efw[0][0]; - p_efw1 = &efw[1][0]; - - for (int i = 0; i < PART_LEN1; ++i) { - __asm __volatile( - "lwc1 %[temp1], 0(%[p_hNl]) \n\t" - "lwc1 %[temp3], 0(%[p_efw1]) \n\t" - "lwc1 %[temp2], 0(%[p_efw0]) \n\t" - "addiu %[p_hNl], %[p_hNl], 4 \n\t" - "mul.s %[temp3], %[temp3], %[temp1] \n\t" - "mul.s %[temp2], %[temp2], %[temp1] \n\t" - "addiu %[p_efw0], %[p_efw0], 4 \n\t" - "addiu %[p_efw1], %[p_efw1], 4 \n\t" - "neg.s %[temp4], %[temp3] \n\t" - "swc1 %[temp2], -4(%[p_efw0]) \n\t" - "swc1 %[temp4], -4(%[p_efw1]) \n\t" - : [temp1] "=&f"(temp1), [temp2] "=&f"(temp2), [temp3] "=&f"(temp3), - [temp4] "=&f"(temp4), [p_efw0] "+r"(p_efw0), [p_efw1] "+r"(p_efw1), - [p_hNl] "+r"(p_hNl) - : - : "memory"); - } -} - -void WebRtcAec_ScaleErrorSignal_mips(float mu, - float error_threshold, - float x_pow[PART_LEN1], - float ef[2][PART_LEN1]) { - int len = (PART_LEN1); - float* ef0 = ef[0]; - float* ef1 = ef[1]; - float fac1 = 1e-10f; - float err_th2 = error_threshold * error_threshold; - float f0, f1, f2; -#if !defined(MIPS32_R2_LE) - float f3; -#endif - - __asm __volatile( - ".set push \n\t" - ".set noreorder \n\t" - "1: \n\t" - "lwc1 %[f0], 0(%[x_pow]) \n\t" - "lwc1 %[f1], 0(%[ef0]) \n\t" - "lwc1 %[f2], 0(%[ef1]) \n\t" - "add.s %[f0], %[f0], %[fac1] \n\t" - "div.s %[f1], %[f1], %[f0] \n\t" - "div.s %[f2], %[f2], %[f0] \n\t" - "mul.s %[f0], %[f1], %[f1] \n\t" -#if defined(MIPS32_R2_LE) - "madd.s %[f0], %[f0], %[f2], %[f2] \n\t" -#else - "mul.s %[f3], %[f2], %[f2] \n\t" - "add.s %[f0], %[f0], %[f3] \n\t" -#endif - "c.le.s %[f0], %[err_th2] \n\t" - "nop \n\t" - "bc1t 2f \n\t" - " nop \n\t" - "sqrt.s %[f0], %[f0] \n\t" - "add.s %[f0], %[f0], %[fac1] \n\t" - "div.s %[f0], %[err_th], %[f0] \n\t" - "mul.s %[f1], %[f1], %[f0] \n\t" - "mul.s %[f2], %[f2], %[f0] \n\t" - "2: \n\t" - "mul.s %[f1], %[f1], %[mu] \n\t" - "mul.s %[f2], %[f2], %[mu] \n\t" - "swc1 %[f1], 0(%[ef0]) \n\t" - "swc1 %[f2], 0(%[ef1]) \n\t" - "addiu %[len], %[len], -1 \n\t" - "addiu %[x_pow], %[x_pow], 4 \n\t" - "addiu %[ef0], %[ef0], 4 \n\t" - "bgtz %[len], 1b \n\t" - " addiu %[ef1], %[ef1], 4 \n\t" - ".set pop \n\t" - : [f0] "=&f"(f0), [f1] "=&f"(f1), [f2] "=&f"(f2), -#if !defined(MIPS32_R2_LE) - [f3] "=&f"(f3), -#endif - [x_pow] "+r"(x_pow), [ef0] "+r"(ef0), [ef1] "+r"(ef1), [len] "+r"(len) - : [fac1] "f"(fac1), [err_th2] "f"(err_th2), [mu] "f"(mu), - [err_th] "f"(error_threshold) - : "memory"); -} - -void WebRtcAec_InitAec_mips(void) { - WebRtcAec_FilterFar = WebRtcAec_FilterFar_mips; - WebRtcAec_FilterAdaptation = WebRtcAec_FilterAdaptation_mips; - WebRtcAec_ScaleErrorSignal = WebRtcAec_ScaleErrorSignal_mips; - WebRtcAec_Overdrive = WebRtcAec_Overdrive_mips; - WebRtcAec_Suppress = WebRtcAec_Suppress_mips; -} -} // namespace webrtc diff --git a/modules/audio_processing/aec/aec_core_neon.cc b/modules/audio_processing/aec/aec_core_neon.cc deleted file mode 100644 index 072bd17dfe..0000000000 --- a/modules/audio_processing/aec/aec_core_neon.cc +++ /dev/null @@ -1,736 +0,0 @@ -/* - * Copyright (c) 2014 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. - */ - -/* - * The core AEC algorithm, neon version of speed-critical functions. - * - * Based on aec_core_sse2.c. - */ - -#include -#include -#include // memset - -extern "C" { -#include "common_audio/signal_processing/include/signal_processing_library.h" -} -#include "modules/audio_processing/aec/aec_common.h" -#include "modules/audio_processing/aec/aec_core_optimized_methods.h" -#include "modules/audio_processing/utility/ooura_fft.h" - -namespace webrtc { - -enum { kShiftExponentIntoTopMantissa = 8 }; -enum { kFloatExponentShift = 23 }; - -__inline static float MulRe(float aRe, float aIm, float bRe, float bIm) { - return aRe * bRe - aIm * bIm; -} - -__inline static float MulIm(float aRe, float aIm, float bRe, float bIm) { - return aRe * bIm + aIm * bRe; -} - -static void FilterFarNEON( - int num_partitions, - int x_fft_buf_block_pos, - float x_fft_buf[2][kExtendedNumPartitions * PART_LEN1], - float h_fft_buf[2][kExtendedNumPartitions * PART_LEN1], - float y_fft[2][PART_LEN1]) { - int i; - for (i = 0; i < num_partitions; i++) { - int j; - int xPos = (i + x_fft_buf_block_pos) * PART_LEN1; - int pos = i * PART_LEN1; - // Check for wrap - if (i + x_fft_buf_block_pos >= num_partitions) { - xPos -= num_partitions * PART_LEN1; - } - - // vectorized code (four at once) - for (j = 0; j + 3 < PART_LEN1; j += 4) { - const float32x4_t x_fft_buf_re = vld1q_f32(&x_fft_buf[0][xPos + j]); - const float32x4_t x_fft_buf_im = vld1q_f32(&x_fft_buf[1][xPos + j]); - const float32x4_t h_fft_buf_re = vld1q_f32(&h_fft_buf[0][pos + j]); - const float32x4_t h_fft_buf_im = vld1q_f32(&h_fft_buf[1][pos + j]); - const float32x4_t y_fft_re = vld1q_f32(&y_fft[0][j]); - const float32x4_t y_fft_im = vld1q_f32(&y_fft[1][j]); - const float32x4_t a = vmulq_f32(x_fft_buf_re, h_fft_buf_re); - const float32x4_t e = vmlsq_f32(a, x_fft_buf_im, h_fft_buf_im); - const float32x4_t c = vmulq_f32(x_fft_buf_re, h_fft_buf_im); - const float32x4_t f = vmlaq_f32(c, x_fft_buf_im, h_fft_buf_re); - const float32x4_t g = vaddq_f32(y_fft_re, e); - const float32x4_t h = vaddq_f32(y_fft_im, f); - vst1q_f32(&y_fft[0][j], g); - vst1q_f32(&y_fft[1][j], h); - } - // scalar code for the remaining items. - for (; j < PART_LEN1; j++) { - y_fft[0][j] += MulRe(x_fft_buf[0][xPos + j], x_fft_buf[1][xPos + j], - h_fft_buf[0][pos + j], h_fft_buf[1][pos + j]); - y_fft[1][j] += MulIm(x_fft_buf[0][xPos + j], x_fft_buf[1][xPos + j], - h_fft_buf[0][pos + j], h_fft_buf[1][pos + j]); - } - } -} - -// ARM64's arm_neon.h has already defined vdivq_f32 vsqrtq_f32. -#if !defined(WEBRTC_ARCH_ARM64) -static float32x4_t vdivq_f32(float32x4_t a, float32x4_t b) { - int i; - float32x4_t x = vrecpeq_f32(b); - // from arm documentation - // The Newton-Raphson iteration: - // x[n+1] = x[n] * (2 - d * x[n]) - // converges to (1/d) if x0 is the result of VRECPE applied to d. - // - // Note: The precision did not improve after 2 iterations. - for (i = 0; i < 2; i++) { - x = vmulq_f32(vrecpsq_f32(b, x), x); - } - // a/b = a*(1/b) - return vmulq_f32(a, x); -} - -static float32x4_t vsqrtq_f32(float32x4_t s) { - int i; - float32x4_t x = vrsqrteq_f32(s); - - // Code to handle sqrt(0). - // If the input to sqrtf() is zero, a zero will be returned. - // If the input to vrsqrteq_f32() is zero, positive infinity is returned. - const uint32x4_t vec_p_inf = vdupq_n_u32(0x7F800000); - // check for divide by zero - const uint32x4_t div_by_zero = vceqq_u32(vec_p_inf, vreinterpretq_u32_f32(x)); - // zero out the positive infinity results - x = vreinterpretq_f32_u32( - vandq_u32(vmvnq_u32(div_by_zero), vreinterpretq_u32_f32(x))); - // from arm documentation - // The Newton-Raphson iteration: - // x[n+1] = x[n] * (3 - d * (x[n] * x[n])) / 2) - // converges to (1/√d) if x0 is the result of VRSQRTE applied to d. - // - // Note: The precision did not improve after 2 iterations. - for (i = 0; i < 2; i++) { - x = vmulq_f32(vrsqrtsq_f32(vmulq_f32(x, x), s), x); - } - // sqrt(s) = s * 1/sqrt(s) - return vmulq_f32(s, x); -} -#endif // WEBRTC_ARCH_ARM64 - -static void ScaleErrorSignalNEON(float mu, - float error_threshold, - float x_pow[PART_LEN1], - float ef[2][PART_LEN1]) { - const float32x4_t k1e_10f = vdupq_n_f32(1e-10f); - const float32x4_t kMu = vmovq_n_f32(mu); - const float32x4_t kThresh = vmovq_n_f32(error_threshold); - int i; - // vectorized code (four at once) - for (i = 0; i + 3 < PART_LEN1; i += 4) { - const float32x4_t x_pow_local = vld1q_f32(&x_pow[i]); - const float32x4_t ef_re_base = vld1q_f32(&ef[0][i]); - const float32x4_t ef_im_base = vld1q_f32(&ef[1][i]); - const float32x4_t xPowPlus = vaddq_f32(x_pow_local, k1e_10f); - float32x4_t ef_re = vdivq_f32(ef_re_base, xPowPlus); - float32x4_t ef_im = vdivq_f32(ef_im_base, xPowPlus); - const float32x4_t ef_re2 = vmulq_f32(ef_re, ef_re); - const float32x4_t ef_sum2 = vmlaq_f32(ef_re2, ef_im, ef_im); - const float32x4_t absEf = vsqrtq_f32(ef_sum2); - const uint32x4_t bigger = vcgtq_f32(absEf, kThresh); - const float32x4_t absEfPlus = vaddq_f32(absEf, k1e_10f); - const float32x4_t absEfInv = vdivq_f32(kThresh, absEfPlus); - uint32x4_t ef_re_if = vreinterpretq_u32_f32(vmulq_f32(ef_re, absEfInv)); - uint32x4_t ef_im_if = vreinterpretq_u32_f32(vmulq_f32(ef_im, absEfInv)); - uint32x4_t ef_re_u32 = - vandq_u32(vmvnq_u32(bigger), vreinterpretq_u32_f32(ef_re)); - uint32x4_t ef_im_u32 = - vandq_u32(vmvnq_u32(bigger), vreinterpretq_u32_f32(ef_im)); - ef_re_if = vandq_u32(bigger, ef_re_if); - ef_im_if = vandq_u32(bigger, ef_im_if); - ef_re_u32 = vorrq_u32(ef_re_u32, ef_re_if); - ef_im_u32 = vorrq_u32(ef_im_u32, ef_im_if); - ef_re = vmulq_f32(vreinterpretq_f32_u32(ef_re_u32), kMu); - ef_im = vmulq_f32(vreinterpretq_f32_u32(ef_im_u32), kMu); - vst1q_f32(&ef[0][i], ef_re); - vst1q_f32(&ef[1][i], ef_im); - } - // scalar code for the remaining items. - for (; i < PART_LEN1; i++) { - float abs_ef; - ef[0][i] /= (x_pow[i] + 1e-10f); - ef[1][i] /= (x_pow[i] + 1e-10f); - abs_ef = sqrtf(ef[0][i] * ef[0][i] + ef[1][i] * ef[1][i]); - - if (abs_ef > error_threshold) { - abs_ef = error_threshold / (abs_ef + 1e-10f); - ef[0][i] *= abs_ef; - ef[1][i] *= abs_ef; - } - - // Stepsize factor - ef[0][i] *= mu; - ef[1][i] *= mu; - } -} - -static void FilterAdaptationNEON( - const OouraFft& ooura_fft, - int num_partitions, - int x_fft_buf_block_pos, - float x_fft_buf[2][kExtendedNumPartitions * PART_LEN1], - float e_fft[2][PART_LEN1], - float h_fft_buf[2][kExtendedNumPartitions * PART_LEN1]) { - float fft[PART_LEN2]; - int i; - for (i = 0; i < num_partitions; i++) { - int xPos = (i + x_fft_buf_block_pos) * PART_LEN1; - int pos = i * PART_LEN1; - int j; - // Check for wrap - if (i + x_fft_buf_block_pos >= num_partitions) { - xPos -= num_partitions * PART_LEN1; - } - - // Process the whole array... - for (j = 0; j < PART_LEN; j += 4) { - // Load x_fft_buf and e_fft. - const float32x4_t x_fft_buf_re = vld1q_f32(&x_fft_buf[0][xPos + j]); - const float32x4_t x_fft_buf_im = vld1q_f32(&x_fft_buf[1][xPos + j]); - const float32x4_t e_fft_re = vld1q_f32(&e_fft[0][j]); - const float32x4_t e_fft_im = vld1q_f32(&e_fft[1][j]); - // Calculate the product of conjugate(x_fft_buf) by e_fft. - // re(conjugate(a) * b) = aRe * bRe + aIm * bIm - // im(conjugate(a) * b)= aRe * bIm - aIm * bRe - const float32x4_t a = vmulq_f32(x_fft_buf_re, e_fft_re); - const float32x4_t e = vmlaq_f32(a, x_fft_buf_im, e_fft_im); - const float32x4_t c = vmulq_f32(x_fft_buf_re, e_fft_im); - const float32x4_t f = vmlsq_f32(c, x_fft_buf_im, e_fft_re); - // Interleave real and imaginary parts. - const float32x4x2_t g_n_h = vzipq_f32(e, f); - // Store - vst1q_f32(&fft[2 * j + 0], g_n_h.val[0]); - vst1q_f32(&fft[2 * j + 4], g_n_h.val[1]); - } - // ... and fixup the first imaginary entry. - fft[1] = - MulRe(x_fft_buf[0][xPos + PART_LEN], -x_fft_buf[1][xPos + PART_LEN], - e_fft[0][PART_LEN], e_fft[1][PART_LEN]); - - ooura_fft.InverseFft(fft); - memset(fft + PART_LEN, 0, sizeof(float) * PART_LEN); - - // fft scaling - { - const float scale = 2.0f / PART_LEN2; - const float32x4_t scale_ps = vmovq_n_f32(scale); - for (j = 0; j < PART_LEN; j += 4) { - const float32x4_t fft_ps = vld1q_f32(&fft[j]); - const float32x4_t fft_scale = vmulq_f32(fft_ps, scale_ps); - vst1q_f32(&fft[j], fft_scale); - } - } - ooura_fft.Fft(fft); - - { - const float wt1 = h_fft_buf[1][pos]; - h_fft_buf[0][pos + PART_LEN] += fft[1]; - for (j = 0; j < PART_LEN; j += 4) { - float32x4_t wtBuf_re = vld1q_f32(&h_fft_buf[0][pos + j]); - float32x4_t wtBuf_im = vld1q_f32(&h_fft_buf[1][pos + j]); - const float32x4_t fft0 = vld1q_f32(&fft[2 * j + 0]); - const float32x4_t fft4 = vld1q_f32(&fft[2 * j + 4]); - const float32x4x2_t fft_re_im = vuzpq_f32(fft0, fft4); - wtBuf_re = vaddq_f32(wtBuf_re, fft_re_im.val[0]); - wtBuf_im = vaddq_f32(wtBuf_im, fft_re_im.val[1]); - - vst1q_f32(&h_fft_buf[0][pos + j], wtBuf_re); - vst1q_f32(&h_fft_buf[1][pos + j], wtBuf_im); - } - h_fft_buf[1][pos] = wt1; - } - } -} - -static float32x4_t vpowq_f32(float32x4_t a, float32x4_t b) { - // a^b = exp2(b * log2(a)) - // exp2(x) and log2(x) are calculated using polynomial approximations. - float32x4_t log2_a, b_log2_a, a_exp_b; - - // Calculate log2(x), x = a. - { - // To calculate log2(x), we decompose x like this: - // x = y * 2^n - // n is an integer - // y is in the [1.0, 2.0) range - // - // log2(x) = log2(y) + n - // n can be evaluated by playing with float representation. - // log2(y) in a small range can be approximated, this code uses an order - // five polynomial approximation. The coefficients have been - // estimated with the Remez algorithm and the resulting - // polynomial has a maximum relative error of 0.00086%. - - // Compute n. - // This is done by masking the exponent, shifting it into the top bit of - // the mantissa, putting eight into the biased exponent (to shift/ - // compensate the fact that the exponent has been shifted in the top/ - // fractional part and finally getting rid of the implicit leading one - // from the mantissa by substracting it out. - const uint32x4_t vec_float_exponent_mask = vdupq_n_u32(0x7F800000); - const uint32x4_t vec_eight_biased_exponent = vdupq_n_u32(0x43800000); - const uint32x4_t vec_implicit_leading_one = vdupq_n_u32(0x43BF8000); - const uint32x4_t two_n = - vandq_u32(vreinterpretq_u32_f32(a), vec_float_exponent_mask); - const uint32x4_t n_1 = vshrq_n_u32(two_n, kShiftExponentIntoTopMantissa); - const uint32x4_t n_0 = vorrq_u32(n_1, vec_eight_biased_exponent); - const float32x4_t n = - vsubq_f32(vreinterpretq_f32_u32(n_0), - vreinterpretq_f32_u32(vec_implicit_leading_one)); - // Compute y. - const uint32x4_t vec_mantissa_mask = vdupq_n_u32(0x007FFFFF); - const uint32x4_t vec_zero_biased_exponent_is_one = vdupq_n_u32(0x3F800000); - const uint32x4_t mantissa = - vandq_u32(vreinterpretq_u32_f32(a), vec_mantissa_mask); - const float32x4_t y = vreinterpretq_f32_u32( - vorrq_u32(mantissa, vec_zero_biased_exponent_is_one)); - // Approximate log2(y) ~= (y - 1) * pol5(y). - // pol5(y) = C5 * y^5 + C4 * y^4 + C3 * y^3 + C2 * y^2 + C1 * y + C0 - const float32x4_t C5 = vdupq_n_f32(-3.4436006e-2f); - const float32x4_t C4 = vdupq_n_f32(3.1821337e-1f); - const float32x4_t C3 = vdupq_n_f32(-1.2315303f); - const float32x4_t C2 = vdupq_n_f32(2.5988452f); - const float32x4_t C1 = vdupq_n_f32(-3.3241990f); - const float32x4_t C0 = vdupq_n_f32(3.1157899f); - float32x4_t pol5_y = C5; - pol5_y = vmlaq_f32(C4, y, pol5_y); - pol5_y = vmlaq_f32(C3, y, pol5_y); - pol5_y = vmlaq_f32(C2, y, pol5_y); - pol5_y = vmlaq_f32(C1, y, pol5_y); - pol5_y = vmlaq_f32(C0, y, pol5_y); - const float32x4_t y_minus_one = - vsubq_f32(y, vreinterpretq_f32_u32(vec_zero_biased_exponent_is_one)); - const float32x4_t log2_y = vmulq_f32(y_minus_one, pol5_y); - - // Combine parts. - log2_a = vaddq_f32(n, log2_y); - } - - // b * log2(a) - b_log2_a = vmulq_f32(b, log2_a); - - // Calculate exp2(x), x = b * log2(a). - { - // To calculate 2^x, we decompose x like this: - // x = n + y - // n is an integer, the value of x - 0.5 rounded down, therefore - // y is in the [0.5, 1.5) range - // - // 2^x = 2^n * 2^y - // 2^n can be evaluated by playing with float representation. - // 2^y in a small range can be approximated, this code uses an order two - // polynomial approximation. The coefficients have been estimated - // with the Remez algorithm and the resulting polynomial has a - // maximum relative error of 0.17%. - // To avoid over/underflow, we reduce the range of input to ]-127, 129]. - const float32x4_t max_input = vdupq_n_f32(129.f); - const float32x4_t min_input = vdupq_n_f32(-126.99999f); - const float32x4_t x_min = vminq_f32(b_log2_a, max_input); - const float32x4_t x_max = vmaxq_f32(x_min, min_input); - // Compute n. - const float32x4_t half = vdupq_n_f32(0.5f); - const float32x4_t x_minus_half = vsubq_f32(x_max, half); - const int32x4_t x_minus_half_floor = vcvtq_s32_f32(x_minus_half); - - // Compute 2^n. - const int32x4_t float_exponent_bias = vdupq_n_s32(127); - const int32x4_t two_n_exponent = - vaddq_s32(x_minus_half_floor, float_exponent_bias); - const float32x4_t two_n = - vreinterpretq_f32_s32(vshlq_n_s32(two_n_exponent, kFloatExponentShift)); - // Compute y. - const float32x4_t y = vsubq_f32(x_max, vcvtq_f32_s32(x_minus_half_floor)); - - // Approximate 2^y ~= C2 * y^2 + C1 * y + C0. - const float32x4_t C2 = vdupq_n_f32(3.3718944e-1f); - const float32x4_t C1 = vdupq_n_f32(6.5763628e-1f); - const float32x4_t C0 = vdupq_n_f32(1.0017247f); - float32x4_t exp2_y = C2; - exp2_y = vmlaq_f32(C1, y, exp2_y); - exp2_y = vmlaq_f32(C0, y, exp2_y); - - // Combine parts. - a_exp_b = vmulq_f32(exp2_y, two_n); - } - - return a_exp_b; -} - -static void OverdriveNEON(float overdrive_scaling, - float hNlFb, - float hNl[PART_LEN1]) { - int i; - const float32x4_t vec_hNlFb = vmovq_n_f32(hNlFb); - const float32x4_t vec_one = vdupq_n_f32(1.0f); - const float32x4_t vec_overdrive_scaling = vmovq_n_f32(overdrive_scaling); - - // vectorized code (four at once) - for (i = 0; i + 3 < PART_LEN1; i += 4) { - // Weight subbands - float32x4_t vec_hNl = vld1q_f32(&hNl[i]); - const float32x4_t vec_weightCurve = vld1q_f32(&WebRtcAec_weightCurve[i]); - const uint32x4_t bigger = vcgtq_f32(vec_hNl, vec_hNlFb); - const float32x4_t vec_weightCurve_hNlFb = - vmulq_f32(vec_weightCurve, vec_hNlFb); - const float32x4_t vec_one_weightCurve = vsubq_f32(vec_one, vec_weightCurve); - const float32x4_t vec_one_weightCurve_hNl = - vmulq_f32(vec_one_weightCurve, vec_hNl); - const uint32x4_t vec_if0 = - vandq_u32(vmvnq_u32(bigger), vreinterpretq_u32_f32(vec_hNl)); - const float32x4_t vec_one_weightCurve_add = - vaddq_f32(vec_weightCurve_hNlFb, vec_one_weightCurve_hNl); - const uint32x4_t vec_if1 = - vandq_u32(bigger, vreinterpretq_u32_f32(vec_one_weightCurve_add)); - - vec_hNl = vreinterpretq_f32_u32(vorrq_u32(vec_if0, vec_if1)); - - const float32x4_t vec_overDriveCurve = - vld1q_f32(&WebRtcAec_overDriveCurve[i]); - const float32x4_t vec_overDriveSm_overDriveCurve = - vmulq_f32(vec_overdrive_scaling, vec_overDriveCurve); - vec_hNl = vpowq_f32(vec_hNl, vec_overDriveSm_overDriveCurve); - vst1q_f32(&hNl[i], vec_hNl); - } - - // scalar code for the remaining items. - for (; i < PART_LEN1; i++) { - // Weight subbands - if (hNl[i] > hNlFb) { - hNl[i] = WebRtcAec_weightCurve[i] * hNlFb + - (1 - WebRtcAec_weightCurve[i]) * hNl[i]; - } - - hNl[i] = powf(hNl[i], overdrive_scaling * WebRtcAec_overDriveCurve[i]); - } -} - -static void SuppressNEON(const float hNl[PART_LEN1], float efw[2][PART_LEN1]) { - int i; - const float32x4_t vec_minus_one = vdupq_n_f32(-1.0f); - // vectorized code (four at once) - for (i = 0; i + 3 < PART_LEN1; i += 4) { - float32x4_t vec_hNl = vld1q_f32(&hNl[i]); - float32x4_t vec_efw_re = vld1q_f32(&efw[0][i]); - float32x4_t vec_efw_im = vld1q_f32(&efw[1][i]); - vec_efw_re = vmulq_f32(vec_efw_re, vec_hNl); - vec_efw_im = vmulq_f32(vec_efw_im, vec_hNl); - - // Ooura fft returns incorrect sign on imaginary component. It matters - // here because we are making an additive change with comfort noise. - vec_efw_im = vmulq_f32(vec_efw_im, vec_minus_one); - vst1q_f32(&efw[0][i], vec_efw_re); - vst1q_f32(&efw[1][i], vec_efw_im); - } - - // scalar code for the remaining items. - for (; i < PART_LEN1; i++) { - efw[0][i] *= hNl[i]; - efw[1][i] *= hNl[i]; - - // Ooura fft returns incorrect sign on imaginary component. It matters - // here because we are making an additive change with comfort noise. - efw[1][i] *= -1; - } -} - -static int PartitionDelayNEON( - int num_partitions, - float h_fft_buf[2][kExtendedNumPartitions * PART_LEN1]) { - // Measures the energy in each filter partition and returns the partition with - // highest energy. - // TODO(bjornv): Spread computational cost by computing one partition per - // block? - float wfEnMax = 0; - int i; - int delay = 0; - - for (i = 0; i < num_partitions; i++) { - int j; - int pos = i * PART_LEN1; - float wfEn = 0; - float32x4_t vec_wfEn = vdupq_n_f32(0.0f); - // vectorized code (four at once) - for (j = 0; j + 3 < PART_LEN1; j += 4) { - const float32x4_t vec_wfBuf0 = vld1q_f32(&h_fft_buf[0][pos + j]); - const float32x4_t vec_wfBuf1 = vld1q_f32(&h_fft_buf[1][pos + j]); - vec_wfEn = vmlaq_f32(vec_wfEn, vec_wfBuf0, vec_wfBuf0); - vec_wfEn = vmlaq_f32(vec_wfEn, vec_wfBuf1, vec_wfBuf1); - } - { - float32x2_t vec_total; - // A B C D - vec_total = vpadd_f32(vget_low_f32(vec_wfEn), vget_high_f32(vec_wfEn)); - // A+B C+D - vec_total = vpadd_f32(vec_total, vec_total); - // A+B+C+D A+B+C+D - wfEn = vget_lane_f32(vec_total, 0); - } - - // scalar code for the remaining items. - for (; j < PART_LEN1; j++) { - wfEn += h_fft_buf[0][pos + j] * h_fft_buf[0][pos + j] + - h_fft_buf[1][pos + j] * h_fft_buf[1][pos + j]; - } - - if (wfEn > wfEnMax) { - wfEnMax = wfEn; - delay = i; - } - } - return delay; -} - -// Updates the following smoothed Power Spectral Densities (PSD): -// - sd : near-end -// - se : residual echo -// - sx : far-end -// - sde : cross-PSD of near-end and residual echo -// - sxd : cross-PSD of near-end and far-end -// -// In addition to updating the PSDs, also the filter diverge state is determined -// upon actions are taken. -static void UpdateCoherenceSpectraNEON(int mult, - bool extended_filter_enabled, - float efw[2][PART_LEN1], - float dfw[2][PART_LEN1], - float xfw[2][PART_LEN1], - CoherenceState* coherence_state, - short* filter_divergence_state, - int* extreme_filter_divergence) { - // Power estimate smoothing coefficients. - const float* ptrGCoh = - extended_filter_enabled - ? WebRtcAec_kExtendedSmoothingCoefficients[mult - 1] - : WebRtcAec_kNormalSmoothingCoefficients[mult - 1]; - int i; - float sdSum = 0, seSum = 0; - const float32x4_t vec_15 = vdupq_n_f32(WebRtcAec_kMinFarendPSD); - float32x4_t vec_sdSum = vdupq_n_f32(0.0f); - float32x4_t vec_seSum = vdupq_n_f32(0.0f); - - for (i = 0; i + 3 < PART_LEN1; i += 4) { - const float32x4_t vec_dfw0 = vld1q_f32(&dfw[0][i]); - const float32x4_t vec_dfw1 = vld1q_f32(&dfw[1][i]); - const float32x4_t vec_efw0 = vld1q_f32(&efw[0][i]); - const float32x4_t vec_efw1 = vld1q_f32(&efw[1][i]); - const float32x4_t vec_xfw0 = vld1q_f32(&xfw[0][i]); - const float32x4_t vec_xfw1 = vld1q_f32(&xfw[1][i]); - float32x4_t vec_sd = - vmulq_n_f32(vld1q_f32(&coherence_state->sd[i]), ptrGCoh[0]); - float32x4_t vec_se = - vmulq_n_f32(vld1q_f32(&coherence_state->se[i]), ptrGCoh[0]); - float32x4_t vec_sx = - vmulq_n_f32(vld1q_f32(&coherence_state->sx[i]), ptrGCoh[0]); - float32x4_t vec_dfw_sumsq = vmulq_f32(vec_dfw0, vec_dfw0); - float32x4_t vec_efw_sumsq = vmulq_f32(vec_efw0, vec_efw0); - float32x4_t vec_xfw_sumsq = vmulq_f32(vec_xfw0, vec_xfw0); - - vec_dfw_sumsq = vmlaq_f32(vec_dfw_sumsq, vec_dfw1, vec_dfw1); - vec_efw_sumsq = vmlaq_f32(vec_efw_sumsq, vec_efw1, vec_efw1); - vec_xfw_sumsq = vmlaq_f32(vec_xfw_sumsq, vec_xfw1, vec_xfw1); - vec_xfw_sumsq = vmaxq_f32(vec_xfw_sumsq, vec_15); - vec_sd = vmlaq_n_f32(vec_sd, vec_dfw_sumsq, ptrGCoh[1]); - vec_se = vmlaq_n_f32(vec_se, vec_efw_sumsq, ptrGCoh[1]); - vec_sx = vmlaq_n_f32(vec_sx, vec_xfw_sumsq, ptrGCoh[1]); - - vst1q_f32(&coherence_state->sd[i], vec_sd); - vst1q_f32(&coherence_state->se[i], vec_se); - vst1q_f32(&coherence_state->sx[i], vec_sx); - - { - float32x4x2_t vec_sde = vld2q_f32(&coherence_state->sde[i][0]); - float32x4_t vec_dfwefw0011 = vmulq_f32(vec_dfw0, vec_efw0); - float32x4_t vec_dfwefw0110 = vmulq_f32(vec_dfw0, vec_efw1); - vec_sde.val[0] = vmulq_n_f32(vec_sde.val[0], ptrGCoh[0]); - vec_sde.val[1] = vmulq_n_f32(vec_sde.val[1], ptrGCoh[0]); - vec_dfwefw0011 = vmlaq_f32(vec_dfwefw0011, vec_dfw1, vec_efw1); - vec_dfwefw0110 = vmlsq_f32(vec_dfwefw0110, vec_dfw1, vec_efw0); - vec_sde.val[0] = vmlaq_n_f32(vec_sde.val[0], vec_dfwefw0011, ptrGCoh[1]); - vec_sde.val[1] = vmlaq_n_f32(vec_sde.val[1], vec_dfwefw0110, ptrGCoh[1]); - vst2q_f32(&coherence_state->sde[i][0], vec_sde); - } - - { - float32x4x2_t vec_sxd = vld2q_f32(&coherence_state->sxd[i][0]); - float32x4_t vec_dfwxfw0011 = vmulq_f32(vec_dfw0, vec_xfw0); - float32x4_t vec_dfwxfw0110 = vmulq_f32(vec_dfw0, vec_xfw1); - vec_sxd.val[0] = vmulq_n_f32(vec_sxd.val[0], ptrGCoh[0]); - vec_sxd.val[1] = vmulq_n_f32(vec_sxd.val[1], ptrGCoh[0]); - vec_dfwxfw0011 = vmlaq_f32(vec_dfwxfw0011, vec_dfw1, vec_xfw1); - vec_dfwxfw0110 = vmlsq_f32(vec_dfwxfw0110, vec_dfw1, vec_xfw0); - vec_sxd.val[0] = vmlaq_n_f32(vec_sxd.val[0], vec_dfwxfw0011, ptrGCoh[1]); - vec_sxd.val[1] = vmlaq_n_f32(vec_sxd.val[1], vec_dfwxfw0110, ptrGCoh[1]); - vst2q_f32(&coherence_state->sxd[i][0], vec_sxd); - } - - vec_sdSum = vaddq_f32(vec_sdSum, vec_sd); - vec_seSum = vaddq_f32(vec_seSum, vec_se); - } - { - float32x2_t vec_sdSum_total; - float32x2_t vec_seSum_total; - // A B C D - vec_sdSum_total = - vpadd_f32(vget_low_f32(vec_sdSum), vget_high_f32(vec_sdSum)); - vec_seSum_total = - vpadd_f32(vget_low_f32(vec_seSum), vget_high_f32(vec_seSum)); - // A+B C+D - vec_sdSum_total = vpadd_f32(vec_sdSum_total, vec_sdSum_total); - vec_seSum_total = vpadd_f32(vec_seSum_total, vec_seSum_total); - // A+B+C+D A+B+C+D - sdSum = vget_lane_f32(vec_sdSum_total, 0); - seSum = vget_lane_f32(vec_seSum_total, 0); - } - - // scalar code for the remaining items. - for (; i < PART_LEN1; i++) { - coherence_state->sd[i] = - ptrGCoh[0] * coherence_state->sd[i] + - ptrGCoh[1] * (dfw[0][i] * dfw[0][i] + dfw[1][i] * dfw[1][i]); - coherence_state->se[i] = - ptrGCoh[0] * coherence_state->se[i] + - ptrGCoh[1] * (efw[0][i] * efw[0][i] + efw[1][i] * efw[1][i]); - // We threshold here to protect against the ill-effects of a zero farend. - // The threshold is not arbitrarily chosen, but balances protection and - // adverse interaction with the algorithm's tuning. - // TODO(bjornv): investigate further why this is so sensitive. - coherence_state->sx[i] = - ptrGCoh[0] * coherence_state->sx[i] + - ptrGCoh[1] * - WEBRTC_SPL_MAX(xfw[0][i] * xfw[0][i] + xfw[1][i] * xfw[1][i], - WebRtcAec_kMinFarendPSD); - - coherence_state->sde[i][0] = - ptrGCoh[0] * coherence_state->sde[i][0] + - ptrGCoh[1] * (dfw[0][i] * efw[0][i] + dfw[1][i] * efw[1][i]); - coherence_state->sde[i][1] = - ptrGCoh[0] * coherence_state->sde[i][1] + - ptrGCoh[1] * (dfw[0][i] * efw[1][i] - dfw[1][i] * efw[0][i]); - - coherence_state->sxd[i][0] = - ptrGCoh[0] * coherence_state->sxd[i][0] + - ptrGCoh[1] * (dfw[0][i] * xfw[0][i] + dfw[1][i] * xfw[1][i]); - coherence_state->sxd[i][1] = - ptrGCoh[0] * coherence_state->sxd[i][1] + - ptrGCoh[1] * (dfw[0][i] * xfw[1][i] - dfw[1][i] * xfw[0][i]); - - sdSum += coherence_state->sd[i]; - seSum += coherence_state->se[i]; - } - - // Divergent filter safeguard update. - *filter_divergence_state = - (*filter_divergence_state ? 1.05f : 1.0f) * seSum > sdSum; - - // Signal extreme filter divergence if the error is significantly larger - // than the nearend (13 dB). - *extreme_filter_divergence = (seSum > (19.95f * sdSum)); -} - -// Window time domain data to be used by the fft. -static void WindowDataNEON(float* x_windowed, const float* x) { - int i; - for (i = 0; i < PART_LEN; i += 4) { - const float32x4_t vec_Buf1 = vld1q_f32(&x[i]); - const float32x4_t vec_Buf2 = vld1q_f32(&x[PART_LEN + i]); - const float32x4_t vec_sqrtHanning = vld1q_f32(&WebRtcAec_sqrtHanning[i]); - // A B C D - float32x4_t vec_sqrtHanning_rev = - vld1q_f32(&WebRtcAec_sqrtHanning[PART_LEN - i - 3]); - // B A D C - vec_sqrtHanning_rev = vrev64q_f32(vec_sqrtHanning_rev); - // D C B A - vec_sqrtHanning_rev = vcombine_f32(vget_high_f32(vec_sqrtHanning_rev), - vget_low_f32(vec_sqrtHanning_rev)); - vst1q_f32(&x_windowed[i], vmulq_f32(vec_Buf1, vec_sqrtHanning)); - vst1q_f32(&x_windowed[PART_LEN + i], - vmulq_f32(vec_Buf2, vec_sqrtHanning_rev)); - } -} - -// Puts fft output data into a complex valued array. -static void StoreAsComplexNEON(const float* data, - float data_complex[2][PART_LEN1]) { - int i; - for (i = 0; i < PART_LEN; i += 4) { - const float32x4x2_t vec_data = vld2q_f32(&data[2 * i]); - vst1q_f32(&data_complex[0][i], vec_data.val[0]); - vst1q_f32(&data_complex[1][i], vec_data.val[1]); - } - // fix beginning/end values - data_complex[1][0] = 0; - data_complex[1][PART_LEN] = 0; - data_complex[0][0] = data[0]; - data_complex[0][PART_LEN] = data[1]; -} - -static void ComputeCoherenceNEON(const CoherenceState* coherence_state, - float* cohde, - float* cohxd) { - int i; - - { - const float32x4_t vec_1eminus10 = vdupq_n_f32(1e-10f); - - // Subband coherence - for (i = 0; i + 3 < PART_LEN1; i += 4) { - const float32x4_t vec_sd = vld1q_f32(&coherence_state->sd[i]); - const float32x4_t vec_se = vld1q_f32(&coherence_state->se[i]); - const float32x4_t vec_sx = vld1q_f32(&coherence_state->sx[i]); - const float32x4_t vec_sdse = vmlaq_f32(vec_1eminus10, vec_sd, vec_se); - const float32x4_t vec_sdsx = vmlaq_f32(vec_1eminus10, vec_sd, vec_sx); - float32x4x2_t vec_sde = vld2q_f32(&coherence_state->sde[i][0]); - float32x4x2_t vec_sxd = vld2q_f32(&coherence_state->sxd[i][0]); - float32x4_t vec_cohde = vmulq_f32(vec_sde.val[0], vec_sde.val[0]); - float32x4_t vec_cohxd = vmulq_f32(vec_sxd.val[0], vec_sxd.val[0]); - vec_cohde = vmlaq_f32(vec_cohde, vec_sde.val[1], vec_sde.val[1]); - vec_cohde = vdivq_f32(vec_cohde, vec_sdse); - vec_cohxd = vmlaq_f32(vec_cohxd, vec_sxd.val[1], vec_sxd.val[1]); - vec_cohxd = vdivq_f32(vec_cohxd, vec_sdsx); - - vst1q_f32(&cohde[i], vec_cohde); - vst1q_f32(&cohxd[i], vec_cohxd); - } - } - // scalar code for the remaining items. - for (; i < PART_LEN1; i++) { - cohde[i] = (coherence_state->sde[i][0] * coherence_state->sde[i][0] + - coherence_state->sde[i][1] * coherence_state->sde[i][1]) / - (coherence_state->sd[i] * coherence_state->se[i] + 1e-10f); - cohxd[i] = (coherence_state->sxd[i][0] * coherence_state->sxd[i][0] + - coherence_state->sxd[i][1] * coherence_state->sxd[i][1]) / - (coherence_state->sx[i] * coherence_state->sd[i] + 1e-10f); - } -} - -void WebRtcAec_InitAec_neon(void) { - WebRtcAec_FilterFar = FilterFarNEON; - WebRtcAec_ScaleErrorSignal = ScaleErrorSignalNEON; - WebRtcAec_FilterAdaptation = FilterAdaptationNEON; - WebRtcAec_Overdrive = OverdriveNEON; - WebRtcAec_Suppress = SuppressNEON; - WebRtcAec_ComputeCoherence = ComputeCoherenceNEON; - WebRtcAec_UpdateCoherenceSpectra = UpdateCoherenceSpectraNEON; - WebRtcAec_StoreAsComplex = StoreAsComplexNEON; - WebRtcAec_PartitionDelay = PartitionDelayNEON; - WebRtcAec_WindowData = WindowDataNEON; -} -} // namespace webrtc diff --git a/modules/audio_processing/aec/aec_core_optimized_methods.h b/modules/audio_processing/aec/aec_core_optimized_methods.h deleted file mode 100644 index 03c027dabf..0000000000 --- a/modules/audio_processing/aec/aec_core_optimized_methods.h +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright (c) 2016 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. - */ - -#ifndef MODULES_AUDIO_PROCESSING_AEC_AEC_CORE_OPTIMIZED_METHODS_H_ -#define MODULES_AUDIO_PROCESSING_AEC_AEC_CORE_OPTIMIZED_METHODS_H_ - -#include - -#include "modules/audio_processing/aec/aec_core.h" - -namespace webrtc { - -typedef void (*WebRtcAecFilterFar)( - int num_partitions, - int x_fft_buf_block_pos, - float x_fft_buf[2][kExtendedNumPartitions * PART_LEN1], - float h_fft_buf[2][kExtendedNumPartitions * PART_LEN1], - float y_fft[2][PART_LEN1]); -extern WebRtcAecFilterFar WebRtcAec_FilterFar; -typedef void (*WebRtcAecScaleErrorSignal)(float mu, - float error_threshold, - float x_pow[PART_LEN1], - float ef[2][PART_LEN1]); -extern WebRtcAecScaleErrorSignal WebRtcAec_ScaleErrorSignal; -typedef void (*WebRtcAecFilterAdaptation)( - const OouraFft& ooura_fft, - int num_partitions, - int x_fft_buf_block_pos, - float x_fft_buf[2][kExtendedNumPartitions * PART_LEN1], - float e_fft[2][PART_LEN1], - float h_fft_buf[2][kExtendedNumPartitions * PART_LEN1]); -extern WebRtcAecFilterAdaptation WebRtcAec_FilterAdaptation; - -typedef void (*WebRtcAecOverdrive)(float overdrive_scaling, - const float hNlFb, - float hNl[PART_LEN1]); -extern WebRtcAecOverdrive WebRtcAec_Overdrive; - -typedef void (*WebRtcAecSuppress)(const float hNl[PART_LEN1], - float efw[2][PART_LEN1]); -extern WebRtcAecSuppress WebRtcAec_Suppress; - -typedef void (*WebRtcAecComputeCoherence)(const CoherenceState* coherence_state, - float* cohde, - float* cohxd); -extern WebRtcAecComputeCoherence WebRtcAec_ComputeCoherence; - -typedef void (*WebRtcAecUpdateCoherenceSpectra)(int mult, - bool extended_filter_enabled, - float efw[2][PART_LEN1], - float dfw[2][PART_LEN1], - float xfw[2][PART_LEN1], - CoherenceState* coherence_state, - short* filter_divergence_state, - int* extreme_filter_divergence); -extern WebRtcAecUpdateCoherenceSpectra WebRtcAec_UpdateCoherenceSpectra; - -typedef int (*WebRtcAecPartitionDelay)( - int num_partitions, - float h_fft_buf[2][kExtendedNumPartitions * PART_LEN1]); -extern WebRtcAecPartitionDelay WebRtcAec_PartitionDelay; - -typedef void (*WebRtcAecStoreAsComplex)(const float* data, - float data_complex[2][PART_LEN1]); -extern WebRtcAecStoreAsComplex WebRtcAec_StoreAsComplex; - -typedef void (*WebRtcAecWindowData)(float* x_windowed, const float* x); -extern WebRtcAecWindowData WebRtcAec_WindowData; - -} // namespace webrtc - -#endif // MODULES_AUDIO_PROCESSING_AEC_AEC_CORE_OPTIMIZED_METHODS_H_ diff --git a/modules/audio_processing/aec/aec_core_sse2.cc b/modules/audio_processing/aec/aec_core_sse2.cc deleted file mode 100644 index ede04ddfc3..0000000000 --- a/modules/audio_processing/aec/aec_core_sse2.cc +++ /dev/null @@ -1,749 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * The core AEC algorithm, SSE2 version of speed-critical functions. - */ - -#include -#include -#include // memset - -extern "C" { -#include "common_audio/signal_processing/include/signal_processing_library.h" -} -#include "modules/audio_processing/aec/aec_common.h" -#include "modules/audio_processing/aec/aec_core_optimized_methods.h" -#include "modules/audio_processing/utility/ooura_fft.h" - -namespace webrtc { - -__inline static float MulRe(float aRe, float aIm, float bRe, float bIm) { - return aRe * bRe - aIm * bIm; -} - -__inline static float MulIm(float aRe, float aIm, float bRe, float bIm) { - return aRe * bIm + aIm * bRe; -} - -static void FilterFarSSE2( - int num_partitions, - int x_fft_buf_block_pos, - float x_fft_buf[2][kExtendedNumPartitions * PART_LEN1], - float h_fft_buf[2][kExtendedNumPartitions * PART_LEN1], - float y_fft[2][PART_LEN1]) { - int i; - for (i = 0; i < num_partitions; i++) { - int j; - int xPos = (i + x_fft_buf_block_pos) * PART_LEN1; - int pos = i * PART_LEN1; - // Check for wrap - if (i + x_fft_buf_block_pos >= num_partitions) { - xPos -= num_partitions * (PART_LEN1); - } - - // vectorized code (four at once) - for (j = 0; j + 3 < PART_LEN1; j += 4) { - const __m128 x_fft_buf_re = _mm_loadu_ps(&x_fft_buf[0][xPos + j]); - const __m128 x_fft_buf_im = _mm_loadu_ps(&x_fft_buf[1][xPos + j]); - const __m128 h_fft_buf_re = _mm_loadu_ps(&h_fft_buf[0][pos + j]); - const __m128 h_fft_buf_im = _mm_loadu_ps(&h_fft_buf[1][pos + j]); - const __m128 y_fft_re = _mm_loadu_ps(&y_fft[0][j]); - const __m128 y_fft_im = _mm_loadu_ps(&y_fft[1][j]); - const __m128 a = _mm_mul_ps(x_fft_buf_re, h_fft_buf_re); - const __m128 b = _mm_mul_ps(x_fft_buf_im, h_fft_buf_im); - const __m128 c = _mm_mul_ps(x_fft_buf_re, h_fft_buf_im); - const __m128 d = _mm_mul_ps(x_fft_buf_im, h_fft_buf_re); - const __m128 e = _mm_sub_ps(a, b); - const __m128 f = _mm_add_ps(c, d); - const __m128 g = _mm_add_ps(y_fft_re, e); - const __m128 h = _mm_add_ps(y_fft_im, f); - _mm_storeu_ps(&y_fft[0][j], g); - _mm_storeu_ps(&y_fft[1][j], h); - } - // scalar code for the remaining items. - for (; j < PART_LEN1; j++) { - y_fft[0][j] += MulRe(x_fft_buf[0][xPos + j], x_fft_buf[1][xPos + j], - h_fft_buf[0][pos + j], h_fft_buf[1][pos + j]); - y_fft[1][j] += MulIm(x_fft_buf[0][xPos + j], x_fft_buf[1][xPos + j], - h_fft_buf[0][pos + j], h_fft_buf[1][pos + j]); - } - } -} - -static void ScaleErrorSignalSSE2(float mu, - float error_threshold, - float x_pow[PART_LEN1], - float ef[2][PART_LEN1]) { - const __m128 k1e_10f = _mm_set1_ps(1e-10f); - const __m128 kMu = _mm_set1_ps(mu); - const __m128 kThresh = _mm_set1_ps(error_threshold); - - int i; - // vectorized code (four at once) - for (i = 0; i + 3 < PART_LEN1; i += 4) { - const __m128 x_pow_local = _mm_loadu_ps(&x_pow[i]); - const __m128 ef_re_base = _mm_loadu_ps(&ef[0][i]); - const __m128 ef_im_base = _mm_loadu_ps(&ef[1][i]); - - const __m128 xPowPlus = _mm_add_ps(x_pow_local, k1e_10f); - __m128 ef_re = _mm_div_ps(ef_re_base, xPowPlus); - __m128 ef_im = _mm_div_ps(ef_im_base, xPowPlus); - const __m128 ef_re2 = _mm_mul_ps(ef_re, ef_re); - const __m128 ef_im2 = _mm_mul_ps(ef_im, ef_im); - const __m128 ef_sum2 = _mm_add_ps(ef_re2, ef_im2); - const __m128 absEf = _mm_sqrt_ps(ef_sum2); - const __m128 bigger = _mm_cmpgt_ps(absEf, kThresh); - __m128 absEfPlus = _mm_add_ps(absEf, k1e_10f); - const __m128 absEfInv = _mm_div_ps(kThresh, absEfPlus); - __m128 ef_re_if = _mm_mul_ps(ef_re, absEfInv); - __m128 ef_im_if = _mm_mul_ps(ef_im, absEfInv); - ef_re_if = _mm_and_ps(bigger, ef_re_if); - ef_im_if = _mm_and_ps(bigger, ef_im_if); - ef_re = _mm_andnot_ps(bigger, ef_re); - ef_im = _mm_andnot_ps(bigger, ef_im); - ef_re = _mm_or_ps(ef_re, ef_re_if); - ef_im = _mm_or_ps(ef_im, ef_im_if); - ef_re = _mm_mul_ps(ef_re, kMu); - ef_im = _mm_mul_ps(ef_im, kMu); - - _mm_storeu_ps(&ef[0][i], ef_re); - _mm_storeu_ps(&ef[1][i], ef_im); - } - // scalar code for the remaining items. - { - for (; i < (PART_LEN1); i++) { - float abs_ef; - ef[0][i] /= (x_pow[i] + 1e-10f); - ef[1][i] /= (x_pow[i] + 1e-10f); - abs_ef = sqrtf(ef[0][i] * ef[0][i] + ef[1][i] * ef[1][i]); - - if (abs_ef > error_threshold) { - abs_ef = error_threshold / (abs_ef + 1e-10f); - ef[0][i] *= abs_ef; - ef[1][i] *= abs_ef; - } - - // Stepsize factor - ef[0][i] *= mu; - ef[1][i] *= mu; - } - } -} - -static void FilterAdaptationSSE2( - const OouraFft& ooura_fft, - int num_partitions, - int x_fft_buf_block_pos, - float x_fft_buf[2][kExtendedNumPartitions * PART_LEN1], - float e_fft[2][PART_LEN1], - float h_fft_buf[2][kExtendedNumPartitions * PART_LEN1]) { - float fft[PART_LEN2]; - int i, j; - for (i = 0; i < num_partitions; i++) { - int xPos = (i + x_fft_buf_block_pos) * (PART_LEN1); - int pos = i * PART_LEN1; - // Check for wrap - if (i + x_fft_buf_block_pos >= num_partitions) { - xPos -= num_partitions * PART_LEN1; - } - - // Process the whole array... - for (j = 0; j < PART_LEN; j += 4) { - // Load x_fft_buf and e_fft. - const __m128 x_fft_buf_re = _mm_loadu_ps(&x_fft_buf[0][xPos + j]); - const __m128 x_fft_buf_im = _mm_loadu_ps(&x_fft_buf[1][xPos + j]); - const __m128 e_fft_re = _mm_loadu_ps(&e_fft[0][j]); - const __m128 e_fft_im = _mm_loadu_ps(&e_fft[1][j]); - // Calculate the product of conjugate(x_fft_buf) by e_fft. - // re(conjugate(a) * b) = aRe * bRe + aIm * bIm - // im(conjugate(a) * b)= aRe * bIm - aIm * bRe - const __m128 a = _mm_mul_ps(x_fft_buf_re, e_fft_re); - const __m128 b = _mm_mul_ps(x_fft_buf_im, e_fft_im); - const __m128 c = _mm_mul_ps(x_fft_buf_re, e_fft_im); - const __m128 d = _mm_mul_ps(x_fft_buf_im, e_fft_re); - const __m128 e = _mm_add_ps(a, b); - const __m128 f = _mm_sub_ps(c, d); - // Interleave real and imaginary parts. - const __m128 g = _mm_unpacklo_ps(e, f); - const __m128 h = _mm_unpackhi_ps(e, f); - // Store - _mm_storeu_ps(&fft[2 * j + 0], g); - _mm_storeu_ps(&fft[2 * j + 4], h); - } - // ... and fixup the first imaginary entry. - fft[1] = - MulRe(x_fft_buf[0][xPos + PART_LEN], -x_fft_buf[1][xPos + PART_LEN], - e_fft[0][PART_LEN], e_fft[1][PART_LEN]); - - ooura_fft.InverseFft(fft); - memset(fft + PART_LEN, 0, sizeof(float) * PART_LEN); - - // fft scaling - { - float scale = 2.0f / PART_LEN2; - const __m128 scale_ps = _mm_load_ps1(&scale); - for (j = 0; j < PART_LEN; j += 4) { - const __m128 fft_ps = _mm_loadu_ps(&fft[j]); - const __m128 fft_scale = _mm_mul_ps(fft_ps, scale_ps); - _mm_storeu_ps(&fft[j], fft_scale); - } - } - ooura_fft.Fft(fft); - - { - float wt1 = h_fft_buf[1][pos]; - h_fft_buf[0][pos + PART_LEN] += fft[1]; - for (j = 0; j < PART_LEN; j += 4) { - __m128 wtBuf_re = _mm_loadu_ps(&h_fft_buf[0][pos + j]); - __m128 wtBuf_im = _mm_loadu_ps(&h_fft_buf[1][pos + j]); - const __m128 fft0 = _mm_loadu_ps(&fft[2 * j + 0]); - const __m128 fft4 = _mm_loadu_ps(&fft[2 * j + 4]); - const __m128 fft_re = - _mm_shuffle_ps(fft0, fft4, _MM_SHUFFLE(2, 0, 2, 0)); - const __m128 fft_im = - _mm_shuffle_ps(fft0, fft4, _MM_SHUFFLE(3, 1, 3, 1)); - wtBuf_re = _mm_add_ps(wtBuf_re, fft_re); - wtBuf_im = _mm_add_ps(wtBuf_im, fft_im); - _mm_storeu_ps(&h_fft_buf[0][pos + j], wtBuf_re); - _mm_storeu_ps(&h_fft_buf[1][pos + j], wtBuf_im); - } - h_fft_buf[1][pos] = wt1; - } - } -} - -static __m128 mm_pow_ps(__m128 a, __m128 b) { - // a^b = exp2(b * log2(a)) - // exp2(x) and log2(x) are calculated using polynomial approximations. - __m128 log2_a, b_log2_a, a_exp_b; - - // Calculate log2(x), x = a. - { - // To calculate log2(x), we decompose x like this: - // x = y * 2^n - // n is an integer - // y is in the [1.0, 2.0) range - // - // log2(x) = log2(y) + n - // n can be evaluated by playing with float representation. - // log2(y) in a small range can be approximated, this code uses an order - // five polynomial approximation. The coefficients have been - // estimated with the Remez algorithm and the resulting - // polynomial has a maximum relative error of 0.00086%. - - // Compute n. - // This is done by masking the exponent, shifting it into the top bit of - // the mantissa, putting eight into the biased exponent (to shift/ - // compensate the fact that the exponent has been shifted in the top/ - // fractional part and finally getting rid of the implicit leading one - // from the mantissa by substracting it out. - static const ALIGN16_BEG int float_exponent_mask[4] ALIGN16_END = { - 0x7F800000, 0x7F800000, 0x7F800000, 0x7F800000}; - static const ALIGN16_BEG int eight_biased_exponent[4] ALIGN16_END = { - 0x43800000, 0x43800000, 0x43800000, 0x43800000}; - static const ALIGN16_BEG int implicit_leading_one[4] ALIGN16_END = { - 0x43BF8000, 0x43BF8000, 0x43BF8000, 0x43BF8000}; - static const int shift_exponent_into_top_mantissa = 8; - const __m128 two_n = - _mm_and_ps(a, *(reinterpret_cast(float_exponent_mask))); - const __m128 n_1 = _mm_castsi128_ps(_mm_srli_epi32( - _mm_castps_si128(two_n), shift_exponent_into_top_mantissa)); - const __m128 n_0 = _mm_or_ps( - n_1, *(reinterpret_cast(eight_biased_exponent))); - const __m128 n = _mm_sub_ps( - n_0, *(reinterpret_cast(implicit_leading_one))); - - // Compute y. - static const ALIGN16_BEG int mantissa_mask[4] ALIGN16_END = { - 0x007FFFFF, 0x007FFFFF, 0x007FFFFF, 0x007FFFFF}; - static const ALIGN16_BEG int zero_biased_exponent_is_one[4] ALIGN16_END = { - 0x3F800000, 0x3F800000, 0x3F800000, 0x3F800000}; - const __m128 mantissa = - _mm_and_ps(a, *(reinterpret_cast(mantissa_mask))); - const __m128 y = _mm_or_ps( - mantissa, - *(reinterpret_cast(zero_biased_exponent_is_one))); - - // Approximate log2(y) ~= (y - 1) * pol5(y). - // pol5(y) = C5 * y^5 + C4 * y^4 + C3 * y^3 + C2 * y^2 + C1 * y + C0 - static const ALIGN16_BEG float ALIGN16_END C5[4] = { - -3.4436006e-2f, -3.4436006e-2f, -3.4436006e-2f, -3.4436006e-2f}; - static const ALIGN16_BEG float ALIGN16_END C4[4] = { - 3.1821337e-1f, 3.1821337e-1f, 3.1821337e-1f, 3.1821337e-1f}; - static const ALIGN16_BEG float ALIGN16_END C3[4] = { - -1.2315303f, -1.2315303f, -1.2315303f, -1.2315303f}; - static const ALIGN16_BEG float ALIGN16_END C2[4] = {2.5988452f, 2.5988452f, - 2.5988452f, 2.5988452f}; - static const ALIGN16_BEG float ALIGN16_END C1[4] = { - -3.3241990f, -3.3241990f, -3.3241990f, -3.3241990f}; - static const ALIGN16_BEG float ALIGN16_END C0[4] = {3.1157899f, 3.1157899f, - 3.1157899f, 3.1157899f}; - const __m128 pol5_y_0 = - _mm_mul_ps(y, *(reinterpret_cast(C5))); - const __m128 pol5_y_1 = - _mm_add_ps(pol5_y_0, *(reinterpret_cast(C4))); - const __m128 pol5_y_2 = _mm_mul_ps(pol5_y_1, y); - const __m128 pol5_y_3 = - _mm_add_ps(pol5_y_2, *(reinterpret_cast(C3))); - const __m128 pol5_y_4 = _mm_mul_ps(pol5_y_3, y); - const __m128 pol5_y_5 = - _mm_add_ps(pol5_y_4, *(reinterpret_cast(C2))); - const __m128 pol5_y_6 = _mm_mul_ps(pol5_y_5, y); - const __m128 pol5_y_7 = - _mm_add_ps(pol5_y_6, *(reinterpret_cast(C1))); - const __m128 pol5_y_8 = _mm_mul_ps(pol5_y_7, y); - const __m128 pol5_y = - _mm_add_ps(pol5_y_8, *(reinterpret_cast(C0))); - const __m128 y_minus_one = _mm_sub_ps( - y, *(reinterpret_cast(zero_biased_exponent_is_one))); - const __m128 log2_y = _mm_mul_ps(y_minus_one, pol5_y); - - // Combine parts. - log2_a = _mm_add_ps(n, log2_y); - } - - // b * log2(a) - b_log2_a = _mm_mul_ps(b, log2_a); - - // Calculate exp2(x), x = b * log2(a). - { - // To calculate 2^x, we decompose x like this: - // x = n + y - // n is an integer, the value of x - 0.5 rounded down, therefore - // y is in the [0.5, 1.5) range - // - // 2^x = 2^n * 2^y - // 2^n can be evaluated by playing with float representation. - // 2^y in a small range can be approximated, this code uses an order two - // polynomial approximation. The coefficients have been estimated - // with the Remez algorithm and the resulting polynomial has a - // maximum relative error of 0.17%. - - // To avoid over/underflow, we reduce the range of input to ]-127, 129]. - static const ALIGN16_BEG float max_input[4] ALIGN16_END = {129.f, 129.f, - 129.f, 129.f}; - static const ALIGN16_BEG float min_input[4] ALIGN16_END = { - -126.99999f, -126.99999f, -126.99999f, -126.99999f}; - const __m128 x_min = - _mm_min_ps(b_log2_a, *(reinterpret_cast(max_input))); - const __m128 x_max = - _mm_max_ps(x_min, *(reinterpret_cast(min_input))); - // Compute n. - static const ALIGN16_BEG float half[4] ALIGN16_END = {0.5f, 0.5f, 0.5f, - 0.5f}; - const __m128 x_minus_half = - _mm_sub_ps(x_max, *(reinterpret_cast(half))); - const __m128i x_minus_half_floor = _mm_cvtps_epi32(x_minus_half); - // Compute 2^n. - static const ALIGN16_BEG int float_exponent_bias[4] ALIGN16_END = { - 127, 127, 127, 127}; - static const int float_exponent_shift = 23; - const __m128i two_n_exponent = - _mm_add_epi32(x_minus_half_floor, - *(reinterpret_cast(float_exponent_bias))); - const __m128 two_n = - _mm_castsi128_ps(_mm_slli_epi32(two_n_exponent, float_exponent_shift)); - // Compute y. - const __m128 y = _mm_sub_ps(x_max, _mm_cvtepi32_ps(x_minus_half_floor)); - // Approximate 2^y ~= C2 * y^2 + C1 * y + C0. - static const ALIGN16_BEG float C2[4] ALIGN16_END = { - 3.3718944e-1f, 3.3718944e-1f, 3.3718944e-1f, 3.3718944e-1f}; - static const ALIGN16_BEG float C1[4] ALIGN16_END = { - 6.5763628e-1f, 6.5763628e-1f, 6.5763628e-1f, 6.5763628e-1f}; - static const ALIGN16_BEG float C0[4] ALIGN16_END = {1.0017247f, 1.0017247f, - 1.0017247f, 1.0017247f}; - const __m128 exp2_y_0 = - _mm_mul_ps(y, *(reinterpret_cast(C2))); - const __m128 exp2_y_1 = - _mm_add_ps(exp2_y_0, *(reinterpret_cast(C1))); - const __m128 exp2_y_2 = _mm_mul_ps(exp2_y_1, y); - const __m128 exp2_y = - _mm_add_ps(exp2_y_2, *(reinterpret_cast(C0))); - - // Combine parts. - a_exp_b = _mm_mul_ps(exp2_y, two_n); - } - return a_exp_b; -} - -static void OverdriveSSE2(float overdrive_scaling, - float hNlFb, - float hNl[PART_LEN1]) { - int i; - const __m128 vec_hNlFb = _mm_set1_ps(hNlFb); - const __m128 vec_one = _mm_set1_ps(1.0f); - const __m128 vec_overdrive_scaling = _mm_set1_ps(overdrive_scaling); - // vectorized code (four at once) - for (i = 0; i + 3 < PART_LEN1; i += 4) { - // Weight subbands - __m128 vec_hNl = _mm_loadu_ps(&hNl[i]); - const __m128 vec_weightCurve = _mm_loadu_ps(&WebRtcAec_weightCurve[i]); - const __m128 bigger = _mm_cmpgt_ps(vec_hNl, vec_hNlFb); - const __m128 vec_weightCurve_hNlFb = _mm_mul_ps(vec_weightCurve, vec_hNlFb); - const __m128 vec_one_weightCurve = _mm_sub_ps(vec_one, vec_weightCurve); - const __m128 vec_one_weightCurve_hNl = - _mm_mul_ps(vec_one_weightCurve, vec_hNl); - const __m128 vec_if0 = _mm_andnot_ps(bigger, vec_hNl); - const __m128 vec_if1 = _mm_and_ps( - bigger, _mm_add_ps(vec_weightCurve_hNlFb, vec_one_weightCurve_hNl)); - vec_hNl = _mm_or_ps(vec_if0, vec_if1); - - const __m128 vec_overDriveCurve = - _mm_loadu_ps(&WebRtcAec_overDriveCurve[i]); - const __m128 vec_overDriveSm_overDriveCurve = - _mm_mul_ps(vec_overdrive_scaling, vec_overDriveCurve); - vec_hNl = mm_pow_ps(vec_hNl, vec_overDriveSm_overDriveCurve); - _mm_storeu_ps(&hNl[i], vec_hNl); - } - // scalar code for the remaining items. - for (; i < PART_LEN1; i++) { - // Weight subbands - if (hNl[i] > hNlFb) { - hNl[i] = WebRtcAec_weightCurve[i] * hNlFb + - (1 - WebRtcAec_weightCurve[i]) * hNl[i]; - } - hNl[i] = powf(hNl[i], overdrive_scaling * WebRtcAec_overDriveCurve[i]); - } -} - -static void SuppressSSE2(const float hNl[PART_LEN1], float efw[2][PART_LEN1]) { - int i; - const __m128 vec_minus_one = _mm_set1_ps(-1.0f); - // vectorized code (four at once) - for (i = 0; i + 3 < PART_LEN1; i += 4) { - // Suppress error signal - __m128 vec_hNl = _mm_loadu_ps(&hNl[i]); - __m128 vec_efw_re = _mm_loadu_ps(&efw[0][i]); - __m128 vec_efw_im = _mm_loadu_ps(&efw[1][i]); - vec_efw_re = _mm_mul_ps(vec_efw_re, vec_hNl); - vec_efw_im = _mm_mul_ps(vec_efw_im, vec_hNl); - - // Ooura fft returns incorrect sign on imaginary component. It matters - // here because we are making an additive change with comfort noise. - vec_efw_im = _mm_mul_ps(vec_efw_im, vec_minus_one); - _mm_storeu_ps(&efw[0][i], vec_efw_re); - _mm_storeu_ps(&efw[1][i], vec_efw_im); - } - // scalar code for the remaining items. - for (; i < PART_LEN1; i++) { - // Suppress error signal - efw[0][i] *= hNl[i]; - efw[1][i] *= hNl[i]; - - // Ooura fft returns incorrect sign on imaginary component. It matters - // here because we are making an additive change with comfort noise. - efw[1][i] *= -1; - } -} - -__inline static void _mm_add_ps_4x1(__m128 sum, float* dst) { - // A+B C+D - sum = _mm_add_ps(sum, _mm_shuffle_ps(sum, sum, _MM_SHUFFLE(0, 0, 3, 2))); - // A+B+C+D A+B+C+D - sum = _mm_add_ps(sum, _mm_shuffle_ps(sum, sum, _MM_SHUFFLE(1, 1, 1, 1))); - _mm_store_ss(dst, sum); -} - -static int PartitionDelaySSE2( - int num_partitions, - float h_fft_buf[2][kExtendedNumPartitions * PART_LEN1]) { - // Measures the energy in each filter partition and returns the partition with - // highest energy. - // TODO(bjornv): Spread computational cost by computing one partition per - // block? - float wfEnMax = 0; - int i; - int delay = 0; - - for (i = 0; i < num_partitions; i++) { - int j; - int pos = i * PART_LEN1; - float wfEn = 0; - __m128 vec_wfEn = _mm_set1_ps(0.0f); - // vectorized code (four at once) - for (j = 0; j + 3 < PART_LEN1; j += 4) { - const __m128 vec_wfBuf0 = _mm_loadu_ps(&h_fft_buf[0][pos + j]); - const __m128 vec_wfBuf1 = _mm_loadu_ps(&h_fft_buf[1][pos + j]); - vec_wfEn = _mm_add_ps(vec_wfEn, _mm_mul_ps(vec_wfBuf0, vec_wfBuf0)); - vec_wfEn = _mm_add_ps(vec_wfEn, _mm_mul_ps(vec_wfBuf1, vec_wfBuf1)); - } - _mm_add_ps_4x1(vec_wfEn, &wfEn); - - // scalar code for the remaining items. - for (; j < PART_LEN1; j++) { - wfEn += h_fft_buf[0][pos + j] * h_fft_buf[0][pos + j] + - h_fft_buf[1][pos + j] * h_fft_buf[1][pos + j]; - } - - if (wfEn > wfEnMax) { - wfEnMax = wfEn; - delay = i; - } - } - return delay; -} - -// Updates the following smoothed Power Spectral Densities (PSD): -// - sd : near-end -// - se : residual echo -// - sx : far-end -// - sde : cross-PSD of near-end and residual echo -// - sxd : cross-PSD of near-end and far-end -// -// In addition to updating the PSDs, also the filter diverge state is determined -// upon actions are taken. -static void UpdateCoherenceSpectraSSE2(int mult, - bool extended_filter_enabled, - float efw[2][PART_LEN1], - float dfw[2][PART_LEN1], - float xfw[2][PART_LEN1], - CoherenceState* coherence_state, - short* filter_divergence_state, - int* extreme_filter_divergence) { - // Power estimate smoothing coefficients. - const float* ptrGCoh = - extended_filter_enabled - ? WebRtcAec_kExtendedSmoothingCoefficients[mult - 1] - : WebRtcAec_kNormalSmoothingCoefficients[mult - 1]; - int i; - float sdSum = 0, seSum = 0; - const __m128 vec_15 = _mm_set1_ps(WebRtcAec_kMinFarendPSD); - const __m128 vec_GCoh0 = _mm_set1_ps(ptrGCoh[0]); - const __m128 vec_GCoh1 = _mm_set1_ps(ptrGCoh[1]); - __m128 vec_sdSum = _mm_set1_ps(0.0f); - __m128 vec_seSum = _mm_set1_ps(0.0f); - - for (i = 0; i + 3 < PART_LEN1; i += 4) { - const __m128 vec_dfw0 = _mm_loadu_ps(&dfw[0][i]); - const __m128 vec_dfw1 = _mm_loadu_ps(&dfw[1][i]); - const __m128 vec_efw0 = _mm_loadu_ps(&efw[0][i]); - const __m128 vec_efw1 = _mm_loadu_ps(&efw[1][i]); - const __m128 vec_xfw0 = _mm_loadu_ps(&xfw[0][i]); - const __m128 vec_xfw1 = _mm_loadu_ps(&xfw[1][i]); - __m128 vec_sd = - _mm_mul_ps(_mm_loadu_ps(&coherence_state->sd[i]), vec_GCoh0); - __m128 vec_se = - _mm_mul_ps(_mm_loadu_ps(&coherence_state->se[i]), vec_GCoh0); - __m128 vec_sx = - _mm_mul_ps(_mm_loadu_ps(&coherence_state->sx[i]), vec_GCoh0); - __m128 vec_dfw_sumsq = _mm_mul_ps(vec_dfw0, vec_dfw0); - __m128 vec_efw_sumsq = _mm_mul_ps(vec_efw0, vec_efw0); - __m128 vec_xfw_sumsq = _mm_mul_ps(vec_xfw0, vec_xfw0); - vec_dfw_sumsq = _mm_add_ps(vec_dfw_sumsq, _mm_mul_ps(vec_dfw1, vec_dfw1)); - vec_efw_sumsq = _mm_add_ps(vec_efw_sumsq, _mm_mul_ps(vec_efw1, vec_efw1)); - vec_xfw_sumsq = _mm_add_ps(vec_xfw_sumsq, _mm_mul_ps(vec_xfw1, vec_xfw1)); - vec_xfw_sumsq = _mm_max_ps(vec_xfw_sumsq, vec_15); - vec_sd = _mm_add_ps(vec_sd, _mm_mul_ps(vec_dfw_sumsq, vec_GCoh1)); - vec_se = _mm_add_ps(vec_se, _mm_mul_ps(vec_efw_sumsq, vec_GCoh1)); - vec_sx = _mm_add_ps(vec_sx, _mm_mul_ps(vec_xfw_sumsq, vec_GCoh1)); - _mm_storeu_ps(&coherence_state->sd[i], vec_sd); - _mm_storeu_ps(&coherence_state->se[i], vec_se); - _mm_storeu_ps(&coherence_state->sx[i], vec_sx); - - { - const __m128 vec_3210 = _mm_loadu_ps(&coherence_state->sde[i][0]); - const __m128 vec_7654 = _mm_loadu_ps(&coherence_state->sde[i + 2][0]); - __m128 vec_a = - _mm_shuffle_ps(vec_3210, vec_7654, _MM_SHUFFLE(2, 0, 2, 0)); - __m128 vec_b = - _mm_shuffle_ps(vec_3210, vec_7654, _MM_SHUFFLE(3, 1, 3, 1)); - __m128 vec_dfwefw0011 = _mm_mul_ps(vec_dfw0, vec_efw0); - __m128 vec_dfwefw0110 = _mm_mul_ps(vec_dfw0, vec_efw1); - vec_a = _mm_mul_ps(vec_a, vec_GCoh0); - vec_b = _mm_mul_ps(vec_b, vec_GCoh0); - vec_dfwefw0011 = - _mm_add_ps(vec_dfwefw0011, _mm_mul_ps(vec_dfw1, vec_efw1)); - vec_dfwefw0110 = - _mm_sub_ps(vec_dfwefw0110, _mm_mul_ps(vec_dfw1, vec_efw0)); - vec_a = _mm_add_ps(vec_a, _mm_mul_ps(vec_dfwefw0011, vec_GCoh1)); - vec_b = _mm_add_ps(vec_b, _mm_mul_ps(vec_dfwefw0110, vec_GCoh1)); - _mm_storeu_ps(&coherence_state->sde[i][0], _mm_unpacklo_ps(vec_a, vec_b)); - _mm_storeu_ps(&coherence_state->sde[i + 2][0], - _mm_unpackhi_ps(vec_a, vec_b)); - } - - { - const __m128 vec_3210 = _mm_loadu_ps(&coherence_state->sxd[i][0]); - const __m128 vec_7654 = _mm_loadu_ps(&coherence_state->sxd[i + 2][0]); - __m128 vec_a = - _mm_shuffle_ps(vec_3210, vec_7654, _MM_SHUFFLE(2, 0, 2, 0)); - __m128 vec_b = - _mm_shuffle_ps(vec_3210, vec_7654, _MM_SHUFFLE(3, 1, 3, 1)); - __m128 vec_dfwxfw0011 = _mm_mul_ps(vec_dfw0, vec_xfw0); - __m128 vec_dfwxfw0110 = _mm_mul_ps(vec_dfw0, vec_xfw1); - vec_a = _mm_mul_ps(vec_a, vec_GCoh0); - vec_b = _mm_mul_ps(vec_b, vec_GCoh0); - vec_dfwxfw0011 = - _mm_add_ps(vec_dfwxfw0011, _mm_mul_ps(vec_dfw1, vec_xfw1)); - vec_dfwxfw0110 = - _mm_sub_ps(vec_dfwxfw0110, _mm_mul_ps(vec_dfw1, vec_xfw0)); - vec_a = _mm_add_ps(vec_a, _mm_mul_ps(vec_dfwxfw0011, vec_GCoh1)); - vec_b = _mm_add_ps(vec_b, _mm_mul_ps(vec_dfwxfw0110, vec_GCoh1)); - _mm_storeu_ps(&coherence_state->sxd[i][0], _mm_unpacklo_ps(vec_a, vec_b)); - _mm_storeu_ps(&coherence_state->sxd[i + 2][0], - _mm_unpackhi_ps(vec_a, vec_b)); - } - - vec_sdSum = _mm_add_ps(vec_sdSum, vec_sd); - vec_seSum = _mm_add_ps(vec_seSum, vec_se); - } - - _mm_add_ps_4x1(vec_sdSum, &sdSum); - _mm_add_ps_4x1(vec_seSum, &seSum); - - for (; i < PART_LEN1; i++) { - coherence_state->sd[i] = - ptrGCoh[0] * coherence_state->sd[i] + - ptrGCoh[1] * (dfw[0][i] * dfw[0][i] + dfw[1][i] * dfw[1][i]); - coherence_state->se[i] = - ptrGCoh[0] * coherence_state->se[i] + - ptrGCoh[1] * (efw[0][i] * efw[0][i] + efw[1][i] * efw[1][i]); - // We threshold here to protect against the ill-effects of a zero farend. - // The threshold is not arbitrarily chosen, but balances protection and - // adverse interaction with the algorithm's tuning. - // TODO(bjornv): investigate further why this is so sensitive. - coherence_state->sx[i] = - ptrGCoh[0] * coherence_state->sx[i] + - ptrGCoh[1] * - WEBRTC_SPL_MAX(xfw[0][i] * xfw[0][i] + xfw[1][i] * xfw[1][i], - WebRtcAec_kMinFarendPSD); - - coherence_state->sde[i][0] = - ptrGCoh[0] * coherence_state->sde[i][0] + - ptrGCoh[1] * (dfw[0][i] * efw[0][i] + dfw[1][i] * efw[1][i]); - coherence_state->sde[i][1] = - ptrGCoh[0] * coherence_state->sde[i][1] + - ptrGCoh[1] * (dfw[0][i] * efw[1][i] - dfw[1][i] * efw[0][i]); - - coherence_state->sxd[i][0] = - ptrGCoh[0] * coherence_state->sxd[i][0] + - ptrGCoh[1] * (dfw[0][i] * xfw[0][i] + dfw[1][i] * xfw[1][i]); - coherence_state->sxd[i][1] = - ptrGCoh[0] * coherence_state->sxd[i][1] + - ptrGCoh[1] * (dfw[0][i] * xfw[1][i] - dfw[1][i] * xfw[0][i]); - - sdSum += coherence_state->sd[i]; - seSum += coherence_state->se[i]; - } - - // Divergent filter safeguard update. - *filter_divergence_state = - (*filter_divergence_state ? 1.05f : 1.0f) * seSum > sdSum; - - // Signal extreme filter divergence if the error is significantly larger - // than the nearend (13 dB). - *extreme_filter_divergence = (seSum > (19.95f * sdSum)); -} - -// Window time domain data to be used by the fft. -static void WindowDataSSE2(float* x_windowed, const float* x) { - int i; - for (i = 0; i < PART_LEN; i += 4) { - const __m128 vec_Buf1 = _mm_loadu_ps(&x[i]); - const __m128 vec_Buf2 = _mm_loadu_ps(&x[PART_LEN + i]); - const __m128 vec_sqrtHanning = _mm_load_ps(&WebRtcAec_sqrtHanning[i]); - // A B C D - __m128 vec_sqrtHanning_rev = - _mm_loadu_ps(&WebRtcAec_sqrtHanning[PART_LEN - i - 3]); - // D C B A - vec_sqrtHanning_rev = _mm_shuffle_ps( - vec_sqrtHanning_rev, vec_sqrtHanning_rev, _MM_SHUFFLE(0, 1, 2, 3)); - _mm_storeu_ps(&x_windowed[i], _mm_mul_ps(vec_Buf1, vec_sqrtHanning)); - _mm_storeu_ps(&x_windowed[PART_LEN + i], - _mm_mul_ps(vec_Buf2, vec_sqrtHanning_rev)); - } -} - -// Puts fft output data into a complex valued array. -static void StoreAsComplexSSE2(const float* data, - float data_complex[2][PART_LEN1]) { - int i; - for (i = 0; i < PART_LEN; i += 4) { - const __m128 vec_fft0 = _mm_loadu_ps(&data[2 * i]); - const __m128 vec_fft4 = _mm_loadu_ps(&data[2 * i + 4]); - const __m128 vec_a = - _mm_shuffle_ps(vec_fft0, vec_fft4, _MM_SHUFFLE(2, 0, 2, 0)); - const __m128 vec_b = - _mm_shuffle_ps(vec_fft0, vec_fft4, _MM_SHUFFLE(3, 1, 3, 1)); - _mm_storeu_ps(&data_complex[0][i], vec_a); - _mm_storeu_ps(&data_complex[1][i], vec_b); - } - // fix beginning/end values - data_complex[1][0] = 0; - data_complex[1][PART_LEN] = 0; - data_complex[0][0] = data[0]; - data_complex[0][PART_LEN] = data[1]; -} - -static void ComputeCoherenceSSE2(const CoherenceState* coherence_state, - float* cohde, - float* cohxd) { - int i; - - { - const __m128 vec_1eminus10 = _mm_set1_ps(1e-10f); - - // Subband coherence - for (i = 0; i + 3 < PART_LEN1; i += 4) { - const __m128 vec_sd = _mm_loadu_ps(&coherence_state->sd[i]); - const __m128 vec_se = _mm_loadu_ps(&coherence_state->se[i]); - const __m128 vec_sx = _mm_loadu_ps(&coherence_state->sx[i]); - const __m128 vec_sdse = - _mm_add_ps(vec_1eminus10, _mm_mul_ps(vec_sd, vec_se)); - const __m128 vec_sdsx = - _mm_add_ps(vec_1eminus10, _mm_mul_ps(vec_sd, vec_sx)); - const __m128 vec_sde_3210 = _mm_loadu_ps(&coherence_state->sde[i][0]); - const __m128 vec_sde_7654 = _mm_loadu_ps(&coherence_state->sde[i + 2][0]); - const __m128 vec_sxd_3210 = _mm_loadu_ps(&coherence_state->sxd[i][0]); - const __m128 vec_sxd_7654 = _mm_loadu_ps(&coherence_state->sxd[i + 2][0]); - const __m128 vec_sde_0 = - _mm_shuffle_ps(vec_sde_3210, vec_sde_7654, _MM_SHUFFLE(2, 0, 2, 0)); - const __m128 vec_sde_1 = - _mm_shuffle_ps(vec_sde_3210, vec_sde_7654, _MM_SHUFFLE(3, 1, 3, 1)); - const __m128 vec_sxd_0 = - _mm_shuffle_ps(vec_sxd_3210, vec_sxd_7654, _MM_SHUFFLE(2, 0, 2, 0)); - const __m128 vec_sxd_1 = - _mm_shuffle_ps(vec_sxd_3210, vec_sxd_7654, _MM_SHUFFLE(3, 1, 3, 1)); - __m128 vec_cohde = _mm_mul_ps(vec_sde_0, vec_sde_0); - __m128 vec_cohxd = _mm_mul_ps(vec_sxd_0, vec_sxd_0); - vec_cohde = _mm_add_ps(vec_cohde, _mm_mul_ps(vec_sde_1, vec_sde_1)); - vec_cohde = _mm_div_ps(vec_cohde, vec_sdse); - vec_cohxd = _mm_add_ps(vec_cohxd, _mm_mul_ps(vec_sxd_1, vec_sxd_1)); - vec_cohxd = _mm_div_ps(vec_cohxd, vec_sdsx); - _mm_storeu_ps(&cohde[i], vec_cohde); - _mm_storeu_ps(&cohxd[i], vec_cohxd); - } - - // scalar code for the remaining items. - for (; i < PART_LEN1; i++) { - cohde[i] = (coherence_state->sde[i][0] * coherence_state->sde[i][0] + - coherence_state->sde[i][1] * coherence_state->sde[i][1]) / - (coherence_state->sd[i] * coherence_state->se[i] + 1e-10f); - cohxd[i] = (coherence_state->sxd[i][0] * coherence_state->sxd[i][0] + - coherence_state->sxd[i][1] * coherence_state->sxd[i][1]) / - (coherence_state->sx[i] * coherence_state->sd[i] + 1e-10f); - } - } -} - -void WebRtcAec_InitAec_SSE2(void) { - WebRtcAec_FilterFar = FilterFarSSE2; - WebRtcAec_ScaleErrorSignal = ScaleErrorSignalSSE2; - WebRtcAec_FilterAdaptation = FilterAdaptationSSE2; - WebRtcAec_Overdrive = OverdriveSSE2; - WebRtcAec_Suppress = SuppressSSE2; - WebRtcAec_ComputeCoherence = ComputeCoherenceSSE2; - WebRtcAec_UpdateCoherenceSpectra = UpdateCoherenceSpectraSSE2; - WebRtcAec_StoreAsComplex = StoreAsComplexSSE2; - WebRtcAec_PartitionDelay = PartitionDelaySSE2; - WebRtcAec_WindowData = WindowDataSSE2; -} -} // namespace webrtc diff --git a/modules/audio_processing/aec/aec_resampler.cc b/modules/audio_processing/aec/aec_resampler.cc deleted file mode 100644 index 210c2bebe0..0000000000 --- a/modules/audio_processing/aec/aec_resampler.cc +++ /dev/null @@ -1,206 +0,0 @@ -/* - * 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. - */ - -/* Resamples a signal to an arbitrary rate. Used by the AEC to compensate for - * clock skew by resampling the farend signal. - */ - -#include "modules/audio_processing/aec/aec_resampler.h" - -#include -#include - -#include "modules/audio_processing/aec/aec_core.h" -#include "rtc_base/checks.h" - -namespace webrtc { - -enum { kEstimateLengthFrames = 400 }; - -typedef struct { - float buffer[kResamplerBufferSize]; - float position; - - int deviceSampleRateHz; - int skewData[kEstimateLengthFrames]; - int skewDataIndex; - float skewEstimate; -} AecResampler; - -static int EstimateSkew(const int* rawSkew, - int size, - int deviceSampleRateHz, - float* skewEst); - -void* WebRtcAec_CreateResampler() { - return malloc(sizeof(AecResampler)); -} - -int WebRtcAec_InitResampler(void* resampInst, int deviceSampleRateHz) { - AecResampler* obj = static_cast(resampInst); - memset(obj->buffer, 0, sizeof(obj->buffer)); - obj->position = 0.0; - - obj->deviceSampleRateHz = deviceSampleRateHz; - memset(obj->skewData, 0, sizeof(obj->skewData)); - obj->skewDataIndex = 0; - obj->skewEstimate = 0.0; - - return 0; -} - -void WebRtcAec_FreeResampler(void* resampInst) { - AecResampler* obj = static_cast(resampInst); - free(obj); -} - -void WebRtcAec_ResampleLinear(void* resampInst, - const float* inspeech, - size_t size, - float skew, - float* outspeech, - size_t* size_out) { - AecResampler* obj = static_cast(resampInst); - - float* y; - float be, tnew; - size_t tn, mm; - - RTC_DCHECK_LE(size, 2 * FRAME_LEN); - RTC_DCHECK(resampInst); - RTC_DCHECK(inspeech); - RTC_DCHECK(outspeech); - RTC_DCHECK(size_out); - - // Add new frame data in lookahead - memcpy(&obj->buffer[FRAME_LEN + kResamplingDelay], inspeech, - size * sizeof(inspeech[0])); - - // Sample rate ratio - be = 1 + skew; - - // Loop over input frame - mm = 0; - y = &obj->buffer[FRAME_LEN]; // Point at current frame - - tnew = be * mm + obj->position; - tn = (size_t)tnew; - - while (tn < size) { - // Interpolation - outspeech[mm] = y[tn] + (tnew - tn) * (y[tn + 1] - y[tn]); - mm++; - - tnew = be * mm + obj->position; - tn = static_cast(tnew); - } - - *size_out = mm; - obj->position += (*size_out) * be - size; - - // Shift buffer - memmove(obj->buffer, &obj->buffer[size], - (kResamplerBufferSize - size) * sizeof(obj->buffer[0])); -} - -int WebRtcAec_GetSkew(void* resampInst, int rawSkew, float* skewEst) { - AecResampler* obj = static_cast(resampInst); - int err = 0; - - if (obj->skewDataIndex < kEstimateLengthFrames) { - obj->skewData[obj->skewDataIndex] = rawSkew; - obj->skewDataIndex++; - } else if (obj->skewDataIndex == kEstimateLengthFrames) { - err = EstimateSkew(obj->skewData, kEstimateLengthFrames, - obj->deviceSampleRateHz, skewEst); - obj->skewEstimate = *skewEst; - obj->skewDataIndex++; - } else { - *skewEst = obj->skewEstimate; - } - - return err; -} - -int EstimateSkew(const int* rawSkew, - int size, - int deviceSampleRateHz, - float* skewEst) { - const int absLimitOuter = static_cast(0.04f * deviceSampleRateHz); - const int absLimitInner = static_cast(0.0025f * deviceSampleRateHz); - int i = 0; - int n = 0; - float rawAvg = 0; - float err = 0; - float rawAbsDev = 0; - int upperLimit = 0; - int lowerLimit = 0; - float cumSum = 0; - float x = 0; - float x2 = 0; - float y = 0; - float xy = 0; - float xAvg = 0; - float denom = 0; - float skew = 0; - - *skewEst = 0; // Set in case of error below. - for (i = 0; i < size; i++) { - if ((rawSkew[i] < absLimitOuter && rawSkew[i] > -absLimitOuter)) { - n++; - rawAvg += rawSkew[i]; - } - } - - if (n == 0) { - return -1; - } - RTC_DCHECK_GT(n, 0); - rawAvg /= n; - - for (i = 0; i < size; i++) { - if ((rawSkew[i] < absLimitOuter && rawSkew[i] > -absLimitOuter)) { - err = rawSkew[i] - rawAvg; - rawAbsDev += err >= 0 ? err : -err; - } - } - RTC_DCHECK_GT(n, 0); - rawAbsDev /= n; - upperLimit = static_cast(rawAvg + 5 * rawAbsDev + 1); // +1 for ceiling. - lowerLimit = static_cast(rawAvg - 5 * rawAbsDev - 1); // -1 for floor. - - n = 0; - for (i = 0; i < size; i++) { - if ((rawSkew[i] < absLimitInner && rawSkew[i] > -absLimitInner) || - (rawSkew[i] < upperLimit && rawSkew[i] > lowerLimit)) { - n++; - cumSum += rawSkew[i]; - x += n; - x2 += n * n; - y += cumSum; - xy += n * cumSum; - } - } - - if (n == 0) { - return -1; - } - RTC_DCHECK_GT(n, 0); - xAvg = x / n; - denom = x2 - xAvg * x; - - if (denom != 0) { - skew = (xy - xAvg * y) / denom; - } - - *skewEst = skew; - return 0; -} -} // namespace webrtc diff --git a/modules/audio_processing/aec/aec_resampler.h b/modules/audio_processing/aec/aec_resampler.h deleted file mode 100644 index a112c434d0..0000000000 --- a/modules/audio_processing/aec/aec_resampler.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * 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. - */ - -#ifndef MODULES_AUDIO_PROCESSING_AEC_AEC_RESAMPLER_H_ -#define MODULES_AUDIO_PROCESSING_AEC_AEC_RESAMPLER_H_ - -#include - -#include "modules/audio_processing/aec/aec_core.h" - -namespace webrtc { - -enum { kResamplingDelay = 1 }; -enum { kResamplerBufferSize = FRAME_LEN * 4 }; - -// Unless otherwise specified, functions return 0 on success and -1 on error. -void* WebRtcAec_CreateResampler(); // Returns NULL on error. -int WebRtcAec_InitResampler(void* resampInst, int deviceSampleRateHz); -void WebRtcAec_FreeResampler(void* resampInst); - -// Estimates skew from raw measurement. -int WebRtcAec_GetSkew(void* resampInst, int rawSkew, float* skewEst); - -// Resamples input using linear interpolation. -void WebRtcAec_ResampleLinear(void* resampInst, - const float* inspeech, - size_t size, - float skew, - float* outspeech, - size_t* size_out); - -} // namespace webrtc - -#endif // MODULES_AUDIO_PROCESSING_AEC_AEC_RESAMPLER_H_ diff --git a/modules/audio_processing/aec/echo_cancellation.cc b/modules/audio_processing/aec/echo_cancellation.cc deleted file mode 100644 index fd1aec4058..0000000000 --- a/modules/audio_processing/aec/echo_cancellation.cc +++ /dev/null @@ -1,864 +0,0 @@ -/* - * 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. - */ - -/* - * Contains the API functions for the AEC. - */ -#include "modules/audio_processing/aec/echo_cancellation.h" - -#include -#include -#include - -extern "C" { -#include "common_audio/ring_buffer.h" -#include "common_audio/signal_processing/include/signal_processing_library.h" -} -#include "modules/audio_processing/aec/aec_core.h" -#include "modules/audio_processing/aec/aec_resampler.h" -#include "modules/audio_processing/logging/apm_data_dumper.h" - -namespace webrtc { - -Aec::Aec() = default; -Aec::~Aec() = default; - -// Measured delays [ms] -// Device Chrome GTP -// MacBook Air 10 -// MacBook Retina 10 100 -// MacPro 30? -// -// Win7 Desktop 70 80? -// Win7 T430s 110 -// Win8 T420s 70 -// -// Daisy 50 -// Pixel (w/ preproc?) 240 -// Pixel (w/o preproc?) 110 110 - -// The extended filter mode gives us the flexibility to ignore the system's -// reported delays. We do this for platforms which we believe provide results -// which are incompatible with the AEC's expectations. Based on measurements -// (some provided above) we set a conservative (i.e. lower than measured) -// fixed delay. -// -// WEBRTC_UNTRUSTED_DELAY will only have an impact when |extended_filter_mode| -// is enabled. See the note along with |DelayCorrection| in -// echo_cancellation_impl.h for more details on the mode. -// -// Justification: -// Chromium/Mac: Here, the true latency is so low (~10-20 ms), that it plays -// havoc with the AEC's buffering. To avoid this, we set a fixed delay of 20 ms -// and then compensate by rewinding by 10 ms (in wideband) through -// kDelayDiffOffsetSamples. This trick does not seem to work for larger rewind -// values, but fortunately this is sufficient. -// -// Chromium/Linux(ChromeOS): The values we get on this platform don't correspond -// well to reality. The variance doesn't match the AEC's buffer changes, and the -// bulk values tend to be too low. However, the range across different hardware -// appears to be too large to choose a single value. -// -// GTP/Linux(ChromeOS): TBD, but for the moment we will trust the values. -#if defined(WEBRTC_CHROMIUM_BUILD) && defined(WEBRTC_MAC) -#define WEBRTC_UNTRUSTED_DELAY -#endif - -#if defined(WEBRTC_UNTRUSTED_DELAY) && defined(WEBRTC_MAC) -static const int kDelayDiffOffsetSamples = -160; -#else -// Not enabled for now. -static const int kDelayDiffOffsetSamples = 0; -#endif - -#if defined(WEBRTC_MAC) -static const int kFixedDelayMs = 20; -#else -static const int kFixedDelayMs = 50; -#endif -#if !defined(WEBRTC_UNTRUSTED_DELAY) -static const int kMinTrustedDelayMs = 20; -#endif -static const int kMaxTrustedDelayMs = 500; - -// Maximum length of resampled signal. Must be an integer multiple of frames -// (ceil(1/(1 + MIN_SKEW)*2) + 1)*FRAME_LEN -// The factor of 2 handles wb, and the + 1 is as a safety margin -// TODO(bjornv): Replace with kResamplerBufferSize -#define MAX_RESAMP_LEN (5 * FRAME_LEN) - -static const int kMaxBufSizeStart = 62; // In partitions -static const int sampMsNb = 8; // samples per ms in nb -static const int initCheck = 42; - -int Aec::instance_count = 0; - -// Estimates delay to set the position of the far-end buffer read pointer -// (controlled by knownDelay) -static void EstBufDelayNormal(Aec* aecInst); -static void EstBufDelayExtended(Aec* aecInst); -static int ProcessNormal(Aec* aecInst, - const float* const* nearend, - size_t num_bands, - float* const* out, - size_t num_samples, - int16_t reported_delay_ms, - int32_t skew); -static void ProcessExtended(Aec* aecInst, - const float* const* nearend, - size_t num_bands, - float* const* out, - size_t num_samples, - int16_t reported_delay_ms, - int32_t skew); - -void* WebRtcAec_Create() { - Aec* aecpc = new Aec(); - - if (!aecpc) { - return NULL; - } - aecpc->data_dumper.reset(new ApmDataDumper(aecpc->instance_count)); - - aecpc->aec = WebRtcAec_CreateAec(aecpc->instance_count); - if (!aecpc->aec) { - WebRtcAec_Free(aecpc); - return NULL; - } - aecpc->resampler = WebRtcAec_CreateResampler(); - if (!aecpc->resampler) { - WebRtcAec_Free(aecpc); - return NULL; - } - // Create far-end pre-buffer. The buffer size has to be large enough for - // largest possible drift compensation (kResamplerBufferSize) + "almost" an - // FFT buffer (PART_LEN2 - 1). - aecpc->far_pre_buf = - WebRtc_CreateBuffer(PART_LEN2 + kResamplerBufferSize, sizeof(float)); - if (!aecpc->far_pre_buf) { - WebRtcAec_Free(aecpc); - return NULL; - } - - aecpc->initFlag = 0; - - aecpc->instance_count++; - return aecpc; -} - -void WebRtcAec_Free(void* aecInst) { - Aec* aecpc = reinterpret_cast(aecInst); - - if (aecpc == NULL) { - return; - } - - WebRtc_FreeBuffer(aecpc->far_pre_buf); - - WebRtcAec_FreeAec(aecpc->aec); - WebRtcAec_FreeResampler(aecpc->resampler); - delete aecpc; -} - -int32_t WebRtcAec_Init(void* aecInst, int32_t sampFreq, int32_t scSampFreq) { - Aec* aecpc = reinterpret_cast(aecInst); - aecpc->data_dumper->InitiateNewSetOfRecordings(); - AecConfig aecConfig; - - if (sampFreq != 8000 && sampFreq != 16000 && sampFreq != 32000 && - sampFreq != 48000) { - return AEC_BAD_PARAMETER_ERROR; - } - aecpc->sampFreq = sampFreq; - - if (scSampFreq < 1 || scSampFreq > 96000) { - return AEC_BAD_PARAMETER_ERROR; - } - aecpc->scSampFreq = scSampFreq; - - // Initialize echo canceller core - if (WebRtcAec_InitAec(aecpc->aec, aecpc->sampFreq) == -1) { - return AEC_UNSPECIFIED_ERROR; - } - - if (WebRtcAec_InitResampler(aecpc->resampler, aecpc->scSampFreq) == -1) { - return AEC_UNSPECIFIED_ERROR; - } - - WebRtc_InitBuffer(aecpc->far_pre_buf); - WebRtc_MoveReadPtr(aecpc->far_pre_buf, -PART_LEN); // Start overlap. - - aecpc->initFlag = initCheck; // indicates that initialization has been done - - if (aecpc->sampFreq == 32000 || aecpc->sampFreq == 48000) { - aecpc->splitSampFreq = 16000; - } else { - aecpc->splitSampFreq = sampFreq; - } - - aecpc->delayCtr = 0; - aecpc->sampFactor = (aecpc->scSampFreq * 1.0f) / aecpc->splitSampFreq; - // Sampling frequency multiplier (SWB is processed as 160 frame size). - aecpc->rate_factor = aecpc->splitSampFreq / 8000; - - aecpc->sum = 0; - aecpc->counter = 0; - aecpc->checkBuffSize = 1; - aecpc->firstVal = 0; - - // We skip the startup_phase completely (setting to 0) if DA-AEC is enabled, - // but not extended_filter mode. - aecpc->startup_phase = WebRtcAec_extended_filter_enabled(aecpc->aec) || - !WebRtcAec_delay_agnostic_enabled(aecpc->aec); - aecpc->bufSizeStart = 0; - aecpc->checkBufSizeCtr = 0; - aecpc->msInSndCardBuf = 0; - aecpc->filtDelay = -1; // -1 indicates an initialized state. - aecpc->timeForDelayChange = 0; - aecpc->knownDelay = 0; - aecpc->lastDelayDiff = 0; - - aecpc->skewFrCtr = 0; - aecpc->resample = kAecFalse; - aecpc->highSkewCtr = 0; - aecpc->skew = 0; - - aecpc->farend_started = 0; - - // Default settings. - aecConfig.nlpMode = kAecNlpModerate; - aecConfig.skewMode = kAecFalse; - aecConfig.metricsMode = kAecFalse; - aecConfig.delay_logging = kAecFalse; - - if (WebRtcAec_set_config(aecpc, aecConfig) == -1) { - return AEC_UNSPECIFIED_ERROR; - } - - return 0; -} - -// Returns any error that is caused when buffering the -// far-end signal. -int32_t WebRtcAec_GetBufferFarendError(void* aecInst, - const float* farend, - size_t nrOfSamples) { - Aec* aecpc = reinterpret_cast(aecInst); - - if (!farend) - return AEC_NULL_POINTER_ERROR; - - if (aecpc->initFlag != initCheck) - return AEC_UNINITIALIZED_ERROR; - - // number of samples == 160 for SWB input - if (nrOfSamples != 80 && nrOfSamples != 160) - return AEC_BAD_PARAMETER_ERROR; - - return 0; -} - -// only buffer L band for farend -int32_t WebRtcAec_BufferFarend(void* aecInst, - const float* farend, - size_t nrOfSamples) { - Aec* aecpc = reinterpret_cast(aecInst); - size_t newNrOfSamples = nrOfSamples; - float new_farend[MAX_RESAMP_LEN]; - const float* farend_ptr = farend; - - // Get any error caused by buffering the farend signal. - int32_t error_code = - WebRtcAec_GetBufferFarendError(aecInst, farend, nrOfSamples); - - if (error_code != 0) - return error_code; - - if (aecpc->skewMode == kAecTrue && aecpc->resample == kAecTrue) { - // Resample and get a new number of samples - WebRtcAec_ResampleLinear(aecpc->resampler, farend, nrOfSamples, aecpc->skew, - new_farend, &newNrOfSamples); - farend_ptr = new_farend; - } - - aecpc->farend_started = 1; - WebRtcAec_SetSystemDelay(aecpc->aec, WebRtcAec_system_delay(aecpc->aec) + - static_cast(newNrOfSamples)); - - // Write the time-domain data to |far_pre_buf|. - WebRtc_WriteBuffer(aecpc->far_pre_buf, farend_ptr, newNrOfSamples); - - // TODO(minyue): reduce to |PART_LEN| samples for each buffering. - while (WebRtc_available_read(aecpc->far_pre_buf) >= PART_LEN2) { - // We have enough data to pass to the FFT, hence read PART_LEN2 samples. - { - float* ptmp = NULL; - float tmp[PART_LEN2]; - WebRtc_ReadBuffer(aecpc->far_pre_buf, reinterpret_cast(&ptmp), - tmp, PART_LEN2); - WebRtcAec_BufferFarendBlock(aecpc->aec, &ptmp[PART_LEN]); - } - - // Rewind |far_pre_buf| PART_LEN samples for overlap before continuing. - WebRtc_MoveReadPtr(aecpc->far_pre_buf, -PART_LEN); - } - - return 0; -} - -int32_t WebRtcAec_Process(void* aecInst, - const float* const* nearend, - size_t num_bands, - float* const* out, - size_t nrOfSamples, - int16_t msInSndCardBuf, - int32_t skew) { - Aec* aecpc = reinterpret_cast(aecInst); - int32_t retVal = 0; - - if (out == NULL) { - return AEC_NULL_POINTER_ERROR; - } - - if (aecpc->initFlag != initCheck) { - return AEC_UNINITIALIZED_ERROR; - } - - // number of samples == 160 for SWB input - if (nrOfSamples != 80 && nrOfSamples != 160) { - return AEC_BAD_PARAMETER_ERROR; - } - - if (msInSndCardBuf < 0) { - msInSndCardBuf = 0; - retVal = AEC_BAD_PARAMETER_WARNING; - } else if (msInSndCardBuf > kMaxTrustedDelayMs) { - // The clamping is now done in ProcessExtended/Normal(). - retVal = AEC_BAD_PARAMETER_WARNING; - } - - // This returns the value of aec->extended_filter_enabled. - if (WebRtcAec_extended_filter_enabled(aecpc->aec)) { - ProcessExtended(aecpc, nearend, num_bands, out, nrOfSamples, msInSndCardBuf, - skew); - } else { - retVal = ProcessNormal(aecpc, nearend, num_bands, out, nrOfSamples, - msInSndCardBuf, skew); - } - - int far_buf_size_samples = WebRtcAec_system_delay(aecpc->aec); - aecpc->data_dumper->DumpRaw("aec_system_delay", 1, &far_buf_size_samples); - aecpc->data_dumper->DumpRaw("aec_known_delay", 1, &aecpc->knownDelay); - - return retVal; -} - -int WebRtcAec_set_config(void* handle, AecConfig config) { - Aec* self = reinterpret_cast(handle); - if (self->initFlag != initCheck) { - return AEC_UNINITIALIZED_ERROR; - } - - if (config.skewMode != kAecFalse && config.skewMode != kAecTrue) { - return AEC_BAD_PARAMETER_ERROR; - } - self->skewMode = config.skewMode; - - if (config.nlpMode != kAecNlpConservative && - config.nlpMode != kAecNlpModerate && - config.nlpMode != kAecNlpAggressive) { - return AEC_BAD_PARAMETER_ERROR; - } - - if (config.metricsMode != kAecFalse && config.metricsMode != kAecTrue) { - return AEC_BAD_PARAMETER_ERROR; - } - - if (config.delay_logging != kAecFalse && config.delay_logging != kAecTrue) { - return AEC_BAD_PARAMETER_ERROR; - } - - WebRtcAec_SetConfigCore(self->aec, config.nlpMode, config.metricsMode, - config.delay_logging); - return 0; -} - -int WebRtcAec_get_echo_status(void* handle, int* status) { - Aec* self = reinterpret_cast(handle); - if (status == NULL) { - return AEC_NULL_POINTER_ERROR; - } - if (self->initFlag != initCheck) { - return AEC_UNINITIALIZED_ERROR; - } - - *status = WebRtcAec_echo_state(self->aec); - - return 0; -} - -int WebRtcAec_GetMetrics(void* handle, AecMetrics* metrics) { - const float kUpWeight = 0.7f; - float dtmp; - int stmp; - Aec* self = reinterpret_cast(handle); - Stats erl; - Stats erle; - Stats a_nlp; - - if (handle == NULL) { - return -1; - } - if (metrics == NULL) { - return AEC_NULL_POINTER_ERROR; - } - if (self->initFlag != initCheck) { - return AEC_UNINITIALIZED_ERROR; - } - - WebRtcAec_GetEchoStats(self->aec, &erl, &erle, &a_nlp, - &metrics->divergent_filter_fraction); - - // ERL - metrics->erl.instant = static_cast(erl.instant); - - if ((erl.himean > kOffsetLevel) && (erl.average > kOffsetLevel)) { - // Use a mix between regular average and upper part average. - dtmp = kUpWeight * erl.himean + (1 - kUpWeight) * erl.average; - metrics->erl.average = static_cast(dtmp); - } else { - metrics->erl.average = kOffsetLevel; - } - - metrics->erl.max = static_cast(erl.max); - - if (erl.min < (kOffsetLevel * (-1))) { - metrics->erl.min = static_cast(erl.min); - } else { - metrics->erl.min = kOffsetLevel; - } - - // ERLE - metrics->erle.instant = static_cast(erle.instant); - - if ((erle.himean > kOffsetLevel) && (erle.average > kOffsetLevel)) { - // Use a mix between regular average and upper part average. - dtmp = kUpWeight * erle.himean + (1 - kUpWeight) * erle.average; - metrics->erle.average = static_cast(dtmp); - } else { - metrics->erle.average = kOffsetLevel; - } - - metrics->erle.max = static_cast(erle.max); - - if (erle.min < (kOffsetLevel * (-1))) { - metrics->erle.min = static_cast(erle.min); - } else { - metrics->erle.min = kOffsetLevel; - } - - // RERL - if ((metrics->erl.average > kOffsetLevel) && - (metrics->erle.average > kOffsetLevel)) { - stmp = metrics->erl.average + metrics->erle.average; - } else { - stmp = kOffsetLevel; - } - metrics->rerl.average = stmp; - - // No other statistics needed, but returned for completeness. - metrics->rerl.instant = stmp; - metrics->rerl.max = stmp; - metrics->rerl.min = stmp; - - // A_NLP - metrics->aNlp.instant = static_cast(a_nlp.instant); - - if ((a_nlp.himean > kOffsetLevel) && (a_nlp.average > kOffsetLevel)) { - // Use a mix between regular average and upper part average. - dtmp = kUpWeight * a_nlp.himean + (1 - kUpWeight) * a_nlp.average; - metrics->aNlp.average = static_cast(dtmp); - } else { - metrics->aNlp.average = kOffsetLevel; - } - - metrics->aNlp.max = static_cast(a_nlp.max); - - if (a_nlp.min < (kOffsetLevel * (-1))) { - metrics->aNlp.min = static_cast(a_nlp.min); - } else { - metrics->aNlp.min = kOffsetLevel; - } - - return 0; -} - -int WebRtcAec_GetDelayMetrics(void* handle, - int* median, - int* std, - float* fraction_poor_delays) { - Aec* self = reinterpret_cast(handle); - if (median == NULL) { - return AEC_NULL_POINTER_ERROR; - } - if (std == NULL) { - return AEC_NULL_POINTER_ERROR; - } - if (self->initFlag != initCheck) { - return AEC_UNINITIALIZED_ERROR; - } - if (WebRtcAec_GetDelayMetricsCore(self->aec, median, std, - fraction_poor_delays) == -1) { - // Logging disabled. - return AEC_UNSUPPORTED_FUNCTION_ERROR; - } - - return 0; -} - -AecCore* WebRtcAec_aec_core(void* handle) { - if (!handle) { - return NULL; - } - return reinterpret_cast(handle)->aec; -} - -static int ProcessNormal(Aec* aecInst, - const float* const* nearend, - size_t num_bands, - float* const* out, - size_t num_samples, - int16_t reported_delay_ms, - int32_t skew) { - int retVal = 0; - size_t i; - size_t nBlocks10ms; - // Limit resampling to doubling/halving of signal - const float minSkewEst = -0.5f; - const float maxSkewEst = 1.0f; - - reported_delay_ms = reported_delay_ms > kMaxTrustedDelayMs - ? kMaxTrustedDelayMs - : reported_delay_ms; - // TODO(andrew): we need to investigate if this +10 is really wanted. - reported_delay_ms += 10; - aecInst->msInSndCardBuf = reported_delay_ms; - - if (aecInst->skewMode == kAecTrue) { - if (aecInst->skewFrCtr < 25) { - aecInst->skewFrCtr++; - } else { - retVal = WebRtcAec_GetSkew(aecInst->resampler, skew, &aecInst->skew); - if (retVal == -1) { - aecInst->skew = 0; - retVal = AEC_BAD_PARAMETER_WARNING; - } - - aecInst->skew /= aecInst->sampFactor * num_samples; - - if (aecInst->skew < 1.0e-3 && aecInst->skew > -1.0e-3) { - aecInst->resample = kAecFalse; - } else { - aecInst->resample = kAecTrue; - } - - if (aecInst->skew < minSkewEst) { - aecInst->skew = minSkewEst; - } else if (aecInst->skew > maxSkewEst) { - aecInst->skew = maxSkewEst; - } - - aecInst->data_dumper->DumpRaw("aec_skew", 1, &aecInst->skew); - } - } - - nBlocks10ms = num_samples / (FRAME_LEN * aecInst->rate_factor); - - if (aecInst->startup_phase) { - for (i = 0; i < num_bands; ++i) { - // Only needed if they don't already point to the same place. - if (nearend[i] != out[i]) { - memcpy(out[i], nearend[i], sizeof(nearend[i][0]) * num_samples); - } - } - - // The AEC is in the start up mode - // AEC is disabled until the system delay is OK - - // Mechanism to ensure that the system delay is reasonably stable. - if (aecInst->checkBuffSize) { - aecInst->checkBufSizeCtr++; - // Before we fill up the far-end buffer we require the system delay - // to be stable (+/-8 ms) compared to the first value. This - // comparison is made during the following 6 consecutive 10 ms - // blocks. If it seems to be stable then we start to fill up the - // far-end buffer. - if (aecInst->counter == 0) { - aecInst->firstVal = aecInst->msInSndCardBuf; - aecInst->sum = 0; - } - - if (abs(aecInst->firstVal - aecInst->msInSndCardBuf) < - WEBRTC_SPL_MAX(0.2 * aecInst->msInSndCardBuf, sampMsNb)) { - aecInst->sum += aecInst->msInSndCardBuf; - aecInst->counter++; - } else { - aecInst->counter = 0; - } - - if (aecInst->counter * nBlocks10ms >= 6) { - // The far-end buffer size is determined in partitions of - // PART_LEN samples. Use 75% of the average value of the system - // delay as buffer size to start with. - aecInst->bufSizeStart = - WEBRTC_SPL_MIN((3 * aecInst->sum * aecInst->rate_factor * 8) / - (4 * aecInst->counter * PART_LEN), - kMaxBufSizeStart); - // Buffer size has now been determined. - aecInst->checkBuffSize = 0; - } - - if (aecInst->checkBufSizeCtr * nBlocks10ms > 50) { - // For really bad systems, don't disable the echo canceller for - // more than 0.5 sec. - aecInst->bufSizeStart = WEBRTC_SPL_MIN( - (aecInst->msInSndCardBuf * aecInst->rate_factor * 3) / 40, - kMaxBufSizeStart); - aecInst->checkBuffSize = 0; - } - } - - // If |checkBuffSize| changed in the if-statement above. - if (!aecInst->checkBuffSize) { - // The system delay is now reasonably stable (or has been unstable - // for too long). When the far-end buffer is filled with - // approximately the same amount of data as reported by the system - // we end the startup phase. - int overhead_elements = WebRtcAec_system_delay(aecInst->aec) / PART_LEN - - aecInst->bufSizeStart; - if (overhead_elements == 0) { - // Enable the AEC - aecInst->startup_phase = 0; - } else if (overhead_elements > 0) { - // TODO(bjornv): Do we need a check on how much we actually - // moved the read pointer? It should always be possible to move - // the pointer |overhead_elements| since we have only added data - // to the buffer and no delay compensation nor AEC processing - // has been done. - WebRtcAec_AdjustFarendBufferSizeAndSystemDelay(aecInst->aec, - overhead_elements); - - // Enable the AEC - aecInst->startup_phase = 0; - } - } - } else { - // AEC is enabled. - EstBufDelayNormal(aecInst); - - // Call the AEC. - // TODO(bjornv): Re-structure such that we don't have to pass - // |aecInst->knownDelay| as input. Change name to something like - // |system_buffer_diff|. - WebRtcAec_ProcessFrames(aecInst->aec, nearend, num_bands, num_samples, - aecInst->knownDelay, out); - } - - return retVal; -} - -static void ProcessExtended(Aec* self, - const float* const* nearend, - size_t num_bands, - float* const* out, - size_t num_samples, - int16_t reported_delay_ms, - int32_t skew) { - size_t i; - const int delay_diff_offset = kDelayDiffOffsetSamples; - RTC_DCHECK(num_samples == 80 || num_samples == 160); -#if defined(WEBRTC_UNTRUSTED_DELAY) - reported_delay_ms = kFixedDelayMs; -#else - // This is the usual mode where we trust the reported system delay values. - // Due to the longer filter, we no longer add 10 ms to the reported delay - // to reduce chance of non-causality. Instead we apply a minimum here to avoid - // issues with the read pointer jumping around needlessly. - reported_delay_ms = reported_delay_ms < kMinTrustedDelayMs - ? kMinTrustedDelayMs - : reported_delay_ms; - // If the reported delay appears to be bogus, we attempt to recover by using - // the measured fixed delay values. We use >= here because higher layers - // may already clamp to this maximum value, and we would otherwise not - // detect it here. - reported_delay_ms = reported_delay_ms >= kMaxTrustedDelayMs - ? kFixedDelayMs - : reported_delay_ms; -#endif - self->msInSndCardBuf = reported_delay_ms; - - if (!self->farend_started) { - for (i = 0; i < num_bands; ++i) { - // Only needed if they don't already point to the same place. - if (nearend[i] != out[i]) { - memcpy(out[i], nearend[i], sizeof(nearend[i][0]) * num_samples); - } - } - return; - } - if (self->startup_phase) { - // In the extended mode, there isn't a startup "phase", just a special - // action on the first frame. In the trusted delay case, we'll take the - // current reported delay, unless it's less then our conservative - // measurement. - int startup_size_ms = - reported_delay_ms < kFixedDelayMs ? kFixedDelayMs : reported_delay_ms; -#if defined(WEBRTC_ANDROID) - int target_delay = startup_size_ms * self->rate_factor * 8; -#else - // To avoid putting the AEC in a non-causal state we're being slightly - // conservative and scale by 2. On Android we use a fixed delay and - // therefore there is no need to scale the target_delay. - int target_delay = startup_size_ms * self->rate_factor * 8 / 2; -#endif - int overhead_elements = - (WebRtcAec_system_delay(self->aec) - target_delay) / PART_LEN; - WebRtcAec_AdjustFarendBufferSizeAndSystemDelay(self->aec, - overhead_elements); - self->startup_phase = 0; - } - - EstBufDelayExtended(self); - - { - // |delay_diff_offset| gives us the option to manually rewind the delay on - // very low delay platforms which can't be expressed purely through - // |reported_delay_ms|. - const int adjusted_known_delay = - WEBRTC_SPL_MAX(0, self->knownDelay + delay_diff_offset); - - WebRtcAec_ProcessFrames(self->aec, nearend, num_bands, num_samples, - adjusted_known_delay, out); - } -} - -static void EstBufDelayNormal(Aec* aecInst) { - int nSampSndCard = aecInst->msInSndCardBuf * sampMsNb * aecInst->rate_factor; - int current_delay = nSampSndCard - WebRtcAec_system_delay(aecInst->aec); - int delay_difference = 0; - - // Before we proceed with the delay estimate filtering we: - // 1) Compensate for the frame that will be read. - // 2) Compensate for drift resampling. - // 3) Compensate for non-causality if needed, since the estimated delay can't - // be negative. - - // 1) Compensating for the frame(s) that will be read/processed. - current_delay += FRAME_LEN * aecInst->rate_factor; - - // 2) Account for resampling frame delay. - if (aecInst->skewMode == kAecTrue && aecInst->resample == kAecTrue) { - current_delay -= kResamplingDelay; - } - - // 3) Compensate for non-causality, if needed, by flushing one block. - if (current_delay < PART_LEN) { - current_delay += - WebRtcAec_AdjustFarendBufferSizeAndSystemDelay(aecInst->aec, 1) * - PART_LEN; - } - - // We use -1 to signal an initialized state in the "extended" implementation; - // compensate for that. - aecInst->filtDelay = aecInst->filtDelay < 0 ? 0 : aecInst->filtDelay; - aecInst->filtDelay = WEBRTC_SPL_MAX( - 0, static_cast(0.8 * aecInst->filtDelay + 0.2 * current_delay)); - - delay_difference = aecInst->filtDelay - aecInst->knownDelay; - if (delay_difference > 224) { - if (aecInst->lastDelayDiff < 96) { - aecInst->timeForDelayChange = 0; - } else { - aecInst->timeForDelayChange++; - } - } else if (delay_difference < 96 && aecInst->knownDelay > 0) { - if (aecInst->lastDelayDiff > 224) { - aecInst->timeForDelayChange = 0; - } else { - aecInst->timeForDelayChange++; - } - } else { - aecInst->timeForDelayChange = 0; - } - aecInst->lastDelayDiff = delay_difference; - - if (aecInst->timeForDelayChange > 25) { - aecInst->knownDelay = WEBRTC_SPL_MAX((int)aecInst->filtDelay - 160, 0); - } -} - -static void EstBufDelayExtended(Aec* aecInst) { - int reported_delay = - aecInst->msInSndCardBuf * sampMsNb * aecInst->rate_factor; - int current_delay = reported_delay - WebRtcAec_system_delay(aecInst->aec); - int delay_difference = 0; - - // Before we proceed with the delay estimate filtering we: - // 1) Compensate for the frame that will be read. - // 2) Compensate for drift resampling. - // 3) Compensate for non-causality if needed, since the estimated delay can't - // be negative. - - // 1) Compensating for the frame(s) that will be read/processed. - current_delay += FRAME_LEN * aecInst->rate_factor; - - // 2) Account for resampling frame delay. - if (aecInst->skewMode == kAecTrue && aecInst->resample == kAecTrue) { - current_delay -= kResamplingDelay; - } - - // 3) Compensate for non-causality, if needed, by flushing two blocks. - if (current_delay < PART_LEN) { - current_delay += - WebRtcAec_AdjustFarendBufferSizeAndSystemDelay(aecInst->aec, 2) * - PART_LEN; - } - - if (aecInst->filtDelay == -1) { - aecInst->filtDelay = WEBRTC_SPL_MAX(0, 0.5 * current_delay); - } else { - aecInst->filtDelay = WEBRTC_SPL_MAX( - 0, - static_cast(0.95 * aecInst->filtDelay + 0.05 * current_delay)); - } - - delay_difference = aecInst->filtDelay - aecInst->knownDelay; - if (delay_difference > 384) { - if (aecInst->lastDelayDiff < 128) { - aecInst->timeForDelayChange = 0; - } else { - aecInst->timeForDelayChange++; - } - } else if (delay_difference < 128 && aecInst->knownDelay > 0) { - if (aecInst->lastDelayDiff > 384) { - aecInst->timeForDelayChange = 0; - } else { - aecInst->timeForDelayChange++; - } - } else { - aecInst->timeForDelayChange = 0; - } - aecInst->lastDelayDiff = delay_difference; - - if (aecInst->timeForDelayChange > 25) { - aecInst->knownDelay = WEBRTC_SPL_MAX((int)aecInst->filtDelay - 256, 0); - } -} -} // namespace webrtc diff --git a/modules/audio_processing/aec/echo_cancellation.h b/modules/audio_processing/aec/echo_cancellation.h deleted file mode 100644 index 62dc0f03fe..0000000000 --- a/modules/audio_processing/aec/echo_cancellation.h +++ /dev/null @@ -1,298 +0,0 @@ -/* - * 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. - */ - -#ifndef MODULES_AUDIO_PROCESSING_AEC_ECHO_CANCELLATION_H_ -#define MODULES_AUDIO_PROCESSING_AEC_ECHO_CANCELLATION_H_ - -#include - -#include - -extern "C" { -#include "common_audio/ring_buffer.h" -} -#include "modules/audio_processing/aec/aec_core.h" - -namespace webrtc { - -// Errors -#define AEC_UNSPECIFIED_ERROR 12000 -#define AEC_UNSUPPORTED_FUNCTION_ERROR 12001 -#define AEC_UNINITIALIZED_ERROR 12002 -#define AEC_NULL_POINTER_ERROR 12003 -#define AEC_BAD_PARAMETER_ERROR 12004 - -// Warnings -#define AEC_BAD_PARAMETER_WARNING 12050 - -enum { kAecNlpConservative = 0, kAecNlpModerate, kAecNlpAggressive }; - -enum { kAecFalse = 0, kAecTrue }; - -typedef struct { - int16_t nlpMode; // default kAecNlpModerate - int16_t skewMode; // default kAecFalse - int16_t metricsMode; // default kAecFalse - int delay_logging; // default kAecFalse - // float realSkew; -} AecConfig; - -typedef struct { - int instant; - int average; - int max; - int min; -} AecLevel; - -typedef struct { - AecLevel rerl; - AecLevel erl; - AecLevel erle; - AecLevel aNlp; - float divergent_filter_fraction; -} AecMetrics; - -struct AecCore; - -class ApmDataDumper; - -typedef struct Aec { - Aec(); - ~Aec(); - - std::unique_ptr data_dumper; - - int delayCtr; - int sampFreq; - int splitSampFreq; - int scSampFreq; - float sampFactor; // scSampRate / sampFreq - short skewMode; - int bufSizeStart; - int knownDelay; - int rate_factor; - - short initFlag; // indicates if AEC has been initialized - - // Variables used for averaging far end buffer size - short counter; - int sum; - short firstVal; - short checkBufSizeCtr; - - // Variables used for delay shifts - short msInSndCardBuf; - short filtDelay; // Filtered delay estimate. - int timeForDelayChange; - int startup_phase; - int checkBuffSize; - short lastDelayDiff; - - // Structures - void* resampler; - - int skewFrCtr; - int resample; // if the skew is small enough we don't resample - int highSkewCtr; - float skew; - - RingBuffer* far_pre_buf; // Time domain far-end pre-buffer. - - int farend_started; - - // Aec instance counter. - static int instance_count; - AecCore* aec; -} Aec; - -/* - * Allocates the memory needed by the AEC. The memory needs to be initialized - * separately using the WebRtcAec_Init() function. Returns a pointer to the - * object or NULL on error. - */ -void* WebRtcAec_Create(); - -/* - * This function releases the memory allocated by WebRtcAec_Create(). - * - * Inputs Description - * ------------------------------------------------------------------- - * void* aecInst Pointer to the AEC instance - */ -void WebRtcAec_Free(void* aecInst); - -/* - * Initializes an AEC instance. - * - * Inputs Description - * ------------------------------------------------------------------- - * void* aecInst Pointer to the AEC instance - * int32_t sampFreq Sampling frequency of data - * int32_t scSampFreq Soundcard sampling frequency - * - * Outputs Description - * ------------------------------------------------------------------- - * int32_t return 0: OK - * -1: error - */ -int32_t WebRtcAec_Init(void* aecInst, int32_t sampFreq, int32_t scSampFreq); - -/* - * Inserts an 80 or 160 sample block of data into the farend buffer. - * - * Inputs Description - * ------------------------------------------------------------------- - * void* aecInst Pointer to the AEC instance - * const float* farend In buffer containing one frame of - * farend signal for L band - * int16_t nrOfSamples Number of samples in farend buffer - * - * Outputs Description - * ------------------------------------------------------------------- - * int32_t return 0: OK - * 12000-12050: error code - */ -int32_t WebRtcAec_BufferFarend(void* aecInst, - const float* farend, - size_t nrOfSamples); - -/* - * Reports any errors that would arise if buffering a farend buffer - * - * Inputs Description - * ------------------------------------------------------------------- - * void* aecInst Pointer to the AEC instance - * const float* farend In buffer containing one frame of - * farend signal for L band - * int16_t nrOfSamples Number of samples in farend buffer - * - * Outputs Description - * ------------------------------------------------------------------- - * int32_t return 0: OK - * 12000-12050: error code - */ -int32_t WebRtcAec_GetBufferFarendError(void* aecInst, - const float* farend, - size_t nrOfSamples); - -/* - * Runs the echo canceller on an 80 or 160 sample blocks of data. - * - * Inputs Description - * ------------------------------------------------------------------- - * void* aecInst Pointer to the AEC instance - * float* const* nearend In buffer containing one frame of - * nearend+echo signal for each band - * int num_bands Number of bands in nearend buffer - * int16_t nrOfSamples Number of samples in nearend buffer - * int16_t msInSndCardBuf Delay estimate for sound card and - * system buffers - * int16_t skew Difference between number of samples played - * and recorded at the soundcard (for clock skew - * compensation) - * - * Outputs Description - * ------------------------------------------------------------------- - * float* const* out Out buffer, one frame of processed nearend - * for each band - * int32_t return 0: OK - * 12000-12050: error code - */ -int32_t WebRtcAec_Process(void* aecInst, - const float* const* nearend, - size_t num_bands, - float* const* out, - size_t nrOfSamples, - int16_t msInSndCardBuf, - int32_t skew); - -/* - * This function enables the user to set certain parameters on-the-fly. - * - * Inputs Description - * ------------------------------------------------------------------- - * void* handle Pointer to the AEC instance - * AecConfig config Config instance that contains all - * properties to be set - * - * Outputs Description - * ------------------------------------------------------------------- - * int return 0: OK - * 12000-12050: error code - */ -int WebRtcAec_set_config(void* handle, AecConfig config); - -/* - * Gets the current echo status of the nearend signal. - * - * Inputs Description - * ------------------------------------------------------------------- - * void* handle Pointer to the AEC instance - * - * Outputs Description - * ------------------------------------------------------------------- - * int* status 0: Almost certainly nearend single-talk - * 1: Might not be neared single-talk - * int return 0: OK - * 12000-12050: error code - */ -int WebRtcAec_get_echo_status(void* handle, int* status); - -/* - * Gets the current echo metrics for the session. - * - * Inputs Description - * ------------------------------------------------------------------- - * void* handle Pointer to the AEC instance - * - * Outputs Description - * ------------------------------------------------------------------- - * AecMetrics* metrics Struct which will be filled out with the - * current echo metrics. - * int return 0: OK - * 12000-12050: error code - */ -int WebRtcAec_GetMetrics(void* handle, AecMetrics* metrics); - -/* - * Gets the current delay metrics for the session. - * - * Inputs Description - * ------------------------------------------------------------------- - * void* handle Pointer to the AEC instance - * - * Outputs Description - * ------------------------------------------------------------------- - * int* median Delay median value. - * int* std Delay standard deviation. - * float* fraction_poor_delays Fraction of the delay estimates that may - * cause the AEC to perform poorly. - * - * int return 0: OK - * 12000-12050: error code - */ -int WebRtcAec_GetDelayMetrics(void* handle, - int* median, - int* std, - float* fraction_poor_delays); - -// Returns a pointer to the low level AEC handle. -// -// Input: -// - handle : Pointer to the AEC instance. -// -// Return value: -// - AecCore pointer : NULL for error. -// -struct AecCore* WebRtcAec_aec_core(void* handle); - -} // namespace webrtc - -#endif // MODULES_AUDIO_PROCESSING_AEC_ECHO_CANCELLATION_H_ diff --git a/modules/audio_processing/aec/echo_cancellation_unittest.cc b/modules/audio_processing/aec/echo_cancellation_unittest.cc deleted file mode 100644 index b9c89fd9e9..0000000000 --- a/modules/audio_processing/aec/echo_cancellation_unittest.cc +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (c) 2013 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. - */ - -// TODO(bjornv): Make this a comprehensive test. - -#include "modules/audio_processing/aec/echo_cancellation.h" - -#include -#include - -#include "modules/audio_processing/aec/aec_core.h" -#include "rtc_base/checks.h" -#include "test/gtest.h" - -namespace webrtc { - -TEST(EchoCancellationTest, CreateAndFreeHasExpectedBehavior) { - void* handle = WebRtcAec_Create(); - ASSERT_TRUE(handle); - WebRtcAec_Free(nullptr); - WebRtcAec_Free(handle); -} - -TEST(EchoCancellationTest, ApplyAecCoreHandle) { - void* handle = WebRtcAec_Create(); - ASSERT_TRUE(handle); - EXPECT_TRUE(WebRtcAec_aec_core(NULL) == NULL); - AecCore* aec_core = WebRtcAec_aec_core(handle); - EXPECT_TRUE(aec_core != NULL); - // A simple test to verify that we can set and get a value from the lower - // level |aec_core| handle. - int delay = 111; - WebRtcAec_SetSystemDelay(aec_core, delay); - EXPECT_EQ(delay, WebRtcAec_system_delay(aec_core)); - WebRtcAec_Free(handle); -} - -} // namespace webrtc diff --git a/modules/audio_processing/aec/system_delay_unittest.cc b/modules/audio_processing/aec/system_delay_unittest.cc deleted file mode 100644 index 9c57e8b84e..0000000000 --- a/modules/audio_processing/aec/system_delay_unittest.cc +++ /dev/null @@ -1,587 +0,0 @@ -/* - * 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/aec/aec_core.h" -#include "modules/audio_processing/aec/echo_cancellation.h" -#include "rtc_base/numerics/safe_conversions.h" -#include "test/gtest.h" -namespace webrtc { -namespace { - -class SystemDelayTest : public ::testing::Test { - protected: - SystemDelayTest(); - void SetUp() override; - void TearDown() override; - - // Initialization of AEC handle with respect to |sample_rate_hz|. Since the - // device sample rate is unimportant we set that value to 48000 Hz. - void Init(int sample_rate_hz); - - // Makes one render call and one capture call in that specific order. - void RenderAndCapture(int device_buffer_ms); - - // Fills up the far-end buffer with respect to the default device buffer size. - size_t BufferFillUp(); - - // Runs and verifies the behavior in a stable startup procedure. - void RunStableStartup(); - - // Maps buffer size in ms into samples, taking the unprocessed frame into - // account. - int MapBufferSizeToSamples(int size_in_ms, bool extended_filter); - - void* handle_; - Aec* self_; - size_t samples_per_frame_; - // Dummy input/output speech data. - static const int kSamplesPerChunk = 160; - float far_[kSamplesPerChunk]; - float near_[kSamplesPerChunk]; - float out_[kSamplesPerChunk]; - const float* near_ptr_; - float* out_ptr_; -}; - -SystemDelayTest::SystemDelayTest() - : handle_(NULL), self_(NULL), samples_per_frame_(0) { - // Dummy input data are set with more or less arbitrary non-zero values. - for (int i = 0; i < kSamplesPerChunk; i++) { - far_[i] = 257.0; - near_[i] = 514.0; - } - memset(out_, 0, sizeof(out_)); - near_ptr_ = near_; - out_ptr_ = out_; -} - -void SystemDelayTest::SetUp() { - handle_ = WebRtcAec_Create(); - ASSERT_TRUE(handle_); - self_ = reinterpret_cast(handle_); -} - -void SystemDelayTest::TearDown() { - // Free AEC - WebRtcAec_Free(handle_); - handle_ = NULL; -} - -// In SWB mode nothing is added to the buffer handling with respect to -// functionality compared to WB. We therefore only verify behavior in NB and WB. -static const int kSampleRateHz[] = {8000, 16000}; -static const size_t kNumSampleRates = - sizeof(kSampleRateHz) / sizeof(*kSampleRateHz); - -// Default audio device buffer size used. -static const int kDeviceBufMs = 100; - -// Requirement for a stable device convergence time in ms. Should converge in -// less than |kStableConvergenceMs|. -static const int kStableConvergenceMs = 100; - -// Maximum convergence time in ms. This means that we should leave the startup -// phase after |kMaxConvergenceMs| independent of device buffer stability -// conditions. -static const int kMaxConvergenceMs = 500; - -void SystemDelayTest::Init(int sample_rate_hz) { - // Initialize AEC - EXPECT_EQ(0, WebRtcAec_Init(handle_, sample_rate_hz, 48000)); - EXPECT_EQ(0, WebRtcAec_system_delay(self_->aec)); - - // One frame equals 10 ms of data. - samples_per_frame_ = static_cast(sample_rate_hz / 100); -} - -void SystemDelayTest::RenderAndCapture(int device_buffer_ms) { - EXPECT_EQ(0, WebRtcAec_BufferFarend(handle_, far_, samples_per_frame_)); - EXPECT_EQ(0, WebRtcAec_Process(handle_, &near_ptr_, 1, &out_ptr_, - samples_per_frame_, device_buffer_ms, 0)); -} - -size_t SystemDelayTest::BufferFillUp() { - // To make sure we have a full buffer when we verify stability we first fill - // up the far-end buffer with the same amount as we will report in through - // Process(). - size_t buffer_size = 0; - for (int i = 0; i < kDeviceBufMs / 10; i++) { - EXPECT_EQ(0, WebRtcAec_BufferFarend(handle_, far_, samples_per_frame_)); - buffer_size += samples_per_frame_; - EXPECT_EQ(static_cast(buffer_size), - WebRtcAec_system_delay(self_->aec)); - } - return buffer_size; -} - -void SystemDelayTest::RunStableStartup() { - // To make sure we have a full buffer when we verify stability we first fill - // up the far-end buffer with the same amount as we will report in through - // Process(). - size_t buffer_size = BufferFillUp(); - - if (WebRtcAec_delay_agnostic_enabled(self_->aec) == 1) { - // In extended_filter mode we set the buffer size after the first processed - // 10 ms chunk. Hence, we don't need to wait for the reported system delay - // values to become stable. - RenderAndCapture(kDeviceBufMs); - buffer_size += samples_per_frame_; - EXPECT_EQ(0, self_->startup_phase); - } else { - // A stable device should be accepted and put in a regular process mode - // within |kStableConvergenceMs|. - int process_time_ms = 0; - for (; process_time_ms < kStableConvergenceMs; process_time_ms += 10) { - RenderAndCapture(kDeviceBufMs); - buffer_size += samples_per_frame_; - if (self_->startup_phase == 0) { - // We have left the startup phase. - break; - } - } - // Verify convergence time. - EXPECT_GT(kStableConvergenceMs, process_time_ms); - } - // Verify that the buffer has been flushed. - EXPECT_GE(static_cast(buffer_size), WebRtcAec_system_delay(self_->aec)); -} - -int SystemDelayTest::MapBufferSizeToSamples(int size_in_ms, - bool extended_filter) { - // If extended_filter is disabled we add an extra 10 ms for the unprocessed - // frame. That is simply how the algorithm is constructed. - return static_cast((size_in_ms + (extended_filter ? 0 : 10)) * - samples_per_frame_ / 10); -} - -// The tests should meet basic requirements and not be adjusted to what is -// actually implemented. If we don't get good code coverage this way we either -// lack in tests or have unnecessary code. -// General requirements: -// 1) If we add far-end data the system delay should be increased with the same -// amount we add. -// 2) If the far-end buffer is full we should flush the oldest data to make room -// for the new. In this case the system delay is unaffected. -// 3) There should exist a startup phase in which the buffer size is to be -// determined. In this phase no cancellation should be performed. -// 4) Under stable conditions (small variations in device buffer sizes) the AEC -// should determine an appropriate local buffer size within -// |kStableConvergenceMs| ms. -// 5) Under unstable conditions the AEC should make a decision within -// |kMaxConvergenceMs| ms. -// 6) If the local buffer runs out of data we should stuff the buffer with older -// frames. -// 7) The system delay should within |kMaxConvergenceMs| ms heal from -// disturbances like drift, data glitches, toggling events and outliers. -// 8) The system delay should never become negative. - -TEST_F(SystemDelayTest, CorrectIncreaseWhenBufferFarend) { - // When we add data to the AEC buffer the internal system delay should be - // incremented with the same amount as the size of data. - // This process should be independent of DA-AEC and extended_filter mode. - for (int extended_filter = 0; extended_filter <= 1; ++extended_filter) { - WebRtcAec_enable_extended_filter(self_->aec, extended_filter); - EXPECT_EQ(extended_filter, WebRtcAec_extended_filter_enabled(self_->aec)); - for (int da_aec = 0; da_aec <= 1; ++da_aec) { - WebRtcAec_enable_delay_agnostic(self_->aec, da_aec); - EXPECT_EQ(da_aec, WebRtcAec_delay_agnostic_enabled(self_->aec)); - for (size_t i = 0; i < kNumSampleRates; i++) { - Init(kSampleRateHz[i]); - // Loop through a couple of calls to make sure the system delay - // increments correctly. - for (int j = 1; j <= 5; j++) { - EXPECT_EQ(0, - WebRtcAec_BufferFarend(handle_, far_, samples_per_frame_)); - EXPECT_EQ(static_cast(j * samples_per_frame_), - WebRtcAec_system_delay(self_->aec)); - } - } - } - } -} - -// TODO(bjornv): Add a test to verify behavior if the far-end buffer is full -// when adding new data. - -TEST_F(SystemDelayTest, CorrectDelayAfterStableStartup) { - // We run the system in a stable startup. After that we verify that the system - // delay meets the requirements. - // This process should be independent of DA-AEC and extended_filter mode. - for (int extended_filter = 0; extended_filter <= 1; ++extended_filter) { - WebRtcAec_enable_extended_filter(self_->aec, extended_filter); - EXPECT_EQ(extended_filter, WebRtcAec_extended_filter_enabled(self_->aec)); - for (int da_aec = 0; da_aec <= 1; ++da_aec) { - WebRtcAec_enable_delay_agnostic(self_->aec, da_aec); - EXPECT_EQ(da_aec, WebRtcAec_delay_agnostic_enabled(self_->aec)); - for (size_t i = 0; i < kNumSampleRates; i++) { - Init(kSampleRateHz[i]); - RunStableStartup(); - - // Verify system delay with respect to requirements, i.e., the - // |system_delay| is in the interval [75%, 100%] of what's reported on - // the average. - // In extended_filter mode we target 50% and measure after one processed - // 10 ms chunk. - int average_reported_delay = - static_cast(kDeviceBufMs * samples_per_frame_ / 10); - EXPECT_GE(average_reported_delay, WebRtcAec_system_delay(self_->aec)); - int lower_bound = WebRtcAec_extended_filter_enabled(self_->aec) - ? (average_reported_delay / 2 - - rtc::checked_cast(samples_per_frame_)) - : average_reported_delay * 3 / 4; - EXPECT_LE(lower_bound, WebRtcAec_system_delay(self_->aec)); - } - } - } -} - -TEST_F(SystemDelayTest, CorrectDelayAfterUnstableStartup) { - // This test does not apply in extended_filter mode, since we only use the - // the first 10 ms chunk to determine a reasonable buffer size. Neither does - // it apply if DA-AEC is on because that overrides the startup procedure. - WebRtcAec_enable_extended_filter(self_->aec, 0); - EXPECT_EQ(0, WebRtcAec_extended_filter_enabled(self_->aec)); - WebRtcAec_enable_delay_agnostic(self_->aec, 0); - EXPECT_EQ(0, WebRtcAec_delay_agnostic_enabled(self_->aec)); - - // In an unstable system we would start processing after |kMaxConvergenceMs|. - // On the last frame the AEC buffer is adjusted to 60% of the last reported - // device buffer size. - // We construct an unstable system by altering the device buffer size between - // two values |kDeviceBufMs| +- 25 ms. - for (size_t i = 0; i < kNumSampleRates; i++) { - Init(kSampleRateHz[i]); - - // To make sure we have a full buffer when we verify stability we first fill - // up the far-end buffer with the same amount as we will report in on the - // average through Process(). - size_t buffer_size = BufferFillUp(); - - int buffer_offset_ms = 25; - int reported_delay_ms = 0; - int process_time_ms = 0; - for (; process_time_ms <= kMaxConvergenceMs; process_time_ms += 10) { - reported_delay_ms = kDeviceBufMs + buffer_offset_ms; - RenderAndCapture(reported_delay_ms); - buffer_size += samples_per_frame_; - buffer_offset_ms = -buffer_offset_ms; - if (self_->startup_phase == 0) { - // We have left the startup phase. - break; - } - } - // Verify convergence time. - EXPECT_GE(kMaxConvergenceMs, process_time_ms); - // Verify that the buffer has been flushed. - EXPECT_GE(static_cast(buffer_size), - WebRtcAec_system_delay(self_->aec)); - - // Verify system delay with respect to requirements, i.e., the - // |system_delay| is in the interval [60%, 100%] of what's last reported. - EXPECT_GE(static_cast(reported_delay_ms * samples_per_frame_ / 10), - WebRtcAec_system_delay(self_->aec)); - EXPECT_LE( - static_cast(reported_delay_ms * samples_per_frame_ / 10 * 3 / 5), - WebRtcAec_system_delay(self_->aec)); - } -} - -TEST_F(SystemDelayTest, CorrectDelayAfterStableBufferBuildUp) { - // This test does not apply in extended_filter mode, since we only use the - // the first 10 ms chunk to determine a reasonable buffer size. Neither does - // it apply if DA-AEC is on because that overrides the startup procedure. - WebRtcAec_enable_extended_filter(self_->aec, 0); - EXPECT_EQ(0, WebRtcAec_extended_filter_enabled(self_->aec)); - WebRtcAec_enable_delay_agnostic(self_->aec, 0); - EXPECT_EQ(0, WebRtcAec_delay_agnostic_enabled(self_->aec)); - - // In this test we start by establishing the device buffer size during stable - // conditions, but with an empty internal far-end buffer. Once that is done we - // verify that the system delay is increased correctly until we have reach an - // internal buffer size of 75% of what's been reported. - for (size_t i = 0; i < kNumSampleRates; i++) { - Init(kSampleRateHz[i]); - - // We assume that running |kStableConvergenceMs| calls will put the - // algorithm in a state where the device buffer size has been determined. We - // can make that assumption since we have a separate stability test. - int process_time_ms = 0; - for (; process_time_ms < kStableConvergenceMs; process_time_ms += 10) { - EXPECT_EQ(0, WebRtcAec_Process(handle_, &near_ptr_, 1, &out_ptr_, - samples_per_frame_, kDeviceBufMs, 0)); - } - // Verify that a buffer size has been established. - EXPECT_EQ(0, self_->checkBuffSize); - - // We now have established the required buffer size. Let us verify that we - // fill up before leaving the startup phase for normal processing. - size_t buffer_size = 0; - size_t target_buffer_size = kDeviceBufMs * samples_per_frame_ / 10 * 3 / 4; - process_time_ms = 0; - for (; process_time_ms <= kMaxConvergenceMs; process_time_ms += 10) { - RenderAndCapture(kDeviceBufMs); - buffer_size += samples_per_frame_; - if (self_->startup_phase == 0) { - // We have left the startup phase. - break; - } - } - // Verify convergence time. - EXPECT_GT(kMaxConvergenceMs, process_time_ms); - // Verify that the buffer has reached the desired size. - EXPECT_LE(static_cast(target_buffer_size), - WebRtcAec_system_delay(self_->aec)); - - // Verify normal behavior (system delay is kept constant) after startup by - // running a couple of calls to BufferFarend() and Process(). - for (int j = 0; j < 6; j++) { - int system_delay_before_calls = WebRtcAec_system_delay(self_->aec); - RenderAndCapture(kDeviceBufMs); - EXPECT_EQ(system_delay_before_calls, WebRtcAec_system_delay(self_->aec)); - } - } -} - -TEST_F(SystemDelayTest, CorrectDelayWhenBufferUnderrun) { - // Here we test a buffer under run scenario. If we keep on calling - // WebRtcAec_Process() we will finally run out of data, but should - // automatically stuff the buffer. We verify this behavior by checking if the - // system delay goes negative. - // This process should be independent of DA-AEC and extended_filter mode. - for (int extended_filter = 0; extended_filter <= 1; ++extended_filter) { - WebRtcAec_enable_extended_filter(self_->aec, extended_filter); - EXPECT_EQ(extended_filter, WebRtcAec_extended_filter_enabled(self_->aec)); - for (int da_aec = 0; da_aec <= 1; ++da_aec) { - WebRtcAec_enable_delay_agnostic(self_->aec, da_aec); - EXPECT_EQ(da_aec, WebRtcAec_delay_agnostic_enabled(self_->aec)); - for (size_t i = 0; i < kNumSampleRates; i++) { - Init(kSampleRateHz[i]); - RunStableStartup(); - - // The AEC has now left the Startup phase. We now have at most - // |kStableConvergenceMs| in the buffer. Keep on calling Process() until - // we run out of data and verify that the system delay is non-negative. - for (int j = 0; j <= kStableConvergenceMs; j += 10) { - EXPECT_EQ(0, WebRtcAec_Process(handle_, &near_ptr_, 1, &out_ptr_, - samples_per_frame_, kDeviceBufMs, 0)); - EXPECT_LE(0, WebRtcAec_system_delay(self_->aec)); - } - } - } - } -} - -TEST_F(SystemDelayTest, CorrectDelayDuringDrift) { - // This drift test should verify that the system delay is never exceeding the - // device buffer. The drift is simulated by decreasing the reported device - // buffer size by 1 ms every 100 ms. If the device buffer size goes below 30 - // ms we jump (add) 10 ms to give a repeated pattern. - - // This process should be independent of DA-AEC and extended_filter mode. - for (int extended_filter = 0; extended_filter <= 1; ++extended_filter) { - WebRtcAec_enable_extended_filter(self_->aec, extended_filter); - EXPECT_EQ(extended_filter, WebRtcAec_extended_filter_enabled(self_->aec)); - for (int da_aec = 0; da_aec <= 1; ++da_aec) { - WebRtcAec_enable_delay_agnostic(self_->aec, da_aec); - EXPECT_EQ(da_aec, WebRtcAec_delay_agnostic_enabled(self_->aec)); - for (size_t i = 0; i < kNumSampleRates; i++) { - Init(kSampleRateHz[i]); - RunStableStartup(); - - // We have left the startup phase and proceed with normal processing. - int jump = 0; - for (int j = 0; j < 1000; j++) { - // Drift = -1 ms per 100 ms of data. - int device_buf_ms = kDeviceBufMs - (j / 10) + jump; - int device_buf = - MapBufferSizeToSamples(device_buf_ms, extended_filter == 1); - - if (device_buf_ms < 30) { - // Add 10 ms data, taking affect next frame. - jump += 10; - } - RenderAndCapture(device_buf_ms); - - // Verify that the system delay does not exceed the device buffer. - EXPECT_GE(device_buf, WebRtcAec_system_delay(self_->aec)); - - // Verify that the system delay is non-negative. - EXPECT_LE(0, WebRtcAec_system_delay(self_->aec)); - } - } - } - } -} - -TEST_F(SystemDelayTest, ShouldRecoverAfterGlitch) { - // This glitch test should verify that the system delay recovers if there is - // a glitch in data. The data glitch is constructed as 200 ms of buffering - // after which the stable procedure continues. The glitch is never reported by - // the device. - // The system is said to be in a non-causal state if the difference between - // the device buffer and system delay is less than a block (64 samples). - - // This process should be independent of DA-AEC and extended_filter mode. - for (int extended_filter = 0; extended_filter <= 1; ++extended_filter) { - WebRtcAec_enable_extended_filter(self_->aec, extended_filter); - EXPECT_EQ(extended_filter, WebRtcAec_extended_filter_enabled(self_->aec)); - for (int da_aec = 0; da_aec <= 1; ++da_aec) { - WebRtcAec_enable_delay_agnostic(self_->aec, da_aec); - EXPECT_EQ(da_aec, WebRtcAec_delay_agnostic_enabled(self_->aec)); - for (size_t i = 0; i < kNumSampleRates; i++) { - Init(kSampleRateHz[i]); - RunStableStartup(); - int device_buf = - MapBufferSizeToSamples(kDeviceBufMs, extended_filter == 1); - // Glitch state. - for (int j = 0; j < 20; j++) { - EXPECT_EQ(0, - WebRtcAec_BufferFarend(handle_, far_, samples_per_frame_)); - // No need to verify system delay, since that is done in a separate - // test. - } - // Verify that we are in a non-causal state, i.e., - // |system_delay| > |device_buf|. - EXPECT_LT(device_buf, WebRtcAec_system_delay(self_->aec)); - - // Recover state. Should recover at least 4 ms of data per 10 ms, hence - // a glitch of 200 ms will take at most 200 * 10 / 4 = 500 ms to recover - // from. - bool non_causal = true; // We are currently in a non-causal state. - for (int j = 0; j < 50; j++) { - int system_delay_before = WebRtcAec_system_delay(self_->aec); - RenderAndCapture(kDeviceBufMs); - int system_delay_after = WebRtcAec_system_delay(self_->aec); - // We have recovered if - // |device_buf| - |system_delay_after| >= PART_LEN (1 block). - // During recovery, |system_delay_after| < |system_delay_before|, - // otherwise they are equal. - if (non_causal) { - EXPECT_LT(system_delay_after, system_delay_before); - if (device_buf - system_delay_after >= PART_LEN) { - non_causal = false; - } - } else { - EXPECT_EQ(system_delay_before, system_delay_after); - } - // Verify that the system delay is non-negative. - EXPECT_LE(0, WebRtcAec_system_delay(self_->aec)); - } - // Check that we have recovered. - EXPECT_FALSE(non_causal); - } - } - } -} - -TEST_F(SystemDelayTest, UnaffectedWhenSpuriousDeviceBufferValues) { - // This test does not apply in extended_filter mode, since we only use the - // the first 10 ms chunk to determine a reasonable buffer size. - const int extended_filter = 0; - WebRtcAec_enable_extended_filter(self_->aec, extended_filter); - EXPECT_EQ(extended_filter, WebRtcAec_extended_filter_enabled(self_->aec)); - - // Should be DA-AEC independent. - for (int da_aec = 0; da_aec <= 1; ++da_aec) { - WebRtcAec_enable_delay_agnostic(self_->aec, da_aec); - EXPECT_EQ(da_aec, WebRtcAec_delay_agnostic_enabled(self_->aec)); - // This spurious device buffer data test aims at verifying that the system - // delay is unaffected by large outliers. - // The system is said to be in a non-causal state if the difference between - // the device buffer and system delay is less than a block (64 samples). - for (size_t i = 0; i < kNumSampleRates; i++) { - Init(kSampleRateHz[i]); - RunStableStartup(); - int device_buf = - MapBufferSizeToSamples(kDeviceBufMs, extended_filter == 1); - - // Normal state. We are currently not in a non-causal state. - bool non_causal = false; - - // Run 1 s and replace device buffer size with 500 ms every 100 ms. - for (int j = 0; j < 100; j++) { - int system_delay_before_calls = WebRtcAec_system_delay(self_->aec); - int device_buf_ms = j % 10 == 0 ? 500 : kDeviceBufMs; - RenderAndCapture(device_buf_ms); - - // Check for non-causality. - if (device_buf - WebRtcAec_system_delay(self_->aec) < PART_LEN) { - non_causal = true; - } - EXPECT_FALSE(non_causal); - EXPECT_EQ(system_delay_before_calls, - WebRtcAec_system_delay(self_->aec)); - - // Verify that the system delay is non-negative. - EXPECT_LE(0, WebRtcAec_system_delay(self_->aec)); - } - } - } -} - -TEST_F(SystemDelayTest, CorrectImpactWhenTogglingDeviceBufferValues) { - // This test aims at verifying that the system delay is "unaffected" by - // toggling values reported by the device. - // The test is constructed such that every other device buffer value is zero - // and then 2 * |kDeviceBufMs|, hence the size is constant on the average. The - // zero values will force us into a non-causal state and thereby lowering the - // system delay until we basically run out of data. Once that happens the - // buffer will be stuffed. - // TODO(bjornv): This test will have a better impact if we verified that the - // delay estimate goes up when the system delay goes down to meet the average - // device buffer size. - - // This test does not apply if DA-AEC is enabled and extended_filter mode - // disabled. - for (int extended_filter = 0; extended_filter <= 1; ++extended_filter) { - WebRtcAec_enable_extended_filter(self_->aec, extended_filter); - EXPECT_EQ(extended_filter, WebRtcAec_extended_filter_enabled(self_->aec)); - for (int da_aec = 0; da_aec <= 1; ++da_aec) { - WebRtcAec_enable_delay_agnostic(self_->aec, da_aec); - EXPECT_EQ(da_aec, WebRtcAec_delay_agnostic_enabled(self_->aec)); - if (extended_filter == 0 && da_aec == 1) { - continue; - } - for (size_t i = 0; i < kNumSampleRates; i++) { - Init(kSampleRateHz[i]); - RunStableStartup(); - const int device_buf = - MapBufferSizeToSamples(kDeviceBufMs, extended_filter == 1); - - // Normal state. We are currently not in a non-causal state. - bool non_causal = false; - - // Loop through 100 frames (both render and capture), which equals 1 s - // of data. Every odd frame we set the device buffer size to - // 2 * |kDeviceBufMs| and even frames we set the device buffer size to - // zero. - for (int j = 0; j < 100; j++) { - int system_delay_before_calls = WebRtcAec_system_delay(self_->aec); - int device_buf_ms = 2 * (j % 2) * kDeviceBufMs; - RenderAndCapture(device_buf_ms); - - // Check for non-causality, compared with the average device buffer - // size. - non_causal |= (device_buf - WebRtcAec_system_delay(self_->aec) < 64); - EXPECT_GE(system_delay_before_calls, - WebRtcAec_system_delay(self_->aec)); - - // Verify that the system delay is non-negative. - EXPECT_LE(0, WebRtcAec_system_delay(self_->aec)); - } - // Verify we are not in a non-causal state. - EXPECT_FALSE(non_causal); - } - } - } -} - -} // namespace -} // namespace webrtc diff --git a/modules/audio_processing/audio_processing_impl.cc b/modules/audio_processing/audio_processing_impl.cc index 97a8379452..4d9cdb4d65 100644 --- a/modules/audio_processing/audio_processing_impl.cc +++ b/modules/audio_processing/audio_processing_impl.cc @@ -155,7 +155,6 @@ AudioProcessingImpl::SubmoduleStates::SubmoduleStates( bool AudioProcessingImpl::SubmoduleStates::Update( bool high_pass_filter_enabled, - bool echo_canceller_enabled, bool mobile_echo_controller_enabled, bool residual_echo_detector_enabled, bool noise_suppressor_enabled, @@ -167,7 +166,6 @@ bool AudioProcessingImpl::SubmoduleStates::Update( bool transient_suppressor_enabled) { bool changed = false; changed |= (high_pass_filter_enabled != high_pass_filter_enabled_); - changed |= (echo_canceller_enabled != echo_canceller_enabled_); changed |= (mobile_echo_controller_enabled != mobile_echo_controller_enabled_); changed |= @@ -182,7 +180,6 @@ bool AudioProcessingImpl::SubmoduleStates::Update( changed |= (transient_suppressor_enabled != transient_suppressor_enabled_); if (changed) { high_pass_filter_enabled_ = high_pass_filter_enabled; - echo_canceller_enabled_ = echo_canceller_enabled; mobile_echo_controller_enabled_ = mobile_echo_controller_enabled; residual_echo_detector_enabled_ = residual_echo_detector_enabled; noise_suppressor_enabled_ = noise_suppressor_enabled; @@ -212,9 +209,8 @@ bool AudioProcessingImpl::SubmoduleStates::CaptureMultiBandProcessingPresent() bool AudioProcessingImpl::SubmoduleStates::CaptureMultiBandProcessingActive( bool ec_processing_active) const { - return high_pass_filter_enabled_ || echo_canceller_enabled_ || - mobile_echo_controller_enabled_ || noise_suppressor_enabled_ || - adaptive_gain_controller_enabled_ || + return high_pass_filter_enabled_ || mobile_echo_controller_enabled_ || + noise_suppressor_enabled_ || adaptive_gain_controller_enabled_ || (echo_controller_enabled_ && ec_processing_active); } @@ -230,9 +226,8 @@ bool AudioProcessingImpl::SubmoduleStates::CaptureAnalyzerActive() const { bool AudioProcessingImpl::SubmoduleStates::RenderMultiBandSubModulesActive() const { - return RenderMultiBandProcessingActive() || echo_canceller_enabled_ || - mobile_echo_controller_enabled_ || adaptive_gain_controller_enabled_ || - echo_controller_enabled_; + return RenderMultiBandProcessingActive() || mobile_echo_controller_enabled_ || + adaptive_gain_controller_enabled_ || echo_controller_enabled_; } bool AudioProcessingImpl::SubmoduleStates::RenderFullBandProcessingActive() @@ -246,8 +241,8 @@ bool AudioProcessingImpl::SubmoduleStates::RenderMultiBandProcessingActive() } bool AudioProcessingImpl::SubmoduleStates::HighPassFilteringRequired() const { - return high_pass_filter_enabled_ || echo_canceller_enabled_ || - mobile_echo_controller_enabled_ || noise_suppressor_enabled_; + return high_pass_filter_enabled_ || mobile_echo_controller_enabled_ || + noise_suppressor_enabled_; } AudioProcessingBuilder::AudioProcessingBuilder() = default; @@ -638,12 +633,7 @@ void AudioProcessingImpl::ApplyConfig(const AudioProcessing::Config& config) { const bool aec_config_changed = config_.echo_canceller.enabled != config.echo_canceller.enabled || - config_.echo_canceller.use_legacy_aec != - config.echo_canceller.use_legacy_aec || - config_.echo_canceller.mobile_mode != config.echo_canceller.mobile_mode || - (config_.echo_canceller.enabled && config.echo_canceller.use_legacy_aec && - config_.echo_canceller.legacy_moderate_suppression_level != - config.echo_canceller.legacy_moderate_suppression_level); + config_.echo_canceller.mobile_mode != config.echo_canceller.mobile_mode; const bool agc1_config_changed = config_.gain_controller1.enabled != config.gain_controller1.enabled || @@ -668,6 +658,9 @@ void AudioProcessingImpl::ApplyConfig(const AudioProcessing::Config& config) { config_ = config; + // Ensure that this deprecated setting is not used by mistake. + RTC_DCHECK(!config_.echo_canceller.use_legacy_aec); + if (aec_config_changed) { InitializeEchoController(); } @@ -737,13 +730,6 @@ void AudioProcessingImpl::SetExtraOptions(const webrtc::Config& config) { rtc::CritScope cs_render(&crit_render_); rtc::CritScope cs_capture(&crit_capture_); - capture_nonlocked_.use_aec2_extended_filter = - config.Get().enabled; - capture_nonlocked_.use_aec2_delay_agnostic = - config.Get().enabled; - capture_nonlocked_.use_aec2_refined_adaptive_filter = - config.Get().enabled; - if (capture_.transient_suppressor_enabled != config.Get().enabled) { capture_.transient_suppressor_enabled = @@ -997,23 +983,6 @@ void AudioProcessingImpl::HandleRenderRuntimeSettings() { void AudioProcessingImpl::QueueBandedRenderAudio(AudioBuffer* audio) { RTC_DCHECK_GE(160, audio->num_frames_per_band()); - // Insert the samples into the queue. - if (submodules_.echo_cancellation) { - RTC_DCHECK(aec_render_signal_queue_); - EchoCancellationImpl::PackRenderAudioBuffer(audio, num_output_channels(), - num_reverse_channels(), - &aec_render_queue_buffer_); - - if (!aec_render_signal_queue_->Insert(&aec_render_queue_buffer_)) { - // The data queue is full and needs to be emptied. - EmptyQueuedRenderAudio(); - - // Retry the insert (should always work). - bool result = aec_render_signal_queue_->Insert(&aec_render_queue_buffer_); - RTC_DCHECK(result); - } - } - if (submodules_.echo_control_mobile) { EchoControlMobileImpl::PackRenderAudioBuffer(audio, num_output_channels(), num_reverse_channels(), @@ -1110,14 +1079,6 @@ void AudioProcessingImpl::AllocateRenderQueue() { void AudioProcessingImpl::EmptyQueuedRenderAudio() { rtc::CritScope cs_capture(&crit_capture_); - if (submodules_.echo_cancellation) { - RTC_DCHECK(aec_render_signal_queue_); - while (aec_render_signal_queue_->Remove(&aec_capture_queue_buffer_)) { - submodules_.echo_cancellation->ProcessRenderAudio( - aec_capture_queue_buffer_); - } - } - if (submodules_.echo_control_mobile) { RTC_DCHECK(aecm_render_signal_queue_); while (aecm_render_signal_queue_->Remove(&aecm_capture_queue_buffer_)) { @@ -1236,7 +1197,6 @@ int AudioProcessingImpl::ProcessCaptureStreamLocked() { // TODO(peah): Simplify once the public API Enable functions for these // are moved to APM. RTC_DCHECK_LE(!!submodules_.echo_controller + - !!submodules_.echo_cancellation + !!submodules_.echo_control_mobile, 1); @@ -1350,15 +1310,6 @@ int AudioProcessingImpl::ProcessCaptureStreamLocked() { AudioBuffer* linear_aec_buffer = capture_.linear_aec_output.get(); submodules_.echo_controller->ProcessCapture( capture_buffer, linear_aec_buffer, capture_.echo_path_gain_change); - } else if (submodules_.echo_cancellation) { - // Ensure that the stream delay was set before the call to the - // AEC ProcessCaptureAudio function. - if (!was_stream_delay_set()) { - return AudioProcessing::kStreamParameterNotSetError; - } - - RETURN_ON_ERR(submodules_.echo_cancellation->ProcessCaptureAudio( - capture_buffer, stream_delay_ms())); } if (submodules_.noise_suppressor) { @@ -1387,8 +1338,7 @@ int AudioProcessingImpl::ProcessCaptureStreamLocked() { } // TODO(peah): Add reporting from AEC3 whether there is echo. RETURN_ON_ERR(submodules_.gain_control->ProcessCaptureAudio( - capture_buffer, submodules_.echo_cancellation && - submodules_.echo_cancellation->stream_has_echo())); + capture_buffer, /*stream_has_echo*/ false)); if (submodule_states_.CaptureMultiBandProcessingPresent() && SampleRateSupportsMultiBand( @@ -1754,7 +1704,6 @@ AudioProcessingStats AudioProcessingImpl::GetStatistics( return capture_.stats; } AudioProcessingStats stats = capture_.stats; - EchoCancellationImpl::Metrics metrics; if (submodules_.echo_controller) { auto ec_metrics = submodules_.echo_controller->GetMetrics(); stats.echo_return_loss = ec_metrics.echo_return_loss; @@ -1788,8 +1737,8 @@ AudioProcessing::Config AudioProcessingImpl::GetConfig() const { bool AudioProcessingImpl::UpdateActiveSubmoduleStates() { return submodule_states_.Update( - config_.high_pass_filter.enabled, !!submodules_.echo_cancellation, - !!submodules_.echo_control_mobile, config_.residual_echo_detector.enabled, + config_.high_pass_filter.enabled, !!submodules_.echo_control_mobile, + config_.residual_echo_detector.enabled, !!submodules_.legacy_noise_suppressor || !!submodules_.noise_suppressor, submodules_.gain_control->is_enabled(), config_.gain_controller2.enabled, config_.pre_amplifier.enabled, capture_nonlocked_.echo_controller_enabled, @@ -1831,8 +1780,7 @@ void AudioProcessingImpl::InitializeVoiceDetector() { void AudioProcessingImpl::InitializeEchoController() { bool use_echo_controller = echo_control_factory_ || - (config_.echo_canceller.enabled && !config_.echo_canceller.mobile_mode && - !config_.echo_canceller.use_legacy_aec); + (config_.echo_canceller.enabled && !config_.echo_canceller.mobile_mode); if (use_echo_controller) { // Create and activate the echo controller. @@ -1863,8 +1811,6 @@ void AudioProcessingImpl::InitializeEchoController() { capture_nonlocked_.echo_controller_enabled = true; - submodules_.echo_cancellation.reset(); - aec_render_signal_queue_.reset(); submodules_.echo_control_mobile.reset(); aecm_render_signal_queue_.reset(); return; @@ -1875,8 +1821,6 @@ void AudioProcessingImpl::InitializeEchoController() { capture_.linear_aec_output.reset(); if (!config_.echo_canceller.enabled) { - submodules_.echo_cancellation.reset(); - aec_render_signal_queue_.reset(); submodules_.echo_control_mobile.reset(); aecm_render_signal_queue_.reset(); return; @@ -1905,46 +1849,11 @@ void AudioProcessingImpl::InitializeEchoController() { submodules_.echo_control_mobile->Initialize(proc_split_sample_rate_hz(), num_reverse_channels(), num_output_channels()); - - submodules_.echo_cancellation.reset(); - aec_render_signal_queue_.reset(); return; } submodules_.echo_control_mobile.reset(); aecm_render_signal_queue_.reset(); - - // Create and activate AEC2. - submodules_.echo_cancellation.reset(new EchoCancellationImpl()); - submodules_.echo_cancellation->SetExtraOptions( - capture_nonlocked_.use_aec2_extended_filter, - capture_nonlocked_.use_aec2_delay_agnostic, - capture_nonlocked_.use_aec2_refined_adaptive_filter); - - size_t element_max_size = - std::max(static_cast(1), - kMaxAllowedValuesOfSamplesPerBand * - EchoCancellationImpl::NumCancellersRequired( - num_output_channels(), num_reverse_channels())); - - std::vector template_queue_element(element_max_size); - - aec_render_signal_queue_.reset( - new SwapQueue, RenderQueueItemVerifier>( - kMaxNumFramesToBuffer, template_queue_element, - RenderQueueItemVerifier(element_max_size))); - - aec_render_queue_buffer_.resize(element_max_size); - aec_capture_queue_buffer_.resize(element_max_size); - - submodules_.echo_cancellation->Initialize( - proc_sample_rate_hz(), num_reverse_channels(), num_output_channels(), - num_proc_channels()); - - submodules_.echo_cancellation->set_suppression_level( - config_.echo_canceller.legacy_moderate_suppression_level - ? EchoCancellationImpl::SuppressionLevel::kModerateSuppression - : EchoCancellationImpl::SuppressionLevel::kHighSuppression); } void AudioProcessingImpl::InitializeGainController2() { @@ -2039,10 +1948,6 @@ void AudioProcessingImpl::WriteAecDumpConfigMessage(bool forced) { } std::string experiments_description = ""; - if (submodules_.echo_cancellation) { - experiments_description += - submodules_.echo_cancellation->GetExperimentsDescription(); - } // TODO(peah): Add semicolon-separated concatenations of experiment // descriptions for other submodules. if (constants_.agc_clipped_level_min != kClippedLevelMin) { @@ -2058,19 +1963,9 @@ void AudioProcessingImpl::WriteAecDumpConfigMessage(bool forced) { InternalAPMConfig apm_config; apm_config.aec_enabled = config_.echo_canceller.enabled; - apm_config.aec_delay_agnostic_enabled = - submodules_.echo_cancellation && - submodules_.echo_cancellation->is_delay_agnostic_enabled(); - apm_config.aec_drift_compensation_enabled = - submodules_.echo_cancellation && - submodules_.echo_cancellation->is_drift_compensation_enabled(); - apm_config.aec_extended_filter_enabled = - submodules_.echo_cancellation && - submodules_.echo_cancellation->is_extended_filter_enabled(); - apm_config.aec_suppression_level = - submodules_.echo_cancellation - ? static_cast(submodules_.echo_cancellation->suppression_level()) - : 0; + apm_config.aec_delay_agnostic_enabled = false; + apm_config.aec_extended_filter_enabled = false; + apm_config.aec_suppression_level = 0; apm_config.aecm_enabled = !!submodules_.echo_control_mobile; apm_config.aecm_comfort_noise_enabled = @@ -2151,10 +2046,7 @@ void AudioProcessingImpl::RecordAudioProcessingState() { RTC_DCHECK(aec_dump_); AecDump::AudioProcessingState audio_proc_state; audio_proc_state.delay = capture_nonlocked_.stream_delay_ms; - audio_proc_state.drift = - submodules_.echo_cancellation - ? submodules_.echo_cancellation->stream_drift_samples() - : 0; + audio_proc_state.drift = 0; audio_proc_state.level = recommended_stream_analog_level(); audio_proc_state.keypress = capture_.key_pressed; aec_dump_->AddAudioProcessingState(audio_proc_state); diff --git a/modules/audio_processing/audio_processing_impl.h b/modules/audio_processing/audio_processing_impl.h index e5d0573e12..dcc2fa6f5f 100644 --- a/modules/audio_processing/audio_processing_impl.h +++ b/modules/audio_processing/audio_processing_impl.h @@ -20,7 +20,6 @@ #include "modules/audio_processing/agc/agc_manager_direct.h" #include "modules/audio_processing/agc/gain_control.h" #include "modules/audio_processing/audio_buffer.h" -#include "modules/audio_processing/echo_cancellation_impl.h" #include "modules/audio_processing/echo_control_mobile_impl.h" #include "modules/audio_processing/gain_control_impl.h" #include "modules/audio_processing/gain_controller2.h" @@ -171,7 +170,6 @@ class AudioProcessingImpl : public AudioProcessing { bool capture_analyzer_enabled); // Updates the submodule state and returns true if it has changed. bool Update(bool high_pass_filter_enabled, - bool echo_canceller_enabled, bool mobile_echo_controller_enabled, bool residual_echo_detector_enabled, bool noise_suppressor_enabled, @@ -196,7 +194,6 @@ class AudioProcessingImpl : public AudioProcessing { const bool render_pre_processor_enabled_ = false; const bool capture_analyzer_enabled_ = false; bool high_pass_filter_enabled_ = false; - bool echo_canceller_enabled_ = false; bool mobile_echo_controller_enabled_ = false; bool residual_echo_detector_enabled_ = false; bool noise_suppressor_enabled_ = false; @@ -337,7 +334,6 @@ class AudioProcessingImpl : public AudioProcessing { std::unique_ptr gain_controller2; std::unique_ptr high_pass_filter; rtc::scoped_refptr echo_detector; - std::unique_ptr echo_cancellation; std::unique_ptr echo_controller; std::unique_ptr echo_control_mobile; std::unique_ptr legacy_noise_suppressor; @@ -436,9 +432,6 @@ class AudioProcessingImpl : public AudioProcessing { int split_rate; int stream_delay_ms; bool echo_controller_enabled = false; - bool use_aec2_extended_filter = false; - bool use_aec2_delay_agnostic = false; - bool use_aec2_refined_adaptive_filter = false; } capture_nonlocked_; struct ApmRenderState { @@ -469,8 +462,6 @@ class AudioProcessingImpl : public AudioProcessing { int capture_rms_interval_counter_ RTC_GUARDED_BY(crit_capture_) = 0; // Lock protection not needed. - std::unique_ptr, RenderQueueItemVerifier>> - aec_render_signal_queue_; std::unique_ptr< SwapQueue, RenderQueueItemVerifier>> aecm_render_signal_queue_; diff --git a/modules/audio_processing/audio_processing_impl_locking_unittest.cc b/modules/audio_processing/audio_processing_impl_locking_unittest.cc index 9aa3f7a1fb..d09e979223 100644 --- a/modules/audio_processing/audio_processing_impl_locking_unittest.cc +++ b/modules/audio_processing/audio_processing_impl_locking_unittest.cc @@ -551,17 +551,6 @@ void AudioProcessingImplLockTest::SetUp() { apm_config.voice_detection.enabled = true; apm_config.level_estimation.enabled = true; apm_->ApplyConfig(apm_config); - - Config config; - config.Set( - new ExtendedFilter(test_config_.aec_type == - AecType::BasicWebRtcAecSettingsWithExtentedFilter)); - - config.Set( - new DelayAgnostic(test_config_.aec_type == - AecType::BasicWebRtcAecSettingsWithDelayAgnosticAec)); - - apm_->SetExtraOptions(config); } void AudioProcessingImplLockTest::TearDown() { diff --git a/modules/audio_processing/audio_processing_performance_unittest.cc b/modules/audio_processing/audio_processing_performance_unittest.cc index ebb2480d97..2ed6f174af 100644 --- a/modules/audio_processing/audio_processing_performance_unittest.cc +++ b/modules/audio_processing/audio_processing_performance_unittest.cc @@ -483,12 +483,6 @@ class CallSimulator : public ::testing::TestWithParam { apm->ApplyConfig(apm_config); }; - // Lambda function for adding default desktop APM settings to a config. - auto add_default_desktop_config = [](Config* config) { - config->Set(new ExtendedFilter(true)); - config->Set(new DelayAgnostic(true)); - }; - int num_capture_channels = 1; switch (simulation_config_.simulation_settings) { case SettingsType::kDefaultApmMobile: { @@ -499,7 +493,6 @@ class CallSimulator : public ::testing::TestWithParam { } case SettingsType::kDefaultApmDesktop: { Config config; - add_default_desktop_config(&config); apm_.reset(AudioProcessingBuilder().Create(config)); ASSERT_TRUE(!!apm_); set_default_desktop_apm_runtime_settings(apm_.get()); @@ -514,8 +507,6 @@ class CallSimulator : public ::testing::TestWithParam { } case SettingsType::kDefaultApmDesktopWithoutDelayAgnostic: { Config config; - config.Set(new ExtendedFilter(true)); - config.Set(new DelayAgnostic(false)); apm_.reset(AudioProcessingBuilder().Create(config)); ASSERT_TRUE(!!apm_); set_default_desktop_apm_runtime_settings(apm_.get()); @@ -524,8 +515,6 @@ class CallSimulator : public ::testing::TestWithParam { } case SettingsType::kDefaultApmDesktopWithoutExtendedFilter: { Config config; - config.Set(new ExtendedFilter(false)); - config.Set(new DelayAgnostic(true)); apm_.reset(AudioProcessingBuilder().Create(config)); ASSERT_TRUE(!!apm_); set_default_desktop_apm_runtime_settings(apm_.get()); diff --git a/modules/audio_processing/audio_processing_unittest.cc b/modules/audio_processing/audio_processing_unittest.cc index 8f29a739ed..0fd07bf3f6 100644 --- a/modules/audio_processing/audio_processing_unittest.cc +++ b/modules/audio_processing/audio_processing_unittest.cc @@ -1536,8 +1536,6 @@ TEST_F(ApmTest, Process) { Config config; config.Set(new ExperimentalAgc(false)); - config.Set( - new ExtendedFilter(test->use_aec_extended_filter())); apm_.reset(AudioProcessingBuilder().Create(config)); EnableAllComponents(); diff --git a/modules/audio_processing/echo_cancellation_bit_exact_unittest.cc b/modules/audio_processing/echo_cancellation_bit_exact_unittest.cc deleted file mode 100644 index c8c665e87e..0000000000 --- a/modules/audio_processing/echo_cancellation_bit_exact_unittest.cc +++ /dev/null @@ -1,353 +0,0 @@ -/* - * Copyright (c) 2016 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 - -#include "api/array_view.h" -#include "modules/audio_processing/audio_buffer.h" -#include "modules/audio_processing/echo_cancellation_impl.h" -#include "modules/audio_processing/test/audio_buffer_tools.h" -#include "modules/audio_processing/test/bitexactness_tools.h" -#include "test/gtest.h" - -namespace webrtc { -namespace { - -const int kNumFramesToProcess = 100; - -void SetupComponent(int sample_rate_hz, - EchoCancellationImpl::SuppressionLevel suppression_level, - bool drift_compensation_enabled, - EchoCancellationImpl* echo_canceller) { - echo_canceller->Initialize(sample_rate_hz, 1, 1, 1); - echo_canceller->set_suppression_level(suppression_level); - echo_canceller->enable_drift_compensation(drift_compensation_enabled); - - Config config; - config.Set(new DelayAgnostic(true)); - config.Set(new ExtendedFilter(true)); - echo_canceller->SetExtraOptions(true, true, false); -} - -void ProcessOneFrame(int sample_rate_hz, - int stream_delay_ms, - bool drift_compensation_enabled, - int stream_drift_samples, - AudioBuffer* render_audio_buffer, - AudioBuffer* capture_audio_buffer, - EchoCancellationImpl* echo_canceller) { - if (sample_rate_hz > AudioProcessing::kSampleRate16kHz) { - render_audio_buffer->SplitIntoFrequencyBands(); - capture_audio_buffer->SplitIntoFrequencyBands(); - } - - std::vector render_audio; - EchoCancellationImpl::PackRenderAudioBuffer( - render_audio_buffer, 1, render_audio_buffer->num_channels(), - &render_audio); - echo_canceller->ProcessRenderAudio(render_audio); - - if (drift_compensation_enabled) { - echo_canceller->set_stream_drift_samples(stream_drift_samples); - } - - echo_canceller->ProcessCaptureAudio(capture_audio_buffer, stream_delay_ms); - - if (sample_rate_hz > AudioProcessing::kSampleRate16kHz) { - capture_audio_buffer->MergeFrequencyBands(); - } -} - -void RunBitexactnessTest( - int sample_rate_hz, - size_t num_channels, - int stream_delay_ms, - bool drift_compensation_enabled, - int stream_drift_samples, - EchoCancellationImpl::SuppressionLevel suppression_level, - bool stream_has_echo_reference, - const rtc::ArrayView& output_reference) { - EchoCancellationImpl echo_canceller; - SetupComponent(sample_rate_hz, suppression_level, drift_compensation_enabled, - &echo_canceller); - - const int samples_per_channel = rtc::CheckedDivExact(sample_rate_hz, 100); - const StreamConfig render_config(sample_rate_hz, num_channels, false); - AudioBuffer render_buffer( - render_config.sample_rate_hz(), render_config.num_channels(), - render_config.sample_rate_hz(), 1, render_config.sample_rate_hz(), 1); - test::InputAudioFile render_file( - test::GetApmRenderTestVectorFileName(sample_rate_hz)); - std::vector render_input(samples_per_channel * num_channels); - - const StreamConfig capture_config(sample_rate_hz, num_channels, false); - AudioBuffer capture_buffer( - capture_config.sample_rate_hz(), capture_config.num_channels(), - capture_config.sample_rate_hz(), 1, capture_config.sample_rate_hz(), 1); - test::InputAudioFile capture_file( - test::GetApmCaptureTestVectorFileName(sample_rate_hz)); - std::vector capture_input(samples_per_channel * num_channels); - - for (int frame_no = 0; frame_no < kNumFramesToProcess; ++frame_no) { - ReadFloatSamplesFromStereoFile(samples_per_channel, num_channels, - &render_file, render_input); - ReadFloatSamplesFromStereoFile(samples_per_channel, num_channels, - &capture_file, capture_input); - - test::CopyVectorToAudioBuffer(render_config, render_input, &render_buffer); - test::CopyVectorToAudioBuffer(capture_config, capture_input, - &capture_buffer); - - ProcessOneFrame(sample_rate_hz, stream_delay_ms, drift_compensation_enabled, - stream_drift_samples, &render_buffer, &capture_buffer, - &echo_canceller); - } - - // Extract and verify the test results. - std::vector capture_output; - test::ExtractVectorFromAudioBuffer(capture_config, &capture_buffer, - &capture_output); - - EXPECT_EQ(stream_has_echo_reference, echo_canceller.stream_has_echo()); - - // Compare the output with the reference. Only the first values of the output - // from last frame processed are compared in order not having to specify all - // preceeding frames as testvectors. As the algorithm being tested has a - // memory, testing only the last frame implicitly also tests the preceeding - // frames. - const float kElementErrorBound = 1.0f / 32768.0f; - EXPECT_TRUE(test::VerifyDeinterleavedArray( - capture_config.num_frames(), capture_config.num_channels(), - output_reference, capture_output, kElementErrorBound)); -} - -const bool kStreamHasEchoReference = true; - -} // namespace - -// TODO(peah): Activate all these tests for ARM and ARM64 once the issue on the -// Chromium ARM and ARM64 boths have been identified. This is tracked in the -// issue https://bugs.chromium.org/p/webrtc/issues/detail?id=5711. - -#if !(defined(WEBRTC_ARCH_ARM64) || defined(WEBRTC_ARCH_ARM) || \ - defined(WEBRTC_ANDROID)) -TEST(EchoCancellationBitExactnessTest, - Mono8kHz_HighLevel_NoDrift_StreamDelay0) { -#else -TEST(EchoCancellationBitExactnessTest, - DISABLED_Mono8kHz_HighLevel_NoDrift_StreamDelay0) { -#endif - const float kOutputReference[] = {-0.000646f, -0.001525f, 0.002688f}; - RunBitexactnessTest(8000, 1, 0, false, 0, - EchoCancellationImpl::SuppressionLevel::kHighSuppression, - kStreamHasEchoReference, kOutputReference); -} - -#if !(defined(WEBRTC_ARCH_ARM64) || defined(WEBRTC_ARCH_ARM) || \ - defined(WEBRTC_ANDROID)) -TEST(EchoCancellationBitExactnessTest, - Mono16kHz_HighLevel_NoDrift_StreamDelay0) { -#else -TEST(EchoCancellationBitExactnessTest, - DISABLED_Mono16kHz_HighLevel_NoDrift_StreamDelay0) { -#endif - const float kOutputReference[] = {0.000055f, 0.000421f, 0.001149f}; - RunBitexactnessTest(16000, 1, 0, false, 0, - EchoCancellationImpl::SuppressionLevel::kHighSuppression, - kStreamHasEchoReference, kOutputReference); -} - -#if !(defined(WEBRTC_ARCH_ARM64) || defined(WEBRTC_ARCH_ARM) || \ - defined(WEBRTC_ANDROID)) -TEST(EchoCancellationBitExactnessTest, - Mono32kHz_HighLevel_NoDrift_StreamDelay0) { -#else -TEST(EchoCancellationBitExactnessTest, - DISABLED_Mono32kHz_HighLevel_NoDrift_StreamDelay0) { -#endif - const float kOutputReference[] = {-0.000671f, 0.000061f, -0.000031f}; - RunBitexactnessTest(32000, 1, 0, false, 0, - EchoCancellationImpl::SuppressionLevel::kHighSuppression, - kStreamHasEchoReference, kOutputReference); -} - -#if !(defined(WEBRTC_ARCH_ARM64) || defined(WEBRTC_ARCH_ARM) || \ - defined(WEBRTC_ANDROID)) -TEST(EchoCancellationBitExactnessTest, - Mono48kHz_HighLevel_NoDrift_StreamDelay0) { -#else -TEST(EchoCancellationBitExactnessTest, - DISABLED_Mono48kHz_HighLevel_NoDrift_StreamDelay0) { -#endif - const float kOutputReference[] = {-0.001403f, -0.001411f, -0.000755f}; - RunBitexactnessTest(48000, 1, 0, false, 0, - EchoCancellationImpl::SuppressionLevel::kHighSuppression, - kStreamHasEchoReference, kOutputReference); -} - -#if !(defined(WEBRTC_ARCH_ARM64) || defined(WEBRTC_ARCH_ARM) || \ - defined(WEBRTC_ANDROID)) -TEST(EchoCancellationBitExactnessTest, - Mono16kHz_LowLevel_NoDrift_StreamDelay0) { -#else -TEST(EchoCancellationBitExactnessTest, - DISABLED_Mono16kHz_LowLevel_NoDrift_StreamDelay0) { -#endif -#if defined(WEBRTC_MAC) - const float kOutputReference[] = {-0.000145f, 0.000179f, 0.000917f}; -#else - const float kOutputReference[] = {-0.000009f, 0.000363f, 0.001094f}; -#endif - RunBitexactnessTest(16000, 1, 0, false, 0, - EchoCancellationImpl::SuppressionLevel::kLowSuppression, - kStreamHasEchoReference, kOutputReference); -} - -#if !(defined(WEBRTC_ARCH_ARM64) || defined(WEBRTC_ARCH_ARM) || \ - defined(WEBRTC_ANDROID)) -TEST(EchoCancellationBitExactnessTest, - Mono16kHz_ModerateLevel_NoDrift_StreamDelay0) { -#else -TEST(EchoCancellationBitExactnessTest, - DISABLED_Mono16kHz_ModerateLevel_NoDrift_StreamDelay0) { -#endif - const float kOutputReference[] = {0.000055f, 0.000421f, 0.001149f}; - RunBitexactnessTest( - 16000, 1, 0, false, 0, - EchoCancellationImpl::SuppressionLevel::kModerateSuppression, - kStreamHasEchoReference, kOutputReference); -} - -#if !(defined(WEBRTC_ARCH_ARM64) || defined(WEBRTC_ARCH_ARM) || \ - defined(WEBRTC_ANDROID)) -TEST(EchoCancellationBitExactnessTest, - Mono16kHz_HighLevel_NoDrift_StreamDelay10) { -#else -TEST(EchoCancellationBitExactnessTest, - DISABLED_Mono16kHz_HighLevel_NoDrift_StreamDelay10) { -#endif - const float kOutputReference[] = {0.000055f, 0.000421f, 0.001149f}; - RunBitexactnessTest(16000, 1, 10, false, 0, - EchoCancellationImpl::SuppressionLevel::kHighSuppression, - kStreamHasEchoReference, kOutputReference); -} - -#if !(defined(WEBRTC_ARCH_ARM64) || defined(WEBRTC_ARCH_ARM) || \ - defined(WEBRTC_ANDROID)) -TEST(EchoCancellationBitExactnessTest, - Mono16kHz_HighLevel_NoDrift_StreamDelay20) { -#else -TEST(EchoCancellationBitExactnessTest, - DISABLED_Mono16kHz_HighLevel_NoDrift_StreamDelay20) { -#endif - const float kOutputReference[] = {0.000055f, 0.000421f, 0.001149f}; - RunBitexactnessTest(16000, 1, 20, false, 0, - EchoCancellationImpl::SuppressionLevel::kHighSuppression, - kStreamHasEchoReference, kOutputReference); -} - -#if !(defined(WEBRTC_ARCH_ARM64) || defined(WEBRTC_ARCH_ARM) || \ - defined(WEBRTC_ANDROID)) -TEST(EchoCancellationBitExactnessTest, - Mono16kHz_HighLevel_Drift0_StreamDelay0) { -#else -TEST(EchoCancellationBitExactnessTest, - DISABLED_Mono16kHz_HighLevel_Drift0_StreamDelay0) { -#endif - const float kOutputReference[] = {0.000055f, 0.000421f, 0.001149f}; - RunBitexactnessTest(16000, 1, 0, true, 0, - EchoCancellationImpl::SuppressionLevel::kHighSuppression, - kStreamHasEchoReference, kOutputReference); -} - -#if !(defined(WEBRTC_ARCH_ARM64) || defined(WEBRTC_ARCH_ARM) || \ - defined(WEBRTC_ANDROID)) -TEST(EchoCancellationBitExactnessTest, - Mono16kHz_HighLevel_Drift5_StreamDelay0) { -#else -TEST(EchoCancellationBitExactnessTest, - DISABLED_Mono16kHz_HighLevel_Drift5_StreamDelay0) { -#endif - const float kOutputReference[] = {0.000055f, 0.000421f, 0.001149f}; - RunBitexactnessTest(16000, 1, 0, true, 5, - EchoCancellationImpl::SuppressionLevel::kHighSuppression, - kStreamHasEchoReference, kOutputReference); -} - -#if !(defined(WEBRTC_ARCH_ARM64) || defined(WEBRTC_ARCH_ARM) || \ - defined(WEBRTC_ANDROID)) -TEST(EchoCancellationBitExactnessTest, - Stereo8kHz_HighLevel_NoDrift_StreamDelay0) { -#else -TEST(EchoCancellationBitExactnessTest, - DISABLED_Stereo8kHz_HighLevel_NoDrift_StreamDelay0) { -#endif -#if defined(WEBRTC_MAC) - const float kOutputReference[] = {-0.000392f, -0.001449f, 0.003004f, - -0.000392f, -0.001449f, 0.003004f}; -#else - const float kOutputReference[] = {-0.000464f, -0.001525f, 0.002933f, - -0.000464f, -0.001525f, 0.002933f}; -#endif - RunBitexactnessTest(8000, 2, 0, false, 0, - EchoCancellationImpl::SuppressionLevel::kHighSuppression, - kStreamHasEchoReference, kOutputReference); -} - -#if !(defined(WEBRTC_ARCH_ARM64) || defined(WEBRTC_ARCH_ARM) || \ - defined(WEBRTC_ANDROID)) -TEST(EchoCancellationBitExactnessTest, - Stereo16kHz_HighLevel_NoDrift_StreamDelay0) { -#else -TEST(EchoCancellationBitExactnessTest, - DISABLED_Stereo16kHz_HighLevel_NoDrift_StreamDelay0) { -#endif - const float kOutputReference[] = {0.000166f, 0.000735f, 0.000841f, - 0.000166f, 0.000735f, 0.000841f}; - RunBitexactnessTest(16000, 2, 0, false, 0, - EchoCancellationImpl::SuppressionLevel::kHighSuppression, - kStreamHasEchoReference, kOutputReference); -} - -#if !(defined(WEBRTC_ARCH_ARM64) || defined(WEBRTC_ARCH_ARM) || \ - defined(WEBRTC_ANDROID)) -TEST(EchoCancellationBitExactnessTest, - Stereo32kHz_HighLevel_NoDrift_StreamDelay0) { -#else -TEST(EchoCancellationBitExactnessTest, - DISABLED_Stereo32kHz_HighLevel_NoDrift_StreamDelay0) { -#endif -#if defined(WEBRTC_MAC) - const float kOutputReference[] = {-0.000458f, 0.000214f, 0.000122f, - -0.000458f, 0.000214f, 0.000122f}; -#else - const float kOutputReference[] = {-0.000427f, 0.000183f, 0.000183f, - -0.000427f, 0.000183f, 0.000183f}; -#endif - RunBitexactnessTest(32000, 2, 0, false, 0, - EchoCancellationImpl::SuppressionLevel::kHighSuppression, - kStreamHasEchoReference, kOutputReference); -} - -#if !(defined(WEBRTC_ARCH_ARM64) || defined(WEBRTC_ARCH_ARM) || \ - defined(WEBRTC_ANDROID)) -TEST(EchoCancellationBitExactnessTest, - Stereo48kHz_HighLevel_NoDrift_StreamDelay0) { -#else -TEST(EchoCancellationBitExactnessTest, - DISABLED_Stereo48kHz_HighLevel_NoDrift_StreamDelay0) { -#endif - const float kOutputReference[] = {-0.001101f, -0.001101f, -0.000449f, - -0.001101f, -0.001101f, -0.000449f}; - RunBitexactnessTest(48000, 2, 0, false, 0, - EchoCancellationImpl::SuppressionLevel::kHighSuppression, - kStreamHasEchoReference, kOutputReference); -} - -} // namespace webrtc diff --git a/modules/audio_processing/echo_cancellation_impl.cc b/modules/audio_processing/echo_cancellation_impl.cc deleted file mode 100644 index 25e8d70b52..0000000000 --- a/modules/audio_processing/echo_cancellation_impl.cc +++ /dev/null @@ -1,432 +0,0 @@ -/* - * 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/echo_cancellation_impl.h" - -#include -#include - -#include "modules/audio_processing/aec/aec_core.h" -#include "modules/audio_processing/aec/echo_cancellation.h" -#include "modules/audio_processing/audio_buffer.h" -#include "rtc_base/checks.h" -#include "system_wrappers/include/field_trial.h" - -namespace webrtc { - -namespace { -int16_t MapSetting(EchoCancellationImpl::SuppressionLevel level) { - switch (level) { - case EchoCancellationImpl::kLowSuppression: - return kAecNlpConservative; - case EchoCancellationImpl::kModerateSuppression: - return kAecNlpModerate; - case EchoCancellationImpl::kHighSuppression: - return kAecNlpAggressive; - } - RTC_NOTREACHED(); - return -1; -} - -AudioProcessing::Error MapError(int err) { - switch (err) { - case AEC_UNSUPPORTED_FUNCTION_ERROR: - return AudioProcessing::kUnsupportedFunctionError; - case AEC_BAD_PARAMETER_ERROR: - return AudioProcessing::kBadParameterError; - case AEC_BAD_PARAMETER_WARNING: - return AudioProcessing::kBadStreamParameterWarning; - default: - // AEC_UNSPECIFIED_ERROR - // AEC_UNINITIALIZED_ERROR - // AEC_NULL_POINTER_ERROR - return AudioProcessing::kUnspecifiedError; - } -} - -bool EnforceZeroStreamDelay() { -#if defined(CHROMEOS) - return !field_trial::IsEnabled("WebRTC-Aec2ZeroStreamDelayKillSwitch"); -#else - return false; -#endif -} - -} // namespace - -struct EchoCancellationImpl::StreamProperties { - StreamProperties() = delete; - StreamProperties(int sample_rate_hz, - size_t num_reverse_channels, - size_t num_output_channels, - size_t num_proc_channels) - : sample_rate_hz(sample_rate_hz), - num_reverse_channels(num_reverse_channels), - num_output_channels(num_output_channels), - num_proc_channels(num_proc_channels) {} - - const int sample_rate_hz; - const size_t num_reverse_channels; - const size_t num_output_channels; - const size_t num_proc_channels; -}; - -class EchoCancellationImpl::Canceller { - public: - Canceller() { - state_ = WebRtcAec_Create(); - RTC_DCHECK(state_); - } - - ~Canceller() { - RTC_CHECK(state_); - WebRtcAec_Free(state_); - } - - void* state() { return state_; } - - void Initialize(int sample_rate_hz) { - // TODO(ajm): Drift compensation is disabled in practice. If restored, it - // should be managed internally and not depend on the hardware sample rate. - // For now, just hardcode a 48 kHz value. - const int error = WebRtcAec_Init(state_, sample_rate_hz, 48000); - RTC_DCHECK_EQ(0, error); - } - - private: - void* state_; -}; - -EchoCancellationImpl::EchoCancellationImpl() - : drift_compensation_enabled_(false), - metrics_enabled_(true), - suppression_level_(kHighSuppression), - stream_drift_samples_(0), - was_stream_drift_set_(false), - stream_has_echo_(false), - delay_logging_enabled_(true), - extended_filter_enabled_(false), - delay_agnostic_enabled_(false), - enforce_zero_stream_delay_(EnforceZeroStreamDelay()) {} - -EchoCancellationImpl::~EchoCancellationImpl() = default; - -void EchoCancellationImpl::ProcessRenderAudio( - rtc::ArrayView packed_render_audio) { - RTC_DCHECK(stream_properties_); - size_t handle_index = 0; - size_t buffer_index = 0; - const size_t num_frames_per_band = - packed_render_audio.size() / (stream_properties_->num_output_channels * - stream_properties_->num_reverse_channels); - for (size_t i = 0; i < stream_properties_->num_output_channels; i++) { - for (size_t j = 0; j < stream_properties_->num_reverse_channels; j++) { - WebRtcAec_BufferFarend(cancellers_[handle_index++]->state(), - &packed_render_audio[buffer_index], - num_frames_per_band); - - buffer_index += num_frames_per_band; - } - } -} - -int EchoCancellationImpl::ProcessCaptureAudio(AudioBuffer* audio, - int stream_delay_ms) { - const int stream_delay_ms_use = - enforce_zero_stream_delay_ ? 0 : stream_delay_ms; - - if (drift_compensation_enabled_ && !was_stream_drift_set_) { - return AudioProcessing::kStreamParameterNotSetError; - } - - RTC_DCHECK(stream_properties_); - RTC_DCHECK_GE(160, audio->num_frames_per_band()); - RTC_DCHECK_EQ(audio->num_channels(), stream_properties_->num_proc_channels); - - int err = AudioProcessing::kNoError; - - // The ordering convention must be followed to pass to the correct AEC. - size_t handle_index = 0; - stream_has_echo_ = false; - for (size_t i = 0; i < audio->num_channels(); i++) { - for (size_t j = 0; j < stream_properties_->num_reverse_channels; j++) { - err = - WebRtcAec_Process(cancellers_[handle_index]->state(), - audio->split_bands_const(i), audio->num_bands(), - audio->split_bands(i), audio->num_frames_per_band(), - stream_delay_ms_use, stream_drift_samples_); - - if (err != AudioProcessing::kNoError) { - err = MapError(err); - // TODO(ajm): Figure out how to return warnings properly. - if (err != AudioProcessing::kBadStreamParameterWarning) { - return err; - } - } - - int status = 0; - err = WebRtcAec_get_echo_status(cancellers_[handle_index]->state(), - &status); - if (err != AudioProcessing::kNoError) { - return MapError(err); - } - - if (status == 1) { - stream_has_echo_ = true; - } - - handle_index++; - } - } - - was_stream_drift_set_ = false; - return AudioProcessing::kNoError; -} - -int EchoCancellationImpl::set_suppression_level(SuppressionLevel level) { - if (MapSetting(level) == -1) { - return AudioProcessing::kBadParameterError; - } - suppression_level_ = level; - return Configure(); -} - -EchoCancellationImpl::SuppressionLevel EchoCancellationImpl::suppression_level() - const { - return suppression_level_; -} - -int EchoCancellationImpl::enable_drift_compensation(bool enable) { - drift_compensation_enabled_ = enable; - return Configure(); -} - -bool EchoCancellationImpl::is_drift_compensation_enabled() const { - return drift_compensation_enabled_; -} - -void EchoCancellationImpl::set_stream_drift_samples(int drift) { - was_stream_drift_set_ = true; - stream_drift_samples_ = drift; -} - -int EchoCancellationImpl::stream_drift_samples() const { - return stream_drift_samples_; -} - -int EchoCancellationImpl::enable_metrics(bool enable) { - metrics_enabled_ = enable; - return Configure(); -} - -bool EchoCancellationImpl::are_metrics_enabled() const { - return metrics_enabled_; -} - -// TODO(ajm): we currently just use the metrics from the first AEC. Think more -// aboue the best way to extend this to multi-channel. -int EchoCancellationImpl::GetMetrics(Metrics* metrics) { - if (metrics == NULL) { - return AudioProcessing::kNullPointerError; - } - - if (!metrics_enabled_) { - return AudioProcessing::kNotEnabledError; - } - - AecMetrics my_metrics; - memset(&my_metrics, 0, sizeof(my_metrics)); - memset(metrics, 0, sizeof(Metrics)); - - const int err = WebRtcAec_GetMetrics(cancellers_[0]->state(), &my_metrics); - if (err != AudioProcessing::kNoError) { - return MapError(err); - } - - metrics->residual_echo_return_loss.instant = my_metrics.rerl.instant; - metrics->residual_echo_return_loss.average = my_metrics.rerl.average; - metrics->residual_echo_return_loss.maximum = my_metrics.rerl.max; - metrics->residual_echo_return_loss.minimum = my_metrics.rerl.min; - - metrics->echo_return_loss.instant = my_metrics.erl.instant; - metrics->echo_return_loss.average = my_metrics.erl.average; - metrics->echo_return_loss.maximum = my_metrics.erl.max; - metrics->echo_return_loss.minimum = my_metrics.erl.min; - - metrics->echo_return_loss_enhancement.instant = my_metrics.erle.instant; - metrics->echo_return_loss_enhancement.average = my_metrics.erle.average; - metrics->echo_return_loss_enhancement.maximum = my_metrics.erle.max; - metrics->echo_return_loss_enhancement.minimum = my_metrics.erle.min; - - metrics->a_nlp.instant = my_metrics.aNlp.instant; - metrics->a_nlp.average = my_metrics.aNlp.average; - metrics->a_nlp.maximum = my_metrics.aNlp.max; - metrics->a_nlp.minimum = my_metrics.aNlp.min; - - metrics->divergent_filter_fraction = my_metrics.divergent_filter_fraction; - return AudioProcessing::kNoError; -} - -bool EchoCancellationImpl::stream_has_echo() const { - return stream_has_echo_; -} - -int EchoCancellationImpl::enable_delay_logging(bool enable) { - delay_logging_enabled_ = enable; - return Configure(); -} - -bool EchoCancellationImpl::is_delay_logging_enabled() const { - return delay_logging_enabled_; -} - -bool EchoCancellationImpl::is_delay_agnostic_enabled() const { - return delay_agnostic_enabled_; -} - -std::string EchoCancellationImpl::GetExperimentsDescription() { - return refined_adaptive_filter_enabled_ ? "Legacy AEC;RefinedAdaptiveFilter;" - : "Legacy AEC;"; -} - -bool EchoCancellationImpl::is_refined_adaptive_filter_enabled() const { - return refined_adaptive_filter_enabled_; -} - -bool EchoCancellationImpl::is_extended_filter_enabled() const { - return extended_filter_enabled_; -} - -// TODO(bjornv): How should we handle the multi-channel case? -int EchoCancellationImpl::GetDelayMetrics(int* median, int* std) { - float fraction_poor_delays = 0; - return GetDelayMetrics(median, std, &fraction_poor_delays); -} - -int EchoCancellationImpl::GetDelayMetrics(int* median, - int* std, - float* fraction_poor_delays) { - if (median == NULL) { - return AudioProcessing::kNullPointerError; - } - if (std == NULL) { - return AudioProcessing::kNullPointerError; - } - - if (!delay_logging_enabled_) { - return AudioProcessing::kNotEnabledError; - } - - const int err = WebRtcAec_GetDelayMetrics(cancellers_[0]->state(), median, - std, fraction_poor_delays); - if (err != AudioProcessing::kNoError) { - return MapError(err); - } - - return AudioProcessing::kNoError; -} - -struct AecCore* EchoCancellationImpl::aec_core() const { - return WebRtcAec_aec_core(cancellers_[0]->state()); -} - -void EchoCancellationImpl::Initialize(int sample_rate_hz, - size_t num_reverse_channels, - size_t num_output_channels, - size_t num_proc_channels) { - stream_properties_.reset( - new StreamProperties(sample_rate_hz, num_reverse_channels, - num_output_channels, num_proc_channels)); - - const size_t num_cancellers_required = - NumCancellersRequired(stream_properties_->num_output_channels, - stream_properties_->num_reverse_channels); - if (num_cancellers_required > cancellers_.size()) { - const size_t cancellers_old_size = cancellers_.size(); - cancellers_.resize(num_cancellers_required); - - for (size_t i = cancellers_old_size; i < cancellers_.size(); ++i) { - cancellers_[i].reset(new Canceller()); - } - } - - for (auto& canceller : cancellers_) { - canceller->Initialize(sample_rate_hz); - } - - Configure(); -} - -int EchoCancellationImpl::GetSystemDelayInSamples() const { - // Report the delay for the first AEC component. - return WebRtcAec_system_delay(WebRtcAec_aec_core(cancellers_[0]->state())); -} - -void EchoCancellationImpl::PackRenderAudioBuffer( - const AudioBuffer* audio, - size_t num_output_channels, - size_t num_channels, - std::vector* packed_buffer) { - RTC_DCHECK_GE(160, audio->num_frames_per_band()); - RTC_DCHECK_EQ(num_channels, audio->num_channels()); - - packed_buffer->clear(); - // The ordering convention must be followed to pass the correct data. - for (size_t i = 0; i < num_output_channels; i++) { - for (size_t j = 0; j < audio->num_channels(); j++) { - // Buffer the samples in the render queue. - packed_buffer->insert(packed_buffer->end(), - audio->split_bands_const(j)[kBand0To8kHz], - (audio->split_bands_const(j)[kBand0To8kHz] + - audio->num_frames_per_band())); - } - } -} - -void EchoCancellationImpl::SetExtraOptions(bool use_extended_filter, - bool use_delay_agnostic, - bool use_refined_adaptive_filter) { - extended_filter_enabled_ = use_extended_filter; - delay_agnostic_enabled_ = use_delay_agnostic; - refined_adaptive_filter_enabled_ = use_refined_adaptive_filter; - Configure(); -} - -int EchoCancellationImpl::Configure() { - AecConfig config; - config.metricsMode = metrics_enabled_; - config.nlpMode = MapSetting(suppression_level_); - config.skewMode = drift_compensation_enabled_; - config.delay_logging = delay_logging_enabled_; - - int error = AudioProcessing::kNoError; - for (auto& canceller : cancellers_) { - WebRtcAec_enable_extended_filter(WebRtcAec_aec_core(canceller->state()), - extended_filter_enabled_ ? 1 : 0); - WebRtcAec_enable_delay_agnostic(WebRtcAec_aec_core(canceller->state()), - delay_agnostic_enabled_ ? 1 : 0); - WebRtcAec_enable_refined_adaptive_filter( - WebRtcAec_aec_core(canceller->state()), - refined_adaptive_filter_enabled_); - const int handle_error = WebRtcAec_set_config(canceller->state(), config); - if (handle_error != AudioProcessing::kNoError) { - error = AudioProcessing::kNoError; - } - } - return error; -} - -size_t EchoCancellationImpl::NumCancellersRequired( - size_t num_output_channels, - size_t num_reverse_channels) { - return num_output_channels * num_reverse_channels; -} - -} // namespace webrtc diff --git a/modules/audio_processing/echo_cancellation_impl.h b/modules/audio_processing/echo_cancellation_impl.h deleted file mode 100644 index 1df41a780b..0000000000 --- a/modules/audio_processing/echo_cancellation_impl.h +++ /dev/null @@ -1,179 +0,0 @@ -/* - * 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. - */ - -#ifndef MODULES_AUDIO_PROCESSING_ECHO_CANCELLATION_IMPL_H_ -#define MODULES_AUDIO_PROCESSING_ECHO_CANCELLATION_IMPL_H_ - -#include - -#include -#include -#include - -#include "api/array_view.h" -#include "rtc_base/constructor_magic.h" - -namespace webrtc { - -class AudioBuffer; - -// The acoustic echo cancellation (AEC) component provides better performance -// than AECM but also requires more processing power and is dependent on delay -// stability and reporting accuracy. As such it is well-suited and recommended -// for PC and IP phone applications. -class EchoCancellationImpl { - public: - explicit EchoCancellationImpl(); - ~EchoCancellationImpl(); - - void ProcessRenderAudio(rtc::ArrayView packed_render_audio); - int ProcessCaptureAudio(AudioBuffer* audio, int stream_delay_ms); - - // Differences in clock speed on the primary and reverse streams can impact - // the AEC performance. On the client-side, this could be seen when different - // render and capture devices are used, particularly with webcams. - // - // This enables a compensation mechanism, and requires that - // set_stream_drift_samples() be called. - int enable_drift_compensation(bool enable); - bool is_drift_compensation_enabled() const; - - // Sets the difference between the number of samples rendered and captured by - // the audio devices since the last call to |ProcessStream()|. Must be called - // if drift compensation is enabled, prior to |ProcessStream()|. - void set_stream_drift_samples(int drift); - int stream_drift_samples() const; - - enum SuppressionLevel { - kLowSuppression, - kModerateSuppression, - kHighSuppression - }; - - // Sets the aggressiveness of the suppressor. A higher level trades off - // double-talk performance for increased echo suppression. - int set_suppression_level(SuppressionLevel level); - SuppressionLevel suppression_level() const; - - // Returns false if the current frame almost certainly contains no echo - // and true if it _might_ contain echo. - bool stream_has_echo() const; - - // Enables the computation of various echo metrics. These are obtained - // through |GetMetrics()|. - int enable_metrics(bool enable); - bool are_metrics_enabled() const; - - // Each statistic is reported in dB. - // P_far: Far-end (render) signal power. - // P_echo: Near-end (capture) echo signal power. - // P_out: Signal power at the output of the AEC. - // P_a: Internal signal power at the point before the AEC's non-linear - // processor. - struct Metrics { - struct Statistic { - int instant = 0; // Instantaneous value. - int average = 0; // Long-term average. - int maximum = 0; // Long-term maximum. - int minimum = 0; // Long-term minimum. - }; - // RERL = ERL + ERLE - Statistic residual_echo_return_loss; - - // ERL = 10log_10(P_far / P_echo) - Statistic echo_return_loss; - - // ERLE = 10log_10(P_echo / P_out) - Statistic echo_return_loss_enhancement; - - // (Pre non-linear processing suppression) A_NLP = 10log_10(P_echo / P_a) - Statistic a_nlp; - - // Fraction of time that the AEC linear filter is divergent, in a 1-second - // non-overlapped aggregation window. - float divergent_filter_fraction; - }; - - // Provides various statistics about the AEC. - int GetMetrics(Metrics* metrics); - - // Enables computation and logging of delay values. Statistics are obtained - // through |GetDelayMetrics()|. - int enable_delay_logging(bool enable); - bool is_delay_logging_enabled() const; - - // Provides delay metrics. - // The delay metrics consists of the delay |median| and the delay standard - // deviation |std|. It also consists of the fraction of delay estimates - // |fraction_poor_delays| that can make the echo cancellation perform poorly. - // The values are aggregated until the first call to |GetDelayMetrics()| and - // afterwards aggregated and updated every second. - // Note that if there are several clients pulling metrics from - // |GetDelayMetrics()| during a session the first call from any of them will - // change to one second aggregation window for all. - int GetDelayMetrics(int* median, int* std); - int GetDelayMetrics(int* median, int* std, float* fraction_poor_delays); - - // Returns a pointer to the low level AEC component. In case of multiple - // channels, the pointer to the first one is returned. A NULL pointer is - // returned when the AEC component is disabled or has not been initialized - // successfully. - struct AecCore* aec_core() const; - - void Initialize(int sample_rate_hz, - size_t num_reverse_channels_, - size_t num_output_channels_, - size_t num_proc_channels_); - void SetExtraOptions(bool use_extended_filter, - bool use_delay_agnostic, - bool use_refined_adaptive_filter); - bool is_delay_agnostic_enabled() const; - bool is_extended_filter_enabled() const; - std::string GetExperimentsDescription(); - bool is_refined_adaptive_filter_enabled() const; - - // Returns the system delay of the first AEC component. - int GetSystemDelayInSamples() const; - - static void PackRenderAudioBuffer(const AudioBuffer* audio, - size_t num_output_channels, - size_t num_channels, - std::vector* packed_buffer); - static size_t NumCancellersRequired(size_t num_output_channels, - size_t num_reverse_channels); - - private: - class Canceller; - struct StreamProperties; - - void AllocateRenderQueue(); - int Configure(); - - bool drift_compensation_enabled_; - bool metrics_enabled_; - SuppressionLevel suppression_level_; - int stream_drift_samples_; - bool was_stream_drift_set_; - bool stream_has_echo_; - bool delay_logging_enabled_; - bool extended_filter_enabled_; - bool delay_agnostic_enabled_; - bool refined_adaptive_filter_enabled_ = false; - - // Only active on Chrome OS devices. - const bool enforce_zero_stream_delay_; - - std::vector> cancellers_; - std::unique_ptr stream_properties_; -}; - -} // namespace webrtc - -#endif // MODULES_AUDIO_PROCESSING_ECHO_CANCELLATION_IMPL_H_ diff --git a/modules/audio_processing/echo_cancellation_impl_unittest.cc b/modules/audio_processing/echo_cancellation_impl_unittest.cc deleted file mode 100644 index a970a4ea5a..0000000000 --- a/modules/audio_processing/echo_cancellation_impl_unittest.cc +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright (c) 2013 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/echo_cancellation_impl.h" - -#include - -#include "modules/audio_processing/aec/aec_core.h" -#include "modules/audio_processing/include/audio_processing.h" -#include "rtc_base/critical_section.h" -#include "test/gtest.h" - -namespace webrtc { -TEST(EchoCancellationInternalTest, ExtendedFilter) { - EchoCancellationImpl echo_canceller; - echo_canceller.Initialize(AudioProcessing::kSampleRate32kHz, 2, 2, 2); - - AecCore* aec_core = echo_canceller.aec_core(); - ASSERT_TRUE(aec_core != NULL); - // Disabled by default. - EXPECT_EQ(0, WebRtcAec_extended_filter_enabled(aec_core)); - - Config config; - echo_canceller.SetExtraOptions(true, false, false); - EXPECT_EQ(1, WebRtcAec_extended_filter_enabled(aec_core)); - - // Retains setting after initialization. - echo_canceller.Initialize(AudioProcessing::kSampleRate16kHz, 2, 2, 2); - EXPECT_EQ(1, WebRtcAec_extended_filter_enabled(aec_core)); - - echo_canceller.SetExtraOptions(false, false, false); - EXPECT_EQ(0, WebRtcAec_extended_filter_enabled(aec_core)); - - // Retains setting after initialization. - echo_canceller.Initialize(AudioProcessing::kSampleRate16kHz, 1, 1, 1); - EXPECT_EQ(0, WebRtcAec_extended_filter_enabled(aec_core)); -} - -TEST(EchoCancellationInternalTest, DelayAgnostic) { - EchoCancellationImpl echo_canceller; - echo_canceller.Initialize(AudioProcessing::kSampleRate32kHz, 1, 1, 1); - - AecCore* aec_core = echo_canceller.aec_core(); - ASSERT_TRUE(aec_core != NULL); - // Enabled by default. - EXPECT_EQ(0, WebRtcAec_delay_agnostic_enabled(aec_core)); - - Config config; - echo_canceller.SetExtraOptions(false, true, false); - EXPECT_EQ(1, WebRtcAec_delay_agnostic_enabled(aec_core)); - - // Retains setting after initialization. - echo_canceller.Initialize(AudioProcessing::kSampleRate32kHz, 2, 2, 2); - EXPECT_EQ(1, WebRtcAec_delay_agnostic_enabled(aec_core)); - - config.Set(new DelayAgnostic(false)); - echo_canceller.SetExtraOptions(false, false, false); - EXPECT_EQ(0, WebRtcAec_delay_agnostic_enabled(aec_core)); - - // Retains setting after initialization. - echo_canceller.Initialize(AudioProcessing::kSampleRate16kHz, 2, 2, 2); - EXPECT_EQ(0, WebRtcAec_delay_agnostic_enabled(aec_core)); -} - -TEST(EchoCancellationInternalTest, InterfaceConfiguration) { - EchoCancellationImpl echo_canceller; - echo_canceller.Initialize(AudioProcessing::kSampleRate16kHz, 1, 1, 1); - - EXPECT_EQ(0, echo_canceller.enable_drift_compensation(true)); - EXPECT_TRUE(echo_canceller.is_drift_compensation_enabled()); - EXPECT_EQ(0, echo_canceller.enable_drift_compensation(false)); - EXPECT_FALSE(echo_canceller.is_drift_compensation_enabled()); - - EchoCancellationImpl::SuppressionLevel level[] = { - EchoCancellationImpl::kLowSuppression, - EchoCancellationImpl::kModerateSuppression, - EchoCancellationImpl::kHighSuppression, - }; - for (size_t i = 0; i < arraysize(level); i++) { - EXPECT_EQ(0, echo_canceller.set_suppression_level(level[i])); - EXPECT_EQ(level[i], echo_canceller.suppression_level()); - } - - EchoCancellationImpl::Metrics metrics; - EXPECT_EQ(0, echo_canceller.enable_metrics(true)); - EXPECT_TRUE(echo_canceller.are_metrics_enabled()); - EXPECT_EQ(0, echo_canceller.enable_metrics(false)); - EXPECT_FALSE(echo_canceller.are_metrics_enabled()); - - EXPECT_EQ(0, echo_canceller.enable_delay_logging(true)); - EXPECT_TRUE(echo_canceller.is_delay_logging_enabled()); - EXPECT_EQ(0, echo_canceller.enable_delay_logging(false)); - EXPECT_FALSE(echo_canceller.is_delay_logging_enabled()); - - int median = 0; - int std = 0; - float poor_fraction = 0; - EXPECT_EQ(AudioProcessing::kNotEnabledError, - echo_canceller.GetDelayMetrics(&median, &std, &poor_fraction)); - - EXPECT_TRUE(echo_canceller.aec_core() != NULL); -} - -} // namespace webrtc diff --git a/modules/audio_processing/include/audio_processing.h b/modules/audio_processing/include/audio_processing.h index e8ecd6e308..2340834278 100644 --- a/modules/audio_processing/include/audio_processing.h +++ b/modules/audio_processing/include/audio_processing.h @@ -37,8 +37,6 @@ namespace webrtc { -struct AecCore; - class AecDump; class AudioBuffer; class AudioFrame; @@ -50,53 +48,6 @@ class EchoDetector; class CustomAudioAnalyzer; class CustomProcessing; -// Use to enable the extended filter mode in the AEC, along with robustness -// measures around the reported system delays. It comes with a significant -// increase in AEC complexity, but is much more robust to unreliable reported -// delays. -// -// Detailed changes to the algorithm: -// - The filter length is changed from 48 to 128 ms. This comes with tuning of -// several parameters: i) filter adaptation stepsize and error threshold; -// ii) non-linear processing smoothing and overdrive. -// - Option to ignore the reported delays on platforms which we deem -// sufficiently unreliable. See WEBRTC_UNTRUSTED_DELAY in echo_cancellation.c. -// - Faster startup times by removing the excessive "startup phase" processing -// of reported delays. -// - Much more conservative adjustments to the far-end read pointer. We smooth -// the delay difference more heavily, and back off from the difference more. -// Adjustments force a readaptation of the filter, so they should be avoided -// except when really necessary. -struct ExtendedFilter { - ExtendedFilter() : enabled(false) {} - explicit ExtendedFilter(bool enabled) : enabled(enabled) {} - static const ConfigOptionID identifier = ConfigOptionID::kExtendedFilter; - bool enabled; -}; - -// Enables the refined linear filter adaptation in the echo canceller. -// This configuration only applies to non-mobile echo cancellation. -// It can be set in the constructor or using AudioProcessing::SetExtraOptions(). -struct RefinedAdaptiveFilter { - RefinedAdaptiveFilter() : enabled(false) {} - explicit RefinedAdaptiveFilter(bool enabled) : enabled(enabled) {} - static const ConfigOptionID identifier = - ConfigOptionID::kAecRefinedAdaptiveFilter; - bool enabled; -}; - -// Enables delay-agnostic echo cancellation. This feature relies on internally -// estimated delays between the process and reverse streams, thus not relying -// on reported system delays. This configuration only applies to non-mobile echo -// cancellation. It can be set in the constructor or using -// AudioProcessing::SetExtraOptions(). -struct DelayAgnostic { - DelayAgnostic() : enabled(false) {} - explicit DelayAgnostic(bool enabled) : enabled(enabled) {} - static const ConfigOptionID identifier = ConfigOptionID::kDelayAgnostic; - bool enabled; -}; - // Use to enable experimental gain control (AGC). At startup the experimental // AGC moves the microphone volume up to |startup_min_volume| if the current // microphone volume is set too low. The value is clamped to its operating range @@ -279,9 +230,10 @@ class RTC_EXPORT AudioProcessing : public rtc::RefCountInterface { bool enabled = false; bool mobile_mode = false; // Recommended not to use. Will be removed in the future. - // APM components are not fine-tuned for legacy suppression levels. + // TODO(peah): Remove. bool legacy_moderate_suppression_level = false; // Recommended not to use. Will be removed in the future. + // TODO(webrtc:11165): Remove. bool use_legacy_aec = false; bool export_linear_aec_output = false; // Enforce the highpass filter to be on (has no effect for the mobile diff --git a/modules/audio_processing/include/config.h b/modules/audio_processing/include/config.h index 930cf7e85e..8a245864ee 100644 --- a/modules/audio_processing/include/config.h +++ b/modules/audio_processing/include/config.h @@ -27,15 +27,15 @@ enum class ConfigOptionID { kNetEqCapacityConfig, // Deprecated kNetEqFastAccelerate, // Deprecated kVoicePacing, // Deprecated - kExtendedFilter, - kDelayAgnostic, + kExtendedFilter, // Deprecated + kDelayAgnostic, // Deprecated kExperimentalAgc, kExperimentalNs, - kBeamforming, // Deprecated - kIntelligibility, // Deprecated - kEchoCanceller3, // Deprecated - kAecRefinedAdaptiveFilter, - kLevelControl // Deprecated + kBeamforming, // Deprecated + kIntelligibility, // Deprecated + kEchoCanceller3, // Deprecated + kAecRefinedAdaptiveFilter, // Deprecated + kLevelControl // Deprecated }; // Class Config is designed to ease passing a set of options across webrtc code. diff --git a/modules/audio_processing/test/aec_dump_based_simulator.cc b/modules/audio_processing/test/aec_dump_based_simulator.cc index d9bd5bc6b7..e050f48086 100644 --- a/modules/audio_processing/test/aec_dump_based_simulator.cc +++ b/modules/audio_processing/test/aec_dump_based_simulator.cc @@ -13,7 +13,6 @@ #include #include -#include "modules/audio_processing/echo_cancellation_impl.h" #include "modules/audio_processing/echo_control_mobile_impl.h" #include "modules/audio_processing/test/protobuf_utils.h" #include "rtc_base/checks.h" @@ -300,57 +299,6 @@ void AecDumpBasedSimulator::HandleMessage( } } - if (msg.has_aec_delay_agnostic_enabled() || settings_.use_delay_agnostic) { - bool enable = settings_.use_delay_agnostic - ? *settings_.use_delay_agnostic - : msg.aec_delay_agnostic_enabled(); - config.Set(new DelayAgnostic(enable)); - if (settings_.use_verbose_logging) { - std::cout << " aec_delay_agnostic_enabled: " - << (enable ? "true" : "false") << std::endl; - } - } - - if (msg.has_aec_drift_compensation_enabled() || - settings_.use_drift_compensation) { - if (settings_.use_drift_compensation - ? *settings_.use_drift_compensation - : msg.aec_drift_compensation_enabled()) { - RTC_LOG(LS_ERROR) - << "Ignoring deprecated setting: AEC2 drift compensation"; - } - } - - if (msg.has_aec_extended_filter_enabled() || - settings_.use_extended_filter) { - bool enable = settings_.use_extended_filter - ? *settings_.use_extended_filter - : msg.aec_extended_filter_enabled(); - config.Set(new ExtendedFilter(enable)); - if (settings_.use_verbose_logging) { - std::cout << " aec_extended_filter_enabled: " - << (enable ? "true" : "false") << std::endl; - } - } - - if (msg.has_aec_suppression_level() || settings_.aec_suppression_level) { - auto level = static_cast( - settings_.aec_suppression_level ? *settings_.aec_suppression_level - : msg.aec_suppression_level()); - if (level == - webrtc::EchoCancellationImpl::SuppressionLevel::kLowSuppression) { - RTC_LOG(LS_ERROR) - << "Ignoring deprecated setting: AEC2 low suppression"; - } else { - apm_config.echo_canceller.legacy_moderate_suppression_level = - (level == webrtc::EchoCancellationImpl::SuppressionLevel:: - kModerateSuppression); - if (settings_.use_verbose_logging) { - std::cout << " aec_suppression_level: " << level << std::endl; - } - } - } - if (msg.has_aecm_enabled() || settings_.use_aecm) { bool enable = settings_.use_aecm ? *settings_.use_aecm : msg.aecm_enabled(); @@ -486,11 +434,6 @@ void AecDumpBasedSimulator::HandleMessage( << msg.experiments_description() << std::endl; } - if (settings_.use_refined_adaptive_filter) { - config.Set( - new RefinedAdaptiveFilter(*settings_.use_refined_adaptive_filter)); - } - if (settings_.use_ed) { apm_config.residual_echo_detector.enabled = *settings_.use_ed; } diff --git a/modules/audio_processing/test/audio_processing_simulator.cc b/modules/audio_processing/test/audio_processing_simulator.cc index cb1d1ed37c..5677600fd0 100644 --- a/modules/audio_processing/test/audio_processing_simulator.cc +++ b/modules/audio_processing/test/audio_processing_simulator.cc @@ -22,7 +22,6 @@ #include "api/audio/echo_canceller3_factory.h" #include "common_audio/include/audio_util.h" #include "modules/audio_processing/aec_dump/aec_dump_factory.h" -#include "modules/audio_processing/echo_cancellation_impl.h" #include "modules/audio_processing/echo_control_mobile_impl.h" #include "modules/audio_processing/include/audio_processing.h" #include "modules/audio_processing/logging/apm_data_dumper.h" @@ -433,23 +432,17 @@ void AudioProcessingSimulator::CreateAudioProcessor() { } } - const bool use_legacy_aec = settings_.use_aec && *settings_.use_aec && - settings_.use_legacy_aec && - *settings_.use_legacy_aec; const bool use_aec = settings_.use_aec && *settings_.use_aec; const bool use_aecm = settings_.use_aecm && *settings_.use_aecm; - if (use_legacy_aec || use_aec || use_aecm) { + if (use_aec || use_aecm) { apm_config.echo_canceller.enabled = true; apm_config.echo_canceller.mobile_mode = use_aecm; - apm_config.echo_canceller.use_legacy_aec = use_legacy_aec; + apm_config.echo_canceller.use_legacy_aec = false; } apm_config.echo_canceller.export_linear_aec_output = !!settings_.linear_aec_output_filename; - RTC_CHECK(!(use_legacy_aec && settings_.aec_settings_filename)) - << "The legacy AEC cannot be configured using settings"; - - if (use_aec && !use_legacy_aec) { + if (use_aec) { EchoCanceller3Config cfg; if (settings_.aec_settings_filename) { if (settings_.use_verbose_logging) { @@ -472,22 +465,6 @@ void AudioProcessingSimulator::CreateAudioProcessor() { } } - if (settings_.use_drift_compensation && *settings_.use_drift_compensation) { - RTC_LOG(LS_ERROR) << "Ignoring deprecated setting: AEC2 drift compensation"; - } - if (settings_.aec_suppression_level) { - auto level = static_cast( - *settings_.aec_suppression_level); - if (level == - webrtc::EchoCancellationImpl::SuppressionLevel::kLowSuppression) { - RTC_LOG(LS_ERROR) << "Ignoring deprecated setting: AEC2 low suppression"; - } else { - apm_config.echo_canceller.legacy_moderate_suppression_level = - (level == webrtc::EchoCancellationImpl::SuppressionLevel:: - kModerateSuppression); - } - } - if (settings_.use_hpf) { apm_config.high_pass_filter.enabled = *settings_.use_hpf; } @@ -519,14 +496,6 @@ void AudioProcessingSimulator::CreateAudioProcessor() { *settings_.agc_compression_gain; } - if (settings_.use_refined_adaptive_filter) { - config.Set( - new RefinedAdaptiveFilter(*settings_.use_refined_adaptive_filter)); - } - config.Set(new ExtendedFilter( - !settings_.use_extended_filter || *settings_.use_extended_filter)); - config.Set(new DelayAgnostic(!settings_.use_delay_agnostic || - *settings_.use_delay_agnostic)); config.Set(new ExperimentalAgc( !settings_.use_experimental_agc || *settings_.use_experimental_agc, !!settings_.use_experimental_agc_agc2_level_estimator && diff --git a/modules/audio_processing/test/audio_processing_simulator.h b/modules/audio_processing/test/audio_processing_simulator.h index 5b26b5f494..abef2fa398 100644 --- a/modules/audio_processing/test/audio_processing_simulator.h +++ b/modules/audio_processing/test/audio_processing_simulator.h @@ -37,7 +37,6 @@ struct SimulationSettings { ~SimulationSettings(); absl::optional stream_delay; absl::optional use_stream_delay; - absl::optional stream_drift_samples; absl::optional output_sample_rate_hz; absl::optional output_num_channels; absl::optional reverse_output_sample_rate_hz; @@ -61,11 +60,6 @@ struct SimulationSettings { absl::optional use_vad; absl::optional use_le; absl::optional use_all; - absl::optional aec_suppression_level; - absl::optional use_delay_agnostic; - absl::optional use_extended_filter; - absl::optional use_drift_compensation; - absl::optional use_legacy_aec; absl::optional use_legacy_ns; absl::optional use_experimental_agc; absl::optional use_experimental_agc_agc2_level_estimator; @@ -82,7 +76,6 @@ struct SimulationSettings { absl::optional pre_amplifier_gain_factor; absl::optional ns_level; absl::optional maximum_internal_processing_rate; - absl::optional use_refined_adaptive_filter; int initial_mic_level; bool simulate_mic_gain = false; absl::optional multi_channel_render; diff --git a/modules/audio_processing/test/audioproc_float_impl.cc b/modules/audio_processing/test/audioproc_float_impl.cc index 4902acb9ee..6cfcef2f8c 100644 --- a/modules/audio_processing/test/audioproc_float_impl.cc +++ b/modules/audio_processing/test/audioproc_float_impl.cc @@ -114,26 +114,10 @@ ABSL_FLAG(bool, false, "Activate all of the default components (will be overridden by any " "other settings)"); -ABSL_FLAG(int, - aec_suppression_level, - kParameterNotSpecifiedValue, - "Set the aec suppression level (0-2)"); -ABSL_FLAG(int, - delay_agnostic, - kParameterNotSpecifiedValue, - "Activate (1) or deactivate(0) the AEC delay agnostic mode"); -ABSL_FLAG(int, - extended_filter, - kParameterNotSpecifiedValue, - "Activate (1) or deactivate(0) the AEC extended filter mode"); -ABSL_FLAG(int, - use_legacy_aec, - kParameterNotSpecifiedValue, - "Activate (1) or deactivate(0) the legacy AEC"); ABSL_FLAG(int, use_legacy_ns, kParameterNotSpecifiedValue, - "Activate (1) or deactivate(0) the legacy AEC"); + "Activate (1) or deactivate(0) the legacy NS"); ABSL_FLAG(int, experimental_agc, kParameterNotSpecifiedValue, @@ -153,11 +137,6 @@ ABSL_FLAG(int, kParameterNotSpecifiedValue, "AGC2 level estimation" " in the experimental AGC. AGC1 level estimation is the default (0)"); -ABSL_FLAG( - int, - refined_adaptive_filter, - kParameterNotSpecifiedValue, - "Activate (1) or deactivate(0) the refined adaptive filter functionality"); ABSL_FLAG(int, agc_mode, kParameterNotSpecifiedValue, @@ -395,17 +374,6 @@ SimulationSettings CreateSettings() { SetSettingIfFlagSet(absl::GetFlag(FLAGS_ts), &settings.use_ts); SetSettingIfFlagSet(absl::GetFlag(FLAGS_vad), &settings.use_vad); SetSettingIfFlagSet(absl::GetFlag(FLAGS_le), &settings.use_le); - SetSettingIfSpecified(absl::GetFlag(FLAGS_aec_suppression_level), - &settings.aec_suppression_level); - SetSettingIfFlagSet(absl::GetFlag(FLAGS_delay_agnostic), - &settings.use_delay_agnostic); - SetSettingIfFlagSet(absl::GetFlag(FLAGS_extended_filter), - &settings.use_extended_filter); - SetSettingIfFlagSet(absl::GetFlag(FLAGS_refined_adaptive_filter), - &settings.use_refined_adaptive_filter); - - SetSettingIfFlagSet(absl::GetFlag(FLAGS_use_legacy_aec), - &settings.use_legacy_aec); SetSettingIfFlagSet(absl::GetFlag(FLAGS_use_legacy_ns), &settings.use_legacy_ns); SetSettingIfFlagSet(absl::GetFlag(FLAGS_experimental_agc), @@ -440,8 +408,6 @@ SimulationSettings CreateSettings() { &settings.stream_delay); SetSettingIfFlagSet(absl::GetFlag(FLAGS_use_stream_delay), &settings.use_stream_delay); - SetSettingIfSpecified(absl::GetFlag(FLAGS_stream_drift_samples), - &settings.stream_drift_samples); SetSettingIfSpecified(absl::GetFlag(FLAGS_custom_call_order_file), &settings.call_order_input_filename); SetSettingIfSpecified(absl::GetFlag(FLAGS_output_custom_call_order_file), @@ -524,14 +490,6 @@ void PerformBasicParameterSanityChecks(const SimulationSettings& settings) { "Error: The linear AEC ouput filename cannot " "be specified without the AEC being active"); - ReportConditionalErrorAndExit( - ((settings.use_aec && *settings.use_aec && settings.use_legacy_aec && - *settings.use_legacy_aec) || - (settings.use_aecm && *settings.use_aecm)) && - !!settings.linear_aec_output_filename, - "Error: The linear AEC ouput filename cannot be specified when the " - "legacy AEC or the AECm are used"); - ReportConditionalErrorAndExit( settings.use_aec && *settings.use_aec && settings.use_aecm && *settings.use_aecm, @@ -556,13 +514,6 @@ void PerformBasicParameterSanityChecks(const SimulationSettings& settings) { *settings.reverse_output_num_channels <= 0, "Error: --reverse_output_num_channels must be positive!\n"); - ReportConditionalErrorAndExit(settings.aec_suppression_level && - ((*settings.aec_suppression_level) < 1 || - (*settings.aec_suppression_level) > 2), - "Error: --aec_suppression_level must be " - "specified between 1 and 2. 0 is " - "deprecated.\n"); - ReportConditionalErrorAndExit( settings.agc_target_level && ((*settings.agc_target_level) < 0 || (*settings.agc_target_level) > 31), diff --git a/modules/audio_processing/test/debug_dump_replayer.cc b/modules/audio_processing/test/debug_dump_replayer.cc index 45600f05b6..7cb6ec8f6d 100644 --- a/modules/audio_processing/test/debug_dump_replayer.cc +++ b/modules/audio_processing/test/debug_dump_replayer.cc @@ -10,7 +10,6 @@ #include "modules/audio_processing/test/debug_dump_replayer.h" -#include "modules/audio_processing/echo_cancellation_impl.h" #include "modules/audio_processing/test/protobuf_utils.h" #include "modules/audio_processing/test/runtime_setting_util.h" #include "rtc_base/checks.h" @@ -181,8 +180,6 @@ void DebugDumpReplayer::MaybeRecreateApm(const audioproc::Config& msg) { // These configurations cannot be changed on the fly. Config config; RTC_CHECK(msg.has_aec_delay_agnostic_enabled()); - config.Set( - new DelayAgnostic(msg.aec_delay_agnostic_enabled())); RTC_CHECK(msg.has_noise_robust_agc_enabled()); config.Set( @@ -193,8 +190,6 @@ void DebugDumpReplayer::MaybeRecreateApm(const audioproc::Config& msg) { new ExperimentalNs(msg.transient_suppression_enabled())); RTC_CHECK(msg.has_aec_extended_filter_enabled()); - config.Set( - new ExtendedFilter(msg.aec_extended_filter_enabled())); // We only create APM once, since changes on these fields should not // happen in current implementation. @@ -212,12 +207,6 @@ void DebugDumpReplayer::ConfigureApm(const audioproc::Config& msg) { apm_config.echo_canceller.enabled = msg.aec_enabled() || msg.aecm_enabled(); apm_config.echo_canceller.mobile_mode = msg.aecm_enabled(); - RTC_CHECK(msg.has_aec_suppression_level()); - apm_config.echo_canceller.legacy_moderate_suppression_level = - static_cast( - msg.aec_suppression_level()) == - EchoCancellationImpl::SuppressionLevel::kModerateSuppression; - // HPF configs. RTC_CHECK(msg.has_hpf_enabled()); apm_config.high_pass_filter.enabled = msg.hpf_enabled(); diff --git a/modules/audio_processing/test/debug_dump_test.cc b/modules/audio_processing/test/debug_dump_test.cc index 956109148c..28280910bd 100644 --- a/modules/audio_processing/test/debug_dump_test.cc +++ b/modules/audio_processing/test/debug_dump_test.cc @@ -354,35 +354,6 @@ TEST_F(DebugDumpTest, ToggleAec) { VerifyDebugDump(generator.dump_file_name()); } -TEST_F(DebugDumpTest, VerifyRefinedAdaptiveFilterExperimentalString) { - Config config; - AudioProcessing::Config apm_config; - apm_config.echo_canceller.enabled = true; - apm_config.echo_canceller.use_legacy_aec = true; - config.Set(new RefinedAdaptiveFilter(true)); - DebugDumpGenerator generator(config, apm_config); - generator.StartRecording(); - generator.Process(100); - generator.StopRecording(); - - DebugDumpReplayer debug_dump_replayer_; - - ASSERT_TRUE(debug_dump_replayer_.SetDumpFile(generator.dump_file_name())); - - while (const absl::optional event = - debug_dump_replayer_.GetNextEvent()) { - debug_dump_replayer_.RunNextEvent(); - if (event->type() == audioproc::Event::CONFIG) { - const audioproc::Config* msg = &event->config(); - ASSERT_TRUE(msg->has_experiments_description()); - EXPECT_PRED_FORMAT2(::testing::IsSubstring, "RefinedAdaptiveFilter", - msg->experiments_description().c_str()); - EXPECT_PRED_FORMAT2(::testing::IsSubstring, "Legacy AEC", - msg->experiments_description().c_str()); - } - } -} - TEST_F(DebugDumpTest, VerifyCombinedExperimentalStringInclusive) { Config config; AudioProcessing::Config apm_config; @@ -406,8 +377,6 @@ TEST_F(DebugDumpTest, VerifyCombinedExperimentalStringInclusive) { ASSERT_TRUE(msg->has_experiments_description()); EXPECT_PRED_FORMAT2(::testing::IsSubstring, "EchoController", msg->experiments_description().c_str()); - EXPECT_PRED_FORMAT2(::testing::IsNotSubstring, "Legacy AEC", - msg->experiments_description().c_str()); EXPECT_PRED_FORMAT2(::testing::IsSubstring, "AgcClippingLevelExperiment", msg->experiments_description().c_str()); } @@ -418,7 +387,6 @@ TEST_F(DebugDumpTest, VerifyCombinedExperimentalStringExclusive) { Config config; AudioProcessing::Config apm_config; apm_config.echo_canceller.enabled = true; - apm_config.echo_canceller.use_legacy_aec = true; DebugDumpGenerator generator(config, apm_config); generator.StartRecording(); generator.Process(100); @@ -434,8 +402,6 @@ TEST_F(DebugDumpTest, VerifyCombinedExperimentalStringExclusive) { if (event->type() == audioproc::Event::CONFIG) { const audioproc::Config* msg = &event->config(); ASSERT_TRUE(msg->has_experiments_description()); - EXPECT_PRED_FORMAT2(::testing::IsNotSubstring, "EchoController", - msg->experiments_description().c_str()); EXPECT_PRED_FORMAT2(::testing::IsNotSubstring, "AgcClippingLevelExperiment", msg->experiments_description().c_str()); @@ -462,8 +428,6 @@ TEST_F(DebugDumpTest, VerifyAec3ExperimentalString) { if (event->type() == audioproc::Event::CONFIG) { const audioproc::Config* msg = &event->config(); ASSERT_TRUE(msg->has_experiments_description()); - EXPECT_PRED_FORMAT2(::testing::IsNotSubstring, "Legacy AEC", - msg->experiments_description().c_str()); EXPECT_PRED_FORMAT2(::testing::IsSubstring, "EchoController", msg->experiments_description().c_str()); } diff --git a/modules/audio_processing/utility/BUILD.gn b/modules/audio_processing/utility/BUILD.gn index 745775e8cc..a808625ea5 100644 --- a/modules/audio_processing/utility/BUILD.gn +++ b/modules/audio_processing/utility/BUILD.gn @@ -19,17 +19,6 @@ rtc_library("cascaded_biquad_filter") { ] } -rtc_library("block_mean_calculator") { - sources = [ - "block_mean_calculator.cc", - "block_mean_calculator.h", - ] - deps = [ - "../../../rtc_base:checks", - "../../../rtc_base:rtc_base_approved", - ] -} - rtc_library("legacy_delay_estimator") { sources = [ "delay_estimator.cc", @@ -113,20 +102,6 @@ if (rtc_include_tests) { ] } - rtc_library("block_mean_calculator_unittest") { - testonly = true - - sources = [ - "block_mean_calculator_unittest.cc", - ] - deps = [ - ":block_mean_calculator", - "../../../rtc_base:rtc_base_approved", - "../../../test:test_support", - "//testing/gtest", - ] - } - rtc_library("legacy_delay_estimator_unittest") { testonly = true diff --git a/modules/audio_processing/utility/block_mean_calculator.cc b/modules/audio_processing/utility/block_mean_calculator.cc deleted file mode 100644 index 82c1c0face..0000000000 --- a/modules/audio_processing/utility/block_mean_calculator.cc +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright 2016 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/utility/block_mean_calculator.h" - -#include "rtc_base/checks.h" - -namespace webrtc { - -BlockMeanCalculator::BlockMeanCalculator(size_t block_length) - : block_length_(block_length), count_(0), sum_(0.0), mean_(0.0) { - RTC_DCHECK(block_length_ != 0); -} - -void BlockMeanCalculator::Reset() { - Clear(); - mean_ = 0.0; -} - -void BlockMeanCalculator::AddValue(float value) { - sum_ += value; - ++count_; - if (count_ == block_length_) { - mean_ = sum_ / block_length_; - Clear(); - } -} - -bool BlockMeanCalculator::EndOfBlock() const { - return count_ == 0; -} - -float BlockMeanCalculator::GetLatestMean() const { - return mean_; -} - -// Flush all samples added. -void BlockMeanCalculator::Clear() { - count_ = 0; - sum_ = 0.0; -} - -} // namespace webrtc diff --git a/modules/audio_processing/utility/block_mean_calculator.h b/modules/audio_processing/utility/block_mean_calculator.h deleted file mode 100644 index 5ccdbef562..0000000000 --- a/modules/audio_processing/utility/block_mean_calculator.h +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright 2016 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. - */ - -#ifndef MODULES_AUDIO_PROCESSING_UTILITY_BLOCK_MEAN_CALCULATOR_H_ -#define MODULES_AUDIO_PROCESSING_UTILITY_BLOCK_MEAN_CALCULATOR_H_ - -#include - -#include "rtc_base/constructor_magic.h" - -namespace webrtc { - -// BlockMeanCalculator calculates the mean of a block of values. Values are -// added one after another, and the mean is updated at the end of every block. -class BlockMeanCalculator { - public: - explicit BlockMeanCalculator(size_t block_length); - - // Reset. - void Reset(); - - // Add one value to the sequence. - void AddValue(float value); - - // Return whether the latest added value was at the end of a block. - bool EndOfBlock() const; - - // Return the latest mean. - float GetLatestMean() const; - - private: - // Clear all values added. - void Clear(); - - const size_t block_length_; - size_t count_; - float sum_; - float mean_; - - RTC_DISALLOW_COPY_AND_ASSIGN(BlockMeanCalculator); -}; - -} // namespace webrtc - -#endif // MODULES_AUDIO_PROCESSING_UTILITY_BLOCK_MEAN_CALCULATOR_H_ diff --git a/modules/audio_processing/utility/block_mean_calculator_unittest.cc b/modules/audio_processing/utility/block_mean_calculator_unittest.cc deleted file mode 100644 index e829f69f7e..0000000000 --- a/modules/audio_processing/utility/block_mean_calculator_unittest.cc +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (c) 2016 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/utility/block_mean_calculator.h" - -#include "test/gtest.h" - -namespace webrtc { - -TEST(MeanCalculatorTest, Correctness) { - const size_t kBlockLength = 10; - BlockMeanCalculator mean_calculator(kBlockLength); - size_t i = 0; - float reference = 0.0; - - for (; i < kBlockLength - 1; ++i) { - mean_calculator.AddValue(static_cast(i)); - EXPECT_FALSE(mean_calculator.EndOfBlock()); - } - mean_calculator.AddValue(static_cast(i++)); - EXPECT_TRUE(mean_calculator.EndOfBlock()); - - for (; i < 3 * kBlockLength; ++i) { - const bool end_of_block = i % kBlockLength == 0; - if (end_of_block) { - // Sum of (i - kBlockLength) ... (i - 1) - reference = i - 0.5 * (1 + kBlockLength); - } - EXPECT_EQ(mean_calculator.EndOfBlock(), end_of_block); - EXPECT_EQ(reference, mean_calculator.GetLatestMean()); - mean_calculator.AddValue(static_cast(i)); - } -} - -TEST(MeanCalculatorTest, Reset) { - const size_t kBlockLength = 10; - BlockMeanCalculator mean_calculator(kBlockLength); - for (size_t i = 0; i < kBlockLength - 1; ++i) { - mean_calculator.AddValue(static_cast(i)); - } - mean_calculator.Reset(); - size_t i = 0; - for (; i < kBlockLength - 1; ++i) { - mean_calculator.AddValue(static_cast(i)); - EXPECT_FALSE(mean_calculator.EndOfBlock()); - } - mean_calculator.AddValue(static_cast(i)); - EXPECT_TRUE(mean_calculator.EndOfBlock()); - EXPECT_EQ(mean_calculator.GetLatestMean(), 0.5 * (kBlockLength - 1)); -} - -} // namespace webrtc diff --git a/test/fuzzers/audio_processing_configs_fuzzer.cc b/test/fuzzers/audio_processing_configs_fuzzer.cc index c7ce0a9f33..8fe9ad1c55 100644 --- a/test/fuzzers/audio_processing_configs_fuzzer.cc +++ b/test/fuzzers/audio_processing_configs_fuzzer.cc @@ -41,8 +41,6 @@ std::unique_ptr CreateApm(test::FuzzDataHelper* fuzz_data, bool exp_agc = fuzz_data->ReadOrDefaultValue(true); bool exp_ns = fuzz_data->ReadOrDefaultValue(true); static_cast(fuzz_data->ReadOrDefaultValue(true)); - bool ef = fuzz_data->ReadOrDefaultValue(true); - bool raf = fuzz_data->ReadOrDefaultValue(true); static_cast(fuzz_data->ReadOrDefaultValue(true)); static_cast(fuzz_data->ReadOrDefaultValue(true)); bool red = fuzz_data->ReadOrDefaultValue(true); @@ -108,9 +106,6 @@ std::unique_ptr CreateApm(test::FuzzDataHelper* fuzz_data, config.Set(new ExperimentalAgc(exp_agc)); config.Set(new ExperimentalNs(exp_ns)); - config.Set(new ExtendedFilter(ef)); - config.Set(new RefinedAdaptiveFilter(raf)); - config.Set(new DelayAgnostic(true)); std::unique_ptr apm( AudioProcessingBuilder()