AEC3: Adding the option for applying a fixed delay to the capture signal

This CL adds functionality for applying an optional fixed delay in AEC3
to the capture signal

Bug: webrtc:9647
Change-Id: Id3b3f896bcf203e6611298dc804c3c80da9f1883
Reviewed-on: https://webrtc-review.googlesource.com/95142
Commit-Queue: Per Åhgren <peah@webrtc.org>
Reviewed-by: Gustaf Ullberg <gustaf@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#24399}
This commit is contained in:
Per Åhgren 2018-08-23 11:38:27 +02:00 committed by Commit Bot
parent 404be7f302
commit 398689f581
9 changed files with 201 additions and 1 deletions

View File

@ -14,6 +14,10 @@ namespace webrtc {
EchoCanceller3Config::EchoCanceller3Config() = default;
EchoCanceller3Config::EchoCanceller3Config(const EchoCanceller3Config& e) =
default;
EchoCanceller3Config::Delay::Delay() = default;
EchoCanceller3Config::Delay::Delay(const EchoCanceller3Config::Delay& e) =
default;
EchoCanceller3Config::Mask::Mask() = default;
EchoCanceller3Config::Mask::Mask(const EchoCanceller3Config::Mask& m) = default;

View File

@ -20,6 +20,8 @@ struct EchoCanceller3Config {
EchoCanceller3Config();
EchoCanceller3Config(const EchoCanceller3Config& e);
struct Delay {
Delay();
Delay(const Delay& e);
size_t default_delay = 5;
size_t down_sampling_factor = 4;
size_t num_filters = 6;
@ -29,6 +31,7 @@ struct EchoCanceller3Config {
size_t hysteresis_limit_1_blocks = 1;
size_t hysteresis_limit_2_blocks = 1;
size_t skew_hysteresis_blocks = 3;
size_t fixed_capture_delay_samples = 0;
} delay;
struct Filter {

View File

@ -20,6 +20,8 @@ rtc_static_library("aec3") {
"aec3_fft.h",
"aec_state.cc",
"aec_state.h",
"block_delay_buffer.cc",
"block_delay_buffer.h",
"block_framer.cc",
"block_framer.h",
"block_processor.cc",
@ -179,6 +181,7 @@ if (rtc_include_tests) {
"adaptive_fir_filter_unittest.cc",
"aec3_fft_unittest.cc",
"aec_state_unittest.cc",
"block_delay_buffer_unittest.cc",
"block_framer_unittest.cc",
"block_processor_metrics_unittest.cc",
"block_processor_unittest.cc",

View File

@ -0,0 +1,47 @@
/*
* Copyright (c) 2018 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include "modules/audio_processing/aec3/block_delay_buffer.h"
#include "rtc_base/checks.h"
namespace webrtc {
BlockDelayBuffer::BlockDelayBuffer(size_t num_bands,
size_t frame_length,
size_t delay_samples)
: frame_length_(frame_length),
delay_(delay_samples),
buf_(num_bands, std::vector<float>(delay_, 0.f)) {}
BlockDelayBuffer::~BlockDelayBuffer() = default;
void BlockDelayBuffer::DelaySignal(AudioBuffer* frame) {
RTC_DCHECK_EQ(1, frame->num_channels());
RTC_DCHECK_EQ(buf_.size(), frame->num_bands());
if (delay_ == 0) {
return;
}
const size_t i_start = last_insert_;
size_t i = 0;
for (size_t j = 0; j < buf_.size(); ++j) {
i = i_start;
for (size_t k = 0; k < frame_length_; ++k) {
const float tmp = buf_[j][i];
buf_[j][i] = frame->split_bands_f(0)[j][k];
frame->split_bands_f(0)[j][k] = tmp;
i = i < buf_[0].size() - 1 ? i + 1 : 0;
}
}
last_insert_ = i;
}
} // namespace webrtc

View File

@ -0,0 +1,38 @@
/*
* Copyright (c) 2018 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#ifndef MODULES_AUDIO_PROCESSING_AEC3_BLOCK_DELAY_BUFFER_H_
#define MODULES_AUDIO_PROCESSING_AEC3_BLOCK_DELAY_BUFFER_H_
#include <vector>
#include "modules/audio_processing/audio_buffer.h"
namespace webrtc {
// Class for applying a fixed delay to the samples in a signal partitioned using
// the audiobuffer band-splitting scheme.
class BlockDelayBuffer {
public:
BlockDelayBuffer(size_t num_bands, size_t frame_length, size_t delay_samples);
~BlockDelayBuffer();
// Delays the samples by the specified delay.
void DelaySignal(AudioBuffer* frame);
private:
const size_t frame_length_;
const size_t delay_;
std::vector<std::vector<float>> buf_;
size_t last_insert_ = 0;
};
} // namespace webrtc
#endif // MODULES_AUDIO_PROCESSING_AEC3_BLOCK_DELAY_BUFFER_H_

View File

@ -0,0 +1,91 @@
/*
* 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/block_delay_buffer.h"
#include <string>
#include "modules/audio_processing/aec3/aec3_common.h"
#include "modules/audio_processing/audio_buffer.h"
#include "rtc_base/strings/string_builder.h"
#include "test/gtest.h"
namespace webrtc {
namespace {
float SampleValue(size_t sample_index) {
return sample_index % 32768;
}
// Populates the frame with linearly increasing sample values for each band.
void PopulateInputFrame(size_t frame_length,
size_t num_bands,
size_t first_sample_index,
float* const* frame) {
for (size_t k = 0; k < num_bands; ++k) {
for (size_t i = 0; i < frame_length; ++i) {
frame[k][i] = SampleValue(first_sample_index + i);
}
}
}
std::string ProduceDebugText(int sample_rate_hz, size_t delay) {
char log_stream_buffer[8 * 1024];
rtc::SimpleStringBuilder ss(log_stream_buffer);
ss << "Sample rate: " << sample_rate_hz;
ss << ", Delay: " << delay;
return ss.str();
}
} // namespace
// Verifies that the correct signal delay is achived.
TEST(BlockDelayBuffer, CorrectDelayApplied) {
for (size_t delay : {0, 1, 27, 160, 4321, 7021}) {
for (auto rate : {8000, 16000, 32000, 48000}) {
SCOPED_TRACE(ProduceDebugText(rate, delay));
size_t num_bands = NumBandsForRate(rate);
size_t fullband_frame_length = rate / 100;
size_t subband_frame_length = rate == 8000 ? 80 : 160;
BlockDelayBuffer delay_buffer(num_bands, subband_frame_length, delay);
static constexpr size_t kNumFramesToProcess = 20;
for (size_t frame_index = 0; frame_index < kNumFramesToProcess;
++frame_index) {
AudioBuffer audio_buffer(fullband_frame_length, 1,
fullband_frame_length, 1,
fullband_frame_length);
if (rate > 16000) {
audio_buffer.SplitIntoFrequencyBands();
}
size_t first_sample_index = frame_index * subband_frame_length;
PopulateInputFrame(subband_frame_length, num_bands, first_sample_index,
&audio_buffer.split_bands_f(0)[0]);
delay_buffer.DelaySignal(&audio_buffer);
for (size_t k = 0; k < num_bands; ++k) {
size_t sample_index = first_sample_index;
for (size_t i = 0; i < subband_frame_length; ++i, ++sample_index) {
if (sample_index < delay) {
EXPECT_EQ(0.f, audio_buffer.split_bands_f(0)[k][i]);
} else {
EXPECT_EQ(SampleValue(sample_index - delay),
audio_buffer.split_bands_f(0)[k][i]);
}
}
}
}
}
}
}
} // namespace webrtc

View File

@ -342,6 +342,7 @@ EchoCanceller3::EchoCanceller3(const EchoCanceller3Config& config,
std::unique_ptr<BlockProcessor> block_processor)
: data_dumper_(
new ApmDataDumper(rtc::AtomicOps::Increment(&instance_count_))),
config_(config),
sample_rate_hz_(sample_rate_hz),
num_bands_(NumBandsForRate(sample_rate_hz_)),
frame_length_(rtc::CheckedDivExact(LowestBandRate(sample_rate_hz_), 100)),
@ -358,7 +359,10 @@ EchoCanceller3::EchoCanceller3(const EchoCanceller3Config& config,
render_queue_output_frame_(num_bands_,
std::vector<float>(frame_length_, 0.f)),
block_(num_bands_, std::vector<float>(kBlockSize, 0.f)),
sub_frame_view_(num_bands_) {
sub_frame_view_(num_bands_),
block_delay_buffer_(num_bands_,
frame_length_,
config_.delay.fixed_capture_delay_samples) {
RTC_DCHECK(ValidFullBandRate(sample_rate_hz_));
std::unique_ptr<CascadedBiQuadFilter> render_highpass_filter;
@ -421,6 +425,11 @@ void EchoCanceller3::ProcessCapture(AudioBuffer* capture, bool level_change) {
data_dumper_->DumpRaw("aec3_call_order",
static_cast<int>(EchoCanceller3ApiCall::kCapture));
// Optionally delay the capture signal.
if (config_.delay.fixed_capture_delay_samples > 0) {
block_delay_buffer_.DelaySignal(capture);
}
rtc::ArrayView<float> capture_lower_band =
rtc::ArrayView<float>(&capture->split_bands_f(0)[0][0], frame_length_);

View File

@ -12,6 +12,7 @@
#define MODULES_AUDIO_PROCESSING_AEC3_ECHO_CANCELLER3_H_
#include "api/audio/echo_canceller3_config.h"
#include "modules/audio_processing/aec3/block_delay_buffer.h"
#include "modules/audio_processing/aec3/block_framer.h"
#include "modules/audio_processing/aec3/block_processor.h"
#include "modules/audio_processing/aec3/cascaded_biquad_filter.h"
@ -110,6 +111,7 @@ class EchoCanceller3 : public EchoControl {
// State that may be accessed by the capture thread.
static int instance_count_;
std::unique_ptr<ApmDataDumper> data_dumper_;
const EchoCanceller3Config config_;
const int sample_rate_hz_;
const int num_bands_;
const size_t frame_length_;
@ -129,6 +131,7 @@ class EchoCanceller3 : public EchoControl {
std::vector<std::vector<float>> block_ RTC_GUARDED_BY(capture_race_checker_);
std::vector<rtc::ArrayView<float>> sub_frame_view_
RTC_GUARDED_BY(capture_race_checker_);
BlockDelayBuffer block_delay_buffer_ RTC_GUARDED_BY(capture_race_checker_);
RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(EchoCanceller3);
};

View File

@ -203,6 +203,8 @@ EchoCanceller3Config ParseAec3Parameters(const std::string& filename) {
&cfg.delay.hysteresis_limit_2_blocks);
ReadParam(section, "skew_hysteresis_blocks",
&cfg.delay.skew_hysteresis_blocks);
ReadParam(section, "fixed_capture_delay_samples",
&cfg.delay.fixed_capture_delay_samples);
}
if (rtc::GetValueFromJsonObject(root, "filter", &section)) {