2009-05-07

Playing mp3s from Java

JNI

It's pretty obvious that mp3d needs to be able to actually play mp3s. This was another reason why I initially chose C++, my assumption being that there would be plenty of C++ mp3-playing libraries to choose from.

In a way, there were. But, perhaps because audio code isn't very portable and perhaps because Linux's audio API is abandoned for something new every six months or so, the libraries I came across were mainly focused on decoding mp3 data in some raw form. Presumably if I'd looked hard enough I'd have found the other pieces of the puzzle and been able to put them together, but after a brief search I gave up and started looking at command-line tools instead.

Spawning subprocesses

This is how the first version worked. If all you want to do is play an mp3 from start to finish and have no real control over – or feedback about – what's going on, this is a really easy option. But I was surprised by the strange programmatic interfaces offered. mplayer(1) seemed the best overall, and does indeed have a mode where it expects to communicate via a pipe, but the command set available seemed to assume a greater autonomy for mplayer than I was looking for. I wanted to keep the "playlist" in my server, for example, and basically use mplayer as my low-level API. It wasn't clear that I could really do that, and the various bits of calling code I could find on the web didn't suggest otherwise.

When I used mplayer from Java, I actually used Unix signals to control it, but I soon decided I wanted more control, and didn't particularly want to be tied to running on Linux (even though personally I'm unlikely to ever run mp3d anywhere else).

javax.sound

You might think that Java would have built-in support for the most popular audio format, but it doesn't. Presumably licensing problems. I hope this will be fixed by OpenJDK, at least for Linux, where it seems possible that whatever Free library they use for audio will have out of the box support for mp3 (and ogg and all kinds of other thus-far neglected formats).

Sun's bug database considers JMF (remember that?) to be the answer.

JavaZOOM JLayer

Despite the awful name (though I of all people should be careful about throwing stones where naming is concerned) JavaZOOM's JLayer is "the" Java library for decoding/playing mp3s. There may be others, but this is the one that everyone seems to use, so it was the one I went for too.

The API is a bit odd. Some of the names suggest no native speakers of English were involved. You stop a Player by calling "close", for example.

The API is also rather limited. If seems like more of a demo for the underlying mp3 decoder than an real API anyone's actually expected to use. If all you want to do is play an mp3 from start to finish, without pause or fast-forward (though you can stop prematurely), you're fine. Anything else and you'll be using the low-level API to shuffle frames from the decoder to the audio device.

Even the trivial case isn't well documented, so here's the minimal sample:

  String filename = "muzak.mp3";
  AudioDevice audioDevice = FactoryRegistry.systemRegistry().createAudioDevice();
  Player player = new Player(new FileInputStream(filename), audioDevice);
  player.play();

The call to "play" will block until the mp3's finished playing, but you can call "close" in another thread if you want.

(I'd have sent in patches, but the project doesn't have any visible source repository and appears pretty dormant.)

This is actually pretty much the code I'm using in mp3 at the moment. I keep expecting to want more, but I'm not yet convinced. I will admit to having used SIGSTOP and SIGCONT from a shell on a couple of occasions, but "pause" doesn't make as much sense as you might think on the web. Maybe if I bound my "media keys" on all my machines to POST appropriate form data I'd be more convinced. That would actually be pretty cool.

Without moving to something distinctly more AJAXy, though, I'm not sure "pause" fits the UI. And you probably would have to present it for the benefit of other users.