audioparam-setValueCurve-exceptions.html (15100B)
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <title> 5 Test Exceptions from setValueCurveAtTime 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 let sampleRate = 48000; 15 // Some short duration because we don't need to run the test for very 16 // long. 17 let testDurationSec = 0.125; 18 let testDurationFrames = testDurationSec * sampleRate; 19 20 let audit = Audit.createTaskRunner(); 21 22 audit.define('setValueCurve', (task, should) => { 23 let context = 24 new OfflineAudioContext(1, testDurationFrames, sampleRate); 25 let g = context.createGain(); 26 let curve = new Float32Array(2); 27 28 // Start time and duration for setValueCurveAtTime 29 let curveStartTime = 0.1 * testDurationSec; 30 let duration = 0.1 * testDurationSec; 31 32 // Some time that is known to be during the setValueCurveTime interval. 33 let automationTime = curveStartTime + duration / 2; 34 35 should( 36 () => { 37 g.gain.setValueCurveAtTime(curve, curveStartTime, duration); 38 }, 39 'setValueCurveAtTime(curve, ' + curveStartTime + ', ' + duration + 40 ')') 41 .notThrow(); 42 43 should( 44 function() { 45 g.gain.setValueAtTime(1, automationTime); 46 }, 47 'setValueAtTime(1, ' + automationTime + ')') 48 .throw(DOMException, 'NotSupportedError'); 49 50 should( 51 function() { 52 g.gain.linearRampToValueAtTime(1, automationTime); 53 }, 54 'linearRampToValueAtTime(1, ' + automationTime + ')') 55 .throw(DOMException, 'NotSupportedError'); 56 57 should( 58 function() { 59 g.gain.exponentialRampToValueAtTime(1, automationTime); 60 }, 61 'exponentialRampToValueAtTime(1, ' + automationTime + ')') 62 .throw(DOMException, 'NotSupportedError'); 63 64 should( 65 function() { 66 g.gain.setTargetAtTime(1, automationTime, 1); 67 }, 68 'setTargetAtTime(1, ' + automationTime + ', 1)') 69 .throw(DOMException, 'NotSupportedError'); 70 71 should( 72 function() { 73 g.gain.setValueAtTime(1, curveStartTime + 1.1 * duration); 74 }, 75 'setValueAtTime(1, ' + (curveStartTime + 1.1 * duration) + ')') 76 .notThrow(); 77 78 task.done(); 79 }); 80 81 audit.define('value setter', (task, should) => { 82 let context = 83 new OfflineAudioContext(1, testDurationFrames, sampleRate); 84 let g = context.createGain(); 85 let curve = new Float32Array(2); 86 87 // Start time and duration for setValueCurveAtTime 88 let curveStartTime = 0.; 89 let duration = 0.2 * testDurationSec; 90 91 // Some time that is known to be during the setValueCurveTime interval. 92 let automationTime = 0.; 93 94 should( 95 () => { 96 g.gain.setValueCurveAtTime(curve, curveStartTime, duration); 97 }, 98 'setValueCurveAtTime(curve, ' + curveStartTime + ', ' + duration + 99 ')') 100 .notThrow(); 101 102 should( 103 function() { 104 g.gain.value = 0.; 105 }, 106 'value setter') 107 .throw(DOMException, 'NotSupportedError'); 108 109 task.done(); 110 }); 111 112 audit.define('automations', (task, should) => { 113 let context = 114 new OfflineAudioContext(1, testDurationFrames, sampleRate); 115 let g = context.createGain(); 116 117 let curve = new Float32Array(2); 118 // Start time and duration for setValueCurveAtTime 119 let startTime = 0; 120 let timeInterval = testDurationSec / 10; 121 let time; 122 123 startTime += timeInterval; 124 should(() => { 125 g.gain.linearRampToValueAtTime(1, startTime); 126 }, 'linearRampToValueAtTime(1, ' + startTime + ')').notThrow(); 127 128 startTime += timeInterval; 129 should(() => { 130 g.gain.exponentialRampToValueAtTime(1, startTime); 131 }, 'exponentialRampToValueAtTime(1, ' + startTime + ')').notThrow(); 132 133 startTime += timeInterval; 134 should(() => { 135 g.gain.setTargetAtTime(1, startTime, 0.1); 136 }, 'setTargetAtTime(1, ' + startTime + ', 0.1)').notThrow(); 137 138 startTime += timeInterval; 139 should(() => { 140 g.gain.setValueCurveAtTime(curve, startTime, 0.1); 141 }, 'setValueCurveAtTime(curve, ' + startTime + ', 0.1)').notThrow(); 142 143 // Now try to setValueCurve that overlaps each of the above automations 144 startTime = timeInterval / 2; 145 146 for (let k = 0; k < 4; ++k) { 147 time = startTime + timeInterval * k; 148 should( 149 () => { 150 g.gain.setValueCurveAtTime(curve, time, 0.01); 151 }, 152 'setValueCurveAtTime(curve, ' + time + ', 0.01)') 153 .throw(DOMException, 'NotSupportedError'); 154 } 155 156 // Elements of setValueCurve should be finite. 157 should( 158 () => { 159 g.gain.setValueCurveAtTime( 160 Float32Array.from([NaN, NaN]), time, 0.01); 161 }, 162 'setValueCurveAtTime([NaN, NaN], ' + time + ', 0.01)') 163 .throw(TypeError); 164 165 should( 166 () => { 167 g.gain.setValueCurveAtTime( 168 Float32Array.from([1, Infinity]), time, 0.01); 169 }, 170 'setValueCurveAtTime([1, Infinity], ' + time + ', 0.01)') 171 .throw(TypeError); 172 173 let d = context.createDelay(); 174 // Check that we get warnings for out-of-range values and also throw for 175 // non-finite values. 176 should( 177 () => { 178 d.delayTime.setValueCurveAtTime( 179 Float32Array.from([1, 5]), time, 0.01); 180 }, 181 'delayTime.setValueCurveAtTime([1, 5], ' + time + ', 0.01)') 182 .notThrow(); 183 184 should( 185 () => { 186 d.delayTime.setValueCurveAtTime( 187 Float32Array.from([1, 5, Infinity]), time, 0.01); 188 }, 189 'delayTime.setValueCurveAtTime([1, 5, Infinity], ' + time + 190 ', 0.01)') 191 .throw(TypeError); 192 193 // One last test that prints out lots of digits for the time. 194 time = Math.PI / 100; 195 should( 196 () => { 197 g.gain.setValueCurveAtTime(curve, time, 0.01); 198 }, 199 'setValueCurveAtTime(curve, ' + time + ', 0.01)') 200 .throw(DOMException, 'NotSupportedError'); 201 202 task.done(); 203 }); 204 205 audit.define('catch-exception', (task, should) => { 206 // Verify that the curve isn't inserted into the time line even if we 207 // catch the exception. 208 let context = 209 new OfflineAudioContext(1, testDurationFrames, sampleRate); 210 let gain = context.createGain(); 211 let source = context.createBufferSource(); 212 let buffer = context.createBuffer(1, 1, context.sampleRate); 213 buffer.getChannelData(0)[0] = 1; 214 source.buffer = buffer; 215 source.loop = true; 216 217 source.connect(gain); 218 gain.connect(context.destination); 219 220 gain.gain.setValueAtTime(1, 0); 221 try { 222 // The value curve has an invalid element. This automation shouldn't 223 // be inserted into the timeline at all. 224 gain.gain.setValueCurveAtTime( 225 Float32Array.from([0, NaN]), 128 / context.sampleRate, .5); 226 } catch (e) { 227 }; 228 source.start(); 229 230 context.startRendering() 231 .then(function(resultBuffer) { 232 // Since the setValueCurve wasn't inserted, the output should be 233 // exactly 1 for the entire duration. 234 should( 235 resultBuffer.getChannelData(0), 236 'Handled setValueCurve exception so output') 237 .beConstantValueOf(1); 238 239 }) 240 .then(() => task.done()); 241 }); 242 243 audit.define('start-end', (task, should) => { 244 let context = 245 new OfflineAudioContext(1, testDurationFrames, sampleRate); 246 let g = context.createGain(); 247 let curve = new Float32Array(2); 248 249 // Verify that a setValueCurve can start at the end of an automation. 250 let time = 0; 251 let timeInterval = testDurationSec / 50; 252 should(() => { 253 g.gain.setValueAtTime(1, time); 254 }, 'setValueAtTime(1, ' + time + ')').notThrow(); 255 256 time += timeInterval; 257 should(() => { 258 g.gain.linearRampToValueAtTime(0, time); 259 }, 'linearRampToValueAtTime(0, ' + time + ')').notThrow(); 260 261 // setValueCurve starts at the end of the linear ramp. This should be 262 // fine. 263 should( 264 () => { 265 g.gain.setValueCurveAtTime(curve, time, timeInterval); 266 }, 267 'setValueCurveAtTime(..., ' + time + ', ' + timeInterval + ')') 268 .notThrow(); 269 270 // exponentialRamp ending one interval past the setValueCurve should be 271 // fine. 272 time += 2 * timeInterval; 273 should(() => { 274 g.gain.exponentialRampToValueAtTime(1, time); 275 }, 'exponentialRampToValueAtTime(1, ' + time + ')').notThrow(); 276 277 // setValueCurve starts at the end of the exponential ramp. This should 278 // be fine. 279 should( 280 () => { 281 g.gain.setValueCurveAtTime(curve, time, timeInterval); 282 }, 283 'setValueCurveAtTime(..., ' + time + ', ' + timeInterval + ')') 284 .notThrow(); 285 286 // setValueCurve at the end of the setValueCurve should be fine. 287 time += timeInterval; 288 should( 289 () => { 290 g.gain.setValueCurveAtTime(curve, time, timeInterval); 291 }, 292 'setValueCurveAtTime(..., ' + time + ', ' + timeInterval + ')') 293 .notThrow(); 294 295 // setValueAtTime at the end of setValueCurve should be fine. 296 time += timeInterval; 297 should(() => { 298 g.gain.setValueAtTime(0, time); 299 }, 'setValueAtTime(0, ' + time + ')').notThrow(); 300 301 // setValueCurve at the end of setValueAtTime should be fine. 302 should( 303 () => { 304 g.gain.setValueCurveAtTime(curve, time, timeInterval); 305 }, 306 'setValueCurveAtTime(..., ' + time + ', ' + timeInterval + ')') 307 .notThrow(); 308 309 // setTarget starting at the end of setValueCurve should be fine. 310 time += timeInterval; 311 should(() => { 312 g.gain.setTargetAtTime(1, time, 1); 313 }, 'setTargetAtTime(1, ' + time + ', 1)').notThrow(); 314 315 task.done(); 316 }); 317 318 audit.define('curve overlap', (task, should) => { 319 let context = 320 new OfflineAudioContext(1, testDurationFrames, sampleRate); 321 let g = context.createGain(); 322 let startTime = 5; 323 let startTimeLater = 10; 324 let startTimeEarlier = 2.5; 325 let curveDuration = 10; 326 let curveDurationShorter = 5; 327 let curve = [1, 2, 3]; 328 329 // An initial curve event 330 should( 331 () => { 332 g.gain.setValueCurveAtTime(curve, startTime, curveDuration); 333 }, 334 `g.gain.setValueCurveAtTime([${curve}], ${startTime}, ${curveDuration})`) 335 .notThrow(); 336 337 // Check that an exception is thrown when trying to overlap two curves, 338 // in various ways 339 340 // Same start time and end time (curve exactly overlapping) 341 should( 342 () => { 343 g.gain.setValueCurveAtTime(curve, startTime, curveDuration); 344 }, 345 `second g.gain.setValueCurveAtTime([${curve}], ${startTime}, ${curveDuration})`) 346 .throw(DOMException, 'NotSupportedError'); 347 // Same start time, shorter end time 348 should( 349 () => { 350 g.gain.setValueCurveAtTime(curve, startTime, curveDurationShorter); 351 }, 352 `g.gain.setValueCurveAtTime([${curve}], ${startTime}, ${curveDurationShorter})`) 353 .throw(DOMException, 'NotSupportedError'); 354 // Earlier start time, end time after the start time an another curve 355 should( 356 () => { 357 g.gain.setValueCurveAtTime(curve, startTimeEarlier, curveDuration); 358 }, 359 `g.gain.setValueCurveAtTime([${curve}], ${startTimeEarlier}, ${curveDuration})`) 360 .throw(DOMException, 'NotSupportedError'); 361 // Start time after the start time of the other curve, but earlier than 362 // its end. 363 should( 364 () => { 365 g.gain.setValueCurveAtTime(curve, startTimeLater, curveDuration); 366 }, 367 `g.gain.setValueCurveAtTime([${curve}], ${startTimeLater}, ${curveDuration})`) 368 .throw(DOMException, 'NotSupportedError'); 369 370 // New event wholly contained inside existing event 371 should( 372 () => { 373 g.gain.setValueCurveAtTime(curve, startTime + 1, curveDuration - 1); 374 }, 375 `g.gain.setValueCurveAtTime([${curve}], ${startTime+1}, ${curveDuration-1})`) 376 .throw(DOMException, 'NotSupportedError'); 377 // Old event completely contained inside new event 378 should( 379 () => { 380 g.gain.setValueCurveAtTime(curve, startTime - 1, curveDuration + 1); 381 }, 382 `g.gain.setValueCurveAtTime([${curve}], ${startTime-1}, ${curveDuration+1})`) 383 .throw(DOMException, 'NotSupportedError'); 384 // Setting an event exactly at the end of the curve should work. 385 should( 386 () => { 387 g.gain.setValueAtTime(1.0, startTime + curveDuration); 388 }, 389 `g.gain.setValueAtTime(1.0, ${startTime + curveDuration})`) 390 .notThrow(); 391 392 task.done(); 393 }); 394 395 audit.define('curve lengths', (task, should) => { 396 let context = 397 new OfflineAudioContext(1, testDurationFrames, sampleRate); 398 let g = context.createGain(); 399 let time = 0; 400 401 // Check for invalid curve lengths 402 should( 403 () => { 404 g.gain.setValueCurveAtTime(Float32Array.from([]), time, 0.01); 405 }, 406 'setValueCurveAtTime([], ' + time + ', 0.01)') 407 .throw(DOMException, 'InvalidStateError'); 408 409 should( 410 () => { 411 g.gain.setValueCurveAtTime(Float32Array.from([1]), time, 0.01); 412 }, 413 'setValueCurveAtTime([1], ' + time + ', 0.01)') 414 .throw(DOMException, 'InvalidStateError'); 415 416 should(() => { 417 g.gain.setValueCurveAtTime(Float32Array.from([1, 2]), time, 0.01); 418 }, 'setValueCurveAtTime([1,2], ' + time + ', 0.01)').notThrow(); 419 420 task.done(); 421 }); 422 423 audit.run(); 424 </script> 425 </body> 426 </html>