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
;
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
);
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();
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
)
111 .ToBase
<media::TimeUnit::CeilingPolicy
>(aTargetRate
)
112 .ToTicksAtRate(aTargetRate
),
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
,
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();
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
,
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();
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
));
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
) {
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
);
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
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
390 EXPECT_EQ(outToneVerifier
.PreSilenceSamples(), 2528U);
391 EXPECT_NEAR(outToneVerifier
.PreSilenceSamples() - transmitterBlockSize1
,
392 media::TimeUnit::FromSeconds(0.05).ToTicksAtRate(sampleRate
) +
393 receiverBlockSize
- transmitterBlockSize1
,
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)
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
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);