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:
parent
a2ce423efb
commit
d8d09c3c5a
@ -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());
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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_;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user