Search

 

July 2011
S M T W T F S
« Jun   Aug »
 12
3456789
10111213141516
17181920212223
24252627282930
31  

Tags

Archives


« | Main | »

Creating a mobile mp3 player

By Rich Tretola | July 6, 2011
19,224 views

Creating a mobile mp3 player is easy with the classes available within Flex 4.5 for mobile.

In this tutorial, we will browse a mobile device with a filter for only mp3 file types, select and load the mp3 file, read the meta data about the mp3 file, play, pause, resume, and restart the mp3 file.

Here is what the final product will look like:

Let’s review the code below:

The visual components included in this sample 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
  • When the user clicks the Browse button, the browse_clickHandler method is called. Within this method, a File is instantiated, and event listener is added for to listen for the Event.SELECT event and the browseForOpen method is called on the file with a FileFilter passed in to limit to only mp3 file types. For more information on FileFilter, please see my recipe on this here.

    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.

    When the Event.ID3 event listener is called, the sounds meta data is read by reading the sound’s id3 data. The properties available within the id3 object of the sound include the artist, genre, songname,track, and year. These properties are 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.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    <?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>

    Topics: ActionScript 3, Adobe AIR, android, Flex | 18 Comments »

    18 Responses to “Creating a mobile mp3 player”

    1. Jasmanik Says:
      July 11th, 2011 at 11:37 am

      It looks wonderfull….

      Can I have a look at the code.

      Reply to this comment

      Rich Tretola Reply:

      The source code is included in the post.

      Reply to this comment

    2. Yop Says:
      July 28th, 2011 at 3:49 pm

      quick and easy :)

      Reply to this comment

    3. Yop Says:
      July 28th, 2011 at 4:05 pm

      The file filter rock ^^

      I have a graphic glitch with the Android native fileBrowser window.
      (I am using a HTC desire with Android 2.29)

      Reply to this comment

    4. cole Says:
      July 29th, 2011 at 2:41 pm

      will this work with IOS?

      Reply to this comment

      Rich Tretola Reply:

      Well, the file browse functionality does not work on iOS. So, if you know the path to an audio file, the player would work. However, the user browse part is restricted on iOS.

      Reply to this comment

    5. JP Says:
      August 19th, 2011 at 10:48 am

      Hi Rich,

      Hope you don’t mind giving me a pointer, but is the following correct:

      Objective: Have a sound effect play with button TouchTap event on a mobile device.

      The sound file will be added as source in Flex Build Path

      private var buttonClick_snd:Sound;
      private var soundChannel:SoundChannel = new SoundChannel();

      private const PATH:String = ‘buttonClick.mp3′;
      private var file:File;

      protected function loadSounds(event:FlexEvent):void
      {
      file = new File();
      file = File.applicationDirectory;
      file = file.resolvePath(PATH);

      buttonClick_snd = new Sound();
      buttonClick_snd.load(new URLRequest(file.url));
      }

      protected function playSound(event:TouchEvent):void
      {
      soundChannel = buttonClick_snd.play();
      }

      Ques 1: Is this approach correct or is it more efficient to embed sounds?
      Ques 2: Is mp3 the best format for sounds on devices?

      Thanks in advance!

      Reply to this comment

      Rich Tretola Reply:

      For simple sounds for button clicks, I would recommend embedding them.

      Example:

      [Embed('/sounds/mysound.mp3')]
      public var mysound:Class;

      Then simply call mysound.play() on a button click event.

      Reply to this comment

      Mark Reply:

      I know that this is rather old… However, I am pulling my hair out over this. No matter what I attempt, I can not get the result I am looking for… All I want to do is play an mp3 based on which button is hit… It is driving me bonkers. Everything I attempt results in error. I attempted URLRequest with the external files, I have attempted embeded… Can someone point me in the right direction?

      Reply to this comment

      YoungSoul Reply:

      I did something like the following. I realize it is snippets of code and I can try to provide more offline if you like.

      The FileUtils.getFileUrl is only needed because I need to pull my mp3 file from the ios cache directory. Not sure about the difference with Android just yet. Assuming you can get a valid File reference though.

      protected function loadSampleFromFile():void {
      var fileUrl:String = FileUtils.getFileUrl(fileName);
      if(_originalSample == null && fileUrl != null && fileUrl.length > 0 ) {
      _originalSample = new SoundAsset();
      _originalSample.addEventListener(Event.COMPLETE, loadFromFileComplete);

      _originalSample.load(new URLRequest(fileUrl));
      }

      }

      in the complete handler, after all of the error checking I did something like the following: where pos starts out as 0 which means from the beginning.

      // 1/2 volume from beginning
      var transform:SoundTransform = new SoundTransform(0.5, 0);
      var _channelOriginalMP3:SoundChannel = _originalSample.play(pos, 1, transform);

      Hope that helps

      YoungSoul Reply:

      BTW – I used this technique in a Flex iOS app that I have in the app store now. If you search for betterwithbacon that app pulls mp3 files from a server, saves them in the local ios cache directory and then plays the mp3 file from the devices filesystem. I know this works – but it is not straight forward.

      Look at my other reply in this blog about saving data locally if your platform is iOS. Getting the file in the right directory was a little tricky.

    6. Chris Bizzell Says:
      September 8th, 2011 at 7:19 pm

      Would this work with streaming audio?

      Thanks

      Reply to this comment

    7. Chris bizzell Says:
      October 8th, 2011 at 9:09 am

      How would this work for streaming audio on iOS?
      It seems flex mobile doesn’t support all stream types

      Thanks

      Reply to this comment

    8. Nataraj Says:
      November 2nd, 2011 at 4:54 am

      HiAll,

      I am developing a “Android File Browser” application With Flex 4.5 Mobile Application using Flash Builder 4.5.
      Can any one help me with the flex code from which I can browse the Files in SD Card.

      Thank you,
      Nataraj

      Reply to this comment

    9. YoungSoul Says:
      November 16th, 2011 at 12:14 am

      Hi Rich
      I am creating a band app with Flex and I am currently streaming mp3 files from our server to play following a very similar patter of Sound.load(URLRequest(…))

      My question is, I would like to save the MP3 file so the person could listen in an offline mode. What is the best way for me to save the MP3 or Sound to a file?

      Any help and pointers to the best way to do this is appreciated.

      Thanks

      Reply to this comment

    10. YoungSoul Says:
      November 16th, 2011 at 2:31 am

      I think I have answered my own question, but for anyone else looking for the answer I will try to summarize it.

      First, use the URLLoader to load the mp3 file like the following:

      loader = new URLLoader();
      loader.dataFormat = URLLoaderDataFormat.BINARY;
      loader.addEventListener(Event.COMPLETE, onLoadComplete);
      loader.addEventListener(ProgressEvent.PROGRESS,onLoadProgress);
      loader.load(new URLRequest(_originalSampleUrl));

      Notice the setting of the format to BINARY.

      Then I used the ‘saveLocally’ method as described in this post:
      http://corlan.org/2008/09/02/storing-data-locally-in-air/

      then to play the file after it was saved, I used what was described here. Something like:
      sound.load(new URLRequest(file.url));

      Those are pretty much the steps to read a remote MP3, save it, and then play it from the file.

      Reply to this comment

    11. Bernard Says:
      December 9th, 2011 at 11:44 am

      Hi Rich,

      I’m developing an app called Radio-C and this is the link in the Android Market :
      https://market.android.com/details?id=air.RadioC

      With the app, I can select many radio stations (streaming mp3) and listen without problem.

      My problem is this …

      When I select the same station several times, the connection does not resume. But if I select another station, where everything works. I encounter this problem on Air for Windows and Air for Android. I have no problem on Air for Mac or IOS. For now, the only solution is to exit the application and restart.

      Have an idea?

      Here are some of the code :

      private function playSound(externalUrl:String):void
      {
      if (channel) {channel.stop();channel=null;}

      channel = new SoundChannel();

      soundPlay = new Sound();

      soundPlay.load(new URLRequest(externalUrl));

      channel = soundPlay.play();
      }

      Reply to this comment

    12. sidik Says:
      December 18th, 2011 at 11:33 pm

      help me
      TypeError: Error #1034: Type Coercion failed: cannot convert views:

      Reply to this comment

    Comments