Mercurial > hg > Members > nobuyasu > presen
comparison OpenSourceConference/ui/audio_support/soundmanager2.js @ 0:7451f481250b
add OSC presen
author | Nobuyasu Oshiro <dimolto@cr.ie.u-ryukyu.ac.jp> |
---|---|
date | Wed, 07 Sep 2011 21:43:27 +0900 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:7451f481250b |
---|---|
1 /* | |
2 SoundManager 2: Javascript Sound for the Web. | |
3 -------------------------------------------------- | |
4 http://www.schillmania.com/projects/soundmanager2/ | |
5 | |
6 Copyright (c) 2007, Scott Schiller. All rights reserved. | |
7 Code licensed under the BSD License: | |
8 http://www.schillmania.com/projects/soundmanager2/license.txt | |
9 | |
10 Beta V2.0b.20070118 | |
11 */ | |
12 | |
13 function SoundManager(smURL,smID) { | |
14 var self = this; | |
15 this.enabled = false; | |
16 this.o = null; | |
17 this.url = (smURL||'ui/audio_support/soundmanager2.swf'); | |
18 this.id = (smID||'sm2movie'); | |
19 this.oMC = null; | |
20 this.sounds = []; | |
21 this.soundIDs = []; | |
22 this.allowPolling = true; // allow flash to poll for status update (required for "while playing", "progress" etc. to work.) | |
23 this.isIE = (navigator.userAgent.match(/MSIE/)); | |
24 this.isSafari = (navigator.userAgent.match(/safari/i)); | |
25 this._didAppend = false; | |
26 this._didInit = false; | |
27 this._disabled = false; | |
28 this.version = 'V2.0b.20070118'; | |
29 | |
30 this.defaultOptions = { | |
31 // ----------------- | |
32 'debugMode': false, // enable debug/status messaging, handy for development/troubleshooting - will be written to a DIV with an ID of "soundmanager-debug", you can use CSS to make it pretty | |
33 'autoLoad': false, // enable automatic loading (otherwise .load() will be called on demand with .play(), the latter being nicer on bandwidth - if you want to .load yourself, you also can) | |
34 'stream': true, // allows playing before entire file has loaded (recommended) | |
35 'autoPlay': false, // enable playing of file as soon as possible (much faster if "stream" is true) | |
36 'onid3': null, // callback function for "ID3 data is added/available" | |
37 'onload': null, // callback function for "load finished" | |
38 'whileloading': null, // callback function for "download progress update" (X of Y bytes received) | |
39 'onplay': null, // callback for "play" start | |
40 'whileplaying': null, // callback during play (position update) | |
41 'onstop': null, // callback for "user stop" | |
42 'onfinish': null, // callback function for "sound finished playing" | |
43 'onbeforefinish': null, // callback for "before sound finished playing (at [time])" | |
44 'onbeforefinishtime': 2000, // offset (milliseconds) before end of sound to trigger beforefinish (eg. 1000 msec = 1 second) | |
45 'onbeforefinishcomplete':null, // function to call when said sound finishes playing | |
46 'onjustbeforefinish':null, // callback for [n] msec before end of current sound | |
47 'onjustbeforefinishtime':200, // [n] - if not using, set to 0 (or null handler) and event will not fire. | |
48 'multiShot': true, // let sounds "restart" or layer on top of each other when played multiple times, rather than one-shot/one at a time | |
49 'pan': 0, // "pan" settings, left-to-right, -100 to 100 | |
50 'volume': 100, // self-explanatory. 0-100, the latter being the max. | |
51 // ----------------- | |
52 'foo': 'bar' // you don't need to change this one - it's actually an intentional filler. | |
53 } | |
54 | |
55 // --- public methods --- | |
56 | |
57 this.getMovie = function(smID) { | |
58 // return self.isIE?window[smID]:document[smID]; | |
59 return self.isIE?window[smID]:(self.isSafari?document[smID+'-embed']:document.getElementById(smID+'-embed')); | |
60 } | |
61 | |
62 this.loadFromXML = function(sXmlUrl) { | |
63 try { | |
64 self.o._loadFromXML(sXmlUrl); | |
65 } catch(e) { | |
66 self._failSafely(); | |
67 return true; | |
68 } | |
69 } | |
70 | |
71 this.createSound = function(oOptions) { | |
72 if (!self._didInit) throw new Error('soundManager.createSound(): Not loaded yet - wait for soundManager.onload() before calling sound-related methods'); | |
73 if (arguments.length==2) { | |
74 // function overloading in JS! :) ..assume simple createSound(id,url) use case | |
75 oOptions = {'id':arguments[0],'url':arguments[1]} | |
76 } | |
77 var thisOptions = self._mergeObjects(oOptions); | |
78 self._writeDebug('soundManager.createSound(): "<a href="#" onclick="soundManager.play(\''+thisOptions.id+'\');return false" title="play this sound">'+thisOptions.id+'</a>" ('+thisOptions.url+')'); | |
79 if (self._idCheck(thisOptions.id,true)) { | |
80 self._writeDebug('sound '+thisOptions.id+' already defined - exiting'); | |
81 return false; | |
82 } | |
83 self.sounds[thisOptions.id] = new SMSound(self,thisOptions); | |
84 self.soundIDs[self.soundIDs.length] = thisOptions.id; | |
85 try { | |
86 self.o._createSound(thisOptions.id,thisOptions.onjustbeforefinishtime); | |
87 } catch(e) { | |
88 self._failSafely(); | |
89 return true; | |
90 } | |
91 if (thisOptions.autoLoad || thisOptions.autoPlay) self.sounds[thisOptions.id].load(thisOptions); | |
92 if (thisOptions.autoPlay) self.sounds[thisOptions.id].playState = 1; // we can only assume this sound will be playing soon. | |
93 } | |
94 | |
95 this.load = function(sID,oOptions) { | |
96 if (!self._idCheck(sID)) return false; | |
97 self.sounds[sID].load(oOptions); | |
98 } | |
99 | |
100 this.unload = function(sID) { | |
101 if (!self._idCheck(sID)) return false; | |
102 self.sounds[sID].unload(); | |
103 } | |
104 | |
105 this.play = function(sID,oOptions) { | |
106 if (!self._idCheck(sID)) { | |
107 if (typeof oOptions != 'Object') oOptions = {url:oOptions}; // overloading use case: play('mySound','/path/to/some.mp3'); | |
108 if (oOptions && oOptions.url) { | |
109 // overloading use case, creation + playing of sound: .play('someID',{url:'/path/to.mp3'}); | |
110 self._writeDebug('soundController.play(): attempting to create "'+sID+'"'); | |
111 oOptions.id = sID; | |
112 self.createSound(oOptions); | |
113 } else { | |
114 return false; | |
115 } | |
116 } | |
117 self.sounds[sID].play(oOptions); | |
118 } | |
119 | |
120 this.start = this.play; // just for convenience | |
121 | |
122 this.setPosition = function(sID,nMsecOffset) { | |
123 if (!self._idCheck(sID)) return false; | |
124 self.sounds[sID].setPosition(nMsecOffset); | |
125 } | |
126 | |
127 this.stop = function(sID) { | |
128 if (!self._idCheck(sID)) return false; | |
129 self._writeDebug('soundManager.stop('+sID+')'); | |
130 self.sounds[sID].stop(); | |
131 } | |
132 | |
133 this.stopAll = function() { | |
134 for (var oSound in self.sounds) { | |
135 if (oSound instanceof SMSound) oSound.stop(); // apply only to sound objects | |
136 } | |
137 } | |
138 | |
139 this.pause = function(sID) { | |
140 if (!self._idCheck(sID)) return false; | |
141 self.sounds[sID].pause(); | |
142 } | |
143 | |
144 this.resume = function(sID) { | |
145 if (!self._idCheck(sID)) return false; | |
146 self.sounds[sID].resume(); | |
147 } | |
148 | |
149 this.togglePause = function(sID) { | |
150 if (!self._idCheck(sID)) return false; | |
151 self.sounds[sID].togglePause(); | |
152 } | |
153 | |
154 this.setPan = function(sID,nPan) { | |
155 if (!self._idCheck(sID)) return false; | |
156 self.sounds[sID].setPan(nPan); | |
157 } | |
158 | |
159 this.setVolume = function(sID,nVol) { | |
160 if (!self._idCheck(sID)) return false; | |
161 self.sounds[sID].setVolume(nVol); | |
162 } | |
163 | |
164 this.setPolling = function(bPolling) { | |
165 if (!self.o || !self.allowPolling) return false; | |
166 self._writeDebug('soundManager.setPolling('+bPolling+')'); | |
167 self.o._setPolling(bPolling); | |
168 } | |
169 | |
170 this.disable = function() { | |
171 // destroy all functions | |
172 if (self._disabled) return false; | |
173 self._disabled = true; | |
174 self._writeDebug('soundManager.disable(): Disabling all functions - future calls will return false.'); | |
175 for (var i=self.soundIDs.length; i--;) { | |
176 self._disableObject(self.sounds[self.soundIDs[i]]); | |
177 } | |
178 self.initComplete(); // fire "complete", despite fail | |
179 self._disableObject(self); | |
180 } | |
181 | |
182 this.getSoundById = function(sID,suppressDebug) { | |
183 if (!sID) throw new Error('SoundManager.getSoundById(): sID is null/undefined'); | |
184 var result = self.sounds[sID]; | |
185 if (!result && !suppressDebug) { | |
186 self._writeDebug('"'+sID+'" is an invalid sound ID.'); | |
187 // soundManager._writeDebug('trace: '+arguments.callee.caller); | |
188 } | |
189 return result; | |
190 } | |
191 | |
192 this.onload = function() { | |
193 onloadSM2(); | |
194 // window.onload() equivalent for SM2, ready to create sounds etc. | |
195 // this is a stub - you can override this in your own external script, eg. soundManager.onload = function() {} | |
196 // soundManager._writeDebug('<em>Warning</em>: soundManager.onload() is undefined.'); | |
197 } | |
198 | |
199 this.onerror = function() { | |
200 onerrorSM2(); | |
201 // stub for user handler, called when SM2 fails to load/init | |
202 } | |
203 | |
204 // --- "private" methods --- | |
205 | |
206 this._idCheck = this.getSoundById; | |
207 | |
208 this._disableObject = function(o) { | |
209 for (var oProp in o) { | |
210 if (typeof o[oProp] == 'function') o[oProp] = function(){return false;} | |
211 } | |
212 oProp = null; | |
213 } | |
214 | |
215 this._failSafely = function() { | |
216 // exception handler for "object doesn't support this property or method" | |
217 var flashCPLink = 'http://www.macromedia.com/support/documentation/en/flashplayer/help/settings_manager04.html'; | |
218 var fpgssTitle = 'You may need to whitelist this location/domain eg. file://C:/ or C:/ or mysite.com, or set ALWAYS ALLOW under the Flash Player Global Security Settings page. Note that this seems to apply only to file system viewing.'; | |
219 var flashCPL = '<a href="'+flashCPLink+'" title="'+fpgssTitle+'">view/edit</a>'; | |
220 var FPGSS = '<a href="'+flashCPLink+'" title="Flash Player Global Security Settings">FPGSS</a>'; | |
221 if (!self._disabled) { | |
222 self._writeDebug('soundManager: JS->Flash communication failed. Possible causes: flash/browser security restrictions ('+flashCPL+'), insufficient browser/plugin support, or .swf not found'); | |
223 self._writeDebug('Verify that the movie path of <em>'+self.url+'</em> is correct (<a href="'+self.url+'" title="If you get a 404/not found, fix it!">test link</a>)'); | |
224 if (self._didAppend) { | |
225 if (!document.domain) { | |
226 self._writeDebug('Loading from local file system? (document.domain appears to be null, this location may need whitelisting by '+FPGSS+')'); | |
227 self._writeDebug('Possible security/domain restrictions ('+flashCPL+'), should work when served by http on same domain'); | |
228 } | |
229 // self._writeDebug('Note: Movie added via JS method, static object/embed in-page markup method may work instead.'); | |
230 } | |
231 self.disable(); | |
232 } | |
233 } | |
234 | |
235 this._createMovie = function(smID,smURL) { | |
236 var useDOM = !self.isIE; // IE needs document.write() to work, long story short - lame | |
237 if (window.location.href.indexOf('debug=1')+1) self.defaultOptions.debugMode = true; // allow force of debug mode via URL | |
238 var html = ['<object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" codebase="http://fpdownload.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=8,0,0,0" width="16" height="16" id="'+smID+'"><param name="movie" value="'+smURL+'"><param name="quality" value="high"><param name="allowScriptAccess" value="always" /></object>','<embed name="'+smID+'-embed" id="'+smID+'-embed" src="'+smURL+'" width="1" height="1" quality="high" allowScriptAccess="always" pluginspage="http://www.macromedia.com/go/getflashplayer" type="application/x-shockwave-flash"></embed>']; | |
239 var debugID = 'soundmanager-debug'; | |
240 var debugHTML = '<div id="'+debugID+'" style="display:'+(self.defaultOptions.debugMode?'block':'none')+'"></div>'; | |
241 if (useDOM) { | |
242 self.oMC = document.createElement('div'); | |
243 self.oMC.className = 'movieContainer'; | |
244 // "hide" flash movie | |
245 self.oMC.style.position = 'absolute'; | |
246 self.oMC.style.left = '-256px'; | |
247 self.oMC.style.width = '1px'; | |
248 self.oMC.style.height = '1px'; | |
249 self.oMC.innerHTML = html[self.isIE?0:1]; | |
250 var oTarget = (!self.isIE && document.body?document.body:document.getElementsByTagName('div')[0]); | |
251 oTarget.appendChild(self.oMC); // append to body here can throw "operation aborted" under IE | |
252 if (!document.getElementById(debugID)) { | |
253 var oDebug = document.createElement('div'); | |
254 oDebug.id = debugID; | |
255 oDebug.style.display = (self.defaultOptions.debugMode?'block':'none'); | |
256 oTarget.appendChild(oDebug); | |
257 } | |
258 oTarget = null; | |
259 } else { | |
260 // IE method - local file system, may cause strange JS error at line 53? | |
261 // I hate this method, but it appears to be the only one that will work (createElement works, but JS->Flash communication oddly fails when viewing locally.) | |
262 // Finally, IE doesn't do application/xhtml+xml yet (thus document.write() is still minimally acceptable here.) | |
263 if (document.getElementById(debugID)) debugHTML = ''; // avoid overwriting | |
264 document.write('<div style="position:absolute;left:-256px;top:-256px;width:1px;height:1px" class="movieContainer">'+html[self.isIE?0:1]+'</div>'+debugHTML); | |
265 } | |
266 self._didAppend = true; | |
267 self._writeDebug('-- SoundManager 2 Version '+self.version.substr(1)+' --'); | |
268 self._writeDebug('soundManager._createMovie(): trying to load <a href="'+smURL+'" title="Test this link (404=bad)">'+smURL+'</a>'); | |
269 } | |
270 | |
271 this._writeDebug = function(sText) { | |
272 if (!self.defaultOptions.debugMode) return false; | |
273 var sDID = 'soundmanager-debug'; | |
274 try { | |
275 var o = document.getElementById(sDID); | |
276 if (!o) { | |
277 // attempt to create soundManager debug element | |
278 var oNew = document.createElement('div'); | |
279 oNew.id = sDID; | |
280 // o = document.body.appendChild(oNew); | |
281 o = null; | |
282 if (!o) return false; | |
283 } | |
284 var p = document.createElement('div'); | |
285 p.innerHTML = sText; | |
286 // o.appendChild(p); // top-to-bottom | |
287 o.insertBefore(p,o.firstChild); // bottom-to-top | |
288 } catch(e) { | |
289 // oh well | |
290 } | |
291 o = null; | |
292 } | |
293 | |
294 this._debug = function() { | |
295 self._writeDebug('soundManager._debug(): sounds by id/url:'); | |
296 for (var i=0,j=self.soundIDs.length; i<j; i++) { | |
297 self._writeDebug(self.sounds[self.soundIDs[i]].sID+' | '+self.sounds[self.soundIDs[i]].url); | |
298 } | |
299 } | |
300 | |
301 this._mergeObjects = function(oMain,oAdd) { | |
302 // non-destructive merge | |
303 var o1 = oMain; | |
304 var o2 = (typeof oAdd == 'undefined'?self.defaultOptions:oAdd); | |
305 for (var o in o2) { | |
306 if (typeof o1[o] == 'undefined') o1[o] = o2[o]; | |
307 } | |
308 return o1; | |
309 } | |
310 | |
311 this.createMovie = function(sURL) { | |
312 self._writeDebug('soundManager.createMovie('+(sURL||'')+')'); | |
313 if (sURL) self.url = sURL; | |
314 self._initMovie(); | |
315 } | |
316 | |
317 this._initMovie = function() { | |
318 // attempt to get, or create, movie | |
319 if (self.o) return false; // pre-init may have fired this function before window.onload(), may already exist | |
320 self.o = self.getMovie(self.id); // try to get flash movie (inline markup) | |
321 if (!self.o) { | |
322 // try to create | |
323 self._createMovie(self.id,self.url); | |
324 self.o = self.getMovie(self.id); | |
325 } | |
326 if (!self.o) { | |
327 self._writeDebug('SoundManager(): Could not find object/embed element. Sound will be disabled.'); | |
328 self.disable(); | |
329 } else { | |
330 self._writeDebug('SoundManager(): Got '+self.o.nodeName+' element ('+(self._didAppend?'created via JS':'static HTML')+')'); | |
331 } | |
332 } | |
333 | |
334 this.initComplete = function() { | |
335 if (self._didInit) return false; | |
336 self._didInit = true; | |
337 self._writeDebug('-- SoundManager 2 '+(self._disabled?'failed to load':'loaded')+' ('+(self._disabled?'security/load error':'OK')+') --'); | |
338 if (self._disabled) { | |
339 self._writeDebug('soundManager.initComplete(): calling soundManager.onerror()'); | |
340 self.onerror.apply(window); | |
341 return false; | |
342 } | |
343 self._writeDebug('soundManager.initComplete(): calling soundManager.onload()'); | |
344 try { | |
345 // call user-defined "onload", scoped to window | |
346 self.onload.apply(window); | |
347 } catch(e) { | |
348 // something broke (likely JS error in user function) | |
349 self._writeDebug('soundManager.onload() threw an exception: '+e.message); | |
350 throw e; // (so browser/console gets message) | |
351 } | |
352 self._writeDebug('soundManager.onload() complete'); | |
353 } | |
354 | |
355 this.init = function() { | |
356 // called after onload() | |
357 self._initMovie(); | |
358 // event cleanup | |
359 if (window.removeEventListener) { | |
360 window.removeEventListener('load',self.beginInit,false); | |
361 } else if (window.detachEvent) { | |
362 window.detachEvent('onload',self.beginInit); | |
363 } | |
364 try { | |
365 self.o._externalInterfaceTest(); // attempt to talk to Flash | |
366 self._writeDebug('Flash ExternalInterface call (JS -> Flash) succeeded.'); | |
367 if (!self.allowPolling) self._writeDebug('Polling (whileloading/whileplaying support) is disabled.'); | |
368 self.setPolling(true); | |
369 self.enabled = true; | |
370 } catch(e) { | |
371 self._failSafely(); | |
372 self.initComplete(); | |
373 return false; | |
374 } | |
375 self.initComplete(); | |
376 } | |
377 | |
378 this.beginInit = function() { | |
379 self._initMovie(); | |
380 setTimeout(self.init,1000); // some delay required, otherwise JS<->Flash/ExternalInterface communication fails under non-IE (?!) | |
381 } | |
382 | |
383 this.destruct = function() { | |
384 if (self.isSafari) { | |
385 /* -- | |
386 Safari 1.3.2 (v312.6)/OSX 10.3.9 and perhaps newer will crash if a sound is actively loading when user exits/refreshes/leaves page | |
387 (Apparently related to ExternalInterface making calls to an unloading/unloaded page?) | |
388 Unloading sounds (detaching handlers and so on) may help to prevent this | |
389 -- */ | |
390 for (var i=self.soundIDs.length; i--;) { | |
391 if (self.sounds[self.soundIDs[i]].readyState == 1) self.sounds[self.soundIDs[i]].unload(); | |
392 } | |
393 } | |
394 self.disable(); | |
395 // self.o = null; | |
396 // self.oMC = null; | |
397 } | |
398 | |
399 } | |
400 | |
401 function SMSound(oSM,oOptions) { | |
402 var self = this; | |
403 var sm = oSM; | |
404 this.sID = oOptions.id; | |
405 this.url = oOptions.url; | |
406 this.options = sm._mergeObjects(oOptions); | |
407 this.id3 = { | |
408 /* | |
409 Name/value pairs set via Flash when available - see reference for names: | |
410 http://livedocs.macromedia.com/flash/8/main/wwhelp/wwhimpl/common/html/wwhelp.htm?context=LiveDocs_Parts&file=00001567.html | |
411 (eg., this.id3.songname or this.id3['songname']) | |
412 */ | |
413 } | |
414 | |
415 self.resetProperties = function(bLoaded) { | |
416 self.bytesLoaded = null; | |
417 self.bytesTotal = null; | |
418 self.position = null; | |
419 self.duration = null; | |
420 self.durationEstimate = null; | |
421 self.loaded = false; | |
422 self.loadSuccess = null; | |
423 self.playState = 0; | |
424 self.paused = false; | |
425 self.readyState = 0; // 0 = uninitialised, 1 = loading, 2 = failed/error, 3 = loaded/success | |
426 self.didBeforeFinish = false; | |
427 self.didJustBeforeFinish = false; | |
428 } | |
429 | |
430 self.resetProperties(); | |
431 | |
432 // --- public methods --- | |
433 | |
434 this.load = function(oOptions) { | |
435 self.loaded = false; | |
436 self.loadSuccess = null; | |
437 self.readyState = 1; | |
438 self.playState = (oOptions.autoPlay||false); // if autoPlay, assume "playing" is true (no way to detect when it actually starts in Flash unless onPlay is watched?) | |
439 var thisOptions = sm._mergeObjects(oOptions); | |
440 if (typeof thisOptions.url == 'undefined') thisOptions.url = self.url; | |
441 try { | |
442 sm._writeDebug('loading '+thisOptions.url); | |
443 sm.o._load(self.sID,thisOptions.url,thisOptions.stream,thisOptions.autoPlay,thisOptions.whileloading?1:0); | |
444 } catch(e) { | |
445 sm._writeDebug('SMSound().load(): JS->Flash communication failed.'); | |
446 } | |
447 } | |
448 | |
449 this.unload = function() { | |
450 // Flash 8/AS2 can't "close" a stream - fake it by loading an empty MP3 | |
451 sm._writeDebug('SMSound().unload()'); | |
452 self.setPosition(0); // reset current sound positioning | |
453 sm.o._unload(self.sID,'ui/audio_support/null.mp3'); | |
454 // reset load/status flags | |
455 self.resetProperties(); | |
456 } | |
457 | |
458 this.play = function(oOptions) { | |
459 if (!oOptions) oOptions = {}; | |
460 | |
461 // --- TODO: make event handlers specified via oOptions only apply to this instance of play() (eg. onfinish applies but will reset to default on finish) | |
462 if (oOptions.onfinish) self.options.onfinish = oOptions.onfinish; | |
463 if (oOptions.onbeforefinish) self.options.onbeforefinish = oOptions.onbeforefinish; | |
464 if (oOptions.onjustbeforefinish) self.options.onjustbeforefinish = oOptions.onjustbeforefinish; | |
465 // --- | |
466 | |
467 var thisOptions = sm._mergeObjects(oOptions); | |
468 if (self.playState == 1) { | |
469 // var allowMulti = typeof oOptions.multiShot!='undefined'?oOptions.multiShot:sm.defaultOptions.multiShot; | |
470 var allowMulti = thisOptions.multiShot; | |
471 if (!allowMulti) { | |
472 sm._writeDebug('SMSound.play(): "'+self.sID+'" already playing? (one-shot)'); | |
473 return false; | |
474 } else { | |
475 sm._writeDebug('SMSound.play(): "'+self.sID+'" already playing (multi-shot)'); | |
476 } | |
477 } | |
478 if (!self.loaded) { | |
479 if (self.readyState == 0) { | |
480 sm._writeDebug('SMSound.play(): .play() before load request. Attempting to load "'+self.sID+'"'); | |
481 // try to get this sound playing ASAP | |
482 thisOptions.stream = true; | |
483 thisOptions.autoPlay = true; | |
484 // TODO: need to investigate when false, double-playing | |
485 // if (typeof oOptions.autoPlay=='undefined') thisOptions.autoPlay = true; // only set autoPlay if unspecified here | |
486 self.load(thisOptions); // try to get this sound playing ASAP | |
487 } else if (self.readyState == 2) { | |
488 sm._writeDebug('SMSound.play(): Could not load "'+self.sID+'" - exiting'); | |
489 return false; | |
490 } else { | |
491 sm._writeDebug('SMSound.play(): "'+self.sID+'" is loading - attempting to play..'); | |
492 } | |
493 } else { | |
494 sm._writeDebug('SMSound.play(): "'+self.sID+'"'); | |
495 } | |
496 if (self.paused) { | |
497 self.resume(); | |
498 } else { | |
499 self.playState = 1; | |
500 self.position = (thisOptions.offset||0); | |
501 if (thisOptions.onplay) thisOptions.onplay.apply(self); | |
502 self.setVolume(thisOptions.volume); | |
503 self.setPan(thisOptions.pan); | |
504 if (!thisOptions.autoPlay) { | |
505 sm._writeDebug('starting sound '+self.sID); | |
506 sm.o._start(self.sID,thisOptions.loop||1,self.position); // TODO: verify !autoPlay doesn't cause issue | |
507 } | |
508 } | |
509 } | |
510 | |
511 this.start = this.play; // just for convenience | |
512 | |
513 this.stop = function(bAll) { | |
514 if (self.playState == 1) { | |
515 self.playState = 0; | |
516 self.paused = false; | |
517 if (sm.defaultOptions.onstop) sm.defaultOptions.onstop.apply(self); | |
518 sm.o._stop(self.sID); | |
519 } | |
520 } | |
521 | |
522 this.setPosition = function(nMsecOffset) { | |
523 // sm._writeDebug('setPosition('+nMsecOffset+')'); | |
524 sm.o._setPosition(self.sID,nMsecOffset/1000,self.paused||!self.playState); // if paused or not playing, will not resume (by playing) | |
525 } | |
526 | |
527 this.pause = function() { | |
528 if (self.paused) return false; | |
529 sm._writeDebug('SMSound.pause()'); | |
530 self.paused = true; | |
531 sm.o._pause(self.sID); | |
532 } | |
533 | |
534 this.resume = function() { | |
535 if (!self.paused) return false; | |
536 sm._writeDebug('SMSound.resume()'); | |
537 self.paused = false; | |
538 sm.o._pause(self.sID); // flash method is toggle-based (pause/resume) | |
539 } | |
540 | |
541 this.togglePause = function() { | |
542 // if playing, pauses - if paused, resumes playing. | |
543 sm._writeDebug('SMSound.togglePause()'); | |
544 if (!self.playState) { | |
545 // self.setPosition(); | |
546 self.play({offset:self.position/1000}); | |
547 return false; | |
548 } | |
549 if (self.paused) { | |
550 sm._writeDebug('SMSound.togglePause(): resuming..'); | |
551 self.resume(); | |
552 } else { | |
553 sm._writeDebug('SMSound.togglePause(): pausing..'); | |
554 self.pause(); | |
555 } | |
556 } | |
557 | |
558 this.setPan = function(nPan) { | |
559 if (typeof nPan == 'undefined') nPan = 0; | |
560 sm.o._setPan(self.sID,nPan); | |
561 self.options.pan = nPan; | |
562 } | |
563 | |
564 this.setVolume = function(nVol) { | |
565 if (typeof nVol == 'undefined') nVol = 100; | |
566 sm.o._setVolume(self.sID,nVol); | |
567 self.options.volume = nVol; | |
568 } | |
569 | |
570 // --- "private" methods called by Flash --- | |
571 | |
572 this._whileloading = function(nBytesLoaded,nBytesTotal,nDuration) { | |
573 self.bytesLoaded = nBytesLoaded; | |
574 self.bytesTotal = nBytesTotal; | |
575 self.duration = nDuration; | |
576 self.durationEstimate = parseInt((self.bytesTotal/self.bytesLoaded)*self.duration); // estimate total time (will only be accurate with CBR MP3s.) | |
577 if (self.readyState != 3 && self.options.whileloading) self.options.whileloading.apply(self); | |
578 // soundManager._writeDebug('duration/durationEst: '+self.duration+' / '+self.durationEstimate); | |
579 } | |
580 | |
581 this._onid3 = function(oID3PropNames,oID3Data) { | |
582 // oID3PropNames: string array (names) | |
583 // ID3Data: string array (data) | |
584 sm._writeDebug('SMSound()._onid3(): "'+this.sID+'" ID3 data received.'); | |
585 var oData = []; | |
586 for (var i=0,j=oID3PropNames.length; i<j; i++) { | |
587 oData[oID3PropNames[i]] = oID3Data[i]; | |
588 // sm._writeDebug(oID3PropNames[i]+': '+oID3Data[i]); | |
589 } | |
590 self.id3 = sm._mergeObjects(self.id3,oData); | |
591 if (self.options.onid3) self.options.onid3.apply(self); | |
592 } | |
593 | |
594 this._whileplaying = function(nPosition) { | |
595 if (isNaN(nPosition) || nPosition == null) return false; // Flash may return NaN at times | |
596 self.position = nPosition; | |
597 if (self.playState == 1) { | |
598 if (self.options.whileplaying) self.options.whileplaying.apply(self); // flash may call after actual finish | |
599 if (self.loaded && self.options.onbeforefinish && self.options.onbeforefinishtime && !self.didBeforeFinish && self.duration-self.position <= self.options.onbeforefinishtime) { | |
600 sm._writeDebug('duration-position <= onbeforefinishtime: '+self.duration+' - '+self.position+' <= '+self.options.onbeforefinishtime+' ('+(self.duration-self.position)+')'); | |
601 self._onbeforefinish(); | |
602 } | |
603 } | |
604 } | |
605 | |
606 this._onload = function(bSuccess) { | |
607 bSuccess = (bSuccess==1?true:false); | |
608 sm._writeDebug('SMSound._onload(): "'+self.sID+'"'+(bSuccess?' loaded.':' failed to load (or loaded from cache - weird bug) - [<a href="'+self.url+'">test URL</a>]')); | |
609 self.loaded = bSuccess; | |
610 self.loadSuccess = bSuccess; | |
611 self.readyState = bSuccess?3:2; | |
612 if (self.options.onload) self.options.onload.apply(self); | |
613 } | |
614 | |
615 this._onbeforefinish = function() { | |
616 if (!self.didBeforeFinish) { | |
617 self.didBeforeFinish = true; | |
618 if (self.options.onbeforefinish) self.options.onbeforefinish.apply(self); | |
619 } | |
620 } | |
621 | |
622 this._onjustbeforefinish = function(msOffset) { | |
623 // msOffset: "end of sound" delay actual value (eg. 200 msec, value at event fire time was 187) | |
624 if (!self.didJustBeforeFinish) { | |
625 self.didJustBeforeFinish = true; | |
626 soundManager._writeDebug('SMSound._onjustbeforefinish()'); | |
627 if (self.options.onjustbeforefinish) self.options.onjustbeforefinish.apply(self);; | |
628 } | |
629 } | |
630 | |
631 this._onfinish = function() { | |
632 // sound has finished playing | |
633 sm._writeDebug('SMSound._onfinish(): "'+self.sID+'" finished playing'); | |
634 self.playState = 0; | |
635 self.paused = false; | |
636 if (self.options.onfinish) self.options.onfinish.apply(self); | |
637 if (self.options.onbeforefinishcomplete) self.options.onbeforefinishcomplete.apply(self); | |
638 // reset some state items | |
639 self.setPosition(0); | |
640 self.didBeforeFinish = false; | |
641 self.didJustBeforeFinish = false; | |
642 } | |
643 | |
644 } | |
645 | |
646 var soundManager = new SoundManager(); | |
647 | |
648 // attach onload handler | |
649 if (window.addEventListener) { | |
650 window.addEventListener('load',soundManager.beginInit,false); | |
651 window.addEventListener('beforeunload',soundManager.destruct,false); | |
652 } else if (window.attachEvent) { | |
653 window.attachEvent('onload',soundManager.beginInit); | |
654 window.attachEvent('beforeunload',soundManager.destruct); | |
655 } else { | |
656 // no add/attachevent support - safe to assume no JS->Flash either. | |
657 soundManager.disable(); | |
658 } |