mirror of
https://github.com/mollyim/webrtc.git
synced 2025-05-13 05:40:42 +01:00
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:
parent
634f27950e
commit
7d0203c723
15 changed files with 244 additions and 131 deletions
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -259,6 +259,7 @@ void Aec3ConfigFromJsonString(absl::string_view json_string,
|
|||
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 @@ 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\": "
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue