AEC3: adding a milder exponential decay parameter that is used for dominant nearend regions when enabled.
Bug: webrtc:13143
Change-Id: Iedc6ff39ed5c7cd372b54a82c86354957c8852de
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/231131
Commit-Queue: Per Åhgren <peah@webrtc.org>
Reviewed-by: Per Åhgren <peah@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#34947}
diff --git a/api/audio/echo_canceller3_config.cc b/api/audio/echo_canceller3_config.cc
index b38d6b5..0224c71 100644
--- a/api/audio/echo_canceller3_config.cc
+++ b/api/audio/echo_canceller3_config.cc
@@ -166,6 +166,7 @@
res = res & Limit(&c->ep_strength.default_gain, 0.f, 1000000.f);
res = res & Limit(&c->ep_strength.default_len, -1.f, 1.f);
+ res = res & Limit(&c->ep_strength.nearend_len, -1.0f, 1.0f);
res =
res & Limit(&c->echo_audibility.low_render_limit, 0.f, 32768.f * 32768.f);
diff --git a/api/audio/echo_canceller3_config.h b/api/audio/echo_canceller3_config.h
index 087e8da..bc7c6f0 100644
--- a/api/audio/echo_canceller3_config.h
+++ b/api/audio/echo_canceller3_config.h
@@ -108,6 +108,7 @@
struct EpStrength {
float default_gain = 1.f;
float default_len = 0.83f;
+ float nearend_len = 0.83f;
bool echo_can_saturate = true;
bool bounded_erl = false;
bool erle_onset_compensation_in_dominant_nearend = false;
diff --git a/api/audio/echo_canceller3_config_json.cc b/api/audio/echo_canceller3_config_json.cc
index 263599c..eaf95b3 100644
--- a/api/audio/echo_canceller3_config_json.cc
+++ b/api/audio/echo_canceller3_config_json.cc
@@ -259,6 +259,7 @@
if (rtc::GetValueFromJsonObject(aec3_root, "ep_strength", §ion)) {
ReadParam(section, "default_gain", &cfg.ep_strength.default_gain);
ReadParam(section, "default_len", &cfg.ep_strength.default_len);
+ ReadParam(section, "nearend_len", &cfg.ep_strength.nearend_len);
ReadParam(section, "echo_can_saturate", &cfg.ep_strength.echo_can_saturate);
ReadParam(section, "bounded_erl", &cfg.ep_strength.bounded_erl);
ReadParam(section, "erle_onset_compensation_in_dominant_nearend",
@@ -560,6 +561,7 @@
ost << "\"ep_strength\": {";
ost << "\"default_gain\": " << config.ep_strength.default_gain << ",";
ost << "\"default_len\": " << config.ep_strength.default_len << ",";
+ ost << "\"nearend_len\": " << config.ep_strength.nearend_len << ",";
ost << "\"echo_can_saturate\": "
<< (config.ep_strength.echo_can_saturate ? "true" : "false") << ",";
ost << "\"bounded_erl\": "
diff --git a/api/audio/test/echo_canceller3_config_json_unittest.cc b/api/audio/test/echo_canceller3_config_json_unittest.cc
index d6edd07..bb28b4f 100644
--- a/api/audio/test/echo_canceller3_config_json_unittest.cc
+++ b/api/audio/test/echo_canceller3_config_json_unittest.cc
@@ -37,6 +37,8 @@
// Expect unchanged values to remain default.
EXPECT_EQ(cfg.ep_strength.default_len,
cfg_transformed.ep_strength.default_len);
+ EXPECT_EQ(cfg.ep_strength.nearend_len,
+ cfg_transformed.ep_strength.nearend_len);
EXPECT_EQ(cfg.suppressor.normal_tuning.mask_lf.enr_suppress,
cfg_transformed.suppressor.normal_tuning.mask_lf.enr_suppress);
diff --git a/modules/audio_processing/aec3/aec_state.cc b/modules/audio_processing/aec3/aec_state.cc
index 21cad21..b09acfd 100644
--- a/modules/audio_processing/aec3/aec_state.cc
+++ b/modules/audio_processing/aec3/aec_state.cc
@@ -229,8 +229,9 @@
std::array<float, kFftLengthBy2Plus1> avg_render_spectrum_with_reverb;
ComputeAvgRenderReverb(render_buffer.GetSpectrumBuffer(),
- delay_state_.MinDirectPathFilterDelay(), ReverbDecay(),
- &avg_render_reverb_, avg_render_spectrum_with_reverb);
+ delay_state_.MinDirectPathFilterDelay(),
+ ReverbDecay(/*mild=*/false), &avg_render_reverb_,
+ avg_render_spectrum_with_reverb);
if (config_.echo_audibility.use_stationarity_properties) {
// Update the echo audibility evaluator.
diff --git a/modules/audio_processing/aec3/aec_state.h b/modules/audio_processing/aec3/aec_state.h
index e2f70a4..5994465 100644
--- a/modules/audio_processing/aec3/aec_state.h
+++ b/modules/audio_processing/aec3/aec_state.h
@@ -116,8 +116,12 @@
// Takes appropriate action at an echo path change.
void HandleEchoPathChange(const EchoPathVariability& echo_path_variability);
- // Returns the decay factor for the echo reverberation.
- float ReverbDecay() const { return reverb_model_estimator_.ReverbDecay(); }
+ // Returns the decay factor for the echo reverberation. The parameter `mild`
+ // indicates which exponential decay to return. The default one or a milder
+ // one that can be used during nearend regions.
+ float ReverbDecay(bool mild) const {
+ return reverb_model_estimator_.ReverbDecay(mild);
+ }
// Return the frequency response of the reverberant echo.
rtc::ArrayView<const float> GetReverbFrequencyResponse() const {
diff --git a/modules/audio_processing/aec3/echo_canceller3.cc b/modules/audio_processing/aec3/echo_canceller3.cc
index 181b649..a0432e6 100644
--- a/modules/audio_processing/aec3/echo_canceller3.cc
+++ b/modules/audio_processing/aec3/echo_canceller3.cc
@@ -267,20 +267,23 @@
adjusted_cfg.ep_strength.echo_can_saturate = false;
}
- if (field_trial::IsEnabled("WebRTC-Aec3UseDot2ReverbDefaultLen")) {
- adjusted_cfg.ep_strength.default_len = 0.2f;
- } else if (field_trial::IsEnabled("WebRTC-Aec3UseDot3ReverbDefaultLen")) {
- adjusted_cfg.ep_strength.default_len = 0.3f;
- } else if (field_trial::IsEnabled("WebRTC-Aec3UseDot4ReverbDefaultLen")) {
- adjusted_cfg.ep_strength.default_len = 0.4f;
- } else if (field_trial::IsEnabled("WebRTC-Aec3UseDot5ReverbDefaultLen")) {
- adjusted_cfg.ep_strength.default_len = 0.5f;
- } else if (field_trial::IsEnabled("WebRTC-Aec3UseDot6ReverbDefaultLen")) {
- adjusted_cfg.ep_strength.default_len = 0.6f;
- } else if (field_trial::IsEnabled("WebRTC-Aec3UseDot7ReverbDefaultLen")) {
- adjusted_cfg.ep_strength.default_len = 0.7f;
- } else if (field_trial::IsEnabled("WebRTC-Aec3UseDot8ReverbDefaultLen")) {
- adjusted_cfg.ep_strength.default_len = 0.8f;
+ const std::string use_nearend_reverb_len_tunings =
+ field_trial::FindFullName("WebRTC-Aec3UseNearendReverbLen");
+ FieldTrialParameter<double> nearend_reverb_default_len(
+ "default_len", adjusted_cfg.ep_strength.default_len);
+ FieldTrialParameter<double> nearend_reverb_nearend_len(
+ "nearend_len", adjusted_cfg.ep_strength.nearend_len);
+
+ ParseFieldTrial({&nearend_reverb_default_len, &nearend_reverb_nearend_len},
+ use_nearend_reverb_len_tunings);
+ float default_len = static_cast<float>(nearend_reverb_default_len.Get());
+ float nearend_len = static_cast<float>(nearend_reverb_nearend_len.Get());
+ if (default_len > -1 && default_len < 1 && nearend_len > -1 &&
+ nearend_len < 1) {
+ adjusted_cfg.ep_strength.default_len =
+ static_cast<float>(nearend_reverb_default_len.Get());
+ adjusted_cfg.ep_strength.nearend_len =
+ static_cast<float>(nearend_reverb_nearend_len.Get());
}
if (field_trial::IsEnabled("WebRTC-Aec3ShortHeadroomKillSwitch")) {
@@ -459,8 +462,6 @@
FieldTrialParameter<int> dominant_nearend_detection_trigger_threshold(
"dominant_nearend_detection_trigger_threshold",
adjusted_cfg.suppressor.dominant_nearend_detection.trigger_threshold);
- FieldTrialParameter<double> ep_strength_default_len(
- "ep_strength_default_len", adjusted_cfg.ep_strength.default_len);
ParseFieldTrial(
{&nearend_tuning_mask_lf_enr_transparent,
@@ -477,7 +478,7 @@
&dominant_nearend_detection_enr_exit_threshold,
&dominant_nearend_detection_snr_threshold,
&dominant_nearend_detection_hold_duration,
- &dominant_nearend_detection_trigger_threshold, &ep_strength_default_len},
+ &dominant_nearend_detection_trigger_threshold},
suppressor_tuning_override_trial_name);
adjusted_cfg.suppressor.nearend_tuning.mask_lf.enr_transparent =
@@ -514,8 +515,6 @@
dominant_nearend_detection_hold_duration.Get();
adjusted_cfg.suppressor.dominant_nearend_detection.trigger_threshold =
dominant_nearend_detection_trigger_threshold.Get();
- adjusted_cfg.ep_strength.default_len =
- static_cast<float>(ep_strength_default_len.Get());
// Field trial-based overrides of individual suppressor parameters.
RetrieveFieldTrialValue(
@@ -577,15 +576,13 @@
"WebRTC-Aec3SuppressorAntiHowlingGainOverride", 0.f, 10.f,
&adjusted_cfg.suppressor.high_bands_suppression.anti_howling_gain);
- RetrieveFieldTrialValue("WebRTC-Aec3SuppressorEpStrengthDefaultLenOverride",
- -1.f, 1.f, &adjusted_cfg.ep_strength.default_len);
-
// Field trial-based overrides of individual delay estimator parameters.
RetrieveFieldTrialValue("WebRTC-Aec3DelayEstimateSmoothingOverride", 0.f, 1.f,
&adjusted_cfg.delay.delay_estimate_smoothing);
RetrieveFieldTrialValue(
"WebRTC-Aec3DelayEstimateSmoothingDelayFoundOverride", 0.f, 1.f,
&adjusted_cfg.delay.delay_estimate_smoothing_delay_found);
+
return adjusted_cfg;
}
diff --git a/modules/audio_processing/aec3/echo_canceller3_unittest.cc b/modules/audio_processing/aec3/echo_canceller3_unittest.cc
index 4a3c466..b405e0c 100644
--- a/modules/audio_processing/aec3/echo_canceller3_unittest.cc
+++ b/modules/audio_processing/aec3/echo_canceller3_unittest.cc
@@ -698,23 +698,6 @@
}
// Tests the parameter functionality for the field trial override for the
-// default_len parameter.
-TEST(EchoCanceller3FieldTrials, Aec3SuppressorEpStrengthDefaultLenOverride) {
- EchoCanceller3Config default_config;
- EchoCanceller3Config adjusted_config = AdjustConfig(default_config);
- ASSERT_EQ(default_config.ep_strength.default_len,
- adjusted_config.ep_strength.default_len);
-
- webrtc::test::ScopedFieldTrials field_trials(
- "WebRTC-Aec3SuppressorEpStrengthDefaultLenOverride/-0.02/");
- adjusted_config = AdjustConfig(default_config);
-
- ASSERT_NE(default_config.ep_strength.default_len,
- adjusted_config.ep_strength.default_len);
- EXPECT_FLOAT_EQ(-0.02f, adjusted_config.ep_strength.default_len);
-}
-
-// Tests the parameter functionality for the field trial override for the
// anti-howling gain.
TEST(EchoCanceller3FieldTrials, Aec3SuppressorAntiHowlingGainOverride) {
EchoCanceller3Config default_config;
@@ -767,7 +750,7 @@
"detection_enr_threshold:1.3,dominant_nearend_detection_enr_exit_"
"threshold:1.4,dominant_nearend_detection_snr_threshold:1.5,dominant_"
"nearend_detection_hold_duration:10,dominant_nearend_detection_trigger_"
- "threshold:11,ep_strength_default_len:1.6/");
+ "threshold:11/");
EchoCanceller3Config default_config;
EchoCanceller3Config adjusted_config = AdjustConfig(default_config);
@@ -808,8 +791,6 @@
ASSERT_NE(
adjusted_config.suppressor.dominant_nearend_detection.trigger_threshold,
default_config.suppressor.dominant_nearend_detection.trigger_threshold);
- ASSERT_NE(adjusted_config.ep_strength.default_len,
- default_config.ep_strength.default_len);
EXPECT_FLOAT_EQ(
adjusted_config.suppressor.nearend_tuning.mask_lf.enr_transparent, 0.1);
@@ -846,7 +827,6 @@
EXPECT_EQ(
adjusted_config.suppressor.dominant_nearend_detection.trigger_threshold,
11);
- EXPECT_FLOAT_EQ(adjusted_config.ep_strength.default_len, 1.6);
}
// Testing the field trial-based override of the suppressor parameters for
@@ -900,6 +880,16 @@
0.5);
}
+// Testing the field trial-based that override the exponential decay parameters.
+TEST(EchoCanceller3FieldTrials, Aec3UseNearendReverb) {
+ webrtc::test::ScopedFieldTrials field_trials(
+ "WebRTC-Aec3UseNearendReverbLen/default_len:0.9,nearend_len:0.8/");
+ EchoCanceller3Config default_config;
+ EchoCanceller3Config adjusted_config = AdjustConfig(default_config);
+ EXPECT_FLOAT_EQ(adjusted_config.ep_strength.default_len, 0.9);
+ EXPECT_FLOAT_EQ(adjusted_config.ep_strength.nearend_len, 0.8);
+}
+
#if RTC_DCHECK_IS_ON && GTEST_HAS_DEATH_TEST && !defined(WEBRTC_ANDROID)
TEST(EchoCanceller3InputCheckDeathTest, WrongCaptureNumBandsCheckVerification) {
diff --git a/modules/audio_processing/aec3/residual_echo_estimator.cc b/modules/audio_processing/aec3/residual_echo_estimator.cc
index 15bebec..640a3e3 100644
--- a/modules/audio_processing/aec3/residual_echo_estimator.cc
+++ b/modules/audio_processing/aec3/residual_echo_estimator.cc
@@ -203,7 +203,8 @@
LinearEstimate(S2_linear, aec_state.ErleUnbounded(), R2_unbounded);
}
- UpdateReverb(ReverbType::kLinear, aec_state, render_buffer);
+ UpdateReverb(ReverbType::kLinear, aec_state, render_buffer,
+ dominant_nearend);
AddReverb(R2);
AddReverb(R2_unbounded);
} else {
@@ -240,7 +241,8 @@
if (config_.echo_model.model_reverb_in_nonlinear_mode &&
!aec_state.TransparentModeActive()) {
- UpdateReverb(ReverbType::kNonLinear, aec_state, render_buffer);
+ UpdateReverb(ReverbType::kNonLinear, aec_state, render_buffer,
+ dominant_nearend);
AddReverb(R2);
AddReverb(R2_unbounded);
}
@@ -305,7 +307,8 @@
// Updates the reverb estimation.
void ResidualEchoEstimator::UpdateReverb(ReverbType reverb_type,
const AecState& aec_state,
- const RenderBuffer& render_buffer) {
+ const RenderBuffer& render_buffer,
+ bool dominant_nearend) {
// Choose reverb partition based on what type of echo power model is used.
const size_t first_reverb_partition =
reverb_type == ReverbType::kLinear
@@ -330,15 +333,15 @@
}
// Update the reverb estimate.
+ float reverb_decay = aec_state.ReverbDecay(/*mild=*/dominant_nearend);
if (reverb_type == ReverbType::kLinear) {
- echo_reverb_.UpdateReverb(render_power,
- aec_state.GetReverbFrequencyResponse(),
- aec_state.ReverbDecay());
+ echo_reverb_.UpdateReverb(
+ render_power, aec_state.GetReverbFrequencyResponse(), reverb_decay);
} else {
const float echo_path_gain =
GetEchoPathGain(aec_state, /*gain_for_early_reflections=*/false);
echo_reverb_.UpdateReverbNoFreqShaping(render_power, echo_path_gain,
- aec_state.ReverbDecay());
+ reverb_decay);
}
}
// Adds the estimated power of the reverb to the residual echo power.
diff --git a/modules/audio_processing/aec3/residual_echo_estimator.h b/modules/audio_processing/aec3/residual_echo_estimator.h
index c071854..c468764 100644
--- a/modules/audio_processing/aec3/residual_echo_estimator.h
+++ b/modules/audio_processing/aec3/residual_echo_estimator.h
@@ -56,7 +56,8 @@
// Updates the reverb estimation.
void UpdateReverb(ReverbType reverb_type,
const AecState& aec_state,
- const RenderBuffer& render_buffer);
+ const RenderBuffer& render_buffer,
+ bool dominant_nearend);
// Adds the estimated unmodelled echo power to the residual echo power
// estimate.
diff --git a/modules/audio_processing/aec3/residual_echo_estimator_unittest.cc b/modules/audio_processing/aec3/residual_echo_estimator_unittest.cc
index 3d760b7..05a6103 100644
--- a/modules/audio_processing/aec3/residual_echo_estimator_unittest.cc
+++ b/modules/audio_processing/aec3/residual_echo_estimator_unittest.cc
@@ -10,6 +10,8 @@
#include "modules/audio_processing/aec3/residual_echo_estimator.h"
+#include <numeric>
+
#include "api/audio/echo_canceller3_config.h"
#include "modules/audio_processing/aec3/aec3_fft.h"
#include "modules/audio_processing/aec3/aec_state.h"
@@ -21,6 +23,109 @@
namespace webrtc {
+namespace {
+constexpr int kSampleRateHz = 48000;
+constexpr size_t kNumBands = NumBandsForRate(kSampleRateHz);
+constexpr float kEpsilon = 1e-4f;
+} // namespace
+
+class ResidualEchoEstimatorTest {
+ public:
+ ResidualEchoEstimatorTest(size_t num_render_channels,
+ size_t num_capture_channels,
+ const EchoCanceller3Config& config)
+ : num_render_channels_(num_render_channels),
+ num_capture_channels_(num_capture_channels),
+ config_(config),
+ estimator_(config_, num_render_channels_),
+ aec_state_(config_, num_capture_channels_),
+ render_delay_buffer_(RenderDelayBuffer::Create(config_,
+ kSampleRateHz,
+ num_render_channels_)),
+ E2_refined_(num_capture_channels_),
+ S2_linear_(num_capture_channels_),
+ Y2_(num_capture_channels_),
+ R2_(num_capture_channels_),
+ R2_unbounded_(num_capture_channels_),
+ x_(kNumBands,
+ std::vector<std::vector<float>>(
+ num_render_channels_,
+ std::vector<float>(kBlockSize, 0.0f))),
+ H2_(num_capture_channels_,
+ std::vector<std::array<float, kFftLengthBy2Plus1>>(10)),
+ h_(num_capture_channels_,
+ std::vector<float>(
+ GetTimeDomainLength(config_.filter.refined.length_blocks),
+ 0.0f)),
+ random_generator_(42U),
+ output_(num_capture_channels_) {
+ for (auto& H2_ch : H2_) {
+ for (auto& H2_k : H2_ch) {
+ H2_k.fill(0.01f);
+ }
+ H2_ch[2].fill(10.f);
+ H2_ch[2][0] = 0.1f;
+ }
+
+ for (auto& subtractor_output : output_) {
+ subtractor_output.Reset();
+ subtractor_output.s_refined.fill(100.f);
+ }
+ y_.fill(0.f);
+
+ constexpr float kLevel = 10.f;
+ for (auto& E2_refined_ch : E2_refined_) {
+ E2_refined_ch.fill(kLevel);
+ }
+ S2_linear_[0].fill(kLevel);
+ for (auto& Y2_ch : Y2_) {
+ Y2_ch.fill(kLevel);
+ }
+ }
+
+ void RunOneFrame(bool dominant_nearend) {
+ RandomizeSampleVector(&random_generator_, x_[0][0]);
+ render_delay_buffer_->Insert(x_);
+ if (first_frame_) {
+ render_delay_buffer_->Reset();
+ first_frame_ = false;
+ }
+ render_delay_buffer_->PrepareCaptureProcessing();
+
+ aec_state_.Update(delay_estimate_, H2_, h_,
+ *render_delay_buffer_->GetRenderBuffer(), E2_refined_,
+ Y2_, output_);
+
+ estimator_.Estimate(aec_state_, *render_delay_buffer_->GetRenderBuffer(),
+ S2_linear_, Y2_, dominant_nearend, R2_, R2_unbounded_);
+ }
+
+ rtc::ArrayView<const std::array<float, kFftLengthBy2Plus1>> R2() const {
+ return R2_;
+ }
+
+ private:
+ const size_t num_render_channels_;
+ const size_t num_capture_channels_;
+ const EchoCanceller3Config& config_;
+ ResidualEchoEstimator estimator_;
+ AecState aec_state_;
+ std::unique_ptr<RenderDelayBuffer> render_delay_buffer_;
+ std::vector<std::array<float, kFftLengthBy2Plus1>> E2_refined_;
+ std::vector<std::array<float, kFftLengthBy2Plus1>> S2_linear_;
+ std::vector<std::array<float, kFftLengthBy2Plus1>> Y2_;
+ std::vector<std::array<float, kFftLengthBy2Plus1>> R2_;
+ std::vector<std::array<float, kFftLengthBy2Plus1>> R2_unbounded_;
+ std::vector<std::vector<std::vector<float>>> x_;
+ std::vector<std::vector<std::array<float, kFftLengthBy2Plus1>>> H2_;
+ std::vector<std::vector<float>> h_;
+ Random random_generator_;
+ std::vector<SubtractorOutput> output_;
+ std::array<float, kBlockSize> y_;
+ absl::optional<DelayEstimate> delay_estimate_;
+ bool first_frame_ = true;
+};
+
class ResidualEchoEstimatorMultiChannel
: public ::testing::Test,
public ::testing::WithParamInterface<std::tuple<size_t, size_t>> {};
@@ -33,77 +138,63 @@
TEST_P(ResidualEchoEstimatorMultiChannel, BasicTest) {
const size_t num_render_channels = std::get<0>(GetParam());
const size_t num_capture_channels = std::get<1>(GetParam());
- constexpr int kSampleRateHz = 48000;
- constexpr size_t kNumBands = NumBandsForRate(kSampleRateHz);
EchoCanceller3Config config;
- ResidualEchoEstimator estimator(config, num_render_channels);
- AecState aec_state(config, num_capture_channels);
- std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
- RenderDelayBuffer::Create(config, kSampleRateHz, num_render_channels));
-
- std::vector<std::array<float, kFftLengthBy2Plus1>> E2_refined(
- num_capture_channels);
- std::vector<std::array<float, kFftLengthBy2Plus1>> S2_linear(
- num_capture_channels);
- std::vector<std::array<float, kFftLengthBy2Plus1>> Y2(num_capture_channels);
- std::vector<std::array<float, kFftLengthBy2Plus1>> R2(num_capture_channels);
- std::vector<std::array<float, kFftLengthBy2Plus1>> R2_unbounded(
- num_capture_channels);
- std::vector<std::vector<std::vector<float>>> x(
- kNumBands, std::vector<std::vector<float>>(
- num_render_channels, std::vector<float>(kBlockSize, 0.f)));
- std::vector<std::vector<std::array<float, kFftLengthBy2Plus1>>> H2(
- num_capture_channels,
- std::vector<std::array<float, kFftLengthBy2Plus1>>(10));
- Random random_generator(42U);
- std::vector<SubtractorOutput> output(num_capture_channels);
- std::array<float, kBlockSize> y;
- absl::optional<DelayEstimate> delay_estimate;
-
- for (auto& H2_ch : H2) {
- for (auto& H2_k : H2_ch) {
- H2_k.fill(0.01f);
- }
- H2_ch[2].fill(10.f);
- H2_ch[2][0] = 0.1f;
- }
-
- std::vector<std::vector<float>> h(
- num_capture_channels,
- std::vector<float>(
- GetTimeDomainLength(config.filter.refined.length_blocks), 0.f));
-
- for (auto& subtractor_output : output) {
- subtractor_output.Reset();
- subtractor_output.s_refined.fill(100.f);
- }
- y.fill(0.f);
-
- constexpr float kLevel = 10.f;
- for (auto& E2_refined_ch : E2_refined) {
- E2_refined_ch.fill(kLevel);
- }
- S2_linear[0].fill(kLevel);
- for (auto& Y2_ch : Y2) {
- Y2_ch.fill(kLevel);
- }
-
+ ResidualEchoEstimatorTest residual_echo_estimator_test(
+ num_render_channels, num_capture_channels, config);
for (int k = 0; k < 1993; ++k) {
- RandomizeSampleVector(&random_generator, x[0][0]);
- render_delay_buffer->Insert(x);
- if (k == 0) {
- render_delay_buffer->Reset();
+ residual_echo_estimator_test.RunOneFrame(/*dominant_nearend=*/false);
+ }
+}
+
+TEST(ResidualEchoEstimatorMultiChannel, ReverbTest) {
+ const size_t num_render_channels = 1;
+ const size_t num_capture_channels = 1;
+ const size_t nFrames = 100;
+
+ EchoCanceller3Config reference_config;
+ reference_config.ep_strength.default_len = 0.95f;
+ reference_config.ep_strength.nearend_len = 0.95f;
+ EchoCanceller3Config config_use_nearend_len = reference_config;
+ config_use_nearend_len.ep_strength.default_len = 0.95f;
+ config_use_nearend_len.ep_strength.nearend_len = 0.83f;
+
+ ResidualEchoEstimatorTest reference_residual_echo_estimator_test(
+ num_render_channels, num_capture_channels, reference_config);
+ ResidualEchoEstimatorTest use_nearend_len_residual_echo_estimator_test(
+ num_render_channels, num_capture_channels, config_use_nearend_len);
+
+ std::vector<float> acum_energy_reference_R2(num_capture_channels, 0.0f);
+ std::vector<float> acum_energy_R2(num_capture_channels, 0.0f);
+ for (size_t frame = 0; frame < nFrames; ++frame) {
+ bool dominant_nearend = frame <= nFrames / 2 ? false : true;
+ reference_residual_echo_estimator_test.RunOneFrame(dominant_nearend);
+ use_nearend_len_residual_echo_estimator_test.RunOneFrame(dominant_nearend);
+ const auto& reference_R2 = reference_residual_echo_estimator_test.R2();
+ const auto& R2 = use_nearend_len_residual_echo_estimator_test.R2();
+ ASSERT_EQ(reference_R2.size(), R2.size());
+ for (size_t ch = 0; ch < reference_R2.size(); ++ch) {
+ float energy_reference_R2 = std::accumulate(
+ reference_R2[ch].cbegin(), reference_R2[ch].cend(), 0.0f);
+ float energy_R2 = std::accumulate(R2[ch].cbegin(), R2[ch].cend(), 0.0f);
+ if (dominant_nearend) {
+ EXPECT_GE(energy_reference_R2, energy_R2);
+ } else {
+ EXPECT_NEAR(energy_reference_R2, energy_R2, kEpsilon);
+ }
+ acum_energy_reference_R2[ch] += energy_reference_R2;
+ acum_energy_R2[ch] += energy_R2;
}
- render_delay_buffer->PrepareCaptureProcessing();
-
- aec_state.Update(delay_estimate, H2, h,
- *render_delay_buffer->GetRenderBuffer(), E2_refined, Y2,
- output);
-
- estimator.Estimate(aec_state, *render_delay_buffer->GetRenderBuffer(),
- S2_linear, Y2, /*dominant_nearend=*/false, R2,
- R2_unbounded);
+ if (frame == nFrames / 2 || frame == nFrames - 1) {
+ for (size_t ch = 0; ch < acum_energy_reference_R2.size(); ch++) {
+ if (dominant_nearend) {
+ EXPECT_GT(acum_energy_reference_R2[ch], acum_energy_R2[ch]);
+ } else {
+ EXPECT_NEAR(acum_energy_reference_R2[ch], acum_energy_R2[ch],
+ kEpsilon);
+ }
+ }
+ }
}
}
diff --git a/modules/audio_processing/aec3/reverb_decay_estimator.cc b/modules/audio_processing/aec3/reverb_decay_estimator.cc
index f160b83..24f579b 100644
--- a/modules/audio_processing/aec3/reverb_decay_estimator.cc
+++ b/modules/audio_processing/aec3/reverb_decay_estimator.cc
@@ -93,7 +93,8 @@
late_reverb_start_(kEarlyReverbMinSizeBlocks),
late_reverb_end_(kEarlyReverbMinSizeBlocks),
previous_gains_(config.filter.refined.length_blocks, 0.f),
- decay_(std::fabs(config.ep_strength.default_len)) {
+ decay_(std::fabs(config.ep_strength.default_len)),
+ mild_decay_(std::fabs(config.ep_strength.nearend_len)) {
RTC_DCHECK_GT(config.filter.refined.length_blocks,
static_cast<size_t>(kEarlyReverbMinSizeBlocks));
}
diff --git a/modules/audio_processing/aec3/reverb_decay_estimator.h b/modules/audio_processing/aec3/reverb_decay_estimator.h
index 3bb9b2b..fee5421 100644
--- a/modules/audio_processing/aec3/reverb_decay_estimator.h
+++ b/modules/audio_processing/aec3/reverb_decay_estimator.h
@@ -34,8 +34,15 @@
int filter_delay_blocks,
bool usable_linear_filter,
bool stationary_signal);
- // Returns the decay for the exponential model.
- float Decay() const { return decay_; }
+ // Returns the decay for the exponential model. The parameter `mild` indicates
+ // which exponential decay to return, the default one or a milder one.
+ float Decay(bool mild) const {
+ if (use_adaptive_echo_decay_) {
+ return decay_;
+ } else {
+ return mild ? mild_decay_ : decay_;
+ }
+ }
// Dumps debug data.
void Dump(ApmDataDumper* data_dumper) const;
@@ -103,6 +110,7 @@
bool estimation_region_identified_ = false;
std::vector<float> previous_gains_;
float decay_;
+ float mild_decay_;
float tail_gain_ = 0.f;
float smoothing_constant_ = 0.f;
};
diff --git a/modules/audio_processing/aec3/reverb_model_estimator.h b/modules/audio_processing/aec3/reverb_model_estimator.h
index e4e9540..63bade9 100644
--- a/modules/audio_processing/aec3/reverb_model_estimator.h
+++ b/modules/audio_processing/aec3/reverb_model_estimator.h
@@ -43,9 +43,13 @@
const std::vector<bool>& usable_linear_estimates,
bool stationary_block);
- // Returns the exponential decay of the reverberant echo.
+ // Returns the exponential decay of the reverberant echo. The parameter `mild`
+ // indicates which exponential decay to return, the default one or a milder
+ // one.
// TODO(peah): Correct to properly support multiple channels.
- float ReverbDecay() const { return reverb_decay_estimators_[0]->Decay(); }
+ float ReverbDecay(bool mild) const {
+ return reverb_decay_estimators_[0]->Decay(mild);
+ }
// Return the frequency response of the reverberant echo.
// TODO(peah): Correct to properly support multiple channels.
diff --git a/modules/audio_processing/aec3/reverb_model_estimator_unittest.cc b/modules/audio_processing/aec3/reverb_model_estimator_unittest.cc
index f360a6f..fb7dcef 100644
--- a/modules/audio_processing/aec3/reverb_model_estimator_unittest.cc
+++ b/modules/audio_processing/aec3/reverb_model_estimator_unittest.cc
@@ -56,7 +56,9 @@
CreateImpulseResponseWithDecay();
}
void RunEstimator();
- float GetDecay() { return estimated_decay_; }
+ float GetDecay(bool mild) {
+ return mild ? mild_estimated_decay_ : estimated_decay_;
+ }
float GetTrueDecay() { return kTruePowerDecay; }
float GetPowerTailDb() { return 10.f * std::log10(estimated_power_tail_); }
float GetTruePowerTailDb() { return 10.f * std::log10(true_power_tail_); }
@@ -67,6 +69,7 @@
static constexpr float kTruePowerDecay = 0.5f;
const EchoCanceller3Config aec3_config_;
float estimated_decay_;
+ float mild_estimated_decay_;
float estimated_power_tail_ = 0.f;
float true_power_tail_ = 0.f;
std::vector<std::vector<float>> h_;
@@ -121,7 +124,8 @@
estimator.Update(h_, H2_, quality_linear_, filter_delay_blocks,
usable_linear_estimates, kStationaryBlock);
}
- estimated_decay_ = estimator.ReverbDecay();
+ estimated_decay_ = estimator.ReverbDecay(/*mild=*/false);
+ mild_estimated_decay_ = estimator.ReverbDecay(/*mild=*/true);
auto freq_resp_tail = estimator.GetReverbFrequencyResponse();
estimated_power_tail_ =
std::accumulate(freq_resp_tail.begin(), freq_resp_tail.end(), 0.f);
@@ -132,7 +136,9 @@
for (size_t num_capture_channels : {1, 2, 4, 8}) {
ReverbModelEstimatorTest test(kDefaultDecay, num_capture_channels);
test.RunEstimator();
- EXPECT_EQ(test.GetDecay(), kDefaultDecay);
+ EXPECT_EQ(test.GetDecay(/*mild=*/false), kDefaultDecay);
+ EXPECT_EQ(test.GetDecay(/*mild=*/true),
+ EchoCanceller3Config().ep_strength.nearend_len);
EXPECT_NEAR(test.GetPowerTailDb(), test.GetTruePowerTailDb(), 5.f);
}
}
@@ -142,7 +148,8 @@
for (size_t num_capture_channels : {1, 2, 4, 8}) {
ReverbModelEstimatorTest test(kDefaultDecay, num_capture_channels);
test.RunEstimator();
- EXPECT_NEAR(test.GetDecay(), test.GetTrueDecay(), 0.1);
+ EXPECT_NEAR(test.GetDecay(/*mild=*/false), test.GetTrueDecay(), 0.1f);
+ EXPECT_NEAR(test.GetDecay(/*mild=*/true), test.GetTrueDecay(), 0.1f);
EXPECT_NEAR(test.GetPowerTailDb(), test.GetTruePowerTailDb(), 5.f);
}
}