Playing sounds



Playing a loaded sound can be as simple as calling the Sound.play() method for a Sound object, as follows:

var req = new air.URLRequest("smallSound.mp3"); 
var snd = new air.Sound(req); 
snd.play();

Sound playback operations

When playing back sounds, you can perform the following operations:

  • Play a sound from a specific starting position

  • Pause a sound and resume playback from the same position later

  • Know exactly when a sound finishes playing

  • Track the playback progress of a sound

  • Change volume or panning while a sound plays

To perform these operations during playback, use the SoundChannel, SoundMixer, and SoundTransform classes.

The SoundChannel class controls the playback of a single sound. The SoundChannel.position property can be thought of as a playhead, indicating the current point in the sound data that’s being played.

When an application calls the Sound.play() method, a new instance of the SoundChannel class is created to control the playback.

Your application can play a sound from a specific starting position by passing that position, in terms of milliseconds, as the startTime parameter of the Sound.play() method. It can also specify a fixed number of times to repeat the sound in rapid succession by passing a numeric value in the loops parameter of the Sound.play() method.

When the Sound.play() method is called with both a startTime parameter and a loops parameter, the sound is played back repeatedly from the same starting point each time. The following code shows this:

var req = new air.URLRequest("repeatingSound.mp3"); 
var snd = new air.Sound(); 
snd.play(1000, 3);

In this example, the sound is played from a point one second after the start of the sound, three times in succession.

Pausing and resuming a sound

If your application plays long sounds, like songs or podcasts, you probably want to let users pause and resume the playback of those sounds. A sound cannot literally be paused during playback; it can only be stopped. However, a sound can be played starting from any point. You can record the position of the sound at the time it was stopped, and then replay the sound starting at that position later.

For example, let’s say your code loads and plays a sound file like this:

var req = new air.URLRequest("bigSound.mp3"); 
var snd = new air.Sound(req); 
var channel = snd.play();

While the sound plays, the position property of the channel object indicates the point in the sound file that is currently being played. Your application can store the position value before stopping the sound from playing, as follows:

var pausePosition = channel.position; 
channel.stop();

To resume playing the sound, pass the previously stored position value to restart the sound from the same point it stopped at before.

channel = snd.play(pausePosition);

Monitoring playback

Your application might want to know when a sound stops playing. Then it can start playing another sound or clean up some resources used during the previous playback. The SoundChannel class dispatches a soundComplete event when its sound finishes playing. Your application can listen for this event and take appropriate action, as the following example shows:

var snd = new air.Sound("smallSound.mp3"); 
var channel = snd.play(); 
s.addEventListener(air.Event.SOUND_COMPLETE, onPlaybackComplete); 
 
public function onPlaybackComplete(event) 
{ 
    air.trace("The sound has finished playing."); 
}

The SoundChannel class does not dispatch progress events during playback. To report on playback progress, your application can set up its own timing mechanism and track the position of the sound playhead.

To calculate what percentage of a sound has been played, you can divide the value of the SoundChannel.position property by the length of the sound data that’s being played:

var playbackPercent = 100 * (channel.position / snd.length);

However, this code only reports accurate playback percentages if the sound data was fully loaded before playback began. The Sound.length property shows the size of the sound data that is currently loaded, not the eventual size of the entire sound file. To track the playback progress of a streaming sound that is still loading, your application should estimate the eventual size of the full sound file and use that value in its calculations. You can estimate the eventual length of the sound data using the bytesLoaded and bytesTotal properties of the Sound object, as follows:

var estimatedLength = Math.ceil(snd.length / (snd.bytesLoaded / snd.bytesTotal)); 
var playbackPercent = 100 * (channel.position / estimatedLength);

The following code loads a larger sound file and uses the setInterval() function as its timing mechanism for showing playback progress. It periodically reports on the playback percentage, which is the current position value divided by the total length of the sound data:

var snd = new air.Sound(); 
var url = "http://www.example.com/sounds/test.mp3"; 
var req = new air.URLRequest(url); 
snd.load(req); 
 
var channel = snd.play(); 
var timer = setInterval(monitorProgress, 100); 
snd.addEventListener(air.Event.SOUND_COMPLETE, onPlaybackComplete); 
 
function monitorProgress(event) 
{ 
    var estimatedLength = Math.ceil(snd.length / (snd.bytesLoaded / snd.bytesTotal)); 
    var playbackPercent = Math.round(100 * (channel.position / estimatedLength)); 
    air.trace("Sound playback is " + playbackPercent + "% complete."); 
} 
 
