8bitrocket.com
4Dec/080

Tutorial: Using MP3 Files In Flex 3 with Flash Player 9.x

Tutorial: Using MP3 Files InFlex 3 with Flash Player 9.x

This tutorial will explore several different methods to use MP3 files in Flex 3. We will cover simple loading and embedding, as well as strategies for seamlessly looping MP3 files. Some of these concepts can be used for Flash CS2/CS3 AS3 IDE projects where noted.

The Basics

The software used and selections made when encoding an MP3 file for use with Flex can make all the difference when it comes to executing a project. First, when creating a MP3 file for use with Flex, you must ensure that it is encoded with 44kHz or 11kHz sample rate. Flex cannot load in a file encoded at 24kHz or 16kHz. The bits/sec do not matter for compatibility, but the sample rate does. Also, if you are going to be attempting to loop the MP3 file, you will want to find an encoder that DOES NOT add any space at the end of the file. Space at the beginning can be mitigated with offset start playing, but space at the end can be a killer.

We will explore a polling example below that can be used to work with files that contain both leader and follower space, but overall it is best to use an encoder that doesn't add space at the end of the file. The encoder that comes with Sony Acid (for example) doesn't add any "follower" space, just "leader" space.

Simple Loading an MP3 at run time (IDE or Flex)

The first example is the simplest way to load an external sound at run-time. It doesn't wait for the sound to be loaded before it is played, and simply assumes they sound is available.

[cc lang="javascript" width="550"]
package{
import flash.display.Sprite;
import flash.media.*;
import flash.net.URLRequest;
/**
* ...
* @author Jeff Fulton 2008
*/
public class Main extends Sprite{
private var sndChannel:SoundChannel;
private var snd:Sound;
private var sndUrl:URLRequest = new URLRequest("../assets/ambientrock4.mp3");
public function Main() {
trace("loading sound");
snd = new Sound(sndUrl);
trace("playing sound");
sndChannel = snd.play();
}
}
}
[/cc]
In the above example, you must first create a new URLRequest and give it the the location of the file to load. You then need to create Sound object to hold that loaded sound, and lastly, to play the sound, you must add it to a SoundChannel object when calling the Sound.Play() method. That's pretty much it for a simple sound load and play.

Monitoring the load of an MP3 and then Playing the Sound (IDE or Flex)

[cc lang="javascript" width="550"]
package{
import flash.display.Sprite;
import flash.media.*;
import flash.net.URLRequest;
import flash.events.ProgressEvent;
import flash.events.IOErrorEvent;

/**
* ...
* @author Jeff Fulton 2008
*/
public class Main extends Sprite{
private var sndChannel:SoundChannel;
private var snd:Sound;
private var sndUrl:URLRequest = new URLRequest("../assets/ambientrock4.mp3");

public function Main() {
trace("loading sound");
snd = new Sound(sndUrl);
snd.addEventListener(IOErrorEvent.IO_ERROR, snderrorHandler);
snd.addEventListener(ProgressEvent.PROGRESS, sndprogressHandler);
}

private function sndprogressHandler(e:ProgressEvent):void {
trace("loading sound...");
if (e.bytesLoaded >= e.bytesTotal) {
playSound();
}
}

private function snderrorHandler(e:IOErrorEvent):void {
trace("sound load error " + e.text);
}

private function playSound():void {
sndChannel = snd.play();
}
}
}
[/cc]

In the above example code we have added 2 event listeners to aid in monitoring the load progress of the file and have also added a function called playSound():void that will be called when the file has been completely loaded in. There is no onComplete event that fires when the sound has been loaded, so we must check the sndProgressHandler
method until the sound has been loaded, and then we call the playSound():void method. The snderrorHandler method would be called if there was some type of security access violation or a file not found error.

Simple Embedding of an MP3 at compile time (Flex Only)

[cc lang="javascript" width="550"]
package{

import flash.display.Sprite;
import flash.media.*;

/**
* ...
* @author Jeff Fulton 2008
*/
public class Main extends Sprite {

//*** 1 Basic
trace("attempting straing sound embed");
[Embed(source="../assets/ambientrock4.mp3")]
[Bindable]

public var sndCls:Class;
trace("finished straight sound embed");
public var snd:Sound = new sndCls() as Sound;
public var sndChannel:SoundChannel;

public function Main(){
//*** 1 basic
//basic sound play with offset
sndChannel = snd.play();
}
}
}
[/cc]

A Simple embed of a sound in a Flex app is quite easy (you cannot embed in a CS3 file). It is very similar to the simple load of an MP3, but even easier. Once the [Embed] is called, you must declare a class for the file directly underneath. You use this class name when you instantiate your Sound object and use the "as" construct to cast the generic loaded asset as a Sound. You then can play the sound in the exact same manner you would if it was loaded in, by assigning it to a sound channel.

Simple Seamless Looping the play of a loaded or embedded .mp3 file (IDE or Flex)

You can easily loop the play of an MP3 by calling play() and passing in
an offset and a number of loops. The offset is critically important
here, because if you were able to encode your MP3 file with an encoder
that doesn't put and "follower" space at the end of the file, you will
be able to simply play with the offset number of milliseconds until the
file loops seamlessly. For the example above, I changed the play() to
look like this:

