Wednesday, May 18, 2011

Using the Media Class in PhoneGap

I've seen a lot of posts recently on the PhoneGap Google Group so I wanted to put something together illustrating the use of the PhoneGap Media class. While putting this example together I noticed a few quirks of the Media class which I'll try in describe in more detail so that other folks don't run into the same problems. Also, this example has been tested on Android but it, for the most part, should work on iOS as well. Part of PhoneGap 0.9.6 will be normalizing how the Media class works between all the major platforms so look for improvements soon.

The example will be a basic Media player with play/pause and stop functionality.  Here's a quick screen shot:


On Android you can create a Media object using one of three ways.  The first way is referencing a file in your "android_asset" directory.  For instance if you have a file called "test.mp3" in your projects "assets/www" directory you would create a new media object by doing:
myMedia = new Media("/android_asset/www/test.mp3");
The second way to create a media object is by referencing a file on your devices file system. Say for instance, you have a file at "/sdcard/test.mp3" of your device you would create the media object by doing:
myMedia = new Media("test.mp3");
You'll notice the "/sdcard/" portion of the file name is omitted. This is because on Android the Media object makes an assumption that the file is stored on your "/sdcard". Note: I may change this behaviour in an upcoming version of PhoneGap but I'll strive to retain backward compatibility.

The third and final way to create a media object is by URL:
myMedia = new Media("http://audio.ibeat.org/content/p1rj1s/p1rj1s_-_rockGuitar.mp3");
Now that we've got our media object we want to be able to do the sensible thing and be able to play the audio. For that we'll create a play button:
<a href="#" onclick="playAudio()"><img id="play" src="images/play.png" /></a>
and corresponding function:
function playAudio() {
  myMedia.play();
}
Now the stop button is just as simple:
<a href="#" onclick="stopAudio()"><img id="stop" src="images/stop.png" /></a>
function stopAudio() {
  myMedia.stop();
}
However, that's pretty lame as it would be great to be able to pause the audio playback so let's enhance out play and stop functions:
function playAudio() {
  if (!playing) {
    myMedia.play(); 
    document.getElementById('play').src = "images/pause.png";
    playing = true; 
  } else {
    myMedia.pause();
    document.getElementById('play').src = "images/play.png";    
    playing = false; 
  }
}

function stopAudio() {
  myMedia.stop();
  playing = false;
  document.getElementById('play').src = "images/play.png";    
}
Now when the user is playing some audio the play button image is changed to a pause button and hitting the pause button again will, well, pause the playing of the audio. I'm cheating a little bit by using a global variable called playing as it could easily be changed to check what the currently set image is and doing the correct action.

Of course it is always nice to know where we are at in a given audio clip so we can add some functionality to get the current position of where we are at in the file. So let's create a function that will run every second and update our HTML with the current position of the audio file.
<p id="audio_position">0.000 sec</p>

var mediaTimer = setInterval(function() {
  myMedia.getCurrentPosition(
    function(position) {
      if (position > -1) {
        document.getElementById('audio_position').innerHTML = (position/1000) + " sec";
      }
    },
    function(e) {
      console.log("Error getting pos=" + e);
    }
    );
}, 1000);

That's all well and good but there is one thing that's slipped by our notice so far. When an audio clip finishes playing the pause button should become a play button in case the user wants to play the clip again. That can be accomplished by adding a success call back to our Media object constructor. The success call back is executed when the clip has finished playing. So we'll now instantiate our media like this:
myMedia = new Media("/android_asset/www/test.mp3", stopAudio);
So what we have here are the beginnings of a audio player application for Android. In part two of this article I'll enhance the app with the File System and Directory API showing you how to scan your device for audio files.

The HTML and images can be found here and below is a listing of the HTML code uses in this article. I added a select box so that users could switch between the different ways of creating a Media object. You'll have to supply your own MP3 file as I don't want the RIAA coming after me.

