Wednesday, January 20, 2010

Flash AS3 Volume Control Tutorial

Exercise Files:
Adjusting_Volume_Start.fla
CheerfulSong.mp3

NOTE: Be sure to save both files in the same folder.

To adjust sound volume in Flash, you will need a SoundTransform object. The SoundTransform class has a volume property, which you can assign a value from 0 - 1. Where 0 is mute, and 1 is full volume.

For example:
var volumeAdjust:SoundTransform = new SoundTransform();
volumeAdjust.volume = .5;
The first line creates a SoundTransform object named volumeAdjust. The next line sets the volume property to .5.

But this doesn't adjust the volume of the sound just yet. Yes, we've set a new volume level, but we still haven't applied it to any sound. So after creating the SoundTransform object and setting the volume level, we must then apply the SoundTransform object to a specific SoundChannel. You can do this using the soundTransform property of the SoundChannel class.

NOTE: Be aware of the distinction between a SoundTransform object (which would be an instance of the SoundTransform class) and the soundTransform property (which is a property of the SoundChannel class). A SoundTransform object is what holds the value for the volume level adjustment, where as the soundTransform property of the SoundChannel class is used in order to apply that volume level adjustment to the sound. Also notice that the soundTransform property starts with a lowercase s, while the SoundTransform class starts with an uppercase S.

Example:
// Assume that channel1 is a SoundChannel object and that there is
// already a sound assigned to that channel
channel1.soundTransform = volumeAdjust;
This statement assigns the SoundTransform object named volumeAdjust, to the soundTransform property of the SoundChannel object named channel1. So this means that whatever sound is being played on that SoundChannel will have the volume adjustment applied to it.

NOTE: The volume property can actually accept values that are greater than 1, as well as negative values. But generally, you should only allow values between 0 - 1 (nothing greater nothing less) because values outside that range can end up distorting the sound. Negative values will actually increase the volume as well. So if the volume property has a value that goes below 0, the sound volume comes back up. You can use an if statement with an else clause in order to set constraints (which we will do later on).

So let's begin.

Open the exercise file and select the first frame of the Actions layer. In the Actions Panel, you'll see that there's already some code:
var mySound:Sound = new Sound();
var songURL:URLRequest = new URLRequest("CheerfulSong.mp3");
var channel1:SoundChannel = new SoundChannel();

mySound.load(songURL);

play_btn.addEventListener(MouseEvent.CLICK, playSound);
stop_btn.addEventListener(MouseEvent.CLICK, stopSound);

function playSound(e:MouseEvent):void 
{
     channel1 = mySound.play();
}

function stopSound(e:MouseEvent):void 
{
     channel1.stop();
}
This code just loads the sound and creates the playing and stopping functionality. We will just be adding the volume controls.

On the stage, you will see two small buttons: one pointing up (volUp_btn) and one pointing down (volDown_btn). These buttons will be made clickable in order to adjust the volume.

But first, let's go ahead an create the SoundTransform object. I will name it volumeAdjust:
var mySound:Sound = new Sound();
var songURL:URLRequest = new URLRequest("CheerfulSong.mp3");
var channel1:SoundChannel = new SoundChannel();
var volumeAdjust:SoundTransform = new SoundTransform();

mySound.load(songURL);

Next, let's go ahead and create the event handlers for the volume buttons. I will name the listener functions volUp (for increasing the volume) and volDown (for decreasing the volume):
play_btn.addEventListener(MouseEvent.CLICK, playSound);
stop_btn.addEventListener(MouseEvent.CLICK, stopSound);
volUp_btn.addEventListener(MouseEvent.CLICK, volUp);
volDown_btn.addEventListener(MouseEvent.CLICK, volDown);

function playSound(e:MouseEvent):void
{
     channel1 = mySound.play();
}

function stopSound(e:MouseEvent):void 
{
     channel1.stop();
}

function volUp(e:MouseEvent):void 
{
     //code to increase the volume goes here
}

function volDown(e:MouseEvent):void 
{
     //code to decrease the volume goes here
}

Next, let's set the initial volume.

Set the volume property of the SoundTransform object to the desired level. I will give it a value of .5:
var mySound:Sound = new Sound();
var songURL:URLRequest = new URLRequest("CheerfulSong.mp3");
var channel1:SoundChannel = new SoundChannel();
var volumeAdjust:SoundTransform = new SoundTransform();

