blob: 82f3dab86f439c5e72f9d83b5c3c1a9b60299fd7 [file] [log] [blame]
/*
* 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