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 }