From 6d822adac4439258b0a07c5458620fcf2314874f Mon Sep 17 00:00:00 2001 From: peah Date: Mon, 10 Apr 2017 13:52:14 -0700 Subject: [PATCH] Added forced zero AEC output after call startup and echo path changes During the first few capture frames, there is no way for the AEC to tell whether there is echo in the capture signal as the echo removal functionality in the AEC has not yet seen any render signal. To avoid initial echo bursts due to this, this CL adds functionality for forcing the echo suppression gain to zero during the first 50 blocks (200 ms) after call start and after a reported echo path change. BUG=webrtc:6018 Review-Url: https://codereview.webrtc.org/2808733002 Cr-Commit-Position: refs/heads/master@{#17624} --- .../modules/audio_processing/aec3/aec_state.cc | 11 +++++++++++ webrtc/modules/audio_processing/aec3/aec_state.h | 5 +++++ .../audio_processing/aec3/echo_remover.cc | 2 +- .../audio_processing/aec3/suppression_gain.cc | 10 ++++++++++ .../audio_processing/aec3/suppression_gain.h | 1 + .../aec3/suppression_gain_unittest.cc | 16 ++++++++++++---- 6 files changed, 40 insertions(+), 5 deletions(-) diff --git a/webrtc/modules/audio_processing/aec3/aec_state.cc b/webrtc/modules/audio_processing/aec3/aec_state.cc index 8e92f5fbda..1a9f66ffcb 100644 --- a/webrtc/modules/audio_processing/aec3/aec_state.cc +++ b/webrtc/modules/audio_processing/aec3/aec_state.cc @@ -97,6 +97,11 @@ void AecState::HandleEchoPathChange( echo_saturation_ = false; headset_detected_ = false; previous_max_sample_ = 0.f; + + if (echo_path_variability.delay_change) { + force_zero_gain_counter_ = 0; + force_zero_gain_ = true; + } } } @@ -117,6 +122,12 @@ void AecState::Update(const std::vector>& active_render_blocks_ += active_render_block ? 1 : 0; --echo_path_change_counter_; + // Force zero echo suppression gain after an echo path change to allow at + // least some render data to be collected in order to avoid an initial echo + // burst. + constexpr size_t kZeroGainBlocksAfterChange = kNumBlocksPerSecond / 5; + force_zero_gain_ = (++force_zero_gain_counter_) < kZeroGainBlocksAfterChange; + // Estimate delays. filter_delay_ = EstimateFilterDelay(adaptive_filter_frequency_response); external_delay_ = diff --git a/webrtc/modules/audio_processing/aec3/aec_state.h b/webrtc/modules/audio_processing/aec3/aec_state.h index 00b62529f6..387c6ea42d 100644 --- a/webrtc/modules/audio_processing/aec3/aec_state.h +++ b/webrtc/modules/audio_processing/aec3/aec_state.h @@ -80,6 +80,9 @@ class AecState { // TODO(peah): Make this adaptive. float ReverbDecayFactor() const { return 0.f; } + // Returns whether the echo suppression gain should be forced to zero. + bool ForcedZeroGain() const { return force_zero_gain_; } + // Updates the aec state. void Update(const std::vector>& adaptive_filter_frequency_response, @@ -103,6 +106,8 @@ class AecState { bool echo_saturation_ = false; bool headset_detected_ = false; float previous_max_sample_ = 0.f; + bool force_zero_gain_ = false; + size_t force_zero_gain_counter_ = 0; rtc::Optional filter_delay_; rtc::Optional external_delay_; size_t blocks_since_last_saturation_ = 1000; diff --git a/webrtc/modules/audio_processing/aec3/echo_remover.cc b/webrtc/modules/audio_processing/aec3/echo_remover.cc index 2b28a21751..ea4025365a 100644 --- a/webrtc/modules/audio_processing/aec3/echo_remover.cc +++ b/webrtc/modules/audio_processing/aec3/echo_remover.cc @@ -182,7 +182,7 @@ void EchoRemoverImpl::ProcessCapture( // A choose and apply echo suppression gain. suppression_gain_.GetGain(E2, R2, cng_.NoiseSpectrum(), aec_state_.SaturatedEcho(), x, y->size(), - &high_bands_gain, &G); + aec_state_.ForcedZeroGain(), &high_bands_gain, &G); suppression_filter_.ApplyGain(comfort_noise, high_band_comfort_noise, G, high_bands_gain, y); diff --git a/webrtc/modules/audio_processing/aec3/suppression_gain.cc b/webrtc/modules/audio_processing/aec3/suppression_gain.cc index 0e50292008..4bf452cbbe 100644 --- a/webrtc/modules/audio_processing/aec3/suppression_gain.cc +++ b/webrtc/modules/audio_processing/aec3/suppression_gain.cc @@ -325,11 +325,21 @@ void SuppressionGain::GetGain( bool saturated_echo, const std::vector>& render, size_t num_capture_bands, + bool force_zero_gain, float* high_bands_gain, std::array* low_band_gain) { RTC_DCHECK(high_bands_gain); RTC_DCHECK(low_band_gain); + if (force_zero_gain) { + previous_gain_squared_.fill(0.f); + std::copy(comfort_noise_power.begin() + 1, comfort_noise_power.end() - 1, + previous_masker_.begin()); + low_band_gain->fill(0.f); + *high_bands_gain = 0.f; + return; + } + // Choose margin to use. const float margin = saturated_echo ? 0.001f : 0.01f; switch (optimization_) { diff --git a/webrtc/modules/audio_processing/aec3/suppression_gain.h b/webrtc/modules/audio_processing/aec3/suppression_gain.h index 6b36a63399..d0b4114393 100644 --- a/webrtc/modules/audio_processing/aec3/suppression_gain.h +++ b/webrtc/modules/audio_processing/aec3/suppression_gain.h @@ -52,6 +52,7 @@ class SuppressionGain { bool saturated_echo, const std::vector>& render, size_t num_capture_bands, + bool force_zero_gain, float* high_bands_gain, std::array* low_band_gain); diff --git a/webrtc/modules/audio_processing/aec3/suppression_gain_unittest.cc b/webrtc/modules/audio_processing/aec3/suppression_gain_unittest.cc index f4feb74a77..83c41e1254 100644 --- a/webrtc/modules/audio_processing/aec3/suppression_gain_unittest.cc +++ b/webrtc/modules/audio_processing/aec3/suppression_gain_unittest.cc @@ -33,7 +33,7 @@ TEST(SuppressionGain, NullOutputGains) { .GetGain(E2, R2, N2, false, std::vector>( 3, std::vector(kBlockSize, 0.f)), - 1, &high_bands_gain, nullptr), + 1, false, &high_bands_gain, nullptr), ""); } @@ -128,7 +128,8 @@ TEST(SuppressionGain, BasicGainComputation) { R2.fill(0.1f); N2.fill(100.f); for (int k = 0; k < 10; ++k) { - suppression_gain.GetGain(E2, R2, N2, false, x, 1, &high_bands_gain, &g); + suppression_gain.GetGain(E2, R2, N2, false, x, 1, false, &high_bands_gain, + &g); } std::for_each(g.begin(), g.end(), [](float a) { EXPECT_NEAR(1.f, a, 0.001); }); @@ -138,7 +139,8 @@ TEST(SuppressionGain, BasicGainComputation) { R2.fill(0.1f); N2.fill(0.f); for (int k = 0; k < 10; ++k) { - suppression_gain.GetGain(E2, R2, N2, false, x, 1, &high_bands_gain, &g); + suppression_gain.GetGain(E2, R2, N2, false, x, 1, false, &high_bands_gain, + &g); } std::for_each(g.begin(), g.end(), [](float a) { EXPECT_NEAR(1.f, a, 0.001); }); @@ -148,10 +150,16 @@ TEST(SuppressionGain, BasicGainComputation) { R2.fill(100.f); N2.fill(0.f); for (int k = 0; k < 10; ++k) { - suppression_gain.GetGain(E2, R2, N2, false, x, 1, &high_bands_gain, &g); + suppression_gain.GetGain(E2, R2, N2, false, x, 1, false, &high_bands_gain, + &g); } std::for_each(g.begin(), g.end(), [](float a) { EXPECT_NEAR(0.f, a, 0.001); }); + + // Verify the functionality for forcing a zero gain. + suppression_gain.GetGain(E2, R2, N2, false, x, 1, true, &high_bands_gain, &g); + std::for_each(g.begin(), g.end(), [](float a) { EXPECT_FLOAT_EQ(0.f, a); }); + EXPECT_FLOAT_EQ(0.f, high_bands_gain); } } // namespace aec3