- /*
- * Sound
- * Visit http://createjs.com/ for documentation, updates and examples.
- *
- *
- * Copyright (c) 2012 gskinner.com, inc.
- *
- * Permission is hereby granted, free of charge, to any person
- * obtaining a copy of this software and associated documentation
- * files (the "Software"), to deal in the Software without
- * restriction, including without limitation the rights to use,
- * copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following
- * conditions:
- *
- * The above copyright notice and this permission notice shall be
- * included in all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
- * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
- * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
- * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
- * OTHER DEALINGS IN THE SOFTWARE.
- */
-
- // namespace:
- this.createjs = this.createjs || {};
-
- /**
- * The SoundJS library manages the playback of audio on the web. It works via plugins which abstract the actual audio
- * implementation, so playback is possible on any platform without specific knowledge of what mechanisms are necessary
- * to play sounds.
- *
- * To use SoundJS, use the public API on the {{#crossLink "Sound"}}{{/crossLink}} class. This API is for:
- * <ul>
- * <li>Installing audio playback Plugins</li>
- * <li>Registering (and preloading) sounds</li>
- * <li>Creating and playing sounds</li>
- * <li>Master volume, mute, and stop controls for all sounds at once</li>
- * </ul>
- *
- * <b>Controlling Sounds</b><br />
- * Playing sounds creates {{#crossLink "AbstractSoundInstance"}}{{/crossLink}} instances, which can be controlled
- * individually.
- * <ul>
- * <li>Pause, resume, seek, and stop sounds</li>
- * <li>Control a sound's volume, mute, and pan</li>
- * <li>Listen for events on sound instances to get notified when they finish, loop, or fail</li>
- * </ul>
- *
- * <h4>Example</h4>
- *
- * createjs.Sound.alternateExtensions = ["mp3"];
- * createjs.Sound.on("fileload", this.loadHandler, this);
- * createjs.Sound.registerSound("path/to/mySound.ogg", "sound");
- * function loadHandler(event) {
- * // This is fired for each sound that is registered.
- * var instance = createjs.Sound.play("sound"); // play using id. Could also use full sourcepath or event.src.
- * instance.on("complete", this.handleComplete, this);
- * instance.volume = 0.5;
- * }
- *
- * <h4>Browser Support</h4>
- * Audio will work in browsers which support Web Audio (<a href="http://caniuse.com/audio-api" target="_blank">http://caniuse.com/audio-api</a>)
- * or HTMLAudioElement (<a href="http://caniuse.com/audio" target="_blank">http://caniuse.com/audio</a>).
- * A Flash fallback can be used for any browser that supports the Flash player, and the Cordova plugin can be used in
- * any webview that supports <a href="http://plugins.cordova.io/#/package/org.apache.cordova.media" target="_blank">Cordova.Media</a>.
- * IE8 and earlier are not supported, even with the Flash fallback. To support earlier browsers, you can use an older
- * version of SoundJS (version 0.5.2 and earlier).
- *
- * @module SoundJS
- * @main SoundJS
- */
-
- (function () {
- "use strict";
-
- /**
- * The Sound class is the public API for creating sounds, controlling the overall sound levels, and managing plugins.
- * All Sound APIs on this class are static.
- *
- * <b>Registering and Preloading</b><br />
- * Before you can play a sound, it <b>must</b> be registered. You can do this with {{#crossLink "Sound/registerSound"}}{{/crossLink}},
- * or register multiple sounds using {{#crossLink "Sound/registerSounds"}}{{/crossLink}}. If you don't register a
- * sound prior to attempting to play it using {{#crossLink "Sound/play"}}{{/crossLink}} or create it using {{#crossLink "Sound/createInstance"}}{{/crossLink}},
- * the sound source will be automatically registered but playback will fail as the source will not be ready. If you use
- * <a href="http://preloadjs.com" target="_blank">PreloadJS</a>, registration is handled for you when the sound is
- * preloaded. It is recommended to preload sounds either internally using the register functions or externally using
- * PreloadJS so they are ready when you want to use them.
- *
- * <b>Playback</b><br />
- * To play a sound once it's been registered and preloaded, use the {{#crossLink "Sound/play"}}{{/crossLink}} method.
- * This method returns a {{#crossLink "AbstractSoundInstance"}}{{/crossLink}} which can be paused, resumed, muted, etc.
- * Please see the {{#crossLink "AbstractSoundInstance"}}{{/crossLink}} documentation for more on the instance control APIs.
- *
- * <b>Plugins</b><br />
- * By default, the {{#crossLink "WebAudioPlugin"}}{{/crossLink}} or the {{#crossLink "HTMLAudioPlugin"}}{{/crossLink}}
- * are used (when available), although developers can change plugin priority or add new plugins (such as the
- * provided {{#crossLink "FlashAudioPlugin"}}{{/crossLink}}). Please see the {{#crossLink "Sound"}}{{/crossLink}} API
- * methods for more on the playback and plugin APIs. To install plugins, or specify a different plugin order, see
- * {{#crossLink "Sound/installPlugins"}}{{/crossLink}}.
- *
- * <h4>Example</h4>
- *
- * createjs.FlashAudioPlugin.swfPath = "../src/soundjs/flashaudio";
- * createjs.Sound.registerPlugins([createjs.WebAudioPlugin, createjs.FlashAudioPlugin]);
- * createjs.Sound.alternateExtensions = ["mp3"];
- * createjs.Sound.on("fileload", this.loadHandler, this);
- * createjs.Sound.registerSound("path/to/mySound.ogg", "sound");
- * function loadHandler(event) {
- * // This is fired for each sound that is registered.
- * var instance = createjs.Sound.play("sound"); // play using id. Could also use full source path or event.src.
- * instance.on("complete", this.handleComplete, this);
- * instance.volume = 0.5;
- * }
- *
- * The maximum number of concurrently playing instances of the same sound can be specified in the "data" argument
- * of {{#crossLink "Sound/registerSound"}}{{/crossLink}}. Note that if not specified, the active plugin will apply
- * a default limit. Currently HTMLAudioPlugin sets a default limit of 2, while WebAudioPlugin and FlashAudioPlugin set a
- * default limit of 100.
- *
- * createjs.Sound.registerSound("sound.mp3", "soundId", 4);
- *
- * Sound can be used as a plugin with PreloadJS to help preload audio properly. Audio preloaded with PreloadJS is
- * automatically registered with the Sound class. When audio is not preloaded, Sound will do an automatic internal
- * load. As a result, it may fail to play the first time play is called if the audio is not finished loading. Use
- * the {{#crossLink "Sound/fileload:event"}}{{/crossLink}} event to determine when a sound has finished internally
- * preloading. It is recommended that all audio is preloaded before it is played.
- *
- * var queue = new createjs.LoadQueue();
- * queue.installPlugin(createjs.Sound);
- *
- * <b>Audio Sprites</b><br />
- * SoundJS has added support for {{#crossLink "AudioSprite"}}{{/crossLink}}, available as of version 0.6.0.
- * For those unfamiliar with audio sprites, they are much like CSS sprites or sprite sheets: multiple audio assets
- * grouped into a single file.
- *
- * <h4>Example</h4>
- *
- * var assetsPath = "./assets/";
- * var sounds = [{
- * src:"MyAudioSprite.ogg", data: {
- * audioSprite: [
- * {id:"sound1", startTime:0, duration:500},
- * {id:"sound2", startTime:1000, duration:400},
- * {id:"sound3", startTime:1700, duration: 1000}
- * ]}
- * }
- * ];
- * createjs.Sound.alternateExtensions = ["mp3"];
- * createjs.Sound.on("fileload", loadSound);
- * createjs.Sound.registerSounds(sounds, assetsPath);
- * // after load is complete
- * createjs.Sound.play("sound2");
- *
- * <b>Mobile Playback</b><br />
- * Devices running iOS require the WebAudio context to be "unlocked" by playing at least one sound inside of a user-
- * initiated event (such as touch/click). Earlier versions of SoundJS included a "MobileSafe" sample, but this is no
- * longer necessary as of SoundJS 0.6.2.
- * <ul>
- * <li>
- * In SoundJS 0.4.1 and above, you can either initialize plugins or use the {{#crossLink "WebAudioPlugin/playEmptySound"}}{{/crossLink}}
- * method in the call stack of a user input event to manually unlock the audio context.
- * </li>
- * <li>
- * In SoundJS 0.6.2 and above, SoundJS will automatically listen for the first document-level "mousedown"
- * and "touchend" event, and unlock WebAudio. This will continue to check these events until the WebAudio
- * context becomes "unlocked" (changes from "suspended" to "running")
- * </li>
- * <li>
- * Both the "mousedown" and "touchend" events can be used to unlock audio in iOS9+, the "touchstart" event
- * will work in iOS8 and below. The "touchend" event will only work in iOS9 when the gesture is interpreted
- * as a "click", so if the user long-presses the button, it will no longer work.
- * </li>
- * <li>
- * When using the <a href="http://www.createjs.com/docs/easeljs/classes/Touch.html">EaselJS Touch class</a>,
- * the "mousedown" event will not fire when a canvas is clicked, since MouseEvents are prevented, to ensure
- * only touch events fire. To get around this, you can either rely on "touchend", or:
- * <ol>
- * <li>Set the `allowDefault` property on the Touch class constructor to `true` (defaults to `false`).</li>
- * <li>Set the `preventSelection` property on the EaselJS `Stage` to `false`.</li>
- * </ol>
- * These settings may change how your application behaves, and are not recommended.
- * </li>
- * </ul>
- *
- * <b>Loading Alternate Paths and Extension-less Files</b><br />
- * SoundJS supports loading alternate paths and extension-less files by passing an object instead of a string for
- * the `src` property, which is a hash using the format `{extension:"path", extension2:"path2"}`. These labels are
- * how SoundJS determines if the browser will support the sound. This also enables multiple formats to live in
- * different folders, or on CDNs, which often has completely different filenames for each file.
- *
- * Priority is determined by the property order (first property is tried first). This is supported by both internal loading
- * and loading with PreloadJS.
- *
- * <em>Note: an id is required for playback.</em>
- *
- * <h4>Example</h4>
- *
- * var sounds = {path:"./audioPath/",
- * manifest: [
- * {id: "cool", src: {mp3:"mp3/awesome.mp3", ogg:"noExtensionOggFile"}}
- * ]};
- *
- * createjs.Sound.alternateExtensions = ["mp3"];
- * createjs.Sound.addEventListener("fileload", handleLoad);
- * createjs.Sound.registerSounds(sounds);
- *
- * <h3>Known Browser and OS issues</h3>
- * <b>IE 9 HTML Audio limitations</b><br />
- * <ul><li>There is a delay in applying volume changes to tags that occurs once playback is started. So if you have
- * muted all sounds, they will all play during this delay until the mute applies internally. This happens regardless of
- * when or how you apply the volume change, as the tag seems to need to play to apply it.</li>
- * <li>MP3 encoding will not always work for audio tags, particularly in Internet Explorer. We've found default
- * encoding with 64kbps works.</li>
- * <li>Occasionally very short samples will get cut off.</li>
- * <li>There is a limit to how many audio tags you can load and play at once, which appears to be determined by
- * hardware and browser settings. See {{#crossLink "HTMLAudioPlugin.MAX_INSTANCES"}}{{/crossLink}} for a safe
- * estimate.</li></ul>
- *
- * <b>Firefox 25 Web Audio limitations</b>
- * <ul><li>mp3 audio files do not load properly on all windows machines, reported
- * <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=929969" target="_blank">here</a>. </br>
- * For this reason it is recommended to pass another FF supported type (ie ogg) first until this bug is resolved, if
- * possible.</li></ul>
-
- * <b>Safari limitations</b><br />
- * <ul><li>Safari requires Quicktime to be installed for audio playback.</li></ul>
- *
- * <b>iOS 6 Web Audio limitations</b><br />
- * <ul><li>Sound is initially locked, and must be unlocked via a user-initiated event. Please see the section on
- * Mobile Playback above.</li>
- * <li>A bug exists that will distort un-cached web audio when a video element is present in the DOM that has audio
- * at a different sampleRate.</li>
- * </ul>
- *
- * <b>Android HTML Audio limitations</b><br />
- * <ul><li>We have no control over audio volume. Only the user can set volume on their device.</li>
- * <li>We can only play audio inside a user event (touch/click). This currently means you cannot loop sound or use
- * a delay.</li></ul>
- *
- * <b>Web Audio and PreloadJS</b><br />
- * <ul><li>Web Audio must be loaded through XHR, therefore when used with PreloadJS, tag loading is not possible.
- * This means that tag loading can not be used to avoid cross domain issues.</li><ul>
- *
- * @class Sound
- * @static
- * @uses EventDispatcher
- */
- function Sound() {
- throw "Sound cannot be instantiated";
- }
-
- var s = Sound;
-
-
- // Static Properties
- /**
- * The interrupt value to interrupt any currently playing instance with the same source, if the maximum number of
- * instances of the sound are already playing.
- * @property INTERRUPT_ANY
- * @type {String}
- * @default any
- * @static
- */
- s.INTERRUPT_ANY = "any";
-
- /**
- * The interrupt value to interrupt the earliest currently playing instance with the same source that progressed the
- * least distance in the audio track, if the maximum number of instances of the sound are already playing.
- * @property INTERRUPT_EARLY
- * @type {String}
- * @default early
- * @static
- */
- s.INTERRUPT_EARLY = "early";
-
- /**
- * The interrupt value to interrupt the currently playing instance with the same source that progressed the most
- * distance in the audio track, if the maximum number of instances of the sound are already playing.
- * @property INTERRUPT_LATE
- * @type {String}
- * @default late
- * @static
- */
- s.INTERRUPT_LATE = "late";
-
- /**
- * The interrupt value to not interrupt any currently playing instances with the same source, if the maximum number of
- * instances of the sound are already playing.
- * @property INTERRUPT_NONE
- * @type {String}
- * @default none
- * @static
- */
- s.INTERRUPT_NONE = "none";
-
- /**
- * Defines the playState of an instance that is still initializing.
- * @property PLAY_INITED
- * @type {String}
- * @default playInited
- * @static
- */
- s.PLAY_INITED = "playInited";
-
- /**
- * Defines the playState of an instance that is currently playing or paused.
- * @property PLAY_SUCCEEDED
- * @type {String}
- * @default playSucceeded
- * @static
- */
- s.PLAY_SUCCEEDED = "playSucceeded";
-
- /**
- * Defines the playState of an instance that was interrupted by another instance.
- * @property PLAY_INTERRUPTED
- * @type {String}
- * @default playInterrupted
- * @static
- */
- s.PLAY_INTERRUPTED = "playInterrupted";
-
- /**
- * Defines the playState of an instance that completed playback.
- * @property PLAY_FINISHED
- * @type {String}
- * @default playFinished
- * @static
- */
- s.PLAY_FINISHED = "playFinished";
-
- /**
- * Defines the playState of an instance that failed to play. This is usually caused by a lack of available channels
- * when the interrupt mode was "INTERRUPT_NONE", the playback stalled, or the sound could not be found.
- * @property PLAY_FAILED
- * @type {String}
- * @default playFailed
- * @static
- */
- s.PLAY_FAILED = "playFailed";
-
- /**
- * A list of the default supported extensions that Sound will <i>try</i> to play. Plugins will check if the browser
- * can play these types, so modifying this list before a plugin is initialized will allow the plugins to try to
- * support additional media types.
- *
- * NOTE this does not currently work for {{#crossLink "FlashAudioPlugin"}}{{/crossLink}}.
- *
- * More details on file formats can be found at <a href="http://en.wikipedia.org/wiki/Audio_file_format" target="_blank">http://en.wikipedia.org/wiki/Audio_file_format</a>.<br />
- * A very detailed list of file formats can be found at <a href="http://www.fileinfo.com/filetypes/audio" target="_blank">http://www.fileinfo.com/filetypes/audio</a>.
- * @property SUPPORTED_EXTENSIONS
- * @type {Array[String]}
- * @default ["mp3", "ogg", "opus", "mpeg", "wav", "m4a", "mp4", "aiff", "wma", "mid"]
- * @since 0.4.0
- * @static
- */
- s.SUPPORTED_EXTENSIONS = ["mp3", "ogg", "opus", "mpeg", "wav", "m4a", "mp4", "aiff", "wma", "mid"];
-
- /**
- * Some extensions use another type of extension support to play (one of them is a codex). This allows you to map
- * that support so plugins can accurately determine if an extension is supported. Adding to this list can help
- * plugins determine more accurately if an extension is supported.
- *
- * A useful list of extensions for each format can be found at <a href="http://html5doctor.com/html5-audio-the-state-of-play/" target="_blank">http://html5doctor.com/html5-audio-the-state-of-play/</a>.
- * @property EXTENSION_MAP
- * @type {Object}
- * @since 0.4.0
- * @default {m4a:"mp4"}
- * @static
- */
- s.EXTENSION_MAP = {
- m4a:"mp4"
- };
-
- /**
- * The RegExp pattern used to parse file URIs. This supports simple file names, as well as full domain URIs with
- * query strings. The resulting match is: protocol:$1 domain:$2 path:$3 file:$4 extension:$5 query:$6.
- * @property FILE_PATTERN
- * @type {RegExp}
- * @static
- * @private
- */
- s.FILE_PATTERN = /^(?:(\w+:)\/{2}(\w+(?:\.\w+)*\/?))?([/.]*?(?:[^?]+)?\/)?((?:[^/?]+)\.(\w+))(?:\?(\S+)?)?$/;
-
-
- // Class Public properties
- /**
- * Determines the default behavior for interrupting other currently playing instances with the same source, if the
- * maximum number of instances of the sound are already playing. Currently the default is {{#crossLink "Sound/INTERRUPT_NONE:property"}}{{/crossLink}}
- * but this can be set and will change playback behavior accordingly. This is only used when {{#crossLink "Sound/play"}}{{/crossLink}}
- * is called without passing a value for interrupt.
- * @property defaultInterruptBehavior
- * @type {String}
- * @default Sound.INTERRUPT_NONE, or "none"
- * @static
- * @since 0.4.0
- */
- s.defaultInterruptBehavior = s.INTERRUPT_NONE; // OJR does s.INTERRUPT_ANY make more sense as default? Needs game dev testing to see which case makes more sense.
-
- /**
- * An array of extensions to attempt to use when loading sound, if the default is unsupported by the active plugin.
- * These are applied in order, so if you try to Load Thunder.ogg in a browser that does not support ogg, and your
- * extensions array is ["mp3", "m4a", "wav"] it will check mp3 support, then m4a, then wav. The audio files need
- * to exist in the same location, as only the extension is altered.
- *
- * Note that regardless of which file is loaded, you can call {{#crossLink "Sound/createInstance"}}{{/crossLink}}
- * and {{#crossLink "Sound/play"}}{{/crossLink}} using the same id or full source path passed for loading.
- *
- * <h4>Example</h4>
- *
- * var sounds = [
- * {src:"myPath/mySound.ogg", id:"example"},
- * ];
- * createjs.Sound.alternateExtensions = ["mp3"]; // now if ogg is not supported, SoundJS will try asset0.mp3
- * createjs.Sound.on("fileload", handleLoad); // call handleLoad when each sound loads
- * createjs.Sound.registerSounds(sounds, assetPath);
- * // ...
- * createjs.Sound.play("myPath/mySound.ogg"); // works regardless of what extension is supported. Note calling with ID is a better approach
- *
- * @property alternateExtensions
- * @type {Array}
- * @since 0.5.2
- * @static
- */
- s.alternateExtensions = [];
-
- /**
- * The currently active plugin. If this is null, then no plugin could be initialized. If no plugin was specified,
- * Sound attempts to apply the default plugins: {{#crossLink "WebAudioPlugin"}}{{/crossLink}}, followed by
- * {{#crossLink "HTMLAudioPlugin"}}{{/crossLink}}.
- * @property activePlugin
- * @type {Object}
- * @static
- */
- s.activePlugin = null;
-
-
- // class getter / setter properties
-
- /**
- * Set the master volume of Sound. The master volume is multiplied against each sound's individual volume. For
- * example, if master volume is 0.5 and a sound's volume is 0.5, the resulting volume is 0.25. To set individual
- * sound volume, use AbstractSoundInstance {{#crossLink "AbstractSoundInstance/volume:property"}}{{/crossLink}}
- * instead.
- *
- * <h4>Example</h4>
- *
- * createjs.Sound.volume = 0.5;
- *
- * @property volume
- * @type {Number}
- * @default 1
- * @static
- * @since 0.6.1
- */
-
- /**
- * The internal volume level. Use {{#crossLink "Sound/volume:property"}}{{/crossLink}} to adjust the master volume.
- * @property _masterVolume
- * @type {number}
- * @default 1
- * @private
- */
- s._masterVolume = 1;
-
- /**
- * Use the {{#crossLink "Sound/volume:property"}}{{/crossLink}} property instead.
- * @method _getMasterVolume
- * @private
- * @static
- * @return {Number}
- **/
- s._getMasterVolume = function() {
- return this._masterVolume;
- };
-
- /**
- * Use the {{#crossLink "Sound/volume:property"}}{{/crossLink}} property instead.
- * @method getMasterVolume
- * @deprecated
- */
- // Sound.getMasterVolume is @deprecated. Remove for 1.1+
- s.getVolume = createjs.deprecate(s._getMasterVolume, "Sound.getVolume");
- /**
- * Use the {{#crossLink "Sound/volume:property"}}{{/crossLink}} property instead.
- * @method _setMasterVolume
- * @static
- * @private
- **/
- s._setMasterVolume = function(value) {
- if (Number(value) == null) { return; }
- value = Math.max(0, Math.min(1, value));
- s._masterVolume = value;
- if (!this.activePlugin || !this.activePlugin.setVolume || !this.activePlugin.setVolume(value)) {
- var instances = this._instances;
- for (var i = 0, l = instances.length; i < l; i++) {
- instances[i].setMasterVolume(value);
- }
- }
- };
-
- /**
- * Use the {{#crossLink "Sound/volume:property"}}{{/crossLink}} property instead.
- * @method setVolume
- * @deprecated
- */
- // Sound.setVolume is @deprecated. Remove for 1.1+
- s.setVolume = createjs.deprecate(s._setMasterVolume, "Sound.setVolume");
-
- /**
- * Mute/Unmute all audio. Note that muted audio still plays at 0 volume. This global mute value is maintained
- * separately and when set will override, but not change the mute property of individual instances. To mute an individual
- * instance, use AbstractSoundInstance {{#crossLink "AbstractSoundInstance/muted:property"}}{{/crossLink}} instead.
- *
- * <h4>Example</h4>
- *
- * createjs.Sound.muted = true;
- *
- *
- * @property muted
- * @type {Boolean}
- * @default false
- * @static
- * @since 0.6.1
- */
- s._masterMute = false;
-
- /**
- * Use the {{#crossLink "Sound/muted:property"}}{{/crossLink}} property instead.
- * @method _getMute
- * @returns {Boolean}
- * @static
- * @private
- */
- s._getMute = function () {
- return this._masterMute;
- };
-
- /**
- * Use the {{#crossLink "Sound/muted:property"}}{{/crossLink}} property instead.
- * @method getMute
- * @deprecated
- */
- // Sound.getMute is @deprecated. Remove for 1.1+
- s.getMute = createjs.deprecate(s._getMute, "Sound.getMute");
-
- /**
- * Use the {{#crossLink "Sound/muted:property"}}{{/crossLink}} property instead.
- * @method _setMute
- * @param {Boolean} value The muted value
- * @static
- * @private
- */
- s._setMute = function (value) {
- if (value == null) { return; }
- this._masterMute = value;
- if (!this.activePlugin || !this.activePlugin.setMute || !this.activePlugin.setMute(value)) {
- var instances = this._instances;
- for (var i = 0, l = instances.length; i < l; i++) {
- instances[i].setMasterMute(value);
- }
- }
- };
-
- /**
- * Use the {{#crossLink "Sound/muted:property"}}{{/crossLink}} property instead.
- * @method setMute
- * @deprecated
- */
- // Sound.setMute is @deprecated. Remove for 1.1+
- s.setMute = createjs.deprecate(s._setMute, "Sound.setMute");
-
- /**
- * Get the active plugins capabilities, which help determine if a plugin can be used in the current environment,
- * or if the plugin supports a specific feature. Capabilities include:
- * <ul>
- * <li><b>panning:</b> If the plugin can pan audio from left to right</li>
- * <li><b>volume;</b> If the plugin can control audio volume.</li>
- * <li><b>tracks:</b> The maximum number of audio tracks that can be played back at a time. This will be -1
- * if there is no known limit.</li>
- * <br />An entry for each file type in {{#crossLink "Sound/SUPPORTED_EXTENSIONS:property"}}{{/crossLink}}:
- * <li><b>mp3:</b> If MP3 audio is supported.</li>
- * <li><b>ogg:</b> If OGG audio is supported.</li>
- * <li><b>wav:</b> If WAV audio is supported.</li>
- * <li><b>mpeg:</b> If MPEG audio is supported.</li>
- * <li><b>m4a:</b> If M4A audio is supported.</li>
- * <li><b>mp4:</b> If MP4 audio is supported.</li>
- * <li><b>aiff:</b> If aiff audio is supported.</li>
- * <li><b>wma:</b> If wma audio is supported.</li>
- * <li><b>mid:</b> If mid audio is supported.</li>
- * </ul>
- *
- * You can get a specific capability of the active plugin using standard object notation
- *
- * <h4>Example</h4>
- *
- * var mp3 = createjs.Sound.capabilities.mp3;
- *
- * Note this property is read only.
- *
- * @property capabilities
- * @type {Object}
- * @static
- * @readOnly
- * @since 0.6.1
- */
-
- /**
- * Use the {{#crossLink "Sound/capabilities:property"}}{{/crossLink}} property instead.
- * @returns {null}
- * @private
- */
- s._getCapabilities = function() {
- if (s.activePlugin == null) { return null; }
- return s.activePlugin._capabilities;
- };
-
- /**
- * Use the {{#crossLink "Sound/capabilities:property"}}{{/crossLink}} property instead.
- * @method getCapabilities
- * @deprecated
- */
- // Sound.getCapabilities is @deprecated. Remove for 1.1+
- s.getCapabilities = createjs.deprecate(s._getCapabilities, "Sound.getCapabilities");
-
- Object.defineProperties(s, {
- volume: { get: s._getMasterVolume, set: s._setMasterVolume },
- muted: { get: s._getMute, set: s._setMute },
- capabilities: { get: s._getCapabilities }
- });
-
-
- // Class Private properties
- /**
- * Determines if the plugins have been registered. If false, the first call to {{#crossLink "play"}}{{/crossLink}} will instantiate the default
- * plugins ({{#crossLink "WebAudioPlugin"}}{{/crossLink}}, followed by {{#crossLink "HTMLAudioPlugin"}}{{/crossLink}}).
- * If plugins have been registered, but none are applicable, then sound playback will fail.
- * @property _pluginsRegistered
- * @type {Boolean}
- * @default false
- * @static
- * @private
- */
- s._pluginsRegistered = false;
-
- /**
- * Used internally to assign unique IDs to each AbstractSoundInstance.
- * @property _lastID
- * @type {Number}
- * @static
- * @private
- */
- s._lastID = 0;
-
- /**
- * An array containing all currently playing instances. This allows Sound to control the volume, mute, and playback of
- * all instances when using static APIs like {{#crossLink "Sound/stop"}}{{/crossLink}} and {{#crossLink "Sound/volume:property"}}{{/crossLink}}.
- * When an instance has finished playback, it gets removed via the {{#crossLink "Sound/finishedPlaying"}}{{/crossLink}}
- * method. If the user replays an instance, it gets added back in via the {{#crossLink "Sound/_beginPlaying"}}{{/crossLink}}
- * method.
- * @property _instances
- * @type {Array}
- * @private
- * @static
- */
- s._instances = [];
-
- /**
- * An object hash storing objects with sound sources, startTime, and duration via there corresponding ID.
- * @property _idHash
- * @type {Object}
- * @private
- * @static
- */
- s._idHash = {};
-
- /**
- * An object hash that stores preloading sound sources via the parsed source that is passed to the plugin. Contains the
- * source, id, and data that was passed in by the user. Parsed sources can contain multiple instances of source, id,
- * and data.
- * @property _preloadHash
- * @type {Object}
- * @private
- * @static
- */
- s._preloadHash = {};
-
- /**
- * An object hash storing {{#crossLink "PlayPropsConfig"}}{{/crossLink}} via the parsed source that is passed as defaultPlayProps in
- * {{#crossLink "Sound/registerSound"}}{{/crossLink}} and {{#crossLink "Sound/registerSounds"}}{{/crossLink}}.
- * @property _defaultPlayPropsHash
- * @type {Object}
- * @private
- * @static
- * @since 0.6.1
- */
- s._defaultPlayPropsHash = {};
-
-
- // EventDispatcher methods:
- s.addEventListener = null;
- s.removeEventListener = null;
- s.removeAllEventListeners = null;
- s.dispatchEvent = null;
- s.hasEventListener = null;
- s._listeners = null;
-
- createjs.EventDispatcher.initialize(s); // inject EventDispatcher methods.
-
-
- // Events
- /**
- * This event is fired when a file finishes loading internally. This event is fired for each loaded sound,
- * so any handler methods should look up the <code>event.src</code> to handle a particular sound.
- * @event fileload
- * @param {Object} target The object that dispatched the event.
- * @param {String} type The event type.
- * @param {String} src The source of the sound that was loaded.
- * @param {String} [id] The id passed in when the sound was registered. If one was not provided, it will be null.
- * @param {Number|Object} [data] Any additional data associated with the item. If not provided, it will be undefined.
- * @since 0.4.1
- */
-
- /**
- * This event is fired when a file fails loading internally. This event is fired for each loaded sound,
- * so any handler methods should look up the <code>event.src</code> to handle a particular sound.
- * @event fileerror
- * @param {Object} target The object that dispatched the event.
- * @param {String} type The event type.
- * @param {String} src The source of the sound that was loaded.
- * @param {String} [id] The id passed in when the sound was registered. If one was not provided, it will be null.
- * @param {Number|Object} [data] Any additional data associated with the item. If not provided, it will be undefined.
- * @since 0.6.0
- */
-
-
- // Class Public Methods
- /**
- * Get the preload rules to allow Sound to be used as a plugin by <a href="http://preloadjs.com" target="_blank">PreloadJS</a>.
- * Any load calls that have the matching type or extension will fire the callback method, and use the resulting
- * object, which is potentially modified by Sound. This helps when determining the correct path, as well as
- * registering the audio instance(s) with Sound. This method should not be called, except by PreloadJS.
- * @method getPreloadHandlers
- * @return {Object} An object containing:
- * <ul><li>callback: A preload callback that is fired when a file is added to PreloadJS, which provides
- * Sound a mechanism to modify the load parameters, select the correct file format, register the sound, etc.</li>
- * <li>types: A list of file types that are supported by Sound (currently supports "sound").</li>
- * <li>extensions: A list of file extensions that are supported by Sound (see {{#crossLink "Sound/SUPPORTED_EXTENSIONS:property"}}{{/crossLink}}).</li></ul>
- * @static
- * @private
- */
- s.getPreloadHandlers = function () {
- return {
- callback:createjs.proxy(s.initLoad, s),
- types:["sound"],
- extensions:s.SUPPORTED_EXTENSIONS
- };
- };
-
- /**
- * Used to dispatch fileload events from internal loading.
- * @method _handleLoadComplete
- * @param event A loader event.
- * @private
- * @static
- * @since 0.6.0
- */
- s._handleLoadComplete = function(event) {
- var src = event.target.getItem().src;
- if (!s._preloadHash[src]) {return;}
-
- for (var i = 0, l = s._preloadHash[src].length; i < l; i++) {
- var item = s._preloadHash[src][i];
- s._preloadHash[src][i] = true;
-
- if (!s.hasEventListener("fileload")) { continue; }
-
- var event = new createjs.Event("fileload");
- event.src = item.src;
- event.id = item.id;
- event.data = item.data;
- event.sprite = item.sprite;
-
- s.dispatchEvent(event);
- }
- };
-
- /**
- * Used to dispatch error events from internal preloading.
- * @param event
- * @private
- * @since 0.6.0
- * @static
- */
- s._handleLoadError = function(event) {
- var src = event.target.getItem().src;
- if (!s._preloadHash[src]) {return;}
-
- for (var i = 0, l = s._preloadHash[src].length; i < l; i++) {
- var item = s._preloadHash[src][i];
- s._preloadHash[src][i] = false;
-
- if (!s.hasEventListener("fileerror")) { continue; }
-
- var event = new createjs.Event("fileerror");
- event.src = item.src;
- event.id = item.id;
- event.data = item.data;
- event.sprite = item.sprite;
-
- s.dispatchEvent(event);
- }
- };
-
- /**
- * Used by {{#crossLink "Sound/registerPlugins"}}{{/crossLink}} to register a Sound plugin.
- *
- * @method _registerPlugin
- * @param {Object} plugin The plugin class to install.
- * @return {Boolean} Whether the plugin was successfully initialized.
- * @static
- * @private
- */
- s._registerPlugin = function (plugin) {
- // Note: Each plugin is passed in as a class reference, but we store the activePlugin as an instance
- if (plugin.isSupported()) {
- s.activePlugin = new plugin();
- return true;
- }
- return false;
- };
-
- /**
- * Register a list of Sound plugins, in order of precedence. To register a single plugin, pass a single element in the array.
- *
- * <h4>Example</h4>
- *
- * createjs.FlashAudioPlugin.swfPath = "../src/soundjs/flashaudio/";
- * createjs.Sound.registerPlugins([createjs.WebAudioPlugin, createjs.HTMLAudioPlugin, createjs.FlashAudioPlugin]);
- *
- * @method registerPlugins
- * @param {Array} plugins An array of plugins classes to install.
- * @return {Boolean} Whether a plugin was successfully initialized.
- * @static
- */
- s.registerPlugins = function (plugins) {
- s._pluginsRegistered = true;
- for (var i = 0, l = plugins.length; i < l; i++) {
- if (s._registerPlugin(plugins[i])) {
- return true;
- }
- }
- return false;
- };
-
- /**
- * Initialize the default plugins. This method is automatically called when any audio is played or registered before
- * the user has manually registered plugins, and enables Sound to work without manual plugin setup. Currently, the
- * default plugins are {{#crossLink "WebAudioPlugin"}}{{/crossLink}} followed by {{#crossLink "HTMLAudioPlugin"}}{{/crossLink}}.
- *
- * <h4>Example</h4>
- *
- * if (!createjs.initializeDefaultPlugins()) { return; }
- *
- * @method initializeDefaultPlugins
- * @returns {Boolean} True if a plugin was initialized, false otherwise.
- * @since 0.4.0
- * @static
- */
- s.initializeDefaultPlugins = function () {
- if (s.activePlugin != null) {return true;}
- if (s._pluginsRegistered) {return false;}
- if (s.registerPlugins([createjs.WebAudioPlugin, createjs.HTMLAudioPlugin])) {return true;}
- return false;
- };
-
- /**
- * Determines if Sound has been initialized, and a plugin has been activated.
- *
- * <h4>Example</h4>
- * This example sets up a Flash fallback, but only if there is no plugin specified yet.
- *
- * if (!createjs.Sound.isReady()) {
- * createjs.FlashAudioPlugin.swfPath = "../src/soundjs/flashaudio/";
- * createjs.Sound.registerPlugins([createjs.WebAudioPlugin, createjs.HTMLAudioPlugin, createjs.FlashAudioPlugin]);
- * }
- *
- * @method isReady
- * @return {Boolean} If Sound has initialized a plugin.
- * @static
- */
- s.isReady = function () {
- return (s.activePlugin != null);
- };
-
- /**
- * Process manifest items from <a href="http://preloadjs.com" target="_blank">PreloadJS</a>. This method is intended
- * for usage by a plugin, and not for direct interaction.
- * @method initLoad
- * @param {Object} src The object to load.
- * @return {Object|AbstractLoader} An instance of AbstractLoader.
- * @private
- * @static
- */
- s.initLoad = function (loadItem) {
- if (loadItem.type == "video") { return true; } // Don't handle video. PreloadJS's plugin model is really aggressive.
- return s._registerSound(loadItem);
- };
-
- /**
- * Internal method for loading sounds. This should not be called directly.
- *
- * @method _registerSound
- * @param {Object} src The object to load, containing src property and optionally containing id and data.
- * @return {Object} An object with the modified values that were passed in, which defines the sound.
- * Returns false if the source cannot be parsed or no plugins can be initialized.
- * Returns true if the source is already loaded.
- * @static
- * @private
- * @since 0.6.0
- */
-
- s._registerSound = function (loadItem) {
- if (!s.initializeDefaultPlugins()) {return false;}
-
- var details;
- if (loadItem.src instanceof Object) {
- details = s._parseSrc(loadItem.src);
- details.src = loadItem.path + details.src;
- } else {
- details = s._parsePath(loadItem.src);
- }
- if (details == null) {return false;}
- loadItem.src = details.src;
- loadItem.type = "sound";
-
- var data = loadItem.data;
- var numChannels = null;
- if (data != null) {
- if (!isNaN(data.channels)) {
- numChannels = parseInt(data.channels);
- } else if (!isNaN(data)) {
- numChannels = parseInt(data);
- }
-
- if(data.audioSprite) {
- var sp;
- for(var i = data.audioSprite.length; i--; ) {
- sp = data.audioSprite[i];
- s._idHash[sp.id] = {src: loadItem.src, startTime: parseInt(sp.startTime), duration: parseInt(sp.duration)};
-
- if (sp.defaultPlayProps) {
- s._defaultPlayPropsHash[sp.id] = createjs.PlayPropsConfig.create(sp.defaultPlayProps);
- }
- }
- }
- }
- if (loadItem.id != null) {s._idHash[loadItem.id] = {src: loadItem.src}};
- var loader = s.activePlugin.register(loadItem);
-
- SoundChannel.create(loadItem.src, numChannels);
-
- // return the number of instances to the user. This will also be returned in the load event.
- if (data == null || !isNaN(data)) {
- loadItem.data = numChannels || SoundChannel.maxPerChannel();
- } else {
- loadItem.data.channels = numChannels || SoundChannel.maxPerChannel();
- }
-
- if (loader.type) {loadItem.type = loader.type;}
-
- if (loadItem.defaultPlayProps) {
- s._defaultPlayPropsHash[loadItem.src] = createjs.PlayPropsConfig.create(loadItem.defaultPlayProps);
- }
- return loader;
- };
-
- /**
- * Register an audio file for loading and future playback in Sound. This is automatically called when using
- * <a href="http://preloadjs.com" target="_blank">PreloadJS</a>. It is recommended to register all sounds that
- * need to be played back in order to properly prepare and preload them. Sound does internal preloading when required.
- *
- * <h4>Example</h4>
- *
- * createjs.Sound.alternateExtensions = ["mp3"];
- * createjs.Sound.on("fileload", handleLoad); // add an event listener for when load is completed
- * createjs.Sound.registerSound("myAudioPath/mySound.ogg", "myID", 3);
- * createjs.Sound.registerSound({ogg:"path1/mySound.ogg", mp3:"path2/mySoundNoExtension"}, "myID", 3);
- *
- *
- * @method registerSound
- * @param {String | Object} src The source or an Object with a "src" property or an Object with multiple extension labeled src properties.
- * @param {String} [id] An id specified by the user to play the sound later. Note id is required for when src is multiple extension labeled src properties.
- * @param {Number | Object} [data] Data associated with the item. Sound uses the data parameter as the number of
- * channels for an audio instance, however a "channels" property can be appended to the data object if it is used
- * for other information. The audio channels will set a default based on plugin if no value is found.
- * Sound also uses the data property to hold an {{#crossLink "AudioSprite"}}{{/crossLink}} array of objects in the following format {id, startTime, duration}.<br/>
- * id used to play the sound later, in the same manner as a sound src with an id.<br/>
- * startTime is the initial offset to start playback and loop from, in milliseconds.<br/>
- * duration is the amount of time to play the clip for, in milliseconds.<br/>
- * This allows Sound to support audio sprites that are played back by id.
- * @param {string} basePath Set a path that will be prepended to src for loading.
- * @param {Object | PlayPropsConfig} defaultPlayProps Optional Playback properties that will be set as the defaults on any new AbstractSoundInstance.
- * See {{#crossLink "PlayPropsConfig"}}{{/crossLink}} for options.
- * @return {Object} An object with the modified values that were passed in, which defines the sound.
- * Returns false if the source cannot be parsed or no plugins can be initialized.
- * Returns true if the source is already loaded.
- * @static
- * @since 0.4.0
- */
- s.registerSound = function (src, id, data, basePath, defaultPlayProps) {
- var loadItem = {src: src, id: id, data:data, defaultPlayProps:defaultPlayProps};
- if (src instanceof Object && src.src) {
- basePath = id;
- loadItem = src;
- }
- loadItem = createjs.LoadItem.create(loadItem);
- loadItem.path = basePath;
-
- if (basePath != null && !(loadItem.src instanceof Object)) {loadItem.src = basePath + loadItem.src;}
-
- var loader = s._registerSound(loadItem);
- if(!loader) {return false;}
-
- if (!s._preloadHash[loadItem.src]) { s._preloadHash[loadItem.src] = [];}
- s._preloadHash[loadItem.src].push(loadItem);
- if (s._preloadHash[loadItem.src].length == 1) {
- // OJR note this will disallow reloading a sound if loading fails or the source changes
- loader.on("complete", this._handleLoadComplete, this);
- loader.on("error", this._handleLoadError, this);
- s.activePlugin.preload(loader);
- } else {
- if (s._preloadHash[loadItem.src][0] == true) {return true;}
- }
-
- return loadItem;
- };
-
- /**
- * Register an array of audio files for loading and future playback in Sound. It is recommended to register all
- * sounds that need to be played back in order to properly prepare and preload them. Sound does internal preloading
- * when required.
- *
- * <h4>Example</h4>
- *
- * var assetPath = "./myAudioPath/";
- * var sounds = [
- * {src:"asset0.ogg", id:"example"},
- * {src:"asset1.ogg", id:"1", data:6},
- * {src:"asset2.mp3", id:"works"}
- * {src:{mp3:"path1/asset3.mp3", ogg:"path2/asset3NoExtension"}, id:"better"}
- * ];
- * createjs.Sound.alternateExtensions = ["mp3"]; // if the passed extension is not supported, try this extension
- * createjs.Sound.on("fileload", handleLoad); // call handleLoad when each sound loads
- * createjs.Sound.registerSounds(sounds, assetPath);
- *
- * @method registerSounds
- * @param {Array} sounds An array of objects to load. Objects are expected to be in the format needed for
- * {{#crossLink "Sound/registerSound"}}{{/crossLink}}: <code>{src:srcURI, id:ID, data:Data}</code>
- * with "id" and "data" being optional.
- * You can also pass an object with path and manifest properties, where path is a basePath and manifest is an array of objects to load.
- * Note id is required if src is an object with extension labeled src properties.
- * @param {string} basePath Set a path that will be prepended to each src when loading. When creating, playing, or removing
- * audio that was loaded with a basePath by src, the basePath must be included.
- * @return {Object} An array of objects with the modified values that were passed in, which defines each sound.
- * Like registerSound, it will return false for any values when the source cannot be parsed or if no plugins can be initialized.
- * Also, it will return true for any values when the source is already loaded.
- * @static
- * @since 0.6.0
- */
- s.registerSounds = function (sounds, basePath) {
- var returnValues = [];
- if (sounds.path) {
- if (!basePath) {
- basePath = sounds.path;
- } else {
- basePath = basePath + sounds.path;
- }
- sounds = sounds.manifest;
- // TODO document this feature
- }
- for (var i = 0, l = sounds.length; i < l; i++) {
- returnValues[i] = createjs.Sound.registerSound(sounds[i].src, sounds[i].id, sounds[i].data, basePath, sounds[i].defaultPlayProps);
- }
- return returnValues;
- };
-
- /**
- * Remove a sound that has been registered with {{#crossLink "Sound/registerSound"}}{{/crossLink}} or
- * {{#crossLink "Sound/registerSounds"}}{{/crossLink}}.
- * <br />Note this will stop playback on active instances playing this sound before deleting them.
- * <br />Note if you passed in a basePath, you need to pass it or prepend it to the src here.
- *
- * <h4>Example</h4>
- *
- * createjs.Sound.removeSound("myID");
- * createjs.Sound.removeSound("myAudioBasePath/mySound.ogg");
- * createjs.Sound.removeSound("myPath/myOtherSound.mp3", "myBasePath/");
- * createjs.Sound.removeSound({mp3:"musicNoExtension", ogg:"music.ogg"}, "myBasePath/");
- *
- * @method removeSound
- * @param {String | Object} src The src or ID of the audio, or an Object with a "src" property, or an Object with multiple extension labeled src properties.
- * @param {string} basePath Set a path that will be prepended to each src when removing.
- * @return {Boolean} True if sound is successfully removed.
- * @static
- * @since 0.4.1
- */
- s.removeSound = function(src, basePath) {
- if (s.activePlugin == null) {return false;}
-
- if (src instanceof Object && src.src) {src = src.src;}
-
- var details;
- if (src instanceof Object) {
- details = s._parseSrc(src);
- } else {
- src = s._getSrcById(src).src;
- details = s._parsePath(src);
- }
- if (details == null) {return false;}
- src = details.src;
- if (basePath != null) {src = basePath + src;}
-
- for(var prop in s._idHash){
- if(s._idHash[prop].src == src) {
- delete(s._idHash[prop]);
- }
- }
-
- // clear from SoundChannel, which also stops and deletes all instances
- SoundChannel.removeSrc(src);
-
- delete(s._preloadHash[src]);
-
- s.activePlugin.removeSound(src);
-
- return true;
- };
-
- /**
- * Remove an array of audio files that have been registered with {{#crossLink "Sound/registerSound"}}{{/crossLink}} or
- * {{#crossLink "Sound/registerSounds"}}{{/crossLink}}.
- * <br />Note this will stop playback on active instances playing this audio before deleting them.
- * <br />Note if you passed in a basePath, you need to pass it or prepend it to the src here.
- *
- * <h4>Example</h4>
- *
- * assetPath = "./myPath/";
- * var sounds = [
- * {src:"asset0.ogg", id:"example"},
- * {src:"asset1.ogg", id:"1", data:6},
- * {src:"asset2.mp3", id:"works"}
- * ];
- * createjs.Sound.removeSounds(sounds, assetPath);
- *
- * @method removeSounds
- * @param {Array} sounds An array of objects to remove. Objects are expected to be in the format needed for
- * {{#crossLink "Sound/removeSound"}}{{/crossLink}}: <code>{srcOrID:srcURIorID}</code>.
- * You can also pass an object with path and manifest properties, where path is a basePath and manifest is an array of objects to remove.
- * @param {string} basePath Set a path that will be prepended to each src when removing.
- * @return {Object} An array of Boolean values representing if the sounds with the same array index were
- * successfully removed.
- * @static
- * @since 0.4.1
- */
- s.removeSounds = function (sounds, basePath) {
- var returnValues = [];
- if (sounds.path) {
- if (!basePath) {
- basePath = sounds.path;
- } else {
- basePath = basePath + sounds.path;
- }
- sounds = sounds.manifest;
- }
- for (var i = 0, l = sounds.length; i < l; i++) {
- returnValues[i] = createjs.Sound.removeSound(sounds[i].src, basePath);
- }
- return returnValues;
- };
-
- /**
- * Remove all sounds that have been registered with {{#crossLink "Sound/registerSound"}}{{/crossLink}} or
- * {{#crossLink "Sound/registerSounds"}}{{/crossLink}}.
- * <br />Note this will stop playback on all active sound instances before deleting them.
- *
- * <h4>Example</h4>
- *
- * createjs.Sound.removeAllSounds();
- *
- * @method removeAllSounds
- * @static
- * @since 0.4.1
- */
- s.removeAllSounds = function() {
- s._idHash = {};
- s._preloadHash = {};
- SoundChannel.removeAll();
- if (s.activePlugin) {s.activePlugin.removeAllSounds();}
- };
-
- /**
- * Check if a source has been loaded by internal preloaders. This is necessary to ensure that sounds that are
- * not completed preloading will not kick off a new internal preload if they are played.
- *
- * <h4>Example</h4>
- *
- * var mySound = "assetPath/asset0.ogg";
- * if(createjs.Sound.loadComplete(mySound) {
- * createjs.Sound.play(mySound);
- * }
- *
- * @method loadComplete
- * @param {String} src The src or id that is being loaded.
- * @return {Boolean} If the src is already loaded.
- * @since 0.4.0
- * @static
- */
- s.loadComplete = function (src) {
- if (!s.isReady()) { return false; }
- var details = s._parsePath(src);
- if (details) {
- src = s._getSrcById(details.src).src;
- } else {
- src = s._getSrcById(src).src;
- }
- if(s._preloadHash[src] == undefined) {return false;}
- return (s._preloadHash[src][0] == true); // src only loads once, so if it's true for the first it's true for all
- };
-
- /**
- * Parse the path of a sound. Alternate extensions will be attempted in order if the
- * current extension is not supported
- * @method _parsePath
- * @param {String} value The path to an audio source.
- * @return {Object} A formatted object that can be registered with the {{#crossLink "Sound/activePlugin:property"}}{{/crossLink}}
- * and returned to a preloader like <a href="http://preloadjs.com" target="_blank">PreloadJS</a>.
- * @private
- * @static
- */
- s._parsePath = function (value) {
- if (typeof(value) != "string") {value = value.toString();}
-
- var match = value.match(s.FILE_PATTERN);
- if (match == null) {return false;}
-
- var name = match[4];
- var ext = match[5];
- var c = s.capabilities;
- var i = 0;
- while (!c[ext]) {
- ext = s.alternateExtensions[i++];
- if (i > s.alternateExtensions.length) { return null;} // no extensions are supported
- }
- value = value.replace("."+match[5], "."+ext);
-
- var ret = {name:name, src:value, extension:ext};
- return ret;
- };
-
- /**
- * Parse the path of a sound based on properties of src matching with supported extensions.
- * Returns false if none of the properties are supported
- * @method _parseSrc
- * @param {Object} value The paths to an audio source, indexed by extension type.
- * @return {Object} A formatted object that can be registered with the {{#crossLink "Sound/activePlugin:property"}}{{/crossLink}}
- * and returned to a preloader like <a href="http://preloadjs.com" target="_blank">PreloadJS</a>.
- * @private
- * @static
- */
- s._parseSrc = function (value) {
- var ret = {name:undefined, src:undefined, extension:undefined};
- var c = s.capabilities;
-
- for (var prop in value) {
- if(value.hasOwnProperty(prop) && c[prop]) {
- ret.src = value[prop];
- ret.extension = prop;
- break;
- }
- }
- if (!ret.src) {return false;} // no matches
-
- var i = ret.src.lastIndexOf("/");
- if (i != -1) {
- ret.name = ret.src.slice(i+1);
- } else {
- ret.name = ret.src;
- }
-
- return ret;
- };
-
- /* ---------------
- Static API.
- --------------- */
- /**
- * Play a sound and get a {{#crossLink "AbstractSoundInstance"}}{{/crossLink}} to control. If the sound fails to
- * play, an AbstractSoundInstance will still be returned, and have a playState of {{#crossLink "Sound/PLAY_FAILED:property"}}{{/crossLink}}.
- * Note that even on sounds with failed playback, you may still be able to call the {{#crossLink "AbstractSoundInstance/play"}}{{/crossLink}},
- * method, since the failure could be due to lack of available channels. If the src does not have a supported
- * extension or if there is no available plugin, a default AbstractSoundInstance will still be returned, which will
- * not play any audio, but will not generate errors.
- *
- * <h4>Example</h4>
- *
- * createjs.Sound.on("fileload", handleLoad);
- * createjs.Sound.registerSound("myAudioPath/mySound.mp3", "myID", 3);
- * function handleLoad(event) {
- * createjs.Sound.play("myID");
- * // store off AbstractSoundInstance for controlling
- * var myInstance = createjs.Sound.play("myID", {interrupt: createjs.Sound.INTERRUPT_ANY, loop:-1});
- * }
- *
- * NOTE: To create an audio sprite that has not already been registered, both startTime and duration need to be set.
- * This is only when creating a new audio sprite, not when playing using the id of an already registered audio sprite.
- *
- * @method play
- * @param {String} src The src or ID of the audio.
- * @param {Object | PlayPropsConfig} props A PlayPropsConfig instance, or an object that contains the parameters to
- * play a sound. See the {{#crossLink "PlayPropsConfig"}}{{/crossLink}} for more info.
- * @return {AbstractSoundInstance} A {{#crossLink "AbstractSoundInstance"}}{{/crossLink}} that can be controlled
- * after it is created.
- * @static
- */
- s.play = function (src, props) {
- var playProps = createjs.PlayPropsConfig.create(props);
- var instance = s.createInstance(src, playProps.startTime, playProps.duration);
- var ok = s._playInstance(instance, playProps);
- if (!ok) {instance._playFailed();}
- return instance;
- };
-
- /**
- * Creates a {{#crossLink "AbstractSoundInstance"}}{{/crossLink}} using the passed in src. If the src does not have a
- * supported extension or if there is no available plugin, a default AbstractSoundInstance will be returned that can be
- * called safely but does nothing.
- *
- * <h4>Example</h4>
- *
- * var myInstance = null;
- * createjs.Sound.on("fileload", handleLoad);
- * createjs.Sound.registerSound("myAudioPath/mySound.mp3", "myID", 3);
- * function handleLoad(event) {
- * myInstance = createjs.Sound.createInstance("myID");
- * // alternately we could call the following
- * myInstance = createjs.Sound.createInstance("myAudioPath/mySound.mp3");
- * }
- *
- * NOTE to create an audio sprite that has not already been registered, both startTime and duration need to be set.
- * This is only when creating a new audio sprite, not when playing using the id of an already registered audio sprite.
- *
- * @method createInstance
- * @param {String} src The src or ID of the audio.
- * @param {Number} [startTime=null] To create an audio sprite (with duration), the initial offset to start playback and loop from, in milliseconds.
- * @param {Number} [duration=null] To create an audio sprite (with startTime), the amount of time to play the clip for, in milliseconds.
- * @return {AbstractSoundInstance} A {{#crossLink "AbstractSoundInstance"}}{{/crossLink}} that can be controlled after it is created.
- * Unsupported extensions will return the default AbstractSoundInstance.
- * @since 0.4.0
- * @static
- */
- s.createInstance = function (src, startTime, duration) {
- if (!s.initializeDefaultPlugins()) { return new createjs.DefaultSoundInstance(src, startTime, duration); }
-
- var defaultPlayProps = s._defaultPlayPropsHash[src]; // for audio sprites, which create and store defaults by id
- src = s._getSrcById(src);
-
- var details = s._parsePath(src.src);
-
- var instance = null;
- if (details != null && details.src != null) {
- SoundChannel.create(details.src);
- if (startTime == null) { startTime = src.startTime; }
- instance = s.activePlugin.create(details.src, startTime, duration || src.duration);
-
- defaultPlayProps = defaultPlayProps || s._defaultPlayPropsHash[details.src];
- if (defaultPlayProps) {
- instance.applyPlayProps(defaultPlayProps);
- }
- } else {
- instance = new createjs.DefaultSoundInstance(src, startTime, duration);
- }
-
- instance.uniqueId = s._lastID++;
-
- return instance;
- };
-
- /**
- * Stop all audio (global stop). Stopped audio is reset, and not paused. To play audio that has been stopped,
- * call AbstractSoundInstance {{#crossLink "AbstractSoundInstance/play"}}{{/crossLink}}.
- *
- * <h4>Example</h4>
- *
- * createjs.Sound.stop();
- *
- * @method stop
- * @static
- */
- s.stop = function () {
- var instances = this._instances;
- for (var i = instances.length; i--; ) {
- instances[i].stop(); // NOTE stop removes instance from this._instances
- }
- };
-
- /**
- * Set the default playback properties for all new SoundInstances of the passed in src or ID.
- * See {{#crossLink "PlayPropsConfig"}}{{/crossLink}} for available properties.
- *
- * @method setDefaultPlayProps
- * @param {String} src The src or ID used to register the audio.
- * @param {Object | PlayPropsConfig} playProps The playback properties you would like to set.
- * @since 0.6.1
- */
- s.setDefaultPlayProps = function(src, playProps) {
- src = s._getSrcById(src);
- s._defaultPlayPropsHash[s._parsePath(src.src).src] = createjs.PlayPropsConfig.create(playProps);
- };
-
- /**
- * Get the default playback properties for the passed in src or ID. These properties are applied to all
- * new SoundInstances. Returns null if default does not exist.
- *
- * @method getDefaultPlayProps
- * @param {String} src The src or ID used to register the audio.
- * @returns {PlayPropsConfig} returns an existing PlayPropsConfig or null if one does not exist
- * @since 0.6.1
- */
- s.getDefaultPlayProps = function(src) {
- src = s._getSrcById(src);
- return s._defaultPlayPropsHash[s._parsePath(src.src).src];
- };
-
-
- /* ---------------
- Internal methods
- --------------- */
- /**
- * Play an instance. This is called by the static API, as well as from plugins. This allows the core class to
- * control delays.
- * @method _playInstance
- * @param {AbstractSoundInstance} instance The {{#crossLink "AbstractSoundInstance"}}{{/crossLink}} to start playing.
- * @param {PlayPropsConfig} playProps A PlayPropsConfig object.
- * @return {Boolean} If the sound can start playing. Sounds that fail immediately will return false. Sounds that
- * have a delay will return true, but may still fail to play.
- * @private
- * @static
- */
- s._playInstance = function (instance, playProps) {
- var defaultPlayProps = s._defaultPlayPropsHash[instance.src] || {};
- if (playProps.interrupt == null) {playProps.interrupt = defaultPlayProps.interrupt || s.defaultInterruptBehavior};
- if (playProps.delay == null) {playProps.delay = defaultPlayProps.delay || 0;}
- if (playProps.offset == null) {playProps.offset = instance.position;}
- if (playProps.loop == null) {playProps.loop = instance.loop;}
- if (playProps.volume == null) {playProps.volume = instance.volume;}
- if (playProps.pan == null) {playProps.pan = instance.pan;}
-
- if (playProps.delay == 0) {
- var ok = s._beginPlaying(instance, playProps);
- if (!ok) {return false;}
- } else {
- //Note that we can't pass arguments to proxy OR setTimeout (IE only), so just wrap the function call.
- // OJR WebAudio may want to handle this differently, so it might make sense to move this functionality into the plugins in the future
- var delayTimeoutId = setTimeout(function () {
- s._beginPlaying(instance, playProps);
- }, playProps.delay);
- instance.delayTimeoutId = delayTimeoutId;
- }
-
- this._instances.push(instance);
-
- return true;
- };
-
- /**
- * Begin playback. This is called immediately or after delay by {{#crossLink "Sound/playInstance"}}{{/crossLink}}.
- * @method _beginPlaying
- * @param {AbstractSoundInstance} instance A {{#crossLink "AbstractSoundInstance"}}{{/crossLink}} to begin playback.
- * @param {PlayPropsConfig} playProps A PlayPropsConfig object.
- * @return {Boolean} If the sound can start playing. If there are no available channels, or the instance fails to
- * start, this will return false.
- * @private
- * @static
- */
- s._beginPlaying = function (instance, playProps) {
- if (!SoundChannel.add(instance, playProps.interrupt)) {
- return false;
- }
- var result = instance._beginPlaying(playProps);
- if (!result) {
- var index = createjs.indexOf(this._instances, instance);
- if (index > -1) {this._instances.splice(index, 1);}
- return false;
- }
- return true;
- };
-
- /**
- * Get the source of a sound via the ID passed in with a register call. If no ID is found the value is returned
- * instead.
- * @method _getSrcById
- * @param {String} value The ID the sound was registered with.
- * @return {String} The source of the sound if it has been registered with this ID or the value that was passed in.
- * @private
- * @static
- */
- s._getSrcById = function (value) {
- return s._idHash[value] || {src: value};
- };
-
- /**
- * A sound has completed playback, been interrupted, failed, or been stopped. This method removes the instance from
- * Sound management. It will be added again, if the sound re-plays. Note that this method is called from the
- * instances themselves.
- * @method _playFinished
- * @param {AbstractSoundInstance} instance The instance that finished playback.
- * @private
- * @static
- */
- s._playFinished = function (instance) {
- SoundChannel.remove(instance);
- var index = createjs.indexOf(this._instances, instance);
- if (index > -1) {this._instances.splice(index, 1);} // OJR this will always be > -1, there is no way for an instance to exist without being added to this._instances
- };
-
- createjs.Sound = Sound;
-
- /**
- * An internal class that manages the number of active {{#crossLink "AbstractSoundInstance"}}{{/crossLink}} instances for
- * each sound type. This method is only used internally by the {{#crossLink "Sound"}}{{/crossLink}} class.
- *
- * The number of sounds is artificially limited by Sound in order to prevent over-saturation of a
- * single sound, as well as to stay within hardware limitations, although the latter may disappear with better
- * browser support.
- *
- * When a sound is played, this class ensures that there is an available instance, or interrupts an appropriate
- * sound that is already playing.
- * #class SoundChannel
- * @param {String} src The source of the instances
- * @param {Number} [max=1] The number of instances allowed
- * @constructor
- * @protected
- */
- function SoundChannel(src, max) {
- this.init(src, max);
- }
-
- /* ------------
- Static API
- ------------ */
- /**
- * A hash of channel instances indexed by source.
- * #property channels
- * @type {Object}
- * @static
- */
- SoundChannel.channels = {};
-
- /**
- * Create a sound channel. Note that if the sound channel already exists, this will fail.
- * #method create
- * @param {String} src The source for the channel
- * @param {Number} max The maximum amount this channel holds. The default is {{#crossLink "SoundChannel.maxDefault"}}{{/crossLink}}.
- * @return {Boolean} If the channels were created.
- * @static
- */
- SoundChannel.create = function (src, max) {
- var channel = SoundChannel.get(src);
- if (channel == null) {
- SoundChannel.channels[src] = new SoundChannel(src, max);
- return true;
- }
- return false;
- };
- /**
- * Delete a sound channel, stop and delete all related instances. Note that if the sound channel does not exist, this will fail.
- * #method remove
- * @param {String} src The source for the channel
- * @return {Boolean} If the channels were deleted.
- * @static
- */
- SoundChannel.removeSrc = function (src) {
- var channel = SoundChannel.get(src);
- if (channel == null) {return false;}
- channel._removeAll(); // this stops and removes all active instances
- delete(SoundChannel.channels[src]);
- return true;
- };
- /**
- * Delete all sound channels, stop and delete all related instances.
- * #method removeAll
- * @static
- */
- SoundChannel.removeAll = function () {
- for(var channel in SoundChannel.channels) {
- SoundChannel.channels[channel]._removeAll(); // this stops and removes all active instances
- }
- SoundChannel.channels = {};
- };
- /**
- * Add an instance to a sound channel.
- * #method add
- * @param {AbstractSoundInstance} instance The instance to add to the channel
- * @param {String} interrupt The interrupt value to use. Please see the {{#crossLink "Sound/play"}}{{/crossLink}}
- * for details on interrupt modes.
- * @return {Boolean} The success of the method call. If the channel is full, it will return false.
- * @static
- */
- SoundChannel.add = function (instance, interrupt) {
- var channel = SoundChannel.get(instance.src);
- if (channel == null) {return false;}
- return channel._add(instance, interrupt);
- };
- /**
- * Remove an instance from the channel.
- * #method remove
- * @param {AbstractSoundInstance} instance The instance to remove from the channel
- * @return The success of the method call. If there is no channel, it will return false.
- * @static
- */
- SoundChannel.remove = function (instance) {
- var channel = SoundChannel.get(instance.src);
- if (channel == null) {return false;}
- channel._remove(instance);
- return true;
- };
- /**
- * Get the maximum number of sounds you can have in a channel.
- * #method maxPerChannel
- * @return {Number} The maximum number of sounds you can have in a channel.
- */
- SoundChannel.maxPerChannel = function () {
- return p.maxDefault;
- };
- /**
- * Get a channel instance by its src.
- * #method get
- * @param {String} src The src to use to look up the channel
- * @static
- */
- SoundChannel.get = function (src) {
- return SoundChannel.channels[src];
- };
-
- var p = SoundChannel.prototype;
- p.constructor = SoundChannel;
-
- /**
- * The source of the channel.
- * #property src
- * @type {String}
- */
- p.src = null;
-
- /**
- * The maximum number of instances in this channel. -1 indicates no limit
- * #property max
- * @type {Number}
- */
- p.max = null;
-
- /**
- * The default value to set for max, if it isn't passed in. Also used if -1 is passed.
- * #property maxDefault
- * @type {Number}
- * @default 100
- * @since 0.4.0
- */
- p.maxDefault = 100;
-
- /**
- * The current number of active instances.
- * #property length
- * @type {Number}
- */
- p.length = 0;
-
- /**
- * Initialize the channel.
- * #method init
- * @param {String} src The source of the channel
- * @param {Number} max The maximum number of instances in the channel
- * @protected
- */
- p.init = function (src, max) {
- this.src = src;
- this.max = max || this.maxDefault;
- if (this.max == -1) {this.max = this.maxDefault;}
- this._instances = [];
- };
-
- /**
- * Get an instance by index.
- * #method get
- * @param {Number} index The index to return.
- * @return {AbstractSoundInstance} The AbstractSoundInstance at a specific instance.
- */
- p._get = function (index) {
- return this._instances[index];
- };
-
- /**
- * Add a new instance to the channel.
- * #method add
- * @param {AbstractSoundInstance} instance The instance to add.
- * @return {Boolean} The success of the method call. If the channel is full, it will return false.
- */
- p._add = function (instance, interrupt) {
- if (!this._getSlot(interrupt, instance)) {return false;}
- this._instances.push(instance);
- this.length++;
- return true;
- };
-
- /**
- * Remove an instance from the channel, either when it has finished playing, or it has been interrupted.
- * #method remove
- * @param {AbstractSoundInstance} instance The instance to remove
- * @return {Boolean} The success of the remove call. If the instance is not found in this channel, it will
- * return false.
- */
- p._remove = function (instance) {
- var index = createjs.indexOf(this._instances, instance);
- if (index == -1) {return false;}
- this._instances.splice(index, 1);
- this.length--;
- return true;
- };
-
- /**
- * Stop playback and remove all instances from the channel. Usually in response to a delete call.
- * #method removeAll
- */
- p._removeAll = function () {
- // Note that stop() removes the item from the list
- for (var i=this.length-1; i>=0; i--) {
- this._instances[i].stop();
- }
- };
-
- /**
- * Get an available slot depending on interrupt value and if slots are available.
- * #method getSlot
- * @param {String} interrupt The interrupt value to use.
- * @param {AbstractSoundInstance} instance The sound instance that will go in the channel if successful.
- * @return {Boolean} Determines if there is an available slot. Depending on the interrupt mode, if there are no slots,
- * an existing AbstractSoundInstance may be interrupted. If there are no slots, this method returns false.
- */
- p._getSlot = function (interrupt, instance) {
- var target, replacement;
-
- if (interrupt != Sound.INTERRUPT_NONE) {
- // First replacement candidate
- replacement = this._get(0);
- if (replacement == null) {
- return true;
- }
- }
-
- for (var i = 0, l = this.max; i < l; i++) {
- target = this._get(i);
-
- // Available Space
- if (target == null) {
- return true;
- }
-
- // Audio is complete or not playing
- if (target.playState == Sound.PLAY_FINISHED ||
- target.playState == Sound.PLAY_INTERRUPTED ||
- target.playState == Sound.PLAY_FAILED) {
- replacement = target;
- break;
- }
-
- if (interrupt == Sound.INTERRUPT_NONE) {
- continue;
- }
-
- // Audio is a better candidate than the current target, according to playhead
- if ((interrupt == Sound.INTERRUPT_EARLY && target.position < replacement.position) ||
- (interrupt == Sound.INTERRUPT_LATE && target.position > replacement.position)) {
- replacement = target;
- }
- }
-
- if (replacement != null) {
- replacement._interrupt();
- this._remove(replacement);
- return true;
- }
- return false;
- };
-
- p.toString = function () {
- return "[Sound SoundChannel]";
- };
- // do not add SoundChannel to namespace
-
- }());
-
-