no-dezippering.html (7401B)
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <title> 5 Test DelayNode Has No Dezippering 6 </title> 7 <script src="/resources/testharness.js"></script> 8 <script src="/resources/testharnessreport.js"></script> 9 <script src="/webaudio/resources/audit-util.js"></script> 10 <script src="/webaudio/resources/audit.js"></script> 11 </head> 12 <body> 13 <script id="layout-test-code"> 14 // The sample rate must be a power of two to avoid any round-off errors in 15 // computing when to suspend a context on a rendering quantum boundary. 16 // Otherwise this is pretty arbitrary. 17 let sampleRate = 16384; 18 19 let audit = Audit.createTaskRunner(); 20 21 audit.define( 22 {label: 'test0', description: 'Test DelayNode has no dezippering'}, 23 (task, should) => { 24 let context = new OfflineAudioContext(1, sampleRate, sampleRate); 25 26 // Simple integer ramp for testing delay node 27 let buffer = new AudioBuffer( 28 {length: context.length, sampleRate: context.sampleRate}); 29 let rampData = buffer.getChannelData(0); 30 for (let k = 0; k < rampData.length; ++k) { 31 rampData[k] = k + 1; 32 } 33 34 // |delay0Frame| is the initial delay in frames. |delay1Frame| is 35 // the new delay in frames. These must be integers. 36 let delay0Frame = 64; 37 let delay1Frame = 16; 38 39 let src = new AudioBufferSourceNode(context, {buffer: buffer}); 40 let delay = new DelayNode( 41 context, {delayTime: delay0Frame / context.sampleRate}); 42 43 src.connect(delay).connect(context.destination); 44 45 // After a render quantum, change the delay to |delay1Frame|. 46 context.suspend(RENDER_QUANTUM_FRAMES / context.sampleRate) 47 .then(() => { 48 delay.delayTime.value = delay1Frame / context.sampleRate; 49 }) 50 .then(() => context.resume()); 51 52 src.start(); 53 context.startRendering() 54 .then(renderedBuffer => { 55 let renderedData = renderedBuffer.getChannelData(0); 56 57 // The first |delay0Frame| frames should be zero. 58 should( 59 renderedData.slice(0, delay0Frame), 60 'output[0:' + (delay0Frame - 1) + ']') 61 .beConstantValueOf(0); 62 63 // Now we have the ramp should show up from the delay. 64 let ramp0 = 65 new Float32Array(RENDER_QUANTUM_FRAMES - delay0Frame); 66 for (let k = 0; k < ramp0.length; ++k) { 67 ramp0[k] = rampData[k]; 68 } 69 70 should( 71 renderedData.slice(delay0Frame, RENDER_QUANTUM_FRAMES), 72 'output[' + delay0Frame + ':' + 73 (RENDER_QUANTUM_FRAMES - 1) + ']') 74 .beEqualToArray(ramp0); 75 76 // After one rendering quantum, the delay is changed to 77 // |delay1Frame|. 78 let ramp1 = 79 new Float32Array(context.length - RENDER_QUANTUM_FRAMES); 80 for (let k = 0; k < ramp1.length; ++k) { 81 // ramp1[k] = 1 + k + RENDER_QUANTUM_FRAMES - delay1Frame; 82 ramp1[k] = 83 rampData[k + RENDER_QUANTUM_FRAMES - delay1Frame]; 84 } 85 should( 86 renderedData.slice(RENDER_QUANTUM_FRAMES), 87 'output[' + RENDER_QUANTUM_FRAMES + ':]') 88 .beEqualToArray(ramp1); 89 }) 90 .then(() => task.done()); 91 }); 92 93 audit.define( 94 {label: 'test1', description: 'Test value setter and setValueAtTime'}, 95 (task, should) => { 96 testWithAutomation(should, {prefix: '', threshold: 6.5819e-5}) 97 .then(() => task.done()); 98 }); 99 100 audit.define( 101 {label: 'test2', description: 'Test value setter and modulation'}, 102 (task, should) => { 103 testWithAutomation(should, { 104 prefix: 'With modulation: ', 105 modulator: true 106 }).then(() => task.done()); 107 }); 108 109 // Compare .value setter with setValueAtTime, Optionally allow modulation 110 // of |delayTime|. 111 function testWithAutomation(should, options) { 112 let prefix = options.prefix; 113 // Channel 0 is the output of delay node using the setter and channel 1 114 // is the output using setValueAtTime. 115 let context = new OfflineAudioContext(2, sampleRate, sampleRate); 116 117 let merger = new ChannelMergerNode( 118 context, {numberOfInputs: context.destination.channelCount}); 119 merger.connect(context.destination); 120 121 let src = new OscillatorNode(context); 122 123 // |delay0Frame| is the initial delay value in frames. |delay1Frame| is 124 // the new delay in frames. The values here are constrained only by the 125 // constraints for a DelayNode. These are pretty arbitrary except we 126 // wanted them to be fractional so as not be on a frame boundary to 127 // test interpolation compared with |setValueAtTime()|.. 128 let delay0Frame = 3.1; 129 let delay1Frame = 47.2; 130 131 let delayTest = new DelayNode( 132 context, {delayTime: delay0Frame / context.sampleRate}); 133 let delayRef = new DelayNode( 134 context, {delayTime: delay0Frame / context.sampleRate}); 135 136 src.connect(delayTest).connect(merger, 0, 0); 137 src.connect(delayRef).connect(merger, 0, 1); 138 139 if (options.modulator) { 140 // Fairly arbitrary modulation of the delay time, with a peak 141 // variation of 10 ms. 142 let mod = new OscillatorNode(context, {frequency: 1000}); 143 let modGain = new GainNode(context, {gain: .01}); 144 mod.connect(modGain); 145 modGain.connect(delayTest.delayTime); 146 modGain.connect(delayRef.delayTime); 147 mod.start(); 148 } 149 150 // The time at which the delay time of |delayTest| node will be 151 // changed. This MUST be on a render quantum boundary, but is 152 // otherwise arbitrary. 153 let changeTime = 3 * RENDER_QUANTUM_FRAMES / context.sampleRate; 154 155 // Schedule the delay change on |delayRef| and also apply the value 156 // setter for |delayTest| at |changeTime|. 157 delayRef.delayTime.setValueAtTime( 158 delay1Frame / context.sampleRate, changeTime); 159 context.suspend(changeTime) 160 .then(() => { 161 delayTest.delayTime.value = delay1Frame / context.sampleRate; 162 }) 163 .then(() => context.resume()); 164 165 src.start(); 166 167 return context.startRendering().then(renderedBuffer => { 168 let actual = renderedBuffer.getChannelData(0); 169 let expected = renderedBuffer.getChannelData(1); 170 171 let match = should(actual, prefix + '.value setter output') 172 .beCloseToArray( 173 expected, {absoluteThreshold: options.threshold}); 174 should( 175 match, 176 prefix + '.value setter output matches setValueAtTime output') 177 .beTrue(); 178 }); 179 } 180 181 audit.run(); 182 </script> 183 </body> 184 </html>