PageRenderTime 53ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 1ms

/src/SoundManager2_AS3.as

https://github.com/darkseed/SoundManager2
ActionScript | 1186 lines | 925 code | 100 blank | 161 comment | 270 complexity | d7ce93fbaf631e5ec6dab4f41b53bf01 MD5 | raw file
  1. /*
  2. SoundManager 2: Javascript Sound for the Web
  3. ----------------------------------------------
  4. http://schillmania.com/projects/soundmanager2/
  5. Copyright (c) 2007, Scott Schiller. All rights reserved.
  6. Code licensed under the BSD License:
  7. http://www.schillmania.com/projects/soundmanager2/license.txt
  8. Flash 9 / ActionScript 3 version
  9. */
  10. package {
  11. import flash.display.Sprite;
  12. import flash.display.StageAlign;
  13. import flash.display.StageDisplayState;
  14. import flash.display.StageScaleMode;
  15. import flash.events.Event;
  16. import flash.events.FullScreenEvent;
  17. import flash.events.IOErrorEvent;
  18. import flash.events.MouseEvent;
  19. import flash.events.SecurityErrorEvent;
  20. import flash.events.AsyncErrorEvent;
  21. import flash.events.NetStatusEvent;
  22. import flash.events.TimerEvent;
  23. import flash.external.ExternalInterface; // woo
  24. import flash.geom.Rectangle;
  25. import flash.media.Sound;
  26. import flash.media.SoundChannel;
  27. import flash.media.SoundMixer;
  28. import flash.net.URLLoader;
  29. import flash.net.URLRequest;
  30. import flash.system.Security;
  31. import flash.system.System;
  32. import flash.text.TextField;
  33. import flash.text.TextFormat;
  34. import flash.text.TextFieldAutoSize;
  35. import flash.ui.ContextMenu;
  36. import flash.ui.ContextMenuItem;
  37. import flash.utils.setInterval;
  38. import flash.utils.clearInterval;
  39. import flash.utils.Dictionary;
  40. import flash.utils.Timer;
  41. import flash.xml.XMLDocument;
  42. import flash.xml.XMLNode;
  43. public class SoundManager2_AS3 extends Sprite {
  44. public var version:String = "V2.96a.20100822";
  45. public var version_as:String = "(AS3/Flash 9)";
  46. /*
  47. * Cross-domain security options
  48. * HTML on foo.com loading .swf hosted on bar.com? Define your "HTML domain" here to allow JS+Flash communication to work.
  49. * // allow_xdomain_scripting = true;
  50. * // xdomain = "foo.com";
  51. * For all domains (possible security risk?), use xdomain = "*"; which ends up as System.security.allowDomain("*");
  52. * When loading from HTTPS, use System.security.allowInsecureDomain();
  53. * See http://livedocs.adobe.com/flash/9.0/ActionScriptLangRefV3/flash/system/Security.html#allowDomain()
  54. */
  55. public var allow_xdomain_scripting:Boolean = false;
  56. public var xdomain:String = "*";
  57. // externalInterface references (for Javascript callbacks)
  58. public var baseJSController:String = "soundManager";
  59. public var baseJSObject:String = baseJSController + ".sounds";
  60. // internal objects
  61. public var sounds:Array = []; // indexed string array
  62. public var soundObjects: Dictionary = new Dictionary(); // associative Sound() object Dictionary type
  63. public var timerInterval: uint = 50;
  64. public var timerIntervalHighPerformance: uint = 10; // ~30fps (in Safari on OSX, anyway..)
  65. public var timer: Timer = null;
  66. public var pollingEnabled: Boolean = false; // polling (timer) flag - disabled by default, enabled by JS->Flash call
  67. public var debugEnabled: Boolean = true; // Flash debug output enabled by default, disabled by JS call
  68. public var flashDebugEnabled: Boolean = false; // Flash internal debug output (write to visible SWF in browser)
  69. public var loaded: Boolean = false;
  70. public var isFullScreen: Boolean = false;
  71. public var currentObject: SoundManager2_SMSound_AS3 = null;
  72. public var paramList:Object = null;
  73. public var messages:Array = [];
  74. public var textField: TextField = null;
  75. public var textStyle: TextFormat = new TextFormat();
  76. public var didSandboxMessage: Boolean = false;
  77. public var caughtFatal: Boolean = false;
  78. public function SoundManager2_AS3() {
  79. if (allow_xdomain_scripting && xdomain) {
  80. Security.allowDomain(xdomain);
  81. version_as += ' - cross-domain enabled';
  82. }
  83. this.setDefaultStageScale();
  84. this.paramList = this.root.loaderInfo.parameters;
  85. // <d>
  86. if (this.paramList['debug'] == 1) {
  87. this.flashDebugEnabled = true;
  88. }
  89. if (this.flashDebugEnabled) {
  90. var canvas: Sprite = new Sprite();
  91. canvas.graphics.drawRect(0, 0, stage.stageWidth, stage.stageHeight);
  92. addChild(canvas);
  93. }
  94. // </d>
  95. flashDebug('SM2 SWF ' + version + ' ' + version_as);
  96. // context menu item with version info
  97. var sm2Menu:ContextMenu = new ContextMenu();
  98. var sm2MenuItem:ContextMenuItem = new ContextMenuItem('SoundManager ' + version + ' ' + version_as);
  99. sm2MenuItem.enabled = false;
  100. sm2Menu.customItems.push(sm2MenuItem);
  101. contextMenu = sm2Menu;
  102. if (ExternalInterface.available) {
  103. flashDebug('ExternalInterface available');
  104. try {
  105. flashDebug('Adding ExternalInterface callbacks...');
  106. ExternalInterface.addCallback('_load', _load);
  107. ExternalInterface.addCallback('_unload', _unload);
  108. ExternalInterface.addCallback('_stop', _stop);
  109. ExternalInterface.addCallback('_start', _start);
  110. ExternalInterface.addCallback('_pause', _pause);
  111. ExternalInterface.addCallback('_setPosition', _setPosition);
  112. ExternalInterface.addCallback('_setPan', _setPan);
  113. ExternalInterface.addCallback('_setVolume', _setVolume);
  114. ExternalInterface.addCallback('_setPolling', _setPolling);
  115. ExternalInterface.addCallback('_externalInterfaceTest', _externalInterfaceTest);
  116. ExternalInterface.addCallback('_disableDebug', _disableDebug);
  117. ExternalInterface.addCallback('_getMemoryUse', _getMemoryUse);
  118. ExternalInterface.addCallback('_loadFromXML', _loadFromXML);
  119. ExternalInterface.addCallback('_createSound', _createSound);
  120. ExternalInterface.addCallback('_destroySound', _destroySound);
  121. ExternalInterface.addCallback('_setAutoPlay', _setAutoPlay);
  122. } catch(e: Error) {
  123. flashDebug('Fatal: ExternalInterface error: ' + e.toString());
  124. }
  125. } else {
  126. flashDebug('Fatal: ExternalInterface (Flash &lt;-&gt; JS) not available');
  127. };
  128. // call after delay, to be safe (ensure callbacks are registered by the time JS is called below)
  129. var timer: Timer = new Timer(20, 0);
  130. timer.addEventListener(TimerEvent.TIMER, function () : void {
  131. timer.reset();
  132. _externalInterfaceTest(true);
  133. // timer.reset();
  134. // flashDebug('Init OK');
  135. });
  136. timer.start();
  137. // delayed, see above
  138. // _externalInterfaceTest(true);
  139. this.stage.addEventListener(MouseEvent.DOUBLE_CLICK, toggleFullScreen);
  140. this.stage.doubleClickEnabled = true;
  141. this.stage.addEventListener(FullScreenEvent.FULL_SCREEN, fullscreenHandler);
  142. } // SoundManager2()
  143. public function flashDebug (txt:String) : void {
  144. // <d>
  145. messages.push(txt);
  146. if (this.flashDebugEnabled) {
  147. var didCreate: Boolean = false;
  148. textStyle.font = 'Arial';
  149. textStyle.size = 12;
  150. // 320x240 if no stage dimensions (happens in IE, apparently 0 before stage resize event fires.)
  151. var w:Number = this.stage.width?this.stage.width:320;
  152. var h:Number = this.stage.height?this.stage.height:240;
  153. if (textField == null) {
  154. didCreate = true;
  155. textField = new TextField();
  156. textField.autoSize = TextFieldAutoSize.LEFT;
  157. textField.x = 0;
  158. textField.y = 0;
  159. textField.multiline = true;
  160. textField.textColor = 0;
  161. textField.wordWrap = true;
  162. }
  163. textField.htmlText = messages.join('\n');
  164. textField.setTextFormat(textStyle);
  165. textField.width = w;
  166. textField.height = h;
  167. if (didCreate) {
  168. this.addChild(textField);
  169. }
  170. }
  171. // </d>
  172. }
  173. public function _setAutoPlay(sID:String, autoPlay:Boolean) : void {
  174. var s: SoundManager2_SMSound_AS3 = soundObjects[sID];
  175. if (s) {
  176. s.setAutoPlay(autoPlay);
  177. }
  178. }
  179. public function fullscreenHandler(e: FullScreenEvent) : void {
  180. writeDebug('fullscreenHandler(): ' + e.toString());
  181. if (e.fullScreen == true) {
  182. this.isFullScreen = true;
  183. } else {
  184. // user left full-screen
  185. this.isFullScreen = false;
  186. }
  187. ExternalInterface.call(baseJSController + "['_onfullscreenchange']", e.fullScreen == true ? 1 : 0);
  188. }
  189. public function toggleFullScreen(e: MouseEvent) : void {
  190. writeDebug('SoundManager2_AS3.toggleFullScreen()');
  191. if (this.currentObject && this.currentObject.useVideo) {
  192. if (this.currentObject.videoWidth == 0) {
  193. writeDebug('toggleFullScreen(): video width is 0 (metadata missing/not loaded yet?) Trying stage width/height');
  194. this.currentObject.videoWidth = this.stage.width;
  195. this.currentObject.videoHeight = this.stage.height;
  196. }
  197. try {
  198. stage.scaleMode = StageScaleMode.NO_SCALE;
  199. stage.align = StageAlign.TOP_LEFT;
  200. stage.fullScreenSourceRect = new Rectangle(0, 0, this.currentObject.videoWidth, this.currentObject.videoHeight);
  201. stage.displayState = StageDisplayState.FULL_SCREEN;
  202. } catch(e: Error) {
  203. // write debug message?
  204. writeDebug('Unable to switch to full-screen. ' + e.toString());
  205. }
  206. } else {
  207. writeDebug('toggleFullScreen(): No active video to show?');
  208. }
  209. }
  210. public function setDefaultStageScale() : void {
  211. stage.scaleMode = StageScaleMode.NO_SCALE;
  212. stage.align = StageAlign.TOP_LEFT;
  213. }
  214. // methods
  215. // -----------------------------------
  216. public function _exitFullScreen() : void {
  217. try {
  218. stage.displayState = StageDisplayState.NORMAL;
  219. this.setDefaultStageScale();
  220. this.isFullScreen = false;
  221. ExternalInterface.call(baseJSController + "._onfullscreenchange", 0);
  222. } catch(e: Error) {
  223. // oh well
  224. writeDebug('exitFullScreen error: ' + e.toString());
  225. }
  226. }
  227. public function writeDebug (s:String, bTimestamp: Boolean = false) : Boolean {
  228. if (!debugEnabled) return false;
  229. // <d>
  230. ExternalInterface.call(baseJSController + "['_writeDebug']", "(Flash): " + s, null, bTimestamp);
  231. // </d>
  232. return true;
  233. }
  234. public function _externalInterfaceTest(isFirstCall: Boolean) : Boolean {
  235. var sandboxType:String = flash.system.Security['sandboxType'];
  236. if (!didSandboxMessage && sandboxType != 'localTrusted' && sandboxType != 'remote') {
  237. didSandboxMessage = true;
  238. 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');
  239. }
  240. try {
  241. if (isFirstCall == true) {
  242. flashDebug('Testing Flash -&gt; JS...');
  243. var d: Date = new Date();
  244. ExternalInterface.call(baseJSController + "._externalInterfaceOK", d.getTime());
  245. flashDebug('Flash -&gt; JS OK');
  246. } else {
  247. writeDebug('SM2 SWF ' + version + ' ' + version_as);
  248. flashDebug('JS -> Flash OK');
  249. ExternalInterface.call(baseJSController + "._setSandboxType", sandboxType);
  250. writeDebug('JS to/from Flash OK');
  251. }
  252. } catch(e: Error) {
  253. flashDebug('Fatal: Flash &lt;-&gt; JS error: ' + e.toString());
  254. writeDebug('_externalInterfaceTest: Error: ' + e.toString());
  255. if (!caughtFatal) {
  256. caughtFatal = true;
  257. }
  258. return false;
  259. }
  260. return true; // to verify that a call from JS to here, works. (eg. JS receives "true", thus OK.)
  261. }
  262. public function _disableDebug() : void {
  263. // prevent future debug calls from Flash going to client (maybe improve performance)
  264. writeDebug('_disableDebug()');
  265. debugEnabled = false;
  266. }
  267. public function checkLoadProgress(e: Event) : void {
  268. try {
  269. var oSound:Object = e.target;
  270. var bL: int = oSound.bytesLoaded;
  271. var bT: int = oSound.bytesTotal || oSound.totalBytes;
  272. var nD: int = oSound.length || oSound.duration || 0;
  273. var sMethod:String = baseJSObject + "['" + oSound.sID + "']._whileloading";
  274. ExternalInterface.call(sMethod, bL, bT, nD);
  275. if (bL && bT && bL != oSound.lastValues.bytes) {
  276. oSound.lastValues.bytes = bL;
  277. ExternalInterface.call(sMethod, bL, bT, nD);
  278. }
  279. } catch(e: Error) {
  280. writeDebug('checkLoadProgress(): ' + e.toString());
  281. }
  282. }
  283. public function checkProgress() : void {
  284. var bL: int = 0;
  285. var bT: int = 0;
  286. var nD: int = 0;
  287. var nP: int = 0;
  288. var lP:Number = 0;
  289. var rP:Number = 0;
  290. var isBuffering:Object = null;
  291. var oSound: SoundManager2_SMSound_AS3 = null;
  292. var oSoundChannel: flash.media.SoundChannel = null;
  293. var sMethod:String = null;
  294. var newPeakData: Boolean = false;
  295. var newWaveformData: Boolean = false;
  296. var newEQData: Boolean = false;
  297. var areSoundsInaccessible: Boolean = SoundMixer.areSoundsInaccessible();
  298. var isPlaying: Boolean = true; // special case for NetStream when ending
  299. for (var i: int = 0, j: int = sounds.length; i < j; i++) {
  300. oSound = soundObjects[sounds[i]];
  301. sMethod = baseJSObject + "['" + sounds[i] + "']._whileloading";
  302. if (!oSound || !oSound.useEvents || oSound.failed || !oSound.connected) {
  303. // various cases for ignoring
  304. continue; // if sounds are destructed within event handlers while this loop is running, may be null
  305. }
  306. var hasNew:Boolean = false;
  307. var hasNewLoaded:Boolean = false;
  308. if (oSound.useNetstream) {
  309. // video stream
  310. bL = oSound.ns.bytesLoaded;
  311. bT = oSound.ns.bytesTotal || oSound.totalBytes;
  312. nD = int(oSound.duration || 0); // can sometimes be null with short MP3s? Wack.
  313. nP = oSound.ns.time * 1000;
  314. if (nP != oSound.lastValues.position) {
  315. oSound.lastValues.position = nP;
  316. hasNew = true;
  317. }
  318. if (nD > oSound.lastValues.duration) {
  319. oSound.lastValues.duration = nD;
  320. hasNew = true;
  321. }
  322. if (bL > oSound.lastValues.bytesLoaded) {
  323. oSound.lastValues.bytesLoaded = bL;
  324. hasNew = true;
  325. }
  326. if (bT > oSound.lastValues.bytes) {
  327. oSound.lastValues.bytes = bT;
  328. hasNew = true;
  329. }
  330. if (oSound.loaded != true && nD > 0 && bL == bT) {
  331. // non-MP3 has loaded
  332. // writeDebug('ns: time/duration, bytesloaded/total: '+nP+'/'+nD+', '+bL+'/'+bT);
  333. oSound.loaded = true;
  334. try {
  335. ExternalInterface.call(sMethod, bL, bT, nD); // _whileloading()
  336. ExternalInterface.call(baseJSObject + "['" + oSound.sID + "']._onload", oSound.duration > 0 ? 1 : 0);
  337. } catch(e: Error) {
  338. writeDebug('_whileLoading/_onload error: ' + e.toString());
  339. }
  340. } else if (oSound.loaded != true && hasNew) {
  341. // writeDebug('whileloading() loaded/total/duration: '+bL+', '+bT+', '+nD);
  342. ExternalInterface.call(sMethod, bL, bT, nD); // _whileloading()
  343. } else if (!oSound.loaded && bL == 0 && bT && oSound.ns.bufferLength != oSound.lastValues.bufferLength) {
  344. // TODO: Verify if this merge is correct with above logic.
  345. // KJV For our RTMP streams bytesLoaded is always 0!
  346. // writeDebug('updating position with bufferLength ' + oSound.ns.bufferLength);
  347. oSound.lastValues.bufferLength = oSound.ns.bufferLength;
  348. ExternalInterface.call(sMethod, bL, bT, nD, oSound.ns.bufferLength);
  349. }
  350. } else {
  351. // MP3 sound
  352. oSoundChannel = oSound.soundChannel;
  353. bL = oSound.bytesLoaded;
  354. bT = oSound.bytesTotal;
  355. nD = int(oSound.length || 0); // can sometimes be null with short MP3s? Wack.
  356. isBuffering = oSound.isBuffering;
  357. if (oSoundChannel) {
  358. nP = (oSoundChannel.position || 0);
  359. if (oSound.lastValues.loops > 1 && nP > oSound.length) {
  360. // round down to nearest loop
  361. var playedLoops:Number = Math.floor(nP/oSound.length);
  362. nP = nP - (oSound.length*playedLoops);
  363. }
  364. if (oSound.usePeakData) {
  365. lP = int((oSoundChannel.leftPeak) * 1000) / 1000;
  366. rP = int((oSoundChannel.rightPeak) * 1000) / 1000;
  367. } else {
  368. lP = 0;
  369. rP = 0;
  370. }
  371. } else {
  372. // stopped, not loaded or feature not used
  373. nP = 0;
  374. }
  375. if (nP != oSound.lastValues.position) {
  376. oSound.lastValues.position = nP;
  377. hasNew = true;
  378. }
  379. if (nD > oSound.lastValues.duration) { // original sound duration * number of sound loops
  380. oSound.lastValues.duration = nD;
  381. hasNew = true;
  382. }
  383. if (bL > oSound.lastValues.bytesLoaded) {
  384. oSound.lastValues.bytesLoaded = bL;
  385. hasNew = true;
  386. }
  387. if (bT > oSound.lastValues.bytes) {
  388. oSound.lastValues.bytes = bT;
  389. hasNew = true;
  390. hasNewLoaded = true;
  391. }
  392. // loading progress
  393. if (hasNewLoaded) {
  394. oSound.lastValues.bytes = bL;
  395. ExternalInterface.call(sMethod, bL, bT, nD);
  396. }
  397. }
  398. // peak data
  399. if (oSoundChannel && oSound.usePeakData) {
  400. if (lP != oSound.lastValues.leftPeak) {
  401. oSound.lastValues.leftPeak = lP;
  402. newPeakData = true;
  403. }
  404. if (rP != oSound.lastValues.rightPeak) {
  405. oSound.lastValues.rightPeak = rP;
  406. newPeakData = true;
  407. }
  408. }
  409. var newDataError:Boolean = false;
  410. var dataErrors:Array = [];
  411. // raw waveform + EQ spectrum data
  412. if (oSoundChannel || oSound.useNetstream) {
  413. if (oSound.useWaveformData) {
  414. if (areSoundsInaccessible == false) {
  415. try {
  416. oSound.getWaveformData();
  417. } catch(e: Error) {
  418. // this shouldn't happen, but does seem to fire from time to time.
  419. writeDebug('getWaveformData() warning: ' + e.toString());
  420. }
  421. } else if (oSound.handledDataError != true && oSound.ignoreDataError != true) {
  422. try {
  423. oSound.getWaveformData();
  424. } catch(e: Error) {
  425. writeDebug('getWaveformData() (waveform data) '+e.toString());
  426. // oSound.useWaveformData = false;
  427. newDataError = true;
  428. dataErrors.push(e.toString());
  429. oSound.handledDataError = true;
  430. }
  431. }
  432. }
  433. if (oSound.useEQData) {
  434. if (areSoundsInaccessible == false) {
  435. try {
  436. oSound.getEQData();
  437. } catch(e: Error) {
  438. writeDebug('getEQData() warning: ' + e.toString());
  439. newDataError = true;
  440. dataErrors.push(e.toString());
  441. oSound.handledDataError = true;
  442. }
  443. } else if (oSound.handledDataError != true && oSound.ignoreDataError != true) {
  444. try {
  445. oSound.getEQData();
  446. } catch(e: Error) {
  447. // writeDebug('computeSpectrum() (EQ data) '+e.toString());
  448. // oSound.useEQData = false;
  449. newDataError = true;
  450. dataErrors.push(e.toString());
  451. oSound.handledDataError = true;
  452. }
  453. }
  454. }
  455. if (oSound.waveformDataArray != oSound.lastValues.waveformDataArray) {
  456. oSound.lastValues.waveformDataArray = oSound.waveformDataArray;
  457. newWaveformData = true;
  458. }
  459. if (oSound.eqDataArray != oSound.lastValues.eqDataArray) {
  460. oSound.lastValues.eqDataArray = oSound.eqDataArray;
  461. newEQData = true;
  462. }
  463. }
  464. if (newDataError) {
  465. sMethod = baseJSObject + "['" + sounds[i] + "']._ondataerror";
  466. var errors:String = dataErrors.join('<br>\n');
  467. ExternalInterface.call(sMethod, 'data unavailable: ' + errors);
  468. }
  469. // special case: Netstream may try to fire whileplaying() after finishing. check that stop hasn't fired.
  470. isPlaying = (!oSound.useNetstream || (oSound.useNetstream && oSound.lastNetStatus != "NetStream.Play.Stop")); // don't update if stream has ended
  471. if (typeof nP != 'undefined' && hasNew && isPlaying) { // and IF VIDEO, is still playing?
  472. // oSound.lastValues.position = nP;
  473. sMethod = baseJSObject + "['" + sounds[i] + "']._whileplaying";
  474. var waveDataLeft:String = (newWaveformData ? oSound.waveformDataArray.slice(0, 256).join(',') : null);
  475. var waveDataRight:String = (newWaveformData ? oSound.waveformDataArray.slice(256).join(',') : null);
  476. var eqDataLeft:String = (newEQData ? oSound.eqDataArray.slice(0, 256).join(',') : null);
  477. var eqDataRight:String = (newEQData ? oSound.eqDataArray.slice(256).join(',') : null);
  478. ExternalInterface.call(sMethod, nP, (newPeakData ? {
  479. leftPeak: lP,
  480. rightPeak: rP
  481. } : null), waveDataLeft, waveDataRight, (newEQData ? {
  482. leftEQ: eqDataLeft,
  483. rightEQ: eqDataRight
  484. } : null));
  485. // if position changed, check for near-end
  486. if (oSound.didJustBeforeFinish != true && oSound.loaded == true && oSound.justBeforeFinishOffset > 0 && nD - nP <= oSound.justBeforeFinishOffset) {
  487. // fully-loaded, near end and haven't done this yet..
  488. sMethod = baseJSObject + "['" + sounds[i] + "']._onjustbeforefinish";
  489. ExternalInterface.call(sMethod, (nD - nP));
  490. oSound.didJustBeforeFinish = true;
  491. }
  492. }
  493. // check isBuffering
  494. if (!oSound.useNetstream && oSound.isBuffering != oSound.lastValues.isBuffering) {
  495. // property has changed
  496. oSound.lastValues.isBuffering = oSound.isBuffering;
  497. sMethod = baseJSObject + "['" + sounds[i] + "']._onbufferchange";
  498. ExternalInterface.call(sMethod, oSound.isBuffering ? 1 : 0);
  499. }
  500. }
  501. }
  502. public function onLoadError(oSound:Object) : void {
  503. // something went wrong. 404, bad format etc.
  504. ExternalInterface.call(baseJSObject + "['" + oSound.sID + "']._onload", 0);
  505. }
  506. public function onLoad(e: Event) : void {
  507. checkProgress(); // ensure progress stats are up-to-date
  508. var oSound:Object = e.target;
  509. if (!oSound.useNetstream) { // FLV must also have metadata
  510. oSound.loaded = true;
  511. // force duration update (doesn't seem to be always accurate)
  512. ExternalInterface.call(baseJSObject + "['" + oSound.sID + "']._whileloading", oSound.bytesLoaded, oSound.bytesTotal, oSound.length || oSound.duration);
  513. // TODO: Determine if loaded or failed - bSuccess?
  514. // ExternalInterface.call(baseJSObject+"['"+oSound.sID+"']._onload",bSuccess?1:0);
  515. ExternalInterface.call(baseJSObject + "['" + oSound.sID + "']._onload", 1);
  516. }
  517. }
  518. public function onID3(e: Event) : void {
  519. // --- NOTE: BUGGY (Flash 8 only? Haven't really checked 9 + 10.) ---
  520. // TODO: Investigate holes in ID3 parsing - for some reason, Album will be populated with Date if empty and date is provided. (?)
  521. // ID3V1 seem to parse OK, but "holes" / blanks in ID3V2 data seem to get messed up (eg. missing album gets filled with date.)
  522. // iTunes issues: onID3 was not called with a test MP3 encoded with iTunes 7.01, and what appeared to be valid ID3V2 data.
  523. // May be related to thumbnails for album art included in MP3 file by iTunes. See http://mabblog.com/blog/?p=33
  524. try {
  525. var oSound:Object = e.target;
  526. var id3Data:Array = [];
  527. var id3Props:Array = [];
  528. for (var prop:String in oSound.id3) {
  529. id3Props.push(prop);
  530. id3Data.push(oSound.id3[prop]);
  531. // writeDebug('id3['+prop+']: '+oSound.id3[prop]);
  532. }
  533. ExternalInterface.call(baseJSObject + "['" + oSound.sID + "']._onid3", id3Props, id3Data);
  534. // unhook own event handler, prevent second call (can fire twice as data is received - ID3V2 at beginning, ID3V1 at end.)
  535. // Therefore if ID3V2 data is received, ID3V1 is ignored.
  536. // soundObjects[oSound.sID].onID3 = null;
  537. } catch(e: Error) {
  538. writeDebug('onID3(): Unable to get ID3 info for ' + oSound.sID + '.');
  539. }
  540. oSound.removeEventListener(Event.ID3, onID3);
  541. }
  542. public function registerOnComplete(sID:String) : void {
  543. var oSound: SoundManager2_SMSound_AS3 = soundObjects[sID];
  544. if (oSound && oSound.soundChannel) {
  545. oSound.soundChannel.addEventListener(Event.SOUND_COMPLETE, function () : void {
  546. if (oSound) {
  547. oSound.didJustBeforeFinish = false; // reset
  548. checkProgress();
  549. try {
  550. oSound.ignoreDataError = true; // workaround: avoid data error handling for this manual step..
  551. oSound.start(0, 1); // go back to 0
  552. oSound.soundChannel.stop();
  553. } catch(e: Error) {
  554. writeDebug('Could not set position on ' + sID + ': ' + e.toString());
  555. }
  556. oSound.ignoreDataError = false; // ..and reset
  557. oSound.handledDataError = false; // reset this flag
  558. }
  559. // checkProgress();
  560. ExternalInterface.call(baseJSObject + "['" + sID + "']._onfinish");
  561. });
  562. }
  563. }
  564. public function doSecurityError(oSound: SoundManager2_SMSound_AS3, e: SecurityErrorEvent) : void {
  565. writeDebug('securityError: ' + e.text);
  566. // when this happens, you don't have security rights on the server containing the FLV file
  567. // a crossdomain.xml file would fix the problem easily
  568. }
  569. public function doIOError(oSound: SoundManager2_SMSound_AS3, e: IOErrorEvent) : void {
  570. // writeDebug('ioError: '+e.text);
  571. // call checkProgress()?
  572. ExternalInterface.call(baseJSObject + "['" + oSound.sID + "']._onload", 0); // call onload, assume it failed.
  573. // there was a connection drop, a loss of internet connection, or something else wrong. 404 error too.
  574. }
  575. public function doAsyncError(oSound: SoundManager2_SMSound_AS3, e: AsyncErrorEvent) : void {
  576. writeDebug('asyncError: ' + e.text);
  577. // this is more related to streaming server from my experience, but you never know
  578. }
  579. public function doNetStatus(oSound: SoundManager2_SMSound_AS3, e: NetStatusEvent) : void {
  580. // this will eventually let us know what is going on.. is the stream loading, empty, full, stopped?
  581. // oSound.lastNetStatus = e.info.code;
  582. if (e.info.code != "NetStream.Buffer.Full" && e.info.code != "NetStream.Buffer.Empty" && e.info.code != "NetStream.Seek.Notify") {
  583. writeDebug('netStatusEvent: ' + e.info.code);
  584. }
  585. // When streaming, Stop is called when buffering stops, not when the stream is actually finished.
  586. // @see http://www.actionscript.org/forums/archive/index.php3/t-159194.html
  587. if (!oSound.serverUrl && e.info.code == "NetStream.Play.Stop") { // && !oSound.didFinish && oSound.loaded == true && nD == nP
  588. writeDebug('NetStream.Play.Stop');
  589. // if (!oSound.useNetstream) {
  590. // finished playing
  591. // oSound.didFinish = true; // will be reset via JS callback
  592. oSound.didJustBeforeFinish = false; // reset
  593. writeDebug('calling onfinish for a sound');
  594. // reset the sound? Move back to position 0?
  595. checkProgress();
  596. ExternalInterface.call(baseJSObject + "['" + oSound.sID + "']._onfinish");
  597. // and exit full-screen mode, too?
  598. stage.displayState = StageDisplayState.NORMAL;
  599. } else if (e.info.code == "NetStream.Play.FileStructureInvalid" || e.info.code == "NetStream.Play.FileStructureInvalid" || e.info.code == "NetStream.Play.StreamNotFound") {
  600. writeDebug('NetStream load error: '+e.info.code);
  601. this.onLoadError(oSound);
  602. } else if (e.info.code == "NetStream.Play.Start" || e.info.code == "NetStream.Buffer.Empty" || e.info.code == "NetStream.Buffer.Full") {
  603. // RTMP case..
  604. // We wait for the buffer to fill up before pausing the just-loaded song because only if the
  605. // buffer is full will the song continue to buffer until the user hits play.
  606. if (oSound.serverUrl && e.info.code == "NetStream.Buffer.Full" && oSound.pauseOnBufferFull) {
  607. oSound.ns.pause();
  608. oSound.paused = true;
  609. oSound.pauseOnBufferFull = false;
  610. // Call pause in JS. This will call back to us to pause again, but
  611. // that should be harmless.
  612. writeDebug('Pausing song because buffer is now full.');
  613. ExternalInterface.call(baseJSObject + "['" + oSound.sID + "'].pause", false);
  614. }
  615. // Increase the size of the buffer
  616. if (e.info.code == "NetStream.Buffer.Full") {
  617. if (oSound.ns.bufferTime == oSound.bufferTime) {
  618. oSound.ns.bufferTime = 15;
  619. writeDebug('increasing buffer to 15 secs');
  620. }/* else if (oSound.ns.bufferTime == 15) {
  621. oSound.ns.bufferTime = 30;
  622. writeDebug('increasing buffer to 30 secs');
  623. }*/
  624. }
  625. var isNetstreamBuffering: Boolean = (e.info.code == "NetStream.Buffer.Empty" || e.info.code == "NetStream.Play.Start");
  626. // assume buffering when we start playing, eg. initial load.
  627. if (isNetstreamBuffering != oSound.lastValues.isBuffering) {
  628. oSound.lastValues.isBuffering = isNetstreamBuffering;
  629. ExternalInterface.call(baseJSObject + "['" + oSound.sID + "']._onbufferchange", oSound.lastValues.isBuffering ? 1 : 0);
  630. }
  631. // We can detect the end of the stream when Play.Stop is called followed by Buffer.Empty.
  632. // However, if you pause and let the whole song buffer, Buffer.Flush is called followed by
  633. // Buffer.Empty, so handle that case too.
  634. if (e.info.code == "NetStream.Buffer.Empty" && (oSound.lastNetStatus == 'NetStream.Play.Stop' || oSound.lastNetStatus == 'NetStream.Buffer.Flush')) {
  635. //writeDebug('Buffer empty and last net status was Play.Stop or Buffer.Flush. This must be the end!');
  636. oSound.didJustBeforeFinish = false; // reset
  637. oSound.finished = true;
  638. writeDebug('calling onfinish for sound '+oSound.sID);
  639. checkProgress();
  640. ExternalInterface.call(baseJSObject + "['" + oSound.sID + "']._onfinish");
  641. } else if (e.info.code == "NetStream.Buffer.Empty" && oSound.ns.bufferTime != oSound.bufferTime) {
  642. oSound.ns.bufferTime = oSound.bufferTime;
  643. writeDebug('setting buffer to '+oSound.ns.bufferTime+' secs');
  644. }
  645. // Recover from failures
  646. } else if (e.info.code == "NetConnection.Connect.Closed"
  647. || e.info.code == "NetStream.Failed"
  648. || e.info.code == "NetStream.Play.FileStructureInvalid"
  649. || e.info.code == "NetStream.Play.StreamNotFound") {
  650. if (oSound.failed) {
  651. writeDebug('doNetStatus: ignoring, already reported failure.');
  652. } else {
  653. oSound.failed = true;
  654. ExternalInterface.call(baseJSObject + "['" + oSound.sID + "']._onfailure");
  655. }
  656. }
  657. oSound.lastNetStatus = e.info.code;
  658. }
  659. public function addNetstreamEvents(oSound: SoundManager2_SMSound_AS3) : void {
  660. oSound.ns.addEventListener(AsyncErrorEvent.ASYNC_ERROR, function (e: AsyncErrorEvent) : void {
  661. doAsyncError(oSound, e)
  662. });
  663. oSound.ns.addEventListener(NetStatusEvent.NET_STATUS, function (e: NetStatusEvent) : void {
  664. doNetStatus(oSound, e)
  665. });
  666. oSound.ns.addEventListener(IOErrorEvent.IO_ERROR, function (e: IOErrorEvent) : void {
  667. doIOError(oSound, e)
  668. });
  669. }
  670. public function removeNetstreamEvents(oSound: SoundManager2_SMSound_AS3) : void {
  671. // for the record, I'm sure this is completely wrong. ;)
  672. oSound.ns.removeEventListener(AsyncErrorEvent.ASYNC_ERROR, function (e: AsyncErrorEvent) : void {
  673. doAsyncError(oSound, e)
  674. });
  675. oSound.ns.removeEventListener(NetStatusEvent.NET_STATUS, function (e: NetStatusEvent) : void {
  676. doNetStatus(oSound, e)
  677. });
  678. oSound.ns.removeEventListener(IOErrorEvent.IO_ERROR, function (e: IOErrorEvent) : void {
  679. doIOError(oSound, e)
  680. });
  681. }
  682. public function _setPosition(sID:String, nSecOffset:Number, isPaused: Boolean) : void {
  683. var s: SoundManager2_SMSound_AS3 = soundObjects[sID];
  684. if (!s) return void;
  685. // writeDebug('_setPosition()');
  686. // don't allow seek past loaded duration. (Will stop + fail.)
  687. if (s.useVideo && nSecOffset > s.duration*1000) {
  688. writeDebug('setPosition: Cannot seek past current duration of '+s.duration+', using this value');
  689. nSecOffset = s.duration*1000;
  690. }
  691. // stop current channel, start new one.
  692. if (s.lastValues) {
  693. s.lastValues.position = nSecOffset; // s.soundChannel.position;
  694. }
  695. if (s.useNetstream) {
  696. // Minimize the buffer so playback starts ASAP
  697. s.ns.bufferTime = s.bufferTime;
  698. writeDebug('setPosition: setting buffer to '+s.ns.bufferTime+' secs');
  699. nSecOffset = nSecOffset > 0 ? nSecOffset / 1000 : 0;
  700. // writeDebug('setPosition: ' + nSecOffset/(!s.useVideo?1000:1));
  701. writeDebug('setPosition: ' + nSecOffset);
  702. s.ns.seek(nSecOffset);
  703. checkProgress(); // force UI update
  704. } else {
  705. if (s.soundChannel) {
  706. s.soundChannel.stop();
  707. }
  708. writeDebug('setPosition: ' + nSecOffset); // +', '+(s.lastValues.loops?s.lastValues.loops:1));
  709. if (s.lastValues.loops > 1 && nSecOffset != 0) {
  710. writeDebug('Warning: Looping functionality being disabled due to Flash limitation.');
  711. s.lastValues.loops = 1;
  712. }
  713. try {
  714. s.start(nSecOffset, s.lastValues.loops || 1); // start playing at new position
  715. } catch(e: Error) {
  716. writeDebug('Warning: Could not set position on ' + sID + ': ' + e.toString());
  717. }
  718. checkProgress(); // force UI update
  719. try {
  720. registerOnComplete(sID);
  721. } catch(e: Error) {
  722. writeDebug('_setPosition(): Could not register onComplete');
  723. }
  724. if (isPaused && s.soundChannel) {
  725. // writeDebug('_setPosition: stopping (paused) sound');
  726. // writeDebug('last position: '+s.lastValues.position+' vs '+s.soundChannel.position);
  727. s.soundChannel.stop();
  728. }
  729. }
  730. }
  731. public function _load(sID:String, sURL:String, bStream: Boolean, bAutoPlay: Boolean, nLoops:Number) : void {
  732. // writeDebug('_load()');
  733. if (typeof bAutoPlay == 'undefined') bAutoPlay = false;
  734. var s: SoundManager2_SMSound_AS3 = soundObjects[sID];
  735. if (!s) return void;
  736. var didRecreate: Boolean = false;
  737. if (s.didLoad == true) {
  738. // need to recreate sound
  739. didRecreate = true;
  740. writeDebug('recreating sound ' + sID + ' in order to load ' + sURL);
  741. var ns:Object = new Object();
  742. ns.sID = s.sID;
  743. ns.loops = nLoops||1;
  744. ns.justBeforeFinishOffset = s.justBeforeFinishOffset;
  745. ns.usePeakData = s.usePeakData;
  746. ns.useWaveformData = s.useWaveformData;
  747. ns.useEQData = s.useEQData;
  748. ns.useNetstream = s.useNetstream;
  749. ns.useVideo = s.useVideo;
  750. ns.bufferTime = s.bufferTime;
  751. ns.serverUrl = s.serverUrl;
  752. ns.duration = s.duration;
  753. ns.totalBytes = s.totalBytes;
  754. ns.useEvents = true;
  755. _destroySound(s.sID);
  756. _createSound(ns.sID, sURL, ns.justBeforeFinishOffset, ns.usePeakData, ns.useWaveformData, ns.useEQData, ns.useNetstream, ns.useVideo, ns.bufferTime, ns.loops, ns.serverUrl, ns.duration, ns.totalBytes, bAutoPlay, ns.useEvents);
  757. s = soundObjects[sID];
  758. // writeDebug('Sound object replaced');
  759. }
  760. checkProgress();
  761. if (!s.didLoad) {
  762. try {
  763. s.addEventListener(Event.ID3, onID3);
  764. s.addEventListener(Event.COMPLETE, onLoad);
  765. } catch(e: Error) {
  766. writeDebug('_load(): could not assign ID3/complete event handlers');
  767. }
  768. }
  769. // s.addEventListener(ProgressEvent.PROGRESS, checkLoadProgress); // May be called often, potential CPU drain
  770. // s.addEventListener(Event.FINISH, onFinish);
  771. // s.loaded = true; // TODO: Investigate - Flash 9 non-FLV bug??
  772. // s.didLoad = true; // TODO: Investigate - bug?
  773. // if (didRecreate || s.sURL != sURL) {
  774. // don't try to load if same request already made
  775. s.sURL = sURL;
  776. if (s.useNetstream) {
  777. try {
  778. // s.ns.close();
  779. s.useEvents = true;
  780. if (s.ns) {
  781. this.addNetstreamEvents(s);
  782. ExternalInterface.call(baseJSObject + "['" + s.sID + "']._whileloading", s.ns.bytesLoaded, s.ns.bytesTotal || s.totalBytes, int(s.duration || 0));
  783. s.ns.play(sURL);
  784. if (!bAutoPlay) {
  785. s.ns.pause();
  786. }
  787. } else {
  788. writeDebug('_load(): Note: No netStream found.'+(!s.connected?' (Not connected yet.)':''));
  789. }
  790. } catch(e: Error) {
  791. writeDebug('_load(): error: ' + e.toString());
  792. }
  793. } else {
  794. try {
  795. s.addEventListener(IOErrorEvent.IO_ERROR, function (e: IOErrorEvent) : void {
  796. doIOError(s, e)
  797. });
  798. s.loadSound(sURL, bStream);
  799. } catch(e: Error) {
  800. // oh well
  801. writeDebug('_load: Error loading ' + sURL + '. Flash error detail: ' + e.toString());
  802. }
  803. }
  804. s.didJustBeforeFinish = false;
  805. }
  806. public function _unload(sID:String) : void {
  807. var s: SoundManager2_SMSound_AS3 = soundObjects[sID];
  808. if (!s) return void;
  809. var sURL:String = s.sURL; // save existing sound URL for object recreation
  810. try {
  811. removeEventListener(Event.ID3, onID3);
  812. removeEventListener(Event.COMPLETE, onLoad);
  813. } catch(e: Error) {
  814. writeDebug('_unload() warn: Could not remove ID3/complete events');
  815. }
  816. s.paused = false;
  817. if (s.soundChannel) {
  818. s.soundChannel.stop();
  819. }
  820. try {
  821. if (s.didLoad && !s.loaded && !s.useNetstream) {
  822. s.close(); // close stream only if still loading?
  823. }
  824. } catch(e: Error) {
  825. // stream may already have closed if sound loaded, etc.
  826. writeDebug(sID + '._unload(): Note: Unable to close stream: ' + e.toString());
  827. // oh well
  828. }
  829. // destroy and recreate Flash sound object, try to reclaim memory
  830. // writeDebug('sound._unload(): recreating sound '+sID+' to free memory');
  831. if (s.useNetstream) {
  832. // writeDebug('_unload(): closing netStream stuff');
  833. try {
  834. this.removeNetstreamEvents(s);
  835. s.ns.close();
  836. s.nc.close();
  837. // s.nc = null;
  838. // s.ns = null;
  839. } catch(e: Error) {
  840. // oh well
  841. writeDebug('_unload(): caught exception during netConnection/netStream close');
  842. }
  843. if (s.useVideo) {
  844. writeDebug('_unload(): clearing video');
  845. s.oVideo.clear();
  846. // s.oVideo = null;
  847. }
  848. }
  849. var ns:Object = new Object();
  850. ns.sID = s.sID;
  851. ns.loops = s.loops;
  852. ns.justBeforeFinishOffset = s.justBeforeFinishOffset;
  853. ns.usePeakData = s.usePeakData;
  854. ns.useWaveformData = s.useWaveformData;
  855. ns.useEQData = s.useEQData;
  856. ns.useNetstream = s.useNetstream;
  857. ns.useVideo = s.useVideo;
  858. ns.bufferTime = s.bufferTime;
  859. ns.serverUrl = s.serverUrl;
  860. ns.duration = s.duration;
  861. ns.totalBytes = s.totalBytes;
  862. ns.autoPlay = s.autoPlay;
  863. _destroySound(s.sID);
  864. _createSound(ns.sID, sURL, ns.justBeforeFinishOffset, ns.usePeakData, ns.useWaveformData, ns.useEQData, ns.useNetstream, ns.useVideo, ns.bufferTime, ns.loops, ns.serverUrl, ns.duration, ns.totalBytes, ns.autoPlay, false); // false: don't allow events just yet
  865. soundObjects[sID].connected = true; // fake it?
  866. writeDebug(s.sID + '.unload(): ok');
  867. }
  868. public function _createSound(sID:String, sURL:String, justBeforeFinishOffset: int, usePeakData: Boolean, useWaveformData: Boolean, useEQData: Boolean, useNetstream: Boolean, useVideo: Boolean, bufferTime:Number, loops:Number, serverUrl:String, duration:Number, totalBytes:Number, autoPlay:Boolean, useEvents:Boolean) : void {
  869. soundObjects[sID] = new SoundManager2_SMSound_AS3(this, sID, sURL, usePeakData, useWaveformData, useEQData, useNetstream, useVideo, bufferTime, serverUrl, duration, totalBytes, autoPlay, useEvents);
  870. var s: SoundManager2_SMSound_AS3 = soundObjects[sID];
  871. if (!s) return void;
  872. this.currentObject = s;
  873. // s.setVolume(100);
  874. s.didJustBeforeFinish = false;
  875. s.sID = sID;
  876. s.sURL = sURL;
  877. s.paused = false;
  878. s.loaded = false;
  879. s.justBeforeFinishOffset = justBeforeFinishOffset || 0;
  880. s.lastValues = {
  881. bytes: 0,
  882. position: 0,
  883. loops: loops||1,
  884. leftPeak: 0,
  885. rightPeak: 0,
  886. bufferLength: 0
  887. };
  888. if (! (sID in sounds)) sounds.push(sID);
  889. // sounds.push(sID);
  890. }
  891. public function _destroySound(sID:String) : void {
  892. // for the power of garbage collection! .. er, Greyskull!
  893. var s: SoundManager2_SMSound_AS3 = (soundObjects[sID] || null);
  894. if (!s) return void;
  895. // try to unload the sound
  896. for (var i: int = 0, j: int = sounds.length; i < j; i++) {
  897. if (sounds[i] == s) {
  898. sounds.splice(i, 1);
  899. continue;
  900. }
  901. }
  902. if (s.soundChannel) {
  903. s.soundChannel.stop();
  904. }
  905. this.stage.removeEventListener(Event.RESIZE, s.resizeHandler);
  906. // if is a movie, remove that as well.
  907. if (s.useNetstream) {
  908. // s.nc.client = null;
  909. try {
  910. this.removeNetstreamEvents(s);
  911. // s.nc.removeEventListener(NetStatusEvent.NET_STATUS, s.doNetStatus);
  912. } catch(e: Error) {
  913. writeDebug('_destroySound(): Events already removed from netStream/netConnection?');
  914. }
  915. if (s.useVideo) {
  916. try {
  917. this.removeChild(s.oVideo);
  918. } catch(e: Error) {
  919. writeDebug('_destoySound(): could not remove video?');
  920. }
  921. }
  922. if (s.didLoad) {
  923. // TODO: figure out if stream is still open first, can't close an already-closed stream.
  924. try {
  925. s.ns.close();
  926. s.nc.close();
  927. } catch(e: Error) {
  928. // oh well
  929. writeDebug('_destroySound(): caught exception: '+e.toString());
  930. }
  931. }
  932. } else if (s.didLoad) {
  933. // non-netstream case
  934. try {
  935. s.close(); // close stream only if still loading?
  936. } catch(e: Error) {
  937. // oh well
  938. }
  939. }
  940. s = null;
  941. soundObjects[sID] = null;
  942. delete soundObjects[sID];
  943. }
  944. public function _stop(sID:String, bStopAll: Boolean) : void {
  945. // stop this particular instance (or "all", based on parameter)
  946. if (bStopAll) {
  947. SoundMixer.stopAll();
  948. } else {
  949. var s: SoundManager2_SMSound_AS3 = soundObjects[sID];
  950. if (!s) return void;
  951. if (s.useNetstream && s.ns) {
  952. s.ns.pause();
  953. if (s.oVideo) {
  954. s.oVideo.visible = false;
  955. }
  956. } else if (s.soundChannel) {
  957. s.soundChannel.stop();
  958. }
  959. s.paused = false;
  960. s.didJustBeforeFinish = false;
  961. }
  962. }
  963. public function _start(sID:String, nLoops: int, nMsecOffset: int) : void {
  964. var s: SoundManager2_SMSound_AS3 = soundObjects[sID];
  965. if (!s) return void;
  966. writeDebug('start: ' + nMsecOffset+(nLoops?', loops: '+nLoops:''));
  967. s.lastValues.paused = false; // reset pause if applicable
  968. s.lastValues.loops = (nLoops || 1);
  969. if (!s.useNetstream) {
  970. s.lastValues.position = nMsecOffset;
  971. }
  972. s.handledDataError = false; // reset this flag
  973. try {
  974. s.start(nMsecOffset, nLoops);
  975. } catch(e: Error) {
  976. writeDebug('Could not start ' + sID + ': ' + e.toString());
  977. }
  978. try {
  979. registerOnComplete(sID);
  980. } catch(e: Error) {
  981. writeDebug('_start(): registerOnComplete failed');
  982. }
  983. }
  984. public function _pause(sID:String) : void {
  985. // writeDebug('_pause()');
  986. var s: SoundManager2_SMSound_AS3 = soundObjects[sID];
  987. if (!s) return void;
  988. // writeDebug('s.paused: '+s.paused);
  989. if (!s.paused) {
  990. // reference current position, stop sound
  991. s.paused = true;
  992. // writeDebug('_pause(): position: '+s.lastValues.position);
  993. if (s.useNetstream) {
  994. if (s.ns) {
  995. s.lastValues.position = s.ns.time;
  996. s.ns.pause();
  997. }
  998. } else {
  999. if (s.soundChannel) {
  1000. s.lastValues.position = s.soundChannel.position;
  1001. s.soundChannel.stop();
  1002. }
  1003. }
  1004. } else {
  1005. // resume playing from last position
  1006. // writeDebug('resuming - playing at '+s.lastValues.position+', '+s.lastValues.loops+' times');
  1007. s.paused = false;
  1008. if (s.useNetstream) {
  1009. s.ns.resume();
  1010. } else {
  1011. s.start(s.lastValues.position, s.lastValues.loops);
  1012. }
  1013. try {
  1014. registerOnComplete(sID);
  1015. } catch(e: Error) {
  1016. writeDebug('_pause(): registerOnComplete() failed');
  1017. }
  1018. }
  1019. }
  1020. public function _setPan(sID:String, nPan:Number) : void {
  1021. soundObjects[sID].setPan(nPan);
  1022. }
  1023. public function _setVolume(sID:String, nVol:Number) : void {
  1024. // writeDebug('_setVolume: '+nVol);
  1025. soundObjects[sID].setVolume(nVol);
  1026. }
  1027. public function _setPolling(bPolling: Boolean = false, bUseHighPerformanceTimer: Boolean = false) : void {
  1028. pollingEnabled = bPolling;
  1029. if (timer == null && pollingEnabled) {
  1030. var nTimerInterval: uint = (bUseHighPerformanceTimer ? timerIntervalHighPerformance : timerInterval);
  1031. writeDebug('Enabling polling, ' + nTimerInterval + ' ms interval');
  1032. timer = new Timer(nTimerInterval, 0);
  1033. timer.addEventListener(TimerEvent.TIMER, function () : void {
  1034. checkProgress();
  1035. }); // direct reference eg. checkProgress doesn't work? .. odd.
  1036. timer.start();
  1037. } else if (timer && !pollingEnabled) {
  1038. writeDebug('Disabling polling');
  1039. // flash.utils.clearInterval(timer);
  1040. timer.reset();
  1041. }
  1042. }
  1043. public function _getMemoryUse() : String {
  1044. return System.totalMemory.toString();
  1045. }
  1046. // XML handler stuff
  1047. public function _loadFromXML(sURL:String) : void {
  1048. var loader: URLLoader = new URLLoader();
  1049. loader.addEventListener(Event.COMPLETE, parseXML);
  1050. writeDebug('Attempting to load XML: ' + sURL);
  1051. try {
  1052. loader.load(new URLRequest(sURL));
  1053. } catch(e: Error) {
  1054. writeDebug('Error loading XML: ' + e.toString());
  1055. }
  1056. }
  1057. public function parseXML(e: Event) : void {
  1058. try {
  1059. var oXML: XMLDocument = new XMLDocument();
  1060. oXML.ignoreWhite = true;
  1061. oXML.parseXML(e.target.data);
  1062. var xmlRoot: XMLNode = oXML.firstChild;
  1063. var xmlAttr:Object = xmlRoot.attributes;
  1064. var oOptions:Object = {};
  1065. var i: int = 0;
  1066. var j: int = 0;
  1067. for (i = 0, j = xmlRoot.childNodes.length; i < j; i++) {
  1068. xmlAttr = xmlRoot.childNodes[i].attributes;
  1069. oOptions = {
  1070. id: xmlAttr.id,
  1071. url: xmlRoot.attributes.baseHref + xmlAttr.href,
  1072. stream: xmlAttr.stream
  1073. }
  1074. ExternalInterface.call(baseJSController + ".createSound", oOptions);
  1075. }
  1076. } catch(e: Error) {
  1077. writeDebug('Error parsing XML: ' + e.toString());
  1078. }
  1079. }
  1080. // -----------------------------------
  1081. // end methods
  1082. }
  1083. // package
  1084. }