blob: ed664aae53773696e50a491177c0e11f5933b5b1 [file] [log] [blame]
peah522d71b2017-02-23 05:16:26 -08001/*
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
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020011#include "modules/audio_processing/aec3/aec_state.h"
peah522d71b2017-02-23 05:16:26 -080012
13#include <math.h>
Raphael Kubo da Costa07438142017-10-16 17:00:02 +020014
peah522d71b2017-02-23 05:16:26 -080015#include <numeric>
16#include <vector>
17
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020018#include "api/array_view.h"
19#include "modules/audio_processing/logging/apm_data_dumper.h"
20#include "rtc_base/atomicops.h"
21#include "rtc_base/checks.h"
peah522d71b2017-02-23 05:16:26 -080022
23namespace webrtc {
24namespace {
25
Per Åhgrenb6b00dc2018-02-20 22:18:27 +010026float ComputeGainRampupIncrease(const EchoCanceller3Config& config) {
27 const auto& c = config.echo_removal_control.gain_rampup;
28 return powf(1.f / c.first_non_zero_gain, 1.f / c.non_zero_gain_blocks);
29}
30
Per Åhgren5c532d32018-03-22 00:29:25 +010031constexpr size_t kBlocksSinceConvergencedFilterInit = 10000;
32constexpr size_t kBlocksSinceConsistentEstimateInit = 10000;
33
peah522d71b2017-02-23 05:16:26 -080034} // namespace
35
36int AecState::instance_count_ = 0;
37
Gustaf Ullbergbd83b912017-10-18 12:32:42 +020038AecState::AecState(const EchoCanceller3Config& config)
peah522d71b2017-02-23 05:16:26 -080039 : data_dumper_(
40 new ApmDataDumper(rtc::AtomicOps::Increment(&instance_count_))),
Gustaf Ullbergbd83b912017-10-18 12:32:42 +020041 erle_estimator_(config.erle.min, config.erle.max_l, config.erle.max_h),
peah8cee56f2017-08-24 22:36:53 -070042 config_(config),
Per Åhgren08ea5892018-01-15 08:07:41 +010043 max_render_(config_.filter.main.length_blocks, 0.f),
Christian Schuldtf4e99db2018-03-01 11:32:50 +010044 reverb_decay_(fabsf(config_.ep_strength.default_len)),
Per Åhgren12eb8582018-03-06 10:40:51 +010045 gain_rampup_increase_(ComputeGainRampupIncrease(config_)),
Per Åhgren5c532d32018-03-22 00:29:25 +010046 suppression_gain_limiter_(config_),
47 filter_analyzer_(config_),
48 blocks_since_converged_filter_(kBlocksSinceConvergencedFilterInit),
49 active_blocks_since_consistent_filter_estimate_(
50 kBlocksSinceConsistentEstimateInit) {}
peah522d71b2017-02-23 05:16:26 -080051
52AecState::~AecState() = default;
53
peah86afe9d2017-04-06 15:45:32 -070054void AecState::HandleEchoPathChange(
55 const EchoPathVariability& echo_path_variability) {
Per Åhgren8ba58612017-12-01 23:01:44 +010056 const auto full_reset = [&]() {
Per Åhgren5c532d32018-03-22 00:29:25 +010057 filter_analyzer_.Reset();
Per Åhgren63b494d2017-12-06 11:32:38 +010058 blocks_since_last_saturation_ = 0;
peah86afe9d2017-04-06 15:45:32 -070059 usable_linear_estimate_ = false;
peah86afe9d2017-04-06 15:45:32 -070060 capture_signal_saturation_ = false;
61 echo_saturation_ = false;
Per Åhgren09a718a2017-12-11 22:28:45 +010062 std::fill(max_render_.begin(), max_render_.end(), 0.f);
Per Åhgren4b3bc0f2017-12-20 15:26:13 +010063 blocks_with_proper_filter_adaptation_ = 0;
Per Åhgren5c532d32018-03-22 00:29:25 +010064 blocks_since_reset_ = 0;
Per Åhgren4b3bc0f2017-12-20 15:26:13 +010065 filter_has_had_time_to_converge_ = false;
Per Åhgren8ba58612017-12-01 23:01:44 +010066 render_received_ = false;
Per Åhgren4b3bc0f2017-12-20 15:26:13 +010067 blocks_with_active_render_ = 0;
Per Åhgrena98c8072018-01-15 19:17:16 +010068 initial_state_ = true;
Per Åhgren12eb8582018-03-06 10:40:51 +010069 suppression_gain_limiter_.Reset();
Per Åhgren5c532d32018-03-22 00:29:25 +010070 blocks_since_converged_filter_ = kBlocksSinceConvergencedFilterInit;
71 diverged_blocks_ = 0;
Per Åhgren8ba58612017-12-01 23:01:44 +010072 };
peah6d822ad2017-04-10 13:52:14 -070073
Per Åhgren8ba58612017-12-01 23:01:44 +010074 // TODO(peah): Refine the reset scheme according to the type of gain and
75 // delay adjustment.
76 if (echo_path_variability.gain_change) {
77 full_reset();
78 }
79
80 if (echo_path_variability.delay_change !=
81 EchoPathVariability::DelayAdjustment::kBufferReadjustment) {
82 full_reset();
83 } else if (echo_path_variability.delay_change !=
84 EchoPathVariability::DelayAdjustment::kBufferFlush) {
85 full_reset();
Per Åhgren8ba58612017-12-01 23:01:44 +010086 } else if (echo_path_variability.delay_change !=
87 EchoPathVariability::DelayAdjustment::kDelayReset) {
88 full_reset();
89 } else if (echo_path_variability.delay_change !=
90 EchoPathVariability::DelayAdjustment::kNewDetectedDelay) {
91 full_reset();
92 } else if (echo_path_variability.gain_change) {
Per Åhgren5c532d32018-03-22 00:29:25 +010093 blocks_since_reset_ = kNumBlocksPerSecond;
peah86afe9d2017-04-06 15:45:32 -070094 }
95}
96
Per Åhgren09a718a2017-12-11 22:28:45 +010097void AecState::Update(
Per Åhgren5c532d32018-03-22 00:29:25 +010098 const rtc::Optional<DelayEstimate>& external_delay,
Per Åhgren09a718a2017-12-11 22:28:45 +010099 const std::vector<std::array<float, kFftLengthBy2Plus1>>&
100 adaptive_filter_frequency_response,
101 const std::vector<float>& adaptive_filter_impulse_response,
102 bool converged_filter,
Per Åhgren5c532d32018-03-22 00:29:25 +0100103 bool diverged_filter,
Per Åhgren09a718a2017-12-11 22:28:45 +0100104 const RenderBuffer& render_buffer,
105 const std::array<float, kFftLengthBy2Plus1>& E2_main,
106 const std::array<float, kFftLengthBy2Plus1>& Y2,
Per Åhgren5c532d32018-03-22 00:29:25 +0100107 const std::array<float, kBlockSize>& s) {
108 // Analyze the filter and compute the delays.
109 filter_analyzer_.Update(adaptive_filter_impulse_response, render_buffer);
110 filter_delay_blocks_ = filter_analyzer_.DelayBlocks();
peah86afe9d2017-04-06 15:45:32 -0700111
Per Åhgren5c532d32018-03-22 00:29:25 +0100112 if (filter_analyzer_.Consistent()) {
113 internal_delay_ = filter_analyzer_.DelayBlocks();
114 } else {
115 internal_delay_ = rtc::nullopt;
116 }
117
118 external_delay_seen_ = external_delay_seen_ || external_delay;
119
120 const std::vector<float>& x = render_buffer.Block(-filter_delay_blocks_)[0];
Per Åhgren0e6d2f52017-12-20 22:19:56 +0100121
peah86afe9d2017-04-06 15:45:32 -0700122 // Update counters.
Per Åhgren1b4059e2017-10-15 20:19:21 +0200123 ++capture_block_counter_;
Per Åhgren5c532d32018-03-22 00:29:25 +0100124 ++blocks_since_reset_;
Per Åhgren4b3bc0f2017-12-20 15:26:13 +0100125 const bool active_render_block = DetectActiveRender(x);
126 blocks_with_active_render_ += active_render_block ? 1 : 0;
127 blocks_with_proper_filter_adaptation_ +=
128 active_render_block && !SaturatedCapture() ? 1 : 0;
peah86afe9d2017-04-06 15:45:32 -0700129
Per Åhgrenb6b00dc2018-02-20 22:18:27 +0100130 // Update the limit on the echo suppression after an echo path change to avoid
131 // an initial echo burst.
Per Åhgren5c532d32018-03-22 00:29:25 +0100132 suppression_gain_limiter_.Update(render_buffer.GetRenderActivity(),
133 transparent_mode_);
Per Åhgren4b3bc0f2017-12-20 15:26:13 +0100134
peah86afe9d2017-04-06 15:45:32 -0700135 // Update the ERL and ERLE measures.
Per Åhgren5c532d32018-03-22 00:29:25 +0100136 if (converged_filter && blocks_since_reset_ >= 2 * kNumBlocksPerSecond) {
137 const auto& X2 = render_buffer.Spectrum(filter_delay_blocks_);
peah522d71b2017-02-23 05:16:26 -0800138 erle_estimator_.Update(X2, Y2, E2_main);
139 erl_estimator_.Update(X2, Y2);
peah522d71b2017-02-23 05:16:26 -0800140 }
peah86afe9d2017-04-06 15:45:32 -0700141
Per Åhgren63b494d2017-12-06 11:32:38 +0100142 // Detect and flag echo saturation.
143 // TODO(peah): Add the delay in this computation to ensure that the render and
144 // capture signals are properly aligned.
Gustaf Ullbergbd83b912017-10-18 12:32:42 +0200145 if (config_.ep_strength.echo_can_saturate) {
Per Åhgren31122d62018-04-10 16:33:55 +0200146 echo_saturation_ = DetectEchoSaturation(x, EchoPathGain());
Per Åhgren1b4059e2017-10-15 20:19:21 +0200147 }
peah86afe9d2017-04-06 15:45:32 -0700148
Per Åhgren5c532d32018-03-22 00:29:25 +0100149 bool filter_has_had_time_to_converge =
Per Åhgren29f14322018-02-06 15:31:57 +0100150 blocks_with_proper_filter_adaptation_ >= 1.5f * kNumBlocksPerSecond;
Per Åhgren4b3bc0f2017-12-20 15:26:13 +0100151
Per Åhgren5c532d32018-03-22 00:29:25 +0100152 if (!filter_should_have_converged_) {
153 filter_should_have_converged_ =
154 blocks_with_proper_filter_adaptation_ > 6 * kNumBlocksPerSecond;
155 }
156
157 // Flag whether the initial state is still active.
Per Åhgrena98c8072018-01-15 19:17:16 +0100158 initial_state_ =
159 blocks_with_proper_filter_adaptation_ < 5 * kNumBlocksPerSecond;
160
Per Åhgren5c532d32018-03-22 00:29:25 +0100161 // Update counters for the filter divergence and convergence.
162 diverged_blocks_ = diverged_filter ? diverged_blocks_ + 1 : 0;
163 if (diverged_blocks_ >= 60) {
164 blocks_since_converged_filter_ = kBlocksSinceConvergencedFilterInit;
165 } else {
166 blocks_since_converged_filter_ =
167 converged_filter ? 0 : blocks_since_converged_filter_ + 1;
168 }
Per Åhgren8131eb02018-03-28 18:13:41 +0200169 if (converged_filter) {
170 active_blocks_since_converged_filter_ = 0;
171 } else if (active_render_block) {
172 ++active_blocks_since_converged_filter_;
173 }
174
Per Åhgren5c532d32018-03-22 00:29:25 +0100175 bool recently_converged_filter =
176 blocks_since_converged_filter_ < 60 * kNumBlocksPerSecond;
177
Per Åhgrenf3e2bf12018-03-22 10:15:59 +0100178 if (blocks_since_converged_filter_ > 20 * kNumBlocksPerSecond) {
179 converged_filter_count_ = 0;
180 } else if (converged_filter) {
181 ++converged_filter_count_;
182 }
183 if (converged_filter_count_ > 50) {
184 finite_erl_ = true;
185 }
186
Per Åhgren5c532d32018-03-22 00:29:25 +0100187 if (filter_analyzer_.Consistent() && filter_delay_blocks_ < 5) {
188 consistent_filter_seen_ = true;
189 active_blocks_since_consistent_filter_estimate_ = 0;
190 } else if (active_render_block) {
191 ++active_blocks_since_consistent_filter_estimate_;
192 }
193
194 bool consistent_filter_estimate_not_seen;
195 if (!consistent_filter_seen_) {
196 consistent_filter_estimate_not_seen =
197 capture_block_counter_ > 5 * kNumBlocksPerSecond;
198 } else {
199 consistent_filter_estimate_not_seen =
200 active_blocks_since_consistent_filter_estimate_ >
201 30 * kNumBlocksPerSecond;
202 }
203
204 converged_filter_seen_ = converged_filter_seen_ || converged_filter;
Per Åhgren63b494d2017-12-06 11:32:38 +0100205
Per Åhgren8131eb02018-03-28 18:13:41 +0200206 // If no filter convergence is seen for a long time, reset the estimated
207 // properties of the echo path.
208 if (active_blocks_since_converged_filter_ > 60 * kNumBlocksPerSecond) {
209 converged_filter_seen_ = false;
210 finite_erl_ = false;
211 }
212
Per Åhgren63b494d2017-12-06 11:32:38 +0100213 // After an amount of active render samples for which an echo should have been
214 // detected in the capture signal if the ERL was not infinite, flag that a
215 // transparent mode should be entered.
Per Åhgrenf3e2bf12018-03-22 10:15:59 +0100216 transparent_mode_ = !config_.ep_strength.bounded_erl && !finite_erl_;
Per Åhgren4b3bc0f2017-12-20 15:26:13 +0100217 transparent_mode_ =
Per Åhgren5c532d32018-03-22 00:29:25 +0100218 transparent_mode_ &&
219 (consistent_filter_estimate_not_seen || !converged_filter_seen_);
220 transparent_mode_ = transparent_mode_ &&
221 (filter_should_have_converged_ ||
222 (!external_delay_seen_ &&
223 capture_block_counter_ > 10 * kNumBlocksPerSecond));
224
225 usable_linear_estimate_ = !echo_saturation_;
226 usable_linear_estimate_ =
227 usable_linear_estimate_ && filter_has_had_time_to_converge;
228 usable_linear_estimate_ =
229 usable_linear_estimate_ && recently_converged_filter;
230 usable_linear_estimate_ = usable_linear_estimate_ && !diverged_filter;
231 usable_linear_estimate_ = usable_linear_estimate_ && external_delay;
232
233 use_linear_filter_output_ = usable_linear_estimate_ && !TransparentMode();
234
235 data_dumper_->DumpRaw("aec3_erle", Erle());
Jesús de Vicente Peña7682c6e2018-03-22 14:53:23 +0100236 data_dumper_->DumpRaw("aec3_erle_onset", erle_estimator_.ErleOnsets());
Per Åhgren5c532d32018-03-22 00:29:25 +0100237 data_dumper_->DumpRaw("aec3_erl", Erl());
238 data_dumper_->DumpRaw("aec3_erle_time_domain", ErleTimeDomain());
239 data_dumper_->DumpRaw("aec3_erl_time_domain", ErlTimeDomain());
240 data_dumper_->DumpRaw("aec3_usable_linear_estimate", UsableLinearEstimate());
241 data_dumper_->DumpRaw("aec3_transparent_mode", transparent_mode_);
242 data_dumper_->DumpRaw("aec3_state_internal_delay",
243 internal_delay_ ? *internal_delay_ : -1);
244 data_dumper_->DumpRaw("aec3_filter_delay", filter_analyzer_.DelayBlocks());
245
246 data_dumper_->DumpRaw("aec3_consistent_filter",
247 filter_analyzer_.Consistent());
248 data_dumper_->DumpRaw("aec3_suppression_gain_limit", SuppressionGainLimit());
249 data_dumper_->DumpRaw("aec3_initial_state", InitialState());
250 data_dumper_->DumpRaw("aec3_capture_saturation", SaturatedCapture());
251 data_dumper_->DumpRaw("aec3_echo_saturation", echo_saturation_);
252 data_dumper_->DumpRaw("aec3_converged_filter", converged_filter);
253 data_dumper_->DumpRaw("aec3_diverged_filter", diverged_filter);
254
255 data_dumper_->DumpRaw("aec3_external_delay_avaliable",
256 external_delay ? 1 : 0);
257 data_dumper_->DumpRaw("aec3_consistent_filter_estimate_not_seen",
258 consistent_filter_estimate_not_seen);
259 data_dumper_->DumpRaw("aec3_filter_should_have_converged",
260 filter_should_have_converged_);
261 data_dumper_->DumpRaw("aec3_filter_has_had_time_to_converge",
262 filter_has_had_time_to_converge);
263 data_dumper_->DumpRaw("aec3_recently_converged_filter",
264 recently_converged_filter);
peah29103572017-07-11 02:54:02 -0700265}
266
Per Åhgren09a718a2017-12-11 22:28:45 +0100267void AecState::UpdateReverb(const std::vector<float>& impulse_response) {
Christian Schuldtf4e99db2018-03-01 11:32:50 +0100268 // Echo tail estimation enabled if the below variable is set as negative.
269 if (config_.ep_strength.default_len > 0.f) {
270 return;
271 }
272
Per Åhgren5c532d32018-03-22 00:29:25 +0100273 if ((!(filter_delay_blocks_ && usable_linear_estimate_)) ||
274 (filter_delay_blocks_ >
Per Åhgren08ea5892018-01-15 08:07:41 +0100275 static_cast<int>(config_.filter.main.length_blocks) - 4)) {
peah29103572017-07-11 02:54:02 -0700276 return;
277 }
278
Christian Schuldtf4e99db2018-03-01 11:32:50 +0100279 constexpr float kOneByFftLengthBy2 = 1.f / kFftLengthBy2;
280
peah29103572017-07-11 02:54:02 -0700281 // Form the data to match against by squaring the impulse response
282 // coefficients.
Per Åhgren09a718a2017-12-11 22:28:45 +0100283 std::array<float, GetTimeDomainLength(kMaxAdaptiveFilterLength)>
284 matching_data_data;
Per Åhgren08ea5892018-01-15 08:07:41 +0100285 RTC_DCHECK_LE(GetTimeDomainLength(config_.filter.main.length_blocks),
Per Åhgren09a718a2017-12-11 22:28:45 +0100286 matching_data_data.size());
287 rtc::ArrayView<float> matching_data(
288 matching_data_data.data(),
Per Åhgren08ea5892018-01-15 08:07:41 +0100289 GetTimeDomainLength(config_.filter.main.length_blocks));
peah29103572017-07-11 02:54:02 -0700290 std::transform(impulse_response.begin(), impulse_response.end(),
291 matching_data.begin(), [](float a) { return a * a; });
292
Christian Schuldtf4e99db2018-03-01 11:32:50 +0100293 if (current_reverb_decay_section_ < config_.filter.main.length_blocks) {
294 // Update accumulated variables for the current filter section.
peah29103572017-07-11 02:54:02 -0700295
Christian Schuldtf4e99db2018-03-01 11:32:50 +0100296 const size_t start_index = current_reverb_decay_section_ * kFftLengthBy2;
peah29103572017-07-11 02:54:02 -0700297
Christian Schuldtf4e99db2018-03-01 11:32:50 +0100298 RTC_DCHECK_GT(matching_data.size(), start_index);
299 RTC_DCHECK_GE(matching_data.size(), start_index + kFftLengthBy2);
300 float section_energy =
301 std::accumulate(matching_data.begin() + start_index,
302 matching_data.begin() + start_index + kFftLengthBy2,
303 0.f) *
304 kOneByFftLengthBy2;
305
306 section_energy = std::max(
307 section_energy, 1e-32f); // Regularization to avoid division by 0.
308
309 RTC_DCHECK_LT(current_reverb_decay_section_, block_energies_.size());
310 const float energy_ratio =
311 block_energies_[current_reverb_decay_section_] / section_energy;
312
313 main_filter_is_adapting_ = main_filter_is_adapting_ ||
314 (energy_ratio > 1.1f || energy_ratio < 0.9f);
315
316 // Count consecutive number of "good" filter sections, where "good" means:
317 // 1) energy is above noise floor.
318 // 2) energy of current section has not changed too much from last check.
319 if (!found_end_of_reverb_decay_ && section_energy > tail_energy_ &&
320 !main_filter_is_adapting_) {
321 ++num_reverb_decay_sections_next_;
322 } else {
323 found_end_of_reverb_decay_ = true;
324 }
325
326 block_energies_[current_reverb_decay_section_] = section_energy;
327
328 if (num_reverb_decay_sections_ > 0) {
329 // Linear regression of log squared magnitude of impulse response.
330 for (size_t i = 0; i < kFftLengthBy2; i++) {
331 auto fast_approx_log2f = [](const float in) {
332 RTC_DCHECK_GT(in, .0f);
333 // Read and interpret float as uint32_t and then cast to float.
334 // This is done to extract the exponent (bits 30 - 23).
335 // "Right shift" of the exponent is then performed by multiplying
336 // with the constant (1/2^23). Finally, we subtract a constant to
337 // remove the bias (https://en.wikipedia.org/wiki/Exponent_bias).
338 union {
339 float dummy;
340 uint32_t a;
341 } x = {in};
342 float out = x.a;
343 out *= 1.1920929e-7f; // 1/2^23
344 out -= 126.942695f; // Remove bias.
345 return out;
346 };
347 RTC_DCHECK_GT(matching_data.size(), start_index + i);
348 float z = fast_approx_log2f(matching_data[start_index + i]);
349 accumulated_nz_ += accumulated_count_ * z;
350 ++accumulated_count_;
peah29103572017-07-11 02:54:02 -0700351 }
peah29103572017-07-11 02:54:02 -0700352 }
353
Christian Schuldtf4e99db2018-03-01 11:32:50 +0100354 num_reverb_decay_sections_ =
355 num_reverb_decay_sections_ > 0 ? num_reverb_decay_sections_ - 1 : 0;
356 ++current_reverb_decay_section_;
357
358 } else {
359 constexpr float kMaxDecay = 0.95f; // ~1 sec min RT60.
360 constexpr float kMinDecay = 0.02f; // ~15 ms max RT60.
361
362 // Accumulated variables throughout whole filter.
363
364 // Solve for decay rate.
365
366 float decay = reverb_decay_;
367
368 if (accumulated_nn_ != 0.f) {
369 const float exp_candidate = -accumulated_nz_ / accumulated_nn_;
370 decay = powf(2.0f, -exp_candidate * kFftLengthBy2);
371 decay = std::min(decay, kMaxDecay);
372 decay = std::max(decay, kMinDecay);
peah29103572017-07-11 02:54:02 -0700373 }
peah29103572017-07-11 02:54:02 -0700374
Christian Schuldtf4e99db2018-03-01 11:32:50 +0100375 // Filter tail energy (assumed to be noise).
peah29103572017-07-11 02:54:02 -0700376
Christian Schuldtf4e99db2018-03-01 11:32:50 +0100377 constexpr size_t kTailLength = kFftLength;
378 constexpr float k1ByTailLength = 1.f / kTailLength;
379 const size_t tail_index =
380 GetTimeDomainLength(config_.filter.main.length_blocks) - kTailLength;
peah29103572017-07-11 02:54:02 -0700381
Christian Schuldtf4e99db2018-03-01 11:32:50 +0100382 RTC_DCHECK_GT(matching_data.size(), tail_index);
383 tail_energy_ = std::accumulate(matching_data.begin() + tail_index,
384 matching_data.end(), 0.f) *
385 k1ByTailLength;
386
387 // Update length of decay.
388 num_reverb_decay_sections_ = num_reverb_decay_sections_next_;
389 num_reverb_decay_sections_next_ = 0;
390 // Must have enough data (number of sections) in order
391 // to estimate decay rate.
392 if (num_reverb_decay_sections_ < 5) {
393 num_reverb_decay_sections_ = 0;
peah29103572017-07-11 02:54:02 -0700394 }
Christian Schuldtf4e99db2018-03-01 11:32:50 +0100395
396 const float N = num_reverb_decay_sections_ * kFftLengthBy2;
397 accumulated_nz_ = 0.f;
398 const float k1By12 = 1.f / 12.f;
399 // Arithmetic sum $2 \sum_{i=0}^{(N-1)/2}i^2$ calculated directly.
400 accumulated_nn_ = N * (N * N - 1.0f) * k1By12;
401 accumulated_count_ = -N * 0.5f;
402 // Linear regression approach assumes symmetric index around 0.
403 accumulated_count_ += 0.5f;
404
405 // Identify the peak index of the impulse response.
406 const size_t peak_index = std::distance(
407 matching_data.begin(),
408 std::max_element(matching_data.begin(), matching_data.end()));
409
410 current_reverb_decay_section_ = peak_index * kOneByFftLengthBy2 + 3;
411 // Make sure we're not out of bounds.
412 if (current_reverb_decay_section_ + 1 >=
413 config_.filter.main.length_blocks) {
414 current_reverb_decay_section_ = config_.filter.main.length_blocks;
415 }
416 size_t start_index = current_reverb_decay_section_ * kFftLengthBy2;
417 float first_section_energy =
418 std::accumulate(matching_data.begin() + start_index,
419 matching_data.begin() + start_index + kFftLengthBy2,
420 0.f) *
421 kOneByFftLengthBy2;
422
423 // To estimate the reverb decay, the energy of the first filter section
424 // must be substantially larger than the last.
425 // Also, the first filter section energy must not deviate too much
426 // from the max peak.
427 bool main_filter_has_reverb = first_section_energy > 4.f * tail_energy_;
428 bool main_filter_is_sane = first_section_energy > 2.f * tail_energy_ &&
429 matching_data[peak_index] < 100.f;
430
431 // Not detecting any decay, but tail is over noise - assume max decay.
432 if (num_reverb_decay_sections_ == 0 && main_filter_is_sane &&
433 main_filter_has_reverb) {
434 decay = kMaxDecay;
435 }
436
437 if (!main_filter_is_adapting_ && main_filter_is_sane &&
438 num_reverb_decay_sections_ > 0) {
439 decay = std::max(.97f * reverb_decay_, decay);
440 reverb_decay_ -= .1f * (reverb_decay_ - decay);
441 }
442
443 found_end_of_reverb_decay_ =
444 !(main_filter_is_sane && main_filter_has_reverb);
445 main_filter_is_adapting_ = false;
peah29103572017-07-11 02:54:02 -0700446 }
447
peah29103572017-07-11 02:54:02 -0700448 data_dumper_->DumpRaw("aec3_reverb_decay", reverb_decay_);
Christian Schuldtf4e99db2018-03-01 11:32:50 +0100449 data_dumper_->DumpRaw("aec3_reverb_tail_energy", tail_energy_);
Per Åhgren12eb8582018-03-06 10:40:51 +0100450 data_dumper_->DumpRaw("aec3_suppression_gain_limit", SuppressionGainLimit());
peah29103572017-07-11 02:54:02 -0700451}
452
Per Åhgren4b3bc0f2017-12-20 15:26:13 +0100453bool AecState::DetectActiveRender(rtc::ArrayView<const float> x) const {
454 const float x_energy = std::inner_product(x.begin(), x.end(), x.begin(), 0.f);
455 return x_energy > (config_.render_levels.active_render_limit *
456 config_.render_levels.active_render_limit) *
457 kFftLengthBy2;
458}
459
Per Åhgren31122d62018-04-10 16:33:55 +0200460bool AecState::DetectEchoSaturation(rtc::ArrayView<const float> x,
461 float echo_path_gain) {
Per Åhgren4b3bc0f2017-12-20 15:26:13 +0100462 RTC_DCHECK_LT(0, x.size());
463 const float max_sample = fabs(*std::max_element(
464 x.begin(), x.end(), [](float a, float b) { return a * a < b * b; }));
Per Åhgren4b3bc0f2017-12-20 15:26:13 +0100465
466 // Set flag for potential presence of saturated echo
Per Åhgren31122d62018-04-10 16:33:55 +0200467 const float kMargin = 10.f;
468 float peak_echo_amplitude = max_sample * echo_path_gain * kMargin;
469 if (SaturatedCapture() && peak_echo_amplitude > 32000) {
470 blocks_since_last_saturation_ = 0;
471 } else {
472 ++blocks_since_last_saturation_;
473 }
Per Åhgren4b3bc0f2017-12-20 15:26:13 +0100474
Per Åhgren31122d62018-04-10 16:33:55 +0200475 return blocks_since_last_saturation_ < 5;
Per Åhgren4b3bc0f2017-12-20 15:26:13 +0100476}
477
peah522d71b2017-02-23 05:16:26 -0800478} // namespace webrtc