AEC3: General cleanup after multichannel changes

This CL contains various cleanups/corrections to the multichannel AEC
code.

The changes have been shown to be bitexact over a large dataset.

Bug: webrtc:10913
Change-Id: Idd3e410b04527666e052f57ad81d0ac9eef3179b
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/157173
Reviewed-by: Gustaf Ullberg <gustaf@webrtc.org>
Commit-Queue: Per Åhgren <peah@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#29530}
This commit is contained in:
Per Åhgren 2019-10-18 08:50:50 +02:00 committed by Commit Bot
parent 2167163770
commit 119e2197b7
16 changed files with 515 additions and 502 deletions

View File

@ -329,150 +329,155 @@ TEST(AdaptiveFirFilter, FilterAndAdapt) {
constexpr int kSampleRateHz = 48000;
constexpr size_t kNumBands = NumBandsForRate(kSampleRateHz);
constexpr size_t kNumBlocksToProcessPerRenderChannel = 1000;
constexpr size_t kNumCaptureChannels = 1;
for (size_t num_render_channels : {1, 2, 3, 6, 8}) {
ApmDataDumper data_dumper(42);
EchoCanceller3Config config;
for (size_t num_capture_channels : {1, 2, 4}) {
for (size_t num_render_channels : {1, 2, 3, 6, 8}) {
ApmDataDumper data_dumper(42);
EchoCanceller3Config config;
if (num_render_channels == 33) {
config.filter.main = {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.shadow_initial = {12, 0.7f, 20075344.f};
}
AdaptiveFirFilter filter(
config.filter.main.length_blocks, config.filter.main.length_blocks,
config.filter.config_change_duration_blocks, num_render_channels,
DetectOptimization(), &data_dumper);
std::vector<std::vector<std::array<float, kFftLengthBy2Plus1>>> H2(
kNumCaptureChannels, std::vector<std::array<float, kFftLengthBy2Plus1>>(
filter.max_filter_size_partitions(),
std::array<float, kFftLengthBy2Plus1>()));
std::vector<std::vector<float>> h(
kNumCaptureChannels,
std::vector<float>(
GetTimeDomainLength(filter.max_filter_size_partitions()), 0.f));
Aec3Fft fft;
config.delay.default_delay = 1;
std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
RenderDelayBuffer::Create(config, kSampleRateHz, num_render_channels));
ShadowFilterUpdateGain gain(config.filter.shadow,
config.filter.config_change_duration_blocks);
Random random_generator(42U);
std::vector<std::vector<std::vector<float>>> x(
kNumBands,
std::vector<std::vector<float>>(num_render_channels,
std::vector<float>(kBlockSize, 0.f)));
std::vector<float> n(kBlockSize, 0.f);
std::vector<float> y(kBlockSize, 0.f);
AecState aec_state(EchoCanceller3Config{}, kNumCaptureChannels);
RenderSignalAnalyzer render_signal_analyzer(config);
absl::optional<DelayEstimate> delay_estimate;
std::vector<float> e(kBlockSize, 0.f);
std::array<float, kFftLength> s_scratch;
std::vector<SubtractorOutput> output(kNumCaptureChannels);
FftData S;
FftData G;
FftData E;
std::vector<std::array<float, kFftLengthBy2Plus1>> Y2(kNumCaptureChannels);
std::vector<std::array<float, kFftLengthBy2Plus1>> E2_main(
kNumCaptureChannels);
std::array<float, kFftLengthBy2Plus1> E2_shadow;
// [B,A] = butter(2,100/8000,'high')
constexpr CascadedBiQuadFilter::BiQuadCoefficients
kHighPassFilterCoefficients = {{0.97261f, -1.94523f, 0.97261f},
{-1.94448f, 0.94598f}};
for (auto& Y2_ch : Y2) {
Y2_ch.fill(0.f);
}
for (auto& E2_main_ch : E2_main) {
E2_main_ch.fill(0.f);
}
E2_shadow.fill(0.f);
for (auto& subtractor_output : output) {
subtractor_output.Reset();
}
constexpr float kScale = 1.0f / kFftLengthBy2;
for (size_t delay_samples : {0, 64, 150, 200, 301}) {
std::vector<DelayBuffer<float>> delay_buffer(
num_render_channels, DelayBuffer<float>(delay_samples));
std::vector<std::unique_ptr<CascadedBiQuadFilter>> x_hp_filter(
num_render_channels);
for (size_t ch = 0; ch < num_render_channels; ++ch) {
x_hp_filter[ch] = std::make_unique<CascadedBiQuadFilter>(
kHighPassFilterCoefficients, 1);
if (num_render_channels == 33) {
config.filter.main = {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.shadow_initial = {12, 0.7f, 20075344.f};
}
CascadedBiQuadFilter y_hp_filter(kHighPassFilterCoefficients, 1);
SCOPED_TRACE(ProduceDebugText(num_render_channels, delay_samples));
const size_t num_blocks_to_process =
kNumBlocksToProcessPerRenderChannel * num_render_channels;
for (size_t j = 0; j < num_blocks_to_process; ++j) {
std::fill(y.begin(), y.end(), 0.f);
AdaptiveFirFilter filter(
config.filter.main.length_blocks, config.filter.main.length_blocks,
config.filter.config_change_duration_blocks, num_render_channels,
DetectOptimization(), &data_dumper);
std::vector<std::vector<std::array<float, kFftLengthBy2Plus1>>> H2(
num_capture_channels,
std::vector<std::array<float, kFftLengthBy2Plus1>>(
filter.max_filter_size_partitions(),
std::array<float, kFftLengthBy2Plus1>()));
std::vector<std::vector<float>> h(
num_capture_channels,
std::vector<float>(
GetTimeDomainLength(filter.max_filter_size_partitions()), 0.f));
Aec3Fft fft;
config.delay.default_delay = 1;
std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
RenderDelayBuffer::Create(config, kSampleRateHz,
num_render_channels));
ShadowFilterUpdateGain gain(config.filter.shadow,
config.filter.config_change_duration_blocks);
Random random_generator(42U);
std::vector<std::vector<std::vector<float>>> x(
kNumBands,
std::vector<std::vector<float>>(num_render_channels,
std::vector<float>(kBlockSize, 0.f)));
std::vector<float> n(kBlockSize, 0.f);
std::vector<float> y(kBlockSize, 0.f);
AecState aec_state(EchoCanceller3Config{}, num_capture_channels);
RenderSignalAnalyzer render_signal_analyzer(config);
absl::optional<DelayEstimate> delay_estimate;
std::vector<float> e(kBlockSize, 0.f);
std::array<float, kFftLength> s_scratch;
std::vector<SubtractorOutput> output(num_capture_channels);
FftData S;
FftData G;
FftData E;
std::vector<std::array<float, kFftLengthBy2Plus1>> Y2(
num_capture_channels);
std::vector<std::array<float, kFftLengthBy2Plus1>> E2_main(
num_capture_channels);
std::array<float, kFftLengthBy2Plus1> E2_shadow;
// [B,A] = butter(2,100/8000,'high')
constexpr CascadedBiQuadFilter::BiQuadCoefficients
kHighPassFilterCoefficients = {{0.97261f, -1.94523f, 0.97261f},
{-1.94448f, 0.94598f}};
for (auto& Y2_ch : Y2) {
Y2_ch.fill(0.f);
}
for (auto& E2_main_ch : E2_main) {
E2_main_ch.fill(0.f);
}
E2_shadow.fill(0.f);
for (auto& subtractor_output : output) {
subtractor_output.Reset();
}
constexpr float kScale = 1.0f / kFftLengthBy2;
for (size_t delay_samples : {0, 64, 150, 200, 301}) {
std::vector<DelayBuffer<float>> delay_buffer(
num_render_channels, DelayBuffer<float>(delay_samples));
std::vector<std::unique_ptr<CascadedBiQuadFilter>> x_hp_filter(
num_render_channels);
for (size_t ch = 0; ch < num_render_channels; ++ch) {
RandomizeSampleVector(&random_generator, x[0][ch]);
std::array<float, kBlockSize> y_channel;
delay_buffer[ch].Delay(x[0][ch], y_channel);
x_hp_filter[ch] = std::make_unique<CascadedBiQuadFilter>(
kHighPassFilterCoefficients, 1);
}
CascadedBiQuadFilter y_hp_filter(kHighPassFilterCoefficients, 1);
SCOPED_TRACE(ProduceDebugText(num_render_channels, delay_samples));
const size_t num_blocks_to_process =
kNumBlocksToProcessPerRenderChannel * num_render_channels;
for (size_t j = 0; j < num_blocks_to_process; ++j) {
std::fill(y.begin(), y.end(), 0.f);
for (size_t ch = 0; ch < num_render_channels; ++ch) {
RandomizeSampleVector(&random_generator, x[0][ch]);
std::array<float, kBlockSize> y_channel;
delay_buffer[ch].Delay(x[0][ch], y_channel);
for (size_t k = 0; k < y.size(); ++k) {
y[k] += y_channel[k] / num_render_channels;
}
}
RandomizeSampleVector(&random_generator, n);
const float noise_scaling = 1.f / 100.f / num_render_channels;
for (size_t k = 0; k < y.size(); ++k) {
y[k] += y_channel[k] / num_render_channels;
y[k] += n[k] * noise_scaling;
}
}
RandomizeSampleVector(&random_generator, n);
const float noise_scaling = 1.f / 100.f / num_render_channels;
for (size_t k = 0; k < y.size(); ++k) {
y[k] += n[k] * noise_scaling;
}
for (size_t ch = 0; ch < num_render_channels; ++ch) {
x_hp_filter[ch]->Process(x[0][ch]);
}
y_hp_filter.Process(y);
render_delay_buffer->Insert(x);
if (j == 0) {
render_delay_buffer->Reset();
}
render_delay_buffer->PrepareCaptureProcessing();
auto* const render_buffer = render_delay_buffer->GetRenderBuffer();
render_signal_analyzer.Update(*render_buffer,
aec_state.MinDirectPathFilterDelay());
filter.Filter(*render_buffer, &S);
fft.Ifft(S, &s_scratch);
std::transform(y.begin(), y.end(), s_scratch.begin() + kFftLengthBy2,
e.begin(),
[&](float a, float b) { return a - b * kScale; });
std::for_each(e.begin(), e.end(), [](float& a) {
a = rtc::SafeClamp(a, -32768.f, 32767.f);
});
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];
for (size_t ch = 0; ch < num_render_channels; ++ch) {
x_hp_filter[ch]->Process(x[0][ch]);
}
y_hp_filter.Process(y);
render_delay_buffer->Insert(x);
if (j == 0) {
render_delay_buffer->Reset();
}
render_delay_buffer->PrepareCaptureProcessing();
auto* const render_buffer = render_delay_buffer->GetRenderBuffer();
render_signal_analyzer.Update(*render_buffer,
aec_state.MinDirectPathFilterDelay());
filter.Filter(*render_buffer, &S);
fft.Ifft(S, &s_scratch);
std::transform(y.begin(), y.end(), s_scratch.begin() + kFftLengthBy2,
e.begin(),
[&](float a, float b) { return a - b * kScale; });
std::for_each(e.begin(), e.end(), [](float& a) {
a = rtc::SafeClamp(a, -32768.f, 32767.f);
});
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];
}
}
std::array<float, kFftLengthBy2Plus1> render_power;
render_buffer->SpectralSum(filter.SizePartitions(), &render_power);
gain.Compute(render_power, render_signal_analyzer, E,
filter.SizePartitions(), false, &G);
filter.Adapt(*render_buffer, G, &h[0]);
aec_state.HandleEchoPathChange(EchoPathVariability(
false, EchoPathVariability::DelayAdjustment::kNone, false));
filter.ComputeFrequencyResponse(&H2[0]);
aec_state.Update(delay_estimate, H2, h, *render_buffer, E2_main, Y2,
output);
}
std::array<float, kFftLengthBy2Plus1> render_power;
render_buffer->SpectralSum(filter.SizePartitions(), &render_power);
gain.Compute(render_power, render_signal_analyzer, E,
filter.SizePartitions(), false, &G);
filter.Adapt(*render_buffer, G, &h[0]);
aec_state.HandleEchoPathChange(EchoPathVariability(
false, EchoPathVariability::DelayAdjustment::kNone, false));
filter.ComputeFrequencyResponse(&H2[0]);
aec_state.Update(delay_estimate, H2, h, *render_buffer, E2_main, Y2,
output);
// Verify that the filter is able to perform well.
EXPECT_LT(1000 * std::inner_product(e.begin(), e.end(), e.begin(), 0.f),
std::inner_product(y.begin(), y.end(), y.begin(), 0.f));
}
// Verify that the filter is able to perform well.
EXPECT_LT(1000 * std::inner_product(e.begin(), e.end(), e.begin(), 0.f),
std::inner_product(y.begin(), y.end(), y.begin(), 0.f));
}
}
}

