Blob Blame History Raw
diff -Nur audacious-plugins-fedora-1.5.1-orig/src/alsa/about.c audacious-plugins-fedora-1.5.1/src/alsa/about.c
--- audacious-plugins-fedora-1.5.1-orig/src/alsa/about.c	2008-06-08 10:37:44.000000000 +0200
+++ audacious-plugins-fedora-1.5.1/src/alsa/about.c	1970-01-01 01:00:00.000000000 +0100
@@ -1,49 +0,0 @@
-/*  XMMS - ALSA output plugin
- *    Copyright (C) 2001-2003 Matthieu Sozeau <mattam@altern.org>
- * 
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or
- *  (at your option) any later version.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-
-#include "alsa.h"
-
-void alsa_about(void)
-{
-	static GtkWidget *dialog;
-
-	if (dialog != NULL)
-		return;
-	
-	dialog = audacious_info_dialog(
-		_("About ALSA Driver"),
-		_("Audacious ALSA 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.\n"
-		  "Author: Matthieu Sozeau (mattam@altern.org)"), _("OK"), FALSE, NULL, NULL);
-	gtk_signal_connect(GTK_OBJECT(dialog), "destroy",
-			   GTK_SIGNAL_FUNC(gtk_widget_destroyed),
-			   &dialog);
-}
diff -Nur audacious-plugins-fedora-1.5.1-orig/src/alsa/alsa.c audacious-plugins-fedora-1.5.1/src/alsa/alsa.c
--- audacious-plugins-fedora-1.5.1-orig/src/alsa/alsa.c	2008-06-08 10:37:44.000000000 +0200
+++ audacious-plugins-fedora-1.5.1/src/alsa/alsa.c	1970-01-01 01:00:00.000000000 +0100
@@ -1,100 +0,0 @@
-/*  XMMS - ALSA output plugin
- *    Copyright (C) 2001 Matthieu Sozeau
- * 
- *  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 "alsa.h"
-#include <glib.h>
-#include <stdlib.h>
-#include <dlfcn.h>
-#include <ctype.h>
-
-struct alsa_config alsa_cfg;
-
-
-static void alsa_cleanup(void)
-{
-	if (alsa_cfg.pcm_device) {
-		free(alsa_cfg.pcm_device);
-		alsa_cfg.pcm_device = NULL;
-	}
-
-	if (alsa_cfg.mixer_device) {
-		free(alsa_cfg.mixer_device);
-		alsa_cfg.mixer_device = NULL;
-	}
-}
-
-
-static void alsa_init(void)
-{
-	mcs_handle_t *cfgfile;
-
-	memset(&alsa_cfg, 0, sizeof (alsa_cfg));
-	
-	alsa_cfg.buffer_time = 500;
-	alsa_cfg.period_time = 100;
-	alsa_cfg.debug = 0;
-	alsa_cfg.vol.left = 100;
-	alsa_cfg.vol.right = 100;
-
-	cfgfile = aud_cfg_db_open();
-	if (!aud_cfg_db_get_string(cfgfile, ALSA_CFGID, "pcm_device",
-				  &alsa_cfg.pcm_device))
-		alsa_cfg.pcm_device = g_strdup("default");
-	g_message("device: %s", alsa_cfg.pcm_device);
-	if (!aud_cfg_db_get_string(cfgfile, ALSA_CFGID, "mixer_device",
-				  &alsa_cfg.mixer_device))
-		alsa_cfg.mixer_device = g_strdup("PCM");
-	aud_cfg_db_get_int(cfgfile, ALSA_CFGID, "mixer_card", &alsa_cfg.mixer_card);
-	aud_cfg_db_get_int(cfgfile, ALSA_CFGID, "buffer_time", &alsa_cfg.buffer_time);
-	aud_cfg_db_get_int(cfgfile, ALSA_CFGID, "period_time", &alsa_cfg.period_time);
-
-	aud_cfg_db_get_bool(cfgfile, ALSA_CFGID, "debug", &alsa_cfg.debug);
-	aud_cfg_db_close(cfgfile);
-
-	if (dlopen("libasound.so.2", RTLD_NOW | RTLD_GLOBAL) == NULL)
-	{
-		g_message("Cannot load alsa library: %s", dlerror());
-		/* FIXME, this plugin wont work... */
-	}
-}
-
-
-static OutputPlugin alsa_op =
-{
-	.description = "ALSA Output Plugin",
-	.init = alsa_init,
-	.cleanup = alsa_cleanup,
-	.about = alsa_about,
-	.configure = alsa_configure,
-	.get_volume = alsa_get_volume,
-	.set_volume = alsa_set_volume,
-	.open_audio = alsa_open,
-	.write_audio = alsa_write,
-	.close_audio = alsa_close,
-	.flush = alsa_flush,
-	.pause = alsa_pause,
-	.buffer_free = alsa_free,
-	.buffer_playing = alsa_playing,
-	.output_time = alsa_get_output_time,
-	.written_time = alsa_get_written_time,
-	.tell_audio = alsa_tell
-};
-
-OutputPlugin *alsa_oplist[] = { &alsa_op, NULL };
-
-DECLARE_PLUGIN(alsa, NULL, NULL, NULL, alsa_oplist, NULL, NULL, NULL, NULL)
diff -Nur audacious-plugins-fedora-1.5.1-orig/src/alsa/alsa-configure.c audacious-plugins-fedora-1.5.1/src/alsa/alsa-configure.c
--- audacious-plugins-fedora-1.5.1-orig/src/alsa/alsa-configure.c	1970-01-01 01:00:00.000000000 +0100
+++ audacious-plugins-fedora-1.5.1/src/alsa/alsa-configure.c	2009-06-29 18:01:20.000000000 +0200
@@ -0,0 +1,355 @@
+/*
+ * Audacious ALSA Plugin (-ng)
+ * Copyright (c) 2009 William Pitcock <nenolod@dereferenced.org>
+ * Portions copyright (C) 2001-2003 Matthieu Sozeau <mattam@altern.org>
+ * Portions copyright (C) 2003-2005 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 "alsa-stdinc.h"
+#include <stdio.h>
+
+#include <gtk/gtk.h>
+#include <glib/gi18n.h>
+
+static GtkWidget *configure_win = NULL;
+static GtkWidget *devices_combo, *mixer_devices_combo;
+
+static gint current_mixer_card;
+
+gint alsaplug_mixer_new_for_card(snd_mixer_t **mixer, const gchar *card);
+
+#define GET_TOGGLE(tb) gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(tb))
+#define GET_CHARS(edit) gtk_editable_get_chars(GTK_EDITABLE(edit), 0, -1)
+
+static void configure_win_ok_cb(GtkWidget * w, gpointer data)
+{
+	g_free(alsaplug_cfg.pcm_device);
+	alsaplug_cfg.pcm_device = GET_CHARS(GTK_COMBO(devices_combo)->entry);
+	alsaplug_cfg.mixer_card = g_strdup_printf("hw:%d", current_mixer_card);
+	alsaplug_cfg.mixer_device = GET_CHARS(GTK_COMBO(mixer_devices_combo)->entry);
+
+	gtk_widget_destroy(configure_win);
+
+	/* Save configuration */
+	mcs_handle_t *cfgfile = aud_cfg_db_open();
+	aud_cfg_db_set_string(cfgfile, "alsaplug", "pcm_device", alsaplug_cfg.pcm_device);
+	aud_cfg_db_set_string(cfgfile, "alsaplug", "mixer_card", alsaplug_cfg.mixer_card);
+	aud_cfg_db_set_string(cfgfile, "alsaplug","mixer_device", alsaplug_cfg.mixer_device);
+	aud_cfg_db_close(cfgfile);
+}
+
+void alsaplug_get_config(void)
+{
+	mcs_handle_t *cfgfile = aud_cfg_db_open();
+	aud_cfg_db_get_string(cfgfile, "alsaplug", "pcm_device", &alsaplug_cfg.pcm_device);
+	aud_cfg_db_get_string(cfgfile, "alsaplug", "mixer_card", &alsaplug_cfg.mixer_card);
+	aud_cfg_db_get_string(cfgfile, "alsaplug","mixer_device", &alsaplug_cfg.mixer_device);
+	aud_cfg_db_close(cfgfile);
+}
+
+static gint get_cards(GtkOptionMenu *omenu, GtkSignalFunc cb)
+{
+	GtkWidget *menu, *item;
+	gint card = -1, err, set = 0, curr = -1;
+
+	menu = gtk_menu_new();
+	if ((err = snd_card_next(&card)) != 0)
+		g_warning("snd_next_card() failed: %s", snd_strerror(err));
+
+	while (card > -1)
+	{
+		gchar *label;
+
+		curr++;
+		if ((err = snd_card_get_name(card, &label)) != 0)
+		{
+			g_warning("snd_carg_get_name() failed: %s",
+				  snd_strerror(err));
+			break;
+		}
+
+		item = gtk_menu_item_new_with_label(label);
+		gtk_signal_connect(GTK_OBJECT(item), "activate", cb,
+				   GINT_TO_POINTER(card));
+		gtk_widget_show(item);
+		gtk_menu_append(GTK_MENU(menu), item);
+		if ((err = snd_card_next(&card)) != 0)
+		{
+			g_warning("snd_next_card() failed: %s",
+				  snd_strerror(err));
+			break;
+		}
+	}
+
+	gtk_option_menu_set_menu(omenu, menu);
+	return set;
+}
+
+static gint get_mixer_devices(GtkCombo *combo, const gchar *card)
+{
+	GList *items = NULL;
+	gint err;
+	snd_mixer_t *mixer;
+	snd_mixer_elem_t *current;
+
+	if ((err = alsaplug_mixer_new_for_card(&mixer, card)) < 0)
+		return err;
+
+	current = snd_mixer_first_elem(mixer);
+
+	while (current)
+	{
+		if (snd_mixer_selem_is_active(current) &&
+		    snd_mixer_selem_has_playback_volume(current))
+		{
+			const gchar *sname = snd_mixer_selem_get_name(current);
+			gint index = snd_mixer_selem_get_index(current);
+			if (index)
+				items = g_list_append(items, g_strdup_printf("%s,%d", sname, index));
+			else
+				items = g_list_append(items, g_strdup(sname));
+		}
+		current = snd_mixer_elem_next(current);
+	}
+
+	gtk_combo_set_popdown_strings(combo, items);
+
+	return 0;
+}
+
+static void get_devices_for_card(GtkCombo *combo, gint card)
+{
+	GtkWidget *item;
+	gint pcm_device = -1, err;
+	snd_pcm_info_t *pcm_info = NULL;
+	snd_ctl_t *ctl;
+	gchar dev[64], *card_name;
+
+	sprintf(dev, "hw:%i", card);
+
+	if ((err = snd_ctl_open(&ctl, dev, 0)) < 0)
+	{
+		printf("snd_ctl_open() failed: %s", snd_strerror(err));
+		return;
+	}
+
+	if ((err = snd_card_get_name(card, &card_name)) != 0)
+	{
+		g_warning("snd_card_get_name() failed: %s", snd_strerror(err));
+		card_name = _("Unknown soundcard");
+	}
+
+	snd_pcm_info_alloca(&pcm_info);
+
+	for (;;)
+	{
+		char *device, *descr;
+		if ((err = snd_ctl_pcm_next_device(ctl, &pcm_device)) < 0)
+		{
+			g_warning("snd_ctl_pcm_next_device() failed: %s",
+				  snd_strerror(err));
+			pcm_device = -1;
+		}
+		if (pcm_device < 0)
+			break;
+
+		snd_pcm_info_set_device(pcm_info, pcm_device);
+		snd_pcm_info_set_subdevice(pcm_info, 0);
+		snd_pcm_info_set_stream(pcm_info, SND_PCM_STREAM_PLAYBACK);
+
+		if ((err = snd_ctl_pcm_info(ctl, pcm_info)) < 0)
+		{
+			if (err != -ENOENT)
+				g_warning("get_devices_for_card(): "
+					  "snd_ctl_pcm_info() "
+					  "failed (%d:%d): %s.", card,
+					  pcm_device, snd_strerror(err));
+			continue;
+		}
+
+		device = g_strdup_printf("hw:%d,%d", card, pcm_device);
+		descr = g_strconcat(card_name, ": ",
+				    snd_pcm_info_get_name(pcm_info),
+				    " (", device, ")", NULL);
+		item = gtk_list_item_new_with_label(descr);
+		gtk_widget_show(item);
+		g_free(descr);
+		gtk_combo_set_item_string(combo, GTK_ITEM(item), device);
+		g_free(device);
+		gtk_container_add(GTK_CONTAINER(combo->list), item);
+	}
+
+	snd_ctl_close(ctl);
+}
+
+
+
+static void get_devices(GtkCombo *combo)
+{
+	GtkWidget *item;
+	gint card = -1;
+	gint err = 0;
+	gchar *descr;
+
+	descr = g_strdup_printf(_("Default PCM device (%s)"), "default");
+	item = gtk_list_item_new_with_label(descr);
+	gtk_widget_show(item);
+	g_free(descr);
+	gtk_combo_set_item_string(combo, GTK_ITEM(item), "default");
+	gtk_container_add(GTK_CONTAINER(combo->list), item);
+
+	if ((err = snd_card_next(&card)) != 0)
+	{
+		g_warning("snd_next_card() failed: %s", snd_strerror(err));
+		return;
+	}
+
+	while (card > -1)
+	{
+		get_devices_for_card(combo, card);
+		if ((err = snd_card_next(&card)) != 0)
+		{
+			g_warning("snd_next_card() failed: %s",
+				  snd_strerror(err));
+			break;
+		}
+	}
+}
+
+static void mixer_card_cb(GtkWidget * widget, gpointer card)
+{
+	gchar scratch[128];
+
+	if (current_mixer_card == GPOINTER_TO_INT(card))
+		return;
+	current_mixer_card = GPOINTER_TO_INT(card);
+
+	snprintf(scratch, 128, "hw:%d", current_mixer_card);
+	get_mixer_devices(GTK_COMBO(mixer_devices_combo),
+			  scratch);
+}
+
+void alsaplug_configure(void)
+{
+	GtkWidget *vbox, *notebook;
+	GtkWidget *dev_vbox, *adevice_frame, *adevice_box;
+	GtkWidget *mixer_frame, *mixer_box, *mixer_table, *mixer_card_om;
+	GtkWidget *mixer_card_label, *mixer_device_label;
+	GtkWidget *bbox, *ok, *cancel;
+
+	gint mset;
+
+	if (configure_win)
+	{
+                gtk_window_present(GTK_WINDOW(configure_win));
+		return;
+	}
+
+	configure_win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+	gtk_signal_connect(GTK_OBJECT(configure_win), "destroy",
+			   GTK_SIGNAL_FUNC(gtk_widget_destroyed),
+			   &configure_win);
+	gtk_window_set_title(GTK_WINDOW(configure_win),
+			     _("ALSA Driver configuration"));
+	gtk_window_set_policy(GTK_WINDOW(configure_win),
+			      FALSE, TRUE, FALSE);
+	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);
+
+	devices_combo = gtk_combo_new();
+	gtk_box_pack_start(GTK_BOX(adevice_box), devices_combo,
+			   FALSE, FALSE, 0);
+	get_devices(GTK_COMBO(devices_combo));
+	gtk_entry_set_text(GTK_ENTRY(GTK_COMBO(devices_combo)->entry),
+			   alsaplug_cfg.pcm_device);
+
+	mixer_frame = gtk_frame_new(_("Mixer:"));
+	gtk_box_pack_start(GTK_BOX(dev_vbox), mixer_frame, FALSE, FALSE, 0);
+
+	mixer_box = gtk_vbox_new(FALSE, 5);
+	gtk_container_set_border_width(GTK_CONTAINER(mixer_box), 5);
+	gtk_container_add(GTK_CONTAINER(mixer_frame), mixer_box);
+
+	mixer_table = gtk_table_new(2, 2, FALSE);
+	gtk_table_set_row_spacings(GTK_TABLE(mixer_table), 5);
+	gtk_table_set_col_spacings(GTK_TABLE(mixer_table), 5);
+	gtk_box_pack_start(GTK_BOX(mixer_box), mixer_table, FALSE, FALSE, 0);
+
+	mixer_card_label = gtk_label_new(_("Mixer card:"));
+	gtk_label_set_justify(GTK_LABEL(mixer_card_label), GTK_JUSTIFY_LEFT);
+	gtk_misc_set_alignment(GTK_MISC(mixer_card_label), 0, 0.5);
+	gtk_table_attach(GTK_TABLE(mixer_table), mixer_card_label,
+			 0, 1, 0, 1, GTK_FILL, 0, 0, 0);
+
+	mixer_card_om = gtk_option_menu_new();
+	mset = get_cards(GTK_OPTION_MENU(mixer_card_om),
+			 (GtkSignalFunc)mixer_card_cb);
+
+	gtk_table_attach(GTK_TABLE(mixer_table), mixer_card_om,
+			 1, 2, 0, 1, GTK_FILL | GTK_EXPAND, GTK_FILL, 0, 0);
+
+	mixer_device_label = gtk_label_new(_("Mixer device:"));
+	gtk_label_set_justify(GTK_LABEL(mixer_device_label), GTK_JUSTIFY_LEFT);
+	gtk_misc_set_alignment(GTK_MISC(mixer_device_label), 0, 0.5);
+	gtk_table_attach(GTK_TABLE(mixer_table), mixer_device_label,
+			 0, 1, 1, 2, GTK_FILL, 0, 0, 0);
+	mixer_devices_combo = gtk_combo_new();
+	gtk_option_menu_set_history(GTK_OPTION_MENU(mixer_card_om), mset);
+	get_mixer_devices(GTK_COMBO(mixer_devices_combo), alsaplug_cfg.mixer_card);
+	gtk_entry_set_text(GTK_ENTRY(GTK_COMBO(mixer_devices_combo)->entry),
+			   alsaplug_cfg.mixer_device);
+
+	gtk_table_attach(GTK_TABLE(mixer_table), mixer_devices_combo,
+			 1, 2, 1, 2, GTK_FILL | GTK_EXPAND, 0, 0, 0);
+
+	gtk_notebook_append_page(GTK_NOTEBOOK(notebook), dev_vbox,
+				 gtk_label_new(_("Device settings")));
+
+	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", (GCallback)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",
+				  (GCallback)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);
+
+	gtk_widget_show_all(configure_win);
+}
diff -Nur audacious-plugins-fedora-1.5.1-orig/src/alsa/alsa-core.c audacious-plugins-fedora-1.5.1/src/alsa/alsa-core.c
--- audacious-plugins-fedora-1.5.1-orig/src/alsa/alsa-core.c	1970-01-01 01:00:00.000000000 +0100
+++ audacious-plugins-fedora-1.5.1/src/alsa/alsa-core.c	2009-06-29 18:01:20.000000000 +0200
@@ -0,0 +1,511 @@
+/*
+ * Audacious ALSA Plugin (-ng)
+ * Copyright (c) 2009 William Pitcock <nenolod@dereferenced.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#define ALSA_DEBUG
+#include "alsa-stdinc.h"
+
+alsaplug_cfg_t alsaplug_cfg;
+
+static snd_pcm_t *pcm_handle = NULL;
+static alsaplug_ringbuf_t pcm_ringbuf;
+static gboolean pcm_going = FALSE;
+static GThread *audio_thread = NULL;
+static gint bps;
+
+static gsize wr_total = 0;
+static gsize wr_hwframes = 0;
+
+static gint flush_request, paused;
+
+static GMutex * pcm_state_mutex;
+static GCond * pcm_state_cond, * pcm_flush_cond;
+
+extern void alsaplug_configure(void);
+extern void alsaplug_get_config(void);
+
+/********************************************************************************
+ * ALSA Mixer setting functions.                                                *
+ ********************************************************************************/
+
+static snd_mixer_t *amixer = NULL;
+static gboolean mixer_ready = FALSE;
+
+static snd_mixer_elem_t *
+alsaplug_get_mixer_elem_by_name(snd_mixer_t *mixer, gchar *name)
+{
+    snd_mixer_selem_id_t *selem_id;
+    snd_mixer_elem_t *elem;
+
+    g_return_val_if_fail(mixer != NULL, NULL);
+    g_return_val_if_fail(name != NULL, NULL);
+
+    snd_mixer_selem_id_alloca(&selem_id);
+    snd_mixer_selem_id_set_name(selem_id, name);
+
+    elem = snd_mixer_find_selem(mixer, selem_id);
+    if (elem == NULL)
+        return NULL;
+
+    snd_mixer_selem_set_playback_volume_range(elem, 0, 100);
+
+    return elem;
+}
+
+/* try to determine the best choice... may need tweaking. --nenolod */
+static snd_mixer_elem_t *
+alsaplug_guess_mixer_elem(snd_mixer_t *mixer)
+{
+    gchar *elem_names[] = { "Wave", "PCM", "Front", "Master" };
+    gint i;
+    snd_mixer_elem_t *elem;
+
+    if (alsaplug_cfg.mixer_device != NULL)
+        return alsaplug_get_mixer_elem_by_name(mixer, alsaplug_cfg.mixer_device);
+
+    for (i = 0; i < G_N_ELEMENTS(elem_names); i++)
+    {
+        elem = alsaplug_get_mixer_elem_by_name(mixer, elem_names[i]);
+        if (elem != NULL)
+            return elem;
+    }
+
+    return NULL;
+}
+
+gint
+alsaplug_mixer_new_for_card(snd_mixer_t **mixer, const gchar *card)
+{
+    gint ret;
+
+    ret = snd_mixer_open(mixer, 0);
+    if (ret < 0)
+    {
+        _ERROR("mixer initialization failed: %s", snd_strerror(ret));
+        return ret;
+    }
+
+    ret = snd_mixer_attach(*mixer, card);
+    if (ret < 0)
+    {
+        snd_mixer_close(*mixer);
+        _ERROR("failed to attach to hardware mixer: %s", snd_strerror(ret));
+        return ret;
+    }
+
+    ret = snd_mixer_selem_register(*mixer, NULL, NULL);
+    if (ret < 0)
+    {
+        snd_mixer_detach(*mixer, card);
+        snd_mixer_close(*mixer);
+        _ERROR("failed to register hardware mixer: %s", snd_strerror(ret));
+        return ret;
+    }
+
+    ret = snd_mixer_load(*mixer);
+    if (ret < 0)
+    {
+        snd_mixer_detach(*mixer, card);
+        snd_mixer_close(*mixer);
+        _ERROR("failed to load hardware mixer controls: %s", snd_strerror(ret));
+        return ret;
+    }
+
+    return 0;
+}
+
+gint
+alsaplug_mixer_new(snd_mixer_t **mixer)
+{
+    return alsaplug_mixer_new_for_card(mixer, alsaplug_cfg.mixer_card);
+}
+
+static void
+alsaplug_set_volume(gint l, gint r)
+{
+    snd_mixer_elem_t *elem = alsaplug_guess_mixer_elem(amixer);
+
+    if (elem == NULL)
+        return;
+
+    if (snd_mixer_selem_is_playback_mono(elem))
+    {
+        gint vol = (l > r) ? l : r;
+
+        snd_mixer_selem_set_playback_volume(elem, SND_MIXER_SCHN_MONO, vol);
+
+        if (snd_mixer_selem_has_playback_switch(elem))
+            snd_mixer_selem_set_playback_switch(elem, SND_MIXER_SCHN_MONO, vol != 0);
+    }
+    else
+    {
+        snd_mixer_selem_set_playback_volume(elem, SND_MIXER_SCHN_FRONT_LEFT, l);
+        snd_mixer_selem_set_playback_volume(elem, SND_MIXER_SCHN_FRONT_RIGHT, r);
+
+        if (snd_mixer_selem_has_playback_switch(elem) && !snd_mixer_selem_has_playback_switch_joined(elem))
+        {
+            snd_mixer_selem_set_playback_switch(elem, SND_MIXER_SCHN_FRONT_LEFT, l != 0);
+            snd_mixer_selem_set_playback_switch(elem, SND_MIXER_SCHN_FRONT_RIGHT, r != 0);
+        }
+    }
+
+    snd_mixer_handle_events(amixer);
+}
+
+static void
+alsaplug_get_volume(gint *l, gint *r)
+{
+    snd_mixer_elem_t *elem = alsaplug_guess_mixer_elem(amixer);
+
+    if (elem == NULL)
+        return;
+
+    snd_mixer_handle_events(amixer);
+
+    *l = 0;
+    *r = 0;
+
+    if (snd_mixer_selem_is_playback_mono(elem))
+    {
+        snd_mixer_selem_get_playback_volume(elem, SND_MIXER_SCHN_MONO, (glong *) l);
+        snd_mixer_selem_get_playback_volume(elem, SND_MIXER_SCHN_MONO, (glong *) r);
+    }
+    else
+    {
+        snd_mixer_selem_get_playback_volume(elem, SND_MIXER_SCHN_FRONT_LEFT, (glong *) l);
+        snd_mixer_selem_get_playback_volume(elem, SND_MIXER_SCHN_FRONT_RIGHT, (glong *) r);
+    }
+}
+
+/********************************************************************************
+ * ALSA PCM I/O functions.                                                      *
+ ********************************************************************************/
+
+static void
+alsaplug_write_buffer(gpointer data, gint length)
+{
+    snd_pcm_sframes_t wr_frames;
+
+    while (length > 0)
+    {
+        gint frames = snd_pcm_bytes_to_frames(pcm_handle, length);
+        wr_frames = snd_pcm_writei(pcm_handle, data, frames);
+
+        if (wr_frames > 0)
+        {
+            gint written = snd_pcm_frames_to_bytes(pcm_handle, wr_frames);
+            length -= written;
+            data += written;
+        }
+        else
+        {
+            gint err = snd_pcm_recover(pcm_handle, wr_frames, 1);
+            if (err < 0)
+                _ERROR("(write) snd_pcm_recover: %s", snd_strerror(err));
+
+            return;
+        }
+    }
+}
+
+static gpointer
+alsaplug_loop(gpointer unused)
+{
+    guchar buf[2048];
+    int size;
+
+    while (pcm_going)
+    {
+        g_mutex_lock (pcm_state_mutex);
+
+        if (flush_request != -1)
+        {
+            alsaplug_ringbuffer_reset (& pcm_ringbuf);
+            snd_pcm_drop(pcm_handle);
+            snd_pcm_prepare(pcm_handle);
+            wr_total = flush_request * (long long) bps / 1000;
+            flush_request = -1;
+
+            g_cond_broadcast(pcm_flush_cond);
+        }
+
+        size = alsaplug_ringbuffer_used (& pcm_ringbuf);
+
+        if (size == 0 || paused)
+        {
+            g_cond_wait (pcm_state_cond, pcm_state_mutex);
+            g_mutex_unlock (pcm_state_mutex);
+            continue;
+        }
+
+        if (size > sizeof buf)
+            size = sizeof buf;
+
+        alsaplug_ringbuffer_read (& pcm_ringbuf, buf, size);
+        g_mutex_unlock (pcm_state_mutex);
+        alsaplug_write_buffer (buf, size);
+    }
+
+    snd_pcm_drain(pcm_handle);
+    snd_pcm_close(pcm_handle);
+    pcm_handle = NULL;
+    audio_thread = NULL;
+    alsaplug_ringbuffer_destroy(&pcm_ringbuf);
+
+    return NULL;
+}
+
+/********************************************************************************
+ * Output Plugin API implementation.                                            *
+ ********************************************************************************/
+
+/*static OutputPluginInitStatus*/
+void alsaplug_init(void)
+{
+    gint card = -1;
+
+    pcm_state_mutex = g_mutex_new();
+    pcm_state_cond = g_cond_new();
+    pcm_flush_cond = g_cond_new();
+
+    /*    if (snd_card_next(&card) != 0)
+          return OUTPUT_PLUGIN_INIT_NO_DEVICES;*/
+
+    alsaplug_get_config();
+    if (alsaplug_cfg.pcm_device == NULL)
+        alsaplug_cfg.pcm_device = g_strdup("default");
+    if (alsaplug_cfg.mixer_card == NULL)
+        alsaplug_cfg.mixer_card = g_strdup("default");
+
+    /*    return OUTPUT_PLUGIN_INIT_FOUND_DEVICES;*/
+}
+
+static gint
+alsaplug_open_audio(AFormat fmt, gint rate, gint nch)
+{
+    gint err, bitwidth, ringbuf_size;
+    snd_pcm_format_t afmt;
+    snd_pcm_hw_params_t *hwparams = NULL;
+
+    afmt = alsaplug_format_convert(fmt);
+    if (afmt == SND_PCM_FORMAT_UNKNOWN)
+    {
+        _ERROR("unsupported format requested: %d -> %d", fmt, afmt);
+        return -1;
+    }
+
+    if (!alsaplug_mixer_new(&amixer))
+        mixer_ready = TRUE;
+
+    if ((err = snd_pcm_open(&pcm_handle, alsaplug_cfg.pcm_device, SND_PCM_STREAM_PLAYBACK, 0)) < 0)
+    {
+        _ERROR("snd_pcm_open: %s", snd_strerror(err));
+        pcm_handle = NULL;
+        return -1;
+    }
+
+    snd_pcm_hw_params_alloca(&hwparams);
+    snd_pcm_hw_params_any(pcm_handle, hwparams);
+    snd_pcm_hw_params_set_access(pcm_handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED);
+    snd_pcm_hw_params_set_format(pcm_handle, hwparams, afmt);
+    snd_pcm_hw_params_set_channels(pcm_handle, hwparams, nch);
+    snd_pcm_hw_params_set_rate(pcm_handle, hwparams, rate, 0);
+
+    err = snd_pcm_hw_params(pcm_handle, hwparams);
+    if (err < 0)
+    {
+        _ERROR("snd_pcm_hw_params failed: %s", snd_strerror(err));
+        return -1;
+    }
+
+    bitwidth = snd_pcm_format_physical_width(afmt);
+    bps = (rate * bitwidth * nch) >> 3;
+    ringbuf_size = aud_cfg->output_buffer_size * bps / 1000;
+    if (alsaplug_ringbuffer_init(&pcm_ringbuf, ringbuf_size) == -1) {
+        _ERROR("alsaplug_ringbuffer_init failed");
+        return -1;
+    }
+    pcm_going = TRUE;
+    flush_request = -1;
+
+    audio_thread = g_thread_create(alsaplug_loop, NULL, TRUE, NULL);
+    return 1;
+}
+
+static void
+alsaplug_close_audio(void)
+{
+    g_mutex_lock(pcm_state_mutex);
+
+    pcm_going = FALSE;
+    wr_total = 0;
+    wr_hwframes = 0;
+    bps = 0;
+
+    g_cond_broadcast (pcm_state_cond);
+    g_mutex_unlock(pcm_state_mutex);
+
+    if (audio_thread != NULL)
+        g_thread_join(audio_thread);
+
+    audio_thread = NULL;
+
+    if (mixer_ready == TRUE)
+    {
+        snd_mixer_detach(amixer, alsaplug_cfg.mixer_card);
+        snd_mixer_close(amixer);
+
+        amixer = NULL;
+        mixer_ready = FALSE;
+    }
+}
+
+static void
+alsaplug_write_audio(gpointer data, gint length)
+{
+    g_mutex_lock(pcm_state_mutex);
+    wr_total += length;
+    alsaplug_ringbuffer_write(&pcm_ringbuf, data, length);
+    g_cond_broadcast (pcm_state_cond);
+    g_mutex_unlock(pcm_state_mutex);
+}
+
+static gint
+alsaplug_output_time(void)
+{
+    gint ret = 0;
+    snd_pcm_sframes_t delay;
+    gsize bytes = wr_total;
+
+    g_mutex_lock(pcm_state_mutex);
+
+    if (pcm_going && pcm_handle != NULL)
+    {
+        guint d = alsaplug_ringbuffer_used(&pcm_ringbuf);
+
+        if (!snd_pcm_delay(pcm_handle, &delay))
+            d += snd_pcm_frames_to_bytes(pcm_handle, delay);
+
+        if (bytes < d)
+            bytes = 0;
+        else
+            bytes -= d;
+
+        ret = bytes * (long long) 1000 / bps;
+    }
+
+    g_mutex_unlock(pcm_state_mutex);
+
+    return ret;
+}
+
+static gint
+alsaplug_written_time(void)
+{
+    gint ret = 0;
+
+    g_mutex_lock(pcm_state_mutex);
+
+    if (pcm_going)
+        ret = wr_total * (long long) 1000 / bps;
+
+    g_mutex_unlock(pcm_state_mutex);
+
+    return ret;
+}
+
+static gint
+alsaplug_buffer_free(void)
+{
+    gint ret;
+
+    g_mutex_lock(pcm_state_mutex);
+
+    if (pcm_going == FALSE)
+        ret = 0;
+    else
+        ret = alsaplug_ringbuffer_free(&pcm_ringbuf);
+
+    g_mutex_unlock(pcm_state_mutex);
+
+    return ret;
+}
+
+static void
+alsaplug_flush(gint time)
+{
+    /* make the request... */
+    g_mutex_lock(pcm_state_mutex);
+    flush_request = time;
+    g_cond_broadcast(pcm_state_cond);
+
+    /* ...then wait for the transaction to complete. */
+    g_cond_wait(pcm_flush_cond, pcm_state_mutex);
+    g_mutex_unlock(pcm_state_mutex);
+}
+
+static gint
+alsaplug_buffer_playing(void)
+{
+    gint ret;
+
+    g_mutex_lock(pcm_state_mutex);
+
+    if (pcm_going == FALSE)
+        ret = 0;
+    else
+        ret = alsaplug_ringbuffer_used(&pcm_ringbuf) != 0;
+
+    g_mutex_unlock(pcm_state_mutex);
+
+    return ret;
+}
+
+static void
+alsaplug_pause(short p)
+{
+    g_mutex_lock (pcm_state_mutex);
+    paused = p;
+    g_cond_broadcast (pcm_state_cond);
+    g_mutex_unlock (pcm_state_mutex);
+}
+
+/********************************************************************************
+ * Plugin glue.                                                                 *
+ ********************************************************************************/
+
+static OutputPlugin alsa_op = {
+    .description = "ALSA Output Plugin (-ng)",
+    /*    .probe_priority = 1,*/
+    .init = alsaplug_init,
+    .open_audio = alsaplug_open_audio,
+    .close_audio = alsaplug_close_audio,
+    .write_audio = alsaplug_write_audio,
+    .output_time = alsaplug_output_time,
+    .written_time = alsaplug_written_time,
+    .buffer_free = alsaplug_buffer_free,
+    .buffer_playing = alsaplug_buffer_playing,
+    .flush = alsaplug_flush,
+    .pause = alsaplug_pause,
+    .set_volume = alsaplug_set_volume,
+    .get_volume = alsaplug_get_volume,
+    .configure = alsaplug_configure,
+};
+
+OutputPlugin *alsa_oplist[] = { &alsa_op, NULL };
+SIMPLE_OUTPUT_PLUGIN(alsa, alsa_oplist);
diff -Nur audacious-plugins-fedora-1.5.1-orig/src/alsa/alsa-debug.h audacious-plugins-fedora-1.5.1/src/alsa/alsa-debug.h
--- audacious-plugins-fedora-1.5.1-orig/src/alsa/alsa-debug.h	1970-01-01 01:00:00.000000000 +0100
+++ audacious-plugins-fedora-1.5.1/src/alsa/alsa-debug.h	2009-06-29 18:01:20.000000000 +0200
@@ -0,0 +1,36 @@
+/*
+ *  Copyright (C) 2005 Ralf Ertzinger
+ *
+ *  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 DEBUG_H
+#define DEBUG_H
+
+#include <stdio.h>
+
+#define _ENTER _DEBUG("enter")
+#define _LEAVE _DEBUG("leave"); return
+#define _MESSAGE(tag, string, ...) do { fprintf(stderr, "%s: ALSA: %s:%d (%s): " string "\n", \
+    tag, __FILE__, __LINE__, __func__, ##__VA_ARGS__); } while(0)
+
+#define _ERROR(...) _MESSAGE("ERROR", __VA_ARGS__)
+
+#ifdef ALSA_DEBUG
+#define _DEBUG(...) _MESSAGE("DEBUG",  __VA_ARGS__)
+#else
+#define _DEBUG(...) {}
+#endif
+
+#endif
diff -Nur audacious-plugins-fedora-1.5.1-orig/src/alsa/alsa.h audacious-plugins-fedora-1.5.1/src/alsa/alsa.h
--- audacious-plugins-fedora-1.5.1-orig/src/alsa/alsa.h	2008-06-08 10:37:44.000000000 +0200
+++ audacious-plugins-fedora-1.5.1/src/alsa/alsa.h	1970-01-01 01:00:00.000000000 +0100
@@ -1,79 +0,0 @@
-/*  XMMS - ALSA output plugin
- *  Copyright (C) 2001-2003 Matthieu Sozeau
- *  Copyright (C) 1998-2003  Peter Alm, Mikael Alm, Olle Hallnas,
- *                           Thomas Nilsson and 4Front Technologies
- *  Copyright (C) 1999-2004  Håvard Kvålen
- *
- *  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 ALSA_H
-#define ALSA_H
-
-#define NDEBUG
-
-#include "config.h"
-
-#include <audacious/plugin.h>
-#include <audacious/i18n.h>
-
-#define ALSA_PCM_NEW_HW_PARAMS_API
-#define ALSA_PCM_NEW_SW_PARAMS_API
-#include <alsa/asoundlib.h>
-#include <alsa/pcm_plugin.h>
-
-#include <gtk/gtk.h>
-
-#define ALSA_CFGID  "ALSA"
-
-extern OutputPlugin op;
-
-struct alsa_config
-{
-	char *pcm_device;
-	int mixer_card;
-	char *mixer_device;
-	int buffer_time;
-	int period_time;
-	gboolean debug;
-	struct
-	{
-		int left, right;
-	} vol;
-};
-
-extern struct alsa_config alsa_cfg;
-
-void alsa_about(void);
-void alsa_configure(void);
-int alsa_get_mixer(snd_mixer_t **mixer, int card);
-void alsa_save_config(void);
-
-void alsa_get_volume(int *l, int *r);
-void alsa_set_volume(int l, int r);
-
-int alsa_playing(void);
-int alsa_free(void);
-void alsa_write(void *ptr, int length);
-void alsa_close(void);
-void alsa_flush(int time);
-void alsa_pause(short p);
-int alsa_open(AFormat fmt, int rate, int nch);
-int alsa_get_output_time(void);
-int alsa_get_written_time(void);
-void alsa_tell(AFormat * fmt, gint * rate, gint * nch);
-
-extern GStaticMutex alsa_mutex;
-
-#endif
diff -Nur audacious-plugins-fedora-1.5.1-orig/src/alsa/alsa-ringbuffer.c audacious-plugins-fedora-1.5.1/src/alsa/alsa-ringbuffer.c
--- audacious-plugins-fedora-1.5.1-orig/src/alsa/alsa-ringbuffer.c	1970-01-01 01:00:00.000000000 +0100
+++ audacious-plugins-fedora-1.5.1/src/alsa/alsa-ringbuffer.c	2009-06-29 18:01:20.000000000 +0200
@@ -0,0 +1,366 @@
+/*
+ *  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.
+ */
+
+/*
+ * Ringbuffer implementation
+ *
+ * GPL
+ */
+#include <string.h>
+#include "alsa-ringbuffer.h"
+#include "alsa-debug.h"
+
+#ifdef ALSAPLUG_RINGBUFFER_DEBUG
+/*
+ * An internal assertion function to make sure that the
+ * ringbuffer structure is consistient.
+ *
+ * WARNING: This function will call abort() if the ringbuffer
+ * is found to be inconsistient.
+ */
+static void _alsaplug_ringbuffer_assert(alsaplug_ringbuf_t* rb) {
+
+    unsigned int realused;
+
+    _ENTER;
+
+    _DEBUG("rb->buf=%p, rb->end=%p, rb->wp=%p, rb->rp=%p, rb->free=%u, rb->used=%u, rb->size=%u",
+            rb->buf, rb->end, rb->wp, rb->rp, rb->free, rb->used, rb->size);
+
+    if (0 == rb->size) {
+        _ERROR("Buffer size is 0");
+        abort();
+    }
+
+    if (NULL == rb->buf) {
+        _ERROR("Buffer start is NULL");
+        abort();
+    }
+
+    if (rb->used+rb->free != rb->size) {
+        _ERROR("rb->free and rb->used do not add up to rb->size");
+        abort();
+    }
+
+    if (rb->buf+(rb->size-1) != rb->end) {
+        _ERROR("rb->buf and rb->end not rb->size bytes apart");
+        abort();
+    }
+
+    if ((rb->wp < rb->buf) || (rb->wp > rb->end)) {
+        _ERROR("Write pointer outside buffer space");
+        abort();
+    }
+
+    if ((rb->rp < rb->buf) || (rb->rp > rb->end)) {
+        _ERROR("Read pointer outside buffer space");
+        abort();
+    }
+
+    if (rb->rp <= rb->wp) {
+        realused = rb->wp - rb->rp;
+    } else {
+        realused = (rb->end - rb->rp) + 1 + (rb->wp-rb->buf);
+    }
+
+    if (rb->used != realused) {
+        _ERROR("Usage count is inconsistient (is %d, should be %d)", rb->used, realused);
+        abort();
+    }
+
+    _LEAVE;
+}
+#endif
+
+/*
+ * Reset a ringbuffer structure (i.e. discard
+ * all data inside of it)
+ */
+void alsaplug_ringbuffer_reset(alsaplug_ringbuf_t* rb) {
+
+    _ENTER;
+
+    _ALSAPLUG_RINGBUFFER_LOCK(rb->lock);
+
+    rb->wp = rb->buf;
+    rb->rp = rb->buf;
+    rb->free = rb->size;
+    rb->used = 0;
+    rb->end = rb->buf+(rb->size-1);
+
+    _ALSAPLUG_RINGBUFFER_UNLOCK(rb->lock);
+
+    _LEAVE;
+}
+
+/* 
+ * Initialize a ringbuffer structure (including
+ * memory allocation.
+ *
+ * Return -1 on error
+ */
+int alsaplug_ringbuffer_init(alsaplug_ringbuf_t* rb, unsigned int size) {
+
+    _ENTER;
+
+    if (0 == size) {
+        _LEAVE -1;
+    }
+
+    if (NULL == (rb->buf = malloc(size))) {
+        _LEAVE -1;
+    }
+    rb->size = size;
+
+    if (NULL == (rb->lock = g_mutex_new())) {
+        _LEAVE -1;
+    }
+
+    rb->_free_lock = 1;
+
+    alsaplug_ringbuffer_reset(rb);
+
+    ASSERT_RB(rb);
+
+    _LEAVE 0;
+}
+
+/* 
+ * Initialize a ringbuffer structure (including
+ * memory allocation.
+ * The mutex to be used is passed in the function call.
+ * The mutex must not be held while calling this function.
+ *
+ * Return -1 on error
+ */
+int alsaplug_ringbuffer_init_with_lock(alsaplug_ringbuf_t* rb, unsigned int size, alsaplug_ringbuffer_mutex_t* lock) {
+
+    _ENTER;
+
+    if (0 == size) {
+        _LEAVE -1;
+    }
+
+    rb->lock = lock;
+    rb->_free_lock = 0;
+
+    if (NULL == (rb->buf = malloc(size))) {
+        _LEAVE -1;
+    }
+    rb->size = size;
+    alsaplug_ringbuffer_reset(rb);
+
+    ASSERT_RB(rb);
+
+    _LEAVE 0;
+}
+
+/*
+ * Write size bytes at buf into the ringbuffer.
+ * Return -1 on error (not enough space in buffer)
+ */
+int alsaplug_ringbuffer_write(alsaplug_ringbuf_t* rb, void* buf, unsigned int size) {
+
+    int ret = -1;
+    int endfree;
+
+    _ENTER;
+
+    _ALSAPLUG_RINGBUFFER_LOCK(rb->lock);
+
+    ASSERT_RB(rb);
+
+    if (rb->free < size) {
+        ret = -1;
+        goto out;
+    }
+
+    endfree = (rb->end - rb->wp)+1;
+    if (endfree < size) {
+        /*
+         * There is enough space in the buffer, but not in
+         * one piece. We need to split the copy into two parts.
+         */
+        memcpy(rb->wp, buf, endfree);
+        memcpy(rb->buf, buf+endfree, size-endfree);
+        rb->wp = rb->buf + (size-endfree);
+    } else if (endfree > size) {
+        /*
+         * There is more space than needed at the end
+         */
+        memcpy(rb->wp, buf, size);
+        rb->wp += size;
+    } else {
+        /*
+         * There is exactly the space needed at the end.
+         * We need to wrap around the read pointer.
+         */
+        memcpy(rb->wp, buf, size);
+        rb->wp = rb->buf;
+    }
+
+    rb->free -= size;
+    rb->used += size;
+
+    ret = 0;
+
+out:
+    ASSERT_RB(rb);
+    _ALSAPLUG_RINGBUFFER_UNLOCK(rb->lock);
+
+    _LEAVE ret;
+}
+
+/*
+ * Read size byes from buffer into buf.
+ * Return -1 on error (not enough data in buffer)
+ */
+int alsaplug_ringbuffer_read(alsaplug_ringbuf_t* rb, void* buf, unsigned int size) {
+
+    int ret;
+
+    _ENTER;
+
+    _ALSAPLUG_RINGBUFFER_LOCK(rb->lock);
+    ret = alsaplug_ringbuffer_read_locked(rb, buf, size);
+    _ALSAPLUG_RINGBUFFER_UNLOCK(rb->lock);
+
+    _LEAVE ret;
+}
+
+/*
+ * Read size bytes from buffer into buf, assuming the buffer lock
+ * is already held.
+ * Return -1 on error (not enough data in buffer)
+ */
+int alsaplug_ringbuffer_read_locked(alsaplug_ringbuf_t* rb, void* buf, unsigned int size) {
+
+    int endused;
+
+    _ENTER;
+
+    ASSERT_RB(rb);
+
+    if (rb->used < size) {
+        /* Not enough bytes in buffer */
+        _LEAVE -1;
+    }
+
+    if (rb->rp < rb->wp) {
+        /*
+        Read pointer is behind write pointer, all the data is available in one chunk
+        */
+        memcpy(buf, rb->rp, size);
+        rb->rp += size;
+    } else {
+        /*
+         * Read pointer is before write pointer
+         */
+        endused = (rb->end - rb->rp)+1;
+
+        if (size < endused) {
+            /*
+             * Data is available in one chunk
+             */
+            memcpy(buf, rb->rp, size);
+            rb->rp += size;
+        } else {
+            /*
+             * There is enough data in the buffer, but it is fragmented.
+             */
+            memcpy(buf, rb->rp, endused);
+            memcpy(buf+endused, rb->buf, size-endused);
+            rb->rp = rb->buf + (size-endused);
+        }
+    }
+
+    rb->free += size;
+    rb->used -= size;
+
+    ASSERT_RB(rb);
+
+    _LEAVE 0;
+}
+
+/*
+ * Return the amount of free space currently in the rb
+ */
+unsigned int alsaplug_ringbuffer_free(alsaplug_ringbuf_t* rb) {
+
+    unsigned int f;
+
+    _ENTER;
+
+    _ALSAPLUG_RINGBUFFER_LOCK(rb->lock);
+    f = alsaplug_ringbuffer_free_locked(rb);
+    _ALSAPLUG_RINGBUFFER_UNLOCK(rb->lock);
+
+    _LEAVE f;
+}
+
+/*
+ * Return the amount of free space currently in the rb.
+ * Assume the rb lock is already being held.
+ */
+unsigned int alsaplug_ringbuffer_free_locked(alsaplug_ringbuf_t* rb) {
+
+    _ENTER;
+
+    _LEAVE rb->free;
+}
+
+
+/*
+ * Return the amount of used space currently in the rb
+ */
+unsigned int alsaplug_ringbuffer_used(alsaplug_ringbuf_t* rb) {
+
+    unsigned int u;
+
+    _ENTER;
+
+    _ALSAPLUG_RINGBUFFER_LOCK(rb->lock);
+    u = alsaplug_ringbuffer_used_locked(rb);
+    _ALSAPLUG_RINGBUFFER_UNLOCK(rb->lock);
+
+    _LEAVE u;
+}
+
+/*
+ * Return the amount of used space currently in the rb.
+ * Assume the rb lock is already being held.
+ */
+unsigned int alsaplug_ringbuffer_used_locked(alsaplug_ringbuf_t* rb) {
+
+    _ENTER;
+
+    _LEAVE rb->used;
+}
+
+
+/*
+ * destroy a ringbuffer
+ */
+void alsaplug_ringbuffer_destroy(alsaplug_ringbuf_t* rb) {
+
+    _ENTER;
+    free(rb->buf);
+    if (rb->_free_lock) {
+        g_mutex_free(rb->lock);
+    }
+
+    _LEAVE;
+}
diff -Nur audacious-plugins-fedora-1.5.1-orig/src/alsa/alsa-ringbuffer.h audacious-plugins-fedora-1.5.1/src/alsa/alsa-ringbuffer.h
--- audacious-plugins-fedora-1.5.1-orig/src/alsa/alsa-ringbuffer.h	1970-01-01 01:00:00.000000000 +0100
+++ audacious-plugins-fedora-1.5.1/src/alsa/alsa-ringbuffer.h	2009-06-29 18:01:20.000000000 +0200
@@ -0,0 +1,58 @@
+/*
+ *  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 _ALSAPLUG_RINGBUFFER_H
+#define _ALSAPLUG_RINGBUFFER_H
+
+#include <glib.h>
+
+typedef GMutex alsaplug_ringbuffer_mutex_t;
+#define _ALSAPLUG_RINGBUFFER_LOCK(L) g_mutex_lock(L)
+#define _ALSAPLUG_RINGBUFFER_UNLOCK(L) g_mutex_unlock(L)
+
+#include <stdlib.h>
+
+#ifdef ALSAPLUG_RINGBUFFER_DEBUG
+#define ASSERT_RB(buf) _alsaplug_ringbuffer_assert(buf)
+#else
+#define ASSERT_RB(buf)
+#endif
+
+typedef struct {
+    alsaplug_ringbuffer_mutex_t* lock;
+    char _free_lock;
+    char* buf;
+    char* end;
+    char* wp;
+    char* rp;
+    unsigned int free;
+    unsigned int used;
+    unsigned int size;
+} alsaplug_ringbuf_t;
+
+int alsaplug_ringbuffer_init(alsaplug_ringbuf_t* rb, unsigned int size);
+int alsaplug_ringbuffer_init_with_lock(alsaplug_ringbuf_t* rb, unsigned int size, alsaplug_ringbuffer_mutex_t* lock);
+int alsaplug_ringbuffer_write(alsaplug_ringbuf_t* rb, void* buf, unsigned int size);
+int alsaplug_ringbuffer_read(alsaplug_ringbuf_t* rb, void* buf, unsigned int size);
+int alsaplug_ringbuffer_read_locked(alsaplug_ringbuf_t* rb, void* buf, unsigned int size);
+void alsaplug_ringbuffer_reset(alsaplug_ringbuf_t* rb);
+unsigned int alsaplug_ringbuffer_free(alsaplug_ringbuf_t* rb);
+unsigned int alsaplug_ringbuffer_free_locked(alsaplug_ringbuf_t* rb);
+unsigned int alsaplug_ringbuffer_used(alsaplug_ringbuf_t* rb);
+unsigned int alsaplug_ringbuffer_used_locked(alsaplug_ringbuf_t* rb);
+void alsaplug_ringbuffer_destroy(alsaplug_ringbuf_t* rb);
+
+#endif
diff -Nur audacious-plugins-fedora-1.5.1-orig/src/alsa/alsa-stdinc.h audacious-plugins-fedora-1.5.1/src/alsa/alsa-stdinc.h
--- audacious-plugins-fedora-1.5.1-orig/src/alsa/alsa-stdinc.h	1970-01-01 01:00:00.000000000 +0100
+++ audacious-plugins-fedora-1.5.1/src/alsa/alsa-stdinc.h	2009-06-29 18:01:20.000000000 +0200
@@ -0,0 +1,36 @@
+/*
+ * Audacious ALSA Plugin (-ng)
+ * Copyright (c) 2009 William Pitcock <nenolod@dereferenced.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef _ALSA_STDINC_H_GUARD
+#define _ALSA_STDINC_H_GUARD
+
+#include <audacious/plugin.h>
+
+#define ALSA_PCM_NEW_HW_PARAMS_API
+#define ALSA_PCM_NEW_SW_PARAMS_API
+#include <alsa/asoundlib.h>
+#include <alsa/pcm_plugin.h>
+
+#include <gtk/gtk.h>
+
+#include "alsa-debug.h"
+#include "alsa-types.h"
+#include "alsa-ringbuffer.h"
+
+#endif
diff -Nur audacious-plugins-fedora-1.5.1-orig/src/alsa/alsa-types.h audacious-plugins-fedora-1.5.1/src/alsa/alsa-types.h
--- audacious-plugins-fedora-1.5.1-orig/src/alsa/alsa-types.h	1970-01-01 01:00:00.000000000 +0100
+++ audacious-plugins-fedora-1.5.1/src/alsa/alsa-types.h	2009-06-29 18:01:20.000000000 +0200
@@ -0,0 +1,40 @@
+/*
+ * Audacious ALSA Plugin (-ng)
+ * Copyright (c) 2009 William Pitcock <nenolod@dereferenced.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef _ALSA_TYPES_H_GUARD
+#define _ALSA_TYPES_H_GUARD
+
+#include "alsa-stdinc.h"
+
+typedef struct {
+    AFormat aud_fmt;
+    snd_pcm_format_t alsa_fmt;
+} alsaplug_format_mapping_t;
+
+extern snd_pcm_format_t alsaplug_format_convert(AFormat aud_fmt);
+
+typedef struct {
+     gchar *pcm_device;
+     gchar *mixer_card;
+     gchar *mixer_device;
+} alsaplug_cfg_t;
+
+extern alsaplug_cfg_t alsaplug_cfg;
+
+#endif
diff -Nur audacious-plugins-fedora-1.5.1-orig/src/alsa/alsa-util.c audacious-plugins-fedora-1.5.1/src/alsa/alsa-util.c
--- audacious-plugins-fedora-1.5.1-orig/src/alsa/alsa-util.c	1970-01-01 01:00:00.000000000 +0100
+++ audacious-plugins-fedora-1.5.1/src/alsa/alsa-util.c	2009-06-29 18:01:20.000000000 +0200
@@ -0,0 +1,55 @@
+/*
+ * Audacious ALSA Plugin (-ng)
+ * Copyright (c) 2009 William Pitcock <nenolod@dereferenced.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "alsa-stdinc.h"
+
+static alsaplug_format_mapping_t alsaplug_format_conv_tbl[] = {
+    {FMT_FLOAT,  SND_PCM_FORMAT_FLOAT},
+    {FMT_S32_LE, SND_PCM_FORMAT_S32_LE},
+    {FMT_S32_BE, SND_PCM_FORMAT_S32_BE},
+    {FMT_S32_NE, SND_PCM_FORMAT_S32},
+    {FMT_S24_LE, SND_PCM_FORMAT_S24_LE},
+    {FMT_S24_BE, SND_PCM_FORMAT_S24_BE},
+    {FMT_S24_NE, SND_PCM_FORMAT_S24},
+    {FMT_U24_LE, SND_PCM_FORMAT_U24_LE},
+    {FMT_U24_BE, SND_PCM_FORMAT_U24_BE},
+    {FMT_U24_NE, SND_PCM_FORMAT_U24},
+    {FMT_S16_LE, SND_PCM_FORMAT_S16_LE},
+    {FMT_S16_BE, SND_PCM_FORMAT_S16_BE},
+    {FMT_S16_NE, SND_PCM_FORMAT_S16},
+    {FMT_U16_LE, SND_PCM_FORMAT_U16_LE},
+    {FMT_U16_BE, SND_PCM_FORMAT_U16_BE},
+    {FMT_U16_NE, SND_PCM_FORMAT_U16},
+    {FMT_U8, SND_PCM_FORMAT_U8},
+    {FMT_S8, SND_PCM_FORMAT_S8},
+};
+
+snd_pcm_format_t
+alsaplug_format_convert(AFormat fmt)
+{
+    gint i;
+
+    for (i = 0; i < G_N_ELEMENTS(alsaplug_format_conv_tbl); i++)
+    {
+         if (alsaplug_format_conv_tbl[i].aud_fmt == fmt)
+             return alsaplug_format_conv_tbl[i].alsa_fmt;
+    }
+
+    return SND_PCM_FORMAT_UNKNOWN;
+}
diff -Nur audacious-plugins-fedora-1.5.1-orig/src/alsa/audio.c audacious-plugins-fedora-1.5.1/src/alsa/audio.c
--- audacious-plugins-fedora-1.5.1-orig/src/alsa/audio.c	2008-06-08 10:37:44.000000000 +0200
+++ audacious-plugins-fedora-1.5.1/src/alsa/audio.c	1970-01-01 01:00:00.000000000 +0100
@@ -1,978 +0,0 @@
-/*  XMMS - ALSA output plugin
- *  Copyright (C) 2001-2003 Matthieu Sozeau <mattam@altern.org>
- *  Copyright (C) 1998-2003  Peter Alm, Mikael Alm, Olle Hallnas,
- *                           Thomas Nilsson and 4Front Technologies
- *  Copyright (C) 1999-2006  Haavard Kvaalen
- *  Copyright (C) 2005       Takashi Iwai
- *  Copyright (C) 2007-2008  William Pitcock
- *
- *  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.
- */
-
-/*
- *  CHANGES
- *
- *  2005.01.05  Takashi Iwai <tiwai@suse.de>
- *	Impelemented the multi-threaded mode with an audio-thread.
- *	Many fixes and cleanups.
- */
-
-/*#define AUD_DEBUG*/
-
-#include "alsa.h"
-#include <ctype.h>
-#include <math.h>
-
-static snd_pcm_t *alsa_pcm;
-static snd_output_t *logs;
-
-
-/* Number of bytes that we have received from the input plugin */
-static guint64 alsa_total_written;
-/* Number of bytes that we have sent to the sound card */
-static guint64 alsa_hw_written;
-static guint64 output_time_offset;
-
-/* device buffer/period sizes in bytes */
-static int hw_buffer_size, hw_period_size;		/* in output bytes */
-static int hw_buffer_size_in, hw_period_size_in;	/* in input bytes */
-
-/* Set/Get volume */
-static snd_mixer_elem_t *pcm_element;
-static snd_mixer_t *mixer;
-
-static gboolean going, paused, mixer_start = TRUE;
-static gboolean prebuffer, remove_prebuffer;
-
-static gboolean alsa_can_pause;
-
-/* for audio thread */
-static GThread *audio_thread;	 /* audio loop thread */
-static int thread_buffer_size;	 /* size of intermediate buffer in bytes */
-static char *thread_buffer;	 /* audio intermediate buffer */
-static int rd_index, wr_index;	 /* current read/write position in int-buffer */
-static gboolean pause_request;	 /* pause status currently requested */
-static int flush_request;	 /* flush status (time) currently requested */
-static int prebuffer_size;
-GStaticMutex alsa_mutex = G_STATIC_MUTEX_INIT;
-
-struct snd_format {
-	unsigned int rate;
-	unsigned int channels;
-	snd_pcm_format_t format;
-	AFormat xmms_format;
-	int sample_bits;
-	int bps;
-};
-
-static struct snd_format *inputf = NULL;
-static struct snd_format *outputf = NULL;
-
-static int alsa_setup(struct snd_format *f);
-static void alsa_write_audio(char *data, int length);
-static void alsa_cleanup_mixer(void);
-static int get_thread_buffer_filled(void);
-
-static struct snd_format * snd_format_from_xmms(AFormat fmt, int rate, int channels);
-
-static const struct {
-	AFormat xmms;
-	snd_pcm_format_t alsa;
-} format_table[] =
-{
-#if 0
-/* i don't know if we will ever do this --nenolod */
- {FMT_S32_LE, SND_PCM_FORMAT_S32_LE},
- {FMT_S32_BE, SND_PCM_FORMAT_S32_BE},
- {FMT_S32_NE, SND_PCM_FORMAT_S32},
-#endif
- {FMT_S24_LE, SND_PCM_FORMAT_S24_LE},
- {FMT_S24_BE, SND_PCM_FORMAT_S24_BE},
- {FMT_S24_NE, SND_PCM_FORMAT_S24},
- {FMT_U24_LE, SND_PCM_FORMAT_U24_LE},
- {FMT_U24_BE, SND_PCM_FORMAT_U24_BE},
- {FMT_U24_NE, SND_PCM_FORMAT_U24},
- {FMT_S16_LE, SND_PCM_FORMAT_S16_LE},
- {FMT_S16_BE, SND_PCM_FORMAT_S16_BE},
- {FMT_S16_NE, SND_PCM_FORMAT_S16},
- {FMT_U16_LE, SND_PCM_FORMAT_U16_LE},
- {FMT_U16_BE, SND_PCM_FORMAT_U16_BE},
- {FMT_U16_NE, SND_PCM_FORMAT_U16},
- {FMT_U8, SND_PCM_FORMAT_U8},
- {FMT_S8, SND_PCM_FORMAT_S8},
-};
-
-
-static void debug(char *str, ...) G_GNUC_PRINTF(1, 2);
-
-static void debug(char *str, ...)
-{
-	va_list args;
-
-	if (alsa_cfg.debug)
-	{
-		va_start(args, str);
-		g_logv(NULL, G_LOG_LEVEL_MESSAGE, str, args);
-		va_end(args);
-	}
-}
-
-int alsa_playing(void)
-{
-	if (!going || paused || alsa_pcm == NULL)
-		return FALSE;
-
-	return snd_pcm_state(alsa_pcm) == SND_PCM_STATE_RUNNING &&
-               !paused &&
-               !prebuffer &&
-               get_thread_buffer_filled() > hw_period_size_in;
-}
-
-static int
-alsa_recovery(int err)
-{
-	int err2;
-
-	/* if debug mode is enabled, dump ALSA state to console */
-	if (alsa_cfg.debug)
-	{
-		snd_pcm_status_t *alsa_status = NULL;
-		snd_pcm_status_alloca(&alsa_status);
-		if (snd_pcm_status(alsa_pcm, alsa_status) < 0)
-			g_warning("xrun_recover(): snd_pcm_status() failed");
-		else
-		{
-			printf("Status:\n");
-			snd_pcm_status_dump(alsa_status, logs);
-		}
-	}
-
-	/*
-	 * specifically handle -EPIPE and -ESTRPIPE to recover 
-	 * PCM fragment periods without losing data.
-	 */
-	switch (err)
-	{
-	case -ESTRPIPE:
-	case ESTRPIPE:   /* "suspend": wait until ALSA is "running" again. */
-		while ((err2 = snd_pcm_resume(alsa_pcm)) == -EAGAIN)
-			g_usleep(100000);
-
-		if (err2 < 0)
-			return snd_pcm_prepare(alsa_pcm);
-
-		break;
-
-	case -EPIPE:
-	case EPIPE:      /* under-run and the I/O pipe closed on us */
-		return snd_pcm_prepare(alsa_pcm);
-		break;
-
-	case EINTR:
-	case -EINTR:
-		break;
-
-	default:
-		g_warning("Unhandled ALSA exception code %d (%s), trying hard restart.", err, snd_strerror(err));
-		return snd_pcm_prepare(alsa_pcm);
-		break;
-	}
-
-	return 0;
-}
-
-/* update and get the available space on h/w buffer (in frames) */
-static snd_pcm_sframes_t alsa_get_avail(void)
-{
-	snd_pcm_sframes_t ret;
-
-	if (alsa_pcm == NULL)
-		return 0;
-
-	while ((ret = snd_pcm_avail_update(alsa_pcm)) < 0)
-	{
-		ret = alsa_recovery(ret);
-		if (ret < 0)
-		{
-			g_warning("alsa_get_avail(): snd_pcm_avail_update() failed: %s",
-				  snd_strerror(ret));
-			return 0;
-		}
-	}
-	return ret;
-}
-
-/* get the free space on buffer */
-int alsa_free(void)
-{
-	if (remove_prebuffer && prebuffer)
-	{
-		prebuffer = FALSE;
-		remove_prebuffer = FALSE;
-	}
-	if (prebuffer)
-		remove_prebuffer = TRUE;
-	
-	return thread_buffer_size - get_thread_buffer_filled() - 1;
-}
-
-/* do pause operation */
-static void alsa_do_pause(gboolean p)
-{
-	if (paused == p)
-		return;
-
-	if (alsa_pcm)
-	{
-		if (alsa_can_pause)
-			snd_pcm_pause(alsa_pcm, p);
-		else if (p)
-		{
-			snd_pcm_drop(alsa_pcm);
-			snd_pcm_prepare(alsa_pcm);
-		}
-	}
-	paused = p;
-}
-
-void alsa_pause(short p)
-{
-	debug("alsa_pause");
-	pause_request = p;
-}
-
-/* close PCM and release associated resources */
-static void alsa_close_pcm(void)
-{
-	if (alsa_pcm)
-	{
-		int err;
-		snd_pcm_drop(alsa_pcm);
-		if ((err = snd_pcm_close(alsa_pcm)) < 0)
-			g_warning("alsa_pcm_close() failed: %s",
-				  snd_strerror(err));
-		alsa_pcm = NULL;
-	}
-}
-
-void alsa_close(void)
-{
-	if (!going)
-		return;
-
-	debug("Closing device");
-
-	going = 0;
-
-	g_thread_join(audio_thread);
-
-	g_static_mutex_lock(&alsa_mutex); /* alsa_loop locks alsa_mutex! */
-
-	alsa_cleanup_mixer();
-
-	g_free(inputf);
-	inputf = NULL;
-	g_free(outputf);
-	outputf = NULL;
-
-	alsa_save_config();
-
-	if (alsa_cfg.debug)
-		snd_output_close(logs);
-	debug("Device closed");
-
-	g_static_mutex_unlock(&alsa_mutex);
-}
-
-/* reopen ALSA PCM */
-#if 0
-static int alsa_reopen(struct snd_format *f)
-{
-	/* remember the current position */
-	output_time_offset += (alsa_hw_written * 1000) / outputf->bps;
-	alsa_hw_written = 0;
-
-	alsa_close_pcm();
-
-	return alsa_setup(f);
-}
-#endif
-
-/* do flush (drop) operation */
-static void alsa_do_flush(int time)
-{
-	if (alsa_pcm)
-	{
-		snd_pcm_drop(alsa_pcm);
-		snd_pcm_prepare(alsa_pcm);
-	}
-	/* correct the offset */
-	output_time_offset = time;
-	alsa_total_written = (guint64) time * inputf->bps / 1000;
-	rd_index = wr_index = alsa_hw_written = 0;
-}
-
-void alsa_flush(int time)
-{
-	flush_request = time;
-	while ((flush_request != -1) && (going))
-		g_usleep(10000);
-}
-
-static void parse_mixer_name(char *str, char **name, int *index)
-{
-	char *end;
-
-	while (isspace(*str))
-		str++;
-
-	if ((end = strchr(str, ',')) != NULL)
-	{
-		*name = g_strndup(str, end - str);
-		end++;
-		*index = atoi(end);
-	}
-	else
-	{
-		*name = g_strdup(str);
-		*index = 0;
-	}
-}
-
-int alsa_get_mixer(snd_mixer_t **mixer, int card)
-{
-	char *dev;
-	int err;
-
-	debug("alsa_get_mixer");
-
-	if ((err = snd_mixer_open(mixer, 0)) < 0)
-	{
-		g_warning("alsa_get_mixer(): Failed to open empty mixer: %s",
-			  snd_strerror(err));
-		mixer = NULL;
-		return -1;
-	}
-
-	dev = g_strdup_printf("hw:%i", card);
-	if ((err = snd_mixer_attach(*mixer, dev)) < 0)
-	{
-		g_warning("alsa_get_mixer(): Attaching to mixer %s failed: %s",
-			  dev, snd_strerror(err));
-		g_free(dev);
-		return -1;
-	}
-	g_free(dev);
-
-	if ((err = snd_mixer_selem_register(*mixer, NULL, NULL)) < 0)
-	{
-		g_warning("alsa_get_mixer(): Failed to register mixer: %s",
-			  snd_strerror(err));
-		return -1;
-	}
-	if ((err = snd_mixer_load(*mixer)) < 0)
-	{
-		g_warning("alsa_get_mixer(): Failed to load mixer: %s",
-			  snd_strerror(err));
-		return -1;
-	}
-
-	return (*mixer != NULL);
-}
-
-
-static snd_mixer_elem_t* alsa_get_mixer_elem(snd_mixer_t *mixer, char *name, int index)
-{
-	snd_mixer_selem_id_t *selem_id;
-	snd_mixer_elem_t* elem;
-	snd_mixer_selem_id_alloca(&selem_id);
-
-	if (index != -1)
-		snd_mixer_selem_id_set_index(selem_id, index);
-	if (name != NULL)
-		snd_mixer_selem_id_set_name(selem_id, name);
-
-	elem = snd_mixer_find_selem(mixer, selem_id);
-
-	return elem;
-}
-
-static int alsa_setup_mixer(void)
-{
-	char *name;
-	long int a, b;
-	long alsa_min_vol, alsa_max_vol;
-	int err, index;
-
-	debug("alsa_setup_mixer");
-
-	if ((err = alsa_get_mixer(&mixer, alsa_cfg.mixer_card)) < 0)
-		return err;
-
-	parse_mixer_name(alsa_cfg.mixer_device, &name, &index);
-
-	pcm_element = alsa_get_mixer_elem(mixer, name, index);
-
-	g_free(name);
-
-	if (!pcm_element)
-	{
-		g_warning("alsa_setup_mixer(): Failed to find mixer element: %s",
-			  alsa_cfg.mixer_device);
-		return -1;
-	}
-
-	/*
-	 * Work around a bug in alsa-lib up to 1.0.0rc2 where the
-	 * new range don't take effect until the volume is changed.
-	 * This hack should be removed once we depend on Alsa 1.0.0.
-	 */
-	snd_mixer_selem_get_playback_volume(pcm_element,
-					    SND_MIXER_SCHN_FRONT_LEFT, &a);
-	snd_mixer_selem_get_playback_volume(pcm_element,
-					    SND_MIXER_SCHN_FRONT_RIGHT, &b);
-
-	snd_mixer_selem_get_playback_volume_range(pcm_element,
-						  &alsa_min_vol, &alsa_max_vol);
-	snd_mixer_selem_set_playback_volume_range(pcm_element, 0, 100);
-
-	if (alsa_max_vol == 0)
-	{
-		pcm_element = NULL;
-		return -1;
-	}
-
-	alsa_set_volume(a * 100 / alsa_max_vol, b * 100 / alsa_max_vol);
-
-	debug("alsa_setup_mixer: end");
-
-	return 0;
-}
-
-static void alsa_cleanup_mixer(void)
-{
-	pcm_element = NULL;
-	if (mixer)
-	{
-		snd_mixer_close(mixer);
-		mixer = NULL;
-	}
-}
-
-void alsa_get_volume(int *l, int *r)
-{
-	long ll = *l, lr = *r;
-
-	if (mixer_start)
-	{
-		alsa_setup_mixer();
-		mixer_start = FALSE;
-	}
-
-	if (!pcm_element)
-		return;
-
-	snd_mixer_handle_events(mixer);
-
-	snd_mixer_selem_get_playback_volume(pcm_element,
-					    SND_MIXER_SCHN_FRONT_LEFT,
-					    &ll);
-	snd_mixer_selem_get_playback_volume(pcm_element,
-					    SND_MIXER_SCHN_FRONT_RIGHT,
-					    &lr);
-	*l = ll;
-	*r = lr;
-}
-
-
-void alsa_set_volume(int l, int r)
-{
-	if (!pcm_element)
-		return;
-
-	if (snd_mixer_selem_is_playback_mono(pcm_element))
-	{
-		if (l > r)
-			snd_mixer_selem_set_playback_volume(pcm_element,
-							    SND_MIXER_SCHN_MONO, l);
-		else
-			snd_mixer_selem_set_playback_volume(pcm_element,
-							    SND_MIXER_SCHN_MONO, r);
-	}
-	else
-	{
-		snd_mixer_selem_set_playback_volume(pcm_element,
-						    SND_MIXER_SCHN_FRONT_LEFT, l);
-		snd_mixer_selem_set_playback_volume(pcm_element,
-						    SND_MIXER_SCHN_FRONT_RIGHT, r);
-	}
-
-	if (snd_mixer_selem_has_playback_switch(pcm_element) && !snd_mixer_selem_has_playback_switch_joined(pcm_element))
-	{
-		snd_mixer_selem_set_playback_switch(pcm_element,
-			SND_MIXER_SCHN_FRONT_LEFT, l != 0);
-		snd_mixer_selem_set_playback_switch(pcm_element,
-			SND_MIXER_SCHN_FRONT_RIGHT, r != 0);
-	}
-}
-
-
-/*
- * audio stuff
- */
-
-/* return the size of audio data filled in the audio thread buffer */
-static int get_thread_buffer_filled(void)
-{
-	if (wr_index >= rd_index)
-		return wr_index - rd_index;
-	return thread_buffer_size - (rd_index - wr_index);
-}
-
-int alsa_get_output_time(void)
-{
-	snd_pcm_sframes_t delay;
-	guint64 bytes = alsa_hw_written;
-
-	if (!going || alsa_pcm == NULL)
-		return 0;
-
-	if (!snd_pcm_delay(alsa_pcm, &delay))
-	{
-		unsigned int d = snd_pcm_frames_to_bytes(alsa_pcm, delay);
-		if (bytes < d)
-			bytes = 0;
-		else
-			bytes -= d;
-	}
-	return output_time_offset + (bytes * 1000) / outputf->bps;
-}
-
-int alsa_get_written_time(void)
-{
-	if (!going)
-		return 0;
-	return (alsa_total_written * 1000) / inputf->bps;
-}
-
-/* transfer data to audio h/w; length is given in bytes
- *
- * data can be modified via effect plugin, rate conversion or
- * software volume before passed to audio h/w
- */
-static void alsa_do_write(gpointer data, int length)
-{
-	if (paused)
-		return;
-
-	alsa_write_audio(data, length);
-}
-
-/* write callback */
-void alsa_write(gpointer data, int length)
-{
-	int cnt;
-	char *src = (char *)data;
-	
-	remove_prebuffer = FALSE;
-	
-	alsa_total_written += length;
-	while (length > 0)
-	{
-		int wr;
-		cnt = MIN(length, thread_buffer_size - wr_index);
-		memcpy(thread_buffer + wr_index, src, cnt);
-		wr = (wr_index + cnt) % thread_buffer_size;
-		wr_index = wr;
-		length -= cnt;
-		src += cnt;
-	}
-}
-
-/* transfer data to audio h/w via normal write */
-static void alsa_write_audio(char *data, int length)
-{
-	snd_pcm_sframes_t written_frames;
-
-	while (length > 0)
-	{
-		int frames = snd_pcm_bytes_to_frames(alsa_pcm, length);
-		written_frames = snd_pcm_writei(alsa_pcm, data, frames);
-
-		if (written_frames > 0)
-		{
-			int written = snd_pcm_frames_to_bytes(alsa_pcm,
-							      written_frames);
-			length -= written;
-			data += written;
-			alsa_hw_written += written;
-		}
-		else
-		{
-			int err = alsa_recovery((int)written_frames);
-			if (err < 0)
-			{
-				g_warning("alsa_write_audio(): write error: %s",
-					  snd_strerror(err));
-				break;
-			}
-		}
-	}
-}
-
-/* transfer audio data from thread buffer to h/w */
-static void alsa_write_out_thread_data(void)
-{
-	gint length, cnt, avail;
-
-	length = MIN(hw_period_size_in, get_thread_buffer_filled());
-	avail = snd_pcm_frames_to_bytes(alsa_pcm, alsa_get_avail());
-	length = MIN(length, avail);
-	while (length > 0)
-	{
-		int rd;
-		cnt = MIN(length, thread_buffer_size - rd_index);
-		alsa_do_write(thread_buffer + rd_index, cnt);
-		rd = (rd_index + cnt) % thread_buffer_size;
-		rd_index = rd;
-		length -= cnt;
-	}
-}
-
-/* audio thread loop */
-/* FIXME: proper lock? */
-static void *alsa_loop(void *arg)
-{
-	int npfds = snd_pcm_poll_descriptors_count(alsa_pcm);
-	int wr = 0;
-
-	g_static_mutex_lock(&alsa_mutex);
-
-	if (npfds <= 0)
-		goto _error;
-
-	while (going && alsa_pcm)
-	{
-		if (get_thread_buffer_filled() > prebuffer_size)
-			prebuffer = FALSE;
-		if (!paused && !prebuffer &&
-		    get_thread_buffer_filled() > hw_period_size_in)
-		{
-			wr = snd_pcm_wait(alsa_pcm, 10);
-			if (wr > 0)
-			{
-				alsa_write_out_thread_data();
-			}
-			else if (wr < 0)
-			{
-				alsa_recovery(wr);
-			}
-		}
-		else	/* XXX: why is this here? --nenolod */
-			g_usleep(10000);
-
-		if (pause_request != paused)
-			alsa_do_pause(pause_request);
-
-		if (flush_request != -1)
-		{
-			alsa_do_flush(flush_request);
-			flush_request = -1;
-			prebuffer = TRUE;
-		}
-	}
-
- _error:
-	g_static_mutex_unlock(&alsa_mutex);
-	alsa_close_pcm();
-	g_free(thread_buffer);
-	thread_buffer = NULL;
-	g_thread_exit(NULL);
-	return(NULL);
-}
-
-/* open callback */
-int alsa_open(AFormat fmt, int rate, int nch)
-{
-	debug("Opening device");
-	if((inputf = snd_format_from_xmms(fmt, rate, nch)) == NULL) return 0;
-
-	if (alsa_cfg.debug)
-		snd_output_stdio_attach(&logs, stdout, 0);
-
-	if (alsa_setup(inputf) < 0)
-	{
-		alsa_close();
-		return 0;
-	}
-
-	g_static_mutex_lock(&alsa_mutex);
-
-	if (!mixer)
-		alsa_setup_mixer();
-
-	output_time_offset = 0;
-	alsa_total_written = alsa_hw_written = 0;
-	going = TRUE;
-	paused = FALSE;
-	prebuffer = TRUE;
-	remove_prebuffer = FALSE;
-
-	thread_buffer_size = (guint64)aud_cfg->output_buffer_size * inputf->bps / 1000;
-	if (thread_buffer_size < hw_buffer_size)
-		thread_buffer_size = hw_buffer_size * 2;
-	if (thread_buffer_size < 8192)
-		thread_buffer_size = 8192;
-	prebuffer_size = thread_buffer_size / 2;
-	if (prebuffer_size < 8192)
-		prebuffer_size = 8192;
-	thread_buffer_size += hw_buffer_size;
-	thread_buffer_size -= thread_buffer_size % hw_period_size;
-	thread_buffer = g_malloc0(thread_buffer_size);
-	wr_index = rd_index = 0;
-	pause_request = FALSE;
-	flush_request = -1;
-
-	g_static_mutex_unlock(&alsa_mutex);
-	
-	audio_thread = g_thread_create((GThreadFunc)alsa_loop, NULL, TRUE, NULL);
-	return 1;
-}
-
-static struct snd_format * snd_format_from_xmms(AFormat fmt, int rate, int channels)
-{
-	struct snd_format *f = g_malloc(sizeof(struct snd_format));
-	size_t i;
-        int found = 0;
-
-	f->xmms_format = fmt;
-	f->format = SND_PCM_FORMAT_UNKNOWN;
-
-	for (i = 0; i < sizeof(format_table) / sizeof(format_table[0]); i++)
-		if (format_table[i].xmms == fmt)
-		{
-			f->format = format_table[i].alsa;
-                        found = 1;
-			break;
-		}
-
-        if(!found) {
-            g_free(f);
-            return NULL;
-        }
-
-	/* Get rid of _NE */
-	for (i = 0; i < sizeof(format_table) / sizeof(format_table[0]); i++)
-		if (format_table[i].alsa == f->format)
-		{
-			f->xmms_format = format_table[i].xmms;
-			break;
-		}
-
-
-	f->rate = rate;
-	f->channels = channels;
-	f->sample_bits = snd_pcm_format_physical_width(f->format);
-	f->bps = (rate * f->sample_bits * channels) >> 3;
-
-	return f;
-}
-
-static int alsa_setup(struct snd_format *f)
-{
-	int err;
-	snd_pcm_hw_params_t *hwparams = NULL;
-	snd_pcm_sw_params_t *swparams = NULL;
-	unsigned int alsa_buffer_time, alsa_period_time;
-	snd_pcm_uframes_t alsa_buffer_size, alsa_period_size;
-
-	debug("alsa_setup");
-
-	g_free(outputf);
-	outputf = snd_format_from_xmms(f->xmms_format, f->rate, f->channels);
-        if(outputf == NULL) return -1;
-
-	debug("Opening device: %s", alsa_cfg.pcm_device);
-	/* FIXME: Can snd_pcm_open() return EAGAIN? */
-	if ((err = snd_pcm_open(&alsa_pcm, alsa_cfg.pcm_device,
-				SND_PCM_STREAM_PLAYBACK,
-				SND_PCM_NONBLOCK)) < 0)
-	{
-		g_warning("alsa_setup(): Failed to open pcm device (%s): %s",
-			  alsa_cfg.pcm_device, snd_strerror(err));
-		alsa_pcm = NULL;
-		g_free(outputf);
-		outputf = NULL;
-		return -1;
-	}
-
-	/* doesn't care about non-blocking */
-	/* snd_pcm_nonblock(alsa_pcm, 0); */
-
-	if (alsa_cfg.debug)
-	{
-		snd_pcm_info_t *info = NULL;
-		int alsa_card, alsa_device, alsa_subdevice;
-
-		snd_pcm_info_alloca(&info);
-		snd_pcm_info(alsa_pcm, info);
-		alsa_card = snd_pcm_info_get_card(info);
-		alsa_device = snd_pcm_info_get_device(info);
-		alsa_subdevice = snd_pcm_info_get_subdevice(info);
-		printf("Card %i, Device %i, Subdevice %i\n",
-		       alsa_card, alsa_device, alsa_subdevice);
-	}
-
-	snd_pcm_hw_params_alloca(&hwparams);
-
-	if ((err = snd_pcm_hw_params_any(alsa_pcm, hwparams)) < 0)
-	{
-		g_warning("alsa_setup(): No configuration available for "
-			  "playback: %s", snd_strerror(err));
-		return -1;
-	}
-
-	if ((err = snd_pcm_hw_params_set_access(alsa_pcm, hwparams,
-						SND_PCM_ACCESS_RW_INTERLEAVED)) < 0)
-	{
-		g_warning("alsa_setup(): Cannot set direct write mode: %s",
-			  snd_strerror(err));
-		return -1;
-	}
-
-	/* XXX: there is no down-dithering for 24bit yet --nenolod */
-	if ((err = snd_pcm_hw_params_set_format(alsa_pcm, hwparams, outputf->format)) < 0)
-	{
-		g_warning("alsa_setup(): Sample format not "
-			  "available for playback: %s",
-			  snd_strerror(err));
-		return -1;
-	}
-
-	snd_pcm_hw_params_set_channels_near(alsa_pcm, hwparams, &outputf->channels);
-	if (outputf->channels != f->channels)
-		return -1;
-
-	snd_pcm_hw_params_set_rate_near(alsa_pcm, hwparams, &outputf->rate, 0);
-	if (outputf->rate == 0)
-	{
-		g_warning("alsa_setup(): No usable samplerate available.");
-		return -1;
-	}
-	if (outputf->rate != f->rate)
-		return -1;
-
-	outputf->sample_bits = snd_pcm_format_physical_width(outputf->format);
-	outputf->bps = (outputf->rate * outputf->sample_bits * outputf->channels) >> 3;
-
-	alsa_buffer_time = alsa_cfg.buffer_time * 1000;
-	if ((err = snd_pcm_hw_params_set_buffer_time_near(alsa_pcm, hwparams,
-							  &alsa_buffer_time, 0)) < 0)
-	{
-		g_warning("alsa_setup(): Set buffer time failed: %s.",
-			  snd_strerror(err));
-		return -1;
-	}
-
-	alsa_period_time = alsa_cfg.period_time * 1000;
-	if ((err = snd_pcm_hw_params_set_period_time_near(alsa_pcm, hwparams,
-							  &alsa_period_time, 0)) < 0)
-	{
-		g_warning("alsa_setup(): Set period time failed: %s.",
-			  snd_strerror(err));
-		return -1;
-	}
-
-	if (snd_pcm_hw_params(alsa_pcm, hwparams) < 0)
-	{
-		if (alsa_cfg.debug)
-			snd_pcm_hw_params_dump(hwparams, logs);
-		g_warning("alsa_setup(): Unable to install hw params");
-		return -1;
-	}
-
-	if ((err = snd_pcm_hw_params_get_buffer_size(hwparams, &alsa_buffer_size)) < 0)
-	{
-		g_warning("alsa_setup(): snd_pcm_hw_params_get_buffer_size() "
-			  "failed: %s",
-			  snd_strerror(err));
-		return -1;
-	}
-
-	if ((err = snd_pcm_hw_params_get_period_size(hwparams, &alsa_period_size, 0)) < 0)
-	{
-		g_warning("alsa_setup(): snd_pcm_hw_params_get_period_size() "
-			  "failed: %s",
-			  snd_strerror(err));
-		return -1;
-	}
-
-	alsa_can_pause = snd_pcm_hw_params_can_pause(hwparams);
-	debug("can pause: %d", alsa_can_pause);
-
-	snd_pcm_sw_params_alloca(&swparams);
-	snd_pcm_sw_params_current(alsa_pcm, swparams);
-
-	if ((err = snd_pcm_sw_params_set_start_threshold(alsa_pcm,
-			swparams, alsa_buffer_size - alsa_period_size) < 0))
-		g_warning("alsa_setup(): setting start "
-			  "threshold failed: %s", snd_strerror(err));
-	if (snd_pcm_sw_params(alsa_pcm, swparams) < 0)
-	{
-		g_warning("alsa_setup(): Unable to install sw params");
-		return -1;
-	}
-
-	if (alsa_cfg.debug)
-	{
-		snd_pcm_sw_params_dump(swparams, logs);
-		snd_pcm_dump(alsa_pcm, logs);
-	}
-
-	hw_buffer_size = snd_pcm_frames_to_bytes(alsa_pcm, alsa_buffer_size);
-	hw_period_size = snd_pcm_frames_to_bytes(alsa_pcm, alsa_period_size);
-	if (inputf->bps != outputf->bps)
-	{
-		int align = (inputf->sample_bits * inputf->channels) / 8;
-		hw_buffer_size_in = ((guint64)hw_buffer_size * inputf->bps +
-				     outputf->bps/2) / outputf->bps;
-		hw_period_size_in = ((guint64)hw_period_size * inputf->bps +
-				     outputf->bps/2) / outputf->bps;
-		hw_buffer_size_in -= hw_buffer_size_in % align;
-		hw_period_size_in -= hw_period_size_in % align;
-	}
-	else
-	{
-		hw_buffer_size_in = hw_buffer_size;
-		hw_period_size_in = hw_period_size;
-	}
-
-	debug("Device setup: buffer time: %i, size: %i.", alsa_buffer_time,
-	      hw_buffer_size);
-	debug("Device setup: period time: %i, size: %i.", alsa_period_time,
-	      hw_period_size);
-	debug("bits per sample: %i; frame size: %i; Bps: %i",
-	      snd_pcm_format_physical_width(outputf->format),
-	      (int)snd_pcm_frames_to_bytes(alsa_pcm, 1), outputf->bps);
-
-	return 0;
-}
-
-void alsa_tell(AFormat * fmt, gint * rate, gint * nch)
-{
-	(*fmt) = inputf->xmms_format;
-	(*rate) = inputf->rate;
-	(*nch) = inputf->channels;
-}
diff -Nur audacious-plugins-fedora-1.5.1-orig/src/alsa/configure.c audacious-plugins-fedora-1.5.1/src/alsa/configure.c
--- audacious-plugins-fedora-1.5.1-orig/src/alsa/configure.c	2008-06-08 10:37:44.000000000 +0200
+++ audacious-plugins-fedora-1.5.1/src/alsa/configure.c	1970-01-01 01:00:00.000000000 +0100
@@ -1,396 +0,0 @@
-/*  XMMS - ALSA output plugin
- *  Copyright (C) 2001-2003 Matthieu Sozeau <mattam@altern.org>
- *  Copyright (C) 2003-2005 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 "alsa.h"
-#include <stdio.h>
-
-static GtkWidget *configure_win = NULL;
-static GtkWidget *buffer_time_spin, *period_time_spin;
-
-static GtkWidget *devices_combo, *mixer_devices_combo;
-
-static int current_mixer_card;
-
-#define GET_SPIN_INT(spin) \
-	gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin))
-#define GET_TOGGLE(tb) gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(tb))
-#define GET_CHARS(edit) gtk_editable_get_chars(GTK_EDITABLE(edit), 0, -1)
-
-static void configure_win_ok_cb(GtkWidget * w, gpointer data)
-{
-	g_free(alsa_cfg.pcm_device);
-	alsa_cfg.pcm_device = GET_CHARS(GTK_COMBO(devices_combo)->entry);
-	alsa_cfg.buffer_time = GET_SPIN_INT(buffer_time_spin);
-	alsa_cfg.period_time = GET_SPIN_INT(period_time_spin);
-	alsa_cfg.mixer_card = current_mixer_card;
-	alsa_cfg.mixer_device = GET_CHARS(GTK_COMBO(mixer_devices_combo)->entry);
-
-	alsa_save_config();
-	gtk_widget_destroy(configure_win);
-}
-
-void alsa_save_config(void)
-{
-	mcs_handle_t *cfgfile = aud_cfg_db_open();
-
-	aud_cfg_db_set_int(cfgfile, ALSA_CFGID, "buffer_time", alsa_cfg.buffer_time);
-	aud_cfg_db_set_int(cfgfile, ALSA_CFGID, "period_time", alsa_cfg.period_time);
-	aud_cfg_db_set_string(cfgfile,ALSA_CFGID,"pcm_device", alsa_cfg.pcm_device);
-	aud_cfg_db_set_int(cfgfile, ALSA_CFGID, "mixer_card", alsa_cfg.mixer_card);
-	aud_cfg_db_set_string(cfgfile,ALSA_CFGID,"mixer_device", alsa_cfg.mixer_device);
-	aud_cfg_db_set_int(cfgfile, ALSA_CFGID, "volume_left", alsa_cfg.vol.left);
-	aud_cfg_db_set_int(cfgfile, ALSA_CFGID, "volume_right", alsa_cfg.vol.right);
-	aud_cfg_db_close(cfgfile);
-}
-
-static int get_cards(GtkOptionMenu *omenu, GtkSignalFunc cb, int active)
-{
-	GtkWidget *menu, *item;
-	int card = -1, err, set = 0, curr = -1;
-
-	menu = gtk_menu_new();
-	if ((err = snd_card_next(&card)) != 0)
-		g_warning("snd_next_card() failed: %s", snd_strerror(err));
-
-	while (card > -1)
-	{
-		char *label;
-
-		curr++;
-		if (card == active)
-			set = curr;
-		if ((err = snd_card_get_name(card, &label)) != 0)
-		{
-			g_warning("snd_carg_get_name() failed: %s",
-				  snd_strerror(err));
-			break;
-		}
-
-		item = gtk_menu_item_new_with_label(label);
-		gtk_signal_connect(GTK_OBJECT(item), "activate", cb,
-				   GINT_TO_POINTER(card));
-		gtk_widget_show(item);
-		gtk_menu_append(GTK_MENU(menu), item);
-		if ((err = snd_card_next(&card)) != 0)
-		{
-			g_warning("snd_next_card() failed: %s",
-				  snd_strerror(err));
-			break;
-		}
-	}
-
-	gtk_option_menu_set_menu(omenu, menu);
-	return set;
-}
-
-static int get_mixer_devices(GtkCombo *combo, int card)
-{
-	GList *items = NULL;
-	int err;
-	snd_mixer_t *mixer;
-	snd_mixer_elem_t *current;
-
-	if ((err = alsa_get_mixer(&mixer, card)) < 0)
-		return err;
-
-	current = snd_mixer_first_elem(mixer);
-
-	while (current)
-	{
-		const char *sname = snd_mixer_selem_get_name(current);
-		if (snd_mixer_selem_is_active(current) &&
-		    snd_mixer_selem_has_playback_volume(current))
-			items = g_list_append(items, g_strdup(sname));
-		current = snd_mixer_elem_next(current);
-	}
-
-	gtk_combo_set_popdown_strings(combo, items);
-
-	return 0;
-}
-
-static void get_devices_for_card(GtkCombo *combo, int card)
-{
-	GtkWidget *item;
-	int pcm_device = -1, err;
-	snd_pcm_info_t *pcm_info = NULL;
-	snd_ctl_t *ctl;
-	char dev[64], *card_name;
-
-	sprintf(dev, "hw:%i", card);
-
-	if ((err = snd_ctl_open(&ctl, dev, 0)) < 0)
-	{
-		printf("snd_ctl_open() failed: %s", snd_strerror(err));
-		return;
-	}
-
-	if ((err = snd_card_get_name(card, &card_name)) != 0)
-	{
-		g_warning("snd_card_get_name() failed: %s", snd_strerror(err));
-		card_name = _("Unknown soundcard");
-	}
-
-	snd_pcm_info_alloca(&pcm_info);
-
-	for (;;)
-	{
-		char *device, *descr;
-		if ((err = snd_ctl_pcm_next_device(ctl, &pcm_device)) < 0)
-		{
-			g_warning("snd_ctl_pcm_next_device() failed: %s",
-				  snd_strerror(err));
-			pcm_device = -1;
-		}
-		if (pcm_device < 0)
-			break;
-
-		snd_pcm_info_set_device(pcm_info, pcm_device);
-		snd_pcm_info_set_subdevice(pcm_info, 0);
-		snd_pcm_info_set_stream(pcm_info, SND_PCM_STREAM_PLAYBACK);
-
-		if ((err = snd_ctl_pcm_info(ctl, pcm_info)) < 0)
-		{
-			if (err != -ENOENT)
-				g_warning("get_devices_for_card(): "
-					  "snd_ctl_pcm_info() "
-					  "failed (%d:%d): %s.", card,
-					  pcm_device, snd_strerror(err));
-			continue;
-		}
-
-		device = g_strdup_printf("hw:%d,%d", card, pcm_device);
-		descr = g_strconcat(card_name, ": ",
-				    snd_pcm_info_get_name(pcm_info),
-				    " (", device, ")", NULL);
-		item = gtk_list_item_new_with_label(descr);
-		gtk_widget_show(item);
-		g_free(descr);
-		gtk_combo_set_item_string(combo, GTK_ITEM(item), device);
-		g_free(device);
-		gtk_container_add(GTK_CONTAINER(combo->list), item);
-	}
-
-	snd_ctl_close(ctl);
-}
-
-
-
-static void get_devices(GtkCombo *combo)
-{
-	GtkWidget *item;
-	int card = -1;
-	int err = 0;
-	char *descr;
-
-	descr = g_strdup_printf(_("Default PCM device (%s)"), "default");
-	item = gtk_list_item_new_with_label(descr);
-	gtk_widget_show(item);
-	g_free(descr);
-	gtk_combo_set_item_string(combo, GTK_ITEM(item), "default");
-	gtk_container_add(GTK_CONTAINER(combo->list), item);
-
-	if ((err = snd_card_next(&card)) != 0)
-	{
-		g_warning("snd_next_card() failed: %s", snd_strerror(err));
-		return;
-	}
-
-	while (card > -1)
-	{
-		get_devices_for_card(combo, card);
-		if ((err = snd_card_next(&card)) != 0)
-		{
-			g_warning("snd_next_card() failed: %s",
-				  snd_strerror(err));
-			break;
-		}
-	}
-}
-
-static void mixer_card_cb(GtkWidget * widget, gpointer card)
-{
-	if (current_mixer_card == GPOINTER_TO_INT(card))
-		return;
-	current_mixer_card = GPOINTER_TO_INT(card);
-	get_mixer_devices(GTK_COMBO(mixer_devices_combo),
-			  current_mixer_card);
-}
-
-void alsa_configure(void)
-{
-	GtkWidget *vbox, *notebook;
-	GtkWidget *dev_vbox, *adevice_frame, *adevice_box;
-	GtkWidget *mixer_frame, *mixer_box, *mixer_table, *mixer_card_om;
-	GtkWidget *mixer_card_label, *mixer_device_label;
-	GtkWidget *advanced_vbox, *card_vbox;
-	GtkWidget *buffer_table;
-	GtkWidget *card_frame, *buffer_time_label, *period_time_label;
-	GtkObject *buffer_time_adj, *period_time_adj;
-	GtkWidget *bbox, *ok, *cancel;
-
-	int mset;
-
-	if (configure_win)
-	{
-                gtk_window_present(GTK_WINDOW(configure_win));
-		return;
-	}
-
-	configure_win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
-	gtk_signal_connect(GTK_OBJECT(configure_win), "destroy",
-			   GTK_SIGNAL_FUNC(gtk_widget_destroyed),
-			   &configure_win);
-	gtk_window_set_title(GTK_WINDOW(configure_win),
-			     _("ALSA Driver configuration"));
-	gtk_window_set_policy(GTK_WINDOW(configure_win),
-			      FALSE, TRUE, FALSE);
-	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);
-
-	devices_combo = gtk_combo_new();
-	gtk_box_pack_start(GTK_BOX(adevice_box), devices_combo,
-			   FALSE, FALSE, 0);
-	get_devices(GTK_COMBO(devices_combo));
-	gtk_entry_set_text(GTK_ENTRY(GTK_COMBO(devices_combo)->entry),
-			   alsa_cfg.pcm_device);
-
-	mixer_frame = gtk_frame_new(_("Mixer:"));
-	gtk_box_pack_start(GTK_BOX(dev_vbox), mixer_frame, FALSE, FALSE, 0);
-
-	mixer_box = gtk_vbox_new(FALSE, 5);
-	gtk_container_set_border_width(GTK_CONTAINER(mixer_box), 5);
-	gtk_container_add(GTK_CONTAINER(mixer_frame), mixer_box);
-
-	mixer_table = gtk_table_new(2, 2, FALSE);
-	gtk_table_set_row_spacings(GTK_TABLE(mixer_table), 5);
-	gtk_table_set_col_spacings(GTK_TABLE(mixer_table), 5);
-	gtk_box_pack_start(GTK_BOX(mixer_box), mixer_table, FALSE, FALSE, 0);
-
-	mixer_card_label = gtk_label_new(_("Mixer card:"));
-	gtk_label_set_justify(GTK_LABEL(mixer_card_label), GTK_JUSTIFY_LEFT);
-	gtk_misc_set_alignment(GTK_MISC(mixer_card_label), 0, 0.5);
-	gtk_table_attach(GTK_TABLE(mixer_table), mixer_card_label,
-			 0, 1, 0, 1, GTK_FILL, 0, 0, 0);
-
-	mixer_card_om = gtk_option_menu_new();
-	mset = get_cards(GTK_OPTION_MENU(mixer_card_om),
-			 (GtkSignalFunc)mixer_card_cb, alsa_cfg.mixer_card);
-
-	gtk_table_attach(GTK_TABLE(mixer_table), mixer_card_om,
-			 1, 2, 0, 1, GTK_FILL | GTK_EXPAND, GTK_FILL, 0, 0);
-
-	mixer_device_label = gtk_label_new(_("Mixer device:"));
-	gtk_label_set_justify(GTK_LABEL(mixer_device_label), GTK_JUSTIFY_LEFT);
-	gtk_misc_set_alignment(GTK_MISC(mixer_device_label), 0, 0.5);
-	gtk_table_attach(GTK_TABLE(mixer_table), mixer_device_label,
-			 0, 1, 1, 2, GTK_FILL, 0, 0, 0);
-	mixer_devices_combo = gtk_combo_new();
-	gtk_option_menu_set_history(GTK_OPTION_MENU(mixer_card_om), mset);
-	get_mixer_devices(GTK_COMBO(mixer_devices_combo), alsa_cfg.mixer_card);
-	gtk_entry_set_text(GTK_ENTRY(GTK_COMBO(mixer_devices_combo)->entry),
-			   alsa_cfg.mixer_device);
-
-	gtk_table_attach(GTK_TABLE(mixer_table), mixer_devices_combo,
-			 1, 2, 1, 2, GTK_FILL | GTK_EXPAND, 0, 0, 0);
-
-	gtk_notebook_append_page(GTK_NOTEBOOK(notebook), dev_vbox,
-				 gtk_label_new(_("Device settings")));
-
-
-	advanced_vbox = gtk_vbox_new(FALSE, 0);
-	gtk_container_set_border_width(GTK_CONTAINER(advanced_vbox), 5);
-
-	card_frame = gtk_frame_new(_("Soundcard:"));
-	gtk_box_pack_start_defaults(GTK_BOX(advanced_vbox), card_frame);
-
-	card_vbox = gtk_vbox_new(FALSE, 0);
-	gtk_container_add(GTK_CONTAINER(card_frame), card_vbox);
-
-	gtk_container_set_border_width(GTK_CONTAINER(card_vbox), 5);
-
-	buffer_table = gtk_table_new(2, 2, TRUE);
-	gtk_table_set_row_spacings(GTK_TABLE(buffer_table), 5);
-	gtk_table_set_col_spacings(GTK_TABLE(buffer_table), 5);
-	gtk_box_pack_start_defaults(GTK_BOX(card_vbox), buffer_table);
-
-	buffer_time_label = gtk_label_new(_("Buffer time (ms):"));
-	gtk_label_set_justify(GTK_LABEL(buffer_time_label), GTK_JUSTIFY_LEFT);
-	gtk_misc_set_alignment(GTK_MISC(buffer_time_label), 0, 0.5);
-	gtk_table_attach(GTK_TABLE(buffer_table), buffer_time_label,
-			 0, 1, 0, 1, GTK_FILL, 0, 0, 0);
-
-	buffer_time_adj = gtk_adjustment_new(alsa_cfg.buffer_time,
-					     200, 10000, 100, 100, 100);
-	buffer_time_spin = gtk_spin_button_new(GTK_ADJUSTMENT(buffer_time_adj),
-					       8, 0);
-	gtk_widget_set_usize(buffer_time_spin, 60, -1);
-	gtk_table_attach(GTK_TABLE(buffer_table), buffer_time_spin,
-			 1, 2, 0, 1, 0, 0, 0, 0);
-
-	period_time_label = gtk_label_new(_("Period time (ms):"));
-	gtk_label_set_justify(GTK_LABEL(period_time_label), GTK_JUSTIFY_LEFT);
-	gtk_misc_set_alignment(GTK_MISC(period_time_label), 0, 0.5);
-	gtk_table_attach(GTK_TABLE(buffer_table), period_time_label,
-			 0, 1, 1, 2, GTK_FILL, 0, 0, 0);
-	period_time_adj = gtk_adjustment_new(alsa_cfg.period_time,
-					     1, 500, 1, 100, 100);
-	period_time_spin = gtk_spin_button_new(GTK_ADJUSTMENT(period_time_adj),
-					       8, 0);
-
-	gtk_widget_set_usize(period_time_spin, 60, -1);
-	gtk_table_attach(GTK_TABLE(buffer_table), period_time_spin,
-			 1, 2, 1, 2, 0, 0, 0, 0);
-
-	gtk_notebook_append_page(GTK_NOTEBOOK(notebook), advanced_vbox,
-				 gtk_label_new(_("Advanced settings")));
-
-	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", (GCallback)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",
-				  (GCallback)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);
-
-	gtk_widget_show_all(configure_win);
-}
diff -Nur audacious-plugins-fedora-1.5.1-orig/src/alsa/Makefile audacious-plugins-fedora-1.5.1/src/alsa/Makefile
--- audacious-plugins-fedora-1.5.1-orig/src/alsa/Makefile	2008-06-08 10:37:44.000000000 +0200
+++ audacious-plugins-fedora-1.5.1/src/alsa/Makefile	2009-06-29 18:01:20.000000000 +0200
@@ -1,9 +1,9 @@
 PLUGIN = ALSA${PLUGIN_SUFFIX}
 
-SRCS = alsa.c		\
-       about.c		\
-       audio.c		\
-       configure.c
+SRCS = alsa-core.c \
+       alsa-configure.c \
+       alsa-ringbuffer.c \
+       alsa-util.c
 
 include ../../buildsys.mk
 include ../../extra.mk
diff -Nur audacious-plugins-fedora-1.5.1-orig/src/alsa/TODO audacious-plugins-fedora-1.5.1/src/alsa/TODO
--- audacious-plugins-fedora-1.5.1-orig/src/alsa/TODO	1970-01-01 01:00:00.000000000 +0100
+++ audacious-plugins-fedora-1.5.1/src/alsa/TODO	2009-06-29 18:01:20.000000000 +0200
@@ -0,0 +1,30 @@
+The following things are not implemented yet:
+
+- prerolling: this means that it is possible for buffer underruns occasionally.
+  we want to preroll at least 50% of buffer_size before actually writing anything
+  to the soundcard.
+
+The following things would be nice:
+
+- use HAL (or DeviceKit) to match up devices and mixer settings. The old way sucks
+  majorly, I want to be able to just choose a soundcard and have it set up the mixer
+  the right way on it's own. This is 2009, and this should be possible.
+
+(right now we don't use HAL/DeviceKit, but have a trivial function to determine what
+ mixer settings we use.  pcm:default seems like a good choice for finding the system
+ default soundcard/mixer, which we use already...)
+
+Things that won't be reimplemented:
+
+- support for custom period sizes and buffer sizes. we want to use the ALSA defaults
+  since they provide the optimal values for each card. this means that specifying
+  period/buffer sizes is retarded.
+
+Notable differences between the old ALSA plugin and the new one:
+
+- tickless design:
+  The old ALSA plugin basically did a lot of polling. This does not, instead using
+  an event-driven tickless design using conditionals.
+
+- uses ALSA "safe" API subset:
+  Plugin should work correctly with all userspace plugins.