volumeAdjust.volume = .5;
Then go to the playSound function (that's the listener function that's called whenever the play button is clicked) and add the following line (highlighted in bold):
function playSound(e:MouseEvent):void 
{
     channel1 = mySound.play();
     channel1.soundTransform = volumeAdjust;
}
Code explained:
volumeAdjust.volume = .5;
This line sets the volume property of the SoundTransform object to .5 (which is at half the full volume level).

channel1.soundTransform = volumeAdjust;
Then to apply the change in volume, the volumeAdjust SoundTransform object is assigned to the soundTransform property of channel1. It's important to note that this line must be added after the play sound statement is assigned to the SoundChannel object. Otherwise, the volume change will not be applied.

So now, we've set the initial volume to .5. Test the movie and try applying different volume levels in order to hear the difference. Try putting in a value greater than 1 (around 15-20, for example) and you will notice some sound distortion (be sure to keep your ears a comfortable distance away from the sound source when you do this). Then make sure you bring it back down to .5 when you continue the tutorial.

After setting the initial volume, let's now start working on the buttons for adjusting the volume.

To increase the volume, we can simply increment the value of the SoundTransform object's volume property whenever the volume up button is clicked. Go to the volUp listener function and add the following lines highlighted in bold:
function volUp(e:MouseEvent):void 
{
     volumeAdjust.volume += .1;
     channel1.soundTransform = volumeAdjust;
}

Code explained:
volumeAdjust.volume += .1;
This line increments the current volume property value by .1. So every time the volume up button is clicked, the volume property's value increases by .1.

channel1.soundTransform = volumeAdjust;
Every time changes are made to the volume property of the SoundTransform object, it must be reapplied to the SoundChannel's soundTransform property in order for the changes to take effect. So you have to make sure that you add this line as well.

But wait! Early on, I mentioned that continuously increasing the volume way above a value of 1 will end up distorting the sound. So we'll need to limit the volume adjustment to a range of just 0 to 1. In order to do that, we can use if statements.

Go back to the volUp function and add the following (highlighted in bold):
function volUp(e:MouseEvent):void 
{
     volumeAdjust.volume += .1;
     if(volumeAdjust.volume > 1)
     {
          volumeAdjust.volume = 1;
     } 
     channel1.soundTransform = volumeAdjust;
}
Code explained:
So whenever the volume up button is clicked, the volume property is incremented first. Then before the volume adjustment is applied using the soundTransform property, the if statement checks whether the new volume value has gone over 1. If it has, then the volume property value is immediately brought back down to 1, before the volume adjustment is applied. This effectively limits the maximum volume to 1 no matter how many times the volume button is pressed.

And lastly, for decreasing the volume, we use the same concept. Except for this one, we would like to DECREMENT the value of the volume property instead. And for the if statement, we would like to check whether the volume property's value is LESS THAN 0. Whenever it goes below 0, then we want to immediately pull it back up again to 0. So that way, we effectively limit the minimum volume to 0. So go to the volDown function and add the following lines highlighted in bold:
function volDown(e:MouseEvent):void 
{
     volumeAdjust.volume -= .1;
     if(volumeAdjust.volume < 0)
     {
          volumeAdjust.volume = 0;
     } 
     channel1.soundTransform = volumeAdjust;
}
So there you have it. You've just created simple volume controls in ActionScript 3.0.

Here's the code in full:
var mySound:Sound = new Sound();
var songURL:URLRequest = new URLRequest("CheerfulSong.mp3");
var channel1:SoundChannel = new SoundChannel();
var volumeAdjust:SoundTransform = new SoundTransform();

volumeAdjust.volume = .5;

mySound.load(songURL);

play_btn.addEventListener(MouseEvent.CLICK, playSound);
stop_btn.addEventListener(MouseEvent.CLICK, stopSound);
volUp_btn.addEventListener(MouseEvent.CLICK, volUp);
volDown_btn.addEventListener(MouseEvent.CLICK, volDown);

function playSound(e:MouseEvent):void 
{
  channel1 = mySound.play();
  channel1.soundTransform = volumeAdjust;
}

function stopSound(e:MouseEvent):void 
{
  channel1.stop();
}

function volUp(e:MouseEvent):void 
{
  volumeAdjust.volume += .1;
  if(volumeAdjust.volume > 1)
  {
    volumeAdjust.volume = 1;
  } 
  channel1.soundTransform = volumeAdjust;
}

