blob: b442e0466419905e9062213d1254267db78893b2 [file] [log] [blame]
Per Åhgren31122d62018-04-10 16:33:55 +02001
peah522d71b2017-02-23 05:16:26 -08002/*
3 * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
4 *
5 * Use of this source code is governed by a BSD-style license
6 * that can be found in the LICENSE file in the root of the source
7 * tree. An additional intellectual property rights grant can be found
8 * in the file PATENTS. All contributing project authors may
9 * be found in the AUTHORS file in the root of the source tree.
10 */
11
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020012#include "modules/audio_processing/aec3/suppression_gain.h"
peah522d71b2017-02-23 05:16:26 -080013
peah522d71b2017-02-23 05:16:26 -080014#include <math.h>
15#include <algorithm>
16#include <functional>
peah86afe9d2017-04-06 15:45:32 -070017#include <numeric>
peah522d71b2017-02-23 05:16:26 -080018
Gustaf Ullberg8406c432018-06-19 12:31:33 +020019#include "modules/audio_processing/aec3/moving_average.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020020#include "modules/audio_processing/aec3/vector_math.h"
Gustaf Ullberg216af842018-04-26 12:39:11 +020021#include "modules/audio_processing/logging/apm_data_dumper.h"
22#include "rtc_base/atomicops.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020023#include "rtc_base/checks.h"
Gustaf Ullberg0e6375e2018-05-04 11:29:02 +020024#include "system_wrappers/include/field_trial.h"
peahcf02cf12017-04-05 14:18:07 -070025
peah522d71b2017-02-23 05:16:26 -080026namespace webrtc {
27namespace {
28
Gustaf Ullbergec642172018-07-03 13:48:32 +020029bool EnableNewSuppression() {
30 return !field_trial::IsEnabled("WebRTC-Aec3NewSuppressionKillSwitch");
31}
32
peah1d680892017-05-23 04:07:10 -070033// Adjust the gains according to the presence of known external filters.
34void AdjustForExternalFilters(std::array<float, kFftLengthBy2Plus1>* gain) {
peaha2376e72017-02-27 01:15:24 -080035 // Limit the low frequency gains to avoid the impact of the high-pass filter
36 // on the lower-frequency gain influencing the overall achieved gain.
peah1d680892017-05-23 04:07:10 -070037 (*gain)[0] = (*gain)[1] = std::min((*gain)[1], (*gain)[2]);
peaha2376e72017-02-27 01:15:24 -080038
39 // Limit the high frequency gains to avoid the impact of the anti-aliasing
40 // filter on the upper-frequency gains influencing the overall achieved
41 // gain. TODO(peah): Update this when new anti-aliasing filters are
42 // implemented.
peah86afe9d2017-04-06 15:45:32 -070043 constexpr size_t kAntiAliasingImpactLimit = (64 * 2000) / 8000;
peah1d680892017-05-23 04:07:10 -070044 const float min_upper_gain = (*gain)[kAntiAliasingImpactLimit];
45 std::for_each(
46 gain->begin() + kAntiAliasingImpactLimit, gain->end() - 1,
47 [min_upper_gain](float& a) { a = std::min(a, min_upper_gain); });
48 (*gain)[kFftLengthBy2] = (*gain)[kFftLengthBy2Minus1];
peaha2376e72017-02-27 01:15:24 -080049}
50
peah1d680892017-05-23 04:07:10 -070051// Computes the gain to apply for the bands beyond the first band.
52float UpperBandsGain(
Danil Chapovalovdb9f7ab2018-06-19 10:50:11 +020053 const absl::optional<int>& narrow_peak_band,
peah1d680892017-05-23 04:07:10 -070054 bool saturated_echo,
55 const std::vector<std::vector<float>>& render,
56 const std::array<float, kFftLengthBy2Plus1>& low_band_gain) {
57 RTC_DCHECK_LT(0, render.size());
peah86afe9d2017-04-06 15:45:32 -070058 if (render.size() == 1) {
59 return 1.f;
60 }
61
peah14c11a42017-07-11 06:13:43 -070062 if (narrow_peak_band &&
63 (*narrow_peak_band > static_cast<int>(kFftLengthBy2Plus1 - 10))) {
64 return 0.001f;
65 }
66
peah1d680892017-05-23 04:07:10 -070067 constexpr size_t kLowBandGainLimit = kFftLengthBy2 / 2;
68 const float gain_below_8_khz = *std::min_element(
69 low_band_gain.begin() + kLowBandGainLimit, low_band_gain.end());
70
peah86afe9d2017-04-06 15:45:32 -070071 // Always attenuate the upper bands when there is saturated echo.
72 if (saturated_echo) {
peah1d680892017-05-23 04:07:10 -070073 return std::min(0.001f, gain_below_8_khz);
peah86afe9d2017-04-06 15:45:32 -070074 }
75
76 // Compute the upper and lower band energies.
peah1d680892017-05-23 04:07:10 -070077 const auto sum_of_squares = [](float a, float b) { return a + b * b; };
78 const float low_band_energy =
79 std::accumulate(render[0].begin(), render[0].end(), 0.f, sum_of_squares);
80 float high_band_energy = 0.f;
peah86afe9d2017-04-06 15:45:32 -070081 for (size_t k = 1; k < render.size(); ++k) {
peah1d680892017-05-23 04:07:10 -070082 const float energy = std::accumulate(render[k].begin(), render[k].end(),
83 0.f, sum_of_squares);
84 high_band_energy = std::max(high_band_energy, energy);
peah86afe9d2017-04-06 15:45:32 -070085 }
86
87 // If there is more power in the lower frequencies than the upper frequencies,
peah1d680892017-05-23 04:07:10 -070088 // or if the power in upper frequencies is low, do not bound the gain in the
peah86afe9d2017-04-06 15:45:32 -070089 // upper bands.
peah1d680892017-05-23 04:07:10 -070090 float anti_howling_gain;
Per Åhgren38e2d952017-11-17 14:54:28 +010091 constexpr float kThreshold = kBlockSize * 10.f * 10.f / 4.f;
peah1d680892017-05-23 04:07:10 -070092 if (high_band_energy < std::max(low_band_energy, kThreshold)) {
93 anti_howling_gain = 1.f;
94 } else {
95 // In all other cases, bound the gain for upper frequencies.
96 RTC_DCHECK_LE(low_band_energy, high_band_energy);
97 RTC_DCHECK_NE(0.f, high_band_energy);
98 anti_howling_gain = 0.01f * sqrtf(low_band_energy / high_band_energy);
peah86afe9d2017-04-06 15:45:32 -070099 }
100
peah1d680892017-05-23 04:07:10 -0700101 // Choose the gain as the minimum of the lower and upper gains.
102 return std::min(gain_below_8_khz, anti_howling_gain);
103}
104
Per Åhgrenb02644f2018-04-17 11:52:17 +0200105// Scales the echo according to assessed audibility at the other end.
106void WeightEchoForAudibility(const EchoCanceller3Config& config,
107 rtc::ArrayView<const float> echo,
108 rtc::ArrayView<float> weighted_echo,
109 rtc::ArrayView<float> one_by_weighted_echo) {
110 RTC_DCHECK_EQ(kFftLengthBy2Plus1, echo.size());
111 RTC_DCHECK_EQ(kFftLengthBy2Plus1, weighted_echo.size());
112 RTC_DCHECK_EQ(kFftLengthBy2Plus1, one_by_weighted_echo.size());
113
114 auto weigh = [](float threshold, float normalizer, size_t begin, size_t end,
115 rtc::ArrayView<const float> echo,
116 rtc::ArrayView<float> weighted_echo,
117 rtc::ArrayView<float> one_by_weighted_echo) {
118 for (size_t k = begin; k < end; ++k) {
119 if (echo[k] < threshold) {
120 float tmp = (threshold - echo[k]) * normalizer;
121 weighted_echo[k] = echo[k] * std::max(0.f, 1.f - tmp * tmp);
122 } else {
123 weighted_echo[k] = echo[k];
124 }
125 one_by_weighted_echo[k] =
126 weighted_echo[k] > 0.f ? 1.f / weighted_echo[k] : 1.f;
127 }
128 };
129
130 float threshold = config.echo_audibility.floor_power *
131 config.echo_audibility.audibility_threshold_lf;
132 float normalizer = 1.f / (threshold - config.echo_audibility.floor_power);
133 weigh(threshold, normalizer, 0, 3, echo, weighted_echo, one_by_weighted_echo);
134
135 threshold = config.echo_audibility.floor_power *
136 config.echo_audibility.audibility_threshold_mf;
137 normalizer = 1.f / (threshold - config.echo_audibility.floor_power);
138 weigh(threshold, normalizer, 3, 7, echo, weighted_echo, one_by_weighted_echo);
139
140 threshold = config.echo_audibility.floor_power *
141 config.echo_audibility.audibility_threshold_hf;
142 normalizer = 1.f / (threshold - config.echo_audibility.floor_power);
143 weigh(threshold, normalizer, 7, kFftLengthBy2Plus1, echo, weighted_echo,
144 one_by_weighted_echo);
145}
146
peah1d680892017-05-23 04:07:10 -0700147// Computes the gain to reduce the echo to a non audible level.
Gustaf Ullbergec642172018-07-03 13:48:32 +0200148void GainToNoAudibleEchoFallback(
Gustaf Ullbergbd83b912017-10-18 12:32:42 +0200149 const EchoCanceller3Config& config,
peah1d680892017-05-23 04:07:10 -0700150 bool low_noise_render,
151 bool saturated_echo,
Per Åhgrenc65ce782017-10-09 13:01:39 +0200152 bool linear_echo_estimate,
peah1d680892017-05-23 04:07:10 -0700153 const std::array<float, kFftLengthBy2Plus1>& nearend,
Per Åhgrenb02644f2018-04-17 11:52:17 +0200154 const std::array<float, kFftLengthBy2Plus1>& weighted_echo,
peah1d680892017-05-23 04:07:10 -0700155 const std::array<float, kFftLengthBy2Plus1>& masker,
156 const std::array<float, kFftLengthBy2Plus1>& min_gain,
157 const std::array<float, kFftLengthBy2Plus1>& max_gain,
Per Åhgrenb02644f2018-04-17 11:52:17 +0200158 const std::array<float, kFftLengthBy2Plus1>& one_by_weighted_echo,
peah1d680892017-05-23 04:07:10 -0700159 std::array<float, kFftLengthBy2Plus1>* gain) {
Per Åhgrenc65ce782017-10-09 13:01:39 +0200160 float nearend_masking_margin = 0.f;
Per Åhgren63b494d2017-12-06 11:32:38 +0100161 if (linear_echo_estimate) {
162 nearend_masking_margin =
163 low_noise_render
164 ? config.gain_mask.m9
165 : (saturated_echo ? config.gain_mask.m2 : config.gain_mask.m3);
Per Åhgrenc65ce782017-10-09 13:01:39 +0200166 } else {
Per Åhgren63b494d2017-12-06 11:32:38 +0100167 nearend_masking_margin = config.gain_mask.m7;
Per Åhgrenc65ce782017-10-09 13:01:39 +0200168 }
Per Åhgren7ddd4632017-10-25 02:59:45 +0200169
Per Åhgrend309b002017-10-09 23:50:44 +0200170 RTC_DCHECK_LE(0.f, nearend_masking_margin);
171 RTC_DCHECK_GT(1.f, nearend_masking_margin);
Per Åhgrend309b002017-10-09 23:50:44 +0200172
Per Åhgren63b494d2017-12-06 11:32:38 +0100173 const float masker_margin =
Gustaf Ullbergecb2d562018-08-23 15:11:38 +0200174 linear_echo_estimate ? config.gain_mask.m0 : config.gain_mask.m8;
peah1d680892017-05-23 04:07:10 -0700175
176 for (size_t k = 0; k < gain->size(); ++k) {
Jesús de Vicente Peña075cb2b2018-06-13 15:13:55 +0200177 // TODO(devicentepena): Experiment by removing the reverberation estimation
178 // from the nearend signal before computing the gains.
Per Åhgren7106d932017-10-09 08:25:18 +0200179 const float unity_gain_masker = std::max(nearend[k], masker[k]);
180 RTC_DCHECK_LE(0.f, nearend_masking_margin * unity_gain_masker);
Per Åhgrenb02644f2018-04-17 11:52:17 +0200181 if (weighted_echo[k] <= nearend_masking_margin * unity_gain_masker ||
Per Åhgren7106d932017-10-09 08:25:18 +0200182 unity_gain_masker <= 0.f) {
peah1d680892017-05-23 04:07:10 -0700183 (*gain)[k] = 1.f;
184 } else {
Per Åhgrend309b002017-10-09 23:50:44 +0200185 RTC_DCHECK_LT(0.f, unity_gain_masker);
Per Åhgrend309b002017-10-09 23:50:44 +0200186 (*gain)[k] =
Per Åhgrenb02644f2018-04-17 11:52:17 +0200187 std::max(0.f, (1.f - config.gain_mask.gain_curve_slope *
188 weighted_echo[k] / unity_gain_masker) *
189 config.gain_mask.gain_curve_offset);
190 (*gain)[k] = std::max(masker_margin * masker[k] * one_by_weighted_echo[k],
191 (*gain)[k]);
peah1d680892017-05-23 04:07:10 -0700192 }
193
194 (*gain)[k] = std::min(std::max((*gain)[k], min_gain[k]), max_gain[k]);
195 }
196}
197
Per Åhgren85a11a32017-10-02 14:42:06 +0200198// TODO(peah): Make adaptive to take the actual filter error into account.
199constexpr size_t kUpperAccurateBandPlus1 = 29;
200
Per Åhgren85a11a32017-10-02 14:42:06 +0200201// Limits the gain in the frequencies for which the adaptive filter has not
202// converged. Currently, these frequencies are not hardcoded to the frequencies
203// which are typically not excited by speech.
204// TODO(peah): Make adaptive to take the actual filter error into account.
205void AdjustNonConvergedFrequencies(
206 std::array<float, kFftLengthBy2Plus1>* gain) {
207 constexpr float oneByBandsInSum =
208 1 / static_cast<float>(kUpperAccurateBandPlus1 - 20);
209 const float hf_gain_bound =
210 std::accumulate(gain->begin() + 20,
211 gain->begin() + kUpperAccurateBandPlus1, 0.f) *
212 oneByBandsInSum;
213
214 std::for_each(gain->begin() + kUpperAccurateBandPlus1, gain->end(),
215 [hf_gain_bound](float& a) { a = std::min(a, hf_gain_bound); });
216}
217
peah1d680892017-05-23 04:07:10 -0700218} // namespace
219
Gustaf Ullberg216af842018-04-26 12:39:11 +0200220int SuppressionGain::instance_count_ = 0;
221
Gustaf Ullbergec642172018-07-03 13:48:32 +0200222// Computes the gain to reduce the echo to a non audible level.
223void SuppressionGain::GainToNoAudibleEcho(
224 const std::array<float, kFftLengthBy2Plus1>& nearend,
225 const std::array<float, kFftLengthBy2Plus1>& echo,
226 const std::array<float, kFftLengthBy2Plus1>& masker,
227 const std::array<float, kFftLengthBy2Plus1>& min_gain,
228 const std::array<float, kFftLengthBy2Plus1>& max_gain,
229 std::array<float, kFftLengthBy2Plus1>* gain) const {
230 for (size_t k = 0; k < gain->size(); ++k) {
231 float enr = echo[k] / (nearend[k] + 1.f); // Echo-to-nearend ratio.
232 float emr = echo[k] / (masker[k] + 1.f); // Echo-to-masker (noise) ratio.
233 float g = 1.0f;
234 if (enr > enr_transparent_[k] && emr > emr_transparent_[k]) {
235 g = (enr_suppress_[k] - enr) / (enr_suppress_[k] - enr_transparent_[k]);
236 g = std::max(g, emr_transparent_[k] / emr);
237 }
238 (*gain)[k] = std::max(std::min(g, max_gain[k]), min_gain[k]);
239 }
240}
241
peah1d680892017-05-23 04:07:10 -0700242// TODO(peah): Add further optimizations, in particular for the divisions.
243void SuppressionGain::LowerBandGain(
244 bool low_noise_render,
Per Åhgren5f1a31c2018-03-08 15:54:41 +0100245 const AecState& aec_state,
peah1d680892017-05-23 04:07:10 -0700246 const std::array<float, kFftLengthBy2Plus1>& nearend,
247 const std::array<float, kFftLengthBy2Plus1>& echo,
248 const std::array<float, kFftLengthBy2Plus1>& comfort_noise,
249 std::array<float, kFftLengthBy2Plus1>* gain) {
Per Åhgren5f1a31c2018-03-08 15:54:41 +0100250 const bool saturated_echo = aec_state.SaturatedEcho();
Per Åhgren5f1a31c2018-03-08 15:54:41 +0100251 const bool linear_echo_estimate = aec_state.UsableLinearEstimate();
252
Per Åhgrenb02644f2018-04-17 11:52:17 +0200253 // Weight echo power in terms of audibility. // Precompute 1/weighted echo
254 // (note that when the echo is zero, the precomputed value is never used).
255 std::array<float, kFftLengthBy2Plus1> weighted_echo;
256 std::array<float, kFftLengthBy2Plus1> one_by_weighted_echo;
257 WeightEchoForAudibility(config_, echo, weighted_echo, one_by_weighted_echo);
peah1d680892017-05-23 04:07:10 -0700258
259 // Compute the minimum gain as the attenuating gain to put the signal just
260 // above the zero sample values.
261 std::array<float, kFftLengthBy2Plus1> min_gain;
peah8cee56f2017-08-24 22:36:53 -0700262 const float min_echo_power =
Gustaf Ullbergbd83b912017-10-18 12:32:42 +0200263 low_noise_render ? config_.echo_audibility.low_render_limit
264 : config_.echo_audibility.normal_render_limit;
Per Åhgren31122d62018-04-10 16:33:55 +0200265 if (!saturated_echo) {
peah1d680892017-05-23 04:07:10 -0700266 for (size_t k = 0; k < nearend.size(); ++k) {
Per Åhgrenb02644f2018-04-17 11:52:17 +0200267 const float denom = std::min(nearend[k], weighted_echo[k]);
peah1d680892017-05-23 04:07:10 -0700268 min_gain[k] = denom > 0.f ? min_echo_power / denom : 1.f;
269 min_gain[k] = std::min(min_gain[k], 1.f);
270 }
Gustaf Ullbergecb2d562018-08-23 15:11:38 +0200271 for (size_t k = 0; k < 6; ++k) {
272 // Make sure the gains of the low frequencies do not decrease too
273 // quickly after strong nearend.
274 if (last_nearend_[k] > last_echo_[k]) {
275 min_gain[k] =
276 std::max(min_gain[k],
277 last_gain_[k] * config_.gain_updates.max_dec_factor_lf);
278 min_gain[k] = std::min(min_gain[k], 1.f);
Gustaf Ullberg0e6375e2018-05-04 11:29:02 +0200279 }
280 }
peah1d680892017-05-23 04:07:10 -0700281 } else {
282 min_gain.fill(0.f);
283 }
284
285 // Compute the maximum gain by limiting the gain increase from the previous
286 // gain.
287 std::array<float, kFftLengthBy2Plus1> max_gain;
Gustaf Ullbergecb2d562018-08-23 15:11:38 +0200288 for (size_t k = 0; k < gain->size(); ++k) {
289 max_gain[k] =
290 std::min(std::max(last_gain_[k] * config_.gain_updates.max_inc_factor,
291 config_.gain_updates.floor_first_increase),
292 1.f);
peah1d680892017-05-23 04:07:10 -0700293 }
294
295 // Iteratively compute the gain required to attenuate the echo to a non
296 // noticeable level.
Gustaf Ullberg216af842018-04-26 12:39:11 +0200297 std::array<float, kFftLengthBy2Plus1> masker;
Gustaf Ullbergec642172018-07-03 13:48:32 +0200298 if (enable_new_suppression_) {
299 GainToNoAudibleEcho(nearend, weighted_echo, comfort_noise, min_gain,
300 max_gain, gain);
peah1d680892017-05-23 04:07:10 -0700301 AdjustForExternalFilters(gain);
Gustaf Ullbergec642172018-07-03 13:48:32 +0200302 } else {
303 gain->fill(0.f);
304 for (int k = 0; k < 2; ++k) {
Gustaf Ullbergecb2d562018-08-23 15:11:38 +0200305 std::copy(comfort_noise.begin(), comfort_noise.end(), masker.begin());
306 GainToNoAudibleEchoFallback(config_, low_noise_render, saturated_echo,
307 linear_echo_estimate, nearend, weighted_echo,
308 masker, min_gain, max_gain,
309 one_by_weighted_echo, gain);
Gustaf Ullbergec642172018-07-03 13:48:32 +0200310 AdjustForExternalFilters(gain);
311 }
peah1d680892017-05-23 04:07:10 -0700312 }
313
Per Åhgren85a11a32017-10-02 14:42:06 +0200314 // Adjust the gain for frequencies which have not yet converged.
315 AdjustNonConvergedFrequencies(gain);
316
peah1d680892017-05-23 04:07:10 -0700317 // Store data required for the gain computation of the next block.
Gustaf Ullberg0e6375e2018-05-04 11:29:02 +0200318 std::copy(nearend.begin(), nearend.end(), last_nearend_.begin());
Per Åhgrenb02644f2018-04-17 11:52:17 +0200319 std::copy(weighted_echo.begin(), weighted_echo.end(), last_echo_.begin());
peah1d680892017-05-23 04:07:10 -0700320 std::copy(gain->begin(), gain->end(), last_gain_.begin());
peah1d680892017-05-23 04:07:10 -0700321 aec3::VectorMath(optimization_).Sqrt(*gain);
Gustaf Ullberg216af842018-04-26 12:39:11 +0200322
323 // Debug outputs for the purpose of development and analysis.
324 data_dumper_->DumpRaw("aec3_suppressor_min_gain", min_gain);
325 data_dumper_->DumpRaw("aec3_suppressor_max_gain", max_gain);
326 data_dumper_->DumpRaw("aec3_suppressor_masker", masker);
peah86afe9d2017-04-06 15:45:32 -0700327}
328
Gustaf Ullbergbd83b912017-10-18 12:32:42 +0200329SuppressionGain::SuppressionGain(const EchoCanceller3Config& config,
Per Åhgren47d7fbd2018-04-24 12:44:29 +0200330 Aec3Optimization optimization,
331 int sample_rate_hz)
Gustaf Ullberg216af842018-04-26 12:39:11 +0200332 : data_dumper_(
333 new ApmDataDumper(rtc::AtomicOps::Increment(&instance_count_))),
334 optimization_(optimization),
Per Åhgren5f1a31c2018-03-08 15:54:41 +0100335 config_(config),
336 state_change_duration_blocks_(
Per Åhgren47d7fbd2018-04-24 12:44:29 +0200337 static_cast<int>(config_.filter.config_change_duration_blocks)),
Gustaf Ullbergec642172018-07-03 13:48:32 +0200338 enable_new_suppression_(EnableNewSuppression()),
Gustaf Ullberg8406c432018-06-19 12:31:33 +0200339 moving_average_(kFftLengthBy2Plus1,
340 config.suppressor.nearend_average_blocks) {
Per Åhgren5f1a31c2018-03-08 15:54:41 +0100341 RTC_DCHECK_LT(0, state_change_duration_blocks_);
342 one_by_state_change_duration_blocks_ = 1.f / state_change_duration_blocks_;
peah1d680892017-05-23 04:07:10 -0700343 last_gain_.fill(1.f);
Gustaf Ullberg0e6375e2018-05-04 11:29:02 +0200344 last_nearend_.fill(0.f);
peah1d680892017-05-23 04:07:10 -0700345 last_echo_.fill(0.f);
Gustaf Ullbergec642172018-07-03 13:48:32 +0200346
347 // Compute per-band masking thresholds.
348 constexpr size_t kLastLfBand = 5;
349 constexpr size_t kFirstHfBand = 8;
350 RTC_DCHECK_LT(kLastLfBand, kFirstHfBand);
351 auto& lf = config.suppressor.mask_lf;
352 auto& hf = config.suppressor.mask_hf;
353 RTC_DCHECK_LT(lf.enr_transparent, lf.enr_suppress);
354 RTC_DCHECK_LT(hf.enr_transparent, hf.enr_suppress);
355 for (size_t k = 0; k < kFftLengthBy2Plus1; k++) {
356 float a;
357 if (k <= kLastLfBand) {
358 a = 0.f;
359 } else if (k < kFirstHfBand) {
360 a = (k - kLastLfBand) / static_cast<float>(kFirstHfBand - kLastLfBand);
361 } else {
362 a = 1.f;
363 }
364 enr_transparent_[k] = (1 - a) * lf.enr_transparent + a * hf.enr_transparent;
365 enr_suppress_[k] = (1 - a) * lf.enr_suppress + a * hf.enr_suppress;
366 emr_transparent_[k] = (1 - a) * lf.emr_transparent + a * hf.emr_transparent;
367 }
peah522d71b2017-02-23 05:16:26 -0800368}
369
Per Åhgren47d7fbd2018-04-24 12:44:29 +0200370SuppressionGain::~SuppressionGain() = default;
371
peah522d71b2017-02-23 05:16:26 -0800372void SuppressionGain::GetGain(
Per Åhgren47d7fbd2018-04-24 12:44:29 +0200373 const std::array<float, kFftLengthBy2Plus1>& nearend_spectrum,
374 const std::array<float, kFftLengthBy2Plus1>& echo_spectrum,
375 const std::array<float, kFftLengthBy2Plus1>& comfort_noise_spectrum,
376 const FftData& linear_aec_fft,
Per Åhgren47d7fbd2018-04-24 12:44:29 +0200377 const FftData& capture_fft,
peah14c11a42017-07-11 06:13:43 -0700378 const RenderSignalAnalyzer& render_signal_analyzer,
Per Åhgren7ddd4632017-10-25 02:59:45 +0200379 const AecState& aec_state,
peah86afe9d2017-04-06 15:45:32 -0700380 const std::vector<std::vector<float>>& render,
peah86afe9d2017-04-06 15:45:32 -0700381 float* high_bands_gain,
382 std::array<float, kFftLengthBy2Plus1>* low_band_gain) {
383 RTC_DCHECK(high_bands_gain);
384 RTC_DCHECK(low_band_gain);
Per Åhgren7343f562018-08-17 10:08:34 +0200385 const auto& cfg = config_.suppressor;
386
387 if (cfg.enforce_transparent) {
388 low_band_gain->fill(1.f);
389 *high_bands_gain = cfg.enforce_empty_higher_bands ? 0.f : 1.f;
390 return;
391 }
peah86afe9d2017-04-06 15:45:32 -0700392
Gustaf Ullberg8406c432018-06-19 12:31:33 +0200393 std::array<float, kFftLengthBy2Plus1> nearend_average;
394 moving_average_.Average(nearend_spectrum, nearend_average);
395
peah1d680892017-05-23 04:07:10 -0700396 // Compute gain for the lower band.
Per Åhgren5f1a31c2018-03-08 15:54:41 +0100397 bool low_noise_render = low_render_detector_.Detect(render);
Danil Chapovalovdb9f7ab2018-06-19 10:50:11 +0200398 const absl::optional<int> narrow_peak_band =
peah14c11a42017-07-11 06:13:43 -0700399 render_signal_analyzer.NarrowPeakBand();
Gustaf Ullberg8406c432018-06-19 12:31:33 +0200400 LowerBandGain(low_noise_render, aec_state, nearend_average, echo_spectrum,
Gustaf Ullberg5bb98972018-04-25 12:54:59 +0200401 comfort_noise_spectrum, low_band_gain);
peah86afe9d2017-04-06 15:45:32 -0700402
Gustaf Ullberg0cb4a252018-04-26 15:45:44 +0200403 // Limit the gain of the lower bands during start up and after resets.
404 const float gain_upper_bound = aec_state.SuppressionGainLimit();
405 if (gain_upper_bound < 1.f) {
406 for (size_t k = 0; k < low_band_gain->size(); ++k) {
407 (*low_band_gain)[k] = std::min((*low_band_gain)[k], gain_upper_bound);
408 }
409 }
410
411 // Compute the gain for the upper bands.
412 *high_bands_gain = UpperBandsGain(narrow_peak_band, aec_state.SaturatedEcho(),
413 render, *low_band_gain);
Per Åhgren7343f562018-08-17 10:08:34 +0200414 if (cfg.enforce_empty_higher_bands) {
415 *high_bands_gain = 0.f;
416 }
Per Åhgren5f1a31c2018-03-08 15:54:41 +0100417}
418
419void SuppressionGain::SetInitialState(bool state) {
420 initial_state_ = state;
421 if (state) {
422 initial_state_change_counter_ = state_change_duration_blocks_;
423 } else {
424 initial_state_change_counter_ = 0;
425 }
426}
427
peah1d680892017-05-23 04:07:10 -0700428// Detects when the render signal can be considered to have low power and
429// consist of stationary noise.
430bool SuppressionGain::LowNoiseRenderDetector::Detect(
431 const std::vector<std::vector<float>>& render) {
432 float x2_sum = 0.f;
433 float x2_max = 0.f;
434 for (auto x_k : render[0]) {
435 const float x2 = x_k * x_k;
436 x2_sum += x2;
437 x2_max = std::max(x2_max, x2);
peah522d71b2017-02-23 05:16:26 -0800438 }
peah1d680892017-05-23 04:07:10 -0700439
440 constexpr float kThreshold = 50.f * 50.f * 64.f;
441 const bool low_noise_render =
442 average_power_ < kThreshold && x2_max < 3 * average_power_;
443 average_power_ = average_power_ * 0.9f + x2_sum * 0.1f;
444 return low_noise_render;
peah522d71b2017-02-23 05:16:26 -0800445}
446
447} // namespace webrtc