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.