diff --git a/modules/audio_processing/aec3/adaptive_fir_filter_unittest.cc b/modules/audio_processing/aec3/adaptive_fir_filter_unittest.cc index b3a93a700e..791612a3b6 100644 --- a/modules/audio_processing/aec3/adaptive_fir_filter_unittest.cc +++ b/modules/audio_processing/aec3/adaptive_fir_filter_unittest.cc @@ -71,7 +71,8 @@ TEST(AdaptiveFirFilter, FilterAdaptationNeonOptimizations) { if (k == 0) { 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(); @@ -84,10 +85,10 @@ TEST(AdaptiveFirFilter, FilterAdaptationNeonOptimizations) { G.im[0] = 0.f; G.im[G.im.size() - 1] = 0.f; - AdaptPartitions_NEON(render_buffer, G, H_NEON); - AdaptPartitions(render_buffer, G, H_C); - AdaptPartitions_NEON(render_buffer, G, H_NEON); - AdaptPartitions(render_buffer, G, H_C); + AdaptPartitions_NEON(*render_buffer, G, H_NEON); + AdaptPartitions(*render_buffer, G, H_C); + AdaptPartitions_NEON(*render_buffer, G, H_NEON); + AdaptPartitions(*render_buffer, G, H_C); for (size_t l = 0; l < H_C.size(); ++l) { 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(render_buffer, H_C, &S_C); + ApplyFilter_NEON(*render_buffer, H_NEON, &S_NEON); + ApplyFilter(*render_buffer, H_C, &S_C); 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.im[j], S_NEON.im[j], fabs(S_C.re[j] * 0.00001f)); @@ -182,11 +183,12 @@ TEST(AdaptiveFirFilter, FilterAdaptationSse2Optimizations) { if (k == 0) { 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(); - ApplyFilter_SSE2(render_buffer, H_SSE2, &S_SSE2); - ApplyFilter(render_buffer, H_C, &S_C); + ApplyFilter_SSE2(*render_buffer, H_SSE2, &S_SSE2); + ApplyFilter(*render_buffer, H_C, &S_C); 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.im[j], S_SSE2.im[j]); @@ -197,8 +199,8 @@ TEST(AdaptiveFirFilter, FilterAdaptationSse2Optimizations) { std::for_each(G.im.begin(), G.im.end(), [&](float& a) { a = random_generator.Rand(); }); - AdaptPartitions_SSE2(render_buffer, G, H_SSE2); - AdaptPartitions(render_buffer, G, H_C); + AdaptPartitions_SSE2(*render_buffer, G, H_SSE2); + AdaptPartitions(*render_buffer, G, H_C); for (size_t k = 0; k < H_C.size(); ++k) { 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); std::unique_ptr render_delay_buffer( 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; EchoCanceller3Config config; config.delay.min_echo_path_delay_blocks = 0; + config.delay.default_delay = 1; std::unique_ptr render_delay_buffer( RenderDelayBuffer::Create(config, 3)); ShadowFilterUpdateGain gain; @@ -362,12 +365,13 @@ TEST(AdaptiveFirFilter, FilterAndAdapt) { if (k == 0) { 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(); - 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); std::transform(y.begin(), y.end(), s_scratch.begin() + kFftLengthBy2, e.begin(), @@ -379,14 +383,14 @@ TEST(AdaptiveFirFilter, FilterAndAdapt) { 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.Adapt(render_buffer, G); + filter.Adapt(*render_buffer, G); aec_state.HandleEchoPathChange(EchoPathVariability( false, EchoPathVariability::DelayAdjustment::kNone, false)); aec_state.Update(filter.FilterFrequencyResponse(), 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. EXPECT_LT(1000 * std::inner_product(e.begin(), e.end(), e.begin(), 0.f), diff --git a/modules/audio_processing/aec3/aec3_common.h b/modules/audio_processing/aec3/aec3_common.h index 13842f9854..2e25b199d1 100644 --- a/modules/audio_processing/aec3/aec3_common.h +++ b/modules/audio_processing/aec3/aec3_common.h @@ -48,6 +48,8 @@ constexpr size_t kMaxNumBands = 3; constexpr size_t kSubFrameLength = 80; constexpr size_t kBlockSize = kFftLengthBy2; +constexpr size_t kBlockSizeLog2 = 6; + constexpr size_t kExtendedBlockSize = 2 * kFftLengthBy2; constexpr size_t kMatchedFilterWindowSizeSubBlocks = 32; 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, size_t 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. 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(16000), "Number of bands for 16 kHz"); static_assert(2 == NumBandsForRate(32000), "Number of bands for 32 kHz"); diff --git a/modules/audio_processing/aec3/aec_state_unittest.cc b/modules/audio_processing/aec3/aec_state_unittest.cc index fe09086740..016c2c174e 100644 --- a/modules/audio_processing/aec3/aec_state_unittest.cc +++ b/modules/audio_processing/aec3/aec_state_unittest.cc @@ -49,7 +49,7 @@ TEST(AecState, NormalUsage) { // Verify that linear AEC usability is false when the filter is diverged and // there is no external delay reported. 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); EXPECT_FALSE(state.UsableLinearEstimate()); @@ -57,7 +57,7 @@ TEST(AecState, NormalUsage) { std::fill(x[0].begin(), x[0].end(), 101.f); for (int k = 0; k < 3000; ++k) { 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); } EXPECT_TRUE(state.UsableLinearEstimate()); @@ -67,7 +67,7 @@ TEST(AecState, NormalUsage) { state.HandleEchoPathChange(EchoPathVariability( true, EchoPathVariability::DelayAdjustment::kNone, false)); 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); EXPECT_FALSE(state.UsableLinearEstimate()); @@ -76,25 +76,25 @@ TEST(AecState, NormalUsage) { state.HandleEchoPathChange(EchoPathVariability( true, EchoPathVariability::DelayAdjustment::kNewDetectedDelay, false)); 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); EXPECT_FALSE(state.ActiveRender()); for (int k = 0; k < 1000; ++k) { 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); } EXPECT_TRUE(state.ActiveRender()); // Verify that echo leakage is properly reported. 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); EXPECT_FALSE(state.EchoLeakageDetected()); 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); EXPECT_TRUE(state.EchoLeakageDetected()); @@ -104,19 +104,20 @@ TEST(AecState, NormalUsage) { } x[0][0] = 5000.f; - for (size_t k = 0; k < render_delay_buffer->GetRenderBuffer().Buffer().size(); - ++k) { + for (size_t k = 0; + k < render_delay_buffer->GetRenderBuffer()->Buffer().size(); ++k) { render_delay_buffer->Insert(x); if (k == 0) { 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); for (size_t k = 0; k < 1000; ++k) { 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); } @@ -133,7 +134,7 @@ TEST(AecState, NormalUsage) { Y2.fill(10.f * E2_main[0]); for (size_t k = 0; k < 1000; ++k) { 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); } ASSERT_TRUE(state.UsableLinearEstimate()); @@ -154,7 +155,7 @@ TEST(AecState, NormalUsage) { Y2.fill(5.f * E2_main[0]); for (size_t k = 0; k < 1000; ++k) { 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); } @@ -204,7 +205,7 @@ TEST(AecState, ConvergedFilterDelay) { frequency_response[k][0] = 0.f; state.HandleEchoPathChange(echo_path_variability); 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); EXPECT_TRUE(k == (kFilterLength - 1) || state.FilterDelay()); if (k != (kFilterLength - 1)) { @@ -243,7 +244,7 @@ TEST(AecState, ExternalDelay) { state.HandleEchoPathChange(EchoPathVariability( false, EchoPathVariability::DelayAdjustment::kNone, false)); 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); EXPECT_TRUE(state.ExternalDelay()); EXPECT_EQ(k, state.ExternalDelay()); @@ -254,7 +255,7 @@ TEST(AecState, ExternalDelay) { state.HandleEchoPathChange(EchoPathVariability( false, EchoPathVariability::DelayAdjustment::kNone, false)); 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); EXPECT_FALSE(state.ExternalDelay()); } diff --git a/modules/audio_processing/aec3/block_processor.cc b/modules/audio_processing/aec3/block_processor.cc index f12e7eed3c..71f7960916 100644 --- a/modules/audio_processing/aec3/block_processor.cc +++ b/modules/audio_processing/aec3/block_processor.cc @@ -55,6 +55,7 @@ class BlockProcessorImpl final : public BlockProcessor { std::unique_ptr echo_remover_; BlockProcessorMetrics metrics_; RenderDelayBuffer::BufferingEvent render_event_; + size_t capture_call_counter_ = 0; RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(BlockProcessorImpl); }; @@ -86,6 +87,9 @@ void BlockProcessorImpl::ProcessCapture( RTC_DCHECK(capture_block); RTC_DCHECK_EQ(NumBandsForRate(sample_rate_hz_), capture_block->size()); RTC_DCHECK_EQ(kBlockSize, (*capture_block)[0].size()); + + capture_call_counter_++; + data_dumper_->DumpRaw("aec3_processblock_call_order", static_cast(BlockProcessorApiCall::kCapture)); data_dumper_->DumpWav("aec3_processblock_capture_input", kBlockSize, @@ -111,30 +115,35 @@ void BlockProcessorImpl::ProcessCapture( echo_path_variability.delay_change = EchoPathVariability::DelayAdjustment::kBufferFlush; delay_controller_->Reset(); - render_buffer_->Reset(); - RTC_LOG(LS_WARNING) << "Reset due to render buffer overrun."; + RTC_LOG(LS_WARNING) << "Reset due to render buffer overrun at block " + << capture_call_counter_; } // Update the render buffers with any newly arrived render blocks and prepare // the render buffers for reading the render data corresponding to the current // capture block. - render_event_ = render_buffer_->PrepareCaptureCall(); + render_event_ = render_buffer_->PrepareCaptureProcessing(); RTC_DCHECK(RenderDelayBuffer::BufferingEvent::kRenderOverrun != render_event_); if (render_event_ == RenderDelayBuffer::BufferingEvent::kRenderUnderrun) { echo_path_variability.delay_change = - EchoPathVariability::DelayAdjustment::kBufferReadjustment; + EchoPathVariability::DelayAdjustment::kDelayReset; 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) { // There have been too many render calls in a row. Reset to avoid noncausal // echo. echo_path_variability.delay_change = EchoPathVariability::DelayAdjustment::kDelayReset; delay_controller_->Reset(); - render_buffer_->Reset(); capture_properly_started_ = false; render_properly_started_ = false; + RTC_LOG(LS_WARNING) << "Reset due to render buffer api skew at block " + << capture_call_counter_; } 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 // alignment. - const size_t estimated_delay = delay_controller_->GetDelay( + rtc::Optional estimated_delay = delay_controller_->GetDelay( 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 (delay_change && new_delay >= config_.delay.min_echo_path_delay_blocks) { - echo_path_variability.delay_change = - EchoPathVariability::DelayAdjustment::kNewDetectedDelay; - render_buffer_->SetDelay(new_delay); - 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 - // clockdrift, an audio pipeline issue has occurred or the specified minimum - // delay is too short. Perform a full reset. - echo_path_variability.delay_change = - EchoPathVariability::DelayAdjustment::kDelayReset; - delay_controller_->Reset(); - render_buffer_->Reset(); - capture_properly_started_ = false; - render_properly_started_ = false; - RTC_LOG(LS_WARNING) << "Reset due to noncausal delay."; + if (estimated_delay) { + 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 = + EchoPathVariability::DelayAdjustment::kNewDetectedDelay; + } else { + // A noncausal delay has been detected. This can only happen if there is + // clockdrift, an audio pipeline issue has occurred or the specified + // minimum delay is too short. Perform a full reset. + echo_path_variability.delay_change = + EchoPathVariability::DelayAdjustment::kDelayReset; + delay_controller_->Reset(); + render_buffer_->Reset(); + capture_properly_started_ = false; + render_properly_started_ = false; + RTC_LOG(LS_WARNING) << "Reset due to noncausal delay at block " + << capture_call_counter_; + } + } } // Remove the echo from the capture signal. @@ -207,7 +218,8 @@ void BlockProcessorImpl::UpdateEchoLeakageStatus(bool leakage_detected) { void BlockProcessorImpl::GetMetrics(EchoControl::Metrics* metrics) const { echo_remover_->GetMetrics(metrics); const int block_size_ms = sample_rate_hz_ == 8000 ? 8 : 4; - metrics->delay_ms = static_cast(render_buffer_->Delay()) * block_size_ms; + rtc::Optional delay = render_buffer_->Delay(); + metrics->delay_ms = delay ? static_cast(*delay) * block_size_ms : 0; } } // namespace @@ -217,7 +229,9 @@ BlockProcessor* BlockProcessor::Create(const EchoCanceller3Config& config, std::unique_ptr render_buffer( RenderDelayBuffer::Create(config, NumBandsForRate(sample_rate_hz))); std::unique_ptr delay_controller( - RenderDelayController::Create(config, sample_rate_hz)); + RenderDelayController::Create( + config, RenderDelayBuffer::DelayEstimatorOffset(config), + sample_rate_hz)); std::unique_ptr echo_remover( EchoRemover::Create(config, sample_rate_hz)); return Create(config, sample_rate_hz, std::move(render_buffer), @@ -229,7 +243,9 @@ BlockProcessor* BlockProcessor::Create( int sample_rate_hz, std::unique_ptr render_buffer) { std::unique_ptr delay_controller( - RenderDelayController::Create(config, sample_rate_hz)); + RenderDelayController::Create( + config, RenderDelayBuffer::DelayEstimatorOffset(config), + sample_rate_hz)); std::unique_ptr echo_remover( EchoRemover::Create(config, sample_rate_hz)); return Create(config, sample_rate_hz, std::move(render_buffer), diff --git a/modules/audio_processing/aec3/block_processor_unittest.cc b/modules/audio_processing/aec3/block_processor_unittest.cc index 1d97002ecf..19269864b7 100644 --- a/modules/audio_processing/aec3/block_processor_unittest.cc +++ b/modules/audio_processing/aec3/block_processor_unittest.cc @@ -116,9 +116,6 @@ TEST(BlockProcessor, DISABLED_DelayControllerIntegration) { EXPECT_CALL(*render_delay_buffer_mock, Insert(_)) .Times(kNumBlocks) .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)) .Times(AtLeast(1)); 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(_)) .Times(kNumBlocks - 1) .WillRepeatedly(Return(RenderDelayBuffer::BufferingEvent::kNone)); - EXPECT_CALL(*render_delay_buffer_mock, IsBlockAvailable()) - .Times(kNumBlocks) - .WillRepeatedly(Return(true)); - EXPECT_CALL(*render_delay_buffer_mock, PrepareCaptureCall()) + EXPECT_CALL(*render_delay_buffer_mock, PrepareCaptureProcessing()) .Times(kNumBlocks); EXPECT_CALL(*render_delay_buffer_mock, SetDelay(9)).Times(AtLeast(1)); EXPECT_CALL(*render_delay_buffer_mock, Delay()) diff --git a/modules/audio_processing/aec3/downsampled_render_buffer.cc b/modules/audio_processing/aec3/downsampled_render_buffer.cc index cd6ae48649..df0af6e0d8 100644 --- a/modules/audio_processing/aec3/downsampled_render_buffer.cc +++ b/modules/audio_processing/aec3/downsampled_render_buffer.cc @@ -13,7 +13,8 @@ namespace webrtc { DownsampledRenderBuffer::DownsampledRenderBuffer(size_t downsampled_buffer_size) - : size(downsampled_buffer_size), buffer(downsampled_buffer_size, 0.f) { + : size(static_cast(downsampled_buffer_size)), + buffer(downsampled_buffer_size, 0.f) { std::fill(buffer.begin(), buffer.end(), 0.f); } diff --git a/modules/audio_processing/aec3/downsampled_render_buffer.h b/modules/audio_processing/aec3/downsampled_render_buffer.h index c656846960..943949666a 100644 --- a/modules/audio_processing/aec3/downsampled_render_buffer.h +++ b/modules/audio_processing/aec3/downsampled_render_buffer.h @@ -23,17 +23,20 @@ struct DownsampledRenderBuffer { explicit DownsampledRenderBuffer(size_t downsampled_buffer_size); ~DownsampledRenderBuffer(); - size_t IncIndex(size_t index) { - return index < (buffer.size() - 1) ? index + 1 : 0; + int IncIndex(int index) const { + RTC_DCHECK_EQ(buffer.size(), static_cast(size)); + return index < size - 1 ? index + 1 : 0; } - size_t DecIndex(size_t index) { - return index > 0 ? index - 1 : buffer.size() - 1; + int DecIndex(int index) const { + RTC_DCHECK_EQ(buffer.size(), static_cast(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); - return (buffer.size() + index + offset) % buffer.size(); + RTC_DCHECK_EQ(buffer.size(), static_cast(size)); + return (size + index + offset) % size; } void UpdateWriteIndex(int offset) { write = OffsetIndex(write, offset); } @@ -43,7 +46,7 @@ struct DownsampledRenderBuffer { void IncReadIndex() { read = IncIndex(read); } void DecReadIndex() { read = DecIndex(read); } - size_t size; + const int size; std::vector buffer; int write = 0; int read = 0; diff --git a/modules/audio_processing/aec3/echo_path_delay_estimator_unittest.cc b/modules/audio_processing/aec3/echo_path_delay_estimator_unittest.cc index e51287b053..3ea3dd6f46 100644 --- a/modules/audio_processing/aec3/echo_path_delay_estimator_unittest.cc +++ b/modules/audio_processing/aec3/echo_path_delay_estimator_unittest.cc @@ -65,25 +65,15 @@ TEST(EchoPathDelayEstimator, DelayEstimation) { for (size_t delay_samples : {30, 64, 150, 200, 800, 4000}) { SCOPED_TRACE(ProduceDebugText(delay_samples, down_sampling_factor)); - config.delay.min_echo_path_delay_blocks = 0; - while ((config.delay.min_echo_path_delay_blocks + 1) * kBlockSize < - delay_samples && - config.delay.min_echo_path_delay_blocks + 1 <= 5) { - ++config.delay.min_echo_path_delay_blocks; - } - - const int delay_estimate_offset = - std::max(std::min(config.delay.api_call_jitter_blocks, - config.delay.min_echo_path_delay_blocks) - - 1, - 0); + config.delay.api_call_jitter_blocks = 5; std::unique_ptr render_delay_buffer( RenderDelayBuffer::Create(config, 3)); - DelayBuffer signal_delay_buffer(delay_samples); + DelayBuffer signal_delay_buffer( + delay_samples + 2 * config.delay.api_call_jitter_blocks * 64); EchoPathDelayEstimator estimator(&data_dumper, config); rtc::Optional 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]); signal_delay_buffer.Delay(render[0], capture); render_delay_buffer->Insert(render); @@ -92,7 +82,9 @@ TEST(EchoPathDelayEstimator, DelayEstimation) { render_delay_buffer->Reset(); } - render_delay_buffer->PrepareCaptureCall(); + render_delay_buffer->PrepareCaptureProcessing(); + + render_delay_buffer->GetRenderBuffer()->UpdateSpectralSum(); estimated_delay_samples = estimator.EstimateDelay( render_delay_buffer->GetDownsampledRenderBuffer(), capture); } @@ -100,10 +92,10 @@ TEST(EchoPathDelayEstimator, DelayEstimation) { if (estimated_delay_samples) { // Due to the internal down-sampling done inside the delay estimator // the estimated delay cannot be expected to be exact to the true delay. - EXPECT_NEAR( - delay_samples, - *estimated_delay_samples + delay_estimate_offset * kBlockSize, - config.delay.down_sampling_factor); + EXPECT_NEAR(delay_samples, + *estimated_delay_samples - + (config.delay.api_call_jitter_blocks + 1) * 64, + config.delay.down_sampling_factor); } else { ADD_FAILURE(); } @@ -127,7 +119,8 @@ TEST(EchoPathDelayEstimator, NoInitialDelayestimates) { RandomizeSampleVector(&random_generator, render[0]); std::copy(render[0].begin(), render[0].end(), capture.begin()); render_delay_buffer->Insert(render); - render_delay_buffer->PrepareCaptureCall(); + render_delay_buffer->PrepareCaptureProcessing(); + render_delay_buffer->GetRenderBuffer()->UpdateSpectralSum(); EXPECT_FALSE(estimator.EstimateDelay( render_delay_buffer->GetDownsampledRenderBuffer(), capture)); } @@ -151,7 +144,8 @@ TEST(EchoPathDelayEstimator, NoDelayEstimatesForLowLevelRenderSignals) { } std::copy(render[0].begin(), render[0].end(), capture.begin()); render_delay_buffer->Insert(render); - render_delay_buffer->PrepareCaptureCall(); + render_delay_buffer->PrepareCaptureProcessing(); + render_delay_buffer->GetRenderBuffer()->UpdateSpectralSum(); EXPECT_FALSE(estimator.EstimateDelay( render_delay_buffer->GetDownsampledRenderBuffer(), capture)); } @@ -172,7 +166,7 @@ TEST(EchoPathDelayEstimator, NoDelayEstimatesForUncorrelatedSignals) { RandomizeSampleVector(&random_generator, render[0]); RandomizeSampleVector(&random_generator, capture); render_delay_buffer->Insert(render); - render_delay_buffer->PrepareCaptureCall(); + render_delay_buffer->PrepareCaptureProcessing(); EXPECT_FALSE(estimator.EstimateDelay( render_delay_buffer->GetDownsampledRenderBuffer(), capture)); } diff --git a/modules/audio_processing/aec3/echo_remover.cc b/modules/audio_processing/aec3/echo_remover.cc index fc72207f2e..17b201af68 100644 --- a/modules/audio_processing/aec3/echo_remover.cc +++ b/modules/audio_processing/aec3/echo_remover.cc @@ -61,7 +61,7 @@ class EchoRemoverImpl final : public EchoRemover { void ProcessCapture(const rtc::Optional& echo_path_delay_samples, const EchoPathVariability& echo_path_variability, bool capture_signal_saturation, - const RenderBuffer& render_buffer, + RenderBuffer* render_buffer, std::vector>* capture) override; // Updates the status on whether echo leakage is detected in the output of the @@ -123,11 +123,11 @@ void EchoRemoverImpl::ProcessCapture( const rtc::Optional& echo_path_delay_samples, const EchoPathVariability& echo_path_variability, bool capture_signal_saturation, - const RenderBuffer& render_buffer, + RenderBuffer* render_buffer, std::vector>* capture) { - const std::vector>& x = render_buffer.MostRecentBlock(); + const std::vector>& x = render_buffer->MostRecentBlock(); std::vector>* y = capture; - + RTC_DCHECK(render_buffer); RTC_DCHECK(y); RTC_DCHECK_EQ(x.size(), NumBandsForRate(sample_rate_hz_)); RTC_DCHECK_EQ(y->size(), NumBandsForRate(sample_rate_hz_)); @@ -143,6 +143,8 @@ void EchoRemoverImpl::ProcessCapture( data_dumper_->DumpRaw("aec3_echo_remover_capture_input", y0); data_dumper_->DumpRaw("aec3_echo_remover_render_input", x0); + render_buffer->UpdateSpectralSum(); + aec_state_.UpdateCaptureSaturation(capture_signal_saturation); if (echo_path_variability.AudioPathChanged()) { @@ -165,10 +167,10 @@ void EchoRemoverImpl::ProcessCapture( auto& e_main = subtractor_output.e_main; // 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. - subtractor_.Process(render_buffer, y0, render_signal_analyzer_, aec_state_, + subtractor_.Process(*render_buffer, y0, render_signal_analyzer_, aec_state_, &subtractor_output); // Compute spectra. @@ -180,7 +182,7 @@ void EchoRemoverImpl::ProcessCapture( aec_state_.Update(subtractor_.FilterFrequencyResponse(), subtractor_.FilterImpulseResponse(), 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_); // Choose the linear output. @@ -191,7 +193,7 @@ void EchoRemoverImpl::ProcessCapture( const auto& E2 = output_selector_.UseSubtractorOutput() ? E2_main : Y2; // 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); // Estimate the comfort noise. @@ -229,7 +231,7 @@ void EchoRemoverImpl::ProcessCapture( data_dumper_->DumpRaw("aec3_E2_shadow", E2_shadow); data_dumper_->DumpRaw("aec3_S2_linear", S2_linear); 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_erle", aec_state_.Erle()); data_dumper_->DumpRaw("aec3_erl", aec_state_.Erl()); diff --git a/modules/audio_processing/aec3/echo_remover.h b/modules/audio_processing/aec3/echo_remover.h index 7411b5c45c..230ed985a9 100644 --- a/modules/audio_processing/aec3/echo_remover.h +++ b/modules/audio_processing/aec3/echo_remover.h @@ -37,7 +37,7 @@ class EchoRemover { const rtc::Optional& echo_path_delay_samples, const EchoPathVariability& echo_path_variability, bool capture_signal_saturation, - const RenderBuffer& render_buffer, + RenderBuffer* render_buffer, std::vector>* capture) = 0; // Updates the status on whether echo leakage is detected in the output of the diff --git a/modules/audio_processing/aec3/echo_remover_unittest.cc b/modules/audio_processing/aec3/echo_remover_unittest.cc index f035f4f4d1..bed5b6e5be 100644 --- a/modules/audio_processing/aec3/echo_remover_unittest.cc +++ b/modules/audio_processing/aec3/echo_remover_unittest.cc @@ -64,7 +64,8 @@ TEST(EchoRemover, BasicApiCalls) { (k % 6 == 0 ? rtc::Optional(k * 10) : rtc::nullopt); render_buffer->Insert(render); - render_buffer->PrepareCaptureCall(); + render_buffer->PrepareCaptureProcessing(); + remover->ProcessCapture(echo_path_delay_samples, echo_path_variability, k % 2 == 0 ? true : false, render_buffer->GetRenderBuffer(), &capture); @@ -162,9 +163,8 @@ TEST(EchoRemover, BasicEchoRemoval) { std::unique_ptr remover(EchoRemover::Create(config, rate)); std::unique_ptr render_buffer( 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>> delay_buffers(x.size()); for (size_t j = 0; j < x.size(); ++j) { delay_buffers[j].reset(new DelayBuffer(delay_samples)); @@ -192,7 +192,7 @@ TEST(EchoRemover, BasicEchoRemoval) { } render_buffer->Insert(x); - render_buffer->PrepareCaptureCall(); + render_buffer->PrepareCaptureProcessing(); remover->ProcessCapture(delay_samples, echo_path_variability, false, render_buffer->GetRenderBuffer(), &y); diff --git a/modules/audio_processing/aec3/fft_buffer.cc b/modules/audio_processing/aec3/fft_buffer.cc index 133d77bec2..379ef7c521 100644 --- a/modules/audio_processing/aec3/fft_buffer.cc +++ b/modules/audio_processing/aec3/fft_buffer.cc @@ -12,7 +12,7 @@ namespace webrtc { -FftBuffer::FftBuffer(size_t size) : buffer(size) { +FftBuffer::FftBuffer(size_t size) : size(static_cast(size)), buffer(size) { for (auto& b : buffer) { b.Clear(); } diff --git a/modules/audio_processing/aec3/fft_buffer.h b/modules/audio_processing/aec3/fft_buffer.h index 42fbb4a623..47ede41825 100644 --- a/modules/audio_processing/aec3/fft_buffer.h +++ b/modules/audio_processing/aec3/fft_buffer.h @@ -24,17 +24,20 @@ struct FftBuffer { explicit FftBuffer(size_t size); ~FftBuffer(); - size_t IncIndex(size_t index) { - return index < buffer.size() - 1 ? index + 1 : 0; + int IncIndex(int index) const { + RTC_DCHECK_EQ(buffer.size(), static_cast(size)); + return index < size - 1 ? index + 1 : 0; } - size_t DecIndex(size_t index) { - return index > 0 ? index - 1 : buffer.size() - 1; + int DecIndex(int index) const { + RTC_DCHECK_EQ(buffer.size(), static_cast(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); - return (buffer.size() + index + offset) % buffer.size(); + RTC_DCHECK_EQ(buffer.size(), static_cast(size)); + return (size + index + offset) % size; } void UpdateWriteIndex(int offset) { write = OffsetIndex(write, offset); } @@ -44,9 +47,10 @@ struct FftBuffer { void IncReadIndex() { read = IncIndex(read); } void DecReadIndex() { read = DecIndex(read); } + const int size; std::vector buffer; - size_t write = 0; - size_t read = 0; + int write = 0; + int read = 0; }; } // namespace webrtc diff --git a/modules/audio_processing/aec3/main_filter_update_gain_unittest.cc b/modules/audio_processing/aec3/main_filter_update_gain_unittest.cc index b59273db42..b39067cd41 100644 --- a/modules/audio_processing/aec3/main_filter_update_gain_unittest.cc +++ b/modules/audio_processing/aec3/main_filter_update_gain_unittest.cc @@ -52,6 +52,7 @@ void RunFilterUpdateTest(int num_blocks_to_process, std::vector y(kBlockSize, 0.f); EchoCanceller3Config config; config.delay.min_echo_path_delay_blocks = 0; + config.delay.default_delay = 1; std::unique_ptr render_delay_buffer( RenderDelayBuffer::Create(config, 3)); AecState aec_state(config); @@ -98,13 +99,14 @@ void RunFilterUpdateTest(int num_blocks_to_process, if (k == 0) { 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()); // 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); std::transform(y.begin(), y.end(), s_scratch.begin() + kFftLengthBy2, e_main.begin(), @@ -117,7 +119,7 @@ void RunFilterUpdateTest(int num_blocks_to_process, } // 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); std::transform(y.begin(), y.end(), s_scratch.begin() + kFftLengthBy2, e_shadow.begin(), @@ -131,23 +133,23 @@ void RunFilterUpdateTest(int num_blocks_to_process, E_shadow.Spectrum(Aec3Optimization::kNone, output.E2_shadow); // Adapt the shadow filter. - shadow_gain.Compute(render_delay_buffer->GetRenderBuffer(), + shadow_gain.Compute(*render_delay_buffer->GetRenderBuffer(), render_signal_analyzer, E_shadow, 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 - main_gain.Compute(render_delay_buffer->GetRenderBuffer(), + main_gain.Compute(*render_delay_buffer->GetRenderBuffer(), render_signal_analyzer, output, main_filter, saturation, &G); - main_filter.Adapt(render_delay_buffer->GetRenderBuffer(), G); + main_filter.Adapt(*render_delay_buffer->GetRenderBuffer(), G); // Update the delay. aec_state.HandleEchoPathChange(EchoPathVariability( false, EchoPathVariability::DelayAdjustment::kNone, false)); aec_state.Update(main_filter.FilterFrequencyResponse(), 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); } @@ -177,7 +179,7 @@ TEST(MainFilterUpdateGain, NullDataOutputGain) { RenderSignalAnalyzer analyzer; SubtractorOutput output; MainFilterUpdateGain gain; - EXPECT_DEATH(gain.Compute(render_delay_buffer->GetRenderBuffer(), analyzer, + EXPECT_DEATH(gain.Compute(*render_delay_buffer->GetRenderBuffer(), analyzer, output, filter, false, nullptr), ""); } diff --git a/modules/audio_processing/aec3/matched_filter_unittest.cc b/modules/audio_processing/aec3/matched_filter_unittest.cc index aec68821d9..9ae3ee2795 100644 --- a/modules/audio_processing/aec3/matched_filter_unittest.cc +++ b/modules/audio_processing/aec3/matched_filter_unittest.cc @@ -145,16 +145,17 @@ TEST(MatchedFilter, LagEstimation) { ApmDataDumper data_dumper(0); for (size_t delay_samples : {5, 64, 150, 200, 800, 1000}) { 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); DelayBuffer signal_delay_buffer(down_sampling_factor * delay_samples); MatchedFilter filter(&data_dumper, DetectOptimization(), sub_block_size, kWindowSizeSubBlocks, kNumMatchedFilters, 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 render_delay_buffer( RenderDelayBuffer::Create(config, 3)); @@ -164,11 +165,13 @@ TEST(MatchedFilter, LagEstimation) { RandomizeSampleVector(&random_generator, render[0]); signal_delay_buffer.Delay(render[0], capture); render_delay_buffer->Insert(render); + if (k == 0) { render_delay_buffer->Reset(); } - render_delay_buffer->PrepareCaptureCall(); + render_delay_buffer->PrepareCaptureProcessing(); + render_delay_buffer->GetRenderBuffer()->UpdateSpectralSum(); std::array downsampled_capture_data; rtc::ArrayView downsampled_capture( downsampled_capture_data.data(), sub_block_size); diff --git a/modules/audio_processing/aec3/matrix_buffer.cc b/modules/audio_processing/aec3/matrix_buffer.cc index 23a5ad4d64..f95e7f4690 100644 --- a/modules/audio_processing/aec3/matrix_buffer.cc +++ b/modules/audio_processing/aec3/matrix_buffer.cc @@ -15,7 +15,7 @@ namespace webrtc { MatrixBuffer::MatrixBuffer(size_t size, size_t height, size_t width) - : size(size), + : size(static_cast(size)), buffer(size, std::vector>(height, std::vector(width, 0.f))) { diff --git a/modules/audio_processing/aec3/matrix_buffer.h b/modules/audio_processing/aec3/matrix_buffer.h index 67980bab0c..64aac0a4f6 100644 --- a/modules/audio_processing/aec3/matrix_buffer.h +++ b/modules/audio_processing/aec3/matrix_buffer.h @@ -23,17 +23,20 @@ struct MatrixBuffer { MatrixBuffer(size_t size, size_t height, size_t width); ~MatrixBuffer(); - size_t IncIndex(size_t index) { - return index < buffer.size() - 1 ? index + 1 : 0; + int IncIndex(int index) const { + RTC_DCHECK_EQ(buffer.size(), static_cast(size)); + return index < size - 1 ? index + 1 : 0; } - size_t DecIndex(size_t index) { - return index > 0 ? index - 1 : buffer.size() - 1; + int DecIndex(int index) const { + RTC_DCHECK_EQ(buffer.size(), static_cast(size)); + return index > 0 ? index - 1 : size - 1; } - size_t OffsetIndex(size_t index, int offset) { - RTC_DCHECK_GE(buffer.size(), offset); - return (buffer.size() + index + offset) % buffer.size(); + int OffsetIndex(int index, int offset) const { + RTC_DCHECK_EQ(buffer.size(), static_cast(size)); + RTC_DCHECK_GE(size, offset); + return (size + index + offset) % size; } void UpdateWriteIndex(int offset) { write = OffsetIndex(write, offset); } @@ -43,10 +46,10 @@ struct MatrixBuffer { void IncReadIndex() { read = IncIndex(read); } void DecReadIndex() { read = DecIndex(read); } - size_t size; + const int size; std::vector>> buffer; - size_t write = 0; - size_t read = 0; + int write = 0; + int read = 0; }; } // namespace webrtc diff --git a/modules/audio_processing/aec3/mock/mock_echo_remover.h b/modules/audio_processing/aec3/mock/mock_echo_remover.h index 44d3778e6d..1cdb82560b 100644 --- a/modules/audio_processing/aec3/mock/mock_echo_remover.h +++ b/modules/audio_processing/aec3/mock/mock_echo_remover.h @@ -30,7 +30,7 @@ class MockEchoRemover : public EchoRemover { void(const rtc::Optional& echo_path_delay_samples, const EchoPathVariability& echo_path_variability, bool capture_signal_saturation, - const RenderBuffer& render_buffer, + RenderBuffer* render_buffer, std::vector>* capture)); MOCK_METHOD1(UpdateEchoLeakageStatus, void(bool leakage_detected)); diff --git a/modules/audio_processing/aec3/mock/mock_render_delay_buffer.h b/modules/audio_processing/aec3/mock/mock_render_delay_buffer.h index 02ac6316ca..15320e5616 100644 --- a/modules/audio_processing/aec3/mock/mock_render_delay_buffer.h +++ b/modules/audio_processing/aec3/mock/mock_render_delay_buffer.h @@ -48,18 +48,17 @@ class MockRenderDelayBuffer : public RenderDelayBuffer { MOCK_METHOD1(Insert, RenderDelayBuffer::BufferingEvent( const std::vector>& block)); - MOCK_METHOD0(PrepareCaptureCall, RenderDelayBuffer::BufferingEvent()); - MOCK_METHOD1(SetDelay, void(size_t delay)); - MOCK_CONST_METHOD0(Delay, size_t()); + MOCK_METHOD0(PrepareCaptureProcessing, RenderDelayBuffer::BufferingEvent()); + MOCK_METHOD1(SetDelay, bool(size_t delay)); + MOCK_CONST_METHOD0(Delay, rtc::Optional()); MOCK_CONST_METHOD0(MaxDelay, size_t()); - MOCK_CONST_METHOD0(MaxApiJitter, size_t()); - MOCK_CONST_METHOD0(IsBlockAvailable, bool()); - MOCK_CONST_METHOD0(GetRenderBuffer, const RenderBuffer&()); + MOCK_METHOD0(GetRenderBuffer, RenderBuffer*()); MOCK_CONST_METHOD0(GetDownsampledRenderBuffer, const DownsampledRenderBuffer&()); + MOCK_CONST_METHOD0(CausalDelay, bool()); private: - const RenderBuffer& FakeGetRenderBuffer() const { return render_buffer_; } + RenderBuffer* FakeGetRenderBuffer() { return &render_buffer_; } const DownsampledRenderBuffer& FakeGetDownsampledRenderBuffer() const { return downsampled_render_buffer_; } diff --git a/modules/audio_processing/aec3/mock/mock_render_delay_controller.h b/modules/audio_processing/aec3/mock/mock_render_delay_controller.h index b1f1cbe6cf..eecc46fcac 100644 --- a/modules/audio_processing/aec3/mock/mock_render_delay_controller.h +++ b/modules/audio_processing/aec3/mock/mock_render_delay_controller.h @@ -26,9 +26,10 @@ class MockRenderDelayController : public RenderDelayController { MOCK_METHOD0(Reset, void()); MOCK_METHOD1(SetDelay, void(size_t render_delay)); - MOCK_METHOD2(GetDelay, - size_t(const DownsampledRenderBuffer& render_buffer, - rtc::ArrayView capture)); + MOCK_METHOD2( + GetDelay, + rtc::Optional(const DownsampledRenderBuffer& render_buffer, + rtc::ArrayView capture)); MOCK_CONST_METHOD0(AlignmentHeadroomSamples, rtc::Optional()); }; diff --git a/modules/audio_processing/aec3/render_buffer.h b/modules/audio_processing/aec3/render_buffer.h index aa132b872d..507c082a62 100644 --- a/modules/audio_processing/aec3/render_buffer.h +++ b/modules/audio_processing/aec3/render_buffer.h @@ -64,7 +64,7 @@ class RenderBuffer { private: const MatrixBuffer* const block_buffer_; - VectorBuffer* spectrum_buffer_; + const VectorBuffer* const spectrum_buffer_; const FftBuffer* const fft_buffer_; const size_t spectral_sums_length_; std::array spectral_sums_; diff --git a/modules/audio_processing/aec3/render_delay_buffer.cc b/modules/audio_processing/aec3/render_delay_buffer.cc index 9640131885..4acc7848f1 100644 --- a/modules/audio_processing/aec3/render_delay_buffer.cc +++ b/modules/audio_processing/aec3/render_delay_buffer.cc @@ -37,51 +37,109 @@ class RenderDelayBufferImpl final : public RenderDelayBuffer { void Reset() override; BufferingEvent Insert(const std::vector>& block) override; - BufferingEvent PrepareCaptureCall() override; - void SetDelay(size_t delay) override; - size_t Delay() const override { return delay_; } + BufferingEvent PrepareCaptureProcessing() override; + bool SetDelay(size_t delay) override; + rtc::Optional Delay() const override { return delay_; } size_t MaxDelay() const override { return blocks_.buffer.size() - 1 - kBufferHeadroom; } - size_t MaxApiJitter() const override { return max_api_jitter_; } - const RenderBuffer& GetRenderBuffer() const override { - return echo_remover_buffer_; - } + RenderBuffer* GetRenderBuffer() override { return &echo_remover_buffer_; } const DownsampledRenderBuffer& GetDownsampledRenderBuffer() const override { return low_rate_; } + bool CausalDelay() const override; + private: static int instance_count_; std::unique_ptr data_dumper_; const Aec3Optimization optimization_; - const size_t api_call_jitter_blocks_; - const size_t min_echo_path_delay_blocks_; + const EchoCanceller3Config config_; const int sub_block_size_; MatrixBuffer blocks_; VectorBuffer spectra_; FftBuffer ffts_; - size_t delay_; - int max_api_jitter_ = 0; - int render_surplus_ = 0; - bool first_reset_occurred_ = false; + rtc::Optional delay_; + rtc::Optional internal_delay_; RenderBuffer echo_remover_buffer_; DownsampledRenderBuffer low_rate_; Decimator render_decimator_; const std::vector> zero_block_; const Aec3Fft fft_; - size_t capture_call_counter_ = 0; std::vector render_ds_; - int render_calls_in_a_row_ = 0; - void UpdateBuffersWithLatestBlock(size_t previous_write); - void IncreaseRead(); - void IncreaseInsert(); + int LowRateBufferOffset() const { return DelayEstimatorOffset(config_) >> 1; } + int MaxExternalDelayToInternalDelay(size_t delay) const; + void ApplyDelay(int delay); + void InsertBlock(const std::vector>& block, + int previous_write); 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& 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& 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; RenderDelayBufferImpl::RenderDelayBufferImpl(const EchoCanceller3Config& config, @@ -89,8 +147,7 @@ RenderDelayBufferImpl::RenderDelayBufferImpl(const EchoCanceller3Config& config, : data_dumper_( new ApmDataDumper(rtc::AtomicOps::Increment(&instance_count_))), optimization_(DetectOptimization()), - api_call_jitter_blocks_(config.delay.api_call_jitter_blocks), - min_echo_path_delay_blocks_(config.delay.min_echo_path_delay_blocks), + config_(config), sub_block_size_( static_cast(config.delay.down_sampling_factor > 0 ? kBlockSize / config.delay.down_sampling_factor @@ -101,7 +158,6 @@ RenderDelayBufferImpl::RenderDelayBufferImpl(const EchoCanceller3Config& config, kBlockSize), spectra_(blocks_.buffer.size(), kFftLengthBy2Plus1), ffts_(blocks_.buffer.size()), - delay_(min_echo_path_delay_blocks_), echo_remover_buffer_(kAdaptiveFilterLength, &blocks_, &spectra_, &ffts_), low_rate_(GetDownSampledBufferSize(config.delay.down_sampling_factor, config.delay.num_filters)), @@ -111,127 +167,153 @@ RenderDelayBufferImpl::RenderDelayBufferImpl(const EchoCanceller3Config& config, render_ds_(sub_block_size_, 0.f) { RTC_DCHECK_EQ(blocks_.buffer.size(), ffts_.buffer.size()); RTC_DCHECK_EQ(spectra_.buffer.size(), ffts_.buffer.size()); + + // Necessary condition to avoid unrecoverable echp due to noncausal alignment. + RTC_DCHECK_EQ(DelayEstimatorOffset(config_), LowRateBufferOffset() * 2); Reset(); - first_reset_occurred_ = false; } RenderDelayBufferImpl::~RenderDelayBufferImpl() = default; +// Resets the buffer delays and clears the reported delays. void RenderDelayBufferImpl::Reset() { - delay_ = min_echo_path_delay_blocks_; - const int offset1 = std::max( - std::min(api_call_jitter_blocks_, min_echo_path_delay_blocks_), 1); - const int offset2 = static_cast(delay_ + offset1); - const int offset3 = offset1 * sub_block_size_; - low_rate_.read = low_rate_.OffsetIndex(low_rate_.write, offset3); - blocks_.read = blocks_.OffsetIndex(blocks_.write, -offset2); - spectra_.read = spectra_.OffsetIndex(spectra_.write, offset2); - ffts_.read = ffts_.OffsetIndex(ffts_.write, offset2); - render_surplus_ = 0; - first_reset_occurred_ = true; + // Pre-fill the low rate buffer (which is used for delay estimation) to add + // headroom for the allowed api call jitter. + low_rate_.read = low_rate_.OffsetIndex( + low_rate_.write, LowRateBufferOffset() * sub_block_size_); + + // Set the render buffer delays to the default delay. + ApplyDelay(config_.delay.default_delay); + + // Unset the delays which are set by ApplyConfig. + delay_ = rtc::nullopt; + internal_delay_ = rtc::nullopt; } +// Inserts a new block into the render buffers. RenderDelayBuffer::BufferingEvent RenderDelayBufferImpl::Insert( const std::vector>& block) { - RTC_DCHECK_EQ(block.size(), blocks_.buffer[0].size()); - RTC_DCHECK_EQ(block[0].size(), blocks_.buffer[0][0].size()); - BufferingEvent event = BufferingEvent::kNone; + // Increase the write indices to where the new blocks should be written. + const int previous_write = blocks_.write; + IncreaseWriteIndices(sub_block_size_, &blocks_, &spectra_, &ffts_, + &low_rate_); - ++render_surplus_; - if (first_reset_occurred_) { - ++render_calls_in_a_row_; - max_api_jitter_ = std::max(max_api_jitter_, render_calls_in_a_row_); + // Allow overrun and do a reset when render overrun occurrs due to more render + // data being inserted than capture data is received. + BufferingEvent event = RenderOverrun(blocks_, low_rate_) + ? 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; } -RenderDelayBuffer::BufferingEvent RenderDelayBufferImpl::PrepareCaptureCall() { +// Prepares the render buffers for processing another capture block. +RenderDelayBuffer::BufferingEvent +RenderDelayBufferImpl::PrepareCaptureProcessing() { 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; } else { - IncreaseRead(); - } - --render_surplus_; + // Increase the read indices in the render buffers to point to the most + // recent block to use in the capture processing. + IncreaseReadIndices(internal_delay_, sub_block_size_, &blocks_, &spectra_, + &ffts_, &low_rate_); - echo_remover_buffer_.UpdateSpectralSum(); - - if (render_surplus_ >= static_cast(api_call_jitter_blocks_)) { - event = BufferingEvent::kApiCallSkew; - RTC_LOG(LS_WARNING) << "Api call skew detected at " << capture_call_counter_ - << "."; + // 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 + // increase saves one unit of allowed skew. Note that the skew check only + // should need to be one-sided as one of the skew directions results in an + // 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; } -void RenderDelayBufferImpl::SetDelay(size_t delay) { - if (delay_ == delay) { - return; +// Sets the delay and returns a bool indicating whether the delay was changed. +bool RenderDelayBufferImpl::SetDelay(size_t delay) { + if (delay_ && *delay_ == delay) { + return false; } - - const int delta_delay = static_cast(delay_) - static_cast(delay); delay_ = delay; - if (delay_ > MaxDelay()) { - delay_ = std::min(MaxDelay(), delay); - RTC_NOTREACHED(); - } - // Recompute the read indices according to the set delay. - blocks_.UpdateReadIndex(delta_delay); - spectra_.UpdateReadIndex(-delta_delay); - ffts_.UpdateReadIndex(-delta_delay); + // Compute the internal delay and limit the delay to the allowed range. + int internal_delay = MaxExternalDelayToInternalDelay(*delay_); + internal_delay_ = + std::min(MaxDelay(), static_cast(std::max(internal_delay, 0))); + + // Apply the delay to the buffers. + ApplyDelay(*internal_delay_); + return true; } -void RenderDelayBufferImpl::UpdateBuffersWithLatestBlock( - size_t previous_write) { - render_decimator_.Decimate(blocks_.buffer[blocks_.write][0], render_ds_); - std::copy(render_ds_.rbegin(), render_ds_.rend(), - low_rate_.buffer.begin() + low_rate_.write); +// Returns whether the specified delay is causal. +bool RenderDelayBufferImpl::CausalDelay() const { + return !internal_delay_ || + *internal_delay_ >= + static_cast(config_.delay.min_echo_path_delay_blocks); +} - fft_.PaddedFft(blocks_.buffer[blocks_.write][0], - blocks_.buffer[previous_write][0], &ffts_.buffer[ffts_.write]); +// Maps the externally computed delay to the delay used internally. +int RenderDelayBufferImpl::MaxExternalDelayToInternalDelay( + size_t external_delay_blocks) const { + const int latency = BufferLatency(low_rate_); + 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(external_delay_blocks) - + DelayEstimatorOffset(config_); +} - ffts_.buffer[ffts_.write].Spectrum(optimization_, - spectra_.buffer[spectra_.write]); -}; +// Set the read indices according to the delay. +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); +} -void RenderDelayBufferImpl::IncreaseRead() { - low_rate_.UpdateReadIndex(-sub_block_size_); - blocks_.IncReadIndex(); - spectra_.DecReadIndex(); - ffts_.DecReadIndex(); -}; +// Inserts a block into the render buffers. +void RenderDelayBufferImpl::InsertBlock( + const std::vector>& 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::IncreaseInsert() { - low_rate_.UpdateWriteIndex(-sub_block_size_); - blocks_.IncWriteIndex(); - spectra_.DecWriteIndex(); - ffts_.DecWriteIndex(); -}; + render_decimator_.Decimate(block[0], ds); + std::copy(ds.rbegin(), ds.rend(), lr.buffer.begin() + lr.write); + fft_.PaddedFft(block[0], b.buffer[previous_write][0], &f.buffer[f.write]); + f.buffer[f.write].Spectrum(optimization_, s.buffer[s.write]); +} } // namespace +int RenderDelayBuffer::RenderDelayBuffer::DelayEstimatorOffset( + const EchoCanceller3Config& config) { + return config.delay.api_call_jitter_blocks * 2; +} + RenderDelayBuffer* RenderDelayBuffer::Create(const EchoCanceller3Config& config, size_t num_bands) { return new RenderDelayBufferImpl(config, num_bands); diff --git a/modules/audio_processing/aec3/render_delay_buffer.h b/modules/audio_processing/aec3/render_delay_buffer.h index 0261e841c8..b7718aa2c3 100644 --- a/modules/audio_processing/aec3/render_delay_buffer.h +++ b/modules/audio_processing/aec3/render_delay_buffer.h @@ -49,25 +49,29 @@ class RenderDelayBuffer { // Updates the buffers one step based on the specified buffer delay. Returns // an enum indicating whether there was a special event that occurred. - virtual BufferingEvent PrepareCaptureCall() = 0; + virtual BufferingEvent PrepareCaptureProcessing() = 0; - // Sets the buffer delay. - virtual void SetDelay(size_t delay) = 0; + // Sets the buffer delay and returns a bool indicating whether the delay + // changed. + virtual bool SetDelay(size_t delay) = 0; // Gets the buffer delay. - virtual size_t Delay() const = 0; + virtual rtc::Optional Delay() const = 0; // Gets the buffer delay. virtual size_t MaxDelay() const = 0; - // Gets the observed jitter in the render and capture call sequence. - virtual size_t MaxApiJitter() const = 0; - // Returns the render buffer for the echo remover. - virtual const RenderBuffer& GetRenderBuffer() const = 0; + virtual RenderBuffer* GetRenderBuffer() = 0; // Returns the downsampled render buffer. 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 diff --git a/modules/audio_processing/aec3/render_delay_buffer_unittest.cc b/modules/audio_processing/aec3/render_delay_buffer_unittest.cc index 9b99a8e9b3..fb9c48d0e8 100644 --- a/modules/audio_processing/aec3/render_delay_buffer_unittest.cc +++ b/modules/audio_processing/aec3/render_delay_buffer_unittest.cc @@ -45,12 +45,16 @@ TEST(RenderDelayBuffer, BufferOverflow) { EXPECT_EQ(RenderDelayBuffer::BufferingEvent::kNone, delay_buffer->Insert(block_to_insert)); } + bool overrun_occurred = false; for (size_t k = 0; k < 1000; ++k) { - delay_buffer->Insert(block_to_insert); + RenderDelayBuffer::BufferingEvent event = + delay_buffer->Insert(block_to_insert); + overrun_occurred = + overrun_occurred || + RenderDelayBuffer::BufferingEvent::kRenderOverrun == event; } - EXPECT_EQ(RenderDelayBuffer::BufferingEvent::kRenderOverrun, - delay_buffer->Insert(block_to_insert)); + EXPECT_TRUE(overrun_occurred); } } @@ -63,7 +67,7 @@ TEST(RenderDelayBuffer, AvailableBlock) { kNumBands, std::vector(kBlockSize, 1.f)); EXPECT_EQ(RenderDelayBuffer::BufferingEvent::kNone, delay_buffer->Insert(input_block)); - delay_buffer->PrepareCaptureCall(); + delay_buffer->PrepareCaptureProcessing(); } // Verifies the SetDelay method. @@ -71,11 +75,12 @@ TEST(RenderDelayBuffer, SetDelay) { EchoCanceller3Config config; std::unique_ptr delay_buffer( 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; ++delay) { delay_buffer->SetDelay(delay); - EXPECT_EQ(delay, delay_buffer->Delay()); + ASSERT_TRUE(delay_buffer->Delay()); + EXPECT_EQ(delay, *delay_buffer->Delay()); } } diff --git a/modules/audio_processing/aec3/render_delay_controller.cc b/modules/audio_processing/aec3/render_delay_controller.cc index 05121f26f0..e37207ec54 100644 --- a/modules/audio_processing/aec3/render_delay_controller.cc +++ b/modules/audio_processing/aec3/render_delay_controller.cc @@ -28,12 +28,13 @@ namespace { class RenderDelayControllerImpl final : public RenderDelayController { public: RenderDelayControllerImpl(const EchoCanceller3Config& config, + int non_causal_offset, int sample_rate_hz); ~RenderDelayControllerImpl() override; void Reset() override; void SetDelay(size_t render_delay) override; - size_t GetDelay(const DownsampledRenderBuffer& render_buffer, - rtc::ArrayView capture) override; + rtc::Optional GetDelay(const DownsampledRenderBuffer& render_buffer, + rtc::ArrayView capture) override; rtc::Optional AlignmentHeadroomSamples() const override { return headroom_samples_; } @@ -41,32 +42,30 @@ class RenderDelayControllerImpl final : public RenderDelayController { private: static int instance_count_; std::unique_ptr data_dumper_; - const size_t min_echo_path_delay_; - const size_t default_delay_; - size_t delay_; + rtc::Optional delay_; EchoPathDelayEstimator delay_estimator_; - size_t blocks_since_last_delay_estimate_ = 300000; - int echo_path_delay_samples_; size_t align_call_counter_ = 0; rtc::Optional headroom_samples_; - std::vector capture_delay_buffer_; - int capture_delay_buffer_index_ = 0; + std::vector delay_buf_; + int delay_buf_index_ = 0; RenderDelayControllerMetrics metrics_; RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(RenderDelayControllerImpl); }; -size_t ComputeNewBufferDelay(size_t current_delay, - size_t echo_path_delay_samples) { +size_t ComputeNewBufferDelay(rtc::Optional current_delay, + size_t delay_samples) { // 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; // Compute the buffer delay increase required to achieve the desired latency. size_t new_delay = std::max(echo_path_delay_blocks - kDelayHeadroomBlocks, 0); // Add hysteresis. - if (new_delay == current_delay + 1) { - new_delay = current_delay; + if (current_delay) { + if (new_delay == *current_delay + 1) { + new_delay = *current_delay; + } } return new_delay; @@ -76,32 +75,24 @@ int RenderDelayControllerImpl::instance_count_ = 0; RenderDelayControllerImpl::RenderDelayControllerImpl( const EchoCanceller3Config& config, + int non_causal_offset, int sample_rate_hz) : data_dumper_( 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), - echo_path_delay_samples_(default_delay_ * kBlockSize), - capture_delay_buffer_( - kBlockSize * (config.delay.api_call_jitter_blocks + 2), - 0.f) { + delay_buf_(kBlockSize * non_causal_offset, 0.f) { RTC_DCHECK(ValidFullBandRate(sample_rate_hz)); delay_estimator_.LogDelayEstimationProperties(sample_rate_hz, - capture_delay_buffer_.size()); + delay_buf_.size()); } RenderDelayControllerImpl::~RenderDelayControllerImpl() = default; void RenderDelayControllerImpl::Reset() { - delay_ = default_delay_; - blocks_since_last_delay_estimate_ = 300000; - echo_path_delay_samples_ = delay_ * kBlockSize; + delay_ = rtc::nullopt; align_call_counter_ = 0; 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(); } @@ -114,60 +105,43 @@ void RenderDelayControllerImpl::SetDelay(size_t render_delay) { } } -size_t RenderDelayControllerImpl::GetDelay( +rtc::Optional RenderDelayControllerImpl::GetDelay( const DownsampledRenderBuffer& render_buffer, rtc::ArrayView capture) { RTC_DCHECK_EQ(kBlockSize, capture.size()); - ++align_call_counter_; - // Estimate the delay with a delayed capture signal in order to catch - // noncausal delays. - RTC_DCHECK_LT(capture_delay_buffer_index_ + kBlockSize - 1, - capture_delay_buffer_.size()); - const rtc::Optional echo_path_delay_samples_shifted = - delay_estimator_.EstimateDelay( - render_buffer, - rtc::ArrayView( - &capture_delay_buffer_[capture_delay_buffer_index_], kBlockSize)); + // Estimate the delay with a delayed capture. + RTC_DCHECK_LT(delay_buf_index_ + kBlockSize - 1, delay_buf_.size()); + rtc::ArrayView capture_delayed(&delay_buf_[delay_buf_index_], + kBlockSize); + auto delay_samples = + delay_estimator_.EstimateDelay(render_buffer, capture_delayed); + std::copy(capture.begin(), capture.end(), - capture_delay_buffer_.begin() + capture_delay_buffer_index_); - capture_delay_buffer_index_ = - (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(*echo_path_delay_samples_shifted) - - static_cast(capture_delay_buffer_.size()); - echo_path_delay_samples_ = std::max(0, echo_path_delay_samples_corrected); + delay_buf_.begin() + delay_buf_index_); + delay_buf_index_ = (delay_buf_index_ + kBlockSize) % delay_buf_.size(); + if (delay_samples) { // Compute and set new render delay buffer delay. - const size_t new_delay = - ComputeNewBufferDelay(delay_, echo_path_delay_samples_); if (align_call_counter_ > kNumBlocksPerSecond) { - delay_ = new_delay; - + delay_ = ComputeNewBufferDelay(delay_, static_cast(*delay_samples)); // Update render delay buffer headroom. - if (echo_path_delay_samples_corrected >= 0) { - const int headroom = echo_path_delay_samples_ - delay_ * kBlockSize; - RTC_DCHECK_LE(0, headroom); - headroom_samples_ = headroom; - } else { - headroom_samples_ = rtc::nullopt; - } + const int headroom = + static_cast(*delay_samples) - *delay_ * kBlockSize; + RTC_DCHECK_LE(0, headroom); + headroom_samples_ = headroom; } - metrics_.Update(echo_path_delay_samples_, delay_); + metrics_.Update(static_cast(*delay_samples), delay_ ? *delay_ : 0); } else { - metrics_.Update(rtc::nullopt, delay_); + metrics_.Update(rtc::nullopt, delay_ ? *delay_ : 0); } - data_dumper_->DumpRaw("aec3_render_delay_controller_delay", 1, - &echo_path_delay_samples_); - data_dumper_->DumpRaw("aec3_render_delay_controller_buffer_delay", delay_); + data_dumper_->DumpRaw("aec3_render_delay_controller_delay", + delay_samples ? *delay_samples : 0); + data_dumper_->DumpRaw("aec3_render_delay_controller_buffer_delay", + delay_ ? *delay_ : 0); return delay_; } @@ -176,8 +150,10 @@ size_t RenderDelayControllerImpl::GetDelay( RenderDelayController* RenderDelayController::Create( const EchoCanceller3Config& config, + int non_causal_offset, int sample_rate_hz) { - return new RenderDelayControllerImpl(config, sample_rate_hz); + return new RenderDelayControllerImpl(config, non_causal_offset, + sample_rate_hz); } } // namespace webrtc diff --git a/modules/audio_processing/aec3/render_delay_controller.h b/modules/audio_processing/aec3/render_delay_controller.h index e971b5656a..f5e1bdcb68 100644 --- a/modules/audio_processing/aec3/render_delay_controller.h +++ b/modules/audio_processing/aec3/render_delay_controller.h @@ -24,6 +24,7 @@ namespace webrtc { class RenderDelayController { public: static RenderDelayController* Create(const EchoCanceller3Config& config, + int non_causal_offset, int sample_rate_hz); virtual ~RenderDelayController() = default; @@ -34,8 +35,9 @@ class RenderDelayController { virtual void SetDelay(size_t render_delay) = 0; // Aligns the render buffer content with the capture signal. - virtual size_t GetDelay(const DownsampledRenderBuffer& render_buffer, - rtc::ArrayView capture) = 0; + virtual rtc::Optional GetDelay( + const DownsampledRenderBuffer& render_buffer, + rtc::ArrayView capture) = 0; // Returns an approximate value for the headroom in the buffer alignment. virtual rtc::Optional AlignmentHeadroomSamples() const = 0; diff --git a/modules/audio_processing/aec3/render_delay_controller_unittest.cc b/modules/audio_processing/aec3/render_delay_controller_unittest.cc index 0f5432b728..e7d85ed60e 100644 --- a/modules/audio_processing/aec3/render_delay_controller_unittest.cc +++ b/modules/audio_processing/aec3/render_delay_controller_unittest.cc @@ -58,7 +58,8 @@ TEST(RenderDelayController, NoRenderSignal) { std::unique_ptr delay_buffer( RenderDelayBuffer::Create(config, NumBandsForRate(rate))); std::unique_ptr delay_controller( - RenderDelayController::Create(config, rate)); + RenderDelayController::Create( + config, RenderDelayBuffer::DelayEstimatorOffset(config), rate)); for (size_t k = 0; k < 100; ++k) { EXPECT_EQ(config.delay.min_echo_path_delay_blocks, delay_controller->GetDelay( @@ -72,7 +73,7 @@ TEST(RenderDelayController, NoRenderSignal) { // Verifies the basic API call sequence. TEST(RenderDelayController, BasicApiCalls) { std::vector capture_block(kBlockSize, 0.f); - size_t delay_blocks = 0; + rtc::Optional delay_blocks = 0; for (size_t num_matched_filters = 4; num_matched_filters == 10; num_matched_filters++) { for (auto down_sampling_factor : kDownSamplingFactors) { @@ -85,13 +86,17 @@ TEST(RenderDelayController, BasicApiCalls) { std::unique_ptr render_delay_buffer( RenderDelayBuffer::Create(config, NumBandsForRate(rate))); std::unique_ptr delay_controller( - RenderDelayController::Create(EchoCanceller3Config(), rate)); + RenderDelayController::Create( + EchoCanceller3Config(), + RenderDelayBuffer::DelayEstimatorOffset(config), rate)); for (size_t k = 0; k < 10; ++k) { render_delay_buffer->Insert(render_block); - render_delay_buffer->PrepareCaptureCall(); + render_delay_buffer->PrepareCaptureProcessing(); + delay_blocks = delay_controller->GetDelay( render_delay_buffer->GetDownsampledRenderBuffer(), capture_block); } + EXPECT_TRUE(delay_blocks); EXPECT_FALSE(delay_controller->AlignmentHeadroomSamples()); EXPECT_EQ(config.delay.min_echo_path_delay_blocks, delay_blocks); } @@ -104,7 +109,6 @@ TEST(RenderDelayController, BasicApiCalls) { TEST(RenderDelayController, Alignment) { Random random_generator(42U); std::vector capture_block(kBlockSize, 0.f); - size_t delay_blocks = 0; for (size_t num_matched_filters = 4; num_matched_filters == 10; num_matched_filters++) { for (auto down_sampling_factor : kDownSamplingFactors) { @@ -117,21 +121,25 @@ TEST(RenderDelayController, Alignment) { NumBandsForRate(rate), std::vector(kBlockSize, 0.f)); for (size_t delay_samples : {15, 50, 150, 200, 800, 4000}) { + rtc::Optional delay_blocks; SCOPED_TRACE(ProduceDebugText(rate, delay_samples)); std::unique_ptr render_delay_buffer( RenderDelayBuffer::Create(config, NumBandsForRate(rate))); std::unique_ptr delay_controller( - RenderDelayController::Create(config, rate)); + RenderDelayController::Create( + config, RenderDelayBuffer::DelayEstimatorOffset(config), + rate)); DelayBuffer signal_delay_buffer(delay_samples); for (size_t k = 0; k < (400 + delay_samples / kBlockSize); ++k) { RandomizeSampleVector(&random_generator, render_block[0]); signal_delay_buffer.Delay(render_block[0], capture_block); render_delay_buffer->Insert(render_block); - render_delay_buffer->PrepareCaptureCall(); + render_delay_buffer->PrepareCaptureProcessing(); delay_blocks = delay_controller->GetDelay( render_delay_buffer->GetDownsampledRenderBuffer(), capture_block); } + ASSERT_TRUE(!!delay_blocks); constexpr int kDelayHeadroomBlocks = 1; size_t expected_delay_blocks = @@ -143,7 +151,7 @@ TEST(RenderDelayController, Alignment) { const rtc::Optional headroom_samples = delay_controller->AlignmentHeadroomSamples(); ASSERT_TRUE(headroom_samples); - EXPECT_NEAR(delay_samples - delay_blocks * kBlockSize, + EXPECT_NEAR(delay_samples - *delay_blocks * kBlockSize, *headroom_samples, 4); } } @@ -155,7 +163,6 @@ TEST(RenderDelayController, Alignment) { // delays. TEST(RenderDelayController, NonCausalAlignment) { Random random_generator(42U); - size_t delay_blocks = 0; for (size_t num_matched_filters = 4; num_matched_filters == 10; num_matched_filters++) { for (auto down_sampling_factor : kDownSamplingFactors) { @@ -169,24 +176,27 @@ TEST(RenderDelayController, NonCausalAlignment) { NumBandsForRate(rate), std::vector(kBlockSize, 0.f)); for (int delay_samples : {-15, -50, -150, -200}) { + rtc::Optional delay_blocks; SCOPED_TRACE(ProduceDebugText(rate, -delay_samples)); std::unique_ptr render_delay_buffer( RenderDelayBuffer::Create(config, NumBandsForRate(rate))); std::unique_ptr delay_controller( - RenderDelayController::Create(EchoCanceller3Config(), rate)); + RenderDelayController::Create( + EchoCanceller3Config(), + RenderDelayBuffer::DelayEstimatorOffset(config), rate)); DelayBuffer signal_delay_buffer(-delay_samples); for (int k = 0; k < (400 - delay_samples / static_cast(kBlockSize)); ++k) { RandomizeSampleVector(&random_generator, capture_block[0]); signal_delay_buffer.Delay(capture_block[0], render_block[0]); render_delay_buffer->Insert(render_block); - render_delay_buffer->PrepareCaptureCall(); + render_delay_buffer->PrepareCaptureProcessing(); delay_blocks = delay_controller->GetDelay( render_delay_buffer->GetDownsampledRenderBuffer(), capture_block[0]); } - EXPECT_EQ(0u, delay_blocks); + ASSERT_FALSE(delay_blocks); const rtc::Optional headroom_samples = delay_controller->AlignmentHeadroomSamples(); @@ -212,12 +222,14 @@ TEST(RenderDelayController, AlignmentWithJitter) { std::vector> render_block( NumBandsForRate(rate), std::vector(kBlockSize, 0.f)); for (size_t delay_samples : {15, 50, 300, 800}) { - size_t delay_blocks = 0; + rtc::Optional delay_blocks; SCOPED_TRACE(ProduceDebugText(rate, delay_samples)); std::unique_ptr render_delay_buffer( RenderDelayBuffer::Create(config, NumBandsForRate(rate))); std::unique_ptr delay_controller( - RenderDelayController::Create(config, rate)); + RenderDelayController::Create( + config, RenderDelayBuffer::DelayEstimatorOffset(config), + rate)); DelayBuffer signal_delay_buffer(delay_samples); for (size_t j = 0; j < (1000 + delay_samples / kBlockSize) / 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); ++k) { - render_delay_buffer->PrepareCaptureCall(); + render_delay_buffer->PrepareCaptureProcessing(); delay_blocks = delay_controller->GetDelay( render_delay_buffer->GetDownsampledRenderBuffer(), capture_block_buffer[k]); @@ -248,12 +260,13 @@ TEST(RenderDelayController, AlignmentWithJitter) { 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 headroom_samples = delay_controller->AlignmentHeadroomSamples(); ASSERT_TRUE(headroom_samples); - EXPECT_NEAR(delay_samples - delay_blocks * kBlockSize, + EXPECT_NEAR(delay_samples - *delay_blocks * kBlockSize, *headroom_samples, 4); } } @@ -277,7 +290,8 @@ TEST(RenderDelayController, InitialHeadroom) { RenderDelayBuffer::Create(config, NumBandsForRate(rate))); std::unique_ptr delay_controller( - RenderDelayController::Create(config, rate)); + RenderDelayController::Create( + config, RenderDelayBuffer::DelayEstimatorOffset(config), rate)); EXPECT_FALSE(delay_controller->AlignmentHeadroomSamples()); } } @@ -296,7 +310,9 @@ TEST(RenderDelayController, WrongCaptureSize) { RenderDelayBuffer::Create(config, NumBandsForRate(rate))); EXPECT_DEATH( std::unique_ptr( - RenderDelayController::Create(EchoCanceller3Config(), rate)) + RenderDelayController::Create( + EchoCanceller3Config(), + RenderDelayBuffer::DelayEstimatorOffset(config), rate)) ->GetDelay(render_delay_buffer->GetDownsampledRenderBuffer(), block), ""); @@ -313,8 +329,9 @@ TEST(RenderDelayController, DISABLED_WrongSampleRate) { std::unique_ptr render_delay_buffer( RenderDelayBuffer::Create(config, NumBandsForRate(rate))); EXPECT_DEATH( - std::unique_ptr( - RenderDelayController::Create(EchoCanceller3Config(), rate)), + std::unique_ptr(RenderDelayController::Create( + EchoCanceller3Config(), + RenderDelayBuffer::DelayEstimatorOffset(config), rate)), ""); } } diff --git a/modules/audio_processing/aec3/render_signal_analyzer_unittest.cc b/modules/audio_processing/aec3/render_signal_analyzer_unittest.cc index eba28cf9c6..8b68960bee 100644 --- a/modules/audio_processing/aec3/render_signal_analyzer_unittest.cc +++ b/modules/audio_processing/aec3/render_signal_analyzer_unittest.cc @@ -70,9 +70,9 @@ TEST(RenderSignalAnalyzer, NoFalseDetectionOfNarrowBands) { if (k == 0) { 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(0)); } @@ -109,9 +109,9 @@ TEST(RenderSignalAnalyzer, NarrowBandDetection) { if (k == 0) { 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(0) : rtc::nullopt); } }; diff --git a/modules/audio_processing/aec3/residual_echo_estimator_unittest.cc b/modules/audio_processing/aec3/residual_echo_estimator_unittest.cc index 92669c873d..06b7db68b1 100644 --- a/modules/audio_processing/aec3/residual_echo_estimator_unittest.cc +++ b/modules/audio_processing/aec3/residual_echo_estimator_unittest.cc @@ -32,7 +32,7 @@ TEST(ResidualEchoEstimator, NullResidualEchoPowerOutput) { std::array S2_linear; std::array Y2; EXPECT_DEATH(ResidualEchoEstimator(EchoCanceller3Config{}) - .Estimate(aec_state, render_delay_buffer->GetRenderBuffer(), + .Estimate(aec_state, *render_delay_buffer->GetRenderBuffer(), S2_linear, Y2, nullptr), ""); } @@ -89,13 +89,13 @@ TEST(ResidualEchoEstimator, DISABLED_BasicTest) { if (k == 0) { render_delay_buffer->Reset(); } - render_delay_buffer->PrepareCaptureCall(); + render_delay_buffer->PrepareCaptureProcessing(); 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); - estimator.Estimate(aec_state, render_delay_buffer->GetRenderBuffer(), + estimator.Estimate(aec_state, *render_delay_buffer->GetRenderBuffer(), S2_linear, Y2, &R2); } std::for_each(R2.begin(), R2.end(), diff --git a/modules/audio_processing/aec3/shadow_filter_update_gain_unittest.cc b/modules/audio_processing/aec3/shadow_filter_update_gain_unittest.cc index 41645ae8cd..fa6713bfbb 100644 --- a/modules/audio_processing/aec3/shadow_filter_update_gain_unittest.cc +++ b/modules/audio_processing/aec3/shadow_filter_update_gain_unittest.cc @@ -42,6 +42,7 @@ void RunFilterUpdateTest(int num_blocks_to_process, EchoCanceller3Config config; config.delay.min_echo_path_delay_blocks = 0; + config.delay.default_delay = 1; std::unique_ptr render_delay_buffer( RenderDelayBuffer::Create(config, 3)); @@ -76,12 +77,13 @@ void RunFilterUpdateTest(int num_blocks_to_process, if (k == 0) { 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); - shadow_filter.Filter(render_delay_buffer->GetRenderBuffer(), &S); + shadow_filter.Filter(*render_delay_buffer->GetRenderBuffer(), &S); fft.Ifft(S, &s); std::transform(y.begin(), y.end(), s.begin() + kFftLengthBy2, e_shadow.begin(), @@ -90,10 +92,10 @@ void RunFilterUpdateTest(int num_blocks_to_process, [](float& a) { a = rtc::SafeClamp(a, -32768.f, 32767.f); }); 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, 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()); diff --git a/modules/audio_processing/aec3/subtractor_unittest.cc b/modules/audio_processing/aec3/subtractor_unittest.cc index bd8c7777c9..a9fc9c2c1f 100644 --- a/modules/audio_processing/aec3/subtractor_unittest.cc +++ b/modules/audio_processing/aec3/subtractor_unittest.cc @@ -35,6 +35,7 @@ float RunSubtractorTest(int num_blocks_to_process, SubtractorOutput output; EchoCanceller3Config config; config.delay.min_echo_path_delay_blocks = 0; + config.delay.default_delay = 1; std::unique_ptr render_delay_buffer( RenderDelayBuffer::Create(config, 3)); RenderSignalAnalyzer render_signal_analyzer; @@ -61,8 +62,9 @@ float RunSubtractorTest(int num_blocks_to_process, if (k == 0) { render_delay_buffer->Reset(); } - render_delay_buffer->PrepareCaptureCall(); - render_signal_analyzer.Update(render_delay_buffer->GetRenderBuffer(), + render_delay_buffer->PrepareCaptureProcessing(); + render_delay_buffer->GetRenderBuffer()->UpdateSpectralSum(); + render_signal_analyzer.Update(*render_delay_buffer->GetRenderBuffer(), aec_state.FilterDelay()); // Handle echo path changes. @@ -73,7 +75,7 @@ float RunSubtractorTest(int num_blocks_to_process, true, EchoPathVariability::DelayAdjustment::kNewDetectedDelay, false)); } - subtractor.Process(render_delay_buffer->GetRenderBuffer(), y, + subtractor.Process(*render_delay_buffer->GetRenderBuffer(), y, render_signal_analyzer, aec_state, &output); aec_state.HandleEchoPathChange(EchoPathVariability( @@ -82,7 +84,7 @@ float RunSubtractorTest(int num_blocks_to_process, subtractor.FilterImpulseResponse(), subtractor.ConvergedFilter(), rtc::Optional(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); } @@ -122,7 +124,7 @@ TEST(Subtractor, DISABLED_NullOutput) { RenderSignalAnalyzer render_signal_analyzer; std::vector 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, AecState(EchoCanceller3Config{}), nullptr), ""); @@ -138,7 +140,7 @@ TEST(Subtractor, WrongCaptureSize) { std::vector y(kBlockSize - 1, 0.f); SubtractorOutput output; - EXPECT_DEATH(subtractor.Process(render_delay_buffer->GetRenderBuffer(), y, + EXPECT_DEATH(subtractor.Process(*render_delay_buffer->GetRenderBuffer(), y, render_signal_analyzer, AecState(EchoCanceller3Config{}), &output), ""); diff --git a/modules/audio_processing/aec3/suppression_gain_unittest.cc b/modules/audio_processing/aec3/suppression_gain_unittest.cc index 2b5e702e31..39a7be063f 100644 --- a/modules/audio_processing/aec3/suppression_gain_unittest.cc +++ b/modules/audio_processing/aec3/suppression_gain_unittest.cc @@ -71,7 +71,7 @@ TEST(SuppressionGain, BasicGainComputation) { s.fill(10.f); aec_state.Update( subtractor.FilterFrequencyResponse(), subtractor.FilterImpulseResponse(), - subtractor.ConvergedFilter(), 10, render_delay_buffer->GetRenderBuffer(), + subtractor.ConvergedFilter(), 10, *render_delay_buffer->GetRenderBuffer(), E2, Y2, x[0], s, false); suppression_gain.GetGain(E2, R2, N2, analyzer, aec_state, x, &high_bands_gain, &g); @@ -88,14 +88,14 @@ TEST(SuppressionGain, BasicGainComputation) { aec_state.Update( subtractor.FilterFrequencyResponse(), 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) { aec_state.Update( subtractor.FilterFrequencyResponse(), 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, &high_bands_gain, &g); } @@ -111,7 +111,7 @@ TEST(SuppressionGain, BasicGainComputation) { aec_state.Update( subtractor.FilterFrequencyResponse(), 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, &high_bands_gain, &g); } diff --git a/modules/audio_processing/aec3/vector_buffer.cc b/modules/audio_processing/aec3/vector_buffer.cc index 5fd664644b..f491168d9a 100644 --- a/modules/audio_processing/aec3/vector_buffer.cc +++ b/modules/audio_processing/aec3/vector_buffer.cc @@ -15,7 +15,8 @@ namespace webrtc { VectorBuffer::VectorBuffer(size_t size, size_t height) - : size(size), buffer(size, std::vector(height, 0.f)) { + : size(static_cast(size)), + buffer(size, std::vector(height, 0.f)) { for (auto& c : buffer) { std::fill(c.begin(), c.end(), 0.f); } diff --git a/modules/audio_processing/aec3/vector_buffer.h b/modules/audio_processing/aec3/vector_buffer.h index 9dce1ef65b..a7f9932545 100644 --- a/modules/audio_processing/aec3/vector_buffer.h +++ b/modules/audio_processing/aec3/vector_buffer.h @@ -23,17 +23,20 @@ struct VectorBuffer { VectorBuffer(size_t size, size_t height); ~VectorBuffer(); - size_t IncIndex(size_t index) { - return index < buffer.size() - 1 ? index + 1 : 0; + int IncIndex(int index) const { + RTC_DCHECK_EQ(buffer.size(), static_cast(size)); + return index < size - 1 ? index + 1 : 0; } - size_t DecIndex(size_t index) { - return index > 0 ? index - 1 : buffer.size() - 1; + int DecIndex(int index) const { + RTC_DCHECK_EQ(buffer.size(), static_cast(size)); + return index > 0 ? index - 1 : size - 1; } - size_t OffsetIndex(size_t index, int offset) { - RTC_DCHECK_GE(buffer.size(), offset); - return (buffer.size() + index + offset) % buffer.size(); + int OffsetIndex(int index, int offset) const { + RTC_DCHECK_GE(size, offset); + RTC_DCHECK_EQ(buffer.size(), static_cast(size)); + return (size + index + offset) % size; } void UpdateWriteIndex(int offset) { write = OffsetIndex(write, offset); } @@ -43,10 +46,10 @@ struct VectorBuffer { void IncReadIndex() { read = IncIndex(read); } void DecReadIndex() { read = DecIndex(read); } - size_t size; + const int size; std::vector> buffer; - size_t write = 0; - size_t read = 0; + int write = 0; + int read = 0; }; } // namespace webrtc