Added faster initial model adaptation speed in AEC3

Bug: webrtc:8746
Change-Id: Idcb65e2b1241a7da8c4a98622923e401d174b879
Reviewed-on: https://webrtc-review.googlesource.com/39506
Commit-Queue: Per Åhgren <peah@webrtc.org>
Reviewed-by: Per Åhgren <peah@webrtc.org>
Reviewed-by: Gustaf Ullberg <gustaf@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#21619}
This commit is contained in:
Per Åhgren
2018-01-15 19:17:16 +01:00
committed by Commit Bot
parent afd1d6c709
commit a98c8074ba
10 changed files with 116 additions and 13 deletions

View File

@ -22,6 +22,7 @@
#include "modules/audio_processing/aec3/fft_data.h"
#include "rtc_base/checks.h"
#include "rtc_base/logging.h"
namespace webrtc {
@ -414,15 +415,16 @@ void ApplyFilter_SSE2(const RenderBuffer& render_buffer,
} // namespace aec3
AdaptiveFirFilter::AdaptiveFirFilter(size_t size_partitions,
AdaptiveFirFilter::AdaptiveFirFilter(size_t max_size_partitions,
Aec3Optimization optimization,
ApmDataDumper* data_dumper)
: data_dumper_(data_dumper),
fft_(),
optimization_(optimization),
H_(size_partitions),
H2_(size_partitions, std::array<float, kFftLengthBy2Plus1>()),
h_(GetTimeDomainLength(size_partitions), 0.f) {
max_size_partitions_(max_size_partitions),
H_(max_size_partitions_),
H2_(max_size_partitions_, std::array<float, kFftLengthBy2Plus1>()),
h_(GetTimeDomainLength(max_size_partitions_), 0.f) {
RTC_DCHECK(data_dumper_);
for (auto& H_j : H_) {
@ -437,16 +439,53 @@ AdaptiveFirFilter::AdaptiveFirFilter(size_t size_partitions,
AdaptiveFirFilter::~AdaptiveFirFilter() = default;
void AdaptiveFirFilter::HandleEchoPathChange() {
size_t current_h_size = h_.size();
h_.resize(GetTimeDomainLength(max_size_partitions_));
std::fill(h_.begin(), h_.end(), 0.f);
h_.resize(current_h_size);
size_t current_size_partitions = H_.size();
H_.resize(max_size_partitions_);
for (auto& H_j : H_) {
H_j.Clear();
}
H_.resize(current_size_partitions);
H2_.resize(max_size_partitions_);
for (auto& H2_k : H2_) {
H2_k.fill(0.f);
}
H2_.resize(current_size_partitions);
erl_.fill(0.f);
}
void AdaptiveFirFilter::SetSizePartitions(size_t size) {
RTC_DCHECK_EQ(max_size_partitions_, H_.capacity());
RTC_DCHECK_EQ(max_size_partitions_, H2_.capacity());
RTC_DCHECK_EQ(GetTimeDomainLength(max_size_partitions_), h_.capacity());
RTC_DCHECK_EQ(H_.size(), H2_.size());
RTC_DCHECK_EQ(h_.size(), GetTimeDomainLength(H_.size()));
if (size > max_size_partitions_) {
RTC_LOG(LS_ERROR) << "Too large adaptive filter size specificed: " << size;
size = max_size_partitions_;
}
if (size < H_.size()) {
for (size_t k = size; k < H_.size(); ++k) {
H_[k].Clear();
H2_[k].fill(0.f);
}
std::fill(h_.begin() + GetTimeDomainLength(size), h_.end(), 0.f);
}
H_.resize(size);
H2_.resize(size);
h_.resize(GetTimeDomainLength(size));
}
void AdaptiveFirFilter::Filter(const RenderBuffer& render_buffer,
FftData* S) const {
RTC_DCHECK(S);

View File

@ -91,7 +91,7 @@ void ApplyFilter_SSE2(const RenderBuffer& render_buffer,
// Provides a frequency domain adaptive filter functionality.
class AdaptiveFirFilter {
public:
AdaptiveFirFilter(size_t size_partitions,
AdaptiveFirFilter(size_t max_size_partitions,
Aec3Optimization optimization,
ApmDataDumper* data_dumper);
@ -110,6 +110,9 @@ class AdaptiveFirFilter {
// Returns the filter size.
size_t SizePartitions() const { return H_.size(); }
// Sets the filter size.
void SetSizePartitions(size_t size);
// Returns the filter based echo return loss.
const std::array<float, kFftLengthBy2Plus1>& Erl() const { return erl_; }
@ -123,10 +126,13 @@ class AdaptiveFirFilter {
const std::vector<float>& FilterImpulseResponse() const { return h_; }
void DumpFilter(const char* name) {
size_t current_size_partitions = H_.size();
H_.resize(max_size_partitions_);
for (auto& H : H_) {
data_dumper_->DumpRaw(name, H.re);
data_dumper_->DumpRaw(name, H.im);
}
H_.resize(current_size_partitions);
}
private:
@ -136,6 +142,7 @@ class AdaptiveFirFilter {
ApmDataDumper* const data_dumper_;
const Aec3Fft fft_;
const Aec3Optimization optimization_;
const size_t max_size_partitions_;
std::vector<FftData> H_;
std::vector<std::array<float, kFftLengthBy2Plus1>> H2_;
std::vector<float> h_;

View File

@ -78,6 +78,7 @@ void AecState::HandleEchoPathChange(
render_received_ = false;
force_zero_gain_ = true;
blocks_with_active_render_ = 0;
initial_state_ = true;
};
// TODO(peah): Refine the reset scheme according to the type of gain and
@ -155,6 +156,9 @@ void AecState::Update(
filter_has_had_time_to_converge_ =
blocks_with_proper_filter_adaptation_ >= 2 * kNumBlocksPerSecond;
initial_state_ =
blocks_with_proper_filter_adaptation_ < 5 * kNumBlocksPerSecond;
// Flag whether the linear filter estimate is usable.
usable_linear_estimate_ =
!echo_saturation_ &&

View File

@ -103,6 +103,9 @@ class AecState {
return filter_has_had_time_to_converge_;
}
// Returns whether the filter adaptation is still in the initial state.
bool InitialState() const { return initial_state_; }
// Updates the aec state.
void Update(const std::vector<std::array<float, kFftLengthBy2Plus1>>&
adaptive_filter_frequency_response,
@ -161,6 +164,7 @@ class AecState {
float reverb_decay_;
bool saturating_echo_path_ = false;
bool filter_has_had_time_to_converge_ = false;
bool initial_state_ = true;
RTC_DISALLOW_COPY_AND_ASSIGN(AecState);
};

View File

@ -86,6 +86,7 @@ class EchoRemoverImpl final : public EchoRemover {
bool echo_leakage_detected_ = false;
AecState aec_state_;
EchoRemoverMetrics metrics_;
bool initial_state_ = true;
RTC_DISALLOW_COPY_AND_ASSIGN(EchoRemoverImpl);
};
@ -146,6 +147,7 @@ void EchoRemoverImpl::ProcessCapture(
if (echo_path_variability.AudioPathChanged()) {
subtractor_.HandleEchoPathChange(echo_path_variability);
aec_state_.HandleEchoPathChange(echo_path_variability);
initial_state_ = true;
}
std::array<float, kFftLengthBy2Plus1> Y2;
@ -166,6 +168,10 @@ void EchoRemoverImpl::ProcessCapture(
render_signal_analyzer_.Update(*render_buffer, aec_state_.FilterDelay());
// Perform linear echo cancellation.
if (initial_state_ && !aec_state_.InitialState()) {
subtractor_.ExitInitialState();
initial_state_ = false;
}
subtractor_.Process(*render_buffer, y0, render_signal_analyzer_, aec_state_,
&subtractor_output);

View File

@ -44,10 +44,16 @@ class MainFilterUpdateGain {
bool saturated_capture_signal,
FftData* gain_fft);
// Sets a new config.
void SetConfig(
const EchoCanceller3Config::Filter::MainConfiguration& config) {
config_ = config;
}
private:
static int instance_count_;
std::unique_ptr<ApmDataDumper> data_dumper_;
const EchoCanceller3Config::Filter::MainConfiguration config_;
EchoCanceller3Config::Filter::MainConfiguration config_;
std::array<float, kFftLengthBy2Plus1> H_error_;
size_t poor_excitation_counter_;
size_t call_counter_ = 0;

View File

@ -36,8 +36,14 @@ class ShadowFilterUpdateGain {
bool saturated_capture_signal,
FftData* G);
// Sets a new config.
void SetConfig(
const EchoCanceller3Config::Filter::ShadowConfiguration& config) {
config_ = config;
}
private:
const EchoCanceller3Config::Filter::ShadowConfiguration config_;
EchoCanceller3Config::Filter::ShadowConfiguration config_;
// TODO(peah): Check whether this counter should instead be initialized to a
// large value.
size_t poor_signal_excitation_counter_ = 0;

View File

@ -51,19 +51,30 @@ Subtractor::Subtractor(const EchoCanceller3Config& config,
: fft_(),
data_dumper_(data_dumper),
optimization_(optimization),
main_filter_(config.filter.main.length_blocks,
config_(config),
main_filter_(config_.filter.main.length_blocks,
optimization,
data_dumper_),
shadow_filter_(config.filter.shadow.length_blocks,
shadow_filter_(config_.filter.shadow.length_blocks,
optimization,
data_dumper_),
G_main_(config.filter.main),
G_shadow_(config.filter.shadow) {
G_main_(config_.filter.main_initial),
G_shadow_(config_.filter.shadow_initial) {
RTC_DCHECK(data_dumper_);
// Currently, the rest of AEC3 requires the main and shadow filter lengths to
// be identical.
RTC_DCHECK_EQ(config.filter.main.length_blocks,
config.filter.shadow.length_blocks);
RTC_DCHECK_EQ(config_.filter.main.length_blocks,
config_.filter.shadow.length_blocks);
RTC_DCHECK_EQ(config_.filter.main_initial.length_blocks,
config_.filter.shadow_initial.length_blocks);
RTC_DCHECK_GE(config_.filter.main.length_blocks,
config_.filter.main_initial.length_blocks);
RTC_DCHECK_GE(config_.filter.shadow.length_blocks,
config_.filter.shadow_initial.length_blocks);
main_filter_.SetSizePartitions(config_.filter.main_initial.length_blocks);
shadow_filter_.SetSizePartitions(config_.filter.shadow_initial.length_blocks);
}
Subtractor::~Subtractor() = default;
@ -75,6 +86,12 @@ void Subtractor::HandleEchoPathChange(
shadow_filter_.HandleEchoPathChange();
G_main_.HandleEchoPathChange(echo_path_variability);
G_shadow_.HandleEchoPathChange();
G_main_.SetConfig(config_.filter.main_initial);
G_shadow_.SetConfig(config_.filter.shadow_initial);
main_filter_.SetSizePartitions(config_.filter.main_initial.length_blocks);
shadow_filter_.SetSizePartitions(
config_.filter.shadow_initial.length_blocks);
converged_filter_ = false;
};
@ -93,6 +110,13 @@ void Subtractor::HandleEchoPathChange(
}
}
void Subtractor::ExitInitialState() {
G_main_.SetConfig(config_.filter.main);
G_shadow_.SetConfig(config_.filter.shadow);
main_filter_.SetSizePartitions(config_.filter.main.length_blocks);
shadow_filter_.SetSizePartitions(config_.filter.shadow.length_blocks);
}
void Subtractor::Process(const RenderBuffer& render_buffer,
const rtc::ArrayView<const float> capture,
const RenderSignalAnalyzer& render_signal_analyzer,

View File

@ -47,6 +47,9 @@ class Subtractor {
void HandleEchoPathChange(const EchoPathVariability& echo_path_variability);
// Exits the initial state.
void ExitInitialState();
// Returns the block-wise frequency response for the main adaptive filter.
const std::vector<std::array<float, kFftLengthBy2Plus1>>&
FilterFrequencyResponse() const {
@ -64,6 +67,7 @@ class Subtractor {
const Aec3Fft fft_;
ApmDataDumper* data_dumper_;
const Aec3Optimization optimization_;
const EchoCanceller3Config config_;
AdaptiveFirFilter main_filter_;
AdaptiveFirFilter shadow_filter_;
MainFilterUpdateGain G_main_;

View File

@ -1260,6 +1260,9 @@ struct EchoCanceller3Config {
MainConfiguration main = {12, 0.005f, 0.05f, 0.001f, 20075344.f};
ShadowConfiguration shadow = {12, 0.1f, 20075344.f};
MainConfiguration main_initial = {12, 0.01f, 0.1f, 0.001f, 20075344.f};
ShadowConfiguration shadow_initial = {12, 0.7f, 20075344.f};
} filter;
struct Erle {