Corrections of the render buffering scheme in AEC3 to ensure causality

This CL modifies the refactored render buffering scheme in AEC3
so that:
-A non-causal state can never occur which means that situations with
 nonrecoverable echo should not occur.
-For a stable audio pipeline with a predefined API call jitter,
 render overruns and underruns can never occur.

Bug: webrtc:8629,chromium:793305
Change-Id: I06ba1c368f92db95274090b08475dd02dbb85145
Reviewed-on: https://webrtc-review.googlesource.com/29861
Commit-Queue: Per Åhgren <peah@webrtc.org>
Reviewed-by: Gustaf Ullberg <gustaf@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#21215}
This commit is contained in:
Per Åhgren 2017-12-11 21:34:19 +01:00 committed by Commit Bot
parent efc5fbd8e0
commit c59a576c86
34 changed files with 535 additions and 408 deletions

View File

@ -71,7 +71,8 @@ TEST(AdaptiveFirFilter, FilterAdaptationNeonOptimizations) {
if (k == 0) { if (k == 0) {
render_delay_buffer->Reset(); render_delay_buffer->Reset();
} }
render_delay_buffer->PrepareCaptureCall(); render_delay_buffer->PrepareCaptureProcessing();
render_delay_buffer->GetRenderBuffer()->UpdateSpectralSum();
} }
const auto& render_buffer = render_delay_buffer->GetRenderBuffer(); const auto& render_buffer = render_delay_buffer->GetRenderBuffer();
@ -84,10 +85,10 @@ TEST(AdaptiveFirFilter, FilterAdaptationNeonOptimizations) {
G.im[0] = 0.f; G.im[0] = 0.f;
G.im[G.im.size() - 1] = 0.f; G.im[G.im.size() - 1] = 0.f;
AdaptPartitions_NEON(render_buffer, G, H_NEON); AdaptPartitions_NEON(*render_buffer, G, H_NEON);
AdaptPartitions(render_buffer, G, H_C); AdaptPartitions(*render_buffer, G, H_C);
AdaptPartitions_NEON(render_buffer, G, H_NEON); AdaptPartitions_NEON(*render_buffer, G, H_NEON);
AdaptPartitions(render_buffer, G, H_C); AdaptPartitions(*render_buffer, G, H_C);
for (size_t l = 0; l < H_C.size(); ++l) { for (size_t l = 0; l < H_C.size(); ++l) {
for (size_t j = 0; j < H_C[l].im.size(); ++j) { for (size_t j = 0; j < H_C[l].im.size(); ++j) {
@ -96,8 +97,8 @@ TEST(AdaptiveFirFilter, FilterAdaptationNeonOptimizations) {
} }
} }
ApplyFilter_NEON(render_buffer, H_NEON, &S_NEON); ApplyFilter_NEON(*render_buffer, H_NEON, &S_NEON);
ApplyFilter(render_buffer, H_C, &S_C); ApplyFilter(*render_buffer, H_C, &S_C);
for (size_t j = 0; j < S_C.re.size(); ++j) { for (size_t j = 0; j < S_C.re.size(); ++j) {
EXPECT_NEAR(S_C.re[j], S_NEON.re[j], fabs(S_C.re[j] * 0.00001f)); EXPECT_NEAR(S_C.re[j], S_NEON.re[j], fabs(S_C.re[j] * 0.00001f));
EXPECT_NEAR(S_C.im[j], S_NEON.im[j], fabs(S_C.re[j] * 0.00001f)); EXPECT_NEAR(S_C.im[j], S_NEON.im[j], fabs(S_C.re[j] * 0.00001f));
@ -182,11 +183,12 @@ TEST(AdaptiveFirFilter, FilterAdaptationSse2Optimizations) {
if (k == 0) { if (k == 0) {
render_delay_buffer->Reset(); render_delay_buffer->Reset();
} }
render_delay_buffer->PrepareCaptureCall(); render_delay_buffer->PrepareCaptureProcessing();
render_delay_buffer->GetRenderBuffer()->UpdateSpectralSum();
const auto& render_buffer = render_delay_buffer->GetRenderBuffer(); 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);
for (size_t j = 0; j < S_C.re.size(); ++j) { for (size_t j = 0; j < S_C.re.size(); ++j) {
EXPECT_FLOAT_EQ(S_C.re[j], S_SSE2.re[j]); EXPECT_FLOAT_EQ(S_C.re[j], S_SSE2.re[j]);
EXPECT_FLOAT_EQ(S_C.im[j], S_SSE2.im[j]); EXPECT_FLOAT_EQ(S_C.im[j], S_SSE2.im[j]);
@ -197,8 +199,8 @@ TEST(AdaptiveFirFilter, FilterAdaptationSse2Optimizations) {
std::for_each(G.im.begin(), G.im.end(), std::for_each(G.im.begin(), G.im.end(),
[&](float& a) { a = random_generator.Rand<float>(); }); [&](float& a) { a = random_generator.Rand<float>(); });
AdaptPartitions_SSE2(render_buffer, G, H_SSE2); AdaptPartitions_SSE2(*render_buffer, G, H_SSE2);
AdaptPartitions(render_buffer, G, H_C); AdaptPartitions(*render_buffer, G, H_C);
for (size_t k = 0; k < H_C.size(); ++k) { for (size_t k = 0; k < H_C.size(); ++k) {
for (size_t j = 0; j < H_C[k].re.size(); ++j) { for (size_t j = 0; j < H_C[k].re.size(); ++j) {
@ -277,7 +279,7 @@ TEST(AdaptiveFirFilter, NullFilterOutput) {
AdaptiveFirFilter filter(9, DetectOptimization(), &data_dumper); AdaptiveFirFilter filter(9, DetectOptimization(), &data_dumper);
std::unique_ptr<RenderDelayBuffer> render_delay_buffer( std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
RenderDelayBuffer::Create(EchoCanceller3Config(), 3)); RenderDelayBuffer::Create(EchoCanceller3Config(), 3));
EXPECT_DEATH(filter.Filter(render_delay_buffer->GetRenderBuffer(), nullptr), EXPECT_DEATH(filter.Filter(*render_delay_buffer->GetRenderBuffer(), nullptr),
""); "");
} }
@ -311,6 +313,7 @@ TEST(AdaptiveFirFilter, FilterAndAdapt) {
Aec3Fft fft; Aec3Fft fft;
EchoCanceller3Config config; EchoCanceller3Config config;
config.delay.min_echo_path_delay_blocks = 0; config.delay.min_echo_path_delay_blocks = 0;
config.delay.default_delay = 1;
std::unique_ptr<RenderDelayBuffer> render_delay_buffer( std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
RenderDelayBuffer::Create(config, 3)); RenderDelayBuffer::Create(config, 3));
ShadowFilterUpdateGain gain; ShadowFilterUpdateGain gain;
@ -362,12 +365,13 @@ TEST(AdaptiveFirFilter, FilterAndAdapt) {
if (k == 0) { if (k == 0) {
render_delay_buffer->Reset(); render_delay_buffer->Reset();
} }
render_delay_buffer->PrepareCaptureCall(); render_delay_buffer->PrepareCaptureProcessing();
render_delay_buffer->GetRenderBuffer()->UpdateSpectralSum();
const auto& render_buffer = render_delay_buffer->GetRenderBuffer(); 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);
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.begin(), e.begin(),
@ -379,14 +383,14 @@ TEST(AdaptiveFirFilter, FilterAndAdapt) {
s[k] = kScale * s_scratch[k + kFftLengthBy2]; s[k] = kScale * s_scratch[k + kFftLengthBy2];
} }
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( aec_state.HandleEchoPathChange(EchoPathVariability(
false, EchoPathVariability::DelayAdjustment::kNone, false)); 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);
} }
// Verify that the filter is able to perform well. // Verify that the filter is able to perform well.
EXPECT_LT(1000 * std::inner_product(e.begin(), e.end(), e.begin(), 0.f), EXPECT_LT(1000 * std::inner_product(e.begin(), e.end(), e.begin(), 0.f),

View File

@ -48,6 +48,8 @@ constexpr size_t kMaxNumBands = 3;
constexpr size_t kSubFrameLength = 80; constexpr size_t kSubFrameLength = 80;
constexpr size_t kBlockSize = kFftLengthBy2; constexpr size_t kBlockSize = kFftLengthBy2;
constexpr size_t kBlockSizeLog2 = 6;
constexpr size_t kExtendedBlockSize = 2 * kFftLengthBy2; constexpr size_t kExtendedBlockSize = 2 * kFftLengthBy2;
constexpr size_t kMatchedFilterWindowSizeSubBlocks = 32; constexpr size_t kMatchedFilterWindowSizeSubBlocks = 32;
constexpr size_t kMatchedFilterAlignmentShiftSizeSubBlocks = constexpr size_t kMatchedFilterAlignmentShiftSizeSubBlocks =
@ -80,12 +82,16 @@ 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 GetDownSampledBufferSize(down_sampling_factor, num_matched_filters) / return GetDownSampledBufferSize(down_sampling_factor, num_matched_filters) /
(kBlockSize / down_sampling_factor); (kBlockSize / down_sampling_factor) +
kAdaptiveFilterLength + 1;
} }
// Detects what kind of optimizations to use for the code. // Detects what kind of optimizations to use for the code.
Aec3Optimization DetectOptimization(); Aec3Optimization DetectOptimization();
static_assert(1 << kBlockSizeLog2 == kBlockSize,
"Proper number of shifts for blocksize");
static_assert(1 == NumBandsForRate(8000), "Number of bands for 8 kHz"); static_assert(1 == NumBandsForRate(8000), "Number of bands for 8 kHz");
static_assert(1 == NumBandsForRate(16000), "Number of bands for 16 kHz"); static_assert(1 == NumBandsForRate(16000), "Number of bands for 16 kHz");
static_assert(2 == NumBandsForRate(32000), "Number of bands for 32 kHz"); static_assert(2 == NumBandsForRate(32000), "Number of bands for 32 kHz");

View File

@ -49,7 +49,7 @@ 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_delay_buffer->GetRenderBuffer(), E2_main, rtc::nullopt, *render_delay_buffer->GetRenderBuffer(), E2_main,
Y2, x[0], s, false); Y2, x[0], s, false);
EXPECT_FALSE(state.UsableLinearEstimate()); EXPECT_FALSE(state.UsableLinearEstimate());
@ -57,7 +57,7 @@ TEST(AecState, NormalUsage) {
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_delay_buffer->GetRenderBuffer(), E2_main, Y2, x[0], s, *render_delay_buffer->GetRenderBuffer(), E2_main, Y2, x[0], s,
false); false);
} }
EXPECT_TRUE(state.UsableLinearEstimate()); EXPECT_TRUE(state.UsableLinearEstimate());
@ -67,7 +67,7 @@ TEST(AecState, NormalUsage) {
state.HandleEchoPathChange(EchoPathVariability( state.HandleEchoPathChange(EchoPathVariability(
true, EchoPathVariability::DelayAdjustment::kNone, false)); 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_delay_buffer->GetRenderBuffer(), E2_main, Y2, x[0], s, *render_delay_buffer->GetRenderBuffer(), E2_main, Y2, x[0], s,
false); false);
EXPECT_FALSE(state.UsableLinearEstimate()); EXPECT_FALSE(state.UsableLinearEstimate());
@ -76,25 +76,25 @@ TEST(AecState, NormalUsage) {
state.HandleEchoPathChange(EchoPathVariability( state.HandleEchoPathChange(EchoPathVariability(
true, EchoPathVariability::DelayAdjustment::kNewDetectedDelay, false)); 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_delay_buffer->GetRenderBuffer(), E2_main, Y2, x[0], s, *render_delay_buffer->GetRenderBuffer(), E2_main, Y2, x[0], s,
false); 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_delay_buffer->GetRenderBuffer(), E2_main, Y2, x[0], s, *render_delay_buffer->GetRenderBuffer(), E2_main, Y2, x[0], s,
false); 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_delay_buffer->GetRenderBuffer(), E2_main, Y2, x[0], s, *render_delay_buffer->GetRenderBuffer(), E2_main, Y2, x[0], s,
false); 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_delay_buffer->GetRenderBuffer(), E2_main, Y2, x[0], s, *render_delay_buffer->GetRenderBuffer(), E2_main, Y2, x[0], s,
true); true);
EXPECT_TRUE(state.EchoLeakageDetected()); EXPECT_TRUE(state.EchoLeakageDetected());
@ -104,19 +104,20 @@ TEST(AecState, NormalUsage) {
} }
x[0][0] = 5000.f; x[0][0] = 5000.f;
for (size_t k = 0; k < render_delay_buffer->GetRenderBuffer().Buffer().size(); for (size_t k = 0;
++k) { k < render_delay_buffer->GetRenderBuffer()->Buffer().size(); ++k) {
render_delay_buffer->Insert(x); render_delay_buffer->Insert(x);
if (k == 0) { if (k == 0) {
render_delay_buffer->Reset(); render_delay_buffer->Reset();
} }
render_delay_buffer->PrepareCaptureCall(); render_delay_buffer->PrepareCaptureProcessing();
render_delay_buffer->GetRenderBuffer()->UpdateSpectralSum();
} }
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_delay_buffer->GetRenderBuffer(), E2_main, Y2, x[0], s, *render_delay_buffer->GetRenderBuffer(), E2_main, Y2, x[0], s,
false); false);
} }
@ -133,7 +134,7 @@ 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_delay_buffer->GetRenderBuffer(), E2_main, Y2, x[0], s, *render_delay_buffer->GetRenderBuffer(), E2_main, Y2, x[0], s,
false); false);
} }
ASSERT_TRUE(state.UsableLinearEstimate()); ASSERT_TRUE(state.UsableLinearEstimate());
@ -154,7 +155,7 @@ 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_delay_buffer->GetRenderBuffer(), E2_main, Y2, x[0], s, *render_delay_buffer->GetRenderBuffer(), E2_main, Y2, x[0], s,
false); false);
} }
@ -204,7 +205,7 @@ 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_delay_buffer->GetRenderBuffer(), E2_main, Y2, x, s, *render_delay_buffer->GetRenderBuffer(), E2_main, Y2, x, s,
false); false);
EXPECT_TRUE(k == (kFilterLength - 1) || state.FilterDelay()); EXPECT_TRUE(k == (kFilterLength - 1) || state.FilterDelay());
if (k != (kFilterLength - 1)) { if (k != (kFilterLength - 1)) {
@ -243,7 +244,7 @@ TEST(AecState, ExternalDelay) {
state.HandleEchoPathChange(EchoPathVariability( state.HandleEchoPathChange(EchoPathVariability(
false, EchoPathVariability::DelayAdjustment::kNone, false)); 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_delay_buffer->GetRenderBuffer(), E2_main, Y2, x, s, *render_delay_buffer->GetRenderBuffer(), E2_main, Y2, x, s,
false); false);
EXPECT_TRUE(state.ExternalDelay()); EXPECT_TRUE(state.ExternalDelay());
EXPECT_EQ(k, state.ExternalDelay()); EXPECT_EQ(k, state.ExternalDelay());
@ -254,7 +255,7 @@ TEST(AecState, ExternalDelay) {
state.HandleEchoPathChange(EchoPathVariability( state.HandleEchoPathChange(EchoPathVariability(
false, EchoPathVariability::DelayAdjustment::kNone, false)); false, EchoPathVariability::DelayAdjustment::kNone, false));
state.Update(frequency_response, impulse_response, true, rtc::nullopt, state.Update(frequency_response, impulse_response, true, rtc::nullopt,
render_delay_buffer->GetRenderBuffer(), E2_main, Y2, x, s, *render_delay_buffer->GetRenderBuffer(), E2_main, Y2, x, s,
false); false);
EXPECT_FALSE(state.ExternalDelay()); EXPECT_FALSE(state.ExternalDelay());
} }

View File

@ -55,6 +55,7 @@ class BlockProcessorImpl final : public BlockProcessor {
std::unique_ptr<EchoRemover> echo_remover_; std::unique_ptr<EchoRemover> echo_remover_;
BlockProcessorMetrics metrics_; BlockProcessorMetrics metrics_;
RenderDelayBuffer::BufferingEvent render_event_; RenderDelayBuffer::BufferingEvent render_event_;
size_t capture_call_counter_ = 0;
RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(BlockProcessorImpl); RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(BlockProcessorImpl);
}; };
@ -86,6 +87,9 @@ void BlockProcessorImpl::ProcessCapture(
RTC_DCHECK(capture_block); RTC_DCHECK(capture_block);
RTC_DCHECK_EQ(NumBandsForRate(sample_rate_hz_), capture_block->size()); RTC_DCHECK_EQ(NumBandsForRate(sample_rate_hz_), capture_block->size());
RTC_DCHECK_EQ(kBlockSize, (*capture_block)[0].size()); RTC_DCHECK_EQ(kBlockSize, (*capture_block)[0].size());
capture_call_counter_++;
data_dumper_->DumpRaw("aec3_processblock_call_order", data_dumper_->DumpRaw("aec3_processblock_call_order",
static_cast<int>(BlockProcessorApiCall::kCapture)); static_cast<int>(BlockProcessorApiCall::kCapture));
data_dumper_->DumpWav("aec3_processblock_capture_input", kBlockSize, data_dumper_->DumpWav("aec3_processblock_capture_input", kBlockSize,
@ -111,30 +115,35 @@ void BlockProcessorImpl::ProcessCapture(
echo_path_variability.delay_change = echo_path_variability.delay_change =
EchoPathVariability::DelayAdjustment::kBufferFlush; EchoPathVariability::DelayAdjustment::kBufferFlush;
delay_controller_->Reset(); delay_controller_->Reset();
render_buffer_->Reset(); RTC_LOG(LS_WARNING) << "Reset due to render buffer overrun at block "
RTC_LOG(LS_WARNING) << "Reset due to render buffer overrun."; << capture_call_counter_;
} }
// Update the render buffers with any newly arrived render blocks and prepare // Update the render buffers with any newly arrived render blocks and prepare
// the render buffers for reading the render data corresponding to the current // the render buffers for reading the render data corresponding to the current
// capture block. // capture block.
render_event_ = render_buffer_->PrepareCaptureCall(); render_event_ = render_buffer_->PrepareCaptureProcessing();
RTC_DCHECK(RenderDelayBuffer::BufferingEvent::kRenderOverrun != RTC_DCHECK(RenderDelayBuffer::BufferingEvent::kRenderOverrun !=
render_event_); render_event_);
if (render_event_ == RenderDelayBuffer::BufferingEvent::kRenderUnderrun) { if (render_event_ == RenderDelayBuffer::BufferingEvent::kRenderUnderrun) {
echo_path_variability.delay_change = echo_path_variability.delay_change =
EchoPathVariability::DelayAdjustment::kBufferReadjustment; EchoPathVariability::DelayAdjustment::kDelayReset;
delay_controller_->Reset(); delay_controller_->Reset();
render_buffer_->Reset(); capture_properly_started_ = false;
render_properly_started_ = false;
RTC_LOG(LS_WARNING) << "Reset due to render buffer underrrun at block "
<< capture_call_counter_;
} else if (render_event_ == RenderDelayBuffer::BufferingEvent::kApiCallSkew) { } else if (render_event_ == RenderDelayBuffer::BufferingEvent::kApiCallSkew) {
// There have been too many render calls in a row. Reset to avoid noncausal // There have been too many render calls in a row. Reset to avoid noncausal
// echo. // echo.
echo_path_variability.delay_change = echo_path_variability.delay_change =
EchoPathVariability::DelayAdjustment::kDelayReset; EchoPathVariability::DelayAdjustment::kDelayReset;
delay_controller_->Reset(); delay_controller_->Reset();
render_buffer_->Reset();
capture_properly_started_ = false; capture_properly_started_ = false;
render_properly_started_ = false; render_properly_started_ = false;
RTC_LOG(LS_WARNING) << "Reset due to render buffer api skew at block "
<< capture_call_counter_;
} }
data_dumper_->DumpWav("aec3_processblock_capture_input2", kBlockSize, data_dumper_->DumpWav("aec3_processblock_capture_input2", kBlockSize,
@ -143,30 +152,32 @@ void BlockProcessorImpl::ProcessCapture(
// 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 estimated_delay = delay_controller_->GetDelay( rtc::Optional<size_t> estimated_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 = render_buffer_->Delay() != new_delay; if (estimated_delay) {
if (delay_change && new_delay >= config_.delay.min_echo_path_delay_blocks) { bool delay_change = render_buffer_->SetDelay(*estimated_delay);
if (delay_change) {
RTC_LOG(LS_WARNING) << "Delay changed to " << *estimated_delay
<< " at block " << capture_call_counter_;
if (render_buffer_->CausalDelay()) {
echo_path_variability.delay_change = echo_path_variability.delay_change =
EchoPathVariability::DelayAdjustment::kNewDetectedDelay; EchoPathVariability::DelayAdjustment::kNewDetectedDelay;
render_buffer_->SetDelay(new_delay); } else {
RTC_DCHECK_EQ(render_buffer_->Delay(), new_delay);
delay_controller_->SetDelay(new_delay);
} else if (delay_change &&
new_delay < config_.delay.min_echo_path_delay_blocks) {
// A noncausal delay has been detected. This can only happen if there is // A noncausal delay has been detected. This can only happen if there is
// clockdrift, an audio pipeline issue has occurred or the specified minimum // clockdrift, an audio pipeline issue has occurred or the specified
// delay is too short. Perform a full reset. // minimum delay is too short. Perform a full reset.
echo_path_variability.delay_change = echo_path_variability.delay_change =
EchoPathVariability::DelayAdjustment::kDelayReset; EchoPathVariability::DelayAdjustment::kDelayReset;
delay_controller_->Reset(); delay_controller_->Reset();
render_buffer_->Reset(); render_buffer_->Reset();
capture_properly_started_ = false; capture_properly_started_ = false;
render_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 at block "
<< capture_call_counter_;
}
}
} }
// Remove the echo from the capture signal. // Remove the echo from the capture signal.
@ -207,7 +218,8 @@ void BlockProcessorImpl::UpdateEchoLeakageStatus(bool leakage_detected) {
void BlockProcessorImpl::GetMetrics(EchoControl::Metrics* metrics) const { void BlockProcessorImpl::GetMetrics(EchoControl::Metrics* metrics) const {
echo_remover_->GetMetrics(metrics); echo_remover_->GetMetrics(metrics);
const int block_size_ms = sample_rate_hz_ == 8000 ? 8 : 4; const int block_size_ms = sample_rate_hz_ == 8000 ? 8 : 4;
metrics->delay_ms = static_cast<int>(render_buffer_->Delay()) * block_size_ms; rtc::Optional<size_t> delay = render_buffer_->Delay();
metrics->delay_ms = delay ? static_cast<int>(*delay) * block_size_ms : 0;
} }
} // namespace } // namespace
@ -217,7 +229,9 @@ BlockProcessor* BlockProcessor::Create(const EchoCanceller3Config& config,
std::unique_ptr<RenderDelayBuffer> render_buffer( std::unique_ptr<RenderDelayBuffer> render_buffer(
RenderDelayBuffer::Create(config, NumBandsForRate(sample_rate_hz))); RenderDelayBuffer::Create(config, NumBandsForRate(sample_rate_hz)));
std::unique_ptr<RenderDelayController> delay_controller( std::unique_ptr<RenderDelayController> delay_controller(
RenderDelayController::Create(config, sample_rate_hz)); RenderDelayController::Create(
config, RenderDelayBuffer::DelayEstimatorOffset(config),
sample_rate_hz));
std::unique_ptr<EchoRemover> echo_remover( std::unique_ptr<EchoRemover> echo_remover(
EchoRemover::Create(config, sample_rate_hz)); EchoRemover::Create(config, sample_rate_hz));
return Create(config, sample_rate_hz, std::move(render_buffer), return Create(config, sample_rate_hz, std::move(render_buffer),
@ -229,7 +243,9 @@ BlockProcessor* BlockProcessor::Create(
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(
RenderDelayController::Create(config, sample_rate_hz)); RenderDelayController::Create(
config, RenderDelayBuffer::DelayEstimatorOffset(config),
sample_rate_hz));
std::unique_ptr<EchoRemover> echo_remover( std::unique_ptr<EchoRemover> echo_remover(
EchoRemover::Create(config, sample_rate_hz)); EchoRemover::Create(config, sample_rate_hz));
return Create(config, sample_rate_hz, std::move(render_buffer), return Create(config, sample_rate_hz, std::move(render_buffer),

View File

@ -116,9 +116,6 @@ TEST(BlockProcessor, DISABLED_DelayControllerIntegration) {
EXPECT_CALL(*render_delay_buffer_mock, Insert(_)) EXPECT_CALL(*render_delay_buffer_mock, Insert(_))
.Times(kNumBlocks) .Times(kNumBlocks)
.WillRepeatedly(Return(RenderDelayBuffer::BufferingEvent::kNone)); .WillRepeatedly(Return(RenderDelayBuffer::BufferingEvent::kNone));
EXPECT_CALL(*render_delay_buffer_mock, IsBlockAvailable())
.Times(kNumBlocks)
.WillRepeatedly(Return(true));
EXPECT_CALL(*render_delay_buffer_mock, SetDelay(kDelayInBlocks)) EXPECT_CALL(*render_delay_buffer_mock, SetDelay(kDelayInBlocks))
.Times(AtLeast(1)); .Times(AtLeast(1));
EXPECT_CALL(*render_delay_buffer_mock, MaxDelay()).WillOnce(Return(30)); EXPECT_CALL(*render_delay_buffer_mock, MaxDelay()).WillOnce(Return(30));
@ -161,10 +158,7 @@ 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(RenderDelayBuffer::BufferingEvent::kNone)); .WillRepeatedly(Return(RenderDelayBuffer::BufferingEvent::kNone));
EXPECT_CALL(*render_delay_buffer_mock, IsBlockAvailable()) EXPECT_CALL(*render_delay_buffer_mock, PrepareCaptureProcessing())
.Times(kNumBlocks)
.WillRepeatedly(Return(true));
EXPECT_CALL(*render_delay_buffer_mock, PrepareCaptureCall())
.Times(kNumBlocks); .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())

View File

@ -13,7 +13,8 @@
namespace webrtc { namespace webrtc {
DownsampledRenderBuffer::DownsampledRenderBuffer(size_t downsampled_buffer_size) DownsampledRenderBuffer::DownsampledRenderBuffer(size_t downsampled_buffer_size)
: size(downsampled_buffer_size), buffer(downsampled_buffer_size, 0.f) { : size(static_cast<int>(downsampled_buffer_size)),
buffer(downsampled_buffer_size, 0.f) {
std::fill(buffer.begin(), buffer.end(), 0.f); std::fill(buffer.begin(), buffer.end(), 0.f);
} }

View File

@ -23,17 +23,20 @@ struct DownsampledRenderBuffer {
explicit DownsampledRenderBuffer(size_t downsampled_buffer_size); explicit DownsampledRenderBuffer(size_t downsampled_buffer_size);
~DownsampledRenderBuffer(); ~DownsampledRenderBuffer();
size_t IncIndex(size_t index) { int IncIndex(int index) const {
return index < (buffer.size() - 1) ? index + 1 : 0; RTC_DCHECK_EQ(buffer.size(), static_cast<size_t>(size));
return index < size - 1 ? index + 1 : 0;
} }
size_t DecIndex(size_t index) { int DecIndex(int index) const {
return index > 0 ? index - 1 : buffer.size() - 1; RTC_DCHECK_EQ(buffer.size(), static_cast<size_t>(size));
return index > 0 ? index - 1 : size - 1;
} }
size_t OffsetIndex(size_t index, int offset) { int OffsetIndex(int index, int offset) const {
RTC_DCHECK_GE(buffer.size(), offset); RTC_DCHECK_GE(buffer.size(), offset);
return (buffer.size() + index + offset) % buffer.size(); RTC_DCHECK_EQ(buffer.size(), static_cast<size_t>(size));
return (size + index + offset) % size;
} }
void UpdateWriteIndex(int offset) { write = OffsetIndex(write, offset); } void UpdateWriteIndex(int offset) { write = OffsetIndex(write, offset); }
@ -43,7 +46,7 @@ struct DownsampledRenderBuffer {
void IncReadIndex() { read = IncIndex(read); } void IncReadIndex() { read = IncIndex(read); }
void DecReadIndex() { read = DecIndex(read); } void DecReadIndex() { read = DecIndex(read); }
size_t size; const int size;
std::vector<float> buffer; std::vector<float> buffer;
int write = 0; int write = 0;
int read = 0; int read = 0;

View File

@ -65,25 +65,15 @@ TEST(EchoPathDelayEstimator, DelayEstimation) {
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, down_sampling_factor)); SCOPED_TRACE(ProduceDebugText(delay_samples, down_sampling_factor));
config.delay.min_echo_path_delay_blocks = 0; config.delay.api_call_jitter_blocks = 5;
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(config, 3)); RenderDelayBuffer::Create(config, 3));
DelayBuffer<float> signal_delay_buffer(delay_samples); DelayBuffer<float> signal_delay_buffer(
delay_samples + 2 * config.delay.api_call_jitter_blocks * 64);
EchoPathDelayEstimator estimator(&data_dumper, config); EchoPathDelayEstimator estimator(&data_dumper, config);
rtc::Optional<size_t> estimated_delay_samples; rtc::Optional<size_t> estimated_delay_samples;
for (size_t k = 0; k < (300 + delay_samples / kBlockSize); ++k) { for (size_t k = 0; k < (500 + (delay_samples) / kBlockSize); ++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);
@ -92,7 +82,9 @@ TEST(EchoPathDelayEstimator, DelayEstimation) {
render_delay_buffer->Reset(); render_delay_buffer->Reset();
} }
render_delay_buffer->PrepareCaptureCall(); render_delay_buffer->PrepareCaptureProcessing();
render_delay_buffer->GetRenderBuffer()->UpdateSpectralSum();
estimated_delay_samples = estimator.EstimateDelay( estimated_delay_samples = estimator.EstimateDelay(
render_delay_buffer->GetDownsampledRenderBuffer(), capture); render_delay_buffer->GetDownsampledRenderBuffer(), capture);
} }
@ -100,9 +92,9 @@ TEST(EchoPathDelayEstimator, DelayEstimation) {
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( EXPECT_NEAR(delay_samples,
delay_samples, *estimated_delay_samples -
*estimated_delay_samples + delay_estimate_offset * kBlockSize, (config.delay.api_call_jitter_blocks + 1) * 64,
config.delay.down_sampling_factor); config.delay.down_sampling_factor);
} else { } else {
ADD_FAILURE(); ADD_FAILURE();
@ -127,7 +119,8 @@ TEST(EchoPathDelayEstimator, NoInitialDelayestimates) {
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->PrepareCaptureCall(); render_delay_buffer->PrepareCaptureProcessing();
render_delay_buffer->GetRenderBuffer()->UpdateSpectralSum();
EXPECT_FALSE(estimator.EstimateDelay( EXPECT_FALSE(estimator.EstimateDelay(
render_delay_buffer->GetDownsampledRenderBuffer(), capture)); render_delay_buffer->GetDownsampledRenderBuffer(), capture));
} }
@ -151,7 +144,8 @@ 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->PrepareCaptureCall(); render_delay_buffer->PrepareCaptureProcessing();
render_delay_buffer->GetRenderBuffer()->UpdateSpectralSum();
EXPECT_FALSE(estimator.EstimateDelay( EXPECT_FALSE(estimator.EstimateDelay(
render_delay_buffer->GetDownsampledRenderBuffer(), capture)); render_delay_buffer->GetDownsampledRenderBuffer(), capture));
} }
@ -172,7 +166,7 @@ TEST(EchoPathDelayEstimator, NoDelayEstimatesForUncorrelatedSignals) {
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->PrepareCaptureCall(); render_delay_buffer->PrepareCaptureProcessing();
EXPECT_FALSE(estimator.EstimateDelay( EXPECT_FALSE(estimator.EstimateDelay(
render_delay_buffer->GetDownsampledRenderBuffer(), capture)); render_delay_buffer->GetDownsampledRenderBuffer(), capture));
} }

View File

@ -61,7 +61,7 @@ class EchoRemoverImpl final : public EchoRemover {
void ProcessCapture(const rtc::Optional<size_t>& echo_path_delay_samples, void ProcessCapture(const rtc::Optional<size_t>& echo_path_delay_samples,
const EchoPathVariability& echo_path_variability, const EchoPathVariability& echo_path_variability,
bool capture_signal_saturation, bool capture_signal_saturation,
const RenderBuffer& render_buffer, RenderBuffer* render_buffer,
std::vector<std::vector<float>>* capture) override; std::vector<std::vector<float>>* capture) override;
// Updates the status on whether echo leakage is detected in the output of the // Updates the status on whether echo leakage is detected in the output of the
@ -123,11 +123,11 @@ void EchoRemoverImpl::ProcessCapture(
const rtc::Optional<size_t>& echo_path_delay_samples, const rtc::Optional<size_t>& echo_path_delay_samples,
const EchoPathVariability& echo_path_variability, const EchoPathVariability& echo_path_variability,
bool capture_signal_saturation, bool capture_signal_saturation,
const RenderBuffer& render_buffer, RenderBuffer* render_buffer,
std::vector<std::vector<float>>* capture) { std::vector<std::vector<float>>* capture) {
const std::vector<std::vector<float>>& x = render_buffer.MostRecentBlock(); const std::vector<std::vector<float>>& x = render_buffer->MostRecentBlock();
std::vector<std::vector<float>>* y = capture; std::vector<std::vector<float>>* y = capture;
RTC_DCHECK(render_buffer);
RTC_DCHECK(y); RTC_DCHECK(y);
RTC_DCHECK_EQ(x.size(), NumBandsForRate(sample_rate_hz_)); RTC_DCHECK_EQ(x.size(), NumBandsForRate(sample_rate_hz_));
RTC_DCHECK_EQ(y->size(), NumBandsForRate(sample_rate_hz_)); RTC_DCHECK_EQ(y->size(), NumBandsForRate(sample_rate_hz_));
@ -143,6 +143,8 @@ void EchoRemoverImpl::ProcessCapture(
data_dumper_->DumpRaw("aec3_echo_remover_capture_input", y0); data_dumper_->DumpRaw("aec3_echo_remover_capture_input", y0);
data_dumper_->DumpRaw("aec3_echo_remover_render_input", x0); data_dumper_->DumpRaw("aec3_echo_remover_render_input", x0);
render_buffer->UpdateSpectralSum();
aec_state_.UpdateCaptureSaturation(capture_signal_saturation); aec_state_.UpdateCaptureSaturation(capture_signal_saturation);
if (echo_path_variability.AudioPathChanged()) { if (echo_path_variability.AudioPathChanged()) {
@ -165,10 +167,10 @@ void EchoRemoverImpl::ProcessCapture(
auto& e_main = subtractor_output.e_main; auto& e_main = subtractor_output.e_main;
// Analyze the render signal. // Analyze the render signal.
render_signal_analyzer_.Update(render_buffer, aec_state_.FilterDelay()); render_signal_analyzer_.Update(*render_buffer, aec_state_.FilterDelay());
// Perform linear echo cancellation. // Perform linear echo cancellation.
subtractor_.Process(render_buffer, y0, render_signal_analyzer_, aec_state_, subtractor_.Process(*render_buffer, y0, render_signal_analyzer_, aec_state_,
&subtractor_output); &subtractor_output);
// Compute spectra. // Compute spectra.
@ -180,7 +182,7 @@ void EchoRemoverImpl::ProcessCapture(
aec_state_.Update(subtractor_.FilterFrequencyResponse(), aec_state_.Update(subtractor_.FilterFrequencyResponse(),
subtractor_.FilterImpulseResponse(), subtractor_.FilterImpulseResponse(),
subtractor_.ConvergedFilter(), echo_path_delay_samples, subtractor_.ConvergedFilter(), echo_path_delay_samples,
render_buffer, E2_main, Y2, x0, subtractor_output.s_main, *render_buffer, E2_main, Y2, x0, subtractor_output.s_main,
echo_leakage_detected_); echo_leakage_detected_);
// Choose the linear output. // Choose the linear output.
@ -191,7 +193,7 @@ void EchoRemoverImpl::ProcessCapture(
const auto& E2 = output_selector_.UseSubtractorOutput() ? E2_main : Y2; const auto& E2 = output_selector_.UseSubtractorOutput() ? E2_main : Y2;
// Estimate the residual echo power. // Estimate the residual echo power.
residual_echo_estimator_.Estimate(aec_state_, render_buffer, S2_linear, Y2, residual_echo_estimator_.Estimate(aec_state_, *render_buffer, S2_linear, Y2,
&R2); &R2);
// Estimate the comfort noise. // Estimate the comfort noise.
@ -229,7 +231,7 @@ void EchoRemoverImpl::ProcessCapture(
data_dumper_->DumpRaw("aec3_E2_shadow", E2_shadow); data_dumper_->DumpRaw("aec3_E2_shadow", E2_shadow);
data_dumper_->DumpRaw("aec3_S2_linear", S2_linear); data_dumper_->DumpRaw("aec3_S2_linear", S2_linear);
data_dumper_->DumpRaw("aec3_Y2", Y2); data_dumper_->DumpRaw("aec3_Y2", Y2);
data_dumper_->DumpRaw("aec3_X2", render_buffer.Spectrum(0)); data_dumper_->DumpRaw("aec3_X2", render_buffer->Spectrum(0));
data_dumper_->DumpRaw("aec3_R2", R2); data_dumper_->DumpRaw("aec3_R2", R2);
data_dumper_->DumpRaw("aec3_erle", aec_state_.Erle()); data_dumper_->DumpRaw("aec3_erle", aec_state_.Erle());
data_dumper_->DumpRaw("aec3_erl", aec_state_.Erl()); data_dumper_->DumpRaw("aec3_erl", aec_state_.Erl());

View File

@ -37,7 +37,7 @@ class EchoRemover {
const rtc::Optional<size_t>& echo_path_delay_samples, const rtc::Optional<size_t>& echo_path_delay_samples,
const EchoPathVariability& echo_path_variability, const EchoPathVariability& echo_path_variability,
bool capture_signal_saturation, bool capture_signal_saturation,
const RenderBuffer& render_buffer, RenderBuffer* render_buffer,
std::vector<std::vector<float>>* capture) = 0; std::vector<std::vector<float>>* capture) = 0;
// Updates the status on whether echo leakage is detected in the output of the // Updates the status on whether echo leakage is detected in the output of the

View File

@ -64,7 +64,8 @@ TEST(EchoRemover, BasicApiCalls) {
(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->PrepareCaptureCall(); render_buffer->PrepareCaptureProcessing();
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);
@ -162,9 +163,8 @@ TEST(EchoRemover, BasicEchoRemoval) {
std::unique_ptr<EchoRemover> remover(EchoRemover::Create(config, rate)); std::unique_ptr<EchoRemover> remover(EchoRemover::Create(config, rate));
std::unique_ptr<RenderDelayBuffer> render_buffer( std::unique_ptr<RenderDelayBuffer> render_buffer(
RenderDelayBuffer::Create(config, NumBandsForRate(rate))); RenderDelayBuffer::Create(config, NumBandsForRate(rate)));
if (delay_samples != render_buffer->Delay() * kBlockSize) {
render_buffer->SetDelay(delay_samples / kBlockSize); render_buffer->SetDelay(delay_samples / kBlockSize);
}
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));
@ -192,7 +192,7 @@ TEST(EchoRemover, BasicEchoRemoval) {
} }
render_buffer->Insert(x); render_buffer->Insert(x);
render_buffer->PrepareCaptureCall(); render_buffer->PrepareCaptureProcessing();
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

@ -12,7 +12,7 @@
namespace webrtc { namespace webrtc {
FftBuffer::FftBuffer(size_t size) : buffer(size) { FftBuffer::FftBuffer(size_t size) : size(static_cast<int>(size)), buffer(size) {
for (auto& b : buffer) { for (auto& b : buffer) {
b.Clear(); b.Clear();
} }

View File

@ -24,17 +24,20 @@ struct FftBuffer {
explicit FftBuffer(size_t size); explicit FftBuffer(size_t size);
~FftBuffer(); ~FftBuffer();
size_t IncIndex(size_t index) { int IncIndex(int index) const {
return index < buffer.size() - 1 ? index + 1 : 0; RTC_DCHECK_EQ(buffer.size(), static_cast<size_t>(size));
return index < size - 1 ? index + 1 : 0;
} }
size_t DecIndex(size_t index) { int DecIndex(int index) const {
return index > 0 ? index - 1 : buffer.size() - 1; RTC_DCHECK_EQ(buffer.size(), static_cast<size_t>(size));
return index > 0 ? index - 1 : size - 1;
} }
size_t OffsetIndex(size_t index, int offset) { int OffsetIndex(int index, int offset) const {
RTC_DCHECK_GE(buffer.size(), offset); RTC_DCHECK_GE(buffer.size(), offset);
return (buffer.size() + index + offset) % buffer.size(); RTC_DCHECK_EQ(buffer.size(), static_cast<size_t>(size));
return (size + index + offset) % size;
} }
void UpdateWriteIndex(int offset) { write = OffsetIndex(write, offset); } void UpdateWriteIndex(int offset) { write = OffsetIndex(write, offset); }
@ -44,9 +47,10 @@ struct FftBuffer {
void IncReadIndex() { read = IncIndex(read); } void IncReadIndex() { read = IncIndex(read); }
void DecReadIndex() { read = DecIndex(read); } void DecReadIndex() { read = DecIndex(read); }
const int size;
std::vector<FftData> buffer; std::vector<FftData> buffer;
size_t write = 0; int write = 0;
size_t read = 0; int read = 0;
}; };
} // namespace webrtc } // namespace webrtc

View File

@ -52,6 +52,7 @@ void RunFilterUpdateTest(int num_blocks_to_process,
std::vector<float> y(kBlockSize, 0.f); std::vector<float> y(kBlockSize, 0.f);
EchoCanceller3Config config; EchoCanceller3Config config;
config.delay.min_echo_path_delay_blocks = 0; config.delay.min_echo_path_delay_blocks = 0;
config.delay.default_delay = 1;
std::unique_ptr<RenderDelayBuffer> render_delay_buffer( std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
RenderDelayBuffer::Create(config, 3)); RenderDelayBuffer::Create(config, 3));
AecState aec_state(config); AecState aec_state(config);
@ -98,13 +99,14 @@ void RunFilterUpdateTest(int num_blocks_to_process,
if (k == 0) { if (k == 0) {
render_delay_buffer->Reset(); render_delay_buffer->Reset();
} }
render_delay_buffer->PrepareCaptureCall(); render_delay_buffer->PrepareCaptureProcessing();
render_delay_buffer->GetRenderBuffer()->UpdateSpectralSum();
render_signal_analyzer.Update(render_delay_buffer->GetRenderBuffer(), render_signal_analyzer.Update(*render_delay_buffer->GetRenderBuffer(),
aec_state.FilterDelay()); aec_state.FilterDelay());
// Apply the main filter. // Apply the main filter.
main_filter.Filter(render_delay_buffer->GetRenderBuffer(), &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(),
@ -117,7 +119,7 @@ void RunFilterUpdateTest(int num_blocks_to_process,
} }
// Apply the shadow filter. // Apply the shadow filter.
shadow_filter.Filter(render_delay_buffer->GetRenderBuffer(), &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(),
@ -131,23 +133,23 @@ void RunFilterUpdateTest(int num_blocks_to_process,
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_delay_buffer->GetRenderBuffer(), shadow_gain.Compute(*render_delay_buffer->GetRenderBuffer(),
render_signal_analyzer, E_shadow, render_signal_analyzer, E_shadow,
shadow_filter.SizePartitions(), saturation, &G); shadow_filter.SizePartitions(), saturation, &G);
shadow_filter.Adapt(render_delay_buffer->GetRenderBuffer(), G); shadow_filter.Adapt(*render_delay_buffer->GetRenderBuffer(), G);
// Adapt the main filter // Adapt the main filter
main_gain.Compute(render_delay_buffer->GetRenderBuffer(), main_gain.Compute(*render_delay_buffer->GetRenderBuffer(),
render_signal_analyzer, output, main_filter, saturation, render_signal_analyzer, output, main_filter, saturation,
&G); &G);
main_filter.Adapt(render_delay_buffer->GetRenderBuffer(), G); main_filter.Adapt(*render_delay_buffer->GetRenderBuffer(), G);
// Update the delay. // Update the delay.
aec_state.HandleEchoPathChange(EchoPathVariability( aec_state.HandleEchoPathChange(EchoPathVariability(
false, EchoPathVariability::DelayAdjustment::kNone, false)); 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_delay_buffer->GetRenderBuffer(), E2_main, Y2, x[0], *render_delay_buffer->GetRenderBuffer(), E2_main, Y2, x[0],
s, false); s, false);
} }
@ -177,7 +179,7 @@ TEST(MainFilterUpdateGain, NullDataOutputGain) {
RenderSignalAnalyzer analyzer; RenderSignalAnalyzer analyzer;
SubtractorOutput output; SubtractorOutput output;
MainFilterUpdateGain gain; MainFilterUpdateGain gain;
EXPECT_DEATH(gain.Compute(render_delay_buffer->GetRenderBuffer(), analyzer, EXPECT_DEATH(gain.Compute(*render_delay_buffer->GetRenderBuffer(), analyzer,
output, filter, false, nullptr), output, filter, false, nullptr),
""); "");
} }

View File

@ -145,16 +145,17 @@ TEST(MatchedFilter, LagEstimation) {
ApmDataDumper data_dumper(0); ApmDataDumper data_dumper(0);
for (size_t delay_samples : {5, 64, 150, 200, 800, 1000}) { for (size_t delay_samples : {5, 64, 150, 200, 800, 1000}) {
SCOPED_TRACE(ProduceDebugText(delay_samples, down_sampling_factor)); SCOPED_TRACE(ProduceDebugText(delay_samples, down_sampling_factor));
EchoCanceller3Config config;
config.delay.down_sampling_factor = down_sampling_factor;
config.delay.num_filters = kNumMatchedFilters;
config.delay.min_echo_path_delay_blocks = 0;
config.delay.api_call_jitter_blocks = 0;
Decimator capture_decimator(down_sampling_factor); Decimator capture_decimator(down_sampling_factor);
DelayBuffer<float> signal_delay_buffer(down_sampling_factor * DelayBuffer<float> signal_delay_buffer(down_sampling_factor *
delay_samples); delay_samples);
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(config, 3)); RenderDelayBuffer::Create(config, 3));
@ -164,11 +165,13 @@ TEST(MatchedFilter, LagEstimation) {
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);
if (k == 0) { if (k == 0) {
render_delay_buffer->Reset(); render_delay_buffer->Reset();
} }
render_delay_buffer->PrepareCaptureCall(); render_delay_buffer->PrepareCaptureProcessing();
render_delay_buffer->GetRenderBuffer()->UpdateSpectralSum();
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);

View File

@ -15,7 +15,7 @@
namespace webrtc { namespace webrtc {
MatrixBuffer::MatrixBuffer(size_t size, size_t height, size_t width) MatrixBuffer::MatrixBuffer(size_t size, size_t height, size_t width)
: size(size), : size(static_cast<int>(size)),
buffer(size, buffer(size,
std::vector<std::vector<float>>(height, std::vector<std::vector<float>>(height,
std::vector<float>(width, 0.f))) { std::vector<float>(width, 0.f))) {

View File

@ -23,17 +23,20 @@ struct MatrixBuffer {
MatrixBuffer(size_t size, size_t height, size_t width); MatrixBuffer(size_t size, size_t height, size_t width);
~MatrixBuffer(); ~MatrixBuffer();
size_t IncIndex(size_t index) { int IncIndex(int index) const {
return index < buffer.size() - 1 ? index + 1 : 0; RTC_DCHECK_EQ(buffer.size(), static_cast<size_t>(size));
return index < size - 1 ? index + 1 : 0;
} }
size_t DecIndex(size_t index) { int DecIndex(int index) const {
return index > 0 ? index - 1 : buffer.size() - 1; RTC_DCHECK_EQ(buffer.size(), static_cast<size_t>(size));
return index > 0 ? index - 1 : size - 1;
} }
size_t OffsetIndex(size_t index, int offset) { int OffsetIndex(int index, int offset) const {
RTC_DCHECK_GE(buffer.size(), offset); RTC_DCHECK_EQ(buffer.size(), static_cast<size_t>(size));
return (buffer.size() + index + offset) % buffer.size(); RTC_DCHECK_GE(size, offset);
return (size + index + offset) % size;
} }
void UpdateWriteIndex(int offset) { write = OffsetIndex(write, offset); } void UpdateWriteIndex(int offset) { write = OffsetIndex(write, offset); }
@ -43,10 +46,10 @@ struct MatrixBuffer {
void IncReadIndex() { read = IncIndex(read); } void IncReadIndex() { read = IncIndex(read); }
void DecReadIndex() { read = DecIndex(read); } void DecReadIndex() { read = DecIndex(read); }
size_t size; const int size;
std::vector<std::vector<std::vector<float>>> buffer; std::vector<std::vector<std::vector<float>>> buffer;
size_t write = 0; int write = 0;
size_t read = 0; int read = 0;
}; };
} // namespace webrtc } // namespace webrtc

View File

@ -30,7 +30,7 @@ class MockEchoRemover : public EchoRemover {
void(const rtc::Optional<size_t>& echo_path_delay_samples, void(const rtc::Optional<size_t>& echo_path_delay_samples,
const EchoPathVariability& echo_path_variability, const EchoPathVariability& echo_path_variability,
bool capture_signal_saturation, bool capture_signal_saturation,
const RenderBuffer& render_buffer, RenderBuffer* render_buffer,
std::vector<std::vector<float>>* capture)); std::vector<std::vector<float>>* capture));
MOCK_METHOD1(UpdateEchoLeakageStatus, void(bool leakage_detected)); MOCK_METHOD1(UpdateEchoLeakageStatus, void(bool leakage_detected));

View File

@ -48,18 +48,17 @@ class MockRenderDelayBuffer : public RenderDelayBuffer {
MOCK_METHOD1(Insert, MOCK_METHOD1(Insert,
RenderDelayBuffer::BufferingEvent( RenderDelayBuffer::BufferingEvent(
const std::vector<std::vector<float>>& block)); const std::vector<std::vector<float>>& block));
MOCK_METHOD0(PrepareCaptureCall, RenderDelayBuffer::BufferingEvent()); MOCK_METHOD0(PrepareCaptureProcessing, RenderDelayBuffer::BufferingEvent());
MOCK_METHOD1(SetDelay, void(size_t delay)); MOCK_METHOD1(SetDelay, bool(size_t delay));
MOCK_CONST_METHOD0(Delay, size_t()); MOCK_CONST_METHOD0(Delay, rtc::Optional<size_t>());
MOCK_CONST_METHOD0(MaxDelay, size_t()); MOCK_CONST_METHOD0(MaxDelay, size_t());
MOCK_CONST_METHOD0(MaxApiJitter, size_t()); MOCK_METHOD0(GetRenderBuffer, RenderBuffer*());
MOCK_CONST_METHOD0(IsBlockAvailable, bool());
MOCK_CONST_METHOD0(GetRenderBuffer, const RenderBuffer&());
MOCK_CONST_METHOD0(GetDownsampledRenderBuffer, MOCK_CONST_METHOD0(GetDownsampledRenderBuffer,
const DownsampledRenderBuffer&()); const DownsampledRenderBuffer&());
MOCK_CONST_METHOD0(CausalDelay, bool());
private: private:
const RenderBuffer& FakeGetRenderBuffer() const { return render_buffer_; } RenderBuffer* FakeGetRenderBuffer() { return &render_buffer_; }
const DownsampledRenderBuffer& FakeGetDownsampledRenderBuffer() const { const DownsampledRenderBuffer& FakeGetDownsampledRenderBuffer() const {
return downsampled_render_buffer_; return downsampled_render_buffer_;
} }

View File

@ -26,8 +26,9 @@ class MockRenderDelayController : public RenderDelayController {
MOCK_METHOD0(Reset, void()); MOCK_METHOD0(Reset, void());
MOCK_METHOD1(SetDelay, void(size_t render_delay)); MOCK_METHOD1(SetDelay, void(size_t render_delay));
MOCK_METHOD2(GetDelay, MOCK_METHOD2(
size_t(const DownsampledRenderBuffer& render_buffer, GetDelay,
rtc::Optional<size_t>(const DownsampledRenderBuffer& render_buffer,
rtc::ArrayView<const float> capture)); rtc::ArrayView<const float> capture));
MOCK_CONST_METHOD0(AlignmentHeadroomSamples, rtc::Optional<size_t>()); MOCK_CONST_METHOD0(AlignmentHeadroomSamples, rtc::Optional<size_t>());
}; };

View File

@ -64,7 +64,7 @@ class RenderBuffer {
private: private:
const MatrixBuffer* const block_buffer_; const MatrixBuffer* const block_buffer_;
VectorBuffer* spectrum_buffer_; const VectorBuffer* const spectrum_buffer_;
const FftBuffer* const fft_buffer_; const FftBuffer* const fft_buffer_;
const size_t spectral_sums_length_; const size_t spectral_sums_length_;
std::array<float, kFftLengthBy2Plus1> spectral_sums_; std::array<float, kFftLengthBy2Plus1> spectral_sums_;

View File

@ -37,51 +37,109 @@ class RenderDelayBufferImpl final : public RenderDelayBuffer {
void Reset() override; void Reset() override;
BufferingEvent Insert(const std::vector<std::vector<float>>& block) override; BufferingEvent Insert(const std::vector<std::vector<float>>& block) override;
BufferingEvent PrepareCaptureCall() override; BufferingEvent PrepareCaptureProcessing() override;
void SetDelay(size_t delay) override; bool SetDelay(size_t delay) override;
size_t Delay() const override { return delay_; } rtc::Optional<size_t> Delay() const override { return delay_; }
size_t MaxDelay() const override { size_t MaxDelay() const override {
return blocks_.buffer.size() - 1 - kBufferHeadroom; return blocks_.buffer.size() - 1 - kBufferHeadroom;
} }
size_t MaxApiJitter() const override { return max_api_jitter_; } RenderBuffer* GetRenderBuffer() override { return &echo_remover_buffer_; }
const RenderBuffer& GetRenderBuffer() const override {
return echo_remover_buffer_;
}
const DownsampledRenderBuffer& GetDownsampledRenderBuffer() const override { const DownsampledRenderBuffer& GetDownsampledRenderBuffer() const override {
return low_rate_; return low_rate_;
} }
bool CausalDelay() const override;
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 api_call_jitter_blocks_; const EchoCanceller3Config config_;
const size_t min_echo_path_delay_blocks_;
const int sub_block_size_; const int sub_block_size_;
MatrixBuffer blocks_; MatrixBuffer blocks_;
VectorBuffer spectra_; VectorBuffer spectra_;
FftBuffer ffts_; FftBuffer ffts_;
size_t delay_; rtc::Optional<size_t> delay_;
int max_api_jitter_ = 0; rtc::Optional<int> internal_delay_;
int render_surplus_ = 0;
bool first_reset_occurred_ = false;
RenderBuffer echo_remover_buffer_; RenderBuffer echo_remover_buffer_;
DownsampledRenderBuffer low_rate_; DownsampledRenderBuffer low_rate_;
Decimator render_decimator_; Decimator render_decimator_;
const std::vector<std::vector<float>> zero_block_; const std::vector<std::vector<float>> zero_block_;
const Aec3Fft fft_; const Aec3Fft fft_;
size_t capture_call_counter_ = 0;
std::vector<float> render_ds_; std::vector<float> render_ds_;
int render_calls_in_a_row_ = 0;
void UpdateBuffersWithLatestBlock(size_t previous_write); int LowRateBufferOffset() const { return DelayEstimatorOffset(config_) >> 1; }
void IncreaseRead(); int MaxExternalDelayToInternalDelay(size_t delay) const;
void IncreaseInsert(); void ApplyDelay(int delay);
void InsertBlock(const std::vector<std::vector<float>>& block,
int previous_write);
RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(RenderDelayBufferImpl); RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(RenderDelayBufferImpl);
}; };
// Increases the write indices for the render buffers.
void IncreaseWriteIndices(int sub_block_size,
MatrixBuffer* blocks,
VectorBuffer* spectra,
FftBuffer* ffts,
DownsampledRenderBuffer* low_rate) {
low_rate->UpdateWriteIndex(-sub_block_size);
blocks->IncWriteIndex();
spectra->DecWriteIndex();
ffts->DecWriteIndex();
}
// Increases the read indices for the render buffers.
void IncreaseReadIndices(const rtc::Optional<int>& delay,
int sub_block_size,
MatrixBuffer* blocks,
VectorBuffer* spectra,
FftBuffer* ffts,
DownsampledRenderBuffer* low_rate) {
RTC_DCHECK_NE(low_rate->read, low_rate->write);
low_rate->UpdateReadIndex(-sub_block_size);
if (blocks->read != blocks->write) {
blocks->IncReadIndex();
spectra->DecReadIndex();
ffts->DecReadIndex();
} else {
// Only allow underrun for blocks_ when the delay is not set.
RTC_DCHECK(!delay);
}
}
// Checks for a render buffer overrun.
bool RenderOverrun(const MatrixBuffer& b, const DownsampledRenderBuffer& l) {
return l.read == l.write || b.read == b.write;
}
// Checks for a render buffer underrun. If the delay is not specified, only the
// low rate buffer underrun is counted as the delay offset for the other buffers
// is unknown.
bool RenderUnderrun(const rtc::Optional<int>& delay,
const MatrixBuffer& b,
const DownsampledRenderBuffer& l) {
return l.read == l.write || (delay && b.read == b.write);
}
// Computes the latency in the buffer (the number of unread elements).
int BufferLatency(const DownsampledRenderBuffer& l) {
return (l.buffer.size() + l.read - l.write) % l.buffer.size();
}
// Computes the mismatch between the number of render and capture calls based on
// the known offset (achieved during reset) of the low rate buffer.
bool ApiCallSkew(const DownsampledRenderBuffer& low_rate_buffer,
int sub_block_size,
int low_rate_buffer_offset_sub_blocks) {
int latency = BufferLatency(low_rate_buffer);
int skew = abs(low_rate_buffer_offset_sub_blocks * sub_block_size - latency);
int skew_limit = low_rate_buffer_offset_sub_blocks * sub_block_size;
return skew >= skew_limit;
}
int RenderDelayBufferImpl::instance_count_ = 0; int RenderDelayBufferImpl::instance_count_ = 0;
RenderDelayBufferImpl::RenderDelayBufferImpl(const EchoCanceller3Config& config, RenderDelayBufferImpl::RenderDelayBufferImpl(const EchoCanceller3Config& config,
@ -89,8 +147,7 @@ RenderDelayBufferImpl::RenderDelayBufferImpl(const EchoCanceller3Config& config,
: data_dumper_( : data_dumper_(
new ApmDataDumper(rtc::AtomicOps::Increment(&instance_count_))), new ApmDataDumper(rtc::AtomicOps::Increment(&instance_count_))),
optimization_(DetectOptimization()), optimization_(DetectOptimization()),
api_call_jitter_blocks_(config.delay.api_call_jitter_blocks), config_(config),
min_echo_path_delay_blocks_(config.delay.min_echo_path_delay_blocks),
sub_block_size_( sub_block_size_(
static_cast<int>(config.delay.down_sampling_factor > 0 static_cast<int>(config.delay.down_sampling_factor > 0
? kBlockSize / config.delay.down_sampling_factor ? kBlockSize / config.delay.down_sampling_factor
@ -101,7 +158,6 @@ RenderDelayBufferImpl::RenderDelayBufferImpl(const EchoCanceller3Config& config,
kBlockSize), kBlockSize),
spectra_(blocks_.buffer.size(), kFftLengthBy2Plus1), spectra_(blocks_.buffer.size(), kFftLengthBy2Plus1),
ffts_(blocks_.buffer.size()), ffts_(blocks_.buffer.size()),
delay_(min_echo_path_delay_blocks_),
echo_remover_buffer_(kAdaptiveFilterLength, &blocks_, &spectra_, &ffts_), echo_remover_buffer_(kAdaptiveFilterLength, &blocks_, &spectra_, &ffts_),
low_rate_(GetDownSampledBufferSize(config.delay.down_sampling_factor, low_rate_(GetDownSampledBufferSize(config.delay.down_sampling_factor,
config.delay.num_filters)), config.delay.num_filters)),
@ -111,127 +167,153 @@ RenderDelayBufferImpl::RenderDelayBufferImpl(const EchoCanceller3Config& config,
render_ds_(sub_block_size_, 0.f) { render_ds_(sub_block_size_, 0.f) {
RTC_DCHECK_EQ(blocks_.buffer.size(), ffts_.buffer.size()); RTC_DCHECK_EQ(blocks_.buffer.size(), ffts_.buffer.size());
RTC_DCHECK_EQ(spectra_.buffer.size(), ffts_.buffer.size()); RTC_DCHECK_EQ(spectra_.buffer.size(), ffts_.buffer.size());
// Necessary condition to avoid unrecoverable echp due to noncausal alignment.
RTC_DCHECK_EQ(DelayEstimatorOffset(config_), LowRateBufferOffset() * 2);
Reset(); Reset();
first_reset_occurred_ = false;
} }
RenderDelayBufferImpl::~RenderDelayBufferImpl() = default; RenderDelayBufferImpl::~RenderDelayBufferImpl() = default;
// Resets the buffer delays and clears the reported delays.
void RenderDelayBufferImpl::Reset() { void RenderDelayBufferImpl::Reset() {
delay_ = min_echo_path_delay_blocks_; // Pre-fill the low rate buffer (which is used for delay estimation) to add
const int offset1 = std::max<int>( // headroom for the allowed api call jitter.
std::min(api_call_jitter_blocks_, min_echo_path_delay_blocks_), 1); low_rate_.read = low_rate_.OffsetIndex(
const int offset2 = static_cast<int>(delay_ + offset1); low_rate_.write, LowRateBufferOffset() * sub_block_size_);
const int offset3 = offset1 * sub_block_size_;
low_rate_.read = low_rate_.OffsetIndex(low_rate_.write, offset3); // Set the render buffer delays to the default delay.
blocks_.read = blocks_.OffsetIndex(blocks_.write, -offset2); ApplyDelay(config_.delay.default_delay);
spectra_.read = spectra_.OffsetIndex(spectra_.write, offset2);
ffts_.read = ffts_.OffsetIndex(ffts_.write, offset2); // Unset the delays which are set by ApplyConfig.
render_surplus_ = 0; delay_ = rtc::nullopt;
first_reset_occurred_ = true; internal_delay_ = rtc::nullopt;
} }
// Inserts a new block into the render buffers.
RenderDelayBuffer::BufferingEvent 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(), blocks_.buffer[0].size()); // Increase the write indices to where the new blocks should be written.
RTC_DCHECK_EQ(block[0].size(), blocks_.buffer[0][0].size()); const int previous_write = blocks_.write;
BufferingEvent event = BufferingEvent::kNone; IncreaseWriteIndices(sub_block_size_, &blocks_, &spectra_, &ffts_,
&low_rate_);
++render_surplus_; // Allow overrun and do a reset when render overrun occurrs due to more render
if (first_reset_occurred_) { // data being inserted than capture data is received.
++render_calls_in_a_row_; BufferingEvent event = RenderOverrun(blocks_, low_rate_)
max_api_jitter_ = std::max(max_api_jitter_, render_calls_in_a_row_); ? event = BufferingEvent::kRenderOverrun
: BufferingEvent::kNone;
// Insert the new render block into the specified position.
InsertBlock(block, previous_write);
if (event != BufferingEvent::kNone) {
Reset();
} }
const size_t previous_write = blocks_.write;
IncreaseInsert();
if (low_rate_.read == low_rate_.write || blocks_.read == blocks_.write) {
// Render overrun due to more render data being inserted than read. Discard
// the oldest render data.
event = BufferingEvent::kRenderOverrun;
IncreaseRead();
}
for (size_t k = 0; k < block.size(); ++k) {
std::copy(block[k].begin(), block[k].end(),
blocks_.buffer[blocks_.write][k].begin());
}
UpdateBuffersWithLatestBlock(previous_write);
return event; return event;
} }
RenderDelayBuffer::BufferingEvent RenderDelayBufferImpl::PrepareCaptureCall() { // Prepares the render buffers for processing another capture block.
RenderDelayBuffer::BufferingEvent
RenderDelayBufferImpl::PrepareCaptureProcessing() {
BufferingEvent event = BufferingEvent::kNone; BufferingEvent event = BufferingEvent::kNone;
render_calls_in_a_row_ = 0;
if (low_rate_.read == low_rate_.write || blocks_.read == blocks_.write) { if (RenderUnderrun(internal_delay_, blocks_, low_rate_)) {
// Don't increase the read indices if there is a render underrun.
event = BufferingEvent::kRenderUnderrun; event = BufferingEvent::kRenderUnderrun;
} else { } else {
IncreaseRead(); // Increase the read indices in the render buffers to point to the most
} // recent block to use in the capture processing.
--render_surplus_; IncreaseReadIndices(internal_delay_, sub_block_size_, &blocks_, &spectra_,
&ffts_, &low_rate_);
echo_remover_buffer_.UpdateSpectralSum(); // Check for skew in the API calls which, if too large, causes the delay
// estimation to be noncausal. Doing this check after the render indice
if (render_surplus_ >= static_cast<int>(api_call_jitter_blocks_)) { // increase saves one unit of allowed skew. Note that the skew check only
event = BufferingEvent::kApiCallSkew; // should need to be one-sided as one of the skew directions results in an
RTC_LOG(LS_WARNING) << "Api call skew detected at " << capture_call_counter_ // underrun.
<< "."; bool skew = ApiCallSkew(low_rate_, sub_block_size_, LowRateBufferOffset());
event = skew ? BufferingEvent::kApiCallSkew : BufferingEvent::kNone;
}
if (event != BufferingEvent::kNone) {
Reset();
} }
++capture_call_counter_;
return event; return event;
} }
void RenderDelayBufferImpl::SetDelay(size_t delay) { // Sets the delay and returns a bool indicating whether the delay was changed.
if (delay_ == delay) { bool RenderDelayBufferImpl::SetDelay(size_t delay) {
return; if (delay_ && *delay_ == delay) {
return false;
} }
const int delta_delay = static_cast<int>(delay_) - static_cast<int>(delay);
delay_ = delay; delay_ = delay;
if (delay_ > MaxDelay()) {
delay_ = std::min(MaxDelay(), delay); // Compute the internal delay and limit the delay to the allowed range.
RTC_NOTREACHED(); int internal_delay = MaxExternalDelayToInternalDelay(*delay_);
internal_delay_ =
std::min(MaxDelay(), static_cast<size_t>(std::max(internal_delay, 0)));
// Apply the delay to the buffers.
ApplyDelay(*internal_delay_);
return true;
} }
// Recompute the read indices according to the set delay. // Returns whether the specified delay is causal.
blocks_.UpdateReadIndex(delta_delay); bool RenderDelayBufferImpl::CausalDelay() const {
spectra_.UpdateReadIndex(-delta_delay); return !internal_delay_ ||
ffts_.UpdateReadIndex(-delta_delay); *internal_delay_ >=
static_cast<int>(config_.delay.min_echo_path_delay_blocks);
} }
void RenderDelayBufferImpl::UpdateBuffersWithLatestBlock( // Maps the externally computed delay to the delay used internally.
size_t previous_write) { int RenderDelayBufferImpl::MaxExternalDelayToInternalDelay(
render_decimator_.Decimate(blocks_.buffer[blocks_.write][0], render_ds_); size_t external_delay_blocks) const {
std::copy(render_ds_.rbegin(), render_ds_.rend(), const int latency = BufferLatency(low_rate_);
low_rate_.buffer.begin() + low_rate_.write); RTC_DCHECK_LT(0, sub_block_size_);
RTC_DCHECK_EQ(0, latency % sub_block_size_);
int latency_blocks = latency / sub_block_size_;
return latency_blocks + static_cast<int>(external_delay_blocks) -
DelayEstimatorOffset(config_);
}
fft_.PaddedFft(blocks_.buffer[blocks_.write][0], // Set the read indices according to the delay.
blocks_.buffer[previous_write][0], &ffts_.buffer[ffts_.write]); void RenderDelayBufferImpl::ApplyDelay(int delay) {
blocks_.read = blocks_.OffsetIndex(blocks_.write, -delay);
spectra_.read = spectra_.OffsetIndex(spectra_.write, delay);
ffts_.read = ffts_.OffsetIndex(ffts_.write, delay);
}
ffts_.buffer[ffts_.write].Spectrum(optimization_, // Inserts a block into the render buffers.
spectra_.buffer[spectra_.write]); void RenderDelayBufferImpl::InsertBlock(
}; const std::vector<std::vector<float>>& block,
int previous_write) {
auto& b = blocks_;
auto& lr = low_rate_;
auto& ds = render_ds_;
auto& f = ffts_;
auto& s = spectra_;
RTC_DCHECK_EQ(block.size(), b.buffer[b.write].size());
for (size_t k = 0; k < block.size(); ++k) {
RTC_DCHECK_EQ(block[k].size(), b.buffer[b.write][k].size());
std::copy(block[k].begin(), block[k].end(), b.buffer[b.write][k].begin());
}
void RenderDelayBufferImpl::IncreaseRead() { render_decimator_.Decimate(block[0], ds);
low_rate_.UpdateReadIndex(-sub_block_size_); std::copy(ds.rbegin(), ds.rend(), lr.buffer.begin() + lr.write);
blocks_.IncReadIndex(); fft_.PaddedFft(block[0], b.buffer[previous_write][0], &f.buffer[f.write]);
spectra_.DecReadIndex(); f.buffer[f.write].Spectrum(optimization_, s.buffer[s.write]);
ffts_.DecReadIndex(); }
};
void RenderDelayBufferImpl::IncreaseInsert() {
low_rate_.UpdateWriteIndex(-sub_block_size_);
blocks_.IncWriteIndex();
spectra_.DecWriteIndex();
ffts_.DecWriteIndex();
};
} // namespace } // namespace
int RenderDelayBuffer::RenderDelayBuffer::DelayEstimatorOffset(
const EchoCanceller3Config& config) {
return config.delay.api_call_jitter_blocks * 2;
}
RenderDelayBuffer* RenderDelayBuffer::Create(const EchoCanceller3Config& config, RenderDelayBuffer* RenderDelayBuffer::Create(const EchoCanceller3Config& config,
size_t num_bands) { size_t num_bands) {
return new RenderDelayBufferImpl(config, num_bands); return new RenderDelayBufferImpl(config, num_bands);

View File

@ -49,25 +49,29 @@ class RenderDelayBuffer {
// Updates the buffers one step based on the specified buffer delay. Returns // Updates the buffers one step based on the specified buffer delay. Returns
// an enum indicating whether there was a special event that occurred. // an enum indicating whether there was a special event that occurred.
virtual BufferingEvent PrepareCaptureCall() = 0; virtual BufferingEvent PrepareCaptureProcessing() = 0;
// Sets the buffer delay. // Sets the buffer delay and returns a bool indicating whether the delay
virtual void SetDelay(size_t delay) = 0; // changed.
virtual bool SetDelay(size_t delay) = 0;
// Gets the buffer delay. // Gets the buffer delay.
virtual size_t Delay() const = 0; virtual rtc::Optional<size_t> Delay() const = 0;
// Gets the buffer delay. // Gets the buffer delay.
virtual size_t MaxDelay() const = 0; 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 RenderBuffer* GetRenderBuffer() = 0;
// Returns the downsampled render buffer. // Returns the downsampled render buffer.
virtual const DownsampledRenderBuffer& GetDownsampledRenderBuffer() const = 0; virtual const DownsampledRenderBuffer& GetDownsampledRenderBuffer() const = 0;
// Returns whether the current delay is noncausal.
virtual bool CausalDelay() const = 0;
// Returns the maximum non calusal offset that can occur in the delay buffer.
static int DelayEstimatorOffset(const EchoCanceller3Config& config);
}; };
} // namespace webrtc } // namespace webrtc

View File

@ -45,12 +45,16 @@ TEST(RenderDelayBuffer, BufferOverflow) {
EXPECT_EQ(RenderDelayBuffer::BufferingEvent::kNone, EXPECT_EQ(RenderDelayBuffer::BufferingEvent::kNone,
delay_buffer->Insert(block_to_insert)); delay_buffer->Insert(block_to_insert));
} }
bool overrun_occurred = false;
for (size_t k = 0; k < 1000; ++k) { for (size_t k = 0; k < 1000; ++k) {
RenderDelayBuffer::BufferingEvent event =
delay_buffer->Insert(block_to_insert); delay_buffer->Insert(block_to_insert);
overrun_occurred =
overrun_occurred ||
RenderDelayBuffer::BufferingEvent::kRenderOverrun == event;
} }
EXPECT_EQ(RenderDelayBuffer::BufferingEvent::kRenderOverrun, EXPECT_TRUE(overrun_occurred);
delay_buffer->Insert(block_to_insert));
} }
} }
@ -63,7 +67,7 @@ TEST(RenderDelayBuffer, AvailableBlock) {
kNumBands, std::vector<float>(kBlockSize, 1.f)); kNumBands, std::vector<float>(kBlockSize, 1.f));
EXPECT_EQ(RenderDelayBuffer::BufferingEvent::kNone, EXPECT_EQ(RenderDelayBuffer::BufferingEvent::kNone,
delay_buffer->Insert(input_block)); delay_buffer->Insert(input_block));
delay_buffer->PrepareCaptureCall(); delay_buffer->PrepareCaptureProcessing();
} }
// Verifies the SetDelay method. // Verifies the SetDelay method.
@ -71,11 +75,12 @@ TEST(RenderDelayBuffer, SetDelay) {
EchoCanceller3Config config; EchoCanceller3Config config;
std::unique_ptr<RenderDelayBuffer> delay_buffer( std::unique_ptr<RenderDelayBuffer> delay_buffer(
RenderDelayBuffer::Create(config, 1)); RenderDelayBuffer::Create(config, 1));
EXPECT_EQ(config.delay.min_echo_path_delay_blocks, delay_buffer->Delay()); ASSERT_FALSE(delay_buffer->Delay());
for (size_t delay = config.delay.min_echo_path_delay_blocks + 1; delay < 20; for (size_t delay = config.delay.min_echo_path_delay_blocks + 1; delay < 20;
++delay) { ++delay) {
delay_buffer->SetDelay(delay); delay_buffer->SetDelay(delay);
EXPECT_EQ(delay, delay_buffer->Delay()); ASSERT_TRUE(delay_buffer->Delay());
EXPECT_EQ(delay, *delay_buffer->Delay());
} }
} }

View File

@ -28,11 +28,12 @@ namespace {
class RenderDelayControllerImpl final : public RenderDelayController { class RenderDelayControllerImpl final : public RenderDelayController {
public: public:
RenderDelayControllerImpl(const EchoCanceller3Config& config, RenderDelayControllerImpl(const EchoCanceller3Config& config,
int non_causal_offset,
int sample_rate_hz); int sample_rate_hz);
~RenderDelayControllerImpl() override; ~RenderDelayControllerImpl() override;
void Reset() override; void Reset() override;
void SetDelay(size_t render_delay) override; void SetDelay(size_t render_delay) override;
size_t GetDelay(const DownsampledRenderBuffer& render_buffer, rtc::Optional<size_t> GetDelay(const DownsampledRenderBuffer& render_buffer,
rtc::ArrayView<const float> capture) override; rtc::ArrayView<const float> capture) override;
rtc::Optional<size_t> AlignmentHeadroomSamples() const override { rtc::Optional<size_t> AlignmentHeadroomSamples() const override {
return headroom_samples_; return headroom_samples_;
@ -41,32 +42,30 @@ 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_; rtc::Optional<size_t> delay_;
const size_t default_delay_;
size_t delay_;
EchoPathDelayEstimator delay_estimator_; EchoPathDelayEstimator delay_estimator_;
size_t blocks_since_last_delay_estimate_ = 300000;
int echo_path_delay_samples_;
size_t align_call_counter_ = 0; size_t align_call_counter_ = 0;
rtc::Optional<size_t> headroom_samples_; rtc::Optional<size_t> headroom_samples_;
std::vector<float> capture_delay_buffer_; std::vector<float> delay_buf_;
int capture_delay_buffer_index_ = 0; int delay_buf_index_ = 0;
RenderDelayControllerMetrics metrics_; RenderDelayControllerMetrics metrics_;
RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(RenderDelayControllerImpl); RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(RenderDelayControllerImpl);
}; };
size_t ComputeNewBufferDelay(size_t current_delay, size_t ComputeNewBufferDelay(rtc::Optional<size_t> current_delay,
size_t echo_path_delay_samples) { size_t delay_samples) {
// The below division is not exact and the truncation is intended. // The below division is not exact and the truncation is intended.
const int echo_path_delay_blocks = echo_path_delay_samples / kBlockSize; const int echo_path_delay_blocks = delay_samples >> kBlockSizeLog2;
constexpr int kDelayHeadroomBlocks = 1; constexpr int kDelayHeadroomBlocks = 1;
// Compute the buffer delay increase required to achieve the desired latency. // Compute the buffer delay increase required to achieve the desired latency.
size_t new_delay = std::max(echo_path_delay_blocks - kDelayHeadroomBlocks, 0); size_t new_delay = std::max(echo_path_delay_blocks - kDelayHeadroomBlocks, 0);
// Add hysteresis. // Add hysteresis.
if (new_delay == current_delay + 1) { if (current_delay) {
new_delay = current_delay; if (new_delay == *current_delay + 1) {
new_delay = *current_delay;
}
} }
return new_delay; return new_delay;
@ -76,32 +75,24 @@ int RenderDelayControllerImpl::instance_count_ = 0;
RenderDelayControllerImpl::RenderDelayControllerImpl( RenderDelayControllerImpl::RenderDelayControllerImpl(
const EchoCanceller3Config& config, const EchoCanceller3Config& config,
int non_causal_offset,
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_(
std::max(config.delay.default_delay, min_echo_path_delay_)),
delay_(default_delay_),
delay_estimator_(data_dumper_.get(), config), delay_estimator_(data_dumper_.get(), config),
echo_path_delay_samples_(default_delay_ * kBlockSize), delay_buf_(kBlockSize * non_causal_offset, 0.f) {
capture_delay_buffer_(
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()); delay_buf_.size());
} }
RenderDelayControllerImpl::~RenderDelayControllerImpl() = default; RenderDelayControllerImpl::~RenderDelayControllerImpl() = default;
void RenderDelayControllerImpl::Reset() { void RenderDelayControllerImpl::Reset() {
delay_ = default_delay_; delay_ = rtc::nullopt;
blocks_since_last_delay_estimate_ = 300000;
echo_path_delay_samples_ = delay_ * kBlockSize;
align_call_counter_ = 0; align_call_counter_ = 0;
headroom_samples_ = rtc::nullopt; headroom_samples_ = rtc::nullopt;
std::fill(capture_delay_buffer_.begin(), capture_delay_buffer_.end(), 0.f); std::fill(delay_buf_.begin(), delay_buf_.end(), 0.f);
delay_estimator_.Reset(); delay_estimator_.Reset();
} }
@ -114,60 +105,43 @@ void RenderDelayControllerImpl::SetDelay(size_t render_delay) {
} }
} }
size_t RenderDelayControllerImpl::GetDelay( rtc::Optional<size_t> RenderDelayControllerImpl::GetDelay(
const DownsampledRenderBuffer& render_buffer, const DownsampledRenderBuffer& render_buffer,
rtc::ArrayView<const float> capture) { rtc::ArrayView<const float> capture) {
RTC_DCHECK_EQ(kBlockSize, capture.size()); RTC_DCHECK_EQ(kBlockSize, capture.size());
++align_call_counter_; ++align_call_counter_;
// Estimate the delay with a delayed capture signal in order to catch // Estimate the delay with a delayed capture.
// noncausal delays. RTC_DCHECK_LT(delay_buf_index_ + kBlockSize - 1, delay_buf_.size());
RTC_DCHECK_LT(capture_delay_buffer_index_ + kBlockSize - 1, rtc::ArrayView<const float> capture_delayed(&delay_buf_[delay_buf_index_],
capture_delay_buffer_.size()); kBlockSize);
const rtc::Optional<size_t> echo_path_delay_samples_shifted = auto delay_samples =
delay_estimator_.EstimateDelay( delay_estimator_.EstimateDelay(render_buffer, capture_delayed);
render_buffer,
rtc::ArrayView<const float>(
&capture_delay_buffer_[capture_delay_buffer_index_], kBlockSize));
std::copy(capture.begin(), capture.end(), std::copy(capture.begin(), capture.end(),
capture_delay_buffer_.begin() + capture_delay_buffer_index_); delay_buf_.begin() + delay_buf_index_);
capture_delay_buffer_index_ = delay_buf_index_ = (delay_buf_index_ + kBlockSize) % delay_buf_.size();
(capture_delay_buffer_index_ + kBlockSize) % capture_delay_buffer_.size();
if (echo_path_delay_samples_shifted) {
blocks_since_last_delay_estimate_ = 0;
// Correct for the capture signal delay.
const int echo_path_delay_samples_corrected =
static_cast<int>(*echo_path_delay_samples_shifted) -
static_cast<int>(capture_delay_buffer_.size());
echo_path_delay_samples_ = std::max(0, echo_path_delay_samples_corrected);
if (delay_samples) {
// Compute and set new render delay buffer delay. // Compute and set new render delay buffer delay.
const size_t new_delay =
ComputeNewBufferDelay(delay_, echo_path_delay_samples_);
if (align_call_counter_ > kNumBlocksPerSecond) { if (align_call_counter_ > kNumBlocksPerSecond) {
delay_ = new_delay; delay_ = ComputeNewBufferDelay(delay_, static_cast<int>(*delay_samples));
// Update render delay buffer headroom. // Update render delay buffer headroom.
if (echo_path_delay_samples_corrected >= 0) { const int headroom =
const int headroom = echo_path_delay_samples_ - delay_ * kBlockSize; static_cast<int>(*delay_samples) - *delay_ * kBlockSize;
RTC_DCHECK_LE(0, headroom); RTC_DCHECK_LE(0, headroom);
headroom_samples_ = headroom; headroom_samples_ = headroom;
} else {
headroom_samples_ = rtc::nullopt;
}
} }
metrics_.Update(echo_path_delay_samples_, delay_); metrics_.Update(static_cast<int>(*delay_samples), delay_ ? *delay_ : 0);
} else { } else {
metrics_.Update(rtc::nullopt, delay_); metrics_.Update(rtc::nullopt, delay_ ? *delay_ : 0);
} }
data_dumper_->DumpRaw("aec3_render_delay_controller_delay", 1, data_dumper_->DumpRaw("aec3_render_delay_controller_delay",
&echo_path_delay_samples_); delay_samples ? *delay_samples : 0);
data_dumper_->DumpRaw("aec3_render_delay_controller_buffer_delay", delay_); data_dumper_->DumpRaw("aec3_render_delay_controller_buffer_delay",
delay_ ? *delay_ : 0);
return delay_; return delay_;
} }
@ -176,8 +150,10 @@ size_t RenderDelayControllerImpl::GetDelay(
RenderDelayController* RenderDelayController::Create( RenderDelayController* RenderDelayController::Create(
const EchoCanceller3Config& config, const EchoCanceller3Config& config,
int non_causal_offset,
int sample_rate_hz) { int sample_rate_hz) {
return new RenderDelayControllerImpl(config, sample_rate_hz); return new RenderDelayControllerImpl(config, non_causal_offset,
sample_rate_hz);
} }
} // namespace webrtc } // namespace webrtc

View File

@ -24,6 +24,7 @@ namespace webrtc {
class RenderDelayController { class RenderDelayController {
public: public:
static RenderDelayController* Create(const EchoCanceller3Config& config, static RenderDelayController* Create(const EchoCanceller3Config& config,
int non_causal_offset,
int sample_rate_hz); int sample_rate_hz);
virtual ~RenderDelayController() = default; virtual ~RenderDelayController() = default;
@ -34,7 +35,8 @@ class RenderDelayController {
virtual void SetDelay(size_t render_delay) = 0; virtual void SetDelay(size_t render_delay) = 0;
// Aligns the render buffer content with the capture signal. // Aligns the render buffer content with the capture signal.
virtual size_t GetDelay(const DownsampledRenderBuffer& render_buffer, virtual rtc::Optional<size_t> GetDelay(
const DownsampledRenderBuffer& render_buffer,
rtc::ArrayView<const float> capture) = 0; rtc::ArrayView<const float> capture) = 0;
// Returns an approximate value for the headroom in the buffer alignment. // Returns an approximate value for the headroom in the buffer alignment.

View File

@ -58,7 +58,8 @@ TEST(RenderDelayController, NoRenderSignal) {
std::unique_ptr<RenderDelayBuffer> delay_buffer( std::unique_ptr<RenderDelayBuffer> delay_buffer(
RenderDelayBuffer::Create(config, NumBandsForRate(rate))); RenderDelayBuffer::Create(config, NumBandsForRate(rate)));
std::unique_ptr<RenderDelayController> delay_controller( std::unique_ptr<RenderDelayController> delay_controller(
RenderDelayController::Create(config, rate)); RenderDelayController::Create(
config, RenderDelayBuffer::DelayEstimatorOffset(config), rate));
for (size_t k = 0; k < 100; ++k) { for (size_t k = 0; k < 100; ++k) {
EXPECT_EQ(config.delay.min_echo_path_delay_blocks, EXPECT_EQ(config.delay.min_echo_path_delay_blocks,
delay_controller->GetDelay( delay_controller->GetDelay(
@ -72,7 +73,7 @@ TEST(RenderDelayController, NoRenderSignal) {
// Verifies the basic API call sequence. // Verifies the basic API call sequence.
TEST(RenderDelayController, BasicApiCalls) { TEST(RenderDelayController, BasicApiCalls) {
std::vector<float> capture_block(kBlockSize, 0.f); std::vector<float> capture_block(kBlockSize, 0.f);
size_t delay_blocks = 0; rtc::Optional<size_t> delay_blocks = 0;
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) {
@ -85,13 +86,17 @@ TEST(RenderDelayController, BasicApiCalls) {
std::unique_ptr<RenderDelayBuffer> render_delay_buffer( std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
RenderDelayBuffer::Create(config, NumBandsForRate(rate))); RenderDelayBuffer::Create(config, NumBandsForRate(rate)));
std::unique_ptr<RenderDelayController> delay_controller( std::unique_ptr<RenderDelayController> delay_controller(
RenderDelayController::Create(EchoCanceller3Config(), rate)); RenderDelayController::Create(
EchoCanceller3Config(),
RenderDelayBuffer::DelayEstimatorOffset(config), 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->PrepareCaptureCall(); render_delay_buffer->PrepareCaptureProcessing();
delay_blocks = delay_controller->GetDelay( delay_blocks = delay_controller->GetDelay(
render_delay_buffer->GetDownsampledRenderBuffer(), capture_block); render_delay_buffer->GetDownsampledRenderBuffer(), capture_block);
} }
EXPECT_TRUE(delay_blocks);
EXPECT_FALSE(delay_controller->AlignmentHeadroomSamples()); EXPECT_FALSE(delay_controller->AlignmentHeadroomSamples());
EXPECT_EQ(config.delay.min_echo_path_delay_blocks, delay_blocks); EXPECT_EQ(config.delay.min_echo_path_delay_blocks, delay_blocks);
} }
@ -104,7 +109,6 @@ TEST(RenderDelayController, BasicApiCalls) {
TEST(RenderDelayController, Alignment) { TEST(RenderDelayController, Alignment) {
Random random_generator(42U); Random random_generator(42U);
std::vector<float> capture_block(kBlockSize, 0.f); std::vector<float> capture_block(kBlockSize, 0.f);
size_t delay_blocks = 0;
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) {
@ -117,21 +121,25 @@ TEST(RenderDelayController, Alignment) {
NumBandsForRate(rate), std::vector<float>(kBlockSize, 0.f)); NumBandsForRate(rate), std::vector<float>(kBlockSize, 0.f));
for (size_t delay_samples : {15, 50, 150, 200, 800, 4000}) { for (size_t delay_samples : {15, 50, 150, 200, 800, 4000}) {
rtc::Optional<size_t> delay_blocks;
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(config, NumBandsForRate(rate))); RenderDelayBuffer::Create(config, NumBandsForRate(rate)));
std::unique_ptr<RenderDelayController> delay_controller( std::unique_ptr<RenderDelayController> delay_controller(
RenderDelayController::Create(config, rate)); RenderDelayController::Create(
config, RenderDelayBuffer::DelayEstimatorOffset(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->PrepareCaptureCall(); render_delay_buffer->PrepareCaptureProcessing();
delay_blocks = delay_controller->GetDelay( delay_blocks = delay_controller->GetDelay(
render_delay_buffer->GetDownsampledRenderBuffer(), render_delay_buffer->GetDownsampledRenderBuffer(),
capture_block); capture_block);
} }
ASSERT_TRUE(!!delay_blocks);
constexpr int kDelayHeadroomBlocks = 1; constexpr int kDelayHeadroomBlocks = 1;
size_t expected_delay_blocks = size_t expected_delay_blocks =
@ -143,7 +151,7 @@ TEST(RenderDelayController, Alignment) {
const rtc::Optional<size_t> headroom_samples = const rtc::Optional<size_t> headroom_samples =
delay_controller->AlignmentHeadroomSamples(); delay_controller->AlignmentHeadroomSamples();
ASSERT_TRUE(headroom_samples); ASSERT_TRUE(headroom_samples);
EXPECT_NEAR(delay_samples - delay_blocks * kBlockSize, EXPECT_NEAR(delay_samples - *delay_blocks * kBlockSize,
*headroom_samples, 4); *headroom_samples, 4);
} }
} }
@ -155,7 +163,6 @@ TEST(RenderDelayController, Alignment) {
// delays. // delays.
TEST(RenderDelayController, NonCausalAlignment) { TEST(RenderDelayController, NonCausalAlignment) {
Random random_generator(42U); Random random_generator(42U);
size_t delay_blocks = 0;
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) {
@ -169,24 +176,27 @@ TEST(RenderDelayController, NonCausalAlignment) {
NumBandsForRate(rate), std::vector<float>(kBlockSize, 0.f)); NumBandsForRate(rate), std::vector<float>(kBlockSize, 0.f));
for (int delay_samples : {-15, -50, -150, -200}) { for (int delay_samples : {-15, -50, -150, -200}) {
rtc::Optional<size_t> delay_blocks;
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(config, NumBandsForRate(rate))); RenderDelayBuffer::Create(config, NumBandsForRate(rate)));
std::unique_ptr<RenderDelayController> delay_controller( std::unique_ptr<RenderDelayController> delay_controller(
RenderDelayController::Create(EchoCanceller3Config(), rate)); RenderDelayController::Create(
EchoCanceller3Config(),
RenderDelayBuffer::DelayEstimatorOffset(config), rate));
DelayBuffer<float> signal_delay_buffer(-delay_samples); DelayBuffer<float> signal_delay_buffer(-delay_samples);
for (int k = 0; for (int k = 0;
k < (400 - delay_samples / static_cast<int>(kBlockSize)); ++k) { k < (400 - delay_samples / static_cast<int>(kBlockSize)); ++k) {
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->PrepareCaptureCall(); render_delay_buffer->PrepareCaptureProcessing();
delay_blocks = delay_controller->GetDelay( delay_blocks = delay_controller->GetDelay(
render_delay_buffer->GetDownsampledRenderBuffer(), render_delay_buffer->GetDownsampledRenderBuffer(),
capture_block[0]); capture_block[0]);
} }
EXPECT_EQ(0u, delay_blocks); ASSERT_FALSE(delay_blocks);
const rtc::Optional<size_t> headroom_samples = const rtc::Optional<size_t> headroom_samples =
delay_controller->AlignmentHeadroomSamples(); delay_controller->AlignmentHeadroomSamples();
@ -212,12 +222,14 @@ TEST(RenderDelayController, AlignmentWithJitter) {
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));
for (size_t delay_samples : {15, 50, 300, 800}) { for (size_t delay_samples : {15, 50, 300, 800}) {
size_t delay_blocks = 0; rtc::Optional<size_t> delay_blocks;
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(config, NumBandsForRate(rate))); RenderDelayBuffer::Create(config, NumBandsForRate(rate)));
std::unique_ptr<RenderDelayController> delay_controller( std::unique_ptr<RenderDelayController> delay_controller(
RenderDelayController::Create(config, rate)); RenderDelayController::Create(
config, RenderDelayBuffer::DelayEstimatorOffset(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) /
config.delay.api_call_jitter_blocks + config.delay.api_call_jitter_blocks +
@ -233,7 +245,7 @@ TEST(RenderDelayController, AlignmentWithJitter) {
} }
for (size_t k = 0; k < (config.delay.api_call_jitter_blocks - 1); for (size_t k = 0; k < (config.delay.api_call_jitter_blocks - 1);
++k) { ++k) {
render_delay_buffer->PrepareCaptureCall(); render_delay_buffer->PrepareCaptureProcessing();
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]);
@ -248,12 +260,13 @@ TEST(RenderDelayController, AlignmentWithJitter) {
expected_delay_blocks = 0; expected_delay_blocks = 0;
} }
EXPECT_EQ(expected_delay_blocks, delay_blocks); ASSERT_TRUE(delay_blocks);
EXPECT_EQ(expected_delay_blocks, *delay_blocks);
const rtc::Optional<size_t> headroom_samples = const rtc::Optional<size_t> headroom_samples =
delay_controller->AlignmentHeadroomSamples(); delay_controller->AlignmentHeadroomSamples();
ASSERT_TRUE(headroom_samples); ASSERT_TRUE(headroom_samples);
EXPECT_NEAR(delay_samples - delay_blocks * kBlockSize, EXPECT_NEAR(delay_samples - *delay_blocks * kBlockSize,
*headroom_samples, 4); *headroom_samples, 4);
} }
} }
@ -277,7 +290,8 @@ TEST(RenderDelayController, InitialHeadroom) {
RenderDelayBuffer::Create(config, NumBandsForRate(rate))); RenderDelayBuffer::Create(config, NumBandsForRate(rate)));
std::unique_ptr<RenderDelayController> delay_controller( std::unique_ptr<RenderDelayController> delay_controller(
RenderDelayController::Create(config, rate)); RenderDelayController::Create(
config, RenderDelayBuffer::DelayEstimatorOffset(config), rate));
EXPECT_FALSE(delay_controller->AlignmentHeadroomSamples()); EXPECT_FALSE(delay_controller->AlignmentHeadroomSamples());
} }
} }
@ -296,7 +310,9 @@ TEST(RenderDelayController, WrongCaptureSize) {
RenderDelayBuffer::Create(config, NumBandsForRate(rate))); RenderDelayBuffer::Create(config, NumBandsForRate(rate)));
EXPECT_DEATH( EXPECT_DEATH(
std::unique_ptr<RenderDelayController>( std::unique_ptr<RenderDelayController>(
RenderDelayController::Create(EchoCanceller3Config(), rate)) RenderDelayController::Create(
EchoCanceller3Config(),
RenderDelayBuffer::DelayEstimatorOffset(config), rate))
->GetDelay(render_delay_buffer->GetDownsampledRenderBuffer(), ->GetDelay(render_delay_buffer->GetDownsampledRenderBuffer(),
block), block),
""); "");
@ -313,8 +329,9 @@ TEST(RenderDelayController, DISABLED_WrongSampleRate) {
std::unique_ptr<RenderDelayBuffer> render_delay_buffer( std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
RenderDelayBuffer::Create(config, NumBandsForRate(rate))); RenderDelayBuffer::Create(config, NumBandsForRate(rate)));
EXPECT_DEATH( EXPECT_DEATH(
std::unique_ptr<RenderDelayController>( std::unique_ptr<RenderDelayController>(RenderDelayController::Create(
RenderDelayController::Create(EchoCanceller3Config(), rate)), EchoCanceller3Config(),
RenderDelayBuffer::DelayEstimatorOffset(config), rate)),
""); "");
} }
} }

View File

@ -70,9 +70,9 @@ TEST(RenderSignalAnalyzer, NoFalseDetectionOfNarrowBands) {
if (k == 0) { if (k == 0) {
render_delay_buffer->Reset(); render_delay_buffer->Reset();
} }
render_delay_buffer->PrepareCaptureCall(); render_delay_buffer->PrepareCaptureProcessing();
analyzer.Update(render_delay_buffer->GetRenderBuffer(), analyzer.Update(*render_delay_buffer->GetRenderBuffer(),
rtc::Optional<size_t>(0)); rtc::Optional<size_t>(0));
} }
@ -109,9 +109,9 @@ TEST(RenderSignalAnalyzer, NarrowBandDetection) {
if (k == 0) { if (k == 0) {
render_delay_buffer->Reset(); render_delay_buffer->Reset();
} }
render_delay_buffer->PrepareCaptureCall(); render_delay_buffer->PrepareCaptureProcessing();
analyzer.Update(render_delay_buffer->GetRenderBuffer(), analyzer.Update(*render_delay_buffer->GetRenderBuffer(),
known_delay ? rtc::Optional<size_t>(0) : rtc::nullopt); known_delay ? rtc::Optional<size_t>(0) : rtc::nullopt);
} }
}; };

View File

@ -32,7 +32,7 @@ TEST(ResidualEchoEstimator, NullResidualEchoPowerOutput) {
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_delay_buffer->GetRenderBuffer(), .Estimate(aec_state, *render_delay_buffer->GetRenderBuffer(),
S2_linear, Y2, nullptr), S2_linear, Y2, nullptr),
""); "");
} }
@ -89,13 +89,13 @@ TEST(ResidualEchoEstimator, DISABLED_BasicTest) {
if (k == 0) { if (k == 0) {
render_delay_buffer->Reset(); render_delay_buffer->Reset();
} }
render_delay_buffer->PrepareCaptureCall(); render_delay_buffer->PrepareCaptureProcessing();
aec_state.HandleEchoPathChange(echo_path_variability); aec_state.HandleEchoPathChange(echo_path_variability);
aec_state.Update(H2, h, true, 2, render_delay_buffer->GetRenderBuffer(), aec_state.Update(H2, h, true, 2, *render_delay_buffer->GetRenderBuffer(),
E2_main, Y2, x[0], s, false); E2_main, Y2, x[0], s, false);
estimator.Estimate(aec_state, render_delay_buffer->GetRenderBuffer(), estimator.Estimate(aec_state, *render_delay_buffer->GetRenderBuffer(),
S2_linear, Y2, &R2); S2_linear, Y2, &R2);
} }
std::for_each(R2.begin(), R2.end(), std::for_each(R2.begin(), R2.end(),

View File

@ -42,6 +42,7 @@ void RunFilterUpdateTest(int num_blocks_to_process,
EchoCanceller3Config config; EchoCanceller3Config config;
config.delay.min_echo_path_delay_blocks = 0; config.delay.min_echo_path_delay_blocks = 0;
config.delay.default_delay = 1;
std::unique_ptr<RenderDelayBuffer> render_delay_buffer( std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
RenderDelayBuffer::Create(config, 3)); RenderDelayBuffer::Create(config, 3));
@ -76,12 +77,13 @@ void RunFilterUpdateTest(int num_blocks_to_process,
if (k == 0) { if (k == 0) {
render_delay_buffer->Reset(); render_delay_buffer->Reset();
} }
render_delay_buffer->PrepareCaptureCall(); render_delay_buffer->PrepareCaptureProcessing();
render_delay_buffer->GetRenderBuffer()->UpdateSpectralSum();
render_signal_analyzer.Update(render_delay_buffer->GetRenderBuffer(), render_signal_analyzer.Update(*render_delay_buffer->GetRenderBuffer(),
delay_samples / kBlockSize); delay_samples / kBlockSize);
shadow_filter.Filter(render_delay_buffer->GetRenderBuffer(), &S); 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(),
@ -90,10 +92,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_delay_buffer->GetRenderBuffer(), shadow_gain.Compute(*render_delay_buffer->GetRenderBuffer(),
render_signal_analyzer, E_shadow, render_signal_analyzer, E_shadow,
shadow_filter.SizePartitions(), saturation, &G); shadow_filter.SizePartitions(), saturation, &G);
shadow_filter.Adapt(render_delay_buffer->GetRenderBuffer(), 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());

View File

@ -35,6 +35,7 @@ float RunSubtractorTest(int num_blocks_to_process,
SubtractorOutput output; SubtractorOutput output;
EchoCanceller3Config config; EchoCanceller3Config config;
config.delay.min_echo_path_delay_blocks = 0; config.delay.min_echo_path_delay_blocks = 0;
config.delay.default_delay = 1;
std::unique_ptr<RenderDelayBuffer> render_delay_buffer( std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
RenderDelayBuffer::Create(config, 3)); RenderDelayBuffer::Create(config, 3));
RenderSignalAnalyzer render_signal_analyzer; RenderSignalAnalyzer render_signal_analyzer;
@ -61,8 +62,9 @@ float RunSubtractorTest(int num_blocks_to_process,
if (k == 0) { if (k == 0) {
render_delay_buffer->Reset(); render_delay_buffer->Reset();
} }
render_delay_buffer->PrepareCaptureCall(); render_delay_buffer->PrepareCaptureProcessing();
render_signal_analyzer.Update(render_delay_buffer->GetRenderBuffer(), render_delay_buffer->GetRenderBuffer()->UpdateSpectralSum();
render_signal_analyzer.Update(*render_delay_buffer->GetRenderBuffer(),
aec_state.FilterDelay()); aec_state.FilterDelay());
// Handle echo path changes. // Handle echo path changes.
@ -73,7 +75,7 @@ float RunSubtractorTest(int num_blocks_to_process,
true, EchoPathVariability::DelayAdjustment::kNewDetectedDelay, true, EchoPathVariability::DelayAdjustment::kNewDetectedDelay,
false)); false));
} }
subtractor.Process(render_delay_buffer->GetRenderBuffer(), y, subtractor.Process(*render_delay_buffer->GetRenderBuffer(), y,
render_signal_analyzer, aec_state, &output); render_signal_analyzer, aec_state, &output);
aec_state.HandleEchoPathChange(EchoPathVariability( aec_state.HandleEchoPathChange(EchoPathVariability(
@ -82,7 +84,7 @@ float RunSubtractorTest(int num_blocks_to_process,
subtractor.FilterImpulseResponse(), subtractor.FilterImpulseResponse(),
subtractor.ConvergedFilter(), subtractor.ConvergedFilter(),
rtc::Optional<size_t>(delay_samples / kBlockSize), rtc::Optional<size_t>(delay_samples / kBlockSize),
render_delay_buffer->GetRenderBuffer(), E2_main, Y2, x[0], *render_delay_buffer->GetRenderBuffer(), E2_main, Y2, x[0],
output.s_main, false); output.s_main, false);
} }
@ -122,7 +124,7 @@ TEST(Subtractor, DISABLED_NullOutput) {
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_delay_buffer->GetRenderBuffer(), y, EXPECT_DEATH(subtractor.Process(*render_delay_buffer->GetRenderBuffer(), y,
render_signal_analyzer, render_signal_analyzer,
AecState(EchoCanceller3Config{}), nullptr), AecState(EchoCanceller3Config{}), nullptr),
""); "");
@ -138,7 +140,7 @@ TEST(Subtractor, WrongCaptureSize) {
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_delay_buffer->GetRenderBuffer(), y, EXPECT_DEATH(subtractor.Process(*render_delay_buffer->GetRenderBuffer(), y,
render_signal_analyzer, render_signal_analyzer,
AecState(EchoCanceller3Config{}), &output), AecState(EchoCanceller3Config{}), &output),
""); "");

