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:
parent
ede0759c04
commit
ebe7778ce9
@ -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;
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user