| <!DOCTYPE html> |
| <html> |
| <head> |
| <title> |
| AudioBufferSourceNode - playbackRate test |
| </title> |
| <script src="../../imported/w3c/web-platform-tests/resources/testharness.js"></script> |
| <script src="../../resources/testharnessreport.js"></script> |
| <script src="../resources/audit-util.js"></script> |
| <script src="../resources/audit.js"></script> |
| </head> |
| <body> |
| <script id="layout-test-code"> |
| let audit = Audit.createTaskRunner(); |
| |
| // Any sample rate mutiple of 128 is valid for this test, but here it uses |
| // 48000Hz because it is a commonly used number that happens to be |
| // multiple of 128. |
| let sampleRate = 32768; |
| |
| // The test iterates over 60 pitches starting from 36. (MIDI pitch of C2) |
| let fundamentalPitch = 36; |
| let numberOfPitches = 60; |
| |
| let noteDuration = Math.floor(0.025 * sampleRate) / sampleRate; |
| let totalDuration = noteDuration * numberOfPitches; |
| |
| // Test constraints for each octave. |
| let testConstraints = [ |
| {thresholdSNR: 97.215, thresholdDiffULP: 0.6446}, |
| {thresholdSNR: 97.212, thresholdDiffULP: 0.6446}, |
| {thresholdSNR: 97.217, thresholdDiffULP: 0.6446}, |
| {thresholdSNR: 97.196, thresholdDiffULP: 0.6485}, |
| {thresholdSNR: 97.074, thresholdDiffULP: 0.6915} |
| ]; |
| |
| function pitchToFrequency(midiPitch) { |
| return 440 * Math.pow(2, (Math.floor(midiPitch) - 69) / 12); |
| } |
| |
| function pitchDiffToPlaybackRate(midiPitchDiff) { |
| return Math.pow(2, midiPitchDiff / 12); |
| } |
| |
| function createSineWaveBuffer(context, frequency, duration) { |
| let buffer = context.createBuffer(1, duration * sampleRate, sampleRate); |
| let data = buffer.getChannelData(0); |
| let omega = 2 * Math.PI * frequency / sampleRate; |
| for (let i = 0; i < data.length; i++) |
| data[i] = Math.sin(omega * i); |
| |
| return buffer; |
| } |
| |
| // This is the fundamental buffer for playbackRate modulation. The |
| // duration of this buffer is arbitrary but long enough to produce the |
| // sound without running short. |
| let fundamentalBuffer; |
| |
| // A unit test consists of 2 sources: the 'actual' source runs a buffer |
| // with the playback rate modulated and the 'expected' source runs a |
| // mathmatically generated sound buffer. |
| function runUnitTest(context, noteStart, notePitch) { |
| let actualSrc = context.createBufferSource(); |
| let expectedSrc = context.createBufferSource(); |
| let merger = context.createChannelMerger(2); |
| |
| actualSrc.buffer = fundamentalBuffer; |
| expectedSrc.buffer = createSineWaveBuffer( |
| context, pitchToFrequency(notePitch), noteDuration); |
| actualSrc.playbackRate.value = |
| pitchDiffToPlaybackRate(notePitch - fundamentalPitch); |
| |
| actualSrc.connect(merger, 0, 0); |
| expectedSrc.connect(merger, 0, 1); |
| merger.connect(context.destination); |
| |
| actualSrc.start(noteStart); |
| actualSrc.stop(noteStart + noteDuration); |
| expectedSrc.start(noteStart); |
| expectedSrc.stop(noteStart + noteDuration); |
| } |
| |
| |
| // Test if AudioBufferSourceNode.playbackRate can playback at different |
| // rates properly. |
| audit.define('playbackrate-test', (task, should) => { |
| let context = |
| new OfflineAudioContext(2, totalDuration * sampleRate, sampleRate); |
| fundamentalBuffer = createSineWaveBuffer( |
| context, pitchToFrequency(fundamentalPitch), totalDuration); |
| |
| // Schedule tests up to 60 pitches above from the fundamental pitch. |
| for (let iteration = 0; iteration < numberOfPitches; iteration++) |
| runUnitTest( |
| context, noteDuration * iteration, fundamentalPitch + iteration); |
| |
| // Once the rendering is complete, split the buffer into 5 octaves. Then |
| // perform the SNR and the maximum difference ULP check for each octave |
| // with different constraints. |
| context.startRendering() |
| .then(function(renderedBuffer) { |
| let actual = renderedBuffer.getChannelData(0); |
| let expected = renderedBuffer.getChannelData(1); |
| let octaveLength = Math.floor(noteDuration * 12 * sampleRate); |
| |
| for (let i = 0; i < numberOfPitches / 12; i++) { |
| let start = i * octaveLength; |
| let end = (i + 1) * octaveLength; |
| let octaveActual = actual.subarray(start, end); |
| let octaveExpected = expected.subarray(start, end); |
| |
| compareBuffersWithConstraints( |
| should, octaveActual, octaveExpected, { |
| prefix: i, |
| thresholdSNR: testConstraints[i].thresholdSNR, |
| thresholdDiffULP: testConstraints[i].thresholdDiffULP |
| }); |
| } |
| |
| }) |
| .then(() => task.done()); |
| }); |
| |
| audit.run(); |
| </script> |
| </body> |
| </html> |