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 | 10 |
(...skipping 22 matching lines...) Expand all Loading... | |
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; | |
AleBzk
2017/04/07 09:34:39
It's for sure already ok as it is, but double chec
peah-webrtc
2017/04/07 12:04:29
The intention of caching this is that otherwise I
| |
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(delay); | |
83 | |
84 LinearEstimate(S2_linear, aec_state.Erle(), *aec_state.FilterDelay(), R2); | |
85 AddEchoReverb(S2_linear, aec_state.SaturatedEcho(), *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 that blocks surrounding the delay. | |
AleBzk
2017/04/07 09:34:39
Either "that block" or "those blocks" - I guess th
aleloi
2017/04/07 11:08:57
that -> the
peah-webrtc
2017/04/07 12:19:44
Done.
peah-webrtc
2017/04/07 12:19:44
Done.
| |
95 EchoGeneratingPower( | |
96 render_buffer, std::max(0, delay_use - 1), | |
97 std::min(kResidualEchoPowerRenderWindowSize - 1, delay_use + 1), &X2); | |
AleBzk
2017/04/07 09:34:39
I'm not sure, but maybe you want to DCHECK (kResid
peah-webrtc
2017/04/07 12:19:44
I'll put a DCHECK on delay_use.
Done.
| |
98 } else { | |
99 // Computes the spectral power over the latest blocks. | |
100 EchoGeneratingPower(render_buffer, 0, | |
101 kResidualEchoPowerRenderWindowSize - 1, &X2); | |
102 } | |
103 | |
104 NonLinearEstimate(X2, Y2, R2); | |
105 AddEchoReverb(*R2, aec_state.SaturatedEcho(), | |
106 delay ? *delay : kAdaptiveFilterLength, | |
aleloi
2017/04/07 11:08:56
Please replace ternary op expression with 'delay.v
peah-webrtc
2017/04/07 12:19:44
Awesome!
Done.
| |
107 aec_state.ReverbDecayFactor(), R2); | |
133 } | 108 } |
134 | 109 |
135 // If the echo is saturated, estimate the echo power as the maximum echo power | 110 // If the echo is saturated, estimate the echo power as the maximum echo power |
136 // with a leakage factor. | 111 // with a leakage factor. |
137 if (aec_state.SaturatedEcho()) { | 112 if (aec_state.SaturatedEcho()) { |
138 constexpr float kSaturationLeakageFactor = 100.f; | 113 R2->fill((*std::max_element(R2->begin(), R2->end())) * 100.f); |
aleloi
2017/04/07 11:08:57
Should the '100.f' also be kFixedEchoPathGain'?
peah-webrtc
2017/04/07 12:19:44
No. Not necessary at least. This is some kind of m
| |
139 R2->fill((*std::max_element(R2->begin(), R2->end())) * | |
140 kSaturationLeakageFactor); | |
141 } | 114 } |
142 | 115 |
143 std::copy(R2->begin(), R2->end(), R2_old_.begin()); | 116 std::copy(R2->begin(), R2->end(), R2_old_.begin()); |
144 } | 117 } |
145 | 118 |
119 void ResidualEchoEstimator::Reset() { | |
120 R2_reverb_.fill(0.f); | |
121 R2_old_.fill(0.f); | |
122 R2_hold_counter_.fill(0.f); | |
123 for (auto& S2_k : S2_old_) { | |
124 S2_k.fill(0.f); | |
125 } | |
126 } | |
127 | |
128 void ResidualEchoEstimator::LinearEstimate( | |
129 const std::array<float, kFftLengthBy2Plus1>& S2_linear, | |
130 const std::array<float, kFftLengthBy2Plus1>& erle, | |
131 size_t delay, | |
132 std::array<float, kFftLengthBy2Plus1>* R2) { | |
133 std::fill(R2_hold_counter_.begin(), R2_hold_counter_.end(), 10.f); | |
134 std::transform(erle.begin(), erle.end(), S2_linear.begin(), R2->begin(), | |
135 [](float a, float b) { | |
136 RTC_DCHECK_LT(0.f, a); | |
137 return b / a; | |
138 }); | |
139 } | |
140 | |
141 void ResidualEchoEstimator::NonLinearEstimate( | |
142 const std::array<float, kFftLengthBy2Plus1>& X2, | |
143 const std::array<float, kFftLengthBy2Plus1>& Y2, | |
144 std::array<float, kFftLengthBy2Plus1>* R2) { | |
145 // Compute preliminary residual echo. | |
146 // TODO(peah): Try to make this adaptive. Currently the gain is hardcoded to | |
147 // 20 dB. | |
148 std::transform(X2.begin(), X2.end(), R2->begin(), | |
149 [](float a) { return a * kFixedEchoPathGain; }); | |
150 | |
151 for (size_t k = 0; k < R2->size(); ++k) { | |
152 // Update hold counter. | |
153 R2_hold_counter_[k] = R2_old_[k] < (*R2)[k] ? 0 : R2_hold_counter_[k] + 1; | |
154 | |
155 // Compute the residual echo by holding a maximum echo powers and an echo | |
156 // fading corresponding to a room with an RT60 value of about 50 ms. | |
157 (*R2)[k] = R2_hold_counter_[k] < 2 | |
158 ? std::max((*R2)[k], R2_old_[k]) | |
159 : std::min((*R2)[k] + R2_old_[k] * 0.1f, Y2[k]); | |
160 } | |
161 } | |
162 | |
163 void ResidualEchoEstimator::AddEchoReverb( | |
164 const std::array<float, kFftLengthBy2Plus1>& S2, | |
165 bool saturated_echo, | |
166 size_t delay, | |
167 float reverb_decay_factor, | |
168 std::array<float, kFftLengthBy2Plus1>* R2) { | |
169 // Compute the decay factor for how much the echo has decayed before leaving | |
170 // the region covered by the linear model. | |
171 auto integer_power = [](float base, int exp) { | |
172 float result = 1.f; | |
173 for (int k = 0; k < exp; ++k) { | |
174 result *= base; | |
175 } | |
176 return result; | |
177 }; | |
178 RTC_DCHECK_LE(delay, S2_old_.size()); | |
179 const float reverb_decay_for_delay = | |
180 integer_power(reverb_decay_factor, S2_old_.size() - delay); | |
181 | |
182 // Update the estimate of the reverberant residual echo power. | |
183 S2_old_index_ = S2_old_index_ > 0 ? S2_old_index_ - 1 : S2_old_.size() - 1; | |
184 const auto& S2_end = S2_old_[S2_old_index_]; | |
185 std::transform( | |
186 S2_end.begin(), S2_end.end(), R2_reverb_.begin(), R2_reverb_.begin(), | |
187 [reverb_decay_for_delay, reverb_decay_factor](float a, float b) { | |
188 return (b + a * reverb_decay_for_delay) * reverb_decay_factor; | |
189 }); | |
190 | |
191 // Update the buffer of old echo powers. | |
192 if (saturated_echo) { | |
193 S2_old_[S2_old_index_].fill((*std::max_element(S2.begin(), S2.end())) * | |
194 100.f); | |
195 } else { | |
196 std::copy(S2.begin(), S2.end(), S2_old_[S2_old_index_].begin()); | |
197 } | |
198 | |
199 // Add the power of the echo reverb to the residual echo power. | |
200 std::transform(R2->begin(), R2->end(), R2_reverb_.begin(), R2->begin(), | |
201 std::plus<float>()); | |
202 } | |
203 | |
146 } // namespace webrtc | 204 } // namespace webrtc |
OLD | NEW |