changeset 758:f9e8807ea6e5 trunk

[svn] - Initial port of the bsd/sun audio output plugin from XMMS. Needs testing.
author nenolod
date Tue, 28 Feb 2006 11:32:33 -0800
parents 30fe36d312c8
children 9b1126434a72
files Plugins/Output/sun/Makefile.in Plugins/Output/sun/about.c Plugins/Output/sun/audio.c Plugins/Output/sun/audioio.h Plugins/Output/sun/configure.c Plugins/Output/sun/convert.c Plugins/Output/sun/mixer.c Plugins/Output/sun/mixer.h Plugins/Output/sun/resample.h Plugins/Output/sun/sun.c Plugins/Output/sun/sun.h configure.ac
diffstat 12 files changed, 2454 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Output/sun/Makefile.in	Tue Feb 28 11:32:33 2006 -0800
@@ -0,0 +1,17 @@
+include ../../../mk/rules.mk
+include ../../../mk/objective.mk
+
+sunsources =	sun.c	    \
+		audio.c	    \
+		convert.c   \
+		mixer.c	    \
+		configure.c \
+		about.c
+
+LIBDIR = $(plugindir)/$(OUTPUT_PLUGIN_DIR)
+OBJECTIVE_LIBS = libsun.so
+
+CFLAGS += -fPIC -DPIC $(GTK_CFLAGS) -I../../../intl -I../../..
+LIBADD = $(GTK_LIBS)
+SOURCES= $(sunsources)
+OBJECTS= ${SOURCES:.c=.o}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Output/sun/about.c	Tue Feb 28 11:32:33 2006 -0800
@@ -0,0 +1,44 @@
+/*
+ *  Copyright (C) 2001  CubeSoft Communications, Inc.
+ *  <http://www.csoft.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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "sun.h"
+#include "libaudacious/util.h"
+
+#include <glib.h>
+#include <glib/gi18n.h>
+
+void sun_about(void)
+{
+	static GtkWidget *dialog;
+
+	if (dialog != NULL)
+		return;
+
+	dialog = xmms_show_message(
+		_("About the Sun Driver"),
+		_("XMMS BSD Sun Driver\n\n"
+		  "Copyright (c) 2001 CubeSoft Communications, Inc.\n"
+		  "Maintainer: <vedge at csoft.org>.\n"),
+		_("Ok"), FALSE, NULL, NULL);
+
+	gtk_signal_connect(GTK_OBJECT(dialog), "destroy",
+			   GTK_SIGNAL_FUNC(gtk_widget_destroyed),
+			   &dialog);
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Output/sun/audio.c	Tue Feb 28 11:32:33 2006 -0800
@@ -0,0 +1,586 @@
+/*
+ *  Copyright (C) 2001  CubeSoft Communications, Inc.
+ *  <http://www.csoft.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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/* FIXME: g_error() is used several places, it will exit xmms */
+
+#include <errno.h>
+#include "libaudacious/util.h"
+#include "sun.h"
+#include "resample.h"
+
+static int sun_bps(int, int, int);
+static int sun_format(AFormat);
+
+static void sun_setformat(AFormat, int, int);
+static void sun_setparams(void);
+static void *sun_loop(void *);
+static int sun_downsample(gpointer, guint, guint, guint);
+
+static gboolean	prebuffer, remove_prebuffer;
+static pthread_t buffer_thread;
+static int (*sun_convert)(void **, int);
+static int realtime;
+static int rd_index, wr_index;
+static int buffer_size;
+static int prebuffer_size;
+static int output_time_offset;
+static int device_buffer_used;
+static int blocksize;
+static char *buffer;
+static guint64 output_bytes;
+static guint64 written;
+
+/*
+ * The format of the data from the input plugin
+ * This will never change during a song. 
+ */
+struct sun_format 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 sun_format 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 sun_format output;
+
+static int sun_bps(int sunfmt, int rate, int nch)
+{
+	int bitrate;
+
+	bitrate = rate * nch;
+
+	switch (sunfmt)
+	{
+		case AUDIO_ENCODING_ULINEAR_BE:
+		case AUDIO_ENCODING_ULINEAR_LE:
+		case AUDIO_ENCODING_SLINEAR_BE:
+		case AUDIO_ENCODING_SLINEAR_LE:
+			bitrate *= 2;
+			break;
+	}
+
+	return (bitrate);
+}
+
+static int sun_format(AFormat fmt)
+{
+	switch (fmt)
+	{
+		case FMT_U8:
+			return (AUDIO_ENCODING_PCM8);
+		case FMT_S8:
+			return (AUDIO_ENCODING_SLINEAR);
+		case FMT_U16_LE:
+			return (AUDIO_ENCODING_ULINEAR_LE);
+		case FMT_U16_BE:
+			return (AUDIO_ENCODING_ULINEAR_BE);
+		case FMT_U16_NE:
+#ifdef WORDS_BIGENDIAN
+			return (AUDIO_ENCODING_ULINEAR_BE);
+#else
+			return (AUDIO_ENCODING_ULINEAR_LE);
+#endif
+		case FMT_S16_LE:
+			return (AUDIO_ENCODING_SLINEAR_LE);
+		case FMT_S16_BE:
+			return (AUDIO_ENCODING_SLINEAR_BE);
+		case FMT_S16_NE:
+#ifdef WORDS_BIGENDIAN
+			return (AUDIO_ENCODING_SLINEAR_BE);
+#else
+			return (AUDIO_ENCODING_SLINEAR_LE);
+#endif
+	}
+	return -1;
+}
+
+static void sun_setformat(AFormat fmt, int rate, int nch)
+{
+	int sun;
+	
+	sun = sun_format(fmt);
+
+	effect.format.sun = sun;
+	effect.format.xmms = fmt;
+	effect.frequency = rate;
+	effect.channels = nch;
+	effect.bps = sun_bps(sun, rate, nch);
+
+	output.format.sun = sun;
+	output.format.xmms = fmt;
+	output.frequency = rate;
+	output.channels = nch;
+	sun_setparams();
+
+	output.bps = sun_bps(output.format.sun, output.frequency,
+			     output.channels);
+
+	audio.input = &input;
+	audio.output = &output;
+	audio.effect = &effect;
+}
+
+void sun_setparams(void)
+{
+	audio_info_t info;
+	audio_encoding_t enc;
+
+	AUDIO_INITINFO(&info);
+
+	info.mode = AUMODE_PLAY;
+	if (ioctl(audio.fd, AUDIO_SETINFO, &info) != 0)
+	{
+		g_error("%s: cannot play (%s)", audio.devaudio,
+			strerror(errno));
+		return;
+	}
+
+	/*
+	 * Pass 1: try the preferred encoding, if it is supported.
+	 */
+	enc.index = 0;
+	while (ioctl(audio.fd, AUDIO_GETENC, &enc) == 0 &&
+	       enc.encoding != output.format.sun)
+		enc.index++;
+
+	info.play.encoding = enc.encoding;
+	info.play.precision = enc.precision;
+	strcpy(output.name, enc.name);
+	if (ioctl(audio.fd, AUDIO_SETINFO, &info) != 0)
+	{
+		g_error("%s: unsupported encoding: %s (%s)", audio.devaudio,
+			output.name, strerror(errno));
+		return;
+	}
+
+	info.play.channels = output.channels;
+	ioctl(audio.fd, AUDIO_SETINFO, &info);
+
+	info.play.sample_rate = output.frequency;
+	if (ioctl(audio.fd, AUDIO_SETINFO, &info) < 0)
+	{
+		g_error("%s: cannot handle %i Hz (%s)", audio.devaudio,
+			output.frequency, strerror(errno));
+		return;
+	}
+
+	if (ioctl(audio.fd, AUDIO_GETINFO, &info) != 0)
+	{
+		blocksize = SUN_DEFAULT_BLOCKSIZE;
+		output.channels = info.play.channels;
+	}
+	else
+	{
+		blocksize = blocksize;
+	}
+
+	sun_convert = sun_get_convert_func(output.format.sun,
+					   sun_format(effect.format.xmms));
+#if 0
+	if (sun_convert != NULL)
+	{
+		g_warning("audio conversion (output=0x%x effect=0x%x)",
+		    output.format.sun, sun_format(effect.format.xmms));
+	}
+#endif
+}
+
+static inline void sun_bufused(void)
+{
+	audio_offset_t ooffs;
+
+	if (audio.paused)
+		device_buffer_used = 0;
+	else if (ioctl(audio.fd, AUDIO_GETOOFFS, &ooffs) == 0)
+		device_buffer_used = ooffs.offset;
+}
+
+int sun_written_time(void)
+{
+	if (!audio.going)
+		return 0;
+
+	return ((written * 1000) / effect.bps);
+}
+
+int sun_output_time(void)
+{
+	guint64 bytes;
+
+	if (!audio.fd || !audio.going)
+		return 0;
+
+	if (realtime)
+		sun_bufused();
+
+	bytes = output_bytes < device_buffer_used ?
+		0 : output_bytes - device_buffer_used;
+	return (output_time_offset + ((bytes * 1000) / output.bps));
+}
+
+static inline int sun_used(void)
+{
+	if (realtime)
+		return 0;
+	
+	if (wr_index >= rd_index)
+		return (wr_index - rd_index);
+
+	return (buffer_size - (rd_index - wr_index));
+}
+
+int sun_playing(void)
+{
+	if (!audio.going)
+		return 0;
+
+	if (realtime)
+		sun_bufused();
+
+	if (!sun_used() && (device_buffer_used - (3 * blocksize)) <= 0)
+		return (FALSE);
+
+	return (TRUE);
+}
+
+int sun_free(void)
+{
+	if (realtime)
+		return (audio.paused ? 0 : 1000000);
+	
+	if (remove_prebuffer && prebuffer)
+	{
+		prebuffer = FALSE;
+		remove_prebuffer = FALSE;
+	}
+	if (prebuffer)
+		remove_prebuffer = TRUE;
+
+	if (rd_index > wr_index)
+		return ((rd_index - wr_index) - blocksize - 1);
+
+	return ((buffer_size - (wr_index - rd_index)) - blocksize - 1);
+}
+
+static inline ssize_t write_all(int fd, const void *buf, size_t count)
+{
+	static ssize_t done;
+
+	for (done = 0; count > done; )
+	{
+		static ssize_t n;
+
+		n = write(fd, buf, count - done);
+		if (n == -1)
+		{
+			if (errno == EINTR)
+				continue;
+			else
+				break;
+		}
+		done += n;
+	}
+
+	return (done);
+}
+
+static inline void sun_write_audio(gpointer data, int length)
+{
+	AFormat new_format;
+	EffectPlugin *ep;
+	int new_frequency, new_channels;
+
+	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(audio.fd);
+		audio.fd = open(audio.devaudio, O_RDWR);
+		sun_setformat(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 (sun_convert != NULL)
+		length = sun_convert(&data, length);
+
+	if (effect.frequency == output.frequency)
+	{
+		output_bytes += write_all(audio.fd, data, length);
+	}
+	else
+	{
+		output_bytes += sun_downsample(data, length,
+		    effect.frequency, output.frequency);
+	}
+}
+
+static void sun_bswap16(guint16 *data, int len)
+{
+	int i;
+
+	for (i = 0; i < len; i += 2, data++)
+		*data = GUINT16_SWAP_LE_BE(*data);
+}
+
+static int sun_downsample(gpointer ob, guint length, guint speed, guint espeed)
+{
+	guint w = 0;
+	static gpointer nbuffer = NULL;
+	static int nbuffer_size = 0;
+
+	switch (output.format.sun) {
+		case AUDIO_ENCODING_SLINEAR_BE:
+		case AUDIO_ENCODING_SLINEAR_LE:
+			if (output.channels == 2)
+				RESAMPLE_STEREO(gint16);
+			else
+				RESAMPLE_MONO(gint16);
+			break;
+		case AUDIO_ENCODING_ULINEAR_BE:
+		case AUDIO_ENCODING_ULINEAR_LE:
+			if (output.channels == 2)
+				RESAMPLE_STEREO(guint16);
+			else
+				RESAMPLE_MONO(guint16);
+			break;
+		case AUDIO_ENCODING_SLINEAR:
+			if (output.channels == 2)
+				RESAMPLE_STEREO(gint8);
+			else
+				RESAMPLE_MONO(gint8);
+			break;
+		case AUDIO_ENCODING_ULINEAR:
+			if (output.channels == 2)
+				RESAMPLE_STEREO(guint8);
+			else
+				RESAMPLE_MONO(guint8);
+			break;
+	}
+	return (w);
+}
+
+void sun_write(gpointer ptr, int length)
+{
+	int cnt, off = 0;
+
+	if (realtime)
+	{
+		if (audio.paused)
+			return;
+		sun_write_audio(ptr, length);
+		written += length;
+		return;
+	}
+
+	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;
+	}
+}
+
+void sun_close(void)
+{
+	if (!audio.going)
+		return;
+
+	audio.going = 0;
+
+	if (realtime)
+	{
+		ioctl(audio.fd, AUDIO_FLUSH, NULL);
+		close(audio.fd);
+	}
+	else
+	{
+		pthread_join(buffer_thread, NULL);
+	}
+
+	sun_get_convert_buffer(0);
+	wr_index = 0;
+	rd_index = 0;
+}
+
+void sun_flush(int time)
+{
+	ioctl(audio.fd, AUDIO_FLUSH, NULL);
+
+	output_time_offset = time;
+	written = (guint16)(time / 10) * (guint64)(input.bps / 100);
+	output_bytes = 0;
+}
+
+void sun_pause(short p)
+{
+	if (!realtime)
+	{
+		if (p == TRUE)
+			audio.do_pause = TRUE;
+		else
+			audio.unpause = TRUE;
+	}
+	else
+		audio.paused = p;
+}
+
+static void* sun_loop(void *arg)
+{
+	struct timeval tv;
+	int length, cnt;
+	fd_set set;
+
+	while (audio.going)
+	{
+		if (sun_used() > prebuffer_size)
+			prebuffer = FALSE;
+
+		if (sun_used() > 0 && !audio.paused && !prebuffer)
+		{
+			tv.tv_sec = 0;
+			tv.tv_usec = 10000;
+			FD_ZERO(&set);
+			FD_SET(audio.fd, &set);
+
+			if (select(audio.fd + 1, NULL, &set, NULL, &tv) > 0)
+			{
+				length = MIN(blocksize, sun_used());
+				while (length > 0)
+				{
+					cnt = MIN(length,
+					    buffer_size - rd_index);
+					sun_write_audio(
+					    buffer + rd_index, cnt);
+					rd_index = (rd_index + cnt) %
+					    buffer_size;
+					length -= cnt;
+				}
+			}
+		}
+		else
+			xmms_usleep(10000);
+
+		sun_bufused();
+
+		if (audio.do_pause && !audio.paused)
+		{
+			audio.do_pause = FALSE;
+			audio.paused = TRUE;
+
+			rd_index -= device_buffer_used;
+			output_bytes -= device_buffer_used;
+			if (rd_index < 0)
+				rd_index += buffer_size;
+			ioctl(audio.fd, AUDIO_FLUSH, NULL);
+		}
+		else if (audio.unpause && audio.paused)
+		{
+			audio.unpause = FALSE;
+			close(audio.fd);
+			audio.fd = open(audio.devaudio, O_RDWR);
+			sun_setparams();
+			audio.paused = FALSE;
+		}
+	}
+
+	close(audio.fd);
+	g_free(buffer);
+	pthread_exit(NULL);
+}
+
+int sun_open(AFormat fmt, int rate, int nch)
+{
+	audio_info_t info;
+
+	AUDIO_INITINFO(&info);
+
+	if ((audio.fd = open(audio.devaudio, O_RDWR)) < 0)
+	{
+		g_error("%s: %s", audio.devaudio, strerror(errno));
+		return 0;
+	}
+
+	input.format.xmms = fmt;
+	input.frequency = rate;
+	input.channels = nch;
+	input.bps = sun_bps(sun_format(fmt), rate, nch);
+	sun_setformat(fmt, rate, nch);
+
+	realtime = xmms_check_realtime_priority();
+
+	if (ioctl(audio.fd, AUDIO_GETINFO, &info) != 0)
+		blocksize = SUN_DEFAULT_BLOCKSIZE;
+	else
+		blocksize = info.blocksize;
+
+	if (!realtime)
+	{
+		buffer_size = audio.req_buffer_size;
+
+		if (buffer_size < SUN_MIN_BUFFER_SIZE)
+			buffer_size = SUN_MIN_BUFFER_SIZE;
+
+		prebuffer_size = (buffer_size * audio.req_prebuffer_size) / 100;
+
+		buffer_size += blocksize;
+		buffer = g_malloc0(buffer_size);
+	}
+	prebuffer = TRUE;
+	wr_index = 0;
+	rd_index = 0;
+	output_time_offset = 0;
+	written = 0;
+	output_bytes = 0;
+
+	audio.paused = FALSE;
+	audio.do_pause = FALSE;
+	audio.unpause = FALSE;
+	remove_prebuffer = FALSE;
+
+	audio.going++;
+	if (!realtime)
+		pthread_create(&buffer_thread, NULL, sun_loop, NULL);
+
+	return 1;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Output/sun/audioio.h	Tue Feb 28 11:32:33 2006 -0800
@@ -0,0 +1,326 @@
+/*	$NetBSD: audioio.h,v 1.29 2005/12/11 12:25:20 christos Exp $	*/
+
+/*
+ * Copyright (c) 1991-1993 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *	This product includes software developed by the Computer Systems
+ *	Engineering Group at Lawrence Berkeley Laboratory.
+ * 4. Neither the name of the University nor of the Laboratory may be used
+ *    to endorse or promote products derived from this software without
+ *    specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#ifndef _SYS_AUDIOIO_H_
+#define _SYS_AUDIOIO_H_
+
+/*
+ * Audio device
+ */
+struct audio_prinfo {
+	u_int	sample_rate;	/* sample rate in bit/s */
+	u_int	channels;	/* number of channels, usually 1 or 2 */
+	u_int	precision;	/* number of bits/sample */
+	u_int	encoding;	/* data encoding (AUDIO_ENCODING_* below) */
+	u_int	gain;		/* volume level */
+	u_int	port;		/* selected I/O port */
+	u_int	seek;		/* BSD extension */
+	u_int	avail_ports;	/* available I/O ports */
+	u_int	buffer_size;	/* total size audio buffer */
+	u_int	_ispare[1];
+	/* Current state of device: */
+	u_int	samples;	/* number of samples */
+	u_int	eof;		/* End Of File (zero-size writes) counter */
+	u_char	pause;		/* non-zero if paused, zero to resume */
+	u_char	error;		/* non-zero if underflow/overflow ocurred */
+	u_char	waiting;	/* non-zero if another process hangs in open */
+	u_char	balance;	/* stereo channel balance */
+	u_char	cspare[2];
+	u_char	open;		/* non-zero if currently open */
+	u_char	active;		/* non-zero if I/O is currently active */
+};
+typedef struct audio_prinfo audio_prinfo_t;
+
+struct audio_info {
+	struct	audio_prinfo play;	/* Info for play (output) side */
+	struct	audio_prinfo record;	/* Info for record (input) side */
+
+	u_int	monitor_gain;	/* input to output mix */
+	/* BSD extensions */
+	u_int	blocksize;	/* H/W read/write block size */
+	u_int	hiwat;		/* output high water mark */
+	u_int	lowat;		/* output low water mark */
+	u_int	_ispare1;
+	u_int	mode;		/* current device mode */
+#define AUMODE_PLAY	0x01
+#define AUMODE_RECORD	0x02
+#define AUMODE_PLAY_ALL	0x04	/* don't do real-time correction */
+};
+typedef struct audio_info audio_info_t;
+
+#define AUDIO_INITINFO(p) \
+	(void)memset((void *)(p), 0xff, sizeof(struct audio_info))
+
+/*
+ * Parameter for the AUDIO_GETDEV ioctl to determine current
+ * audio devices.
+ */
+#define MAX_AUDIO_DEV_LEN       16
+typedef struct audio_device {
+        char name[MAX_AUDIO_DEV_LEN];
+        char version[MAX_AUDIO_DEV_LEN];
+        char config[MAX_AUDIO_DEV_LEN];
+} audio_device_t;
+
+typedef struct audio_offset {
+	u_int	samples;	/* Total number of bytes transferred */
+	u_int	deltablks;	/* Blocks transferred since last checked */
+	u_int	offset;		/* Physical transfer offset in buffer */
+} audio_offset_t;
+
+/*
+ * Supported audio encodings
+ */
+/* Encoding ID's */
+#define	AUDIO_ENCODING_NONE		0 /* no encoding assigned */
+#define	AUDIO_ENCODING_ULAW		1 /* ITU G.711 mu-law */
+#define	AUDIO_ENCODING_ALAW		2 /* ITU G.711 A-law */
+#define	AUDIO_ENCODING_PCM16		3 /* signed linear PCM, obsolete */
+#define AUDIO_ENCODING_LINEAR		AUDIO_ENCODING_PCM16 /* SunOS compat */
+#define	AUDIO_ENCODING_PCM8		4 /* unsigned linear PCM, obsolete */
+#define AUDIO_ENCODING_LINEAR8		AUDIO_ENCODING_PCM8 /* SunOS compat */
+#define	AUDIO_ENCODING_ADPCM		5 /* adaptive differential PCM */
+#define AUDIO_ENCODING_SLINEAR_LE	6
+#define AUDIO_ENCODING_SLINEAR_BE	7
+#define AUDIO_ENCODING_ULINEAR_LE	8
+#define AUDIO_ENCODING_ULINEAR_BE	9
+#define AUDIO_ENCODING_SLINEAR		10
+#define AUDIO_ENCODING_ULINEAR		11
+#define AUDIO_ENCODING_MPEG_L1_STREAM	12
+#define AUDIO_ENCODING_MPEG_L1_PACKETS	13
+#define AUDIO_ENCODING_MPEG_L1_SYSTEM	14
+#define AUDIO_ENCODING_MPEG_L2_STREAM	15
+#define AUDIO_ENCODING_MPEG_L2_PACKETS	16
+#define AUDIO_ENCODING_MPEG_L2_SYSTEM	17
+
+typedef struct audio_encoding {
+	int	index;
+	char	name[MAX_AUDIO_DEV_LEN];
+	int	encoding;
+	int	precision;
+	int	flags;
+#define AUDIO_ENCODINGFLAG_EMULATED 1 /* software emulation mode */
+} audio_encoding_t;
+
+/*
+ * Balance settings.
+ */
+#define	AUDIO_LEFT_BALANCE	0	/* left channel only	*/
+#define	AUDIO_MID_BALANCE	32	/* equal left/right channel */
+#define	AUDIO_RIGHT_BALANCE	64	/* right channel only	*/
+#define	AUDIO_BALANCE_SHIFT	3
+
+/*
+ * Output ports
+ */
+#define	AUDIO_SPEAKER		0x01	/* built-in speaker */
+#define	AUDIO_HEADPHONE		0x02	/* headphone jack */
+#define	AUDIO_LINE_OUT		0x04	/* line out	 */
+
+/*
+ * Input ports
+ */
+#define	AUDIO_MICROPHONE	0x01	/* microphone */
+#define	AUDIO_LINE_IN		0x02	/* line in	 */
+#define	AUDIO_CD		0x04	/* on-board CD inputs */
+#define	AUDIO_INTERNAL_CD_IN	AUDIO_CD	/* internal CDROM */
+
+/*
+ * Audio device operations
+ */
+#define AUDIO_GETINFO	_IOR('A', 21, struct audio_info)
+#define AUDIO_SETINFO	_IOWR('A', 22, struct audio_info)
+#define AUDIO_DRAIN	_IO('A', 23)
+#define AUDIO_FLUSH	_IO('A', 24)
+#define AUDIO_WSEEK	_IOR('A', 25, u_long)
+#define AUDIO_RERROR	_IOR('A', 26, int)
+#define AUDIO_GETDEV	_IOR('A', 27, struct audio_device)
+#define AUDIO_GETENC	_IOWR('A', 28, struct audio_encoding)
+#define AUDIO_GETFD	_IOR('A', 29, int)
+#define AUDIO_SETFD	_IOWR('A', 30, int)
+#define AUDIO_PERROR	_IOR('A', 31, int)
+#define AUDIO_GETIOFFS	_IOR('A', 32, struct audio_offset)
+#define AUDIO_GETOOFFS	_IOR('A', 33, struct audio_offset)
+#define AUDIO_GETPROPS	_IOR('A', 34, int)
+#define  AUDIO_PROP_FULLDUPLEX	0x01
+#define  AUDIO_PROP_MMAP	0x02
+#define  AUDIO_PROP_INDEPENDENT	0x04
+
+/*
+ * Mixer device
+ */
+#define AUDIO_MIN_GAIN	0
+#define AUDIO_MAX_GAIN	255
+
+typedef struct mixer_level {
+	int num_channels;
+	u_char level[8];	/* [num_channels] */
+} mixer_level_t;
+#define AUDIO_MIXER_LEVEL_MONO	0
+#define AUDIO_MIXER_LEVEL_LEFT	0
+#define AUDIO_MIXER_LEVEL_RIGHT	1
+
+/*
+ * Device operations
+ */
+
+typedef struct audio_mixer_name {
+	char name[MAX_AUDIO_DEV_LEN];
+	int msg_id;
+} audio_mixer_name_t;
+
+typedef struct mixer_devinfo {
+	int index;
+	audio_mixer_name_t label;
+	int type;
+#define AUDIO_MIXER_CLASS	0
+#define AUDIO_MIXER_ENUM	1
+#define AUDIO_MIXER_SET		2
+#define AUDIO_MIXER_VALUE	3
+	int mixer_class;
+	int next, prev;
+#define AUDIO_MIXER_LAST	-1
+	union {
+		struct audio_mixer_enum {
+			int num_mem;
+			struct {
+				audio_mixer_name_t label;
+				int ord;
+			} member[32];
+		} e;
+		struct audio_mixer_set {
+			int num_mem;
+			struct {
+				audio_mixer_name_t label;
+				int mask;
+			} member[32];
+		} s;
+		struct audio_mixer_value {
+			audio_mixer_name_t units;
+			int num_channels;
+			int delta;
+		} v;
+	} un;
+} mixer_devinfo_t;
+
+
+typedef struct mixer_ctrl {
+	int dev;
+	int type;
+	union {
+		int ord;		/* enum */
+		int mask;		/* set */
+		mixer_level_t value;	/* value */
+	} un;
+} mixer_ctrl_t;
+
+/*
+ * Mixer operations
+ */
+#define AUDIO_MIXER_READ		_IOWR('M', 0, mixer_ctrl_t)
+#define AUDIO_MIXER_WRITE		_IOWR('M', 1, mixer_ctrl_t)
+#define AUDIO_MIXER_DEVINFO		_IOWR('M', 2, mixer_devinfo_t)
+
+/*
+ * Well known device names
+ */
+#define AudioNmicrophone	"mic"
+#define AudioNline	"line"
+#define AudioNcd	"cd"
+#define AudioNdac	"dac"
+#define AudioNaux	"aux"
+#define AudioNrecord	"record"
+#define AudioNvolume	"volume"
+#define AudioNmonitor	"monitor"
+#define AudioNtreble	"treble"
+#define AudioNmid	"mid"
+#define AudioNbass	"bass"
+#define AudioNbassboost	"bassboost"
+#define AudioNspeaker	"speaker"
+#define AudioNheadphone	"headphones"
+#define AudioNoutput	"output"
+#define AudioNinput	"input"
+#define AudioNmaster	"master"
+#define AudioNstereo	"stereo"
+#define AudioNmono	"mono"
+#define AudioNloudness	"loudness"
+#define AudioNspatial	"spatial"
+#define AudioNsurround	"surround"
+#define AudioNpseudo	"pseudo"
+#define AudioNmute	"mute"
+#define AudioNenhanced	"enhanced"
+#define AudioNpreamp	"preamp"
+#define AudioNon	"on"
+#define AudioNoff	"off"
+#define AudioNmode	"mode"
+#define AudioNsource	"source"
+#define AudioNfmsynth	"fmsynth"
+#define AudioNwave	"wave"
+#define AudioNmidi	"midi"
+#define AudioNmixerout	"mixerout"
+#define AudioNswap	"swap"	/* swap left and right channels */
+#define AudioNagc	"agc"
+#define AudioNdelay	"delay"
+#define AudioNselect	"select" /* select destination */
+#define AudioNvideo     "video"
+#define AudioNcenter    "center"
+#define AudioNdepth     "depth"
+#define AudioNlfe       "lfe"
+
+#define AudioEmulaw		"mulaw"
+#define AudioEalaw		"alaw"
+#define AudioEadpcm		"adpcm"
+#define AudioEslinear		"slinear"
+#define AudioEslinear_le	"slinear_le"
+#define AudioEslinear_be	"slinear_be"
+#define AudioEulinear		"ulinear"
+#define AudioEulinear_le	"ulinear_le"
+#define AudioEulinear_be	"ulinear_be"
+#define AudioEmpeg_l1_stream	"mpeg_l1_stream"
+#define AudioEmpeg_l1_packets	"mpeg_l1_packets"
+#define AudioEmpeg_l1_system	"mpeg_l1_system"
+#define AudioEmpeg_l2_stream	"mpeg_l2_stream"
+#define AudioEmpeg_l2_packets	"mpeg_l2_packets"
+#define AudioEmpeg_l2_system	"mpeg_l2_system"
+
+#define AudioCinputs	"inputs"
+#define AudioCoutputs	"outputs"
+#define AudioCrecord	"record"
+#define AudioCmonitor	"monitor"
+#define AudioCequalization	"equalization"
+#define AudioCmodem	"modem"
+
+#endif /* !_SYS_AUDIOIO_H_ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Output/sun/configure.c	Tue Feb 28 11:32:33 2006 -0800
@@ -0,0 +1,577 @@
+/*
+ *  Copyright (C) 2001  CubeSoft Communications, Inc.
+ *  <http://www.csoft.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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <errno.h>
+#include "sun.h"
+#include "libaudacious/util.h"
+#include "libaudacious/configdb.h"
+
+#include <glib.h>
+#include <glib/gi18n.h>
+
+#include "mixer.h"
+
+struct sun_statsframe stats_frame;
+
+static GtkWidget *configure_win;
+static GtkWidget *buffer_size_spin, *buffer_pre_spin;
+static GtkWidget *adevice_entry, *actldevice_entry, *mdevice_entry;
+static GtkWidget *keepopen_cbutton;
+static char devaudio[64], devaudioctl[64], devmixer[64], mixer_toggle[64];
+
+static void configure_win_destroy();
+
+static void configure_win_ok_cb(GtkWidget *w, gpointer data)
+{
+	ConfigFile *cfgfile;
+
+	strcpy(audio.devaudio, gtk_entry_get_text(GTK_ENTRY(adevice_entry)));
+	strcpy(audio.devmixer, gtk_entry_get_text(GTK_ENTRY(mdevice_entry)));
+
+	audio.req_buffer_size = gtk_spin_button_get_value_as_int(
+		GTK_SPIN_BUTTON(buffer_size_spin));
+	audio.req_prebuffer_size = gtk_spin_button_get_value_as_int(
+		GTK_SPIN_BUTTON(buffer_pre_spin));
+
+	if (sun_mixer_open() == 0)
+	{
+		audio.mixer_keepopen = gtk_toggle_button_get_active(
+			GTK_TOGGLE_BUTTON(keepopen_cbutton));
+		sun_mixer_close();
+	}
+
+	cfgfile = xmms_cfg_open_default_file();
+
+	xmms_cfg_write_string(cfgfile, "sun",
+			      "audio_devaudio", audio.devaudio);
+	xmms_cfg_write_string(cfgfile, "sun",
+			      "audio_devaudioctl", audio.devaudioctl);
+	xmms_cfg_write_string(cfgfile, "sun",
+			      "audio_devmixer", audio.devmixer);
+
+	xmms_cfg_write_string(cfgfile, "sun",
+			      "mixer_voldev", audio.mixer_voldev);
+	xmms_cfg_write_boolean(cfgfile, "sun",
+			       "mixer_keepopen", audio.mixer_keepopen);
+
+	xmms_cfg_write_int(cfgfile, "sun",
+			   "buffer_size", audio.req_buffer_size);
+	xmms_cfg_write_int(cfgfile, "sun",
+			   "prebuffer_size", audio.req_prebuffer_size);
+
+	xmms_cfg_write_default_file(cfgfile);
+	xmms_cfg_free(cfgfile);
+
+	configure_win_destroy();
+}
+
+static void configure_win_cancel_cb(GtkWidget *w, gpointer data)
+{
+	configure_win_destroy();
+}
+
+static void mixer_cbutton_toggled_cb(GtkWidget *w, int id)
+{
+	mixer_ctrl_t mixer;
+
+	if (sun_mixer_open() == 0)
+	{
+		mixer.type = AUDIO_MIXER_ENUM;
+		mixer.dev = id;
+		mixer_toggle[id] = !mixer_toggle[id];
+		mixer.un.ord = mixer_toggle[id];
+
+		if (ioctl(audio.mixerfd, AUDIO_MIXER_WRITE, &mixer) != 0)
+			g_warning("Could not toggle mixer setting %i", id);
+		sun_mixer_close();
+	}
+}
+
+static void configure_win_mixer_volume_dev_cb(GtkWidget *w, gint voldev_index)
+{
+	mixer_devinfo_t info;
+
+	if (sun_mixer_open() == 0)
+	{
+		info.index = voldev_index;
+		if (!ioctl(audio.mixerfd, AUDIO_MIXER_DEVINFO, &info))
+			strcpy(audio.mixer_voldev, info.label.name);
+		sun_mixer_close();
+	}
+}
+
+static void configure_win_destroy(void)
+{
+	stats_frame.active = 0;
+
+	if (!pthread_mutex_lock(&stats_frame.active_mutex))
+	{
+		if (!pthread_mutex_lock(&stats_frame.audioctl_mutex))
+		{
+			if (stats_frame.fd)
+			{
+				close(stats_frame.fd);
+				stats_frame.fd = 0;
+			}
+			pthread_mutex_unlock(&stats_frame.audioctl_mutex);
+			pthread_mutex_destroy(&stats_frame.audioctl_mutex);
+		}
+		pthread_mutex_unlock(&stats_frame.active_mutex);
+		pthread_mutex_destroy(&stats_frame.active_mutex);
+	}
+	gtk_widget_destroy(configure_win);
+	configure_win = NULL;
+}
+
+static void configure_mixer_volumedev_scan(gchar *type, GtkWidget *option_menu)
+{
+	mixer_devinfo_t info;
+	GtkWidget *menu;
+
+	if (sun_mixer_open() < 0)
+		return;
+
+	menu = gtk_menu_new();
+
+	/* FIXME: info is used while undefined here */
+	for (info.index = 0;
+	     ioctl(audio.mixerfd, AUDIO_MIXER_DEVINFO, &info) == 0;
+	     info.index++)
+	{
+		GtkWidget *item;
+
+		if (info.type == AUDIO_MIXER_VALUE)
+		{
+			item = gtk_menu_item_new_with_label(info.label.name);
+			gtk_signal_connect(GTK_OBJECT(item), "activate",
+					   (GCallback) configure_win_mixer_volume_dev_cb,
+					   (gpointer) info.index);
+
+			gtk_widget_show(item);
+			gtk_menu_append(GTK_MENU(menu), item);
+
+			if (!strcmp(info.label.name, audio.mixer_voldev))
+				gtk_menu_reorder_child(GTK_MENU(menu), item, 0);
+		}
+	}
+
+	gtk_option_menu_set_menu(GTK_OPTION_MENU(option_menu), menu);
+
+	sun_mixer_close();
+}
+
+static void configure_adevice_box(GtkWidget *dev_vbox)
+{
+	GtkWidget *adevice_frame, *adevice_vbox;
+
+	adevice_frame = gtk_frame_new(_("Audio device:"));
+	gtk_box_pack_start(GTK_BOX(dev_vbox), adevice_frame, FALSE, FALSE, 0);
+
+	adevice_vbox = gtk_vbox_new(FALSE, 5);
+	gtk_container_set_border_width(GTK_CONTAINER(adevice_vbox), 5);
+	gtk_container_add(GTK_CONTAINER(adevice_frame), adevice_vbox);
+
+	strcpy(devaudio, audio.devaudio);
+
+	adevice_entry = gtk_entry_new();
+	gtk_entry_set_text(GTK_ENTRY(adevice_entry), devaudio);
+	gtk_box_pack_start_defaults(GTK_BOX(adevice_vbox), adevice_entry);
+}
+
+static void configure_actldevice_box(GtkWidget *dev_vbox)
+{
+	GtkWidget *actldevice_frame, *actldevice_vbox;
+
+	actldevice_frame = gtk_frame_new(_("Audio control device:"));
+	gtk_box_pack_start(GTK_BOX(dev_vbox), actldevice_frame,
+			   FALSE, FALSE, 0);
+
+	actldevice_vbox = gtk_vbox_new(FALSE, 5);
+	gtk_container_set_border_width(GTK_CONTAINER(actldevice_vbox), 5);
+	gtk_container_add(GTK_CONTAINER(actldevice_frame), actldevice_vbox);
+
+	strcpy(devaudioctl, audio.devaudioctl);
+
+	actldevice_entry = gtk_entry_new();
+	gtk_entry_set_text(GTK_ENTRY(actldevice_entry), devaudioctl);
+	gtk_box_pack_start_defaults(GTK_BOX(actldevice_vbox), actldevice_entry);
+}
+
+static void configure_mdevice_box(GtkWidget *dev_vbox)
+{
+	GtkWidget *mdevice_frame, *mdevice_vbox;
+
+	mdevice_frame = gtk_frame_new(_("Mixer device:"));
+	gtk_box_pack_start(GTK_BOX(dev_vbox), mdevice_frame, FALSE, FALSE, 0);
+
+	mdevice_vbox = gtk_vbox_new(FALSE, 0);
+	gtk_container_set_border_width(GTK_CONTAINER(mdevice_vbox), 5);
+	gtk_container_add(GTK_CONTAINER(mdevice_frame), mdevice_vbox);
+
+	strcpy(devmixer, audio.devmixer);
+
+	mdevice_entry = gtk_entry_new();
+	gtk_entry_set_text(GTK_ENTRY(mdevice_entry), devmixer);
+	gtk_box_pack_start_defaults(GTK_BOX(mdevice_vbox), mdevice_entry);
+
+}
+
+
+static void configure_devices_frame(GtkWidget *vbox, GtkWidget * notebook)
+{
+	GtkWidget *dev_vbox;
+
+	dev_vbox = gtk_vbox_new(FALSE, 5);
+	gtk_container_set_border_width(GTK_CONTAINER(dev_vbox), 5);
+
+	configure_adevice_box(dev_vbox);
+	configure_actldevice_box(dev_vbox);
+	configure_mdevice_box(dev_vbox);
+
+	gtk_notebook_append_page(GTK_NOTEBOOK(notebook), dev_vbox,
+				 gtk_label_new(_("Devices")));
+}
+
+
+static void configure_buffering_frame(GtkWidget *vbox, GtkWidget * notebook)
+{
+	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;
+
+	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(audio.req_buffer_size,
+					     200, 131072, 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(audio.req_prebuffer_size,
+					    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")));
+}
+
+static void configure_mixer_toggle_button(GtkWidget *vbox, gchar *devname, gchar *label)
+{
+	GtkWidget *toggle_cbutton;
+	gint devid;
+	mixer_ctrl_t mixer;
+
+	if (!sun_mixer_get_dev(audio.mixerfd, &devid, devname))
+	{
+		mixer.type = AUDIO_MIXER_ENUM;
+		mixer.dev = devid;
+
+		if (!ioctl(audio.mixerfd, AUDIO_MIXER_READ, &mixer))
+		{
+			toggle_cbutton =
+				gtk_check_button_new_with_label(_(label));
+			gtk_box_pack_start_defaults(GTK_BOX(vbox),
+						    toggle_cbutton);
+
+			if (mixer.un.ord)
+			{
+				gtk_toggle_button_set_active(
+				    GTK_TOGGLE_BUTTON(toggle_cbutton), TRUE);
+				mixer_toggle[mixer.dev]++;
+			}
+			else
+			{
+				mixer_toggle[mixer.dev] = 0;
+			}
+
+			gtk_signal_connect(GTK_OBJECT(toggle_cbutton),
+			    "toggled",
+			    GTK_SIGNAL_FUNC(mixer_cbutton_toggled_cb),
+			    (gpointer) mixer.dev);
+		}
+	}
+}
+
+
+static void configure_mixer_box(GtkWidget *vbox, GtkWidget *notebook)
+{
+	GtkWidget *mixervol_frame, *mixervol_box;
+	GtkWidget *mixervol_menu;
+
+	mixervol_frame = gtk_frame_new(_("Volume controls device:"));
+	gtk_box_pack_start(GTK_BOX(vbox), mixervol_frame, FALSE, FALSE, 0);
+
+	mixervol_box = gtk_vbox_new(FALSE, 0);
+	gtk_container_set_border_width(GTK_CONTAINER(mixervol_box), 5);
+	gtk_container_add(GTK_CONTAINER(mixervol_frame), mixervol_box);
+
+	mixervol_menu = gtk_option_menu_new();
+	gtk_box_pack_start(GTK_BOX(mixervol_box), mixervol_menu, TRUE, TRUE, 0);
+
+	configure_mixer_volumedev_scan("Volume devices:", mixervol_menu);
+
+	keepopen_cbutton = gtk_check_button_new_with_label(
+		_("XMMS uses mixer exclusively."));
+	if (audio.mixer_keepopen)
+		gtk_toggle_button_set_active(
+			GTK_TOGGLE_BUTTON(keepopen_cbutton), TRUE);
+
+	gtk_box_pack_start_defaults(GTK_BOX(vbox), keepopen_cbutton);
+
+	if (sun_mixer_open() == 0)
+	{
+		configure_mixer_toggle_button(vbox, "bassboost", "Bass boost");
+		configure_mixer_toggle_button(vbox, "loudness", "Loudness");
+		configure_mixer_toggle_button(vbox, "spatial", "Spatial");
+		configure_mixer_toggle_button(vbox, "surround", "Surround");
+		configure_mixer_toggle_button(vbox, "enhanced", "Enhanced");
+		configure_mixer_toggle_button(vbox, "preamp", "Preamp");
+		configure_mixer_toggle_button(vbox, "swap", "Swap channels");
+		sun_mixer_close();
+	}
+}
+
+
+static void configure_mixer_frame(GtkWidget *vbox, GtkWidget *notebook)
+{
+	GtkWidget *mixervol_vbox;
+
+	mixervol_vbox = gtk_vbox_new(FALSE, 5);
+	gtk_container_set_border_width(GTK_CONTAINER(mixervol_vbox), 5);
+
+	configure_mixer_box(mixervol_vbox, notebook);
+
+	gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
+				 mixervol_vbox, gtk_label_new(_("Mixer")));
+}
+
+
+static void configure_stats_loop(void)
+{
+	if (pthread_mutex_lock(&stats_frame.active_mutex) != 0)
+	{
+		perror("active_mutex");
+		return;
+	}
+
+	while (stats_frame.active && stats_frame.fd)
+	{
+		audio_info_t info;
+		char sl[32];
+
+		pthread_mutex_lock(&stats_frame.audioctl_mutex);
+
+		sl[0] = '\0';
+
+		if (!ioctl(stats_frame.fd, AUDIO_GETINFO, &info))
+		{
+			char s[128];
+
+			sprintf(s, "Currently %s",
+				(info.mode == AUMODE_PLAY) ? "playing" :
+				(info.mode == AUMODE_RECORD) ? "recording" :
+				(info.mode == AUMODE_PLAY_ALL) ? "playing" :
+				"not playing");
+
+			if (info.mode == AUMODE_PLAY)
+			{
+				sprintf(s, "%s at %i Hz (%i-bit %s)", s,
+					info.play.sample_rate, info.play.precision,
+					audio.output->name);
+				sprintf(sl,"%i samples, %i error(s). %s",
+					info.play.samples, info.play.error,
+					info.play.active ?
+					"I/O in progress." : "");
+			}
+			gtk_label_set_text(GTK_LABEL(stats_frame.mode_label),
+					   s);
+
+			sprintf(s, "H/W block: %i bytes. Buffer: %i bytes",
+				info.blocksize, info.play.buffer_size);
+			gtk_label_set_text(
+				GTK_LABEL(stats_frame.blocksize_label), s);
+		}
+		gtk_label_set_text(GTK_LABEL(stats_frame.ooffs_label), sl);
+
+		pthread_mutex_unlock(&stats_frame.audioctl_mutex);
+		xmms_usleep(400000);
+	}
+	pthread_mutex_unlock(&stats_frame.active_mutex);
+
+	pthread_exit(NULL);
+}
+
+static void configure_status_frame(GtkWidget *vbox, GtkWidget *notebook)
+{
+	GtkWidget *status_vbox;
+	GtkWidget *name_label, *props_label;
+	pthread_t loop_thread;
+
+	memset(&stats_frame, 0, sizeof(struct sun_statsframe));
+
+	if (pthread_mutex_init(&stats_frame.audioctl_mutex, NULL) != 0)
+	{
+		perror("audioctl_mutex");
+		return;
+	}
+	if (pthread_mutex_init(&stats_frame.active_mutex, NULL) != 0)
+	{
+		perror("active_mutex");
+		return;
+	}
+	status_vbox = gtk_vbox_new(FALSE, 5);
+	gtk_container_set_border_width(GTK_CONTAINER(status_vbox), 5);
+
+	name_label = gtk_label_new(NULL);
+	gtk_container_add(GTK_CONTAINER(status_vbox), name_label);
+	props_label = gtk_label_new(NULL);
+	gtk_container_add(GTK_CONTAINER(status_vbox), props_label);
+
+	stats_frame.mode_label = gtk_label_new(NULL);
+	gtk_container_add(GTK_CONTAINER(status_vbox), stats_frame.mode_label);
+	stats_frame.blocksize_label = gtk_label_new(NULL);
+	gtk_container_add(GTK_CONTAINER(status_vbox),
+	    stats_frame.blocksize_label);
+	stats_frame.ooffs_label = gtk_label_new(NULL);
+	gtk_container_add(GTK_CONTAINER(status_vbox), stats_frame.ooffs_label);
+
+	gtk_notebook_append_page(GTK_NOTEBOOK(notebook), status_vbox,
+				 gtk_label_new(_("Status")));
+
+	if ((stats_frame.fd = open(audio.devaudioctl, O_RDWR)) >= 0)
+	{
+		audio_device_t device;
+		int props;
+
+		if (ioctl(stats_frame.fd, AUDIO_GETDEV, &device) >= 0)
+		{
+			char *s = g_strdup_printf("%s - %s(4) %s",
+				device.name, device.config, device.version);
+			gtk_label_set_text(GTK_LABEL(name_label), s);
+			g_free(s);
+		}
+		if (ioctl(stats_frame.fd, AUDIO_GETPROPS, &props) >= 0)
+		{
+			char s[32];
+			s[0] = '\0';
+
+			if ((props & AUDIO_PROP_FULLDUPLEX) ==
+			    AUDIO_PROP_FULLDUPLEX)
+				sprintf(s, "FULLDUPLEX ");
+			if ((props & AUDIO_PROP_MMAP) == AUDIO_PROP_MMAP)
+				sprintf(s, "%s MMAP ", s);
+			if ((props & AUDIO_PROP_INDEPENDENT) ==
+			    AUDIO_PROP_INDEPENDENT)
+				sprintf(s, "%s INDEPENDENT ", s);
+
+			gtk_label_set_text(GTK_LABEL(props_label), s);
+		}
+	}
+	stats_frame.active++;
+	pthread_create(&loop_thread, NULL, (void *) configure_stats_loop, NULL);
+}
+
+void sun_configure(void)
+{
+	GtkWidget *vbox, *notebook;
+	GtkWidget *bbox, *ok, *cancel;
+
+	if (configure_win)
+	{
+		gdk_window_raise(configure_win->window);
+		return;
+	}
+	configure_win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+	gtk_signal_connect(GTK_OBJECT(configure_win), "destroy",
+			   GTK_SIGNAL_FUNC(configure_win_destroy), NULL);
+
+	gtk_window_set_title(GTK_WINDOW(configure_win),
+			     _("Sun driver configuration"));
+	gtk_window_set_policy(GTK_WINDOW(configure_win), FALSE, FALSE, FALSE);
+	gtk_window_set_position(GTK_WINDOW(configure_win), GTK_WIN_POS_MOUSE);
+	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);
+
+	configure_devices_frame(vbox, notebook);
+	configure_buffering_frame(vbox, notebook);
+	configure_mixer_frame(vbox, notebook);
+	configure_status_frame(vbox, notebook);
+
+	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);
+
+	ok = gtk_button_new_with_label(_("Ok"));
+	gtk_signal_connect(GTK_OBJECT(ok), "clicked",
+			   GTK_SIGNAL_FUNC(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);
+
+	cancel = gtk_button_new_with_label(_("Cancel"));
+	gtk_signal_connect_object(GTK_OBJECT(cancel), "clicked",
+				  GTK_SIGNAL_FUNC(configure_win_cancel_cb),
+				  GTK_OBJECT(configure_win));
+
+	GTK_WIDGET_SET_FLAGS(cancel, GTK_CAN_DEFAULT);
+	gtk_box_pack_start(GTK_BOX(bbox), cancel, TRUE, TRUE, 0);
+
+	gtk_widget_show_all(configure_win);
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Output/sun/convert.c	Tue Feb 28 11:32:33 2006 -0800
@@ -0,0 +1,348 @@
+/*
+ *  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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "sun.h"
+
+void * sun_get_convert_buffer(size_t size)
+{
+	static size_t length;
+	static void *buffer;
+
+	if (size > 0 && size <= length)
+		return buffer;
+
+	length = size;
+	buffer = g_realloc(buffer, size);
+	return buffer;
+}
+
+static int convert_swap_endian(void **data, int length)
+{
+	int i;
+	guint16 *ptr = *data;
+
+	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)
+{
+	int i;
+	guint16 *ptr = *data;
+
+	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)
+{
+	int i;
+	guint16 *ptr = *data;
+
+	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)
+{
+	int i;
+	gint16 *ptr = *data;
+
+	for (i = 0; i < length; i += 2, ptr++)
+		*ptr ^= 1 << 15;
+
+	return i;
+}
+
+static int convert_swap_sign8(void **data, int length)
+{
+	int i;
+	gint8 *ptr = *data;
+
+	for (i = 0; i < length; i++)
+		*ptr++ ^= 1 << 7;
+
+	return i;
+}
+
+static int convert_to_8_native_endian(void **data, int length)
+{
+	int i;
+	gint16 *input = *data;
+	gint8 *output = *data;
+
+	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)
+{
+	int i;
+	gint16 *input = *data;
+	gint8 *output = *data;
+
+	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)
+{
+	int i;
+	gint16 *input = *data;
+	gint8 *output = *data;
+
+	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)
+{
+	int i;
+	gint16 *input = *data;
+	gint8 *output = *data;
+
+	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)
+{
+	int i;
+	guint16 *output;
+	guint8 *input = *data;
+
+	*data = sun_get_convert_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)
+{
+	int i;
+	guint16 *output;
+	guint8 *input = *data;
+
+	*data = sun_get_convert_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)
+{
+	int i;
+	guint16 *output;
+	guint8 *input = *data;
+
+	*data = sun_get_convert_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)
+{
+	int i;
+	guint16 *output;
+	guint8 *input = *data;
+
+	*data = sun_get_convert_buffer(length * 2);
+	output = *data;
+	for (i = 0; i < length; i++)
+		*output++ = *input++ ^ (1 << 7);
+
+	return (i * 2);
+}
+
+int (*sun_get_convert_func(int output, int input))(void **, int)
+{
+	if (output == input)
+		return NULL;
+
+	if ((output == AUDIO_ENCODING_ULINEAR_BE &&
+	     input == AUDIO_ENCODING_ULINEAR_LE) ||
+	    (output == AUDIO_ENCODING_ULINEAR_LE &&
+	     input == AUDIO_ENCODING_ULINEAR_BE) ||
+	    (output == AUDIO_ENCODING_SLINEAR_BE &&
+	     input == AUDIO_ENCODING_SLINEAR_LE) ||
+	    (output == AUDIO_ENCODING_SLINEAR_LE &&
+	     input == AUDIO_ENCODING_SLINEAR_BE))
+		return convert_swap_endian;
+
+	if ((output == AUDIO_ENCODING_ULINEAR_BE &&
+	     input == AUDIO_ENCODING_SLINEAR_BE) ||
+	    (output == AUDIO_ENCODING_ULINEAR_LE &&
+	     input == AUDIO_ENCODING_SLINEAR_LE) ||
+	    (output == AUDIO_ENCODING_SLINEAR_BE &&
+	     input == AUDIO_ENCODING_ULINEAR_BE) ||
+	    (output == AUDIO_ENCODING_SLINEAR_LE &&
+	     input == AUDIO_ENCODING_ULINEAR_LE))
+		return convert_swap_sign16;
+
+	if ((IS_BIG_ENDIAN &&
+	     ((output == AUDIO_ENCODING_ULINEAR_BE &&
+	       input == AUDIO_ENCODING_SLINEAR_LE) ||
+	      (output == AUDIO_ENCODING_SLINEAR_BE &&
+	       input == AUDIO_ENCODING_ULINEAR_LE))) ||
+	    (!IS_BIG_ENDIAN &&
+	     ((output == AUDIO_ENCODING_ULINEAR_LE &&
+	       input == AUDIO_ENCODING_SLINEAR_BE) ||
+	      (output == AUDIO_ENCODING_SLINEAR_LE &&
+	       input == AUDIO_ENCODING_ULINEAR_BE))))
+		return convert_swap_sign_and_endian_to_native;
+		
+	if ((!IS_BIG_ENDIAN &&
+	     ((output == AUDIO_ENCODING_ULINEAR_BE &&
+	      input == AUDIO_ENCODING_SLINEAR_LE) ||
+	      (output == AUDIO_ENCODING_SLINEAR_BE &&
+	       input == AUDIO_ENCODING_ULINEAR_LE))) ||
+	    (IS_BIG_ENDIAN &&
+	     ((output == AUDIO_ENCODING_ULINEAR_LE &&
+	      input == AUDIO_ENCODING_SLINEAR_BE) ||
+	      (output == AUDIO_ENCODING_SLINEAR_LE &&
+	       input == AUDIO_ENCODING_ULINEAR_BE))))
+		return convert_swap_sign_and_endian_to_alien;
+
+	if ((IS_BIG_ENDIAN &&
+	     ((output == AUDIO_ENCODING_PCM8 &&
+	       input == AUDIO_ENCODING_ULINEAR_BE) ||
+	      (output == AUDIO_ENCODING_SLINEAR &&
+	       input == AUDIO_ENCODING_SLINEAR_BE))) ||
+	    (!IS_BIG_ENDIAN &&
+	     ((output == AUDIO_ENCODING_PCM8 &&
+	       input == AUDIO_ENCODING_ULINEAR_LE) ||
+	      (output == AUDIO_ENCODING_SLINEAR &&
+	       input == AUDIO_ENCODING_SLINEAR_LE))))
+		return convert_to_8_native_endian;
+
+	if ((IS_BIG_ENDIAN &&
+	     ((output == AUDIO_ENCODING_PCM8 &&
+	      input == AUDIO_ENCODING_SLINEAR_BE) ||
+	      (output == AUDIO_ENCODING_SLINEAR &&
+	       input == AUDIO_ENCODING_ULINEAR_BE))) ||
+	    (!IS_BIG_ENDIAN &&
+	     ((output == AUDIO_ENCODING_PCM8 &&
+	      input == AUDIO_ENCODING_SLINEAR_LE) ||
+	      (output == AUDIO_ENCODING_SLINEAR &&
+	       input == AUDIO_ENCODING_ULINEAR_LE))))
+		return convert_to_8_native_endian_swap_sign;
+
+	if ((!IS_BIG_ENDIAN &&
+	     ((output == AUDIO_ENCODING_PCM8 &&
+	       input == AUDIO_ENCODING_ULINEAR_BE) ||
+	      (output == AUDIO_ENCODING_SLINEAR &&
+	       input == AUDIO_ENCODING_SLINEAR_BE))) ||
+	    (IS_BIG_ENDIAN &&
+	     ((output == AUDIO_ENCODING_PCM8 &&
+	       input == AUDIO_ENCODING_ULINEAR_LE) ||
+	      (output == AUDIO_ENCODING_SLINEAR &&
+	       input == AUDIO_ENCODING_SLINEAR_LE))))
+		return convert_to_8_alien_endian;
+
+	if ((!IS_BIG_ENDIAN &&
+	     ((output == AUDIO_ENCODING_PCM8 &&
+	       input == AUDIO_ENCODING_SLINEAR_BE) ||
+	      (output == AUDIO_ENCODING_SLINEAR &&
+	       input == AUDIO_ENCODING_ULINEAR_BE))) ||
+	    (IS_BIG_ENDIAN &&
+	     ((output == AUDIO_ENCODING_PCM8 &&
+	       input == AUDIO_ENCODING_SLINEAR_LE) ||
+	      (output == AUDIO_ENCODING_SLINEAR &&
+	       input == AUDIO_ENCODING_ULINEAR_LE))))
+		return convert_to_8_alien_endian_swap_sign;
+
+	if ((output == AUDIO_ENCODING_PCM8 &&
+	     input == AUDIO_ENCODING_SLINEAR) ||
+	    (output == AUDIO_ENCODING_SLINEAR &&
+	     input == AUDIO_ENCODING_PCM8))
+		return convert_swap_sign8;
+
+	if ((IS_BIG_ENDIAN &&
+	     ((output == AUDIO_ENCODING_ULINEAR_BE &&
+	       input == AUDIO_ENCODING_PCM8) ||
+	      (output == AUDIO_ENCODING_SLINEAR_BE &&
+	       input == AUDIO_ENCODING_SLINEAR))) ||
+	    (!IS_BIG_ENDIAN &&
+	     ((output == AUDIO_ENCODING_ULINEAR_LE &&
+	       input == AUDIO_ENCODING_PCM8) ||
+	      (output == AUDIO_ENCODING_SLINEAR_LE &&
+	       input == AUDIO_ENCODING_SLINEAR))))
+		return convert_to_16_native_endian;
+
+	if ((IS_BIG_ENDIAN &&
+	     ((output == AUDIO_ENCODING_ULINEAR_BE &&
+	       input == AUDIO_ENCODING_SLINEAR) ||
+	      (output == AUDIO_ENCODING_SLINEAR_BE &&
+	       input == AUDIO_ENCODING_PCM8))) ||
+	    (!IS_BIG_ENDIAN &&
+	     ((output == AUDIO_ENCODING_ULINEAR_LE &&
+	       input == AUDIO_ENCODING_SLINEAR) ||
+	      (output == AUDIO_ENCODING_SLINEAR_LE &&
+	       input == AUDIO_ENCODING_PCM8))))
+		return convert_to_16_native_endian_swap_sign;
+
+	if ((!IS_BIG_ENDIAN &&
+	     ((output == AUDIO_ENCODING_ULINEAR_BE &&
+	       input == AUDIO_ENCODING_PCM8) ||
+	      (output == AUDIO_ENCODING_SLINEAR_BE &&
+	       input == AUDIO_ENCODING_SLINEAR))) ||
+	    (IS_BIG_ENDIAN &&
+	     ((output == AUDIO_ENCODING_ULINEAR_LE &&
+	       input == AUDIO_ENCODING_PCM8) ||
+	      (output == AUDIO_ENCODING_SLINEAR_LE &&
+	       input == AUDIO_ENCODING_SLINEAR))))
+		return convert_to_16_alien_endian;
+
+	if ((!IS_BIG_ENDIAN &&
+	     ((output == AUDIO_ENCODING_ULINEAR_BE &&
+	       input == AUDIO_ENCODING_SLINEAR) ||
+	      (output == AUDIO_ENCODING_SLINEAR_BE &&
+	       input == AUDIO_ENCODING_PCM8))) ||
+	    (IS_BIG_ENDIAN &&
+	     ((output == AUDIO_ENCODING_ULINEAR_LE &&
+	       input == AUDIO_ENCODING_SLINEAR) ||
+	      (output == AUDIO_ENCODING_SLINEAR_LE &&
+	       input == AUDIO_ENCODING_PCM8))))
+		return convert_to_16_alien_endian_swap_sign;
+
+	return NULL;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Output/sun/mixer.c	Tue Feb 28 11:32:33 2006 -0800
@@ -0,0 +1,126 @@
+/*
+ *  Copyright (C) 2001  CubeSoft Communications, Inc.
+ *  <http://www.csoft.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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "sun.h"
+#include "mixer.h"
+
+int sun_mixer_open(void)
+{
+	if (pthread_mutex_lock(&audio.mixer_mutex) != 0)
+		return -1;
+
+    	if (!audio.mixer_keepopen || audio.mixerfd < 1)
+	{
+		audio.mixerfd = open(audio.devmixer, O_RDWR);
+		if (audio.mixerfd < 0)
+			perror(audio.devmixer);
+	}
+	return 0;
+}
+
+void sun_mixer_close(void)
+{
+	if (!audio.mixer_keepopen)
+	{
+		close(audio.mixerfd);
+		audio.mixerfd = 0;
+	}
+	pthread_mutex_unlock(&audio.mixer_mutex);
+}
+
+int sun_mixer_get_dev(int fd, int *dev, char *id)
+{
+	mixer_devinfo_t info;
+
+	for (info.index = 0; ioctl(fd, AUDIO_MIXER_DEVINFO, &info) >= 0;
+	     info.index++)
+	{
+		if (!strcmp(id, info.label.name))
+		{
+			*dev = info.index;
+			return 0;
+		}
+	}
+	return -1;
+}
+
+void sun_get_volume(int *l, int *r)
+{
+	mixer_ctrl_t mixer;
+
+	if (sun_mixer_open() < 0)
+	{
+		*l = 0;
+		*r = 0;
+		return;
+	}
+
+	if ((sun_mixer_get_dev(audio.mixerfd, &mixer.dev,
+	    audio.mixer_voldev) < 0))
+		goto closemixer;
+
+	mixer.type = AUDIO_MIXER_VALUE;
+	if (audio.output != NULL)
+		mixer.un.value.num_channels = audio.output->channels;
+	else
+		mixer.un.value.num_channels = 2;
+
+	if (ioctl(audio.mixerfd, AUDIO_MIXER_READ, &mixer) < 0)
+		goto closemixer;
+	*l = (mixer.un.value.level[AUDIO_MIXER_LEVEL_LEFT] * 100) / 255;
+	if (mixer.un.value.num_channels > 1)
+		*r = (mixer.un.value.level[AUDIO_MIXER_LEVEL_RIGHT] * 100) / 255;
+	else
+		*r = *l;
+
+ closemixer:
+	sun_mixer_close();
+}
+
+void sun_set_volume(int l, int r)
+{
+	mixer_ctrl_t mixer;
+
+	if (sun_mixer_open() < 0)
+		return;
+
+	if ((sun_mixer_get_dev(audio.mixerfd, &mixer.dev,
+			       audio.mixer_voldev) < 0))
+	{
+		if (!audio.mixer_keepopen)
+			close(audio.mixerfd);
+		return;
+	}
+	mixer.type = AUDIO_MIXER_VALUE;
+	if (audio.output != NULL)
+		mixer.un.value.num_channels = audio.output->channels;
+	else
+		mixer.un.value.num_channels = 2;
+	mixer.un.value.level[AUDIO_MIXER_LEVEL_LEFT] = (l * 255) / 100;
+	if (mixer.un.value.num_channels > 1)
+		mixer.un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = (r * 255) / 100;
+
+	if (ioctl(audio.mixerfd, AUDIO_MIXER_WRITE, &mixer) < 0)
+	{
+		if (!audio.mixer_keepopen)
+			close(audio.mixerfd);
+		return;
+	}
+	sun_mixer_close();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Output/sun/mixer.h	Tue Feb 28 11:32:33 2006 -0800
@@ -0,0 +1,25 @@
+/*
+ *  Copyright (C) 2001  CubeSoft Communications, Inc.
+ *  <http://www.csoft.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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+int	sun_mixer_open(void);
+void	sun_mixer_close(void);
+int	sun_mixer_get_dev(int, int *, char *);
+void	sun_get_volume(int *, int *);
+void	sun_set_volume(int, int);
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Output/sun/resample.h	Tue Feb 28 11:32:33 2006 -0800
@@ -0,0 +1,110 @@
+/*  XMMS - Cross-platform multimedia player
+ *  Copyright (C) 1998-2001  Peter Alm, Mikael Alm, Olle Hallnas,
+ *                           Thomas Nilsson and 4Front Technologies
+ *  Copyright (C) 1999-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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#define NOT_NATIVE_ENDIAN						\
+		  ((IS_BIG_ENDIAN &&					\
+		   (output.format.sun == AUDIO_ENCODING_SLINEAR_LE ||	\
+		    output.format.sun == AUDIO_ENCODING_ULINEAR_LE))||	\
+		  (!IS_BIG_ENDIAN &&					\
+		   (output.format.sun == AUDIO_ENCODING_SLINEAR_BE ||	\
+		    output.format.sun == AUDIO_ENCODING_ULINEAR_BE)))
+
+#define RESAMPLE_STEREO(sample_type)				\
+do {								\
+	const int shift = sizeof (sample_type);			\
+        int 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)					\
+		sun_bswap16(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++)		\
+	{							\
+		int 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)					\
+		sun_bswap16(nbuffer, nlen);			\
+	w = write_all(audio.fd, nbuffer, nlen);			\
+} while (0)
+
+#define RESAMPLE_MONO(sample_type)				\
+do {								\
+	const int shift = sizeof (sample_type) - 1;		\
+        int 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)					\
+		sun_bswap16(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++)		\
+	{							\
+		int 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)					\
+		sun_bswap16(nbuffer, nlen);			\
+	w = write_all(audio.fd, nbuffer, nlen);			\
+} while (0)
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Output/sun/sun.c	Tue Feb 28 11:32:33 2006 -0800
@@ -0,0 +1,134 @@
+/*
+ *  Copyright (C) 2001  CubeSoft Communications, Inc.
+ *  <http://www.csoft.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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "sun.h"
+#include "libaudacious/configfile.h"
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <errno.h>
+
+struct sun_audio audio;
+
+OutputPlugin sun_op =
+{
+	NULL,
+	NULL,
+	NULL,			/* Description */
+	sun_init,
+	sun_about,
+	sun_cleanup,
+	sun_configure,
+	sun_get_volume,
+	sun_set_volume,
+	sun_open,
+	sun_write,
+	sun_close,
+	sun_flush,
+	sun_pause,
+	sun_free,
+	sun_playing,
+	sun_output_time,
+	sun_written_time
+};
+
+
+OutputPlugin * get_oplugin_info(void)
+{
+	sun_op.description = g_strdup_printf(_("BSD Sun Driver %s"),
+					     SUN_VERSION);
+	return (&sun_op);
+}
+
+void sun_init(void)
+{
+	ConfigFile *cfgfile;
+	char *s;
+
+	memset(&audio, 0, sizeof(struct sun_audio));
+
+	cfgfile = xmms_cfg_open_default_file();
+	/* Devices */
+	xmms_cfg_read_string(cfgfile, "sun", "audio_devaudio", &audio.devaudio);
+	xmms_cfg_read_string(cfgfile, "sun",
+			     "audio_devaudioctl", &audio.devaudioctl);
+	xmms_cfg_read_string(cfgfile, "sun", "audio_devmixer", &audio.devmixer);
+
+	/* Buffering */
+	xmms_cfg_read_int(cfgfile, "sun",
+			  "buffer_size", &audio.req_buffer_size);
+	xmms_cfg_read_int(cfgfile, "sun",
+			  "prebuffer_size", &audio.req_prebuffer_size);
+
+	/* Mixer */
+	xmms_cfg_read_string(cfgfile, "sun", "mixer_voldev", &audio.mixer_voldev);
+	xmms_cfg_read_boolean(cfgfile, "sun",
+			      "mixer_keepopen", &audio.mixer_keepopen);
+
+	xmms_cfg_free(cfgfile);
+
+	/* Audio device path */
+	if ((s = getenv("AUDIODEVICE")))
+		audio.devaudio = g_strdup(s);
+	else if (!audio.devaudio || !strcmp("", audio.devaudio))
+		audio.devaudio = g_strdup(SUN_DEV_AUDIO);
+
+	/* Audio control device path */
+	if (!audio.devaudioctl || !strcmp("", audio.devaudioctl))
+		audio.devaudioctl = g_strdup(SUN_DEV_AUDIOCTL);
+
+	/* Mixer device path */
+	if ((s = getenv("MIXERDEVICE")))
+		audio.devmixer = g_strdup(s);
+	else if (!audio.devmixer || !strcmp("", audio.devmixer))
+		audio.devmixer = g_strdup(SUN_DEV_MIXER);
+
+	if (!audio.mixer_voldev || !strcmp("", audio.mixer_voldev))
+		audio.mixer_voldev = g_strdup(SUN_DEFAULT_VOLUME_DEV);
+
+	/* Default buffering settings */
+	if (!audio.req_buffer_size)
+		audio.req_buffer_size = SUN_DEFAULT_BUFFER_SIZE;
+	if (!audio.req_prebuffer_size)
+		audio.req_prebuffer_size = SUN_DEFAULT_PREBUFFER_SIZE;
+
+	audio.input = NULL;
+	audio.output = NULL;
+	audio.effect = NULL;
+
+	if (pthread_mutex_init(&audio.mixer_mutex, NULL) != 0)
+		perror("mixer_mutex");
+}
+
+void sun_cleanup(void)
+{
+	g_free(audio.devaudio);
+	g_free(audio.devaudioctl);
+	g_free(audio.devmixer);
+	g_free(audio.mixer_voldev);
+
+	if (!pthread_mutex_lock(&audio.mixer_mutex))
+	{
+		if (audio.mixerfd)
+			close(audio.mixerfd);
+		pthread_mutex_unlock(&audio.mixer_mutex);
+		pthread_mutex_destroy(&audio.mixer_mutex);
+	}
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Output/sun/sun.h	Tue Feb 28 11:32:33 2006 -0800
@@ -0,0 +1,159 @@
+/*
+ *  Copyright (C) 2001  CubeSoft Communications, Inc.
+ *  <http://www.csoft.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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include <gtk/gtk.h>
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <pthread.h>
+
+#include "audioio.h"
+
+#include "audacious/plugin.h"
+#include "libaudacious/configfile.h"
+
+/* Default path to audio device. */
+#ifndef SUN_DEV_AUDIO
+#define SUN_DEV_AUDIO "/dev/audio"
+#endif
+
+/* Default path to audioctl device */
+#ifndef SUN_DEV_AUDIOCTL
+#define SUN_DEV_AUDIOCTL "/dev/audioctl"
+#endif
+
+/* Default path to mixer device */
+#ifndef SUN_DEV_MIXER
+#define SUN_DEV_MIXER "/dev/mixer"
+#endif
+
+/* Default mixer device to control */
+#ifndef SUN_DEFAULT_VOLUME_DEV
+#define SUN_DEFAULT_VOLUME_DEV "dac"
+#endif
+
+/* Default hardware block size */
+#ifndef SUN_DEFAULT_BLOCKSIZE
+#define SUN_DEFAULT_BLOCKSIZE 8800
+#endif
+
+/* Default `requested' buffer size */
+#ifndef SUN_DEFAULT_BUFFER_SIZE
+#define SUN_DEFAULT_BUFFER_SIZE 8800
+#endif
+
+/* Minimum total buffer size */
+#ifndef SUN_MIN_BUFFER_SIZE
+#define SUN_MIN_BUFFER_SIZE 14336
+#endif
+
+/* Default prebuffering (%) */
+#ifndef SUN_DEFAULT_PREBUFFER_SIZE
+#define SUN_DEFAULT_PREBUFFER_SIZE 25
+#endif
+
+#define SUN_VERSION "0.6"
+
+struct sun_format {
+	char	name[16];
+	union {
+		AFormat	xmms;
+		gint	sun;
+	} format;
+	int	frequency;
+	int	channels;
+	int	bps;
+};
+
+struct sun_audio {
+	struct	sun_format *input;
+	struct	sun_format *output;
+	struct	sun_format *effect;
+
+	gchar	*devaudio;
+	gchar	*devaudioctl;
+	gchar	*devmixer;
+	gchar	*mixer_voldev;
+
+	gint	fd;
+	gint	mixerfd;
+
+	gboolean mixer_keepopen;
+
+	gboolean going;
+	gboolean paused;
+	gboolean unpause, do_pause;
+
+	gint	req_prebuffer_size;
+	gint	req_buffer_size;
+
+	pthread_mutex_t	mixer_mutex;
+};
+
+struct sun_statsframe {
+	int	fd;
+	int	active;
+
+	GtkWidget	*mode_label;
+	GtkWidget	*blocksize_label;
+	GtkWidget	*ooffs_label;
+	pthread_mutex_t	audioctl_mutex;
+	pthread_mutex_t	active_mutex;
+};
+
+extern	OutputPlugin	op;
+
+extern struct sun_audio		audio;
+extern struct sun_statsframe	stats_frame;
+
+void	 sun_init(void);
+void	 sun_about(void);
+void	 sun_configure(void);
+void	 sun_cleanup(void);
+
+gint	 sun_open(AFormat, int, int);
+void	 sun_write(void *, int);
+void	 sun_close(void);
+void	 sun_flush(int);
+void	 sun_pause(short);
+gint	 sun_free(void);
+gint	 sun_playing(void);
+gint	 sun_output_time(void);
+gint	 sun_written_time(void);
+void	 sun_get_volume(int *, int *);
+void	 sun_set_volume(int, int);
+void	*sun_get_convert_buffer(size_t);
+int	(*sun_get_convert_func(int, int))(void **, int);
+
+#ifdef WORDS_BIGENDIAN
+#define IS_BIG_ENDIAN TRUE
+#else
+#define IS_BIG_ENDIAN FALSE
+#endif
+
--- a/configure.ac	Mon Feb 27 22:12:32 2006 -0800
+++ b/configure.ac	Tue Feb 28 11:32:33 2006 -0800
@@ -702,6 +702,7 @@
         then
             AC_DEFINE(SYMBOL_PREFIX, "_", [Define to symbol prefix, if any])
         fi
+        OUTPUT_PLUGINS="$OUTPUT_PLUGINS sun"
     ;;
     *-*-darwin*)
         AC_DEFINE(SYMBOL_PREFIX, "_", [Define to symbol prefix, if any])
@@ -921,6 +922,7 @@
 echo "  Advanced Linux Sound Arch. (alsa):      $have_alsa"
 echo "  Enlightenment Sound Daemon (esd):       $have_esd"
 echo "  Jack Audio Connection Kit (jack):       $have_jack"
+echo "  BSD/SUN audio output (sun):             $have_sun"
 echo
 echo "  Input Plugins"
 echo "  -------------"