/* * Copyright (c) 2018 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 "modules/audio_processing/aec3/subband_erle_estimator.h" #include #include #include "rtc_base/checks.h" #include "rtc_base/numerics/safe_minmax.h" namespace webrtc { namespace { constexpr float kX2BandEnergyThreshold = 44015068.0f; constexpr int kBlocksToHoldErle = 100; constexpr int kBlocksForOnsetDetection = kBlocksToHoldErle + 150; constexpr int kPointsToAccumulate = 6; std::array SetMaxErleBands(float max_erle_l, float max_erle_h) { std::array max_erle; std::fill(max_erle.begin(), max_erle.begin() + kFftLengthBy2 / 2, max_erle_l); std::fill(max_erle.begin() + kFftLengthBy2 / 2, max_erle.end(), max_erle_h); return max_erle; } } // namespace SubbandErleEstimator::SubbandErleEstimator(const EchoCanceller3Config& config) : min_erle_(config.erle.min), max_erle_(SetMaxErleBands(config.erle.max_l, config.erle.max_h)) { Reset(); } SubbandErleEstimator::~SubbandErleEstimator() = default; void SubbandErleEstimator::Reset() { erle_.fill(min_erle_); erle_onsets_.fill(min_erle_); coming_onset_.fill(true); hold_counters_.fill(0); ResetAccumulatedSpectra(); } void SubbandErleEstimator::Update(rtc::ArrayView X2, rtc::ArrayView Y2, rtc::ArrayView E2, bool converged_filter, bool onset_detection) { if (converged_filter) { // Note that the use of the converged_filter flag already imposed // a minimum of the erle that can be estimated as that flag would // be false if the filter is performing poorly. UpdateAccumulatedSpectra(X2, Y2, E2); UpdateBands(onset_detection); } if (onset_detection) { DecreaseErlePerBandForLowRenderSignals(); } erle_[0] = erle_[1]; erle_[kFftLengthBy2] = erle_[kFftLengthBy2 - 1]; } void SubbandErleEstimator::Dump( const std::unique_ptr& data_dumper) const { data_dumper->DumpRaw("aec3_erle_onset", ErleOnsets()); } void SubbandErleEstimator::UpdateBands(bool onset_detection) { std::array new_erle; std::array is_erle_updated; is_erle_updated.fill(false); for (size_t k = 1; k < kFftLengthBy2; ++k) { if (accum_spectra_.num_points_[k] == kPointsToAccumulate && accum_spectra_.E2_[k] > 0.f) { new_erle[k] = accum_spectra_.Y2_[k] / accum_spectra_.E2_[k]; is_erle_updated[k] = true; } } if (onset_detection) { for (size_t k = 1; k < kFftLengthBy2; ++k) { if (is_erle_updated[k] && !accum_spectra_.low_render_energy_[k]) { if (coming_onset_[k]) { coming_onset_[k] = false; float alpha = new_erle[k] < erle_onsets_[k] ? 0.3f : 0.15f; erle_onsets_[k] = rtc::SafeClamp( erle_onsets_[k] + alpha * (new_erle[k] - erle_onsets_[k]), min_erle_, max_erle_[k]); } hold_counters_[k] = kBlocksForOnsetDetection; } } } for (size_t k = 1; k < kFftLengthBy2; ++k) { if (is_erle_updated[k]) { float alpha = 0.05f; if (new_erle[k] < erle_[k]) { alpha = accum_spectra_.low_render_energy_[k] ? 0.f : 0.1f; } erle_[k] = rtc::SafeClamp(erle_[k] + alpha * (new_erle[k] - erle_[k]), min_erle_, max_erle_[k]); } } } void SubbandErleEstimator::DecreaseErlePerBandForLowRenderSignals() { for (size_t k = 1; k < kFftLengthBy2; ++k) { hold_counters_[k]--; if (hold_counters_[k] <= (kBlocksForOnsetDetection - kBlocksToHoldErle)) { if (erle_[k] > erle_onsets_[k]) { erle_[k] = std::max(erle_onsets_[k], 0.97f * erle_[k]); RTC_DCHECK_LE(min_erle_, erle_[k]); } if (hold_counters_[k] <= 0) { coming_onset_[k] = true; hold_counters_[k] = 0; } } } } void SubbandErleEstimator::ResetAccumulatedSpectra() { accum_spectra_.Y2_.fill(0.f); accum_spectra_.E2_.fill(0.f); accum_spectra_.num_points_.fill(0); accum_spectra_.low_render_energy_.fill(false); } void SubbandErleEstimator::UpdateAccumulatedSpectra( rtc::ArrayView X2, rtc::ArrayView Y2, rtc::ArrayView E2) { auto& st = accum_spectra_; if (st.num_points_[0] == kPointsToAccumulate) { st.num_points_[0] = 0; st.Y2_.fill(0.f); st.E2_.fill(0.f); st.low_render_energy_.fill(false); } std::transform(Y2.begin(), Y2.end(), st.Y2_.begin(), st.Y2_.begin(), std::plus()); std::transform(E2.begin(), E2.end(), st.E2_.begin(), st.E2_.begin(), std::plus()); for (size_t k = 0; k < X2.size(); ++k) { st.low_render_energy_[k] = st.low_render_energy_[k] || X2[k] < kX2BandEnergyThreshold; } st.num_points_[0]++; st.num_points_.fill(st.num_points_[0]); } } // namespace webrtc