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 <cmath>
18 #include <memory>
19
20 #include "api/array_view.h"
21 #include "modules/audio_processing/aec3/aec3_common.h"
22 #include "modules/audio_processing/aec3/aec3_fft.h"
23 #include "modules/audio_processing/aec3/aec_state.h"
24 #include "modules/audio_processing/aec3/comfort_noise_generator.h"
25 #include "modules/audio_processing/aec3/echo_path_variability.h"
26 #include "modules/audio_processing/aec3/echo_remover_metrics.h"
27 #include "modules/audio_processing/aec3/fft_data.h"
28 #include "modules/audio_processing/aec3/render_buffer.h"
29 #include "modules/audio_processing/aec3/render_signal_analyzer.h"
30 #include "modules/audio_processing/aec3/residual_echo_estimator.h"
31 #include "modules/audio_processing/aec3/subtractor.h"
32 #include "modules/audio_processing/aec3/subtractor_output.h"
33 #include "modules/audio_processing/aec3/suppression_filter.h"
34 #include "modules/audio_processing/aec3/suppression_gain.h"
35 #include "modules/audio_processing/logging/apm_data_dumper.h"
36 #include "rtc_base/atomic_ops.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(
122 EchoPathVariability echo_path_variability,
123 bool capture_signal_saturation,
124 const absl::optional<DelayEstimate>& external_delay,
125 RenderBuffer* render_buffer,
126 std::vector<std::vector<std::vector<float>>>* linear_output,
127 std::vector<std::vector<std::vector<float>>>* capture) override;
128
129 // Updates the status on whether echo leakage is detected in the output of the
130 // echo remover.
UpdateEchoLeakageStatus(bool leakage_detected)131 void UpdateEchoLeakageStatus(bool leakage_detected) override {
132 echo_leakage_detected_ = leakage_detected;
133 }
134
135 private:
136 // Selects which of the coarse and refined linear filter outputs that is most
137 // appropriate to pass to the suppressor and forms the linear filter output by
138 // smoothly transition between those.
139 void FormLinearFilterOutput(const SubtractorOutput& subtractor_output,
140 rtc::ArrayView<float> output);
141
142 static int instance_count_;
143 const EchoCanceller3Config config_;
144 const Aec3Fft fft_;
145 std::unique_ptr<ApmDataDumper> data_dumper_;
146 const Aec3Optimization optimization_;
147 const int sample_rate_hz_;
148 const size_t num_render_channels_;
149 const size_t num_capture_channels_;
150 const bool use_coarse_filter_output_;
151 Subtractor subtractor_;
152 SuppressionGain suppression_gain_;
153 ComfortNoiseGenerator cng_;
154 SuppressionFilter suppression_filter_;
155 RenderSignalAnalyzer render_signal_analyzer_;
156 ResidualEchoEstimator residual_echo_estimator_;
157 bool echo_leakage_detected_ = false;
158 AecState aec_state_;
159 EchoRemoverMetrics metrics_;
160 std::vector<std::array<float, kFftLengthBy2>> e_old_;
161 std::vector<std::array<float, kFftLengthBy2>> y_old_;
162 size_t block_counter_ = 0;
163 int gain_change_hangover_ = 0;
164 bool refined_filter_output_last_selected_ = true;
165
166 std::vector<std::array<float, kFftLengthBy2>> e_heap_;
167 std::vector<std::array<float, kFftLengthBy2Plus1>> Y2_heap_;
168 std::vector<std::array<float, kFftLengthBy2Plus1>> E2_heap_;
169 std::vector<std::array<float, kFftLengthBy2Plus1>> R2_heap_;
170 std::vector<std::array<float, kFftLengthBy2Plus1>> S2_linear_heap_;
171 std::vector<FftData> Y_heap_;
172 std::vector<FftData> E_heap_;
173 std::vector<FftData> comfort_noise_heap_;
174 std::vector<FftData> high_band_comfort_noise_heap_;
175 std::vector<SubtractorOutput> subtractor_output_heap_;
176 };
177
178 int EchoRemoverImpl::instance_count_ = 0;
179
EchoRemoverImpl(const EchoCanceller3Config & config,int sample_rate_hz,size_t num_render_channels,size_t num_capture_channels)180 EchoRemoverImpl::EchoRemoverImpl(const EchoCanceller3Config& config,
181 int sample_rate_hz,
182 size_t num_render_channels,
183 size_t num_capture_channels)
184 : config_(config),
185 fft_(),
186 data_dumper_(
187 new ApmDataDumper(rtc::AtomicOps::Increment(&instance_count_))),
188 optimization_(DetectOptimization()),
189 sample_rate_hz_(sample_rate_hz),
190 num_render_channels_(num_render_channels),
191 num_capture_channels_(num_capture_channels),
192 use_coarse_filter_output_(
193 config_.filter.enable_coarse_filter_output_usage),
194 subtractor_(config,
195 num_render_channels_,
196 num_capture_channels_,
197 data_dumper_.get(),
198 optimization_),
199 suppression_gain_(config_,
200 optimization_,
201 sample_rate_hz,
202 num_capture_channels),
203 cng_(config_, optimization_, num_capture_channels_),
204 suppression_filter_(optimization_,
205 sample_rate_hz_,
206 num_capture_channels_),
207 render_signal_analyzer_(config_),
208 residual_echo_estimator_(config_, num_render_channels),
209 aec_state_(config_, num_capture_channels_),
210 e_old_(num_capture_channels_, {0.f}),
211 y_old_(num_capture_channels_, {0.f}),
212 e_heap_(NumChannelsOnHeap(num_capture_channels_), {0.f}),
213 Y2_heap_(NumChannelsOnHeap(num_capture_channels_)),
214 E2_heap_(NumChannelsOnHeap(num_capture_channels_)),
215 R2_heap_(NumChannelsOnHeap(num_capture_channels_)),
216 S2_linear_heap_(NumChannelsOnHeap(num_capture_channels_)),
217 Y_heap_(NumChannelsOnHeap(num_capture_channels_)),
218 E_heap_(NumChannelsOnHeap(num_capture_channels_)),
219 comfort_noise_heap_(NumChannelsOnHeap(num_capture_channels_)),
220 high_band_comfort_noise_heap_(NumChannelsOnHeap(num_capture_channels_)),
221 subtractor_output_heap_(NumChannelsOnHeap(num_capture_channels_)) {
222 RTC_DCHECK(ValidFullBandRate(sample_rate_hz));
223 }
224
225 EchoRemoverImpl::~EchoRemoverImpl() = default;
226
GetMetrics(EchoControl::Metrics * metrics) const227 void EchoRemoverImpl::GetMetrics(EchoControl::Metrics* metrics) const {
228 // Echo return loss (ERL) is inverted to go from gain to attenuation.
229 metrics->echo_return_loss = -10.0 * std::log10(aec_state_.ErlTimeDomain());
230 metrics->echo_return_loss_enhancement =
231 Log2TodB(aec_state_.FullBandErleLog2());
232 }
233
ProcessCapture(EchoPathVariability echo_path_variability,bool capture_signal_saturation,const absl::optional<DelayEstimate> & external_delay,RenderBuffer * render_buffer,std::vector<std::vector<std::vector<float>>> * linear_output,std::vector<std::vector<std::vector<float>>> * capture)234 void EchoRemoverImpl::ProcessCapture(
235 EchoPathVariability echo_path_variability,
236 bool capture_signal_saturation,
237 const absl::optional<DelayEstimate>& external_delay,
238 RenderBuffer* render_buffer,
239 std::vector<std::vector<std::vector<float>>>* linear_output,
240 std::vector<std::vector<std::vector<float>>>* capture) {
241 ++block_counter_;
242 const std::vector<std::vector<std::vector<float>>>& x =
243 render_buffer->Block(0);
244 std::vector<std::vector<std::vector<float>>>* y = capture;
245 RTC_DCHECK(render_buffer);
246 RTC_DCHECK(y);
247 RTC_DCHECK_EQ(x.size(), NumBandsForRate(sample_rate_hz_));
248 RTC_DCHECK_EQ(y->size(), NumBandsForRate(sample_rate_hz_));
249 RTC_DCHECK_EQ(x[0].size(), num_render_channels_);
250 RTC_DCHECK_EQ((*y)[0].size(), num_capture_channels_);
251 RTC_DCHECK_EQ(x[0][0].size(), kBlockSize);
252 RTC_DCHECK_EQ((*y)[0][0].size(), kBlockSize);
253
254 // Stack allocated data to use when the number of channels is low.
255 std::array<std::array<float, kFftLengthBy2>, kMaxNumChannelsOnStack> e_stack;
256 std::array<std::array<float, kFftLengthBy2Plus1>, kMaxNumChannelsOnStack>
257 Y2_stack;
258 std::array<std::array<float, kFftLengthBy2Plus1>, kMaxNumChannelsOnStack>
259 E2_stack;
260 std::array<std::array<float, kFftLengthBy2Plus1>, kMaxNumChannelsOnStack>
261 R2_stack;
262 std::array<std::array<float, kFftLengthBy2Plus1>, kMaxNumChannelsOnStack>
263 S2_linear_stack;
264 std::array<FftData, kMaxNumChannelsOnStack> Y_stack;
265 std::array<FftData, kMaxNumChannelsOnStack> E_stack;
266 std::array<FftData, kMaxNumChannelsOnStack> comfort_noise_stack;
267 std::array<FftData, kMaxNumChannelsOnStack> high_band_comfort_noise_stack;
268 std::array<SubtractorOutput, kMaxNumChannelsOnStack> subtractor_output_stack;
269
270 rtc::ArrayView<std::array<float, kFftLengthBy2>> e(e_stack.data(),
271 num_capture_channels_);
272 rtc::ArrayView<std::array<float, kFftLengthBy2Plus1>> Y2(
273 Y2_stack.data(), num_capture_channels_);
274 rtc::ArrayView<std::array<float, kFftLengthBy2Plus1>> E2(
275 E2_stack.data(), num_capture_channels_);
276 rtc::ArrayView<std::array<float, kFftLengthBy2Plus1>> R2(
277 R2_stack.data(), num_capture_channels_);
278 rtc::ArrayView<std::array<float, kFftLengthBy2Plus1>> S2_linear(
279 S2_linear_stack.data(), num_capture_channels_);
280 rtc::ArrayView<FftData> Y(Y_stack.data(), num_capture_channels_);
281 rtc::ArrayView<FftData> E(E_stack.data(), num_capture_channels_);
282 rtc::ArrayView<FftData> comfort_noise(comfort_noise_stack.data(),
283 num_capture_channels_);
284 rtc::ArrayView<FftData> high_band_comfort_noise(
285 high_band_comfort_noise_stack.data(), num_capture_channels_);
286 rtc::ArrayView<SubtractorOutput> subtractor_output(
287 subtractor_output_stack.data(), num_capture_channels_);
288 if (NumChannelsOnHeap(num_capture_channels_) > 0) {
289 // If the stack-allocated space is too small, use the heap for storing the
290 // microphone data.
291 e = rtc::ArrayView<std::array<float, kFftLengthBy2>>(e_heap_.data(),
292 num_capture_channels_);
293 Y2 = rtc::ArrayView<std::array<float, kFftLengthBy2Plus1>>(
294 Y2_heap_.data(), num_capture_channels_);
295 E2 = rtc::ArrayView<std::array<float, kFftLengthBy2Plus1>>(
296 E2_heap_.data(), num_capture_channels_);
297 R2 = rtc::ArrayView<std::array<float, kFftLengthBy2Plus1>>(
298 R2_heap_.data(), num_capture_channels_);
299 S2_linear = rtc::ArrayView<std::array<float, kFftLengthBy2Plus1>>(
300 S2_linear_heap_.data(), num_capture_channels_);
301 Y = rtc::ArrayView<FftData>(Y_heap_.data(), num_capture_channels_);
302 E = rtc::ArrayView<FftData>(E_heap_.data(), num_capture_channels_);
303 comfort_noise = rtc::ArrayView<FftData>(comfort_noise_heap_.data(),
304 num_capture_channels_);
305 high_band_comfort_noise = rtc::ArrayView<FftData>(
306 high_band_comfort_noise_heap_.data(), num_capture_channels_);
307 subtractor_output = rtc::ArrayView<SubtractorOutput>(
308 subtractor_output_heap_.data(), num_capture_channels_);
309 }
310
311 data_dumper_->DumpWav("aec3_echo_remover_capture_input", kBlockSize,
312 &(*y)[0][0][0], 16000, 1);
313 data_dumper_->DumpWav("aec3_echo_remover_render_input", kBlockSize,
314 &x[0][0][0], 16000, 1);
315 data_dumper_->DumpRaw("aec3_echo_remover_capture_input", (*y)[0][0]);
316 data_dumper_->DumpRaw("aec3_echo_remover_render_input", x[0][0]);
317
318 aec_state_.UpdateCaptureSaturation(capture_signal_saturation);
319
320 if (echo_path_variability.AudioPathChanged()) {
321 // Ensure that the gain change is only acted on once per frame.
322 if (echo_path_variability.gain_change) {
323 if (gain_change_hangover_ == 0) {
324 constexpr int kMaxBlocksPerFrame = 3;
325 gain_change_hangover_ = kMaxBlocksPerFrame;
326 rtc::LoggingSeverity log_level =
327 config_.delay.log_warning_on_delay_changes ? rtc::LS_WARNING
328 : rtc::LS_VERBOSE;
329 RTC_LOG_V(log_level)
330 << "Gain change detected at block " << block_counter_;
331 } else {
332 echo_path_variability.gain_change = false;
333 }
334 }
335
336 subtractor_.HandleEchoPathChange(echo_path_variability);
337 aec_state_.HandleEchoPathChange(echo_path_variability);
338
339 if (echo_path_variability.delay_change !=
340 EchoPathVariability::DelayAdjustment::kNone) {
341 suppression_gain_.SetInitialState(true);
342 }
343 }
344 if (gain_change_hangover_ > 0) {
345 --gain_change_hangover_;
346 }
347
348 // Analyze the render signal.
349 render_signal_analyzer_.Update(*render_buffer,
350 aec_state_.MinDirectPathFilterDelay());
351
352 // State transition.
353 if (aec_state_.TransitionTriggered()) {
354 subtractor_.ExitInitialState();
355 suppression_gain_.SetInitialState(false);
356 }
357
358 // Perform linear echo cancellation.
359 subtractor_.Process(*render_buffer, (*y)[0], render_signal_analyzer_,
360 aec_state_, subtractor_output);
361
362 // Compute spectra.
363 for (size_t ch = 0; ch < num_capture_channels_; ++ch) {
364 FormLinearFilterOutput(subtractor_output[ch], e[ch]);
365 WindowedPaddedFft(fft_, (*y)[0][ch], y_old_[ch], &Y[ch]);
366 WindowedPaddedFft(fft_, e[ch], e_old_[ch], &E[ch]);
367 LinearEchoPower(E[ch], Y[ch], &S2_linear[ch]);
368 Y[ch].Spectrum(optimization_, Y2[ch]);
369 E[ch].Spectrum(optimization_, E2[ch]);
370 }
371
372 // Optionally return the linear filter output.
373 if (linear_output) {
374 RTC_DCHECK_GE(1, linear_output->size());
375 RTC_DCHECK_EQ(num_capture_channels_, linear_output[0].size());
376 for (size_t ch = 0; ch < num_capture_channels_; ++ch) {
377 RTC_DCHECK_EQ(kBlockSize, (*linear_output)[0][ch].size());
378 std::copy(e[ch].begin(), e[ch].end(), (*linear_output)[0][ch].begin());
379 }
380 }
381
382 // Update the AEC state information.
383 aec_state_.Update(external_delay, subtractor_.FilterFrequencyResponses(),
384 subtractor_.FilterImpulseResponses(), *render_buffer, E2,
385 Y2, subtractor_output);
386
387 // Choose the linear output.
388 const auto& Y_fft = aec_state_.UseLinearFilterOutput() ? E : Y;
389
390 data_dumper_->DumpWav("aec3_output_linear", kBlockSize, &(*y)[0][0][0], 16000,
391 1);
392 data_dumper_->DumpWav("aec3_output_linear2", kBlockSize, &e[0][0], 16000, 1);
393
394 // Estimate the residual echo power.
395 residual_echo_estimator_.Estimate(aec_state_, *render_buffer, S2_linear, Y2,
396 R2);
397
398 // Estimate the comfort noise.
399 cng_.Compute(aec_state_.SaturatedCapture(), Y2, comfort_noise,
400 high_band_comfort_noise);
401
402 // Suppressor nearend estimate.
403 if (aec_state_.UsableLinearEstimate()) {
404 // E2 is bound by Y2.
405 for (size_t ch = 0; ch < num_capture_channels_; ++ch) {
406 std::transform(E2[ch].begin(), E2[ch].end(), Y2[ch].begin(),
407 E2[ch].begin(),
408 [](float a, float b) { return std::min(a, b); });
409 }
410 }
411 const auto& nearend_spectrum = aec_state_.UsableLinearEstimate() ? E2 : Y2;
412
413 // Suppressor echo estimate.
414 const auto& echo_spectrum =
415 aec_state_.UsableLinearEstimate() ? S2_linear : R2;
416
417 // Compute preferred gains.
418 float high_bands_gain;
419 std::array<float, kFftLengthBy2Plus1> G;
420 suppression_gain_.GetGain(nearend_spectrum, echo_spectrum, R2,
421 cng_.NoiseSpectrum(), render_signal_analyzer_,
422 aec_state_, x, &high_bands_gain, &G);
423
424 suppression_filter_.ApplyGain(comfort_noise, high_band_comfort_noise, G,
425 high_bands_gain, Y_fft, y);
426
427 // Update the metrics.
428 metrics_.Update(aec_state_, cng_.NoiseSpectrum()[0], G);
429
430 // Debug outputs for the purpose of development and analysis.
431 data_dumper_->DumpWav("aec3_echo_estimate", kBlockSize,
432 &subtractor_output[0].s_refined[0], 16000, 1);
433 data_dumper_->DumpRaw("aec3_output", (*y)[0][0]);
434 data_dumper_->DumpRaw("aec3_narrow_render",
435 render_signal_analyzer_.NarrowPeakBand() ? 1 : 0);
436 data_dumper_->DumpRaw("aec3_N2", cng_.NoiseSpectrum()[0]);
437 data_dumper_->DumpRaw("aec3_suppressor_gain", G);
438 data_dumper_->DumpWav("aec3_output",
439 rtc::ArrayView<const float>(&(*y)[0][0][0], kBlockSize),
440 16000, 1);
441 data_dumper_->DumpRaw("aec3_using_subtractor_output[0]",
442 aec_state_.UseLinearFilterOutput() ? 1 : 0);
443 data_dumper_->DumpRaw("aec3_E2", E2[0]);
444 data_dumper_->DumpRaw("aec3_S2_linear", S2_linear[0]);
445 data_dumper_->DumpRaw("aec3_Y2", Y2[0]);
446 data_dumper_->DumpRaw(
447 "aec3_X2", render_buffer->Spectrum(
448 aec_state_.MinDirectPathFilterDelay())[/*channel=*/0]);
449 data_dumper_->DumpRaw("aec3_R2", R2[0]);
450 data_dumper_->DumpRaw("aec3_filter_delay",
451 aec_state_.MinDirectPathFilterDelay());
452 data_dumper_->DumpRaw("aec3_capture_saturation",
453 aec_state_.SaturatedCapture() ? 1 : 0);
454 }
455
FormLinearFilterOutput(const SubtractorOutput & subtractor_output,rtc::ArrayView<float> output)456 void EchoRemoverImpl::FormLinearFilterOutput(
457 const SubtractorOutput& subtractor_output,
458 rtc::ArrayView<float> output) {
459 RTC_DCHECK_EQ(subtractor_output.e_refined.size(), output.size());
460 RTC_DCHECK_EQ(subtractor_output.e_coarse.size(), output.size());
461 bool use_refined_output = true;
462 if (use_coarse_filter_output_) {
463 // As the output of the refined adaptive filter generally should be better
464 // than the coarse filter output, add a margin and threshold for when
465 // choosing the coarse filter output.
466 if (subtractor_output.e2_coarse < 0.9f * subtractor_output.e2_refined &&
467 subtractor_output.y2 > 30.f * 30.f * kBlockSize &&
468 (subtractor_output.s2_refined > 60.f * 60.f * kBlockSize ||
469 subtractor_output.s2_coarse > 60.f * 60.f * kBlockSize)) {
470 use_refined_output = false;
471 } else {
472 // If the refined filter is diverged, choose the filter output that has
473 // the lowest power.
474 if (subtractor_output.e2_coarse < subtractor_output.e2_refined &&
475 subtractor_output.y2 < subtractor_output.e2_refined) {
476 use_refined_output = false;
477 }
478 }
479 }
480
481 SignalTransition(refined_filter_output_last_selected_
482 ? subtractor_output.e_refined
483 : subtractor_output.e_coarse,
484 use_refined_output ? subtractor_output.e_refined
485 : subtractor_output.e_coarse,
486 output);
487 refined_filter_output_last_selected_ = use_refined_output;
488 }
489
490 } // namespace
491
Create(const EchoCanceller3Config & config,int sample_rate_hz,size_t num_render_channels,size_t num_capture_channels)492 EchoRemover* EchoRemover::Create(const EchoCanceller3Config& config,
493 int sample_rate_hz,
494 size_t num_render_channels,
495 size_t num_capture_channels) {
496 return new EchoRemoverImpl(config, sample_rate_hz, num_render_channels,
497 num_capture_channels);
498 }
499
500 } // namespace webrtc
501