1 /*
2 * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
3 *
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10 #include "modules/audio_processing/aec3/matched_filter_lag_aggregator.h"
11
12 #include <algorithm>
13 #include <iterator>
14
15 #include "modules/audio_processing/logging/apm_data_dumper.h"
16 #include "rtc_base/checks.h"
17 #include "rtc_base/numerics/safe_minmax.h"
18
19 namespace webrtc {
20 namespace {
21 constexpr int kPreEchoHistogramDataNotUpdated = -1;
22
GetDownSamplingBlockSizeLog2(int down_sampling_factor)23 int GetDownSamplingBlockSizeLog2(int down_sampling_factor) {
24 int down_sampling_factor_log2 = 0;
25 down_sampling_factor >>= 1;
26 while (down_sampling_factor > 0) {
27 down_sampling_factor_log2++;
28 down_sampling_factor >>= 1;
29 }
30 return static_cast<int>(kBlockSizeLog2) > down_sampling_factor_log2
31 ? static_cast<int>(kBlockSizeLog2) - down_sampling_factor_log2
32 : 0;
33 }
34 } // namespace
35
MatchedFilterLagAggregator(ApmDataDumper * data_dumper,size_t max_filter_lag,const EchoCanceller3Config::Delay & delay_config)36 MatchedFilterLagAggregator::MatchedFilterLagAggregator(
37 ApmDataDumper* data_dumper,
38 size_t max_filter_lag,
39 const EchoCanceller3Config::Delay& delay_config)
40 : data_dumper_(data_dumper),
41 thresholds_(delay_config.delay_selection_thresholds),
42 headroom_(static_cast<int>(delay_config.delay_headroom_samples /
43 delay_config.down_sampling_factor)),
44 highest_peak_aggregator_(max_filter_lag) {
45 if (delay_config.detect_pre_echo) {
46 pre_echo_lag_aggregator_ = std::make_unique<PreEchoLagAggregator>(
47 max_filter_lag, delay_config.down_sampling_factor);
48 }
49 RTC_DCHECK(data_dumper);
50 RTC_DCHECK_LE(thresholds_.initial, thresholds_.converged);
51 }
52
53 MatchedFilterLagAggregator::~MatchedFilterLagAggregator() = default;
54
Reset(bool hard_reset)55 void MatchedFilterLagAggregator::Reset(bool hard_reset) {
56 highest_peak_aggregator_.Reset();
57 if (pre_echo_lag_aggregator_ != nullptr) {
58 pre_echo_lag_aggregator_->Reset();
59 }
60 if (hard_reset) {
61 significant_candidate_found_ = false;
62 }
63 }
64
Aggregate(const absl::optional<const MatchedFilter::LagEstimate> & lag_estimate)65 absl::optional<DelayEstimate> MatchedFilterLagAggregator::Aggregate(
66 const absl::optional<const MatchedFilter::LagEstimate>& lag_estimate) {
67 if (lag_estimate && pre_echo_lag_aggregator_) {
68 pre_echo_lag_aggregator_->Dump(data_dumper_);
69 pre_echo_lag_aggregator_->Aggregate(
70 std::max(0, static_cast<int>(lag_estimate->pre_echo_lag) - headroom_));
71 }
72
73 if (lag_estimate) {
74 highest_peak_aggregator_.Aggregate(
75 std::max(0, static_cast<int>(lag_estimate->lag) - headroom_));
76 rtc::ArrayView<const int> histogram = highest_peak_aggregator_.histogram();
77 int candidate = highest_peak_aggregator_.candidate();
78 significant_candidate_found_ = significant_candidate_found_ ||
79 histogram[candidate] > thresholds_.converged;
80 if (histogram[candidate] > thresholds_.converged ||
81 (histogram[candidate] > thresholds_.initial &&
82 !significant_candidate_found_)) {
83 DelayEstimate::Quality quality = significant_candidate_found_
84 ? DelayEstimate::Quality::kRefined
85 : DelayEstimate::Quality::kCoarse;
86 int reported_delay = pre_echo_lag_aggregator_ != nullptr
87 ? pre_echo_lag_aggregator_->pre_echo_candidate()
88 : candidate;
89 return DelayEstimate(quality, reported_delay);
90 }
91 }
92
93 return absl::nullopt;
94 }
95
HighestPeakAggregator(size_t max_filter_lag)96 MatchedFilterLagAggregator::HighestPeakAggregator::HighestPeakAggregator(
97 size_t max_filter_lag)
98 : histogram_(max_filter_lag + 1, 0) {
99 histogram_data_.fill(0);
100 }
101
Reset()102 void MatchedFilterLagAggregator::HighestPeakAggregator::Reset() {
103 std::fill(histogram_.begin(), histogram_.end(), 0);
104 histogram_data_.fill(0);
105 histogram_data_index_ = 0;
106 }
107
Aggregate(int lag)108 void MatchedFilterLagAggregator::HighestPeakAggregator::Aggregate(int lag) {
109 RTC_DCHECK_GT(histogram_.size(), histogram_data_[histogram_data_index_]);
110 RTC_DCHECK_LE(0, histogram_data_[histogram_data_index_]);
111 --histogram_[histogram_data_[histogram_data_index_]];
112 histogram_data_[histogram_data_index_] = lag;
113 RTC_DCHECK_GT(histogram_.size(), histogram_data_[histogram_data_index_]);
114 RTC_DCHECK_LE(0, histogram_data_[histogram_data_index_]);
115 ++histogram_[histogram_data_[histogram_data_index_]];
116 histogram_data_index_ = (histogram_data_index_ + 1) % histogram_data_.size();
117 candidate_ =
118 std::distance(histogram_.begin(),
119 std::max_element(histogram_.begin(), histogram_.end()));
120 }
121
PreEchoLagAggregator(size_t max_filter_lag,size_t down_sampling_factor)122 MatchedFilterLagAggregator::PreEchoLagAggregator::PreEchoLagAggregator(
123 size_t max_filter_lag,
124 size_t down_sampling_factor)
125 : block_size_log2_(GetDownSamplingBlockSizeLog2(down_sampling_factor)),
126 histogram_(
127 ((max_filter_lag + 1) * down_sampling_factor) >> kBlockSizeLog2,
128 0) {
129 Reset();
130 }
131
Reset()132 void MatchedFilterLagAggregator::PreEchoLagAggregator::Reset() {
133 std::fill(histogram_.begin(), histogram_.end(), 0);
134 histogram_data_.fill(kPreEchoHistogramDataNotUpdated);
135 histogram_data_index_ = 0;
136 pre_echo_candidate_ = 0;
137 }
138
Aggregate(int pre_echo_lag)139 void MatchedFilterLagAggregator::PreEchoLagAggregator::Aggregate(
140 int pre_echo_lag) {
141 int pre_echo_block_size = pre_echo_lag >> block_size_log2_;
142 RTC_DCHECK(pre_echo_block_size >= 0 &&
143 pre_echo_block_size < static_cast<int>(histogram_.size()));
144 pre_echo_block_size =
145 rtc::SafeClamp(pre_echo_block_size, 0, histogram_.size() - 1);
146 // Remove the oldest point from the `histogram_`, it ignores the initial
147 // points where no updates have been done to the `histogram_data_` array.
148 if (histogram_data_[histogram_data_index_] !=
149 kPreEchoHistogramDataNotUpdated) {
150 --histogram_[histogram_data_[histogram_data_index_]];
151 }
152 histogram_data_[histogram_data_index_] = pre_echo_block_size;
153 ++histogram_[histogram_data_[histogram_data_index_]];
154 histogram_data_index_ = (histogram_data_index_ + 1) % histogram_data_.size();
155 int pre_echo_candidate_block_size =
156 std::distance(histogram_.begin(),
157 std::max_element(histogram_.begin(), histogram_.end()));
158 pre_echo_candidate_ = (pre_echo_candidate_block_size << block_size_log2_);
159 }
160
Dump(ApmDataDumper * const data_dumper)161 void MatchedFilterLagAggregator::PreEchoLagAggregator::Dump(
162 ApmDataDumper* const data_dumper) {
163 data_dumper->DumpRaw("aec3_pre_echo_delay_candidate", pre_echo_candidate_);
164 }
165
166 } // namespace webrtc
167