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 |