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:
parent
efc5fbd8e0
commit
c59a576c86
@ -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),
|
||||||
|
|||||||
@ -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");
|
||||||
|
|||||||
@ -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());
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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),
|
||||||
|
|||||||
@ -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())
|
||||||
|
|||||||
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
@ -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));
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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());
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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);
|
||||||
|
|||||||
@ -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();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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),
|
||||||
"");
|
"");
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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);
|
||||||
|
|||||||
@ -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))) {
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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));
|
||||||
|
|||||||
@ -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_;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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>());
|
||||||
};
|
};
|
||||||
|
|||||||
@ -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_;
|
||||||
|
|||||||
@ -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);
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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.
|
||||||
|
|||||||
@ -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)),
|
||||||
"");
|
"");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@ -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(),
|
||||||
|
|||||||
@ -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());
|
||||||
|
|||||||
@ -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),
|
||||||
"");
|
"");
|
||||||
|
|||||||
@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user