Advertisement
  1. Code
  2. ActionScript
Code

Managing Sounds with Commands

by
Difficulty:IntermediateLength:ShortLanguages:

Sound management is very important for many types of Flash applications, such as interactive websites and games. As long as you want to deliver a rich interactive experience, you might want to consider making use of sound effects and background music. In this tutorial, I'll present a minimalistic sound management framework that manages sounds into sound tracks. And I'll show how to integrate the sound framework with the command framework from my previous tutorials.


Introduction

I've played games with incautious sound management, and that degrades user experience. Have you ever played a game, say, an action game, where the character's exclamation voice plays before the previous voice ends, overlapping each other? That's a result of bad sound management: there shouldn't be more than one voice of the same character playing at a time. The sound management framework I'm about to cover will take care of this issue by managing sounds with sound tracks.

The examples in this tutorial make use of the command framework and scene management framework from my previous tutorial, Thinking in Commands (Part 1, Part 2), and the examples also use the data manager class from Loading Data with Commands. I highly recommend that you go through these tutorials first before going on. Also, you'll need the GreenSock Tweening Platform to complete the examples.


Step 1: Theory Sound Tracks

The sound track we're talking about here has nothing to do with game or movie sound tracks. A sound track is an imaginary "track" associated with a playback of a single sound. One single sound track does not allow more than one sound playing at a time. If a sound track is currently playing a sound, we say it's occupied. If another sound is to be played on an occupied sound track, the currently playing sound is stopped, and then the new one is played on the track. It is thus reasonable to play a single character's voices on a single sound track, so as to avoid the sound overlapping issue mentioned earlier. Also, in most cases, there should be only one track for background music.

Let's take a look at some conceptual figures. A single application can have multiple sound tracks.

Each sound track can hold one single playing sound.

If a sound is to be played on an occupied track, the "old" sound is first stopped, and then the "new" sound is played on the track.


Step 2: Theory The Framework

The Sound Management framework consistes of two classes, the SoundManager class and the SoundTrack class. Each sound track is assigned a unique key string. An occupied sound track's underlying playing sound is actually a native SoundChannel object obtained from the native Sound.play() method, and the SoundManager class manages sound tracks and organizes the playback of sounds.

Here are some quick previews of the usage of the framework. The following code plays a new sound on a sound track associated with the key string "music", where MySound is a sound class from the library.

If the same line of code is executed again before the playback is finished, the original sound is stopped, and a new sound is played on the "music" track.

The SoundManager.stop() method stops a sound track associated with a specified key string.

In order to transform the sound, like to adjust the volume, we'll need to obtain a reference to a sound track's underlying sound channel. The reference can be obtained by accessing the SoundTrack.channel property.


Step 3: Classes The SoundTrack Class

Enough theory. Let's get down to the coding. We are going to use different key strings to distinguish different sound tracks. Here's the SoundTrack class, which essentially represents a key-channel pair. Details are described in comments.


Step 4: Classes The SoundManager Class

And here's the SoundManager class. Note that the key-track association is handled by using the Dictionary class. A track is emptied automatically if a playing sound has reached its end.


Step 5: Example Sound Manager Testdrive

Now let's test our sound management framework. We're going to compare the outcome of repeated requests to play a sound with and without using the sound manager.


Step 6: Example New Flash Document

Create a new Flash document (duh).


Step 7: Example Create Buttons

Create two buttons on the stage. You can draw your own and convert them to symbols, or you can, as in my case, drag two Button components from the Components panel. Name them "boing_btn" and "managedBoing_btn".


Step 8: Example Import the Sound

Import the sound we're going to play to the library. You can find the "Boing.wav" file in the example source folder.


Step 9: Example The Document Class

Finally, create an AS file for the document class. The code is rather straightforward, so I just explain everything in the comments.


Step 10: Example Test Drive

We're done. Press Ctrl+Enter to test the movie, and try rapidly clicking the buttons (remember to turn on your speakers). For the "Boing!" button, multiple sounds are overlapping when played. As for the "Managed Boing!" button, which makes use of the sound manager, one sound is forced to stop before the next one is played, so you won't hear sounds mixed up together.


Step 11: Framework Integration

Commands, commands, commands. It's always nice to integrate your work with your previous ones, right? Now we're going to integrate the sound management framework with the command framework, along with the scene management framework. Again, if you're not familiar with the command framework and the scene management framework, you'd better check them out in my previous tutorials (Part 1, Part 2) before going on.


Step 12: Framework PlaySound Command

The name of this command is pretty self-explanatory: it plays a sound with the sound manager.


