diff --git a/modules/audio_processing/aec3/aec_state.cc b/modules/audio_processing/aec3/aec_state.cc index d5f256b1c1..45b361fc59 100644 --- a/modules/audio_processing/aec3/aec_state.cc +++ b/modules/audio_processing/aec3/aec_state.cc @@ -151,8 +151,7 @@ void AecState::Update( subtractor_output_analyzer_.Update(subtractor_output); // Analyze the properties of the filter. - filter_analyzer_.Update(adaptive_filter_impulse_response, - adaptive_filter_frequency_response, render_buffer); + filter_analyzer_.Update(adaptive_filter_impulse_response, render_buffer); // Estimate the direct path delay of the filter. delay_state_.Update(filter_analyzer_, external_delay, diff --git a/modules/audio_processing/aec3/filter_analyzer.cc b/modules/audio_processing/aec3/filter_analyzer.cc index 5b890d7474..3e69be6585 100644 --- a/modules/audio_processing/aec3/filter_analyzer.cc +++ b/modules/audio_processing/aec3/filter_analyzer.cc @@ -25,18 +25,22 @@ namespace webrtc { namespace { -size_t FindPeakIndex(rtc::ArrayView filter_time_domain) { - size_t peak_index = 0; - float max_h2 = filter_time_domain[0] * filter_time_domain[0]; - for (size_t k = 1; k < filter_time_domain.size(); ++k) { +size_t FindPeakIndex(rtc::ArrayView filter_time_domain, + size_t peak_index_in, + size_t start_sample, + size_t end_sample) { + size_t peak_index_out = peak_index_in; + float max_h2 = + filter_time_domain[peak_index_out] * filter_time_domain[peak_index_out]; + for (size_t k = start_sample; k <= end_sample; ++k) { float tmp = filter_time_domain[k] * filter_time_domain[k]; if (tmp > max_h2) { - peak_index = k; + peak_index_out = k; max_h2 = tmp; } } - return peak_index; + return peak_index_out; } bool EnableFilterPreprocessing() { @@ -44,6 +48,11 @@ bool EnableFilterPreprocessing() { "WebRTC-Aec3FilterAnalyzerPreprocessorKillSwitch"); } +bool EnableIncrementalAnalysis() { + return !field_trial::IsEnabled( + "WebRTC-Aec3FilterAnalyzerIncrementalAnalysisKillSwitch"); +} + } // namespace int FilterAnalyzer::instance_count_ = 0; @@ -54,46 +63,37 @@ FilterAnalyzer::FilterAnalyzer(const EchoCanceller3Config& config) 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), + use_incremental_analysis_(EnableIncrementalAnalysis()), h_highpass_(GetTimeDomainLength(config.filter.main.length_blocks), 0.f), - filter_length_blocks_(config.filter.main_initial.length_blocks) { + filter_length_blocks_(config.filter.main_initial.length_blocks), + consistent_filter_detector_(config) { 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() { delay_blocks_ = 0; - consistent_estimate_ = false; blocks_since_reset_ = 0; - consistent_estimate_ = false; - consistent_estimate_counter_ = 0; - consistent_delay_reference_ = -10; gain_ = default_gain_; + peak_index_ = 0; + ResetRegion(); + consistent_filter_detector_.Reset(); } -void FilterAnalyzer::Update( +void FilterAnalyzer::Update(rtc::ArrayView filter_time_domain, + const RenderBuffer& render_buffer) { + SetRegionToAnalyze(filter_time_domain); + AnalyzeRegion(filter_time_domain, render_buffer); +} + +void FilterAnalyzer::AnalyzeRegion( rtc::ArrayView filter_time_domain, - const std::vector>& - filter_freq_response, const RenderBuffer& render_buffer) { + RTC_DCHECK_LT(region_.start_sample_, filter_time_domain.size()); + RTC_DCHECK_LT(peak_index_, filter_time_domain.size()); + RTC_DCHECK_LT(region_.end_sample_, filter_time_domain.size()); + // Preprocess the filter to avoid issues with low-frequency components in the // filter. PreProcessFilter(filter_time_domain); @@ -103,51 +103,15 @@ void FilterAnalyzer::Update( use_preprocessed_filter_ ? h_highpass_ : filter_time_domain; RTC_DCHECK_EQ(filter_to_analyze.size(), filter_time_domain.size()); - 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_to_analyze.size() - 129 ? 0 : peak_index + 128; - - for (size_t k = 0; k < limit1; ++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_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_to_analyze.size() - limit2); - - 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; - - if (consistent_delay_reference_ != delay_blocks_ || !significant_peak_index) { - consistent_estimate_counter_ = 0; - consistent_delay_reference_ = delay_blocks_; - } else { - const auto& x = render_buffer.Block(-delay_blocks_)[0]; - const float x_energy = - std::inner_product(x.begin(), x.end(), x.begin(), 0.f); - const bool active_render_block = x_energy > active_render_threshold_; - - if (active_render_block) { - ++consistent_estimate_counter_; - } - } - - consistent_estimate_ = - consistent_estimate_counter_ > 1.5f * kNumBlocksPerSecond; - + peak_index_ = FindPeakIndex(filter_to_analyze, peak_index_, + region_.start_sample_, region_.end_sample_); + delay_blocks_ = peak_index_ >> kBlockSizeLog2; + UpdateFilterGain(filter_to_analyze, peak_index_); filter_length_blocks_ = filter_time_domain.size() * (1.f / kBlockSize); + + consistent_estimate_ = consistent_filter_detector_.Detect( + filter_to_analyze, region_, render_buffer.Block(-delay_blocks_)[0], + peak_index_, delay_blocks_); } void FilterAnalyzer::UpdateFilterGain( @@ -169,4 +133,114 @@ void FilterAnalyzer::UpdateFilterGain( } } +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() + region_.start_sample_, + h_highpass_.begin() + region_.end_sample_ + 1, 0.f); + for (size_t k = std::max(h.size() - 1, region_.start_sample_); + k <= region_.end_sample_; ++k) { + for (size_t j = 0; j < h.size(); ++j) { + h_highpass_[k] += filter_time_domain[k - j] * h[j]; + } + } +} + +void FilterAnalyzer::ResetRegion() { + region_.start_sample_ = 0; + region_.end_sample_ = 0; +} + +void FilterAnalyzer::SetRegionToAnalyze( + rtc::ArrayView filter_time_domain) { + constexpr size_t kNumberBlocksToUpdate = 1; + auto& r = region_; + if (use_incremental_analysis_) { + r.start_sample_ = + r.end_sample_ == filter_time_domain.size() - 1 ? 0 : r.end_sample_ + 1; + r.end_sample_ = + std::min(r.start_sample_ + kNumberBlocksToUpdate * kBlockSize - 1, + filter_time_domain.size() - 1); + + } else { + r.start_sample_ = 0; + r.end_sample_ = filter_time_domain.size() - 1; + } +} + +FilterAnalyzer::ConsistentFilterDetector::ConsistentFilterDetector( + const EchoCanceller3Config& config) + : active_render_threshold_(config.render_levels.active_render_limit * + config.render_levels.active_render_limit * + kFftLengthBy2) {} + +void FilterAnalyzer::ConsistentFilterDetector::Reset() { + significant_peak_ = false; + filter_floor_accum_ = 0.f; + filter_secondary_peak_ = 0.f; + filter_floor_low_limit_ = 0; + filter_floor_high_limit_ = 0; + consistent_estimate_counter_ = 0; + consistent_delay_reference_ = -10; +} + +bool FilterAnalyzer::ConsistentFilterDetector::Detect( + rtc::ArrayView filter_to_analyze, + const FilterRegion& region, + rtc::ArrayView x_block, + size_t peak_index, + int delay_blocks) { + if (region.start_sample_ == 0) { + filter_floor_accum_ = 0.f; + filter_secondary_peak_ = 0.f; + filter_floor_low_limit_ = peak_index < 64 ? 0 : peak_index - 64; + filter_floor_high_limit_ = + peak_index > filter_to_analyze.size() - 129 ? 0 : peak_index + 128; + } + + for (size_t k = region.start_sample_; + k < std::min(region.end_sample_ + 1, filter_floor_low_limit_); ++k) { + float abs_h = fabsf(filter_to_analyze[k]); + filter_floor_accum_ += abs_h; + filter_secondary_peak_ = std::max(filter_secondary_peak_, abs_h); + } + + for (size_t k = std::max(filter_floor_high_limit_, region.start_sample_); + k <= region.end_sample_; ++k) { + float abs_h = fabsf(filter_to_analyze[k]); + filter_floor_accum_ += abs_h; + filter_secondary_peak_ = std::max(filter_secondary_peak_, abs_h); + } + + if (region.end_sample_ == filter_to_analyze.size() - 1) { + float filter_floor = filter_floor_accum_ / + (filter_floor_low_limit_ + filter_to_analyze.size() - + filter_floor_high_limit_); + + float abs_peak = fabsf(filter_to_analyze[peak_index]); + significant_peak_ = abs_peak > 10.f * filter_floor && + abs_peak > 2.f * filter_secondary_peak_; + } + + if (significant_peak_) { + const float x_energy = std::inner_product(x_block.begin(), x_block.end(), + x_block.begin(), 0.f); + const bool active_render_block = x_energy > active_render_threshold_; + + if (consistent_delay_reference_ == delay_blocks) { + if (active_render_block) { + ++consistent_estimate_counter_; + } + } else { + consistent_estimate_counter_ = 0; + consistent_delay_reference_ = delay_blocks; + } + } + return consistent_estimate_counter_ > 1.5f * kNumBlocksPerSecond; +} + } // namespace webrtc diff --git a/modules/audio_processing/aec3/filter_analyzer.h b/modules/audio_processing/aec3/filter_analyzer.h index 99a0e25973..e0fd0695cb 100644 --- a/modules/audio_processing/aec3/filter_analyzer.h +++ b/modules/audio_processing/aec3/filter_analyzer.h @@ -37,8 +37,6 @@ class FilterAnalyzer { // Updates the estimates with new input data. void Update(rtc::ArrayView filter_time_domain, - const std::vector>& - filter_freq_response, const RenderBuffer& render_buffer); // Returns the delay of the filter in terms of blocks. @@ -58,24 +56,61 @@ class FilterAnalyzer { rtc::ArrayView GetAdjustedFilter() const { return h_highpass_; } private: + void AnalyzeRegion(rtc::ArrayView filter_time_domain, + const RenderBuffer& render_buffer); + void UpdateFilterGain(rtc::ArrayView filter_time_domain, size_t max_index); void PreProcessFilter(rtc::ArrayView filter_time_domain); + void ResetRegion(); + + void SetRegionToAnalyze(rtc::ArrayView filter_time_domain); + + struct FilterRegion { + size_t start_sample_; + size_t end_sample_; + }; + + // This class checks whether the shape of the impulse response has been + // consistent over time. + class ConsistentFilterDetector { + public: + explicit ConsistentFilterDetector(const EchoCanceller3Config& config); + void Reset(); + bool Detect(rtc::ArrayView filter_to_analyze, + const FilterRegion& region, + rtc::ArrayView x_block, + size_t peak_index, + int delay_blocks); + + private: + bool significant_peak_; + float filter_floor_accum_; + float filter_secondary_peak_; + size_t filter_floor_low_limit_; + size_t filter_floor_high_limit_; + const float active_render_threshold_; + size_t consistent_estimate_counter_ = 0; + int consistent_delay_reference_ = -10; + }; + 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_; + const bool use_incremental_analysis_; std::vector h_highpass_; int delay_blocks_ = 0; size_t blocks_since_reset_ = 0; bool consistent_estimate_ = false; - size_t consistent_estimate_counter_ = 0; - int consistent_delay_reference_ = -10; float gain_; + size_t peak_index_; int filter_length_blocks_; + FilterRegion region_; + ConsistentFilterDetector consistent_filter_detector_; + RTC_DISALLOW_COPY_AND_ASSIGN(FilterAnalyzer); };