diff --git a/api/audio/echo_canceller3_config.h b/api/audio/echo_canceller3_config.h index 4cb9fe6329..e7376ed7bc 100644 --- a/api/audio/echo_canceller3_config.h +++ b/api/audio/echo_canceller3_config.h @@ -32,6 +32,12 @@ struct EchoCanceller3Config { size_t hysteresis_limit_2_blocks = 1; size_t skew_hysteresis_blocks = 3; 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; struct Filter { diff --git a/modules/audio_processing/aec3/echo_path_delay_estimator.cc b/modules/audio_processing/aec3/echo_path_delay_estimator.cc index 4cae277c8e..638ddc4408 100644 --- a/modules/audio_processing/aec3/echo_path_delay_estimator.cc +++ b/modules/audio_processing/aec3/echo_path_delay_estimator.cc @@ -47,9 +47,12 @@ EchoPathDelayEstimator::EchoPathDelayEstimator( kMatchedFilterAlignmentShiftSizeSubBlocks, GetDownSamplingFactor(config) == 8 ? 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_.GetMaxFilterLag()) { + matched_filter_.GetMaxFilterLag(), + config.delay.delay_selection_thresholds) { RTC_DCHECK(data_dumper); RTC_DCHECK(down_sampling_factor_ > 0); } diff --git a/modules/audio_processing/aec3/matched_filter.cc b/modules/audio_processing/aec3/matched_filter.cc index 7486ddfa08..9573d93c87 100644 --- a/modules/audio_processing/aec3/matched_filter.cc +++ b/modules/audio_processing/aec3/matched_filter.cc @@ -32,6 +32,7 @@ namespace aec3 { void MatchedFilterCore_NEON(size_t x_start_index, float x2_sum_threshold, + float smoothing, rtc::ArrayView x, rtc::ArrayView y, rtc::ArrayView h, @@ -102,10 +103,10 @@ void MatchedFilterCore_NEON(size_t x_start_index, // Update the matched filter estimate in an NLMS manner. if (x2_sum > x2_sum_threshold && !saturation) { 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); - // filter = filter + 0.7 * (y - filter * x) / x * x. + // filter = filter + smoothing * (y - filter * x) * x / x * x. float* h_p = &h[0]; 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, float x2_sum_threshold, + float smoothing, rtc::ArrayView x, rtc::ArrayView y, rtc::ArrayView h, @@ -217,10 +219,10 @@ void MatchedFilterCore_SSE2(size_t x_start_index, // Update the matched filter estimate in an NLMS manner. if (x2_sum > x2_sum_threshold && !saturation) { 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); - // filter = filter + 0.7 * (y - filter * x) / x * x. + // filter = filter + smoothing * (y - filter * x) * x / x * x. float* h_p = &h[0]; 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, float x2_sum_threshold, + float smoothing, rtc::ArrayView x, rtc::ArrayView y, rtc::ArrayView h, @@ -288,9 +291,9 @@ void MatchedFilterCore(size_t x_start_index, // Update the matched filter estimate in an NLMS manner. if (x2_sum > x2_sum_threshold && !saturation) { 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; for (size_t k = 0; k < h.size(); ++k) { h[k] += alpha * x[x_index]; @@ -311,7 +314,9 @@ MatchedFilter::MatchedFilter(ApmDataDumper* data_dumper, size_t window_size_sub_blocks, int num_matched_filters, size_t alignment_shift_sub_blocks, - float excitation_limit) + float excitation_limit, + float smoothing, + float matching_filter_threshold) : data_dumper_(data_dumper), optimization_(optimization), sub_block_size_(sub_block_size), @@ -321,7 +326,9 @@ MatchedFilter::MatchedFilter(ApmDataDumper* data_dumper, std::vector(window_size_sub_blocks * sub_block_size_, 0.f)), lag_estimates_(num_matched_filters), 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_LT(0, window_size_sub_blocks); RTC_DCHECK((kBlockSize % sub_block_size) == 0); @@ -362,19 +369,19 @@ void MatchedFilter::Update(const DownsampledRenderBuffer& render_buffer, #if defined(WEBRTC_ARCH_X86_FAMILY) case Aec3Optimization::kSse2: aec3::MatchedFilterCore_SSE2(x_start_index, x2_sum_threshold, - render_buffer.buffer, y, filters_[n], - &filters_updated, &error_sum); + smoothing_, render_buffer.buffer, y, + filters_[n], &filters_updated, &error_sum); break; #endif #if defined(WEBRTC_HAS_NEON) case Aec3Optimization::kNeon: aec3::MatchedFilterCore_NEON(x_start_index, x2_sum_threshold, - render_buffer.buffer, y, filters_[n], - &filters_updated, &error_sum); + smoothing_, render_buffer.buffer, y, + filters_[n], &filters_updated, &error_sum); break; #endif default: - aec3::MatchedFilterCore(x_start_index, x2_sum_threshold, + aec3::MatchedFilterCore(x_start_index, x2_sum_threshold, smoothing_, render_buffer.buffer, y, filters_[n], &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; })); // Update the lag estimates for the matched filter. - const float kMatchingFilterThreshold = 0.2f; lag_estimates_[n] = LagEstimate( error_sum_anchor - error_sum, (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); RTC_DCHECK_GE(10, filters_.size()); diff --git a/modules/audio_processing/aec3/matched_filter.h b/modules/audio_processing/aec3/matched_filter.h index 1c06b5eb2e..2ef48286d7 100644 --- a/modules/audio_processing/aec3/matched_filter.h +++ b/modules/audio_processing/aec3/matched_filter.h @@ -30,6 +30,7 @@ namespace aec3 { // Filter core for the matched filter that is optimized for NEON. void MatchedFilterCore_NEON(size_t x_start_index, float x2_sum_threshold, + float smoothing, rtc::ArrayView x, rtc::ArrayView y, rtc::ArrayView h, @@ -43,6 +44,7 @@ void MatchedFilterCore_NEON(size_t x_start_index, // Filter core for the matched filter that is optimized for SSE2. void MatchedFilterCore_SSE2(size_t x_start_index, float x2_sum_threshold, + float smoothing, rtc::ArrayView x, rtc::ArrayView y, rtc::ArrayView h, @@ -54,6 +56,7 @@ void MatchedFilterCore_SSE2(size_t x_start_index, // Filter core for the matched filter. void MatchedFilterCore(size_t x_start_index, float x2_sum_threshold, + float smoothing, rtc::ArrayView x, rtc::ArrayView y, rtc::ArrayView h, @@ -87,7 +90,9 @@ class MatchedFilter { size_t window_size_sub_blocks, int num_matched_filters, size_t alignment_shift_sub_blocks, - float excitation_limit); + float excitation_limit, + float smoothing, + float matching_filter_threshold); ~MatchedFilter(); @@ -122,6 +127,8 @@ class MatchedFilter { std::vector lag_estimates_; std::vector filters_offsets_; const float excitation_limit_; + const float smoothing_; + const float matching_filter_threshold_; RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(MatchedFilter); }; diff --git a/modules/audio_processing/aec3/matched_filter_lag_aggregator.cc b/modules/audio_processing/aec3/matched_filter_lag_aggregator.cc index 9cd2eb3d7b..7a03e60d8b 100644 --- a/modules/audio_processing/aec3/matched_filter_lag_aggregator.cc +++ b/modules/audio_processing/aec3/matched_filter_lag_aggregator.cc @@ -15,9 +15,13 @@ namespace webrtc { MatchedFilterLagAggregator::MatchedFilterLagAggregator( ApmDataDumper* data_dumper, - size_t max_filter_lag) - : data_dumper_(data_dumper), histogram_(max_filter_lag + 1, 0) { + size_t max_filter_lag, + const EchoCanceller3Config::Delay::DelaySelectionThresholds& thresholds) + : data_dumper_(data_dumper), + histogram_(max_filter_lag + 1, 0), + thresholds_(thresholds) { RTC_DCHECK(data_dumper); + RTC_DCHECK_LE(thresholds_.initial, thresholds_.converged); histogram_data_.fill(0); } @@ -67,8 +71,12 @@ absl::optional MatchedFilterLagAggregator::Aggregate( std::distance(histogram_.begin(), std::max_element(histogram_.begin(), histogram_.end())); - if (histogram_[candidate] > 25) { - significant_candidate_found_ = true; + significant_candidate_found_ = + 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); } } diff --git a/modules/audio_processing/aec3/matched_filter_lag_aggregator.h b/modules/audio_processing/aec3/matched_filter_lag_aggregator.h index fddcfbff79..c57051aa68 100644 --- a/modules/audio_processing/aec3/matched_filter_lag_aggregator.h +++ b/modules/audio_processing/aec3/matched_filter_lag_aggregator.h @@ -14,6 +14,7 @@ #include #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/matched_filter.h" #include "rtc_base/constructormagic.h" @@ -26,7 +27,10 @@ class ApmDataDumper; // reliable combined lag estimate. class MatchedFilterLagAggregator { 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(); // Resets the aggregator. @@ -42,6 +46,7 @@ class MatchedFilterLagAggregator { std::array histogram_data_; int histogram_data_index_ = 0; bool significant_candidate_found_ = false; + const EchoCanceller3Config::Delay::DelaySelectionThresholds thresholds_; RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(MatchedFilterLagAggregator); }; diff --git a/modules/audio_processing/aec3/matched_filter_lag_aggregator_unittest.cc b/modules/audio_processing/aec3/matched_filter_lag_aggregator_unittest.cc index cea5f13ce7..e136c89877 100644 --- a/modules/audio_processing/aec3/matched_filter_lag_aggregator_unittest.cc +++ b/modules/audio_processing/aec3/matched_filter_lag_aggregator_unittest.cc @@ -15,6 +15,7 @@ #include #include "api/array_view.h" +#include "api/audio/echo_canceller3_config.h" #include "modules/audio_processing/aec3/aec3_common.h" #include "modules/audio_processing/logging/apm_data_dumper.h" #include "test/gtest.h" @@ -31,8 +32,11 @@ TEST(MatchedFilterLagAggregator, MostAccurateLagChosen) { constexpr size_t kLag1 = 5; constexpr size_t kLag2 = 10; ApmDataDumper data_dumper(0); + EchoCanceller3Config config; std::vector 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[1] = MatchedFilter::LagEstimate(0.5f, true, kLag2, true); @@ -65,8 +69,10 @@ TEST(MatchedFilterLagAggregator, MostAccurateLagChosen) { TEST(MatchedFilterLagAggregator, LagEstimateInvarianceRequiredForAggregatedLag) { ApmDataDumper data_dumper(0); + EchoCanceller3Config config; std::vector lag_estimates(1); - MatchedFilterLagAggregator aggregator(&data_dumper, 100); + MatchedFilterLagAggregator aggregator( + &data_dumper, 100, config.delay.delay_selection_thresholds); absl::optional aggregated_lag; for (size_t k = 0; k < kNumLagsBeforeDetection; ++k) { @@ -94,8 +100,10 @@ TEST(MatchedFilterLagAggregator, DISABLED_LagEstimateUpdatesRequiredForAggregatedLag) { constexpr size_t kLag = 5; ApmDataDumper data_dumper(0); + EchoCanceller3Config config; std::vector 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) { lag_estimates[0] = MatchedFilter::LagEstimate(1.f, true, kLag, false); absl::optional aggregated_lag = @@ -112,8 +120,11 @@ TEST(MatchedFilterLagAggregator, DISABLED_PersistentAggregatedLag) { constexpr size_t kLag1 = 5; constexpr size_t kLag2 = 10; ApmDataDumper data_dumper(0); + EchoCanceller3Config config; std::vector 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 aggregated_lag; for (size_t k = 0; k < kNumLagsBeforeDetection; ++k) { 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. TEST(MatchedFilterLagAggregator, NullDataDumper) { - EXPECT_DEATH(MatchedFilterLagAggregator(nullptr, 10), ""); + EchoCanceller3Config config; + EXPECT_DEATH(MatchedFilterLagAggregator( + nullptr, 10, config.delay.delay_selection_thresholds), + ""); } #endif diff --git a/modules/audio_processing/aec3/matched_filter_unittest.cc b/modules/audio_processing/aec3/matched_filter_unittest.cc index c13241f398..4353003a87 100644 --- a/modules/audio_processing/aec3/matched_filter_unittest.cc +++ b/modules/audio_processing/aec3/matched_filter_unittest.cc @@ -52,6 +52,7 @@ constexpr size_t kAlignmentShiftSubBlocks = kWindowSizeSubBlocks * 3 / 4; // counterparts. TEST(MatchedFilter, TestNeonOptimizations) { Random random_generator(42U); + constexpr float kSmoothing = 0.7f; for (auto down_sampling_factor : kDownSamplingFactors) { const size_t sub_block_size = kBlockSize / down_sampling_factor; @@ -69,10 +70,10 @@ TEST(MatchedFilter, TestNeonOptimizations) { bool filters_updated_NEON = false; float error_sum_NEON = 0.f; - MatchedFilterCore_NEON(x_index, h.size() * 150.f * 150.f, x, y, h_NEON, - &filters_updated_NEON, &error_sum_NEON); + MatchedFilterCore_NEON(x_index, h.size() * 150.f * 150.f, kSmoothing, x, + 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); EXPECT_EQ(filters_updated, filters_updated_NEON); @@ -95,6 +96,7 @@ TEST(MatchedFilter, TestSse2Optimizations) { bool use_sse2 = (WebRtc_GetCPUInfo(kSSE2) != 0); if (use_sse2) { Random random_generator(42U); + constexpr float kSmoothing = 0.7f; for (auto down_sampling_factor : kDownSamplingFactors) { const size_t sub_block_size = kBlockSize / down_sampling_factor; std::vector x(2000); @@ -111,11 +113,12 @@ TEST(MatchedFilter, TestSse2Optimizations) { bool filters_updated_SSE2 = false; float error_sum_SSE2 = 0.f; - MatchedFilterCore_SSE2(x_index, h.size() * 150.f * 150.f, x, y, h_SSE2, - &filters_updated_SSE2, &error_sum_SSE2); + MatchedFilterCore_SSE2(x_index, h.size() * 150.f * 150.f, kSmoothing, x, + y, h_SSE2, &filters_updated_SSE2, + &error_sum_SSE2); - MatchedFilterCore(x_index, h.size() * 150.f * 150.f, x, y, h, - &filters_updated, &error_sum); + MatchedFilterCore(x_index, h.size() * 150.f * 150.f, kSmoothing, x, y, + h, &filters_updated, &error_sum); EXPECT_EQ(filters_updated, filters_updated_SSE2); EXPECT_NEAR(error_sum, error_sum_SSE2, error_sum / 100000.f); @@ -157,7 +160,9 @@ TEST(MatchedFilter, LagEstimation) { delay_samples); MatchedFilter filter(&data_dumper, DetectOptimization(), sub_block_size, kWindowSizeSubBlocks, kNumMatchedFilters, - kAlignmentShiftSubBlocks, 150); + kAlignmentShiftSubBlocks, 150, + config.delay.delay_estimate_smoothing, + config.delay.delay_candidate_detection_threshold); std::unique_ptr render_delay_buffer( RenderDelayBuffer::Create(config, 3)); @@ -259,7 +264,9 @@ TEST(MatchedFilter, LagNotReliableForUncorrelatedRenderAndCapture) { RenderDelayBuffer::Create(config, 3)); MatchedFilter filter(&data_dumper, DetectOptimization(), sub_block_size, kWindowSizeSubBlocks, kNumMatchedFilters, - kAlignmentShiftSubBlocks, 150); + kAlignmentShiftSubBlocks, 150, + config.delay.delay_estimate_smoothing, + config.delay.delay_candidate_detection_threshold); // Analyze the correlation between render and capture. for (size_t k = 0; k < 100; ++k) { @@ -292,11 +299,14 @@ TEST(MatchedFilter, LagNotUpdatedForLowLevelRender) { std::array capture; capture.fill(0.f); ApmDataDumper data_dumper(0); + EchoCanceller3Config config; MatchedFilter filter(&data_dumper, DetectOptimization(), sub_block_size, kWindowSizeSubBlocks, kNumMatchedFilters, - kAlignmentShiftSubBlocks, 150); + kAlignmentShiftSubBlocks, 150, + config.delay.delay_estimate_smoothing, + config.delay.delay_candidate_detection_threshold); std::unique_ptr render_delay_buffer( - RenderDelayBuffer::Create(EchoCanceller3Config(), 3)); + RenderDelayBuffer::Create(config, 3)); Decimator capture_decimator(down_sampling_factor); // Analyze the correlation between render and capture. @@ -331,12 +341,15 @@ TEST(MatchedFilter, LagNotUpdatedForLowLevelRender) { // number of alignment shifts. TEST(MatchedFilter, NumberOfLagEstimates) { ApmDataDumper data_dumper(0); + EchoCanceller3Config config; for (auto down_sampling_factor : kDownSamplingFactors) { const size_t sub_block_size = kBlockSize / down_sampling_factor; for (size_t num_matched_filters = 0; num_matched_filters < 10; ++num_matched_filters) { 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()); } } @@ -347,13 +360,19 @@ TEST(MatchedFilter, NumberOfLagEstimates) { // Verifies the check for non-zero windows size. TEST(MatchedFilter, ZeroWindowSize) { ApmDataDumper data_dumper(0); - EXPECT_DEATH( - MatchedFilter(&data_dumper, DetectOptimization(), 16, 0, 1, 1, 150), ""); + EchoCanceller3Config config; + 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. 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. TEST(MatchedFilter, DISABLED_BlockSizeMultipleOf4) { ApmDataDumper data_dumper(0); - EXPECT_DEATH( - MatchedFilter(&data_dumper, DetectOptimization(), 15, 1, 1, 1, 150), ""); + EchoCanceller3Config config; + 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 @@ -370,8 +392,11 @@ TEST(MatchedFilter, DISABLED_BlockSizeMultipleOf4) { // TODO(peah): Activate the unittest once the required code has been landed. TEST(MatchedFilter, DISABLED_SubBlockSizeAddsUpToBlockSize) { ApmDataDumper data_dumper(0); - EXPECT_DEATH( - MatchedFilter(&data_dumper, DetectOptimization(), 12, 1, 1, 1, 150), ""); + EchoCanceller3Config config; + EXPECT_DEATH(MatchedFilter(&data_dumper, DetectOptimization(), 12, 1, 1, 1, + 150, config.delay.delay_estimate_smoothing, + config.delay.delay_candidate_detection_threshold), + ""); } #endif diff --git a/modules/audio_processing/test/audio_processing_simulator.cc b/modules/audio_processing/test/audio_processing_simulator.cc index e8b880463a..9481c98206 100644 --- a/modules/audio_processing/test/audio_processing_simulator.cc +++ b/modules/audio_processing/test/audio_processing_simulator.cc @@ -222,6 +222,19 @@ class Aec3ParametersParser { &cfg.delay.skew_hysteresis_blocks); ReadParam(section, "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", §ion)) {