<!DOCTYPE html> <!-- Tests that we are able to schedule a series of notes to playback with sample-accuracy. We use an impulse so we can tell exactly where the rendering is happening. --> <html> <head> <title> sample-accurate-scheduling.html </title> <script src="/resources/testharness.js"></script> <script src="/resources/testharnessreport.js"></script> <script src="/webaudio/resources/audit-util.js"></script> <script src="/webaudio/resources/audit.js"></script> <script src="/webaudio/resources/buffer-loader.js"></script> </head> <body> <script id="layout-test-code"> let audit = Audit.createTaskRunner(); let sampleRate = 44100.0; let lengthInSeconds = 4; let context = 0; let bufferLoader = 0; let impulse; // See if we can render at exactly these sample offsets. let sampleOffsets = [0, 3, 512, 517, 1000, 1005, 20000, 21234, 37590]; function createImpulse() { // An impulse has a value of 1 at time 0, and is otherwise 0. impulse = context.createBuffer(2, 512, sampleRate); let sampleDataL = impulse.getChannelData(0); let sampleDataR = impulse.getChannelData(1); sampleDataL[0] = 1.0; sampleDataR[0] = 1.0; } function playNote(time) { let bufferSource = context.createBufferSource(); bufferSource.buffer = impulse; bufferSource.connect(context.destination); bufferSource.start(time); } function checkSampleAccuracy(buffer, should) { let bufferDataL = buffer.getChannelData(0); let bufferDataR = buffer.getChannelData(1); let impulseCount = 0; let badOffsetCount = 0; // Left and right channels must be the same. should(bufferDataL, 'Content of left and right channels match and') .beEqualToArray(bufferDataR); // Go through every sample and make sure it's 0, except at positions in // sampleOffsets. for (let i = 0; i < buffer.length; ++i) { if (bufferDataL[i] != 0) { // Make sure this index is in sampleOffsets let found = false; for (let j = 0; j < sampleOffsets.length; ++j) { if (sampleOffsets[j] == i) { found = true; break; } } ++impulseCount; should(found, 'Non-zero sample found at sample offset ' + i) .beTrue(); if (!found) { ++badOffsetCount; } } } should(impulseCount, 'Number of impulses found') .beEqualTo(sampleOffsets.length); if (impulseCount == sampleOffsets.length) { should(badOffsetCount, 'bad offset').beEqualTo(0); } } audit.define( {label: 'test', description: 'Test sample-accurate scheduling'}, function(task, should) { // Create offline audio context. context = new OfflineAudioContext( 2, sampleRate * lengthInSeconds, sampleRate); createImpulse(); for (let i = 0; i < sampleOffsets.length; ++i) { let timeInSeconds = sampleOffsets[i] / sampleRate; playNote(timeInSeconds); } context.startRendering().then(function(buffer) { checkSampleAccuracy(buffer, should); task.done(); }); }); audit.run(); </script> </body> </html>