diff --git a/src/soundjs/Sound.js b/src/soundjs/Sound.js
index 13882bfe..3e863cf3 100644
--- a/src/soundjs/Sound.js
+++ b/src/soundjs/Sound.js
@@ -1,1802 +1,141 @@
-/*
- * 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:
- *
- * - Installing audio playback Plugins
- * - Registering (and preloading) sounds
- * - Creating and playing sounds
- * - Master volume, mute, and stop controls for all sounds at once
- *
- *
- * Controlling Sounds
- * Playing sounds creates {{#crossLink "AbstractSoundInstance"}}{{/crossLink}} instances, which can be controlled
- * individually.
- *
- * - Pause, resume, seek, and stop sounds
- * - Control a sound's volume, mute, and pan
- * - Listen for events on sound instances to get notified when they finish, loop, or fail
- *
- *
- * Example
- *
- * 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;
- * }
- *
- * Browser Support
- * Audio will work in browsers which support Web Audio (http://caniuse.com/audio-api)
- * or HTMLAudioElement (http://caniuse.com/audio).
- * 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 Cordova.Media.
- * 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 () {
+createjs.Sound = (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.
- *
- * Registering and Preloading
- * Before you can play a sound, it must 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
- * PreloadJS, 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.
- *
- * Playback
- * 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.
- *
- * Plugins
- * 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}}.
- *
- * Example
- *
- * 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);
- *
- * Audio Sprites
- * 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.
- *
- * Example
- *
- * 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");
- *
- * Mobile Playback
- * 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.
- *
- * -
- * 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.
- *
- * -
- * 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")
- *
- * -
- * 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.
- *
- * -
- * When using the EaselJS Touch class,
- * 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:
- *
- * - Set the `allowDefault` property on the Touch class constructor to `true` (defaults to `false`).
- * - Set the `preventSelection` property on the EaselJS `Stage` to `false`.
- *
- * These settings may change how your application behaves, and are not recommended.
- *
- *
- *
- * Loading Alternate Paths and Extension-less Files
- * 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.
- *
- * Note: an id is required for playback.
- *
- * Example
- *
- * 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);
- *
- * Known Browser and OS issues
- * IE 9 HTML Audio limitations
- * - 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.
- * - MP3 encoding will not always work for audio tags, particularly in Internet Explorer. We've found default
- * encoding with 64kbps works.
- * - Occasionally very short samples will get cut off.
- * - 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.
- *
- * Firefox 25 Web Audio limitations
- * - mp3 audio files do not load properly on all windows machines, reported
- * here.
- * For this reason it is recommended to pass another FF supported type (ie ogg) first until this bug is resolved, if
- * possible.
+ var InterruptMode = createjs.metadata.sound.InterruptMode,
+ PlayState = createjs.metadata.sound.PlayState,
+ SoundParser = createjs.soundUtils.SoundParser,
+ SoundFactory = createjs.soundUtils.SoundFactory;
+
+ var _soundPlugin = SoundFactory.getSoundPlugin(),
+ _soundVolume = SoundFactory.getSoundVolume(),
+ _soundRegister = SoundFactory.getSoundRegister(),
+ _soundInstance = SoundFactory.getSoundInstance(),
+ _soundParser = SoundFactory.getSoundParser(),
+ _soundEventHandler = SoundFactory.getSoundEventHandler();
- * Safari limitations
- * - Safari requires Quicktime to be installed for audio playback.
- *
- * iOS 6 Web Audio limitations
- * - Sound is initially locked, and must be unlocked via a user-initiated event. Please see the section on
- * Mobile Playback above.
- * - 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.
- *
- *
- * Android HTML Audio limitations
- * - We have no control over audio volume. Only the user can set volume on their device.
- * - We can only play audio inside a user event (touch/click). This currently means you cannot loop sound or use
- * a delay.
- *
- * Web Audio and PreloadJS
- * - 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.
- *
- * @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";
+ Sound.INTERRUPT_ANY = InterruptMode.INTERRUPT_ANY;
+ Sound.INTERRUPT_EARLY = InterruptMode.INTERRUPT_EARLY;
+ Sound.INTERRUPT_LATE = InterruptMode.INTERRUPT_LATE;
+ Sound.INTERRUPT_NONE = InterruptMode.INTERRUPT_NONE;
- /**
- * 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";
+ Sound.PLAY_INITED = PlayState.PLAY_INITED;
+ Sound.PLAY_SUCCEEDED = PlayState.PLAY_SUCCEEDED;
+ Sound.PLAY_INTERRUPTED = PlayState.PLAY_INTERRUPTED;
+ Sound.PLAY_FINISHED = PlayState.PLAY_FINISHED;
+ Sound.PLAY_FAILED = PlayState.PLAY_FAILED;
- /**
- * A list of the default supported extensions that Sound will try 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 http://en.wikipedia.org/wiki/Audio_file_format.
- * A very detailed list of file formats can be found at http://www.fileinfo.com/filetypes/audio.
- * @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"];
+ Sound.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 http://html5doctor.com/html5-audio-the-state-of-play/.
- * @property EXTENSION_MAP
- * @type {Object}
- * @since 0.4.0
- * @default {m4a:"mp4"}
- * @static
- */
- s.EXTENSION_MAP = {
+ Sound.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.
- *
- * Example
- *
- * 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;
-
+ Sound.FILE_PATTERN = SoundParser.FILE_PATTERN;
// 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.
- *
- * Example
- *
- * 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;
- };
-
- /**
- * @method getMasterVolume
- * @deprecated Use the {{#crossLink "Sound/volume:property"}}{{/crossLink}} property instead.
- */
- // 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);
- }
- }
- };
-
- /**
- * @method setVolume
- * @deprecated Use the {{#crossLink "Sound/volume:property"}}{{/crossLink}} property instead.
- */
- // 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.
- *
- * Example
- *
- * 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;
- };
-
- /**
- * @method getMute
- * @deprecated Use the {{#crossLink "Sound/muted:property"}}{{/crossLink}} property instead.
- */
- // 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);
- }
- }
- };
-
- /**
- * @method setMute
- * @deprecated Use the {{#crossLink "Sound/muted:property"}}{{/crossLink}} property instead.
- */
- // 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:
- *
- * - panning: If the plugin can pan audio from left to right
- * - volume; If the plugin can control audio volume.
- * - tracks: The maximum number of audio tracks that can be played back at a time. This will be -1
- * if there is no known limit.
- *
An entry for each file type in {{#crossLink "Sound/SUPPORTED_EXTENSIONS:property"}}{{/crossLink}}:
- * - mp3: If MP3 audio is supported.
- * - ogg: If OGG audio is supported.
- * - wav: If WAV audio is supported.
- * - mpeg: If MPEG audio is supported.
- * - m4a: If M4A audio is supported.
- * - mp4: If MP4 audio is supported.
- * - aiff: If aiff audio is supported.
- * - wma: If wma audio is supported.
- * - mid: If mid audio is supported.
- *
- *
- * You can get a specific capability of the active plugin using standard object notation
- *
- * Example
- *
- * 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;
- };
-
- /**
- * @method getCapabilities
- * @deprecated Use the {{#crossLink "Sound/capabilities:property"}}{{/crossLink}} property instead.
- */
- // Sound.getCapabilities is @deprecated. Remove for 1.1+
- s.getCapabilities = createjs.deprecate(s._getCapabilities, "Sound.getCapabilities");
+
+ Sound.getVolume = _soundVolume.getVolume;
+ Sound.setVolume = _soundVolume.setVolume;
+ Sound.getMute = _soundVolume.getMute;
+ Sound.setMute = _soundVolume.setMute;
+ Sound.getCapabilities = _soundVolume.getCapabilities;
Object.defineProperties(s, {
- volume: { get: s._getMasterVolume, set: s._setMasterVolume },
- muted: { get: s._getMute, set: s._setMute },
- capabilities: { get: s._getCapabilities }
+ volume: { get: _getMasterVolume, set: _setMasterVolume },
+ muted: { get: _getMute, set: _setMute },
+ capabilities: { get: _getCapabilities },
+ defaultInterruptBehavior: { get: _getDefaultInterruptBehavior, set: _setDefaultInterruptBehavior },
+ alternateExtensions: { get: _getAlternateExtensions, set: _setAlternateExtensions },
+ activePlugin: { get: _getActivePlugin, set: _setActivePlugin }
});
-// 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 event.src
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 event.src
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
- */
-
+ Sound.addEventListener = _soundEventHandler.addEventListener;
+ Sound.removeEventListener = _soundEventHandler.removeEventListener;
+ Sound.removeAllEventListeners = _soundEventHandler.removeAllEventListeners;
+ Sound.dispatchEvent = _soundEventHandler.dispatchEvent;
+ Sound.hasEventListener = _soundEventHandler.hasEventListener;
+ Sound._listeners = _soundEventHandler._listeners;
// Class Public Methods
- /**
- * Get the preload rules to allow Sound to be used as a plugin by PreloadJS.
- * 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:
- * - 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.
- * - types: A list of file types that are supported by Sound (currently supports "sound").
- * - extensions: A list of file extensions that are supported by Sound (see {{#crossLink "Sound/SUPPORTED_EXTENSIONS:property"}}{{/crossLink}}).
- * @static
- * @private
- */
- s.getPreloadHandlers = function () {
+ Sound.getPreloadHandlers = getPreloadHandlers;
+ Sound.registerPlugins = _soundPlugin.registerPlugins;
+ Sound.initializeDefaultPlugins = _soundPlugin.initializeDefaultPlugins;
+ Sound.isReady = _soundPlugin.isReady;
+ Sound.initLoad = _soundRegister.initLoad;
+ Sound.registerSound = _soundRegister.registerSound;
+ Sound.registerSounds = _soundRegister.registerSounds;
+ Sound.removeSound = _soundRegister.removeSound;
+ Sound.removeSounds = _soundRegister.removeSounds;
+ Sound.removeAllSounds = _soundRegister.removeAllSounds;
+ Sound.loadComplete = _soundRegister.loadComplete;
+ Sound.play = _soundInstance.play;
+ Sound.createInstance = _soundInstance.createInstance;
+ Sound.stop = _soundInstance.setStop;
+ Sound.setDefaultPlayProps = _soundRegister.setDefaultPlayProps;
+ Sound.getDefaultPlayProps = _soundRegister.getDefaultPlayProps;
+
+ return Sound;
+
+ function getPreloadHandlers() {
return {
- callback:createjs.proxy(s.initLoad, s),
+ callback:createjs.proxy(Sound.initLoad, s),
types:["sound"],
- extensions:s.SUPPORTED_EXTENSIONS
+ extensions:Sound.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.
- *
- * Example
- *
- * 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}}.
- *
- * Example
- *
- * 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.
- *
- * Example
- * 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 PreloadJS. 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
- * PreloadJS. 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.
- *
- * Example
- *
- * 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}.
- * id used to play the sound later, in the same manner as a sound src with an id.
- * startTime is the initial offset to start playback and loop from, in milliseconds.
- * duration is the amount of time to play the clip for, in milliseconds.
- * 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.
- *
- * Example
- *
- * 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}}: {src:srcURI, id:ID, data:Data}
- * 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}}.
- *
Note this will stop playback on active instances playing this sound before deleting them.
- *
Note if you passed in a basePath, you need to pass it or prepend it to the src here.
- *
- * Example
- *
- * 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}}.
- *
Note this will stop playback on active instances playing this audio before deleting them.
- *
Note if you passed in a basePath, you need to pass it or prepend it to the src here.
- *
- * Example
- *
- * 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}}: {srcOrID:srcURIorID}
.
- * 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}}.
- *
Note this will stop playback on all active sound instances before deleting them.
- *
- * Example
- *
- * 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.
- *
- * Example
- *
- * 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 PreloadJS.
- * @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 PreloadJS.
- * @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.
- *
- * Example
- *
- * 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.
- *
- * Example
- *
- * 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}}.
- *
- * Example
- *
- * 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;
- };
+ function _getMasterVolume() {
+ return _soundVolume.volume;
+ }
- /**
- * 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();
- }
- };
+ function _setMasterVolume(value) {
+ _soundVolume.volume = value;
+ }
- /**
- * 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;
+ function _getMute() {
+ return _soundVolume.muted;
+ }
- if (interrupt != Sound.INTERRUPT_NONE) {
- // First replacement candidate
- replacement = this._get(0);
- if (replacement == null) {
- return true;
- }
- }
+ function _setMute(value) {
+ _soundVolume.muted = value;
+ }
- for (var i = 0, l = this.max; i < l; i++) {
- target = this._get(i);
+ function _getCapabilities() {
+ return _soundVolume.capabilities;
+ }
- // Available Space
- if (target == null) {
- return true;
- }
+ function _getDefaultInterruptBehavior() {
+ return _soundInstance.defaultInterruptBehavior;
+ }
- // 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;
- }
+ function _setDefaultInterruptBehavior(value) {
+ _soundInstance.defaultInterruptBehavior = value;
+ }
- if (interrupt == Sound.INTERRUPT_NONE) {
- continue;
- }
+ function _getAlternateExtensions() {
+ return _soundParser.alternateExtensions;
+ }
- // 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;
- }
- }
+ function _setAlternateExtensions(value) {
+ _soundParser.alternateExtensions = value;
+ }
- if (replacement != null) {
- replacement._interrupt();
- this._remove(replacement);
- return true;
- }
- return false;
- };
+ function _getActivePlugin() {
+ return _soundPlugin.activePlugin;
+ }
- p.toString = function () {
- return "[Sound SoundChannel]";
- };
- // do not add SoundChannel to namespace
+ function _setActivePlugin(value) {
+ _soundPlugin.activePlugin = value;
+ }
}());
diff --git a/src/soundjs/docs/Sound.js b/src/soundjs/docs/Sound.js
new file mode 100644
index 00000000..6eef6617
--- /dev/null
+++ b/src/soundjs/docs/Sound.js
@@ -0,0 +1,1117 @@
+/*
+ * 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.
+ */
+/**
+ * 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:
+ *
+ * - Installing audio playback Plugins
+ * - Registering (and preloading) sounds
+ * - Creating and playing sounds
+ * - Master volume, mute, and stop controls for all sounds at once
+ *
+ *
+ * Controlling Sounds
+ * Playing sounds creates {{#crossLink "AbstractSoundInstance"}}{{/crossLink}} instances, which can be controlled
+ * individually.
+ *
+ * - Pause, resume, seek, and stop sounds
+ * - Control a sound's volume, mute, and pan
+ * - Listen for events on sound instances to get notified when they finish, loop, or fail
+ *
+ *
+ * Example
+ *
+ * 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;
+ * }
+ *
+ * Browser Support
+ * Audio will work in browsers which support Web Audio (http://caniuse.com/audio-api)
+ * or HTMLAudioElement (http://caniuse.com/audio).
+ * 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 Cordova.Media.
+ * 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
+ */
+/**
+ * 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.
+ *
+ * Registering and Preloading
+ * Before you can play a sound, it must 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
+ * PreloadJS, 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.
+ *
+ * Playback
+ * 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.
+ *
+ * Plugins
+ * 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}}.
+ *
+ * Example
+ *
+ * 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);
+ *
+ * Audio Sprites
+ * 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.
+ *
+ * Example
+ *
+ * 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");
+ *
+ * Mobile Playback
+ * 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.
+ *
+ * -
+ * 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.
+ *
+ * -
+ * 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")
+ *
+ * -
+ * 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.
+ *
+ * -
+ * When using the EaselJS Touch class,
+ * 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:
+ *
+ * - Set the `allowDefault` property on the Touch class constructor to `true` (defaults to `false`).
+ * - Set the `preventSelection` property on the EaselJS `Stage` to `false`.
+ *
+ * These settings may change how your application behaves, and are not recommended.
+ *
+ *
+ *
+ * Loading Alternate Paths and Extension-less Files
+ * 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.
+ *
+ * Note: an id is required for playback.
+ *
+ * Example
+ *
+ * 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);
+ *
+ * Known Browser and OS issues
+ * IE 9 HTML Audio limitations
+ * - 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.
+ * - MP3 encoding will not always work for audio tags, particularly in Internet Explorer. We've found default
+ * encoding with 64kbps works.
+ * - Occasionally very short samples will get cut off.
+ * - 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.
+ *
+ * Firefox 25 Web Audio limitations
+ * - mp3 audio files do not load properly on all windows machines, reported
+ * here.
+ * For this reason it is recommended to pass another FF supported type (ie ogg) first until this bug is resolved, if
+ * possible.
+
+ * Safari limitations
+ * - Safari requires Quicktime to be installed for audio playback.
+ *
+ * iOS 6 Web Audio limitations
+ * - Sound is initially locked, and must be unlocked via a user-initiated event. Please see the section on
+ * Mobile Playback above.
+ * - 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.
+ *
+ *
+ * Android HTML Audio limitations
+ * - We have no control over audio volume. Only the user can set volume on their device.
+ * - We can only play audio inside a user event (touch/click). This currently means you cannot loop sound or use
+ * a delay.
+ *
+ * Web Audio and PreloadJS
+ * - 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.
+ *
+ * @class Sound
+ * @static
+ * @uses EventDispatcher
+ */
+/**
+ * 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
+ */
+/**
+ * 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
+ */
+/**
+ * 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
+ */
+/**
+ * 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
+ */
+/**
+ * Defines the playState of an instance that is still initializing.
+ * @property PLAY_INITED
+ * @type {String}
+ * @default playInited
+ * @static
+ */
+/**
+ * Defines the playState of an instance that is currently playing or paused.
+ * @property PLAY_SUCCEEDED
+ * @type {String}
+ * @default playSucceeded
+ * @static
+ */
+/**
+ * Defines the playState of an instance that was interrupted by another instance.
+ * @property PLAY_INTERRUPTED
+ * @type {String}
+ * @default playInterrupted
+ * @static
+ */
+/**
+ * Defines the playState of an instance that completed playback.
+ * @property PLAY_FINISHED
+ * @type {String}
+ * @default playFinished
+ * @static
+ */
+/**
+ * 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
+ */
+/**
+ * A list of the default supported extensions that Sound will try 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 http://en.wikipedia.org/wiki/Audio_file_format.
+ * A very detailed list of file formats can be found at http://www.fileinfo.com/filetypes/audio.
+ * @property SUPPORTED_EXTENSIONS
+ * @type {Array[String]}
+ * @default ["mp3", "ogg", "opus", "mpeg", "wav", "m4a", "mp4", "aiff", "wma", "mid"]
+ * @since 0.4.0
+ * @static
+ */
+/**
+ * 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 http://html5doctor.com/html5-audio-the-state-of-play/.
+ * @property EXTENSION_MAP
+ * @type {Object}
+ * @since 0.4.0
+ * @default {m4a:"mp4"}
+ * @static
+ */
+/**
+ * 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
+ */
+/**
+ * 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
+ */
+/**
+ * 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.
+ *
+ * Example
+ *
+ * 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
+ */
+/**
+ * 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
+ */
+/**
+ * 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.
+ *
+ * Example
+ *
+ * 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
+ */
+/**
+ * Use the {{#crossLink "Sound/volume:property"}}{{/crossLink}} property instead.
+ * @method _getMasterVolume
+ * @private
+ * @static
+ * @return {Number}
+ **/
+/**
+ * @method getMasterVolume
+ * @deprecated Use the {{#crossLink "Sound/volume:property"}}{{/crossLink}} property instead.
+ */
+/**
+ * Use the {{#crossLink "Sound/volume:property"}}{{/crossLink}} property instead.
+ * @method _setMasterVolume
+ * @static
+ * @private
+ **/
+/**
+ * @method setVolume
+ * @deprecated Use the {{#crossLink "Sound/volume:property"}}{{/crossLink}} property instead.
+ */
+/**
+ * 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.
+ *
+ * Example
+ *
+ * createjs.Sound.muted = true;
+ *
+ *
+ * @property muted
+ * @type {Boolean}
+ * @default false
+ * @static
+ * @since 0.6.1
+ */
+/**
+ * Use the {{#crossLink "Sound/muted:property"}}{{/crossLink}} property instead.
+ * @method _getMute
+ * @returns {Boolean}
+ * @static
+ * @private
+ */
+/**
+ * @method getMute
+ * @deprecated Use the {{#crossLink "Sound/muted:property"}}{{/crossLink}} property instead.
+ */
+/**
+ * Use the {{#crossLink "Sound/muted:property"}}{{/crossLink}} property instead.
+ * @method _setMute
+ * @param {Boolean} value The muted value
+ * @static
+ * @private
+ */
+/**
+ * @method setMute
+ * @deprecated Use the {{#crossLink "Sound/muted:property"}}{{/crossLink}} property instead.
+ */
+/**
+ * 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:
+ *
+ * - panning: If the plugin can pan audio from left to right
+ * - volume; If the plugin can control audio volume.
+ * - tracks: The maximum number of audio tracks that can be played back at a time. This will be -1
+ * if there is no known limit.
+ *
An entry for each file type in {{#crossLink "Sound/SUPPORTED_EXTENSIONS:property"}}{{/crossLink}}:
+ * - mp3: If MP3 audio is supported.
+ * - ogg: If OGG audio is supported.
+ * - wav: If WAV audio is supported.
+ * - mpeg: If MPEG audio is supported.
+ * - m4a: If M4A audio is supported.
+ * - mp4: If MP4 audio is supported.
+ * - aiff: If aiff audio is supported.
+ * - wma: If wma audio is supported.
+ * - mid: If mid audio is supported.
+ *
+ *
+ * You can get a specific capability of the active plugin using standard object notation
+ *
+ * Example
+ *
+ * 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
+ */
+/**
+ * @method getCapabilities
+ * @deprecated Use the {{#crossLink "Sound/capabilities:property"}}{{/crossLink}} property instead.
+ */
+/**
+ * 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
+ */
+/**
+ * Used internally to assign unique IDs to each AbstractSoundInstance.
+ * @property _lastID
+ * @type {Number}
+ * @static
+ * @private
+ */
+/**
+ * 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
+ */
+/**
+ * An object hash storing objects with sound sources, startTime, and duration via there corresponding ID.
+ * @property _idHash
+ * @type {Object}
+ * @private
+ * @static
+ */
+/**
+ * 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
+ */
+/**
+ * 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
+ */
+/**
+ * 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 event.src
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 event.src
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
+ */
+/**
+ * Get the preload rules to allow Sound to be used as a plugin by PreloadJS.
+ * 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:
+ * - 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.
+ * - types: A list of file types that are supported by Sound (currently supports "sound").
+ * - extensions: A list of file extensions that are supported by Sound (see {{#crossLink "Sound/SUPPORTED_EXTENSIONS:property"}}{{/crossLink}}).
+ * @static
+ * @private
+ */
+/**
+ * Used to dispatch fileload events from internal loading.
+ * @method _handleLoadComplete
+ * @param event A loader event.
+ * @private
+ * @static
+ * @since 0.6.0
+ */
+/**
+ * Used to dispatch error events from internal preloading.
+ * @param event
+ * @private
+ * @since 0.6.0
+ * @static
+ */
+/**
+ * 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
+ */
+/**
+ * Register a list of Sound plugins, in order of precedence. To register a single plugin, pass a single element in the array.
+ *
+ * Example
+ *
+ * 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
+ */
+/**
+ * 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}}.
+ *
+ * Example
+ *
+ * if (!createjs.initializeDefaultPlugins()) { return; }
+ *
+ * @method initializeDefaultPlugins
+ * @returns {Boolean} True if a plugin was initialized, false otherwise.
+ * @since 0.4.0
+ * @static
+ */
+/**
+ * Determines if Sound has been initialized, and a plugin has been activated.
+ *
+ * Example
+ * 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
+ */
+/**
+ * Process manifest items from PreloadJS. 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
+ */
+/**
+ * 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
+ */
+/**
+ * Register an audio file for loading and future playback in Sound. This is automatically called when using
+ * PreloadJS. 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.
+ *
+ * Example
+ *
+ * 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}.
+ * id used to play the sound later, in the same manner as a sound src with an id.
+ * startTime is the initial offset to start playback and loop from, in milliseconds.
+ * duration is the amount of time to play the clip for, in milliseconds.
+ * 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
+ */
+/**
+ * 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.
+ *
+ * Example
+ *
+ * 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}}: {src:srcURI, id:ID, data:Data}
+ * 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
+ */
+/**
+ * Remove a sound that has been registered with {{#crossLink "Sound/registerSound"}}{{/crossLink}} or
+ * {{#crossLink "Sound/registerSounds"}}{{/crossLink}}.
+ *
Note this will stop playback on active instances playing this sound before deleting them.
+ *
Note if you passed in a basePath, you need to pass it or prepend it to the src here.
+ *
+ * Example
+ *
+ * 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
+ */
+/**
+ * Remove an array of audio files that have been registered with {{#crossLink "Sound/registerSound"}}{{/crossLink}} or
+ * {{#crossLink "Sound/registerSounds"}}{{/crossLink}}.
+ *
Note this will stop playback on active instances playing this audio before deleting them.
+ *
Note if you passed in a basePath, you need to pass it or prepend it to the src here.
+ *
+ * Example
+ *
+ * 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}}: {srcOrID:srcURIorID}
.
+ * 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
+ */
+/**
+ * Remove all sounds that have been registered with {{#crossLink "Sound/registerSound"}}{{/crossLink}} or
+ * {{#crossLink "Sound/registerSounds"}}{{/crossLink}}.
+ *
Note this will stop playback on all active sound instances before deleting them.
+ *
+ * Example
+ *
+ * createjs.Sound.removeAllSounds();
+ *
+ * @method removeAllSounds
+ * @static
+ * @since 0.4.1
+ */
+/**
+ * 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.
+ *
+ * Example
+ *
+ * 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
+ */
+/**
+ * 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 PreloadJS.
+ * @private
+ * @static
+ */
+/**
+ * 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 PreloadJS.
+ * @private
+ * @static
+ */
+/* ---------------
+ 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.
+ *
+ * Example
+ *
+ * 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
+ */
+/**
+ * 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.
+ *
+ * Example
+ *
+ * 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
+ */
+/**
+ * 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}}.
+ *
+ * Example
+ *
+ * createjs.Sound.stop();
+ *
+ * @method stop
+ * @static
+ */
+/**
+ * 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
+ */
+/**
+ * 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
+ */
+/* ---------------
+ 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
+ */
+/**
+ * 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
+ */
+/**
+ * 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
+ */
+/**
+ * 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
+ */
+/**
+ * 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
+ */
+/* ------------
+ Static API
+ ------------ */
+/**
+ * A hash of channel instances indexed by source.
+ * #property channels
+ * @type {Object}
+ * @static
+ */
+/**
+ * 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
+ */
+/**
+ * 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
+ */
+/**
+ * Delete all sound channels, stop and delete all related instances.
+ * #method removeAll
+ * @static
+ */
+/**
+ * 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
+ */
+/**
+ * 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
+ */
+/**
+ * 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.
+ */
+/**
+ * Get a channel instance by its src.
+ * #method get
+ * @param {String} src The src to use to look up the channel
+ * @static
+ */
+/**
+ * The source of the channel.
+ * #property src
+ * @type {String}
+ */
+/**
+ * The maximum number of instances in this channel. -1 indicates no limit
+ * #property max
+ * @type {Number}
+ */
+/**
+ * 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
+ */
+/**
+ * The current number of active instances.
+ * #property length
+ * @type {Number}
+ */
+/**
+ * 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
+ */
+/**
+ * Get an instance by index.
+ * #method get
+ * @param {Number} index The index to return.
+ * @return {AbstractSoundInstance} The AbstractSoundInstance at a specific instance.
+ */
+/**
+ * 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.
+ */
+/**
+ * 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.
+ */
+/**
+ * Stop playback and remove all instances from the channel. Usually in response to a delete call.
+ * #method removeAll
+ */
+/**
+ * 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.
+ */
\ No newline at end of file
diff --git a/src/soundjs/metadata/SoundMetadata.js b/src/soundjs/metadata/SoundMetadata.js
new file mode 100644
index 00000000..21b724a9
--- /dev/null
+++ b/src/soundjs/metadata/SoundMetadata.js
@@ -0,0 +1,25 @@
+// namespace:
+this.createjs = this.createjs || {};
+
+createjs.metadata.sound = (function () {
+
+ var InterruptMode = {
+ INTERRUPT_ANY: "any",
+ INTERRUPT_EARLY: "early",
+ INTERRUPT_LATE: "late",
+ INTERRUPT_NONE: "none"
+ };
+
+ var PlayState = {
+ PLAY_INITED: "playInited",
+ PLAY_SUCCEEDED: "playSucceeded",
+ PLAY_INTERRUPTED: "playInterrupted",
+ PLAY_FINISHED: "playFinished",
+ PLAY_FAILED: "playFailed"
+ };
+
+ return {
+ InterruptMode: InterruptMode,
+ PlayState: PlayState
+ };
+})();
\ No newline at end of file
diff --git a/src/soundjs/sound-utils/SoundChannel.js b/src/soundjs/sound-utils/SoundChannel.js
new file mode 100644
index 00000000..2ad7ed6c
--- /dev/null
+++ b/src/soundjs/sound-utils/SoundChannel.js
@@ -0,0 +1,190 @@
+// TODO remove SoundChannel from namespace
+// namespace:
+this.createjs = this.createjs || {};
+createjs.soundUtils = createjs.soundUtils || {};
+
+createjs.soundUtils.SoundChannel = (function () {
+
+ var SoundChannel = function (source, max) {
+ this.init(source, max);
+ };
+
+ var prototype = SoundChannel.prototype;
+ prototype.constructor = SoundChannel;
+
+ prototype.init = function (source, max) {
+ this.src = source;
+ this.max = max || this.maxDefault;
+
+ if (this.max == -1) {
+ this.max = this.maxDefault;
+ }
+
+ this._instances = [];
+ this.src = null;
+ this.max = null;
+ this.maxDefault = 100;
+ this.length = 0;
+ };
+
+ prototype.toString = function () {
+ return "[Sound SoundChannel]";
+ };
+
+ prototype._get = _get;
+ prototype._add = _add;
+ prototype._remove = _remove;
+ prototype._removeAll = _removeAll;
+ prototype._getSlot = _getSlot;
+
+ SoundChannel.channels = {};
+
+ SoundChannel.create = create;
+ SoundChannel.removeSrc = removeSrc;
+ SoundChannel.removeAll = removeAll;
+ SoundChannel.add = add;
+ SoundChannel.remove = remove;
+ SoundChannel.maxPerChannel = maxPerChannel;
+ SoundChannel.get = get;
+
+ return SoundChannel;
+
+ function _get(index) {
+ return this._instances[index];
+ }
+
+ function _add(instance, interrupt) {
+ if (!this._getSlot(interrupt, instance)) {
+ return false;
+ }
+
+ this._instances.push(instance);
+ this.length++;
+
+ return true;
+ }
+
+ function _remove(instance) {
+ var index = createjs.indexOf(this._instances, instance);
+
+ if (index == -1) {
+ return false;
+ }
+
+ this._instances.splice(index, 1);
+ this.length--;
+
+ return true;
+ }
+
+ function _removeAll() {
+ // Note that stop() removes the item from the list
+ for (var i=this.length-1; i>=0; i--) {
+ this._instances[i].stop();
+ }
+ }
+
+ function _getSlot(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;
+ }
+
+ function create(source, max) {
+ var channel = SoundChannel.get(source);
+ if (channel == null) {
+ SoundChannel.channels[source] = new SoundChannel(source, max);
+ return true;
+ }
+
+ return false;
+ }
+
+ function removeSrc(source) {
+ var channel = SoundChannel.get(source);
+ if (channel == null) {
+ return false;
+ }
+
+ channel._removeAll(); // this stops and removes all active instances
+ delete(SoundChannel.channels[source]);
+
+ return true;
+ }
+
+ function removeAll() {
+ for(var channel in SoundChannel.channels) {
+ SoundChannel.channels[channel]._removeAll(); // this stops and removes all active instances
+ }
+ SoundChannel.channels = {};
+ }
+
+ function add(instance, interrupt) {
+ var channel = SoundChannel.get(instance.src);
+ if (channel == null) {
+ return false;
+ }
+
+ return channel._add(instance, interrupt);
+ }
+
+ function remove(instance) {
+ var channel = SoundChannel.get(instance.src);
+ if (channel == null) {
+ return false;
+ }
+
+ channel._remove(instance);
+ return true;
+ }
+
+ function maxPerChannel() {
+ return p.maxDefault;
+ }
+
+ function get(source) {
+ return SoundChannel.channels[source];
+ }
+
+})();
\ No newline at end of file
diff --git a/src/soundjs/sound-utils/SoundEventHandler.js b/src/soundjs/sound-utils/SoundEventHandler.js
new file mode 100644
index 00000000..fbb1cb0d
--- /dev/null
+++ b/src/soundjs/sound-utils/SoundEventHandler.js
@@ -0,0 +1,73 @@
+// TODO remove SoundEventHandler from namespace
+// namespace:
+this.createjs = this.createjs || {};
+createjs.soundUtils = createjs.soundUtils || {};
+
+createjs.soundUtils.SoundEventHandler = (function (){
+ var SoundEventHandler = function () {
+ this.init();
+ };
+
+ var prototype = SoundEventHandler.prototype;
+ prototype.constructor = SoundEventHandler;
+
+ prototype.init = function () {
+ this.addEventListener = null;
+ this.removeEventListener = null;
+ this.removeAllEventListeners = null;
+ this.dispatchEvent = null;
+ this.hasEventListener = null;
+ this._listeners = null;
+
+ createjs.EventDispatcher.initialize(this);
+ };
+
+ prototype.handleLoadComplete = _handleLoadComplete;
+ prototype.handleLoadError = _handleLoadError;
+
+ return SoundEventHandler;
+
+ function _handleLoadComplete(event, preloadHash) {
+ var source = event.target.getItem().src;
+ if (!preloadHash || !preloadHash[source]) {
+ return;
+ }
+
+ for (var i = 0, l = preloadHash[source].length; i < l; i++) {
+ var preloadItem = preloadHash[source][i];
+ preloadHash[source][i] = true;
+
+ if (!this.hasEventListener("fileload")) { continue; }
+
+ var event = new createjs.Event("fileload");
+ event.src = preloadItem.src;
+ event.id = preloadItem.id;
+ event.data = preloadItem.data;
+ event.sprite = preloadItem.sprite;
+
+ this.dispatchEvent(event);
+ }
+ }
+
+ function _handleLoadError(event, preloadHash) {
+ var source = event.target.getItem().src;
+ if (!preloadHash || !preloadHash[source]) {
+ return;
+ }
+
+ for (var i = 0, l = preloadHash[source].length; i < l; i++) {
+ var preloadItem = preloadHash[source][i];
+ preloadHash[source][i] = false;
+
+ if (!this.hasEventListener("fileerror")) { continue; }
+
+ var event = new createjs.Event("fileerror");
+ event.src = preloadItem.src;
+ event.id = preloadItem.id;
+ event.data = preloadItem.data;
+ event.sprite = preloadItem.sprite;
+
+ this.dispatchEvent(event);
+ }
+ }
+})();
\ No newline at end of file
diff --git a/src/soundjs/sound-utils/SoundFactory.js b/src/soundjs/sound-utils/SoundFactory.js
new file mode 100644
index 00000000..e0f7cfe5
--- /dev/null
+++ b/src/soundjs/sound-utils/SoundFactory.js
@@ -0,0 +1,82 @@
+// TODO remove SoundFactory from namespace
+// namespace:
+this.createjs = this.createjs || {};
+createjs.soundUtils = createjs.soundUtils || {};
+
+createjs.soundUtils.SoundFactory = (function () {
+ var SoundEventHandler = createjs.soundUtils.SoundEventHandler,
+ SoundInstance = createjs.soundUtils.SoundInstance,
+ SoundParser = createjs.soundUtils.SoundParser,
+ SoundPlugin = createjs.soundUtils.SoundPlugin,
+ SoundRegister = createjs.soundUtils.SoundRegister,
+ SoundVolume = createjs.soundUtils.SoundVolume;
+
+ var _soundEventHandler = null,
+ _soundInstance = null,
+ _soundParser = null,
+ _soundPlugin = null,
+ _soundRegister = null,
+ _soundVolume = null;
+
+ return {
+ getSoundEventHandler: getSoundEventHandler,
+ getSoundInstance: getSoundInstance,
+ getSoundParser: getSoundParser,
+ getSoundPlugin: getSoundPlugin,
+ getSoundRegister: getSoundRegister,
+ getSoundVolume: getSoundVolume
+ }
+
+ function getSoundEventHandler() {
+ if (!_soundEventHandler)
+ _soundEventHandler = new SoundEventHandler();
+
+ return _soundEventHandler;
+ }
+ function getSoundInstance() {
+ if (!_soundInstance) {
+ var soundPlugin = getSoundPlugin(),
+ soundParser = getSoundParser(),
+ soundRegister = getSoundRegister();
+
+ _soundInstance = new SoundInstance(soundPlugin, soundParser, soundRegister);
+ }
+
+ return _soundInstance;
+ }
+ function getSoundParser() {
+ if (!_soundParser) {
+ var soundVolume = getSoundVolume();
+ _soundParser = new SoundParser(soundVolume);
+ }
+
+ return _soundParser;
+ }
+ function getSoundPlugin() {
+ if (!_soundPlugin)
+ _soundPlugin = new SoundPlugin();
+
+ return _soundPlugin;
+ }
+ function getSoundRegister() {
+ if (!_soundRegister) {
+ var soundEventHandler = getSoundEventHandler(),
+ soundPlugin = getSoundPlugin(),
+ soundParser = getSoundParser();
+
+ _soundRegister = new SoundRegister(soundEventHandler, soundPlugin, soundParser);
+ }
+
+ return _soundRegister;
+ }
+ function getSoundVolume() {
+ if (!_soundVolume) {
+ var soundPlugin = getSoundPlugin(),
+ soundInstance = getSoundInstance();
+
+ _soundVolume = new SoundVolume(soundPlugin, soundInstance);
+ }
+
+ return _soundVolume;
+ }
+})();
\ No newline at end of file
diff --git a/src/soundjs/sound-utils/SoundInstance.js b/src/soundjs/sound-utils/SoundInstance.js
new file mode 100644
index 00000000..c8464ca4
--- /dev/null
+++ b/src/soundjs/sound-utils/SoundInstance.js
@@ -0,0 +1,129 @@
+// TODO remove SoundInstance from namespace
+// namespace:
+this.createjs = this.createjs || {};
+createjs.soundUtils = createjs.soundUtils || {};
+
+createjs.soundUtils.SoundInstance = (function () {
+
+ var SoundChannel = createjs.soundUtils.SoundChannel;
+
+ var _soundPlugin = null,
+ _soundParser = null,
+ _soundRegister = null;
+
+ var _instances = [],
+ _lastID = 0;
+
+ var SoundInstance = function (soundPlugin, soundParser, soundRegister) {
+ _soundPlugin = soundPlugin;
+ _soundParser = soundParser;
+ _soundRegister = soundRegister;
+ this.defaultInterruptBehavior = InterruptMode.INTERRUPT_NONE;
+ };
+
+ var prototype = SoundInstance.prototype;
+ prototype.constructor = SoundInstance;
+
+ prototype.playInstance = _playInstance;
+ prototype.beginPlaying = _beginPlaying;
+ prototype.playFinished = _playFinished;
+ prototype.createInstance = createInstance;
+ prototype.play = play;
+ prototype.stop = stop;
+ prototype.setDefaultPlayProps = setDefaultPlayProps;
+ prototype.getDefaultPlayProps = getDefaultPlayProps;
+
+ return SoundInstance;
+
+ function _playInstance(instance, playProps) {
+ var defaultPlayProps = _soundRegister.defaultPlayPropsHash[instance.src] || {};
+ if (playProps.interrupt == null) {playProps.interrupt = defaultPlayProps.interrupt || this.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 = _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 () {
+ _beginPlaying(instance, playProps);
+ }, playProps.delay);
+ instance.delayTimeoutId = delayTimeoutId;
+ }
+
+ _instances.push(instance);
+
+ return true;
+ }
+
+ function _beginPlaying(instance, playProps) {
+ if (!SoundChannel.add(instance, playProps.interrupt)) {
+ return false;
+ }
+ var result = instance._beginPlaying(playProps);
+ if (!result) {
+ var index = createjs.indexOf(_instances, instance);
+ if (index > -1) { _instances.splice(index, 1); }
+ return false;
+ }
+ return true;
+ }
+
+ function _playFinished(instance) {
+ SoundChannel.remove(instance);
+ var index = createjs.indexOf(_instances, instance);
+ if (index > -1) { _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
+ }
+
+ function createInstance(src, startTime, duration) {
+ if (!_soundPlugin.initializeDefaultPlugins()) {
+ return new createjs.DefaultSoundInstance(src, startTime, duration);
+ }
+
+ var defaultPlayProps = _soundRegister.defaultPlayPropsHash[src]; // for audio sprites, which create and store defaults by id
+ src = _soundParser.getSrcById(src);
+
+ var details = _soundParser.parsePath(src.src);
+
+ var instance = null;
+ if (details != null && details.src != null) {
+ createInstanceFromDetails(detals);
+ } else {
+ instance = new createjs.DefaultSoundInstance(src, startTime, duration);
+ }
+
+ instance.uniqueId = _lastID++;
+
+ return instance;
+ }
+
+ function play(src, props) {
+ var playProps = createjs.PlayPropsConfig.create(props);
+ var instance = createInstance(src, playProps.startTime, playProps.duration);
+ var ok = _playInstance(instance, playProps);
+ if (!ok) { instance._playFailed(); }
+ return instance;
+ }
+
+ function stop () {
+ for (var i = _instances.length; i--; ) {
+ _instances[i].stop(); // NOTE stop removes instance from this._instances
+ }
+ }
+
+ function createInstanceFromDetails(details) {
+ SoundChannel.create(details.src);
+ if (startTime == null) { startTime = src.startTime; }
+ instance = _soundPlugin.activePlugin.create(details.src, startTime, duration || src.duration);
+
+ defaultPlayProps = defaultPlayProps || _soundRegister.defaultPlayPropsHash[details.src];
+ if (defaultPlayProps) {
+ instance.applyPlayProps(defaultPlayProps);
+ }
+ }
+})();
\ No newline at end of file
diff --git a/src/soundjs/sound-utils/SoundParser.js b/src/soundjs/sound-utils/SoundParser.js
new file mode 100644
index 00000000..6d6e1191
--- /dev/null
+++ b/src/soundjs/sound-utils/SoundParser.js
@@ -0,0 +1,85 @@
+// TODO remove SoundParser from namespace
+// namespace:
+this.createjs = this.createjs || {};
+createjs.soundUtils = createjs.soundUtils || {};
+
+createjs.soundUtils.SoundParser = (function () {
+
+ var FILE_PATTERN = /^(?:(\w+:)\/{2}(\w+(?:\.\w+)*\/?))?([/.]*?(?:[^?]+)?\/)?((?:[^/?]+)\.(\w+))(?:\?(\S+)?)?$/,
+ MATCH_NAME = 4,
+ MATCH_EXTENSION = 5;
+
+ var _soundVolume = null;
+
+ var SoundParser = function (soundVolume) {
+ _soundVolume = soundVolume;
+ this.alternateExtensions = [];
+ };
+
+ var prototype = SoundParser.prototype;
+ prototype.constructor = SoundParser;
+
+ prototype.parsePath = _parsePath;
+ prototype.parseSrc = _parseSrc;
+
+ SoundParser.FILE_PATTERN = FILE_PATTERN;
+
+ return SoundParser;
+
+ function _parsePath(value) {
+ if (typeof(value) != "string") {
+ value = value.toString();
+ }
+
+ var fileOptions = parseFileOptions(value);
+ if (!fileOptions) { return false; }
+
+ var name = fileOptions.name;
+ var extension = fileOptions.extension;
+ var capabilities = _soundVolume.capabilities;
+ var i = 0;
+ while (!capabilities[extension]) {
+ extension = this.alternateExtensions[i++];
+ if (i > this.alternateExtensions.length) { return null; } // no extensions are supported
+ }
+ value = value.replace("."+match[MATCH_EXTENSION], "."+extension);
+
+ var ret = { name: name, src:value, extension: extension };
+ return ret;
+ }
+
+ function _parseSrc(value) {
+ var ret = {name:undefined, src:undefined, extension:undefined};
+ var c = _soundVolume.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;
+ }
+
+ function parseFileOptions(value) {
+ var match = value.match(FILE_PATTERN);
+ if (!match) {
+ return null;
+ }
+
+ return {
+ name: match[MATCH_NAME],
+ extension = match[MATCH_EXTENSION]
+ };
+ }
+})();
\ No newline at end of file
diff --git a/src/soundjs/sound-utils/SoundPlugin.js b/src/soundjs/sound-utils/SoundPlugin.js
new file mode 100644
index 00000000..8842c32a
--- /dev/null
+++ b/src/soundjs/sound-utils/SoundPlugin.js
@@ -0,0 +1,59 @@
+// TODO remove SoundPlugin from namespace
+// namespace:
+this.createjs = this.createjs || {};
+createjs.soundUtils = createjs.soundUtils || {};
+
+createjs.soundUtils.SoundPlugin = (function () {
+
+ var _pluginsRegistered = false;
+
+ var SoundPlugin = function () {
+ this.activePlugin = null;
+ };
+
+ var prototype = SoundPlugin.prototype;
+ prototype.constructor = SoundPlugin;
+
+ prototype.registerPlugins = registerPlugins;
+ prototype.registerPlugin = _registerPlugin;
+ prototype.initializeDefaultPlugins = initializeDefaultPlugins;
+ prototype.isReady = isReady;
+
+ return SoundPlugin;
+
+ function registerPlugins(plugins) {
+ _pluginsRegistered = true;
+ for (var i = 0, l = plugins.length; i < l; i++) {
+ if (_registerPlugin(plugins[i])) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ function _registerPlugin(plugin) {
+ // Note: Each plugin is passed in as a class reference, but we store the activePlugin as an instance
+ if (plugin && plugin.isSupported()) {
+ this.activePlugin = new plugin();
+ return true;
+ }
+ return false;
+ }
+
+ function initializeDefaultPlugins() {
+ if (this.activePlugin != null) {
+ return true;
+ }
+ if (_pluginsRegistered) {
+ return false;
+ }
+ if (registerPlugins([createjs.WebAudioPlugin, createjs.HTMLAudioPlugin])) {
+ return true;
+ }
+ return false;
+ }
+
+ function isReady() {
+ return (this.activePlugin != null);
+ }
+})();
\ No newline at end of file
diff --git a/src/soundjs/sound-utils/SoundRegister.js b/src/soundjs/sound-utils/SoundRegister.js
new file mode 100644
index 00000000..23b43738
--- /dev/null
+++ b/src/soundjs/sound-utils/SoundRegister.js
@@ -0,0 +1,245 @@
+// TODO remove SoundRegister from namespace
+// namespace:
+this.createjs = this.createjs || {};
+createjs.soundUtils = createjs.soundUtils || {};
+
+createjs.soundUtils.SoundRegister = (function () {
+
+ var _soundEventHandler = null,
+ _soundPlugin = null,
+ _soundParser = null;
+
+ var _idHash = {},
+ _preloadHash = {};
+
+ var SoundRegister = function (soundEventHandler, soundPlugin, soundParser) {
+ _soundEventHandler = soundEventHandler;
+ _soundPlugin = soundPlugin;
+ _soundParser = soundParser;
+
+ this.defaultPlayPropsHash = {};
+ };
+
+ var prototype = SoundRegister.prototype;
+ prototype.constructor = SoundRegister;
+
+ prototype.initLoad = initLoad;
+ prototype.registerSound = registerSound;
+ prototype.registerSounds = registerSounds;
+ prototype.removeSound = removeSound;
+ prototype.removeSounds = removeSounds;
+ prototype.removeAllSounds = removeAllSounds;
+ prototype.loadComplete = loadComplete;
+ prototype.setDefaultPlayProps = setDefaultPlayProps;
+ prototype.getDefaultPlayProps = getDefaultPlayProps;
+ prototype.getSrcById = _getSrcById;
+
+ return SoundRegister;
+
+ function initLoad(loadItem) {
+ if (loadItem.type == "video") { return true; } // Don't handle video. PreloadJS's plugin model is really aggressive.
+ return _registerSound(loadItem);
+ }
+
+ function registerSound(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 = _registerSound(loadItem);
+ if(!loader) {return false;}
+
+ if (!_preloadHash[loadItem.src]) { _preloadHash[loadItem.src] = []; }
+ _preloadHash[loadItem.src].push(loadItem);
+ if (_preloadHash[loadItem.src].length == 1) {
+ // OJR note this will disallow reloading a sound if loading fails or the source changes
+ loader.on("complete", _handleLoadComplete, this);
+ loader.on("error", _handleLoadError, this);
+
+ _soundPlugin.activePlugin.preload(loader);
+ } else {
+ if (_preloadHash[loadItem.src][0] == true) {return true;}
+ }
+
+ return loadItem;
+ }
+
+ function registerSounds(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;
+ }
+
+ function removeSound(src, basePath) {
+ if (_soundPlugin.activePlugin == null) {return false;}
+
+ if (src instanceof Object && src.src) {src = src.src;}
+
+ var details;
+ if (src instanceof Object) {
+ details = _soundParser.parseSrc(src);
+ } else {
+ src = _getSrcById(src).src;
+ details = _soundParser.parsePath(src);
+ }
+ if (details == null) {return false;}
+ src = details.src;
+ if (basePath != null) {src = basePath + src;}
+
+ for(var prop in _idHash){
+ if(_idHash[prop].src == src) {
+ delete(_idHash[prop]);
+ }
+ }
+
+ // clear from SoundChannel, which also stops and deletes all instances
+ SoundChannel.removeSrc(src);
+
+ delete(_preloadHash[src]);
+
+ _soundPlugin.activePlugin.removeSound(src);
+
+ return true;
+ }
+
+ function removeSounds(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;
+ }
+
+ function removeAllSounds() {
+ _idHash = {};
+ _preloadHash = {};
+ SoundChannel.removeAll();
+ if (_soundPlugin.activePlugin) {
+ _soundPlugin.activePlugin.removeAllSounds();
+ }
+ }
+
+ function loadComplete(src) {
+ if (!src) { return false; }
+ if (!_soundPlugin.isReady()) { return false; }
+ var details = _soundParser.parsePath(src);
+ if (details) {
+ src = _getSrcById(details.src).src;
+ } else {
+ src = _getSrcById(src).src;
+ }
+ if(_preloadHash[src] == undefined) {return false;}
+ return (_preloadHash[src][0] == true); // src only loads once, so if it's true for the first it's true for all
+ }
+
+ function _registerSound(loadItem) {
+ if (!loadItem) { return false; }
+ if (!_soundPlugin.initializeDefaultPlugins()) {return false;}
+
+ var details;
+ if (loadItem.src instanceof Object) {
+ details = _soundParser.parseSrc(loadItem.src);
+ details.src = loadItem.path + details.src;
+ } else {
+ details = _soundParser.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];
+ _idHash[sp.id] = {src: loadItem.src, startTime: parseInt(sp.startTime), duration: parseInt(sp.duration)};
+
+ if (sp.defaultPlayProps) {
+ this.defaultPlayPropsHash[sp.id] = createjs.PlayPropsConfig.create(sp.defaultPlayProps);
+ }
+ }
+ }
+ }
+ if (loadItem.id != null) {_idHash[loadItem.id] = {src: loadItem.src}};
+ var loader = _soundPlugin.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) {
+ this.defaultPlayPropsHash[loadItem.src] = createjs.PlayPropsConfig.create(loadItem.defaultPlayProps);
+ }
+ return loader;
+ }
+
+ function setDefaultPlayProps(src, playProps) {
+ if (!src || !playProps) {
+ return;
+ }
+
+ src = _getSrcById(src);
+ this.defaultPlayPropsHash[_soundParser.parsePath(src.src).src] = createjs.PlayPropsConfig.create(playProps);
+ }
+
+ function getDefaultPlayProps(src) {
+ if (!src) {
+ return;
+ }
+
+ src = _getSrcById(src);
+ return this.defaultPlayPropsHash[_soundParser.parsePath(src.src).src];
+ }
+
+ function _getSrcById(value) {
+ return _idHash[value] || { src: value };
+ }
+
+ function _handleLoadComplete(event) {
+ _soundEventHandler.handleLoadComplete(event, _preloadHash);
+ }
+
+ function _handleLoadError(event) {
+ _soundEventHandler.handleLoadError(event, _preloadHash);
+ }
+})();
\ No newline at end of file
diff --git a/src/soundjs/sound-utils/SoundVolume.js b/src/soundjs/sound-utils/SoundVolume.js
new file mode 100644
index 00000000..0e2fbd2d
--- /dev/null
+++ b/src/soundjs/sound-utils/SoundVolume.js
@@ -0,0 +1,94 @@
+// TODO remove SoundVolume from namespace
+// namespace:
+this.createjs = this.createjs || {};
+createjs.soundUtils = createjs.soundUtils || {};
+
+createjs.soundUtils.SoundVolume = (function () {
+
+ var SOUND_MIN = 0,
+ SOUND_MAX = 1;
+
+ var _soundPlugin = null,
+ _soundInstance = null;
+
+ var _masterVolume = 1,
+ _masterMute = false;
+
+ var SoundVolume = function (soundPlugin, soundInstance) {
+ _soundPlugin = soundPlugin;
+ _soundInstance = soundInstance
+ };
+
+ var prototype = SoundVolume.prototype;
+ prototype.constructor = SoundVolume;
+
+ // @deprecated Remove for 1.1+
+ prototype.getVolume = createjs.deprecate(_getMasterVolume, "SoundVolume.getVolume");
+ prototype.setVolume = createjs.deprecate(_setMasterVolume, "SoundVolume.setVolume");
+
+ prototype.getMute = createjs.deprecate(_getMute, "SoundVolume.getMute");
+ prototype.setMute = createjs.deprecate(_setMute, "SoundVolume.setMute");
+
+ prototype.getCapabilities = createjs.deprecate(_getCapabilities, "SoundVolume.getCapabilities");
+
+ Object.defineProperties(prototype, {
+ volume: { get: _getMasterVolume, set: _setMasterVolume },
+ muted: { get: _getMute, set: _setMute },
+ capabilities: { get: _getCapabilities }
+ });
+
+ return SoundVolume;
+
+ function _getMasterVolume() {
+ return this._masterVolume;
+ }
+
+ function _setMasterVolume(value) {
+ if (isNaN(value)) {
+ return;
+ }
+
+ _masterVolume = adjustMasterVolumeToLimit(value);
+
+ var activePlugin = _soundPlugin.activePlugin;
+
+ if (!activePlugin || !activePlugin.setVolume || !activePlugin.setVolume(value)) {
+ var instances = _soundInstance.instances;
+ for (var i = 0, l = instances.length; i < l; i++) {
+ instances[i].setMasterVolume(value);
+ }
+ }
+ }
+
+ function adjustMasterVolumeToLimit(value) {
+ return Math.max(SOUND_MIN, Math.min(SOUND_MAX, value));
+ }
+
+ function _getMute () {
+ return this._masterMute;
+ }
+
+ function _setMute (value) {
+ if (value == null) {
+ return;
+ }
+
+ _masterMute = value;
+
+ var activePlugin = _soundPlugin.activePlugin;
+ if (!activePlugin || !activePlugin.setMute || !activePlugin.setMute(value)) {
+ var instances = _soundInstance.instances;
+ for (var i = 0, l = instances.length; i < l; i++) {
+ instances[i].setMasterMute(value);
+ }
+ }
+ }
+
+ function _getCapabilities() {
+ var activePlugin = _soundPlugin.activePlugin;
+ if (activePlugin == null) {
+ return null;
+ }
+ return activePlugin.capabilities;
+ }
+});
\ No newline at end of file