| OLD | NEW |
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 /** | 5 /** |
| 6 * @fileoverview This is the low-level class that generates ChromeVox's | 6 * @fileoverview This is the low-level class that generates ChromeVox's |
| 7 * earcons. It's designed to be self-contained and not depend on the | 7 * earcons. It's designed to be self-contained and not depend on the |
| 8 * rest of the code. | 8 * rest of the code. |
| 9 */ | 9 */ |
| 10 | 10 |
| (...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 84 | 84 |
| 85 // Private variables. | 85 // Private variables. |
| 86 | 86 |
| 87 /** @type {AudioContext} @private The audio context. */ | 87 /** @type {AudioContext} @private The audio context. */ |
| 88 this.context_ = new AudioContext(); | 88 this.context_ = new AudioContext(); |
| 89 | 89 |
| 90 /** @type {?ConvolverNode} @private The reverb node, lazily initialized. */ | 90 /** @type {?ConvolverNode} @private The reverb node, lazily initialized. */ |
| 91 this.reverbConvolver_ = null; | 91 this.reverbConvolver_ = null; |
| 92 | 92 |
| 93 /** | 93 /** |
| 94 * @type {Object<string, AudioBuffer>} A map between the name of an | 94 * @type {Object<string, AudioBuffer>} A map between the name of an |
| 95 * audio data file and its loaded AudioBuffer. | 95 * audio data file and its loaded AudioBuffer. |
| 96 * @private | 96 * @private |
| 97 */ | 97 */ |
| 98 this.buffers_ = {}; | 98 this.buffers_ = {}; |
| 99 | 99 |
| 100 /** | 100 /** |
| 101 * The source audio nodes for queued tick / tocks for progress. | 101 * The source audio nodes for queued tick / tocks for progress. |
| 102 * Kept around so they can be canceled. | 102 * Kept around so they can be canceled. |
| 103 * | 103 * |
| 104 * @type {Array<Array<AudioNode>>} | 104 * @type {Array<Array<AudioNode>>} |
| 105 * @private | 105 * @private |
| 106 */ | 106 */ |
| 107 this.progressSources_ = []; | 107 this.progressSources_ = []; |
| 108 | 108 |
| 109 /** @type {number} The current gain for progress sounds. @private */ | 109 /** @type {number} The current gain for progress sounds. @private */ |
| 110 this.progressGain_ = 1.0; | 110 this.progressGain_ = 1.0; |
| 111 | 111 |
| 112 /** @type {?number} The current time for progress sounds. @private */ | 112 /** @type {?number} The current time for progress sounds. @private */ |
| 113 this.progressTime_ = this.context_.currentTime; | 113 this.progressTime_ = this.context_.currentTime; |
| 114 | 114 |
| 115 /** | 115 /** |
| 116 * @type {?number} The window.setInterval ID for progress sounds. | 116 * @type {?number} The window.setInterval ID for progress sounds. |
| 117 * @private | 117 * @private |
| 118 */ | 118 */ |
| 119 this.progressIntervalID_ = null; | 119 this.progressIntervalID_ = null; |
| 120 | 120 |
| 121 // Initialization: load the base sound data files asynchronously. | 121 // Initialization: load the base sound data files asynchronously. |
| 122 var allSoundFilesToLoad = EarconEngine.SOUNDS.concat(EarconEngine.REVERBS); | 122 var allSoundFilesToLoad = EarconEngine.SOUNDS.concat(EarconEngine.REVERBS); |
| 123 allSoundFilesToLoad.forEach((function(sound) { | 123 allSoundFilesToLoad.forEach((function(sound) { |
| 124 var url = EarconEngine.BASE_URL + sound + '.wav'; | 124 var url = |
| 125 this.loadSound(sound, url); | 125 EarconEngine.BASE_URL + sound + '.wav'; |
| 126 }).bind(this)); | 126 this.loadSound(sound, url); |
| 127 }).bind(this)); |
| 127 }; | 128 }; |
| 128 | 129 |
| 129 /** | 130 /** |
| 130 * @type {Array<string>} The list of sound data files to load. | 131 * @type {Array<string>} The list of sound data files to load. |
| 131 * @const | 132 * @const |
| 132 */ | 133 */ |
| 133 EarconEngine.SOUNDS = [ | 134 EarconEngine.SOUNDS = |
| 134 'control', | 135 ['control', 'selection', 'selection_reverse', 'skim', 'static']; |
| 135 'selection', | |
| 136 'selection_reverse', | |
| 137 'skim', | |
| 138 'static']; | |
| 139 | 136 |
| 140 /** | 137 /** |
| 141 * @type {Array<string>} The list of reverb data files to load. | 138 * @type {Array<string>} The list of reverb data files to load. |
| 142 * @const | 139 * @const |
| 143 */ | 140 */ |
| 144 EarconEngine.REVERBS = [ | 141 EarconEngine.REVERBS = ['small_room_2']; |
| 145 'small_room_2']; | |
| 146 | 142 |
| 147 /** | 143 /** |
| 148 * @type {number} The scale factor for one half-step. | 144 * @type {number} The scale factor for one half-step. |
| 149 * @const | 145 * @const |
| 150 */ | 146 */ |
| 151 EarconEngine.HALF_STEP = Math.pow(2.0, 1.0 / 12.0); | 147 EarconEngine.HALF_STEP = Math.pow(2.0, 1.0 / 12.0); |
| 152 | 148 |
| 153 /** | 149 /** |
| 154 * @type {string} The base url for earcon sound resources. | 150 * @type {string} The base url for earcon sound resources. |
| 155 * @const | 151 * @const |
| (...skipping 11 matching lines...) Expand all Loading... |
| 167 * @param {string} name The name of the sound to load. | 163 * @param {string} name The name of the sound to load. |
| 168 * @param {string} url The url where the sound should be fetched from. | 164 * @param {string} url The url where the sound should be fetched from. |
| 169 */ | 165 */ |
| 170 EarconEngine.prototype.loadSound = function(name, url) { | 166 EarconEngine.prototype.loadSound = function(name, url) { |
| 171 var request = new XMLHttpRequest(); | 167 var request = new XMLHttpRequest(); |
| 172 request.open('GET', url, true); | 168 request.open('GET', url, true); |
| 173 request.responseType = 'arraybuffer'; | 169 request.responseType = 'arraybuffer'; |
| 174 | 170 |
| 175 // Decode asynchronously. | 171 // Decode asynchronously. |
| 176 request.onload = (function() { | 172 request.onload = (function() { |
| 177 this.context_.decodeAudioData( | 173 this.context_.decodeAudioData( |
| 178 /** @type {!ArrayBuffer} */ (request.response), | 174 /** @type {!ArrayBuffer} */ (request.response), |
| 179 (function(buffer) { | 175 (function(buffer) { |
| 180 this.buffers_[name] = buffer; | 176 this.buffers_[name] = buffer; |
| 181 }).bind(this)); | 177 }).bind(this)); |
| 182 }).bind(this); | 178 }).bind(this); |
| 183 request.send(); | 179 request.send(); |
| 184 }; | 180 }; |
| 185 | 181 |
| 186 /** | 182 /** |
| 187 * Return an AudioNode containing the final processing that all | 183 * Return an AudioNode containing the final processing that all |
| 188 * sounds go through: master volume / gain, panning, and reverb. | 184 * sounds go through: master volume / gain, panning, and reverb. |
| 189 * The chain is hooked up to the destination automatically, so you | 185 * The chain is hooked up to the destination automatically, so you |
| 190 * just need to connect your source to the return value from this | 186 * just need to connect your source to the return value from this |
| 191 * method. | 187 * method. |
| 192 * | 188 * |
| (...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 269 * @return {AudioBufferSourceNode} The source node, so you can stop it | 265 * @return {AudioBufferSourceNode} The source node, so you can stop it |
| 270 * or set event handlers on it. | 266 * or set event handlers on it. |
| 271 */ | 267 */ |
| 272 EarconEngine.prototype.play = function(sound, opt_properties) { | 268 EarconEngine.prototype.play = function(sound, opt_properties) { |
| 273 var source = this.context_.createBufferSource(); | 269 var source = this.context_.createBufferSource(); |
| 274 source.buffer = this.buffers_[sound]; | 270 source.buffer = this.buffers_[sound]; |
| 275 | 271 |
| 276 if (!opt_properties) { | 272 if (!opt_properties) { |
| 277 // This typecast looks silly, but the Closure compiler doesn't support | 273 // This typecast looks silly, but the Closure compiler doesn't support |
| 278 // optional fields in record types very well so this is the shortest hack. | 274 // optional fields in record types very well so this is the shortest hack. |
| 279 opt_properties = /** @type {undefined} */({}); | 275 opt_properties = /** @type {undefined} */ ({}); |
| 280 } | 276 } |
| 281 | 277 |
| 282 var pitch = this.masterPitch; | 278 var pitch = this.masterPitch; |
| 283 if (opt_properties.pitch) { | 279 if (opt_properties.pitch) { |
| 284 pitch += opt_properties.pitch; | 280 pitch += opt_properties.pitch; |
| 285 } | 281 } |
| 286 if (pitch != 0) { | 282 if (pitch != 0) { |
| 287 source.playbackRate.value = Math.pow(EarconEngine.HALF_STEP, pitch); | 283 source.playbackRate.value = Math.pow(EarconEngine.HALF_STEP, pitch); |
| 288 } | 284 } |
| 289 | 285 |
| (...skipping 30 matching lines...) Expand all Loading... |
| 320 EarconEngine.prototype.onButton = function() { | 316 EarconEngine.prototype.onButton = function() { |
| 321 this.play('static', {gain: this.clickVolume}); | 317 this.play('static', {gain: this.clickVolume}); |
| 322 this.play(this.controlSound); | 318 this.play(this.controlSound); |
| 323 }; | 319 }; |
| 324 | 320 |
| 325 /** | 321 /** |
| 326 * Play the text field sound. | 322 * Play the text field sound. |
| 327 */ | 323 */ |
| 328 EarconEngine.prototype.onTextField = function() { | 324 EarconEngine.prototype.onTextField = function() { |
| 329 this.play('static', {gain: this.clickVolume}); | 325 this.play('static', {gain: this.clickVolume}); |
| 330 this.play('static', {time: this.baseDelay * 1.5, | 326 this.play( |
| 331 gain: this.clickVolume * 0.5}); | 327 'static', {time: this.baseDelay * 1.5, gain: this.clickVolume * 0.5}); |
| 332 this.play(this.controlSound, {pitch: 4}); | 328 this.play(this.controlSound, {pitch: 4}); |
| 333 this.play(this.controlSound, | 329 this.play( |
| 334 {pitch: 4, | 330 this.controlSound, {pitch: 4, time: this.baseDelay * 1.5, gain: 0.5}); |
| 335 time: this.baseDelay * 1.5, | |
| 336 gain: 0.5}); | |
| 337 }; | 331 }; |
| 338 | 332 |
| 339 /** | 333 /** |
| 340 * Play the pop up button sound. | 334 * Play the pop up button sound. |
| 341 */ | 335 */ |
| 342 EarconEngine.prototype.onPopUpButton = function() { | 336 EarconEngine.prototype.onPopUpButton = function() { |
| 343 this.play('static', {gain: this.clickVolume}); | 337 this.play('static', {gain: this.clickVolume}); |
| 344 | 338 |
| 345 this.play(this.controlSound); | 339 this.play(this.controlSound); |
| 346 this.play(this.controlSound, | 340 this.play( |
| 347 {time: this.baseDelay * 3, | 341 this.controlSound, {time: this.baseDelay * 3, gain: 0.2, pitch: 12}); |
| 348 gain: 0.2, | 342 this.play( |
| 349 pitch: 12}); | 343 this.controlSound, {time: this.baseDelay * 4.5, gain: 0.2, pitch: 12}); |
| 350 this.play(this.controlSound, | |
| 351 {time: this.baseDelay * 4.5, | |
| 352 gain: 0.2, | |
| 353 pitch: 12}); | |
| 354 }; | 344 }; |
| 355 | 345 |
| 356 /** | 346 /** |
| 357 * Play the check on sound. | 347 * Play the check on sound. |
| 358 */ | 348 */ |
| 359 EarconEngine.prototype.onCheckOn = function() { | 349 EarconEngine.prototype.onCheckOn = function() { |
| 360 this.play('static', {gain: this.clickVolume}); | 350 this.play('static', {gain: this.clickVolume}); |
| 361 this.play(this.controlSound, {pitch: -5}); | 351 this.play(this.controlSound, {pitch: -5}); |
| 362 this.play(this.controlSound, {pitch: 7, time: this.baseDelay * 2}); | 352 this.play(this.controlSound, {pitch: 7, time: this.baseDelay * 2}); |
| 363 }; | 353 }; |
| (...skipping 16 matching lines...) Expand all Loading... |
| 380 this.play(this.controlSound, {time: this.baseDelay}); | 370 this.play(this.controlSound, {time: this.baseDelay}); |
| 381 this.play(this.controlSound, {time: this.baseDelay * 2}); | 371 this.play(this.controlSound, {time: this.baseDelay * 2}); |
| 382 }; | 372 }; |
| 383 | 373 |
| 384 /** | 374 /** |
| 385 * Play the slider sound. | 375 * Play the slider sound. |
| 386 */ | 376 */ |
| 387 EarconEngine.prototype.onSlider = function() { | 377 EarconEngine.prototype.onSlider = function() { |
| 388 this.play('static', {gain: this.clickVolume}); | 378 this.play('static', {gain: this.clickVolume}); |
| 389 this.play(this.controlSound); | 379 this.play(this.controlSound); |
| 390 this.play(this.controlSound, | 380 this.play(this.controlSound, {time: this.baseDelay, gain: 0.5, pitch: 2}); |
| 391 {time: this.baseDelay, | 381 this.play( |
| 392 gain: 0.5, | 382 this.controlSound, {time: this.baseDelay * 2, gain: 0.25, pitch: 4}); |
| 393 pitch: 2}); | 383 this.play( |
| 394 this.play(this.controlSound, | 384 this.controlSound, {time: this.baseDelay * 3, gain: 0.125, pitch: 6}); |
| 395 {time: this.baseDelay * 2, | 385 this.play( |
| 396 gain: 0.25, | 386 this.controlSound, {time: this.baseDelay * 4, gain: 0.0625, pitch: 8}); |
| 397 pitch: 4}); | |
| 398 this.play(this.controlSound, | |
| 399 {time: this.baseDelay * 3, | |
| 400 gain: 0.125, | |
| 401 pitch: 6}); | |
| 402 this.play(this.controlSound, | |
| 403 {time: this.baseDelay * 4, | |
| 404 gain: 0.0625, | |
| 405 pitch: 8}); | |
| 406 }; | 387 }; |
| 407 | 388 |
| 408 /** | 389 /** |
| 409 * Play the skim sound. | 390 * Play the skim sound. |
| 410 */ | 391 */ |
| 411 EarconEngine.prototype.onSkim = function() { | 392 EarconEngine.prototype.onSkim = function() { |
| 412 this.play('skim'); | 393 this.play('skim'); |
| 413 }; | 394 }; |
| 414 | 395 |
| 415 /** | 396 /** |
| (...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 473 // | 454 // |
| 474 // If an end frequency is specified, do an exponential ramp to that end | 455 // If an end frequency is specified, do an exponential ramp to that end |
| 475 // frequency. | 456 // frequency. |
| 476 var gain = properties.gain; | 457 var gain = properties.gain; |
| 477 for (var i = 0; i < properties.overtones; i++) { | 458 for (var i = 0; i < properties.overtones; i++) { |
| 478 var osc = this.context_.createOscillator(); | 459 var osc = this.context_.createOscillator(); |
| 479 osc.frequency.value = properties.freq * (i + 1); | 460 osc.frequency.value = properties.freq * (i + 1); |
| 480 | 461 |
| 481 if (properties.endFreq) { | 462 if (properties.endFreq) { |
| 482 osc.frequency.setValueAtTime( | 463 osc.frequency.setValueAtTime( |
| 483 properties.freq * (i + 1), | 464 properties.freq * (i + 1), this.context_.currentTime + time); |
| 484 this.context_.currentTime + time); | |
| 485 osc.frequency.exponentialRampToValueAtTime( | 465 osc.frequency.exponentialRampToValueAtTime( |
| 486 properties.endFreq * (i + 1), | 466 properties.endFreq * (i + 1), |
| 487 this.context_.currentTime + properties.dur); | 467 this.context_.currentTime + properties.dur); |
| 488 } | 468 } |
| 489 | 469 |
| 490 osc.start(this.context_.currentTime + time); | 470 osc.start(this.context_.currentTime + time); |
| 491 osc.stop(this.context_.currentTime + time + properties.dur); | 471 osc.stop(this.context_.currentTime + time + properties.dur); |
| 492 | 472 |
| 493 var gainNode = this.context_.createGain(); | 473 var gainNode = this.context_.createGain(); |
| 494 gainNode.gain.value = gain; | 474 gainNode.gain.value = gain; |
| 495 osc.connect(gainNode); | 475 osc.connect(gainNode); |
| 496 gainNode.connect(envelopeNode); | 476 gainNode.connect(envelopeNode); |
| 497 | 477 |
| 498 gain *= properties.overtoneFactor; | 478 gain *= properties.overtoneFactor; |
| 499 } | 479 } |
| 500 | 480 |
| 501 // Shape the overall sound by an envelope based on the attack and | 481 // Shape the overall sound by an envelope based on the attack and |
| 502 // decay times. | 482 // decay times. |
| 503 envelopeNode.gain.setValueAtTime(0, this.context_.currentTime + time); | 483 envelopeNode.gain.setValueAtTime(0, this.context_.currentTime + time); |
| 504 envelopeNode.gain.linearRampToValueAtTime( | 484 envelopeNode.gain.linearRampToValueAtTime( |
| 505 1, this.context_.currentTime + time + properties.attack); | 485 1, this.context_.currentTime + time + properties.attack); |
| 506 envelopeNode.gain.setValueAtTime( | 486 envelopeNode.gain.setValueAtTime( |
| 507 1, this.context_.currentTime + time + | 487 1, this.context_.currentTime + time + properties.dur - properties.decay); |
| 508 properties.dur - properties.decay); | |
| 509 envelopeNode.gain.linearRampToValueAtTime( | 488 envelopeNode.gain.linearRampToValueAtTime( |
| 510 0, this.context_.currentTime + time + properties.dur); | 489 0, this.context_.currentTime + time + properties.dur); |
| 511 | 490 |
| 512 // Route everything through the common filters like reverb at the end. | 491 // Route everything through the common filters like reverb at the end. |
| 513 var destination = this.createCommonFilters({}); | 492 var destination = this.createCommonFilters({}); |
| 514 envelopeNode.connect(destination); | 493 envelopeNode.connect(destination); |
| 515 }; | 494 }; |
| 516 | 495 |
| 517 /** | 496 /** |
| 518 * Play a sweep over a bunch of notes in a scale, with an echo, | 497 * Play a sweep over a bunch of notes in a scale, with an echo, |
| (...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 593 EarconEngine.prototype.onChromeVoxOff = function() { | 572 EarconEngine.prototype.onChromeVoxOff = function() { |
| 594 this.onChromeVoxSweep(true); | 573 this.onChromeVoxSweep(true); |
| 595 }; | 574 }; |
| 596 | 575 |
| 597 /** | 576 /** |
| 598 * Play an alert sound. | 577 * Play an alert sound. |
| 599 */ | 578 */ |
| 600 EarconEngine.prototype.onAlert = function() { | 579 EarconEngine.prototype.onAlert = function() { |
| 601 var freq1 = 220 * Math.pow(EarconEngine.HALF_STEP, this.alertPitch - 2); | 580 var freq1 = 220 * Math.pow(EarconEngine.HALF_STEP, this.alertPitch - 2); |
| 602 var freq2 = 220 * Math.pow(EarconEngine.HALF_STEP, this.alertPitch - 3); | 581 var freq2 = 220 * Math.pow(EarconEngine.HALF_STEP, this.alertPitch - 3); |
| 603 this.generateSinusoidal({attack: 0.02, | 582 this.generateSinusoidal({ |
| 604 decay: 0.07, | 583 attack: 0.02, |
| 605 dur: 0.15, | 584 decay: 0.07, |
| 606 gain: 0.3, | 585 dur: 0.15, |
| 607 freq: freq1, | 586 gain: 0.3, |
| 608 overtones: 3, | 587 freq: freq1, |
| 609 overtoneFactor: 0.1}); | 588 overtones: 3, |
| 610 this.generateSinusoidal({attack: 0.02, | 589 overtoneFactor: 0.1 |
| 611 decay: 0.07, | 590 }); |
| 612 dur: 0.15, | 591 this.generateSinusoidal({ |
| 613 gain: 0.3, | 592 attack: 0.02, |
| 614 freq: freq2, | 593 decay: 0.07, |
| 615 overtones: 3, | 594 dur: 0.15, |
| 616 overtoneFactor: 0.1}); | 595 gain: 0.3, |
| 596 freq: freq2, |
| 597 overtones: 3, |
| 598 overtoneFactor: 0.1 |
| 599 }); |
| 617 }; | 600 }; |
| 618 | 601 |
| 619 /** | 602 /** |
| 620 * Play a wrap sound. | 603 * Play a wrap sound. |
| 621 */ | 604 */ |
| 622 EarconEngine.prototype.onWrap = function() { | 605 EarconEngine.prototype.onWrap = function() { |
| 623 this.play('static', {gain: this.clickVolume * 0.3}); | 606 this.play('static', {gain: this.clickVolume * 0.3}); |
| 624 var freq1 = 220 * Math.pow(EarconEngine.HALF_STEP, this.wrapPitch - 8); | 607 var freq1 = 220 * Math.pow(EarconEngine.HALF_STEP, this.wrapPitch - 8); |
| 625 var freq2 = 220 * Math.pow(EarconEngine.HALF_STEP, this.wrapPitch + 8); | 608 var freq2 = 220 * Math.pow(EarconEngine.HALF_STEP, this.wrapPitch + 8); |
| 626 this.generateSinusoidal({attack: 0.01, | 609 this.generateSinusoidal({ |
| 627 decay: 0.1, | 610 attack: 0.01, |
| 628 dur: 0.15, | 611 decay: 0.1, |
| 629 gain: 0.3, | 612 dur: 0.15, |
| 630 freq: freq1, | 613 gain: 0.3, |
| 631 endFreq: freq2, | 614 freq: freq1, |
| 632 overtones: 1, | 615 endFreq: freq2, |
| 633 overtoneFactor: 0.1}); | 616 overtones: 1, |
| 617 overtoneFactor: 0.1 |
| 618 }); |
| 634 }; | 619 }; |
| 635 | 620 |
| 636 /** | 621 /** |
| 637 * Queue up a few tick/tock sounds for a progress bar. This is called | 622 * Queue up a few tick/tock sounds for a progress bar. This is called |
| 638 * repeatedly by setInterval to keep the sounds going continuously. | 623 * repeatedly by setInterval to keep the sounds going continuously. |
| 639 * @private | 624 * @private |
| 640 */ | 625 */ |
| 641 EarconEngine.prototype.generateProgressTickTocks_ = function() { | 626 EarconEngine.prototype.generateProgressTickTocks_ = function() { |
| 642 while (this.progressTime_ < this.context_.currentTime + 3.0) { | 627 while (this.progressTime_ < this.context_.currentTime + 3.0) { |
| 643 var t = this.progressTime_ - this.context_.currentTime; | 628 var t = this.progressTime_ - this.context_.currentTime; |
| 644 this.progressSources_.push( | 629 this.progressSources_.push([ |
| 645 [this.progressTime_, | 630 this.progressTime_, |
| 646 this.play('static', | 631 this.play('static', {gain: 0.5 * this.progressGain_, time: t}) |
| 647 {gain: 0.5 * this.progressGain_, | 632 ]); |
| 648 time: t})]); | 633 this.progressSources_.push([ |
| 649 this.progressSources_.push( | 634 this.progressTime_, |
| 650 [this.progressTime_, | 635 this.play( |
| 651 this.play(this.controlSound, | 636 this.controlSound, {pitch: 20, time: t, gain: this.progressGain_}) |
| 652 {pitch: 20, | 637 ]); |
| 653 time: t, | |
| 654 gain: this.progressGain_})]); | |
| 655 | 638 |
| 656 if (this.progressGain_ > this.progressFinalGain) { | 639 if (this.progressGain_ > this.progressFinalGain) { |
| 657 this.progressGain_ *= this.progressGain_Decay; | 640 this.progressGain_ *= this.progressGain_Decay; |
| 658 } | 641 } |
| 659 t += 0.5; | 642 t += 0.5; |
| 660 | 643 |
| 661 this.progressSources_.push( | 644 this.progressSources_.push([ |
| 662 [this.progressTime_, | 645 this.progressTime_, |
| 663 this.play('static', | 646 this.play('static', {gain: 0.5 * this.progressGain_, time: t}) |
| 664 {gain: 0.5 * this.progressGain_, | 647 ]); |
| 665 time: t})]); | 648 this.progressSources_.push([ |
| 666 this.progressSources_.push( | 649 this.progressTime_, |
| 667 [this.progressTime_, | 650 this.play( |
| 668 this.play(this.controlSound, | 651 this.controlSound, {pitch: 8, time: t, gain: this.progressGain_}) |
| 669 {pitch: 8, | 652 ]); |
| 670 time: t, | |
| 671 gain: this.progressGain_})]); | |
| 672 | 653 |
| 673 if (this.progressGain_ > this.progressFinalGain) { | 654 if (this.progressGain_ > this.progressFinalGain) { |
| 674 this.progressGain_ *= this.progressGain_Decay; | 655 this.progressGain_ *= this.progressGain_Decay; |
| 675 } | 656 } |
| 676 | 657 |
| 677 this.progressTime_ += 1.0; | 658 this.progressTime_ += 1.0; |
| 678 } | 659 } |
| 679 | 660 |
| 680 var removeCount = 0; | 661 var removeCount = 0; |
| 681 while (removeCount < this.progressSources_.length && | 662 while (removeCount < this.progressSources_.length && |
| 682 this.progressSources_[removeCount][0] < this.context_.currentTime - 0.2) { | 663 this.progressSources_[removeCount][0] < |
| 664 this.context_.currentTime - 0.2) { |
| 683 removeCount++; | 665 removeCount++; |
| 684 } | 666 } |
| 685 this.progressSources_.splice(0, removeCount); | 667 this.progressSources_.splice(0, removeCount); |
| 686 }; | 668 }; |
| 687 | 669 |
| 688 /** | 670 /** |
| 689 * Start playing tick / tock progress sounds continuously until | 671 * Start playing tick / tock progress sounds continuously until |
| 690 * explicitly canceled. | 672 * explicitly canceled. |
| 691 */ | 673 */ |
| 692 EarconEngine.prototype.startProgress = function() { | 674 EarconEngine.prototype.startProgress = function() { |
| 693 if (this.progressIntervalID_) { | 675 if (this.progressIntervalID_) { |
| 694 this.cancelProgress(); | 676 this.cancelProgress(); |
| 695 } | 677 } |
| 696 | 678 |
| 697 this.progressSources_ = []; | 679 this.progressSources_ = []; |
| 698 this.progressGain_ = 0.5; | 680 this.progressGain_ = 0.5; |
| 699 this.progressTime_ = this.context_.currentTime; | 681 this.progressTime_ = this.context_.currentTime; |
| 700 this.generateProgressTickTocks_(); | 682 this.generateProgressTickTocks_(); |
| 701 this.progressIntervalID_ = window.setInterval( | 683 this.progressIntervalID_ = |
| 702 this.generateProgressTickTocks_.bind(this), 1000); | 684 window.setInterval(this.generateProgressTickTocks_.bind(this), 1000); |
| 703 }; | 685 }; |
| 704 | 686 |
| 705 /** | 687 /** |
| 706 * Stop playing any tick / tock progress sounds. | 688 * Stop playing any tick / tock progress sounds. |
| 707 */ | 689 */ |
| 708 EarconEngine.prototype.cancelProgress = function() { | 690 EarconEngine.prototype.cancelProgress = function() { |
| 709 if (!this.progressIntervalID_) { | 691 if (!this.progressIntervalID_) { |
| 710 return; | 692 return; |
| 711 } | 693 } |
| 712 | 694 |
| (...skipping 16 matching lines...) Expand all Loading... |
| 729 | 711 |
| 730 // Clamp. | 712 // Clamp. |
| 731 x = Math.min(Math.max(x, 0.0), 1.0); | 713 x = Math.min(Math.max(x, 0.0), 1.0); |
| 732 | 714 |
| 733 // Map to between the negative maximum pan x position and the positive max x | 715 // Map to between the negative maximum pan x position and the positive max x |
| 734 // pan position. | 716 // pan position. |
| 735 x = (2 * x - 1) * EarconEngine.MAX_PAN_ABS_X_POSITION; | 717 x = (2 * x - 1) * EarconEngine.MAX_PAN_ABS_X_POSITION; |
| 736 | 718 |
| 737 this.masterPan = x; | 719 this.masterPan = x; |
| 738 }; | 720 }; |
| OLD | NEW |