| <!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> |