AEC3: Prevent diverging coarse filter from influencing the refined filter
After the refined filter has been determined to perform better than the coarse filter, and the coefficients of the coarse filters are overwritten by the ones from the refined filter, at least 100 ms have to pass before the adaptation of the refined filter is allowed to speed up due to good coarse filter performance. This change solves the vicious circle described in webrtc:12265, where the coarse and refined filters can diverge over time. This feature can be disabled remotely via a kill-switch. When disabled the AEC output is bit-exact to before the change. Bug: webrtc:12265,chromium:1155477 Change-Id: Iacd6e325e987dd8a475bb3e8163fee714c65b20a Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/196501 Reviewed-by: Per Åhgren <peah@webrtc.org> Commit-Queue: Gustaf Ullberg <gustaf@webrtc.org> Cr-Commit-Position: refs/heads/master@{#32801}
This commit is contained in:
committed by
Commit Bot
parent
04ed0a0773
commit
992a96f68e
@ -322,6 +322,11 @@ void AecState::Update(
|
||||
external_delay ? 1 : 0);
|
||||
data_dumper_->DumpRaw("aec3_filter_tail_freq_resp_est",
|
||||
GetReverbFrequencyResponse());
|
||||
data_dumper_->DumpRaw("aec3_subtractor_y2", subtractor_output[0].y2);
|
||||
data_dumper_->DumpRaw("aec3_subtractor_e2_coarse",
|
||||
subtractor_output[0].e2_coarse);
|
||||
data_dumper_->DumpRaw("aec3_subtractor_e2_refined",
|
||||
subtractor_output[0].e2_refined);
|
||||
}
|
||||
|
||||
AecState::InitialState::InitialState(const EchoCanceller3Config& config)
|
||||
|
||||
@ -73,6 +73,7 @@ void RefinedFilterUpdateGain::Compute(
|
||||
rtc::ArrayView<const float> erl,
|
||||
size_t size_partitions,
|
||||
bool saturated_capture_signal,
|
||||
bool disallow_leakage_diverged,
|
||||
FftData* gain_fft) {
|
||||
RTC_DCHECK(gain_fft);
|
||||
// Introducing shorter notation to improve readability.
|
||||
@ -125,7 +126,7 @@ void RefinedFilterUpdateGain::Compute(
|
||||
|
||||
// H_error = H_error + factor * erl.
|
||||
for (size_t k = 0; k < kFftLengthBy2Plus1; ++k) {
|
||||
if (E2_coarse[k] >= E2_refined[k]) {
|
||||
if (E2_refined[k] <= E2_coarse[k] || disallow_leakage_diverged) {
|
||||
H_error_[k] += current_config_.leakage_converged * erl[k];
|
||||
} else {
|
||||
H_error_[k] += current_config_.leakage_diverged * erl[k];
|
||||
|
||||
@ -51,6 +51,7 @@ class RefinedFilterUpdateGain {
|
||||
rtc::ArrayView<const float> erl,
|
||||
size_t size_partitions,
|
||||
bool saturated_capture_signal,
|
||||
bool disallow_leakage_diverged,
|
||||
FftData* gain_fft);
|
||||
|
||||
// Sets a new config.
|
||||
|
||||
@ -196,7 +196,8 @@ void RunFilterUpdateTest(int num_blocks_to_process,
|
||||
std::array<float, kFftLengthBy2Plus1> erl;
|
||||
ComputeErl(optimization, H2[0], erl);
|
||||
refined_gain.Compute(render_power, render_signal_analyzer, output[0], erl,
|
||||
refined_filter.SizePartitions(), saturation, &G);
|
||||
refined_filter.SizePartitions(), saturation, false,
|
||||
&G);
|
||||
refined_filter.Adapt(*render_delay_buffer->GetRenderBuffer(), G, &h[0]);
|
||||
|
||||
// Update the delay.
|
||||
@ -247,7 +248,7 @@ TEST(RefinedFilterUpdateGainDeathTest, NullDataOutputGain) {
|
||||
erl.fill(0.f);
|
||||
EXPECT_DEATH(
|
||||
gain.Compute(render_power, analyzer, output, erl,
|
||||
config.filter.refined.length_blocks, false, nullptr),
|
||||
config.filter.refined.length_blocks, false, false, nullptr),
|
||||
"");
|
||||
}
|
||||
|
||||
|
||||
@ -19,11 +19,17 @@
|
||||
#include "modules/audio_processing/logging/apm_data_dumper.h"
|
||||
#include "rtc_base/checks.h"
|
||||
#include "rtc_base/numerics/safe_minmax.h"
|
||||
#include "system_wrappers/include/field_trial.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
namespace {
|
||||
|
||||
bool UseCoarseFilterResetHangover() {
|
||||
return !field_trial::IsEnabled(
|
||||
"WebRTC-Aec3CoarseFilterResetHangoverKillSwitch");
|
||||
}
|
||||
|
||||
void PredictionError(const Aec3Fft& fft,
|
||||
const FftData& S,
|
||||
rtc::ArrayView<const float> y,
|
||||
@ -66,12 +72,14 @@ Subtractor::Subtractor(const EchoCanceller3Config& config,
|
||||
optimization_(optimization),
|
||||
config_(config),
|
||||
num_capture_channels_(num_capture_channels),
|
||||
use_coarse_filter_reset_hangover_(UseCoarseFilterResetHangover()),
|
||||
refined_filters_(num_capture_channels_),
|
||||
coarse_filter_(num_capture_channels_),
|
||||
refined_gains_(num_capture_channels_),
|
||||
coarse_gains_(num_capture_channels_),
|
||||
filter_misadjustment_estimators_(num_capture_channels_),
|
||||
poor_coarse_filter_counters_(num_capture_channels_, 0),
|
||||
coarse_filter_reset_hangover_(num_capture_channels_, 0),
|
||||
refined_frequency_responses_(
|
||||
num_capture_channels_,
|
||||
std::vector<std::array<float, kFftLengthBy2Plus1>>(
|
||||
@ -228,11 +236,19 @@ void Subtractor::Process(const RenderBuffer& render_buffer,
|
||||
|
||||
// Update the refined filter.
|
||||
if (!refined_filters_adjusted) {
|
||||
// Do not allow the performance of the coarse filter to affect the
|
||||
// adaptation speed of the refined filter just after the coarse filter has
|
||||
// been reset.
|
||||
const bool disallow_leakage_diverged =
|
||||
coarse_filter_reset_hangover_[ch] > 0 &&
|
||||
use_coarse_filter_reset_hangover_;
|
||||
|
||||
std::array<float, kFftLengthBy2Plus1> erl;
|
||||
ComputeErl(optimization_, refined_frequency_responses_[ch], erl);
|
||||
refined_gains_[ch]->Compute(X2_refined, render_signal_analyzer, output,
|
||||
erl, refined_filters_[ch]->SizePartitions(),
|
||||
aec_state.SaturatedCapture(), &G);
|
||||
aec_state.SaturatedCapture(),
|
||||
disallow_leakage_diverged, &G);
|
||||
} else {
|
||||
G.re.fill(0.f);
|
||||
G.im.fill(0.f);
|
||||
@ -256,6 +272,8 @@ void Subtractor::Process(const RenderBuffer& render_buffer,
|
||||
coarse_gains_[ch]->Compute(X2_coarse, render_signal_analyzer, E_coarse,
|
||||
coarse_filter_[ch]->SizePartitions(),
|
||||
aec_state.SaturatedCapture(), &G);
|
||||
coarse_filter_reset_hangover_[ch] =
|
||||
std::max(coarse_filter_reset_hangover_[ch] - 1, 0);
|
||||
} else {
|
||||
poor_coarse_filter_counters_[ch] = 0;
|
||||
coarse_filter_[ch]->SetFilter(refined_filters_[ch]->SizePartitions(),
|
||||
@ -263,6 +281,8 @@ void Subtractor::Process(const RenderBuffer& render_buffer,
|
||||
coarse_gains_[ch]->Compute(X2_coarse, render_signal_analyzer, E_refined,
|
||||
coarse_filter_[ch]->SizePartitions(),
|
||||
aec_state.SaturatedCapture(), &G);
|
||||
coarse_filter_reset_hangover_[ch] =
|
||||
config_.filter.coarse_reset_hangover_blocks;
|
||||
}
|
||||
|
||||
coarse_filter_[ch]->Adapt(render_buffer, G);
|
||||
|
||||
@ -120,6 +120,7 @@ class Subtractor {
|
||||
const Aec3Optimization optimization_;
|
||||
const EchoCanceller3Config config_;
|
||||
const size_t num_capture_channels_;
|
||||
const bool use_coarse_filter_reset_hangover_;
|
||||
|
||||
std::vector<std::unique_ptr<AdaptiveFirFilter>> refined_filters_;
|
||||
std::vector<std::unique_ptr<AdaptiveFirFilter>> coarse_filter_;
|
||||
@ -127,6 +128,7 @@ class Subtractor {
|
||||
std::vector<std::unique_ptr<CoarseFilterUpdateGain>> coarse_gains_;
|
||||
std::vector<FilterMisadjustmentEstimator> filter_misadjustment_estimators_;
|
||||
std::vector<size_t> poor_coarse_filter_counters_;
|
||||
std::vector<int> coarse_filter_reset_hangover_;
|
||||
std::vector<std::vector<std::array<float, kFftLengthBy2Plus1>>>
|
||||
refined_frequency_responses_;
|
||||
std::vector<std::vector<float>> refined_impulse_responses_;
|
||||
|
||||
Reference in New Issue
Block a user