test_bug1447273.html (7239B)
1 <!DOCTYPE HTML> 2 <html> 3 <head> 4 <title>Test bug 1447273 - GainNode with a stereo input and changing volume</title> 5 <script src="/tests/SimpleTest/SimpleTest.js"></script> 6 <script type="text/javascript" src="webaudio.js"></script> 7 <script type="text/javascript" src="head.js"></script> 8 <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> 9 </head> 10 <body> 11 <pre id="test"> 12 <script class="testbody" type="text/javascript"> 13 /** 14 * Sets up a stereo BufferSource and plumbs it through different gain node 15 * configurations. A control gain path with no changes to gain is used along 16 * with 2 other paths which should increase their gain. The result should be 17 * that audio travelling along the increased gain paths is louder than the 18 * control path. 19 */ 20 21 SimpleTest.waitForExplicitFinish(); 22 SimpleTest.requestFlakyTimeout( 23 "This test uses a live audio context and uses a setTimeout to schedule a " + 24 "change to the graph."); 25 addLoadEvent(function() { 26 let context = new AudioContext(); 27 28 let numChannels = 2; 29 let sampleRate = context.sampleRate; 30 // 60 seconds to mitigate timing issues on slow test machines 31 let recordingLength = 60; 32 let bufferLength = sampleRate * recordingLength; 33 let gainExplicitlyIncreased = false; 34 let sourceFinished = false; 35 36 // Create source buffer 37 let sourceBuffer = context.createBuffer(numChannels, bufferLength, sampleRate); 38 for (let i = 0; i < bufferLength; ++i) { 39 sourceBuffer.getChannelData(0)[i] = 1; 40 sourceBuffer.getChannelData(1)[i] = 1; 41 } 42 let source = context.createBufferSource(); 43 source.buffer = sourceBuffer; 44 45 let gainNoChange = context.createGain(); 46 let gainExplicitAssignment = context.createGain(); 47 let gainSetValueAtTime = context.createGain(); 48 49 // All gain nodes start of with the same gain 50 gainNoChange.gain.value = 0.25; 51 gainExplicitAssignment.gain.value = 0.25; 52 gainSetValueAtTime.gain.value = 0.25; 53 54 // Connect source to gain nodes: 55 // source--> gainNoChange 56 // |-> gainExplicitAssignment 57 // \-> gainSetValueAtTime 58 source.connect(gainNoChange); 59 source.connect(gainExplicitAssignment); 60 source.connect(gainSetValueAtTime); 61 62 // Create intermediate media streams (required to repro bug 1447273) 63 let destNoChange = context.createMediaStreamDestination(); 64 let destExplicitAssignement = context.createMediaStreamDestination(); 65 let destSetValueAtTime = context.createMediaStreamDestination(); 66 67 let sourceNoChange = context.createMediaStreamSource(destNoChange.stream); 68 let sourceExplicitAssignement = context.createMediaStreamSource(destExplicitAssignement.stream); 69 let sourceSetValueAtTime = context.createMediaStreamSource(destSetValueAtTime.stream); 70 71 // Connect gain nodes to our intermediate streams: 72 // source--> gainNoChange -> destNoChange -> sourceNoChange 73 // |-> gainExplicitAssignment -> destExplicitAssignement -> sourceExplicitAssignement 74 // \-> gainSetValueAtTime -> destSetValueAtTime -> sourceSetValueAtTime 75 gainNoChange.connect(destNoChange); 76 gainExplicitAssignment.connect(destExplicitAssignement); 77 gainSetValueAtTime.connect(destSetValueAtTime); 78 79 // Create analysers for each path 80 let analyserNoChange = context.createAnalyser(); 81 let analyserExplicitAssignment = context.createAnalyser(); 82 let analyserSetValueAtTime = context.createAnalyser(); 83 84 // Connect our intermediate media streams to analysers: 85 // source--> gainNoChange -> destNoChange -> sourceNoChange -> analyserNoChange 86 // |-> gainExplicitAssignment -> destExplicitAssignement -> sourceExplicitAssignement -> analyserExplicitAssignment 87 // \-> gainSetValueAtTime -> destSetValueAtTime -> sourceSetValueAtTime -> analyserSetValueAtTime 88 sourceNoChange.connect(analyserNoChange); 89 sourceExplicitAssignement.connect(analyserExplicitAssignment); 90 sourceSetValueAtTime.connect(analyserSetValueAtTime); 91 92 // Two seconds in, increase gain for setValueAt path 93 gainSetValueAtTime.gain.setValueAtTime(0.5, 2); 94 95 // Maximum values seen at each analyser node, will be updated by 96 // checkAnalysersForMaxValues() during test. 97 let maxNoGainChange = 0; 98 let maxExplicitAssignment = 0; 99 let maxSetValueAtTime = 0; 100 101 // Poll analysers and check for max values 102 function checkAnalysersForMaxValues() { 103 let findMaxValue = 104 (array) => array.reduce((a, b) => Math.max(Math.abs(a), Math.abs(b))); 105 106 let dataArray = new Float32Array(analyserNoChange.fftSize); 107 analyserNoChange.getFloatTimeDomainData(dataArray); 108 maxNoGainChange = Math.max(maxNoGainChange, findMaxValue(dataArray)); 109 110 analyserExplicitAssignment.getFloatTimeDomainData(dataArray); 111 maxExplicitAssignment = Math.max(maxExplicitAssignment, findMaxValue(dataArray)); 112 113 analyserSetValueAtTime.getFloatTimeDomainData(dataArray); 114 maxSetValueAtTime = Math.max(maxSetValueAtTime, findMaxValue(dataArray)); 115 116 // End test if we've met our conditions 117 // Add a small amount to initial gain to make sure we're not getting 118 // passes due to rounding errors. 119 let epsilon = 0.01; 120 if (maxExplicitAssignment > (maxNoGainChange + epsilon) && 121 maxSetValueAtTime > (maxNoGainChange + epsilon)) { 122 source.stop(); 123 } 124 } 125 126 source.onended = () => { 127 sourceFinished = true; 128 info(`maxNoGainChange: ${maxNoGainChange}`); 129 info(`maxExplicitAssignment: ${maxExplicitAssignment}`); 130 info(`maxSetValueAtTime: ${maxSetValueAtTime}`); 131 ok(gainExplicitlyIncreased, 132 "Gain should be explicitly assinged during test!"); 133 // Add a small amount to initial gain to make sure we're not getting 134 // passes due to rounding errors. 135 let epsilon = 0.01; 136 ok(maxExplicitAssignment > (maxNoGainChange + epsilon), 137 "Volume should increase due to explicit assignment to gain.value"); 138 ok(maxSetValueAtTime > (maxNoGainChange + epsilon), 139 "Volume should increase due to setValueAtTime on gain.value"); 140 SimpleTest.finish(); 141 }; 142 143 // Start the graph 144 source.start(0); 145 146 // We'll use this callback to check our analysers for gain 147 function animationFrameCb() { 148 if (sourceFinished) { 149 return; 150 } 151 requestAnimationFrame(animationFrameCb); 152 checkAnalysersForMaxValues(); 153 } 154 155 // Using timers is gross, but as of writing there doesn't appear to be a 156 // nicer way to perform gain.value = 0.5 on our node. When/if we support 157 // suspend(time) on offline contexts we could potentially use that instead. 158 159 // Roughly 2 seconds through our source buffer (setTimeout flakiness) increase 160 // our gain on gainExplicitAssignment path. 161 window.setTimeout(() => { 162 gainExplicitAssignment.gain.value = 0.5; 163 // Make debugging flaky timeouts in test easier 164 info("Gain explicitly set!") 165 gainExplicitlyIncreased = true; 166 // Start checking analysers, we do this only after changing volume to avoid 167 // possible starvation of this timeout from requestAnimationFrame calls. 168 animationFrameCb(); 169 }, 2000); 170 }); 171 172 </script> 173 </pre> 174 </body> 175 </html>