Summary: Learn to play sounds in ActionScript, both continuously and on call. Also learn how handle events fired by SoundChannel objects.
This tutorial lesson is part of a series of lessons dedicated to object-oriented programming (OOP) with ActionScript.
Several ways to create and launch ActionScript programs
There are several ways to create and launch programs written in the ActionScript programming language. Many of the lessons in this series will use Adobe Flex as the launch pad for the sample ActionScript programs.
An earlier lesson titled The Default Application Container provided information on how to get started programming with Adobe's Flex Builder 3. The lesson titled Using Flex 3 in a Flex 4 World was added later to accommodate the release of Flash Builder 4. (See Baldwin's Flex programming website .) You should study those lessons before embarking on the lessons in this series.
Some understanding of Flex MXML will be required
I also recommend that you study all of the lessons on Baldwin's Flex programming website in parallel with your study of these ActionScript lessons. Eventually you will probably need to understand both ActionScript and Flex and the relationships that exist between them in order to become a successful ActionScript programmer.
Will emphasize ActionScript code
It is often possible to use either ActionScript code or Flex MXML code to achieve the same result. Insofar as this series of lessons is concerned, the emphasis will be on ActionScript code even in those cases where Flex MXML code may be a suitable alternative.
I recommend that you open another copy of this document in a separate browser window and use the following links to easily find and view the figures and listings while you are reading about them.
I recommend that you also study the other lessons in my extensive collection of online programming tutorials. You will find a consolidated index at www.DickBaldwin.com .
A sound in ActionScript is represented by an object of the class named Sound .
When the information encapsulated in a Sound object is played , that process is represented by an object of the class named SoundChannel .
According to the documentation :
"The Sound class lets you work with sound in an application. The Sound class lets you create a new Sound object, load and play an external MP3 file into that object, close the sound stream, and access data about the sound, such as information about the number of bytes in the stream and ID3 metadata. More detailed control of the sound is performed through the sound source -- the SoundChannel or Microphone object for the sound -- and through the properties in the SoundTransform class that control the output of the sound to the computer's speakers."
The program that I will explain in this lesson makes use of events and methods of the Sound class and the SoundChannel class.
You will find a lot of interesting and useful information in the document titled Basics of working with sound on the Adobe website, including the following:
"Although there are various sound file formats used to encode digital audio, ActionScript 3.0 and Flash Player support sound files that are stored in the mp3 format. They cannot directly load or play sound files in other formats like WAV or AIFF."
A variety of different sound file converter programs are available on the web that can be used to convert other sound file formats into mp3 format.
Run the ActionScript program named Sound03
If you have the Flash Player plug-in (version 10 or later) installed in your browser, click here to run the program named Sound03 .
If you don't have the proper Flash Player installed, you should be notified of that fact and given an opportunity to download and install the Flash Player plug-in program.
Demonstrates the use of sound with ActionScript
This project is intended to demonstrate the use of sound with ActionScript. It also displays the image of a cloudy sky shown in Figure 1, but the only purpose of the image is to make it obvious when the program starts running.
| Sound03 image. |
|---|
![]() |
Stormy weather
The project is designed to give you the impression of the sounds that you might hear while sitting on your covered deck looking at the sky during a thunder storm.
Four stormy-weather sounds
The project plays the following sounds extracted from mp3 files:
The rain sound is continuous.
The wind sound is played on startup and then occasionally thereafter on the basis of a random number generator.
The sizzle sound is also played occasionally on the basis of a random number generator. (You will probably need to be patient to hear this sound because it isn't played very often.) As soon as the sizzle sound finishes, the sound of a thunder clap is played.
The project file structure
The final project file structure, captured from the FlashDevelop project window, is shown in Figure 2.
| Project file structure for Sound03. |
|---|
![]() |
As you can see in Figure 2, all of the sound and image files are stored in the folder named src . In addition, all of the sound files were manually copied into the folder named bin .
Will explain in fragments
I will explain the code for this program in fragments. Complete listings of the MXML code and the ActionScript code are provided in Listing 10 and Listing 11 near the end of the lesson.
The MXML code is shown in Listing 1 and also in Listing 10 for your convenience.
<?xml version="1.0" encoding="utf-8"?>
<!--
This project is intended to demonstrate the use of sound
with ActionScript. See the file named Driver.as for more
information
-->
<mx:Application
xmlns:mx="http://www.adobe.com/2006/mxml"
xmlns:cc="CustomClasses.*">
<cc:Driver/>
</mx:Application>As is often the case in this series of tutorial lessons, the MXML file is very simple because the program was coded almost entirely in ActionScript. The MXML code simply instantiates an object of the Driver class. From that point forward, the behavior of the program is controlled by ActionScript code.
Import directives for the Driver class
The code for the Driver class begins in Listing 2, which shows the package declaration and the import directives.
package CustomClasses{
import flash.display.Bitmap;
import mx.containers.Canvas;
import mx.controls.Image;
import mx.events.FlexEvent;
import flash.events.TimerEvent;
import flash.utils.Timer;
import flash.media.Sound;
import flash.net.URLRequest;
import flash.media.SoundChannel;
import flash.events.Event;
New to this lesson
The directives to import the Sound class, the URLRequest class, the SoundChannel class, and possibly the Event class are all new to this lesson.
Beginning of the Driver class proper
The definition of the Driver class begins in Listing 3.
public class Driver extends Canvas {
//Extending Canvas makes it possible to locate
// images with absolute coordinates. The default
// location is 0,0;
private var smallSky:Image = new Image();
//Instantiate a Timer object that will fire ten events
// per second.
private var timer:Timer = new Timer(100);
//Declare a counter that will keep track of the number
// of timer events that have been fired.
private var loopCntr:uint;
//Declare variables for the four sounds.
private var sizzle:Sound;
private var thunder:Sound;
private var wind:Sound;
private var rain:Sound;
//Declare variables that are used to control when the
// thunder sound is played.
private var channel:SoundChannel;
private var sizzlePlaying:Boolean = false;
Nothing new here
There is nothing new in Listing 3. I will call your attention to the declaration of variables of type Sound and SoundChannel . Otherwise, no explanation beyond the embedded comments should be required.
The constructor for the Driver class
The constructor for the Driver class is shown in its entirety in Listing 4.
public function Driver(){//constructor
//Load the sky image.
//Note the use of a / to eliminate the "Unable to
// resolve asset for transcoding" Compiler Error
[Embed("/smallsky.jpg")]
var imgSmall:Class;
smallSky.load(imgSmall);
//Load four sound files and play two of them now.
sizzle = new Sound();
sizzle.load(new URLRequest("sizzle.mp3"));
thunder = new Sound();
thunder.load(new URLRequest("thunder.mp3"));
wind = new Sound();
wind.load(new URLRequest("wind.mp3"));
//Play the wind sound through twice at startup.
wind.play(0,2);
rain = new Sound();
rain.load(new URLRequest("rain.mp3"));
//Play the rain sound forever
rain.play(0,int.MAX_VALUE);
//Register an event listener on the CREATION_
// COMPLETE event.
this.addEventListener(FlexEvent.CREATION_COMPLETE,
creationCompleteHandler);
} //end constructor
There are quite a few things in Listing 4 that are new to this lesson.
Embed the image file
Although the code required to embed the image file in the swf file is not new to this lesson, it is worth highlighting the need to include the slash character to make the code compatible with the FlashDevelop IDE.
Load the sizzle sound
Listing 4 instantiates a new Sound object and stores the object's reference in the instance variable named sizzle . Then it calls the load method on that object to load the contents of the sound file named sizzle.mp3 into the new Sound object.
The load method of the Sound class
Here is part of what the documentation has to say about the load method of the class named Sound :
"Initiates loading of an external MP3 file from the specified URL. If you provide a valid URLRequest object to the Sound constructor, the constructor calls Sound.load() for you. You only need to call Sound.load() yourself if you don't pass a valid URLRequest object to the Sound constructor or you pass a null value.
Once load() is called on a Sound object, you can't later load a different sound file into that Sound object. To load a different sound file, create a new Sound object."
Because I didn't provide a URLRequest object to the constructor when I instantiated the object of the Sound class, it was necessary for me to call the load method on the Sound object to load the sound file named sizzle.mp3 .
Required parameter for the load method
Only one parameter is required by the load method and it must be of type URLRequest . To make a long story short, at least for the case where the sound file is located in the src folder as shown in Figure 2, you can create the required URLRequest object by calling the constructor for the URLRequest class and passing the name of the sound file as a String parameter to the constructor as shown in Listing 4.
Don't play the sizzle sound yet
The sizzle sound and the thunder sound are both encapsulated in Sound objects by the constructor in Listing 4. However, those sounds are not played by the constructor.
Encapsulate and play the wind wound
Listing 4 uses similar code to encapsulate the contents of the file named wind.mp3 in an object of type Sound referred to by the instance variable named wind .
Then Listing 4 calls the play method on the wind object to cause the wind sound to be played from beginning to end twice when the program first starts running. (It will be played again later at random times.)
The play method of the Sound class
Here is part of what the documentation has to say about the play method of the Sound class:
"Generates a new SoundChannel object to play back the sound. This method returns a SoundChannel object, which you access to stop the sound and to monitor volume. (To control the volume, panning, and balance, access the SoundTransform object assigned to the sound channel.) "
In other words, the play method causes the sound to start playing through a SoundChannel object, which you can manipulate to achieve various effects.
Didn't save a reference to SoundChannel object
Because I didn't have any need to manipulate the wind sound by way of the SoundChannel object, I didn't capture and save a reference to the object returned by the play method.
Parameters of the play method of the Sound class
The play method has three parameters, each of which has a default value. By default (and this doesn't seem to agree with the documentation) , if you call the play method on a Sound object and don't pass any parameters, the sound encapsulated in the object will be played once, starting at the beginning of the sound.
The first parameter
The first parameter is the initial position in milliseconds at which playback should start. The default value for this parameter is 0, which causes the sound to start at the beginning by default.
As is always the case with default parameters, if you want to provide a non-default value for the second parameter, you must also provide a value for the first parameter. When the play method is called on the wind sound in Listing 4, a value of 0 is passed as the first parameter to cause the sound to play from the beginning.
The second parameter
Instead of telling you what the documentation seems to say about the second parameter, I'm going to tell you how the second parameter behaves, which doesn't seem to agree with the documentation.
The value of the second parameter defines the number of times the sound will be played before the sound channel stops playback. For example, a value of 2 is passed as the second parameter when the play method is called on the wind sound in Listing 4. This parameter, in conjunction with the first parameter, causes the wind sound to be played through twice from the beginning to the end when the program starts running.
Rain, rain, go away: not any time soon
The maximum possible integer value is passed as the second parameter when the play method is called on the rain sound in Listing 4. This causes the rain sound to play over and over for a length of time that is probably longer than anyone would want to listen to it.
A CREATION_COMPLETE event listener
The last statement in the constructor in Listing 4 registers a CREATION_COMPLETE event handler on the Canvas object. You are already familiar with event listeners of this type. The code for the listener is shown in its entirety in Listing 5.
private function creationCompleteHandler(
event:mx.events.FlexEvent):void{
//Set the width and height of the Canvas object
// based on the size of the bitmap in the smallSky
// image.
this.width = Bitmap(smallSky.content).width;
this.height = Bitmap(smallSky.content).height;
//Add the image to this Canvas object.
this.addChild(smallSky);
//Register a timer listener and start the timer
// running.
timer.addEventListener(TimerEvent.TIMER, onTimer);
timer.start();
} //end creationCompleteHandler
As you learned in earlier lessons, this handler method is executed when the Canvas object has been fully created.
Nothing new here
There is nothing new in Listing 5. The code in Listing 5:
Beginning of the TIMER event handler
The event handler that is registered on the Timer object begins in Listing 6. This method is executed each time the Timer object fires an event.
public function onTimer(event:TimerEvent):void {
//Update the loop counter.
loopCntr++;
if (loopCntr == int.MAX_VALUE - 1) {
//Guard against numeric overflow.
loopCntr = 0;
}//end if
Update the loop counter
Among other things, the code in the Timer event handler maintains a count of the number of events that have been fired by the timer. The code in Listing 6 increments the timer each time the event-handler method is executed, and sets the value back to zero when it reaches a very large value to guard against binary overflow.
Play an occasional wind sound
According to the code in Listing 3, the Timer object will fire an event every 100 milliseconds, or ten times per second. That causes the event handler to be called ten times per second.
The code in Listing 7 uses the modulus operator to identify every 25th call to the event handler. This occurs approximately once every 2.5 seconds, depending on the accuracy of the timer.
if ((loopCntr % 25 == 0) && (Math.random() > 0.75)){
wind.play();
}//end if
Let the wind blow: or maybe not
When the code in Listing 7 determines that 2.5 seconds have passed since the last attempt to play the wind sound, it gets a random value of type Number with a value between 0 and 1.0. If that random value is greater than 0.75, it calls the play method on the wind sound to cause the sound to be played once from start to finish.
One wind sound every ten seconds on average
Assuming that the random values are uniformly distributed, about one out of every four random values will be greater than 0.75. Therefore, the wind sound should be played about once every ten seconds on average.
Play an occasional sizzle sound
Listing 8 uses a similar process to play an occasional sizzle sound. Listing 8 also causes the sizzle sound to be followed immediately by a clap of thunder.
if ((loopCntr % 35 == 0) && (Math.random() > 0.5)
&& (sizzlePlaying == false)) {
//Don't play another sizzle sound until this one
// finishes.
sizzlePlaying = true;
//Play the sizzle sound and get a reference to the
// SoundChannel object through which it is being
// played.
channel = sizzle.play();
//Register an event listener that will be called
// when the sizzle sound finishes playing.
channel.addEventListener(
Event.SOUND_COMPLETE, soundCompleteHandler);
}//end if
}//end onTimer
Save the SoundChannel reference
Listing 8 saves the SoundChannel reference returned by the play method in an instance variable named channel when the play method is called to play the sizzle sound.
Don't corrupt the reference to the SoundChannel object
In order to preclude the possibility of corrupting this reference by changing its value while the sound is playing, Listing 8 uses a Boolean instance variable named sizzlePlaying to guarantee that a new sizzle sound is not played before the previous one finishes.
The value of sizzlePlaying is set to true when the sizzle sound starts playing in Listing 8 and is set to false later when the sizzle sound finishes playing. Because the value of sizzlePlaying is tested by the conditional clause in the if statement in Listing 8, that conditional clause will never return true while sizzlePlaying is true.
Register a SOUND_COMPLETE event handler
The SoundChannel object fires a SOUND_COMPLETE event when the sound that it is playing finishes. Listing 8 registers an event listener on the SoundChannel object that is called each time the sizzle sound finishes playing. As you will see shortly, the code in the event handler sets the value of sizzlePlaying to false and also causes the thunder sound to be played as soon as the sizzle sound finishes.
The SOUND_COMPLETE event handler
The SOUND_COMPLETE event handler is shown in its entirety in Listing 9. This method is called each time the sizzle sound finishes playing.
private function soundCompleteHandler(e:Event):void {
//Allow another sizzle sound to be played now that
// this one is finished.
sizzlePlaying = false;
//Play the thunder immediately following the end of
// the sizzle sound.
thunder.play();
}//end soundCompleteHandler
//--------------------------------------------------//
} //end class
} //end packageAllow another sizzle sound to be played
Listing 9 begins by setting the value of sizzlePlaying to false. This makes it possible for the sizzle sound to be played again when the other two expressions in the conditional clause of the if statement in Listing 8 return true.
Play a thunder clap
Then Listing 9 calls the play method on the thunder sound to cause the thunder sound to be played once immediately following the completion of each sizzle sound.
The end of the program
Listing 9 also signals the end of the Driver class and the end of the program.
I encourage you to run this program from the web. Then copy the code from Listing 10 and Listing 11. Use that code to create your own project. Compile and run the project. Experiment with the code, making changes, and observing the results of your changes. Make certain that you can explain why your changes behave as they do.
I will publish a list containing links to ActionScript resources as a separate document. Search for ActionScript Resources in the Connexions search box.
Complete listings of the programs discussed in this lesson are provided below.
<?xml version="1.0" encoding="utf-8"?>
<!--
This project is intended to demonstrate the use of sound
with ActionScript. See the file named Driver.as for more
information
-->
<mx:Application
xmlns:mx="http://www.adobe.com/2006/mxml"
xmlns:cc="CustomClasses.*">
<cc:Driver/>
</mx:Application>
/*Project Sound03
This project is intended to demonstrate the use of sound
with ActionScript. It also displays an image of a cloudy
sky but the only purpose of the image is to make it
obvious when the program starts running.
This project plays the following sounds extracted from
mp3 files.
rain
wind
sizzle
thunder
The rain sound is continuous.
The wind sound is played occasionally on the basis of a
random number generator.
The sizzle sound is also played occasionally on the basis
of a random number generator. As soon as the sizzle sound
finishes, a thunder clap sound is played.
Note that with FlashDevelop, you must manually put a copy
of the sound files in the bin folder.
*********************************************************/
package CustomClasses{
import flash.display.Bitmap;
import mx.containers.Canvas;
import mx.controls.Image;
import mx.events.FlexEvent;
import flash.events.TimerEvent;
import flash.utils.Timer;
import flash.media.Sound;
import flash.net.URLRequest;
import flash.media.SoundChannel;
import flash.events.Event;
//====================================================//
public class Driver extends Canvas {
//Extending Canvas makes it possible to locate
// images with absolute coordinates. The default
// location is 0,0;
private var smallSky:Image = new Image();
//Instantiate a Timer object that will fire ten events
// per second.
private var timer:Timer = new Timer(100);
//Declare a counter that will keep track of the number
// of timer events that have been fired.
private var loopCntr:uint;
//Declare variables for the four sounds.
private var sizzle:Sound;
private var thunder:Sound;
private var wind:Sound;
private var rain:Sound;
//Declare variables that are used to control when the
// thunder sound is played.
private var channel:SoundChannel;
private var sizzlePlaying:Boolean = false;
//--------------------------------------------------//
public function Driver(){//constructor
//Load the sky image.
//Note the use of a / to eliminate the "Unable to
// resolve asset for transcoding" Compiler Error
[Embed("/smallsky.jpg")]
var imgSmall:Class;
smallSky.load(imgSmall);
//Load four sound files and play two of them now.
sizzle = new Sound();
sizzle.load(new URLRequest("sizzle.mp3"));
thunder = new Sound();
thunder.load(new URLRequest("thunder.mp3"));
wind = new Sound();
wind.load(new URLRequest("wind.mp3"));
//Play the wind sound through twice at startup.
wind.play(0,2);
rain = new Sound();
rain.load(new URLRequest("rain.mp3"));
//Play the rain sound forever
rain.play(0,int.MAX_VALUE);
//Register an event listener on the CREATION_
// COMPLETE event.
this.addEventListener(FlexEvent.CREATION_COMPLETE,
creationCompleteHandler);
} //end constructor
//--------------------------------------------------//
//This handler method is executed when the Canvas has
// been fully created.
private function creationCompleteHandler(
event:mx.events.FlexEvent):void{
//Set the width and height of the Canvas object
// based on the size of the bitmap in the smallSky
// image.
this.width = Bitmap(smallSky.content).width;
this.height = Bitmap(smallSky.content).height;
//Add the image to this Canvas object.
this.addChild(smallSky);
//Register a timer listener and start the timer
// running.
timer.addEventListener(TimerEvent.TIMER, onTimer);
timer.start();
} //end creationCompleteHandler
//--------------------------------------------------//
//TimerEvent handler. This method is executed each
// time the Timer object fires an event.
public function onTimer(event:TimerEvent):void {
//Update the loop counter.
loopCntr++;
if (loopCntr == int.MAX_VALUE - 1) {
//Guard against numeric overflow.
loopCntr = 0;
}//end if
//Play an occasional wind sound.
if ((loopCntr % 25 == 0) && (Math.random() > 0.75)){
wind.play();
}//end if
//Play an occasional sizzle sound followed
// immediately by a clap of thunder.
if ((loopCntr % 35 == 0) && (Math.random() > 0.5)
&& (sizzlePlaying == false)) {
//Don't play another sizzle sound until this one
// finishes.
sizzlePlaying = true;
//Play the sizzle sound and get a reference to the
// SoundChannel object through which it is being
// played.
channel = sizzle.play();
//Register an event listener that will be called
// when the sizzle sound finishes playing.
channel.addEventListener(
Event.SOUND_COMPLETE, soundCompleteHandler);
}//end if
}//end onTimer
//--------------------------------------------------//
//This method is called each time the sizzle sound
// finishes playing. Each time it is called, it plays
// a thunder sound.
private function soundCompleteHandler(e:Event):void {
//Allow another sizzle sound to be played now that
// this one is finished.
sizzlePlaying = false;
//Play the thunder immediately following the end of
// the sizzle sound.
thunder.play();
}//end soundCompleteHandler
//--------------------------------------------------//
} //end class
} //end packageThis section contains a variety of miscellaneous materials.
-end-