[cc lang="javascript" width="550"]sndChannel = snd.play(956,5);[/cc]

That tells the SoundChannel to play the loop 5 times and always start 956 milliseconds into the file on every loop.

Advanced Seamless Looping of a MP3 file- for files that have space at the end of the file, as well as the beginning.
(IDE or Flex)

I attempted many different methods of seamlessly looping the play of a MP3 file in Flex, but none of them worked out any better than a version I did in AS2 a while back. My advanced attempt was to Embed the MP3 with a Mime Type setting of "mimeType="application/octet-stream". This will basically load the file into a ByteArray instance. I then copied the bytes after the leader blank space to another ByteArray and attempted to cast the new ByteArray as a sound. That will not work because when a sound is embedded as a MP3 file, it is converted to a type of file that can be used by the Flash Player, and is no longer a straight set of Bytes that represent an MP3. Since the cast would not work, I got an run-time exception and the sound never played.

Why then did Adobe not strip the leader and follower space off of the file when it converted to an internal type? I have no idea. I searched far and wide to find a solution, but it was fruitless. There were may discussions on the topic, but no answers. I even found a bug in the online Flex bug database that offered Adobe a brilliant solution - add an option into the play() function that allows the user to set a number of milliseconds as an
ending offset to skip before playing the sound again. I was able to see some hope in the Flash Player 10 spec, but since this entry is focused on Flex 3 and Flash Player 9, that will have to wait for another  research effort.

How to seamlessly (but not painlessly) loop an MP3 file with no help for Adobe.

Is there an answer that will help skip both the "leader" and "follower" space in an MP3 file in Flex 3, Flash Player 9? The answer is yes, but it makes use of a polling concept that will add some slight overhead to your application as you will need to constantly check the location of the MP3 play head and force it to start over when it passes the last real byte of music, before the follower space. I will present it here as a solution that you should use wisely. It is best added to a main loop single timer and should not be used with a
separate timer for each sound.

That being said, here is the code:

[cc lang="javascript" width="550"]

package{

import flash.display.Sprite;
import flash.media.*;
import flash.events.TimerEvent;
import flash.utils.Timer;

/**
* ...
* @author Jeff Fulton 2008
*/
public class Main extends Sprite{
//*** 1 Basic
trace("attempting straing sound embed");
[Embed(source="../assets/ambientrock4.mp3")]
[Bindable]
public var sndCls:Class;
trace("finished straight sound embed");
public var snd:Sound = new sndCls() as Sound;
public var sndChannel:SoundChannel;
public var leader:int = 1000;
public var follower:int = 400;
public var placeToStop:int=snd.length-follower;
public var gameTimer:Timer;

public function Main(){
//*** 1 basic
//basic sound play with offset
sndChannel = snd.play(leader);
gameTimer=new Timer(1);
gameTimer.addEventListener(TimerEvent.TIMER, runGame);
gameTimer.start();

//sndChannel = snd.play();
}

public function runGame(e:Event):void {
trace(int(sndChannel.position) +"/" + placeToStop);
if (int(sndChannel.position) >= placeToStop) {
trace("hit position")
sndChannel.stop();
sndChannel = snd.play(leader);
}
}
}
}
[/cc]

The basic theory behind this approach (hack) is to play the sound one time, starting at an offset that is passed the leader space one time without setting any looping properties. While the sound is playing, during a timer or EnterFrame event, you poll repeatedly to see if the current position of the sound playing is near your offset for the ending before follower space. If that condition is true,  the sound is stopped, and started again at the beginning + the starting offset (leader).

The leader variable above is set to 1000 and the follower is set to 400. The sound will start playing 1000 milliseconds in and stop 400 milliseconds from the end.  The placeToStop variable is set to be the length of the MP3 in milliseconds minus the follower value.  You will need to play with these numbers as they will be wildly different with various MP3 codecs and sample rates.
Also, in AS2, the sound.duration attribute (not in as3) was exactly correct and could easily be polled. In AS3, the sndChannel.length seems to be longer than the actual max position of the play head for the sound file. I had to set the follower value to be much greater than I thought for it to work properly.  As the above example plays, I can see the traces of the current position never actually get to the exact length that is reported in the length
attribute.

While this approach works (the above, when used with that particular sample will seamlessly loop the file), it does take some trial and error. Also, you will really want to use this type of polling inside your main loop and not as a separate timer for every looping sound.

One other thing to note is that the position parameter of the SoundChannel object is a real number, but I changed it to an int for this example. For this particular MP3, it worked best as an int, but a real number might be the choice for an MP3 that is encoded with a different codec.

For further reading on the theory behind polling for the end of the MP3, take a look at my
AS2 article that was the basis for this version
.

If you enjoyed this post, please consider leaving a comment or subscribing to the RSS feed to have future articles delivered to your feed reader.
Comments (0) Trackbacks (0)

No comments yet.


Leave a comment

No trackbacks yet.

This site is protected by Comment SPAM Wiper.