Redesign of the render buffering in AEC3

This CL centralizes the render buffering in AEC3 so that all render
buffers are updated and synchronized/aligned with the render alignment
buffer.

Bug: webrtc:8597, chromium:790905
Change-Id: I8a94e5c1f27316b6100b420eec9652ea31c1a91d
Reviewed-on: https://webrtc-review.googlesource.com/25680
Commit-Queue: Per Åhgren <peah@webrtc.org>
Reviewed-by: Gustaf Ullberg <gustaf@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#20989}
This commit is contained in:
Per Åhgren
2017-12-01 23:01:44 +01:00
committed by Commit Bot
parent f388803265
commit 8ba5861f7e
55 changed files with 1213 additions and 804 deletions

View File

@ -62,6 +62,8 @@ rtc_static_library("audio_processing") {
"aec3/erl_estimator.h", "aec3/erl_estimator.h",
"aec3/erle_estimator.cc", "aec3/erle_estimator.cc",
"aec3/erle_estimator.h", "aec3/erle_estimator.h",
"aec3/fft_buffer.cc",
"aec3/fft_buffer.h",
"aec3/fft_data.h", "aec3/fft_data.h",
"aec3/frame_blocker.cc", "aec3/frame_blocker.cc",
"aec3/frame_blocker.h", "aec3/frame_blocker.h",
@ -71,6 +73,8 @@ rtc_static_library("audio_processing") {
"aec3/matched_filter.h", "aec3/matched_filter.h",
"aec3/matched_filter_lag_aggregator.cc", "aec3/matched_filter_lag_aggregator.cc",
"aec3/matched_filter_lag_aggregator.h", "aec3/matched_filter_lag_aggregator.h",
"aec3/matrix_buffer.cc",
"aec3/matrix_buffer.h",
"aec3/output_selector.cc", "aec3/output_selector.cc",
"aec3/output_selector.h", "aec3/output_selector.h",
"aec3/render_buffer.cc", "aec3/render_buffer.cc",
@ -94,6 +98,8 @@ rtc_static_library("audio_processing") {
"aec3/suppression_filter.h", "aec3/suppression_filter.h",
"aec3/suppression_gain.cc", "aec3/suppression_gain.cc",
"aec3/suppression_gain.h", "aec3/suppression_gain.h",
"aec3/vector_buffer.cc",
"aec3/vector_buffer.h",
"aec3/vector_math.h", "aec3/vector_math.h",
"aecm/aecm_core.cc", "aecm/aecm_core.cc",
"aecm/aecm_core.h", "aecm/aecm_core.h",

View File

@ -21,6 +21,7 @@
#include "modules/audio_processing/aec3/aec3_fft.h" #include "modules/audio_processing/aec3/aec3_fft.h"
#include "modules/audio_processing/aec3/aec_state.h" #include "modules/audio_processing/aec3/aec_state.h"
#include "modules/audio_processing/aec3/cascaded_biquad_filter.h" #include "modules/audio_processing/aec3/cascaded_biquad_filter.h"
#include "modules/audio_processing/aec3/render_delay_buffer.h"
#include "modules/audio_processing/aec3/render_signal_analyzer.h" #include "modules/audio_processing/aec3/render_signal_analyzer.h"
#include "modules/audio_processing/aec3/shadow_filter_update_gain.h" #include "modules/audio_processing/aec3/shadow_filter_update_gain.h"
#include "modules/audio_processing/logging/apm_data_dumper.h" #include "modules/audio_processing/logging/apm_data_dumper.h"
@ -47,8 +48,8 @@ std::string ProduceDebugText(size_t delay) {
// Verifies that the optimized methods for filter adaptation are similar to // Verifies that the optimized methods for filter adaptation are similar to
// their reference counterparts. // their reference counterparts.
TEST(AdaptiveFirFilter, FilterAdaptationNeonOptimizations) { TEST(AdaptiveFirFilter, FilterAdaptationNeonOptimizations) {
RenderBuffer render_buffer(Aec3Optimization::kNone, 3, 12, std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
std::vector<size_t>(1, 12)); RenderDelayBuffer::Create(EchoCanceller3Config(), 3));
Random random_generator(42U); Random random_generator(42U);
std::vector<std::vector<float>> x(3, std::vector<float>(kBlockSize, 0.f)); std::vector<std::vector<float>> x(3, std::vector<float>(kBlockSize, 0.f));
FftData S_C; FftData S_C;
@ -66,8 +67,13 @@ TEST(AdaptiveFirFilter, FilterAdaptationNeonOptimizations) {
for (size_t k = 0; k < 30; ++k) { for (size_t k = 0; k < 30; ++k) {
RandomizeSampleVector(&random_generator, x[0]); RandomizeSampleVector(&random_generator, x[0]);
render_buffer.Insert(x); render_delay_buffer->Insert(x);
if (k == 0) {
render_delay_buffer->Reset();
} }
render_delay_buffer->PrepareCaptureCall();
}
const auto& render_buffer = render_delay_buffer->GetRenderBuffer();
for (size_t j = 0; j < G.re.size(); ++j) { for (size_t j = 0; j < G.re.size(); ++j) {
G.re[j] = j / 10001.f; G.re[j] = j / 10001.f;
@ -153,8 +159,8 @@ TEST(AdaptiveFirFilter, UpdateErlNeonOptimization) {
TEST(AdaptiveFirFilter, FilterAdaptationSse2Optimizations) { TEST(AdaptiveFirFilter, FilterAdaptationSse2Optimizations) {
bool use_sse2 = (WebRtc_GetCPUInfo(kSSE2) != 0); bool use_sse2 = (WebRtc_GetCPUInfo(kSSE2) != 0);
if (use_sse2) { if (use_sse2) {
RenderBuffer render_buffer(Aec3Optimization::kNone, 3, 12, std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
std::vector<size_t>(1, 12)); RenderDelayBuffer::Create(EchoCanceller3Config(), 3));
Random random_generator(42U); Random random_generator(42U);
std::vector<std::vector<float>> x(3, std::vector<float>(kBlockSize, 0.f)); std::vector<std::vector<float>> x(3, std::vector<float>(kBlockSize, 0.f));
FftData S_C; FftData S_C;
@ -172,7 +178,12 @@ TEST(AdaptiveFirFilter, FilterAdaptationSse2Optimizations) {
for (size_t k = 0; k < 500; ++k) { for (size_t k = 0; k < 500; ++k) {
RandomizeSampleVector(&random_generator, x[0]); RandomizeSampleVector(&random_generator, x[0]);
render_buffer.Insert(x); render_delay_buffer->Insert(x);
if (k == 0) {
render_delay_buffer->Reset();
}
render_delay_buffer->PrepareCaptureCall();
const auto& render_buffer = render_delay_buffer->GetRenderBuffer();
ApplyFilter_SSE2(render_buffer, H_SSE2, &S_SSE2); ApplyFilter_SSE2(render_buffer, H_SSE2, &S_SSE2);
ApplyFilter(render_buffer, H_C, &S_C); ApplyFilter(render_buffer, H_C, &S_C);
@ -264,10 +275,10 @@ TEST(AdaptiveFirFilter, NullDataDumper) {
TEST(AdaptiveFirFilter, NullFilterOutput) { TEST(AdaptiveFirFilter, NullFilterOutput) {
ApmDataDumper data_dumper(42); ApmDataDumper data_dumper(42);
AdaptiveFirFilter filter(9, DetectOptimization(), &data_dumper); AdaptiveFirFilter filter(9, DetectOptimization(), &data_dumper);
RenderBuffer render_buffer(Aec3Optimization::kNone, 3, std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
filter.SizePartitions(), RenderDelayBuffer::Create(EchoCanceller3Config(), 3));
std::vector<size_t>(1, filter.SizePartitions())); EXPECT_DEATH(filter.Filter(render_delay_buffer->GetRenderBuffer(), nullptr),
EXPECT_DEATH(filter.Filter(render_buffer, nullptr), ""); "");
} }
#endif #endif
@ -295,11 +306,13 @@ TEST(AdaptiveFirFilter, FilterSize) {
TEST(AdaptiveFirFilter, FilterAndAdapt) { TEST(AdaptiveFirFilter, FilterAndAdapt) {
constexpr size_t kNumBlocksToProcess = 500; constexpr size_t kNumBlocksToProcess = 500;
ApmDataDumper data_dumper(42); ApmDataDumper data_dumper(42);
AdaptiveFirFilter filter(9, DetectOptimization(), &data_dumper); AdaptiveFirFilter filter(kAdaptiveFilterLength, DetectOptimization(),
&data_dumper);
Aec3Fft fft; Aec3Fft fft;
RenderBuffer render_buffer(Aec3Optimization::kNone, 3, EchoCanceller3Config config;
filter.SizePartitions(), config.delay.min_echo_path_delay_blocks = 0;
std::vector<size_t>(1, filter.SizePartitions())); std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
RenderDelayBuffer::Create(config, 3));
ShadowFilterUpdateGain gain; ShadowFilterUpdateGain gain;
Random random_generator(42U); Random random_generator(42U);
std::vector<std::vector<float>> x(3, std::vector<float>(kBlockSize, 0.f)); std::vector<std::vector<float>> x(3, std::vector<float>(kBlockSize, 0.f));
@ -345,7 +358,13 @@ TEST(AdaptiveFirFilter, FilterAndAdapt) {
x_hp_filter.Process(x[0]); x_hp_filter.Process(x[0]);
y_hp_filter.Process(y); y_hp_filter.Process(y);
render_buffer.Insert(x); render_delay_buffer->Insert(x);
if (k == 0) {
render_delay_buffer->Reset();
}
render_delay_buffer->PrepareCaptureCall();
const auto& render_buffer = render_delay_buffer->GetRenderBuffer();
render_signal_analyzer.Update(render_buffer, aec_state.FilterDelay()); render_signal_analyzer.Update(render_buffer, aec_state.FilterDelay());
filter.Filter(render_buffer, &S); filter.Filter(render_buffer, &S);
@ -363,7 +382,8 @@ TEST(AdaptiveFirFilter, FilterAndAdapt) {
gain.Compute(render_buffer, render_signal_analyzer, E, gain.Compute(render_buffer, render_signal_analyzer, E,
filter.SizePartitions(), false, &G); filter.SizePartitions(), false, &G);
filter.Adapt(render_buffer, G); filter.Adapt(render_buffer, G);
aec_state.HandleEchoPathChange(EchoPathVariability(false, false)); aec_state.HandleEchoPathChange(EchoPathVariability(
false, EchoPathVariability::DelayAdjustment::kNone, false));
aec_state.Update(filter.FilterFrequencyResponse(), aec_state.Update(filter.FilterFrequencyResponse(),
filter.FilterImpulseResponse(), true, rtc::nullopt, filter.FilterImpulseResponse(), true, rtc::nullopt,
render_buffer, E2_main, Y2, x[0], s, false); render_buffer, E2_main, Y2, x[0], s, false);

View File

@ -42,6 +42,7 @@ constexpr int kAdaptiveFilterLength = 12;
constexpr int kUnknownDelayRenderWindowSize = 30; constexpr int kUnknownDelayRenderWindowSize = 30;
constexpr int kAdaptiveFilterTimeDomainLength = constexpr int kAdaptiveFilterTimeDomainLength =
kAdaptiveFilterLength * kFftLengthBy2; kAdaptiveFilterLength * kFftLengthBy2;
constexpr int kRenderTransferQueueSizeFrames = 100;
constexpr size_t kMaxNumBands = 3; constexpr size_t kMaxNumBands = 3;
constexpr size_t kSubFrameLength = 80; constexpr size_t kSubFrameLength = 80;
@ -52,11 +53,6 @@ constexpr size_t kMatchedFilterWindowSizeSubBlocks = 32;
constexpr size_t kMatchedFilterAlignmentShiftSizeSubBlocks = constexpr size_t kMatchedFilterAlignmentShiftSizeSubBlocks =
kMatchedFilterWindowSizeSubBlocks * 3 / 4; kMatchedFilterWindowSizeSubBlocks * 3 / 4;
constexpr size_t kMinEchoPathDelayBlocks = 5;
constexpr size_t kMaxApiCallsJitterBlocks = 26;
constexpr size_t kRenderTransferQueueSize = kMaxApiCallsJitterBlocks / 2;
static_assert(2 * kRenderTransferQueueSize >= kMaxApiCallsJitterBlocks,
"Requirement to ensure buffer overflow detection");
constexpr size_t kEchoPathChangeConvergenceBlocks = 2 * kNumBlocksPerSecond; constexpr size_t kEchoPathChangeConvergenceBlocks = 2 * kNumBlocksPerSecond;
@ -83,9 +79,8 @@ constexpr size_t GetDownSampledBufferSize(size_t down_sampling_factor,
constexpr size_t GetRenderDelayBufferSize(size_t down_sampling_factor, constexpr size_t GetRenderDelayBufferSize(size_t down_sampling_factor,
size_t num_matched_filters) { size_t num_matched_filters) {
return (3 * return GetDownSampledBufferSize(down_sampling_factor, num_matched_filters) /
GetDownSampledBufferSize(down_sampling_factor, num_matched_filters)) / (kBlockSize / down_sampling_factor);
(4 * kBlockSize / down_sampling_factor);
} }
// Detects what kind of optimizations to use for the code. // Detects what kind of optimizations to use for the code.

View File

@ -64,28 +64,45 @@ AecState::~AecState() = default;
void AecState::HandleEchoPathChange( void AecState::HandleEchoPathChange(
const EchoPathVariability& echo_path_variability) { const EchoPathVariability& echo_path_variability) {
if (echo_path_variability.AudioPathChanged()) { const auto full_reset = [&]() {
blocks_since_last_saturation_ = kUnknownDelayRenderWindowSize + 1; blocks_since_last_saturation_ = kUnknownDelayRenderWindowSize + 1;
usable_linear_estimate_ = false; usable_linear_estimate_ = false;
echo_leakage_detected_ = false; echo_leakage_detected_ = false;
capture_signal_saturation_ = false; capture_signal_saturation_ = false;
echo_saturation_ = false; echo_saturation_ = false;
max_render_.fill(0.f); max_render_.fill(0.f);
if (echo_path_variability.delay_change) {
force_zero_gain_counter_ = 0; force_zero_gain_counter_ = 0;
blocks_with_filter_adaptation_ = 0; blocks_with_filter_adaptation_ = 0;
blocks_with_strong_render_ = 0; blocks_with_strong_render_ = 0;
initial_state_ = true; initial_state_ = true;
capture_block_counter_ = 0;
linear_echo_estimate_ = false; linear_echo_estimate_ = false;
sufficient_filter_updates_ = false; sufficient_filter_updates_ = false;
render_received_ = false; render_received_ = false;
force_zero_gain_ = true; force_zero_gain_ = true;
capture_block_counter_ = 0; };
}
// TODO(peah): Refine the reset scheme according to the type of gain and
// delay adjustment.
if (echo_path_variability.gain_change) { if (echo_path_variability.gain_change) {
capture_block_counter_ = kNumBlocksPerSecond; full_reset();
} }
if (echo_path_variability.delay_change !=
EchoPathVariability::DelayAdjustment::kBufferReadjustment) {
full_reset();
} else if (echo_path_variability.delay_change !=
EchoPathVariability::DelayAdjustment::kBufferFlush) {
full_reset();
} else if (echo_path_variability.delay_change !=
EchoPathVariability::DelayAdjustment::kDelayReset) {
full_reset();
} else if (echo_path_variability.delay_change !=
EchoPathVariability::DelayAdjustment::kNewDetectedDelay) {
full_reset();
} else if (echo_path_variability.gain_change) {
capture_block_counter_ = kNumBlocksPerSecond;
} }
} }

View File

@ -10,6 +10,8 @@
#include "modules/audio_processing/aec3/aec_state.h" #include "modules/audio_processing/aec3/aec_state.h"
#include "modules/audio_processing/aec3/aec3_fft.h"
#include "modules/audio_processing/aec3/render_delay_buffer.h"
#include "modules/audio_processing/logging/apm_data_dumper.h" #include "modules/audio_processing/logging/apm_data_dumper.h"
#include "test/gtest.h" #include "test/gtest.h"
@ -18,14 +20,17 @@ namespace webrtc {
// Verify the general functionality of AecState // Verify the general functionality of AecState
TEST(AecState, NormalUsage) { TEST(AecState, NormalUsage) {
ApmDataDumper data_dumper(42); ApmDataDumper data_dumper(42);
AecState state(EchoCanceller3Config{}); EchoCanceller3Config config;
RenderBuffer render_buffer(Aec3Optimization::kNone, 3, 30, AecState state(config);
std::vector<size_t>(1, 30)); std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
RenderDelayBuffer::Create(config, 3));
std::array<float, kFftLengthBy2Plus1> E2_main = {}; std::array<float, kFftLengthBy2Plus1> E2_main = {};
std::array<float, kFftLengthBy2Plus1> Y2 = {}; std::array<float, kFftLengthBy2Plus1> Y2 = {};
std::vector<std::vector<float>> x(3, std::vector<float>(kBlockSize, 0.f)); std::vector<std::vector<float>> x(3, std::vector<float>(kBlockSize, 0.f));
EchoPathVariability echo_path_variability(false, false); EchoPathVariability echo_path_variability(
false, EchoPathVariability::DelayAdjustment::kNone, false);
std::array<float, kBlockSize> s; std::array<float, kBlockSize> s;
Aec3Fft fft;
s.fill(100.f); s.fill(100.f);
std::vector<std::array<float, kFftLengthBy2Plus1>> std::vector<std::array<float, kFftLengthBy2Plus1>>
@ -44,44 +49,53 @@ TEST(AecState, NormalUsage) {
// Verify that linear AEC usability is false when the filter is diverged and // Verify that linear AEC usability is false when the filter is diverged and
// there is no external delay reported. // there is no external delay reported.
state.Update(diverged_filter_frequency_response, impulse_response, true, state.Update(diverged_filter_frequency_response, impulse_response, true,
rtc::nullopt, render_buffer, E2_main, Y2, x[0], s, false); rtc::nullopt, render_delay_buffer->GetRenderBuffer(), E2_main,
Y2, x[0], s, false);
EXPECT_FALSE(state.UsableLinearEstimate()); EXPECT_FALSE(state.UsableLinearEstimate());
// Verify that linear AEC usability is true when the filter is converged // Verify that linear AEC usability is true when the filter is converged
std::fill(x[0].begin(), x[0].end(), 101.f); std::fill(x[0].begin(), x[0].end(), 101.f);
for (int k = 0; k < 3000; ++k) { for (int k = 0; k < 3000; ++k) {
state.Update(converged_filter_frequency_response, impulse_response, true, 2, state.Update(converged_filter_frequency_response, impulse_response, true, 2,
render_buffer, E2_main, Y2, x[0], s, false); render_delay_buffer->GetRenderBuffer(), E2_main, Y2, x[0], s,
false);
} }
EXPECT_TRUE(state.UsableLinearEstimate()); EXPECT_TRUE(state.UsableLinearEstimate());
// Verify that linear AEC usability becomes false after an echo path change is // Verify that linear AEC usability becomes false after an echo path change is
// reported // reported
state.HandleEchoPathChange(EchoPathVariability(true, false)); state.HandleEchoPathChange(EchoPathVariability(
true, EchoPathVariability::DelayAdjustment::kNone, false));
state.Update(converged_filter_frequency_response, impulse_response, true, 2, state.Update(converged_filter_frequency_response, impulse_response, true, 2,
render_buffer, E2_main, Y2, x[0], s, false); render_delay_buffer->GetRenderBuffer(), E2_main, Y2, x[0], s,
false);
EXPECT_FALSE(state.UsableLinearEstimate()); EXPECT_FALSE(state.UsableLinearEstimate());
// Verify that the active render detection works as intended. // Verify that the active render detection works as intended.
std::fill(x[0].begin(), x[0].end(), 101.f); std::fill(x[0].begin(), x[0].end(), 101.f);
state.HandleEchoPathChange(EchoPathVariability(true, true)); state.HandleEchoPathChange(EchoPathVariability(
true, EchoPathVariability::DelayAdjustment::kNewDetectedDelay, false));
state.Update(converged_filter_frequency_response, impulse_response, true, 2, state.Update(converged_filter_frequency_response, impulse_response, true, 2,
render_buffer, E2_main, Y2, x[0], s, false); render_delay_buffer->GetRenderBuffer(), E2_main, Y2, x[0], s,
false);
EXPECT_FALSE(state.ActiveRender()); EXPECT_FALSE(state.ActiveRender());
for (int k = 0; k < 1000; ++k) { for (int k = 0; k < 1000; ++k) {
state.Update(converged_filter_frequency_response, impulse_response, true, 2, state.Update(converged_filter_frequency_response, impulse_response, true, 2,
render_buffer, E2_main, Y2, x[0], s, false); render_delay_buffer->GetRenderBuffer(), E2_main, Y2, x[0], s,
false);
} }
EXPECT_TRUE(state.ActiveRender()); EXPECT_TRUE(state.ActiveRender());
// Verify that echo leakage is properly reported. // Verify that echo leakage is properly reported.
state.Update(converged_filter_frequency_response, impulse_response, true, 2, state.Update(converged_filter_frequency_response, impulse_response, true, 2,
render_buffer, E2_main, Y2, x[0], s, false); render_delay_buffer->GetRenderBuffer(), E2_main, Y2, x[0], s,
false);
EXPECT_FALSE(state.EchoLeakageDetected()); EXPECT_FALSE(state.EchoLeakageDetected());
state.Update(converged_filter_frequency_response, impulse_response, true, 2, state.Update(converged_filter_frequency_response, impulse_response, true, 2,
render_buffer, E2_main, Y2, x[0], s, true); render_delay_buffer->GetRenderBuffer(), E2_main, Y2, x[0], s,
true);
EXPECT_TRUE(state.EchoLeakageDetected()); EXPECT_TRUE(state.EchoLeakageDetected());
// Verify that the ERL is properly estimated // Verify that the ERL is properly estimated
@ -90,14 +104,20 @@ TEST(AecState, NormalUsage) {
} }
x[0][0] = 5000.f; x[0][0] = 5000.f;
for (size_t k = 0; k < render_buffer.Buffer().size(); ++k) { for (size_t k = 0; k < render_delay_buffer->GetRenderBuffer().Buffer().size();
render_buffer.Insert(x); ++k) {
render_delay_buffer->Insert(x);
if (k == 0) {
render_delay_buffer->Reset();
}
render_delay_buffer->PrepareCaptureCall();
} }
Y2.fill(10.f * 10000.f * 10000.f); Y2.fill(10.f * 10000.f * 10000.f);
for (size_t k = 0; k < 1000; ++k) { for (size_t k = 0; k < 1000; ++k) {
state.Update(converged_filter_frequency_response, impulse_response, true, 2, state.Update(converged_filter_frequency_response, impulse_response, true, 2,
render_buffer, E2_main, Y2, x[0], s, false); render_delay_buffer->GetRenderBuffer(), E2_main, Y2, x[0], s,
false);
} }
ASSERT_TRUE(state.UsableLinearEstimate()); ASSERT_TRUE(state.UsableLinearEstimate());
@ -113,7 +133,8 @@ TEST(AecState, NormalUsage) {
Y2.fill(10.f * E2_main[0]); Y2.fill(10.f * E2_main[0]);
for (size_t k = 0; k < 1000; ++k) { for (size_t k = 0; k < 1000; ++k) {
state.Update(converged_filter_frequency_response, impulse_response, true, 2, state.Update(converged_filter_frequency_response, impulse_response, true, 2,
render_buffer, E2_main, Y2, x[0], s, false); render_delay_buffer->GetRenderBuffer(), E2_main, Y2, x[0], s,
false);
} }
ASSERT_TRUE(state.UsableLinearEstimate()); ASSERT_TRUE(state.UsableLinearEstimate());
{ {
@ -133,7 +154,8 @@ TEST(AecState, NormalUsage) {
Y2.fill(5.f * E2_main[0]); Y2.fill(5.f * E2_main[0]);
for (size_t k = 0; k < 1000; ++k) { for (size_t k = 0; k < 1000; ++k) {
state.Update(converged_filter_frequency_response, impulse_response, true, 2, state.Update(converged_filter_frequency_response, impulse_response, true, 2,
render_buffer, E2_main, Y2, x[0], s, false); render_delay_buffer->GetRenderBuffer(), E2_main, Y2, x[0], s,
false);
} }
ASSERT_TRUE(state.UsableLinearEstimate()); ASSERT_TRUE(state.UsableLinearEstimate());
@ -154,13 +176,15 @@ TEST(AecState, NormalUsage) {
// Verifies the delay for a converged filter is correctly identified. // Verifies the delay for a converged filter is correctly identified.
TEST(AecState, ConvergedFilterDelay) { TEST(AecState, ConvergedFilterDelay) {
constexpr int kFilterLength = 10; constexpr int kFilterLength = 10;
AecState state(EchoCanceller3Config{}); EchoCanceller3Config config;
RenderBuffer render_buffer(Aec3Optimization::kNone, 3, 30, AecState state(config);
std::vector<size_t>(1, 30)); std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
RenderDelayBuffer::Create(config, 3));
std::array<float, kFftLengthBy2Plus1> E2_main; std::array<float, kFftLengthBy2Plus1> E2_main;
std::array<float, kFftLengthBy2Plus1> Y2; std::array<float, kFftLengthBy2Plus1> Y2;
std::array<float, kBlockSize> x; std::array<float, kBlockSize> x;
EchoPathVariability echo_path_variability(false, false); EchoPathVariability echo_path_variability(
false, EchoPathVariability::DelayAdjustment::kNone, false);
std::array<float, kBlockSize> s; std::array<float, kBlockSize> s;
s.fill(100.f); s.fill(100.f);
x.fill(0.f); x.fill(0.f);
@ -180,7 +204,8 @@ TEST(AecState, ConvergedFilterDelay) {
frequency_response[k][0] = 0.f; frequency_response[k][0] = 0.f;
state.HandleEchoPathChange(echo_path_variability); state.HandleEchoPathChange(echo_path_variability);
state.Update(frequency_response, impulse_response, true, rtc::nullopt, state.Update(frequency_response, impulse_response, true, rtc::nullopt,
render_buffer, E2_main, Y2, x, s, false); render_delay_buffer->GetRenderBuffer(), E2_main, Y2, x, s,
false);
EXPECT_TRUE(k == (kFilterLength - 1) || state.FilterDelay()); EXPECT_TRUE(k == (kFilterLength - 1) || state.FilterDelay());
if (k != (kFilterLength - 1)) { if (k != (kFilterLength - 1)) {
EXPECT_EQ(k, state.FilterDelay()); EXPECT_EQ(k, state.FilterDelay());
@ -190,7 +215,10 @@ TEST(AecState, ConvergedFilterDelay) {
// Verify that the externally reported delay is properly reported and converted. // Verify that the externally reported delay is properly reported and converted.
TEST(AecState, ExternalDelay) { TEST(AecState, ExternalDelay) {
AecState state(EchoCanceller3Config{}); EchoCanceller3Config config;
AecState state(config);
std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
RenderDelayBuffer::Create(config, 3));
std::array<float, kFftLengthBy2Plus1> E2_main; std::array<float, kFftLengthBy2Plus1> E2_main;
std::array<float, kFftLengthBy2Plus1> E2_shadow; std::array<float, kFftLengthBy2Plus1> E2_shadow;
std::array<float, kFftLengthBy2Plus1> Y2; std::array<float, kFftLengthBy2Plus1> Y2;
@ -201,8 +229,7 @@ TEST(AecState, ExternalDelay) {
E2_shadow.fill(0.f); E2_shadow.fill(0.f);
Y2.fill(0.f); Y2.fill(0.f);
x.fill(0.f); x.fill(0.f);
RenderBuffer render_buffer(Aec3Optimization::kNone, 3, 30,
std::vector<size_t>(1, 30));
std::vector<std::array<float, kFftLengthBy2Plus1>> frequency_response( std::vector<std::array<float, kFftLengthBy2Plus1>> frequency_response(
kAdaptiveFilterLength); kAdaptiveFilterLength);
for (auto& v : frequency_response) { for (auto& v : frequency_response) {
@ -213,18 +240,22 @@ TEST(AecState, ExternalDelay) {
impulse_response.fill(0.f); impulse_response.fill(0.f);
for (size_t k = 0; k < frequency_response.size() - 1; ++k) { for (size_t k = 0; k < frequency_response.size() - 1; ++k) {
state.HandleEchoPathChange(EchoPathVariability(false, false)); state.HandleEchoPathChange(EchoPathVariability(
false, EchoPathVariability::DelayAdjustment::kNone, false));
state.Update(frequency_response, impulse_response, true, k * kBlockSize + 5, state.Update(frequency_response, impulse_response, true, k * kBlockSize + 5,
render_buffer, E2_main, Y2, x, s, false); render_delay_buffer->GetRenderBuffer(), E2_main, Y2, x, s,
false);
EXPECT_TRUE(state.ExternalDelay()); EXPECT_TRUE(state.ExternalDelay());
EXPECT_EQ(k, state.ExternalDelay()); EXPECT_EQ(k, state.ExternalDelay());
} }
// Verify that the externally reported delay is properly unset when it is no // Verify that the externally reported delay is properly unset when it is no
// longer present. // longer present.
state.HandleEchoPathChange(EchoPathVariability(false, false)); state.HandleEchoPathChange(EchoPathVariability(
false, EchoPathVariability::DelayAdjustment::kNone, false));
state.Update(frequency_response, impulse_response, true, rtc::nullopt, state.Update(frequency_response, impulse_response, true, rtc::nullopt,
render_buffer, E2_main, Y2, x, s, false); render_delay_buffer->GetRenderBuffer(), E2_main, Y2, x, s,
false);
EXPECT_FALSE(state.ExternalDelay()); EXPECT_FALSE(state.ExternalDelay());
} }

View File

@ -25,7 +25,8 @@ enum class BlockProcessorApiCall { kCapture, kRender };
class BlockProcessorImpl final : public BlockProcessor { class BlockProcessorImpl final : public BlockProcessor {
public: public:
BlockProcessorImpl(int sample_rate_hz, BlockProcessorImpl(const EchoCanceller3Config& config,
int sample_rate_hz,
std::unique_ptr<RenderDelayBuffer> render_buffer, std::unique_ptr<RenderDelayBuffer> render_buffer,
std::unique_ptr<RenderDelayController> delay_controller, std::unique_ptr<RenderDelayController> delay_controller,
std::unique_ptr<EchoRemover> echo_remover); std::unique_ptr<EchoRemover> echo_remover);
@ -44,31 +45,35 @@ class BlockProcessorImpl final : public BlockProcessor {
private: private:
static int instance_count_; static int instance_count_;
bool no_capture_data_received_ = true;
bool no_render_data_received_ = true;
std::unique_ptr<ApmDataDumper> data_dumper_; std::unique_ptr<ApmDataDumper> data_dumper_;
const EchoCanceller3Config config_;
bool capture_properly_started_ = false;
bool render_properly_started_ = false;
const size_t sample_rate_hz_; const size_t sample_rate_hz_;
std::unique_ptr<RenderDelayBuffer> render_buffer_; std::unique_ptr<RenderDelayBuffer> render_buffer_;
std::unique_ptr<RenderDelayController> delay_controller_; std::unique_ptr<RenderDelayController> delay_controller_;
std::unique_ptr<EchoRemover> echo_remover_; std::unique_ptr<EchoRemover> echo_remover_;
BlockProcessorMetrics metrics_; BlockProcessorMetrics metrics_;
bool render_buffer_overrun_occurred_ = false; RenderDelayBuffer::BufferingEvent render_event_;
RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(BlockProcessorImpl); RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(BlockProcessorImpl);
}; };
int BlockProcessorImpl::instance_count_ = 0; int BlockProcessorImpl::instance_count_ = 0;
BlockProcessorImpl::BlockProcessorImpl( BlockProcessorImpl::BlockProcessorImpl(
const EchoCanceller3Config& config,
int sample_rate_hz, int sample_rate_hz,
std::unique_ptr<RenderDelayBuffer> render_buffer, std::unique_ptr<RenderDelayBuffer> render_buffer,
std::unique_ptr<RenderDelayController> delay_controller, std::unique_ptr<RenderDelayController> delay_controller,
std::unique_ptr<EchoRemover> echo_remover) std::unique_ptr<EchoRemover> echo_remover)
: data_dumper_( : data_dumper_(
new ApmDataDumper(rtc::AtomicOps::Increment(&instance_count_))), new ApmDataDumper(rtc::AtomicOps::Increment(&instance_count_))),
config_(config),
sample_rate_hz_(sample_rate_hz), sample_rate_hz_(sample_rate_hz),
render_buffer_(std::move(render_buffer)), render_buffer_(std::move(render_buffer)),
delay_controller_(std::move(delay_controller)), delay_controller_(std::move(delay_controller)),
echo_remover_(std::move(echo_remover)) { echo_remover_(std::move(echo_remover)),
render_event_(RenderDelayBuffer::BufferingEvent::kNone) {
RTC_DCHECK(ValidFullBandRate(sample_rate_hz_)); RTC_DCHECK(ValidFullBandRate(sample_rate_hz_));
} }
@ -87,67 +92,93 @@ void BlockProcessorImpl::ProcessCapture(
&(*capture_block)[0][0], &(*capture_block)[0][0],
LowestBandRate(sample_rate_hz_), 1); LowestBandRate(sample_rate_hz_), 1);
// Do not start processing until render data has been buffered as that will if (render_properly_started_) {
// cause the buffers to be wrongly aligned. if (!capture_properly_started_) {
no_capture_data_received_ = false; capture_properly_started_ = true;
if (no_render_data_received_) { render_buffer_->Reset();
}
} else {
// If no render data has yet arrived, do not process the capture signal.
return; return;
} }
EchoPathVariability echo_path_variability(
echo_path_gain_change, EchoPathVariability::DelayAdjustment::kNone,
false);
if (render_event_ == RenderDelayBuffer::BufferingEvent::kRenderOverrun &&
render_properly_started_) {
echo_path_variability.delay_change =
EchoPathVariability::DelayAdjustment::kBufferFlush;
delay_controller_->Reset();
render_buffer_->Reset();
RTC_LOG(LS_WARNING) << "Reset due to render buffer overrun.";
}
// Update the render buffers with any newly arrived render blocks and prepare
// the render buffers for reading the render data corresponding to the current
// capture block.
render_event_ = render_buffer_->PrepareCaptureCall();
RTC_DCHECK(RenderDelayBuffer::BufferingEvent::kRenderOverrun !=
render_event_);
if (render_event_ == RenderDelayBuffer::BufferingEvent::kRenderUnderrun) {
echo_path_variability.delay_change =
EchoPathVariability::DelayAdjustment::kBufferReadjustment;
delay_controller_->Reset();
render_buffer_->Reset();
} else if (render_event_ == RenderDelayBuffer::BufferingEvent::kApiCallSkew) {
// There have been too many render calls in a row. Reset to avoid noncausal
// echo.
echo_path_variability.delay_change =
EchoPathVariability::DelayAdjustment::kDelayReset;
delay_controller_->Reset();
render_buffer_->Reset();
capture_properly_started_ = false;
render_properly_started_ = false;
}
data_dumper_->DumpWav("aec3_processblock_capture_input2", kBlockSize, data_dumper_->DumpWav("aec3_processblock_capture_input2", kBlockSize,
&(*capture_block)[0][0], &(*capture_block)[0][0],
LowestBandRate(sample_rate_hz_), 1); LowestBandRate(sample_rate_hz_), 1);
bool render_buffer_underrun = false;
if (render_buffer_overrun_occurred_) {
// Reset the render buffers and the alignment functionality when there has
// been a render buffer overrun as the buffer alignment may be noncausal.
delay_controller_->Reset();
render_buffer_->Reset();
RTC_LOG(LS_WARNING) << "Reset due to detected render buffer overrun.";
}
// Update the render buffers with new render data, filling the buffers with
// empty blocks when there is no render data available.
render_buffer_underrun = !render_buffer_->UpdateBuffers();
if (render_buffer_underrun) {
RTC_LOG(LS_WARNING) << "Render API jitter buffer underrun.";
}
// Compute and and apply the render delay required to achieve proper signal // Compute and and apply the render delay required to achieve proper signal
// alignment. // alignment.
const size_t old_delay = render_buffer_->Delay(); const size_t estimated_delay = delay_controller_->GetDelay(
const size_t new_delay = delay_controller_->GetDelay(
render_buffer_->GetDownsampledRenderBuffer(), (*capture_block)[0]); render_buffer_->GetDownsampledRenderBuffer(), (*capture_block)[0]);
const size_t new_delay =
std::min(render_buffer_->MaxDelay(), estimated_delay);
bool delay_change; bool delay_change = render_buffer_->Delay() != new_delay;
if (new_delay >= kMinEchoPathDelayBlocks) { if (delay_change && new_delay >= config_.delay.min_echo_path_delay_blocks) {
echo_path_variability.delay_change =
EchoPathVariability::DelayAdjustment::kNewDetectedDelay;
render_buffer_->SetDelay(new_delay); render_buffer_->SetDelay(new_delay);
const size_t achieved_delay = render_buffer_->Delay(); RTC_DCHECK_EQ(render_buffer_->Delay(), new_delay);
delay_change = old_delay != achieved_delay || old_delay != new_delay || delay_controller_->SetDelay(new_delay);
render_buffer_overrun_occurred_; } else if (delay_change &&
new_delay < config_.delay.min_echo_path_delay_blocks) {
// Inform the delay controller of the actually set delay to allow it to // A noncausal delay has been detected. This can only happen if there is
// properly react to a non-feasible delay. // clockdrift, an audio pipeline issue has occurred or the specified minimum
delay_controller_->SetDelay(achieved_delay); // delay is too short. Perform a full reset.
} else { echo_path_variability.delay_change =
EchoPathVariability::DelayAdjustment::kDelayReset;
delay_controller_->Reset(); delay_controller_->Reset();
render_buffer_->Reset(); render_buffer_->Reset();
delay_change = true; capture_properly_started_ = false;
render_properly_started_ = false;
RTC_LOG(LS_WARNING) << "Reset due to noncausal delay."; RTC_LOG(LS_WARNING) << "Reset due to noncausal delay.";
} }
// Remove the echo from the capture signal. // Remove the echo from the capture signal.
echo_remover_->ProcessCapture( echo_remover_->ProcessCapture(
delay_controller_->AlignmentHeadroomSamples(), delay_controller_->AlignmentHeadroomSamples(), echo_path_variability,
EchoPathVariability(echo_path_gain_change, delay_change),
capture_signal_saturation, render_buffer_->GetRenderBuffer(), capture_signal_saturation, render_buffer_->GetRenderBuffer(),
capture_block); capture_block);
// Update the metrics. // Update the metrics.
metrics_.UpdateCapture(render_buffer_underrun); metrics_.UpdateCapture(false);
render_buffer_overrun_occurred_ = false; render_event_ = RenderDelayBuffer::BufferingEvent::kNone;
} }
void BlockProcessorImpl::BufferRender( void BlockProcessorImpl::BufferRender(
@ -158,23 +189,15 @@ void BlockProcessorImpl::BufferRender(
static_cast<int>(BlockProcessorApiCall::kRender)); static_cast<int>(BlockProcessorApiCall::kRender));
data_dumper_->DumpWav("aec3_processblock_render_input", kBlockSize, data_dumper_->DumpWav("aec3_processblock_render_input", kBlockSize,
&block[0][0], LowestBandRate(sample_rate_hz_), 1); &block[0][0], LowestBandRate(sample_rate_hz_), 1);
no_render_data_received_ = false;
// Do not start buffer render data until capture data has been received as
// that data may give a false alignment.
if (no_capture_data_received_) {
return;
}
data_dumper_->DumpWav("aec3_processblock_render_input2", kBlockSize, data_dumper_->DumpWav("aec3_processblock_render_input2", kBlockSize,
&block[0][0], LowestBandRate(sample_rate_hz_), 1); &block[0][0], LowestBandRate(sample_rate_hz_), 1);
// Buffer the render data. render_event_ = render_buffer_->Insert(block);
render_buffer_overrun_occurred_ = !render_buffer_->Insert(block);
// Update the metrics. metrics_.UpdateRender(render_event_ !=
metrics_.UpdateRender(render_buffer_overrun_occurred_); RenderDelayBuffer::BufferingEvent::kNone);
render_properly_started_ = true;
} }
void BlockProcessorImpl::UpdateEchoLeakageStatus(bool leakage_detected) { void BlockProcessorImpl::UpdateEchoLeakageStatus(bool leakage_detected) {
@ -191,12 +214,8 @@ void BlockProcessorImpl::GetMetrics(EchoControl::Metrics* metrics) const {
BlockProcessor* BlockProcessor::Create(const EchoCanceller3Config& config, BlockProcessor* BlockProcessor::Create(const EchoCanceller3Config& config,
int sample_rate_hz) { int sample_rate_hz) {
std::unique_ptr<RenderDelayBuffer> render_buffer(RenderDelayBuffer::Create( std::unique_ptr<RenderDelayBuffer> render_buffer(
NumBandsForRate(sample_rate_hz), config.delay.down_sampling_factor, RenderDelayBuffer::Create(config, NumBandsForRate(sample_rate_hz)));
GetDownSampledBufferSize(config.delay.down_sampling_factor,
config.delay.num_filters),
GetRenderDelayBufferSize(config.delay.down_sampling_factor,
config.delay.num_filters)));
std::unique_ptr<RenderDelayController> delay_controller( std::unique_ptr<RenderDelayController> delay_controller(
RenderDelayController::Create(config, sample_rate_hz)); RenderDelayController::Create(config, sample_rate_hz));
std::unique_ptr<EchoRemover> echo_remover( std::unique_ptr<EchoRemover> echo_remover(
@ -223,9 +242,9 @@ BlockProcessor* BlockProcessor::Create(
std::unique_ptr<RenderDelayBuffer> render_buffer, std::unique_ptr<RenderDelayBuffer> render_buffer,
std::unique_ptr<RenderDelayController> delay_controller, std::unique_ptr<RenderDelayController> delay_controller,
std::unique_ptr<EchoRemover> echo_remover) { std::unique_ptr<EchoRemover> echo_remover) {
return new BlockProcessorImpl(sample_rate_hz, std::move(render_buffer), return new BlockProcessorImpl(
std::move(delay_controller), config, sample_rate_hz, std::move(render_buffer),
std::move(echo_remover)); std::move(delay_controller), std::move(echo_remover));
} }
} // namespace webrtc } // namespace webrtc

View File

@ -115,7 +115,7 @@ TEST(BlockProcessor, DISABLED_DelayControllerIntegration) {
new StrictMock<webrtc::test::MockRenderDelayBuffer>(rate)); new StrictMock<webrtc::test::MockRenderDelayBuffer>(rate));
EXPECT_CALL(*render_delay_buffer_mock, Insert(_)) EXPECT_CALL(*render_delay_buffer_mock, Insert(_))
.Times(kNumBlocks) .Times(kNumBlocks)
.WillRepeatedly(Return(true)); .WillRepeatedly(Return(RenderDelayBuffer::BufferingEvent::kNone));
EXPECT_CALL(*render_delay_buffer_mock, IsBlockAvailable()) EXPECT_CALL(*render_delay_buffer_mock, IsBlockAvailable())
.Times(kNumBlocks) .Times(kNumBlocks)
.WillRepeatedly(Return(true)); .WillRepeatedly(Return(true));
@ -160,11 +160,12 @@ TEST(BlockProcessor, DISABLED_SubmoduleIntegration) {
EXPECT_CALL(*render_delay_buffer_mock, Insert(_)) EXPECT_CALL(*render_delay_buffer_mock, Insert(_))
.Times(kNumBlocks - 1) .Times(kNumBlocks - 1)
.WillRepeatedly(Return(true)); .WillRepeatedly(Return(RenderDelayBuffer::BufferingEvent::kNone));
EXPECT_CALL(*render_delay_buffer_mock, IsBlockAvailable()) EXPECT_CALL(*render_delay_buffer_mock, IsBlockAvailable())
.Times(kNumBlocks) .Times(kNumBlocks)
.WillRepeatedly(Return(true)); .WillRepeatedly(Return(true));
EXPECT_CALL(*render_delay_buffer_mock, UpdateBuffers()).Times(kNumBlocks); EXPECT_CALL(*render_delay_buffer_mock, PrepareCaptureCall())
.Times(kNumBlocks);
EXPECT_CALL(*render_delay_buffer_mock, SetDelay(9)).Times(AtLeast(1)); EXPECT_CALL(*render_delay_buffer_mock, SetDelay(9)).Times(AtLeast(1));
EXPECT_CALL(*render_delay_buffer_mock, Delay()) EXPECT_CALL(*render_delay_buffer_mock, Delay())
.Times(kNumBlocks) .Times(kNumBlocks)

View File

@ -24,7 +24,7 @@ namespace {
float Power(const FftData& N) { float Power(const FftData& N) {
std::array<float, kFftLengthBy2Plus1> N2; std::array<float, kFftLengthBy2Plus1> N2;
N.Spectrum(Aec3Optimization::kNone, &N2); N.Spectrum(Aec3Optimization::kNone, N2);
return std::accumulate(N2.begin(), N2.end(), 0.f) / N2.size(); return std::accumulate(N2.begin(), N2.end(), 0.f) / N2.size();
} }

View File

@ -13,7 +13,9 @@
namespace webrtc { namespace webrtc {
DownsampledRenderBuffer::DownsampledRenderBuffer(size_t downsampled_buffer_size) DownsampledRenderBuffer::DownsampledRenderBuffer(size_t downsampled_buffer_size)
: buffer(downsampled_buffer_size, 0.f) {} : size(downsampled_buffer_size), buffer(downsampled_buffer_size, 0.f) {
std::fill(buffer.begin(), buffer.end(), 0.f);
}
DownsampledRenderBuffer::~DownsampledRenderBuffer() = default; DownsampledRenderBuffer::~DownsampledRenderBuffer() = default;

View File

@ -14,6 +14,7 @@
#include <vector> #include <vector>
#include "modules/audio_processing/aec3/aec3_common.h" #include "modules/audio_processing/aec3/aec3_common.h"
#include "rtc_base/checks.h"
namespace webrtc { namespace webrtc {
@ -21,8 +22,31 @@ namespace webrtc {
struct DownsampledRenderBuffer { struct DownsampledRenderBuffer {
explicit DownsampledRenderBuffer(size_t downsampled_buffer_size); explicit DownsampledRenderBuffer(size_t downsampled_buffer_size);
~DownsampledRenderBuffer(); ~DownsampledRenderBuffer();
size_t IncIndex(size_t index) {
return index < (buffer.size() - 1) ? index + 1 : 0;
}
size_t DecIndex(size_t index) {
return index > 0 ? index - 1 : buffer.size() - 1;
}
size_t OffsetIndex(size_t index, int offset) {
RTC_DCHECK_GE(buffer.size(), offset);
return (buffer.size() + index + offset) % buffer.size();
}
void UpdateWriteIndex(int offset) { write = OffsetIndex(write, offset); }
void IncWriteIndex() { write = IncIndex(write); }
void DecWriteIndex() { write = DecIndex(write); }
void UpdateReadIndex(int offset) { read = OffsetIndex(read, offset); }
void IncReadIndex() { read = IncIndex(read); }
void DecReadIndex() { read = DecIndex(read); }
size_t size;
std::vector<float> buffer; std::vector<float> buffer;
int position = 0; int write = 0;
int read = 0;
}; };
} // namespace webrtc } // namespace webrtc

View File

@ -203,11 +203,13 @@ int EchoCanceller3::instance_count_ = 0;
EchoCanceller3::EchoCanceller3(const EchoCanceller3Config& config, EchoCanceller3::EchoCanceller3(const EchoCanceller3Config& config,
int sample_rate_hz, int sample_rate_hz,
bool use_highpass_filter) bool use_highpass_filter)
: EchoCanceller3(sample_rate_hz, : EchoCanceller3(config,
sample_rate_hz,
use_highpass_filter, use_highpass_filter,
std::unique_ptr<BlockProcessor>( std::unique_ptr<BlockProcessor>(
BlockProcessor::Create(config, sample_rate_hz))) {} BlockProcessor::Create(config, sample_rate_hz))) {}
EchoCanceller3::EchoCanceller3(int sample_rate_hz, EchoCanceller3::EchoCanceller3(const EchoCanceller3Config& config,
int sample_rate_hz,
bool use_highpass_filter, bool use_highpass_filter,
std::unique_ptr<BlockProcessor> block_processor) std::unique_ptr<BlockProcessor> block_processor)
: data_dumper_( : data_dumper_(
@ -219,7 +221,7 @@ EchoCanceller3::EchoCanceller3(int sample_rate_hz,
capture_blocker_(num_bands_), capture_blocker_(num_bands_),
render_blocker_(num_bands_), render_blocker_(num_bands_),
render_transfer_queue_( render_transfer_queue_(
kRenderTransferQueueSize, kRenderTransferQueueSizeFrames,
std::vector<std::vector<float>>( std::vector<std::vector<float>>(
num_bands_, num_bands_,
std::vector<float>(frame_length_, 0.f)), std::vector<float>(frame_length_, 0.f)),

View File

@ -67,7 +67,8 @@ class EchoCanceller3 : public EchoControl {
int sample_rate_hz, int sample_rate_hz,
bool use_highpass_filter); bool use_highpass_filter);
// Testing c-tor that is used only for testing purposes. // Testing c-tor that is used only for testing purposes.
EchoCanceller3(int sample_rate_hz, EchoCanceller3(const EchoCanceller3Config& config,
int sample_rate_hz,
bool use_highpass_filter, bool use_highpass_filter,
std::unique_ptr<BlockProcessor> block_processor); std::unique_ptr<BlockProcessor> block_processor);
~EchoCanceller3() override; ~EchoCanceller3() override;

View File

@ -160,7 +160,7 @@ class EchoCanceller3Tester {
// output. // output.
void RunCaptureTransportVerificationTest() { void RunCaptureTransportVerificationTest() {
EchoCanceller3 aec3( EchoCanceller3 aec3(
sample_rate_hz_, false, EchoCanceller3Config(), sample_rate_hz_, false,
std::unique_ptr<BlockProcessor>( std::unique_ptr<BlockProcessor>(
new CaptureTransportVerificationProcessor(num_bands_))); new CaptureTransportVerificationProcessor(num_bands_)));
@ -185,7 +185,7 @@ class EchoCanceller3Tester {
// block processor. // block processor.
void RunRenderTransportVerificationTest() { void RunRenderTransportVerificationTest() {
EchoCanceller3 aec3( EchoCanceller3 aec3(
sample_rate_hz_, false, EchoCanceller3Config(), sample_rate_hz_, false,
std::unique_ptr<BlockProcessor>( std::unique_ptr<BlockProcessor>(
new RenderTransportVerificationProcessor(num_bands_))); new RenderTransportVerificationProcessor(num_bands_)));
@ -249,7 +249,7 @@ class EchoCanceller3Tester {
break; break;
} }
EchoCanceller3 aec3(sample_rate_hz_, false, EchoCanceller3 aec3(EchoCanceller3Config(), sample_rate_hz_, false,
std::move(block_processor_mock)); std::move(block_processor_mock));
for (size_t frame_index = 0; frame_index < kNumFramesToProcess; for (size_t frame_index = 0; frame_index < kNumFramesToProcess;
@ -331,7 +331,7 @@ class EchoCanceller3Tester {
} break; } break;
} }
EchoCanceller3 aec3(sample_rate_hz_, false, EchoCanceller3 aec3(EchoCanceller3Config(), sample_rate_hz_, false,
std::move(block_processor_mock)); std::move(block_processor_mock));
for (size_t frame_index = 0; frame_index < kNumFramesToProcess; for (size_t frame_index = 0; frame_index < kNumFramesToProcess;
@ -420,7 +420,7 @@ class EchoCanceller3Tester {
} break; } break;
} }
EchoCanceller3 aec3(sample_rate_hz_, false, EchoCanceller3 aec3(EchoCanceller3Config(), sample_rate_hz_, false,
std::move(block_processor_mock)); std::move(block_processor_mock));
for (size_t frame_index = 0; frame_index < kNumFramesToProcess; for (size_t frame_index = 0; frame_index < kNumFramesToProcess;
++frame_index) { ++frame_index) {
@ -458,12 +458,13 @@ class EchoCanceller3Tester {
// This test verifies that the swapqueue is able to handle jitter in the // This test verifies that the swapqueue is able to handle jitter in the
// capture and render API calls. // capture and render API calls.
void RunRenderSwapQueueVerificationTest() { void RunRenderSwapQueueVerificationTest() {
const EchoCanceller3Config config;
EchoCanceller3 aec3( EchoCanceller3 aec3(
sample_rate_hz_, false, config, sample_rate_hz_, false,
std::unique_ptr<BlockProcessor>( std::unique_ptr<BlockProcessor>(
new RenderTransportVerificationProcessor(num_bands_))); new RenderTransportVerificationProcessor(num_bands_)));
for (size_t frame_index = 0; frame_index < kRenderTransferQueueSize; for (size_t frame_index = 0; frame_index < kRenderTransferQueueSizeFrames;
++frame_index) { ++frame_index) {
if (sample_rate_hz_ > 16000) { if (sample_rate_hz_ > 16000) {
render_buffer_.SplitIntoFrequencyBands(); render_buffer_.SplitIntoFrequencyBands();
@ -478,7 +479,7 @@ class EchoCanceller3Tester {
aec3.AnalyzeRender(&render_buffer_); aec3.AnalyzeRender(&render_buffer_);
} }
for (size_t frame_index = 0; frame_index < kRenderTransferQueueSize; for (size_t frame_index = 0; frame_index < kRenderTransferQueueSizeFrames;
++frame_index) { ++frame_index) {
aec3.AnalyzeCapture(&capture_buffer_); aec3.AnalyzeCapture(&capture_buffer_);
if (sample_rate_hz_ > 16000) { if (sample_rate_hz_ > 16000) {

View File

@ -16,6 +16,7 @@
#include "modules/audio_processing/aec3/aec3_common.h" #include "modules/audio_processing/aec3/aec3_common.h"
#include "modules/audio_processing/aec3/render_delay_buffer.h" #include "modules/audio_processing/aec3/render_delay_buffer.h"
#include "modules/audio_processing/include/audio_processing.h"
#include "modules/audio_processing/logging/apm_data_dumper.h" #include "modules/audio_processing/logging/apm_data_dumper.h"
#include "modules/audio_processing/test/echo_canceller_test_tools.h" #include "modules/audio_processing/test/echo_canceller_test_tools.h"
#include "rtc_base/random.h" #include "rtc_base/random.h"
@ -24,9 +25,10 @@
namespace webrtc { namespace webrtc {
namespace { namespace {
std::string ProduceDebugText(size_t delay) { std::string ProduceDebugText(size_t delay, size_t down_sampling_factor) {
std::ostringstream ss; std::ostringstream ss;
ss << "Delay: " << delay; ss << "Delay: " << delay;
ss << ", Down sampling factor: " << down_sampling_factor;
return ss.str(); return ss.str();
} }
@ -37,12 +39,7 @@ TEST(EchoPathDelayEstimator, BasicApiCalls) {
ApmDataDumper data_dumper(0); ApmDataDumper data_dumper(0);
EchoCanceller3Config config; EchoCanceller3Config config;
std::unique_ptr<RenderDelayBuffer> render_delay_buffer( std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
RenderDelayBuffer::Create( RenderDelayBuffer::Create(config, 3));
3, config.delay.down_sampling_factor,
GetDownSampledBufferSize(config.delay.down_sampling_factor,
config.delay.num_filters),
GetRenderDelayBufferSize(config.delay.down_sampling_factor,
config.delay.num_filters)));
EchoPathDelayEstimator estimator(&data_dumper, config); EchoPathDelayEstimator estimator(&data_dumper, config);
std::vector<std::vector<float>> render(3, std::vector<float>(kBlockSize)); std::vector<std::vector<float>> render(3, std::vector<float>(kBlockSize));
std::vector<float> capture(kBlockSize); std::vector<float> capture(kBlockSize);
@ -66,14 +63,22 @@ TEST(EchoPathDelayEstimator, DelayEstimation) {
config.delay.down_sampling_factor = down_sampling_factor; config.delay.down_sampling_factor = down_sampling_factor;
config.delay.num_filters = 10; config.delay.num_filters = 10;
for (size_t delay_samples : {30, 64, 150, 200, 800, 4000}) { for (size_t delay_samples : {30, 64, 150, 200, 800, 4000}) {
SCOPED_TRACE(ProduceDebugText(delay_samples)); SCOPED_TRACE(ProduceDebugText(delay_samples, down_sampling_factor));
config.delay.min_echo_path_delay_blocks = 0;
while ((config.delay.min_echo_path_delay_blocks + 1) * kBlockSize <
delay_samples &&
config.delay.min_echo_path_delay_blocks + 1 <= 5) {
++config.delay.min_echo_path_delay_blocks;
}
const int delay_estimate_offset =
std::max<int>(std::min(config.delay.api_call_jitter_blocks,
config.delay.min_echo_path_delay_blocks) -
1,
0);
std::unique_ptr<RenderDelayBuffer> render_delay_buffer( std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
RenderDelayBuffer::Create( RenderDelayBuffer::Create(config, 3));
3, config.delay.down_sampling_factor,
GetDownSampledBufferSize(config.delay.down_sampling_factor,
config.delay.num_filters),
GetRenderDelayBufferSize(config.delay.down_sampling_factor,
config.delay.num_filters)));
DelayBuffer<float> signal_delay_buffer(delay_samples); DelayBuffer<float> signal_delay_buffer(delay_samples);
EchoPathDelayEstimator estimator(&data_dumper, config); EchoPathDelayEstimator estimator(&data_dumper, config);
@ -82,14 +87,22 @@ TEST(EchoPathDelayEstimator, DelayEstimation) {
RandomizeSampleVector(&random_generator, render[0]); RandomizeSampleVector(&random_generator, render[0]);
signal_delay_buffer.Delay(render[0], capture); signal_delay_buffer.Delay(render[0], capture);
render_delay_buffer->Insert(render); render_delay_buffer->Insert(render);
render_delay_buffer->UpdateBuffers();
if (k == 0) {
render_delay_buffer->Reset();
}
render_delay_buffer->PrepareCaptureCall();
estimated_delay_samples = estimator.EstimateDelay( estimated_delay_samples = estimator.EstimateDelay(
render_delay_buffer->GetDownsampledRenderBuffer(), capture); render_delay_buffer->GetDownsampledRenderBuffer(), capture);
} }
if (estimated_delay_samples) { if (estimated_delay_samples) {
// Due to the internal down-sampling done inside the delay estimator // Due to the internal down-sampling done inside the delay estimator
// the estimated delay cannot be expected to be exact to the true delay. // the estimated delay cannot be expected to be exact to the true delay.
EXPECT_NEAR(delay_samples, *estimated_delay_samples, EXPECT_NEAR(
delay_samples,
*estimated_delay_samples + delay_estimate_offset * kBlockSize,
config.delay.down_sampling_factor); config.delay.down_sampling_factor);
} else { } else {
ADD_FAILURE(); ADD_FAILURE();
@ -107,19 +120,14 @@ TEST(EchoPathDelayEstimator, NoInitialDelayestimates) {
std::vector<float> capture(kBlockSize); std::vector<float> capture(kBlockSize);
ApmDataDumper data_dumper(0); ApmDataDumper data_dumper(0);
std::unique_ptr<RenderDelayBuffer> render_delay_buffer( std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
RenderDelayBuffer::Create( RenderDelayBuffer::Create(config, 3));
3, config.delay.down_sampling_factor,
GetDownSampledBufferSize(config.delay.down_sampling_factor,
config.delay.num_filters),
GetRenderDelayBufferSize(config.delay.down_sampling_factor,
config.delay.num_filters)));
EchoPathDelayEstimator estimator(&data_dumper, config); EchoPathDelayEstimator estimator(&data_dumper, config);
for (size_t k = 0; k < 19; ++k) { for (size_t k = 0; k < 19; ++k) {
RandomizeSampleVector(&random_generator, render[0]); RandomizeSampleVector(&random_generator, render[0]);
std::copy(render[0].begin(), render[0].end(), capture.begin()); std::copy(render[0].begin(), render[0].end(), capture.begin());
render_delay_buffer->Insert(render); render_delay_buffer->Insert(render);
render_delay_buffer->UpdateBuffers(); render_delay_buffer->PrepareCaptureCall();
EXPECT_FALSE(estimator.EstimateDelay( EXPECT_FALSE(estimator.EstimateDelay(
render_delay_buffer->GetDownsampledRenderBuffer(), capture)); render_delay_buffer->GetDownsampledRenderBuffer(), capture));
} }
@ -135,12 +143,7 @@ TEST(EchoPathDelayEstimator, NoDelayEstimatesForLowLevelRenderSignals) {
ApmDataDumper data_dumper(0); ApmDataDumper data_dumper(0);
EchoPathDelayEstimator estimator(&data_dumper, config); EchoPathDelayEstimator estimator(&data_dumper, config);
std::unique_ptr<RenderDelayBuffer> render_delay_buffer( std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
RenderDelayBuffer::Create( RenderDelayBuffer::Create(EchoCanceller3Config(), 3));
3, config.delay.down_sampling_factor,
GetDownSampledBufferSize(config.delay.down_sampling_factor,
config.delay.num_filters),
GetRenderDelayBufferSize(config.delay.down_sampling_factor,
config.delay.num_filters)));
for (size_t k = 0; k < 100; ++k) { for (size_t k = 0; k < 100; ++k) {
RandomizeSampleVector(&random_generator, render[0]); RandomizeSampleVector(&random_generator, render[0]);
for (auto& render_k : render[0]) { for (auto& render_k : render[0]) {
@ -148,7 +151,7 @@ TEST(EchoPathDelayEstimator, NoDelayEstimatesForLowLevelRenderSignals) {
} }
std::copy(render[0].begin(), render[0].end(), capture.begin()); std::copy(render[0].begin(), render[0].end(), capture.begin());
render_delay_buffer->Insert(render); render_delay_buffer->Insert(render);
render_delay_buffer->UpdateBuffers(); render_delay_buffer->PrepareCaptureCall();
EXPECT_FALSE(estimator.EstimateDelay( EXPECT_FALSE(estimator.EstimateDelay(
render_delay_buffer->GetDownsampledRenderBuffer(), capture)); render_delay_buffer->GetDownsampledRenderBuffer(), capture));
} }
@ -164,17 +167,12 @@ TEST(EchoPathDelayEstimator, NoDelayEstimatesForUncorrelatedSignals) {
ApmDataDumper data_dumper(0); ApmDataDumper data_dumper(0);
EchoPathDelayEstimator estimator(&data_dumper, config); EchoPathDelayEstimator estimator(&data_dumper, config);
std::unique_ptr<RenderDelayBuffer> render_delay_buffer( std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
RenderDelayBuffer::Create( RenderDelayBuffer::Create(config, 3));
3, config.delay.down_sampling_factor,
GetDownSampledBufferSize(config.delay.down_sampling_factor,
config.delay.num_filters),
GetRenderDelayBufferSize(config.delay.down_sampling_factor,
config.delay.num_filters)));
for (size_t k = 0; k < 100; ++k) { for (size_t k = 0; k < 100; ++k) {
RandomizeSampleVector(&random_generator, render[0]); RandomizeSampleVector(&random_generator, render[0]);
RandomizeSampleVector(&random_generator, capture); RandomizeSampleVector(&random_generator, capture);
render_delay_buffer->Insert(render); render_delay_buffer->Insert(render);
render_delay_buffer->UpdateBuffers(); render_delay_buffer->PrepareCaptureCall();
EXPECT_FALSE(estimator.EstimateDelay( EXPECT_FALSE(estimator.EstimateDelay(
render_delay_buffer->GetDownsampledRenderBuffer(), capture)); render_delay_buffer->GetDownsampledRenderBuffer(), capture));
} }
@ -190,12 +188,7 @@ TEST(EchoPathDelayEstimator, DISABLED_WrongRenderBlockSize) {
EchoCanceller3Config config; EchoCanceller3Config config;
EchoPathDelayEstimator estimator(&data_dumper, config); EchoPathDelayEstimator estimator(&data_dumper, config);
std::unique_ptr<RenderDelayBuffer> render_delay_buffer( std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
RenderDelayBuffer::Create( RenderDelayBuffer::Create(config, 3));
3, config.delay.down_sampling_factor,
GetDownSampledBufferSize(config.delay.down_sampling_factor,
config.delay.num_filters),
GetRenderDelayBufferSize(config.delay.down_sampling_factor,
config.delay.num_filters)));
std::vector<float> capture(kBlockSize); std::vector<float> capture(kBlockSize);
EXPECT_DEATH(estimator.EstimateDelay( EXPECT_DEATH(estimator.EstimateDelay(
render_delay_buffer->GetDownsampledRenderBuffer(), capture), render_delay_buffer->GetDownsampledRenderBuffer(), capture),
@ -210,12 +203,7 @@ TEST(EchoPathDelayEstimator, WrongCaptureBlockSize) {
EchoCanceller3Config config; EchoCanceller3Config config;
EchoPathDelayEstimator estimator(&data_dumper, config); EchoPathDelayEstimator estimator(&data_dumper, config);
std::unique_ptr<RenderDelayBuffer> render_delay_buffer( std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
RenderDelayBuffer::Create( RenderDelayBuffer::Create(config, 3));
3, config.delay.down_sampling_factor,
GetDownSampledBufferSize(config.delay.down_sampling_factor,
config.delay.num_filters),
GetRenderDelayBufferSize(config.delay.down_sampling_factor,
config.delay.num_filters)));
std::vector<float> capture(std::vector<float>(kBlockSize - 1)); std::vector<float> capture(std::vector<float>(kBlockSize - 1));
EXPECT_DEATH(estimator.EstimateDelay( EXPECT_DEATH(estimator.EstimateDelay(
render_delay_buffer->GetDownsampledRenderBuffer(), capture), render_delay_buffer->GetDownsampledRenderBuffer(), capture),

View File

@ -12,7 +12,11 @@
namespace webrtc { namespace webrtc {
EchoPathVariability::EchoPathVariability(bool gain_change, bool delay_change) EchoPathVariability::EchoPathVariability(bool gain_change,
: gain_change(gain_change), delay_change(delay_change) {} DelayAdjustment delay_change,
bool clock_drift)
: gain_change(gain_change),
delay_change(delay_change),
clock_drift(clock_drift) {}
} // namespace webrtc } // namespace webrtc

View File

@ -14,11 +14,24 @@
namespace webrtc { namespace webrtc {
struct EchoPathVariability { struct EchoPathVariability {
EchoPathVariability(bool gain_change, bool delay_change); enum class DelayAdjustment {
kNone,
kBufferReadjustment,
kBufferFlush,
kDelayReset,
kNewDetectedDelay
};
bool AudioPathChanged() const { return gain_change || delay_change; } EchoPathVariability(bool gain_change,
DelayAdjustment delay_change,
bool clock_drift);
bool AudioPathChanged() const {
return gain_change || delay_change != DelayAdjustment::kNone;
}
bool gain_change; bool gain_change;
bool delay_change; DelayAdjustment delay_change;
bool clock_drift;
}; };
} // namespace webrtc } // namespace webrtc

View File

@ -15,25 +15,35 @@ namespace webrtc {
TEST(EchoPathVariability, CorrectBehavior) { TEST(EchoPathVariability, CorrectBehavior) {
// Test correct passing and reporting of the gain change information. // Test correct passing and reporting of the gain change information.
EchoPathVariability v(true, true); EchoPathVariability v(
true, EchoPathVariability::DelayAdjustment::kNewDetectedDelay, false);
EXPECT_TRUE(v.gain_change); EXPECT_TRUE(v.gain_change);
EXPECT_TRUE(v.delay_change); EXPECT_TRUE(v.delay_change ==
EchoPathVariability::DelayAdjustment::kNewDetectedDelay);
EXPECT_TRUE(v.AudioPathChanged()); EXPECT_TRUE(v.AudioPathChanged());
EXPECT_FALSE(v.clock_drift);
v = EchoPathVariability(true, false); v = EchoPathVariability(true, EchoPathVariability::DelayAdjustment::kNone,
false);
EXPECT_TRUE(v.gain_change); EXPECT_TRUE(v.gain_change);
EXPECT_FALSE(v.delay_change); EXPECT_TRUE(v.delay_change == EchoPathVariability::DelayAdjustment::kNone);
EXPECT_TRUE(v.AudioPathChanged()); EXPECT_TRUE(v.AudioPathChanged());
EXPECT_FALSE(v.clock_drift);
v = EchoPathVariability(false, true); v = EchoPathVariability(
false, EchoPathVariability::DelayAdjustment::kNewDetectedDelay, false);
EXPECT_FALSE(v.gain_change); EXPECT_FALSE(v.gain_change);
EXPECT_TRUE(v.delay_change); EXPECT_TRUE(v.delay_change ==
EchoPathVariability::DelayAdjustment::kNewDetectedDelay);
EXPECT_TRUE(v.AudioPathChanged()); EXPECT_TRUE(v.AudioPathChanged());
EXPECT_FALSE(v.clock_drift);
v = EchoPathVariability(false, false); v = EchoPathVariability(false, EchoPathVariability::DelayAdjustment::kNone,
false);
EXPECT_FALSE(v.gain_change); EXPECT_FALSE(v.gain_change);
EXPECT_FALSE(v.delay_change); EXPECT_TRUE(v.delay_change == EchoPathVariability::DelayAdjustment::kNone);
EXPECT_FALSE(v.AudioPathChanged()); EXPECT_FALSE(v.AudioPathChanged());
EXPECT_FALSE(v.clock_drift);
} }
} // namespace webrtc } // namespace webrtc

View File

@ -174,7 +174,7 @@ void EchoRemoverImpl::ProcessCapture(
// Compute spectra. // Compute spectra.
fft_.ZeroPaddedFft(y0, &Y); fft_.ZeroPaddedFft(y0, &Y);
LinearEchoPower(E_main, Y, &S2_linear); LinearEchoPower(E_main, Y, &S2_linear);
Y.Spectrum(optimization_, &Y2); Y.Spectrum(optimization_, Y2);
// Update the AEC state information. // Update the AEC state information.
aec_state_.Update(subtractor_.FilterFrequencyResponse(), aec_state_.Update(subtractor_.FilterFrequencyResponse(),

View File

@ -65,7 +65,7 @@ TEST(TransformDbMetricForReporting, DbFsScaling) {
Aec3Fft fft; Aec3Fft fft;
x.fill(1000.f); x.fill(1000.f);
fft.ZeroPaddedFft(x, &X); fft.ZeroPaddedFft(x, &X);
X.Spectrum(Aec3Optimization::kNone, &X2); X.Spectrum(Aec3Optimization::kNone, X2);
float offset = -10.f * log10(32768.f * 32768.f); float offset = -10.f * log10(32768.f * 32768.f);
EXPECT_NEAR(offset, -90.3f, 0.1f); EXPECT_NEAR(offset, -90.3f, 0.1f);

View File

@ -39,9 +39,6 @@ std::string ProduceDebugText(int sample_rate_hz, int delay) {
return ss.str(); return ss.str();
} }
constexpr size_t kDownSamplingFactor = 4;
constexpr size_t kNumMatchedFilters = 4;
} // namespace } // namespace
// Verifies the basic API call sequence // Verifies the basic API call sequence
@ -51,22 +48,23 @@ TEST(EchoRemover, BasicApiCalls) {
std::unique_ptr<EchoRemover> remover( std::unique_ptr<EchoRemover> remover(
EchoRemover::Create(EchoCanceller3Config(), rate)); EchoRemover::Create(EchoCanceller3Config(), rate));
std::unique_ptr<RenderDelayBuffer> render_buffer(RenderDelayBuffer::Create( std::unique_ptr<RenderDelayBuffer> render_buffer(RenderDelayBuffer::Create(
NumBandsForRate(rate), kDownSamplingFactor, EchoCanceller3Config(), NumBandsForRate(rate)));
GetDownSampledBufferSize(kDownSamplingFactor, kNumMatchedFilters),
GetRenderDelayBufferSize(kDownSamplingFactor, kNumMatchedFilters)));
std::vector<std::vector<float>> render(NumBandsForRate(rate), std::vector<std::vector<float>> render(NumBandsForRate(rate),
std::vector<float>(kBlockSize, 0.f)); std::vector<float>(kBlockSize, 0.f));
std::vector<std::vector<float>> capture( std::vector<std::vector<float>> capture(
NumBandsForRate(rate), std::vector<float>(kBlockSize, 0.f)); NumBandsForRate(rate), std::vector<float>(kBlockSize, 0.f));
for (size_t k = 0; k < 100; ++k) { for (size_t k = 0; k < 100; ++k) {
EchoPathVariability echo_path_variability(k % 3 == 0 ? true : false, EchoPathVariability echo_path_variability(
k % 5 == 0 ? true : false); k % 3 == 0 ? true : false,
k % 5 == 0 ? EchoPathVariability::DelayAdjustment::kNewDetectedDelay
: EchoPathVariability::DelayAdjustment::kNone,
false);
rtc::Optional<size_t> echo_path_delay_samples = rtc::Optional<size_t> echo_path_delay_samples =
(k % 6 == 0 ? rtc::Optional<size_t>(k * 10) (k % 6 == 0 ? rtc::Optional<size_t>(k * 10)
: rtc::nullopt); : rtc::nullopt);
render_buffer->Insert(render); render_buffer->Insert(render);
render_buffer->UpdateBuffers(); render_buffer->PrepareCaptureCall();
remover->ProcessCapture(echo_path_delay_samples, echo_path_variability, remover->ProcessCapture(echo_path_delay_samples, echo_path_variability,
k % 2 == 0 ? true : false, k % 2 == 0 ? true : false,
render_buffer->GetRenderBuffer(), &capture); render_buffer->GetRenderBuffer(), &capture);
@ -92,12 +90,11 @@ TEST(EchoRemover, WrongCaptureBlockSize) {
std::unique_ptr<EchoRemover> remover( std::unique_ptr<EchoRemover> remover(
EchoRemover::Create(EchoCanceller3Config(), rate)); EchoRemover::Create(EchoCanceller3Config(), rate));
std::unique_ptr<RenderDelayBuffer> render_buffer(RenderDelayBuffer::Create( std::unique_ptr<RenderDelayBuffer> render_buffer(RenderDelayBuffer::Create(
NumBandsForRate(rate), kDownSamplingFactor, EchoCanceller3Config(), NumBandsForRate(rate)));
GetDownSampledBufferSize(kDownSamplingFactor, kNumMatchedFilters),
GetRenderDelayBufferSize(kDownSamplingFactor, kNumMatchedFilters)));
std::vector<std::vector<float>> capture( std::vector<std::vector<float>> capture(
NumBandsForRate(rate), std::vector<float>(kBlockSize - 1, 0.f)); NumBandsForRate(rate), std::vector<float>(kBlockSize - 1, 0.f));
EchoPathVariability echo_path_variability(false, false); EchoPathVariability echo_path_variability(
false, EchoPathVariability::DelayAdjustment::kNone, false);
rtc::Optional<size_t> echo_path_delay_samples; rtc::Optional<size_t> echo_path_delay_samples;
EXPECT_DEATH(remover->ProcessCapture( EXPECT_DEATH(remover->ProcessCapture(
echo_path_delay_samples, echo_path_variability, false, echo_path_delay_samples, echo_path_variability, false,
@ -115,13 +112,12 @@ TEST(EchoRemover, DISABLED_WrongCaptureNumBands) {
std::unique_ptr<EchoRemover> remover( std::unique_ptr<EchoRemover> remover(
EchoRemover::Create(EchoCanceller3Config(), rate)); EchoRemover::Create(EchoCanceller3Config(), rate));
std::unique_ptr<RenderDelayBuffer> render_buffer(RenderDelayBuffer::Create( std::unique_ptr<RenderDelayBuffer> render_buffer(RenderDelayBuffer::Create(
NumBandsForRate(rate), kDownSamplingFactor, EchoCanceller3Config(), NumBandsForRate(rate)));
GetDownSampledBufferSize(kDownSamplingFactor, kNumMatchedFilters),
GetRenderDelayBufferSize(kDownSamplingFactor, kNumMatchedFilters)));
std::vector<std::vector<float>> capture( std::vector<std::vector<float>> capture(
NumBandsForRate(rate == 48000 ? 16000 : rate + 16000), NumBandsForRate(rate == 48000 ? 16000 : rate + 16000),
std::vector<float>(kBlockSize, 0.f)); std::vector<float>(kBlockSize, 0.f));
EchoPathVariability echo_path_variability(false, false); EchoPathVariability echo_path_variability(
false, EchoPathVariability::DelayAdjustment::kNone, false);
rtc::Optional<size_t> echo_path_delay_samples; rtc::Optional<size_t> echo_path_delay_samples;
EXPECT_DEATH(remover->ProcessCapture( EXPECT_DEATH(remover->ProcessCapture(
echo_path_delay_samples, echo_path_variability, false, echo_path_delay_samples, echo_path_variability, false,
@ -134,11 +130,10 @@ TEST(EchoRemover, DISABLED_WrongCaptureNumBands) {
TEST(EchoRemover, NullCapture) { TEST(EchoRemover, NullCapture) {
std::unique_ptr<EchoRemover> remover( std::unique_ptr<EchoRemover> remover(
EchoRemover::Create(EchoCanceller3Config(), 8000)); EchoRemover::Create(EchoCanceller3Config(), 8000));
std::unique_ptr<RenderDelayBuffer> render_buffer(RenderDelayBuffer::Create( std::unique_ptr<RenderDelayBuffer> render_buffer(
3, kDownSamplingFactor, RenderDelayBuffer::Create(EchoCanceller3Config(), 3));
GetDownSampledBufferSize(kDownSamplingFactor, kNumMatchedFilters), EchoPathVariability echo_path_variability(
GetRenderDelayBufferSize(kDownSamplingFactor, kNumMatchedFilters))); false, EchoPathVariability::DelayAdjustment::kNone, false);
EchoPathVariability echo_path_variability(false, false);
rtc::Optional<size_t> echo_path_delay_samples; rtc::Optional<size_t> echo_path_delay_samples;
EXPECT_DEATH( EXPECT_DEATH(
remover->ProcessCapture(echo_path_delay_samples, echo_path_variability, remover->ProcessCapture(echo_path_delay_samples, echo_path_variability,
@ -158,17 +153,18 @@ TEST(EchoRemover, BasicEchoRemoval) {
std::vector<float>(kBlockSize, 0.f)); std::vector<float>(kBlockSize, 0.f));
std::vector<std::vector<float>> y(NumBandsForRate(rate), std::vector<std::vector<float>> y(NumBandsForRate(rate),
std::vector<float>(kBlockSize, 0.f)); std::vector<float>(kBlockSize, 0.f));
EchoPathVariability echo_path_variability(false, false); EchoPathVariability echo_path_variability(
false, EchoPathVariability::DelayAdjustment::kNone, false);
for (size_t delay_samples : {0, 64, 150, 200, 301}) { for (size_t delay_samples : {0, 64, 150, 200, 301}) {
SCOPED_TRACE(ProduceDebugText(rate, delay_samples)); SCOPED_TRACE(ProduceDebugText(rate, delay_samples));
std::unique_ptr<EchoRemover> remover( EchoCanceller3Config config;
EchoRemover::Create(EchoCanceller3Config(), rate)); config.delay.min_echo_path_delay_blocks = 0;
std::unique_ptr<EchoRemover> remover(EchoRemover::Create(config, rate));
std::unique_ptr<RenderDelayBuffer> render_buffer( std::unique_ptr<RenderDelayBuffer> render_buffer(
RenderDelayBuffer::Create( RenderDelayBuffer::Create(config, NumBandsForRate(rate)));
NumBandsForRate(rate), kDownSamplingFactor, if (delay_samples != render_buffer->Delay() * kBlockSize) {
GetDownSampledBufferSize(kDownSamplingFactor, kNumMatchedFilters), render_buffer->SetDelay(delay_samples / kBlockSize);
GetRenderDelayBufferSize(kDownSamplingFactor, }
kNumMatchedFilters)));
std::vector<std::unique_ptr<DelayBuffer<float>>> delay_buffers(x.size()); std::vector<std::unique_ptr<DelayBuffer<float>>> delay_buffers(x.size());
for (size_t j = 0; j < x.size(); ++j) { for (size_t j = 0; j < x.size(); ++j) {
delay_buffers[j].reset(new DelayBuffer<float>(delay_samples)); delay_buffers[j].reset(new DelayBuffer<float>(delay_samples));
@ -196,7 +192,7 @@ TEST(EchoRemover, BasicEchoRemoval) {
} }
render_buffer->Insert(x); render_buffer->Insert(x);
render_buffer->UpdateBuffers(); render_buffer->PrepareCaptureCall();
remover->ProcessCapture(delay_samples, echo_path_variability, false, remover->ProcessCapture(delay_samples, echo_path_variability, false,
render_buffer->GetRenderBuffer(), &y); render_buffer->GetRenderBuffer(), &y);

View File

@ -31,9 +31,10 @@ ErlEstimator::ErlEstimator() {
ErlEstimator::~ErlEstimator() = default; ErlEstimator::~ErlEstimator() = default;
void ErlEstimator::Update( void ErlEstimator::Update(rtc::ArrayView<const float> render_spectrum,
const std::array<float, kFftLengthBy2Plus1>& render_spectrum, rtc::ArrayView<const float> capture_spectrum) {
const std::array<float, kFftLengthBy2Plus1>& capture_spectrum) { RTC_DCHECK_EQ(kFftLengthBy2Plus1, render_spectrum.size());
RTC_DCHECK_EQ(kFftLengthBy2Plus1, capture_spectrum.size());
const auto& X2 = render_spectrum; const auto& X2 = render_spectrum;
const auto& Y2 = capture_spectrum; const auto& Y2 = capture_spectrum;

View File

@ -13,6 +13,7 @@
#include <array> #include <array>
#include "api/array_view.h"
#include "modules/audio_processing/aec3/aec3_common.h" #include "modules/audio_processing/aec3/aec3_common.h"
#include "rtc_base/constructormagic.h" #include "rtc_base/constructormagic.h"
@ -25,8 +26,8 @@ class ErlEstimator {
~ErlEstimator(); ~ErlEstimator();
// Updates the ERL estimate. // Updates the ERL estimate.
void Update(const std::array<float, kFftLengthBy2Plus1>& render_spectrum, void Update(rtc::ArrayView<const float> render_spectrum,
const std::array<float, kFftLengthBy2Plus1>& capture_spectrum); rtc::ArrayView<const float> capture_spectrum);
// Returns the most recent ERL estimate. // Returns the most recent ERL estimate.
const std::array<float, kFftLengthBy2Plus1>& Erl() const { return erl_; } const std::array<float, kFftLengthBy2Plus1>& Erl() const { return erl_; }

View File

@ -31,10 +31,12 @@ ErleEstimator::ErleEstimator(float min_erle,
ErleEstimator::~ErleEstimator() = default; ErleEstimator::~ErleEstimator() = default;
void ErleEstimator::Update( void ErleEstimator::Update(rtc::ArrayView<const float> render_spectrum,
const std::array<float, kFftLengthBy2Plus1>& render_spectrum, rtc::ArrayView<const float> capture_spectrum,
const std::array<float, kFftLengthBy2Plus1>& capture_spectrum, rtc::ArrayView<const float> subtractor_spectrum) {
const std::array<float, kFftLengthBy2Plus1>& subtractor_spectrum) { RTC_DCHECK_EQ(kFftLengthBy2Plus1, render_spectrum.size());
RTC_DCHECK_EQ(kFftLengthBy2Plus1, capture_spectrum.size());
RTC_DCHECK_EQ(kFftLengthBy2Plus1, subtractor_spectrum.size());
const auto& X2 = render_spectrum; const auto& X2 = render_spectrum;
const auto& Y2 = capture_spectrum; const auto& Y2 = capture_spectrum;
const auto& E2 = subtractor_spectrum; const auto& E2 = subtractor_spectrum;

View File

@ -13,6 +13,7 @@
#include <array> #include <array>
#include "api/array_view.h"
#include "modules/audio_processing/aec3/aec3_common.h" #include "modules/audio_processing/aec3/aec3_common.h"
#include "rtc_base/constructormagic.h" #include "rtc_base/constructormagic.h"
@ -25,9 +26,9 @@ class ErleEstimator {
~ErleEstimator(); ~ErleEstimator();
// Updates the ERLE estimate. // Updates the ERLE estimate.
void Update(const std::array<float, kFftLengthBy2Plus1>& render_spectrum, void Update(rtc::ArrayView<const float> render_spectrum,
const std::array<float, kFftLengthBy2Plus1>& capture_spectrum, rtc::ArrayView<const float> capture_spectrum,
const std::array<float, kFftLengthBy2Plus1>& subtractor_spectrum); rtc::ArrayView<const float> subtractor_spectrum);
// Returns the most recent ERLE estimate. // Returns the most recent ERLE estimate.
const std::array<float, kFftLengthBy2Plus1>& Erle() const { return erle_; } const std::array<float, kFftLengthBy2Plus1>& Erle() const { return erle_; }

View File

@ -0,0 +1,23 @@
/*
* Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include "modules/audio_processing/aec3/fft_buffer.h"
namespace webrtc {
FftBuffer::FftBuffer(size_t size) : buffer(size) {
for (auto& b : buffer) {
b.Clear();
}
}
FftBuffer::~FftBuffer() = default;
} // namespace webrtc

View File

@ -0,0 +1,54 @@
/*
* Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#ifndef MODULES_AUDIO_PROCESSING_AEC3_FFT_BUFFER_H_
#define MODULES_AUDIO_PROCESSING_AEC3_FFT_BUFFER_H_
#include <vector>
#include "modules/audio_processing/aec3/fft_data.h"
#include "rtc_base/checks.h"
namespace webrtc {
// Struct for bundling a circular buffer of FftData objects together with the
// read and write indices.
struct FftBuffer {
explicit FftBuffer(size_t size);
~FftBuffer();
size_t IncIndex(size_t index) {
return index < buffer.size() - 1 ? index + 1 : 0;
}
size_t DecIndex(size_t index) {
return index > 0 ? index - 1 : buffer.size() - 1;
}
size_t OffsetIndex(size_t index, int offset) {
RTC_DCHECK_GE(buffer.size(), offset);
return (buffer.size() + index + offset) % buffer.size();
}
void UpdateWriteIndex(int offset) { write = OffsetIndex(write, offset); }
void IncWriteIndex() { write = IncIndex(write); }
void DecWriteIndex() { write = DecIndex(write); }
void UpdateReadIndex(int offset) { read = OffsetIndex(read, offset); }
void IncReadIndex() { read = IncIndex(read); }
void DecReadIndex() { read = DecIndex(read); }
std::vector<FftData> buffer;
size_t write = 0;
size_t read = 0;
};
} // namespace webrtc
#endif // MODULES_AUDIO_PROCESSING_AEC3_FFT_BUFFER_H_

View File

@ -40,8 +40,8 @@ struct FftData {
// Computes the power spectrum of the data. // Computes the power spectrum of the data.
void Spectrum(Aec3Optimization optimization, void Spectrum(Aec3Optimization optimization,
std::array<float, kFftLengthBy2Plus1>* power_spectrum) const { rtc::ArrayView<float> power_spectrum) const {
RTC_DCHECK(power_spectrum); RTC_DCHECK_EQ(kFftLengthBy2Plus1, power_spectrum.size());
switch (optimization) { switch (optimization) {
#if defined(WEBRTC_ARCH_X86_FAMILY) #if defined(WEBRTC_ARCH_X86_FAMILY)
case Aec3Optimization::kSse2: { case Aec3Optimization::kSse2: {
@ -53,16 +53,14 @@ struct FftData {
const __m128 ii = _mm_mul_ps(i, i); const __m128 ii = _mm_mul_ps(i, i);
const __m128 rr = _mm_mul_ps(r, r); const __m128 rr = _mm_mul_ps(r, r);
const __m128 rrii = _mm_add_ps(rr, ii); const __m128 rrii = _mm_add_ps(rr, ii);
_mm_storeu_ps(&(*power_spectrum)[k], rrii); _mm_storeu_ps(&power_spectrum[k], rrii);
} }
(*power_spectrum)[kFftLengthBy2] = power_spectrum[kFftLengthBy2] = re[kFftLengthBy2] * re[kFftLengthBy2] +
re[kFftLengthBy2] * re[kFftLengthBy2] +
im[kFftLengthBy2] * im[kFftLengthBy2]; im[kFftLengthBy2] * im[kFftLengthBy2];
} break; } break;
#endif #endif
default: default:
std::transform(re.begin(), re.end(), im.begin(), std::transform(re.begin(), re.end(), im.begin(), power_spectrum.begin(),
power_spectrum->begin(),
[](float a, float b) { return a * a + b * b; }); [](float a, float b) { return a * a + b * b; });
} }
} }

View File

@ -34,8 +34,8 @@ TEST(FftData, TestOptimizations) {
std::array<float, kFftLengthBy2Plus1> spectrum; std::array<float, kFftLengthBy2Plus1> spectrum;
std::array<float, kFftLengthBy2Plus1> spectrum_sse2; std::array<float, kFftLengthBy2Plus1> spectrum_sse2;
x.Spectrum(Aec3Optimization::kNone, &spectrum); x.Spectrum(Aec3Optimization::kNone, spectrum);
x.Spectrum(Aec3Optimization::kSse2, &spectrum_sse2); x.Spectrum(Aec3Optimization::kSse2, spectrum_sse2);
EXPECT_EQ(spectrum, spectrum_sse2); EXPECT_EQ(spectrum, spectrum_sse2);
} }
} }
@ -102,7 +102,7 @@ TEST(FftData, Spectrum) {
} }
std::array<float, kFftLengthBy2Plus1> spectrum; std::array<float, kFftLengthBy2Plus1> spectrum;
x.Spectrum(Aec3Optimization::kNone, &spectrum); x.Spectrum(Aec3Optimization::kNone, spectrum);
EXPECT_EQ(x.re[0] * x.re[0], spectrum[0]); EXPECT_EQ(x.re[0] * x.re[0], spectrum[0]);
EXPECT_EQ(x.re[spectrum.size() - 1] * x.re[spectrum.size() - 1], EXPECT_EQ(x.re[spectrum.size() - 1] * x.re[spectrum.size() - 1],

View File

@ -37,7 +37,9 @@ MainFilterUpdateGain::MainFilterUpdateGain()
MainFilterUpdateGain::~MainFilterUpdateGain() {} MainFilterUpdateGain::~MainFilterUpdateGain() {}
void MainFilterUpdateGain::HandleEchoPathChange() { void MainFilterUpdateGain::HandleEchoPathChange(
const EchoPathVariability& echo_path_variability) {
// TODO(peah): Add even-specific behavior.
H_error_.fill(kHErrorInitial); H_error_.fill(kHErrorInitial);
poor_excitation_counter_ = kPoorExcitationCounterInitial; poor_excitation_counter_ = kPoorExcitationCounterInitial;
call_counter_ = 0; call_counter_ = 0;
@ -57,7 +59,7 @@ void MainFilterUpdateGain::Compute(
const auto& E2_shadow = subtractor_output.E2_shadow; const auto& E2_shadow = subtractor_output.E2_shadow;
FftData* G = gain_fft; FftData* G = gain_fft;
const size_t size_partitions = filter.SizePartitions(); const size_t size_partitions = filter.SizePartitions();
const auto& X2 = render_buffer.SpectralSum(size_partitions); auto X2 = render_buffer.SpectralSum(size_partitions);
const auto& erl = filter.Erl(); const auto& erl = filter.Erl();
++call_counter_; ++call_counter_;

View File

@ -16,7 +16,7 @@
#include "modules/audio_processing/aec3/adaptive_fir_filter.h" #include "modules/audio_processing/aec3/adaptive_fir_filter.h"
#include "modules/audio_processing/aec3/aec3_common.h" #include "modules/audio_processing/aec3/aec3_common.h"
#include "modules/audio_processing/aec3/render_buffer.h" #include "modules/audio_processing/aec3/echo_path_variability.h"
#include "modules/audio_processing/aec3/render_signal_analyzer.h" #include "modules/audio_processing/aec3/render_signal_analyzer.h"
#include "modules/audio_processing/aec3/subtractor_output.h" #include "modules/audio_processing/aec3/subtractor_output.h"
#include "rtc_base/constructormagic.h" #include "rtc_base/constructormagic.h"
@ -32,7 +32,7 @@ class MainFilterUpdateGain {
~MainFilterUpdateGain(); ~MainFilterUpdateGain();
// Takes action in the case of a known echo path change. // Takes action in the case of a known echo path change.
void HandleEchoPathChange(); void HandleEchoPathChange(const EchoPathVariability& echo_path_variability);
// Computes the gain. // Computes the gain.
void Compute(const RenderBuffer& render_buffer, void Compute(const RenderBuffer& render_buffer,

View File

@ -16,7 +16,7 @@
#include "modules/audio_processing/aec3/adaptive_fir_filter.h" #include "modules/audio_processing/aec3/adaptive_fir_filter.h"
#include "modules/audio_processing/aec3/aec_state.h" #include "modules/audio_processing/aec3/aec_state.h"
#include "modules/audio_processing/aec3/render_buffer.h" #include "modules/audio_processing/aec3/render_delay_buffer.h"
#include "modules/audio_processing/aec3/render_signal_analyzer.h" #include "modules/audio_processing/aec3/render_signal_analyzer.h"
#include "modules/audio_processing/aec3/shadow_filter_update_gain.h" #include "modules/audio_processing/aec3/shadow_filter_update_gain.h"
#include "modules/audio_processing/aec3/subtractor_output.h" #include "modules/audio_processing/aec3/subtractor_output.h"
@ -40,12 +40,9 @@ void RunFilterUpdateTest(int num_blocks_to_process,
std::array<float, kBlockSize>* y_last_block, std::array<float, kBlockSize>* y_last_block,
FftData* G_last_block) { FftData* G_last_block) {
ApmDataDumper data_dumper(42); ApmDataDumper data_dumper(42);
AdaptiveFirFilter main_filter(9, DetectOptimization(), &data_dumper); AdaptiveFirFilter main_filter(12, DetectOptimization(), &data_dumper);
AdaptiveFirFilter shadow_filter(9, DetectOptimization(), &data_dumper); AdaptiveFirFilter shadow_filter(12, DetectOptimization(), &data_dumper);
Aec3Fft fft; Aec3Fft fft;
RenderBuffer render_buffer(
Aec3Optimization::kNone, 3, main_filter.SizePartitions(),
std::vector<size_t>(1, main_filter.SizePartitions()));
std::array<float, kBlockSize> x_old; std::array<float, kBlockSize> x_old;
x_old.fill(0.f); x_old.fill(0.f);
ShadowFilterUpdateGain shadow_gain; ShadowFilterUpdateGain shadow_gain;
@ -53,7 +50,11 @@ void RunFilterUpdateTest(int num_blocks_to_process,
Random random_generator(42U); Random random_generator(42U);
std::vector<std::vector<float>> x(3, std::vector<float>(kBlockSize, 0.f)); std::vector<std::vector<float>> x(3, std::vector<float>(kBlockSize, 0.f));
std::vector<float> y(kBlockSize, 0.f); std::vector<float> y(kBlockSize, 0.f);
AecState aec_state(EchoCanceller3Config{}); EchoCanceller3Config config;
config.delay.min_echo_path_delay_blocks = 0;
std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
RenderDelayBuffer::Create(config, 3));
AecState aec_state(config);
RenderSignalAnalyzer render_signal_analyzer; RenderSignalAnalyzer render_signal_analyzer;
std::array<float, kFftLength> s_scratch; std::array<float, kFftLength> s_scratch;
std::array<float, kBlockSize> s; std::array<float, kBlockSize> s;
@ -92,11 +93,18 @@ void RunFilterUpdateTest(int num_blocks_to_process,
RandomizeSampleVector(&random_generator, x[0]); RandomizeSampleVector(&random_generator, x[0]);
} }
delay_buffer.Delay(x[0], y); delay_buffer.Delay(x[0], y);
render_buffer.Insert(x);
render_signal_analyzer.Update(render_buffer, aec_state.FilterDelay()); render_delay_buffer->Insert(x);
if (k == 0) {
render_delay_buffer->Reset();
}
render_delay_buffer->PrepareCaptureCall();
render_signal_analyzer.Update(render_delay_buffer->GetRenderBuffer(),
aec_state.FilterDelay());
// Apply the main filter. // Apply the main filter.
main_filter.Filter(render_buffer, &S); main_filter.Filter(render_delay_buffer->GetRenderBuffer(), &S);
fft.Ifft(S, &s_scratch); fft.Ifft(S, &s_scratch);
std::transform(y.begin(), y.end(), s_scratch.begin() + kFftLengthBy2, std::transform(y.begin(), y.end(), s_scratch.begin() + kFftLengthBy2,
e_main.begin(), e_main.begin(),
@ -109,7 +117,7 @@ void RunFilterUpdateTest(int num_blocks_to_process,
} }
// Apply the shadow filter. // Apply the shadow filter.
shadow_filter.Filter(render_buffer, &S); shadow_filter.Filter(render_delay_buffer->GetRenderBuffer(), &S);
fft.Ifft(S, &s_scratch); fft.Ifft(S, &s_scratch);
std::transform(y.begin(), y.end(), s_scratch.begin() + kFftLengthBy2, std::transform(y.begin(), y.end(), s_scratch.begin() + kFftLengthBy2,
e_shadow.begin(), e_shadow.begin(),
@ -119,24 +127,28 @@ void RunFilterUpdateTest(int num_blocks_to_process,
fft.ZeroPaddedFft(e_shadow, &E_shadow); fft.ZeroPaddedFft(e_shadow, &E_shadow);
// Compute spectra for future use. // Compute spectra for future use.
E_main.Spectrum(Aec3Optimization::kNone, &output.E2_main); E_main.Spectrum(Aec3Optimization::kNone, output.E2_main);
E_shadow.Spectrum(Aec3Optimization::kNone, &output.E2_shadow); E_shadow.Spectrum(Aec3Optimization::kNone, output.E2_shadow);
// Adapt the shadow filter. // Adapt the shadow filter.
shadow_gain.Compute(render_buffer, render_signal_analyzer, E_shadow, shadow_gain.Compute(render_delay_buffer->GetRenderBuffer(),
render_signal_analyzer, E_shadow,
shadow_filter.SizePartitions(), saturation, &G); shadow_filter.SizePartitions(), saturation, &G);
shadow_filter.Adapt(render_buffer, G); shadow_filter.Adapt(render_delay_buffer->GetRenderBuffer(), G);
// Adapt the main filter // Adapt the main filter
main_gain.Compute(render_buffer, render_signal_analyzer, output, main_gain.Compute(render_delay_buffer->GetRenderBuffer(),
main_filter, saturation, &G); render_signal_analyzer, output, main_filter, saturation,
main_filter.Adapt(render_buffer, G); &G);
main_filter.Adapt(render_delay_buffer->GetRenderBuffer(), G);
// Update the delay. // Update the delay.
aec_state.HandleEchoPathChange(EchoPathVariability(false, false)); aec_state.HandleEchoPathChange(EchoPathVariability(
false, EchoPathVariability::DelayAdjustment::kNone, false));
aec_state.Update(main_filter.FilterFrequencyResponse(), aec_state.Update(main_filter.FilterFrequencyResponse(),
main_filter.FilterImpulseResponse(), true, rtc::nullopt, main_filter.FilterImpulseResponse(), true, rtc::nullopt,
render_buffer, E2_main, Y2, x[0], s, false); render_delay_buffer->GetRenderBuffer(), E2_main, Y2, x[0],
s, false);
} }
std::copy(e_main.begin(), e_main.end(), e_last_block->begin()); std::copy(e_main.begin(), e_main.end(), e_last_block->begin());
@ -158,15 +170,15 @@ std::string ProduceDebugText(size_t delay) {
// Verifies that the check for non-null output gain parameter works. // Verifies that the check for non-null output gain parameter works.
TEST(MainFilterUpdateGain, NullDataOutputGain) { TEST(MainFilterUpdateGain, NullDataOutputGain) {
ApmDataDumper data_dumper(42); ApmDataDumper data_dumper(42);
AdaptiveFirFilter filter(9, DetectOptimization(), &data_dumper); AdaptiveFirFilter filter(kAdaptiveFilterLength, DetectOptimization(),
RenderBuffer render_buffer(Aec3Optimization::kNone, 3, &data_dumper);
filter.SizePartitions(), std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
std::vector<size_t>(1, filter.SizePartitions())); RenderDelayBuffer::Create(EchoCanceller3Config(), 3));
RenderSignalAnalyzer analyzer; RenderSignalAnalyzer analyzer;
SubtractorOutput output; SubtractorOutput output;
MainFilterUpdateGain gain; MainFilterUpdateGain gain;
EXPECT_DEATH( EXPECT_DEATH(gain.Compute(render_delay_buffer->GetRenderBuffer(), analyzer,
gain.Compute(render_buffer, analyzer, output, filter, false, nullptr), output, filter, false, nullptr),
""); "");
} }
@ -214,9 +226,9 @@ TEST(MainFilterUpdateGain, DecreasingGain) {
RunFilterUpdateTest(300, 65, blocks_with_echo_path_changes, RunFilterUpdateTest(300, 65, blocks_with_echo_path_changes,
blocks_with_saturation, false, &e, &y, &G_c); blocks_with_saturation, false, &e, &y, &G_c);
G_a.Spectrum(Aec3Optimization::kNone, &G_a_power); G_a.Spectrum(Aec3Optimization::kNone, G_a_power);
G_b.Spectrum(Aec3Optimization::kNone, &G_b_power); G_b.Spectrum(Aec3Optimization::kNone, G_b_power);
G_c.Spectrum(Aec3Optimization::kNone, &G_c_power); G_c.Spectrum(Aec3Optimization::kNone, G_c_power);
EXPECT_GT(std::accumulate(G_a_power.begin(), G_a_power.end(), 0.), EXPECT_GT(std::accumulate(G_a_power.begin(), G_a_power.end(), 0.),
std::accumulate(G_b_power.begin(), G_b_power.end(), 0.)); std::accumulate(G_b_power.begin(), G_b_power.end(), 0.));
@ -256,8 +268,8 @@ TEST(MainFilterUpdateGain, SaturationBehavior) {
RunFilterUpdateTest(201, 65, blocks_with_echo_path_changes, RunFilterUpdateTest(201, 65, blocks_with_echo_path_changes,
blocks_with_saturation, false, &e, &y, &G_b); blocks_with_saturation, false, &e, &y, &G_b);
G_a.Spectrum(Aec3Optimization::kNone, &G_a_power); G_a.Spectrum(Aec3Optimization::kNone, G_a_power);
G_b.Spectrum(Aec3Optimization::kNone, &G_b_power); G_b.Spectrum(Aec3Optimization::kNone, G_b_power);
EXPECT_LT(std::accumulate(G_a_power.begin(), G_a_power.end(), 0.), EXPECT_LT(std::accumulate(G_a_power.begin(), G_a_power.end(), 0.),
std::accumulate(G_b_power.begin(), G_b_power.end(), 0.)); std::accumulate(G_b_power.begin(), G_b_power.end(), 0.));
@ -276,13 +288,13 @@ TEST(MainFilterUpdateGain, EchoPathChangeBehavior) {
std::array<float, kFftLengthBy2Plus1> G_a_power; std::array<float, kFftLengthBy2Plus1> G_a_power;
std::array<float, kFftLengthBy2Plus1> G_b_power; std::array<float, kFftLengthBy2Plus1> G_b_power;
RunFilterUpdateTest(99, 65, blocks_with_echo_path_changes,
blocks_with_saturation, false, &e, &y, &G_a);
RunFilterUpdateTest(100, 65, blocks_with_echo_path_changes, RunFilterUpdateTest(100, 65, blocks_with_echo_path_changes,
blocks_with_saturation, false, &e, &y, &G_a);
RunFilterUpdateTest(101, 65, blocks_with_echo_path_changes,
blocks_with_saturation, false, &e, &y, &G_b); blocks_with_saturation, false, &e, &y, &G_b);
G_a.Spectrum(Aec3Optimization::kNone, &G_a_power); G_a.Spectrum(Aec3Optimization::kNone, G_a_power);
G_b.Spectrum(Aec3Optimization::kNone, &G_b_power); G_b.Spectrum(Aec3Optimization::kNone, G_b_power);
EXPECT_LT(std::accumulate(G_a_power.begin(), G_a_power.end(), 0.), EXPECT_LT(std::accumulate(G_a_power.begin(), G_a_power.end(), 0.),
std::accumulate(G_b_power.begin(), G_b_power.end(), 0.)); std::accumulate(G_b_power.begin(), G_b_power.end(), 0.));

View File

@ -338,7 +338,7 @@ void MatchedFilter::Update(const DownsampledRenderBuffer& render_buffer,
bool filters_updated = false; bool filters_updated = false;
size_t x_start_index = size_t x_start_index =
(render_buffer.position + alignment_shift + sub_block_size_ - 1) % (render_buffer.read + alignment_shift + sub_block_size_ - 1) %
render_buffer.buffer.size(); render_buffer.buffer.size();
switch (optimization_) { switch (optimization_) {

View File

@ -151,20 +151,24 @@ TEST(MatchedFilter, LagEstimation) {
MatchedFilter filter(&data_dumper, DetectOptimization(), sub_block_size, MatchedFilter filter(&data_dumper, DetectOptimization(), sub_block_size,
kWindowSizeSubBlocks, kNumMatchedFilters, kWindowSizeSubBlocks, kNumMatchedFilters,
kAlignmentShiftSubBlocks, 150); kAlignmentShiftSubBlocks, 150);
EchoCanceller3Config config;
config.delay.down_sampling_factor = down_sampling_factor;
config.delay.num_filters = kNumMatchedFilters;
config.delay.min_echo_path_delay_blocks = 0;
std::unique_ptr<RenderDelayBuffer> render_delay_buffer( std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
RenderDelayBuffer::Create( RenderDelayBuffer::Create(config, 3));
3, down_sampling_factor,
GetDownSampledBufferSize(down_sampling_factor,
kNumMatchedFilters),
GetRenderDelayBufferSize(down_sampling_factor,
kNumMatchedFilters)));
// Analyze the correlation between render and capture. // Analyze the correlation between render and capture.
for (size_t k = 0; k < (300 + delay_samples / sub_block_size); ++k) { for (size_t k = 0; k < (600 + delay_samples / sub_block_size); ++k) {
RandomizeSampleVector(&random_generator, render[0]); RandomizeSampleVector(&random_generator, render[0]);
signal_delay_buffer.Delay(render[0], capture); signal_delay_buffer.Delay(render[0], capture);
render_delay_buffer->Insert(render); render_delay_buffer->Insert(render);
render_delay_buffer->UpdateBuffers(); if (k == 0) {
render_delay_buffer->Reset();
}
render_delay_buffer->PrepareCaptureCall();
std::array<float, kBlockSize> downsampled_capture_data; std::array<float, kBlockSize> downsampled_capture_data;
rtc::ArrayView<float> downsampled_capture( rtc::ArrayView<float> downsampled_capture(
downsampled_capture_data.data(), sub_block_size); downsampled_capture_data.data(), sub_block_size);
@ -179,7 +183,7 @@ TEST(MatchedFilter, LagEstimation) {
// Find which lag estimate should be the most accurate. // Find which lag estimate should be the most accurate.
rtc::Optional<size_t> expected_most_accurate_lag_estimate; rtc::Optional<size_t> expected_most_accurate_lag_estimate;
size_t alignment_shift_sub_blocks = 0; size_t alignment_shift_sub_blocks = 0;
for (size_t k = 0; k < kNumMatchedFilters; ++k) { for (size_t k = 0; k < config.delay.num_filters; ++k) {
if ((alignment_shift_sub_blocks + 3 * kWindowSizeSubBlocks / 4) * if ((alignment_shift_sub_blocks + 3 * kWindowSizeSubBlocks / 4) *
sub_block_size > sub_block_size >
delay_samples) { delay_samples) {
@ -236,6 +240,9 @@ TEST(MatchedFilter, LagEstimation) {
TEST(MatchedFilter, LagNotReliableForUncorrelatedRenderAndCapture) { TEST(MatchedFilter, LagNotReliableForUncorrelatedRenderAndCapture) {
Random random_generator(42U); Random random_generator(42U);
for (auto down_sampling_factor : kDownSamplingFactors) { for (auto down_sampling_factor : kDownSamplingFactors) {
EchoCanceller3Config config;
config.delay.down_sampling_factor = down_sampling_factor;
config.delay.num_filters = kNumMatchedFilters;
const size_t sub_block_size = kBlockSize / down_sampling_factor; const size_t sub_block_size = kBlockSize / down_sampling_factor;
std::vector<std::vector<float>> render(3, std::vector<std::vector<float>> render(3,
@ -245,11 +252,7 @@ TEST(MatchedFilter, LagNotReliableForUncorrelatedRenderAndCapture) {
std::fill(capture.begin(), capture.end(), 0.f); std::fill(capture.begin(), capture.end(), 0.f);
ApmDataDumper data_dumper(0); ApmDataDumper data_dumper(0);
std::unique_ptr<RenderDelayBuffer> render_delay_buffer( std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
RenderDelayBuffer::Create( RenderDelayBuffer::Create(config, 3));
3, down_sampling_factor,
GetDownSampledBufferSize(down_sampling_factor, kNumMatchedFilters),
GetRenderDelayBufferSize(down_sampling_factor,
kNumMatchedFilters)));
MatchedFilter filter(&data_dumper, DetectOptimization(), sub_block_size, MatchedFilter filter(&data_dumper, DetectOptimization(), sub_block_size,
kWindowSizeSubBlocks, kNumMatchedFilters, kWindowSizeSubBlocks, kNumMatchedFilters,
kAlignmentShiftSubBlocks, 150); kAlignmentShiftSubBlocks, 150);
@ -289,11 +292,7 @@ TEST(MatchedFilter, LagNotUpdatedForLowLevelRender) {
kWindowSizeSubBlocks, kNumMatchedFilters, kWindowSizeSubBlocks, kNumMatchedFilters,
kAlignmentShiftSubBlocks, 150); kAlignmentShiftSubBlocks, 150);
std::unique_ptr<RenderDelayBuffer> render_delay_buffer( std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
RenderDelayBuffer::Create( RenderDelayBuffer::Create(EchoCanceller3Config(), 3));
3, down_sampling_factor,
GetDownSampledBufferSize(down_sampling_factor, kNumMatchedFilters),
GetRenderDelayBufferSize(down_sampling_factor,
kNumMatchedFilters)));
Decimator capture_decimator(down_sampling_factor); Decimator capture_decimator(down_sampling_factor);
// Analyze the correlation between render and capture. // Analyze the correlation between render and capture.

View File

@ -0,0 +1,31 @@
/*
* Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include "modules/audio_processing/aec3/matrix_buffer.h"
#include "modules/audio_processing/aec3/aec3_common.h"
namespace webrtc {
MatrixBuffer::MatrixBuffer(size_t size, size_t height, size_t width)
: size(size),
buffer(size,
std::vector<std::vector<float>>(height,
std::vector<float>(width, 0.f))) {
for (auto& c : buffer) {
for (auto& b : c) {
std::fill(b.begin(), b.end(), 0.f);
}
}
}
MatrixBuffer::~MatrixBuffer() = default;
} // namespace webrtc

View File

@ -0,0 +1,54 @@
/*
* Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#ifndef MODULES_AUDIO_PROCESSING_AEC3_MATRIX_BUFFER_H_
#define MODULES_AUDIO_PROCESSING_AEC3_MATRIX_BUFFER_H_
#include <vector>
#include "rtc_base/checks.h"
namespace webrtc {
// Struct for bundling a circular buffer of two dimensional vector objects
// together with the read and write indices.
struct MatrixBuffer {
MatrixBuffer(size_t size, size_t height, size_t width);
~MatrixBuffer();
size_t IncIndex(size_t index) {
return index < buffer.size() - 1 ? index + 1 : 0;
}
size_t DecIndex(size_t index) {
return index > 0 ? index - 1 : buffer.size() - 1;
}
size_t OffsetIndex(size_t index, int offset) {
RTC_DCHECK_GE(buffer.size(), offset);
return (buffer.size() + index + offset) % buffer.size();
}
void UpdateWriteIndex(int offset) { write = OffsetIndex(write, offset); }
void IncWriteIndex() { write = IncIndex(write); }
void DecWriteIndex() { write = DecIndex(write); }
void UpdateReadIndex(int offset) { read = OffsetIndex(read, offset); }
void IncReadIndex() { read = IncIndex(read); }
void DecReadIndex() { read = DecIndex(read); }
size_t size;
std::vector<std::vector<std::vector<float>>> buffer;
size_t write = 0;
size_t read = 0;
};
} // namespace webrtc
#endif // MODULES_AUDIO_PROCESSING_AEC3_MATRIX_BUFFER_H_

View File

@ -25,10 +25,15 @@ namespace test {
class MockRenderDelayBuffer : public RenderDelayBuffer { class MockRenderDelayBuffer : public RenderDelayBuffer {
public: public:
explicit MockRenderDelayBuffer(int sample_rate_hz) explicit MockRenderDelayBuffer(int sample_rate_hz)
: render_buffer_(Aec3Optimization::kNone, : block_buffer_(GetRenderDelayBufferSize(4, 4),
NumBandsForRate(sample_rate_hz), NumBandsForRate(sample_rate_hz),
GetRenderDelayBufferSize(4, 4), kBlockSize),
std::vector<size_t>(1, kAdaptiveFilterLength)), spectrum_buffer_(block_buffer_.buffer.size(), kFftLengthBy2Plus1),
fft_buffer_(block_buffer_.buffer.size()),
render_buffer_(kAdaptiveFilterLength,
&block_buffer_,
&spectrum_buffer_,
&fft_buffer_),
downsampled_render_buffer_(GetDownSampledBufferSize(4, 4)) { downsampled_render_buffer_(GetDownSampledBufferSize(4, 4)) {
ON_CALL(*this, GetRenderBuffer()) ON_CALL(*this, GetRenderBuffer())
.WillByDefault( .WillByDefault(
@ -40,11 +45,14 @@ class MockRenderDelayBuffer : public RenderDelayBuffer {
virtual ~MockRenderDelayBuffer() = default; virtual ~MockRenderDelayBuffer() = default;
MOCK_METHOD0(Reset, void()); MOCK_METHOD0(Reset, void());
MOCK_METHOD1(Insert, bool(const std::vector<std::vector<float>>& block)); MOCK_METHOD1(Insert,
MOCK_METHOD0(UpdateBuffers, bool()); RenderDelayBuffer::BufferingEvent(
const std::vector<std::vector<float>>& block));
MOCK_METHOD0(PrepareCaptureCall, RenderDelayBuffer::BufferingEvent());
MOCK_METHOD1(SetDelay, void(size_t delay)); MOCK_METHOD1(SetDelay, void(size_t delay));
MOCK_CONST_METHOD0(Delay, size_t()); MOCK_CONST_METHOD0(Delay, size_t());
MOCK_CONST_METHOD0(MaxDelay, size_t()); MOCK_CONST_METHOD0(MaxDelay, size_t());
MOCK_CONST_METHOD0(MaxApiJitter, size_t());
MOCK_CONST_METHOD0(IsBlockAvailable, bool()); MOCK_CONST_METHOD0(IsBlockAvailable, bool());
MOCK_CONST_METHOD0(GetRenderBuffer, const RenderBuffer&()); MOCK_CONST_METHOD0(GetRenderBuffer, const RenderBuffer&());
MOCK_CONST_METHOD0(GetDownsampledRenderBuffer, MOCK_CONST_METHOD0(GetDownsampledRenderBuffer,
@ -55,6 +63,9 @@ class MockRenderDelayBuffer : public RenderDelayBuffer {
const DownsampledRenderBuffer& FakeGetDownsampledRenderBuffer() const { const DownsampledRenderBuffer& FakeGetDownsampledRenderBuffer() const {
return downsampled_render_buffer_; return downsampled_render_buffer_;
} }
MatrixBuffer block_buffer_;
VectorBuffer spectrum_buffer_;
FftBuffer fft_buffer_;
RenderBuffer render_buffer_; RenderBuffer render_buffer_;
DownsampledRenderBuffer downsampled_render_buffer_; DownsampledRenderBuffer downsampled_render_buffer_;
}; };

View File

@ -17,21 +17,18 @@
namespace webrtc { namespace webrtc {
RenderBuffer::RenderBuffer(Aec3Optimization optimization, RenderBuffer::RenderBuffer(size_t num_ffts_for_spectral_sums,
size_t num_bands, MatrixBuffer* block_buffer,
size_t num_partitions, VectorBuffer* spectrum_buffer,
const std::vector<size_t> num_ffts_for_spectral_sums) FftBuffer* fft_buffer)
: optimization_(optimization), : block_buffer_(block_buffer),
fft_buffer_(num_partitions), spectrum_buffer_(spectrum_buffer),
spectrum_buffer_(num_partitions, std::array<float, kFftLengthBy2Plus1>()), fft_buffer_(fft_buffer),
spectral_sums_(num_ffts_for_spectral_sums.size(), spectral_sums_length_(num_ffts_for_spectral_sums) {
std::array<float, kFftLengthBy2Plus1>()), RTC_DCHECK(block_buffer_);
last_block_(num_bands, std::vector<float>(kBlockSize, 0.f)), RTC_DCHECK(spectrum_buffer_);
fft_() { RTC_DCHECK(fft_buffer_);
// Current implementation only allows a maximum of one spectral sum lengths. RTC_DCHECK_GE(fft_buffer_->buffer.size(), spectral_sums_length_);
RTC_DCHECK_EQ(1, num_ffts_for_spectral_sums.size());
spectral_sums_length_ = num_ffts_for_spectral_sums[0];
RTC_DCHECK_GE(fft_buffer_.size(), spectral_sums_length_);
Clear(); Clear();
} }
@ -39,56 +36,17 @@ RenderBuffer::RenderBuffer(Aec3Optimization optimization,
RenderBuffer::~RenderBuffer() = default; RenderBuffer::~RenderBuffer() = default;
void RenderBuffer::Clear() { void RenderBuffer::Clear() {
position_ = 0; spectral_sums_.fill(0.f);
for (auto& sum : spectral_sums_) {
sum.fill(0.f);
} }
for (auto& spectrum : spectrum_buffer_) { void RenderBuffer::UpdateSpectralSum() {
spectrum.fill(0.f); std::fill(spectral_sums_.begin(), spectral_sums_.end(), 0.f);
size_t position = spectrum_buffer_->read;
for (size_t j = 0; j < spectral_sums_length_; ++j) {
for (size_t k = 0; k < spectral_sums_.size(); ++k) {
spectral_sums_[k] += spectrum_buffer_->buffer[position][k];
} }
position = spectrum_buffer_->IncIndex(position);
for (auto& fft : fft_buffer_) {
fft.Clear();
}
for (auto& b : last_block_) {
std::fill(b.begin(), b.end(), 0.f);
}
}
void RenderBuffer::Insert(const std::vector<std::vector<float>>& block) {
// Compute the FFT of the data in the lowest band.
FftData X;
fft_.PaddedFft(block[0], last_block_[0], &X);
// Copy the last render frame.
RTC_DCHECK_EQ(last_block_.size(), block.size());
for (size_t k = 0; k < block.size(); ++k) {
RTC_DCHECK_EQ(last_block_[k].size(), block[k].size());
std::copy(block[k].begin(), block[k].end(), last_block_[k].begin());
}
// Insert X into the buffer.
position_ = position_ > 0 ? position_ - 1 : fft_buffer_.size() - 1;
fft_buffer_[position_].Assign(X);
// Compute and insert the spectrum for the FFT into the spectrum buffer.
X.Spectrum(optimization_, &spectrum_buffer_[position_]);
// Pre-compute and cache the spectral sums.
std::copy(spectrum_buffer_[position_].begin(),
spectrum_buffer_[position_].end(), spectral_sums_[0].begin());
size_t position = (position_ + 1) % fft_buffer_.size();
for (size_t j = 1; j < spectral_sums_length_; ++j) {
const std::array<float, kFftLengthBy2Plus1>& spectrum =
spectrum_buffer_[position];
for (size_t k = 0; k < spectral_sums_[0].size(); ++k) {
spectral_sums_[0][k] += spectrum[k];
}
position = position < (fft_buffer_.size() - 1) ? position + 1 : 0;
} }
} }

View File

@ -11,12 +11,14 @@
#ifndef MODULES_AUDIO_PROCESSING_AEC3_RENDER_BUFFER_H_ #ifndef MODULES_AUDIO_PROCESSING_AEC3_RENDER_BUFFER_H_
#define MODULES_AUDIO_PROCESSING_AEC3_RENDER_BUFFER_H_ #define MODULES_AUDIO_PROCESSING_AEC3_RENDER_BUFFER_H_
#include <array>
#include <memory> #include <memory>
#include <vector>
#include "api/array_view.h" #include "api/array_view.h"
#include "modules/audio_processing/aec3/aec3_fft.h" #include "modules/audio_processing/aec3/fft_buffer.h"
#include "modules/audio_processing/aec3/fft_data.h" #include "modules/audio_processing/aec3/fft_data.h"
#include "modules/audio_processing/aec3/matrix_buffer.h"
#include "modules/audio_processing/aec3/vector_buffer.h"
#include "rtc_base/constructormagic.h" #include "rtc_base/constructormagic.h"
namespace webrtc { namespace webrtc {
@ -24,55 +26,48 @@ namespace webrtc {
// Provides a buffer of the render data for the echo remover. // Provides a buffer of the render data for the echo remover.
class RenderBuffer { class RenderBuffer {
public: public:
// The constructor takes, besides from the other parameters, a vector RenderBuffer(size_t num_ffts_for_spectral_sums,
// containing the number of FFTs that will be included in the spectral sums in MatrixBuffer* block_buffer,
// the call to SpectralSum. VectorBuffer* spectrum_buffer,
RenderBuffer(Aec3Optimization optimization, FftBuffer* fft_buffer);
size_t num_bands,
size_t size,
const std::vector<size_t> num_ffts_for_spectral_sums);
~RenderBuffer(); ~RenderBuffer();
// Clears the buffer. // Clears the buffer.
void Clear(); void Clear();
// Insert a block into the buffer. // Insert a block into the buffer.
void Insert(const std::vector<std::vector<float>>& block); void UpdateSpectralSum();
// Gets the last inserted block. // Gets the last inserted block.
const std::vector<std::vector<float>>& MostRecentBlock() const { const std::vector<std::vector<float>>& MostRecentBlock() const {
return last_block_; return block_buffer_->buffer[block_buffer_->read];
} }
// Get the spectrum from one of the FFTs in the buffer // Get the spectrum from one of the FFTs in the buffer.
const std::array<float, kFftLengthBy2Plus1>& Spectrum( rtc::ArrayView<const float> Spectrum(size_t buffer_offset_ffts) const {
size_t buffer_offset_ffts) const { size_t position = spectrum_buffer_->OffsetIndex(spectrum_buffer_->read,
return spectrum_buffer_[(position_ + buffer_offset_ffts) % buffer_offset_ffts);
fft_buffer_.size()]; return spectrum_buffer_->buffer[position];
} }
// Returns the sum of the spectrums for a certain number of FFTs. // Returns the sum of the spectrums for a certain number of FFTs.
const std::array<float, kFftLengthBy2Plus1>& SpectralSum( rtc::ArrayView<const float> SpectralSum(size_t num_ffts) const {
size_t num_ffts) const {
RTC_DCHECK_EQ(spectral_sums_length_, num_ffts); RTC_DCHECK_EQ(spectral_sums_length_, num_ffts);
return spectral_sums_[0]; return spectral_sums_;
} }
// Returns the circular buffer. // Returns the circular buffer.
rtc::ArrayView<const FftData> Buffer() const { return fft_buffer_; } rtc::ArrayView<const FftData> Buffer() const { return fft_buffer_->buffer; }
// Returns the current position in the circular buffer // Returns the current position in the circular buffer.
size_t Position() const { return position_; } size_t Position() const { return fft_buffer_->read; }
private: private:
const Aec3Optimization optimization_; const MatrixBuffer* const block_buffer_;
std::vector<FftData> fft_buffer_; VectorBuffer* spectrum_buffer_;
std::vector<std::array<float, kFftLengthBy2Plus1>> spectrum_buffer_; const FftBuffer* const fft_buffer_;
size_t spectral_sums_length_; const size_t spectral_sums_length_;
std::vector<std::array<float, kFftLengthBy2Plus1>> spectral_sums_; std::array<float, kFftLengthBy2Plus1> spectral_sums_;
size_t position_ = 0;
std::vector<std::vector<float>> last_block_;
const Aec3Fft fft_;
RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(RenderBuffer); RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(RenderBuffer);
}; };

View File

@ -20,25 +20,25 @@ namespace webrtc {
#if RTC_DCHECK_IS_ON && GTEST_HAS_DEATH_TEST && !defined(WEBRTC_ANDROID) #if RTC_DCHECK_IS_ON && GTEST_HAS_DEATH_TEST && !defined(WEBRTC_ANDROID)
// Verifies the check for the provided numbers of Ffts to include in the // Verifies the check for non-null fft buffer.
// spectral sum. TEST(RenderBuffer, NullExternalFftBuffer) {
TEST(RenderBuffer, TooLargeNumberOfSpectralSums) { MatrixBuffer block_buffer(10, 3, kBlockSize);
EXPECT_DEATH( VectorBuffer spectrum_buffer(10, kFftLengthBy2Plus1);
RenderBuffer(Aec3Optimization::kNone, 3, 1, std::vector<size_t>(2, 1)), EXPECT_DEATH(RenderBuffer(1, &block_buffer, &spectrum_buffer, nullptr), "");
"");
} }
TEST(RenderBuffer, TooSmallNumberOfSpectralSums) { // Verifies the check for non-null spectrum buffer.
EXPECT_DEATH( TEST(RenderBuffer, NullExternalSpectrumBuffer) {
RenderBuffer(Aec3Optimization::kNone, 3, 1, std::vector<size_t>()), ""); FftBuffer fft_buffer(10);
MatrixBuffer block_buffer(10, 3, kBlockSize);
EXPECT_DEATH(RenderBuffer(1, &block_buffer, nullptr, &fft_buffer), "");
} }
// Verifies the feasibility check for the provided number of Ffts to include in // Verifies the check for non-null block buffer.
// the spectral. TEST(RenderBuffer, NullExternalBlockBuffer) {
TEST(RenderBuffer, FeasibleNumberOfFftsInSum) { FftBuffer fft_buffer(10);
EXPECT_DEATH( VectorBuffer spectrum_buffer(10, kFftLengthBy2Plus1);
RenderBuffer(Aec3Optimization::kNone, 3, 1, std::vector<size_t>(1, 2)), EXPECT_DEATH(RenderBuffer(1, nullptr, &spectrum_buffer, &fft_buffer), "");
"");
} }
#endif #endif

View File

@ -14,9 +14,12 @@
#include <algorithm> #include <algorithm>
#include "modules/audio_processing/aec3/aec3_common.h" #include "modules/audio_processing/aec3/aec3_common.h"
#include "modules/audio_processing/aec3/aec3_fft.h"
#include "modules/audio_processing/aec3/block_processor.h" #include "modules/audio_processing/aec3/block_processor.h"
#include "modules/audio_processing/aec3/decimator.h" #include "modules/audio_processing/aec3/decimator.h"
#include "modules/audio_processing/aec3/fft_buffer.h"
#include "modules/audio_processing/aec3/fft_data.h" #include "modules/audio_processing/aec3/fft_data.h"
#include "modules/audio_processing/aec3/matrix_buffer.h"
#include "rtc_base/atomicops.h" #include "rtc_base/atomicops.h"
#include "rtc_base/checks.h" #include "rtc_base/checks.h"
#include "rtc_base/constructormagic.h" #include "rtc_base/constructormagic.h"
@ -25,191 +28,161 @@
namespace webrtc { namespace webrtc {
namespace { namespace {
class ApiCallJitterBuffer { constexpr int kBufferHeadroom = kAdaptiveFilterLength;
public:
explicit ApiCallJitterBuffer(size_t num_bands) {
buffer_.fill(std::vector<std::vector<float>>(
num_bands, std::vector<float>(kBlockSize, 0.f)));
}
~ApiCallJitterBuffer() = default;
void Reset() {
size_ = 0;
last_insert_index_ = 0;
}
void Insert(const std::vector<std::vector<float>>& block) {
RTC_DCHECK_LT(size_, buffer_.size());
last_insert_index_ = (last_insert_index_ + 1) % buffer_.size();
RTC_DCHECK_EQ(buffer_[last_insert_index_].size(), block.size());
RTC_DCHECK_EQ(buffer_[last_insert_index_][0].size(), block[0].size());
for (size_t k = 0; k < block.size(); ++k) {
std::copy(block[k].begin(), block[k].end(),
buffer_[last_insert_index_][k].begin());
}
++size_;
}
void Remove(std::vector<std::vector<float>>* block) {
RTC_DCHECK_LT(0, size_);
--size_;
const size_t extract_index =
(last_insert_index_ - size_ + buffer_.size()) % buffer_.size();
for (size_t k = 0; k < block->size(); ++k) {
std::copy(buffer_[extract_index][k].begin(),
buffer_[extract_index][k].end(), (*block)[k].begin());
}
}
size_t Size() const { return size_; }
bool Full() const { return size_ >= (buffer_.size()); }
bool Empty() const { return size_ == 0; }
private:
std::array<std::vector<std::vector<float>>, kMaxApiCallsJitterBlocks> buffer_;
size_t size_ = 0;
int last_insert_index_ = 0;
};
class RenderDelayBufferImpl final : public RenderDelayBuffer { class RenderDelayBufferImpl final : public RenderDelayBuffer {
public: public:
RenderDelayBufferImpl(size_t num_bands, RenderDelayBufferImpl(const EchoCanceller3Config& config, size_t num_bands);
size_t down_sampling_factor,
size_t downsampled_render_buffer_size,
size_t render_delay_buffer_size);
~RenderDelayBufferImpl() override; ~RenderDelayBufferImpl() override;
void Reset() override; void Reset() override;
bool Insert(const std::vector<std::vector<float>>& block) override; BufferingEvent Insert(const std::vector<std::vector<float>>& block) override;
bool UpdateBuffers() override; BufferingEvent PrepareCaptureCall() override;
void SetDelay(size_t delay) override; void SetDelay(size_t delay) override;
size_t Delay() const override { return delay_; } size_t Delay() const override { return delay_; }
size_t MaxDelay() const override {
const RenderBuffer& GetRenderBuffer() const override { return fft_buffer_; } return blocks_.buffer.size() - 1 - kBufferHeadroom;
}
size_t MaxApiJitter() const override { return max_api_jitter_; }
const RenderBuffer& GetRenderBuffer() const override {
return echo_remover_buffer_;
}
const DownsampledRenderBuffer& GetDownsampledRenderBuffer() const override { const DownsampledRenderBuffer& GetDownsampledRenderBuffer() const override {
return downsampled_render_buffer_; return low_rate_;
} }
private: private:
static int instance_count_; static int instance_count_;
std::unique_ptr<ApmDataDumper> data_dumper_; std::unique_ptr<ApmDataDumper> data_dumper_;
const Aec3Optimization optimization_; const Aec3Optimization optimization_;
const size_t down_sampling_factor_; const size_t api_call_jitter_blocks_;
const size_t sub_block_size_; const size_t min_echo_path_delay_blocks_;
std::vector<std::vector<std::vector<float>>> buffer_; const int sub_block_size_;
size_t delay_ = 0; MatrixBuffer blocks_;
size_t last_insert_index_ = 0; VectorBuffer spectra_;
RenderBuffer fft_buffer_; FftBuffer ffts_;
DownsampledRenderBuffer downsampled_render_buffer_; size_t delay_;
int max_api_jitter_ = 0;
int render_surplus_ = 0;
bool first_reset_occurred_ = false;
RenderBuffer echo_remover_buffer_;
DownsampledRenderBuffer low_rate_;
Decimator render_decimator_; Decimator render_decimator_;
ApiCallJitterBuffer api_call_jitter_buffer_;
const std::vector<std::vector<float>> zero_block_; const std::vector<std::vector<float>> zero_block_;
const Aec3Fft fft_;
size_t capture_call_counter_ = 0;
std::vector<float> render_ds_;
int render_calls_in_a_row_ = 0;
void UpdateBuffersWithLatestBlock(size_t previous_write);
void IncreaseRead();
void IncreaseInsert();
RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(RenderDelayBufferImpl); RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(RenderDelayBufferImpl);
}; };
int RenderDelayBufferImpl::instance_count_ = 0; int RenderDelayBufferImpl::instance_count_ = 0;
RenderDelayBufferImpl::RenderDelayBufferImpl( RenderDelayBufferImpl::RenderDelayBufferImpl(const EchoCanceller3Config& config,
size_t num_bands, size_t num_bands)
size_t down_sampling_factor,
size_t downsampled_render_buffer_size,
size_t render_delay_buffer_size)
: data_dumper_( : data_dumper_(
new ApmDataDumper(rtc::AtomicOps::Increment(&instance_count_))), new ApmDataDumper(rtc::AtomicOps::Increment(&instance_count_))),
optimization_(DetectOptimization()), optimization_(DetectOptimization()),
down_sampling_factor_(down_sampling_factor), api_call_jitter_blocks_(config.delay.api_call_jitter_blocks),
sub_block_size_(down_sampling_factor_ > 0 min_echo_path_delay_blocks_(config.delay.min_echo_path_delay_blocks),
? kBlockSize / down_sampling_factor sub_block_size_(
: kBlockSize), static_cast<int>(config.delay.down_sampling_factor > 0
buffer_( ? kBlockSize / config.delay.down_sampling_factor
render_delay_buffer_size, : kBlockSize)),
std::vector<std::vector<float>>(num_bands, blocks_(GetRenderDelayBufferSize(config.delay.down_sampling_factor,
std::vector<float>(kBlockSize, 0.f))), config.delay.num_filters),
fft_buffer_(
optimization_,
num_bands, num_bands,
std::max(kUnknownDelayRenderWindowSize, kAdaptiveFilterLength), kBlockSize),
std::vector<size_t>(1, kAdaptiveFilterLength)), spectra_(blocks_.buffer.size(), kFftLengthBy2Plus1),
downsampled_render_buffer_(downsampled_render_buffer_size), ffts_(blocks_.buffer.size()),
render_decimator_(down_sampling_factor_), delay_(min_echo_path_delay_blocks_),
api_call_jitter_buffer_(num_bands), echo_remover_buffer_(kAdaptiveFilterLength, &blocks_, &spectra_, &ffts_),
zero_block_(num_bands, std::vector<float>(kBlockSize, 0.f)) { low_rate_(GetDownSampledBufferSize(config.delay.down_sampling_factor,
RTC_DCHECK_LT(buffer_.size(), downsampled_render_buffer_.buffer.size()); config.delay.num_filters)),
render_decimator_(config.delay.down_sampling_factor),
zero_block_(num_bands, std::vector<float>(kBlockSize, 0.f)),
fft_(),
render_ds_(sub_block_size_, 0.f) {
RTC_DCHECK_EQ(blocks_.buffer.size(), ffts_.buffer.size());
RTC_DCHECK_EQ(spectra_.buffer.size(), ffts_.buffer.size());
Reset();
first_reset_occurred_ = false;
} }
RenderDelayBufferImpl::~RenderDelayBufferImpl() = default; RenderDelayBufferImpl::~RenderDelayBufferImpl() = default;
void RenderDelayBufferImpl::Reset() { void RenderDelayBufferImpl::Reset() {
// Empty all data in the buffers. delay_ = min_echo_path_delay_blocks_;
delay_ = 0; const int offset1 = std::max<int>(
last_insert_index_ = 0; std::min(api_call_jitter_blocks_, min_echo_path_delay_blocks_), 1);
downsampled_render_buffer_.position = 0; const int offset2 = static_cast<int>(delay_ + offset1);
std::fill(downsampled_render_buffer_.buffer.begin(), const int offset3 = offset1 * sub_block_size_;
downsampled_render_buffer_.buffer.end(), 0.f); low_rate_.read = low_rate_.OffsetIndex(low_rate_.write, offset3);
fft_buffer_.Clear(); blocks_.read = blocks_.OffsetIndex(blocks_.write, -offset2);
api_call_jitter_buffer_.Reset(); spectra_.read = spectra_.OffsetIndex(spectra_.write, offset2);
for (auto& c : buffer_) { ffts_.read = ffts_.OffsetIndex(ffts_.write, offset2);
for (auto& b : c) { render_surplus_ = 0;
std::fill(b.begin(), b.end(), 0.f); first_reset_occurred_ = true;
}
}
} }
bool RenderDelayBufferImpl::Insert( RenderDelayBuffer::BufferingEvent RenderDelayBufferImpl::Insert(
const std::vector<std::vector<float>>& block) { const std::vector<std::vector<float>>& block) {
RTC_DCHECK_EQ(block.size(), buffer_[0].size()); RTC_DCHECK_EQ(block.size(), blocks_.buffer[0].size());
RTC_DCHECK_EQ(block[0].size(), buffer_[0][0].size()); RTC_DCHECK_EQ(block[0].size(), blocks_.buffer[0][0].size());
BufferingEvent event = BufferingEvent::kNone;
if (api_call_jitter_buffer_.Full()) { ++render_surplus_;
// Report buffer overrun and let the caller handle the overrun. if (first_reset_occurred_) {
return false; ++render_calls_in_a_row_;
} max_api_jitter_ = std::max(max_api_jitter_, render_calls_in_a_row_);
api_call_jitter_buffer_.Insert(block);
return true;
} }
bool RenderDelayBufferImpl::UpdateBuffers() { const size_t previous_write = blocks_.write;
bool underrun = true; IncreaseInsert();
// Update the buffers with a new block if such is available, otherwise insert
// a block of silence. if (low_rate_.read == low_rate_.write || blocks_.read == blocks_.write) {
if (api_call_jitter_buffer_.Size() > 0) { // Render overrun due to more render data being inserted than read. Discard
last_insert_index_ = (last_insert_index_ + 1) % buffer_.size(); // the oldest render data.
api_call_jitter_buffer_.Remove(&buffer_[last_insert_index_]); event = BufferingEvent::kRenderOverrun;
underrun = false; IncreaseRead();
} }
downsampled_render_buffer_.position = for (size_t k = 0; k < block.size(); ++k) {
(downsampled_render_buffer_.position - sub_block_size_ + std::copy(block[k].begin(), block[k].end(),
downsampled_render_buffer_.buffer.size()) % blocks_.buffer[blocks_.write][k].begin());
downsampled_render_buffer_.buffer.size();
rtc::ArrayView<const float> input(
underrun ? zero_block_[0].data() : buffer_[last_insert_index_][0].data(),
kBlockSize);
rtc::ArrayView<float> output(downsampled_render_buffer_.buffer.data() +
downsampled_render_buffer_.position,
sub_block_size_);
data_dumper_->DumpWav("aec3_render_decimator_input", input.size(),
input.data(), 16000, 1);
render_decimator_.Decimate(input, output);
data_dumper_->DumpWav("aec3_render_decimator_output", output.size(),
output.data(), 16000 / down_sampling_factor_, 1);
for (size_t k = 0; k < output.size() / 2; ++k) {
float tmp = output[k];
output[k] = output[output.size() - 1 - k];
output[output.size() - 1 - k] = tmp;
} }
if (underrun) { UpdateBuffersWithLatestBlock(previous_write);
fft_buffer_.Insert(zero_block_); return event;
}
RenderDelayBuffer::BufferingEvent RenderDelayBufferImpl::PrepareCaptureCall() {
BufferingEvent event = BufferingEvent::kNone;
render_calls_in_a_row_ = 0;
if (low_rate_.read == low_rate_.write || blocks_.read == blocks_.write) {
event = BufferingEvent::kRenderUnderrun;
} else { } else {
fft_buffer_.Insert(buffer_[(last_insert_index_ - delay_ + buffer_.size()) % IncreaseRead();
buffer_.size()]);
} }
return !underrun; --render_surplus_;
echo_remover_buffer_.UpdateSpectralSum();
if (render_surplus_ >= static_cast<int>(api_call_jitter_blocks_)) {
event = BufferingEvent::kApiCallSkew;
RTC_LOG(LS_WARNING) << "Api call skew detected at " << capture_call_counter_
<< ".";
}
++capture_call_counter_;
return event;
} }
void RenderDelayBufferImpl::SetDelay(size_t delay) { void RenderDelayBufferImpl::SetDelay(size_t delay) {
@ -217,37 +190,51 @@ void RenderDelayBufferImpl::SetDelay(size_t delay) {
return; return;
} }
// If there is a new delay set, clear the fft buffer. const int delta_delay = static_cast<int>(delay_) - static_cast<int>(delay);
fft_buffer_.Clear();
if ((buffer_.size() - 1) < delay) {
// If the desired delay is larger than the delay buffer, shorten the delay
// buffer size to achieve the desired alignment with the available buffer
// size.
downsampled_render_buffer_.position =
(downsampled_render_buffer_.position +
sub_block_size_ * (delay - (buffer_.size() - 1))) %
downsampled_render_buffer_.buffer.size();
last_insert_index_ =
(last_insert_index_ - (delay - (buffer_.size() - 1)) + buffer_.size()) %
buffer_.size();
delay_ = buffer_.size() - 1;
} else {
delay_ = delay; delay_ = delay;
if (delay_ > MaxDelay()) {
delay_ = std::min(MaxDelay(), delay);
RTC_NOTREACHED();
} }
// Recompute the read indices according to the set delay.
blocks_.UpdateReadIndex(delta_delay);
spectra_.UpdateReadIndex(-delta_delay);
ffts_.UpdateReadIndex(-delta_delay);
} }
void RenderDelayBufferImpl::UpdateBuffersWithLatestBlock(
size_t previous_write) {
render_decimator_.Decimate(blocks_.buffer[blocks_.write][0], render_ds_);
std::copy(render_ds_.rbegin(), render_ds_.rend(),
low_rate_.buffer.begin() + low_rate_.write);
fft_.PaddedFft(blocks_.buffer[blocks_.write][0],
blocks_.buffer[previous_write][0], &ffts_.buffer[ffts_.write]);
ffts_.buffer[ffts_.write].Spectrum(optimization_,
spectra_.buffer[spectra_.write]);
};
void RenderDelayBufferImpl::IncreaseRead() {
low_rate_.UpdateReadIndex(-sub_block_size_);
blocks_.IncReadIndex();
spectra_.DecReadIndex();
ffts_.DecReadIndex();
};
void RenderDelayBufferImpl::IncreaseInsert() {
low_rate_.UpdateWriteIndex(-sub_block_size_);
blocks_.IncWriteIndex();
spectra_.DecWriteIndex();
ffts_.DecWriteIndex();
};
} // namespace } // namespace
RenderDelayBuffer* RenderDelayBuffer::Create( RenderDelayBuffer* RenderDelayBuffer::Create(const EchoCanceller3Config& config,
size_t num_bands, size_t num_bands) {
size_t down_sampling_factor, return new RenderDelayBufferImpl(config, num_bands);
size_t downsampled_render_buffer_size,
size_t render_delay_buffer_size) {
return new RenderDelayBufferImpl(num_bands, down_sampling_factor,
downsampled_render_buffer_size,
render_delay_buffer_size);
} }
} // namespace webrtc } // namespace webrtc

View File

@ -20,6 +20,7 @@
#include "modules/audio_processing/aec3/downsampled_render_buffer.h" #include "modules/audio_processing/aec3/downsampled_render_buffer.h"
#include "modules/audio_processing/aec3/fft_data.h" #include "modules/audio_processing/aec3/fft_data.h"
#include "modules/audio_processing/aec3/render_buffer.h" #include "modules/audio_processing/aec3/render_buffer.h"
#include "modules/audio_processing/include/audio_processing.h"
namespace webrtc { namespace webrtc {
@ -27,22 +28,28 @@ namespace webrtc {
// extracted with a specified delay. // extracted with a specified delay.
class RenderDelayBuffer { class RenderDelayBuffer {
public: public:
static RenderDelayBuffer* Create(size_t num_bands, enum class BufferingEvent {
size_t down_sampling_factor, kNone,
size_t downsampled_render_buffer_size, kRenderUnderrun,
size_t render_delay_buffer_size); kRenderOverrun,
kApiCallSkew,
kRenderDataLost
};
static RenderDelayBuffer* Create(const EchoCanceller3Config& config,
size_t num_bands);
virtual ~RenderDelayBuffer() = default; virtual ~RenderDelayBuffer() = default;
// Resets the buffer data. // Resets the buffer alignment.
virtual void Reset() = 0; virtual void Reset() = 0;
// Inserts a block into the buffer and returns true if the insert is // Inserts a block into the buffer.
// successful. virtual BufferingEvent Insert(
virtual bool Insert(const std::vector<std::vector<float>>& block) = 0; const std::vector<std::vector<float>>& block) = 0;
// Updates the buffers one step based on the specified buffer delay. Returns // Updates the buffers one step based on the specified buffer delay. Returns
// true if there was no overrun, otherwise returns false. // an enum indicating whether there was a special event that occurred.
virtual bool UpdateBuffers() = 0; virtual BufferingEvent PrepareCaptureCall() = 0;
// Sets the buffer delay. // Sets the buffer delay.
virtual void SetDelay(size_t delay) = 0; virtual void SetDelay(size_t delay) = 0;
@ -50,6 +57,12 @@ class RenderDelayBuffer {
// Gets the buffer delay. // Gets the buffer delay.
virtual size_t Delay() const = 0; virtual size_t Delay() const = 0;
// Gets the buffer delay.
virtual size_t MaxDelay() const = 0;
// Gets the observed jitter in the render and capture call sequence.
virtual size_t MaxApiJitter() const = 0;
// Returns the render buffer for the echo remover. // Returns the render buffer for the echo remover.
virtual const RenderBuffer& GetRenderBuffer() const = 0; virtual const RenderBuffer& GetRenderBuffer() const = 0;

View File

@ -30,49 +30,50 @@ std::string ProduceDebugText(int sample_rate_hz) {
return ss.str(); return ss.str();
} }
constexpr size_t kDownSamplingFactor = 4;
constexpr size_t kNumMatchedFilters = 4;
} // namespace } // namespace
// Verifies that the buffer overflow is correctly reported. // Verifies that the buffer overflow is correctly reported.
TEST(RenderDelayBuffer, BufferOverflow) { TEST(RenderDelayBuffer, BufferOverflow) {
const EchoCanceller3Config config;
for (auto rate : {8000, 16000, 32000, 48000}) { for (auto rate : {8000, 16000, 32000, 48000}) {
SCOPED_TRACE(ProduceDebugText(rate)); SCOPED_TRACE(ProduceDebugText(rate));
std::unique_ptr<RenderDelayBuffer> delay_buffer(RenderDelayBuffer::Create( std::unique_ptr<RenderDelayBuffer> delay_buffer(
NumBandsForRate(rate), kDownSamplingFactor, RenderDelayBuffer::Create(config, NumBandsForRate(rate)));
GetDownSampledBufferSize(kDownSamplingFactor, kNumMatchedFilters),
GetRenderDelayBufferSize(kDownSamplingFactor, kNumMatchedFilters)));
std::vector<std::vector<float>> block_to_insert( std::vector<std::vector<float>> block_to_insert(
NumBandsForRate(rate), std::vector<float>(kBlockSize, 0.f)); NumBandsForRate(rate), std::vector<float>(kBlockSize, 0.f));
for (size_t k = 0; k < kMaxApiCallsJitterBlocks; ++k) { for (size_t k = 0; k < 10; ++k) {
EXPECT_TRUE(delay_buffer->Insert(block_to_insert)); EXPECT_EQ(RenderDelayBuffer::BufferingEvent::kNone,
delay_buffer->Insert(block_to_insert));
} }
EXPECT_FALSE(delay_buffer->Insert(block_to_insert)); for (size_t k = 0; k < 1000; ++k) {
delay_buffer->Insert(block_to_insert);
}
EXPECT_EQ(RenderDelayBuffer::BufferingEvent::kRenderOverrun,
delay_buffer->Insert(block_to_insert));
} }
} }
// Verifies that the check for available block works. // Verifies that the check for available block works.
TEST(RenderDelayBuffer, AvailableBlock) { TEST(RenderDelayBuffer, AvailableBlock) {
constexpr size_t kNumBands = 1; constexpr size_t kNumBands = 1;
std::unique_ptr<RenderDelayBuffer> delay_buffer(RenderDelayBuffer::Create( std::unique_ptr<RenderDelayBuffer> delay_buffer(
kNumBands, kDownSamplingFactor, RenderDelayBuffer::Create(EchoCanceller3Config(), kNumBands));
GetDownSampledBufferSize(kDownSamplingFactor, kNumMatchedFilters),
GetRenderDelayBufferSize(kDownSamplingFactor, kNumMatchedFilters)));
std::vector<std::vector<float>> input_block( std::vector<std::vector<float>> input_block(
kNumBands, std::vector<float>(kBlockSize, 1.f)); kNumBands, std::vector<float>(kBlockSize, 1.f));
EXPECT_TRUE(delay_buffer->Insert(input_block)); EXPECT_EQ(RenderDelayBuffer::BufferingEvent::kNone,
delay_buffer->UpdateBuffers(); delay_buffer->Insert(input_block));
delay_buffer->PrepareCaptureCall();
} }
// Verifies the SetDelay method. // Verifies the SetDelay method.
TEST(RenderDelayBuffer, SetDelay) { TEST(RenderDelayBuffer, SetDelay) {
std::unique_ptr<RenderDelayBuffer> delay_buffer(RenderDelayBuffer::Create( EchoCanceller3Config config;
1, kDownSamplingFactor, std::unique_ptr<RenderDelayBuffer> delay_buffer(
GetDownSampledBufferSize(kDownSamplingFactor, kNumMatchedFilters), RenderDelayBuffer::Create(config, 1));
GetRenderDelayBufferSize(kDownSamplingFactor, kNumMatchedFilters))); EXPECT_EQ(config.delay.min_echo_path_delay_blocks, delay_buffer->Delay());
EXPECT_EQ(0u, delay_buffer->Delay()); for (size_t delay = config.delay.min_echo_path_delay_blocks + 1; delay < 20;
for (size_t delay = 0; delay < 20; ++delay) { ++delay) {
delay_buffer->SetDelay(delay); delay_buffer->SetDelay(delay);
EXPECT_EQ(delay, delay_buffer->Delay()); EXPECT_EQ(delay, delay_buffer->Delay());
} }
@ -84,10 +85,8 @@ TEST(RenderDelayBuffer, SetDelay) {
// TODO(peah): Re-enable the test once the issue with memory leaks during DEATH // TODO(peah): Re-enable the test once the issue with memory leaks during DEATH
// tests on test bots has been fixed. // tests on test bots has been fixed.
TEST(RenderDelayBuffer, DISABLED_WrongDelay) { TEST(RenderDelayBuffer, DISABLED_WrongDelay) {
std::unique_ptr<RenderDelayBuffer> delay_buffer(RenderDelayBuffer::Create( std::unique_ptr<RenderDelayBuffer> delay_buffer(
3, kDownSamplingFactor, RenderDelayBuffer::Create(EchoCanceller3Config(), 3));
GetDownSampledBufferSize(kDownSamplingFactor, kNumMatchedFilters),
GetRenderDelayBufferSize(kDownSamplingFactor, kNumMatchedFilters)));
EXPECT_DEATH(delay_buffer->SetDelay(21), ""); EXPECT_DEATH(delay_buffer->SetDelay(21), "");
} }
@ -96,9 +95,7 @@ TEST(RenderDelayBuffer, WrongNumberOfBands) {
for (auto rate : {16000, 32000, 48000}) { for (auto rate : {16000, 32000, 48000}) {
SCOPED_TRACE(ProduceDebugText(rate)); SCOPED_TRACE(ProduceDebugText(rate));
std::unique_ptr<RenderDelayBuffer> delay_buffer(RenderDelayBuffer::Create( std::unique_ptr<RenderDelayBuffer> delay_buffer(RenderDelayBuffer::Create(
NumBandsForRate(rate), kDownSamplingFactor, EchoCanceller3Config(), NumBandsForRate(rate)));
GetDownSampledBufferSize(kDownSamplingFactor, kNumMatchedFilters),
GetRenderDelayBufferSize(kDownSamplingFactor, kNumMatchedFilters)));
std::vector<std::vector<float>> block_to_insert( std::vector<std::vector<float>> block_to_insert(
NumBandsForRate(rate < 48000 ? rate + 16000 : 16000), NumBandsForRate(rate < 48000 ? rate + 16000 : 16000),
std::vector<float>(kBlockSize, 0.f)); std::vector<float>(kBlockSize, 0.f));
@ -110,10 +107,8 @@ TEST(RenderDelayBuffer, WrongNumberOfBands) {
TEST(RenderDelayBuffer, WrongBlockLength) { TEST(RenderDelayBuffer, WrongBlockLength) {
for (auto rate : {8000, 16000, 32000, 48000}) { for (auto rate : {8000, 16000, 32000, 48000}) {
SCOPED_TRACE(ProduceDebugText(rate)); SCOPED_TRACE(ProduceDebugText(rate));
std::unique_ptr<RenderDelayBuffer> delay_buffer(RenderDelayBuffer::Create( std::unique_ptr<RenderDelayBuffer> delay_buffer(
3, kDownSamplingFactor, RenderDelayBuffer::Create(EchoCanceller3Config(), 3));
GetDownSampledBufferSize(kDownSamplingFactor, kNumMatchedFilters),
GetRenderDelayBufferSize(kDownSamplingFactor, kNumMatchedFilters)));
std::vector<std::vector<float>> block_to_insert( std::vector<std::vector<float>> block_to_insert(
NumBandsForRate(rate), std::vector<float>(kBlockSize - 1, 0.f)); NumBandsForRate(rate), std::vector<float>(kBlockSize - 1, 0.f));
EXPECT_DEATH(delay_buffer->Insert(block_to_insert), ""); EXPECT_DEATH(delay_buffer->Insert(block_to_insert), "");

View File

@ -41,8 +41,10 @@ class RenderDelayControllerImpl final : public RenderDelayController {
private: private:
static int instance_count_; static int instance_count_;
std::unique_ptr<ApmDataDumper> data_dumper_; std::unique_ptr<ApmDataDumper> data_dumper_;
const size_t min_echo_path_delay_;
const size_t default_delay_; const size_t default_delay_;
size_t delay_; size_t delay_;
EchoPathDelayEstimator delay_estimator_;
size_t blocks_since_last_delay_estimate_ = 300000; size_t blocks_since_last_delay_estimate_ = 300000;
int echo_path_delay_samples_; int echo_path_delay_samples_;
size_t align_call_counter_ = 0; size_t align_call_counter_ = 0;
@ -50,7 +52,6 @@ class RenderDelayControllerImpl final : public RenderDelayController {
std::vector<float> capture_delay_buffer_; std::vector<float> capture_delay_buffer_;
int capture_delay_buffer_index_ = 0; int capture_delay_buffer_index_ = 0;
RenderDelayControllerMetrics metrics_; RenderDelayControllerMetrics metrics_;
EchoPathDelayEstimator delay_estimator_;
RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(RenderDelayControllerImpl); RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(RenderDelayControllerImpl);
}; };
@ -78,12 +79,15 @@ RenderDelayControllerImpl::RenderDelayControllerImpl(
int sample_rate_hz) int sample_rate_hz)
: data_dumper_( : data_dumper_(
new ApmDataDumper(rtc::AtomicOps::Increment(&instance_count_))), new ApmDataDumper(rtc::AtomicOps::Increment(&instance_count_))),
min_echo_path_delay_(config.delay.min_echo_path_delay_blocks),
default_delay_( default_delay_(
std::max(config.delay.default_delay, kMinEchoPathDelayBlocks)), std::max(config.delay.default_delay, min_echo_path_delay_)),
delay_(default_delay_), delay_(default_delay_),
delay_estimator_(data_dumper_.get(), config),
echo_path_delay_samples_(default_delay_ * kBlockSize), echo_path_delay_samples_(default_delay_ * kBlockSize),
capture_delay_buffer_(kBlockSize * (kMaxApiCallsJitterBlocks + 2), 0.f), capture_delay_buffer_(
delay_estimator_(data_dumper_.get(), config) { kBlockSize * (config.delay.api_call_jitter_blocks + 2),
0.f) {
RTC_DCHECK(ValidFullBandRate(sample_rate_hz)); RTC_DCHECK(ValidFullBandRate(sample_rate_hz));
delay_estimator_.LogDelayEstimationProperties(sample_rate_hz, delay_estimator_.LogDelayEstimationProperties(sample_rate_hz,
capture_delay_buffer_.size()); capture_delay_buffer_.size());

View File

@ -47,22 +47,20 @@ constexpr size_t kDownSamplingFactors[] = {2, 4, 8};
// Verifies the output of GetDelay when there are no AnalyzeRender calls. // Verifies the output of GetDelay when there are no AnalyzeRender calls.
TEST(RenderDelayController, NoRenderSignal) { TEST(RenderDelayController, NoRenderSignal) {
std::vector<float> block(kBlockSize, 0.f); std::vector<float> block(kBlockSize, 0.f);
EchoCanceller3Config config;
for (size_t num_matched_filters = 4; num_matched_filters == 10; for (size_t num_matched_filters = 4; num_matched_filters == 10;
num_matched_filters++) { num_matched_filters++) {
for (auto down_sampling_factor : kDownSamplingFactors) { for (auto down_sampling_factor : kDownSamplingFactors) {
config.delay.down_sampling_factor = down_sampling_factor;
config.delay.num_filters = num_matched_filters;
for (auto rate : {8000, 16000, 32000, 48000}) { for (auto rate : {8000, 16000, 32000, 48000}) {
SCOPED_TRACE(ProduceDebugText(rate)); SCOPED_TRACE(ProduceDebugText(rate));
std::unique_ptr<RenderDelayBuffer> delay_buffer( std::unique_ptr<RenderDelayBuffer> delay_buffer(
RenderDelayBuffer::Create( RenderDelayBuffer::Create(config, NumBandsForRate(rate)));
NumBandsForRate(rate), down_sampling_factor,
GetDownSampledBufferSize(down_sampling_factor,
num_matched_filters),
GetRenderDelayBufferSize(down_sampling_factor,
num_matched_filters)));
std::unique_ptr<RenderDelayController> delay_controller( std::unique_ptr<RenderDelayController> delay_controller(
RenderDelayController::Create(EchoCanceller3Config(), rate)); RenderDelayController::Create(config, rate));
for (size_t k = 0; k < 100; ++k) { for (size_t k = 0; k < 100; ++k) {
EXPECT_EQ(kMinEchoPathDelayBlocks, EXPECT_EQ(config.delay.min_echo_path_delay_blocks,
delay_controller->GetDelay( delay_controller->GetDelay(
delay_buffer->GetDownsampledRenderBuffer(), block)); delay_buffer->GetDownsampledRenderBuffer(), block));
} }
@ -78,26 +76,24 @@ TEST(RenderDelayController, BasicApiCalls) {
for (size_t num_matched_filters = 4; num_matched_filters == 10; for (size_t num_matched_filters = 4; num_matched_filters == 10;
num_matched_filters++) { num_matched_filters++) {
for (auto down_sampling_factor : kDownSamplingFactors) { for (auto down_sampling_factor : kDownSamplingFactors) {
EchoCanceller3Config config;
config.delay.down_sampling_factor = down_sampling_factor;
config.delay.num_filters = num_matched_filters;
for (auto rate : {8000, 16000, 32000, 48000}) { for (auto rate : {8000, 16000, 32000, 48000}) {
std::vector<std::vector<float>> render_block( std::vector<std::vector<float>> render_block(
NumBandsForRate(rate), std::vector<float>(kBlockSize, 0.f)); NumBandsForRate(rate), std::vector<float>(kBlockSize, 0.f));
std::unique_ptr<RenderDelayBuffer> render_delay_buffer( std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
RenderDelayBuffer::Create( RenderDelayBuffer::Create(config, NumBandsForRate(rate)));
NumBandsForRate(rate), down_sampling_factor,
GetDownSampledBufferSize(down_sampling_factor,
num_matched_filters),
GetRenderDelayBufferSize(down_sampling_factor,
num_matched_filters)));
std::unique_ptr<RenderDelayController> delay_controller( std::unique_ptr<RenderDelayController> delay_controller(
RenderDelayController::Create(EchoCanceller3Config(), rate)); RenderDelayController::Create(EchoCanceller3Config(), rate));
for (size_t k = 0; k < 10; ++k) { for (size_t k = 0; k < 10; ++k) {
render_delay_buffer->Insert(render_block); render_delay_buffer->Insert(render_block);
render_delay_buffer->UpdateBuffers(); render_delay_buffer->PrepareCaptureCall();
delay_blocks = delay_controller->GetDelay( delay_blocks = delay_controller->GetDelay(
render_delay_buffer->GetDownsampledRenderBuffer(), capture_block); render_delay_buffer->GetDownsampledRenderBuffer(), capture_block);
} }
EXPECT_FALSE(delay_controller->AlignmentHeadroomSamples()); EXPECT_FALSE(delay_controller->AlignmentHeadroomSamples());
EXPECT_EQ(kMinEchoPathDelayBlocks, delay_blocks); EXPECT_EQ(config.delay.min_echo_path_delay_blocks, delay_blocks);
} }
} }
} }
@ -112,6 +108,10 @@ TEST(RenderDelayController, Alignment) {
for (size_t num_matched_filters = 4; num_matched_filters == 10; for (size_t num_matched_filters = 4; num_matched_filters == 10;
num_matched_filters++) { num_matched_filters++) {
for (auto down_sampling_factor : kDownSamplingFactors) { for (auto down_sampling_factor : kDownSamplingFactors) {
EchoCanceller3Config config;
config.delay.down_sampling_factor = down_sampling_factor;
config.delay.num_filters = num_matched_filters;
for (auto rate : {8000, 16000, 32000, 48000}) { for (auto rate : {8000, 16000, 32000, 48000}) {
std::vector<std::vector<float>> render_block( std::vector<std::vector<float>> render_block(
NumBandsForRate(rate), std::vector<float>(kBlockSize, 0.f)); NumBandsForRate(rate), std::vector<float>(kBlockSize, 0.f));
@ -119,20 +119,15 @@ TEST(RenderDelayController, Alignment) {
for (size_t delay_samples : {15, 50, 150, 200, 800, 4000}) { for (size_t delay_samples : {15, 50, 150, 200, 800, 4000}) {
SCOPED_TRACE(ProduceDebugText(rate, delay_samples)); SCOPED_TRACE(ProduceDebugText(rate, delay_samples));
std::unique_ptr<RenderDelayBuffer> render_delay_buffer( std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
RenderDelayBuffer::Create( RenderDelayBuffer::Create(config, NumBandsForRate(rate)));
NumBandsForRate(rate), down_sampling_factor,
GetDownSampledBufferSize(down_sampling_factor,
num_matched_filters),
GetRenderDelayBufferSize(down_sampling_factor,
num_matched_filters)));
std::unique_ptr<RenderDelayController> delay_controller( std::unique_ptr<RenderDelayController> delay_controller(
RenderDelayController::Create(EchoCanceller3Config(), rate)); RenderDelayController::Create(config, rate));
DelayBuffer<float> signal_delay_buffer(delay_samples); DelayBuffer<float> signal_delay_buffer(delay_samples);
for (size_t k = 0; k < (400 + delay_samples / kBlockSize); ++k) { for (size_t k = 0; k < (400 + delay_samples / kBlockSize); ++k) {
RandomizeSampleVector(&random_generator, render_block[0]); RandomizeSampleVector(&random_generator, render_block[0]);
signal_delay_buffer.Delay(render_block[0], capture_block); signal_delay_buffer.Delay(render_block[0], capture_block);
render_delay_buffer->Insert(render_block); render_delay_buffer->Insert(render_block);
render_delay_buffer->UpdateBuffers(); render_delay_buffer->PrepareCaptureCall();
delay_blocks = delay_controller->GetDelay( delay_blocks = delay_controller->GetDelay(
render_delay_buffer->GetDownsampledRenderBuffer(), render_delay_buffer->GetDownsampledRenderBuffer(),
capture_block); capture_block);
@ -164,6 +159,9 @@ TEST(RenderDelayController, NonCausalAlignment) {
for (size_t num_matched_filters = 4; num_matched_filters == 10; for (size_t num_matched_filters = 4; num_matched_filters == 10;
num_matched_filters++) { num_matched_filters++) {
for (auto down_sampling_factor : kDownSamplingFactors) { for (auto down_sampling_factor : kDownSamplingFactors) {
EchoCanceller3Config config;
config.delay.down_sampling_factor = down_sampling_factor;
config.delay.num_filters = num_matched_filters;
for (auto rate : {8000, 16000, 32000, 48000}) { for (auto rate : {8000, 16000, 32000, 48000}) {
std::vector<std::vector<float>> render_block( std::vector<std::vector<float>> render_block(
NumBandsForRate(rate), std::vector<float>(kBlockSize, 0.f)); NumBandsForRate(rate), std::vector<float>(kBlockSize, 0.f));
@ -173,12 +171,7 @@ TEST(RenderDelayController, NonCausalAlignment) {
for (int delay_samples : {-15, -50, -150, -200}) { for (int delay_samples : {-15, -50, -150, -200}) {
SCOPED_TRACE(ProduceDebugText(rate, -delay_samples)); SCOPED_TRACE(ProduceDebugText(rate, -delay_samples));
std::unique_ptr<RenderDelayBuffer> render_delay_buffer( std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
RenderDelayBuffer::Create( RenderDelayBuffer::Create(config, NumBandsForRate(rate)));
NumBandsForRate(rate), down_sampling_factor,
GetDownSampledBufferSize(down_sampling_factor,
num_matched_filters),
GetRenderDelayBufferSize(down_sampling_factor,
num_matched_filters)));
std::unique_ptr<RenderDelayController> delay_controller( std::unique_ptr<RenderDelayController> delay_controller(
RenderDelayController::Create(EchoCanceller3Config(), rate)); RenderDelayController::Create(EchoCanceller3Config(), rate));
DelayBuffer<float> signal_delay_buffer(-delay_samples); DelayBuffer<float> signal_delay_buffer(-delay_samples);
@ -187,7 +180,7 @@ TEST(RenderDelayController, NonCausalAlignment) {
RandomizeSampleVector(&random_generator, capture_block[0]); RandomizeSampleVector(&random_generator, capture_block[0]);
signal_delay_buffer.Delay(capture_block[0], render_block[0]); signal_delay_buffer.Delay(capture_block[0], render_block[0]);
render_delay_buffer->Insert(render_block); render_delay_buffer->Insert(render_block);
render_delay_buffer->UpdateBuffers(); render_delay_buffer->PrepareCaptureCall();
delay_blocks = delay_controller->GetDelay( delay_blocks = delay_controller->GetDelay(
render_delay_buffer->GetDownsampledRenderBuffer(), render_delay_buffer->GetDownsampledRenderBuffer(),
capture_block[0]); capture_block[0]);
@ -212,6 +205,9 @@ TEST(RenderDelayController, AlignmentWithJitter) {
for (size_t num_matched_filters = 4; num_matched_filters == 10; for (size_t num_matched_filters = 4; num_matched_filters == 10;
num_matched_filters++) { num_matched_filters++) {
for (auto down_sampling_factor : kDownSamplingFactors) { for (auto down_sampling_factor : kDownSamplingFactors) {
EchoCanceller3Config config;
config.delay.down_sampling_factor = down_sampling_factor;
config.delay.num_filters = num_matched_filters;
for (auto rate : {8000, 16000, 32000, 48000}) { for (auto rate : {8000, 16000, 32000, 48000}) {
std::vector<std::vector<float>> render_block( std::vector<std::vector<float>> render_block(
NumBandsForRate(rate), std::vector<float>(kBlockSize, 0.f)); NumBandsForRate(rate), std::vector<float>(kBlockSize, 0.f));
@ -219,28 +215,25 @@ TEST(RenderDelayController, AlignmentWithJitter) {
size_t delay_blocks = 0; size_t delay_blocks = 0;
SCOPED_TRACE(ProduceDebugText(rate, delay_samples)); SCOPED_TRACE(ProduceDebugText(rate, delay_samples));
std::unique_ptr<RenderDelayBuffer> render_delay_buffer( std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
RenderDelayBuffer::Create( RenderDelayBuffer::Create(config, NumBandsForRate(rate)));
NumBandsForRate(rate), down_sampling_factor,
GetDownSampledBufferSize(down_sampling_factor,
num_matched_filters),
GetRenderDelayBufferSize(down_sampling_factor,
num_matched_filters)));
std::unique_ptr<RenderDelayController> delay_controller( std::unique_ptr<RenderDelayController> delay_controller(
RenderDelayController::Create(EchoCanceller3Config(), rate)); RenderDelayController::Create(config, rate));
DelayBuffer<float> signal_delay_buffer(delay_samples); DelayBuffer<float> signal_delay_buffer(delay_samples);
for (size_t j = 0; j < (1000 + delay_samples / kBlockSize) / for (size_t j = 0; j < (1000 + delay_samples / kBlockSize) /
kMaxApiCallsJitterBlocks + config.delay.api_call_jitter_blocks +
1; 1;
++j) { ++j) {
std::vector<std::vector<float>> capture_block_buffer; std::vector<std::vector<float>> capture_block_buffer;
for (size_t k = 0; k < (kMaxApiCallsJitterBlocks - 1); ++k) { for (size_t k = 0; k < (config.delay.api_call_jitter_blocks - 1);
++k) {
RandomizeSampleVector(&random_generator, render_block[0]); RandomizeSampleVector(&random_generator, render_block[0]);
signal_delay_buffer.Delay(render_block[0], capture_block); signal_delay_buffer.Delay(render_block[0], capture_block);
capture_block_buffer.push_back(capture_block); capture_block_buffer.push_back(capture_block);
render_delay_buffer->Insert(render_block); render_delay_buffer->Insert(render_block);
} }
for (size_t k = 0; k < (kMaxApiCallsJitterBlocks - 1); ++k) { for (size_t k = 0; k < (config.delay.api_call_jitter_blocks - 1);
render_delay_buffer->UpdateBuffers(); ++k) {
render_delay_buffer->PrepareCaptureCall();
delay_blocks = delay_controller->GetDelay( delay_blocks = delay_controller->GetDelay(
render_delay_buffer->GetDownsampledRenderBuffer(), render_delay_buffer->GetDownsampledRenderBuffer(),
capture_block_buffer[k]); capture_block_buffer[k]);
@ -275,17 +268,16 @@ TEST(RenderDelayController, InitialHeadroom) {
for (size_t num_matched_filters = 4; num_matched_filters == 10; for (size_t num_matched_filters = 4; num_matched_filters == 10;
num_matched_filters++) { num_matched_filters++) {
for (auto down_sampling_factor : kDownSamplingFactors) { for (auto down_sampling_factor : kDownSamplingFactors) {
EchoCanceller3Config config;
config.delay.down_sampling_factor = down_sampling_factor;
config.delay.num_filters = num_matched_filters;
for (auto rate : {8000, 16000, 32000, 48000}) { for (auto rate : {8000, 16000, 32000, 48000}) {
SCOPED_TRACE(ProduceDebugText(rate)); SCOPED_TRACE(ProduceDebugText(rate));
std::unique_ptr<RenderDelayBuffer> render_delay_buffer( std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
RenderDelayBuffer::Create( RenderDelayBuffer::Create(config, NumBandsForRate(rate)));
NumBandsForRate(rate), down_sampling_factor,
GetDownSampledBufferSize(down_sampling_factor,
num_matched_filters),
GetRenderDelayBufferSize(down_sampling_factor,
num_matched_filters)));
std::unique_ptr<RenderDelayController> delay_controller( std::unique_ptr<RenderDelayController> delay_controller(
RenderDelayController::Create(EchoCanceller3Config(), rate)); RenderDelayController::Create(config, rate));
EXPECT_FALSE(delay_controller->AlignmentHeadroomSamples()); EXPECT_FALSE(delay_controller->AlignmentHeadroomSamples());
} }
} }
@ -297,12 +289,11 @@ TEST(RenderDelayController, InitialHeadroom) {
// Verifies the check for the capture signal block size. // Verifies the check for the capture signal block size.
TEST(RenderDelayController, WrongCaptureSize) { TEST(RenderDelayController, WrongCaptureSize) {
std::vector<float> block(kBlockSize - 1, 0.f); std::vector<float> block(kBlockSize - 1, 0.f);
EchoCanceller3Config config;
for (auto rate : {8000, 16000, 32000, 48000}) { for (auto rate : {8000, 16000, 32000, 48000}) {
SCOPED_TRACE(ProduceDebugText(rate)); SCOPED_TRACE(ProduceDebugText(rate));
std::unique_ptr<RenderDelayBuffer> render_delay_buffer( std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
RenderDelayBuffer::Create(NumBandsForRate(rate), 4, RenderDelayBuffer::Create(config, NumBandsForRate(rate)));
GetDownSampledBufferSize(4, 4),
GetRenderDelayBufferSize(4, 4)));
EXPECT_DEATH( EXPECT_DEATH(
std::unique_ptr<RenderDelayController>( std::unique_ptr<RenderDelayController>(
RenderDelayController::Create(EchoCanceller3Config(), rate)) RenderDelayController::Create(EchoCanceller3Config(), rate))
@ -318,10 +309,9 @@ TEST(RenderDelayController, WrongCaptureSize) {
TEST(RenderDelayController, DISABLED_WrongSampleRate) { TEST(RenderDelayController, DISABLED_WrongSampleRate) {
for (auto rate : {-1, 0, 8001, 16001}) { for (auto rate : {-1, 0, 8001, 16001}) {
SCOPED_TRACE(ProduceDebugText(rate)); SCOPED_TRACE(ProduceDebugText(rate));
EchoCanceller3Config config;
std::unique_ptr<RenderDelayBuffer> render_delay_buffer( std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
RenderDelayBuffer::Create(NumBandsForRate(rate), 4, RenderDelayBuffer::Create(config, NumBandsForRate(rate)));
GetDownSampledBufferSize(4, 4),
GetRenderDelayBufferSize(4, 4)));
EXPECT_DEATH( EXPECT_DEATH(
std::unique_ptr<RenderDelayController>( std::unique_ptr<RenderDelayController>(
RenderDelayController::Create(EchoCanceller3Config(), rate)), RenderDelayController::Create(EchoCanceller3Config(), rate)),

View File

@ -30,8 +30,8 @@ void IdentifySmallNarrowBandRegions(
return; return;
} }
const std::array<float, kFftLengthBy2Plus1>& X2 = rtc::ArrayView<const float> X2 = render_buffer.Spectrum(*delay_partitions);
render_buffer.Spectrum(*delay_partitions); RTC_DCHECK_EQ(kFftLengthBy2Plus1, X2.size());
for (size_t k = 1; k < (X2.size() - 1); ++k) { for (size_t k = 1; k < (X2.size() - 1); ++k) {
(*narrow_band_counters)[k - 1] = X2[k] > 3 * std::max(X2[k - 1], X2[k + 1]) (*narrow_band_counters)[k - 1] = X2[k] > 3 * std::max(X2[k - 1], X2[k + 1])

View File

@ -18,7 +18,7 @@
#include "modules/audio_processing/aec3/aec3_common.h" #include "modules/audio_processing/aec3/aec3_common.h"
#include "modules/audio_processing/aec3/aec3_fft.h" #include "modules/audio_processing/aec3/aec3_fft.h"
#include "modules/audio_processing/aec3/fft_data.h" #include "modules/audio_processing/aec3/fft_data.h"
#include "modules/audio_processing/aec3/render_buffer.h" #include "modules/audio_processing/aec3/render_delay_buffer.h"
#include "modules/audio_processing/test/echo_canceller_test_tools.h" #include "modules/audio_processing/test/echo_canceller_test_tools.h"
#include "rtc_base/random.h" #include "rtc_base/random.h"
#include "test/gtest.h" #include "test/gtest.h"
@ -58,18 +58,22 @@ TEST(RenderSignalAnalyzer, NoFalseDetectionOfNarrowBands) {
Random random_generator(42U); Random random_generator(42U);
std::vector<std::vector<float>> x(3, std::vector<float>(kBlockSize, 0.f)); std::vector<std::vector<float>> x(3, std::vector<float>(kBlockSize, 0.f));
std::array<float, kBlockSize> x_old; std::array<float, kBlockSize> x_old;
FftData X; std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
Aec3Fft fft; RenderDelayBuffer::Create(EchoCanceller3Config(), 3));
RenderBuffer render_buffer(Aec3Optimization::kNone, 3, 1,
std::vector<size_t>(1, 1));
std::array<float, kFftLengthBy2Plus1> mask; std::array<float, kFftLengthBy2Plus1> mask;
x_old.fill(0.f); x_old.fill(0.f);
for (size_t k = 0; k < 100; ++k) { for (size_t k = 0; k < 100; ++k) {
RandomizeSampleVector(&random_generator, x[0]); RandomizeSampleVector(&random_generator, x[0]);
fft.PaddedFft(x[0], x_old, &X);
render_buffer.Insert(x); render_delay_buffer->Insert(x);
analyzer.Update(render_buffer, 0); if (k == 0) {
render_delay_buffer->Reset();
}
render_delay_buffer->PrepareCaptureCall();
analyzer.Update(render_delay_buffer->GetRenderBuffer(),
rtc::Optional<size_t>(0));
} }
mask.fill(1.f); mask.fill(1.f);
@ -86,8 +90,11 @@ TEST(RenderSignalAnalyzer, NarrowBandDetection) {
std::vector<std::vector<float>> x(3, std::vector<float>(kBlockSize, 0.f)); std::vector<std::vector<float>> x(3, std::vector<float>(kBlockSize, 0.f));
std::array<float, kBlockSize> x_old; std::array<float, kBlockSize> x_old;
Aec3Fft fft; Aec3Fft fft;
RenderBuffer render_buffer(Aec3Optimization::kNone, 3, 1, EchoCanceller3Config config;
std::vector<size_t>(1, 1)); config.delay.min_echo_path_delay_blocks = 0;
std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
RenderDelayBuffer::Create(config, 3));
std::array<float, kFftLengthBy2Plus1> mask; std::array<float, kFftLengthBy2Plus1> mask;
x_old.fill(0.f); x_old.fill(0.f);
constexpr int kSinusFrequencyBin = 32; constexpr int kSinusFrequencyBin = 32;
@ -97,9 +104,15 @@ TEST(RenderSignalAnalyzer, NarrowBandDetection) {
for (size_t k = 0; k < 100; ++k) { for (size_t k = 0; k < 100; ++k) {
ProduceSinusoid(16000, 16000 / 2 * kSinusFrequencyBin / kFftLengthBy2, ProduceSinusoid(16000, 16000 / 2 * kSinusFrequencyBin / kFftLengthBy2,
&sample_counter, x[0]); &sample_counter, x[0]);
render_buffer.Insert(x);
analyzer.Update(render_buffer, known_delay ? rtc::Optional<size_t>(0) render_delay_buffer->Insert(x);
: rtc::nullopt); if (k == 0) {
render_delay_buffer->Reset();
}
render_delay_buffer->PrepareCaptureCall();
analyzer.Update(render_delay_buffer->GetRenderBuffer(),
known_delay ? rtc::Optional<size_t>(0) : rtc::nullopt);
} }
}; };

View File

@ -12,6 +12,7 @@
#include "modules/audio_processing/aec3/aec3_fft.h" #include "modules/audio_processing/aec3/aec3_fft.h"
#include "modules/audio_processing/aec3/aec_state.h" #include "modules/audio_processing/aec3/aec_state.h"
#include "modules/audio_processing/aec3/render_delay_buffer.h"
#include "modules/audio_processing/include/audio_processing.h" #include "modules/audio_processing/include/audio_processing.h"
#include "modules/audio_processing/test/echo_canceller_test_tools.h" #include "modules/audio_processing/test/echo_canceller_test_tools.h"
#include "rtc_base/random.h" #include "rtc_base/random.h"
@ -23,38 +24,43 @@ namespace webrtc {
// Verifies that the check for non-null output residual echo power works. // Verifies that the check for non-null output residual echo power works.
TEST(ResidualEchoEstimator, NullResidualEchoPowerOutput) { TEST(ResidualEchoEstimator, NullResidualEchoPowerOutput) {
AecState aec_state(EchoCanceller3Config{}); EchoCanceller3Config config;
RenderBuffer render_buffer(Aec3Optimization::kNone, 3, 10, AecState aec_state(config);
std::vector<size_t>(1, 10)); std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
RenderDelayBuffer::Create(config, 3));
std::vector<std::array<float, kFftLengthBy2Plus1>> H2; std::vector<std::array<float, kFftLengthBy2Plus1>> H2;
std::array<float, kFftLengthBy2Plus1> S2_linear; std::array<float, kFftLengthBy2Plus1> S2_linear;
std::array<float, kFftLengthBy2Plus1> Y2; std::array<float, kFftLengthBy2Plus1> Y2;
EXPECT_DEATH(ResidualEchoEstimator(EchoCanceller3Config{}) EXPECT_DEATH(ResidualEchoEstimator(EchoCanceller3Config{})
.Estimate(aec_state, render_buffer, S2_linear, Y2, nullptr), .Estimate(aec_state, render_delay_buffer->GetRenderBuffer(),
S2_linear, Y2, nullptr),
""); "");
} }
#endif #endif
TEST(ResidualEchoEstimator, BasicTest) { // TODO(peah): This test is broken in the sense that it not at all tests what it
ResidualEchoEstimator estimator(EchoCanceller3Config{}); // seems to test. Enable the test once that is adressed.
TEST(ResidualEchoEstimator, DISABLED_BasicTest) {
EchoCanceller3Config config; EchoCanceller3Config config;
config.ep_strength.default_len = 0.f; config.ep_strength.default_len = 0.f;
config.delay.min_echo_path_delay_blocks = 0;
ResidualEchoEstimator estimator(config);
AecState aec_state(config); AecState aec_state(config);
RenderBuffer render_buffer(Aec3Optimization::kNone, 3, 10, std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
std::vector<size_t>(1, 10)); RenderDelayBuffer::Create(config, 3));
std::array<float, kFftLengthBy2Plus1> E2_main; std::array<float, kFftLengthBy2Plus1> E2_main;
std::array<float, kFftLengthBy2Plus1> E2_shadow; std::array<float, kFftLengthBy2Plus1> E2_shadow;
std::array<float, kFftLengthBy2Plus1> S2_linear; std::array<float, kFftLengthBy2Plus1> S2_linear;
std::array<float, kFftLengthBy2Plus1> S2_fallback; std::array<float, kFftLengthBy2Plus1> S2_fallback;
std::array<float, kFftLengthBy2Plus1> Y2; std::array<float, kFftLengthBy2Plus1> Y2;
std::array<float, kFftLengthBy2Plus1> R2; std::array<float, kFftLengthBy2Plus1> R2;
EchoPathVariability echo_path_variability(false, false); EchoPathVariability echo_path_variability(
false, EchoPathVariability::DelayAdjustment::kNone, false);
std::vector<std::vector<float>> x(3, std::vector<float>(kBlockSize, 0.f)); std::vector<std::vector<float>> x(3, std::vector<float>(kBlockSize, 0.f));
std::vector<std::array<float, kFftLengthBy2Plus1>> H2(10); std::vector<std::array<float, kFftLengthBy2Plus1>> H2(10);
Random random_generator(42U); Random random_generator(42U);
FftData X;
std::array<float, kBlockSize> x_old;
std::array<float, kBlockSize> s; std::array<float, kBlockSize> s;
Aec3Fft fft; Aec3Fft fft;
@ -76,17 +82,21 @@ TEST(ResidualEchoEstimator, BasicTest) {
S2_fallback.fill(kLevel); S2_fallback.fill(kLevel);
Y2.fill(kLevel); Y2.fill(kLevel);
for (int k = 0; k < 2000; ++k) { for (int k = 0; k < 1993; ++k) {
RandomizeSampleVector(&random_generator, x[0]); RandomizeSampleVector(&random_generator, x[0]);
std::for_each(x[0].begin(), x[0].end(), [](float& a) { a /= 30.f; }); std::for_each(x[0].begin(), x[0].end(), [](float& a) { a /= 30.f; });
fft.PaddedFft(x[0], x_old, &X); render_delay_buffer->Insert(x);
render_buffer.Insert(x); if (k == 0) {
render_delay_buffer->Reset();
}
render_delay_buffer->PrepareCaptureCall();
aec_state.HandleEchoPathChange(echo_path_variability); aec_state.HandleEchoPathChange(echo_path_variability);
aec_state.Update(H2, h, true, 2, render_buffer, E2_main, Y2, x[0], s, aec_state.Update(H2, h, true, 2, render_delay_buffer->GetRenderBuffer(),
false); E2_main, Y2, x[0], s, false);
estimator.Estimate(aec_state, render_buffer, S2_linear, Y2, &R2); estimator.Estimate(aec_state, render_delay_buffer->GetRenderBuffer(),
S2_linear, Y2, &R2);
} }
std::for_each(R2.begin(), R2.end(), std::for_each(R2.begin(), R2.end(),
[&](float a) { EXPECT_NEAR(kLevel, a, 0.1f); }); [&](float a) { EXPECT_NEAR(kLevel, a, 0.1f); });

View File

@ -51,7 +51,7 @@ void ShadowFilterUpdateGain::Compute(
constexpr float kNoiseGatePower = 220075344.f; constexpr float kNoiseGatePower = 220075344.f;
constexpr float kMuFixed = .5f; constexpr float kMuFixed = .5f;
std::array<float, kFftLengthBy2Plus1> mu; std::array<float, kFftLengthBy2Plus1> mu;
const auto& X2 = render_buffer.SpectralSum(size_partitions); auto X2 = render_buffer.SpectralSum(size_partitions);
std::transform(X2.begin(), X2.end(), mu.begin(), [&](float a) { std::transform(X2.begin(), X2.end(), mu.begin(), [&](float a) {
return a > kNoiseGatePower ? kMuFixed / a : 0.f; return a > kNoiseGatePower ? kMuFixed / a : 0.f;
}); });

View File

@ -18,6 +18,7 @@
#include "modules/audio_processing/aec3/adaptive_fir_filter.h" #include "modules/audio_processing/aec3/adaptive_fir_filter.h"
#include "modules/audio_processing/aec3/aec3_common.h" #include "modules/audio_processing/aec3/aec3_common.h"
#include "modules/audio_processing/aec3/aec_state.h" #include "modules/audio_processing/aec3/aec_state.h"
#include "modules/audio_processing/aec3/render_delay_buffer.h"
#include "modules/audio_processing/test/echo_canceller_test_tools.h" #include "modules/audio_processing/test/echo_canceller_test_tools.h"
#include "rtc_base/numerics/safe_minmax.h" #include "rtc_base/numerics/safe_minmax.h"
#include "rtc_base/random.h" #include "rtc_base/random.h"
@ -35,19 +36,22 @@ void RunFilterUpdateTest(int num_blocks_to_process,
std::array<float, kBlockSize>* y_last_block, std::array<float, kBlockSize>* y_last_block,
FftData* G_last_block) { FftData* G_last_block) {
ApmDataDumper data_dumper(42); ApmDataDumper data_dumper(42);
AdaptiveFirFilter main_filter(9, DetectOptimization(), &data_dumper); AdaptiveFirFilter main_filter(12, DetectOptimization(), &data_dumper);
AdaptiveFirFilter shadow_filter(9, DetectOptimization(), &data_dumper); AdaptiveFirFilter shadow_filter(12, DetectOptimization(), &data_dumper);
Aec3Fft fft; Aec3Fft fft;
RenderBuffer render_buffer(
Aec3Optimization::kNone, 3, main_filter.SizePartitions(), EchoCanceller3Config config;
std::vector<size_t>(1, main_filter.SizePartitions())); config.delay.min_echo_path_delay_blocks = 0;
std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
RenderDelayBuffer::Create(config, 3));
std::array<float, kBlockSize> x_old; std::array<float, kBlockSize> x_old;
x_old.fill(0.f); x_old.fill(0.f);
ShadowFilterUpdateGain shadow_gain; ShadowFilterUpdateGain shadow_gain;
Random random_generator(42U); Random random_generator(42U);
std::vector<std::vector<float>> x(3, std::vector<float>(kBlockSize, 0.f)); std::vector<std::vector<float>> x(3, std::vector<float>(kBlockSize, 0.f));
std::vector<float> y(kBlockSize, 0.f); std::vector<float> y(kBlockSize, 0.f);
AecState aec_state(EchoCanceller3Config{}); AecState aec_state(config);
RenderSignalAnalyzer render_signal_analyzer; RenderSignalAnalyzer render_signal_analyzer;
std::array<float, kFftLength> s; std::array<float, kFftLength> s;
FftData S; FftData S;
@ -67,10 +71,17 @@ void RunFilterUpdateTest(int num_blocks_to_process,
// Create the render signal. // Create the render signal.
RandomizeSampleVector(&random_generator, x[0]); RandomizeSampleVector(&random_generator, x[0]);
delay_buffer.Delay(x[0], y); delay_buffer.Delay(x[0], y);
render_buffer.Insert(x);
render_signal_analyzer.Update(render_buffer, delay_samples / kBlockSize);
shadow_filter.Filter(render_buffer, &S); render_delay_buffer->Insert(x);
if (k == 0) {
render_delay_buffer->Reset();
}
render_delay_buffer->PrepareCaptureCall();
render_signal_analyzer.Update(render_delay_buffer->GetRenderBuffer(),
delay_samples / kBlockSize);
shadow_filter.Filter(render_delay_buffer->GetRenderBuffer(), &S);
fft.Ifft(S, &s); fft.Ifft(S, &s);
std::transform(y.begin(), y.end(), s.begin() + kFftLengthBy2, std::transform(y.begin(), y.end(), s.begin() + kFftLengthBy2,
e_shadow.begin(), e_shadow.begin(),
@ -79,9 +90,10 @@ void RunFilterUpdateTest(int num_blocks_to_process,
[](float& a) { a = rtc::SafeClamp(a, -32768.f, 32767.f); }); [](float& a) { a = rtc::SafeClamp(a, -32768.f, 32767.f); });
fft.ZeroPaddedFft(e_shadow, &E_shadow); fft.ZeroPaddedFft(e_shadow, &E_shadow);
shadow_gain.Compute(render_buffer, render_signal_analyzer, E_shadow, shadow_gain.Compute(render_delay_buffer->GetRenderBuffer(),
render_signal_analyzer, E_shadow,
shadow_filter.SizePartitions(), saturation, &G); shadow_filter.SizePartitions(), saturation, &G);
shadow_filter.Adapt(render_buffer, G); shadow_filter.Adapt(render_delay_buffer->GetRenderBuffer(), G);
} }
std::copy(e_shadow.begin(), e_shadow.end(), e_last_block->begin()); std::copy(e_shadow.begin(), e_shadow.end(), e_last_block->begin());
@ -103,8 +115,10 @@ std::string ProduceDebugText(size_t delay) {
// Verifies that the check for non-null output gain parameter works. // Verifies that the check for non-null output gain parameter works.
TEST(ShadowFilterUpdateGain, NullDataOutputGain) { TEST(ShadowFilterUpdateGain, NullDataOutputGain) {
ApmDataDumper data_dumper(42); ApmDataDumper data_dumper(42);
RenderBuffer render_buffer(Aec3Optimization::kNone, 3, 1, FftBuffer fft_buffer(1);
std::vector<size_t>(1, 1)); MatrixBuffer block_buffer(fft_buffer.buffer.size(), 3, kBlockSize);
VectorBuffer spectrum_buffer(fft_buffer.buffer.size(), kFftLengthBy2Plus1);
RenderBuffer render_buffer(1, &block_buffer, &spectrum_buffer, &fft_buffer);
RenderSignalAnalyzer analyzer; RenderSignalAnalyzer analyzer;
FftData E; FftData E;
ShadowFilterUpdateGain gain; ShadowFilterUpdateGain gain;
@ -151,9 +165,9 @@ TEST(ShadowFilterUpdateGain, DecreasingGain) {
RunFilterUpdateTest(200, 65, blocks_with_saturation, &e, &y, &G_b); RunFilterUpdateTest(200, 65, blocks_with_saturation, &e, &y, &G_b);
RunFilterUpdateTest(300, 65, blocks_with_saturation, &e, &y, &G_c); RunFilterUpdateTest(300, 65, blocks_with_saturation, &e, &y, &G_c);
G_a.Spectrum(Aec3Optimization::kNone, &G_a_power); G_a.Spectrum(Aec3Optimization::kNone, G_a_power);
G_b.Spectrum(Aec3Optimization::kNone, &G_b_power); G_b.Spectrum(Aec3Optimization::kNone, G_b_power);
G_c.Spectrum(Aec3Optimization::kNone, &G_c_power); G_c.Spectrum(Aec3Optimization::kNone, G_c_power);
EXPECT_GT(std::accumulate(G_a_power.begin(), G_a_power.end(), 0.), EXPECT_GT(std::accumulate(G_a_power.begin(), G_a_power.end(), 0.),
std::accumulate(G_b_power.begin(), G_b_power.end(), 0.)); std::accumulate(G_b_power.begin(), G_b_power.end(), 0.));

View File

@ -59,14 +59,28 @@ Subtractor::~Subtractor() = default;
void Subtractor::HandleEchoPathChange( void Subtractor::HandleEchoPathChange(
const EchoPathVariability& echo_path_variability) { const EchoPathVariability& echo_path_variability) {
const auto full_reset = [&]() {
use_shadow_filter_frequency_response_ = false; use_shadow_filter_frequency_response_ = false;
if (echo_path_variability.delay_change) {
main_filter_.HandleEchoPathChange(); main_filter_.HandleEchoPathChange();
shadow_filter_.HandleEchoPathChange(); shadow_filter_.HandleEchoPathChange();
G_main_.HandleEchoPathChange(); G_main_.HandleEchoPathChange(echo_path_variability);
G_shadow_.HandleEchoPathChange(); G_shadow_.HandleEchoPathChange();
converged_filter_ = false; converged_filter_ = false;
converged_filter_counter_ = 0; converged_filter_counter_ = 0;
};
// TODO(peah): Add delay-change specific reset behavior.
if ((echo_path_variability.delay_change ==
EchoPathVariability::DelayAdjustment::kBufferFlush) ||
(echo_path_variability.delay_change ==
EchoPathVariability::DelayAdjustment::kDelayReset)) {
full_reset();
} else if (echo_path_variability.delay_change ==
EchoPathVariability::DelayAdjustment::kNewDetectedDelay) {
full_reset();
} else if (echo_path_variability.delay_change ==
EchoPathVariability::DelayAdjustment::kBufferReadjustment) {
full_reset();
} }
} }
@ -120,8 +134,8 @@ void Subtractor::Process(const RenderBuffer& render_buffer,
} }
// Compute spectra for future use. // Compute spectra for future use.
E_main.Spectrum(optimization_, &output->E2_main); E_main.Spectrum(optimization_, output->E2_main);
E_shadow.Spectrum(optimization_, &output->E2_shadow); E_shadow.Spectrum(optimization_, output->E2_shadow);
// Update the main filter. // Update the main filter.
G_main_.Compute(render_buffer, render_signal_analyzer, *output, main_filter_, G_main_.Compute(render_buffer, render_signal_analyzer, *output, main_filter_,

View File

@ -15,6 +15,7 @@
#include <string> #include <string>
#include "modules/audio_processing/aec3/aec_state.h" #include "modules/audio_processing/aec3/aec_state.h"
#include "modules/audio_processing/aec3/render_delay_buffer.h"
#include "modules/audio_processing/test/echo_canceller_test_tools.h" #include "modules/audio_processing/test/echo_canceller_test_tools.h"
#include "rtc_base/random.h" #include "rtc_base/random.h"
#include "test/gtest.h" #include "test/gtest.h"
@ -32,8 +33,10 @@ float RunSubtractorTest(int num_blocks_to_process,
std::vector<float> y(kBlockSize, 0.f); std::vector<float> y(kBlockSize, 0.f);
std::array<float, kBlockSize> x_old; std::array<float, kBlockSize> x_old;
SubtractorOutput output; SubtractorOutput output;
RenderBuffer render_buffer(Aec3Optimization::kNone, 3, kAdaptiveFilterLength, EchoCanceller3Config config;
std::vector<size_t>(1, kAdaptiveFilterLength)); config.delay.min_echo_path_delay_blocks = 0;
std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
RenderDelayBuffer::Create(config, 3));
RenderSignalAnalyzer render_signal_analyzer; RenderSignalAnalyzer render_signal_analyzer;
Random random_generator(42U); Random random_generator(42U);
Aec3Fft fft; Aec3Fft fft;
@ -54,23 +57,33 @@ float RunSubtractorTest(int num_blocks_to_process,
} else { } else {
delay_buffer.Delay(x[0], y); delay_buffer.Delay(x[0], y);
} }
render_buffer.Insert(x); render_delay_buffer->Insert(x);
render_signal_analyzer.Update(render_buffer, aec_state.FilterDelay()); if (k == 0) {
render_delay_buffer->Reset();
}
render_delay_buffer->PrepareCaptureCall();
render_signal_analyzer.Update(render_delay_buffer->GetRenderBuffer(),
aec_state.FilterDelay());
// Handle echo path changes. // Handle echo path changes.
if (std::find(blocks_with_echo_path_changes.begin(), if (std::find(blocks_with_echo_path_changes.begin(),
blocks_with_echo_path_changes.end(), blocks_with_echo_path_changes.end(),
k) != blocks_with_echo_path_changes.end()) { k) != blocks_with_echo_path_changes.end()) {
subtractor.HandleEchoPathChange(EchoPathVariability(true, true)); subtractor.HandleEchoPathChange(EchoPathVariability(
true, EchoPathVariability::DelayAdjustment::kNewDetectedDelay,
false));
} }
subtractor.Process(render_buffer, y, render_signal_analyzer, aec_state, subtractor.Process(render_delay_buffer->GetRenderBuffer(), y,
&output); render_signal_analyzer, aec_state, &output);
aec_state.HandleEchoPathChange(EchoPathVariability(false, false)); aec_state.HandleEchoPathChange(EchoPathVariability(
false, EchoPathVariability::DelayAdjustment::kNone, false));
aec_state.Update(subtractor.FilterFrequencyResponse(), aec_state.Update(subtractor.FilterFrequencyResponse(),
subtractor.FilterImpulseResponse(), subtractor.FilterImpulseResponse(),
subtractor.ConvergedFilter(), delay_samples / kBlockSize, subtractor.ConvergedFilter(),
render_buffer, E2_main, Y2, x[0], output.s_main, false); rtc::Optional<size_t>(delay_samples / kBlockSize),
render_delay_buffer->GetRenderBuffer(), E2_main, Y2, x[0],
output.s_main, false);
} }
const float output_power = std::inner_product( const float output_power = std::inner_product(
@ -104,12 +117,13 @@ TEST(Subtractor, NullDataDumper) {
TEST(Subtractor, DISABLED_NullOutput) { TEST(Subtractor, DISABLED_NullOutput) {
ApmDataDumper data_dumper(42); ApmDataDumper data_dumper(42);
Subtractor subtractor(&data_dumper, DetectOptimization()); Subtractor subtractor(&data_dumper, DetectOptimization());
RenderBuffer render_buffer(Aec3Optimization::kNone, 3, kAdaptiveFilterLength, std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
std::vector<size_t>(1, kAdaptiveFilterLength)); RenderDelayBuffer::Create(EchoCanceller3Config(), 3));
RenderSignalAnalyzer render_signal_analyzer; RenderSignalAnalyzer render_signal_analyzer;
std::vector<float> y(kBlockSize, 0.f); std::vector<float> y(kBlockSize, 0.f);
EXPECT_DEATH(subtractor.Process(render_buffer, y, render_signal_analyzer, EXPECT_DEATH(subtractor.Process(render_delay_buffer->GetRenderBuffer(), y,
render_signal_analyzer,
AecState(EchoCanceller3Config{}), nullptr), AecState(EchoCanceller3Config{}), nullptr),
""); "");
} }
@ -118,13 +132,14 @@ TEST(Subtractor, DISABLED_NullOutput) {
TEST(Subtractor, WrongCaptureSize) { TEST(Subtractor, WrongCaptureSize) {
ApmDataDumper data_dumper(42); ApmDataDumper data_dumper(42);
Subtractor subtractor(&data_dumper, DetectOptimization()); Subtractor subtractor(&data_dumper, DetectOptimization());
RenderBuffer render_buffer(Aec3Optimization::kNone, 3, kAdaptiveFilterLength, std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
std::vector<size_t>(1, kAdaptiveFilterLength)); RenderDelayBuffer::Create(EchoCanceller3Config(), 3));
RenderSignalAnalyzer render_signal_analyzer; RenderSignalAnalyzer render_signal_analyzer;
std::vector<float> y(kBlockSize - 1, 0.f); std::vector<float> y(kBlockSize - 1, 0.f);
SubtractorOutput output; SubtractorOutput output;
EXPECT_DEATH(subtractor.Process(render_buffer, y, render_signal_analyzer, EXPECT_DEATH(subtractor.Process(render_delay_buffer->GetRenderBuffer(), y,
render_signal_analyzer,
AecState(EchoCanceller3Config{}), &output), AecState(EchoCanceller3Config{}), &output),
""); "");
} }

View File

@ -11,7 +11,7 @@
#include "modules/audio_processing/aec3/suppression_gain.h" #include "modules/audio_processing/aec3/suppression_gain.h"
#include "modules/audio_processing/aec3/aec_state.h" #include "modules/audio_processing/aec3/aec_state.h"
#include "modules/audio_processing/aec3/render_buffer.h" #include "modules/audio_processing/aec3/render_delay_buffer.h"
#include "modules/audio_processing/aec3/subtractor.h" #include "modules/audio_processing/aec3/subtractor.h"
#include "modules/audio_processing/logging/apm_data_dumper.h" #include "modules/audio_processing/logging/apm_data_dumper.h"
#include "rtc_base/checks.h" #include "rtc_base/checks.h"
@ -57,13 +57,12 @@ TEST(SuppressionGain, BasicGainComputation) {
std::array<float, kFftLengthBy2Plus1> g; std::array<float, kFftLengthBy2Plus1> g;
std::array<float, kBlockSize> s; std::array<float, kBlockSize> s;
std::vector<std::vector<float>> x(1, std::vector<float>(kBlockSize, 0.f)); std::vector<std::vector<float>> x(1, std::vector<float>(kBlockSize, 0.f));
AecState aec_state(EchoCanceller3Config{}); EchoCanceller3Config config;
AecState aec_state(config);
ApmDataDumper data_dumper(42); ApmDataDumper data_dumper(42);
Subtractor subtractor(&data_dumper, DetectOptimization()); Subtractor subtractor(&data_dumper, DetectOptimization());
RenderBuffer render_buffer( std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
DetectOptimization(), 1, RenderDelayBuffer::Create(config, 3));
std::max(kUnknownDelayRenderWindowSize, kAdaptiveFilterLength),
std::vector<size_t>(1, kAdaptiveFilterLength));
// Verify the functionality for forcing a zero gain. // Verify the functionality for forcing a zero gain.
E2.fill(1000000000.f); E2.fill(1000000000.f);
@ -72,7 +71,8 @@ TEST(SuppressionGain, BasicGainComputation) {
s.fill(10.f); s.fill(10.f);
aec_state.Update( aec_state.Update(
subtractor.FilterFrequencyResponse(), subtractor.FilterImpulseResponse(), subtractor.FilterFrequencyResponse(), subtractor.FilterImpulseResponse(),
subtractor.ConvergedFilter(), 10, render_buffer, E2, Y2, x[0], s, false); subtractor.ConvergedFilter(), 10, render_delay_buffer->GetRenderBuffer(),
E2, Y2, x[0], s, false);
suppression_gain.GetGain(E2, R2, N2, analyzer, aec_state, x, &high_bands_gain, suppression_gain.GetGain(E2, R2, N2, analyzer, aec_state, x, &high_bands_gain,
&g); &g);
std::for_each(g.begin(), g.end(), [](float a) { EXPECT_FLOAT_EQ(0.f, a); }); std::for_each(g.begin(), g.end(), [](float a) { EXPECT_FLOAT_EQ(0.f, a); });
@ -85,17 +85,17 @@ TEST(SuppressionGain, BasicGainComputation) {
N2.fill(100.f); N2.fill(100.f);
// Ensure that the gain is no longer forced to zero. // Ensure that the gain is no longer forced to zero.
for (int k = 0; k <= kNumBlocksPerSecond / 5 + 1; ++k) { for (int k = 0; k <= kNumBlocksPerSecond / 5 + 1; ++k) {
aec_state.Update(subtractor.FilterFrequencyResponse(), aec_state.Update(
subtractor.FilterImpulseResponse(), subtractor.FilterFrequencyResponse(),
subtractor.ConvergedFilter(), 10, render_buffer, E2, Y2, subtractor.FilterImpulseResponse(), subtractor.ConvergedFilter(), 10,
x[0], s, false); render_delay_buffer->GetRenderBuffer(), E2, Y2, x[0], s, false);
} }
for (int k = 0; k < 100; ++k) { for (int k = 0; k < 100; ++k) {
aec_state.Update(subtractor.FilterFrequencyResponse(), aec_state.Update(
subtractor.FilterImpulseResponse(), subtractor.FilterFrequencyResponse(),
subtractor.ConvergedFilter(), 10, render_buffer, E2, Y2, subtractor.FilterImpulseResponse(), subtractor.ConvergedFilter(), 10,
x[0], s, false); render_delay_buffer->GetRenderBuffer(), E2, Y2, x[0], s, false);
suppression_gain.GetGain(E2, R2, N2, analyzer, aec_state, x, suppression_gain.GetGain(E2, R2, N2, analyzer, aec_state, x,
&high_bands_gain, &g); &high_bands_gain, &g);
} }
@ -108,10 +108,10 @@ TEST(SuppressionGain, BasicGainComputation) {
R2.fill(0.1f); R2.fill(0.1f);
N2.fill(0.f); N2.fill(0.f);
for (int k = 0; k < 100; ++k) { for (int k = 0; k < 100; ++k) {
aec_state.Update(subtractor.FilterFrequencyResponse(), aec_state.Update(
subtractor.FilterImpulseResponse(), subtractor.FilterFrequencyResponse(),
subtractor.ConvergedFilter(), 10, render_buffer, E2, Y2, subtractor.FilterImpulseResponse(), subtractor.ConvergedFilter(), 10,
x[0], s, false); render_delay_buffer->GetRenderBuffer(), E2, Y2, x[0], s, false);
suppression_gain.GetGain(E2, R2, N2, analyzer, aec_state, x, suppression_gain.GetGain(E2, R2, N2, analyzer, aec_state, x,
&high_bands_gain, &g); &high_bands_gain, &g);
} }

View File

@ -0,0 +1,26 @@
/*
* Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include "modules/audio_processing/aec3/vector_buffer.h"
#include "modules/audio_processing/aec3/aec3_common.h"
namespace webrtc {
VectorBuffer::VectorBuffer(size_t size, size_t height)
: size(size), buffer(size, std::vector<float>(height, 0.f)) {
for (auto& c : buffer) {
std::fill(c.begin(), c.end(), 0.f);
}
}
VectorBuffer::~VectorBuffer() = default;
} // namespace webrtc

View File

@ -0,0 +1,54 @@
/*
* Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#ifndef MODULES_AUDIO_PROCESSING_AEC3_VECTOR_BUFFER_H_
#define MODULES_AUDIO_PROCESSING_AEC3_VECTOR_BUFFER_H_
#include <vector>
#include "rtc_base/checks.h"
namespace webrtc {
// Struct for bundling a circular buffer of one dimensional vector objects
// together with the read and write indices.
struct VectorBuffer {
VectorBuffer(size_t size, size_t height);
~VectorBuffer();
size_t IncIndex(size_t index) {
return index < buffer.size() - 1 ? index + 1 : 0;
}
size_t DecIndex(size_t index) {
return index > 0 ? index - 1 : buffer.size() - 1;
}
size_t OffsetIndex(size_t index, int offset) {
RTC_DCHECK_GE(buffer.size(), offset);
return (buffer.size() + index + offset) % buffer.size();
}
void UpdateWriteIndex(int offset) { write = OffsetIndex(write, offset); }
void IncWriteIndex() { write = IncIndex(write); }
void DecWriteIndex() { write = DecIndex(write); }
void UpdateReadIndex(int offset) { read = OffsetIndex(read, offset); }
void IncReadIndex() { read = IncIndex(read); }
void DecReadIndex() { read = DecIndex(read); }
size_t size;
std::vector<std::vector<float>> buffer;
size_t write = 0;
size_t read = 0;
};
} // namespace webrtc
#endif // MODULES_AUDIO_PROCESSING_AEC3_VECTOR_BUFFER_H_

View File

@ -1154,6 +1154,8 @@ struct EchoCanceller3Config {
size_t default_delay = 5; size_t default_delay = 5;
size_t down_sampling_factor = 4; size_t down_sampling_factor = 4;
size_t num_filters = 4; size_t num_filters = 4;
size_t api_call_jitter_blocks = 26;
size_t min_echo_path_delay_blocks = 5;
} delay; } delay;
struct Erle { struct Erle {