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