Honey, I shrunk the MP3 decoder
A few months ago, I implemented a minuscule MPEG Audio Layer II decoder. While I still consider this as a cool hack, it’s not of too much use nowadays. Everyone uses MP3 or Vorbis; MPEG Audio Layer II is only used together with MPEG Video (think VCD, SVCD and DVB), but you usually don’t have MP2 audio files on your disk. So the aim was clear: I wanted to have an as-small-as-possible MP3 decoder, too.
This time, I didn’t even try to implement it myself. Layer II was already hard to understand, but Layer III is about 3 to 5 times more complex. Considering the bad quality of the standard, I decided against a fresh reimplementation. Instead, I looked at the large pool of existing open-source decoding software.
The most common MP3 decoder in the F/OSS world is mpg123 and the mpglib library based off it, so this was naturally my first choice. Digging deeper into the source, I found that it is too complex, hard to read and even harder to port. It is chock full of #ifdefs, uses assembler code and floating-point math all over the place and is quite big overall. So I checked the next alternative: FFmpeg. The MPEG Audio decoder of libavcodec is already implemented in a single file, so I used it as a starting point. I had to dig a bit through all the FFmpeg intricacies (to find out that the bitstream reader is actually a bunch of intertwined and largely redundant macros) to get it together, but finally it worked.
The harder part was to make a really small Win32 executable. Creating a debug build in Visual C++ Express was quite easy, but the Release build without C runtime library dependencies took me quite some time to finish. The special problem were some weird floating-point operations like frexp() and pow() used during initialization. These operations don’t map directly to a x87 FPU instruction, so the compiler insisted on calling a library function. I needed to create a replacement of these two functions, but finding out how to implement them wasn’t trivial at all. Well, at least I learned a thing or two about floating-point assembler programming :)
All in all, the result is roughly 1680 lines of code (not counting empty lines, comments and 600 lines of tables) that compile to a 28k executable. The fabulous executable compressor kkrunchy manages to pack this down to a reasonable 13312 bytes. Not quite as small as the Layer II decoder, but OK. I guess that a carefully-designed full reimplementation would be possible in 8 or 10k, but I’ll leave it to someone else to do that.
The small size comes at a cost, though. The FFmpeg MP3 decoder is neither fast nor bug-free. It uses around 1-2% CPU on my Athlon64 3000+ (fully optimized, of course), while other decoders can’t be measured at all (i.e. below 1%). An even greater problem is a bug in the huffmann decoder that renders some (but not all) silent parts in some (but not all) files wrong. I first thought that has to be a problem of my port, but it’s there in vanilla FFmpeg, too, so I’m writing a bug report and leave it to Fabrice and the others to fix it :)
[Update 2007-02-05: Two days ago, Michael Niedermayer (a lead FFmpeg developer) fixed the bug. Now minimp3 decodes all files correctly o/]
If you don’t mind the shortcomings of the current implementations, feel invited to try it or use it in your projects. My personal incentive was to get rid of bass.dll or fmod.dll when writing demos – but if anyone feels like implementing a full-featured graphical MP3 player in, say, 64k, I’d love to see that, too :)
Download
- minimp3-src.tar.gz (36k) — the source code
and compiled example application
(note: the Win32 example application has been removed from the archive because it was frequently mis-detected as malware, causing Google Safe Browsing to flag the whole website as malicious)
Deprecation Notice
By now, there is another minimal MP3 decoder on GitHub:
https://github.com/lieff/minimp3
I really recommend checking this out before trying my decoder, because it’s obviously much faster and more correct, and it’s a clean-room implementation under a public-domain license, no rogue copy of FFmpeg code.
very cool, and drag and drop works on the exe :)
You would better use Vorbis. It has much better quality, than Mp3
simply awesome! although I would expect on windows it would be much smaller just to call the appropriate directshow functions to play the mp3. nevertheless, very kool! i’m glad you did this, as i didn’t want to use your mp2 for the very reasons you outlined here. great work!
aboeing: You’re right, just having DirectShow play a MP3 file is much simpler and smaller than this decoder. But it has some limitations, though:
1. Results vary depending on which MP3 decoder gets chosen. This would not be an issue if all decoders did their job well, but I remember that even the standard decoder that comes with Windows has some severe (audible!) bugs. I’m not going to say that minimp3 is bug-free (it’s certainly not), but it’s a fixed implementation you can test with and be sure that it sounds the same on any system.
2. As far as I know, DirectShow can’t play from memory chunks easily.
3. Finally, DirectShow isn’t exactly portable :)
Depending on what application you target at, these points might or might not apply for you. I developed minimp3 as part of a demo engine, and for this type of application, these three points matter :)
Hey KeyJ,
Yes, I’ve had trouble with getting DirectShow to play from and TO memory chunks and it certainly isn’t portable. I’ll see if I can get your MP3 player going on an embedded device we do some research on and I’ll send you a link if it works :)
(I wasn’t aware DirectShows MP3 decoders were buggy, I’ll have to look in to that!)
Well, every decoder is buggy at some point, I think :) But I remember hearing a suspiciously high amount of “blubs” in MP3s decoded with Windows Media Player when compared to WinAMP a while back. The problem might have been solved by now, but I learned my lesson already :)
Great work on this tiny MP3 player. How much work would be required to create a managed class wrapper, so this could be used within C#?
Mark: Not much, I think … on the C side, it would mean that the decoder needs to be compiled into a DLL, and on the C# side, it would require some standard P/Invoke stuff, I guess. Frankly, I didn’t dig very deep into .NET yet, so I can’t say much more about this.
Anyway, if you’re using C#, having a small native executable most likely isn’t one of your goals. So I rather recommend using some of the higher-level APIs like Windows Media / DirectShow or a ready-made third-party library like Bass.Net might be a better choice.
KeyJ: Thanks for your response. I’ve already looked at DirectShow, but it’s rather difficult to use in C#. Bass.Net looks like a great option however. Another alternative I’ve found is a managed Ogg library (http://www.j-ogg.de/CsOgg.zip) that may suit my needs.
Your entry points for the library seem to have a bug.
The mp3_decoder_t is typedef’d as:
typedef void* mp3_decoder_t;
And rightly so, the main create function does this:
mp3_decoder_t mp3_create(void)
{
// Something along the lines of:
return calloc(mp3_context_t);
}
Which is fine, since an mp3_context_t* is converted to a void*.
However, the _decode function does this:
int mp3_decode(mp3_decoder_t *dec, void *buf, int bytes, signed short *out, mp3_info_t *info)
Notice that the first parameter is an mp3_decoder_t*, which is effectively a void**. This parameter is then cast to an mp3_context_t* (which is wrong).
Furthermore, mp3_done has the same flaw.
This can be fixed by removing one pointer indirection to the mp3_done and mp3_decode function.
Needless to say, the reason why the Windows example compiles and runs is b/c in the calling code, the mp3_decoder_t is passed, as opposed to mp3_decoder_t* (which would essentially crash if it did).
Also, in the mp3_done function, there is no need to check for the pointer before free’ing. As per the C standard, free(NULL) is a no-op.
Dude, you rule ! I was wandering the net for a simple and LIGHT mp3 decoder : libmad, mpglib, oh god … then i found it !
Thx a bunch ! ( i added loop and stream from memory, now to implement simple fft )
i downloaded minimp3.
while testing i found a bug in that.
for some mp3 files left side audio sound is good, but right side it is giving some noise(hsss sound) along with original sound
Nice little mp3 player -works great with glibc and uclibc
(dietlibc works but it’s math library makes it sound “off”)
Thought it would be a nice thing to have in busybox so:
I hacked it all down to a single file,
and added loop code to play a list of files,
ported it to busybox (patch is in their mail list)
http://www.mail-archive.com/busybox@busybox.net/msg12341.html
rakesh: Could you please provide a minimal example file that exhibits the problem?
This is great – the only MP3 decoder I could find that doesn’t require tons of libraries to function.
Do you know how many bits the output PCM is? I can’t figure it out…
George: As the type (
signed short
) in the C declaration already hints at, it’s native-endian signed 16-bit integers. So far, so straightforward.Make sure to point to a buffer of at least
MP3_MAX_SAMPLES_PER_FRAME
samples (not bytes) in themp3_decode
call. The actual number of bytes (not samples) decoded there is then returned in themp3_info_t
structure. And now that I read the header file again, I must agree that this is highly unlogical :)Martin,
Thank you for your help – after far too many hours of my time I have managed to acheive real time MP3 decoding on an Altera FPGA development board, which makes use of your mini mp3 code. This is for my final year undergraduate University project (BEng Electronic Engineering). I have of course kept your code header in place and am acknowledging your code & assistance in my final report. Thanks again. George.
Hi, dude ! you’re awesome! I really admire your masterpiece!!!
I’m a beginner in mp3 decodec territory.It’s so luck that I can find your code in the Internet to read and study.I downloaded your work, this easy to understand the main() function. Meanwhile ,I add some code to explain the ID3v2 tag(or frame) in the mp3 file to display the mp3 information. However, the decode part, especially the initial part is sort of obscure. So there are some code and the detail that I can’t understand in your shrunk code, like decode_init() part, I read some paper about mp3 decode before read your code, But you know, principle is principle , There did are some differences between the principle and the implementation.
So, Would you mind releasing a comment-version for us, the beginners ,for learning(or explain some small but important functions what they works in the program).
Thank you every much.I am looking forward to your reply.
tyousan: I’m not an expert either: While I implemented an MPEG-Audio Layer II (MP2) decoder myself, MP3 is already too cryptic for me. As I mentioned in the article,
minimp3
is not an original work by me, but a stripped-down version of the MP3 decoder from FFmpeg’slibavcodec
.KeyJ:thanks all the same.
If I have some questions on program,I will contact you by E-mail
Danke!( This is the first German I learned :-) )
Thank you very much for this nice implementation!!
It saved me a lot of time trying to cross-platform-compile other big-library solutions!
And this library does exactly what it needs to do :D
(Maybe you should publish the sources to github)
Excellent, it’s written exactly how I like!
Thank you!
Hi,
thanks for the decoder. It plays really well. However it decodes some mp3 files into hissing noise. I suspect it may be corrupted mp3 file itself, however other players decode it correctly (or are able to detect the issue and ignore it). Would you mind testing the following sample ? http://jpst.it/wHsw
use base64 -d text > sample.zip
Thank you.
Nice work,That sounds pretty good. If it support to seek a time point to play mp3 will
be wonderful. Like this: minimp3.exe m.mp3 -s 500 The source code support it?
Alice: Seeking is generally possible, but it needs to be done in the application that uses the minimp3 library. The demo application does not support it; it just decodes the whole stream.
A very lengthy comment follows. Click here to unfold it.
I guess this is too long ago now, but my first go at this crashed in a memcpy. I had a few errors building into visual studio, so perhaps my fixes for these went awry. As someone else pointed out, there’s anomalies in the definitions too. I just wanted to add this note so other people don’t waste time on something that’s perhaps not robust enough for everyday use. In the end I’m going to just call out to an FFMPEG task to convert to WAV or use Directshow.
Still has the bug that Aly pointed out in 2009. Clearly this code is no longer being maintained, which is unfortunate.
The mp3_decoder_t is typedef’d as:
typedef void* mp3_decoder_t;
And rightly so, the main create function does this:
mp3_decoder_t mp3_create(void)
{
// Something along the lines of:
return calloc(mp3_context_t);
}
Which is fine, since an mp3_context_t* is converted to a void*.
However, the _decode function does this:
int mp3_decode(mp3_decoder_t *dec, void *buf, int bytes, signed short *out, mp3_info_t *info)
Notice that the first parameter is an mp3_decoder_t*, which is effectively a void**. This parameter is then cast to an mp3_context_t* (which is wrong).
Furthermore, mp3_done has the same flaw.
This can be fixed by removing one pointer indirection to the mp3_done and mp3_decode function.
Needless to say, the reason why the Windows example compiles and runs is b/c in the calling code, the mp3_decoder_t is passed, as opposed to mp3_decoder_t* (which would essentially crash if it did).
Also, in the mp3_done function, there is no need to check for the pointer before free’ing. As per the C standard, free(NULL) is a no-op.
Thanks, you save my time :D.
Just a little edit and it works perfect on Windows phone.
mente, in my case I have to support both Vorbis and MP3. I prefer Vorbis (always compressing OGG for listening music on phone or using in games, and FLAC/WAV to keep audio as-is) and other people too, but while I making my project of game engine to replace legacy (which widely used MP3s), I still keep compatibility with any “broken” MP3s to take my project be drop-in replace of legacy. I used libMAD, and it’s nice and powerful (has many asm-optimizations and in result I have ~1% CPU usage on Pentium 4 machine), however minimp3 is much smaller than libmad and easier to deal with most of C compilers, but I still need try it to embed into my SDL2 Mixer fork (which I made because troubles with resampling and necessary to add support more audio file formats and standalone MIDI player with OPL3 Synth) to check how it will work in practic.
Great!
Is there any documentation for the source code describing variable and functions and etc?
Saeid: Only the API of minimp3 is documented, the inner functions are not, as they are a (more or less) verbatim port of FFmpeg’s MP3 decoder.
Just a hat tip: minimp3 has been implemented as the built-in decoder in TenFourFox (a port of Firefox for vintage Power Macs) since version 31.2. It took a little work to glue it into Mozilla’s memory allocator and design a C++ interface to it, but it works. Thanks for a great little self-contained implementation.
I just discovered your library and i want to thank you for the splendid work.. My question Is, it possible for me to read the BPM of an mp3 using minimp3? Thank you..
Dev: Unfortunately not. MP3 just encodes audio as a »dumb« stream and doesn’t have any notion of musical structures like beats and bars. For beat detection, you need some additional signal processing that operates on the decoded data.
Thank you, your response was helpful
This mp3 decoder in 1 file is really great. It was imported to a VC project with a few modifications and it works well. Thanks!
The unpacked files of minimp3.tar.gz seemed to be infected
Haje: I’m certain they are not. It’s just that your antivirus is broken (as is pretty much anyone else’s).
There’s definitely something up with the download. Chrome and Firefox are blocking the site saying it was detected to be hosting malware, and then if you bypass all that and unpack the archive, Windows Defender flags minimp3.exe as a trojan and quarantines it.
Out of curiosity and against my better judgment, I ran the .exe from the command line anyway, and it locks up after saying “no input file specified”, which does seem suspicious.
Bruce: There’s definitely something … with the state of so-called »anti-virus« (AV) software. Long story short, it’s a false alarm: AV vendors think that the file is suspicious because it does uncommon things (like, you know, decoding MP3 in 13 kilobytes instead of taking 10 times that much). I can assure you that it’s not dangerous. The warning you get from Chrome and Firefox is because AV vendors detected that my site hosts many more of such »suspicous« executables and the browsers check all visited sites against a blacklist.
The »no input file specified« message is expected – you need to tell the program what to play by passing a file name as a parameter. You can do that e.g. by dragging an MP3 file onto the .exe.
I want to compile it with gcc
need help to convert this fpu register stack into gas at&t format (libc.h)
__asm {
fldl2e
fld qword ptr [esp+4]
fmul
fst st(1)
frndint
fxch
fsub st(0), st(1)
f2xm1
fld1
fadd
fscale
ret
}
thanks
BTW I got 16KB (kkrunched) without -DNEED_MINILIBC, I want it smaller like yours :)
Hi, thanks for a great project! I was able to build and run on Ubuntu 16.04 LTS. The object file size text is small, but RAM adds up. Taking together BSS with heap malloc in mp3_decode_init(), is there any way to reduce BSS and heap size?
Cheers,
Stefan
@KeyJ: The error message wasn’t what was suspicious, it was that the program locked up after displaying it. Looking at the source code I see no reason why it should have locked up.
https://www.virustotal.com/#/file/1d13dc4af76640349732fe8464e16ba2cc360b5be2ba7f8e701f34589b7c4be7/detection
It looks like AVs are flagging it because it was compressed with kkrunchy (an EXE packer). If I compile it myself there’s no issue.
There’s a new competitor on the block: https://github.com/lieff/minimp3 :)
Saga: Cool, thanks for the information! I’ll add a link to it in the post.
This might still be the best game in town for an integer based mp3 decoder. Hardware floating point is still a bit of a luxury on small embedded MCUs.
lieff/minimp3
is a float based decoder.