2010-08-13 19:36:05 +00:00
|
|
|
#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,
|
2010-08-17 09:25:26 +00:00
|
|
|
including ALSA, OSS, PortAudio, PulseAudio and SDL. Using this
|
2010-08-13 19:36:05 +00:00
|
|
|
library as a pure output library (if that is possible) would be a
|
|
|
|
nice shortcut over using those libraries - OTOH it's another
|
2010-08-17 09:25:26 +00:00
|
|
|
dependency.
|
2010-08-13 19:36:05 +00:00
|
|
|
|
|
|
|
- we could implement seek(), tell() and size(), but they aren't
|
2010-08-17 09:25:26 +00:00
|
|
|
really necessary. Furthermore, since the returned size is only a
|
2010-08-13 19:36:05 +00:00
|
|
|
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;
|
|
|
|
|
2010-08-13 20:59:56 +00:00
|
|
|
// Open the file (hack around constness)
|
|
|
|
err = mpg123_open(mhh, (char*)file.c_str());
|
2010-08-13 19:36:05 +00:00
|
|
|
checkError(err, mh);
|
|
|
|
|
|
|
|
// Get the format
|
|
|
|
int encoding;
|
|
|
|
err = mpg123_getformat(mhh, &rate, &channels, &encoding);
|
|
|
|
checkError(err, mh);
|
|
|
|
if(encoding != MPG123_ENC_SIGNED_16)
|
2010-08-17 14:03:51 +00:00
|
|
|
fail("Unsupported encoding in " + file);
|
2010-08-13 19:36:05 +00:00
|
|
|
|
|
|
|
// 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);
|
|
|
|
}
|