diff --git a/webrtc/modules/audio_processing/aec3/aec_state.cc b/webrtc/modules/audio_processing/aec3/aec_state.cc index c18fd6d870..e43cfc4795 100644 --- a/webrtc/modules/audio_processing/aec3/aec_state.cc +++ b/webrtc/modules/audio_processing/aec3/aec_state.cc @@ -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>& 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; diff --git a/webrtc/modules/audio_processing/aec3/aec_state.h b/webrtc/modules/audio_processing/aec3/aec_state.h index e3502b4a4f..56fee2cf8f 100644 --- a/webrtc/modules/audio_processing/aec3/aec_state.h +++ b/webrtc/modules/audio_processing/aec3/aec_state.h @@ -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& 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; diff --git a/webrtc/modules/audio_processing/aec3/echo_canceller3.cc b/webrtc/modules/audio_processing/aec3/echo_canceller3.cc index 60efced0ec..ed12a47995 100644 --- a/webrtc/modules/audio_processing/aec3/echo_canceller3.cc +++ b/webrtc/modules/audio_processing/aec3/echo_canceller3.cc @@ -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; diff --git a/webrtc/modules/audio_processing/aec3/echo_remover.cc b/webrtc/modules/audio_processing/aec3/echo_remover.cc index 47ff4c2e7a..2d6328655c 100644 --- a/webrtc/modules/audio_processing/aec3/echo_remover.cc +++ b/webrtc/modules/audio_processing/aec3/echo_remover.cc @@ -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 Y2; diff --git a/webrtc/modules/audio_processing/aec3/power_echo_model.cc b/webrtc/modules/audio_processing/aec3/power_echo_model.cc index 8ad5486e07..9bdc971b9d 100644 --- a/webrtc/modules/audio_processing/aec3/power_echo_model.cc +++ b/webrtc/modules/audio_processing/aec3/power_echo_model.cc @@ -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 diff --git a/webrtc/modules/audio_processing/aec3/residual_echo_estimator.cc b/webrtc/modules/audio_processing/aec3/residual_echo_estimator.cc index 38d5beb5fb..18a07b2f5a 100644 --- a/webrtc/modules/audio_processing/aec3/residual_echo_estimator.cc +++ b/webrtc/modules/audio_processing/aec3/residual_echo_estimator.cc @@ -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& S2_fallback, std::array* 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& bands_with_reliable_filter, const std::array& echo_path_gain, const std::array& 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 diff --git a/webrtc/modules/audio_processing/aec3/residual_echo_estimator.h b/webrtc/modules/audio_processing/aec3/residual_echo_estimator.h index 6c59f434b9..a4f85c40c2 100644 --- a/webrtc/modules/audio_processing/aec3/residual_echo_estimator.h +++ b/webrtc/modules/audio_processing/aec3/residual_echo_estimator.h @@ -39,9 +39,10 @@ class ResidualEchoEstimator { const std::array& Y2, std::array* R2); + void HandleEchoPathChange(const EchoPathVariability& echo_path_variability); + private: std::array echo_path_gain_; - size_t active_render_counter_ = 0; size_t blocks_since_last_saturation_ = 1000; RTC_DISALLOW_COPY_AND_ASSIGN(ResidualEchoEstimator);