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