tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

TestAudioDriftCorrection.cpp (23574B)


      1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/
      2 /* This Source Code Form is subject to the terms of the Mozilla Public
      3 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
      4 * You can obtain one at http://mozilla.org/MPL/2.0/. */
      5 
      6 #include "AudioDriftCorrection.h"
      7 #include "AudioGenerator.h"
      8 #include "AudioVerifier.h"
      9 #include "gtest/gtest.h"
     10 #include "nsContentUtils.h"
     11 
     12 using namespace mozilla;
     13 
     14 template <class T>
     15 AudioChunk CreateAudioChunk(uint32_t aFrames, uint32_t aChannels,
     16                            AudioSampleFormat aSampleFormat);
     17 
     18 void testAudioCorrection(int32_t aSourceRate, int32_t aTargetRate,
     19                         bool aTestMonoToStereoInput = false) {
     20  const uint32_t frequency = 100;
     21  const PrincipalHandle testPrincipal =
     22      MakePrincipalHandle(nsContentUtils::GetSystemPrincipal());
     23  AudioDriftCorrection ad(aSourceRate, aTargetRate, testPrincipal);
     24 
     25  uint8_t numChannels = 1;
     26  AudioGenerator<AudioDataValue> tone(numChannels, aSourceRate, frequency);
     27  AudioVerifier<AudioDataValue> inToneVerifier(aSourceRate, frequency);
     28  AudioVerifier<AudioDataValue> outToneVerifier(aTargetRate, frequency);
     29 
     30  // Run for some time: 3 * 5000 = 15000 iterations
     31  for (uint32_t j = 0; j < 3; ++j) {
     32    TrackTime sourceFramesIteration = 0;
     33    TrackTime targetFramesIteration = 0;
     34 
     35    // apply some drift (+/- .2%)
     36    const int8_t additionalDriftFrames =
     37        ((j % 2 == 0) ? aSourceRate : -aSourceRate) * 2 / 1000;
     38 
     39    // If the number of frames before changing channel count (and thereby
     40    // resetting the resampler) is very low, the measured buffering level curve
     41    // may look odd, as each resampler reset will reset the (possibly
     42    // fractional) output frame counter.
     43    const uint32_t numFramesBeforeChangingChannelCount = aSourceRate;
     44    uint32_t numFramesAtCurrentChannelCount = 0;
     45 
     46    // 50 seconds, allows for at least 50 correction changes, to stabilize
     47    // on the current drift.
     48    for (uint32_t n = 0; n < 5000; ++n) {
     49      const TrackTime sourceFrames =
     50          (n + 1) * (aSourceRate + additionalDriftFrames) / 100 -
     51          sourceFramesIteration;
     52      const TrackTime targetFrames =
     53          (n + 1) * aTargetRate / 100 - targetFramesIteration;
     54      AudioSegment inSegment;
     55      if (aTestMonoToStereoInput) {
     56        // Create the input (sine tone) of two chunks.
     57        const TrackTime sourceFramesPart1 = std::min<TrackTime>(
     58            sourceFrames, numFramesBeforeChangingChannelCount -
     59                              numFramesAtCurrentChannelCount);
     60        tone.Generate(inSegment, sourceFramesPart1);
     61        numFramesAtCurrentChannelCount += sourceFramesPart1;
     62        if (numFramesBeforeChangingChannelCount ==
     63            numFramesAtCurrentChannelCount) {
     64          tone.SetChannelsCount(numChannels = (numChannels % 2) + 1);
     65          numFramesAtCurrentChannelCount = sourceFrames - sourceFramesPart1;
     66          tone.Generate(inSegment, numFramesAtCurrentChannelCount);
     67        }
     68      } else {
     69        // Create the input (sine tone)
     70        tone.Generate(inSegment, sourceFrames);
     71      }
     72      inToneVerifier.AppendData(inSegment);
     73 
     74      // Get the output of the correction
     75      AudioSegment outSegment = ad.RequestFrames(inSegment, targetFrames);
     76      EXPECT_EQ(outSegment.GetDuration(), targetFrames);
     77      for (AudioSegment::ConstChunkIterator ci(outSegment); !ci.IsEnded();
     78           ci.Next()) {
     79        EXPECT_EQ(ci->mPrincipalHandle, testPrincipal);
     80      }
     81      outToneVerifier.AppendData(outSegment);
     82      sourceFramesIteration += sourceFrames;
     83      targetFramesIteration += targetFrames;
     84    }
     85  }
     86 
     87  // Initial buffering is 50ms, which is then expected to be reduced as the
     88  // drift adaptation stabilizes.
     89  EXPECT_LT(ad.CurrentBuffering(), aSourceRate * 50U / 1000);
     90  // Desired buffering should not go lower than some 130% of the source buffer
     91  // size per-iteration.
     92  EXPECT_GT(ad.CurrentBuffering(), aSourceRate * 10U / 1000);
     93 
     94  EXPECT_EQ(ad.NumUnderruns(), 0U);
     95 
     96  EXPECT_FLOAT_EQ(inToneVerifier.EstimatedFreq(), tone.mFrequency);
     97  EXPECT_EQ(inToneVerifier.PreSilenceSamples(), 0U);
     98  EXPECT_EQ(inToneVerifier.CountDiscontinuities(), 0U);
     99 
    100  EXPECT_NEAR(outToneVerifier.EstimatedFreq(), tone.mFrequency, 1.0f);
    101  // The expected pre-silence is equal to the initial desired buffering (50ms)
    102  // minus what is left after resampling the first input segment.
    103  const auto buffering = media::TimeUnit::FromSeconds(0.05);
    104  const auto sourceStep =
    105      media::TimeUnit(aSourceRate * 1002 / 1000 / 100, aSourceRate);
    106  const auto targetStep = media::TimeUnit(aTargetRate / 100, aTargetRate);
    107  EXPECT_NEAR(static_cast<int64_t>(outToneVerifier.PreSilenceSamples()),
    108              (targetStep + buffering - sourceStep)
    109                  .ToBase(aSourceRate)
    110                  .ToBase<media::TimeUnit::CeilingPolicy>(aTargetRate)
    111                  .ToTicksAtRate(aTargetRate),
    112              1U);
    113  EXPECT_EQ(outToneVerifier.CountDiscontinuities(), 0U);
    114 }
    115 
    116 TEST(TestAudioDriftCorrection, Basic)
    117 {
    118  printf("Testing AudioCorrection 48 -> 48\n");
    119  testAudioCorrection(48000, 48000);
    120  printf("Testing AudioCorrection 48 -> 44.1\n");
    121  testAudioCorrection(48000, 44100);
    122  printf("Testing AudioCorrection 44.1 -> 48\n");
    123  testAudioCorrection(44100, 48000);
    124  printf("Testing AudioCorrection 23458 -> 25113\n");
    125  testAudioCorrection(23458, 25113);
    126 }
    127 
    128 TEST(TestAudioDriftCorrection, MonoToStereoInput)
    129 {
    130  constexpr bool testMonoToStereoInput = true;
    131  printf("Testing MonoToStereoInput 48 -> 48\n");
    132  testAudioCorrection(48000, 48000, testMonoToStereoInput);
    133  printf("Testing MonoToStereoInput 48 -> 44.1\n");
    134  testAudioCorrection(48000, 44100, testMonoToStereoInput);
    135  printf("Testing MonoToStereoInput 44.1 -> 48\n");
    136  testAudioCorrection(44100, 48000, testMonoToStereoInput);
    137 }
    138 
    139 TEST(TestAudioDriftCorrection, NotEnoughFrames)
    140 {
    141  const uint32_t frequency = 100;
    142  const uint32_t sampleRateTransmitter = 48000;
    143  const uint32_t sampleRateReceiver = 48000;
    144  const PrincipalHandle testPrincipal =
    145      MakePrincipalHandle(nsContentUtils::GetSystemPrincipal());
    146  AudioDriftCorrection ad(sampleRateTransmitter, sampleRateReceiver,
    147                          testPrincipal);
    148  const uint32_t targetFrames = sampleRateReceiver / 100;
    149 
    150  AudioGenerator<AudioDataValue> tone(1, sampleRateTransmitter, frequency);
    151  AudioVerifier<AudioDataValue> outToneVerifier(sampleRateReceiver, frequency);
    152 
    153  for (uint32_t i = 0; i < 7; ++i) {
    154    // Input is something small, 10 frames here, in order to dry out fast,
    155    // after 4 iterations (pre-buffer = 2400)
    156    AudioSegment inSegment;
    157    tone.Generate(inSegment, 10);
    158 
    159    AudioSegment outSegment = ad.RequestFrames(inSegment, targetFrames);
    160    EXPECT_EQ(outSegment.GetDuration(), targetFrames);
    161    EXPECT_FALSE(outSegment.IsNull());
    162    for (AudioSegment::ConstChunkIterator ci(outSegment); !ci.IsEnded();
    163         ci.Next()) {
    164      if (i < 5) {
    165        if (!ci->IsNull()) {
    166          EXPECT_EQ(ci->mPrincipalHandle, testPrincipal);
    167        }
    168      }
    169    }
    170 
    171    outToneVerifier.AppendData(outSegment);
    172  }
    173  EXPECT_EQ(ad.BufferSize(), 4800U);
    174  EXPECT_EQ(ad.NumUnderruns(), 1u);
    175  EXPECT_EQ(outToneVerifier.CountDiscontinuities(), 1u);
    176 }
    177 
    178 TEST(TestAudioDriftCorrection, CrashInAudioResampler)
    179 {
    180  const uint32_t sampleRateTransmitter = 48000;
    181  const uint32_t sampleRateReceiver = 48000;
    182  const PrincipalHandle testPrincipal =
    183      MakePrincipalHandle(nsContentUtils::GetSystemPrincipal());
    184  AudioDriftCorrection ad(sampleRateTransmitter, sampleRateReceiver,
    185                          testPrincipal);
    186  const uint32_t targetFrames = sampleRateReceiver / 100;
    187 
    188  for (uint32_t i = 0; i < 100; ++i) {
    189    AudioChunk chunk = CreateAudioChunk<float>(sampleRateTransmitter / 1000, 1,
    190                                               AUDIO_FORMAT_FLOAT32);
    191    AudioSegment inSegment;
    192    inSegment.AppendAndConsumeChunk(std::move(chunk));
    193 
    194    AudioSegment outSegment = ad.RequestFrames(inSegment, targetFrames);
    195    EXPECT_EQ(outSegment.GetDuration(), targetFrames);
    196    for (AudioSegment::ConstChunkIterator ci(outSegment); !ci.IsEnded();
    197         ci.Next()) {
    198      if (!ci->IsNull()) {  // Don't check the data if ad is dried out.
    199        EXPECT_EQ(ci->mPrincipalHandle, testPrincipal);
    200      }
    201    }
    202  }
    203 }
    204 
    205 TEST(TestAudioDriftCorrection, HighLatencyProducerLowLatencyConsumer)
    206 {
    207  constexpr uint32_t transmitterBlockSize = 2048;
    208  constexpr uint32_t receiverBlockSize = 128;
    209  constexpr uint32_t sampleRate = 48000;
    210  const PrincipalHandle testPrincipal =
    211      MakePrincipalHandle(nsContentUtils::GetSystemPrincipal());
    212  AudioDriftCorrection ad(sampleRate, sampleRate, testPrincipal);
    213 
    214  uint32_t numBlocksProduced = 0;
    215  for (uint32_t i = 0; i < (sampleRate / 1000) * 500; i += receiverBlockSize) {
    216    AudioSegment inSegment;
    217    if ((i / transmitterBlockSize) > numBlocksProduced) {
    218      AudioChunk chunk = CreateAudioChunk<float>(transmitterBlockSize, 1,
    219                                                 AUDIO_FORMAT_FLOAT32);
    220      inSegment.AppendAndConsumeChunk(std::move(chunk));
    221      ++numBlocksProduced;
    222    }
    223 
    224    AudioSegment outSegment = ad.RequestFrames(inSegment, receiverBlockSize);
    225    EXPECT_EQ(outSegment.GetDuration(), receiverBlockSize);
    226  }
    227 
    228  // Input is stable so no corrections should occur.
    229  EXPECT_EQ(ad.NumCorrectionChanges(), 0U);
    230 }
    231 
    232 TEST(TestAudioDriftCorrection, LargerTransmitterBlockSizeThanDesiredBuffering)
    233 {
    234  constexpr uint32_t transmitterBlockSize = 4096;
    235  constexpr uint32_t receiverBlockSize = 128;
    236  constexpr uint32_t sampleRate = 48000;
    237  const PrincipalHandle testPrincipal =
    238      MakePrincipalHandle(nsContentUtils::GetSystemPrincipal());
    239  AudioDriftCorrection ad(sampleRate, sampleRate, testPrincipal);
    240 
    241  uint32_t numBlocksTransmitted = 0;
    242  for (uint32_t i = 0; i < (sampleRate / 1000) * 500; i += receiverBlockSize) {
    243    AudioSegment inSegment;
    244    if (uint32_t currentBlock = i / transmitterBlockSize;
    245        currentBlock > numBlocksTransmitted) {
    246      AudioChunk chunk = CreateAudioChunk<float>(transmitterBlockSize, 1,
    247                                                 AUDIO_FORMAT_FLOAT32);
    248      inSegment.AppendAndConsumeChunk(std::move(chunk));
    249      numBlocksTransmitted = currentBlock;
    250    }
    251 
    252    AudioSegment outSegment = ad.RequestFrames(inSegment, receiverBlockSize);
    253    EXPECT_EQ(outSegment.GetDuration(), receiverBlockSize);
    254 
    255    if (numBlocksTransmitted > 0) {
    256      EXPECT_GT(ad.CurrentBuffering(), 0U);
    257    }
    258  }
    259 
    260  // Input is stable so no corrections should occur.
    261  EXPECT_EQ(ad.NumCorrectionChanges(), 0U);
    262  // The desired buffering and pre-buffering level was
    263  // transmitterBlockSize * 11 / 10 to accomodate the large input block size.
    264  // The buffer size was twice the pre-buffering level.
    265  EXPECT_EQ(ad.BufferSize(), transmitterBlockSize * 11 / 10 * 2);
    266 }
    267 
    268 TEST(TestAudioDriftCorrection, LargerReceiverBlockSizeThanDesiredBuffering)
    269 {
    270  constexpr uint32_t transmitterBlockSize = 128;
    271  constexpr uint32_t receiverBlockSize = 4096;
    272  constexpr uint32_t sampleRate = 48000;
    273  const PrincipalHandle testPrincipal =
    274      MakePrincipalHandle(nsContentUtils::GetSystemPrincipal());
    275  AudioDriftCorrection ad(sampleRate, sampleRate, testPrincipal);
    276 
    277  AudioSegment inSegment;
    278  for (uint32_t i = 0; i < (sampleRate / 1000) * 500;
    279       i += transmitterBlockSize) {
    280    AudioChunk chunk =
    281        CreateAudioChunk<float>(transmitterBlockSize, 1, AUDIO_FORMAT_FLOAT32);
    282    inSegment.AppendAndConsumeChunk(std::move(chunk));
    283 
    284    if (i % receiverBlockSize == 0) {
    285      AudioSegment outSegment = ad.RequestFrames(inSegment, receiverBlockSize);
    286      EXPECT_EQ(outSegment.GetDuration(), receiverBlockSize);
    287      inSegment.Clear();
    288    }
    289 
    290    if (i >= receiverBlockSize) {
    291      EXPECT_GT(ad.CurrentBuffering(), 0U);
    292    }
    293  }
    294 
    295  // Input is stable so no corrections should occur.
    296  EXPECT_EQ(ad.NumCorrectionChanges(), 0U);
    297  EXPECT_EQ(ad.NumUnderruns(), 0U);
    298  // The drift correction buffer size had to be larger than the desired (the
    299  // buffer size is twice the initial buffering level), to accomodate the large
    300  // input block size that gets buffered in the resampler only when processing
    301  // output.
    302  EXPECT_EQ(ad.BufferSize(), 9600U);
    303 }
    304 
    305 TEST(TestAudioDriftCorrection, DynamicInputBufferSizeChanges)
    306 {
    307  constexpr uint32_t transmitterBlockSize1 = 2048;
    308  constexpr uint32_t transmitterBlockSize2 = 4096;
    309  constexpr uint32_t receiverBlockSize = 128;
    310  constexpr uint32_t sampleRate = 48000;
    311  constexpr uint32_t frequencyHz = 100;
    312  const PrincipalHandle testPrincipal =
    313      MakePrincipalHandle(nsContentUtils::GetSystemPrincipal());
    314  AudioDriftCorrection ad(sampleRate, sampleRate, testPrincipal);
    315 
    316  AudioGenerator<AudioDataValue> tone(1, sampleRate, frequencyHz);
    317  AudioVerifier<AudioDataValue> inToneVerifier(sampleRate, frequencyHz);
    318  AudioVerifier<AudioDataValue> outToneVerifier(sampleRate, frequencyHz);
    319 
    320  TrackTime totalFramesTransmitted = 0;
    321  TrackTime totalFramesReceived = 0;
    322 
    323  const auto produceSomeData = [&](uint32_t aTransmitterBlockSize,
    324                                   uint32_t aDuration) {
    325    TrackTime transmittedFramesStart = totalFramesTransmitted;
    326    TrackTime receivedFramesStart = totalFramesReceived;
    327    uint32_t numBlocksTransmitted = 0;
    328    for (uint32_t i = 0; i < aDuration; i += receiverBlockSize) {
    329      AudioSegment inSegment;
    330      if (((receivedFramesStart - transmittedFramesStart + i) /
    331           aTransmitterBlockSize) > numBlocksTransmitted) {
    332        tone.Generate(inSegment, aTransmitterBlockSize);
    333        MOZ_RELEASE_ASSERT(!inSegment.IsNull());
    334        inToneVerifier.AppendData(inSegment);
    335        MOZ_RELEASE_ASSERT(!inSegment.IsNull());
    336        ++numBlocksTransmitted;
    337        totalFramesTransmitted += aTransmitterBlockSize;
    338      }
    339 
    340      AudioSegment outSegment = ad.RequestFrames(inSegment, receiverBlockSize);
    341      EXPECT_EQ(outSegment.GetDuration(), receiverBlockSize);
    342      outToneVerifier.AppendData(outSegment);
    343      totalFramesReceived += receiverBlockSize;
    344    }
    345  };
    346 
    347  produceSomeData(transmitterBlockSize1, 5 * sampleRate);
    348  EXPECT_EQ(ad.BufferSize(), 4800U);
    349  // No input is provided for the first transmitterBlockSize1 of output
    350  // requested.  This causes a lower input rate estimate, so there are some
    351  // initial corrections.
    352  EXPECT_GT(ad.NumCorrectionChanges(), 0U);
    353  EXPECT_EQ(ad.NumUnderruns(), 0U);
    354 
    355  // Increase input latency. We expect this to underrun, but only once as the
    356  // drift correction adapts its buffer size and desired buffering level.
    357  produceSomeData(transmitterBlockSize2, 25 * sampleRate);
    358  auto numCorrectionChanges = ad.NumCorrectionChanges();
    359  EXPECT_EQ(ad.NumUnderruns(), 1U);
    360 
    361  // Adapting to the new input block size should have stabilized.
    362  EXPECT_GT(ad.BufferSize(), transmitterBlockSize2);
    363  produceSomeData(transmitterBlockSize2, 10 * sampleRate);
    364  EXPECT_LE(ad.NumCorrectionChanges(), numCorrectionChanges + 1);
    365  EXPECT_EQ(ad.NumUnderruns(), 1U);
    366 
    367  // Decrease input latency. We expect the drift correction to gradually
    368  // decrease its desired buffering level.
    369  produceSomeData(transmitterBlockSize1, 100 * sampleRate);
    370  numCorrectionChanges = ad.NumCorrectionChanges();
    371  EXPECT_EQ(ad.NumUnderruns(), 1U);
    372 
    373  EXPECT_EQ(ad.BufferSize(), 9600U);
    374  // Adjustments to the desired buffering level continue.
    375  produceSomeData(transmitterBlockSize1, 20 * sampleRate);
    376  EXPECT_LE(ad.NumCorrectionChanges(), numCorrectionChanges + 2);
    377  EXPECT_EQ(ad.NumUnderruns(), 1U);
    378 
    379  EXPECT_NEAR(inToneVerifier.EstimatedFreq(), tone.mFrequency, 1.0f);
    380  EXPECT_EQ(inToneVerifier.PreSilenceSamples(), 0U);
    381  EXPECT_EQ(inToneVerifier.CountDiscontinuities(), 0U);
    382 
    383  EXPECT_NEAR(outToneVerifier.EstimatedFreq(), tone.mFrequency, 1.0f);
    384  // No input is provided for the first transmitterBlockSize1 of output
    385  // requested, so this output is all silent.
    386  // The expected additional pre-silence is equal to the desired buffering
    387  // plus what's needed to produce the first output segment minus the first
    388  // input segment.
    389  EXPECT_EQ(outToneVerifier.PreSilenceSamples(), 2528U);
    390  EXPECT_NEAR(outToneVerifier.PreSilenceSamples() - transmitterBlockSize1,
    391              media::TimeUnit::FromSeconds(0.05).ToTicksAtRate(sampleRate) +
    392                  receiverBlockSize - transmitterBlockSize1,
    393              1U);
    394  // One mid-stream period of silence from increasing the input buffer size,
    395  // causing an underrun. Counts as two discontinuities.
    396  EXPECT_EQ(outToneVerifier.CountDiscontinuities(), 2U);
    397 }
    398 
    399 /**
    400 * This is helpful to run together with
    401 *   MOZ_LOG=raw,DriftControllerGraphs:5 MOZ_LOG_FILE=./plot_values.csv
    402 * to be able to plot the step response of a change in source clock rate (i.e.
    403 * drift). Useful for calculating and verifying PID coefficients.
    404 */
    405 TEST(TestAudioDriftCorrection, DriftStepResponse)
    406 {
    407  constexpr uint32_t nominalRate = 48000;
    408  constexpr uint32_t interval = nominalRate;
    409  constexpr uint32_t inputRate = nominalRate * 1005 / 1000;  // 0.5% drift
    410  constexpr uint32_t inputInterval = inputRate;
    411  constexpr uint32_t iterations = 200;
    412  const PrincipalHandle testPrincipal =
    413      MakePrincipalHandle(nsContentUtils::GetSystemPrincipal());
    414  AudioGenerator<AudioDataValue> tone(1, nominalRate, 440);
    415  AudioDriftCorrection ad(nominalRate, nominalRate, testPrincipal);
    416  for (uint32_t i = 0; i < interval * iterations; i += interval / 100) {
    417    AudioSegment inSegment;
    418    tone.Generate(inSegment, inputInterval / 100);
    419    ad.RequestFrames(inSegment, interval / 100);
    420  }
    421 
    422  EXPECT_EQ(ad.BufferSize(), 4800U);
    423  EXPECT_EQ(ad.NumUnderruns(), 0u);
    424 }
    425 
    426 /**
    427 * Similar to DriftStepResponse but will underrun to allow testing the underrun
    428 * handling. This is helpful to run together with
    429 *   MOZ_LOG=raw,DriftControllerGraphs:5 MOZ_LOG_FILE=./plot_values.csv
    430 */
    431 TEST(TestAudioDriftCorrection, DriftStepResponseUnderrun)
    432 {
    433  constexpr uint32_t nominalRate = 48000;
    434  constexpr uint32_t interval = nominalRate;
    435  constexpr uint32_t iterations = 200;
    436  const PrincipalHandle testPrincipal =
    437      MakePrincipalHandle(nsContentUtils::GetSystemPrincipal());
    438  uint32_t inputRate = nominalRate * 1005 / 1000;  // 0.5% drift
    439  uint32_t inputInterval = inputRate;
    440  AudioGenerator<AudioDataValue> tone(1, nominalRate, 440);
    441  AudioDriftCorrection ad(nominalRate, nominalRate, testPrincipal);
    442  for (uint32_t i = 0; i < interval * iterations; i += interval / 100) {
    443    AudioSegment inSegment;
    444    tone.Generate(inSegment, inputInterval / 100);
    445    ad.RequestFrames(inSegment, interval / 100);
    446  }
    447 
    448  inputRate = nominalRate * 997 / 1000;  // -0.3% drift
    449  inputInterval = inputRate;
    450  for (uint32_t i = 0; i < interval * iterations; i += interval / 100) {
    451    AudioSegment inSegment;
    452    tone.Generate(inSegment, inputInterval / 100);
    453    ad.RequestFrames(inSegment, interval / 100);
    454  }
    455 
    456  EXPECT_EQ(ad.BufferSize(), 4800U);
    457  EXPECT_EQ(ad.NumUnderruns(), 1u);
    458 }
    459 
    460 /**
    461 * Similar to DriftStepResponse but with a high-latency input, and will underrun
    462 * to allow testing the underrun handling. This is helpful to run together with
    463 *   MOZ_LOG=raw,DriftControllerGraphs:5 MOZ_LOG_FILE=./plot_values.csv
    464 */
    465 TEST(TestAudioDriftCorrection, DriftStepResponseUnderrunHighLatencyInput)
    466 {
    467  constexpr uint32_t nominalRate = 48000;
    468  constexpr uint32_t interval = nominalRate;
    469  constexpr uint32_t iterations = 200;
    470  const PrincipalHandle testPrincipal =
    471      MakePrincipalHandle(nsContentUtils::GetSystemPrincipal());
    472  uint32_t inputRate1 = nominalRate * 1005 / 1000;  // 0.5% drift
    473  uint32_t inputInterval1 = inputRate1;
    474  AudioGenerator<AudioDataValue> tone(1, nominalRate, 440);
    475  AudioDriftCorrection ad(nominalRate, nominalRate, testPrincipal);
    476  for (uint32_t i = 0; i < interval * iterations; i += interval / 100) {
    477    AudioSegment inSegment;
    478    if (i > 0 && i % interval == 0) {
    479      tone.Generate(inSegment, inputInterval1);
    480    }
    481    ad.RequestFrames(inSegment, interval / 100);
    482  }
    483 
    484  uint32_t inputRate2 = nominalRate * 995 / 1000;  // -0.5% drift
    485  uint32_t inputInterval2 = inputRate2;
    486  for (uint32_t i = 0; i < interval * iterations; i += interval / 100) {
    487    AudioSegment inSegment;
    488    // The first segment is skipped to cause an underrun.
    489    if (i > 0 && i % interval == 0) {
    490      tone.Generate(inSegment, inputInterval2);
    491    }
    492    ad.RequestFrames(inSegment, interval / 100);
    493    if (i >= interval * 8 / 10 && i < interval) {
    494      // While the DynamicResampler has not set its pre-buffer after the
    495      // underrun, InFramesBuffered() reports the pre-buffer size.
    496      // The initial desired buffer and pre-buffer size was
    497      // inputInterval1 * 11 / 10 to accomodate the large input block size.
    498      // This was doubled when the underrun occurred.
    499      EXPECT_EQ(ad.CurrentBuffering(), inputInterval1 * 11 / 10 * 2)
    500          << "for i=" << i;
    501    } else if (i == interval) {
    502      // After the pre-buffer was set and used to generate the first output
    503      // with a non-empty input segment after the underun, the actual number
    504      // of frames buffered almost matches the pre-buffer size, with some
    505      // rounding from output to input frame count conversion.
    506      EXPECT_NEAR(ad.CurrentBuffering(), inputInterval1 * 11 / 10 * 2, 1)
    507          << "after first input after underrun";
    508    }
    509  }
    510 
    511  // The initial desired buffering and pre-buffering level was
    512  // inputInterval1 * 11 / 10 to accomodate the large input block size.
    513  // The buffer size was initially twice the pre-buffering level, and then
    514  // doubled when the underrun occurred.
    515  EXPECT_EQ(ad.BufferSize(), inputInterval1 * 11 / 10 * 2 * 2);
    516  EXPECT_EQ(ad.NumUnderruns(), 1u);
    517 }
    518 
    519 /**
    520 * Similar to DriftStepResponse but with a high-latency input, and will overrun
    521 * (input callback buffer is larger than AudioDriftCorrection's ring buffer for
    522 * input data) to allow testing the overrun handling. This is helpful to run
    523 * together with
    524 *   MOZ_LOG=raw,DriftControllerGraphs:5 MOZ_LOG_FILE=./plot_values.csv
    525 */
    526 TEST(TestAudioDriftCorrection, DriftStepResponseOverrun)
    527 {
    528  constexpr uint32_t nominalRate = 48000;
    529  constexpr uint32_t interval = nominalRate;
    530  constexpr uint32_t inputRate = nominalRate * 1005 / 1000;  // 0.5% drift
    531  constexpr uint32_t inputInterval = inputRate;
    532  constexpr uint32_t iterations = 200;
    533  const PrincipalHandle testPrincipal =
    534      MakePrincipalHandle(nsContentUtils::GetSystemPrincipal());
    535 
    536  AudioGenerator<AudioDataValue> tone(1, nominalRate, 440);
    537  AudioDriftCorrection ad(nominalRate, nominalRate, testPrincipal);
    538 
    539  for (uint32_t i = 0; i < interval * iterations; i += interval / 100) {
    540    AudioSegment inSegment;
    541    tone.Generate(inSegment, inputInterval / 100);
    542    ad.RequestFrames(inSegment, interval / 100);
    543  }
    544 
    545  // Change input callbacks to 1000ms (+0.5% drift) = 48200 frames, which will
    546  // overrun the ring buffer.
    547  for (uint32_t i = 0; i < interval * iterations; i += interval / 100) {
    548    AudioSegment inSegment;
    549    if (i > 0 && i % interval == 0) {
    550      // This simulates the input stream latency increasing externally. It's
    551      // building up a second worth of data before the next callback. This also
    552      // causes an underrun.
    553      tone.Generate(inSegment, inputInterval);
    554    }
    555    ad.RequestFrames(inSegment, interval / 100);
    556  }
    557 
    558  // The desired buffering and pre-buffering levels were increased to
    559  // inputInterval * 11 / 10 to accomodate the large input block size.
    560  // The buffer size was increased to twice the pre-buffering level.
    561  EXPECT_EQ(ad.BufferSize(), inputInterval * 11 / 10 * 2);
    562  EXPECT_EQ(ad.NumUnderruns(), 1u);
    563 }