AEC3: Adding explicit handling of microphone gain changes
This CL re-activates the explicit handling of microphone
gain changes in the AEC3 code. The implementation is done
beneath a kill-switch so that when that switch is active
the changes in this CL are bitexact.
Bug: webrtc:9526,chromium:863826
Change-Id: I58e93d8bc0bce7bec91e102de9891ad48ebc55d8
Reviewed-on: https://webrtc-review.googlesource.com/88620
Commit-Queue: Per Åhgren <peah@webrtc.org>
Reviewed-by: Sam Zackrisson <saza@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#23986}
diff --git a/modules/audio_processing/aec3/aec_state.cc b/modules/audio_processing/aec3/aec_state.cc
index f6ce23b..119b0ee 100644
--- a/modules/audio_processing/aec3/aec_state.cc
+++ b/modules/audio_processing/aec3/aec_state.cc
@@ -103,24 +103,10 @@
// TODO(peah): Refine the reset scheme according to the type of gain and
// delay adjustment.
- if (echo_path_variability.gain_change) {
- full_reset();
- }
if (echo_path_variability.delay_change !=
- EchoPathVariability::DelayAdjustment::kBufferReadjustment) {
+ EchoPathVariability::DelayAdjustment::kNone) {
full_reset();
- } else if (echo_path_variability.delay_change !=
- EchoPathVariability::DelayAdjustment::kBufferFlush) {
- full_reset();
- } else if (echo_path_variability.delay_change !=
- EchoPathVariability::DelayAdjustment::kDelayReset) {
- full_reset();
- } else if (echo_path_variability.delay_change !=
- EchoPathVariability::DelayAdjustment::kNewDetectedDelay) {
- full_reset();
- } else if (echo_path_variability.gain_change) {
- blocks_since_reset_ = kNumBlocksPerSecond;
}
subtractor_output_analyzer_.HandleEchoPathChange();
diff --git a/modules/audio_processing/aec3/aec_state_unittest.cc b/modules/audio_processing/aec3/aec_state_unittest.cc
index 6111979..ef05707 100644
--- a/modules/audio_processing/aec3/aec_state_unittest.cc
+++ b/modules/audio_processing/aec3/aec_state_unittest.cc
@@ -70,7 +70,7 @@
// Verify that linear AEC usability becomes false after an echo path change is
// reported
state.HandleEchoPathChange(EchoPathVariability(
- true, EchoPathVariability::DelayAdjustment::kNone, false));
+ false, EchoPathVariability::DelayAdjustment::kBufferReadjustment, false));
state.Update(delay_estimate, converged_filter_frequency_response,
impulse_response, *render_delay_buffer->GetRenderBuffer(),
E2_main, Y2, output, y);
diff --git a/modules/audio_processing/aec3/echo_remover.cc b/modules/audio_processing/aec3/echo_remover.cc
index 2e69298..67929b1 100644
--- a/modules/audio_processing/aec3/echo_remover.cc
+++ b/modules/audio_processing/aec3/echo_remover.cc
@@ -31,6 +31,7 @@
#include "modules/audio_processing/logging/apm_data_dumper.h"
#include "rtc_base/atomicops.h"
#include "rtc_base/constructormagic.h"
+#include "rtc_base/logging.h"
namespace webrtc {
@@ -66,7 +67,7 @@
// Removes the echo from a block of samples from the capture signal. The
// supplied render signal is assumed to be pre-aligned with the capture
// signal.
- void ProcessCapture(const EchoPathVariability& echo_path_variability,
+ void ProcessCapture(EchoPathVariability echo_path_variability,
bool capture_signal_saturation,
const absl::optional<DelayEstimate>& external_delay,
RenderBuffer* render_buffer,
@@ -104,6 +105,8 @@
std::array<float, kFftLengthBy2> e_old_;
std::array<float, kFftLengthBy2> x_old_;
std::array<float, kFftLengthBy2> y_old_;
+ size_t block_counter_ = 0;
+ int gain_change_hangover_ = 0;
RTC_DISALLOW_COPY_AND_ASSIGN(EchoRemoverImpl);
};
@@ -141,11 +144,12 @@
}
void EchoRemoverImpl::ProcessCapture(
- const EchoPathVariability& echo_path_variability,
+ EchoPathVariability echo_path_variability,
bool capture_signal_saturation,
const absl::optional<DelayEstimate>& external_delay,
RenderBuffer* render_buffer,
std::vector<std::vector<float>>* capture) {
+ ++block_counter_;
const std::vector<std::vector<float>>& x = render_buffer->Block(0);
std::vector<std::vector<float>>* y = capture;
RTC_DCHECK(render_buffer);
@@ -167,10 +171,29 @@
aec_state_.UpdateCaptureSaturation(capture_signal_saturation);
if (echo_path_variability.AudioPathChanged()) {
+ // Ensure that the gain change is only acted on once per frame.
+ if (echo_path_variability.gain_change) {
+ if (gain_change_hangover_ == 0) {
+ constexpr int kMaxBlocksPerFrame = 3;
+ gain_change_hangover_ = kMaxBlocksPerFrame;
+ RTC_LOG(LS_WARNING)
+ << "Gain change detected at block " << block_counter_;
+ } else {
+ echo_path_variability.gain_change = false;
+ }
+ }
+
subtractor_.HandleEchoPathChange(echo_path_variability);
aec_state_.HandleEchoPathChange(echo_path_variability);
- suppression_gain_.SetInitialState(true);
- initial_state_ = true;
+
+ if (echo_path_variability.delay_change !=
+ EchoPathVariability::DelayAdjustment::kNone) {
+ suppression_gain_.SetInitialState(true);
+ initial_state_ = true;
+ }
+ }
+ if (gain_change_hangover_ > 0) {
+ --gain_change_hangover_;
}
std::array<float, kFftLengthBy2Plus1> Y2;
diff --git a/modules/audio_processing/aec3/echo_remover.h b/modules/audio_processing/aec3/echo_remover.h
index 710afac..cc8dae9 100644
--- a/modules/audio_processing/aec3/echo_remover.h
+++ b/modules/audio_processing/aec3/echo_remover.h
@@ -36,7 +36,7 @@
// supplied render signal is assumed to be pre-aligned with the capture
// signal.
virtual void ProcessCapture(
- const EchoPathVariability& echo_path_variability,
+ EchoPathVariability echo_path_variability,
bool capture_signal_saturation,
const absl::optional<DelayEstimate>& external_delay,
RenderBuffer* render_buffer,
diff --git a/modules/audio_processing/aec3/main_filter_update_gain.cc b/modules/audio_processing/aec3/main_filter_update_gain.cc
index 6aa5780..6d31ed0 100644
--- a/modules/audio_processing/aec3/main_filter_update_gain.cc
+++ b/modules/audio_processing/aec3/main_filter_update_gain.cc
@@ -22,6 +22,7 @@
namespace {
constexpr float kHErrorInitial = 10000.f;
+constexpr float kHErrorGainChange = 10000.f;
constexpr int kPoorExcitationCounterInitial = 1000;
} // namespace
@@ -46,10 +47,19 @@
void MainFilterUpdateGain::HandleEchoPathChange(
const EchoPathVariability& echo_path_variability) {
- // TODO(peah): Add even-specific behavior.
- H_error_.fill(kHErrorInitial);
- poor_excitation_counter_ = kPoorExcitationCounterInitial;
- call_counter_ = 0;
+ if (echo_path_variability.gain_change) {
+ H_error_.fill(kHErrorGainChange);
+ }
+
+ if (echo_path_variability.delay_change !=
+ EchoPathVariability::DelayAdjustment::kNone) {
+ H_error_.fill(kHErrorInitial);
+ }
+
+ if (!echo_path_variability.gain_change) {
+ poor_excitation_counter_ = kPoorExcitationCounterInitial;
+ call_counter_ = 0;
+ }
}
void MainFilterUpdateGain::Compute(
diff --git a/modules/audio_processing/aec3/mock/mock_echo_remover.h b/modules/audio_processing/aec3/mock/mock_echo_remover.h
index 0eb9508..6b64785 100644
--- a/modules/audio_processing/aec3/mock/mock_echo_remover.h
+++ b/modules/audio_processing/aec3/mock/mock_echo_remover.h
@@ -27,7 +27,7 @@
virtual ~MockEchoRemover() = default;
MOCK_METHOD5(ProcessCapture,
- void(const EchoPathVariability& echo_path_variability,
+ void(EchoPathVariability echo_path_variability,
bool capture_signal_saturation,
const absl::optional<DelayEstimate>& delay_estimate,
RenderBuffer* render_buffer,
diff --git a/modules/audio_processing/aec3/subtractor.cc b/modules/audio_processing/aec3/subtractor.cc
index a9a3e6f..123ad7d 100644
--- a/modules/audio_processing/aec3/subtractor.cc
+++ b/modules/audio_processing/aec3/subtractor.cc
@@ -16,6 +16,7 @@
#include "api/array_view.h"
#include "modules/audio_processing/logging/apm_data_dumper.h"
#include "rtc_base/checks.h"
+#include "rtc_base/logging.h"
#include "rtc_base/numerics/safe_minmax.h"
#include "system_wrappers/include/field_trial.h"
@@ -23,6 +24,10 @@
namespace {
+bool EnableAgcGainChangeResponse() {
+ return !field_trial::IsEnabled("WebRTC-Aec3AgcGainChangeResponseKillSwitch");
+}
+
bool EnableAdaptationDuringSaturation() {
return !field_trial::IsEnabled("WebRTC-Aec3RapidAgcGainRecoveryKillSwitch");
}
@@ -77,6 +82,7 @@
config_(config),
adaptation_during_saturation_(EnableAdaptationDuringSaturation()),
enable_misadjustment_estimator_(EnableMisadjustmentEstimator()),
+ enable_agc_gain_change_response_(EnableAgcGainChangeResponse()),
main_filter_(config_.filter.main.length_blocks,
config_.filter.main_initial.length_blocks,
config.filter.config_change_duration_blocks,
@@ -117,18 +123,15 @@
config_.filter.shadow_initial.length_blocks, true);
};
- // TODO(peah): Add delay-change specific reset behavior.
- if ((echo_path_variability.delay_change ==
- EchoPathVariability::DelayAdjustment::kBufferFlush) ||
- (echo_path_variability.delay_change ==
- EchoPathVariability::DelayAdjustment::kDelayReset)) {
+ if (echo_path_variability.delay_change !=
+ EchoPathVariability::DelayAdjustment::kNone) {
full_reset();
- } else if (echo_path_variability.delay_change ==
- EchoPathVariability::DelayAdjustment::kNewDetectedDelay) {
- full_reset();
- } else if (echo_path_variability.delay_change ==
- EchoPathVariability::DelayAdjustment::kBufferReadjustment) {
- full_reset();
+ }
+
+ if (echo_path_variability.gain_change && enable_agc_gain_change_response_) {
+ RTC_LOG(LS_WARNING) << "Resetting main filter adaptation speed due to "
+ "microphone gain change";
+ G_main_.HandleEchoPathChange(echo_path_variability);
}
}
diff --git a/modules/audio_processing/aec3/subtractor.h b/modules/audio_processing/aec3/subtractor.h
index 67ecdc0..9af6499 100644
--- a/modules/audio_processing/aec3/subtractor.h
+++ b/modules/audio_processing/aec3/subtractor.h
@@ -106,6 +106,7 @@
const EchoCanceller3Config config_;
const bool adaptation_during_saturation_;
const bool enable_misadjustment_estimator_;
+ const bool enable_agc_gain_change_response_;
AdaptiveFirFilter main_filter_;
AdaptiveFirFilter shadow_filter_;
MainFilterUpdateGain G_main_;
diff --git a/modules/audio_processing/audio_processing_impl.cc b/modules/audio_processing/audio_processing_impl.cc
index e87d2cf..7e5955a 100644
--- a/modules/audio_processing/audio_processing_impl.cc
+++ b/modules/audio_processing/audio_processing_impl.cc
@@ -1198,9 +1198,13 @@
}
if (private_submodules_->echo_controller) {
- // TODO(peah): Reactivate analogue AGC gain detection once the analogue AGC
- // issues have been addressed.
- capture_.echo_path_gain_change = false;
+ // Detect and flag any change in the analog gain.
+ int analog_mic_level = gain_control()->stream_analog_level();
+ capture_.echo_path_gain_change =
+ capture_.prev_analog_mic_level != analog_mic_level &&
+ capture_.prev_analog_mic_level != -1;
+ capture_.prev_analog_mic_level = analog_mic_level;
+
private_submodules_->echo_controller->AnalyzeCapture(capture_buffer);
}
@@ -2049,7 +2053,8 @@
transient_suppressor_enabled(transient_suppressor_enabled),
capture_processing_format(kSampleRate16kHz),
split_rate(kSampleRate16kHz),
- echo_path_gain_change(false) {}
+ echo_path_gain_change(false),
+ prev_analog_mic_level(-1) {}
AudioProcessingImpl::ApmCaptureState::~ApmCaptureState() = default;
diff --git a/modules/audio_processing/audio_processing_impl.h b/modules/audio_processing/audio_processing_impl.h
index 8a5d245..44d0d08 100644
--- a/modules/audio_processing/audio_processing_impl.h
+++ b/modules/audio_processing/audio_processing_impl.h
@@ -390,6 +390,7 @@
StreamConfig capture_processing_format;
int split_rate;
bool echo_path_gain_change;
+ int prev_analog_mic_level;
} capture_ RTC_GUARDED_BY(crit_capture_);
struct ApmCaptureNonLockedState {
diff --git a/test/fuzzers/audio_processing_configs_fuzzer.cc b/test/fuzzers/audio_processing_configs_fuzzer.cc
index 46f8918..545e455 100644
--- a/test/fuzzers/audio_processing_configs_fuzzer.cc
+++ b/test/fuzzers/audio_processing_configs_fuzzer.cc
@@ -45,7 +45,8 @@
"WebRTC-Aec3RapidAgcGainRecoveryKillSwitch",
"WebRTC-Aec3SlowFilterAdaptationKillSwitch",
"WebRTC-Aec3SmoothUpdatesTailFreqRespKillSwitch",
- "WebRTC-Aec3SuppressorNearendAveragingKillSwitch"};
+ "WebRTC-Aec3SuppressorNearendAveragingKillSwitch",
+ "WebRTC-Aec3AgcGainChangeResponseKillSwitch"};
std::unique_ptr<AudioProcessing> CreateApm(test::FuzzDataHelper* fuzz_data,
std::string* field_trial_string) {