| 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 |