/* * 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 #include #include #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>& filter_frequency_response, std::array* bands_with_reliable_filter, std::array* filter_estimate_strength, rtc::Optional* 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(reliable_delays_sum / num_reliable_delays) : rtc::Optional(); } constexpr int kActiveRenderCounterInitial = 50; constexpr int kActiveRenderCounterMax = 200; constexpr int kEchoPathChangeCounterInitial = 50; constexpr int kEchoPathChangeCounterMax = 3 * 250; } // 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>& filter_frequency_response, const rtc::Optional& external_delay_samples, const FftBuffer& X_buffer, const std::array& E2_main, const std::array& E2_shadow, const std::array& Y2, rtc::ArrayView 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(*external_delay_samples / kBlockSize) : rtc::Optional(); const float x_energy = std::inner_product(x.begin(), x.end(), x.begin(), 0.f); active_render_blocks_ = echo_path_variability.AudioPathChanged() ? 0 : active_render_blocks_ + 1; 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