mirror of
https://github.com/TES3MP/openmw-tes3mp.git
synced 2025-02-02 14:15:32 +00:00
Added mpg123 sound source
This commit is contained in:
parent
dc0c026ce0
commit
5b8e8d6b48
5 changed files with 249 additions and 1 deletions
24
sound/filters/openal_mpg123.hpp
Normal file
24
sound/filters/openal_mpg123.hpp
Normal file
|
@ -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
|
121
sound/sources/mpg123_source.cpp
Normal file
121
sound/sources/mpg123_source.cpp
Normal file
|
@ -0,0 +1,121 @@
|
||||||
|
#include "mpg123_source.hpp"
|
||||||
|
|
||||||
|
#include "../../tools/str_exception.hpp"
|
||||||
|
|
||||||
|
#include <mpg123.h>
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
47
sound/sources/mpg123_source.hpp
Normal file
47
sound/sources/mpg123_source.hpp
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
#ifndef MANGLE_SOUND_MPG123_SOURCE_H
|
||||||
|
#define MANGLE_SOUND_MPG123_SOURCE_H
|
||||||
|
|
||||||
|
#include "../source.hpp"
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
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<Mpg123Source,false,true>
|
||||||
|
{
|
||||||
|
/** 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
|
|
@ -1,6 +1,6 @@
|
||||||
GCC=g++ -I../ -Wall
|
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)
|
L_FFMPEG=$(shell pkg-config --libs libavcodec libavformat)
|
||||||
I_FFMPEG=-I/usr/include/libavcodec -I/usr/include/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
|
openal_ffmpeg_test: openal_ffmpeg_test.cpp ../sources/ffmpeg_source.cpp ../outputs/openal_out.cpp
|
||||||
$(GCC) $^ -o $@ $(L_FFMPEG) $(L_OPENAL) $(I_FFMPEG)
|
$(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
|
openal_output_test: openal_output_test.cpp ../outputs/openal_out.cpp
|
||||||
$(GCC) $^ -o $@ $(L_OPENAL)
|
$(GCC) $^ -o $@ $(L_OPENAL)
|
||||||
|
|
||||||
|
|
53
sound/tests/openal_mpg123_test.cpp
Normal file
53
sound/tests/openal_mpg123_test.cpp
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
#include <iostream>
|
||||||
|
#include <exception>
|
||||||
|
|
||||||
|
#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;
|
||||||
|
}
|
Loading…
Reference in a new issue