test_stereoPannerNode.html (9818B)
1 <!DOCTYPE HTML> 2 <html> 3 <head> 4 <title>Test StereoPannerNode</title> 5 <script src="/tests/SimpleTest/SimpleTest.js"></script> 6 <script type="text/javascript" src="webaudio.js"></script> 7 <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> 8 </head> 9 <body> 10 <pre id="test"> 11 <script class="testbody" type="text/javascript"> 12 13 var SR = 44100; 14 var BUF_SIZE = 128; 15 var PANNING = 0.1; 16 var GAIN = 0.5; 17 18 // Cheap reimplementation of some bits of the spec 19 function gainForPanningMonoToStereo(panning) { 20 panning += 1; 21 panning /= 2; 22 return [ Math.cos(0.5 * Math.PI * panning), 23 Math.sin(0.5 * Math.PI * panning) ]; 24 } 25 26 function gainForPanningStereoToStereo(panning) { 27 if (panning <= 0) { 28 panning += 1.; 29 } 30 return [ Math.cos(0.5 * Math.PI * panning), 31 Math.sin(0.5 * Math.PI * panning) ]; 32 } 33 34 function applyStereoToStereoPanning(l, r, panningValues, panning) { 35 var outL, outR; 36 if (panning <= 0) { 37 outL = l + r * panningValues[0]; 38 outR = r * panningValues[1]; 39 } else { 40 outL = l * panningValues[0]; 41 outR = r + l * panningValues[1]; 42 } 43 return [outL,outR]; 44 } 45 46 function applyMonoToStereoPanning(c, panning) { 47 return [c * panning[0], c * panning[1]]; 48 } 49 50 // Test the DOM interface 51 var context = new OfflineAudioContext(1, 1, SR); 52 var stereoPanner = new StereoPannerNode(context); 53 ok(stereoPanner.pan, "The AudioParam member must exist"); 54 is(stereoPanner.pan.value, 0.0, "Correct initial value"); 55 is(stereoPanner.pan.defaultValue, 0.0, "Correct default value"); 56 is(stereoPanner.channelCount, 2, "StereoPannerNode node has 2 input channels by default"); 57 is(stereoPanner.channelCountMode, "clamped-max", "Correct channelCountMode for the StereoPannerNode"); 58 is(stereoPanner.channelInterpretation, "speakers", "Correct channelCountInterpretation for the StereoPannerNode"); 59 expectException(function() { 60 stereoPanner.channelCount = 3; 61 }, DOMException.NOT_SUPPORTED_ERR); 62 expectException(function() { 63 stereoPanner.channelCountMode = "max"; 64 }, DOMException.NOT_SUPPORTED_ERR); 65 66 // A sine to be used to fill the buffers 67 function sine(t) { 68 return Math.sin(440 * 2 * Math.PI * t / context.sampleRate); 69 } 70 71 // A couple mono and stereo buffers: the StereoPannerNode equation is different 72 // if the input is mono or stereo 73 var stereoBuffer = new AudioBuffer({ numberOfChannels: 2, 74 length: BUF_SIZE, 75 sampleRate: context.sampleRate }); 76 var monoBuffer = new AudioBuffer({ numberOfChannels: 1, 77 length: BUF_SIZE, 78 sampleRate: context.sampleRate }); 79 for (var i = 0; i < BUF_SIZE; ++i) { 80 monoBuffer.getChannelData(0)[i] = 81 stereoBuffer.getChannelData(0)[i] = 82 stereoBuffer.getChannelData(1)[i] = sine(i); 83 } 84 85 // Expected test vectors 86 function expectedBufferNoop(gain) { 87 gain = gain || 1.0; 88 var expectedBuffer = new AudioBuffer({ numberOfChannels: 2, 89 length: BUF_SIZE, 90 sampleRate: SR }); 91 for (var i = 0; i < BUF_SIZE; i++) { 92 expectedBuffer.getChannelData(0)[i] = gain * sine(i); 93 expectedBuffer.getChannelData(1)[i] = gain * sine(i); 94 } 95 return expectedBuffer; 96 } 97 98 function expectedBufferForStereo(panning, gain) { 99 gain = gain || 1.0; 100 var expectedBuffer = new AudioBuffer({ numberOfChannels: 2, 101 length: BUF_SIZE, 102 sampleRate: SR }); 103 var gainPanning = gainForPanningStereoToStereo(panning); 104 for (var i = 0; i < BUF_SIZE; i++) { 105 var values = [ gain * sine(i), gain * sine(i) ]; 106 var processed = applyStereoToStereoPanning(values[0], values[1], gainPanning, PANNING); 107 expectedBuffer.getChannelData(0)[i] = processed[0]; 108 expectedBuffer.getChannelData(1)[i] = processed[1]; 109 } 110 return expectedBuffer; 111 } 112 113 function expectedBufferForMono(panning, gain) { 114 gain = gain || 1.0; 115 var expectedBuffer = new AudioBuffer({ numberOfChannels: 2, 116 length: BUF_SIZE, 117 sampleRate: SR }); 118 var gainPanning = gainForPanningMonoToStereo(panning); 119 gainPanning[0] *= gain; 120 gainPanning[1] *= gain; 121 for (var i = 0; i < BUF_SIZE; i++) { 122 var value = sine(i); 123 var processed = applyMonoToStereoPanning(value, gainPanning); 124 expectedBuffer.getChannelData(0)[i] = processed[0]; 125 expectedBuffer.getChannelData(1)[i] = processed[1]; 126 } 127 return expectedBuffer; 128 } 129 130 // Actual test cases 131 var tests = [ 132 function monoPanningNoop(ctx, panner) { 133 var monoSource = ctx.createBufferSource(); 134 monoSource.connect(panner); 135 monoSource.buffer = monoBuffer; 136 monoSource.start(0); 137 return expectedBufferForMono(0); 138 }, 139 function stereoPanningNoop(ctx, panner) { 140 var stereoSource = ctx.createBufferSource(); 141 stereoSource.connect(panner); 142 stereoSource.buffer = stereoBuffer; 143 stereoSource.start(0); 144 return expectedBufferNoop(); 145 }, 146 function monoPanningNoopWithGain(ctx, panner) { 147 var monoSource = ctx.createBufferSource(); 148 var gain = ctx.createGain(); 149 gain.gain.value = GAIN; 150 monoSource.connect(gain); 151 gain.connect(panner); 152 monoSource.buffer = monoBuffer; 153 monoSource.start(0); 154 return expectedBufferForMono(0, GAIN); 155 }, 156 function stereoPanningNoopWithGain(ctx, panner) { 157 var stereoSource = ctx.createBufferSource(); 158 var gain = ctx.createGain(); 159 gain.gain.value = GAIN; 160 stereoSource.connect(gain); 161 gain.connect(panner); 162 stereoSource.buffer = stereoBuffer; 163 stereoSource.start(0); 164 return expectedBufferNoop(GAIN); 165 }, 166 function stereoPanningAutomation(ctx, panner) { 167 var stereoSource = ctx.createBufferSource(); 168 stereoSource.connect(panner); 169 stereoSource.buffer = stereoBuffer; 170 panner.pan.setValueAtTime(0.1, 0.0); 171 stereoSource.start(0); 172 return expectedBufferForStereo(PANNING); 173 }, 174 function stereoPanning(ctx, panner) { 175 var stereoSource = ctx.createBufferSource(); 176 stereoSource.buffer = stereoBuffer; 177 stereoSource.connect(panner); 178 panner.pan.value = 0.1; 179 stereoSource.start(0); 180 return expectedBufferForStereo(PANNING); 181 }, 182 function monoPanningAutomation(ctx, panner) { 183 var monoSource = ctx.createBufferSource(); 184 monoSource.connect(panner); 185 monoSource.buffer = monoBuffer; 186 panner.pan.setValueAtTime(PANNING, 0.0); 187 monoSource.start(0); 188 return expectedBufferForMono(PANNING); 189 }, 190 function monoPanning(ctx, panner) { 191 var monoSource = ctx.createBufferSource(); 192 monoSource.connect(panner); 193 monoSource.buffer = monoBuffer; 194 panner.pan.value = 0.1; 195 monoSource.start(0); 196 return expectedBufferForMono(PANNING); 197 }, 198 function monoPanningWithGain(ctx, panner) { 199 var monoSource = ctx.createBufferSource(); 200 var gain = ctx.createGain(); 201 gain.gain.value = GAIN; 202 monoSource.connect(gain); 203 gain.connect(panner); 204 monoSource.buffer = monoBuffer; 205 panner.pan.value = 0.1; 206 monoSource.start(0); 207 return expectedBufferForMono(PANNING, GAIN); 208 }, 209 function stereoPanningWithGain(ctx, panner) { 210 var stereoSource = ctx.createBufferSource(); 211 var gain = ctx.createGain(); 212 gain.gain.value = GAIN; 213 stereoSource.connect(gain); 214 gain.connect(panner); 215 stereoSource.buffer = stereoBuffer; 216 panner.pan.value = 0.1; 217 stereoSource.start(0); 218 return expectedBufferForStereo(PANNING, GAIN); 219 }, 220 function monoPanningWithGainAndAutomation(ctx, panner) { 221 var monoSource = ctx.createBufferSource(); 222 var gain = ctx.createGain(); 223 gain.gain.value = GAIN; 224 monoSource.connect(gain); 225 gain.connect(panner); 226 monoSource.buffer = monoBuffer; 227 panner.pan.setValueAtTime(PANNING, 0); 228 monoSource.start(0); 229 return expectedBufferForMono(PANNING, GAIN); 230 }, 231 function stereoPanningWithGainAndAutomation(ctx, panner) { 232 var stereoSource = ctx.createBufferSource(); 233 var gain = ctx.createGain(); 234 gain.gain.value = GAIN; 235 stereoSource.connect(gain); 236 gain.connect(panner); 237 stereoSource.buffer = stereoBuffer; 238 panner.pan.setValueAtTime(PANNING, 0); 239 stereoSource.start(0); 240 return expectedBufferForStereo(PANNING, GAIN); 241 }, 242 function bug_1783181(ctx, panner) { 243 const length = 128; 244 const buffer = new AudioBuffer({ length, numberOfChannels: 2, sampleRate: ctx.sampleRate }); 245 246 buffer.copyToChannel(new Float32Array([1, 0.5, 0, -0.5, -1]), 0); 247 buffer.copyToChannel(new Float32Array([-0.5, -0.25, 0, 0.25, 0.5]), 1); 248 249 const audioBufferSourceNode = new AudioBufferSourceNode(ctx, { buffer }); 250 251 audioBufferSourceNode.connect(panner); 252 253 panner.pan.setValueAtTime(0.5, 0); 254 panner.pan.setValueAtTime(0, 2 / ctx.sampleRate); 255 panner.pan.linearRampToValueAtTime(1, 5 / ctx.sampleRate); 256 panner.pan.cancelScheduledValues(3 / ctx.sampleRate); 257 258 audioBufferSourceNode.start(0); 259 260 const expected = new AudioBuffer({ length, numberOfChannels: 2, sampleRate: ctx.sampleRate }); 261 expected.copyToChannel(new Float32Array([ 0.7071067690849304, 0.3535533845424652, 0, -0.5, -1 ]), 0); 262 expected.copyToChannel(new Float32Array([ 0.20710676908493042, 0.10355338454246521, 0, 0.25, 0.5 ]), 1); 263 264 return expected; 265 } 266 ]; 267 268 var finished = 0; 269 function finish() { 270 if (++finished == tests.length) { 271 SimpleTest.finish(); 272 } 273 } 274 275 tests.forEach(function(f) { 276 var ac = new OfflineAudioContext(2, BUF_SIZE, SR); 277 var panner = ac.createStereoPanner(); 278 panner.connect(ac.destination); 279 var expected = f(ac, panner); 280 ac.oncomplete = function(e) { 281 info(f.name); 282 compareBuffers(e.renderedBuffer, expected); 283 finish(); 284 }; 285 ac.startRendering() 286 }); 287 288 SimpleTest.waitForExplicitFinish(); 289 290 </script> 291 </pre> 292 <pre id=dump> 293 </pre> 294 </body> 295 </html>