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}
This commit is contained in:
Jesús de Vicente Peña 2021-09-08 10:44:55 +02:00 committed by WebRTC LUCI CQ
parent 634f27950e
commit 7d0203c723
15 changed files with 244 additions and 131 deletions

View file

@ -166,6 +166,7 @@ bool EchoCanceller3Config::Validate(EchoCanceller3Config* config) {
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);

View file

@ -108,6 +108,7 @@ struct RTC_EXPORT EchoCanceller3Config {
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;

View file

@ -259,6 +259,7 @@ void Aec3ConfigFromJsonString(absl::string_view json_string,
if (rtc::GetValueFromJsonObject(aec3_root, "ep_strength", &section)) {
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 @@ std::string Aec3ConfigToJsonString(const EchoCanceller3Config& config) {
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\": "

View file

@ -37,6 +37,8 @@ TEST(EchoCanceller3JsonHelpers, ToStringAndParseJson) {
// 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);

View file

@ -229,8 +229,9 @@ void AecState::Update(
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.

View file

@ -116,8 +116,12 @@ class AecState {
// 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 {

View file

@ -267,20 +267,23 @@ EchoCanceller3Config AdjustConfig(const EchoCanceller3Config& config) {
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 @@ EchoCanceller3Config AdjustConfig(const EchoCanceller3Config& config) {
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 @@ EchoCanceller3Config AdjustConfig(const EchoCanceller3Config& config) {
&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 @@ EchoCanceller3Config AdjustConfig(const EchoCanceller3Config& config) {
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 @@ EchoCanceller3Config AdjustConfig(const EchoCanceller3Config& config) {
"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;
}

View file

@ -697,23 +697,6 @@ TEST(EchoCanceller3Messaging, EchoLeakage) {
}
}
// 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) {
@ -767,7 +750,7 @@ TEST(EchoCanceller3FieldTrials, Aec3SuppressorTuningOverrideAllParams) {
"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 @@ TEST(EchoCanceller3FieldTrials, Aec3SuppressorTuningOverrideAllParams) {
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 @@ TEST(EchoCanceller3FieldTrials, Aec3SuppressorTuningOverrideAllParams) {
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 @@ TEST(EchoCanceller3FieldTrials, Aec3SuppressorTuningOverrideOneParam) {
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) {

View file

@ -203,7 +203,8 @@ void ResidualEchoEstimator::Estimate(
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 @@ void ResidualEchoEstimator::Estimate(
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 @@ void ResidualEchoEstimator::UpdateRenderNoisePower(
// 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 @@ void ResidualEchoEstimator::UpdateReverb(ReverbType reverb_type,
}
// 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.

View file

@ -56,7 +56,8 @@ class ResidualEchoEstimator {
// 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.

View file

@ -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 @@ INSTANTIATE_TEST_SUITE_P(MultiChannel,
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);
}
render_delay_buffer->PrepareCaptureProcessing();
}
aec_state.Update(delay_estimate, H2, h,
*render_delay_buffer->GetRenderBuffer(), E2_refined, Y2,
output);
TEST(ResidualEchoEstimatorMultiChannel, ReverbTest) {
const size_t num_render_channels = 1;
const size_t num_capture_channels = 1;
const size_t nFrames = 100;
estimator.Estimate(aec_state, *render_delay_buffer->GetRenderBuffer(),
S2_linear, Y2, /*dominant_nearend=*/false, R2,
R2_unbounded);
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;
}
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);
}
}
}
}
}

View file

@ -93,7 +93,8 @@ ReverbDecayEstimator::ReverbDecayEstimator(const EchoCanceller3Config& config)
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));
}

View file

@ -34,8 +34,15 @@ class ReverbDecayEstimator {
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 @@ class ReverbDecayEstimator {
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;
};

View file

@ -43,9 +43,13 @@ class ReverbModelEstimator {
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.

View file

@ -56,7 +56,9 @@ class ReverbModelEstimatorTest {
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 @@ class ReverbModelEstimatorTest {
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 @@ void ReverbModelEstimatorTest::RunEstimator() {
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 @@ TEST(ReverbModelEstimatorTests, NotChangingDecay) {
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 @@ TEST(ReverbModelEstimatorTests, ChangingDecay) {
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);
}
}