Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

@roxlu

roxlu/Audio.cpp Secret

Created January 7, 2014 21:43
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save roxlu/1de2eababf595147afea to your computer and use it in GitHub Desktop.
Save roxlu/1de2eababf595147afea to your computer and use it in GitHub Desktop.
#include <tinylib.h>
#include <swnt/Audio.h>
Sound::Sound(Audio& audio, int name)
:audio(audio)
,name(name)
,buffer_size(0)
,channels(0)
,encoding(0)
,rate(0)
,must_stop(false)
{
uv_mutex_init(&mutex);
}
Sound::~Sound() {
uv_mutex_destroy(&mutex);
}
// ---------------------------------------------------------------------
void audio_start(uv_work_t* req) {
Sound* snd = static_cast<Sound*>(req->data);
if(!snd) {
printf("Error: cannot cast the sound object for the audio thread.\n");
return;
}
int driver = ao_default_driver_id();
ao_device* device = ao_open_live(driver, &snd->format, NULL);
if(!device) {
printf("Cannot open ao device.\n");
return;
}
size_t done = 0;
size_t to_play = 0;
bool must_stop = false;
while(!must_stop) {
if(done + snd->buffer_size > snd->buffer.size()) {
to_play = snd->buffer.size() - done;
}
else {
to_play = snd->buffer_size;
}
ao_play(device, (char*)(&snd->buffer[0]) + done, to_play);
done += to_play;
if(done >= snd->buffer.size()) {
break;
}
snd->lock();
must_stop = snd->must_stop;
snd->unlock();
}
}
void audio_ready(uv_work_t* req, int status) {
delete req;
}
// ---------------------------------------------------------------------
Audio::Audio()
:loop(NULL)
{
ao_initialize();
mpg123_init();
loop = uv_loop_new();
}
Audio::~Audio() {
shutdown();
for(std::vector<Sound*>::iterator it = sounds.begin(); it != sounds.end(); ++it) {
Sound* s = *it;
delete s;
}
sounds.clear();
if(loop) {
uv_loop_delete(loop);
}
mpg123_exit();
ao_shutdown();
}
void Audio::shutdown() {
for(std::vector<Sound*>::iterator it = sounds.begin(); it != sounds.end(); ++it) {
Sound* s = *it;
s->stop();
}
}
bool Audio::setup() {
return true;
}
bool Audio::add(int name, std::string filepath) {
Sound* snd = new Sound(*this, name);
if(!snd) {
printf("Cannot allocate Sound for file: %s\n", filepath.c_str());
return false;
}
if(!loadFile(filepath, *snd)) {
printf("Cannot load the audio file.\n");
delete snd;
snd = NULL;
return false;
}
sounds.push_back(snd);
return true;
}
void Audio::update() {
uv_run(loop, UV_RUN_NOWAIT);
}
bool Audio::stop(int name) {
Sound* s = find(name);
if(!s) {
printf("Cannot find audio: %d\n", name);
return false;
}
s->stop();
return true;
}
bool Audio::play(int name) {
Sound* s = find(name);
if(!s) {
printf("Cannot find Sound for name: %d\n", name);
return false;
}
return play(s);
}
bool Audio::play(Sound* snd) {
uv_work_t* w = new uv_work_t();
w->data = snd;
uv_queue_work(loop, w, audio_start, audio_ready);
return true;
}
bool Audio::loadFile(std::string filepath, Sound& snd) {
int err = 0;
mpg123_handle* mh = NULL;
unsigned char* tmp_buffer = NULL;
size_t done = 0;
mh = mpg123_new(NULL, &err);
if(!mh) {
printf("Error while creating a mpg123 object, err: %d\n", err);
return false;
}
snd.buffer_size = mpg123_outblock(mh);
if(!snd.buffer_size) {
printf("Error: cannot get a valid outblock.\n");
goto audio_error;
}
err = mpg123_open(mh, filepath.c_str());
if(err != MPG123_OK) {
printf("Error while trying to open the audio file: %s, %s\n", filepath.c_str(), mpg123_plain_strerror(err));
goto audio_error;
}
err = mpg123_getformat(mh, &snd.rate, &snd.channels, &snd.encoding);
if(err != MPG123_OK) {
printf("Error while trying to get the audio format for: %s, %s\n", filepath.c_str(), mpg123_plain_strerror(err));
goto audio_error;
}
snd.format.bits = mpg123_encsize(snd.encoding) * 8;
snd.format.rate = snd.rate;
snd.format.channels = snd.channels;
snd.format.byte_format = AO_FMT_NATIVE;
snd.format.matrix = 0;
tmp_buffer = new unsigned char[snd.buffer_size];
if(!tmp_buffer) {
printf("Error: cannot allocate a buffer for the audio, of size: %ld\n", snd.buffer_size);
goto audio_error;
}
while(mpg123_read(mh, tmp_buffer, snd.buffer_size, &done) == MPG123_OK) {
std::copy(tmp_buffer, tmp_buffer+done, std::back_inserter(snd.buffer));
}
mpg123_delete(mh);
mh = NULL;
delete[] tmp_buffer;
tmp_buffer = NULL;
return snd.buffer.size();
audio_error:
if(mh) {
mpg123_close(mh);
mpg123_delete(mh);
mh = NULL;
}
return false;
}
Sound* Audio::find(int name) {
Sound* s = NULL;
for(std::vector<Sound*>::iterator it = sounds.begin(); it != sounds.end(); ++it) {
Sound* snd = *it;
if(snd->name == name) {
s = snd;
break;
}
}
return s;
}
#ifndef SWNT_AUDIO_H
#define SWNT_AUDIO_H
extern "C" {
# include <mpg123.h>
# include <ao/ao.h>
# include <uv.h>
}
#include <vector>
void audio_start(uv_work_t* req);
void audio_ready(uv_work_t* req, int status);
class Audio;
class Sound {
public:
Sound(Audio& audio, int name);
~Sound();
void lock();
void unlock();
void play();
void stop();
public:
int name;
size_t buffer_size;
int channels;
int encoding;
long rate;
ao_sample_format format;
std::vector<char> buffer;
bool must_stop;
Audio& audio;
uv_mutex_t mutex;
};
class Audio {
public:
Audio();
~Audio();
bool setup();
bool add(int name, std::string filepath);
void update();
bool play(int name);
bool stop(int name);
void shutdown();
private:
Sound* find(int name);
bool play(Sound* snd);
bool loadFile(std::string filepath, Sound& sound);
public:
std::vector<Sound*> sounds;
uv_loop_t* loop;
};
inline void Sound::lock() {
uv_mutex_lock(&mutex);
}
inline void Sound::unlock() {
uv_mutex_unlock(&mutex);
}
inline void Sound::stop() {
lock();
must_stop = true;
unlock();
}
inline void Sound::play() {
lock();
must_stop = false;
unlock();
}
#endif
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment