blob: b03a1215555b2fa8cdb9001a168b39ce9671c49f [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 Åhgrend18e87e2018-05-09 12:07:26 +020029bool EnableTransparentMode() {
30 return !field_trial::IsEnabled("WebRTC-Aec3TransparentModeKillSwitch");
31}
32
Per Åhgren90e3fbd2018-05-16 15:25:04 +020033bool EnableStationaryRenderImprovements() {
34 return !field_trial::IsEnabled(
35 "WebRTC-Aec3StationaryRenderImprovementsKillSwitch");
36}
37
Per Åhgren05d8ee12018-06-07 15:59:59 +020038bool EnableEnforcingDelayAfterRealignment() {
39 return !field_trial::IsEnabled(
40 "WebRTC-Aec3EnforceDelayAfterRealignmentKillSwitch");
41}
42
Gustaf Ullberg6c618c72018-06-28 14:21:16 +020043bool EnableLinearModeWithDivergedFilter() {
44 return !field_trial::IsEnabled(
45 "WebRTC-Aec3LinearModeWithDivergedFilterKillSwitch");
46}
47
Per Åhgrenb6b00dc2018-02-20 22:18:27 +010048float ComputeGainRampupIncrease(const EchoCanceller3Config& config) {
49 const auto& c = config.echo_removal_control.gain_rampup;
50 return powf(1.f / c.first_non_zero_gain, 1.f / c.non_zero_gain_blocks);
51}
52
Per Åhgren5c532d32018-03-22 00:29:25 +010053constexpr size_t kBlocksSinceConvergencedFilterInit = 10000;
54constexpr size_t kBlocksSinceConsistentEstimateInit = 10000;
55
peah522d71b2017-02-23 05:16:26 -080056} // namespace
57
58int AecState::instance_count_ = 0;
59
Gustaf Ullbergbd83b912017-10-18 12:32:42 +020060AecState::AecState(const EchoCanceller3Config& config)
peah522d71b2017-02-23 05:16:26 -080061 : data_dumper_(
62 new ApmDataDumper(rtc::AtomicOps::Increment(&instance_count_))),
peah8cee56f2017-08-24 22:36:53 -070063 config_(config),
Per Åhgren90e3fbd2018-05-16 15:25:04 +020064 allow_transparent_mode_(EnableTransparentMode()),
65 use_stationary_properties_(
66 EnableStationaryRenderImprovements() &&
67 config_.echo_audibility.use_stationary_properties),
Per Åhgren05d8ee12018-06-07 15:59:59 +020068 enforce_delay_after_realignment_(EnableEnforcingDelayAfterRealignment()),
Gustaf Ullberg6c618c72018-06-28 14:21:16 +020069 allow_linear_mode_with_diverged_filter_(
70 EnableLinearModeWithDivergedFilter()),
Per Åhgren90e3fbd2018-05-16 15:25:04 +020071 erle_estimator_(config.erle.min, config.erle.max_l, config.erle.max_h),
Per Åhgren08ea5892018-01-15 08:07:41 +010072 max_render_(config_.filter.main.length_blocks, 0.f),
Per Åhgren12eb8582018-03-06 10:40:51 +010073 gain_rampup_increase_(ComputeGainRampupIncrease(config_)),
Per Åhgren5c532d32018-03-22 00:29:25 +010074 suppression_gain_limiter_(config_),
75 filter_analyzer_(config_),
76 blocks_since_converged_filter_(kBlocksSinceConvergencedFilterInit),
77 active_blocks_since_consistent_filter_estimate_(
Jesús de Vicente Peña496cedf2018-07-04 11:02:09 +020078 kBlocksSinceConsistentEstimateInit),
79 reverb_model_estimator_(config) {}
peah522d71b2017-02-23 05:16:26 -080080
81AecState::~AecState() = default;
82
peah86afe9d2017-04-06 15:45:32 -070083void AecState::HandleEchoPathChange(
84 const EchoPathVariability& echo_path_variability) {
Per Åhgren8ba58612017-12-01 23:01:44 +010085 const auto full_reset = [&]() {
Per Åhgren5c532d32018-03-22 00:29:25 +010086 filter_analyzer_.Reset();
Per Åhgren63b494d2017-12-06 11:32:38 +010087 blocks_since_last_saturation_ = 0;
peah86afe9d2017-04-06 15:45:32 -070088 usable_linear_estimate_ = false;
Gustaf Ullberg6c618c72018-06-28 14:21:16 +020089 diverged_linear_filter_ = false;
peah86afe9d2017-04-06 15:45:32 -070090 capture_signal_saturation_ = false;
91 echo_saturation_ = false;
Per Åhgren09a718a2017-12-11 22:28:45 +010092 std::fill(max_render_.begin(), max_render_.end(), 0.f);
Per Åhgren4b3bc0f2017-12-20 15:26:13 +010093 blocks_with_proper_filter_adaptation_ = 0;
Per Åhgren5c532d32018-03-22 00:29:25 +010094 blocks_since_reset_ = 0;
Per Åhgren4b3bc0f2017-12-20 15:26:13 +010095 filter_has_had_time_to_converge_ = false;
Per Åhgren8ba58612017-12-01 23:01:44 +010096 render_received_ = false;
Per Åhgren4b3bc0f2017-12-20 15:26:13 +010097 blocks_with_active_render_ = 0;
Per Åhgrena98c8072018-01-15 19:17:16 +010098 initial_state_ = true;
Per Åhgren12eb8582018-03-06 10:40:51 +010099 suppression_gain_limiter_.Reset();
Per Åhgren5c532d32018-03-22 00:29:25 +0100100 blocks_since_converged_filter_ = kBlocksSinceConvergencedFilterInit;
101 diverged_blocks_ = 0;
Per Åhgren8ba58612017-12-01 23:01:44 +0100102 };
peah6d822ad2017-04-10 13:52:14 -0700103
Per Åhgren8ba58612017-12-01 23:01:44 +0100104 // TODO(peah): Refine the reset scheme according to the type of gain and
105 // delay adjustment.
106 if (echo_path_variability.gain_change) {
107 full_reset();
108 }
109
110 if (echo_path_variability.delay_change !=
111 EchoPathVariability::DelayAdjustment::kBufferReadjustment) {
112 full_reset();
113 } else if (echo_path_variability.delay_change !=
114 EchoPathVariability::DelayAdjustment::kBufferFlush) {
115 full_reset();
Per Åhgren8ba58612017-12-01 23:01:44 +0100116 } else if (echo_path_variability.delay_change !=
117 EchoPathVariability::DelayAdjustment::kDelayReset) {
118 full_reset();
119 } else if (echo_path_variability.delay_change !=
120 EchoPathVariability::DelayAdjustment::kNewDetectedDelay) {
121 full_reset();
122 } else if (echo_path_variability.gain_change) {
Per Åhgren5c532d32018-03-22 00:29:25 +0100123 blocks_since_reset_ = kNumBlocksPerSecond;
peah86afe9d2017-04-06 15:45:32 -0700124 }
125}
126
Per Åhgren09a718a2017-12-11 22:28:45 +0100127void AecState::Update(
Danil Chapovalovdb9f7ab2018-06-19 10:50:11 +0200128 const absl::optional<DelayEstimate>& external_delay,
Per Åhgren09a718a2017-12-11 22:28:45 +0100129 const std::vector<std::array<float, kFftLengthBy2Plus1>>&
130 adaptive_filter_frequency_response,
131 const std::vector<float>& adaptive_filter_impulse_response,
132 bool converged_filter,
Per Åhgren5c532d32018-03-22 00:29:25 +0100133 bool diverged_filter,
Per Åhgren09a718a2017-12-11 22:28:45 +0100134 const RenderBuffer& render_buffer,
135 const std::array<float, kFftLengthBy2Plus1>& E2_main,
136 const std::array<float, kFftLengthBy2Plus1>& Y2,
Per Åhgren5c532d32018-03-22 00:29:25 +0100137 const std::array<float, kBlockSize>& s) {
138 // Analyze the filter and compute the delays.
Jesús de Vicente Peñae58bd8a2018-06-26 17:19:15 +0200139 filter_analyzer_.Update(adaptive_filter_impulse_response,
140 adaptive_filter_frequency_response, render_buffer);
Per Åhgren5c532d32018-03-22 00:29:25 +0100141 filter_delay_blocks_ = filter_analyzer_.DelayBlocks();
Per Åhgren05d8ee12018-06-07 15:59:59 +0200142 if (enforce_delay_after_realignment_) {
143 if (external_delay &&
144 (!external_delay_ || external_delay_->delay != external_delay->delay)) {
145 frames_since_external_delay_change_ = 0;
146 external_delay_ = external_delay;
147 }
148 if (blocks_with_proper_filter_adaptation_ < 2 * kNumBlocksPerSecond &&
149 external_delay_) {
150 filter_delay_blocks_ = config_.delay.delay_headroom_blocks;
151 }
152 }
peah86afe9d2017-04-06 15:45:32 -0700153
Per Åhgren5c532d32018-03-22 00:29:25 +0100154 if (filter_analyzer_.Consistent()) {
155 internal_delay_ = filter_analyzer_.DelayBlocks();
156 } else {
Danil Chapovalovdb9f7ab2018-06-19 10:50:11 +0200157 internal_delay_ = absl::nullopt;
Per Åhgren5c532d32018-03-22 00:29:25 +0100158 }
159
160 external_delay_seen_ = external_delay_seen_ || external_delay;
161
162 const std::vector<float>& x = render_buffer.Block(-filter_delay_blocks_)[0];
Per Åhgren0e6d2f52017-12-20 22:19:56 +0100163
peah86afe9d2017-04-06 15:45:32 -0700164 // Update counters.
Per Åhgren1b4059e2017-10-15 20:19:21 +0200165 ++capture_block_counter_;
Per Åhgren5c532d32018-03-22 00:29:25 +0100166 ++blocks_since_reset_;
Per Åhgren4b3bc0f2017-12-20 15:26:13 +0100167 const bool active_render_block = DetectActiveRender(x);
168 blocks_with_active_render_ += active_render_block ? 1 : 0;
169 blocks_with_proper_filter_adaptation_ +=
170 active_render_block && !SaturatedCapture() ? 1 : 0;
peah86afe9d2017-04-06 15:45:32 -0700171
Per Åhgrenb6b00dc2018-02-20 22:18:27 +0100172 // Update the limit on the echo suppression after an echo path change to avoid
173 // an initial echo burst.
Per Åhgren5c532d32018-03-22 00:29:25 +0100174 suppression_gain_limiter_.Update(render_buffer.GetRenderActivity(),
175 transparent_mode_);
Per Åhgren4b3bc0f2017-12-20 15:26:13 +0100176
Jesús de Vicente Peñadc872b62018-04-25 16:11:42 +0200177 if (UseStationaryProperties()) {
Jesús de Vicente Peñad5cb4772018-04-25 13:58:45 +0200178 // Update the echo audibility evaluator.
Jesús de Vicente Peña075cb2b2018-06-13 15:13:55 +0200179 echo_audibility_.Update(
180 render_buffer, FilterDelayBlocks(), external_delay_seen_,
181 config_.ep_strength.reverb_based_on_render ? ReverbDecay() : 0.f);
Jesús de Vicente Peñad5cb4772018-04-25 13:58:45 +0200182 }
183
peah86afe9d2017-04-06 15:45:32 -0700184 // Update the ERL and ERLE measures.
Jesús de Vicente Peña666beca2018-05-21 15:23:48 +0200185 if (blocks_since_reset_ >= 2 * kNumBlocksPerSecond) {
Per Åhgren5c532d32018-03-22 00:29:25 +0100186 const auto& X2 = render_buffer.Spectrum(filter_delay_blocks_);
Jesús de Vicente Peña666beca2018-05-21 15:23:48 +0200187 erle_estimator_.Update(X2, Y2, E2_main, converged_filter);
188 if (converged_filter) {
189 erl_estimator_.Update(X2, Y2);
190 }
peah522d71b2017-02-23 05:16:26 -0800191 }
peah86afe9d2017-04-06 15:45:32 -0700192
Per Åhgren63b494d2017-12-06 11:32:38 +0100193 // Detect and flag echo saturation.
194 // TODO(peah): Add the delay in this computation to ensure that the render and
195 // capture signals are properly aligned.
Gustaf Ullbergbd83b912017-10-18 12:32:42 +0200196 if (config_.ep_strength.echo_can_saturate) {
Per Åhgren31122d62018-04-10 16:33:55 +0200197 echo_saturation_ = DetectEchoSaturation(x, EchoPathGain());
Per Åhgren1b4059e2017-10-15 20:19:21 +0200198 }
peah86afe9d2017-04-06 15:45:32 -0700199
Per Åhgren5c532d32018-03-22 00:29:25 +0100200 bool filter_has_had_time_to_converge =
Per Åhgren29f14322018-02-06 15:31:57 +0100201 blocks_with_proper_filter_adaptation_ >= 1.5f * kNumBlocksPerSecond;
Per Åhgren4b3bc0f2017-12-20 15:26:13 +0100202
Per Åhgren5c532d32018-03-22 00:29:25 +0100203 if (!filter_should_have_converged_) {
204 filter_should_have_converged_ =
205 blocks_with_proper_filter_adaptation_ > 6 * kNumBlocksPerSecond;
206 }
207
208 // Flag whether the initial state is still active.
Per Åhgrena98c8072018-01-15 19:17:16 +0100209 initial_state_ =
210 blocks_with_proper_filter_adaptation_ < 5 * kNumBlocksPerSecond;
211
Per Åhgren5c532d32018-03-22 00:29:25 +0100212 // Update counters for the filter divergence and convergence.
213 diverged_blocks_ = diverged_filter ? diverged_blocks_ + 1 : 0;
214 if (diverged_blocks_ >= 60) {
215 blocks_since_converged_filter_ = kBlocksSinceConvergencedFilterInit;
216 } else {
217 blocks_since_converged_filter_ =
218 converged_filter ? 0 : blocks_since_converged_filter_ + 1;
219 }
Per Åhgren8131eb02018-03-28 18:13:41 +0200220 if (converged_filter) {
221 active_blocks_since_converged_filter_ = 0;
222 } else if (active_render_block) {
223 ++active_blocks_since_converged_filter_;
224 }
225
Per Åhgren5c532d32018-03-22 00:29:25 +0100226 bool recently_converged_filter =
227 blocks_since_converged_filter_ < 60 * kNumBlocksPerSecond;
228
Per Åhgrenf3e2bf12018-03-22 10:15:59 +0100229 if (blocks_since_converged_filter_ > 20 * kNumBlocksPerSecond) {
230 converged_filter_count_ = 0;
231 } else if (converged_filter) {
232 ++converged_filter_count_;
233 }
234 if (converged_filter_count_ > 50) {
235 finite_erl_ = true;
236 }
237
Per Åhgren5c532d32018-03-22 00:29:25 +0100238 if (filter_analyzer_.Consistent() && filter_delay_blocks_ < 5) {
239 consistent_filter_seen_ = true;
240 active_blocks_since_consistent_filter_estimate_ = 0;
241 } else if (active_render_block) {
242 ++active_blocks_since_consistent_filter_estimate_;
243 }
244
245 bool consistent_filter_estimate_not_seen;
246 if (!consistent_filter_seen_) {
247 consistent_filter_estimate_not_seen =
248 capture_block_counter_ > 5 * kNumBlocksPerSecond;
249 } else {
250 consistent_filter_estimate_not_seen =
251 active_blocks_since_consistent_filter_estimate_ >
252 30 * kNumBlocksPerSecond;
253 }
254
255 converged_filter_seen_ = converged_filter_seen_ || converged_filter;
Per Åhgren63b494d2017-12-06 11:32:38 +0100256
Per Åhgren8131eb02018-03-28 18:13:41 +0200257 // If no filter convergence is seen for a long time, reset the estimated
258 // properties of the echo path.
259 if (active_blocks_since_converged_filter_ > 60 * kNumBlocksPerSecond) {
260 converged_filter_seen_ = false;
261 finite_erl_ = false;
262 }
263
Per Åhgren63b494d2017-12-06 11:32:38 +0100264 // After an amount of active render samples for which an echo should have been
265 // detected in the capture signal if the ERL was not infinite, flag that a
266 // transparent mode should be entered.
Per Åhgrenf3e2bf12018-03-22 10:15:59 +0100267 transparent_mode_ = !config_.ep_strength.bounded_erl && !finite_erl_;
Per Åhgren4b3bc0f2017-12-20 15:26:13 +0100268 transparent_mode_ =
Per Åhgren5c532d32018-03-22 00:29:25 +0100269 transparent_mode_ &&
270 (consistent_filter_estimate_not_seen || !converged_filter_seen_);
Per Åhgrend18e87e2018-05-09 12:07:26 +0200271 transparent_mode_ = transparent_mode_ && filter_should_have_converged_;
272 transparent_mode_ = transparent_mode_ && allow_transparent_mode_;
Per Åhgren5c532d32018-03-22 00:29:25 +0100273
274 usable_linear_estimate_ = !echo_saturation_;
275 usable_linear_estimate_ =
276 usable_linear_estimate_ && filter_has_had_time_to_converge;
Per Åhgrene3ca9912018-05-28 22:57:17 +0200277
Per Åhgren5c532d32018-03-22 00:29:25 +0100278 usable_linear_estimate_ = usable_linear_estimate_ && external_delay;
Per Åhgrene3ca9912018-05-28 22:57:17 +0200279 if (!config_.echo_removal_control.linear_and_stable_echo_path) {
280 usable_linear_estimate_ =
281 usable_linear_estimate_ && recently_converged_filter;
Gustaf Ullberg6c618c72018-06-28 14:21:16 +0200282 if (!allow_linear_mode_with_diverged_filter_) {
283 usable_linear_estimate_ = usable_linear_estimate_ && !diverged_filter;
284 }
Per Åhgrene3ca9912018-05-28 22:57:17 +0200285 }
Per Åhgren5c532d32018-03-22 00:29:25 +0100286
287 use_linear_filter_output_ = usable_linear_estimate_ && !TransparentMode();
Gustaf Ullberg6c618c72018-06-28 14:21:16 +0200288 diverged_linear_filter_ = diverged_filter;
Per Åhgren5c532d32018-03-22 00:29:25 +0100289
Jesús de Vicente Peña496cedf2018-07-04 11:02:09 +0200290 reverb_model_estimator_.Update(
291 adaptive_filter_impulse_response, adaptive_filter_frequency_response,
292 erle_estimator_.GetInstLinearQualityEstimate(), filter_delay_blocks_,
293 usable_linear_estimate_, config_.ep_strength.default_len,
294 IsBlockStationary());
Jesús de Vicente Peña075cb2b2018-06-13 15:13:55 +0200295
Jesús de Vicente Peña496cedf2018-07-04 11:02:09 +0200296 erle_estimator_.Dump(data_dumper_);
297 reverb_model_estimator_.Dump(data_dumper_);
Per Åhgren5c532d32018-03-22 00:29:25 +0100298 data_dumper_->DumpRaw("aec3_erl", Erl());
Per Åhgren5c532d32018-03-22 00:29:25 +0100299 data_dumper_->DumpRaw("aec3_erl_time_domain", ErlTimeDomain());
300 data_dumper_->DumpRaw("aec3_usable_linear_estimate", UsableLinearEstimate());
301 data_dumper_->DumpRaw("aec3_transparent_mode", transparent_mode_);
302 data_dumper_->DumpRaw("aec3_state_internal_delay",
303 internal_delay_ ? *internal_delay_ : -1);
304 data_dumper_->DumpRaw("aec3_filter_delay", filter_analyzer_.DelayBlocks());
305
306 data_dumper_->DumpRaw("aec3_consistent_filter",
307 filter_analyzer_.Consistent());
308 data_dumper_->DumpRaw("aec3_suppression_gain_limit", SuppressionGainLimit());
309 data_dumper_->DumpRaw("aec3_initial_state", InitialState());
310 data_dumper_->DumpRaw("aec3_capture_saturation", SaturatedCapture());
311 data_dumper_->DumpRaw("aec3_echo_saturation", echo_saturation_);
312 data_dumper_->DumpRaw("aec3_converged_filter", converged_filter);
313 data_dumper_->DumpRaw("aec3_diverged_filter", diverged_filter);
314
315 data_dumper_->DumpRaw("aec3_external_delay_avaliable",
316 external_delay ? 1 : 0);
317 data_dumper_->DumpRaw("aec3_consistent_filter_estimate_not_seen",
318 consistent_filter_estimate_not_seen);
319 data_dumper_->DumpRaw("aec3_filter_should_have_converged",
320 filter_should_have_converged_);
321 data_dumper_->DumpRaw("aec3_filter_has_had_time_to_converge",
322 filter_has_had_time_to_converge);
323 data_dumper_->DumpRaw("aec3_recently_converged_filter",
324 recently_converged_filter);
Jesús de Vicente Peñadd092872018-05-25 16:55:11 +0200325 data_dumper_->DumpRaw("aec3_suppresion_gain_limiter_running",
326 IsSuppressionGainLimitActive());
Jesús de Vicente Peñae58bd8a2018-06-26 17:19:15 +0200327 data_dumper_->DumpRaw("aec3_filter_tail_freq_resp_est", GetFreqRespTail());
peah29103572017-07-11 02:54:02 -0700328
peah29103572017-07-11 02:54:02 -0700329}
330
Per Åhgren4b3bc0f2017-12-20 15:26:13 +0100331bool AecState::DetectActiveRender(rtc::ArrayView<const float> x) const {
332 const float x_energy = std::inner_product(x.begin(), x.end(), x.begin(), 0.f);
333 return x_energy > (config_.render_levels.active_render_limit *
334 config_.render_levels.active_render_limit) *
335 kFftLengthBy2;
336}
337
Per Åhgren31122d62018-04-10 16:33:55 +0200338bool AecState::DetectEchoSaturation(rtc::ArrayView<const float> x,
339 float echo_path_gain) {
Per Åhgren4b3bc0f2017-12-20 15:26:13 +0100340 RTC_DCHECK_LT(0, x.size());
341 const float max_sample = fabs(*std::max_element(
342 x.begin(), x.end(), [](float a, float b) { return a * a < b * b; }));
Per Åhgren4b3bc0f2017-12-20 15:26:13 +0100343
344 // Set flag for potential presence of saturated echo
Per Åhgren31122d62018-04-10 16:33:55 +0200345 const float kMargin = 10.f;
346 float peak_echo_amplitude = max_sample * echo_path_gain * kMargin;
347 if (SaturatedCapture() && peak_echo_amplitude > 32000) {
348 blocks_since_last_saturation_ = 0;
349 } else {
350 ++blocks_since_last_saturation_;
351 }
Per Åhgren4b3bc0f2017-12-20 15:26:13 +0100352
Per Åhgren31122d62018-04-10 16:33:55 +0200353 return blocks_since_last_saturation_ < 5;
Per Åhgren4b3bc0f2017-12-20 15:26:13 +0100354}
355
peah522d71b2017-02-23 05:16:26 -0800356} // namespace webrtc