changeset 1635:a3a6f657f895 trunk

[svn] - fork the OSS plugin for CoreAudio work
author nenolod
date Thu, 07 Sep 2006 09:11:46 -0700
parents f84a1228e6b4
children 09905c29250d
files ChangeLog Plugins/Output/CoreAudio/OSS.c Plugins/Output/CoreAudio/OSS.h Plugins/Output/CoreAudio/about.c Plugins/Output/CoreAudio/audio.c Plugins/Output/CoreAudio/configure.c Plugins/Output/CoreAudio/convert.c Plugins/Output/CoreAudio/init.c Plugins/Output/CoreAudio/mixer.c
diffstat 9 files changed, 1921 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Wed Sep 06 22:10:19 2006 -0700
+++ b/ChangeLog	Thu Sep 07 09:11:46 2006 -0700
@@ -1,3 +1,13 @@
+2006-09-07 05:10:19 +0000  William Pitcock <nenolod@nenolod.net>
+  revision [2199]
+  - kludge for libintl borkenness
+  
+
+  Changes:        Modified:
+  +0 -2           trunk/audacious/Makefile  
+  +1 -0           trunk/audacious/intl  
+
+
 2006-09-07 05:03:27 +0000  William Pitcock <nenolod@nenolod.net>
   revision [2197]
   - include time.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Output/CoreAudio/OSS.c	Thu Sep 07 09:11:46 2006 -0700
