k-rate-delay-connections.html (4403B)
1 <!doctype html> 2 <html> 3 <head> 4 <title>DelayNode delayTime with k-rate input should match automation</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 // Power-of-two sample rate to eliminate round-off errors. 13 const sampleRate = 8192; 14 15 // Arbitrary test duration (must be ≥ 1s). 16 const testDurationSeconds = 1.5; 17 18 // Total number of frames based on sample rate and duration. 19 const totalFrames = sampleRate * testDurationSeconds; 20 21 promise_test(async t => { 22 // Two channels: 0 = test result, 1 = expected result. 23 const context = new OfflineAudioContext({ 24 numberOfChannels: 2, 25 sampleRate: sampleRate, 26 length: totalFrames 27 }); 28 29 const merger = new ChannelMergerNode(context, { 30 numberOfInputs: context.destination.channelCount 31 }); 32 merger.connect(context.destination); 33 34 // Test the DelayNode by having a reference node (refNode) that uses 35 // k-rate automations of delayTime. The test node (testNode) sets 36 // delayTime to k-rate with a connected input that has the same 37 // automation vlaues as the reference node. The test passes if the 38 // output from each node is identical to each other. 39 40 const oscillator = new OscillatorNode(context); 41 42 // The end value and time for the linear ramp. These values are 43 // chosen so that the delay advances faster than real time. 44 const rampEndValue = 1.125; 45 const rampEndTime = 1; 46 47 const refNode = new DelayNode(context); 48 refNode.delayTime.automationRate = 'k-rate'; 49 refNode.delayTime.setValueAtTime(0, 0); 50 refNode.delayTime.linearRampToValueAtTime(rampEndValue, rampEndTime); 51 52 const testNode = new DelayNode(context); 53 testNode.delayTime.automationRate = 'k-rate'; 54 55 const modulator = new ConstantSourceNode(context); 56 modulator.offset.setValueAtTime(0, 0); 57 modulator.offset.linearRampToValueAtTime(rampEndValue, rampEndTime); 58 modulator.connect(testNode.delayTime); 59 60 oscillator.connect(testNode).connect(merger, 0, 0); 61 oscillator.connect(refNode).connect(merger, 0, 1); 62 63 oscillator.start(); 64 modulator.start(); 65 66 const renderedBuffer = await context.startRendering(); 67 const actual = renderedBuffer.getChannelData(0); 68 const expected = renderedBuffer.getChannelData(1); 69 70 // Quick sanity check that output isn't zero. This means we messed 71 // up the connections or automations or the buffer source. 72 assert_not_constant_value( 73 expected, 0, 'Expected output should not be constant zero'); 74 assert_not_constant_value( 75 actual, 0, 'Actual output should not be constant zero'); 76 77 // Quick sanity check. The amount of delay after one render is 78 // endValue * 128 / sampleRate. But after 1 render, time has 79 // advanced 128/sampleRate. Hence, the delay exceeds the time by 80 // (endValue - 1)*128/sampleRate sec or (endValue - 1)*128 frames. 81 // This means the output must be EXACTLY zero for this many frames 82 // in the second render. 83 const silentFrames = (rampEndValue - 1) * RENDER_QUANTUM_FRAMES; 84 85 assert_strict_constant_value( 86 actual.slice( 87 RENDER_QUANTUM_FRAMES, RENDER_QUANTUM_FRAMES + silentFrames), 88 0, 89 `output[${RENDER_QUANTUM_FRAMES}` + 90 `..${RENDER_QUANTUM_FRAMES + silentFrames - 1}] must be silent` 91 ); 92 93 // Next quantum should NOT be silent 94 assert_not_constant_value( 95 actual.slice( 96 RENDER_QUANTUM_FRAMES + silentFrames, 97 2 * RENDER_QUANTUM_FRAMES), 98 0, 99 `output[${RENDER_QUANTUM_FRAMES + silentFrames}` + 100 `..${2 * RENDER_QUANTUM_FRAMES - 1}] must have signal` 101 ); 102 103 // Compare actual vs expected output 104 assert_array_equals_exact( 105 actual, 106 expected, 107 'Output from testNode should exactly match reference'); 108 }, 'k-rate DelayNode.delayTime with input matches automation behavior'); 109 </script> 110 </body> 111 </html>