Friday, January 14, 2011

Sound in ActionScript

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.
Figure 1: Sound03 image.
Sound03 image.
Missing 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:
  • rain
  • wind
  • sizzle
  • thunder
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.

Discussion and sample code

NOTE: 

If you develop this project using FlashDevelop , you will need to manually copy all of the sound files into the bin folder.
The project file structure
The final project file structure, captured from the FlashDevelop project window, is shown in Figure 2.
Figure 2: Project file structure for Sound03.
Project file structure for Sound03.
Missing image
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

The MXML code is shown in Listing 1 and also in Listing 10 for your convenience.

LISTING 1: MXML code for the project named Sound03.

<?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.

The 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.

LISTING 2: Import directives for the Driver class

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.

LISTING 3: Beginning of the Driver class proper.

  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.

LISTING 4: The constructor for the Driver class.

    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 Soundobject 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 srcfolder 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 Stringparameter 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 aSound 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 playmethod 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.

LISTING 5: A CREATION_COMPLETE event handler.

    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:
  • Sets the width and the height of the Canvas object to match the width and the height of the image that is displayed while the program is running.
  • Adds the image to the Canvas object.
  • Registers an event listener on the Timer object that was instantiated in Listing 3 and starts the timer running to fire ten events per second.
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.

LISTING 6: Beginning of the Timer event handler.

    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.

LISTING 7: Play an occasional wind sound.

      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.

LISTING 8: Play an occasional sizzle sound.

      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 ofsizzlePlaying 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.

LISTING 9: The SOUND_COMPLETE event handler.

    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 package
Allow 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.

Run 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.

Resources

I will publish a list containing links to ActionScript resources as a separate document. Search for ActionScript Resources in the Connexions search box.

Complete program listings

Complete listings of the programs discussed in this lesson are provided below.

LISTING 10: MXML code for the project named Sound03.

<?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>

LISTING 11: Class named Driver for the project named Sound03.

/*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 package

No comments:

Post a Comment

Related Posts Plugin for WordPress, Blogger...

java

Popular java Topics