Cleanup and simplification of the logic in the AEC3 state management

Bug: webrtc:8671
Change-Id: Ie34cee85b43b67da12b5c34e97eeacfd6d8baf7d
Reviewed-on: https://webrtc-review.googlesource.com/35120
Commit-Queue: Per Åhgren <peah@webrtc.org>
Reviewed-by: Gustaf Ullberg <gustaf@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#21388}
This commit is contained in:
Per Åhgren 2017-12-20 15:26:13 +01:00 committed by Commit Bot
parent 84d8ae5df7
commit 4b3bc0f1d3
6 changed files with 60 additions and 66 deletions

View File

@ -53,9 +53,6 @@ constexpr size_t kMatchedFilterWindowSizeSubBlocks = 32;
constexpr size_t kMatchedFilterAlignmentShiftSizeSubBlocks =
kMatchedFilterWindowSizeSubBlocks * 3 / 4;
constexpr size_t kEchoPathChangeConvergenceBlocks = 2 * kNumBlocksPerSecond;
// TODO(peah): Integrate this with how it is done inside audio_processing_impl.
constexpr size_t NumBandsForRate(int sample_rate_hz) {
return static_cast<size_t>(sample_rate_hz == 8000 ? 1

View File

@ -72,14 +72,13 @@ void AecState::HandleEchoPathChange(
previous_max_sample_ = 0.f;
std::fill(max_render_.begin(), max_render_.end(), 0.f);
force_zero_gain_counter_ = 0;
blocks_with_filter_adaptation_ = 0;
blocks_with_strong_render_ = 0;
blocks_with_proper_filter_adaptation_ = 0;
initial_state_ = true;
capture_block_counter_ = 0;
linear_echo_estimate_ = false;
sufficient_filter_updates_ = false;
filter_has_had_time_to_converge_ = false;
render_received_ = false;
force_zero_gain_ = true;
blocks_with_active_render_ = 0;
};
// TODO(peah): Refine the reset scheme according to the type of gain and
@ -123,14 +122,20 @@ void AecState::Update(
// Update counters.
++capture_block_counter_;
const bool active_render_block = DetectActiveRender(x);
blocks_with_active_render_ += active_render_block ? 1 : 0;
blocks_with_proper_filter_adaptation_ +=
active_render_block && !SaturatedCapture() ? 1 : 0;
// Force zero echo suppression gain after an echo path change to allow at
// least some render data to be collected in order to avoid an initial echo
// burst.
force_zero_gain_ = (++force_zero_gain_counter_) < kNumBlocksPerSecond / 5;
force_zero_gain_ = ++force_zero_gain_counter_ < kNumBlocksPerSecond / 5;
// Estimate delays.
filter_delay_ = EstimateFilterDelay(adaptive_filter_frequency_response);
// TODO(peah): Remove the dependency on the external delay.
external_delay_ =
external_delay_samples
? rtc::Optional<size_t>(*external_delay_samples / kBlockSize)
@ -149,59 +154,31 @@ void AecState::Update(
// Detect and flag echo saturation.
// TODO(peah): Add the delay in this computation to ensure that the render and
// capture signals are properly aligned.
RTC_DCHECK_LT(0, x.size());
const float max_sample = fabs(*std::max_element(
x.begin(), x.end(), [](float a, float b) { return a * a < b * b; }));
if (config_.ep_strength.echo_can_saturate) {
const bool saturated_echo =
(previous_max_sample_ > 200.f) && SaturatedCapture();
// Counts the blocks since saturation.
constexpr size_t kSaturationLeakageBlocks = 20;
// Set flag for potential presence of saturated echo
blocks_since_last_saturation_ =
saturated_echo ? 0 : blocks_since_last_saturation_ + 1;
echo_saturation_ = blocks_since_last_saturation_ < kSaturationLeakageBlocks;
} else {
echo_saturation_ = false;
echo_saturation_ = DetectEchoSaturation(x);
}
previous_max_sample_ = max_sample;
// TODO(peah): Move?
sufficient_filter_updates_ =
blocks_with_filter_adaptation_ >= kEchoPathChangeConvergenceBlocks;
filter_has_had_time_to_converge_ =
blocks_with_proper_filter_adaptation_ >= 2 * kNumBlocksPerSecond;
// TODO(peah): Remove.
initial_state_ = capture_block_counter_ < 3 * kNumBlocksPerSecond;
// Flag whether the linear filter estimate is usable.
usable_linear_estimate_ =
(!echo_saturation_) && (converged_filter || SufficientFilterUpdates()) &&
capture_block_counter_ >= 2 * kNumBlocksPerSecond && external_delay_;
linear_echo_estimate_ = UsableLinearEstimate() && !TransparentMode();
!echo_saturation_ &&
(converged_filter || filter_has_had_time_to_converge_) &&
capture_block_counter_ >= 2 * kNumBlocksPerSecond && external_delay_ &&
!TransparentMode();
// After an amount of active render samples for which an echo should have been
// detected in the capture signal if the ERL was not infinite, flag that a
// transparent mode should be entered.
const float x_energy = std::inner_product(x.begin(), x.end(), x.begin(), 0.f);
const bool active_render_block =
x_energy > (config_.render_levels.active_render_limit *
config_.render_levels.active_render_limit) *
kFftLengthBy2;
if (active_render_block) {
render_received_ = true;
}
// Update counters.
blocks_with_filter_adaptation_ +=
(active_render_block && (!SaturatedCapture()) ? 1 : 0);
transparent_mode_ = !converged_filter &&
(!render_received_ || blocks_with_filter_adaptation_ >=
5 * kNumBlocksPerSecond);
transparent_mode_ =
!converged_filter &&
(blocks_with_active_render_ == 0 ||
blocks_with_proper_filter_adaptation_ >= 5 * kNumBlocksPerSecond);
// Update the room reverb estimate.
UpdateReverb(adaptive_filter_impulse_response);
@ -289,6 +266,28 @@ void AecState::UpdateReverb(const std::vector<float>& impulse_response) {
data_dumper_->DumpRaw("aec3_tail_power", tail_power);
}
bool AecState::DetectActiveRender(rtc::ArrayView<const float> x) const {
const float x_energy = std::inner_product(x.begin(), x.end(), x.begin(), 0.f);
return x_energy > (config_.render_levels.active_render_limit *
config_.render_levels.active_render_limit) *
kFftLengthBy2;
}
bool AecState::DetectEchoSaturation(rtc::ArrayView<const float> x) {
RTC_DCHECK_LT(0, x.size());
const float max_sample = fabs(*std::max_element(
x.begin(), x.end(), [](float a, float b) { return a * a < b * b; }));
previous_max_sample_ = max_sample;
// Set flag for potential presence of saturated echo
blocks_since_last_saturation_ =
previous_max_sample_ > 200.f && SaturatedCapture()
? 0
: blocks_since_last_saturation_ + 1;
return blocks_since_last_saturation_ < 20;
}
void AecState::EchoAudibility::Update(rtc::ArrayView<const float> x,
const std::array<float, kBlockSize>& s,
bool converged_filter) {

View File

@ -35,15 +35,15 @@ class AecState {
explicit AecState(const EchoCanceller3Config& config);
~AecState();
// Returns whether the linear filter estimate is usable.
// Returns whether the echo subtractor can be used to determine the residual
// echo.
bool UsableLinearEstimate() const { return usable_linear_estimate_; }
// Returns whether there has been echo leakage detected.
bool EchoLeakageDetected() const { return echo_leakage_detected_; }
// Returns whether the render signal is currently active.
// TODO(peah): Deprecate this in an upcoming CL.
bool ActiveRender() const { return blocks_with_filter_adaptation_ > 200; }
bool ActiveRender() const { return blocks_with_active_render_ > 200; }
// Returns the ERLE.
const std::array<float, kFftLengthBy2Plus1>& Erle() const {
@ -101,12 +101,10 @@ class AecState {
echo_audibility_.UpdateWithOutput(e);
}
// Returns whether the linear filter should have been able to adapt properly.
bool SufficientFilterUpdates() const { return sufficient_filter_updates_; }
// Returns whether the echo subtractor can be used to determine the residual
// echo.
bool LinearEchoEstimate() const { return linear_echo_estimate_; }
// Returns whether the linear filter should have been able to properly adapt.
bool FilterHasHadTimeToConverge() const {
return filter_has_had_time_to_converge_;
}
// Returns whether the AEC is in an initial state.
bool InitialState() const { return initial_state_; }
@ -141,14 +139,16 @@ class AecState {
};
void UpdateReverb(const std::vector<float>& impulse_response);
bool DetectActiveRender(rtc::ArrayView<const float> x) const;
bool DetectEchoSaturation(rtc::ArrayView<const float> x);
static int instance_count_;
std::unique_ptr<ApmDataDumper> data_dumper_;
ErlEstimator erl_estimator_;
ErleEstimator erle_estimator_;
size_t capture_block_counter_ = 0;
size_t blocks_with_filter_adaptation_ = 0;
size_t blocks_with_strong_render_ = 0;
size_t blocks_with_proper_filter_adaptation_ = 0;
size_t blocks_with_active_render_ = 0;
bool usable_linear_estimate_ = false;
bool echo_leakage_detected_ = false;
bool capture_signal_saturation_ = false;
@ -170,8 +170,7 @@ class AecState {
float reverb_decay_;
bool saturating_echo_path_ = false;
bool initial_state_ = true;
bool linear_echo_estimate_ = false;
bool sufficient_filter_updates_ = false;
bool filter_has_had_time_to_converge_ = false;
RTC_DISALLOW_COPY_AND_ASSIGN(AecState);
};

View File

@ -233,7 +233,6 @@ void EchoRemoverImpl::ProcessCapture(
data_dumper_->DumpRaw("aec3_R2", R2);
data_dumper_->DumpRaw("aec3_erle", aec_state_.Erle());
data_dumper_->DumpRaw("aec3_erl", aec_state_.Erl());
data_dumper_->DumpRaw("aec3_active_render", aec_state_.ActiveRender());
data_dumper_->DumpRaw("aec3_usable_linear_estimate",
aec_state_.UsableLinearEstimate());
data_dumper_->DumpRaw(

View File

@ -95,7 +95,7 @@ void ResidualEchoEstimator::Estimate(
RenderNoisePower(render_buffer, &X2_noise_floor_, &X2_noise_floor_counter_);
// Estimate the residual echo power.
if (aec_state.LinearEchoEstimate()) {
if (aec_state.UsableLinearEstimate()) {
RTC_DCHECK(aec_state.FilterDelay());
const int filter_delay = *aec_state.FilterDelay();
LinearEstimate(S2_linear, aec_state.Erle(), filter_delay, R2);
@ -143,7 +143,7 @@ void ResidualEchoEstimator::Estimate(
[](float a, float b) { return std::max(0.f, a - 10.f * b); });
NonLinearEstimate(
aec_state.SufficientFilterUpdates(), aec_state.SaturatedEcho(),
aec_state.FilterHasHadTimeToConverge(), aec_state.SaturatedEcho(),
config_.ep_strength.bounded_erl, aec_state.TransparentMode(),
aec_state.InitialState(), X2, Y2, R2);

View File

@ -379,7 +379,7 @@ void SuppressionGain::GetGain(
const bool saturated_echo = aec_state.SaturatedEcho();
const bool saturating_echo_path = aec_state.SaturatingEchoPath();
const bool force_zero_gain = aec_state.ForcedZeroGain();
const bool linear_echo_estimate = aec_state.LinearEchoEstimate();
const bool linear_echo_estimate = aec_state.UsableLinearEstimate();
if (force_zero_gain) {
last_gain_.fill(0.f);