@@ -0,0 +1,72 @@
+/*  BMP - Cross-platform multimedia player
+ *  Copyright (C) 2003-2004  BMP development team.
+ *
+ *  Based on XMMS:
+ *  Copyright (C) 1998-2003  XMMS development team.
+ *
+ *  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 "OSS.h"
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <stdlib.h>
+
+OutputPlugin oss_op = {
+    NULL,
+    NULL,
+    NULL,                       /* Description */
+    oss_init,
+    oss_cleanup,
+    oss_about,
+    oss_configure,
+    oss_get_volume,
+    oss_set_volume,
+    oss_open,
+    oss_write,
+    oss_close,
+    oss_flush,
+    oss_pause,
+    oss_free,
+    oss_playing,
+    oss_get_output_time,
+    oss_get_written_time,
+    oss_tell
+};
+
+OutputPlugin *
+get_oplugin_info(void)
+{
+    oss_op.description = g_strdup_printf(_("OSS Output Plugin"));
+    return &oss_op;
+}
+
+
+void oss_cleanup(void)
+{
+    g_free(oss_op.description);
+    oss_op.description = NULL;
+
+    if (oss_cfg.alt_audio_device) {
+        free(oss_cfg.alt_audio_device);
+        oss_cfg.alt_audio_device = NULL;
+    }
+
+    if (oss_cfg.alt_mixer_device) {
+        free(oss_cfg.alt_mixer_device);
+        oss_cfg.alt_mixer_device = NULL;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Output/CoreAudio/OSS.h	Thu Sep 07 09:11:46 2006 -0700
@@ -0,0 +1,67 @@
+/*
+ *  Copyright (C) 2006 William Pitcock <nenolod -at- nenolod.net>
+ *
+ *  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 COREAUDIO_H
+#define COREAUDIO_H
+
+#ifdef HAVE_CONFIG_H
+#  include "config.h"
+#endif
+
+#include <glib.h>
+
+#include <CoreServices/CoreServices.h>
+#include <AudioUnit/AudioUnit.h>
+
+#include "audacious/plugin.h"
+
+#define IS_BIG_ENDIAN (G_BYTE_ORDER == G_BIG_ENDIAN)
+
+extern OutputPlugin op;
+
+struct CoreAudioState {
+	
+};
+
+void ca_init(void);
+void ca_cleanup(void);
+void ca_about(void);
+void ca_configure(void);
+
+void ca_get_volume(int *l, int *r);
+void ca_set_volume(int l, int r);
+
+int ca_playing(void);
+int ca_free(void);
+void ca_write(void *ptr, int length);
+void ca_close(void);
+void ca_flush(int time);
+void ca_pause(short p);
+int ca_open(AFormat fmt, int rate, int nch);
+int ca_get_output_time(void);
+int ca_get_written_time(void);
+void ca_set_audio_params(void);
+
+void ca_free_convert_buffer(void);
+int (*ca_get_convert_func(int output, int input)) (void **, int);
+int (*ca_get_stereo_convert_func(int output, int input)) (void **, int,
+                                                           int);
+
+void ca_tell(AFormat * fmt, gint * rate, gint * nch);
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Output/CoreAudio/about.c	Thu Sep 07 09:11:46 2006 -0700
@@ -0,0 +1,57 @@
+/*  BMP - Cross-platform multimedia player
+ *  Copyright (C) 2003-2004  BMP development team.
+ *
+ *  Based on XMMS:
+ *  Copyright (C) 1998-2003  XMMS development team.
+ *
+ *  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 "OSS.h"
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+
+#include <libaudacious/util.h>
+
+
+void
+oss_about(void)
+{
+    static GtkWidget *dialog;
+
+    if (dialog != NULL)
+        return;
+
+    dialog = xmms_show_message(_("About OSS Driver"),
+                               _("Audacious OSS Driver\n\n "
+                                 "This program is free software; you can redistribute it and/or modify\n"
+                                 "it under the terms of the GNU General Public License as published by\n"
+                                 "the Free Software Foundation; either version 2 of the License, or\n"
+                                 "(at your option) any later version.\n"
+                                 "\n"
+                                 "This program is distributed in the hope that it will be useful,\n"
+                                 "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
+                                 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n"
+                                 "GNU General Public License for more details.\n"
+                                 "\n"
+                                 "You should have received a copy of the GNU General Public License\n"
+                                 "along with this program; if not, write to the Free Software\n"
+                                 "Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,\n"
+                                 "USA."), _("Ok"), FALSE, NULL, NULL);
+    g_signal_connect(G_OBJECT(dialog), "destroy",
+                     G_CALLBACK(gtk_widget_destroyed), &dialog);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Output/CoreAudio/audio.c	Thu Sep 07 09:11:46 2006 -0700
@@ -0,0 +1,723 @@
+/*  BMP - Cross-platform multimedia player
+ *  Copyright (C) 2003-2004  BMP development team.
+ *
+ *  Based on XMMS:
+ *  Copyright (C) 1998-2003  XMMS development team.
+ *
+ *  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 <glib.h>
+#include <libaudacious/util.h>
+#include <string.h>
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+
+#include "OSS.h"
+
+
+#define NFRAGS		32
+
+static gint fd = 0;
+static char *buffer;
+static gboolean going, prebuffer, paused, unpause, do_pause, remove_prebuffer;
+static gint device_buffer_used, buffer_size, prebuffer_size, blk_size;
+static gint rd_index = 0, wr_index = 0;
+static gint output_time_offset = 0;
+static guint64 written = 0, output_bytes = 0;
+static gint flush;
+static gint fragsize, device_buffer_size;
+static gchar *device_name;
+static GThread *buffer_thread;
+static gboolean realtime, select_works;
+
+static int (*oss_convert_func) (void **data, int length);
+static int (*oss_stereo_convert_func) (void **data, int length, int fmt);
+
+struct format_info {
+    union {
+        AFormat xmms;
+        int oss;
+    } format;
+    int frequency;
+    int channels;
+    int bps;
+};
+
+
+/*
+ * The format of the data from the input plugin
+ * This will never change during a song. 
+ */
+struct format_info input;
+
+/*
+ * The format we get from the effect plugin.
+ * This will be different from input if the effect plugin does
+ * some kind of format conversion.
+ */
+struct format_info effect;
+
+/*
+ * The format of the data we actually send to the soundcard.
+ * This might be different from effect if we need to resample or do
+ * some other format conversion.
+ */
+struct format_info output;
+
+
+static void
+oss_calc_device_buffer_used(void)
+{
+    audio_buf_info buf_info;
+    if (paused)
+        device_buffer_used = 0;
+    else if (!ioctl(fd, SNDCTL_DSP_GETOSPACE, &buf_info))
+        device_buffer_used =
+            (buf_info.fragstotal * buf_info.fragsize) - buf_info.bytes;
+}
+
+
+static gint oss_downsample(gpointer ob, guint length, guint speed,
+                           guint espeed);
+
+static int
+oss_calc_bitrate(int oss_fmt, int rate, int channels)
+{
+    int bitrate = rate * channels;
+
+    if (oss_fmt == AFMT_U16_BE || oss_fmt == AFMT_U16_LE ||
+        oss_fmt == AFMT_S16_BE || oss_fmt == AFMT_S16_LE)
+        bitrate *= 2;
+
+    return bitrate;
+}
+
+static int
+oss_get_format(AFormat fmt)
+{
+    int format = 0;
+
+    switch (fmt) {
+    case FMT_U8:
+        format = AFMT_U8;
+        break;
+    case FMT_S8:
+        format = AFMT_S8;
+        break;
+    case FMT_U16_LE:
+        format = AFMT_U16_LE;
+        break;
+    case FMT_U16_BE:
+        format = AFMT_U16_BE;
+        break;
+    case FMT_U16_NE:
+#if (G_BYTE_ORDER == G_BIG_ENDIAN)
+        format = AFMT_U16_BE;
+#else
+        format = AFMT_U16_LE;
+#endif
+        break;
+    case FMT_S16_LE:
+        format = AFMT_S16_LE;
+        break;
+    case FMT_S16_BE:
+        format = AFMT_S16_BE;
+        break;
+    case FMT_S16_NE:
+#if (G_BYTE_ORDER == G_BIG_ENDIAN)
+        format = AFMT_S16_BE;
+#else
+        format = AFMT_S16_LE;
+#endif
+        break;
+    }
+
+    return format;
+}
+
+static void
+oss_setup_format(AFormat fmt, int rate, int nch)
+{
+    effect.format.xmms = fmt;
+    effect.frequency = rate;
+    effect.channels = nch;
+    effect.bps = oss_calc_bitrate(oss_get_format(fmt), rate, nch);
+
+    output.format.oss = oss_get_format(fmt);
+    output.frequency = rate;
+    output.channels = nch;
+
+
+    fragsize = 0;
+    while ((1L << fragsize) < effect.bps / 25)
+        fragsize++;
+    fragsize--;
+
+    device_buffer_size = ((1L << fragsize) * (NFRAGS + 1));
+
+    oss_set_audio_params();
+
+    output.bps = oss_calc_bitrate(output.format.oss, output.frequency,
+                                  output.channels);
+}
+
+
+gint
+oss_get_written_time(void)
+{
+    if (!going)
+        return 0;
+    return (written * 1000) / effect.bps;
+}
+
+gint
+oss_get_output_time(void)
+{
+    guint64 bytes;
+
+    if (!fd || !going)
+        return 0;
+
+    if (realtime)
+        oss_calc_device_buffer_used();
+    bytes = output_bytes < device_buffer_used ?
+        0 : output_bytes - device_buffer_used;
+
+    return output_time_offset + ((bytes * 1000) / output.bps);
+}
+
+static int
+oss_used(void)
+{
+    if (realtime)
+        return 0;
+    else {
+        if (wr_index >= rd_index)
+            return wr_index - rd_index;
+        return buffer_size - (rd_index - wr_index);
+    }
+}
+
+gint
+oss_playing(void)
+{
+    if (!going)
+        return 0;
+    if (realtime)
+        oss_calc_device_buffer_used();
+    if (!oss_used() && (device_buffer_used - (3 * blk_size)) <= 0)
+        return FALSE;
+
+    return TRUE;
+}
+
+gint
+oss_free(void)
+{
+    if (!realtime) {
+        if (remove_prebuffer && prebuffer) {
+            prebuffer = FALSE;
+            remove_prebuffer = FALSE;
+        }
+        if (prebuffer)
+            remove_prebuffer = TRUE;
+
+        if (rd_index > wr_index)
+            return (rd_index - wr_index) - device_buffer_size - 1;
+        return (buffer_size - (wr_index - rd_index)) - device_buffer_size - 1;
+    }
+    else if (paused)
+        return 0;
+    else
+        return 1000000;
+}
+
+static inline ssize_t
+write_all(int fd, const void *buf, size_t count)
+{
+    size_t done = 0;
+    do {
+        ssize_t n = write(fd, (gchar *) buf + done, count - done);
+        if (n == -1) {
+            if (errno == EINTR)
+                continue;
+            else
+                break;
+        }
+        done += n;
+    } while (count > done);
+
+    return done;
+}
+
+static void
+oss_write_audio(gpointer data, int length)
+{
+
+    audio_buf_info abuf_info;
+    AFormat new_format;
+    int new_frequency, new_channels;
+    EffectPlugin *ep;
+
+    new_format = input.format.xmms;
+    new_frequency = input.frequency;
+    new_channels = input.channels;
+
+
+    ep = get_current_effect_plugin();
+    if (effects_enabled() && ep && ep->query_format) {
+        ep->query_format(&new_format, &new_frequency, &new_channels);
+    }
+
+    if (new_format != effect.format.xmms ||
+        new_frequency != effect.frequency ||
+        new_channels != effect.channels) {
+        output_time_offset += (output_bytes * 1000) / output.bps;
+        output_bytes = 0;
+        close(fd);
+        fd = open(device_name, O_WRONLY);
+        oss_setup_format(new_format, new_frequency, new_channels);
+    }
+    if (effects_enabled() && ep && ep->mod_samples)
+        length = ep->mod_samples(&data, length,
+                                 input.format.xmms,
+                                 input.frequency, input.channels);
+    if (realtime && !ioctl(fd, SNDCTL_DSP_GETOSPACE, &abuf_info)) {
+        while (abuf_info.bytes < length) {
+            xmms_usleep(10000);
+            if (ioctl(fd, SNDCTL_DSP_GETOSPACE, &abuf_info))
+                break;
+        }
+    }
+
+    if (oss_convert_func != NULL)
+        length = oss_convert_func(&data, length);
+
+    if (oss_stereo_convert_func != NULL)
+        length = oss_stereo_convert_func(&data, length, output.format.oss);
+
+    if (effect.frequency == output.frequency)
+        output_bytes += write_all(fd, data, length);
+    else
+        output_bytes += oss_downsample(data, length,
+                                       effect.frequency, output.frequency);
+}
+
+static void
+swap_endian(guint16 * data, int length)
+{
+    int i;
+    for (i = 0; i < length; i += 2, data++)
+        *data = GUINT16_SWAP_LE_BE(*data);
+}
+
+#define NOT_NATIVE_ENDIAN ((IS_BIG_ENDIAN &&				\
+			   (output.format.oss == AFMT_S16_LE ||		\
+			    output.format.oss == AFMT_U16_LE)) ||	\
+			  (!IS_BIG_ENDIAN &&				\
+			   (output.format.oss == AFMT_S16_BE ||		\
+			    output.format.oss == AFMT_U16_BE)))
+
+
+#define RESAMPLE_STEREO(sample_type)				\
+do {								\
+	const gint shift = sizeof (sample_type);		\
+        gint i, in_samples, out_samples, x, delta;		\
+	sample_type *inptr = (sample_type *)ob, *outptr;	\
+	guint nlen = (((length >> shift) * espeed) / speed);	\
+	if (nlen == 0)						\
+		break;						\
+	nlen <<= shift;						\
+	if (NOT_NATIVE_ENDIAN)					\
+		swap_endian(ob, length);			\
+	if(nlen > nbuffer_size)					\
+	{							\
+		nbuffer = g_realloc(nbuffer, nlen);		\
+		nbuffer_size = nlen;				\
+	}							\
+	outptr = (sample_type *)nbuffer;			\
+	in_samples = length >> shift;				\
+        out_samples = nlen >> shift;				\
+	delta = (in_samples << 12) / out_samples;		\
+	for (x = 0, i = 0; i < out_samples; i++)		\
+	{							\
+		gint x1, frac;					\
+		x1 = (x >> 12) << 12;				\
+		frac = x - x1;					\
+		*outptr++ =					\
+			(sample_type)				\
+			((inptr[(x1 >> 12) << 1] *		\
+			  ((1<<12) - frac) +			\
+			  inptr[((x1 >> 12) + 1) << 1] *	\
+			  frac) >> 12);				\
+		*outptr++ =					\
+			(sample_type)				\
+			((inptr[((x1 >> 12) << 1) + 1] *	\
+			  ((1<<12) - frac) +			\
+			  inptr[(((x1 >> 12) + 1) << 1) + 1] *	\
+			  frac) >> 12);				\
+		x += delta;					\
+	}							\
+	if (NOT_NATIVE_ENDIAN)					\
+		swap_endian(nbuffer, nlen);			\
+	w = write_all(fd, nbuffer, nlen);			\
+} while (0)
+
+
+#define RESAMPLE_MONO(sample_type)				\
+do {								\
+	const gint shift = sizeof (sample_type) - 1;		\
+        gint i, x, delta, in_samples, out_samples;		\
+	sample_type *inptr = (sample_type *)ob, *outptr;	\
+	guint nlen = (((length >> shift) * espeed) / speed);	\
+	if (nlen == 0)						\
+		break;						\
+	nlen <<= shift;						\
+	if (NOT_NATIVE_ENDIAN)					\
+		swap_endian(ob, length);			\
+	if(nlen > nbuffer_size)					\
+	{							\
+		nbuffer = g_realloc(nbuffer, nlen);		\
+		nbuffer_size = nlen;				\
+	}							\
+	outptr = (sample_type *)nbuffer;			\
+	in_samples = length >> shift;				\
+        out_samples = nlen >> shift;				\
+	delta = ((length >> shift) << 12) / out_samples;	\
+	for (x = 0, i = 0; i < out_samples; i++)		\
+	{							\
+		gint x1, frac;					\
+		x1 = (x >> 12) << 12;				\
+		frac = x - x1;					\
+		*outptr++ =					\
+			(sample_type)				\
+			((inptr[x1 >> 12] * ((1<<12) - frac) +	\
+			  inptr[(x1 >> 12) + 1] * frac) >> 12);	\
+		x += delta;					\
+	}							\
+	if (NOT_NATIVE_ENDIAN)					\
+		swap_endian(nbuffer, nlen);			\
+	w = write_all(fd, nbuffer, nlen);			\
+} while (0)
+
+
+static gint
+oss_downsample(gpointer ob, guint length, guint speed, guint espeed)
+{
+    guint w = 0;
+    static gpointer nbuffer = NULL;
+    static guint nbuffer_size = 0;
+
+    switch (output.format.oss) {
+    case AFMT_S16_BE:
+    case AFMT_S16_LE:
+        if (output.channels == 2)
+            RESAMPLE_STEREO(gint16);
+        else
+            RESAMPLE_MONO(gint16);
+        break;
+    case AFMT_U16_BE:
+    case AFMT_U16_LE:
+        if (output.channels == 2)
+            RESAMPLE_STEREO(guint16);
+        else
+            RESAMPLE_MONO(guint16);
+        break;
+    case AFMT_S8:
+        if (output.channels == 2)
+            RESAMPLE_STEREO(gint8);
+        else
+            RESAMPLE_MONO(gint8);
+        break;
+    case AFMT_U8:
+        if (output.channels == 2)
+            RESAMPLE_STEREO(guint8);
+        else
+            RESAMPLE_MONO(guint8);
+        break;
+    }
+    return w;
+}
+
+void
+oss_write(gpointer ptr, int length)
+{
+    int cnt, off = 0;
+
+    if (!realtime) {
+        remove_prebuffer = FALSE;
+
+        written += length;
+        while (length > 0) {
+            cnt = MIN(length, buffer_size - wr_index);
+            memcpy(buffer + wr_index, (char *) ptr + off, cnt);
+            wr_index = (wr_index + cnt) % buffer_size;
+            length -= cnt;
+            off += cnt;
+        }
+    }
+    else {
+        if (paused)
+            return;
+        oss_write_audio(ptr, length);
+        written += length;
+    }
+}
+
+void
+oss_close(void)
+{
+    if (!going)
+        return;
+    going = 0;
+    if (!realtime)
+        g_thread_join(buffer_thread);
+    else {
+        ioctl(fd, SNDCTL_DSP_RESET, 0);
+        close(fd);
+    }
+    g_free(device_name);
+    oss_free_convert_buffer();
+    wr_index = 0;
+    rd_index = 0;
+}
+
+void
+oss_flush(gint time)
+{
+    if (!realtime) {
+        flush = time;
+        while (flush != -1)
+            xmms_usleep(10000);
+    }
+    else {
+        ioctl(fd, SNDCTL_DSP_RESET, 0);
+        close(fd);
+        fd = open(device_name, O_WRONLY);
+        oss_set_audio_params();
+        output_time_offset = time;
+        written = ((guint64) time * input.bps) / 1000;
+        output_bytes = 0;
+    }
+}
+
+void
+oss_pause(short p)
+{
+    if (!realtime) {
+        if (p == TRUE)
+            do_pause = TRUE;
+        else
+            unpause = TRUE;
+    }
+    else
+        paused = p;
+
+}
+
+gpointer
+oss_loop(gpointer arg)
+{
+    gint length, cnt;
+    fd_set set;
+    struct timeval tv;
+
+    while (going) {
+        if (oss_used() > prebuffer_size)
+            prebuffer = FALSE;
+        if (oss_used() > 0 && !paused && !prebuffer) {
+            tv.tv_sec = 0;
+            tv.tv_usec = 10000;
+            FD_ZERO(&set);
+            FD_SET(fd, &set);
+            if (!select_works || (select(fd + 1, NULL, &set, NULL, &tv) > 0)) {
+                length = MIN(blk_size, oss_used());
+                while (length > 0) {
+                    cnt = MIN(length, buffer_size - rd_index);
+                    oss_write_audio(buffer + rd_index, cnt);
+                    rd_index = (rd_index + cnt) % buffer_size;
+                    length -= cnt;
+                }
+                if (!oss_used())
+                    ioctl(fd, SNDCTL_DSP_POST, 0);
+            }
+        }
+        else
+            xmms_usleep(10000);
+        oss_calc_device_buffer_used();
+        if (do_pause && !paused) {
+            do_pause = FALSE;
+            paused = TRUE;
+            /*
+             * We lose some data here that is sent to the
+             * soundcard, but not yet played.  I don't
+             * think this is worth fixing.
+             */
+            ioctl(fd, SNDCTL_DSP_RESET, 0);
+        }
+        else if (unpause && paused) {
+            unpause = FALSE;
+            close(fd);
+            fd = open(device_name, O_WRONLY);
+            oss_set_audio_params();
+            paused = FALSE;
+        }
+
+        if (flush != -1) {
+            /*
+             * This close and open is a work around of a
+             * bug that exists in some drivers which cause
+             * the driver to get fucked up by a reset
+             */
+
+            ioctl(fd, SNDCTL_DSP_RESET, 0);
+            close(fd);
+            fd = open(device_name, O_WRONLY);
+            oss_set_audio_params();
+            output_time_offset = flush;
+            written = ((guint64) flush * input.bps) / 1000;
+            rd_index = wr_index = output_bytes = 0;
+            flush = -1;
+            prebuffer = TRUE;
+        }
+
+    }
+
+    ioctl(fd, SNDCTL_DSP_RESET, 0);
+    close(fd);
+    g_free(buffer);
+    return NULL;
+}
+
+void
+oss_set_audio_params(void)
+{
+    int frag, stereo, ret;
+    struct timeval tv;
+    fd_set set;
+
+    ioctl(fd, SNDCTL_DSP_RESET, 0);
+    frag = (NFRAGS << 16) | fragsize;
+    ioctl(fd, SNDCTL_DSP_SETFRAGMENT, &frag);
+    /*
+     * Set the stream format.  This ioctl() might fail, but should
+     * return a format that works if it does.
+     */
+    ioctl(fd, SNDCTL_DSP_SETFMT, &output.format.oss);
+    if (ioctl(fd, SNDCTL_DSP_SETFMT, &output.format.oss) == -1)
+        g_warning("SNDCTL_DSP_SETFMT ioctl failed: %s", strerror(errno));
+
+    stereo = output.channels - 1;
+    ioctl(fd, SNDCTL_DSP_STEREO, &stereo);
+    output.channels = stereo + 1;
+
+    oss_stereo_convert_func = oss_get_stereo_convert_func(output.channels,
+                                                          effect.channels);
+
+    if (ioctl(fd, SNDCTL_DSP_SPEED, &output.frequency) == -1)
+        g_warning("SNDCTL_DSP_SPEED ioctl failed: %s", strerror(errno));
+
+    if (ioctl(fd, SNDCTL_DSP_GETBLKSIZE, &blk_size) == -1)
+        blk_size = 1L << fragsize;
+
+    oss_convert_func =
+        oss_get_convert_func(output.format.oss,
+                             oss_get_format(effect.format.xmms));
+
+    /*
+     * Stupid hack to find out if the driver support selects, some
+     * drivers won't work properly without a select and some won't
+     * work with a select :/
+     */
+
+    tv.tv_sec = 0;
+    tv.tv_usec = 50000;
+    FD_ZERO(&set);
+    FD_SET(fd, &set);
+    ret = select(fd + 1, NULL, &set, NULL, &tv);
+    if (ret > 0)
+        select_works = TRUE;
+    else
+        select_works = FALSE;
+}
+
+gint
+oss_open(AFormat fmt, gint rate, gint nch)
+{
+
+    if (oss_cfg.use_alt_audio_device && oss_cfg.alt_audio_device)
+        device_name = g_strdup(oss_cfg.alt_audio_device);
+    else {
+        if (oss_cfg.audio_device > 0)
+            device_name =
+                g_strdup_printf("%s%d", DEV_DSP, oss_cfg.audio_device);
+        else
+            device_name = g_strdup(DEV_DSP);
+    }
+
+    fd = open(device_name, O_WRONLY);
+
+    if (fd == -1) {
+        g_warning("oss_open(): Failed to open audio device (%s): %s",
+                  device_name, strerror(errno));
+        g_free(device_name);
+        return 0;
+    }
+
+    input.format.xmms = fmt;
+    input.frequency = rate;
+    input.channels = nch;
+    input.bps = oss_calc_bitrate(oss_get_format(fmt), rate, nch);
+
+    oss_setup_format(fmt, rate, nch);
+
+    realtime = xmms_check_realtime_priority();
+
+    if (!realtime) {
+        buffer_size = (oss_cfg.buffer_size * input.bps) / 1000;
+        if (buffer_size < 8192)
+            buffer_size = 8192;
+        prebuffer_size = (buffer_size * oss_cfg.prebuffer) / 100;
+        if (buffer_size - prebuffer_size < 4096)
+            prebuffer_size = buffer_size - 4096;
+
+        buffer_size += device_buffer_size;
+        buffer = g_malloc0(buffer_size);
+    }
+    flush = -1;
+    prebuffer = TRUE;
+    wr_index = rd_index = output_time_offset = written = output_bytes = 0;
+    paused = FALSE;
+    do_pause = FALSE;
+    unpause = FALSE;
+    remove_prebuffer = FALSE;
+
+    going = 1;
+    if (!realtime)
+        buffer_thread = g_thread_create(oss_loop, NULL, TRUE, NULL);
+    return 1;
+}
+
+void oss_tell(AFormat * fmt, gint * rate, gint * nch)
+{
+	(*fmt) = input.format.xmms;
+	(*rate) = input.frequency;
+	(*nch) = input.channels;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Output/CoreAudio/configure.c	Thu Sep 07 09:11:46 2006 -0700
@@ -0,0 +1,375 @@
+/*  BMP - Cross-platform multimedia player
+ *  Copyright (C) 2003-2004  BMP development team.
+ *
+ *  Based on XMMS:
+ *  Copyright (C) 1998-2003  XMMS development team.
+ *
+ *  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 "OSS.h"
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <libaudacious/configdb.h>
+
+
+static GtkWidget *configure_win = NULL;
+static GtkWidget *mixer_usemaster_check, *buffer_size_spin, *buffer_pre_spin;
+static GtkWidget *adevice_use_alt_check, *audio_alt_device_entry;
+static GtkWidget *mdevice_use_alt_check, *mixer_alt_device_entry;
+static gint audio_device, mixer_device;
+
+static void
+configure_win_ok_cb(GtkWidget * w, gpointer data)
+{
+    ConfigDb *db;
+
+    oss_cfg.audio_device = audio_device;
+    oss_cfg.mixer_device = mixer_device;
+    oss_cfg.buffer_size =
+        gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(buffer_size_spin));
+    oss_cfg.prebuffer =
+        gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(buffer_pre_spin));
+    oss_cfg.use_master =
+        gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON
+                                     (mixer_usemaster_check));
+    oss_cfg.use_alt_audio_device =
+        gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON
+                                     (adevice_use_alt_check));
+    oss_cfg.use_alt_mixer_device =
+        gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON
+                                     (mdevice_use_alt_check));
+    g_free(oss_cfg.alt_audio_device);
+    oss_cfg.alt_audio_device =
+        gtk_editable_get_chars(GTK_EDITABLE(audio_alt_device_entry), 0, -1);
+    g_strstrip(oss_cfg.alt_audio_device);
+    g_free(oss_cfg.alt_mixer_device);
+    oss_cfg.alt_mixer_device =
+        gtk_editable_get_chars(GTK_EDITABLE(mixer_alt_device_entry), 0, -1);
+    g_strstrip(oss_cfg.alt_mixer_device);
+
+    if (oss_cfg.use_alt_audio_device)
+        /* do a minimum of sanity checking */
+        if (oss_cfg.alt_audio_device[0] != '/')
+            oss_cfg.use_alt_audio_device = FALSE;
+    if (oss_cfg.use_alt_mixer_device)
+        if (oss_cfg.alt_mixer_device[0] != '/')
+            oss_cfg.use_alt_mixer_device = FALSE;
+
+    db = bmp_cfg_db_open();
+
+    bmp_cfg_db_set_int(db, "OSS", "audio_device", oss_cfg.audio_device);
+    bmp_cfg_db_set_int(db, "OSS", "mixer_device", oss_cfg.mixer_device);
+    bmp_cfg_db_set_int(db, "OSS", "buffer_size", oss_cfg.buffer_size);
+    bmp_cfg_db_set_int(db, "OSS", "prebuffer", oss_cfg.prebuffer);
+    bmp_cfg_db_set_bool(db, "OSS", "use_master", oss_cfg.use_master);
+    bmp_cfg_db_set_bool(db, "OSS", "use_alt_audio_device",
+                        oss_cfg.use_alt_audio_device);
+    bmp_cfg_db_set_string(db, "OSS", "alt_audio_device",
+                          oss_cfg.alt_audio_device);
+    bmp_cfg_db_set_bool(db, "OSS", "use_alt_mixer_device",
+                        oss_cfg.use_alt_mixer_device);
+    bmp_cfg_db_set_string(db, "OSS", "alt_mixer_device",
+                          oss_cfg.alt_mixer_device);
+    bmp_cfg_db_close(db);
+}
+
+static void
+configure_win_audio_dev_cb(GtkWidget * widget, gint device)
+{
+    audio_device = device;
+}
+
+static void
+configure_win_mixer_dev_cb(GtkWidget * widget, gint device)
+{
+    mixer_device = device;
+}
+
+static void
+audio_device_toggled(GtkToggleButton * widget, gpointer data)
+{
+    gboolean use_alt_audio_device = gtk_toggle_button_get_active(widget);
+    gtk_widget_set_sensitive(GTK_WIDGET(data), !use_alt_audio_device);
+    gtk_widget_set_sensitive(audio_alt_device_entry, use_alt_audio_device);
+}
+
+static void
+mixer_device_toggled(GtkToggleButton * widget, gpointer data)
+{
+    gboolean use_alt_device = gtk_toggle_button_get_active(widget);
+    gtk_widget_set_sensitive(GTK_WIDGET(data), !use_alt_device);
+    gtk_widget_set_sensitive(mixer_alt_device_entry, use_alt_device);
+}
+
+static void
+scan_devices(gchar * type, GtkWidget * option_menu, GtkSignalFunc sigfunc)
+{
+    GtkWidget *menu, *item;
+    FILE *file;
+    gchar buffer[256], *temp, *tmp2;
+    gboolean found = FALSE;
+    gint index = 0;
+
+    menu = gtk_menu_new();
+
+    if ((file = fopen("/dev/sndstat", "r"))) {
+        while (fgets(buffer, 255, file)) {
+            if (found && buffer[0] == '\n')
+                break;
+            if (buffer[strlen(buffer) - 1] == '\n')
+                buffer[strlen(buffer) - 1] = '\0';
+            if (found) {
+                if (index == 0) {
+                    tmp2 = strchr(buffer, ':');
+                    if (tmp2) {
+                        tmp2++;
+                        while (*tmp2 == ' ')
+                            tmp2++;
+                    }
+                    else
+                        tmp2 = buffer;
+                    temp = g_strdup_printf(_("Default (%s)"), tmp2);
+                    item = gtk_menu_item_new_with_label(temp);
+                    g_free(temp);
+                }
+                else
+                    item = gtk_menu_item_new_with_label(buffer);
+                g_signal_connect(G_OBJECT(item), "activate",
+                                 G_CALLBACK(sigfunc), (gpointer)(long)(index++));
+                gtk_widget_show(item);
+                gtk_menu_append(GTK_MENU(menu), item);
+            }
+            if (!strcasecmp(buffer, type))
+                found = 1;
+
+        }
+        fclose(file);
+    }
+    else {
+        item = gtk_menu_item_new_with_label(_("Default"));
+        g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(sigfunc),
+                         (gpointer) 0);
+        gtk_widget_show(item);
+        gtk_menu_append(GTK_MENU(menu), item);
+    }
+    gtk_option_menu_set_menu(GTK_OPTION_MENU(option_menu), menu);
+}
+
+void
+oss_configure(void)
+{
+    GtkWidget *vbox, *notebook;
+    GtkWidget *dev_vbox, *adevice_frame, *adevice_box, *adevice;
+    GtkWidget *mdevice_frame, *mdevice_box, *mdevice;
+    GtkWidget *buffer_frame, *buffer_vbox, *buffer_table;
+    GtkWidget *buffer_size_box, *buffer_size_label;
+    GtkObject *buffer_size_adj, *buffer_pre_adj;
+    GtkWidget *buffer_pre_box, *buffer_pre_label;
+    GtkWidget *audio_alt_box, *mixer_alt_box;
+    GtkWidget *bbox, *ok, *cancel;
+    GtkWidget *mixer_table, *mixer_frame;
+
+    if (configure_win) {
+        gtk_window_present(GTK_WINDOW(configure_win));
+        return;
+    }
+
+    configure_win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+    g_signal_connect(G_OBJECT(configure_win), "destroy",
+                     G_CALLBACK(gtk_widget_destroyed), &configure_win);
+    gtk_window_set_title(GTK_WINDOW(configure_win),
+                         _("OSS Driver configuration"));
+    gtk_window_set_type_hint(GTK_WINDOW(configure_win),
+                             GDK_WINDOW_TYPE_HINT_DIALOG);
+    gtk_window_set_resizable(GTK_WINDOW(configure_win), FALSE);
+    gtk_window_set_position(GTK_WINDOW(configure_win), GTK_WIN_POS_CENTER);
+    gtk_container_border_width(GTK_CONTAINER(configure_win), 10);
+
+    vbox = gtk_vbox_new(FALSE, 10);
+    gtk_container_add(GTK_CONTAINER(configure_win), vbox);
+
+    notebook = gtk_notebook_new();
+    gtk_box_pack_start(GTK_BOX(vbox), notebook, TRUE, TRUE, 0);
+
+    dev_vbox = gtk_vbox_new(FALSE, 5);
+    gtk_container_set_border_width(GTK_CONTAINER(dev_vbox), 5);
+
+    adevice_frame = gtk_frame_new(_("Audio device:"));
+    gtk_box_pack_start(GTK_BOX(dev_vbox), adevice_frame, FALSE, FALSE, 0);
+
+    adevice_box = gtk_vbox_new(FALSE, 5);
+    gtk_container_set_border_width(GTK_CONTAINER(adevice_box), 5);
+    gtk_container_add(GTK_CONTAINER(adevice_frame), adevice_box);
+
+    adevice = gtk_option_menu_new();
+    gtk_box_pack_start(GTK_BOX(adevice_box), adevice, TRUE, TRUE, 0);
+#if defined(HAVE_NEWPCM)
+    scan_devices("Installed devices:", adevice,
+                 GTK_SIGNAL_FUNC(configure_win_audio_dev_cb));
+#else
+    scan_devices("Audio devices:", adevice,
+                 GTK_SIGNAL_FUNC(configure_win_audio_dev_cb));
+#endif
+    audio_device = oss_cfg.audio_device;
+    gtk_option_menu_set_history(GTK_OPTION_MENU(adevice),
+                                oss_cfg.audio_device);
+    audio_alt_box = gtk_hbox_new(FALSE, 0);
+    gtk_box_pack_start_defaults(GTK_BOX(adevice_box), audio_alt_box);
+    adevice_use_alt_check =
+        gtk_check_button_new_with_label(_("Use alternate device:"));
+    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(adevice_use_alt_check),
+                                 oss_cfg.use_alt_audio_device);
+    g_signal_connect(G_OBJECT(adevice_use_alt_check), "toggled",
+                     G_CALLBACK(audio_device_toggled), adevice);
+    gtk_box_pack_start(GTK_BOX(audio_alt_box), adevice_use_alt_check,
+                       FALSE, FALSE, 0);
+    audio_alt_device_entry = gtk_entry_new();
+    if (oss_cfg.alt_audio_device != NULL)
+        gtk_entry_set_text(GTK_ENTRY(audio_alt_device_entry),
+                           oss_cfg.alt_audio_device);
+    else
+        gtk_entry_set_text(GTK_ENTRY(audio_alt_device_entry), DEV_DSP);
+    gtk_box_pack_start_defaults(GTK_BOX(audio_alt_box),
+                                audio_alt_device_entry);
+
+    if (oss_cfg.use_alt_audio_device)
+        gtk_widget_set_sensitive(adevice, FALSE);
+    else
+        gtk_widget_set_sensitive(audio_alt_device_entry, FALSE);
+
+    mdevice_frame = gtk_frame_new(_("Mixer device:"));
+    gtk_box_pack_start(GTK_BOX(dev_vbox), mdevice_frame, FALSE, FALSE, 0);
+
+    mdevice_box = gtk_vbox_new(FALSE, 0);
+    gtk_container_set_border_width(GTK_CONTAINER(mdevice_box), 5);
+    gtk_container_add(GTK_CONTAINER(mdevice_frame), mdevice_box);
+
+    mdevice = gtk_option_menu_new();
+    gtk_box_pack_start(GTK_BOX(mdevice_box), mdevice, TRUE, TRUE, 0);
+#if defined(HAVE_NEWPCM)
+    scan_devices("Installed devices:", mdevice, configure_win_mixer_dev_cb);
+#else
+    scan_devices("Mixers:", mdevice, G_CALLBACK(configure_win_mixer_dev_cb));
+#endif
+    mixer_device = oss_cfg.mixer_device;
+    gtk_option_menu_set_history(GTK_OPTION_MENU(mdevice),
+                                oss_cfg.mixer_device);
+    mixer_alt_box = gtk_hbox_new(FALSE, 0);
+    gtk_box_pack_start_defaults(GTK_BOX(mdevice_box), mixer_alt_box);
+    mdevice_use_alt_check =
+        gtk_check_button_new_with_label(_("Use alternate device:"));
+    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(mdevice_use_alt_check),
+                                 oss_cfg.use_alt_mixer_device);
+    g_signal_connect(G_OBJECT(mdevice_use_alt_check), "toggled",
+                     G_CALLBACK(mixer_device_toggled), mdevice);
+    gtk_box_pack_start(GTK_BOX(mixer_alt_box), mdevice_use_alt_check,
+                       FALSE, FALSE, 0);
+    mixer_alt_device_entry = gtk_entry_new();
+    if (oss_cfg.alt_mixer_device != NULL)
+        gtk_entry_set_text(GTK_ENTRY(mixer_alt_device_entry),
+                           oss_cfg.alt_mixer_device);
+    else
+        gtk_entry_set_text(GTK_ENTRY(mixer_alt_device_entry), DEV_MIXER);
+    gtk_box_pack_start_defaults(GTK_BOX(mixer_alt_box),
+                                mixer_alt_device_entry);
+
+    if (oss_cfg.use_alt_mixer_device)
+        gtk_widget_set_sensitive(mdevice, FALSE);
+    else
+        gtk_widget_set_sensitive(mixer_alt_device_entry, FALSE);
+
+    gtk_notebook_append_page(GTK_NOTEBOOK(notebook), dev_vbox,
+                             gtk_label_new(_("Devices")));
+
+    buffer_frame = gtk_frame_new(_("Buffering:"));
+    gtk_container_set_border_width(GTK_CONTAINER(buffer_frame), 5);
+
+    buffer_vbox = gtk_vbox_new(FALSE, 0);
+    gtk_container_add(GTK_CONTAINER(buffer_frame), buffer_vbox);
+
+    buffer_table = gtk_table_new(2, 1, TRUE);
+    gtk_container_set_border_width(GTK_CONTAINER(buffer_table), 5);
+    gtk_box_pack_start(GTK_BOX(buffer_vbox), buffer_table, FALSE, FALSE, 0);
+
+    buffer_size_box = gtk_hbox_new(FALSE, 5);
+    gtk_table_attach_defaults(GTK_TABLE(buffer_table), buffer_size_box, 0,
+                              1, 0, 1);
+    buffer_size_label = gtk_label_new(_("Buffer size (ms):"));
+    gtk_box_pack_start(GTK_BOX(buffer_size_box), buffer_size_label, FALSE,
+                       FALSE, 0);
+    buffer_size_adj =
+        gtk_adjustment_new(oss_cfg.buffer_size, 200, 10000, 100, 100, 100);
+    buffer_size_spin =
+        gtk_spin_button_new(GTK_ADJUSTMENT(buffer_size_adj), 8, 0);
+    gtk_widget_set_usize(buffer_size_spin, 60, -1);
+    gtk_box_pack_start(GTK_BOX(buffer_size_box), buffer_size_spin, FALSE,
+                       FALSE, 0);
+
+    buffer_pre_box = gtk_hbox_new(FALSE, 5);
+    gtk_table_attach_defaults(GTK_TABLE(buffer_table), buffer_pre_box, 1,
+                              2, 0, 1);
+    buffer_pre_label = gtk_label_new(_("Pre-buffer (percent):"));
+    gtk_box_pack_start(GTK_BOX(buffer_pre_box), buffer_pre_label, FALSE,
+                       FALSE, 0);
+    buffer_pre_adj = gtk_adjustment_new(oss_cfg.prebuffer, 0, 90, 1, 1, 1);
+    buffer_pre_spin =
+        gtk_spin_button_new(GTK_ADJUSTMENT(buffer_pre_adj), 1, 0);
+    gtk_widget_set_usize(buffer_pre_spin, 60, -1);
+    gtk_box_pack_start(GTK_BOX(buffer_pre_box), buffer_pre_spin, FALSE,
+                       FALSE, 0);
+
+    gtk_notebook_append_page(GTK_NOTEBOOK(notebook), buffer_frame,
+                             gtk_label_new(_("Buffering")));
+    mixer_frame = gtk_frame_new(_("Mixer Settings:"));
+    gtk_container_set_border_width(GTK_CONTAINER(mixer_frame), 5);
+    mixer_table = gtk_table_new(3, 2, TRUE);
+    gtk_container_add(GTK_CONTAINER(mixer_frame), mixer_table);
+    gtk_container_set_border_width(GTK_CONTAINER(mixer_table), 5);
+    mixer_usemaster_check =
+        gtk_check_button_new_with_label(_("Volume controls Master not PCM"));
+    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(mixer_usemaster_check),
+                                 oss_cfg.use_master);
+    gtk_table_attach_defaults(GTK_TABLE(mixer_table),
+                              mixer_usemaster_check, 0, 1, 0, 1);
+    gtk_notebook_append_page(GTK_NOTEBOOK(notebook), mixer_frame,
+                             gtk_label_new(_("Mixer")));
+
+    bbox = gtk_hbutton_box_new();
+    gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END);
+    gtk_button_box_set_spacing(GTK_BUTTON_BOX(bbox), 5);
+    gtk_box_pack_start(GTK_BOX(vbox), bbox, FALSE, FALSE, 0);
+
+    cancel = gtk_button_new_from_stock(GTK_STOCK_CLOSE);
+    g_signal_connect_swapped(G_OBJECT(cancel), "clicked",
+                             G_CALLBACK(gtk_widget_destroy),
+                             GTK_OBJECT(configure_win));
+    GTK_WIDGET_SET_FLAGS(cancel, GTK_CAN_DEFAULT);
+    gtk_box_pack_start(GTK_BOX(bbox), cancel, TRUE, TRUE, 0);
+
+    ok = gtk_button_new_from_stock(GTK_STOCK_APPLY);
+    g_signal_connect(G_OBJECT(ok), "clicked",
+                     G_CALLBACK(configure_win_ok_cb), NULL);
+    GTK_WIDGET_SET_FLAGS(ok, GTK_CAN_DEFAULT);
+    gtk_box_pack_start(GTK_BOX(bbox), ok, TRUE, TRUE, 0);
+    gtk_widget_grab_default(ok);
+
+    gtk_widget_show_all(configure_win);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Output/CoreAudio/convert.c	Thu Sep 07 09:11:46 2006 -0700
@@ -0,0 +1,446 @@
+/*
+ *  Copyright (C) 2001  Haavard Kvaalen
+ *
+ *  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 "OSS.h"
+
+struct buffer {
+    void *buffer;
+    int size;
+} format_buffer, stereo_buffer;
+
+
+static void *
+oss_get_convert_buffer(struct buffer *buffer, size_t size)
+{
+    if (size > 0 && size <= (size_t)buffer->size)
+        return buffer->buffer;
+
+    buffer->size = size;
+    buffer->buffer = g_realloc(buffer->buffer, size);
+    return buffer->buffer;
+}
+
+void
+oss_free_convert_buffer(void)
+{
+    oss_get_convert_buffer(&format_buffer, 0);
+    oss_get_convert_buffer(&stereo_buffer, 0);
+}
+
+
+static int
+convert_swap_endian(void **data, int length)
+{
+    guint16 *ptr = *data;
+    int i;
+    for (i = 0; i < length; i += 2, ptr++)
+        *ptr = GUINT16_SWAP_LE_BE(*ptr);
+
+    return i;
+}
+
+static int
+convert_swap_sign_and_endian_to_native(void **data, int length)
+{
+    guint16 *ptr = *data;
+    int i;
+    for (i = 0; i < length; i += 2, ptr++)
+        *ptr = GUINT16_SWAP_LE_BE(*ptr) ^ 1 << 15;
+
+    return i;
+}
+
+static int
+convert_swap_sign_and_endian_to_alien(void **data, int length)
+{
+    guint16 *ptr = *data;
+    int i;
+    for (i = 0; i < length; i += 2, ptr++)
+        *ptr = GUINT16_SWAP_LE_BE(*ptr ^ 1 << 15);
+
+    return i;
+}
+
+static int
+convert_swap_sign16(void **data, int length)
+{
+    gint16 *ptr = *data;
+    int i;
+    for (i = 0; i < length; i += 2, ptr++)
+        *ptr ^= 1 << 15;
+
+    return i;
+}
+
+static int
+convert_swap_sign8(void **data, int length)
+{
+    gint8 *ptr = *data;
+    int i;
+    for (i = 0; i < length; i++)
+        *ptr++ ^= 1 << 7;
+
+    return i;
+}
+
+static int
+convert_to_8_native_endian(void **data, int length)
+{
+    gint8 *output = *data;
+    gint16 *input = *data;
+    int i;
+    for (i = 0; i < length / 2; i++)
+        *output++ = *input++ >> 8;
+
+    return i;
+}
+
+static int
+convert_to_8_native_endian_swap_sign(void **data, int length)
+{
+    gint8 *output = *data;
+    gint16 *input = *data;
+    int i;
+    for (i = 0; i < length / 2; i++)
+        *output++ = (*input++ >> 8) ^ (1 << 7);
+
+    return i;
+}
+
+
+static int
+convert_to_8_alien_endian(void **data, int length)
+{
+    gint8 *output = *data;
+    gint16 *input = *data;
+    int i;
+    for (i = 0; i < length / 2; i++)
+        *output++ = *input++ & 0xff;
+
+    return i;
+}
+
+static int
+convert_to_8_alien_endian_swap_sign(void **data, int length)
+{
+    gint8 *output = *data;
+    gint16 *input = *data;
+    int i;
+    for (i = 0; i < length / 2; i++)
+        *output++ = (*input++ & 0xff) ^ (1 << 7);
+
+    return i;
+}
+
+static int
+convert_to_16_native_endian(void **data, int length)
+{
+    guint8 *input = *data;
+    guint16 *output;
+    int i;
+    *data = oss_get_convert_buffer(&format_buffer, length * 2);
+    output = *data;
+    for (i = 0; i < length; i++)
+        *output++ = *input++ << 8;
+
+    return i * 2;
+}
+
+static int
+convert_to_16_native_endian_swap_sign(void **data, int length)
+{
+    guint8 *input = *data;
+    guint16 *output;
+    int i;
+    *data = oss_get_convert_buffer(&format_buffer, length * 2);
+    output = *data;
+    for (i = 0; i < length; i++)
+        *output++ = (*input++ << 8) ^ (1 << 15);
+
+    return i * 2;
+}
+
+
+static int
+convert_to_16_alien_endian(void **data, int length)
+{
+    guint8 *input = *data;
+    guint16 *output;
+    int i;
+    *data = oss_get_convert_buffer(&format_buffer, length * 2);
+    output = *data;
+    for (i = 0; i < length; i++)
+        *output++ = *input++;
+
+    return i * 2;
+}
+
+static int
+convert_to_16_alien_endian_swap_sign(void **data, int length)
+{
+    guint8 *input = *data;
+    guint16 *output;
+    int i;
+    *data = oss_get_convert_buffer(&format_buffer, length * 2);
+    output = *data;
+    for (i = 0; i < length; i++)
+        *output++ = *input++ ^ (1 << 7);
+
+    return i * 2;
+}
+
+int (*oss_get_convert_func(int output, int input)) (void **, int) {
+    if (output == input)
+        return NULL;
+
+    if ((output == AFMT_U16_BE && input == AFMT_U16_LE) ||
+        (output == AFMT_U16_LE && input == AFMT_U16_BE) ||
+        (output == AFMT_S16_BE && input == AFMT_S16_LE) ||
+        (output == AFMT_S16_LE && input == AFMT_S16_BE))
+        return convert_swap_endian;
+
+    if ((output == AFMT_U16_BE && input == AFMT_S16_BE) ||
+        (output == AFMT_U16_LE && input == AFMT_S16_LE) ||
+        (output == AFMT_S16_BE && input == AFMT_U16_BE) ||
+        (output == AFMT_S16_LE && input == AFMT_U16_LE))
+        return convert_swap_sign16;
+
+    if ((IS_BIG_ENDIAN &&
+         ((output == AFMT_U16_BE && input == AFMT_S16_LE) ||
+          (output == AFMT_S16_BE && input == AFMT_U16_LE))) ||
+        (!IS_BIG_ENDIAN &&
+         ((output == AFMT_U16_LE && input == AFMT_S16_BE) ||
+          (output == AFMT_S16_LE && input == AFMT_U16_BE))))
+        return convert_swap_sign_and_endian_to_native;
+
+    if ((!IS_BIG_ENDIAN &&
+         ((output == AFMT_U16_BE && input == AFMT_S16_LE) ||
+          (output == AFMT_S16_BE && input == AFMT_U16_LE))) ||
+        (IS_BIG_ENDIAN &&
+         ((output == AFMT_U16_LE && input == AFMT_S16_BE) ||
+          (output == AFMT_S16_LE && input == AFMT_U16_BE))))
+        return convert_swap_sign_and_endian_to_alien;
+
+    if ((IS_BIG_ENDIAN &&
+         ((output == AFMT_U8 && input == AFMT_U16_BE) ||
+          (output == AFMT_S8 && input == AFMT_S16_BE))) ||
+        (!IS_BIG_ENDIAN &&
+         ((output == AFMT_U8 && input == AFMT_U16_LE) ||
+          (output == AFMT_S8 && input == AFMT_S16_LE))))
+        return convert_to_8_native_endian;
+
+    if ((IS_BIG_ENDIAN &&
+         ((output == AFMT_U8 && input == AFMT_S16_BE) ||
+          (output == AFMT_S8 && input == AFMT_U16_BE))) ||
+        (!IS_BIG_ENDIAN &&
+         ((output == AFMT_U8 && input == AFMT_S16_LE) ||
+          (output == AFMT_S8 && input == AFMT_U16_LE))))
+        return convert_to_8_native_endian_swap_sign;
+
+    if ((!IS_BIG_ENDIAN &&
+         ((output == AFMT_U8 && input == AFMT_U16_BE) ||
+          (output == AFMT_S8 && input == AFMT_S16_BE))) ||
+        (IS_BIG_ENDIAN &&
+         ((output == AFMT_U8 && input == AFMT_U16_LE) ||
+          (output == AFMT_S8 && input == AFMT_S16_LE))))
+        return convert_to_8_alien_endian;
+
+    if ((!IS_BIG_ENDIAN &&
+         ((output == AFMT_U8 && input == AFMT_S16_BE) ||
+          (output == AFMT_S8 && input == AFMT_U16_BE))) ||
+        (IS_BIG_ENDIAN &&
+         ((output == AFMT_U8 && input == AFMT_S16_LE) ||
+          (output == AFMT_S8 && input == AFMT_U16_LE))))
+        return convert_to_8_alien_endian_swap_sign;
+
+    if ((output == AFMT_U8 && input == AFMT_S8) ||
+        (output == AFMT_S8 && input == AFMT_U8))
+        return convert_swap_sign8;
+
+    if ((IS_BIG_ENDIAN &&
+         ((output == AFMT_U16_BE && input == AFMT_U8) ||
+          (output == AFMT_S16_BE && input == AFMT_S8))) ||
+        (!IS_BIG_ENDIAN &&
+         ((output == AFMT_U16_LE && input == AFMT_U8) ||
+          (output == AFMT_S16_LE && input == AFMT_S8))))
+        return convert_to_16_native_endian;
+
+    if ((IS_BIG_ENDIAN &&
+         ((output == AFMT_U16_BE && input == AFMT_S8) ||
+          (output == AFMT_S16_BE && input == AFMT_U8))) ||
+        (!IS_BIG_ENDIAN &&
+         ((output == AFMT_U16_LE && input == AFMT_S8) ||
+          (output == AFMT_S16_LE && input == AFMT_U8))))
+        return convert_to_16_native_endian_swap_sign;
+
+    if ((!IS_BIG_ENDIAN &&
+         ((output == AFMT_U16_BE && input == AFMT_U8) ||
+          (output == AFMT_S16_BE && input == AFMT_S8))) ||
+        (IS_BIG_ENDIAN &&
+         ((output == AFMT_U16_LE && input == AFMT_U8) ||
+          (output == AFMT_S16_LE && input == AFMT_S8))))
+        return convert_to_16_alien_endian;
+
+    if ((!IS_BIG_ENDIAN &&
+         ((output == AFMT_U16_BE && input == AFMT_S8) ||
+          (output == AFMT_S16_BE && input == AFMT_U8))) ||
+        (IS_BIG_ENDIAN &&
+         ((output == AFMT_U16_LE && input == AFMT_S8) ||
+          (output == AFMT_S16_LE && input == AFMT_U8))))
+        return convert_to_16_alien_endian_swap_sign;
+
+    g_warning("Translation needed, but not available.\n"
+              "Input: %d; Output %d.", input, output);
+    return NULL;
+}
+
+static int
+convert_mono_to_stereo(void **data, int length, int fmt)
+{
+    int i;
+    void *outbuf = oss_get_convert_buffer(&stereo_buffer, length * 2);
+
+    if (fmt == AFMT_U8 || fmt == AFMT_S8) {
+        guint8 *output = outbuf, *input = *data;
+        for (i = 0; i < length; i++) {
+            *output++ = *input;
+            *output++ = *input;
+            input++;
+        }
+    }
+    else {
+        guint16 *output = outbuf, *input = *data;
+        for (i = 0; i < length / 2; i++) {
+            *output++ = *input;
+            *output++ = *input;
+            input++;
+        }
+    }
+    *data = outbuf;
+
+    return length * 2;
+}
+
+static int
+convert_stereo_to_mono(void **data, int length, int fmt)
+{
+    int i;
+
+    switch (fmt) {
+    case AFMT_U8:
+        {
+            guint8 *output = *data, *input = *data;
+            for (i = 0; i < length / 2; i++) {
+                guint16 tmp;
+                tmp = *input++;
+                tmp += *input++;
+                *output++ = tmp / 2;
+            }
+        }
+        break;
+    case AFMT_S8:
+        {
+            gint8 *output = *data, *input = *data;
+            for (i = 0; i < length / 2; i++) {
+                gint16 tmp;
+                tmp = *input++;
+                tmp += *input++;
+                *output++ = tmp / 2;
+            }
+        }
+        break;
+    case AFMT_U16_LE:
+        {
+            guint16 *output = *data, *input = *data;
+            for (i = 0; i < length / 4; i++) {
+                guint32 tmp;
+                guint16 stmp;
+                tmp = GUINT16_FROM_LE(*input);
+                input++;
+                tmp += GUINT16_FROM_LE(*input);
+                input++;
+                stmp = tmp / 2;
+                *output++ = GUINT16_TO_LE(stmp);
+            }
+        }
+        break;
+    case AFMT_U16_BE:
+        {
+            guint16 *output = *data, *input = *data;
+            for (i = 0; i < length / 4; i++) {
+                guint32 tmp;
+                guint16 stmp;
+                tmp = GUINT16_FROM_BE(*input);
+                input++;
+                tmp += GUINT16_FROM_BE(*input);
+                input++;
+                stmp = tmp / 2;
+                *output++ = GUINT16_TO_BE(stmp);
+            }
+        }
+        break;
+    case AFMT_S16_LE:
+        {
+            gint16 *output = *data, *input = *data;
+            for (i = 0; i < length / 4; i++) {
+                gint32 tmp;
+                gint16 stmp;
+                tmp = GINT16_FROM_LE(*input);
+                input++;
+                tmp += GINT16_FROM_LE(*input);
+                input++;
+                stmp = tmp / 2;
+                *output++ = GINT16_TO_LE(stmp);
+            }
+        }
+        break;
+    case AFMT_S16_BE:
+        {
+            gint16 *output = *data, *input = *data;
+            for (i = 0; i < length / 4; i++) {
+                gint32 tmp;
+                gint16 stmp;
+                tmp = GINT16_FROM_BE(*input);
+                input++;
+                tmp += GINT16_FROM_BE(*input);
+                input++;
+                stmp = tmp / 2;
+                *output++ = GINT16_TO_BE(stmp);
+            }
+        }
+        break;
+    default:
+        g_error("unknown format");
+    }
+
+    return length / 2;
+}
+
+int (*oss_get_stereo_convert_func(int output, int input)) (void **, int, int) {
+    if (output == input)
+        return NULL;
+
+    if (input == 1 && output == 2)
+        return convert_mono_to_stereo;
+    if (input == 2 && output == 1)
+        return convert_stereo_to_mono;
+
+    g_warning("Input has %d channels, soundcard uses %d channels\n"
+              "No conversion is available", input, output);
+    return NULL;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Output/CoreAudio/init.c	Thu Sep 07 09:11:46 2006 -0700
@@ -0,0 +1,61 @@
+/*  BMP - Cross-platform multimedia player
+ *  Copyright (C) 2003-2004  BMP development team.
+ *
+ *  Based on XMMS:
+ *  Copyright (C) 1998-2003  XMMS development team.
+ *
+ *  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 <glib.h>
+#include <string.h>
+#include <libaudacious/configdb.h>
+#include "OSS.h"
+
+
+OSSConfig oss_cfg;
+
+void
+oss_init(void)
+{
+    ConfigDb *db;
+
+    memset(&oss_cfg, 0, sizeof(OSSConfig));
+
+    oss_cfg.audio_device = 0;
+    oss_cfg.mixer_device = 0;
+    oss_cfg.buffer_size = 3000;
+    oss_cfg.prebuffer = 25;
+    oss_cfg.use_alt_audio_device = FALSE;
+    oss_cfg.alt_audio_device = NULL;
+    oss_cfg.use_master = 0;
+
+    if ((db = bmp_cfg_db_open())) {
+        bmp_cfg_db_get_int(db, "OSS", "audio_device", &oss_cfg.audio_device);
+        bmp_cfg_db_get_int(db, "OSS", "mixer_device", &oss_cfg.mixer_device);
+        bmp_cfg_db_get_int(db, "OSS", "buffer_size", &oss_cfg.buffer_size);
+        bmp_cfg_db_get_int(db, "OSS", "prebuffer", &oss_cfg.prebuffer);
+        bmp_cfg_db_get_bool(db, "OSS", "use_master", &oss_cfg.use_master);
+        bmp_cfg_db_get_bool(db, "OSS", "use_alt_audio_device",
+                            &oss_cfg.use_alt_audio_device);
+        bmp_cfg_db_get_string(db, "OSS", "alt_audio_device",
+                              &oss_cfg.alt_audio_device);
+        bmp_cfg_db_get_bool(db, "OSS", "use_alt_mixer_device",
+                            &oss_cfg.use_alt_mixer_device);
+        bmp_cfg_db_get_string(db, "OSS", "alt_mixer_device",
+                              &oss_cfg.alt_mixer_device);
+        bmp_cfg_db_close(db);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Output/CoreAudio/mixer.c	Thu Sep 07 09:11:46 2006 -0700
@@ -0,0 +1,110 @@
+/*  BMP - Cross-platform multimedia player
+ *  Copyright (C) 2003-2004  BMP development team.
+ *
+ *  Based on XMMS:
+ *  Copyright (C) 1998-2003  XMMS development team.
+ *
+ *  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 <glib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+
+#include "OSS.h"
+
+
+static char *
+get_mixer_device(void)
+{
+    char *name;
+
+    if (oss_cfg.use_alt_mixer_device && oss_cfg.alt_mixer_device)
+        name = g_strdup(oss_cfg.alt_mixer_device);
+    else if (oss_cfg.mixer_device > 0)
+        name = g_strdup_printf("%s%d", DEV_MIXER, oss_cfg.mixer_device);
+    else
+        name = g_strdup(DEV_MIXER);
+
+    return name;
+}
+
+void
+oss_get_volume(int *l, int *r)
+{
+    int fd, v, devs;
+    long cmd;
+    gchar *devname;
+
+    devname = get_mixer_device();
+    fd = open(devname, O_RDONLY);
+    g_free(devname);
+
+    /*
+     * We dont show any errors if this fails, as this is called
+     * rather often
+     */
+    if (fd != -1) {
+        ioctl(fd, SOUND_MIXER_READ_DEVMASK, &devs);
+        if ((devs & SOUND_MASK_PCM) && (oss_cfg.use_master == 0))
+            cmd = SOUND_MIXER_READ_PCM;
+        else if ((devs & SOUND_MASK_VOLUME) && (oss_cfg.use_master == 1))
+            cmd = SOUND_MIXER_READ_VOLUME;
+        else {
+            close(fd);
+            return;
+        }
+        ioctl(fd, cmd, &v);
+        *r = (v & 0xFF00) >> 8;
+        *l = (v & 0x00FF);
+        close(fd);
+    }
+}
+
+void
+oss_set_volume(int l, int r)
+{
+    int fd, v, devs;
+    long cmd;
+    gchar *devname;
+
+    devname = get_mixer_device();
+    fd = open(devname, O_RDONLY);
+
+    if (fd != -1) {
+        ioctl(fd, SOUND_MIXER_READ_DEVMASK, &devs);
+        if ((devs & SOUND_MASK_PCM) && (oss_cfg.use_master == 0))
+            cmd = SOUND_MIXER_WRITE_PCM;
+        else if ((devs & SOUND_MASK_VOLUME) && (oss_cfg.use_master == 1))
+            cmd = SOUND_MIXER_WRITE_VOLUME;
+        else {
+            close(fd);
+            return;
+        }
+        v = (r << 8) | l;
+        ioctl(fd, cmd, &v);
+        close(fd);
+    }
+    else
+        g_warning("oss_set_volume(): Failed to open mixer device (%s): %s",
+                  devname, strerror(errno));
+    g_free(devname);
+}