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/residual_echo_estimator.h" | |
12 | |
13 #include <math.h> | |
14 #include <vector> | |
15 | |
16 #include "webrtc/base/checks.h" | |
17 | |
18 namespace webrtc { | |
19 namespace { | |
20 | |
21 constexpr float kSaturationLeakageFactor = 10.f; | |
22 constexpr size_t kSaturationLeakageBlocks = 10; | |
23 | |
24 // Estimates the residual echo power when there is no detection correlation | |
25 // between the render and capture signals. | |
26 void InfiniteErlPowerEstimate( | |
27 size_t active_render_counter, | |
28 size_t blocks_since_last_saturation, | |
29 const std::array<float, kFftLengthBy2Plus1>& S2_fallback, | |
30 std::array<float, kFftLengthBy2Plus1>* R2) { | |
31 if (active_render_counter > 5 * 250) { | |
32 // After an amount of active render sumples for which an echo should have | |
hlundin-webrtc
2017/02/21 16:34:02
sumples -> samples
peah-webrtc
2017/02/21 23:00:40
Done.
| |
33 // been detected in the capture signal if the ERL was not infinite, set the | |
34 // residual echo to 0. | |
35 R2->fill(0.f); | |
36 } else { | |
37 // Before certainty has been reached about the presence of echo, use the | |
38 // fallback echo power estimate as the residual echo estimate. Add a leakage | |
39 // factor when there is saturation. | |
40 std::copy(S2_fallback.begin(), S2_fallback.end(), R2->begin()); | |
41 if (blocks_since_last_saturation < kSaturationLeakageBlocks) { | |
42 std::for_each(R2->begin(), R2->end(), | |
43 [](float& a) { a *= kSaturationLeakageFactor; }); | |
44 } | |
45 } | |
46 } | |
47 | |
48 // Estimates the echo power in an half-duplex manner. | |
49 void HalfDuplecPowerEstimate(bool active_render, | |
hlundin-webrtc
2017/02/21 16:34:02
Duplec -> Duplex
peah-webrtc
2017/02/21 23:00:40
Done.
| |
50 const std::array<float, kFftLengthBy2Plus1>& Y2, | |
51 std::array<float, kFftLengthBy2Plus1>* R2) { | |
52 // Set the residual echo power to the power of the capture signal. | |
53 if (active_render) { | |
54 std::copy(Y2.begin(), Y2.end(), R2->begin()); | |
55 } else { | |
56 R2->fill(0.f); | |
57 } | |
58 } | |
59 | |
60 // Estimates the residual echo power based on gains. | |
61 void GainBasedPowerEstimate( | |
62 size_t external_delay, | |
63 const FftBuffer& X_buffer, | |
64 size_t blocks_since_last_saturation, | |
65 const std::array<bool, kFftLengthBy2Plus1>& bands_with_reliable_filter, | |
66 const std::array<float, kFftLengthBy2Plus1>& echo_path_gain, | |
67 const std::array<float, kFftLengthBy2Plus1>& S2_fallback, | |
68 std::array<float, kFftLengthBy2Plus1>* R2) { | |
69 const auto& X2 = X_buffer.Spectrum(external_delay); | |
70 | |
71 // Base the residual echo power on gain of the linear echo path estimate if | |
72 // that is reliable, otherwise use the fallback echo path estimate. Add a | |
73 // leakage factor when there is saturation. | |
74 for (size_t k = 0; k < R2->size(); ++k) { | |
75 (*R2)[k] = bands_with_reliable_filter[k] ? echo_path_gain[k] * X2[k] | |
76 : S2_fallback[k]; | |
77 } | |
78 if (blocks_since_last_saturation < kSaturationLeakageBlocks) { | |
79 std::for_each(R2->begin(), R2->end(), | |
80 [](float& a) { a *= kSaturationLeakageFactor; }); | |
81 } | |
82 } | |
83 | |
84 // Estimates the residual echo power based on the linear echo path. | |
85 void ErleBasedPowerEstimate( | |
86 bool headset_detected, | |
87 const FftBuffer& X_buffer, | |
88 bool using_subtractor_output, | |
89 size_t linear_filter_based_delay, | |
90 size_t blocks_since_last_saturation, | |
91 bool poorly_aligned_filter, | |
92 const std::array<bool, kFftLengthBy2Plus1>& bands_with_reliable_filter, | |
93 const std::array<float, kFftLengthBy2Plus1>& echo_path_gain, | |
94 const std::array<float, kFftLengthBy2Plus1>& S2_fallback, | |
95 const std::array<float, kFftLengthBy2Plus1>& S2_linear, | |
96 const std::array<float, kFftLengthBy2Plus1>& Y2, | |
97 const std::array<float, kFftLengthBy2Plus1>& erle, | |
98 const std::array<float, kFftLengthBy2Plus1>& erl, | |
99 std::array<float, kFftLengthBy2Plus1>* R2) { | |
100 // Residual echo power after saturation. | |
101 if (blocks_since_last_saturation < kSaturationLeakageBlocks) { | |
102 for (size_t k = 0; k < R2->size(); ++k) { | |
103 (*R2)[k] = kSaturationLeakageFactor * | |
104 (bands_with_reliable_filter[k] && using_subtractor_output | |
105 ? S2_linear[k] | |
106 : std::min(S2_fallback[k], Y2[k])); | |
107 } | |
108 return; | |
109 } | |
110 | |
111 // Residual echo power when a headset is used. | |
112 if (headset_detected) { | |
113 const auto& X2 = X_buffer.Spectrum(linear_filter_based_delay); | |
114 for (size_t k = 0; k < R2->size(); ++k) { | |
115 RTC_DCHECK_LT(0.f, erle[k]); | |
116 (*R2)[k] = bands_with_reliable_filter[k] && using_subtractor_output | |
117 ? S2_linear[k] / erle[k] | |
118 : std::min(S2_fallback[k], Y2[k]); | |
119 (*R2)[k] = std::min((*R2)[k], X2[k] * erl[k]); | |
120 } | |
121 return; | |
122 } | |
123 | |
124 // Residual echo power when the adaptive filter is poorly aligned. | |
125 if (poorly_aligned_filter) { | |
126 for (size_t k = 0; k < R2->size(); ++k) { | |
127 (*R2)[k] = bands_with_reliable_filter[k] && using_subtractor_output | |
128 ? S2_linear[k] | |
129 : std::min(S2_fallback[k], Y2[k]); | |
130 } | |
131 return; | |
132 } | |
133 | |
134 // Residual echo power when there is no recent saturation, no headset detected | |
135 // and when the adaptive filter is well aligned. | |
136 for (size_t k = 0; k < R2->size(); ++k) { | |
137 RTC_DCHECK_LT(0.f, erle[k]); | |
138 (*R2)[k] = bands_with_reliable_filter[k] && using_subtractor_output | |
139 ? S2_linear[k] / erle[k] | |
140 : std::min(S2_fallback[k], Y2[k]); | |
141 } | |
142 } | |
143 | |
144 } // namespace | |
145 | |
146 ResidualEchoEstimator::ResidualEchoEstimator() { | |
147 echo_path_gain_.fill(0.f); | |
148 } | |
149 | |
150 ResidualEchoEstimator::~ResidualEchoEstimator() = default; | |
151 | |
152 void ResidualEchoEstimator::Estimate( | |
153 bool using_subtractor_output, | |
154 const AecState& aec_state, | |
155 const FftBuffer& X_buffer, | |
156 const std::vector<std::array<float, kFftLengthBy2Plus1>>& H2, | |
157 const std::array<float, kFftLengthBy2Plus1>& E2_main, | |
158 const std::array<float, kFftLengthBy2Plus1>& E2_shadow, | |
159 const std::array<float, kFftLengthBy2Plus1>& S2_linear, | |
160 const std::array<float, kFftLengthBy2Plus1>& S2_fallback, | |
161 const std::array<float, kFftLengthBy2Plus1>& Y2, | |
162 std::array<float, kFftLengthBy2Plus1>* R2) { | |
163 RTC_DCHECK(R2); | |
164 const rtc::Optional<size_t>& linear_filter_based_delay = | |
165 aec_state.FilterDelay(); | |
166 | |
167 // Update the echo path gain. | |
168 if (linear_filter_based_delay) { | |
169 std::copy(H2[*linear_filter_based_delay].begin(), | |
170 H2[*linear_filter_based_delay].end(), echo_path_gain_.begin()); | |
171 } | |
172 | |
173 // Counte the blocks since saturation. | |
hlundin-webrtc
2017/02/21 16:34:02
Counte?
peah-webrtc
2017/02/21 23:00:40
Done.
| |
174 if (aec_state.SaturatedCapture()) { | |
175 blocks_since_last_saturation_ = 0; | |
176 } else { | |
177 ++blocks_since_last_saturation_; | |
178 } | |
179 | |
180 // Count the upon each other following blocks after saturation. | |
hlundin-webrtc
2017/02/21 16:34:02
Read this comment one more time.
peah-webrtc
2017/02/21 23:00:40
Done.
| |
181 if (aec_state.ActiveRender()) { | |
182 ++active_render_counter_; | |
183 } | |
184 | |
185 const auto& bands_with_reliable_filter = aec_state.BandsWithReliableFilter(); | |
186 | |
187 if (aec_state.UsableLinearEstimate()) { | |
188 // Residual echo power estimation when the adaptive filter is reliable. | |
189 RTC_DCHECK(linear_filter_based_delay); | |
190 ErleBasedPowerEstimate( | |
191 aec_state.HeadsetDetected(), X_buffer, using_subtractor_output, | |
192 *linear_filter_based_delay, blocks_since_last_saturation_, | |
193 aec_state.PoorlyAlignedFilter(), bands_with_reliable_filter, | |
194 echo_path_gain_, S2_fallback, S2_linear, Y2, aec_state.Erle(), | |
195 aec_state.Erl(), R2); | |
196 } else if (aec_state.ModelBasedAecFeasible()) { | |
197 // Residual echo power when the adaptive filter is not reliable but still an | |
198 // external echo path delay is provided (and hence can be estimated). | |
199 RTC_DCHECK(aec_state.ExternalDelay()); | |
200 GainBasedPowerEstimate( | |
201 *aec_state.ExternalDelay(), X_buffer, blocks_since_last_saturation_, | |
202 bands_with_reliable_filter, echo_path_gain_, S2_fallback, R2); | |
203 } else if (aec_state.EchoLeakageDetected()) { | |
204 // Residual echo power when an external residual echo detection algorithm | |
205 // has deemed the echo canceller to leak echoes. | |
206 HalfDuplecPowerEstimate(aec_state.ActiveRender(), Y2, R2); | |
207 } else { | |
208 // Residual echo power when none of the other cases are fulfilled. | |
209 InfiniteErlPowerEstimate(active_render_counter_, | |
210 blocks_since_last_saturation_, S2_fallback, R2); | |
211 } | |
212 } | |
213 | |
214 } // namespace webrtc | |
OLD | NEW |