From 753f60c7d4769fa72d3b910e491f37db6f130898 Mon Sep 17 00:00:00 2001 From: Jules Laplace Date: Fri, 2 Aug 2013 17:19:21 -0500 Subject: dymaxion --- .../src/SoundManager2.as | 452 ++++++++++ .../src/SoundManager2_AS3.as | 970 +++++++++++++++++++++ .../src/SoundManager2_SMSound_AS3.as | 597 +++++++++++++ .../soundmanagerv297a-20101010/src/make-flash8.bat | 5 + .../soundmanagerv297a-20101010/src/make-flash8.sh | 2 + .../soundmanagerv297a-20101010/src/make-flash9.bat | 4 + .../soundmanagerv297a-20101010/src/make-flash9.sh | 2 + 7 files changed, 2032 insertions(+) create mode 100755 docs/dymaxion/soundmanagerv297a-20101010/src/SoundManager2.as create mode 100755 docs/dymaxion/soundmanagerv297a-20101010/src/SoundManager2_AS3.as create mode 100755 docs/dymaxion/soundmanagerv297a-20101010/src/SoundManager2_SMSound_AS3.as create mode 100755 docs/dymaxion/soundmanagerv297a-20101010/src/make-flash8.bat create mode 100755 docs/dymaxion/soundmanagerv297a-20101010/src/make-flash8.sh create mode 100755 docs/dymaxion/soundmanagerv297a-20101010/src/make-flash9.bat create mode 100755 docs/dymaxion/soundmanagerv297a-20101010/src/make-flash9.sh (limited to 'docs/dymaxion/soundmanagerv297a-20101010/src') diff --git a/docs/dymaxion/soundmanagerv297a-20101010/src/SoundManager2.as b/docs/dymaxion/soundmanagerv297a-20101010/src/SoundManager2.as new file mode 100755 index 0000000..e9d4011 --- /dev/null +++ b/docs/dymaxion/soundmanagerv297a-20101010/src/SoundManager2.as @@ -0,0 +1,452 @@ +/* + 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 8 / ActionScript 2 version + + Compiling AS to Flash 8 SWF using MTASC (free compiler - http://www.mtasc.org/): + mtasc -swf soundmanager2.swf -main -header 16:16:30 SoundManager2.as -version 8 + + ActionScript Sound class reference (Macromedia), documentation download: + http://livedocs.macromedia.com/flash/8/ + Previously-live URL: + http://livedocs.macromedia.com/flash/8/main/wwhelp/wwhimpl/common/html/wwhelp.htm?context=LiveDocs_Parts&file=00002668.html + + *** NOTE ON LOCAL FILE SYSTEM ACCESS *** + + Flash security allows local OR network access, but not both + unless explicitly whitelisted/allowed by the flash player's + security settings. + + To enable in-flash messaging for troubleshooting, pass debug=1 in FlashVars (within object/embed code) + SM2 will do this by default when soundManager.debugFlash = true. + +*/ + +import flash.external.ExternalInterface; // woo + +class SoundManager2 { + + static var app: SoundManager2; + + function SoundManager2() { + + var version = "V2.97a.20101010"; + var version_as = "(AS2/Flash 8)"; + + /* + * 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 "allowDomain (security.allowDomain method)" in Flash 8/AS2 liveDocs documentation (AS2 reference -> classes -> security) + * download from http://livedocs.macromedia.com/flash/8/ + * Related AS3 documentation: http://livedocs.adobe.com/flash/9.0/ActionScriptLangRefV3/flash/system/Security.html#allowDomain%28%29 + */ + var allow_xdomain_scripting = false; + var xdomain = "*"; + + if (allow_xdomain_scripting && xdomain) { + System.security.allowDomain(xdomain); + version_as += ' - cross-domain enabled'; + } + + // externalInterface references (for Javascript callbacks) + var baseJSController = "soundManager"; + var baseJSObject = baseJSController + ".sounds"; + + // internal objects + var sounds = []; // indexed string array + var soundObjects = []; // associative Sound() object array + var timer = null; + var timerInterval = 50; + var pollingEnabled = false; // polling (timer) flag - disabled by default, enabled by JS->Flash call + var debugEnabled = true; // Flash debug output enabled by default, disabled by JS call + var flashDebugEnabled = false; // debug output to flash movie, off by default + var didSandboxMessage = false; + var caughtFatal = false; + + // for flash text output, debugging etc. + var _messages = []; + var _messageObj = null; + flashDebugEnabled = (_root.debug == 1); + + // display stuffs + Stage.scaleMode = 'noScale'; + Stage.align = 'TL'; + + // context menu item with version info + + var doNothing = function() {} + + var sm2Menu:ContextMenu = new ContextMenu(); + var sm2MenuItem:ContextMenuItem = new ContextMenuItem('SoundManager ' + version + ' ' + version_as, doNothing); + sm2MenuItem.enabled = false; + sm2Menu.customItems.push(sm2MenuItem); + _root.menu = sm2Menu; + + var writeDebug = function(s) { + // + if (!debugEnabled) return false; + ExternalInterface.call(baseJSController + "['_writeDebug']", "(Flash): " + s); + // + } + + var flashDebug = function(messageText) { + // + _messages.push(messageText); + if (!flashDebugEnabled) { + return false; + } + var sans = new TextFormat(); + sans.size = 12; + sans.font = "Arial"; + + // 320x240 if no stage dimensions (happens in IE, apparently 0 before stage resize event fires.) + var w = Stage.width?Stage.width:320; + var h = Stage.height?Stage.height:240; + if (!_messageObj) { + _messageObj = _root.createTextField("_messageObj", 0, 0, 0, w, h); + _messageObj.x = 0; + _messageObj.y = 0; + _messageObj.multiline = true; + _messageObj.html = true; + _messageObj.wordWrap = true; + _messageObj.align = 'left'; + _messageObj.autoSize = false; + } + _messageObj.htmlText = _messages.join('\n'); + _messageObj.setTextFormat(sans); + _messageObj.width = w; + _messageObj.height = h; + // + } + + var _externalInterfaceTest = function(isFirstCall) { + var sandboxType = System.security['sandboxType']; + try { + if (isFirstCall) { + flashDebug('Testing Flash -> JS...') + if (!didSandboxMessage && sandboxType != 'remote' && sandboxType != 'localTrusted') { + 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'); + } + var d = new Date(); + ExternalInterface.call(baseJSController + "._externalInterfaceOK", d.getTime()); + if (!didSandboxMessage) { + flashDebug('Flash -> JS OK'); + } + } else { + writeDebug('SM2 SWF ' + version + ' ' + version_as); + flashDebug('JS -> Flash OK'); + writeDebug('JS to/from Flash OK'); + ExternalInterface.call(baseJSController + "._setSandboxType", sandboxType); + } + } catch(e) { + flashDebug(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.) + } + + var _disableDebug = function() { + // prevent future debug calls from Flash going to client (maybe improve performance) + writeDebug('_disableDebug()'); + debugEnabled = false; + } + + var checkProgress = function() { + var bL = 0; + var bT = 0; + var nD = 0; + var nP = 0; + var oSound = null; + for (var i = 0, j = sounds.length; i < j; i++) { + oSound = soundObjects[sounds[i]]; + bL = oSound.getBytesLoaded(); + bT = oSound.getBytesTotal(); + nD = oSound.duration || 0; // can sometimes be null with short MP3s? Wack. + nP = oSound.position; + if (bL && bT && bL != oSound.lastValues.bytes) { + oSound.lastValues.bytes = bL; + ExternalInterface.call(baseJSObject + "['" + oSound.sID + "']._whileloading", bL, bT, nD); + } + if (typeof nP != 'undefined' && nP != oSound.lastValues.position) { + oSound.lastValues.position = nP; + ExternalInterface.call(baseJSObject + "['" + oSound.sID + "']._whileplaying", nP); + // 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.. + ExternalInterface.call(baseJSObject + "['" + oSound.sID + "']._onjustbeforefinish", (nD - nP)); + oSound.didJustBeforeFinish = true; + } + } + } + } + + var onLoad = function(bSuccess) { + checkProgress(); // ensure progress stats are up-to-date + // force duration update (doesn't seem to be always accurate) + ExternalInterface.call(baseJSObject + "['" + this.sID + "']._whileloading", this.getBytesLoaded(), this.getBytesTotal(), this.duration); + ExternalInterface.call(baseJSObject + "['" + this.sID + "']._onload", this.duration > 0 ? 1 : 0); // bSuccess doesn't always seem to work, so check MP3 duration instead. + } + + var onID3 = function() { + // --- NOTE: BUGGY? --- + // -------------------- + // 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 + var id3Data = []; + var id3Props = []; + for (var prop in this.id3) { + id3Props.push(prop); + id3Data.push(this.id3[prop]); + // writeDebug('id3['+prop+']: '+this.id3[prop]); + } + ExternalInterface.call(baseJSObject + "['" + this.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[this.sID].onID3 = null; + } + + var registerOnComplete = function(sID) { + soundObjects[sID].onSoundComplete = function() { + checkProgress(); + this.didJustBeforeFinish = false; // reset + ExternalInterface.call(baseJSObject + "['" + sID + "']._onfinish"); + } + } + + var _setPosition = function(sID, nSecOffset, isPaused) { + var s = soundObjects[sID]; + // writeDebug('_setPosition()'); + s.lastValues.position = s.position; + if (s.lastValues.loops > 1 && nSecOffset != 0) { + writeDebug('Warning: Looping functionality being disabled due to Flash limitation.'); + s.lastValues.loops = 1; + } + s.start(nSecOffset, s.lastValues.nLoops || 1); // start playing at new position + if (isPaused) s.stop(); + } + + var _load = function(sID, sURL, bStream, bAutoPlay, bCheckPolicyFile) { + // writeDebug('_load(): '+sID+', '+sURL+', '+bStream+', '+bAutoPlay); + if (typeof bAutoPlay == 'undefined') bAutoPlay = false; + if (typeof bStream == 'undefined') bStream = true; + if (typeof bCheckPolicyFile == 'undefined') bCheckPolicyFile = false; + // writeDebug('bStream: '+bStream); + // writeDebug('bAutoPlay: '+bAutoPlay); + // checkProgress(); + var s = soundObjects[sID]; + s.onID3 = onID3; + s.onLoad = onLoad; + s.loaded = true; + s.checkPolicyFile = bCheckPolicyFile; + s.loadSound(sURL, bStream); + s.didJustBeforeFinish = false; + if (bAutoPlay != true) { + s.stop(); // prevent default auto-play behaviour + } else { + writeDebug('auto-play allowed'); + } + registerOnComplete(sID); + } + + var _unload = function(sID, sURL) { + // effectively "stop" loading by loading a tiny MP3 + // writeDebug('_unload()'); + var s = soundObjects[sID]; + s.onID3 = null; + s.onLoad = null; + s.loaded = false; + s.loadSound(sURL, true); + s.stop(); // prevent auto-play + s.didJustBeforeFinish = false; + } + + var _createSound = function(sID, justBeforeFinishOffset, loops, checkPolicyFile) { + var s = new Sound(); + if (!soundObjects[sID]) { + sounds.push(sID); + } + soundObjects[sID] = s; + s.setVolume(100); + s.didJustBeforeFinish = false; + s.sID = sID; + s.paused = false; + s.loaded = false; + s.justBeforeFinishOffset = justBeforeFinishOffset || 0; + s.checkPolicyFile = checkPolicyFile; + s.lastValues = { + bytes: 0, + position: 0, + nLoops: loops||1 + }; + } + + var _destroySound = function(sID) { + // for the power of garbage collection! .. er, Greyskull! + var s = (soundObjects[sID] || null); + if (!s) return false; + for (var i = 0; i < sounds.length; i++) { + if (sounds[i] == sID) { + sounds.splice(i, 1); + break; + } + } + s = null; + delete soundObjects[sID]; + } + + var _stop = function(sID, bStopAll) { + // stop this particular instance (or "all", based on parameter) + if (bStopAll) { + _root.stop(); + } else { + soundObjects[sID].stop(); + soundObjects[sID].paused = false; + soundObjects[sID].didJustBeforeFinish = false; + } + } + + var _start = function(sID, nLoops, nMsecOffset) { + // writeDebug('_start: ' + sID + ', loops: ' + nLoops + ', nMsecOffset: ' + nMsecOffset); + registerOnComplete(); + var s = soundObjects[sID]; + s.lastValues.paused = false; // reset pause if applicable + s.lastValues.nLoops = (nLoops || 1); + s.start(nMsecOffset, nLoops); + } + + var _pause = function(sID) { + // writeDebug('_pause()'); + var s = soundObjects[sID]; + if (!s.paused) { + // reference current position, stop sound + s.paused = true; + s.lastValues.position = s.position; + // writeDebug('_pause(): position: '+s.lastValues.position); + s.stop(); + } else { + // resume playing from last position + // writeDebug('resuming - playing at '+s.lastValues.position+', '+s.lastValues.nLoops+' times'); + s.paused = false; + s.start(s.lastValues.position / 1000, s.lastValues.nLoops); + } + } + + var _setPan = function(sID, nPan) { + soundObjects[sID].setPan(nPan); + } + + var _setVolume = function(sID, nVol) { + soundObjects[sID].setVolume(nVol); + } + + var _setPolling = function(bPolling) { + pollingEnabled = bPolling; + if (timer == null && pollingEnabled) { + writeDebug('Enabling polling, ' + timerInterval + ' ms interval'); + timer = setInterval(checkProgress, timerInterval); + } else if (timer && !pollingEnabled) { + writeDebug('Disabling polling'); + clearInterval(timer); + timer = null; + } + } + + // XML handler stuff + var parseXML = function(oXML) { + var xmlRoot = oXML.firstChild; + var xmlAttr = xmlRoot.attributes; + var oOptions = {}; + for (var i = 0, j = xmlRoot.childNodes.length; i < j; i++) { + xmlAttr = xmlRoot.childNodes[i].attributes; + oOptions = { + id: xmlAttr.id, + url: xmlRoot.attributes.baseHref + xmlAttr.href, + stream: xmlAttr.stream + } + ExternalInterface.call(baseJSController + ".createSound", oOptions); + } + } + + var xmlOnloadHandler = function(ok) { + if (ok) { + writeDebug("XML loaded"); + parseXML(this); + } else { + writeDebug('XML load failed'); + } + } + + // --- + var _loadFromXML = function(sXmlUrl) { + writeDebug("_loadFromXML(" + sXmlUrl + ")"); + // ExternalInterface.call(baseJSController+"._writeDebug","_loadFromXML("+sXmlUrl+")"); + // var oXmlHandler = new XMLHandler(sXmlUrl); + var oXML = new XML(); + oXML.ignoreWhite = true; + oXML.onLoad = xmlOnloadHandler; + writeDebug("Attempting to load XML: " + sXmlUrl); + oXML.load(sXmlUrl); + } + + var _init = function() { + + // OK now stuff should be available + try { + flashDebug('Adding ExternalInterface callbacks...'); + ExternalInterface.addCallback('_load', this, _load); + ExternalInterface.addCallback('_unload', this, _unload); + ExternalInterface.addCallback('_stop', this, _stop); + ExternalInterface.addCallback('_start', this, _start); + ExternalInterface.addCallback('_pause', this, _pause); + ExternalInterface.addCallback('_setPosition', this, _setPosition); + ExternalInterface.addCallback('_setPan', this, _setPan); + ExternalInterface.addCallback('_setVolume', this, _setVolume); + ExternalInterface.addCallback('_setPolling', this, _setPolling); + ExternalInterface.addCallback('_externalInterfaceTest', this, _externalInterfaceTest); + ExternalInterface.addCallback('_disableDebug', this, _disableDebug); + ExternalInterface.addCallback('_loadFromXML', null, _loadFromXML); + ExternalInterface.addCallback('_createSound', this, _createSound); + ExternalInterface.addCallback('_destroySound', this, _destroySound); + } catch(e) { + flashDebug('Fatal: ExternalInterface error: ' + e.toString()); + } + // try to talk to JS, do init etc. + _externalInterfaceTest(true); + // flashDebug('Init OK'); + } + + flashDebug('SM2 SWF ' + version + ' ' + version_as); + + if (ExternalInterface.available) { + flashDebug('ExternalInterface available'); + _init(); + } else { + // d'oh! - may be from a corrupt install, ancient (pre-Netscape 6?) browser etc. + flashDebug('Fatal: ExternalInterface (Flash <-> JS) not available'); + } + + + } // SoundManager2() + + // entry point + static function main(mc) { + app = new SoundManager2(); + } + +} \ No newline at end of file diff --git a/docs/dymaxion/soundmanagerv297a-20101010/src/SoundManager2_AS3.as b/docs/dymaxion/soundmanagerv297a-20101010/src/SoundManager2_AS3.as new file mode 100755 index 0000000..445de48 --- /dev/null +++ b/docs/dymaxion/soundmanagerv297a-20101010/src/SoundManager2_AS3.as @@ -0,0 +1,970 @@ +/* + 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; + import flash.xml.XMLDocument; + import flash.xml.XMLNode; + + public class SoundManager2_AS3 extends Sprite { + + public var version:String = "V2.97a.20101010"; + 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 timerInterval: uint = 50; + public var timerIntervalHighPerformance: uint = 10; // ~30fps (in Safari on OSX, anyway..) + 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('_loadFromXML', _loadFromXML); + 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 (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 dataErrors:Array = []; + + // 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 == false) { + try { + oSound.getWaveformData(); + } catch(e: Error) { + // this shouldn't happen, but does seem to fire from time to time. + writeDebug('getWaveformData() warning: ' + e.toString()); + } + } else if (oSound.handledDataError != true && oSound.ignoreDataError != true) { + try { + oSound.getWaveformData(); + } catch(e: Error) { + writeDebug('getWaveformData() (waveform data) '+e.toString()); + // oSound.useWaveformData = false; + newDataError = true; + dataErrors.push(e.toString()); + oSound.handledDataError = true; + } + } + } + if (oSound.useEQData) { + if (areSoundsInaccessible == false) { + try { + oSound.getEQData(); + } catch(e: Error) { + writeDebug('getEQData() warning: ' + e.toString()); + newDataError = true; + dataErrors.push(e.toString()); + oSound.handledDataError = true; + } + } else if (oSound.handledDataError != true && oSound.ignoreDataError != true) { + try { + oSound.getEQData(); + } catch(e: Error) { + // writeDebug('computeSpectrum() (EQ data) '+e.toString()); + // oSound.useEQData = false; + newDataError = true; + dataErrors.push(e.toString()); + oSound.handledDataError = true; + } + } + } + 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) { + sMethod = baseJSObject + "['" + sounds[i] + "']._ondataerror"; + var errors:String = dataErrors.join('
\n'); + ExternalInterface.call(sMethod, 'data unavailable: ' + errors); + } + + 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.setBuffer(s.getStartBuffer()); + writeDebug('setPosition: setting buffer to '+s.ns.bufferTime+' secs'); + + nSecOffset = nSecOffset > 0 ? nSecOffset / 1000 : 0; + writeDebug('setPosition: ' + nSecOffset); + 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.bufferTimes = s.bufferTimes; + ns.serverUrl = s.serverUrl; + ns.duration = s.duration; + ns.recordStats = s.recordStats; + 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, ns.bufferTimes, ns.recordStats, 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; + + if (s.useNetstream) { + try { + s.useEvents = true; + s.ns.play(sURL); + } catch(e: Error) { + writeDebug('_load(): error: ' + e.toString()); + } + } else { + try { + s.addEventListener(IOErrorEvent.IO_ERROR, function(e: IOErrorEvent) : void { + s.doIOError(e); + }); + s.loadSound(sURL, bStream); + } 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.bufferTimes = s.bufferTimes; + ns.serverUrl = s.serverUrl; + ns.duration = s.duration; + ns.autoPlay = s.autoPlay; + ns.recordStats = s.recordStats; + 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.bufferTimes, ns.recordStats, 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, bufferTimes:Array, recordStats: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, bufferTimes, recordStats, 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, + 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; + 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, bUseHighPerformanceTimer: Boolean = false) : void { + pollingEnabled = bPolling; + if (timer == null && pollingEnabled) { + var nTimerInterval: uint = (bUseHighPerformanceTimer ? timerIntervalHighPerformance : timerInterval); + 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(); + } + + // XML handler stuff + public function _loadFromXML(sURL:String) : void { + var loader: URLLoader = new URLLoader(); + loader.addEventListener(Event.COMPLETE, parseXML); + writeDebug('Attempting to load XML: ' + sURL); + try { + loader.load(new URLRequest(sURL)); + } catch(e: Error) { + writeDebug('Error loading XML: ' + e.toString()); + } + } + + public function parseXML(e: Event) : void { + try { + var oXML: XMLDocument = new XMLDocument(); + oXML.ignoreWhite = true; + oXML.parseXML(e.target.data); + var xmlRoot: XMLNode = oXML.firstChild; + var xmlAttr:Object = xmlRoot.attributes; + var oOptions:Object = {}; + var i: int = 0; + var j: int = 0; + for (i = 0, j = xmlRoot.childNodes.length; i < j; i++) { + xmlAttr = xmlRoot.childNodes[i].attributes; + oOptions = { + id: xmlAttr.id, + url: xmlRoot.attributes.baseHref + xmlAttr.href, + stream: xmlAttr.stream + } + ExternalInterface.call(baseJSController + ".createSound", oOptions); + } + } catch(e: Error) { + writeDebug('Error parsing XML: ' + e.toString()); + } + } + + // ----------------------------------- + // end methods + } + + // package + +} \ No newline at end of file diff --git a/docs/dymaxion/soundmanagerv297a-20101010/src/SoundManager2_SMSound_AS3.as b/docs/dymaxion/soundmanagerv297a-20101010/src/SoundManager2_SMSound_AS3.as new file mode 100755 index 0000000..f9a8c35 --- /dev/null +++ b/docs/dymaxion/soundmanagerv297a-20101010/src/SoundManager2_SMSound_AS3.as @@ -0,0 +1,597 @@ +/* + 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.external.*; + import flash.events.*; + import flash.media.Sound; + import flash.media.SoundChannel; + import flash.media.SoundLoaderContext; + import flash.media.SoundTransform; + import flash.media.SoundMixer; + import flash.net.URLRequest; + import flash.utils.ByteArray; + import flash.utils.getTimer; + import flash.net.NetConnection; + import flash.net.NetStream; + + public class SoundManager2_SMSound_AS3 extends Sound { + + public var sm: SoundManager2_AS3 = null; + // externalInterface references (for Javascript callbacks) + public var baseJSController: String = "soundManager"; + public var baseJSObject: String = baseJSController + ".sounds"; + public var soundChannel: SoundChannel = new SoundChannel(); + public var urlRequest: URLRequest; + public var soundLoaderContext: SoundLoaderContext; + public var waveformData: ByteArray = new ByteArray(); + public var waveformDataArray: Array = []; + public var eqData: ByteArray = new ByteArray(); + public var eqDataArray: Array = []; + public var usePeakData: Boolean = false; + public var useWaveformData: Boolean = false; + public var useEQData: Boolean = false; + public var sID: String; + public var sURL: String; + public var justBeforeFinishOffset: int; + public var didJustBeforeFinish: Boolean; + public var didFinish: Boolean; + public var loaded: Boolean; + public var connected: Boolean; + public var failed: Boolean; + public var paused: Boolean; + public var finished: Boolean; + public var duration: Number; + public var handledDataError: Boolean = false; + public var ignoreDataError: Boolean = false; + public var autoPlay: Boolean = false; + public var autoLoad: Boolean = false; + public var pauseOnBufferFull: Boolean = true; + public var loops: Number = 1; + public var lastValues: Object = { + bytes: 0, + position: 0, + volume: 100, + pan: 0, + loops: 1, + leftPeak: 0, + rightPeak: 0, + waveformDataArray: null, + eqDataArray: null, + isBuffering: null, + bufferLength: 0 + }; + public var didLoad: Boolean = false; + public var useEvents: Boolean = false; + public var sound: Sound = new Sound(); + + public var cc: Object; + public var nc: NetConnection; + public var ns: NetStream = null; + public var st: SoundTransform; + public var useNetstream: Boolean; + public var bufferTime: Number = 3; // previously 0.1 + public var bufferTimes: Array; // an array of integers (for specifying multiple buffers) + public var lastNetStatus: String = null; + public var serverUrl: String = null; + + public var start_time: Number; + public var connect_time: Number; + public var play_time: Number; + public var recordStats: Boolean = false; + public var checkPolicyFile:Boolean = false; + + public function SoundManager2_SMSound_AS3(oSoundManager: SoundManager2_AS3, sIDArg: String = null, sURLArg: String = null, usePeakData: Boolean = false, useWaveformData: Boolean = false, useEQData: Boolean = false, useNetstreamArg: Boolean = false, netStreamBufferTime: Number = 1, serverUrl: String = null, duration: Number = 0, autoPlay: Boolean = false, useEvents: Boolean = false, bufferTimes: Array = null, recordStats: Boolean = false, autoLoad: Boolean = false, checkPolicyFile: Boolean = false) { + this.sm = oSoundManager; + this.sID = sIDArg; + this.sURL = sURLArg; + this.usePeakData = usePeakData; + this.useWaveformData = useWaveformData; + this.useEQData = useEQData; + this.urlRequest = new URLRequest(sURLArg); + this.justBeforeFinishOffset = 0; + this.didJustBeforeFinish = false; + this.didFinish = false; // non-MP3 formats only + this.loaded = false; + this.connected = false; + this.failed = false; + this.finished = false; + this.soundChannel = null; + this.lastNetStatus = null; + this.useNetstream = useNetstreamArg; + this.serverUrl = serverUrl; + this.duration = duration; + this.recordStats = recordStats; + this.useEvents = useEvents; + this.autoLoad = autoLoad; + if (netStreamBufferTime) { + this.bufferTime = netStreamBufferTime; + } + // Use bufferTimes instead of bufferTime + if (bufferTimes !== null) { + this.bufferTimes = bufferTimes; + } else { + this.bufferTimes = [this.bufferTime]; + } + setAutoPlay(autoPlay); + if (recordStats) { + this.start_time = getTimer(); + } + this.checkPolicyFile = checkPolicyFile; + + writeDebug('SoundManager2_SMSound_AS3: Got duration: '+duration+', autoPlay: '+autoPlay); + + if (this.useNetstream) { + + this.cc = new Object(); + this.nc = new NetConnection(); + + // Handle FMS bandwidth check callback. + // @see onBWDone + // @see http://www.adobe.com/devnet/flashmediaserver/articles/dynamic_stream_switching_04.html + // @see http://www.johncblandii.com/index.php/2007/12/fms-a-quick-fix-for-missing-onbwdone-onfcsubscribe-etc.html + this.nc.client = this; + + // TODO: security/IO error handling + // this.nc.addEventListener(SecurityErrorEvent.SECURITY_ERROR, doSecurityError); + nc.addEventListener(NetStatusEvent.NET_STATUS, netStatusHandler); + + if (this.serverUrl != null) { + writeDebug('SoundManager2_SMSound_AS3: NetConnection: connecting to server ' + this.serverUrl + '...'); + } + this.nc.connect(serverUrl); + } else { + this.connect_time = this.start_time; + this.connected = true; + } + + } + + public function netStatusHandler(event:NetStatusEvent):void { + + if (this.useEvents) { + writeDebug('netStatusHandler: '+event.info.code); + } + + switch (event.info.code) { + + case "NetConnection.Connect.Success": + writeDebug('NetConnection: connected'); + try { + this.ns = new NetStream(this.nc); + this.ns.checkPolicyFile = this.checkPolicyFile; + // bufferTime reference: http://livedocs.adobe.com/flash/9.0/ActionScriptLangRefV3/flash/net/NetStream.html#bufferTime + this.ns.bufferTime = getStartBuffer(); // set to 0.1 or higher. 0 is reported to cause playback issues with static files. + this.st = new SoundTransform(); + this.cc.onMetaData = this.metaDataHandler; + this.ns.client = this.cc; + this.ns.receiveAudio(true); + this.addNetstreamEvents(); + + this.connected = true; + if (recordStats) { + this.recordConnectTime(); + } + if (this.useEvents) { + writeDebug('firing _onconnect for '+this.sID); + ExternalInterface.call(this.sm.baseJSObject + "['" + this.sID + "']._onconnect", 1); + } + } catch(e: Error) { + this.failed = true; + writeDebug('netStream error: ' + e.toString()); + ExternalInterface.call(baseJSObject + "['" + this.sID + "']._onfailure", 'Connection failed!', event.info.level, event.info.code); + } + break; + + case "NetStream.Play.StreamNotFound": + this.failed = true; + writeDebug("NetConnection: Stream not found!"); + ExternalInterface.call(baseJSObject + "['" + this.sID + "']._onfailure", 'Stream not found!', event.info.level, event.info.code); + break; + + // This is triggered when the sound loses the connection with the server. + // In some cases one could just try to reconnect to the server and resume playback. + // However for streams protected by expiring tokens, I don't think that will work. + // + // Flash says that this is not an error code, but a status code... + // should this call the onFailure handler? + case "NetConnection.Connect.Closed": + this.failed = true; + ExternalInterface.call(baseJSObject + "['" + this.sID + "']._onfailure", 'Connection closed!', event.info.level, event.info.code); + writeDebug("NetConnection: Connection closed!"); + break; + + // Couldn't establish a connection with the server. Attempts to connect to the server + // can also fail if the permissible number of socket connections on either the client + // or the server computer is at its limit. This also happens when the internet + // connection is lost. + case "NetConnection.Connect.Failed": + this.failed = true; + writeDebug("NetConnection: Connection failed! Lost internet connection? Try again... Description: " + event.info.description); + ExternalInterface.call(baseJSObject + "['" + this.sID + "']._onfailure", 'Connection failed!', event.info.level, event.info.code); + break; + + // A change has occurred to the network status. This could mean that the network + // connection is back, or it could mean that it has been lost...just try to resume + // playback. + + // KJV: Can't use this yet because by the time you get your connection back the + // song has reached it's maximum retries, so it doesn't retry again. We need + // a new _ondisconnect handler. + //case "NetConnection.Connect.NetworkChange": + // this.failed = true; + // writeDebug("NetConnection: Network connection status changed"); + // ExternalInterface.call(baseJSObject + "['" + this.sID + "']._onfailure", 'Reconnecting...'); + // break; + + // Consider everything else a failure... + default: + this.failed = true; + writeDebug("NetConnection: got unhandled code '" + event.info.code + "'! Description: " + event.info.description); + ExternalInterface.call(baseJSObject + "['" + this.sID + "']._onfailure", '', event.info.level, event.info.code); + break; + } + + } + + // Set the buffer size on the current NetSream instance to buffer secs + // Only set the buffer if it's different to the current buffer. + public function setBuffer(buffer: int) : void { + if (buffer != this.ns.bufferTime) { + this.ns.bufferTime = buffer; + writeDebug('set buffer to '+this.ns.bufferTime+' secs'); + } + } + + // Return the size of the starting buffer. + public function getStartBuffer() : int { + return this.bufferTimes[0]; + } + + // Return the size of the next buffer, given the size of the current buffer. + // If there are no more buffers, returns the current buffer size. + public function getNextBuffer(current_buffer: int) : int { + var i: int = bufferTimes.indexOf(current_buffer); + if (i == -1) { + // Couldn't find the buffer, start from the start buffer size + return getStartBuffer(); + } else if (i + 1 >= bufferTimes.length) { + // Last (or only) buffer, keep the current buffer + return current_buffer; + } else { + return this.bufferTimes[i+1]; + } + } + + public function writeDebug (s: String, bTimestamp: Boolean = false) : Boolean { + return this.sm.writeDebug (s, bTimestamp); // defined in main SM object + } + + public function metaDataHandler(infoObject: Object) : void { + /* + var data:String = new String(); + for (var prop:* in infoObject) { + data += prop+': '+infoObject[prop]+' '; + } + ExternalInterface.call('soundManager._writeDebug','Metadata: '+data); + */ + if (!this.loaded) { + // writeDebug('not loaded yet: '+this.ns.bytesLoaded+', '+this.ns.bytesTotal+', '+infoObject.duration*1000); + // TODO: investigate loaded/total values + // ExternalInterface.call(baseJSObject + "['" + this.sID + "']._whileloading", this.ns.bytesLoaded, this.ns.bytesTotal, infoObject.duration*1000); + ExternalInterface.call(baseJSObject + "['" + this.sID + "']._whileloading", this.bytesLoaded, this.bytesTotal, (infoObject.duration || this.duration)) + } + this.duration = infoObject.duration * 1000; + // null this out for the duration of this object's existence. + // it may be called multiple times. + this.cc.onMetaData = function(infoObject: Object) : void {} + + } + + public function getWaveformData() : void { + // http://livedocs.adobe.com/flash/9.0/ActionScriptLangRefV3/flash/media/SoundMixer.html#computeSpectrum() + SoundMixer.computeSpectrum(this.waveformData, false, 0); // sample wave data at 44.1 KHz + this.waveformDataArray = []; + for (var i: int = 0, j: int = this.waveformData.length / 4; i < j; i++) { // get all 512 values (256 per channel) + this.waveformDataArray.push(int(this.waveformData.readFloat() * 1000) / 1000); + } + } + + public function getEQData() : void { + // http://livedocs.adobe.com/flash/9.0/ActionScriptLangRefV3/flash/media/SoundMixer.html#computeSpectrum() + SoundMixer.computeSpectrum(this.eqData, true, 0); // sample EQ data at 44.1 KHz + this.eqDataArray = []; + for (var i: int = 0, j: int = this.eqData.length / 4; i < j; i++) { // get all 512 values (256 per channel) + this.eqDataArray.push(int(this.eqData.readFloat() * 1000) / 1000); + } + } + + public function start(nMsecOffset: int, nLoops: int) : void { + this.useEvents = true; + if (this.useNetstream) { + + writeDebug("SMSound::start nMsecOffset "+ nMsecOffset+ ' nLoops '+nLoops + ' current bufferTime '+this.ns.bufferTime+' current bufferLength '+this.ns.bufferLength+ ' this.lastValues.position '+this.lastValues.position); + + this.cc.onMetaData = this.metaDataHandler; + + // Don't seek if we don't have to because it destroys the buffer + var set_position:Boolean = this.lastValues.position != null && this.lastValues.position != nMsecOffset; + if (set_position) { + // Minimize the buffer so playback starts ASAP + this.setBuffer(this.getStartBuffer()); + } + + if (this.paused) { + writeDebug('start: resuming from paused state'); + this.ns.resume(); // get the sound going again + if (!this.didLoad) { + this.didLoad = true; + } + this.paused = false; + } else if (!this.didLoad) { + writeDebug('start: !didLoad - playing '+this.sURL); + this.ns.play(this.sURL); + this.didLoad = true; + this.paused = false; + } else { + // previously loaded, perhaps stopped/finished. play again? + writeDebug('playing again (not paused, didLoad = true)'); + this.ns.play(this.sURL); + } + + // KJV seek after calling play otherwise some streams get a NetStream.Seek.Failed + // Should only apply to the !didLoad case, but do it for all for simplicity. + // nMsecOffset is in milliseconds for streams but in seconds for progressive + // download. + if (set_position) { + this.ns.seek(this.serverUrl ? nMsecOffset / 1000 : nMsecOffset); + this.lastValues.position = nMsecOffset; // https://gist.github.com/1de8a3113cf33d0cff67 + } + + // this.ns.addEventListener(Event.SOUND_COMPLETE, _onfinish); + this.applyTransform(); + + } else { + // writeDebug('start: seeking to '+nMsecOffset+', '+nLoops+(nLoops==1?' loop':' loops')); + this.soundChannel = this.play(nMsecOffset, nLoops); + this.addEventListener(Event.SOUND_COMPLETE, _onfinish); + this.applyTransform(); + } + + } + + private function _onfinish() : void { + this.removeEventListener(Event.SOUND_COMPLETE, _onfinish); + } + + public function loadSound(sURL: String, bStream: Boolean) : void { + if (this.useNetstream) { + this.useEvents = true; + if (this.didLoad != true) { + ExternalInterface.call('loadSound(): loading ' + this.sURL); + this.ns.play(this.sURL); + this.didLoad = true; + } + // this.addEventListener(Event.SOUND_COMPLETE, _onfinish); + this.applyTransform(); + } else { + try { + this.didLoad = true; + this.urlRequest = new URLRequest(sURL); + this.soundLoaderContext = new SoundLoaderContext(1000, this.checkPolicyFile); // check for policy (crossdomain.xml) file on remote domains - http://livedocs.adobe.com/flash/9.0/ActionScriptLangRefV3/flash/media/SoundLoaderContext.html + this.load(this.urlRequest, this.soundLoaderContext); + } catch(e: Error) { + writeDebug('error during loadSound(): ' + e.toString()); + } + } + } + + public function setAutoPlay(autoPlay: Boolean) : void { + if (!this.serverUrl) { + // don't apply to non-RTMP, netstream stuff. + this.autoPlay = true; + this.pauseOnBufferFull = false; + } else { + this.autoPlay = autoPlay; + if (this.autoPlay) { + this.pauseOnBufferFull = false; + // writeDebug('ignoring pauseOnBufferFull because autoPlay is on'); + } else if (!this.autoPlay) { + this.pauseOnBufferFull = true; + // writeDebug('pausing on buffer full because autoPlay is off'); + } + } + } + + public function setVolume(nVolume: Number) : void { + this.lastValues.volume = nVolume / 100; + this.applyTransform(); + } + + public function setPan(nPan: Number) : void { + this.lastValues.pan = nPan / 100; + this.applyTransform(); + } + + public function applyTransform() : void { + var st: SoundTransform = new SoundTransform(this.lastValues.volume, this.lastValues.pan); + if (this.useNetstream) { + if (this.ns) { + this.ns.soundTransform = st; + } else { + // writeDebug('applyTransform(): Note: No active netStream'); + } + } else if (this.soundChannel) { + this.soundChannel.soundTransform = st; // new SoundTransform(this.lastValues.volume, this.lastValues.pan); + } + } + + public function recordPlayTime() : void { + this.play_time = Math.round(getTimer() - (this.start_time + this.connect_time)); + writeDebug('Play took '+ this.play_time + ' ms'); + // We must now have both stats, call the onstats callback + ExternalInterface.call(baseJSObject + "['" + this.sID + "']._onstats", { + play_time: this.play_time, + connect_time: this.connect_time + }); + // Stop tracking any stats for this object + this.recordStats = false; + } + + public function recordConnectTime() : void { + this.connect_time = Math.round(getTimer() - this.start_time); + writeDebug('Connect took '+ this.connect_time + ' ms'); + } + + // Handle FMS bandwidth check callback. + // @see http://www.adobe.com/devnet/flashmediaserver/articles/dynamic_stream_switching_04.html + // @see http://www.johncblandii.com/index.php/2007/12/fms-a-quick-fix-for-missing-onbwdone-onfcsubscribe-etc.html + public function onBWDone() : void { + // writeDebug('onBWDone: called and ignored'); + } + + // NetStream client callback. Invoked when the song is complete. + public function onPlayStatus(info:Object):void { + writeDebug('onPlayStatus called with '+info); + switch(info.code) { + case "NetStream.Play.Complete": + writeDebug('Song has finished!'); + break; + } + } + + public function doIOError(e: IOErrorEvent) : void { + ExternalInterface.call(baseJSObject + "['" + this.sID + "']._onload", 0); // call onload, assume it failed. + // there was a connection drop, a loss of internet connection, or something else wrong. 404 error too. + } + + public function doAsyncError(e: AsyncErrorEvent) : void { + writeDebug('asyncError: ' + e.text); + } + + public function doNetStatus(e: NetStatusEvent) : void { + + // Handle failures + if (e.info.code == "NetStream.Failed" + || e.info.code == "NetStream.Play.FileStructureInvalid" + || e.info.code == "NetStream.Play.StreamNotFound") { + + this.lastNetStatus = e.info.code; + writeDebug('netStatusEvent: ' + e.info.code); + this.failed = true; + ExternalInterface.call(baseJSObject + "['" + this.sID + "']._onfailure", '', e.info.level, e.info.code); + return; + } + + writeDebug('netStatusEvent: ' + e.info.code); // KJV we like to see all events + + // When streaming, Stop is called when buffering stops, not when the stream is actually finished. + // @see http://www.actionscript.org/forums/archive/index.php3/t-159194.html + if (e.info.code == "NetStream.Play.Stop") { + + if (!this.useNetstream) { + // finished playing + // this.didFinish = true; // will be reset via JS callback + this.didJustBeforeFinish = false; // reset + writeDebug('calling onfinish for a sound'); + // reset the sound? Move back to position 0? + this.sm.checkProgress(); + ExternalInterface.call(baseJSObject + "['" + this.sID + "']._onfinish"); + } + + } else if (e.info.code == "NetStream.Play.Start" || e.info.code == "NetStream.Buffer.Empty" || e.info.code == "NetStream.Buffer.Full") { + + // First time buffer has filled. Print debugging output. + if (this.recordStats && !this.play_time) { + this.recordPlayTime(); + } + + // RTMP case.. + // We wait for the buffer to fill up before pausing the just-loaded song because only if the + // buffer is full will the song continue to buffer until the user hits play. + if (this.serverUrl && e.info.code == "NetStream.Buffer.Full" && this.pauseOnBufferFull) { + this.ns.pause(); + this.paused = true; + this.pauseOnBufferFull = false; + // Call pause in JS. This will call back to us to pause again, but + // that should be harmless. + writeDebug('Pausing song because buffer is full'); + ExternalInterface.call(baseJSObject + "['" + this.sID + "'].pause", false); + } + + // The buffer is full. Increase its size if possible. + // Double buffering has not been shown to cause false starts, so this is safe. + if (e.info.code == "NetStream.Buffer.Full") { + var next_buffer: int = this.getNextBuffer(this.ns.bufferTime); + if (next_buffer != this.ns.bufferTime) { + this.setBuffer(next_buffer); + } + } + + var isNetstreamBuffering: Boolean = (e.info.code == "NetStream.Buffer.Empty" || e.info.code == "NetStream.Play.Start"); + // assume buffering when we start playing, eg. initial load. + if (isNetstreamBuffering != this.lastValues.isBuffering) { + this.lastValues.isBuffering = isNetstreamBuffering; + ExternalInterface.call(baseJSObject + "['" + this.sID + "']._onbufferchange", this.lastValues.isBuffering ? 1 : 0); + } + + // We can detect the end of the stream when Play.Stop is called followed by Buffer.Empty. + // However, if you pause and let the whole song buffer, Buffer.Flush is called followed by + // Buffer.Empty, so handle that case too. + // + // Ignore this event if we are more than 5 seconds from the end of the song. + if (e.info.code == "NetStream.Buffer.Empty" && (this.lastNetStatus == 'NetStream.Play.Stop' || this.lastNetStatus == 'NetStream.Buffer.Flush')) { + if (this.duration && (this.ns.time * 1000) < (this.duration - 5000)) { + writeDebug('Ignoring Buffer.Empty because this is too early to be the end of the stream! (sID: '+this.sID+', time: '+(this.ns.time*1000)+', duration: '+this.duration+')'); + } else { + this.didJustBeforeFinish = false; // reset + this.finished = true; + writeDebug('calling onfinish for sound '+this.sID); + this.sm.checkProgress(); + ExternalInterface.call(baseJSObject + "['" + this.sID + "']._onfinish"); + } + + } else if (e.info.code == "NetStream.Buffer.Empty") { + + // The buffer is empty. Start from the smallest buffer again. + this.setBuffer(this.getStartBuffer()); + } + } + + // Remember the last NetStatus event + this.lastNetStatus = e.info.code; + } + + // KJV The sound adds some of its own netstatus handlers so we don't need to do it here. + public function addNetstreamEvents() : void { + ns.addEventListener(AsyncErrorEvent.ASYNC_ERROR, doAsyncError); + ns.addEventListener(NetStatusEvent.NET_STATUS, doNetStatus); + ns.addEventListener(IOErrorEvent.IO_ERROR, doIOError); + } + + public function removeNetstreamEvents() : void { + ns.removeEventListener(AsyncErrorEvent.ASYNC_ERROR, doAsyncError); + ns.removeEventListener(NetStatusEvent.NET_STATUS, doNetStatus); + ns.addEventListener(NetStatusEvent.NET_STATUS, dummyNetStatusHandler); + ns.removeEventListener(IOErrorEvent.IO_ERROR, doIOError); + // KJV Stop listening for NetConnection events on the sound + nc.removeEventListener(NetStatusEvent.NET_STATUS, netStatusHandler); + } + + // Prevents possible 'Unhandled NetStatusEvent' condition if a sound is being + // destroyed and interacted with at the same time. + public function dummyNetStatusHandler(e: NetStatusEvent) : void { + // Don't do anything + } + } +} \ No newline at end of file diff --git a/docs/dymaxion/soundmanagerv297a-20101010/src/make-flash8.bat b/docs/dymaxion/soundmanagerv297a-20101010/src/make-flash8.bat new file mode 100755 index 0000000..fcab423 --- /dev/null +++ b/docs/dymaxion/soundmanagerv297a-20101010/src/make-flash8.bat @@ -0,0 +1,5 @@ +@REM this builds the soundmanager 2 SWF from source + +mtasc -swf ../swf/soundmanager2_debug.swf -main -header 16:16:30 SoundManager2.as -version 8 +@pause + diff --git a/docs/dymaxion/soundmanagerv297a-20101010/src/make-flash8.sh b/docs/dymaxion/soundmanagerv297a-20101010/src/make-flash8.sh new file mode 100755 index 0000000..8857d15 --- /dev/null +++ b/docs/dymaxion/soundmanagerv297a-20101010/src/make-flash8.sh @@ -0,0 +1,2 @@ +#!/bin/bash +/Applications/mtasc/mtasc -swf ../swf/soundmanager2_debug.swf -main -header 16:16:30 SoundManager2.as -version 8 diff --git a/docs/dymaxion/soundmanagerv297a-20101010/src/make-flash9.bat b/docs/dymaxion/soundmanagerv297a-20101010/src/make-flash9.bat new file mode 100755 index 0000000..ed63934 --- /dev/null +++ b/docs/dymaxion/soundmanagerv297a-20101010/src/make-flash9.bat @@ -0,0 +1,4 @@ +@REM this builds the soundmanager 2 SWF from source +@REM using mxmlc from the Adobe open-source Flex SDK + +c:\progra~1\flexsdk\bin\mxmlc -debug=true -use-network=false -static-link-runtime-shared-libraries=true -optimize=true -o ../swf/soundmanager2_flash9_debug.swf -file-specs SoundManager2_AS3.as diff --git a/docs/dymaxion/soundmanagerv297a-20101010/src/make-flash9.sh b/docs/dymaxion/soundmanagerv297a-20101010/src/make-flash9.sh new file mode 100755 index 0000000..ec6041c --- /dev/null +++ b/docs/dymaxion/soundmanagerv297a-20101010/src/make-flash9.sh @@ -0,0 +1,2 @@ +#!/bin/bash +/Applications/flexsdk/bin/mxmlc -debug=true -static-link-runtime-shared-libraries=true -optimize=true -o ../swf/soundmanager2_flash9_debug.swf -file-specs SoundManager2_AS3.as -- cgit v1.2.3-70-g09d2