From 643140fc983ad017a56f700d946c3c5aae9449bc Mon Sep 17 00:00:00 2001 From: Scott Erickson Date: Sat, 22 Mar 2014 09:40:23 -0700 Subject: [PATCH] Updated createjs. --- vendor/scripts/easeljs-NEXT.combined.js | 65 +- vendor/scripts/preloadjs-NEXT.combined.js | 182 +++- vendor/scripts/soundjs-NEXT.combined.js | 1018 +++++++-------------- 3 files changed, 517 insertions(+), 748 deletions(-) diff --git a/vendor/scripts/easeljs-NEXT.combined.js b/vendor/scripts/easeljs-NEXT.combined.js index f25f23e63..9e51cf98c 100644 --- a/vendor/scripts/easeljs-NEXT.combined.js +++ b/vendor/scripts/easeljs-NEXT.combined.js @@ -5300,7 +5300,7 @@ var p = DisplayObject.prototype = new createjs.EventDispatcher(); * event. * @property onTick * @type {Function} - * @deprecatedtick + * @deprecated Use addEventListener and the "tick" event. */ /** @@ -5768,7 +5768,7 @@ var p = DisplayObject.prototype = new createjs.EventDispatcher(); * be used to transform positions between coordinate spaces, such as with {{#crossLink "DisplayObject/localToGlobal"}}{{/crossLink}} * and {{#crossLink "DisplayObject/globalToLocal"}}{{/crossLink}}. * @method getConcatenatedMatrix - * @param {Matrix2D} [mtx] A {{#crossLink "Matrix2D"}}{{/crossLink}} object to populate with the calculated values. + * @param {Matrix2D} [matrix] A {{#crossLink "Matrix2D"}}{{/crossLink}} object to populate with the calculated values. * If null, a new Matrix2D object is returned. * @return {Matrix2D} a concatenated Matrix2D object representing the combined transform of the display object and * all of its parent Containers up to the highest level ancestor (usually the {{#crossLink "Stage"}}{{/crossLink}}). @@ -7012,14 +7012,6 @@ var p = Stage.prototype = new createjs.Container(); * @default false **/ p.mouseMoveOutside = false; - - // TODO: deprecated. - /** - * Replaced by {{#crossLink "Stage/relayEventsTo"}}{{/crossLink}}. - * @property nextStage - * @type Stage - * @deprecated Use relayEventsTo instead. - **/ /** * The hitArea property is not supported for Stage. @@ -9794,18 +9786,17 @@ var p = SpriteSheetBuilder.prototype = new createjs.EventDispatcher; * source to draw to the frame. If not specified, it will look for a getBounds method, bounds property, * or nominalBounds property on the source to use. If one is not found, the frame will be skipped. * @param {Number} [scale=1] Optional. The scale to draw this frame at. Default is 1. - * @param {Function} [setupFunction] Optional. A function to call immediately before drawing this frame. - * @param {Array} [setupParams] Parameters to pass to the setup function. - * @param {Object} [setupScope] The scope to call the setupFunction in. + * @param {Function} [setupFunction] A function to call immediately before drawing this frame. It will be called with two parameters: the source, and setupData. + * @param {Object} [setupData] Arbitrary setup data to pass to setupFunction as the second parameter. * @return {Number} The index of the frame that was just added, or null if a sourceRect could not be determined. **/ - p.addFrame = function(source, sourceRect, scale, setupFunction, setupParams, setupScope) { + p.addFrame = function(source, sourceRect, scale, setupFunction, setupData) { if (this._data) { throw SpriteSheetBuilder.ERR_RUNNING; } var rect = sourceRect||source.bounds||source.nominalBounds; if (!rect&&source.getBounds) { rect = source.getBounds(); } if (!rect) { return null; } scale = scale||1; - return this._frames.push({source:source, sourceRect:rect, scale:scale, funct:setupFunction, params:setupParams, scope:setupScope, index:this._frames.length, height:rect.height*scale})-1; + return this._frames.push({source:source, sourceRect:rect, scale:scale, funct:setupFunction, data:setupData, index:this._frames.length, height:rect.height*scale})-1; }; /** @@ -9825,37 +9816,35 @@ var p = SpriteSheetBuilder.prototype = new createjs.EventDispatcher; }; /** - * This will take a MovieClip, and add its frames and labels to this builder. Labels will be added as an animation + * This will take a MovieClip instance, and add its frames and labels to this builder. Labels will be added as an animation * running from the label index to the next label. For example, if there is a label named "foo" at frame 0 and a label * named "bar" at frame 10, in a MovieClip with 15 frames, it will add an animation named "foo" that runs from frame * index 0 to 9, and an animation named "bar" that runs from frame index 10 to 14. * * Note that this will iterate through the full MovieClip with actionsEnabled set to false, ending on the last frame. * @method addMovieClip - * @param {MovieClip} source The source MovieClip to add to the sprite sheet. + * @param {MovieClip} source The source MovieClip instance to add to the sprite sheet. * @param {Rectangle} [sourceRect] A {{#crossLink "Rectangle"}}{{/crossLink}} defining the portion of the source to * draw to the frame. If not specified, it will look for a getBounds method, frameBounds * Array, bounds property, or nominalBounds property on the source to use. If one is not * found, the MovieClip will be skipped. * @param {Number} [scale=1] The scale to draw the movie clip at. + * @param {Function} [setupFunction] A function to call immediately before drawing each frame. It will be called with three parameters: the source, setupData, and the frame index. + * @param {Object} [setupData] Arbitrary setup data to pass to setupFunction as the second parameter. + * @param {Function} [labelFunction] This method will be called for each movieclip label that is added with four parameters: the label name, the source movieclip instance, the starting frame index (in the movieclip timeline) and the end index. It must return a new name for the label/animation, or false to exclude the label. **/ - p.addMovieClip = function(source, sourceRect, scale) { + p.addMovieClip = function(source, sourceRect, scale, setupFunction, setupData, labelFunction) { if (this._data) { throw SpriteSheetBuilder.ERR_RUNNING; } var rects = source.frameBounds; var rect = sourceRect||source.bounds||source.nominalBounds; if (!rect&&source.getBounds) { rect = source.getBounds(); } - if (!rect && !rects) { return null; } + if (!rect && !rects) { return; } - var baseFrameIndex = this._frames.length; + var i, l, baseFrameIndex = this._frames.length; var duration = source.timeline.duration; - for (var i=0; iExample + * + * var queue = new createjs.LoadQueue(); + * queue.setMaxConnections(3); // Set a higher number to load multiple items at once + * queue.maintainScriptOrder = true; // Ensure scripts are loaded in order + * queue.loadManifest([ + * "script1.js", + * "script2.js", + * "image.png", // Load any time + * {src: "image2.png", maintainOrder: true} // Will wait for script2.js + * "image3.png", + * "script3.js" // Will wait for image2.png before loading (or completing when loading with XHR) + * ]); + * * @property maintainScriptOrder * @type {Boolean} * @default true @@ -2247,6 +2269,11 @@ TODO: WINDOWS ISSUES * of types using the extension. Supported types are defined on LoadQueue, such as LoadQueue.IMAGE. * It is recommended that a type is specified when a non-standard file URI (such as a php script) us used. *
  • id: A string identifier which can be used to reference the loaded object.
  • + *
  • maintainOrder: Set to `true` to ensure this asset loads in the order defined in the manifest. This + * will happen when the max connections has been set above 1 (using {{#crossLink "LoadQueue/setMaxConnections"}}{{/crossLink}}), + * and will only affect other assets also defined as `maintainOrder`. Everything else will finish as it is + * loaded. Ordered items are combined with script tags loading in order when {{#crossLink "LoadQueue/maintainScriptOrder:property"}}{{/crossLink}} + * is set to `true`.
  • *
  • callback: Optional, used for JSONP requests, to define what method to call when the JSONP is loaded.
  • *
  • data: An arbitrary data object, which is included with the loaded object
  • *
  • method: used to define if this request uses GET or POST when sending data to the server. The default @@ -2314,9 +2341,14 @@ TODO: WINDOWS ISSUES *
  • src: The source of the file that is being loaded. This property is required. The source can * either be a string (recommended), or an HTML tag.
  • *
  • type: The type of file that will be loaded (image, sound, json, etc). PreloadJS does auto-detection - * of types using the extension. Supported types are defined on LoadQueue, such as LoadQueue.IMAGE. + * of types using the extension. Supported types are defined on LoadQueue, such as {{#crossLink "LoadQueue/IMAGE:property"}}{{/crossLink}}. * It is recommended that a type is specified when a non-standard file URI (such as a php script) us used.
  • *
  • id: A string identifier which can be used to reference the loaded object.
  • + *
  • maintainOrder: Set to `true` to ensure this asset loads in the order defined in the manifest. This + * will happen when the max connections has been set above 1 (using {{#crossLink "LoadQueue/setMaxConnections"}}{{/crossLink}}), + * and will only affect other assets also defined as `maintainOrder`. Everything else will finish as it is + * loaded. Ordered items are combined with script tags loading in order when {{#crossLink "LoadQueue/maintainScriptOrder:property"}}{{/crossLink}} + * is set to `true`.
  • *
  • callback: Optional, used for JSONP requests, to define what method to call when the JSONP is loaded.
  • *
  • data: An arbitrary data object, which is included with the loaded object
  • *
  • method: used to define if this request uses GET or POST when sending data to the server. The default @@ -2499,6 +2531,7 @@ TODO: WINDOWS ISSUES if (item == null) { return; } // Sometimes plugins or types should be skipped. var loader = this._createLoader(item); if (loader != null) { + item._loader = loader; this._loadQueue.push(loader); this._loadQueueBackup.push(loader); @@ -2506,9 +2539,11 @@ TODO: WINDOWS ISSUES this._updateProgress(); // Only worry about script order when using XHR to load scripts. Tags are only loading one at a time. - if (this.maintainScriptOrder + if ((this.maintainScriptOrder && item.type == createjs.LoadQueue.JAVASCRIPT - && loader instanceof createjs.XHRLoader) { + //&& loader instanceof createjs.XHRLoader //NOTE: Have to track all JS files this way + ) + || item.maintainOrder === true) { this._scriptOrder.push(item); this._loadedScripts.push(null); } @@ -2717,13 +2752,9 @@ TODO: WINDOWS ISSUES if (this._currentLoads.length >= this._maxConnections) { break; } var loader = this._loadQueue[i]; - // Determine if we should be only loading one at a time: - if (this.maintainScriptOrder - && loader instanceof createjs.TagLoader - && loader.getItem().type == createjs.LoadQueue.JAVASCRIPT) { - if (this._currentlyLoadingScript) { continue; } // Later items in the queue might not be scripts. - this._currentlyLoadingScript = true; - } + // Determine if we should be only loading one tag-script at a time: + // Note: maintainOrder items don't do anything here because we can hold onto their loaded value + if (!this._canStartLoad(loader)) { continue; } this._loadQueue.splice(i, 1); i--; this._loadItem(loader); @@ -2755,6 +2786,8 @@ TODO: WINDOWS ISSUES p._handleFileError = function(event) { var loader = event.target; this._numItemsLoaded++; + + this._finishOrderedItem(loader, true); this._updateProgress(); var newEvent = new createjs.Event("error"); @@ -2787,47 +2820,43 @@ TODO: WINDOWS ISSUES this._loadedRawResults[item.id] = loader.getResult(true); } + // Clean up the load item this._removeLoadItem(loader); - // Ensure that script loading happens in the right order. - if (this.maintainScriptOrder && item.type == createjs.LoadQueue.JAVASCRIPT) { - if (loader instanceof createjs.TagLoader) { - this._currentlyLoadingScript = false; - } else { - this._loadedScripts[createjs.indexOf(this._scriptOrder, item)] = item; - this._checkScriptLoadOrder(loader); - return; - } + if (!this._finishOrderedItem(loader)) { + // The item was NOT managed, so process it now + this._processFinishedLoad(item, loader); } - - // Clean up the load item - delete item._loadAsJSONP; - - // If the item was a manifest, then - if (item.type == createjs.LoadQueue.MANIFEST) { - var result = loader.getResult(); - if (result != null && result.manifest !== undefined) { - this.loadManifest(result, true); - } - } - - this._processFinishedLoad(item, loader); }; /** - * @method _processFinishedLoad - * @param {Object} item + * Flag an item as finished. If the item's order is being managed, then set it up to finish + * @method _finishOrderedItem * @param {AbstractLoader} loader - * @protected + * @return {Boolean} If the item's order is being managed. This allows the caller to take an alternate + * behaviour if it is. + * @private */ - p._processFinishedLoad = function(item, loader) { - // Old handleFileTagComplete follows here. - this._numItemsLoaded++; + p._finishOrderedItem = function(loader, loadFailed) { + var item = loader.getItem(); - this._updateProgress(); - this._sendFileComplete(item, loader); + if ((this.maintainScriptOrder && item.type == createjs.LoadQueue.JAVASCRIPT) + || item.maintainOrder) { - this._loadNext(); + //TODO: Evaluate removal of the _currentlyLoadingScript + if (loader instanceof createjs.TagLoader && item.type == createjs.LoadQueue.JAVASCRIPT) { + this._currentlyLoadingScript = false; + } + + var index = createjs.indexOf(this._scriptOrder, item); + if (index == -1) { return false; } // This loader no longer exists + this._loadedScripts[index] = (loadFailed === true) ? true : item; + + this._checkScriptLoadOrder(); + return true; + } + + return false; }; /** @@ -2845,17 +2874,68 @@ TODO: WINDOWS ISSUES for (var i=0;ibefore + * the script can even be started, since it exist in the DOM while loading. + * @method _canStartLoad + * @param {XHRLoader|TagLoader} loader The loader for the item + * @return {Boolean} Whether the item can start a load or not. + * @private + */ + p._canStartLoad = function(loader) { + if (!this.maintainScriptOrder || loader instanceof createjs.XHRLoader) { return true; } + var item = loader.getItem(); + if (item.type != createjs.LoadQueue.JAVASCRIPT) { return true; } + if (this._currentlyLoadingScript) { return false; } + + var index = this._scriptOrder.indexOf(item); + var i = 0; + while (i < index) { + var checkItem = this._loadedScripts[i]; + if (checkItem == null) { return false; } + i++; + } + this._currentlyLoadingScript = true; + return true; + }; + /** * A load item is completed or was canceled, and needs to be removed from the LoadQueue. * @method _removeLoadItem @@ -2863,6 +2943,10 @@ TODO: WINDOWS ISSUES * @private */ p._removeLoadItem = function(loader) { + var item = loader.getItem(); + delete item._loader; + delete item._loadAsJSONP; + var l = this._currentLoads.length; for (var i=0;iBrowser Support - * Audio will work in browsers which support HTMLAudioElement (http://caniuse.com/audio) - * or WebAudio (http://caniuse.com/audio-api). A Flash fallback can be added + * Audio will work in browsers which support WebAudio (http://caniuse.com/audio-api) + * or HTMLAudioElement (http://caniuse.com/audio). A Flash fallback can be added * as well, which will work in any browser that supports the Flash player. * @module SoundJS * @main SoundJS @@ -931,11 +997,6 @@ this.createjs = this.createjs || {}; "use strict"; - //TODO: Interface to validate plugins and throw warnings - //TODO: Determine if methods exist on a plugin before calling // OJR this is only an issue if something breaks or user changes something - //TODO: Interface to validate instances and throw warnings - //TODO: Surface errors on audio from all plugins - //TODO: Timeouts // OJR for? /** * 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. @@ -945,7 +1006,7 @@ this.createjs = this.createjs || {}; * or register multiple sounds using {{#crossLink "Sound/registerManifest"}}{{/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, this is handled for you when the sound is + * 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. * @@ -982,11 +1043,12 @@ this.createjs = this.createjs || {}; * * 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 not play immediately the first time play is called. Use the + * 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"}}{{/crossLink}} event to determine when a sound has finished internally preloading. * It is recommended that all audio is preloaded before it is played. * - * createjs.PreloadJS.installPlugin(createjs.Sound); + * var queue = new createjs.LoadQueue(); + * queue.installPlugin(createjs.Sound); * * Mobile Safe Approach
    * Mobile devices require sounds to be played inside of a user initiated event (touch/click) in varying degrees. @@ -1009,6 +1071,7 @@ this.createjs = this.createjs || {}; * 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.
  • * @@ -1044,19 +1107,16 @@ this.createjs = this.createjs || {}; var s = Sound; + // TODO DEPRECATED /** - * DEPRECATED - * This approach has is being replaced by {{#crossLink "Sound/alternateExtensions:property"}}{{/crossLink}}, and - * support will be removed in the next version. - * - * The character (or characters) that are used to split multiple paths from an audio source. + * REMOVED + * Use {{#crossLink "Sound/alternateExtensions:property"}}{{/crossLink}} instead * @property DELIMITER * @type {String} * @default | * @static * @deprecated */ - s.DELIMITER = "|"; /** * The interrupt value to interrupt any currently playing instance with the same source, if the maximum number of @@ -1159,7 +1219,7 @@ this.createjs = this.createjs || {}; * @default ["mp3", "ogg", "mpeg", "wav", "m4a", "mp4", "aiff", "wma", "mid"] * @since 0.4.0 */ - s.SUPPORTED_EXTENSIONS = ["mp3", "ogg", "mpeg", "wav", "m4a", "mp4", "aiff", "wma", "mid"]; // OJR FlashPlugin does not currently support + s.SUPPORTED_EXTENSIONS = ["mp3", "ogg", "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 @@ -1346,16 +1406,6 @@ this.createjs = this.createjs || {}; * @since 0.4.1 */ - //TODO: Deprecated - /** - * REMOVED. Use {{#crossLink "EventDispatcher/addEventListener"}}{{/crossLink}} and the {{#crossLink "Sound/fileload:event"}}{{/crossLink}} - * event. - * @property onLoadComplete - * @type {Function} - * @deprecated Use addEventListener and the fileload event. - * @since 0.4.0 - */ - /** * Used by external plugins to dispatch file load events. * @method _sendFileLoadEvent @@ -1406,33 +1456,7 @@ this.createjs = this.createjs || {}; }; /** - * Deprecated in favor of {{#crossLink "Sound/registerPlugins"}}{{/crossLink}} with a single argument. - * createjs.Sound.registerPlugins([createjs.WebAudioPlugin]); - * - * @method registerPlugin - * @param {Object} plugin The plugin class to install. - * @return {Boolean} Whether the plugin was successfully initialized. - * @static - * @deprecated - */ - s.registerPlugin = function (plugin) { - try { - console.log("createjs.Sound.registerPlugin has been deprecated. Please use registerPlugins."); - } catch (err) { - // you are in IE with the console closed, you monster - } - return s._registerPlugin(plugin); - }; - - /** - * Register a Sound plugin. Plugins handle the actual playback of audio. The default plugins are - * ({{#crossLink "WebAudioPlugin"}}{{/crossLink}} followed by {{#crossLink "HTMLAudioPlugin"}}{{/crossLink}}), - * and are installed if no other plugins are present when the user attempts to start playback or register sound. - *

    Example

    - * createjs.FlashPlugin.swfPath = "../src/SoundJS/"; - * createjs.Sound._registerPlugin(createjs.FlashPlugin); - * - * To register multiple plugins, use {{#crossLink "Sound/registerPlugins"}}{{/crossLink}}. + * Used by {{#crossLink "Sound/registerPlugins"}}{{/crossLink}} to register a Sound plugin. * * @method _registerPlugin * @param {Object} plugin The plugin class to install. @@ -1441,14 +1465,9 @@ this.createjs = this.createjs || {}; * @private */ s._registerPlugin = function (plugin) { - s._pluginsRegistered = true; - if (plugin == null) { - return false; - } // 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(); - //TODO: Check error on initialization return true; } return false; @@ -1467,9 +1486,9 @@ this.createjs = this.createjs || {}; * @static */ s.registerPlugins = function (plugins) { + s._pluginsRegistered = true; for (var i = 0, l = plugins.length; i < l; i++) { - var plugin = plugins[i]; - if (s._registerPlugin(plugin)) { + if (s._registerPlugin(plugins[i])) { return true; } } @@ -1489,15 +1508,9 @@ this.createjs = this.createjs || {}; * @since 0.4.0 */ s.initializeDefaultPlugins = function () { - if (s.activePlugin != null) { - return true; - } - if (s._pluginsRegistered) { - return false; - } - if (s.registerPlugins([createjs.WebAudioPlugin, createjs.HTMLAudioPlugin])) { - return true; - } + if (s.activePlugin != null) {return true;} + if (s._pluginsRegistered) {return false;} + if (s.registerPlugins([createjs.WebAudioPlugin, createjs.HTMLAudioPlugin])) {return true;} return false; }; @@ -1544,9 +1557,7 @@ this.createjs = this.createjs || {}; * @static */ s.getCapabilities = function () { - if (s.activePlugin == null) { - return null; - } + if (s.activePlugin == null) {return null;} return s.activePlugin._capabilities; }; @@ -1564,9 +1575,7 @@ this.createjs = this.createjs || {}; * @see getCapabilities */ s.getCapability = function (key) { - if (s.activePlugin == null) { - return null; - } + if (s.activePlugin == null) {return null;} return s.activePlugin._capabilities[key]; }; @@ -1581,19 +1590,64 @@ this.createjs = this.createjs || {}; * @param {Number|String|Boolean|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 * this property is used for other information. The audio channels will default to 1 if no value is found. - * @param {String} [path] A combined basepath and subPath from PreloadJS that has already been prepended to src. * @return {Boolean|Object} An object with the modified values of those that were passed in, or false if the active * plugin can not play the audio type. * @protected * @static */ - s.initLoad = function (src, type, id, data, path) { - // remove path from src so we can continue to support "|" splitting of src files // TODO remove this when "|" is removed - src = src.replace(path, ""); - var details = s.registerSound(src, id, data, false, path); - if (details == null) { - return false; + s.initLoad = function (src, type, id, data) { + return s._registerSound(src, id, data); + }; + + /** + * Internal method for loading sounds. This should not be called directly. + * + * @method _registerSound + * @param {String | Object} src The source to load. + * @param {String} [id] An id specified by the user to play the sound later. + * @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. + * @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.5.3 + */ + + s._registerSound = function (src, id, data) { + if (!s.initializeDefaultPlugins()) {return false;} + + var details = s._parsePath(src, "sound", id, data); + if (details == null) {return false;} + + if (id != null) {s._idHash[id] = details.src;} + + var numChannels = s.activePlugin.defaultNumChannels || null; + if (data != null) { + if (!isNaN(data.channels)) { + numChannels = parseInt(data.channels); + } + else if (!isNaN(data)) { + numChannels = parseInt(data); + } } + var loader = s.activePlugin.register(details.src, numChannels); // Note only HTML audio uses numChannels + + SoundChannel.create(details.src, numChannels); + + // return the number of instances to the user. This will also be returned in the load event. + if (data == null || !isNaN(data)) { + details.data = numChannels || SoundChannel.maxPerChannel(); + } else { + details.data.channels = numChannels || SoundChannel.maxPerChannel(); + } + + details.tag = loader.tag; + if (loader.completeHandler) {details.completeHandler = loader.completeHandler;} + if (loader.type) {details.type = loader.type;} + return details; }; @@ -1613,8 +1667,6 @@ this.createjs = this.createjs || {}; * @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. - * @param {Boolean} [preload=true] If the sound should be internally preloaded so that it can be played back - * without an external preloader. This is currently used by PreloadJS when loading sounds to disable internal preloading. * @param {string} basePath Set a path that will be prepended to src for loading. * @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. @@ -1622,90 +1674,27 @@ this.createjs = this.createjs || {}; * @static * @since 0.4.0 */ - s.registerSound = function (src, id, data, preload, basePath) { - if (!s.initializeDefaultPlugins()) { - return false; - } - + s.registerSound = function (src, id, data, basePath) { if (src instanceof Object) { - basePath = id; //this assumes preload has not be passed in as a property // OJR check if arguments == 3 would be less fragile - //?? preload = src.preload; - // OJR refactor how data is passed in to make the parameters work better + basePath = id; id = src.id; data = src.data; src = src.src; } - // branch to different parse based on alternate formats setting - if (s.alternateExtensions.length) { - var details = s._parsePath2(src, "sound", id, data); + if (basePath != null) {src = basePath + src;} + + var details = s._registerSound(src, id, data); + + if(!details) {return false;} + + if (!s._preloadHash[details.src]) { s._preloadHash[details.src] = [];} + s._preloadHash[details.src].push({src:src, id:id, data:details.data}); + if (s._preloadHash[details.src].length == 1) { + // OJR note this will disallow reloading a sound if loading fails or the source changes + s.activePlugin.preload(details.src, details.tag); } else { - var details = s._parsePath(src, "sound", id, data); - } - if (details == null) { - return false; - } - if (basePath != null) { - src = basePath + src; - details.src = basePath + details.src; - } - - if (id != null) { - s._idHash[id] = details.src; - } - - var numChannels = null; // null tells SoundChannel to set this to it's internal maxDefault - if (data != null) { - if (!isNaN(data.channels)) { - numChannels = parseInt(data.channels); - } - else if (!isNaN(data)) { - numChannels = parseInt(data); - } - } - var loader = s.activePlugin.register(details.src, numChannels); // Note only HTML audio uses numChannels - - if (loader != null) { // all plugins currently return a loader - if (loader.numChannels != null) { - numChannels = loader.numChannels; - } // currently only HTMLAudio returns this - SoundChannel.create(details.src, numChannels); - - // return the number of instances to the user. This will also be returned in the load event. - if (data == null || !isNaN(data)) { - data = details.data = numChannels || SoundChannel.maxPerChannel(); - } else { - data.channels = details.data.channels = numChannels || SoundChannel.maxPerChannel(); - } - - // If the loader returns a tag, return it instead for preloading. - // OJR all loaders currently use tags? - if (loader.tag != null) { - details.tag = loader.tag; - } else if (loader.src) { - details.src = loader.src; - } - // If the loader returns a complete handler, pass it on to the prelaoder. - if (loader.completeHandler != null) { - details.completeHandler = loader.completeHandler; - } - if (loader.type) { - details.type = loader.type; - } - } - - if (preload != false) { - if (!s._preloadHash[details.src]) { - s._preloadHash[details.src] = []; - } // we do this so we can store multiple id's and data if needed - s._preloadHash[details.src].push({src:src, id:id, data:data}); // keep this data so we can return it in fileload event - if (s._preloadHash[details.src].length == 1) { - // if already loaded once, don't load a second time // OJR note this will disallow reloading a sound if loading fails or the source changes - s.activePlugin.preload(details.src, loader); - } else { - // if src already loaded successfully, return true - if (s._preloadHash[details.src][0] == true) {return true;} - } + if (s._preloadHash[details.src][0] == true) {return true;} } return details; @@ -1741,8 +1730,8 @@ this.createjs = this.createjs || {}; s.registerManifest = function (manifest, basePath) { var returnValues = []; for (var i = 0, l = manifest.length; i < l; i++) { - returnValues[i] = createjs.Sound.registerSound(manifest[i].src, manifest[i].id, manifest[i].data, manifest[i].preload, basePath); - } // OJR consider removing .preload from args, as it is only used by PreloadJS + returnValues[i] = createjs.Sound.registerSound(manifest[i].src, manifest[i].id, manifest[i].data, basePath); + } return returnValues; }; @@ -1764,27 +1753,16 @@ this.createjs = this.createjs || {}; * @since 0.4.1 */ s.removeSound = function(src, basePath) { - if (s.activePlugin == null) { - return false; - } + if (s.activePlugin == null) {return false;} - if (src instanceof Object) { - src = src.src; - } + if (src instanceof Object) {src = src.src;} src = s._getSrcById(src); + if (basePath != null) {src = basePath + src;} - if (s.alternateExtensions.length) { - var details = s._parsePath2(src); - } else { - var details = s._parsePath(src); - } - if (details == null) { - return false; - } - if (basePath != null) {details.src = basePath + details.src;} + var details = s._parsePath(src); + if (details == null) {return false;} src = details.src; - // remove src from _idHash // Note "for in" can be a slow operation for(var prop in s._idHash){ if(s._idHash[prop] == src) { delete(s._idHash[prop]); @@ -1794,10 +1772,8 @@ this.createjs = this.createjs || {}; // clear from SoundChannel, which also stops and deletes all instances SoundChannel.removeSrc(src); - // remove src from _preloadHash delete(s._preloadHash[src]); - // activePlugin cleanup s.activePlugin.removeSound(src); return true; @@ -1850,7 +1826,7 @@ this.createjs = this.createjs || {}; s._idHash = {}; s._preloadHash = {}; SoundChannel.removeAll(); - s.activePlugin.removeAllSounds(); + if (s.activePlugin) {s.activePlugin.removeAllSounds();} }; /** @@ -1869,11 +1845,7 @@ this.createjs = this.createjs || {}; * @since 0.4.0 */ s.loadComplete = function (src) { - if (s.alternateExtensions.length) { - var details = s._parsePath2(src, "sound"); - } else { - var details = s._parsePath(src, "sound"); - } + var details = s._parsePath(src, "sound"); if (details) { src = s._getSrcById(details.src); } else { @@ -1883,10 +1855,8 @@ this.createjs = this.createjs || {}; }; /** - * Parse the path of a sound, usually from a manifest item. Manifest items support single file paths, as well as - * composite paths using {{#crossLink "Sound/DELIMITER:property"}}{{/crossLink}}, which defaults to "|". The first path supported by the - * current browser/plugin will be used. - * NOTE the "|" approach is deprecated and will be removed in the next version + * Parse the path of a sound, usually from a manifest item. 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. * @param {String} [type] The type of path. This will typically be "sound" or null. @@ -1898,60 +1868,22 @@ this.createjs = this.createjs || {}; * @protected */ s._parsePath = function (value, type, id, data) { - if (typeof(value) != "string") {value = value.toString();} - var sounds = value.split(s.DELIMITER); - if (sounds.length > 1) { - try { - console.log("createjs.Sound.DELIMITER \"|\" loading approach has been deprecated. Please use the new alternateExtensions property."); - } catch (err) { - // you are in IE with the console closed, you monster - } - } - var ret = {type:type || "sound", id:id, data:data}; - var c = s.getCapabilities(); - for (var i = 0, l = sounds.length; i < l; i++) { - var sound = sounds[i]; - - var match = sound.match(s.FILE_PATTERN); - if (match == null) { - return false; - } - var name = match[4]; - var ext = match[5]; - - if (c[ext] && createjs.indexOf(s.SUPPORTED_EXTENSIONS, ext) > -1) { - ret.name = name; - ret.src = sound; - ret.extension = ext; - return ret; - } - } - return null; - }; - - // new approach, when old approach is deprecated this will become _parsePath - s._parsePath2 = function (value, type, id, data) { if (typeof(value) != "string") {value = value.toString();} var match = value.match(s.FILE_PATTERN); - if (match == null) { - return false; - } + if (match == null) {return false;} + var name = match[4]; var ext = match[5]; - var c = s.getCapabilities(); 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 = {type:type || "sound", id:id, data:data}; - ret.name = name; - ret.src = value; - ret.extension = ext; + + var ret = {type:type || "sound", id:id, data:data, name:name, src:value, extension:ext}; return ret; }; @@ -1996,11 +1928,8 @@ this.createjs = this.createjs || {}; */ s.play = function (src, interrupt, delay, offset, loop, volume, pan) { var instance = s.createInstance(src); - var ok = s._playInstance(instance, interrupt, delay, offset, loop, volume, pan); - if (!ok) { - instance.playFailed(); - } + if (!ok) {instance.playFailed();} return instance; }; @@ -2026,27 +1955,17 @@ this.createjs = this.createjs || {}; * @since 0.4.0 */ s.createInstance = function (src) { - if (!s.initializeDefaultPlugins()) { - return s._defaultSoundInstance; - } + if (!s.initializeDefaultPlugins()) {return s._defaultSoundInstance;} src = s._getSrcById(src); - if (s.alternateExtensions.length) { - var details = s._parsePath2(src, "sound"); - } else { - var details = s._parsePath(src, "sound"); - } + var details = s._parsePath(src, "sound"); var instance = null; if (details != null && details.src != null) { - // make sure that we have a sound channel (sound is registered or previously played) SoundChannel.create(details.src); instance = s.activePlugin.create(details.src); } else { - // the src is not supported, so give back a dummy instance. - // This can happen if PreloadJS fails because the plugin does not support the ext, and was passed an id which - // will not get added to the _idHash. instance = Sound._defaultSoundInstance; } @@ -2068,13 +1987,11 @@ this.createjs = this.createjs || {}; * @static */ s.setVolume = function (value) { - if (Number(value) == null) { - return false; - } + if (Number(value) == null) {return false;} 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; // OJR does this impact garbage collection more than it helps performance? + var instances = this._instances; for (var i = 0, l = instances.length; i < l; i++) { instances[i].setMasterVolume(value); } @@ -2096,14 +2013,6 @@ this.createjs = this.createjs || {}; return s._masterVolume; }; - /** - * REMOVED. Please see {{#crossLink "Sound/setMute"}}{{/crossLink}}. - * @method mute - * @param {Boolean} value Whether the audio should be muted or not. - * @static - * @deprecated This function has been deprecated. Please use setMute 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 @@ -2119,9 +2028,7 @@ this.createjs = this.createjs || {}; * @since 0.4.0 */ s.setMute = function (value) { - if (value == null || value == undefined) { - return false; - } + if (value == null) {return false;} this._masterMute = value; if (!this.activePlugin || !this.activePlugin.setMute || !this.activePlugin.setMute(value)) { @@ -2205,15 +2112,13 @@ this.createjs = this.createjs || {}; interrupt = interrupt || s.defaultInterruptBehavior; if (delay == null) {delay = 0;} if (offset == null) {offset = instance.getPosition();} - if (loop == null) {loop = 0;} + if (loop == null) {loop = 0;} // OJR consider using instance._remainingLoops if (volume == null) {volume = instance.volume;} if (pan == null) {pan = instance.pan;} if (delay == 0) { var ok = s._beginPlaying(instance, interrupt, offset, loop, volume, pan); - if (!ok) { - return false; - } + 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 @@ -2251,11 +2156,8 @@ this.createjs = this.createjs || {}; } var result = instance._beginPlaying(offset, loop, volume, pan); if (!result) { - //LM: Should we remove this from the SoundChannel (see finishedPlaying) var index = createjs.indexOf(this._instances, instance); - if (index > -1) { - this._instances.splice(index, 1); - } + if (index > -1) {this._instances.splice(index, 1);} return false; } return true; @@ -2266,15 +2168,12 @@ this.createjs = this.createjs || {}; * instead. * @method _getSrcById * @param {String} value The ID the sound was registered with. - * @return {String} The source of the sound. Returns null if src has been registered with this id. + * @return {String} The source of the sound if it has been registered with this ID or the value that was passed in. * @protected * @static */ s._getSrcById = function (value) { - if (s._idHash == null || s._idHash[value] == null) { - return value; - } - return s._idHash[value]; + return s._idHash[value] || value; }; /** @@ -2289,9 +2188,7 @@ this.createjs = this.createjs || {}; s._playFinished = function (instance) { SoundChannel.remove(instance); var index = createjs.indexOf(this._instances, instance); - if (index > -1) { - this._instances.splice(index, 1); - } + 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; @@ -2352,10 +2249,8 @@ this.createjs = this.createjs || {}; */ SoundChannel.removeSrc = function (src) { var channel = SoundChannel.get(src); - if (channel == null) { - return false; - } - channel.removeAll(); // this stops and removes all active instances + if (channel == null) {return false;} + channel._removeAll(); // this stops and removes all active instances delete(SoundChannel.channels[src]); return true; }; @@ -2366,7 +2261,7 @@ this.createjs = this.createjs || {}; */ SoundChannel.removeAll = function () { for(var channel in SoundChannel.channels) { - SoundChannel.channels[channel].removeAll(); // this stops and removes all active instances + SoundChannel.channels[channel]._removeAll(); // this stops and removes all active instances } SoundChannel.channels = {}; }; @@ -2381,10 +2276,8 @@ this.createjs = this.createjs || {}; */ SoundChannel.add = function (instance, interrupt) { var channel = SoundChannel.get(instance.src); - if (channel == null) { - return false; - } - return channel.add(instance, interrupt); + if (channel == null) {return false;} + return channel._add(instance, interrupt); }; /** * Remove an instance from the channel. @@ -2395,10 +2288,8 @@ this.createjs = this.createjs || {}; */ SoundChannel.remove = function (instance) { var channel = SoundChannel.get(instance.src); - if (channel == null) { - return false; - } - channel.remove(instance); + if (channel == null) {return false;} + channel._remove(instance); return true; }; /** @@ -2461,9 +2352,7 @@ this.createjs = this.createjs || {}; p.init = function (src, max) { this.src = src; this.max = max || this.maxDefault; - if (this.max == -1) { - this.max = this.maxDefault; - } + if (this.max == -1) {this.max = this.maxDefault;} this._instances = []; }; @@ -2473,7 +2362,7 @@ this.createjs = this.createjs || {}; * @param {Number} index The index to return. * @return {SoundInstance} The SoundInstance at a specific instance. */ - p.get = function (index) { + p._get = function (index) { return this._instances[index]; }; @@ -2483,10 +2372,8 @@ this.createjs = this.createjs || {}; * @param {SoundInstance} 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; - } + p._add = function (instance, interrupt) { + if (!this._getSlot(interrupt, instance)) {return false;} this._instances.push(instance); this.length++; return true; @@ -2499,11 +2386,9 @@ this.createjs = this.createjs || {}; * @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) { + p._remove = function (instance) { var index = createjs.indexOf(this._instances, instance); - if (index == -1) { - return false; - } + if (index == -1) {return false;} this._instances.splice(index, 1); this.length--; return true; @@ -2513,8 +2398,8 @@ this.createjs = this.createjs || {}; * 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, but we don't want to assume that. + 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(); } @@ -2528,11 +2413,11 @@ this.createjs = this.createjs || {}; * @return {Boolean} Determines if there is an available slot. Depending on the interrupt mode, if there are no slots, * an existing SoundInstance may be interrupted. If there are no slots, this method returns false. */ - p.getSlot = function (interrupt, instance) { + p._getSlot = function (interrupt, instance) { var target, replacement; for (var i = 0, l = this.max; i < l; i++) { - target = this.get(i); + target = this._get(i); // Available Space if (target == null) { @@ -2554,16 +2439,15 @@ this.createjs = this.createjs || {}; replacement = target; // Audio is a better candidate than the current target, according to playhead - } else if ( - (interrupt == Sound.INTERRUPT_EARLY && target.getPosition() < replacement.getPosition()) || - (interrupt == Sound.INTERRUPT_LATE && target.getPosition() > replacement.getPosition())) { + } else if ( (interrupt == Sound.INTERRUPT_EARLY && target.getPosition() < replacement.getPosition()) || + (interrupt == Sound.INTERRUPT_LATE && target.getPosition() > replacement.getPosition())) { replacement = target; } } if (replacement != null) { replacement._interrupt(); - this.remove(replacement); + this._remove(replacement); return true; } return false; @@ -2720,9 +2604,7 @@ this.createjs = this.createjs || {}; // OJR isMobile may be redundant with _isFileXHRSupported available. Consider removing. if (location.protocol == "file:" && !isMobilePhoneGap && !this._isFileXHRSupported()) { return false; } // Web Audio requires XHR, which is not usually available locally s._generateCapabilities(); - if (s.context == null) { - return false; - } + if (s.context == null) {return false;} return true; }; @@ -2767,28 +2649,19 @@ this.createjs = this.createjs || {}; * @protected */ s._generateCapabilities = function () { - if (s._capabilities != null) { - return; - } - // Web Audio can be in any formats supported by the audio element, from http://www.w3.org/TR/webaudio/#AudioContext-section, - // therefore tag is still required for the capabilities check + if (s._capabilities != null) {return;} + // Web Audio can be in any formats supported by the audio element, from http://www.w3.org/TR/webaudio/#AudioContext-section var t = document.createElement("audio"); + if (t.canPlayType == null) {return null;} - if (t.canPlayType == null) { - return null; - } - - // This check is first because it's what is currently used, but the spec calls for it to be AudioContext so this - // will probably change in time - if (window.webkitAudioContext) { - s.context = new webkitAudioContext(); - } else if (window.AudioContext) { + if (window.AudioContext) { s.context = new AudioContext(); + } else if (window.webkitAudioContext) { + s.context = new webkitAudioContext(); } else { return null; } - // this handles if only deprecated Web Audio API calls are supported s._compatibilitySetUp(); // playing this inside of a touch event will enable audio on iOS, which starts muted @@ -2814,12 +2687,6 @@ this.createjs = this.createjs || {}; if (s.context.destination.numberOfChannels < 2) { s._capabilities.panning = false; } - - // set up AudioNodes that all of our source audio will connect to - s.dynamicsCompressorNode = s.context.createDynamicsCompressor(); - s.dynamicsCompressorNode.connect(s.context.destination); - s.gainNode = s.context.createGain(); - s.gainNode.connect(s.dynamicsCompressorNode); }; /** @@ -2829,10 +2696,12 @@ this.createjs = this.createjs || {}; * don't support new calls. * * @method _compatibilitySetUp + * @static * @protected * @since 0.4.2 */ s._compatibilitySetUp = function() { + s._panningModel = "equalpower"; //assume that if one new call is supported, they all are if (s.context.createGain) { return; } @@ -2845,7 +2714,7 @@ this.createjs = this.createjs || {}; audioNode.__proto__.stop = audioNode.__proto__.noteOff; // panningModel - this._panningModel = 0; + s._panningModel = 0; }; /** @@ -2855,24 +2724,18 @@ this.createjs = this.createjs || {}; * for example). * *

    Example

    - * * function handleTouch(event) { * createjs.WebAudioPlugin.playEmptySound(); * } * * @method playEmptySound + * @static * @since 0.4.1 */ s.playEmptySound = function() { - // create empty buffer - var buffer = this.context.createBuffer(1, 1, 22050); - var source = this.context.createBufferSource(); - source.buffer = buffer; - - // connect to output (your speakers) - source.connect(this.context.destination); - - // play the file + var source = s.context.createBufferSource(); + source.buffer = s.context.createBuffer(1, 1, 22050); + source.connect(s.context.destination); source.start(0, 0, 0); }; @@ -2888,7 +2751,6 @@ this.createjs = this.createjs || {}; * @default 1 * @protected */ - // TODO refactor Sound.js so we can use getter setter for volume p._volume = 1; /** @@ -2910,20 +2772,24 @@ this.createjs = this.createjs || {}; /** * A DynamicsCompressorNode, which is used to improve sound quality and prevent audio distortion. * It is connected to context.destination. + * + * Can be accessed by advanced users through createjs.Sound.activePlugin.dynamicsCompressorNode. * @property dynamicsCompressorNode * @type {AudioNode} */ p.dynamicsCompressorNode = null; /** - * A GainNode for controlling master _volume. It is connected to {{#crossLink "WebAudioPlugin/dynamicsCompressorNode:property"}}{{/crossLink}}. + * A GainNode for controlling master volume. It is connected to {{#crossLink "WebAudioPlugin/dynamicsCompressorNode:property"}}{{/crossLink}}. + * + * Can be accessed by advanced users through createjs.Sound.activePlugin.gainNode. * @property gainNode * @type {AudioGainNode} */ p.gainNode = null; /** - * An object hash used internally to store ArrayBuffers, indexed by the source URI used to load it. This + * An object hash used internally to store ArrayBuffers, indexed by the source URI used to load it. This * prevents having to load and decode audio files more than once. If a load has been started on a file, * arrayBuffers[src] will be set to true. Once load is complete, it is set the the loaded * ArrayBuffer instance. @@ -2943,8 +2809,13 @@ this.createjs = this.createjs || {}; this._arrayBuffers = {}; this.context = s.context; - this.gainNode = s.gainNode; - this.dynamicsCompressorNode = s.dynamicsCompressorNode; + this._panningModel = s._panningModel; + + // set up AudioNodes that all of our source audio will connect to + this.dynamicsCompressorNode = this.context.createDynamicsCompressor(); + this.dynamicsCompressorNode.connect(this.context.destination); + this.gainNode = this.context.createGain(); + this.gainNode.connect(this.dynamicsCompressorNode); }; /** @@ -2958,11 +2829,9 @@ this.createjs = this.createjs || {}; * @return {Object} A result object, containing a "tag" for preloading purposes. */ p.register = function (src, instances) { - this._arrayBuffers[src] = true; // This is needed for PreloadJS - var tag = new createjs.WebAudioPlugin.Loader(src, this); - return { - tag:tag - }; + this._arrayBuffers[src] = true; + var loader = {tag: new createjs.WebAudioPlugin.Loader(src, this)}; + return loader; }; /** @@ -3021,19 +2890,18 @@ this.createjs = this.createjs || {}; * @method _handlePreloadComplete * @protected */ - p._handlePreloadComplete = function () { - //LM: I would recommend having the Loader include an "event" in the onload, and properly binding this callback. - createjs.Sound._sendFileLoadEvent(this.src); // fire event or callback on Sound - // note "this" will reference Loader object + p._handlePreloadComplete = function (loader) { + createjs.Sound._sendFileLoadEvent(loader.src); + loader.cleanUp(); }; /** * Internally preload a sound. Loading uses XHR2 to load an array buffer for use with WebAudio. * @method preload * @param {String} src The sound URI to load. - * @param {Object} instance Not used in this plugin. + * @param {Object} tag Not used in this plugin. */ - p.preload = function (src, instance) { + p.preload = function (src, tag) { this._arrayBuffers[src] = true; var loader = new createjs.WebAudioPlugin.Loader(src, this); loader.onload = this._handlePreloadComplete; @@ -3047,9 +2915,7 @@ this.createjs = this.createjs || {}; * @return {SoundInstance} A sound instance for playback and control. */ p.create = function (src) { - if (!this.isPreloadStarted(src)) { - this.preload(src); - } + if (!this.isPreloadStarted(src)) {this.preload(src);} return new createjs.WebAudioPlugin.SoundInstance(src, this); }; @@ -3116,7 +2982,6 @@ this.createjs = this.createjs || {}; * for control by the user. * *

    Example

    - * * var myInstance = createjs.Sound.play("myAssetPath/mySrcFile.mp3"); * * A number of additional parameters provide a quick way to determine how a sound is played. Please see the Sound @@ -3198,16 +3063,6 @@ this.createjs = this.createjs || {}; */ p._offset = 0; - /** - * The time in milliseconds before the sound starts. - * Note this is handled by {{#crossLink "Sound"}}{{/crossLink}}. - * @property _delay - * @type {Number} - * @default 0 - * @protected - */ - p._delay = 0; // OJR remove this property from SoundInstance as it is not used here? - /** * The volume of the sound, between 0 and 1. *
    Note this uses a getter setter, which is not supported by Firefox versions 3.6 or lower and Opera versions 11.50 or lower, @@ -3221,8 +3076,7 @@ this.createjs = this.createjs || {}; * @default 1 */ p._volume = 1; - // IE8 has Object.defineProperty, but only for DOM objects, so check if fails to suppress errors - try { + if (createjs.definePropertySupported) { Object.defineProperty(p, "volume", { get: function() { return this._volume; @@ -3234,9 +3088,7 @@ this.createjs = this.createjs || {}; this._updateVolume(); } }); - } catch (e) { - // dispatch message or error? - }; + } /** * The pan of the sound, between -1 (left) and 1 (right). Note that pan is not supported by HTML Audio. @@ -3250,8 +3102,7 @@ this.createjs = this.createjs || {}; * @default 0 */ p._pan = 0; - // IE8 has Object.defineProperty, but only for DOM objects, so check if fails to suppress errors - try { + if (createjs.definePropertySupported) { Object.defineProperty(p, "pan", { get: function() { return this._pan; @@ -3265,10 +3116,7 @@ this.createjs = this.createjs || {}; this.panNode.setPosition(value, 0, -0.5); // z need to be -0.5 otherwise the sound only plays in left, right, or center } }); - } catch (e) { - // dispatch message or error? - }; - + } /** * The length of the audio clip, in milliseconds. @@ -3291,7 +3139,7 @@ this.createjs = this.createjs || {}; /** * A Timeout created by {{#crossLink "Sound"}}{{/crossLink}} when this SoundInstance is played with a delay. - * This allows SoundInstance to remove the delay if stop or pause or cleanup are called before playback begins. + * This allows SoundInstance to remove the delay if stop, pause, or cleanup are called before playback begins. * @property _delayTimeoutId * @type {timeoutVariable} * @default null @@ -3432,43 +3280,6 @@ this.createjs = this.createjs || {}; * @since 0.4.0 */ - //TODO: Deprecated - /** - * REMOVED. Use {{#crossLink "EventDispatcher/addEventListener"}}{{/crossLink}} and the {{#crossLink "SoundInstance/succeeded:event"}}{{/crossLink}} - * event. - * @property onPlaySucceeded - * @type {Function} - * @deprecated Use addEventListener and the "succeeded" event. - */ - /** - * REMOVED. Use {{#crossLink "EventDispatcher/addEventListener"}}{{/crossLink}} and the {{#crossLink "SoundInstance/interrupted:event"}}{{/crossLink}} - * event. - * @property onPlayInterrupted - * @type {Function} - * @deprecated Use addEventListener and the "interrupted" event. - */ - /** - * REMOVED. Use {{#crossLink "EventDispatcher/addEventListener"}}{{/crossLink}} and the {{#crossLink "SoundInstance/failed:event"}}{{/crossLink}} - * event. - * @property onPlayFailed - * @type {Function} - * @deprecated Use addEventListener and the "failed" event. - */ - /** - * REMOVED. Use {{#crossLink "EventDispatcher/addEventListener"}}{{/crossLink}} and the {{#crossLink "SoundInstance/complete:event"}}{{/crossLink}} - * event. - * @property onComplete - * @type {Function} - * @deprecated Use addEventListener and the "complete" event. - */ - /** - * REMOVED. Use {{#crossLink "EventDispatcher/addEventListener"}}{{/crossLink}} and the {{#crossLink "SoundInstance/loop:event"}}{{/crossLink}} - * event. - * @property onLoop - * @type {Function} - * @deprecated Use addEventListener and the "loop" event. - */ - /** * A helper method that dispatches all events for SoundInstance. * @method _sendEvent @@ -3489,18 +3300,16 @@ this.createjs = this.createjs || {}; * @protected */ p._init = function (src, owner) { - this._owner = owner; this.src = src; + this._owner = owner; this.gainNode = this._owner.context.createGain(); - this.panNode = this._owner.context.createPanner(); //TODO test how this affects when we have mono audio + this.panNode = this._owner.context.createPanner(); this.panNode.panningModel = this._owner._panningModel; this.panNode.connect(this.gainNode); - if (this._owner.isPreloadComplete(this.src)) { - this._duration = this._owner._arrayBuffers[this.src].duration * 1000; - } + if (this._owner.isPreloadComplete(this.src)) {this._duration = this._owner._arrayBuffers[this.src].duration * 1000;} this._endedHandler = createjs.proxy(this._handleSoundComplete, this); }; @@ -3516,9 +3325,7 @@ this.createjs = this.createjs || {}; this._sourceNodeNext = this._cleanUpAudioNode(this._sourceNodeNext); } - if (this.gainNode.numberOfOutputs != 0) { - this.gainNode.disconnect(0); - } // this works because we only have one connection, and it returns 0 if we've already disconnected it. + if (this.gainNode.numberOfOutputs != 0) {this.gainNode.disconnect(0);} // OJR there appears to be a bug that this doesn't always work in webkit (Chrome and Safari). According to the documentation, this should work. clearTimeout(this._delayTimeoutId); // clear timeout that plays delayed sound @@ -3526,9 +3333,6 @@ this.createjs = this.createjs || {}; this._startTime = 0; // This is used by getPosition - if (window.createjs == null) { - return; - } createjs.Sound._playFinished(this); }; @@ -3544,7 +3348,7 @@ this.createjs = this.createjs || {}; if(audioNode) { audioNode.stop(0); audioNode.disconnect(this.panNode); - audioNode = null; // release reference so Web Audio can handle removing references and garbage collection + audioNode = null; } return audioNode; }; @@ -3567,11 +3371,7 @@ this.createjs = this.createjs || {}; * @protected */ p._handleSoundReady = function (event) { - if (window.createjs == null) { - return; - } - - if ((this._offset*1000) > this.getDuration()) { // converting offset to ms + if ((this._offset*1000) > this.getDuration()) { this.playFailed(); return; } else if (this._offset < 0) { // may not need this check if play ignores negative values, this is not specified in the API http://www.w3.org/TR/webaudio/#AudioBufferSourceNode @@ -3609,7 +3409,7 @@ this.createjs = this.createjs || {}; audioNode.buffer = this._owner._arrayBuffers[this.src]; audioNode.connect(this.panNode); var currentTime = this._owner.context.currentTime; - audioNode.startTime = startTime + audioNode.buffer.duration; //currentTime + audioNode.buffer.duration - (currentTime - startTime); + audioNode.startTime = startTime + audioNode.buffer.duration; audioNode.start(audioNode.startTime, offset, audioNode.buffer.duration - offset); return audioNode; }; @@ -3654,14 +3454,6 @@ this.createjs = this.createjs || {}; * @protected */ p._beginPlaying = function (offset, loop, volume, pan) { - if (window.createjs == null) { - return; - } - - if (!this.src) { - return; - } - this._offset = offset / 1000; //convert ms to sec this._remainingLoops = loop; this.volume = volume; @@ -3689,22 +3481,19 @@ this.createjs = this.createjs || {}; * @return {Boolean} If the pause call succeeds. This will return false if the sound isn't currently playing. */ p.pause = function () { - if (!this._paused && this.playState == createjs.Sound.PLAY_SUCCEEDED) { - this.paused = this._paused = true; + if (this._paused || this.playState != createjs.Sound.PLAY_SUCCEEDED) {return false;} - this._offset = this._owner.context.currentTime - this._startTime; // this allows us to restart the sound at the same point in playback - this.sourceNode = this._cleanUpAudioNode(this.sourceNode); - this.sourceNodeNext = this._cleanUpAudioNode(this._sourceNodeNext); + this.paused = this._paused = true; - if (this.gainNode.numberOfOutputs != 0) { - this.gainNode.disconnect(); - } // this works because we only have one connection, and it returns 0 if we've already disconnected it. + this._offset = this._owner.context.currentTime - this._startTime; // this allows us to restart the sound at the same point in playback + this.sourceNode = this._cleanUpAudioNode(this.sourceNode); + this._sourceNodeNext = this._cleanUpAudioNode(this._sourceNodeNext); - clearTimeout(this._delayTimeoutId); // clear timeout that plays delayed sound - clearTimeout(this._soundCompleteTimeout); // clear timeout that triggers sound complete - return true; - } - return false; + if (this.gainNode.numberOfOutputs != 0) {this.gainNode.disconnect();} + + clearTimeout(this._delayTimeoutId); + clearTimeout(this._soundCompleteTimeout); + return true; }; /** @@ -3721,10 +3510,8 @@ this.createjs = this.createjs || {}; * @return {Boolean} If the resume call succeeds. This will return false if called on a sound that is not paused. */ p.resume = function () { - if (!this._paused) { - return false; - } - this._handleSoundReady(null); + if (!this._paused) {return false;} + this._handleSoundReady(); return true; }; @@ -3764,23 +3551,20 @@ this.createjs = this.createjs || {}; */ p.setVolume = function (value) { this.volume = value; - return true; // This is always true because even if the volume is not updated, the value is set + return true; }; /** * Internal function used to update the volume based on the instance volume, master volume, instance mute value, * and master mute value. * @method _updateVolume - * @return {Boolean} if the volume was updated. * @protected */ p._updateVolume = function () { var newVolume = this._muted ? 0 : this._volume; if (newVolume != this.gainNode.gain.value) { this.gainNode.gain.value = newVolume; - return true; } - return false; }; /** @@ -3810,9 +3594,7 @@ this.createjs = this.createjs || {}; * @since 0.4.0 */ p.setMute = function (value) { - if (value == null || value == undefined) { - return false; - } + if (value == null) {return false;} this._muted = value; this._updateVolume(); @@ -3852,6 +3634,7 @@ this.createjs = this.createjs || {}; p.setPan = function (value) { this.pan = value; // Unfortunately panner does not give us a way to access this after it is set http://www.w3.org/TR/webaudio/#AudioPannerNode if(this.pan != value) {return false;} + return true; }; /** @@ -3912,9 +3695,7 @@ this.createjs = this.createjs || {}; clearTimeout(this._soundCompleteTimeout); // clear timeout that triggers sound complete } // NOTE we cannot just call cleanup because it also calls the Sound function _playFinished which releases this instance in SoundChannel - if (!this._paused && this.playState == createjs.Sound.PLAY_SUCCEEDED) { - this._handleSoundReady(null); - } + if (!this._paused && this.playState == createjs.Sound.PLAY_SUCCEEDED) {this._handleSoundReady();} return true; }; @@ -3960,16 +3741,13 @@ this.createjs = this.createjs || {}; this._soundCompleteTimeout = setTimeout(this._endedHandler, this._duration); } else { - this._handleSoundReady(null); + this._handleSoundReady(); } this._sendEvent("loop"); return; } - if (window.createjs == null) { - return; - } this._cleanUp(); this.playState = createjs.Sound.PLAY_FINISHED; this._sendEvent("complete"); @@ -3977,9 +3755,6 @@ this.createjs = this.createjs || {}; // Play has failed, which can happen for a variety of reasons. p.playFailed = function () { - if (window.createjs == null) { - return; - } this._cleanUp(); this.playState = createjs.Sound.PLAY_FAILED; this._sendEvent("failed"); @@ -4023,13 +3798,6 @@ this.createjs = this.createjs || {}; */ p.src = null; - /** - * The original source of the sound, before it is altered with a basePath. - * #property src - * @type {String} - */ - p.originalSrc = null; - /** * The decoded AudioBuffer array that is returned when loading is complete. * #property result @@ -4054,17 +3822,16 @@ this.createjs = this.createjs || {}; p.onprogress = null; /** - * The callback that fires if the load hits an error. - * #property onError + * The callback that fires if the load hits an error. This follows HTML tag naming. + * #property onerror * @type {Method} * @protected */ - p.onError = null; + p.onerror = null; // constructor p._init = function (src, owner) { this.src = src; - this.originalSrc = src; this.owner = owner; }; @@ -4074,16 +3841,13 @@ this.createjs = this.createjs || {}; * @param {String} src The path to the sound. */ p.load = function (src) { - if (src != null) { - // TODO does this need to set this.originalSrc - this.src = src; - } + if (src != null) {this.src = src;} this.request = new XMLHttpRequest(); this.request.open("GET", this.src, true); this.request.responseType = "arraybuffer"; this.request.onload = createjs.proxy(this.handleLoad, this); - this.request.onError = createjs.proxy(this.handleError, this); + this.request.onerror = createjs.proxy(this.handleError, this); this.request.onprogress = createjs.proxy(this.handleProgress, this); this.request.send(); @@ -4101,7 +3865,7 @@ this.createjs = this.createjs || {}; */ p.handleProgress = function (loaded, total) { this.progress = loaded / total; - this.onprogress != null && this.onprogress({loaded:loaded, total:total, progress:this.progress}); + this.onprogress && this.onprogress({loaded:loaded, total:total, progress:this.progress}); }; /** @@ -4123,9 +3887,8 @@ this.createjs = this.createjs || {}; p.handleAudioDecoded = function (decodedAudio) { this.progress = 1; this.result = decodedAudio; - this.src = this.originalSrc; this.owner.addPreloadResults(this.src, this.result); - this.onload && this.onload(); + this.onload && this.onload(this); }; /** @@ -4138,6 +3901,23 @@ this.createjs = this.createjs || {}; this.onerror && this.onerror(evt); }; + /** + * Remove all external references from loader + * #method cleanUp + */ + p.cleanUp = function () { + if(!this.request) {return;} + this.src = null; + this.owner = null; + this.request.onload = null; + this.request.onerror = null; + this.request.onprogress = null; + this.request = null; + this.onload = null; + this.onprogress = null; + this.onerror = null; + }; + p.toString = function () { return "[WebAudioPlugin Loader]"; }; @@ -4197,12 +3977,13 @@ this.createjs = this.createjs || {}; * tags are precreated to allow Chrome to load them. Please use {{#crossLink "Sound.MAX_INSTANCES"}}{{/crossLink}} as * a guide to how many total audio tags you can safely use in all browsers. * - * IE 9 html limitations
    + * IE html limitations
    * * @@ -4325,14 +4106,9 @@ this.createjs = this.createjs || {}; * @static */ s.isSupported = function () { - if (createjs.Sound.BrowserDetect.isIOS && !s.enableIOS) { - return false; - } + if (createjs.Sound.BrowserDetect.isIOS && !s.enableIOS) {return false;} s._generateCapabilities(); - var t = s.tag; // OJR do we still need this check, when cap will already be null if this is the case - if (t == null || s._capabilities == null) { - return false; - } + if (s._capabilities == null) {return false;} return true; }; @@ -4344,13 +4120,9 @@ this.createjs = this.createjs || {}; * @protected */ s._generateCapabilities = function () { - if (s._capabilities != null) { - return; - } - var t = s.tag = document.createElement("audio"); - if (t.canPlayType == null) { - return null; - } + if (s._capabilities != null) {return;} + var t = document.createElement("audio"); + if (t.canPlayType == null) {return null;} s._capabilities = { panning:true, @@ -4383,7 +4155,7 @@ this.createjs = this.createjs || {}; p._audioSources = null; /** - * The default number of instances to allow. Passed back to {{#crossLink "Sound"}}{{/crossLink}} when a source + * The default number of instances to allow. Used by {{#crossLink "Sound"}}{{/crossLink}} when a source * is registered using the {{#crossLink "Sound/register"}}{{/crossLink}} method. This is only used if * a value is not provided. * @@ -4395,9 +4167,6 @@ this.createjs = this.createjs || {}; */ p.defaultNumChannels = 2; - // Proxies, make removing listeners easier. - p.loadedHandler = null; - /** * An initialization function run by the constructor * @method _init @@ -4422,52 +4191,17 @@ this.createjs = this.createjs || {}; this._audioSources[src] = true; // Note this does not mean preloading has started var channel = createjs.HTMLAudioPlugin.TagPool.get(src); var tag = null; - var l = instances || this.defaultNumChannels; - for (var i = 0; i < l; i++) { // OJR should we be enforcing s.MAX_INSTANCES here? Does the chrome bug still exist, or can we change this code? + var l = instances; + for (var i = 0; i < l; i++) { tag = this._createTag(src); channel.add(tag); } - tag.id = src; // co-opting id as we need a way to store original src in case it is changed before loading - this.loadedHandler = createjs.proxy(this._handleTagLoad, this); // we need this bind to be able to remove event listeners - tag.addEventListener && tag.addEventListener("canplaythrough", this.loadedHandler); - if(tag.onreadystatechange == null) { - tag.onreadystatechange = this.loadedHandler; - } else { - var f = tag.onreadystatechange; - // OJR will this lose scope? - tag.onreadystatechange = function() { - f(); - this.loadedHandler(); - } - } - return { - tag:tag, // Return one instance for preloading purposes - numChannels:l // The default number of channels to make for this Sound or the passed in value + tag:tag // Return one instance for preloading purposes }; }; - // TODO remove this when | approach is removed - /** - * Deprecated as this will not be required with new approach to basePath. - * Checks if src was changed on tag used to create instances in TagPool before loading - * Currently PreloadJS does this when a basePath is set, so we are replicating that behavior for internal preloading. - * @method _handleTagLoad - * @param event - * @protected - * @deprecated - */ - p._handleTagLoad = function(event) { - // cleanup and so we don't send the event more than once - event.target.removeEventListener && event.target.removeEventListener("canplaythrough", this.loadedHandler); - event.target.onreadystatechange = null; - - if (event.target.src == event.target.id) { return; } - // else src has changed before loading, and we need to make the change to TagPool because we pre create tags - createjs.HTMLAudioPlugin.TagPool.checkSrc(event.target.id); - }; - /** * Create an HTML audio tag. * @method _createTag @@ -4503,7 +4237,7 @@ this.createjs = this.createjs || {}; * @since 0.4.1 */ p.removeAllSounds = function () { - this._audioSources = {}; // this drops all references, in theory freeing them for garbage collection + this._audioSources = {}; createjs.HTMLAudioPlugin.TagPool.removeAll(); }; @@ -4541,12 +4275,12 @@ this.createjs = this.createjs || {}; * Internally preload a sound. * @method preload * @param {String} src The sound URI to load. - * @param {Object} instance An object containing a tag property that is an HTML audio tag used to load src. + * @param {Object} tag An HTML audio tag used to load src. * @since 0.4.0 */ - p.preload = function (src, instance) { + p.preload = function (src, tag) { this._audioSources[src] = true; - new createjs.HTMLAudioPlugin.Loader(src, instance.tag); + new createjs.HTMLAudioPlugin.Loader(src, tag); }; p.toString = function () { @@ -4575,10 +4309,8 @@ this.createjs = this.createjs || {}; p._owner = null; p.loaded = false; p._offset = 0; - p._delay = 0; p._volume = 1; - // IE8 has Object.defineProperty, but only for DOM objects, so check if fails to suppress errors - try { + if (createjs.definePropertySupported) { Object.defineProperty(p, "volume", { get: function() { return this._volume; @@ -4590,9 +4322,7 @@ this.createjs = this.createjs || {}; this._updateVolume(); } }); - } catch (e) { - // dispatch message or error? - }; + } p.pan = 0; p._duration = 0; p._remainingLoops = 0; @@ -4613,6 +4343,8 @@ this.createjs = this.createjs || {}; this.src = src; this._owner = owner; + this._duration = createjs.HTMLAudioPlugin.TagPool.getDuration(this.src); + this._endedHandler = createjs.proxy(this._handleSoundComplete, this); this._readyHandler = createjs.proxy(this._handleSoundReady, this); this._stalledHandler = createjs.proxy(this._handleSoundStalled, this); @@ -4640,16 +4372,11 @@ this.createjs = this.createjs || {}; } clearTimeout(this._delayTimeoutId); - if (window.createjs == null) { - return; - } createjs.Sound._playFinished(this); }; p._interrupt = function () { - if (this.tag == null) { - return; - } + if (this.tag == null) {return;} this.playState = createjs.Sound.PLAY_INTERRUPTED; this._cleanUp(); this.paused = this._paused = false; @@ -4658,14 +4385,11 @@ this.createjs = this.createjs || {}; // Public API p.play = function (interrupt, delay, offset, loop, volume, pan) { - this._cleanUp(); //LM: Is this redundant? + this._cleanUp(); createjs.Sound._playInstance(this, interrupt, delay, offset, loop, volume, pan); }; p._beginPlaying = function (offset, loop, volume, pan) { - if (window.createjs == null) { - return -1; - } var tag = this.tag = createjs.HTMLAudioPlugin.TagPool.getInstance(this.src); if (tag == null) { this.playFailed(); @@ -4677,8 +4401,7 @@ this.createjs = this.createjs || {}; // Reset this instance. this._offset = offset; this.volume = volume; - this.pan = pan; // not pan has no effect - this._updateVolume(); // note this will set for mute and _masterMute + this._updateVolume(); this._remainingLoops = loop; if (tag.readyState !== 4) { @@ -4697,24 +4420,17 @@ this.createjs = this.createjs || {}; // Note: Sounds stall when trying to begin playback of a new audio instance when the existing instances // has not loaded yet. This doesn't mean the sound will not play. p._handleSoundStalled = function (event) { - this._cleanUp(); // OJR NOTE this will stop playback, and I think we should remove this and let the developer decide how to handle stalled instances + this._cleanUp(); // OJR this will stop playback, we could remove this and let the developer decide how to handle stalled instances this._sendEvent("failed"); }; p._handleSoundReady = function (event) { - if (window.createjs == null) { - return; - } - - // OJR would like a cleaner way to do this in _init, discuss with LM - this._duration = this.tag.duration * 1000; // need this for setPosition on stopped sounds - this.playState = createjs.Sound.PLAY_SUCCEEDED; this.paused = this._paused = false; this.tag.removeEventListener(createjs.HTMLAudioPlugin._AUDIO_READY, this._readyHandler, false); if (this._offset >= this.getDuration()) { - this.playFailed(); // OJR: throw error? + this.playFailed(); return; } else if (this._offset > 0) { this.tag.currentTime = this._offset * 0.001; @@ -4732,20 +4448,15 @@ this.createjs = this.createjs || {}; p.pause = function () { if (!this._paused && this.playState == createjs.Sound.PLAY_SUCCEEDED && this.tag != null) { this.paused = this._paused = true; - // Note: when paused by user, we hold a reference to our tag. We do not release it until stopped. this.tag.pause(); - clearTimeout(this._delayTimeoutId); - return true; } return false; }; p.resume = function () { - if (!this._paused || this.tag == null) { - return false; - } + if (!this._paused || this.tag == null) {return false;} this.paused = this._paused = false; this.tag.play(); return true; @@ -4761,7 +4472,6 @@ this.createjs = this.createjs || {}; p.setMasterVolume = function (value) { this._updateVolume(); - return true; }; p.setVolume = function (value) { @@ -4772,12 +4482,7 @@ this.createjs = this.createjs || {}; p._updateVolume = function () { if (this.tag != null) { var newVolume = (this._muted || createjs.Sound._masterMute) ? 0 : this._volume * createjs.Sound._masterVolume; - if (newVolume != this.tag.volume) { - this.tag.volume = newVolume; - } - return true; - } else { - return false; + if (newVolume != this.tag.volume) {this.tag.volume = newVolume;} } }; @@ -4787,14 +4492,10 @@ this.createjs = this.createjs || {}; p.setMasterMute = function (isMuted) { this._updateVolume(); - return true; }; p.setMute = function (isMuted) { - if (isMuted == null || isMuted == undefined) { - return false; - } - + if (isMuted == null) {return false;} this._muted = isMuted; this._updateVolume(); return true; @@ -4804,7 +4505,7 @@ this.createjs = this.createjs || {}; return this._muted; }; - // Can not set pan in HTML + // Can not set pan in HTML audio p.setPan = function (value) { return false; }; @@ -4814,9 +4515,7 @@ this.createjs = this.createjs || {}; }; p.getPosition = function () { - if (this.tag == null) { - return this._offset; - } + if (this.tag == null) {return this._offset;} return this.tag.currentTime * 1000; }; @@ -4841,21 +4540,15 @@ this.createjs = this.createjs || {}; p._handleSoundComplete = function (event) { this._offset = 0; - - if (window.createjs == null) { - return; - } this.playState = createjs.Sound.PLAY_FINISHED; this._cleanUp(); this._sendEvent("complete"); }; - // handles looping functionality // NOTE with this approach audio will loop as reliably as the browser allows // but we could end up sending the loop event after next loop playback begins p.handleSoundLoop = function (event) { this._offset = 0; - this._remainingLoops--; if(this._remainingLoops == 0) { this.tag.loop = false; @@ -4865,9 +4558,6 @@ this.createjs = this.createjs || {}; }; p.playFailed = function () { - if (window.createjs == null) { - return; - } this.playState = createjs.Sound.PLAY_FAILED; this._cleanUp(); this._sendEvent("failed"); @@ -4946,12 +4636,12 @@ this.createjs = this.createjs || {}; this.loadedHandler = createjs.proxy(this.sendLoadedEvent, this); // we need this bind to be able to remove event listeners this.tag.addEventListener && this.tag.addEventListener("canplaythrough", this.loadedHandler); if(this.tag.onreadystatechange == null) { - this.tag.onreadystatechange = createjs.proxy(this.sendLoadedEvent, this); // OJR not 100% sure we need this, just copied from PreloadJS + this.tag.onreadystatechange = createjs.proxy(this.sendLoadedEvent, this); } else { var f = this.tag.onreadystatechange; this.tag.onreadystatechange = function() { f(); - this.tag.onreadystatechange = createjs.proxy(this.sendLoadedEvent, this); // OJR not 100% sure we need this, just copied from PreloadJS + this.tag.onreadystatechange = createjs.proxy(this.sendLoadedEvent, this); } } @@ -4994,6 +4684,7 @@ this.createjs = this.createjs || {}; this.tag.removeEventListener && this.tag.removeEventListener("canplaythrough", this.loadedHandler); // cleanup and so we don't send the event more than once this.tag.onreadystatechange = null; // cleanup and so we don't send the event more than once createjs.Sound._sendFileLoadEvent(this.src); // fire event or callback on Sound + }; // used for debugging @@ -5056,9 +4747,7 @@ this.createjs = this.createjs || {}; */ s.remove = function (src) { var channel = s.tags[src]; - if (channel == null) { - return false; - } + if (channel == null) {return false;} channel.removeAll(); delete(s.tags[src]); return true; @@ -5085,9 +4774,7 @@ this.createjs = this.createjs || {}; */ s.getInstance = function (src) { var channel = s.tags[src]; - if (channel == null) { - return null; - } + if (channel == null) {return null;} return channel.get(); }; @@ -5101,27 +4788,20 @@ this.createjs = this.createjs || {}; */ s.setInstance = function (src, tag) { var channel = s.tags[src]; - if (channel == null) { - return null; - } + if (channel == null) {return null;} return channel.set(tag); }; /** - * A function to check if src has changed in the loaded audio tag. - * This is required because PreloadJS appends a basePath to the src before loading. - * Note this is currently only called when a change is detected - * #method checkSrc - * @param src the unaltered src that is used to store the channel. - * @static - * @protected + * Gets the duration of the src audio in milliseconds + * #method getDuration + * @param {String} src The source file used by the audio tag. + * @return {Number} Duration of src in milliseconds */ - s.checkSrc = function (src) { + s.getDuration= function (src) { var channel = s.tags[src]; - if (channel == null) { - return null; - } - channel.checkSrcChange(); + if (channel == null) {return 0;} + return channel.getDuration(); }; var p = TagPool.prototype; @@ -5161,6 +4841,15 @@ this.createjs = this.createjs || {}; */ p.tags = null; + /** + * The duration property of all audio tags, converted to milliseconds, which originally is only available on the + * last tag in the tags array because that is the one that is loaded. + * #property + * @type {Number} + * @protected + */ + p.duration = 0; + // constructor p._init = function (src) { this.src = src; @@ -5197,14 +4886,10 @@ this.createjs = this.createjs || {}; * @return {HTMLAudioElement} An HTML audio tag. */ p.get = function () { - if (this.tags.length == 0) { - return null; - } + if (this.tags.length == 0) {return null;} this.available = this.tags.length; var tag = this.tags.pop(); - if (tag.parentNode == null) { - document.body.appendChild(tag); - } + if (tag.parentNode == null) {document.body.appendChild(tag);} return tag; }; @@ -5215,26 +4900,19 @@ this.createjs = this.createjs || {}; */ p.set = function (tag) { var index = createjs.indexOf(this.tags, tag); - if (index == -1) { - this.tags.push(tag); - } + if (index == -1) {this.tags.push(tag);} this.available = this.tags.length; }; /** - * Make sure the src of all other tags is correct after load. - * This is needed because PreloadJS appends a basePath to src before loading. - * #method checkSrcChange + * Gets the duration for the src audio and on first call stores it to this.duration + * #method getDuration + * @return {Number} Duration of the src in milliseconds */ - p.checkSrcChange = function () { - // the last tag always has the latest src after loading - //var i = this.length-1; // this breaks in Firefox because it is not correctly removing an event listener - var i = this.tags.length - 1; - if(i == -1) return; // CodeCombat addition; sometimes errors in IE without this... - var newSrc = this.tags[i].src; - while(i--) { - this.tags[i].src = newSrc; - } + p.getDuration = function () { + // this will work because this will be only be run the first time a sound instance is created and before any tags are taken from the pool + if (!this.duration) {this.duration = this.tags[this.tags.length - 1].duration * 1000;} + return this.duration; }; p.toString = function () {