Libmpg123 on Android

In libgdx i used libmad via a JNI bridge for decoding mp3 files. Libmad is GPL and if you want to use it in a commercial product you have to pay license fees. So today i removed the libmad dependancy and replaced it with libmpg123. From what i knew it is a float decoder so i was surprised to see that it was as fast as libmad on Android! For a 9 minute 196kbi/s mp3 file libmpg123 takes around 18 seconds on my Motorola Milestone. If you are interested in the native sources you can find them at http://code.google.com/p/libgdx/source/browse/#svn/trunk/gdx/jni/mpg123. I took them from the smuuz project and there’s nothing you need to do in order to compile it, no silly flags etc. Awesome! In libgdx there’s now a new Decoder called Mpg123Decoder which is not finished yet, but i have some tests that directly use the native methods and it works just fine.

15 thoughts on “Libmpg123 on Android

  1. The performance situation is not surprising; modern processors including all the current Cortex A8s smartphone chipsets (Snapdragon, OMAP3, Samsung Hummingbird) and many ARM11 chipsets (MSM7xxx, iPhone 2.5/3G chipset) have hardware floating point that is just as fast as the integer execution unit.

  2. Oh, that explains it of course. I was under the impression that none of the currently available devices have an FPU. The G1 and the Hero don’t have one from what i’ve gathered.

    Thanks for the info!

  3. I tried to play an mp3 file with Mpg123Decoder for some reason it takes 60-70% of the cpu on the beagleboard (omap cortex a8) with android 2.1 and on the nexus one with android 2.2 too.
    I took libgdx (0.6) as is and even compiled the sources as my own jni nothing helped.
    I tried to remove the playback and leave only the processing but that didn’t solve it either.

  4. That’s what you get for software decoding. I’m afraid there’s no solution to this. Also, the decoders are not meant to be used for playback (there’s system classes which do the job a lot better via hardware acceleration).

  5. So the prime use is for gaming? which is probably small sound effect buffers?
    Can you please explain, what you’ve said, that you decoded a 9 minute mp3 in 18 seconds on the Motorola Milestone, I think I’ve missed something.

    thanks!

  6. 1) The prime use is for decoding mp3s for doing audio analysis. You can use it to decode your mp3s for playback too of course but will hog your cpu as it’s not hardware accelerated (e.g. a dsp that does the actual encoding instead of the cpu).
    2) given a 196kbit/s mp3 with a length of 9 minutes it takes my motorola milestone about 18 seconds to decode it. The process uses 100% cpu of course and nothing else is running in the meantime in this same process (e.g. a rendering thread or what have you).

    For me the primary use was to decode a user selected mp3 from the sd card once, analyse it and then create a level from it. This process is done once per mp3, the result is stored in compressed form and loaded the next time the user selects the same mp3. Think Audiosurf.

    hth,
    Mario

  7. If I’m doing what you did, but instead of decoding a full mp3 file at once, I decode a small buffer at a time and play it, assuming playback doesn’t consume much (which isn’t I’ve checked) on average it should consume 18/(9*60)=0.03333 percent of the cpu which is great, I’ve confirmed the numbers using Ubuntu on the beagle board and playing a mp3 file with mpg123 it consumed 5% of the cpu! I’ve took the same code from ubuntu with the same compilation options to a jni module and again it took 60-70% so I’m thinking it’s something with releasing the cpu or buffering or jni, I’m investigating, if I’ll have results I’ll keep you posted…

    -md

  8. problem solved!

    the jni implementation of MPG123Decoder uses ShortBuffer to transfer the audio from the mp3 to the java (through readSamples) then to be able to play it I needed to copy the data into a standard short[] array because android’s AudioTrack uses short array as input, that copy caused the enormous cpu consumption. I’ve changed readSamples to get a short array with jni’s GetPrimitiveArrayCritical/ReleasePrimitiveArrayCritical functions and the cpu consumption dropped to what it should be (11-14% on the beagleboard down from 60-70%).
    Remember that the only problem was the copy from ShortBuffer to short[] so if one can find other ways, to get a reference to the short[] inside the ShortBuffer or to make AudioTrack to use ShortBuffer it’ll solve the problem, I tried to do that but failed.

    enjoy,
    -md

  9. Oi, that’s nasty of course. Nio performance is a bit ridiculous on Android at times. I wrote a few articles on the topic just the last days ago, maybe you can find more useful info there. Glad it worked out for you in the end.

  10. I had some difficulty compiling the lib123 sources under the r5b version of the Android NDK. I made the following modifications to Android.mk as well as moved the sources (with the exception of Android.mk under jni/mpg123)

    LOCAL_PATH := $(call my-dir)

    include $(CLEAR_VARS)

    LOCAL_MODULE := libmpg123
    LOCAL_ARM_MODE := arm
    LOCAL_CFLAGS := -O2 -Wall

    LOCAL_SRC_FILES = mpg123/equalizer.c \
    mpg123/index.c \
    mpg123/layer2.c \
    mpg123/synth.c \
    mpg123/dct64.c \
    mpg123/format.c \
    mpg123/layer3.c \
    mpg123/ntom.c \
    mpg123/parse.c \
    mpg123/readers.c \
    mpg123/frame.c \
    mpg123/layer1.c \
    mpg123/libmpg123.c \
    mpg123/optimize.c \
    mpg123/synth_arm.S \
    mpg123/tabinit.c \
    mpg123/id3.c

    include $(BUILD_SHARED_LIBRARY)

    Like this there should be no trouble compiling with ndk-build

  11. Hi. Thank you so much for all your tutorials -> they are invaluable and I can’t thank you enough!!! So, I got the mpg123 built and working using your code/makefiles… if I write a “super basic” android app (i.e. an activity with 1 button and, clicking the button opens up an mp3 and decodes it) that includes the mpg123 decoder, then, everything is all good; I get decoded pcm data that “just works”. However, when I then integrated it into my actual application where I have 3 other threads running, the decoded mp3 pcm data, when output, is all garbled and sounds like junk. If I disable all the additional threads (i.e. I don’t start() them), then, the decoded pcm data again sounds good. So, seems that, under load, the mpg123 decoder introduces lots of artifacts… is there any way, that you know of, to which I can decode the mp3 to pcm (i.e. a wav file) and have the pcm be sound equivalent (subjective, I’m sure but, you know what I mean) even under heavy load???

  12. hello, Andy.
    I think I have met the same problem as you.
    Have you found the solution?
    It is really hard to get any clue…

    I am doubt that the ndk interface source files may have some bugs

  13. Kinda running into the same performance issues. Data copying / changing is expensive as hell in Java. So now we got a perfectly fast mpg123 decoder, it produces a PCM buffer (of shorts), and passing that data to Java involves a lottle of copying, pinning, and managed-code-silly-ness. It might even be that in java the AudioTrack requries big endian format, while your decoder produces little endian. So we need to swap the bytes around which is _expensive_ in Java, whatever the JIT is doing. It could even be worse, that the native audio implementation on your device avtually requires little endian, so after AudioTrack is sending the data to the device, the low-level driver in the linux stack is swapping the bytes back again. Argh.

    For my project, I’m going with OpenSL to do the audio playback directly in the NDK, so no copying of data from and to the JNI. You could decrypt, decode and play the data directly in JNI / Native without having to pass it back to Java. This seems the most efficient.

  14. Hi Mario
    I followed your tutorial, Libmad on Android with the NDK, and was able to implement it fairly easily.
    But this one, Libmpg123 on Android, is driving me crazy.
    Tried to download the sources from the link you gave and from the smuuz project.
    Was not able to use the svn and had to do it manually (took me a couple of hours).
    When trying to put everything together I got an enormous amount of errors. Some very basic as redefinition of variables and some when invoking a function with the wrong parameters(I wonder how it worked).
    Is there a way in which you or someone else can help me by sending a zip of the sources of this project that just does what the libmad project did: open an mp3 file and play it.
    Thanks
    Rony

Leave a Reply

Your email address will not be published.