aboutsummaryrefslogtreecommitdiff
path: root/webrtc/modules/audio_processing/aec/system_delay_unittest.cc
blob: 567118d828d6184099ce144049a56b4a52b39593 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
/*
 *  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 "testing/gtest/include/gtest/gtest.h"
extern "C" {
#include "webrtc/modules/audio_processing/aec/aec_core.h"
}
#include "webrtc/modules/audio_processing/aec/echo_cancellation_internal.h"
#include "webrtc/modules/audio_processing/aec/echo_cancellation.h"
#include "webrtc/typedefs.h"

namespace {

class SystemDelayTest : public ::testing::Test {
 protected:
  SystemDelayTest();
  virtual void SetUp();
  virtual void TearDown();

  // 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 - 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