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 <numeric> |
14 | 14 #include <string> |
15 | |
16 #include "webrtc/base/atomicops.h" | |
15 #include "webrtc/base/constructormagic.h" | 17 #include "webrtc/base/constructormagic.h" |
16 #include "webrtc/base/checks.h" | 18 #include "webrtc/base/array_view.h" |
aleloi
2017/02/13 16:44:50
Include order. Also, array_view is not used.
peah-webrtc
2017/02/20 07:37:16
Reordered the includes. I did, however, keep the i
| |
17 #include "webrtc/base/optional.h" | |
18 #include "webrtc/modules/audio_processing/aec3/aec3_constants.h" | 19 #include "webrtc/modules/audio_processing/aec3/aec3_constants.h" |
20 #include "webrtc/modules/audio_processing/aec3/aec_state.h" | |
21 #include "webrtc/modules/audio_processing/aec3/fft_data.h" | |
22 #include "webrtc/modules/audio_processing/aec3/echo_path_variability.h" | |
23 #include "webrtc/modules/audio_processing/aec3/comfort_noise_generator.h" | |
24 #include "webrtc/modules/audio_processing/aec3/delay_handler.h" | |
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/audio_buffer.h" | |
34 #include "webrtc/modules/audio_processing/include/audio_processing.h" | |
aleloi
2017/02/13 16:44:50
Is audio_buffer and audio_processing used?
peah-webrtc
2017/02/20 07:37:16
Done.
| |
35 #include "webrtc/modules/audio_processing/logging/apm_data_dumper.h" | |
aleloi
2017/02/13 16:44:50
Include order
peah-webrtc
2017/02/20 07:37:16
But this one is correctly ordered, right?
aleloi
2017/02/23 10:56:44
Acknowledged.
| |
19 | 36 |
20 namespace webrtc { | 37 namespace webrtc { |
21 | 38 |
22 namespace { | 39 namespace { |
40 | |
41 void LinearEchoPower(const FftData& E, | |
42 const FftData& Y, | |
43 std::array<float, kFftLengthBy2Plus1>* S2) { | |
44 for (size_t k = 0; k < E.re.size(); ++k) { | |
45 (*S2)[k] = (Y.re[k] - E.re[k]) * (Y.re[k] - E.re[k]) + | |
46 (Y.im[k] - E.im[k]) * (Y.im[k] - E.im[k]); | |
47 } | |
48 } | |
49 | |
50 // Class for removing the echo from the capture signal. | |
23 class EchoRemoverImpl final : public EchoRemover { | 51 class EchoRemoverImpl final : public EchoRemover { |
24 public: | 52 public: |
25 explicit EchoRemoverImpl(int sample_rate_hz); | 53 explicit EchoRemoverImpl(int sample_rate_hz); |
26 ~EchoRemoverImpl() override; | 54 ~EchoRemoverImpl() override; |
27 | 55 |
28 void ProcessBlock(const rtc::Optional<size_t>& echo_path_delay_samples, | 56 // Removes the echo from a block of samples from the capture signal. The |
29 const EchoPathVariability& echo_path_variability, | 57 // supplied render signal is assumed to be pre-aligned with the capture |
30 bool capture_signal_saturation, | 58 // signal. |
31 const std::vector<std::vector<float>>& render, | 59 void ProcessBlock( |
32 std::vector<std::vector<float>>* capture) override; | 60 const rtc::Optional<size_t>& external_echo_path_delay_estimate, |
33 | 61 const EchoPathVariability& echo_path_variability, |
34 void UpdateEchoLeakageStatus(bool leakage_detected) override; | 62 bool capture_signal_saturation, |
63 const std::vector<std::vector<float>>& render, | |
64 std::vector<std::vector<float>>* capture) override; | |
65 | |
66 // Updates the status on whether echo leakage is detected in the output of the | |
67 // echo remover. | |
68 void UpdateEchoLeakageStatus(bool leakage_detected) override { | |
69 echo_leakage_detected_ = leakage_detected; | |
70 } | |
35 | 71 |
36 private: | 72 private: |
73 static int instance_count_; | |
74 const Aec3Fft fft_; | |
75 ApmDataDumper* data_dumper_; | |
37 const int sample_rate_hz_; | 76 const int sample_rate_hz_; |
77 Subtractor subtractor_; | |
78 SuppressionGain suppression_gain_; | |
79 ComfortNoiseGenerator cng_; | |
80 SuppressionFilter suppression_filter_; | |
81 PowerEchoModel power_echo_model_; | |
82 FftBuffer X_buffer_; | |
83 RenderSignalAnalyzer render_signal_analyzer_; | |
84 OutputSelector output_selector_; | |
85 DelayHandler delay_handler_; | |
86 ResidualEchoEstimator residual_echo_estimator_; | |
87 bool echo_leakage_detected_ = false; | |
88 std::array<float, kBlockSize> x_old_; | |
89 AecState aec_state_; | |
38 | 90 |
39 RTC_DISALLOW_COPY_AND_ASSIGN(EchoRemoverImpl); | 91 RTC_DISALLOW_COPY_AND_ASSIGN(EchoRemoverImpl); |
40 }; | 92 }; |
41 | 93 |
42 // TODO(peah): Add functionality. | 94 int EchoRemoverImpl::instance_count_ = 0; |
95 | |
43 EchoRemoverImpl::EchoRemoverImpl(int sample_rate_hz) | 96 EchoRemoverImpl::EchoRemoverImpl(int sample_rate_hz) |
44 : sample_rate_hz_(sample_rate_hz) { | 97 : data_dumper_( |
45 RTC_DCHECK(ValidFullBandRate(sample_rate_hz_)); | 98 new ApmDataDumper(rtc::AtomicOps::Increment(&instance_count_))), |
99 sample_rate_hz_(sample_rate_hz), | |
100 subtractor_(data_dumper_), | |
101 suppression_filter_(sample_rate_hz_), | |
102 X_buffer_(std::max(subtractor_.MinFarendBufferLength(), | |
103 power_echo_model_.MinFarendBufferLength()), | |
104 subtractor_.NumBlocksInRenderSums()) { | |
105 RTC_DCHECK(ValidFullBandRate(sample_rate_hz)); | |
106 x_old_.fill(0.f); | |
46 } | 107 } |
47 | 108 |
48 EchoRemoverImpl::~EchoRemoverImpl() = default; | 109 EchoRemoverImpl::~EchoRemoverImpl() = default; |
49 | 110 |
50 // TODO(peah): Add functionality. | |
51 void EchoRemoverImpl::ProcessBlock( | 111 void EchoRemoverImpl::ProcessBlock( |
52 const rtc::Optional<size_t>& echo_path_delay_samples, | 112 const rtc::Optional<size_t>& echo_path_delay_samples, |
53 const EchoPathVariability& echo_path_variability, | 113 const EchoPathVariability& echo_path_variability, |
54 bool capture_signal_saturation, | 114 bool capture_signal_saturation, |
55 const std::vector<std::vector<float>>& render, | 115 const std::vector<std::vector<float>>& render, |
56 std::vector<std::vector<float>>* capture) { | 116 std::vector<std::vector<float>>* capture) { |
57 RTC_DCHECK(capture); | 117 const std::vector<std::vector<float>>& x = render; |
58 RTC_DCHECK_EQ(render.size(), NumBandsForRate(sample_rate_hz_)); | 118 std::vector<std::vector<float>>* y = capture; |
aleloi
2017/02/13 16:44:50
Is it infeasible to change the inner vectors to ar
peah-webrtc
2017/02/20 07:37:16
We should definitely do that. But as you point out
aleloi
2017/02/23 10:56:44
SGTM
| |
59 RTC_DCHECK_EQ(capture->size(), NumBandsForRate(sample_rate_hz_)); | 119 |
aleloi
2017/02/13 16:44:50
A few general comments:
I think it would improve
peah-webrtc
2017/02/20 07:37:16
I definitely think you have a point. However, doin
aleloi
2017/02/23 10:56:44
SGTM
| |
60 RTC_DCHECK_EQ(render[0].size(), kBlockSize); | 120 RTC_DCHECK(y); |
61 RTC_DCHECK_EQ((*capture)[0].size(), kBlockSize); | 121 RTC_DCHECK_EQ(x.size(), NumBandsForRate(sample_rate_hz_)); |
122 RTC_DCHECK_EQ(y->size(), NumBandsForRate(sample_rate_hz_)); | |
123 RTC_DCHECK_EQ(x[0].size(), kBlockSize); | |
124 RTC_DCHECK_EQ((*y)[0].size(), kBlockSize); | |
125 const std::vector<float>& x0 = x[0]; | |
126 std::vector<float>& y0 = (*y)[0]; | |
127 | |
128 data_dumper_->DumpWav("aec3_processblock_capture_input", y0, | |
129 LowestBandRate(sample_rate_hz_), 1); | |
130 data_dumper_->DumpWav("aec3_processblock_render_input", x0, | |
131 LowestBandRate(sample_rate_hz_), 1); | |
132 | |
133 if (echo_path_variability.AudioPathChanged()) { | |
134 subtractor_.HandleEchoPathChange(echo_path_variability); | |
135 power_echo_model_.HandleEchoPathChange(echo_path_variability); | |
136 } | |
137 | |
138 std::array<float, kFftLengthBy2Plus1> Y2; | |
139 std::array<float, kFftLengthBy2Plus1> S2_power; | |
140 std::array<float, kFftLengthBy2Plus1> R2; | |
141 std::array<float, kFftLengthBy2Plus1> S2_linear; | |
142 std::array<float, kFftLengthBy2Plus1> G; | |
143 FftData X; | |
144 FftData Y; | |
145 FftData comfort_noise; | |
146 FftData high_band_comfort_noise; | |
147 SubtractorOutput subtractor_output; | |
148 FftData& E_main = subtractor_output.E_main; | |
149 std::array<float, kFftLengthBy2Plus1>& E2_main = subtractor_output.E2_main; | |
150 std::array<float, kFftLengthBy2Plus1>& E2_shadow = | |
151 subtractor_output.E2_shadow; | |
152 std::array<float, kBlockSize>& e_main = subtractor_output.e_main; | |
153 std::array<float, kBlockSize>& e_shadow = subtractor_output.e_shadow; | |
154 | |
155 // Update the render signal buffer. | |
156 fft_.PaddedFft(x0, x_old_, &X); | |
157 X_buffer_.Insert(X); | |
158 | |
159 // Analyze the render signal. | |
160 render_signal_analyzer_.Update(X_buffer_, delay_handler_.FilterDelay()); | |
161 | |
162 // Perform linear echo cancellation. | |
163 subtractor_.Process(X_buffer_, y0, render_signal_analyzer_, | |
164 capture_signal_saturation, &subtractor_output); | |
165 | |
166 // Detect basic doubletalk. | |
167 const float e_main_energy = | |
168 std::accumulate(e_main.begin(), e_main.end(), 0.f, | |
169 [](float a, float b) -> float { return a + b * b; }); | |
170 const float e_shadow_energy = | |
171 std::accumulate(e_shadow.begin(), e_shadow.end(), 0.f, | |
172 [](float a, float b) -> float { return a + b * b; }); | |
aleloi
2017/02/09 17:02:59
Maybe use std::inner_product? Less lambdas is simp
peah-webrtc
2017/02/20 07:37:16
Inner product looks perfect for this. I have some
aleloi
2017/02/23 10:56:45
I think you are right:
https://godbolt.org/g/ZVu
| |
173 const bool doubletalk = e_shadow_energy < e_main_energy; | |
174 | |
175 // Compute spectra. | |
176 fft_.ZeroPaddedFft(y0, &Y); | |
177 LinearEchoPower(E_main, Y, &S2_linear); | |
178 | |
179 Y.Spectrum(&Y2); | |
180 | |
181 // Update the internal delay estimates. | |
182 delay_handler_.UpdateDelays(subtractor_.FilterFrequencyResponse(), | |
183 echo_path_delay_samples); | |
184 | |
185 // Use the power model to estimate the echo. | |
186 power_echo_model_.EstimateEcho(X_buffer_, Y2, delay_handler_, | |
187 capture_signal_saturation, &S2_power); | |
188 | |
189 // Update the AEC state information. | |
190 aec_state_.Update(X_buffer_, E2_main, E2_shadow, Y2, x0, delay_handler_, | |
191 echo_path_variability, echo_leakage_detected_); | |
192 | |
193 // Estimate the residual echo power. | |
194 residual_echo_estimator_.Estimate( | |
195 aec_state_, X_buffer_, subtractor_.FilterFrequencyResponse(), E2_main, | |
196 E2_shadow, S2_linear, S2_power, Y2, delay_handler_, &R2); | |
197 | |
198 // Estimate the comfort noise. | |
199 cng_.Compute(Y2, &comfort_noise, &high_band_comfort_noise); | |
200 | |
201 // Choose the linear output. | |
202 output_selector_.FormLinearOutput(e_main, y0); | |
203 data_dumper_->DumpWav("aec3_output_linear", y0, | |
204 LowestBandRate(sample_rate_hz_), 1); | |
205 const std::array<float, kFftLengthBy2Plus1>& E2 = | |
206 output_selector_.UseSubtractorOutput() ? E2_main : Y2; | |
207 | |
208 // A choose and apply echo suppression gain. | |
209 suppression_gain_.GetGain(E2, R2, cng_.NoiseSpectrum(), | |
210 doubletalk ? 0.001f : 0.0001f, &G); | |
211 suppression_filter_.ApplyGain(comfort_noise, high_band_comfort_noise, G, y); | |
212 | |
213 data_dumper_->DumpWav("aec3_output", y0, LowestBandRate(sample_rate_hz_), 1); | |
62 } | 214 } |
63 | 215 |
64 // TODO(peah): Add functionality. | |
65 void EchoRemoverImpl::UpdateEchoLeakageStatus(bool leakage_detected) {} | |
66 | |
67 } // namespace | 216 } // namespace |
68 | 217 |
69 EchoRemover* EchoRemover::Create(int sample_rate_hz) { | 218 EchoRemover* EchoRemover::Create(int sample_rate_hz) { |
70 return new EchoRemoverImpl(sample_rate_hz); | 219 return new EchoRemoverImpl(sample_rate_hz); |
71 } | 220 } |
72 | 221 |
73 } // namespace webrtc | 222 } // namespace webrtc |
OLD | NEW |