AEC3: Parametrize the delay estimator to leverage strong echo paths

This CL introduces a new behavior for leveraging early information
about the delay that is acquired before the standard delay estimate
has been established.

To simplify the process of setting the parameters for that, the CL
also surfaces the delay estimator parameters to the config struct.

Bug: webrtc:9720,chromium: 880686
Change-Id: If886813f70cd805bd37752c63913d28398f1c6fe
Reviewed-on: https://webrtc-review.googlesource.com/97860
Commit-Queue: Per Åhgren <peah@webrtc.org>
Reviewed-by: Jesus de Vicente Pena <devicentepena@webrtc.org>
Reviewed-by: Gustaf Ullberg <gustaf@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#24614}
This commit is contained in:
Per Åhgren 2018-09-07 00:13:03 +02:00 committed by Commit Bot
parent 88e3e3f570
commit 6a4fd19bbd
9 changed files with 134 additions and 47 deletions

View file

@ -32,6 +32,12 @@ struct EchoCanceller3Config {
size_t hysteresis_limit_2_blocks = 1; size_t hysteresis_limit_2_blocks = 1;
size_t skew_hysteresis_blocks = 3; size_t skew_hysteresis_blocks = 3;
size_t fixed_capture_delay_samples = 0; size_t fixed_capture_delay_samples = 0;
float delay_estimate_smoothing = 0.7f;
float delay_candidate_detection_threshold = 0.2f;
struct DelaySelectionThresholds {
int initial;
int converged;
} delay_selection_thresholds = {25, 25};
} delay; } delay;
struct Filter { struct Filter {

View file

@ -47,9 +47,12 @@ EchoPathDelayEstimator::EchoPathDelayEstimator(
kMatchedFilterAlignmentShiftSizeSubBlocks, kMatchedFilterAlignmentShiftSizeSubBlocks,
GetDownSamplingFactor(config) == 8 GetDownSamplingFactor(config) == 8
? config.render_levels.poor_excitation_render_limit_ds8 ? config.render_levels.poor_excitation_render_limit_ds8
: config.render_levels.poor_excitation_render_limit), : config.render_levels.poor_excitation_render_limit,
config.delay.delay_estimate_smoothing,
config.delay.delay_candidate_detection_threshold),
matched_filter_lag_aggregator_(data_dumper_, matched_filter_lag_aggregator_(data_dumper_,
matched_filter_.GetMaxFilterLag()) { matched_filter_.GetMaxFilterLag(),
config.delay.delay_selection_thresholds) {
RTC_DCHECK(data_dumper); RTC_DCHECK(data_dumper);
RTC_DCHECK(down_sampling_factor_ > 0); RTC_DCHECK(down_sampling_factor_ > 0);
} }

View file

@ -32,6 +32,7 @@ namespace aec3 {
void MatchedFilterCore_NEON(size_t x_start_index, void MatchedFilterCore_NEON(size_t x_start_index,
float x2_sum_threshold, float x2_sum_threshold,
float smoothing,
rtc::ArrayView<const float> x, rtc::ArrayView<const float> x,
rtc::ArrayView<const float> y, rtc::ArrayView<const float> y,
rtc::ArrayView<float> h, rtc::ArrayView<float> h,
@ -102,10 +103,10 @@ void MatchedFilterCore_NEON(size_t x_start_index,
// Update the matched filter estimate in an NLMS manner. // Update the matched filter estimate in an NLMS manner.
if (x2_sum > x2_sum_threshold && !saturation) { if (x2_sum > x2_sum_threshold && !saturation) {
RTC_DCHECK_LT(0.f, x2_sum); RTC_DCHECK_LT(0.f, x2_sum);
const float alpha = 0.7f * e / x2_sum; const float alpha = smoothing * e / x2_sum;
const float32x4_t alpha_128 = vmovq_n_f32(alpha); const float32x4_t alpha_128 = vmovq_n_f32(alpha);
// filter = filter + 0.7 * (y - filter * x) / x * x. // filter = filter + smoothing * (y - filter * x) * x / x * x.
float* h_p = &h[0]; float* h_p = &h[0];
x_p = &x[x_start_index]; x_p = &x[x_start_index];
@ -145,6 +146,7 @@ void MatchedFilterCore_NEON(size_t x_start_index,
void MatchedFilterCore_SSE2(size_t x_start_index, void MatchedFilterCore_SSE2(size_t x_start_index,
float x2_sum_threshold, float x2_sum_threshold,
float smoothing,
rtc::ArrayView<const float> x, rtc::ArrayView<const float> x,
rtc::ArrayView<const float> y, rtc::ArrayView<const float> y,
rtc::ArrayView<float> h, rtc::ArrayView<float> h,
@ -217,10 +219,10 @@ void MatchedFilterCore_SSE2(size_t x_start_index,
// Update the matched filter estimate in an NLMS manner. // Update the matched filter estimate in an NLMS manner.
if (x2_sum > x2_sum_threshold && !saturation) { if (x2_sum > x2_sum_threshold && !saturation) {
RTC_DCHECK_LT(0.f, x2_sum); RTC_DCHECK_LT(0.f, x2_sum);
const float alpha = 0.7f * e / x2_sum; const float alpha = smoothing * e / x2_sum;
const __m128 alpha_128 = _mm_set1_ps(alpha); const __m128 alpha_128 = _mm_set1_ps(alpha);
// filter = filter + 0.7 * (y - filter * x) / x * x. // filter = filter + smoothing * (y - filter * x) * x / x * x.
float* h_p = &h[0]; float* h_p = &h[0];
x_p = &x[x_start_index]; x_p = &x[x_start_index];
@ -259,6 +261,7 @@ void MatchedFilterCore_SSE2(size_t x_start_index,
void MatchedFilterCore(size_t x_start_index, void MatchedFilterCore(size_t x_start_index,
float x2_sum_threshold, float x2_sum_threshold,
float smoothing,
rtc::ArrayView<const float> x, rtc::ArrayView<const float> x,
rtc::ArrayView<const float> y, rtc::ArrayView<const float> y,
rtc::ArrayView<float> h, rtc::ArrayView<float> h,
@ -288,9 +291,9 @@ void MatchedFilterCore(size_t x_start_index,
// Update the matched filter estimate in an NLMS manner. // Update the matched filter estimate in an NLMS manner.
if (x2_sum > x2_sum_threshold && !saturation) { if (x2_sum > x2_sum_threshold && !saturation) {
RTC_DCHECK_LT(0.f, x2_sum); RTC_DCHECK_LT(0.f, x2_sum);
const float alpha = 0.7f * e / x2_sum; const float alpha = smoothing * e / x2_sum;
// filter = filter + 0.7 * (y - filter * x) / x * x. // filter = filter + smoothing * (y - filter * x) * x / x * x.
size_t x_index = x_start_index; size_t x_index = x_start_index;
for (size_t k = 0; k < h.size(); ++k) { for (size_t k = 0; k < h.size(); ++k) {
h[k] += alpha * x[x_index]; h[k] += alpha * x[x_index];
@ -311,7 +314,9 @@ MatchedFilter::MatchedFilter(ApmDataDumper* data_dumper,
size_t window_size_sub_blocks, size_t window_size_sub_blocks,
int num_matched_filters, int num_matched_filters,
size_t alignment_shift_sub_blocks, size_t alignment_shift_sub_blocks,
float excitation_limit) float excitation_limit,
float smoothing,
float matching_filter_threshold)
: data_dumper_(data_dumper), : data_dumper_(data_dumper),
optimization_(optimization), optimization_(optimization),
sub_block_size_(sub_block_size), sub_block_size_(sub_block_size),
@ -321,7 +326,9 @@ MatchedFilter::MatchedFilter(ApmDataDumper* data_dumper,
std::vector<float>(window_size_sub_blocks * sub_block_size_, 0.f)), std::vector<float>(window_size_sub_blocks * sub_block_size_, 0.f)),
lag_estimates_(num_matched_filters), lag_estimates_(num_matched_filters),
filters_offsets_(num_matched_filters, 0), filters_offsets_(num_matched_filters, 0),
excitation_limit_(excitation_limit) { excitation_limit_(excitation_limit),
smoothing_(smoothing),
matching_filter_threshold_(matching_filter_threshold) {
RTC_DCHECK(data_dumper); RTC_DCHECK(data_dumper);
RTC_DCHECK_LT(0, window_size_sub_blocks); RTC_DCHECK_LT(0, window_size_sub_blocks);
RTC_DCHECK((kBlockSize % sub_block_size) == 0); RTC_DCHECK((kBlockSize % sub_block_size) == 0);
@ -362,19 +369,19 @@ void MatchedFilter::Update(const DownsampledRenderBuffer& render_buffer,
#if defined(WEBRTC_ARCH_X86_FAMILY) #if defined(WEBRTC_ARCH_X86_FAMILY)
case Aec3Optimization::kSse2: case Aec3Optimization::kSse2:
aec3::MatchedFilterCore_SSE2(x_start_index, x2_sum_threshold, aec3::MatchedFilterCore_SSE2(x_start_index, x2_sum_threshold,
render_buffer.buffer, y, filters_[n], smoothing_, render_buffer.buffer, y,
&filters_updated, &error_sum); filters_[n], &filters_updated, &error_sum);
break; break;
#endif #endif
#if defined(WEBRTC_HAS_NEON) #if defined(WEBRTC_HAS_NEON)
case Aec3Optimization::kNeon: case Aec3Optimization::kNeon:
aec3::MatchedFilterCore_NEON(x_start_index, x2_sum_threshold, aec3::MatchedFilterCore_NEON(x_start_index, x2_sum_threshold,
render_buffer.buffer, y, filters_[n], smoothing_, render_buffer.buffer, y,
&filters_updated, &error_sum); filters_[n], &filters_updated, &error_sum);
break; break;
#endif #endif
default: default:
aec3::MatchedFilterCore(x_start_index, x2_sum_threshold, aec3::MatchedFilterCore(x_start_index, x2_sum_threshold, smoothing_,
render_buffer.buffer, y, filters_[n], render_buffer.buffer, y, filters_[n],
&filters_updated, &error_sum); &filters_updated, &error_sum);
} }
@ -393,11 +400,10 @@ void MatchedFilter::Update(const DownsampledRenderBuffer& render_buffer,
[](float a, float b) -> bool { return a * a < b * b; })); [](float a, float b) -> bool { return a * a < b * b; }));
// Update the lag estimates for the matched filter. // Update the lag estimates for the matched filter.
const float kMatchingFilterThreshold = 0.2f;
lag_estimates_[n] = LagEstimate( lag_estimates_[n] = LagEstimate(
error_sum_anchor - error_sum, error_sum_anchor - error_sum,
(lag_estimate > 2 && lag_estimate < (filters_[n].size() - 10) && (lag_estimate > 2 && lag_estimate < (filters_[n].size() - 10) &&
error_sum < kMatchingFilterThreshold * error_sum_anchor), error_sum < matching_filter_threshold_ * error_sum_anchor),
lag_estimate + alignment_shift, filters_updated); lag_estimate + alignment_shift, filters_updated);
RTC_DCHECK_GE(10, filters_.size()); RTC_DCHECK_GE(10, filters_.size());

View file

@ -30,6 +30,7 @@ namespace aec3 {
// Filter core for the matched filter that is optimized for NEON. // Filter core for the matched filter that is optimized for NEON.
void MatchedFilterCore_NEON(size_t x_start_index, void MatchedFilterCore_NEON(size_t x_start_index,
float x2_sum_threshold, float x2_sum_threshold,
float smoothing,
rtc::ArrayView<const float> x, rtc::ArrayView<const float> x,
rtc::ArrayView<const float> y, rtc::ArrayView<const float> y,
rtc::ArrayView<float> h, rtc::ArrayView<float> h,
@ -43,6 +44,7 @@ void MatchedFilterCore_NEON(size_t x_start_index,
// Filter core for the matched filter that is optimized for SSE2. // Filter core for the matched filter that is optimized for SSE2.
void MatchedFilterCore_SSE2(size_t x_start_index, void MatchedFilterCore_SSE2(size_t x_start_index,
float x2_sum_threshold, float x2_sum_threshold,
float smoothing,
rtc::ArrayView<const float> x, rtc::ArrayView<const float> x,
rtc::ArrayView<const float> y, rtc::ArrayView<const float> y,
rtc::ArrayView<float> h, rtc::ArrayView<float> h,
@ -54,6 +56,7 @@ void MatchedFilterCore_SSE2(size_t x_start_index,
// Filter core for the matched filter. // Filter core for the matched filter.
void MatchedFilterCore(size_t x_start_index, void MatchedFilterCore(size_t x_start_index,
float x2_sum_threshold, float x2_sum_threshold,
float smoothing,
rtc::ArrayView<const float> x, rtc::ArrayView<const float> x,
rtc::ArrayView<const float> y, rtc::ArrayView<const float> y,
rtc::ArrayView<float> h, rtc::ArrayView<float> h,
@ -87,7 +90,9 @@ class MatchedFilter {
size_t window_size_sub_blocks, size_t window_size_sub_blocks,
int num_matched_filters, int num_matched_filters,
size_t alignment_shift_sub_blocks, size_t alignment_shift_sub_blocks,
float excitation_limit); float excitation_limit,
float smoothing,
float matching_filter_threshold);
~MatchedFilter(); ~MatchedFilter();
@ -122,6 +127,8 @@ class MatchedFilter {
std::vector<LagEstimate> lag_estimates_; std::vector<LagEstimate> lag_estimates_;
std::vector<size_t> filters_offsets_; std::vector<size_t> filters_offsets_;
const float excitation_limit_; const float excitation_limit_;
const float smoothing_;
const float matching_filter_threshold_;
RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(MatchedFilter); RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(MatchedFilter);
}; };

View file

@ -15,9 +15,13 @@ namespace webrtc {
MatchedFilterLagAggregator::MatchedFilterLagAggregator( MatchedFilterLagAggregator::MatchedFilterLagAggregator(
ApmDataDumper* data_dumper, ApmDataDumper* data_dumper,
size_t max_filter_lag) size_t max_filter_lag,
: data_dumper_(data_dumper), histogram_(max_filter_lag + 1, 0) { const EchoCanceller3Config::Delay::DelaySelectionThresholds& thresholds)
: data_dumper_(data_dumper),
histogram_(max_filter_lag + 1, 0),
thresholds_(thresholds) {
RTC_DCHECK(data_dumper); RTC_DCHECK(data_dumper);
RTC_DCHECK_LE(thresholds_.initial, thresholds_.converged);
histogram_data_.fill(0); histogram_data_.fill(0);
} }
@ -67,8 +71,12 @@ absl::optional<DelayEstimate> MatchedFilterLagAggregator::Aggregate(
std::distance(histogram_.begin(), std::distance(histogram_.begin(),
std::max_element(histogram_.begin(), histogram_.end())); std::max_element(histogram_.begin(), histogram_.end()));
if (histogram_[candidate] > 25) { significant_candidate_found_ =
significant_candidate_found_ = true; significant_candidate_found_ ||
histogram_[candidate] > thresholds_.converged;
if (histogram_[candidate] > thresholds_.converged ||
(histogram_[candidate] > thresholds_.initial &&
!significant_candidate_found_)) {
return DelayEstimate(DelayEstimate::Quality::kRefined, candidate); return DelayEstimate(DelayEstimate::Quality::kRefined, candidate);
} }
} }

View file

@ -14,6 +14,7 @@
#include <vector> #include <vector>
#include "absl/types/optional.h" #include "absl/types/optional.h"
#include "api/audio/echo_canceller3_config.h"
#include "modules/audio_processing/aec3/delay_estimate.h" #include "modules/audio_processing/aec3/delay_estimate.h"
#include "modules/audio_processing/aec3/matched_filter.h" #include "modules/audio_processing/aec3/matched_filter.h"
#include "rtc_base/constructormagic.h" #include "rtc_base/constructormagic.h"
@ -26,7 +27,10 @@ class ApmDataDumper;
// reliable combined lag estimate. // reliable combined lag estimate.
class MatchedFilterLagAggregator { class MatchedFilterLagAggregator {
public: public:
MatchedFilterLagAggregator(ApmDataDumper* data_dumper, size_t max_filter_lag); MatchedFilterLagAggregator(
ApmDataDumper* data_dumper,
size_t max_filter_lag,
const EchoCanceller3Config::Delay::DelaySelectionThresholds& thresholds);
~MatchedFilterLagAggregator(); ~MatchedFilterLagAggregator();
// Resets the aggregator. // Resets the aggregator.
@ -42,6 +46,7 @@ class MatchedFilterLagAggregator {
std::array<int, 250> histogram_data_; std::array<int, 250> histogram_data_;
int histogram_data_index_ = 0; int histogram_data_index_ = 0;
bool significant_candidate_found_ = false; bool significant_candidate_found_ = false;
const EchoCanceller3Config::Delay::DelaySelectionThresholds thresholds_;
RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(MatchedFilterLagAggregator); RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(MatchedFilterLagAggregator);
}; };

View file

@ -15,6 +15,7 @@
#include <vector> #include <vector>
#include "api/array_view.h" #include "api/array_view.h"
#include "api/audio/echo_canceller3_config.h"
#include "modules/audio_processing/aec3/aec3_common.h" #include "modules/audio_processing/aec3/aec3_common.h"
#include "modules/audio_processing/logging/apm_data_dumper.h" #include "modules/audio_processing/logging/apm_data_dumper.h"
#include "test/gtest.h" #include "test/gtest.h"
@ -31,8 +32,11 @@ TEST(MatchedFilterLagAggregator, MostAccurateLagChosen) {
constexpr size_t kLag1 = 5; constexpr size_t kLag1 = 5;
constexpr size_t kLag2 = 10; constexpr size_t kLag2 = 10;
ApmDataDumper data_dumper(0); ApmDataDumper data_dumper(0);
EchoCanceller3Config config;
std::vector<MatchedFilter::LagEstimate> lag_estimates(2); std::vector<MatchedFilter::LagEstimate> lag_estimates(2);
MatchedFilterLagAggregator aggregator(&data_dumper, std::max(kLag1, kLag2)); MatchedFilterLagAggregator aggregator(
&data_dumper, std::max(kLag1, kLag2),
config.delay.delay_selection_thresholds);
lag_estimates[0] = MatchedFilter::LagEstimate(1.f, true, kLag1, true); lag_estimates[0] = MatchedFilter::LagEstimate(1.f, true, kLag1, true);
lag_estimates[1] = MatchedFilter::LagEstimate(0.5f, true, kLag2, true); lag_estimates[1] = MatchedFilter::LagEstimate(0.5f, true, kLag2, true);
@ -65,8 +69,10 @@ TEST(MatchedFilterLagAggregator, MostAccurateLagChosen) {
TEST(MatchedFilterLagAggregator, TEST(MatchedFilterLagAggregator,
LagEstimateInvarianceRequiredForAggregatedLag) { LagEstimateInvarianceRequiredForAggregatedLag) {
ApmDataDumper data_dumper(0); ApmDataDumper data_dumper(0);
EchoCanceller3Config config;
std::vector<MatchedFilter::LagEstimate> lag_estimates(1); std::vector<MatchedFilter::LagEstimate> lag_estimates(1);
MatchedFilterLagAggregator aggregator(&data_dumper, 100); MatchedFilterLagAggregator aggregator(
&data_dumper, 100, config.delay.delay_selection_thresholds);
absl::optional<DelayEstimate> aggregated_lag; absl::optional<DelayEstimate> aggregated_lag;
for (size_t k = 0; k < kNumLagsBeforeDetection; ++k) { for (size_t k = 0; k < kNumLagsBeforeDetection; ++k) {
@ -94,8 +100,10 @@ TEST(MatchedFilterLagAggregator,
DISABLED_LagEstimateUpdatesRequiredForAggregatedLag) { DISABLED_LagEstimateUpdatesRequiredForAggregatedLag) {
constexpr size_t kLag = 5; constexpr size_t kLag = 5;
ApmDataDumper data_dumper(0); ApmDataDumper data_dumper(0);
EchoCanceller3Config config;
std::vector<MatchedFilter::LagEstimate> lag_estimates(1); std::vector<MatchedFilter::LagEstimate> lag_estimates(1);
MatchedFilterLagAggregator aggregator(&data_dumper, kLag); MatchedFilterLagAggregator aggregator(
&data_dumper, kLag, config.delay.delay_selection_thresholds);
for (size_t k = 0; k < kNumLagsBeforeDetection * 10; ++k) { for (size_t k = 0; k < kNumLagsBeforeDetection * 10; ++k) {
lag_estimates[0] = MatchedFilter::LagEstimate(1.f, true, kLag, false); lag_estimates[0] = MatchedFilter::LagEstimate(1.f, true, kLag, false);
absl::optional<DelayEstimate> aggregated_lag = absl::optional<DelayEstimate> aggregated_lag =
@ -112,8 +120,11 @@ TEST(MatchedFilterLagAggregator, DISABLED_PersistentAggregatedLag) {
constexpr size_t kLag1 = 5; constexpr size_t kLag1 = 5;
constexpr size_t kLag2 = 10; constexpr size_t kLag2 = 10;
ApmDataDumper data_dumper(0); ApmDataDumper data_dumper(0);
EchoCanceller3Config config;
std::vector<MatchedFilter::LagEstimate> lag_estimates(1); std::vector<MatchedFilter::LagEstimate> lag_estimates(1);
MatchedFilterLagAggregator aggregator(&data_dumper, std::max(kLag1, kLag2)); MatchedFilterLagAggregator aggregator(
&data_dumper, std::max(kLag1, kLag2),
config.delay.delay_selection_thresholds);
absl::optional<DelayEstimate> aggregated_lag; absl::optional<DelayEstimate> aggregated_lag;
for (size_t k = 0; k < kNumLagsBeforeDetection; ++k) { for (size_t k = 0; k < kNumLagsBeforeDetection; ++k) {
lag_estimates[0] = MatchedFilter::LagEstimate(1.f, true, kLag1, true); lag_estimates[0] = MatchedFilter::LagEstimate(1.f, true, kLag1, true);
@ -134,7 +145,10 @@ TEST(MatchedFilterLagAggregator, DISABLED_PersistentAggregatedLag) {
// Verifies the check for non-null data dumper. // Verifies the check for non-null data dumper.
TEST(MatchedFilterLagAggregator, NullDataDumper) { TEST(MatchedFilterLagAggregator, NullDataDumper) {
EXPECT_DEATH(MatchedFilterLagAggregator(nullptr, 10), ""); EchoCanceller3Config config;
EXPECT_DEATH(MatchedFilterLagAggregator(
nullptr, 10, config.delay.delay_selection_thresholds),
"");
} }
#endif #endif

View file

@ -52,6 +52,7 @@ constexpr size_t kAlignmentShiftSubBlocks = kWindowSizeSubBlocks * 3 / 4;
// counterparts. // counterparts.
TEST(MatchedFilter, TestNeonOptimizations) { TEST(MatchedFilter, TestNeonOptimizations) {
Random random_generator(42U); Random random_generator(42U);
constexpr float kSmoothing = 0.7f;
for (auto down_sampling_factor : kDownSamplingFactors) { for (auto down_sampling_factor : kDownSamplingFactors) {
const size_t sub_block_size = kBlockSize / down_sampling_factor; const size_t sub_block_size = kBlockSize / down_sampling_factor;
@ -69,10 +70,10 @@ TEST(MatchedFilter, TestNeonOptimizations) {
bool filters_updated_NEON = false; bool filters_updated_NEON = false;
float error_sum_NEON = 0.f; float error_sum_NEON = 0.f;
MatchedFilterCore_NEON(x_index, h.size() * 150.f * 150.f, x, y, h_NEON, MatchedFilterCore_NEON(x_index, h.size() * 150.f * 150.f, kSmoothing, x,
&filters_updated_NEON, &error_sum_NEON); y, h_NEON, &filters_updated_NEON, &error_sum_NEON);
MatchedFilterCore(x_index, h.size() * 150.f * 150.f, x, y, h, MatchedFilterCore(x_index, h.size() * 150.f * 150.f, kSmoothing, x, y, h,
&filters_updated, &error_sum); &filters_updated, &error_sum);
EXPECT_EQ(filters_updated, filters_updated_NEON); EXPECT_EQ(filters_updated, filters_updated_NEON);
@ -95,6 +96,7 @@ TEST(MatchedFilter, TestSse2Optimizations) {
bool use_sse2 = (WebRtc_GetCPUInfo(kSSE2) != 0); bool use_sse2 = (WebRtc_GetCPUInfo(kSSE2) != 0);
if (use_sse2) { if (use_sse2) {
Random random_generator(42U); Random random_generator(42U);
constexpr float kSmoothing = 0.7f;
for (auto down_sampling_factor : kDownSamplingFactors) { for (auto down_sampling_factor : kDownSamplingFactors) {
const size_t sub_block_size = kBlockSize / down_sampling_factor; const size_t sub_block_size = kBlockSize / down_sampling_factor;
std::vector<float> x(2000); std::vector<float> x(2000);
@ -111,11 +113,12 @@ TEST(MatchedFilter, TestSse2Optimizations) {
bool filters_updated_SSE2 = false; bool filters_updated_SSE2 = false;
float error_sum_SSE2 = 0.f; float error_sum_SSE2 = 0.f;
MatchedFilterCore_SSE2(x_index, h.size() * 150.f * 150.f, x, y, h_SSE2, MatchedFilterCore_SSE2(x_index, h.size() * 150.f * 150.f, kSmoothing, x,
&filters_updated_SSE2, &error_sum_SSE2); y, h_SSE2, &filters_updated_SSE2,
&error_sum_SSE2);
MatchedFilterCore(x_index, h.size() * 150.f * 150.f, x, y, h, MatchedFilterCore(x_index, h.size() * 150.f * 150.f, kSmoothing, x, y,
&filters_updated, &error_sum); h, &filters_updated, &error_sum);
EXPECT_EQ(filters_updated, filters_updated_SSE2); EXPECT_EQ(filters_updated, filters_updated_SSE2);
EXPECT_NEAR(error_sum, error_sum_SSE2, error_sum / 100000.f); EXPECT_NEAR(error_sum, error_sum_SSE2, error_sum / 100000.f);
@ -157,7 +160,9 @@ TEST(MatchedFilter, LagEstimation) {
delay_samples); delay_samples);
MatchedFilter filter(&data_dumper, DetectOptimization(), sub_block_size, MatchedFilter filter(&data_dumper, DetectOptimization(), sub_block_size,
kWindowSizeSubBlocks, kNumMatchedFilters, kWindowSizeSubBlocks, kNumMatchedFilters,
kAlignmentShiftSubBlocks, 150); kAlignmentShiftSubBlocks, 150,
config.delay.delay_estimate_smoothing,
config.delay.delay_candidate_detection_threshold);
std::unique_ptr<RenderDelayBuffer> render_delay_buffer( std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
RenderDelayBuffer::Create(config, 3)); RenderDelayBuffer::Create(config, 3));
@ -259,7 +264,9 @@ TEST(MatchedFilter, LagNotReliableForUncorrelatedRenderAndCapture) {
RenderDelayBuffer::Create(config, 3)); RenderDelayBuffer::Create(config, 3));
MatchedFilter filter(&data_dumper, DetectOptimization(), sub_block_size, MatchedFilter filter(&data_dumper, DetectOptimization(), sub_block_size,
kWindowSizeSubBlocks, kNumMatchedFilters, kWindowSizeSubBlocks, kNumMatchedFilters,
kAlignmentShiftSubBlocks, 150); kAlignmentShiftSubBlocks, 150,
config.delay.delay_estimate_smoothing,
config.delay.delay_candidate_detection_threshold);
// Analyze the correlation between render and capture. // Analyze the correlation between render and capture.
for (size_t k = 0; k < 100; ++k) { for (size_t k = 0; k < 100; ++k) {
@ -292,11 +299,14 @@ TEST(MatchedFilter, LagNotUpdatedForLowLevelRender) {
std::array<float, kBlockSize> capture; std::array<float, kBlockSize> capture;
capture.fill(0.f); capture.fill(0.f);
ApmDataDumper data_dumper(0); ApmDataDumper data_dumper(0);
EchoCanceller3Config config;
MatchedFilter filter(&data_dumper, DetectOptimization(), sub_block_size, MatchedFilter filter(&data_dumper, DetectOptimization(), sub_block_size,
kWindowSizeSubBlocks, kNumMatchedFilters, kWindowSizeSubBlocks, kNumMatchedFilters,
kAlignmentShiftSubBlocks, 150); kAlignmentShiftSubBlocks, 150,
config.delay.delay_estimate_smoothing,
config.delay.delay_candidate_detection_threshold);
std::unique_ptr<RenderDelayBuffer> render_delay_buffer( std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
RenderDelayBuffer::Create(EchoCanceller3Config(), 3)); RenderDelayBuffer::Create(config, 3));
Decimator capture_decimator(down_sampling_factor); Decimator capture_decimator(down_sampling_factor);
// Analyze the correlation between render and capture. // Analyze the correlation between render and capture.
@ -331,12 +341,15 @@ TEST(MatchedFilter, LagNotUpdatedForLowLevelRender) {
// number of alignment shifts. // number of alignment shifts.
TEST(MatchedFilter, NumberOfLagEstimates) { TEST(MatchedFilter, NumberOfLagEstimates) {
ApmDataDumper data_dumper(0); ApmDataDumper data_dumper(0);
EchoCanceller3Config config;
for (auto down_sampling_factor : kDownSamplingFactors) { for (auto down_sampling_factor : kDownSamplingFactors) {
const size_t sub_block_size = kBlockSize / down_sampling_factor; const size_t sub_block_size = kBlockSize / down_sampling_factor;
for (size_t num_matched_filters = 0; num_matched_filters < 10; for (size_t num_matched_filters = 0; num_matched_filters < 10;
++num_matched_filters) { ++num_matched_filters) {
MatchedFilter filter(&data_dumper, DetectOptimization(), sub_block_size, MatchedFilter filter(&data_dumper, DetectOptimization(), sub_block_size,
32, num_matched_filters, 1, 150); 32, num_matched_filters, 1, 150,
config.delay.delay_estimate_smoothing,
config.delay.delay_candidate_detection_threshold);
EXPECT_EQ(num_matched_filters, filter.GetLagEstimates().size()); EXPECT_EQ(num_matched_filters, filter.GetLagEstimates().size());
} }
} }
@ -347,13 +360,19 @@ TEST(MatchedFilter, NumberOfLagEstimates) {
// Verifies the check for non-zero windows size. // Verifies the check for non-zero windows size.
TEST(MatchedFilter, ZeroWindowSize) { TEST(MatchedFilter, ZeroWindowSize) {
ApmDataDumper data_dumper(0); ApmDataDumper data_dumper(0);
EXPECT_DEATH( EchoCanceller3Config config;
MatchedFilter(&data_dumper, DetectOptimization(), 16, 0, 1, 1, 150), ""); EXPECT_DEATH(MatchedFilter(&data_dumper, DetectOptimization(), 16, 0, 1, 1,
150, config.delay.delay_estimate_smoothing,
config.delay.delay_candidate_detection_threshold),
"");
} }
// Verifies the check for non-null data dumper. // Verifies the check for non-null data dumper.
TEST(MatchedFilter, NullDataDumper) { TEST(MatchedFilter, NullDataDumper) {
EXPECT_DEATH(MatchedFilter(nullptr, DetectOptimization(), 16, 1, 1, 1, 150), EchoCanceller3Config config;
EXPECT_DEATH(MatchedFilter(nullptr, DetectOptimization(), 16, 1, 1, 1, 150,
config.delay.delay_estimate_smoothing,
config.delay.delay_candidate_detection_threshold),
""); "");
} }
@ -361,8 +380,11 @@ TEST(MatchedFilter, NullDataDumper) {
// TODO(peah): Activate the unittest once the required code has been landed. // TODO(peah): Activate the unittest once the required code has been landed.
TEST(MatchedFilter, DISABLED_BlockSizeMultipleOf4) { TEST(MatchedFilter, DISABLED_BlockSizeMultipleOf4) {
ApmDataDumper data_dumper(0); ApmDataDumper data_dumper(0);
EXPECT_DEATH( EchoCanceller3Config config;
MatchedFilter(&data_dumper, DetectOptimization(), 15, 1, 1, 1, 150), ""); EXPECT_DEATH(MatchedFilter(&data_dumper, DetectOptimization(), 15, 1, 1, 1,
150, config.delay.delay_estimate_smoothing,
config.delay.delay_candidate_detection_threshold),
"");
} }
// Verifies the check for that there is an integer number of sub blocks that add // Verifies the check for that there is an integer number of sub blocks that add
@ -370,8 +392,11 @@ TEST(MatchedFilter, DISABLED_BlockSizeMultipleOf4) {
// TODO(peah): Activate the unittest once the required code has been landed. // TODO(peah): Activate the unittest once the required code has been landed.
TEST(MatchedFilter, DISABLED_SubBlockSizeAddsUpToBlockSize) { TEST(MatchedFilter, DISABLED_SubBlockSizeAddsUpToBlockSize) {
ApmDataDumper data_dumper(0); ApmDataDumper data_dumper(0);
EXPECT_DEATH( EchoCanceller3Config config;
MatchedFilter(&data_dumper, DetectOptimization(), 12, 1, 1, 1, 150), ""); EXPECT_DEATH(MatchedFilter(&data_dumper, DetectOptimization(), 12, 1, 1, 1,
150, config.delay.delay_estimate_smoothing,
config.delay.delay_candidate_detection_threshold),
"");
} }
#endif #endif

View file

@ -222,6 +222,19 @@ class Aec3ParametersParser {
&cfg.delay.skew_hysteresis_blocks); &cfg.delay.skew_hysteresis_blocks);
ReadParam(section, "fixed_capture_delay_samples", ReadParam(section, "fixed_capture_delay_samples",
&cfg.delay.fixed_capture_delay_samples); &cfg.delay.fixed_capture_delay_samples);
ReadParam(section, "delay_estimate_smoothing",
&cfg.delay.delay_estimate_smoothing);
ReadParam(section, "delay_candidate_detection_threshold",
&cfg.delay.delay_candidate_detection_threshold);
Json::Value subsection;
if (rtc::GetValueFromJsonObject(section, "delay_selection_thresholds",
&subsection)) {
ReadParam(subsection, "initial",
&cfg.delay.delay_selection_thresholds.initial);
ReadParam(subsection, "converged",
&cfg.delay.delay_selection_thresholds.converged);
}
} }
if (rtc::GetValueFromJsonObject(root, "filter", &section)) { if (rtc::GetValueFromJsonObject(root, "filter", &section)) {