summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/webaudio/the-audio-api/the-delaynode-interface
diff options
context:
space:
mode:
Diffstat (limited to 'testing/web-platform/tests/webaudio/the-audio-api/the-delaynode-interface')
-rw-r--r--testing/web-platform/tests/webaudio/the-audio-api/the-delaynode-interface/ctor-delay.html76
-rw-r--r--testing/web-platform/tests/webaudio/the-audio-api/the-delaynode-interface/delay-test.html61
-rw-r--r--testing/web-platform/tests/webaudio/the-audio-api/the-delaynode-interface/delaynode-channel-count-1.html104
-rw-r--r--testing/web-platform/tests/webaudio/the-audio-api/the-delaynode-interface/delaynode-max-default-delay.html49
-rw-r--r--testing/web-platform/tests/webaudio/the-audio-api/the-delaynode-interface/delaynode-max-nondefault-delay.html51
-rw-r--r--testing/web-platform/tests/webaudio/the-audio-api/the-delaynode-interface/delaynode-maxdelay.html54
-rw-r--r--testing/web-platform/tests/webaudio/the-audio-api/the-delaynode-interface/delaynode-maxdelaylimit.html68
-rw-r--r--testing/web-platform/tests/webaudio/the-audio-api/the-delaynode-interface/delaynode-scheduling.html51
-rw-r--r--testing/web-platform/tests/webaudio/the-audio-api/the-delaynode-interface/delaynode.html61
-rw-r--r--testing/web-platform/tests/webaudio/the-audio-api/the-delaynode-interface/maxdelay-rounding.html78
-rw-r--r--testing/web-platform/tests/webaudio/the-audio-api/the-delaynode-interface/no-dezippering.html184
11 files changed, 837 insertions, 0 deletions
diff --git a/testing/web-platform/tests/webaudio/the-audio-api/the-delaynode-interface/ctor-delay.html b/testing/web-platform/tests/webaudio/the-audio-api/the-delaynode-interface/ctor-delay.html
new file mode 100644
index 0000000000..e7ccefc655
--- /dev/null
+++ b/testing/web-platform/tests/webaudio/the-audio-api/the-delaynode-interface/ctor-delay.html
@@ -0,0 +1,76 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>
+ Test Constructor: Delay
+ </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) => {
+ testInvalidConstructor(should, 'DelayNode', context);
+ task.done();
+ });
+
+ audit.define('default constructor', (task, should) => {
+ let prefix = 'node0';
+ let node = testDefaultConstructor(should, 'DelayNode', context, {
+ prefix: prefix,
+ numberOfInputs: 1,
+ numberOfOutputs: 1,
+ channelCount: 2,
+ channelCountMode: 'max',
+ channelInterpretation: 'speakers'
+ });
+
+ testDefaultAttributes(
+ should, node, prefix, [{name: 'delayTime', value: 0}]);
+
+ task.done();
+ });
+
+ audit.define('test AudioNodeOptions', (task, should) => {
+ testAudioNodeOptions(should, context, 'DelayNode');
+ task.done();
+ });
+
+ audit.define('constructor options', (task, should) => {
+ let node;
+ let options = {
+ delayTime: 0.5,
+ maxDelayTime: 1.5,
+ };
+
+ should(
+ () => {
+ node = new DelayNode(context, options);
+ },
+ 'node1 = new DelayNode(c, ' + JSON.stringify(options) + ')')
+ .notThrow();
+
+ should(node.delayTime.value, 'node1.delayTime.value')
+ .beEqualTo(options.delayTime);
+ should(node.delayTime.maxValue, 'node1.delayTime.maxValue')
+ .beEqualTo(options.maxDelayTime);
+
+ task.done();
+ });
+
+ audit.run();
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/webaudio/the-audio-api/the-delaynode-interface/delay-test.html b/testing/web-platform/tests/webaudio/the-audio-api/the-delaynode-interface/delay-test.html
new file mode 100644
index 0000000000..6277c253ec
--- /dev/null
+++ b/testing/web-platform/tests/webaudio/the-audio-api/the-delaynode-interface/delay-test.html
@@ -0,0 +1,61 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Test DelayNode Delay</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: 'test0', description: 'Test delay of 3 frames'},
+ async (task, should) => {
+ // Only need a few outputs samples. The sample rate is arbitrary.
+ const context =
+ new OfflineAudioContext(1, RENDER_QUANTUM_FRAMES, 8192);
+ let src;
+ let delay;
+
+ should(
+ () => {
+ src = new ConstantSourceNode(context);
+ delay = new DelayNode(context);
+ },
+ 'Creating ConstantSourceNode(context) and DelayNode(context)')
+ .notThrow();
+
+ // The number of frames to delay for the DelayNode. Should be a
+ // whole number, but is otherwise arbitrary.
+ const delayFrames = 3;
+
+ should(() => {
+ delay.delayTime.value = delayFrames / context.sampleRate;
+ }, `Setting delayTime to ${delayFrames} frames`).notThrow();
+
+ src.connect(delay).connect(context.destination);
+
+ src.start();
+
+ let buffer = await context.startRendering();
+ let output = buffer.getChannelData(0);
+
+ // Verify output was delayed the correct number of frames.
+ should(output.slice(0, delayFrames), `output[0:${delayFrames - 1}]`)
+ .beConstantValueOf(0);
+ should(
+ output.slice(delayFrames),
+ `output[${delayFrames}:${output.length - 1}]`)
+ .beConstantValueOf(1);
+
+ task.done();
+ });
+
+ audit.run();
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/webaudio/the-audio-api/the-delaynode-interface/delaynode-channel-count-1.html b/testing/web-platform/tests/webaudio/the-audio-api/the-delaynode-interface/delaynode-channel-count-1.html
new file mode 100644
index 0000000000..dd964ef9e3
--- /dev/null
+++ b/testing/web-platform/tests/webaudio/the-audio-api/the-delaynode-interface/delaynode-channel-count-1.html
@@ -0,0 +1,104 @@
+<!DOCTYPE html>
+<title>Test that DelayNode output channelCount matches that of the delayed input</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+// See https://github.com/WebAudio/web-audio-api/issues/25
+
+// sampleRate is a power of two so that delay times are exact in base-2
+// floating point arithmetic.
+const SAMPLE_RATE = 32768;
+// Arbitrary delay time in frames (but this is assumed a multiple of block
+// size below):
+const DELAY_FRAMES = 3 * 128;
+// Implementations may apply interpolation to input samples, which can spread
+// the effect of input with larger channel counts over neighbouring blocks.
+// This test ignores enough neighbouring blocks to ignore the effects of
+// filter radius of up to this number of frames:
+const INTERPOLATION_GRACE = 128;
+// Number of frames of DelayNode output that are known to be stereo:
+const STEREO_FRAMES = 128;
+// The delay will be increased at this frame to switch DelayNode output back
+// to mono.
+const MONO_OUTPUT_START_FRAME =
+ DELAY_FRAMES + INTERPOLATION_GRACE + STEREO_FRAMES;
+// Number of frames of output that are known to be mono after the known stereo
+// and interpolation grace.
+const MONO_FRAMES = 128;
+// Total length allows for interpolation after effects of stereo input are
+// finished and one block to test return to mono output:
+const TOTAL_LENGTH =
+ MONO_OUTPUT_START_FRAME + INTERPOLATION_GRACE + MONO_FRAMES;
+// maxDelayTime, is a multiple of block size, because the Gecko implementation
+// once had a bug with delayTime = maxDelayTime in this situation:
+const MAX_DELAY_FRAMES = TOTAL_LENGTH + INTERPOLATION_GRACE;
+
+promise_test(() => {
+ let context = new OfflineAudioContext({numberOfChannels: 1,
+ length: TOTAL_LENGTH,
+ sampleRate: SAMPLE_RATE});
+
+ // Only channel 1 of the splitter is connected to the destination.
+ let splitter = new ChannelSplitterNode(context, {numberOfOutputs: 2});
+ splitter.connect(context.destination, 1);
+
+ // A gain node has channelCountMode "max" and channelInterpretation
+ // "speakers", and so will up-mix a mono input when there is stereo input.
+ let gain = new GainNode(context);
+ gain.connect(splitter);
+
+ // The delay node initially outputs a single channel of silence, when it
+ // does not have enough signal in its history to output what it has
+ // previously received. After the delay period, it will then output the
+ // stereo signal it received.
+ let delay =
+ new DelayNode(context,
+ {maxDelayTime: MAX_DELAY_FRAMES / context.sampleRate,
+ delayTime: DELAY_FRAMES / context.sampleRate});
+ // Schedule an increase in the delay to return to mono silent output from
+ // the unfilled portion of the DelayNode's buffer.
+ delay.delayTime.setValueAtTime(MAX_DELAY_FRAMES / context.sampleRate,
+ MONO_OUTPUT_START_FRAME / context.sampleRate);
+ delay.connect(gain);
+
+ let stereoMerger = new ChannelMergerNode(context, {numberOfInputs: 2});
+ stereoMerger.connect(delay);
+
+ let leftOffset = 0.125;
+ let rightOffset = 0.5;
+ let leftSource = new ConstantSourceNode(context, {offset: leftOffset});
+ let rightSource = new ConstantSourceNode(context, {offset: rightOffset});
+ leftSource.start();
+ rightSource.start();
+ leftSource.connect(stereoMerger, 0, 0);
+ rightSource.connect(stereoMerger, 0, 1);
+ // Connect a mono source directly to the gain, so that even stereo silence
+ // will be detected in channel 1 of the gain output because it will cause
+ // the mono source to be up-mixed.
+ let monoOffset = 0.25
+ let monoSource = new ConstantSourceNode(context, {offset: monoOffset});
+ monoSource.start();
+ monoSource.connect(gain);
+
+ return context.startRendering().
+ then((buffer) => {
+ let output = buffer.getChannelData(0);
+
+ function assert_samples_equal(startIndex, length, expected, description)
+ {
+ for (let i = startIndex; i < startIndex + length; ++i) {
+ assert_equals(output[i], expected, description + ` at ${i}`);
+ }
+ }
+
+ assert_samples_equal(0, DELAY_FRAMES - INTERPOLATION_GRACE,
+ 0, "Initial mono");
+ assert_samples_equal(DELAY_FRAMES + INTERPOLATION_GRACE, STEREO_FRAMES,
+ monoOffset + rightOffset, "Stereo");
+ assert_samples_equal(MONO_OUTPUT_START_FRAME + INTERPOLATION_GRACE,
+ MONO_FRAMES,
+ 0, "Final mono");
+ });
+});
+
+</script>
diff --git a/testing/web-platform/tests/webaudio/the-audio-api/the-delaynode-interface/delaynode-max-default-delay.html b/testing/web-platform/tests/webaudio/the-audio-api/the-delaynode-interface/delaynode-max-default-delay.html
new file mode 100644
index 0000000000..ef526c96ff
--- /dev/null
+++ b/testing/web-platform/tests/webaudio/the-audio-api/the-delaynode-interface/delaynode-max-default-delay.html
@@ -0,0 +1,49 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>
+ delaynode-max-default-delay.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/delay-testing.js"></script>
+ </head>
+ <body>
+ <script id="layout-test-code">
+ let audit = Audit.createTaskRunner();
+
+ audit.define(
+ {
+ label: 'test',
+ description: 'DelayNode with delay set to default maximum delay'
+ },
+ function(task, should) {
+
+ // Create offline audio context.
+ let context = new OfflineAudioContext(
+ 1, sampleRate * renderLengthSeconds, sampleRate);
+ let toneBuffer = createToneBuffer(
+ context, 20, 20 * toneLengthSeconds, sampleRate); // 20Hz tone
+
+ let bufferSource = context.createBufferSource();
+ bufferSource.buffer = toneBuffer;
+
+ let delay = context.createDelay();
+ delayTimeSeconds = 1;
+ delay.delayTime.value = delayTimeSeconds;
+
+ bufferSource.connect(delay);
+ delay.connect(context.destination);
+ bufferSource.start(0);
+
+ context.startRendering()
+ .then(buffer => checkDelayedResult(buffer, toneBuffer, should))
+ .then(() => task.done());
+ });
+
+ audit.run();
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/webaudio/the-audio-api/the-delaynode-interface/delaynode-max-nondefault-delay.html b/testing/web-platform/tests/webaudio/the-audio-api/the-delaynode-interface/delaynode-max-nondefault-delay.html
new file mode 100644
index 0000000000..3be07255e1
--- /dev/null
+++ b/testing/web-platform/tests/webaudio/the-audio-api/the-delaynode-interface/delaynode-max-nondefault-delay.html
@@ -0,0 +1,51 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>
+ delaynode-max-nondefault-delay.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/delay-testing.js"></script>
+ </head>
+ <body>
+ <script id="layout-test-code">
+ let audit = Audit.createTaskRunner();
+
+ audit.define(
+ {
+ label: 'test',
+ description: 'DelayNode with delay set to non-default maximum delay'
+ },
+ function(task, should) {
+
+ // Create offline audio context.
+ let context = new OfflineAudioContext(
+ 1, sampleRate * renderLengthSeconds, sampleRate);
+ let toneBuffer = createToneBuffer(
+ context, 20, 20 * toneLengthSeconds, sampleRate); // 20Hz tone
+
+ let bufferSource = context.createBufferSource();
+ bufferSource.buffer = toneBuffer;
+
+ let maxDelay = 1.5;
+ let delay = context.createDelay(maxDelay);
+ delayTimeSeconds = maxDelay;
+ delay.delayTime.value = delayTimeSeconds;
+
+ bufferSource.connect(delay);
+ delay.connect(context.destination);
+ bufferSource.start(0);
+
+ context.startRendering()
+ .then(buffer => checkDelayedResult(buffer, toneBuffer, should))
+ .then(() => task.done());
+ ;
+ });
+
+ audit.run();
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/webaudio/the-audio-api/the-delaynode-interface/delaynode-maxdelay.html b/testing/web-platform/tests/webaudio/the-audio-api/the-delaynode-interface/delaynode-maxdelay.html
new file mode 100644
index 0000000000..a43ceeb7be
--- /dev/null
+++ b/testing/web-platform/tests/webaudio/the-audio-api/the-delaynode-interface/delaynode-maxdelay.html
@@ -0,0 +1,54 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>
+ delaynode-maxdelay.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/delay-testing.js"></script>
+ </head>
+ <body>
+ <script id="layout-test-code">
+ let audit = Audit.createTaskRunner();
+
+ audit.define(
+ {
+ label: 'test',
+ description:
+ 'Basic functionality of DelayNode with a non-default max delay time'
+ },
+ function(task, should) {
+
+ // Create offline audio context.
+ let context = new OfflineAudioContext(
+ 1, sampleRate * renderLengthSeconds, sampleRate);
+ let toneBuffer = createToneBuffer(
+ context, 20, 20 * toneLengthSeconds, sampleRate); // 20Hz tone
+
+ let bufferSource = context.createBufferSource();
+ bufferSource.buffer = toneBuffer;
+
+ // Create a delay node with an explicit max delay time (greater than
+ // the default of 1 second).
+ let delay = context.createDelay(2);
+ // Set the delay time to a value greater than the default max delay
+ // so we can verify the delay is working for this case.
+ delayTimeSeconds = 1.5;
+ delay.delayTime.value = delayTimeSeconds;
+
+ bufferSource.connect(delay);
+ delay.connect(context.destination);
+ bufferSource.start(0);
+
+ context.startRendering()
+ .then(buffer => checkDelayedResult(buffer, toneBuffer, should))
+ .then(() => task.done());
+ });
+
+ audit.run();
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/webaudio/the-audio-api/the-delaynode-interface/delaynode-maxdelaylimit.html b/testing/web-platform/tests/webaudio/the-audio-api/the-delaynode-interface/delaynode-maxdelaylimit.html
new file mode 100644
index 0000000000..caf2f85dfd
--- /dev/null
+++ b/testing/web-platform/tests/webaudio/the-audio-api/the-delaynode-interface/delaynode-maxdelaylimit.html
@@ -0,0 +1,68 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>
+ delaynode-maxdelaylimit.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/delay-testing.js"></script>
+ </head>
+ <body>
+ <script id="layout-test-code">
+ let audit = Audit.createTaskRunner();
+
+ audit.define(
+ {
+ label: 'test',
+ description:
+ 'Tests attribute and maximum allowed delay of DelayNode'
+ },
+ function(task, should) {
+
+ // Create offline audio context.
+ let context = new OfflineAudioContext(
+ 1, sampleRate * renderLengthSeconds, sampleRate);
+ let toneBuffer = createToneBuffer(
+ context, 20, 20 * toneLengthSeconds, sampleRate); // 20Hz tone
+
+ let bufferSource = context.createBufferSource();
+ bufferSource.buffer = toneBuffer;
+
+ window.context = context;
+ should(() => context.createDelay(180),
+ 'Setting Delay length to 180 seconds or more')
+ .throw(DOMException, 'NotSupportedError');
+ should(() => context.createDelay(0),
+ 'Setting Delay length to 0 seconds')
+ .throw(DOMException, 'NotSupportedError');
+ should(() => context.createDelay(-1),
+ 'Setting Delay length to negative')
+ .throw(DOMException, 'NotSupportedError');
+ should(() => context.createDelay(NaN),
+ 'Setting Delay length to NaN')
+ .throw(TypeError);
+
+ let delay = context.createDelay(179);
+ delay.delayTime.value = delayTimeSeconds;
+ window.delay = delay;
+ should(
+ delay.delayTime.value,
+ 'delay.delayTime.value = ' + delayTimeSeconds)
+ .beEqualTo(delayTimeSeconds);
+
+ bufferSource.connect(delay);
+ delay.connect(context.destination);
+ bufferSource.start(0);
+
+ context.startRendering()
+ .then(buffer => checkDelayedResult(buffer, toneBuffer, should))
+ .then(() => task.done());
+ });
+
+ audit.run();
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/webaudio/the-audio-api/the-delaynode-interface/delaynode-scheduling.html b/testing/web-platform/tests/webaudio/the-audio-api/the-delaynode-interface/delaynode-scheduling.html
new file mode 100644
index 0000000000..af6c54950a
--- /dev/null
+++ b/testing/web-platform/tests/webaudio/the-audio-api/the-delaynode-interface/delaynode-scheduling.html
@@ -0,0 +1,51 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>
+ delaynode-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/delay-testing.js"></script>
+ </head>
+ <body>
+ <script id="layout-test-code">
+ let audit = Audit.createTaskRunner();
+
+ audit.define(
+ {
+ label: 'test',
+ description:
+ 'DelayNode delayTime parameter can be scheduled at a given time'
+ },
+ function(task, should) {
+
+ // Create offline audio context.
+ let context = new OfflineAudioContext(
+ 1, sampleRate * renderLengthSeconds, sampleRate);
+ let toneBuffer = createToneBuffer(
+ context, 20, 20 * toneLengthSeconds, sampleRate); // 20Hz tone
+
+ let bufferSource = context.createBufferSource();
+ bufferSource.buffer = toneBuffer;
+
+ let delay = context.createDelay();
+
+ // Schedule delay time at time zero.
+ delay.delayTime.setValueAtTime(delayTimeSeconds, 0);
+
+ bufferSource.connect(delay);
+ delay.connect(context.destination);
+ bufferSource.start(0);
+
+ context.startRendering()
+ .then(buffer => checkDelayedResult(buffer, toneBuffer, should))
+ .then(() => task.done());
+ });
+
+ audit.run();
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/webaudio/the-audio-api/the-delaynode-interface/delaynode.html b/testing/web-platform/tests/webaudio/the-audio-api/the-delaynode-interface/delaynode.html
new file mode 100644
index 0000000000..da508e439f
--- /dev/null
+++ b/testing/web-platform/tests/webaudio/the-audio-api/the-delaynode-interface/delaynode.html
@@ -0,0 +1,61 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>
+ delaynode.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/delay-testing.js"></script>
+ </head>
+ <body>
+ <script id="layout-test-code">
+ let audit = Audit.createTaskRunner();
+
+ audit.define(
+ {
+ label: 'test',
+ description: 'Tests attribute and basic functionality of DelayNode'
+ },
+ function(task, should) {
+
+ // Create offline audio context.
+ let context = new OfflineAudioContext(
+ 1, sampleRate * renderLengthSeconds, sampleRate);
+ let toneBuffer = createToneBuffer(
+ context, 20, 20 * toneLengthSeconds, sampleRate); // 20Hz tone
+
+ let bufferSource = context.createBufferSource();
+ bufferSource.buffer = toneBuffer;
+
+ let delay = context.createDelay();
+
+ window.delay = delay;
+ should(delay.numberOfInputs, 'delay.numberOfInputs').beEqualTo(1);
+ should(delay.numberOfOutputs, 'delay.numberOfOutputs').beEqualTo(1);
+ should(delay.delayTime.defaultValue, 'delay.delayTime.defaultValue')
+ .beEqualTo(0.0);
+ should(delay.delayTime.value, 'delay.delayTime.value')
+ .beEqualTo(0.0);
+
+ delay.delayTime.value = delayTimeSeconds;
+ should(
+ delay.delayTime.value,
+ 'delay.delayTime.value = ' + delayTimeSeconds)
+ .beEqualTo(delayTimeSeconds);
+
+ bufferSource.connect(delay);
+ delay.connect(context.destination);
+ bufferSource.start(0);
+
+ context.startRendering()
+ .then(buffer => checkDelayedResult(buffer, toneBuffer, should))
+ .then(task.done.bind(task));
+ });
+
+ audit.run();
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/webaudio/the-audio-api/the-delaynode-interface/maxdelay-rounding.html b/testing/web-platform/tests/webaudio/the-audio-api/the-delaynode-interface/maxdelay-rounding.html
new file mode 100644
index 0000000000..84d9f18138
--- /dev/null
+++ b/testing/web-platform/tests/webaudio/the-audio-api/the-delaynode-interface/maxdelay-rounding.html
@@ -0,0 +1,78 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>
+ Test DelayNode when maxDelayTime requires rounding
+ </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;
+ let inputLengthSeconds = 1;
+ let renderLengthSeconds = 2;
+
+ // Delay for one second plus 0.4 of a sample frame, to test that
+ // DelayNode is properly rounding up when calculating its buffer
+ // size (crbug.com/1065110).
+ let delayTimeSeconds = 1 + 0.4 / sampleRate;
+
+ let audit = Audit.createTaskRunner();
+
+ audit.define(
+ {
+ label: 'maxdelay-rounding',
+ description: 'Test DelayNode when maxDelayTime requires rounding',
+ },
+ (task, should) => {
+ let context = new OfflineAudioContext({
+ numberOfChannels: 1,
+ length: sampleRate * renderLengthSeconds,
+ sampleRate: sampleRate,
+ });
+
+ // Create a constant source to use as input.
+ let src = new ConstantSourceNode(context);
+
+ // Create a DelayNode to delay for delayTimeSeconds.
+ let delay = new DelayNode(context, {
+ maxDelayTime: delayTimeSeconds,
+ delayTime: delayTimeSeconds,
+ });
+
+ src.connect(delay).connect(context.destination);
+
+ src.start();
+ context.startRendering()
+ .then(renderedBuffer => {
+ let renderedData = renderedBuffer.getChannelData(0);
+
+ // The first delayTimeSeconds of output should be silent.
+ let expectedSilentFrames = Math.floor(
+ delayTimeSeconds * sampleRate);
+
+ should(
+ renderedData.slice(0, expectedSilentFrames),
+ `output[0:${expectedSilentFrames - 1}]`)
+ .beConstantValueOf(0);
+
+ // The rest should be non-silent: that is, there should
+ // be at least one non-zero sample. (Any reasonable
+ // interpolation algorithm will make all these samples
+ // non-zero, but I don't think that's guaranteed by the
+ // spec, so we use a conservative test for now.)
+ should(
+ renderedData.slice(expectedSilentFrames),
+ `output[${expectedSilentFrames}:]`)
+ .notBeConstantValueOf(0);
+ })
+ .then(() => task.done());
+ });
+
+ audit.run();
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/webaudio/the-audio-api/the-delaynode-interface/no-dezippering.html b/testing/web-platform/tests/webaudio/the-audio-api/the-delaynode-interface/no-dezippering.html
new file mode 100644
index 0000000000..ccca103a3b
--- /dev/null
+++ b/testing/web-platform/tests/webaudio/the-audio-api/the-delaynode-interface/no-dezippering.html
@@ -0,0 +1,184 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>
+ Test DelayNode Has No Dezippering
+ </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">
+ // The sample rate must be a power of two to avoid any round-off errors in
+ // computing when to suspend a context on a rendering quantum boundary.
+ // Otherwise this is pretty arbitrary.
+ let sampleRate = 16384;
+
+ let audit = Audit.createTaskRunner();
+
+ audit.define(
+ {label: 'test0', description: 'Test DelayNode has no dezippering'},
+ (task, should) => {
+ let context = new OfflineAudioContext(1, sampleRate, sampleRate);
+
+ // Simple integer ramp for testing delay node
+ let buffer = new AudioBuffer(
+ {length: context.length, sampleRate: context.sampleRate});
+ let rampData = buffer.getChannelData(0);
+ for (let k = 0; k < rampData.length; ++k) {
+ rampData[k] = k + 1;
+ }
+
+ // |delay0Frame| is the initial delay in frames. |delay1Frame| is
+ // the new delay in frames. These must be integers.
+ let delay0Frame = 64;
+ let delay1Frame = 16;
+
+ let src = new AudioBufferSourceNode(context, {buffer: buffer});
+ let delay = new DelayNode(
+ context, {delayTime: delay0Frame / context.sampleRate});
+
+ src.connect(delay).connect(context.destination);
+
+ // After a render quantum, change the delay to |delay1Frame|.
+ context.suspend(RENDER_QUANTUM_FRAMES / context.sampleRate)
+ .then(() => {
+ delay.delayTime.value = delay1Frame / context.sampleRate;
+ })
+ .then(() => context.resume());
+
+ src.start();
+ context.startRendering()
+ .then(renderedBuffer => {
+ let renderedData = renderedBuffer.getChannelData(0);
+
+ // The first |delay0Frame| frames should be zero.
+ should(
+ renderedData.slice(0, delay0Frame),
+ 'output[0:' + (delay0Frame - 1) + ']')
+ .beConstantValueOf(0);
+
+ // Now we have the ramp should show up from the delay.
+ let ramp0 =
+ new Float32Array(RENDER_QUANTUM_FRAMES - delay0Frame);
+ for (let k = 0; k < ramp0.length; ++k) {
+ ramp0[k] = rampData[k];
+ }
+
+ should(
+ renderedData.slice(delay0Frame, RENDER_QUANTUM_FRAMES),
+ 'output[' + delay0Frame + ':' +
+ (RENDER_QUANTUM_FRAMES - 1) + ']')
+ .beEqualToArray(ramp0);
+
+ // After one rendering quantum, the delay is changed to
+ // |delay1Frame|.
+ let ramp1 =
+ new Float32Array(context.length - RENDER_QUANTUM_FRAMES);
+ for (let k = 0; k < ramp1.length; ++k) {
+ // ramp1[k] = 1 + k + RENDER_QUANTUM_FRAMES - delay1Frame;
+ ramp1[k] =
+ rampData[k + RENDER_QUANTUM_FRAMES - delay1Frame];
+ }
+ should(
+ renderedData.slice(RENDER_QUANTUM_FRAMES),
+ 'output[' + RENDER_QUANTUM_FRAMES + ':]')
+ .beEqualToArray(ramp1);
+ })
+ .then(() => task.done());
+ });
+
+ audit.define(
+ {label: 'test1', description: 'Test value setter and setValueAtTime'},
+ (task, should) => {
+ testWithAutomation(should, {prefix: '', threshold: 6.5819e-5})
+ .then(() => task.done());
+ });
+
+ audit.define(
+ {label: 'test2', description: 'Test value setter and modulation'},
+ (task, should) => {
+ testWithAutomation(should, {
+ prefix: 'With modulation: ',
+ modulator: true
+ }).then(() => task.done());
+ });
+
+ // Compare .value setter with setValueAtTime, Optionally allow modulation
+ // of |delayTime|.
+ function testWithAutomation(should, options) {
+ let prefix = options.prefix;
+ // Channel 0 is the output of delay node using the setter and channel 1
+ // is the output using setValueAtTime.
+ let context = new OfflineAudioContext(2, sampleRate, sampleRate);
+
+ let merger = new ChannelMergerNode(
+ context, {numberOfInputs: context.destination.channelCount});
+ merger.connect(context.destination);
+
+ let src = new OscillatorNode(context);
+
+ // |delay0Frame| is the initial delay value in frames. |delay1Frame| is
+ // the new delay in frames. The values here are constrained only by the
+ // constraints for a DelayNode. These are pretty arbitrary except we
+ // wanted them to be fractional so as not be on a frame boundary to
+ // test interpolation compared with |setValueAtTime()|..
+ let delay0Frame = 3.1;
+ let delay1Frame = 47.2;
+
+ let delayTest = new DelayNode(
+ context, {delayTime: delay0Frame / context.sampleRate});
+ let delayRef = new DelayNode(
+ context, {delayTime: delay0Frame / context.sampleRate});
+
+ src.connect(delayTest).connect(merger, 0, 0);
+ src.connect(delayRef).connect(merger, 0, 1);
+
+ if (options.modulator) {
+ // Fairly arbitrary modulation of the delay time, with a peak
+ // variation of 10 ms.
+ let mod = new OscillatorNode(context, {frequency: 1000});
+ let modGain = new GainNode(context, {gain: .01});
+ mod.connect(modGain);
+ modGain.connect(delayTest.delayTime);
+ modGain.connect(delayRef.delayTime);
+ mod.start();
+ }
+
+ // The time at which the delay time of |delayTest| node will be
+ // changed. This MUST be on a render quantum boundary, but is
+ // otherwise arbitrary.
+ let changeTime = 3 * RENDER_QUANTUM_FRAMES / context.sampleRate;
+
+ // Schedule the delay change on |delayRef| and also apply the value
+ // setter for |delayTest| at |changeTime|.
+ delayRef.delayTime.setValueAtTime(
+ delay1Frame / context.sampleRate, changeTime);
+ context.suspend(changeTime)
+ .then(() => {
+ delayTest.delayTime.value = delay1Frame / context.sampleRate;
+ })
+ .then(() => context.resume());
+
+ src.start();
+
+ return context.startRendering().then(renderedBuffer => {
+ let actual = renderedBuffer.getChannelData(0);
+ let expected = renderedBuffer.getChannelData(1);
+
+ let match = should(actual, prefix + '.value setter output')
+ .beCloseToArray(
+ expected, {absoluteThreshold: options.threshold});
+ should(
+ match,
+ prefix + '.value setter output matches setValueAtTime output')
+ .beTrue();
+ });
+ }
+
+ audit.run();
+ </script>
+ </body>
+</html>