By way of clarification, MAD also has a low-level API which does not use callbacks. You can control the entire decoding process yourself more or less as follows:
/* load buffer with your MPEG audio data */ mad_stream_buffer(&stream, buffer, buflen); while (1) { mad_frame_decode(&frame, &stream); mad_synth_frame(&synth, &frame); /* output PCM samples in synth.pcm */ }
This is vastly simplified, but it shows the general idea. mad_frame_decode() decodes the next frame's header and subband samples. mad_synth_frame() takes those subband samples and synthesizes PCM samples.
It is also possible to call mad_header_decode() before mad_frame_decode(). This just gives you the frame's header info, in case that's all you want, or perhaps to help you decide whether you want to decode the rest of the frame.
struct mad_stream stream; struct mad_frame frame; struct mad_synth synth; mad_stream_init(&stream); mad_frame_init(&frame); mad_synth_init(&synth); /* ... */ mad_synth_finish(&synth); mad_frame_finish(&frame); mad_stream_finish(&stream);
You can work with just a struct mad_header instead of a struct mad_frame if you only want to decode frame headers.
The thing to remember when converting MAD's fixed-point integer samples to 16-bit PCM (or whatever) is that MAD encodes samples as numbers in the full-scale range [-1.0, +1.0) where the binary point is placed 28 (MAD_F_FRACBITS) bits to the left of the integer.
However, you need to be prepared to handle clipping as some numbers may be less than -1.0 (-MAD_F_ONE) or greater than or equal to +1.0 (MAD_F_ONE, aka 1 << MAD_F_FRACBITS).