AEC3: Rename main filter

This CL renames the main filter in AEC3 to have the more accurate name
refined filter.

The CL consists of 3 main initial patch sets, designed to simplify
the review:
1) Replaces "main" with "refined" and adds a fall-back functionality
to support the old filter naming.
2) Renames the files according to the new naming.
3) Performs a "git cl format"

Bug: webrtc:8671
Change-Id: Ifd0aab34e291736a2250e0986348404618630b1d
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/170825
Reviewed-by: Sam Zackrisson <saza@webrtc.org>
Commit-Queue: Per Åhgren <peah@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#30843}
This commit is contained in:
Per Åhgren 2020-03-20 11:20:39 +01:00 committed by Commit Bot
parent 976cc1ae19
commit ff0451117e
31 changed files with 370 additions and 294 deletions

View File

@ -137,6 +137,26 @@ bool EchoCanceller3Config::Validate(EchoCanceller3Config* config) {
res = false;
}
res = res & FloorLimit(&c->filter.refined.length_blocks, 1);
res = res & Limit(&c->filter.refined.leakage_converged, 0.f, 1000.f);
res = res & Limit(&c->filter.refined.leakage_diverged, 0.f, 1000.f);
res = res & Limit(&c->filter.refined.error_floor, 0.f, 1000.f);
res = res & Limit(&c->filter.refined.error_ceil, 0.f, 100000000.f);
res = res & Limit(&c->filter.refined.noise_gate, 0.f, 100000000.f);
res = res & FloorLimit(&c->filter.refined_initial.length_blocks, 1);
res = res & Limit(&c->filter.refined_initial.leakage_converged, 0.f, 1000.f);
res = res & Limit(&c->filter.refined_initial.leakage_diverged, 0.f, 1000.f);
res = res & Limit(&c->filter.refined_initial.error_floor, 0.f, 1000.f);
res = res & Limit(&c->filter.refined_initial.error_ceil, 0.f, 100000000.f);
res = res & Limit(&c->filter.refined_initial.noise_gate, 0.f, 100000000.f);
if (c->filter.refined.length_blocks <
c->filter.refined_initial.length_blocks) {
c->filter.refined_initial.length_blocks = c->filter.refined.length_blocks;
res = false;
}
res = res & FloorLimit(&c->filter.shadow.length_blocks, 1);
res = res & Limit(&c->filter.shadow.rate, 0.f, 1.f);
res = res & Limit(&c->filter.shadow.noise_gate, 0.f, 100000000.f);
@ -161,6 +181,7 @@ bool EchoCanceller3Config::Validate(EchoCanceller3Config* config) {
res = false;
}
res = res & Limit(&c->erle.num_sections, 1, c->filter.main.length_blocks);
res = res & Limit(&c->erle.num_sections, 1, c->filter.refined.length_blocks);
res = res & Limit(&c->ep_strength.default_gain, 0.f, 1000000.f);
res = res & Limit(&c->ep_strength.default_len, -1.f, 1.f);

View File

