PageRenderTime 50ms CodeModel.GetById 15ms RepoModel.GetById 1ms app.codeStats 0ms

/externals/soundmanager/src/SoundManager2_AS3.as

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