Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(176)

Side by Side Diff: webrtc/modules/audio_processing/aec3/residual_echo_estimator.cc

Issue 2804223002: Adding support for handling highly reverberant echoes in AEC3 (Closed)
Patch Set: Added limiting of the delay size Created 3 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « webrtc/modules/audio_processing/aec3/residual_echo_estimator.h ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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 10
(...skipping 22 matching lines...) Expand all
33 33
34 // Apply soft noise gate of -78 dBFS. 34 // Apply soft noise gate of -78 dBFS.
35 constexpr float kNoiseGatePower = 27509.42f; 35 constexpr float kNoiseGatePower = 27509.42f;
36 std::for_each(X2->begin(), X2->end(), [kNoiseGatePower](float& a) { 36 std::for_each(X2->begin(), X2->end(), [kNoiseGatePower](float& a) {
37 if (kNoiseGatePower > a) { 37 if (kNoiseGatePower > a) {
38 a = std::max(0.f, a - 0.3f * (kNoiseGatePower - a)); 38 a = std::max(0.f, a - 0.3f * (kNoiseGatePower - a));
39 } 39 }
40 }); 40 });
41 } 41 }
42 42
43 // Estimates the residual echo power based on the erle and the linear power
44 // estimate.
45 void LinearResidualPowerEstimate(
46 const std::array<float, kFftLengthBy2Plus1>& S2_linear,
47 const std::array<float, kFftLengthBy2Plus1>& erle,
48 std::array<int, kFftLengthBy2Plus1>* R2_hold_counter,
49 std::array<float, kFftLengthBy2Plus1>* R2) {
50 std::fill(R2_hold_counter->begin(), R2_hold_counter->end(), 10.f);
51 std::transform(erle.begin(), erle.end(), S2_linear.begin(), R2->begin(),
52 [](float a, float b) {
53 RTC_DCHECK_LT(0.f, a);
54 return b / a;
55 });
56 }
57
58 // Estimates the residual echo power based on the estimate of the echo path
59 // gain.
60 void NonLinearResidualPowerEstimate(
61 const std::array<float, kFftLengthBy2Plus1>& X2,
62 const std::array<float, kFftLengthBy2Plus1>& Y2,
63 const std::array<float, kFftLengthBy2Plus1>& R2_old,
64 std::array<int, kFftLengthBy2Plus1>* R2_hold_counter,
65 std::array<float, kFftLengthBy2Plus1>* R2) {
66 // Compute preliminary residual echo.
67 // TODO(peah): Try to make this adaptive. Currently the gain is hardcoded to
68 // 20 dB.
69 std::transform(X2.begin(), X2.end(), R2->begin(),
70 [](float a) { return a * kFixedEchoPathGain; });
71
72 for (size_t k = 0; k < R2->size(); ++k) {
73 // Update hold counter.
74 (*R2_hold_counter)[k] =
75 R2_old[k] < (*R2)[k] ? 0 : (*R2_hold_counter)[k] + 1;
76
77 // Compute the residual echo by holding a maximum echo powers and an echo
78 // fading corresponding to a room with an RT60 value of about 50 ms.
79 (*R2)[k] = (*R2_hold_counter)[k] < 2
80 ? std::max((*R2)[k], R2_old[k])
81 : std::min((*R2)[k] + R2_old[k] * 0.1f, Y2[k]);
82 }
83 }
84
85 } // namespace 43 } // namespace
86 44
87 ResidualEchoEstimator::ResidualEchoEstimator() { 45 ResidualEchoEstimator::ResidualEchoEstimator() {
88 R2_old_.fill(0.f); 46 Reset();
89 R2_hold_counter_.fill(0);
90 } 47 }
91 48
92 ResidualEchoEstimator::~ResidualEchoEstimator() = default; 49 ResidualEchoEstimator::~ResidualEchoEstimator() = default;
93 50
94 void ResidualEchoEstimator::Estimate( 51 void ResidualEchoEstimator::Estimate(
95 bool using_subtractor_output, 52 bool using_subtractor_output,
96 const AecState& aec_state, 53 const AecState& aec_state,
97 const RenderBuffer& render_buffer, 54 const RenderBuffer& render_buffer,
98 const std::array<float, kFftLengthBy2Plus1>& S2_linear, 55 const std::array<float, kFftLengthBy2Plus1>& S2_linear,
99 const std::array<float, kFftLengthBy2Plus1>& Y2, 56 const std::array<float, kFftLengthBy2Plus1>& Y2,
100 std::array<float, kFftLengthBy2Plus1>* R2) { 57 std::array<float, kFftLengthBy2Plus1>* R2) {
101 RTC_DCHECK(R2); 58 RTC_DCHECK(R2);
102 59
103 // Return zero residual echo power when a headset is detected. 60 // Return zero residual echo power when a headset is detected.
104 if (aec_state.HeadsetDetected()) { 61 if (aec_state.HeadsetDetected()) {
62 if (!headset_detected_cached_) {
63 Reset();
64 headset_detected_cached_ = true;
65 }
105 R2->fill(0.f); 66 R2->fill(0.f);
106 R2_old_.fill(0.f);
107 R2_hold_counter_.fill(0.f);
108 return; 67 return;
68 } else {
69 headset_detected_cached_ = false;
109 } 70 }
110 71
111 // Estimate the echo generating signal power. 72 const rtc::Optional<size_t> delay =
112 std::array<float, kFftLengthBy2Plus1> X2; 73 aec_state.FilterDelay()
113 if (aec_state.ExternalDelay() || aec_state.FilterDelay()) { 74 ? aec_state.FilterDelay()
114 const int delay = 75 : (aec_state.ExternalDelay() ? aec_state.ExternalDelay()
115 static_cast<int>(aec_state.FilterDelay() ? *aec_state.FilterDelay() 76 : rtc::Optional<size_t>());
116 : *aec_state.ExternalDelay());
117 // Computes the spectral power over that blocks surrounding the delauy..
118 EchoGeneratingPower(
119 render_buffer, std::max(0, delay - 1),
120 std::min(kResidualEchoPowerRenderWindowSize - 1, delay + 1), &X2);
121 } else {
122 // Computes the spectral power over that last 30 blocks.
123 EchoGeneratingPower(render_buffer, 0,
124 kResidualEchoPowerRenderWindowSize - 1, &X2);
125 }
126 77
127 // Estimate the residual echo power. 78 // Estimate the residual echo power.
128 if ((aec_state.UsableLinearEstimate() && using_subtractor_output)) { 79 const bool use_linear_echo_power =
129 LinearResidualPowerEstimate(S2_linear, aec_state.Erle(), &R2_hold_counter_, 80 aec_state.UsableLinearEstimate() && using_subtractor_output;
130 R2); 81 if (use_linear_echo_power) {
82 RTC_DCHECK(aec_state.FilterDelay());
83 const int filter_delay = *aec_state.FilterDelay();
84 LinearEstimate(S2_linear, aec_state.Erle(), filter_delay, R2);
85 AddEchoReverb(S2_linear, aec_state.SaturatedEcho(), filter_delay,
86 aec_state.ReverbDecayFactor(), R2);
131 } else { 87 } else {
132 NonLinearResidualPowerEstimate(X2, Y2, R2_old_, &R2_hold_counter_, R2); 88 // Estimate the echo generating signal power.
89 std::array<float, kFftLengthBy2Plus1> X2;
90 if (aec_state.ExternalDelay() || aec_state.FilterDelay()) {
91 RTC_DCHECK(delay);
92 const int delay_use = static_cast<int>(*delay);
93
94 // Computes the spectral power over the blocks surrounding the delay.
95 RTC_DCHECK_LT(delay_use, kResidualEchoPowerRenderWindowSize);
96 EchoGeneratingPower(
97 render_buffer, std::max(0, delay_use - 1),
98 std::min(kResidualEchoPowerRenderWindowSize - 1, delay_use + 1), &X2);
99 } else {
100 // Computes the spectral power over the latest blocks.
101 EchoGeneratingPower(render_buffer, 0,
102 kResidualEchoPowerRenderWindowSize - 1, &X2);
103 }
104
105 NonLinearEstimate(X2, Y2, R2);
106 AddEchoReverb(*R2, aec_state.SaturatedEcho(),
107 std::min(static_cast<size_t>(kAdaptiveFilterLength),
108 delay.value_or(kAdaptiveFilterLength)),
109 aec_state.ReverbDecayFactor(), R2);
133 } 110 }
134 111
135 // If the echo is saturated, estimate the echo power as the maximum echo power 112 // If the echo is saturated, estimate the echo power as the maximum echo power
136 // with a leakage factor. 113 // with a leakage factor.
137 if (aec_state.SaturatedEcho()) { 114 if (aec_state.SaturatedEcho()) {
138 constexpr float kSaturationLeakageFactor = 100.f; 115 R2->fill((*std::max_element(R2->begin(), R2->end())) * 100.f);
139 R2->fill((*std::max_element(R2->begin(), R2->end())) *
140 kSaturationLeakageFactor);
141 } 116 }
142 117
143 std::copy(R2->begin(), R2->end(), R2_old_.begin()); 118 std::copy(R2->begin(), R2->end(), R2_old_.begin());
144 } 119 }
145 120
121 void ResidualEchoEstimator::Reset() {
122 R2_reverb_.fill(0.f);
123 R2_old_.fill(0.f);
124 R2_hold_counter_.fill(0.f);
125 for (auto& S2_k : S2_old_) {
126 S2_k.fill(0.f);
127 }
128 }
129
130 void ResidualEchoEstimator::LinearEstimate(
131 const std::array<float, kFftLengthBy2Plus1>& S2_linear,
132 const std::array<float, kFftLengthBy2Plus1>& erle,
133 size_t delay,
134 std::array<float, kFftLengthBy2Plus1>* R2) {
135 std::fill(R2_hold_counter_.begin(), R2_hold_counter_.end(), 10.f);
136 std::transform(erle.begin(), erle.end(), S2_linear.begin(), R2->begin(),
137 [](float a, float b) {
138 RTC_DCHECK_LT(0.f, a);
139 return b / a;
140 });
141 }
142
143 void ResidualEchoEstimator::NonLinearEstimate(
144 const std::array<float, kFftLengthBy2Plus1>& X2,
145 const std::array<float, kFftLengthBy2Plus1>& Y2,
146 std::array<float, kFftLengthBy2Plus1>* R2) {
147 // Compute preliminary residual echo.
148 // TODO(peah): Try to make this adaptive. Currently the gain is hardcoded to
149 // 20 dB.
150 std::transform(X2.begin(), X2.end(), R2->begin(),
151 [](float a) { return a * kFixedEchoPathGain; });
152
153 for (size_t k = 0; k < R2->size(); ++k) {
154 // Update hold counter.
155 R2_hold_counter_[k] = R2_old_[k] < (*R2)[k] ? 0 : R2_hold_counter_[k] + 1;
156
157 // Compute the residual echo by holding a maximum echo powers and an echo
158 // fading corresponding to a room with an RT60 value of about 50 ms.
159 (*R2)[k] = R2_hold_counter_[k] < 2
160 ? std::max((*R2)[k], R2_old_[k])
161 : std::min((*R2)[k] + R2_old_[k] * 0.1f, Y2[k]);
162 }
163 }
164
165 void ResidualEchoEstimator::AddEchoReverb(
166 const std::array<float, kFftLengthBy2Plus1>& S2,
167 bool saturated_echo,
168 size_t delay,
169 float reverb_decay_factor,
170 std::array<float, kFftLengthBy2Plus1>* R2) {
171 // Compute the decay factor for how much the echo has decayed before leaving
172 // the region covered by the linear model.
173 auto integer_power = [](float base, int exp) {
174 float result = 1.f;
175 for (int k = 0; k < exp; ++k) {
176 result *= base;
177 }
178 return result;
179 };
180 RTC_DCHECK_LE(delay, S2_old_.size());
181 const float reverb_decay_for_delay =
182 integer_power(reverb_decay_factor, S2_old_.size() - delay);
183
184 // Update the estimate of the reverberant residual echo power.
185 S2_old_index_ = S2_old_index_ > 0 ? S2_old_index_ - 1 : S2_old_.size() - 1;
186 const auto& S2_end = S2_old_[S2_old_index_];
187 std::transform(
188 S2_end.begin(), S2_end.end(), R2_reverb_.begin(), R2_reverb_.begin(),
189 [reverb_decay_for_delay, reverb_decay_factor](float a, float b) {
190 return (b + a * reverb_decay_for_delay) * reverb_decay_factor;
191 });
192
193 // Update the buffer of old echo powers.
194 if (saturated_echo) {
195 S2_old_[S2_old_index_].fill((*std::max_element(S2.begin(), S2.end())) *
196 100.f);
197 } else {
198 std::copy(S2.begin(), S2.end(), S2_old_[S2_old_index_].begin());
199 }
200
201 // Add the power of the echo reverb to the residual echo power.
202 std::transform(R2->begin(), R2->end(), R2_reverb_.begin(), R2->begin(),
203 std::plus<float>());
204 }
205
146 } // namespace webrtc 206 } // namespace webrtc
OLDNEW
« no previous file with comments | « webrtc/modules/audio_processing/aec3/residual_echo_estimator.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698