Here's a quickie. How do you play audio in an Apache Cordova app and have the sound automatically loop? For example, maybe you want background music for a simple game. It's pretty simple. Begin by adding in the Media plugin.


cordova plugin add cordova-plugin-media

Then get your music. I chose an MP3 from the cool Arcade Ambiance Project. This is exactly what you think it is - background sounds from what you would hear in an arcade. If you don't know what an arcade is, ask the old coder next to you.

Once you have your MP3, then you have an interesting choice. If you are iOS only, then you can actually tell the Media plugin to loop. Technically you can't loop infinitely, but you could use a really large number. Here's an example from the docs:


var myMedia = new Media("http://audio.ibeat.org/content/p1rj1s/p1rj1s_-_rockGuitar.mp3");
myMedia.play({ numberOfLoops: 9999 });

I'm assuming you could probably use JavaScript's MAXINT here: Number.MAX_VALUE. I haven't tried it, but it should work. If not, then certainly 9999 is pretty reasonable. (Be sure to keep reading. I tested MAX_VALUE. It failed.)

A cross-platform solution though isn't that much more difficult. The Media object supports a status event. This is fired every time the current media object has a particular change. Values are:

  • Media.MEDIA_NONE = 0;
  • Media.MEDIA_STARTING = 1;
  • Media.MEDIA_RUNNING = 2;
  • Media.MEDIA_PAUSED = 3;
  • Media.MEDIA_STOPPED = 4;

So all we need to do is listen for the stopped event and then use seekTo to return to the beginning of the file. Here is a complete solution. Notice I'm using the Device plugin to handle how Android loads audio files a bit differently. See my older post for an example: Cordova Media API Example


document.addEventListener('deviceready', init, false);
var media;

function init() {
	media = new Media(getMediaURL('arcade1.mp3'), null, mediaError, mediaStatus);
	media.play();
}

function getMediaURL(s) {
    if(device.platform.toLowerCase() === "android") return "/android_asset/www/" + s;
    return s;
}

function mediaError(e) {
	console.log('mediaError', e);
}

function mediaStatus(status) {
	if(status === Media.MEDIA_STOPPED) {
		media.seekTo(0);
		media.play();
	}
	console.log('status', JSON.stringify(arguments));
}

Pretty simple, right? Unfortunately there is a slight gap when it resets that may be annoying. I guess it just depends on the track you are using. You could, in theory, use the loop feature in iOS and only use the seekTo option for Android.

So I decided to go ahead and try this. The first thing I discovered is that Number.MAX_VALUE really screwed things up. I didn't get an error, but my init function never ran. Weird - but switching to 9999 worked. Unfortunately, you still get a gap, but it is smaller at least. Anyway, here is the version that only does seekTo for Android:


document.addEventListener('deviceready', init, false);
var media;
var isAndroid = false;

function init() {
	console.log('init');
	if(device.platform.toLowerCase() === "android") isAndroid = true;

	media = new Media(getMediaURL('arcade1.mp3'), null, mediaError, mediaStatus);
	media.play({numberOfLoops:9999});
}

function getMediaURL(s) {
    if(isAndroid) return "/android_asset/www/" + s;
    return s;
}

function mediaError(e) {
	console.log('mediaError', e);
}

function mediaStatus(status) {
	if(isAndroid && status === Media.MEDIA_STOPPED) {
		media.seekTo(0);
		media.play();
	}
}

You can find the full source code for this demo here:

https://github.com/cfjedimaster/Cordova-Examples/tree/master/loopingaudio

I went ahead and recorded two examples. The sound quality is pretty bad - I was recording from my laptop output. So these samples may be useless. Please note though that the original MP3s sound great, and it sounds great in the Cordova app.