diff --git a/modules/audio_processing/aec3/filter_analyzer.cc b/modules/audio_processing/aec3/filter_analyzer.cc index 363373caa7..fd78f2a8ee 100644 --- a/modules/audio_processing/aec3/filter_analyzer.cc +++ b/modules/audio_processing/aec3/filter_analyzer.cc @@ -15,7 +15,10 @@ #include #include +#include "modules/audio_processing/logging/apm_data_dumper.h" +#include "rtc_base/atomicops.h" #include "rtc_base/checks.h" +#include "system_wrappers/include/field_trial.h" namespace webrtc { namespace { @@ -34,17 +37,43 @@ size_t FindPeakIndex(rtc::ArrayView filter_time_domain) { return peak_index; } +bool EnableFilterPreprocessing() { + return !field_trial::IsEnabled( + "WebRTC-Aec3FilterAnalyzerPreprocessorKillSwitch"); +} + } // namespace +int FilterAnalyzer::instance_count_ = 0; + FilterAnalyzer::FilterAnalyzer(const EchoCanceller3Config& config) - : bounded_erl_(config.ep_strength.bounded_erl), + : data_dumper_( + new ApmDataDumper(rtc::AtomicOps::Increment(&instance_count_))), + use_preprocessed_filter_(EnableFilterPreprocessing()), + bounded_erl_(config.ep_strength.bounded_erl), default_gain_(config.ep_strength.lf), active_render_threshold_(config.render_levels.active_render_limit * config.render_levels.active_render_limit * - kFftLengthBy2) { + kFftLengthBy2), + h_highpass_(GetTimeDomainLength(config.filter.main.length_blocks), 0.f) { Reset(); } +void FilterAnalyzer::PreProcessFilter( + rtc::ArrayView filter_time_domain) { + RTC_DCHECK_GE(h_highpass_.capacity(), filter_time_domain.size()); + h_highpass_.resize(filter_time_domain.size()); + // Minimum phase high-pass filter with cutoff frequency at about 600 Hz. + constexpr std::array h = {{0.7929742f, -0.36072128f, -0.47047766f}}; + + std::fill(h_highpass_.begin(), h_highpass_.end(), 0.f); + for (size_t k = h.size() - 1; k < filter_time_domain.size(); ++k) { + for (size_t j = 0; j < h.size(); ++j) { + h_highpass_[k] += filter_time_domain[k - j] * h[j]; + } + } +} + FilterAnalyzer::~FilterAnalyzer() = default; void FilterAnalyzer::Reset() { @@ -59,31 +88,40 @@ void FilterAnalyzer::Reset() { void FilterAnalyzer::Update(rtc::ArrayView filter_time_domain, const RenderBuffer& render_buffer) { - size_t peak_index = FindPeakIndex(filter_time_domain); - delay_blocks_ = peak_index / kBlockSize; + // Preprocess the filter to avoid issues with low-frequency components in the + // filter. + if (use_preprocessed_filter_) { + PreProcessFilter(filter_time_domain); + data_dumper_->DumpRaw("aec3_linear_filter_processed_td", h_highpass_); + } + const auto& filter_to_analyze = + use_preprocessed_filter_ ? h_highpass_ : filter_time_domain; + RTC_DCHECK_EQ(filter_to_analyze.size(), filter_time_domain.size()); - UpdateFilterGain(filter_time_domain, peak_index); + size_t peak_index = FindPeakIndex(filter_to_analyze); + delay_blocks_ = peak_index >> kBlockSizeLog2; + UpdateFilterGain(filter_to_analyze, peak_index); float filter_floor = 0; float filter_secondary_peak = 0; size_t limit1 = peak_index < 64 ? 0 : peak_index - 64; size_t limit2 = - peak_index > filter_time_domain.size() - 129 ? 0 : peak_index + 128; + peak_index > filter_to_analyze.size() - 129 ? 0 : peak_index + 128; for (size_t k = 0; k < limit1; ++k) { - float abs_h = fabsf(filter_time_domain[k]); + float abs_h = fabsf(filter_to_analyze[k]); filter_floor += abs_h; filter_secondary_peak = std::max(filter_secondary_peak, abs_h); } - for (size_t k = limit2; k < filter_time_domain.size(); ++k) { - float abs_h = fabsf(filter_time_domain[k]); + for (size_t k = limit2; k < filter_to_analyze.size(); ++k) { + float abs_h = fabsf(filter_to_analyze[k]); filter_floor += abs_h; filter_secondary_peak = std::max(filter_secondary_peak, abs_h); } - filter_floor /= (limit1 + filter_time_domain.size() - limit2); + filter_floor /= (limit1 + filter_to_analyze.size() - limit2); - float abs_peak = fabsf(filter_time_domain[peak_index]); + float abs_peak = fabsf(filter_to_analyze[peak_index]); bool significant_peak_index = abs_peak > 10.f * filter_floor && abs_peak > 2.f * filter_secondary_peak; diff --git a/modules/audio_processing/aec3/filter_analyzer.h b/modules/audio_processing/aec3/filter_analyzer.h index f02a2104d4..d7d568da08 100644 --- a/modules/audio_processing/aec3/filter_analyzer.h +++ b/modules/audio_processing/aec3/filter_analyzer.h @@ -17,11 +17,14 @@ #include "api/audio/echo_canceller3_config.h" #include "api/optional.h" #include "modules/audio_processing/aec3/aec3_common.h" +#include "modules/audio_processing/aec3/cascaded_biquad_filter.h" #include "modules/audio_processing/aec3/render_buffer.h" #include "rtc_base/constructormagic.h" namespace webrtc { +class ApmDataDumper; + // Class for analyzing the properties of an adaptive filter. class FilterAnalyzer { public: @@ -48,11 +51,15 @@ class FilterAnalyzer { private: void UpdateFilterGain(rtc::ArrayView filter_time_domain, size_t max_index); + void PreProcessFilter(rtc::ArrayView filter_time_domain); + static int instance_count_; + std::unique_ptr data_dumper_; + const bool use_preprocessed_filter_; const bool bounded_erl_; const float default_gain_; const float active_render_threshold_; - + std::vector h_highpass_; int delay_blocks_ = 0; size_t blocks_since_reset_ = 0; bool consistent_estimate_ = false;