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:
parent
976cc1ae19
commit
ff0451117e
@ -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);
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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", §ion)) {
|
||||
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", §ion)) {
|
||||
@ -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 << "},";
|
||||
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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);
|
||||
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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));
|
||||
|
||||
@ -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)),
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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) {
|
||||
@ -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_
|
||||
@ -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;
|
||||
@ -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) {
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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(),
|
||||
|
||||
@ -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));
|
||||
}
|
||||
|
||||
|
||||
@ -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);
|
||||
|
||||
|
||||
@ -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),
|
||||
|
||||
@ -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],
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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(
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user