ctor-panner.html (17438B)
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <title> 5 Test Constructor: Panner 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 <script src="/webaudio/resources/audionodeoptions.js"></script> 12 </head> 13 <body> 14 <script id="layout-test-code"> 15 let context; 16 17 let audit = Audit.createTaskRunner(); 18 19 audit.define('initialize', (task, should) => { 20 context = initializeContext(should); 21 task.done(); 22 }); 23 24 audit.define('invalid constructor', (task, should) => { 25 testInvalidConstructor(should, 'PannerNode', context); 26 task.done(); 27 }); 28 29 audit.define('default constructor', (task, should) => { 30 let prefix = 'node0'; 31 let node = testDefaultConstructor(should, 'PannerNode', context, { 32 prefix: prefix, 33 numberOfInputs: 1, 34 numberOfOutputs: 1, 35 channelCount: 2, 36 channelCountMode: 'clamped-max', 37 channelInterpretation: 'speakers' 38 }); 39 40 testDefaultAttributes(should, node, prefix, [ 41 {name: 'panningModel', value: 'equalpower'}, 42 {name: 'positionX', value: 0}, {name: 'positionY', value: 0}, 43 {name: 'positionZ', value: 0}, {name: 'orientationX', value: 1}, 44 {name: 'orientationY', value: 0}, {name: 'orientationZ', value: 0}, 45 {name: 'distanceModel', value: 'inverse'}, 46 {name: 'refDistance', value: 1}, {name: 'maxDistance', value: 10000}, 47 {name: 'rolloffFactor', value: 1}, 48 {name: 'coneInnerAngle', value: 360}, 49 {name: 'coneOuterAngle', value: 360}, 50 {name: 'coneOuterGain', value: 0} 51 ]); 52 53 // Test the listener too, while we're at it. 54 let listenerAttributes = [ 55 {name: 'positionX', value: 0}, 56 {name: 'positionY', value: 0}, 57 {name: 'positionZ', value: 0}, 58 {name: 'forwardX', value: 0}, 59 {name: 'forwardY', value: 0}, 60 {name: 'forwardZ', value: -1}, 61 {name: 'upX', value: 0}, 62 {name: 'upY', value: 1}, 63 {name: 'upZ', value: 0}, 64 ]; 65 66 listenerAttributes.forEach((item) => { 67 should( 68 context.listener[item.name].value, 69 'context.listener.' + item.name + '.value') 70 .beEqualTo(item.value); 71 }); 72 73 task.done(); 74 }); 75 76 audit.define('test AudioNodeOptions', (task, should) => { 77 // Can't use testAudioNodeOptions because the constraints for this node 78 // are not supported there. 79 let node; 80 let success = true; 81 82 // Test that we can set the channel count to 1 or 2. 83 let options = {channelCount: 1}; 84 should( 85 () => { 86 node = new PannerNode(context, options); 87 }, 88 'node1 = new PannerNode(c, ' + JSON.stringify(options) + ')') 89 .notThrow(); 90 should(node.channelCount, 'node1.channelCount') 91 .beEqualTo(options.channelCount); 92 93 options = {channelCount: 2}; 94 should( 95 () => { 96 node = new PannerNode(context, options); 97 }, 98 'node2 = new PannerNode(c, ' + JSON.stringify(options) + ')') 99 .notThrow(); 100 should(node.channelCount, 'node2.channelCount') 101 .beEqualTo(options.channelCount); 102 103 // Test that other channel counts throw an error 104 options = {channelCount: 0}; 105 should( 106 () => { 107 node = new PannerNode(context, options); 108 }, 109 'new PannerNode(c, ' + JSON.stringify(options) + ')') 110 .throw(DOMException, 'NotSupportedError'); 111 should( 112 () => { 113 node = new PannerNode(context); 114 node.channelCount = options.channelCount; 115 }, 116 `node.channelCount = ${options.channelCount}`) 117 .throw(DOMException, "NotSupportedError"); 118 should(node.channelCount, 119 `node.channelCount after setting to ${options.channelCount}`) 120 .beEqualTo(2); 121 122 options = {channelCount: 3}; 123 should( 124 () => { 125 node = new PannerNode(context, options); 126 }, 127 'new PannerNode(c, ' + JSON.stringify(options) + ')') 128 .throw(DOMException, 'NotSupportedError'); 129 should( 130 () => { 131 node = new PannerNode(context); 132 node.channelCount = options.channelCount; 133 }, 134 `node.channelCount = ${options.channelCount}`) 135 .throw(DOMException, "NotSupportedError"); 136 should(node.channelCount, 137 `node.channelCount after setting to ${options.channelCount}`) 138 .beEqualTo(2); 139 140 options = {channelCount: 99}; 141 should( 142 () => { 143 node = new PannerNode(context, options); 144 }, 145 'new PannerNode(c, ' + JSON.stringify(options) + ')') 146 .throw(DOMException, 'NotSupportedError'); 147 should( 148 () => { 149 node = new PannerNode(context); 150 node.channelCount = options.channelCount; 151 }, 152 `node.channelCount = ${options.channelCount}`) 153 .throw(DOMException, "NotSupportedError"); 154 should(node.channelCount, 155 `node.channelCount after setting to ${options.channelCount}`) 156 .beEqualTo(2); 157 158 // Test channelCountMode. A mode of "max" is illegal, but others are 159 // ok. 160 options = {channelCountMode: 'clamped-max'}; 161 should( 162 () => { 163 node = new PannerNode(context, options); 164 }, 165 'node3 = new PannerNode(c, ' + JSON.stringify(options) + ')') 166 .notThrow(); 167 should(node.channelCountMode, 'node3.channelCountMode') 168 .beEqualTo(options.channelCountMode); 169 170 options = {channelCountMode: 'explicit'}; 171 should( 172 () => { 173 node = new PannerNode(context, options); 174 }, 175 'node4 = new PannerNode(c, ' + JSON.stringify(options) + ')') 176 .notThrow(); 177 should(node.channelCountMode, 'node4.channelCountMode') 178 .beEqualTo(options.channelCountMode); 179 180 options = {channelCountMode: 'max'}; 181 should( 182 () => { 183 node = new PannerNode(context, options); 184 }, 185 'new PannerNode(c, ' + JSON.stringify(options) + ')') 186 .throw(DOMException, 'NotSupportedError'); 187 should( 188 () => { 189 node = new PannerNode(context); 190 node.channelCountMode = options.channelCountMode; 191 }, 192 `node.channelCountMode = ${options.channelCountMode}`) 193 .throw(DOMException, "NotSupportedError"); 194 should(node.channelCountMode, 195 `node.channelCountMode after setting to ${options.channelCountMode}`) 196 .beEqualTo("clamped-max"); 197 198 options = {channelCountMode: 'foobar'}; 199 should( 200 () => { 201 node = new PannerNode(context, options); 202 }, 203 'new PannerNode(c, " + JSON.stringify(options) + ")') 204 .throw(TypeError); 205 should( 206 () => { 207 node = new PannerNode(context); 208 node.channelCountMode = options.channelCountMode; 209 }, 210 `node.channelCountMode = ${options.channelCountMode}`) 211 .notThrow(); // Invalid assignment to enum-valued attrs does not throw. 212 should(node.channelCountMode, 213 `node.channelCountMode after setting to ${options.channelCountMode}`) 214 .beEqualTo("clamped-max"); 215 216 // Test channelInterpretation. 217 options = {channelInterpretation: 'speakers'}; 218 should( 219 () => { 220 node = new PannerNode(context, options); 221 }, 222 'node5 = new PannerNode(c, ' + JSON.stringify(options) + ')') 223 .notThrow(); 224 should(node.channelInterpretation, 'node5.channelInterpretation') 225 .beEqualTo(options.channelInterpretation); 226 227 options = {channelInterpretation: 'discrete'}; 228 should( 229 () => { 230 node = new PannerNode(context, options); 231 }, 232 'node6 = new PannerNode(c, ' + JSON.stringify(options) + ')') 233 .notThrow(); 234 should(node.channelInterpretation, 'node6.channelInterpretation') 235 .beEqualTo(options.channelInterpretation); 236 237 options = {channelInterpretation: 'foobar'}; 238 should( 239 () => { 240 node = new PannerNode(context, options); 241 }, 242 'new PannerNode(c, ' + JSON.stringify(options) + ')') 243 .throw(TypeError); 244 245 // Test maxDistance 246 options = {maxDistance: -1}; 247 should( 248 () => { 249 node = new PannerNode(context, options); 250 }, 251 'new PannerNode(c, ' + JSON.stringify(options) + ')') 252 .throw(RangeError); 253 should( 254 () => { 255 node = new PannerNode(context); 256 node.maxDistance = options.maxDistance; 257 }, 258 `node.maxDistance = ${options.maxDistance}`) 259 .throw(RangeError); 260 should(node.maxDistance, 261 `node.maxDistance after setting to ${options.maxDistance}`) 262 .beEqualTo(10000); 263 264 options = {maxDistance: 100}; 265 should( 266 () => { 267 node = new PannerNode(context, options); 268 }, 269 'node7 = new PannerNode(c, ' + JSON.stringify(options) + ')') 270 .notThrow(); 271 should(node.maxDistance, 'node7.maxDistance') 272 .beEqualTo(options.maxDistance); 273 274 // Test rolloffFactor 275 options = {rolloffFactor: -1}; 276 should( 277 () => { 278 node = new PannerNode(context, options); 279 }, 280 'new PannerNode(c, ' + JSON.stringify(options) + ')') 281 .throw(RangeError); 282 should( 283 () => { 284 node = new PannerNode(context); 285 node.rolloffFactor = options.rolloffFactor; 286 }, 287 `node.rolloffFactor = ${options.rolloffFactor}`) 288 .throw(RangeError); 289 should(node.rolloffFactor, 290 `node.rolloffFactor after setting to ${options.rolloffFactor}`) 291 .beEqualTo(1); 292 293 options = {rolloffFactor: 0}; 294 should( 295 () => { 296 node = new PannerNode(context, options); 297 }, 298 'node8 = new PannerNode(c, ' + JSON.stringify(options) + ')') 299 .notThrow(); 300 should(node.rolloffFactor, 'node8.rolloffFactor') 301 .beEqualTo(options.rolloffFactor); 302 303 options = {rolloffFactor: 0.5}; 304 should( 305 () => { 306 node = new PannerNode(context, options); 307 }, 308 'node8 = new PannerNode(c, ' + JSON.stringify(options) + ')') 309 .notThrow(); 310 should(node.rolloffFactor, 'node8.rolloffFactor') 311 .beEqualTo(options.rolloffFactor); 312 313 options = {rolloffFactor: 100}; 314 should( 315 () => { 316 node = new PannerNode(context, options); 317 }, 318 'node8 = new PannerNode(c, ' + JSON.stringify(options) + ')') 319 .notThrow(); 320 should(node.rolloffFactor, 'node8.rolloffFactor') 321 .beEqualTo(options.rolloffFactor); 322 323 // Test coneOuterGain 324 options = {coneOuterGain: -1}; 325 should( 326 () => { 327 node = new PannerNode(context, options); 328 }, 329 'new PannerNode(c, ' + JSON.stringify(options) + ')') 330 .throw(DOMException, 'InvalidStateError'); 331 should( 332 () => { 333 node = new PannerNode(context); 334 node.coneOuterGain = options.coneOuterGain; 335 }, 336 `node.coneOuterGain = ${options.coneOuterGain}`) 337 .throw(DOMException, 'InvalidStateError'); 338 should(node.coneOuterGain, 339 `node.coneOuterGain after setting to ${options.coneOuterGain}`) 340 .beEqualTo(0); 341 342 options = {coneOuterGain: 1.1}; 343 should( 344 () => { 345 node = new PannerNode(context, options); 346 }, 347 'new PannerNode(c, ' + JSON.stringify(options) + ')') 348 .throw(DOMException, 'InvalidStateError'); 349 should( 350 () => { 351 node = new PannerNode(context); 352 node.coneOuterGain = options.coneOuterGain; 353 }, 354 `node.coneOuterGain = ${options.coneOuterGain}`) 355 .throw(DOMException, 'InvalidStateError'); 356 should(node.coneOuterGain, 357 `node.coneOuterGain after setting to ${options.coneOuterGain}`) 358 .beEqualTo(0); 359 360 options = {coneOuterGain: 0.0}; 361 should( 362 () => { 363 node = new PannerNode(context, options); 364 }, 365 'node9 = new PannerNode(c, ' + JSON.stringify(options) + ')') 366 .notThrow(); 367 should(node.coneOuterGain, 'node9.coneOuterGain') 368 .beEqualTo(options.coneOuterGain); 369 options = {coneOuterGain: 0.5}; 370 should( 371 () => { 372 node = new PannerNode(context, options); 373 }, 374 'node9 = new PannerNode(c, ' + JSON.stringify(options) + ')') 375 .notThrow(); 376 should(node.coneOuterGain, 'node9.coneOuterGain') 377 .beEqualTo(options.coneOuterGain); 378 379 options = {coneOuterGain: 1.0}; 380 should( 381 () => { 382 node = new PannerNode(context, options); 383 }, 384 'node9 = new PannerNode(c, ' + JSON.stringify(options) + ')') 385 .notThrow(); 386 should(node.coneOuterGain, 'node9.coneOuterGain') 387 .beEqualTo(options.coneOuterGain); 388 389 task.done(); 390 }); 391 392 audit.define('constructor with options', (task, should) => { 393 let node; 394 let success = true; 395 let options = { 396 panningModel: 'HRTF', 397 // We use full double float values here to verify also that the actual 398 // AudioParam value is properly rounded to a float. The actual value 399 // is immaterial as long as x != Math.fround(x). 400 positionX: Math.SQRT2, 401 positionY: 2 * Math.SQRT2, 402 positionZ: 3 * Math.SQRT2, 403 orientationX: -Math.SQRT2, 404 orientationY: -2 * Math.SQRT2, 405 orientationZ: -3 * Math.SQRT2, 406 distanceModel: 'linear', 407 // We use full double float values here to verify also that the actual 408 // attribute is a double float. The actual value is immaterial as 409 // long as x != Math.fround(x). 410 refDistance: Math.PI, 411 maxDistance: 2 * Math.PI, 412 rolloffFactor: 3 * Math.PI, 413 coneInnerAngle: 4 * Math.PI, 414 coneOuterAngle: 5 * Math.PI, 415 coneOuterGain: 0.1 * Math.PI 416 }; 417 418 should( 419 () => { 420 node = new PannerNode(context, options); 421 }, 422 'node = new PannerNode(c, ' + JSON.stringify(options) + ')') 423 .notThrow(); 424 should(node instanceof PannerNode, 'node instanceof PannerNode') 425 .beEqualTo(true); 426 427 should(node.panningModel, 'node.panningModel') 428 .beEqualTo(options.panningModel); 429 should(node.positionX.value, 'node.positionX.value') 430 .beEqualTo(Math.fround(options.positionX)); 431 should(node.positionY.value, 'node.positionY.value') 432 .beEqualTo(Math.fround(options.positionY)); 433 should(node.positionZ.value, 'node.positionZ.value') 434 .beEqualTo(Math.fround(options.positionZ)); 435 should(node.orientationX.value, 'node.orientationX.value') 436 .beEqualTo(Math.fround(options.orientationX)); 437 should(node.orientationY.value, 'node.orientationY.value') 438 .beEqualTo(Math.fround(options.orientationY)); 439 should(node.orientationZ.value, 'node.orientationZ.value') 440 .beEqualTo(Math.fround(options.orientationZ)); 441 should(node.distanceModel, 'node.distanceModel') 442 .beEqualTo(options.distanceModel); 443 should(node.refDistance, 'node.refDistance') 444 .beEqualTo(options.refDistance); 445 should(node.maxDistance, 'node.maxDistance') 446 .beEqualTo(options.maxDistance); 447 should(node.rolloffFactor, 'node.rolloffFactor') 448 .beEqualTo(options.rolloffFactor); 449 should(node.coneInnerAngle, 'node.coneInnerAngle') 450 .beEqualTo(options.coneInnerAngle); 451 should(node.coneOuterAngle, 'node.coneOuterAngle') 452 .beEqualTo(options.coneOuterAngle); 453 should(node.coneOuterGain, 'node.coneOuterGain') 454 .beEqualTo(options.coneOuterGain); 455 456 should(node.channelCount, 'node.channelCount').beEqualTo(2); 457 should(node.channelCountMode, 'node.channelCountMode') 458 .beEqualTo('clamped-max'); 459 should(node.channelInterpretation, 'node.channelInterpretation') 460 .beEqualTo('speakers'); 461 462 task.done(); 463 }); 464 465 audit.run(); 466 </script> 467 </body> 468 </html>