AEC3: Added filter preprocessing to avoid low frequency artefacts
This filter preprocess the time domain representation of the adaptive linear filter to avoid low-frequency components causing issues in the filter analysis. Bug: webrtc:9343, chromium:848231 Change-Id: I40494959f1b76242a7c9f2a2fc85c2ad4af9e164 Reviewed-on: https://webrtc-review.googlesource.com/79142 Commit-Queue: Per Åhgren <peah@webrtc.org> Reviewed-by: Gustaf Ullberg <gustaf@webrtc.org> Cr-Commit-Position: refs/heads/master@{#23536}
This commit is contained in:
@ -15,7 +15,10 @@
|
||||
#include <array>
|
||||
#include <numeric>
|
||||
|
||||
#include "modules/audio_processing/logging/apm_data_dumper.h"
|
||||
#include "rtc_base/atomicops.h"
|
||||
#include "rtc_base/checks.h"
|
||||
#include "system_wrappers/include/field_trial.h"
|
||||
|
||||
namespace webrtc {
|
||||
namespace {
|
||||
@ -34,17 +37,43 @@ size_t FindPeakIndex(rtc::ArrayView<const float> filter_time_domain) {
|
||||
return peak_index;
|
||||
}
|
||||
|
||||
bool EnableFilterPreprocessing() {
|
||||
return !field_trial::IsEnabled(
|
||||
"WebRTC-Aec3FilterAnalyzerPreprocessorKillSwitch");
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
int FilterAnalyzer::instance_count_ = 0;
|
||||
|
||||
FilterAnalyzer::FilterAnalyzer(const EchoCanceller3Config& config)
|
||||
: bounded_erl_(config.ep_strength.bounded_erl),
|
||||
: data_dumper_(
|
||||
new ApmDataDumper(rtc::AtomicOps::Increment(&instance_count_))),
|
||||
use_preprocessed_filter_(EnableFilterPreprocessing()),
|
||||
bounded_erl_(config.ep_strength.bounded_erl),
|
||||
default_gain_(config.ep_strength.lf),
|
||||
active_render_threshold_(config.render_levels.active_render_limit *
|
||||
config.render_levels.active_render_limit *
|
||||
kFftLengthBy2) {
|
||||
kFftLengthBy2),
|
||||
h_highpass_(GetTimeDomainLength(config.filter.main.length_blocks), 0.f) {
|
||||
Reset();
|
||||
}
|
||||
|
||||
void FilterAnalyzer::PreProcessFilter(
|
||||
rtc::ArrayView<const float> filter_time_domain) {
|
||||
RTC_DCHECK_GE(h_highpass_.capacity(), filter_time_domain.size());
|
||||
h_highpass_.resize(filter_time_domain.size());
|
||||
// Minimum phase high-pass filter with cutoff frequency at about 600 Hz.
|
||||
constexpr std::array<float, 3> h = {{0.7929742f, -0.36072128f, -0.47047766f}};
|
||||
|
||||
std::fill(h_highpass_.begin(), h_highpass_.end(), 0.f);
|
||||
for (size_t k = h.size() - 1; k < filter_time_domain.size(); ++k) {
|
||||
for (size_t j = 0; j < h.size(); ++j) {
|
||||
h_highpass_[k] += filter_time_domain[k - j] * h[j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FilterAnalyzer::~FilterAnalyzer() = default;
|
||||
|
||||
void FilterAnalyzer::Reset() {
|
||||
@ -59,31 +88,40 @@ void FilterAnalyzer::Reset() {
|
||||
|
||||
void FilterAnalyzer::Update(rtc::ArrayView<const float> filter_time_domain,
|
||||
const RenderBuffer& render_buffer) {
|
||||
size_t peak_index = FindPeakIndex(filter_time_domain);
|
||||
delay_blocks_ = peak_index / kBlockSize;
|
||||
// Preprocess the filter to avoid issues with low-frequency components in the
|
||||
// filter.
|
||||
if (use_preprocessed_filter_) {
|
||||
PreProcessFilter(filter_time_domain);
|
||||
data_dumper_->DumpRaw("aec3_linear_filter_processed_td", h_highpass_);
|
||||
}
|
||||
const auto& filter_to_analyze =
|
||||
use_preprocessed_filter_ ? h_highpass_ : filter_time_domain;
|
||||
RTC_DCHECK_EQ(filter_to_analyze.size(), filter_time_domain.size());
|
||||
|
||||
UpdateFilterGain(filter_time_domain, peak_index);
|
||||
size_t peak_index = FindPeakIndex(filter_to_analyze);
|
||||
delay_blocks_ = peak_index >> kBlockSizeLog2;
|
||||
UpdateFilterGain(filter_to_analyze, peak_index);
|
||||
|
||||
float filter_floor = 0;
|
||||
float filter_secondary_peak = 0;
|
||||
size_t limit1 = peak_index < 64 ? 0 : peak_index - 64;
|
||||
size_t limit2 =
|
||||
peak_index > filter_time_domain.size() - 129 ? 0 : peak_index + 128;
|
||||
peak_index > filter_to_analyze.size() - 129 ? 0 : peak_index + 128;
|
||||
|
||||
for (size_t k = 0; k < limit1; ++k) {
|
||||
float abs_h = fabsf(filter_time_domain[k]);
|
||||
float abs_h = fabsf(filter_to_analyze[k]);
|
||||
filter_floor += abs_h;
|
||||
filter_secondary_peak = std::max(filter_secondary_peak, abs_h);
|
||||
}
|
||||
for (size_t k = limit2; k < filter_time_domain.size(); ++k) {
|
||||
float abs_h = fabsf(filter_time_domain[k]);
|
||||
for (size_t k = limit2; k < filter_to_analyze.size(); ++k) {
|
||||
float abs_h = fabsf(filter_to_analyze[k]);
|
||||
filter_floor += abs_h;
|
||||
filter_secondary_peak = std::max(filter_secondary_peak, abs_h);
|
||||
}
|
||||
|
||||
filter_floor /= (limit1 + filter_time_domain.size() - limit2);
|
||||
filter_floor /= (limit1 + filter_to_analyze.size() - limit2);
|
||||
|
||||
float abs_peak = fabsf(filter_time_domain[peak_index]);
|
||||
float abs_peak = fabsf(filter_to_analyze[peak_index]);
|
||||
bool significant_peak_index =
|
||||
abs_peak > 10.f * filter_floor && abs_peak > 2.f * filter_secondary_peak;
|
||||
|
||||
|
||||
@ -17,11 +17,14 @@
|
||||
#include "api/audio/echo_canceller3_config.h"
|
||||
#include "api/optional.h"
|
||||
#include "modules/audio_processing/aec3/aec3_common.h"
|
||||
#include "modules/audio_processing/aec3/cascaded_biquad_filter.h"
|
||||
#include "modules/audio_processing/aec3/render_buffer.h"
|
||||
#include "rtc_base/constructormagic.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
class ApmDataDumper;
|
||||
|
||||
// Class for analyzing the properties of an adaptive filter.
|
||||
class FilterAnalyzer {
|
||||
public:
|
||||
@ -48,11 +51,15 @@ class FilterAnalyzer {
|
||||
private:
|
||||
void UpdateFilterGain(rtc::ArrayView<const float> filter_time_domain,
|
||||
size_t max_index);
|
||||
void PreProcessFilter(rtc::ArrayView<const float> filter_time_domain);
|
||||
|
||||
static int instance_count_;
|
||||
std::unique_ptr<ApmDataDumper> data_dumper_;
|
||||
const bool use_preprocessed_filter_;
|
||||
const bool bounded_erl_;
|
||||
const float default_gain_;
|
||||
const float active_render_threshold_;
|
||||
|
||||
std::vector<float> h_highpass_;
|
||||
int delay_blocks_ = 0;
|
||||
size_t blocks_since_reset_ = 0;
|
||||
bool consistent_estimate_ = false;
|
||||
|
||||
Reference in New Issue
Block a user