Chromium Code Reviews| 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 |