Sound, Part 3: Interactive Sound
Now we move to an even more sophisticated application of the Sound object: playing a sound subject to user control through mouse events. Here we'll learn how to play and pause a sound, so that it resumes playing at the stop point. We'll also explain how to clean up once the sound has reached its natural conclusion.
First, we'll detail the simplest form of this project: a page with a single Movie-Clip-as-button, acting as a play/pause control. Below, we'll show a larger project, with eight instances of the sound control (slightly improved). This project demonstrates an important feature of the Flash Player: its ability to play up to eight sounds simultaneously.
I. Architecture of the basic project
The main movie file for the basic project is called startStop.fla. As indicated, there is only one Symbol in its Library: a Movie Clip (containing the text you see above), designed to act as if it were a button. This Movie Clip is linked to soundBtnClass.as: to which we now proceed.
II. soundBtnClass
This class file starts with the set of imports that should now be familiar, including flash.media.Sound, and flash.net.URLRequest. In addition, we also import flash.media.SoundChannel -- a new object to which we'll come presently.
Within the class body, we declare some familiar variables:
//variables needed for Sound object
var soundReq:URLRequest = new URLRequest("http://iat.ubalt.edu/courses/
idia619.185_Sp08/swf/soundDemos/interactive/tbl.mp3");
var snd:Sound = new Sound();
var sndChan:SoundChannel = new SoundChannel();
Once again, we're using an MP3 sound file stored in the same directory as the SWF. As we noted with our animation sound examplme, a fully-qualified URL is required to embed the SWF into the Web page you are now reading, probably because of the way the Flash Player interprets pages that use Apache Server-Side Includes. For simpler pages, a relative URL may do:
var soundReq:URLRequest = new URLRequest("tbl.mp3");
We also declare and instantiate a Sound object called snd, and also a SoundChannel called sndChan.
SoundChannel is an auxiliary object that essentially provides a control mechanism for sound files, or that is, a control set that goes beyond the automatic start/stop functions we have seen so far. You need to use a SoundChannel object whenever you want a sound to come under user-managed, or interactive control. This is the case whenver a sound is to be managed by mouse events, or (as we'll see in a later posting), if you want volume or stereo channel mixing to be changed in some way.
Using a SoundChannel adds complexity to your code, but its necessary if you want to do more sophisticated things with sound.
Next, we declare some variables that affect the Sound or SoundChannel objects less directly. You'll see how they come into play as we go along.
var isReady:Boolean = false; var isPlaying:Boolean = false; var resumePoint:Number = 0;
Here's the Constructor for our soundBtnClass:
function soundBtnClass():void
{
buttonMode = true;
addEventListener(MouseEvent.CLICK, soundToggle);
//initialize the Sound
snd.load(soundReq);
snd.addEventListener(Event.COMPLETE, soundGood);
}
As with previous pseudo-button Movie Clips, we set buttonMode to true, enabling cursor change when the user mouses over the clip. We also set up a listener for the CLICK event.
Next, we initialize snd, the Sound object we instantiated earlier. As always, we then give it a listener/callback mechanism.
In previous examples, we've used the callback method to play our sound. However, in this example, we'll be using the SoundChannel object to handle play control, and we want that object to wait until there has been a mouse click. Since we still want to know if our sound is fully loaded before we try to play it, however, we do want something to happen in the callback. So we set a flag:
function soundGood(event:Event):void
{
isReady = true;
}
Now we come to soundToggle, the callback for the CLICK event listener, which is where the main action of this class resides:
function soundToggle(event:MouseEvent):void
{
if(isReady == true)
{
if(isPlaying == false)
{
sndChan = snd.play(resumePoint);
isPlaying = true;
sndChan.addEventListener(Event.SOUND_COMPLETE, autoSoundOff)
}
else
{
resumePoint = sndChan.position;
sndChan.stop();
isPlaying = false;
}
}
}
Note that everything within this method sits inside a test on the flag variable isReady, which tells us whether the sound has finished loading. Assuming we have sound data, we then proceed to an if/else structure, testing another flag variable called isPlaying. As the name suggests, this variable tells us in which state we find the Sound object: playing or paused.
If the Sound object is not currently playing, then we want to start it up. This is done by issuing the somewhat odd statement:
sndChan = snd.play(resumePoint);
We'll explain the meaning of resumePoint in just a bit. For the moment, note that you make a SoundChannel do its thing by passing it a method call on a particular Sound object (in our case, snd). This is not consistent with other syntax, we know. Sigh.
While we're starting things up, we set our play/pause flag to true, and then add a new event listener to the SoundChannel object. This listener waits for the SOUND_COMPLETE event -- WHICH YOU MUST NOT CONFUSE WITH THE COMPLETE EVENT of the Movie Clip object. The former (SOUND_COMPLETE) is generated when a sound, playing within a SoundChannel, has played the last of its data, or completed its assigned repetitions. The latter (COMPLETE) is generated when a Sound object has finished loading data -- not when anything is playing.
You will probably find this highly confusing. This feature of the language has not been well designed. Nonetheless, we're stuck with it.
We'll consider the callback to this listener momentarily. Right now, let's look at the other branch of the if/else, which comes into play if the Sound object is currently playing. In this case, we want to pause the playback.
To do this, we're going to invoke the stop() method of the SoundChannel (confusingly, we don't use the same syntax we used to start playback); but before we get there, we issue this statement:
resumePoint = sndChan.position;
Here we record the current playback position of our sound -- a number reflecting the milliseconds of playback time that have elapsed -- in the variable resumePoint. Note that we used resumePoint to set the offset amount of our sound when we started it playing in the other branch of the if/else.
Doing all this gives us a virtual pause button. Toggling the button back to play resumes playback more or less where we left off. (Your observed behavior may vary, depending on quality of the sound file with which you are working.)
The last element of our button is that callback for the SOUND_COMPLETE event, which comes into action if playback reaches the end of the sound file:
//set sounder back to non-playing if sound has played through
function autoSoundOff(event:Event):void
{
sndChan.stop();
resumePoint = 0;
isPlaying = false;
sndChan.removeEventListener(Event.SOUND_COMPLETE, autoSoundOff);
}
This method stops playback, sets the resume or offset point back to 0, sets the play/pause flag back to false, and then (importantly) removes the listener that triggered it. This last bit of business is an important piece of design.
III. Eight-track audio!
The demonstration project included above scales up from one sound to the maximum of eight that can be handled simultaneously by the Flash Player. Each gray ball is linked to a slightly modified version of the class code just explained. Some commands are added to manage timeline effects, and there is also a mechanism to determine which of the eight sound controls is running the code; this mechanism determines which of eight possible MP3 files is loaded.
Click on any of the objects to start its sound playing. Click again to pause the sound, and a third time to resume. You may, of course, click on all eight objects for the full noise effect. The sound objects return automatially to their initial state when the sound files run all the way through their data.
IV. Source files
Source files and sound resources for both projects discussed in this posting are available in the folder interactive, within soundDemos in the shared account on student-iat. To work with these files on your own system, download the MP3 file tbl.mp3, as well as the folder snd and the eight MP3s it contains.
The main movie for the first, simpler example is called startStop.fla. Its associated class file is called sndBtnClass.as. The main movie for the eight-track example is called multiSound.fla, and its class file is called soundClass.as.
|
|