From ebe7778ce9c28c2160dd6770cc58884a908382dd Mon Sep 17 00:00:00 2001 From: peah Date: Mon, 27 Feb 2017 07:29:21 -0800 Subject: [PATCH] Further tuning for AEC3 for initial echo suppression and handling of echo path changes. This CL add tuning for the AEC3 that 1) Improves the handling of the initial echo suppression before the linear filter is reliable. 2) Improves the handling of echo path changes. There are also minor bugfixes included. BUG=webrtc:6018 Review-Url: https://codereview.webrtc.org/2717353002 Cr-Commit-Position: refs/heads/master@{#16873} --- .../audio_processing/aec3/aec_state.cc | 5 ++- .../modules/audio_processing/aec3/aec_state.h | 5 +++ .../audio_processing/aec3/echo_canceller3.cc | 2 +- .../audio_processing/aec3/echo_remover.cc | 1 + .../audio_processing/aec3/power_echo_model.cc | 2 +- .../aec3/residual_echo_estimator.cc | 43 +++++++++++++------ .../aec3/residual_echo_estimator.h | 3 +- 7 files changed, 44 insertions(+), 17 deletions(-) diff --git a/webrtc/modules/audio_processing/aec3/aec_state.cc b/webrtc/modules/audio_processing/aec3/aec_state.cc index c18fd6d870..e43cfc4795 100644 --- a/webrtc/modules/audio_processing/aec3/aec_state.cc +++ b/webrtc/modules/audio_processing/aec3/aec_state.cc @@ -81,7 +81,7 @@ void AnalyzeFilter( constexpr int kActiveRenderCounterInitial = 50; constexpr int kActiveRenderCounterMax = 200; constexpr int kEchoPathChangeCounterInitial = 50; -constexpr int kEchoPathChangeCounterMax = 200; +constexpr int kEchoPathChangeCounterMax = 3 * 250; } // namespace @@ -120,6 +120,9 @@ void AecState::Update(const std::vector>& const float x_energy = std::inner_product(x.begin(), x.end(), x.begin(), 0.f); + active_render_blocks_ = + echo_path_variability.AudioPathChanged() ? 0 : active_render_blocks_ + 1; + echo_path_change_counter_ = echo_path_variability.AudioPathChanged() ? kEchoPathChangeCounterMax : echo_path_change_counter_ - 1; diff --git a/webrtc/modules/audio_processing/aec3/aec_state.h b/webrtc/modules/audio_processing/aec3/aec_state.h index e3502b4a4f..56fee2cf8f 100644 --- a/webrtc/modules/audio_processing/aec3/aec_state.h +++ b/webrtc/modules/audio_processing/aec3/aec_state.h @@ -47,6 +47,10 @@ class AecState { // Returns whether the render signal is currently active. bool ActiveRender() const { return active_render_counter_ > 0; } + // Returns whether the number of active render blocks since an echo path + // change. + size_t ActiveRenderBlocks() const { return active_render_blocks_; } + // Returns the ERLE. const std::array& Erle() const { return erle_estimator_.Erle(); @@ -108,6 +112,7 @@ class AecState { ErleEstimator erle_estimator_; int echo_path_change_counter_; int active_render_counter_; + size_t active_render_blocks_ = 0; bool usable_linear_estimate_ = false; bool echo_leakage_detected_ = false; bool model_based_aec_feasible_ = false; diff --git a/webrtc/modules/audio_processing/aec3/echo_canceller3.cc b/webrtc/modules/audio_processing/aec3/echo_canceller3.cc index 60efced0ec..ed12a47995 100644 --- a/webrtc/modules/audio_processing/aec3/echo_canceller3.cc +++ b/webrtc/modules/audio_processing/aec3/echo_canceller3.cc @@ -260,7 +260,7 @@ bool EchoCanceller3::AnalyzeRender(AudioBuffer* render) { void EchoCanceller3::AnalyzeCapture(AudioBuffer* capture) { RTC_DCHECK_RUNS_SERIALIZED(&capture_race_checker_); RTC_DCHECK(capture); - data_dumper_->DumpWav("aec3_capture_analyze_input", frame_length_, + data_dumper_->DumpWav("aec3_capture_analyze_input", capture->num_frames(), capture->channels_f()[0], sample_rate_hz_, 1); saturated_microphone_signal_ = false; diff --git a/webrtc/modules/audio_processing/aec3/echo_remover.cc b/webrtc/modules/audio_processing/aec3/echo_remover.cc index 47ff4c2e7a..2d6328655c 100644 --- a/webrtc/modules/audio_processing/aec3/echo_remover.cc +++ b/webrtc/modules/audio_processing/aec3/echo_remover.cc @@ -143,6 +143,7 @@ void EchoRemoverImpl::ProcessBlock( if (echo_path_variability.AudioPathChanged()) { subtractor_.HandleEchoPathChange(echo_path_variability); power_echo_model_.HandleEchoPathChange(echo_path_variability); + residual_echo_estimator_.HandleEchoPathChange(echo_path_variability); } std::array Y2; diff --git a/webrtc/modules/audio_processing/aec3/power_echo_model.cc b/webrtc/modules/audio_processing/aec3/power_echo_model.cc index 8ad5486e07..9bdc971b9d 100644 --- a/webrtc/modules/audio_processing/aec3/power_echo_model.cc +++ b/webrtc/modules/audio_processing/aec3/power_echo_model.cc @@ -28,7 +28,7 @@ void RecentMaximum(const FftBuffer& X_buffer, } } -constexpr float kHInitial = 10.f; +constexpr float kHInitial = 100.f; constexpr int kUpdateCounterInitial = 300; } // namespace diff --git a/webrtc/modules/audio_processing/aec3/residual_echo_estimator.cc b/webrtc/modules/audio_processing/aec3/residual_echo_estimator.cc index 38d5beb5fb..18a07b2f5a 100644 --- a/webrtc/modules/audio_processing/aec3/residual_echo_estimator.cc +++ b/webrtc/modules/audio_processing/aec3/residual_echo_estimator.cc @@ -20,15 +20,16 @@ namespace { constexpr float kSaturationLeakageFactor = 10.f; constexpr size_t kSaturationLeakageBlocks = 10; +constexpr size_t kEchoPathChangeConvergenceBlocks = 3 * 250; // Estimates the residual echo power when there is no detection correlation // between the render and capture signals. void InfiniteErlPowerEstimate( - size_t active_render_counter, + size_t active_render_blocks, size_t blocks_since_last_saturation, const std::array& S2_fallback, std::array* R2) { - if (active_render_counter > 5 * 250) { + if (active_render_blocks > 5 * 250) { // After an amount of active render samples for which an echo should have // been detected in the capture signal if the ERL was not infinite, set the // residual echo to 0. @@ -62,6 +63,7 @@ void GainBasedPowerEstimate( size_t external_delay, const FftBuffer& X_buffer, size_t blocks_since_last_saturation, + size_t active_render_blocks, const std::array& bands_with_reliable_filter, const std::array& echo_path_gain, const std::array& S2_fallback, @@ -71,10 +73,17 @@ void GainBasedPowerEstimate( // Base the residual echo power on gain of the linear echo path estimate if // that is reliable, otherwise use the fallback echo path estimate. Add a // leakage factor when there is saturation. - for (size_t k = 0; k < R2->size(); ++k) { - (*R2)[k] = bands_with_reliable_filter[k] ? echo_path_gain[k] * X2[k] - : S2_fallback[k]; + if (active_render_blocks > kEchoPathChangeConvergenceBlocks) { + for (size_t k = 0; k < R2->size(); ++k) { + (*R2)[k] = bands_with_reliable_filter[k] ? echo_path_gain[k] * X2[k] + : S2_fallback[k]; + } + } else { + for (size_t k = 0; k < R2->size(); ++k) { + (*R2)[k] = S2_fallback[k]; + } } + if (blocks_since_last_saturation < kSaturationLeakageBlocks) { std::for_each(R2->begin(), R2->end(), [](float& a) { a *= kSaturationLeakageFactor; }); @@ -145,7 +154,7 @@ void ErleBasedPowerEstimate( } // namespace ResidualEchoEstimator::ResidualEchoEstimator() { - echo_path_gain_.fill(0.f); + echo_path_gain_.fill(100.f); } ResidualEchoEstimator::~ResidualEchoEstimator() = default; @@ -169,6 +178,10 @@ void ResidualEchoEstimator::Estimate( if (linear_filter_based_delay) { std::copy(H2[*linear_filter_based_delay].begin(), H2[*linear_filter_based_delay].end(), echo_path_gain_.begin()); + constexpr float kEchoPathGainHeadroom = 10.f; + std::for_each( + echo_path_gain_.begin(), echo_path_gain_.end(), + [kEchoPathGainHeadroom](float& a) { a *= kEchoPathGainHeadroom; }); } // Counts the blocks since saturation. @@ -178,11 +191,6 @@ void ResidualEchoEstimator::Estimate( ++blocks_since_last_saturation_; } - // Counts the number of active render blocks that are in a row. - if (aec_state.ActiveRender()) { - ++active_render_counter_; - } - const auto& bands_with_reliable_filter = aec_state.BandsWithReliableFilter(); if (aec_state.UsableLinearEstimate()) { @@ -200,16 +208,25 @@ void ResidualEchoEstimator::Estimate( RTC_DCHECK(aec_state.ExternalDelay()); GainBasedPowerEstimate( *aec_state.ExternalDelay(), X_buffer, blocks_since_last_saturation_, - bands_with_reliable_filter, echo_path_gain_, S2_fallback, R2); + aec_state.ActiveRenderBlocks(), bands_with_reliable_filter, + echo_path_gain_, S2_fallback, R2); } else if (aec_state.EchoLeakageDetected()) { // Residual echo power when an external residual echo detection algorithm // has deemed the echo canceller to leak echoes. HalfDuplexPowerEstimate(aec_state.ActiveRender(), Y2, R2); } else { // Residual echo power when none of the other cases are fulfilled. - InfiniteErlPowerEstimate(active_render_counter_, + InfiniteErlPowerEstimate(aec_state.ActiveRenderBlocks(), blocks_since_last_saturation_, S2_fallback, R2); } } +void ResidualEchoEstimator::HandleEchoPathChange( + const EchoPathVariability& echo_path_variability) { + if (echo_path_variability.AudioPathChanged()) { + blocks_since_last_saturation_ = 0; + echo_path_gain_.fill(100.f); + } +} + } // namespace webrtc diff --git a/webrtc/modules/audio_processing/aec3/residual_echo_estimator.h b/webrtc/modules/audio_processing/aec3/residual_echo_estimator.h index 6c59f434b9..a4f85c40c2 100644 --- a/webrtc/modules/audio_processing/aec3/residual_echo_estimator.h +++ b/webrtc/modules/audio_processing/aec3/residual_echo_estimator.h @@ -39,9 +39,10 @@ class ResidualEchoEstimator { const std::array& Y2, std::array* R2); + void HandleEchoPathChange(const EchoPathVariability& echo_path_variability); + private: std::array echo_path_gain_; - size_t active_render_counter_ = 0; size_t blocks_since_last_saturation_ = 1000; RTC_DISALLOW_COPY_AND_ASSIGN(ResidualEchoEstimator);