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}
This commit is contained in:
@ -103,24 +103,10 @@ void AecState::HandleEchoPathChange(
|
|||||||
|
|
||||||
// TODO(peah): Refine the reset scheme according to the type of gain and
|
// TODO(peah): Refine the reset scheme according to the type of gain and
|
||||||
// delay adjustment.
|
// delay adjustment.
|
||||||
if (echo_path_variability.gain_change) {
|
|
||||||
full_reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (echo_path_variability.delay_change !=
|
if (echo_path_variability.delay_change !=
|
||||||
EchoPathVariability::DelayAdjustment::kBufferReadjustment) {
|
EchoPathVariability::DelayAdjustment::kNone) {
|
||||||
full_reset();
|
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();
|
subtractor_output_analyzer_.HandleEchoPathChange();
|
||||||
|
|||||||
@ -70,7 +70,7 @@ TEST(AecState, NormalUsage) {
|
|||||||
// Verify that linear AEC usability becomes false after an echo path change is
|
// Verify that linear AEC usability becomes false after an echo path change is
|
||||||
// reported
|
// reported
|
||||||
state.HandleEchoPathChange(EchoPathVariability(
|
state.HandleEchoPathChange(EchoPathVariability(
|
||||||
true, EchoPathVariability::DelayAdjustment::kNone, false));
|
false, EchoPathVariability::DelayAdjustment::kBufferReadjustment, false));
|
||||||
state.Update(delay_estimate, converged_filter_frequency_response,
|
state.Update(delay_estimate, converged_filter_frequency_response,
|
||||||
impulse_response, *render_delay_buffer->GetRenderBuffer(),
|
impulse_response, *render_delay_buffer->GetRenderBuffer(),
|
||||||
E2_main, Y2, output, y);
|
E2_main, Y2, output, y);
|
||||||
|
|||||||
@ -31,6 +31,7 @@
|
|||||||
#include "modules/audio_processing/logging/apm_data_dumper.h"
|
#include "modules/audio_processing/logging/apm_data_dumper.h"
|
||||||
#include "rtc_base/atomicops.h"
|
#include "rtc_base/atomicops.h"
|
||||||
#include "rtc_base/constructormagic.h"
|
#include "rtc_base/constructormagic.h"
|
||||||
|
#include "rtc_base/logging.h"
|
||||||
|
|
||||||
namespace webrtc {
|
namespace webrtc {
|
||||||
|
|
||||||
@ -66,7 +67,7 @@ class EchoRemoverImpl final : public EchoRemover {
|
|||||||
// Removes the echo from a block of samples from the capture signal. The
|
// 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
|
// supplied render signal is assumed to be pre-aligned with the capture
|
||||||
// signal.
|
// signal.
|
||||||
void ProcessCapture(const EchoPathVariability& echo_path_variability,
|
void ProcessCapture(EchoPathVariability echo_path_variability,
|
||||||
bool capture_signal_saturation,
|
bool capture_signal_saturation,
|
||||||
const absl::optional<DelayEstimate>& external_delay,
|
const absl::optional<DelayEstimate>& external_delay,
|
||||||
RenderBuffer* render_buffer,
|
RenderBuffer* render_buffer,
|
||||||
@ -104,6 +105,8 @@ class EchoRemoverImpl final : public EchoRemover {
|
|||||||
std::array<float, kFftLengthBy2> e_old_;
|
std::array<float, kFftLengthBy2> e_old_;
|
||||||
std::array<float, kFftLengthBy2> x_old_;
|
std::array<float, kFftLengthBy2> x_old_;
|
||||||
std::array<float, kFftLengthBy2> y_old_;
|
std::array<float, kFftLengthBy2> y_old_;
|
||||||
|
size_t block_counter_ = 0;
|
||||||
|
int gain_change_hangover_ = 0;
|
||||||
|
|
||||||
RTC_DISALLOW_COPY_AND_ASSIGN(EchoRemoverImpl);
|
RTC_DISALLOW_COPY_AND_ASSIGN(EchoRemoverImpl);
|
||||||
};
|
};
|
||||||
@ -141,11 +144,12 @@ void EchoRemoverImpl::GetMetrics(EchoControl::Metrics* metrics) const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void EchoRemoverImpl::ProcessCapture(
|
void EchoRemoverImpl::ProcessCapture(
|
||||||
const EchoPathVariability& echo_path_variability,
|
EchoPathVariability echo_path_variability,
|
||||||
bool capture_signal_saturation,
|
bool capture_signal_saturation,
|
||||||
const absl::optional<DelayEstimate>& external_delay,
|
const absl::optional<DelayEstimate>& external_delay,
|
||||||
RenderBuffer* render_buffer,
|
RenderBuffer* render_buffer,
|
||||||
std::vector<std::vector<float>>* capture) {
|
std::vector<std::vector<float>>* capture) {
|
||||||
|
++block_counter_;
|
||||||
const std::vector<std::vector<float>>& x = render_buffer->Block(0);
|
const std::vector<std::vector<float>>& x = render_buffer->Block(0);
|
||||||
std::vector<std::vector<float>>* y = capture;
|
std::vector<std::vector<float>>* y = capture;
|
||||||
RTC_DCHECK(render_buffer);
|
RTC_DCHECK(render_buffer);
|
||||||
@ -167,10 +171,29 @@ void EchoRemoverImpl::ProcessCapture(
|
|||||||
aec_state_.UpdateCaptureSaturation(capture_signal_saturation);
|
aec_state_.UpdateCaptureSaturation(capture_signal_saturation);
|
||||||
|
|
||||||
if (echo_path_variability.AudioPathChanged()) {
|
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);
|
subtractor_.HandleEchoPathChange(echo_path_variability);
|
||||||
aec_state_.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;
|
std::array<float, kFftLengthBy2Plus1> Y2;
|
||||||
|
|||||||
@ -36,7 +36,7 @@ class EchoRemover {
|
|||||||
// supplied render signal is assumed to be pre-aligned with the capture
|
// supplied render signal is assumed to be pre-aligned with the capture
|
||||||
// signal.
|
// signal.
|
||||||
virtual void ProcessCapture(
|
virtual void ProcessCapture(
|
||||||
const EchoPathVariability& echo_path_variability,
|
EchoPathVariability echo_path_variability,
|
||||||
bool capture_signal_saturation,
|
bool capture_signal_saturation,
|
||||||
const absl::optional<DelayEstimate>& external_delay,
|
const absl::optional<DelayEstimate>& external_delay,
|
||||||
RenderBuffer* render_buffer,
|
RenderBuffer* render_buffer,
|
||||||
|
|||||||
@ -22,6 +22,7 @@ namespace webrtc {
|
|||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
constexpr float kHErrorInitial = 10000.f;
|
constexpr float kHErrorInitial = 10000.f;
|
||||||
|
constexpr float kHErrorGainChange = 10000.f;
|
||||||
constexpr int kPoorExcitationCounterInitial = 1000;
|
constexpr int kPoorExcitationCounterInitial = 1000;
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
@ -46,10 +47,19 @@ MainFilterUpdateGain::~MainFilterUpdateGain() {}
|
|||||||
|
|
||||||
void MainFilterUpdateGain::HandleEchoPathChange(
|
void MainFilterUpdateGain::HandleEchoPathChange(
|
||||||
const EchoPathVariability& echo_path_variability) {
|
const EchoPathVariability& echo_path_variability) {
|
||||||
// TODO(peah): Add even-specific behavior.
|
if (echo_path_variability.gain_change) {
|
||||||
H_error_.fill(kHErrorInitial);
|
H_error_.fill(kHErrorGainChange);
|
||||||
poor_excitation_counter_ = kPoorExcitationCounterInitial;
|
}
|
||||||
call_counter_ = 0;
|
|
||||||
|
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(
|
void MainFilterUpdateGain::Compute(
|
||||||
|
|||||||
@ -27,7 +27,7 @@ class MockEchoRemover : public EchoRemover {
|
|||||||
virtual ~MockEchoRemover() = default;
|
virtual ~MockEchoRemover() = default;
|
||||||
|
|
||||||
MOCK_METHOD5(ProcessCapture,
|
MOCK_METHOD5(ProcessCapture,
|
||||||
void(const EchoPathVariability& echo_path_variability,
|
void(EchoPathVariability echo_path_variability,
|
||||||
bool capture_signal_saturation,
|
bool capture_signal_saturation,
|
||||||
const absl::optional<DelayEstimate>& delay_estimate,
|
const absl::optional<DelayEstimate>& delay_estimate,
|
||||||
RenderBuffer* render_buffer,
|
RenderBuffer* render_buffer,
|
||||||
|
|||||||
@ -16,6 +16,7 @@
|
|||||||
#include "api/array_view.h"
|
#include "api/array_view.h"
|
||||||
#include "modules/audio_processing/logging/apm_data_dumper.h"
|
#include "modules/audio_processing/logging/apm_data_dumper.h"
|
||||||
#include "rtc_base/checks.h"
|
#include "rtc_base/checks.h"
|
||||||
|
#include "rtc_base/logging.h"
|
||||||
#include "rtc_base/numerics/safe_minmax.h"
|
#include "rtc_base/numerics/safe_minmax.h"
|
||||||
#include "system_wrappers/include/field_trial.h"
|
#include "system_wrappers/include/field_trial.h"
|
||||||
|
|
||||||
@ -23,6 +24,10 @@ namespace webrtc {
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
bool EnableAgcGainChangeResponse() {
|
||||||
|
return !field_trial::IsEnabled("WebRTC-Aec3AgcGainChangeResponseKillSwitch");
|
||||||
|
}
|
||||||
|
|
||||||
bool EnableAdaptationDuringSaturation() {
|
bool EnableAdaptationDuringSaturation() {
|
||||||
return !field_trial::IsEnabled("WebRTC-Aec3RapidAgcGainRecoveryKillSwitch");
|
return !field_trial::IsEnabled("WebRTC-Aec3RapidAgcGainRecoveryKillSwitch");
|
||||||
}
|
}
|
||||||
@ -77,6 +82,7 @@ Subtractor::Subtractor(const EchoCanceller3Config& config,
|
|||||||
config_(config),
|
config_(config),
|
||||||
adaptation_during_saturation_(EnableAdaptationDuringSaturation()),
|
adaptation_during_saturation_(EnableAdaptationDuringSaturation()),
|
||||||
enable_misadjustment_estimator_(EnableMisadjustmentEstimator()),
|
enable_misadjustment_estimator_(EnableMisadjustmentEstimator()),
|
||||||
|
enable_agc_gain_change_response_(EnableAgcGainChangeResponse()),
|
||||||
main_filter_(config_.filter.main.length_blocks,
|
main_filter_(config_.filter.main.length_blocks,
|
||||||
config_.filter.main_initial.length_blocks,
|
config_.filter.main_initial.length_blocks,
|
||||||
config.filter.config_change_duration_blocks,
|
config.filter.config_change_duration_blocks,
|
||||||
@ -117,19 +123,16 @@ void Subtractor::HandleEchoPathChange(
|
|||||||
config_.filter.shadow_initial.length_blocks, true);
|
config_.filter.shadow_initial.length_blocks, true);
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO(peah): Add delay-change specific reset behavior.
|
if (echo_path_variability.delay_change !=
|
||||||
if ((echo_path_variability.delay_change ==
|
EchoPathVariability::DelayAdjustment::kNone) {
|
||||||
EchoPathVariability::DelayAdjustment::kBufferFlush) ||
|
|
||||||
(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.delay_change ==
|
|
||||||
EchoPathVariability::DelayAdjustment::kBufferReadjustment) {
|
|
||||||
full_reset();
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Subtractor::ExitInitialState() {
|
void Subtractor::ExitInitialState() {
|
||||||
|
|||||||
@ -106,6 +106,7 @@ class Subtractor {
|
|||||||
const EchoCanceller3Config config_;
|
const EchoCanceller3Config config_;
|
||||||
const bool adaptation_during_saturation_;
|
const bool adaptation_during_saturation_;
|
||||||
const bool enable_misadjustment_estimator_;
|
const bool enable_misadjustment_estimator_;
|
||||||
|
const bool enable_agc_gain_change_response_;
|
||||||
AdaptiveFirFilter main_filter_;
|
AdaptiveFirFilter main_filter_;
|
||||||
AdaptiveFirFilter shadow_filter_;
|
AdaptiveFirFilter shadow_filter_;
|
||||||
MainFilterUpdateGain G_main_;
|
MainFilterUpdateGain G_main_;
|
||||||
|
|||||||
@ -1198,9 +1198,13 @@ int AudioProcessingImpl::ProcessCaptureStreamLocked() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (private_submodules_->echo_controller) {
|
if (private_submodules_->echo_controller) {
|
||||||
// TODO(peah): Reactivate analogue AGC gain detection once the analogue AGC
|
// Detect and flag any change in the analog gain.
|
||||||
// issues have been addressed.
|
int analog_mic_level = gain_control()->stream_analog_level();
|
||||||
capture_.echo_path_gain_change = false;
|
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);
|
private_submodules_->echo_controller->AnalyzeCapture(capture_buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2049,7 +2053,8 @@ AudioProcessingImpl::ApmCaptureState::ApmCaptureState(
|
|||||||
transient_suppressor_enabled(transient_suppressor_enabled),
|
transient_suppressor_enabled(transient_suppressor_enabled),
|
||||||
capture_processing_format(kSampleRate16kHz),
|
capture_processing_format(kSampleRate16kHz),
|
||||||
split_rate(kSampleRate16kHz),
|
split_rate(kSampleRate16kHz),
|
||||||
echo_path_gain_change(false) {}
|
echo_path_gain_change(false),
|
||||||
|
prev_analog_mic_level(-1) {}
|
||||||
|
|
||||||
AudioProcessingImpl::ApmCaptureState::~ApmCaptureState() = default;
|
AudioProcessingImpl::ApmCaptureState::~ApmCaptureState() = default;
|
||||||
|
|
||||||
|
|||||||
@ -390,6 +390,7 @@ class AudioProcessingImpl : public AudioProcessing {
|
|||||||
StreamConfig capture_processing_format;
|
StreamConfig capture_processing_format;
|
||||||
int split_rate;
|
int split_rate;
|
||||||
bool echo_path_gain_change;
|
bool echo_path_gain_change;
|
||||||
|
int prev_analog_mic_level;
|
||||||
} capture_ RTC_GUARDED_BY(crit_capture_);
|
} capture_ RTC_GUARDED_BY(crit_capture_);
|
||||||
|
|
||||||
struct ApmCaptureNonLockedState {
|
struct ApmCaptureNonLockedState {
|
||||||
|
|||||||
@ -45,7 +45,8 @@ const std::string kFieldTrialNames[] = {
|
|||||||
"WebRTC-Aec3RapidAgcGainRecoveryKillSwitch",
|
"WebRTC-Aec3RapidAgcGainRecoveryKillSwitch",
|
||||||
"WebRTC-Aec3SlowFilterAdaptationKillSwitch",
|
"WebRTC-Aec3SlowFilterAdaptationKillSwitch",
|
||||||
"WebRTC-Aec3SmoothUpdatesTailFreqRespKillSwitch",
|
"WebRTC-Aec3SmoothUpdatesTailFreqRespKillSwitch",
|
||||||
"WebRTC-Aec3SuppressorNearendAveragingKillSwitch"};
|
"WebRTC-Aec3SuppressorNearendAveragingKillSwitch",
|
||||||
|
"WebRTC-Aec3AgcGainChangeResponseKillSwitch"};
|
||||||
|
|
||||||
std::unique_ptr<AudioProcessing> CreateApm(test::FuzzDataHelper* fuzz_data,
|
std::unique_ptr<AudioProcessing> CreateApm(test::FuzzDataHelper* fuzz_data,
|
||||||
std::string* field_trial_string) {
|
std::string* field_trial_string) {
|
||||||
|
|||||||
Reference in New Issue
Block a user