summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/webaudio/the-audio-api/the-audiobuffer-interface
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:22:09 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:22:09 +0000
commit43a97878ce14b72f0981164f87f2e35e14151312 (patch)
tree620249daf56c0258faa40cbdcf9cfba06de2a846 /testing/web-platform/tests/webaudio/the-audio-api/the-audiobuffer-interface
parentInitial commit. (diff)
downloadfirefox-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')
-rw-r--r--testing/web-platform/tests/webaudio/the-audio-api/the-audiobuffer-interface/acquire-the-content.html85
-rw-r--r--testing/web-platform/tests/webaudio/the-audio-api/the-audiobuffer-interface/audiobuffer-copy-channel.html330
-rw-r--r--testing/web-platform/tests/webaudio/the-audio-api/the-audiobuffer-interface/audiobuffer-getChannelData.html66
-rw-r--r--testing/web-platform/tests/webaudio/the-audio-api/the-audiobuffer-interface/audiobuffer-reuse.html36
-rw-r--r--testing/web-platform/tests/webaudio/the-audio-api/the-audiobuffer-interface/audiobuffer.html71
-rw-r--r--testing/web-platform/tests/webaudio/the-audio-api/the-audiobuffer-interface/crashtests/copyFromChannel-bufferOffset-1.html11
-rw-r--r--testing/web-platform/tests/webaudio/the-audio-api/the-audiobuffer-interface/crashtests/copyToChannel-bufferOffset-1.html10
-rw-r--r--testing/web-platform/tests/webaudio/the-audio-api/the-audiobuffer-interface/ctor-audiobuffer.html236
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>