summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/webaudio/the-audio-api/the-gainnode-interface
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 17:32:43 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 17:32:43 +0000
commit6bf0a5cb5034a7e684dcc3500e841785237ce2dd (patch)
treea68f146d7fa01f0134297619fbe7e33db084e0aa /testing/web-platform/tests/webaudio/the-audio-api/the-gainnode-interface
parentInitial commit. (diff)
downloadthunderbird-upstream.tar.xz
thunderbird-upstream.zip
Adding upstream version 1:115.7.0.upstream/1%115.7.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'testing/web-platform/tests/webaudio/the-audio-api/the-gainnode-interface')
-rw-r--r--testing/web-platform/tests/webaudio/the-audio-api/the-gainnode-interface/ctor-gain.html79
-rw-r--r--testing/web-platform/tests/webaudio/the-audio-api/the-gainnode-interface/gain-basic.html37
-rw-r--r--testing/web-platform/tests/webaudio/the-audio-api/the-gainnode-interface/gain.html162
-rw-r--r--testing/web-platform/tests/webaudio/the-audio-api/the-gainnode-interface/no-dezippering.html121
4 files changed, 399 insertions, 0 deletions
diff --git a/testing/web-platform/tests/webaudio/the-audio-api/the-gainnode-interface/ctor-gain.html b/testing/web-platform/tests/webaudio/the-audio-api/the-gainnode-interface/ctor-gain.html
new file mode 100644
index 0000000000..dec273e969
--- /dev/null
+++ b/testing/web-platform/tests/webaudio/the-audio-api/the-gainnode-interface/ctor-gain.html
@@ -0,0 +1,79 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>
+ Test Constructor: Gain
+ </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, 'GainNode', context);
+ task.done();
+ });
+
+ audit.define('default constructor', (task, should) => {
+ let prefix = 'node0';
+ let node = testDefaultConstructor(should, 'GainNode', context, {
+ prefix: prefix,
+ numberOfInputs: 1,
+ numberOfOutputs: 1,
+ channelCount: 2,
+ channelCountMode: 'max',
+ channelInterpretation: 'speakers'
+ });
+
+ testDefaultAttributes(should, node, prefix, [{name: 'gain', value: 1}]);
+
+ task.done();
+ });
+
+ audit.define('test AudioNodeOptions', (task, should) => {
+ testAudioNodeOptions(should, context, 'GainNode');
+ task.done();
+ });
+
+ audit.define('constructor with options', (task, should) => {
+ let node;
+ let options = {
+ gain: -2,
+ };
+
+ should(
+ () => {
+ node = new GainNode(context, options);
+ },
+ 'node1 = new GainNode(c, ' + JSON.stringify(options) + ')')
+ .notThrow();
+ should(node instanceof GainNode, 'node1 instanceof GainNode')
+ .beEqualTo(true);
+
+ should(node.gain.value, 'node1.gain.value').beEqualTo(options.gain);
+
+ should(node.channelCount, 'node1.channelCount').beEqualTo(2);
+ should(node.channelCountMode, 'node1.channelCountMode')
+ .beEqualTo('max');
+ should(node.channelInterpretation, 'node1.channelInterpretation')
+ .beEqualTo('speakers');
+
+ task.done();
+ });
+
+ audit.run();
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/webaudio/the-audio-api/the-gainnode-interface/gain-basic.html b/testing/web-platform/tests/webaudio/the-audio-api/the-gainnode-interface/gain-basic.html
new file mode 100644
index 0000000000..de2ba11a7f
--- /dev/null
+++ b/testing/web-platform/tests/webaudio/the-audio-api/the-gainnode-interface/gain-basic.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<!--
+Verifies GainNode attributes and their type.
+-->
+<html>
+ <head>
+ <title>
+ gain-basic.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 audit = Audit.createTaskRunner();
+
+ audit.define('test', function(task, should) {
+ // Create audio context.
+ let context = new AudioContext();
+
+ // Create gain node.
+ let gainNode = context.createGain();
+
+ should(
+ gainNode.gain instanceof AudioParam,
+ 'gainNode.gain instanceof AudioParam')
+ .beTrue();
+
+ task.done();
+ });
+
+ audit.run();
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/webaudio/the-audio-api/the-gainnode-interface/gain.html b/testing/web-platform/tests/webaudio/the-audio-api/the-gainnode-interface/gain.html
new file mode 100644
index 0000000000..c41f4c9080
--- /dev/null
+++ b/testing/web-platform/tests/webaudio/the-audio-api/the-gainnode-interface/gain.html
@@ -0,0 +1,162 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>
+ Basic GainNode Functionality
+ </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">
+ // Tests that GainNode is properly scaling the gain. We'll render 11
+ // notes, starting at a gain of 1.0, decreasing in gain by 0.1. The 11th
+ // note will be of gain 0.0, so it should be silent (at the end in the
+ // rendered output).
+
+ let audit = Audit.createTaskRunner();
+
+ // Use a power of two to eliminate any round-off when converting frame to
+ // time.
+ let sampleRate = 32768;
+ // Make sure the buffer duration and spacing are all exact frame lengths
+ // so that the note spacing is also on frame boundaries to eliminate
+ // sub-sample accurate start of a ABSN.
+ let bufferDurationSeconds = Math.floor(0.125 * sampleRate) / sampleRate;
+ let numberOfNotes = 11;
+ // Leave about 20ms of silence, being sure this is an exact frame
+ // duration.
+ let noteSilence = Math.floor(0.020 * sampleRate) / sampleRate;
+ let noteSpacing = bufferDurationSeconds + noteSilence;
+
+ let lengthInSeconds = numberOfNotes * noteSpacing;
+
+ let context = 0;
+ let sinWaveBuffer = 0;
+
+ // Create a stereo AudioBuffer of duration |lengthInSeconds| consisting of
+ // a pure sine wave with the given |frequency|. Both channels contain the
+ // same data.
+ function createSinWaveBuffer(lengthInSeconds, frequency) {
+ let audioBuffer =
+ context.createBuffer(2, lengthInSeconds * sampleRate, sampleRate);
+
+ let n = audioBuffer.length;
+ let channelL = audioBuffer.getChannelData(0);
+ let channelR = audioBuffer.getChannelData(1);
+
+ for (let i = 0; i < n; ++i) {
+ channelL[i] = Math.sin(frequency * 2.0 * Math.PI * i / sampleRate);
+ channelR[i] = channelL[i];
+ }
+
+ return audioBuffer;
+ }
+
+ function playNote(time, gain, merger) {
+ let source = context.createBufferSource();
+ source.buffer = sinWaveBuffer;
+
+ let gainNode = context.createGain();
+ gainNode.gain.value = gain;
+
+ let sourceSplitter = context.createChannelSplitter(2);
+ let gainSplitter = context.createChannelSplitter(2);
+
+ // Split the stereo channels from the source output and the gain output
+ // and merge them into the desired channels of the merger.
+ source.connect(gainNode).connect(gainSplitter);
+ source.connect(sourceSplitter);
+
+ gainSplitter.connect(merger, 0, 0);
+ gainSplitter.connect(merger, 1, 1);
+ sourceSplitter.connect(merger, 0, 2);
+ sourceSplitter.connect(merger, 1, 3);
+
+ source.start(time);
+ }
+
+ audit.define(
+ {label: 'create context', description: 'Create context for test'},
+ function(task, should) {
+ // Create offline audio context.
+ context = new OfflineAudioContext(
+ 4, sampleRate * lengthInSeconds, sampleRate);
+ task.done();
+ });
+
+ audit.define(
+ {label: 'test', description: 'GainNode functionality'},
+ function(task, should) {
+ let merger = new ChannelMergerNode(
+ context, {numberOfInputs: context.destination.channelCount});
+ merger.connect(context.destination);
+
+ // Create a buffer for a short "note".
+ sinWaveBuffer = createSinWaveBuffer(bufferDurationSeconds, 880.0);
+
+ let startTimes = [];
+ let gainValues = [];
+
+ // Render 11 notes, starting at a gain of 1.0, decreasing in gain by
+ // 0.1. The last note will be of gain 0.0, so shouldn't be
+ // perceptible in the rendered output.
+ for (let i = 0; i < numberOfNotes; ++i) {
+ let time = i * noteSpacing;
+ let gain = 1.0 - i / (numberOfNotes - 1);
+ startTimes.push(time);
+ gainValues.push(gain);
+ playNote(time, gain, merger);
+ }
+
+ context.startRendering()
+ .then(buffer => {
+ let actual0 = buffer.getChannelData(0);
+ let actual1 = buffer.getChannelData(1);
+ let reference0 = buffer.getChannelData(2);
+ let reference1 = buffer.getChannelData(3);
+
+ // It's ok to a frame too long since the sine pulses are
+ // followed by silence.
+ let bufferDurationFrames =
+ Math.ceil(bufferDurationSeconds * context.sampleRate);
+
+ // Apply the gains to the reference signal.
+ for (let k = 0; k < startTimes.length; ++k) {
+ // It's ok to be a frame early because the sine pulses are
+ // preceded by silence.
+ let startFrame =
+ Math.floor(startTimes[k] * context.sampleRate);
+ let gain = gainValues[k];
+ for (let n = 0; n < bufferDurationFrames; ++n) {
+ reference0[startFrame + n] *= gain;
+ reference1[startFrame + n] *= gain;
+ }
+ }
+
+ // Verify the channels are clsoe to the reference.
+ should(actual0, 'Left output from gain node')
+ .beCloseToArray(
+ reference0, {relativeThreshold: 1.1877e-7});
+ should(actual1, 'Right output from gain node')
+ .beCloseToArray(
+ reference1, {relativeThreshold: 1.1877e-7});
+
+ // Test the SNR too for both channels.
+ let snr0 = 10 * Math.log10(computeSNR(actual0, reference0));
+ let snr1 = 10 * Math.log10(computeSNR(actual1, reference1));
+ should(snr0, 'Left SNR (in dB)')
+ .beGreaterThanOrEqualTo(148.71);
+ should(snr1, 'Right SNR (in dB)')
+ .beGreaterThanOrEqualTo(148.71);
+ })
+ .then(() => task.done());
+ ;
+ });
+
+ audit.run();
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/webaudio/the-audio-api/the-gainnode-interface/no-dezippering.html b/testing/web-platform/tests/webaudio/the-audio-api/the-gainnode-interface/no-dezippering.html
new file mode 100644
index 0000000000..6326d00dfb
--- /dev/null
+++ b/testing/web-platform/tests/webaudio/the-audio-api/the-gainnode-interface/no-dezippering.html
@@ -0,0 +1,121 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>
+ Gain Dezippering Test: Dezippering Removed
+ </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 audit = Audit.createTaskRunner();
+
+ audit.define(
+ {label: 'test0', description: 'Dezippering of GainNode removed'},
+ (task, should) => {
+ // Only need a few frames to verify that dezippering has been
+ // removed from the GainNode. Sample rate is pretty arbitrary.
+ let context = new OfflineAudioContext(1, 1024, 16000);
+
+ // Send a unit source to the gain node so we can measure the effect
+ // of the gain node.
+ let src = new ConstantSourceNode(context, {offset: 1});
+ let g = new GainNode(context, {gain: 1});
+ src.connect(g).connect(context.destination);
+
+ context.suspend(RENDER_QUANTUM_FRAMES / context.sampleRate)
+ .then(() => {
+ g.gain.value = .5;
+ })
+ .then(() => context.resume());
+
+ src.start();
+
+ context.startRendering()
+ .then(audio => {
+ let c = audio.getChannelData(0);
+
+ // If dezippering has been removed, the gain output should
+ // instantly jump at frame 128 to 0.5.
+ should(c.slice(0, 128), 'output[0:127]').beConstantValueOf(1);
+ should(c.slice(128), 'output[128:]').beConstantValueOf(0.5);
+ })
+ .then(() => task.done());
+ });
+
+ audit.define(
+ {
+ label: 'test2',
+ description: 'Compare value setter and setValueAtTime'
+ },
+ (task, should) => {
+ testWithAutomation(should, {prefix: ''}).then(() => task.done());
+ });
+
+ audit.define(
+ {label: 'test3', description: 'Automation effects'},
+ (task, should) => {
+ testWithAutomation(should, {
+ prefix: 'With modulation: ',
+ modulator: true
+ }).then(() => task.done());
+ });
+
+ audit.run();
+
+ function testWithAutomation(should, options) {
+ // Sample rate must be a power of two to eliminate round-off in
+ // computing the time at render quantum boundaries.
+ let context = new OfflineAudioContext(2, 1024, 16384);
+ let merger = new ChannelMergerNode(context, {numberOfChannels: 2});
+ merger.connect(context.destination);
+
+ let src = new OscillatorNode(context);
+ let gainTest = new GainNode(context);
+ let gainRef = new GainNode(context);
+
+ src.connect(gainTest).connect(merger, 0, 0);
+ src.connect(gainRef).connect(merger, 0, 1);
+
+ if (options.modulator) {
+ let mod = new OscillatorNode(context, {frequency: 1000});
+ let modGain = new GainNode(context);
+ mod.connect(modGain);
+ modGain.connect(gainTest.gain);
+ modGain.connect(gainRef.gain);
+ mod.start();
+ }
+
+ // Change the gains. Must do the change on a render boundary!
+ let changeTime = 3 * RENDER_QUANTUM_FRAMES / context.sampleRate;
+ let newGain = .3;
+
+ gainRef.gain.setValueAtTime(newGain, changeTime);
+ context.suspend(changeTime)
+ .then(() => gainTest.gain.value = newGain)
+ .then(() => context.resume());
+
+ src.start();
+
+ return context.startRendering().then(audio => {
+ let actual = audio.getChannelData(0);
+ let expected = audio.getChannelData(1);
+
+ // The values using the .value setter must be identical to the
+ // values using setValueAtTime.
+ let match = should(actual, options.prefix + '.value setter output')
+ .beEqualToArray(expected);
+
+ should(
+ match,
+ options.prefix +
+ '.value setter output matches setValueAtTime output')
+ .beTrue();
+ });
+ }
+ </script>
+ </body>
+</html>