First step of adding multi-channel support to the echo subtractor
This CL contains the first step of adding multi-channel support to the echo subtractor. The CL is bitexact for the mono case. Bug: webrtc:10913 Change-Id: I10647b45c692bc001407afc6ff00e26a3e2cffaa Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/154356 Reviewed-by: Gustaf Ullberg <gustaf@webrtc.org> Commit-Queue: Per Åhgren <peah@webrtc.org> Cr-Commit-Position: refs/heads/master@{#29303}
This commit is contained in:
parent
538ca57b23
commit
7bdf073c1c
@ -147,7 +147,7 @@ class EchoRemoverImpl final : public EchoRemover {
|
||||
const size_t num_render_channels_;
|
||||
const size_t num_capture_channels_;
|
||||
const bool use_shadow_filter_output_;
|
||||
std::vector<std::unique_ptr<Subtractor>> subtractors_;
|
||||
Subtractor subtractor_;
|
||||
std::vector<std::unique_ptr<SuppressionGain>> suppression_gains_;
|
||||
std::vector<std::unique_ptr<ComfortNoiseGenerator>> cngs_;
|
||||
SuppressionFilter suppression_filter_;
|
||||
@ -190,7 +190,11 @@ EchoRemoverImpl::EchoRemoverImpl(const EchoCanceller3Config& config,
|
||||
num_capture_channels_(num_capture_channels),
|
||||
use_shadow_filter_output_(
|
||||
config_.filter.enable_shadow_filter_output_usage),
|
||||
subtractors_(num_capture_channels_),
|
||||
subtractor_(config,
|
||||
num_render_channels_,
|
||||
num_capture_channels_,
|
||||
data_dumper_.get(),
|
||||
optimization_),
|
||||
suppression_gains_(num_capture_channels_),
|
||||
cngs_(num_capture_channels_),
|
||||
suppression_filter_(optimization_,
|
||||
@ -219,9 +223,6 @@ EchoRemoverImpl::EchoRemoverImpl(const EchoCanceller3Config& config,
|
||||
for (size_t ch = 0; ch < num_capture_channels_; ++ch) {
|
||||
residual_echo_estimators_[ch] =
|
||||
std::make_unique<ResidualEchoEstimator>(config_);
|
||||
subtractors_[ch] = std::make_unique<Subtractor>(
|
||||
config, num_render_channels_, num_capture_channels_, data_dumper_.get(),
|
||||
optimization_);
|
||||
suppression_gains_[ch] = std::make_unique<SuppressionGain>(
|
||||
config_, optimization_, sample_rate_hz);
|
||||
cngs_[ch] = std::make_unique<ComfortNoiseGenerator>(optimization_);
|
||||
@ -339,9 +340,7 @@ void EchoRemoverImpl::ProcessCapture(
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t ch = 0; ch < num_capture_channels_; ++ch) {
|
||||
subtractors_[ch]->HandleEchoPathChange(echo_path_variability);
|
||||
}
|
||||
subtractor_.HandleEchoPathChange(echo_path_variability);
|
||||
aec_state_.HandleEchoPathChange(echo_path_variability);
|
||||
|
||||
if (echo_path_variability.delay_change !=
|
||||
@ -359,21 +358,21 @@ void EchoRemoverImpl::ProcessCapture(
|
||||
render_signal_analyzer_.Update(*render_buffer,
|
||||
aec_state_.FilterDelayBlocks());
|
||||
|
||||
// Perform linear echo cancellation.
|
||||
// State transition.
|
||||
if (aec_state_.TransitionTriggered()) {
|
||||
subtractor_.ExitInitialState();
|
||||
for (size_t ch = 0; ch < num_capture_channels_; ++ch) {
|
||||
subtractors_[ch]->ExitInitialState();
|
||||
suppression_gains_[ch]->SetInitialState(false);
|
||||
}
|
||||
}
|
||||
|
||||
// Perform linear echo cancellation.
|
||||
subtractor_.Process(*render_buffer, (*y)[0], render_signal_analyzer_,
|
||||
aec_state_, subtractor_output);
|
||||
|
||||
for (size_t ch = 0; ch < num_capture_channels_; ++ch) {
|
||||
auto& y_low = (*y)[0][ch];
|
||||
|
||||
// If the delay is known, use the echo subtractor.
|
||||
subtractors_[ch]->Process(*render_buffer, y_low, render_signal_analyzer_,
|
||||
aec_state_, &subtractor_output[ch]);
|
||||
|
||||
// Compute spectra.
|
||||
FormLinearFilterOutput(subtractor_output[ch], e[ch]);
|
||||
WindowedPaddedFft(fft_, y_low, y_old_[ch], &Y[ch]);
|
||||
@ -385,9 +384,9 @@ void EchoRemoverImpl::ProcessCapture(
|
||||
|
||||
// Update the AEC state information.
|
||||
// TODO(bugs.webrtc.org/10913): Take all subtractors into account.
|
||||
aec_state_.Update(external_delay, subtractors_[0]->FilterFrequencyResponse(),
|
||||
subtractors_[0]->FilterImpulseResponse(), *render_buffer,
|
||||
E2[0], Y2[0], subtractor_output[0], y0);
|
||||
aec_state_.Update(external_delay, subtractor_.FilterFrequencyResponse(),
|
||||
subtractor_.FilterImpulseResponse(), *render_buffer, E2[0],
|
||||
Y2[0], subtractor_output[0], y0);
|
||||
|
||||
// Choose the linear output.
|
||||
const auto& Y_fft = aec_state_.UseLinearFilterOutput() ? E : Y;
|
||||
|
||||
@ -65,32 +65,50 @@ Subtractor::Subtractor(const EchoCanceller3Config& config,
|
||||
data_dumper_(data_dumper),
|
||||
optimization_(optimization),
|
||||
config_(config),
|
||||
main_filter_(config_.filter.main.length_blocks,
|
||||
config_.filter.main_initial.length_blocks,
|
||||
config.filter.config_change_duration_blocks,
|
||||
num_render_channels,
|
||||
num_capture_channels,
|
||||
optimization,
|
||||
data_dumper_),
|
||||
shadow_filter_(config_.filter.shadow.length_blocks,
|
||||
config_.filter.shadow_initial.length_blocks,
|
||||
config.filter.config_change_duration_blocks,
|
||||
num_render_channels,
|
||||
num_capture_channels,
|
||||
optimization,
|
||||
data_dumper_),
|
||||
G_main_(config_.filter.main_initial,
|
||||
config_.filter.config_change_duration_blocks),
|
||||
G_shadow_(config_.filter.shadow_initial,
|
||||
config.filter.config_change_duration_blocks),
|
||||
main_frequency_response_(main_filter_.max_filter_size_partitions(),
|
||||
std::array<float, kFftLengthBy2Plus1>()),
|
||||
num_capture_channels_(num_capture_channels),
|
||||
main_filter_(num_capture_channels_),
|
||||
shadow_filter_(num_capture_channels_),
|
||||
G_main_(num_capture_channels_),
|
||||
G_shadow_(num_capture_channels_),
|
||||
filter_misadjustment_estimator_(num_capture_channels_),
|
||||
poor_shadow_filter_counter_(num_capture_channels_, 0),
|
||||
main_frequency_response_(
|
||||
num_capture_channels_,
|
||||
std::vector<std::array<float, kFftLengthBy2Plus1>>(
|
||||
std::max(config_.filter.main_initial.length_blocks,
|
||||
config_.filter.main.length_blocks),
|
||||
std::array<float, kFftLengthBy2Plus1>())),
|
||||
main_impulse_response_(
|
||||
GetTimeDomainLength(main_filter_.max_filter_size_partitions()),
|
||||
0.f) {
|
||||
num_capture_channels_,
|
||||
std::vector<float>(GetTimeDomainLength(std::max(
|
||||
config_.filter.main_initial.length_blocks,
|
||||
config_.filter.main.length_blocks)),
|
||||
0.f)) {
|
||||
for (size_t ch = 0; ch < num_capture_channels_; ++ch) {
|
||||
main_filter_[ch] = std::make_unique<AdaptiveFirFilter>(
|
||||
config_.filter.main.length_blocks,
|
||||
config_.filter.main_initial.length_blocks,
|
||||
config.filter.config_change_duration_blocks, num_render_channels,
|
||||
num_capture_channels, optimization, data_dumper_);
|
||||
|
||||
shadow_filter_[ch] = std::make_unique<AdaptiveFirFilter>(
|
||||
config_.filter.shadow.length_blocks,
|
||||
config_.filter.shadow_initial.length_blocks,
|
||||
config.filter.config_change_duration_blocks, num_render_channels,
|
||||
num_capture_channels, optimization, data_dumper_);
|
||||
G_main_[ch] = std::make_unique<MainFilterUpdateGain>(
|
||||
config_.filter.main_initial,
|
||||
config_.filter.config_change_duration_blocks);
|
||||
G_shadow_[ch] = std::make_unique<ShadowFilterUpdateGain>(
|
||||
config_.filter.shadow_initial,
|
||||
config.filter.config_change_duration_blocks);
|
||||
}
|
||||
|
||||
RTC_DCHECK(data_dumper_);
|
||||
for (auto& H2_k : main_frequency_response_) {
|
||||
H2_k.fill(0.f);
|
||||
for (size_t ch = 0; ch < num_capture_channels_; ++ch) {
|
||||
for (auto& H2_k : main_frequency_response_[ch]) {
|
||||
H2_k.fill(0.f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -99,16 +117,18 @@ Subtractor::~Subtractor() = default;
|
||||
void Subtractor::HandleEchoPathChange(
|
||||
const EchoPathVariability& echo_path_variability) {
|
||||
const auto full_reset = [&]() {
|
||||
main_filter_.HandleEchoPathChange();
|
||||
shadow_filter_.HandleEchoPathChange();
|
||||
G_main_.HandleEchoPathChange(echo_path_variability);
|
||||
G_shadow_.HandleEchoPathChange();
|
||||
G_main_.SetConfig(config_.filter.main_initial, true);
|
||||
G_shadow_.SetConfig(config_.filter.shadow_initial, true);
|
||||
main_filter_.SetSizePartitions(config_.filter.main_initial.length_blocks,
|
||||
true);
|
||||
shadow_filter_.SetSizePartitions(
|
||||
config_.filter.shadow_initial.length_blocks, true);
|
||||
for (size_t ch = 0; ch < num_capture_channels_; ++ch) {
|
||||
main_filter_[ch]->HandleEchoPathChange();
|
||||
shadow_filter_[ch]->HandleEchoPathChange();
|
||||
G_main_[ch]->HandleEchoPathChange(echo_path_variability);
|
||||
G_shadow_[ch]->HandleEchoPathChange();
|
||||
G_main_[ch]->SetConfig(config_.filter.main_initial, true);
|
||||
G_shadow_[ch]->SetConfig(config_.filter.shadow_initial, true);
|
||||
main_filter_[ch]->SetSizePartitions(
|
||||
config_.filter.main_initial.length_blocks, true);
|
||||
shadow_filter_[ch]->SetSizePartitions(
|
||||
config_.filter.shadow_initial.length_blocks, true);
|
||||
}
|
||||
};
|
||||
|
||||
if (echo_path_variability.delay_change !=
|
||||
@ -117,128 +137,149 @@ void Subtractor::HandleEchoPathChange(
|
||||
}
|
||||
|
||||
if (echo_path_variability.gain_change) {
|
||||
G_main_.HandleEchoPathChange(echo_path_variability);
|
||||
for (size_t ch = 0; ch < num_capture_channels_; ++ch) {
|
||||
G_main_[ch]->HandleEchoPathChange(echo_path_variability);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Subtractor::ExitInitialState() {
|
||||
G_main_.SetConfig(config_.filter.main, false);
|
||||
G_shadow_.SetConfig(config_.filter.shadow, false);
|
||||
main_filter_.SetSizePartitions(config_.filter.main.length_blocks, false);
|
||||
shadow_filter_.SetSizePartitions(config_.filter.shadow.length_blocks, false);
|
||||
for (size_t ch = 0; ch < num_capture_channels_; ++ch) {
|
||||
G_main_[ch]->SetConfig(config_.filter.main, false);
|
||||
G_shadow_[ch]->SetConfig(config_.filter.shadow, false);
|
||||
main_filter_[ch]->SetSizePartitions(config_.filter.main.length_blocks,
|
||||
false);
|
||||
shadow_filter_[ch]->SetSizePartitions(config_.filter.shadow.length_blocks,
|
||||
false);
|
||||
}
|
||||
}
|
||||
|
||||
void Subtractor::Process(const RenderBuffer& render_buffer,
|
||||
const rtc::ArrayView<const float> capture,
|
||||
const std::vector<std::vector<float>>& capture,
|
||||
const RenderSignalAnalyzer& render_signal_analyzer,
|
||||
const AecState& aec_state,
|
||||
SubtractorOutput* output) {
|
||||
RTC_DCHECK_EQ(kBlockSize, capture.size());
|
||||
rtc::ArrayView<const float> y = capture;
|
||||
FftData& E_main = output->E_main;
|
||||
FftData E_shadow;
|
||||
std::array<float, kBlockSize>& e_main = output->e_main;
|
||||
std::array<float, kBlockSize>& e_shadow = output->e_shadow;
|
||||
|
||||
FftData S;
|
||||
FftData& G = S;
|
||||
|
||||
// Form the outputs of the main and shadow filters.
|
||||
main_filter_.Filter(render_buffer, &S);
|
||||
PredictionError(fft_, S, y, &e_main, &output->s_main);
|
||||
|
||||
shadow_filter_.Filter(render_buffer, &S);
|
||||
PredictionError(fft_, S, y, &e_shadow, &output->s_shadow);
|
||||
|
||||
// Compute the signal powers in the subtractor output.
|
||||
output->ComputeMetrics(y);
|
||||
|
||||
// Adjust the filter if needed.
|
||||
bool main_filter_adjusted = false;
|
||||
filter_misadjustment_estimator_.Update(*output);
|
||||
if (filter_misadjustment_estimator_.IsAdjustmentNeeded()) {
|
||||
float scale = filter_misadjustment_estimator_.GetMisadjustment();
|
||||
main_filter_.ScaleFilter(scale);
|
||||
for (auto& h_k : main_impulse_response_) {
|
||||
h_k *= scale;
|
||||
}
|
||||
ScaleFilterOutput(y, scale, e_main, output->s_main);
|
||||
filter_misadjustment_estimator_.Reset();
|
||||
main_filter_adjusted = true;
|
||||
}
|
||||
|
||||
// Compute the FFts of the main and shadow filter outputs.
|
||||
fft_.ZeroPaddedFft(e_main, Aec3Fft::Window::kHanning, &E_main);
|
||||
fft_.ZeroPaddedFft(e_shadow, Aec3Fft::Window::kHanning, &E_shadow);
|
||||
|
||||
// Compute spectra for future use.
|
||||
E_shadow.Spectrum(optimization_, output->E2_shadow);
|
||||
E_main.Spectrum(optimization_, output->E2_main);
|
||||
rtc::ArrayView<SubtractorOutput> outputs) {
|
||||
RTC_DCHECK_EQ(num_capture_channels_, capture.size());
|
||||
|
||||
// Compute the render powers.
|
||||
std::array<float, kFftLengthBy2Plus1> X2_main;
|
||||
std::array<float, kFftLengthBy2Plus1> X2_shadow_data;
|
||||
std::array<float, kFftLengthBy2Plus1>& X2_shadow =
|
||||
main_filter_.SizePartitions() == shadow_filter_.SizePartitions()
|
||||
main_filter_[0]->SizePartitions() == shadow_filter_[0]->SizePartitions()
|
||||
? X2_main
|
||||
: X2_shadow_data;
|
||||
if (main_filter_.SizePartitions() == shadow_filter_.SizePartitions()) {
|
||||
render_buffer.SpectralSum(main_filter_.SizePartitions(), &X2_main);
|
||||
} else if (main_filter_.SizePartitions() > shadow_filter_.SizePartitions()) {
|
||||
render_buffer.SpectralSums(shadow_filter_.SizePartitions(),
|
||||
main_filter_.SizePartitions(), &X2_shadow,
|
||||
if (main_filter_[0]->SizePartitions() ==
|
||||
shadow_filter_[0]->SizePartitions()) {
|
||||
render_buffer.SpectralSum(main_filter_[0]->SizePartitions(), &X2_main);
|
||||
} else if (main_filter_[0]->SizePartitions() >
|
||||
shadow_filter_[0]->SizePartitions()) {
|
||||
render_buffer.SpectralSums(shadow_filter_[0]->SizePartitions(),
|
||||
main_filter_[0]->SizePartitions(), &X2_shadow,
|
||||
&X2_main);
|
||||
} else {
|
||||
render_buffer.SpectralSums(main_filter_.SizePartitions(),
|
||||
shadow_filter_.SizePartitions(), &X2_main,
|
||||
render_buffer.SpectralSums(main_filter_[0]->SizePartitions(),
|
||||
shadow_filter_[0]->SizePartitions(), &X2_main,
|
||||
&X2_shadow);
|
||||
}
|
||||
|
||||
// Update the main filter.
|
||||
if (!main_filter_adjusted) {
|
||||
std::array<float, kFftLengthBy2Plus1> erl;
|
||||
ComputeErl(optimization_, main_frequency_response_, erl);
|
||||
G_main_.Compute(X2_main, render_signal_analyzer, *output, erl,
|
||||
main_filter_.SizePartitions(), aec_state.SaturatedCapture(),
|
||||
&G);
|
||||
} else {
|
||||
G.re.fill(0.f);
|
||||
G.im.fill(0.f);
|
||||
// Process all capture channels
|
||||
for (size_t ch = 0; ch < num_capture_channels_; ++ch) {
|
||||
RTC_DCHECK_EQ(kBlockSize, capture[ch].size());
|
||||
SubtractorOutput& output = outputs[ch];
|
||||
rtc::ArrayView<const float> y = capture[ch];
|
||||
FftData& E_main = output.E_main;
|
||||
FftData E_shadow;
|
||||
std::array<float, kBlockSize>& e_main = output.e_main;
|
||||
std::array<float, kBlockSize>& e_shadow = output.e_shadow;
|
||||
|
||||
FftData S;
|
||||
FftData& G = S;
|
||||
|
||||
// Form the outputs of the main and shadow filters.
|
||||
main_filter_[ch]->Filter(render_buffer, &S);
|
||||
PredictionError(fft_, S, y, &e_main, &output.s_main);
|
||||
|
||||
shadow_filter_[ch]->Filter(render_buffer, &S);
|
||||
PredictionError(fft_, S, y, &e_shadow, &output.s_shadow);
|
||||
|
||||
// Compute the signal powers in the subtractor output.
|
||||
output.ComputeMetrics(y);
|
||||
|
||||
// Adjust the filter if needed.
|
||||
bool main_filter_adjusted = false;
|
||||
filter_misadjustment_estimator_[ch].Update(output);
|
||||
if (filter_misadjustment_estimator_[ch].IsAdjustmentNeeded()) {
|
||||
float scale = filter_misadjustment_estimator_[ch].GetMisadjustment();
|
||||
main_filter_[ch]->ScaleFilter(scale);
|
||||
for (auto& h_k : main_impulse_response_[ch]) {
|
||||
h_k *= scale;
|
||||
}
|
||||
ScaleFilterOutput(y, scale, e_main, output.s_main);
|
||||
filter_misadjustment_estimator_[ch].Reset();
|
||||
main_filter_adjusted = true;
|
||||
}
|
||||
|
||||
// Compute the FFts of the main and shadow filter outputs.
|
||||
fft_.ZeroPaddedFft(e_main, Aec3Fft::Window::kHanning, &E_main);
|
||||
fft_.ZeroPaddedFft(e_shadow, Aec3Fft::Window::kHanning, &E_shadow);
|
||||
|
||||
// Compute spectra for future use.
|
||||
E_shadow.Spectrum(optimization_, output.E2_shadow);
|
||||
E_main.Spectrum(optimization_, output.E2_main);
|
||||
|
||||
// Update the main filter.
|
||||
if (!main_filter_adjusted) {
|
||||
std::array<float, kFftLengthBy2Plus1> erl;
|
||||
ComputeErl(optimization_, main_frequency_response_[ch], erl);
|
||||
G_main_[ch]->Compute(X2_main, render_signal_analyzer, output, erl,
|
||||
main_filter_[ch]->SizePartitions(),
|
||||
aec_state.SaturatedCapture(), &G);
|
||||
} else {
|
||||
G.re.fill(0.f);
|
||||
G.im.fill(0.f);
|
||||
}
|
||||
main_filter_[ch]->Adapt(render_buffer, G, &main_impulse_response_[ch]);
|
||||
main_filter_[ch]->ComputeFrequencyResponse(&main_frequency_response_[ch]);
|
||||
|
||||
if (ch == 0) {
|
||||
data_dumper_->DumpRaw("aec3_subtractor_G_main", G.re);
|
||||
data_dumper_->DumpRaw("aec3_subtractor_G_main", G.im);
|
||||
}
|
||||
|
||||
// Update the shadow filter.
|
||||
poor_shadow_filter_counter_[ch] = output.e2_main < output.e2_shadow
|
||||
? poor_shadow_filter_counter_[ch] + 1
|
||||
: 0;
|
||||
if (poor_shadow_filter_counter_[ch] < 5) {
|
||||
G_shadow_[ch]->Compute(X2_shadow, render_signal_analyzer, E_shadow,
|
||||
shadow_filter_[ch]->SizePartitions(),
|
||||
aec_state.SaturatedCapture(), &G);
|
||||
} else {
|
||||
poor_shadow_filter_counter_[ch] = 0;
|
||||
shadow_filter_[ch]->SetFilter(main_filter_[ch]->GetFilter());
|
||||
G_shadow_[ch]->Compute(X2_shadow, render_signal_analyzer, E_main,
|
||||
shadow_filter_[ch]->SizePartitions(),
|
||||
aec_state.SaturatedCapture(), &G);
|
||||
}
|
||||
|
||||
shadow_filter_[ch]->Adapt(render_buffer, G);
|
||||
if (ch == 0) {
|
||||
data_dumper_->DumpRaw("aec3_subtractor_G_shadow", G.re);
|
||||
data_dumper_->DumpRaw("aec3_subtractor_G_shadow", G.im);
|
||||
filter_misadjustment_estimator_[ch].Dump(data_dumper_);
|
||||
DumpFilters();
|
||||
}
|
||||
|
||||
std::for_each(e_main.begin(), e_main.end(),
|
||||
[](float& a) { a = rtc::SafeClamp(a, -32768.f, 32767.f); });
|
||||
|
||||
if (ch == 0) {
|
||||
data_dumper_->DumpWav("aec3_main_filter_output", kBlockSize, &e_main[0],
|
||||
16000, 1);
|
||||
data_dumper_->DumpWav("aec3_shadow_filter_output", kBlockSize,
|
||||
&e_shadow[0], 16000, 1);
|
||||
}
|
||||
}
|
||||
main_filter_.Adapt(render_buffer, G, &main_impulse_response_);
|
||||
main_filter_.ComputeFrequencyResponse(&main_frequency_response_);
|
||||
|
||||
data_dumper_->DumpRaw("aec3_subtractor_G_main", G.re);
|
||||
data_dumper_->DumpRaw("aec3_subtractor_G_main", G.im);
|
||||
|
||||
// Update the shadow filter.
|
||||
poor_shadow_filter_counter_ =
|
||||
output->e2_main < output->e2_shadow ? poor_shadow_filter_counter_ + 1 : 0;
|
||||
if (poor_shadow_filter_counter_ < 5) {
|
||||
G_shadow_.Compute(X2_shadow, render_signal_analyzer, E_shadow,
|
||||
shadow_filter_.SizePartitions(),
|
||||
aec_state.SaturatedCapture(), &G);
|
||||
} else {
|
||||
poor_shadow_filter_counter_ = 0;
|
||||
shadow_filter_.SetFilter(main_filter_.GetFilter());
|
||||
G_shadow_.Compute(X2_shadow, render_signal_analyzer, E_main,
|
||||
shadow_filter_.SizePartitions(),
|
||||
aec_state.SaturatedCapture(), &G);
|
||||
}
|
||||
|
||||
shadow_filter_.Adapt(render_buffer, G);
|
||||
data_dumper_->DumpRaw("aec3_subtractor_G_shadow", G.re);
|
||||
data_dumper_->DumpRaw("aec3_subtractor_G_shadow", G.im);
|
||||
filter_misadjustment_estimator_.Dump(data_dumper_);
|
||||
DumpFilters();
|
||||
|
||||
std::for_each(e_main.begin(), e_main.end(),
|
||||
[](float& a) { a = rtc::SafeClamp(a, -32768.f, 32767.f); });
|
||||
|
||||
data_dumper_->DumpWav("aec3_main_filter_output", kBlockSize, &e_main[0],
|
||||
16000, 1);
|
||||
data_dumper_->DumpWav("aec3_shadow_filter_output", kBlockSize, &e_shadow[0],
|
||||
16000, 1);
|
||||
}
|
||||
|
||||
void Subtractor::FilterMisadjustmentEstimator::Update(
|
||||
|
||||
@ -48,35 +48,40 @@ class Subtractor {
|
||||
|
||||
// Performs the echo subtraction.
|
||||
void Process(const RenderBuffer& render_buffer,
|
||||
const rtc::ArrayView<const float> capture,
|
||||
const std::vector<std::vector<float>>& capture,
|
||||
const RenderSignalAnalyzer& render_signal_analyzer,
|
||||
const AecState& aec_state,
|
||||
SubtractorOutput* output);
|
||||
rtc::ArrayView<SubtractorOutput> outputs);
|
||||
|
||||
void HandleEchoPathChange(const EchoPathVariability& echo_path_variability);
|
||||
|
||||
// Exits the initial state.
|
||||
void ExitInitialState();
|
||||
|
||||
// Returns the block-wise frequency response for the main adaptive filter.
|
||||
// Returns the block-wise frequency responses for the main adaptive filters.
|
||||
// TODO(bugs.webrtc.org/10913): Return the frequency responses for all capture
|
||||
// channels.
|
||||
const std::vector<std::array<float, kFftLengthBy2Plus1>>&
|
||||
FilterFrequencyResponse() const {
|
||||
return main_frequency_response_;
|
||||
return main_frequency_response_[0];
|
||||
}
|
||||
|
||||
// Returns the estimate of the impulse response for the main adaptive filter.
|
||||
// Returns the estimates of the impulse responses for the main adaptive
|
||||
// filters.
|
||||
// TODO(bugs.webrtc.org/10913): Return the impulse responses for all capture
|
||||
// channels.
|
||||
const std::vector<float>& FilterImpulseResponse() const {
|
||||
return main_impulse_response_;
|
||||
return main_impulse_response_[0];
|
||||
}
|
||||
|
||||
void DumpFilters() {
|
||||
size_t current_size = main_impulse_response_.size();
|
||||
main_impulse_response_.resize(main_impulse_response_.capacity());
|
||||
data_dumper_->DumpRaw("aec3_subtractor_h_main", main_impulse_response_);
|
||||
main_impulse_response_.resize(current_size);
|
||||
size_t current_size = main_impulse_response_[0].size();
|
||||
main_impulse_response_[0].resize(main_impulse_response_[0].capacity());
|
||||
data_dumper_->DumpRaw("aec3_subtractor_h_main", main_impulse_response_[0]);
|
||||
main_impulse_response_[0].resize(current_size);
|
||||
|
||||
main_filter_.DumpFilter("aec3_subtractor_H_main");
|
||||
shadow_filter_.DumpFilter("aec3_subtractor_H_shadow");
|
||||
main_filter_[0]->DumpFilter("aec3_subtractor_H_main");
|
||||
shadow_filter_[0]->DumpFilter("aec3_subtractor_H_shadow");
|
||||
}
|
||||
|
||||
private:
|
||||
@ -115,15 +120,17 @@ class Subtractor {
|
||||
ApmDataDumper* data_dumper_;
|
||||
const Aec3Optimization optimization_;
|
||||
const EchoCanceller3Config config_;
|
||||
const size_t num_capture_channels_;
|
||||
|
||||
AdaptiveFirFilter main_filter_;
|
||||
AdaptiveFirFilter shadow_filter_;
|
||||
MainFilterUpdateGain G_main_;
|
||||
ShadowFilterUpdateGain G_shadow_;
|
||||
FilterMisadjustmentEstimator filter_misadjustment_estimator_;
|
||||
size_t poor_shadow_filter_counter_ = 0;
|
||||
std::vector<std::array<float, kFftLengthBy2Plus1>> main_frequency_response_;
|
||||
std::vector<float> main_impulse_response_;
|
||||
std::vector<std::unique_ptr<AdaptiveFirFilter>> main_filter_;
|
||||
std::vector<std::unique_ptr<AdaptiveFirFilter>> shadow_filter_;
|
||||
std::vector<std::unique_ptr<MainFilterUpdateGain>> G_main_;
|
||||
std::vector<std::unique_ptr<ShadowFilterUpdateGain>> G_shadow_;
|
||||
std::vector<FilterMisadjustmentEstimator> filter_misadjustment_estimator_;
|
||||
std::vector<size_t> poor_shadow_filter_counter_;
|
||||
std::vector<std::vector<std::array<float, kFftLengthBy2Plus1>>>
|
||||
main_frequency_response_;
|
||||
std::vector<std::vector<float>> main_impulse_response_;
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
@ -43,9 +43,9 @@ float RunSubtractorTest(int num_blocks_to_process,
|
||||
std::vector<std::vector<std::vector<float>>> x(
|
||||
kNumBands, std::vector<std::vector<float>>(
|
||||
kNumChannels, std::vector<float>(kBlockSize, 0.f)));
|
||||
std::vector<float> y(kBlockSize, 0.f);
|
||||
std::vector<std::vector<float>> y(1, std::vector<float>(kBlockSize, 0.f));
|
||||
std::array<float, kBlockSize> x_old;
|
||||
SubtractorOutput output;
|
||||
std::array<SubtractorOutput, 1> output;
|
||||
config.delay.default_delay = 1;
|
||||
std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
|
||||
RenderDelayBuffer::Create(config, kSampleRateHz, kNumChannels));
|
||||
@ -65,9 +65,9 @@ float RunSubtractorTest(int num_blocks_to_process,
|
||||
for (int k = 0; k < num_blocks_to_process; ++k) {
|
||||
RandomizeSampleVector(&random_generator, x[0][0]);
|
||||
if (uncorrelated_inputs) {
|
||||
RandomizeSampleVector(&random_generator, y);
|
||||
RandomizeSampleVector(&random_generator, y[0]);
|
||||
} else {
|
||||
delay_buffer.Delay(x[0][0], y);
|
||||
delay_buffer.Delay(x[0][0], y[0]);
|
||||
}
|
||||
render_delay_buffer->Insert(x);
|
||||
if (k == 0) {
|
||||
@ -86,19 +86,21 @@ float RunSubtractorTest(int num_blocks_to_process,
|
||||
false));
|
||||
}
|
||||
subtractor.Process(*render_delay_buffer->GetRenderBuffer(), y,
|
||||
render_signal_analyzer, aec_state, &output);
|
||||
render_signal_analyzer, aec_state, output);
|
||||
|
||||
aec_state.HandleEchoPathChange(EchoPathVariability(
|
||||
false, EchoPathVariability::DelayAdjustment::kNone, false));
|
||||
aec_state.Update(delay_estimate, subtractor.FilterFrequencyResponse(),
|
||||
subtractor.FilterImpulseResponse(),
|
||||
*render_delay_buffer->GetRenderBuffer(), E2_main, Y2,
|
||||
output, y);
|
||||
output[0], y[0]);
|
||||
}
|
||||
|
||||
const float output_power = std::inner_product(
|
||||
output.e_main.begin(), output.e_main.end(), output.e_main.begin(), 0.f);
|
||||
const float y_power = std::inner_product(y.begin(), y.end(), y.begin(), 0.f);
|
||||
const float output_power =
|
||||
std::inner_product(output[0].e_main.begin(), output[0].e_main.end(),
|
||||
output[0].e_main.begin(), 0.f);
|
||||
const float y_power =
|
||||
std::inner_product(y[0].begin(), y[0].end(), y[0].begin(), 0.f);
|
||||
if (y_power == 0.f) {
|
||||
ADD_FAILURE();
|
||||
return -1.0;
|
||||
@ -124,24 +126,6 @@ TEST(Subtractor, NullDataDumper) {
|
||||
"");
|
||||
}
|
||||
|
||||
// Verifies the check for null subtractor output.
|
||||
// TODO(peah): Re-enable the test once the issue with memory leaks during DEATH
|
||||
// tests on test bots has been fixed.
|
||||
TEST(Subtractor, DISABLED_NullOutput) {
|
||||
ApmDataDumper data_dumper(42);
|
||||
EchoCanceller3Config config;
|
||||
Subtractor subtractor(config, 1, 1, &data_dumper, DetectOptimization());
|
||||
std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
|
||||
RenderDelayBuffer::Create(config, 48000, 1));
|
||||
RenderSignalAnalyzer render_signal_analyzer(config);
|
||||
std::vector<float> y(kBlockSize, 0.f);
|
||||
|
||||
EXPECT_DEATH(
|
||||
subtractor.Process(*render_delay_buffer->GetRenderBuffer(), y,
|
||||
render_signal_analyzer, AecState(config), nullptr),
|
||||
"");
|
||||
}
|
||||
|
||||
// Verifies the check for the capture signal size.
|
||||
TEST(Subtractor, WrongCaptureSize) {
|
||||
ApmDataDumper data_dumper(42);
|
||||
@ -150,12 +134,12 @@ TEST(Subtractor, WrongCaptureSize) {
|
||||
std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
|
||||
RenderDelayBuffer::Create(config, 48000, 1));
|
||||
RenderSignalAnalyzer render_signal_analyzer(config);
|
||||
std::vector<float> y(kBlockSize - 1, 0.f);
|
||||
SubtractorOutput output;
|
||||
std::vector<std::vector<float>> y(1, std::vector<float>(kBlockSize - 1, 0.f));
|
||||
std::array<SubtractorOutput, 1> output;
|
||||
|
||||
EXPECT_DEATH(
|
||||
subtractor.Process(*render_delay_buffer->GetRenderBuffer(), y,
|
||||
render_signal_analyzer, AecState(config), &output),
|
||||
render_signal_analyzer, AecState(config), output),
|
||||
"");
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user