OLD | NEW |
---|---|
(Empty) | |
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 | |
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> | |
20 | |
21 namespace webrtc { | |
22 namespace { | |
23 | |
24 constexpr int kNumIterations = 2; | |
25 constexpr float kEchoMaskingMargin = 1.f / 10.f; | |
26 constexpr float kBandMaskingFactor = 1.f / 3.f; | |
27 constexpr float kTimeMaskingFactor = 1.f / 10.f; | |
28 | |
29 } // namespace | |
30 | |
31 #if defined(WEBRTC_ARCH_X86_FAMILY) | |
32 | |
33 // Optimized SSE2 code for the gain computation. | |
34 // TODO(peah): Add further optimizations, in particular for the divisions. | |
35 void ComputeGains_SSE2( | |
36 const std::array<float, kFftLengthBy2Plus1>& nearend_power, | |
37 const std::array<float, kFftLengthBy2Plus1>& residual_echo_power, | |
38 const std::array<float, kFftLengthBy2Plus1>& comfort_noise_power, | |
39 float strong_nearend_margin, | |
40 std::array<float, kFftLengthBy2 - 1>* previous_gain_squared, | |
hlundin-webrtc
2017/02/21 16:34:03
You already have kFftLengthBy2Plus1 defined. Why n
peah-webrtc
2017/02/21 23:00:41
Nice!
Done.
| |
41 std::array<float, kFftLengthBy2 - 1>* previous_masker, | |
42 std::array<float, kFftLengthBy2Plus1>* gain) { | |
43 std::array<float, kFftLengthBy2 - 1> masker; | |
44 std::array<float, kFftLengthBy2 - 1> same_band_masker; | |
45 std::array<float, kFftLengthBy2 - 1> one_by_residual_echo_power; | |
46 std::array<bool, kFftLengthBy2 - 1> strong_nearend; | |
47 std::array<float, kFftLengthBy2Plus1> neighboring_bands_masker; | |
48 | |
49 // Precompute 1/residual_echo_power. | |
50 std::transform(residual_echo_power.begin() + 1, residual_echo_power.end() - 1, | |
51 one_by_residual_echo_power.begin(), | |
52 [](float a) { return a > 0.f ? 1.f / a : -1.f; }); | |
53 | |
54 // Precompute indicators for bands with strong nearend. | |
55 std::transform( | |
56 residual_echo_power.begin() + 1, residual_echo_power.end() - 1, | |
57 nearend_power.begin() + 1, strong_nearend.begin(), | |
58 [&](float a, float b) { return a <= strong_nearend_margin * b; }); | |
59 | |
60 // Precompute masker for the same band. | |
61 std::transform(comfort_noise_power.begin() + 1, comfort_noise_power.end() - 1, | |
62 previous_masker->begin(), same_band_masker.begin(), | |
63 [&](float a, float b) { return a + kTimeMaskingFactor * b; }); | |
64 | |
65 for (int k = 0; k < kNumIterations; ++k) { | |
66 if (k == 0) { | |
67 // Add masker from the same band. | |
68 std::copy(same_band_masker.begin(), same_band_masker.end(), | |
69 masker.begin()); | |
70 } else { | |
71 // Add masker for neightboring bands. | |
72 std::transform(nearend_power.begin(), nearend_power.end(), gain->begin(), | |
73 neighboring_bands_masker.begin(), | |
74 std::multiplies<float>()); | |
75 std::transform(neighboring_bands_masker.begin(), | |
76 neighboring_bands_masker.end(), | |
77 comfort_noise_power.begin(), | |
78 neighboring_bands_masker.begin(), std::plus<float>()); | |
79 std::transform( | |
80 neighboring_bands_masker.begin(), neighboring_bands_masker.end() - 2, | |
81 neighboring_bands_masker.begin() + 2, masker.begin(), | |
82 [&](float a, float b) { return kBandMaskingFactor * (a + b); }); | |
83 | |
84 // Add masker from the same band. | |
85 std::transform(same_band_masker.begin(), same_band_masker.end(), | |
86 masker.begin(), masker.begin(), std::plus<float>()); | |
87 } | |
88 | |
89 // Compute new gain as: | |
90 // G2(t,f) = (comfort_noise_power(t,f) + G2(t-1)*nearend_power(t-1)) * | |
91 // kTimeMaskingFactor | |
92 // * kEchoMaskingMargin / residual_echo_power(t,f). | |
93 // or | |
94 // G2(t,f) = ((comfort_noise_power(t,f) + G2(t-1) * | |
95 // nearend_power(t-1)) * kTimeMaskingFactor + | |
96 // (comfort_noise_power(t, f-1) + comfort_noise_power(t, f+1) + | |
97 // (G2(t,f-1)*nearend_power(t, f-1) + | |
98 // G2(t,f+1)*nearend_power(t, f+1)) * | |
99 // kTimeMaskingFactor) * kBandMaskingFactor) | |
100 // * kEchoMaskingMargin / residual_echo_power(t,f). | |
101 std::transform( | |
102 masker.begin(), masker.end(), one_by_residual_echo_power.begin(), | |
103 gain->begin() + 1, [&](float a, float b) { | |
104 return b >= 0 ? std::min(kEchoMaskingMargin * a * b, 1.f) : 1.f; | |
105 }); | |
106 | |
107 // Limit gain for bands with strong nearend. | |
108 std::transform(gain->begin() + 1, gain->end() - 1, strong_nearend.begin(), | |
109 gain->begin() + 1, | |
110 [](float a, bool b) { return b ? 1.f : a; }); | |
111 | |
112 // Limit the allowed gain update over time. | |
113 std::transform( | |
114 gain->begin() + 1, gain->end() - 1, previous_gain_squared->begin(), | |
115 gain->begin() + 1, [](float a, float b) { | |
116 return b < 0.0001f ? std::min(a, 0.0001f) : std::min(a, b * 2.f); | |
117 }); | |
118 | |
119 (*gain)[0] = (*gain)[1]; | |
120 (*gain)[kFftLengthBy2] = (*gain)[kFftLengthBy2 - 1]; | |
121 } | |
122 | |
123 std::copy(gain->begin() + 1, gain->end() - 1, previous_gain_squared->begin()); | |
124 | |
125 std::transform(gain->begin() + 1, gain->end() - 1, nearend_power.begin() + 1, | |
126 previous_masker->begin(), std::multiplies<float>()); | |
127 std::transform(previous_masker->begin(), previous_masker->end(), | |
128 comfort_noise_power.begin() + 1, previous_masker->begin(), | |
129 std::plus<float>()); | |
130 | |
131 for (size_t k = 0; k < kFftLengthBy2; k += 4) { | |
132 __m128 g = _mm_loadu_ps(&(*gain)[k]); | |
133 g = _mm_sqrt_ps(g); | |
134 _mm_storeu_ps(&(*gain)[k], g); | |
135 } | |
136 | |
137 (*gain)[kFftLengthBy2] = sqrtf((*gain)[kFftLengthBy2]); | |
138 } | |
139 | |
140 #endif | |
141 | |
142 void ComputeGains( | |
143 const std::array<float, kFftLengthBy2Plus1>& nearend_power, | |
144 const std::array<float, kFftLengthBy2Plus1>& residual_echo_power, | |
145 const std::array<float, kFftLengthBy2Plus1>& comfort_noise_power, | |
146 float strong_nearend_margin, | |
147 std::array<float, kFftLengthBy2 - 1>* previous_gain_squared, | |
148 std::array<float, kFftLengthBy2 - 1>* previous_masker, | |
149 std::array<float, kFftLengthBy2Plus1>* gain) { | |
150 std::array<float, kFftLengthBy2 - 1> masker; | |
151 std::array<float, kFftLengthBy2 - 1> same_band_masker; | |
152 std::array<float, kFftLengthBy2 - 1> one_by_residual_echo_power; | |
153 std::array<bool, kFftLengthBy2 - 1> strong_nearend; | |
154 std::array<float, kFftLengthBy2Plus1> neighboring_bands_masker; | |
155 | |
156 // Precompute 1/residual_echo_power. | |
157 std::transform(residual_echo_power.begin() + 1, residual_echo_power.end() - 1, | |
158 one_by_residual_echo_power.begin(), | |
159 [](float a) { return a > 0.f ? 1.f / a : -1.f; }); | |
hlundin-webrtc
2017/02/21 16:34:03
Is there any risk that we get _really_ large resul
peah-webrtc
2017/02/21 23:00:41
Yes, the division should actually be done as it is
| |
160 | |
161 // Precompute indicators for bands with strong nearend. | |
162 std::transform( | |
163 residual_echo_power.begin() + 1, residual_echo_power.end() - 1, | |
164 nearend_power.begin() + 1, strong_nearend.begin(), | |
165 [&](float a, float b) { return a <= strong_nearend_margin * b; }); | |
166 | |
167 // Precompute masker for the same band. | |
168 std::transform(comfort_noise_power.begin() + 1, comfort_noise_power.end() - 1, | |
169 previous_masker->begin(), same_band_masker.begin(), | |
170 [&](float a, float b) { return a + kTimeMaskingFactor * b; }); | |
171 | |
172 for (int k = 0; k < kNumIterations; ++k) { | |
173 if (k == 0) { | |
174 // Add masker from the same band. | |
175 std::copy(same_band_masker.begin(), same_band_masker.end(), | |
176 masker.begin()); | |
177 } else { | |
178 // Add masker for neightboring bands. | |
hlundin-webrtc
2017/02/21 16:34:03
neighboring
peah-webrtc
2017/02/21 23:00:41
Done.
| |
179 std::transform(nearend_power.begin(), nearend_power.end(), gain->begin(), | |
180 neighboring_bands_masker.begin(), | |
181 std::multiplies<float>()); | |
182 std::transform(neighboring_bands_masker.begin(), | |
183 neighboring_bands_masker.end(), | |
184 comfort_noise_power.begin(), | |
185 neighboring_bands_masker.begin(), std::plus<float>()); | |
186 std::transform( | |
187 neighboring_bands_masker.begin(), neighboring_bands_masker.end() - 2, | |
188 neighboring_bands_masker.begin() + 2, masker.begin(), | |
189 [&](float a, float b) { return kBandMaskingFactor * (a + b); }); | |
190 | |
191 // Add masker from the same band. | |
192 std::transform(same_band_masker.begin(), same_band_masker.end(), | |
193 masker.begin(), masker.begin(), std::plus<float>()); | |
194 } | |
195 | |
196 // Compute new gain as: | |
197 // G2(t,f) = (comfort_noise_power(t,f) + G2(t-1)*nearend_power(t-1)) * | |
198 // kTimeMaskingFactor | |
199 // * kEchoMaskingMargin / residual_echo_power(t,f). | |
200 // or | |
201 // G2(t,f) = ((comfort_noise_power(t,f) + G2(t-1) * | |
202 // nearend_power(t-1)) * kTimeMaskingFactor + | |
203 // (comfort_noise_power(t, f-1) + comfort_noise_power(t, f+1) + | |
204 // (G2(t,f-1)*nearend_power(t, f-1) + | |
205 // G2(t,f+1)*nearend_power(t, f+1)) * | |
206 // kTimeMaskingFactor) * kBandMaskingFactor) | |
207 // * kEchoMaskingMargin / residual_echo_power(t,f). | |
208 std::transform( | |
209 masker.begin(), masker.end(), one_by_residual_echo_power.begin(), | |
210 gain->begin() + 1, [&](float a, float b) { | |
211 return b >= 0 ? std::min(kEchoMaskingMargin * a * b, 1.f) : 1.f; | |
212 }); | |
213 | |
214 // Limit gain for bands with strong nearend. | |
215 std::transform(gain->begin() + 1, gain->end() - 1, strong_nearend.begin(), | |
216 gain->begin() + 1, | |
217 [](float a, bool b) { return b ? 1.f : a; }); | |
218 | |
219 // Limit the allowed gain update over time. | |
220 std::transform( | |
221 gain->begin() + 1, gain->end() - 1, previous_gain_squared->begin(), | |
222 gain->begin() + 1, [](float a, float b) { | |
223 return b < 0.0001f ? std::min(a, 0.0001f) : std::min(a, b * 2.f); | |
224 }); | |
225 | |
226 (*gain)[0] = (*gain)[1]; | |
227 (*gain)[kFftLengthBy2] = (*gain)[kFftLengthBy2 - 1]; | |
228 } | |
229 | |
230 std::copy(gain->begin() + 1, gain->end() - 1, previous_gain_squared->begin()); | |
hlundin-webrtc
2017/02/21 16:34:03
It's a bit confusing that you copy to "previous_ga
peah-webrtc
2017/02/21 23:00:41
I simplified this with an alias.
PTAL.
hlundin-webrtc
2017/02/22 15:24:05
Better.
peah-webrtc
2017/02/22 23:51:36
Acknowledged.
| |
231 | |
232 std::transform(gain->begin() + 1, gain->end() - 1, nearend_power.begin() + 1, | |
233 previous_masker->begin(), std::multiplies<float>()); | |
234 std::transform(previous_masker->begin(), previous_masker->end(), | |
235 comfort_noise_power.begin() + 1, previous_masker->begin(), | |
236 std::plus<float>()); | |
237 | |
238 std::transform(gain->begin(), gain->end(), gain->begin(), | |
239 [](float a) { return sqrtf(a); }); | |
240 } | |
241 | |
242 SuppressionGain::SuppressionGain(Aec3Optimization optimization) | |
243 : optimization_(optimization) { | |
244 previous_gain_squared_.fill(1.f); | |
245 previous_masker_.fill(0.f); | |
246 } | |
247 | |
248 void SuppressionGain::GetGain( | |
249 const std::array<float, kFftLengthBy2Plus1>& nearend_power, | |
250 const std::array<float, kFftLengthBy2Plus1>& residual_echo_power, | |
251 const std::array<float, kFftLengthBy2Plus1>& comfort_noise_power, | |
252 float strong_nearend_margin, | |
253 std::array<float, kFftLengthBy2Plus1>* gain) { | |
254 RTC_DCHECK(gain); | |
255 if (optimization_ == Aec3Optimization::kNone) { | |
hlundin-webrtc
2017/02/21 16:34:03
I think you should use switch instead:
switch (opt
peah-webrtc
2017/02/21 23:00:41
Great one!
I also changed to this in the other pla
hlundin-webrtc
2017/02/22 15:24:05
That was the way I hoped it would pan out. :)
peah-webrtc
2017/02/23 11:18:32
:-)
Acknowledged.
| |
256 ComputeGains(nearend_power, residual_echo_power, comfort_noise_power, | |
257 strong_nearend_margin, &previous_gain_squared_, | |
258 &previous_masker_, gain); | |
259 } | |
260 #if defined(WEBRTC_ARCH_X86_FAMILY) | |
261 if (optimization_ == Aec3Optimization::kSse2) { | |
262 ComputeGains_SSE2(nearend_power, residual_echo_power, comfort_noise_power, | |
263 strong_nearend_margin, &previous_gain_squared_, | |
264 &previous_masker_, gain); | |
265 } | |
266 #endif | |
267 } | |
268 | |
269 } // namespace webrtc | |
OLD | NEW |