function onPlaybackComplete(event) 
{ 
    air.trace("The sound has finished playing."); 
    clearInterval(timer); 
}

After the sound data starts loading, this code calls the snd.play() method and stores the resulting SoundChannel object in the channel variable. Then it adds a monitorProgress() method, which the setInterval() function calls repeatedly. The code uses an event listener to the SoundChannel object for the soundComplete event that occurs when play back is complete.

The monitorProgress() method estimates the total length of the sound file based on the amount of data that has already been loaded. It then calculates and displays the current playback percentage.

When the entire sound has been played, the onPlaybackComplete() function executes. This function removes the callback method for the setInterval() function, so that the application doesn’t display progress updates after playback is done.

Stopping streaming sounds

There is a quirk in the playback process for sounds that are streaming—that is, for sounds that are still loading while they are being played. When you call the stop() method on a SoundChannel instance that is playing back a streaming sound, the sound playback stops and then it restarts from the beginning of the sound. This occurs because the sound loading process is still underway. To stop both the loading and the playback of a streaming sound, call the Sound.close() method.

Controlling sound volume and panning

An individual SoundChannel object controls both the left and the right stereo channels for a sound. If an MP3 sound is a monaural sound, the left and right stereo channels of the SoundChannel object contain identical waveforms.

You can find out the amplitude of each stereo channel of the sound being played using the leftPeak and rightPeak properties of the SoundChannel object. These properties show the peak amplitude of the sound waveform itself. They do not represent the actual playback volume. The actual playback volume is a function of the amplitude of the sound wave and the volume values set in the SoundChannel object and the SoundMixer class.

The pan property of a SoundChannel object can be used to specify a different volume level for each of the left and right channels during playback. The pan property can have a value ranging from -1 to 1. A value of -1 means the left channel plays at top volume while the right channel is silent. A value of 1 means the right channel plays at top volume while the left channel is silent. Values in between -1 and 1 set proportional values for the left and right channel values. A value of 0 means that both channels play at a balanced, mid-volume level.

The following code example creates a SoundTransform object with a volume value of 0.6 and a pan value of -1 (top left channel volume and no right channel volume). It passes the SoundTransform object as a parameter to the play() method. The play() method applies that SoundTransform object to the new SoundChannel object that is created to control the playback.

var req = new air.URLRequest("bigSound.mp3"); 
var snd = new air.Sound(req);  
var trans = new air.SoundTransform(0.6, -1); 
var channel = snd.play(0, 1, trans);

You can alter the volume and panning while a sound plays. Set the pan or volume properties of a SoundTransform object and then apply that object as the soundTransform property of a SoundChannel object.

You can also set global volume and pan values for all sounds at once, using the soundTransform property of the SoundMixer class. The following example shows this:

SoundMixer.soundTransform = new air.SoundTransform(1, -1);

You can also use a SoundTransform object to set volume and pan values for a Microphone object (see Capturing sound input).

The following example alternates the panning of the sound from the left channel to the right channel and back while the sound plays:

var snd = new air.Sound();          
var req = new air.URLRequest("bigSound.mp3"); 
snd.load(req); 
 
var panCounter = 0; 
 
var trans = new air.SoundTransform(1, 0); 
var channel = snd.play(0, 1, trans); 
channel.addEventListener(air.Event.SOUND_COMPLETE,  
                            onPlaybackComplete); 
 
var timer = setInterval(panner, 100); 
 
function panner() 
{ 
    trans.pan = Math.sin(panCounter); 
    channel.soundTransform = trans; // or SoundMixer.soundTransform = trans; 
    panCounter += 0.05; 
} 
 
function onPlaybackComplete(event) 
{ 
    clearInterval(timer); 
}

This code starts by loading a sound file and then creating a SoundTransform object with volume set to 1 (full volume) and pan set to 0 (evenly balanced between left and right). Then it calls the snd.play() method, passing the SoundTransform object as a parameter.

While the sound plays, the panner() method executes repeatedly. The panner() method uses the Math.sin() function to generate a value between -1 and 1. This range corresponds to the acceptable values of the SoundTransform.pan property. The SoundTransform object’s pan property is set to the new value, and then the channel’s soundTransform property is set to use the altered SoundTransform object.

To run this example, replace the filename bigSound.mp3 with the name of a local MP3 file. Then run the example. You should hear the left channel volume getting louder while the right channel volume gets softer, and vice versa.

In this example, the same effect could be achieved by setting the soundTransform property of the SoundMixer class. However, that would affect the panning of all sounds currently playing, not just the single sound this SoundChannel object plays.