From 0e266d73f9aab89410c736e969eaa51ef914acf1 Mon Sep 17 00:00:00 2001 From: Mikko Rasa Date: Wed, 20 Jun 2018 13:33:20 +0300 Subject: [PATCH] Add support for MP3 files using libmad --- Build | 2 + source/mp3decoder.cpp | 160 ++++++++++++++++++++++++++++++++++++++++ source/mp3decoder.h | 49 ++++++++++++ source/sounddecoder.cpp | 3 + 4 files changed, 214 insertions(+) create mode 100644 source/mp3decoder.cpp create mode 100644 source/mp3decoder.h diff --git a/Build b/Build index 4517ad5..02ebe32 100644 --- a/Build +++ b/Build @@ -3,7 +3,9 @@ package "mspal" version "0.10"; description "C++ wrapper for OpenAL"; + // TODO make these features require "vorbisfile"; + require "mad"; require "openal"; require "sigc++-2.0"; require "mspcore"; diff --git a/source/mp3decoder.cpp b/source/mp3decoder.cpp new file mode 100644 index 0000000..a717cf4 --- /dev/null +++ b/source/mp3decoder.cpp @@ -0,0 +1,160 @@ +#include +#include +#include +#include "mp3decoder.h" + +using namespace std; + +namespace { + +Msp::Int16 scale(mad_fixed_t sample) +{ + if(sample<=-MAD_F_ONE) + return numeric_limits::min(); + else if(sample>=MAD_F_ONE) + return numeric_limits::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()synth.pcm; + + unsigned pos = 0; + while(pos+2*nchan<=len) + { + if(read_pos>=pcm.length) + { + if(!decode_frame()) + break; + } + + for(unsigned i=0; i>8; + } + + ++read_pos; + } + + return pos; +} + +bool Mp3Decoder::fill_input() +{ + unsigned in_pos = 0; + if(priv->stream.next_frame && priv->stream.next_framestream.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(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 diff --git a/source/mp3decoder.h b/source/mp3decoder.h new file mode 100644 index 0000000..06060de --- /dev/null +++ b/source/mp3decoder.h @@ -0,0 +1,49 @@ +#ifndef MSP_AL_MP3DECODER_H_ +#define MSP_AL_MP3DECODER_H_ + +#include +#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 diff --git a/source/sounddecoder.cpp b/source/sounddecoder.cpp index a2cd51a..38ae8fa 100644 --- a/source/sounddecoder.cpp +++ b/source/sounddecoder.cpp @@ -1,6 +1,7 @@ #include #include #include +#include "mp3decoder.h" #include "oggdecoder.h" #include "sounddecoder.h" @@ -42,6 +43,8 @@ SoundDecoder *SoundDecoder::open_io(IO::Seekable &io) string signature(sig_buf, sizeof(sig_buf)); if(OggDecoder::detect(signature)) return new OggDecoder(io); + else if(Mp3Decoder::detect(signature)) + return new Mp3Decoder(io); else { string sig_hex; -- 2.43.0