/* Copyright (c) 2009 Yahoo! Inc. All rights reserved. The copyrights embodied in the content of this file are licensed under the BSD (revised) open source license */ package com.yahoo.astra.fl.controls.mediaPlayerClasses { import flash.events.*; import flash.media.Sound; import flash.media.SoundChannel; import flash.media.SoundTransform; import flash.media.SoundLoaderContext; import com.yahoo.astra.fl.controls.mediaPlayerClasses.IMediaClip; import flash.net.URLRequest; import fl.core.UIComponent; import com.yahoo.astra.fl.events.MediaEvent; import flash.utils.Timer; import flash.display.DisplayObjectContainer; //-------------------------------------- // Events //-------------------------------------- /** * Dispatched when on an interval while the clip is playing. * * @eventType com.yahoo.astra.fl.events.MediaEvent.MEDIA_POSITION * * @langversion 3.0 * @playerversion Flash 9.0.28.0 */ [Event(name="mediaPosition", type="com.yahoo.astra.fl.events.MediaEvent")] /** * Dispatched when the clip is paused or played. * * @eventType com.yahoo.astra.fl.events.MediaEvent.MEDIA_PLAY_PAUSE * * @langversion 3.0 * @playerversion Flash 9.0.28.0 */ [Event(name="mediaPlayPause", type="com.yahoo.astra.fl.events.MediaEvent")] /** * Dispatched when the volume level is changed. * * @eventType com.yahoo.astra.fl.events.MediaEvent.VOLUME_CHANGE * * @langversion 3.0 * @playerversion Flash 9.0.28.0 */ [Event(name="volumeChange", type="com.yahoo.astra.fl.events.MediaEvent")] /** * Dispatched when the id3 event is received. * * @eventType com.yahoo.astra.fl.events.MediaEvent.INFO_CHANGE * * @langversion 3.0 * @playerversion Flash 9.0.28.0 */ [Event(name="infoChange", type="com.yahoo.astra.fl.events.MediaEvent")] /** * Dispatched when a clip has completely loaded. * * @eventType com.yahoo.astra.fl.events.MediaEvent.MEDIA_LOADED * * @langversion 3.0 * @playerversion Flash 9.0.28.0 */ [Event(name="mediaLoaded", type="com.yahoo.astra.fl.events.MediaEvent")] /** * Dispatched when a clip reaches its end point. * * @eventType com.yahoo.astra.fl.events.MediaEvent.MEDIA_ENDED * * @langversion 3.0 * @playerversion Flash 9.0.28.0 */ [Event(name="mediaEnded", type="com.yahoo.astra.fl.events.MediaEvent")] //-------------------------------------- // Class description //-------------------------------------- /** * The AudioClip class extends the EventDispatcher class and wraps the * Sound, SoundChannel, SoundLoaderContext and SoundTransform classes. * * @see flash.media.Sound * @see flash.media.SoundChannel * @see flash.media.SoundTransform * @see flash.media.SoundLoaderContext * @see flash.events.EventDispatcher * * @langversion 3.0 * @playerversion Flash 9.0.28.0 */ public class AudioClip extends EventDispatcher implements IMediaClip { //-------------------------------------- // Constructor //-------------------------------------- /** * Constructor * * @langversion 3.0 * @playerversion Flash 9.0.28.0 */ public function AudioClip():void { super(); _soundTransform = new SoundTransform(_volume, _panning); _positionTimer = new Timer(50); _positionTimer.addEventListener(TimerEvent.TIMER, positionUpdateHandler); } //-------------------------------------- // Properties //-------------------------------------- /** * @private (protected) */ protected var _mediaAvailable:Boolean = false; /** * @private (protected) */ protected var _length:Number = 0; /** * @private (protected) */ protected var _artist:String; /** * @private (protected) */ protected var _title:String; /** * @private (protected) * The Sound object */ protected var _clip:Sound; /** * @private (protected) * The SoundChannel */ protected var _channel:SoundChannel; /** * @private (protected) * Timer used to dispatch "mediaPosition" events. */ protected var _positionTimer:Timer; /** * @private (protected) * place holder for position. */ protected var _position:Number; /** * @private (protected) * indicates whether the state of the clip is paused */ protected var _playing:Boolean = true; /** * @private (protected) * indicates whether the sound is muted */ protected var _mute:Boolean = false; /** * @private (protected) */ protected var _volume:Number = 1; /** * @private (protected) */ protected var _panning:Number = 0; /** * @private (protected) */ protected var _soundTransform:SoundTransform; /** * @private (protected) */ protected var _autoStart:Boolean = false; /** * Gets the url to be used for an AudioClip (read-only) */ public function get url():String { return _clip.url; } /** * Gets or sets the position of the playhead */ public function get position():Number { return _channel.position; } /** * @private (setter) * sets the position for a clip (cues and rewinds) * if the clip is paused, the _position property is set to the received value * otherwise the clip is played from that position * * @langversion 3.0 * @playerversion Flash 9.0.28.0 */ public function set position(value:Number):void { if(_mediaAvailable) { _position = Math.min(value, _length); _channel.removeEventListener(Event.SOUND_COMPLETE, soundCompleteHandler); _channel = _clip.play(_position); _channel.addEventListener(Event.SOUND_COMPLETE, soundCompleteHandler); _channel.soundTransform = _soundTransform; if(!_playing) pause(); dispatchEvent(new MediaEvent(MediaEvent.MEDIA_POSITION, false, false, _playing?Number(_channel.position.toFixed(2)):_position, _length, _soundTransform.volume, _mute)); } } /** * Length of the Audio (read-only) */ public function get length():Number { return _length; } /** * Boolean indicating if the audio is playing. (read-only) us pause() and play() methods to change playing */ public function get playing():Boolean { return _playing; } /** * Gets or sets the volume, ranging from 0 (silent) to 1 (full volume). */ public function get volume():Number { return _soundTransform.volume; } /** * @private (setter) * * @langversion 3.0 * @playerversion Flash 9.0.28.0 */ public function set volume(value:Number):void { //sets the volume property of the _soundTransform object //sets the _soundTransform object to the _channel //dispatches a VOLUME_CHANGE MediaEvent _soundTransform.volume = _volume = Math.max(0, Math.min(value, 1)); _mute = false; if(_mediaAvailable) { _channel.soundTransform = _soundTransform; dispatchEvent(new MediaEvent(MediaEvent.VOLUME_CHANGE, false, false, Number(_channel.position.toFixed(2)), _length, Number(_channel.soundTransform.volume), _mute)); } else { dispatchEvent(new MediaEvent(MediaEvent.VOLUME_CHANGE, false, false, _position, 0, _soundTransform.volume, _mute)); } } /** * Gets or sets the mute property of the AudioClip */ public function get mute():Boolean { return _mute; } /** * @private (setter) * Sets the mute property of the audio */ public function set mute(value:Boolean):void { //sets the value of _mute //sets the volume of the _soundTransform object to 0 if +_mute is true and to _volume if _mute is false //sets the _soundTransform object to the _channel //dispatches a VOLUME_CHANGE MediaEvent _mute = value; _soundTransform.volume = _mute?0:_volume; if(_mediaAvailable) { _channel.soundTransform = _soundTransform; dispatchEvent(new MediaEvent(MediaEvent.VOLUME_CHANGE, false, false, Number(_channel.position.toFixed(2)), _length, Number(_channel.soundTransform.volume), _mute)); } else { dispatchEvent(new MediaEvent(MediaEvent.VOLUME_CHANGE, false, false, _position, 0, _soundTransform.volume, _mute)); } } /** * Gets the artist for the audio clip (read-only). This information is available: * * */ public function get artist():String { return _artist; } /** * Gets the title of the clip (read-only). This information is available: * * */ public function get title():String { return _title; } /** * Gets or sets the autoStart property of an AudioClip */ public function get autoStart():Boolean { return _autoStart; } /** * @private (setter) */ public function set autoStart(value:Boolean):void { _autoStart = value; } /** * @private (protected) */ protected var _soundLoaderContext:SoundLoaderContext; /** * @private (protected) */ protected var _bufferTime:Number = 1000; /** * Gets or sets buffer time. (milliseconds) */ public function get bufferTime():Number { return _bufferTime; } /** * @private (setter) */ public function set bufferTime(value:Number):void { _bufferTime = value; } /** * @private (protected) */ protected var _checkForPolicyFile:Boolean = true; /** * Gets or sets checkForPolicyFile property of the SoundLoaderContext. Specifies whether Flash Player should check * for the existence of a cross-domain policy file upon loading the object (true) or not. * * @see flash.media.SoundLoaderContext; * */ public function get checkForPolicyFile():Boolean { return _checkForPolicyFile; } /** * @private (setter) */ public function set checkForPolicyFile(value:Boolean):void { _checkForPolicyFile = value; } //-------------------------------------- // Public Methods //-------------------------------------- /** * Loads an audio clip * * @param urlValue string for the location of the clip * @param autoStart Boolean indicating whether the clip starts playing when it loads * * @langversion 3.0 * @playerversion Flash 9.0.28.0 */ public function loadMedia(urlValue:String, autoStart:Boolean = true):void { _soundLoaderContext = new SoundLoaderContext(bufferTime, checkForPolicyFile); _length = 0; _playing = autoStart; _mediaAvailable = true; _position = 0; var request:URLRequest = new URLRequest(urlValue); if(_clip != null) { removeClipListeners(_clip); _positionTimer.reset(); } if(_channel != null) { if(_channel.hasEventListener(Event.SOUND_COMPLETE)) _channel.removeEventListener(Event.SOUND_COMPLETE, soundCompleteHandler); _channel.stop(); } _clip = new Sound(); addClipListeners(_clip); _clip.load(request, _soundLoaderContext); _channel = _clip.play(); _channel.addEventListener(Event.SOUND_COMPLETE, soundCompleteHandler); _channel.soundTransform = _soundTransform; _positionTimer.start(); dispatchEvent(new MediaEvent(MediaEvent.MEDIA_POSITION, false, false, _position, _length, _channel.soundTransform.volume, _mute)); dispatchEvent(new MediaEvent(MediaEvent.MEDIA_PLAY_PAUSE, false, false, _position, _length, _channel.soundTransform.volume, _mute)); dispatchEvent(new MediaEvent(MediaEvent.VOLUME_CHANGE, false, false, _position, _length, _channel.soundTransform.volume, _mute)); if(!autoStart) { pause(); _position = 0; } //dispatchEvent(new MediaEvent(MediaEvent.MEDIA_READY)); } /** * Pauses the audio * * @langversion 3.0 * @playerversion Flash 9.0.28.0 */ public function pause():void { //sets pause to true //dispatches a MEDIA_PLAY_PAUSE event //calls the stop function if(_mediaAvailable) { _playing = false; dispatchEvent(new MediaEvent(MediaEvent.MEDIA_PLAY_PAUSE, false, false, Number(_channel.position.toFixed(2)), _length, Number(_channel.soundTransform.volume), _mute)); stop(); _positionTimer.stop(); } } /** * Plays the audio * * @langversion 3.0 * @playerversion Flash 9.0.28.0 */ public function play():void { //sets _playing to true //dispatches a MEDIA_PLAY_PAUSE event //set the SoundChannel object to the play method of the Sound object using the _position value if(_mediaAvailable) { _positionTimer.start(); _playing = true; dispatchEvent(new MediaEvent(MediaEvent.MEDIA_PLAY_PAUSE, false, false, Number(_channel.position.toFixed(2)), _length, Number(_channel.soundTransform.volume), _mute)); _channel.removeEventListener(Event.SOUND_COMPLETE, soundCompleteHandler); _channel = _clip.play(_position); _channel.addEventListener(Event.SOUND_COMPLETE, soundCompleteHandler); _channel.soundTransform = _soundTransform; } } /** * Stops the audio * * @langversion 3.0 * @playerversion Flash 9.0.28.0 */ public function stop():void { //sets _position to the current position of the sound object //calls the stop method on the SoundChannel object //used by pause method, to stop the playback when audio is in a paused state //used by the controller to stop the video while the MediaScrubber seeks if(_mediaAvailable) { _position = Number(_channel.position.toFixed(2)); _channel.stop(); } } //-------------------------------------- // Protected Methods //-------------------------------------- /** * @private (protected) * Dispatches a MediaEvent.MEDIA_POSITION event with the _positionTimer event * @param event The TimerEvent * * @langversion 3.0 * @playerversion Flash 9.0.28.0 */ protected function positionUpdateHandler(event:TimerEvent):void { //if paused, update with the _position value. if not, update with the actual position of the sound channel dispatchEvent(new MediaEvent(MediaEvent.MEDIA_POSITION, false, false, _playing?Number(_channel.position.toFixed(2)):_position, _length, Number(_channel.soundTransform.volume), _mute)); } /** * @private (protected) * Adds event listeners to a sound object when set clip is called. * @param soundDispatcher * * @langversion 3.0 * @playerversion Flash 9.0.28.0 */ protected function addClipListeners(soundDispatcher:Sound):void { soundDispatcher.addEventListener(Event.COMPLETE, completeHandler); soundDispatcher.addEventListener(Event.ID3, id3Handler); soundDispatcher.addEventListener(ProgressEvent.PROGRESS, progressHandler); soundDispatcher.addEventListener(IOErrorEvent.IO_ERROR, ioErrorHandler); } /** * @private (protected) * Removes listeners from the sound object before a new clip is set * @param soundDispatcher * * @langversion 3.0 * @playerversion Flash 9.0.28.0 */ protected function removeClipListeners(soundDispatcher:Sound):void { soundDispatcher.removeEventListener(Event.COMPLETE, completeHandler); soundDispatcher.removeEventListener(Event.ID3, id3Handler); soundDispatcher.removeEventListener(ProgressEvent.PROGRESS, progressHandler); soundDispatcher.removeEventListener(IOErrorEvent.IO_ERROR, ioErrorHandler); } /** * @private (protected) * Resets a clip to the beginning and dispatches MediaEvent.MEDIA_POSITION and MediaEvent.MEDIA_ENDED events. * @param event soundComplete event * * @langversion 3.0 * @playerversion Flash 9.0.28.0 */ protected function soundCompleteHandler(event:Event):void { pause(); position = 0; //does not always fire. may need to add a catch in the timer to force an end dispatchEvent(new MediaEvent(MediaEvent.MEDIA_POSITION, false, false, _playing?Number(_channel.position.toFixed(2)):_position, _length, _soundTransform.volume, _mute)); dispatchEvent(new MediaEvent(MediaEvent.MEDIA_ENDED, false, false, _playing?Number(_channel.position.toFixed(2)):_position, _length, _soundTransform.volume, _mute)); } /** * @private (protected) * Dispatches a MediaEvent.MEDIA_LOADED event when the media has loaded * @param event Event.COMPLETE event * * @langversion 3.0 * @playerversion Flash 9.0.28.0 */ protected function completeHandler(event:Event):void { _length = _clip.length; dispatchEvent(new MediaEvent(MediaEvent.MEDIA_LOADED)); } /** * @private (protected) * Dispatches a MediaEvent.INFO_CHANGE event when the ID3 event is received from the Sound object * * @langversion 3.0 * @playerversion Flash 9.0.28.0 */ protected function id3Handler(event:Event):void { try { _title = _clip.id3.songName as String; _artist = _clip.id3.artist as String; } catch(e:SecurityError) { _artist = "Artist Unavailable"; _title = "Title Unavailable"; } dispatchEvent(new MediaEvent(MediaEvent.INFO_CHANGE, false, false)); } /** * @private * dispatches the ProgressEvent to registered listeners * * @langversion 3.0 * @playerversion Flash 9.0.28.0 */ protected function progressHandler(event:ProgressEvent):void { _length = _clip.length * (event.bytesTotal/event.bytesLoaded); dispatchEvent(event); } /** * @private * * @langversion 3.0 * @playerversion Flash 9.0.28.0 */ protected function ioErrorHandler(event:IOErrorEvent):void { //need to reset everything here. _mediaAvailable = false; _positionTimer.stop(); _length = 0; _artist = "Artist Unavailable"; _title = "Title Unavailable"; dispatchEvent(new MediaEvent(MediaEvent.INFO_CHANGE, false, false)); } public function kill():void { if(_mediaAvailable) { pause(); dispatchEvent(new MediaEvent(MediaEvent.MEDIA_POSITION, false, false, _playing?Number(_channel.position.toFixed(2)):_position, _length, _soundTransform.volume, _mute)); dispatchEvent(new MediaEvent(MediaEvent.MEDIA_ENDED, false, false, _playing?Number(_channel.position.toFixed(2)):_position, _length, _soundTransform.volume, _mute)); } } } }