k-rate-dynamics-compressor-connections.html (3847B)
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <title>k-rate AudioParams with inputs for DynamicsCompressorNode</title> 5 <script src="/resources/testharness.js"></script> 6 <script src="/resources/testharnessreport.js"></script> 7 </head> 8 <body> 9 <script> 10 // Fairly arbitrary sampleRate and somewhat duration 11 const sampleRate = 48000; 12 const testDuration = 0.25; 13 14 ['attack', 'knee', 'ratio', 'release', 'threshold'].forEach(paramName => { 15 promise_test(async () => { 16 await doTest({prefix: paramName, paramName: paramName}); 17 }, `Verify k-rate '${paramName}' with input on DynamicsCompressorNode`); 18 }); 19 20 async function doTest(options) { 21 // Test k-rate automation of DynamicsCompressorNode with connected 22 // input. 23 // 24 // A reference compressor node is created with an automation on the 25 // selected AudioParam. For simplicity, we just use a linear ramp from 26 // the minValue to the maxValue of the AudioParam. 27 // 28 // The test node has an input signal connected to the AudioParam. This 29 // input signal is created to match the automation on the reference 30 // node. 31 // 32 // Finally, the output from the two nodes must be identical if k-rate 33 // inputs are working correctly. 34 // 35 // Options parameter is a dictionary with the following required 36 // members: 37 // prefix - prefix to use for the messages. 38 // paramName - Name of the AudioParam to be tested 39 40 const {prefix, paramName} = options; 41 42 const context = new OfflineAudioContext({ 43 numberOfChannels: 2, 44 sampleRate: sampleRate, 45 length: testDuration * sampleRate 46 }); 47 48 const merger = new ChannelMergerNode( 49 context, {numberOfInputs: context.destination.channelCount}); 50 merger.connect(context.destination); 51 52 // Use an oscillator for the source. Pretty arbitrary parameters. 53 const src = new OscillatorNode(context, { 54 type: 'sawtooth', 55 frequency: 440 56 }); 57 58 // Create the reference and test nodes. 59 let refNode = new DynamicsCompressorNode(context); 60 61 const tstOptions = {}; 62 tstOptions[paramName] = refNode[paramName].minValue; 63 64 let tstNode = new DynamicsCompressorNode(context, tstOptions); 65 66 // Automate the AudioParam of the reference node with a linear ramp 67 refNode[paramName].setValueAtTime(refNode[paramName].minValue, 0); 68 refNode[paramName].linearRampToValueAtTime( 69 refNode[paramName].maxValue, testDuration); 70 71 // Create the input node and automate it so that its output, when added 72 // to the intrinsic value of the AudioParam, matches the automation 73 // on the reference node. 74 // 75 // We need to do it this way because the ratio AudioParam has a nominal 76 // range of [1, 20], so we can't just set the value to 0. 77 let mod = new ConstantSourceNode(context, {offset: 0}); 78 79 const endValue = 80 refNode[paramName].maxValue - refNode[paramName].minValue; 81 82 mod.offset.setValueAtTime(0, 0); 83 mod.offset.linearRampToValueAtTime(endValue, testDuration); 84 mod.connect(tstNode[paramName]); 85 86 src.connect(refNode).connect(merger, 0, 0); 87 src.connect(tstNode).connect(merger, 0, 1); 88 89 src.start(); 90 mod.start(); 91 92 const buffer = await context.startRendering(); 93 const expected = buffer.getChannelData(0); 94 const actual = buffer.getChannelData(1); 95 96 for (let i = 0; i < expected.length; ++i) { 97 assert_equals( 98 actual[i], expected[i], 99 `${prefix}: Sample[${i}] must match exactly`); 100 } 101 } 102 </script> 103 </body> 104 </html>