1 /*
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 
11 #include "modules/audio_processing/aec3/aec_state.h"
12 
13 #include <math.h>
14 
15 #include <algorithm>
16 #include <numeric>
17 #include <vector>
18 
19 #include "absl/types/optional.h"
20 #include "api/array_view.h"
21 #include "modules/audio_processing/aec3/aec3_common.h"
22 #include "modules/audio_processing/logging/apm_data_dumper.h"
23 #include "rtc_base/atomic_ops.h"
24 #include "rtc_base/checks.h"
25 #include "system_wrappers/include/field_trial.h"
26 
27 namespace webrtc {
28 namespace {
29 
30 constexpr size_t kBlocksSinceConvergencedFilterInit = 10000;
31 constexpr size_t kBlocksSinceConsistentEstimateInit = 10000;
32 
DeactivateTransparentMode()33 bool DeactivateTransparentMode() {
34   return field_trial::IsEnabled("WebRTC-Aec3TransparentModeKillSwitch");
35 }
36 
DeactivateInitialStateResetAtEchoPathChange()37 bool DeactivateInitialStateResetAtEchoPathChange() {
38   return field_trial::IsEnabled(
39       "WebRTC-Aec3DeactivateInitialStateResetKillSwitch");
40 }
41 
FullResetAtEchoPathChange()42 bool FullResetAtEchoPathChange() {
43   return !field_trial::IsEnabled("WebRTC-Aec3AecStateFullResetKillSwitch");
44 }
45 
SubtractorAnalyzerResetAtEchoPathChange()46 bool SubtractorAnalyzerResetAtEchoPathChange() {
47   return !field_trial::IsEnabled(
48       "WebRTC-Aec3AecStateSubtractorAnalyzerResetKillSwitch");
49 }
50 
ComputeAvgRenderReverb(const SpectrumBuffer & spectrum_buffer,int delay_blocks,float reverb_decay,ReverbModel * reverb_model,rtc::ArrayView<float,kFftLengthBy2Plus1> reverb_power_spectrum)51 void ComputeAvgRenderReverb(
52     const SpectrumBuffer& spectrum_buffer,
53     int delay_blocks,
54     float reverb_decay,
55     ReverbModel* reverb_model,
56     rtc::ArrayView<float, kFftLengthBy2Plus1> reverb_power_spectrum) {
57   RTC_DCHECK(reverb_model);
58   const size_t num_render_channels = spectrum_buffer.buffer[0].size();
59   int idx_at_delay =
60       spectrum_buffer.OffsetIndex(spectrum_buffer.read, delay_blocks);
61   int idx_past = spectrum_buffer.IncIndex(idx_at_delay);
62 
63   std::array<float, kFftLengthBy2Plus1> X2_data;
64   rtc::ArrayView<const float> X2;
65   if (num_render_channels > 1) {
66     auto average_channels =
67         [](size_t num_render_channels,
68            rtc::ArrayView<const std::array<float, kFftLengthBy2Plus1>>
69                spectrum_band_0,
70            rtc::ArrayView<float, kFftLengthBy2Plus1> render_power) {
71           std::fill(render_power.begin(), render_power.end(), 0.f);
72           for (size_t ch = 0; ch < num_render_channels; ++ch) {
73             for (size_t k = 0; k < kFftLengthBy2Plus1; ++k) {
74               render_power[k] += spectrum_band_0[ch][k];
75             }
76           }
77           const float normalizer = 1.f / num_render_channels;
78           for (size_t k = 0; k < kFftLengthBy2Plus1; ++k) {
79             render_power[k] *= normalizer;
80           }
81         };
82     average_channels(num_render_channels, spectrum_buffer.buffer[idx_past],
83                      X2_data);
84     reverb_model->UpdateReverbNoFreqShaping(
85         X2_data, /*power_spectrum_scaling=*/1.0f, reverb_decay);
86 
87     average_channels(num_render_channels, spectrum_buffer.buffer[idx_at_delay],
88                      X2_data);
89     X2 = X2_data;
90   } else {
91     reverb_model->UpdateReverbNoFreqShaping(
92         spectrum_buffer.buffer[idx_past][/*channel=*/0],
93         /*power_spectrum_scaling=*/1.0f, reverb_decay);
94 
95     X2 = spectrum_buffer.buffer[idx_at_delay][/*channel=*/0];
96   }
97 
98   rtc::ArrayView<const float, kFftLengthBy2Plus1> reverb_power =
99       reverb_model->reverb();
100   for (size_t k = 0; k < X2.size(); ++k) {
101     reverb_power_spectrum[k] = X2[k] + reverb_power[k];
102   }
103 }
104 
105 }  // namespace
106 
107 int AecState::instance_count_ = 0;
108 
GetResidualEchoScaling(rtc::ArrayView<float> residual_scaling) const109 void AecState::GetResidualEchoScaling(
110     rtc::ArrayView<float> residual_scaling) const {
111   bool filter_has_had_time_to_converge;
112   if (config_.filter.conservative_initial_phase) {
113     filter_has_had_time_to_converge =
114         strong_not_saturated_render_blocks_ >= 1.5f * kNumBlocksPerSecond;
115   } else {
116     filter_has_had_time_to_converge =
117         strong_not_saturated_render_blocks_ >= 0.8f * kNumBlocksPerSecond;
118   }
119   echo_audibility_.GetResidualEchoScaling(filter_has_had_time_to_converge,
120                                           residual_scaling);
121 }
122 
ErleUncertainty() const123 absl::optional<float> AecState::ErleUncertainty() const {
124   if (SaturatedEcho()) {
125     return 1.f;
126   }
127 
128   return absl::nullopt;
129 }
130 
AecState(const EchoCanceller3Config & config,size_t num_capture_channels)131 AecState::AecState(const EchoCanceller3Config& config,
132                    size_t num_capture_channels)
133     : data_dumper_(
134           new ApmDataDumper(rtc::AtomicOps::Increment(&instance_count_))),
135       config_(config),
136       num_capture_channels_(num_capture_channels),
137       transparent_mode_activated_(!DeactivateTransparentMode()),
138       deactivate_initial_state_reset_at_echo_path_change_(
139           DeactivateInitialStateResetAtEchoPathChange()),
140       full_reset_at_echo_path_change_(FullResetAtEchoPathChange()),
141       subtractor_analyzer_reset_at_echo_path_change_(
142           SubtractorAnalyzerResetAtEchoPathChange()),
143       initial_state_(config_),
144       delay_state_(config_, num_capture_channels_),
145       transparent_state_(config_),
146       filter_quality_state_(config_, num_capture_channels_),
147       erl_estimator_(2 * kNumBlocksPerSecond),
148       erle_estimator_(2 * kNumBlocksPerSecond, config_, num_capture_channels_),
149       filter_analyzer_(config_, num_capture_channels_),
150       echo_audibility_(
151           config_.echo_audibility.use_stationarity_properties_at_init),
152       reverb_model_estimator_(config_, num_capture_channels_),
153       subtractor_output_analyzer_(num_capture_channels_) {}
154 
155 AecState::~AecState() = default;
156 
HandleEchoPathChange(const EchoPathVariability & echo_path_variability)157 void AecState::HandleEchoPathChange(
158     const EchoPathVariability& echo_path_variability) {
159   const auto full_reset = [&]() {
160     filter_analyzer_.Reset();
161     capture_signal_saturation_ = false;
162     strong_not_saturated_render_blocks_ = 0;
163     blocks_with_active_render_ = 0;
164     if (!deactivate_initial_state_reset_at_echo_path_change_) {
165       initial_state_.Reset();
166     }
167     transparent_state_.Reset();
168     erle_estimator_.Reset(true);
169     erl_estimator_.Reset();
170     filter_quality_state_.Reset();
171   };
172 
173   // TODO(peah): Refine the reset scheme according to the type of gain and
174   // delay adjustment.
175 
176   if (full_reset_at_echo_path_change_ &&
177       echo_path_variability.delay_change !=
178           EchoPathVariability::DelayAdjustment::kNone) {
179     full_reset();
180   } else if (echo_path_variability.gain_change) {
181     erle_estimator_.Reset(false);
182   }
183   if (subtractor_analyzer_reset_at_echo_path_change_) {
184     subtractor_output_analyzer_.HandleEchoPathChange();
185   }
186 }
187 
Update(const absl::optional<DelayEstimate> & external_delay,rtc::ArrayView<const std::vector<std::array<float,kFftLengthBy2Plus1>>> adaptive_filter_frequency_responses,rtc::ArrayView<const std::vector<float>> adaptive_filter_impulse_responses,const RenderBuffer & render_buffer,rtc::ArrayView<const std::array<float,kFftLengthBy2Plus1>> E2_refined,rtc::ArrayView<const std::array<float,kFftLengthBy2Plus1>> Y2,rtc::ArrayView<const SubtractorOutput> subtractor_output)188 void AecState::Update(
189     const absl::optional<DelayEstimate>& external_delay,
190     rtc::ArrayView<const std::vector<std::array<float, kFftLengthBy2Plus1>>>
191         adaptive_filter_frequency_responses,
192     rtc::ArrayView<const std::vector<float>> adaptive_filter_impulse_responses,
193     const RenderBuffer& render_buffer,
194     rtc::ArrayView<const std::array<float, kFftLengthBy2Plus1>> E2_refined,
195     rtc::ArrayView<const std::array<float, kFftLengthBy2Plus1>> Y2,
196     rtc::ArrayView<const SubtractorOutput> subtractor_output) {
197   RTC_DCHECK_EQ(num_capture_channels_, Y2.size());
198   RTC_DCHECK_EQ(num_capture_channels_, subtractor_output.size());
199   RTC_DCHECK_EQ(num_capture_channels_,
200                 adaptive_filter_frequency_responses.size());
201   RTC_DCHECK_EQ(num_capture_channels_,
202                 adaptive_filter_impulse_responses.size());
203 
204   // Analyze the filter outputs and filters.
205   bool any_filter_converged;
206   bool all_filters_diverged;
207   subtractor_output_analyzer_.Update(subtractor_output, &any_filter_converged,
208                                      &all_filters_diverged);
209 
210   bool any_filter_consistent;
211   float max_echo_path_gain;
212   filter_analyzer_.Update(adaptive_filter_impulse_responses, render_buffer,
213                           &any_filter_consistent, &max_echo_path_gain);
214 
215   // Estimate the direct path delay of the filter.
216   if (config_.filter.use_linear_filter) {
217     delay_state_.Update(filter_analyzer_.FilterDelaysBlocks(), external_delay,
218                         strong_not_saturated_render_blocks_);
219   }
220 
221   const std::vector<std::vector<float>>& aligned_render_block =
222       render_buffer.Block(-delay_state_.MinDirectPathFilterDelay())[0];
223 
224   // Update render counters.
225   bool active_render = false;
226   for (size_t ch = 0; ch < aligned_render_block.size(); ++ch) {
227     const float render_energy = std::inner_product(
228         aligned_render_block[ch].begin(), aligned_render_block[ch].end(),
229         aligned_render_block[ch].begin(), 0.f);
230     if (render_energy > (config_.render_levels.active_render_limit *
231                          config_.render_levels.active_render_limit) *
232                             kFftLengthBy2) {
233       active_render = true;
234       break;
235     }
236   }
237   blocks_with_active_render_ += active_render ? 1 : 0;
238   strong_not_saturated_render_blocks_ +=
239       active_render && !SaturatedCapture() ? 1 : 0;
240 
241   std::array<float, kFftLengthBy2Plus1> avg_render_spectrum_with_reverb;
242 
243   ComputeAvgRenderReverb(render_buffer.GetSpectrumBuffer(),
244                          delay_state_.MinDirectPathFilterDelay(), ReverbDecay(),
245                          &avg_render_reverb_, avg_render_spectrum_with_reverb);
246 
247   if (config_.echo_audibility.use_stationarity_properties) {
248     // Update the echo audibility evaluator.
249     echo_audibility_.Update(render_buffer, avg_render_reverb_.reverb(),
250                             delay_state_.MinDirectPathFilterDelay(),
251                             delay_state_.ExternalDelayReported());
252   }
253 
254   // Update the ERL and ERLE measures.
255   if (initial_state_.TransitionTriggered()) {
256     erle_estimator_.Reset(false);
257   }
258 
259   erle_estimator_.Update(render_buffer, adaptive_filter_frequency_responses,
260                          avg_render_spectrum_with_reverb, Y2, E2_refined,
261                          subtractor_output_analyzer_.ConvergedFilters());
262 
263   erl_estimator_.Update(
264       subtractor_output_analyzer_.ConvergedFilters(),
265       render_buffer.Spectrum(delay_state_.MinDirectPathFilterDelay()), Y2);
266 
267   // Detect and flag echo saturation.
268   if (config_.ep_strength.echo_can_saturate) {
269     saturation_detector_.Update(aligned_render_block, SaturatedCapture(),
270                                 UsableLinearEstimate(), subtractor_output,
271                                 max_echo_path_gain);
272   } else {
273     RTC_DCHECK(!saturation_detector_.SaturatedEcho());
274   }
275 
276   // Update the decision on whether to use the initial state parameter set.
277   initial_state_.Update(active_render, SaturatedCapture());
278 
279   // Detect whether the transparent mode should be activated.
280   transparent_state_.Update(delay_state_.MinDirectPathFilterDelay(),
281                             any_filter_consistent, any_filter_converged,
282                             all_filters_diverged, active_render,
283                             SaturatedCapture());
284 
285   // Analyze the quality of the filter.
286   filter_quality_state_.Update(active_render, TransparentMode(),
287                                SaturatedCapture(), external_delay,
288                                any_filter_converged);
289 
290   // Update the reverb estimate.
291   const bool stationary_block =
292       config_.echo_audibility.use_stationarity_properties &&
293       echo_audibility_.IsBlockStationary();
294 
295   reverb_model_estimator_.Update(
296       filter_analyzer_.GetAdjustedFilters(),
297       adaptive_filter_frequency_responses,
298       erle_estimator_.GetInstLinearQualityEstimates(),
299       delay_state_.DirectPathFilterDelays(),
300       filter_quality_state_.UsableLinearFilterOutputs(), stationary_block);
301 
302   erle_estimator_.Dump(data_dumper_);
303   reverb_model_estimator_.Dump(data_dumper_.get());
304   data_dumper_->DumpRaw("aec3_erl", Erl());
305   data_dumper_->DumpRaw("aec3_erl_time_domain", ErlTimeDomain());
306   data_dumper_->DumpRaw("aec3_erle", Erle()[0]);
307   data_dumper_->DumpRaw("aec3_usable_linear_estimate", UsableLinearEstimate());
308   data_dumper_->DumpRaw("aec3_transparent_mode", TransparentMode());
309   data_dumper_->DumpRaw("aec3_filter_delay",
310                         filter_analyzer_.MinFilterDelayBlocks());
311 
312   data_dumper_->DumpRaw("aec3_any_filter_consistent", any_filter_consistent);
313   data_dumper_->DumpRaw("aec3_initial_state",
314                         initial_state_.InitialStateActive());
315   data_dumper_->DumpRaw("aec3_capture_saturation", SaturatedCapture());
316   data_dumper_->DumpRaw("aec3_echo_saturation", SaturatedEcho());
317   data_dumper_->DumpRaw("aec3_any_filter_converged", any_filter_converged);
318   data_dumper_->DumpRaw("aec3_all_filters_diverged", all_filters_diverged);
319 
320   data_dumper_->DumpRaw("aec3_external_delay_avaliable",
321                         external_delay ? 1 : 0);
322   data_dumper_->DumpRaw("aec3_filter_tail_freq_resp_est",
323                         GetReverbFrequencyResponse());
324 }
325 
InitialState(const EchoCanceller3Config & config)326 AecState::InitialState::InitialState(const EchoCanceller3Config& config)
327     : conservative_initial_phase_(config.filter.conservative_initial_phase),
328       initial_state_seconds_(config.filter.initial_state_seconds) {
329   Reset();
330 }
Reset()331 void AecState::InitialState::InitialState::Reset() {
332   initial_state_ = true;
333   strong_not_saturated_render_blocks_ = 0;
334 }
Update(bool active_render,bool saturated_capture)335 void AecState::InitialState::InitialState::Update(bool active_render,
336                                                   bool saturated_capture) {
337   strong_not_saturated_render_blocks_ +=
338       active_render && !saturated_capture ? 1 : 0;
339 
340   // Flag whether the initial state is still active.
341   bool prev_initial_state = initial_state_;
342   if (conservative_initial_phase_) {
343     initial_state_ =
344         strong_not_saturated_render_blocks_ < 5 * kNumBlocksPerSecond;
345   } else {
346     initial_state_ = strong_not_saturated_render_blocks_ <
347                      initial_state_seconds_ * kNumBlocksPerSecond;
348   }
349 
350   // Flag whether the transition from the initial state has started.
351   transition_triggered_ = !initial_state_ && prev_initial_state;
352 }
353 
FilterDelay(const EchoCanceller3Config & config,size_t num_capture_channels)354 AecState::FilterDelay::FilterDelay(const EchoCanceller3Config& config,
355                                    size_t num_capture_channels)
356     : delay_headroom_samples_(config.delay.delay_headroom_samples),
357       filter_delays_blocks_(num_capture_channels, 0) {}
358 
Update(rtc::ArrayView<const int> analyzer_filter_delay_estimates_blocks,const absl::optional<DelayEstimate> & external_delay,size_t blocks_with_proper_filter_adaptation)359 void AecState::FilterDelay::Update(
360     rtc::ArrayView<const int> analyzer_filter_delay_estimates_blocks,
361     const absl::optional<DelayEstimate>& external_delay,
362     size_t blocks_with_proper_filter_adaptation) {
363   // Update the delay based on the external delay.
364   if (external_delay &&
365       (!external_delay_ || external_delay_->delay != external_delay->delay)) {
366     external_delay_ = external_delay;
367     external_delay_reported_ = true;
368   }
369 
370   // Override the estimated delay if it is not certain that the filter has had
371   // time to converge.
372   const bool delay_estimator_may_not_have_converged =
373       blocks_with_proper_filter_adaptation < 2 * kNumBlocksPerSecond;
374   if (delay_estimator_may_not_have_converged && external_delay_) {
375     int delay_guess = delay_headroom_samples_ / kBlockSize;
376     std::fill(filter_delays_blocks_.begin(), filter_delays_blocks_.end(),
377               delay_guess);
378   } else {
379     RTC_DCHECK_EQ(filter_delays_blocks_.size(),
380                   analyzer_filter_delay_estimates_blocks.size());
381     std::copy(analyzer_filter_delay_estimates_blocks.begin(),
382               analyzer_filter_delay_estimates_blocks.end(),
383               filter_delays_blocks_.begin());
384   }
385 
386   min_filter_delay_ = *std::min_element(filter_delays_blocks_.begin(),
387                                         filter_delays_blocks_.end());
388 }
389 
TransparentMode(const EchoCanceller3Config & config)390 AecState::TransparentMode::TransparentMode(const EchoCanceller3Config& config)
391     : bounded_erl_(config.ep_strength.bounded_erl),
392       linear_and_stable_echo_path_(
393           config.echo_removal_control.linear_and_stable_echo_path),
394       active_blocks_since_sane_filter_(kBlocksSinceConsistentEstimateInit),
395       non_converged_sequence_size_(kBlocksSinceConvergencedFilterInit) {}
396 
Reset()397 void AecState::TransparentMode::Reset() {
398   non_converged_sequence_size_ = kBlocksSinceConvergencedFilterInit;
399   diverged_sequence_size_ = 0;
400   strong_not_saturated_render_blocks_ = 0;
401   if (linear_and_stable_echo_path_) {
402     recent_convergence_during_activity_ = false;
403   }
404 }
405 
Update(int filter_delay_blocks,bool any_filter_consistent,bool any_filter_converged,bool all_filters_diverged,bool active_render,bool saturated_capture)406 void AecState::TransparentMode::Update(int filter_delay_blocks,
407                                        bool any_filter_consistent,
408                                        bool any_filter_converged,
409                                        bool all_filters_diverged,
410                                        bool active_render,
411                                        bool saturated_capture) {
412   ++capture_block_counter_;
413   strong_not_saturated_render_blocks_ +=
414       active_render && !saturated_capture ? 1 : 0;
415 
416   if (any_filter_consistent && filter_delay_blocks < 5) {
417     sane_filter_observed_ = true;
418     active_blocks_since_sane_filter_ = 0;
419   } else if (active_render) {
420     ++active_blocks_since_sane_filter_;
421   }
422 
423   bool sane_filter_recently_seen;
424   if (!sane_filter_observed_) {
425     sane_filter_recently_seen =
426         capture_block_counter_ <= 5 * kNumBlocksPerSecond;
427   } else {
428     sane_filter_recently_seen =
429         active_blocks_since_sane_filter_ <= 30 * kNumBlocksPerSecond;
430   }
431 
432   if (any_filter_converged) {
433     recent_convergence_during_activity_ = true;
434     active_non_converged_sequence_size_ = 0;
435     non_converged_sequence_size_ = 0;
436     ++num_converged_blocks_;
437   } else {
438     if (++non_converged_sequence_size_ > 20 * kNumBlocksPerSecond) {
439       num_converged_blocks_ = 0;
440     }
441 
442     if (active_render &&
443         ++active_non_converged_sequence_size_ > 60 * kNumBlocksPerSecond) {
444       recent_convergence_during_activity_ = false;
445     }
446   }
447 
448   if (!all_filters_diverged) {
449     diverged_sequence_size_ = 0;
450   } else if (++diverged_sequence_size_ >= 60) {
451     // TODO(peah): Change these lines to ensure proper triggering of usable
452     // filter.
453     non_converged_sequence_size_ = kBlocksSinceConvergencedFilterInit;
454   }
455 
456   if (active_non_converged_sequence_size_ > 60 * kNumBlocksPerSecond) {
457     finite_erl_recently_detected_ = false;
458   }
459   if (num_converged_blocks_ > 50) {
460     finite_erl_recently_detected_ = true;
461   }
462 
463   if (bounded_erl_) {
464     transparency_activated_ = false;
465   } else if (finite_erl_recently_detected_) {
466     transparency_activated_ = false;
467   } else if (sane_filter_recently_seen && recent_convergence_during_activity_) {
468     transparency_activated_ = false;
469   } else {
470     const bool filter_should_have_converged =
471         strong_not_saturated_render_blocks_ > 6 * kNumBlocksPerSecond;
472     transparency_activated_ = filter_should_have_converged;
473   }
474 }
475 
FilteringQualityAnalyzer(const EchoCanceller3Config & config,size_t num_capture_channels)476 AecState::FilteringQualityAnalyzer::FilteringQualityAnalyzer(
477     const EchoCanceller3Config& config,
478     size_t num_capture_channels)
479     : use_linear_filter_(config.filter.use_linear_filter),
480       usable_linear_filter_estimates_(num_capture_channels, false) {}
481 
Reset()482 void AecState::FilteringQualityAnalyzer::Reset() {
483   std::fill(usable_linear_filter_estimates_.begin(),
484             usable_linear_filter_estimates_.end(), false);
485   overall_usable_linear_estimates_ = false;
486   filter_update_blocks_since_reset_ = 0;
487 }
488 
Update(bool active_render,bool transparent_mode,bool saturated_capture,const absl::optional<DelayEstimate> & external_delay,bool any_filter_converged)489 void AecState::FilteringQualityAnalyzer::Update(
490     bool active_render,
491     bool transparent_mode,
492     bool saturated_capture,
493     const absl::optional<DelayEstimate>& external_delay,
494     bool any_filter_converged) {
495   // Update blocks counter.
496   const bool filter_update = active_render && !saturated_capture;
497   filter_update_blocks_since_reset_ += filter_update ? 1 : 0;
498   filter_update_blocks_since_start_ += filter_update ? 1 : 0;
499 
500   // Store convergence flag when observed.
501   convergence_seen_ = convergence_seen_ || any_filter_converged;
502 
503   // Verify requirements for achieving a decent filter. The requirements for
504   // filter adaptation at call startup are more restrictive than after an
505   // in-call reset.
506   const bool sufficient_data_to_converge_at_startup =
507       filter_update_blocks_since_start_ > kNumBlocksPerSecond * 0.4f;
508   const bool sufficient_data_to_converge_at_reset =
509       sufficient_data_to_converge_at_startup &&
510       filter_update_blocks_since_reset_ > kNumBlocksPerSecond * 0.2f;
511 
512   // The linear filter can only be used if it has had time to converge.
513   overall_usable_linear_estimates_ = sufficient_data_to_converge_at_startup &&
514                                      sufficient_data_to_converge_at_reset;
515 
516   // The linear filter can only be used if an external delay or convergence have
517   // been identified
518   overall_usable_linear_estimates_ =
519       overall_usable_linear_estimates_ && (external_delay || convergence_seen_);
520 
521   // If transparent mode is on, deactivate usign the linear filter.
522   overall_usable_linear_estimates_ =
523       overall_usable_linear_estimates_ && !transparent_mode;
524 
525   if (use_linear_filter_) {
526     std::fill(usable_linear_filter_estimates_.begin(),
527               usable_linear_filter_estimates_.end(),
528               overall_usable_linear_estimates_);
529   }
530 }
531 
Update(rtc::ArrayView<const std::vector<float>> x,bool saturated_capture,bool usable_linear_estimate,rtc::ArrayView<const SubtractorOutput> subtractor_output,float echo_path_gain)532 void AecState::SaturationDetector::Update(
533     rtc::ArrayView<const std::vector<float>> x,
534     bool saturated_capture,
535     bool usable_linear_estimate,
536     rtc::ArrayView<const SubtractorOutput> subtractor_output,
537     float echo_path_gain) {
538   saturated_echo_ = false;
539   if (!saturated_capture) {
540     return;
541   }
542 
543   if (usable_linear_estimate) {
544     constexpr float kSaturationThreshold = 20000.f;
545     for (size_t ch = 0; ch < subtractor_output.size(); ++ch) {
546       saturated_echo_ =
547           saturated_echo_ ||
548           (subtractor_output[ch].s_refined_max_abs > kSaturationThreshold ||
549            subtractor_output[ch].s_coarse_max_abs > kSaturationThreshold);
550     }
551   } else {
552     float max_sample = 0.f;
553     for (auto& channel : x) {
554       for (float sample : channel) {
555         max_sample = std::max(max_sample, fabsf(sample));
556       }
557     }
558 
559     const float kMargin = 10.f;
560     float peak_echo_amplitude = max_sample * echo_path_gain * kMargin;
561     saturated_echo_ = saturated_echo_ || peak_echo_amplitude > 32000;
562   }
563 }
564 
565 }  // namespace webrtc
566