1 /*
2 * Copyright (c) 2018 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
11 #include "modules/audio_processing/aec3/reverb_decay_estimator.h"
12
13 #include <stddef.h>
14
15 #include <algorithm>
16 #include <cmath>
17 #include <numeric>
18
19 #include "api/array_view.h"
20 #include "api/audio/echo_canceller3_config.h"
21 #include "modules/audio_processing/logging/apm_data_dumper.h"
22 #include "rtc_base/checks.h"
23
24 namespace webrtc {
25
26 namespace {
27
28 constexpr int kEarlyReverbMinSizeBlocks = 3;
29 constexpr int kBlocksPerSection = 6;
30 // Linear regression approach assumes symmetric index around 0.
31 constexpr float kEarlyReverbFirstPointAtLinearRegressors =
32 -0.5f * kBlocksPerSection * kFftLengthBy2 + 0.5f;
33
34 // Averages the values in a block of size kFftLengthBy2;
BlockAverage(rtc::ArrayView<const float> v,size_t block_index)35 float BlockAverage(rtc::ArrayView<const float> v, size_t block_index) {
36 constexpr float kOneByFftLengthBy2 = 1.f / kFftLengthBy2;
37 const int i = block_index * kFftLengthBy2;
38 RTC_DCHECK_GE(v.size(), i + kFftLengthBy2);
39 const float sum =
40 std::accumulate(v.begin() + i, v.begin() + i + kFftLengthBy2, 0.f);
41 return sum * kOneByFftLengthBy2;
42 }
43
44 // Analyzes the gain in a block.
AnalyzeBlockGain(const std::array<float,kFftLengthBy2> & h2,float floor_gain,float * previous_gain,bool * block_adapting,bool * decaying_gain)45 void AnalyzeBlockGain(const std::array<float, kFftLengthBy2>& h2,
46 float floor_gain,
47 float* previous_gain,
48 bool* block_adapting,
49 bool* decaying_gain) {
50 float gain = std::max(BlockAverage(h2, 0), 1e-32f);
51 *block_adapting =
52 *previous_gain > 1.1f * gain || *previous_gain < 0.9f * gain;
53 *decaying_gain = gain > floor_gain;
54 *previous_gain = gain;
55 }
56
57 // Arithmetic sum of $2 \sum_{i=0.5}^{(N-1)/2}i^2$ calculated directly.
SymmetricArithmetricSum(int N)58 constexpr float SymmetricArithmetricSum(int N) {
59 return N * (N * N - 1.0f) * (1.f / 12.f);
60 }
61
62 // Returns the peak energy of an impulse response.
BlockEnergyPeak(rtc::ArrayView<const float> h,int peak_block)63 float BlockEnergyPeak(rtc::ArrayView<const float> h, int peak_block) {
64 RTC_DCHECK_LE((peak_block + 1) * kFftLengthBy2, h.size());
65 RTC_DCHECK_GE(peak_block, 0);
66 float peak_value =
67 *std::max_element(h.begin() + peak_block * kFftLengthBy2,
68 h.begin() + (peak_block + 1) * kFftLengthBy2,
69 [](float a, float b) { return a * a < b * b; });
70 return peak_value * peak_value;
71 }
72
73 // Returns the average energy of an impulse response block.
BlockEnergyAverage(rtc::ArrayView<const float> h,int block_index)74 float BlockEnergyAverage(rtc::ArrayView<const float> h, int block_index) {
75 RTC_DCHECK_LE((block_index + 1) * kFftLengthBy2, h.size());
76 RTC_DCHECK_GE(block_index, 0);
77 constexpr float kOneByFftLengthBy2 = 1.f / kFftLengthBy2;
78 const auto sum_of_squares = [](float a, float b) { return a + b * b; };
79 return std::accumulate(h.begin() + block_index * kFftLengthBy2,
80 h.begin() + (block_index + 1) * kFftLengthBy2, 0.f,
81 sum_of_squares) *
82 kOneByFftLengthBy2;
83 }
84
85 } // namespace
86
ReverbDecayEstimator(const EchoCanceller3Config & config)87 ReverbDecayEstimator::ReverbDecayEstimator(const EchoCanceller3Config& config)
88 : filter_length_blocks_(config.filter.refined.length_blocks),
89 filter_length_coefficients_(GetTimeDomainLength(filter_length_blocks_)),
90 use_adaptive_echo_decay_(config.ep_strength.default_len < 0.f),
91 early_reverb_estimator_(config.filter.refined.length_blocks -
92 kEarlyReverbMinSizeBlocks),
93 late_reverb_start_(kEarlyReverbMinSizeBlocks),
94 late_reverb_end_(kEarlyReverbMinSizeBlocks),
95 previous_gains_(config.filter.refined.length_blocks, 0.f),
96 decay_(std::fabs(config.ep_strength.default_len)) {
97 RTC_DCHECK_GT(config.filter.refined.length_blocks,
98 static_cast<size_t>(kEarlyReverbMinSizeBlocks));
99 }
100
101 ReverbDecayEstimator::~ReverbDecayEstimator() = default;
102
Update(rtc::ArrayView<const float> filter,const absl::optional<float> & filter_quality,int filter_delay_blocks,bool usable_linear_filter,bool stationary_signal)103 void ReverbDecayEstimator::Update(rtc::ArrayView<const float> filter,
104 const absl::optional<float>& filter_quality,
105 int filter_delay_blocks,
106 bool usable_linear_filter,
107 bool stationary_signal) {
108 const int filter_size = static_cast<int>(filter.size());
109
110 if (stationary_signal) {
111 return;
112 }
113
114 bool estimation_feasible =
115 filter_delay_blocks <=
116 filter_length_blocks_ - kEarlyReverbMinSizeBlocks - 1;
117 estimation_feasible =
118 estimation_feasible && filter_size == filter_length_coefficients_;
119 estimation_feasible = estimation_feasible && filter_delay_blocks > 0;
120 estimation_feasible = estimation_feasible && usable_linear_filter;
121
122 if (!estimation_feasible) {
123 ResetDecayEstimation();
124 return;
125 }
126
127 if (!use_adaptive_echo_decay_) {
128 return;
129 }
130
131 const float new_smoothing = filter_quality ? *filter_quality * 0.2f : 0.f;
132 smoothing_constant_ = std::max(new_smoothing, smoothing_constant_);
133 if (smoothing_constant_ == 0.f) {
134 return;
135 }
136
137 if (block_to_analyze_ < filter_length_blocks_) {
138 // Analyze the filter and accumulate data for reverb estimation.
139 AnalyzeFilter(filter);
140 ++block_to_analyze_;
141 } else {
142 // When the filter is fully analyzed, estimate the reverb decay and reset
143 // the block_to_analyze_ counter.
144 EstimateDecay(filter, filter_delay_blocks);
145 }
146 }
147
ResetDecayEstimation()148 void ReverbDecayEstimator::ResetDecayEstimation() {
149 early_reverb_estimator_.Reset();
150 late_reverb_decay_estimator_.Reset(0);
151 block_to_analyze_ = 0;
152 estimation_region_candidate_size_ = 0;
153 estimation_region_identified_ = false;
154 smoothing_constant_ = 0.f;
155 late_reverb_start_ = 0;
156 late_reverb_end_ = 0;
157 }
158
EstimateDecay(rtc::ArrayView<const float> filter,int peak_block)159 void ReverbDecayEstimator::EstimateDecay(rtc::ArrayView<const float> filter,
160 int peak_block) {
161 auto& h = filter;
162 RTC_DCHECK_EQ(0, h.size() % kFftLengthBy2);
163
164 // Reset the block analysis counter.
165 block_to_analyze_ =
166 std::min(peak_block + kEarlyReverbMinSizeBlocks, filter_length_blocks_);
167
168 // To estimate the reverb decay, the energy of the first filter section must
169 // be substantially larger than the last. Also, the first filter section
170 // energy must not deviate too much from the max peak.
171 const float first_reverb_gain = BlockEnergyAverage(h, block_to_analyze_);
172 const size_t h_size_blocks = h.size() >> kFftLengthBy2Log2;
173 tail_gain_ = BlockEnergyAverage(h, h_size_blocks - 1);
174 float peak_energy = BlockEnergyPeak(h, peak_block);
175 const bool sufficient_reverb_decay = first_reverb_gain > 4.f * tail_gain_;
176 const bool valid_filter =
177 first_reverb_gain > 2.f * tail_gain_ && peak_energy < 100.f;
178
179 // Estimate the size of the regions with early and late reflections.
180 const int size_early_reverb = early_reverb_estimator_.Estimate();
181 const int size_late_reverb =
182 std::max(estimation_region_candidate_size_ - size_early_reverb, 0);
183
184 // Only update the reverb decay estimate if the size of the identified late
185 // reverb is sufficiently large.
186 if (size_late_reverb >= 5) {
187 if (valid_filter && late_reverb_decay_estimator_.EstimateAvailable()) {
188 float decay = std::pow(
189 2.0f, late_reverb_decay_estimator_.Estimate() * kFftLengthBy2);
190 constexpr float kMaxDecay = 0.95f; // ~1 sec min RT60.
191 constexpr float kMinDecay = 0.02f; // ~15 ms max RT60.
192 decay = std::max(.97f * decay_, decay);
193 decay = std::min(decay, kMaxDecay);
194 decay = std::max(decay, kMinDecay);
195 decay_ += smoothing_constant_ * (decay - decay_);
196 }
197
198 // Update length of decay. Must have enough data (number of sections) in
199 // order to estimate decay rate.
200 late_reverb_decay_estimator_.Reset(size_late_reverb * kFftLengthBy2);
201 late_reverb_start_ =
202 peak_block + kEarlyReverbMinSizeBlocks + size_early_reverb;
203 late_reverb_end_ =
204 block_to_analyze_ + estimation_region_candidate_size_ - 1;
205 } else {
206 late_reverb_decay_estimator_.Reset(0);
207 late_reverb_start_ = 0;
208 late_reverb_end_ = 0;
209 }
210
211 // Reset variables for the identification of the region for reverb decay
212 // estimation.
213 estimation_region_identified_ = !(valid_filter && sufficient_reverb_decay);
214 estimation_region_candidate_size_ = 0;
215
216 // Stop estimation of the decay until another good filter is received.
217 smoothing_constant_ = 0.f;
218
219 // Reset early reflections detector.
220 early_reverb_estimator_.Reset();
221 }
222
AnalyzeFilter(rtc::ArrayView<const float> filter)223 void ReverbDecayEstimator::AnalyzeFilter(rtc::ArrayView<const float> filter) {
224 auto h = rtc::ArrayView<const float>(
225 filter.begin() + block_to_analyze_ * kFftLengthBy2, kFftLengthBy2);
226
227 // Compute squared filter coeffiecients for the block to analyze_;
228 std::array<float, kFftLengthBy2> h2;
229 std::transform(h.begin(), h.end(), h2.begin(), [](float a) { return a * a; });
230
231 // Map out the region for estimating the reverb decay.
232 bool adapting;
233 bool above_noise_floor;
234 AnalyzeBlockGain(h2, tail_gain_, &previous_gains_[block_to_analyze_],
235 &adapting, &above_noise_floor);
236
237 // Count consecutive number of "good" filter sections, where "good" means:
238 // 1) energy is above noise floor.
239 // 2) energy of current section has not changed too much from last check.
240 estimation_region_identified_ =
241 estimation_region_identified_ || adapting || !above_noise_floor;
242 if (!estimation_region_identified_) {
243 ++estimation_region_candidate_size_;
244 }
245
246 // Accumulate data for reverb decay estimation and for the estimation of early
247 // reflections.
248 if (block_to_analyze_ <= late_reverb_end_) {
249 if (block_to_analyze_ >= late_reverb_start_) {
250 for (float h2_k : h2) {
251 float h2_log2 = FastApproxLog2f(h2_k + 1e-10);
252 late_reverb_decay_estimator_.Accumulate(h2_log2);
253 early_reverb_estimator_.Accumulate(h2_log2, smoothing_constant_);
254 }
255 } else {
256 for (float h2_k : h2) {
257 float h2_log2 = FastApproxLog2f(h2_k + 1e-10);
258 early_reverb_estimator_.Accumulate(h2_log2, smoothing_constant_);
259 }
260 }
261 }
262 }
263
Dump(ApmDataDumper * data_dumper) const264 void ReverbDecayEstimator::Dump(ApmDataDumper* data_dumper) const {
265 data_dumper->DumpRaw("aec3_reverb_decay", decay_);
266 data_dumper->DumpRaw("aec3_reverb_tail_energy", tail_gain_);
267 data_dumper->DumpRaw("aec3_reverb_alpha", smoothing_constant_);
268 data_dumper->DumpRaw("aec3_num_reverb_decay_blocks",
269 late_reverb_end_ - late_reverb_start_);
270 data_dumper->DumpRaw("aec3_late_reverb_start", late_reverb_start_);
271 data_dumper->DumpRaw("aec3_late_reverb_end", late_reverb_end_);
272 early_reverb_estimator_.Dump(data_dumper);
273 }
274
Reset(int num_data_points)275 void ReverbDecayEstimator::LateReverbLinearRegressor::Reset(
276 int num_data_points) {
277 RTC_DCHECK_LE(0, num_data_points);
278 RTC_DCHECK_EQ(0, num_data_points % 2);
279 const int N = num_data_points;
280 nz_ = 0.f;
281 // Arithmetic sum of $2 \sum_{i=0.5}^{(N-1)/2}i^2$ calculated directly.
282 nn_ = SymmetricArithmetricSum(N);
283 // The linear regression approach assumes symmetric index around 0.
284 count_ = N > 0 ? -N * 0.5f + 0.5f : 0.f;
285 N_ = N;
286 n_ = 0;
287 }
288
Accumulate(float z)289 void ReverbDecayEstimator::LateReverbLinearRegressor::Accumulate(float z) {
290 nz_ += count_ * z;
291 ++count_;
292 ++n_;
293 }
294
Estimate()295 float ReverbDecayEstimator::LateReverbLinearRegressor::Estimate() {
296 RTC_DCHECK(EstimateAvailable());
297 if (nn_ == 0.f) {
298 RTC_NOTREACHED();
299 return 0.f;
300 }
301 return nz_ / nn_;
302 }
303
EarlyReverbLengthEstimator(int max_blocks)304 ReverbDecayEstimator::EarlyReverbLengthEstimator::EarlyReverbLengthEstimator(
305 int max_blocks)
306 : numerators_smooth_(max_blocks - kBlocksPerSection, 0.f),
307 numerators_(numerators_smooth_.size(), 0.f),
308 coefficients_counter_(0) {
309 RTC_DCHECK_LE(0, max_blocks);
310 }
311
312 ReverbDecayEstimator::EarlyReverbLengthEstimator::
313 ~EarlyReverbLengthEstimator() = default;
314
Reset()315 void ReverbDecayEstimator::EarlyReverbLengthEstimator::Reset() {
316 coefficients_counter_ = 0;
317 std::fill(numerators_.begin(), numerators_.end(), 0.f);
318 block_counter_ = 0;
319 }
320
Accumulate(float value,float smoothing)321 void ReverbDecayEstimator::EarlyReverbLengthEstimator::Accumulate(
322 float value,
323 float smoothing) {
324 // Each section is composed by kBlocksPerSection blocks and each section
325 // overlaps with the next one in (kBlocksPerSection - 1) blocks. For example,
326 // the first section covers the blocks [0:5], the second covers the blocks
327 // [1:6] and so on. As a result, for each value, kBlocksPerSection sections
328 // need to be updated.
329 int first_section_index = std::max(block_counter_ - kBlocksPerSection + 1, 0);
330 int last_section_index =
331 std::min(block_counter_, static_cast<int>(numerators_.size() - 1));
332 float x_value = static_cast<float>(coefficients_counter_) +
333 kEarlyReverbFirstPointAtLinearRegressors;
334 const float value_to_inc = kFftLengthBy2 * value;
335 float value_to_add =
336 x_value * value + (block_counter_ - last_section_index) * value_to_inc;
337 for (int section = last_section_index; section >= first_section_index;
338 --section, value_to_add += value_to_inc) {
339 numerators_[section] += value_to_add;
340 }
341
342 // Check if this update was the last coefficient of the current block. In that
343 // case, check if we are at the end of one of the sections and update the
344 // numerator of the linear regressor that is computed in such section.
345 if (++coefficients_counter_ == kFftLengthBy2) {
346 if (block_counter_ >= (kBlocksPerSection - 1)) {
347 size_t section = block_counter_ - (kBlocksPerSection - 1);
348 RTC_DCHECK_GT(numerators_.size(), section);
349 RTC_DCHECK_GT(numerators_smooth_.size(), section);
350 numerators_smooth_[section] +=
351 smoothing * (numerators_[section] - numerators_smooth_[section]);
352 n_sections_ = section + 1;
353 }
354 ++block_counter_;
355 coefficients_counter_ = 0;
356 }
357 }
358
359 // Estimates the size in blocks of the early reverb. The estimation is done by
360 // comparing the tilt that is estimated in each section. As an optimization
361 // detail and due to the fact that all the linear regressors that are computed
362 // shared the same denominator, the comparison of the tilts is done by a
363 // comparison of the numerator of the linear regressors.
Estimate()364 int ReverbDecayEstimator::EarlyReverbLengthEstimator::Estimate() {
365 constexpr float N = kBlocksPerSection * kFftLengthBy2;
366 constexpr float nn = SymmetricArithmetricSum(N);
367 // numerator_11 refers to the quantity that the linear regressor needs in the
368 // numerator for getting a decay equal to 1.1 (which is not a decay).
369 // log2(1.1) * nn / kFftLengthBy2.
370 constexpr float numerator_11 = 0.13750352374993502f * nn / kFftLengthBy2;
371 // log2(0.8) * nn / kFftLengthBy2.
372 constexpr float numerator_08 = -0.32192809488736229f * nn / kFftLengthBy2;
373 constexpr int kNumSectionsToAnalyze = 9;
374
375 if (n_sections_ < kNumSectionsToAnalyze) {
376 return 0;
377 }
378
379 // Estimation of the blocks that correspond to early reverberations. The
380 // estimation is done by analyzing the impulse response. The portions of the
381 // impulse response whose energy is not decreasing over its coefficients are
382 // considered to be part of the early reverberations. Furthermore, the blocks
383 // where the energy is decreasing faster than what it does at the end of the
384 // impulse response are also considered to be part of the early
385 // reverberations. The estimation is limited to the first
386 // kNumSectionsToAnalyze sections.
387
388 RTC_DCHECK_LE(n_sections_, numerators_smooth_.size());
389 const float min_numerator_tail =
390 *std::min_element(numerators_smooth_.begin() + kNumSectionsToAnalyze,
391 numerators_smooth_.begin() + n_sections_);
392 int early_reverb_size_minus_1 = 0;
393 for (int k = 0; k < kNumSectionsToAnalyze; ++k) {
394 if ((numerators_smooth_[k] > numerator_11) ||
395 (numerators_smooth_[k] < numerator_08 &&
396 numerators_smooth_[k] < 0.9f * min_numerator_tail)) {
397 early_reverb_size_minus_1 = k;
398 }
399 }
400
401 return early_reverb_size_minus_1 == 0 ? 0 : early_reverb_size_minus_1 + 1;
402 }
403
Dump(ApmDataDumper * data_dumper) const404 void ReverbDecayEstimator::EarlyReverbLengthEstimator::Dump(
405 ApmDataDumper* data_dumper) const {
406 data_dumper->DumpRaw("aec3_er_acum_numerator", numerators_smooth_);
407 }
408
409 } // namespace webrtc
410