PageSourceSearch

http://etlady.tw/js/WebAudioPlugin.js

js etlady.tw collected 2026-10-07 01:35:58 UTC 52,131 bytes, 1,519 lines download raw bytes

1/*
2 * WebAudioPlugin
3 * Visit http://createjs.com/ for documentation, updates and examples.
4 *
5 *
6 * Copyright (c) 2012 gskinner.com, inc.
7 *
8 * Permission is hereby granted, free of charge, to any person
9 * obtaining a copy of this software and associated documentation
10 * files (the "Software"), to deal in the Software without
11 * restriction, including without limitation the rights to use,
12 * copy, modify, merge, publish, distribute, sublicense, and/or sell
13 * copies of the Software, and to permit persons to whom the
14 * Software is furnished to do so, subject to the following
15 * conditions:
16 *
17 * The above copyright notice and this permission notice shall be
18 * included in all copies or substantial portions of the Software.
19 *
20 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
22 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
24 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
25 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
27 * OTHER DEALINGS IN THE SOFTWARE.
28 */
29
30/**
31 * @module SoundJS
32 */
33
34// namespace:
35this.createjs = this.createjs || {};
36
37(function () {
38
39	"use strict";
40
41	/**
42	 * Play sounds using Web Audio in the browser. The WebAudioPlugin is currently the default plugin, and will be used
43	 * anywhere that it is supported. To change plugin priority, check out the Sound API
44	 * {{#crossLink "Sound/registerPlugins"}}{{/crossLink}} method.
45
46	 * <h4>Known Browser and OS issues for Web Audio</h4>
47	 * <b>Firefox 25</b>
48	 * <ul><li>mp3 audio files do not load properly on all windows machines, reported
49	 * <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=929969" target="_blank">here</a>. </br>
50	 * For this reason it is recommended to pass another FF supported type (ie ogg) first until this bug is resolved, if possible.</li></ul>
51	 * <br />
52	 * <b>Webkit (Chrome and Safari)</b>
53	 * <ul><li>AudioNode.disconnect does not always seem to work.  This can cause the file size to grow over time if you
54	 * are playing a lot of audio files.</li></ul>
55	 * <br />
56	 * <b>iOS 6 limitations</b>
57	 * 	<ul><li>Sound is initially muted and will only unmute through play being called inside a user initiated event (touch/click).</li>
58	 *	<li>A bug exists that will distort uncached audio when a video element is present in the DOM.  You can avoid this bug
59	 * 	by ensuring the audio and video audio share the same sampleRate.</li>
60	 * </ul>
61	 * @class WebAudioPlugin
62	 * @constructor
63	 * @since 0.4.0
64	 */
65	function WebAudioPlugin() {
66		this._init();
67	}
68
69
70	var s = WebAudioPlugin;
71
72	/**
73	 * The capabilities of the plugin. This is generated via the {{#crossLink "WebAudioPlugin/_generateCapabilities:method"}}{{/crossLink}}
74	 * method and is used internally.
75	 * @property _capabilities
76	 * @type {Object}
77	 * @default null
78	 * @protected
79	 * @static
80	 */
81	s._capabilities = null;
82
83	/**
84	 * Determine if the plugin can be used in the current browser/OS.
85	 * @method isSupported
86	 * @return {Boolean} If the plugin can be initialized.
87	 * @static
88	 */
89	s.isSupported = function () {
90		// check if this is some kind of mobile device, Web Audio works with local protocol under PhoneGap and it is unlikely someone is trying to run a local file
91		var isMobilePhoneGap = createjs.Sound.BrowserDetect.isIOS || createjs.Sound.BrowserDetect.isAndroid || createjs.Sound.BrowserDetect.isBlackberry;
92		// OJR isMobile may be redundant with _isFileXHRSupported available.  Consider removing.
93		if (location.protocol == "file:" && !isMobilePhoneGap && !this._isFileXHRSupported()) { return false; }  // Web Audio requires XHR, which is not usually available locally
94		s._generateCapabilities();
95		if (s.context == null) {
96			return false;
97		}
98		return true;
99	};
100
101	/**
102	 * Determine if XHR is supported, which is necessary for web audio.
103	 * @method _isFileXHRSupported
104	 * @return {Boolean} If XHR is supported.
105	 * @since 0.4.2
106	 * @protected
107	 * @static
108	 */
109	s._isFileXHRSupported = function() {
110		// it's much easier to detect when something goes wrong, so let's start optimistically
111		var supported = true;
112
113		var xhr = new XMLHttpRequest();
114		try {
115			xhr.open("GET", "fail.fail", false); // loading non-existant file triggers 404 only if it could load (synchronous call)
116		} catch (error) {
117			// catch errors in cases where the onerror is passed by
118			supported = false;
119			return supported;
120		}
121		xhr.onerror = function() { supported = false; }; // cause irrelevant
122		// with security turned off, we can get empty success results, which is actually a failed read (status code 0?)
123		xhr.onload = function() { supported = this.status == 404 || (this.status == 200 || (this.status == 0 && this.response != "")); };
124		try {
125			xhr.send();
126		} catch (error) {
127			// catch errors in cases where the onerror is passed by
128			supported = false;
129		}
130
131		return supported;
132	};
133
134	/**
135	 * Determine the capabilities of the plugin. Used internally. Please see the Sound API {{#crossLink "Sound/getCapabilities"}}{{/crossLink}}
136	 * method for an overview of plugin capabilities.
137	 * @method _generateCapabilities
138	 * @static
139	 * @protected
140	 */
141	s._generateCapabilities = function () {
142		if (s._capabilities != null) {
143			return;
144		}
145		// Web Audio can be in any formats supported by the audio element, from http://www.w3.org/TR/webaudio/#AudioContext-section,
146		// therefore tag is still required for the capabilities check
147		var t = document.createElement("audio");
148
149		if (t.canPlayType == null) {
150			return null;
151		}
152
153		// This check is first because it's what is currently used, but the spec calls for it to be AudioContext so this
154		//  will probably change in time
155		if (window.webkitAudioContext) {
156			s.context = new webkitAudioContext();
157		} else if (window.AudioContext) {
158			s.context = new AudioContext();
159		} else {
160			return null;
161		}
162
163		// this handles if only deprecated Web Audio API calls are supported
164		s._compatibilitySetUp();
165
166		// playing this inside of a touch event will enable audio on iOS, which starts muted
167		s.playEmptySound();
168
169		s._capabilities = {
170			panning:true,
171			volume:true,
172			tracks:-1
173		};
174
175		// determine which extensions our browser supports for this plugin by iterating through Sound.SUPPORTED_EXTENSIONS
176		var supportedExtensions = createjs.Sound.SUPPORTED_EXTENSIONS;
177		var extensionMap = createjs.Sound.EXTENSION_MAP;
178		for (var i = 0, l = supportedExtensions.length; i < l; i++) {
179			var ext = supportedExtensions[i];
180			var playType = extensionMap[ext] || ext;
181			s._capabilities[ext] = (t.canPlayType("audio/" + ext) != "no" && t.canPlayType("audio/" + ext) != "") || (t.canPlayType("audio/" + playType) != "no" && t.canPlayType("audio/" + playType) != "");
182		}  // OJR another way to do this might be canPlayType:"m4a", codex: mp4
183
184		// 0=no output, 1=mono, 2=stereo, 4=surround, 6=5.1 surround.
185		// See http://www.w3.org/TR/webaudio/#AudioChannelSplitter for more details on channels.
186		if (s.context.destination.numberOfChannels < 2) {
187			s._capabilities.panning = false;
188		}
189
190		// set up AudioNodes that all of our source audio will connect to
191		s.dynamicsCompressorNode = s.context.createDynamicsCompressor();
192		s.dynamicsCompressorNode.connect(s.context.destination);
193		s.gainNode = s.context.createGain();
194		s.gainNode.connect(s.dynamicsCompressorNode);
195	};
196
197	/**
198	 * Set up compatibility if only deprecated web audio calls are supported.
199	 * See http://www.w3.org/TR/webaudio/#DeprecationNotes
200	 * Needed so we can support new browsers that don't support deprecated calls (Firefox) as well as old browsers that
201	 * don't support new calls.
202	 *
203	 * @method _compatibilitySetUp
204	 * @protected
205	 * @since 0.4.2
206	 */
207	s._compatibilitySetUp = function() {
208		//assume that if one new call is supported, they all are
209		if (s.context.createGain) { return; }
210
211		// simple name change, functionality the same
212		s.context.createGain = s.context.createGainNode;
213
214		// source node, add to prototype
215		var audioNode = s.context.createBufferSource();
216		audioNode.__proto__.start = audioNode.__proto__.noteGrainOn;	// note that noteGrainOn requires all 3 parameters
217		audioNode.__proto__.stop = audioNode.__proto__.noteOff;
218
219		// panningModel
220		this._panningModel = 0;
221	};
222
223	/**
224	 * Plays an empty sound in the web audio context.  This is used to enable web audio on iOS devices, as they
225	 * require the first sound to be played inside of a user initiated event (touch/click).  This is called when
226	 * {{#crossLink "WebAudioPlugin"}}{{/crossLink}} is initialized (by Sound {{#crossLink "Sound/initializeDefaultPlugins"}}{{/crossLink}}
227	 * for example).
228	 *
229	 * <h4>Example</h4>
230	 *
231	 *     function handleTouch(event) {
232	 *         createjs.WebAudioPlugin.playEmptySound();
233	 *     }
234	 *
235	 * @method playEmptySound
236	 * @since 0.4.1
237	 */
238	s.playEmptySound = function() {
239		// create empty buffer
240		var buffer = this.context.createBuffer(1, 1, 22050);
241		var source = this.context.createBufferSource();
242		source.buffer = buffer;
243
244		// connect to output (your speakers)
245		source.connect(this.context.destination);
246
247		// play the file
248		source.start(0, 0, 0);
249	};
250
251
252	var p = WebAudioPlugin.prototype;
253
254	p._capabilities = null; // doc'd above
255
256	/**
257	 * The internal master volume value of the plugin.
258	 * @property _volume
259	 * @type {Number}
260	 * @default 1
261	 * @protected
262	 */
263	// TODO refactor Sound.js so we can use getter setter for volume
264	p._volume = 1;
265
266	/**
267	 * The web audio context, which WebAudio uses to play audio. All nodes that interact with the WebAudioPlugin
268	 * need to be created within this context.
269	 * @property context
270	 * @type {AudioContext}
271	 */
272	p.context = null;
273
274	/**
275	 * Value to set panning model to equal power for SoundInstance.  Can be "equalpower" or 0 depending on browser implementation.
276	 * @property _panningModel
277	 * @type {Number / String}
278	 * @protected
279	 */
280	p._panningModel = "equalpower";
281
282	/**
283	 * A DynamicsCompressorNode, which is used to improve sound quality and prevent audio distortion.
284	 * It is connected to <code>context.destination</code>.
285	 * @property dynamicsCompressorNode
286	 * @type {AudioNode}
287	 */
288	p.dynamicsCompressorNode = null;
289
290	/**
291	 * A GainNode for controlling master _volume. It is connected to {{#crossLink "WebAudioPlugin/dynamicsCompressorNode:property"}}{{/crossLink}}.
292	 * @property gainNode
293	 * @type {AudioGainNode}
294	 */
295	p.gainNode = null;
296
297	/**
298	 * An object hash used internally to store ArrayBuffers, indexed by the source URI used  to load it. This
299	 * prevents having to load and decode audio files more than once. If a load has been started on a file,
300	 * <code>arrayBuffers[src]</code> will be set to true. Once load is complete, it is set the the loaded
301	 * ArrayBuffer instance.
302	 * @property _arrayBuffers
303	 * @type {Object}
304	 * @protected
305	 */
306	p._arrayBuffers = null;
307
308	/**
309	 * An initialization function run by the constructor
310	 * @method _init
311	 * @protected
312	 */
313	p._init = function () {
314		this._capabilities = s._capabilities;
315		this._arrayBuffers = {};
316
317		this.context = s.context;
318		this.gainNode = s.gainNode;
319		this.dynamicsCompressorNode = s.dynamicsCompressorNode;
320	};
321
322	/**
323	 * Pre-register a sound for preloading and setup. This is called by {{#crossLink "Sound"}}{{/crossLink}}.
324	 * Note that WebAudio provides a <code>Loader</code> instance, which <a href="http://preloadjs.com" target="_blank">PreloadJS</a>
325	 * can use to assist with preloading.
326	 * @method register
327	 * @param {String} src The source of the audio
328	 * @param {Number} instances The number of concurrently playing instances to allow for the channel at any time.
329	 * Note that the WebAudioPlugin does not manage this property.
330	 * @return {Object} A result object, containing a "tag" for preloading purposes.
331	 */
332	p.register = function (src, instances) {
333		this._arrayBuffers[src] = true;  // This is needed for PreloadJS
334		var tag = new createjs.WebAudioPlugin.Loader(src, this);
335		return {
336			tag:tag
337		};
338	};
339
340	/**
341	 * Checks if preloading has started for a specific source. If the source is found, we can assume it is loading,
342	 * or has already finished loading.
343	 * @method isPreloadStarted
344	 * @param {String} src The sound URI to check.
345	 * @return {Boolean}
346	 */
347	p.isPreloadStarted = function (src) {
348		return (this._arrayBuffers[src] != null);
349	};
350
351	/**
352	 * Checks if preloading has finished for a specific source.
353	 * @method isPreloadComplete
354	 * @param {String} src The sound URI to load.
355	 * @return {Boolean}
356	 */
357	p.isPreloadComplete = function (src) {
358		return (!(this._arrayBuffers[src] == null || this._arrayBuffers[src] == true));
359	};
360
361	/**
362	 * Remove a sound added using {{#crossLink "WebAudioPlugin/register"}}{{/crossLink}}. Note this does not cancel a preload.
363	 * @method removeSound
364	 * @param {String} src The sound URI to unload.
365	 * @since 0.4.1
366	 */
367	p.removeSound = function (src) {
368		delete(this._arrayBuffers[src]);
369	};
370
371	/**
372	 * Remove all sounds added using {{#crossLink "WebAudioPlugin/register"}}{{/crossLink}}. Note this does not cancel a preload.
373	 * @method removeAllSounds
374	 * @param {String} src The sound URI to unload.
375	 * @since 0.4.1
376	 */
377	p.removeAllSounds = function () {
378		this._arrayBuffers = {};
379	};
380
381	/**
382	 * Add loaded results to the preload object hash.
383	 * @method addPreloadResults
384	 * @param {String} src The sound URI to unload.
385	 * @return {Boolean}
386	 */
387	p.addPreloadResults = function (src, result) {
388		this._arrayBuffers[src] = result;
389	};
390
391	/**
392	 * Handles internal preload completion.
393	 * @method _handlePreloadComplete
394	 * @protected
395	 */
396	p._handlePreloadComplete = function () {
397		//LM: I would recommend having the Loader include an "event" in the onload, and properly binding this callback.
398		createjs.Sound._sendFileLoadEvent(this.src);  // fire event or callback on Sound
399		// note "this" will reference Loader object
400	};
401
402	/**
403	 * Internally preload a sound. Loading uses XHR2 to load an array buffer for use with WebAudio.
404	 * @method preload
405	 * @param {String} src The sound URI to load.
406	 * @param {Object} instance Not used in this plugin.
407	 */
408	p.preload = function (src, instance) {
409		this._arrayBuffers[src] = true;
410		var loader = new createjs.WebAudioPlugin.Loader(src, this);
411		loader.onload = this._handlePreloadComplete;
412		loader.load();
413	};
414
415	/**
416	 * Create a sound instance. If the sound has not been preloaded, it is internally preloaded here.
417	 * @method create
418	 * @param {String} src The sound source to use.
419	 * @return {SoundInstance} A sound instance for playback and control.
420	 */
421	p.create = function (src) {
422		if (!this.isPreloadStarted(src)) {
423			this.preload(src);
424		}
425		return new createjs.WebAudioPlugin.SoundInstance(src, this);
426	};
427
428	/**
429	 * Set the master volume of the plugin, which affects all SoundInstances.
430	 * @method setVolume
431	 * @param {Number} value The volume to set, between 0 and 1.
432	 * @return {Boolean} If the plugin processes the setVolume call (true). The Sound class will affect all the
433	 * instances manually otherwise.
434	 */
435	p.setVolume = function (value) {
436		this._volume = value;
437		this._updateVolume();
438		return true;
439	};
440
441	/**
442	 * Set the gain value for master audio. Should not be called externally.
443	 * @method _updateVolume
444	 * @protected
445	 */
446	p._updateVolume = function () {
447		var newVolume = createjs.Sound._masterMute ? 0 : this._volume;
448		if (newVolume != this.gainNode.gain.value) {
449			this.gainNode.gain.value = newVolume;
450		}
451	};
452
453	/**
454	 * Get the master volume of the plugin, which affects all SoundInstances.
455	 * @method getVolume
456	 * @return The volume level, between 0 and 1.
457	 */
458	p.getVolume = function () {
459		return this._volume;
460	};
461
462	/**
463	 * Mute all sounds via the plugin.
464	 * @method setMute
465	 * @param {Boolean} value If all sound should be muted or not. Note that plugin-level muting just looks up
466	 * the mute value of Sound {{#crossLink "Sound/getMute"}}{{/crossLink}}, so this property is not used here.
467	 * @return {Boolean} If the mute call succeeds.
468	 */
469	p.setMute = function (value) {
470		this._updateVolume();
471		return true;
472	};
473
474	p.toString = function () {
475		return "[WebAudioPlugin]";
476	};
477
478	createjs.WebAudioPlugin = WebAudioPlugin;
479}());
480
481(function () {
482
483	"use strict";
484
485	/**
486	 * A SoundInstance is created when any calls to the Sound API method {{#crossLink "Sound/play"}}{{/crossLink}} or
487	 * {{#crossLink "Sound/createInstance"}}{{/crossLink}} are made. The SoundInstance is returned by the active plugin
488	 * for control by the user.
489	 *
490	 * <h4>Example</h4>
491	 *
492	 *      var myInstance = createjs.Sound.play("myAssetPath/mySrcFile.mp3");
493	 *
494	 * A number of additional parameters provide a quick way to determine how a sound is played. Please see the Sound
495	 * API method {{#crossLink "Sound/play"}}{{/crossLink}} for a list of arguments.
496	 *
497	 * Once a SoundInstance is created, a reference can be stored that can be used to control the audio directly through
498	 * the SoundInstance. If the reference is not stored, the SoundInstance will play out its audio (and any loops), and
499	 * is then de-referenced from the {{#crossLink "Sound"}}{{/crossLink}} class so that it can be cleaned up. If audio
500	 * playback has completed, a simple call to the {{#crossLink "SoundInstance/play"}}{{/crossLink}} instance method
501	 * will rebuild the references the Sound class need to control it.
502	 *
503	 *      var myInstance = createjs.Sound.play("myAssetPath/mySrcFile.mp3", {loop:2});
504	 *      myInstance.addEventListener("loop", handleLoop);
505	 *      function handleLoop(event) {
506	 *          myInstance.volume = myInstance.volume * 0.5;
507	 *      }
508	 *
509	 * Events are dispatched from the instance to notify when the sound has completed, looped, or when playback fails
510	 *
511	 *      var myInstance = createjs.Sound.play("myAssetPath/mySrcFile.mp3");
512	 *      myInstance.addEventListener("complete", handleComplete);
513	 *      myInstance.addEventListener("loop", handleLoop);
514	 *      myInstance.addEventListener("failed", handleFailed);
515	 *
516	 *
517	 * @class SoundInstance
518	 * @param {String} src The path to and file name of the sound.
519	 * @param {Object} owner The plugin instance that created this SoundInstance.
520	 * @extends EventDispatcher
521	 * @constructor
522	 */
523	function SoundInstance(src, owner) {
524		this._init(src, owner);
525	}
526
527	var p = SoundInstance.prototype = new createjs.EventDispatcher();
528
529	/**
530	 * The source of the sound.
531	 * @property src
532	 * @type {String}
533	 * @default null
534	 */
535	p.src = null;
536
537	/**
538	 * The unique ID of the instance. This is set by {{#crossLink "Sound"}}{{/crossLink}}.
539	 * @property uniqueId
540	 * @type {String} | Number
541	 * @default -1
542	 */
543	p.uniqueId = -1;
544
545	/**
546	 * The play state of the sound. Play states are defined as constants on {{#crossLink "Sound"}}{{/crossLink}}.
547	 * @property playState
548	 * @type {String}
549	 * @default null
550	 */
551	p.playState = null;
552
553	/**
554	 * The plugin that created the instance
555	 * @property _owner
556	 * @type {WebAudioPlugin}
557	 * @default null
558	 * @protected
559	 */
560	p._owner = null;
561
562	/**
563	 * How far into the sound to begin playback in milliseconds. This is passed in when play is called and used by
564	 * pause and setPosition to track where the sound should be at.
565	 * Note this is converted from milliseconds to seconds for consistency with the WebAudio API.
566	 * @property _offset
567	 * @type {Number}
568	 * @default 0
569	 * @protected
570	 */
571	p._offset = 0;
572
573	/**
574	 * The time in milliseconds before the sound starts.
575	 * Note this is handled by {{#crossLink "Sound"}}{{/crossLink}}.
576	 * @property _delay
577	 * @type {Number}
578	 * @default 0
579	 * @protected
580	 */
581	p._delay = 0;	// OJR remove this property from SoundInstance as it is not used here?
582
583	/**
584	 * The volume of the sound, between 0 and 1.
585	 * <br />Note this uses a getter setter, which is not supported by Firefox versions 3.6 or lower and Opera versions 11.50 or lower,
586	 * and Internet Explorer 8 or lower.  Instead use {{#crossLink "SoundInstance/setVolume"}}{{/crossLink}} and {{#crossLink "SoundInstance/getVolume"}}{{/crossLink}}.
587	 *
588	 * The actual output volume of a sound can be calculated using:
589	 * <code>myInstance.volume * createjs.Sound.getVolume();</code>
590	 *
591	 * @property volume
592	 * @type {Number}
593	 * @default 1
594	 */
595	p._volume =  1;
596	// IE8 has Object.defineProperty, but only for DOM objects, so check if fails to suppress errors
597	try {
598		Object.defineProperty(p, "volume", {
599		get: function() {
600			return this._volume;
601		},
602		set: function(value) {
603			if (Number(value) == null) {return false}
604			value = Math.max(0, Math.min(1, value));
605			this._volume = value;
606			this._updateVolume();
607		}
608		});
609	} catch (e) {
610		// dispatch message or error?
611	};
612
613	/**
614	 * The pan of the sound, between -1 (left) and 1 (right). Note that pan is not supported by HTML Audio.
615	 *
616	 * <br />Note this uses a getter setter, which is not supported by Firefox versions 3.6 or lower, Opera versions 11.50 or lower,
617	 * and Internet Explorer 8 or lower.  Instead use {{#crossLink "SoundInstance/setPan"}}{{/crossLink}} and {{#crossLink "SoundInstance/getPan"}}{{/crossLink}}.
618	 * <br />Note in WebAudioPlugin this only gives us the "x" value of what is actually 3D audio.
619	 *
620	 * @property pan
621	 * @type {Number}
622	 * @default 0
623	 */
624	p._pan =  0;
625	// IE8 has Object.defineProperty, but only for DOM objects, so check if fails to suppress errors
626	try {
627		Object.defineProperty(p, "pan", {
628			get: function() {
629				return this._pan;
630			},
631			set: function(value) {
632				if (!this._owner._capabilities.panning || Number(value) == null) {return false;}
633
634				value = Math.max(-1, Math.min(1, value));	// force pan to stay in the -1 to 1 range
635				// Note that panning in WebAudioPlugin can support 3D audio, but our implementation does not.
636				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
637				this.panNode.setPosition(value, 0, -0.5);  // z need to be -0.5 otherwise the sound only plays in left, right, or center
638			}
639		});
640	} catch (e) {
641		// dispatch message or error?
642	};
643
644
645/**
646	 * The length of the audio clip, in milliseconds.
647	 * Use {{#crossLink "SoundInstance/getDuration:method"}}{{/crossLink}} to access.
648	 * @property _duration
649	 * @type {Number}
650	 * @default 0
651	 * @protected
652	 */
653	p._duration = 0;
654
655	/**
656	 * The number of play loops remaining. Negative values will loop infinitely.
657	 * @property _remainingLoops
658	 * @type {Number}
659	 * @default 0
660	 * @protected
661	 */
662	p._remainingLoops = 0;
663
664	/**
665	 * A Timeout created by {{#crossLink "Sound"}}{{/crossLink}} when this SoundInstance is played with a delay.
666	 * This allows SoundInstance to remove the delay if stop or pause or cleanup are called before playback begins.
667	 * @property _delayTimeoutId
668	 * @type {timeoutVariable}
669	 * @default null
670	 * @protected
671	 * @since 0.4.0
672	 */
673	p._delayTimeoutId = null;
674
675	/**
676	 * Timeout that is created internally to handle sound playing to completion. Stored so we can remove it when
677	 * stop, pause, or cleanup are called
678	 * @property _soundCompleteTimeout
679	 * @type {timeoutVariable}
680	 * @default null
681	 * @protected
682	 * @since 0.4.0
683	 */
684	p._soundCompleteTimeout = null;
685
686	/**
687	 * NOTE this only exists as a {{#crossLink "WebAudioPlugin"}}{{/crossLink}} property and is only intended for use by advanced users.
688	 * <br />GainNode for controlling <code>SoundInstance</code> volume. Connected to the WebAudioPlugin {{#crossLink "WebAudioPlugin/gainNode:property"}}{{/crossLink}}
689	 * that sequences to <code>context.destination</code>.
690	 * @property gainNode
691	 * @type {AudioGainNode}
692	 * @since 0.4.0
693	 *
694	 */
695	p.gainNode = null;
696
697	/**
698	 * NOTE this only exists as a {{#crossLink "WebAudioPlugin"}}{{/crossLink}} property and is only intended for use by advanced users.
699	 * <br />A panNode allowing left and right audio channel panning only. Connected to SoundInstance {{#crossLink "SoundInstance/gainNode:property"}}{{/crossLink}}.
700	 * @property panNode
701	 * @type {AudioPannerNode}
702	 * @since 0.4.0
703	 */
704	p.panNode = null;
705
706	/**
707	 * NOTE this only exists as a {{#crossLink "WebAudioPlugin"}}{{/crossLink}} property and is only intended for use by advanced users.
708	 * <br />sourceNode is the audio source. Connected to SoundInstance {{#crossLink "SoundInstance/panNode:property"}}{{/crossLink}}.
709	 * @property sourceNode
710	 * @type {AudioNode}
711	 * @since 0.4.0
712	 *
713	 */
714	p.sourceNode = null;
715
716	/**
717	 * NOTE this only exists as a {{#crossLink "WebAudioPlugin"}}{{/crossLink}} property and is only intended for use by advanced users.
718	 * _sourceNodeNext is the audio source for the next loop, inserted in a look ahead approach to allow for smooth
719	 * looping. Connected to {{#crossLink "SoundInstance/gainNode:property"}}{{/crossLink}}.
720	 * @property _sourceNodeNext
721	 * @type {AudioNode}
722	 * @default null
723	 * @protected
724	 * @since 0.4.1
725	 *
726	 */
727	p._sourceNodeNext = null;
728
729	/**
730	 * Determines if the audio is currently muted.
731	 * Use {{#crossLink "SoundInstance/getMute:method"}}{{/crossLink}} and {{#crossLink "SoundInstance/setMute:method"}}{{/crossLink}} to access.
732	 * @property _muted
733	 * @type {Boolean}
734	 * @default false
735	 * @protected
736	 */
737	p._muted = false;
738
739	/**
740	 * Determines if the audio is currently paused.
741	 * Use {{#crossLink "SoundInstance/pause:method"}}{{/crossLink}} and {{#crossLink "SoundInstance/resume:method"}}{{/crossLink}} to set.
742	 * @property _paused
743	 * @type {Boolean}
744	 * @default false
745	 * @protected
746	 */
747	p._paused = false;
748
749	/**
750	 * WebAudioPlugin only.
751	 * Time audio started playback, in seconds. Used to handle set position, get position, and resuming from paused.
752	 * @property _startTime
753	 * @type {Number}
754	 * @default 0
755	 * @protected
756	 * @since 0.4.0
757	 */
758	p._startTime = 0;
759
760	// Proxies, make removing listeners easier.
761	p._endedHandler = null;
762
763// Events
764	/**
765	 * The event that is fired when playback has started successfully.
766	 * @event succeeded
767	 * @param {Object} target The object that dispatched the event.
768	 * @param {String} type The event type.
769	 * @since 0.4.0
770	 */
771
772	/**
773	 * The event that is fired when playback is interrupted. This happens when another sound with the same
774	 * src property is played using an interrupt value that causes this instance to stop playing.
775	 * @event interrupted
776	 * @param {Object} target The object that dispatched the event.
777	 * @param {String} type The event type.
778	 * @since 0.4.0
779	 */
780
781	/**
782	 * The event that is fired when playback has failed. This happens when there are too many channels with the same
783	 * src property already playing (and the interrupt value doesn't cause an interrupt of another instance), or
784	 * the sound could not be played, perhaps due to a 404 error.
785	 * @event failed
786	 * @param {Object} target The object that dispatched the event.
787	 * @param {String} type The event type.
788	 * @since 0.4.0
789	 */
790
791	/**
792	 * The event that is fired when a sound has completed playing but has loops remaining.
793	 * @event loop
794	 * @param {Object} target The object that dispatched the event.
795	 * @param {String} type The event type.
796	 * @since 0.4.0
797	 */
798
799	/**
800	 * The event that is fired when playback completes. This means that the sound has finished playing in its
801	 * entirety, including its loop iterations.
802	 * @event complete
803	 * @param {Object} target The object that dispatched the event.
804	 * @param {String} type The event type.
805	 * @since 0.4.0
806	 */
807
808	//TODO: Deprecated
809	/**
810	 * REMOVED. Use {{#crossLink "EventDispatcher/addEventListener"}}{{/crossLink}} and the {{#crossLink "SoundInstance/succeeded:event"}}{{/crossLink}}
811	 * event.
812	 * @property onPlaySucceeded
813	 * @type {Function}
814	 * @deprecated Use addEventListener and the "succeeded" event.
815	 */
816	/**
817	 * REMOVED. Use {{#crossLink "EventDispatcher/addEventListener"}}{{/crossLink}} and the {{#crossLink "SoundInstance/interrupted:event"}}{{/crossLink}}
818	 * event.
819	 * @property onPlayInterrupted
820	 * @type {Function}
821	 * @deprecated Use addEventListener and the "interrupted" event.
822	 */
823	/**
824	 * REMOVED. Use {{#crossLink "EventDispatcher/addEventListener"}}{{/crossLink}} and the {{#crossLink "SoundInstance/failed:event"}}{{/crossLink}}
825	 * event.
826	 * @property onPlayFailed
827	 * @type {Function}
828	 * @deprecated Use addEventListener and the "failed" event.
829	 */
830	/**
831	 * REMOVED. Use {{#crossLink "EventDispatcher/addEventListener"}}{{/crossLink}} and the {{#crossLink "SoundInstance/complete:event"}}{{/crossLink}}
832	 * event.
833	 * @property onComplete
834	 * @type {Function}
835	 * @deprecated Use addEventListener and the "complete" event.
836	 */
837	/**
838	 * REMOVED. Use {{#crossLink "EventDispatcher/addEventListener"}}{{/crossLink}} and the {{#crossLink "SoundInstance/loop:event"}}{{/crossLink}}
839	 * event.
840	 * @property onLoop
841	 * @type {Function}
842	 * @deprecated Use addEventListener and the "loop" event.
843	 */
844
845	/**
846	 * A helper method that dispatches all events for SoundInstance.
847	 * @method _sendEvent
848	 * @param {String} type The event type
849	 * @protected
850	 */
851	p._sendEvent = function (type) {
852		var event = new createjs.Event(type);
853		this.dispatchEvent(event);
854	};
855
856// Constructor
857	/**
858	 * Initialize the SoundInstance. This is called from the constructor.
859	 * @method _init
860	 * @param {string} src The source of the audio.
861	 * @param {Class} owner The plugin that created this instance.
862	 * @protected
863	 */
864	p._init = function (src, owner) {
865		this._owner = owner;
866		this.src = src;
867
868		this.gainNode = this._owner.context.createGain();
869
870		this.panNode = this._owner.context.createPanner();  //TODO test how this affects when we have mono audio
871		this.panNode.panningModel = this._owner._panningModel;
872		this.panNode.connect(this.gainNode);
873
874		if (this._owner.isPreloadComplete(this.src)) {
875			this._duration = this._owner._arrayBuffers[this.src].duration * 1000;
876		}
877
878		this._endedHandler = createjs.proxy(this._handleSoundComplete, this);
879	};
880
881	/**
882	 * Clean up the instance. Remove references and clean up any additional properties such as timers.
883	 * @method _cleanUp
884	 * @protected
885	 */
886	p._cleanUp = function () {
887		if (this.sourceNode && this.playState == createjs.Sound.PLAY_SUCCEEDED) {
888			this.sourceNode = this._cleanUpAudioNode(this.sourceNode);
889			this._sourceNodeNext = this._cleanUpAudioNode(this._sourceNodeNext);
890		}
891
892		if (this.gainNode.numberOfOutputs != 0) {
893			this.gainNode.disconnect(0);
894		}  // this works because we only have one connection, and it returns 0 if we've already disconnected it.
895		// 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.
896
897		clearTimeout(this._delayTimeoutId); // clear timeout that plays delayed sound
898		clearTimeout(this._soundCompleteTimeout);  // clear timeout that triggers sound complete
899
900		this._startTime = 0;	// This is used by getPosition
901
902		if (window.createjs == null) {
903			return;
904		}
905		createjs.Sound._playFinished(this);
906	};
907
908	/**
909	 * Turn off and disconnect an audioNode, then set reference to null to release it for garbage collection
910	 * @method _cleanUpAudioNode
911	 * @param audioNode
912	 * @return {audioNode}
913	 * @protected
914	 * @since 0.4.1
915	 */
916	p._cleanUpAudioNode = function(audioNode) {
917		if(audioNode) {
918			audioNode.stop(0);
919			audioNode.disconnect(this.panNode);
920			audioNode = null;	// release reference so Web Audio can handle removing references and garbage collection
921		}
922		return audioNode;
923	};
924
925	/**
926	 * The sound has been interrupted.
927	 * @method _interrupt
928	 * @protected
929	 */
930	p._interrupt = function () {
931		this._cleanUp();
932		this.playState = createjs.Sound.PLAY_INTERRUPTED;
933		this._paused = false;
934		this._sendEvent("interrupted");
935	};
936
937	/**
938	 * Handles starting playback when the sound is ready for playing.
939	 * @method _handleSoundReady
940	 * @protected
941 	 */
942	p._handleSoundReady = function (event) {
943		if (window.createjs == null) {
944			return;
945		}
946
947		if ((this._offset*1000) > this.getDuration()) {	// converting offset to ms
948			this.playFailed();
949			return;
950		} 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
951			this._offset = 0;
952		}
953
954		this.playState = createjs.Sound.PLAY_SUCCEEDED;
955		this._paused = false;
956
957		this.gainNode.connect(this._owner.gainNode);  // this line can cause a memory leak.  Nodes need to be disconnected from the audioDestination or any sequence that leads to it.
958
959		var dur = this._owner._arrayBuffers[this.src].duration;
960		this.sourceNode = this._createAndPlayAudioNode((this._owner.context.currentTime - dur), this._offset);
961		this._duration = dur * 1000;	// NOTE *1000 because WebAudio reports everything in seconds but js uses milliseconds
962		this._startTime = this.sourceNode.startTime - this._offset;
963
964		this._soundCompleteTimeout = setTimeout(this._endedHandler, (dur - this._offset) * 1000);
965
966		if(this._remainingLoops != 0) {
967			this._sourceNodeNext = this._createAndPlayAudioNode(this._startTime, 0);
968		}
969	};
970
971	/**
972	 * Creates an audio node using the current src and context, connects it to the gain node, and starts playback.
973	 * @method _createAndPlayAudioNode
974	 * @param {Number} startTime The time to add this to the web audio context, in seconds.
975	 * @param {Number} offset The amount of time into the src audio to start playback, in seconds.
976	 * @return {audioNode}
977	 * @protected
978	 * @since 0.4.1
979	 */
980	p._createAndPlayAudioNode = function(startTime, offset) {
981		var audioNode = this._owner.context.createBufferSource();
982		audioNode.buffer = this._owner._arrayBuffers[this.src];
983		audioNode.connect(this.panNode);
984		var currentTime = this._owner.context.currentTime;
985		audioNode.startTime = startTime + audioNode.buffer.duration;	//currentTime + audioNode.buffer.duration - (currentTime - startTime);
986		audioNode.start(audioNode.startTime, offset, audioNode.buffer.duration - offset);
987		return audioNode;
988	};
989
990	// Public API
991	/**
992	 * Play an instance. This method is intended to be called on SoundInstances that already exist (created
993	 * with the Sound API {{#crossLink "Sound/createInstance"}}{{/crossLink}} or {{#crossLink "Sound/play"}}{{/crossLink}}).
994	 *
995	 * <h4>Example</h4>
996	 *      var myInstance = createjs.Sound.createInstance(mySrc);
997	 *      myInstance.play({offset:1, loop:2, pan:0.5});	// options as object properties
998	 *      myInstance.play(createjs.Sound.INTERRUPT_ANY);	// options as parameters
999	 *
1000	 * @method play
1001	 * @param {String | Object} [interrupt="none"|options] How to interrupt any currently playing instances of audio with the same source,
1002	 * if the maximum number of instances of the sound are already playing. Values are defined as <code>INTERRUPT_TYPE</code>
1003	 * constants on the Sound class, with the default defined by Sound {{#crossLink "Sound/defaultInterruptBehavior:property"}}{{/crossLink}}.
1004	 * <br /><strong>OR</strong><br />
1005	 * This parameter can be an object that contains any or all optional properties by name, including: interrupt,
1006	 * delay, offset, loop, volume, and pan (see the above code sample).
1007	 * @param {Number} [delay=0] The delay in milliseconds before the sound starts
1008	 * @param {Number} [offset=0] How far into the sound to begin playback, in milliseconds.
1009	 * @param {Number} [loop=0] The number of times to loop the audio. Use -1 for infinite loops.
1010	 * @param {Number} [volume=1] The volume of the sound, between 0 and 1.
1011	 * @param {Number} [pan=0] The pan of the sound between -1 (left) and 1 (right). Note that pan is not supported
1012	 * for HTML Audio.
1013	 */
1014	p.play = function (interrupt, delay, offset, loop, volume, pan) {
1015		this._cleanUp();
1016		createjs.Sound._playInstance(this, interrupt, delay, offset, loop, volume, pan);
1017	};
1018
1019	/**
1020	 * Called by the Sound class when the audio is ready to play (delay has completed). Starts sound playing if the
1021	 * src is loaded, otherwise playback will fail.
1022	 * @method _beginPlaying
1023	 * @param {Number} offset How far into the sound to begin playback, in milliseconds.
1024	 * @param {Number} loop The number of times to loop the audio. Use -1 for infinite loops.
1025	 * @param {Number} volume The volume of the sound, between 0 and 1.
1026	 * @param {Number} pan The pan of the sound between -1 (left) and 1 (right). Note that pan does not work for HTML Audio.
1027	 * @protected
1028	 */
1029	p._beginPlaying = function (offset, loop, volume, pan) {
1030		if (window.createjs == null) {
1031			return;
1032		}
1033
1034		if (!this.src) {
1035			return;
1036		}
1037
1038		this._offset = offset / 1000;  //convert ms to sec
1039		this._remainingLoops = loop;
1040		this.volume = volume;
1041		this.pan = pan;
1042
1043		if (this._owner.isPreloadComplete(this.src)) {
1044			this._handleSoundReady(null);
1045			this._sendEvent("succeeded");
1046			return 1;
1047		} else {
1048			this.playFailed();
1049			return;
1050		}
1051	};
1052
1053	/**
1054	 * Pause the instance. Paused audio will stop at the current time, and can be resumed using
1055	 * {{#crossLink "SoundInstance/resume"}}{{/crossLink}}.
1056	 *
1057	 * <h4>Example</h4>
1058	 *
1059	 *      myInstance.pause();
1060	 *
1061	 * @method pause
1062	 * @return {Boolean} If the pause call succeeds. This will return false if the sound isn't currently playing.
1063	 */
1064	p.pause = function () {
1065		if (!this._paused && this.playState == createjs.Sound.PLAY_SUCCEEDED) {
1066			this._paused = true;
1067
1068			this._offset = this._owner.context.currentTime - this._startTime;  // this allows us to restart the sound at the same point in playback
1069			this._cleanUpAudioNode(this.sourceNode);
1070			this._cleanUpAudioNode(this._sourceNodeNext);
1071
1072			if (this.gainNode.numberOfOutputs != 0) {
1073				this.gainNode.disconnect();
1074			}  // this works because we only have one connection, and it returns 0 if we've already disconnected it.
1075
1076			clearTimeout(this._delayTimeoutId); // clear timeout that plays delayed sound
1077			clearTimeout(this._soundCompleteTimeout);  // clear timeout that triggers sound complete
1078			return true;
1079		}
1080		return false;
1081	};
1082
1083	/**
1084	 * Resume an instance that has been paused using {{#crossLink "SoundInstance/pause"}}{{/crossLink}}. Audio that
1085	 * has not been paused will not playback when this method is called.
1086	 *
1087	 * <h4>Example</h4>
1088	 *
1089	 *     myInstance.pause();
1090	 *     // do some stuff
1091	 *     myInstance.resume();
1092	 *
1093	 * @method resume
1094	 * @return {Boolean} If the resume call succeeds. This will return false if called on a sound that is not paused.
1095	 */
1096	p.resume = function () {
1097		if (!this._paused) {
1098			return false;
1099		}
1100		this._handleSoundReady(null);
1101		return true;
1102	};
1103
1104	/**
1105	 * Stop playback of the instance. Stopped sounds will reset their position to 0, and calls to {{#crossLink "SoundInstance/resume"}}{{/crossLink}}
1106	 * will fail.  To start playback again, call {{#crossLink "SoundInstance/play"}}{{/crossLink}}.
1107	 *
1108	 * <h4>Example</h4>
1109	 *
1110	 *     myInstance.stop();
1111	 *
1112	 * @method stop
1113	 * @return {Boolean} If the stop call succeeds.
1114	 */
1115	p.stop = function () {
1116		this._cleanUp();
1117		this.playState = createjs.Sound.PLAY_FINISHED;
1118		this._offset = 0;  // set audio to start at the beginning
1119		return true;
1120	};
1121
1122	/**
1123	 * NOTE that you can set volume directly as a property, and setVolume remains to allow support for IE8 with FlashPlugin.
1124	 * Set the volume of the instance. You can retrieve the volume using {{#crossLink "SoundInstance/getVolume"}}{{/crossLink}}.
1125	 *
1126	 * <h4>Example</h4>
1127	 *
1128	 *      myInstance.setVolume(0.5);
1129	 *
1130	 * Note that the master volume set using the Sound API method {{#crossLink "Sound/setVolume"}}{{/crossLink}}
1131	 * will be applied to the instance volume.
1132	 *
1133	 * @method setVolume
1134	 * @param value The volume to set, between 0 and 1.
1135	 * @return {Boolean} If the setVolume call succeeds.
1136	 */
1137	p.setVolume = function (value) {
1138		this.volume = value;
1139		return true;  // This is always true because even if the volume is not updated, the value is set
1140	};
1141
1142	/**
1143	 * Internal function used to update the volume based on the instance volume, master volume, instance mute value,
1144	 * and master mute value.
1145	 * @method _updateVolume
1146	 * @return {Boolean} if the volume was updated.
1147	 * @protected
1148	 */
1149	p._updateVolume = function () {
1150		var newVolume = this._muted ? 0 : this._volume;
1151		if (newVolume != this.gainNode.gain.value) {
1152			this.gainNode.gain.value = newVolume;
1153			return true;
1154		}
1155		return false;
1156	};
1157
1158	/**
1159	 * NOTE that you can access volume directly as a property, and getVolume remains to allow support for IE8 with FlashPlugin.
1160	 *
1161	 * Get the volume of the instance. The actual output volume of a sound can be calculated using:
1162	 * <code>myInstance.getVolume() * createjs.Sound.getVolume();</code>
1163	 *
1164	 * @method getVolume
1165	 * @return The current volume of the sound instance.
1166	 */
1167	p.getVolume = function () {
1168		return this.volume;
1169	};
1170
1171	/**
1172	 * Mute and unmute the sound. Muted sounds will still play at 0 volume. Note that an unmuted sound may still be
1173	 * silent depending on {{#crossLink "Sound"}}{{/crossLink}} volume, instance volume, and Sound mute.
1174	 *
1175	 * <h4>Example</h4>
1176	 *
1177	 *     myInstance.setMute(true);
1178	 *
1179	 * @method setMute
1180	 * @param {Boolean} value If the sound should be muted.
1181	 * @return {Boolean} If the mute call succeeds.
1182	 * @since 0.4.0
1183	 */
1184	p.setMute = function (value) {
1185		if (value == null || value == undefined) {
1186			return false;
1187		}
1188
1189		this._muted = value;
1190		this._updateVolume();
1191		return true;
1192	};
1193
1194	/**
1195	 * Get the mute value of the instance.
1196	 *
1197	 * <h4>Example</h4>
1198	 *
1199	 *      var isMuted = myInstance.getMute();
1200	 *
1201	 * @method getMute
1202	 * @return {Boolean} If the sound is muted.
1203	 * @since 0.4.0
1204	 */
1205	p.getMute = function () {
1206		return this._muted;
1207	};
1208
1209	/**
1210	 * NOTE that you can set pan directly as a property, and getPan remains to allow support for IE8 with FlashPlugin.
1211	 *
1212	 * Set the left(-1)/right(+1) pan of the instance. Note that {{#crossLink "HTMLAudioPlugin"}}{{/crossLink}} does not
1213	 * support panning, and only simple left/right panning has been implemented for {{#crossLink "WebAudioPlugin"}}{{/crossLink}}.
1214	 * The default pan value is 0 (center).
1215	 *
1216	 * <h4>Example</h4>
1217	 *
1218	 *     myInstance.setPan(-1);  // to the left!
1219	 *
1220	 * @method setPan
1221	 * @param {Number} value The pan value, between -1 (left) and 1 (right).
1222	 * @return {Number} If the setPan call succeeds.
1223	 */
1224	p.setPan = function (value) {
1225		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
1226		if(this.pan != value) {return false;}
1227	};
1228
1229	/**
1230	 * NOTE that you can access pan directly as a property, and getPan remains to allow supp
1230ort for IE8 with FlashPlugin.
1231	 *
1232	 * Get the left/right pan of the instance. Note in WebAudioPlugin this only gives us the "x" value of what is
1233	 * actually 3D audio.
1234	 *
1235	 * <h4>Example</h4>
1236	 *
1237	 *     var myPan = myInstance.getPan();
1238	 *
1239	 * @method getPan
1240	 * @return {Number} The value of the pan, between -1 (left) and 1 (right).
1241	 */
1242	p.getPan = function () {
1243		return this.pan;
1244	};
1245
1246	/**
1247	 * Get the position of the playhead of the instance in milliseconds.
1248	 *
1249	 * <h4>Example</h4>
1250	 *
1251	 *     var currentOffset = myInstance.getPosition();
1252	 *
1253	 * @method getPosition
1254	 * @return {Number} The position of the playhead in the sound, in milliseconds.
1255	 */
1256	p.getPosition = function () {
1257		if (this._paused || this.sourceNode == null) {
1258			var pos = this._offset;
1259		} else {
1260			var pos = this._owner.context.currentTime - this._startTime;
1261		}
1262
1263		return pos * 1000; // pos in seconds * 1000 to give milliseconds
1264	};
1265
1266	/**
1267	 * Set the position of the playhead in the instance. This can be set while a sound is playing, paused, or
1268	 * stopped.
1269	 *
1270	 * <h4>Example</h4>
1271	 *
1272	 *      myInstance.setPosition(myInstance.getDuration()/2); // set audio to its halfway point.
1273	 *
1274	 * @method setPosition
1275	 * @param {Number} value The position to place the playhead, in milliseconds.
1276	 */
1277	p.setPosition = function (value) {
1278		this._offset = value / 1000; // convert milliseconds to seconds
1279
1280		if (this.sourceNode && this.playState == createjs.Sound.PLAY_SUCCEEDED) {
1281			// we need to stop this sound from continuing to play, as we need to recreate the sourceNode to change position
1282			this._cleanUpAudioNode(this.sourceNode);
1283			this._cleanUpAudioNode(this._sourceNodeNext);
1284			clearTimeout(this._soundCompleteTimeout);  // clear timeout that triggers sound complete
1285		}  // NOTE we cannot just call cleanup because it also calls the Sound function _playFinished which releases this instance in SoundChannel
1286
1287		if (!this._paused && this.playState == createjs.Sound.PLAY_SUCCEEDED) {
1288			this._handleSoundReady(null);
1289		}
1290
1291		return true;
1292	};
1293
1294	/**
1295	 * Get the duration of the instance, in milliseconds. Note in most cases, you need to play a sound using
1296	 * {{#crossLink "SoundInstance/play"}}{{/crossLink}} or the Sound API {{#crossLink "Sound/play"}}{{/crossLink}}
1297	 * method before its duration can be reported accurately.
1298	 *
1299	 * <h4>Example</h4>
1300	 *
1301	 *     var soundDur = myInstance.getDuration();
1302	 *
1303	 * @method getDuration
1304	 * @return {Number} The duration of the sound instance in milliseconds.
1305	 */
1306	p.getDuration = function () {
1307		return this._duration;
1308	};
1309
1310	/**
1311	 * Audio has finished playing. Manually loop it if required.
1312	 * @method _handleSoundComplete
1313	 * @param event
1314	 * @protected
1315	 */
1316	 // called internally by _soundCompleteTimeout in WebAudioPlugin
1317	p._handleSoundComplete = function (event) {
1318		this._offset = 0;  // have to set this as it can be set by pause during playback
1319
1320		if (this._remainingLoops != 0) {
1321			this._remainingLoops--;  // NOTE this introduces a theoretical limit on loops = float max size x 2 - 1
1322
1323			// OJR we are using a look ahead approach to ensure smooth looping.  We add _sourceNodeNext to the audio
1324			// context so that it starts playing even if this callback is delayed.  This technique and the reasons for
1325			// using it are described in greater detail here:  http://www.html5rocks.com/en/tutorials/audio/scheduling/
1326			// NOTE the cost of this is that our audio loop may not always match the loop event timing precisely.
1327			if(this._sourceNodeNext) { // this can be set to null, but this should not happen when looping
1328				this._cleanUpAudioNode(this.sourceNode);
1329				this.sourceNode = this._sourceNodeNext;
1330				this._startTime = this.sourceNode.startTime;
1331				this._sourceNodeNext = this._createAndPlayAudioNode(this._startTime, 0);
1332				this._soundCompleteTimeout = setTimeout(this._endedHandler, this._duration);
1333			}
1334			else {
1335				this._handleSoundReady(null);
1336			}
1337
1338			this._sendEvent("loop");
1339			return;
1340		}
1341
1342		if (window.createjs == null) {
1343			return;
1344		}
1345		this._cleanUp();
1346		this.playState = createjs.Sound.PLAY_FINISHED;
1347		this._sendEvent("complete");
1348	};
1349
1350	// Play has failed, which can happen for a variety of reasons.
1351	p.playFailed = function () {
1352		if (window.createjs == null) {
1353			return;
1354		}
1355		this._cleanUp();
1356		this.playState = createjs.Sound.PLAY_FAILED;
1357		this._sendEvent("failed");
1358	};
1359
1360	p.toString = function () {
1361		return "[WebAudioPlugin SoundInstance]";
1362	};
1363
1364	createjs.WebAudioPlugin.SoundInstance = SoundInstance;
1365}());
1366
1367(function () {
1368
1369	"use strict";
1370
1371	/**
1372	 * An internal helper class that preloads web audio via XHR. Note that this class and its methods are not documented
1373	 * properly to avoid generating HTML documentation.
1374	 * #class Loader
1375	 * @param {String} src The source of the sound to load.
1376	 * @param {Object} owner A reference to the class that created this instance.
1377	 * @constructor
1378	 */
1379	function Loader(src, owner) {
1380		this._init(src, owner);
1381	}
1382
1383	var p = Loader.prototype;
1384
1385	// the request object for or XHR2 request
1386	p.request = null;
1387
1388	p.owner = null;
1389	p.progress = -1;
1390
1391	/**
1392	 * The source of the sound to load. Used by callback functions when we return this class.
1393	 * #property src
1394	 * @type {String}
1395	 */
1396	p.src = null;
1397
1398	/**
1399	 * The original source of the sound, before it is altered with a basePath.
1400	 * #property src
1401	 * @type {String}
1402	 */
1403	p.originalSrc = null;
1404
1405	/**
1406	 * The decoded AudioBuffer array that is returned when loading is complete.
1407	 * #property result
1408	 * @type {AudioBuffer}
1409	 * @protected
1410	 */
1411	p.result = null;
1412
1413	// Calbacks
1414	/**
1415	 * The callback that fires when the load completes. This follows HTML tag naming.
1416	 * #property onload
1417	 * @type {Method}
1418	 */
1419	p.onload = null;
1420
1421	/**
1422	 * The callback that fires as the load progresses. This follows HTML tag naming.
1423	 * #property onprogress
1424	 * @type {Method}
1425	 */
1426	p.onprogress = null;
1427
1428	/**
1429	 * The callback that fires if the load hits an error.
1430	 * #property onError
1431	 * @type {Method}
1432	 * @protected
1433	 */
1434	p.onError = null;
1435
1436	// constructor
1437	p._init = function (src, owner) {
1438		this.src = src;
1439		this.originalSrc = src;
1440		this.owner = owner;
1441	};
1442
1443	/**
1444	 * Begin loading the content.
1445	 * #method load
1446	 * @param {String} src The path to the sound.
1447	 */
1448	p.load = function (src) {
1449		if (src != null) {
1450			// TODO does this need to set this.originalSrc
1451			this.src = src;
1452		}
1453
1454		this.request = new XMLHttpRequest();
1455		this.request.open("GET", this.src, true);
1456		this.request.responseType = "arraybuffer";
1457		this.request.onload = createjs.proxy(this.handleLoad, this);
1458		this.request.onError = createjs.proxy(this.handleError, this);
1459		this.request.onprogress = createjs.proxy(this.handleProgress, this);
1460
1461		this.request.send();
1462	};
1463
1464	/**
1465	 * The loader has reported progress.
1466	 *
1467	 * <strong>Note</strong>: this is not a public API, but is used to allow preloaders to subscribe to load
1468	 * progress as if this is an HTML audio tag. This reason is why this still uses a callback instead of an event.
1469	 * #method handleProgress
1470	 * @param {Number} loaded The loaded amount.
1471	 * @param {Number} total The total amount.
1472	 * @protected
1473	 */
1474	p.handleProgress = function (loaded, total) {
1475		this.progress = loaded / total;
1476		this.onprogress != null && this.onprogress({loaded:loaded, total:total, progress:this.progress});
1477	};
1478
1479	/**
1480	 * The sound has completed loading.
1481	 * #method handleLoad
1482	 * @protected
1483	 */
1484	p.handleLoad = function () {
1485		this.owner.context.decodeAudioData(this.request.response,
1486				createjs.proxy(this.handleAudioDecoded, this),
1487				createjs.proxy(this.handleError, this));
1488	};
1489
1490	/**
1491	 * The audio has been decoded.
1492	 * #method handleAudioDecoded
1493	 * @protected
1494	 */
1495	p.handleAudioDecoded = function (decodedAudio) {
1496		this.progress = 1;
1497		this.result = decodedAudio;
1498		this.src = this.originalSrc;
1499		this.owner.addPreloadResults(this.src, this.result);
1500		this.onload && this.onload();
1501	};
1502
1503	/**
1504	 * Errors have been caused by the loader.
1505	 * #method handleError
1506	 * @protected
1507	 */
1508	p.handleError = function (evt) {
1509		this.owner.removeSound(this.src);
1510		this.onerror && this.onerror(evt);
1511	};
1512
1513	p.toString = function () {
1514		return "[WebAudioPlugin Loader]";
1515	};
1516
1517	createjs.WebAudioPlugin.Loader = Loader;
1518
1519}());

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.