From 09a718accde8f6857f3a1e9182a7dc2f6d43bdbb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Per=20=C3=85hgren?= Date: Mon, 11 Dec 2017 22:28:45 +0100 Subject: [PATCH] Added the ability to more easily adjust the filter length in AEC3 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bug: webrtc:8609 Change-Id: If060b332993c2c98d7a12608ab31f4da858b8016 Reviewed-on: https://webrtc-review.googlesource.com/28620 Commit-Queue: Per Ã…hgren Reviewed-by: Gustaf Ullberg Cr-Commit-Position: refs/heads/master@{#21216} --- .../aec3/adaptive_fir_filter.cc | 6 +- .../aec3/adaptive_fir_filter.h | 7 +- .../aec3/adaptive_fir_filter_unittest.cc | 4 +- modules/audio_processing/aec3/aec3_common.h | 15 +- modules/audio_processing/aec3/aec_state.cc | 52 ++--- modules/audio_processing/aec3/aec_state.h | 8 +- .../aec3/aec_state_unittest.cc | 14 +- modules/audio_processing/aec3/echo_remover.cc | 2 +- .../aec3/main_filter_update_gain_unittest.cc | 188 ++++++++++-------- .../aec3/mock/mock_render_delay_buffer.h | 7 +- .../aec3/render_delay_buffer.cc | 17 +- .../aec3/residual_echo_estimator.cc | 6 +- .../aec3/residual_echo_estimator.h | 6 +- .../aec3/residual_echo_estimator_unittest.cc | 3 +- .../shadow_filter_update_gain_unittest.cc | 110 ++++++---- modules/audio_processing/aec3/subtractor.cc | 7 +- modules/audio_processing/aec3/subtractor.h | 7 +- .../aec3/subtractor_unittest.cc | 81 +++++--- .../aec3/suppression_gain_unittest.cc | 2 +- .../include/audio_processing.h | 4 + 20 files changed, 312 insertions(+), 234 deletions(-) diff --git a/modules/audio_processing/aec3/adaptive_fir_filter.cc b/modules/audio_processing/aec3/adaptive_fir_filter.cc index fb3f48a918..713eaa8839 100644 --- a/modules/audio_processing/aec3/adaptive_fir_filter.cc +++ b/modules/audio_processing/aec3/adaptive_fir_filter.cc @@ -415,10 +415,10 @@ AdaptiveFirFilter::AdaptiveFirFilter(size_t size_partitions, fft_(), optimization_(optimization), H_(size_partitions), - H2_(size_partitions, std::array()) { + H2_(size_partitions, std::array()), + h_(GetTimeDomainLength(size_partitions), 0.f) { RTC_DCHECK(data_dumper_); - h_.fill(0.f); for (auto& H_j : H_) { H_j.Clear(); } @@ -431,7 +431,7 @@ AdaptiveFirFilter::AdaptiveFirFilter(size_t size_partitions, AdaptiveFirFilter::~AdaptiveFirFilter() = default; void AdaptiveFirFilter::HandleEchoPathChange() { - h_.fill(0.f); + std::fill(h_.begin(), h_.end(), 0.f); for (auto& H_j : H_) { H_j.Clear(); } diff --git a/modules/audio_processing/aec3/adaptive_fir_filter.h b/modules/audio_processing/aec3/adaptive_fir_filter.h index 7872869351..5fa86a1e53 100644 --- a/modules/audio_processing/aec3/adaptive_fir_filter.h +++ b/modules/audio_processing/aec3/adaptive_fir_filter.h @@ -120,10 +120,7 @@ class AdaptiveFirFilter { } // Returns the estimate of the impulse response. - const std::array& - FilterImpulseResponse() const { - return h_; - } + const std::vector& FilterImpulseResponse() const { return h_; } void DumpFilter(const char* name) { for (auto& H : H_) { @@ -141,7 +138,7 @@ class AdaptiveFirFilter { const Aec3Optimization optimization_; std::vector H_; std::vector> H2_; - std::array h_; + std::vector h_; std::array erl_; size_t partition_to_constrain_ = 0; diff --git a/modules/audio_processing/aec3/adaptive_fir_filter_unittest.cc b/modules/audio_processing/aec3/adaptive_fir_filter_unittest.cc index 791612a3b6..f6a2f35852 100644 --- a/modules/audio_processing/aec3/adaptive_fir_filter_unittest.cc +++ b/modules/audio_processing/aec3/adaptive_fir_filter_unittest.cc @@ -308,10 +308,10 @@ TEST(AdaptiveFirFilter, FilterSize) { TEST(AdaptiveFirFilter, FilterAndAdapt) { constexpr size_t kNumBlocksToProcess = 500; ApmDataDumper data_dumper(42); - AdaptiveFirFilter filter(kAdaptiveFilterLength, DetectOptimization(), + EchoCanceller3Config config; + AdaptiveFirFilter filter(config.filter.length_blocks, DetectOptimization(), &data_dumper); Aec3Fft fft; - EchoCanceller3Config config; config.delay.min_echo_path_delay_blocks = 0; config.delay.default_delay = 1; std::unique_ptr render_delay_buffer( diff --git a/modules/audio_processing/aec3/aec3_common.h b/modules/audio_processing/aec3/aec3_common.h index 2e25b199d1..a9b27e67c3 100644 --- a/modules/audio_processing/aec3/aec3_common.h +++ b/modules/audio_processing/aec3/aec3_common.h @@ -38,10 +38,8 @@ constexpr size_t kFftLengthBy2Plus1 = kFftLengthBy2 + 1; constexpr size_t kFftLengthBy2Minus1 = kFftLengthBy2 - 1; constexpr size_t kFftLength = 2 * kFftLengthBy2; -constexpr int kAdaptiveFilterLength = 12; -constexpr int kUnknownDelayRenderWindowSize = 12; -constexpr int kAdaptiveFilterTimeDomainLength = - kAdaptiveFilterLength * kFftLengthBy2; +constexpr int kMaxAdaptiveFilterLength = 50; +constexpr int kUnknownDelayRenderWindowSize = 30; constexpr int kRenderTransferQueueSizeFrames = 100; constexpr size_t kMaxNumBands = 3; @@ -72,6 +70,10 @@ constexpr bool ValidFullBandRate(int sample_rate_hz) { sample_rate_hz == 32000 || sample_rate_hz == 48000; } +constexpr int GetTimeDomainLength(int filter_length_blocks) { + return filter_length_blocks * kFftLengthBy2; +} + constexpr size_t GetDownSampledBufferSize(size_t down_sampling_factor, size_t num_matched_filters) { return kBlockSize / down_sampling_factor * @@ -80,10 +82,11 @@ constexpr size_t GetDownSampledBufferSize(size_t down_sampling_factor, } constexpr size_t GetRenderDelayBufferSize(size_t down_sampling_factor, - size_t num_matched_filters) { + size_t num_matched_filters, + size_t filter_length_blocks) { return GetDownSampledBufferSize(down_sampling_factor, num_matched_filters) / (kBlockSize / down_sampling_factor) + - kAdaptiveFilterLength + 1; + filter_length_blocks + 1; } // Detects what kind of optimizations to use for the code. diff --git a/modules/audio_processing/aec3/aec_state.cc b/modules/audio_processing/aec3/aec_state.cc index a9d901d2c0..78329a713d 100644 --- a/modules/audio_processing/aec3/aec_state.cc +++ b/modules/audio_processing/aec3/aec_state.cc @@ -29,8 +29,8 @@ int EstimateFilterDelay( adaptive_filter_frequency_response) { const auto& H2 = adaptive_filter_frequency_response; constexpr size_t kUpperBin = kFftLengthBy2 - 5; - RTC_DCHECK_GE(kAdaptiveFilterLength, H2.size()); - std::array delays; + RTC_DCHECK_GE(kMaxAdaptiveFilterLength, H2.size()); + std::array delays; delays.fill(0); for (size_t k = 1; k < kUpperBin; ++k) { // Find the maximum of H2[j]. @@ -56,9 +56,8 @@ AecState::AecState(const EchoCanceller3Config& config) new ApmDataDumper(rtc::AtomicOps::Increment(&instance_count_))), erle_estimator_(config.erle.min, config.erle.max_l, config.erle.max_h), config_(config), - reverb_decay_(config_.ep_strength.default_len) { - max_render_.fill(0.f); -} + max_render_(config_.filter.length_blocks, 0.f), + reverb_decay_(config_.ep_strength.default_len) {} AecState::~AecState() = default; @@ -71,7 +70,7 @@ void AecState::HandleEchoPathChange( capture_signal_saturation_ = false; echo_saturation_ = false; previous_max_sample_ = 0.f; - max_render_.fill(0.f); + std::fill(max_render_.begin(), max_render_.end(), 0.f); force_zero_gain_counter_ = 0; blocks_with_filter_adaptation_ = 0; blocks_with_strong_render_ = 0; @@ -107,18 +106,18 @@ void AecState::HandleEchoPathChange( } } -void AecState::Update(const std::vector>& - adaptive_filter_frequency_response, - const std::array& - adaptive_filter_impulse_response, - bool converged_filter, - const rtc::Optional& external_delay_samples, - const RenderBuffer& render_buffer, - const std::array& E2_main, - const std::array& Y2, - rtc::ArrayView x, - const std::array& s, - bool echo_leakage_detected) { +void AecState::Update( + const std::vector>& + adaptive_filter_frequency_response, + const std::vector& adaptive_filter_impulse_response, + bool converged_filter, + const rtc::Optional& external_delay_samples, + const RenderBuffer& render_buffer, + const std::array& E2_main, + const std::array& Y2, + rtc::ArrayView x, + const std::array& s, + bool echo_leakage_detected) { // Store input parameters. echo_leakage_detected_ = echo_leakage_detected; @@ -208,24 +207,29 @@ void AecState::Update(const std::vector>& UpdateReverb(adaptive_filter_impulse_response); } -void AecState::UpdateReverb( - const std::array& - impulse_response) { +void AecState::UpdateReverb(const std::vector& impulse_response) { if ((!(filter_delay_ && usable_linear_estimate_)) || - (*filter_delay_ > kAdaptiveFilterLength - 4)) { + (*filter_delay_ > config_.filter.length_blocks - 4)) { return; } // Form the data to match against by squaring the impulse response // coefficients. - std::array matching_data; + std::array + matching_data_data; + RTC_DCHECK_LE(GetTimeDomainLength(config_.filter.length_blocks), + matching_data_data.size()); + rtc::ArrayView matching_data( + matching_data_data.data(), + GetTimeDomainLength(config_.filter.length_blocks)); std::transform(impulse_response.begin(), impulse_response.end(), matching_data.begin(), [](float a) { return a * a; }); // Avoid matching against noise in the model by subtracting an estimate of the // model noise power. constexpr size_t kTailLength = 64; - constexpr size_t tail_index = kAdaptiveFilterTimeDomainLength - kTailLength; + const size_t tail_index = + GetTimeDomainLength(config_.filter.length_blocks) - kTailLength; const float tail_power = *std::max_element(matching_data.begin() + tail_index, matching_data.end()); std::for_each(matching_data.begin(), matching_data.begin() + tail_index, diff --git a/modules/audio_processing/aec3/aec_state.h b/modules/audio_processing/aec3/aec_state.h index b8c1523a00..e6089ef1f5 100644 --- a/modules/audio_processing/aec3/aec_state.h +++ b/modules/audio_processing/aec3/aec_state.h @@ -114,8 +114,7 @@ class AecState { // Updates the aec state. void Update(const std::vector>& adaptive_filter_frequency_response, - const std::array& - adaptive_filter_impulse_response, + const std::vector& adaptive_filter_impulse_response, bool converged_filter, const rtc::Optional& external_delay_samples, const RenderBuffer& render_buffer, @@ -141,8 +140,7 @@ class AecState { bool inaudible_echo_ = false; }; - void UpdateReverb(const std::array& - impulse_response); + void UpdateReverb(const std::vector& impulse_response); static int instance_count_; std::unique_ptr data_dumper_; @@ -157,7 +155,6 @@ class AecState { bool echo_saturation_ = false; bool transparent_mode_ = false; float previous_max_sample_ = 0.f; - std::array max_render_; bool force_zero_gain_ = false; bool render_received_ = false; size_t force_zero_gain_counter_ = 0; @@ -169,6 +166,7 @@ class AecState { float reverb_decay_candidate_residual_ = -1.f; EchoAudibility echo_audibility_; const EchoCanceller3Config config_; + std::vector max_render_; float reverb_decay_; bool saturating_echo_path_ = false; bool initial_state_ = true; diff --git a/modules/audio_processing/aec3/aec_state_unittest.cc b/modules/audio_processing/aec3/aec_state_unittest.cc index 016c2c174e..babc0b99eb 100644 --- a/modules/audio_processing/aec3/aec_state_unittest.cc +++ b/modules/audio_processing/aec3/aec_state_unittest.cc @@ -43,8 +43,8 @@ TEST(AecState, NormalUsage) { converged_filter_frequency_response[2].fill(100.f); converged_filter_frequency_response[2][0] = 1.f; - std::array impulse_response; - impulse_response.fill(0.f); + std::vector impulse_response( + GetTimeDomainLength(config.filter.length_blocks), 0.f); // Verify that linear AEC usability is false when the filter is diverged and // there is no external delay reported. @@ -193,8 +193,8 @@ TEST(AecState, ConvergedFilterDelay) { std::vector> frequency_response( kFilterLength); - std::array impulse_response; - impulse_response.fill(0.f); + std::vector impulse_response( + GetTimeDomainLength(config.filter.length_blocks), 0.f); // Verify that the filter delay for a converged filter is properly identified. for (int k = 0; k < kFilterLength; ++k) { @@ -232,13 +232,13 @@ TEST(AecState, ExternalDelay) { x.fill(0.f); std::vector> frequency_response( - kAdaptiveFilterLength); + config.filter.length_blocks); for (auto& v : frequency_response) { v.fill(0.01f); } - std::array impulse_response; - impulse_response.fill(0.f); + std::vector impulse_response( + GetTimeDomainLength(config.filter.length_blocks), 0.f); for (size_t k = 0; k < frequency_response.size() - 1; ++k) { state.HandleEchoPathChange(EchoPathVariability( diff --git a/modules/audio_processing/aec3/echo_remover.cc b/modules/audio_processing/aec3/echo_remover.cc index 17b201af68..5bf96f9498 100644 --- a/modules/audio_processing/aec3/echo_remover.cc +++ b/modules/audio_processing/aec3/echo_remover.cc @@ -101,7 +101,7 @@ EchoRemoverImpl::EchoRemoverImpl(const EchoCanceller3Config& config, new ApmDataDumper(rtc::AtomicOps::Increment(&instance_count_))), optimization_(DetectOptimization()), sample_rate_hz_(sample_rate_hz), - subtractor_(data_dumper_.get(), optimization_), + subtractor_(config, data_dumper_.get(), optimization_), suppression_gain_(config_, optimization_), cng_(optimization_), suppression_filter_(sample_rate_hz_), diff --git a/modules/audio_processing/aec3/main_filter_update_gain_unittest.cc b/modules/audio_processing/aec3/main_filter_update_gain_unittest.cc index b39067cd41..a873d1979e 100644 --- a/modules/audio_processing/aec3/main_filter_update_gain_unittest.cc +++ b/modules/audio_processing/aec3/main_filter_update_gain_unittest.cc @@ -33,6 +33,7 @@ namespace { // gain functionality. void RunFilterUpdateTest(int num_blocks_to_process, size_t delay_samples, + int filter_length_blocks, const std::vector& blocks_with_echo_path_changes, const std::vector& blocks_with_saturation, bool use_silent_render_in_second_half, @@ -40,8 +41,12 @@ void RunFilterUpdateTest(int num_blocks_to_process, std::array* y_last_block, FftData* G_last_block) { ApmDataDumper data_dumper(42); - AdaptiveFirFilter main_filter(12, DetectOptimization(), &data_dumper); - AdaptiveFirFilter shadow_filter(12, DetectOptimization(), &data_dumper); + EchoCanceller3Config config; + config.filter.length_blocks = filter_length_blocks; + AdaptiveFirFilter main_filter(config.filter.length_blocks, + DetectOptimization(), &data_dumper); + AdaptiveFirFilter shadow_filter(config.filter.length_blocks, + DetectOptimization(), &data_dumper); Aec3Fft fft; std::array x_old; x_old.fill(0.f); @@ -50,7 +55,6 @@ void RunFilterUpdateTest(int num_blocks_to_process, Random random_generator(42U); std::vector> x(3, std::vector(kBlockSize, 0.f)); std::vector y(kBlockSize, 0.f); - EchoCanceller3Config config; config.delay.min_echo_path_delay_blocks = 0; config.delay.default_delay = 1; std::unique_ptr render_delay_buffer( @@ -159,9 +163,16 @@ void RunFilterUpdateTest(int num_blocks_to_process, std::copy(G.im.begin(), G.im.end(), G_last_block->im.begin()); } -std::string ProduceDebugText(size_t delay) { +std::string ProduceDebugText(int filter_length_blocks) { std::ostringstream ss; - ss << "Delay: " << delay; + ss << "Length: " << filter_length_blocks; + return ss.str(); +} + +std::string ProduceDebugText(size_t delay, int filter_length_blocks) { + std::ostringstream ss; + ss << "Delay: " << delay << ", "; + ss << ProduceDebugText(filter_length_blocks); return ss.str(); } @@ -172,10 +183,11 @@ std::string ProduceDebugText(size_t delay) { // Verifies that the check for non-null output gain parameter works. TEST(MainFilterUpdateGain, NullDataOutputGain) { ApmDataDumper data_dumper(42); - AdaptiveFirFilter filter(kAdaptiveFilterLength, DetectOptimization(), + EchoCanceller3Config config; + AdaptiveFirFilter filter(config.filter.length_blocks, DetectOptimization(), &data_dumper); std::unique_ptr render_delay_buffer( - RenderDelayBuffer::Create(EchoCanceller3Config(), 3)); + RenderDelayBuffer::Create(config, 3)); RenderSignalAnalyzer analyzer; SubtractorOutput output; MainFilterUpdateGain gain; @@ -190,53 +202,62 @@ TEST(MainFilterUpdateGain, NullDataOutputGain) { TEST(MainFilterUpdateGain, GainCausesFilterToConverge) { std::vector blocks_with_echo_path_changes; std::vector blocks_with_saturation; - for (size_t delay_samples : {0, 64, 150, 200, 301}) { - SCOPED_TRACE(ProduceDebugText(delay_samples)); + for (size_t filter_length_blocks : {12, 20, 30}) { + for (size_t delay_samples : {0, 64, 150, 200, 301}) { + SCOPED_TRACE(ProduceDebugText(delay_samples, filter_length_blocks)); - std::array e; - std::array y; - FftData G; + std::array e; + std::array y; + FftData G; - RunFilterUpdateTest(500, delay_samples, blocks_with_echo_path_changes, - blocks_with_saturation, false, &e, &y, &G); + RunFilterUpdateTest(500, delay_samples, filter_length_blocks, + blocks_with_echo_path_changes, blocks_with_saturation, + false, &e, &y, &G); - // Verify that the main filter is able to perform well. - EXPECT_LT(1000 * std::inner_product(e.begin(), e.end(), e.begin(), 0.f), - std::inner_product(y.begin(), y.end(), y.begin(), 0.f)); + // Verify that the main filter is able to perform well. + EXPECT_LT(1000 * std::inner_product(e.begin(), e.end(), e.begin(), 0.f), + std::inner_product(y.begin(), y.end(), y.begin(), 0.f)); + } } } // Verifies that the magnitude of the gain on average decreases for a // persistently exciting signal. TEST(MainFilterUpdateGain, DecreasingGain) { - std::vector blocks_with_echo_path_changes; - std::vector blocks_with_saturation; + for (size_t filter_length_blocks : {12, 20, 30}) { + SCOPED_TRACE(ProduceDebugText(filter_length_blocks)); + std::vector blocks_with_echo_path_changes; + std::vector blocks_with_saturation; - std::array e; - std::array y; - FftData G_a; - FftData G_b; - FftData G_c; - std::array G_a_power; - std::array G_b_power; - std::array G_c_power; + std::array e; + std::array y; + FftData G_a; + FftData G_b; + FftData G_c; + std::array G_a_power; + std::array G_b_power; + std::array G_c_power; - RunFilterUpdateTest(100, 65, blocks_with_echo_path_changes, - blocks_with_saturation, false, &e, &y, &G_a); - RunFilterUpdateTest(200, 65, blocks_with_echo_path_changes, - blocks_with_saturation, false, &e, &y, &G_b); - RunFilterUpdateTest(300, 65, blocks_with_echo_path_changes, - blocks_with_saturation, false, &e, &y, &G_c); + RunFilterUpdateTest(100, 65, filter_length_blocks, + blocks_with_echo_path_changes, blocks_with_saturation, + false, &e, &y, &G_a); + RunFilterUpdateTest(300, 65, filter_length_blocks, + blocks_with_echo_path_changes, blocks_with_saturation, + false, &e, &y, &G_b); + RunFilterUpdateTest(600, 65, filter_length_blocks, + blocks_with_echo_path_changes, blocks_with_saturation, + false, &e, &y, &G_c); - G_a.Spectrum(Aec3Optimization::kNone, G_a_power); - G_b.Spectrum(Aec3Optimization::kNone, G_b_power); - G_c.Spectrum(Aec3Optimization::kNone, G_c_power); + G_a.Spectrum(Aec3Optimization::kNone, G_a_power); + G_b.Spectrum(Aec3Optimization::kNone, G_b_power); + G_c.Spectrum(Aec3Optimization::kNone, G_c_power); - EXPECT_GT(std::accumulate(G_a_power.begin(), G_a_power.end(), 0.), - std::accumulate(G_b_power.begin(), G_b_power.end(), 0.)); + EXPECT_GT(std::accumulate(G_a_power.begin(), G_a_power.end(), 0.), + std::accumulate(G_b_power.begin(), G_b_power.end(), 0.)); - EXPECT_GT(std::accumulate(G_b_power.begin(), G_b_power.end(), 0.), - std::accumulate(G_c_power.begin(), G_c_power.end(), 0.)); + EXPECT_GT(std::accumulate(G_b_power.begin(), G_b_power.end(), 0.), + std::accumulate(G_c_power.begin(), G_c_power.end(), 0.)); + } } // Verifies that the gain is zero when there is saturation and that the internal @@ -248,58 +269,69 @@ TEST(MainFilterUpdateGain, SaturationBehavior) { blocks_with_saturation.push_back(k); } - std::array e; - std::array y; - FftData G_a; - FftData G_b; - FftData G_a_ref; - G_a_ref.re.fill(0.f); - G_a_ref.im.fill(0.f); + for (size_t filter_length_blocks : {12, 20, 30}) { + SCOPED_TRACE(ProduceDebugText(filter_length_blocks)); + std::array e; + std::array y; + FftData G_a; + FftData G_b; + FftData G_a_ref; + G_a_ref.re.fill(0.f); + G_a_ref.im.fill(0.f); - std::array G_a_power; - std::array G_b_power; + std::array G_a_power; + std::array G_b_power; - RunFilterUpdateTest(100, 65, blocks_with_echo_path_changes, - blocks_with_saturation, false, &e, &y, &G_a); + RunFilterUpdateTest(100, 65, filter_length_blocks, + blocks_with_echo_path_changes, blocks_with_saturation, + false, &e, &y, &G_a); - EXPECT_EQ(G_a_ref.re, G_a.re); - EXPECT_EQ(G_a_ref.im, G_a.im); + EXPECT_EQ(G_a_ref.re, G_a.re); + EXPECT_EQ(G_a_ref.im, G_a.im); - RunFilterUpdateTest(99, 65, blocks_with_echo_path_changes, - blocks_with_saturation, false, &e, &y, &G_a); - RunFilterUpdateTest(201, 65, blocks_with_echo_path_changes, - blocks_with_saturation, false, &e, &y, &G_b); + RunFilterUpdateTest(99, 65, filter_length_blocks, + blocks_with_echo_path_changes, blocks_with_saturation, + false, &e, &y, &G_a); + RunFilterUpdateTest(201, 65, filter_length_blocks, + blocks_with_echo_path_changes, blocks_with_saturation, + false, &e, &y, &G_b); - G_a.Spectrum(Aec3Optimization::kNone, G_a_power); - G_b.Spectrum(Aec3Optimization::kNone, G_b_power); + G_a.Spectrum(Aec3Optimization::kNone, G_a_power); + G_b.Spectrum(Aec3Optimization::kNone, G_b_power); - EXPECT_LT(std::accumulate(G_a_power.begin(), G_a_power.end(), 0.), - std::accumulate(G_b_power.begin(), G_b_power.end(), 0.)); + EXPECT_LT(std::accumulate(G_a_power.begin(), G_a_power.end(), 0.), + std::accumulate(G_b_power.begin(), G_b_power.end(), 0.)); + } } // Verifies that the gain increases after an echo path change. TEST(MainFilterUpdateGain, EchoPathChangeBehavior) { - std::vector blocks_with_echo_path_changes; - std::vector blocks_with_saturation; - blocks_with_echo_path_changes.push_back(99); + for (size_t filter_length_blocks : {12, 20, 30}) { + SCOPED_TRACE(ProduceDebugText(filter_length_blocks)); + std::vector blocks_with_echo_path_changes; + std::vector blocks_with_saturation; + blocks_with_echo_path_changes.push_back(99); - std::array e; - std::array y; - FftData G_a; - FftData G_b; - std::array G_a_power; - std::array G_b_power; + std::array e; + std::array y; + FftData G_a; + FftData G_b; + std::array G_a_power; + std::array G_b_power; - RunFilterUpdateTest(100, 65, blocks_with_echo_path_changes, - blocks_with_saturation, false, &e, &y, &G_a); - RunFilterUpdateTest(101, 65, blocks_with_echo_path_changes, - blocks_with_saturation, false, &e, &y, &G_b); + RunFilterUpdateTest(100, 65, filter_length_blocks, + blocks_with_echo_path_changes, blocks_with_saturation, + false, &e, &y, &G_a); + RunFilterUpdateTest(101, 65, filter_length_blocks, + blocks_with_echo_path_changes, blocks_with_saturation, + false, &e, &y, &G_b); - G_a.Spectrum(Aec3Optimization::kNone, G_a_power); - G_b.Spectrum(Aec3Optimization::kNone, G_b_power); + G_a.Spectrum(Aec3Optimization::kNone, G_a_power); + G_b.Spectrum(Aec3Optimization::kNone, G_b_power); - EXPECT_LT(std::accumulate(G_a_power.begin(), G_a_power.end(), 0.), - std::accumulate(G_b_power.begin(), G_b_power.end(), 0.)); + EXPECT_LT(std::accumulate(G_a_power.begin(), G_a_power.end(), 0.), + std::accumulate(G_b_power.begin(), G_b_power.end(), 0.)); + } } } // namespace webrtc diff --git a/modules/audio_processing/aec3/mock/mock_render_delay_buffer.h b/modules/audio_processing/aec3/mock/mock_render_delay_buffer.h index 15320e5616..ab7e076a49 100644 --- a/modules/audio_processing/aec3/mock/mock_render_delay_buffer.h +++ b/modules/audio_processing/aec3/mock/mock_render_delay_buffer.h @@ -25,15 +25,12 @@ namespace test { class MockRenderDelayBuffer : public RenderDelayBuffer { public: explicit MockRenderDelayBuffer(int sample_rate_hz) - : block_buffer_(GetRenderDelayBufferSize(4, 4), + : block_buffer_(GetRenderDelayBufferSize(4, 4, 12), NumBandsForRate(sample_rate_hz), kBlockSize), spectrum_buffer_(block_buffer_.buffer.size(), kFftLengthBy2Plus1), fft_buffer_(block_buffer_.buffer.size()), - render_buffer_(kAdaptiveFilterLength, - &block_buffer_, - &spectrum_buffer_, - &fft_buffer_), + render_buffer_(12, &block_buffer_, &spectrum_buffer_, &fft_buffer_), downsampled_render_buffer_(GetDownSampledBufferSize(4, 4)) { ON_CALL(*this, GetRenderBuffer()) .WillByDefault( diff --git a/modules/audio_processing/aec3/render_delay_buffer.cc b/modules/audio_processing/aec3/render_delay_buffer.cc index 4acc7848f1..ce9f784bdd 100644 --- a/modules/audio_processing/aec3/render_delay_buffer.cc +++ b/modules/audio_processing/aec3/render_delay_buffer.cc @@ -28,8 +28,6 @@ namespace webrtc { namespace { -constexpr int kBufferHeadroom = kAdaptiveFilterLength; - class RenderDelayBufferImpl final : public RenderDelayBuffer { public: RenderDelayBufferImpl(const EchoCanceller3Config& config, size_t num_bands); @@ -41,7 +39,7 @@ class RenderDelayBufferImpl final : public RenderDelayBuffer { bool SetDelay(size_t delay) override; rtc::Optional Delay() const override { return delay_; } size_t MaxDelay() const override { - return blocks_.buffer.size() - 1 - kBufferHeadroom; + return blocks_.buffer.size() - 1 - buffer_headroom_; } RenderBuffer* GetRenderBuffer() override { return &echo_remover_buffer_; } @@ -68,6 +66,7 @@ class RenderDelayBufferImpl final : public RenderDelayBuffer { const std::vector> zero_block_; const Aec3Fft fft_; std::vector render_ds_; + const int buffer_headroom_; int LowRateBufferOffset() const { return DelayEstimatorOffset(config_) >> 1; } int MaxExternalDelayToInternalDelay(size_t delay) const; @@ -153,18 +152,24 @@ RenderDelayBufferImpl::RenderDelayBufferImpl(const EchoCanceller3Config& config, ? kBlockSize / config.delay.down_sampling_factor : kBlockSize)), blocks_(GetRenderDelayBufferSize(config.delay.down_sampling_factor, - config.delay.num_filters), + config.delay.num_filters, + config.filter.length_blocks), num_bands, kBlockSize), spectra_(blocks_.buffer.size(), kFftLengthBy2Plus1), ffts_(blocks_.buffer.size()), - echo_remover_buffer_(kAdaptiveFilterLength, &blocks_, &spectra_, &ffts_), + delay_(config_.delay.min_echo_path_delay_blocks), + echo_remover_buffer_(config.filter.length_blocks, + &blocks_, + &spectra_, + &ffts_), low_rate_(GetDownSampledBufferSize(config.delay.down_sampling_factor, config.delay.num_filters)), render_decimator_(config.delay.down_sampling_factor), zero_block_(num_bands, std::vector(kBlockSize, 0.f)), fft_(), - render_ds_(sub_block_size_, 0.f) { + render_ds_(sub_block_size_, 0.f), + buffer_headroom_(config.filter.length_blocks) { RTC_DCHECK_EQ(blocks_.buffer.size(), ffts_.buffer.size()); RTC_DCHECK_EQ(spectra_.buffer.size(), ffts_.buffer.size()); diff --git a/modules/audio_processing/aec3/residual_echo_estimator.cc b/modules/audio_processing/aec3/residual_echo_estimator.cc index ba65684fad..3c3899f134 100644 --- a/modules/audio_processing/aec3/residual_echo_estimator.cc +++ b/modules/audio_processing/aec3/residual_echo_estimator.cc @@ -77,7 +77,7 @@ void RenderNoisePower( } // namespace ResidualEchoEstimator::ResidualEchoEstimator(const EchoCanceller3Config& config) - : config_(config) { + : config_(config), S2_old_(config_.filter.length_blocks) { Reset(); } @@ -150,8 +150,8 @@ void ResidualEchoEstimator::Estimate( if (aec_state.ExternalDelay() && aec_state.FilterDelay() && aec_state.SaturatedEcho()) { AddEchoReverb(*R2, aec_state.SaturatedEcho(), - std::min(static_cast(kAdaptiveFilterLength), - delay.value_or(kAdaptiveFilterLength)), + std::min(static_cast(config_.filter.length_blocks), + delay.value_or(config_.filter.length_blocks)), aec_state.ReverbDecay(), R2); } } diff --git a/modules/audio_processing/aec3/residual_echo_estimator.h b/modules/audio_processing/aec3/residual_echo_estimator.h index d4c4307eb5..271b1c0886 100644 --- a/modules/audio_processing/aec3/residual_echo_estimator.h +++ b/modules/audio_processing/aec3/residual_echo_estimator.h @@ -64,16 +64,14 @@ class ResidualEchoEstimator { size_t delay, float reverb_decay_factor, std::array* R2); - + const EchoCanceller3Config config_; std::array R2_old_; std::array R2_hold_counter_; std::array R2_reverb_; int S2_old_index_ = 0; - std::array, kAdaptiveFilterLength> - S2_old_; + std::vector> S2_old_; std::array X2_noise_floor_; std::array X2_noise_floor_counter_; - const EchoCanceller3Config config_; RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(ResidualEchoEstimator); }; diff --git a/modules/audio_processing/aec3/residual_echo_estimator_unittest.cc b/modules/audio_processing/aec3/residual_echo_estimator_unittest.cc index 06b7db68b1..b0d654a489 100644 --- a/modules/audio_processing/aec3/residual_echo_estimator_unittest.cc +++ b/modules/audio_processing/aec3/residual_echo_estimator_unittest.cc @@ -70,8 +70,7 @@ TEST(ResidualEchoEstimator, DISABLED_BasicTest) { H2[2].fill(10.f); H2[2][0] = 0.1f; - std::array h; - h.fill(0.f); + std::vector h(GetTimeDomainLength(config.filter.length_blocks), 0.f); s.fill(100.f); diff --git a/modules/audio_processing/aec3/shadow_filter_update_gain_unittest.cc b/modules/audio_processing/aec3/shadow_filter_update_gain_unittest.cc index fa6713bfbb..596cb86ee2 100644 --- a/modules/audio_processing/aec3/shadow_filter_update_gain_unittest.cc +++ b/modules/audio_processing/aec3/shadow_filter_update_gain_unittest.cc @@ -31,16 +31,20 @@ namespace { // gain functionality. void RunFilterUpdateTest(int num_blocks_to_process, size_t delay_samples, + int filter_length_blocks, const std::vector& blocks_with_saturation, std::array* e_last_block, std::array* y_last_block, FftData* G_last_block) { ApmDataDumper data_dumper(42); - AdaptiveFirFilter main_filter(12, DetectOptimization(), &data_dumper); - AdaptiveFirFilter shadow_filter(12, DetectOptimization(), &data_dumper); + EchoCanceller3Config config; + config.filter.length_blocks = filter_length_blocks; + AdaptiveFirFilter main_filter(config.filter.length_blocks, + DetectOptimization(), &data_dumper); + AdaptiveFirFilter shadow_filter(config.filter.length_blocks, + DetectOptimization(), &data_dumper); Aec3Fft fft; - EchoCanceller3Config config; config.delay.min_echo_path_delay_blocks = 0; config.delay.default_delay = 1; std::unique_ptr render_delay_buffer( @@ -104,9 +108,16 @@ void RunFilterUpdateTest(int num_blocks_to_process, std::copy(G.im.begin(), G.im.end(), G_last_block->im.begin()); } -std::string ProduceDebugText(size_t delay) { +std::string ProduceDebugText(int filter_length_blocks) { std::ostringstream ss; - ss << ", Delay: " << delay; + ss << "Length: " << filter_length_blocks; + return ss.str(); +} + +std::string ProduceDebugText(size_t delay, int filter_length_blocks) { + std::ostringstream ss; + ss << "Delay: " << delay << ", "; + ss << ProduceDebugText(filter_length_blocks); return ss.str(); } @@ -133,49 +144,58 @@ TEST(ShadowFilterUpdateGain, NullDataOutputGain) { TEST(ShadowFilterUpdateGain, GainCausesFilterToConverge) { std::vector blocks_with_echo_path_changes; std::vector blocks_with_saturation; - for (size_t delay_samples : {0, 64, 150, 200, 301}) { - SCOPED_TRACE(ProduceDebugText(delay_samples)); + for (size_t filter_length_blocks : {12, 20, 30}) { + for (size_t delay_samples : {0, 64, 150, 200, 301}) { + SCOPED_TRACE(ProduceDebugText(delay_samples, filter_length_blocks)); - std::array e; - std::array y; - FftData G; + std::array e; + std::array y; + FftData G; - RunFilterUpdateTest(500, delay_samples, blocks_with_saturation, &e, &y, &G); + RunFilterUpdateTest(1000, delay_samples, filter_length_blocks, + blocks_with_saturation, &e, &y, &G); - // Verify that the main filter is able to perform well. - EXPECT_LT(1000 * std::inner_product(e.begin(), e.end(), e.begin(), 0.f), - std::inner_product(y.begin(), y.end(), y.begin(), 0.f)); + // Verify that the main filter is able to perform well. + EXPECT_LT(1000 * std::inner_product(e.begin(), e.end(), e.begin(), 0.f), + std::inner_product(y.begin(), y.end(), y.begin(), 0.f)); + } } } // Verifies that the magnitude of the gain on average decreases for a // persistently exciting signal. TEST(ShadowFilterUpdateGain, DecreasingGain) { - std::vector blocks_with_echo_path_changes; - std::vector blocks_with_saturation; + for (size_t filter_length_blocks : {12, 20, 30}) { + SCOPED_TRACE(ProduceDebugText(filter_length_blocks)); + std::vector blocks_with_echo_path_changes; + std::vector blocks_with_saturation; - std::array e; - std::array y; - FftData G_a; - FftData G_b; - FftData G_c; - std::array G_a_power; - std::array G_b_power; - std::array G_c_power; + std::array e; + std::array y; + FftData G_a; + FftData G_b; + FftData G_c; + std::array G_a_power; + std::array G_b_power; + std::array G_c_power; - RunFilterUpdateTest(100, 65, blocks_with_saturation, &e, &y, &G_a); - RunFilterUpdateTest(200, 65, blocks_with_saturation, &e, &y, &G_b); - RunFilterUpdateTest(300, 65, blocks_with_saturation, &e, &y, &G_c); + RunFilterUpdateTest(100, 65, filter_length_blocks, blocks_with_saturation, + &e, &y, &G_a); + RunFilterUpdateTest(200, 65, filter_length_blocks, blocks_with_saturation, + &e, &y, &G_b); + RunFilterUpdateTest(300, 65, filter_length_blocks, blocks_with_saturation, + &e, &y, &G_c); - G_a.Spectrum(Aec3Optimization::kNone, G_a_power); - G_b.Spectrum(Aec3Optimization::kNone, G_b_power); - G_c.Spectrum(Aec3Optimization::kNone, G_c_power); + G_a.Spectrum(Aec3Optimization::kNone, G_a_power); + G_b.Spectrum(Aec3Optimization::kNone, G_b_power); + G_c.Spectrum(Aec3Optimization::kNone, G_c_power); - EXPECT_GT(std::accumulate(G_a_power.begin(), G_a_power.end(), 0.), - std::accumulate(G_b_power.begin(), G_b_power.end(), 0.)); + EXPECT_GT(std::accumulate(G_a_power.begin(), G_a_power.end(), 0.), + std::accumulate(G_b_power.begin(), G_b_power.end(), 0.)); - EXPECT_GT(std::accumulate(G_b_power.begin(), G_b_power.end(), 0.), - std::accumulate(G_c_power.begin(), G_c_power.end(), 0.)); + EXPECT_GT(std::accumulate(G_b_power.begin(), G_b_power.end(), 0.), + std::accumulate(G_c_power.begin(), G_c_power.end(), 0.)); + } } // Verifies that the gain is zero when there is saturation. @@ -185,18 +205,22 @@ TEST(ShadowFilterUpdateGain, SaturationBehavior) { for (int k = 99; k < 200; ++k) { blocks_with_saturation.push_back(k); } + for (size_t filter_length_blocks : {12, 20, 30}) { + SCOPED_TRACE(ProduceDebugText(filter_length_blocks)); - std::array e; - std::array y; - FftData G_a; - FftData G_a_ref; - G_a_ref.re.fill(0.f); - G_a_ref.im.fill(0.f); + std::array e; + std::array y; + FftData G_a; + FftData G_a_ref; + G_a_ref.re.fill(0.f); + G_a_ref.im.fill(0.f); - RunFilterUpdateTest(100, 65, blocks_with_saturation, &e, &y, &G_a); + RunFilterUpdateTest(100, 65, filter_length_blocks, blocks_with_saturation, + &e, &y, &G_a); - EXPECT_EQ(G_a_ref.re, G_a.re); - EXPECT_EQ(G_a_ref.im, G_a.im); + EXPECT_EQ(G_a_ref.re, G_a.re); + EXPECT_EQ(G_a_ref.im, G_a.im); + } } } // namespace webrtc diff --git a/modules/audio_processing/aec3/subtractor.cc b/modules/audio_processing/aec3/subtractor.cc index 8d91bd03d1..9e690f8491 100644 --- a/modules/audio_processing/aec3/subtractor.cc +++ b/modules/audio_processing/aec3/subtractor.cc @@ -45,13 +45,14 @@ void PredictionError(const Aec3Fft& fft, } } // namespace -Subtractor::Subtractor(ApmDataDumper* data_dumper, +Subtractor::Subtractor(const EchoCanceller3Config& config, + ApmDataDumper* data_dumper, Aec3Optimization optimization) : fft_(), data_dumper_(data_dumper), optimization_(optimization), - main_filter_(kAdaptiveFilterLength, optimization, data_dumper_), - shadow_filter_(kAdaptiveFilterLength, optimization, data_dumper_) { + main_filter_(config.filter.length_blocks, optimization, data_dumper_), + shadow_filter_(config.filter.length_blocks, optimization, data_dumper_) { RTC_DCHECK(data_dumper_); } diff --git a/modules/audio_processing/aec3/subtractor.h b/modules/audio_processing/aec3/subtractor.h index fe7928ea8a..12ae488222 100644 --- a/modules/audio_processing/aec3/subtractor.h +++ b/modules/audio_processing/aec3/subtractor.h @@ -33,7 +33,9 @@ namespace webrtc { // Proves linear echo cancellation functionality class Subtractor { public: - Subtractor(ApmDataDumper* data_dumper, Aec3Optimization optimization); + Subtractor(const EchoCanceller3Config& config, + ApmDataDumper* data_dumper, + Aec3Optimization optimization); ~Subtractor(); // Performs the echo subtraction. @@ -52,8 +54,7 @@ class Subtractor { } // Returns the estimate of the impulse response for the main adaptive filter. - const std::array& - FilterImpulseResponse() const { + const std::vector& FilterImpulseResponse() const { return main_filter_.FilterImpulseResponse(); } diff --git a/modules/audio_processing/aec3/subtractor_unittest.cc b/modules/audio_processing/aec3/subtractor_unittest.cc index a9fc9c2c1f..42801fe056 100644 --- a/modules/audio_processing/aec3/subtractor_unittest.cc +++ b/modules/audio_processing/aec3/subtractor_unittest.cc @@ -25,15 +25,17 @@ namespace { float RunSubtractorTest(int num_blocks_to_process, int delay_samples, + int filter_length_blocks, bool uncorrelated_inputs, const std::vector& blocks_with_echo_path_changes) { ApmDataDumper data_dumper(42); - Subtractor subtractor(&data_dumper, DetectOptimization()); + EchoCanceller3Config config; + config.filter.length_blocks = filter_length_blocks; + Subtractor subtractor(config, &data_dumper, DetectOptimization()); std::vector> x(3, std::vector(kBlockSize, 0.f)); std::vector y(kBlockSize, 0.f); std::array x_old; SubtractorOutput output; - EchoCanceller3Config config; config.delay.min_echo_path_delay_blocks = 0; config.delay.default_delay = 1; std::unique_ptr render_delay_buffer( @@ -44,7 +46,7 @@ float RunSubtractorTest(int num_blocks_to_process, std::array Y2; std::array E2_main; std::array E2_shadow; - AecState aec_state(EchoCanceller3Config{}); + AecState aec_state(config); x_old.fill(0.f); Y2.fill(0.f); E2_main.fill(0.f); @@ -98,9 +100,10 @@ float RunSubtractorTest(int num_blocks_to_process, return output_power / y_power; } -std::string ProduceDebugText(size_t delay) { +std::string ProduceDebugText(size_t delay, int filter_length_blocks) { std::ostringstream ss; - ss << "Delay: " << delay; + ss << "Delay: " << delay << ", "; + ss << "Length: " << filter_length_blocks; return ss.str(); } @@ -110,7 +113,8 @@ std::string ProduceDebugText(size_t delay) { // Verifies that the check for non data dumper works. TEST(Subtractor, NullDataDumper) { - EXPECT_DEATH(Subtractor(nullptr, DetectOptimization()), ""); + EXPECT_DEATH( + Subtractor(EchoCanceller3Config(), nullptr, DetectOptimization()), ""); } // Verifies the check for null subtractor output. @@ -118,32 +122,34 @@ TEST(Subtractor, NullDataDumper) { // tests on test bots has been fixed. TEST(Subtractor, DISABLED_NullOutput) { ApmDataDumper data_dumper(42); - Subtractor subtractor(&data_dumper, DetectOptimization()); + EchoCanceller3Config config; + Subtractor subtractor(config, &data_dumper, DetectOptimization()); std::unique_ptr render_delay_buffer( - RenderDelayBuffer::Create(EchoCanceller3Config(), 3)); + RenderDelayBuffer::Create(config, 3)); RenderSignalAnalyzer render_signal_analyzer; std::vector y(kBlockSize, 0.f); - EXPECT_DEATH(subtractor.Process(*render_delay_buffer->GetRenderBuffer(), y, - render_signal_analyzer, - AecState(EchoCanceller3Config{}), nullptr), - ""); + EXPECT_DEATH( + subtractor.Process(*render_delay_buffer->GetRenderBuffer(), y, + render_signal_analyzer, AecState(config), nullptr), + ""); } // Verifies the check for the capture signal size. TEST(Subtractor, WrongCaptureSize) { ApmDataDumper data_dumper(42); - Subtractor subtractor(&data_dumper, DetectOptimization()); + EchoCanceller3Config config; + Subtractor subtractor(config, &data_dumper, DetectOptimization()); std::unique_ptr render_delay_buffer( - RenderDelayBuffer::Create(EchoCanceller3Config(), 3)); + RenderDelayBuffer::Create(config, 3)); RenderSignalAnalyzer render_signal_analyzer; std::vector y(kBlockSize - 1, 0.f); SubtractorOutput output; - EXPECT_DEATH(subtractor.Process(*render_delay_buffer->GetRenderBuffer(), y, - render_signal_analyzer, - AecState(EchoCanceller3Config{}), &output), - ""); + EXPECT_DEATH( + subtractor.Process(*render_delay_buffer->GetRenderBuffer(), y, + render_signal_analyzer, AecState(config), &output), + ""); } #endif @@ -151,24 +157,30 @@ TEST(Subtractor, WrongCaptureSize) { // Verifies that the subtractor is able to converge on correlated data. TEST(Subtractor, Convergence) { std::vector blocks_with_echo_path_changes; - for (size_t delay_samples : {0, 64, 150, 200, 301}) { - SCOPED_TRACE(ProduceDebugText(delay_samples)); + for (size_t filter_length_blocks : {12, 20, 30}) { + for (size_t delay_samples : {0, 64, 150, 200, 301}) { + SCOPED_TRACE(ProduceDebugText(delay_samples, filter_length_blocks)); - float echo_to_nearend_power = RunSubtractorTest( - 100, delay_samples, false, blocks_with_echo_path_changes); - EXPECT_GT(0.1f, echo_to_nearend_power); + float echo_to_nearend_power = + RunSubtractorTest(300, delay_samples, filter_length_blocks, false, + blocks_with_echo_path_changes); + EXPECT_GT(0.1f, echo_to_nearend_power); + } } } // Verifies that the subtractor does not converge on uncorrelated signals. TEST(Subtractor, NonConvergenceOnUncorrelatedSignals) { std::vector blocks_with_echo_path_changes; - for (size_t delay_samples : {0, 64, 150, 200, 301}) { - SCOPED_TRACE(ProduceDebugText(delay_samples)); + for (size_t filter_length_blocks : {12, 20, 30}) { + for (size_t delay_samples : {0, 64, 150, 200, 301}) { + SCOPED_TRACE(ProduceDebugText(delay_samples, filter_length_blocks)); - float echo_to_nearend_power = RunSubtractorTest( - 100, delay_samples, true, blocks_with_echo_path_changes); - EXPECT_NEAR(1.f, echo_to_nearend_power, 0.05); + float echo_to_nearend_power = + RunSubtractorTest(100, delay_samples, filter_length_blocks, true, + blocks_with_echo_path_changes); + EXPECT_NEAR(1.f, echo_to_nearend_power, 0.05); + } } } @@ -177,12 +189,15 @@ TEST(Subtractor, NonConvergenceOnUncorrelatedSignals) { TEST(Subtractor, EchoPathChangeReset) { std::vector blocks_with_echo_path_changes; blocks_with_echo_path_changes.push_back(99); - for (size_t delay_samples : {0, 64, 150, 200, 301}) { - SCOPED_TRACE(ProduceDebugText(delay_samples)); + for (size_t filter_length_blocks : {12, 20, 30}) { + for (size_t delay_samples : {0, 64, 150, 200, 301}) { + SCOPED_TRACE(ProduceDebugText(delay_samples, filter_length_blocks)); - float echo_to_nearend_power = RunSubtractorTest( - 100, delay_samples, false, blocks_with_echo_path_changes); - EXPECT_NEAR(1.f, echo_to_nearend_power, 0.0000001f); + float echo_to_nearend_power = + RunSubtractorTest(100, delay_samples, filter_length_blocks, false, + blocks_with_echo_path_changes); + EXPECT_NEAR(1.f, echo_to_nearend_power, 0.0000001f); + } } } diff --git a/modules/audio_processing/aec3/suppression_gain_unittest.cc b/modules/audio_processing/aec3/suppression_gain_unittest.cc index 39a7be063f..e8cf6c856b 100644 --- a/modules/audio_processing/aec3/suppression_gain_unittest.cc +++ b/modules/audio_processing/aec3/suppression_gain_unittest.cc @@ -60,7 +60,7 @@ TEST(SuppressionGain, BasicGainComputation) { EchoCanceller3Config config; AecState aec_state(config); ApmDataDumper data_dumper(42); - Subtractor subtractor(&data_dumper, DetectOptimization()); + Subtractor subtractor(config, &data_dumper, DetectOptimization()); std::unique_ptr render_delay_buffer( RenderDelayBuffer::Create(config, 3)); diff --git a/modules/audio_processing/include/audio_processing.h b/modules/audio_processing/include/audio_processing.h index c353be9893..4aeb9f44a2 100644 --- a/modules/audio_processing/include/audio_processing.h +++ b/modules/audio_processing/include/audio_processing.h @@ -1158,6 +1158,10 @@ struct EchoCanceller3Config { size_t min_echo_path_delay_blocks = 5; } delay; + struct Filter { + size_t length_blocks = 12; + } filter; + struct Erle { float min = 1.f; float max_l = 8.f;