Step 13: Framework StopSound Command

This is basically the evil cousin of the previous command. This command stops a sound track by using the sound manager.


Step 14: Framework SoundLoad Command

This command loads an external MP3 file into a Sound object. Not until the loading is complete will the command's complete() method be called. This allows us to easily chain together the command with other commands, without having to worry about handling the loading completion.

Integration complete. Get prepared for our final example!


Step 15: Example Managing Sounds with Commands

In this example, we're going to allow users to play two music on the same sound track. If a sound is to be played when the sound track is occupied, the original music is first faded out, and then the new music is played. The fading-out is handled by the TweenMaxTo command, which internally uses the special property volume provided by the TweenMax class from GreenSock Tweening Platform. The two musics are external MP3 files loaded during run-time.

Note that we're going to use the scene management framework. If you want to refresh your memory, go check it out here.


Step 16: Example Copy Flash Document

Make a copy of the FLA file used in the previous example. Rename the buttons to "music1_btn" and "music2_btn". You can also change the button labels to "Music 1" and "Music 2". And add an extra button named "stop_btn", which is for stopping the music.


Step 17: Example Copy the MP3 Files

The MP3 files can be found in the source folder. Copy them to the same folder as the FLA file.


Step 18: Example Document Class

Create a new AS file for the document class of the new FLA file. Instantiate a scene manager, and initialize it to a loading state, where the two MP3 files are loaded.


Step 19: Example The Loading Scene

The loading scene instantiates two Sound objects for loading the two MP3 files. The buttons are set invisible at the beginning, and will be set visible again when the loading is finished. When the loading is complete, the scene immediately instructs the scene manager to transit to the main scene, as written in the overridden onSceneSet() method. Further details are described in the comments.


Step 20: Example The Main Scene

The main scene brings the hidden buttons back to visible, and registers the playMusic() method and the stopMusic() method as listeners for the click event. In the playMusic() method, a serial command is executed if the "bgm" sound track is occupied. The command first temporarily removes the click listeners, fades out the current music, stops the current music, plays the new music on the now-empty "bgm" sound track, and then finally re-adds the click listeners. The stopMusic method does basically the same thing, only that there's no new music playback. This complex series of actions is carried out in only a few lines of clean code. Pretty neat, huh?

Note that adding and removing the listeners are common actions that are present in both the playMusic() method and the stopMusic() method. So they are factored out as two private properties, addListeners and removeListeners, initialized in the constructor.


Step 21: Example Test the Movie

We're ready to test the movie. Press CTRL+ENTER to test it. When you click a button, a music begins to play. After clicking another, the music fades out, and then a new one starts from the beginning.


Step 22: Extra Code Jockey Version

It's the end of the tutorial, I know. But I just couldn't resist from showing this to you. If you're a code jockey, I bet you've already noticed that there are lots of similarities in the playMusic() method and the stopMusic() method. Why not refactor them into a single one? If you're not interested in this code jocky version of music player, you may skip to the summary section. Otherwise, just go on reading!

First, replace all the playMusic and stopMusic in the source code with handleMusic, our new event listener. Next, delete the playMusic and the stopMusic method, and add the following handleMusic() method in the main scene class.

You'll notice that the only major difference between this method and the original listeners, is the following chunk of code:

What the hell is this anyway? This is actually the ?: conditional operator. It's a ternary operator, meaning that it requires three operands, A, B, and C. The statement "A?B:C" evaluates to B if A is true, or C otherwise. The music variable is supposed to hold a reference to a Sound object, so that the variable evaluates to true. However, if the event dispatcher target is the "stop_btn" button, the variable contains a null value, which evaluates to false in the ternary operator. So, if the two music buttons are clicked, the above code chunk is regarded as the single line of code below.

Otherwise, if the stop button is clicked, the code chunk is regarded as a dummy command, which simply does nothing.

Just one other thing to notice. The following line of code

is changed to

This is for the handling the exception that the sound track is currently empty. If you can understand the code chunk above, I'm pretty sure you could figure out what this line is all about.

Test the movie by pressing Ctrl+Enter, you'll see the exact same result as the last example. You can regard it as a fulfillment of a code jockey's coding vanity.


Summary

In this tutorial, you have learned how to manage sounds with sound tracks. One sound track allows only one sound being played at a time, therefore ideal to represent a single character's voice or background music. Also, you've seen how to integrate the sound management framework with the command framework, which gives you a huge maintainability and flexibility boost on your applications.

This is the end of the tutorial. I hope you enjoyed it. Thank you very much for reading!

Advertisement
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.