summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/webaudio/the-audio-api/the-convolvernode-interface/realtime-conv.html
diff options
context:
space:
mode:
Diffstat (limited to 'testing/web-platform/tests/webaudio/the-audio-api/the-convolvernode-interface/realtime-conv.html')
-rw-r--r--testing/web-platform/tests/webaudio/the-audio-api/the-convolvernode-interface/realtime-conv.html149
1 files changed, 149 insertions, 0 deletions
diff --git a/testing/web-platform/tests/webaudio/the-audio-api/the-convolvernode-interface/realtime-conv.html b/testing/web-platform/tests/webaudio/the-audio-api/the-convolvernode-interface/realtime-conv.html
new file mode 100644
index 0000000000..8668e9d5ac
--- /dev/null
+++ b/testing/web-platform/tests/webaudio/the-audio-api/the-convolvernode-interface/realtime-conv.html
@@ -0,0 +1,149 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>
+ Test Convolver on Real-time Context
+ </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/convolution-testing.js"></script>
+ </head>
+ <body>
+ <script id="layout-test-code">
+ const audit = Audit.createTaskRunner();
+ // Choose a length that is larger enough to cause multiple threads to be
+ // used in the convolver. For browsers that don't support this, this
+ // value doesn't matter.
+ const pulseLength = 16384;
+
+ // The computed SNR should be at least this large. This value depends on
+ // teh platform and browser. Don't set this value to be to much lower
+ // than this. It probably indicates a fairly inaccurate convolver or
+ // constant source node automations that should be fixed instead.
+ const minRequiredSNR = 77.03;
+
+ // To test the real-time convolver, we convolve two square pulses together
+ // to produce a triangular pulse. To verify the result is correct we
+ // compare it against a constant source node configured to generate the
+ // expected ramp.
+ audit.define(
+ {label: 'test', description: 'Test convolver with real-time context'},
+ (task, should) => {
+ // Use a power of two for the sample rate to eliminate round-off in
+ // computing times from frames.
+ const context = new AudioContext({sampleRate: 16384});
+
+ // Square pulse for the convolver impulse response.
+ const squarePulse = new AudioBuffer(
+ {length: pulseLength, sampleRate: context.sampleRate});
+ squarePulse.getChannelData(0).fill(1);
+
+ const convolver = new ConvolverNode(
+ context, {buffer: squarePulse, disableNormalization: true});
+
+ // Square pulse for testing
+ const srcSquare = new ConstantSourceNode(context, {offset: 0});
+ srcSquare.connect(convolver);
+
+ // Reference ramp. Automations on this constant source node will
+ // generate the desired ramp.
+ const srcRamp = new ConstantSourceNode(context, {offset: 0});
+
+ // Use these gain nodes to compute the difference between the
+ // convolver output and the expected ramp to create the error
+ // signal.
+ const inverter = new GainNode(context, {gain: -1});
+ const sum = new GainNode(context, {gain: 1});
+ convolver.connect(sum);
+ srcRamp.connect(inverter).connect(sum);
+
+ // Square the error signal using this gain node.
+ const squarer = new GainNode(context, {gain: 0});
+ sum.connect(squarer);
+ sum.connect(squarer.gain);
+
+ // Merge the error signal and the square source so we can integrate
+ // the error signal to find an SNR.
+ const merger = new ChannelMergerNode(context, {numberOfInputs: 2});
+
+ squarer.connect(merger, 0, 0);
+ srcSquare.connect(merger, 0, 1);
+
+ // For simplicity, use a ScriptProcessor to integrate the error
+ // signal. The square pulse signal is used as a gate over which the
+ // integration is done. When the pulse ends, the SNR is computed
+ // and the test ends.
+
+ // |doSum| is used to determine when to integrate and when it
+ // becomes false, it signals the end of integration.
+ let doSum = false;
+
+ // |signalSum| is the energy in the square pulse. |errorSum| is the
+ // energy in the error signal.
+ let signalSum = 0;
+ let errorSum = 0;
+
+ let spn = context.createScriptProcessor(0, 2, 1);
+ spn.onaudioprocess = (event) => {
+ // Sum the values on the first channel when the second channel is
+ // not zero. When the second channel goes from non-zero to 0,
+ // dump the value out and terminate the test.
+ let c0 = event.inputBuffer.getChannelData(0);
+ let c1 = event.inputBuffer.getChannelData(1);
+
+ for (let k = 0; k < c1.length; ++k) {
+ if (c1[k] == 0) {
+ if (doSum) {
+ doSum = false;
+ // Square wave is now silent and we were integration, so we
+ // can stop now and verify the SNR.
+ should(10 * Math.log10(signalSum / errorSum), 'SNR')
+ .beGreaterThanOrEqualTo(minRequiredSNR);
+ spn.onaudioprocess = null;
+ task.done();
+ }
+ } else {
+ // Signal is non-zero so sum up the values.
+ doSum = true;
+ errorSum += c0[k];
+ signalSum += c1[k] * c1[k];
+ }
+ }
+ };
+
+ merger.connect(spn).connect(context.destination);
+
+ // Schedule everything to start a bit in the futurefrom now, and end
+ // pulseLength frames later.
+ let now = context.currentTime;
+
+ // |startFrame| is the number of frames to schedule ahead for
+ // testing.
+ const startFrame = 512;
+ const startTime = startFrame / context.sampleRate;
+ const pulseDuration = pulseLength / context.sampleRate;
+
+ // Create a square pulse in the constant source node.
+ srcSquare.offset.setValueAtTime(1, now + startTime);
+ srcSquare.offset.setValueAtTime(0, now + startTime + pulseDuration);
+
+ // Create the reference ramp.
+ srcRamp.offset.setValueAtTime(1, now + startTime);
+ srcRamp.offset.linearRampToValueAtTime(
+ pulseLength,
+ now + startTime + pulseDuration - 1 / context.sampleRate);
+ srcRamp.offset.linearRampToValueAtTime(
+ 0,
+ now + startTime + 2 * pulseDuration - 1 / context.sampleRate);
+
+ // Start the ramps!
+ srcRamp.start();
+ srcSquare.start();
+ });
+
+ audit.run();
+ </script>
+ </body>
+</html>