k-rate-audiobuffersource-connections.html (5006B)
1 <!doctype html> 2 <html> 3 <head> 4 <title>k-rate AudioParams with inputs for AudioBufferSourceNode</title> 5 <script src="/resources/testharness.js"></script> 6 <script src="/resources/testharnessreport.js"></script> 7 <script src="/webaudio/resources/audit-util.js"></script> 8 </head> 9 10 <body> 11 <script> 12 const sampleRate = 8000; 13 const testDuration = 0.25; 14 15 const testCases = [ 16 { paramName: 'playbackRate', startValue: [1, 0], 17 endValue: [2, testDuration] }, 18 { paramName: 'detune', startValue: [-1200, 0], 19 endValue: [1200, testDuration] } 20 ]; 21 22 for (const { paramName, startValue, endValue } of testCases) { 23 promise_test(async t => { 24 const prefix = `AudioBufferSourceNode ${paramName}`; 25 await runKRateParamInputTest(t, { 26 prefix, 27 paramName, 28 startValue, 29 endValue 30 }); 31 }, `Verify k-rate AudioParam input works correctly for ${paramName}`); 32 } 33 34 async function runKRateParamInputTest(test, options) { 35 // Test k-rate automation of AudioBufferSourceNode with connected 36 // input. 37 // 38 // A reference source node is created with an automation on the 39 // selected AudioParam. For simplicity, we just use a linear ramp from 40 // the minValue to the maxValue of the AudioParam. 41 // 42 // The test node has an input signal connected to the AudioParam. This 43 // input signal is created to match the automation on the reference 44 // node. 45 // 46 // Finally, the output from the two nodes must be identical if k-rate 47 // inputs are working correctly. 48 // 49 // Options parameter is a dictionary with the following required 50 // members: 51 // prefix - prefix to use for the messages. 52 // paramName - Name of the AudioParam to be tested 53 54 const { prefix, paramName, startValue, endValue } = options; 55 56 const context = new OfflineAudioContext({ 57 numberOfChannels: 2, 58 sampleRate, 59 length: testDuration * sampleRate 60 }); 61 62 const merger = new ChannelMergerNode(context, { 63 numberOfInputs: context.destination.channelCount 64 }); 65 merger.connect(context.destination); 66 67 const rampBuffer = createLinearRampBuffer(context, context.length); 68 69 let refNode, tstNode; 70 71 const nodeOptions = { buffer: rampBuffer }; 72 73 test.step(() => { 74 refNode = new AudioBufferSourceNode(context, nodeOptions); 75 }, `${prefix}: create reference AudioBufferSourceNode`); 76 77 test.step(() => { 78 tstNode = new AudioBufferSourceNode(context, nodeOptions); 79 }, `${prefix}: create test AudioBufferSourceNode`); 80 81 // Set automation on the reference node’s param 82 test.step(() => { 83 refNode[paramName].setValueAtTime(...startValue); 84 }, `${prefix}: setValueAtTime(${startValue[0]}, ${startValue[1]})`); 85 86 test.step(() => { 87 refNode[paramName].linearRampToValueAtTime(...endValue); 88 }, `${prefix}: linearRampToValueAtTime(${endValue[0]}, ` + 89 `${endValue[1]})`); 90 91 // Modulate test node’s param via input signal 92 let mod = new ConstantSourceNode(context, { offset: 0 }); 93 94 const modStart = startValue[0] - refNode[paramName].defaultValue; 95 const modEnd = endValue[0] - refNode[paramName].defaultValue; 96 97 test.step(() => { 98 mod.offset.setValueAtTime(modStart, startValue[1]); 99 }, `${prefix}: mod.offset.setValueAtTime(${modStart}, ` + 100 ` ${startValue[1]})`); 101 102 test.step(() => { 103 mod.offset.linearRampToValueAtTime(modEnd, endValue[1]); 104 }, `${prefix}: mod.offset.linearRampToValueAtTime(${modEnd}, ` + 105 ` ${endValue[1]})`); 106 107 test.step(() => { 108 mod.connect(tstNode[paramName]); 109 }, `${prefix}: mod.connect(tstNode[${paramName}])`); 110 111 // Connect both nodes to different merger inputs 112 refNode.connect(merger, 0, 0); 113 tstNode.connect(merger, 0, 1); 114 115 // Start all nodes 116 refNode.start(); 117 tstNode.start(); 118 mod.start(); 119 120 const renderedBuffer = await context.startRendering(); 121 122 const expected = renderedBuffer.getChannelData(0); 123 const actual = renderedBuffer.getChannelData(1); 124 125 // Sanity check: outputs should not be silent 126 assert_false( 127 expected.every(v => v === 0), 128 `${prefix}: expected output is not silent` 129 ); 130 131 assert_false( 132 actual.every(v => v === 0), 133 `${prefix}: actual output is not silent` 134 ); 135 136 // Expected and actual outputs must match exactly 137 assert_array_approx_equals( 138 actual, 139 expected, 140 0, 141 `${prefix}: actual matches expected for k-rate AudioParam input` 142 ); 143 } 144 </script> 145 </body> 146 </html>