Specially for devices with high echo path gain, even low render signal can allow the linear filter of the AEC3 to converge. However, the conditions that were used for updating the ERLE avoided to update that estimation. In this commit, we allow adapting the ERLE estimator using even low render signal but the update of the ERLE is constraint in a way that decreases are not allowed. Bug: webrtc:9776 Change-Id: Ic4331efcc47a0b05f394cdea9a88f336292de5a1 Reviewed-on: https://webrtc-review.googlesource.com/101641 Commit-Queue: Jesus de Vicente Pena <devicentepena@webrtc.org> Reviewed-by: Per Åhgren <peah@webrtc.org> Cr-Commit-Position: refs/heads/master@{#24859}
188 lines
6.3 KiB
C++
188 lines
6.3 KiB
C++
/*
|
|
* 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 <memory>
|
|
|
|
#include "absl/types/optional.h"
|
|
#include "api/array_view.h"
|
|
#include "modules/audio_processing/aec3/aec3_common.h"
|
|
#include "modules/audio_processing/logging/apm_data_dumper.h"
|
|
#include "rtc_base/numerics/safe_minmax.h"
|
|
#include "system_wrappers/include/field_trial.h"
|
|
|
|
namespace webrtc {
|
|
|
|
namespace {
|
|
constexpr int kPointsToAccumulate = 6;
|
|
constexpr float kX2BandEnergyThreshold = 44015068.0f;
|
|
constexpr int kErleHold = 100;
|
|
constexpr int kBlocksForOnsetDetection = kErleHold + 150;
|
|
|
|
bool EnableAdaptErleOnLowRender() {
|
|
return !field_trial::IsEnabled("WebRTC-Aec3AdaptErleOnLowRenderKillSwitch");
|
|
}
|
|
|
|
} // namespace
|
|
|
|
SubbandErleEstimator::SubbandErleEstimator(float min_erle,
|
|
float max_erle_lf,
|
|
float max_erle_hf)
|
|
: min_erle_(min_erle),
|
|
max_erle_lf_(max_erle_lf),
|
|
max_erle_hf_(max_erle_hf),
|
|
adapt_on_low_render_(EnableAdaptErleOnLowRender()) {
|
|
Reset();
|
|
}
|
|
|
|
SubbandErleEstimator::~SubbandErleEstimator() = default;
|
|
|
|
void SubbandErleEstimator::Reset() {
|
|
erle_.fill(min_erle_);
|
|
erle_onsets_.fill(min_erle_);
|
|
hold_counters_.fill(0);
|
|
coming_onset_.fill(true);
|
|
}
|
|
|
|
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.
|
|
constexpr size_t kFftLengthBy4 = kFftLengthBy2 / 2;
|
|
UpdateBands(X2, Y2, E2, 1, kFftLengthBy4, max_erle_lf_, onset_detection);
|
|
UpdateBands(X2, Y2, E2, kFftLengthBy4, kFftLengthBy2, max_erle_hf_,
|
|
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", Erle());
|
|
data_dumper->DumpRaw("aec3_erle_onset", ErleOnsets());
|
|
}
|
|
|
|
void SubbandErleEstimator::UpdateBands(rtc::ArrayView<const float> X2,
|
|
rtc::ArrayView<const float> Y2,
|
|
rtc::ArrayView<const float> E2,
|
|
size_t start,
|
|
size_t stop,
|
|
float max_erle,
|
|
bool onset_detection) {
|
|
auto erle_band_update = [](float erle_band, float new_erle,
|
|
bool low_render_energy, float alpha_inc,
|
|
float alpha_dec, float min_erle, float max_erle) {
|
|
if (new_erle < erle_band && low_render_energy) {
|
|
// Decreases are not allowed if low render energy signals were used for
|
|
// the erle computation.
|
|
return erle_band;
|
|
}
|
|
float alpha = new_erle > erle_band ? alpha_inc : alpha_dec;
|
|
float erle_band_out = erle_band;
|
|
erle_band_out = erle_band + alpha * (new_erle - erle_band);
|
|
erle_band_out = rtc::SafeClamp(erle_band_out, min_erle, max_erle);
|
|
return erle_band_out;
|
|
};
|
|
|
|
for (size_t k = start; k < stop; ++k) {
|
|
if (adapt_on_low_render_ || X2[k] > kX2BandEnergyThreshold) {
|
|
bool low_render_energy = false;
|
|
absl::optional<float> new_erle = instantaneous_erle_.Update(
|
|
X2[k], Y2[k], E2[k], k, &low_render_energy);
|
|
if (new_erle) {
|
|
RTC_DCHECK(adapt_on_low_render_ || !low_render_energy);
|
|
if (onset_detection && !low_render_energy) {
|
|
if (coming_onset_[k]) {
|
|
coming_onset_[k] = false;
|
|
erle_onsets_[k] = erle_band_update(
|
|
erle_onsets_[k], new_erle.value(), low_render_energy, 0.15f,
|
|
0.3f, min_erle_, max_erle);
|
|
}
|
|
hold_counters_[k] = kBlocksForOnsetDetection;
|
|
}
|
|
|
|
erle_[k] =
|
|
erle_band_update(erle_[k], new_erle.value(), low_render_energy,
|
|
0.05f, 0.1f, min_erle_, max_erle);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void SubbandErleEstimator::DecreaseErlePerBandForLowRenderSignals() {
|
|
for (size_t k = 1; k < kFftLengthBy2; ++k) {
|
|
hold_counters_[k]--;
|
|
if (hold_counters_[k] <= (kBlocksForOnsetDetection - kErleHold)) {
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
SubbandErleEstimator::ErleInstantaneous::ErleInstantaneous() {
|
|
Reset();
|
|
}
|
|
|
|
SubbandErleEstimator::ErleInstantaneous::~ErleInstantaneous() = default;
|
|
|
|
absl::optional<float> SubbandErleEstimator::ErleInstantaneous::Update(
|
|
float X2,
|
|
float Y2,
|
|
float E2,
|
|
size_t band,
|
|
bool* low_render_energy) {
|
|
absl::optional<float> erle_instantaneous = absl::nullopt;
|
|
RTC_DCHECK_LT(band, kFftLengthBy2Plus1);
|
|
Y2_acum_[band] += Y2;
|
|
E2_acum_[band] += E2;
|
|
low_render_energy_[band] =
|
|
low_render_energy_[band] || X2 < kX2BandEnergyThreshold;
|
|
if (++num_points_[band] == kPointsToAccumulate) {
|
|
if (E2_acum_[band]) {
|
|
erle_instantaneous = Y2_acum_[band] / E2_acum_[band];
|
|
}
|
|
*low_render_energy = low_render_energy_[band];
|
|
num_points_[band] = 0;
|
|
Y2_acum_[band] = 0.f;
|
|
E2_acum_[band] = 0.f;
|
|
low_render_energy_[band] = false;
|
|
}
|
|
|
|
return erle_instantaneous;
|
|
}
|
|
|
|
void SubbandErleEstimator::ErleInstantaneous::Reset() {
|
|
Y2_acum_.fill(0.f);
|
|
E2_acum_.fill(0.f);
|
|
low_render_energy_.fill(false);
|
|
num_points_.fill(0);
|
|
}
|
|
|
|
} // namespace webrtc
|