AEC3: Refactor the code for analyzing filter convergence
This CL refactors the code in AEC3 that analyzes how well the adaptive filter performs. The purpose of this is both to simplify code that is more complex than needed and also to pave the wave for the upcoming CLs that softens the echo suppression during doubletalk. The main changes are that: -The shadow adaptive filter is now never analyzed. This turned out to never affect the output in the recordings it was tested on. -The convergence analysis was moved to the aec state code. The changes are bitexact on all testcases where they have been tested on. Bug: webrtc:8671 Change-Id: If76b669565325c8eb4d11d1178a7e20306da9a26 Reviewed-on: https://webrtc-review.googlesource.com/87430 Commit-Queue: Per Åhgren <peah@webrtc.org> Reviewed-by: Sam Zackrisson <saza@webrtc.org> Cr-Commit-Position: refs/heads/master@{#23958}
This commit is contained in:
@ -97,6 +97,8 @@ rtc_static_library("aec3") {
|
|||||||
"subtractor.cc",
|
"subtractor.cc",
|
||||||
"subtractor.h",
|
"subtractor.h",
|
||||||
"subtractor_output.h",
|
"subtractor_output.h",
|
||||||
|
"subtractor_output_analyzer.cc",
|
||||||
|
"subtractor_output_analyzer.h",
|
||||||
"suppression_filter.cc",
|
"suppression_filter.cc",
|
||||||
"suppression_filter.h",
|
"suppression_filter.h",
|
||||||
"suppression_gain.cc",
|
"suppression_gain.cc",
|
||||||
|
@ -328,7 +328,7 @@ TEST(AdaptiveFirFilter, FilterAndAdapt) {
|
|||||||
absl::optional<DelayEstimate> delay_estimate;
|
absl::optional<DelayEstimate> delay_estimate;
|
||||||
std::vector<float> e(kBlockSize, 0.f);
|
std::vector<float> e(kBlockSize, 0.f);
|
||||||
std::array<float, kFftLength> s_scratch;
|
std::array<float, kFftLength> s_scratch;
|
||||||
std::array<float, kBlockSize> s;
|
SubtractorOutput output;
|
||||||
FftData S;
|
FftData S;
|
||||||
FftData G;
|
FftData G;
|
||||||
FftData E;
|
FftData E;
|
||||||
@ -342,6 +342,7 @@ TEST(AdaptiveFirFilter, FilterAndAdapt) {
|
|||||||
Y2.fill(0.f);
|
Y2.fill(0.f);
|
||||||
E2_main.fill(0.f);
|
E2_main.fill(0.f);
|
||||||
E2_shadow.fill(0.f);
|
E2_shadow.fill(0.f);
|
||||||
|
output.Reset();
|
||||||
|
|
||||||
constexpr float kScale = 1.0f / kFftLengthBy2;
|
constexpr float kScale = 1.0f / kFftLengthBy2;
|
||||||
|
|
||||||
@ -382,7 +383,7 @@ TEST(AdaptiveFirFilter, FilterAndAdapt) {
|
|||||||
[](float& a) { a = rtc::SafeClamp(a, -32768.f, 32767.f); });
|
[](float& a) { a = rtc::SafeClamp(a, -32768.f, 32767.f); });
|
||||||
fft.ZeroPaddedFft(e, Aec3Fft::Window::kRectangular, &E);
|
fft.ZeroPaddedFft(e, Aec3Fft::Window::kRectangular, &E);
|
||||||
for (size_t k = 0; k < kBlockSize; ++k) {
|
for (size_t k = 0; k < kBlockSize; ++k) {
|
||||||
s[k] = kScale * s_scratch[k + kFftLengthBy2];
|
output.s_main[k] = kScale * s_scratch[k + kFftLengthBy2];
|
||||||
}
|
}
|
||||||
|
|
||||||
std::array<float, kFftLengthBy2Plus1> render_power;
|
std::array<float, kFftLengthBy2Plus1> render_power;
|
||||||
@ -394,8 +395,8 @@ TEST(AdaptiveFirFilter, FilterAndAdapt) {
|
|||||||
false, EchoPathVariability::DelayAdjustment::kNone, false));
|
false, EchoPathVariability::DelayAdjustment::kNone, false));
|
||||||
|
|
||||||
aec_state.Update(delay_estimate, filter.FilterFrequencyResponse(),
|
aec_state.Update(delay_estimate, filter.FilterFrequencyResponse(),
|
||||||
filter.FilterImpulseResponse(), true, false,
|
filter.FilterImpulseResponse(), *render_buffer, E2_main,
|
||||||
*render_buffer, E2_main, Y2, s);
|
Y2, output, y);
|
||||||
}
|
}
|
||||||
// Verify that the filter is able to perform well.
|
// Verify that the filter is able to perform well.
|
||||||
EXPECT_LT(1000 * std::inner_product(e.begin(), e.end(), e.begin(), 0.f),
|
EXPECT_LT(1000 * std::inner_product(e.begin(), e.end(), e.begin(), 0.f),
|
||||||
|
@ -122,6 +122,8 @@ void AecState::HandleEchoPathChange(
|
|||||||
} else if (echo_path_variability.gain_change) {
|
} else if (echo_path_variability.gain_change) {
|
||||||
blocks_since_reset_ = kNumBlocksPerSecond;
|
blocks_since_reset_ = kNumBlocksPerSecond;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
subtractor_output_analyzer_.HandleEchoPathChange();
|
||||||
}
|
}
|
||||||
|
|
||||||
void AecState::Update(
|
void AecState::Update(
|
||||||
@ -129,12 +131,17 @@ void AecState::Update(
|
|||||||
const std::vector<std::array<float, kFftLengthBy2Plus1>>&
|
const std::vector<std::array<float, kFftLengthBy2Plus1>>&
|
||||||
adaptive_filter_frequency_response,
|
adaptive_filter_frequency_response,
|
||||||
const std::vector<float>& adaptive_filter_impulse_response,
|
const std::vector<float>& adaptive_filter_impulse_response,
|
||||||
bool converged_filter,
|
|
||||||
bool diverged_filter,
|
|
||||||
const RenderBuffer& render_buffer,
|
const RenderBuffer& render_buffer,
|
||||||
const std::array<float, kFftLengthBy2Plus1>& E2_main,
|
const std::array<float, kFftLengthBy2Plus1>& E2_main,
|
||||||
const std::array<float, kFftLengthBy2Plus1>& Y2,
|
const std::array<float, kFftLengthBy2Plus1>& Y2,
|
||||||
const std::array<float, kBlockSize>& s) {
|
const SubtractorOutput& subtractor_output,
|
||||||
|
rtc::ArrayView<const float> y) {
|
||||||
|
// Analyze the filter output.
|
||||||
|
subtractor_output_analyzer_.Update(y, subtractor_output);
|
||||||
|
|
||||||
|
const bool converged_filter = subtractor_output_analyzer_.ConvergedFilter();
|
||||||
|
const bool diverged_filter = subtractor_output_analyzer_.DivergedFilter();
|
||||||
|
|
||||||
// Analyze the filter and compute the delays.
|
// Analyze the filter and compute the delays.
|
||||||
filter_analyzer_.Update(adaptive_filter_impulse_response,
|
filter_analyzer_.Update(adaptive_filter_impulse_response,
|
||||||
adaptive_filter_frequency_response, render_buffer);
|
adaptive_filter_frequency_response, render_buffer);
|
||||||
|
@ -29,6 +29,8 @@
|
|||||||
#include "modules/audio_processing/aec3/filter_analyzer.h"
|
#include "modules/audio_processing/aec3/filter_analyzer.h"
|
||||||
#include "modules/audio_processing/aec3/render_buffer.h"
|
#include "modules/audio_processing/aec3/render_buffer.h"
|
||||||
#include "modules/audio_processing/aec3/reverb_model_estimator.h"
|
#include "modules/audio_processing/aec3/reverb_model_estimator.h"
|
||||||
|
#include "modules/audio_processing/aec3/subtractor_output.h"
|
||||||
|
#include "modules/audio_processing/aec3/subtractor_output_analyzer.h"
|
||||||
#include "modules/audio_processing/aec3/suppression_gain_limiter.h"
|
#include "modules/audio_processing/aec3/suppression_gain_limiter.h"
|
||||||
#include "rtc_base/constructormagic.h"
|
#include "rtc_base/constructormagic.h"
|
||||||
|
|
||||||
@ -151,12 +153,11 @@ class AecState {
|
|||||||
const std::vector<std::array<float, kFftLengthBy2Plus1>>&
|
const std::vector<std::array<float, kFftLengthBy2Plus1>>&
|
||||||
adaptive_filter_frequency_response,
|
adaptive_filter_frequency_response,
|
||||||
const std::vector<float>& adaptive_filter_impulse_response,
|
const std::vector<float>& adaptive_filter_impulse_response,
|
||||||
bool converged_filter,
|
|
||||||
bool diverged_filter,
|
|
||||||
const RenderBuffer& render_buffer,
|
const RenderBuffer& render_buffer,
|
||||||
const std::array<float, kFftLengthBy2Plus1>& E2_main,
|
const std::array<float, kFftLengthBy2Plus1>& E2_main,
|
||||||
const std::array<float, kFftLengthBy2Plus1>& Y2,
|
const std::array<float, kFftLengthBy2Plus1>& Y2,
|
||||||
const std::array<float, kBlockSize>& s);
|
const SubtractorOutput& subtractor_output,
|
||||||
|
rtc::ArrayView<const float> y);
|
||||||
|
|
||||||
// Returns the tail freq. response of the linear filter.
|
// Returns the tail freq. response of the linear filter.
|
||||||
rtc::ArrayView<const float> GetFreqRespTail() const {
|
rtc::ArrayView<const float> GetFreqRespTail() const {
|
||||||
@ -218,6 +219,7 @@ class AecState {
|
|||||||
size_t active_blocks_since_converged_filter_ = 0;
|
size_t active_blocks_since_converged_filter_ = 0;
|
||||||
EchoAudibility echo_audibility_;
|
EchoAudibility echo_audibility_;
|
||||||
ReverbModelEstimator reverb_model_estimator_;
|
ReverbModelEstimator reverb_model_estimator_;
|
||||||
|
SubtractorOutputAnalyzer subtractor_output_analyzer_;
|
||||||
RTC_DISALLOW_COPY_AND_ASSIGN(AecState);
|
RTC_DISALLOW_COPY_AND_ASSIGN(AecState);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -31,9 +31,12 @@ TEST(AecState, NormalUsage) {
|
|||||||
std::vector<std::vector<float>> x(3, std::vector<float>(kBlockSize, 0.f));
|
std::vector<std::vector<float>> x(3, std::vector<float>(kBlockSize, 0.f));
|
||||||
EchoPathVariability echo_path_variability(
|
EchoPathVariability echo_path_variability(
|
||||||
false, EchoPathVariability::DelayAdjustment::kNone, false);
|
false, EchoPathVariability::DelayAdjustment::kNone, false);
|
||||||
std::array<float, kBlockSize> s;
|
SubtractorOutput output;
|
||||||
|
std::array<float, kBlockSize> y;
|
||||||
Aec3Fft fft;
|
Aec3Fft fft;
|
||||||
s.fill(100.f);
|
output.s_main.fill(100.f);
|
||||||
|
output.e_main.fill(100.f);
|
||||||
|
y.fill(1000.f);
|
||||||
|
|
||||||
std::vector<std::array<float, kFftLengthBy2Plus1>>
|
std::vector<std::array<float, kFftLengthBy2Plus1>>
|
||||||
converged_filter_frequency_response(10);
|
converged_filter_frequency_response(10);
|
||||||
@ -50,8 +53,8 @@ TEST(AecState, NormalUsage) {
|
|||||||
|
|
||||||
// Verify that linear AEC usability is false when the filter is diverged.
|
// Verify that linear AEC usability is false when the filter is diverged.
|
||||||
state.Update(delay_estimate, diverged_filter_frequency_response,
|
state.Update(delay_estimate, diverged_filter_frequency_response,
|
||||||
impulse_response, true, false,
|
impulse_response, *render_delay_buffer->GetRenderBuffer(),
|
||||||
*render_delay_buffer->GetRenderBuffer(), E2_main, Y2, s);
|
E2_main, Y2, output, y);
|
||||||
EXPECT_FALSE(state.UsableLinearEstimate());
|
EXPECT_FALSE(state.UsableLinearEstimate());
|
||||||
|
|
||||||
// Verify that linear AEC usability is true when the filter is converged
|
// Verify that linear AEC usability is true when the filter is converged
|
||||||
@ -59,8 +62,8 @@ TEST(AecState, NormalUsage) {
|
|||||||
for (int k = 0; k < 3000; ++k) {
|
for (int k = 0; k < 3000; ++k) {
|
||||||
render_delay_buffer->Insert(x);
|
render_delay_buffer->Insert(x);
|
||||||
state.Update(delay_estimate, converged_filter_frequency_response,
|
state.Update(delay_estimate, converged_filter_frequency_response,
|
||||||
impulse_response, true, false,
|
impulse_response, *render_delay_buffer->GetRenderBuffer(),
|
||||||
*render_delay_buffer->GetRenderBuffer(), E2_main, Y2, s);
|
E2_main, Y2, output, y);
|
||||||
}
|
}
|
||||||
EXPECT_TRUE(state.UsableLinearEstimate());
|
EXPECT_TRUE(state.UsableLinearEstimate());
|
||||||
|
|
||||||
@ -69,8 +72,8 @@ TEST(AecState, NormalUsage) {
|
|||||||
state.HandleEchoPathChange(EchoPathVariability(
|
state.HandleEchoPathChange(EchoPathVariability(
|
||||||
true, EchoPathVariability::DelayAdjustment::kNone, false));
|
true, EchoPathVariability::DelayAdjustment::kNone, false));
|
||||||
state.Update(delay_estimate, converged_filter_frequency_response,
|
state.Update(delay_estimate, converged_filter_frequency_response,
|
||||||
impulse_response, true, false,
|
impulse_response, *render_delay_buffer->GetRenderBuffer(),
|
||||||
*render_delay_buffer->GetRenderBuffer(), E2_main, Y2, s);
|
E2_main, Y2, output, y);
|
||||||
EXPECT_FALSE(state.UsableLinearEstimate());
|
EXPECT_FALSE(state.UsableLinearEstimate());
|
||||||
|
|
||||||
// Verify that the active render detection works as intended.
|
// Verify that the active render detection works as intended.
|
||||||
@ -79,15 +82,15 @@ TEST(AecState, NormalUsage) {
|
|||||||
state.HandleEchoPathChange(EchoPathVariability(
|
state.HandleEchoPathChange(EchoPathVariability(
|
||||||
true, EchoPathVariability::DelayAdjustment::kNewDetectedDelay, false));
|
true, EchoPathVariability::DelayAdjustment::kNewDetectedDelay, false));
|
||||||
state.Update(delay_estimate, converged_filter_frequency_response,
|
state.Update(delay_estimate, converged_filter_frequency_response,
|
||||||
impulse_response, true, false,
|
impulse_response, *render_delay_buffer->GetRenderBuffer(),
|
||||||
*render_delay_buffer->GetRenderBuffer(), E2_main, Y2, s);
|
E2_main, Y2, output, y);
|
||||||
EXPECT_FALSE(state.ActiveRender());
|
EXPECT_FALSE(state.ActiveRender());
|
||||||
|
|
||||||
for (int k = 0; k < 1000; ++k) {
|
for (int k = 0; k < 1000; ++k) {
|
||||||
render_delay_buffer->Insert(x);
|
render_delay_buffer->Insert(x);
|
||||||
state.Update(delay_estimate, converged_filter_frequency_response,
|
state.Update(delay_estimate, converged_filter_frequency_response,
|
||||||
impulse_response, true, false,
|
impulse_response, *render_delay_buffer->GetRenderBuffer(),
|
||||||
*render_delay_buffer->GetRenderBuffer(), E2_main, Y2, s);
|
E2_main, Y2, output, y);
|
||||||
}
|
}
|
||||||
EXPECT_TRUE(state.ActiveRender());
|
EXPECT_TRUE(state.ActiveRender());
|
||||||
|
|
||||||
@ -109,8 +112,8 @@ TEST(AecState, NormalUsage) {
|
|||||||
Y2.fill(10.f * 10000.f * 10000.f);
|
Y2.fill(10.f * 10000.f * 10000.f);
|
||||||
for (size_t k = 0; k < 1000; ++k) {
|
for (size_t k = 0; k < 1000; ++k) {
|
||||||
state.Update(delay_estimate, converged_filter_frequency_response,
|
state.Update(delay_estimate, converged_filter_frequency_response,
|
||||||
impulse_response, true, false,
|
impulse_response, *render_delay_buffer->GetRenderBuffer(),
|
||||||
*render_delay_buffer->GetRenderBuffer(), E2_main, Y2, s);
|
E2_main, Y2, output, y);
|
||||||
}
|
}
|
||||||
|
|
||||||
ASSERT_TRUE(state.UsableLinearEstimate());
|
ASSERT_TRUE(state.UsableLinearEstimate());
|
||||||
@ -126,8 +129,8 @@ TEST(AecState, NormalUsage) {
|
|||||||
Y2.fill(10.f * E2_main[0]);
|
Y2.fill(10.f * E2_main[0]);
|
||||||
for (size_t k = 0; k < 1000; ++k) {
|
for (size_t k = 0; k < 1000; ++k) {
|
||||||
state.Update(delay_estimate, converged_filter_frequency_response,
|
state.Update(delay_estimate, converged_filter_frequency_response,
|
||||||
impulse_response, true, false,
|
impulse_response, *render_delay_buffer->GetRenderBuffer(),
|
||||||
*render_delay_buffer->GetRenderBuffer(), E2_main, Y2, s);
|
E2_main, Y2, output, y);
|
||||||
}
|
}
|
||||||
ASSERT_TRUE(state.UsableLinearEstimate());
|
ASSERT_TRUE(state.UsableLinearEstimate());
|
||||||
{
|
{
|
||||||
@ -147,8 +150,8 @@ TEST(AecState, NormalUsage) {
|
|||||||
Y2.fill(5.f * E2_main[0]);
|
Y2.fill(5.f * E2_main[0]);
|
||||||
for (size_t k = 0; k < 1000; ++k) {
|
for (size_t k = 0; k < 1000; ++k) {
|
||||||
state.Update(delay_estimate, converged_filter_frequency_response,
|
state.Update(delay_estimate, converged_filter_frequency_response,
|
||||||
impulse_response, true, false,
|
impulse_response, *render_delay_buffer->GetRenderBuffer(),
|
||||||
*render_delay_buffer->GetRenderBuffer(), E2_main, Y2, s);
|
E2_main, Y2, output, y);
|
||||||
}
|
}
|
||||||
|
|
||||||
ASSERT_TRUE(state.UsableLinearEstimate());
|
ASSERT_TRUE(state.UsableLinearEstimate());
|
||||||
@ -179,9 +182,11 @@ TEST(AecState, ConvergedFilterDelay) {
|
|||||||
std::array<float, kBlockSize> x;
|
std::array<float, kBlockSize> x;
|
||||||
EchoPathVariability echo_path_variability(
|
EchoPathVariability echo_path_variability(
|
||||||
false, EchoPathVariability::DelayAdjustment::kNone, false);
|
false, EchoPathVariability::DelayAdjustment::kNone, false);
|
||||||
std::array<float, kBlockSize> s;
|
SubtractorOutput output;
|
||||||
s.fill(100.f);
|
std::array<float, kBlockSize> y;
|
||||||
|
output.s_main.fill(100.f);
|
||||||
x.fill(0.f);
|
x.fill(0.f);
|
||||||
|
y.fill(0.f);
|
||||||
|
|
||||||
std::vector<std::array<float, kFftLengthBy2Plus1>> frequency_response(
|
std::vector<std::array<float, kFftLengthBy2Plus1>> frequency_response(
|
||||||
kFilterLengthBlocks);
|
kFilterLengthBlocks);
|
||||||
@ -198,9 +203,9 @@ TEST(AecState, ConvergedFilterDelay) {
|
|||||||
impulse_response[k * kBlockSize + 1] = 1.f;
|
impulse_response[k * kBlockSize + 1] = 1.f;
|
||||||
|
|
||||||
state.HandleEchoPathChange(echo_path_variability);
|
state.HandleEchoPathChange(echo_path_variability);
|
||||||
state.Update(delay_estimate, frequency_response, impulse_response, true,
|
state.Update(delay_estimate, frequency_response, impulse_response,
|
||||||
false, *render_delay_buffer->GetRenderBuffer(), E2_main, Y2,
|
*render_delay_buffer->GetRenderBuffer(), E2_main, Y2, output,
|
||||||
s);
|
y);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -210,9 +210,8 @@ void EchoRemoverImpl::ProcessCapture(
|
|||||||
|
|
||||||
// Update the AEC state information.
|
// Update the AEC state information.
|
||||||
aec_state_.Update(external_delay, subtractor_.FilterFrequencyResponse(),
|
aec_state_.Update(external_delay, subtractor_.FilterFrequencyResponse(),
|
||||||
subtractor_.FilterImpulseResponse(),
|
subtractor_.FilterImpulseResponse(), *render_buffer, E2, Y2,
|
||||||
subtractor_.ConvergedFilter(), subtractor_.DivergedFilter(),
|
subtractor_output, y0);
|
||||||
*render_buffer, E2, Y2, subtractor_output.s_main);
|
|
||||||
|
|
||||||
// Compute spectra.
|
// Compute spectra.
|
||||||
const bool suppression_gain_uses_ffts =
|
const bool suppression_gain_uses_ffts =
|
||||||
|
@ -162,8 +162,9 @@ void RunFilterUpdateTest(int num_blocks_to_process,
|
|||||||
aec_state.HandleEchoPathChange(EchoPathVariability(
|
aec_state.HandleEchoPathChange(EchoPathVariability(
|
||||||
false, EchoPathVariability::DelayAdjustment::kNone, false));
|
false, EchoPathVariability::DelayAdjustment::kNone, false));
|
||||||
aec_state.Update(delay_estimate, main_filter.FilterFrequencyResponse(),
|
aec_state.Update(delay_estimate, main_filter.FilterFrequencyResponse(),
|
||||||
main_filter.FilterImpulseResponse(), true, false,
|
main_filter.FilterImpulseResponse(),
|
||||||
*render_delay_buffer->GetRenderBuffer(), E2_main, Y2, s);
|
*render_delay_buffer->GetRenderBuffer(), E2_main, Y2,
|
||||||
|
output, y);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::copy(e_main.begin(), e_main.end(), e_last_block->begin());
|
std::copy(e_main.begin(), e_main.end(), e_last_block->begin());
|
||||||
|
@ -61,7 +61,8 @@ TEST(ResidualEchoEstimator, DISABLED_BasicTest) {
|
|||||||
std::vector<std::vector<float>> x(3, std::vector<float>(kBlockSize, 0.f));
|
std::vector<std::vector<float>> x(3, std::vector<float>(kBlockSize, 0.f));
|
||||||
std::vector<std::array<float, kFftLengthBy2Plus1>> H2(10);
|
std::vector<std::array<float, kFftLengthBy2Plus1>> H2(10);
|
||||||
Random random_generator(42U);
|
Random random_generator(42U);
|
||||||
std::array<float, kBlockSize> s;
|
SubtractorOutput output;
|
||||||
|
std::array<float, kBlockSize> y;
|
||||||
Aec3Fft fft;
|
Aec3Fft fft;
|
||||||
absl::optional<DelayEstimate> delay_estimate;
|
absl::optional<DelayEstimate> delay_estimate;
|
||||||
|
|
||||||
@ -74,7 +75,9 @@ TEST(ResidualEchoEstimator, DISABLED_BasicTest) {
|
|||||||
std::vector<float> h(GetTimeDomainLength(config.filter.main.length_blocks),
|
std::vector<float> h(GetTimeDomainLength(config.filter.main.length_blocks),
|
||||||
0.f);
|
0.f);
|
||||||
|
|
||||||
s.fill(100.f);
|
output.Reset();
|
||||||
|
output.s_main.fill(100.f);
|
||||||
|
y.fill(0.f);
|
||||||
|
|
||||||
constexpr float kLevel = 10.f;
|
constexpr float kLevel = 10.f;
|
||||||
E2_shadow.fill(kLevel);
|
E2_shadow.fill(kLevel);
|
||||||
@ -93,8 +96,9 @@ TEST(ResidualEchoEstimator, DISABLED_BasicTest) {
|
|||||||
render_delay_buffer->PrepareCaptureProcessing();
|
render_delay_buffer->PrepareCaptureProcessing();
|
||||||
|
|
||||||
aec_state.HandleEchoPathChange(echo_path_variability);
|
aec_state.HandleEchoPathChange(echo_path_variability);
|
||||||
aec_state.Update(delay_estimate, H2, h, true, false,
|
aec_state.Update(delay_estimate, H2, h,
|
||||||
*render_delay_buffer->GetRenderBuffer(), E2_main, Y2, s);
|
*render_delay_buffer->GetRenderBuffer(), E2_main, Y2,
|
||||||
|
output, y);
|
||||||
|
|
||||||
estimator.Estimate(aec_state, *render_delay_buffer->GetRenderBuffer(),
|
estimator.Estimate(aec_state, *render_delay_buffer->GetRenderBuffer(),
|
||||||
S2_linear, Y2, &R2);
|
S2_linear, Y2, &R2);
|
||||||
|
@ -111,11 +111,8 @@ void Subtractor::HandleEchoPathChange(
|
|||||||
G_shadow_.HandleEchoPathChange();
|
G_shadow_.HandleEchoPathChange();
|
||||||
G_main_.SetConfig(config_.filter.main_initial, true);
|
G_main_.SetConfig(config_.filter.main_initial, true);
|
||||||
G_shadow_.SetConfig(config_.filter.shadow_initial, true);
|
G_shadow_.SetConfig(config_.filter.shadow_initial, true);
|
||||||
main_filter_converged_ = false;
|
|
||||||
shadow_filter_converged_ = false;
|
|
||||||
main_filter_.SetSizePartitions(config_.filter.main_initial.length_blocks,
|
main_filter_.SetSizePartitions(config_.filter.main_initial.length_blocks,
|
||||||
true);
|
true);
|
||||||
main_filter_once_converged_ = false;
|
|
||||||
shadow_filter_.SetSizePartitions(
|
shadow_filter_.SetSizePartitions(
|
||||||
config_.filter.shadow_initial.length_blocks, true);
|
config_.filter.shadow_initial.length_blocks, true);
|
||||||
};
|
};
|
||||||
@ -171,24 +168,8 @@ void Subtractor::Process(const RenderBuffer& render_buffer,
|
|||||||
&shadow_saturation);
|
&shadow_saturation);
|
||||||
fft_.ZeroPaddedFft(e_shadow, Aec3Fft::Window::kHanning, &E_shadow);
|
fft_.ZeroPaddedFft(e_shadow, Aec3Fft::Window::kHanning, &E_shadow);
|
||||||
|
|
||||||
// Check for filter convergence.
|
|
||||||
const auto sum_of_squares = [](float a, float b) { return a + b * b; };
|
|
||||||
const float y2 = std::accumulate(y.begin(), y.end(), 0.f, sum_of_squares);
|
|
||||||
const float e2_main =
|
|
||||||
std::accumulate(e_main.begin(), e_main.end(), 0.f, sum_of_squares);
|
|
||||||
const float e2_shadow =
|
|
||||||
std::accumulate(e_shadow.begin(), e_shadow.end(), 0.f, sum_of_squares);
|
|
||||||
|
|
||||||
constexpr float kConvergenceThreshold = 50 * 50 * kBlockSize;
|
|
||||||
main_filter_converged_ = e2_main < 0.5f * y2 && y2 > kConvergenceThreshold;
|
|
||||||
shadow_filter_converged_ =
|
|
||||||
e2_shadow < 0.05 * y2 && y2 > kConvergenceThreshold;
|
|
||||||
main_filter_once_converged_ =
|
|
||||||
main_filter_once_converged_ || main_filter_converged_;
|
|
||||||
main_filter_diverged_ = e2_main > 1.5f * y2 && y2 > 30.f * 30.f * kBlockSize;
|
|
||||||
|
|
||||||
if (enable_misadjustment_estimator_) {
|
if (enable_misadjustment_estimator_) {
|
||||||
filter_misadjustment_estimator_.Update(e2_main, y2);
|
filter_misadjustment_estimator_.Update(e_main, y);
|
||||||
if (filter_misadjustment_estimator_.IsAdjustmentNeeded()) {
|
if (filter_misadjustment_estimator_.IsAdjustmentNeeded()) {
|
||||||
float scale = filter_misadjustment_estimator_.GetMisadjustment();
|
float scale = filter_misadjustment_estimator_.GetMisadjustment();
|
||||||
main_filter_.ScaleFilter(scale);
|
main_filter_.ScaleFilter(scale);
|
||||||
@ -229,7 +210,13 @@ void Subtractor::Process(const RenderBuffer& render_buffer,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Subtractor::FilterMisadjustmentEstimator::Update(float e2, float y2) {
|
void Subtractor::FilterMisadjustmentEstimator::Update(
|
||||||
|
rtc::ArrayView<const float> e,
|
||||||
|
rtc::ArrayView<const float> y) {
|
||||||
|
const auto sum_of_squares = [](float a, float b) { return a + b * b; };
|
||||||
|
const float y2 = std::accumulate(y.begin(), y.end(), 0.f, sum_of_squares);
|
||||||
|
const float e2 = std::accumulate(e.begin(), e.end(), 0.f, sum_of_squares);
|
||||||
|
|
||||||
e2_acum_ += e2;
|
e2_acum_ += e2;
|
||||||
y2_acum_ += y2;
|
y2_acum_ += y2;
|
||||||
if (++n_blocks_acum_ == n_blocks_) {
|
if (++n_blocks_acum_ == n_blocks_) {
|
||||||
|
@ -54,24 +54,14 @@ class Subtractor {
|
|||||||
// Returns the block-wise frequency response for the main adaptive filter.
|
// Returns the block-wise frequency response for the main adaptive filter.
|
||||||
const std::vector<std::array<float, kFftLengthBy2Plus1>>&
|
const std::vector<std::array<float, kFftLengthBy2Plus1>>&
|
||||||
FilterFrequencyResponse() const {
|
FilterFrequencyResponse() const {
|
||||||
return main_filter_once_converged_ || (!shadow_filter_converged_)
|
return main_filter_.FilterFrequencyResponse();
|
||||||
? main_filter_.FilterFrequencyResponse()
|
|
||||||
: shadow_filter_.FilterFrequencyResponse();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns the estimate of the impulse response for the main adaptive filter.
|
// Returns the estimate of the impulse response for the main adaptive filter.
|
||||||
const std::vector<float>& FilterImpulseResponse() const {
|
const std::vector<float>& FilterImpulseResponse() const {
|
||||||
return main_filter_once_converged_ || (!shadow_filter_converged_)
|
return main_filter_.FilterImpulseResponse();
|
||||||
? main_filter_.FilterImpulseResponse()
|
|
||||||
: shadow_filter_.FilterImpulseResponse();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ConvergedFilter() const {
|
|
||||||
return main_filter_converged_ || shadow_filter_converged_;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool DivergedFilter() const { return main_filter_diverged_; }
|
|
||||||
|
|
||||||
void DumpFilters() {
|
void DumpFilters() {
|
||||||
main_filter_.DumpFilter("aec3_subtractor_H_main", "aec3_subtractor_h_main");
|
main_filter_.DumpFilter("aec3_subtractor_H_main", "aec3_subtractor_h_main");
|
||||||
shadow_filter_.DumpFilter("aec3_subtractor_H_shadow",
|
shadow_filter_.DumpFilter("aec3_subtractor_H_shadow",
|
||||||
@ -84,7 +74,7 @@ class Subtractor {
|
|||||||
FilterMisadjustmentEstimator() = default;
|
FilterMisadjustmentEstimator() = default;
|
||||||
~FilterMisadjustmentEstimator() = default;
|
~FilterMisadjustmentEstimator() = default;
|
||||||
// Update the misadjustment estimator.
|
// Update the misadjustment estimator.
|
||||||
void Update(float e2, float y2);
|
void Update(rtc::ArrayView<const float> e, rtc::ArrayView<const float> y);
|
||||||
// GetMisadjustment() Returns a recommended scale for the filter so the
|
// GetMisadjustment() Returns a recommended scale for the filter so the
|
||||||
// prediction error energy gets closer to the energy that is seen at the
|
// prediction error energy gets closer to the energy that is seen at the
|
||||||
// microphone input.
|
// microphone input.
|
||||||
@ -120,10 +110,6 @@ class Subtractor {
|
|||||||
AdaptiveFirFilter shadow_filter_;
|
AdaptiveFirFilter shadow_filter_;
|
||||||
MainFilterUpdateGain G_main_;
|
MainFilterUpdateGain G_main_;
|
||||||
ShadowFilterUpdateGain G_shadow_;
|
ShadowFilterUpdateGain G_shadow_;
|
||||||
bool main_filter_converged_ = false;
|
|
||||||
bool main_filter_once_converged_ = false;
|
|
||||||
bool shadow_filter_converged_ = false;
|
|
||||||
bool main_filter_diverged_ = false;
|
|
||||||
FilterMisadjustmentEstimator filter_misadjustment_estimator_;
|
FilterMisadjustmentEstimator filter_misadjustment_estimator_;
|
||||||
RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(Subtractor);
|
RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(Subtractor);
|
||||||
};
|
};
|
||||||
|
44
modules/audio_processing/aec3/subtractor_output_analyzer.cc
Normal file
44
modules/audio_processing/aec3/subtractor_output_analyzer.cc
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2018 The WebRTC project authors. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by a BSD-style license
|
||||||
|
* that can be found in the LICENSE file in the root of the source
|
||||||
|
* tree. An additional intellectual property rights grant can be found
|
||||||
|
* in the file PATENTS. All contributing project authors may
|
||||||
|
* be found in the AUTHORS file in the root of the source tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "modules/audio_processing/aec3/subtractor_output_analyzer.h"
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <numeric>
|
||||||
|
|
||||||
|
namespace webrtc {
|
||||||
|
|
||||||
|
void SubtractorOutputAnalyzer::Update(
|
||||||
|
rtc::ArrayView<const float> y,
|
||||||
|
const SubtractorOutput& subtractor_output) {
|
||||||
|
const auto& e_main = subtractor_output.e_main;
|
||||||
|
const auto& e_shadow = subtractor_output.e_shadow;
|
||||||
|
|
||||||
|
const auto sum_of_squares = [](float a, float b) { return a + b * b; };
|
||||||
|
const float y2 = std::accumulate(y.begin(), y.end(), 0.f, sum_of_squares);
|
||||||
|
const float e2_main =
|
||||||
|
std::accumulate(e_main.begin(), e_main.end(), 0.f, sum_of_squares);
|
||||||
|
const float e2_shadow =
|
||||||
|
std::accumulate(e_shadow.begin(), e_shadow.end(), 0.f, sum_of_squares);
|
||||||
|
|
||||||
|
constexpr float kConvergenceThreshold = 50 * 50 * kBlockSize;
|
||||||
|
main_filter_converged_ = e2_main < 0.5f * y2 && y2 > kConvergenceThreshold;
|
||||||
|
shadow_filter_converged_ =
|
||||||
|
e2_shadow < 0.05 * y2 && y2 > kConvergenceThreshold;
|
||||||
|
main_filter_diverged_ = e2_main > 1.5f * y2 && y2 > 30.f * 30.f * kBlockSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SubtractorOutputAnalyzer::HandleEchoPathChange() {
|
||||||
|
shadow_filter_converged_ = false;
|
||||||
|
main_filter_converged_ = false;
|
||||||
|
main_filter_diverged_ = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace webrtc
|
46
modules/audio_processing/aec3/subtractor_output_analyzer.h
Normal file
46
modules/audio_processing/aec3/subtractor_output_analyzer.h
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2018 The WebRTC project authors. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by a BSD-style license
|
||||||
|
* that can be found in the LICENSE file in the root of the source
|
||||||
|
* tree. An additional intellectual property rights grant can be found
|
||||||
|
* in the file PATENTS. All contributing project authors may
|
||||||
|
* be found in the AUTHORS file in the root of the source tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef MODULES_AUDIO_PROCESSING_AEC3_SUBTRACTOR_OUTPUT_ANALYZER_H_
|
||||||
|
#define MODULES_AUDIO_PROCESSING_AEC3_SUBTRACTOR_OUTPUT_ANALYZER_H_
|
||||||
|
|
||||||
|
#include "api/array_view.h"
|
||||||
|
#include "modules/audio_processing/aec3/subtractor_output.h"
|
||||||
|
|
||||||
|
namespace webrtc {
|
||||||
|
|
||||||
|
// Class for analyzing the properties subtractor output
|
||||||
|
class SubtractorOutputAnalyzer {
|
||||||
|
public:
|
||||||
|
SubtractorOutputAnalyzer() = default;
|
||||||
|
~SubtractorOutputAnalyzer() = default;
|
||||||
|
|
||||||
|
// Analyses the subtractor output.
|
||||||
|
void Update(rtc::ArrayView<const float> y,
|
||||||
|
const SubtractorOutput& subtractor_output);
|
||||||
|
|
||||||
|
bool ConvergedFilter() const {
|
||||||
|
return main_filter_converged_ || shadow_filter_converged_;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DivergedFilter() const { return main_filter_diverged_; }
|
||||||
|
|
||||||
|
// Handle echo path change.
|
||||||
|
void HandleEchoPathChange();
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool shadow_filter_converged_ = false;
|
||||||
|
bool main_filter_converged_ = false;
|
||||||
|
bool main_filter_diverged_ = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace webrtc
|
||||||
|
|
||||||
|
#endif // MODULES_AUDIO_PROCESSING_AEC3_SUBTRACTOR_OUTPUT_ANALYZER_H_
|
@ -85,9 +85,8 @@ float RunSubtractorTest(int num_blocks_to_process,
|
|||||||
false, EchoPathVariability::DelayAdjustment::kNone, false));
|
false, EchoPathVariability::DelayAdjustment::kNone, false));
|
||||||
aec_state.Update(delay_estimate, subtractor.FilterFrequencyResponse(),
|
aec_state.Update(delay_estimate, subtractor.FilterFrequencyResponse(),
|
||||||
subtractor.FilterImpulseResponse(),
|
subtractor.FilterImpulseResponse(),
|
||||||
subtractor.ConvergedFilter(), subtractor.DivergedFilter(),
|
|
||||||
*render_delay_buffer->GetRenderBuffer(), E2_main, Y2,
|
*render_delay_buffer->GetRenderBuffer(), E2_main, Y2,
|
||||||
output.s_main);
|
output, y);
|
||||||
}
|
}
|
||||||
|
|
||||||
const float output_power = std::inner_product(
|
const float output_power = std::inner_product(
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
#include "modules/audio_processing/aec3/aec_state.h"
|
#include "modules/audio_processing/aec3/aec_state.h"
|
||||||
#include "modules/audio_processing/aec3/render_delay_buffer.h"
|
#include "modules/audio_processing/aec3/render_delay_buffer.h"
|
||||||
#include "modules/audio_processing/aec3/subtractor.h"
|
#include "modules/audio_processing/aec3/subtractor.h"
|
||||||
|
#include "modules/audio_processing/aec3/subtractor_output.h"
|
||||||
#include "modules/audio_processing/logging/apm_data_dumper.h"
|
#include "modules/audio_processing/logging/apm_data_dumper.h"
|
||||||
#include "rtc_base/checks.h"
|
#include "rtc_base/checks.h"
|
||||||
#include "system_wrappers/include/cpu_features_wrapper.h"
|
#include "system_wrappers/include/cpu_features_wrapper.h"
|
||||||
@ -67,7 +68,8 @@ TEST(SuppressionGain, BasicGainComputation) {
|
|||||||
std::array<float, kFftLengthBy2Plus1> R2;
|
std::array<float, kFftLengthBy2Plus1> R2;
|
||||||
std::array<float, kFftLengthBy2Plus1> N2;
|
std::array<float, kFftLengthBy2Plus1> N2;
|
||||||
std::array<float, kFftLengthBy2Plus1> g;
|
std::array<float, kFftLengthBy2Plus1> g;
|
||||||
std::array<float, kBlockSize> s;
|
SubtractorOutput output;
|
||||||
|
std::array<float, kBlockSize> y;
|
||||||
FftData E;
|
FftData E;
|
||||||
FftData X;
|
FftData X;
|
||||||
FftData Y;
|
FftData Y;
|
||||||
@ -85,7 +87,8 @@ TEST(SuppressionGain, BasicGainComputation) {
|
|||||||
Y2.fill(10.f);
|
Y2.fill(10.f);
|
||||||
R2.fill(0.1f);
|
R2.fill(0.1f);
|
||||||
N2.fill(100.f);
|
N2.fill(100.f);
|
||||||
s.fill(10.f);
|
output.Reset();
|
||||||
|
y.fill(0.f);
|
||||||
E.re.fill(sqrtf(E2[0]));
|
E.re.fill(sqrtf(E2[0]));
|
||||||
E.im.fill(0.f);
|
E.im.fill(0.f);
|
||||||
X.re.fill(sqrtf(R2[0]));
|
X.re.fill(sqrtf(R2[0]));
|
||||||
@ -97,15 +100,15 @@ TEST(SuppressionGain, BasicGainComputation) {
|
|||||||
for (int k = 0; k <= kNumBlocksPerSecond / 5 + 1; ++k) {
|
for (int k = 0; k <= kNumBlocksPerSecond / 5 + 1; ++k) {
|
||||||
aec_state.Update(delay_estimate, subtractor.FilterFrequencyResponse(),
|
aec_state.Update(delay_estimate, subtractor.FilterFrequencyResponse(),
|
||||||
subtractor.FilterImpulseResponse(),
|
subtractor.FilterImpulseResponse(),
|
||||||
subtractor.ConvergedFilter(), subtractor.DivergedFilter(),
|
*render_delay_buffer->GetRenderBuffer(), E2, Y2, output,
|
||||||
*render_delay_buffer->GetRenderBuffer(), E2, Y2, s);
|
y);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int k = 0; k < 100; ++k) {
|
for (int k = 0; k < 100; ++k) {
|
||||||
aec_state.Update(delay_estimate, subtractor.FilterFrequencyResponse(),
|
aec_state.Update(delay_estimate, subtractor.FilterFrequencyResponse(),
|
||||||
subtractor.FilterImpulseResponse(),
|
subtractor.FilterImpulseResponse(),
|
||||||
subtractor.ConvergedFilter(), subtractor.DivergedFilter(),
|
*render_delay_buffer->GetRenderBuffer(), E2, Y2, output,
|
||||||
*render_delay_buffer->GetRenderBuffer(), E2, Y2, s);
|
y);
|
||||||
suppression_gain.GetGain(E2, R2, N2, E, X, Y, analyzer, aec_state, x,
|
suppression_gain.GetGain(E2, R2, N2, E, X, Y, analyzer, aec_state, x,
|
||||||
&high_bands_gain, &g);
|
&high_bands_gain, &g);
|
||||||
}
|
}
|
||||||
@ -124,8 +127,8 @@ TEST(SuppressionGain, BasicGainComputation) {
|
|||||||
for (int k = 0; k < 100; ++k) {
|
for (int k = 0; k < 100; ++k) {
|
||||||
aec_state.Update(delay_estimate, subtractor.FilterFrequencyResponse(),
|
aec_state.Update(delay_estimate, subtractor.FilterFrequencyResponse(),
|
||||||
subtractor.FilterImpulseResponse(),
|
subtractor.FilterImpulseResponse(),
|
||||||
subtractor.ConvergedFilter(), subtractor.DivergedFilter(),
|
*render_delay_buffer->GetRenderBuffer(), E2, Y2, output,
|
||||||
*render_delay_buffer->GetRenderBuffer(), E2, Y2, s);
|
y);
|
||||||
suppression_gain.GetGain(E2, R2, N2, E, X, Y, analyzer, aec_state, x,
|
suppression_gain.GetGain(E2, R2, N2, E, X, Y, analyzer, aec_state, x,
|
||||||
&high_bands_gain, &g);
|
&high_bands_gain, &g);
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user