AEC3 delay estimator refactoring and introducing ability to customize

This CL refactors the delay estimator in AEC3.
Furthermore, it adds:
1. Allow for a customized delay estimator behavior to simplify
development.
2. Exposes that behavior to clear configuration settings.
3. Adds logging of the delay range supported by the delay
estimator.

Bug: webrtc:8519
Change-Id: I1764a090519a78b021b2e7de565c52a6c02c848e
Reviewed-on: https://webrtc-review.googlesource.com/21166
Commit-Queue: Per Åhgren <peah@webrtc.org>
Reviewed-by: Gustaf Ullberg <gustaf@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#20733}
This commit is contained in:
Per Åhgren 2017-11-17 14:54:28 +01:00 committed by Commit Bot
parent 76f2a85027
commit 38e2d95bda
27 changed files with 910 additions and 514 deletions

View File

@ -44,8 +44,8 @@ rtc_static_library("audio_processing") {
"aec3/cascaded_biquad_filter.h",
"aec3/comfort_noise_generator.cc",
"aec3/comfort_noise_generator.h",
"aec3/decimator_by_4.cc",
"aec3/decimator_by_4.h",
"aec3/decimator.cc",
"aec3/decimator.h",
"aec3/downsampled_render_buffer.cc",
"aec3/downsampled_render_buffer.h",
"aec3/echo_canceller3.cc",
@ -610,7 +610,7 @@ if (rtc_include_tests) {
"aec3/block_processor_unittest.cc",
"aec3/cascaded_biquad_filter_unittest.cc",
"aec3/comfort_noise_generator_unittest.cc",
"aec3/decimator_by_4_unittest.cc",
"aec3/decimator_unittest.cc",
"aec3/echo_canceller3_unittest.cc",
"aec3/echo_path_delay_estimator_unittest.cc",
"aec3/echo_path_variability_unittest.cc",

View File

@ -48,20 +48,9 @@ constexpr size_t kSubFrameLength = 80;
constexpr size_t kBlockSize = kFftLengthBy2;
constexpr size_t kExtendedBlockSize = 2 * kFftLengthBy2;
constexpr size_t kSubBlockSize = 16;
constexpr size_t kNumMatchedFilters = 4;
constexpr size_t kMatchedFilterWindowSizeSubBlocks = 32;
constexpr size_t kMatchedFilterAlignmentShiftSizeSubBlocks =
kMatchedFilterWindowSizeSubBlocks * 3 / 4;
constexpr size_t kDownsampledRenderBufferSize =
kSubBlockSize *
(kMatchedFilterAlignmentShiftSizeSubBlocks * kNumMatchedFilters +
kMatchedFilterWindowSizeSubBlocks +
1);
constexpr size_t kRenderDelayBufferSize =
(3 * kDownsampledRenderBufferSize) / (4 * kSubBlockSize);
constexpr size_t kMinEchoPathDelayBlocks = 5;
constexpr size_t kMaxApiCallsJitterBlocks = 26;
@ -85,6 +74,20 @@ constexpr bool ValidFullBandRate(int sample_rate_hz) {
sample_rate_hz == 32000 || sample_rate_hz == 48000;
}
constexpr size_t GetDownSampledBufferSize(size_t down_sampling_factor,
size_t num_matched_filters) {
return kBlockSize / down_sampling_factor *
(kMatchedFilterAlignmentShiftSizeSubBlocks * num_matched_filters +
kMatchedFilterWindowSizeSubBlocks + 1);
}
constexpr size_t GetRenderDelayBufferSize(size_t down_sampling_factor,
size_t num_matched_filters) {
return (3 *
GetDownSampledBufferSize(down_sampling_factor, num_matched_filters)) /
(4 * kBlockSize / down_sampling_factor);
}
// Detects what kind of optimizations to use for the code.
Aec3Optimization DetectOptimization();

View File

@ -183,8 +183,12 @@ void BlockProcessorImpl::UpdateEchoLeakageStatus(bool leakage_detected) {
BlockProcessor* BlockProcessor::Create(const EchoCanceller3Config& config,
int sample_rate_hz) {
std::unique_ptr<RenderDelayBuffer> render_buffer(
RenderDelayBuffer::Create(NumBandsForRate(sample_rate_hz)));
std::unique_ptr<RenderDelayBuffer> render_buffer(RenderDelayBuffer::Create(
NumBandsForRate(sample_rate_hz), config.delay.down_sampling_factor,
GetDownSampledBufferSize(config.delay.down_sampling_factor,
config.delay.num_filters),
GetRenderDelayBufferSize(config.delay.down_sampling_factor,
config.delay.num_filters)));
std::unique_ptr<RenderDelayController> delay_controller(
RenderDelayController::Create(config, sample_rate_hz));
std::unique_ptr<EchoRemover> echo_remover(

View File

@ -0,0 +1,70 @@
/*
* Copyright (c) 2017 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/decimator.h"
#include "rtc_base/checks.h"
namespace webrtc {
namespace {
// b, a = signal.butter(2, 3400/8000.0, 'lowpass', analog=False) which are the
// same as b, a = signal.butter(2, 1700/4000.0, 'lowpass', analog=False).
const CascadedBiQuadFilter::BiQuadCoefficients kLowPassFilterCoefficients2 = {
{0.22711796f, 0.45423593f, 0.22711796f},
{-0.27666461f, 0.18513647f}};
constexpr int kNumFilters2 = 3;
// b, a = signal.butter(2, 1500/8000.0, 'lowpass', analog=False) which are the
// same as b, a = signal.butter(2, 75/4000.0, 'lowpass', analog=False).
const CascadedBiQuadFilter::BiQuadCoefficients kLowPassFilterCoefficients4 = {
{0.0179f, 0.0357f, 0.0179f},
{-1.5879f, 0.6594f}};
constexpr int kNumFilters4 = 3;
// b, a = signal.butter(2, 800/8000.0, 'lowpass', analog=False) which are the
// same as b, a = signal.butter(2, 400/4000.0, 'lowpass', analog=False).
const CascadedBiQuadFilter::BiQuadCoefficients kLowPassFilterCoefficients8 = {
{0.02008337f, 0.04016673f, 0.02008337f},
{-1.56101808f, 0.64135154f}};
constexpr int kNumFilters8 = 4;
} // namespace
Decimator::Decimator(size_t down_sampling_factor)
: down_sampling_factor_(down_sampling_factor),
low_pass_filter_(
down_sampling_factor_ == 4
? kLowPassFilterCoefficients4
: (down_sampling_factor_ == 8 ? kLowPassFilterCoefficients8
: kLowPassFilterCoefficients2),
down_sampling_factor_ == 4
? kNumFilters4
: (down_sampling_factor_ == 8 ? kNumFilters8 : kNumFilters2)) {
RTC_DCHECK(down_sampling_factor_ == 2 || down_sampling_factor_ == 4 ||
down_sampling_factor_ == 8);
}
void Decimator::Decimate(rtc::ArrayView<const float> in,
rtc::ArrayView<float> out) {
RTC_DCHECK_EQ(kBlockSize, in.size());
RTC_DCHECK_EQ(kBlockSize / down_sampling_factor_, out.size());
std::array<float, kBlockSize> x;
// Limit the frequency content of the signal to avoid aliasing.
low_pass_filter_.Process(in, x);
// Downsample the signal.
for (size_t j = 0, k = 0; j < out.size(); ++j, k += down_sampling_factor_) {
RTC_DCHECK_GT(kBlockSize, k);
out[j] = x[k];
}
}
} // namespace webrtc

View File

@ -8,8 +8,8 @@
* be found in the AUTHORS file in the root of the source tree.
*/
#ifndef MODULES_AUDIO_PROCESSING_AEC3_DECIMATOR_BY_4_H_
#define MODULES_AUDIO_PROCESSING_AEC3_DECIMATOR_BY_4_H_
#ifndef MODULES_AUDIO_PROCESSING_AEC3_DECIMATOR_H_
#define MODULES_AUDIO_PROCESSING_AEC3_DECIMATOR_H_
#include <array>
@ -20,19 +20,20 @@
namespace webrtc {
// Provides functionality for decimating a signal by 4.
class DecimatorBy4 {
// Provides functionality for decimating a signal.
class Decimator {
public:
DecimatorBy4();
explicit Decimator(size_t down_sampling_factor);
// Downsamples the signal.
void Decimate(rtc::ArrayView<const float> in, rtc::ArrayView<float> out);
private:
const size_t down_sampling_factor_;
CascadedBiQuadFilter low_pass_filter_;
RTC_DISALLOW_COPY_AND_ASSIGN(DecimatorBy4);
RTC_DISALLOW_COPY_AND_ASSIGN(Decimator);
};
} // namespace webrtc
#endif // MODULES_AUDIO_PROCESSING_AEC3_DECIMATOR_BY_4_H_
#endif // MODULES_AUDIO_PROCESSING_AEC3_DECIMATOR_H_

View File

@ -1,44 +0,0 @@
/*
* Copyright (c) 2017 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/decimator_by_4.h"
#include "rtc_base/checks.h"
namespace webrtc {
namespace {
// [B,A] = butter(2,1500/16000) which are the same as [B,A] =
// butter(2,750/8000).
const CascadedBiQuadFilter::BiQuadCoefficients kLowPassFilterCoefficients = {
{0.0179f, 0.0357f, 0.0179f},
{-1.5879f, 0.6594f}};
} // namespace
DecimatorBy4::DecimatorBy4()
: low_pass_filter_(kLowPassFilterCoefficients, 3) {}
void DecimatorBy4::Decimate(rtc::ArrayView<const float> in,
rtc::ArrayView<float> out) {
RTC_DCHECK_EQ(kBlockSize, in.size());
RTC_DCHECK_EQ(kSubBlockSize, out.size());
std::array<float, kBlockSize> x;
// Limit the frequency content of the signal to avoid aliasing.
low_pass_filter_.Process(in, x);
// Downsample the signal.
for (size_t j = 0, k = 0; j < out.size(); ++j, k += 4) {
RTC_DCHECK_GT(kBlockSize, k);
out[j] = x[k];
}
}
} // namespace webrtc

View File

@ -8,7 +8,7 @@
* be found in the AUTHORS file in the root of the source tree.
*/
#include "modules/audio_processing/aec3/decimator_by_4.h"
#include "modules/audio_processing/aec3/decimator.h"
#include <math.h>
#include <algorithm>
@ -31,15 +31,18 @@ std::string ProduceDebugText(int sample_rate_hz) {
return ss.str();
}
constexpr size_t kDownSamplingFactors[] = {2, 4, 8};
constexpr float kPi = 3.141592f;
constexpr size_t kNumStartupBlocks = 50;
constexpr size_t kNumBlocks = 1000;
void ProduceDecimatedSinusoidalOutputPower(int sample_rate_hz,
size_t down_sampling_factor,
float sinusoidal_frequency_hz,
float* input_power,
float* output_power) {
float input[kBlockSize * kNumBlocks];
const size_t sub_block_size = kBlockSize / down_sampling_factor;
// Produce a sinusoid of the specified frequency.
for (size_t k = 0; k < kBlockSize * kNumBlocks; ++k) {
@ -47,18 +50,18 @@ void ProduceDecimatedSinusoidalOutputPower(int sample_rate_hz,
32767.f * sin(2.f * kPi * sinusoidal_frequency_hz * k / sample_rate_hz);
}
DecimatorBy4 decimator;
std::array<float, kSubBlockSize * kNumBlocks> output;
Decimator decimator(down_sampling_factor);
std::vector<float> output(sub_block_size * kNumBlocks);
for (size_t k = 0; k < kNumBlocks; ++k) {
std::array<float, kSubBlockSize> sub_block;
std::vector<float> sub_block(sub_block_size);
decimator.Decimate(
rtc::ArrayView<const float>(&input[k * kBlockSize], kBlockSize),
sub_block);
std::copy(sub_block.begin(), sub_block.end(),
output.begin() + k * kSubBlockSize);
output.begin() + k * sub_block_size);
}
ASSERT_GT(kNumBlocks, kNumStartupBlocks);
@ -66,8 +69,8 @@ void ProduceDecimatedSinusoidalOutputPower(int sample_rate_hz,
&input[kNumStartupBlocks * kBlockSize],
(kNumBlocks - kNumStartupBlocks) * kBlockSize);
rtc::ArrayView<const float> output_to_evaluate(
&output[kNumStartupBlocks * kSubBlockSize],
(kNumBlocks - kNumStartupBlocks) * kSubBlockSize);
&output[kNumStartupBlocks * sub_block_size],
(kNumBlocks - kNumStartupBlocks) * sub_block_size);
*input_power =
std::inner_product(input_to_evaluate.begin(), input_to_evaluate.end(),
input_to_evaluate.begin(), 0.f) /
@ -82,46 +85,64 @@ void ProduceDecimatedSinusoidalOutputPower(int sample_rate_hz,
// Verifies that there is little aliasing from upper frequencies in the
// downsampling.
TEST(DecimatorBy4, NoLeakageFromUpperFrequencies) {
TEST(Decimator, NoLeakageFromUpperFrequencies) {
float input_power;
float output_power;
for (auto rate : {8000, 16000, 32000, 48000}) {
ProduceDebugText(rate);
ProduceDecimatedSinusoidalOutputPower(rate, 3.f / 8.f * rate, &input_power,
&output_power);
EXPECT_GT(0.0001f * input_power, output_power);
for (auto down_sampling_factor : kDownSamplingFactors) {
ProduceDebugText(rate);
ProduceDecimatedSinusoidalOutputPower(rate, down_sampling_factor,
3.f / 8.f * rate, &input_power,
&output_power);
EXPECT_GT(0.0001f * input_power, output_power);
}
}
}
// Verifies that the impact of low-frequency content is small during the
// downsampling.
TEST(DecimatorBy4, NoImpactOnLowerFrequencies) {
TEST(Decimator, NoImpactOnLowerFrequencies) {
float input_power;
float output_power;
for (auto rate : {8000, 16000, 32000, 48000}) {
ProduceDebugText(rate);
ProduceDecimatedSinusoidalOutputPower(rate, 200.f, &input_power,
&output_power);
EXPECT_LT(0.7f * input_power, output_power);
for (auto down_sampling_factor : kDownSamplingFactors) {
ProduceDebugText(rate);
ProduceDecimatedSinusoidalOutputPower(rate, down_sampling_factor, 200.f,
&input_power, &output_power);
EXPECT_LT(0.7f * input_power, output_power);
}
}
}
#if RTC_DCHECK_IS_ON && GTEST_HAS_DEATH_TEST && !defined(WEBRTC_ANDROID)
// Verifies the check for the input size.
TEST(DecimatorBy4, WrongInputSize) {
DecimatorBy4 decimator;
TEST(Decimator, WrongInputSize) {
Decimator decimator(4);
std::vector<float> x(std::vector<float>(kBlockSize - 1, 0.f));
std::array<float, kSubBlockSize> x_downsampled;
std::array<float, kBlockSize / 4> x_downsampled;
EXPECT_DEATH(decimator.Decimate(x, x_downsampled), "");
}
// Verifies the check for non-null output parameter.
TEST(DecimatorBy4, NullOutput) {
DecimatorBy4 decimator;
TEST(Decimator, NullOutput) {
Decimator decimator(4);
std::vector<float> x(std::vector<float>(kBlockSize, 0.f));
EXPECT_DEATH(decimator.Decimate(x, nullptr), "");
}
// Verifies the check for the output size.
TEST(Decimator, WrongOutputSize) {
Decimator decimator(4);
std::vector<float> x(std::vector<float>(kBlockSize, 0.f));
std::array<float, kBlockSize / 4 - 1> x_downsampled;
EXPECT_DEATH(decimator.Decimate(x, x_downsampled), "");
}
// Verifies the check for the correct downsampling factor.
TEST(Decimator, CorrectDownSamplingFactor) {
EXPECT_DEATH(Decimator(3), "");
}
#endif
} // namespace webrtc

View File

@ -12,7 +12,8 @@
namespace webrtc {
DownsampledRenderBuffer::DownsampledRenderBuffer() = default;
DownsampledRenderBuffer::DownsampledRenderBuffer(size_t downsampled_buffer_size)
: buffer(downsampled_buffer_size, 0.f) {}
DownsampledRenderBuffer::~DownsampledRenderBuffer() = default;

View File

@ -11,7 +11,7 @@
#ifndef MODULES_AUDIO_PROCESSING_AEC3_DOWNSAMPLED_RENDER_BUFFER_H_
#define MODULES_AUDIO_PROCESSING_AEC3_DOWNSAMPLED_RENDER_BUFFER_H_
#include <array>
#include <vector>
#include "modules/audio_processing/aec3/aec3_common.h"
@ -19,9 +19,9 @@ namespace webrtc {
// Holds the circular buffer of the downsampled render data.
struct DownsampledRenderBuffer {
DownsampledRenderBuffer();
explicit DownsampledRenderBuffer(size_t downsampled_buffer_size);
~DownsampledRenderBuffer();
std::array<float, kDownsampledRenderBufferSize> buffer = {};
std::vector<float> buffer;
int position = 0;
};

View File

@ -19,23 +19,26 @@
namespace webrtc {
namespace {
constexpr int kDownSamplingFactor = 4;
} // namespace
EchoPathDelayEstimator::EchoPathDelayEstimator(
ApmDataDumper* data_dumper,
const EchoCanceller3Config& config)
: data_dumper_(data_dumper),
down_sampling_factor_(config.delay.down_sampling_factor),
sub_block_size_(down_sampling_factor_ != 0
? kBlockSize / down_sampling_factor_
: kBlockSize),
capture_decimator_(down_sampling_factor_),
matched_filter_(data_dumper_,
DetectOptimization(),
sub_block_size_,
kMatchedFilterWindowSizeSubBlocks,
kNumMatchedFilters,
config.delay.num_filters,
kMatchedFilterAlignmentShiftSizeSubBlocks,
config.render_levels.poor_excitation_render_limit),
matched_filter_lag_aggregator_(data_dumper_) {
matched_filter_lag_aggregator_(data_dumper_,
matched_filter_.GetMaxFilterLag()) {
RTC_DCHECK(data_dumper);
RTC_DCHECK(down_sampling_factor_ > 0);
}
EchoPathDelayEstimator::~EchoPathDelayEstimator() = default;
@ -50,8 +53,15 @@ rtc::Optional<size_t> EchoPathDelayEstimator::EstimateDelay(
rtc::ArrayView<const float> capture) {
RTC_DCHECK_EQ(kBlockSize, capture.size());
std::array<float, kSubBlockSize> downsampled_capture;
std::array<float, kBlockSize> downsampled_capture_data;
rtc::ArrayView<float> downsampled_capture(downsampled_capture_data.data(),
sub_block_size_);
data_dumper_->DumpWav("aec3_capture_decimator_input", capture.size(),
capture.data(), 16000, 1);
capture_decimator_.Decimate(capture, downsampled_capture);
data_dumper_->DumpWav("aec3_capture_decimator_output",
downsampled_capture.size(), downsampled_capture.data(),
16000 / down_sampling_factor_, 1);
matched_filter_.Update(render_buffer, downsampled_capture);
rtc::Optional<size_t> aggregated_matched_filter_lag =
@ -63,14 +73,14 @@ rtc::Optional<size_t> EchoPathDelayEstimator::EstimateDelay(
data_dumper_->DumpRaw("aec3_echo_path_delay_estimator_delay",
aggregated_matched_filter_lag
? static_cast<int>(*aggregated_matched_filter_lag *
kDownSamplingFactor)
down_sampling_factor_)
: -1);
// Return the detected delay in samples as the aggregated matched filter lag
// compensated by the down sampling factor for the signal being correlated.
return aggregated_matched_filter_lag
? rtc::Optional<size_t>(*aggregated_matched_filter_lag *
kDownSamplingFactor)
down_sampling_factor_)
: rtc::Optional<size_t>();
}

View File

@ -14,7 +14,7 @@
#include <vector>
#include "api/optional.h"
#include "modules/audio_processing/aec3/decimator_by_4.h"
#include "modules/audio_processing/aec3/decimator.h"
#include "modules/audio_processing/aec3/downsampled_render_buffer.h"
#include "modules/audio_processing/aec3/matched_filter.h"
#include "modules/audio_processing/aec3/matched_filter_lag_aggregator.h"
@ -40,9 +40,17 @@ class EchoPathDelayEstimator {
const DownsampledRenderBuffer& render_buffer,
rtc::ArrayView<const float> capture);
// Log delay estimator properties.
void LogDelayEstimationProperties(int sample_rate_hz, size_t shift) const {
matched_filter_.LogFilterProperties(sample_rate_hz, shift,
down_sampling_factor_);
}
private:
ApmDataDumper* const data_dumper_;
DecimatorBy4 capture_decimator_;
const size_t down_sampling_factor_;
const size_t sub_block_size_;
Decimator capture_decimator_;
MatchedFilter matched_filter_;
MatchedFilterLagAggregator matched_filter_lag_aggregator_;

View File

@ -35,9 +35,15 @@ std::string ProduceDebugText(size_t delay) {
// Verifies that the basic API calls work.
TEST(EchoPathDelayEstimator, BasicApiCalls) {
ApmDataDumper data_dumper(0);
EchoCanceller3Config config;
std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
RenderDelayBuffer::Create(3));
EchoPathDelayEstimator estimator(&data_dumper, EchoCanceller3Config());
RenderDelayBuffer::Create(
3, config.delay.down_sampling_factor,
GetDownSampledBufferSize(config.delay.down_sampling_factor,
config.delay.num_filters),
GetRenderDelayBufferSize(config.delay.down_sampling_factor,
config.delay.num_filters)));
EchoPathDelayEstimator estimator(&data_dumper, config);
std::vector<std::vector<float>> render(3, std::vector<float>(kBlockSize));
std::vector<float> capture(kBlockSize);
for (size_t k = 0; k < 100; ++k) {
@ -54,29 +60,40 @@ TEST(EchoPathDelayEstimator, DelayEstimation) {
std::vector<std::vector<float>> render(3, std::vector<float>(kBlockSize));
std::vector<float> capture(kBlockSize);
ApmDataDumper data_dumper(0);
for (size_t delay_samples : {15, 64, 150, 200, 800, 4000}) {
SCOPED_TRACE(ProduceDebugText(delay_samples));
std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
RenderDelayBuffer::Create(3));
DelayBuffer<float> signal_delay_buffer(delay_samples);
EchoPathDelayEstimator estimator(&data_dumper, EchoCanceller3Config());
constexpr size_t kDownSamplingFactors[] = {2, 4, 8};
for (auto down_sampling_factor : kDownSamplingFactors) {
EchoCanceller3Config config;
config.delay.down_sampling_factor = down_sampling_factor;
config.delay.num_filters = 10;
for (size_t delay_samples : {30, 64, 150, 200, 800, 4000}) {
SCOPED_TRACE(ProduceDebugText(delay_samples));
std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
RenderDelayBuffer::Create(
3, config.delay.down_sampling_factor,
GetDownSampledBufferSize(config.delay.down_sampling_factor,
config.delay.num_filters),
GetRenderDelayBufferSize(config.delay.down_sampling_factor,
config.delay.num_filters)));
DelayBuffer<float> signal_delay_buffer(delay_samples);
EchoPathDelayEstimator estimator(&data_dumper, config);
rtc::Optional<size_t> estimated_delay_samples;
for (size_t k = 0; k < (150 + delay_samples / kBlockSize); ++k) {
RandomizeSampleVector(&random_generator, render[0]);
signal_delay_buffer.Delay(render[0], capture);
render_delay_buffer->Insert(render);
render_delay_buffer->UpdateBuffers();
estimated_delay_samples = estimator.EstimateDelay(
render_delay_buffer->GetDownsampledRenderBuffer(), capture);
}
if (estimated_delay_samples) {
// Due to the internal down-sampling by 4 done inside the delay estimator
// the estimated delay cannot be expected to be closer than 4 samples to
// the true delay.
EXPECT_NEAR(delay_samples, *estimated_delay_samples, 4);
} else {
ADD_FAILURE();
rtc::Optional<size_t> estimated_delay_samples;
for (size_t k = 0; k < (300 + delay_samples / kBlockSize); ++k) {
RandomizeSampleVector(&random_generator, render[0]);
signal_delay_buffer.Delay(render[0], capture);
render_delay_buffer->Insert(render);
render_delay_buffer->UpdateBuffers();
estimated_delay_samples = estimator.EstimateDelay(
render_delay_buffer->GetDownsampledRenderBuffer(), capture);
}
if (estimated_delay_samples) {
// Due to the internal down-sampling done inside the delay estimator
// the estimated delay cannot be expected to be exact to the true delay.
EXPECT_NEAR(delay_samples, *estimated_delay_samples,
config.delay.down_sampling_factor);
} else {
ADD_FAILURE();
}
}
}
}
@ -85,13 +102,19 @@ TEST(EchoPathDelayEstimator, DelayEstimation) {
// quickly.
TEST(EchoPathDelayEstimator, NoInitialDelayestimates) {
Random random_generator(42U);
EchoCanceller3Config config;
std::vector<std::vector<float>> render(3, std::vector<float>(kBlockSize));
std::vector<float> capture(kBlockSize);
ApmDataDumper data_dumper(0);
std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
RenderDelayBuffer::Create(3));
RenderDelayBuffer::Create(
3, config.delay.down_sampling_factor,
GetDownSampledBufferSize(config.delay.down_sampling_factor,
config.delay.num_filters),
GetRenderDelayBufferSize(config.delay.down_sampling_factor,
config.delay.num_filters)));
EchoPathDelayEstimator estimator(&data_dumper, EchoCanceller3Config());
EchoPathDelayEstimator estimator(&data_dumper, config);
for (size_t k = 0; k < 19; ++k) {
RandomizeSampleVector(&random_generator, render[0]);
std::copy(render[0].begin(), render[0].end(), capture.begin());
@ -106,12 +129,18 @@ TEST(EchoPathDelayEstimator, NoInitialDelayestimates) {
// signals of low level.
TEST(EchoPathDelayEstimator, NoDelayEstimatesForLowLevelRenderSignals) {
Random random_generator(42U);
EchoCanceller3Config config;
std::vector<std::vector<float>> render(3, std::vector<float>(kBlockSize));
std::vector<float> capture(kBlockSize);
ApmDataDumper data_dumper(0);
EchoPathDelayEstimator estimator(&data_dumper, EchoCanceller3Config());
EchoPathDelayEstimator estimator(&data_dumper, config);
std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
RenderDelayBuffer::Create(3));
RenderDelayBuffer::Create(
3, config.delay.down_sampling_factor,
GetDownSampledBufferSize(config.delay.down_sampling_factor,
config.delay.num_filters),
GetRenderDelayBufferSize(config.delay.down_sampling_factor,
config.delay.num_filters)));
for (size_t k = 0; k < 100; ++k) {
RandomizeSampleVector(&random_generator, render[0]);
for (auto& render_k : render[0]) {
@ -129,12 +158,18 @@ TEST(EchoPathDelayEstimator, NoDelayEstimatesForLowLevelRenderSignals) {
// uncorrelated signals.
TEST(EchoPathDelayEstimator, NoDelayEstimatesForUncorrelatedSignals) {
Random random_generator(42U);
EchoCanceller3Config config;
std::vector<std::vector<float>> render(3, std::vector<float>(kBlockSize));
std::vector<float> capture(kBlockSize);
ApmDataDumper data_dumper(0);
EchoPathDelayEstimator estimator(&data_dumper, EchoCanceller3Config());
EchoPathDelayEstimator estimator(&data_dumper, config);
std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
RenderDelayBuffer::Create(3));
RenderDelayBuffer::Create(
3, config.delay.down_sampling_factor,
GetDownSampledBufferSize(config.delay.down_sampling_factor,
config.delay.num_filters),
GetRenderDelayBufferSize(config.delay.down_sampling_factor,
config.delay.num_filters)));
for (size_t k = 0; k < 100; ++k) {
RandomizeSampleVector(&random_generator, render[0]);
RandomizeSampleVector(&random_generator, capture);
@ -152,9 +187,15 @@ TEST(EchoPathDelayEstimator, NoDelayEstimatesForUncorrelatedSignals) {
// tests on test bots has been fixed.
TEST(EchoPathDelayEstimator, DISABLED_WrongRenderBlockSize) {
ApmDataDumper data_dumper(0);
EchoPathDelayEstimator estimator(&data_dumper, EchoCanceller3Config());
EchoCanceller3Config config;
EchoPathDelayEstimator estimator(&data_dumper, config);
std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
RenderDelayBuffer::Create(3));
RenderDelayBuffer::Create(
3, config.delay.down_sampling_factor,
GetDownSampledBufferSize(config.delay.down_sampling_factor,
config.delay.num_filters),
GetRenderDelayBufferSize(config.delay.down_sampling_factor,
config.delay.num_filters)));
std::vector<float> capture(kBlockSize);
EXPECT_DEATH(estimator.EstimateDelay(
render_delay_buffer->GetDownsampledRenderBuffer(), capture),
@ -166,9 +207,15 @@ TEST(EchoPathDelayEstimator, DISABLED_WrongRenderBlockSize) {
// tests on test bots has been fixed.
TEST(EchoPathDelayEstimator, WrongCaptureBlockSize) {
ApmDataDumper data_dumper(0);
EchoPathDelayEstimator estimator(&data_dumper, EchoCanceller3Config());
EchoCanceller3Config config;
EchoPathDelayEstimator estimator(&data_dumper, config);
std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
RenderDelayBuffer::Create(3));
RenderDelayBuffer::Create(
3, config.delay.down_sampling_factor,
GetDownSampledBufferSize(config.delay.down_sampling_factor,
config.delay.num_filters),
GetRenderDelayBufferSize(config.delay.down_sampling_factor,
config.delay.num_filters)));
std::vector<float> capture(std::vector<float>(kBlockSize - 1));
EXPECT_DEATH(estimator.EstimateDelay(
render_delay_buffer->GetDownsampledRenderBuffer(), capture),

View File

@ -39,6 +39,9 @@ std::string ProduceDebugText(int sample_rate_hz, int delay) {
return ss.str();
}
constexpr size_t kDownSamplingFactor = 4;
constexpr size_t kNumMatchedFilters = 4;
} // namespace
// Verifies the basic API call sequence
@ -47,8 +50,10 @@ TEST(EchoRemover, BasicApiCalls) {
SCOPED_TRACE(ProduceDebugText(rate));
std::unique_ptr<EchoRemover> remover(
EchoRemover::Create(EchoCanceller3Config(), rate));
std::unique_ptr<RenderDelayBuffer> render_buffer(
RenderDelayBuffer::Create(NumBandsForRate(rate)));
std::unique_ptr<RenderDelayBuffer> render_buffer(RenderDelayBuffer::Create(
NumBandsForRate(rate), kDownSamplingFactor,
GetDownSampledBufferSize(kDownSamplingFactor, kNumMatchedFilters),
GetRenderDelayBufferSize(kDownSamplingFactor, kNumMatchedFilters)));
std::vector<std::vector<float>> render(NumBandsForRate(rate),
std::vector<float>(kBlockSize, 0.f));
@ -86,8 +91,10 @@ TEST(EchoRemover, WrongCaptureBlockSize) {
SCOPED_TRACE(ProduceDebugText(rate));
std::unique_ptr<EchoRemover> remover(
EchoRemover::Create(EchoCanceller3Config(), rate));
std::unique_ptr<RenderDelayBuffer> render_buffer(
RenderDelayBuffer::Create(NumBandsForRate(rate)));
std::unique_ptr<RenderDelayBuffer> render_buffer(RenderDelayBuffer::Create(
NumBandsForRate(rate), kDownSamplingFactor,
GetDownSampledBufferSize(kDownSamplingFactor, kNumMatchedFilters),
GetRenderDelayBufferSize(kDownSamplingFactor, kNumMatchedFilters)));
std::vector<std::vector<float>> capture(
NumBandsForRate(rate), std::vector<float>(kBlockSize - 1, 0.f));
EchoPathVariability echo_path_variability(false, false);
@ -107,8 +114,10 @@ TEST(EchoRemover, DISABLED_WrongCaptureNumBands) {
SCOPED_TRACE(ProduceDebugText(rate));
std::unique_ptr<EchoRemover> remover(
EchoRemover::Create(EchoCanceller3Config(), rate));
std::unique_ptr<RenderDelayBuffer> render_buffer(
RenderDelayBuffer::Create(NumBandsForRate(rate)));
std::unique_ptr<RenderDelayBuffer> render_buffer(RenderDelayBuffer::Create(
NumBandsForRate(rate), kDownSamplingFactor,
GetDownSampledBufferSize(kDownSamplingFactor, kNumMatchedFilters),
GetRenderDelayBufferSize(kDownSamplingFactor, kNumMatchedFilters)));
std::vector<std::vector<float>> capture(
NumBandsForRate(rate == 48000 ? 16000 : rate + 16000),
std::vector<float>(kBlockSize, 0.f));
@ -125,8 +134,10 @@ TEST(EchoRemover, DISABLED_WrongCaptureNumBands) {
TEST(EchoRemover, NullCapture) {
std::unique_ptr<EchoRemover> remover(
EchoRemover::Create(EchoCanceller3Config(), 8000));
std::unique_ptr<RenderDelayBuffer> render_buffer(
RenderDelayBuffer::Create(3));
std::unique_ptr<RenderDelayBuffer> render_buffer(RenderDelayBuffer::Create(
3, kDownSamplingFactor,
GetDownSampledBufferSize(kDownSamplingFactor, kNumMatchedFilters),
GetRenderDelayBufferSize(kDownSamplingFactor, kNumMatchedFilters)));
EchoPathVariability echo_path_variability(false, false);
rtc::Optional<size_t> echo_path_delay_samples;
EXPECT_DEATH(
@ -153,7 +164,11 @@ TEST(EchoRemover, BasicEchoRemoval) {
std::unique_ptr<EchoRemover> remover(
EchoRemover::Create(EchoCanceller3Config(), rate));
std::unique_ptr<RenderDelayBuffer> render_buffer(
RenderDelayBuffer::Create(NumBandsForRate(rate)));
RenderDelayBuffer::Create(
NumBandsForRate(rate), kDownSamplingFactor,
GetDownSampledBufferSize(kDownSamplingFactor, kNumMatchedFilters),
GetRenderDelayBufferSize(kDownSamplingFactor,
kNumMatchedFilters)));
std::vector<std::unique_ptr<DelayBuffer<float>>> delay_buffers(x.size());
for (size_t j = 0; j < x.size(); ++j) {
delay_buffers[j].reset(new DelayBuffer<float>(delay_samples));

View File

@ -21,6 +21,7 @@
#include "modules/audio_processing/include/audio_processing.h"
#include "modules/audio_processing/logging/apm_data_dumper.h"
#include "rtc_base/logging.h"
namespace webrtc {
namespace aec3 {
@ -39,7 +40,7 @@ void MatchedFilterCore_NEON(size_t x_start_index,
RTC_DCHECK_EQ(0, h_size % 4);
// Process for all samples in the sub-block.
for (size_t i = 0; i < kSubBlockSize; ++i) {
for (size_t i = 0; i < y.size(); ++i) {
// Apply the matched filter as filter * x, and compute x * x.
RTC_DCHECK_GT(x_size, x_start_index);
@ -147,7 +148,7 @@ void MatchedFilterCore_SSE2(size_t x_start_index,
RTC_DCHECK_EQ(0, h_size % 4);
// Process for all samples in the sub-block.
for (size_t i = 0; i < kSubBlockSize; ++i) {
for (size_t i = 0; i < y.size(); ++i) {
// Apply the matched filter as filter * x, and compute x * x.
RTC_DCHECK_GT(x_size, x_start_index);
@ -252,7 +253,7 @@ void MatchedFilterCore(size_t x_start_index,
bool* filters_updated,
float* error_sum) {
// Process for all samples in the sub-block.
for (size_t i = 0; i < kSubBlockSize; ++i) {
for (size_t i = 0; i < y.size(); ++i) {
// Apply the matched filter as filter * x, and compute x * x.
float x2_sum = 0.f;
float s = 0;
@ -289,19 +290,25 @@ void MatchedFilterCore(size_t x_start_index,
MatchedFilter::MatchedFilter(ApmDataDumper* data_dumper,
Aec3Optimization optimization,
size_t sub_block_size,
size_t window_size_sub_blocks,
int num_matched_filters,
size_t alignment_shift_sub_blocks,
float excitation_limit)
: data_dumper_(data_dumper),
optimization_(optimization),
filter_intra_lag_shift_(alignment_shift_sub_blocks * kSubBlockSize),
filters_(num_matched_filters,
std::vector<float>(window_size_sub_blocks * kSubBlockSize, 0.f)),
sub_block_size_(sub_block_size),
filter_intra_lag_shift_(alignment_shift_sub_blocks * sub_block_size_),
filters_(
num_matched_filters,
std::vector<float>(window_size_sub_blocks * sub_block_size_, 0.f)),
lag_estimates_(num_matched_filters),
filters_offsets_(num_matched_filters, 0),
excitation_limit_(excitation_limit) {
RTC_DCHECK(data_dumper);
RTC_DCHECK_LT(0, window_size_sub_blocks);
RTC_DCHECK((kBlockSize % sub_block_size) == 0);
RTC_DCHECK((sub_block_size % 4) == 0);
}
MatchedFilter::~MatchedFilter() = default;
@ -317,8 +324,9 @@ void MatchedFilter::Reset() {
}
void MatchedFilter::Update(const DownsampledRenderBuffer& render_buffer,
const std::array<float, kSubBlockSize>& capture) {
const std::array<float, kSubBlockSize>& y = capture;
rtc::ArrayView<const float> capture) {
RTC_DCHECK_EQ(sub_block_size_, capture.size());
auto& y = capture;
const float x2_sum_threshold =
filters_[0].size() * excitation_limit_ * excitation_limit_;
@ -330,7 +338,7 @@ void MatchedFilter::Update(const DownsampledRenderBuffer& render_buffer,
bool filters_updated = false;
size_t x_start_index =
(render_buffer.position + alignment_shift + kSubBlockSize - 1) %
(render_buffer.position + alignment_shift + sub_block_size_ - 1) %
render_buffer.buffer.size();
switch (optimization_) {
@ -375,8 +383,7 @@ void MatchedFilter::Update(const DownsampledRenderBuffer& render_buffer,
error_sum < kMatchingFilterThreshold * error_sum_anchor),
lag_estimate + alignment_shift, filters_updated);
// TODO(peah): Remove once development of EchoCanceller3 is fully done.
RTC_DCHECK_EQ(4, filters_.size());
RTC_DCHECK_GE(10, filters_.size());
switch (n) {
case 0:
data_dumper_->DumpRaw("aec3_correlator_0_h", filters_[0]);
@ -390,12 +397,47 @@ void MatchedFilter::Update(const DownsampledRenderBuffer& render_buffer,
case 3:
data_dumper_->DumpRaw("aec3_correlator_3_h", filters_[3]);
break;
case 4:
data_dumper_->DumpRaw("aec3_correlator_4_h", filters_[4]);
break;
case 5:
data_dumper_->DumpRaw("aec3_correlator_5_h", filters_[5]);
break;
case 6:
data_dumper_->DumpRaw("aec3_correlator_6_h", filters_[6]);
break;
case 7:
data_dumper_->DumpRaw("aec3_correlator_7_h", filters_[7]);
break;
case 8:
data_dumper_->DumpRaw("aec3_correlator_8_h", filters_[8]);
break;
case 9:
data_dumper_->DumpRaw("aec3_correlator_9_h", filters_[9]);
break;
default:
RTC_DCHECK(false);
RTC_NOTREACHED();
}
alignment_shift += filter_intra_lag_shift_;
}
}
void MatchedFilter::LogFilterProperties(int sample_rate_hz,
size_t shift,
size_t downsampling_factor) const {
size_t alignment_shift = 0;
const int fs_by_1000 = LowestBandRate(sample_rate_hz) / 1000;
for (size_t k = 0; k < filters_.size(); ++k) {
int start = static_cast<int>(alignment_shift * downsampling_factor);
int end = static_cast<int>((alignment_shift + filters_[k].size()) *
downsampling_factor);
RTC_LOG(LS_INFO) << "Filter " << k << ": start: "
<< (start - static_cast<int>(shift)) / fs_by_1000
<< " ms, end: "
<< (end - static_cast<int>(shift)) / fs_by_1000 << " ms.";
alignment_shift += filter_intra_lag_shift_;
}
}
} // namespace webrtc

View File

@ -81,6 +81,7 @@ class MatchedFilter {
MatchedFilter(ApmDataDumper* data_dumper,
Aec3Optimization optimization,
size_t sub_block_size,
size_t window_size_sub_blocks,
int num_matched_filters,
size_t alignment_shift_sub_blocks,
@ -90,7 +91,7 @@ class MatchedFilter {
// Updates the correlation with the values in the capture buffer.
void Update(const DownsampledRenderBuffer& render_buffer,
const std::array<float, kSubBlockSize>& capture);
rtc::ArrayView<const float> capture);
// Resets the matched filter.
void Reset();
@ -100,15 +101,24 @@ class MatchedFilter {
return lag_estimates_;
}
// Returns the number of lag estimates produced using the shifted signals.
size_t NumLagEstimates() const { return filters_.size(); }
// Returns the maximum filter lag.
size_t GetMaxFilterLag() const {
return filters_.size() * filter_intra_lag_shift_ + filters_[0].size();
}
// Log matched filter properties.
void LogFilterProperties(int sample_rate_hz,
size_t shift,
size_t downsampling_factor) const;
private:
ApmDataDumper* const data_dumper_;
const Aec3Optimization optimization_;
const size_t sub_block_size_;
const size_t filter_intra_lag_shift_;
std::vector<std::vector<float>> filters_;
std::vector<LagEstimate> lag_estimates_;
std::vector<size_t> filters_offsets_;
const float excitation_limit_;
RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(MatchedFilter);

View File

@ -14,17 +14,17 @@
namespace webrtc {
MatchedFilterLagAggregator::MatchedFilterLagAggregator(
ApmDataDumper* data_dumper)
: data_dumper_(data_dumper) {
ApmDataDumper* data_dumper,
size_t max_filter_lag)
: data_dumper_(data_dumper), histogram_(max_filter_lag + 1, 0) {
RTC_DCHECK(data_dumper);
histogram_.fill(0);
histogram_data_.fill(0);
}
MatchedFilterLagAggregator::~MatchedFilterLagAggregator() = default;
void MatchedFilterLagAggregator::Reset() {
histogram_.fill(0);
std::fill(histogram_.begin(), histogram_.end(), 0);
histogram_data_.fill(0);
histogram_data_index_ = 0;
}

View File

@ -25,7 +25,7 @@ class ApmDataDumper;
// reliable combined lag estimate.
class MatchedFilterLagAggregator {
public:
explicit MatchedFilterLagAggregator(ApmDataDumper* data_dumper);
MatchedFilterLagAggregator(ApmDataDumper* data_dumper, size_t max_filter_lag);
~MatchedFilterLagAggregator();
// Resets the aggregator.
@ -37,7 +37,7 @@ class MatchedFilterLagAggregator {
private:
ApmDataDumper* const data_dumper_;
std::array<int, 1664> histogram_;
std::vector<int> histogram_;
std::array<int, 250> histogram_data_;
int histogram_data_index_ = 0;

View File

@ -32,7 +32,7 @@ TEST(MatchedFilterLagAggregator, MostAccurateLagChosen) {
constexpr size_t kLag2 = 10;
ApmDataDumper data_dumper(0);
std::vector<MatchedFilter::LagEstimate> lag_estimates(2);
MatchedFilterLagAggregator aggregator(&data_dumper);
MatchedFilterLagAggregator aggregator(&data_dumper, std::max(kLag1, kLag2));
lag_estimates[0] = MatchedFilter::LagEstimate(1.f, true, kLag1, true);
lag_estimates[1] = MatchedFilter::LagEstimate(0.5f, true, kLag2, true);
@ -65,7 +65,7 @@ TEST(MatchedFilterLagAggregator,
LagEstimateInvarianceRequiredForAggregatedLag) {
ApmDataDumper data_dumper(0);
std::vector<MatchedFilter::LagEstimate> lag_estimates(1);
MatchedFilterLagAggregator aggregator(&data_dumper);
MatchedFilterLagAggregator aggregator(&data_dumper, 100);
for (size_t k = 0; k < kNumLagsBeforeDetection * 100; ++k) {
lag_estimates[0] = MatchedFilter::LagEstimate(1.f, true, k % 100, true);
rtc::Optional<size_t> aggregated_lag = aggregator.Aggregate(lag_estimates);
@ -80,7 +80,7 @@ TEST(MatchedFilterLagAggregator,
constexpr size_t kLag = 5;
ApmDataDumper data_dumper(0);
std::vector<MatchedFilter::LagEstimate> lag_estimates(1);
MatchedFilterLagAggregator aggregator(&data_dumper);
MatchedFilterLagAggregator aggregator(&data_dumper, kLag);
for (size_t k = 0; k < kNumLagsBeforeDetection * 10; ++k) {
lag_estimates[0] = MatchedFilter::LagEstimate(1.f, true, kLag, false);
rtc::Optional<size_t> aggregated_lag = aggregator.Aggregate(lag_estimates);
@ -97,7 +97,7 @@ TEST(MatchedFilterLagAggregator, DISABLED_PersistentAggregatedLag) {
constexpr size_t kLag2 = 10;
ApmDataDumper data_dumper(0);
std::vector<MatchedFilter::LagEstimate> lag_estimates(1);
MatchedFilterLagAggregator aggregator(&data_dumper);
MatchedFilterLagAggregator aggregator(&data_dumper, std::max(kLag1, kLag2));
rtc::Optional<size_t> aggregated_lag;
for (size_t k = 0; k < kNumLagsBeforeDetection; ++k) {
lag_estimates[0] = MatchedFilter::LagEstimate(1.f, true, kLag1, true);
@ -118,7 +118,7 @@ TEST(MatchedFilterLagAggregator, DISABLED_PersistentAggregatedLag) {
// Verifies the check for non-null data dumper.
TEST(MatchedFilterLagAggregator, NullDataDumper) {
EXPECT_DEATH(MatchedFilterLagAggregator(nullptr), "");
EXPECT_DEATH(MatchedFilterLagAggregator(nullptr, 10), "");
}
#endif

View File

@ -19,7 +19,7 @@
#include <string>
#include "modules/audio_processing/aec3/aec3_common.h"
#include "modules/audio_processing/aec3/decimator_by_4.h"
#include "modules/audio_processing/aec3/decimator.h"
#include "modules/audio_processing/aec3/render_delay_buffer.h"
#include "modules/audio_processing/logging/apm_data_dumper.h"
#include "modules/audio_processing/test/echo_canceller_test_tools.h"
@ -31,15 +31,17 @@ namespace webrtc {
namespace aec3 {
namespace {
std::string ProduceDebugText(size_t delay) {
std::string ProduceDebugText(size_t delay, size_t down_sampling_factor) {
std::ostringstream ss;
ss << "Delay: " << delay;
ss << ", Down sampling factor: " << down_sampling_factor;
return ss.str();
}
constexpr size_t kNumMatchedFilters = 10;
constexpr size_t kDownSamplingFactors[] = {2, 4, 8};
constexpr size_t kWindowSizeSubBlocks = 32;
constexpr size_t kAlignmentShiftSubBlocks = kWindowSizeSubBlocks * 3 / 4;
constexpr size_t kNumMatchedFilters = 4;
} // namespace
@ -48,34 +50,38 @@ constexpr size_t kNumMatchedFilters = 4;
// counterparts.
TEST(MatchedFilter, TestNeonOptimizations) {
Random random_generator(42U);
std::vector<float> x(2000);
RandomizeSampleVector(&random_generator, x);
std::vector<float> y(kSubBlockSize);
std::vector<float> h_NEON(512);
std::vector<float> h(512);
int x_index = 0;
for (int k = 0; k < 1000; ++k) {
RandomizeSampleVector(&random_generator, y);
for (auto down_sampling_factor : kDownSamplingFactors) {
const size_t sub_block_size = kBlockSize / down_sampling_factor;
bool filters_updated = false;
float error_sum = 0.f;
bool filters_updated_NEON = false;
float error_sum_NEON = 0.f;
std::vector<float> x(2000);
RandomizeSampleVector(&random_generator, x);
std::vector<float> y(sub_block_size);
std::vector<float> h_NEON(512);
std::vector<float> h(512);
int x_index = 0;
for (int k = 0; k < 1000; ++k) {
RandomizeSampleVector(&random_generator, y);
MatchedFilterCore_NEON(x_index, h.size() * 150.f * 150.f, x, y, h_NEON,
&filters_updated_NEON, &error_sum_NEON);
bool filters_updated = false;
float error_sum = 0.f;
bool filters_updated_NEON = false;
float error_sum_NEON = 0.f;
MatchedFilterCore(x_index, h.size() * 150.f * 150.f, x, y, h,
&filters_updated, &error_sum);
MatchedFilterCore_NEON(x_index, h.size() * 150.f * 150.f, x, y, h_NEON,
&filters_updated_NEON, &error_sum_NEON);
EXPECT_EQ(filters_updated, filters_updated_NEON);
EXPECT_NEAR(error_sum, error_sum_NEON, error_sum / 100000.f);
MatchedFilterCore(x_index, h.size() * 150.f * 150.f, x, y, h,
&filters_updated, &error_sum);
for (size_t j = 0; j < h.size(); ++j) {
EXPECT_NEAR(h[j], h_NEON[j], 0.00001f);
EXPECT_EQ(filters_updated, filters_updated_NEON);
EXPECT_NEAR(error_sum, error_sum_NEON, error_sum / 100000.f);
for (size_t j = 0; j < h.size(); ++j) {
EXPECT_NEAR(h[j], h_NEON[j], 0.00001f);
}
x_index = (x_index + sub_block_size) % x.size();
}
x_index = (x_index + kSubBlockSize) % x.size();
}
}
#endif
@ -87,34 +93,37 @@ TEST(MatchedFilter, TestSse2Optimizations) {
bool use_sse2 = (WebRtc_GetCPUInfo(kSSE2) != 0);
if (use_sse2) {
Random random_generator(42U);
std::vector<float> x(2000);
RandomizeSampleVector(&random_generator, x);
std::vector<float> y(kSubBlockSize);
std::vector<float> h_SSE2(512);
std::vector<float> h(512);
int x_index = 0;
for (int k = 0; k < 1000; ++k) {
RandomizeSampleVector(&random_generator, y);
for (auto down_sampling_factor : kDownSamplingFactors) {
const size_t sub_block_size = kBlockSize / down_sampling_factor;
std::vector<float> x(2000);
RandomizeSampleVector(&random_generator, x);
std::vector<float> y(sub_block_size);
std::vector<float> h_SSE2(512);
std::vector<float> h(512);
int x_index = 0;
for (int k = 0; k < 1000; ++k) {
RandomizeSampleVector(&random_generator, y);
bool filters_updated = false;
float error_sum = 0.f;
bool filters_updated_SSE2 = false;
float error_sum_SSE2 = 0.f;
bool filters_updated = false;
float error_sum = 0.f;
bool filters_updated_SSE2 = false;
float error_sum_SSE2 = 0.f;
MatchedFilterCore_SSE2(x_index, h.size() * 150.f * 150.f, x, y, h_SSE2,
&filters_updated_SSE2, &error_sum_SSE2);
MatchedFilterCore_SSE2(x_index, h.size() * 150.f * 150.f, x, y, h_SSE2,
&filters_updated_SSE2, &error_sum_SSE2);
MatchedFilterCore(x_index, h.size() * 150.f * 150.f, x, y, h,
&filters_updated, &error_sum);
MatchedFilterCore(x_index, h.size() * 150.f * 150.f, x, y, h,
&filters_updated, &error_sum);
EXPECT_EQ(filters_updated, filters_updated_SSE2);
EXPECT_NEAR(error_sum, error_sum_SSE2, error_sum / 100000.f);
EXPECT_EQ(filters_updated, filters_updated_SSE2);
EXPECT_NEAR(error_sum, error_sum_SSE2, error_sum / 100000.f);
for (size_t j = 0; j < h.size(); ++j) {
EXPECT_NEAR(h[j], h_SSE2[j], 0.00001f);
for (size_t j = 0; j < h.size(); ++j) {
EXPECT_NEAR(h[j], h_SSE2[j], 0.00001f);
}
x_index = (x_index + sub_block_size) % x.size();
}
x_index = (x_index + kSubBlockSize) % x.size();
}
}
}
@ -126,71 +135,100 @@ TEST(MatchedFilter, TestSse2Optimizations) {
// delayed signals.
TEST(MatchedFilter, LagEstimation) {
Random random_generator(42U);
std::vector<std::vector<float>> render(3,
std::vector<float>(kBlockSize, 0.f));
std::array<float, kBlockSize> capture;
capture.fill(0.f);
ApmDataDumper data_dumper(0);
for (size_t delay_samples : {5, 64, 150, 200, 800, 1000}) {
SCOPED_TRACE(ProduceDebugText(delay_samples));
DecimatorBy4 capture_decimator;
DelayBuffer<float> signal_delay_buffer(4 * delay_samples);
MatchedFilter filter(&data_dumper, DetectOptimization(),
kWindowSizeSubBlocks, kNumMatchedFilters,
kAlignmentShiftSubBlocks, 150);
std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
RenderDelayBuffer::Create(3));
for (auto down_sampling_factor : kDownSamplingFactors) {
const size_t sub_block_size = kBlockSize / down_sampling_factor;
// Analyze the correlation between render and capture.
for (size_t k = 0; k < (150 + delay_samples / kSubBlockSize); ++k) {
RandomizeSampleVector(&random_generator, render[0]);
signal_delay_buffer.Delay(render[0], capture);
render_delay_buffer->Insert(render);
render_delay_buffer->UpdateBuffers();
std::array<float, kSubBlockSize> downsampled_capture;
capture_decimator.Decimate(capture, downsampled_capture);
filter.Update(render_delay_buffer->GetDownsampledRenderBuffer(),
downsampled_capture);
}
std::vector<std::vector<float>> render(3,
std::vector<float>(kBlockSize, 0.f));
std::array<float, kBlockSize> capture;
capture.fill(0.f);
ApmDataDumper data_dumper(0);
for (size_t delay_samples : {5, 64, 150, 200, 800, 1000}) {
SCOPED_TRACE(ProduceDebugText(delay_samples, down_sampling_factor));
Decimator capture_decimator(down_sampling_factor);
DelayBuffer<float> signal_delay_buffer(down_sampling_factor *
delay_samples);
MatchedFilter filter(&data_dumper, DetectOptimization(), sub_block_size,
kWindowSizeSubBlocks, kNumMatchedFilters,
kAlignmentShiftSubBlocks, 150);
std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
RenderDelayBuffer::Create(
3, down_sampling_factor,
GetDownSampledBufferSize(down_sampling_factor,
kNumMatchedFilters),
GetRenderDelayBufferSize(down_sampling_factor,
kNumMatchedFilters)));
// Obtain the lag estimates.
auto lag_estimates = filter.GetLagEstimates();
// Find which lag estimate should be the most accurate.
rtc::Optional<size_t> expected_most_accurate_lag_estimate;
size_t alignment_shift_sub_blocks = 0;
for (size_t k = 0; k < kNumMatchedFilters; ++k) {
if ((alignment_shift_sub_blocks + kWindowSizeSubBlocks / 2) *
kSubBlockSize >
delay_samples) {
expected_most_accurate_lag_estimate = rtc::Optional<size_t>(k);
break;
// Analyze the correlation between render and capture.
for (size_t k = 0; k < (300 + delay_samples / sub_block_size); ++k) {
RandomizeSampleVector(&random_generator, render[0]);
signal_delay_buffer.Delay(render[0], capture);
render_delay_buffer->Insert(render);
render_delay_buffer->UpdateBuffers();
std::array<float, kBlockSize> downsampled_capture_data;
rtc::ArrayView<float> downsampled_capture(
downsampled_capture_data.data(), sub_block_size);
capture_decimator.Decimate(capture, downsampled_capture);
filter.Update(render_delay_buffer->GetDownsampledRenderBuffer(),
downsampled_capture);
}
alignment_shift_sub_blocks += kAlignmentShiftSubBlocks;
}
ASSERT_TRUE(expected_most_accurate_lag_estimate);
// Verify that the expected most accurate lag estimate is the most accurate
// estimate.
for (size_t k = 0; k < kNumMatchedFilters; ++k) {
if (k != *expected_most_accurate_lag_estimate) {
EXPECT_GT(lag_estimates[*expected_most_accurate_lag_estimate].accuracy,
lag_estimates[k].accuracy);
// Obtain the lag estimates.
auto lag_estimates = filter.GetLagEstimates();
// Find which lag estimate should be the most accurate.
rtc::Optional<size_t> expected_most_accurate_lag_estimate;
size_t alignment_shift_sub_blocks = 0;
for (size_t k = 0; k < kNumMatchedFilters; ++k) {
if ((alignment_shift_sub_blocks + 3 * kWindowSizeSubBlocks / 4) *
sub_block_size >
delay_samples) {
expected_most_accurate_lag_estimate =
rtc::Optional<size_t>(k > 0 ? k - 1 : 0);
break;
}
alignment_shift_sub_blocks += kAlignmentShiftSubBlocks;
}
ASSERT_TRUE(expected_most_accurate_lag_estimate);
// Verify that the expected most accurate lag estimate is the most
// accurate estimate.
for (size_t k = 0; k < kNumMatchedFilters; ++k) {
if (k != *expected_most_accurate_lag_estimate &&
k != (*expected_most_accurate_lag_estimate + 1)) {
EXPECT_TRUE(
lag_estimates[*expected_most_accurate_lag_estimate].accuracy >
lag_estimates[k].accuracy ||
!lag_estimates[k].reliable ||
!lag_estimates[*expected_most_accurate_lag_estimate].reliable);
}
}
// Verify that all lag estimates are updated as expected for signals
// containing strong noise.
for (auto& le : lag_estimates) {
EXPECT_TRUE(le.updated);
}
// Verify that the expected most accurate lag estimate is reliable.
EXPECT_TRUE(
lag_estimates[*expected_most_accurate_lag_estimate].reliable ||
lag_estimates[std::min(*expected_most_accurate_lag_estimate + 1,
lag_estimates.size() - 1)]
.reliable);
// Verify that the expected most accurate lag estimate is correct.
if (lag_estimates[*expected_most_accurate_lag_estimate].reliable) {
EXPECT_TRUE(delay_samples ==
lag_estimates[*expected_most_accurate_lag_estimate].lag);
} else {
EXPECT_TRUE(
delay_samples ==
lag_estimates[std::min(*expected_most_accurate_lag_estimate + 1,
lag_estimates.size() - 1)]
.lag);
}
}
// Verify that all lag estimates are updated as expected for signals
// containing strong noise.
for (auto& le : lag_estimates) {
EXPECT_TRUE(le.updated);
}
// Verify that the expected most accurate lag estimate is reliable.
EXPECT_TRUE(lag_estimates[*expected_most_accurate_lag_estimate].reliable);
// Verify that the expected most accurate lag estimate is correct.
EXPECT_EQ(delay_samples,
lag_estimates[*expected_most_accurate_lag_estimate].lag);
}
}
@ -198,31 +236,41 @@ TEST(MatchedFilter, LagEstimation) {
// estimates for uncorrelated render and capture signals.
TEST(MatchedFilter, LagNotReliableForUncorrelatedRenderAndCapture) {
Random random_generator(42U);
std::vector<std::vector<float>> render(3,
std::vector<float>(kBlockSize, 0.f));
std::array<float, kSubBlockSize> capture;
capture.fill(0.f);
ApmDataDumper data_dumper(0);
std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
RenderDelayBuffer::Create(3));
MatchedFilter filter(&data_dumper, DetectOptimization(), kWindowSizeSubBlocks,
kNumMatchedFilters, kAlignmentShiftSubBlocks, 150);
for (auto down_sampling_factor : kDownSamplingFactors) {
const size_t sub_block_size = kBlockSize / down_sampling_factor;
// Analyze the correlation between render and capture.
for (size_t k = 0; k < 100; ++k) {
RandomizeSampleVector(&random_generator, render[0]);
RandomizeSampleVector(&random_generator, capture);
render_delay_buffer->Insert(render);
filter.Update(render_delay_buffer->GetDownsampledRenderBuffer(), capture);
}
std::vector<std::vector<float>> render(3,
std::vector<float>(kBlockSize, 0.f));
std::array<float, kBlockSize> capture_data;
rtc::ArrayView<float> capture(capture_data.data(), sub_block_size);
std::fill(capture.begin(), capture.end(), 0.f);
ApmDataDumper data_dumper(0);
std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
RenderDelayBuffer::Create(
3, down_sampling_factor,
GetDownSampledBufferSize(down_sampling_factor, kNumMatchedFilters),
GetRenderDelayBufferSize(down_sampling_factor,
kNumMatchedFilters)));
MatchedFilter filter(&data_dumper, DetectOptimization(), sub_block_size,
kWindowSizeSubBlocks, kNumMatchedFilters,
kAlignmentShiftSubBlocks, 150);
// Obtain the lag estimates.
auto lag_estimates = filter.GetLagEstimates();
EXPECT_EQ(kNumMatchedFilters, lag_estimates.size());
// Analyze the correlation between render and capture.
for (size_t k = 0; k < 100; ++k) {
RandomizeSampleVector(&random_generator, render[0]);
RandomizeSampleVector(&random_generator, capture);
render_delay_buffer->Insert(render);
filter.Update(render_delay_buffer->GetDownsampledRenderBuffer(), capture);
}
// Verify that no lag estimates are reliable.
for (auto& le : lag_estimates) {
EXPECT_FALSE(le.reliable);
// Obtain the lag estimates.
auto lag_estimates = filter.GetLagEstimates();
EXPECT_EQ(kNumMatchedFilters, lag_estimates.size());
// Verify that no lag estimates are reliable.
for (auto& le : lag_estimates) {
EXPECT_FALSE(le.reliable);
}
}
}
@ -230,39 +278,50 @@ TEST(MatchedFilter, LagNotReliableForUncorrelatedRenderAndCapture) {
// render signals of low level.
TEST(MatchedFilter, LagNotUpdatedForLowLevelRender) {
Random random_generator(42U);
std::vector<std::vector<float>> render(3,
std::vector<float>(kBlockSize, 0.f));
std::array<float, kBlockSize> capture;
capture.fill(0.f);
ApmDataDumper data_dumper(0);
MatchedFilter filter(&data_dumper, DetectOptimization(), kWindowSizeSubBlocks,
kNumMatchedFilters, kAlignmentShiftSubBlocks, 150);
std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
RenderDelayBuffer::Create(3));
DecimatorBy4 capture_decimator;
for (auto down_sampling_factor : kDownSamplingFactors) {
const size_t sub_block_size = kBlockSize / down_sampling_factor;
// Analyze the correlation between render and capture.
for (size_t k = 0; k < 100; ++k) {
RandomizeSampleVector(&random_generator, render[0]);
for (auto& render_k : render[0]) {
render_k *= 149.f / 32767.f;
std::vector<std::vector<float>> render(3,
std::vector<float>(kBlockSize, 0.f));
std::array<float, kBlockSize> capture;
capture.fill(0.f);
ApmDataDumper data_dumper(0);
MatchedFilter filter(&data_dumper, DetectOptimization(), sub_block_size,
kWindowSizeSubBlocks, kNumMatchedFilters,
kAlignmentShiftSubBlocks, 150);
std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
RenderDelayBuffer::Create(
3, down_sampling_factor,
GetDownSampledBufferSize(down_sampling_factor, kNumMatchedFilters),
GetRenderDelayBufferSize(down_sampling_factor,
kNumMatchedFilters)));
Decimator capture_decimator(down_sampling_factor);
// Analyze the correlation between render and capture.
for (size_t k = 0; k < 100; ++k) {
RandomizeSampleVector(&random_generator, render[0]);
for (auto& render_k : render[0]) {
render_k *= 149.f / 32767.f;
}
std::copy(render[0].begin(), render[0].end(), capture.begin());
std::array<float, kBlockSize> downsampled_capture_data;
rtc::ArrayView<float> downsampled_capture(downsampled_capture_data.data(),
sub_block_size);
capture_decimator.Decimate(capture, downsampled_capture);
filter.Update(render_delay_buffer->GetDownsampledRenderBuffer(),
downsampled_capture);
}
std::copy(render[0].begin(), render[0].end(), capture.begin());
std::array<float, kSubBlockSize> downsampled_capture;
capture_decimator.Decimate(capture, downsampled_capture);
filter.Update(render_delay_buffer->GetDownsampledRenderBuffer(),
downsampled_capture);
}
// Obtain the lag estimates.
auto lag_estimates = filter.GetLagEstimates();
EXPECT_EQ(kNumMatchedFilters, lag_estimates.size());
// Obtain the lag estimates.
auto lag_estimates = filter.GetLagEstimates();
EXPECT_EQ(kNumMatchedFilters, lag_estimates.size());
// Verify that no lag estimates are updated and that no lag estimates are
// reliable.
for (auto& le : lag_estimates) {
EXPECT_FALSE(le.updated);
EXPECT_FALSE(le.reliable);
// Verify that no lag estimates are updated and that no lag estimates are
// reliable.
for (auto& le : lag_estimates) {
EXPECT_FALSE(le.updated);
EXPECT_FALSE(le.reliable);
}
}
}
@ -270,11 +329,14 @@ TEST(MatchedFilter, LagNotUpdatedForLowLevelRender) {
// number of alignment shifts.
TEST(MatchedFilter, NumberOfLagEstimates) {
ApmDataDumper data_dumper(0);
for (size_t num_matched_filters = 0; num_matched_filters < 10;
++num_matched_filters) {
MatchedFilter filter(&data_dumper, DetectOptimization(), 32,
num_matched_filters, 1, 150);
EXPECT_EQ(num_matched_filters, filter.GetLagEstimates().size());
for (auto down_sampling_factor : kDownSamplingFactors) {
const size_t sub_block_size = kBlockSize / down_sampling_factor;
for (size_t num_matched_filters = 0; num_matched_filters < 10;
++num_matched_filters) {
MatchedFilter filter(&data_dumper, DetectOptimization(), sub_block_size,
32, num_matched_filters, 1, 150);
EXPECT_EQ(num_matched_filters, filter.GetLagEstimates().size());
}
}
}
@ -283,13 +345,31 @@ TEST(MatchedFilter, NumberOfLagEstimates) {
// Verifies the check for non-zero windows size.
TEST(MatchedFilter, ZeroWindowSize) {
ApmDataDumper data_dumper(0);
EXPECT_DEATH(MatchedFilter(&data_dumper, DetectOptimization(), 0, 1, 1, 150),
"");
EXPECT_DEATH(
MatchedFilter(&data_dumper, DetectOptimization(), 16, 0, 1, 1, 150), "");
}
// Verifies the check for non-null data dumper.
TEST(MatchedFilter, NullDataDumper) {
EXPECT_DEATH(MatchedFilter(nullptr, DetectOptimization(), 1, 1, 1, 150), "");
EXPECT_DEATH(MatchedFilter(nullptr, DetectOptimization(), 16, 1, 1, 1, 150),
"");
}
// Verifies the check for that the sub block size is a multiple of 4.
// TODO(peah): Activate the unittest once the required code has been landed.
TEST(MatchedFilter, DISABLED_BlockSizeMultipleOf4) {
ApmDataDumper data_dumper(0);
EXPECT_DEATH(
MatchedFilter(&data_dumper, DetectOptimization(), 15, 1, 1, 1, 150), "");
}
// Verifies the check for that there is an integer number of sub blocks that add
// up to a block size.
// TODO(peah): Activate the unittest once the required code has been landed.
TEST(MatchedFilter, DISABLED_SubBlockSizeAddsUpToBlockSize) {
ApmDataDumper data_dumper(0);
EXPECT_DEATH(
MatchedFilter(&data_dumper, DetectOptimization(), 12, 1, 1, 1, 150), "");
}
#endif

View File

@ -27,8 +27,9 @@ class MockRenderDelayBuffer : public RenderDelayBuffer {
explicit MockRenderDelayBuffer(int sample_rate_hz)
: render_buffer_(Aec3Optimization::kNone,
NumBandsForRate(sample_rate_hz),
kRenderDelayBufferSize,
std::vector<size_t>(1, kAdaptiveFilterLength)) {
GetRenderDelayBufferSize(4, 4),
std::vector<size_t>(1, kAdaptiveFilterLength)),
downsampled_render_buffer_(GetDownSampledBufferSize(4, 4)) {
ON_CALL(*this, GetRenderBuffer())
.WillByDefault(
testing::Invoke(this, &MockRenderDelayBuffer::FakeGetRenderBuffer));

View File

@ -15,8 +15,9 @@
#include "modules/audio_processing/aec3/aec3_common.h"
#include "modules/audio_processing/aec3/block_processor.h"
#include "modules/audio_processing/aec3/decimator_by_4.h"
#include "modules/audio_processing/aec3/decimator.h"
#include "modules/audio_processing/aec3/fft_data.h"
#include "rtc_base/atomicops.h"
#include "rtc_base/checks.h"
#include "rtc_base/constructormagic.h"
#include "rtc_base/logging.h"
@ -73,7 +74,10 @@ class ApiCallJitterBuffer {
class RenderDelayBufferImpl final : public RenderDelayBuffer {
public:
explicit RenderDelayBufferImpl(size_t num_bands);
RenderDelayBufferImpl(size_t num_bands,
size_t down_sampling_factor,
size_t downsampled_render_buffer_size,
size_t render_delay_buffer_size);
~RenderDelayBufferImpl() override;
void Reset() override;
@ -89,30 +93,49 @@ class RenderDelayBufferImpl final : public RenderDelayBuffer {
}
private:
static int instance_count_;
std::unique_ptr<ApmDataDumper> data_dumper_;
const Aec3Optimization optimization_;
std::array<std::vector<std::vector<float>>, kRenderDelayBufferSize> buffer_;
const size_t down_sampling_factor_;
const size_t sub_block_size_;
std::vector<std::vector<std::vector<float>>> buffer_;
size_t delay_ = 0;
size_t last_insert_index_ = 0;
RenderBuffer fft_buffer_;
DownsampledRenderBuffer downsampled_render_buffer_;
DecimatorBy4 render_decimator_;
Decimator render_decimator_;
ApiCallJitterBuffer api_call_jitter_buffer_;
const std::vector<std::vector<float>> zero_block_;
RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(RenderDelayBufferImpl);
};
RenderDelayBufferImpl::RenderDelayBufferImpl(size_t num_bands)
: optimization_(DetectOptimization()),
int RenderDelayBufferImpl::instance_count_ = 0;
RenderDelayBufferImpl::RenderDelayBufferImpl(
size_t num_bands,
size_t down_sampling_factor,
size_t downsampled_render_buffer_size,
size_t render_delay_buffer_size)
: data_dumper_(
new ApmDataDumper(rtc::AtomicOps::Increment(&instance_count_))),
optimization_(DetectOptimization()),
down_sampling_factor_(down_sampling_factor),
sub_block_size_(down_sampling_factor_ > 0
? kBlockSize / down_sampling_factor
: kBlockSize),
buffer_(
render_delay_buffer_size,
std::vector<std::vector<float>>(num_bands,
std::vector<float>(kBlockSize, 0.f))),
fft_buffer_(
optimization_,
num_bands,
std::max(kUnknownDelayRenderWindowSize, kAdaptiveFilterLength),
std::vector<size_t>(1, kAdaptiveFilterLength)),
downsampled_render_buffer_(downsampled_render_buffer_size),
render_decimator_(down_sampling_factor_),
api_call_jitter_buffer_(num_bands),
zero_block_(num_bands, std::vector<float>(kBlockSize, 0.f)) {
buffer_.fill(std::vector<std::vector<float>>(
num_bands, std::vector<float>(kBlockSize, 0.f)));
RTC_DCHECK_LT(buffer_.size(), downsampled_render_buffer_.buffer.size());
}
@ -123,7 +146,8 @@ void RenderDelayBufferImpl::Reset() {
delay_ = 0;
last_insert_index_ = 0;
downsampled_render_buffer_.position = 0;
downsampled_render_buffer_.buffer.fill(0.f);
std::fill(downsampled_render_buffer_.buffer.begin(),
downsampled_render_buffer_.buffer.end(), 0.f);
fft_buffer_.Clear();
api_call_jitter_buffer_.Reset();
for (auto& c : buffer_) {
@ -158,20 +182,26 @@ bool RenderDelayBufferImpl::UpdateBuffers() {
}
downsampled_render_buffer_.position =
(downsampled_render_buffer_.position - kSubBlockSize +
(downsampled_render_buffer_.position - sub_block_size_ +
downsampled_render_buffer_.buffer.size()) %
downsampled_render_buffer_.buffer.size();
std::array<float, kSubBlockSize> render_downsampled;
if (underrun) {
render_decimator_.Decimate(zero_block_[0], render_downsampled);
} else {
render_decimator_.Decimate(buffer_[last_insert_index_][0],
render_downsampled);
rtc::ArrayView<const float> input(
underrun ? zero_block_[0].data() : buffer_[last_insert_index_][0].data(),
kBlockSize);
rtc::ArrayView<float> output(downsampled_render_buffer_.buffer.data() +
downsampled_render_buffer_.position,
sub_block_size_);
data_dumper_->DumpWav("aec3_render_decimator_input", input.size(),
input.data(), 16000, 1);
render_decimator_.Decimate(input, output);
data_dumper_->DumpWav("aec3_render_decimator_output", output.size(),
output.data(), 16000 / down_sampling_factor_, 1);
for (size_t k = 0; k < output.size() / 2; ++k) {
float tmp = output[k];
output[k] = output[output.size() - 1 - k];
output[output.size() - 1 - k] = tmp;
}
std::copy(render_downsampled.rbegin(), render_downsampled.rend(),
downsampled_render_buffer_.buffer.begin() +
downsampled_render_buffer_.position);
if (underrun) {
fft_buffer_.Insert(zero_block_);
@ -196,7 +226,7 @@ void RenderDelayBufferImpl::SetDelay(size_t delay) {
// size.
downsampled_render_buffer_.position =
(downsampled_render_buffer_.position +
kSubBlockSize * (delay - (buffer_.size() - 1))) %
sub_block_size_ * (delay - (buffer_.size() - 1))) %
downsampled_render_buffer_.buffer.size();
last_insert_index_ =
@ -210,8 +240,14 @@ void RenderDelayBufferImpl::SetDelay(size_t delay) {
} // namespace
RenderDelayBuffer* RenderDelayBuffer::Create(size_t num_bands) {
return new RenderDelayBufferImpl(num_bands);
RenderDelayBuffer* RenderDelayBuffer::Create(
size_t num_bands,
size_t down_sampling_factor,
size_t downsampled_render_buffer_size,
size_t render_delay_buffer_size) {
return new RenderDelayBufferImpl(num_bands, down_sampling_factor,
downsampled_render_buffer_size,
render_delay_buffer_size);
}
} // namespace webrtc

View File

@ -27,7 +27,10 @@ namespace webrtc {
// extracted with a specified delay.
class RenderDelayBuffer {
public:
static RenderDelayBuffer* Create(size_t num_bands);
static RenderDelayBuffer* Create(size_t num_bands,
size_t down_sampling_factor,
size_t downsampled_render_buffer_size,
size_t render_delay_buffer_size);
virtual ~RenderDelayBuffer() = default;
// Resets the buffer data.

View File

@ -30,14 +30,19 @@ std::string ProduceDebugText(int sample_rate_hz) {
return ss.str();
}
constexpr size_t kDownSamplingFactor = 4;
constexpr size_t kNumMatchedFilters = 4;
} // namespace
// Verifies that the buffer overflow is correctly reported.
TEST(RenderDelayBuffer, BufferOverflow) {
for (auto rate : {8000, 16000, 32000, 48000}) {
SCOPED_TRACE(ProduceDebugText(rate));
std::unique_ptr<RenderDelayBuffer> delay_buffer(
RenderDelayBuffer::Create(NumBandsForRate(rate)));
std::unique_ptr<RenderDelayBuffer> delay_buffer(RenderDelayBuffer::Create(
NumBandsForRate(rate), kDownSamplingFactor,
GetDownSampledBufferSize(kDownSamplingFactor, kNumMatchedFilters),
GetRenderDelayBufferSize(kDownSamplingFactor, kNumMatchedFilters)));
std::vector<std::vector<float>> block_to_insert(
NumBandsForRate(rate), std::vector<float>(kBlockSize, 0.f));
for (size_t k = 0; k < kMaxApiCallsJitterBlocks; ++k) {
@ -50,8 +55,10 @@ TEST(RenderDelayBuffer, BufferOverflow) {
// Verifies that the check for available block works.
TEST(RenderDelayBuffer, AvailableBlock) {
constexpr size_t kNumBands = 1;
std::unique_ptr<RenderDelayBuffer> delay_buffer(
RenderDelayBuffer::Create(kNumBands));
std::unique_ptr<RenderDelayBuffer> delay_buffer(RenderDelayBuffer::Create(
kNumBands, kDownSamplingFactor,
GetDownSampledBufferSize(kDownSamplingFactor, kNumMatchedFilters),
GetRenderDelayBufferSize(kDownSamplingFactor, kNumMatchedFilters)));
std::vector<std::vector<float>> input_block(
kNumBands, std::vector<float>(kBlockSize, 1.f));
EXPECT_TRUE(delay_buffer->Insert(input_block));
@ -60,7 +67,10 @@ TEST(RenderDelayBuffer, AvailableBlock) {
// Verifies the SetDelay method.
TEST(RenderDelayBuffer, SetDelay) {
std::unique_ptr<RenderDelayBuffer> delay_buffer(RenderDelayBuffer::Create(1));
std::unique_ptr<RenderDelayBuffer> delay_buffer(RenderDelayBuffer::Create(
1, kDownSamplingFactor,
GetDownSampledBufferSize(kDownSamplingFactor, kNumMatchedFilters),
GetRenderDelayBufferSize(kDownSamplingFactor, kNumMatchedFilters)));
EXPECT_EQ(0u, delay_buffer->Delay());
for (size_t delay = 0; delay < 20; ++delay) {
delay_buffer->SetDelay(delay);
@ -74,7 +84,10 @@ TEST(RenderDelayBuffer, SetDelay) {
// TODO(peah): Re-enable the test once the issue with memory leaks during DEATH
// tests on test bots has been fixed.
TEST(RenderDelayBuffer, DISABLED_WrongDelay) {
std::unique_ptr<RenderDelayBuffer> delay_buffer(RenderDelayBuffer::Create(3));
std::unique_ptr<RenderDelayBuffer> delay_buffer(RenderDelayBuffer::Create(
3, kDownSamplingFactor,
GetDownSampledBufferSize(kDownSamplingFactor, kNumMatchedFilters),
GetRenderDelayBufferSize(kDownSamplingFactor, kNumMatchedFilters)));
EXPECT_DEATH(delay_buffer->SetDelay(21), "");
}
@ -82,8 +95,10 @@ TEST(RenderDelayBuffer, DISABLED_WrongDelay) {
TEST(RenderDelayBuffer, WrongNumberOfBands) {
for (auto rate : {16000, 32000, 48000}) {
SCOPED_TRACE(ProduceDebugText(rate));
std::unique_ptr<RenderDelayBuffer> delay_buffer(
RenderDelayBuffer::Create(NumBandsForRate(rate)));
std::unique_ptr<RenderDelayBuffer> delay_buffer(RenderDelayBuffer::Create(
NumBandsForRate(rate), kDownSamplingFactor,
GetDownSampledBufferSize(kDownSamplingFactor, kNumMatchedFilters),
GetRenderDelayBufferSize(kDownSamplingFactor, kNumMatchedFilters)));
std::vector<std::vector<float>> block_to_insert(
NumBandsForRate(rate < 48000 ? rate + 16000 : 16000),
std::vector<float>(kBlockSize, 0.f));
@ -95,8 +110,10 @@ TEST(RenderDelayBuffer, WrongNumberOfBands) {
TEST(RenderDelayBuffer, WrongBlockLength) {
for (auto rate : {8000, 16000, 32000, 48000}) {
SCOPED_TRACE(ProduceDebugText(rate));
std::unique_ptr<RenderDelayBuffer> delay_buffer(
RenderDelayBuffer::Create(3));
std::unique_ptr<RenderDelayBuffer> delay_buffer(RenderDelayBuffer::Create(
3, kDownSamplingFactor,
GetDownSampledBufferSize(kDownSamplingFactor, kNumMatchedFilters),
GetRenderDelayBufferSize(kDownSamplingFactor, kNumMatchedFilters)));
std::vector<std::vector<float>> block_to_insert(
NumBandsForRate(rate), std::vector<float>(kBlockSize - 1, 0.f));
EXPECT_DEATH(delay_buffer->Insert(block_to_insert), "");

View File

@ -43,7 +43,6 @@ class RenderDelayControllerImpl final : public RenderDelayController {
std::unique_ptr<ApmDataDumper> data_dumper_;
const size_t default_delay_;
size_t delay_;
EchoPathDelayEstimator delay_estimator_;
size_t blocks_since_last_delay_estimate_ = 300000;
int echo_path_delay_samples_;
size_t align_call_counter_ = 0;
@ -51,6 +50,7 @@ class RenderDelayControllerImpl final : public RenderDelayController {
std::vector<float> capture_delay_buffer_;
int capture_delay_buffer_index_ = 0;
RenderDelayControllerMetrics metrics_;
EchoPathDelayEstimator delay_estimator_;
RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(RenderDelayControllerImpl);
};
@ -81,10 +81,12 @@ RenderDelayControllerImpl::RenderDelayControllerImpl(
default_delay_(
std::max(config.delay.default_delay, kMinEchoPathDelayBlocks)),
delay_(default_delay_),
delay_estimator_(data_dumper_.get(), config),
echo_path_delay_samples_(default_delay_ * kBlockSize),
capture_delay_buffer_(kBlockSize * (kMaxApiCallsJitterBlocks + 2), 0.f) {
capture_delay_buffer_(kBlockSize * (kMaxApiCallsJitterBlocks + 2), 0.f),
delay_estimator_(data_dumper_.get(), config) {
RTC_DCHECK(ValidFullBandRate(sample_rate_hz));
delay_estimator_.LogDelayEstimationProperties(sample_rate_hz,
capture_delay_buffer_.size());
}
RenderDelayControllerImpl::~RenderDelayControllerImpl() = default;

View File

@ -18,7 +18,7 @@
#include "modules/audio_processing/aec3/aec3_common.h"
#include "modules/audio_processing/aec3/block_processor.h"
#include "modules/audio_processing/aec3/decimator_by_4.h"
#include "modules/audio_processing/aec3/decimator.h"
#include "modules/audio_processing/aec3/render_delay_buffer.h"
#include "modules/audio_processing/logging/apm_data_dumper.h"
#include "modules/audio_processing/test/echo_canceller_test_tools.h"
@ -40,21 +40,33 @@ std::string ProduceDebugText(int sample_rate_hz, size_t delay) {
return ss.str();
}
constexpr size_t kDownSamplingFactors[] = {2, 4, 8};
} // namespace
// Verifies the output of GetDelay when there are no AnalyzeRender calls.
TEST(RenderDelayController, NoRenderSignal) {
std::vector<float> block(kBlockSize, 0.f);
for (auto rate : {8000, 16000, 32000, 48000}) {
SCOPED_TRACE(ProduceDebugText(rate));
std::unique_ptr<RenderDelayBuffer> delay_buffer(
RenderDelayBuffer::Create(NumBandsForRate(rate)));
std::unique_ptr<RenderDelayController> delay_controller(
RenderDelayController::Create(EchoCanceller3Config(), rate));
for (size_t k = 0; k < 100; ++k) {
EXPECT_EQ(kMinEchoPathDelayBlocks,
delay_controller->GetDelay(
delay_buffer->GetDownsampledRenderBuffer(), block));
for (size_t num_matched_filters = 4; num_matched_filters == 10;
num_matched_filters++) {
for (auto down_sampling_factor : kDownSamplingFactors) {
for (auto rate : {8000, 16000, 32000, 48000}) {
SCOPED_TRACE(ProduceDebugText(rate));
std::unique_ptr<RenderDelayBuffer> delay_buffer(
RenderDelayBuffer::Create(
NumBandsForRate(rate), down_sampling_factor,
GetDownSampledBufferSize(down_sampling_factor,
num_matched_filters),
GetRenderDelayBufferSize(down_sampling_factor,
num_matched_filters)));
std::unique_ptr<RenderDelayController> delay_controller(
RenderDelayController::Create(EchoCanceller3Config(), rate));
for (size_t k = 0; k < 100; ++k) {
EXPECT_EQ(kMinEchoPathDelayBlocks,
delay_controller->GetDelay(
delay_buffer->GetDownsampledRenderBuffer(), block));
}
}
}
}
}
@ -63,21 +75,31 @@ TEST(RenderDelayController, NoRenderSignal) {
TEST(RenderDelayController, BasicApiCalls) {
std::vector<float> capture_block(kBlockSize, 0.f);
size_t delay_blocks = 0;
for (auto rate : {8000, 16000, 32000, 48000}) {
std::vector<std::vector<float>> render_block(
NumBandsForRate(rate), std::vector<float>(kBlockSize, 0.f));
std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
RenderDelayBuffer::Create(NumBandsForRate(rate)));
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->UpdateBuffers();
delay_blocks = delay_controller->GetDelay(
render_delay_buffer->GetDownsampledRenderBuffer(), capture_block);
for (size_t num_matched_filters = 4; num_matched_filters == 10;
num_matched_filters++) {
for (auto down_sampling_factor : kDownSamplingFactors) {
for (auto rate : {8000, 16000, 32000, 48000}) {
std::vector<std::vector<float>> render_block(
NumBandsForRate(rate), std::vector<float>(kBlockSize, 0.f));
std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
RenderDelayBuffer::Create(
NumBandsForRate(rate), down_sampling_factor,
GetDownSampledBufferSize(down_sampling_factor,
num_matched_filters),
GetRenderDelayBufferSize(down_sampling_factor,
num_matched_filters)));
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->UpdateBuffers();
delay_blocks = delay_controller->GetDelay(
render_delay_buffer->GetDownsampledRenderBuffer(), capture_block);
}
EXPECT_FALSE(delay_controller->AlignmentHeadroomSamples());
EXPECT_EQ(kMinEchoPathDelayBlocks, delay_blocks);
}
}
EXPECT_FALSE(delay_controller->AlignmentHeadroomSamples());
EXPECT_EQ(kMinEchoPathDelayBlocks, delay_blocks);
}
}
@ -87,38 +109,49 @@ TEST(RenderDelayController, Alignment) {
Random random_generator(42U);
std::vector<float> capture_block(kBlockSize, 0.f);
size_t delay_blocks = 0;
for (auto rate : {8000, 16000, 32000, 48000}) {
std::vector<std::vector<float>> render_block(
NumBandsForRate(rate), 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) {
for (auto rate : {8000, 16000, 32000, 48000}) {
std::vector<std::vector<float>> render_block(
NumBandsForRate(rate), std::vector<float>(kBlockSize, 0.f));
for (size_t delay_samples : {15, 50, 150, 200, 800, 4000}) {
SCOPED_TRACE(ProduceDebugText(rate, delay_samples));
std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
RenderDelayBuffer::Create(NumBandsForRate(rate)));
std::unique_ptr<RenderDelayController> delay_controller(
RenderDelayController::Create(EchoCanceller3Config(), rate));
DelayBuffer<float> signal_delay_buffer(delay_samples);
for (size_t k = 0; k < (400 + delay_samples / kBlockSize); ++k) {
RandomizeSampleVector(&random_generator, render_block[0]);
signal_delay_buffer.Delay(render_block[0], capture_block);
render_delay_buffer->Insert(render_block);
render_delay_buffer->UpdateBuffers();
delay_blocks = delay_controller->GetDelay(
render_delay_buffer->GetDownsampledRenderBuffer(), capture_block);
for (size_t delay_samples : {15, 50, 150, 200, 800, 4000}) {
SCOPED_TRACE(ProduceDebugText(rate, delay_samples));
std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
RenderDelayBuffer::Create(
NumBandsForRate(rate), down_sampling_factor,
GetDownSampledBufferSize(down_sampling_factor,
num_matched_filters),
GetRenderDelayBufferSize(down_sampling_factor,
num_matched_filters)));
std::unique_ptr<RenderDelayController> delay_controller(
RenderDelayController::Create(EchoCanceller3Config(), rate));
DelayBuffer<float> signal_delay_buffer(delay_samples);
for (size_t k = 0; k < (400 + delay_samples / kBlockSize); ++k) {
RandomizeSampleVector(&random_generator, render_block[0]);
signal_delay_buffer.Delay(render_block[0], capture_block);
render_delay_buffer->Insert(render_block);
render_delay_buffer->UpdateBuffers();
delay_blocks = delay_controller->GetDelay(
render_delay_buffer->GetDownsampledRenderBuffer(),
capture_block);
}
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);
const rtc::Optional<size_t> headroom_samples =
delay_controller->AlignmentHeadroomSamples();
ASSERT_TRUE(headroom_samples);
EXPECT_NEAR(delay_samples - delay_blocks * kBlockSize,
*headroom_samples, 4);
}
}
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);
const rtc::Optional<size_t> headroom_samples =
delay_controller->AlignmentHeadroomSamples();
ASSERT_TRUE(headroom_samples);
EXPECT_NEAR(delay_samples - delay_blocks * kBlockSize, *headroom_samples,
4);
}
}
}
@ -128,35 +161,45 @@ TEST(RenderDelayController, Alignment) {
TEST(RenderDelayController, NonCausalAlignment) {
Random random_generator(42U);
size_t delay_blocks = 0;
for (auto rate : {8000, 16000, 32000, 48000}) {
std::vector<std::vector<float>> render_block(
NumBandsForRate(rate), std::vector<float>(kBlockSize, 0.f));
std::vector<std::vector<float>> capture_block(
NumBandsForRate(rate), 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) {
for (auto rate : {8000, 16000, 32000, 48000}) {
std::vector<std::vector<float>> render_block(
NumBandsForRate(rate), std::vector<float>(kBlockSize, 0.f));
std::vector<std::vector<float>> capture_block(
NumBandsForRate(rate), std::vector<float>(kBlockSize, 0.f));
for (int delay_samples : {-15, -50, -150, -200}) {
SCOPED_TRACE(ProduceDebugText(rate, -delay_samples));
std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
RenderDelayBuffer::Create(NumBandsForRate(rate)));
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]);
signal_delay_buffer.Delay(capture_block[0], render_block[0]);
render_delay_buffer->Insert(render_block);
render_delay_buffer->UpdateBuffers();
delay_blocks = delay_controller->GetDelay(
render_delay_buffer->GetDownsampledRenderBuffer(),
capture_block[0]);
for (int delay_samples : {-15, -50, -150, -200}) {
SCOPED_TRACE(ProduceDebugText(rate, -delay_samples));
std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
RenderDelayBuffer::Create(
NumBandsForRate(rate), down_sampling_factor,
GetDownSampledBufferSize(down_sampling_factor,
num_matched_filters),
GetRenderDelayBufferSize(down_sampling_factor,
num_matched_filters)));
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]);
signal_delay_buffer.Delay(capture_block[0], render_block[0]);
render_delay_buffer->Insert(render_block);
render_delay_buffer->UpdateBuffers();
delay_blocks = delay_controller->GetDelay(
render_delay_buffer->GetDownsampledRenderBuffer(),
capture_block[0]);
}
EXPECT_EQ(0u, delay_blocks);
const rtc::Optional<size_t> headroom_samples =
delay_controller->AlignmentHeadroomSamples();
ASSERT_FALSE(headroom_samples);
}
}
EXPECT_EQ(0u, delay_blocks);
const rtc::Optional<size_t> headroom_samples =
delay_controller->AlignmentHeadroomSamples();
ASSERT_FALSE(headroom_samples);
}
}
}
@ -166,66 +209,86 @@ TEST(RenderDelayController, NonCausalAlignment) {
TEST(RenderDelayController, AlignmentWithJitter) {
Random random_generator(42U);
std::vector<float> capture_block(kBlockSize, 0.f);
for (auto rate : {8000, 16000, 32000, 48000}) {
std::vector<std::vector<float>> render_block(
NumBandsForRate(rate), std::vector<float>(kBlockSize, 0.f));
for (size_t delay_samples : {15, 50, 300, 800}) {
size_t delay_blocks = 0;
SCOPED_TRACE(ProduceDebugText(rate, delay_samples));
std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
RenderDelayBuffer::Create(NumBandsForRate(rate)));
std::unique_ptr<RenderDelayController> delay_controller(
RenderDelayController::Create(EchoCanceller3Config(), rate));
DelayBuffer<float> signal_delay_buffer(delay_samples);
for (size_t j = 0;
j <
(1000 + delay_samples / kBlockSize) / kMaxApiCallsJitterBlocks + 1;
++j) {
std::vector<std::vector<float>> capture_block_buffer;
for (size_t k = 0; k < (kMaxApiCallsJitterBlocks - 1); ++k) {
RandomizeSampleVector(&random_generator, render_block[0]);
signal_delay_buffer.Delay(render_block[0], capture_block);
capture_block_buffer.push_back(capture_block);
render_delay_buffer->Insert(render_block);
for (size_t num_matched_filters = 4; num_matched_filters == 10;
num_matched_filters++) {
for (auto down_sampling_factor : kDownSamplingFactors) {
for (auto rate : {8000, 16000, 32000, 48000}) {
std::vector<std::vector<float>> render_block(
NumBandsForRate(rate), std::vector<float>(kBlockSize, 0.f));
for (size_t delay_samples : {15, 50, 300, 800}) {
size_t delay_blocks = 0;
SCOPED_TRACE(ProduceDebugText(rate, delay_samples));
std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
RenderDelayBuffer::Create(
NumBandsForRate(rate), down_sampling_factor,
GetDownSampledBufferSize(down_sampling_factor,
num_matched_filters),
GetRenderDelayBufferSize(down_sampling_factor,
num_matched_filters)));
std::unique_ptr<RenderDelayController> delay_controller(
RenderDelayController::Create(EchoCanceller3Config(), rate));
DelayBuffer<float> signal_delay_buffer(delay_samples);
for (size_t j = 0; j < (1000 + delay_samples / kBlockSize) /
kMaxApiCallsJitterBlocks +
1;
++j) {
std::vector<std::vector<float>> capture_block_buffer;
for (size_t k = 0; k < (kMaxApiCallsJitterBlocks - 1); ++k) {
RandomizeSampleVector(&random_generator, render_block[0]);
signal_delay_buffer.Delay(render_block[0], capture_block);
capture_block_buffer.push_back(capture_block);
render_delay_buffer->Insert(render_block);
}
for (size_t k = 0; k < (kMaxApiCallsJitterBlocks - 1); ++k) {
render_delay_buffer->UpdateBuffers();
delay_blocks = delay_controller->GetDelay(
render_delay_buffer->GetDownsampledRenderBuffer(),
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;
}
EXPECT_EQ(expected_delay_blocks, delay_blocks);
const rtc::Optional<size_t> headroom_samples =
delay_controller->AlignmentHeadroomSamples();
ASSERT_TRUE(headroom_samples);
EXPECT_NEAR(delay_samples - delay_blocks * kBlockSize,
*headroom_samples, 4);
}
for (size_t k = 0; k < (kMaxApiCallsJitterBlocks - 1); ++k) {
render_delay_buffer->UpdateBuffers();
delay_blocks = delay_controller->GetDelay(
render_delay_buffer->GetDownsampledRenderBuffer(),
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;
}
EXPECT_EQ(expected_delay_blocks, delay_blocks);
const rtc::Optional<size_t> headroom_samples =
delay_controller->AlignmentHeadroomSamples();
ASSERT_TRUE(headroom_samples);
EXPECT_NEAR(delay_samples - delay_blocks * kBlockSize, *headroom_samples,
4);
}
}
}
}
// 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 (auto rate : {8000, 16000, 32000, 48000}) {
SCOPED_TRACE(ProduceDebugText(rate));
std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
RenderDelayBuffer::Create(NumBandsForRate(rate)));
std::unique_ptr<RenderDelayController> delay_controller(
RenderDelayController::Create(EchoCanceller3Config(), rate));
EXPECT_FALSE(delay_controller->AlignmentHeadroomSamples());
for (size_t num_matched_filters = 4; num_matched_filters == 10;
num_matched_filters++) {
for (auto down_sampling_factor : kDownSamplingFactors) {
for (auto rate : {8000, 16000, 32000, 48000}) {
SCOPED_TRACE(ProduceDebugText(rate));
std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
RenderDelayBuffer::Create(
NumBandsForRate(rate), down_sampling_factor,
GetDownSampledBufferSize(down_sampling_factor,
num_matched_filters),
GetRenderDelayBufferSize(down_sampling_factor,
num_matched_filters)));
std::unique_ptr<RenderDelayController> delay_controller(
RenderDelayController::Create(EchoCanceller3Config(), rate));
EXPECT_FALSE(delay_controller->AlignmentHeadroomSamples());
}
}
}
}
@ -237,7 +300,9 @@ TEST(RenderDelayController, WrongCaptureSize) {
for (auto rate : {8000, 16000, 32000, 48000}) {
SCOPED_TRACE(ProduceDebugText(rate));
std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
RenderDelayBuffer::Create(NumBandsForRate(rate)));
RenderDelayBuffer::Create(NumBandsForRate(rate), 4,
GetDownSampledBufferSize(4, 4),
GetRenderDelayBufferSize(4, 4)));
EXPECT_DEATH(
std::unique_ptr<RenderDelayController>(
RenderDelayController::Create(EchoCanceller3Config(), rate))
@ -254,7 +319,9 @@ TEST(RenderDelayController, DISABLED_WrongSampleRate) {
for (auto rate : {-1, 0, 8001, 16001}) {
SCOPED_TRACE(ProduceDebugText(rate));
std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
RenderDelayBuffer::Create(NumBandsForRate(rate)));
RenderDelayBuffer::Create(NumBandsForRate(rate), 4,
GetDownSampledBufferSize(4, 4),
GetRenderDelayBufferSize(4, 4)));
EXPECT_DEATH(
std::unique_ptr<RenderDelayController>(
RenderDelayController::Create(EchoCanceller3Config(), rate)),

View File

@ -93,7 +93,7 @@ float UpperBandsGain(
// or if the power in upper frequencies is low, do not bound the gain in the
// upper bands.
float anti_howling_gain;
constexpr float kThreshold = kSubBlockSize * 10.f * 10.f;
constexpr float kThreshold = kBlockSize * 10.f * 10.f / 4.f;
if (high_band_energy < std::max(low_band_energy, kThreshold)) {
anti_howling_gain = 1.f;
} else {

View File

@ -1137,6 +1137,8 @@ class VoiceDetection {
struct EchoCanceller3Config {
struct Delay {
size_t default_delay = 5;
size_t down_sampling_factor = 4;
size_t num_filters = 4;
} delay;
struct Erle {