From 5b8e8d6b48847cd99d14064e8c7a1115efdd4fed Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Fri, 13 Aug 2010 21:36:05 +0200 Subject: [PATCH] Added mpg123 sound source --- sound/filters/openal_mpg123.hpp | 24 ++++++ sound/sources/mpg123_source.cpp | 121 +++++++++++++++++++++++++++++ sound/sources/mpg123_source.hpp | 47 +++++++++++ sound/tests/Makefile | 5 +- sound/tests/openal_mpg123_test.cpp | 53 +++++++++++++ 5 files changed, 249 insertions(+), 1 deletion(-) create mode 100644 sound/filters/openal_mpg123.hpp create mode 100644 sound/sources/mpg123_source.cpp create mode 100644 sound/sources/mpg123_source.hpp create mode 100644 sound/tests/openal_mpg123_test.cpp diff --git a/sound/filters/openal_mpg123.hpp b/sound/filters/openal_mpg123.hpp new file mode 100644 index 0000000000..bfd926c0bb --- /dev/null +++ b/sound/filters/openal_mpg123.hpp @@ -0,0 +1,24 @@ +#ifndef MANGLE_MPG123_OPENAL_H +#define MANGLE_MPG123_OPENAL_H + +#include "input_filter.hpp" +#include "../sources/mpg123_source.hpp" +#include "../outputs/openal_out.hpp" + +namespace Mangle { +namespace Sound { + +/// A InputFilter that adds mpg123 decoding to OpenAL. Only supports +/// MP3 files. +class OpenAL_Mpg123_Factory : public InputFilter +{ + public: + OpenAL_Mpg123_Factory() + { + set(SoundFactoryPtr(new OpenAL_Factory), + SampleSourceLoaderPtr(new Mpg123Loader)); + } +}; + +}} +#endif diff --git a/sound/sources/mpg123_source.cpp b/sound/sources/mpg123_source.cpp new file mode 100644 index 0000000000..3bf56a4684 --- /dev/null +++ b/sound/sources/mpg123_source.cpp @@ -0,0 +1,121 @@ +#include "mpg123_source.hpp" + +#include "../../tools/str_exception.hpp" + +#include + +using namespace Mangle::Stream; + +/* + TODOs: + + - mpg123 impressively enough supports custom stream reading. Which + means we could (and SHOULD!) support reading from Mangle::Streams + as well. But I'll save it til I need it. + + An alternative way to do this is through feeding (which they also + support), but that's more messy. + + - the library also supports output, via various other sources, + including ALSO, OSS, PortAudio, PulseAudio and SDL. Using this + library as a pure output library (if that is possible) would be a + nice shortcut over using those libraries - OTOH it's another + dependency. But it also means we could scavenge the mpg123 source + for these parts if we want them. + + - we could implement seek(), tell() and size(), but they aren't + really necessary. Further more, since the returned size is only a + guess, it is not safe to rely on it. + */ + +static void fail(const std::string &msg) +{ throw str_exception("Mangle::Mpg123 exception: " + msg); } + +static void checkError(int err, void *mh = NULL) +{ + if(err != MPG123_OK) + { + std::string msg; + if(mh) msg = mpg123_strerror((mpg123_handle*)mh); + else msg = mpg123_plain_strerror(err); + fail(msg); + } +} + +using namespace Mangle::Sound; + +void Mpg123Source::getInfo(int32_t *pRate, int32_t *pChannels, int32_t *pBits) +{ + // Use the values we found in the constructor + *pRate = rate; + *pChannels = channels; + *pBits = bits; +} + +size_t Mpg123Source::read(void *data, size_t length) +{ + size_t done; + // This is extraordinarily nice. I like this library. + int err = mpg123_read((mpg123_handle*)mh, (unsigned char*)data, length, &done); + assert(done <= length); + if(err == MPG123_DONE) + isEof = true; + else + checkError(err, mh); + return done; +} + +Mpg123Loader::Mpg123Loader(bool setup) +{ + // Do as we're told + if(setup) + { + int err = mpg123_init(); + checkError(err); + } + didSetup = setup; +} + +Mpg123Loader::~Mpg123Loader() +{ + // Deinitialize the library on exit + if(didSetup) + mpg123_exit(); +} + +Mpg123Source::Mpg123Source(const std::string &file) +{ + int err; + + // Create a new handle + mh = mpg123_new(NULL, &err); + if(mh == NULL) + checkError(err, mh); + + mpg123_handle *mhh = (mpg123_handle*)mh; + + // Open the file + err = mpg123_open(mhh, file.c_str()); + checkError(err, mh); + + // Get the format + int encoding; + err = mpg123_getformat(mhh, &rate, &channels, &encoding); + checkError(err, mh); + if(encoding != MPG123_ENC_SIGNED_16) + fail("Bad encoding"); + + // This is the only bit size we support. + bits = 16; + + // Ensure the output format does not change. (The tutorial on the + // mpg123 site did this, I assume it's kosher.) + mpg123_format_none(mhh); + mpg123_format(mhh,rate,channels,encoding); +} + +Mpg123Source::~Mpg123Source() +{ + mpg123_close((mpg123_handle*)mh); + mpg123_delete((mpg123_handle*)mh); +} diff --git a/sound/sources/mpg123_source.hpp b/sound/sources/mpg123_source.hpp new file mode 100644 index 0000000000..1ac16b5306 --- /dev/null +++ b/sound/sources/mpg123_source.hpp @@ -0,0 +1,47 @@ +#ifndef MANGLE_SOUND_MPG123_SOURCE_H +#define MANGLE_SOUND_MPG123_SOURCE_H + +#include "../source.hpp" +#include + +namespace Mangle { +namespace Sound { + +/// A sample source that decodes files using libmpg123. Only supports +/// MP3 files. +class Mpg123Source : public SampleSource +{ + void *mh; + long int rate; + int channels, bits; + + public: + /// Decode the given sound file + Mpg123Source(const std::string &file); + + /// Needed by SSL_Template but not yet supported + Mpg123Source(Mangle::Stream::StreamPtr data) + { assert(0); } + + ~Mpg123Source(); + + void getInfo(int32_t *rate, int32_t *channels, int32_t *bits); + size_t read(void *data, size_t length); +}; + +#include "loadertemplate.hpp" + +/// A factory that loads Mpg123Sources from file and stream +struct Mpg123Loader : SSL_Template +{ + /** Sets up libmpg123 for you, and closes it on destruction. If you + want to do this yourself, send setup=false. + */ + Mpg123Loader(bool setup=true); + ~Mpg123Loader(); +private: + bool didSetup; +}; + +}} // Namespace +#endif diff --git a/sound/tests/Makefile b/sound/tests/Makefile index 39b17be189..1c6c1fda5d 100644 --- a/sound/tests/Makefile +++ b/sound/tests/Makefile @@ -1,6 +1,6 @@ GCC=g++ -I../ -Wall -all: audiere_source_test ffmpeg_source_test openal_output_test openal_audiere_test openal_ffmpeg_test +all: audiere_source_test ffmpeg_source_test openal_output_test openal_audiere_test openal_ffmpeg_test openal_mpg123_test L_FFMPEG=$(shell pkg-config --libs libavcodec libavformat) I_FFMPEG=-I/usr/include/libavcodec -I/usr/include/libavformat @@ -13,6 +13,9 @@ openal_audiere_test: openal_audiere_test.cpp ../sources/audiere_source.cpp ../ou openal_ffmpeg_test: openal_ffmpeg_test.cpp ../sources/ffmpeg_source.cpp ../outputs/openal_out.cpp $(GCC) $^ -o $@ $(L_FFMPEG) $(L_OPENAL) $(I_FFMPEG) +openal_mpg123_test: openal_mpg123_test.cpp ../sources/mpg123_source.cpp ../outputs/openal_out.cpp + $(GCC) $^ -o $@ -lmpg123 ${L_OPENAL} + openal_output_test: openal_output_test.cpp ../outputs/openal_out.cpp $(GCC) $^ -o $@ $(L_OPENAL) diff --git a/sound/tests/openal_mpg123_test.cpp b/sound/tests/openal_mpg123_test.cpp new file mode 100644 index 0000000000..a2826b7551 --- /dev/null +++ b/sound/tests/openal_mpg123_test.cpp @@ -0,0 +1,53 @@ +#include +#include + +#include "../../stream/servers/file_stream.hpp" +#include "../filters/openal_mpg123.hpp" + +using namespace std; +using namespace Mangle::Stream; +using namespace Mangle::Sound; + +OpenAL_Mpg123_Factory mg; + +void play(const char* name, bool stream=false) +{ + // Only load streams if the backend supports it + if(stream && !mg.canLoadStream) + return; + + cout << "Playing " << name; + if(stream) cout << " (from stream)"; + cout << "\n"; + + SoundPtr snd; + + try + { + if(stream) + snd = mg.load(StreamPtr(new FileStream(name))); + else + snd = mg.load(name); + + snd->play(); + + while(snd->isPlaying()) + { + usleep(10000); + if(mg.needsUpdate) mg.update(); + } + } + catch(exception &e) + { + cout << " ERROR: " << e.what() << "\n"; + } +} + +int main(int argc, char**argv) +{ + if(argc != 2) + cout << "Please specify an MP3 file\n"; + else + play(argv[1]); + return 0; +}