From 40659c3eaf3764b6eb2720e0454c98a1f3075df4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Per=20=C3=85hgren?= Date: Tue, 17 Oct 2017 12:56:21 +0200 Subject: [PATCH] Corrected and robustified the detection of the delay in the AEC3 filter MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This CL changes the filter delay detection to rely on the largest peak while the correctness of the filter is changed to be based on the performance achieved by the filter. Bug: webrtc:8397,chromium:774867 Change-Id: I70c953815192478f9a8e0da9f2b8fd9edac3f481 Reviewed-on: https://webrtc-review.googlesource.com/10803 Commit-Queue: Per Ã…hgren Reviewed-by: Gustaf Ullberg Cr-Commit-Position: refs/heads/master@{#20321} --- modules/audio_processing/aec3/aec_state.cc | 46 +++++-------------- .../aec3/aec_state_unittest.cc | 32 +------------ 2 files changed, 14 insertions(+), 64 deletions(-) diff --git a/modules/audio_processing/aec3/aec_state.cc b/modules/audio_processing/aec3/aec_state.cc index 01a5fc585e..14b83e10be 100644 --- a/modules/audio_processing/aec3/aec_state.cc +++ b/modules/audio_processing/aec3/aec_state.cc @@ -23,49 +23,27 @@ namespace webrtc { namespace { // Computes delay of the adaptive filter. -rtc::Optional EstimateFilterDelay( +int EstimateFilterDelay( const std::vector>& adaptive_filter_frequency_response) { const auto& H2 = adaptive_filter_frequency_response; - - size_t reliable_delays_sum = 0; - size_t num_reliable_delays = 0; - constexpr size_t kUpperBin = kFftLengthBy2 - 5; - constexpr float kMinPeakMargin = 10.f; - const size_t kTailPartition = H2.size() - 1; + RTC_DCHECK_GE(kAdaptiveFilterLength, H2.size()); + std::array delays; + delays.fill(0); for (size_t k = 1; k < kUpperBin; ++k) { // Find the maximum of H2[j]. - int peak = 0; + size_t peak = 0; for (size_t j = 0; j < H2.size(); ++j) { if (H2[j][k] > H2[peak][k]) { peak = j; } } - - // Count the peak as a delay only if the peak is sufficiently larger than - // the tail. - if (kMinPeakMargin * H2[kTailPartition][k] < H2[peak][k]) { - reliable_delays_sum += peak; - ++num_reliable_delays; - } + ++delays[peak]; } - // Return no delay if not sufficient delays have been found. - if (num_reliable_delays < 21) { - return rtc::Optional(); - } - - const size_t delay = reliable_delays_sum / num_reliable_delays; - // Sanity check that the peak is not caused by a false strong DC-component in - // the filter. - for (size_t k = 1; k < kUpperBin; ++k) { - if (H2[delay][k] > H2[delay][0]) { - RTC_DCHECK_GT(H2.size(), delay); - return rtc::Optional(delay); - } - } - return rtc::Optional(); + return std::distance(delays.begin(), + std::max_element(delays.begin(), delays.end())); } } // namespace @@ -130,14 +108,15 @@ void AecState::Update(const std::vector>& force_zero_gain_ = (++force_zero_gain_counter_) < kNumBlocksPerSecond / 5; // Estimate delays. - filter_delay_ = EstimateFilterDelay(adaptive_filter_frequency_response); + filter_delay_ = rtc::Optional( + EstimateFilterDelay(adaptive_filter_frequency_response)); external_delay_ = external_delay_samples ? rtc::Optional(*external_delay_samples / kBlockSize) : rtc::Optional(); // Update the ERL and ERLE measures. - if (filter_delay_ && capture_block_counter_ >= 2 * kNumBlocksPerSecond) { + if (converged_filter && capture_block_counter_ >= 2 * kNumBlocksPerSecond) { const auto& X2 = render_buffer.Spectrum(*filter_delay_); erle_estimator_.Update(X2, Y2, E2_main); erl_estimator_.Update(X2, Y2); @@ -171,8 +150,7 @@ void AecState::Update(const std::vector>& // Flag whether the linear filter estimate is usable. usable_linear_estimate_ = (!echo_saturation_) && (converged_filter || SufficientFilterUpdates()) && - filter_delay_ && capture_block_counter_ >= 2 * kNumBlocksPerSecond && - external_delay_; + capture_block_counter_ >= 2 * kNumBlocksPerSecond && external_delay_; // 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, flag that a diff --git a/modules/audio_processing/aec3/aec_state_unittest.cc b/modules/audio_processing/aec3/aec_state_unittest.cc index 9a84ef6136..4956456a8c 100644 --- a/modules/audio_processing/aec3/aec_state_unittest.cc +++ b/modules/audio_processing/aec3/aec_state_unittest.cc @@ -161,35 +161,6 @@ TEST(AecState, NormalUsage) { } } -// Verifies the a non-significant delay is correctly identified. -TEST(AecState, NonSignificantDelay) { - AecState state(AudioProcessing::Config::EchoCanceller3{}); - RenderBuffer render_buffer(Aec3Optimization::kNone, 3, 30, - std::vector(1, 30)); - std::array E2_main; - std::array Y2; - std::array x; - EchoPathVariability echo_path_variability(false, false); - std::array s; - s.fill(100.f); - x.fill(0.f); - - std::vector> frequency_response(30); - for (auto& v : frequency_response) { - v.fill(0.01f); - } - - std::array impulse_response; - impulse_response.fill(0.f); - - // Verify that a non-significant filter delay is identified correctly. - state.HandleEchoPathChange(echo_path_variability); - state.Update(frequency_response, impulse_response, true, - rtc::Optional(), render_buffer, E2_main, Y2, x, s, - false); - EXPECT_FALSE(state.FilterDelay()); -} - // Verifies the delay for a converged filter is correctly identified. TEST(AecState, ConvergedFilterDelay) { constexpr int kFilterLength = 10; @@ -243,7 +214,8 @@ TEST(AecState, ExternalDelay) { x.fill(0.f); RenderBuffer render_buffer(Aec3Optimization::kNone, 3, 30, std::vector(1, 30)); - std::vector> frequency_response(30); + std::vector> frequency_response( + kAdaptiveFilterLength); for (auto& v : frequency_response) { v.fill(0.01f); }