View File

@ -71,7 +71,7 @@ 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_delay_buffer->GetRenderBuffer(), subtractor.ConvergedFilter(), 10, *render_delay_buffer->GetRenderBuffer(),
E2, Y2, x[0], s, false); 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);
@ -88,14 +88,14 @@ TEST(SuppressionGain, BasicGainComputation) {
aec_state.Update( aec_state.Update(
subtractor.FilterFrequencyResponse(), subtractor.FilterFrequencyResponse(),
subtractor.FilterImpulseResponse(), subtractor.ConvergedFilter(), 10, subtractor.FilterImpulseResponse(), subtractor.ConvergedFilter(), 10,
render_delay_buffer->GetRenderBuffer(), E2, Y2, 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( aec_state.Update(
subtractor.FilterFrequencyResponse(), subtractor.FilterFrequencyResponse(),
subtractor.FilterImpulseResponse(), subtractor.ConvergedFilter(), 10, subtractor.FilterImpulseResponse(), subtractor.ConvergedFilter(), 10,
render_delay_buffer->GetRenderBuffer(), E2, Y2, 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);
} }
@ -111,7 +111,7 @@ TEST(SuppressionGain, BasicGainComputation) {
aec_state.Update( aec_state.Update(
subtractor.FilterFrequencyResponse(), subtractor.FilterFrequencyResponse(),
subtractor.FilterImpulseResponse(), subtractor.ConvergedFilter(), 10, subtractor.FilterImpulseResponse(), subtractor.ConvergedFilter(), 10,
render_delay_buffer->GetRenderBuffer(), E2, Y2, 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

@ -15,7 +15,8 @@
namespace webrtc { namespace webrtc {
VectorBuffer::VectorBuffer(size_t size, size_t height) VectorBuffer::VectorBuffer(size_t size, size_t height)
: size(size), buffer(size, std::vector<float>(height, 0.f)) { : size(static_cast<int>(size)),
buffer(size, std::vector<float>(height, 0.f)) {
for (auto& c : buffer) { for (auto& c : buffer) {
std::fill(c.begin(), c.end(), 0.f); std::fill(c.begin(), c.end(), 0.f);
} }

View File

@ -23,17 +23,20 @@ struct VectorBuffer {
VectorBuffer(size_t size, size_t height); VectorBuffer(size_t size, size_t height);
~VectorBuffer(); ~VectorBuffer();
size_t IncIndex(size_t index) { int IncIndex(int index) const {
return index < buffer.size() - 1 ? index + 1 : 0; RTC_DCHECK_EQ(buffer.size(), static_cast<size_t>(size));
return index < size - 1 ? index + 1 : 0;
} }
size_t DecIndex(size_t index) { int DecIndex(int index) const {
return index > 0 ? index - 1 : buffer.size() - 1; RTC_DCHECK_EQ(buffer.size(), static_cast<size_t>(size));
return index > 0 ? index - 1 : size - 1;
} }
size_t OffsetIndex(size_t index, int offset) { int OffsetIndex(int index, int offset) const {
RTC_DCHECK_GE(buffer.size(), offset); RTC_DCHECK_GE(size, offset);
return (buffer.size() + index + offset) % buffer.size(); RTC_DCHECK_EQ(buffer.size(), static_cast<size_t>(size));
return (size + index + offset) % size;
} }
void UpdateWriteIndex(int offset) { write = OffsetIndex(write, offset); } void UpdateWriteIndex(int offset) { write = OffsetIndex(write, offset); }
@ -43,10 +46,10 @@ struct VectorBuffer {
void IncReadIndex() { read = IncIndex(read); } void IncReadIndex() { read = IncIndex(read); }
void DecReadIndex() { read = DecIndex(read); } void DecReadIndex() { read = DecIndex(read); }
size_t size; const int size;
std::vector<std::vector<float>> buffer; std::vector<std::vector<float>> buffer;
size_t write = 0; int write = 0;
size_t read = 0; int read = 0;
}; };
} // namespace webrtc } // namespace webrtc