diff options
Diffstat (limited to 'testing/web-platform/tests/webaudio/the-audio-api/the-audioparam-interface/k-rate-oscillator-connections.html')
-rw-r--r-- | testing/web-platform/tests/webaudio/the-audio-api/the-audioparam-interface/k-rate-oscillator-connections.html | 578 |
1 files changed, 578 insertions, 0 deletions
diff --git a/testing/web-platform/tests/webaudio/the-audio-api/the-audioparam-interface/k-rate-oscillator-connections.html b/testing/web-platform/tests/webaudio/the-audio-api/the-audioparam-interface/k-rate-oscillator-connections.html new file mode 100644 index 0000000000..475b364367 --- /dev/null +++ b/testing/web-platform/tests/webaudio/the-audio-api/the-audioparam-interface/k-rate-oscillator-connections.html @@ -0,0 +1,578 @@ +<!doctype html> +<html> + <head> + <title> + k-rate AudioParams with inputs for OscillatorNode + </title> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <script src="/webaudio/resources/audit.js"></script> + <script src="/webaudio/resources/audit-util.js"></script> + </head> + + <body> + <script> + let audit = Audit.createTaskRunner(); + + // Sample rate must be a power of two to eliminate round-off when + // computing time from frames and vice versa. Using a non-power of two + // will work, but the thresholds below will not be zero. They're probably + // closer to 1e-5 or so, but if everything is working correctly, the + // outputs really should be exactly equal. + const sampleRate = 8192; + + // Fairly arbitrary but short duration to limit runtime. + const testFrames = 5 * RENDER_QUANTUM_FRAMES; + const testDuration = testFrames / sampleRate; + + audit.define( + {label: 'Test 1', description: 'k-rate frequency input'}, + async (task, should) => { + // Test that an input to the frequency AudioParam set to k-rate + // works. + + // Fairly arbitrary start and end frequencies for the automation. + const freqStart = 100; + const freqEnd = 2000; + + let refSetup = (context) => { + let srcRef = new OscillatorNode(context, {frequency: 0}); + + should( + () => srcRef.frequency.automationRate = 'k-rate', + `${task.label}: srcRef.frequency.automationRate = 'k-rate'`) + .notThrow(); + should( + () => srcRef.frequency.setValueAtTime(freqStart, 0), + `${task.label}: srcRef.frequency.setValueAtTime(${ + freqStart}, 0)`) + .notThrow(); + should( + () => srcRef.frequency.linearRampToValueAtTime( + freqEnd, testDuration), + `${task.label}: srcRef.frequency.linearRampToValueAtTime(${ + freqEnd}, ${testDuration})`) + .notThrow(); + + return srcRef; + }; + + let testSetup = (context) => { + let srcTest = new OscillatorNode(context, {frequency: 0}); + should( + () => srcTest.frequency.automationRate = 'k-rate', + `${task.label}: srcTest.frequency.automationRate = 'k-rate'`) + .notThrow(); + + return srcTest; + }; + + let modSetup = (context) => { + let mod = new ConstantSourceNode(context, {offset: 0}); + + should( + () => mod.offset.setValueAtTime(freqStart, 0), + `${task.label}: modFreq.offset.setValueAtTime(${ + freqStart}, 0)`) + .notThrow(); + should( + () => + mod.offset.linearRampToValueAtTime(freqEnd, testDuration), + `${task.label}: modFreq.offset.linearRampToValueAtTime(${ + freqEnd}, ${testDuration})`) + .notThrow(); + + // This node is going to be connected to the frequency AudioParam. + return {frequency: mod}; + }; + + await testParams(should, { + prefix: task.label, + summary: 'k-rate frequency with input', + setupRefOsc: refSetup, + setupTestOsc: testSetup, + setupMod: modSetup + }); + + task.done(); + }); + + audit.define( + {label: 'Test 2', description: 'k-rate detune input'}, + async (task, should) => { + // Test that an input to the detune AudioParam set to k-rate works. + // Threshold experimentally determined. It should be probably not + // be much larger than 5e-5. or something is not right. + + // Fairly arbitrary start and end detune values for automation. + const detuneStart = 0; + const detuneEnd = 2000; + + let refSetup = (context) => { + let srcRef = new OscillatorNode(context, {detune: 0}); + + should( + () => srcRef.detune.automationRate = 'k-rate', + `${task.label}: srcRef.detune.automationRate = 'k-rate'`) + .notThrow(); + + should( + () => srcRef.detune.setValueAtTime(detuneStart, 0), + `${task.label}: srcRef.detune.setValueAtTime(${ + detuneStart}, 0)`) + .notThrow(); + should( + () => srcRef.detune.linearRampToValueAtTime( + detuneEnd, testDuration), + `${task.label}: srcRef.detune.linearRampToValueAtTime(${ + detuneEnd}, ${testDuration})`) + .notThrow(); + + return srcRef; + }; + + let testSetup = (context) => { + let srcTest = new OscillatorNode(context, {detune: 0}); + + should( + () => srcTest.detune.automationRate = 'k-rate', + `${task.label}: srcTest.detune.automationRate = 'k-rate'`) + .notThrow(); + + return srcTest; + }; + + let modSetup = (context) => { + let mod = new ConstantSourceNode(context, {offset: 0}); + + should( + () => mod.offset.setValueAtTime(detuneStart, 0), + `${task.label}: modDetune.offset.setValueAtTime(${ + detuneStart}, 0)`) + .notThrow(); + should( + () => mod.offset.linearRampToValueAtTime( + detuneEnd, testDuration), + `${task.label}: modDetune.offset.linearRampToValueAtTime(${ + detuneEnd}, ${testDuration})`) + .notThrow(); + + return {detune: mod}; + }; + + await testParams(should, { + prefix: task.label, + summary: 'k-rate detune with input', + setupRefOsc: refSetup, + setupTestOsc: testSetup, + setupMod: modSetup + }); + + task.done(); + }); + + audit.define( + { + label: 'Test 3', + description: 'k-rate frequency input with a-rate detune' + }, + async (task, should) => { + // Test OscillatorNode with a k-rate frequency with input and an + // a-rate detune iwth automations. + + // Fairly arbitrary start and end values for the frequency and + // detune automations. + const freqStart = 100; + const freqEnd = 2000; + const detuneStart = 0; + const detuneEnd = -2000; + + let refSetup = (context) => { + let node = new OscillatorNode(context, {frequency: 0}); + + // Set up k-rate frequency and a-rate detune + should( + () => node.frequency.automationRate = 'k-rate', + `${task.label}: srcRef.frequency.automationRate = 'k-rate'`) + .notThrow(); + should( + () => node.frequency.setValueAtTime(freqStart, 0), + `${task.label}: srcRef.frequency.setValueAtTime(${ + freqStart}, 0)`) + .notThrow(); + should( + () => node.frequency.linearRampToValueAtTime( + 2000, testDuration), + `${task.label}: srcRef.frequency.linearRampToValueAtTime(${ + freqEnd}, ${testDuration})`) + .notThrow(); + should( + () => node.detune.setValueAtTime(detuneStart, 0), + `${task.label}: srcRef.detune.setValueAtTime(${ + detuneStart}, 0)`) + .notThrow(); + should( + () => node.detune.linearRampToValueAtTime( + detuneEnd, testDuration), + `${task.label}: srcRef.detune.linearRampToValueAtTime(${ + detuneEnd}, ${testDuration})`) + .notThrow(); + + return node; + }; + + let testSetup = (context) => { + let node = new OscillatorNode(context, {frequency: 0}); + + should( + () => node.frequency.automationRate = 'k-rate', + `${task.label}: srcTest.frequency.automationRate = 'k-rate'`) + .notThrow(); + should( + () => node.detune.setValueAtTime(detuneStart, 0), + `${task.label}: srcTest.detune.setValueAtTime(${ + detuneStart}, 0)`) + .notThrow(); + should( + () => node.detune.linearRampToValueAtTime( + detuneEnd, testDuration), + `${task.label}: srcTest.detune.linearRampToValueAtTime(${ + detuneEnd}, ${testDuration})`) + .notThrow(); + + return node; + }; + + let modSetup = (context) => { + let mod = {}; + mod['frequency'] = new ConstantSourceNode(context, {offset: 0}); + + should( + () => mod['frequency'].offset.setValueAtTime(freqStart, 0), + `${task.label}: modFreq.offset.setValueAtTime(${ + freqStart}, 0)`) + .notThrow(); + + should( + () => mod['frequency'].offset.linearRampToValueAtTime( + 2000, testDuration), + `${task.label}: modFreq.offset.linearRampToValueAtTime(${ + freqEnd}, ${testDuration})`) + .notThrow(); + + return mod; + }; + + await testParams(should, { + prefix: task.label, + summary: 'k-rate frequency input with a-rate detune', + setupRefOsc: refSetup, + setupTestOsc: testSetup, + setupMod: modSetup + }); + + task.done(); + }); + + audit.define( + { + label: 'Test 4', + description: 'a-rate frequency with k-rate detune input' + }, + async (task, should) => { + // Test OscillatorNode with an a-rate frequency with automations and + // a k-rate detune with input. + + // Fairly arbitrary start and end values for the frequency and + // detune automations. + const freqStart = 100; + const freqEnd = 2000; + const detuneStart = 0; + const detuneEnd = -2000; + + let refSetup = (context) => { + let node = new OscillatorNode(context, {detune: 0}); + + // Set up a-rate frequency and k-rate detune + should( + () => node.frequency.setValueAtTime(freqStart, 0), + `${task.label}: srcRef.frequency.setValueAtTime(${ + freqStart}, 0)`) + .notThrow(); + should( + () => node.frequency.linearRampToValueAtTime( + 2000, testDuration), + `${task.label}: srcRef.frequency.linearRampToValueAtTime(${ + freqEnd}, ${testDuration})`) + .notThrow(); + should( + () => node.detune.automationRate = 'k-rate', + `${task.label}: srcRef.detune.automationRate = 'k-rate'`) + .notThrow(); + should( + () => node.detune.setValueAtTime(detuneStart, 0), + `${task.label}: srcRef.detune.setValueAtTime(${ + detuneStart}, 0)`) + .notThrow(); + should( + () => node.detune.linearRampToValueAtTime( + detuneEnd, testDuration), + `${task.label}: srcRef.detune.linearRampToValueAtTime(${ + detuneEnd}, ${testDuration})`) + .notThrow(); + + return node; + }; + + let testSetup = (context) => { + let node = new OscillatorNode(context, {detune: 0}); + + should( + () => node.detune.automationRate = 'k-rate', + `${task.label}: srcTest.detune.automationRate = 'k-rate'`) + .notThrow(); + should( + () => node.frequency.setValueAtTime(freqStart, 0), + `${task.label}: srcTest.frequency.setValueAtTime(${ + freqStart}, 0)`) + .notThrow(); + should( + () => node.frequency.linearRampToValueAtTime( + freqEnd, testDuration), + `${task.label}: srcTest.frequency.linearRampToValueAtTime(${ + freqEnd}, ${testDuration})`) + .notThrow(); + + return node; + }; + + let modSetup = (context) => { + let mod = {}; + const name = 'detune'; + + mod['detune'] = new ConstantSourceNode(context, {offset: 0}); + should( + () => mod[name].offset.setValueAtTime(detuneStart, 0), + `${task.label}: modDetune.offset.setValueAtTime(${ + detuneStart}, 0)`) + .notThrow(); + + should( + () => mod[name].offset.linearRampToValueAtTime( + detuneEnd, testDuration), + `${task.label}: modDetune.offset.linearRampToValueAtTime(${ + detuneEnd}, ${testDuration})`) + .notThrow(); + + return mod; + }; + + await testParams(should, { + prefix: task.label, + summary: 'k-rate detune input with a-rate frequency', + setupRefOsc: refSetup, + setupTestOsc: testSetup, + setupMod: modSetup + }); + + task.done(); + }); + + audit.define( + { + label: 'Test 5', + description: 'k-rate inputs for frequency and detune' + }, + async (task, should) => { + // Test OscillatorNode with k-rate frequency and detune with inputs + // on both. + + // Fairly arbitrary start and end values for the frequency and + // detune automations. + const freqStart = 100; + const freqEnd = 2000; + const detuneStart = 0; + const detuneEnd = -2000; + + let refSetup = (context) => { + let node = new OscillatorNode(context, {frequency: 0, detune: 0}); + + should( + () => node.frequency.automationRate = 'k-rate', + `${task.label}: srcRef.frequency.automationRate = 'k-rate'`) + .notThrow(); + should( + () => node.frequency.setValueAtTime(freqStart, 0), + `${task.label}: srcRef.setValueAtTime(${freqStart}, 0)`) + .notThrow(); + should( + () => node.frequency.linearRampToValueAtTime( + freqEnd, testDuration), + `${task.label}: srcRef;.frequency.linearRampToValueAtTime(${ + freqEnd}, ${testDuration})`) + .notThrow(); + should( + () => node.detune.automationRate = 'k-rate', + `${task.label}: srcRef.detune.automationRate = 'k-rate'`) + .notThrow(); + should( + () => node.detune.setValueAtTime(detuneStart, 0), + `${task.label}: srcRef.detune.setValueAtTime(${ + detuneStart}, 0)`) + .notThrow(); + should( + () => node.detune.linearRampToValueAtTime( + detuneEnd, testDuration), + `${task.label}: srcRef.detune.linearRampToValueAtTime(${ + detuneEnd}, ${testDuration})`) + .notThrow(); + + return node; + }; + + let testSetup = (context) => { + let node = new OscillatorNode(context, {frequency: 0, detune: 0}); + + should( + () => node.frequency.automationRate = 'k-rate', + `${task.label}: srcTest.frequency.automationRate = 'k-rate'`) + .notThrow(); + should( + () => node.detune.automationRate = 'k-rate', + `${task.label}: srcTest.detune.automationRate = 'k-rate'`) + .notThrow(); + + return node; + }; + + let modSetup = (context) => { + let modF = new ConstantSourceNode(context, {offset: 0}); + + should( + () => modF.offset.setValueAtTime(freqStart, 0), + `${task.label}: modFreq.offset.setValueAtTime(${ + freqStart}, 0)`) + .notThrow(); + should( + () => modF.offset.linearRampToValueAtTime( + freqEnd, testDuration), + `${task.label}: modFreq.offset.linearRampToValueAtTime(${ + freqEnd}, ${testDuration})`) + .notThrow(); + + let modD = new ConstantSourceNode(context, {offset: 0}); + + should( + () => modD.offset.setValueAtTime(detuneStart, 0), + `${task.label}: modDetune.offset.setValueAtTime(${ + detuneStart}, 0)`) + .notThrow(); + should( + () => modD.offset.linearRampToValueAtTime( + detuneEnd, testDuration), + `${task.label}: modDetune.offset.linearRampToValueAtTime(${ + detuneEnd}, ${testDuration})`) + .notThrow(); + + return {frequency: modF, detune: modD}; + }; + + await testParams(should, { + prefix: task.label, + summary: 'k-rate inputs for both frequency and detune', + setupRefOsc: refSetup, + setupTestOsc: testSetup, + setupMod: modSetup + }); + + task.done(); + }); + + audit.run(); + + async function testParams(should, options) { + // Test a-rate and k-rate AudioParams of an OscillatorNode. + // + // |options| should be a dictionary with these members: + // prefix - prefix to use for messages + // summary - message to be printed with the final results + // setupRefOsc - function returning the reference oscillator + // setupTestOsc - function returning the test oscillator + // setupMod - function returning nodes to be connected to the + // AudioParams. + // + // |setupRefOsc| and |setupTestOsc| are given the context and each + // method is expected to create an OscillatorNode with the appropriate + // automations for testing. The constructed OscillatorNode is returned. + // + // The reference oscillator + // should automate the desired AudioParams at the appropriate automation + // rate, and the output is the expected result. + // + // The test oscillator should set up the AudioParams but expect the + // AudioParam(s) have an input that matches the automation for the + // reference oscillator. + // + // |setupMod| must create one or two ConstantSourceNodes with exactly + // the same automations as used for the reference oscillator. This node + // is used as the input to an AudioParam of the test oscillator. This + // function returns a dictionary whose members are named 'frequency' and + // 'detune'. The name indicates which AudioParam the constant source + // node should be connected to. + + // Two channels: 0 = reference signal, 1 = test signal + let context = new OfflineAudioContext({ + numberOfChannels: 2, + sampleRate: sampleRate, + length: testDuration * sampleRate + }); + + let merger = new ChannelMergerNode( + context, {numberOfInputs: context.destination.channelCount}); + merger.connect(context.destination); + + // The reference oscillator. + let srcRef = options.setupRefOsc(context); + + // The test oscillator. + let srcTest = options.setupTestOsc(context); + + // Inputs to AudioParam. + let mod = options.setupMod(context); + + if (mod['frequency']) { + should( + () => mod['frequency'].connect(srcTest.frequency), + `${options.prefix}: modFreq.connect(srcTest.frequency)`) + .notThrow(); + mod['frequency'].start() + } + + if (mod['detune']) { + should( + () => mod['detune'].connect(srcTest.detune), + `${options.prefix}: modDetune.connect(srcTest.detune)`) + .notThrow(); + mod['detune'].start() + } + + srcRef.connect(merger, 0, 0); + srcTest.connect(merger, 0, 1); + + srcRef.start(); + srcTest.start(); + + let buffer = await context.startRendering(); + let expected = buffer.getChannelData(0); + let actual = buffer.getChannelData(1); + + // The output of the reference and test oscillator should be + // exactly equal because the AudioParam values should be exactly + // equal. + should(actual, options.summary).beCloseToArray(expected, { + absoluteThreshold: 0 + }); + } + </script> + </body> +</html> |