| /* |
| * 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 <algorithm> |
| #include <functional> |
| |
| #include "rtc_base/checks.h" |
| #include "rtc_base/numerics/safe_minmax.h" |
| #include "system_wrappers/include/field_trial.h" |
| |
| namespace webrtc { |
| |
| namespace { |
| |
| constexpr float kX2BandEnergyThreshold = 44015068.0f; |
| constexpr int kBlocksToHoldErle = 100; |
| constexpr int kBlocksForOnsetDetection = kBlocksToHoldErle + 150; |
| constexpr int kPointsToAccumulate = 6; |
| |
| std::array<float, kFftLengthBy2Plus1> SetMaxErleBands(float max_erle_l, |
| float max_erle_h) { |
| std::array<float, kFftLengthBy2Plus1> 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; |
| } |
| |
| bool EnableMinErleDuringOnsets() { |
| return !field_trial::IsEnabled("WebRTC-Aec3MinErleDuringOnsetsKillSwitch"); |
| } |
| |
| } // namespace |
| |
| SubbandErleEstimator::SubbandErleEstimator(const EchoCanceller3Config& config) |
| : min_erle_(config.erle.min), |
| max_erle_(SetMaxErleBands(config.erle.max_l, config.erle.max_h)), |
| use_min_erle_during_onsets_(EnableMinErleDuringOnsets()) { |
| 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<const float> X2, |
| rtc::ArrayView<const float> Y2, |
| rtc::ArrayView<const float> 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<ApmDataDumper>& data_dumper) const { |
| data_dumper->DumpRaw("aec3_erle_onset", ErleOnsets()); |
| } |
| |
| void SubbandErleEstimator::UpdateBands(bool onset_detection) { |
| std::array<float, kFftLengthBy2> new_erle; |
| std::array<bool, kFftLengthBy2> 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; |
| if (!use_min_erle_during_onsets_) { |
| 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<const float> X2, |
| rtc::ArrayView<const float> Y2, |
| rtc::ArrayView<const float> 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<float>()); |
| std::transform(E2.begin(), E2.end(), st.E2_.begin(), st.E2_.begin(), |
| std::plus<float>()); |
| |
| 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 |