| /* |
| * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. |
| * |
| * Use of this source code is governed by a BSD-style license |
| * that can be found in the LICENSE file in the root of the source |
| * tree. An additional intellectual property rights grant can be found |
| * in the file PATENTS. All contributing project authors may |
| * be found in the AUTHORS file in the root of the source tree. |
| */ |
| |
| #include "modules/audio_processing/aec/aec_core.h" |
| #include "modules/audio_processing/aec/echo_cancellation.h" |
| #include "rtc_base/numerics/safe_conversions.h" |
| #include "test/gtest.h" |
| namespace webrtc { |
| namespace { |
| |
| class SystemDelayTest : public ::testing::Test { |
| protected: |
| SystemDelayTest(); |
| void SetUp() override; |
| void TearDown() override; |
| |
| // Initialization of AEC handle with respect to |sample_rate_hz|. Since the |
| // device sample rate is unimportant we set that value to 48000 Hz. |
| void Init(int sample_rate_hz); |
| |
| // Makes one render call and one capture call in that specific order. |
| void RenderAndCapture(int device_buffer_ms); |
| |
| // Fills up the far-end buffer with respect to the default device buffer size. |
| size_t BufferFillUp(); |
| |
| // Runs and verifies the behavior in a stable startup procedure. |
| void RunStableStartup(); |
| |
| // Maps buffer size in ms into samples, taking the unprocessed frame into |
| // account. |
| int MapBufferSizeToSamples(int size_in_ms, bool extended_filter); |
| |
| void* handle_; |
| Aec* self_; |
| size_t samples_per_frame_; |
| // Dummy input/output speech data. |
| static const int kSamplesPerChunk = 160; |
| float far_[kSamplesPerChunk]; |
| float near_[kSamplesPerChunk]; |
| float out_[kSamplesPerChunk]; |
| const float* near_ptr_; |
| float* out_ptr_; |
| }; |
| |
| SystemDelayTest::SystemDelayTest() |
| : handle_(NULL), self_(NULL), samples_per_frame_(0) { |
| // Dummy input data are set with more or less arbitrary non-zero values. |
| for (int i = 0; i < kSamplesPerChunk; i++) { |
| far_[i] = 257.0; |
| near_[i] = 514.0; |
| } |
| memset(out_, 0, sizeof(out_)); |
| near_ptr_ = near_; |
| out_ptr_ = out_; |
| } |
| |
| void SystemDelayTest::SetUp() { |
| handle_ = WebRtcAec_Create(); |
| ASSERT_TRUE(handle_); |
| self_ = reinterpret_cast<Aec*>(handle_); |
| } |
| |
| void SystemDelayTest::TearDown() { |
| // Free AEC |
| WebRtcAec_Free(handle_); |
| handle_ = NULL; |
| } |
| |
| // In SWB mode nothing is added to the buffer handling with respect to |
| // functionality compared to WB. We therefore only verify behavior in NB and WB. |
| static const int kSampleRateHz[] = {8000, 16000}; |
| static const size_t kNumSampleRates = |
| sizeof(kSampleRateHz) / sizeof(*kSampleRateHz); |
| |
| // Default audio device buffer size used. |
| static const int kDeviceBufMs = 100; |
| |
| // Requirement for a stable device convergence time in ms. Should converge in |
| // less than |kStableConvergenceMs|. |
| static const int kStableConvergenceMs = 100; |
| |
| // Maximum convergence time in ms. This means that we should leave the startup |
| // phase after |kMaxConvergenceMs| independent of device buffer stability |
| // conditions. |
| static const int kMaxConvergenceMs = 500; |
| |
| void SystemDelayTest::Init(int sample_rate_hz) { |
| // Initialize AEC |
| EXPECT_EQ(0, WebRtcAec_Init(handle_, sample_rate_hz, 48000)); |
| EXPECT_EQ(0, WebRtcAec_system_delay(self_->aec)); |
| |
| // One frame equals 10 ms of data. |
| samples_per_frame_ = static_cast<size_t>(sample_rate_hz / 100); |
| } |
| |
| void SystemDelayTest::RenderAndCapture(int device_buffer_ms) { |
| EXPECT_EQ(0, WebRtcAec_BufferFarend(handle_, far_, samples_per_frame_)); |
| EXPECT_EQ(0, WebRtcAec_Process(handle_, &near_ptr_, 1, &out_ptr_, |
| samples_per_frame_, device_buffer_ms, 0)); |
| } |
| |
| size_t SystemDelayTest::BufferFillUp() { |
| // To make sure we have a full buffer when we verify stability we first fill |
| // up the far-end buffer with the same amount as we will report in through |
| // Process(). |
| size_t buffer_size = 0; |
| for (int i = 0; i < kDeviceBufMs / 10; i++) { |
| EXPECT_EQ(0, WebRtcAec_BufferFarend(handle_, far_, samples_per_frame_)); |
| buffer_size += samples_per_frame_; |
| EXPECT_EQ(static_cast<int>(buffer_size), |
| WebRtcAec_system_delay(self_->aec)); |
| } |
| return buffer_size; |
| } |
| |
| void SystemDelayTest::RunStableStartup() { |
| // To make sure we have a full buffer when we verify stability we first fill |
| // up the far-end buffer with the same amount as we will report in through |
| // Process(). |
| size_t buffer_size = BufferFillUp(); |
| |
| if (WebRtcAec_delay_agnostic_enabled(self_->aec) == 1) { |
| // In extended_filter mode we set the buffer size after the first processed |
| // 10 ms chunk. Hence, we don't need to wait for the reported system delay |
| // values to become stable. |
| RenderAndCapture(kDeviceBufMs); |
| buffer_size += samples_per_frame_; |
| EXPECT_EQ(0, self_->startup_phase); |
| } else { |
| // A stable device should be accepted and put in a regular process mode |
| // within |kStableConvergenceMs|. |
| int process_time_ms = 0; |
| for (; process_time_ms < kStableConvergenceMs; process_time_ms += 10) { |
| RenderAndCapture(kDeviceBufMs); |
| buffer_size += samples_per_frame_; |
| if (self_->startup_phase == 0) { |
| // We have left the startup phase. |
| break; |
| } |
| } |
| // Verify convergence time. |
| EXPECT_GT(kStableConvergenceMs, process_time_ms); |
| } |
| // Verify that the buffer has been flushed. |
| EXPECT_GE(static_cast<int>(buffer_size), WebRtcAec_system_delay(self_->aec)); |
| } |
| |
| int SystemDelayTest::MapBufferSizeToSamples(int size_in_ms, |
| bool extended_filter) { |
| // If extended_filter is disabled we add an extra 10 ms for the unprocessed |
| // frame. That is simply how the algorithm is constructed. |
| return static_cast<int>((size_in_ms + (extended_filter ? 0 : 10)) * |
| samples_per_frame_ / 10); |
| } |
| |
| // The tests should meet basic requirements and not be adjusted to what is |
| // actually implemented. If we don't get good code coverage this way we either |
| // lack in tests or have unnecessary code. |
| // General requirements: |
| // 1) If we add far-end data the system delay should be increased with the same |
| // amount we add. |
| // 2) If the far-end buffer is full we should flush the oldest data to make room |
| // for the new. In this case the system delay is unaffected. |
| // 3) There should exist a startup phase in which the buffer size is to be |
| // determined. In this phase no cancellation should be performed. |
| // 4) Under stable conditions (small variations in device buffer sizes) the AEC |
| // should determine an appropriate local buffer size within |
| // |kStableConvergenceMs| ms. |
| // 5) Under unstable conditions the AEC should make a decision within |
| // |kMaxConvergenceMs| ms. |
| // 6) If the local buffer runs out of data we should stuff the buffer with older |
| // frames. |
| // 7) The system delay should within |kMaxConvergenceMs| ms heal from |
| // disturbances like drift, data glitches, toggling events and outliers. |
| // 8) The system delay should never become negative. |
| |
| TEST_F(SystemDelayTest, CorrectIncreaseWhenBufferFarend) { |
| // When we add data to the AEC buffer the internal system delay should be |
| // incremented with the same amount as the size of data. |
| // This process should be independent of DA-AEC and extended_filter mode. |
| for (int extended_filter = 0; extended_filter <= 1; ++extended_filter) { |
| WebRtcAec_enable_extended_filter(self_->aec, extended_filter); |
| EXPECT_EQ(extended_filter, WebRtcAec_extended_filter_enabled(self_->aec)); |
| for (int da_aec = 0; da_aec <= 1; ++da_aec) { |
| WebRtcAec_enable_delay_agnostic(self_->aec, da_aec); |
| EXPECT_EQ(da_aec, WebRtcAec_delay_agnostic_enabled(self_->aec)); |
| for (size_t i = 0; i < kNumSampleRates; i++) { |
| Init(kSampleRateHz[i]); |
| // Loop through a couple of calls to make sure the system delay |
| // increments correctly. |
| for (int j = 1; j <= 5; j++) { |
| EXPECT_EQ(0, |
| WebRtcAec_BufferFarend(handle_, far_, samples_per_frame_)); |
| EXPECT_EQ(static_cast<int>(j * samples_per_frame_), |
| WebRtcAec_system_delay(self_->aec)); |
| } |
| } |
| } |
| } |
| } |
| |
| // TODO(bjornv): Add a test to verify behavior if the far-end buffer is full |
| // when adding new data. |
| |
| TEST_F(SystemDelayTest, CorrectDelayAfterStableStartup) { |
| // We run the system in a stable startup. After that we verify that the system |
| // delay meets the requirements. |
| // This process should be independent of DA-AEC and extended_filter mode. |
| for (int extended_filter = 0; extended_filter <= 1; ++extended_filter) { |
| WebRtcAec_enable_extended_filter(self_->aec, extended_filter); |
| EXPECT_EQ(extended_filter, WebRtcAec_extended_filter_enabled(self_->aec)); |
| for (int da_aec = 0; da_aec <= 1; ++da_aec) { |
| WebRtcAec_enable_delay_agnostic(self_->aec, da_aec); |
| EXPECT_EQ(da_aec, WebRtcAec_delay_agnostic_enabled(self_->aec)); |
| for (size_t i = 0; i < kNumSampleRates; i++) { |
| Init(kSampleRateHz[i]); |
| RunStableStartup(); |
| |
| // Verify system delay with respect to requirements, i.e., the |
| // |system_delay| is in the interval [75%, 100%] of what's reported on |
| // the average. |
| // In extended_filter mode we target 50% and measure after one processed |
| // 10 ms chunk. |
| int average_reported_delay = |
| static_cast<int>(kDeviceBufMs * samples_per_frame_ / 10); |
| EXPECT_GE(average_reported_delay, WebRtcAec_system_delay(self_->aec)); |
| int lower_bound = WebRtcAec_extended_filter_enabled(self_->aec) |
| ? (average_reported_delay / 2 - |
| rtc::checked_cast<int>(samples_per_frame_)) |
| : average_reported_delay * 3 / 4; |
| EXPECT_LE(lower_bound, WebRtcAec_system_delay(self_->aec)); |
| } |
| } |
| } |
| } |
| |
| TEST_F(SystemDelayTest, CorrectDelayAfterUnstableStartup) { |
| // This test does not apply in extended_filter mode, since we only use the |
| // the first 10 ms chunk to determine a reasonable buffer size. Neither does |
| // it apply if DA-AEC is on because that overrides the startup procedure. |
| WebRtcAec_enable_extended_filter(self_->aec, 0); |
| EXPECT_EQ(0, WebRtcAec_extended_filter_enabled(self_->aec)); |
| WebRtcAec_enable_delay_agnostic(self_->aec, 0); |
| EXPECT_EQ(0, WebRtcAec_delay_agnostic_enabled(self_->aec)); |
| |
| // In an unstable system we would start processing after |kMaxConvergenceMs|. |
| // On the last frame the AEC buffer is adjusted to 60% of the last reported |
| // device buffer size. |
| // We construct an unstable system by altering the device buffer size between |
| // two values |kDeviceBufMs| +- 25 ms. |
| for (size_t i = 0; i < kNumSampleRates; i++) { |
| Init(kSampleRateHz[i]); |
| |
| // To make sure we have a full buffer when we verify stability we first fill |
| // up the far-end buffer with the same amount as we will report in on the |
| // average through Process(). |
| size_t buffer_size = BufferFillUp(); |
| |
| int buffer_offset_ms = 25; |
| int reported_delay_ms = 0; |
| int process_time_ms = 0; |
| for (; process_time_ms <= kMaxConvergenceMs; process_time_ms += 10) { |
| reported_delay_ms = kDeviceBufMs + buffer_offset_ms; |
| RenderAndCapture(reported_delay_ms); |
| buffer_size += samples_per_frame_; |
| buffer_offset_ms = -buffer_offset_ms; |
| if (self_->startup_phase == 0) { |
| // We have left the startup phase. |
| break; |
| } |
| } |
| // Verify convergence time. |
| EXPECT_GE(kMaxConvergenceMs, process_time_ms); |
| // Verify that the buffer has been flushed. |
| EXPECT_GE(static_cast<int>(buffer_size), |
| WebRtcAec_system_delay(self_->aec)); |
| |
| // Verify system delay with respect to requirements, i.e., the |
| // |system_delay| is in the interval [60%, 100%] of what's last reported. |
| EXPECT_GE(static_cast<int>(reported_delay_ms * samples_per_frame_ / 10), |
| WebRtcAec_system_delay(self_->aec)); |
| EXPECT_LE( |
| static_cast<int>(reported_delay_ms * samples_per_frame_ / 10 * 3 / 5), |
| WebRtcAec_system_delay(self_->aec)); |
| } |
| } |
| |
| TEST_F(SystemDelayTest, CorrectDelayAfterStableBufferBuildUp) { |
| // This test does not apply in extended_filter mode, since we only use the |
| // the first 10 ms chunk to determine a reasonable buffer size. Neither does |
| // it apply if DA-AEC is on because that overrides the startup procedure. |
| WebRtcAec_enable_extended_filter(self_->aec, 0); |
| EXPECT_EQ(0, WebRtcAec_extended_filter_enabled(self_->aec)); |
| WebRtcAec_enable_delay_agnostic(self_->aec, 0); |
| EXPECT_EQ(0, WebRtcAec_delay_agnostic_enabled(self_->aec)); |
| |
| // In this test we start by establishing the device buffer size during stable |
| // conditions, but with an empty internal far-end buffer. Once that is done we |
| // verify that the system delay is increased correctly until we have reach an |
| // internal buffer size of 75% of what's been reported. |
| for (size_t i = 0; i < kNumSampleRates; i++) { |
| Init(kSampleRateHz[i]); |
| |
| // We assume that running |kStableConvergenceMs| calls will put the |
| // algorithm in a state where the device buffer size has been determined. We |
| // can make that assumption since we have a separate stability test. |
| int process_time_ms = 0; |
| for (; process_time_ms < kStableConvergenceMs; process_time_ms += 10) { |
| EXPECT_EQ(0, WebRtcAec_Process(handle_, &near_ptr_, 1, &out_ptr_, |
| samples_per_frame_, kDeviceBufMs, 0)); |
| } |
| // Verify that a buffer size has been established. |
| EXPECT_EQ(0, self_->checkBuffSize); |
| |
| // We now have established the required buffer size. Let us verify that we |
| // fill up before leaving the startup phase for normal processing. |
| size_t buffer_size = 0; |
| size_t target_buffer_size = kDeviceBufMs * samples_per_frame_ / 10 * 3 / 4; |
| process_time_ms = 0; |
| for (; process_time_ms <= kMaxConvergenceMs; process_time_ms += 10) { |
| RenderAndCapture(kDeviceBufMs); |
| buffer_size += samples_per_frame_; |
| if (self_->startup_phase == 0) { |
| // We have left the startup phase. |
| break; |
| } |
| } |
| // Verify convergence time. |
| EXPECT_GT(kMaxConvergenceMs, process_time_ms); |
| // Verify that the buffer has reached the desired size. |
| EXPECT_LE(static_cast<int>(target_buffer_size), |
| WebRtcAec_system_delay(self_->aec)); |
| |
| // Verify normal behavior (system delay is kept constant) after startup by |
| // running a couple of calls to BufferFarend() and Process(). |
| for (int j = 0; j < 6; j++) { |
| int system_delay_before_calls = WebRtcAec_system_delay(self_->aec); |
| RenderAndCapture(kDeviceBufMs); |
| EXPECT_EQ(system_delay_before_calls, WebRtcAec_system_delay(self_->aec)); |
| } |
| } |
| } |
| |
| TEST_F(SystemDelayTest, CorrectDelayWhenBufferUnderrun) { |
| // Here we test a buffer under run scenario. If we keep on calling |
| // WebRtcAec_Process() we will finally run out of data, but should |
| // automatically stuff the buffer. We verify this behavior by checking if the |
| // system delay goes negative. |
| // This process should be independent of DA-AEC and extended_filter mode. |
| for (int extended_filter = 0; extended_filter <= 1; ++extended_filter) { |
| WebRtcAec_enable_extended_filter(self_->aec, extended_filter); |
| EXPECT_EQ(extended_filter, WebRtcAec_extended_filter_enabled(self_->aec)); |
| for (int da_aec = 0; da_aec <= 1; ++da_aec) { |
| WebRtcAec_enable_delay_agnostic(self_->aec, da_aec); |
| EXPECT_EQ(da_aec, WebRtcAec_delay_agnostic_enabled(self_->aec)); |
| for (size_t i = 0; i < kNumSampleRates; i++) { |
| Init(kSampleRateHz[i]); |
| RunStableStartup(); |
| |
| // The AEC has now left the Startup phase. We now have at most |
| // |kStableConvergenceMs| in the buffer. Keep on calling Process() until |
| // we run out of data and verify that the system delay is non-negative. |
| for (int j = 0; j <= kStableConvergenceMs; j += 10) { |
| EXPECT_EQ(0, WebRtcAec_Process(handle_, &near_ptr_, 1, &out_ptr_, |
| samples_per_frame_, kDeviceBufMs, 0)); |
| EXPECT_LE(0, WebRtcAec_system_delay(self_->aec)); |
| } |
| } |
| } |
| } |
| } |
| |
| TEST_F(SystemDelayTest, CorrectDelayDuringDrift) { |
| // This drift test should verify that the system delay is never exceeding the |
| // device buffer. The drift is simulated by decreasing the reported device |
| // buffer size by 1 ms every 100 ms. If the device buffer size goes below 30 |
| // ms we jump (add) 10 ms to give a repeated pattern. |
| |
| // This process should be independent of DA-AEC and extended_filter mode. |
| for (int extended_filter = 0; extended_filter <= 1; ++extended_filter) { |
| WebRtcAec_enable_extended_filter(self_->aec, extended_filter); |
| EXPECT_EQ(extended_filter, WebRtcAec_extended_filter_enabled(self_->aec)); |
| for (int da_aec = 0; da_aec <= 1; ++da_aec) { |
| WebRtcAec_enable_delay_agnostic(self_->aec, da_aec); |
| EXPECT_EQ(da_aec, WebRtcAec_delay_agnostic_enabled(self_->aec)); |
| for (size_t i = 0; i < kNumSampleRates; i++) { |
| Init(kSampleRateHz[i]); |
| RunStableStartup(); |
| |
| // We have left the startup phase and proceed with normal processing. |
| int jump = 0; |
| for (int j = 0; j < 1000; j++) { |
| // Drift = -1 ms per 100 ms of data. |
| int device_buf_ms = kDeviceBufMs - (j / 10) + jump; |
| int device_buf = |
| MapBufferSizeToSamples(device_buf_ms, extended_filter == 1); |
| |
| if (device_buf_ms < 30) { |
| // Add 10 ms data, taking affect next frame. |
| jump += 10; |
| } |
| RenderAndCapture(device_buf_ms); |
| |
| // Verify that the system delay does not exceed the device buffer. |
| EXPECT_GE(device_buf, WebRtcAec_system_delay(self_->aec)); |
| |
| // Verify that the system delay is non-negative. |
| EXPECT_LE(0, WebRtcAec_system_delay(self_->aec)); |
| } |
| } |
| } |
| } |
| } |
| |
| TEST_F(SystemDelayTest, ShouldRecoverAfterGlitch) { |
| // This glitch test should verify that the system delay recovers if there is |
| // a glitch in data. The data glitch is constructed as 200 ms of buffering |
| // after which the stable procedure continues. The glitch is never reported by |
| // the device. |
| // The system is said to be in a non-causal state if the difference between |
| // the device buffer and system delay is less than a block (64 samples). |
| |
| // This process should be independent of DA-AEC and extended_filter mode. |
| for (int extended_filter = 0; extended_filter <= 1; ++extended_filter) { |
| WebRtcAec_enable_extended_filter(self_->aec, extended_filter); |
| EXPECT_EQ(extended_filter, WebRtcAec_extended_filter_enabled(self_->aec)); |
| for (int da_aec = 0; da_aec <= 1; ++da_aec) { |
| WebRtcAec_enable_delay_agnostic(self_->aec, da_aec); |
| EXPECT_EQ(da_aec, WebRtcAec_delay_agnostic_enabled(self_->aec)); |
| for (size_t i = 0; i < kNumSampleRates; i++) { |
| Init(kSampleRateHz[i]); |
| RunStableStartup(); |
| int device_buf = |
| MapBufferSizeToSamples(kDeviceBufMs, extended_filter == 1); |
| // Glitch state. |
| for (int j = 0; j < 20; j++) { |
| EXPECT_EQ(0, |
| WebRtcAec_BufferFarend(handle_, far_, samples_per_frame_)); |
| // No need to verify system delay, since that is done in a separate |
| // test. |
| } |
| // Verify that we are in a non-causal state, i.e., |
| // |system_delay| > |device_buf|. |
| EXPECT_LT(device_buf, WebRtcAec_system_delay(self_->aec)); |
| |
| // Recover state. Should recover at least 4 ms of data per 10 ms, hence |
| // a glitch of 200 ms will take at most 200 * 10 / 4 = 500 ms to recover |
| // from. |
| bool non_causal = true; // We are currently in a non-causal state. |
| for (int j = 0; j < 50; j++) { |
| int system_delay_before = WebRtcAec_system_delay(self_->aec); |
| RenderAndCapture(kDeviceBufMs); |
| int system_delay_after = WebRtcAec_system_delay(self_->aec); |
| // We have recovered if |
| // |device_buf| - |system_delay_after| >= PART_LEN (1 block). |
| // During recovery, |system_delay_after| < |system_delay_before|, |
| // otherwise they are equal. |
| if (non_causal) { |
| EXPECT_LT(system_delay_after, system_delay_before); |
| if (device_buf - system_delay_after >= PART_LEN) { |
| non_causal = false; |
| } |
| } else { |
| EXPECT_EQ(system_delay_before, system_delay_after); |
| } |
| // Verify that the system delay is non-negative. |
| EXPECT_LE(0, WebRtcAec_system_delay(self_->aec)); |
| } |
| // Check that we have recovered. |
| EXPECT_FALSE(non_causal); |
| } |
| } |
| } |
| } |
| |
| TEST_F(SystemDelayTest, UnaffectedWhenSpuriousDeviceBufferValues) { |
| // This test does not apply in extended_filter mode, since we only use the |
| // the first 10 ms chunk to determine a reasonable buffer size. |
| const int extended_filter = 0; |
| WebRtcAec_enable_extended_filter(self_->aec, extended_filter); |
| EXPECT_EQ(extended_filter, WebRtcAec_extended_filter_enabled(self_->aec)); |
| |
| // Should be DA-AEC independent. |
| for (int da_aec = 0; da_aec <= 1; ++da_aec) { |
| WebRtcAec_enable_delay_agnostic(self_->aec, da_aec); |
| EXPECT_EQ(da_aec, WebRtcAec_delay_agnostic_enabled(self_->aec)); |
| // This spurious device buffer data test aims at verifying that the system |
| // delay is unaffected by large outliers. |
| // The system is said to be in a non-causal state if the difference between |
| // the device buffer and system delay is less than a block (64 samples). |
| for (size_t i = 0; i < kNumSampleRates; i++) { |
| Init(kSampleRateHz[i]); |
| RunStableStartup(); |
| int device_buf = |
| MapBufferSizeToSamples(kDeviceBufMs, extended_filter == 1); |
| |
| // Normal state. We are currently not in a non-causal state. |
| bool non_causal = false; |
| |
| // Run 1 s and replace device buffer size with 500 ms every 100 ms. |
| for (int j = 0; j < 100; j++) { |
| int system_delay_before_calls = WebRtcAec_system_delay(self_->aec); |
| int device_buf_ms = j % 10 == 0 ? 500 : kDeviceBufMs; |
| RenderAndCapture(device_buf_ms); |
| |
| // Check for non-causality. |
| if (device_buf - WebRtcAec_system_delay(self_->aec) < PART_LEN) { |
| non_causal = true; |
| } |
| EXPECT_FALSE(non_causal); |
| EXPECT_EQ(system_delay_before_calls, |
| WebRtcAec_system_delay(self_->aec)); |
| |
| // Verify that the system delay is non-negative. |
| EXPECT_LE(0, WebRtcAec_system_delay(self_->aec)); |
| } |
| } |
| } |
| } |
| |
| TEST_F(SystemDelayTest, CorrectImpactWhenTogglingDeviceBufferValues) { |
| // This test aims at verifying that the system delay is "unaffected" by |
| // toggling values reported by the device. |
| // The test is constructed such that every other device buffer value is zero |
| // and then 2 * |kDeviceBufMs|, hence the size is constant on the average. The |
| // zero values will force us into a non-causal state and thereby lowering the |
| // system delay until we basically run out of data. Once that happens the |
| // buffer will be stuffed. |
| // TODO(bjornv): This test will have a better impact if we verified that the |
| // delay estimate goes up when the system delay goes down to meet the average |
| // device buffer size. |
| |
| // This test does not apply if DA-AEC is enabled and extended_filter mode |
| // disabled. |
| for (int extended_filter = 0; extended_filter <= 1; ++extended_filter) { |
| WebRtcAec_enable_extended_filter(self_->aec, extended_filter); |
| EXPECT_EQ(extended_filter, WebRtcAec_extended_filter_enabled(self_->aec)); |
| for (int da_aec = 0; da_aec <= 1; ++da_aec) { |
| WebRtcAec_enable_delay_agnostic(self_->aec, da_aec); |
| EXPECT_EQ(da_aec, WebRtcAec_delay_agnostic_enabled(self_->aec)); |
| if (extended_filter == 0 && da_aec == 1) { |
| continue; |
| } |
| for (size_t i = 0; i < kNumSampleRates; i++) { |
| Init(kSampleRateHz[i]); |
| RunStableStartup(); |
| const int device_buf = |
| MapBufferSizeToSamples(kDeviceBufMs, extended_filter == 1); |
| |
| // Normal state. We are currently not in a non-causal state. |
| bool non_causal = false; |
| |
| // Loop through 100 frames (both render and capture), which equals 1 s |
| // of data. Every odd frame we set the device buffer size to |
| // 2 * |kDeviceBufMs| and even frames we set the device buffer size to |
| // zero. |
| for (int j = 0; j < 100; j++) { |
| int system_delay_before_calls = WebRtcAec_system_delay(self_->aec); |
| int device_buf_ms = 2 * (j % 2) * kDeviceBufMs; |
| RenderAndCapture(device_buf_ms); |
| |
| // Check for non-causality, compared with the average device buffer |
| // size. |
| non_causal |= (device_buf - WebRtcAec_system_delay(self_->aec) < 64); |
| EXPECT_GE(system_delay_before_calls, |
| WebRtcAec_system_delay(self_->aec)); |
| |
| // Verify that the system delay is non-negative. |
| EXPECT_LE(0, WebRtcAec_system_delay(self_->aec)); |
| } |
| // Verify we are not in a non-causal state. |
| EXPECT_FALSE(non_causal); |
| } |
| } |
| } |
| } |
| |
| } // namespace |
| } // namespace webrtc |