AEC3: Optimizing the Update method of the FilterAnalyzer class.

In this CL the analysis of the impulse response that is done in the FilterAnalyzed class is changed in order to reduce its complexity. Instead of analyzing the whole impulse response in each Update call a smaller region is analyzed. That region is changed at each Update call which implies that several calls are needed in order to analyze the complete impulse response.

Bug: webrtc:10032,chromium:909007
Change-Id: Ic58be34ba18485311c63e0fed9b6e892f9cb864c
Reviewed-on: https://webrtc-review.googlesource.com/c/111602
Reviewed-by: Per Åhgren <peah@webrtc.org>
Commit-Queue: Per Åhgren <peah@webrtc.org>
Commit-Queue: Jesus de Vicente Pena <devicentepena@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#25817}
This commit is contained in:
Jesús de Vicente Peña 2018-11-27 09:24:29 +01:00 committed by Commit Bot
parent 154a262b61
commit cf69d2209b
3 changed files with 191 additions and 83 deletions

View File

@ -151,8 +151,7 @@ void AecState::Update(
subtractor_output_analyzer_.Update(subtractor_output);
// Analyze the properties of the filter.
filter_analyzer_.Update(adaptive_filter_impulse_response,
adaptive_filter_frequency_response, render_buffer);
filter_analyzer_.Update(adaptive_filter_impulse_response, render_buffer);
// Estimate the direct path delay of the filter.
delay_state_.Update(filter_analyzer_, external_delay,

View File

@ -25,18 +25,22 @@
namespace webrtc {
namespace {
size_t FindPeakIndex(rtc::ArrayView<const float> filter_time_domain) {
size_t peak_index = 0;
float max_h2 = filter_time_domain[0] * filter_time_domain[0];
for (size_t k = 1; k < filter_time_domain.size(); ++k) {
size_t FindPeakIndex(rtc::ArrayView<const float> filter_time_domain,
size_t peak_index_in,
size_t start_sample,
size_t end_sample) {
size_t peak_index_out = peak_index_in;
float max_h2 =
filter_time_domain[peak_index_out] * filter_time_domain[peak_index_out];
for (size_t k = start_sample; k <= end_sample; ++k) {
float tmp = filter_time_domain[k] * filter_time_domain[k];
if (tmp > max_h2) {
peak_index = k;
peak_index_out = k;
max_h2 = tmp;
}
}
return peak_index;
return peak_index_out;
}
bool EnableFilterPreprocessing() {
@ -44,6 +48,11 @@ bool EnableFilterPreprocessing() {
"WebRTC-Aec3FilterAnalyzerPreprocessorKillSwitch");
}
bool EnableIncrementalAnalysis() {
return !field_trial::IsEnabled(
"WebRTC-Aec3FilterAnalyzerIncrementalAnalysisKillSwitch");
}
} // namespace
int FilterAnalyzer::instance_count_ = 0;
@ -54,46 +63,37 @@ FilterAnalyzer::FilterAnalyzer(const EchoCanceller3Config& config)
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),
use_incremental_analysis_(EnableIncrementalAnalysis()),
h_highpass_(GetTimeDomainLength(config.filter.main.length_blocks), 0.f),
filter_length_blocks_(config.filter.main_initial.length_blocks) {
filter_length_blocks_(config.filter.main_initial.length_blocks),
consistent_filter_detector_(config) {
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() {
delay_blocks_ = 0;
consistent_estimate_ = false;
blocks_since_reset_ = 0;
consistent_estimate_ = false;
consistent_estimate_counter_ = 0;
consistent_delay_reference_ = -10;
gain_ = default_gain_;
peak_index_ = 0;
ResetRegion();
consistent_filter_detector_.Reset();
}
void FilterAnalyzer::Update(
void FilterAnalyzer::Update(rtc::ArrayView<const float> filter_time_domain,
const RenderBuffer& render_buffer) {
SetRegionToAnalyze(filter_time_domain);
AnalyzeRegion(filter_time_domain, render_buffer);
}
void FilterAnalyzer::AnalyzeRegion(
rtc::ArrayView<const float> filter_time_domain,
const std::vector<std::array<float, kFftLengthBy2Plus1>>&
filter_freq_response,
const RenderBuffer& render_buffer) {
RTC_DCHECK_LT(region_.start_sample_, filter_time_domain.size());
RTC_DCHECK_LT(peak_index_, filter_time_domain.size());
RTC_DCHECK_LT(region_.end_sample_, filter_time_domain.size());
// Preprocess the filter to avoid issues with low-frequency components in the
// filter.
PreProcessFilter(filter_time_domain);
@ -103,51 +103,15 @@ void FilterAnalyzer::Update(
use_preprocessed_filter_ ? h_highpass_ : filter_time_domain;
RTC_DCHECK_EQ(filter_to_analyze.size(), filter_time_domain.size());
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_to_analyze.size() - 129 ? 0 : peak_index + 128;
for (size_t k = 0; k < limit1; ++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_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_to_analyze.size() - limit2);
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;
if (consistent_delay_reference_ != delay_blocks_ || !significant_peak_index) {
consistent_estimate_counter_ = 0;
consistent_delay_reference_ = delay_blocks_;
} else {
const auto& x = render_buffer.Block(-delay_blocks_)[0];
const float x_energy =
std::inner_product(x.begin(), x.end(), x.begin(), 0.f);
const bool active_render_block = x_energy > active_render_threshold_;
if (active_render_block) {
++consistent_estimate_counter_;
}
}
consistent_estimate_ =
consistent_estimate_counter_ > 1.5f * kNumBlocksPerSecond;
peak_index_ = FindPeakIndex(filter_to_analyze, peak_index_,
region_.start_sample_, region_.end_sample_);
delay_blocks_ = peak_index_ >> kBlockSizeLog2;
UpdateFilterGain(filter_to_analyze, peak_index_);
filter_length_blocks_ = filter_time_domain.size() * (1.f / kBlockSize);
consistent_estimate_ = consistent_filter_detector_.Detect(
filter_to_analyze, region_, render_buffer.Block(-delay_blocks_)[0],
peak_index_, delay_blocks_);
}
void FilterAnalyzer::UpdateFilterGain(
@ -169,4 +133,114 @@ void FilterAnalyzer::UpdateFilterGain(
}
}
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() + region_.start_sample_,
h_highpass_.begin() + region_.end_sample_ + 1, 0.f);
for (size_t k = std::max(h.size() - 1, region_.start_sample_);
k <= region_.end_sample_; ++k) {
for (size_t j = 0; j < h.size(); ++j) {
h_highpass_[k] += filter_time_domain[k - j] * h[j];
}
}
}
void FilterAnalyzer::ResetRegion() {
region_.start_sample_ = 0;
region_.end_sample_ = 0;
}
void FilterAnalyzer::SetRegionToAnalyze(
rtc::ArrayView<const float> filter_time_domain) {
constexpr size_t kNumberBlocksToUpdate = 1;
auto& r = region_;
if (use_incremental_analysis_) {
r.start_sample_ =
r.end_sample_ == filter_time_domain.size() - 1 ? 0 : r.end_sample_ + 1;
r.end_sample_ =
std::min(r.start_sample_ + kNumberBlocksToUpdate * kBlockSize - 1,
filter_time_domain.size() - 1);
} else {
r.start_sample_ = 0;
r.end_sample_ = filter_time_domain.size() - 1;
}
}
FilterAnalyzer::ConsistentFilterDetector::ConsistentFilterDetector(
const EchoCanceller3Config& config)
: active_render_threshold_(config.render_levels.active_render_limit *
config.render_levels.active_render_limit *
kFftLengthBy2) {}
void FilterAnalyzer::ConsistentFilterDetector::Reset() {
significant_peak_ = false;
filter_floor_accum_ = 0.f;
filter_secondary_peak_ = 0.f;
filter_floor_low_limit_ = 0;
filter_floor_high_limit_ = 0;
consistent_estimate_counter_ = 0;
consistent_delay_reference_ = -10;
}
bool FilterAnalyzer::ConsistentFilterDetector::Detect(
rtc::ArrayView<const float> filter_to_analyze,
const FilterRegion& region,
rtc::ArrayView<const float> x_block,
size_t peak_index,
int delay_blocks) {
if (region.start_sample_ == 0) {
filter_floor_accum_ = 0.f;
filter_secondary_peak_ = 0.f;
filter_floor_low_limit_ = peak_index < 64 ? 0 : peak_index - 64;
filter_floor_high_limit_ =
peak_index > filter_to_analyze.size() - 129 ? 0 : peak_index + 128;
}
for (size_t k = region.start_sample_;
k < std::min(region.end_sample_ + 1, filter_floor_low_limit_); ++k) {
float abs_h = fabsf(filter_to_analyze[k]);
filter_floor_accum_ += abs_h;
filter_secondary_peak_ = std::max(filter_secondary_peak_, abs_h);
}
for (size_t k = std::max(filter_floor_high_limit_, region.start_sample_);
k <= region.end_sample_; ++k) {
float abs_h = fabsf(filter_to_analyze[k]);
filter_floor_accum_ += abs_h;
filter_secondary_peak_ = std::max(filter_secondary_peak_, abs_h);
}
if (region.end_sample_ == filter_to_analyze.size() - 1) {
float filter_floor = filter_floor_accum_ /
(filter_floor_low_limit_ + filter_to_analyze.size() -
filter_floor_high_limit_);
float abs_peak = fabsf(filter_to_analyze[peak_index]);
significant_peak_ = abs_peak > 10.f * filter_floor &&
abs_peak > 2.f * filter_secondary_peak_;
}
if (significant_peak_) {
const float x_energy = std::inner_product(x_block.begin(), x_block.end(),
x_block.begin(), 0.f);
const bool active_render_block = x_energy > active_render_threshold_;
if (consistent_delay_reference_ == delay_blocks) {
if (active_render_block) {
++consistent_estimate_counter_;
}
} else {
consistent_estimate_counter_ = 0;
consistent_delay_reference_ = delay_blocks;
}
}
return consistent_estimate_counter_ > 1.5f * kNumBlocksPerSecond;
}
} // namespace webrtc

View File

@ -37,8 +37,6 @@ class FilterAnalyzer {
// Updates the estimates with new input data.
void Update(rtc::ArrayView<const float> filter_time_domain,
const std::vector<std::array<float, kFftLengthBy2Plus1>>&
filter_freq_response,
const RenderBuffer& render_buffer);
// Returns the delay of the filter in terms of blocks.
@ -58,24 +56,61 @@ class FilterAnalyzer {
rtc::ArrayView<const float> GetAdjustedFilter() const { return h_highpass_; }
private:
void AnalyzeRegion(rtc::ArrayView<const float> filter_time_domain,
const RenderBuffer& render_buffer);
void UpdateFilterGain(rtc::ArrayView<const float> filter_time_domain,
size_t max_index);
void PreProcessFilter(rtc::ArrayView<const float> filter_time_domain);
void ResetRegion();
void SetRegionToAnalyze(rtc::ArrayView<const float> filter_time_domain);
struct FilterRegion {
size_t start_sample_;
size_t end_sample_;
};
// This class checks whether the shape of the impulse response has been
// consistent over time.
class ConsistentFilterDetector {
public:
explicit ConsistentFilterDetector(const EchoCanceller3Config& config);
void Reset();
bool Detect(rtc::ArrayView<const float> filter_to_analyze,
const FilterRegion& region,
rtc::ArrayView<const float> x_block,
size_t peak_index,
int delay_blocks);
private:
bool significant_peak_;
float filter_floor_accum_;
float filter_secondary_peak_;
size_t filter_floor_low_limit_;
size_t filter_floor_high_limit_;
const float active_render_threshold_;
size_t consistent_estimate_counter_ = 0;
int consistent_delay_reference_ = -10;
};
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_;
const bool use_incremental_analysis_;
std::vector<float> h_highpass_;
int delay_blocks_ = 0;
size_t blocks_since_reset_ = 0;
bool consistent_estimate_ = false;
size_t consistent_estimate_counter_ = 0;
int consistent_delay_reference_ = -10;
float gain_;
size_t peak_index_;
int filter_length_blocks_;
FilterRegion region_;
ConsistentFilterDetector consistent_filter_detector_;
RTC_DISALLOW_COPY_AND_ASSIGN(FilterAnalyzer);
};