Not yet rated

Problem

You would like to be able to browse and play music that is stored on a mobile device.

Solution

Using a combination of the File class for browsing the device and the Sound, SoundMixer, and SoundChannel classes, we can create an mp3 player with load, play, pause, and resume functionality.

Detailed explanation

The recipe below will demonstrate how to create a simple mp3 player.

The visual components included in the sample below are:

  • TextArea with an id of songInfo which will hold the information about the song and its metadata.
  • Button to browse device
  • Label to show status of player
  • Button to pause playback
  • Button to Start playback over
  • Button to Resume playback

By using the File class with the browseForOpen method along with a FileFilter we can limit the users selection to only mp3 file types. This was demonstrated within the Flex cookbook recipe titled:  Using FileFilter in Android Mobile.

When the user clicks on the Browse button a new File is created and an event listener is added to listen for the Event.SELECT. After a file is selected by the user, the onFileSelect is called and the event.curentTarget is cast as a File. Next a Sound is instantiated and several event listeners are added to listen for IOErrorEvent.IO_ERROR, ProgressEvent.PROGRESS, Event.ID3, and Event.COMPLETE. Finally, the load method is called on the sound instance and the url property of the file is passed in.

While the song is being loaded, the progressHandler is called and information about the loading of the file is displayed in the songInfo TextArea. Once the song load completes successfully, the loadComplete method is called. Within this method, first the pauseButton_clickHandler is called to stop any currently playing sounds by calling the SoundMixer.stopAll() method, the songs length is calculated in minutes and seconds using the length property of the sound. The readyToPlay property is set to true which is used to set the enabled property of the Start Over and Resume button. Finally, the play method is called.

Within the play method, the playing method is set to true, the soundChannel class variable is set to the sound.play() method response and an event listener is added to listen for Event.ENTER_FRAME. Within the onEnterFrame method, the current minutes and seconds are calculated based on the soundChannel's position and displayed within the playInfo TextArea.

The Event.ID3 event listener will call the showID3 method and the songs metadata is read and displayed within the songInfo TextArea. For more info on reading id3 info, please see the recipe titled  Read the metadata on an mp3 file.

When a song is loaded and starts playing, the Pause Button is enabled. Clicking on this button calls the pauseButton_clickHandler. Within this method, the playing property is set to false, the SoundMixer.stopAll method is called, the playInfo status is updated, and the onEnterFrame event listener is removed. When a sond is ready to play and is not currently playing, the Resume and Start Over Button's are enabled. Clicking on the Resume button calls the resumeButton_clickHandler method. Within the resumeButton_clickHandler method, the play method is called and the soundChannel.position is passed in which will allow the sound to resume where is left off. When the Start Over button is called, the playButton_clickHandler is called and the play method is called without a position passed in which will start the song playing from the start.

Here is the full code for this example and a screen shot of the application:

<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" 
  xmlns:s="library://ns.adobe.com/flex/spark">

<fx:Script>
<![CDATA[
private var sound:Sound;
private var songLength:String;
private var soundChannel:SoundChannel;
[Bindable]
private var readyToPlay:Boolean = false;
[Bindable]
private var playing:Boolean = false;
private var file:File;
private var filter:FileFilter = new FileFilter("Music", "*.mp3;");

protected function browse_clickHandler(event:MouseEvent):void {
file = new File();
file.addEventListener(Event.SELECT, onFileSelect);
file.browseForOpen("Open",[filter]);
}

private function onFileSelect(event:Event):void{
file.removeEventListener(Event.SELECT, onFileSelect);
file = File(event.currentTarget);
sound = new Sound();    
sound.addEventListener(IOErrorEvent.IO_ERROR, errorHandler);
sound.addEventListener(ProgressEvent.PROGRESS, progressHandler);
sound.addEventListener(Event.ID3,showID3);
sound.addEventListener(Event.COMPLETE, loadComplete);
sound.load(new URLRequest(file.url));    
}

private function errorHandler(event:IOErrorEvent):void {
songInfo.text = "Song not loaded";
}

private function progressHandler(event:ProgressEvent):void {
var loadTime:Number = event.bytesLoaded / event.bytesTotal;
var LoadPercent:uint = Math.round(100 * loadTime);

songInfo.text = "Song size: " + event.bytesTotal + " bytes\n"
+ "Loading " + LoadPercent + "%.";
}

private function loadComplete(event:Event):void {     
pauseButton_clickHandler(null);
var minutes:Number = Math.floor(sound.length / 1000 / 60);
var seconds:Number = Math.floor(sound.length / 1000) % 60;
songLength = minutes + ":" + seconds;
readyToPlay = true;
play();
}

private function play(pos:Number=0):void{
playing = true;
soundChannel = sound.play(pos);
addEventListener(Event.ENTER_FRAME, onEnterFrame);
}


private function showID3(event:Event):void {    
songInfo.text = "Album: " + sound.id3.album + "\n" + 
"Artist: " + sound.id3.artist + "\n" +
"Genre: " + sound.id3.genre + "\n" +
"Songname: " + sound.id3.songName + "\n" +
"Track: " + sound.id3.track + "\n" +
"Year: " + sound.id3.year;
}

protected function playButton_clickHandler(event:MouseEvent):void {
play();
}

protected function resumeButton_clickHandler(event:MouseEvent):void {
play(soundChannel.position);
}

protected function pauseButton_clickHandler(event:MouseEvent):void {
playing = false;
SoundMixer.stopAll(); 
playInfo.text = "Stopped";
removeEventListener(Event.ENTER_FRAME, onEnterFrame);
}

protected function onEnterFrame(e:Event):void{    
var minutes:Number = Math.floor(soundChannel.position / 1000 / 60);
var seconds:Number = Math.floor(soundChannel.position / 1000) % 60;
playInfo.text = "playing "+ minutes + ':' + seconds + " of " + songLength;
}

private function playResumeButtonState(readyToPlay:Boolean, playing:Boolean):Boolean{
if(readyToPlay && !playing){
return true;
}
return false;
}

]]>
</fx:Script>

<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
</fx:Declarations>
<s:VGroup horizontalCenter="0" verticalCenter="0">  
<s:TextArea id="songInfo" width="100%" height="300"/>  
<s:HGroup width="100%">   
<s:Button label="Browse" id="browse" click="browse_clickHandler(event)"/>
<s:Label id="playInfo" width="100%" height="40" verticalAlign="bottom"/>    
</s:HGroup>
<s:HGroup>   
<s:Button label="Pause" id="pauseButton" 
  click="pauseButton_clickHandler(event)" 
  enabled="{playing}"/>   
<s:Button label="Start Over" id="playButton" 
  click="playButton_clickHandler(event)" 
  enabled="{playResumeButtonState(readyToPlay,playing)}"/>  
<s:Button label="Resume" id="resumeButton" 
  click="resumeButton_clickHandler(event)" 
  enabled="{playResumeButtonState(readyToPlay,playing)}"/>     
</s:HGroup>
</s:VGroup>

</s:Application>

 

You would like to be able to browse and play music that is stored on a mobile device.

 

Please consider purchasing Developing Android Applications with Flex 4.5.


+
This work is licensed under a Creative Commons Attribution-Noncommercial-Share Alike 3.0 Unported License. Permissions beyond the scope of this license, pertaining to the examples of code included within this work are available at Adobe.

Report abuse

Related recipes