The ActionScript 3.0 sound architecture is
powerful but complex. Applications that only need basic sound loading
and playback features can use a class that hides some of the complexity
by providing a simpler set of method calls and events. In the world
of software design patterns, such a class is called a facade.
The SoundFacade class presents a single interface for performing
the following tasks:
Loading sound files using a Sound object, a SoundLoaderContext
object, and the SoundMixer class
Playing sound files using the Sound object and the SoundChannel
object
Dispatching playback progress events
Pausing and resuming playback of the sound using the Sound
object and the SoundChannel object
The SoundFacade class tries to offer most of the functionality
of the ActionScript sound classes with less complexity.
The following code shows the class declaration, the class properties,
and the SoundFacade() constructor method:
public class SoundFacade extends EventDispatcher
{
public var s:Sound;
public var sc:SoundChannel;
public var url:String;
public var bufferTime:int = 1000;
public var isLoaded:Boolean = false;
public var isReadyToPlay:Boolean = false;
public var isPlaying:Boolean = false;
public var isStreaming:Boolean = true;
public var autoLoad:Boolean = true;
public var autoPlay:Boolean = true;
public var pausePosition:int = 0;
public static const PLAY_PROGRESS:String = "playProgress";
public var progressInterval:int = 1000;
public var playTimer:Timer;
public function SoundFacade(soundUrl:String, autoLoad:Boolean = true,
autoPlay:Boolean = true, streaming:Boolean = true,
bufferTime:int = -1):void
{
this.url = soundUrl;
// Sets Boolean values that determine the behavior of this object
this.autoLoad = autoLoad;
this.autoPlay = autoPlay;
this.isStreaming = streaming;
// Defaults to the global bufferTime value
if (bufferTime < 0)
{
bufferTime = SoundMixer.bufferTime;
}
// Keeps buffer time reasonable, between 0 and 30 seconds
this.bufferTime = Math.min(Math.max(0, bufferTime), 30000);
if (autoLoad)
{
load();
}
}
The SoundFacade class extends the EventDispatcher class so that
it can dispatch its own events. The class code first declares properties
for a Sound object and a SoundChannel object. The class also stores
the value of the URL of the sound file and a bufferTime property
to use when streaming the sound. In addition, it accepts some Boolean
parameter values that affect the loading and playback behavior:
The autoLoad parameter tells the object
that sound loading should start as soon as this object is created.
The autoPlay parameter indicates that sound
playing should start as soon as enough sound data has been loaded.
If this is a streaming sound, playback will begin as soon as enough
data, as specified by the bufferTime property, has
loaded.
The streaming parameter indicates that this
sound file can start playing before loading has completed.
The bufferTime parameter defaults to a value
of -1. If the constructor method detects a negative value in the bufferTime parameter,
it sets the bufferTime property to the value of SoundMixer.bufferTime.
This lets the application default to the global SoundMixer.bufferTime value
as desired.
If the autoLoad parameter is set to true,
the constructor method immediately calls the following load() method
to start loading the sound file:
public function load():void
{
if (this.isPlaying)
{
this.stop();
this.s.close();
}
this.isLoaded = false;
this.s = new Sound();
this.s.addEventListener(ProgressEvent.PROGRESS, onLoadProgress);
this.s.addEventListener(Event.OPEN, onLoadOpen);
this.s.addEventListener(Event.COMPLETE, onLoadComplete);
this.s.addEventListener(Event.ID3, onID3);
this.s.addEventListener(IOErrorEvent.IO_ERROR, onIOError);
this.s.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onIOError);
var req:URLRequest = new URLRequest(this.url);
var context:SoundLoaderContext = new SoundLoaderContext(this.bufferTime, true);
this.s.load(req, context);
}
The load() method creates a new Sound object
and then adds listeners for all of the important sound events. Then
it tells the Sound object to load the sound file, using a SoundLoaderContext
object to pass in the bufferTime value.
Because the url property can be changed, a SoundFacade
instance can be used to play different sound files in succession:
simply change the url property and call the load() method,
and the new sound file will be loaded.
The following three event listener methods show how the SoundFacade
object tracks loading progress and decides when to start playing
the sound:
public function onLoadOpen(event:Event):void
{
if (this.isStreaming)
{
this.isReadyToPlay = true;
if (autoPlay)
{
this.play();
}
}
this.dispatchEvent(event.clone());
}
public function onLoadProgress(event:ProgressEvent):void
{
this.dispatchEvent(event.clone());
}
public function onLoadComplete(event:Event):void
{
this.isReadyToPlay = true;
this.isLoaded = true;
this.dispatchEvent(evt.clone());
if (autoPlay && !isPlaying)
{
play();
}
}
The onLoadOpen() method executes when sound
loading starts. If the sound can be played in streaming mode, the onLoadComplete() method
sets the isReadyToPlay flag to true right
away. The isReadyToPlay flag determines whether
the application can start the sound playing, perhaps in response to
a user action like clicking a Play button. The SoundChannel class
manages the buffering of sound data, so there is no need to explicitly
check whether enough data has been loaded before calling the play() method.
The onLoadProgress() method executes periodically
during the loading process. It simply dispatches a clone of its
ProgressEvent object for use by code that uses this SoundFacade
object.
When the sound data has been fully loaded the onLoadComplete() method executes,
calling the play() method for non-streaming sounds
if needed. The play() method itself is shown below.
public function play(pos:int = 0):void
{
if (!this.isPlaying)
{
if (this.isReadyToPlay)
{
this.sc = this.s.play(pos);
this.sc.addEventListener(Event.SOUND_COMPLETE, onPlayComplete);
this.isPlaying = true;
this.playTimer = new Timer(this.progressInterval);
this.playTimer.addEventListener(TimerEvent.TIMER, onPlayTimer);
this.playTimer.start();
}
}
}
The play() method calls the Sound.play() method
if the sound is ready to play. The resulting SoundChannel object
is stored in the sc property. The play() method
then creates a Timer object that will be used to dispatch playback
progress events at regular intervals.