View File

@ -127,8 +127,7 @@ class AecState {
}
// Updates the aec state.
// TODO(bugs.webrtc.org/10913): Handle multi-channel adaptive filter response.
// TODO(bugs.webrtc.org/10913): Compute multi-channel ERL, ERLE, and reverb.
// TODO(bugs.webrtc.org/10913): Compute multi-channel ERL.
void Update(
const absl::optional<DelayEstimate>& external_delay,
rtc::ArrayView<const std::vector<std::array<float, kFftLengthBy2Plus1>>>

View File

@ -105,7 +105,7 @@ ComfortNoiseGenerator::ComfortNoiseGenerator(Aec3Optimization optimization,
ComfortNoiseGenerator::~ComfortNoiseGenerator() = default;
void ComfortNoiseGenerator::Compute(
const AecState& aec_state,
bool saturated_capture,
const std::array<float, kFftLengthBy2Plus1>& capture_spectrum,
FftData* lower_band_noise,
FftData* upper_band_noise) {
@ -113,7 +113,7 @@ void ComfortNoiseGenerator::Compute(
RTC_DCHECK(upper_band_noise);
const auto& Y2 = capture_spectrum;
if (!aec_state.SaturatedCapture()) {
if (!saturated_capture) {
// Smooth Y2.
std::transform(Y2_smoothed_.begin(), Y2_smoothed_.end(), Y2.begin(),
Y2_smoothed_.begin(),

View File

@ -45,7 +45,7 @@ class ComfortNoiseGenerator {
~ComfortNoiseGenerator();
// Computes the comfort noise.
void Compute(const AecState& aec_state,
void Compute(bool saturated_capture,
const std::array<float, kFftLengthBy2Plus1>& capture_spectrum,
FftData* lower_band_noise,
FftData* upper_band_noise);

View File

@ -36,19 +36,17 @@ float Power(const FftData& N) {
TEST(ComfortNoiseGenerator, NullLowerBandNoise) {
std::array<float, kFftLengthBy2Plus1> N2;
FftData noise;
EXPECT_DEATH(
ComfortNoiseGenerator(DetectOptimization(), 42)
.Compute(AecState(EchoCanceller3Config{}, 1), N2, nullptr, &noise),
"");
EXPECT_DEATH(ComfortNoiseGenerator(DetectOptimization(), 42)
.Compute(false, N2, nullptr, &noise),
"");
}
TEST(ComfortNoiseGenerator, NullUpperBandNoise) {
std::array<float, kFftLengthBy2Plus1> N2;
FftData noise;
EXPECT_DEATH(
ComfortNoiseGenerator(DetectOptimization(), 42)
.Compute(AecState(EchoCanceller3Config{}, 1), N2, &noise, nullptr),
"");
EXPECT_DEATH(ComfortNoiseGenerator(DetectOptimization(), 42)
.Compute(false, N2, &noise, nullptr),
"");
}
#endif
@ -68,12 +66,12 @@ TEST(ComfortNoiseGenerator, CorrectLevel) {
n_upper.im.fill(0.f);
// Ensure instantaneous updata to nonzero noise.
cng.Compute(aec_state, N2, &n_lower, &n_upper);
cng.Compute(false, N2, &n_lower, &n_upper);
EXPECT_LT(0.f, Power(n_lower));
EXPECT_LT(0.f, Power(n_upper));
for (int k = 0; k < 10000; ++k) {
cng.Compute(aec_state, N2, &n_lower, &n_upper);
cng.Compute(false, N2, &n_lower, &n_upper);
}
EXPECT_NEAR(2.f * N2[0], Power(n_lower), N2[0] / 10.f);
EXPECT_NEAR(2.f * N2[0], Power(n_upper), N2[0] / 10.f);

View File

@ -36,37 +36,44 @@ std::string ProduceDebugText(size_t delay, size_t down_sampling_factor) {
// Verifies that the basic API calls work.
TEST(EchoPathDelayEstimator, BasicApiCalls) {
constexpr size_t kNumChannels = 1;
constexpr int kSampleRateHz = 48000;
constexpr size_t kNumBands = NumBandsForRate(kSampleRateHz);
ApmDataDumper data_dumper(0);
EchoCanceller3Config config;
std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
RenderDelayBuffer::Create(config, kSampleRateHz, kNumChannels));
EchoPathDelayEstimator estimator(&data_dumper, config);
std::vector<std::vector<std::vector<float>>> render(
kNumBands, std::vector<std::vector<float>>(
kNumChannels, std::vector<float>(kBlockSize)));
std::vector<std::vector<float>> capture(1, std::vector<float>(kBlockSize));
for (size_t k = 0; k < 100; ++k) {
render_delay_buffer->Insert(render);
estimator.EstimateDelay(render_delay_buffer->GetDownsampledRenderBuffer(),
capture);
for (size_t num_capture_channels : {1, 2, 4}) {
for (size_t num_render_channels : {1, 2, 3, 6, 8}) {
ApmDataDumper data_dumper(0);
EchoCanceller3Config config;
std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
RenderDelayBuffer::Create(config, kSampleRateHz,
num_render_channels));
EchoPathDelayEstimator estimator(&data_dumper, config);
std::vector<std::vector<std::vector<float>>> render(
kNumBands, std::vector<std::vector<float>>(
num_render_channels, std::vector<float>(kBlockSize)));
std::vector<std::vector<float>> capture(num_capture_channels,
std::vector<float>(kBlockSize));
for (size_t k = 0; k < 100; ++k) {
render_delay_buffer->Insert(render);
estimator.EstimateDelay(
render_delay_buffer->GetDownsampledRenderBuffer(), capture);
}
}
}
}
// Verifies that the delay estimator produces correct delay for artificially
// delayed signals.
TEST(EchoPathDelayEstimator, DelayEstimation) {
constexpr size_t kNumChannels = 1;
constexpr size_t kNumRenderChannels = 1;
constexpr size_t kNumCaptureChannels = 1;
constexpr int kSampleRateHz = 48000;
constexpr size_t kNumBands = NumBandsForRate(kSampleRateHz);
Random random_generator(42U);
std::vector<std::vector<std::vector<float>>> render(
kNumBands, std::vector<std::vector<float>>(
kNumChannels, std::vector<float>(kBlockSize)));
std::vector<std::vector<float>> capture(1, std::vector<float>(kBlockSize));
kNumRenderChannels, std::vector<float>(kBlockSize)));
std::vector<std::vector<float>> capture(kNumCaptureChannels,
std::vector<float>(kBlockSize));
ApmDataDumper data_dumper(0);
constexpr size_t kDownSamplingFactors[] = {2, 4, 8};
for (auto down_sampling_factor : kDownSamplingFactors) {
@ -76,7 +83,7 @@ TEST(EchoPathDelayEstimator, DelayEstimation) {
for (size_t delay_samples : {30, 64, 150, 200, 800, 4000}) {
SCOPED_TRACE(ProduceDebugText(delay_samples, down_sampling_factor));
std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
RenderDelayBuffer::Create(config, kSampleRateHz, kNumChannels));
RenderDelayBuffer::Create(config, kSampleRateHz, kNumRenderChannels));
DelayBuffer<float> signal_delay_buffer(delay_samples);
EchoPathDelayEstimator estimator(&data_dumper, config);
@ -117,20 +124,22 @@ TEST(EchoPathDelayEstimator, DelayEstimation) {
// Verifies that the delay estimator does not produce delay estimates for render
// signals of low level.
TEST(EchoPathDelayEstimator, NoDelayEstimatesForLowLevelRenderSignals) {
constexpr size_t kNumChannels = 1;
constexpr size_t kNumRenderChannels = 1;
constexpr size_t kNumCaptureChannels = 1;
constexpr int kSampleRateHz = 48000;
constexpr size_t kNumBands = NumBandsForRate(kSampleRateHz);
Random random_generator(42U);
EchoCanceller3Config config;
std::vector<std::vector<std::vector<float>>> render(
kNumBands, std::vector<std::vector<float>>(
kNumChannels, std::vector<float>(kBlockSize)));
std::vector<std::vector<float>> capture(1, std::vector<float>(kBlockSize));
kNumRenderChannels, std::vector<float>(kBlockSize)));
std::vector<std::vector<float>> capture(kNumCaptureChannels,
std::vector<float>(kBlockSize));
ApmDataDumper data_dumper(0);
EchoPathDelayEstimator estimator(&data_dumper, config);
std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
RenderDelayBuffer::Create(EchoCanceller3Config(), kSampleRateHz,
kNumChannels));
kNumRenderChannels));
for (size_t k = 0; k < 100; ++k) {
RandomizeSampleVector(&random_generator, render[0][0]);
for (auto& render_k : render[0][0]) {

View File

@ -316,15 +316,12 @@ void EchoRemoverImpl::ProcessCapture(
subtractor_output_heap_.data(), num_capture_channels_);
}
const std::vector<float>& x0 = x[0][0];
std::vector<float>& y0 = (*y)[0][0];
data_dumper_->DumpWav("aec3_echo_remover_capture_input", kBlockSize, &y0[0],
16000, 1);
data_dumper_->DumpWav("aec3_echo_remover_render_input", kBlockSize, &x0[0],
16000, 1);
data_dumper_->DumpRaw("aec3_echo_remover_capture_input", y0);
data_dumper_->DumpRaw("aec3_echo_remover_render_input", x0);
data_dumper_->DumpWav("aec3_echo_remover_capture_input", kBlockSize,
&(*y)[0][0][0], 16000, 1);
data_dumper_->DumpWav("aec3_echo_remover_render_input", kBlockSize,
&x[0][0][0], 16000, 1);
data_dumper_->DumpRaw("aec3_echo_remover_capture_input", (*y)[0][0]);
data_dumper_->DumpRaw("aec3_echo_remover_render_input", x[0][0]);
aec_state_.UpdateCaptureSaturation(capture_signal_saturation);
@ -374,12 +371,10 @@ void EchoRemoverImpl::ProcessCapture(
subtractor_.Process(*render_buffer, (*y)[0], render_signal_analyzer_,
aec_state_, subtractor_output);
// Compute spectra.
for (size_t ch = 0; ch < num_capture_channels_; ++ch) {
auto& y_low = (*y)[0][ch];
// Compute spectra.
FormLinearFilterOutput(subtractor_output[ch], e[ch]);
WindowedPaddedFft(fft_, y_low, y_old_[ch], &Y[ch]);
WindowedPaddedFft(fft_, (*y)[0][ch], y_old_[ch], &Y[ch]);
WindowedPaddedFft(fft_, e[ch], e_old_[ch], &E[ch]);
LinearEchoPower(E[ch], Y[ch], &S2_linear[ch]);
Y[ch].Spectrum(optimization_, Y2[ch]);
@ -387,15 +382,15 @@ void EchoRemoverImpl::ProcessCapture(
}
// Update the AEC state information.
// TODO(bugs.webrtc.org/10913): Take all subtractors into account.
aec_state_.Update(external_delay, subtractor_.FilterFrequencyResponse(),
subtractor_.FilterImpulseResponse(), *render_buffer, E2, Y2,
subtractor_output);
aec_state_.Update(external_delay, subtractor_.FilterFrequencyResponses(),
subtractor_.FilterImpulseResponses(), *render_buffer, E2,
Y2, subtractor_output);
// Choose the linear output.
const auto& Y_fft = aec_state_.UseLinearFilterOutput() ? E : Y;
data_dumper_->DumpWav("aec3_output_linear", kBlockSize, &y0[0], 16000, 1);
data_dumper_->DumpWav("aec3_output_linear", kBlockSize, &(*y)[0][0][0], 16000,
1);
data_dumper_->DumpWav("aec3_output_linear2", kBlockSize, &e[0][0], 16000, 1);
float high_bands_gain = 1.f;
@ -408,8 +403,8 @@ void EchoRemoverImpl::ProcessCapture(
for (size_t ch = 0; ch < num_capture_channels_; ++ch) {
// Estimate the comfort noise.
cngs_[ch]->Compute(aec_state_, Y2[ch], &comfort_noise[ch],
&high_band_comfort_noise[ch]);
cngs_[ch]->Compute(aec_state_.SaturatedCapture(), Y2[ch],
&comfort_noise[ch], &high_band_comfort_noise[ch]);
// Suppressor echo estimate.
const auto& echo_spectrum =
@ -448,13 +443,14 @@ 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);
data_dumper_->DumpRaw("aec3_output", y0);
data_dumper_->DumpRaw("aec3_output", (*y)[0][0]);
data_dumper_->DumpRaw("aec3_narrow_render",
render_signal_analyzer_.NarrowPeakBand() ? 1 : 0);
data_dumper_->DumpRaw("aec3_N2", cngs_[0]->NoiseSpectrum());
data_dumper_->DumpRaw("aec3_suppressor_gain", G);
data_dumper_->DumpWav(
"aec3_output", rtc::ArrayView<const float>(&y0[0], kBlockSize), 16000, 1);
data_dumper_->DumpWav("aec3_output",
rtc::ArrayView<const float>(&(*y)[0][0][0], kBlockSize),
16000, 1);
data_dumper_->DumpRaw("aec3_using_subtractor_output[0]",
aec_state_.UseLinearFilterOutput() ? 1 : 0);
data_dumper_->DumpRaw("aec3_E2", E2[0]);

View File

@ -21,11 +21,13 @@ namespace webrtc {
TEST(FilterAnalyzer, FilterResize) {
EchoCanceller3Config c;
std::vector<float> filter(65, 0.f);
FilterAnalyzer fa(c, 1);
fa.SetRegionToAnalyze(filter.size());
fa.SetRegionToAnalyze(filter.size());
filter.resize(32);
fa.SetRegionToAnalyze(filter.size());
for (size_t num_capture_channels : {1, 2, 4}) {
FilterAnalyzer fa(c, num_capture_channels);
fa.SetRegionToAnalyze(filter.size());
fa.SetRegionToAnalyze(filter.size());
filter.resize(32);
fa.SetRegionToAnalyze(filter.size());
}
}
} // namespace webrtc

View File

@ -54,12 +54,12 @@ void RunFilterUpdateTest(int num_blocks_to_process,
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, 1,
optimization, &data_dumper);
AdaptiveFirFilter shadow_filter(config.filter.shadow.length_blocks,
config.filter.shadow.length_blocks,
config.filter.config_change_duration_blocks,
1, optimization, &data_dumper);
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(),

View File

@ -46,24 +46,27 @@ constexpr size_t kDownSamplingFactors[] = {2, 4, 8};
// Verifies the output of GetDelay when there are no AnalyzeRender calls.
TEST(RenderDelayController, NoRenderSignal) {
std::vector<std::vector<float>> block(1, std::vector<float>(kBlockSize, 0.f));
EchoCanceller3Config config;
for (size_t num_matched_filters = 4; num_matched_filters == 10;
num_matched_filters++) {
for (auto down_sampling_factor : kDownSamplingFactors) {
config.delay.down_sampling_factor = down_sampling_factor;
config.delay.num_filters = num_matched_filters;
for (auto rate : {16000, 32000, 48000}) {
SCOPED_TRACE(ProduceDebugText(rate));
std::unique_ptr<RenderDelayBuffer> delay_buffer(
RenderDelayBuffer::Create(config, rate, 1));
std::unique_ptr<RenderDelayController> delay_controller(
RenderDelayController::Create(config, rate));
for (size_t k = 0; k < 100; ++k) {
auto delay = delay_controller->GetDelay(
delay_buffer->GetDownsampledRenderBuffer(), delay_buffer->Delay(),
block);
EXPECT_FALSE(delay->delay);
for (size_t num_render_channels : {1, 2, 8}) {
std::vector<std::vector<float>> block(1,
std::vector<float>(kBlockSize, 0.f));
EchoCanceller3Config config;
for (size_t num_matched_filters = 4; num_matched_filters == 10;
num_matched_filters++) {
for (auto down_sampling_factor : kDownSamplingFactors) {
config.delay.down_sampling_factor = down_sampling_factor;
config.delay.num_filters = num_matched_filters;
for (auto rate : {16000, 32000, 48000}) {
SCOPED_TRACE(ProduceDebugText(rate));
std::unique_ptr<RenderDelayBuffer> delay_buffer(
RenderDelayBuffer::Create(config, rate, num_render_channels));
std::unique_ptr<RenderDelayController> delay_controller(
RenderDelayController::Create(config, rate));
for (size_t k = 0; k < 100; ++k) {
auto delay = delay_controller->GetDelay(
delay_buffer->GetDownsampledRenderBuffer(),
delay_buffer->Delay(), block);
EXPECT_FALSE(delay->delay);
}
}
}
}
@ -72,35 +75,38 @@ TEST(RenderDelayController, NoRenderSignal) {
// Verifies the basic API call sequence.
TEST(RenderDelayController, BasicApiCalls) {
constexpr size_t kNumChannels = 1;
std::vector<std::vector<float>> capture_block(
1, std::vector<float>(kBlockSize, 0.f));
absl::optional<DelayEstimate> delay_blocks;
for (size_t num_matched_filters = 4; num_matched_filters == 10;
num_matched_filters++) {
for (auto down_sampling_factor : kDownSamplingFactors) {
EchoCanceller3Config config;
config.delay.down_sampling_factor = down_sampling_factor;
config.delay.num_filters = num_matched_filters;
for (auto rate : {16000, 32000, 48000}) {
std::vector<std::vector<std::vector<float>>> render_block(
NumBandsForRate(rate),
std::vector<std::vector<float>>(
kNumChannels, std::vector<float>(kBlockSize, 0.f)));
std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
RenderDelayBuffer::Create(config, rate, kNumChannels));
std::unique_ptr<RenderDelayController> delay_controller(
RenderDelayController::Create(EchoCanceller3Config(), rate));
for (size_t k = 0; k < 10; ++k) {
render_delay_buffer->Insert(render_block);
render_delay_buffer->PrepareCaptureProcessing();
for (size_t num_capture_channels : {1, 2, 4}) {
for (size_t num_render_channels : {1, 2, 8}) {
std::vector<std::vector<float>> capture_block(
num_capture_channels, std::vector<float>(kBlockSize, 0.f));
absl::optional<DelayEstimate> delay_blocks;
for (size_t num_matched_filters = 4; num_matched_filters == 10;
num_matched_filters++) {
for (auto down_sampling_factor : kDownSamplingFactors) {
EchoCanceller3Config config;
config.delay.down_sampling_factor = down_sampling_factor;
config.delay.num_filters = num_matched_filters;
for (auto rate : {16000, 32000, 48000}) {
std::vector<std::vector<std::vector<float>>> render_block(
NumBandsForRate(rate),
std::vector<std::vector<float>>(
num_render_channels, std::vector<float>(kBlockSize, 0.f)));
std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
RenderDelayBuffer::Create(config, rate, num_render_channels));
std::unique_ptr<RenderDelayController> delay_controller(
RenderDelayController::Create(EchoCanceller3Config(), rate));
for (size_t k = 0; k < 10; ++k) {
render_delay_buffer->Insert(render_block);
render_delay_buffer->PrepareCaptureProcessing();
delay_blocks = delay_controller->GetDelay(
render_delay_buffer->GetDownsampledRenderBuffer(),
render_delay_buffer->Delay(), capture_block);
delay_blocks = delay_controller->GetDelay(
render_delay_buffer->GetDownsampledRenderBuffer(),
render_delay_buffer->Delay(), capture_block);
}
EXPECT_TRUE(delay_blocks);
EXPECT_FALSE(delay_blocks->delay);
}
}
EXPECT_TRUE(delay_blocks);
EXPECT_FALSE(delay_blocks->delay);
}
}
}
@ -110,53 +116,55 @@ TEST(RenderDelayController, BasicApiCalls) {
// simple timeshifts between the signals.
TEST(RenderDelayController, Alignment) {
Random random_generator(42U);
std::vector<std::vector<float>> capture_block(
1, std::vector<float>(kBlockSize, 0.f));
for (size_t num_matched_filters = 4; num_matched_filters == 10;
num_matched_filters++) {
for (auto down_sampling_factor : kDownSamplingFactors) {
EchoCanceller3Config config;
config.delay.down_sampling_factor = down_sampling_factor;
config.delay.num_filters = num_matched_filters;
for (size_t num_capture_channels : {1, 2, 4}) {
std::vector<std::vector<float>> capture_block(
num_capture_channels, std::vector<float>(kBlockSize, 0.f));
for (size_t num_matched_filters = 4; num_matched_filters == 10;
num_matched_filters++) {
for (auto down_sampling_factor : kDownSamplingFactors) {
EchoCanceller3Config config;
config.delay.down_sampling_factor = down_sampling_factor;
config.delay.num_filters = num_matched_filters;
for (size_t num_render_channels : {1, 2}) {
for (auto rate : {16000, 32000, 48000}) {
std::vector<std::vector<std::vector<float>>> render_block(
NumBandsForRate(rate),
std::vector<std::vector<float>>(
num_render_channels, std::vector<float>(kBlockSize, 0.f)));
for (size_t num_render_channels : {1, 2, 8}) {
for (auto rate : {16000, 32000, 48000}) {
std::vector<std::vector<std::vector<float>>> render_block(
NumBandsForRate(rate),
std::vector<std::vector<float>>(
num_render_channels, std::vector<float>(kBlockSize, 0.f)));
for (size_t delay_samples : {15, 50, 150, 200, 800, 4000}) {
absl::optional<DelayEstimate> delay_blocks;
SCOPED_TRACE(ProduceDebugText(rate, delay_samples));
std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
RenderDelayBuffer::Create(config, rate, num_render_channels));
std::unique_ptr<RenderDelayController> delay_controller(
RenderDelayController::Create(config, rate));
DelayBuffer<float> signal_delay_buffer(delay_samples);
for (size_t k = 0; k < (400 + delay_samples / kBlockSize); ++k) {
for (size_t band = 0; band < render_block.size(); ++band) {
for (size_t channel = 0; channel < render_block[band].size();
++channel) {
RandomizeSampleVector(&random_generator,
render_block[band][channel]);
for (size_t delay_samples : {15, 50, 150, 200, 800, 4000}) {
absl::optional<DelayEstimate> delay_blocks;
SCOPED_TRACE(ProduceDebugText(rate, delay_samples));
std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
RenderDelayBuffer::Create(config, rate, num_render_channels));
std::unique_ptr<RenderDelayController> delay_controller(
RenderDelayController::Create(config, rate));
DelayBuffer<float> signal_delay_buffer(delay_samples);
for (size_t k = 0; k < (400 + delay_samples / kBlockSize); ++k) {
for (size_t band = 0; band < render_block.size(); ++band) {
for (size_t channel = 0; channel < render_block[band].size();
++channel) {
RandomizeSampleVector(&random_generator,
render_block[band][channel]);
}
}
signal_delay_buffer.Delay(render_block[0][0], capture_block[0]);
render_delay_buffer->Insert(render_block);
render_delay_buffer->PrepareCaptureProcessing();
delay_blocks = delay_controller->GetDelay(
render_delay_buffer->GetDownsampledRenderBuffer(),
render_delay_buffer->Delay(), capture_block);
}
signal_delay_buffer.Delay(render_block[0][0], capture_block[0]);
render_delay_buffer->Insert(render_block);
render_delay_buffer->PrepareCaptureProcessing();
delay_blocks = delay_controller->GetDelay(
render_delay_buffer->GetDownsampledRenderBuffer(),
render_delay_buffer->Delay(), capture_block);
ASSERT_TRUE(!!delay_blocks);
constexpr int kDelayHeadroomBlocks = 1;
size_t expected_delay_blocks =
std::max(0, static_cast<int>(delay_samples / kBlockSize) -
kDelayHeadroomBlocks);
EXPECT_EQ(expected_delay_blocks, delay_blocks->delay);
}
ASSERT_TRUE(!!delay_blocks);
constexpr int kDelayHeadroomBlocks = 1;
size_t expected_delay_blocks =
std::max(0, static_cast<int>(delay_samples / kBlockSize) -
kDelayHeadroomBlocks);
EXPECT_EQ(expected_delay_blocks, delay_blocks->delay);
}
}
}
@ -168,44 +176,48 @@ TEST(RenderDelayController, Alignment) {
// delays.
TEST(RenderDelayController, NonCausalAlignment) {
Random random_generator(42U);
constexpr size_t kNumRenderChannels = 1;
constexpr size_t kNumCaptureChannels = 1;
for (size_t num_matched_filters = 4; num_matched_filters == 10;
num_matched_filters++) {
for (auto down_sampling_factor : kDownSamplingFactors) {
EchoCanceller3Config config;
config.delay.down_sampling_factor = down_sampling_factor;
config.delay.num_filters = num_matched_filters;
for (auto rate : {16000, 32000, 48000}) {
std::vector<std::vector<std::vector<float>>> render_block(
NumBandsForRate(rate),
std::vector<std::vector<float>>(
kNumRenderChannels, std::vector<float>(kBlockSize, 0.f)));
std::vector<std::vector<std::vector<float>>> capture_block(
NumBandsForRate(rate),
std::vector<std::vector<float>>(
kNumCaptureChannels, std::vector<float>(kBlockSize, 0.f)));
for (size_t num_capture_channels : {1, 2, 4}) {
for (size_t num_render_channels : {1, 2, 8}) {
for (size_t num_matched_filters = 4; num_matched_filters == 10;
num_matched_filters++) {
for (auto down_sampling_factor : kDownSamplingFactors) {
EchoCanceller3Config config;
config.delay.down_sampling_factor = down_sampling_factor;
config.delay.num_filters = num_matched_filters;
for (auto rate : {16000, 32000, 48000}) {
std::vector<std::vector<std::vector<float>>> render_block(
NumBandsForRate(rate),
std::vector<std::vector<float>>(
num_render_channels, std::vector<float>(kBlockSize, 0.f)));
std::vector<std::vector<std::vector<float>>> capture_block(
NumBandsForRate(rate),
std::vector<std::vector<float>>(
num_capture_channels, std::vector<float>(kBlockSize, 0.f)));
for (int delay_samples : {-15, -50, -150, -200}) {
absl::optional<DelayEstimate> delay_blocks;
SCOPED_TRACE(ProduceDebugText(rate, -delay_samples));
std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
RenderDelayBuffer::Create(config, rate, kNumRenderChannels));
std::unique_ptr<RenderDelayController> delay_controller(
RenderDelayController::Create(EchoCanceller3Config(), rate));
DelayBuffer<float> signal_delay_buffer(-delay_samples);
for (int k = 0;
k < (400 - delay_samples / static_cast<int>(kBlockSize)); ++k) {
RandomizeSampleVector(&random_generator, capture_block[0][0]);
signal_delay_buffer.Delay(capture_block[0][0], render_block[0][0]);
render_delay_buffer->Insert(render_block);
render_delay_buffer->PrepareCaptureProcessing();
delay_blocks = delay_controller->GetDelay(
render_delay_buffer->GetDownsampledRenderBuffer(),
render_delay_buffer->Delay(), capture_block[0]);
for (int delay_samples : {-15, -50, -150, -200}) {
absl::optional<DelayEstimate> delay_blocks;
SCOPED_TRACE(ProduceDebugText(rate, -delay_samples));
std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
RenderDelayBuffer::Create(config, rate, num_render_channels));
std::unique_ptr<RenderDelayController> delay_controller(
RenderDelayController::Create(EchoCanceller3Config(), rate));
DelayBuffer<float> signal_delay_buffer(-delay_samples);
for (int k = 0;
k < (400 - delay_samples / static_cast<int>(kBlockSize));
++k) {
RandomizeSampleVector(&random_generator, capture_block[0][0]);
signal_delay_buffer.Delay(capture_block[0][0],
render_block[0][0]);
render_delay_buffer->Insert(render_block);
render_delay_buffer->PrepareCaptureProcessing();
delay_blocks = delay_controller->GetDelay(
render_delay_buffer->GetDownsampledRenderBuffer(),
render_delay_buffer->Delay(), capture_block[0]);
}
ASSERT_FALSE(delay_blocks);
}
}
ASSERT_FALSE(delay_blocks);
}
}
}
@ -216,86 +228,69 @@ TEST(RenderDelayController, NonCausalAlignment) {
// simple timeshifts between the signals when there is jitter in the API calls.
TEST(RenderDelayController, AlignmentWithJitter) {
Random random_generator(42U);
constexpr size_t kNumRenderChannels = 1;
std::vector<std::vector<float>> capture_block(
1, std::vector<float>(kBlockSize, 0.f));
for (size_t num_matched_filters = 4; num_matched_filters == 10;
num_matched_filters++) {
for (auto down_sampling_factor : kDownSamplingFactors) {
EchoCanceller3Config config;
config.delay.down_sampling_factor = down_sampling_factor;
config.delay.num_filters = num_matched_filters;
for (auto rate : {16000, 32000, 48000}) {
std::vector<std::vector<std::vector<float>>> render_block(
NumBandsForRate(rate),
std::vector<std::vector<float>>(
kNumRenderChannels, std::vector<float>(kBlockSize, 0.f)));
for (size_t delay_samples : {15, 50, 300, 800}) {
absl::optional<DelayEstimate> delay_blocks;
SCOPED_TRACE(ProduceDebugText(rate, delay_samples));
std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
RenderDelayBuffer::Create(config, rate, kNumRenderChannels));
std::unique_ptr<RenderDelayController> delay_controller(
RenderDelayController::Create(config, rate));
DelayBuffer<float> signal_delay_buffer(delay_samples);
constexpr size_t kMaxTestJitterBlocks = 26;
for (size_t j = 0;
j <
(1000 + delay_samples / kBlockSize) / kMaxTestJitterBlocks + 1;
++j) {
std::vector<std::vector<std::vector<float>>> capture_block_buffer;
for (size_t k = 0; k < (kMaxTestJitterBlocks - 1); ++k) {
RandomizeSampleVector(&random_generator, render_block[0][0]);
signal_delay_buffer.Delay(render_block[0][0], capture_block[0]);
capture_block_buffer.push_back(capture_block);
render_delay_buffer->Insert(render_block);
}
for (size_t k = 0; k < (kMaxTestJitterBlocks - 1); ++k) {
render_delay_buffer->PrepareCaptureProcessing();
delay_blocks = delay_controller->GetDelay(
render_delay_buffer->GetDownsampledRenderBuffer(),
render_delay_buffer->Delay(), capture_block_buffer[k]);
for (size_t num_capture_channels : {1, 2, 4}) {
for (size_t num_render_channels : {1, 2, 8}) {
std::vector<std::vector<float>> capture_block(
num_capture_channels, std::vector<float>(kBlockSize, 0.f));
for (size_t num_matched_filters = 4; num_matched_filters == 10;
num_matched_filters++) {
for (auto down_sampling_factor : kDownSamplingFactors) {
EchoCanceller3Config config;
config.delay.down_sampling_factor = down_sampling_factor;
config.delay.num_filters = num_matched_filters;
for (auto rate : {16000, 32000, 48000}) {
std::vector<std::vector<std::vector<float>>> render_block(
NumBandsForRate(rate),
std::vector<std::vector<float>>(
num_render_channels, std::vector<float>(kBlockSize, 0.f)));
for (size_t delay_samples : {15, 50, 300, 800}) {
absl::optional<DelayEstimate> delay_blocks;
SCOPED_TRACE(ProduceDebugText(rate, delay_samples));
std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
RenderDelayBuffer::Create(config, rate, num_render_channels));
std::unique_ptr<RenderDelayController> delay_controller(
RenderDelayController::Create(config, rate));
DelayBuffer<float> signal_delay_buffer(delay_samples);
constexpr size_t kMaxTestJitterBlocks = 26;
for (size_t j = 0; j < (1000 + delay_samples / kBlockSize) /
kMaxTestJitterBlocks +
1;
++j) {
std::vector<std::vector<std::vector<float>>>
capture_block_buffer;
for (size_t k = 0; k < (kMaxTestJitterBlocks - 1); ++k) {
RandomizeSampleVector(&random_generator, render_block[0][0]);
signal_delay_buffer.Delay(render_block[0][0],
capture_block[0]);
capture_block_buffer.push_back(capture_block);
render_delay_buffer->Insert(render_block);
}
for (size_t k = 0; k < (kMaxTestJitterBlocks - 1); ++k) {
render_delay_buffer->PrepareCaptureProcessing();
delay_blocks = delay_controller->GetDelay(
render_delay_buffer->GetDownsampledRenderBuffer(),
render_delay_buffer->Delay(), capture_block_buffer[k]);
}
}
constexpr int kDelayHeadroomBlocks = 1;
size_t expected_delay_blocks =
std::max(0, static_cast<int>(delay_samples / kBlockSize) -
kDelayHeadroomBlocks);
if (expected_delay_blocks < 2) {
expected_delay_blocks = 0;
}
ASSERT_TRUE(delay_blocks);
EXPECT_EQ(expected_delay_blocks, delay_blocks->delay);
}
}
constexpr int kDelayHeadroomBlocks = 1;
size_t expected_delay_blocks =
std::max(0, static_cast<int>(delay_samples / kBlockSize) -
kDelayHeadroomBlocks);
if (expected_delay_blocks < 2) {
expected_delay_blocks = 0;
}
ASSERT_TRUE(delay_blocks);
EXPECT_EQ(expected_delay_blocks, delay_blocks->delay);
}
}
}
}
}
// Verifies the initial value for the AlignmentHeadroomSamples.
TEST(RenderDelayController, InitialHeadroom) {
std::vector<float> render_block(kBlockSize, 0.f);
std::vector<float> capture_block(kBlockSize, 0.f);
for (size_t num_matched_filters = 4; num_matched_filters == 10;
num_matched_filters++) {
for (auto down_sampling_factor : kDownSamplingFactors) {
EchoCanceller3Config config;
config.delay.down_sampling_factor = down_sampling_factor;
config.delay.num_filters = num_matched_filters;
for (auto rate : {16000, 32000, 48000}) {
SCOPED_TRACE(ProduceDebugText(rate));
std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
RenderDelayBuffer::Create(config, rate, 1));
std::unique_ptr<RenderDelayController> delay_controller(
RenderDelayController::Create(config, rate));
}
}
}
}
#if RTC_DCHECK_IS_ON && GTEST_HAS_DEATH_TEST && !defined(WEBRTC_ANDROID)
// Verifies the check for the capture signal block size.

View File

@ -41,14 +41,14 @@ void RunFilterUpdateTest(int num_blocks_to_process,
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.config_change_duration_blocks, 1,
DetectOptimization(), &data_dumper);
AdaptiveFirFilter shadow_filter(config.filter.shadow.length_blocks,
config.filter.shadow.length_blocks,
config.filter.config_change_duration_blocks,
1, DetectOptimization(), &data_dumper);
AdaptiveFirFilter main_filter(
config.filter.main.length_blocks, config.filter.main.length_blocks,
config.filter.config_change_duration_blocks, num_render_channels,
DetectOptimization(), &data_dumper);
AdaptiveFirFilter shadow_filter(
config.filter.shadow.length_blocks, config.filter.shadow.length_blocks,
config.filter.config_change_duration_blocks, num_render_channels,
DetectOptimization(), &data_dumper);
Aec3Fft fft;
constexpr int kSampleRateHz = 48000;
@ -158,8 +158,7 @@ TEST(ShadowFilterUpdateGain, GainCausesFilterToConverge) {
std::vector<int> blocks_with_echo_path_changes;
std::vector<int> blocks_with_saturation;
// TODO(http://bugs.webrtc.org/10913): Test multiple render channel counts.
for (size_t num_render_channels : {1}) {
for (size_t num_render_channels : {1, 2, 8}) {
for (size_t filter_length_blocks : {12, 20, 30}) {
for (size_t delay_samples : {0, 64, 150, 200, 301}) {
SCOPED_TRACE(ProduceDebugText(delay_samples, filter_length_blocks));
@ -168,7 +167,7 @@ TEST(ShadowFilterUpdateGain, GainCausesFilterToConverge) {
std::array<float, kBlockSize> y;
FftData G;
RunFilterUpdateTest(1000, delay_samples, num_render_channels,
RunFilterUpdateTest(5000, delay_samples, num_render_channels,
filter_length_blocks, blocks_with_saturation, &e,
&y, &G);
@ -190,8 +189,7 @@ TEST(ShadowFilterUpdateGain, GainCausesFilterToConverge) {
// Verifies that the magnitude of the gain on average decreases for a
// persistently exciting signal.
TEST(ShadowFilterUpdateGain, DecreasingGain) {
// TODO(http://bugs.webrtc.org/10913): Test multiple render channel counts.
for (size_t num_render_channels : {1}) {
for (size_t num_render_channels : {1, 2, 4}) {
for (size_t filter_length_blocks : {12, 20, 30}) {
SCOPED_TRACE(ProduceDebugText(filter_length_blocks));
std::vector<int> blocks_with_echo_path_changes;
@ -233,8 +231,7 @@ TEST(ShadowFilterUpdateGain, SaturationBehavior) {
for (int k = 99; k < 200; ++k) {
blocks_with_saturation.push_back(k);
}
// TODO(http://bugs.webrtc.org/10913): Test multiple render channel counts.
for (size_t num_render_channels : {1}) {
for (size_t num_render_channels : {1, 2, 8}) {
for (size_t filter_length_blocks : {12, 20, 30}) {
SCOPED_TRACE(ProduceDebugText(filter_length_blocks));

View File

@ -66,26 +66,26 @@ Subtractor::Subtractor(const EchoCanceller3Config& config,
optimization_(optimization),
config_(config),
num_capture_channels_(num_capture_channels),
main_filter_(num_capture_channels_),
main_filters_(num_capture_channels_),
shadow_filter_(num_capture_channels_),
G_main_(num_capture_channels_),
G_shadow_(num_capture_channels_),
filter_misadjustment_estimator_(num_capture_channels_),
poor_shadow_filter_counter_(num_capture_channels_, 0),
main_frequency_response_(
main_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_(
num_capture_channels_,
std::vector<std::array<float, kFftLengthBy2Plus1>>(
std::max(config_.filter.main_initial.length_blocks,
config_.filter.main.length_blocks),
std::array<float, kFftLengthBy2Plus1>())),
main_impulse_response_(
main_impulse_responses_(
num_capture_channels_,
std::vector<float>(GetTimeDomainLength(std::max(
config_.filter.main_initial.length_blocks,
config_.filter.main.length_blocks)),
0.f)) {
for (size_t ch = 0; ch < num_capture_channels_; ++ch) {
main_filter_[ch] = std::make_unique<AdaptiveFirFilter>(
main_filters_[ch] = std::make_unique<AdaptiveFirFilter>(
config_.filter.main.length_blocks,
config_.filter.main_initial.length_blocks,
config.filter.config_change_duration_blocks, num_render_channels,
@ -96,17 +96,17 @@ Subtractor::Subtractor(const EchoCanceller3Config& config,
config_.filter.shadow_initial.length_blocks,
config.filter.config_change_duration_blocks, num_render_channels,
optimization, data_dumper_);
G_main_[ch] = std::make_unique<MainFilterUpdateGain>(
main_gains_[ch] = std::make_unique<MainFilterUpdateGain>(
config_.filter.main_initial,
config_.filter.config_change_duration_blocks);
G_shadow_[ch] = std::make_unique<ShadowFilterUpdateGain>(
shadow_gains_[ch] = std::make_unique<ShadowFilterUpdateGain>(
config_.filter.shadow_initial,
config.filter.config_change_duration_blocks);
}
RTC_DCHECK(data_dumper_);
for (size_t ch = 0; ch < num_capture_channels_; ++ch) {
for (auto& H2_k : main_frequency_response_[ch]) {
for (auto& H2_k : main_frequency_responses_[ch]) {
H2_k.fill(0.f);
}
}
@ -118,13 +118,13 @@ void Subtractor::HandleEchoPathChange(
const EchoPathVariability& echo_path_variability) {
const auto full_reset = [&]() {
for (size_t ch = 0; ch < num_capture_channels_; ++ch) {
main_filter_[ch]->HandleEchoPathChange();
main_filters_[ch]->HandleEchoPathChange();
shadow_filter_[ch]->HandleEchoPathChange();
G_main_[ch]->HandleEchoPathChange(echo_path_variability);
G_shadow_[ch]->HandleEchoPathChange();
G_main_[ch]->SetConfig(config_.filter.main_initial, true);
G_shadow_[ch]->SetConfig(config_.filter.shadow_initial, true);
main_filter_[ch]->SetSizePartitions(
main_gains_[ch]->HandleEchoPathChange(echo_path_variability);
shadow_gains_[ch]->HandleEchoPathChange();
main_gains_[ch]->SetConfig(config_.filter.main_initial, true);
shadow_gains_[ch]->SetConfig(config_.filter.shadow_initial, true);
main_filters_[ch]->SetSizePartitions(
config_.filter.main_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) {
G_main_[ch]->HandleEchoPathChange(echo_path_variability);
main_gains_[ch]->HandleEchoPathChange(echo_path_variability);
}
}
}
void Subtractor::ExitInitialState() {
for (size_t ch = 0; ch < num_capture_channels_; ++ch) {
G_main_[ch]->SetConfig(config_.filter.main, false);
G_shadow_[ch]->SetConfig(config_.filter.shadow, false);
main_filter_[ch]->SetSizePartitions(config_.filter.main.length_blocks,
false);
main_gains_[ch]->SetConfig(config_.filter.main, false);
shadow_gains_[ch]->SetConfig(config_.filter.shadow, false);
main_filters_[ch]->SetSizePartitions(config_.filter.main.length_blocks,
false);
shadow_filter_[ch]->SetSizePartitions(config_.filter.shadow.length_blocks,
false);
}
@ -163,19 +163,19 @@ void Subtractor::Process(const RenderBuffer& render_buffer,
// Compute the render powers.
const bool same_filter_sizes =
main_filter_[0]->SizePartitions() == shadow_filter_[0]->SizePartitions();
main_filters_[0]->SizePartitions() == shadow_filter_[0]->SizePartitions();
std::array<float, kFftLengthBy2Plus1> X2_main;
std::array<float, kFftLengthBy2Plus1> X2_shadow_data;
auto& X2_shadow = same_filter_sizes ? X2_main : X2_shadow_data;
if (same_filter_sizes) {
render_buffer.SpectralSum(main_filter_[0]->SizePartitions(), &X2_main);
} else if (main_filter_[0]->SizePartitions() >
render_buffer.SpectralSum(main_filters_[0]->SizePartitions(), &X2_main);
} else if (main_filters_[0]->SizePartitions() >
shadow_filter_[0]->SizePartitions()) {
render_buffer.SpectralSums(shadow_filter_[0]->SizePartitions(),
main_filter_[0]->SizePartitions(), &X2_shadow,
main_filters_[0]->SizePartitions(), &X2_shadow,
&X2_main);
} else {
render_buffer.SpectralSums(main_filter_[0]->SizePartitions(),
render_buffer.SpectralSums(main_filters_[0]->SizePartitions(),
shadow_filter_[0]->SizePartitions(), &X2_main,
&X2_shadow);
}
@ -194,7 +194,7 @@ void Subtractor::Process(const RenderBuffer& render_buffer,
FftData& G = S;
// Form the outputs of the main and shadow filters.
main_filter_[ch]->Filter(render_buffer, &S);
main_filters_[ch]->Filter(render_buffer, &S);
PredictionError(fft_, S, y, &e_main, &output.s_main);
shadow_filter_[ch]->Filter(render_buffer, &S);
@ -204,17 +204,17 @@ void Subtractor::Process(const RenderBuffer& render_buffer,
output.ComputeMetrics(y);
// Adjust the filter if needed.
bool main_filter_adjusted = false;
filter_misadjustment_estimator_[ch].Update(output);
if (filter_misadjustment_estimator_[ch].IsAdjustmentNeeded()) {
float scale = filter_misadjustment_estimator_[ch].GetMisadjustment();
main_filter_[ch]->ScaleFilter(scale);
for (auto& h_k : main_impulse_response_[ch]) {
bool main_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]) {
h_k *= scale;
}
ScaleFilterOutput(y, scale, e_main, output.s_main);
filter_misadjustment_estimator_[ch].Reset();
main_filter_adjusted = true;
filter_misadjustment_estimators_[ch].Reset();
main_filters_adjusted = true;
}
// Compute the FFts of the main and shadow filter outputs.
@ -226,18 +226,18 @@ void Subtractor::Process(const RenderBuffer& render_buffer,
E_main.Spectrum(optimization_, output.E2_main);
// Update the main filter.
if (!main_filter_adjusted) {
if (!main_filters_adjusted) {
std::array<float, kFftLengthBy2Plus1> erl;
ComputeErl(optimization_, main_frequency_response_[ch], erl);
G_main_[ch]->Compute(X2_main, render_signal_analyzer, output, erl,
main_filter_[ch]->SizePartitions(),
aec_state.SaturatedCapture(), &G);
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);
} else {
G.re.fill(0.f);
G.im.fill(0.f);
}
main_filter_[ch]->Adapt(render_buffer, G, &main_impulse_response_[ch]);
main_filter_[ch]->ComputeFrequencyResponse(&main_frequency_response_[ch]);
main_filters_[ch]->Adapt(render_buffer, G, &main_impulse_responses_[ch]);
main_filters_[ch]->ComputeFrequencyResponse(&main_frequency_responses_[ch]);
if (ch == 0) {
data_dumper_->DumpRaw("aec3_subtractor_G_main", G.re);
@ -245,27 +245,27 @@ void Subtractor::Process(const RenderBuffer& render_buffer,
}
// Update the shadow filter.
poor_shadow_filter_counter_[ch] = output.e2_main < output.e2_shadow
? poor_shadow_filter_counter_[ch] + 1
poor_shadow_filter_counters_[ch] =
output.e2_main < output.e2_shadow ? poor_shadow_filter_counters_[ch] + 1
: 0;
if (poor_shadow_filter_counter_[ch] < 5) {
G_shadow_[ch]->Compute(X2_shadow, render_signal_analyzer, E_shadow,
shadow_filter_[ch]->SizePartitions(),
aec_state.SaturatedCapture(), &G);
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_counter_[ch] = 0;
shadow_filter_[ch]->SetFilter(main_filter_[ch]->SizePartitions(),
main_filter_[ch]->GetFilter());
G_shadow_[ch]->Compute(X2_shadow, render_signal_analyzer, E_main,
shadow_filter_[ch]->SizePartitions(),
aec_state.SaturatedCapture(), &G);
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]->SizePartitions(),
aec_state.SaturatedCapture(), &G);
}
shadow_filter_[ch]->Adapt(render_buffer, G);
if (ch == 0) {
data_dumper_->DumpRaw("aec3_subtractor_G_shadow", G.re);
data_dumper_->DumpRaw("aec3_subtractor_G_shadow", G.im);
filter_misadjustment_estimator_[ch].Dump(data_dumper_);
filter_misadjustment_estimators_[ch].Dump(data_dumper_);
DumpFilters();
}
@ -273,7 +273,7 @@ void Subtractor::Process(const RenderBuffer& render_buffer,
[](float& a) { a = rtc::SafeClamp(a, -32768.f, 32767.f); });
if (ch == 0) {
data_dumper_->DumpWav("aec3_main_filter_output", kBlockSize, &e_main[0],
data_dumper_->DumpWav("aec3_main_filters_output", kBlockSize, &e_main[0],
16000, 1);
data_dumper_->DumpWav("aec3_shadow_filter_output", kBlockSize,
&e_shadow[0], 16000, 1);

View File

@ -60,25 +60,25 @@ class Subtractor {
// Returns the block-wise frequency responses for the main adaptive filters.
const std::vector<std::vector<std::array<float, kFftLengthBy2Plus1>>>&
FilterFrequencyResponse() const {
return main_frequency_response_;
FilterFrequencyResponses() const {
return main_frequency_responses_;
}
// Returns the estimates of the impulse responses for the main adaptive
// filters.
const std::vector<std::vector<float>>& FilterImpulseResponse() const {
return main_impulse_response_;
const std::vector<std::vector<float>>& FilterImpulseResponses() const {
return main_impulse_responses_;
}
void DumpFilters() {
data_dumper_->DumpRaw(
"aec3_subtractor_h_main",
rtc::ArrayView<const float>(
main_impulse_response_[0].data(),
main_impulse_responses_[0].data(),
GetTimeDomainLength(
main_filter_[0]->max_filter_size_partitions())));
main_filters_[0]->max_filter_size_partitions())));
main_filter_[0]->DumpFilter("aec3_subtractor_H_main");
main_filters_[0]->DumpFilter("aec3_subtractor_H_main");
shadow_filter_[0]->DumpFilter("aec3_subtractor_H_shadow");
}
@ -120,15 +120,15 @@ class Subtractor {
const EchoCanceller3Config config_;
const size_t num_capture_channels_;
std::vector<std::unique_ptr<AdaptiveFirFilter>> main_filter_;
std::vector<std::unique_ptr<AdaptiveFirFilter>> main_filters_;
std::vector<std::unique_ptr<AdaptiveFirFilter>> shadow_filter_;
std::vector<std::unique_ptr<MainFilterUpdateGain>> G_main_;
std::vector<std::unique_ptr<ShadowFilterUpdateGain>> G_shadow_;
std::vector<FilterMisadjustmentEstimator> filter_misadjustment_estimator_;
std::vector<size_t> poor_shadow_filter_counter_;
std::vector<std::unique_ptr<MainFilterUpdateGain>> main_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_response_;
std::vector<std::vector<float>> main_impulse_response_;
main_frequency_responses_;
std::vector<std::vector<float>> main_impulse_responses_;
};
} // namespace webrtc

View File

@ -150,8 +150,8 @@ std::vector<float> RunSubtractorTest(
aec_state.HandleEchoPathChange(EchoPathVariability(
false, EchoPathVariability::DelayAdjustment::kNone, false));
aec_state.Update(delay_estimate, subtractor.FilterFrequencyResponse(),
subtractor.FilterImpulseResponse(),
aec_state.Update(delay_estimate, subtractor.FilterFrequencyResponses(),
subtractor.FilterImpulseResponses(),
*render_delay_buffer->GetRenderBuffer(), E2_main, Y2,
output);
}

View File

@ -114,6 +114,7 @@ float SuppressionGain::UpperBandsGain(
if (render.size() == 1) {
return 1.f;
}
const size_t num_render_channels = render[0].size();
if (narrow_peak_band &&
(*narrow_peak_band > static_cast<int>(kFftLengthBy2Plus1 - 10))) {
@ -131,13 +132,19 @@ float SuppressionGain::UpperBandsGain(
// Compute the upper and lower band energies.
const auto sum_of_squares = [](float a, float b) { return a + b * b; };
const float low_band_energy = std::accumulate(
render[0][0].begin(), render[0][0].end(), 0.f, sum_of_squares);
float low_band_energy = 0.f;
for (size_t ch = 0; ch < num_render_channels; ++ch) {
const float channel_energy = std::accumulate(
render[0][0].begin(), render[0][0].end(), 0.f, sum_of_squares);
low_band_energy = std::max(low_band_energy, channel_energy);
}
float high_band_energy = 0.f;
for (size_t k = 1; k < render.size(); ++k) {
const float energy = std::accumulate(
render[k][0].begin(), render[k][0].end(), 0.f, sum_of_squares);
high_band_energy = std::max(high_band_energy, energy);
for (size_t ch = 0; ch < num_render_channels; ++ch) {
const float energy = std::accumulate(
render[k][ch].begin(), render[k][ch].end(), 0.f, sum_of_squares);
high_band_energy = std::max(high_band_energy, energy);
}
}
// If there is more power in the lower frequencies than the upper frequencies,
@ -369,11 +376,16 @@ bool SuppressionGain::LowNoiseRenderDetector::Detect(
const std::vector<std::vector<std::vector<float>>>& render) {
float x2_sum = 0.f;
float x2_max = 0.f;
for (auto x_k : render[0][0]) {
const float x2 = x_k * x_k;
x2_sum += x2;
x2_max = std::max(x2_max, x2);
for (auto x_ch : render[0]) {
for (auto x_k : x_ch) {
const float x2 = x_k * x_k;
x2_sum += x2;
x2_max = std::max(x2_max, x2);
}
}
const size_t num_render_channels = render[0].size();
x2_sum = x2_sum / num_render_channels;
;
constexpr float kThreshold = 50.f * 50.f * 64.f;
const bool low_noise_render =

View File

@ -102,14 +102,14 @@ TEST(SuppressionGain, BasicGainComputation) {
// Ensure that the gain is no longer forced to zero.
for (int k = 0; k <= kNumBlocksPerSecond / 5 + 1; ++k) {
aec_state.Update(delay_estimate, subtractor.FilterFrequencyResponse(),
subtractor.FilterImpulseResponse(),
aec_state.Update(delay_estimate, subtractor.FilterFrequencyResponses(),
subtractor.FilterImpulseResponses(),
*render_delay_buffer->GetRenderBuffer(), E2, Y2, output);
}
for (int k = 0; k < 100; ++k) {
aec_state.Update(delay_estimate, subtractor.FilterFrequencyResponse(),
subtractor.FilterImpulseResponse(),
aec_state.Update(delay_estimate, subtractor.FilterFrequencyResponses(),
subtractor.FilterImpulseResponses(),
*render_delay_buffer->GetRenderBuffer(), E2, Y2, output);
suppression_gain.GetGain(E2[0], S2, R2, N2, analyzer, aec_state, x,
&high_bands_gain, &g);
@ -129,8 +129,8 @@ TEST(SuppressionGain, BasicGainComputation) {
N2.fill(0.f);
for (int k = 0; k < 100; ++k) {
aec_state.Update(delay_estimate, subtractor.FilterFrequencyResponse(),
subtractor.FilterImpulseResponse(),
aec_state.Update(delay_estimate, subtractor.FilterFrequencyResponses(),
subtractor.FilterImpulseResponses(),
*render_delay_buffer->GetRenderBuffer(), E2, Y2, output);
suppression_gain.GetGain(E2[0], S2, R2, N2, analyzer, aec_state, x,
&high_bands_gain, &g);