summaryrefslogtreecommitdiff
path: root/docs/dymaxion/soundmanagerv297a-20101010/src
diff options
context:
space:
mode:
Diffstat (limited to 'docs/dymaxion/soundmanagerv297a-20101010/src')
-rwxr-xr-xdocs/dymaxion/soundmanagerv297a-20101010/src/SoundManager2.as452
-rwxr-xr-xdocs/dymaxion/soundmanagerv297a-20101010/src/SoundManager2_AS3.as970
-rwxr-xr-xdocs/dymaxion/soundmanagerv297a-20101010/src/SoundManager2_SMSound_AS3.as597
-rwxr-xr-xdocs/dymaxion/soundmanagerv297a-20101010/src/make-flash8.bat5
-rwxr-xr-xdocs/dymaxion/soundmanagerv297a-20101010/src/make-flash8.sh2
-rwxr-xr-xdocs/dymaxion/soundmanagerv297a-20101010/src/make-flash9.bat4
-rwxr-xr-xdocs/dymaxion/soundmanagerv297a-20101010/src/make-flash9.sh2
7 files changed, 2032 insertions, 0 deletions
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) {
+ // <d>
+ if (!debugEnabled) return false;
+ ExternalInterface.call(baseJSController + "['_writeDebug']", "(Flash): " + s);
+ // </d>
+ }
+
+ var flashDebug = function(messageText) {
+ // <d>
+ _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;
+ // </d>
+ }
+
+ var _externalInterfaceTest = function(isFirstCall) {
+ var sandboxType = System.security['sandboxType'];
+ try {
+ if (isFirstCall) {
+ flashDebug('Testing Flash -&gt; JS...')
+ if (!didSandboxMessage && sandboxType != 'remote' && sandboxType != 'localTrusted') {
+ didSandboxMessage = true;
+ flashDebug('<br><b>Fatal: Security sandbox error: Got "' + sandboxType + '", expected "remote" or "localTrusted".<br>Additional security permissions need to be granted.<br>See <a href="http://www.macromedia.com/support/documentation/en/flashplayer/help/settings_manager04.html">flash security settings panel</a> for non-HTTP, eg. file:// use.</b><br>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 -&gt; JS OK');
+ }
+ } else {
+ writeDebug('SM2 SWF ' + version + ' ' + version_as);
+ flashDebug('JS -&gt; 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 &lt;-&gt; 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;
+
+ // <d>
+ 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);
+ }
+ // </d>
+
+ 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 &lt;-&gt; 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 {
+ // <d>
+ 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);
+ }
+ }
+ // </d>
+ }
+
+ 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;
+ // <d>
+ ExternalInterface.call(baseJSController + "['_writeDebug']", "(Flash): " + s, null, bTimestamp);
+ // </d>
+ return true;
+ }
+
+ public function _externalInterfaceTest(isFirstCall: Boolean) : Boolean {
+ var sandboxType:String = flash.system.Security['sandboxType'];
+ if (!didSandboxMessage && sandboxType != 'localTrusted' && sandboxType != 'remote') {
+ didSandboxMessage = true;
+ flashDebug('<br><b>Fatal: Security sandbox error: Got "' + sandboxType + '", expected "remote" or "localTrusted".<br>Additional security permissions need to be granted.<br>See <a href="http://www.macromedia.com/support/documentation/en/flashplayer/help/settings_manager04.html">flash security settings panel</a> for non-HTTP, eg., file:// use.</b><br>http://www.macromedia.com/support/documentation/en/flashplayer/help/settings_manager04.html');
+ }
+ try {
+ if (isFirstCall == true) {
+ flashDebug('Testing Flash -&gt; JS...');
+ var d: Date = new Date();
+ ExternalInterface.call(baseJSController + "._externalInterfaceOK", d.getTime());
+ flashDebug('Flash -&gt; 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 &lt;-&gt; 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('<br>\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 <tt>buffer</tt> 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