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 60 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 71 return rtc::Optional<size_t>(); | 71 return rtc::Optional<size_t>(); |
| 72 } | 72 } |
| 73 | 73 |
| 74 constexpr int kEchoPathChangeCounterInitial = kNumBlocksPerSecond / 5; | 74 constexpr int kEchoPathChangeCounterInitial = kNumBlocksPerSecond / 5; |
| 75 constexpr int kEchoPathChangeCounterMax = 2 * kNumBlocksPerSecond; | 75 constexpr int kEchoPathChangeCounterMax = 2 * kNumBlocksPerSecond; |
| 76 | 76 |
| 77 } // namespace | 77 } // namespace |
| 78 | 78 |
| 79 int AecState::instance_count_ = 0; | 79 int AecState::instance_count_ = 0; |
| 80 | 80 |
| 81 AecState::AecState(float echo_decay) | 81 AecState::AecState(float reverb_decay) |
| 82 : data_dumper_( | 82 : data_dumper_( |
| 83 new ApmDataDumper(rtc::AtomicOps::Increment(&instance_count_))), | 83 new ApmDataDumper(rtc::AtomicOps::Increment(&instance_count_))), |
| 84 echo_path_change_counter_(kEchoPathChangeCounterInitial), | 84 echo_path_change_counter_(kEchoPathChangeCounterInitial), |
| 85 echo_decay_factor_(echo_decay) {} | 85 reverb_decay_(reverb_decay) {} |
| 86 | 86 |
| 87 AecState::~AecState() = default; | 87 AecState::~AecState() = default; |
| 88 | 88 |
| 89 void AecState::HandleEchoPathChange( | 89 void AecState::HandleEchoPathChange( |
| 90 const EchoPathVariability& echo_path_variability) { | 90 const EchoPathVariability& echo_path_variability) { |
| 91 if (echo_path_variability.AudioPathChanged()) { | 91 if (echo_path_variability.AudioPathChanged()) { |
| 92 blocks_since_last_saturation_ = 0; | 92 blocks_since_last_saturation_ = 0; |
| 93 usable_linear_estimate_ = false; | 93 usable_linear_estimate_ = false; |
| 94 echo_leakage_detected_ = false; | 94 echo_leakage_detected_ = false; |
| 95 capture_signal_saturation_ = false; | 95 capture_signal_saturation_ = false; |
| 96 echo_saturation_ = false; | 96 echo_saturation_ = false; |
| 97 previous_max_sample_ = 0.f; | 97 previous_max_sample_ = 0.f; |
| 98 | 98 |
| 99 if (echo_path_variability.delay_change) { | 99 if (echo_path_variability.delay_change) { |
| 100 force_zero_gain_counter_ = 0; | 100 force_zero_gain_counter_ = 0; |
| 101 blocks_with_filter_adaptation_ = 0; | 101 blocks_with_filter_adaptation_ = 0; |
| 102 render_received_ = false; | 102 render_received_ = false; |
| 103 force_zero_gain_ = true; | 103 force_zero_gain_ = true; |
| 104 echo_path_change_counter_ = kEchoPathChangeCounterMax; | 104 echo_path_change_counter_ = kEchoPathChangeCounterMax; |
| 105 } | 105 } |
| 106 if (echo_path_variability.gain_change) { | 106 if (echo_path_variability.gain_change) { |
| 107 echo_path_change_counter_ = kEchoPathChangeCounterInitial; | 107 echo_path_change_counter_ = kEchoPathChangeCounterInitial; |
| 108 } | 108 } |
| 109 } | 109 } |
| 110 } | 110 } |
| 111 | 111 |
| 112 void AecState::Update(const std::vector<std::array<float, kFftLengthBy2Plus1>>& | 112 void AecState::Update(const std::vector<std::array<float, kFftLengthBy2Plus1>>& |
| 113 adaptive_filter_frequency_response, | 113 adaptive_filter_frequency_response, |
| 114 const std::array<float, kAdaptiveFilterTimeDomainLength>& | |
| 115 adaptive_filter_impulse_response, | |
| 114 const rtc::Optional<size_t>& external_delay_samples, | 116 const rtc::Optional<size_t>& external_delay_samples, |
| 115 const RenderBuffer& render_buffer, | 117 const RenderBuffer& render_buffer, |
| 116 const std::array<float, kFftLengthBy2Plus1>& E2_main, | 118 const std::array<float, kFftLengthBy2Plus1>& E2_main, |
| 117 const std::array<float, kFftLengthBy2Plus1>& Y2, | 119 const std::array<float, kFftLengthBy2Plus1>& Y2, |
| 118 rtc::ArrayView<const float> x, | 120 rtc::ArrayView<const float> x, |
| 121 const std::array<float, kBlockSize>& s, | |
| 119 bool echo_leakage_detected) { | 122 bool echo_leakage_detected) { |
| 123 // Update the echo audibility evaluator. | |
| 124 echo_audibility_.Update(x, s); | |
| 125 | |
| 120 // Store input parameters. | 126 // Store input parameters. |
| 121 echo_leakage_detected_ = echo_leakage_detected; | 127 echo_leakage_detected_ = echo_leakage_detected; |
| 122 | 128 |
| 123 // Update counters. | 129 // Update counters. |
| 124 const float x_energy = std::inner_product(x.begin(), x.end(), x.begin(), 0.f); | 130 const float x_energy = std::inner_product(x.begin(), x.end(), x.begin(), 0.f); |
| 125 const bool active_render_block = x_energy > 10000.f * kFftLengthBy2; | 131 const bool active_render_block = x_energy > 10000.f * kFftLengthBy2; |
| 126 if (active_render_block) { | 132 if (active_render_block) { |
| 127 render_received_ = true; | 133 render_received_ = true; |
| 128 } | 134 } |
| 129 blocks_with_filter_adaptation_ += | 135 blocks_with_filter_adaptation_ += |
| (...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 172 blocks_with_filter_adaptation_ > kEchoPathChangeConvergenceBlocks) && | 178 blocks_with_filter_adaptation_ > kEchoPathChangeConvergenceBlocks) && |
| 173 filter_delay_ && echo_path_change_counter_ <= 0; | 179 filter_delay_ && echo_path_change_counter_ <= 0; |
| 174 | 180 |
| 175 // After an amount of active render samples for which an echo should have been | 181 // After an amount of active render samples for which an echo should have been |
| 176 // detected in the capture signal if the ERL was not infinite, flag that a | 182 // detected in the capture signal if the ERL was not infinite, flag that a |
| 177 // headset is used. | 183 // headset is used. |
| 178 headset_detected_ = | 184 headset_detected_ = |
| 179 !external_delay_ && !filter_delay_ && | 185 !external_delay_ && !filter_delay_ && |
| 180 (!render_received_ || | 186 (!render_received_ || |
| 181 blocks_with_filter_adaptation_ >= kEchoPathChangeConvergenceBlocks); | 187 blocks_with_filter_adaptation_ >= kEchoPathChangeConvergenceBlocks); |
| 188 | |
| 189 // Update the room reverb estimate. | |
| 190 UpdateReverb(adaptive_filter_impulse_response); | |
| 191 } | |
| 192 | |
| 193 void AecState::UpdateReverb( | |
| 194 const std::array<float, kAdaptiveFilterTimeDomainLength>& | |
| 195 impulse_response) { | |
| 196 if ((!(filter_delay_ && usable_linear_estimate_)) || | |
| 197 (*filter_delay_ > kAdaptiveFilterLength - 4)) { | |
| 198 return; | |
| 199 } | |
| 200 | |
| 201 // Form the data to match against by squaring the impulse response | |
| 202 // coefficients. | |
| 203 std::array<float, kAdaptiveFilterTimeDomainLength> matching_data; | |
| 204 std::transform(impulse_response.begin(), impulse_response.end(), | |
| 205 matching_data.begin(), [](float a) { return a * a; }); | |
| 206 | |
| 207 // Avoid matching against noise in the model by subtracting an estimate of the | |
| 208 // model noise power. | |
| 209 constexpr size_t kTailLength = 64; | |
| 210 constexpr size_t tail_index = kAdaptiveFilterTimeDomainLength - kTailLength; | |
| 211 const float tail_power = *std::max_element(matching_data.begin() + tail_index, | |
| 212 matching_data.end()); | |
| 213 std::for_each(matching_data.begin(), matching_data.begin() + tail_index, | |
| 214 [tail_power](float& a) { a = std::max(0.f, a - tail_power); }); | |
| 215 | |
| 216 // Identify the peak index of the impulse response. | |
| 217 size_t peak_index = 0; | |
| 218 float peak_value = matching_data[0]; | |
| 219 for (size_t k = 1; k < tail_index; ++k) { | |
|
ivoc
2017/07/10 13:53:55
You could use std::max_element instead of this loo
peah-webrtc
2017/07/10 23:18:09
Good point!
Done.
| |
| 220 if (matching_data[k] > peak_value) { | |
| 221 peak_value = matching_data[k]; | |
| 222 peak_index = k; | |
| 223 } | |
| 224 } | |
| 225 | |
| 226 if (peak_index + 128 < tail_index) { | |
| 227 size_t start_index = peak_index + 64; | |
| 228 // Compute the matching residual error for the current candidate to match. | |
| 229 float residual_sqr_sum = 0.f; | |
| 230 float d_k = reverb_decay_to_test_; | |
| 231 for (size_t k = start_index; k < tail_index; ++k) { | |
| 232 if (matching_data[start_index + 1] == 0.f) { | |
| 233 break; | |
| 234 } | |
| 235 | |
| 236 float residual = matching_data[k] - matching_data[peak_index] * d_k; | |
| 237 residual_sqr_sum += residual * residual; | |
| 238 d_k *= reverb_decay_to_test_; | |
| 239 } | |
| 240 | |
| 241 // If needed, update the best candidate for the reverb decay. | |
| 242 if (reverb_decay_candidate_residual_ < 0.f || | |
| 243 residual_sqr_sum < reverb_decay_candidate_residual_) { | |
| 244 reverb_decay_candidate_residual_ = residual_sqr_sum; | |
| 245 reverb_decay_candidate_ = reverb_decay_to_test_; | |
| 246 } | |
| 247 } | |
| 248 | |
| 249 // Compute the next reverb candidate to evaluate such that all candidates will | |
| 250 // be evaluated within one second. | |
| 251 reverb_decay_to_test_ += (0.9965f - 0.9f) / (5 * kNumBlocksPerSecond); | |
| 252 | |
| 253 // If all reverb candidates have been evaluated, choose the best one as the | |
| 254 // reverb decay. | |
| 255 if (reverb_decay_to_test_ >= 0.9965f) { | |
| 256 reverb_decay_to_test_ = 0.9f; | |
| 257 reverb_decay_candidate_residual_ = -1.f; | |
| 258 | |
| 259 if (reverb_decay_candidate_residual_ < 0.f) { | |
|
ivoc
2017/07/10 13:53:55
This is always true (since reverb_decay_candidate_
peah-webrtc
2017/07/10 23:18:09
Good find!
Done.
| |
| 260 // Transform the decay to be in the unit of blocks. | |
| 261 reverb_decay_ = 1.f; | |
| 262 for (size_t k = 0; k < kFftLengthBy2; ++k) { | |
| 263 reverb_decay_ *= reverb_decay_candidate_; | |
|
ivoc
2017/07/10 13:53:55
Is this for-loop more efficient than using pow?
peah-webrtc
2017/07/10 23:18:09
That seems a bit unclear. However, this is only do
| |
| 264 } | |
| 265 | |
| 266 // Limit the estimated reverb_decay_ to the maximum one needed in practice | |
| 267 // to minimize the impact of incorrect estimates. | |
| 268 reverb_decay_ = std::min(0.8f, reverb_decay_); | |
| 269 } | |
| 270 } | |
| 271 | |
| 272 // For noisy impulse responses, assume a fixed tail length. | |
| 273 if (tail_power > 0.0005f) { | |
| 274 reverb_decay_ = 0.7f; | |
| 275 } | |
| 276 data_dumper_->DumpRaw("aec3_reverb_decay", reverb_decay_); | |
| 277 data_dumper_->DumpRaw("aec3_tail_power", tail_power); | |
| 278 } | |
| 279 | |
| 280 void AecState::EchoAudibility::Update(rtc::ArrayView<const float> x, | |
| 281 const std::array<float, kBlockSize>& s) { | |
| 282 const float x_max = *std::max_element(x.begin(), x.end()); | |
| 283 const float x_min = *std::min_element(x.begin(), x.end()); | |
|
ivoc
2017/07/10 13:53:55
There is a std::minmax function that calculates bo
peah-webrtc
2017/07/10 23:18:09
Ah! Great! I did not know about that one. Afaics,
| |
| 284 const float s_max = *std::max_element(s.begin(), s.end()); | |
| 285 const float s_min = *std::min_element(s.begin(), s.end()); | |
| 286 const float s_abs = std::max(std::abs(s_max), std::abs(s_min)); | |
| 287 const float x_abs = std::max(std::abs(x_max), std::abs(x_min)); | |
| 288 | |
| 289 if (x_abs < 5.f) { | |
| 290 ++low_farend_counter_; | |
| 291 } else { | |
| 292 low_farend_counter_ = 0; | |
| 293 } | |
| 294 | |
| 295 // The echo is deemed as not audible if the echo estimate is on the level of | |
| 296 // the quantization noise in the FFTs and the nearend level is sufficiently | |
| 297 // strong to mask that by ensuring that the playout and AGC gains do not boost | |
| 298 // any residual echo that is below the quantization noise level. Furthermore, | |
| 299 // cases where the render signal is very close to zero are also identified as | |
| 300 // not producing audible echo. | |
| 301 inaudible_echo_ = max_nearend_ > 500 && s_abs < 30.f; | |
| 302 inaudible_echo_ = inaudible_echo_ || low_farend_counter_ > 20; | |
| 303 } | |
| 304 | |
| 305 void AecState::EchoAudibility::UpdateWithOutput(rtc::ArrayView<const float> e) { | |
| 306 const float e_max = *std::max_element(e.begin(), e.end()); | |
| 307 const float e_min = *std::min_element(e.begin(), e.end()); | |
| 308 const float e_abs = std::max(std::abs(e_max), std::abs(e_min)); | |
| 309 | |
| 310 if (max_nearend_ < e_abs) { | |
| 311 max_nearend_ = e_abs; | |
| 312 max_nearend_counter_ = 0; | |
| 313 } else { | |
| 314 if (++max_nearend_counter_ > 5 * kNumBlocksPerSecond) { | |
| 315 max_nearend_ *= 0.995f; | |
| 316 } | |
| 317 } | |
| 182 } | 318 } |
| 183 | 319 |
| 184 } // namespace webrtc | 320 } // namespace webrtc |
| OLD | NEW |