1 /**
  2  * The MIT License (MIT)
  3  *
  4  * Copyright (c) 2016 DeNA Co., Ltd.
  5  *
  6  * Permission is hereby granted, free of charge, to any person obtaining a copy
  7  * of this software and associated documentation files (the "Software"), to deal
  8  * in the Software without restriction, including without limitation the rights
  9  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 10  * copies of the Software, and to permit persons to whom the Software is
 11  * furnished to do so, subject to the following conditions:
 12  *
 13  * The above copyright notice and this permission notice shall be included in
 14  * all copies or substantial portions of the Software.
 15  *
 16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 19  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 22  * SOFTWARE.
 23  */
 24 
 25 /// <reference path="base.js"/>
 26 /// <reference path="event_dispatcher.js"/>
 27 /// <reference path="loader.js"/>
 28 /// <reference path="user_agent.js"/>
 29 /// <reference path="../externs/webaudio.js"/>
 30 
 31 /**
 32  * A class that plays sound.
 33  * @extends {createjs.EventDispatcher}
 34  * @constructor
 35  */
 36 createjs.Sound = function() {
 37   createjs.EventDispatcher.call(this);
 38 
 39   /**
 40    * The sound players.
 41    * @type {Object.<string,createjs.Sound.Player>}
 42    * @private
 43    */
 44   this.players_ = {};
 45 };
 46 createjs.inherits('Sound', createjs.Sound, createjs.EventDispatcher);
 47 
 48 /**
 49  * The instance of the createjs.Sound object.
 50  * @type {createjs.Sound}
 51  * @private
 52  */
 53 createjs.Sound.instance_ = null;
 54 
 55 /**
 56  * The character (or characters) that are used to split multiple paths from an
 57  * audio source.
 58  * @const {string}
 59  */
 60 createjs.Sound.DELIMITER = '|';
 61 
 62 /**
 63  * The interrupt value to interrupt any currently playing instance with the same
 64  * source, if the maximum number of instances of the sound are already playing.
 65  * @const {number}
 66  */
 67 createjs.Sound.INTERRUPT_ANY = 0;  // 'any'
 68 
 69 /**
 70  * The interrupt value to interrupt the earliest currently playing instance with
 71  * the same source that progressed the least distance in the audio track, if the
 72  * maximum number of instances of the sound are already playing.
 73  * @const {number}
 74  */
 75 createjs.Sound.INTERRUPT_EARLY = 1;  // 'early';
 76 
 77 /**
 78  * The interrupt value to interrupt the currently playing instance with the same
 79  * source that progressed the most distance in the audio track, if the maximum
 80  * number of instances of the sound are already playing.
 81  * @const {number}
 82  */
 83 createjs.Sound.INTERRUPT_LATE = 2;  // 'late';
 84 
 85 /**
 86  * The interrupt value to not interrupt any currently playing instances with the
 87  * same source, if the maximum number of instances of the sound are already
 88  * playing.
 89  * @const {number}
 90  */
 91 createjs.Sound.INTERRUPT_NONE = 3;  // 'none';
 92 
 93 /**
 94  * Defines the playState of an instance that is still initializing.
 95  * @const {number}
 96  */
 97 createjs.Sound.PLAY_INITED = 0;  // 'playInited';
 98 
 99 /**
100  * Defines the playState of an instance that is currently playing or paused.
101  * @const {number}
102  */
103 createjs.Sound.PLAY_SUCCEEDED = 2;  // 'playSucceeded';
104 
105 /**
106  * Defines the playState of an instance that was interrupted by another instance.
107  * @const {number}
108  */
109 createjs.Sound.PLAY_INTERRUPTED = 3;  // 'playInterrupted';
110 
111 /**
112  * Defines the playState of an instance that completed playback.
113  * @const {number}
114  */
115 createjs.Sound.PLAY_FINISHED = 4;  // 'playFinished';
116 
117 /**
118  * Defines the playState of an instance that failed to play.
119  * @const {number}
120  */
121 createjs.Sound.PLAY_FAILED = 5;  // 'playFailed';
122 
123 /**
124  * Returns the global instance of the createjs.Sound object.
125  * @return {createjs.Sound}
126  */
127 createjs.Sound.getInstance_ = function() {
128   /// <returns type="createjs.Sound"/>
129   if (!createjs.Sound.instance_) {
130     createjs.Sound.instance_ = new createjs.Sound();
131   }
132   return createjs.Sound.instance_;
133 };
134 
135 /**
136  * The features supported by this module.
137  * @type {Object.<string,number>}
138  * @private
139  */
140 createjs.Sound.prototype.capabilities_ = {
141   'panning': 1,
142   'volume': 1,
143   'mp3': 1,
144   'mpeg': 1,
145   'm4a': 1,
146   'mp4': 1,
147   'ogg': 0,
148   'wav': 0
149 };
150 
151 /**
152  * An inner interface that provides methods for the original SoundJS plug-ins.
153  * @interface
154  */
155 createjs.Sound.Plugin = function() {};
156 
157 /**
158  * An inner class that emulates the original createjs.HTMLAudioPlugin class.
159  * @implements {createjs.Sound.Plugin}
160  * @constructor
161  */
162 createjs.HTMLAudioPlugin = function() {
163 };
164 
165 // Export the createjs.HTMLAudioPlugin object to the global namespace.
166 createjs.exportObject('createjs.HTMLAudioPlugin', createjs.HTMLAudioPlugin, {
167 }, {
168   'enableIOS': true
169 });
170 
171 /**
172  * An inner class that emulates the original createjs.WebAudioPlugin class.
173  * @implements {createjs.Sound.Plugin}
174  * @constructor
175  */
176 createjs.WebAudioPlugin = function() {
177 };
178 
179 /**
180  * Whether this plug-in is initialized.
181  * @type {boolean}
182  * @private
183  */
184 createjs.WebAudioPlugin.initialized_ = false;
185 
186 /**
187  * An HTMLAudioElement object that plays a dummy sound.
188  * @type {HTMLAudioElement}
189  * @private
190  */
191 createjs.WebAudioPlugin.audio_ = null;
192 
193 /**
194  * The AudioContext instance used by this plug-in.
195  * @type {AudioContext}
196  * @private
197  */
198 createjs.WebAudioPlugin['context'] = null;
199 
200 /**
201  * The node that plays an empty sound.
202  * @type {AudioBufferSourceNode}
203  * @private
204  */
205 createjs.WebAudioPlugin.source_ = null;
206 
207 /**
208  * The 1-sample audio buffer representing an empty sound.
209  * @type {AudioBuffer}
210  * @private
211  */
212 createjs.WebAudioPlugin.buffer_ = null;
213 
214 /**
215  * Retrieves the audio context.
216  * @return {AudioContext} context
217  * @private
218  */
219 createjs.WebAudioPlugin.getContext_ = function() {
220   /// <returns type="AudioContext"/>
221   if (!createjs.WebAudioPlugin.initialized_) {
222     createjs.WebAudioPlugin.initialized_ = true;
223     if (createjs.AudioContext) {
224       // Attach a dummy object to the 'createjs.WebAudioPlugin.context' property
225       // when the createjs.Sound class uses FrameAudioPlayer objects (i.e. when
226       // it uses an <iframe> element to play sounds) to avoid creating an
227       // unnecessary AudioContent object.
228       if (createjs.Config.useFrame()) {
229         createjs.WebAudioPlugin['context'] = {
230           'currentTime': -1
231         };
232       } else {
233         var context = new createjs.AudioContext();
234         createjs.WebAudioPlugin['context'] = context;
235       }
236     }
237   }
238   return createjs.WebAudioPlugin['context'];
239 };
240 
241 /**
242  * Plays a dummy sound. This method creates an empty <audio> element and plays
243  * it. This method is a workaround for old iPhones, which need to have an
244  * <audio> element played to play sounds even with Web Audio.
245  * @private
246  */
247 createjs.WebAudioPlugin.playDummySound_ = function() {
248   if (!createjs.WebAudioPlugin.audio_) {
249     createjs.WebAudioPlugin.audio_ =
250         /** @type {HTMLAudioElement} */ (document.createElement('audio'));
251   }
252   createjs.WebAudioPlugin.audio_.play();
253 };
254 
255 /**
256  * Resets this plug-in to its initial state.
257  * @private
258  */
259 createjs.WebAudioPlugin.reset_ = function() {
260   createjs.WebAudioPlugin.stopEmptySound_();
261   createjs.WebAudioPlugin.audio_ = null;
262   createjs.WebAudioPlugin['context'] = null;
263   createjs.WebAudioPlugin.initialized_ = false;
264 };
265 
266 /**
267  * Stops playing an empty sound. This method stops playing an empty sound played
268  * by the 'createjs.WebAudioPlugin.playEmptySound()' method and destroys all its
269  * resources.
270  * @private
271  */
272 createjs.WebAudioPlugin.stopEmptySound_ = function() {
273   var source = createjs.WebAudioPlugin.source_;
274   if (source) {
275     if (source.stop) {
276       source.stop(0);
277     } else {
278       source.noteOff(0);
279     }
280     source.disconnect(0);
281     createjs.WebAudioPlugin.source_ = null;
282   }
283 };
284 
285 /**
286  * Plays an empty sound. This method plays an empty sound both with an <audio>
287  * element and with Web Audio. (This method does not play any sounds when this
288  * module uses an <iframe> element, which automatically plays an empty sound.)
289  * @const
290  */
291 createjs.WebAudioPlugin.playEmptySound = function() {
292   var context = createjs.WebAudioPlugin.getContext_();
293   if (context) {
294     // Abort playing empty sounds when the createjs.Sound class uses an <iframe>
295     // element to play sounds. (The returned AudioContent object is a dummy
296     // object and cannot play sounds.)
297     if (createjs.Config.useFrame()) {
298       return;
299     }
300     // An application may call this method before this method finishes playing
301     // the previous sound. To avoid node leaks, this method stops playing the
302     // previous sound, destroys its node, and plays a new one.
303     if (createjs.WebAudioPlugin.source_) {
304       createjs.WebAudioPlugin.stopEmptySound_();
305     }
306     var source = context.createBufferSource();
307     createjs.WebAudioPlugin.source_ = source;
308     if (!createjs.WebAudioPlugin.buffer_) {
309       createjs.WebAudioPlugin.buffer_ = context.createBuffer(1, 1, 22500);
310     }
311     source.buffer = createjs.WebAudioPlugin.buffer_;
312     source.onended = createjs.WebAudioPlugin.stopEmptySound_;
313     if (source.start) {
314       source.start(0);
315     } else {
316       source.noteOn(0);
317     }
318   }
319   createjs.WebAudioPlugin.playDummySound_();
320 };
321 
322 // Export the createjs.WebAudioPlugin object to the global namespace.
323 createjs.exportObject('createjs.WebAudioPlugin', createjs.WebAudioPlugin, {
324 }, {
325   'playEmptySound': createjs.WebAudioPlugin.playEmptySound
326 });
327 
328 /**
329  * The base class that plays audio.
330  * @param {createjs.Loader.Item} item
331  * @extends {createjs.EventDispatcher}
332  * @implements {createjs.Loader.Item.Listener}
333  * @constructor
334  */
335 createjs.Sound.Player = function(item) {
336   /// <param type="cretejs.Loader.Item" name="item"/>
337   /**
338    * @type {createjs.Loader.Item}
339    * @protected
340    */
341   this.item = item;
342 };
343 createjs.inherits(
344     'Sound.Player', createjs.Sound.Player, createjs.EventDispatcher);
345 
346 /**
347  * @enum {number}
348  * @private
349  */
350 createjs.Sound.Player.Event = {
351   CANPLAY: 1 << 0,
352   SEEKED: 1 << 1,
353   ENDED: 1 << 2,
354   TOUCH: 1 << 3
355 };
356 
357 /**
358  * The current state of this player.
359  * @type {number}
360  */
361 createjs.Sound.Player.prototype['playState'] = createjs.Sound.PLAY_FAILED;
362 
363 /**
364  * Whether this player repeats playing a sound portion.
365  * @type {number}
366  * @protected
367  */
368 createjs.Sound.Player.prototype.loop = 0;
369 
370 /**
371  * The start position of a sound portion to be played by this player in seconds.
372  * @type {number}
373  * @protected
374  */
375 createjs.Sound.Player.prototype.offset = 0;
376 
377 /**
378  * The duration of a sound portion to be played by this player in seconds.
379  * (This value is not always equal to the duration of the sound.)
380  * @type {number}
381  * @protected
382  */
383 createjs.Sound.Player.prototype.duration = 0;
384 
385 /**
386  * The volume for this sound player.
387  * @type {number}
388  * @protected
389  */
390 createjs.Sound.Player.prototype.volume = 1;
391 
392 /**
393  * Plays audio. This method initializes internal variables and decodes audio
394  * data. Even though the createjs.Loader object preloads audio data, this player
395  * may wait until a browser finishes decoding it, i.e. this method does not
396  * always plays audio immediately.
397  * @param {number} interrupt
398  * @param {number} delay
399  * @param {number} offset
400  * @param {number} loop
401  * @param {number} volume
402  * @param {number} pan
403  * @private
404  */
405 createjs.Sound.Player.prototype.play_ =
406     function(interrupt, delay, offset, loop, volume, pan) {
407   /// <param type="number" name="interrupt"/>
408   /// <param type="number" name="delay"/>
409   /// <param type="number" name="offset"/>
410   /// <param type="number" name="loop"/>
411   /// <param type="number" name="volume"/>
412   /// <param type="number" name="pan"/>
413   if (loop != null) {
414     this.loop = loop;
415   }
416   if (offset >= 0) {
417     this.offset = offset;
418   }
419   if (volume != null) {
420     this.volume = volume;
421   }
422   this['playState'] = createjs.Sound.PLAY_SUCCEEDED;
423 };
424 
425 /**
426  * Stops playing audio.
427  * @private
428  */
429 createjs.Sound.Player.prototype.stop_ = function() {
430   this['playState'] = createjs.Sound.PLAY_FINISHED;
431 };
432 
433 /**
434  * Sets the mute state.
435  * @param {boolean} mute
436  * @private
437  */
438 createjs.Sound.Player.prototype.setMute_ = function(mute) {
439   /// <param type="boolean" name="mute"/>
440 };
441 
442 /**
443  * Sets the sound volume.
444  * @param {number} volume
445  * @private
446  */
447 createjs.Sound.Player.prototype.setVolume_ = function(volume) {
448   /// <param type="number" name="volume"/>
449 };
450 
451 /**
452  * Initializes this sound as an audio sprite.
453  * @param {string} id
454  * @param {number} offset
455  * @param {number} duration
456  * @private
457  */
458 createjs.Sound.Player.prototype.setSprite_ = function(id, offset, duration) {
459   /// <param type="string" name="id"/>
460   /// <param type="number" name="offset"/>
461   /// <param type="number" name="duration"/>
462 };
463 
464 /** @override */
465 createjs.Sound.Player.prototype.handleLoad = function(loader, buffer) {
466   /// <param type="createjs.Loader" name="loader"/>
467   /// <param type="ArrayBuffer" name="buffer"/>
468   return true;
469 };
470 
471 // Export the methods used by applications.
472 createjs.Sound.Player.prototype['play'] = createjs.notImplemented;
473 createjs.Sound.Player.prototype['stop'] = createjs.notImplemented;
474 createjs.Sound.Player.prototype['setMute'] = createjs.notImplemented;
475 createjs.Sound.Player.prototype['setVolume'] = createjs.notImplemented;
476 
477 /**
478  * A class that implements the createjs.Sound.Player interface with the
479  * HTMLAudioElement interface.
480  * @param {createjs.Loader.Item} item
481  * @extends {createjs.Sound.Player}
482  * @implements {EventListener}
483  * @constructor
484  */
485 createjs.Sound.HTMLAudioPlayer = function(item) {
486   /// <param type="cretejs.Loader.Item" name="item"/>
487   createjs.Sound.Player.call(this, item);
488 };
489 createjs.inherits('Sound.HTMLAudioPlayer',
490                   createjs.Sound.HTMLAudioPlayer,
491                   createjs.Sound.Player);
492 
493 /**
494  * @type {number}
495  * @private
496  */
497 createjs.Sound.HTMLAudioPlayer.prototype.mask_ = 0;
498 
499 /**
500  * @type {boolean}
501  * @private
502  */
503 createjs.Sound.HTMLAudioPlayer.prototype.paused_ = true;
504 
505 /**
506  * The timer ID. This value tells this player has already set a timer.
507  * @type {number}
508  * @private
509  */
510 createjs.Sound.HTMLAudioPlayer.prototype.timer_ = 0;
511 
512 /**
513  * Starts listening an event from the specified HTMLAudioElement object.
514  * @param {EventTarget} audio
515  * @param {string} type
516  * @param {number} id
517  * @private
518  */
519 createjs.Sound.HTMLAudioPlayer.prototype.listen_ = function(audio, type, id) {
520   /// <param type="EventTarget" name="audio"/>
521   /// <param type="string" name="type"/>
522   /// <param type="number" name="id"/>
523   if (!(this.mask_ & id)) {
524     audio.addEventListener(type, this, false);
525     this.mask_ |= id;
526   }
527 };
528 
529 /**
530  * Stops listening an event from the specified HTMLAudioElement object.
531  * @param {EventTarget} audio
532  * @param {string} type
533  * @param {number} id
534  * @private
535  */
536 createjs.Sound.HTMLAudioPlayer.prototype.unlisten_ = function(audio, type, id) {
537   /// <param type="EventTarget" name="audio"/>
538   /// <param type="string" name="type"/>
539   /// <param type="number" name="id"/>
540   audio.removeEventListener(type, this, false);
541   this.mask_ &= ~id;
542 };
543 
544 /**
545  * Called when the hosting browser finishes playing this sound.
546  * @private
547  */
548 createjs.Sound.HTMLAudioPlayer.prototype.handleEnded_ = function() {
549   this.timer_ = 0;
550   this.stop_();
551   this.dispatchNotification('complete');
552 };
553 
554 /**
555  * Starts playing audio.
556  * @private
557  */
558 createjs.Sound.HTMLAudioPlayer.prototype.start_ = function() {
559   createjs.assert(!!this.item);
560   if (!this.paused_) {
561     return;
562   }
563   createjs.log('HTMLAudio: play ' + this.item.id);
564   var audio = /** @type {HTMLAudioElement} */ (this.item.resultObject);
565   // Some Android 4.4 WebViews (e.g. Softbank 302SH) stops playing a sound
566   // without dispatching an 'ended' event when the 'loop' property is true. To
567   // repeat a sound on such devices, this method always set false to the 'loop'
568   // property to false and re-start playing when this player receives an 'ended'
569   // event.
570   if (createjs.FALSE) {
571     audio.loop = !!this.loop;
572   }
573   // Set a timer to stop this sound when it expires. (The HTMLAudioElement
574   // class cannot change its duration and this player has to use a timer to
575   // stop it manually.)
576   if (!this.loop && this.duration) {
577     if (this.offset) {
578       audio.currentTime = this.offset;
579     }
580     if (this.timer_) {
581       clearTimeout(this.timer_);
582     }
583     this.timer_ = setTimeout(this.handleEnded_.bind(this), this.duration);
584   }
585 };
586 
587 /**
588  * Adds the specified HTMLAudioElement object to the DOM tree.
589  * @param {HTMLAudioElement} audio
590  * @private
591  */
592 createjs.Sound.HTMLAudioPlayer.prototype.appendChild_ = function(audio) {
593   /// <param type="HTMLAudioElement" name="audio"/>
594   if (!audio.parentNode) {
595     document.body.appendChild(audio);
596   }
597 };
598 
599 /** @override */
600 createjs.Sound.HTMLAudioPlayer.prototype.play_ =
601     function(interrupt, delay, offset, loop, volume, pan) {
602   /// <param type="number" name="interrupt"/>
603   /// <param type="number" name="delay"/>
604   /// <param type="number" name="offset"/>
605   /// <param type="number" name="loop"/>
606   /// <param type="number" name="volume"/>
607   /// <param type="number" name="pan"/>
608   if (!this.paused_) {
609     return;
610   }
611   createjs.Sound.HTMLAudioPlayer.superClass_.play_.call(
612       this, interrupt, delay, offset, loop, volume, pan);
613   var audio = /** @type {HTMLAudioElement} */ (this.item.resultObject);
614   createjs.assert(!!audio);
615   this.appendChild_(audio);
616   // Wait until the specified audio becomes ready to play when it is not.
617   // Otherwise, play the audio now.
618   var HAVE_NOTHING = 0;
619   var HAVE_METADATA = 1;
620   var HAVE_CURRENT_DATA = 2;
621   var HAVE_FUTURE_DATA = 3;
622   var HAVE_ENOUGH_DATA = 4;
623   if (audio.readyState != HAVE_ENOUGH_DATA) {
624     createjs.log('HTMLAudio: load ' + this.item.id);
625     this.listen_(audio, 'canplay', createjs.Sound.Player.Event.CANPLAY);
626     audio.preload = 'auto';
627     audio.load();
628   } else {
629     this.start_();
630   }
631 };
632 
633 /** @override */
634 createjs.Sound.HTMLAudioPlayer.prototype.stop_ = function() {
635   createjs.assert(!!this.item);
636   createjs.log('HTMLAudio: stop ' + this.item.id);
637   var audio = /** @type {HTMLAudioElement} */ (this.item.resultObject);
638   audio.pause();
639   var parent = audio.parentNode;
640   if (parent) {
641     parent.removeChild(audio);
642   }
643   this.unlisten_(audio, 'canplay', createjs.Sound.Player.Event.CANPLAY);
644   this.unlisten_(audio, 'ended', createjs.Sound.Player.Event.ENDED);
645   this.unlisten_(window, 'touchstart', createjs.Sound.Player.Event.TOUCH);
646   this.paused_ = true;
647   createjs.Sound.HTMLAudioPlayer.superClass_.stop_.call(this);
648 };
649 
650 /** @override */
651 createjs.Sound.HTMLAudioPlayer.prototype.setMute_ = function(mute) {
652   /// <param type="boolean" name="mute"/>
653   if (this.item) {
654     var audio = /** @type {HTMLAudioElement} */ (this.item.resultObject);
655     audio.mute = mute;
656   }
657 };
658 
659 /** @override */
660 createjs.Sound.HTMLAudioPlayer.prototype.setVolume_ = function(volume) {
661   /// <param type="number" name="volume"/>
662   if (this.item && this.volume != volume) {
663     this.volume = volume;
664     var audio = /** @type {HTMLAudioElement} */ (this.item.resultObject);
665     audio.volume = volume;
666   }
667 };
668 
669 /** @override */
670 createjs.Sound.HTMLAudioPlayer.prototype.setSprite_ =
671     function(id, offset, duration) {
672   /// <param type="string" name="id"/>
673   /// <param type="number" name="offset"/>
674   /// <param type="number" name="duration"/>
675   // This player uses the specified offset to set the 'currentTime' property
676   // and the specified duration to call a 'setTimeout()' function, i.e. the
677   // offset should be in seconds and the duration should be in milliseconds.
678   this.offset = offset * 0.001;
679   this.duration = duration;
680 };
681 
682 /** @override */
683 createjs.Sound.HTMLAudioPlayer.prototype.handleEvent = function(event) {
684   /// <param type="Event" name="event"/>
685   var type = event.type;
686   var audio = event.target;
687   createjs.log('HTMLAudio: event ' + this.item.id + ',' + type);
688   if (type == 'canplay') {
689     this.unlisten_(audio, type, createjs.Sound.Player.Event.CANPLAY);
690     this.start_();
691   } else if (type == 'ended') {
692     if (this.loop) {
693       audio.play();
694     } else {
695       this.stop_();
696     }
697   } else if (type == 'touchstart') {
698     this.unlisten_(window, type, createjs.Sound.Player.Event.TOUCH);
699     this.start_();
700   }
701 };
702 
703 // Export overridden methods.
704 createjs.Sound.HTMLAudioPlayer.prototype['play'] =
705     createjs.Sound.HTMLAudioPlayer.prototype.play_;
706 createjs.Sound.HTMLAudioPlayer.prototype['stop'] =
707     createjs.Sound.HTMLAudioPlayer.prototype.stop_;
708 createjs.Sound.HTMLAudioPlayer.prototype['setMute'] =
709     createjs.Sound.HTMLAudioPlayer.prototype.setMute_;
710 createjs.Sound.HTMLAudioPlayer.prototype['setVolume'] =
711     createjs.Sound.HTMLAudioPlayer.prototype.setVolume_;
712 
713 /**
714  * A class that implements the createjs.Sound.Player interface with the WebAudio
715  * API without using HTMLAudioElement objects. This player needs to decode ALL
716  * audio data before playing it and it takes more memory than other players.
717  * @param {createjs.Loader.Item} item
718  * @extends {createjs.Sound.Player}
719  * @constructor
720  */
721 createjs.Sound.BufferAudioPlayer = function(item) {
722   /// <param type="cretejs.Loader.Item" name="item"/>
723   createjs.Sound.Player.call(this, item);
724 };
725 createjs.inherits('Sound.BufferAudioPlayer',
726                   createjs.Sound.BufferAudioPlayer,
727                   createjs.Sound.Player);
728 
729 /**
730  * The source node that plays audio with this player.
731  * @type {AudioBufferSourceNode}
732  * @private
733  */
734 createjs.Sound.BufferAudioPlayer.prototype.source_ = null;
735 
736 /**
737  * The gain node that changes the volume of this player.
738  * @type {GainNode}
739  * @private
740  */
741 createjs.Sound.BufferAudioPlayer.prototype.gain_ = null;
742 
743 /**
744  * The createjs.Loader object that waits for this object to decode data.
745  * @type {createjs.Loader}
746  * @private
747  */
748 createjs.Sound.BufferAudioPlayer.prototype.loader_ = null;
749 
750 /**
751  * @type {number}
752  * @private
753  */
754 createjs.Sound.BufferAudioPlayer.prototype.auto_ = 0;
755 
756 /**
757  * Called when the hosting browser finishes playing this sound.
758  * @private
759  */
760 createjs.Sound.BufferAudioPlayer.prototype.handleEnded_ = function() {
761   this.stop_();
762   this.dispatchNotification('complete');
763 };
764 
765 /**
766  * Plays decoded audio.
767  * @param {AudioBuffer} buffer
768  * @private
769  */
770 createjs.Sound.BufferAudioPlayer.prototype.playAudioBuffer_ = function(buffer) {
771   /// <param type="AudioBuffer" name="buffer"/>
772   // Add the following audio graph to the destination node. (This class
773   // multiplies the master volume to the audio volume to connect the gain node
774   // directly to the destination.)
775   //   +---------------+   +------+   +-------------+
776   //   | buffer source |-->| gain |-->| destination |
777   //   +---------------+   +------+   +-------------+
778   createjs.log('WebAudio: play ' + this.item.id);
779   var context = createjs.WebAudioPlugin.getContext_();
780   var gain =
781       context.createGain ? context.createGain() : context.createGainNode();
782   gain.connect(context.destination);
783   gain.gain.value = this.volume;
784   this.gain_ = gain;
785 
786   var source = context.createBufferSource();
787   source.connect(gain);
788   source.buffer = buffer;
789   if (this.loop) {
790     source.loop = true;
791     if (source.start) {
792       source.start(0);
793     } else {
794       source.noteOn(0);
795     }
796   } else {
797     source.onended = this.handleEnded_.bind(this);
798     if (source.start) {
799       source.start(0, this.offset, this.duration);
800     } else {
801       source.noteGrainOn(0, this.offset, this.duration);
802     }
803   }
804   this.source_ = source;
805 };
806 
807 /**
808  * Called when the AudioContext class finishes decoding sound data.
809  * @param {AudioBuffer} buffer
810  * @private
811  */
812 createjs.Sound.BufferAudioPlayer.prototype.handleDecode_ = function(buffer) {
813   /// <param type="AudioBuffer" name="buffer"/>
814   this.duration = buffer.duration;
815   this.item.resultObject = buffer;
816   if (this.loader_) {
817     this.loader_.sendFileComplete(!buffer, buffer);
818     this.loader_ = null;
819   }
820   if (this.auto_) {
821     this.playAudioBuffer_(buffer);
822     this.auto_ = 0;
823   }
824 };
825 
826 /** @override */
827 createjs.Sound.BufferAudioPlayer.prototype.play_ =
828     function(interrupt, delay, offset, loop, volume, pan) {
829   /// <param type="number" name="interrupt"/>
830   /// <param type="number" name="delay"/>
831   /// <param type="number" name="offset"/>
832   /// <param type="number" name="loop"/>
833   /// <param type="number" name="volume"/>
834   /// <param type="number" name="pan"/>
835   createjs.Sound.BufferAudioPlayer.superClass_.play_.call(
836       this, interrupt, delay, offset, loop, volume, pan);
837   if (!this.item.resultObject) {
838     // A browser does not finish decoding this sound. Play this sound when a
839     // browser finishes decoding it.
840     this.auto_ = 1;
841     return;
842   }
843   if (this.source_) {
844     this.stop_();
845   }
846   createjs.WebAudioPlugin.playDummySound_();
847   this.playAudioBuffer_(/** @type {AudioBuffer} */ (this.item.resultObject));
848 };
849 
850 /** @override */
851 createjs.Sound.BufferAudioPlayer.prototype.stop_ = function() {
852   createjs.Sound.BufferAudioPlayer.superClass_.stop_.call(this);
853   this.auto_ = 0;
854   var source = this.source_;
855   if (source) {
856     createjs.log('WebAudio: stop ' + this.item.id);
857     // Read the 'playbackState' property if this AudioBufferSourceNode has it
858     // and check if its value is 3 (FINISHED_STATE). This code is a workaround
859     // for "InvalidStateError" exceptions on older browsers (e.g. SC-02F) whose
860     // AudioBufferSourceNode interface does not have the onended property.
861     var playbackState = source.playbackState || 0;
862     if (playbackState != 3) {
863       if (source.stop) {
864         source.stop(0);
865       } else {
866         source.noteOff(0);
867       }
868     }
869     source.disconnect(0);
870     source.onended = null;
871     // When an AudioBufferSouceNode object is disconnected from a destination
872     // node, Mobile Safari does not delete its AudioBuffer object. Attach a
873     // 1-sample AudioBuffer object to the AudioBufferSourceNode object to delete
874     // the AudioBuffer object on the browser. (This code throws an exception on
875     // Chrome and Firefox, i.e. this code  must be executed only on Mobile
876     // Safari.)
877     if (createjs.UserAgent.isIPhone()) {
878       if (!createjs.WebAudioPlugin.buffer_) {
879         var context = createjs.WebAudioPlugin.getContext_();
880         createjs.WebAudioPlugin.buffer_ = context.createBuffer(1, 1, 22500);
881       }
882       this.source_.buffer = createjs.WebAudioPlugin.buffer_;
883     }
884     this.source_ = null;
885     this.gain_.disconnect(0);
886     this.gain_ = null;
887   }
888 };
889 
890 /** @override */
891 createjs.Sound.BufferAudioPlayer.prototype.setMute_ = function(mute) {
892   /// <param type="boolean" name="mute"/>
893   if (this.gain_) {
894     if (mute) {
895       this.gain_.gain.value = 0;
896     } else {
897       this.gain_.gain.value = this.volume;
898     }
899   }
900 };
901 
902 /** @override */
903 createjs.Sound.BufferAudioPlayer.prototype.setVolume_ = function(volume) {
904   /// <param type="number" name="volume"/>
905   if (this.volume != volume) {
906     this.volume = volume;
907     if (this.gain_) {
908       this.gain_.gain.value = volume;
909     }
910   }
911 };
912 
913 /** @override */
914 createjs.Sound.BufferAudioPlayer.prototype.setSprite_ =
915     function(id, offset, duration) {
916   /// <param type="string" name="id"/>
917   /// <param type="number" name="offset"/>
918   /// <param type="number" name="duration"/>
919   this.offset = offset * 0.001;
920   this.duration = duration * 0.001;
921 };
922 
923 /** @override */
924 createjs.Sound.BufferAudioPlayer.prototype.handleLoad =
925     function(loader, buffer) {
926   /// <param type="createjs.Loader" name="loader"/>
927   /// <param type="ArrayBuffer" name="buffer"/>
928   if (buffer) {
929     var context = createjs.WebAudioPlugin.getContext_();
930     var handleDecode =
931         createjs.Sound.BufferAudioPlayer.prototype.handleDecode_.bind(this);
932     context.decodeAudioData(
933         buffer, handleDecode, /** @type {function()} */ (handleDecode));
934     if (this.item.isSynchronous()) {
935       this.loader_ = loader;
936       return false;
937     }
938   }
939   return true;
940 };
941 
942 // Export overridden methods.
943 createjs.Sound.BufferAudioPlayer.prototype['play'] =
944     createjs.Sound.BufferAudioPlayer.prototype.play_;
945 createjs.Sound.BufferAudioPlayer.prototype['stop'] =
946     createjs.Sound.BufferAudioPlayer.prototype.stop_;
947 createjs.Sound.BufferAudioPlayer.prototype['setMute'] =
948     createjs.Sound.BufferAudioPlayer.prototype.setMute_;
949 createjs.Sound.BufferAudioPlayer.prototype['setVolume'] =
950     createjs.Sound.BufferAudioPlayer.prototype.setVolume_;
951 
952 /**
953  * A class that implements the createjs.Sound.Player interface with the
954  * WebAudio API and plays sounds on an <iframe> element.
955  * @param {createjs.Loader.Item} item
956  * @extends {createjs.Sound.Player}
957  * @constructor
958  */
959 createjs.Sound.FrameAudioPlayer = function(item) {
960   /// <param type="cretejs.Loader.Item" name="item"/>
961   createjs.Sound.Player.call(this, item);
962 
963   /**
964    * The ID assigned to a sound file or an audio sprite.
965    * @type {string}
966    * @private
967    */
968   this.id_ = item.id;
969 };
970 createjs.inherits('Sound.FrameAudioPlayer',
971                   createjs.Sound.FrameAudioPlayer,
972                   createjs.Sound.Player);
973 
974 /**
975  * An inner class that encapsulates an <iframe> element.
976  * @implements {EventListener}
977  * @constructor
978  */
979 createjs.Sound.FrameAudioPlayer.Frame = function() {
980   var frame =
981       /** @type {HTMLIFrameElement} */ (document.createElement('iframe'));
982   frame.id = 'cjs-iframe';
983   var style = frame.style;
984   style.zIndex = 200000;
985   style.position = 'absolute';
986   style.border = '0px';
987   style.top = '0px';
988   style.left = '0px';
989   var parent = document.body;
990   var rectangle = parent.getBoundingClientRect();
991   style.width = rectangle.width + 'px';
992   style.height = rectangle.height + 'px';
993   window.addEventListener('message', this, false);
994   if (createjs.DEBUG) {
995     frame.src =
996         createjs.Sound.FrameAudioPlayer.Frame.HEADER_ +
997         createjs.Sound.FrameAudioPlayer.Frame.SOURCE_DEBUG_ +
998         createjs.Sound.FrameAudioPlayer.Frame.FOOTER_;
999   } else {
1000     frame.src =
1001         createjs.Sound.FrameAudioPlayer.Frame.HEADER_ +
1002         createjs.Sound.FrameAudioPlayer.Frame.SOURCE_MIN_ +
1003         createjs.Sound.FrameAudioPlayer.Frame.FOOTER_;
1004   }
1005   parent.appendChild(frame);
1006 
1007   /**
1008    * The <iframe> element that actually plays sounds.
1009    * @type {HTMLIFrameElement}
1010    * @private
1011    */
1012   this.frame_ = frame;
1013 
1014   /**
1015    * The messages queued to this frame. An <iframe> element drops messages until
1016    * it finishes loading a page, i.e. this frame cannot send messages until it
1017    * receives an INITIALIZE command. This array temporarily saves messages until
1018    * the <iframe> element becomes ready to receive them.
1019    * @type {Array.<Object>}
1020    * @private
1021    */
1022   this.messages_ = [];
1023 
1024   /**
1025    * The mapping table from a resource ID to a FrameAudioPlayer object. This
1026    * table is used for dispatching messages received from the <iframe> element
1027    * to FrameAudioPlayer objects.
1028    * @type {Object.<string,createjs.Sound.FrameAudioPlayer>}
1029    * @private
1030    */
1031   this.players_ = {};
1032 };
1033 
1034 /**
1035  * @const {string}
1036  * @private
1037  */
1038 createjs.Sound.FrameAudioPlayer.Frame.HEADER_ =
1039   'data:text/html,' +
1040   '<html style="-webkit-user-select:none;">' +
1041   '<head>' +
1042   '<script type="text/javascript">';
1043 
1044 /**
1045  * @const {string}
1046  * @private
1047  */
1048 createjs.Sound.FrameAudioPlayer.Frame.FOOTER_ =
1049   '</script>' +
1050   '</head>' +
1051   '<body>' +
1052   '</body>' +
1053   '</html>';
1054   
1055 /**
1056  * The optimized source code of this <iframe> element.
1057  * @const {string}
1058  * @private
1059  */
1060 createjs.Sound.FrameAudioPlayer.Frame.SOURCE_MIN_ =
1061   'var e,f=window,g=f.webkitAudioContext||f.AudioContext,h=null,k=null,l=' +
1062   '!1;function m(){this.f={}}var n=1;function p(a,b){this.f=a;this.o=b}va' +
1063   'r q=MouseEvent.WEBKIT_FORCE_AT_MOUSE_DOWN?"touchend":"touchstart";e=p.' +
1064   'prototype;e.k=1;e.l=-1;e.n=0;e.i=0;e.p=0;e.h=null;e.g=null;e.m=null;e.' +
1065   'j=null;e.q=function(a){this.g=a;this.o.postMessage({a:6,b:this.f},"*")' +
1066   ';this.i&&(r(this,this.p,this.k),this.i=0);if(this.h){for(var b=0;b<thi' +
1067   's.h.length;++b){var c=this.h[b];c.g=a;c.i&&(r(c,c.p,c.k),c.i=0)}this.h' +
1068   '=null}};e.r=function(){s(this);this.o.postMessage({a:7,b:this.f},"*")}' +
1069   ';function r(a,b,c){if(a.g){a.m&&s(a);a.k=c;var d=h;c=d.createGain?d.cr' +
1070   'eateGain():d.createGainNode();c.connect(d.destination);c.gain.value=a.' +
1071   'k;a.j=c;d=d.createBufferSource();d.connect(c);d.buffer=a.g;b?d.loop=!0' +
1072   ':d.onended=a.r.bind(a);0<=a.l?d.start?d.start(0,a.l,a.n):d.noteGrainOn' +
1073   '(0,a.l,a.n):d.start?d.start(0):d.noteOn(0);a.m=d}else a.i=1,a.p=b,a.k=' +
1074   'c}function s(a){var b=a.m;b&&(3!=(b.playbackState||0)&&(b.stop?b.stop(' +
1075   '0):b.noteOff(0)),b.disconnect(0),b.onended=null,l&&k&&(b.buffer=k),a.m' +
1076   '=null,a.j.disconnect(0),a.j=null)}function t(){h.currentTime||!n?f.par' +
1077   'ent.postMessage({a:5},"*"):(--n,f.addEventListener(q,u,!1))}function v' +
1078   '(){var a=h.createBufferSource();a.buffer=k;a.start?a.start(0):a.noteOn' +
1079   '(0);setTimeout(t,100)}m.prototype.handleEvent=function(a){var b=a.type' +
1080   ';if("message"==b){var b=a.data,c=b.a,d=b.b;if(0==c)this.f[d]||(a=new p' +
1081   '(d,a.source),this.f[d]=a,a=a.q.bind(a),h.decodeAudioData(b.c,a,a));els' +
1082   'e if(a=this.f[d])1==c?r(a,b.c,b.d):2==c?s(a):3==c?(b=b.c,a.j&&(a.j.gai' +
1083   'n.value=b)):8==c&&(c=new p(a.f,a.o),c.f=b.c,c.g=a.g,c.l=b.d,c.n=b.e,th' +
1084   'is.f[b.c]=c,a.g||(a.h||(a.h=[]),a.h.push(c)))}else f.removeEventListen' +
1085   'er(b,this,!1),v()};var u=null;f.onload=function(){h=new g;u=new m;k=h.' +
1086   'createBuffer(1,1,22500);var a=navigator.platform;l="iPhone"==a||"iPad"' +
1087   '==a;v();f.addEventListener("message",u,!1);f.parent.postMessage({a:4},' +
1088   '"*")};';
1089 
1090 /**
1091  * The source code of this <iframe> element used for debug.
1092  * @const {string}
1093  * @private
1094  */
1095 createjs.Sound.FrameAudioPlayer.Frame.SOURCE_DEBUG_ =
1096   'var e,f=window,g=f.webkitAudioContext||f.AudioContext;function h(a){co' +
1097   'nsole.debug(a)}var k=null,l=null,m=!1;function n(){this.f={}}var p=1;f' +
1098   'unction q(a,b){this.f=a;this.o=b}var r=MouseEvent.WEBKIT_FORCE_AT_MOUS' +
1099   'E_DOWN?"touchend":"touchstart";e=q.prototype;e.k=1;e.l=-1;e.n=0;e.i=0;' +
1100   'e.p=0;e.h=null;e.g=null;e.m=null;e.j=null;e.q=function(a){h("decode="+' +
1101   'this.f);this.g=a;this.o.postMessage({a:6,b:this.f},"*");this.i&&(s(thi' +
1102   's,this.p,this.k),this.i=0);if(this.h){for(var b=0;b<this.h.length;++b)' +
1103   '{var c=this.h[b];c.g=a;c.i&&(s(c,c.p,c.k),c.i=0)}this.h=null}};e.r=fun' +
1104   'ction(){h("ended="+this.f);t(this);this.o.postMessage({a:7,b:this.f},"' +
1105   '*")};function s(a,b,c){h("play="+a.f+","+b+","+c);if(a.g){a.m&&t(a);a.' +
1106   'k=c;var d=k;c=d.createGain?d.createGain():d.createGainNode();c.connect' +
1107   '(d.destination);c.gain.value=a.k;a.j=c;d=d.createBufferSource();d.conn' +
1108   'ect(c);d.buffer=a.g;b?d.loop=!0:d.onended=a.r.bind(a);0<=a.l?d.start?d' +
1109   '.start(0,a.l,a.n):d.noteGrainOn(0,a.l,a.n):d.start?d.start(0):d.noteOn' +
1110   '(0);a.m=d}else a.i=1,a.p=b,a.k=c}function t(a){h("stop="+a.f);var b=a.' +
1111   'm;b&&(3!=(b.playbackState||0)&&(b.stop?b.stop(0):b.noteOff(0)),b.disco' +
1112   'nnect(0),b.onended=null,m&&l&&(b.buffer=l),a.m=null,a.j.disconnect(0),' +
1113   'a.j=null)}function u(){h("> currentTime="+k.currentTime);k.currentTime' +
1114   '||!p?f.parent.postMessage({a:5},"*"):(--p,f.addEventListener(r,v,!1))}' +
1115   'function w(){var a=k.createBufferSource();a.buffer=l;a.start?a.start(0' +
1116   '):a.noteOn(0);setTimeout(u,100)}n.prototype.handleEvent=function(a){va' +
1117   'r b=a.type;if("message"==b){var b=a.data,c=b.a,d=b.b;if(0==c)h("load="' +
1118   '+d),this.f[d]||(a=new q(d,a.source),this.f[d]=a,a=a.q.bind(a),k.decode' +
1119   'AudioData(b.c,a,a));else if(a=this.f[d])1==c?s(a,b.c,b.d):2==c?t(a):3=' +
1120   '=c?(b=b.c,h("volume="+a.f+","+b),a.j&&(a.j.gain.value=b)):8==c&&(h("cl' +
1121   'one="+b.c+","+b.d+","+b.e),c=new q(a.f,a.o),c.f=b.c,c.g=a.g,c.l=b.d,c.' +
1122   'n=b.e,this.f[b.c]=c,a.g||(a.h||(a.h=[]),a.h.push(c)))}else h("> type="' +
1123   '+b),f.removeEventListener(b,this,!1),w()};var v=null;f.onload=function' +
1124   '(){k=new g;v=new n;l=k.createBuffer(1,1,22500);var a=navigator.platfor' +
1125   'm;m="iPhone"==a||"iPad"==a;w();f.addEventListener("message",v,!1);f.pa' +
1126   'rent.postMessage({a:4},"*")};';
1127 
1128 /**
1129  * Loads the specified sound and decodes it.
1130  * @param {string} id
1131  * @param {ArrayBuffer} buffer
1132  * @const
1133  */
1134 createjs.Sound.FrameAudioPlayer.Frame.prototype.load_ = function(id, buffer) {
1135   /// <param type="string" name="id"/>
1136   /// <param type="ArrayBuffer" name="buffer"/>
1137   var window = this.frame_.contentWindow;
1138   if (window) {
1139     window.postMessage({
1140       'a': createjs.FrameCommand.LOAD,
1141       'b': id,
1142       'c': buffer
1143     }, '*');
1144   }
1145 };
1146 
1147 /**
1148  * Starts loading the specified sound.
1149  * @param {string} id
1150  * @param {ArrayBuffer} buffer
1151  * @param {createjs.Sound.FrameAudioPlayer} player
1152  * @return {boolean}
1153  * @const
1154  */
1155 createjs.Sound.FrameAudioPlayer.Frame.prototype.load =
1156     function(id, buffer, player) {
1157   /// <param type="string" name="id"/>
1158   /// <param type="ArrayBuffer" name="buffer"/>
1159   /// <param type="createjs.Sound.FrameAudioPlayer" name="player"/>
1160   /// <returns type="boolean"/>
1161   if (this.players_[id]) {
1162     return true;
1163   }
1164   this.players_[id] = player;
1165   var message = {
1166     'a': createjs.FrameCommand.LOAD,
1167     'b': id,
1168     'c': buffer
1169   };
1170   if (this.messages_) {
1171     this.messages_.push(message);
1172   } else {
1173     var window = this.frame_.contentWindow;
1174     if (window) {
1175       window.postMessage(message, '*');
1176     }
1177   }
1178   return false;
1179 };
1180 
1181 /**
1182  * Starts playing the specified sound.
1183  * @param {string} id
1184  * @param {number} loop
1185  * @param {number} volume
1186  * @const
1187  */
1188 createjs.Sound.FrameAudioPlayer.Frame.prototype.play =
1189     function(id, loop, volume) {
1190   /// <param type="string" name="id"/>
1191   /// <param type="number" name="loop"/>
1192   var window = this.frame_.contentWindow;
1193   if (window) {
1194     window.postMessage({
1195       'a': createjs.FrameCommand.PLAY,
1196       'b': id,
1197       'c': loop,
1198       'd': volume
1199   }, '*');
1200   }
1201 };
1202 
1203 /**
1204  * Stops playing the specified sound.
1205  * @param {string} id
1206  * @const
1207  */
1208 createjs.Sound.FrameAudioPlayer.Frame.prototype.stop = function(id) {
1209   /// <param type="string" name="id"/>
1210   var window = this.frame_.contentWindow;
1211   if (window) {
1212     window.postMessage({
1213       'a': createjs.FrameCommand.STOP,
1214       'b': id
1215     }, '*');
1216   }
1217 };
1218 
1219 /**
1220  * Changes the volume of the specified sound.
1221  * @param {string} id
1222  * @param {number} volume
1223  * @const
1224  */
1225 createjs.Sound.FrameAudioPlayer.Frame.prototype.setVolume =
1226     function(id, volume) {
1227   /// <param type="string" name="id"/>
1228   /// <param type="number" name="volume"/>
1229   var window = this.frame_.contentWindow;
1230   if (window) {
1231     window.postMessage({
1232       'a': createjs.FrameCommand.SET_VOLUME,
1233       'b': id,
1234       'c': volume
1235     }, '*');
1236   }
1237 };
1238 
1239 /**
1240  * Creates a clone of the specified sound.
1241  * @param {string} id
1242  * @param {string} clone
1243  * @param {number} offset
1244  * @param {number} duration
1245  * @param {createjs.Sound.FrameAudioPlayer} player
1246  * @const
1247  */
1248 createjs.Sound.FrameAudioPlayer.Frame.prototype.clone =
1249     function(id, clone, offset, duration, player) {
1250   /// <param type="string" name="id"/>
1251   /// <param type="string" name="clone"/>
1252   /// <param type="number" name="offset"/>
1253   /// <param type="number" name="duration"/>
1254   /// <param type="createjs.Sound.FrameAudioPlayer" name="player"/>
1255   var window = this.frame_.contentWindow;
1256   if (window) {
1257     var message = {
1258       'a': createjs.FrameCommand.CLONE,
1259       'b': id,
1260       'c': clone,
1261       'd': offset,
1262       'e': duration
1263     };
1264     if (this.messages_) {
1265       this.messages_.push(message);
1266     } else {
1267       window.postMessage(message, '*');
1268     }
1269     this.players_[clone] = player;
1270   }
1271 };
1272 
1273 /**
1274  * Destroys this player. This player destroys the <iframe> element to delete
1275  * all its resources, e.g. AudioBuffers, BufferSourceNodes, etc.
1276  * @const
1277  */
1278 createjs.Sound.FrameAudioPlayer.Frame.prototype.destroy = function() {
1279   window.removeEventListener('message', this, false);
1280   document.body.removeChild(this.frame_);
1281   this.frame_ = null;
1282 };
1283 
1284 /** @override */
1285 createjs.Sound.FrameAudioPlayer.Frame.prototype.handleEvent = function(event) {
1286   /// <param type="MessageEvent" name="event"/>
1287   var data = /** @type {Object} */ (/** @type{*} */ (event.data));
1288   var command = /** @type {number} */ (data['a']);
1289   if (command == createjs.FrameCommand.INITIALIZE) {
1290     if (this.messages_) {
1291       var window = this.frame_.contentWindow;
1292       if (window) {
1293         for (var i = 0; i < this.messages_.length; ++i) {
1294           window.postMessage(this.messages_[i], '*');
1295         }
1296       }
1297       this.messages_ = null;
1298     }
1299     return;
1300   }
1301   if (command == createjs.FrameCommand.TOUCH) {
1302     var style = this.frame_.style;
1303     style.zIndex = -1;
1304     return;
1305   }
1306   var id = /** @type {string} */ (data['b']);
1307   var player = this.players_[id];
1308   if (!player) {
1309     return;
1310   }
1311   if (command == createjs.FrameCommand.DECODE) {
1312     player.handleDecode();
1313   } else if (command == createjs.FrameCommand.END) {
1314     player.handleEnd();
1315   }
1316 };
1317 
1318 /**
1319  * The player that actually plays sounds in an <iframe> element.
1320  * @type {createjs.Sound.FrameAudioPlayer.Frame}
1321  * @private
1322  */
1323 createjs.Sound.FrameAudioPlayer.frame_ = null;
1324 
1325 /**
1326  * Whether this player is in the mute state.
1327  * @type {boolean}
1328  * @private
1329  */
1330 createjs.Sound.FrameAudioPlayer.prototype.mute_ = false;
1331 
1332 /**
1333  * The createjs.Loader object that loads the sound associated with this
1334  * player.
1335  * @type {createjs.Loader}
1336  * @private
1337  */
1338 createjs.Sound.FrameAudioPlayer.prototype.loader_ = null;
1339 
1340 /**
1341  * The ID assigned to an audio sprite.
1342  * @type {string}
1343  * @private
1344  */
1345 createjs.Sound.FrameAudioPlayer.prototype.id_ = '';
1346 
1347 /**
1348  * Retrieves the global FrameAudioPlayer object.
1349  * @private
1350  */
1351 createjs.Sound.FrameAudioPlayer.getFrame_ = function() {
1352   /// <returns type="createjs.Sound.FrameAudioPlayer.Frame"/>
1353   if (!createjs.Sound.FrameAudioPlayer.frame_) {
1354     createjs.Sound.FrameAudioPlayer.frame_ =
1355         new createjs.Sound.FrameAudioPlayer.Frame();
1356   }
1357   return createjs.Sound.FrameAudioPlayer.frame_;
1358 };
1359 
1360 /**
1361  * Destroys the global resources used by FrameAudioPlayer objects.
1362  * @private
1363  */
1364 createjs.Sound.FrameAudioPlayer.destroy_ = function() {
1365   var frame = createjs.Sound.FrameAudioPlayer.frame_;
1366   if (frame) {
1367     frame.destroy();
1368     createjs.Sound.FrameAudioPlayer.frame_ = null;
1369   }
1370 };
1371 
1372 /** @override */
1373 createjs.Sound.FrameAudioPlayer.prototype.play_ =
1374     function(interrupt, delay, offset, loop, volume, pan) {
1375   /// <param type="number" name="interrupt"/>
1376   /// <param type="number" name="delay"/>
1377   /// <param type="number" name="offset"/>
1378   /// <param type="number" name="loop"/>
1379   /// <param type="number" name="volume"/>
1380   /// <param type="number" name="pan"/>
1381   createjs.Sound.FrameAudioPlayer.superClass_.play_.call(
1382       this, interrupt, delay, offset, loop, volume, pan);
1383   var frame = createjs.Sound.FrameAudioPlayer.getFrame_();
1384   frame.play(this.id_, this.loop, this.volume);
1385 };
1386 
1387 /** @override */
1388 createjs.Sound.FrameAudioPlayer.prototype.stop_ = function() {
1389   createjs.Sound.FrameAudioPlayer.superClass_.stop_.call(this);
1390   var frame = createjs.Sound.FrameAudioPlayer.getFrame_();
1391   frame.stop(this.id_);
1392 };
1393 
1394 /** @override */
1395 createjs.Sound.FrameAudioPlayer.prototype.setMute_ = function(mute) {
1396   /// <param type="boolean" name="mute"/>
1397   var volume = mute ? 0 : this.volume;
1398   var frame = createjs.Sound.FrameAudioPlayer.getFrame_();
1399   frame.setVolume(this.id_, volume);
1400 };
1401 
1402 /** @override */
1403 createjs.Sound.FrameAudioPlayer.prototype.setVolume_ = function(volume) {
1404   /// <param type="number" name="volume"/>
1405   if (this.volume != volume) {
1406     this.volume = volume;
1407     var frame = createjs.Sound.FrameAudioPlayer.getFrame_();
1408     frame.setVolume(this.id_, volume);
1409   }
1410 };
1411 
1412 /** @override */
1413 createjs.Sound.FrameAudioPlayer.prototype.setSprite_ =
1414     function(id, offset, duration) {
1415   /// <param type="string" name="id"/>
1416   /// <param type="number" name="offset"/>
1417   /// <param type="number" name="duration"/>
1418   this.id_ = id;
1419   this.offset = offset * 0.001;
1420   this.duration = duration * 0.001;
1421   var frame = createjs.Sound.FrameAudioPlayer.getFrame_();
1422   frame.clone(this.item.id, id, this.offset, this.duration, this);
1423 };
1424 
1425 /** @override */
1426 createjs.Sound.FrameAudioPlayer.prototype.handleLoad =
1427     function(loader, buffer) {
1428   /// <param type="createjs.Loader" name="loader"/>
1429   /// <param type="ArrayBuffer" name="buffer"/>
1430   if (buffer) {
1431     var frame = createjs.Sound.FrameAudioPlayer.getFrame_();
1432     var result = frame.load(this.item.id, buffer, this);
1433     if (!result && this.item.isSynchronous()) {
1434       this.loader_ = loader;
1435       return false;
1436     }
1437   }
1438   return true;
1439 };
1440 
1441 /**
1442  * Called when the createjs.Sound.FrameAudioPlayer.Frame object finishes
1443  * decoding a sound.
1444  * @const
1445  */
1446 createjs.Sound.FrameAudioPlayer.prototype.handleDecode = function() {
1447   if (this.loader_) {
1448     this.loader_.sendFileComplete(false, null);
1449     this.loader_ = null;
1450   }
1451 };
1452 
1453 /**
1454  * Called when the createjs.Sound.FrameAudioPlayer.Frame object finishes
1455  * playing a sound.
1456  * @const
1457  */
1458 createjs.Sound.FrameAudioPlayer.prototype.handleEnd = function() {
1459   this.dispatchNotification('complete');
1460 };
1461 
1462 // Export overridden methods.
1463 createjs.Sound.FrameAudioPlayer.prototype['play'] =
1464     createjs.Sound.FrameAudioPlayer.prototype.play_;
1465 createjs.Sound.FrameAudioPlayer.prototype['stop'] =
1466     createjs.Sound.FrameAudioPlayer.prototype.stop_;
1467 createjs.Sound.FrameAudioPlayer.prototype['setMute'] =
1468     createjs.Sound.FrameAudioPlayer.prototype.setMute_;
1469 createjs.Sound.FrameAudioPlayer.prototype['setVolume'] =
1470     createjs.Sound.FrameAudioPlayer.prototype.setVolume_;
1471 
1472 /**
1473  * A class that intermediates a createjs.LoadQueue object and a createjs.Sound
1474  * object.
1475  * @implements {createjs.LoadQueue.Listener}
1476  * @constructor
1477  */
1478 createjs.Sound.Proxy = function() {
1479 };
1480 
1481 /** @override */
1482 createjs.Sound.Proxy.prototype.handleFileStart = function(item) {
1483   /// <param type="createjs.Loader.Item" name="item"/>
1484   if (item.isSound()) {
1485     var player = createjs.Sound.getInstance_().addPlayer_(item);
1486     item.setListener(player);
1487   }
1488 };
1489 
1490 /** @override */
1491 createjs.Sound.Proxy.prototype.handleFileComplete = function(item) {
1492   /// <param type="createjs.Loader.Item" name="item"/>
1493   /// <returns type="boolean"/>
1494   return !!createjs.Sound.getInstance_().getPlayer_(item.id);
1495 };
1496 
1497 /**
1498  * Creates an available player.
1499  * @param {createjs.Loader.Item} item
1500  * @return {createjs.Sound.Player}
1501  * @private
1502  */
1503 createjs.Sound.prototype.createPlayer_ = function(item) {
1504   /// <param type="createjs.Loader.Item" name="item"/>
1505   /// <returns type="createjs.Sound.Player"/>
1506   var context = createjs.WebAudioPlugin.getContext_();
1507   if (context) {
1508     if (createjs.Config.useFrame()) {
1509       return new createjs.Sound.FrameAudioPlayer(item);
1510     }
1511     return new createjs.Sound.BufferAudioPlayer(item);
1512   }
1513   createjs.assert(!createjs.UserAgent.isIPhone());
1514   return new createjs.Sound.HTMLAudioPlayer(item);
1515 };
1516 
1517 /**
1518  * Creates a sound player for the specified item and adds it to this object.
1519  * @param {createjs.Loader.Item} item
1520  * @return {createjs.Sound.Player}
1521  * @private
1522  */
1523 createjs.Sound.prototype.addPlayer_ = function(item) {
1524   /// <param type="createjs.Loader.Item" name="item"/>
1525   /// <returns type="createjs.Sound.Player"/>
1526   var source = item.getSource();
1527   var player = this.players_[source];
1528   if (!player) {
1529     player = this.createPlayer_(item);
1530     this.players_[source] = player;
1531     if (item.id != source) {
1532       this.players_[item.id] = player;
1533     }
1534   }
1535   return player;
1536 };
1537 
1538 /**
1539  * Retrieves a preloaded item.
1540  * @param {string} key
1541  * @return {createjs.Sound.Player}
1542  * @private
1543  */
1544 createjs.Sound.prototype.getPlayer_ = function(key) {
1545   /// <param type="string" name="key"/>
1546   /// <returns type="createjs.Sound.Player"/>
1547   return this.players_[key];
1548 };
1549 
1550 /**
1551  * Retrieves the capabilities of the active plug-in,
1552  * @return {Object}
1553  * @private
1554  */
1555 createjs.Sound.prototype.getCapabilities_ = function() {
1556   /// <returns ype="Object"/>
1557   return this.capabilities_;
1558 };
1559 
1560 /**
1561  * Returns a specific capability of the active plugin.
1562  * @param {string} key
1563  * @return {number}
1564  */
1565 createjs.Sound.prototype.getCapability_ = function(key) {
1566   /// <param type="string" name="key"/>
1567   /// <returns type="number"/>
1568   return this.capabilities_[key] || 0;
1569 };
1570 
1571 /**
1572  * Returns the source source from the specified ID.
1573  * @param {string} id
1574  * @return {string}
1575  * @private
1576  */
1577 createjs.Sound.prototype.getSourceById_ = function(id) {
1578   /// <param type="string" name="id"/>
1579   /// <returns type="string"/>
1580   return '';
1581 };
1582 
1583 /**
1584  * Registers an audio file for loading.
1585  * @param {string} source
1586  * @param {Object} data
1587  * @private
1588  */
1589 createjs.Sound.prototype.registerSound_ = function(source, data) {
1590   /// <param type="string" name="source"/>
1591   /// <param type="Object" name="data"/>
1592   var player = this.getPlayer_(source);
1593   var audioSprite = createjs.getArray(data['audioSprite']);
1594   if (player && audioSprite) {
1595     for (var i = 0; i < audioSprite.length; ++i) {
1596       // Create a clone of the source player and update its offset and
1597       // duration. The createjs.Sound.registerSound() method uses the number
1598       // milliseconds both for the 'startTime' property and for the 'duration'
1599       // property. Players should convert their values to their preferable
1600       // formats.)
1601       var sprite = audioSprite[i];
1602       var id = createjs.getString(sprite['id']);
1603       var startTime = createjs.getNumber(sprite['startTime']);
1604       var duration = createjs.getNumber(sprite['duration']);
1605       var clone = this.createPlayer_(player.item);
1606       clone.setSprite_(id, startTime, duration);
1607       this.players_[id] = clone;
1608     }
1609   }
1610 };
1611 
1612 /**
1613  * Removes a sound from this object.
1614  * @param {string} key
1615  * @param {string} basePath
1616  * @return {boolean}
1617  * @private
1618  */
1619 createjs.Sound.prototype.removeSound_ = function(key, basePath) {
1620   /// <param type="string" name="key"/>
1621   /// <param type="string" name="basePath"/>
1622   /// <returns type="boolean"/>
1623   var player = this.getPlayer_(key);
1624   if (!player) {
1625     return false;
1626   }
1627   player.stop_();
1628   var item = player.item;
1629   var source = item.getSource();
1630   this.players_[source] = null;
1631   if (item.id != source) {
1632     this.players_[item.id] = null;
1633   }
1634   return true;
1635 };
1636 
1637 /**
1638  * Removes all sounds.
1639  * @private
1640  */
1641 createjs.Sound.prototype.removeAllSounds_ = function() {
1642   createjs.notImplemented();
1643 };
1644 
1645 /**
1646  * Returns whether a source file has been loaded by this object.
1647  * @param {string} source
1648  * @return {boolean}
1649  * @private
1650  */
1651 createjs.Sound.prototype.loadComplete_ = function(source) {
1652   /// <param type="string" name="source"/>
1653   /// <returns type="boolean"/>
1654   return false;
1655 };
1656 
1657 /**
1658  * Creates a new sound instance.
1659  * @param {string} source
1660  * @param {number=} opt_startTime
1661  * @param {number=} opt_duration
1662  * @return {createjs.Sound.Player}
1663  * @private
1664  */
1665 createjs.Sound.prototype.createInstance_ =
1666     function(source, opt_startTime, opt_duration) {
1667   /// <param type="string" name="source"/>
1668   /// <param type="number" optional="true" name="opt_startTime"/>
1669   /// <param type="number" optional="true" name="opt_duration"/>
1670   /// <returns type="createjs.Sound.Player"/>
1671   return this.getPlayer_(source);
1672 };
1673 
1674 /**
1675  * Plays a sound file.
1676  * @param {string} source
1677  * @param {number} interrupt
1678  * @param {number} delay
1679  * @param {number} offset
1680  * @param {number} loop
1681  * @param {number} volume
1682  * @param {number} pan
1683  * @private
1684  */
1685 createjs.Sound.prototype.play_ =
1686     function(source, interrupt, delay, offset, loop, volume, pan) {
1687   /// <param type="string" name="source"/>
1688   /// <param type="number" name="interrupt"/>
1689   /// <param type="number" name="delay"/>
1690   /// <param type="number" name="offset"/>
1691   /// <param type="number" name="loop"/>
1692   /// <param type="number" name="volume"/>
1693   /// <param type="number" name="pan"/>
1694   var player = this.createInstance_(source);
1695   if (player) {
1696     player.play_(0, delay, offset, loop, volume, pan);
1697   }
1698 };
1699 
1700 /**
1701  * Sets the master volume.
1702  * @param {number} volume
1703  * @private
1704  */
1705 createjs.Sound.prototype.setVolume_ = function(volume) {
1706   /// <param type="number" name="volume"/>
1707 };
1708 
1709 /**
1710  * Gets the master volume.
1711  * @return {number}
1712  * @private
1713  */
1714 createjs.Sound.prototype.getVolume_ = function() {
1715   /// <returns type="number"/>
1716   return 1;
1717 };
1718 
1719 /**
1720  * Sets the mute state.
1721  * @param {boolean} value
1722  * @return {boolean}
1723  * @private
1724  */
1725 createjs.Sound.prototype.setMute_ = function(value) {
1726   /// <param type="boolean" name="value"/>
1727   /// <returns type="boolean"/>
1728   for (var key in this.players_) {
1729     var player = this.players_[key];
1730     if (player) {
1731       player.setMute_(value);
1732     }
1733   }
1734   return true;
1735 };
1736 
1737 /**
1738  * Returns the current mute state.
1739  * @return {boolean}
1740  * @private
1741  */
1742 createjs.Sound.prototype.getMute_ = function() {
1743   /// <returns type="boolean"/>
1744   return false;
1745 };
1746 
1747 /**
1748  * Stops playing all audio.
1749  * @private
1750  */
1751 createjs.Sound.prototype.stop_ = function() {
1752 };
1753 
1754 /**
1755  * Registers a plug-in.
1756  * @param {Object} plugin
1757  * @return {boolean}
1758  */
1759 createjs.Sound.registerPlugin = function(plugin) {
1760   /// <param type="Object" name="plugin"/>
1761   /// <returns type="boolean"/>
1762   return false;
1763 };
1764 
1765 /**
1766  * Registers plug-ins.
1767  * @param {Array} plugins
1768  * @return {boolean}
1769  */
1770 createjs.Sound.registerPlugins = function(plugins) {
1771   /// <param type="Array" elementType="Object" name="plugins"/>
1772   /// <returns type="boolean"/>
1773   return false;
1774 };
1775 
1776 /**
1777  * Returns whether there is at least one listener for the specified event type.
1778  * @return {createjs.Sound.Plugin}
1779  */
1780 createjs.Sound.getActivePlugin = function() {
1781   /// <returns type="createjs.WebAudioPlugin"/>
1782   if (createjs.AudioContext) {
1783     return new createjs.WebAudioPlugin();
1784   }
1785   return new createjs.HTMLAudioPlugin();
1786 };
1787 
1788 /**
1789  * Initializes the default plug-ins.
1790  * @return {boolean}
1791  */
1792 createjs.Sound.initializeDefaultPlugins = function() {
1793   /// <returns type="boolean"/>
1794   return true;
1795 };
1796 
1797 /**
1798  * Returns whether this module can play sound.
1799  * @return {boolean}
1800  */
1801 createjs.Sound.isReady = function() {
1802   /// <returns type="boolean"/>
1803   return true;
1804 };
1805 
1806 /**
1807  * Creates a new sound instance.
1808  * @param {string} source
1809  * @return {createjs.Sound.Player}
1810  */
1811 createjs.Sound.createInstance = function(source) {
1812   /// <param type="string" name="source"/>
1813   /// <returns type="createjs.Sound.Player"/>
1814   return createjs.Sound.getInstance_().createInstance_(source);
1815 };
1816 
1817 /**
1818  * Creates audio sprites from an audio file and registers them. This method
1819  * creates audio sprites from an audio file (loaded by a LoadQueue object) so
1820  * a game can play a part of the audio file as listed in the following snippet.
1821  *   var queue = new createjs.LoadQueue();
1822  *   queue.on('complete', function() {
1823  *     createjs.Sound.registerSound('http://server/audio.m4a', '', {
1824  *       audioSprite: [
1825  *         { id: 'sound1', startTime: 0, duration: 500 },
1826  *         { id: 'sound2', startTime: 1000, duration: 400 },
1827  *         { id: 'sound3', startTime: 1700, duration: 1000 }
1828  *       }
1829  *     );
1830  *   });
1831  *   queue.loadFile({ src: 'http://server/audio.m4a' });
1832  * @param {string|Object} source
1833  * @param {string=} opt_id
1834  * @param {number|Object=} opt_data
1835  * @param {boolean=} opt_preload
1836  * @param {string=} opt_basePath
1837  */
1838 createjs.Sound.registerSound =
1839     function(source, opt_id, opt_data, opt_preload, opt_basePath) {
1840   /// <param type="string" name="source"/>
1841   /// <param type="string" optional="true" name="opt_id"/>
1842   /// <param type="number" optional="true" name="opt_data"/>
1843   /// <param type="boolean" optional="true" name="opt_preload">
1844   /// <param type="string" optional="true" name="opt_basePath"/>
1845   var id = opt_id || createjs.getString(source);
1846   createjs.Sound.getInstance_().registerSound_(
1847       id, createjs.getObject(opt_data));
1848 };
1849 
1850 /**
1851  * Creates audio sprites from audio files and registers them.
1852  *   createjs.Sound.registerSounds([{
1853  *     src: 'http://server/audio.m4a',
1854  *     data: {
1855  *       audioSprite: [
1856  *         { id: 'sound1', startTime: 0, duration: 500 },
1857  *         { id: 'sound2', startTime: 1000, duration: 400 },
1858  *         { id: 'sound3', startTime: 1700, duration: 1000 }
1859  *       ]
1860  *     }
1861  *   }]);
1862  * @param {Array.<Object>} sources
1863  * @param {string=} opt_basePath
1864  */
1865 createjs.Sound.registerSounds = function(sources, opt_basePath) {
1866   for (var i = 0; i < sources.length; ++i) {
1867     var source = sources[i];
1868     createjs.Sound.registerSound(source['src'], '', source['data']);
1869   }
1870 };
1871 
1872 /**
1873  * Registers a manifest of audio files for loading.
1874  * @param {Array} manifest
1875  * @param {string} basePath
1876  * @return {Array.<Object>}
1877  */
1878 createjs.Sound.registerManifest = function(manifest, basePath) {
1879   /// <param type="Array" elementType="Object" name="manifest"/>
1880   /// <param type="string" name="basePath"/>
1881   /// <returns type="Array"/>
1882   var values = [];
1883   var length = manifest.length;
1884   for (var i = 0; i < length; ++i) {
1885     var item = manifest[i];
1886     values.push(createjs.Sound.registerSound(
1887         item['src'], item['id'], item['data'], item['preload'], basePath));
1888   }
1889   return values;
1890 };
1891 
1892 /**
1893  * Removes a sound.
1894  * @param {string|Object} src
1895  * @param {string} basePath
1896  * @return {boolean}
1897  */
1898 createjs.Sound.removeSound = function(src, basePath) {
1899   /// <param type="string" name="src"/>
1900   /// <param type="string" name="basePath"/>
1901   /// <returns type="boolean"/>
1902   var instance = createjs.Sound.getInstance_();
1903   if (createjs.isObject(src)) {
1904     var item = createjs.getObject(src);
1905     return instance.removeSound_(createjs.getString(item['src']), basePath);
1906   }
1907   return instance.removeSound_(createjs.getString(src), basePath);
1908 };
1909 
1910 /**
1911  * Removes all sound files in a manifest.
1912  * @param {Array.<Object>} manifest
1913  * @param {string} basePath
1914  * @return {Array.<boolean>}
1915  */
1916 createjs.Sound.removeManifest = function(manifest, basePath) {
1917   /// <param type="Array" elementType="Object" name="manifest"/>
1918   /// <param type="string" name="basePath"/>
1919   /// <returns type="Array"/>
1920   var instance = createjs.Sound.getInstance_();
1921   var returnValues = [];
1922   var length = manifest.length;
1923   for (var i = 0; i < length; ++i) {
1924     var item = createjs.getObject(manifest[i]);
1925     returnValues[i] = instance.removeSound_(item['src'], basePath);
1926   }
1927   return returnValues;
1928 };
1929 
1930 /**
1931  * Removes all sounds registered to this object.
1932  */
1933 createjs.Sound.removeAllSounds = function() {
1934   return createjs.Sound.getInstance_().removeAllSounds_();
1935 };
1936 
1937 /**
1938  * Returns whether a source has been loaded.
1939  * @param {string} source
1940  * @return {boolean}
1941  */
1942 createjs.Sound.prototype.loadComplete = function(source) {
1943   /// <param type="string" name="source"/>
1944   /// <returns type="boolean"/>
1945   return createjs.Sound.getInstance_().loadComplete_(source);
1946 };
1947 
1948 /**
1949  * Resets the global variables used by this class and the inner classes in this
1950  * file.
1951  * @param {number=} opt_destroy
1952  * @const
1953  */
1954 createjs.Sound.reset = function(opt_destroy) {
1955   /// <param type="number" optional="true" name="opt_destroy"/>
1956   if (opt_destroy) {
1957     var instance = createjs.Sound.getInstance_();
1958     for (var key in instance.players_) {
1959       var player = instance.players_[key];
1960       if (player) {
1961         player.stop_();
1962       }
1963     }
1964     createjs.WebAudioPlugin.reset_();
1965     if (createjs.Config.useFrame()) {
1966       createjs.Sound.FrameAudioPlayer.destroy_();
1967     }
1968   }
1969 };
1970 
1971 /**
1972  * Plays a sound.
1973  * @param {string} source
1974  * @param {string|Object} value
1975  * @param {number=} opt_delay
1976  * @param {number=} opt_offset
1977  * @param {number=} opt_loop
1978  * @param {number=} opt_volume
1979  * @param {number=} opt_pan
1980  * @static
1981  */
1982 createjs.Sound.play = function(source,
1983                                value,
1984                                opt_delay,
1985                                opt_offset,
1986                                opt_loop,
1987                                opt_volume,
1988                                opt_pan) {
1989   /// <param type="string" name="source"/>
1990   /// <param type="string" name="value"/>
1991   /// <param type="number" optional="true" name="opt_delay"/>
1992   /// <param type="number" optional="true" name="opt_offset"/>
1993   /// <param type="number" optional="true" name="opt_loop"/>
1994   /// <param type="number" optional="true" name="opt_volume"/>
1995   /// <param type="number" optional="true" name="opt_pan"/>
1996   if (value != null && createjs.isObject(value)) {
1997     var options = createjs.getObject(value);
1998     value = options['interrupt'];
1999     opt_delay = options['delay'];
2000     opt_offset = options['offset'];
2001     opt_loop = options['loop'];
2002     opt_volume = options['volume'];
2003     opt_pan = options['pan'];
2004   }
2005   var interrupt = createjs.Sound.INTERRUPT_NONE;
2006   if (createjs.isString(value)) {
2007     var key = createjs.getString(value);
2008     var INTERRUPTS = {
2009       'any': createjs.Sound.INTERRUPT_ANY,
2010       'early': createjs.Sound.INTERRUPT_EARLY,
2011       'late': createjs.Sound.INTERRUPT_LATE,
2012       'none': createjs.Sound.INTERRUPT_NONE
2013     };
2014     interrupt = INTERRUPTS[key] || createjs.Sound.INTERRUPT_NONE;
2015   }
2016   var delay = opt_delay || 0;
2017   var offset = opt_offset || 0;
2018   var loop = opt_loop || 0;
2019   var volume = (opt_volume == null) ? 1 : createjs.getNumber(opt_volume);
2020   var pan = opt_pan || 0;
2021   createjs.Sound.getInstance_().play_(
2022       source, interrupt, delay, offset, loop, volume, pan);
2023 };
2024 
2025 /**
2026  * Sets the master volume.
2027  * @param {number} value
2028  * @static
2029  */
2030 createjs.Sound.setVolume = function(value) {
2031   /// <param type="number" name="volume"/>
2032   value = createjs.max(0, createjs.min(1, value));
2033   return createjs.Sound.getInstance_().setVolume_(value);
2034 };
2035 
2036 /**
2037  * Returns the master volume.
2038  * @return {number}
2039  * @static
2040  */
2041 createjs.Sound.getVolume = function() {
2042   /// <returns type="number"/>
2043   return createjs.Sound.getInstance_().getVolume_();
2044 };
2045 
2046 /**
2047  * Mutes/Unmutes all audio.
2048  * @param {boolean} value
2049  * @return {boolean}
2050  */
2051 createjs.Sound.setMute = function(value) {
2052   /// <param type="boolean" name="value"/>
2053   /// <returns type="boolean"/>
2054   createjs.assert(value != null);
2055   return createjs.Sound.getInstance_().setMute_(value);
2056 };
2057 
2058 /**
2059  * Returns the mute status.
2060  * @return {boolean}
2061  */
2062 createjs.Sound.getMute = function() {
2063   /// <returns type="boolean"/>
2064   return createjs.Sound.getInstance_().getMute_();
2065 };
2066 
2067 /**
2068  * Stops all audio (global stop)
2069  */
2070 createjs.Sound.stop = function() {
2071   return createjs.Sound.getInstance_().stop_();
2072 };
2073 
2074 /**
2075  * Adds an event listener.
2076  * @param {string} type
2077  * @param {Function|Object} listener
2078  * @param {boolean=} opt_useCapture
2079  * @return {Function|Object}
2080  */
2081 createjs.Sound.addListener = function(type, listener, opt_useCapture) {
2082   /// <param type="string" name="type"/>
2083   /// <param type="Function" name="listener"/>
2084   /// <param type="boolean" optional="true" name="opt_useCapture"/>
2085   /// <returns type="Function"/>
2086   var instance = createjs.Sound.getInstance_();
2087   return instance.on(type, listener);
2088 };
2089 
2090 /**
2091  * Removes the specified event listener.
2092  * @param {string} type
2093  * @param {Function|Object} listener
2094  * @param {boolean=} opt_useCapture
2095  */
2096 createjs.Sound.removeListener = function(type, listener, opt_useCapture) {
2097   /// <param type="string" name="type"/>
2098   /// <param type="Function" name="listener"/>
2099   /// <param type="boolean" optional="true" name="opt_useCapture"/>
2100   var instance = createjs.Sound.getInstance_();
2101   instance.off(type, listener);
2102 };
2103 
2104 /**
2105  * Removes all listeners for the specified type, or all listeners of all types.
2106  * @param {string=} opt_type
2107  */
2108 createjs.Sound.removeAllListeners = function(opt_type) {
2109   /// <param type="string" optional="true" name="opt_type"/>
2110   var instance = createjs.Sound.getInstance_();
2111   instance.removeAllListeners(opt_type || '');
2112 };
2113 
2114 /**
2115  * Dispatches the specified event to all listeners.
2116  * @param {Object|string|Event} event
2117  * @param {Object=} opt_target
2118  * @return {boolean}
2119  */
2120 createjs.Sound.dispatch = function(event, opt_target) {
2121   /// <param type="Object" name="event"/>
2122   /// <param type="Object" optional="true" name="opt_target"/>
2123   /// <returns type="boolean"/>
2124   var instance = createjs.Sound.getInstance_();
2125   return instance.dispatch(event, opt_target || null);
2126 };
2127 
2128 /**
2129  * Returns whether there is at least one listener for the specified event type.
2130  * @param {string} type
2131  * @return {boolean}
2132  */
2133 createjs.Sound.hasListener = function(type) {
2134   /// <param type="string" name="type"/>
2135   /// <returns type="boolean"/>
2136   var instance = createjs.Sound.getInstance_();
2137   return instance.hasListener(type);
2138 };
2139 
2140 /**
2141  * A table of exported functions.
2142  * @const {Object}
2143  */
2144 createjs.Sound.exports = createjs.exportStatic('createjs.Sound', {
2145   'INTERRUPT_ANY': createjs.Sound.INTERRUPT_ANY,
2146   'INTERRUPT_EARLY': createjs.Sound.INTERRUPT_EARLY,
2147   'INTERRUPT_LATE': createjs.Sound.INTERRUPT_LATE,
2148   'INTERRUPT_NONE': createjs.Sound.INTERRUPT_NONE,
2149   'PLAY_SUCCEEDED': createjs.Sound.PLAY_SUCCEEDED,
2150   'PLAY_FINISHED': createjs.Sound.PLAY_FINISHED,
2151   'PLAY_FAILED': createjs.Sound.PLAY_FAILED,
2152   'activePlugin': createjs.Sound.getActivePlugin(),
2153   'initializeDefaultPlugins': createjs.Sound.initializeDefaultPlugins,
2154   'isReady': createjs.Sound.isReady,
2155   'createInstance': createjs.Sound.createInstance,
2156   'registerSound': createjs.Sound.registerSound,
2157   'registerSounds': createjs.Sound.registerSounds,
2158   'removeSound': createjs.Sound.removeSound,
2159   'play': createjs.Sound.play,
2160   'addEventListener': createjs.Sound.addListener,
2161   'removeEventListener': createjs.Sound.removeListener,
2162   'removeAllEventListeners': createjs.Sound.removeAllListeners,
2163   'dispatchEvent': createjs.Sound.dispatch,
2164   'hasEventListener': createjs.Sound.hasListener,
2165   'setMute': createjs.Sound.setMute,
2166   'setVolume': createjs.Sound.setVolume
2167 });
2168