function volDown(e:MouseEvent):void 
{
  volumeAdjust.volume -= .1;
  if(volumeAdjust.volume < 0) 
  {
    volumeAdjust.volume = 0;
  } 
  channel1.soundTransform = volumeAdjust;
}

Monday, January 18, 2010

Pausing and Resuming Sound in ActionScript 3.0

Exercise Files:
Pausing_Sound_Start.fla
CheerfulSong.mp3

NOTE: Be sure to save both files in the same folder.

In ActionScript 3.0, you can start playing a sound file using the play() method of the Sound class. And in order to stop playing a sound file, you use the stop() method of the SoundChannel class. Pausing the sound however, is not as straightforward. Pausing and resuming sound in ActionScript 3.0 involves a few extra elements.

When pausing sound in ActionScript 3.0, it's important to note that you're not actually pausing the sound. You will still need to stop the sound using the stop() method of the SoundChannel class. But before you stop the sound, you'll need to find a way to tell Flash to remember at which point along the sound file's playback it's currently on. Say for example you stop the sound file at the 10 second mark of the song, you will then need to store that information in a variable and then tell flash to resume playing at that same position when the song is played again. In other words, you're telling Flash to "remember" where the song was before it was stopped.

So how do you know the current position of the sound that is being played?
You'll need to use the position property of the SoundChannel class. The position property refers to the current position of the sound as it is being played. This gives you a value in milliseconds. You can store this value in a Number variable so that Flash can "remember" where the sound was at before it was stopped.

So now that Flash "remembers" the position, how do you tell it to resume playing at that same point?
The play() method of the Sound class has an offset parameter. It's an optional parameter that allows you to start playing a sound at a point other than the beginning. For example:

mySound.play(10000);
//10000 stands for 10000 milliseconds

This tells Flash to start playing the sound at the 10000 millisecond position. Take note that this is NOT a delay. The sound file is going to play immediately. It's simply going to skip the first 9 seconds of the song and start playing at the 10 second mark (the offset will read the value in milliseconds though, so you'll have to specify 1000 for 1 second, 2000 for 2 seconds and so on...).

So now you can store the sound file's current position in a Number variable, then use the offset parameter so that it can resume playing at that same position. For example:

//Let's say that this is the variable that will be used to store the position of the sound
var resumeTime:Number = 0;

//You can then associate the variable with the offset parameter like so:
mySound.play(resumeTime);
Let's begin.

