--- /dev/null
+#include <limits>
+#include <mad.h>
+#include <msp/strings/format.h>
+#include "mp3decoder.h"
+
+using namespace std;
+
+namespace {
+
+Msp::Int16 scale(mad_fixed_t sample)
+{
+ if(sample<=-MAD_F_ONE)
+ return numeric_limits<Msp::Int16>::min();
+ else if(sample>=MAD_F_ONE)
+ return numeric_limits<Msp::Int16>::max();
+ else
+ return (sample + (1<<(MAD_F_FRACBITS-16))) >> (MAD_F_FRACBITS-15);
+}
+
+} // namespace
+
+
+namespace Msp {
+namespace AL {
+
+mp3_error::mp3_error(const std::string &func, int err):
+ runtime_error(format("%s: %s", func, get_message(err)))
+{ }
+
+string mp3_error::get_message(int err)
+{
+ return format("%d", err);
+}
+
+
+struct Mp3Decoder::Private
+{
+ mad_stream stream;
+ mad_frame frame;
+ mad_synth synth;
+};
+
+
+Mp3Decoder::Mp3Decoder(IO::Seekable &io):
+ priv(new Private),
+ in(io),
+ in_buf_size(16384),
+ in_buffer(new char[in_buf_size]),
+ read_pos(0)
+{
+ mad_stream_init(&priv->stream);
+ mad_frame_init(&priv->frame);
+ mad_synth_init(&priv->synth);
+
+ try
+ {
+ decode_frame();
+ format = (priv->frame.header.mode==MAD_MODE_SINGLE_CHANNEL ? MONO16 : STEREO16);
+ freq = priv->frame.header.samplerate;
+ }
+ catch(...)
+ {
+ delete[] in_buffer;
+ throw;
+ }
+}
+
+Mp3Decoder::~Mp3Decoder()
+{
+ delete[] in_buffer;
+ mad_synth_finish(&priv->synth);
+ mad_frame_finish(&priv->frame);
+ mad_stream_finish(&priv->stream);
+}
+
+bool Mp3Decoder::detect(const string &sig)
+{
+ if(sig.size()>=2 && sig[0]=='\xFF' && (sig[1]&0xE0)==0xE0)
+ return true;
+
+ static const char id3_sig[] = { 'I', 'D', '3' };
+ if(sig.size()<sizeof(id3_sig))
+ return false;
+ return !sig.compare(0, sizeof(id3_sig), id3_sig);
+}
+
+void Mp3Decoder::rewind()
+{
+ in.seek(0, IO::S_BEG);
+}
+
+unsigned Mp3Decoder::read(char *buf, unsigned len)
+{
+ unsigned nchan = (format==STEREO16 ? 2 : 1);
+ mad_pcm &pcm = priv->synth.pcm;
+
+ unsigned pos = 0;
+ while(pos+2*nchan<=len)
+ {
+ if(read_pos>=pcm.length)
+ {
+ if(!decode_frame())
+ break;
+ }
+
+ for(unsigned i=0; i<nchan; ++i)
+ {
+ Int16 sample = scale(pcm.samples[i][read_pos]);
+ buf[pos++] = sample;
+ buf[pos++] = sample>>8;
+ }
+
+ ++read_pos;
+ }
+
+ return pos;
+}
+
+bool Mp3Decoder::fill_input()
+{
+ unsigned in_pos = 0;
+ if(priv->stream.next_frame && priv->stream.next_frame<priv->stream.bufend)
+ {
+ copy(priv->stream.next_frame, priv->stream.bufend, in_buffer);
+ in_pos = priv->stream.bufend-priv->stream.next_frame;
+ }
+
+ unsigned len = in.read(in_buffer+in_pos, in_buf_size-in_pos);
+ in_pos += len;
+
+ mad_stream_buffer(&priv->stream, reinterpret_cast<unsigned char *>(in_buffer), in_pos);
+ priv->stream.error = MAD_ERROR_NONE;
+
+ return len;
+}
+
+bool Mp3Decoder::decode_frame()
+{
+ while(1)
+ {
+ if(!priv->stream.buffer || priv->stream.error==MAD_ERROR_BUFLEN)
+ if(!fill_input())
+ return false;
+
+ if(!mad_frame_decode(&priv->frame, &priv->stream))
+ {
+ mad_synth_frame(&priv->synth, &priv->frame);
+ read_pos = 0;
+ return true;
+ }
+
+ if(priv->stream.error==MAD_ERROR_BUFLEN)
+ continue;
+ else if(!MAD_RECOVERABLE(priv->stream.error))
+ throw mp3_error("mad_frame_decode", priv->stream.error);
+ }
+}
+
+} // namespace AL
+} // namespace Msp
--- /dev/null
+#ifndef MSP_AL_MP3DECODER_H_
+#define MSP_AL_MP3DECODER_H_
+
+#include <stdexcept>
+#include "sounddecoder.h"
+
+namespace Msp {
+namespace AL {
+
+class mp3_error: public std::runtime_error
+{
+public:
+ mp3_error(const std::string &, int);
+ virtual ~mp3_error() throw() { }
+
+private:
+ static std::string get_message(int);
+};
+
+
+class Mp3Decoder: public SoundDecoder
+{
+private:
+ struct Private;
+
+ Private *priv;
+ IO::Seekable ∈
+ unsigned in_buf_size;
+ char *in_buffer;
+ unsigned read_pos;
+
+public:
+ Mp3Decoder(IO::Seekable &);
+ virtual ~Mp3Decoder();
+
+ static bool detect(const std::string &);
+
+ virtual void rewind();
+ virtual unsigned read(char *, unsigned);
+
+private:
+ bool fill_input();
+ bool decode_frame();
+};
+
+} // namespace AL
+} // namespace Msp
+
+#endif