131 comments:

  1. Hi Simon,

    So there seems to be a fourth way to create a Media object and that is with src=null. For iOS, this seems to create something like "documents://1203044830072" where the numbered part is unique with each creation.

    What is it creating?

    Thanks for your help,
    Jo

    ReplyDelete
  2. If I'm not mistaken if you pass src=null to the Media class on iOS it will make it's own file name using the current date. This file name will be used if you call the startAudioRecord() method.

    ReplyDelete
  3. media.getCurrentPosition and media.getDuration currently do not seem to work in ios , is this support coming in a future release?

    ReplyDelete
  4. Correct, those methods don't work on iOS yet but we are looking into including them for 1.0 due end of July early August.

    ReplyDelete
  5. I've just started developing a media application with PhoneGap, and I noticed that when loading an audio file from a URL, the app becomes unresponsive until it is ready to play. Could this be mitigated by using Web Workers to load them in a different thread?

    ReplyDelete
  6. in simulator it works

    but in real device its not work

    i want to stream shoutcast

    ReplyDelete
  7. @Rob Archer
    I think it has to do with the OpenCore instance being allocated by the Android OS.

    @Ibnu Maksum
    I'll help you over on the google group.

    ReplyDelete
  8. Hi Simon, i have one cuestion... I made all what u said.. but allways, no matter what i do and what function use i allways get a prompt with this:

    gap:["Media","startPlayingAudio","Media0",true]

    ????... U have any idea what is this... i found the code in phonegap-1.0.0.0.js this code:

    var r = prompt(PhoneGap.stringify(args), "gap:"+PhoneGap.stringify([service, action, callbackId, true]));

    and this is the prompt what i getting... Plis help!

    PS: sorry for my inglish, i just starting with it

    ReplyDelete
  9. Hi again Simon.. a few seconds I realized that in the BROWSER allways going to get the prompt, in the divice it s Work Amazin! Both codes, your and mine.
    Thanks again.

    Best regards

    ReplyDelete
  10. In the example above - with the file page "/android_asset/www/test.mp3" ... will this work "out of the box" on an iPhone is the test.mp3 file is in the same place (www/test.mp3)?

    ReplyDelete
  11. @Dr Senior

    No, it won't work out of the box on iOS as it has no concept of the android_asset directory. You'll need to do a conditional for now to determine if you are on iOS or Android and pass the appropriate path.

    I'm advocating we spend some time in 1.1 to make Media sane across all platforms.

    ReplyDelete
  12. Hello, does media.getDuration work yet on iOS?

    ReplyDelete
  13. @anh

    It should but if it doesn't open an issue on github and we'll get it fixed.

    https://github.com/phonegap/phonegap-iphone/issues

    ReplyDelete
  14. hi, can you explain, how to use a playlist (play several files sequentially)?

    ReplyDelete
  15. @Lars

    I've been meaning to write some code to do exactly that. I would keep and array of file names or urls then in my onSuccess method of the Media constructor, which is stopAudio in the example, I would add one to my array index create the new Media object and start playing.

    To make things faster I'd probably keep a buffer of 3 to 5 Media objects so that people could skip or go back in the play list.

    ReplyDelete
  16. Hi Simon,

    i wanna add the ID3 in the player,
    i used from this library :
    http://web.ist.utl.pt/antonio.afonso/www.aadsm.net//libraries/id3/

    http://github.com/aadsm/JavaScript-ID3-Reader

    this is my code:

    var file="/sdcard/music.mp3";
    function callback() {
    var pic = ID3.getTag(file, "picture");
    var artist = ID3.getTag(file, "artist");
    var title = ID3.getTag(file, "title");

    document.getElementById('judul').innerHTML = artist+ "\r\n";
    document.getElementById('judul').innerHTML += title+ "\r\n";
    document.getElementById('urlartis').src = "data:" + pic.format + ";base64," + Base64.encodeBytes(pic.data);
    }
    myMedia.play();
    ID3.loadTags(file, callback, ["picture", "artist", "title"]);
    playing = true;

    it took 40 second to show the artist & title. but the picture not showed at all.

    is there a way to speed up this, and also why the image does'nt show.

    thanks before for your help.
    Ali

    ReplyDelete
  17. @Arkan

    Sorry, I can't help you here as I've never used that library but it looks like a cool one. If I had to guess what it is slow is that they are using JavaScript to read a binary file which it doesn't support out of the box. I number of tricks have to be done for the library to read the binary data.

    ReplyDelete
  18. Hi simon....

    Thanks for the tutorial...
    I tried it, it working fine in emulator, but it is not working in my device sony erricsson xperia... what is the problem....

    ReplyDelete
  19. @Jithin

    The first thing that jumps out at me is that your Sony may not have to proper codec for playing the file. Run "adb logcat" to see what is going on.

    ReplyDelete
  20. Hi simon, thanks for the quick response.

    Can i add volume controller for this app. ?

    Regard
    Jithin

    ReplyDelete
  21. @Jithin yes, you can. There is a setVolume() method on the Android PhoneGap Media class. It is coming to other platforms.

    ReplyDelete
  22. Hi simon,

    Can u explay how to run "adb log cat", because a m very new to android.

    thanks
    jithin

    ReplyDelete
  23. Hi simon,

    your demo url http://audio.ibeat.org/content/p1rj1s/p1rj1s_-_rockGuitar.mp3 is working fine in both emulator and device. But i applied another url http://live.ardanradio.com:8228/;audio.mp3. but it is working only in emulator, not working in device. i run adb logcat, it showing D/dalvikvm( 2335): GC freed 7225 objects / 344040 bytes in 100ms
    W/MediaPlayer( 2335): info/warning (1, 32)
    I/MediaPlayer( 2335): Info (1,32)
    D/dalvikvm( 2335): GC freed 20579 objects / 799192 bytes in 118ms
    D/dalvikvm( 2335): GC freed 17355 objects / 657824 bytes in 115ms.

    and also i checked with this shoutcast url:http://62.75.216.8:8008/;audio.mp3. but same problem

    ReplyDelete
  24. @Jithin

    Can't test right now as I am at a conference but try removing the /;audio.mp3 from the end of the URL.

    ReplyDelete
  25. hi simon

    thanks for the quick response,
    i tried with http://62.75.216.8:8008 this url. But working only in emulator but not in emulator.

    ReplyDelete
  26. hello sir me new to phonegap n android development i have audio in binary i.e base 64 format however it does not play on android audio tag.....please help me...

    ReplyDelete
  27. @Unknown the "audio" tag does not work in the WebView for some strange reason. Use the Media class instead.

    ReplyDelete
  28. @Jithin that URL plays fine in the emulator and my Samsung Galaxy S phone. What version of Android is your phone using?

    ReplyDelete
  29. Hai simon,

    i am using samsung galaxy s5570(android 2.3)...

    ReplyDelete
  30. Sorry @Jithin I can't reproduce your problem. I wonder if it is a missing codec problem on your phone as, as you say, it works in the emulator.

    ReplyDelete
  31. Hi simon,

    I don't know what wrong thing i did. i checked with these two url 1. http://62.75.216.8:8008
    2. http://stream.lexandterry.com:8000

    But first one working only in emulator and second one working in both device(samsung galaxy s) and emulator. After that i checked with sony Ericsson experia. But same problem happening.

    ReplyDelete
  32. Sorry @Jithin, I don't know what to tell you. Some devices may not support streaming audio. I know you need Android 2.2 or better for this to work though.

    ReplyDelete
  33. hello Simon Sir,

    my application requires me to play mp3 BASE64 audio..

    phonegap my media doesn't play bas64 audio source...
    kindly help Thanks in advance...

    ReplyDelete
  34. @Unknown you'd have to write a plugin to convert your base64 encoded mp3 to a binary file and then pass that the the Media class to be played.

    ReplyDelete
  35. Dear Simon,

    Thanks for writing this awesome tutorial. I'm having trouble getting the full filepath of the new recorded Media object and uploading it to the server. I wonder if you could suggest how to do it? I've already tried just saving it to android_asset but file upload doesn't work with /android_asset/www/myrecording.mp3 - I know the problem isn't with my file upload methods because I can already upload other files. I just can't get the fullpath of this recording I've made using your recorder/player code.

    ReplyDelete
  36. @Andrea Levinge

    That's because the Media class isn't following any know spec. The file name you pass in is assumed to be under /sdcard. So if you pass in temp/data/record.mp3 the file will end up at /sdcard/temp/data/record.mp3.

    You cannot save files to the android_assets directory as that is a "virtual" directory that is part of your .apk.

    ReplyDelete
  37. Hi Simon,

    Thank you so much for replying!
    I created a new Media file:

    mediaRec = new Media('myrecording.amr', onSuccess, onError);
    mediaRec.startRecord();

    I want to get that mediaRec's fullPath.

    I check where the file is actually located on my sdcard and it's located at /mnt/sdcard/external_sd/

    I've tried the following filepaths for uploading:
    /mnt/sdcard/external_sd/myrecording.amr
    /sdcard/myrecording.amr
    myrecording.amr

    but the upload always fails as if the file does not exist.

    I've successfully uploaded photos and videos already but those were captured using the example code from the API. This recording uses a new Media object. I suspect it is saving it at content://media/external but I don't know how to get the URI of the new Media object I've created. All I know is that photos are getting temporarily saved to content://media/external/images/media/ but I'm sure that's different for audio recordings. I've tried grabbing it from the onSuccess method, but it seems to pass nothing to the onSuccess method when creating a new Media file.

    Let us know when your book comes out, I will be ordering it for sure.

    ReplyDelete
  38. Kukulako from Puerto Rico...

    Can I put seek bar or progres bar?

    ReplyDelete
  39. @kukulako

    Sure, but you'd have to implement it yourself using getDuration and getCurrentPosition.

    ReplyDelete
  40. Simon,

    How does this work with PhoneGap Build and PhoneGap 1.2? Where are the media files that are packaged with the application placed and how would I access them cleanly from any platform?

    ReplyDelete
  41. @John

    I'm not sure how it would work on the build service as I don't use it. For regular PhoneGap you'd have to do an OS detection and pass the correct prefix along with your file name to the Media constructor.

    ReplyDelete
  42. This has the potential to be great, but can I have a looping background track with an independent volume, while playing a foreground track, or playlist?

    ReplyDelete
  43. @John A

    These are some great ideas and we should make the Media class better for people making games. You should raise a ticket at:

    https://issues.apache.org/jira/browse/CB

    ReplyDelete
  44. Hi Simon,
    Please help me out in displaying the All Songs and playlists of android device and how to access android device media table to display the list using phone gap.

    Thanks in advance.

    ReplyDelete
  45. Hi Uday, I haven't written the code yet but what you'd need to do is query the content provider for the available music.

    http://developer.android.com/guide/topics/providers/content-providers.html

    ReplyDelete
  46. Thanks Simon for reply,
    Is there any way in phonegap to get the data using content provider and display using phonegap javascript and html5. Do i need to write any phone gag plug in to read data using content provider.

    ReplyDelete
  47. @Uday

    Yes, you'd need to write a plugin for that type of functionality.

    ReplyDelete
  48. @Andrea

    Sorry I missed your comment. After you call stopRecord() the file should be moved to /sdcard. So you should be able to use:

    /sdcard/myrecording.amr
    of
    file:///sdcard/myrecording.amr

    ReplyDelete
  49. In my app, setVolume() possible values are 0.0 - 1.0. It's looking for a float value.

    ReplyDelete
  50. Hi Devgeeks,

    I'm testing your demo with a shoutcast streaming url and it does'nt work :(

    I tested with the streaming url somebody said in this google group.

    The debug window in Xcode stucks on this line:
    2012-02-08 15:02:21.274 altafullaradio[3219:13403] Will use resource 'http://live.ardanradio.com:8228/;audio.mp3' from the Internet.

    I'm using the latest version of phonegap, and iOS 5.0.1

    With a normal mp3 url it works good. But with a streaming url it does'nt

    What I should try?

    Thank you.

    Kind regards,

    ReplyDelete
  51. @MrReview

    Sounds like a bug in iOS. I'll let Becky or Shaz address this on the google group where you also asked your question.

    ReplyDelete
  52. Very useful tutorial on Media Player.
    Maybe you have an hint for a apparently blocking problem: is there a way to pass the mediaplayer between different html pages (not dynamically injected)? If I create the mediaplayer in page one.html, how could I use it in page two.html (loaded as "external")? I know the object is still alive, I can still hear audio (if I don't release it), however, I've no reference to the mediaplayer to control it.
    Thank you for any hint!

    Kind regards
    Paolo

    ReplyDelete
  53. @pcasa.net

    Once you go to two.html all the JS from one.html is unloaded. You'll want to register a "pause" listener to release your Media object. Then create a new Media object in page two.html.

    ReplyDelete
  54. Hi Simon,

    How is it decide where the media file is when I am developing an android phonegap application?
    assets/www or sdcard

    ReplyDelete
  55. @ArulMurugan

    It's up to you. Files that are deployed with the application end up in assets. Files downloaded generally end up on the sd card.

    ReplyDelete
  56. Hi Simon ,
    When the phone is sleeping or active other app ,how can i keep the audio play in the background? would u give me some solution suggestion? and does this be feasible ?

    ReplyDelete
  57. @juxiang peng

    As long as you use the home button to put your app in the background it should continue to play.

    ReplyDelete
  58. Thank u for the reply!
    In android it play good ; but ios is not ok, i maked the audio play as
    //code start
    new Media(src ,onSuccess, onError,onStatus).play() ;
    //code end

    Any suggestion for the ios ?

    ReplyDelete
  59. @juxiang peng

    I would split out the play onto the next line. iOS might need some time to prepare the Media object.

    ReplyDelete
  60. Thank u for your relay ;
    I solve the background play problem from this link :http://mobile.tutsplus.com/tutorials/iphone/ios-sdk_background-audio/

    and *-info.plist a configure :

    Once the project has been created go to NoiseMaker-Info.plist and add UIBackgroundModes as a new row. It should then create the array. "

    ReplyDelete
  61. Thanks Simon, i was buid Android apps with this plugin and tested with Android Ice Cream Sandwich.
    Its works, but the buffering is to much wasting time.

    ReplyDelete
  62. I was succesfully tested it on Android Ice Cream Sandwich, but how about the Blackberry? It also works too?

    ReplyDelete
  63. Hi..

    I'm beginner on phonegap.
    I make some page that play some music on each page.
    Page1.html, play page1.mp3
    Page2.html, play page2.mp3

    but, when I'm on page2.html and go back to page1.html, the page1.mp3 becomes echo..
    when I go to the page2.html and go back again to the page1.html, the page1.mp3 becomes double echo -_-

    what should I do ???

    very need help.
    thank you

    ReplyDelete
  64. I have download music file in phone memory. Location is : file://data/data/com.luontoportti/files/2/audio/Accnis.mp3

    How can I play these files. It works if i download file in sd card. But not for this. Please help me.

    Regards
    Reezo

    ReplyDelete
  65. Thanks for the great post....
    I include my app resources in the html application manifest cache.
    What I want to do is cache the mp3 file in my manifest as well so it can be play when my app is offline. it doesn't seems to work (it works fine if I am online). can this be done using phonegap media type while offline?

    thanks,

    ReplyDelete
  66. @profilcaleg

    Yeah, if you want to get around the buffering you will probably need to download the file to the sdcard to make playback faster. Yes, I believe Drew just added support for Media for BB,

    ReplyDelete
  67. @ikkimassu

    Register an event listener for the "pause" event and stop playing your file when you leave the page and then wait for the "resume" event to start playing the file again.

    ReplyDelete
  68. @reezo ahmad

    Only the application that is "com.luontoportti" will be able to play those files. No other application will have access to your internal app storage directory. That's why it works when they are on the sd card as that has not file protection.

    ReplyDelete
  69. @wilson

    The Media class will not look at the app cache. You'll need to download the audio to the sd card in order to cache it manually.

    ReplyDelete
  70. HI Simon and thanks for your great works
    I use you plugin for playing audio in an app on my Galaxy tab (android 3.2, Cordova-1.8.1) and its works perfectly
    If i try this app on an LG (Android 2.3.3) or a samsung (Android 2.3.3) its does'nt works
    What could you suggest ?
    Thanks

    ReplyDelete
  71. @Patrick

    Are you trying to play a local file, a file in assets or a remote file? Run "adb logcat" to see what is going on.

    ReplyDelete
  72. Thanks for your answer.
    I am sorry but i make a mistake, i was calling phone gap 1.8.1 as i was running on phone gap 1.9.
    All is ok now

    ReplyDelete
  73. Hi Simon,
    I am trying to create a audio player using phonegap and jquery,
    I want to use the duration of the Media object to set the slide 's max value. And the position of the Media to move the slide the slider accordingly.
    But the duration of the media using the getDuration is always returning -1, because of which the slider stop at the end of 100 sec.
    How can I fix this?
    Also I getting these errors..


    (1) D/CordovaLog(7511): file:///android_asset/www/PhopeGapLibraries/cordova-1.6.0.js: Line 3491 : JSCallback Error: TypeError: Cannot set property '_position' of undefined


    (2)JSCallback Error: TypeError: Cannot set property '_position' of undefined at file:///android_asset/www/PhopeGapLibraries/cordova-1.6.0.js:3491


    (3) E/MediaPlayer(7511): Attempt to perform seekTo in wrong state: mPlayer=0x0, mCurrentState=1


    (4) SCallback Error: TypeError: Cannot set property '_duration' of undefined


    (5) I/Web Console(7511): JSCallback Error: TypeError: Cannot set property '_duration' of undefined at file:///android_asset/www/PhopeGapLibraries/cordova-1.6.0.js:3491

    ReplyDelete
  74. @Doubts

    That is odd the getDuration should return the proper duration of any local media file. As for building the slider you won't be able to seek in a file that is not playing as the file is not prepared yet. We've made some changes in 2.0.0 and we are going to get it right in 2.1.0 to make Media work much better. Get ready for another blog post later this month.

    ReplyDelete
  75. Hi Simon,
    I wanted to have a toolbar kind of interface for my media player while the phone is in lock mode. Do you have any suggestions as to how to implement it?
    I am using phonegap, jquery on android.

    ReplyDelete
  76. @Doubts

    It isn't very easy to add stuff to the lock screen. You would need to write a plugin for sure. Check out this SO answer for some possible solutions:

    http://stackoverflow.com/questions/4116001/android-lock-screen-widget

    ReplyDelete
  77. Hi Simon,

    Thank you for your great article.

    It seems that something is not working correctly in my setup. If I call play() only once, the player does not start. Only if I call it twice the playback starts. What am I doing wrong?

    Here is the environment; Samsung Galaxy with android 2.3.3, cordova 2.0.0.

    var m = null;
    function myTest1(){
    if(m != null){
    m.stop();
    m.release();
    m = null;
    }
    m = new Media(media_src, onSuccess, onError);
    m.play();
    m.play();
    }

    Thank you,
    -igor

    ReplyDelete
  78. @igor

    You are running into a bug in 2.0.0. 2.1.0 with a bunch of fixes to Media will take care of it.

    ReplyDelete
  79. hi simon
    i have added your given code and images to asset/www
    but it doesn't work
    any other changes to made?
    i am new to phonegap
    waiting for your response

    ReplyDelete
  80. @umairqureshi_6 et al

    I'm going to stop answering comments that are along the lines of "it just doesn't work". Obviously it does work or I wouldn't have posted this information up in the first place. I'm sorry but you probably didn't follow the instructions correctly or at least that's what I'll have to assume if you don't give me enough information like:

    1) Android version
    2) PhoneGap version
    3) error logs (learn "adb logcat" folks)

    Sorry to be an ass but I don't have time to debug everyones problems or chase them for information they aren't providing.

    ReplyDelete
  81. Hi simon :
    It's ok for playing files under /android_asset/sample.mp3. but it will failed when using url as remote http (ex :http://audio.ibeat.org/content/p1rj1s/p1rj1s_-_rockGuitar.mp3). Log as below . Can media object support playing mp3 via http ?
    emulator : android 2.2
    cordova : 1.8
    Error : E/MediaPlayer(685): Error (1,-2147483648)

    ReplyDelete
  82. @吳志忠

    Yes, it supports playing files over http.

    ReplyDelete
  83. Hi Simon, thanks for this great example, im trying to stream this url: http://streaming507.com:9996 is shoutcast aac+ but when i click play i just hear strange noices not the music itself can u help me please.

    thanks very much.

    ReplyDelete
  84. @Alexis Gaitan

    Right now it says that server is down so I can't tell you for sure if that url would even work. I can tell you that streaming audio only works on Android 2.3 or later, if I remember correctly. Maybe it is Android 2.2.

    ReplyDelete
  85. Simon, thank you for the wonderful article, but could you please help me figure out the correct directory structure to play my audio files in PhoneGap on Android. Currently my audio is stored in the directory: assets/www in a folder audio.

    Thatis the index.html file in assets/www has path names referred as: audio/publisher104_book311_view3.mp3

    ReplyDelete
  86. @Ozzy Shahzaib

    You should use:

    myMedia = new Media("/android_asset/www/audio/publisher104_book311_view3.mp3");
    myMedia.play();

    ReplyDelete
  87. Hi Simon,

    I have been following your posts all over the web (here, stackoverflow, ect), and I have been trying to resolve this issue that I have with Cordova 2.1 for Android...

    Basically I want to create an array of Media objects in javascript upon recording the file. At the top of the js file an array - mediaFiles = []; - is initialized, and, upon each "onRecord" event that is triggered, I do this:
    -
    mediaFiles[counter]= new Media (path, success, error, status) ;
    -
    and then i do:

    mediaFiles[counter].startRecord and mediaFiles[counter].stopRecord respectively.

    --------

    I know that the media files are created successfully, because when I open them with a file manager I can play them back. However here is where my confusion occurs.

    On the "playFile()" event that is triggered, I attempt to simply determine which file the user wants to play, and then call:
    -
    mediaFiles[item].play();
    -
    Now, this is the ONLY place that it does not work! I know I am referencing the correct array index, i've checked it over and over again, and I cannot seem to find out what is wrong... Furthermore, there is nothing in the LogCat either. I have tried to do an "alert(mediaFile[item].play)" and all I get is the function definition of the play function for the media File.

    Do you think you would be able to reproduce the same issue?

    Thank you very much for your help!
    -Mike

    ReplyDelete
  88. @Mike Alireza

    There may be a bug in the Media API. You should try creating a new Media object to play the file you've recorded.

    ReplyDelete
  89. @Simon

    Thank you for your reply.

    Interestingly enough there was a phonegap update on Nov 1st (which you posted as well I believe). I reverted back to my initial version with 2 objects only, each being created when needed and released when not needed (the correct way).

    Surprisingly, it works perfectly fine now...Guess there was a bug!

    Thanks!
    -Mike

    ReplyDelete
  90. @Mike Alireza

    Great, glad we fixed it.

    ReplyDelete
  91. getCurrentPosition can be terribly inaccurate at times. Why is it implemented as an asynchronous callback?

    I have detailed description of the issue posted here:

    http://stackoverflow.com/questions/12633726/phonegap-media-getcurrentposition-is-inaccurate-and-slow

    ReplyDelete
  92. @Dave Lowerre

    I feel your pain. No one is more disappointed in the Media API than I am. I've been after the guys to do a review of this API for ages. I really want to replace it with and implementation of the W3C Audio tag API in JavaScript instead.

    Have you looked at the Low Latency Audio Plugin? It may not do 100% what you want but it is probably more performant and could be modified to return a current position.

    https://github.com/phonegap/phonegap-plugins/tree/master/Android/LowLatencyAudio

    ReplyDelete
  93. Hi Simon,

    Thank you so much for this awesome tutorial. I'm new to PhoneGap and jquery but I would be grateful if you can help me.

    I'm using the code you have posted and I was wondering if its possible to stop the media playback if the user decides to leave the page.

    Excuse my poor english. Many Thanks for your help.

    ReplyDelete
  94. @Mishbah Razzaque

    Register an event listener for the "pause" event. When you get a pause event call media.stop().

    ReplyDelete
  95. Hi Simon

    Can we analyse audio intensity of the recorded sound using phonegap.

    ReplyDelete
  96. @rahulmendonca

    No, you'd need to write a plugin to do this.

    ReplyDelete
  97. any thought on how to continue playing audio in the background?

    thx

    ReplyDelete
  98. @scottii

    What behaviour are you getting as I can do that just fine on Android? Make sure keep running is set to true in onCreate:

    super.setBooleanProperty("keepRunning", true);

    ReplyDelete
  99. Hi Simon,

    How can we add a loading image while the audio file is streaming. My files are bit larger so the loading of file taking some time, how can I solve it.

    ReplyDelete
  100. @joseph

    The Media API is actually pretty lame. There is no event fired when the file is loaded. Without making some additions to the native code I don't know of a way to do this.

    ReplyDelete
  101. @Simon

    Thanks for this great tutorial, it really opend my eyes!! But since 3 days im really disfrustated, i only can play the mp3 from the Url. I tried the whole tutorial with Phonegap Build, so that i add the test.mp3 simply in the same package with the index.html. But somehow it wont play! And how do i save the mp3 file on the sdcard, to load it how you described with ("test.mp3")? Greetings from Germany! Hope you understand my englisch! Sorry!

    ReplyDelete
  102. @gammler

    You should have any trouble playing the file if you use the path I specified in the tutorial if the mp3 is in the same folder as your index.html.

    If you want to put a file on the sdcard use adb push.

    ReplyDelete
  103. Hi Simon, I am using your exact code and when i run the App;icationin Browser Mozilla just to test if this triggers the File or not, I am getting the following error:

    TypeError: myMedia is null
    [Break On This Error]

    myMedia.play();

    ReferenceError: Media is not defined
    [Break On This Error]

    ...dia = new Media(yourSelect.options[yourSelect.selectedIndex].value, stopAudio, n...

    ReplyDelete
  104. @randhawaz.com

    The Media object doesn't exist in Firefox.

    ReplyDelete
  105. Hi Simon,

    Here i tested on Chrome:

    Uncaught TypeError: Cannot call method 'play' of null
    playAudio
    onclick

    Uncaught ReferenceError: Media is not defined
    updateMedia
    onchange

    ReplyDelete
  106. @gavyrandhawa

    It's not in Chrome, Firefox, Opera, etc. you have to run it as a PhoneGap application in the emulator or on the device.

    ReplyDelete
  107. Hi Simon
    thanks for the excellent article.

    I'm experiencing a strange problem that I can't find any leads on:

    In a simple PhoneGap Android project, I can play sound just fine through the Media object.
    However, within a larger framework I'm using on a current project at work, the sound does not play at all. No errors thrown - just no sound at all.

    The framework links in many other javascript libraries for various purposes, but none related to audio.

    Are there any know issues/conflicts between PhoneGap and other libraries?

    (note - iOS works fine)

    Thanks
    Greg

    ReplyDelete
  108. @gmayer

    None that I know of. You may want to try grepping the other libs for instances of "Media" to see if there is a conflict.

    ReplyDelete
  109. Hi Simon I am trying to play an audio using different url. The audio is playing using http://audio.ibeat.org/content/p1rj1s/p1rj1s_-_rockGuitar.mp3 url but when I try a different url, there's no response. Please help.

    ReplyDelete
  110. @Nidhi Dagar

    What URL are you using? Did you make sure it was whitelisted? Do you know if the codec is compatible on your OS?

    ReplyDelete
  111. Hi Simon, I am trying record and play on the same file. Record is working but play from /sdcard/ is not working on the device. I even tried creating a new Media object.
    Code: http://pastebin.com/GUG0jTUh

    What might be the problem?

    ReplyDelete
  112. @Gautam Kishore

    Looking at your code in pastebin you use "/voiceit/myrecording.mp3" as the location of your file. That means it is trying to store it in the root file system in a directory called "voiceit". First thing I would do is to try your code with just "myrecording.mp3" which should just drop it in the sdcard. Then if that works and you have a directory called "/sdcard/voiceit" then try using the path "voiceit/myrecording.mp3" instead.

    ReplyDelete
  113. Thanks Simon. But, I've tried only myrecording.mp3 too. Also, the extra slash I noticed and removed after I pasted it to you in pastebin and its creating the file in /sdcard/voiceit/ but my problem is, its not playing from sd card.

    ReplyDelete
  114. @Gautam Kishore

    Try using the paths "voiceit/myrecording.mp3" and "/sdcard/voiceit/myrecording.mp3" either one of those should work. If they don't what are you seeing in "adb logcat" when you try to play the file?

    ReplyDelete
  115. Hi Simon,

    I wanted to play an Url streamed thru shoutcast server in windows phone 7 in cordova. I tried it many times without any luck. Can u pls suggest a wau to do it. Thanks in advance.

    ReplyDelete
  116. @Sunil

    Sorry, I don't know if Windows Phone can play a shoutcast stream. Try asked Jesse MacFadyen @purplecabbage

    ReplyDelete
  117. Great post Simon. My media player works great on android but on ios it seems to freeze for a bit before finally playing. Any idea why?
    My code is thus

    var path = "http:// ....";
    var player = new media (path, onsucess, onfailure)
    player.play()

    The log file just says "will use resource 'http://....' from the internet."

    Thanks

    ReplyDelete
  118. @Kingsley

    It probably takes a bit to buffer the file before it can start playing. Typically http delay.

    ReplyDelete
  119. Hi, Simon. The file format on Android is not 'mp3'. The codec is 'AMR' and the container is '.3gp'. What's more, on iOS it changes to '.wav'. Why does no one change the document till now? It mislead lots of people.

    ReplyDelete
  120. @My computer science learning log

    Sorry, we're not perfect. Sometimes we make mistakes.

    ReplyDelete
  121. Hi Simon,

    I have been facing an issue with Media object while playing live streaming (shoutcast). It looks like Media object's error function does not get called if the URL fails to return valid stream (bytes) but does return status code 200 (OK). The LOG CAT clearly shows that there is an error and Media cannot be played but this error message does not get forwarded to the media error function and Media object dies silently. The error function only gets called when I try to call Media.Stop() function.

    I tested the same URL on Chrome, that implicitly uses HTML5 Audio object, and it seems like in case they do not get valid stream but 200 (OK), the Audio object keeps on trying and stops until it gets audio stream back. I used fiddler and can see Audio object calls the same URL again and again until it gets response with stream.

    So my question here are,

    1) Can we make Media object works the same way HTML5 Audio control works?

    2) Can we force Media object to call error function in case there is any error (just after play).

    I am hoping that you can help me on this.

    URL
    *******
    http://radio.sgpc.net:8020/live16?1.mp3

    LogCat logs
    ********************
    08-12 11:09:32.348: E/HTTPStream(21328): HTTPStream::receive(): recv failed, server is gone, total received: 0 bytes
    08-12 11:09:32.348: E/NuHTTPDataSource(21328): NuHTTPDataSource::readAt(offset 0, size 65536) err(-1005) = internalRead()
    08-12 11:09:32.348: E/NuCachedSource2(21328): source returned error -1005
    08-12 11:09:32.548: E/MediaPlayer(31929): error (1, -2147483648)
    08-12 11:09:32.548: E/MediaPlayer(31929): Error (1,-2147483648)
    08-12 11:09:42.988: E/CordovaWebView(31929): CordovaWebView: TIMEOUT ERROR!


    Many Thanks,
    Khushwant

    ReplyDelete
  122. @Khushwant Singh

    Yeah, I'd have liked to have fixed the Media object a long time ago but we never seemed to have had the time. You have a good case and description here, you should open a bug on JIRA:

    https://issues.apache.org/jira/browse/CB

    ReplyDelete
  123. Hi Simon
    I have query, when i'm try to convert website to android apps using phonegap its work fine but audio backgroud not functioning. can u please guide me step by step.

    Thanks
    Pradeep
    itspradeepit@gmail.com

    ReplyDelete
  124. @PradeepIT

    Are you using an audio tag or the Media class from PhoneGap? The audio tag doesn't work too well in a WebView.

    ReplyDelete
  125. Hey Simon,

    really hoping you can shed some light on this, as I've spent a bunch of time trying to get things working with the Media class.

    Here's the deal, I'm first using fileTransfer to download the MP3 and putting it into a sub directory called "cornish-mining" on the sdcard. But when I use the exact same URI in the media statement it does not play the file when on an Android device (iOS is just fine)

    The file ends up in file://mnt/sdcard/cornish-mining/track_1.mp3

    So after reading all the other comments and your instructions I tried moving the MP3 to the root of the SD card, and changed the path accordingly, no joy.

    I've had no joy with

    /mnt/sdcard/track_1.mp3
    /mnt/sdcard/cornish-mining/track_1.mp3
    /sdcard/track_1.mp3
    /sdcard/cornish-mining/track_1.mp3
    track_1.mp3
    cornish-mining/track_1.mp3

    (not forgetting that I have the same MP3 in the root and in the cornish-mining directory.

    I'm really at a loss as to where to go next, so strange that it works on iOS and not on Android.

    To add to the plot, I use window.appRootDir.fullPath to get the fully qualified location of the app directory (file://........../cornish-mining/) on the SD card as I know on my old galaxy-y the file path is vastly different to the S-4, neither of which are able to play the audio.

    Please tell me I am just being silly and making a noob mistake.

    Many thanks for your help in advance, if you want to see the code do let me know

    ReplyDelete
  126. @phatreaction

    Okay that should work but one thing I notice is you are using the path "file://mnt/sdcard/cornish-mining/track_1.mp3" with 2 / but you should double check to make sure it has 3 / as in "file:///" as the first to are for the protocol and the last indicates the root of the file system.

    The Media class is a bit of PITA and I wish we'd refactor it. What are you seeing in "adb logcat" when you try to play the file? If you can post a snipped of the code up somewhere like gist.github.com it would probably help.

    ReplyDelete
  127. Thanks for your effort

    Its really helped lot of us...

    ReplyDelete