blob: 7455d29e165aa07dadceb8427d689042ec2cf1be [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
11#include "webrtc/modules/audio_processing/aec3/suppression_gain.h"
12
13#include "webrtc/typedefs.h"
14#if defined(WEBRTC_ARCH_X86_FAMILY)
15#include <emmintrin.h>
16#endif
17#include <math.h>
18#include <algorithm>
19#include <functional>
peah86afe9d2017-04-06 15:45:32 -070020#include <numeric>
peah522d71b2017-02-23 05:16:26 -080021
peahcf02cf12017-04-05 14:18:07 -070022#include "webrtc/base/checks.h"
peah5e79b292017-04-12 01:20:45 -070023#include "webrtc/modules/audio_processing/aec3/vector_math.h"
peahcf02cf12017-04-05 14:18:07 -070024
peah522d71b2017-02-23 05:16:26 -080025namespace webrtc {
26namespace {
27
peah1d680892017-05-23 04:07:10 -070028// Adjust the gains according to the presence of known external filters.
29void AdjustForExternalFilters(std::array<float, kFftLengthBy2Plus1>* gain) {
peaha2376e72017-02-27 01:15:24 -080030 // Limit the low frequency gains to avoid the impact of the high-pass filter
31 // on the lower-frequency gain influencing the overall achieved gain.
peah1d680892017-05-23 04:07:10 -070032 (*gain)[0] = (*gain)[1] = std::min((*gain)[1], (*gain)[2]);
peaha2376e72017-02-27 01:15:24 -080033
34 // Limit the high frequency gains to avoid the impact of the anti-aliasing
35 // filter on the upper-frequency gains influencing the overall achieved
36 // gain. TODO(peah): Update this when new anti-aliasing filters are
37 // implemented.
peah86afe9d2017-04-06 15:45:32 -070038 constexpr size_t kAntiAliasingImpactLimit = (64 * 2000) / 8000;
peah1d680892017-05-23 04:07:10 -070039 const float min_upper_gain = (*gain)[kAntiAliasingImpactLimit];
40 std::for_each(
41 gain->begin() + kAntiAliasingImpactLimit, gain->end() - 1,
42 [min_upper_gain](float& a) { a = std::min(a, min_upper_gain); });
43 (*gain)[kFftLengthBy2] = (*gain)[kFftLengthBy2Minus1];
peaha2376e72017-02-27 01:15:24 -080044}
45
peah1d680892017-05-23 04:07:10 -070046// Computes the gain to apply for the bands beyond the first band.
47float UpperBandsGain(
48 bool saturated_echo,
49 const std::vector<std::vector<float>>& render,
50 const std::array<float, kFftLengthBy2Plus1>& low_band_gain) {
51 RTC_DCHECK_LT(0, render.size());
peah86afe9d2017-04-06 15:45:32 -070052 if (render.size() == 1) {
53 return 1.f;
54 }
55
peah1d680892017-05-23 04:07:10 -070056 constexpr size_t kLowBandGainLimit = kFftLengthBy2 / 2;
57 const float gain_below_8_khz = *std::min_element(
58 low_band_gain.begin() + kLowBandGainLimit, low_band_gain.end());
59
peah86afe9d2017-04-06 15:45:32 -070060 // Always attenuate the upper bands when there is saturated echo.
61 if (saturated_echo) {
peah1d680892017-05-23 04:07:10 -070062 return std::min(0.001f, gain_below_8_khz);
peah86afe9d2017-04-06 15:45:32 -070063 }
64
65 // Compute the upper and lower band energies.
peah1d680892017-05-23 04:07:10 -070066 const auto sum_of_squares = [](float a, float b) { return a + b * b; };
67 const float low_band_energy =
68 std::accumulate(render[0].begin(), render[0].end(), 0.f, sum_of_squares);
69 float high_band_energy = 0.f;
peah86afe9d2017-04-06 15:45:32 -070070 for (size_t k = 1; k < render.size(); ++k) {
peah1d680892017-05-23 04:07:10 -070071 const float energy = std::accumulate(render[k].begin(), render[k].end(),
72 0.f, sum_of_squares);
73 high_band_energy = std::max(high_band_energy, energy);
peah86afe9d2017-04-06 15:45:32 -070074 }
75
76 // If there is more power in the lower frequencies than the upper frequencies,
peah1d680892017-05-23 04:07:10 -070077 // or if the power in upper frequencies is low, do not bound the gain in the
peah86afe9d2017-04-06 15:45:32 -070078 // upper bands.
peah1d680892017-05-23 04:07:10 -070079 float anti_howling_gain;
80 constexpr float kThreshold = kSubBlockSize * 10.f * 10.f;
81 if (high_band_energy < std::max(low_band_energy, kThreshold)) {
82 anti_howling_gain = 1.f;
83 } else {
84 // In all other cases, bound the gain for upper frequencies.
85 RTC_DCHECK_LE(low_band_energy, high_band_energy);
86 RTC_DCHECK_NE(0.f, high_band_energy);
87 anti_howling_gain = 0.01f * sqrtf(low_band_energy / high_band_energy);
peah86afe9d2017-04-06 15:45:32 -070088 }
89
peah1d680892017-05-23 04:07:10 -070090 // Choose the gain as the minimum of the lower and upper gains.
91 return std::min(gain_below_8_khz, anti_howling_gain);
92}
93
94// Limits the gain increase.
95void UpdateMaxGainIncrease(
96 size_t no_saturation_counter,
97 bool low_noise_render,
98 const std::array<float, kFftLengthBy2Plus1>& last_echo,
99 const std::array<float, kFftLengthBy2Plus1>& echo,
100 const std::array<float, kFftLengthBy2Plus1>& last_gain,
101 const std::array<float, kFftLengthBy2Plus1>& new_gain,
102 std::array<float, kFftLengthBy2Plus1>* gain_increase) {
103 float max_increasing;
104 float max_decreasing;
105 float rate_increasing;
106 float rate_decreasing;
107 float min_increasing;
108 float min_decreasing;
109
110 if (low_noise_render) {
111 max_increasing = 8.f;
112 max_decreasing = 8.f;
113 rate_increasing = 2.f;
114 rate_decreasing = 2.f;
115 min_increasing = 4.f;
116 min_decreasing = 4.f;
117 } else if (no_saturation_counter > 10) {
118 max_increasing = 4.f;
119 max_decreasing = 4.f;
120 rate_increasing = 2.f;
121 rate_decreasing = 2.f;
122 min_increasing = 1.2f;
123 min_decreasing = 2.f;
124 } else {
125 max_increasing = 1.2f;
126 max_decreasing = 1.2f;
127 rate_increasing = 1.5f;
128 rate_decreasing = 1.5f;
129 min_increasing = 1.f;
130 min_decreasing = 1.f;
131 }
132
133 for (size_t k = 0; k < new_gain.size(); ++k) {
134 if (echo[k] > last_echo[k]) {
135 (*gain_increase)[k] =
136 new_gain[k] > last_gain[k]
137 ? std::min(max_increasing, (*gain_increase)[k] * rate_increasing)
138 : min_increasing;
139 } else {
140 (*gain_increase)[k] =
141 new_gain[k] > last_gain[k]
142 ? std::min(max_decreasing, (*gain_increase)[k] * rate_decreasing)
143 : min_decreasing;
144 }
145 }
146}
147
148// Computes the gain to reduce the echo to a non audible level.
149void GainToNoAudibleEcho(
150 bool low_noise_render,
151 bool saturated_echo,
152 const std::array<float, kFftLengthBy2Plus1>& nearend,
153 const std::array<float, kFftLengthBy2Plus1>& echo,
154 const std::array<float, kFftLengthBy2Plus1>& masker,
155 const std::array<float, kFftLengthBy2Plus1>& min_gain,
156 const std::array<float, kFftLengthBy2Plus1>& max_gain,
157 const std::array<float, kFftLengthBy2Plus1>& one_by_echo,
158 std::array<float, kFftLengthBy2Plus1>* gain) {
159 constexpr float kEchoMaskingMargin = 1.f / 100.f;
160 const float nearend_masking_margin =
161 low_noise_render ? 2.f : (saturated_echo ? 0.001f : 0.01f);
162
163 for (size_t k = 0; k < gain->size(); ++k) {
164 RTC_DCHECK_LE(0.f, nearend_masking_margin * nearend[k]);
165 if (echo[k] <= nearend_masking_margin * nearend[k]) {
166 (*gain)[k] = 1.f;
167 } else {
168 (*gain)[k] = kEchoMaskingMargin * masker[k] * one_by_echo[k];
169 }
170
171 (*gain)[k] = std::min(std::max((*gain)[k], min_gain[k]), max_gain[k]);
172 }
173}
174
175// Computes the signal output power that masks the echo signal.
176void MaskingPower(const std::array<float, kFftLengthBy2Plus1>& nearend,
177 const std::array<float, kFftLengthBy2Plus1>& comfort_noise,
178 const std::array<float, kFftLengthBy2Plus1>& last_masker,
179 const std::array<float, kFftLengthBy2Plus1>& gain,
180 std::array<float, kFftLengthBy2Plus1>* masker) {
181 std::array<float, kFftLengthBy2Plus1> side_band_masker;
182 for (size_t k = 0; k < gain.size(); ++k) {
183 side_band_masker[k] = nearend[k] * gain[k] + comfort_noise[k];
184 (*masker)[k] = comfort_noise[k] + 0.1f * last_masker[k];
185 }
186 for (size_t k = 1; k < gain.size() - 1; ++k) {
187 (*masker)[k] += 0.1f * (side_band_masker[k - 1] + side_band_masker[k + 1]);
188 }
189}
190
191} // namespace
192
193// TODO(peah): Add further optimizations, in particular for the divisions.
194void SuppressionGain::LowerBandGain(
195 bool low_noise_render,
196 bool saturated_echo,
197 const std::array<float, kFftLengthBy2Plus1>& nearend,
198 const std::array<float, kFftLengthBy2Plus1>& echo,
199 const std::array<float, kFftLengthBy2Plus1>& comfort_noise,
200 std::array<float, kFftLengthBy2Plus1>* gain) {
201 // Count the number of blocks since saturation.
202 no_saturation_counter_ = saturated_echo ? 0 : no_saturation_counter_ + 1;
203
204 // Precompute 1/echo (note that when the echo is zero, the precomputed value
205 // is never used).
206 std::array<float, kFftLengthBy2Plus1> one_by_echo;
207 std::transform(echo.begin(), echo.end(), one_by_echo.begin(),
208 [](float a) { return a > 0.f ? 1.f / a : 1.f; });
209
210 // Compute the minimum gain as the attenuating gain to put the signal just
211 // above the zero sample values.
212 std::array<float, kFftLengthBy2Plus1> min_gain;
213 const float min_echo_power = low_noise_render ? 192.f : 64.f;
214 if (no_saturation_counter_ > 10) {
215 for (size_t k = 0; k < nearend.size(); ++k) {
216 const float denom = std::min(nearend[k], echo[k]);
217 min_gain[k] = denom > 0.f ? min_echo_power / denom : 1.f;
218 min_gain[k] = std::min(min_gain[k], 1.f);
219 }
220 } else {
221 min_gain.fill(0.f);
222 }
223
224 // Compute the maximum gain by limiting the gain increase from the previous
225 // gain.
226 std::array<float, kFftLengthBy2Plus1> max_gain;
227 for (size_t k = 0; k < gain->size(); ++k) {
228 max_gain[k] =
229 std::min(std::max(last_gain_[k] * gain_increase_[k], 0.001f), 1.f);
230 }
231
232 // Iteratively compute the gain required to attenuate the echo to a non
233 // noticeable level.
234 gain->fill(0.f);
235 for (int k = 0; k < 2; ++k) {
236 std::array<float, kFftLengthBy2Plus1> masker;
237 MaskingPower(nearend, comfort_noise, last_masker_, *gain, &masker);
238 GainToNoAudibleEcho(low_noise_render, saturated_echo, nearend, echo, masker,
239 min_gain, max_gain, one_by_echo, gain);
240 AdjustForExternalFilters(gain);
241 }
242
243 // Update the allowed maximum gain increase.
244 UpdateMaxGainIncrease(no_saturation_counter_, low_noise_render, last_echo_,
245 echo, last_gain_, *gain, &gain_increase_);
246
247 // Store data required for the gain computation of the next block.
248 std::copy(echo.begin(), echo.end(), last_echo_.begin());
249 std::copy(gain->begin(), gain->end(), last_gain_.begin());
250 MaskingPower(nearend, comfort_noise, last_masker_, *gain, &last_masker_);
251 aec3::VectorMath(optimization_).Sqrt(*gain);
peah86afe9d2017-04-06 15:45:32 -0700252}
253
peah522d71b2017-02-23 05:16:26 -0800254SuppressionGain::SuppressionGain(Aec3Optimization optimization)
255 : optimization_(optimization) {
peah1d680892017-05-23 04:07:10 -0700256 last_gain_.fill(1.f);
257 last_masker_.fill(0.f);
258 gain_increase_.fill(1.f);
259 last_echo_.fill(0.f);
peah522d71b2017-02-23 05:16:26 -0800260}
261
262void SuppressionGain::GetGain(
peah1d680892017-05-23 04:07:10 -0700263 const std::array<float, kFftLengthBy2Plus1>& nearend,
264 const std::array<float, kFftLengthBy2Plus1>& echo,
265 const std::array<float, kFftLengthBy2Plus1>& comfort_noise,
peah86afe9d2017-04-06 15:45:32 -0700266 bool saturated_echo,
267 const std::vector<std::vector<float>>& render,
peah6d822ad2017-04-10 13:52:14 -0700268 bool force_zero_gain,
peah86afe9d2017-04-06 15:45:32 -0700269 float* high_bands_gain,
270 std::array<float, kFftLengthBy2Plus1>* low_band_gain) {
271 RTC_DCHECK(high_bands_gain);
272 RTC_DCHECK(low_band_gain);
273
peah6d822ad2017-04-10 13:52:14 -0700274 if (force_zero_gain) {
peah1d680892017-05-23 04:07:10 -0700275 last_gain_.fill(0.f);
276 std::copy(comfort_noise.begin(), comfort_noise.end(), last_masker_.begin());
peah6d822ad2017-04-10 13:52:14 -0700277 low_band_gain->fill(0.f);
peah1d680892017-05-23 04:07:10 -0700278 gain_increase_.fill(1.f);
peah6d822ad2017-04-10 13:52:14 -0700279 *high_bands_gain = 0.f;
280 return;
281 }
282
peah1d680892017-05-23 04:07:10 -0700283 bool low_noise_render = low_render_detector_.Detect(render);
peah86afe9d2017-04-06 15:45:32 -0700284
peah1d680892017-05-23 04:07:10 -0700285 // Compute gain for the lower band.
286 LowerBandGain(low_noise_render, saturated_echo, nearend, echo, comfort_noise,
287 low_band_gain);
peah86afe9d2017-04-06 15:45:32 -0700288
peah1d680892017-05-23 04:07:10 -0700289 // Compute the gain for the upper bands.
290 *high_bands_gain = UpperBandsGain(saturated_echo, render, *low_band_gain);
291}
peah86afe9d2017-04-06 15:45:32 -0700292
peah1d680892017-05-23 04:07:10 -0700293// Detects when the render signal can be considered to have low power and
294// consist of stationary noise.
295bool SuppressionGain::LowNoiseRenderDetector::Detect(
296 const std::vector<std::vector<float>>& render) {
297 float x2_sum = 0.f;
298 float x2_max = 0.f;
299 for (auto x_k : render[0]) {
300 const float x2 = x_k * x_k;
301 x2_sum += x2;
302 x2_max = std::max(x2_max, x2);
peah522d71b2017-02-23 05:16:26 -0800303 }
peah1d680892017-05-23 04:07:10 -0700304
305 constexpr float kThreshold = 50.f * 50.f * 64.f;
306 const bool low_noise_render =
307 average_power_ < kThreshold && x2_max < 3 * average_power_;
308 average_power_ = average_power_ * 0.9f + x2_sum * 0.1f;
309 return low_noise_render;
peah522d71b2017-02-23 05:16:26 -0800310}
311
312} // namespace webrtc