blob: b37b303da8d7ed6e806630aa425a691521500ef9 [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
Jesús de Vicente Peña496cedf2018-07-04 11:02:09 +020018#include "absl/types/optional.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020019#include "api/array_view.h"
Jesús de Vicente Peña496cedf2018-07-04 11:02:09 +020020#include "modules/audio_processing/aec3/aec3_common.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020021#include "modules/audio_processing/logging/apm_data_dumper.h"
22#include "rtc_base/atomicops.h"
23#include "rtc_base/checks.h"
Per Åhgrend18e87e2018-05-09 12:07:26 +020024#include "system_wrappers/include/field_trial.h"
peah522d71b2017-02-23 05:16:26 -080025
26namespace webrtc {
27namespace {
28
Per Åhgrenb6b00dc2018-02-20 22:18:27 +010029float ComputeGainRampupIncrease(const EchoCanceller3Config& config) {
30 const auto& c = config.echo_removal_control.gain_rampup;
31 return powf(1.f / c.first_non_zero_gain, 1.f / c.non_zero_gain_blocks);
32}
33
Per Åhgren5c532d32018-03-22 00:29:25 +010034constexpr size_t kBlocksSinceConvergencedFilterInit = 10000;
35constexpr size_t kBlocksSinceConsistentEstimateInit = 10000;
36
peah522d71b2017-02-23 05:16:26 -080037} // namespace
38
39int AecState::instance_count_ = 0;
40
Gustaf Ullbergbd83b912017-10-18 12:32:42 +020041AecState::AecState(const EchoCanceller3Config& config)
peah522d71b2017-02-23 05:16:26 -080042 : data_dumper_(
43 new ApmDataDumper(rtc::AtomicOps::Increment(&instance_count_))),
peah8cee56f2017-08-24 22:36:53 -070044 config_(config),
Per Åhgren90e3fbd2018-05-16 15:25:04 +020045 erle_estimator_(config.erle.min, config.erle.max_l, config.erle.max_h),
Per Åhgren08ea5892018-01-15 08:07:41 +010046 max_render_(config_.filter.main.length_blocks, 0.f),
Per Åhgren12eb8582018-03-06 10:40:51 +010047 gain_rampup_increase_(ComputeGainRampupIncrease(config_)),
Per Åhgren5c532d32018-03-22 00:29:25 +010048 suppression_gain_limiter_(config_),
49 filter_analyzer_(config_),
50 blocks_since_converged_filter_(kBlocksSinceConvergencedFilterInit),
51 active_blocks_since_consistent_filter_estimate_(
Jesús de Vicente Peña496cedf2018-07-04 11:02:09 +020052 kBlocksSinceConsistentEstimateInit),
Jesús de Vicente Peña836a7a22018-08-31 15:03:04 +020053 echo_audibility_(
54 config.echo_audibility.use_stationarity_properties_at_init),
Jesús de Vicente Peña496cedf2018-07-04 11:02:09 +020055 reverb_model_estimator_(config) {}
peah522d71b2017-02-23 05:16:26 -080056
57AecState::~AecState() = default;
58
peah86afe9d2017-04-06 15:45:32 -070059void AecState::HandleEchoPathChange(
60 const EchoPathVariability& echo_path_variability) {
Per Åhgren8ba58612017-12-01 23:01:44 +010061 const auto full_reset = [&]() {
Per Åhgren5c532d32018-03-22 00:29:25 +010062 filter_analyzer_.Reset();
Per Åhgren63b494d2017-12-06 11:32:38 +010063 blocks_since_last_saturation_ = 0;
peah86afe9d2017-04-06 15:45:32 -070064 usable_linear_estimate_ = false;
peah86afe9d2017-04-06 15:45:32 -070065 capture_signal_saturation_ = false;
66 echo_saturation_ = false;
Per Åhgren09a718a2017-12-11 22:28:45 +010067 std::fill(max_render_.begin(), max_render_.end(), 0.f);
Per Åhgren4b3bc0f2017-12-20 15:26:13 +010068 blocks_with_proper_filter_adaptation_ = 0;
Per Åhgren5c532d32018-03-22 00:29:25 +010069 blocks_since_reset_ = 0;
Per Åhgren4b3bc0f2017-12-20 15:26:13 +010070 filter_has_had_time_to_converge_ = false;
Per Åhgren8ba58612017-12-01 23:01:44 +010071 render_received_ = false;
Per Åhgren4b3bc0f2017-12-20 15:26:13 +010072 blocks_with_active_render_ = 0;
Per Åhgrena98c8072018-01-15 19:17:16 +010073 initial_state_ = true;
Per Åhgren12eb8582018-03-06 10:40:51 +010074 suppression_gain_limiter_.Reset();
Per Åhgren5c532d32018-03-22 00:29:25 +010075 blocks_since_converged_filter_ = kBlocksSinceConvergencedFilterInit;
76 diverged_blocks_ = 0;
Per Åhgrenb2d71162018-09-10 14:10:34 +020077 if (config_.echo_removal_control.linear_and_stable_echo_path) {
78 converged_filter_seen_ = false;
79 }
Per Åhgrenf4801a12018-09-27 13:14:02 +020080 erle_estimator_.Reset();
Per Åhgren8ba58612017-12-01 23:01:44 +010081 };
peah6d822ad2017-04-10 13:52:14 -070082
Per Åhgren8ba58612017-12-01 23:01:44 +010083 // TODO(peah): Refine the reset scheme according to the type of gain and
84 // delay adjustment.
Per Åhgren8ba58612017-12-01 23:01:44 +010085
86 if (echo_path_variability.delay_change !=
Per Åhgren88cf0502018-07-16 17:08:41 +020087 EchoPathVariability::DelayAdjustment::kNone) {
Per Åhgren8ba58612017-12-01 23:01:44 +010088 full_reset();
peah86afe9d2017-04-06 15:45:32 -070089 }
Per Åhgrenb20b9372018-07-13 00:22:54 +020090
91 subtractor_output_analyzer_.HandleEchoPathChange();
peah86afe9d2017-04-06 15:45:32 -070092}
93
Per Åhgren09a718a2017-12-11 22:28:45 +010094void AecState::Update(
Danil Chapovalovdb9f7ab2018-06-19 10:50:11 +020095 const absl::optional<DelayEstimate>& external_delay,
Per Åhgren09a718a2017-12-11 22:28:45 +010096 const std::vector<std::array<float, kFftLengthBy2Plus1>>&
97 adaptive_filter_frequency_response,
98 const std::vector<float>& adaptive_filter_impulse_response,
Per Åhgren09a718a2017-12-11 22:28:45 +010099 const RenderBuffer& render_buffer,
100 const std::array<float, kFftLengthBy2Plus1>& E2_main,
101 const std::array<float, kFftLengthBy2Plus1>& Y2,
Per Åhgrenb20b9372018-07-13 00:22:54 +0200102 const SubtractorOutput& subtractor_output,
103 rtc::ArrayView<const float> y) {
104 // Analyze the filter output.
Per Åhgrene4db6a12018-07-26 15:32:24 +0200105 subtractor_output_analyzer_.Update(subtractor_output);
Per Åhgrenb20b9372018-07-13 00:22:54 +0200106
107 const bool converged_filter = subtractor_output_analyzer_.ConvergedFilter();
108 const bool diverged_filter = subtractor_output_analyzer_.DivergedFilter();
109
Per Åhgren5c532d32018-03-22 00:29:25 +0100110 // Analyze the filter and compute the delays.
Jesús de Vicente Peñae58bd8a2018-06-26 17:19:15 +0200111 filter_analyzer_.Update(adaptive_filter_impulse_response,
112 adaptive_filter_frequency_response, render_buffer);
Per Åhgren5c532d32018-03-22 00:29:25 +0100113 filter_delay_blocks_ = filter_analyzer_.DelayBlocks();
Per Åhgrenf4801a12018-09-27 13:14:02 +0200114 if (external_delay &&
115 (!external_delay_ || external_delay_->delay != external_delay->delay)) {
116 frames_since_external_delay_change_ = 0;
117 external_delay_ = external_delay;
118 }
119 if (blocks_with_proper_filter_adaptation_ < 2 * kNumBlocksPerSecond &&
120 external_delay_) {
121 filter_delay_blocks_ = config_.delay.delay_headroom_blocks;
Per Åhgren05d8ee12018-06-07 15:59:59 +0200122 }
peah86afe9d2017-04-06 15:45:32 -0700123
Per Åhgren5c532d32018-03-22 00:29:25 +0100124 if (filter_analyzer_.Consistent()) {
125 internal_delay_ = filter_analyzer_.DelayBlocks();
126 } else {
Danil Chapovalovdb9f7ab2018-06-19 10:50:11 +0200127 internal_delay_ = absl::nullopt;
Per Åhgren5c532d32018-03-22 00:29:25 +0100128 }
129
130 external_delay_seen_ = external_delay_seen_ || external_delay;
131
132 const std::vector<float>& x = render_buffer.Block(-filter_delay_blocks_)[0];
Per Åhgren0e6d2f52017-12-20 22:19:56 +0100133
peah86afe9d2017-04-06 15:45:32 -0700134 // Update counters.
Per Åhgren1b4059e2017-10-15 20:19:21 +0200135 ++capture_block_counter_;
Per Åhgren5c532d32018-03-22 00:29:25 +0100136 ++blocks_since_reset_;
Per Åhgren4b3bc0f2017-12-20 15:26:13 +0100137 const bool active_render_block = DetectActiveRender(x);
138 blocks_with_active_render_ += active_render_block ? 1 : 0;
139 blocks_with_proper_filter_adaptation_ +=
140 active_render_block && !SaturatedCapture() ? 1 : 0;
peah86afe9d2017-04-06 15:45:32 -0700141
Per Åhgrenb6b00dc2018-02-20 22:18:27 +0100142 // Update the limit on the echo suppression after an echo path change to avoid
143 // an initial echo burst.
Per Åhgren5c532d32018-03-22 00:29:25 +0100144 suppression_gain_limiter_.Update(render_buffer.GetRenderActivity(),
145 transparent_mode_);
Per Åhgrenf4801a12018-09-27 13:14:02 +0200146 if (converged_filter) {
Per Åhgren6204adf2018-08-19 11:12:00 +0200147 suppression_gain_limiter_.Deactivate();
148 }
Per Åhgren4b3bc0f2017-12-20 15:26:13 +0100149
Jesús de Vicente Peñadc872b62018-04-25 16:11:42 +0200150 if (UseStationaryProperties()) {
Jesús de Vicente Peñad5cb4772018-04-25 13:58:45 +0200151 // Update the echo audibility evaluator.
Jesús de Vicente Peña075cb2b2018-06-13 15:13:55 +0200152 echo_audibility_.Update(
153 render_buffer, FilterDelayBlocks(), external_delay_seen_,
154 config_.ep_strength.reverb_based_on_render ? ReverbDecay() : 0.f);
Jesús de Vicente Peñad5cb4772018-04-25 13:58:45 +0200155 }
156
peah86afe9d2017-04-06 15:45:32 -0700157 // Update the ERL and ERLE measures.
Per Åhgrenf4801a12018-09-27 13:14:02 +0200158 if (transition_triggered_) {
Jesús de Vicente Peña02e9e442018-08-29 13:34:07 +0200159 erle_estimator_.Reset();
160 }
Jesús de Vicente Peña666beca2018-05-21 15:23:48 +0200161 if (blocks_since_reset_ >= 2 * kNumBlocksPerSecond) {
Per Åhgren5c532d32018-03-22 00:29:25 +0100162 const auto& X2 = render_buffer.Spectrum(filter_delay_blocks_);
Jesús de Vicente Peñaa6878122018-08-28 14:27:45 +0200163 erle_estimator_.Update(X2, Y2, E2_main, converged_filter,
164 config_.erle.onset_detection);
Jesús de Vicente Peña666beca2018-05-21 15:23:48 +0200165 if (converged_filter) {
166 erl_estimator_.Update(X2, Y2);
167 }
peah522d71b2017-02-23 05:16:26 -0800168 }
peah86afe9d2017-04-06 15:45:32 -0700169
Per Åhgren63b494d2017-12-06 11:32:38 +0100170 // Detect and flag echo saturation.
Gustaf Ullbergbd83b912017-10-18 12:32:42 +0200171 if (config_.ep_strength.echo_can_saturate) {
Per Åhgren31122d62018-04-10 16:33:55 +0200172 echo_saturation_ = DetectEchoSaturation(x, EchoPathGain());
Per Åhgren1b4059e2017-10-15 20:19:21 +0200173 }
peah86afe9d2017-04-06 15:45:32 -0700174
Per Åhgrenf4801a12018-09-27 13:14:02 +0200175 if (config_.filter.conservative_initial_phase) {
Per Åhgren22754392018-08-10 18:37:38 +0200176 filter_has_had_time_to_converge_ =
Per Åhgrenf954ba52018-07-27 14:53:58 +0200177 blocks_with_proper_filter_adaptation_ >= 1.5f * kNumBlocksPerSecond;
Per Åhgrenf4801a12018-09-27 13:14:02 +0200178 } else {
179 filter_has_had_time_to_converge_ =
180 blocks_with_proper_filter_adaptation_ >= 0.8f * kNumBlocksPerSecond;
Per Åhgrenf954ba52018-07-27 14:53:58 +0200181 }
Per Åhgren4b3bc0f2017-12-20 15:26:13 +0100182
Per Åhgren5c532d32018-03-22 00:29:25 +0100183 if (!filter_should_have_converged_) {
184 filter_should_have_converged_ =
185 blocks_with_proper_filter_adaptation_ > 6 * kNumBlocksPerSecond;
186 }
187
188 // Flag whether the initial state is still active.
Jesús de Vicente Peña02e9e442018-08-29 13:34:07 +0200189 bool prev_initial_state = initial_state_;
Per Åhgrenf4801a12018-09-27 13:14:02 +0200190 if (config_.filter.conservative_initial_phase) {
Per Åhgrenf954ba52018-07-27 14:53:58 +0200191 initial_state_ =
192 blocks_with_proper_filter_adaptation_ < 5 * kNumBlocksPerSecond;
Per Åhgrenf4801a12018-09-27 13:14:02 +0200193 } else {
194 initial_state_ = blocks_with_proper_filter_adaptation_ <
195 config_.filter.initial_state_seconds * kNumBlocksPerSecond;
Per Åhgrenf954ba52018-07-27 14:53:58 +0200196 }
Jesús de Vicente Peña02e9e442018-08-29 13:34:07 +0200197 transition_triggered_ = !initial_state_ && prev_initial_state;
Per Åhgrena98c8072018-01-15 19:17:16 +0100198
Per Åhgren5c532d32018-03-22 00:29:25 +0100199 // Update counters for the filter divergence and convergence.
200 diverged_blocks_ = diverged_filter ? diverged_blocks_ + 1 : 0;
201 if (diverged_blocks_ >= 60) {
202 blocks_since_converged_filter_ = kBlocksSinceConvergencedFilterInit;
203 } else {
204 blocks_since_converged_filter_ =
205 converged_filter ? 0 : blocks_since_converged_filter_ + 1;
206 }
Per Åhgren8131eb02018-03-28 18:13:41 +0200207 if (converged_filter) {
208 active_blocks_since_converged_filter_ = 0;
209 } else if (active_render_block) {
210 ++active_blocks_since_converged_filter_;
211 }
212
Per Åhgren5c532d32018-03-22 00:29:25 +0100213 bool recently_converged_filter =
214 blocks_since_converged_filter_ < 60 * kNumBlocksPerSecond;
215
Per Åhgrenf3e2bf12018-03-22 10:15:59 +0100216 if (blocks_since_converged_filter_ > 20 * kNumBlocksPerSecond) {
217 converged_filter_count_ = 0;
218 } else if (converged_filter) {
219 ++converged_filter_count_;
220 }
221 if (converged_filter_count_ > 50) {
222 finite_erl_ = true;
223 }
224
Per Åhgren5c532d32018-03-22 00:29:25 +0100225 if (filter_analyzer_.Consistent() && filter_delay_blocks_ < 5) {
226 consistent_filter_seen_ = true;
227 active_blocks_since_consistent_filter_estimate_ = 0;
228 } else if (active_render_block) {
229 ++active_blocks_since_consistent_filter_estimate_;
230 }
231
232 bool consistent_filter_estimate_not_seen;
233 if (!consistent_filter_seen_) {
234 consistent_filter_estimate_not_seen =
235 capture_block_counter_ > 5 * kNumBlocksPerSecond;
236 } else {
237 consistent_filter_estimate_not_seen =
238 active_blocks_since_consistent_filter_estimate_ >
239 30 * kNumBlocksPerSecond;
240 }
241
242 converged_filter_seen_ = converged_filter_seen_ || converged_filter;
Per Åhgren63b494d2017-12-06 11:32:38 +0100243
Per Åhgren8131eb02018-03-28 18:13:41 +0200244 // If no filter convergence is seen for a long time, reset the estimated
245 // properties of the echo path.
246 if (active_blocks_since_converged_filter_ > 60 * kNumBlocksPerSecond) {
247 converged_filter_seen_ = false;
248 finite_erl_ = false;
249 }
250
Per Åhgren63b494d2017-12-06 11:32:38 +0100251 // After an amount of active render samples for which an echo should have been
252 // detected in the capture signal if the ERL was not infinite, flag that a
253 // transparent mode should be entered.
Per Åhgrenf3e2bf12018-03-22 10:15:59 +0100254 transparent_mode_ = !config_.ep_strength.bounded_erl && !finite_erl_;
Per Åhgren4b3bc0f2017-12-20 15:26:13 +0100255 transparent_mode_ =
Per Åhgren5c532d32018-03-22 00:29:25 +0100256 transparent_mode_ &&
257 (consistent_filter_estimate_not_seen || !converged_filter_seen_);
Per Åhgrend18e87e2018-05-09 12:07:26 +0200258 transparent_mode_ = transparent_mode_ && filter_should_have_converged_;
Per Åhgren5c532d32018-03-22 00:29:25 +0100259
260 usable_linear_estimate_ = !echo_saturation_;
Per Åhgrene3ca9912018-05-28 22:57:17 +0200261
Per Åhgrenf4801a12018-09-27 13:14:02 +0200262 if (config_.filter.conservative_initial_phase) {
263 usable_linear_estimate_ =
264 usable_linear_estimate_ && filter_has_had_time_to_converge_;
265 } else {
Per Åhgren22754392018-08-10 18:37:38 +0200266 usable_linear_estimate_ =
267 usable_linear_estimate_ &&
268 ((filter_has_had_time_to_converge_ && external_delay) ||
269 converged_filter_seen_);
Per Åhgren22754392018-08-10 18:37:38 +0200270 }
271
Per Åhgrenf4801a12018-09-27 13:14:02 +0200272 if (config_.filter.conservative_initial_phase) {
Per Åhgren22754392018-08-10 18:37:38 +0200273 usable_linear_estimate_ = usable_linear_estimate_ && external_delay;
274 }
275
Per Åhgrene3ca9912018-05-28 22:57:17 +0200276 if (!config_.echo_removal_control.linear_and_stable_echo_path) {
277 usable_linear_estimate_ =
278 usable_linear_estimate_ && recently_converged_filter;
Per Åhgrene3ca9912018-05-28 22:57:17 +0200279 }
Gustaf Ullberg9ed97922018-08-27 14:38:28 +0200280 usable_linear_estimate_ = usable_linear_estimate_ && !TransparentMode();
Per Åhgren5c532d32018-03-22 00:29:25 +0100281
282 use_linear_filter_output_ = usable_linear_estimate_ && !TransparentMode();
Per Åhgren6204adf2018-08-19 11:12:00 +0200283
Per Åhgrenef5d5af2018-07-31 00:03:46 +0200284 const bool stationary_block =
Per Åhgrenf4801a12018-09-27 13:14:02 +0200285 config_.echo_audibility.use_stationary_properties &&
286 echo_audibility_.IsBlockStationary();
Per Åhgrenef5d5af2018-07-31 00:03:46 +0200287
Jesús de Vicente Peña496cedf2018-07-04 11:02:09 +0200288 reverb_model_estimator_.Update(
Per Åhgrenef5d5af2018-07-31 00:03:46 +0200289 filter_analyzer_.GetAdjustedFilter(), adaptive_filter_frequency_response,
Jesús de Vicente Peña496cedf2018-07-04 11:02:09 +0200290 erle_estimator_.GetInstLinearQualityEstimate(), filter_delay_blocks_,
Per Åhgrenef5d5af2018-07-31 00:03:46 +0200291 usable_linear_estimate_, stationary_block);
Jesús de Vicente Peña075cb2b2018-06-13 15:13:55 +0200292
Jesús de Vicente Peña496cedf2018-07-04 11:02:09 +0200293 erle_estimator_.Dump(data_dumper_);
Per Åhgrenef5d5af2018-07-31 00:03:46 +0200294 reverb_model_estimator_.Dump(data_dumper_.get());
Per Åhgren5c532d32018-03-22 00:29:25 +0100295 data_dumper_->DumpRaw("aec3_erl", Erl());
Per Åhgren5c532d32018-03-22 00:29:25 +0100296 data_dumper_->DumpRaw("aec3_erl_time_domain", ErlTimeDomain());
297 data_dumper_->DumpRaw("aec3_usable_linear_estimate", UsableLinearEstimate());
298 data_dumper_->DumpRaw("aec3_transparent_mode", transparent_mode_);
299 data_dumper_->DumpRaw("aec3_state_internal_delay",
300 internal_delay_ ? *internal_delay_ : -1);
301 data_dumper_->DumpRaw("aec3_filter_delay", filter_analyzer_.DelayBlocks());
302
303 data_dumper_->DumpRaw("aec3_consistent_filter",
304 filter_analyzer_.Consistent());
305 data_dumper_->DumpRaw("aec3_suppression_gain_limit", SuppressionGainLimit());
Jesús de Vicente Peña02e9e442018-08-29 13:34:07 +0200306 data_dumper_->DumpRaw("aec3_initial_state", initial_state_);
Per Åhgren5c532d32018-03-22 00:29:25 +0100307 data_dumper_->DumpRaw("aec3_capture_saturation", SaturatedCapture());
308 data_dumper_->DumpRaw("aec3_echo_saturation", echo_saturation_);
309 data_dumper_->DumpRaw("aec3_converged_filter", converged_filter);
310 data_dumper_->DumpRaw("aec3_diverged_filter", diverged_filter);
311
312 data_dumper_->DumpRaw("aec3_external_delay_avaliable",
313 external_delay ? 1 : 0);
314 data_dumper_->DumpRaw("aec3_consistent_filter_estimate_not_seen",
315 consistent_filter_estimate_not_seen);
316 data_dumper_->DumpRaw("aec3_filter_should_have_converged",
317 filter_should_have_converged_);
318 data_dumper_->DumpRaw("aec3_filter_has_had_time_to_converge",
Per Åhgren22754392018-08-10 18:37:38 +0200319 filter_has_had_time_to_converge_);
Per Åhgren5c532d32018-03-22 00:29:25 +0100320 data_dumper_->DumpRaw("aec3_recently_converged_filter",
321 recently_converged_filter);
Jesús de Vicente Peñadd092872018-05-25 16:55:11 +0200322 data_dumper_->DumpRaw("aec3_suppresion_gain_limiter_running",
323 IsSuppressionGainLimitActive());
Per Åhgrenef5d5af2018-07-31 00:03:46 +0200324 data_dumper_->DumpRaw("aec3_filter_tail_freq_resp_est",
325 GetReverbFrequencyResponse());
peah29103572017-07-11 02:54:02 -0700326}
327
Per Åhgren4b3bc0f2017-12-20 15:26:13 +0100328bool AecState::DetectActiveRender(rtc::ArrayView<const float> x) const {
329 const float x_energy = std::inner_product(x.begin(), x.end(), x.begin(), 0.f);
330 return x_energy > (config_.render_levels.active_render_limit *
331 config_.render_levels.active_render_limit) *
332 kFftLengthBy2;
333}
334
Per Åhgren31122d62018-04-10 16:33:55 +0200335bool AecState::DetectEchoSaturation(rtc::ArrayView<const float> x,
336 float echo_path_gain) {
Per Åhgren4b3bc0f2017-12-20 15:26:13 +0100337 RTC_DCHECK_LT(0, x.size());
338 const float max_sample = fabs(*std::max_element(
339 x.begin(), x.end(), [](float a, float b) { return a * a < b * b; }));
Per Åhgren4b3bc0f2017-12-20 15:26:13 +0100340
341 // Set flag for potential presence of saturated echo
Per Åhgren31122d62018-04-10 16:33:55 +0200342 const float kMargin = 10.f;
343 float peak_echo_amplitude = max_sample * echo_path_gain * kMargin;
344 if (SaturatedCapture() && peak_echo_amplitude > 32000) {
345 blocks_since_last_saturation_ = 0;
346 } else {
347 ++blocks_since_last_saturation_;
348 }
Per Åhgren4b3bc0f2017-12-20 15:26:13 +0100349
Per Åhgren31122d62018-04-10 16:33:55 +0200350 return blocks_since_last_saturation_ < 5;
Per Åhgren4b3bc0f2017-12-20 15:26:13 +0100351}
352
peah522d71b2017-02-23 05:16:26 -0800353} // namespace webrtc