Further tuning for AEC3 for initial echo suppression

and handling of echo path changes.

This CL add tuning for the AEC3 that
1) Improves the handling of the initial echo suppression
before the linear filter is reliable.
2) Improves the handling of echo path changes.

There are also minor bugfixes included.

BUG=webrtc:6018

Review-Url: https://codereview.webrtc.org/2717353002
Cr-Commit-Position: refs/heads/master@{#16873}
This commit is contained in:
peah 2017-02-27 07:29:21 -08:00 committed by Commit bot
parent ede0759c04
commit ebe7778ce9
7 changed files with 44 additions and 17 deletions

View File

@ -81,7 +81,7 @@ void AnalyzeFilter(
constexpr int kActiveRenderCounterInitial = 50;
constexpr int kActiveRenderCounterMax = 200;
constexpr int kEchoPathChangeCounterInitial = 50;
constexpr int kEchoPathChangeCounterMax = 200;
constexpr int kEchoPathChangeCounterMax = 3 * 250;
} // namespace
@ -120,6 +120,9 @@ void AecState::Update(const std::vector<std::array<float, kFftLengthBy2Plus1>>&
const float x_energy = std::inner_product(x.begin(), x.end(), x.begin(), 0.f);
active_render_blocks_ =
echo_path_variability.AudioPathChanged() ? 0 : active_render_blocks_ + 1;
echo_path_change_counter_ = echo_path_variability.AudioPathChanged()
? kEchoPathChangeCounterMax
: echo_path_change_counter_ - 1;

View File

@ -47,6 +47,10 @@ class AecState {
// Returns whether the render signal is currently active.
bool ActiveRender() const { return active_render_counter_ > 0; }
// Returns whether the number of active render blocks since an echo path
// change.
size_t ActiveRenderBlocks() const { return active_render_blocks_; }
// Returns the ERLE.
const std::array<float, kFftLengthBy2Plus1>& Erle() const {
return erle_estimator_.Erle();
@ -108,6 +112,7 @@ class AecState {
ErleEstimator erle_estimator_;
int echo_path_change_counter_;
int active_render_counter_;
size_t active_render_blocks_ = 0;
bool usable_linear_estimate_ = false;
bool echo_leakage_detected_ = false;
bool model_based_aec_feasible_ = false;

View File

@ -260,7 +260,7 @@ bool EchoCanceller3::AnalyzeRender(AudioBuffer* render) {
void EchoCanceller3::AnalyzeCapture(AudioBuffer* capture) {
RTC_DCHECK_RUNS_SERIALIZED(&capture_race_checker_);
RTC_DCHECK(capture);
data_dumper_->DumpWav("aec3_capture_analyze_input", frame_length_,
data_dumper_->DumpWav("aec3_capture_analyze_input", capture->num_frames(),
capture->channels_f()[0], sample_rate_hz_, 1);
saturated_microphone_signal_ = false;

View File

@ -143,6 +143,7 @@ void EchoRemoverImpl::ProcessBlock(
if (echo_path_variability.AudioPathChanged()) {
subtractor_.HandleEchoPathChange(echo_path_variability);
power_echo_model_.HandleEchoPathChange(echo_path_variability);
residual_echo_estimator_.HandleEchoPathChange(echo_path_variability);
}
std::array<float, kFftLengthBy2Plus1> Y2;

View File

@ -28,7 +28,7 @@ void RecentMaximum(const FftBuffer& X_buffer,
}
}
constexpr float kHInitial = 10.f;
constexpr float kHInitial = 100.f;
constexpr int kUpdateCounterInitial = 300;
} // namespace

View File

@ -20,15 +20,16 @@ namespace {
constexpr float kSaturationLeakageFactor = 10.f;
constexpr size_t kSaturationLeakageBlocks = 10;
constexpr size_t kEchoPathChangeConvergenceBlocks = 3 * 250;
// Estimates the residual echo power when there is no detection correlation
// between the render and capture signals.
void InfiniteErlPowerEstimate(
size_t active_render_counter,
size_t active_render_blocks,
size_t blocks_since_last_saturation,
const std::array<float, kFftLengthBy2Plus1>& S2_fallback,
std::array<float, kFftLengthBy2Plus1>* R2) {
if (active_render_counter > 5 * 250) {
if (active_render_blocks > 5 * 250) {
// 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, set the
// residual echo to 0.
@ -62,6 +63,7 @@ void GainBasedPowerEstimate(
size_t external_delay,
const FftBuffer& X_buffer,
size_t blocks_since_last_saturation,
size_t active_render_blocks,
const std::array<bool, kFftLengthBy2Plus1>& bands_with_reliable_filter,
const std::array<float, kFftLengthBy2Plus1>& echo_path_gain,
const std::array<float, kFftLengthBy2Plus1>& S2_fallback,
@ -71,10 +73,17 @@ void GainBasedPowerEstimate(
// Base the residual echo power on gain of the linear echo path estimate if
// that is reliable, otherwise use the fallback echo path estimate. Add a
// leakage factor when there is saturation.
for (size_t k = 0; k < R2->size(); ++k) {
(*R2)[k] = bands_with_reliable_filter[k] ? echo_path_gain[k] * X2[k]
: S2_fallback[k];
if (active_render_blocks > kEchoPathChangeConvergenceBlocks) {
for (size_t k = 0; k < R2->size(); ++k) {
(*R2)[k] = bands_with_reliable_filter[k] ? echo_path_gain[k] * X2[k]
: S2_fallback[k];
}
} else {
for (size_t k = 0; k < R2->size(); ++k) {
(*R2)[k] = S2_fallback[k];
}
}
if (blocks_since_last_saturation < kSaturationLeakageBlocks) {
std::for_each(R2->begin(), R2->end(),
[](float& a) { a *= kSaturationLeakageFactor; });
@ -145,7 +154,7 @@ void ErleBasedPowerEstimate(
} // namespace
ResidualEchoEstimator::ResidualEchoEstimator() {
echo_path_gain_.fill(0.f);
echo_path_gain_.fill(100.f);
}
ResidualEchoEstimator::~ResidualEchoEstimator() = default;
@ -169,6 +178,10 @@ void ResidualEchoEstimator::Estimate(
if (linear_filter_based_delay) {
std::copy(H2[*linear_filter_based_delay].begin(),
H2[*linear_filter_based_delay].end(), echo_path_gain_.begin());
constexpr float kEchoPathGainHeadroom = 10.f;
std::for_each(
echo_path_gain_.begin(), echo_path_gain_.end(),
[kEchoPathGainHeadroom](float& a) { a *= kEchoPathGainHeadroom; });
}
// Counts the blocks since saturation.
@ -178,11 +191,6 @@ void ResidualEchoEstimator::Estimate(
++blocks_since_last_saturation_;
}
// Counts the number of active render blocks that are in a row.
if (aec_state.ActiveRender()) {
++active_render_counter_;
}
const auto& bands_with_reliable_filter = aec_state.BandsWithReliableFilter();
if (aec_state.UsableLinearEstimate()) {
@ -200,16 +208,25 @@ void ResidualEchoEstimator::Estimate(
RTC_DCHECK(aec_state.ExternalDelay());
GainBasedPowerEstimate(
*aec_state.ExternalDelay(), X_buffer, blocks_since_last_saturation_,
bands_with_reliable_filter, echo_path_gain_, S2_fallback, R2);
aec_state.ActiveRenderBlocks(), bands_with_reliable_filter,
echo_path_gain_, S2_fallback, R2);
} else if (aec_state.EchoLeakageDetected()) {
// Residual echo power when an external residual echo detection algorithm
// has deemed the echo canceller to leak echoes.
HalfDuplexPowerEstimate(aec_state.ActiveRender(), Y2, R2);
} else {
// Residual echo power when none of the other cases are fulfilled.
InfiniteErlPowerEstimate(active_render_counter_,
InfiniteErlPowerEstimate(aec_state.ActiveRenderBlocks(),
blocks_since_last_saturation_, S2_fallback, R2);
}
}
void ResidualEchoEstimator::HandleEchoPathChange(
const EchoPathVariability& echo_path_variability) {
if (echo_path_variability.AudioPathChanged()) {
blocks_since_last_saturation_ = 0;
echo_path_gain_.fill(100.f);
}
}
} // namespace webrtc

View File

@ -39,9 +39,10 @@ class ResidualEchoEstimator {
const std::array<float, kFftLengthBy2Plus1>& Y2,
std::array<float, kFftLengthBy2Plus1>* R2);
void HandleEchoPathChange(const EchoPathVariability& echo_path_variability);
private:
std::array<float, kFftLengthBy2Plus1> echo_path_gain_;
size_t active_render_counter_ = 0;
size_t blocks_since_last_saturation_ = 1000;
RTC_DISALLOW_COPY_AND_ASSIGN(ResidualEchoEstimator);