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}
diff --git a/modules/audio_processing/aec3/aec_state.cc b/modules/audio_processing/aec3/aec_state.cc
index 3c2a403..e87f607 100644
--- a/modules/audio_processing/aec3/aec_state.cc
+++ b/modules/audio_processing/aec3/aec_state.cc
@@ -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 @@
 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 @@
           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 @@
     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 @@
   // 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 @@
       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());
diff --git a/modules/audio_processing/aec3/aec_state.h b/modules/audio_processing/aec3/aec_state.h
index aadfde9..e79e64b 100644
--- a/modules/audio_processing/aec3/aec_state.h
+++ b/modules/audio_processing/aec3/aec_state.h
@@ -107,7 +107,9 @@
   }
 
   // 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 @@
   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.
diff --git a/modules/audio_processing/aec3/echo_canceller3.cc b/modules/audio_processing/aec3/echo_canceller3.cc
index bd1b82a..95cd22a 100644
--- a/modules/audio_processing/aec3/echo_canceller3.cc
+++ b/modules/audio_processing/aec3/echo_canceller3.cc
@@ -47,6 +47,58 @@
         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 @@
     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 @@
         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;
 }
 
diff --git a/modules/audio_processing/aec3/residual_echo_estimator.cc b/modules/audio_processing/aec3/residual_echo_estimator.cc
index 3846a79..5d31c66 100644
--- a/modules/audio_processing/aec3/residual_echo_estimator.cc
+++ b/modules/audio_processing/aec3/residual_echo_estimator.cc
@@ -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 @@
   }
 }
 
-// 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 @@
     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 @@
       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 @@
                               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 @@
   }
 }
 
+// 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
diff --git a/modules/audio_processing/aec3/residual_echo_estimator.h b/modules/audio_processing/aec3/residual_echo_estimator.h
index 5c14bdb..081cc06 100644
--- a/modules/audio_processing/aec3/residual_echo_estimator.h
+++ b/modules/audio_processing/aec3/residual_echo_estimator.h
@@ -58,8 +58,17 @@
                  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_;