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:
Per Åhgren 2019-09-25 14:53:30 +02:00 committed by Commit Bot
parent 538ca57b23
commit 7bdf073c1c
4 changed files with 232 additions and 201 deletions

View File

@ -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;

View File

@ -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(

View File

@ -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

View File

@ -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),
"");
}