| /* |
| * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. |
| * |
| * Use of this source code is governed by a BSD-style license |
| * that can be found in the LICENSE file in the root of the source |
| * tree. An additional intellectual property rights grant can be found |
| * in the file PATENTS. All contributing project authors may |
| * be found in the AUTHORS file in the root of the source tree. |
| */ |
| |
| #include "webrtc/modules/audio_processing/aec3/aec_state.h" |
| |
| #include <math.h> |
| #include <numeric> |
| #include <vector> |
| |
| #include "webrtc/base/atomicops.h" |
| #include "webrtc/base/checks.h" |
| #include "webrtc/modules/audio_processing/logging/apm_data_dumper.h" |
| |
| namespace webrtc { |
| namespace { |
| |
| constexpr float kMaxFilterEstimateStrength = 1000.f; |
| |
| // Compute the delay of the adaptive filter as the partition with a distinct |
| // peak. |
| void AnalyzeFilter( |
| const std::vector<std::array<float, kFftLengthBy2Plus1>>& |
| filter_frequency_response, |
| std::array<bool, kFftLengthBy2Plus1>* bands_with_reliable_filter, |
| std::array<float, kFftLengthBy2Plus1>* filter_estimate_strength, |
| rtc::Optional<size_t>* filter_delay) { |
| const auto& H2 = filter_frequency_response; |
| |
| size_t reliable_delays_sum = 0; |
| size_t num_reliable_delays = 0; |
| |
| constexpr size_t kUpperBin = kFftLengthBy2 - 5; |
| for (size_t k = 1; k < kUpperBin; ++k) { |
| int peak = 0; |
| for (size_t j = 0; j < H2.size(); ++j) { |
| if (H2[j][k] > H2[peak][k]) { |
| peak = j; |
| } |
| } |
| |
| if (H2[peak][k] == 0.f) { |
| (*filter_estimate_strength)[k] = 0.f; |
| } else if (H2[H2.size() - 1][k] == 0.f) { |
| (*filter_estimate_strength)[k] = kMaxFilterEstimateStrength; |
| } else { |
| (*filter_estimate_strength)[k] = std::min( |
| kMaxFilterEstimateStrength, H2[peak][k] / H2[H2.size() - 1][k]); |
| } |
| |
| constexpr float kMargin = 10.f; |
| if (kMargin * H2[H2.size() - 1][k] < H2[peak][k]) { |
| (*bands_with_reliable_filter)[k] = true; |
| reliable_delays_sum += peak; |
| ++num_reliable_delays; |
| } else { |
| (*bands_with_reliable_filter)[k] = false; |
| } |
| } |
| (*bands_with_reliable_filter)[0] = (*bands_with_reliable_filter)[1]; |
| std::fill(bands_with_reliable_filter->begin() + kUpperBin, |
| bands_with_reliable_filter->end(), |
| (*bands_with_reliable_filter)[kUpperBin - 1]); |
| (*filter_estimate_strength)[0] = (*filter_estimate_strength)[1]; |
| std::fill(filter_estimate_strength->begin() + kUpperBin, |
| filter_estimate_strength->end(), |
| (*filter_estimate_strength)[kUpperBin - 1]); |
| |
| *filter_delay = |
| num_reliable_delays > 20 |
| ? rtc::Optional<size_t>(reliable_delays_sum / num_reliable_delays) |
| : rtc::Optional<size_t>(); |
| } |
| |
| constexpr int kActiveRenderCounterInitial = 50; |
| constexpr int kActiveRenderCounterMax = 200; |
| constexpr int kEchoPathChangeCounterInitial = 50; |
| constexpr int kEchoPathChangeCounterMax = 200; |
| |
| } // namespace |
| |
| int AecState::instance_count_ = 0; |
| |
| AecState::AecState() |
| : data_dumper_( |
| new ApmDataDumper(rtc::AtomicOps::Increment(&instance_count_))), |
| echo_path_change_counter_(kEchoPathChangeCounterInitial), |
| active_render_counter_(kActiveRenderCounterInitial) { |
| bands_with_reliable_filter_.fill(false); |
| filter_estimate_strength_.fill(0.f); |
| } |
| |
| AecState::~AecState() = default; |
| |
| void AecState::Update(const std::vector<std::array<float, kFftLengthBy2Plus1>>& |
| filter_frequency_response, |
| const rtc::Optional<size_t>& external_delay_samples, |
| const FftBuffer& X_buffer, |
| const std::array<float, kFftLengthBy2Plus1>& E2_main, |
| const std::array<float, kFftLengthBy2Plus1>& E2_shadow, |
| const std::array<float, kFftLengthBy2Plus1>& Y2, |
| rtc::ArrayView<const float> x, |
| const EchoPathVariability& echo_path_variability, |
| bool echo_leakage_detected) { |
| filter_length_ = filter_frequency_response.size(); |
| AnalyzeFilter(filter_frequency_response, &bands_with_reliable_filter_, |
| &filter_estimate_strength_, &filter_delay_); |
| // Compute the externally provided delay in partitions. The truncation is |
| // intended here. |
| external_delay_ = |
| external_delay_samples |
| ? rtc::Optional<size_t>(*external_delay_samples / kBlockSize) |
| : rtc::Optional<size_t>(); |
| |
| const float x_energy = std::inner_product(x.begin(), x.end(), x.begin(), 0.f); |
| |
| echo_path_change_counter_ = echo_path_variability.AudioPathChanged() |
| ? kEchoPathChangeCounterMax |
| : echo_path_change_counter_ - 1; |
| active_render_counter_ = x_energy > 10000.f * kFftLengthBy2 |
| ? kActiveRenderCounterMax |
| : active_render_counter_ - 1; |
| |
| usable_linear_estimate_ = filter_delay_ && echo_path_change_counter_ <= 0; |
| |
| echo_leakage_detected_ = echo_leakage_detected; |
| |
| model_based_aec_feasible_ = usable_linear_estimate_ || external_delay_; |
| |
| if (usable_linear_estimate_) { |
| const auto& X2 = X_buffer.Spectrum(*filter_delay_); |
| |
| // TODO(peah): Expose these as stats. |
| erle_estimator_.Update(X2, Y2, E2_main); |
| erl_estimator_.Update(X2, Y2); |
| |
| // TODO(peah): Add working functionality for headset detection. Until the |
| // functionality for that is working the headset detector is hardcoded to detect |
| // no headset. |
| #if 0 |
| const auto& erl = erl_estimator_.Erl(); |
| const int low_erl_band_count = std::count_if( |
| erl.begin(), erl.end(), [](float a) { return a <= 0.1f; }); |
| |
| const int noisy_band_count = std::count_if( |
| filter_estimate_strength_.begin(), filter_estimate_strength_.end(), |
| [](float a) { return a <= 10.f; }); |
| headset_detected_ = low_erl_band_count > 20 && noisy_band_count > 20; |
| #endif |
| headset_detected_ = false; |
| } else { |
| headset_detected_ = false; |
| } |
| } |
| |
| } // namespace webrtc |