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:
Gustaf Ullberg
2020-12-08 13:03:55 +01:00
committed by Commit Bot
parent 04ed0a0773
commit 992a96f68e
11 changed files with 42 additions and 6 deletions

View File

@ -153,6 +153,7 @@ bool EchoCanceller3Config::Validate(EchoCanceller3Config* config) {
res = res & Limit(&c->filter.config_change_duration_blocks, 0, 100000);
res = res & Limit(&c->filter.initial_state_seconds, 0.f, 100.f);
res = res & Limit(&c->filter.coarse_reset_hangover_blocks, 0, 2500);
res = res & Limit(&c->erle.min, 1.f, 100000.f);
res = res & Limit(&c->erle.max_l, 1.f, 100000.f);

View File

@ -86,6 +86,7 @@ struct RTC_EXPORT EchoCanceller3Config {
size_t config_change_duration_blocks = 250;
float initial_state_seconds = 2.5f;
int coarse_reset_hangover_blocks = 25;
bool conservative_initial_phase = false;
bool enable_coarse_filter_output_usage = true;
bool use_linear_filter = true;

View File

@ -223,6 +223,8 @@ void Aec3ConfigFromJsonString(absl::string_view json_string,
&cfg.filter.config_change_duration_blocks);
ReadParam(section, "initial_state_seconds",
&cfg.filter.initial_state_seconds);
ReadParam(section, "coarse_reset_hangover_blocks",
&cfg.filter.coarse_reset_hangover_blocks);
ReadParam(section, "conservative_initial_phase",
&cfg.filter.conservative_initial_phase);
ReadParam(section, "enable_coarse_filter_output_usage",
@ -502,6 +504,8 @@ std::string Aec3ConfigToJsonString(const EchoCanceller3Config& config) {
<< config.filter.config_change_duration_blocks << ",";
ost << "\"initial_state_seconds\": " << config.filter.initial_state_seconds
<< ",";
ost << "\"coarse_reset_hangover_blocks\": "
<< config.filter.coarse_reset_hangover_blocks << ",";
ost << "\"conservative_initial_phase\": "
<< (config.filter.conservative_initial_phase ? "true" : "false") << ",";
ost << "\"enable_coarse_filter_output_usage\": "

View File

@ -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)

View File

@ -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];

View File

@ -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.

View File

@ -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),
"");
}

View File

@ -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);

View File

@ -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_;

View File

@ -1 +1 @@
ed1172c80a1a001a8aa7ac0680a99018cbb7d278
365a02046fdb30357d649e73766d2f6eb2b33677

View File

@ -1 +1 @@
a1dd718a6882bf8033a934e5beec73086cc91240
847035cbe0bc7ee0620c32fa5ac857cc5b2c7ec4