AEC3: 'Block' class

This change adds a Block class to reduce the need for std::vector<std::vector<std::vector<float>>>. This make the code
easier to read and less error prone.

It also enables future changes to the underlying data structure of a
block. For instance, the data of all bands and channels could be stored
in a single vector.

The change has been verified to be bit-exact.

Bug: webrtc:14089
Change-Id: Ied9a78124c0bbafe0e912017aef91f7c311de2ae
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/262252
Reviewed-by: Per Åhgren <peah@webrtc.org>
Commit-Queue: Gustaf Ullberg <gustaf@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#36968}
This commit is contained in:
Gustaf Ullberg 2022-05-23 10:39:53 +02:00 committed by WebRTC LUCI CQ
parent 742714870a
commit d3ead1a942
62 changed files with 663 additions and 937 deletions

View File

@ -22,6 +22,7 @@ rtc_library("aec3") {
"alignment_mixer.h",
"api_call_jitter_metrics.cc",
"api_call_jitter_metrics.h",
"block.h",
"block_buffer.cc",
"block_delay_buffer.cc",
"block_delay_buffer.h",
@ -182,6 +183,7 @@ rtc_source_set("aec3_fft") {
rtc_source_set("render_buffer") {
sources = [
"block.h",
"block_buffer.h",
"fft_buffer.h",
"render_buffer.h",

View File

@ -73,10 +73,7 @@ TEST_P(AdaptiveFirFilterOneTwoFourEightRenderChannels,
RenderDelayBuffer::Create(EchoCanceller3Config(), kSampleRateHz,
num_render_channels));
Random random_generator(42U);
std::vector<std::vector<std::vector<float>>> x(
kNumBands,
std::vector<std::vector<float>>(num_render_channels,
std::vector<float>(kBlockSize, 0.f)));
Block x(kNumBands, num_render_channels);
FftData S_C;
FftData S_Neon;
FftData G;
@ -92,10 +89,10 @@ TEST_P(AdaptiveFirFilterOneTwoFourEightRenderChannels,
}
}
for (size_t k = 0; k < 30; ++k) {
for (size_t band = 0; band < x.size(); ++band) {
for (size_t ch = 0; ch < x[band].size(); ++ch) {
RandomizeSampleVector(&random_generator, x[band][ch]);
for (int k = 0; k < 30; ++k) {
for (int band = 0; band < x.NumBands(); ++band) {
for (int ch = 0; ch < x.NumChannels(); ++ch) {
RandomizeSampleVector(&random_generator, x.View(band, ch));
}
}
render_delay_buffer->Insert(x);
@ -186,10 +183,7 @@ TEST_P(AdaptiveFirFilterOneTwoFourEightRenderChannels,
RenderDelayBuffer::Create(EchoCanceller3Config(), kSampleRateHz,
num_render_channels));
Random random_generator(42U);
std::vector<std::vector<std::vector<float>>> x(
kNumBands,
std::vector<std::vector<float>>(num_render_channels,
std::vector<float>(kBlockSize, 0.f)));
Block x(kNumBands, num_render_channels);
FftData S_C;
FftData S_Sse2;
FftData G;
@ -206,9 +200,9 @@ TEST_P(AdaptiveFirFilterOneTwoFourEightRenderChannels,
}
for (size_t k = 0; k < 500; ++k) {
for (size_t band = 0; band < x.size(); ++band) {
for (size_t ch = 0; ch < x[band].size(); ++ch) {
RandomizeSampleVector(&random_generator, x[band][ch]);
for (int band = 0; band < x.NumBands(); ++band) {
for (int ch = 0; ch < x.NumChannels(); ++ch) {
RandomizeSampleVector(&random_generator, x.View(band, ch));
}
}
render_delay_buffer->Insert(x);
@ -261,10 +255,7 @@ TEST_P(AdaptiveFirFilterOneTwoFourEightRenderChannels,
RenderDelayBuffer::Create(EchoCanceller3Config(), kSampleRateHz,
num_render_channels));
Random random_generator(42U);
std::vector<std::vector<std::vector<float>>> x(
kNumBands,
std::vector<std::vector<float>>(num_render_channels,
std::vector<float>(kBlockSize, 0.f)));
Block x(kNumBands, num_render_channels);
FftData S_C;
FftData S_Avx2;
FftData G;
@ -281,9 +272,9 @@ TEST_P(AdaptiveFirFilterOneTwoFourEightRenderChannels,
}
for (size_t k = 0; k < 500; ++k) {
for (size_t band = 0; band < x.size(); ++band) {
for (size_t ch = 0; ch < x[band].size(); ++ch) {
RandomizeSampleVector(&random_generator, x[band][ch]);
for (int band = 0; band < x.NumBands(); ++band) {
for (int ch = 0; ch < x.NumChannels(); ++ch) {
RandomizeSampleVector(&random_generator, x.View(band, ch));
}
}
render_delay_buffer->Insert(x);
@ -488,9 +479,7 @@ TEST_P(AdaptiveFirFilterMultiChannel, FilterAndAdapt) {
CoarseFilterUpdateGain gain(config.filter.coarse,
config.filter.config_change_duration_blocks);
Random random_generator(42U);
std::vector<std::vector<std::vector<float>>> x(
kNumBands, std::vector<std::vector<float>>(
num_render_channels, std::vector<float>(kBlockSize, 0.f)));
Block x(kNumBands, num_render_channels);
std::vector<float> n(kBlockSize, 0.f);
std::vector<float> y(kBlockSize, 0.f);
AecState aec_state(EchoCanceller3Config{}, num_capture_channels);
@ -540,9 +529,9 @@ TEST_P(AdaptiveFirFilterMultiChannel, FilterAndAdapt) {
for (size_t j = 0; j < num_blocks_to_process; ++j) {
std::fill(y.begin(), y.end(), 0.f);
for (size_t ch = 0; ch < num_render_channels; ++ch) {
RandomizeSampleVector(&random_generator, x[0][ch]);
RandomizeSampleVector(&random_generator, x.View(/*band=*/0, ch));
std::array<float, kBlockSize> y_channel;
delay_buffer[ch].Delay(x[0][ch], y_channel);
delay_buffer[ch].Delay(x.View(/*band=*/0, ch), y_channel);
for (size_t k = 0; k < y.size(); ++k) {
y[k] += y_channel[k] / num_render_channels;
}
@ -555,7 +544,7 @@ TEST_P(AdaptiveFirFilterMultiChannel, FilterAndAdapt) {
}
for (size_t ch = 0; ch < num_render_channels; ++ch) {
x_hp_filter[ch]->Process(x[0][ch]);
x_hp_filter[ch]->Process(x.View(/*band=*/0, ch));
}
y_hp_filter.Process(y);

View File

@ -206,15 +206,16 @@ void AecState::Update(
strong_not_saturated_render_blocks_);
}
const std::vector<std::vector<float>>& aligned_render_block =
render_buffer.Block(-delay_state_.MinDirectPathFilterDelay())[0];
const Block& aligned_render_block =
render_buffer.Block(-delay_state_.MinDirectPathFilterDelay());
// Update render counters.
bool active_render = false;
for (size_t ch = 0; ch < aligned_render_block.size(); ++ch) {
const float render_energy = std::inner_product(
aligned_render_block[ch].begin(), aligned_render_block[ch].end(),
aligned_render_block[ch].begin(), 0.f);
for (int ch = 0; ch < aligned_render_block.NumChannels(); ++ch) {
const float render_energy =
std::inner_product(aligned_render_block.begin(/*block=*/0, ch),
aligned_render_block.end(/*block=*/0, ch),
aligned_render_block.begin(/*block=*/0, ch), 0.f);
if (render_energy > (config_.render_levels.active_render_limit *
config_.render_levels.active_render_limit) *
kFftLengthBy2) {
@ -446,7 +447,7 @@ void AecState::FilteringQualityAnalyzer::Update(
}
void AecState::SaturationDetector::Update(
rtc::ArrayView<const std::vector<float>> x,
const Block& x,
bool saturated_capture,
bool usable_linear_estimate,
rtc::ArrayView<const SubtractorOutput> subtractor_output,
@ -466,8 +467,9 @@ void AecState::SaturationDetector::Update(
}
} else {
float max_sample = 0.f;
for (auto& channel : x) {
for (float sample : channel) {
for (int ch = 0; ch < x.NumChannels(); ++ch) {
rtc::ArrayView<const float, kBlockSize> x_ch = x.View(/*band=*/0, ch);
for (float sample : x_ch) {
max_sample = std::max(max_sample, fabsf(sample));
}
}

View File

@ -272,7 +272,7 @@ class AecState {
bool SaturatedEcho() const { return saturated_echo_; }
// Updates the detection decision based on new data.
void Update(rtc::ArrayView<const std::vector<float>> x,
void Update(const Block& x,
bool saturated_capture,
bool usable_linear_estimate,
rtc::ArrayView<const SubtractorOutput> subtractor_output,

View File

@ -35,9 +35,7 @@ void RunNormalUsageTest(size_t num_render_channels,
std::vector<std::array<float, kFftLengthBy2Plus1>> E2_refined(
num_capture_channels);
std::vector<std::array<float, kFftLengthBy2Plus1>> Y2(num_capture_channels);
std::vector<std::vector<std::vector<float>>> x(
kNumBands, std::vector<std::vector<float>>(
num_render_channels, std::vector<float>(kBlockSize, 0.f)));
Block x(kNumBands, num_render_channels);
EchoPathVariability echo_path_variability(
false, EchoPathVariability::DelayAdjustment::kNone, false);
std::vector<std::array<float, kBlockSize>> y(num_capture_channels);
@ -72,7 +70,7 @@ void RunNormalUsageTest(size_t num_render_channels,
// Verify that linear AEC usability is true when the filter is converged
for (size_t band = 0; band < kNumBands; ++band) {
for (size_t ch = 0; ch < num_render_channels; ++ch) {
std::fill(x[band][ch].begin(), x[band][ch].end(), 101.f);
std::fill(x.begin(band, ch), x.end(band, ch), 101.f);
}
}
for (int k = 0; k < 3000; ++k) {
@ -100,7 +98,7 @@ void RunNormalUsageTest(size_t num_render_channels,
// Verify that the active render detection works as intended.
for (size_t ch = 0; ch < num_render_channels; ++ch) {
std::fill(x[0][ch].begin(), x[0][ch].end(), 101.f);
std::fill(x.begin(0, ch), x.end(0, ch), 101.f);
}
render_delay_buffer->Insert(x);
for (size_t ch = 0; ch < num_capture_channels; ++ch) {
@ -125,14 +123,14 @@ void RunNormalUsageTest(size_t num_render_channels,
EXPECT_TRUE(state.ActiveRender());
// Verify that the ERL is properly estimated
for (auto& band : x) {
for (auto& channel : band) {
channel = std::vector<float>(kBlockSize, 0.f);
for (int band = 0; band < x.NumBands(); ++band) {
for (int channel = 0; channel < x.NumChannels(); ++channel) {
std::fill(x.begin(band, channel), x.end(band, channel), 0.0f);
}
}
for (size_t ch = 0; ch < num_render_channels; ++ch) {
x[0][ch][0] = 5000.f;
x.View(/*band=*/0, ch)[0] = 5000.f;
}
for (size_t k = 0;
k < render_delay_buffer->GetRenderBuffer()->GetFftBuffer().size(); ++k) {
@ -266,9 +264,9 @@ TEST(AecState, ConvergedFilterDelay) {
y.fill(0.f);
std::vector<std::vector<std::array<float, kFftLengthBy2Plus1>>>
frequency_response(
kNumCaptureChannels,
std::vector<std::array<float, kFftLengthBy2Plus1>>(kFilterLengthBlocks));
frequency_response(kNumCaptureChannels,
std::vector<std::array<float, kFftLengthBy2Plus1>>(
kFilterLengthBlocks));
for (auto& v_ch : frequency_response) {
for (auto& v : v_ch) {
v.fill(0.01f);

View File

@ -63,9 +63,10 @@ AlignmentMixer::AlignmentMixer(size_t num_channels,
}
}
void AlignmentMixer::ProduceOutput(rtc::ArrayView<const std::vector<float>> x,
void AlignmentMixer::ProduceOutput(const Block& x,
rtc::ArrayView<float, kBlockSize> y) {
RTC_DCHECK_EQ(x.size(), num_channels_);
RTC_DCHECK_EQ(x.NumChannels(), num_channels_);
if (selection_variant_ == MixingVariant::kDownmix) {
Downmix(x, y);
return;
@ -73,18 +74,20 @@ void AlignmentMixer::ProduceOutput(rtc::ArrayView<const std::vector<float>> x,
int ch = selection_variant_ == MixingVariant::kFixed ? 0 : SelectChannel(x);
RTC_DCHECK_GE(x.size(), ch);
std::copy(x[ch].begin(), x[ch].end(), y.begin());
RTC_DCHECK_GT(x.NumChannels(), ch);
std::copy(x.begin(/*band=*/0, ch), x.end(/*band=*/0, ch), y.begin());
}
void AlignmentMixer::Downmix(rtc::ArrayView<const std::vector<float>> x,
void AlignmentMixer::Downmix(const Block& x,
rtc::ArrayView<float, kBlockSize> y) const {
RTC_DCHECK_EQ(x.size(), num_channels_);
RTC_DCHECK_EQ(x.NumChannels(), num_channels_);
RTC_DCHECK_GE(num_channels_, 2);
std::copy(x[0].begin(), x[0].end(), y.begin());
std::memcpy(&y[0], x.View(/*band=*/0, /*channel=*/0).data(),
kBlockSize * sizeof(y[0]));
for (size_t ch = 1; ch < num_channels_; ++ch) {
const auto x_ch = x.View(/*band=*/0, ch);
for (size_t i = 0; i < kBlockSize; ++i) {
y[i] += x[ch][i];
y[i] += x_ch[i];
}
}
@ -93,8 +96,8 @@ void AlignmentMixer::Downmix(rtc::ArrayView<const std::vector<float>> x,
}
}
int AlignmentMixer::SelectChannel(rtc::ArrayView<const std::vector<float>> x) {
RTC_DCHECK_EQ(x.size(), num_channels_);
int AlignmentMixer::SelectChannel(const Block& x) {
RTC_DCHECK_EQ(x.NumChannels(), num_channels_);
RTC_DCHECK_GE(num_channels_, 2);
RTC_DCHECK_EQ(cumulative_energies_.size(), num_channels_);
@ -112,10 +115,10 @@ int AlignmentMixer::SelectChannel(rtc::ArrayView<const std::vector<float>> x) {
++block_counter_;
for (int ch = 0; ch < num_ch_to_analyze; ++ch) {
RTC_DCHECK_EQ(x[ch].size(), kBlockSize);
float x2_sum = 0.f;
rtc::ArrayView<const float, kBlockSize> x_ch = x.View(/*band=*/0, ch);
for (size_t i = 0; i < kBlockSize; ++i) {
x2_sum += x[ch][i] * x[ch][i];
x2_sum += x_ch[i] * x_ch[i];
}
if (ch < 2 && x2_sum > excitation_energy_threshold_) {

View File

@ -16,6 +16,7 @@
#include "api/array_view.h"
#include "api/audio/echo_canceller3_config.h"
#include "modules/audio_processing/aec3/aec3_common.h"
#include "modules/audio_processing/aec3/block.h"
namespace webrtc {
@ -33,8 +34,7 @@ class AlignmentMixer {
float excitation_limit,
bool prefer_first_two_channels);
void ProduceOutput(rtc::ArrayView<const std::vector<float>> x,
rtc::ArrayView<float, kBlockSize> y);
void ProduceOutput(const Block& x, rtc::ArrayView<float, kBlockSize> y);
enum class MixingVariant { kDownmix, kAdaptive, kFixed };
@ -49,9 +49,8 @@ class AlignmentMixer {
int selected_channel_ = 0;
size_t block_counter_ = 0;
void Downmix(rtc::ArrayView<const std::vector<float>> x,
rtc::ArrayView<float, kBlockSize> y) const;
int SelectChannel(rtc::ArrayView<const std::vector<float>> x);
void Downmix(const Block& x, rtc::ArrayView<float, kBlockSize> y) const;
int SelectChannel(const Block& x);
};
} // namespace webrtc

View File

@ -60,12 +60,9 @@ TEST(AlignmentMixer, GeneralAdaptiveMode) {
/*adaptive_selection*/ true, excitation_limit,
prefer_first_two_channels);
std::vector<std::vector<float>> x(
num_channels, std::vector<float>(kBlockSize, 0.f));
Block x(
/*num_bands=*/1, num_channels);
if (initial_silence) {
for (int ch = 0; ch < num_channels; ++ch) {
std::fill(x[ch].begin(), x[ch].end(), 0.f);
}
std::array<float, kBlockSize> y;
for (int frame = 0; frame < 10 * kNumBlocksPerSecond; ++frame) {
am.ProduceOutput(x, y);
@ -82,7 +79,8 @@ TEST(AlignmentMixer, GeneralAdaptiveMode) {
for (int ch = 0; ch < num_channels; ++ch) {
float scaling =
ch == strongest_ch ? kStrongestSignalScaling : 1.f;
std::fill(x[ch].begin(), x[ch].end(),
auto x_ch = x.View(/*band=*/0, ch);
std::fill(x_ch.begin(), x_ch.end(),
channel_value(frame, ch) * scaling);
}
@ -92,13 +90,15 @@ TEST(AlignmentMixer, GeneralAdaptiveMode) {
if (frame > 1 * kNumBlocksPerSecond) {
if (!prefer_first_two_channels || huge_activity_threshold) {
EXPECT_THAT(y, AllOf(Each(x[strongest_ch][0])));
EXPECT_THAT(y,
AllOf(Each(x.View(/*band=*/0, strongest_ch)[0])));
} else {
bool left_or_right_chosen;
for (int ch = 0; ch < 2; ++ch) {
left_or_right_chosen = true;
const auto x_ch = x.View(/*band=*/0, ch);
for (size_t k = 0; k < kBlockSize; ++k) {
if (y[k] != x[ch][k]) {
if (y[k] != x_ch[k]) {
left_or_right_chosen = false;
break;
}
@ -124,14 +124,14 @@ TEST(AlignmentMixer, DownmixMode) {
/*adaptive_selection*/ false, /*excitation_limit*/ 1.f,
/*prefer_first_two_channels*/ false);
std::vector<std::vector<float>> x(num_channels,
std::vector<float>(kBlockSize, 0.f));
Block x(/*num_bands=*/1, num_channels);
const auto channel_value = [](int frame_index, int channel_index) {
return static_cast<float>(frame_index + channel_index);
};
for (int frame = 0; frame < 10; ++frame) {
for (int ch = 0; ch < num_channels; ++ch) {
std::fill(x[ch].begin(), x[ch].end(), channel_value(frame, ch));
auto x_ch = x.View(/*band=*/0, ch);
std::fill(x_ch.begin(), x_ch.end(), channel_value(frame, ch));
}
std::array<float, kBlockSize> y;
@ -155,20 +155,20 @@ TEST(AlignmentMixer, FixedMode) {
/*adaptive_selection*/ false, /*excitation_limit*/ 1.f,
/*prefer_first_two_channels*/ false);
std::vector<std::vector<float>> x(num_channels,
std::vector<float>(kBlockSize, 0.f));
Block x(/*num_band=*/1, num_channels);
const auto channel_value = [](int frame_index, int channel_index) {
return static_cast<float>(frame_index + channel_index);
};
for (int frame = 0; frame < 10; ++frame) {
for (int ch = 0; ch < num_channels; ++ch) {
std::fill(x[ch].begin(), x[ch].end(), channel_value(frame, ch));
auto x_ch = x.View(/*band=*/0, ch);
std::fill(x_ch.begin(), x_ch.end(), channel_value(frame, ch));
}
std::array<float, kBlockSize> y;
y.fill(-1.f);
am.ProduceOutput(x, y);
EXPECT_THAT(y, AllOf(Each(x[0][0])));
EXPECT_THAT(y, AllOf(Each(x.View(/*band=*/0, /*channel=*/0)[0])));
}
}
}

View File

@ -0,0 +1,73 @@
/*
* Copyright (c) 2022 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_H_
#define MODULES_AUDIO_PROCESSING_AEC3_BLOCK_H_
#include <array>
#include <vector>
#include "api/array_view.h"
#include "modules/audio_processing/aec3/aec3_common.h"
namespace webrtc {
class Block {
public:
Block(int num_bands, int num_channels, float default_value = 0.0f)
: data_(num_bands,
std::vector<std::array<float, kBlockSize>>(
num_channels,
std::array<float, kBlockSize>({default_value}))) {}
// Returns the number of bands.
int NumBands() const { return data_.size(); }
// Returns the number of channels.
int NumChannels() const { return data_[0].size(); }
// Modifies the number of channels.
void SetNumChannels(int num_channels) {
for (std::vector<std::array<float, kBlockSize>>& block_band : data_) {
block_band.resize(num_channels, std::array<float, kBlockSize>({0.0f}));
}
}
// Iterators for accessing the data.
auto begin(int band, int channel) { return data_[band][channel].begin(); }
auto begin(int band, int channel) const {
return data_[band][channel].begin();
}
auto end(int band, int channel) { return data_[band][channel].end(); }
auto end(int band, int channel) const { return data_[band][channel].end(); }
// Access data via ArrayView.
rtc::ArrayView<float, kBlockSize> View(int band, int channel) {
return rtc::ArrayView<float, kBlockSize>(data_[band][channel].data(),
kBlockSize);
}
rtc::ArrayView<const float, kBlockSize> View(int band, int channel) const {
return rtc::ArrayView<const float, kBlockSize>(data_[band][channel].data(),
kBlockSize);
}
// Lets two Blocks swap audio data.
void Swap(Block& b) { data_.swap(b.data_); }
private:
std::vector<std::vector<std::array<float, kBlockSize>>> data_;
};
} // namespace webrtc
#endif // MODULES_AUDIO_PROCESSING_AEC3_BLOCK_H_

View File

@ -14,25 +14,9 @@
namespace webrtc {
BlockBuffer::BlockBuffer(size_t size,
size_t num_bands,
size_t num_channels,
size_t frame_length)
BlockBuffer::BlockBuffer(size_t size, size_t num_bands, size_t num_channels)
: size(static_cast<int>(size)),
buffer(size,
std::vector<std::vector<std::vector<float>>>(
num_bands,
std::vector<std::vector<float>>(
num_channels,
std::vector<float>(frame_length, 0.f)))) {
for (auto& block : buffer) {
for (auto& band : block) {
for (auto& channel : band) {
std::fill(channel.begin(), channel.end(), 0.f);
}
}
}
}
buffer(size, Block(num_bands, num_channels)) {}
BlockBuffer::~BlockBuffer() = default;

View File

@ -15,6 +15,7 @@
#include <vector>
#include "modules/audio_processing/aec3/block.h"
#include "rtc_base/checks.h"
namespace webrtc {
@ -22,10 +23,7 @@ namespace webrtc {
// Struct for bundling a circular buffer of two dimensional vector objects
// together with the read and write indices.
struct BlockBuffer {
BlockBuffer(size_t size,
size_t num_bands,
size_t num_channels,
size_t frame_length);
BlockBuffer(size_t size, size_t num_bands, size_t num_channels);
~BlockBuffer();
int IncIndex(int index) const {
@ -52,7 +50,7 @@ struct BlockBuffer {
void DecReadIndex() { read = DecIndex(read); }
const int size;
std::vector<std::vector<std::vector<std::vector<float>>>> buffer;
std::vector<Block> buffer;
int write = 0;
int read = 0;
};

View File

@ -34,35 +34,32 @@ BlockFramer::~BlockFramer() = default;
// samples for InsertBlockAndExtractSubFrame to produce a frame. In order to
// achieve this, the InsertBlockAndExtractSubFrame and InsertBlock methods need
// to be called in the correct order.
void BlockFramer::InsertBlock(
const std::vector<std::vector<std::vector<float>>>& block) {
RTC_DCHECK_EQ(num_bands_, block.size());
void BlockFramer::InsertBlock(const Block& block) {
RTC_DCHECK_EQ(num_bands_, block.NumBands());
RTC_DCHECK_EQ(num_channels_, block.NumChannels());
for (size_t band = 0; band < num_bands_; ++band) {
RTC_DCHECK_EQ(num_channels_, block[band].size());
for (size_t channel = 0; channel < num_channels_; ++channel) {
RTC_DCHECK_EQ(kBlockSize, block[band][channel].size());
RTC_DCHECK_EQ(0, buffer_[band][channel].size());
buffer_[band][channel].insert(buffer_[band][channel].begin(),
block[band][channel].begin(),
block[band][channel].end());
block.begin(band, channel),
block.end(band, channel));
}
}
}
void BlockFramer::InsertBlockAndExtractSubFrame(
const std::vector<std::vector<std::vector<float>>>& block,
const Block& block,
std::vector<std::vector<rtc::ArrayView<float>>>* sub_frame) {
RTC_DCHECK(sub_frame);
RTC_DCHECK_EQ(num_bands_, block.size());
RTC_DCHECK_EQ(num_bands_, block.NumBands());
RTC_DCHECK_EQ(num_channels_, block.NumChannels());
RTC_DCHECK_EQ(num_bands_, sub_frame->size());
for (size_t band = 0; band < num_bands_; ++band) {
RTC_DCHECK_EQ(num_channels_, block[band].size());
RTC_DCHECK_EQ(num_channels_, (*sub_frame)[0].size());
for (size_t channel = 0; channel < num_channels_; ++channel) {
RTC_DCHECK_LE(kSubFrameLength,
buffer_[band][channel].size() + kBlockSize);
RTC_DCHECK_EQ(kBlockSize, block[band][channel].size());
RTC_DCHECK_GE(kBlockSize, buffer_[band][channel].size());
RTC_DCHECK_EQ(kSubFrameLength, (*sub_frame)[band][channel].size());
@ -71,14 +68,14 @@ void BlockFramer::InsertBlockAndExtractSubFrame(
std::copy(buffer_[band][channel].begin(), buffer_[band][channel].end(),
(*sub_frame)[band][channel].begin());
std::copy(
block[band][channel].begin(),
block[band][channel].begin() + samples_to_frame,
block.begin(band, channel),
block.begin(band, channel) + samples_to_frame,
(*sub_frame)[band][channel].begin() + buffer_[band][channel].size());
buffer_[band][channel].clear();
buffer_[band][channel].insert(
buffer_[band][channel].begin(),
block[band][channel].begin() + samples_to_frame,
block[band][channel].end());
block.begin(band, channel) + samples_to_frame,
block.end(band, channel));
}
}
}

View File

@ -15,6 +15,7 @@
#include "api/array_view.h"
#include "modules/audio_processing/aec3/aec3_common.h"
#include "modules/audio_processing/aec3/block.h"
namespace webrtc {
@ -32,10 +33,10 @@ class BlockFramer {
BlockFramer& operator=(const BlockFramer&) = delete;
// Adds a 64 sample block into the data that will form the next output frame.
void InsertBlock(const std::vector<std::vector<std::vector<float>>>& block);
void InsertBlock(const Block& block);
// Adds a 64 sample block and extracts an 80 sample subframe.
void InsertBlockAndExtractSubFrame(
const std::vector<std::vector<std::vector<float>>>& block,
const Block& block,
std::vector<std::vector<rtc::ArrayView<float>>>* sub_frame);
private:

View File

@ -64,14 +64,13 @@ bool VerifySubFrame(
return true;
}
void FillBlock(size_t block_counter,
std::vector<std::vector<std::vector<float>>>* block) {
for (size_t band = 0; band < block->size(); ++band) {
for (size_t channel = 0; channel < (*block)[band].size(); ++channel) {
for (size_t sample = 0; sample < (*block)[band][channel].size();
++sample) {
(*block)[band][channel][sample] = ComputeSampleValue(
block_counter, kBlockSize, band, channel, sample, 0);
void FillBlock(size_t block_counter, Block* block) {
for (int band = 0; band < block->NumBands(); ++band) {
for (int channel = 0; channel < block->NumChannels(); ++channel) {
auto b = block->View(band, channel);
for (size_t sample = 0; sample < kBlockSize; ++sample) {
b[sample] = ComputeSampleValue(block_counter, kBlockSize, band, channel,
sample, 0);
}
}
}
@ -82,9 +81,7 @@ void RunFramerTest(int sample_rate_hz, size_t num_channels) {
constexpr size_t kNumSubFramesToProcess = 10;
const size_t num_bands = NumBandsForRate(sample_rate_hz);
std::vector<std::vector<std::vector<float>>> block(
num_bands, std::vector<std::vector<float>>(
num_channels, std::vector<float>(kBlockSize, 0.f)));
Block block(num_bands, num_channels);
std::vector<std::vector<std::vector<float>>> output_sub_frame(
num_bands, std::vector<std::vector<float>>(
num_channels, std::vector<float>(kSubFrameLength, 0.f)));
@ -117,16 +114,12 @@ void RunWronglySizedInsertAndExtractParametersTest(
size_t correct_num_channels,
size_t num_block_bands,
size_t num_block_channels,
size_t block_length,
size_t num_sub_frame_bands,
size_t num_sub_frame_channels,
size_t sub_frame_length) {
const size_t correct_num_bands = NumBandsForRate(sample_rate_hz);
std::vector<std::vector<std::vector<float>>> block(
num_block_bands,
std::vector<std::vector<float>>(num_block_channels,
std::vector<float>(block_length, 0.f)));
Block block(num_block_bands, num_block_channels);
std::vector<std::vector<std::vector<float>>> output_sub_frame(
num_sub_frame_bands,
std::vector<std::vector<float>>(
@ -145,18 +138,11 @@ void RunWronglySizedInsertAndExtractParametersTest(
void RunWronglySizedInsertParameterTest(int sample_rate_hz,
size_t correct_num_channels,
size_t num_block_bands,
size_t num_block_channels,
size_t block_length) {
size_t num_block_channels) {
const size_t correct_num_bands = NumBandsForRate(sample_rate_hz);
std::vector<std::vector<std::vector<float>>> correct_block(
correct_num_bands,
std::vector<std::vector<float>>(correct_num_channels,
std::vector<float>(kBlockSize, 0.f)));
std::vector<std::vector<std::vector<float>>> wrong_block(
num_block_bands,
std::vector<std::vector<float>>(num_block_channels,
std::vector<float>(block_length, 0.f)));
Block correct_block(correct_num_bands, correct_num_channels);
Block wrong_block(num_block_bands, num_block_channels);
std::vector<std::vector<std::vector<float>>> output_sub_frame(
correct_num_bands,
std::vector<std::vector<float>>(
@ -183,10 +169,7 @@ void RunWronglyInsertOrderTest(int sample_rate_hz,
size_t num_preceeding_api_calls) {
const size_t correct_num_bands = NumBandsForRate(sample_rate_hz);
std::vector<std::vector<std::vector<float>>> block(
correct_num_bands,
std::vector<std::vector<float>>(num_channels,
std::vector<float>(kBlockSize, 0.f)));
Block block(correct_num_bands, num_channels);
std::vector<std::vector<std::vector<float>>> output_sub_frame(
correct_num_bands,
std::vector<std::vector<float>>(
@ -223,7 +206,7 @@ TEST(BlockFramerDeathTest,
const size_t wrong_num_bands = (correct_num_bands % 3) + 1;
RunWronglySizedInsertAndExtractParametersTest(
rate, correct_num_channels, wrong_num_bands, correct_num_channels,
kBlockSize, correct_num_bands, correct_num_channels, kSubFrameLength);
correct_num_bands, correct_num_channels, kSubFrameLength);
}
}
}
@ -237,7 +220,7 @@ TEST(BlockFramerDeathTest,
const size_t wrong_num_channels = correct_num_channels + 1;
RunWronglySizedInsertAndExtractParametersTest(
rate, correct_num_channels, correct_num_bands, wrong_num_channels,
kBlockSize, correct_num_bands, correct_num_channels, kSubFrameLength);
correct_num_bands, correct_num_channels, kSubFrameLength);
}
}
}
@ -251,7 +234,7 @@ TEST(BlockFramerDeathTest,
const size_t wrong_num_bands = (correct_num_bands % 3) + 1;
RunWronglySizedInsertAndExtractParametersTest(
rate, correct_num_channels, correct_num_bands, correct_num_channels,
kBlockSize, wrong_num_bands, correct_num_channels, kSubFrameLength);
wrong_num_bands, correct_num_channels, kSubFrameLength);
}
}
}
@ -265,21 +248,7 @@ TEST(BlockFramerDeathTest,
const size_t wrong_num_channels = correct_num_channels + 1;
RunWronglySizedInsertAndExtractParametersTest(
rate, correct_num_channels, correct_num_bands, correct_num_channels,
kBlockSize, correct_num_bands, wrong_num_channels, kSubFrameLength);
}
}
}
TEST(BlockFramerDeathTest,
WrongNumberOfSamplesInBlockForInsertBlockAndExtractSubFrame) {
for (auto rate : {16000, 32000, 48000}) {
for (auto correct_num_channels : {1, 2, 8}) {
SCOPED_TRACE(ProduceDebugText(rate, correct_num_channels));
const size_t correct_num_bands = NumBandsForRate(rate);
RunWronglySizedInsertAndExtractParametersTest(
rate, correct_num_channels, correct_num_bands, correct_num_channels,
kBlockSize - 1, correct_num_bands, correct_num_channels,
kSubFrameLength);
correct_num_bands, wrong_num_channels, kSubFrameLength);
}
}
}
@ -292,8 +261,7 @@ TEST(BlockFramerDeathTest,
const size_t correct_num_bands = NumBandsForRate(rate);
RunWronglySizedInsertAndExtractParametersTest(
rate, correct_num_channels, correct_num_bands, correct_num_channels,
kBlockSize, correct_num_bands, correct_num_channels,
kSubFrameLength - 1);
correct_num_bands, correct_num_channels, kSubFrameLength - 1);
}
}
@ -304,8 +272,7 @@ TEST(BlockFramerDeathTest, WrongNumberOfBandsInBlockForInsertBlock) {
const size_t correct_num_bands = NumBandsForRate(rate);
const size_t wrong_num_bands = (correct_num_bands % 3) + 1;
RunWronglySizedInsertParameterTest(rate, correct_num_channels,
wrong_num_bands, correct_num_channels,
kBlockSize);
wrong_num_bands, correct_num_channels);
}
}
}
@ -317,20 +284,7 @@ TEST(BlockFramerDeathTest, WrongNumberOfChannelsInBlockForInsertBlock) {
const size_t correct_num_bands = NumBandsForRate(rate);
const size_t wrong_num_channels = correct_num_channels + 1;
RunWronglySizedInsertParameterTest(rate, correct_num_channels,
correct_num_bands, wrong_num_channels,
kBlockSize);
}
}
}
TEST(BlockFramerDeathTest, WrongNumberOfSamplesInBlockForInsertBlock) {
for (auto rate : {16000, 32000, 48000}) {
for (auto correct_num_channels : {1, 2, 8}) {
SCOPED_TRACE(ProduceDebugText(rate, correct_num_channels));
const size_t correct_num_bands = NumBandsForRate(rate);
RunWronglySizedInsertParameterTest(rate, correct_num_channels,
correct_num_bands,
correct_num_channels, kBlockSize - 1);
correct_num_bands, wrong_num_channels);
}
}
}
@ -364,11 +318,8 @@ TEST(BlockFramerDeathTest, ZeroNumberOfBandsParameter) {
// Verifies that the verification for null sub_frame pointer works.
TEST(BlockFramerDeathTest, NullSubFrameParameter) {
EXPECT_DEATH(BlockFramer(1, 1).InsertBlockAndExtractSubFrame(
std::vector<std::vector<std::vector<float>>>(
1, std::vector<std::vector<float>>(
1, std::vector<float>(kBlockSize, 0.f))),
nullptr),
EXPECT_DEATH(
BlockFramer(1, 1).InsertBlockAndExtractSubFrame(Block(1, 1), nullptr),
"");
}

View File

@ -49,14 +49,12 @@ class BlockProcessorImpl final : public BlockProcessor {
~BlockProcessorImpl() override;
void ProcessCapture(
bool echo_path_gain_change,
void ProcessCapture(bool echo_path_gain_change,
bool capture_signal_saturation,
std::vector<std::vector<std::vector<float>>>* linear_output,
std::vector<std::vector<std::vector<float>>>* capture_block) override;
Block* linear_output,
Block* capture_block) override;
void BufferRender(
const std::vector<std::vector<std::vector<float>>>& block) override;
void BufferRender(const Block& block) override;
void UpdateEchoLeakageStatus(bool leakage_detected) override;
@ -104,21 +102,20 @@ BlockProcessorImpl::BlockProcessorImpl(
BlockProcessorImpl::~BlockProcessorImpl() = default;
void BlockProcessorImpl::ProcessCapture(
bool echo_path_gain_change,
void BlockProcessorImpl::ProcessCapture(bool echo_path_gain_change,
bool capture_signal_saturation,
std::vector<std::vector<std::vector<float>>>* linear_output,
std::vector<std::vector<std::vector<float>>>* capture_block) {
Block* linear_output,
Block* capture_block) {
RTC_DCHECK(capture_block);
RTC_DCHECK_EQ(NumBandsForRate(sample_rate_hz_), capture_block->size());
RTC_DCHECK_EQ(kBlockSize, (*capture_block)[0][0].size());
RTC_DCHECK_EQ(NumBandsForRate(sample_rate_hz_), capture_block->NumBands());
capture_call_counter_++;
data_dumper_->DumpRaw("aec3_processblock_call_order",
static_cast<int>(BlockProcessorApiCall::kCapture));
data_dumper_->DumpWav("aec3_processblock_capture_input", kBlockSize,
&(*capture_block)[0][0][0], 16000, 1);
data_dumper_->DumpWav("aec3_processblock_capture_input",
capture_block->View(/*band=*/0, /*channel=*/0), 16000,
1);
if (render_properly_started_) {
if (!capture_properly_started_) {
@ -159,8 +156,9 @@ void BlockProcessorImpl::ProcessCapture(
delay_controller_->Reset(false);
}
data_dumper_->DumpWav("aec3_processblock_capture_input2", kBlockSize,
&(*capture_block)[0][0][0], 16000, 1);
data_dumper_->DumpWav("aec3_processblock_capture_input2",
capture_block->View(/*band=*/0, /*channel=*/0), 16000,
1);
bool has_delay_estimator = !config_.delay.use_external_delay_estimator;
if (has_delay_estimator) {
@ -169,7 +167,7 @@ void BlockProcessorImpl::ProcessCapture(
// alignment.
estimated_delay_ = delay_controller_->GetDelay(
render_buffer_->GetDownsampledRenderBuffer(), render_buffer_->Delay(),
(*capture_block)[0]);
*capture_block);
if (estimated_delay_) {
bool delay_change =
@ -202,16 +200,14 @@ void BlockProcessorImpl::ProcessCapture(
metrics_.UpdateCapture(false);
}
void BlockProcessorImpl::BufferRender(
const std::vector<std::vector<std::vector<float>>>& block) {
RTC_DCHECK_EQ(NumBandsForRate(sample_rate_hz_), block.size());
RTC_DCHECK_EQ(kBlockSize, block[0][0].size());
void BlockProcessorImpl::BufferRender(const Block& block) {
RTC_DCHECK_EQ(NumBandsForRate(sample_rate_hz_), block.NumBands());
data_dumper_->DumpRaw("aec3_processblock_call_order",
static_cast<int>(BlockProcessorApiCall::kRender));
data_dumper_->DumpWav("aec3_processblock_render_input", kBlockSize,
&block[0][0][0], 16000, 1);
data_dumper_->DumpWav("aec3_processblock_render_input2", kBlockSize,
&block[0][0][0], 16000, 1);
data_dumper_->DumpWav("aec3_processblock_render_input",
block.View(/*band=*/0, /*channel=*/0), 16000, 1);
data_dumper_->DumpWav("aec3_processblock_render_input2",
block.View(/*band=*/0, /*channel=*/0), 16000, 1);
render_event_ = render_buffer_->Insert(block);

View File

@ -18,6 +18,7 @@
#include "api/audio/echo_canceller3_config.h"
#include "api/audio/echo_control.h"
#include "modules/audio_processing/aec3/block.h"
#include "modules/audio_processing/aec3/echo_remover.h"
#include "modules/audio_processing/aec3/render_delay_buffer.h"
#include "modules/audio_processing/aec3/render_delay_controller.h"
@ -56,15 +57,13 @@ class BlockProcessor {
virtual void SetAudioBufferDelay(int delay_ms) = 0;
// Processes a block of capture data.
virtual void ProcessCapture(
bool echo_path_gain_change,
virtual void ProcessCapture(bool echo_path_gain_change,
bool capture_signal_saturation,
std::vector<std::vector<std::vector<float>>>* linear_output,
std::vector<std::vector<std::vector<float>>>* capture_block) = 0;
Block* linear_output,
Block* capture_block) = 0;
// Buffers a block of render data supplied by a FrameBlocker object.
virtual void BufferRender(
const std::vector<std::vector<std::vector<float>>>& render_block) = 0;
virtual void BufferRender(const Block& render_block) = 0;
// Reports whether echo leakage has been detected in the echo canceller
// output.

View File

@ -43,10 +43,7 @@ void RunBasicSetupAndApiCallTest(int sample_rate_hz, int num_iterations) {
std::unique_ptr<BlockProcessor> block_processor(
BlockProcessor::Create(EchoCanceller3Config(), sample_rate_hz,
kNumRenderChannels, kNumCaptureChannels));
std::vector<std::vector<std::vector<float>>> block(
NumBandsForRate(sample_rate_hz),
std::vector<std::vector<float>>(kNumRenderChannels,
std::vector<float>(kBlockSize, 1000.f)));
Block block(NumBandsForRate(sample_rate_hz), kNumRenderChannels, 1000.f);
for (int k = 0; k < num_iterations; ++k) {
block_processor->BufferRender(block);
block_processor->ProcessCapture(false, false, nullptr, &block);
@ -62,30 +59,11 @@ void RunRenderBlockSizeVerificationTest(int sample_rate_hz) {
std::unique_ptr<BlockProcessor> block_processor(
BlockProcessor::Create(EchoCanceller3Config(), sample_rate_hz,
kNumRenderChannels, kNumCaptureChannels));
std::vector<std::vector<std::vector<float>>> block(
NumBandsForRate(sample_rate_hz),
std::vector<std::vector<float>>(kNumRenderChannels,
std::vector<float>(kBlockSize - 1, 0.f)));
Block block(NumBandsForRate(sample_rate_hz), kNumRenderChannels);
EXPECT_DEATH(block_processor->BufferRender(block), "");
}
void RunCaptureBlockSizeVerificationTest(int sample_rate_hz) {
constexpr size_t kNumRenderChannels = 1;
constexpr size_t kNumCaptureChannels = 1;
std::unique_ptr<BlockProcessor> block_processor(
BlockProcessor::Create(EchoCanceller3Config(), sample_rate_hz,
kNumRenderChannels, kNumCaptureChannels));
std::vector<std::vector<std::vector<float>>> block(
NumBandsForRate(sample_rate_hz),
std::vector<std::vector<float>>(kNumRenderChannels,
std::vector<float>(kBlockSize - 1, 0.f)));
EXPECT_DEATH(block_processor->ProcessCapture(false, false, nullptr, &block),
"");
}
void RunRenderNumBandsVerificationTest(int sample_rate_hz) {
constexpr size_t kNumRenderChannels = 1;
constexpr size_t kNumCaptureChannels = 1;
@ -96,10 +74,7 @@ void RunRenderNumBandsVerificationTest(int sample_rate_hz) {
std::unique_ptr<BlockProcessor> block_processor(
BlockProcessor::Create(EchoCanceller3Config(), sample_rate_hz,
kNumRenderChannels, kNumCaptureChannels));
std::vector<std::vector<std::vector<float>>> block(
wrong_num_bands,
std::vector<std::vector<float>>(kNumRenderChannels,
std::vector<float>(kBlockSize, 0.f)));
Block block(wrong_num_bands, kNumRenderChannels);
EXPECT_DEATH(block_processor->BufferRender(block), "");
}
@ -114,10 +89,7 @@ void RunCaptureNumBandsVerificationTest(int sample_rate_hz) {
std::unique_ptr<BlockProcessor> block_processor(
BlockProcessor::Create(EchoCanceller3Config(), sample_rate_hz,
kNumRenderChannels, kNumCaptureChannels));
std::vector<std::vector<std::vector<float>>> block(
wrong_num_bands,
std::vector<std::vector<float>>(kNumRenderChannels,
std::vector<float>(kBlockSize, 0.f)));
Block block(wrong_num_bands, kNumRenderChannels);
EXPECT_DEATH(block_processor->ProcessCapture(false, false, nullptr, &block),
"");
@ -170,18 +142,14 @@ TEST(BlockProcessor, DISABLED_DelayControllerIntegration) {
EchoCanceller3Config(), rate, kNumRenderChannels, kNumCaptureChannels,
std::move(render_delay_buffer_mock)));
std::vector<std::vector<std::vector<float>>> render_block(
NumBandsForRate(rate),
std::vector<std::vector<float>>(kNumRenderChannels,
std::vector<float>(kBlockSize, 0.f)));
std::vector<std::vector<std::vector<float>>> capture_block(
NumBandsForRate(rate),
std::vector<std::vector<float>>(kNumCaptureChannels,
std::vector<float>(kBlockSize, 0.f)));
Block render_block(NumBandsForRate(rate), kNumRenderChannels);
Block capture_block(NumBandsForRate(rate), kNumCaptureChannels);
DelayBuffer<float> signal_delay_buffer(kDelayInSamples);
for (size_t k = 0; k < kNumBlocks; ++k) {
RandomizeSampleVector(&random_generator, render_block[0][0]);
signal_delay_buffer.Delay(render_block[0][0], capture_block[0][0]);
RandomizeSampleVector(&random_generator,
render_block.View(/*band=*/0, /*capture=*/0));
signal_delay_buffer.Delay(render_block.View(/*band=*/0, /*capture=*/0),
capture_block.View(/*band=*/0, /*capture=*/0));
block_processor->BufferRender(render_block);
block_processor->ProcessCapture(false, false, nullptr, &capture_block);
}
@ -228,18 +196,14 @@ TEST(BlockProcessor, DISABLED_SubmoduleIntegration) {
std::move(render_delay_buffer_mock),
std::move(render_delay_controller_mock), std::move(echo_remover_mock)));
std::vector<std::vector<std::vector<float>>> render_block(
NumBandsForRate(rate),
std::vector<std::vector<float>>(kNumRenderChannels,
std::vector<float>(kBlockSize, 0.f)));
std::vector<std::vector<std::vector<float>>> capture_block(
NumBandsForRate(rate),
std::vector<std::vector<float>>(kNumCaptureChannels,
std::vector<float>(kBlockSize, 0.f)));
Block render_block(NumBandsForRate(rate), kNumRenderChannels);
Block capture_block(NumBandsForRate(rate), kNumCaptureChannels);
DelayBuffer<float> signal_delay_buffer(640);
for (size_t k = 0; k < kNumBlocks; ++k) {
RandomizeSampleVector(&random_generator, render_block[0][0]);
signal_delay_buffer.Delay(render_block[0][0], capture_block[0][0]);
RandomizeSampleVector(&random_generator,
render_block.View(/*band=*/0, /*capture=*/0));
signal_delay_buffer.Delay(render_block.View(/*band=*/0, /*capture=*/0),
capture_block.View(/*band=*/0, /*capture=*/0));
block_processor->BufferRender(render_block);
block_processor->ProcessCapture(false, false, nullptr, &capture_block);
block_processor->UpdateEchoLeakageStatus(false);
@ -268,13 +232,6 @@ TEST(BlockProcessorDeathTest, DISABLED_VerifyRenderBlockSizeCheck) {
}
}
TEST(BlockProcessorDeathTest, VerifyCaptureBlockSizeCheck) {
for (auto rate : {16000, 32000, 48000}) {
SCOPED_TRACE(ProduceDebugText(rate));
RunCaptureBlockSizeVerificationTest(rate);
}
}
TEST(BlockProcessorDeathTest, VerifyRenderNumBandsCheck) {
for (auto rate : {16000, 32000, 48000}) {
SCOPED_TRACE(ProduceDebugText(rate));
@ -333,14 +290,8 @@ TEST(BlockProcessor, ExternalDelayAppliedCorrectlyWithInitialCaptureCalls) {
std::move(delay_buffer), /*delay_controller=*/nullptr,
std::move(echo_remover_mock)));
std::vector<std::vector<std::vector<float>>> render_block(
NumBandsForRate(kSampleRateHz),
std::vector<std::vector<float>>(kNumRenderChannels,
std::vector<float>(kBlockSize, 0.f)));
std::vector<std::vector<std::vector<float>>> capture_block(
NumBandsForRate(kSampleRateHz),
std::vector<std::vector<float>>(kNumCaptureChannels,
std::vector<float>(kBlockSize, 0.f)));
Block render_block(NumBandsForRate(kSampleRateHz), kNumRenderChannels);
Block capture_block(NumBandsForRate(kSampleRateHz), kNumCaptureChannels);
// Process...
// - 10 capture calls, where no render data is available,
@ -354,11 +305,12 @@ TEST(BlockProcessor, ExternalDelayAppliedCorrectlyWithInitialCaptureCalls) {
int render_call_counter = 0;
for (size_t k = 0; k < 10; ++k) {
FillSampleVector(++capture_call_counter, kDelayInBlocks,
capture_block[0][0]);
capture_block.View(/*band=*/0, /*capture=*/0));
block_processor->ProcessCapture(false, false, nullptr, &capture_block);
}
for (size_t k = 0; k < 10; ++k) {
FillSampleVector(++render_call_counter, 0, render_block[0][0]);
FillSampleVector(++render_call_counter, 0,
render_block.View(/*band=*/0, /*capture=*/0));
block_processor->BufferRender(render_block);
}
@ -367,19 +319,22 @@ TEST(BlockProcessor, ExternalDelayAppliedCorrectlyWithInitialCaptureCalls) {
[](EchoPathVariability /*echo_path_variability*/,
bool /*capture_signal_saturation*/,
const absl::optional<DelayEstimate>& /*external_delay*/,
RenderBuffer* render_buffer,
std::vector<std::vector<std::vector<float>>>* /*linear_output*/,
std::vector<std::vector<std::vector<float>>>* capture) {
RenderBuffer* render_buffer, Block* /*linear_output*/,
Block* capture) {
const auto& render = render_buffer->Block(0);
const auto render_view = render.View(/*band=*/0, /*channel=*/0);
const auto capture_view = capture->View(/*band=*/0, /*channel=*/0);
for (size_t i = 0; i < kBlockSize; ++i) {
EXPECT_FLOAT_EQ(render[0][0][i], (*capture)[0][0][i]);
EXPECT_FLOAT_EQ(render_view[i], capture_view[i]);
}
});
FillSampleVector(++capture_call_counter, kDelayInBlocks, capture_block[0][0]);
FillSampleVector(++capture_call_counter, kDelayInBlocks,
capture_block.View(/*band=*/0, /*capture=*/0));
block_processor->ProcessCapture(false, false, nullptr, &capture_block);
FillSampleVector(++capture_call_counter, kDelayInBlocks, capture_block[0][0]);
FillSampleVector(++capture_call_counter, kDelayInBlocks,
capture_block.View(/*band=*/0, /*capture=*/0));
block_processor->ProcessCapture(false, false, nullptr, &capture_block);
}

View File

@ -59,10 +59,7 @@ void RunFilterUpdateTest(int num_blocks_to_process,
CoarseFilterUpdateGain coarse_gain(
config.filter.coarse, config.filter.config_change_duration_blocks);
Random random_generator(42U);
std::vector<std::vector<std::vector<float>>> x(
NumBandsForRate(kSampleRateHz),
std::vector<std::vector<float>>(num_render_channels,
std::vector<float>(kBlockSize, 0.f)));
Block x(NumBandsForRate(kSampleRateHz), num_render_channels);
std::array<float, kBlockSize> y;
RenderSignalAnalyzer render_signal_analyzer(config);
std::array<float, kFftLength> s;
@ -81,12 +78,12 @@ void RunFilterUpdateTest(int num_blocks_to_process,
k) != blocks_with_saturation.end();
// Create the render signal.
for (size_t band = 0; band < x.size(); ++band) {
for (size_t channel = 0; channel < x[band].size(); ++channel) {
RandomizeSampleVector(&random_generator, x[band][channel]);
for (int band = 0; band < x.NumBands(); ++band) {
for (int channel = 0; channel < x.NumChannels(); ++channel) {
RandomizeSampleVector(&random_generator, x.View(band, channel));
}
}
delay_buffer.Delay(x[0][0], y);
delay_buffer.Delay(x.View(/*band=*/0, /*channel*/ 0), y);
render_delay_buffer->Insert(x);
if (k == 0) {

View File

@ -88,7 +88,7 @@ void EchoAudibility::UpdateRenderNoiseEstimator(
bool EchoAudibility::IsRenderTooLow(const BlockBuffer& block_buffer) {
const int num_render_channels =
static_cast<int>(block_buffer.buffer[0][0].size());
static_cast<int>(block_buffer.buffer[0].NumChannels());
bool too_low = false;
const int render_block_write_current = block_buffer.write;
if (render_block_write_current == render_block_write_prev_) {
@ -98,7 +98,8 @@ bool EchoAudibility::IsRenderTooLow(const BlockBuffer& block_buffer) {
idx = block_buffer.IncIndex(idx)) {
float max_abs_over_channels = 0.f;
for (int ch = 0; ch < num_render_channels; ++ch) {
auto block = block_buffer.buffer[idx][0][ch];
rtc::ArrayView<const float, kBlockSize> block =
block_buffer.buffer[idx].View(/*band=*/0, /*channel=*/ch);
auto r = std::minmax_element(block.cbegin(), block.cend());
float max_abs_channel =
std::max(std::fabs(*r.first), std::fabs(*r.second));

View File

@ -154,10 +154,10 @@ void ProcessCaptureFrameContent(
BlockFramer* linear_output_framer,
BlockFramer* output_framer,
BlockProcessor* block_processor,
std::vector<std::vector<std::vector<float>>>* linear_output_block,
Block* linear_output_block,
std::vector<std::vector<rtc::ArrayView<float>>>*
linear_output_sub_frame_view,
std::vector<std::vector<std::vector<float>>>* capture_block,
Block* capture_block,
std::vector<std::vector<rtc::ArrayView<float>>>* capture_sub_frame_view) {
FillSubFrameView(capture, sub_frame_index, capture_sub_frame_view);
@ -171,10 +171,10 @@ void ProcessCaptureFrameContent(
capture_blocker->InsertSubFrameAndExtractBlock(*capture_sub_frame_view,
capture_block);
block_processor->ProcessCapture(/*echo_path_gain_change=*/level_change ||
block_processor->ProcessCapture(
/*echo_path_gain_change=*/level_change ||
aec_reference_is_downmixed_stereo,
saturated_microphone_signal,
linear_output_block, capture_block);
saturated_microphone_signal, linear_output_block, capture_block);
output_framer->InsertBlockAndExtractSubFrame(*capture_block,
capture_sub_frame_view);
@ -185,16 +185,15 @@ void ProcessCaptureFrameContent(
}
}
void ProcessRemainingCaptureFrameContent(
bool level_change,
void ProcessRemainingCaptureFrameContent(bool level_change,
bool aec_reference_is_downmixed_stereo,
bool saturated_microphone_signal,
FrameBlocker* capture_blocker,
BlockFramer* linear_output_framer,
BlockFramer* output_framer,
BlockProcessor* block_processor,
std::vector<std::vector<std::vector<float>>>* linear_output_block,
std::vector<std::vector<std::vector<float>>>* block) {
Block* linear_output_block,
Block* block) {
if (!capture_blocker->IsBlockAvailable()) {
return;
}
@ -218,7 +217,7 @@ void BufferRenderFrameContent(
size_t sub_frame_index,
FrameBlocker* render_blocker,
BlockProcessor* block_processor,
std::vector<std::vector<std::vector<float>>>* block,
Block* block,
std::vector<std::vector<rtc::ArrayView<float>>>* sub_frame_view) {
FillSubFrameView(proper_downmix_needed, render_frame, sub_frame_index,
sub_frame_view);
@ -226,10 +225,9 @@ void BufferRenderFrameContent(
block_processor->BufferRender(*block);
}
void BufferRemainingRenderFrameContent(
FrameBlocker* render_blocker,
void BufferRemainingRenderFrameContent(FrameBlocker* render_blocker,
BlockProcessor* block_processor,
std::vector<std::vector<std::vector<float>>>* block) {
Block* block) {
if (!render_blocker->IsBlockAvailable()) {
return;
}
@ -753,14 +751,8 @@ EchoCanceller3::EchoCanceller3(
std::vector<std::vector<float>>(
num_render_input_channels_,
std::vector<float>(AudioBuffer::kSplitBandSize, 0.f))),
render_block_(
num_bands_,
std::vector<std::vector<float>>(num_render_input_channels_,
std::vector<float>(kBlockSize, 0.f))),
capture_block_(
num_bands_,
std::vector<std::vector<float>>(num_capture_channels_,
std::vector<float>(kBlockSize, 0.f))),
render_block_(num_bands_, num_render_input_channels_),
capture_block_(num_bands_, num_capture_channels_),
capture_sub_frame_view_(
num_bands_,
std::vector<rtc::ArrayView<float>>(num_capture_channels_)) {
@ -780,11 +772,10 @@ EchoCanceller3::EchoCanceller3(
RTC_DCHECK_GE(kMaxNumBands, num_bands_);
if (config_selector_.active_config().filter.export_linear_aec_output) {
linear_output_framer_.reset(new BlockFramer(1, num_capture_channels_));
linear_output_framer_.reset(
new BlockFramer(/*num_bands=*/1, num_capture_channels_));
linear_output_block_ =
std::make_unique<std::vector<std::vector<std::vector<float>>>>(
1, std::vector<std::vector<float>>(
num_capture_channels_, std::vector<float>(kBlockSize, 0.f)));
std::make_unique<Block>(/*num_bands=*/1, num_capture_channels_),
linear_output_sub_frame_view_ =
std::vector<std::vector<rtc::ArrayView<float>>>(
1, std::vector<rtc::ArrayView<float>>(num_capture_channels_));
@ -810,12 +801,7 @@ void EchoCanceller3::Initialize() {
config_selector_.Update(
multichannel_content_detector_.IsProperMultiChannelContentDetected());
for (std::vector<std::vector<float>>& block_band : render_block_) {
block_band.resize(num_render_channels_to_aec_);
for (std::vector<float>& block_channel : block_band) {
block_channel.resize(kBlockSize, 0.0f);
}
}
render_block_.SetNumChannels(num_render_channels_to_aec_);
render_blocker_.reset(
new FrameBlocker(num_bands_, num_render_channels_to_aec_));

View File

@ -210,12 +210,10 @@ class EchoCanceller3 : public EchoControl {
RTC_GUARDED_BY(capture_race_checker_);
bool saturated_microphone_signal_ RTC_GUARDED_BY(capture_race_checker_) =
false;
std::vector<std::vector<std::vector<float>>> render_block_
RTC_GUARDED_BY(capture_race_checker_);
std::unique_ptr<std::vector<std::vector<std::vector<float>>>>
linear_output_block_ RTC_GUARDED_BY(capture_race_checker_);
std::vector<std::vector<std::vector<float>>> capture_block_
Block render_block_ RTC_GUARDED_BY(capture_race_checker_);
std::unique_ptr<Block> linear_output_block_
RTC_GUARDED_BY(capture_race_checker_);
Block capture_block_ RTC_GUARDED_BY(capture_race_checker_);
std::vector<std::vector<rtc::ArrayView<float>>> render_sub_frame_view_
RTC_GUARDED_BY(capture_race_checker_);
std::vector<std::vector<rtc::ArrayView<float>>> linear_output_sub_frame_view_

View File

@ -117,14 +117,12 @@ class CaptureTransportVerificationProcessor : public BlockProcessor {
~CaptureTransportVerificationProcessor() override = default;
void ProcessCapture(
bool level_change,
void ProcessCapture(bool level_change,
bool saturated_microphone_signal,
std::vector<std::vector<std::vector<float>>>* linear_output,
std::vector<std::vector<std::vector<float>>>* capture_block) override {}
Block* linear_output,
Block* capture_block) override {}
void BufferRender(
const std::vector<std::vector<std::vector<float>>>& block) override {}
void BufferRender(const Block& block) override {}
void UpdateEchoLeakageStatus(bool leakage_detected) override {}
@ -149,19 +147,16 @@ class RenderTransportVerificationProcessor : public BlockProcessor {
~RenderTransportVerificationProcessor() override = default;
void ProcessCapture(
bool level_change,
void ProcessCapture(bool level_change,
bool saturated_microphone_signal,
std::vector<std::vector<std::vector<float>>>* linear_output,
std::vector<std::vector<std::vector<float>>>* capture_block) override {
std::vector<std::vector<std::vector<float>>> render_block =
received_render_blocks_.front();
Block* linear_output,
Block* capture_block) override {
Block render_block = received_render_blocks_.front();
received_render_blocks_.pop_front();
capture_block->swap(render_block);
capture_block->Swap(render_block);
}
void BufferRender(
const std::vector<std::vector<std::vector<float>>>& block) override {
void BufferRender(const Block& block) override {
received_render_blocks_.push_back(block);
}
@ -174,8 +169,7 @@ class RenderTransportVerificationProcessor : public BlockProcessor {
void SetCaptureOutputUsage(bool capture_output_used) {}
private:
std::deque<std::vector<std::vector<std::vector<float>>>>
received_render_blocks_;
std::deque<Block> received_render_blocks_;
};
std::string ProduceDebugText(int sample_rate_hz) {

View File

@ -59,9 +59,7 @@ void EchoPathDelayEstimator::Reset(bool reset_delay_confidence) {
absl::optional<DelayEstimate> EchoPathDelayEstimator::EstimateDelay(
const DownsampledRenderBuffer& render_buffer,
const std::vector<std::vector<float>>& capture) {
RTC_DCHECK_EQ(kBlockSize, capture[0].size());
const Block& capture) {
std::array<float, kBlockSize> downsampled_capture_data;
rtc::ArrayView<float> downsampled_capture(downsampled_capture_data.data(),
sub_block_size_);

View File

@ -16,6 +16,7 @@
#include "absl/types/optional.h"
#include "api/array_view.h"
#include "modules/audio_processing/aec3/alignment_mixer.h"
#include "modules/audio_processing/aec3/block.h"
#include "modules/audio_processing/aec3/clockdrift_detector.h"
#include "modules/audio_processing/aec3/decimator.h"
#include "modules/audio_processing/aec3/delay_estimate.h"
@ -46,7 +47,7 @@ class EchoPathDelayEstimator {
// Produce a delay estimate if such is avaliable.
absl::optional<DelayEstimate> EstimateDelay(
const DownsampledRenderBuffer& render_buffer,
const std::vector<std::vector<float>>& capture);
const Block& capture);
// Log delay estimator properties.
void LogDelayEstimationProperties(int sample_rate_hz, size_t shift) const {

View File

@ -54,11 +54,8 @@ TEST_P(EchoPathDelayEstimatorMultiChannel, BasicApiCalls) {
std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
RenderDelayBuffer::Create(config, kSampleRateHz, num_render_channels));
EchoPathDelayEstimator estimator(&data_dumper, config, num_capture_channels);
std::vector<std::vector<std::vector<float>>> render(
kNumBands, std::vector<std::vector<float>>(
num_render_channels, std::vector<float>(kBlockSize)));
std::vector<std::vector<float>> capture(num_capture_channels,
std::vector<float>(kBlockSize));
Block render(kNumBands, num_render_channels);
Block capture(/*num_bands=*/1, num_capture_channels);
for (size_t k = 0; k < 100; ++k) {
render_delay_buffer->Insert(render);
estimator.EstimateDelay(render_delay_buffer->GetDownsampledRenderBuffer(),
@ -75,11 +72,8 @@ TEST(EchoPathDelayEstimator, DelayEstimation) {
constexpr size_t kNumBands = NumBandsForRate(kSampleRateHz);
Random random_generator(42U);
std::vector<std::vector<std::vector<float>>> render(
kNumBands, std::vector<std::vector<float>>(
kNumRenderChannels, std::vector<float>(kBlockSize)));
std::vector<std::vector<float>> capture(kNumCaptureChannels,
std::vector<float>(kBlockSize));
Block render(kNumBands, kNumRenderChannels);
Block capture(/*num_bands=*/1, kNumCaptureChannels);
ApmDataDumper data_dumper(0);
constexpr size_t kDownSamplingFactors[] = {2, 4, 8};
for (auto down_sampling_factor : kDownSamplingFactors) {
@ -96,8 +90,10 @@ TEST(EchoPathDelayEstimator, DelayEstimation) {
absl::optional<DelayEstimate> estimated_delay_samples;
for (size_t k = 0; k < (500 + (delay_samples) / kBlockSize); ++k) {
RandomizeSampleVector(&random_generator, render[0][0]);
signal_delay_buffer.Delay(render[0][0], capture[0]);
RandomizeSampleVector(&random_generator,
render.View(/*band=*/0, /*channel=*/0));
signal_delay_buffer.Delay(render.View(/*band=*/0, /*channel=*/0),
capture.View(/*band=*/0, /*channel=*/0));
render_delay_buffer->Insert(render);
if (k == 0) {
@ -137,22 +133,22 @@ TEST(EchoPathDelayEstimator, NoDelayEstimatesForLowLevelRenderSignals) {
constexpr size_t kNumBands = NumBandsForRate(kSampleRateHz);
Random random_generator(42U);
EchoCanceller3Config config;
std::vector<std::vector<std::vector<float>>> render(
kNumBands, std::vector<std::vector<float>>(
kNumRenderChannels, std::vector<float>(kBlockSize)));
std::vector<std::vector<float>> capture(kNumCaptureChannels,
std::vector<float>(kBlockSize));
Block render(kNumBands, kNumRenderChannels);
Block capture(/*num_bands=*/1, kNumCaptureChannels);
ApmDataDumper data_dumper(0);
EchoPathDelayEstimator estimator(&data_dumper, config, kNumCaptureChannels);
std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
RenderDelayBuffer::Create(EchoCanceller3Config(), kSampleRateHz,
kNumRenderChannels));
for (size_t k = 0; k < 100; ++k) {
RandomizeSampleVector(&random_generator, render[0][0]);
for (auto& render_k : render[0][0]) {
RandomizeSampleVector(&random_generator,
render.View(/*band=*/0, /*channel=*/0));
for (auto& render_k : render.View(/*band=*/0, /*channel=*/0)) {
render_k *= 100.f / 32767.f;
}
std::copy(render[0][0].begin(), render[0][0].end(), capture[0].begin());
std::copy(render.begin(/*band=*/0, /*channel=*/0),
render.end(/*band=*/0, /*channel=*/0),
capture.begin(/*band*/ 0, /*channel=*/0));
render_delay_buffer->Insert(render);
render_delay_buffer->PrepareCaptureProcessing();
EXPECT_FALSE(estimator.EstimateDelay(
@ -171,23 +167,7 @@ TEST(EchoPathDelayEstimatorDeathTest, DISABLED_WrongRenderBlockSize) {
EchoPathDelayEstimator estimator(&data_dumper, config, 1);
std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
RenderDelayBuffer::Create(config, 48000, 1));
std::vector<std::vector<float>> capture(1, std::vector<float>(kBlockSize));
EXPECT_DEATH(estimator.EstimateDelay(
render_delay_buffer->GetDownsampledRenderBuffer(), capture),
"");
}
// Verifies the check for the capture blocksize.
// TODO(peah): Re-enable the test once the issue with memory leaks during DEATH
// tests on test bots has been fixed.
TEST(EchoPathDelayEstimatorDeathTest, WrongCaptureBlockSize) {
ApmDataDumper data_dumper(0);
EchoCanceller3Config config;
EchoPathDelayEstimator estimator(&data_dumper, config, 1);
std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
RenderDelayBuffer::Create(config, 48000, 1));
std::vector<std::vector<float>> capture(1,
std::vector<float>(kBlockSize - 1));
Block capture(/*num_bands=*/1, /*num_channels=*/1);
EXPECT_DEATH(estimator.EstimateDelay(
render_delay_buffer->GetDownsampledRenderBuffer(), capture),
"");

View File

@ -118,13 +118,12 @@ class EchoRemoverImpl final : public EchoRemover {
// Removes the echo from a block of samples from the capture signal. The
// supplied render signal is assumed to be pre-aligned with the capture
// signal.
void ProcessCapture(
EchoPathVariability echo_path_variability,
void ProcessCapture(EchoPathVariability echo_path_variability,
bool capture_signal_saturation,
const absl::optional<DelayEstimate>& external_delay,
RenderBuffer* render_buffer,
std::vector<std::vector<std::vector<float>>>* linear_output,
std::vector<std::vector<std::vector<float>>>* capture) override;
Block* linear_output,
Block* capture) override;
// Updates the status on whether echo leakage is detected in the output of the
// echo remover.
@ -243,20 +242,17 @@ void EchoRemoverImpl::ProcessCapture(
bool capture_signal_saturation,
const absl::optional<DelayEstimate>& external_delay,
RenderBuffer* render_buffer,
std::vector<std::vector<std::vector<float>>>* linear_output,
std::vector<std::vector<std::vector<float>>>* capture) {
Block* linear_output,
Block* capture) {
++block_counter_;
const std::vector<std::vector<std::vector<float>>>& x =
render_buffer->Block(0);
std::vector<std::vector<std::vector<float>>>* y = capture;
const Block& x = render_buffer->Block(0);
Block* y = capture;
RTC_DCHECK(render_buffer);
RTC_DCHECK(y);
RTC_DCHECK_EQ(x.size(), NumBandsForRate(sample_rate_hz_));
RTC_DCHECK_EQ(y->size(), NumBandsForRate(sample_rate_hz_));
RTC_DCHECK_EQ(x[0].size(), num_render_channels_);
RTC_DCHECK_EQ((*y)[0].size(), num_capture_channels_);
RTC_DCHECK_EQ(x[0][0].size(), kBlockSize);
RTC_DCHECK_EQ((*y)[0][0].size(), kBlockSize);
RTC_DCHECK_EQ(x.NumBands(), NumBandsForRate(sample_rate_hz_));
RTC_DCHECK_EQ(y->NumBands(), NumBandsForRate(sample_rate_hz_));
RTC_DCHECK_EQ(x.NumChannels(), num_render_channels_);
RTC_DCHECK_EQ(y->NumChannels(), num_capture_channels_);
// Stack allocated data to use when the number of channels is low.
std::array<std::array<float, kFftLengthBy2>, kMaxNumChannelsOnStack> e_stack;
@ -321,12 +317,14 @@ void EchoRemoverImpl::ProcessCapture(
subtractor_output_heap_.data(), num_capture_channels_);
}
data_dumper_->DumpWav("aec3_echo_remover_capture_input", kBlockSize,
&(*y)[0][0][0], 16000, 1);
data_dumper_->DumpWav("aec3_echo_remover_render_input", kBlockSize,
&x[0][0][0], 16000, 1);
data_dumper_->DumpRaw("aec3_echo_remover_capture_input", (*y)[0][0]);
data_dumper_->DumpRaw("aec3_echo_remover_render_input", x[0][0]);
data_dumper_->DumpWav("aec3_echo_remover_capture_input",
y->View(/*band=*/0, /*channel=*/0), 16000, 1);
data_dumper_->DumpWav("aec3_echo_remover_render_input",
x.View(/*band=*/0, /*channel=*/0), 16000, 1);
data_dumper_->DumpRaw("aec3_echo_remover_capture_input",
y->View(/*band=*/0, /*channel=*/0));
data_dumper_->DumpRaw("aec3_echo_remover_render_input",
x.View(/*band=*/0, /*channel=*/0));
aec_state_.UpdateCaptureSaturation(capture_signal_saturation);
@ -369,13 +367,13 @@ void EchoRemoverImpl::ProcessCapture(
}
// Perform linear echo cancellation.
subtractor_.Process(*render_buffer, (*y)[0], render_signal_analyzer_,
aec_state_, subtractor_output);
subtractor_.Process(*render_buffer, *y, render_signal_analyzer_, aec_state_,
subtractor_output);
// Compute spectra.
for (size_t ch = 0; ch < num_capture_channels_; ++ch) {
FormLinearFilterOutput(subtractor_output[ch], e[ch]);
WindowedPaddedFft(fft_, (*y)[0][ch], y_old_[ch], &Y[ch]);
WindowedPaddedFft(fft_, y->View(/*band=*/0, ch), y_old_[ch], &Y[ch]);
WindowedPaddedFft(fft_, e[ch], e_old_[ch], &E[ch]);
LinearEchoPower(E[ch], Y[ch], &S2_linear[ch]);
Y[ch].Spectrum(optimization_, Y2[ch]);
@ -384,11 +382,11 @@ void EchoRemoverImpl::ProcessCapture(
// Optionally return the linear filter output.
if (linear_output) {
RTC_DCHECK_GE(1, linear_output->size());
RTC_DCHECK_EQ(num_capture_channels_, linear_output[0].size());
RTC_DCHECK_GE(1, linear_output->NumBands());
RTC_DCHECK_EQ(num_capture_channels_, linear_output->NumChannels());
for (size_t ch = 0; ch < num_capture_channels_; ++ch) {
RTC_DCHECK_EQ(kBlockSize, (*linear_output)[0][ch].size());
std::copy(e[ch].begin(), e[ch].end(), (*linear_output)[0][ch].begin());
std::copy(e[ch].begin(), e[ch].end(),
linear_output->begin(/*band=*/0, ch));
}
}
@ -400,8 +398,8 @@ void EchoRemoverImpl::ProcessCapture(
// Choose the linear output.
const auto& Y_fft = aec_state_.UseLinearFilterOutput() ? E : Y;
data_dumper_->DumpWav("aec3_output_linear", kBlockSize, &(*y)[0][0][0], 16000,
1);
data_dumper_->DumpWav("aec3_output_linear",
y->View(/*band=*/0, /*channel=*/0), 16000, 1);
data_dumper_->DumpWav("aec3_output_linear2", kBlockSize, &e[0][0], 16000, 1);
// Estimate the comfort noise.
@ -455,13 +453,12 @@ void EchoRemoverImpl::ProcessCapture(
// Debug outputs for the purpose of development and analysis.
data_dumper_->DumpWav("aec3_echo_estimate", kBlockSize,
&subtractor_output[0].s_refined[0], 16000, 1);
data_dumper_->DumpRaw("aec3_output", (*y)[0][0]);
data_dumper_->DumpRaw("aec3_output", y->View(/*band=*/0, /*channel=*/0));
data_dumper_->DumpRaw("aec3_narrow_render",
render_signal_analyzer_.NarrowPeakBand() ? 1 : 0);
data_dumper_->DumpRaw("aec3_N2", cng_.NoiseSpectrum()[0]);
data_dumper_->DumpRaw("aec3_suppressor_gain", G);
data_dumper_->DumpWav("aec3_output",
rtc::ArrayView<const float>(&(*y)[0][0][0], kBlockSize),
data_dumper_->DumpWav("aec3_output", y->View(/*band=*/0, /*channel=*/0),
16000, 1);
data_dumper_->DumpRaw("aec3_using_subtractor_output[0]",
aec_state_.UseLinearFilterOutput() ? 1 : 0);

View File

@ -16,6 +16,7 @@
#include "absl/types/optional.h"
#include "api/audio/echo_canceller3_config.h"
#include "api/audio/echo_control.h"
#include "modules/audio_processing/aec3/block.h"
#include "modules/audio_processing/aec3/delay_estimate.h"
#include "modules/audio_processing/aec3/echo_path_variability.h"
#include "modules/audio_processing/aec3/render_buffer.h"
@ -42,8 +43,8 @@ class EchoRemover {
bool capture_signal_saturation,
const absl::optional<DelayEstimate>& external_delay,
RenderBuffer* render_buffer,
std::vector<std::vector<std::vector<float>>>* linear_output,
std::vector<std::vector<std::vector<float>>>* capture) = 0;
Block* linear_output,
Block* capture) = 0;
// Updates the status on whether echo leakage is detected in the output of the
// echo remover.

View File

@ -62,14 +62,8 @@ TEST_P(EchoRemoverMultiChannel, BasicApiCalls) {
std::unique_ptr<RenderDelayBuffer> render_buffer(RenderDelayBuffer::Create(
EchoCanceller3Config(), rate, num_render_channels));
std::vector<std::vector<std::vector<float>>> render(
NumBandsForRate(rate),
std::vector<std::vector<float>>(num_render_channels,
std::vector<float>(kBlockSize, 0.f)));
std::vector<std::vector<std::vector<float>>> capture(
NumBandsForRate(rate),
std::vector<std::vector<float>>(num_capture_channels,
std::vector<float>(kBlockSize, 0.f)));
Block render(NumBandsForRate(rate), num_render_channels);
Block capture(NumBandsForRate(rate), num_capture_channels);
for (size_t k = 0; k < 100; ++k) {
EchoPathVariability echo_path_variability(
k % 3 == 0 ? true : false,
@ -97,27 +91,6 @@ TEST(EchoRemoverDeathTest, DISABLED_WrongSampleRate) {
"");
}
// Verifies the check for the capture block size.
TEST(EchoRemoverDeathTest, WrongCaptureBlockSize) {
absl::optional<DelayEstimate> delay_estimate;
for (auto rate : {16000, 32000, 48000}) {
SCOPED_TRACE(ProduceDebugText(rate));
std::unique_ptr<EchoRemover> remover(
EchoRemover::Create(EchoCanceller3Config(), rate, 1, 1));
std::unique_ptr<RenderDelayBuffer> render_buffer(
RenderDelayBuffer::Create(EchoCanceller3Config(), rate, 1));
std::vector<std::vector<std::vector<float>>> capture(
NumBandsForRate(rate), std::vector<std::vector<float>>(
1, std::vector<float>(kBlockSize - 1, 0.f)));
EchoPathVariability echo_path_variability(
false, EchoPathVariability::DelayAdjustment::kNone, false);
EXPECT_DEATH(remover->ProcessCapture(
echo_path_variability, false, delay_estimate,
render_buffer->GetRenderBuffer(), nullptr, &capture),
"");
}
}
// Verifies the check for the number of capture bands.
// TODO(peah): Re-enable the test once the issue with memory leaks during DEATH
// tests on test bots has been fixed.c
@ -129,10 +102,7 @@ TEST(EchoRemoverDeathTest, DISABLED_WrongCaptureNumBands) {
EchoRemover::Create(EchoCanceller3Config(), rate, 1, 1));
std::unique_ptr<RenderDelayBuffer> render_buffer(
RenderDelayBuffer::Create(EchoCanceller3Config(), rate, 1));
std::vector<std::vector<std::vector<float>>> capture(
NumBandsForRate(rate == 48000 ? 16000 : rate + 16000),
std::vector<std::vector<float>>(1,
std::vector<float>(kBlockSize, 0.f)));
Block capture(NumBandsForRate(rate == 48000 ? 16000 : rate + 16000), 1);
EchoPathVariability echo_path_variability(
false, EchoPathVariability::DelayAdjustment::kNone, false);
EXPECT_DEATH(remover->ProcessCapture(
@ -167,14 +137,8 @@ TEST(EchoRemover, BasicEchoRemoval) {
absl::optional<DelayEstimate> delay_estimate;
for (size_t num_channels : {1, 2, 4}) {
for (auto rate : {16000, 32000, 48000}) {
std::vector<std::vector<std::vector<float>>> x(
NumBandsForRate(rate),
std::vector<std::vector<float>>(num_channels,
std::vector<float>(kBlockSize, 0.f)));
std::vector<std::vector<std::vector<float>>> y(
NumBandsForRate(rate),
std::vector<std::vector<float>>(num_channels,
std::vector<float>(kBlockSize, 0.f)));
Block x(NumBandsForRate(rate), num_channels);
Block y(NumBandsForRate(rate), num_channels);
EchoPathVariability echo_path_variability(
false, EchoPathVariability::DelayAdjustment::kNone, false);
for (size_t delay_samples : {0, 64, 150, 200, 301}) {
@ -187,13 +151,13 @@ TEST(EchoRemover, BasicEchoRemoval) {
render_buffer->AlignFromDelay(delay_samples / kBlockSize);
std::vector<std::vector<std::unique_ptr<DelayBuffer<float>>>>
delay_buffers(x.size());
delay_buffers(x.NumBands());
for (size_t band = 0; band < delay_buffers.size(); ++band) {
delay_buffers[band].resize(x[0].size());
delay_buffers[band].resize(x.NumChannels());
}
for (size_t band = 0; band < x.size(); ++band) {
for (size_t channel = 0; channel < x[0].size(); ++channel) {
for (int band = 0; band < x.NumBands(); ++band) {
for (int channel = 0; channel < x.NumChannels(); ++channel) {
delay_buffers[band][channel].reset(
new DelayBuffer<float>(delay_samples));
}
@ -204,22 +168,23 @@ TEST(EchoRemover, BasicEchoRemoval) {
for (int k = 0; k < kNumBlocksToProcess; ++k) {
const bool silence = k < 100 || (k % 100 >= 10);
for (size_t band = 0; band < x.size(); ++band) {
for (size_t channel = 0; channel < x[0].size(); ++channel) {
for (int band = 0; band < x.NumBands(); ++band) {
for (int channel = 0; channel < x.NumChannels(); ++channel) {
if (silence) {
std::fill(x[band][channel].begin(), x[band][channel].end(),
0.f);
std::fill(x.begin(band, channel), x.end(band, channel), 0.f);
} else {
RandomizeSampleVector(&random_generator, x[band][channel]);
RandomizeSampleVector(&random_generator, x.View(band, channel));
}
delay_buffers[band][channel]->Delay(x[band][channel],
y[band][channel]);
delay_buffers[band][channel]->Delay(x.View(band, channel),
y.View(band, channel));
}
}
if (k > kNumBlocksToProcess / 2) {
input_energy = std::inner_product(y[0][0].begin(), y[0][0].end(),
y[0][0].begin(), input_energy);
input_energy = std::inner_product(
y.begin(/*band=*/0, /*channel=*/0),
y.end(/*band=*/0, /*channel=*/0),
y.begin(/*band=*/0, /*channel=*/0), input_energy);
}
render_buffer->Insert(x);
@ -230,8 +195,10 @@ TEST(EchoRemover, BasicEchoRemoval) {
&y);
if (k > kNumBlocksToProcess / 2) {
output_energy = std::inner_product(y[0][0].begin(), y[0][0].end(),
y[0][0].begin(), output_energy);
output_energy = std::inner_product(
y.begin(/*band=*/0, /*channel=*/0),
y.end(/*band=*/0, /*channel=*/0),
y.begin(/*band=*/0, /*channel=*/0), output_energy);
}
}
EXPECT_GT(input_energy, 10.f * output_energy);

View File

@ -60,7 +60,7 @@ void VerifyErleGreaterOrEqual(
}
}
void FormFarendTimeFrame(std::vector<std::vector<std::vector<float>>>* x) {
void FormFarendTimeFrame(Block* x) {
const std::array<float, kBlockSize> frame = {
7459.88, 17209.6, 17383, 20768.9, 16816.7, 18386.3, 4492.83, 9675.85,
6665.52, 14808.6, 9342.3, 7483.28, 19261.7, 4145.98, 1622.18, 13475.2,
@ -70,10 +70,10 @@ void FormFarendTimeFrame(std::vector<std::vector<std::vector<float>>>* x) {
11405, 15031.4, 14541.6, 19765.5, 18346.3, 19350.2, 3157.47, 18095.8,
1743.68, 21328.2, 19727.5, 7295.16, 10332.4, 11055.5, 20107.4, 14708.4,
12416.2, 16434, 2454.69, 9840.8, 6867.23, 1615.75, 6059.9, 8394.19};
for (size_t band = 0; band < x->size(); ++band) {
for (size_t channel = 0; channel < (*x)[band].size(); ++channel) {
RTC_DCHECK_GE((*x)[band][channel].size(), frame.size());
std::copy(frame.begin(), frame.end(), (*x)[band][channel].begin());
for (int band = 0; band < x->NumBands(); ++band) {
for (int channel = 0; channel < x->NumChannels(); ++channel) {
RTC_DCHECK_GE(kBlockSize, frame.size());
std::copy(frame.begin(), frame.end(), x->begin(band, channel));
}
}
}
@ -104,13 +104,13 @@ void FormFarendFrame(const RenderBuffer& render_buffer,
}
void FormNearendFrame(
std::vector<std::vector<std::vector<float>>>* x,
Block* x,
std::array<float, kFftLengthBy2Plus1>* X2,
rtc::ArrayView<std::array<float, kFftLengthBy2Plus1>> E2,
rtc::ArrayView<std::array<float, kFftLengthBy2Plus1>> Y2) {
for (size_t band = 0; band < x->size(); ++band) {
for (size_t ch = 0; ch < (*x)[band].size(); ++ch) {
std::fill((*x)[band][ch].begin(), (*x)[band][ch].end(), 0.f);
for (int band = 0; band < x->NumBands(); ++band) {
for (int ch = 0; ch < x->NumChannels(); ++ch) {
std::fill(x->begin(band, ch), x->end(band, ch), 0.f);
}
}
@ -162,9 +162,7 @@ TEST_P(ErleEstimatorMultiChannel, VerifyErleIncreaseAndHold) {
EchoCanceller3Config config;
config.erle.onset_detection = true;
std::vector<std::vector<std::vector<float>>> x(
kNumBands, std::vector<std::vector<float>>(
num_render_channels, std::vector<float>(kBlockSize, 0.f)));
Block x(kNumBands, num_render_channels);
std::vector<std::vector<std::array<float, kFftLengthBy2Plus1>>>
filter_frequency_response(
config.filter.refined.length_blocks,
@ -227,9 +225,7 @@ TEST_P(ErleEstimatorMultiChannel, VerifyErleTrackingOnOnsets) {
std::vector<bool> converged_filters(num_capture_channels, true);
EchoCanceller3Config config;
config.erle.onset_detection = true;
std::vector<std::vector<std::vector<float>>> x(
kNumBands, std::vector<std::vector<float>>(
num_render_channels, std::vector<float>(kBlockSize, 0.f)));
Block x(kNumBands, num_render_channels);
std::vector<std::vector<std::array<float, kFftLengthBy2Plus1>>>
filter_frequency_response(
config.filter.refined.length_blocks,

View File

@ -131,7 +131,7 @@ void FilterAnalyzer::AnalyzeRegion(
st_ch.consistent_estimate = st_ch.consistent_filter_detector.Detect(
h_highpass_[ch], region_,
render_buffer.Block(-filter_delays_blocks_[ch])[0], st_ch.peak_index,
render_buffer.Block(-filter_delays_blocks_[ch]), st_ch.peak_index,
filter_delays_blocks_[ch]);
}
}
@ -224,7 +224,7 @@ void FilterAnalyzer::ConsistentFilterDetector::Reset() {
bool FilterAnalyzer::ConsistentFilterDetector::Detect(
rtc::ArrayView<const float> filter_to_analyze,
const FilterRegion& region,
rtc::ArrayView<const std::vector<float>> x_block,
const Block& x_block,
size_t peak_index,
int delay_blocks) {
if (region.start_sample_ == 0) {
@ -265,7 +265,9 @@ bool FilterAnalyzer::ConsistentFilterDetector::Detect(
if (significant_peak_) {
bool active_render_block = false;
for (auto& x_channel : x_block) {
for (int ch = 0; ch < x_block.NumChannels(); ++ch) {
rtc::ArrayView<const float, kBlockSize> x_channel =
x_block.View(/*band=*/0, ch);
const float x_energy = std::inner_product(
x_channel.begin(), x_channel.end(), x_channel.begin(), 0.f);
if (x_energy > active_render_threshold_) {

View File

@ -20,6 +20,7 @@
#include "api/array_view.h"
#include "api/audio/echo_canceller3_config.h"
#include "modules/audio_processing/aec3/aec3_common.h"
#include "modules/audio_processing/aec3/block.h"
namespace webrtc {
@ -93,7 +94,7 @@ class FilterAnalyzer {
void Reset();
bool Detect(rtc::ArrayView<const float> filter_to_analyze,
const FilterRegion& region,
rtc::ArrayView<const std::vector<float>> x_block,
const Block& x_block,
size_t peak_index,
int delay_blocks);

View File

@ -33,26 +33,22 @@ FrameBlocker::~FrameBlocker() = default;
void FrameBlocker::InsertSubFrameAndExtractBlock(
const std::vector<std::vector<rtc::ArrayView<float>>>& sub_frame,
std::vector<std::vector<std::vector<float>>>* block) {
Block* block) {
RTC_DCHECK(block);
RTC_DCHECK_EQ(num_bands_, block->size());
RTC_DCHECK_EQ(num_bands_, block->NumBands());
RTC_DCHECK_EQ(num_bands_, sub_frame.size());
for (size_t band = 0; band < num_bands_; ++band) {
RTC_DCHECK_EQ(num_channels_, (*block)[band].size());
RTC_DCHECK_EQ(num_channels_, block->NumChannels());
RTC_DCHECK_EQ(num_channels_, sub_frame[band].size());
for (size_t channel = 0; channel < num_channels_; ++channel) {
RTC_DCHECK_GE(kBlockSize - 16, buffer_[band][channel].size());
RTC_DCHECK_EQ(kBlockSize, (*block)[band][channel].size());
RTC_DCHECK_EQ(kSubFrameLength, sub_frame[band][channel].size());
const int samples_to_block = kBlockSize - buffer_[band][channel].size();
(*block)[band][channel].clear();
(*block)[band][channel].insert((*block)[band][channel].begin(),
buffer_[band][channel].begin(),
buffer_[band][channel].end());
(*block)[band][channel].insert(
(*block)[band][channel].begin() + buffer_[band][channel].size(),
sub_frame[band][channel].begin(),
sub_frame[band][channel].begin() + samples_to_block);
std::copy(buffer_[band][channel].begin(), buffer_[band][channel].end(),
block->begin(band, channel));
std::copy(sub_frame[band][channel].begin(),
sub_frame[band][channel].begin() + samples_to_block,
block->begin(band, channel) + kBlockSize - samples_to_block);
buffer_[band][channel].clear();
buffer_[band][channel].insert(
buffer_[band][channel].begin(),
@ -66,20 +62,16 @@ bool FrameBlocker::IsBlockAvailable() const {
return kBlockSize == buffer_[0][0].size();
}
void FrameBlocker::ExtractBlock(
std::vector<std::vector<std::vector<float>>>* block) {
void FrameBlocker::ExtractBlock(Block* block) {
RTC_DCHECK(block);
RTC_DCHECK_EQ(num_bands_, block->size());
RTC_DCHECK_EQ(num_bands_, block->NumBands());
RTC_DCHECK_EQ(num_channels_, block->NumChannels());
RTC_DCHECK(IsBlockAvailable());
for (size_t band = 0; band < num_bands_; ++band) {
RTC_DCHECK_EQ(num_channels_, (*block)[band].size());
for (size_t channel = 0; channel < num_channels_; ++channel) {
RTC_DCHECK_EQ(kBlockSize, buffer_[band][channel].size());
RTC_DCHECK_EQ(kBlockSize, (*block)[band][channel].size());
(*block)[band][channel].clear();
(*block)[band][channel].insert((*block)[band][channel].begin(),
buffer_[band][channel].begin(),
buffer_[band][channel].end());
std::copy(buffer_[band][channel].begin(), buffer_[band][channel].end(),
block->begin(band, channel));
buffer_[band][channel].clear();
}
}

View File

@ -17,6 +17,7 @@
#include "api/array_view.h"
#include "modules/audio_processing/aec3/aec3_common.h"
#include "modules/audio_processing/aec3/block.h"
namespace webrtc {
@ -33,12 +34,12 @@ class FrameBlocker {
// extracts one 64 sample multiband block.
void InsertSubFrameAndExtractBlock(
const std::vector<std::vector<rtc::ArrayView<float>>>& sub_frame,
std::vector<std::vector<std::vector<float>>>* block);
Block* block);
// Reports whether a multiband block of 64 samples is available for
// extraction.
bool IsBlockAvailable() const;
// Extracts a multiband block of 64 samples.
void ExtractBlock(std::vector<std::vector<std::vector<float>>>* block);
void ExtractBlock(Block* block);
private:
const size_t num_bands_;

View File

@ -86,15 +86,14 @@ bool VerifySubFrame(
return true;
}
bool VerifyBlock(size_t block_counter,
int offset,
const std::vector<std::vector<std::vector<float>>>& block) {
for (size_t band = 0; band < block.size(); ++band) {
for (size_t channel = 0; channel < block[band].size(); ++channel) {
for (size_t sample = 0; sample < block[band][channel].size(); ++sample) {
bool VerifyBlock(size_t block_counter, int offset, const Block& block) {
for (int band = 0; band < block.NumBands(); ++band) {
for (int channel = 0; channel < block.NumChannels(); ++channel) {
for (size_t sample = 0; sample < kBlockSize; ++sample) {
auto it = block.begin(band, channel) + sample;
const float reference_value = ComputeSampleValue(
block_counter, kBlockSize, band, channel, sample, offset);
if (reference_value != block[band][channel][sample]) {
if (reference_value != *it) {
return false;
}
}
@ -108,9 +107,7 @@ void RunBlockerTest(int sample_rate_hz, size_t num_channels) {
constexpr size_t kNumSubFramesToProcess = 20;
const size_t num_bands = NumBandsForRate(sample_rate_hz);
std::vector<std::vector<std::vector<float>>> block(
num_bands, std::vector<std::vector<float>>(
num_channels, std::vector<float>(kBlockSize, 0.f)));
Block block(num_bands, num_channels);
std::vector<std::vector<std::vector<float>>> input_sub_frame(
num_bands, std::vector<std::vector<float>>(
num_channels, std::vector<float>(kSubFrameLength, 0.f)));
@ -145,9 +142,7 @@ void RunBlockerAndFramerTest(int sample_rate_hz, size_t num_channels) {
const size_t kNumSubFramesToProcess = 20;
const size_t num_bands = NumBandsForRate(sample_rate_hz);
std::vector<std::vector<std::vector<float>>> block(
num_bands, std::vector<std::vector<float>>(
num_channels, std::vector<float>(kBlockSize, 0.f)));
Block block(num_bands, num_channels);
std::vector<std::vector<std::vector<float>>> input_sub_frame(
num_bands, std::vector<std::vector<float>>(
num_channels, std::vector<float>(kSubFrameLength, 0.f)));
@ -194,16 +189,12 @@ void RunWronglySizedInsertAndExtractParametersTest(
size_t correct_num_channels,
size_t num_block_bands,
size_t num_block_channels,
size_t block_length,
size_t num_sub_frame_bands,
size_t num_sub_frame_channels,
size_t sub_frame_length) {
const size_t correct_num_bands = NumBandsForRate(sample_rate_hz);
std::vector<std::vector<std::vector<float>>> block(
num_block_bands,
std::vector<std::vector<float>>(num_block_channels,
std::vector<float>(block_length, 0.f)));
Block block(num_block_bands, num_block_channels);
std::vector<std::vector<std::vector<float>>> input_sub_frame(
num_sub_frame_bands,
std::vector<std::vector<float>>(
@ -222,18 +213,11 @@ void RunWronglySizedInsertAndExtractParametersTest(
void RunWronglySizedExtractParameterTest(int sample_rate_hz,
size_t correct_num_channels,
size_t num_block_bands,
size_t num_block_channels,
size_t block_length) {
size_t num_block_channels) {
const size_t correct_num_bands = NumBandsForRate(sample_rate_hz);
std::vector<std::vector<std::vector<float>>> correct_block(
correct_num_bands,
std::vector<std::vector<float>>(correct_num_channels,
std::vector<float>(kBlockSize, 0.f)));
std::vector<std::vector<std::vector<float>>> wrong_block(
num_block_bands,
std::vector<std::vector<float>>(num_block_channels,
std::vector<float>(block_length, 0.f)));
Block correct_block(correct_num_bands, correct_num_channels);
Block wrong_block(num_block_bands, num_block_channels);
std::vector<std::vector<std::vector<float>>> input_sub_frame(
correct_num_bands,
std::vector<std::vector<float>>(
@ -259,9 +243,7 @@ void RunWrongExtractOrderTest(int sample_rate_hz,
size_t num_preceeding_api_calls) {
const size_t num_bands = NumBandsForRate(sample_rate_hz);
std::vector<std::vector<std::vector<float>>> block(
num_bands, std::vector<std::vector<float>>(
num_channels, std::vector<float>(kBlockSize, 0.f)));
Block block(num_bands, num_channels);
std::vector<std::vector<std::vector<float>>> input_sub_frame(
num_bands, std::vector<std::vector<float>>(
num_channels, std::vector<float>(kSubFrameLength, 0.f)));
@ -296,7 +278,7 @@ TEST(FrameBlockerDeathTest,
const size_t wrong_num_bands = (correct_num_bands % 3) + 1;
RunWronglySizedInsertAndExtractParametersTest(
rate, correct_num_channels, wrong_num_bands, correct_num_channels,
kBlockSize, correct_num_bands, correct_num_channels, kSubFrameLength);
correct_num_bands, correct_num_channels, kSubFrameLength);
}
}
}
@ -310,7 +292,7 @@ TEST(FrameBlockerDeathTest,
const size_t wrong_num_channels = correct_num_channels + 1;
RunWronglySizedInsertAndExtractParametersTest(
rate, correct_num_channels, correct_num_bands, wrong_num_channels,
kBlockSize, correct_num_bands, correct_num_channels, kSubFrameLength);
correct_num_bands, correct_num_channels, kSubFrameLength);
}
}
}
@ -324,7 +306,7 @@ TEST(FrameBlockerDeathTest,
const size_t wrong_num_bands = (correct_num_bands % 3) + 1;
RunWronglySizedInsertAndExtractParametersTest(
rate, correct_num_channels, correct_num_bands, correct_num_channels,
kBlockSize, wrong_num_bands, correct_num_channels, kSubFrameLength);
wrong_num_bands, correct_num_channels, kSubFrameLength);
}
}
}
@ -338,21 +320,7 @@ TEST(FrameBlockerDeathTest,
const size_t wrong_num_channels = correct_num_channels + 1;
RunWronglySizedInsertAndExtractParametersTest(
rate, correct_num_channels, correct_num_bands, wrong_num_channels,
kBlockSize, correct_num_bands, wrong_num_channels, kSubFrameLength);
}
}
}
TEST(FrameBlockerDeathTest,
WrongNumberOfSamplesInBlockForInsertSubFrameAndExtractBlock) {
for (auto rate : {16000, 32000, 48000}) {
for (size_t correct_num_channels : {1, 2, 4, 8}) {
SCOPED_TRACE(ProduceDebugText(rate, correct_num_channels));
const size_t correct_num_bands = NumBandsForRate(rate);
RunWronglySizedInsertAndExtractParametersTest(
rate, correct_num_channels, correct_num_bands, correct_num_channels,
kBlockSize - 1, correct_num_bands, correct_num_channels,
kSubFrameLength);
correct_num_bands, wrong_num_channels, kSubFrameLength);
}
}
}
@ -365,8 +333,7 @@ TEST(FrameBlockerDeathTest,
const size_t correct_num_bands = NumBandsForRate(rate);
RunWronglySizedInsertAndExtractParametersTest(
rate, correct_num_channels, correct_num_bands, correct_num_channels,
kBlockSize, correct_num_bands, correct_num_channels,
kSubFrameLength - 1);
correct_num_bands, correct_num_channels, kSubFrameLength - 1);
}
}
}
@ -377,9 +344,8 @@ TEST(FrameBlockerDeathTest, WrongNumberOfBandsInBlockForExtractBlock) {
SCOPED_TRACE(ProduceDebugText(rate, correct_num_channels));
const size_t correct_num_bands = NumBandsForRate(rate);
const size_t wrong_num_bands = (correct_num_bands % 3) + 1;
RunWronglySizedExtractParameterTest(rate, correct_num_channels,
wrong_num_bands, correct_num_channels,
kBlockSize);
RunWronglySizedExtractParameterTest(
rate, correct_num_channels, wrong_num_bands, correct_num_channels);
}
}
}
@ -390,21 +356,8 @@ TEST(FrameBlockerDeathTest, WrongNumberOfChannelsInBlockForExtractBlock) {
SCOPED_TRACE(ProduceDebugText(rate, correct_num_channels));
const size_t correct_num_bands = NumBandsForRate(rate);
const size_t wrong_num_channels = correct_num_channels + 1;
RunWronglySizedExtractParameterTest(rate, correct_num_channels,
correct_num_bands, wrong_num_channels,
kBlockSize);
}
}
}
TEST(FrameBlockerDeathTest, WrongNumberOfSamplesInBlockForExtractBlock) {
for (auto rate : {16000, 32000, 48000}) {
for (size_t correct_num_channels : {1, 2, 4, 8}) {
SCOPED_TRACE(ProduceDebugText(rate, correct_num_channels));
const size_t correct_num_bands = NumBandsForRate(rate);
RunWronglySizedExtractParameterTest(rate, correct_num_channels,
correct_num_bands,
correct_num_channels, kBlockSize - 1);
RunWronglySizedExtractParameterTest(
rate, correct_num_channels, correct_num_bands, wrong_num_channels);
}
}
}

View File

@ -210,9 +210,7 @@ TEST(MatchedFilter, LagEstimation) {
for (auto down_sampling_factor : kDownSamplingFactors) {
const size_t sub_block_size = kBlockSize / down_sampling_factor;
std::vector<std::vector<std::vector<float>>> render(
kNumBands, std::vector<std::vector<float>>(
kNumChannels, std::vector<float>(kBlockSize, 0.f)));
Block render(kNumBands, kNumChannels);
std::vector<std::vector<float>> capture(
1, std::vector<float>(kBlockSize, 0.f));
ApmDataDumper data_dumper(0);
@ -238,10 +236,12 @@ TEST(MatchedFilter, LagEstimation) {
for (size_t k = 0; k < (600 + delay_samples / sub_block_size); ++k) {
for (size_t band = 0; band < kNumBands; ++band) {
for (size_t channel = 0; channel < kNumChannels; ++channel) {
RandomizeSampleVector(&random_generator, render[band][channel]);
RandomizeSampleVector(&random_generator,
render.View(band, channel));
}
}
signal_delay_buffer.Delay(render[0][0], capture[0]);
signal_delay_buffer.Delay(render.View(/*band=*/0, /*channel=*/0),
capture[0]);
render_delay_buffer->Insert(render);
if (k == 0) {
@ -328,9 +328,7 @@ TEST(MatchedFilter, LagNotReliableForUncorrelatedRenderAndCapture) {
config.delay.num_filters = kNumMatchedFilters;
const size_t sub_block_size = kBlockSize / down_sampling_factor;
std::vector<std::vector<std::vector<float>>> render(
kNumBands, std::vector<std::vector<float>>(
kNumChannels, std::vector<float>(kBlockSize, 0.f)));
Block render(kNumBands, kNumChannels);
std::array<float, kBlockSize> capture_data;
rtc::ArrayView<float> capture(capture_data.data(), sub_block_size);
std::fill(capture.begin(), capture.end(), 0.f);
@ -346,7 +344,8 @@ TEST(MatchedFilter, LagNotReliableForUncorrelatedRenderAndCapture) {
// Analyze the correlation between render and capture.
for (size_t k = 0; k < 100; ++k) {
RandomizeSampleVector(&random_generator, render[0][0]);
RandomizeSampleVector(&random_generator,
render.View(/*band=*/0, /*channel=*/0));
RandomizeSampleVector(&random_generator, capture);
render_delay_buffer->Insert(render);
filter.Update(render_delay_buffer->GetDownsampledRenderBuffer(), capture,

View File

@ -28,13 +28,10 @@ class MockBlockProcessor : public BlockProcessor {
ProcessCapture,
(bool level_change,
bool saturated_microphone_signal,
std::vector<std::vector<std::vector<float>>>* linear_output,
std::vector<std::vector<std::vector<float>>>* capture_block),
(override));
MOCK_METHOD(void,
BufferRender,
(const std::vector<std::vector<std::vector<float>>>& block),
Block* linear_output,
Block* capture_block),
(override));
MOCK_METHOD(void, BufferRender, (const Block& block), (override));
MOCK_METHOD(void,
UpdateEchoLeakageStatus,
(bool leakage_detected),

View File

@ -33,8 +33,8 @@ class MockEchoRemover : public EchoRemover {
bool capture_signal_saturation,
const absl::optional<DelayEstimate>& delay_estimate,
RenderBuffer* render_buffer,
std::vector<std::vector<std::vector<float>>>* linear_output,
std::vector<std::vector<std::vector<float>>>* capture),
Block* linear_output,
Block* capture),
(override));
MOCK_METHOD(void,
UpdateEchoLeakageStatus,

View File

@ -17,8 +17,7 @@ MockRenderDelayBuffer::MockRenderDelayBuffer(int sample_rate_hz,
size_t num_channels)
: block_buffer_(GetRenderDelayBufferSize(4, 4, 12),
NumBandsForRate(sample_rate_hz),
num_channels,
kBlockSize),
num_channels),
spectrum_buffer_(block_buffer_.buffer.size(), num_channels),
fft_buffer_(block_buffer_.buffer.size(), num_channels),
render_buffer_(&block_buffer_, &spectrum_buffer_, &fft_buffer_),

View File

@ -30,7 +30,7 @@ class MockRenderDelayBuffer : public RenderDelayBuffer {
MOCK_METHOD(void, Reset, (), (override));
MOCK_METHOD(RenderDelayBuffer::BufferingEvent,
Insert,
(const std::vector<std::vector<std::vector<float>>>& block),
(const Block& block),
(override));
MOCK_METHOD(void, HandleSkippedCaptureProcessing, (), (override));
MOCK_METHOD(RenderDelayBuffer::BufferingEvent,

View File

@ -31,7 +31,7 @@ class MockRenderDelayController : public RenderDelayController {
GetDelay,
(const DownsampledRenderBuffer& render_buffer,
size_t render_delay_buffer_delay,
const std::vector<std::vector<float>>& capture),
const Block& capture),
(override));
MOCK_METHOD(bool, HasClockdrift, (), (const, override));
};

View File

@ -84,9 +84,7 @@ void RunFilterUpdateTest(int num_blocks_to_process,
RefinedFilterUpdateGain refined_gain(
config.filter.refined, config.filter.config_change_duration_blocks);
Random random_generator(42U);
std::vector<std::vector<std::vector<float>>> x(
kNumBands, std::vector<std::vector<float>>(
kNumRenderChannels, std::vector<float>(kBlockSize, 0.f)));
Block x(kNumBands, kNumRenderChannels);
std::vector<float> y(kBlockSize, 0.f);
config.delay.default_delay = 1;
std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
@ -131,19 +129,19 @@ void RunFilterUpdateTest(int num_blocks_to_process,
// Create the render signal.
if (use_silent_render_in_second_half && k > num_blocks_to_process / 2) {
for (size_t band = 0; band < x.size(); ++band) {
for (size_t channel = 0; channel < x[band].size(); ++channel) {
std::fill(x[band][channel].begin(), x[band][channel].end(), 0.f);
for (int band = 0; band < x.NumBands(); ++band) {
for (int channel = 0; channel < x.NumChannels(); ++channel) {
std::fill(x.begin(band, channel), x.end(band, channel), 0.f);
}
}
} else {
for (size_t band = 0; band < x.size(); ++band) {
for (size_t channel = 0; channel < x[band].size(); ++channel) {
RandomizeSampleVector(&random_generator, x[band][channel]);
for (int band = 0; band < x.NumChannels(); ++band) {
for (int channel = 0; channel < x.NumChannels(); ++channel) {
RandomizeSampleVector(&random_generator, x.View(band, channel));
}
}
}
delay_buffer.Delay(x[0][0], y);
delay_buffer.Delay(x.View(/*band=*/0, /*channel=*/0), y);
render_delay_buffer->Insert(x);
if (k == 0) {

View File

@ -40,8 +40,7 @@ class RenderBuffer {
~RenderBuffer();
// Get a block.
const std::vector<std::vector<std::vector<float>>>& Block(
int buffer_offset_blocks) const {
const Block& Block(int buffer_offset_blocks) const {
int position =
block_buffer_->OffsetIndex(block_buffer_->read, buffer_offset_blocks);
return block_buffer_->buffer[position];

View File

@ -22,7 +22,7 @@ namespace webrtc {
// Verifies the check for non-null fft buffer.
TEST(RenderBufferDeathTest, NullExternalFftBuffer) {
BlockBuffer block_buffer(10, 3, 1, kBlockSize);
BlockBuffer block_buffer(10, 3, 1);
SpectrumBuffer spectrum_buffer(10, 1);
EXPECT_DEATH(RenderBuffer(&block_buffer, &spectrum_buffer, nullptr), "");
}
@ -30,7 +30,7 @@ TEST(RenderBufferDeathTest, NullExternalFftBuffer) {
// Verifies the check for non-null spectrum buffer.
TEST(RenderBufferDeathTest, NullExternalSpectrumBuffer) {
FftBuffer fft_buffer(10, 1);
BlockBuffer block_buffer(10, 3, 1, kBlockSize);
BlockBuffer block_buffer(10, 3, 1);
EXPECT_DEATH(RenderBuffer(&block_buffer, nullptr, &fft_buffer), "");
}

View File

@ -54,8 +54,7 @@ class RenderDelayBufferImpl final : public RenderDelayBuffer {
~RenderDelayBufferImpl() override;
void Reset() override;
BufferingEvent Insert(
const std::vector<std::vector<std::vector<float>>>& block) override;
BufferingEvent Insert(const Block& block) override;
BufferingEvent PrepareCaptureProcessing() override;
void HandleSkippedCaptureProcessing() override;
bool AlignFromDelay(size_t delay) override;
@ -110,8 +109,7 @@ class RenderDelayBufferImpl final : public RenderDelayBuffer {
int MapDelayToTotalDelay(size_t delay) const;
int ComputeDelay() const;
void ApplyTotalDelay(int delay);
void InsertBlock(const std::vector<std::vector<std::vector<float>>>& block,
int previous_write);
void InsertBlock(const Block& block, int previous_write);
bool DetectActiveRender(rtc::ArrayView<const float> x) const;
bool DetectExcessRenderBlocks();
void IncrementWriteIndices();
@ -145,8 +143,7 @@ RenderDelayBufferImpl::RenderDelayBufferImpl(const EchoCanceller3Config& config,
config.delay.num_filters,
config.filter.refined.length_blocks),
NumBandsForRate(sample_rate_hz),
num_render_channels,
kBlockSize),
num_render_channels),
spectra_(blocks_.buffer.size(), num_render_channels),
ffts_(blocks_.buffer.size(), num_render_channels),
delay_(config_.delay.default_delay),
@ -161,7 +158,7 @@ RenderDelayBufferImpl::RenderDelayBufferImpl(const EchoCanceller3Config& config,
RTC_DCHECK_EQ(blocks_.buffer.size(), ffts_.buffer.size());
RTC_DCHECK_EQ(spectra_.buffer.size(), ffts_.buffer.size());
for (size_t i = 0; i < blocks_.buffer.size(); ++i) {
RTC_DCHECK_EQ(blocks_.buffer[i][0].size(), ffts_.buffer[i].size());
RTC_DCHECK_EQ(blocks_.buffer[i].NumChannels(), ffts_.buffer[i].size());
RTC_DCHECK_EQ(spectra_.buffer[i].size(), ffts_.buffer[i].size());
}
@ -211,7 +208,7 @@ void RenderDelayBufferImpl::Reset() {
// Inserts a new block into the render buffers.
RenderDelayBuffer::BufferingEvent RenderDelayBufferImpl::Insert(
const std::vector<std::vector<std::vector<float>>>& block) {
const Block& block) {
++render_call_counter_;
if (delay_) {
if (!last_call_was_render_) {
@ -239,7 +236,8 @@ RenderDelayBuffer::BufferingEvent RenderDelayBufferImpl::Insert(
// Detect and update render activity.
if (!render_activity_) {
render_activity_counter_ += DetectActiveRender(block[0][0]) ? 1 : 0;
render_activity_counter_ +=
DetectActiveRender(block.View(/*band=*/0, /*channel=*/0)) ? 1 : 0;
render_activity_ = render_activity_counter_ >= 20;
}
@ -394,46 +392,45 @@ void RenderDelayBufferImpl::AlignFromExternalDelay() {
}
// Inserts a block into the render buffers.
void RenderDelayBufferImpl::InsertBlock(
const std::vector<std::vector<std::vector<float>>>& block,
void RenderDelayBufferImpl::InsertBlock(const Block& block,
int previous_write) {
auto& b = blocks_;
auto& lr = low_rate_;
auto& ds = render_ds_;
auto& f = ffts_;
auto& s = spectra_;
const size_t num_bands = b.buffer[b.write].size();
const size_t num_render_channels = b.buffer[b.write][0].size();
RTC_DCHECK_EQ(block.size(), b.buffer[b.write].size());
const size_t num_bands = b.buffer[b.write].NumBands();
const size_t num_render_channels = b.buffer[b.write].NumChannels();
RTC_DCHECK_EQ(block.NumBands(), num_bands);
RTC_DCHECK_EQ(block.NumChannels(), num_render_channels);
for (size_t band = 0; band < num_bands; ++band) {
RTC_DCHECK_EQ(block[band].size(), num_render_channels);
RTC_DCHECK_EQ(b.buffer[b.write][band].size(), num_render_channels);
for (size_t ch = 0; ch < num_render_channels; ++ch) {
RTC_DCHECK_EQ(block[band][ch].size(), b.buffer[b.write][band][ch].size());
std::copy(block[band][ch].begin(), block[band][ch].end(),
b.buffer[b.write][band][ch].begin());
std::copy(block.begin(band, ch), block.end(band, ch),
b.buffer[b.write].begin(band, ch));
}
}
if (render_linear_amplitude_gain_ != 1.f) {
for (size_t band = 0; band < num_bands; ++band) {
for (size_t ch = 0; ch < num_render_channels; ++ch) {
for (size_t k = 0; k < 64; ++k) {
b.buffer[b.write][band][ch][k] *= render_linear_amplitude_gain_;
rtc::ArrayView<float, kBlockSize> b_view =
b.buffer[b.write].View(band, ch);
for (float& sample : b_view) {
sample *= render_linear_amplitude_gain_;
}
}
}
}
std::array<float, kBlockSize> downmixed_render;
render_mixer_.ProduceOutput(b.buffer[b.write][0], downmixed_render);
render_mixer_.ProduceOutput(b.buffer[b.write], downmixed_render);
render_decimator_.Decimate(downmixed_render, ds);
data_dumper_->DumpWav("aec3_render_decimator_output", ds.size(), ds.data(),
16000 / down_sampling_factor_, 1);
std::copy(ds.rbegin(), ds.rend(), lr.buffer.begin() + lr.write);
for (size_t channel = 0; channel < b.buffer[b.write][0].size(); ++channel) {
fft_.PaddedFft(b.buffer[b.write][0][channel],
b.buffer[previous_write][0][channel],
for (int channel = 0; channel < b.buffer[b.write].NumChannels(); ++channel) {
fft_.PaddedFft(b.buffer[b.write].View(/*band=*/0, channel),
b.buffer[previous_write].View(/*band=*/0, channel),
&f.buffer[f.write][channel]);
f.buffer[f.write][channel].Spectrum(optimization_,
s.buffer[s.write][channel]);

View File

@ -16,6 +16,7 @@
#include <vector>
#include "api/audio/echo_canceller3_config.h"
#include "modules/audio_processing/aec3/block.h"
#include "modules/audio_processing/aec3/downsampled_render_buffer.h"
#include "modules/audio_processing/aec3/render_buffer.h"
@ -41,8 +42,7 @@ class RenderDelayBuffer {
virtual void Reset() = 0;
// Inserts a block into the buffer.
virtual BufferingEvent Insert(
const std::vector<std::vector<std::vector<float>>>& block) = 0;
virtual BufferingEvent Insert(const Block& block) = 0;
// Updates the buffers one step based on the specified buffer delay. Returns
// an enum indicating whether there was a special event that occurred.

View File

@ -40,10 +40,7 @@ TEST(RenderDelayBuffer, BufferOverflow) {
SCOPED_TRACE(ProduceDebugText(rate));
std::unique_ptr<RenderDelayBuffer> delay_buffer(
RenderDelayBuffer::Create(config, rate, num_channels));
std::vector<std::vector<std::vector<float>>> block_to_insert(
NumBandsForRate(rate),
std::vector<std::vector<float>>(num_channels,
std::vector<float>(kBlockSize, 0.f)));
Block block_to_insert(NumBandsForRate(rate), num_channels);
for (size_t k = 0; k < 10; ++k) {
EXPECT_EQ(RenderDelayBuffer::BufferingEvent::kNone,
delay_buffer->Insert(block_to_insert));
@ -69,9 +66,7 @@ TEST(RenderDelayBuffer, AvailableBlock) {
constexpr size_t kNumBands = NumBandsForRate(kSampleRateHz);
std::unique_ptr<RenderDelayBuffer> delay_buffer(RenderDelayBuffer::Create(
EchoCanceller3Config(), kSampleRateHz, kNumChannels));
std::vector<std::vector<std::vector<float>>> input_block(
kNumBands, std::vector<std::vector<float>>(
kNumChannels, std::vector<float>(kBlockSize, 1.f)));
Block input_block(kNumBands, kNumChannels, 1.0f);
EXPECT_EQ(RenderDelayBuffer::BufferingEvent::kNone,
delay_buffer->Insert(input_block));
delay_buffer->PrepareCaptureProcessing();
@ -110,10 +105,8 @@ TEST(RenderDelayBufferDeathTest, WrongNumberOfBands) {
SCOPED_TRACE(ProduceDebugText(rate));
std::unique_ptr<RenderDelayBuffer> delay_buffer(RenderDelayBuffer::Create(
EchoCanceller3Config(), rate, num_channels));
std::vector<std::vector<std::vector<float>>> block_to_insert(
NumBandsForRate(rate < 48000 ? rate + 16000 : 16000),
std::vector<std::vector<float>>(num_channels,
std::vector<float>(kBlockSize, 0.f)));
Block block_to_insert(
NumBandsForRate(rate < 48000 ? rate + 16000 : 16000), num_channels);
EXPECT_DEATH(delay_buffer->Insert(block_to_insert), "");
}
}
@ -126,26 +119,7 @@ TEST(RenderDelayBufferDeathTest, WrongNumberOfChannels) {
SCOPED_TRACE(ProduceDebugText(rate));
std::unique_ptr<RenderDelayBuffer> delay_buffer(RenderDelayBuffer::Create(
EchoCanceller3Config(), rate, num_channels));
std::vector<std::vector<std::vector<float>>> block_to_insert(
NumBandsForRate(rate),
std::vector<std::vector<float>>(num_channels + 1,
std::vector<float>(kBlockSize, 0.f)));
EXPECT_DEATH(delay_buffer->Insert(block_to_insert), "");
}
}
}
// Verifies the check of the length of the inserted blocks.
TEST(RenderDelayBufferDeathTest, WrongBlockLength) {
for (auto rate : {16000, 32000, 48000}) {
for (size_t num_channels : {1, 2, 8}) {
SCOPED_TRACE(ProduceDebugText(rate));
std::unique_ptr<RenderDelayBuffer> delay_buffer(RenderDelayBuffer::Create(
EchoCanceller3Config(), rate, num_channels));
std::vector<std::vector<std::vector<float>>> block_to_insert(
NumBandsForRate(rate),
std::vector<std::vector<float>>(
num_channels, std::vector<float>(kBlockSize - 1, 0.f)));
Block block_to_insert(NumBandsForRate(rate), num_channels + 1);
EXPECT_DEATH(delay_buffer->Insert(block_to_insert), "");
}
}

View File

@ -47,7 +47,7 @@ class RenderDelayControllerImpl final : public RenderDelayController {
absl::optional<DelayEstimate> GetDelay(
const DownsampledRenderBuffer& render_buffer,
size_t render_delay_buffer_delay,
const std::vector<std::vector<float>>& capture) override;
const Block& capture) override;
bool HasClockdrift() const override;
private:
@ -124,8 +124,7 @@ void RenderDelayControllerImpl::LogRenderCall() {}
absl::optional<DelayEstimate> RenderDelayControllerImpl::GetDelay(
const DownsampledRenderBuffer& render_buffer,
size_t render_delay_buffer_delay,
const std::vector<std::vector<float>>& capture) {
RTC_DCHECK_EQ(kBlockSize, capture[0].size());
const Block& capture) {
++capture_call_counter_;
auto delay_samples = delay_estimator_.EstimateDelay(render_buffer, capture);

View File

@ -14,6 +14,7 @@
#include "absl/types/optional.h"
#include "api/array_view.h"
#include "api/audio/echo_canceller3_config.h"
#include "modules/audio_processing/aec3/block.h"
#include "modules/audio_processing/aec3/delay_estimate.h"
#include "modules/audio_processing/aec3/downsampled_render_buffer.h"
#include "modules/audio_processing/aec3/render_delay_buffer.h"
@ -40,7 +41,7 @@ class RenderDelayController {
virtual absl::optional<DelayEstimate> GetDelay(
const DownsampledRenderBuffer& render_buffer,
size_t render_delay_buffer_delay,
const std::vector<std::vector<float>>& capture) = 0;
const Block& capture) = 0;
// Returns true if clockdrift has been detected.
virtual bool HasClockdrift() const = 0;

View File

@ -53,8 +53,7 @@ constexpr size_t kDownSamplingFactors[] = {2, 4, 8};
// TODO(bugs.webrtc.org/11161): Re-enable tests.
TEST(RenderDelayController, DISABLED_NoRenderSignal) {
for (size_t num_render_channels : {1, 2, 8}) {
std::vector<std::vector<float>> block(1,
std::vector<float>(kBlockSize, 0.f));
Block block(/*num_bands=1*/ 1, /*num_channels=*/1);
EchoCanceller3Config config;
for (size_t num_matched_filters = 4; num_matched_filters <= 10;
num_matched_filters++) {
@ -85,8 +84,7 @@ TEST(RenderDelayController, DISABLED_NoRenderSignal) {
TEST(RenderDelayController, DISABLED_BasicApiCalls) {
for (size_t num_capture_channels : {1, 2, 4}) {
for (size_t num_render_channels : {1, 2, 8}) {
std::vector<std::vector<float>> capture_block(
num_capture_channels, std::vector<float>(kBlockSize, 0.f));
Block capture_block(/*num_bands=*/1, num_capture_channels);
absl::optional<DelayEstimate> delay_blocks;
for (size_t num_matched_filters = 4; num_matched_filters <= 10;
num_matched_filters++) {
@ -98,10 +96,7 @@ TEST(RenderDelayController, DISABLED_BasicApiCalls) {
config.delay.capture_alignment_mixing.adaptive_selection = false;
for (auto rate : {16000, 32000, 48000}) {
std::vector<std::vector<std::vector<float>>> render_block(
NumBandsForRate(rate),
std::vector<std::vector<float>>(
num_render_channels, std::vector<float>(kBlockSize, 0.f)));
Block render_block(NumBandsForRate(rate), num_render_channels);
std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
RenderDelayBuffer::Create(config, rate, num_render_channels));
std::unique_ptr<RenderDelayController> delay_controller(
@ -130,8 +125,7 @@ TEST(RenderDelayController, DISABLED_BasicApiCalls) {
TEST(RenderDelayController, DISABLED_Alignment) {
Random random_generator(42U);
for (size_t num_capture_channels : {1, 2, 4}) {
std::vector<std::vector<float>> capture_block(
num_capture_channels, std::vector<float>(kBlockSize, 0.f));
Block capture_block(/*num_bands=*/1, num_capture_channels);
for (size_t num_matched_filters = 4; num_matched_filters <= 10;
num_matched_filters++) {
for (auto down_sampling_factor : kDownSamplingFactors) {
@ -143,10 +137,7 @@ TEST(RenderDelayController, DISABLED_Alignment) {
for (size_t num_render_channels : {1, 2, 8}) {
for (auto rate : {16000, 32000, 48000}) {
std::vector<std::vector<std::vector<float>>> render_block(
NumBandsForRate(rate),
std::vector<std::vector<float>>(
num_render_channels, std::vector<float>(kBlockSize, 0.f)));
Block render_block(NumBandsForRate(rate), num_render_channels);
for (size_t delay_samples : {15, 50, 150, 200, 800, 4000}) {
absl::optional<DelayEstimate> delay_blocks;
@ -160,14 +151,16 @@ TEST(RenderDelayController, DISABLED_Alignment) {
num_capture_channels));
DelayBuffer<float> signal_delay_buffer(delay_samples);
for (size_t k = 0; k < (400 + delay_samples / kBlockSize); ++k) {
for (size_t band = 0; band < render_block.size(); ++band) {
for (size_t channel = 0; channel < render_block[band].size();
for (int band = 0; band < render_block.NumBands(); ++band) {
for (int channel = 0; channel < render_block.NumChannels();
++channel) {
RandomizeSampleVector(&random_generator,
render_block[band][channel]);
render_block.View(band, channel));
}
}
signal_delay_buffer.Delay(render_block[0][0], capture_block[0]);
signal_delay_buffer.Delay(
render_block.View(/*band=*/0, /*channel=*/0),
capture_block.View(/*band=*/0, /*channel=*/0));
render_delay_buffer->Insert(render_block);
render_delay_buffer->PrepareCaptureProcessing();
delay_blocks = delay_controller->GetDelay(
@ -206,14 +199,8 @@ TEST(RenderDelayController, DISABLED_NonCausalAlignment) {
config.delay.capture_alignment_mixing.downmix = false;
config.delay.capture_alignment_mixing.adaptive_selection = false;
for (auto rate : {16000, 32000, 48000}) {
std::vector<std::vector<std::vector<float>>> render_block(
NumBandsForRate(rate),
std::vector<std::vector<float>>(
num_render_channels, std::vector<float>(kBlockSize, 0.f)));
std::vector<std::vector<std::vector<float>>> capture_block(
NumBandsForRate(rate),
std::vector<std::vector<float>>(
num_capture_channels, std::vector<float>(kBlockSize, 0.f)));
Block render_block(NumBandsForRate(rate), num_render_channels);
Block capture_block(NumBandsForRate(rate), num_capture_channels);
for (int delay_samples : {-15, -50, -150, -200}) {
absl::optional<DelayEstimate> delay_blocks;
@ -229,14 +216,17 @@ TEST(RenderDelayController, DISABLED_NonCausalAlignment) {
for (int k = 0;
k < (400 - delay_samples / static_cast<int>(kBlockSize));
++k) {
RandomizeSampleVector(&random_generator, capture_block[0][0]);
signal_delay_buffer.Delay(capture_block[0][0],
render_block[0][0]);
RandomizeSampleVector(
&random_generator,
capture_block.View(/*band=*/0, /*channel=*/0));
signal_delay_buffer.Delay(
capture_block.View(/*band=*/0, /*channel=*/0),
render_block.View(/*band=*/0, /*channel=*/0));
render_delay_buffer->Insert(render_block);
render_delay_buffer->PrepareCaptureProcessing();
delay_blocks = delay_controller->GetDelay(
render_delay_buffer->GetDownsampledRenderBuffer(),
render_delay_buffer->Delay(), capture_block[0]);
render_delay_buffer->Delay(), capture_block);
}
ASSERT_FALSE(delay_blocks);
@ -255,8 +245,8 @@ TEST(RenderDelayController, DISABLED_AlignmentWithJitter) {
Random random_generator(42U);
for (size_t num_capture_channels : {1, 2, 4}) {
for (size_t num_render_channels : {1, 2, 8}) {
std::vector<std::vector<float>> capture_block(
num_capture_channels, std::vector<float>(kBlockSize, 0.f));
Block capture_block(
/*num_bands=*/1, num_capture_channels);
for (size_t num_matched_filters = 4; num_matched_filters <= 10;
num_matched_filters++) {
for (auto down_sampling_factor : kDownSamplingFactors) {
@ -267,10 +257,7 @@ TEST(RenderDelayController, DISABLED_AlignmentWithJitter) {
config.delay.capture_alignment_mixing.adaptive_selection = false;
for (auto rate : {16000, 32000, 48000}) {
std::vector<std::vector<std::vector<float>>> render_block(
NumBandsForRate(rate),
std::vector<std::vector<float>>(
num_render_channels, std::vector<float>(kBlockSize, 0.f)));
Block render_block(NumBandsForRate(rate), num_render_channels);
for (size_t delay_samples : {15, 50, 300, 800}) {
absl::optional<DelayEstimate> delay_blocks;
SCOPED_TRACE(ProduceDebugText(rate, delay_samples,
@ -287,12 +274,14 @@ TEST(RenderDelayController, DISABLED_AlignmentWithJitter) {
kMaxTestJitterBlocks +
1;
++j) {
std::vector<std::vector<std::vector<float>>>
capture_block_buffer;
std::vector<Block> capture_block_buffer;
for (size_t k = 0; k < (kMaxTestJitterBlocks - 1); ++k) {
RandomizeSampleVector(&random_generator, render_block[0][0]);
signal_delay_buffer.Delay(render_block[0][0],
capture_block[0]);
RandomizeSampleVector(
&random_generator,
render_block.View(/*band=*/0, /*channel=*/0));
signal_delay_buffer.Delay(
render_block.View(/*band=*/0, /*channel=*/0),
capture_block.View(/*band=*/0, /*channel=*/0));
capture_block_buffer.push_back(capture_block);
render_delay_buffer->Insert(render_block);
}
@ -324,24 +313,6 @@ TEST(RenderDelayController, DISABLED_AlignmentWithJitter) {
#if RTC_DCHECK_IS_ON && GTEST_HAS_DEATH_TEST && !defined(WEBRTC_ANDROID)
// Verifies the check for the capture signal block size.
TEST(RenderDelayControllerDeathTest, WrongCaptureSize) {
std::vector<std::vector<float>> block(
1, std::vector<float>(kBlockSize - 1, 0.f));
EchoCanceller3Config config;
for (auto rate : {16000, 32000, 48000}) {
SCOPED_TRACE(ProduceDebugText(rate));
std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
RenderDelayBuffer::Create(config, rate, 1));
EXPECT_DEATH(
std::unique_ptr<RenderDelayController>(
RenderDelayController::Create(EchoCanceller3Config(), rate, 1))
->GetDelay(render_delay_buffer->GetDownsampledRenderBuffer(),
render_delay_buffer->Delay(), block),
"");
}
}
// Verifies the check for correct sample rate.
// TODO(peah): Re-enable the test once the issue with memory leaks during DEATH
// tests on test bots has been fixed.

View File

@ -66,10 +66,9 @@ void IdentifyStrongNarrowBandComponent(const RenderBuffer& render_buffer,
*narrow_peak_band = absl::nullopt;
}
const std::vector<std::vector<std::vector<float>>>& x_latest =
render_buffer.Block(0);
const Block& x_latest = render_buffer.Block(0);
float max_peak_level = 0.f;
for (size_t channel = 0; channel < x_latest[0].size(); ++channel) {
for (int channel = 0; channel < x_latest.NumChannels(); ++channel) {
rtc::ArrayView<const float, kFftLengthBy2Plus1> X2_latest =
render_buffer.Spectrum(0)[channel];
@ -90,13 +89,14 @@ void IdentifyStrongNarrowBandComponent(const RenderBuffer& render_buffer,
}
// Assess the render signal strength.
auto result0 = std::minmax_element(x_latest[0][channel].begin(),
x_latest[0][channel].end());
auto result0 = std::minmax_element(x_latest.begin(/*band=*/0, channel),
x_latest.end(/*band=*/0, channel));
float max_abs = std::max(fabs(*result0.first), fabs(*result0.second));
if (x_latest.size() > 1) {
const auto result1 = std::minmax_element(x_latest[1][channel].begin(),
x_latest[1][channel].end());
if (x_latest.NumBands() > 1) {
const auto result1 =
std::minmax_element(x_latest.begin(/*band=*/1, channel),
x_latest.end(/*band=*/1, channel));
max_abs =
std::max(max_abs, static_cast<float>(std::max(
fabs(*result1.first), fabs(*result1.second))));

View File

@ -36,18 +36,18 @@ void ProduceSinusoidInNoise(int sample_rate_hz,
float sinusoidal_frequency_hz,
Random* random_generator,
size_t* sample_counter,
std::vector<std::vector<std::vector<float>>>* x) {
Block* x) {
// Fill x with low-amplitude noise.
for (auto& band : *x) {
for (auto& channel : band) {
RandomizeSampleVector(random_generator, channel,
for (int band = 0; band < x->NumBands(); ++band) {
for (int channel = 0; channel < x->NumChannels(); ++channel) {
RandomizeSampleVector(random_generator, x->View(band, channel),
/*amplitude=*/500.f);
}
}
// Produce a sinusoid of the specified frequency in the specified channel.
for (size_t k = *sample_counter, j = 0; k < (*sample_counter + kBlockSize);
++k, ++j) {
(*x)[0][sinusoid_channel][j] +=
x->View(/*band=*/0, sinusoid_channel)[j] +=
32000.f *
std::sin(2.f * kPi * sinusoidal_frequency_hz * k / sample_rate_hz);
}
@ -59,9 +59,7 @@ void RunNarrowBandDetectionTest(size_t num_channels) {
Random random_generator(42U);
constexpr int kSampleRateHz = 48000;
constexpr size_t kNumBands = NumBandsForRate(kSampleRateHz);
std::vector<std::vector<std::vector<float>>> x(
kNumBands, std::vector<std::vector<float>>(
num_channels, std::vector<float>(kBlockSize, 0.f)));
Block x(kNumBands, num_channels);
std::array<float, kBlockSize> x_old;
Aec3Fft fft;
EchoCanceller3Config config;
@ -130,19 +128,17 @@ TEST(RenderSignalAnalyzer, NoFalseDetectionOfNarrowBands) {
SCOPED_TRACE(ProduceDebugText(num_channels));
RenderSignalAnalyzer analyzer(EchoCanceller3Config{});
Random random_generator(42U);
std::vector<std::vector<std::vector<float>>> x(
3, std::vector<std::vector<float>>(
num_channels, std::vector<float>(kBlockSize, 0.f)));
Block x(3, num_channels);
std::array<float, kBlockSize> x_old;
std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
RenderDelayBuffer::Create(EchoCanceller3Config(), 48000, num_channels));
std::array<float, kFftLengthBy2Plus1> mask;
x_old.fill(0.f);
for (size_t k = 0; k < 100; ++k) {
for (auto& band : x) {
for (auto& channel : band) {
RandomizeSampleVector(&random_generator, channel);
for (int k = 0; k < 100; ++k) {
for (int band = 0; band < x.NumBands(); ++band) {
for (int channel = 0; channel < x.NumChannels(); ++channel) {
RandomizeSampleVector(&random_generator, x.View(band, channel));
}
}

View File

@ -47,10 +47,7 @@ class ResidualEchoEstimatorTest {
Y2_(num_capture_channels_),
R2_(num_capture_channels_),
R2_unbounded_(num_capture_channels_),
x_(kNumBands,
std::vector<std::vector<float>>(
num_render_channels_,
std::vector<float>(kBlockSize, 0.0f))),
x_(kNumBands, num_render_channels_),
H2_(num_capture_channels_,
std::vector<std::array<float, kFftLengthBy2Plus1>>(10)),
h_(num_capture_channels_,
@ -84,7 +81,8 @@ class ResidualEchoEstimatorTest {
}
void RunOneFrame(bool dominant_nearend) {
RandomizeSampleVector(&random_generator_, x_[0][0]);
RandomizeSampleVector(&random_generator_,
x_.View(/*band=*/0, /*channel=*/0));
render_delay_buffer_->Insert(x_);
if (first_frame_) {
render_delay_buffer_->Reset();
@ -116,7 +114,7 @@ class ResidualEchoEstimatorTest {
std::vector<std::array<float, kFftLengthBy2Plus1>> Y2_;
std::vector<std::array<float, kFftLengthBy2Plus1>> R2_;
std::vector<std::array<float, kFftLengthBy2Plus1>> R2_unbounded_;
std::vector<std::vector<std::vector<float>>> x_;
Block x_;
std::vector<std::vector<std::array<float, kFftLengthBy2Plus1>>> H2_;
std::vector<std::vector<float>> h_;
Random random_generator_;

View File

@ -24,7 +24,7 @@ namespace webrtc {
namespace {
void GetActiveFrame(std::vector<std::vector<std::vector<float>>>* x) {
void GetActiveFrame(Block* x) {
const std::array<float, kBlockSize> frame = {
7459.88, 17209.6, 17383, 20768.9, 16816.7, 18386.3, 4492.83, 9675.85,
6665.52, 14808.6, 9342.3, 7483.28, 19261.7, 4145.98, 1622.18, 13475.2,
@ -34,10 +34,10 @@ void GetActiveFrame(std::vector<std::vector<std::vector<float>>>* x) {
11405, 15031.4, 14541.6, 19765.5, 18346.3, 19350.2, 3157.47, 18095.8,
1743.68, 21328.2, 19727.5, 7295.16, 10332.4, 11055.5, 20107.4, 14708.4,
12416.2, 16434, 2454.69, 9840.8, 6867.23, 1615.75, 6059.9, 8394.19};
for (size_t band = 0; band < x->size(); ++band) {
for (size_t channel = 0; channel < (*x)[band].size(); ++channel) {
RTC_DCHECK_GE((*x)[band][channel].size(), frame.size());
std::copy(frame.begin(), frame.end(), (*x)[band][channel].begin());
for (int band = 0; band < x->NumBands(); ++band) {
for (int channel = 0; channel < x->NumChannels(); ++channel) {
RTC_DCHECK_GE(kBlockSize, frame.size());
std::copy(frame.begin(), frame.end(), x->begin(band, channel));
}
}
}
@ -74,7 +74,7 @@ class TestInputs {
std::vector<std::array<float, kFftLengthBy2Plus1>> Y2_;
std::vector<std::array<float, kFftLengthBy2Plus1>> E2_;
std::vector<std::vector<std::array<float, kFftLengthBy2Plus1>>> H2_;
std::vector<std::vector<std::vector<float>>> x_;
Block x_;
std::vector<bool> converged_filters_;
};
@ -88,9 +88,7 @@ TestInputs::TestInputs(const EchoCanceller3Config& cfg,
H2_(num_capture_channels,
std::vector<std::array<float, kFftLengthBy2Plus1>>(
cfg.filter.refined.length_blocks)),
x_(1,
std::vector<std::vector<float>>(num_render_channels,
std::vector<float>(kBlockSize, 0.f))),
x_(1, num_render_channels),
converged_filters_(num_capture_channels, true) {
render_delay_buffer_->AlignFromDelay(4);
render_buffer_ = render_delay_buffer_->GetRenderBuffer();
@ -108,7 +106,8 @@ TestInputs::~TestInputs() = default;
void TestInputs::Update() {
if (n_ % 2 == 0) {
std::fill(x_[0][0].begin(), x_[0][0].end(), 0.f);
std::fill(x_.begin(/*band=*/0, /*channel=*/0),
x_.end(/*band=*/0, /*channel=*/0), 0.f);
} else {
GetActiveFrame(&x_);
}

View File

@ -176,11 +176,11 @@ void Subtractor::ExitInitialState() {
}
void Subtractor::Process(const RenderBuffer& render_buffer,
const std::vector<std::vector<float>>& capture,
const Block& capture,
const RenderSignalAnalyzer& render_signal_analyzer,
const AecState& aec_state,
rtc::ArrayView<SubtractorOutput> outputs) {
RTC_DCHECK_EQ(num_capture_channels_, capture.size());
RTC_DCHECK_EQ(num_capture_channels_, capture.NumChannels());
// Compute the render powers.
const bool same_filter_sizes = refined_filters_[0]->SizePartitions() ==
@ -204,9 +204,8 @@ void Subtractor::Process(const RenderBuffer& render_buffer,
// 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];
rtc::ArrayView<const float> y = capture.View(/*band=*/0, ch);
FftData& E_refined = output.E_refined;
FftData E_coarse;
std::array<float, kBlockSize>& e_refined = output.e_refined;

View File

@ -23,6 +23,7 @@
#include "modules/audio_processing/aec3/aec3_common.h"
#include "modules/audio_processing/aec3/aec3_fft.h"
#include "modules/audio_processing/aec3/aec_state.h"
#include "modules/audio_processing/aec3/block.h"
#include "modules/audio_processing/aec3/coarse_filter_update_gain.h"
#include "modules/audio_processing/aec3/echo_path_variability.h"
#include "modules/audio_processing/aec3/refined_filter_update_gain.h"
@ -48,7 +49,7 @@ class Subtractor {
// Performs the echo subtraction.
void Process(const RenderBuffer& render_buffer,
const std::vector<std::vector<float>>& capture,
const Block& capture,
const RenderSignalAnalyzer& render_signal_analyzer,
const AecState& aec_state,
rtc::ArrayView<SubtractorOutput> outputs);

View File

@ -45,11 +45,8 @@ std::vector<float> RunSubtractorTest(
Subtractor subtractor(config, num_render_channels, num_capture_channels,
&data_dumper, DetectOptimization());
absl::optional<DelayEstimate> delay_estimate;
std::vector<std::vector<std::vector<float>>> x(
kNumBands, std::vector<std::vector<float>>(
num_render_channels, std::vector<float>(kBlockSize, 0.f)));
std::vector<std::vector<float>> y(num_capture_channels,
std::vector<float>(kBlockSize, 0.f));
Block x(kNumBands, num_render_channels);
Block y(/*num_bands=*/1, num_capture_channels);
std::array<float, kBlockSize> x_old;
std::vector<SubtractorOutput> output(num_capture_channels);
config.delay.default_delay = 1;
@ -101,32 +98,34 @@ std::vector<float> RunSubtractorTest(
for (int k = 0; k < num_blocks_to_process; ++k) {
for (size_t render_ch = 0; render_ch < num_render_channels; ++render_ch) {
RandomizeSampleVector(&random_generator, x[0][render_ch]);
RandomizeSampleVector(&random_generator, x.View(/*band=*/0, render_ch));
}
if (uncorrelated_inputs) {
for (size_t capture_ch = 0; capture_ch < num_capture_channels;
++capture_ch) {
RandomizeSampleVector(&random_generator, y[capture_ch]);
RandomizeSampleVector(&random_generator,
y.View(/*band=*/0, capture_ch));
}
} else {
for (size_t capture_ch = 0; capture_ch < num_capture_channels;
++capture_ch) {
rtc::ArrayView<float> y_view = y.View(/*band=*/0, capture_ch);
for (size_t render_ch = 0; render_ch < num_render_channels;
++render_ch) {
std::array<float, kBlockSize> y_channel;
delay_buffer[capture_ch][render_ch]->Delay(x[0][render_ch],
y_channel);
for (size_t k = 0; k < y.size(); ++k) {
y[capture_ch][k] += y_channel[k] / num_render_channels;
delay_buffer[capture_ch][render_ch]->Delay(
x.View(/*band=*/0, render_ch), y_channel);
for (size_t k = 0; k < kBlockSize; ++k) {
y_view[k] += y_channel[k] / num_render_channels;
}
}
}
}
for (size_t ch = 0; ch < num_render_channels; ++ch) {
x_hp_filter[ch]->Process(x[0][ch]);
x_hp_filter[ch]->Process(x.View(/*band=*/0, ch));
}
for (size_t ch = 0; ch < num_capture_channels; ++ch) {
y_hp_filter[ch]->Process(y[ch]);
y_hp_filter[ch]->Process(y.View(/*band=*/0, ch));
}
render_delay_buffer->Insert(x);
@ -162,7 +161,8 @@ std::vector<float> RunSubtractorTest(
output[ch].e_refined.begin(), output[ch].e_refined.end(),
output[ch].e_refined.begin(), 0.f);
const float y_power =
std::inner_product(y[ch].begin(), y[ch].end(), y[ch].begin(), 0.f);
std::inner_product(y.begin(/*band=*/0, ch), y.end(/*band=*/0, ch),
y.begin(/*band=*/0, ch), 0.f);
if (y_power == 0.f) {
ADD_FAILURE();
results[ch] = -1.f;
@ -195,23 +195,6 @@ TEST(SubtractorDeathTest, NullDataDumper) {
"");
}
// Verifies the check for the capture signal size.
TEST(Subtractor, WrongCaptureSize) {
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<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, 1), output),
"");
}
#endif
// Verifies that the subtractor is able to converge on correlated data.

View File

@ -86,9 +86,9 @@ void SuppressionFilter::ApplyGain(
const std::array<float, kFftLengthBy2Plus1>& suppression_gain,
float high_bands_gain,
rtc::ArrayView<const FftData> E_lowest_band,
std::vector<std::vector<std::vector<float>>>* e) {
Block* e) {
RTC_DCHECK(e);
RTC_DCHECK_EQ(e->size(), NumBandsForRate(sample_rate_hz_));
RTC_DCHECK_EQ(e->NumBands(), NumBandsForRate(sample_rate_hz_));
// Comfort noise gain is sqrt(1-g^2), where g is the suppression gain.
std::array<float, kFftLengthBy2Plus1> noise_gain;
@ -121,7 +121,7 @@ void SuppressionFilter::ApplyGain(
constexpr float kIfftNormalization = 2.f / kFftLength;
fft_.Ifft(E, &e_extended);
float* e0 = (*e)[0][ch].data();
auto e0 = e->View(/*band=*/0, ch);
float* e0_old = e_output_old_[0][ch].data();
// Window and add the first half of e_extended with the second half of
@ -138,20 +138,20 @@ void SuppressionFilter::ApplyGain(
std::begin(e_output_old_[0][ch]));
// Apply suppression gain to upper bands.
for (size_t b = 1; b < e->size(); ++b) {
float* e_band = (*e)[b][ch].data();
for (int b = 1; b < e->NumBands(); ++b) {
auto e_band = e->View(b, ch);
for (size_t i = 0; i < kFftLengthBy2; ++i) {
e_band[i] *= high_bands_gain;
}
}
// Add comfort noise to band 1.
if (e->size() > 1) {
if (e->NumBands() > 1) {
E.Assign(comfort_noise_high_band[ch]);
std::array<float, kFftLength> time_domain_high_band_noise;
fft_.Ifft(E, &time_domain_high_band_noise);
float* e1 = (*e)[1][ch].data();
auto e1 = e->View(/*band=*/1, ch);
const float gain = high_bands_noise_scaling * kIfftNormalization;
for (size_t i = 0; i < kFftLengthBy2; ++i) {
e1[i] += time_domain_high_band_noise[i] * gain;
@ -159,8 +159,8 @@ void SuppressionFilter::ApplyGain(
}
// Delay upper bands to match the delay of the filter bank.
for (size_t b = 1; b < e->size(); ++b) {
float* e_band = (*e)[b][ch].data();
for (int b = 1; b < e->NumBands(); ++b) {
auto e_band = e->View(b, ch);
float* e_band_old = e_output_old_[b][ch].data();
for (size_t i = 0; i < kFftLengthBy2; ++i) {
std::swap(e_band[i], e_band_old[i]);
@ -168,8 +168,8 @@ void SuppressionFilter::ApplyGain(
}
// Clamp output of all bands.
for (size_t b = 0; b < e->size(); ++b) {
float* e_band = (*e)[b][ch].data();
for (int b = 0; b < e->NumBands(); ++b) {
auto e_band = e->View(b, ch);
for (size_t i = 0; i < kFftLengthBy2; ++i) {
e_band[i] = rtc::SafeClamp(e_band[i], -32768.f, 32767.f);
}

View File

@ -16,6 +16,7 @@
#include "modules/audio_processing/aec3/aec3_common.h"
#include "modules/audio_processing/aec3/aec3_fft.h"
#include "modules/audio_processing/aec3/block.h"
#include "modules/audio_processing/aec3/fft_data.h"
namespace webrtc {
@ -35,7 +36,7 @@ class SuppressionFilter {
const std::array<float, kFftLengthBy2Plus1>& suppression_gain,
float high_bands_gain,
rtc::ArrayView<const FftData> E_lowest_band,
std::vector<std::vector<std::vector<float>>>* e);
Block* e);
private:
const Aec3Optimization optimization_;

View File

@ -26,21 +26,21 @@ constexpr float kPi = 3.141592f;
void ProduceSinusoid(int sample_rate_hz,
float sinusoidal_frequency_hz,
size_t* sample_counter,
std::vector<std::vector<std::vector<float>>>* x) {
Block* x) {
// Produce a sinusoid of the specified frequency.
for (size_t k = *sample_counter, j = 0; k < (*sample_counter + kBlockSize);
++k, ++j) {
for (size_t channel = 0; channel < (*x)[0].size(); ++channel) {
(*x)[0][channel][j] =
for (int channel = 0; channel < x->NumChannels(); ++channel) {
x->View(/*band=*/0, channel)[j] =
32767.f *
std::sin(2.f * kPi * sinusoidal_frequency_hz * k / sample_rate_hz);
}
}
*sample_counter = *sample_counter + kBlockSize;
for (size_t band = 1; band < x->size(); ++band) {
for (size_t channel = 0; channel < (*x)[band].size(); ++channel) {
std::fill((*x)[band][channel].begin(), (*x)[band][channel].end(), 0.f);
for (int band = 1; band < x->NumBands(); ++band) {
for (int channel = 0; channel < x->NumChannels(); ++channel) {
std::fill(x->begin(band, channel), x->end(band, channel), 0.f);
}
}
}
@ -84,21 +84,23 @@ TEST(SuppressionFilter, ComfortNoiseInUnityGain) {
cn_high_bands[0].re.fill(1.f);
cn_high_bands[0].im.fill(1.f);
std::vector<std::vector<std::vector<float>>> e(
3,
std::vector<std::vector<float>>(1, std::vector<float>(kBlockSize, 0.f)));
std::vector<std::vector<std::vector<float>>> e_ref = e;
Block e(3, kBlockSize);
Block e_ref = e;
std::vector<FftData> E(1);
fft.PaddedFft(e[0][0], e_old_, Aec3Fft::Window::kSqrtHanning, &E[0]);
std::copy(e[0][0].begin(), e[0][0].end(), e_old_.begin());
fft.PaddedFft(e.View(/*band=*/0, /*channel=*/0), e_old_,
Aec3Fft::Window::kSqrtHanning, &E[0]);
std::copy(e.begin(/*band=*/0, /*channel=*/0),
e.end(/*band=*/0, /*channel=*/0), e_old_.begin());
filter.ApplyGain(cn, cn_high_bands, gain, 1.f, E, &e);
for (size_t band = 0; band < e.size(); ++band) {
for (size_t channel = 0; channel < e[band].size(); ++channel) {
for (size_t sample = 0; sample < e[band][channel].size(); ++sample) {
EXPECT_EQ(e_ref[band][channel][sample], e[band][channel][sample]);
for (int band = 0; band < e.NumBands(); ++band) {
for (int channel = 0; channel < e.NumChannels(); ++channel) {
const auto e_view = e.View(band, channel);
const auto e_ref_view = e_ref.View(band, channel);
for (size_t sample = 0; sample < e_view.size(); ++sample) {
EXPECT_EQ(e_ref_view[sample], e_view[sample]);
}
}
}
@ -116,9 +118,7 @@ TEST(SuppressionFilter, SignalSuppression) {
std::array<float, kFftLengthBy2> e_old_;
Aec3Fft fft;
std::array<float, kFftLengthBy2Plus1> gain;
std::vector<std::vector<std::vector<float>>> e(
kNumBands, std::vector<std::vector<float>>(
kNumChannels, std::vector<float>(kBlockSize, 0.f)));
Block e(kNumBands, kNumChannels);
e_old_.fill(0.f);
gain.fill(1.f);
@ -135,16 +135,20 @@ TEST(SuppressionFilter, SignalSuppression) {
float e0_output = 0.f;
for (size_t k = 0; k < 100; ++k) {
ProduceSinusoid(16000, 16000 * 40 / kFftLengthBy2 / 2, &sample_counter, &e);
e0_input = std::inner_product(e[0][0].begin(), e[0][0].end(),
e[0][0].begin(), e0_input);
e0_input = std::inner_product(e.begin(/*band=*/0, /*channel=*/0),
e.end(/*band=*/0, /*channel=*/0),
e.begin(/*band=*/0, /*channel=*/0), e0_input);
std::vector<FftData> E(1);
fft.PaddedFft(e[0][0], e_old_, Aec3Fft::Window::kSqrtHanning, &E[0]);
std::copy(e[0][0].begin(), e[0][0].end(), e_old_.begin());
fft.PaddedFft(e.View(/*band=*/0, /*channel=*/0), e_old_,
Aec3Fft::Window::kSqrtHanning, &E[0]);
std::copy(e.begin(/*band=*/0, /*channel=*/0),
e.end(/*band=*/0, /*channel=*/0), e_old_.begin());
filter.ApplyGain(cn, cn_high_bands, gain, 1.f, E, &e);
e0_output = std::inner_product(e[0][0].begin(), e[0][0].end(),
e[0][0].begin(), e0_output);
e0_output = std::inner_product(
e.begin(/*band=*/0, /*channel=*/0), e.end(/*band=*/0, /*channel=*/0),
e.begin(/*band=*/0, /*channel=*/0), e0_output);
}
EXPECT_LT(e0_output, e0_input / 1000.f);
@ -163,9 +167,7 @@ TEST(SuppressionFilter, SignalTransparency) {
Aec3Fft fft;
std::vector<FftData> cn_high_bands(1);
std::array<float, kFftLengthBy2Plus1> gain;
std::vector<std::vector<std::vector<float>>> e(
kNumBands, std::vector<std::vector<float>>(
kNumChannels, std::vector<float>(kBlockSize, 0.f)));
Block e(kNumBands, kNumChannels);
e_old_.fill(0.f);
gain.fill(1.f);
std::for_each(gain.begin() + 30, gain.end(), [](float& a) { a = 0.f; });
@ -181,16 +183,20 @@ TEST(SuppressionFilter, SignalTransparency) {
float e0_output = 0.f;
for (size_t k = 0; k < 100; ++k) {
ProduceSinusoid(16000, 16000 * 10 / kFftLengthBy2 / 2, &sample_counter, &e);
e0_input = std::inner_product(e[0][0].begin(), e[0][0].end(),
e[0][0].begin(), e0_input);
e0_input = std::inner_product(e.begin(/*band=*/0, /*channel=*/0),
e.end(/*band=*/0, /*channel=*/0),
e.begin(/*band=*/0, /*channel=*/0), e0_input);
std::vector<FftData> E(1);
fft.PaddedFft(e[0][0], e_old_, Aec3Fft::Window::kSqrtHanning, &E[0]);
std::copy(e[0][0].begin(), e[0][0].end(), e_old_.begin());
fft.PaddedFft(e.View(/*band=*/0, /*channel=*/0), e_old_,
Aec3Fft::Window::kSqrtHanning, &E[0]);
std::copy(e.begin(/*band=*/0, /*channel=*/0),
e.end(/*band=*/0, /*channel=*/0), e_old_.begin());
filter.ApplyGain(cn, cn_high_bands, gain, 1.f, E, &e);
e0_output = std::inner_product(e[0][0].begin(), e[0][0].end(),
e[0][0].begin(), e0_output);
e0_output = std::inner_product(
e.begin(/*band=*/0, /*channel=*/0), e.end(/*band=*/0, /*channel=*/0),
e.begin(/*band=*/0, /*channel=*/0), e0_output);
}
EXPECT_LT(0.9f * e0_input, e0_output);
@ -208,9 +214,7 @@ TEST(SuppressionFilter, Delay) {
std::array<float, kFftLengthBy2> e_old_;
Aec3Fft fft;
std::array<float, kFftLengthBy2Plus1> gain;
std::vector<std::vector<std::vector<float>>> e(
kNumBands, std::vector<std::vector<float>>(
kNumChannels, std::vector<float>(kBlockSize, 0.f)));
Block e(kNumBands, kNumChannels);
gain.fill(1.f);
@ -222,23 +226,27 @@ TEST(SuppressionFilter, Delay) {
for (size_t k = 0; k < 100; ++k) {
for (size_t band = 0; band < kNumBands; ++band) {
for (size_t channel = 0; channel < kNumChannels; ++channel) {
auto e_view = e.View(band, channel);
for (size_t sample = 0; sample < kBlockSize; ++sample) {
e[band][channel][sample] = k * kBlockSize + sample + channel;
e_view[sample] = k * kBlockSize + sample + channel;
}
}
}
std::vector<FftData> E(1);
fft.PaddedFft(e[0][0], e_old_, Aec3Fft::Window::kSqrtHanning, &E[0]);
std::copy(e[0][0].begin(), e[0][0].end(), e_old_.begin());
fft.PaddedFft(e.View(/*band=*/0, /*channel=*/0), e_old_,
Aec3Fft::Window::kSqrtHanning, &E[0]);
std::copy(e.begin(/*band=*/0, /*channel=*/0),
e.end(/*band=*/0, /*channel=*/0), e_old_.begin());
filter.ApplyGain(cn, cn_high_bands, gain, 1.f, E, &e);
if (k > 2) {
for (size_t band = 0; band < kNumBands; ++band) {
for (size_t channel = 0; channel < kNumChannels; ++channel) {
const auto e_view = e.View(band, channel);
for (size_t sample = 0; sample < kBlockSize; ++sample) {
EXPECT_NEAR(k * kBlockSize + sample - kBlockSize + channel,
e[band][channel][sample], 0.01);
e_view[sample], 0.01);
}
}
}

View File

@ -110,13 +110,13 @@ float SuppressionGain::UpperBandsGain(
comfort_noise_spectrum,
const absl::optional<int>& narrow_peak_band,
bool saturated_echo,
const std::vector<std::vector<std::vector<float>>>& render,
const Block& render,
const std::array<float, kFftLengthBy2Plus1>& low_band_gain) const {
RTC_DCHECK_LT(0, render.size());
if (render.size() == 1) {
RTC_DCHECK_LT(0, render.NumBands());
if (render.NumBands() == 1) {
return 1.f;
}
const size_t num_render_channels = render[0].size();
const int num_render_channels = render.NumChannels();
if (narrow_peak_band &&
(*narrow_peak_band > static_cast<int>(kFftLengthBy2Plus1 - 10))) {
@ -135,16 +135,19 @@ float SuppressionGain::UpperBandsGain(
// Compute the upper and lower band energies.
const auto sum_of_squares = [](float a, float b) { return a + b * b; };
float low_band_energy = 0.f;
for (size_t ch = 0; ch < num_render_channels; ++ch) {
for (int ch = 0; ch < num_render_channels; ++ch) {
// TODO(bugs.webrtc.org/14108): Fix the computation below to compute the
// energy for each channel.
const float channel_energy = std::accumulate(
render[0][0].begin(), render[0][0].end(), 0.f, sum_of_squares);
render.begin(/*band=0*/ 0, /*channel=*/0),
render.end(/*band=0*/ 0, /*channel=*/0), 0.f, sum_of_squares);
low_band_energy = std::max(low_band_energy, channel_energy);
}
float high_band_energy = 0.f;
for (size_t k = 1; k < render.size(); ++k) {
for (size_t ch = 0; ch < num_render_channels; ++ch) {
for (int k = 1; k < render.NumBands(); ++k) {
for (int ch = 0; ch < num_render_channels; ++ch) {
const float energy = std::accumulate(
render[k][ch].begin(), render[k][ch].end(), 0.f, sum_of_squares);
render.begin(k, ch), render.end(k, ch), 0.f, sum_of_squares);
high_band_energy = std::max(high_band_energy, energy);
}
}
@ -372,7 +375,7 @@ void SuppressionGain::GetGain(
comfort_noise_spectrum,
const RenderSignalAnalyzer& render_signal_analyzer,
const AecState& aec_state,
const std::vector<std::vector<std::vector<float>>>& render,
const Block& render,
bool clock_drift,
float* high_bands_gain,
std::array<float, kFftLengthBy2Plus1>* low_band_gain) {
@ -417,20 +420,17 @@ void SuppressionGain::SetInitialState(bool state) {
// Detects when the render signal can be considered to have low power and
// consist of stationary noise.
bool SuppressionGain::LowNoiseRenderDetector::Detect(
const std::vector<std::vector<std::vector<float>>>& render) {
bool SuppressionGain::LowNoiseRenderDetector::Detect(const Block& render) {
float x2_sum = 0.f;
float x2_max = 0.f;
for (const auto& x_ch : render[0]) {
for (const auto& x_k : x_ch) {
for (int ch = 0; ch < render.NumChannels(); ++ch) {
for (float x_k : render.View(/*band=*/0, ch)) {
const float x2 = x_k * x_k;
x2_sum += x2;
x2_max = std::max(x2_max, x2);
}
}
const size_t num_render_channels = render[0].size();
x2_sum = x2_sum / num_render_channels;
;
x2_sum = x2_sum / render.NumChannels();
constexpr float kThreshold = 50.f * 50.f * 64.f;
const bool low_noise_render =

View File

@ -51,7 +51,7 @@ class SuppressionGain {
comfort_noise_spectrum,
const RenderSignalAnalyzer& render_signal_analyzer,
const AecState& aec_state,
const std::vector<std::vector<std::vector<float>>>& render,
const Block& render,
bool clock_drift,
float* high_bands_gain,
std::array<float, kFftLengthBy2Plus1>* low_band_gain);
@ -71,7 +71,7 @@ class SuppressionGain {
comfort_noise_spectrum,
const absl::optional<int>& narrow_peak_band,
bool saturated_echo,
const std::vector<std::vector<std::vector<float>>>& render,
const Block& render,
const std::array<float, kFftLengthBy2Plus1>& low_band_gain) const;
void GainToNoAudibleEcho(const std::array<float, kFftLengthBy2Plus1>& nearend,
@ -100,7 +100,7 @@ class SuppressionGain {
class LowNoiseRenderDetector {
public:
bool Detect(const std::vector<std::vector<std::vector<float>>>& render);
bool Detect(const Block& render);
private:
float average_power_ = 32768.f * 32768.f;

View File

@ -47,10 +47,7 @@ TEST(SuppressionGainDeathTest, NullOutputGains) {
SuppressionGain(EchoCanceller3Config{}, DetectOptimization(), 16000, 1)
.GetGain(E2, S2, R2, R2_unbounded, N2,
RenderSignalAnalyzer((EchoCanceller3Config{})), aec_state,
std::vector<std::vector<std::vector<float>>>(
3, std::vector<std::vector<float>>(
1, std::vector<float>(kBlockSize, 0.0f))),
false, &high_bands_gain, nullptr),
Block(3, 1), false, &high_bands_gain, nullptr),
"");
}
@ -76,9 +73,7 @@ TEST(SuppressionGain, BasicGainComputation) {
std::vector<std::array<float, kFftLengthBy2Plus1>> N2(kNumCaptureChannels);
std::array<float, kFftLengthBy2Plus1> g;
std::vector<SubtractorOutput> output(kNumCaptureChannels);
std::vector<std::vector<std::vector<float>>> x(
kNumBands, std::vector<std::vector<float>>(
kNumRenderChannels, std::vector<float>(kBlockSize, 0.0f)));
Block x(kNumBands, kNumRenderChannels);
EchoCanceller3Config config;
AecState aec_state(config, kNumCaptureChannels);
ApmDataDumper data_dumper(42);