/* SoundManager 2: Javascript Sound for the Web ---------------------------------------------- http://schillmania.com/projects/soundmanager2/ Copyright (c) 2007, Scott Schiller. All rights reserved. Code licensed under the BSD License: http://www.schillmania.com/projects/soundmanager2/license.txt Flash 9 / ActionScript 3 version */ package { import flash.display.Sprite; import flash.events.Event; import flash.events.IOErrorEvent; import flash.events.MouseEvent; import flash.events.SecurityErrorEvent; import flash.events.AsyncErrorEvent; import flash.events.NetStatusEvent; import flash.events.TimerEvent; import flash.external.ExternalInterface; // woo import flash.media.Sound; import flash.media.SoundChannel; import flash.media.SoundMixer; import flash.net.URLLoader; import flash.net.URLRequest; import flash.system.Security; import flash.system.System; import flash.text.TextField; import flash.text.TextFormat; import flash.text.TextFieldAutoSize; import flash.ui.ContextMenu; import flash.ui.ContextMenuItem; import flash.utils.setInterval; import flash.utils.clearInterval; import flash.utils.Dictionary; import flash.utils.Timer; public class SoundManager2_AS3 extends Sprite { public var version:String = "V2.97a.20110424"; public var version_as:String = "(AS3/Flash 9)"; /* * Cross-domain security options * HTML on foo.com loading .swf hosted on bar.com? Define your "HTML domain" here to allow JS+Flash communication to work. * // allow_xdomain_scripting = true; * // xdomain = "foo.com"; * For all domains (possible security risk?), use xdomain = "*"; which ends up as System.security.allowDomain("*"); * When loading from HTTPS, use System.security.allowInsecureDomain(); * See http://livedocs.adobe.com/flash/9.0/ActionScriptLangRefV3/flash/system/Security.html#allowDomain() */ public var allow_xdomain_scripting:Boolean = false; public var xdomain:String = "*"; // externalInterface references (for Javascript callbacks) public var baseJSController:String = "soundManager"; public var baseJSObject:String = baseJSController + ".sounds"; // internal objects public var sounds:Array = []; // indexed string array public var soundObjects: Dictionary = new Dictionary(); // associative Sound() object Dictionary type public var timer: Timer = null; public var pollingEnabled: Boolean = false; // polling (timer) flag - disabled by default, enabled by JS->Flash call public var debugEnabled: Boolean = true; // Flash debug output enabled by default, disabled by JS call public var flashDebugEnabled: Boolean = false; // Flash internal debug output (write to visible SWF in browser) public var loaded: Boolean = false; public var currentObject: SoundManager2_SMSound_AS3 = null; public var paramList:Object = null; public var messages:Array = []; public var textField: TextField = null; public var textStyle: TextFormat = new TextFormat(); public var didSandboxMessage: Boolean = false; public var caughtFatal: Boolean = false; public function SoundManager2_AS3() { if (allow_xdomain_scripting && xdomain) { Security.allowDomain(xdomain); version_as += ' - cross-domain enabled'; } this.paramList = this.root.loaderInfo.parameters; // if (this.paramList['debug'] == 1) { this.flashDebugEnabled = true; } if (this.flashDebugEnabled) { var canvas: Sprite = new Sprite(); canvas.graphics.drawRect(0, 0, stage.stageWidth, stage.stageHeight); addChild(canvas); } // flashDebug('SM2 SWF ' + version + ' ' + version_as); // context menu item with version info var sm2Menu:ContextMenu = new ContextMenu(); var sm2MenuItem:ContextMenuItem = new ContextMenuItem('SoundManager ' + version + ' ' + version_as); sm2MenuItem.enabled = false; sm2Menu.customItems.push(sm2MenuItem); contextMenu = sm2Menu; if (ExternalInterface.available) { flashDebug('ExternalInterface available'); try { flashDebug('Adding ExternalInterface callbacks...'); ExternalInterface.addCallback('_load', _load); ExternalInterface.addCallback('_unload', _unload); ExternalInterface.addCallback('_stop', _stop); ExternalInterface.addCallback('_start', _start); ExternalInterface.addCallback('_pause', _pause); ExternalInterface.addCallback('_setPosition', _setPosition); ExternalInterface.addCallback('_setPan', _setPan); ExternalInterface.addCallback('_setVolume', _setVolume); ExternalInterface.addCallback('_setPolling', _setPolling); ExternalInterface.addCallback('_externalInterfaceTest', _externalInterfaceTest); ExternalInterface.addCallback('_disableDebug', _disableDebug); ExternalInterface.addCallback('_getMemoryUse', _getMemoryUse); ExternalInterface.addCallback('_createSound', _createSound); ExternalInterface.addCallback('_destroySound', _destroySound); ExternalInterface.addCallback('_setAutoPlay', _setAutoPlay); } catch(e: Error) { flashDebug('Fatal: ExternalInterface error: ' + e.toString()); } } else { flashDebug('Fatal: ExternalInterface (Flash <-> JS) not available'); }; // call after delay, to be safe (ensure callbacks are registered by the time JS is called below) var timer: Timer = new Timer(20, 0); timer.addEventListener(TimerEvent.TIMER, function() : void { timer.reset(); _externalInterfaceTest(true); // timer.reset(); // flashDebug('Init OK'); }); timer.start(); // delayed, see above // _externalInterfaceTest(true); } // SoundManager2() public function flashDebug (txt:String) : void { // messages.push(txt); if (this.flashDebugEnabled) { var didCreate: Boolean = false; textStyle.font = 'Arial'; textStyle.size = 12; // 320x240 if no stage dimensions (happens in IE, apparently 0 before stage resize event fires.) var w:Number = this.stage.width?this.stage.width:320; var h:Number = this.stage.height?this.stage.height:240; if (textField == null) { didCreate = true; textField = new TextField(); textField.autoSize = TextFieldAutoSize.LEFT; textField.x = 0; textField.y = 0; textField.multiline = true; textField.textColor = 0; textField.wordWrap = true; } textField.htmlText = messages.join('\n'); textField.setTextFormat(textStyle); textField.width = w; textField.height = h; if (didCreate) { this.addChild(textField); } } // } public function _setAutoPlay(sID:String, autoPlay:Boolean) : void { var s: SoundManager2_SMSound_AS3 = soundObjects[sID]; if (s) { s.setAutoPlay(autoPlay); } } // methods // ----------------------------------- public function writeDebug (s:String, bTimestamp: Boolean = false) : Boolean { if (!debugEnabled) return false; // ExternalInterface.call(baseJSController + "['_writeDebug']", "(Flash): " + s, null, bTimestamp); // return true; } public function _externalInterfaceTest(isFirstCall: Boolean) : Boolean { var sandboxType:String = flash.system.Security['sandboxType']; if (!didSandboxMessage && sandboxType != 'localTrusted' && sandboxType != 'remote') { didSandboxMessage = true; flashDebug('
Fatal: Security sandbox error: Got "' + sandboxType + '", expected "remote" or "localTrusted".
Additional security permissions need to be granted.
See flash security settings panel for non-HTTP, eg., file:// use.

http://www.macromedia.com/support/documentation/en/flashplayer/help/settings_manager04.html'); } try { if (isFirstCall == true) { flashDebug('Testing Flash -> JS...'); var d: Date = new Date(); ExternalInterface.call(baseJSController + "._externalInterfaceOK", d.getTime()); flashDebug('Flash -> JS OK'); } else { writeDebug('SM2 SWF ' + version + ' ' + version_as); flashDebug('JS -> Flash OK'); ExternalInterface.call(baseJSController + "._setSandboxType", sandboxType); writeDebug('JS to/from Flash OK'); } } catch(e: Error) { flashDebug('Fatal: Flash <-> JS error: ' + e.toString()); writeDebug('_externalInterfaceTest: Error: ' + e.toString()); if (!caughtFatal) { caughtFatal = true; } return false; } return true; // to verify that a call from JS to here, works. (eg. JS receives "true", thus OK.) } public function _disableDebug() : void { // prevent future debug calls from Flash going to client (maybe improve performance) writeDebug('_disableDebug()'); debugEnabled = false; } public function checkLoadProgress(e: Event) : void { try { var oSound:Object = e.target; var bL: int = oSound.bytesLoaded; var bT: int = oSound.bytesTotal; var nD: int = oSound.length || oSound.duration || 0; var sMethod:String = baseJSObject + "['" + oSound.sID + "']._whileloading"; ExternalInterface.call(sMethod, bL, bT, nD); if (bL && bT && bL != oSound.lastValues.bytes) { oSound.lastValues.bytes = bL; ExternalInterface.call(sMethod, bL, bT, nD); } } catch(e: Error) { writeDebug('checkLoadProgress(): ' + e.toString()); } } public function checkProgress() : void { var bL: int = 0; var bT: int = 0; var nD: int = 0; var nP: int = 0; var bufferLength: int = 0; var lP:Number = 0; var rP:Number = 0; var isBuffering:Object = null; var oSound: SoundManager2_SMSound_AS3 = null; var oSoundChannel: flash.media.SoundChannel = null; var sMethod:String = null; var newPeakData: Boolean = false; var newWaveformData: Boolean = false; var newEQData: Boolean = false; var areSoundsInaccessible: Boolean = SoundMixer.areSoundsInaccessible(); var isPlaying: Boolean = true; // special case for NetStream when ending var hasNew:Boolean = false; var hasNewLoaded:Boolean = false; for (var i: int = 0, j: int = sounds.length; i < j; i++) { oSound = soundObjects[sounds[i]]; sMethod = baseJSObject + "['" + sounds[i] + "']._whileloading"; if (!oSound || !oSound.useEvents || oSound.failed || !oSound.connected) { // various cases for ignoring continue; // if sounds are destructed within event handlers while this loop is running, may be null } if (oSound.useNetstream) { // Don't do anything if there is no NetStream object yet if (!oSound.ns) { continue; } // stream bufferLength = oSound.ns.bufferLength; bL = oSound.ns.bytesLoaded; bT = oSound.ns.bytesTotal; nD = int(oSound.duration || 0); // can sometimes be null with short MP3s? Wack. nP = oSound.ns.time * 1000; if (oSound.paused) { // special case: paused netStreams don't update if setPosition() is called while they are paused. // instead, return lastValues.position which should reflect setPosition() call. // writeDebug('paused case, setting nP of '+nP+' to -1'); // writeDebug('lastValues: '+oSound.lastValues.position); nP = oSound.lastValues.position; } if (nP >= 0 && nP != oSound.lastValues.position) { oSound.lastValues.position = nP; hasNew = true; } if (nD > oSound.lastValues.duration) { oSound.lastValues.duration = nD; hasNew = true; } if (bL > oSound.lastValues.bytesLoaded) { oSound.lastValues.bytesLoaded = bL; hasNew = true; } if (bT > oSound.lastValues.bytes) { oSound.lastValues.bytes = bT; hasNew = true; } if (bufferLength != oSound.lastValues.bufferLength) { oSound.lastValues.bufferLength = bufferLength; hasNew = true; } // Don't set loaded for streams because bytesLoaded and bytesTotal are always 0 // writeDebug('ns: time/duration, bytesloaded/total: '+nP+'/'+nD+', '+bL+'/'+bT); if (oSound.loaded != true && nD > 0 && bL == bT && bL != 0 && bT != 0) { // non-MP3 has loaded oSound.loaded = true; try { ExternalInterface.call(sMethod, bL, bT, nD, bufferLength); ExternalInterface.call(baseJSObject + "['" + oSound.sID + "']._onload", oSound.duration > 0 ? 1 : 0); } catch(e: Error) { writeDebug('_whileLoading/_onload error: ' + e.toString()); } } else if (oSound.loaded != true && hasNew) { ExternalInterface.call(sMethod, bL, bT, nD, bufferLength); } } else { // MP3 sound oSoundChannel = oSound.soundChannel; bL = oSound.bytesLoaded; bT = oSound.bytesTotal; nD = int(oSound.length || 0); // can sometimes be null with short MP3s? Wack. isBuffering = oSound.isBuffering; if (oSoundChannel) { nP = (oSoundChannel.position || 0); if (oSound.lastValues.loops > 1 && nP > oSound.length) { // round down to nearest loop var playedLoops:Number = Math.floor(nP/oSound.length); nP = nP - (oSound.length*playedLoops); } if (oSound.usePeakData) { lP = int((oSoundChannel.leftPeak) * 1000) / 1000; rP = int((oSoundChannel.rightPeak) * 1000) / 1000; } else { lP = 0; rP = 0; } } else { // stopped, not loaded or feature not used nP = 0; } if (nP != oSound.lastValues.position) { oSound.lastValues.position = nP; hasNew = true; } if (nD > oSound.lastValues.duration) { // original sound duration * number of sound loops oSound.lastValues.duration = nD; hasNew = true; } if (bL > oSound.lastValues.bytesLoaded) { oSound.lastValues.bytesLoaded = bL; hasNew = true; } if (bT > oSound.lastValues.bytes) { oSound.lastValues.bytes = bT; hasNew = true; hasNewLoaded = true; } // loading progress if (hasNewLoaded) { oSound.lastValues.bytes = bL; ExternalInterface.call(sMethod, bL, bT, nD); } } // peak data if (oSoundChannel && oSound.usePeakData) { if (lP != oSound.lastValues.leftPeak) { oSound.lastValues.leftPeak = lP; newPeakData = true; } if (rP != oSound.lastValues.rightPeak) { oSound.lastValues.rightPeak = rP; newPeakData = true; } } var newDataError:Boolean = false; var dataError:String; // special case: Netstream may try to fire whileplaying() after finishing. check that stop hasn't fired. isPlaying = (oSound.didLoad && !oSound.paused && (!oSound.useNetstream || (oSound.useNetstream && oSound.lastNetStatus != "NetStream.Play.Stop"))); // don't update if stream has ended // raw waveform + EQ spectrum data if (isPlaying && oSoundChannel) { // || oSound.useNetstream)) { if (oSound.useWaveformData) { if (!areSoundsInaccessible && !oSound.handledDataError && !oSound.ignoreDataError) { try { oSound.getWaveformData(); } catch(e: Error) { if (!oSound.handledDataError) { writeDebug('getWaveformData() (waveform data) '+e.toString()); } // oSound.useWaveformData = false; newDataError = true; dataError = e.toString(); } } } if (oSound.useEQData) { if (!areSoundsInaccessible && !oSound.handledDataError && !oSound.ignoreDataError) { try { oSound.getEQData(); } catch(e: Error) { if (!oSound.handledDataError) { writeDebug('computeSpectrum() (EQ data) '+e.toString()); } // oSound.useEQData = false; newDataError = true; dataError = e.toString(); } } } if (oSound.waveformDataArray != oSound.lastValues.waveformDataArray) { oSound.lastValues.waveformDataArray = oSound.waveformDataArray; newWaveformData = true; } if (oSound.eqDataArray != oSound.lastValues.eqDataArray) { oSound.lastValues.eqDataArray = oSound.eqDataArray; newEQData = true; } if (newDataError && !oSound.handledDataError) { sMethod = baseJSObject + "['" + sounds[i] + "']._ondataerror"; ExternalInterface.call(sMethod, 'data unavailable: ' + dataError); oSound.handledDataError = true; } } if (typeof nP != 'undefined' && hasNew) { // && isPlaying - removed to allow updates while paused, eg. from setPosition() calls // oSound.lastValues.position = nP; sMethod = baseJSObject + "['" + sounds[i] + "']._whileplaying"; var waveDataLeft:String = (newWaveformData ? oSound.waveformDataArray.slice(0, 256).join(',') : null); var waveDataRight:String = (newWaveformData ? oSound.waveformDataArray.slice(256).join(',') : null); var eqDataLeft:String = (newEQData ? oSound.eqDataArray.slice(0, 256).join(',') : null); var eqDataRight:String = (newEQData ? oSound.eqDataArray.slice(256).join(',') : null); ExternalInterface.call(sMethod, nP, (newPeakData ? { leftPeak: lP, rightPeak: rP } : null), waveDataLeft, waveDataRight, (newEQData ? { leftEQ: eqDataLeft, rightEQ: eqDataRight } : null)); // if position changed, check for near-end if (oSound.didJustBeforeFinish != true && oSound.loaded == true && oSound.justBeforeFinishOffset > 0 && nD - nP <= oSound.justBeforeFinishOffset) { // fully-loaded, near end and haven't done this yet.. sMethod = baseJSObject + "['" + sounds[i] + "']._onjustbeforefinish"; ExternalInterface.call(sMethod, (nD - nP)); oSound.didJustBeforeFinish = true; } } // check isBuffering if (!oSound.useNetstream && oSound.isBuffering != oSound.lastValues.isBuffering) { // property has changed oSound.lastValues.isBuffering = oSound.isBuffering; sMethod = baseJSObject + "['" + sounds[i] + "']._onbufferchange"; ExternalInterface.call(sMethod, oSound.isBuffering ? 1 : 0); } } } public function onLoadError(oSound:Object) : void { // something went wrong. 404, bad format etc. ExternalInterface.call(baseJSObject + "['" + oSound.sID + "']._onload", 0); } public function onLoad(e: Event) : void { checkProgress(); // ensure progress stats are up-to-date var oSound:Object = e.target; if (!oSound.useNetstream) { // FLV must also have metadata oSound.loaded = true; // force duration update (doesn't seem to be always accurate) ExternalInterface.call(baseJSObject + "['" + oSound.sID + "']._whileloading", oSound.bytesLoaded, oSound.bytesTotal, oSound.length || oSound.duration); // TODO: Determine if loaded or failed - bSuccess? // ExternalInterface.call(baseJSObject+"['"+oSound.sID+"']._onload",bSuccess?1:0); ExternalInterface.call(baseJSObject + "['" + oSound.sID + "']._onload", 1); } } public function onID3(e: Event) : void { // --- NOTE: BUGGY (Flash 8 only? Haven't really checked 9 + 10.) --- // TODO: Investigate holes in ID3 parsing - for some reason, Album will be populated with Date if empty and date is provided. (?) // ID3V1 seem to parse OK, but "holes" / blanks in ID3V2 data seem to get messed up (eg. missing album gets filled with date.) // iTunes issues: onID3 was not called with a test MP3 encoded with iTunes 7.01, and what appeared to be valid ID3V2 data. // May be related to thumbnails for album art included in MP3 file by iTunes. See http://mabblog.com/blog/?p=33 try { var oSound:Object = e.target; var id3Data:Array = []; var id3Props:Array = []; for (var prop:String in oSound.id3) { id3Props.push(prop); id3Data.push(oSound.id3[prop]); // writeDebug('id3['+prop+']: '+oSound.id3[prop]); } ExternalInterface.call(baseJSObject + "['" + oSound.sID + "']._onid3", id3Props, id3Data); // unhook own event handler, prevent second call (can fire twice as data is received - ID3V2 at beginning, ID3V1 at end.) // Therefore if ID3V2 data is received, ID3V1 is ignored. // soundObjects[oSound.sID].onID3 = null; } catch(e: Error) { writeDebug('onID3(): Unable to get ID3 info for ' + oSound.sID + '.'); } oSound.removeEventListener(Event.ID3, onID3); } public function registerOnComplete(sID:String) : void { var oSound: SoundManager2_SMSound_AS3 = soundObjects[sID]; if (oSound && oSound.soundChannel) { oSound.soundChannel.addEventListener(Event.SOUND_COMPLETE, function() : void { if (oSound) { oSound.didJustBeforeFinish = false; // reset checkProgress(); try { oSound.ignoreDataError = true; // workaround: avoid data error handling for this manual step.. oSound.start(0, 1); // go back to 0 oSound.soundChannel.stop(); } catch(e: Error) { writeDebug('Could not set position on ' + sID + ': ' + e.toString()); } oSound.ignoreDataError = false; // ..and reset oSound.handledDataError = false; // reset this flag } // checkProgress(); ExternalInterface.call(baseJSObject + "['" + sID + "']._onfinish"); }); } } public function doSecurityError(oSound: SoundManager2_SMSound_AS3, e: SecurityErrorEvent) : void { writeDebug('securityError: ' + e.text); // when this happens, you don't have security rights on the server containing the FLV file // a crossdomain.xml file would fix the problem easily } public function _setPosition(sID:String, nSecOffset:Number, isPaused: Boolean) : void { var s: SoundManager2_SMSound_AS3 = soundObjects[sID]; if (!s) return void; // writeDebug('_setPosition()'); // stop current channel, start new one. if (s.lastValues) { s.lastValues.position = nSecOffset; // s.soundChannel.position; } if (s.useNetstream) { // Minimize the buffer so playback starts ASAP s.ns.bufferTime = s.bufferTime; writeDebug('setPosition: setting buffer to '+s.ns.bufferTime+' secs'); nSecOffset = nSecOffset > 0 ? nSecOffset / 1000 : 0; s.ns.seek(nSecOffset); checkProgress(); // force UI update } else { if (s.soundChannel) { s.soundChannel.stop(); } writeDebug('setPosition: ' + nSecOffset); // +', '+(s.lastValues.loops?s.lastValues.loops:1)); if (s.lastValues.loops > 1 && nSecOffset != 0) { writeDebug('Warning: Looping functionality being disabled due to Flash limitation.'); s.lastValues.loops = 1; } try { s.start(nSecOffset, s.lastValues.loops || 1); // start playing at new position } catch(e: Error) { writeDebug('Warning: Could not set position on ' + sID + ': ' + e.toString()); } checkProgress(); // force UI update try { registerOnComplete(sID); } catch(e: Error) { writeDebug('_setPosition(): Could not register onComplete'); } if (isPaused && s.soundChannel) { // writeDebug('_setPosition: stopping (paused) sound'); // writeDebug('last position: '+s.lastValues.position+' vs '+s.soundChannel.position); s.soundChannel.stop(); } } } public function _load(sID:String, sURL:String, bStream: Boolean, bAutoPlay: Boolean, nLoops: Number, bAutoLoad: Boolean, bCheckPolicyFile: Boolean) : void { // writeDebug('_load()'); if (typeof bAutoPlay == 'undefined') bAutoPlay = false; var s: SoundManager2_SMSound_AS3 = soundObjects[sID]; if (!s) return void; var didRecreate: Boolean = false; if (s.didLoad == true) { // need to recreate sound didRecreate = true; writeDebug('recreating sound ' + sID + ' in order to load ' + sURL); var ns:Object = new Object(); ns.sID = s.sID; ns.loops = nLoops||1; ns.justBeforeFinishOffset = s.justBeforeFinishOffset; ns.usePeakData = s.usePeakData; ns.useWaveformData = s.useWaveformData; ns.useEQData = s.useEQData; ns.useNetstream = s.useNetstream; ns.bufferTime = s.bufferTime; ns.serverUrl = s.serverUrl; ns.duration = s.duration; ns.checkPolicyFile = s.checkPolicyFile; ns.useEvents = true; _destroySound(s.sID); _createSound(ns.sID, sURL, ns.justBeforeFinishOffset, ns.usePeakData, ns.useWaveformData, ns.useEQData, ns.useNetstream, ns.bufferTime, ns.loops, ns.serverUrl, ns.duration, bAutoPlay, ns.useEvents, bAutoLoad, ns.checkPolicyFile); s = soundObjects[sID]; // writeDebug('Sound object replaced'); } checkProgress(); if (!s.didLoad) { try { s.addEventListener(Event.ID3, onID3); s.addEventListener(Event.COMPLETE, onLoad); } catch(e: Error) { writeDebug('_load(): could not assign ID3/complete event handlers'); } } // don't try to load if same request already made s.sURL = sURL; try { if (!s.useNetstream) { s.addEventListener(IOErrorEvent.IO_ERROR, function(e: IOErrorEvent) : void { s.doIOError(e); }); } s.loadSound(sURL); } catch(e: Error) { // oh well writeDebug('_load: Error loading ' + sURL + '. Flash error detail: ' + e.toString()); } s.didJustBeforeFinish = false; } public function _unload(sID:String) : void { var s: SoundManager2_SMSound_AS3 = (soundObjects[sID] || null); if (!s) return void; var sURL:String = s.sURL; // save existing sound URL for object recreation try { removeEventListener(Event.ID3, onID3); removeEventListener(Event.COMPLETE, onLoad); } catch(e: Error) { writeDebug('_unload() warn: Could not remove ID3/complete events'); } s.paused = false; if (s.soundChannel) { s.soundChannel.stop(); } try { if (s.didLoad && !s.loaded && !s.useNetstream) { s.close(); // close stream only if still loading? } } catch(e: Error) { // stream may already have closed if sound loaded, etc. writeDebug(sID + '._unload(): Note: Unable to close stream: ' + e.toString()); // oh well } // destroy and recreate Flash sound object, try to reclaim memory // writeDebug('sound._unload(): recreating sound '+sID+' to free memory'); if (s.useNetstream) { // writeDebug('_unload(): closing netStream stuff'); try { s.removeNetstreamEvents(); s.ns.close(); s.nc.close(); // s.nc = null; // s.ns = null; } catch(e: Error) { // oh well writeDebug('_unload(): caught exception during netConnection/netStream close'); } } var ns:Object = new Object(); ns.sID = s.sID; ns.loops = s.loops; ns.justBeforeFinishOffset = s.justBeforeFinishOffset; ns.usePeakData = s.usePeakData; ns.useWaveformData = s.useWaveformData; ns.useEQData = s.useEQData; ns.useNetstream = s.useNetstream; ns.bufferTime = s.bufferTime; ns.serverUrl = s.serverUrl; ns.duration = s.duration; ns.autoPlay = s.autoPlay; ns.autoLoad = s.autoLoad; ns.checkPolicyFile = s.checkPolicyFile; _destroySound(s.sID); _createSound(ns.sID, sURL, ns.justBeforeFinishOffset, ns.usePeakData, ns.useWaveformData, ns.useEQData, ns.useNetstream, ns.bufferTime, ns.loops, ns.serverUrl, ns.duration, ns.autoPlay, false, ns.autoLoad, ns.checkPolicyFile); // false: don't allow events just yet soundObjects[sID].connected = true; // fake it? writeDebug(s.sID + '.unload(): ok'); } public function _createSound(sID:String, sURL:String, justBeforeFinishOffset: int, usePeakData: Boolean, useWaveformData: Boolean, useEQData: Boolean, useNetstream: Boolean, bufferTime:Number, loops:Number, serverUrl:String, duration:Number, autoPlay:Boolean, useEvents:Boolean, autoLoad:Boolean, checkPolicyFile:Boolean) : void { var s: SoundManager2_SMSound_AS3 = new SoundManager2_SMSound_AS3(this, sID, sURL, usePeakData, useWaveformData, useEQData, useNetstream, bufferTime, serverUrl, duration, autoPlay, useEvents, autoLoad, checkPolicyFile); if (!soundObjects[sID]) { sounds.push(sID); } soundObjects[sID] = s; this.currentObject = s; s.didJustBeforeFinish = false; s.sID = sID; s.sURL = sURL; s.paused = false; s.loaded = false; s.justBeforeFinishOffset = justBeforeFinishOffset || 0; s.checkPolicyFile = checkPolicyFile; s.lastValues = { bytes: 0, duration: 0, position: 0, loops: loops||1, leftPeak: 0, rightPeak: 0, bufferLength: 0 }; } public function _destroySound(sID:String) : void { // for the power of garbage collection! .. er, Greyskull! var s: SoundManager2_SMSound_AS3 = (soundObjects[sID] || null); if (!s) return void; // try to unload the sound for (var i: int = 0, j: int = sounds.length; i < j; i++) { if (sounds[i] == sID) { sounds.splice(i, 1); break; } } if (s.soundChannel) { s.soundChannel.stop(); } // if is a movie, remove that as well. if (s.useNetstream) { // s.nc.client = null; try { s.removeNetstreamEvents(); // s.nc.removeEventListener(NetStatusEvent.NET_STATUS, s.doNetStatus); } catch(e: Error) { writeDebug('_destroySound(): Events already removed from netStream/netConnection?'); } if (s.didLoad) { // TODO: figure out if stream is still open first, can't close an already-closed stream. try { s.ns.close(); s.nc.close(); } catch(e: Error) { // oh well writeDebug('_destroySound(): caught exception: '+e.toString()); } } } else if (s.didLoad) { // non-netstream case try { s.close(); // close stream only if still loading? } catch(e: Error) { // oh well } } s = null; soundObjects[sID] = null; delete soundObjects[sID]; } public function _stop(sID:String, bStopAll: Boolean) : void { // stop this particular instance (or "all", based on parameter) if (bStopAll) { SoundMixer.stopAll(); } else { var s: SoundManager2_SMSound_AS3 = soundObjects[sID]; if (!s) return void; if (s.useNetstream && s.ns) { s.ns.pause(); } else if (s.soundChannel) { s.soundChannel.stop(); } s.paused = false; s.didJustBeforeFinish = false; } } public function _start(sID:String, nLoops: int, nMsecOffset: int) : void { var s: SoundManager2_SMSound_AS3 = soundObjects[sID]; if (!s) return void; writeDebug('start: ' + nMsecOffset+(nLoops?', loops: '+nLoops:'')); s.lastValues.paused = false; // reset pause if applicable s.lastValues.loops = (nLoops || 1); if (!s.useNetstream) { s.lastValues.position = nMsecOffset; } s.handledDataError = false; // reset this flag try { s.start(nMsecOffset, nLoops); } catch(e: Error) { writeDebug('Could not start ' + sID + ': ' + e.toString()); } try { registerOnComplete(sID); } catch(e: Error) { writeDebug('_start(): registerOnComplete failed'); } } public function _pause(sID:String) : void { // writeDebug('_pause()'); var s: SoundManager2_SMSound_AS3 = soundObjects[sID]; if (!s) return void; // writeDebug('s.paused: '+s.paused); if (!s.paused) { // reference current position, stop sound s.paused = true; // writeDebug('_pause(): position: '+s.lastValues.position); if (s.useNetstream) { if (s.ns) { s.lastValues.position = s.ns.time*1000; s.ns.pause(); } else if (s.autoPlay) { s.setAutoPlay(false); } } else { if (s.soundChannel) { s.lastValues.position = s.soundChannel.position; s.soundChannel.stop(); } } } else { // resume playing from last position // writeDebug('resuming - playing at '+s.lastValues.position+', '+s.lastValues.loops+' times'); s.paused = false; if (s.useNetstream) { s.ns.resume(); } else { s.start(s.lastValues.position, s.lastValues.loops); } try { registerOnComplete(sID); } catch(e: Error) { writeDebug('_pause(): registerOnComplete() failed'); } } } public function _setPan(sID:String, nPan:Number) : void { if (soundObjects[sID]) { soundObjects[sID].setPan(nPan); } } public function _setVolume(sID:String, nVol:Number) : void { // writeDebug('_setVolume: '+nVol); if (soundObjects[sID]) { soundObjects[sID].setVolume(nVol); } } public function _setPolling(bPolling: Boolean = false, nTimerInterval: uint = 50) : void { pollingEnabled = bPolling; if (timer == null && pollingEnabled) { writeDebug('Enabling polling, ' + nTimerInterval + ' ms interval'); timer = new Timer(nTimerInterval, 0); timer.addEventListener(TimerEvent.TIMER, function() : void { checkProgress(); }); // direct reference eg. checkProgress doesn't work? .. odd. timer.start(); } else if (timer && !pollingEnabled) { writeDebug('Disabling polling'); // flash.utils.clearInterval(timer); timer.reset(); } } public function _getMemoryUse() : String { return System.totalMemory.toString(); } // ----------------------------------- // end methods } // package }