Hi.

On Sun, Aug 30, 2009 at 06:56PM +0200, Max Kellermann wrote:
> Hi Serge,
> 
> thanks for your contribution.  I have two questions about your patch:
> 
> On 2009/08/30 16:51, Serge Ziryukin <ftrvxm...@gmail.com> wrote:
> > OpenAL output plugin
> > 
> > +static void *
> > +openal_init(G_GNUC_UNUSED const struct audio_format *audio_format,
> > +       const struct config_param *param,
> > +       GError **error)
> > +{
> > +   const char *device_name = config_get_block_string(param, "device", 
> > NULL);
> > +   ALCdevice *device = alcOpenDevice(device_name);
> 
> Why does it open the device in the init() method?  This way, MPD
> cannot start while another program locks the sound card.  Do that in
> open().
> 

OK, I moved that. Patch attached.

> > +static size_t
> > +openal_play(void *data, const void *chunk, size_t size,
> > +       G_GNUC_UNUSED GError **error)
> > +{
> > +   struct openal_data *od = data;
> > +   ALuint buffer;
> > +   ALint num;
> > +
> > +   if (alcGetCurrentContext() != od->context) {
> > +           alcMakeContextCurrent(od->context);
> > +   }
> > +
> > +   if (od->filled < NUM_BUFFERS) {
> > +           /* fill all buffers */
> > +           buffer = od->buffers[od->filled];
> > +           od->filled++;
> > +   } else {
> > +           alGetSourcei(od->source, AL_BUFFERS_PROCESSED, &num);
> > +
> > +           /* wait for processed buffer */
> > +           while (num < 1) {
> > +                   if (!od->timer->started) {
> > +                           timer_start(od->timer);
> > +                   } else {
> > +                           timer_sync(od->timer);
> > +                   }
> > +
> > +                   timer_add(od->timer, size);
> 
> I don't know how openal works - why are you using the timer object
> here?  Other plugins rely on the timing of the library (e.g. ALSA's
> snd_pcm_writei() blocks until there is enough room in the buffer).
> 

As I understand, OpenAL uses buffer objects, which one should feed with data 
and queue
them to audio source object. The problem is, OpenAL does not block these 
alBufferData calls,
so I must wait for buffer to be processed, then deattach it, feed with some 
data and reattach again.
>From dc60a65cfdeed591c3b449c7769d1596a69ee489 Mon Sep 17 00:00:00 2001
From: Serge Ziryukin <ftrvxm...@gmail.com>
Date: Mon, 31 Aug 2009 10:26:22 +0300
Subject: [PATCH] openal output plugin

---
 Makefile.am                |    4 +
 configure.ac               |   25 ++++
 src/output/openal_plugin.c |  267 ++++++++++++++++++++++++++++++++++++++++++++
 src/output_list.c          |    4 +
 4 files changed, 300 insertions(+), 0 deletions(-)
 create mode 100644 src/output/openal_plugin.c

diff --git a/Makefile.am b/Makefile.am
index b017d97..5a6a8da 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -590,6 +590,10 @@ OUTPUT_SRC += src/output/oss_plugin.c
 MIXER_SRC += src/mixer/oss_mixer.c
 endif
 
+if HAVE_OPENAL
+OUTPUT_SRC += src/output/openal_plugin.c
+endif
+
 if HAVE_OSX
 OUTPUT_SRC += src/output/osx_plugin.c
 endif
diff --git a/configure.ac b/configure.ac
index e730c69..b74fbf9 100644
--- a/configure.ac
+++ b/configure.ac
@@ -711,6 +711,11 @@ AC_ARG_ENABLE(oss,
 		[disable OSS support (default: enable)]),,
 	enable_oss=yes)
 
+AC_ARG_ENABLE(openal,
+	AS_HELP_STRING([--enable-openal],
+		[enable OpenAL support (default: disable)]),,
+	enable_openal=no)
+
 AC_ARG_ENABLE(pulse,
 	AS_HELP_STRING([--enable-pulse],
 		[enable support for the PulseAudio sound server]),,
@@ -779,6 +784,19 @@ fi
 
 AM_CONDITIONAL(HAVE_OSS, test x$enable_oss = xyes)
 
+if test x$enable_openal = xyes; then
+	PKG_CHECK_MODULES([OPENAL], [openal],
+		AC_DEFINE(HAVE_OPENAL, 1, [Define for OpenAL support]),
+		enable_openal=no)
+fi
+
+if test x$enable_openal = xyes; then
+	MPD_CFLAGS="$MPD_CFLAGS $OPENAL_CFLAGS"
+	MPD_LIBS="$MPD_LIBS $OPENAL_LIBS"
+fi
+
+AM_CONDITIONAL(HAVE_OPENAL, test x$enable_openal = xyes)
+
 if test x$enable_fifo = xyes; then
 	AC_CHECK_FUNC([mkfifo],
 		[enable_fifo=yes;AC_DEFINE([HAVE_FIFO], 1,
@@ -1297,6 +1315,12 @@ else
 	echo " OSS support ...................disabled"
 fi
 
+if test x$enable_openal = xyes; then
+	echo " OpenAL support ................enabled"
+else
+	echo " OpenAL support ................disabled"
+fi
+
 if test x$enable_osx = xyes; then
 	echo " OS X support ..................enabled"
 else
@@ -1338,6 +1362,7 @@ echo ""
 if
 	test x$enable_ao = xno &&
 	test x$enable_oss = xno &&
+	test x$enable_openal = xno &&
 	test x$enable_shout = xno &&
 	test x$enable_recorder_output = xno &&
 	test x$enable_httpd_output = xno &&
diff --git a/src/output/openal_plugin.c b/src/output/openal_plugin.c
new file mode 100644
index 0000000..2d581eb
--- /dev/null
+++ b/src/output/openal_plugin.c
@@ -0,0 +1,267 @@
+/*
+ * Copyright (C) 2003-2009 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 "../output_api.h"
+#include "../timer.h"
+
+#include <glib.h>
+
+#include <AL/al.h>
+#include <AL/alc.h>
+
+#undef G_LOG_DOMAIN
+#define G_LOG_DOMAIN "openal"
+
+/* should be enough for buffer size = 2048 */
+#define NUM_BUFFERS 16
+
+struct openal_data {
+	const char *device_name;
+	ALCdevice *device;
+	ALCcontext *context;
+	Timer *timer;
+	ALuint buffers[NUM_BUFFERS];
+	int 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)
+{
+	/* Only 8 and 16 bit samples are supported */
+	if (audio_format->bits != 16 && audio_format->bits != 8)
+		audio_format->bits = 16;
+
+	switch (audio_format->bits)
+	{
+	case 16:
+		if (audio_format->channels == 2)
+			return AL_FORMAT_STEREO16;
+		if (audio_format->channels == 1)
+			return AL_FORMAT_MONO16;
+		break;
+
+	case 8:
+		if (audio_format->channels == 2)
+			return AL_FORMAT_STEREO8;
+		if (audio_format->channels == 1)
+			return AL_FORMAT_MONO8;
+		break;
+	}
+
+	return 0;
+}
+
+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 void
+openal_unqueue_buffers(struct openal_data *od)
+{
+	ALint num;
+	ALuint buffer;
+
+	alGetSourcei(od->source, AL_BUFFERS_QUEUED, &num);
+
+	while (num--) {
+		alSourceUnqueueBuffers(od->source, 1, &buffer);
+	}
+}
+
+static void *
+openal_init(G_GNUC_UNUSED const struct audio_format *audio_format,
+	    const struct config_param *param,
+	    G_GNUC_UNUSED GError **error)
+{
+	const char *device_name = config_get_block_string(param, "device", NULL);
+	struct openal_data *od;
+
+	od = g_new(struct openal_data, 1);
+	od->device_name = device_name;
+
+	if (device_name == NULL) {
+		device_name = alcGetString(NULL, ALC_DEFAULT_DEVICE_SPECIFIER);
+	}
+
+	return od;
+}
+
+static void
+openal_finish(void *data)
+{
+	struct openal_data *od = data;
+
+	g_free(od);
+}
+
+static bool
+openal_open(void *data, struct audio_format *audio_format,
+	    GError **error)
+{
+	struct openal_data *od = data;
+
+	od->format = openal_audio_format(audio_format);
+
+	if (!od->format) {
+		g_set_error(error, openal_output_quark(), 0,
+			    "Unsupported audio format (%i channels, %i bps)",
+			    audio_format->channels,
+			    audio_format->bits);
+		return false;
+	}
+
+	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->timer = timer_new(audio_format);
+	od->frequency = audio_format->sample_rate;
+
+	return true;
+}
+
+static void
+openal_close(void *data)
+{
+	struct openal_data *od = data;
+
+	timer_free(od->timer);
+	alcMakeContextCurrent(od->context);
+	alDeleteSources(1, &od->source);
+	alDeleteBuffers(NUM_BUFFERS, od->buffers);
+	alcDestroyContext(od->context);
+	alcCloseDevice(od->device);
+}
+
+static size_t
+openal_play(void *data, const void *chunk, size_t size,
+	    G_GNUC_UNUSED GError **error)
+{
+	struct openal_data *od = data;
+	ALuint buffer;
+	ALint num, state;
+
+	if (alcGetCurrentContext() != od->context) {
+		alcMakeContextCurrent(od->context);
+	}
+
+	alGetSourcei(od->source, AL_BUFFERS_PROCESSED, &num);
+
+	if (od->filled < NUM_BUFFERS) {
+		/* fill all buffers */
+		buffer = od->buffers[od->filled];
+		od->filled++;
+	} else {
+		/* wait for processed buffer */
+		while (num < 1) {
+			if (!od->timer->started) {
+				timer_start(od->timer);
+			} else {
+				timer_sync(od->timer);
+			}
+
+			timer_add(od->timer, size);
+
+			alGetSourcei(od->source, AL_BUFFERS_PROCESSED, &num);
+		}
+
+		alSourceUnqueueBuffers(od->source, 1, &buffer);
+	}
+
+	alBufferData(buffer, od->format, chunk, size, od->frequency);
+	alSourceQueueBuffers(od->source, 1, &buffer);
+	alGetSourcei(od->source, AL_SOURCE_STATE, &state);
+
+	if (state != AL_PLAYING) {
+		alSourcePlay(od->source);
+	}
+
+	return size;
+}
+
+static void
+openal_cancel(void *data)
+{
+	struct openal_data *od = data;
+
+	od->filled = 0;
+	alcMakeContextCurrent(od->context);
+	alSourceStop(od->source);
+	openal_unqueue_buffers(od);
+}
+
+const struct audio_output_plugin openal_output_plugin = {
+	.name = "openal",
+	.init = openal_init,
+	.finish = openal_finish,
+	.open = openal_open,
+	.close = openal_close,
+	.play = openal_play,
+	.cancel = openal_cancel,
+};
diff --git a/src/output_list.c b/src/output_list.c
index 74a9be8..476701a 100644
--- a/src/output_list.c
+++ b/src/output_list.c
@@ -28,6 +28,7 @@ extern const struct audio_output_plugin pipe_output_plugin;
 extern const struct audio_output_plugin alsaPlugin;
 extern const struct audio_output_plugin ao_output_plugin;
 extern const struct audio_output_plugin oss_output_plugin;
+extern const struct audio_output_plugin openal_output_plugin;
 extern const struct audio_output_plugin osxPlugin;
 extern const struct audio_output_plugin solaris_output_plugin;
 extern const struct audio_output_plugin pulse_plugin;
@@ -56,6 +57,9 @@ const struct audio_output_plugin *audio_output_plugins[] = {
 #ifdef HAVE_OSS
 	&oss_output_plugin,
 #endif
+#ifdef HAVE_OPENAL
+	&openal_output_plugin,
+#endif
 #ifdef HAVE_OSX
 	&osxPlugin,
 #endif
-- 
1.6.3.2

------------------------------------------------------------------------------
Let Crystal Reports handle the reporting - Free Crystal Reports 2008 30-Day 
trial. Simplify your report design, integration and deployment - and focus on 
what you do best, core application coding. Discover what's new with 
Crystal Reports now.  http://p.sf.net/sfu/bobj-july
_______________________________________________
Musicpd-dev-team mailing list
Musicpd-dev-team@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/musicpd-dev-team

Reply via email to