@ -61,7 +61,7 @@ struct RTC_EXPORT EchoCanceller3Config {
} delay;
struct Filter {
struct MainConfiguration {
struct RefinedConfiguration {
size_t length_blocks;
float leakage_converged;
float leakage_diverged;
@ -76,12 +76,16 @@ struct RTC_EXPORT EchoCanceller3Config {
float noise_gate;
};
MainConfiguration main = {13, 0.00005f, 0.05f, 0.001f, 2.f, 20075344.f};
RefinedConfiguration main = {13, 0.00005f, 0.05f, 0.001f, 2.f, 20075344.f};
ShadowConfiguration shadow = {13, 0.7f, 20075344.f};
RefinedConfiguration refined = {13, 0.00005f, 0.05f,
0.001f, 2.f, 20075344.f};
MainConfiguration main_initial = {12, 0.005f, 0.5f,
0.001f, 2.f, 20075344.f};
RefinedConfiguration main_initial = {12, 0.005f, 0.5f,
0.001f, 2.f, 20075344.f};
ShadowConfiguration shadow_initial = {12, 0.9f, 20075344.f};
RefinedConfiguration refined_initial = {12, 0.005f, 0.5f,
0.001f, 2.f, 20075344.f};
size_t config_change_duration_blocks = 250;
float initial_state_seconds = 2.5f;
@ -89,6 +93,9 @@ struct RTC_EXPORT EchoCanceller3Config {
bool enable_shadow_filter_output_usage = true;
bool use_linear_filter = true;
bool export_linear_aec_output = false;
// Uses the filter configurations named main rather than those named
// refined.
bool use_legacy_filter_naming = true;
} filter;
struct Erle {

View File

@ -55,7 +55,7 @@ void ReadParam(const Json::Value& root, std::string param_name, float* param) {
void ReadParam(const Json::Value& root,
std::string param_name,
EchoCanceller3Config::Filter::MainConfiguration* param) {
EchoCanceller3Config::Filter::RefinedConfiguration* param) {
RTC_DCHECK(param);
Json::Value json_array;
if (rtc::GetValueFromJsonObject(root, param_name, &json_array)) {
@ -216,8 +216,10 @@ void Aec3ConfigFromJsonString(absl::string_view json_string,
if (rtc::GetValueFromJsonObject(aec3_root, "filter", &section)) {
ReadParam(section, "main", &cfg.filter.main);
ReadParam(section, "refined", &cfg.filter.refined);
ReadParam(section, "shadow", &cfg.filter.shadow);
ReadParam(section, "main_initial", &cfg.filter.main_initial);
ReadParam(section, "refined_initial", &cfg.filter.refined_initial);
ReadParam(section, "shadow_initial", &cfg.filter.shadow_initial);
ReadParam(section, "config_change_duration_blocks",
&cfg.filter.config_change_duration_blocks);
@ -230,6 +232,8 @@ void Aec3ConfigFromJsonString(absl::string_view json_string,
ReadParam(section, "use_linear_filter", &cfg.filter.use_linear_filter);
ReadParam(section, "export_linear_aec_output",
&cfg.filter.export_linear_aec_output);
ReadParam(section, "use_legacy_filter_naming",
&cfg.filter.use_legacy_filter_naming);
}
if (rtc::GetValueFromJsonObject(aec3_root, "erle", &section)) {
@ -468,6 +472,15 @@ std::string Aec3ConfigToJsonString(const EchoCanceller3Config& config) {
ost << config.filter.main.noise_gate;
ost << "],";
ost << "\"refined\": [";
ost << config.filter.refined.length_blocks << ",";
ost << config.filter.refined.leakage_converged << ",";
ost << config.filter.refined.leakage_diverged << ",";
ost << config.filter.refined.error_floor << ",";
ost << config.filter.refined.error_ceil << ",";
ost << config.filter.refined.noise_gate;
ost << "],";
ost << "\"shadow\": [";
ost << config.filter.shadow.length_blocks << ",";
ost << config.filter.shadow.rate << ",";
@ -483,6 +496,15 @@ std::string Aec3ConfigToJsonString(const EchoCanceller3Config& config) {
ost << config.filter.main_initial.noise_gate;
ost << "],";
ost << "\"refined_initial\": [";
ost << config.filter.refined_initial.length_blocks << ",";
ost << config.filter.refined_initial.leakage_converged << ",";
ost << config.filter.refined_initial.leakage_diverged << ",";
ost << config.filter.refined_initial.error_floor << ",";
ost << config.filter.refined_initial.error_ceil << ",";
ost << config.filter.refined_initial.noise_gate;
ost << "],";
ost << "\"shadow_initial\": [";
ost << config.filter.shadow_initial.length_blocks << ",";
ost << config.filter.shadow_initial.rate << ",";
@ -501,7 +523,9 @@ std::string Aec3ConfigToJsonString(const EchoCanceller3Config& config) {
ost << "\"use_linear_filter\": "
<< (config.filter.use_linear_filter ? "true" : "false") << ",";
ost << "\"export_linear_aec_output\": "
<< (config.filter.export_linear_aec_output ? "true" : "false");
<< (config.filter.export_linear_aec_output ? "true" : "false") << ",";
ost << "\"use_legacy_filter_naming\": "
<< (config.filter.use_legacy_filter_naming ? "true" : "false");
ost << "},";

View File

@ -19,6 +19,8 @@ TEST(EchoCanceller3JsonHelpers, ToStringAndParseJson) {
EchoCanceller3Config cfg;
cfg.delay.down_sampling_factor = 1u;
cfg.delay.log_warning_on_delay_changes = true;
cfg.filter.main.error_floor = 1.f;
cfg.filter.refined.error_floor = 2.f;
cfg.filter.shadow_initial.length_blocks = 7u;
cfg.suppressor.normal_tuning.mask_hf.enr_suppress = .5f;
cfg.suppressor.subband_nearend_detection.nearend_average_blocks = 3;
@ -30,8 +32,6 @@ TEST(EchoCanceller3JsonHelpers, ToStringAndParseJson) {
EchoCanceller3Config cfg_transformed = Aec3ConfigFromJsonString(json_string);
// Expect unchanged values to remain default.
EXPECT_EQ(cfg.filter.main.error_floor,
cfg_transformed.filter.main.error_floor);
EXPECT_EQ(cfg.ep_strength.default_len,
cfg_transformed.ep_strength.default_len);
EXPECT_EQ(cfg.suppressor.normal_tuning.mask_lf.enr_suppress,
@ -42,6 +42,10 @@ TEST(EchoCanceller3JsonHelpers, ToStringAndParseJson) {
cfg_transformed.delay.down_sampling_factor);
EXPECT_EQ(cfg.delay.log_warning_on_delay_changes,
cfg_transformed.delay.log_warning_on_delay_changes);
EXPECT_EQ(cfg.filter.main.error_floor,
cfg_transformed.filter.main.error_floor);
EXPECT_EQ(cfg.filter.refined.error_floor,
cfg_transformed.filter.refined.error_floor);
EXPECT_EQ(cfg.filter.shadow_initial.length_blocks,
cfg_transformed.filter.shadow_initial.length_blocks);
EXPECT_EQ(cfg.suppressor.normal_tuning.mask_hf.enr_suppress,

View File

@ -72,8 +72,6 @@ rtc_library("aec3") {
"frame_blocker.h",
"fullband_erle_estimator.cc",
"fullband_erle_estimator.h",
"main_filter_update_gain.cc",
"main_filter_update_gain.h",
"matched_filter.cc",
"matched_filter.h",
"matched_filter_lag_aggregator.cc",
@ -81,6 +79,8 @@ rtc_library("aec3") {
"moving_average.cc",
"moving_average.h",
"nearend_detector.h",
"refined_filter_update_gain.cc",
"refined_filter_update_gain.h",
"render_buffer.cc",
"render_buffer.h",
"render_delay_buffer.cc",
@ -215,10 +215,10 @@ if (rtc_include_tests) {
"fft_data_unittest.cc",
"filter_analyzer_unittest.cc",
"frame_blocker_unittest.cc",
"main_filter_update_gain_unittest.cc",
"matched_filter_lag_aggregator_unittest.cc",
"matched_filter_unittest.cc",
"moving_average_unittest.cc",
"refined_filter_update_gain_unittest.cc",
"render_buffer_unittest.cc",
"render_delay_buffer_unittest.cc",
"render_delay_controller_metrics_unittest.cc",

View File

@ -353,14 +353,14 @@ TEST_P(AdaptiveFirFilterMultiChannel, FilterAndAdapt) {
EchoCanceller3Config config;
if (num_render_channels == 33) {
config.filter.main = {13, 0.00005f, 0.0005f, 0.0001f, 2.f, 20075344.f};
config.filter.refined = {13, 0.00005f, 0.0005f, 0.0001f, 2.f, 20075344.f};
config.filter.shadow = {13, 0.1f, 20075344.f};
config.filter.main_initial = {12, 0.005f, 0.5f, 0.001f, 2.f, 20075344.f};
config.filter.refined_initial = {12, 0.005f, 0.5f, 0.001f, 2.f, 20075344.f};
config.filter.shadow_initial = {12, 0.7f, 20075344.f};
}
AdaptiveFirFilter filter(
config.filter.main.length_blocks, config.filter.main.length_blocks,
config.filter.refined.length_blocks, config.filter.refined.length_blocks,
config.filter.config_change_duration_blocks, num_render_channels,
DetectOptimization(), &data_dumper);
std::vector<std::vector<std::array<float, kFftLengthBy2Plus1>>> H2(
@ -393,7 +393,7 @@ TEST_P(AdaptiveFirFilterMultiChannel, FilterAndAdapt) {
FftData G;
FftData E;
std::vector<std::array<float, kFftLengthBy2Plus1>> Y2(num_capture_channels);
std::vector<std::array<float, kFftLengthBy2Plus1>> E2_main(
std::vector<std::array<float, kFftLengthBy2Plus1>> E2_refined(
num_capture_channels);
std::array<float, kFftLengthBy2Plus1> E2_shadow;
// [B,A] = butter(2,100/8000,'high')
@ -403,8 +403,8 @@ TEST_P(AdaptiveFirFilterMultiChannel, FilterAndAdapt) {
for (auto& Y2_ch : Y2) {
Y2_ch.fill(0.f);
}
for (auto& E2_main_ch : E2_main) {
E2_main_ch.fill(0.f);
for (auto& E2_refined_ch : E2_refined) {
E2_refined_ch.fill(0.f);
}
E2_shadow.fill(0.f);
for (auto& subtractor_output : output) {
@ -469,7 +469,7 @@ TEST_P(AdaptiveFirFilterMultiChannel, FilterAndAdapt) {
fft.ZeroPaddedFft(e, Aec3Fft::Window::kRectangular, &E);
for (auto& o : output) {
for (size_t k = 0; k < kBlockSize; ++k) {
o.s_main[k] = kScale * s_scratch[k + kFftLengthBy2];
o.s_refined[k] = kScale * s_scratch[k + kFftLengthBy2];
}
}
@ -482,7 +482,7 @@ TEST_P(AdaptiveFirFilterMultiChannel, FilterAndAdapt) {
false, EchoPathVariability::DelayAdjustment::kNone, false));
filter.ComputeFrequencyResponse(&H2[0]);
aec_state.Update(delay_estimate, H2, h, *render_buffer, E2_main, Y2,
aec_state.Update(delay_estimate, H2, h, *render_buffer, E2_refined, Y2,
output);
}
// Verify that the filter is able to perform well.

View File

@ -161,7 +161,7 @@ void AecState::Update(
adaptive_filter_frequency_responses,
rtc::ArrayView<const std::vector<float>> adaptive_filter_impulse_responses,
const RenderBuffer& render_buffer,
rtc::ArrayView<const std::array<float, kFftLengthBy2Plus1>> E2_main,
rtc::ArrayView<const std::array<float, kFftLengthBy2Plus1>> E2_refined,
rtc::ArrayView<const std::array<float, kFftLengthBy2Plus1>> Y2,
rtc::ArrayView<const SubtractorOutput> subtractor_output) {
RTC_DCHECK_EQ(num_capture_channels_, Y2.size());
@ -227,7 +227,7 @@ void AecState::Update(
}
erle_estimator_.Update(render_buffer, adaptive_filter_frequency_responses,
avg_render_spectrum_with_reverb, Y2, E2_main,
avg_render_spectrum_with_reverb, Y2, E2_refined,
subtractor_output_analyzer_.ConvergedFilters());
erl_estimator_.Update(
@ -511,7 +511,7 @@ void AecState::SaturationDetector::Update(
for (size_t ch = 0; ch < subtractor_output.size(); ++ch) {
saturated_echo_ =
saturated_echo_ ||
(subtractor_output[ch].s_main_max_abs > kSaturationThreshold ||
(subtractor_output[ch].s_refined_max_abs > kSaturationThreshold ||
subtractor_output[ch].s_shadow_max_abs > kSaturationThreshold);
}
} else {

View File

@ -135,7 +135,7 @@ class AecState {
rtc::ArrayView<const std::vector<float>>
adaptive_filter_impulse_responses,
const RenderBuffer& render_buffer,
rtc::ArrayView<const std::array<float, kFftLengthBy2Plus1>> E2_main,
rtc::ArrayView<const std::array<float, kFftLengthBy2Plus1>> E2_refined,
rtc::ArrayView<const std::array<float, kFftLengthBy2Plus1>> Y2,
rtc::ArrayView<const SubtractorOutput> subtractor_output);

View File

@ -32,7 +32,7 @@ void RunNormalUsageTest(size_t num_render_channels,
DelayEstimate(DelayEstimate::Quality::kRefined, 10);
std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
RenderDelayBuffer::Create(config, kSampleRateHz, num_render_channels));
std::vector<std::array<float, kFftLengthBy2Plus1>> E2_main(
std::vector<std::array<float, kFftLengthBy2Plus1>> E2_refined(
num_capture_channels);
std::vector<std::array<float, kFftLengthBy2Plus1>> Y2(num_capture_channels);
std::vector<std::vector<std::vector<float>>> x(
@ -44,10 +44,10 @@ void RunNormalUsageTest(size_t num_render_channels,
std::vector<SubtractorOutput> subtractor_output(num_capture_channels);
for (size_t ch = 0; ch < num_capture_channels; ++ch) {
subtractor_output[ch].Reset();
subtractor_output[ch].s_main.fill(100.f);
subtractor_output[ch].e_main.fill(100.f);
subtractor_output[ch].s_refined.fill(100.f);
subtractor_output[ch].e_refined.fill(100.f);
y[ch].fill(1000.f);
E2_main[ch].fill(0.f);
E2_refined[ch].fill(0.f);
Y2[ch].fill(0.f);
}
Aec3Fft fft;
@ -66,8 +66,8 @@ void RunNormalUsageTest(size_t num_render_channels,
converged_filter_frequency_response[0][2][0] = 1.f;
std::vector<std::vector<float>> impulse_response(
num_capture_channels,
std::vector<float>(GetTimeDomainLength(config.filter.main.length_blocks),
0.f));
std::vector<float>(
GetTimeDomainLength(config.filter.refined.length_blocks), 0.f));
// Verify that linear AEC usability is true when the filter is converged
for (size_t band = 0; band < kNumBands; ++band) {
@ -82,7 +82,7 @@ void RunNormalUsageTest(size_t num_render_channels,
}
state.Update(delay_estimate, converged_filter_frequency_response,
impulse_response, *render_delay_buffer->GetRenderBuffer(),
E2_main, Y2, subtractor_output);
E2_refined, Y2, subtractor_output);
}
EXPECT_TRUE(state.UsableLinearEstimate());
@ -95,7 +95,7 @@ void RunNormalUsageTest(size_t num_render_channels,
false, EchoPathVariability::DelayAdjustment::kBufferReadjustment, false));
state.Update(delay_estimate, converged_filter_frequency_response,
impulse_response, *render_delay_buffer->GetRenderBuffer(),
E2_main, Y2, subtractor_output);
E2_refined, Y2, subtractor_output);
EXPECT_FALSE(state.UsableLinearEstimate());
// Verify that the active render detection works as intended.
@ -110,7 +110,7 @@ void RunNormalUsageTest(size_t num_render_channels,
true, EchoPathVariability::DelayAdjustment::kNewDetectedDelay, false));
state.Update(delay_estimate, converged_filter_frequency_response,
impulse_response, *render_delay_buffer->GetRenderBuffer(),
E2_main, Y2, subtractor_output);
E2_refined, Y2, subtractor_output);
EXPECT_FALSE(state.ActiveRender());
for (int k = 0; k < 1000; ++k) {
@ -120,7 +120,7 @@ void RunNormalUsageTest(size_t num_render_channels,
}
state.Update(delay_estimate, converged_filter_frequency_response,
impulse_response, *render_delay_buffer->GetRenderBuffer(),
E2_main, Y2, subtractor_output);
E2_refined, Y2, subtractor_output);
}
EXPECT_TRUE(state.ActiveRender());
@ -152,7 +152,7 @@ void RunNormalUsageTest(size_t num_render_channels,
}
state.Update(delay_estimate, converged_filter_frequency_response,
impulse_response, *render_delay_buffer->GetRenderBuffer(),
E2_main, Y2, subtractor_output);
E2_refined, Y2, subtractor_output);
}
ASSERT_TRUE(state.UsableLinearEstimate());
@ -164,11 +164,11 @@ void RunNormalUsageTest(size_t num_render_channels,
EXPECT_EQ(erl[erl.size() - 2], erl[erl.size() - 1]);
// Verify that the ERLE is properly estimated
for (auto& E2_main_ch : E2_main) {
E2_main_ch.fill(1.f * 10000.f * 10000.f);
for (auto& E2_refined_ch : E2_refined) {
E2_refined_ch.fill(1.f * 10000.f * 10000.f);
}
for (auto& Y2_ch : Y2) {
Y2_ch.fill(10.f * E2_main[0][0]);
Y2_ch.fill(10.f * E2_refined[0][0]);
}
for (size_t k = 0; k < 1000; ++k) {
for (size_t ch = 0; ch < num_capture_channels; ++ch) {
@ -176,7 +176,7 @@ void RunNormalUsageTest(size_t num_render_channels,
}
state.Update(delay_estimate, converged_filter_frequency_response,
impulse_response, *render_delay_buffer->GetRenderBuffer(),
E2_main, Y2, subtractor_output);
E2_refined, Y2, subtractor_output);
}
ASSERT_TRUE(state.UsableLinearEstimate());
{
@ -193,11 +193,11 @@ void RunNormalUsageTest(size_t num_render_channels,
}
EXPECT_EQ(erle[erle.size() - 2], erle[erle.size() - 1]);
}
for (auto& E2_main_ch : E2_main) {
E2_main_ch.fill(1.f * 10000.f * 10000.f);
for (auto& E2_refined_ch : E2_refined) {
E2_refined_ch.fill(1.f * 10000.f * 10000.f);
}
for (auto& Y2_ch : Y2) {
Y2_ch.fill(5.f * E2_main[0][0]);
Y2_ch.fill(5.f * E2_refined[0][0]);
}
for (size_t k = 0; k < 1000; ++k) {
for (size_t ch = 0; ch < num_capture_channels; ++ch) {
@ -205,7 +205,7 @@ void RunNormalUsageTest(size_t num_render_channels,
}
state.Update(delay_estimate, converged_filter_frequency_response,
impulse_response, *render_delay_buffer->GetRenderBuffer(),
E2_main, Y2, subtractor_output);
E2_refined, Y2, subtractor_output);
}
ASSERT_TRUE(state.UsableLinearEstimate());
@ -250,7 +250,7 @@ TEST(AecState, ConvergedFilterDelay) {
std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
RenderDelayBuffer::Create(config, 48000, 1));
absl::optional<DelayEstimate> delay_estimate;
std::vector<std::array<float, kFftLengthBy2Plus1>> E2_main(
std::vector<std::array<float, kFftLengthBy2Plus1>> E2_refined(
kNumCaptureChannels);
std::vector<std::array<float, kFftLengthBy2Plus1>> Y2(kNumCaptureChannels);
std::array<float, kBlockSize> x;
@ -259,7 +259,7 @@ TEST(AecState, ConvergedFilterDelay) {
std::vector<SubtractorOutput> subtractor_output(kNumCaptureChannels);
for (auto& output : subtractor_output) {
output.Reset();
output.s_main.fill(100.f);
output.s_refined.fill(100.f);
}
std::array<float, kBlockSize> y;
x.fill(0.f);
@ -277,8 +277,8 @@ TEST(AecState, ConvergedFilterDelay) {
std::vector<std::vector<float>> impulse_response(
kNumCaptureChannels,
std::vector<float>(GetTimeDomainLength(config.filter.main.length_blocks),
0.f));
std::vector<float>(
GetTimeDomainLength(config.filter.refined.length_blocks), 0.f));
// Verify that the filter delay for a converged filter is properly
// identified.
@ -291,7 +291,7 @@ TEST(AecState, ConvergedFilterDelay) {
state.HandleEchoPathChange(echo_path_variability);
subtractor_output[0].ComputeMetrics(y);
state.Update(delay_estimate, frequency_response, impulse_response,
*render_delay_buffer->GetRenderBuffer(), E2_main, Y2,
*render_delay_buffer->GetRenderBuffer(), E2_refined, Y2,
subtractor_output);
}
}

View File

@ -38,6 +38,11 @@ bool DetectSaturation(rtc::ArrayView<const float> y) {
EchoCanceller3Config AdjustConfig(const EchoCanceller3Config& config) {
EchoCanceller3Config adjusted_cfg = config;
if (adjusted_cfg.filter.use_legacy_filter_naming) {
adjusted_cfg.filter.refined = adjusted_cfg.filter.main;
adjusted_cfg.filter.refined_initial = adjusted_cfg.filter.main_initial;
}
if (field_trial::IsEnabled("WebRTC-Aec3ShortHeadroomKillSwitch")) {
// Two blocks headroom.
adjusted_cfg.delay.delay_headroom_samples = kBlockSize * 2;

View File

@ -133,7 +133,7 @@ class EchoRemoverImpl final : public EchoRemover {
}
private:
// Selects which of the shadow and main linear filter outputs that is most
// Selects which of the shadow and refined linear filter outputs that is most
// appropriate to pass to the suppressor and forms the linear filter output by
// smoothly transition between those.
void FormLinearFilterOutput(const SubtractorOutput& subtractor_output,
@ -161,7 +161,7 @@ class EchoRemoverImpl final : public EchoRemover {
std::vector<std::array<float, kFftLengthBy2>> y_old_;
size_t block_counter_ = 0;
int gain_change_hangover_ = 0;
bool main_filter_output_last_selected_ = true;
bool refined_filter_output_last_selected_ = true;
std::vector<std::array<float, kFftLengthBy2>> e_heap_;
std::vector<std::array<float, kFftLengthBy2Plus1>> Y2_heap_;
@ -429,7 +429,7 @@ void EchoRemoverImpl::ProcessCapture(
// Debug outputs for the purpose of development and analysis.
data_dumper_->DumpWav("aec3_echo_estimate", kBlockSize,
&subtractor_output[0].s_main[0], 16000, 1);
&subtractor_output[0].s_refined[0], 16000, 1);
data_dumper_->DumpRaw("aec3_output", (*y)[0][0]);
data_dumper_->DumpRaw("aec3_narrow_render",
render_signal_analyzer_.NarrowPeakBand() ? 1 : 0);
@ -456,34 +456,35 @@ void EchoRemoverImpl::ProcessCapture(
void EchoRemoverImpl::FormLinearFilterOutput(
const SubtractorOutput& subtractor_output,
rtc::ArrayView<float> output) {
RTC_DCHECK_EQ(subtractor_output.e_main.size(), output.size());
RTC_DCHECK_EQ(subtractor_output.e_refined.size(), output.size());
RTC_DCHECK_EQ(subtractor_output.e_shadow.size(), output.size());
bool use_main_output = true;
bool use_refined_output = true;
if (use_shadow_filter_output_) {
// As the output of the main adaptive filter generally should be better
// As the output of the refined adaptive filter generally should be better
// than the shadow filter output, add a margin and threshold for when
// choosing the shadow filter output.
if (subtractor_output.e2_shadow < 0.9f * subtractor_output.e2_main &&
if (subtractor_output.e2_shadow < 0.9f * subtractor_output.e2_refined &&
subtractor_output.y2 > 30.f * 30.f * kBlockSize &&
(subtractor_output.s2_main > 60.f * 60.f * kBlockSize ||
(subtractor_output.s2_refined > 60.f * 60.f * kBlockSize ||
subtractor_output.s2_shadow > 60.f * 60.f * kBlockSize)) {
use_main_output = false;
use_refined_output = false;
} else {
// If the main filter is diverged, choose the filter output that has the
// lowest power.
if (subtractor_output.e2_shadow < subtractor_output.e2_main &&
subtractor_output.y2 < subtractor_output.e2_main) {
use_main_output = false;
// If the refined filter is diverged, choose the filter output that has
// the lowest power.
if (subtractor_output.e2_shadow < subtractor_output.e2_refined &&
subtractor_output.y2 < subtractor_output.e2_refined) {
use_refined_output = false;
}
}
}
SignalTransition(
main_filter_output_last_selected_ ? subtractor_output.e_main
: subtractor_output.e_shadow,
use_main_output ? subtractor_output.e_main : subtractor_output.e_shadow,
output);
main_filter_output_last_selected_ = use_main_output;
SignalTransition(refined_filter_output_last_selected_
? subtractor_output.e_refined
: subtractor_output.e_shadow,
use_refined_output ? subtractor_output.e_refined
: subtractor_output.e_shadow,
output);
refined_filter_output_last_selected_ = use_refined_output;
}
} // namespace

View File

@ -157,7 +157,7 @@ TEST_P(ErleEstimatorMultiChannel, VerifyErleIncreaseAndHold) {
num_render_channels, std::vector<float>(kBlockSize, 0.f)));
std::vector<std::vector<std::array<float, kFftLengthBy2Plus1>>>
filter_frequency_response(
config.filter.main.length_blocks,
config.filter.refined.length_blocks,
std::vector<std::array<float, kFftLengthBy2Plus1>>(num_capture_channels));
std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
RenderDelayBuffer::Create(config, kSampleRateHz, num_render_channels));
@ -211,7 +211,7 @@ TEST_P(ErleEstimatorMultiChannel, VerifyErleTrackingOnOnsets) {
num_render_channels, std::vector<float>(kBlockSize, 0.f)));
std::vector<std::vector<std::array<float, kFftLengthBy2Plus1>>>
filter_frequency_response(
config.filter.main.length_blocks,
config.filter.refined.length_blocks,
std::vector<std::array<float, kFftLengthBy2Plus1>>(num_capture_channels));
std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
RenderDelayBuffer::Create(config, kSampleRateHz, num_render_channels));

View File

@ -55,7 +55,7 @@ FilterAnalyzer::FilterAnalyzer(const EchoCanceller3Config& config,
default_gain_(config.ep_strength.default_gain),
h_highpass_(num_capture_channels,
std::vector<float>(
GetTimeDomainLength(config.filter.main.length_blocks),
GetTimeDomainLength(config.filter.refined.length_blocks),
0.f)),
filter_analysis_states_(num_capture_channels,
FilterAnalysisState(config)),

View File

@ -111,7 +111,7 @@ class FilterAnalyzer {
struct FilterAnalysisState {
explicit FilterAnalysisState(const EchoCanceller3Config& config)
: filter_length_blocks(config.filter.main_initial.length_blocks),
: filter_length_blocks(config.filter.refined_initial.length_blocks),
consistent_filter_detector(config) {}
float gain;
size_t peak_index;

View File

@ -8,7 +8,7 @@
* be found in the AUTHORS file in the root of the source tree.
*/
#include "modules/audio_processing/aec3/main_filter_update_gain.h"
#include "modules/audio_processing/aec3/refined_filter_update_gain.h"
#include <algorithm>
#include <functional>
@ -31,10 +31,10 @@ constexpr int kPoorExcitationCounterInitial = 1000;
} // namespace
int MainFilterUpdateGain::instance_count_ = 0;
int RefinedFilterUpdateGain::instance_count_ = 0;
MainFilterUpdateGain::MainFilterUpdateGain(
const EchoCanceller3Config::Filter::MainConfiguration& config,
RefinedFilterUpdateGain::RefinedFilterUpdateGain(
const EchoCanceller3Config::Filter::RefinedConfiguration& config,
size_t config_change_duration_blocks)
: data_dumper_(
new ApmDataDumper(rtc::AtomicOps::Increment(&instance_count_))),
@ -47,9 +47,9 @@ MainFilterUpdateGain::MainFilterUpdateGain(
one_by_config_change_duration_blocks_ = 1.f / config_change_duration_blocks_;
}
MainFilterUpdateGain::~MainFilterUpdateGain() {}
RefinedFilterUpdateGain::~RefinedFilterUpdateGain() {}
void MainFilterUpdateGain::HandleEchoPathChange(
void RefinedFilterUpdateGain::HandleEchoPathChange(
const EchoPathVariability& echo_path_variability) {
if (echo_path_variability.gain_change) {
// TODO(bugs.webrtc.org/9526) Handle gain changes.
@ -66,7 +66,7 @@ void MainFilterUpdateGain::HandleEchoPathChange(
}
}
void MainFilterUpdateGain::Compute(
void RefinedFilterUpdateGain::Compute(
const std::array<float, kFftLengthBy2Plus1>& render_power,
const RenderSignalAnalyzer& render_signal_analyzer,
const SubtractorOutput& subtractor_output,
@ -76,8 +76,8 @@ void MainFilterUpdateGain::Compute(
FftData* gain_fft) {
RTC_DCHECK(gain_fft);
// Introducing shorter notation to improve readability.
const FftData& E_main = subtractor_output.E_main;
const auto& E2_main = subtractor_output.E2_main;
const FftData& E_refined = subtractor_output.E_refined;
const auto& E2_refined = subtractor_output.E2_refined;
const auto& E2_shadow = subtractor_output.E2_shadow;
FftData* G = gain_fft;
const auto& X2 = render_power;
@ -102,7 +102,7 @@ void MainFilterUpdateGain::Compute(
for (size_t k = 0; k < kFftLengthBy2Plus1; ++k) {
if (X2[k] >= current_config_.noise_gate) {
mu[k] = H_error_[k] /
(0.5f * H_error_[k] * X2[k] + size_partitions * E2_main[k]);
(0.5f * H_error_[k] * X2[k] + size_partitions * E2_refined[k]);
} else {
mu[k] = 0.f;
}
@ -118,14 +118,14 @@ void MainFilterUpdateGain::Compute(
// G = mu * E.
for (size_t k = 0; k < kFftLengthBy2Plus1; ++k) {
G->re[k] = mu[k] * E_main.re[k];
G->im[k] = mu[k] * E_main.im[k];
G->re[k] = mu[k] * E_refined.re[k];
G->im[k] = mu[k] * E_refined.im[k];
}
}
// H_error = H_error + factor * erl.
for (size_t k = 0; k < kFftLengthBy2Plus1; ++k) {
if (E2_shadow[k] >= E2_main[k]) {
if (E2_shadow[k] >= E2_refined[k]) {
H_error_[k] += current_config_.leakage_converged * erl[k];
} else {
H_error_[k] += current_config_.leakage_diverged * erl[k];
@ -135,10 +135,10 @@ void MainFilterUpdateGain::Compute(
H_error_[k] = std::min(H_error_[k], current_config_.error_ceil);
}
data_dumper_->DumpRaw("aec3_main_gain_H_error", H_error_);
data_dumper_->DumpRaw("aec3_refined_gain_H_error", H_error_);
}
void MainFilterUpdateGain::UpdateCurrentConfig() {
void RefinedFilterUpdateGain::UpdateCurrentConfig() {
RTC_DCHECK_GE(config_change_duration_blocks_, config_change_counter_);
if (config_change_counter_ > 0) {
if (--config_change_counter_ > 0) {

View File

@ -8,8 +8,8 @@
* be found in the AUTHORS file in the root of the source tree.
*/
#ifndef MODULES_AUDIO_PROCESSING_AEC3_MAIN_FILTER_UPDATE_GAIN_H_
#define MODULES_AUDIO_PROCESSING_AEC3_MAIN_FILTER_UPDATE_GAIN_H_
#ifndef MODULES_AUDIO_PROCESSING_AEC3_REFINED_FILTER_UPDATE_GAIN_H_
#define MODULES_AUDIO_PROCESSING_AEC3_REFINED_FILTER_UPDATE_GAIN_H_
#include <stddef.h>
@ -29,16 +29,17 @@ struct FftData;
class RenderSignalAnalyzer;
struct SubtractorOutput;
// Provides functionality for computing the adaptive gain for the main filter.
class MainFilterUpdateGain {
// Provides functionality for computing the adaptive gain for the refined
// filter.
class RefinedFilterUpdateGain {
public:
MainFilterUpdateGain(
const EchoCanceller3Config::Filter::MainConfiguration& config,
RefinedFilterUpdateGain(
const EchoCanceller3Config::Filter::RefinedConfiguration& config,
size_t config_change_duration_blocks);
~MainFilterUpdateGain();
~RefinedFilterUpdateGain();
MainFilterUpdateGain(const MainFilterUpdateGain&) = delete;
MainFilterUpdateGain& operator=(const MainFilterUpdateGain&) = delete;
RefinedFilterUpdateGain(const RefinedFilterUpdateGain&) = delete;
RefinedFilterUpdateGain& operator=(const RefinedFilterUpdateGain&) = delete;
// Takes action in the case of a known echo path change.
void HandleEchoPathChange(const EchoPathVariability& echo_path_variability);
@ -53,8 +54,9 @@ class MainFilterUpdateGain {
FftData* gain_fft);
// Sets a new config.
void SetConfig(const EchoCanceller3Config::Filter::MainConfiguration& config,
bool immediate_effect) {
void SetConfig(
const EchoCanceller3Config::Filter::RefinedConfiguration& config,
bool immediate_effect) {
if (immediate_effect) {
old_target_config_ = current_config_ = target_config_ = config;
config_change_counter_ = 0;
@ -70,9 +72,9 @@ class MainFilterUpdateGain {
std::unique_ptr<ApmDataDumper> data_dumper_;
const int config_change_duration_blocks_;
float one_by_config_change_duration_blocks_;
EchoCanceller3Config::Filter::MainConfiguration current_config_;
EchoCanceller3Config::Filter::MainConfiguration target_config_;
EchoCanceller3Config::Filter::MainConfiguration old_target_config_;
EchoCanceller3Config::Filter::RefinedConfiguration current_config_;
EchoCanceller3Config::Filter::RefinedConfiguration target_config_;
EchoCanceller3Config::Filter::RefinedConfiguration old_target_config_;
std::array<float, kFftLengthBy2Plus1> H_error_;
size_t poor_excitation_counter_;
size_t call_counter_ = 0;
@ -84,4 +86,4 @@ class MainFilterUpdateGain {
} // namespace webrtc
#endif // MODULES_AUDIO_PROCESSING_AEC3_MAIN_FILTER_UPDATE_GAIN_H_
#endif // MODULES_AUDIO_PROCESSING_AEC3_REFINED_FILTER_UPDATE_GAIN_H_

View File

@ -8,11 +8,12 @@
* be found in the AUTHORS file in the root of the source tree.
*/
#include "modules/audio_processing/aec3/main_filter_update_gain.h"
#include "modules/audio_processing/aec3/refined_filter_update_gain.h"
#include <algorithm>
#include <numeric>
#include <string>
#include <vector>
#include "modules/audio_processing/aec3/adaptive_fir_filter.h"
#include "modules/audio_processing/aec3/adaptive_fir_filter_erl.h"
@ -31,8 +32,8 @@
namespace webrtc {
namespace {
// Method for performing the simulations needed to test the main filter update
// gain functionality.
// Method for performing the simulations needed to test the refined filter
// update gain functionality.
void RunFilterUpdateTest(int num_blocks_to_process,
size_t delay_samples,
int filter_length_blocks,
@ -50,19 +51,19 @@ void RunFilterUpdateTest(int num_blocks_to_process,
constexpr size_t kNumBands = NumBandsForRate(kSampleRateHz);
EchoCanceller3Config config;
config.filter.main.length_blocks = filter_length_blocks;
config.filter.refined.length_blocks = filter_length_blocks;
config.filter.shadow.length_blocks = filter_length_blocks;
AdaptiveFirFilter main_filter(config.filter.main.length_blocks,
config.filter.main.length_blocks,
config.filter.config_change_duration_blocks,
kNumRenderChannels, optimization, &data_dumper);
AdaptiveFirFilter refined_filter(
config.filter.refined.length_blocks, config.filter.refined.length_blocks,
config.filter.config_change_duration_blocks, kNumRenderChannels,
optimization, &data_dumper);
AdaptiveFirFilter shadow_filter(
config.filter.shadow.length_blocks, config.filter.shadow.length_blocks,
config.filter.config_change_duration_blocks, kNumRenderChannels,
optimization, &data_dumper);
std::vector<std::vector<std::array<float, kFftLengthBy2Plus1>>> H2(
kNumCaptureChannels, std::vector<std::array<float, kFftLengthBy2Plus1>>(
main_filter.max_filter_size_partitions(),
refined_filter.max_filter_size_partitions(),
std::array<float, kFftLengthBy2Plus1>()));
for (auto& H2_ch : H2) {
for (auto& H2_k : H2_ch) {
@ -72,15 +73,16 @@ void RunFilterUpdateTest(int num_blocks_to_process,
std::vector<std::vector<float>> h(
kNumCaptureChannels,
std::vector<float>(
GetTimeDomainLength(main_filter.max_filter_size_partitions()), 0.f));
GetTimeDomainLength(refined_filter.max_filter_size_partitions()),
0.f));
Aec3Fft fft;
std::array<float, kBlockSize> x_old;
x_old.fill(0.f);
ShadowFilterUpdateGain shadow_gain(
config.filter.shadow, config.filter.config_change_duration_blocks);
MainFilterUpdateGain main_gain(config.filter.main,
config.filter.config_change_duration_blocks);
RefinedFilterUpdateGain refined_gain(
config.filter.refined, config.filter.config_change_duration_blocks);
Random random_generator(42U);
std::vector<std::vector<std::vector<float>>> x(
kNumBands, std::vector<std::vector<float>>(
@ -100,12 +102,12 @@ void RunFilterUpdateTest(int num_blocks_to_process,
for (auto& subtractor_output : output) {
subtractor_output.Reset();
}
FftData& E_main = output[0].E_main;
FftData& E_refined = output[0].E_refined;
FftData E_shadow;
std::vector<std::array<float, kFftLengthBy2Plus1>> Y2(kNumCaptureChannels);
std::vector<std::array<float, kFftLengthBy2Plus1>> E2_main(
std::vector<std::array<float, kFftLengthBy2Plus1>> E2_refined(
kNumCaptureChannels);
std::array<float, kBlockSize>& e_main = output[0].e_main;
std::array<float, kBlockSize>& e_refined = output[0].e_refined;
std::array<float, kBlockSize>& e_shadow = output[0].e_shadow;
for (auto& Y2_ch : Y2) {
Y2_ch.fill(0.f);
@ -119,7 +121,7 @@ void RunFilterUpdateTest(int num_blocks_to_process,
if (std::find(blocks_with_echo_path_changes.begin(),
blocks_with_echo_path_changes.end(),
k) != blocks_with_echo_path_changes.end()) {
main_filter.HandleEchoPathChange();
refined_filter.HandleEchoPathChange();
}
// Handle saturation.
@ -152,15 +154,15 @@ void RunFilterUpdateTest(int num_blocks_to_process,
render_signal_analyzer.Update(*render_delay_buffer->GetRenderBuffer(),
aec_state.MinDirectPathFilterDelay());
// Apply the main filter.
main_filter.Filter(*render_delay_buffer->GetRenderBuffer(), &S);
// Apply the refined filter.
refined_filter.Filter(*render_delay_buffer->GetRenderBuffer(), &S);
fft.Ifft(S, &s_scratch);
std::transform(y.begin(), y.end(), s_scratch.begin() + kFftLengthBy2,
e_main.begin(),
e_refined.begin(),
[&](float a, float b) { return a - b * kScale; });
std::for_each(e_main.begin(), e_main.end(),
std::for_each(e_refined.begin(), e_refined.end(),
[](float& a) { a = rtc::SafeClamp(a, -32768.f, 32767.f); });
fft.ZeroPaddedFft(e_main, Aec3Fft::Window::kRectangular, &E_main);
fft.ZeroPaddedFft(e_refined, Aec3Fft::Window::kRectangular, &E_refined);
for (size_t k = 0; k < kBlockSize; ++k) {
s[k] = kScale * s_scratch[k + kFftLengthBy2];
}
@ -176,7 +178,7 @@ void RunFilterUpdateTest(int num_blocks_to_process,
fft.ZeroPaddedFft(e_shadow, Aec3Fft::Window::kRectangular, &E_shadow);
// Compute spectra for future use.
E_main.Spectrum(Aec3Optimization::kNone, output[0].E2_main);
E_refined.Spectrum(Aec3Optimization::kNone, output[0].E2_refined);
E_shadow.Spectrum(Aec3Optimization::kNone, output[0].E2_shadow);
// Adapt the shadow filter.
@ -187,28 +189,28 @@ void RunFilterUpdateTest(int num_blocks_to_process,
shadow_filter.SizePartitions(), saturation, &G);
shadow_filter.Adapt(*render_delay_buffer->GetRenderBuffer(), G);
// Adapt the main filter
// Adapt the refined filter
render_delay_buffer->GetRenderBuffer()->SpectralSum(
main_filter.SizePartitions(), &render_power);
refined_filter.SizePartitions(), &render_power);
std::array<float, kFftLengthBy2Plus1> erl;
ComputeErl(optimization, H2[0], erl);
main_gain.Compute(render_power, render_signal_analyzer, output[0], erl,
main_filter.SizePartitions(), saturation, &G);
main_filter.Adapt(*render_delay_buffer->GetRenderBuffer(), G, &h[0]);
refined_gain.Compute(render_power, render_signal_analyzer, output[0], erl,
refined_filter.SizePartitions(), saturation, &G);
refined_filter.Adapt(*render_delay_buffer->GetRenderBuffer(), G, &h[0]);
// Update the delay.
aec_state.HandleEchoPathChange(EchoPathVariability(
false, EchoPathVariability::DelayAdjustment::kNone, false));
main_filter.ComputeFrequencyResponse(&H2[0]);
std::copy(output[0].E2_main.begin(), output[0].E2_main.end(),
E2_main[0].begin());
refined_filter.ComputeFrequencyResponse(&H2[0]);
std::copy(output[0].E2_refined.begin(), output[0].E2_refined.end(),
E2_refined[0].begin());
aec_state.Update(delay_estimate, H2, h,
*render_delay_buffer->GetRenderBuffer(), E2_main, Y2,
*render_delay_buffer->GetRenderBuffer(), E2_refined, Y2,
output);
}
std::copy(e_main.begin(), e_main.end(), e_last_block->begin());
std::copy(e_refined.begin(), e_refined.end(), e_last_block->begin());
std::copy(y.begin(), y.end(), y_last_block->begin());
std::copy(G.re.begin(), G.re.end(), G_last_block->re.begin());
std::copy(G.im.begin(), G.im.end(), G_last_block->im.begin());
@ -232,26 +234,27 @@ std::string ProduceDebugText(size_t delay, int filter_length_blocks) {
#if RTC_DCHECK_IS_ON && GTEST_HAS_DEATH_TEST && !defined(WEBRTC_ANDROID)
// Verifies that the check for non-null output gain parameter works.
TEST(MainFilterUpdateGain, NullDataOutputGain) {
TEST(RefinedFilterUpdateGain, NullDataOutputGain) {
ApmDataDumper data_dumper(42);
EchoCanceller3Config config;
RenderSignalAnalyzer analyzer(config);
SubtractorOutput output;
MainFilterUpdateGain gain(config.filter.main,
config.filter.config_change_duration_blocks);
RefinedFilterUpdateGain gain(config.filter.refined,
config.filter.config_change_duration_blocks);
std::array<float, kFftLengthBy2Plus1> render_power;
render_power.fill(0.f);
std::array<float, kFftLengthBy2Plus1> erl;
erl.fill(0.f);
EXPECT_DEATH(gain.Compute(render_power, analyzer, output, erl,
config.filter.main.length_blocks, false, nullptr),
"");
EXPECT_DEATH(
gain.Compute(render_power, analyzer, output, erl,
config.filter.refined.length_blocks, false, nullptr),
"");
}
#endif
// Verifies that the gain formed causes the filter using it to converge.
TEST(MainFilterUpdateGain, GainCausesFilterToConverge) {
TEST(RefinedFilterUpdateGain, GainCausesFilterToConverge) {
std::vector<int> blocks_with_echo_path_changes;
std::vector<int> blocks_with_saturation;
for (size_t filter_length_blocks : {12, 20, 30}) {
@ -266,7 +269,7 @@ TEST(MainFilterUpdateGain, GainCausesFilterToConverge) {
blocks_with_echo_path_changes, blocks_with_saturation,
false, &e, &y, &G);
// Verify that the main filter is able to perform well.
// Verify that the refined filter is able to perform well.
// Use different criteria to take overmodelling into account.
if (filter_length_blocks == 12) {
EXPECT_LT(1000 * std::inner_product(e.begin(), e.end(), e.begin(), 0.f),
@ -281,7 +284,7 @@ TEST(MainFilterUpdateGain, GainCausesFilterToConverge) {
// Verifies that the magnitude of the gain on average decreases for a
// persistently exciting signal.
TEST(MainFilterUpdateGain, DecreasingGain) {
TEST(RefinedFilterUpdateGain, DecreasingGain) {
std::vector<int> blocks_with_echo_path_changes;
std::vector<int> blocks_with_saturation;
@ -314,7 +317,7 @@ TEST(MainFilterUpdateGain, DecreasingGain) {
// Verifies that the gain is zero when there is saturation and that the internal
// error estimates cause the gain to increase after a period of saturation.
TEST(MainFilterUpdateGain, SaturationBehavior) {
TEST(RefinedFilterUpdateGain, SaturationBehavior) {
std::vector<int> blocks_with_echo_path_changes;
std::vector<int> blocks_with_saturation;
for (int k = 99; k < 200; ++k) {
@ -358,7 +361,7 @@ TEST(MainFilterUpdateGain, SaturationBehavior) {
// Verifies that the gain increases after an echo path change.
// TODO(peah): Correct and reactivate this test.
TEST(MainFilterUpdateGain, DISABLED_EchoPathChangeBehavior) {
TEST(RefinedFilterUpdateGain, DISABLED_EchoPathChangeBehavior) {
for (size_t filter_length_blocks : {12, 20, 30}) {
SCOPED_TRACE(ProduceDebugText(filter_length_blocks));
std::vector<int> blocks_with_echo_path_changes;

View File

@ -133,7 +133,7 @@ RenderDelayBufferImpl::RenderDelayBufferImpl(const EchoCanceller3Config& config,
: kBlockSize)),
blocks_(GetRenderDelayBufferSize(down_sampling_factor_,
config.delay.num_filters,
config.filter.main.length_blocks),
config.filter.refined.length_blocks),
NumBandsForRate(sample_rate_hz),
num_render_channels,
kBlockSize),
@ -147,7 +147,7 @@ RenderDelayBufferImpl::RenderDelayBufferImpl(const EchoCanceller3Config& config,
render_decimator_(down_sampling_factor_),
fft_(),
render_ds_(sub_block_size_, 0.f),
buffer_headroom_(config.filter.main.length_blocks) {
buffer_headroom_(config.filter.refined.length_blocks) {
RTC_DCHECK_EQ(blocks_.buffer.size(), ffts_.buffer.size());
RTC_DCHECK_EQ(spectra_.buffer.size(), ffts_.buffer.size());
for (size_t i = 0; i < blocks_.buffer.size(); ++i) {

View File

@ -118,7 +118,7 @@ void IdentifyStrongNarrowBandComponent(const RenderBuffer& render_buffer,
} // namespace
RenderSignalAnalyzer::RenderSignalAnalyzer(const EchoCanceller3Config& config)
: strong_peak_freeze_duration_(config.filter.main.length_blocks) {
: strong_peak_freeze_duration_(config.filter.refined.length_blocks) {
narrow_band_counters_.fill(0);
}
RenderSignalAnalyzer::~RenderSignalAnalyzer() = default;

View File

@ -42,7 +42,7 @@ TEST_P(ResidualEchoEstimatorMultiChannel, BasicTest) {
std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
RenderDelayBuffer::Create(config, kSampleRateHz, num_render_channels));
std::vector<std::array<float, kFftLengthBy2Plus1>> E2_main(
std::vector<std::array<float, kFftLengthBy2Plus1>> E2_refined(
num_capture_channels);
std::vector<std::array<float, kFftLengthBy2Plus1>> S2_linear(
num_capture_channels);
@ -69,18 +69,18 @@ TEST_P(ResidualEchoEstimatorMultiChannel, BasicTest) {
std::vector<std::vector<float>> h(
num_capture_channels,
std::vector<float>(GetTimeDomainLength(config.filter.main.length_blocks),
0.f));
std::vector<float>(
GetTimeDomainLength(config.filter.refined.length_blocks), 0.f));
for (auto& subtractor_output : output) {
subtractor_output.Reset();
subtractor_output.s_main.fill(100.f);
subtractor_output.s_refined.fill(100.f);
}
y.fill(0.f);
constexpr float kLevel = 10.f;
for (auto& E2_main_ch : E2_main) {
E2_main_ch.fill(kLevel);
for (auto& E2_refined_ch : E2_refined) {
E2_refined_ch.fill(kLevel);
}
S2_linear[0].fill(kLevel);
for (auto& Y2_ch : Y2) {
@ -96,7 +96,7 @@ TEST_P(ResidualEchoEstimatorMultiChannel, BasicTest) {
render_delay_buffer->PrepareCaptureProcessing();
aec_state.Update(delay_estimate, H2, h,
*render_delay_buffer->GetRenderBuffer(), E2_main, Y2,
*render_delay_buffer->GetRenderBuffer(), E2_refined, Y2,
output);
estimator.Estimate(aec_state, *render_delay_buffer->GetRenderBuffer(),

View File

@ -85,16 +85,16 @@ float BlockEnergyAverage(rtc::ArrayView<const float> h, int block_index) {
} // namespace
ReverbDecayEstimator::ReverbDecayEstimator(const EchoCanceller3Config& config)
: filter_length_blocks_(config.filter.main.length_blocks),
: filter_length_blocks_(config.filter.refined.length_blocks),
filter_length_coefficients_(GetTimeDomainLength(filter_length_blocks_)),
use_adaptive_echo_decay_(config.ep_strength.default_len < 0.f),
early_reverb_estimator_(config.filter.main.length_blocks -
early_reverb_estimator_(config.filter.refined.length_blocks -
kEarlyReverbMinSizeBlocks),
late_reverb_start_(kEarlyReverbMinSizeBlocks),
late_reverb_end_(kEarlyReverbMinSizeBlocks),
previous_gains_(config.filter.main.length_blocks, 0.f),
previous_gains_(config.filter.refined.length_blocks, 0.f),
decay_(std::fabs(config.ep_strength.default_len)) {
RTC_DCHECK_GT(config.filter.main.length_blocks,
RTC_DCHECK_GT(config.filter.refined.length_blocks,
static_cast<size_t>(kEarlyReverbMinSizeBlocks));
}

View File

@ -32,7 +32,7 @@ namespace {
EchoCanceller3Config CreateConfigForTest(float default_decay) {
EchoCanceller3Config cfg;
cfg.ep_strength.default_len = default_decay;
cfg.filter.main.length_blocks = 40;
cfg.filter.refined.length_blocks = 40;
return cfg;
}
@ -47,11 +47,11 @@ class ReverbModelEstimatorTest {
estimated_decay_(default_decay),
h_(num_capture_channels,
std::vector<float>(
aec3_config_.filter.main.length_blocks * kBlockSize,
aec3_config_.filter.refined.length_blocks * kBlockSize,
0.f)),
H2_(num_capture_channels,
std::vector<std::array<float, kFftLengthBy2Plus1>>(
aec3_config_.filter.main.length_blocks)),
aec3_config_.filter.refined.length_blocks)),
quality_linear_(num_capture_channels, 1.0f) {
CreateImpulseResponseWithDecay();
}
@ -78,10 +78,10 @@ void ReverbModelEstimatorTest::CreateImpulseResponseWithDecay() {
const Aec3Fft fft;
for (const auto& h_k : h_) {
RTC_DCHECK_EQ(h_k.size(),
aec3_config_.filter.main.length_blocks * kBlockSize);
aec3_config_.filter.refined.length_blocks * kBlockSize);
}
for (const auto& H2_k : H2_) {
RTC_DCHECK_EQ(H2_k.size(), aec3_config_.filter.main.length_blocks);
RTC_DCHECK_EQ(H2_k.size(), aec3_config_.filter.refined.length_blocks);
}
RTC_DCHECK_EQ(kFilterDelayBlocks, 2);

View File

@ -27,8 +27,8 @@
namespace webrtc {
namespace {
// Method for performing the simulations needed to test the main filter update
// gain functionality.
// Method for performing the simulations needed to test the refined filter
// update gain functionality.
void RunFilterUpdateTest(int num_blocks_to_process,
size_t delay_samples,
size_t num_render_channels,
@ -39,9 +39,9 @@ void RunFilterUpdateTest(int num_blocks_to_process,
FftData* G_last_block) {
ApmDataDumper data_dumper(42);
EchoCanceller3Config config;
config.filter.main.length_blocks = filter_length_blocks;
AdaptiveFirFilter main_filter(
config.filter.main.length_blocks, config.filter.main.length_blocks,
config.filter.refined.length_blocks = filter_length_blocks;
AdaptiveFirFilter refined_filter(
config.filter.refined.length_blocks, config.filter.refined.length_blocks,
config.filter.config_change_duration_blocks, num_render_channels,
DetectOptimization(), &data_dumper);
AdaptiveFirFilter shadow_filter(
@ -179,7 +179,7 @@ TEST_P(ShadowFilterUpdateGainOneTwoEightRenderChannels,
filter_length_blocks, blocks_with_saturation, &e, &y,
&G);
// Verify that the main filter is able to perform well.
// Verify that the refined filter is able to perform well.
// Use different criteria to take overmodelling into account.
if (filter_length_blocks == 12) {
EXPECT_LT(1000 * std::inner_product(e.begin(), e.end(), e.begin(), 0.f),

View File

@ -122,7 +122,7 @@ SignalDependentErleEstimator::SignalDependentErleEstimator(
size_t num_capture_channels)
: min_erle_(config.erle.min),
num_sections_(config.erle.num_sections),
num_blocks_(config.filter.main.length_blocks),
num_blocks_(config.filter.refined.length_blocks),
delay_headroom_blocks_(config.delay.delay_headroom_samples / kBlockSize),
band_to_subband_(FormSubbandMap()),
max_erle_(SetMaxErleSubbands(config.erle.max_l,
@ -261,9 +261,9 @@ void SignalDependentErleEstimator::UpdateCorrectionFactors(
for (size_t subband = 0; subband < kSubbands; ++subband) {
// When aggregating the number of active sections in the filter for
// different bands we choose to take the minimum of all of them. As an
// example, if for one of the bands it is the direct path its main
// example, if for one of the bands it is the direct path its refined
// contributor to the final echo estimate, we consider the direct path
// is as well the main contributor for the subband that contains that
// is as well the refined contributor for the subband that contains that
// particular band. That aggregate number of sections will be later used
// as the identifier of the erle estimator that needs to be updated.
RTC_DCHECK_LE(kBandBoundaries[subband + 1],

View File

@ -87,7 +87,7 @@ TestInputs::TestInputs(const EchoCanceller3Config& cfg,
E2_(num_capture_channels),
H2_(num_capture_channels,
std::vector<std::array<float, kFftLengthBy2Plus1>>(
cfg.filter.main.length_blocks)),
cfg.filter.refined.length_blocks)),
x_(1,
std::vector<std::vector<float>>(num_render_channels,
std::vector<float>(kBlockSize, 0.f))),
@ -156,9 +156,9 @@ TEST_P(SignalDependentErleEstimatorMultiChannel, SweepSettings) {
for (size_t delay_headroom = 0; delay_headroom < 5; ++delay_headroom) {
for (size_t num_sections = 2; num_sections < max_length_blocks;
++num_sections) {
cfg.filter.main.length_blocks = blocks;
cfg.filter.main_initial.length_blocks =
std::min(cfg.filter.main_initial.length_blocks, blocks);
cfg.filter.refined.length_blocks = blocks;
cfg.filter.refined_initial.length_blocks =
std::min(cfg.filter.refined_initial.length_blocks, blocks);
cfg.delay.delay_headroom_samples = delay_headroom * kBlockSize;
cfg.erle.num_sections = num_sections;
if (EchoCanceller3Config::Validate(&cfg)) {
@ -185,8 +185,8 @@ TEST_P(SignalDependentErleEstimatorMultiChannel, LongerRun) {
const size_t num_render_channels = std::get<0>(GetParam());
const size_t num_capture_channels = std::get<1>(GetParam());
EchoCanceller3Config cfg;
cfg.filter.main.length_blocks = 2;
cfg.filter.main_initial.length_blocks = 1;
cfg.filter.refined.length_blocks = 2;
cfg.filter.refined_initial.length_blocks = 1;
cfg.delay.delay_headroom_samples = 0;
cfg.delay.hysteresis_limit_blocks = 0;
cfg.erle.num_sections = 2;

View File

@ -66,28 +66,28 @@ Subtractor::Subtractor(const EchoCanceller3Config& config,
optimization_(optimization),
config_(config),
num_capture_channels_(num_capture_channels),
main_filters_(num_capture_channels_),
refined_filters_(num_capture_channels_),
shadow_filter_(num_capture_channels_),
main_gains_(num_capture_channels_),
refined_gains_(num_capture_channels_),
shadow_gains_(num_capture_channels_),
filter_misadjustment_estimators_(num_capture_channels_),
poor_shadow_filter_counters_(num_capture_channels_, 0),
main_frequency_responses_(
refined_frequency_responses_(
num_capture_channels_,
std::vector<std::array<float, kFftLengthBy2Plus1>>(
std::max(config_.filter.main_initial.length_blocks,
config_.filter.main.length_blocks),
std::max(config_.filter.refined_initial.length_blocks,
config_.filter.refined.length_blocks),
std::array<float, kFftLengthBy2Plus1>())),
main_impulse_responses_(
refined_impulse_responses_(
num_capture_channels_,
std::vector<float>(GetTimeDomainLength(std::max(
config_.filter.main_initial.length_blocks,
config_.filter.main.length_blocks)),
config_.filter.refined_initial.length_blocks,
config_.filter.refined.length_blocks)),
0.f)) {
for (size_t ch = 0; ch < num_capture_channels_; ++ch) {
main_filters_[ch] = std::make_unique<AdaptiveFirFilter>(
config_.filter.main.length_blocks,
config_.filter.main_initial.length_blocks,
refined_filters_[ch] = std::make_unique<AdaptiveFirFilter>(
config_.filter.refined.length_blocks,
config_.filter.refined_initial.length_blocks,
config.filter.config_change_duration_blocks, num_render_channels,
optimization, data_dumper_);
@ -96,8 +96,8 @@ Subtractor::Subtractor(const EchoCanceller3Config& config,
config_.filter.shadow_initial.length_blocks,
config.filter.config_change_duration_blocks, num_render_channels,
optimization, data_dumper_);
main_gains_[ch] = std::make_unique<MainFilterUpdateGain>(
config_.filter.main_initial,
refined_gains_[ch] = std::make_unique<RefinedFilterUpdateGain>(
config_.filter.refined_initial,
config_.filter.config_change_duration_blocks);
shadow_gains_[ch] = std::make_unique<ShadowFilterUpdateGain>(
config_.filter.shadow_initial,
@ -106,7 +106,7 @@ Subtractor::Subtractor(const EchoCanceller3Config& config,
RTC_DCHECK(data_dumper_);
for (size_t ch = 0; ch < num_capture_channels_; ++ch) {
for (auto& H2_k : main_frequency_responses_[ch]) {
for (auto& H2_k : refined_frequency_responses_[ch]) {
H2_k.fill(0.f);
}
}
@ -118,14 +118,14 @@ void Subtractor::HandleEchoPathChange(
const EchoPathVariability& echo_path_variability) {
const auto full_reset = [&]() {
for (size_t ch = 0; ch < num_capture_channels_; ++ch) {
main_filters_[ch]->HandleEchoPathChange();
refined_filters_[ch]->HandleEchoPathChange();
shadow_filter_[ch]->HandleEchoPathChange();
main_gains_[ch]->HandleEchoPathChange(echo_path_variability);
refined_gains_[ch]->HandleEchoPathChange(echo_path_variability);
shadow_gains_[ch]->HandleEchoPathChange();
main_gains_[ch]->SetConfig(config_.filter.main_initial, true);
refined_gains_[ch]->SetConfig(config_.filter.refined_initial, true);
shadow_gains_[ch]->SetConfig(config_.filter.shadow_initial, true);
main_filters_[ch]->SetSizePartitions(
config_.filter.main_initial.length_blocks, true);
refined_filters_[ch]->SetSizePartitions(
config_.filter.refined_initial.length_blocks, true);
shadow_filter_[ch]->SetSizePartitions(
config_.filter.shadow_initial.length_blocks, true);
}
@ -138,17 +138,17 @@ void Subtractor::HandleEchoPathChange(
if (echo_path_variability.gain_change) {
for (size_t ch = 0; ch < num_capture_channels_; ++ch) {
main_gains_[ch]->HandleEchoPathChange(echo_path_variability);
refined_gains_[ch]->HandleEchoPathChange(echo_path_variability);
}
}
}
void Subtractor::ExitInitialState() {
for (size_t ch = 0; ch < num_capture_channels_; ++ch) {
main_gains_[ch]->SetConfig(config_.filter.main, false);
refined_gains_[ch]->SetConfig(config_.filter.refined, false);
shadow_gains_[ch]->SetConfig(config_.filter.shadow, false);
main_filters_[ch]->SetSizePartitions(config_.filter.main.length_blocks,
false);
refined_filters_[ch]->SetSizePartitions(
config_.filter.refined.length_blocks, false);
shadow_filter_[ch]->SetSizePartitions(config_.filter.shadow.length_blocks,
false);
}
@ -162,21 +162,22 @@ void Subtractor::Process(const RenderBuffer& render_buffer,
RTC_DCHECK_EQ(num_capture_channels_, capture.size());
// Compute the render powers.
const bool same_filter_sizes =
main_filters_[0]->SizePartitions() == shadow_filter_[0]->SizePartitions();
std::array<float, kFftLengthBy2Plus1> X2_main;
const bool same_filter_sizes = refined_filters_[0]->SizePartitions() ==
shadow_filter_[0]->SizePartitions();
std::array<float, kFftLengthBy2Plus1> X2_refined;
std::array<float, kFftLengthBy2Plus1> X2_shadow_data;
auto& X2_shadow = same_filter_sizes ? X2_main : X2_shadow_data;
auto& X2_shadow = same_filter_sizes ? X2_refined : X2_shadow_data;
if (same_filter_sizes) {
render_buffer.SpectralSum(main_filters_[0]->SizePartitions(), &X2_main);
} else if (main_filters_[0]->SizePartitions() >
render_buffer.SpectralSum(refined_filters_[0]->SizePartitions(),
&X2_refined);
} else if (refined_filters_[0]->SizePartitions() >
shadow_filter_[0]->SizePartitions()) {
render_buffer.SpectralSums(shadow_filter_[0]->SizePartitions(),
main_filters_[0]->SizePartitions(), &X2_shadow,
&X2_main);
refined_filters_[0]->SizePartitions(),
&X2_shadow, &X2_refined);
} else {
render_buffer.SpectralSums(main_filters_[0]->SizePartitions(),
shadow_filter_[0]->SizePartitions(), &X2_main,
render_buffer.SpectralSums(refined_filters_[0]->SizePartitions(),
shadow_filter_[0]->SizePartitions(), &X2_refined,
&X2_shadow);
}
@ -185,17 +186,17 @@ void Subtractor::Process(const RenderBuffer& render_buffer,
RTC_DCHECK_EQ(kBlockSize, capture[ch].size());
SubtractorOutput& output = outputs[ch];
rtc::ArrayView<const float> y = capture[ch];
FftData& E_main = output.E_main;
FftData& E_refined = output.E_refined;
FftData E_shadow;
std::array<float, kBlockSize>& e_main = output.e_main;
std::array<float, kBlockSize>& e_refined = output.e_refined;
std::array<float, kBlockSize>& e_shadow = output.e_shadow;
FftData S;
FftData& G = S;
// Form the outputs of the main and shadow filters.
main_filters_[ch]->Filter(render_buffer, &S);
PredictionError(fft_, S, y, &e_main, &output.s_main);
// Form the outputs of the refined and shadow filters.
refined_filters_[ch]->Filter(render_buffer, &S);
PredictionError(fft_, S, y, &e_refined, &output.s_refined);
shadow_filter_[ch]->Filter(render_buffer, &S);
PredictionError(fft_, S, y, &e_shadow, &output.s_shadow);
@ -204,59 +205,62 @@ void Subtractor::Process(const RenderBuffer& render_buffer,
output.ComputeMetrics(y);
// Adjust the filter if needed.
bool main_filters_adjusted = false;
bool refined_filters_adjusted = false;
filter_misadjustment_estimators_[ch].Update(output);
if (filter_misadjustment_estimators_[ch].IsAdjustmentNeeded()) {
float scale = filter_misadjustment_estimators_[ch].GetMisadjustment();
main_filters_[ch]->ScaleFilter(scale);
for (auto& h_k : main_impulse_responses_[ch]) {
refined_filters_[ch]->ScaleFilter(scale);
for (auto& h_k : refined_impulse_responses_[ch]) {
h_k *= scale;
}
ScaleFilterOutput(y, scale, e_main, output.s_main);
ScaleFilterOutput(y, scale, e_refined, output.s_refined);
filter_misadjustment_estimators_[ch].Reset();
main_filters_adjusted = true;
refined_filters_adjusted = true;
}
// Compute the FFts of the main and shadow filter outputs.
fft_.ZeroPaddedFft(e_main, Aec3Fft::Window::kHanning, &E_main);
// Compute the FFts of the refined and shadow filter outputs.
fft_.ZeroPaddedFft(e_refined, Aec3Fft::Window::kHanning, &E_refined);
fft_.ZeroPaddedFft(e_shadow, Aec3Fft::Window::kHanning, &E_shadow);
// Compute spectra for future use.
E_shadow.Spectrum(optimization_, output.E2_shadow);
E_main.Spectrum(optimization_, output.E2_main);
E_refined.Spectrum(optimization_, output.E2_refined);
// Update the main filter.
if (!main_filters_adjusted) {
// Update the refined filter.
if (!refined_filters_adjusted) {
std::array<float, kFftLengthBy2Plus1> erl;
ComputeErl(optimization_, main_frequency_responses_[ch], erl);
main_gains_[ch]->Compute(X2_main, render_signal_analyzer, output, erl,
main_filters_[ch]->SizePartitions(),
aec_state.SaturatedCapture(), &G);
ComputeErl(optimization_, refined_frequency_responses_[ch], erl);
refined_gains_[ch]->Compute(X2_refined, render_signal_analyzer, output,
erl, refined_filters_[ch]->SizePartitions(),
aec_state.SaturatedCapture(), &G);
} else {
G.re.fill(0.f);
G.im.fill(0.f);
}
main_filters_[ch]->Adapt(render_buffer, G, &main_impulse_responses_[ch]);
main_filters_[ch]->ComputeFrequencyResponse(&main_frequency_responses_[ch]);
refined_filters_[ch]->Adapt(render_buffer, G,
&refined_impulse_responses_[ch]);
refined_filters_[ch]->ComputeFrequencyResponse(
&refined_frequency_responses_[ch]);
if (ch == 0) {
data_dumper_->DumpRaw("aec3_subtractor_G_main", G.re);
data_dumper_->DumpRaw("aec3_subtractor_G_main", G.im);
data_dumper_->DumpRaw("aec3_subtractor_G_refined", G.re);
data_dumper_->DumpRaw("aec3_subtractor_G_refined", G.im);
}
// Update the shadow filter.
poor_shadow_filter_counters_[ch] =
output.e2_main < output.e2_shadow ? poor_shadow_filter_counters_[ch] + 1
: 0;
output.e2_refined < output.e2_shadow
? poor_shadow_filter_counters_[ch] + 1
: 0;
if (poor_shadow_filter_counters_[ch] < 5) {
shadow_gains_[ch]->Compute(X2_shadow, render_signal_analyzer, E_shadow,
shadow_filter_[ch]->SizePartitions(),
aec_state.SaturatedCapture(), &G);
} else {
poor_shadow_filter_counters_[ch] = 0;
shadow_filter_[ch]->SetFilter(main_filters_[ch]->SizePartitions(),
main_filters_[ch]->GetFilter());
shadow_gains_[ch]->Compute(X2_shadow, render_signal_analyzer, E_main,
shadow_filter_[ch]->SetFilter(refined_filters_[ch]->SizePartitions(),
refined_filters_[ch]->GetFilter());
shadow_gains_[ch]->Compute(X2_shadow, render_signal_analyzer, E_refined,
shadow_filter_[ch]->SizePartitions(),
aec_state.SaturatedCapture(), &G);
}
@ -269,12 +273,12 @@ void Subtractor::Process(const RenderBuffer& render_buffer,
DumpFilters();
}
std::for_each(e_main.begin(), e_main.end(),
std::for_each(e_refined.begin(), e_refined.end(),
[](float& a) { a = rtc::SafeClamp(a, -32768.f, 32767.f); });
if (ch == 0) {
data_dumper_->DumpWav("aec3_main_filters_output", kBlockSize, &e_main[0],
16000, 1);
data_dumper_->DumpWav("aec3_refined_filters_output", kBlockSize,
&e_refined[0], 16000, 1);
data_dumper_->DumpWav("aec3_shadow_filter_output", kBlockSize,
&e_shadow[0], 16000, 1);
}
@ -283,7 +287,7 @@ void Subtractor::Process(const RenderBuffer& render_buffer,
void Subtractor::FilterMisadjustmentEstimator::Update(
const SubtractorOutput& output) {
e2_acum_ += output.e2_main;
e2_acum_ += output.e2_refined;
y2_acum_ += output.y2;
if (++n_blocks_acum_ == n_blocks_) {
if (y2_acum_ > n_blocks_ * 200.f * 200.f * kBlockSize) {

View File

@ -24,7 +24,7 @@
#include "modules/audio_processing/aec3/aec3_fft.h"
#include "modules/audio_processing/aec3/aec_state.h"
#include "modules/audio_processing/aec3/echo_path_variability.h"
#include "modules/audio_processing/aec3/main_filter_update_gain.h"
#include "modules/audio_processing/aec3/refined_filter_update_gain.h"
#include "modules/audio_processing/aec3/render_buffer.h"
#include "modules/audio_processing/aec3/render_signal_analyzer.h"
#include "modules/audio_processing/aec3/shadow_filter_update_gain.h"
@ -58,27 +58,28 @@ class Subtractor {
// Exits the initial state.
void ExitInitialState();
// Returns the block-wise frequency responses for the main adaptive filters.
// Returns the block-wise frequency responses for the refined adaptive
// filters.
const std::vector<std::vector<std::array<float, kFftLengthBy2Plus1>>>&
FilterFrequencyResponses() const {
return main_frequency_responses_;
return refined_frequency_responses_;
}
// Returns the estimates of the impulse responses for the main adaptive
// Returns the estimates of the impulse responses for the refined adaptive
// filters.
const std::vector<std::vector<float>>& FilterImpulseResponses() const {
return main_impulse_responses_;
return refined_impulse_responses_;
}
void DumpFilters() {
data_dumper_->DumpRaw(
"aec3_subtractor_h_main",
"aec3_subtractor_h_refined",
rtc::ArrayView<const float>(
main_impulse_responses_[0].data(),
refined_impulse_responses_[0].data(),
GetTimeDomainLength(
main_filters_[0]->max_filter_size_partitions())));
refined_filters_[0]->max_filter_size_partitions())));
main_filters_[0]->DumpFilter("aec3_subtractor_H_main");
refined_filters_[0]->DumpFilter("aec3_subtractor_H_refined");
shadow_filter_[0]->DumpFilter("aec3_subtractor_H_shadow");
}
@ -120,15 +121,15 @@ class Subtractor {
const EchoCanceller3Config config_;
const size_t num_capture_channels_;
std::vector<std::unique_ptr<AdaptiveFirFilter>> main_filters_;
std::vector<std::unique_ptr<AdaptiveFirFilter>> refined_filters_;
std::vector<std::unique_ptr<AdaptiveFirFilter>> shadow_filter_;
std::vector<std::unique_ptr<MainFilterUpdateGain>> main_gains_;
std::vector<std::unique_ptr<RefinedFilterUpdateGain>> refined_gains_;
std::vector<std::unique_ptr<ShadowFilterUpdateGain>> shadow_gains_;
std::vector<FilterMisadjustmentEstimator> filter_misadjustment_estimators_;
std::vector<size_t> poor_shadow_filter_counters_;
std::vector<std::vector<std::array<float, kFftLengthBy2Plus1>>>
main_frequency_responses_;
std::vector<std::vector<float>> main_impulse_responses_;
refined_frequency_responses_;
std::vector<std::vector<float>> refined_impulse_responses_;
};
} // namespace webrtc

View File

@ -18,17 +18,17 @@ SubtractorOutput::SubtractorOutput() = default;
SubtractorOutput::~SubtractorOutput() = default;
void SubtractorOutput::Reset() {
s_main.fill(0.f);
s_refined.fill(0.f);
s_shadow.fill(0.f);
e_main.fill(0.f);
e_refined.fill(0.f);
e_shadow.fill(0.f);
E_main.re.fill(0.f);
E_main.im.fill(0.f);
E2_main.fill(0.f);
E_refined.re.fill(0.f);
E_refined.im.fill(0.f);
E2_refined.fill(0.f);
E2_shadow.fill(0.f);
e2_main = 0.f;
e2_refined = 0.f;
e2_shadow = 0.f;
s2_main = 0.f;
s2_refined = 0.f;
s2_shadow = 0.f;
y2 = 0.f;
}
@ -36,16 +36,19 @@ void SubtractorOutput::Reset() {
void SubtractorOutput::ComputeMetrics(rtc::ArrayView<const float> y) {
const auto sum_of_squares = [](float a, float b) { return a + b * b; };
y2 = std::accumulate(y.begin(), y.end(), 0.f, sum_of_squares);
e2_main = std::accumulate(e_main.begin(), e_main.end(), 0.f, sum_of_squares);
e2_refined =
std::accumulate(e_refined.begin(), e_refined.end(), 0.f, sum_of_squares);
e2_shadow =
std::accumulate(e_shadow.begin(), e_shadow.end(), 0.f, sum_of_squares);
s2_main = std::accumulate(s_main.begin(), s_main.end(), 0.f, sum_of_squares);
s2_refined =
std::accumulate(s_refined.begin(), s_refined.end(), 0.f, sum_of_squares);
s2_shadow =
std::accumulate(s_shadow.begin(), s_shadow.end(), 0.f, sum_of_squares);
s_main_max_abs = *std::max_element(s_main.begin(), s_main.end());
s_main_max_abs = std::max(s_main_max_abs,
-(*std::min_element(s_main.begin(), s_main.end())));
s_refined_max_abs = *std::max_element(s_refined.begin(), s_refined.end());
s_refined_max_abs =
std::max(s_refined_max_abs,
-(*std::min_element(s_refined.begin(), s_refined.end())));
s_shadow_max_abs = *std::max_element(s_shadow.begin(), s_shadow.end());
s_shadow_max_abs = std::max(

View File

@ -25,19 +25,19 @@ struct SubtractorOutput {
SubtractorOutput();
~SubtractorOutput();
std::array<float, kBlockSize> s_main;
std::array<float, kBlockSize> s_refined;
std::array<float, kBlockSize> s_shadow;
std::array<float, kBlockSize> e_main;
std::array<float, kBlockSize> e_refined;
std::array<float, kBlockSize> e_shadow;
FftData E_main;
std::array<float, kFftLengthBy2Plus1> E2_main;
FftData E_refined;
std::array<float, kFftLengthBy2Plus1> E2_refined;
std::array<float, kFftLengthBy2Plus1> E2_shadow;
float s2_main = 0.f;
float s2_refined = 0.f;
float s2_shadow = 0.f;
float e2_main = 0.f;
float e2_refined = 0.f;
float e2_shadow = 0.f;
float y2 = 0.f;
float s_main_max_abs = 0.f;
float s_refined_max_abs = 0.f;
float s_shadow_max_abs = 0.f;
// Reset the struct content.

View File

@ -32,17 +32,18 @@ void SubtractorOutputAnalyzer::Update(
for (size_t ch = 0; ch < subtractor_output.size(); ++ch) {
const float y2 = subtractor_output[ch].y2;
const float e2_main = subtractor_output[ch].e2_main;
const float e2_refined = subtractor_output[ch].e2_refined;
const float e2_shadow = subtractor_output[ch].e2_shadow;
constexpr float kConvergenceThreshold = 50 * 50 * kBlockSize;
bool main_filter_converged =
e2_main < 0.5f * y2 && y2 > kConvergenceThreshold;
bool refined_filter_converged =
e2_refined < 0.5f * y2 && y2 > kConvergenceThreshold;
bool shadow_filter_converged =
e2_shadow < 0.05f * y2 && y2 > kConvergenceThreshold;
float min_e2 = std::min(e2_main, e2_shadow);
float min_e2 = std::min(e2_refined, e2_shadow);
bool filter_diverged = min_e2 > 1.5f * y2 && y2 > 30.f * 30.f * kBlockSize;
filters_converged_[ch] = main_filter_converged || shadow_filter_converged;
filters_converged_[ch] =
refined_filter_converged || shadow_filter_converged;
*any_filter_converged = *any_filter_converged || filters_converged_[ch];
*all_filters_diverged = *all_filters_diverged && filter_diverged;

View File

@ -31,7 +31,7 @@ std::vector<float> RunSubtractorTest(
size_t num_capture_channels,
int num_blocks_to_process,
int delay_samples,
int main_filter_length_blocks,
int refined_filter_length_blocks,
int shadow_filter_length_blocks,
bool uncorrelated_inputs,
const std::vector<int>& blocks_with_echo_path_changes) {
@ -39,7 +39,7 @@ std::vector<float> RunSubtractorTest(
constexpr int kSampleRateHz = 48000;
constexpr size_t kNumBands = NumBandsForRate(kSampleRateHz);
EchoCanceller3Config config;
config.filter.main.length_blocks = main_filter_length_blocks;
config.filter.refined.length_blocks = refined_filter_length_blocks;
config.filter.shadow.length_blocks = shadow_filter_length_blocks;
Subtractor subtractor(config, num_render_channels, num_capture_channels,
@ -59,7 +59,7 @@ std::vector<float> RunSubtractorTest(
Random random_generator(42U);
Aec3Fft fft;
std::vector<std::array<float, kFftLengthBy2Plus1>> Y2(num_capture_channels);
std::vector<std::array<float, kFftLengthBy2Plus1>> E2_main(
std::vector<std::array<float, kFftLengthBy2Plus1>> E2_refined(
num_capture_channels);
std::array<float, kFftLengthBy2Plus1> E2_shadow;
AecState aec_state(config, num_capture_channels);
@ -67,8 +67,8 @@ std::vector<float> RunSubtractorTest(
for (auto& Y2_ch : Y2) {
Y2_ch.fill(0.f);
}
for (auto& E2_main_ch : E2_main) {
E2_main_ch.fill(0.f);
for (auto& E2_refined_ch : E2_refined) {
E2_refined_ch.fill(0.f);
}
E2_shadow.fill(0.f);
@ -152,15 +152,15 @@ std::vector<float> RunSubtractorTest(
false, EchoPathVariability::DelayAdjustment::kNone, false));
aec_state.Update(delay_estimate, subtractor.FilterFrequencyResponses(),
subtractor.FilterImpulseResponses(),
*render_delay_buffer->GetRenderBuffer(), E2_main, Y2,
*render_delay_buffer->GetRenderBuffer(), E2_refined, Y2,
output);
}
std::vector<float> results(num_capture_channels);
for (size_t ch = 0; ch < num_capture_channels; ++ch) {
const float output_power =
std::inner_product(output[ch].e_main.begin(), output[ch].e_main.end(),
output[ch].e_main.begin(), 0.f);
const float output_power = std::inner_product(
output[ch].e_refined.begin(), output[ch].e_refined.end(),
output[ch].e_refined.begin(), 0.f);
const float y_power =
std::inner_product(y[ch].begin(), y[ch].end(), y[ch].begin(), 0.f);
if (y_power == 0.f) {
@ -231,9 +231,9 @@ TEST(Subtractor, Convergence) {
}
}
// Verifies that the subtractor is able to handle the case when the main filter
// is longer than the shadow filter.
TEST(Subtractor, MainFilterLongerThanShadowFilter) {
// Verifies that the subtractor is able to handle the case when the refined
// filter is longer than the shadow filter.
TEST(Subtractor, RefinedFilterLongerThanShadowFilter) {
std::vector<int> blocks_with_echo_path_changes;
std::vector<float> echo_to_nearend_powers = RunSubtractorTest(
1, 1, 400, 64, 20, 15, false, blocks_with_echo_path_changes);
@ -243,8 +243,8 @@ TEST(Subtractor, MainFilterLongerThanShadowFilter) {
}
// Verifies that the subtractor is able to handle the case when the shadow
// filter is longer than the main filter.
TEST(Subtractor, ShadowFilterLongerThanMainFilter) {
// filter is longer than the refined filter.
TEST(Subtractor, ShadowFilterLongerThanRefinedFilter) {
std::vector<int> blocks_with_echo_path_changes;
std::vector<float> echo_to_nearend_powers = RunSubtractorTest(
1, 1, 400, 64, 15, 20, false, blocks_with_echo_path_changes);