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