FLAudio.js 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389
  1. /*
  2. Copyright (c) 2004-2012, The Dojo Foundation All Rights Reserved.
  3. Available via Academic Free License >= 2.1 OR the modified BSD license.
  4. see: http://dojotoolkit.org/license for details
  5. */
  6. if(!dojo._hasResource["dojox.av.FLAudio"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  7. dojo._hasResource["dojox.av.FLAudio"] = true;
  8. dojo.provide("dojox.av.FLAudio");
  9. dojo.experimental("dojox.av.FLAudio");
  10. dojo.require("dojox.embed.Flash");
  11. dojo.require("dojox.timing.doLater");
  12. dojo.declare("dojox.av.FLAudio", null, {
  13. // summary:
  14. // Play MP3 files through the Flash SWF built in the
  15. // DEFT project.
  16. // description:
  17. // This class is brand new, so there is a lot of
  18. // functionality not yet available. The initial
  19. // purpose is for playing "event" sounds like button
  20. // clicks, and for loading and controlling multiple
  21. // sounds at once. As of yet, streaming is not supported
  22. // and polling the sounds for events during playback
  23. // may still be missing information. Markup is not
  24. // supported, as it may not be needed.
  25. //
  26. // TODO:
  27. // Streaming, playback events, crossdomain, CDN support,
  28. // (alternate SWF location), global volume, ID3 tag,
  29. // factor out doLater, onLoadStatus needs work,
  30. // play(position) / seek()
  31. //
  32. // example:
  33. // | new dojox.av.FLAudio({
  34. // | initialVolume:.7,
  35. // | initialPan:0,
  36. // | autoPlay:false
  37. // | });
  38. //
  39. // id: String?
  40. // The id of this widget and the id of the SWF movie.
  41. id:"",
  42. //
  43. // initialVolume: Number
  44. // From 0-1
  45. // Sets volume for all files unless changed with doPlay
  46. // or setVolume
  47. initialVolume: 0.7,
  48. //
  49. // initialPan: Number
  50. // From -1 to 1 (-1 is left, 1 is right, 0 is middle)
  51. // Sets pan for all files unless changed with play
  52. // or setPan
  53. initialPan: 0,
  54. //
  55. // autoPlay: Boolean
  56. // If true, all files will play upon load. If false,
  57. // they load and wait for doPlay() command.
  58. //
  59. // isDebug: Boolean?
  60. // Setting to true tells the SWF to output log messages to Firebug.
  61. isDebug: false,
  62. //
  63. // statusInterval: Number
  64. // How often in milliseconds that the status of the
  65. // player is checked - both load and play
  66. statusInterval:200,
  67. //
  68. // _swfPath: Uri
  69. // The path to the video player SWF resource
  70. _swfPath: dojo.moduleUrl("dojox.av", "resources/audio.swf"),
  71. //
  72. //
  73. // allowScriptAccess: String
  74. // Whether the SWF can access the container JS
  75. allowScriptAccess:"always",
  76. //
  77. // allowNetworking: String
  78. // Whether SWF is restricted to a domain
  79. allowNetworking: "all",
  80. //
  81. constructor: function(/*Object*/options){
  82. // Provide this function for the SWF to ensure that the it is playing
  83. // in HTML.
  84. dojo.global.swfIsInHTML = function(){ return true; }
  85. dojo.mixin(this, options || {});
  86. if(!this.id){ this.id = "flaudio_"+new Date().getTime(); }
  87. this.domNode = dojo.doc.createElement("div");
  88. dojo.style(this.domNode, {
  89. position:"relative",
  90. width:"1px",
  91. height:"1px",
  92. top:"1px",
  93. left:"1px"
  94. });
  95. dojo.body().appendChild(this.domNode);
  96. this.init();
  97. },
  98. init: function(){
  99. // summary:
  100. // Initialize the media.
  101. //
  102. //
  103. this._subs = [];
  104. this.initialVolume = this._normalizeVolume(this.initialVolume);
  105. var args = {
  106. path:this._swfPath.uri,
  107. width:"1px",
  108. height:"1px",
  109. minimumVersion:9, // this may need to be 10, not sure
  110. expressInstall:true,
  111. params:{
  112. wmode:"transparent",
  113. allowScriptAccess:this.allowScriptAccess,
  114. allowNetworking:this.allowNetworking
  115. },
  116. // only pass in simple variables - no deep objects
  117. vars:{
  118. id:this.id,
  119. autoPlay:this.autoPlay,
  120. initialVolume:this.initialVolume,
  121. initialPan:this.initialPan,
  122. statusInterval:this.statusInterval,
  123. isDebug:this.isDebug
  124. }
  125. };
  126. this._sub("mediaError", "onError");
  127. this._sub("filesProgress", "onLoadStatus");
  128. this._sub("filesAllLoaded", "onAllLoaded");
  129. this._sub("mediaPosition", "onPlayStatus");
  130. this._sub("mediaEnd", "onComplete");
  131. this._sub("mediaMeta", "onID3");
  132. this._flashObject = new dojox.embed.Flash(args, this.domNode);
  133. this._flashObject.onError = function(err){
  134. console.warn("Flash Error:", err);
  135. };
  136. this._flashObject.onLoad = dojo.hitch(this, function(mov){
  137. this.flashMedia = mov;
  138. this.isPlaying = this.autoPlay;
  139. this.isStopped = !this.autoPlay;
  140. this.onLoad(this.flashMedia);
  141. });
  142. },
  143. // ============== //
  144. // Loading Files //
  145. // ============== //
  146. load: function(/*Object*/options){
  147. // summary:
  148. // Adds a media object to the playlist
  149. // ***This can be called repeatedly to add multiple items.
  150. // options: Object
  151. // url: String
  152. // (required) path to MP3 media
  153. // url must be absolute or relative to SWF,
  154. // not dojo or the html. An effort will be made
  155. // to fix incorrect paths.
  156. // id: String
  157. // (optional) an identifier to later determine
  158. // which media to control.
  159. // returns:
  160. // The normalized url, which can be used to identify the
  161. // audio.
  162. //
  163. if(dojox.timing.doLater(this.flashMedia, this)){ return false; }
  164. if(!options.url){
  165. throw new Error("An url is required for loading media");
  166. return false;
  167. }else{
  168. options.url = this._normalizeUrl(options.url);
  169. }
  170. this.flashMedia.load(options);
  171. return options.url; // String
  172. },
  173. // ============================= //
  174. // Methods to control the sound //
  175. // ============================= //
  176. doPlay: function(/*Object*/options){
  177. // summary:
  178. // Tell media to play, based on
  179. // the options passed.
  180. // options: Object
  181. // volume: Number
  182. // Sets the volume
  183. // pan: Number
  184. // Sets left/right pan
  185. // index:Number OR id:String OR url:String
  186. // Choose one of the above to indentify
  187. // the media you wish to control. id is
  188. // set by you. index is the order in which
  189. // media was added (zero based)
  190. // NOTE: lack of an identifier will default
  191. // to first (or only) item.
  192. // NOTE: Can't name this method "play()" as it causes
  193. // an IE error.
  194. this.flashMedia.doPlay(options);
  195. },
  196. pause: function(/*Object*/options){
  197. // summary:
  198. // Tell media to pause, based on identifier in
  199. // the options passed.
  200. // options: Object
  201. // index:Number OR id:String OR url:String
  202. // See doPlay()
  203. //
  204. this.flashMedia.pause(options);
  205. },
  206. stop: function(/*Object*/options){
  207. // summary:
  208. // Tell media to stop, based on identifier in
  209. // the options passed.
  210. // options:
  211. // index:Number OR id:String OR url:String
  212. // See doPlay()
  213. //
  214. this.flashMedia.doStop(options);
  215. },
  216. setVolume: function(/*Object*/options){
  217. // summary:
  218. // Set media volume, based on identifier in
  219. // the options passed.
  220. // options:
  221. // volume: Number
  222. // 0 to 1
  223. // index:Number OR id:String OR url:String
  224. // See doPlay()
  225. //
  226. this.flashMedia.setVolume(options);
  227. },
  228. setPan: function(/*Object*/options){
  229. // summary:
  230. // Set media pan, based on identifier in
  231. // the options passed.
  232. // options:
  233. // pan:Number
  234. // -1 to 1
  235. // index:Number OR id:String OR url:String
  236. // See doPlay()
  237. //
  238. this.flashMedia.setPan(options);
  239. },
  240. getVolume: function(/*Object*/options){
  241. // summary:
  242. // Get media volume, based on identifier in
  243. // the options passed.
  244. // options:
  245. // index:Number OR id:String OR url:String
  246. // See doPlay()
  247. //
  248. return this.flashMedia.getVolume(options);
  249. },
  250. getPan: function(/*Object*/options){
  251. // summary:
  252. // Set media pan, based on identifier in
  253. // the options passed.
  254. // options:
  255. // index:Number OR id:String OR url:String
  256. // See doPlay()
  257. //
  258. return this.flashMedia.getPan(options);
  259. },
  260. getPosition: function(/*Object*/options){
  261. // summary:
  262. // Get the current time.
  263. // options:
  264. // index:Number OR id:String OR url:String
  265. // See doPlay()
  266. //
  267. return this.flashMedia.getPosition(options);
  268. },
  269. // ============= //
  270. // Sound Events //
  271. // ============= //
  272. onError: function(msg){
  273. // summary:
  274. // stub fired when an error occurs
  275. console.warn("SWF ERROR:", msg)
  276. },
  277. onLoadStatus: function(/*Array*/events){
  278. // summary:
  279. },
  280. onAllLoaded: function(){
  281. // summary:
  282. // stub fired
  283. },
  284. onPlayStatus: function(/*Array*/events){
  285. // summary:
  286. },
  287. onComplete: function(/*Array*/events){
  288. // summary:
  289. // Fired at the end of a media file.
  290. },
  291. onLoad: function(){
  292. // summary:
  293. // stub fired when SWF is ready
  294. },
  295. onID3: function(evt){
  296. // summary:
  297. // Fired when the ID3 data is received.
  298. },
  299. destroy: function(){
  300. // summary:
  301. // destroys flash
  302. if(!this.flashMedia){
  303. this._cons.push(dojo.connect(this, "onLoad", this, "destroy"));
  304. return;
  305. }
  306. dojo.forEach(this._subs, function(s){
  307. dojo.unsubscribe(s);
  308. });
  309. dojo.forEach(this._cons, function(c){
  310. dojo.disconnect(c);
  311. });
  312. this._flashObject.destroy();
  313. //dojo._destroyElement(this.flashDiv);
  314. },
  315. _sub: function(topic, method){
  316. // summary:
  317. // helper for subscribing to topics
  318. dojo.subscribe(this.id+"/"+topic, this, method);
  319. },
  320. _normalizeVolume: function(vol){
  321. // summary:
  322. // Ensures volume is less than one
  323. //
  324. if(vol>1){
  325. while(vol>1){
  326. vol*=.1
  327. }
  328. }
  329. return vol;
  330. },
  331. _normalizeUrl: function(_url){
  332. // summary:
  333. // Checks that path is relative to HTML file or
  334. // convertes it to an absolute path.
  335. //
  336. if(_url && _url.toLowerCase().indexOf("http")<0){
  337. //
  338. // Appears to be a relative path. Attempt to convert it to absolute,
  339. // so it will better target the SWF.
  340. var loc = window.location.href.split("/");
  341. loc.pop();
  342. loc = loc.join("/")+"/";
  343. _url = loc+_url;
  344. }
  345. return _url;
  346. }
  347. });
  348. }