From 7e4ad828d6b8327c9abed630fc255857bcc8ec02 Mon Sep 17 00:00:00 2001 From: Gustaf Ullberg Date: Thu, 22 Oct 2020 14:36:37 +0200 Subject: [PATCH] Increased high frequency transparency MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Avoid excessive echo suppression in frequencies above 2 kHz when there is a dominant nearend. Calls with clock drift will not be affected by this change as they tend to have less accurate linear filters. Bug: webrtc:11985 Change-Id: Iddc628da5e2ba572c1b47acd87dd3be35260dca1 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/188580 Reviewed-by: Per Ã…hgren Commit-Queue: Gustaf Ullberg Cr-Commit-Position: refs/heads/master@{#32533} --- api/audio/echo_canceller3_config.h | 1 + api/audio/echo_canceller3_config_json.cc | 7 ++- .../audio_processing/aec3/echo_canceller3.cc | 4 ++ modules/audio_processing/aec3/echo_remover.cc | 6 +- .../audio_processing/aec3/suppression_gain.cc | 62 +++++++++++-------- .../audio_processing/aec3/suppression_gain.h | 2 + .../aec3/suppression_gain_unittest.cc | 8 +-- .../output_data_fixed.pb.sha1 | 2 +- .../output_data_float.pb.sha1 | 2 +- .../output_data_float_avx2.pb.sha1 | 2 +- 10 files changed, 61 insertions(+), 35 deletions(-) diff --git a/api/audio/echo_canceller3_config.h b/api/audio/echo_canceller3_config.h index af57d048a1..3ed11ff8b3 100644 --- a/api/audio/echo_canceller3_config.h +++ b/api/audio/echo_canceller3_config.h @@ -221,6 +221,7 @@ struct RTC_EXPORT EchoCanceller3Config { } high_bands_suppression; float floor_first_increase = 0.00001f; + bool conservative_hf_suppression = false; } suppressor; }; } // namespace webrtc diff --git a/api/audio/echo_canceller3_config_json.cc b/api/audio/echo_canceller3_config_json.cc index 6a99630e85..907b472714 100644 --- a/api/audio/echo_canceller3_config_json.cc +++ b/api/audio/echo_canceller3_config_json.cc @@ -383,6 +383,8 @@ void Aec3ConfigFromJsonString(absl::string_view json_string, ReadParam(section, "floor_first_increase", &cfg.suppressor.floor_first_increase); + ReadParam(section, "conservative_hf_suppression", + &cfg.suppressor.conservative_hf_suppression); } } @@ -676,7 +678,10 @@ std::string Aec3ConfigToJsonString(const EchoCanceller3Config& config) { ost << "\"anti_howling_gain\": " << config.suppressor.high_bands_suppression.anti_howling_gain; ost << "},"; - ost << "\"floor_first_increase\": " << config.suppressor.floor_first_increase; + ost << "\"floor_first_increase\": " << config.suppressor.floor_first_increase + << ","; + ost << "\"conservative_hf_suppression\": " + << config.suppressor.conservative_hf_suppression; ost << "}"; ost << "}"; ost << "}"; diff --git a/modules/audio_processing/aec3/echo_canceller3.cc b/modules/audio_processing/aec3/echo_canceller3.cc index d2847df126..98da232bba 100644 --- a/modules/audio_processing/aec3/echo_canceller3.cc +++ b/modules/audio_processing/aec3/echo_canceller3.cc @@ -368,6 +368,10 @@ EchoCanceller3Config AdjustConfig(const EchoCanceller3Config& config) { adjusted_cfg.suppressor.nearend_tuning.max_dec_factor_lf = .2f; } + if (field_trial::IsEnabled("WebRTC-Aec3EnforceConservativeHfSuppression")) { + adjusted_cfg.suppressor.conservative_hf_suppression = true; + } + if (field_trial::IsEnabled("WebRTC-Aec3EnforceStationarityProperties")) { adjusted_cfg.echo_audibility.use_stationarity_properties = true; } diff --git a/modules/audio_processing/aec3/echo_remover.cc b/modules/audio_processing/aec3/echo_remover.cc index a3cd22f21a..df539bfad0 100644 --- a/modules/audio_processing/aec3/echo_remover.cc +++ b/modules/audio_processing/aec3/echo_remover.cc @@ -414,12 +414,16 @@ void EchoRemoverImpl::ProcessCapture( const auto& echo_spectrum = aec_state_.UsableLinearEstimate() ? S2_linear : R2; + // Determine if the suppressor should assume clock drift. + const bool clock_drift = config_.echo_removal_control.has_clock_drift || + echo_path_variability.clock_drift; + // Compute preferred gains. float high_bands_gain; std::array G; suppression_gain_.GetGain(nearend_spectrum, echo_spectrum, R2, cng_.NoiseSpectrum(), render_signal_analyzer_, - aec_state_, x, &high_bands_gain, &G); + aec_state_, x, clock_drift, &high_bands_gain, &G); suppression_filter_.ApplyGain(comfort_noise, high_band_comfort_noise, G, high_bands_gain, Y_fft, y); diff --git a/modules/audio_processing/aec3/suppression_gain.cc b/modules/audio_processing/aec3/suppression_gain.cc index c1f12b7748..5b01c52908 100644 --- a/modules/audio_processing/aec3/suppression_gain.cc +++ b/modules/audio_processing/aec3/suppression_gain.cc @@ -27,39 +27,40 @@ namespace webrtc { namespace { -void PostprocessGains(std::array* gain) { - // TODO(gustaf): Investigate if this can be relaxed to achieve higher - // transparency above 2 kHz. - +void LimitLowFrequencyGains(std::array* gain) { // Limit the low frequency gains to avoid the impact of the high-pass filter // on the lower-frequency gain influencing the overall achieved gain. (*gain)[0] = (*gain)[1] = std::min((*gain)[1], (*gain)[2]); +} - // Limit the high frequency gains to avoid the impact of the anti-aliasing - // filter on the upper-frequency gains influencing the overall achieved - // gain. TODO(peah): Update this when new anti-aliasing filters are - // implemented. - constexpr size_t kAntiAliasingImpactLimit = (64 * 2000) / 8000; - const float min_upper_gain = (*gain)[kAntiAliasingImpactLimit]; +void LimitHighFrequencyGains(bool conservative_hf_suppression, + std::array* gain) { + // Limit the high frequency gains to avoid echo leakage due to an imperfect + // filter. + constexpr size_t kFirstBandToLimit = (64 * 2000) / 8000; + const float min_upper_gain = (*gain)[kFirstBandToLimit]; std::for_each( - gain->begin() + kAntiAliasingImpactLimit, gain->end() - 1, + gain->begin() + kFirstBandToLimit + 1, gain->end(), [min_upper_gain](float& a) { a = std::min(a, min_upper_gain); }); (*gain)[kFftLengthBy2] = (*gain)[kFftLengthBy2Minus1]; - // Limits the gain in the frequencies for which the adaptive filter has not - // converged. - // TODO(peah): Make adaptive to take the actual filter error into account. - constexpr size_t kUpperAccurateBandPlus1 = 29; + if (conservative_hf_suppression) { + // Limits the gain in the frequencies for which the adaptive filter has not + // converged. + // TODO(peah): Make adaptive to take the actual filter error into account. + constexpr size_t kUpperAccurateBandPlus1 = 29; - constexpr float oneByBandsInSum = - 1 / static_cast(kUpperAccurateBandPlus1 - 20); - const float hf_gain_bound = - std::accumulate(gain->begin() + 20, - gain->begin() + kUpperAccurateBandPlus1, 0.f) * - oneByBandsInSum; + constexpr float oneByBandsInSum = + 1 / static_cast(kUpperAccurateBandPlus1 - 20); + const float hf_gain_bound = + std::accumulate(gain->begin() + 20, + gain->begin() + kUpperAccurateBandPlus1, 0.f) * + oneByBandsInSum; - std::for_each(gain->begin() + kUpperAccurateBandPlus1, gain->end(), - [hf_gain_bound](float& a) { a = std::min(a, hf_gain_bound); }); + std::for_each( + gain->begin() + kUpperAccurateBandPlus1, gain->end(), + [hf_gain_bound](float& a) { a = std::min(a, hf_gain_bound); }); + } } // Scales the echo according to assessed audibility at the other end. @@ -265,6 +266,7 @@ void SuppressionGain::LowerBandGain( suppressor_input, rtc::ArrayView> residual_echo, rtc::ArrayView> comfort_noise, + bool clock_drift, std::array* gain) { gain->fill(1.f); const bool saturated_echo = aec_state.SaturatedEcho(); @@ -298,8 +300,14 @@ void SuppressionGain::LowerBandGain( last_echo_[ch].begin()); } - // Limit high-frequency gains. - PostprocessGains(gain); + LimitLowFrequencyGains(gain); + // Use conservative high-frequency gains during clock-drift or when not in + // dominant nearend. + if (!dominant_nearend_detector_->IsNearendState() || clock_drift || + config_.suppressor.conservative_hf_suppression) { + LimitHighFrequencyGains(config_.suppressor.conservative_hf_suppression, + gain); + } // Store computed gains. std::copy(gain->begin(), gain->end(), last_gain_.begin()); @@ -352,6 +360,7 @@ void SuppressionGain::GetGain( const RenderSignalAnalyzer& render_signal_analyzer, const AecState& aec_state, const std::vector>>& render, + bool clock_drift, float* high_bands_gain, std::array* low_band_gain) { RTC_DCHECK(high_bands_gain); @@ -364,7 +373,8 @@ void SuppressionGain::GetGain( // Compute gain for the lower band. bool low_noise_render = low_render_detector_.Detect(render); LowerBandGain(low_noise_render, aec_state, nearend_spectrum, - residual_echo_spectrum, comfort_noise_spectrum, low_band_gain); + residual_echo_spectrum, comfort_noise_spectrum, clock_drift, + low_band_gain); // Compute the gain for the upper bands. const absl::optional narrow_peak_band = diff --git a/modules/audio_processing/aec3/suppression_gain.h b/modules/audio_processing/aec3/suppression_gain.h index f46db0b7b2..e7175c36da 100644 --- a/modules/audio_processing/aec3/suppression_gain.h +++ b/modules/audio_processing/aec3/suppression_gain.h @@ -47,6 +47,7 @@ class SuppressionGain { const RenderSignalAnalyzer& render_signal_analyzer, const AecState& aec_state, const std::vector>>& render, + bool clock_drift, float* high_bands_gain, std::array* low_band_gain); @@ -76,6 +77,7 @@ class SuppressionGain { suppressor_input, rtc::ArrayView> residual_echo, rtc::ArrayView> comfort_noise, + bool clock_drift, std::array* gain); void GetMinGain(rtc::ArrayView weighted_residual_echo, diff --git a/modules/audio_processing/aec3/suppression_gain_unittest.cc b/modules/audio_processing/aec3/suppression_gain_unittest.cc index 4fb4cd7142..26bfc24ebb 100644 --- a/modules/audio_processing/aec3/suppression_gain_unittest.cc +++ b/modules/audio_processing/aec3/suppression_gain_unittest.cc @@ -49,7 +49,7 @@ TEST(SuppressionGainDeathTest, NullOutputGains) { std::vector>>( 3, std::vector>( 1, std::vector(kBlockSize, 0.f))), - &high_bands_gain, nullptr), + false, &high_bands_gain, nullptr), ""); } @@ -107,7 +107,7 @@ TEST(SuppressionGain, BasicGainComputation) { aec_state.Update(delay_estimate, subtractor.FilterFrequencyResponses(), subtractor.FilterImpulseResponses(), *render_delay_buffer->GetRenderBuffer(), E2, Y2, output); - suppression_gain.GetGain(E2, S2, R2, N2, analyzer, aec_state, x, + suppression_gain.GetGain(E2, S2, R2, N2, analyzer, aec_state, x, false, &high_bands_gain, &g); } std::for_each(g.begin(), g.end(), @@ -126,7 +126,7 @@ TEST(SuppressionGain, BasicGainComputation) { aec_state.Update(delay_estimate, subtractor.FilterFrequencyResponses(), subtractor.FilterImpulseResponses(), *render_delay_buffer->GetRenderBuffer(), E2, Y2, output); - suppression_gain.GetGain(E2, S2, R2, N2, analyzer, aec_state, x, + suppression_gain.GetGain(E2, S2, R2, N2, analyzer, aec_state, x, false, &high_bands_gain, &g); } std::for_each(g.begin(), g.end(), @@ -137,7 +137,7 @@ TEST(SuppressionGain, BasicGainComputation) { R2[1].fill(10000000000000.f); for (int k = 0; k < 10; ++k) { - suppression_gain.GetGain(E2, S2, R2, N2, analyzer, aec_state, x, + suppression_gain.GetGain(E2, S2, R2, N2, analyzer, aec_state, x, false, &high_bands_gain, &g); } std::for_each(g.begin(), g.end(), diff --git a/resources/audio_processing/output_data_fixed.pb.sha1 b/resources/audio_processing/output_data_fixed.pb.sha1 index f27905087e..43e68303ac 100644 --- a/resources/audio_processing/output_data_fixed.pb.sha1 +++ b/resources/audio_processing/output_data_fixed.pb.sha1 @@ -1 +1 @@ -4010b1fe15eda1b42968cdb3f9fed399e1aa7197 \ No newline at end of file +0ff9ab4d46929552e21d16f266f9eba42575ba8d \ No newline at end of file diff --git a/resources/audio_processing/output_data_float.pb.sha1 b/resources/audio_processing/output_data_float.pb.sha1 index b8312fc58f..6c3ab91815 100644 --- a/resources/audio_processing/output_data_float.pb.sha1 +++ b/resources/audio_processing/output_data_float.pb.sha1 @@ -1 +1 @@ -d22d4b0bc8f59aa27da61e158b9d35596f3844f5 \ No newline at end of file +ed1172c80a1a001a8aa7ac0680a99018cbb7d278 \ No newline at end of file diff --git a/resources/audio_processing/output_data_float_avx2.pb.sha1 b/resources/audio_processing/output_data_float_avx2.pb.sha1 index 539623e869..2d4ad0c141 100644 --- a/resources/audio_processing/output_data_float_avx2.pb.sha1 +++ b/resources/audio_processing/output_data_float_avx2.pb.sha1 @@ -1 +1 @@ -514543fbee78d0a71e87adb92e23138d762d1da8 \ No newline at end of file +a1dd718a6882bf8033a934e5beec73086cc91240 \ No newline at end of file