OLD | NEW |
---|---|
1 /* | 1 /* |
2 * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. | 2 * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. |
3 * | 3 * |
4 * Use of this source code is governed by a BSD-style license | 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 | 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 | 6 * tree. An additional intellectual property rights grant can be found |
7 * in the file PATENTS. All contributing project authors may | 7 * in the file PATENTS. All contributing project authors may |
8 * be found in the AUTHORS file in the root of the source tree. | 8 * be found in the AUTHORS file in the root of the source tree. |
9 */ | 9 */ |
10 #include "webrtc/modules/audio_processing/aec3/echo_remover.h" | 10 #include "webrtc/modules/audio_processing/aec3/echo_remover.h" |
11 | 11 |
12 #include <algorithm> | 12 #include <algorithm> |
13 #include <vector> | 13 #include <memory> |
14 | 14 #include <numeric> |
15 #include <string> | |
16 | |
17 #include "webrtc/base/array_view.h" | |
18 #include "webrtc/base/atomicops.h" | |
15 #include "webrtc/base/constructormagic.h" | 19 #include "webrtc/base/constructormagic.h" |
16 #include "webrtc/base/checks.h" | 20 #include "webrtc/modules/audio_processing/aec3/aec3_common.h" |
17 #include "webrtc/base/optional.h" | 21 #include "webrtc/modules/audio_processing/aec3/aec_state.h" |
18 #include "webrtc/modules/audio_processing/aec3/aec3_constants.h" | 22 #include "webrtc/modules/audio_processing/aec3/fft_data.h" |
23 #include "webrtc/modules/audio_processing/aec3/echo_path_variability.h" | |
24 #include "webrtc/modules/audio_processing/aec3/comfort_noise_generator.h" | |
ivoc
2017/02/22 17:08:12
Order of includes
peah-webrtc
2017/02/22 23:51:37
Done.
| |
25 #include "webrtc/modules/audio_processing/aec3/power_echo_model.h" | |
26 #include "webrtc/modules/audio_processing/aec3/output_selector.h" | |
27 #include "webrtc/modules/audio_processing/aec3/fft_buffer.h" | |
28 #include "webrtc/modules/audio_processing/aec3/render_delay_buffer.h" | |
29 #include "webrtc/modules/audio_processing/aec3/residual_echo_estimator.h" | |
30 #include "webrtc/modules/audio_processing/aec3/subtractor.h" | |
31 #include "webrtc/modules/audio_processing/aec3/suppression_filter.h" | |
32 #include "webrtc/modules/audio_processing/aec3/suppression_gain.h" | |
33 #include "webrtc/modules/audio_processing/logging/apm_data_dumper.h" | |
19 | 34 |
20 namespace webrtc { | 35 namespace webrtc { |
21 | 36 |
22 namespace { | 37 namespace { |
38 | |
39 void LinearEchoPower(const FftData& E, | |
40 const FftData& Y, | |
41 std::array<float, kFftLengthBy2Plus1>* S2) { | |
42 for (size_t k = 0; k < E.re.size(); ++k) { | |
43 (*S2)[k] = (Y.re[k] - E.re[k]) * (Y.re[k] - E.re[k]) + | |
44 (Y.im[k] - E.im[k]) * (Y.im[k] - E.im[k]); | |
45 } | |
46 } | |
47 | |
48 float BlockPower(const std::array<float, kBlockSize> x) { | |
49 return std::accumulate(x.begin(), x.end(), 0.f, | |
50 [](float a, float b) -> float { return a + b * b; }); | |
51 } | |
52 | |
53 // Class for removing the echo from the capture signal. | |
23 class EchoRemoverImpl final : public EchoRemover { | 54 class EchoRemoverImpl final : public EchoRemover { |
24 public: | 55 public: |
25 explicit EchoRemoverImpl(int sample_rate_hz); | 56 explicit EchoRemoverImpl(int sample_rate_hz); |
26 ~EchoRemoverImpl() override; | 57 ~EchoRemoverImpl() override; |
27 | 58 |
28 void ProcessBlock(const rtc::Optional<size_t>& echo_path_delay_samples, | 59 // Removes the echo from a block of samples from the capture signal. The |
29 const EchoPathVariability& echo_path_variability, | 60 // supplied render signal is assumed to be pre-aligned with the capture |
30 bool capture_signal_saturation, | 61 // signal. |
31 const std::vector<std::vector<float>>& render, | 62 void ProcessBlock( |
32 std::vector<std::vector<float>>* capture) override; | 63 const rtc::Optional<size_t>& external_echo_path_delay_estimate, |
33 | 64 const EchoPathVariability& echo_path_variability, |
34 void UpdateEchoLeakageStatus(bool leakage_detected) override; | 65 bool capture_signal_saturation, |
66 const std::vector<std::vector<float>>& render, | |
67 std::vector<std::vector<float>>* capture) override; | |
68 | |
69 // Updates the status on whether echo leakage is detected in the output of the | |
70 // echo remover. | |
71 void UpdateEchoLeakageStatus(bool leakage_detected) override { | |
72 echo_leakage_detected_ = leakage_detected; | |
73 } | |
35 | 74 |
36 private: | 75 private: |
76 static int instance_count_; | |
77 const Aec3Fft fft_; | |
78 std::unique_ptr<ApmDataDumper> data_dumper_; | |
79 const Aec3Optimization optimization_; | |
37 const int sample_rate_hz_; | 80 const int sample_rate_hz_; |
81 Subtractor subtractor_; | |
82 SuppressionGain suppression_gain_; | |
83 ComfortNoiseGenerator cng_; | |
84 SuppressionFilter suppression_filter_; | |
85 PowerEchoModel power_echo_model_; | |
86 FftBuffer X_buffer_; | |
87 RenderSignalAnalyzer render_signal_analyzer_; | |
88 OutputSelector output_selector_; | |
89 ResidualEchoEstimator residual_echo_estimator_; | |
90 bool echo_leakage_detected_ = false; | |
91 std::array<float, kBlockSize> x_old_; | |
92 AecState aec_state_; | |
38 | 93 |
39 RTC_DISALLOW_COPY_AND_ASSIGN(EchoRemoverImpl); | 94 RTC_DISALLOW_COPY_AND_ASSIGN(EchoRemoverImpl); |
40 }; | 95 }; |
41 | 96 |
42 // TODO(peah): Add functionality. | 97 int EchoRemoverImpl::instance_count_ = 0; |
98 | |
43 EchoRemoverImpl::EchoRemoverImpl(int sample_rate_hz) | 99 EchoRemoverImpl::EchoRemoverImpl(int sample_rate_hz) |
44 : sample_rate_hz_(sample_rate_hz) { | 100 : data_dumper_( |
45 RTC_DCHECK(ValidFullBandRate(sample_rate_hz_)); | 101 new ApmDataDumper(rtc::AtomicOps::Increment(&instance_count_))), |
102 optimization_(DetectOptimization()), | |
103 sample_rate_hz_(sample_rate_hz), | |
104 subtractor_(data_dumper_.get(), optimization_), | |
105 suppression_gain_(optimization_), | |
106 cng_(optimization_), | |
107 suppression_filter_(sample_rate_hz_), | |
108 X_buffer_(optimization_, | |
109 std::max(subtractor_.MinFarendBufferLength(), | |
110 power_echo_model_.MinFarendBufferLength()), | |
111 subtractor_.NumBlocksInRenderSums()) { | |
112 RTC_DCHECK(ValidFullBandRate(sample_rate_hz)); | |
113 x_old_.fill(0.f); | |
46 } | 114 } |
47 | 115 |
48 EchoRemoverImpl::~EchoRemoverImpl() = default; | 116 EchoRemoverImpl::~EchoRemoverImpl() = default; |
49 | 117 |
50 // TODO(peah): Add functionality. | |
51 void EchoRemoverImpl::ProcessBlock( | 118 void EchoRemoverImpl::ProcessBlock( |
52 const rtc::Optional<size_t>& echo_path_delay_samples, | 119 const rtc::Optional<size_t>& echo_path_delay_samples, |
53 const EchoPathVariability& echo_path_variability, | 120 const EchoPathVariability& echo_path_variability, |
54 bool capture_signal_saturation, | 121 bool capture_signal_saturation, |
55 const std::vector<std::vector<float>>& render, | 122 const std::vector<std::vector<float>>& render, |
56 std::vector<std::vector<float>>* capture) { | 123 std::vector<std::vector<float>>* capture) { |
57 RTC_DCHECK(capture); | 124 const std::vector<std::vector<float>>& x = render; |
58 RTC_DCHECK_EQ(render.size(), NumBandsForRate(sample_rate_hz_)); | 125 std::vector<std::vector<float>>* y = capture; |
59 RTC_DCHECK_EQ(capture->size(), NumBandsForRate(sample_rate_hz_)); | 126 |
60 RTC_DCHECK_EQ(render[0].size(), kBlockSize); | 127 RTC_DCHECK(y); |
61 RTC_DCHECK_EQ((*capture)[0].size(), kBlockSize); | 128 RTC_DCHECK_EQ(x.size(), NumBandsForRate(sample_rate_hz_)); |
62 } | 129 RTC_DCHECK_EQ(y->size(), NumBandsForRate(sample_rate_hz_)); |
63 | 130 RTC_DCHECK_EQ(x[0].size(), kBlockSize); |
64 // TODO(peah): Add functionality. | 131 RTC_DCHECK_EQ((*y)[0].size(), kBlockSize); |
65 void EchoRemoverImpl::UpdateEchoLeakageStatus(bool leakage_detected) {} | 132 const std::vector<float>& x0 = x[0]; |
133 std::vector<float>& y0 = (*y)[0]; | |
134 | |
135 data_dumper_->DumpWav("aec3_processblock_capture_input", kBlockSize, &y0[0], | |
ivoc
2017/02/22 17:08:12
Is this debug code?
peah-webrtc
2017/02/22 23:51:37
Yes, it is. The dumping of this data is only done
| |
136 LowestBandRate(sample_rate_hz_), 1); | |
137 data_dumper_->DumpWav("aec3_processblock_render_input", kBlockSize, &x0[0], | |
138 LowestBandRate(sample_rate_hz_), 1); | |
139 | |
140 aec_state_.UpdateCaptureSaturation(capture_signal_saturation); | |
141 | |
142 if (echo_path_variability.AudioPathChanged()) { | |
143 subtractor_.HandleEchoPathChange(echo_path_variability); | |
144 power_echo_model_.HandleEchoPathChange(echo_path_variability); | |
145 } | |
146 | |
147 std::array<float, kFftLengthBy2Plus1> Y2; | |
148 std::array<float, kFftLengthBy2Plus1> S2_power; | |
149 std::array<float, kFftLengthBy2Plus1> R2; | |
150 std::array<float, kFftLengthBy2Plus1> S2_linear; | |
151 std::array<float, kFftLengthBy2Plus1> G; | |
152 FftData X; | |
153 FftData Y; | |
154 FftData comfort_noise; | |
155 FftData high_band_comfort_noise; | |
156 SubtractorOutput subtractor_output; | |
157 FftData& E_main = subtractor_output.E_main; | |
158 auto& E2_main = subtractor_output.E2_main; | |
159 auto& E2_shadow = subtractor_output.E2_shadow; | |
160 auto& e_main = subtractor_output.e_main; | |
161 auto& e_shadow = subtractor_output.e_shadow; | |
162 | |
163 // Update the render signal buffer. | |
164 fft_.PaddedFft(x0, x_old_, &X); | |
165 X_buffer_.Insert(X); | |
166 | |
167 // Analyze the render signal. | |
168 render_signal_analyzer_.Update(X_buffer_, aec_state_.FilterDelay()); | |
169 | |
170 // Perform linear echo cancellation. | |
171 subtractor_.Process(X_buffer_, y0, render_signal_analyzer_, | |
172 aec_state_.SaturatedCapture(), &subtractor_output); | |
173 | |
174 // Compute spectra. | |
175 fft_.ZeroPaddedFft(y0, &Y); | |
176 LinearEchoPower(E_main, Y, &S2_linear); | |
177 Y.Spectrum(optimization_, &Y2); | |
178 | |
179 // Update the AEC state information. | |
180 aec_state_.Update(subtractor_.FilterFrequencyResponse(), | |
181 echo_path_delay_samples, X_buffer_, E2_main, E2_shadow, Y2, | |
182 x0, echo_path_variability, echo_leakage_detected_); | |
183 | |
184 // Use the power model to estimate the echo. | |
185 power_echo_model_.EstimateEcho(X_buffer_, Y2, aec_state_, &S2_power); | |
186 | |
187 // Choose the linear output. | |
188 output_selector_.FormLinearOutput(e_main, y0); | |
189 data_dumper_->DumpWav("aec3_output_linear", kBlockSize, &y0[0], | |
190 LowestBandRate(sample_rate_hz_), 1); | |
191 const auto& E2 = output_selector_.UseSubtractorOutput() ? E2_main : Y2; | |
192 | |
193 // Estimate the residual echo power. | |
194 residual_echo_estimator_.Estimate( | |
195 output_selector_.UseSubtractorOutput(), aec_state_, X_buffer_, | |
196 subtractor_.FilterFrequencyResponse(), E2_main, E2_shadow, S2_linear, | |
197 S2_power, Y2, &R2); | |
198 | |
199 // Estimate the comfort noise. | |
200 cng_.Compute(aec_state_, Y2, &comfort_noise, &high_band_comfort_noise); | |
201 | |
202 // Detect basic doubletalk. | |
203 const bool doubletalk = BlockPower(e_shadow) < BlockPower(e_main); | |
204 | |
205 // A choose and apply echo suppression gain. | |
206 suppression_gain_.GetGain(E2, R2, cng_.NoiseSpectrum(), | |
207 doubletalk ? 0.001f : 0.0001f, &G); | |
208 suppression_filter_.ApplyGain(comfort_noise, high_band_comfort_noise, G, y); | |
aleloi
2017/02/22 16:20:31
Is 'high_band_comfort_noise' needed at lower sampl
peah-webrtc
2017/02/22 23:51:37
You are correct, it is only needed for rates inclu
aleloi
2017/02/23 10:56:45
Acknowledged.
peah-webrtc
2017/02/23 11:18:32
Acknowledged.
| |
209 | |
210 // Debug outputs for the purpose of development and analysis. | |
211 data_dumper_->DumpRaw("aec3_N2", cng_.NoiseSpectrum()); | |
212 data_dumper_->DumpRaw("aec3_suppressor_gain", G); | |
213 data_dumper_->DumpWav("aec3_output", | |
214 rtc::ArrayView<const float>(&y0[0], kBlockSize), | |
215 LowestBandRate(sample_rate_hz_), 1); | |
216 data_dumper_->DumpRaw("aec3_using_subtractor_output", | |
217 output_selector_.UseSubtractorOutput() ? 1 : 0); | |
218 data_dumper_->DumpRaw("aec3_doubletalk", doubletalk ? 1 : 0); | |
219 data_dumper_->DumpRaw("aec3_E2", E2); | |
220 data_dumper_->DumpRaw("aec3_E2_main", E2_main); | |
221 data_dumper_->DumpRaw("aec3_E2_shadow", E2_shadow); | |
222 data_dumper_->DumpRaw("aec3_S2_linear", S2_linear); | |
223 data_dumper_->DumpRaw("aec3_S2_power", S2_power); | |
224 data_dumper_->DumpRaw("aec3_Y2", Y2); | |
225 data_dumper_->DumpRaw("aec3_R2", R2); | |
226 data_dumper_->DumpRaw("aec3_erle", aec_state_.Erle()); | |
227 data_dumper_->DumpRaw("aec3_erl", aec_state_.Erl()); | |
228 data_dumper_->DumpRaw("aec3_reliable_filter_bands", | |
229 aec_state_.BandsWithReliableFilter()); | |
230 data_dumper_->DumpRaw("aec3_active_render", aec_state_.ActiveRender()); | |
231 data_dumper_->DumpRaw("aec3_model_based_aec_feasible", | |
232 aec_state_.ModelBasedAecFeasible()); | |
233 data_dumper_->DumpRaw("aec3_usable_linear_estimate", | |
234 aec_state_.UsableLinearEstimate()); | |
235 data_dumper_->DumpRaw("aec3_filter_delay", aec_state_.FilterDelay() | |
236 ? *aec_state_.FilterDelay() | |
237 : -1); | |
238 data_dumper_->DumpRaw("aec3_external_delay", aec_state_.ExternalDelay() | |
239 ? *aec_state_.ExternalDelay() | |
240 : -1); | |
241 data_dumper_->DumpRaw("aec3_capture_saturation", | |
242 aec_state_.SaturatedCapture() ? 1 : 0); | |
243 } | |
66 | 244 |
67 } // namespace | 245 } // namespace |
68 | 246 |
69 EchoRemover* EchoRemover::Create(int sample_rate_hz) { | 247 EchoRemover* EchoRemover::Create(int sample_rate_hz) { |
70 return new EchoRemoverImpl(sample_rate_hz); | 248 return new EchoRemoverImpl(sample_rate_hz); |
71 } | 249 } |
72 | 250 |
73 } // namespace webrtc | 251 } // namespace webrtc |
OLD | NEW |