Open the Flash exercise file and take a moment to observe the elements on the stage. You'll see a play button (play_btn) and a stop button (stop_btn). There's also a pause button (pause_btn). The pause button however is hidden underneath the play button. Move the play button to a different position in order to see the pause button (but be sure to bring it back to the original position where it's covering the pause button). Later on, we'll toggle the visibility of the play and the pause button so that when one of them is clicked, it becomes invisible so that the other one is revealed.

Create the following variables:
//This will be used to load the external mp3 file as well as to start playing the sound
var mySound:Sound = new Sound();

//Use a URLRequest to specify the path to the sound file to be loaded
var songURL:URLRequest = new URLRequest("CheerfulSong.mp3");

//This creates the SoundChannel object for the sound file to be played
var channel1:SoundChannel = new SoundChannel();

//This is the variable that will be used for the pause functionality.
//It will be used to store the sound file's position just right before it's stopped.
//Initialize it to 0 so that the sound file will play at the very beginning the first time the song is played.
var resumeTime:Number = 0;

Then use the load method of the Sound class to load the external sound file:
mySound.load(songURL);


Then create the event Handlers for the play, pause and stop buttons:
play_btn.addEventListener(MouseEvent.CLICK, playSound);
pause_btn.addEventListener(MouseEvent.CLICK, pauseSound);
stop_btn.addEventListener(MouseEvent.CLICK, stopSound);

function playSound(e:MouseEvent):void
{
//Code for starting and resuming sound playback goes here
}

function pauseSound(e:MouseEvent):void 
{
//Code for the pausing functionality goes here
}

function stopSound(e:MouseEvent):void 
{
//Code for stopping the sound goes here
}

Let's go ahead and complete the playSound function first (which is responsible for starting and resuming sound playback):
function playSound(e:MouseEvent):void
{
  channel1 = mySound.play(resumeTime);
  play_btn.visible = false;
  pause_btn.visible = true;
}

Code explained:
channel1 = mySound.play(resumeTime);
This plays the sound file. It will be assigned to the channel1 SoundChannel object. You have to assign it to the SoundChannel object because the SoundChannel will be used to stop the sound and to get it's current position. The resumeTime variable is then specified as the offset parameter of the play method so that the sound file will begin playing at whatever value is stored in resumeTime.

play_btn.visible = false;
pause_btn.visible = true;
These lines toggle the visibility of the play and pause buttons. Upon clicking the play button, it will become invisible in order to reveal the pause button underneath.

And now for the pause functionality. This function will be called whenever the pause button is clicked:
function pauseSound(e:MouseEvent):void 
{
  resumeTime = channel1.position;
  channel1.stop();
  pause_btn.visible = false;
  play_btn.visible = true;
}

Code explained:
resumeTime = channel1.position;
channel1.stop();
Upon clicking the pause button, the sound file's current position is retrieved and then stored in the resumeTime variable. The sound is then immediately stopped.
pause_btn.visible = false;
play_btn.visible = true;
Just like in the previous function, this toggles the visibility of the play and pause buttons. But this time, when the pause button is clicked, it becomes invisible while the play button becomes visible again.

And now for the function assigned to the stop button:
function stopSound(e:MouseEvent):void 
{
  //This stops the sound
  channel1.stop();

  //Be sure to reset the resumeTime variable back to 0, otherwise, 
  //the playback will resume at the same point when it was previously paused
  resumeTime = 0;

  //Make sure that the play button is visible and the pause button 
  //is hidden whenever the stop button is clicked
  play_btn.visible = true;
  pause_btn.visible = false;
}

There's one more thing we need to add. If you test the Flash movie now, you will notice that when the song reaches the end, the play button does not come back, so you're just stuck with the pause button. We want the play button to come back once the song finishes playing. So let's add a SOUND_COMPLETE event handler for that. Whenever the song finishes playing, we need to make sure that the the play button will be visible again. In addition, we'll have to reset the resumeTime variable back to 0 to ensure that the sound will play from the very beginning once the user clicks on the play button again.

So first, go back to the playSound function and create the addEventListener statement for whenever the sound finishes playing. The event is Event.SOUND_COMPLETE and we'll name the function to be called when the event is triggered as onSongEnd:
function playSound(e:MouseEvent):void
{
  channel1 = mySound.play(resumeTime);

  channel1.addEventListener(Event.SOUND_COMPLETE, onSongEnd);
  // Remember that this should only be added after the play sound statement
  // is assigned to the SoundChannel

  play_btn.visible = false;
  pause_btn.visible = true;
}

Next, let's create the listener function for our SOUND_COMPLETE event handler. You can place this right after the stopSound function.
function onSongEnd(e:Event):void 
{
  //This sets resumeTime back to 0 so that the sound plays at the beginning
  //the next time it is played
  resumeTime = 0;

  //This makes sure that the play button is visible and the pause button is not
  play_btn.visible = true;
  pause_btn.visible = false;
}

So there you have it. You've just learned how to successfully pause and resume sound playback.

Here's the code in full:
var mySound:Sound = new Sound();
var songURL:URLRequest = new URLRequest("CheerfulSong.mp3");
var channel1:SoundChannel = new SoundChannel();
var resumeTime:Number = 0;

mySound.load(songURL);

play_btn.addEventListener(MouseEvent.CLICK, playSound);
pause_btn.addEventListener(MouseEvent.CLICK, pauseSound);
stop_btn.addEventListener(MouseEvent.CLICK, stopSound);

function playSound(e:MouseEvent):void
{
  channel1 = mySound.play(resumeTime);
  channel1.addEventListener(Event.SOUND_COMPLETE, onSongEnd);
  play_btn.visible = false;
  pause_btn.visible = true;
}

function pauseSound(e:MouseEvent):void 
{
  resumeTime = channel1.position;
  channel1.stop();
  pause_btn.visible = false;
  play_btn.visible = true;
}

function stopSound(e:MouseEvent):void 
{
  channel1.stop();
  resumeTime = 0;
  play_btn.visible = true;
  pause_btn.visible = false;
}

function onSongEnd(e:Event):void 
{
  resumeTime = 0;
  play_btn.visible = true;
  pause_btn.visible = false;
}