diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:22:09 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:22:09 +0000 |
commit | 43a97878ce14b72f0981164f87f2e35e14151312 (patch) | |
tree | 620249daf56c0258faa40cbdcf9cfba06de2a846 /testing/web-platform/tests/webaudio/the-audio-api/the-audiobuffer-interface | |
parent | Initial commit. (diff) | |
download | firefox-upstream.tar.xz firefox-upstream.zip |
Adding upstream version 110.0.1.upstream/110.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'testing/web-platform/tests/webaudio/the-audio-api/the-audiobuffer-interface')
8 files changed, 845 insertions, 0 deletions
diff --git a/testing/web-platform/tests/webaudio/the-audio-api/the-audiobuffer-interface/acquire-the-content.html b/testing/web-platform/tests/webaudio/the-audio-api/the-audiobuffer-interface/acquire-the-content.html new file mode 100644 index 0000000000..659a69c866 --- /dev/null +++ b/testing/web-platform/tests/webaudio/the-audio-api/the-audiobuffer-interface/acquire-the-content.html @@ -0,0 +1,85 @@ +<!doctype html> +<meta charset="utf-8"> +<title>Test for AudioBuffer's "acquire the content" operation</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script> +const SAMPLERATE = 8000; +const LENGTH = 128; + +var tests = { + "AudioBufferSourceNode setter set with non-null buffer": function(oac) { + var buf = oac.createBuffer(1, LENGTH, SAMPLERATE) + var bs = new AudioBufferSourceNode(oac); + var channelData = buf.getChannelData(0); + for (var i = 0; i < channelData.length; i++) { + channelData[i] = 1.0; + } + bs.buffer = buf; + bs.start(); // This acquires the content since buf is not null + for (var i = 0; i < channelData.length; i++) { + channelData[i] = 0.5; + } + allSamplesAtOne(buf, "reading back"); + bs.connect(oac.destination); + return oac.startRendering(); + }, + "AudioBufferSourceNode buffer setter set with null" : (oac) => { + var buf = oac.createBuffer(1, LENGTH, SAMPLERATE) + var bs = new AudioBufferSourceNode(oac); + var channelData = buf.getChannelData(0); + for (var i = 0; i < channelData.length; i++) { + channelData[i] = 1.0; + } + bs.buffer = null; + bs.start(); // This does not acquire the content + bs.buffer = buf; // This does + for (var i = 0; i < channelData.length; i++) { + channelData[i] = 0.5; + } + allSamplesAtOne(buf, "reading back"); + bs.connect(oac.destination); + return oac.startRendering(); + }, + "ConvolverNode": (oac) => { + var buf = oac.createBuffer(1, LENGTH, SAMPLERATE) + var impulse = oac.createBuffer(1, 1, SAMPLERATE) + var bs = new AudioBufferSourceNode(oac); + var convolver = new ConvolverNode(oac, {disableNormalization: true}); + + impulse.getChannelData(0)[0] = 1.0; // unit impulse function + convolver.buffer = impulse; // This does acquire the content + impulse.getChannelData(0)[0] = 0.5; + + var channelData = buf.getChannelData(0); + for (var i = 0; i < channelData.length; i++) { + channelData[i] = 1.0; + } + bs.buffer = buf; + bs.start(); + + bs.connect(convolver).connect(oac.destination); + return oac.startRendering(); + } +}; + +function allSamplesAtOne(audiobuffer, location) { + var buf = audiobuffer.getChannelData(0); + for (var i = 0; i < buf.length; i++) { + // The convolver can introduce a slight numerical error. + if (Math.abs(buf[i] - 1.0) > 0.0001) { + assert_true(false, `Invalid value at index ${i}, expecte close to 1.0, found ${buf[i]} when ${location}`) + return Promise.reject(); + } + } + assert_true(true, `Buffer unmodified when ${location}.`); + return Promise.resolve(); +} + +for (const test of Object.keys(tests)) { + promise_test(async function(t) { + var buf = await tests[test](new OfflineAudioContext(1, LENGTH, SAMPLERATE)); + return allSamplesAtOne(buf, "rendering"); + }, test); +}; +</script> diff --git a/testing/web-platform/tests/webaudio/the-audio-api/the-audiobuffer-interface/audiobuffer-copy-channel.html b/testing/web-platform/tests/webaudio/the-audio-api/the-audiobuffer-interface/audiobuffer-copy-channel.html new file mode 100644 index 0000000000..c0cd49d325 --- /dev/null +++ b/testing/web-platform/tests/webaudio/the-audio-api/the-audiobuffer-interface/audiobuffer-copy-channel.html @@ -0,0 +1,330 @@ +<!DOCTYPE html> +<html> + <head> + <title> + Test Basic Functionality of AudioBuffer.copyFromChannel and + AudioBuffer.copyToChannel + </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 id="layout-test-code"> + // Define utility routines. + + // Initialize the AudioBuffer |buffer| with a ramp signal on each channel. + // The ramp starts at channel number + 1. + function initializeAudioBufferRamp(buffer) { + for (let c = 0; c < buffer.numberOfChannels; ++c) { + let d = buffer.getChannelData(c); + for (let k = 0; k < d.length; ++k) { + d[k] = k + c + 1; + } + } + } + + // Create a Float32Array of length |length| and initialize the array to + // -1. + function createInitializedF32Array(length) { + let x = new Float32Array(length); + for (let k = 0; k < length; ++k) { + x[k] = -1; + } + return x; + } + + // Create a Float32Array of length |length| that is initialized to be a + // ramp starting at 1. + function createFloat32RampArray(length) { + let x = new Float32Array(length); + for (let k = 0; k < x.length; ++k) { + x[k] = k + 1; + } + + return x; + } + + // Test that the array |x| is a ramp starting at value |start| of length + // |length|, starting at |startIndex| in the array. |startIndex| is + // optional and defaults to 0. Any other values must be -1. + function shouldBeRamp( + should, testName, x, startValue, length, startIndex) { + let k; + let startingIndex = startIndex || 0; + let expected = Array(x.length); + + // Fill the expected array with the correct results. + + // The initial part (if any) must be -1. + for (k = 0; k < startingIndex; ++k) { + expected[k] = -1; + } + + // The second part should be a ramp starting with |startValue| + for (; k < startingIndex + length; ++k) { + expected[k] = startValue + k - startingIndex; + } + + // The last part (if any) should be -1. + for (; k < x.length; ++k) { + expected[k] = -1; + } + + should(x, testName, {numberOfArrayLog: 32}).beEqualToArray(expected); + } + + let audit = Audit.createTaskRunner(); + + let context = new AudioContext(); + // Temp array for testing exceptions for copyToChannel/copyFromChannel. + // The length is arbitrary. + let x = new Float32Array(8); + + // Number of frames in the AudioBuffer for testing. This is pretty + // arbitrary so choose a fairly small value. + let bufferLength = 16; + + // Number of channels in the AudioBuffer. Also arbitrary, but it should + // be greater than 1 for test coverage. + let numberOfChannels = 3; + + // AudioBuffer that will be used for testing copyFrom and copyTo. + let buffer = context.createBuffer( + numberOfChannels, bufferLength, context.sampleRate); + + let initialValues = Array(numberOfChannels); + + // Initialize things + audit.define('initialize', (task, should) => { + // Initialize to -1. + initialValues.fill(-1); + should(initialValues, 'Initialized values').beConstantValueOf(-1) + task.done(); + }); + + // Test that expected exceptions are signaled for copyFrom. + audit.define('copyFrom-exceptions', (task, should) => { + should( + AudioBuffer.prototype.copyFromChannel, + 'AudioBuffer.prototype.copyFromChannel') + .exist(); + + should( + () => { + buffer = context.createBuffer( + numberOfChannels, bufferLength, context.sampleRate); + }, + '0: buffer = context.createBuffer(' + numberOfChannels + ', ' + + bufferLength + ', context.sampleRate)') + .notThrow(); + should(() => { + buffer.copyFromChannel(null, 0); + }, '1: buffer.copyFromChannel(null, 0)').throw(TypeError); + should(() => { + buffer.copyFromChannel(context, 0); + }, '2: buffer.copyFromChannel(context, 0)').throw(TypeError); + should(() => { + buffer.copyFromChannel(x, -1); + }, '3: buffer.copyFromChannel(x, -1)').throw(DOMException, 'IndexSizeError'); + should( + () => { + buffer.copyFromChannel(x, numberOfChannels); + }, + '4: buffer.copyFromChannel(x, ' + numberOfChannels + ')') + .throw(DOMException, 'IndexSizeError'); + ; + should(() => { + buffer.copyFromChannel(x, 0, -1); + }, '5: buffer.copyFromChannel(x, 0, -1)').notThrow(); + should( + () => { + buffer.copyFromChannel(x, 0, bufferLength); + }, + '6: buffer.copyFromChannel(x, 0, ' + bufferLength + ')') + .notThrow(); + + should(() => { + buffer.copyFromChannel(x, 3); + }, '7: buffer.copyFromChannel(x, 3)').throw(DOMException, 'IndexSizeError'); + + // See https://github.com/whatwg/html/issues/5380 for why not `new SharedArrayBuffer()` + // WebAssembly.Memory's size is in multiples of 64 KiB + const shared_buffer = new Float32Array(new WebAssembly.Memory({ shared:true, initial:1, maximum:1 }).buffer); + should( + () => { + buffer.copyFromChannel(shared_buffer, 0); + }, + '8: buffer.copyFromChannel(SharedArrayBuffer view, 0)') + .throw(TypeError); + + should( + () => { + buffer.copyFromChannel(shared_buffer, 0, 0); + }, + '9: buffer.copyFromChannel(SharedArrayBuffer view, 0, 0)') + .throw(TypeError); + + task.done(); + }); + + // Test that expected exceptions are signaled for copyTo. + audit.define('copyTo-exceptions', (task, should) => { + should( + AudioBuffer.prototype.copyToChannel, + 'AudioBuffer.prototype.copyToChannel') + .exist(); + should(() => { + buffer.copyToChannel(null, 0); + }, '0: buffer.copyToChannel(null, 0)').throw(TypeError); + should(() => { + buffer.copyToChannel(context, 0); + }, '1: buffer.copyToChannel(context, 0)').throw(TypeError); + should(() => { + buffer.copyToChannel(x, -1); + }, '2: buffer.copyToChannel(x, -1)').throw(DOMException, 'IndexSizeError'); + should( + () => { + buffer.copyToChannel(x, numberOfChannels); + }, + '3: buffer.copyToChannel(x, ' + numberOfChannels + ')') + .throw(DOMException, 'IndexSizeError'); + should(() => { + buffer.copyToChannel(x, 0, -1); + }, '4: buffer.copyToChannel(x, 0, -1)').notThrow(); + should( + () => { + buffer.copyToChannel(x, 0, bufferLength); + }, + '5: buffer.copyToChannel(x, 0, ' + bufferLength + ')') + .notThrow(); + + should(() => { + buffer.copyToChannel(x, 3); + }, '6: buffer.copyToChannel(x, 3)').throw(DOMException, 'IndexSizeError'); + + // See https://github.com/whatwg/html/issues/5380 for why not `new SharedArrayBuffer()` + // WebAssembly.Memory's size is in multiples of 64 KiB + const shared_buffer = new Float32Array(new WebAssembly.Memory({ shared:true, initial:1, maximum:1 }).buffer); + should( + () => { + buffer.copyToChannel(shared_buffer, 0); + }, + '7: buffer.copyToChannel(SharedArrayBuffer view, 0)') + .throw(TypeError); + + should( + () => { + buffer.copyToChannel(shared_buffer, 0, 0); + }, + '8: buffer.copyToChannel(SharedArrayBuffer view, 0, 0)') + .throw(TypeError); + + task.done(); + }); + + // Test copyFromChannel + audit.define('copyFrom-validate', (task, should) => { + // Initialize the AudioBuffer to a ramp for testing copyFrom. + initializeAudioBufferRamp(buffer); + + // Test copyFrom operation with a short destination array, filling the + // destination completely. + for (let c = 0; c < numberOfChannels; ++c) { + let dst8 = createInitializedF32Array(8); + buffer.copyFromChannel(dst8, c); + shouldBeRamp( + should, 'buffer.copyFromChannel(dst8, ' + c + ')', dst8, c + 1, 8) + } + + // Test copyFrom operation with a short destination array using a + // non-zero start index that still fills the destination completely. + for (let c = 0; c < numberOfChannels; ++c) { + let dst8 = createInitializedF32Array(8); + buffer.copyFromChannel(dst8, c, 1); + shouldBeRamp( + should, 'buffer.copyFromChannel(dst8, ' + c + ', 1)', dst8, c + 2, + 8) + } + + // Test copyFrom operation with a short destination array using a + // non-zero start index that does not fill the destinatiom completely. + // The extra elements should be unchanged. + for (let c = 0; c < numberOfChannels; ++c) { + let dst8 = createInitializedF32Array(8); + let startInChannel = bufferLength - 5; + buffer.copyFromChannel(dst8, c, startInChannel); + shouldBeRamp( + should, + 'buffer.copyFromChannel(dst8, ' + c + ', ' + startInChannel + ')', + dst8, c + 1 + startInChannel, bufferLength - startInChannel); + } + + // Copy operation with the destination longer than the buffer, leaving + // the trailing elements of the destination untouched. + for (let c = 0; c < numberOfChannels; ++c) { + let dst26 = createInitializedF32Array(bufferLength + 10); + buffer.copyFromChannel(dst26, c); + shouldBeRamp( + should, 'buffer.copyFromChannel(dst26, ' + c + ')', dst26, c + 1, + bufferLength); + } + + task.done(); + }); + + // Test copyTo + audit.define('copyTo-validate', (task, should) => { + // Create a source consisting of a ramp starting at 1, longer than the + // AudioBuffer + let src = createFloat32RampArray(bufferLength + 10); + + // Test copyTo with AudioBuffer shorter than Float32Array. The + // AudioBuffer should be completely filled with the Float32Array. + should( + () => { + buffer = + createConstantBuffer(context, bufferLength, initialValues); + }, + 'buffer = createConstantBuffer(context, ' + bufferLength + ', [' + + initialValues + '])') + .notThrow(); + + for (let c = 0; c < numberOfChannels; ++c) { + buffer.copyToChannel(src, c); + shouldBeRamp( + should, 'buffer.copyToChannel(src, ' + c + ')', + buffer.getChannelData(c), 1, bufferLength); + } + + // Test copyTo with AudioBuffer longer than the Float32Array. The tail + // of the AudioBuffer should be unchanged. + buffer = createConstantBuffer(context, bufferLength, initialValues); + let src10 = createFloat32RampArray(10); + for (let c = 0; c < numberOfChannels; ++c) { + buffer.copyToChannel(src10, c); + shouldBeRamp( + should, 'buffer.copyToChannel(src10, ' + c + ')', + buffer.getChannelData(c), 1, 10); + } + + // Test copyTo with non-default startInChannel. Part of the AudioBuffer + // should filled with the beginning and end sections untouched. + buffer = createConstantBuffer(context, bufferLength, initialValues); + for (let c = 0; c < numberOfChannels; ++c) { + let startInChannel = 5; + buffer.copyToChannel(src10, c, startInChannel); + + shouldBeRamp( + should, + 'buffer.copyToChannel(src10, ' + c + ', ' + startInChannel + ')', + buffer.getChannelData(c), 1, src10.length, startInChannel); + } + task.done(); + }); + + audit.run(); + </script> + </body> +</html> diff --git a/testing/web-platform/tests/webaudio/the-audio-api/the-audiobuffer-interface/audiobuffer-getChannelData.html b/testing/web-platform/tests/webaudio/the-audio-api/the-audiobuffer-interface/audiobuffer-getChannelData.html new file mode 100644 index 0000000000..612a91cf4e --- /dev/null +++ b/testing/web-platform/tests/webaudio/the-audio-api/the-audiobuffer-interface/audiobuffer-getChannelData.html @@ -0,0 +1,66 @@ +<!DOCTYPE html> +<html> + <head> + <title> + Test AudioBuffer.getChannelData() Returns the Same Object + </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/audioparam-testing.js"></script> + </head> + <body> + <script id="layout-test-code"> + let sampleRate = 48000; + let renderDuration = 0.5; + + let audit = Audit.createTaskRunner(); + + audit.define('buffer-eq', (task, should) => { + // Verify that successive calls to getChannelData return the same + // buffer. + let context = new AudioContext(); + let channelCount = 2; + let frameLength = 1000; + let buffer = + context.createBuffer(channelCount, frameLength, context.sampleRate); + + for (let c = 0; c < channelCount; ++c) { + let a = buffer.getChannelData(c); + let b = buffer.getChannelData(c); + + let message = 'buffer.getChannelData(' + c + ')'; + should(a === b, message + ' === ' + message).beEqualTo(true); + } + + task.done(); + }); + + audit.define('buffer-not-eq', (task, should) => { + let context = new AudioContext(); + let channelCount = 2; + let frameLength = 1000; + let buffer1 = + context.createBuffer(channelCount, frameLength, context.sampleRate); + let buffer2 = + context.createBuffer(channelCount, frameLength, context.sampleRate); + let success = true; + + for (let c = 0; c < channelCount; ++c) { + let a = buffer1.getChannelData(c); + let b = buffer2.getChannelData(c); + + let message = 'getChannelData(' + c + ')'; + should(a === b, 'buffer1.' + message + ' === buffer2.' + message) + .beEqualTo(false) && + success; + } + + task.done(); + }); + + audit.run(); + </script> + </body> +</html> diff --git a/testing/web-platform/tests/webaudio/the-audio-api/the-audiobuffer-interface/audiobuffer-reuse.html b/testing/web-platform/tests/webaudio/the-audio-api/the-audiobuffer-interface/audiobuffer-reuse.html new file mode 100644 index 0000000000..dabe323cbe --- /dev/null +++ b/testing/web-platform/tests/webaudio/the-audio-api/the-audiobuffer-interface/audiobuffer-reuse.html @@ -0,0 +1,36 @@ +<!doctype html> +<meta charset="utf-8"> +<title>AudioBuffer can be reused between AudioBufferSourceNodes</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script> +function render_audio_context() { + let sampleRate = 44100; + let context = new OfflineAudioContext( + 2, sampleRate * 0.1, sampleRate); + let buf = context.createBuffer(1, 0.1 * sampleRate, context.sampleRate); + let data = buf.getChannelData(0); + data[0] = 0.5; + data[1] = 0.25; + let b1 = context.createBufferSource(); + b1.buffer = buf; + b1.start(); + let b2 = context.createBufferSource(); + b2.buffer = buf; + b2.start(); + let merger = context.createChannelMerger(2); + b1.connect(merger, 0, 0); + b2.connect(merger, 0, 1); + merger.connect(context.destination); + return context.startRendering(); +} +promise_test(function() { + return render_audio_context() + .then(function(buffer) { + assert_equals(buffer.getChannelData(0)[0], 0.5); + assert_equals(buffer.getChannelData(1)[0], 0.5); + assert_equals(buffer.getChannelData(0)[1], 0.25); + assert_equals(buffer.getChannelData(1)[1], 0.25); + }); +}, "AudioBuffer can be reused between AudioBufferSourceNodes"); +</script> diff --git a/testing/web-platform/tests/webaudio/the-audio-api/the-audiobuffer-interface/audiobuffer.html b/testing/web-platform/tests/webaudio/the-audio-api/the-audiobuffer-interface/audiobuffer.html new file mode 100644 index 0000000000..a2c4581c4e --- /dev/null +++ b/testing/web-platform/tests/webaudio/the-audio-api/the-audiobuffer-interface/audiobuffer.html @@ -0,0 +1,71 @@ +<!DOCTYPE html> +<html> + <head> + <title> + audiobuffer.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> + </head> + <body> + <script id="layout-test-code"> + let sampleRate = 44100.0 + let lengthInSeconds = 2; + let numberOfChannels = 4; + + let audit = Audit.createTaskRunner(); + + audit.define('Basic tests for AudioBuffer', function(task, should) { + let context = new AudioContext(); + let buffer = context.createBuffer( + numberOfChannels, sampleRate * lengthInSeconds, sampleRate); + + // Just for printing out a message describing what "buffer" is in the + // following tests. + should( + true, + 'buffer = context.createBuffer(' + numberOfChannels + ', ' + + (sampleRate * lengthInSeconds) + ', ' + sampleRate + ')') + .beTrue(); + + should(buffer.sampleRate, 'buffer.sampleRate').beEqualTo(sampleRate); + + should(buffer.length, 'buffer.length') + .beEqualTo(sampleRate * lengthInSeconds); + + should(buffer.duration, 'buffer.duration').beEqualTo(lengthInSeconds); + + should(buffer.numberOfChannels, 'buffer.numberOfChannels') + .beEqualTo(numberOfChannels); + + for (let index = 0; index < buffer.numberOfChannels; ++index) { + should( + buffer.getChannelData(index) instanceof window.Float32Array, + 'buffer.getChannelData(' + index + + ') instanceof window.Float32Array') + .beTrue(); + } + + should( + function() { + buffer.getChannelData(buffer.numberOfChannels); + }, + 'buffer.getChannelData(' + buffer.numberOfChannels + ')') + .throw(DOMException, 'IndexSizeError'); + + let buffer2 = context.createBuffer(1, 1000, 24576); + let expectedDuration = 1000 / 24576; + + should( + buffer2.duration, 'context.createBuffer(1, 1000, 24576).duration') + .beEqualTo(expectedDuration); + + task.done(); + }); + + audit.run(); + </script> + </body> +</html> diff --git a/testing/web-platform/tests/webaudio/the-audio-api/the-audiobuffer-interface/crashtests/copyFromChannel-bufferOffset-1.html b/testing/web-platform/tests/webaudio/the-audio-api/the-audiobuffer-interface/crashtests/copyFromChannel-bufferOffset-1.html new file mode 100644 index 0000000000..564317f7de --- /dev/null +++ b/testing/web-platform/tests/webaudio/the-audio-api/the-audiobuffer-interface/crashtests/copyFromChannel-bufferOffset-1.html @@ -0,0 +1,11 @@ +<html> +<head> + <title>Test large bufferOffset in copyFromChannel()</title> +</head> +<script> + const a = new AudioBuffer({length: 0x51986, sampleRate: 44100}); + const b = new Float32Array(0x10); + a.getChannelData(0); // to avoid zero data optimization + a.copyFromChannel(b, 0, 0x1523c7cc) +</script> +</html> diff --git a/testing/web-platform/tests/webaudio/the-audio-api/the-audiobuffer-interface/crashtests/copyToChannel-bufferOffset-1.html b/testing/web-platform/tests/webaudio/the-audio-api/the-audiobuffer-interface/crashtests/copyToChannel-bufferOffset-1.html new file mode 100644 index 0000000000..999925a983 --- /dev/null +++ b/testing/web-platform/tests/webaudio/the-audio-api/the-audiobuffer-interface/crashtests/copyToChannel-bufferOffset-1.html @@ -0,0 +1,10 @@ +<html> +<head> + <title>Test large bufferOffset in copyToChannel()</title> +</head> +<script> + const a = new AudioBuffer({length: 0x10, sampleRate: 44100}); + const b = new Float32Array(0x51986); + a.copyToChannel(b, 0, 0x40004000) +</script> +</html> diff --git a/testing/web-platform/tests/webaudio/the-audio-api/the-audiobuffer-interface/ctor-audiobuffer.html b/testing/web-platform/tests/webaudio/the-audio-api/the-audiobuffer-interface/ctor-audiobuffer.html new file mode 100644 index 0000000000..fbe6e42e31 --- /dev/null +++ b/testing/web-platform/tests/webaudio/the-audio-api/the-audiobuffer-interface/ctor-audiobuffer.html @@ -0,0 +1,236 @@ +<!DOCTYPE html> +<html> + <head> + <title> + Test Constructor: AudioBuffer + </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/audionodeoptions.js"></script> + </head> + <body> + <script id="layout-test-code"> + let context; + + let audit = Audit.createTaskRunner(); + + audit.define('initialize', (task, should) => { + context = initializeContext(should); + task.done(); + }); + + audit.define('invalid constructor', (task, should) => { + should(() => { + new AudioBuffer(); + }, 'new AudioBuffer()').throw(TypeError); + should(() => { + new AudioBuffer(1); + }, 'new AudioBuffer(1)').throw(TypeError); + should(() => { + new AudioBuffer(Date, 42); + }, 'new AudioBuffer(Date, 42)').throw(TypeError); + + task.done(); + }); + + audit.define('required options', (task, should) => { + let buffer; + + // The length and sampleRate attributes are required; all others are + // optional. + should(() => { + new AudioBuffer({}); + }, 'buffer = new AudioBuffer({})').throw(TypeError); + + should(() => { + new AudioBuffer({length: 1}); + }, 'buffer = new AudioBuffer({length: 1})').throw(TypeError); + + should(() => { + new AudioBuffer({sampleRate: 48000}); + }, 'buffer = new AudioBuffer({sampleRate: 48000})').throw(TypeError); + + should(() => { + buffer = new AudioBuffer({numberOfChannels: 1}); + }, 'buffer = new AudioBuffer({numberOfChannels: 1}').throw(TypeError); + + // Length and sampleRate are required, but others are optional. + should( + () => { + buffer = + new AudioBuffer({length: 21, sampleRate: context.sampleRate}); + }, + 'buffer0 = new AudioBuffer({length: 21, sampleRate: ' + + context.sampleRate + '}') + .notThrow(); + // Verify the buffer has the correct values. + should(buffer.numberOfChannels, 'buffer0.numberOfChannels') + .beEqualTo(1); + should(buffer.length, 'buffer0.length').beEqualTo(21); + should(buffer.sampleRate, 'buffer0.sampleRate') + .beEqualTo(context.sampleRate); + + should( + () => { + buffer = new AudioBuffer( + {numberOfChannels: 3, length: 1, sampleRate: 48000}); + }, + 'buffer1 = new AudioBuffer(' + + '{numberOfChannels: 3, length: 1, sampleRate: 48000})') + .notThrow(); + // Verify the buffer has the correct values. + should(buffer.numberOfChannels, 'buffer1.numberOfChannels') + .beEqualTo(3); + should(buffer.length, 'buffer1.length').beEqualTo(1); + should(buffer.sampleRate, 'buffer1.sampleRate').beEqualTo(48000); + + task.done(); + }); + + audit.define('invalid option values', (task, should) => { + let options = {numberOfChannels: 0, length: 1, sampleRate: 16000}; + should( + () => { + let buffer = new AudioBuffer(options); + }, + 'new AudioBuffer(' + JSON.stringify(options) + ')') + .throw(DOMException, 'NotSupportedError'); + + options = {numberOfChannels: 99, length: 0, sampleRate: 16000}; + should( + () => { + let buffer = new AudioBuffer(options); + }, + 'new AudioBuffer(' + JSON.stringify(options) + ')') + .throw(DOMException, 'NotSupportedError'); + + options = {numberOfChannels: 1, length: 0, sampleRate: 16000}; + should( + () => { + let buffer = new AudioBuffer(options); + }, + 'new AudioBuffer(' + JSON.stringify(options) + ')') + .throw(DOMException, 'NotSupportedError'); + + options = {numberOfChannels: 1, length: 1, sampleRate: 100}; + should( + () => { + let buffer = new AudioBuffer(options); + }, + 'new AudioBuffer(' + JSON.stringify(options) + ')') + .throw(DOMException, 'NotSupportedError'); + + task.done(); + }); + + audit.define('default constructor', (task, should) => { + let buffer; + + let options = {numberOfChannels: 5, length: 17, sampleRate: 16000}; + should( + () => { + buffer = new AudioBuffer(options); + }, + 'buffer = new AudioBuffer(' + JSON.stringify(options) + ')') + .notThrow(); + + should(buffer.numberOfChannels, 'buffer.numberOfChannels') + .beEqualTo(options.numberOfChannels); + should(buffer.length, 'buffer.length').beEqualTo(options.length); + should(buffer.sampleRate, 'buffer.sampleRate').beEqualTo(16000); + + task.done(); + }); + + audit.define('valid constructor', (task, should) => { + let buffer; + + let options = {numberOfChannels: 3, length: 42, sampleRate: 54321}; + + let message = 'new AudioBuffer(' + JSON.stringify(options) + ')'; + should(() => { + buffer = new AudioBuffer(options); + }, message).notThrow(); + + should(buffer.numberOfChannels, 'buffer.numberOfChannels') + .beEqualTo(options.numberOfChannels); + + should(buffer.length, 'buffer.length').beEqualTo(options.length); + + should(buffer.sampleRate, 'buffer.sampleRate') + .beEqualTo(options.sampleRate); + + // Verify that we actually got the right number of channels + for (let k = 0; k < options.numberOfChannels; ++k) { + let data; + let message = 'buffer.getChannelData(' + k + ')'; + should(() => { + data = buffer.getChannelData(k); + }, message).notThrow(); + + should(data.length, message + ' length').beEqualTo(options.length); + } + + should( + () => { + buffer.getChannelData(options.numberOfChannels); + }, + 'buffer.getChannelData(' + options.numberOfChannels + ')') + .throw(DOMException, 'IndexSizeError'); + + task.done(); + }); + + audit.define('multiple contexts', (task, should) => { + // Test that an AudioBuffer can be used for different contexts. + let buffer = + new AudioBuffer({length: 128, sampleRate: context.sampleRate}); + + // Don't use getChannelData here because we want to be able to use + // |data| to compare the final results of playing out this buffer. (If + // we did, |data| gets detached when the sources play.) + let data = new Float32Array(buffer.length); + for (let k = 0; k < data.length; ++k) + data[k] = 1 + k; + buffer.copyToChannel(data, 0); + + let c1 = new OfflineAudioContext(1, 128, context.sampleRate); + let c2 = new OfflineAudioContext(1, 128, context.sampleRate); + + let s1 = new AudioBufferSourceNode(c1, {buffer: buffer}); + let s2 = new AudioBufferSourceNode(c2, {buffer: buffer}); + + s1.connect(c1.destination); + s2.connect(c2.destination); + + s1.start(); + s2.start(); + + Promise + .all([ + c1.startRendering().then(function(resultBuffer) { + return resultBuffer; + }), + c2.startRendering().then(function(resultBuffer) { + return resultBuffer; + }), + ]) + .then(resultBuffers => { + let c1ResultValue = should(resultBuffers[0].getChannelData(0), 'c1 result') + .beEqualToArray(data); + let c2ResultValue = should(resultBuffers[1].getChannelData(0), 'c2 result') + .beEqualToArray(data); + should( + c1ResultValue && c2ResultValue, + 'AudioBuffer shared between two different contexts') + .message('correctly', 'incorrectly'); + task.done(); + }); + }); + + audit.run(); + </script> + </body> +</html> |