AEC3: Add transparency-related killswitches

This CL adds a number of kill-switches to the AEC3 code to be used as
safe fallbacks to increase AEC transparency.

The changes have been shown to be bitexact for a test dataset.

Bug: webrtc:11475,chromium:1066836
Change-Id: Ibebcbbfbbd958cb6fcc6993247e3030fa65b582c
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/172600
Reviewed-by: Sam Zackrisson <saza@webrtc.org>
Commit-Queue: Per Åhgren <peah@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#30964}
This commit is contained in:
Per Åhgren 2020-04-01 17:30:18 +02:00 committed by Commit Bot
parent a2ce423efb
commit d8d09c3c5a
5 changed files with 260 additions and 20 deletions

View File

@ -22,6 +22,7 @@
#include "modules/audio_processing/logging/apm_data_dumper.h"
#include "rtc_base/atomic_ops.h"
#include "rtc_base/checks.h"
#include "system_wrappers/include/field_trial.h"
namespace webrtc {
namespace {
@ -29,6 +30,24 @@ namespace {
constexpr size_t kBlocksSinceConvergencedFilterInit = 10000;
constexpr size_t kBlocksSinceConsistentEstimateInit = 10000;
bool DeactivateTransparentMode() {
return field_trial::IsEnabled("WebRTC-Aec3TransparentModeKillSwitch");
}
bool DeactivateInitialStateResetAtEchoPathChange() {
return field_trial::IsEnabled(
"WebRTC-Aec3DeactivateInitialStateResetKillSwitch");
}
bool FullResetAtEchoPathChange() {
return !field_trial::IsEnabled("WebRTC-Aec3AecStateFullResetKillSwitch");
}
bool SubtractorAnalyzerResetAtEchoPathChange() {
return !field_trial::IsEnabled(
"WebRTC-Aec3AecStateSubtractorAnalyzerResetKillSwitch");
}
void ComputeAvgRenderReverb(
const SpectrumBuffer& spectrum_buffer,
int delay_blocks,
@ -115,6 +134,12 @@ AecState::AecState(const EchoCanceller3Config& config,
new ApmDataDumper(rtc::AtomicOps::Increment(&instance_count_))),
config_(config),
num_capture_channels_(num_capture_channels),
transparent_mode_activated_(!DeactivateTransparentMode()),
deactivate_initial_state_reset_at_echo_path_change_(
DeactivateInitialStateResetAtEchoPathChange()),
full_reset_at_echo_path_change_(FullResetAtEchoPathChange()),
subtractor_analyzer_reset_at_echo_path_change_(
SubtractorAnalyzerResetAtEchoPathChange()),
initial_state_(config_),
delay_state_(config_, num_capture_channels_),
transparent_state_(config_),
@ -136,7 +161,9 @@ void AecState::HandleEchoPathChange(
capture_signal_saturation_ = false;
strong_not_saturated_render_blocks_ = 0;
blocks_with_active_render_ = 0;
initial_state_.Reset();
if (!deactivate_initial_state_reset_at_echo_path_change_) {
initial_state_.Reset();
}
transparent_state_.Reset();
erle_estimator_.Reset(true);
erl_estimator_.Reset();
@ -146,13 +173,16 @@ void AecState::HandleEchoPathChange(
// TODO(peah): Refine the reset scheme according to the type of gain and
// delay adjustment.
if (echo_path_variability.delay_change !=
EchoPathVariability::DelayAdjustment::kNone) {
if (full_reset_at_echo_path_change_ &&
echo_path_variability.delay_change !=
EchoPathVariability::DelayAdjustment::kNone) {
full_reset();
} else if (echo_path_variability.gain_change) {
erle_estimator_.Reset(false);
}
subtractor_output_analyzer_.HandleEchoPathChange();
if (subtractor_analyzer_reset_at_echo_path_change_) {
subtractor_output_analyzer_.HandleEchoPathChange();
}
}
void AecState::Update(
@ -235,9 +265,13 @@ void AecState::Update(
render_buffer.Spectrum(delay_state_.MinDirectPathFilterDelay()), Y2);
// Detect and flag echo saturation.
saturation_detector_.Update(aligned_render_block, SaturatedCapture(),
UsableLinearEstimate(), subtractor_output,
max_echo_path_gain);
if (config_.ep_strength.echo_can_saturate) {
saturation_detector_.Update(aligned_render_block, SaturatedCapture(),
UsableLinearEstimate(), subtractor_output,
max_echo_path_gain);
} else {
RTC_DCHECK(!saturation_detector_.SaturatedEcho());
}
// Update the decision on whether to use the initial state parameter set.
initial_state_.Update(active_render, SaturatedCapture());

View File

@ -107,7 +107,9 @@ class AecState {
}
// Returns whether the transparent mode is active
bool TransparentMode() const { return transparent_state_.Active(); }
bool TransparentMode() const {
return transparent_mode_activated_ && transparent_state_.Active();
}
// Takes appropriate action at an echo path change.
void HandleEchoPathChange(const EchoPathVariability& echo_path_variability);
@ -150,6 +152,10 @@ class AecState {
std::unique_ptr<ApmDataDumper> data_dumper_;
const EchoCanceller3Config config_;
const size_t num_capture_channels_;
const bool transparent_mode_activated_;
const bool deactivate_initial_state_reset_at_echo_path_change_;
const bool full_reset_at_echo_path_change_;
const bool subtractor_analyzer_reset_at_echo_path_change_;
// Class for controlling the transition from the intial state, which in turn
// controls when the filter parameters for the initial state should be used.

View File

@ -47,6 +47,58 @@ EchoCanceller3Config AdjustConfig(const EchoCanceller3Config& config) {
adjusted_cfg.filter.enable_shadow_filter_output_usage;
}
if (field_trial::IsEnabled("WebRTC-Aec3UseShortConfigChangeDuration")) {
adjusted_cfg.filter.config_change_duration_blocks = 10;
}
if (field_trial::IsEnabled("WebRTC-Aec3UseZeroInitialStateDuration")) {
adjusted_cfg.filter.initial_state_seconds = 0.f;
} else if (field_trial::IsEnabled(
"WebRTC-Aec3UseDot1SecondsInitialStateDuration")) {
adjusted_cfg.filter.initial_state_seconds = .1f;
} else if (field_trial::IsEnabled(
"WebRTC-Aec3UseDot2SecondsInitialStateDuration")) {
adjusted_cfg.filter.initial_state_seconds = .2f;
} else if (field_trial::IsEnabled(
"WebRTC-Aec3UseDot3SecondsInitialStateDuration")) {
adjusted_cfg.filter.initial_state_seconds = .3f;
} else if (field_trial::IsEnabled(
"WebRTC-Aec3UseDot6SecondsInitialStateDuration")) {
adjusted_cfg.filter.initial_state_seconds = .6f;
} else if (field_trial::IsEnabled(
"WebRTC-Aec3UseDot9SecondsInitialStateDuration")) {
adjusted_cfg.filter.initial_state_seconds = .9f;
} else if (field_trial::IsEnabled(
"WebRTC-Aec3Use1Dot2SecondsInitialStateDuration")) {
adjusted_cfg.filter.initial_state_seconds = 1.2f;
} else if (field_trial::IsEnabled(
"WebRTC-Aec3Use1Dot6SecondsInitialStateDuration")) {
adjusted_cfg.filter.initial_state_seconds = 1.6f;
} else if (field_trial::IsEnabled(
"WebRTC-Aec3Use2Dot0SecondsInitialStateDuration")) {
adjusted_cfg.filter.initial_state_seconds = 2.0f;
}
if (field_trial::IsEnabled("WebRTC-Aec3EchoSaturationDetectionKillSwitch")) {
adjusted_cfg.ep_strength.echo_can_saturate = false;
}
if (field_trial::IsEnabled("WebRTC-Aec3UseDot2ReverbDefaultLen")) {
adjusted_cfg.ep_strength.default_len = 0.2f;
} else if (field_trial::IsEnabled("WebRTC-Aec3UseDot3ReverbDefaultLen")) {
adjusted_cfg.ep_strength.default_len = 0.3f;
} else if (field_trial::IsEnabled("WebRTC-Aec3UseDot4ReverbDefaultLen")) {
adjusted_cfg.ep_strength.default_len = 0.4f;
} else if (field_trial::IsEnabled("WebRTC-Aec3UseDot5ReverbDefaultLen")) {
adjusted_cfg.ep_strength.default_len = 0.5f;
} else if (field_trial::IsEnabled("WebRTC-Aec3UseDot6ReverbDefaultLen")) {
adjusted_cfg.ep_strength.default_len = 0.6f;
} else if (field_trial::IsEnabled("WebRTC-Aec3UseDot7ReverbDefaultLen")) {
adjusted_cfg.ep_strength.default_len = 0.7f;
} else if (field_trial::IsEnabled("WebRTC-Aec3UseDot8ReverbDefaultLen")) {
adjusted_cfg.ep_strength.default_len = 0.8f;
}
if (field_trial::IsEnabled("WebRTC-Aec3ShortHeadroomKillSwitch")) {
// Two blocks headroom.
adjusted_cfg.delay.delay_headroom_samples = kBlockSize * 2;
@ -60,6 +112,10 @@ EchoCanceller3Config AdjustConfig(const EchoCanceller3Config& config) {
adjusted_cfg.erle.clamp_quality_estimate_to_one = false;
}
if (field_trial::IsEnabled("WebRTC-Aec3OnsetDetectionKillSwitch")) {
adjusted_cfg.erle.onset_detection = false;
}
if (field_trial::IsEnabled(
"WebRTC-Aec3EnforceRenderDelayEstimationDownmixing")) {
adjusted_cfg.delay.render_alignment_mixing.downmix = true;
@ -85,6 +141,65 @@ EchoCanceller3Config AdjustConfig(const EchoCanceller3Config& config) {
false;
}
if (field_trial::IsEnabled("WebRTC-Aec3SensitiveDominantNearendActivation")) {
adjusted_cfg.suppressor.dominant_nearend_detection.enr_threshold = 0.5f;
} else if (field_trial::IsEnabled(
"WebRTC-Aec3VerySensitiveDominantNearendActivation")) {
adjusted_cfg.suppressor.dominant_nearend_detection.enr_threshold = 0.75f;
}
if (field_trial::IsEnabled("WebRTC-Aec3TransparentAntiHowlingGain")) {
adjusted_cfg.suppressor.high_bands_suppression.anti_howling_gain = 1.f;
}
if (field_trial::IsEnabled(
"WebRTC-Aec3EnforceMoreTransparentNormalSuppressorTuning")) {
adjusted_cfg.suppressor.normal_tuning.mask_lf.enr_transparent = 0.4f;
adjusted_cfg.suppressor.normal_tuning.mask_lf.enr_suppress = 0.5f;
}
if (field_trial::IsEnabled(
"WebRTC-Aec3EnforceMoreTransparentNearendSuppressorTuning")) {
adjusted_cfg.suppressor.nearend_tuning.mask_lf.enr_transparent = 1.29f;
adjusted_cfg.suppressor.nearend_tuning.mask_lf.enr_suppress = 1.3f;
}
if (field_trial::IsEnabled(
"WebRTC-Aec3EnforceRapidlyAdjustingNormalSuppressorTunings")) {
adjusted_cfg.suppressor.normal_tuning.max_inc_factor = 2.5f;
}
if (field_trial::IsEnabled(
"WebRTC-Aec3EnforceRapidlyAdjustingNearendSuppressorTunings")) {
adjusted_cfg.suppressor.nearend_tuning.max_inc_factor = 2.5f;
}
if (field_trial::IsEnabled(
"WebRTC-Aec3EnforceSlowlyAdjustingNormalSuppressorTunings")) {
adjusted_cfg.suppressor.normal_tuning.max_dec_factor_lf = .2f;
}
if (field_trial::IsEnabled(
"WebRTC-Aec3EnforceSlowlyAdjustingNearendSuppressorTunings")) {
adjusted_cfg.suppressor.nearend_tuning.max_dec_factor_lf = .2f;
}
if (field_trial::IsEnabled("WebRTC-Aec3EnforceStationarityProperties")) {
adjusted_cfg.echo_audibility.use_stationarity_properties = true;
}
if (field_trial::IsEnabled(
"WebRTC-Aec3EnforceStationarityPropertiesAtInit")) {
adjusted_cfg.echo_audibility.use_stationarity_properties_at_init = true;
}
if (field_trial::IsEnabled("WebRTC-Aec3EnforceLowActiveRenderLimit")) {
adjusted_cfg.render_levels.active_render_limit = 50.f;
} else if (field_trial::IsEnabled(
"WebRTC-Aec3EnforceVeryLowActiveRenderLimit")) {
adjusted_cfg.render_levels.active_render_limit = 30.f;
}
return adjusted_cfg;
}

View File

@ -18,10 +18,67 @@
#include "api/array_view.h"
#include "modules/audio_processing/aec3/reverb_model.h"
#include "rtc_base/checks.h"
#include "system_wrappers/include/field_trial.h"
namespace webrtc {
namespace {
bool UseLowEarlyReflectionsTransparentModeGain() {
return field_trial::IsEnabled(
"WebRTC-Aec3UseLowEarlyReflectionsTransparentModeGain");
}
bool UseLowLateReflectionsTransparentModeGain() {
return field_trial::IsEnabled(
"WebRTC-Aec3UseLowLateReflectionsTransparentModeGain");
}
bool UseLowEarlyReflectionsDefaultGain() {
return field_trial::IsEnabled("WebRTC-Aec3UseLowEarlyReflectionsDefaultGain");
}
bool UseLowLateReflectionsDefaultGain() {
return field_trial::IsEnabled("WebRTC-Aec3UseLowLateReflectionsDefaultGain");
}
bool ModelReverbInNonlinearMode() {
return !field_trial::IsEnabled("WebRTC-Aec3rNonlinearModeReverbKillSwitch");
}
constexpr float kDefaultTransparentModeGain = 0.01f;
float GetEarlyReflectionsTransparentModeGain() {
if (UseLowEarlyReflectionsTransparentModeGain()) {
return 0.001f;
}
return kDefaultTransparentModeGain;
}
float GetLateReflectionsTransparentModeGain() {
if (UseLowLateReflectionsTransparentModeGain()) {
return 0.001f;
}
return kDefaultTransparentModeGain;
}
float GetEarlyReflectionsDefaultModeGain(
const EchoCanceller3Config::EpStrength& config) {
if (UseLowEarlyReflectionsDefaultGain()) {
return 0.1f;
}
return config.default_gain;
}
float GetLateReflectionsDefaultModeGain(
const EchoCanceller3Config::EpStrength& config) {
if (UseLowLateReflectionsDefaultGain()) {
return 0.1f;
}
return config.default_gain;
}
// Computes the indexes that will be used for computing spectral power over
// the blocks surrounding the delay.
void GetRenderIndexesToAnalyze(
@ -138,19 +195,21 @@ void EchoGeneratingPower(size_t num_render_channels,
}
}
// Chooses the echo path gain to use.
float GetEchoPathGain(const AecState& aec_state,
const EchoCanceller3Config::EpStrength& config) {
float gain_amplitude =
aec_state.TransparentMode() ? 0.01f : config.default_gain;
return gain_amplitude * gain_amplitude;
}
} // namespace
ResidualEchoEstimator::ResidualEchoEstimator(const EchoCanceller3Config& config,
size_t num_render_channels)
: config_(config), num_render_channels_(num_render_channels) {
: config_(config),
num_render_channels_(num_render_channels),
early_reflections_transparent_mode_gain_(
GetEarlyReflectionsTransparentModeGain()),
late_reflections_transparent_mode_gain_(
GetLateReflectionsTransparentModeGain()),
early_reflections_general_gain_(
GetEarlyReflectionsDefaultModeGain(config_.ep_strength)),
late_reflections_general_gain_(
GetLateReflectionsDefaultModeGain(config_.ep_strength)),
model_reverb_in_nonlinear_mode_(ModelReverbInNonlinearMode()) {
Reset();
}
@ -190,7 +249,7 @@ void ResidualEchoEstimator::Estimate(
AddReverb(ReverbType::kLinear, aec_state, render_buffer, R2);
} else {
const float echo_path_gain =
GetEchoPathGain(aec_state, config_.ep_strength);
GetEchoPathGain(aec_state, /*gain_for_early_reflections=*/true);
// When there is saturated echo, assume the same spectral content as is
// present in the microphone signal.
@ -218,7 +277,7 @@ void ResidualEchoEstimator::Estimate(
NonLinearEstimate(echo_path_gain, X2, R2);
}
if (!aec_state.TransparentMode()) {
if (model_reverb_in_nonlinear_mode_ && !aec_state.TransparentMode()) {
AddReverb(ReverbType::kNonLinear, aec_state, render_buffer, R2);
}
}
@ -316,7 +375,7 @@ void ResidualEchoEstimator::AddReverb(
aec_state.ReverbDecay());
} else {
const float echo_path_gain =
GetEchoPathGain(aec_state, config_.ep_strength);
GetEchoPathGain(aec_state, /*gain_for_early_reflections=*/false);
echo_reverb_.UpdateReverbNoFreqShaping(render_power, echo_path_gain,
aec_state.ReverbDecay());
}
@ -331,4 +390,21 @@ void ResidualEchoEstimator::AddReverb(
}
}
// Chooses the echo path gain to use.
float ResidualEchoEstimator::GetEchoPathGain(
const AecState& aec_state,
bool gain_for_early_reflections) const {
float gain_amplitude;
if (aec_state.TransparentMode()) {
gain_amplitude = gain_for_early_reflections
? early_reflections_transparent_mode_gain_
: late_reflections_transparent_mode_gain_;
} else {
gain_amplitude = gain_for_early_reflections
? early_reflections_general_gain_
: late_reflections_general_gain_;
}
return gain_amplitude * gain_amplitude;
}
} // namespace webrtc

View File

@ -58,8 +58,17 @@ class ResidualEchoEstimator {
const RenderBuffer& render_buffer,
rtc::ArrayView<std::array<float, kFftLengthBy2Plus1>> R2);
// Gets the echo path gain to apply.
float GetEchoPathGain(const AecState& aec_state,
bool gain_for_early_reflections) const;
const EchoCanceller3Config config_;
const size_t num_render_channels_;
const float early_reflections_transparent_mode_gain_;
const float late_reflections_transparent_mode_gain_;
const float early_reflections_general_gain_;
const float late_reflections_general_gain_;
const bool model_reverb_in_nonlinear_mode_;
std::array<float, kFftLengthBy2Plus1> X2_noise_floor_;
std::array<int, kFftLengthBy2Plus1> X2_noise_floor_counter_;
ReverbModel echo_reverb_;