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; |
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 |
OLD | NEW |