dojo.provide("dojox.av.FLAudio"); dojo.experimental("dojox.av.FLAudio"); dojo.require("dijit._Widget"); dojo.require("dojox.embed.Flash"); dojo.require("dojox.av._Media"); dojo.require("dojox.timing.doLater"); dojo.declare("dojox.av.FLAudio", null, { // summary: // Play MP3 files through the Flash SWF built in the // DEFT project. // description: // This class is brand new, so there is a lot of // functionality not yet available. The initial // purpose is for playing "event" sounds like button // clicks, and for loading and controlling multiple // sounds at once. As of yet, streaming is not supported // and polling the sounds for events during playback // may still be missing information. Markup is not // supported, as it may not be needed. // // TODO: // Streaming, playback events, crossdomain, CDN support, // (alternate SWF location), global volume, ID3 tag, // factor out doLater, onLoadStatus needs work, // play(position) / seek() // // example: // | new dojox.av.FLAudio({ // | initialVolume:.7, // | initialPan:0, // | autoPlay:false // | }); // // id: String? // The id of this widget and the id of the SWF movie. id:"", // // initialVolume: Number // From 0-1 // Sets volume for all files unless changed with doPlay // or setVolume initialVolume: 0.7, // // initialPan: Number // From -1 to 1 (-1 is left, 1 is right, 0 is middle) // Sets pan for all files unless changed with play // or setPan initialPan: 0, // // autoPlay: Boolean // If true, all files will play upon load. If false, // they load and wait for doPlay() command. // // isDebug: Boolean? // Setting to true tells the SWF to output log messages to Firebug. isDebug: false, // // statusInterval: Number // How often in milliseconds that the status of the // player is checked - both load and play statusInterval:200, // // _swfPath: Uri // The path to the video player SWF resource _swfPath: dojo.moduleUrl("dojox.av", "resources/audio.swf"), // // constructor: function(/*Object*/options){ dojo.mixin(this, options || {}); if(!this.id){ this.id = "flaudio_"+new Date().getTime(); } this.domNode = dojo.doc.createElement("div"); dojo.style(this.domNode, { postion:"relative", width:"1px", height:"1px", top:"1px", left:"1px" }); dojo.body().appendChild(this.domNode); this.init(); }, init: function(){ // summary: // Initialize the media. // // this._subs = []; this.initialVolume = this._normalizeVolume(this.initialVolume); var args = { path:this._swfPath.uri, width:"1px", height:"1px", minimumVersion:9, // this may need to be 10, not sure expressInstall:true, params:{ wmode:"transparent" }, // only pass in simple variables - no deep objects vars:{ id:this.id, autoPlay:this.autoPlay, initialVolume:this.initialVolume, initialPan:this.initialPan, statusInterval:this.statusInterval, isDebug:this.isDebug } }; this._sub("mediaError", "onError"); this._sub("filesProgress", "onLoadStatus"); this._sub("filesAllLoaded", "onAllLoaded"); this._sub("mediaPosition", "onPlayStatus"); this._sub("mediaMeta", "onID3"); this._flashObject = new dojox.embed.Flash(args, this.domNode); this._flashObject.onError = function(err){ console.warn("Flash Error:", err); alert(err); }; this._flashObject.onLoad = dojo.hitch(this, function(mov){ this.flashMedia = mov; this.isPlaying = this.autoPlay; this.isStopped = !this.autoPlay; this.onLoad(this.flashMedia); }); }, // ============== // // Loading Files // // ============== // load: function(/*Object*/options){ // summary: // Adds a media object to the playlist // ***This can be called repeatedly to add multiple items. // options: Object // url: String // (required) path to MP3 media // url must be absolute or relative to SWF, // not dojo or the html. An effort will be made // to fix incorrect paths. // id: String // (optional) an identifier to later determine // which media to control. // returns: // The normalized url, which can be used to identify the // audio. // if(dojox.timing.doLater(this.flashMedia, this)){ return false; } if(!options.url){ throw new Error("An url is required for loading media"); return false; }else{ options.url = this._normalizeUrl(options.url); } this.flashMedia.load(options); return options.url; // String }, // ============================= // // Methods to control the sound // // ============================= // doPlay: function(/*Object*/options){ // summary: // Tell media to play, based on // the options passed. // options: Object // volume: Number // Sets the volume // pan: Number // Sets left/right pan // index:Number OR id:String OR url:String // Choose one of the above to indentify // the media you wish to control. id is // set by you. index is the order in which // media was added (zero based) // NOTE: lack of an identifier will default // to first (or only) item. // NOTE: Can't name this method "play()" as it causes // an IE error. this.flashMedia.doPlay(options); }, pause: function(/*Object*/options){ // summary: // Tell media to pause, based on identifier in // the options passed. // options: Object // index:Number OR id:String OR url:String // See doPlay() // this.flashMedia.pause(options); }, stop: function(/*Object*/options){ // summary: // Tell media to stop, based on identifier in // the options passed. // options: // index:Number OR id:String OR url:String // See doPlay() // this.flashMedia.doStop(options); }, setVolume: function(/*Object*/options){ // summary: // Set media volume, based on identifier in // the options passed. // options: // volume: Number // 0 to 1 // index:Number OR id:String OR url:String // See doPlay() // this.flashMedia.setVolume(options); }, setPan: function(/*Object*/options){ // summary: // Set media pan, based on identifier in // the options passed. // options: // pan:Number // -1 to 1 // index:Number OR id:String OR url:String // See doPlay() // this.flashMedia.setPan(options); }, getVolume: function(/*Object*/options){ // summary: // Get media volume, based on identifier in // the options passed. // options: // index:Number OR id:String OR url:String // See doPlay() // return this.flashMedia.getVolume(options); }, getPan: function(/*Object*/options){ // summary: // Set media pan, based on identifier in // the options passed. // options: // index:Number OR id:String OR url:String // See doPlay() // return this.flashMedia.getPan(options); }, // ============= // // Sound Events // // ============= // onError: function(msg){ // summary: // stub fired when an error occurs console.warn("SWF ERROR:", msg) }, onLoadStatus: function(/*Array*/events){ // summary: }, onAllLoaded: function(){ // summary: // stub fired }, onPlayStatus: function(/*Array*/events){ // summary: }, onLoad: function(){ // summary: // stub fired when SWF is ready }, onID3: function(evt){ // summary: // Fired when the ID3 data is received. }, destroy: function(){ // summary: // destroys flash if(!this.flashMedia){ this._cons.push(dojo.connect(this, "onLoad", this, "destroy")); return; } dojo.forEach(this._subs, function(s){ dojo.unsubscribe(s); }); dojo.forEach(this._cons, function(c){ dojo.disconnect(c); }); this._flashObject.destroy(); //dojo._destroyElement(this.flashDiv); }, _sub: function(topic, method){ // summary: // helper for subscribing to topics dojo.subscribe(this.id+"/"+topic, this, method); }, _normalizeVolume: function(vol){ // summary: // Ensures volume is less than one // if(vol>1){ while(vol>1){ vol*=.1 } } return vol; }, _normalizeUrl: function(_url){ // summary: // Checks that path is relative to HTML file or // convertes it to an absolute path. // if(_url && _url.toLowerCase().indexOf("http")<0){ // // Appears to be a relative path. Attempt to convert it to absolute, // so it will better target the SWF. var loc = window.location.href.split("/"); loc.pop(); loc = loc.join("/")+"/"; _url = loc+_url; } return _url; } });