diff options
Diffstat (limited to 'testing/web-platform/tests/webaudio/the-audio-api/the-audiobuffersourcenode-interface/sub-sample-buffer-stitching.html')
-rw-r--r-- | testing/web-platform/tests/webaudio/the-audio-api/the-audiobuffersourcenode-interface/sub-sample-buffer-stitching.html | 133 |
1 files changed, 133 insertions, 0 deletions
diff --git a/testing/web-platform/tests/webaudio/the-audio-api/the-audiobuffersourcenode-interface/sub-sample-buffer-stitching.html b/testing/web-platform/tests/webaudio/the-audio-api/the-audiobuffersourcenode-interface/sub-sample-buffer-stitching.html new file mode 100644 index 0000000000..3700bfa8ce --- /dev/null +++ b/testing/web-platform/tests/webaudio/the-audio-api/the-audiobuffersourcenode-interface/sub-sample-buffer-stitching.html @@ -0,0 +1,133 @@ +<!doctype html> +<html> + <head> + <title> + Test Sub-Sample Accurate Stitching of ABSNs + </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> + </head> + <body> + <script> + let audit = Audit.createTaskRunner(); + + audit.define( + { + label: 'buffer-stitching-1', + description: 'Subsample buffer stitching, same rates' + }, + (task, should) => { + const sampleRate = 44100; + const bufferRate = 44100; + const bufferLength = 30; + + // Experimentally determined thresholds. DO NOT relax these values + // to far from these values to make the tests pass. + const errorThreshold = 9.0957e-5; + const snrThreshold = 85.580; + + // Informative message + should(sampleRate, 'Test 1: context.sampleRate') + .beEqualTo(sampleRate); + testBufferStitching(sampleRate, bufferRate, bufferLength) + .then(resultBuffer => { + const actual = resultBuffer.getChannelData(0); + const expected = resultBuffer.getChannelData(1); + should( + actual, + `Stitched sine-wave buffers at sample rate ${bufferRate}`) + .beCloseToArray( + expected, {absoluteThreshold: errorThreshold}); + const SNR = 10 * Math.log10(computeSNR(actual, expected)); + should(SNR, `SNR (${SNR} dB)`) + .beGreaterThanOrEqualTo(snrThreshold); + }) + .then(() => task.done()); + }); + + audit.define( + { + label: 'buffer-stitching-2', + description: 'Subsample buffer stitching, different rates' + }, + (task, should) => { + const sampleRate = 44100; + const bufferRate = 43800; + const bufferLength = 30; + + // Experimentally determined thresholds. DO NOT relax these values + // to far from these values to make the tests pass. + const errorThreshold = 3.8986e-3; + const snrThreshold = 65.737; + + // Informative message + should(sampleRate, 'Test 2: context.sampleRate') + .beEqualTo(sampleRate); + testBufferStitching(sampleRate, bufferRate, bufferLength) + .then(resultBuffer => { + const actual = resultBuffer.getChannelData(0); + const expected = resultBuffer.getChannelData(1); + should( + actual, + `Stitched sine-wave buffers at sample rate ${bufferRate}`) + .beCloseToArray( + expected, {absoluteThreshold: errorThreshold}); + const SNR = 10 * Math.log10(computeSNR(actual, expected)); + should(SNR, `SNR (${SNR} dB)`) + .beGreaterThanOrEqualTo(snrThreshold); + }) + .then(() => task.done()); + }); + + audit.run(); + + // Create graph to test stitching of consecutive ABSNs. The context rate + // is |sampleRate|, and the buffers have a fixed length of |bufferLength| + // and rate of |bufferRate|. The |bufferRate| should not be too different + // from |sampleRate| because of interpolation of the buffer to the context + // rate. + function testBufferStitching(sampleRate, bufferRate, bufferLength) { + // The context for testing. Channel 0 contains the output from + // stitching all the buffers together, and channel 1 contains the + // expected output. + const context = new OfflineAudioContext( + {numberOfChannels: 2, length: sampleRate, sampleRate: sampleRate}); + + const merger = new ChannelMergerNode( + context, {numberOfInputs: context.destination.channelCount}); + + merger.connect(context.destination); + + // The reference is a sine wave at 440 Hz. + const ref = new OscillatorNode(context, {frequency: 440, type: 'sine'}); + ref.connect(merger, 0, 1); + ref.start(); + + // The test signal is a bunch of short AudioBufferSources containing + // bits of a sine wave. + let waveSignal = new Float32Array(context.length); + const omega = 2 * Math.PI / bufferRate * ref.frequency.value; + for (let k = 0; k < context.length; ++k) { + waveSignal[k] = Math.sin(omega * k); + } + + // Slice the sine wave into many little buffers to be assigned to ABSNs + // that are started at the appropriate times to produce a final sine + // wave. + for (let k = 0; k < context.length; k += bufferLength) { + const buffer = + new AudioBuffer({length: bufferLength, sampleRate: bufferRate}); + buffer.copyToChannel(waveSignal.slice(k, k + bufferLength), 0); + + const src = new AudioBufferSourceNode(context, {buffer: buffer}); + src.connect(merger, 0, 0); + src.start(k / bufferRate); + } + + return context.startRendering(); + } + </script> + </body> +</html> |