xref: /aosp_15_r20/external/webrtc/modules/audio_processing/aec3/echo_remover.cc (revision d9f758449e529ab9291ac668be2861e7a55c2422)
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 #include "modules/audio_processing/aec3/echo_remover.h"
11 
12 #include <math.h>
13 #include <stddef.h>
14 
15 #include <algorithm>
16 #include <array>
17 #include <atomic>
18 #include <cmath>
19 #include <memory>
20 
21 #include "api/array_view.h"
22 #include "modules/audio_processing/aec3/aec3_common.h"
23 #include "modules/audio_processing/aec3/aec3_fft.h"
24 #include "modules/audio_processing/aec3/aec_state.h"
25 #include "modules/audio_processing/aec3/comfort_noise_generator.h"
26 #include "modules/audio_processing/aec3/echo_path_variability.h"
27 #include "modules/audio_processing/aec3/echo_remover_metrics.h"
28 #include "modules/audio_processing/aec3/fft_data.h"
29 #include "modules/audio_processing/aec3/render_buffer.h"
30 #include "modules/audio_processing/aec3/render_signal_analyzer.h"
31 #include "modules/audio_processing/aec3/residual_echo_estimator.h"
32 #include "modules/audio_processing/aec3/subtractor.h"
33 #include "modules/audio_processing/aec3/subtractor_output.h"
34 #include "modules/audio_processing/aec3/suppression_filter.h"
35 #include "modules/audio_processing/aec3/suppression_gain.h"
36 #include "modules/audio_processing/logging/apm_data_dumper.h"
37 #include "rtc_base/checks.h"
38 #include "rtc_base/logging.h"
39 
40 namespace webrtc {
41 
42 namespace {
43 
44 // Maximum number of channels for which the capture channel data is stored on
45 // the stack. If the number of channels are larger than this, they are stored
46 // using scratch memory that is pre-allocated on the heap. The reason for this
47 // partitioning is not to waste heap space for handling the more common numbers
48 // of channels, while at the same time not limiting the support for higher
49 // numbers of channels by enforcing the capture channel data to be stored on the
50 // stack using a fixed maximum value.
51 constexpr size_t kMaxNumChannelsOnStack = 2;
52 
53 // Chooses the number of channels to store on the heap when that is required due
54 // to the number of capture channels being larger than the pre-defined number
55 // of channels to store on the stack.
NumChannelsOnHeap(size_t num_capture_channels)56 size_t NumChannelsOnHeap(size_t num_capture_channels) {
57   return num_capture_channels > kMaxNumChannelsOnStack ? num_capture_channels
58                                                        : 0;
59 }
60 
LinearEchoPower(const FftData & E,const FftData & Y,std::array<float,kFftLengthBy2Plus1> * S2)61 void LinearEchoPower(const FftData& E,
62                      const FftData& Y,
63                      std::array<float, kFftLengthBy2Plus1>* S2) {
64   for (size_t k = 0; k < E.re.size(); ++k) {
65     (*S2)[k] = (Y.re[k] - E.re[k]) * (Y.re[k] - E.re[k]) +
66                (Y.im[k] - E.im[k]) * (Y.im[k] - E.im[k]);
67   }
68 }
69 
70 // Fades between two input signals using a fix-sized transition.
SignalTransition(rtc::ArrayView<const float> from,rtc::ArrayView<const float> to,rtc::ArrayView<float> out)71 void SignalTransition(rtc::ArrayView<const float> from,
72                       rtc::ArrayView<const float> to,
73                       rtc::ArrayView<float> out) {
74   if (from == to) {
75     RTC_DCHECK_EQ(to.size(), out.size());
76     std::copy(to.begin(), to.end(), out.begin());
77   } else {
78     constexpr size_t kTransitionSize = 30;
79     constexpr float kOneByTransitionSizePlusOne = 1.f / (kTransitionSize + 1);
80 
81     RTC_DCHECK_EQ(from.size(), to.size());
82     RTC_DCHECK_EQ(from.size(), out.size());
83     RTC_DCHECK_LE(kTransitionSize, out.size());
84 
85     for (size_t k = 0; k < kTransitionSize; ++k) {
86       float a = (k + 1) * kOneByTransitionSizePlusOne;
87       out[k] = a * to[k] + (1.f - a) * from[k];
88     }
89 
90     std::copy(to.begin() + kTransitionSize, to.end(),
91               out.begin() + kTransitionSize);
92   }
93 }
94 
95 // Computes a windowed (square root Hanning) padded FFT and updates the related
96 // memory.
WindowedPaddedFft(const Aec3Fft & fft,rtc::ArrayView<const float> v,rtc::ArrayView<float> v_old,FftData * V)97 void WindowedPaddedFft(const Aec3Fft& fft,
98                        rtc::ArrayView<const float> v,
99                        rtc::ArrayView<float> v_old,
100                        FftData* V) {
101   fft.PaddedFft(v, v_old, Aec3Fft::Window::kSqrtHanning, V);
102   std::copy(v.begin(), v.end(), v_old.begin());
103 }
104 
105 // Class for removing the echo from the capture signal.
106 class EchoRemoverImpl final : public EchoRemover {
107  public:
108   EchoRemoverImpl(const EchoCanceller3Config& config,
109                   int sample_rate_hz,
110                   size_t num_render_channels,
111                   size_t num_capture_channels);
112   ~EchoRemoverImpl() override;
113   EchoRemoverImpl(const EchoRemoverImpl&) = delete;
114   EchoRemoverImpl& operator=(const EchoRemoverImpl&) = delete;
115 
116   void GetMetrics(EchoControl::Metrics* metrics) const override;
117 
118   // Removes the echo from a block of samples from the capture signal. The
119   // supplied render signal is assumed to be pre-aligned with the capture
120   // signal.
121   void ProcessCapture(EchoPathVariability echo_path_variability,
122                       bool capture_signal_saturation,
123                       const absl::optional<DelayEstimate>& external_delay,
124                       RenderBuffer* render_buffer,
125                       Block* linear_output,
126                       Block* capture) override;
127 
128   // Updates the status on whether echo leakage is detected in the output of the
129   // echo remover.
UpdateEchoLeakageStatus(bool leakage_detected)130   void UpdateEchoLeakageStatus(bool leakage_detected) override {
131     echo_leakage_detected_ = leakage_detected;
132   }
133 
SetCaptureOutputUsage(bool capture_output_used)134   void SetCaptureOutputUsage(bool capture_output_used) override {
135     capture_output_used_ = capture_output_used;
136   }
137 
138  private:
139   // Selects which of the coarse and refined linear filter outputs that is most
140   // appropriate to pass to the suppressor and forms the linear filter output by
141   // smoothly transition between those.
142   void FormLinearFilterOutput(const SubtractorOutput& subtractor_output,
143                               rtc::ArrayView<float> output);
144 
145   static std::atomic<int> instance_count_;
146   const EchoCanceller3Config config_;
147   const Aec3Fft fft_;
148   std::unique_ptr<ApmDataDumper> data_dumper_;
149   const Aec3Optimization optimization_;
150   const int sample_rate_hz_;
151   const size_t num_render_channels_;
152   const size_t num_capture_channels_;
153   const bool use_coarse_filter_output_;
154   Subtractor subtractor_;
155   SuppressionGain suppression_gain_;
156   ComfortNoiseGenerator cng_;
157   SuppressionFilter suppression_filter_;
158   RenderSignalAnalyzer render_signal_analyzer_;
159   ResidualEchoEstimator residual_echo_estimator_;
160   bool echo_leakage_detected_ = false;
161   bool capture_output_used_ = true;
162   AecState aec_state_;
163   EchoRemoverMetrics metrics_;
164   std::vector<std::array<float, kFftLengthBy2>> e_old_;
165   std::vector<std::array<float, kFftLengthBy2>> y_old_;
166   size_t block_counter_ = 0;
167   int gain_change_hangover_ = 0;
168   bool refined_filter_output_last_selected_ = true;
169 
170   std::vector<std::array<float, kFftLengthBy2>> e_heap_;
171   std::vector<std::array<float, kFftLengthBy2Plus1>> Y2_heap_;
172   std::vector<std::array<float, kFftLengthBy2Plus1>> E2_heap_;
173   std::vector<std::array<float, kFftLengthBy2Plus1>> R2_heap_;
174   std::vector<std::array<float, kFftLengthBy2Plus1>> R2_unbounded_heap_;
175   std::vector<std::array<float, kFftLengthBy2Plus1>> S2_linear_heap_;
176   std::vector<FftData> Y_heap_;
177   std::vector<FftData> E_heap_;
178   std::vector<FftData> comfort_noise_heap_;
179   std::vector<FftData> high_band_comfort_noise_heap_;
180   std::vector<SubtractorOutput> subtractor_output_heap_;
181 };
182 
183 std::atomic<int> EchoRemoverImpl::instance_count_(0);
184 
EchoRemoverImpl(const EchoCanceller3Config & config,int sample_rate_hz,size_t num_render_channels,size_t num_capture_channels)185 EchoRemoverImpl::EchoRemoverImpl(const EchoCanceller3Config& config,
186                                  int sample_rate_hz,
187                                  size_t num_render_channels,
188                                  size_t num_capture_channels)
189     : config_(config),
190       fft_(),
191       data_dumper_(new ApmDataDumper(instance_count_.fetch_add(1) + 1)),
192       optimization_(DetectOptimization()),
193       sample_rate_hz_(sample_rate_hz),
194       num_render_channels_(num_render_channels),
195       num_capture_channels_(num_capture_channels),
196       use_coarse_filter_output_(
197           config_.filter.enable_coarse_filter_output_usage),
198       subtractor_(config,
199                   num_render_channels_,
200                   num_capture_channels_,
201                   data_dumper_.get(),
202                   optimization_),
203       suppression_gain_(config_,
204                         optimization_,
205                         sample_rate_hz,
206                         num_capture_channels),
207       cng_(config_, optimization_, num_capture_channels_),
208       suppression_filter_(optimization_,
209                           sample_rate_hz_,
210                           num_capture_channels_),
211       render_signal_analyzer_(config_),
212       residual_echo_estimator_(config_, num_render_channels),
213       aec_state_(config_, num_capture_channels_),
214       e_old_(num_capture_channels_, {0.f}),
215       y_old_(num_capture_channels_, {0.f}),
216       e_heap_(NumChannelsOnHeap(num_capture_channels_), {0.f}),
217       Y2_heap_(NumChannelsOnHeap(num_capture_channels_)),
218       E2_heap_(NumChannelsOnHeap(num_capture_channels_)),
219       R2_heap_(NumChannelsOnHeap(num_capture_channels_)),
220       R2_unbounded_heap_(NumChannelsOnHeap(num_capture_channels_)),
221       S2_linear_heap_(NumChannelsOnHeap(num_capture_channels_)),
222       Y_heap_(NumChannelsOnHeap(num_capture_channels_)),
223       E_heap_(NumChannelsOnHeap(num_capture_channels_)),
224       comfort_noise_heap_(NumChannelsOnHeap(num_capture_channels_)),
225       high_band_comfort_noise_heap_(NumChannelsOnHeap(num_capture_channels_)),
226       subtractor_output_heap_(NumChannelsOnHeap(num_capture_channels_)) {
227   RTC_DCHECK(ValidFullBandRate(sample_rate_hz));
228 }
229 
230 EchoRemoverImpl::~EchoRemoverImpl() = default;
231 
GetMetrics(EchoControl::Metrics * metrics) const232 void EchoRemoverImpl::GetMetrics(EchoControl::Metrics* metrics) const {
233   // Echo return loss (ERL) is inverted to go from gain to attenuation.
234   metrics->echo_return_loss = -10.0 * std::log10(aec_state_.ErlTimeDomain());
235   metrics->echo_return_loss_enhancement =
236       Log2TodB(aec_state_.FullBandErleLog2());
237 }
238 
ProcessCapture(EchoPathVariability echo_path_variability,bool capture_signal_saturation,const absl::optional<DelayEstimate> & external_delay,RenderBuffer * render_buffer,Block * linear_output,Block * capture)239 void EchoRemoverImpl::ProcessCapture(
240     EchoPathVariability echo_path_variability,
241     bool capture_signal_saturation,
242     const absl::optional<DelayEstimate>& external_delay,
243     RenderBuffer* render_buffer,
244     Block* linear_output,
245     Block* capture) {
246   ++block_counter_;
247   const Block& x = render_buffer->GetBlock(0);
248   Block* y = capture;
249   RTC_DCHECK(render_buffer);
250   RTC_DCHECK(y);
251   RTC_DCHECK_EQ(x.NumBands(), NumBandsForRate(sample_rate_hz_));
252   RTC_DCHECK_EQ(y->NumBands(), NumBandsForRate(sample_rate_hz_));
253   RTC_DCHECK_EQ(x.NumChannels(), num_render_channels_);
254   RTC_DCHECK_EQ(y->NumChannels(), num_capture_channels_);
255 
256   // Stack allocated data to use when the number of channels is low.
257   std::array<std::array<float, kFftLengthBy2>, kMaxNumChannelsOnStack> e_stack;
258   std::array<std::array<float, kFftLengthBy2Plus1>, kMaxNumChannelsOnStack>
259       Y2_stack;
260   std::array<std::array<float, kFftLengthBy2Plus1>, kMaxNumChannelsOnStack>
261       E2_stack;
262   std::array<std::array<float, kFftLengthBy2Plus1>, kMaxNumChannelsOnStack>
263       R2_stack;
264   std::array<std::array<float, kFftLengthBy2Plus1>, kMaxNumChannelsOnStack>
265       R2_unbounded_stack;
266   std::array<std::array<float, kFftLengthBy2Plus1>, kMaxNumChannelsOnStack>
267       S2_linear_stack;
268   std::array<FftData, kMaxNumChannelsOnStack> Y_stack;
269   std::array<FftData, kMaxNumChannelsOnStack> E_stack;
270   std::array<FftData, kMaxNumChannelsOnStack> comfort_noise_stack;
271   std::array<FftData, kMaxNumChannelsOnStack> high_band_comfort_noise_stack;
272   std::array<SubtractorOutput, kMaxNumChannelsOnStack> subtractor_output_stack;
273 
274   rtc::ArrayView<std::array<float, kFftLengthBy2>> e(e_stack.data(),
275                                                      num_capture_channels_);
276   rtc::ArrayView<std::array<float, kFftLengthBy2Plus1>> Y2(
277       Y2_stack.data(), num_capture_channels_);
278   rtc::ArrayView<std::array<float, kFftLengthBy2Plus1>> E2(
279       E2_stack.data(), num_capture_channels_);
280   rtc::ArrayView<std::array<float, kFftLengthBy2Plus1>> R2(
281       R2_stack.data(), num_capture_channels_);
282   rtc::ArrayView<std::array<float, kFftLengthBy2Plus1>> R2_unbounded(
283       R2_unbounded_stack.data(), num_capture_channels_);
284   rtc::ArrayView<std::array<float, kFftLengthBy2Plus1>> S2_linear(
285       S2_linear_stack.data(), num_capture_channels_);
286   rtc::ArrayView<FftData> Y(Y_stack.data(), num_capture_channels_);
287   rtc::ArrayView<FftData> E(E_stack.data(), num_capture_channels_);
288   rtc::ArrayView<FftData> comfort_noise(comfort_noise_stack.data(),
289                                         num_capture_channels_);
290   rtc::ArrayView<FftData> high_band_comfort_noise(
291       high_band_comfort_noise_stack.data(), num_capture_channels_);
292   rtc::ArrayView<SubtractorOutput> subtractor_output(
293       subtractor_output_stack.data(), num_capture_channels_);
294   if (NumChannelsOnHeap(num_capture_channels_) > 0) {
295     // If the stack-allocated space is too small, use the heap for storing the
296     // microphone data.
297     e = rtc::ArrayView<std::array<float, kFftLengthBy2>>(e_heap_.data(),
298                                                          num_capture_channels_);
299     Y2 = rtc::ArrayView<std::array<float, kFftLengthBy2Plus1>>(
300         Y2_heap_.data(), num_capture_channels_);
301     E2 = rtc::ArrayView<std::array<float, kFftLengthBy2Plus1>>(
302         E2_heap_.data(), num_capture_channels_);
303     R2 = rtc::ArrayView<std::array<float, kFftLengthBy2Plus1>>(
304         R2_heap_.data(), num_capture_channels_);
305     R2_unbounded = rtc::ArrayView<std::array<float, kFftLengthBy2Plus1>>(
306         R2_unbounded_heap_.data(), num_capture_channels_);
307     S2_linear = rtc::ArrayView<std::array<float, kFftLengthBy2Plus1>>(
308         S2_linear_heap_.data(), num_capture_channels_);
309     Y = rtc::ArrayView<FftData>(Y_heap_.data(), num_capture_channels_);
310     E = rtc::ArrayView<FftData>(E_heap_.data(), num_capture_channels_);
311     comfort_noise = rtc::ArrayView<FftData>(comfort_noise_heap_.data(),
312                                             num_capture_channels_);
313     high_band_comfort_noise = rtc::ArrayView<FftData>(
314         high_band_comfort_noise_heap_.data(), num_capture_channels_);
315     subtractor_output = rtc::ArrayView<SubtractorOutput>(
316         subtractor_output_heap_.data(), num_capture_channels_);
317   }
318 
319   data_dumper_->DumpWav("aec3_echo_remover_capture_input",
320                         y->View(/*band=*/0, /*channel=*/0), 16000, 1);
321   data_dumper_->DumpWav("aec3_echo_remover_render_input",
322                         x.View(/*band=*/0, /*channel=*/0), 16000, 1);
323   data_dumper_->DumpRaw("aec3_echo_remover_capture_input",
324                         y->View(/*band=*/0, /*channel=*/0));
325   data_dumper_->DumpRaw("aec3_echo_remover_render_input",
326                         x.View(/*band=*/0, /*channel=*/0));
327 
328   aec_state_.UpdateCaptureSaturation(capture_signal_saturation);
329 
330   if (echo_path_variability.AudioPathChanged()) {
331     // Ensure that the gain change is only acted on once per frame.
332     if (echo_path_variability.gain_change) {
333       if (gain_change_hangover_ == 0) {
334         constexpr int kMaxBlocksPerFrame = 3;
335         gain_change_hangover_ = kMaxBlocksPerFrame;
336         rtc::LoggingSeverity log_level =
337             config_.delay.log_warning_on_delay_changes ? rtc::LS_WARNING
338                                                        : rtc::LS_VERBOSE;
339         RTC_LOG_V(log_level)
340             << "Gain change detected at block " << block_counter_;
341       } else {
342         echo_path_variability.gain_change = false;
343       }
344     }
345 
346     subtractor_.HandleEchoPathChange(echo_path_variability);
347     aec_state_.HandleEchoPathChange(echo_path_variability);
348 
349     if (echo_path_variability.delay_change !=
350         EchoPathVariability::DelayAdjustment::kNone) {
351       suppression_gain_.SetInitialState(true);
352     }
353   }
354   if (gain_change_hangover_ > 0) {
355     --gain_change_hangover_;
356   }
357 
358   // Analyze the render signal.
359   render_signal_analyzer_.Update(*render_buffer,
360                                  aec_state_.MinDirectPathFilterDelay());
361 
362   // State transition.
363   if (aec_state_.TransitionTriggered()) {
364     subtractor_.ExitInitialState();
365     suppression_gain_.SetInitialState(false);
366   }
367 
368   // Perform linear echo cancellation.
369   subtractor_.Process(*render_buffer, *y, render_signal_analyzer_, aec_state_,
370                       subtractor_output);
371 
372   // Compute spectra.
373   for (size_t ch = 0; ch < num_capture_channels_; ++ch) {
374     FormLinearFilterOutput(subtractor_output[ch], e[ch]);
375     WindowedPaddedFft(fft_, y->View(/*band=*/0, ch), y_old_[ch], &Y[ch]);
376     WindowedPaddedFft(fft_, e[ch], e_old_[ch], &E[ch]);
377     LinearEchoPower(E[ch], Y[ch], &S2_linear[ch]);
378     Y[ch].Spectrum(optimization_, Y2[ch]);
379     E[ch].Spectrum(optimization_, E2[ch]);
380   }
381 
382   // Optionally return the linear filter output.
383   if (linear_output) {
384     RTC_DCHECK_GE(1, linear_output->NumBands());
385     RTC_DCHECK_EQ(num_capture_channels_, linear_output->NumChannels());
386     for (size_t ch = 0; ch < num_capture_channels_; ++ch) {
387       std::copy(e[ch].begin(), e[ch].end(),
388                 linear_output->begin(/*band=*/0, ch));
389     }
390   }
391 
392   // Update the AEC state information.
393   aec_state_.Update(external_delay, subtractor_.FilterFrequencyResponses(),
394                     subtractor_.FilterImpulseResponses(), *render_buffer, E2,
395                     Y2, subtractor_output);
396 
397   // Choose the linear output.
398   const auto& Y_fft = aec_state_.UseLinearFilterOutput() ? E : Y;
399 
400   data_dumper_->DumpWav("aec3_output_linear",
401                         y->View(/*band=*/0, /*channel=*/0), 16000, 1);
402   data_dumper_->DumpWav("aec3_output_linear2", kBlockSize, &e[0][0], 16000, 1);
403 
404   // Estimate the comfort noise.
405   cng_.Compute(aec_state_.SaturatedCapture(), Y2, comfort_noise,
406                high_band_comfort_noise);
407 
408   // Only do the below processing if the output of the audio processing module
409   // is used.
410   std::array<float, kFftLengthBy2Plus1> G;
411   if (capture_output_used_) {
412     // Estimate the residual echo power.
413     residual_echo_estimator_.Estimate(aec_state_, *render_buffer, S2_linear, Y2,
414                                       suppression_gain_.IsDominantNearend(), R2,
415                                       R2_unbounded);
416 
417     // Suppressor nearend estimate.
418     if (aec_state_.UsableLinearEstimate()) {
419       // E2 is bound by Y2.
420       for (size_t ch = 0; ch < num_capture_channels_; ++ch) {
421         std::transform(E2[ch].begin(), E2[ch].end(), Y2[ch].begin(),
422                        E2[ch].begin(),
423                        [](float a, float b) { return std::min(a, b); });
424       }
425     }
426     const auto& nearend_spectrum = aec_state_.UsableLinearEstimate() ? E2 : Y2;
427 
428     // Suppressor echo estimate.
429     const auto& echo_spectrum =
430         aec_state_.UsableLinearEstimate() ? S2_linear : R2;
431 
432     // Determine if the suppressor should assume clock drift.
433     const bool clock_drift = config_.echo_removal_control.has_clock_drift ||
434                              echo_path_variability.clock_drift;
435 
436     // Compute preferred gains.
437     float high_bands_gain;
438     suppression_gain_.GetGain(nearend_spectrum, echo_spectrum, R2, R2_unbounded,
439                               cng_.NoiseSpectrum(), render_signal_analyzer_,
440                               aec_state_, x, clock_drift, &high_bands_gain, &G);
441 
442     suppression_filter_.ApplyGain(comfort_noise, high_band_comfort_noise, G,
443                                   high_bands_gain, Y_fft, y);
444 
445   } else {
446     G.fill(0.f);
447   }
448 
449   // Update the metrics.
450   metrics_.Update(aec_state_, cng_.NoiseSpectrum()[0], G);
451 
452   // Debug outputs for the purpose of development and analysis.
453   data_dumper_->DumpWav("aec3_echo_estimate", kBlockSize,
454                         &subtractor_output[0].s_refined[0], 16000, 1);
455   data_dumper_->DumpRaw("aec3_output", y->View(/*band=*/0, /*channel=*/0));
456   data_dumper_->DumpRaw("aec3_narrow_render",
457                         render_signal_analyzer_.NarrowPeakBand() ? 1 : 0);
458   data_dumper_->DumpRaw("aec3_N2", cng_.NoiseSpectrum()[0]);
459   data_dumper_->DumpRaw("aec3_suppressor_gain", G);
460   data_dumper_->DumpWav("aec3_output", y->View(/*band=*/0, /*channel=*/0),
461                         16000, 1);
462   data_dumper_->DumpRaw("aec3_using_subtractor_output[0]",
463                         aec_state_.UseLinearFilterOutput() ? 1 : 0);
464   data_dumper_->DumpRaw("aec3_E2", E2[0]);
465   data_dumper_->DumpRaw("aec3_S2_linear", S2_linear[0]);
466   data_dumper_->DumpRaw("aec3_Y2", Y2[0]);
467   data_dumper_->DumpRaw(
468       "aec3_X2", render_buffer->Spectrum(
469                      aec_state_.MinDirectPathFilterDelay())[/*channel=*/0]);
470   data_dumper_->DumpRaw("aec3_R2", R2[0]);
471   data_dumper_->DumpRaw("aec3_filter_delay",
472                         aec_state_.MinDirectPathFilterDelay());
473   data_dumper_->DumpRaw("aec3_capture_saturation",
474                         aec_state_.SaturatedCapture() ? 1 : 0);
475 }
476 
FormLinearFilterOutput(const SubtractorOutput & subtractor_output,rtc::ArrayView<float> output)477 void EchoRemoverImpl::FormLinearFilterOutput(
478     const SubtractorOutput& subtractor_output,
479     rtc::ArrayView<float> output) {
480   RTC_DCHECK_EQ(subtractor_output.e_refined.size(), output.size());
481   RTC_DCHECK_EQ(subtractor_output.e_coarse.size(), output.size());
482   bool use_refined_output = true;
483   if (use_coarse_filter_output_) {
484     // As the output of the refined adaptive filter generally should be better
485     // than the coarse filter output, add a margin and threshold for when
486     // choosing the coarse filter output.
487     if (subtractor_output.e2_coarse < 0.9f * subtractor_output.e2_refined &&
488         subtractor_output.y2 > 30.f * 30.f * kBlockSize &&
489         (subtractor_output.s2_refined > 60.f * 60.f * kBlockSize ||
490          subtractor_output.s2_coarse > 60.f * 60.f * kBlockSize)) {
491       use_refined_output = false;
492     } else {
493       // If the refined filter is diverged, choose the filter output that has
494       // the lowest power.
495       if (subtractor_output.e2_coarse < subtractor_output.e2_refined &&
496           subtractor_output.y2 < subtractor_output.e2_refined) {
497         use_refined_output = false;
498       }
499     }
500   }
501 
502   SignalTransition(refined_filter_output_last_selected_
503                        ? subtractor_output.e_refined
504                        : subtractor_output.e_coarse,
505                    use_refined_output ? subtractor_output.e_refined
506                                       : subtractor_output.e_coarse,
507                    output);
508   refined_filter_output_last_selected_ = use_refined_output;
509 }
510 
511 }  // namespace
512 
Create(const EchoCanceller3Config & config,int sample_rate_hz,size_t num_render_channels,size_t num_capture_channels)513 EchoRemover* EchoRemover::Create(const EchoCanceller3Config& config,
514                                  int sample_rate_hz,
515                                  size_t num_render_channels,
516                                  size_t num_capture_channels) {
517   return new EchoRemoverImpl(config, sample_rate_hz, num_render_channels,
518                              num_capture_channels);
519 }
520 
521 }  // namespace webrtc
522