/* Flash MP3 player Copyleft (C) 2009 BohwaZ, Licence GNU/GPL. Basé sur le player de neolao et des idées de soundmanager v2 Changements par rapport au player neolao : * Le player ne fait plus du polling que pendant le chargement ou la lecture. * Le callback onUpdate n'est donc plus appelé que quand c'est nécessaire. * la propriété isPlaying devient un vrai booléen * Nouvelle propriété isPaused (bool) -> indique si le player est en pause * La propriété url disparaît * id3 devient un objet JS (n'est accessible que à partir du premier appel à onID3) * Nouveau callback onID3, appelé quand des tags ID3 sont reçus par le flash * Nouveau callback onSoundComplete, appelé quand le mp3 a terminé de lire * Nouveau callback writeDebug appelé uniquement si on met le paramètre "debug=1" au flash * Nouvelle fonctionnalité qui teste si le client sait lire du son (flash ne sait pas dire si le client a une carte son configurée ou pas). * Nouveau callback onInitFail appelé quand le test de son échoue * onInit n'est appelé que quand le test fonctionne Voir simple_test.html pour un exemple simple. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ import flash.external.*; class Mp3Player { /** * Enable debug */ private var _enableDebug:Boolean = false; /** * Javascript object listener */ private var _listener:String = ""; private var _testTimer:Number; /** * Interval update in milliseconds */ private var _updateInterval:Number = 50; private var _updateTimer:Number; /** * JS update each update iteration */ private var _pollJsEach:Number = 5; private var _lastPolling:Number = 0; /** * Last sent datas to the JS (to avoid sending everything all the time) */ private var _lastSentDatas:Object; /** * Last values, used to avoid some flash bugs */ private var _lastValues:Object; /** * Flash sound object */ private var _sound:Sound; /** * Current position in the track */ private var _position:Number = 0; private var _url:String = ""; /** * Is the track playing ? */ public var isPlaying:Boolean = false; /** * Is the track paused ? */ public var isPaused:Boolean = false; /** * Volume */ public var volume:Number = 100; public function Mp3Player() { // Javascript object listener if (_root.listener) { _listener = _root.listener + "."; } if (_root.debug) { this._enableDebug = true; _debug("Debug enabled"); } if (_root.interval) { if (Number(_root.interval) >= this._updateInterval) { this._pollJsEach = Math.round(_root.interval / this._updateInterval); } else { this._pollJsEach = 1; } } _debug("Polling JS each "+this._pollJsEach+" iterations"); // Test des capacités audio this._sound = new Sound(); this._sound.setVolume(0); this._sound.attachSound("silenceTestFile"); this._sound.onSoundComplete = function() { var self:Object = _root.method; self._debug("Sound init OK"); clearInterval(self._testTimer); self._sendToJavascript(self._listener+"onInit();"); }; this._sound.start(); this._testTimer = setInterval(this, '_onSoundFail', 1000); } /** * Lancé par mtasc */ static function main():Void { _root.method = new Mp3Player(); } private function _onSoundFail():Void { _debug("Sound init failed"); clearInterval(this._testTimer); this._sendToJavascript(this._listener+"onInitFail();"); } private function _debug(msg:String):Void { if (this._enableDebug) { this._sendToJavascript(_listener + 'writeDebug("Flash: ' + _escapeJS(msg) + '");'); } } private function _escapeJS(subject:String):String { return subject.split('"').join('\\"'); } /** * Send command to javascript */ private function _sendToJavascript(pCommand:String) { if (System.capabilities.playerType == "ActiveX") { fscommand("update", pCommand); } else { getURL("javascript:"+pCommand); } } /** * Mise à jour des données, renvoie du code JS qui ne contient que ce qui a été modifié depuis la dernière fois */ private function _updateSentDatas(variables:Object):String { var js:String = ""; for (var i:String in variables) { if (this._lastSentDatas[i] != variables[i]) { js += _listener + i + "=" + variables[i] + ";"; this._lastSentDatas[i] = variables[i]; } } return js; } /** * Lance le timer qui lance _update() */ private function _startPolling():Void { _debug("Starting polling"); clearInterval(_updateTimer); // Just to be sure _updateTimer = setInterval(this, "_update", _updateInterval); } /** * Arrête le timer */ private function _stopPolling():Void { _debug("Stopping polling"); clearInterval(_updateTimer); this._lastPolling = 0; } /** * Charge un mp3 */ private function _setTrack(url:String):Void { _debug("Loading "+url); this._url = url; this._position = 0; this._sound = new Sound(); this._sound.setVolume(this.volume); this._sound.onLoad = function() { _root.method._update(true); }; this._sound.onSoundComplete = function() { var self:Object = _root.method; self._stop(); self._sendToJavascript(self._listener + "onSoundComplete();"); }; this._sound.onID3 = function() { var self:Object = _root.method; var js:String = ""; for (var key:String in self._sound.id3) { js += key + ':"' + self._escapeJS(self._sound.id3[key]) + '",'; } if (js.length > 1) { js = self._listener + "id3={" + js.slice(0, -1) + "};"; self._sendToJavascript(js + self._listener + "onID3();"); } }; this._lastValues = { bytes: 0, position: 0, loaded: false }; this._lastSentDatas = { bytesLoaded: -1, bytesTotal: -1, bytesPercent: -1, duration: -1, position: -1, volume: -1, isPlaying: -1, isPaused: -1 }; //this._startPolling(); } /** * Fonction qui suit l'avancement des données et renvoie à JS régulièrement */ private function _update(forceUpdate:Boolean):Void { var bytesLoaded:Number = this._sound.getBytesLoaded(); var bytesTotal:Number = this._sound.getBytesTotal(); var duration:Number = this._sound.duration||0; // From soundManager (thanks) var position:Number = this._sound.position; if (typeof forceUpdate == "undefined") forceUpdate = false; // To check if something was updated this time var callWhileLoading:Boolean = false; var callUpdate:Boolean = false; // Update bytes value if (bytesLoaded && bytesTotal && bytesLoaded != this._lastValues.bytes) { this._lastValues.bytes = bytesLoaded; callWhileLoading = true; } // Update track properties but don't hang trying to update nothing if (typeof this._sound.position != "undefined" && position != this._lastValues.position) { this._lastValues.position = position; callUpdate = true; } // We're polling the JS at some interval if ((this._lastPolling >= this._pollJsEach && (callWhileLoading || callUpdate)) || forceUpdate) { this._lastPolling = 0; var variables:Object = new Object(); if (this._lastSentDatas['bytesLoaded'] != bytesLoaded) { variables['bytesTotal'] = bytesTotal; variables['bytesLoaded'] = bytesLoaded; variables['bytesPercent'] = Math.round(bytesLoaded / bytesTotal * 100); } variables['position'] = position; variables['duration'] = duration; variables['isPlaying'] = this.isPlaying.toString(); variables['isPaused'] = this.isPaused.toString(); variables['volume'] = this.volume; var js:String = this._updateSentDatas(variables); /* if (callWhileLoading) { //js += _listener + "onWhileLoading();"; } */ js += _listener + "onUpdate();"; _debug(js); _sendToJavascript(js); // We don't need to keep the timer running when sound is stopped and file is loaded if (bytesLoaded >= bytesTotal && !this.isPlaying) { this._stopPolling(); } } this._lastPolling++; } private function _play(n:String):Void { _debug("Play"); if (this._lastValues.loaded == false) { this._sound.loadSound(this._url, true); this._lastValues.loaded = true; } else { this._sound.start(Math.round(this._position / 1000), 0); } this.isPlaying = true; this.isPaused = false; this._startPolling(); } private function _stop():Void { _debug("Stop"); this._position = 0; this._sound.start(0); this._sound.stop(); this.isPlaying = false; this.isPaused = false; this._update(true); } private function _pause():Void { _debug("Pause"); this._position = this._sound.position; this._sound.stop(); this.isPlaying = false; this.isPaused = true; this._update(true); } //////////////////////////////////////////////////////////////////////////////// // Méthodes JS public function set setPosition(pPosition:Number):Void { _debug("Set position "+pPosition); this._position = pPosition; this._sound.start(Math.round(this._position / 1000)); this._sound.stop(); if (this.isPlaying) { this._play(); } } public function set setVolume(pVolume:Number):Void { this.volume = Number(pVolume); this._sound.setVolume(this.volume); } public function set play(n:String):Void { this._play(); } public function set pause(n:String):Void { this._pause(); } public function set stop(n:String):Void { this._stop(); } public function set setUrl(n:String):Void { this._setTrack(n); } }