| /* |
| * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. |
| * |
| * Use of this source code is governed by a BSD-style license |
| * that can be found in the LICENSE file in the root of the source |
| * tree. An additional intellectual property rights grant can be found |
| * in the file PATENTS. All contributing project authors may |
| * be found in the AUTHORS file in the root of the source tree. |
| */ |
| #include "modules/audio_processing/aec3/matched_filter_lag_aggregator.h" |
| |
| #include <algorithm> |
| #include <iterator> |
| |
| #include "modules/audio_processing/logging/apm_data_dumper.h" |
| #include "rtc_base/checks.h" |
| #include "rtc_base/numerics/safe_minmax.h" |
| #include "system_wrappers/include/field_trial.h" |
| |
| namespace webrtc { |
| namespace { |
| constexpr int kPreEchoHistogramDataNotUpdated = -1; |
| |
| int GetDownSamplingBlockSizeLog2(int down_sampling_factor) { |
| int down_sampling_factor_log2 = 0; |
| down_sampling_factor >>= 1; |
| while (down_sampling_factor > 0) { |
| down_sampling_factor_log2++; |
| down_sampling_factor >>= 1; |
| } |
| return static_cast<int>(kBlockSizeLog2) > down_sampling_factor_log2 |
| ? static_cast<int>(kBlockSizeLog2) - down_sampling_factor_log2 |
| : 0; |
| } |
| } // namespace |
| |
| MatchedFilterLagAggregator::MatchedFilterLagAggregator( |
| ApmDataDumper* data_dumper, |
| size_t max_filter_lag, |
| const EchoCanceller3Config::Delay& delay_config) |
| : data_dumper_(data_dumper), |
| thresholds_(delay_config.delay_selection_thresholds), |
| headroom_(static_cast<int>(delay_config.delay_headroom_samples / |
| delay_config.down_sampling_factor)), |
| highest_peak_aggregator_(max_filter_lag) { |
| if (delay_config.detect_pre_echo) { |
| pre_echo_lag_aggregator_ = std::make_unique<PreEchoLagAggregator>( |
| max_filter_lag, delay_config.down_sampling_factor); |
| } |
| RTC_DCHECK(data_dumper); |
| RTC_DCHECK_LE(thresholds_.initial, thresholds_.converged); |
| } |
| |
| MatchedFilterLagAggregator::~MatchedFilterLagAggregator() = default; |
| |
| void MatchedFilterLagAggregator::Reset(bool hard_reset) { |
| highest_peak_aggregator_.Reset(); |
| if (pre_echo_lag_aggregator_ != nullptr) { |
| pre_echo_lag_aggregator_->Reset(); |
| } |
| if (hard_reset) { |
| significant_candidate_found_ = false; |
| } |
| } |
| |
| absl::optional<DelayEstimate> MatchedFilterLagAggregator::Aggregate( |
| const absl::optional<const MatchedFilter::LagEstimate>& lag_estimate) { |
| if (lag_estimate && pre_echo_lag_aggregator_) { |
| pre_echo_lag_aggregator_->Dump(data_dumper_); |
| pre_echo_lag_aggregator_->Aggregate( |
| std::max(0, static_cast<int>(lag_estimate->pre_echo_lag) - headroom_)); |
| } |
| |
| if (lag_estimate) { |
| highest_peak_aggregator_.Aggregate( |
| std::max(0, static_cast<int>(lag_estimate->lag) - headroom_)); |
| rtc::ArrayView<const int> histogram = highest_peak_aggregator_.histogram(); |
| int candidate = highest_peak_aggregator_.candidate(); |
| significant_candidate_found_ = significant_candidate_found_ || |
| histogram[candidate] > thresholds_.converged; |
| if (histogram[candidate] > thresholds_.converged || |
| (histogram[candidate] > thresholds_.initial && |
| !significant_candidate_found_)) { |
| DelayEstimate::Quality quality = significant_candidate_found_ |
| ? DelayEstimate::Quality::kRefined |
| : DelayEstimate::Quality::kCoarse; |
| int reported_delay = pre_echo_lag_aggregator_ != nullptr |
| ? pre_echo_lag_aggregator_->pre_echo_candidate() |
| : candidate; |
| return DelayEstimate(quality, reported_delay); |
| } |
| } |
| |
| return absl::nullopt; |
| } |
| |
| MatchedFilterLagAggregator::HighestPeakAggregator::HighestPeakAggregator( |
| size_t max_filter_lag) |
| : histogram_(max_filter_lag + 1, 0) { |
| histogram_data_.fill(0); |
| } |
| |
| void MatchedFilterLagAggregator::HighestPeakAggregator::Reset() { |
| std::fill(histogram_.begin(), histogram_.end(), 0); |
| histogram_data_.fill(0); |
| histogram_data_index_ = 0; |
| } |
| |
| void MatchedFilterLagAggregator::HighestPeakAggregator::Aggregate(int lag) { |
| RTC_DCHECK_GT(histogram_.size(), histogram_data_[histogram_data_index_]); |
| RTC_DCHECK_LE(0, histogram_data_[histogram_data_index_]); |
| --histogram_[histogram_data_[histogram_data_index_]]; |
| histogram_data_[histogram_data_index_] = lag; |
| RTC_DCHECK_GT(histogram_.size(), histogram_data_[histogram_data_index_]); |
| RTC_DCHECK_LE(0, histogram_data_[histogram_data_index_]); |
| ++histogram_[histogram_data_[histogram_data_index_]]; |
| histogram_data_index_ = (histogram_data_index_ + 1) % histogram_data_.size(); |
| candidate_ = |
| std::distance(histogram_.begin(), |
| std::max_element(histogram_.begin(), histogram_.end())); |
| } |
| |
| MatchedFilterLagAggregator::PreEchoLagAggregator::PreEchoLagAggregator( |
| size_t max_filter_lag, |
| size_t down_sampling_factor) |
| : block_size_log2_(GetDownSamplingBlockSizeLog2(down_sampling_factor)), |
| penalize_high_delays_initial_phase_(!field_trial::IsDisabled( |
| "WebRTC-Aec3PenalyzeHighDelaysInitialPhase")), |
| histogram_( |
| ((max_filter_lag + 1) * down_sampling_factor) >> kBlockSizeLog2, |
| 0) { |
| Reset(); |
| } |
| |
| void MatchedFilterLagAggregator::PreEchoLagAggregator::Reset() { |
| std::fill(histogram_.begin(), histogram_.end(), 0); |
| histogram_data_.fill(kPreEchoHistogramDataNotUpdated); |
| histogram_data_index_ = 0; |
| pre_echo_candidate_ = 0; |
| } |
| |
| void MatchedFilterLagAggregator::PreEchoLagAggregator::Aggregate( |
| int pre_echo_lag) { |
| int pre_echo_block_size = pre_echo_lag >> block_size_log2_; |
| RTC_DCHECK(pre_echo_block_size >= 0 && |
| pre_echo_block_size < static_cast<int>(histogram_.size())); |
| pre_echo_block_size = |
| rtc::SafeClamp(pre_echo_block_size, 0, histogram_.size() - 1); |
| // Remove the oldest point from the `histogram_`, it ignores the initial |
| // points where no updates have been done to the `histogram_data_` array. |
| if (histogram_data_[histogram_data_index_] != |
| kPreEchoHistogramDataNotUpdated) { |
| --histogram_[histogram_data_[histogram_data_index_]]; |
| } |
| histogram_data_[histogram_data_index_] = pre_echo_block_size; |
| ++histogram_[histogram_data_[histogram_data_index_]]; |
| histogram_data_index_ = (histogram_data_index_ + 1) % histogram_data_.size(); |
| int pre_echo_candidate_block_size = 0; |
| if (penalize_high_delays_initial_phase_ && |
| number_updates_ < kNumBlocksPerSecond * 2) { |
| number_updates_++; |
| float penalization_per_delay = 1.0f; |
| float max_histogram_value = -1.0f; |
| for (auto it = histogram_.begin(); |
| std::distance(it, histogram_.end()) >= |
| static_cast<int>(kMatchedFilterWindowSizeSubBlocks); |
| it = it + kMatchedFilterWindowSizeSubBlocks) { |
| auto it_max_element = |
| std::max_element(it, it + kMatchedFilterWindowSizeSubBlocks); |
| float weighted_max_value = |
| static_cast<float>(*it_max_element) * penalization_per_delay; |
| if (weighted_max_value > max_histogram_value) { |
| max_histogram_value = weighted_max_value; |
| pre_echo_candidate_block_size = |
| std::distance(histogram_.begin(), it_max_element); |
| } |
| penalization_per_delay *= 0.7f; |
| } |
| } else { |
| pre_echo_candidate_block_size = |
| std::distance(histogram_.begin(), |
| std::max_element(histogram_.begin(), histogram_.end())); |
| } |
| pre_echo_candidate_ = (pre_echo_candidate_block_size << block_size_log2_); |
| } |
| |
| void MatchedFilterLagAggregator::PreEchoLagAggregator::Dump( |
| ApmDataDumper* const data_dumper) { |
| data_dumper->DumpRaw("aec3_pre_echo_delay_candidate", pre_echo_candidate_); |
| } |
| |
| } // namespace webrtc |