From 750b2ad6a87e4081e68c5e88924c121b9ab6a078 Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Wed, 17 Apr 2013 01:04:27 +0200 Subject: output/openal: convert to C++ --- Makefile.am | 2 +- src/OutputList.cxx | 2 +- src/output/OpenALOutputPlugin.cxx | 294 ++++++++++++++++++++++++++++++++++++++ src/output/OpenALOutputPlugin.hxx | 25 ++++ src/output/openal_output_plugin.c | 279 ------------------------------------ src/output/openal_output_plugin.h | 25 ---- 6 files changed, 321 insertions(+), 306 deletions(-) create mode 100644 src/output/OpenALOutputPlugin.cxx create mode 100644 src/output/OpenALOutputPlugin.hxx delete mode 100644 src/output/openal_output_plugin.c delete mode 100644 src/output/openal_output_plugin.h diff --git a/Makefile.am b/Makefile.am index 57f668578..a654939c3 100644 --- a/Makefile.am +++ b/Makefile.am @@ -844,7 +844,7 @@ endif if HAVE_OPENAL liboutput_plugins_a_SOURCES += \ - src/output/openal_output_plugin.c src/output/openal_output_plugin.h + src/output/OpenALOutputPlugin.cxx src/output/OpenALOutputPlugin.hxx endif if HAVE_OSX diff --git a/src/OutputList.cxx b/src/OutputList.cxx index c754d5ff2..f6fd215ab 100644 --- a/src/OutputList.cxx +++ b/src/OutputList.cxx @@ -26,7 +26,7 @@ #include "output/HttpdOutputPlugin.hxx" #include "output/JackOutputPlugin.hxx" #include "output/NullOutputPlugin.hxx" -#include "output/openal_output_plugin.h" +#include "output/OpenALOutputPlugin.hxx" #include "output/OssOutputPlugin.hxx" #include "output/OSXOutputPlugin.hxx" #include "output/PipeOutputPlugin.hxx" diff --git a/src/output/OpenALOutputPlugin.cxx b/src/output/OpenALOutputPlugin.cxx new file mode 100644 index 000000000..297291d4e --- /dev/null +++ b/src/output/OpenALOutputPlugin.cxx @@ -0,0 +1,294 @@ +/* + * Copyright (C) 2003-2013 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "config.h" +#include "OpenALOutputPlugin.hxx" +#include "output_api.h" + +#include + +#ifndef HAVE_OSX +#include +#include +#else +#include +#include +#endif + +#undef G_LOG_DOMAIN +#define G_LOG_DOMAIN "openal" + +/* should be enough for buffer size = 2048 */ +#define NUM_BUFFERS 16 + +struct OpenALOutput { + struct audio_output base; + + const char *device_name; + ALCdevice *device; + ALCcontext *context; + ALuint buffers[NUM_BUFFERS]; + unsigned filled; + ALuint source; + ALenum format; + ALuint frequency; + + bool Initialize(const config_param *param, GError **error_r) { + return ao_base_init(&base, &openal_output_plugin, param, + error_r); + } + + void Deinitialize() { + ao_base_finish(&base); + } +}; + +static inline GQuark +openal_output_quark(void) +{ + return g_quark_from_static_string("openal_output"); +} + +static ALenum +openal_audio_format(struct audio_format *audio_format) +{ + /* note: cannot map SAMPLE_FORMAT_S8 to AL_FORMAT_STEREO8 or + AL_FORMAT_MONO8 since OpenAL expects unsigned 8 bit + samples, while MPD uses signed samples */ + + switch (audio_format->format) { + case SAMPLE_FORMAT_S16: + if (audio_format->channels == 2) + return AL_FORMAT_STEREO16; + if (audio_format->channels == 1) + return AL_FORMAT_MONO16; + + /* fall back to mono */ + audio_format->channels = 1; + return openal_audio_format(audio_format); + + default: + /* fall back to 16 bit */ + audio_format->format = SAMPLE_FORMAT_S16; + return openal_audio_format(audio_format); + } +} + +G_GNUC_PURE +static inline ALint +openal_get_source_i(const OpenALOutput *od, ALenum param) +{ + ALint value; + alGetSourcei(od->source, param, &value); + return value; +} + +G_GNUC_PURE +static inline bool +openal_has_processed(const OpenALOutput *od) +{ + return openal_get_source_i(od, AL_BUFFERS_PROCESSED) > 0; +} + +G_GNUC_PURE +static inline ALint +openal_is_playing(const OpenALOutput *od) +{ + return openal_get_source_i(od, AL_SOURCE_STATE) == AL_PLAYING; +} + +static bool +openal_setup_context(OpenALOutput *od, + GError **error) +{ + od->device = alcOpenDevice(od->device_name); + + if (od->device == nullptr) { + g_set_error(error, openal_output_quark(), 0, + "Error opening OpenAL device \"%s\"\n", + od->device_name); + return false; + } + + od->context = alcCreateContext(od->device, nullptr); + + if (od->context == nullptr) { + g_set_error(error, openal_output_quark(), 0, + "Error creating context for \"%s\"\n", + od->device_name); + alcCloseDevice(od->device); + return false; + } + + return true; +} + +static struct audio_output * +openal_init(const config_param *param, GError **error_r) +{ + const char *device_name = config_get_block_string(param, "device", nullptr); + + if (device_name == nullptr) { + device_name = alcGetString(nullptr, ALC_DEFAULT_DEVICE_SPECIFIER); + } + + OpenALOutput *od = new OpenALOutput(); + if (!od->Initialize(param, error_r)) { + delete od; + return nullptr; + } + + od->device_name = device_name; + + return &od->base; +} + +static void +openal_finish(struct audio_output *ao) +{ + OpenALOutput *od = (OpenALOutput *)ao; + + od->Deinitialize(); + delete od; +} + +static bool +openal_open(struct audio_output *ao, struct audio_format *audio_format, + GError **error) +{ + OpenALOutput *od = (OpenALOutput *)ao; + + od->format = openal_audio_format(audio_format); + + if (!openal_setup_context(od, error)) { + return false; + } + + alcMakeContextCurrent(od->context); + alGenBuffers(NUM_BUFFERS, od->buffers); + + if (alGetError() != AL_NO_ERROR) { + g_set_error(error, openal_output_quark(), 0, + "Failed to generate buffers"); + return false; + } + + alGenSources(1, &od->source); + + if (alGetError() != AL_NO_ERROR) { + g_set_error(error, openal_output_quark(), 0, + "Failed to generate source"); + alDeleteBuffers(NUM_BUFFERS, od->buffers); + return false; + } + + od->filled = 0; + od->frequency = audio_format->sample_rate; + + return true; +} + +static void +openal_close(struct audio_output *ao) +{ + OpenALOutput *od = (OpenALOutput *)ao; + + alcMakeContextCurrent(od->context); + alDeleteSources(1, &od->source); + alDeleteBuffers(NUM_BUFFERS, od->buffers); + alcDestroyContext(od->context); + alcCloseDevice(od->device); +} + +static unsigned +openal_delay(struct audio_output *ao) +{ + OpenALOutput *od = (OpenALOutput *)ao; + + return od->filled < NUM_BUFFERS || openal_has_processed(od) + ? 0 + /* we don't know exactly how long we must wait for the + next buffer to finish, so this is a random + guess: */ + : 50; +} + +static size_t +openal_play(struct audio_output *ao, const void *chunk, size_t size, + G_GNUC_UNUSED GError **error) +{ + OpenALOutput *od = (OpenALOutput *)ao; + ALuint buffer; + + if (alcGetCurrentContext() != od->context) { + alcMakeContextCurrent(od->context); + } + + if (od->filled < NUM_BUFFERS) { + /* fill all buffers */ + buffer = od->buffers[od->filled]; + od->filled++; + } else { + /* wait for processed buffer */ + while (!openal_has_processed(od)) + g_usleep(10); + + alSourceUnqueueBuffers(od->source, 1, &buffer); + } + + alBufferData(buffer, od->format, chunk, size, od->frequency); + alSourceQueueBuffers(od->source, 1, &buffer); + + if (!openal_is_playing(od)) + alSourcePlay(od->source); + + return size; +} + +static void +openal_cancel(struct audio_output *ao) +{ + OpenALOutput *od = (OpenALOutput *)ao; + + od->filled = 0; + alcMakeContextCurrent(od->context); + alSourceStop(od->source); + + /* force-unqueue all buffers */ + alSourcei(od->source, AL_BUFFER, 0); + od->filled = 0; +} + +const struct audio_output_plugin openal_output_plugin = { + "openal", + nullptr, + openal_init, + openal_finish, + nullptr, + nullptr, + openal_open, + openal_close, + openal_delay, + nullptr, + openal_play, + nullptr, + openal_cancel, + nullptr, + nullptr, +}; diff --git a/src/output/OpenALOutputPlugin.hxx b/src/output/OpenALOutputPlugin.hxx new file mode 100644 index 000000000..e1ebf3d4f --- /dev/null +++ b/src/output/OpenALOutputPlugin.hxx @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2003-2013 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef MPD_OPENAL_OUTPUT_PLUGIN_HXX +#define MPD_OPENAL_OUTPUT_PLUGIN_HXX + +extern const struct audio_output_plugin openal_output_plugin; + +#endif diff --git a/src/output/openal_output_plugin.c b/src/output/openal_output_plugin.c deleted file mode 100644 index ebd35ef12..000000000 --- a/src/output/openal_output_plugin.c +++ /dev/null @@ -1,279 +0,0 @@ -/* - * Copyright (C) 2003-2011 The Music Player Daemon Project - * http://www.musicpd.org - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include "config.h" -#include "openal_output_plugin.h" -#include "output_api.h" - -#include - -#ifndef HAVE_OSX -#include -#include -#else -#include -#include -#endif - -#undef G_LOG_DOMAIN -#define G_LOG_DOMAIN "openal" - -/* should be enough for buffer size = 2048 */ -#define NUM_BUFFERS 16 - -struct openal_data { - struct audio_output base; - - const char *device_name; - ALCdevice *device; - ALCcontext *context; - ALuint buffers[NUM_BUFFERS]; - unsigned filled; - ALuint source; - ALenum format; - ALuint frequency; -}; - -static inline GQuark -openal_output_quark(void) -{ - return g_quark_from_static_string("openal_output"); -} - -static ALenum -openal_audio_format(struct audio_format *audio_format) -{ - /* note: cannot map SAMPLE_FORMAT_S8 to AL_FORMAT_STEREO8 or - AL_FORMAT_MONO8 since OpenAL expects unsigned 8 bit - samples, while MPD uses signed samples */ - - switch (audio_format->format) { - case SAMPLE_FORMAT_S16: - if (audio_format->channels == 2) - return AL_FORMAT_STEREO16; - if (audio_format->channels == 1) - return AL_FORMAT_MONO16; - - /* fall back to mono */ - audio_format->channels = 1; - return openal_audio_format(audio_format); - - default: - /* fall back to 16 bit */ - audio_format->format = SAMPLE_FORMAT_S16; - return openal_audio_format(audio_format); - } -} - -G_GNUC_PURE -static inline ALint -openal_get_source_i(const struct openal_data *od, ALenum param) -{ - ALint value; - alGetSourcei(od->source, param, &value); - return value; -} - -G_GNUC_PURE -static inline bool -openal_has_processed(const struct openal_data *od) -{ - return openal_get_source_i(od, AL_BUFFERS_PROCESSED) > 0; -} - -G_GNUC_PURE -static inline ALint -openal_is_playing(const struct openal_data *od) -{ - return openal_get_source_i(od, AL_SOURCE_STATE) == AL_PLAYING; -} - -static bool -openal_setup_context(struct openal_data *od, - GError **error) -{ - od->device = alcOpenDevice(od->device_name); - - if (od->device == NULL) { - g_set_error(error, openal_output_quark(), 0, - "Error opening OpenAL device \"%s\"\n", - od->device_name); - return false; - } - - od->context = alcCreateContext(od->device, NULL); - - if (od->context == NULL) { - g_set_error(error, openal_output_quark(), 0, - "Error creating context for \"%s\"\n", - od->device_name); - alcCloseDevice(od->device); - return false; - } - - return true; -} - -static struct audio_output * -openal_init(const struct config_param *param, GError **error_r) -{ - const char *device_name = config_get_block_string(param, "device", NULL); - struct openal_data *od; - - if (device_name == NULL) { - device_name = alcGetString(NULL, ALC_DEFAULT_DEVICE_SPECIFIER); - } - - od = g_new(struct openal_data, 1); - if (!ao_base_init(&od->base, &openal_output_plugin, param, error_r)) { - g_free(od); - return NULL; - } - - od->device_name = device_name; - - return &od->base; -} - -static void -openal_finish(struct audio_output *ao) -{ - struct openal_data *od = (struct openal_data *)ao; - - ao_base_finish(&od->base); - g_free(od); -} - -static bool -openal_open(struct audio_output *ao, struct audio_format *audio_format, - GError **error) -{ - struct openal_data *od = (struct openal_data *)ao; - - od->format = openal_audio_format(audio_format); - - if (!openal_setup_context(od, error)) { - return false; - } - - alcMakeContextCurrent(od->context); - alGenBuffers(NUM_BUFFERS, od->buffers); - - if (alGetError() != AL_NO_ERROR) { - g_set_error(error, openal_output_quark(), 0, - "Failed to generate buffers"); - return false; - } - - alGenSources(1, &od->source); - - if (alGetError() != AL_NO_ERROR) { - g_set_error(error, openal_output_quark(), 0, - "Failed to generate source"); - alDeleteBuffers(NUM_BUFFERS, od->buffers); - return false; - } - - od->filled = 0; - od->frequency = audio_format->sample_rate; - - return true; -} - -static void -openal_close(struct audio_output *ao) -{ - struct openal_data *od = (struct openal_data *)ao; - - alcMakeContextCurrent(od->context); - alDeleteSources(1, &od->source); - alDeleteBuffers(NUM_BUFFERS, od->buffers); - alcDestroyContext(od->context); - alcCloseDevice(od->device); -} - -static unsigned -openal_delay(struct audio_output *ao) -{ - struct openal_data *od = (struct openal_data *)ao; - - return od->filled < NUM_BUFFERS || openal_has_processed(od) - ? 0 - /* we don't know exactly how long we must wait for the - next buffer to finish, so this is a random - guess: */ - : 50; -} - -static size_t -openal_play(struct audio_output *ao, const void *chunk, size_t size, - G_GNUC_UNUSED GError **error) -{ - struct openal_data *od = (struct openal_data *)ao; - ALuint buffer; - - if (alcGetCurrentContext() != od->context) { - alcMakeContextCurrent(od->context); - } - - if (od->filled < NUM_BUFFERS) { - /* fill all buffers */ - buffer = od->buffers[od->filled]; - od->filled++; - } else { - /* wait for processed buffer */ - while (!openal_has_processed(od)) - g_usleep(10); - - alSourceUnqueueBuffers(od->source, 1, &buffer); - } - - alBufferData(buffer, od->format, chunk, size, od->frequency); - alSourceQueueBuffers(od->source, 1, &buffer); - - if (!openal_is_playing(od)) - alSourcePlay(od->source); - - return size; -} - -static void -openal_cancel(struct audio_output *ao) -{ - struct openal_data *od = (struct openal_data *)ao; - - od->filled = 0; - alcMakeContextCurrent(od->context); - alSourceStop(od->source); - - /* force-unqueue all buffers */ - alSourcei(od->source, AL_BUFFER, 0); - od->filled = 0; -} - -const struct audio_output_plugin openal_output_plugin = { - .name = "openal", - .init = openal_init, - .finish = openal_finish, - .open = openal_open, - .close = openal_close, - .delay = openal_delay, - .play = openal_play, - .cancel = openal_cancel, -}; diff --git a/src/output/openal_output_plugin.h b/src/output/openal_output_plugin.h deleted file mode 100644 index 25f6ccf46..000000000 --- a/src/output/openal_output_plugin.h +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (C) 2003-2011 The Music Player Daemon Project - * http://www.musicpd.org - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#ifndef MPD_OPENAL_OUTPUT_PLUGIN_H -#define MPD_OPENAL_OUTPUT_PLUGIN_H - -extern const struct audio_output_plugin openal_output_plugin; - -#endif -- cgit v1.2.3