summary refs log tree commit diff
path: root/pkgs
diff options
context:
space:
mode:
authorEelco Dolstra <eelco.dolstra@logicblox.com>2006-01-26 23:34:22 +0000
committerEelco Dolstra <eelco.dolstra@logicblox.com>2006-01-26 23:34:22 +0000
commit69b41650adb254e52abe044a74c495d5ea04cd08 (patch)
tree91bebf70ea1c96193c14ed3a8d11770158238d78 /pkgs
parentcb27290a7a16cc09fab7c2b3dbe2adf87cc6c209 (diff)
downloadnixlib-69b41650adb254e52abe044a74c495d5ea04cd08.tar
nixlib-69b41650adb254e52abe044a74c495d5ea04cd08.tar.gz
nixlib-69b41650adb254e52abe044a74c495d5ea04cd08.tar.bz2
nixlib-69b41650adb254e52abe044a74c495d5ea04cd08.tar.lz
nixlib-69b41650adb254e52abe044a74c495d5ea04cd08.tar.xz
nixlib-69b41650adb254e52abe044a74c495d5ea04cd08.tar.zst
nixlib-69b41650adb254e52abe044a74c495d5ea04cd08.zip
* Fix an annoying problem with xmms's ALSA output driver. Thanks
  SuSE!

svn path=/nixpkgs/trunk/; revision=4607
Diffstat (limited to 'pkgs')
-rw-r--r--pkgs/applications/audio/xmms/alsa.patch1475
-rw-r--r--pkgs/applications/audio/xmms/default.nix3
2 files changed, 1478 insertions, 0 deletions
diff --git a/pkgs/applications/audio/xmms/alsa.patch b/pkgs/applications/audio/xmms/alsa.patch
new file mode 100644
index 000000000000..62cf7354fd25
--- /dev/null
+++ b/pkgs/applications/audio/xmms/alsa.patch
@@ -0,0 +1,1475 @@
+diff -rc xmms-1.2.10-orig/Output/alsa/alsa.h xmms-1.2.10/Output/alsa/alsa.h
+*** xmms-1.2.10-orig/Output/alsa/alsa.h	2004-01-11 17:27:26.000000000 +0100
+--- xmms-1.2.10/Output/alsa/alsa.h	2006-01-27 00:28:49.000000000 +0100
+***************
+*** 50,57 ****
+  	char *mixer_device;
+  	int buffer_time;
+  	int period_time;
+  	gboolean debug;
+- 	gboolean mmap;
+  	struct
+  	{
+  		int left, right;
+--- 50,57 ----
+  	char *mixer_device;
+  	int buffer_time;
+  	int period_time;
++ 	int thread_buffer_time;
+  	gboolean debug;
+  	struct
+  	{
+  		int left, right;
+***************
+*** 65,72 ****
+  void alsa_about(void);
+  void alsa_configure(void);
+  int alsa_get_mixer(snd_mixer_t **mixer, int card);
+- snd_mixer_elem_t* alsa_get_mixer_elem(snd_mixer_t *mixer, char *name, int index);
+- int alsa_setup_mixer(void);
+  void alsa_save_config(void);
+  
+  void alsa_get_volume(int *l, int *r);
+--- 65,70 ----
+diff -rc xmms-1.2.10-orig/Output/alsa/audio.c xmms-1.2.10/Output/alsa/audio.c
+*** xmms-1.2.10-orig/Output/alsa/audio.c	2004-01-28 00:09:39.000000000 +0100
+--- xmms-1.2.10/Output/alsa/audio.c	2006-01-27 00:28:49.000000000 +0100
+***************
+*** 17,52 ****
+   *  You should have received a copy of the GNU General Public License
+   *  along with this program; if not, write to the Free Software
+   *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+   */
+  
+  #include "alsa.h"
+  #include <ctype.h>
+  #include <libxmms/xconvert.h>
+  
+  static snd_pcm_t		*alsa_pcm 	    = NULL;
+- static snd_pcm_status_t		*alsa_status 	    = NULL;
+- static snd_pcm_channel_area_t   *areas              = NULL;
+  
+  static snd_output_t		*logs 		    = NULL;
+  
+! static int 			 alsa_bps 	    = 0;
+! static guint64			 alsa_total_written = 0;
+  
+  /* Set/Get volume */
+  static snd_mixer_elem_t 	*pcm_element 	    = NULL;
+  static snd_mixer_t 		*mixer 		    = NULL;
+  
+! static gboolean mmap, force_start, going, paused;
+  
+! static gpointer buffer;
+  
+- static int alsa_can_pause;
+  
+  struct snd_format {
+  	unsigned int rate;
+  	unsigned int channels;
+  	snd_pcm_format_t format;
+  	AFormat xmms_format;
+  };
+  
+  static struct snd_format *inputf = NULL;
+--- 17,72 ----
+   *  You should have received a copy of the GNU General Public License
+   *  along with this program; if not, write to the Free Software
+   *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
++  *
++  *
++  *  CHANGES
++  *
++  *  2005.01.05  Takashi Iwai <tiwai@suse.de>
++  *	Impelemented the multi-threaded mode with an audio-thread.
++  *	Many fixes and cleanups.
+   */
+  
+  #include "alsa.h"
+  #include <ctype.h>
++ #include <pthread.h>
+  #include <libxmms/xconvert.h>
+  
+  static snd_pcm_t		*alsa_pcm 	    = NULL;
+  
+  static snd_output_t		*logs 		    = NULL;
+  
+! static guint64			 alsa_total_written = 0;	/* input bytes */
+! static guint64			 alsa_hw_written    = 0;	/* output bytes */
+! static gint			 output_time_offset = 0;
+! 
+! /* 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 	    = NULL;
+  static snd_mixer_t 		*mixer 		    = NULL;
+  
+! static gboolean going, paused;
+  
+! static gboolean alsa_can_pause;
+! 
+! /* for audio thread */
+! static pthread_t 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 gint flush_request;		/* flush status (time) currently requested */
+  
+  
+  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;
+***************
+*** 54,61 ****
+  static struct snd_format *outputf = NULL;
+  
+  static int alsa_setup(struct snd_format *f);
+! static void alsa_mmap_audio(char *data, int length);
+! static void alsa_write_audio(gpointer data, int length);
+  
+  static struct snd_format * snd_format_from_xmms(AFormat fmt, int rate, int channels);
+  
+--- 74,80 ----
+  static struct snd_format *outputf = NULL;
+  
+  static int alsa_setup(struct snd_format *f);
+! static void alsa_write_audio(char *data, int length);
+  
+  static struct snd_format * snd_format_from_xmms(AFormat fmt, int rate, int channels);
+  
+***************
+*** 71,92 ****
+  } format_table[] =
+  {{FMT_S16_LE, SND_PCM_FORMAT_S16_LE},
+   {FMT_S16_BE, SND_PCM_FORMAT_S16_BE},
+!  {FMT_S16_NE,
+! #ifdef WORDS_BIGENDIAN
+!   SND_PCM_FORMAT_S16_BE
+! #else
+!   SND_PCM_FORMAT_S16_LE
+! #endif
+!  },
+   {FMT_U16_LE, SND_PCM_FORMAT_U16_LE},
+   {FMT_U16_BE, SND_PCM_FORMAT_U16_BE},
+!  {FMT_U16_NE,
+! #ifdef WORDS_BIGENDIAN
+!   SND_PCM_FORMAT_U16_BE
+! #else
+!   SND_PCM_FORMAT_U16_LE
+! #endif
+!  },
+   {FMT_U8, SND_PCM_FORMAT_U8},
+   {FMT_S8, SND_PCM_FORMAT_S8},
+  };
+--- 90,99 ----
+  } format_table[] =
+  {{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},
+  };
+***************
+*** 106,281 ****
+  	}
+  }
+  
+! int alsa_playing(void)
+! {
+! 	if (!going || paused)
+! 		return FALSE;
+! 
+! 	return(snd_pcm_state(alsa_pcm) == SND_PCM_STATE_RUNNING);
+! }
+! 
+! static void xrun_recover(void)
+! {
+! 	int err;
+! 
+! 	if (alsa_cfg.debug)
+! 	{
+! 		snd_pcm_status_alloca(&alsa_status);
+! 		if ((err = 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);
+! 		}
+! 	}
+! 
+! 	if (snd_pcm_state(alsa_pcm) == SND_PCM_STATE_XRUN)
+! 	{
+! 		if ((err = snd_pcm_prepare(alsa_pcm)) < 0)
+! 			g_warning("xrun_recover(): snd_pcm_prepare() failed.");
+! 	}
+! }
+! 
+! static snd_pcm_sframes_t alsa_get_avail(void)
+! {
+! 	snd_pcm_sframes_t ret;
+! 	if ((ret = snd_pcm_avail_update(alsa_pcm)) == -EPIPE)
+! 		xrun_recover();
+! 	else if (ret < 0)
+! 	{
+! 		g_warning("alsa_get_avail(): snd_pcm_avail_update() failed: %s",
+! 			  snd_strerror(-ret));
+! 		return 0;
+! 	}
+! 	else
+! 		return ret;
+! 	if ((ret = snd_pcm_avail_update(alsa_pcm)) < 0)
+! 	{
+! 		g_warning("alsa_get_avail(): snd_pcm_avail_update() failed: %s",
+! 			  snd_strerror(-ret));
+! 		return 0;
+! 	}
+! 	return ret;
+! }
+! 
+! int alsa_free(void)
+! {
+! 	if (paused)
+! 		return 0;
+! 	else
+! 	{
+! 		int err;
+! 		if (force_start &&
+! 		    snd_pcm_state(alsa_pcm) == SND_PCM_STATE_PREPARED)
+! 		{
+! 			if ((err = snd_pcm_start(alsa_pcm)) < 0)
+! 				g_warning("alsa_free(): snd_pcm_start() "
+! 					  "failed: %s", snd_strerror(-err));
+! 			else
+! 				debug("Stream started");
+! 		}
+! 		force_start = TRUE;
+! 
+! 		return snd_pcm_frames_to_bytes(alsa_pcm, alsa_get_avail());
+! 	}
+! }
+! 
+! void alsa_pause(short p)
+! {
+! 	debug("alsa_pause");
+! 	if (p)
+! 		paused = TRUE;
+! 
+! 	if (alsa_can_pause)
+! 		snd_pcm_pause(alsa_pcm, p);
+! 	else if (p)
+! 		snd_pcm_drop(alsa_pcm);
+! 
+! 	if (!p)
+! 		paused = FALSE;
+! }
+! 
+! void alsa_close(void)
+! {
+! 	int err, started;
+! 
+! 	debug("Closing device");
+! 
+! 	started = going;
+! 	going = 0;
+! 
+! 	pcm_element = NULL;
+! 
+! 	if (mixer)
+! 	{
+! 		snd_mixer_close(mixer);
+! 		mixer = NULL;
+! 	}
+! 
+! 	if (alsa_pcm != NULL)
+! 	{
+! 		if (started)
+! 			if ((err = snd_pcm_drop(alsa_pcm)) < 0)
+! 				g_warning("alsa_pcm_drop() failed: %s",
+! 					  snd_strerror(-err));
+! 
+! 		if ((err = snd_pcm_close(alsa_pcm)) < 0)
+! 			g_warning("alsa_pcm_close() failed: %s",
+! 				  snd_strerror(-err));
+! 		alsa_pcm = NULL;
+! 	}
+! 
+! 	if (mmap) {
+! 		g_free(buffer);
+! 		buffer = NULL;
+! 
+! 		g_free(areas);
+! 		areas = NULL;
+! 	}
+! 
+! 	xmms_convert_buffers_destroy(convertb);
+! 	convertb = NULL;
+! 	g_free(inputf);
+! 	inputf = NULL;
+! 	g_free(effectf);
+! 	effectf = NULL;
+! 
+! 	alsa_save_config();
+! 
+! 	debug("Device closed");
+! }
+! 
+! static void alsa_reopen(struct snd_format *f)
+! {
+! 	unsigned int tmp = alsa_get_written_time();
+! 
+! 	if (alsa_pcm != NULL)
+! 	{
+! 		snd_pcm_close(alsa_pcm);
+! 		alsa_pcm = NULL;
+! 	}
+! 
+! 	if (mmap) {
+! 		g_free(buffer);
+! 		buffer = NULL;
+! 		
+! 		g_free(areas);
+! 		areas = NULL;
+! 	}
+! 
+! 	if (alsa_setup(f) < 0)
+! 		g_warning("Failed to reopen the audio device");
+! 
+! 	alsa_total_written = tmp;
+! 	snd_pcm_prepare(alsa_pcm);
+! }
+! 
+! void alsa_flush(int time)
+! {
+! 	alsa_total_written = (guint64) time * alsa_bps / 1000;
+! }
+! 
+  static void parse_mixer_name(char *str, char **name, int *index)
+  {
+  	char *end;
+--- 113,121 ----
+  	}
+  }
+  
+! /*
+!  * mixer stuff
+!  */
+  static void parse_mixer_name(char *str, char **name, int *index)
+  {
+  	char *end;
+***************
+*** 337,343 ****
+  }
+  	
+  
+! 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;
+--- 177,183 ----
+  }
+  	
+  
+! 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;
+***************
+*** 353,359 ****
+  	return elem;
+  }
+  
+! int alsa_setup_mixer(void)
+  {
+  	char *name;
+  	long int a, b;
+--- 193,199 ----
+  	return elem;
+  }
+  
+! static int alsa_setup_mixer(void)
+  {
+  	char *name;
+  	long int a, b;
+***************
+*** 406,411 ****
+--- 246,260 ----
+  	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)
+  {
+  	static gboolean first = TRUE;
+***************
+*** 461,485 ****
+  }
+  
+  
+  int alsa_get_output_time(void)
+  {
+  	snd_pcm_sframes_t delay;
+! 	ssize_t db = 0;
+  
+! 	if (!going)
+  		return 0;
+  
+! 	if (!snd_pcm_delay(alsa_pcm, &delay))
+! 		db = snd_pcm_frames_to_bytes(alsa_pcm, delay);
+! 	
+! 	if (db < alsa_total_written)
+! 		return ((alsa_total_written - db) * 1000 / alsa_bps);
+! 	return 0;
+  }
+  
+  int alsa_get_written_time(void)
+  {
+! 	return (alsa_total_written * 1000 / alsa_bps);
+  }
+  
+  #define STEREO_ADJUST(type, type2, endian)					\
+--- 310,512 ----
+  }
+  
+  
++ /*
++  * audio stuff
++  */
++ 
++ int alsa_playing(void)
++ {
++ 	if (!going || paused || alsa_pcm == NULL)
++ 		return FALSE;
++ 
++ 	return(snd_pcm_state(alsa_pcm) == SND_PCM_STATE_RUNNING);
++ }
++ 
++ 
++ /* handle generic errors */
++ static int alsa_handle_error(int err)
++ {
++ 	switch (err) {
++ 	case -EPIPE: /* XRUN */
++ 		if (alsa_cfg.debug) {
++ 			snd_pcm_status_t *alsa_status;
++ 			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);
++ 			}
++ 		}
++ 		return snd_pcm_prepare(alsa_pcm);
++ 
++ 	case -ESTRPIPE: /* suspend */
++ 		while ((err = snd_pcm_resume(alsa_pcm)) == -EAGAIN)
++ 			sleep(1);	/* wait until suspend flag is released */
++ 		if (err < 0) {
++ 			g_warning("suspend_recover(): snd_pcm_resume() failed.");
++ 			return snd_pcm_prepare(alsa_pcm);
++ 		}
++ 		break;
++ 	}
++ 
++ 	return err;
++ }
++ 
++ /* 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_handle_error(ret);
++ 		if (ret < 0) {
++ 			g_warning("alsa_get_avail(): snd_pcm_avail_update() failed: %s",
++ 				  snd_strerror(-ret));
++ 			return 0;
++ 		}
++ 	}
++ 	return ret;
++ }
++ 
++ /* 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;
++ 	}
++ }
++ 
++ /* reopen ALSA PCM */
++ 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);
++ }
++ 
++ /* 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)
++ 		xmms_usleep(10000);
++ }
++ 
++ void alsa_close(void)
++ {
++ 	if (! going)
++ 		return;
++ 
++ 	debug("Closing device");
++ 
++ 	going = 0;
++ 
++ 	pthread_join(audio_thread, NULL);
++ 
++ 	alsa_cleanup_mixer();
++ 
++ 	xmms_convert_buffers_destroy(convertb);
++ 	convertb = NULL;
++ 	g_free(inputf);
++ 	inputf = NULL;
++ 	g_free(effectf);
++ 	effectf = NULL;
++ 	g_free(outputf);
++ 	outputf = NULL;
++ 
++ 	alsa_save_config();
++ 
++ 	if (alsa_cfg.debug)
++ 		snd_output_close(logs);
++ 	debug("Device closed");
++ }
++ 
++ /* return the size of audio data filled in the audio thread buffer */
++ static int get_thread_buffer_filled(void)
++ {
++ 	int filled = wr_index - rd_index;
++ 	if (filled >= 0)
++ 		return filled;
++ 	return thread_buffer_size + filled;
++ }
++ 
++ /* get the free space on buffer */
++ int alsa_free(void)
++ {
++ 	return thread_buffer_size - get_thread_buffer_filled() - 1;
++ }
++ 
+  int alsa_get_output_time(void)
+  {
+  	snd_pcm_sframes_t delay;
+! 	guint64 bytes = 0;
+  
+! 	if (!going || alsa_pcm == NULL)
+  		return 0;
+  
+! 	if (!snd_pcm_delay(alsa_pcm, &delay)) {
+! 		bytes = snd_pcm_frames_to_bytes(alsa_pcm, delay);
+! 		if (alsa_hw_written < bytes)
+! 			bytes = 0;
+! 		else
+! 			bytes = alsa_hw_written - bytes;
+! 	}
+! 	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;
+  }
+  
+  #define STEREO_ADJUST(type, type2, endian)					\
+***************
+*** 584,636 ****
+  }
+  
+  
+! void alsa_write(gpointer data, int length)
+  {
+! 	EffectPlugin *ep;
+  
+  	if (paused)
+  		return;
+  
+! 	force_start = FALSE;
+! 
+! 	if (effects_enabled() && (ep = get_current_effect_plugin()))
+! 	{
+! 		int new_freq = inputf->rate;
+! 		int new_chn = inputf->channels;
+! 		AFormat f = inputf->xmms_format;
+  		
+! 		if (ep->query_format)
+! 		{
+! 			ep->query_format(&f, &new_freq, &new_chn);
+! 			
+! 			if (f != effectf->xmms_format ||
+! 			    new_freq != effectf->rate ||
+! 			    new_chn != effectf->channels)
+! 			{
+! 				debug("Changing audio format for effect plugin");
+! 
+! 				g_free(effectf);
+! 				effectf = snd_format_from_xmms(f, new_freq,
+! 							       new_chn);
+! 				alsa_reopen(effectf);
+! 			}
+! 			
+! 		}
+  	
+  		length = ep->mod_samples(&data, length,
+  					 inputf->xmms_format,
+  					 inputf->rate,
+  					 inputf->channels);
+  	}
+- 	else if (effectf)
+- 	{
+- 		g_free(effectf);
+- 		effectf = NULL;
+- 		effectf = snd_format_from_xmms(inputf->xmms_format,
+- 					       inputf->rate,
+- 					       inputf->channels);
+- 		alsa_reopen(inputf);
+- 	}
+  
+  	if (alsa_convert_func != NULL)
+  		length = alsa_convert_func(convertb, &data, length);
+--- 611,657 ----
+  }
+  
+  
+! /* 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)
+  {
+! 	EffectPlugin *ep = NULL;
+! 	int new_freq;
+! 	int new_chn;
+! 	AFormat f;
+  
+  	if (paused)
+  		return;
+  
+! 	new_freq = inputf->rate;
+! 	new_chn = inputf->channels;
+! 	f = inputf->xmms_format;
+  		
+! 	if (effects_enabled() && (ep = get_current_effect_plugin()) &&
+! 	    ep->query_format)
+! 		ep->query_format(&f, &new_freq, &new_chn);
+  	
++ 	if (f != effectf->xmms_format || new_freq != effectf->rate ||
++ 	    new_chn != effectf->channels) {
++ 		debug("Changing audio format for effect plugin");
++ 		g_free(effectf);
++ 		effectf = snd_format_from_xmms(f, new_freq, new_chn);
++ 		if (alsa_reopen(effectf) < 0) {
++ 			/* fatal error... */
++ 			alsa_close();
++ 			return;
++ 		}
++ 	}
++ 
++ 	if (ep) {
+  		length = ep->mod_samples(&data, length,
+  					 inputf->xmms_format,
+  					 inputf->rate,
+  					 inputf->channels);
+  	}
+  
+  	if (alsa_convert_func != NULL)
+  		length = alsa_convert_func(convertb, &data, length);
+***************
+*** 644,656 ****
+  	if (alsa_cfg.soft_volume)
+  		volume_adjust(data, length, outputf->xmms_format, outputf->channels);
+  
+! 	if (mmap)
+! 		alsa_mmap_audio(data, length);
+! 	else
+! 		alsa_write_audio(data, length);
+  }
+  
+! static void alsa_write_audio(gpointer data, int length)
+  {
+  	snd_pcm_sframes_t written_frames;
+  
+--- 665,693 ----
+  	if (alsa_cfg.soft_volume)
+  		volume_adjust(data, length, outputf->xmms_format, outputf->channels);
+  
+! 	alsa_write_audio(data, length);
+  }
+  
+! /* write callback */
+! void alsa_write(gpointer data, int length)
+! {
+! 	int cnt;
+! 	char *src = (char *)data;
+! 
+! 	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;
+  
+***************
+*** 663,735 ****
+  		{
+  			int written = snd_pcm_frames_to_bytes(alsa_pcm,
+  							      written_frames);
+- 			alsa_total_written += written;
+  			length -= written;
+! 			data = (char*) data + written;
+  		}
+! 		else if (written_frames == -EPIPE)
+! 			xrun_recover();
+! 		else
+! 		{
+! 			g_warning("alsa_write_audio(): write error: %s",
+! 				  snd_strerror(-written_frames));
+! 			break;
+  		}
+  	}
+  }
+  
+! static void alsa_mmap_audio(char *data, int length)
+  {
+! 	int cnt = 0, err;
+! 	snd_pcm_uframes_t offset, frames, frame;
+! 	const snd_pcm_channel_area_t *chan_areas = areas;
+! 	int channel_offset = 0, channel;
+! 	ssize_t sample_size, offset_bytes, step;
+! 
+! 	alsa_get_avail();
+  
+! 	while (length > 0)
+! 	{
+! 		frames = snd_pcm_bytes_to_frames(alsa_pcm, length);
+! 		if ((err = snd_pcm_mmap_begin(alsa_pcm, &chan_areas, &offset, &frames) < 0))
+! 			g_warning("alsa_mmap_audio(): snd_pcm_mmap_begin() "
+! 				  "failed: %s", snd_strerror(-err));
+! 
+! 		cnt = snd_pcm_frames_to_bytes(alsa_pcm, frames);
+! 
+! 		sample_size = snd_pcm_samples_to_bytes(alsa_pcm, 1);
+! 		step = chan_areas[0].step / 8;
+! 		offset_bytes = offset * step;
+  
+! 		for (frame = 0; frame < frames; frame++)
+! 		{
+! 			for (channel = 0; channel < outputf->channels; channel++)
+! 			{
+! 				char *ptr = chan_areas[channel].addr;
+! 				memcpy(ptr + chan_areas[channel].first / 8 +
+! 				       offset_bytes,
+! 				       data + channel_offset, sample_size);
+! 				channel_offset += sample_size;
+  			}
+! 			offset_bytes += step;
+  		}
+- 			
+- 		err = snd_pcm_mmap_commit(alsa_pcm, offset, frames);
+- 		if (err == -EPIPE)
+- 			xrun_recover();
+- 		else if (err < 0)
+- 			g_warning("alsa_mmap_audio(): snd_pcm_mmap_commit() "
+- 				  "failed: %s", snd_strerror(-err));
+- 		else if (err != frames)
+- 			g_warning("alsa_mmap_audio(): snd_pcm_mmap_commit "
+- 				  "returned %d, expected %d", err, (int)frames);
+- 		
+- 		alsa_total_written += cnt;
+- 		
+- 		length -= cnt;
+  	}
+  }
+  
+  int alsa_open(AFormat fmt, int rate, int nch)
+  {
+  	debug("Opening device");
+--- 700,785 ----
+  		{
+  			int written = snd_pcm_frames_to_bytes(alsa_pcm,
+  							      written_frames);
+  			length -= written;
+! 			data += written;
+! 			alsa_hw_written += written;
+  		}
+! 		else {
+! 			int err = alsa_handle_error((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);
+! 	struct pollfd *pfds;
+! 	unsigned short *revents;
+! 
+! 	if (npfds <= 0)
+! 		goto _error;
+! 	pfds = alloca(sizeof(*pfds) * npfds);
+! 	revents = alloca(sizeof(*revents) * npfds);
+! 	while (going && alsa_pcm) {
+! 		if (! paused && get_thread_buffer_filled() > hw_period_size_in) {
+! 			snd_pcm_poll_descriptors(alsa_pcm, pfds, npfds);
+! 			if (poll(pfds, npfds, 10) > 0) { 
+! 				/* need to check revents.  poll() with dmix returns
+! 				 * a postive value even if no data is available
+! 				 */
+! 				int i;
+! 				snd_pcm_poll_descriptors_revents(alsa_pcm, pfds, npfds, revents);
+! 				for (i = 0; i < npfds; i++)
+! 					if (revents[i] & POLLOUT) {
+! 						alsa_write_out_thread_data();
+! 						break;
+! 					}
+  			}
+! 		} else
+! 			xmms_usleep(10000);
+! 
+! 		if (pause_request != paused)
+! 			alsa_do_pause(pause_request);
+! 
+! 		if (flush_request != -1) {
+! 			alsa_do_flush(flush_request);
+! 			flush_request = -1;
+  		}
+  	}
++ 
++  _error:
++ 	alsa_close_pcm();
++ 	g_free(thread_buffer);
++ 	thread_buffer = NULL;
++ 	pthread_exit(NULL);
+  }
+  
++ /* open callback */
+  int alsa_open(AFormat fmt, int rate, int nch)
+  {
+  	debug("Opening device");
+***************
+*** 739,746 ****
+  	if (alsa_cfg.debug)
+  		snd_output_stdio_attach(&logs, stdout, 0);
+  
+- 	mmap = alsa_cfg.mmap;
+- 
+  	if (alsa_setup(inputf) < 0)
+  	{
+  		alsa_close();
+--- 789,794 ----
+***************
+*** 751,763 ****
+  
+  	convertb = xmms_convert_buffers_new();
+  	
+! 	alsa_total_written = 0;
+  	going = TRUE;
+  	paused = FALSE;
+- 	force_start = FALSE;
+- 	
+- 	snd_pcm_prepare(alsa_pcm);
+  	
+  	return 1;
+  }
+  
+--- 799,823 ----
+  
+  	convertb = xmms_convert_buffers_new();
+  	
+! 	output_time_offset = 0;
+! 	alsa_total_written = alsa_hw_written = 0;
+  	going = TRUE;
+  	paused = FALSE;
+  	
++ 	thread_buffer_size = (guint64)alsa_cfg.thread_buffer_time * 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;
++ 	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;
++ 
++ 	pthread_create(&audio_thread, NULL, alsa_loop, NULL);
++ 
+  	return 1;
+  }
+  
+***************
+*** 787,792 ****
+--- 847,854 ----
+  
+  	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;
+  }
+***************
+*** 806,812 ****
+  	int err;
+  	snd_pcm_hw_params_t *hwparams;
+  	snd_pcm_sw_params_t *swparams;
+! 	int alsa_buffer_time, bits_per_sample;
+  	unsigned int alsa_period_time;
+  	snd_pcm_uframes_t alsa_buffer_size, alsa_period_size;
+  
+--- 868,874 ----
+  	int err;
+  	snd_pcm_hw_params_t *hwparams;
+  	snd_pcm_sw_params_t *swparams;
+! 	int alsa_buffer_time;
+  	unsigned int alsa_period_time;
+  	snd_pcm_uframes_t alsa_buffer_size, alsa_period_size;
+  
+***************
+*** 816,824 ****
+  	alsa_stereo_convert_func = NULL;
+  	alsa_frequency_convert_func = NULL;
+  
+! 	outputf = snd_format_from_xmms(effectf->xmms_format,
+! 				       effectf->rate,
+! 				       effectf->channels);
+  
+  	debug("Opening device: %s", alsa_cfg.pcm_device);
+  	/* FIXME: Can snd_pcm_open() return EAGAIN? */
+--- 878,885 ----
+  	alsa_stereo_convert_func = NULL;
+  	alsa_frequency_convert_func = NULL;
+  
+! 	g_free(outputf);
+! 	outputf = snd_format_from_xmms(f->xmms_format, f->rate, f->channels);
+  
+  	debug("Opening device: %s", alsa_cfg.pcm_device);
+  	/* FIXME: Can snd_pcm_open() return EAGAIN? */
+***************
+*** 829,838 ****
+  		g_warning("alsa_setup(): Failed to open pcm device (%s): %s",
+  			  alsa_cfg.pcm_device, snd_strerror(-err));
+  		alsa_pcm = NULL;
+  		return -1;
+  	}
+- 	snd_pcm_nonblock(alsa_pcm, FALSE);
+  
+  	if (alsa_cfg.debug)
+  	{
+  		snd_pcm_info_t *info;
+--- 890,903 ----
+  		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;
+***************
+*** 856,872 ****
+  		return -1;
+  	}
+  
+! 	if (mmap &&
+! 	    (err = snd_pcm_hw_params_set_access(alsa_pcm, hwparams,
+! 						SND_PCM_ACCESS_MMAP_INTERLEAVED)) < 0)
+! 	{
+! 		g_message("alsa_setup(): Cannot set mmap'ed mode: %s. "
+! 			  "falling back to direct write", snd_strerror(-err));
+! 		mmap = 0;
+! 	}
+! 
+! 	if (!mmap &&
+! 	    (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",
+--- 921,927 ----
+  		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",
+***************
+*** 894,910 ****
+  				break;
+  			}
+  		}
+! 		if (outputf->format != effectf->format)
+  		{
+  			outputf->xmms_format =
+  				format_from_alsa(outputf->format);
+  			debug("Converting format from %d to %d",
+! 			      effectf->xmms_format, outputf->xmms_format);
+  			if (outputf->xmms_format < 0)
+  				return -1;
+  			alsa_convert_func =
+  				xmms_convert_get_func(outputf->xmms_format,
+! 						      effectf->xmms_format);
+  			if (alsa_convert_func == NULL)
+  				return -1;
+  		}
+--- 949,965 ----
+  				break;
+  			}
+  		}
+! 		if (outputf->format != f->format)
+  		{
+  			outputf->xmms_format =
+  				format_from_alsa(outputf->format);
+  			debug("Converting format from %d to %d",
+! 			      f->xmms_format, outputf->xmms_format);
+  			if (outputf->xmms_format < 0)
+  				return -1;
+  			alsa_convert_func =
+  				xmms_convert_get_func(outputf->xmms_format,
+! 						      f->xmms_format);
+  			if (alsa_convert_func == NULL)
+  				return -1;
+  		}
+***************
+*** 918,931 ****
+  	}
+  
+  	snd_pcm_hw_params_set_channels_near(alsa_pcm, hwparams, &outputf->channels);
+! 	if (outputf->channels != effectf->channels)
+  	{
+  		debug("Converting channels from %d to %d",
+! 		      effectf->channels, outputf->channels);
+  		alsa_stereo_convert_func =
+  			xmms_convert_get_channel_func(outputf->xmms_format,
+  						      outputf->channels,
+! 						      effectf->channels);
+  		if (alsa_stereo_convert_func == NULL)
+  			return -1;
+  	}
+--- 973,986 ----
+  	}
+  
+  	snd_pcm_hw_params_set_channels_near(alsa_pcm, hwparams, &outputf->channels);
+! 	if (outputf->channels != f->channels)
+  	{
+  		debug("Converting channels from %d to %d",
+! 		      f->channels, outputf->channels);
+  		alsa_stereo_convert_func =
+  			xmms_convert_get_channel_func(outputf->xmms_format,
+  						      outputf->channels,
+! 						      f->channels);
+  		if (alsa_stereo_convert_func == NULL)
+  			return -1;
+  	}
+***************
+*** 936,945 ****
+  		g_warning("alsa_setup(): No usable samplerate available.");
+  		return -1;
+  	}
+! 	if (outputf->rate != effectf->rate)
+  	{
+  		debug("Converting samplerate from %d to %d",
+! 		      effectf->rate, outputf->rate);
+  		alsa_frequency_convert_func =
+  			xmms_convert_get_frequency_func(outputf->xmms_format,
+  							outputf->channels);
+--- 991,1000 ----
+  		g_warning("alsa_setup(): No usable samplerate available.");
+  		return -1;
+  	}
+! 	if (outputf->rate != f->rate)
+  	{
+  		debug("Converting samplerate from %d to %d",
+! 		      f->rate, outputf->rate);
+  		alsa_frequency_convert_func =
+  			xmms_convert_get_frequency_func(outputf->xmms_format,
+  							outputf->channels);
+***************
+*** 947,960 ****
+  			return -1;
+  	}
+  
+! 	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,
+--- 1002,1009 ----
+  			return -1;
+  	}
+  
+! 	outputf->sample_bits = snd_pcm_format_physical_width(outputf->format);
+! 	outputf->bps = (outputf->rate * outputf->sample_bits * outputf->channels) >> 3;
+  
+  	alsa_period_time = alsa_cfg.period_time * 1000;
+  	if ((err = snd_pcm_hw_params_set_period_time_near(alsa_pcm, hwparams,
+***************
+*** 965,970 ****
+--- 1014,1028 ----
+  		return -1;
+  	}
+  
++ 	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;
++ 	}
++ 
+  	if (snd_pcm_hw_params(alsa_pcm, hwparams) < 0)
+  	{
+  		if (alsa_cfg.debug)
+***************
+*** 1011,1037 ****
+  		snd_pcm_dump(alsa_pcm, logs);
+  	}
+  
+! 	bits_per_sample = snd_pcm_format_physical_width(outputf->format);
+! 	alsa_bps = (outputf->rate * bits_per_sample * outputf->channels) >> 3;
+! 
+! 	if (mmap)
+! 	{
+! 		int chn;
+! 		buffer = g_malloc(alsa_period_size * bits_per_sample / 8 * outputf->channels);
+! 		areas = g_malloc0(outputf->channels * sizeof(snd_pcm_channel_area_t));
+! 		
+! 		for (chn = 0; chn < outputf->channels; chn++)
+! 		{
+! 			areas[chn].addr = buffer;
+! 			areas[chn].first = chn * bits_per_sample;
+! 			areas[chn].step = outputf->channels * bits_per_sample;
+! 		}
+  	}
+  
+  	debug("Device setup: buffer time: %i, size: %i.", alsa_buffer_time,
+! 	      snd_pcm_frames_to_bytes(alsa_pcm, alsa_buffer_size));
+  	debug("bits per sample: %i; frame size: %i; Bps: %i",
+! 	      bits_per_sample, snd_pcm_frames_to_bytes(alsa_pcm, 1), alsa_bps);
+  
+  	return 0;
+  }
+--- 1069,1096 ----
+  		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),
+! 	      snd_pcm_frames_to_bytes(alsa_pcm, 1), outputf->bps);
+  
+  	return 0;
+  }
+diff -rc xmms-1.2.10-orig/Output/alsa/configure.c xmms-1.2.10/Output/alsa/configure.c
+*** xmms-1.2.10-orig/Output/alsa/configure.c	2004-01-28 00:09:39.000000000 +0100
+--- xmms-1.2.10/Output/alsa/configure.c	2006-01-27 00:28:49.000000000 +0100
+***************
+*** 20,27 ****
+  #include <stdio.h>
+  
+  static GtkWidget *configure_win = NULL;
+! static GtkWidget *buffer_time_spin, *period_time_spin;
+! static GtkWidget *mmap_button, *mixer_card_spin, *softvolume_toggle_button;
+  
+  static GtkWidget *devices_combo, *mixer_devices_combo;
+  
+--- 20,27 ----
+  #include <stdio.h>
+  
+  static GtkWidget *configure_win = NULL;
+! static GtkWidget *buffer_time_spin, *period_time_spin, *thread_buffer_time_spin;
+! static GtkWidget *mixer_card_spin, *softvolume_toggle_button;
+  
+  static GtkWidget *devices_combo, *mixer_devices_combo;
+  
+***************
+*** 36,42 ****
+  	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.mmap = GET_TOGGLE(mmap_button);
+  	alsa_cfg.soft_volume = GET_TOGGLE(softvolume_toggle_button);
+  	alsa_cfg.mixer_card = GET_SPIN_INT(mixer_card_spin);
+  	alsa_cfg.mixer_device = GET_CHARS(GTK_COMBO(mixer_devices_combo)->entry);
+--- 36,42 ----
+  	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.thread_buffer_time = GET_SPIN_INT(thread_buffer_time_spin);
+  	alsa_cfg.soft_volume = GET_TOGGLE(softvolume_toggle_button);
+  	alsa_cfg.mixer_card = GET_SPIN_INT(mixer_card_spin);
+  	alsa_cfg.mixer_device = GET_CHARS(GTK_COMBO(mixer_devices_combo)->entry);
+***************
+*** 51,57 ****
+  	
+  	xmms_cfg_write_int(cfgfile, "ALSA", "buffer_time", alsa_cfg.buffer_time);
+  	xmms_cfg_write_int(cfgfile, "ALSA", "period_time", alsa_cfg.period_time);
+! 	xmms_cfg_write_boolean(cfgfile,"ALSA","mmap",alsa_cfg.mmap);
+  	xmms_cfg_write_string(cfgfile,"ALSA","pcm_device", alsa_cfg.pcm_device);
+  	xmms_cfg_write_int(cfgfile, "ALSA", "mixer_card", alsa_cfg.mixer_card);
+  	xmms_cfg_write_string(cfgfile,"ALSA","mixer_device", alsa_cfg.mixer_device);
+--- 51,57 ----
+  	
+  	xmms_cfg_write_int(cfgfile, "ALSA", "buffer_time", alsa_cfg.buffer_time);
+  	xmms_cfg_write_int(cfgfile, "ALSA", "period_time", alsa_cfg.period_time);
+! 	xmms_cfg_write_int(cfgfile, "ALSA", "thread_buffer_time", alsa_cfg.thread_buffer_time);
+  	xmms_cfg_write_string(cfgfile,"ALSA","pcm_device", alsa_cfg.pcm_device);
+  	xmms_cfg_write_int(cfgfile, "ALSA", "mixer_card", alsa_cfg.mixer_card);
+  	xmms_cfg_write_string(cfgfile,"ALSA","mixer_device", alsa_cfg.mixer_device);
+***************
+*** 212,219 ****
+  	GtkWidget *dev_vbox, *adevice_frame, *adevice_box;
+  	GtkWidget *mixer_frame, *mixer_box, *mixer_card_box;
+  	GtkWidget *buffer_frame, *buffer_vbox, *buffer_table;
+! 	GtkWidget *buffer_time_label, *period_time_label;
+! 	GtkObject *buffer_time_adj, *period_time_adj, *mixer_card_adj;
+  	GtkWidget *bbox, *ok, *cancel;
+  	
+  	if (configure_win)
+--- 212,219 ----
+  	GtkWidget *dev_vbox, *adevice_frame, *adevice_box;
+  	GtkWidget *mixer_frame, *mixer_box, *mixer_card_box;
+  	GtkWidget *buffer_frame, *buffer_vbox, *buffer_table;
+! 	GtkWidget *buffer_time_label, *period_time_label, *thread_buffer_time_label;
+! 	GtkObject *buffer_time_adj, *period_time_adj, *thread_buffer_time_adj, *mixer_card_adj;
+  	GtkWidget *bbox, *ok, *cancel;
+  	
+  	if (configure_win)
+***************
+*** 312,318 ****
+  	
+  	gtk_container_set_border_width(GTK_CONTAINER(buffer_vbox), 5);
+  
+! 	buffer_table = gtk_table_new(2, 2, FALSE);
+  	gtk_table_set_row_spacings(GTK_TABLE(buffer_table), 5);
+  	gtk_table_set_col_spacings(GTK_TABLE(buffer_table), 5);
+  	gtk_box_pack_start(GTK_BOX(buffer_vbox), buffer_table, FALSE, FALSE, 0);
+--- 312,318 ----
+  	
+  	gtk_container_set_border_width(GTK_CONTAINER(buffer_vbox), 5);
+  
+! 	buffer_table = gtk_table_new(2, 3, FALSE);
+  	gtk_table_set_row_spacings(GTK_TABLE(buffer_table), 5);
+  	gtk_table_set_col_spacings(GTK_TABLE(buffer_table), 5);
+  	gtk_box_pack_start(GTK_BOX(buffer_vbox), buffer_table, FALSE, FALSE, 0);
+***************
+*** 345,354 ****
+  	gtk_table_attach(GTK_TABLE(buffer_table), period_time_spin,
+  			 1, 2, 1, 2, 0, 0, 0, 0);
+  	
+! 	mmap_button = gtk_check_button_new_with_label(_("Mmap mode"));
+! 	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(mmap_button),
+! 				     alsa_cfg.mmap);
+! 	gtk_box_pack_start(GTK_BOX(buffer_vbox), mmap_button, FALSE, FALSE, 0);
+  	
+  	gtk_notebook_append_page(GTK_NOTEBOOK(notebook), buffer_frame,
+  				 gtk_label_new(_("Advanced settings")));
+--- 345,363 ----
+  	gtk_table_attach(GTK_TABLE(buffer_table), period_time_spin,
+  			 1, 2, 1, 2, 0, 0, 0, 0);
+  	
+! 	thread_buffer_time_label = gtk_label_new(_("Thread buffer time (ms):"));
+! 	gtk_label_set_justify(GTK_LABEL(thread_buffer_time_label), GTK_JUSTIFY_LEFT);
+! 	gtk_misc_set_alignment(GTK_MISC(thread_buffer_time_label), 0, 0.5);
+! 	gtk_table_attach(GTK_TABLE(buffer_table), thread_buffer_time_label,
+! 			 0, 1, 2, 3, GTK_FILL, 0, 0, 0);
+! 	thread_buffer_time_adj = gtk_adjustment_new(alsa_cfg.thread_buffer_time,
+! 						    1000, 1000000, 100, 100, 100);
+! 	thread_buffer_time_spin = gtk_spin_button_new(GTK_ADJUSTMENT(thread_buffer_time_adj),
+! 						      8, 0);
+! 	
+! 	gtk_widget_set_usize(thread_buffer_time_spin, 60, -1);
+! 	gtk_table_attach(GTK_TABLE(buffer_table), thread_buffer_time_spin,
+! 			 1, 2, 2, 3, 0, 0, 0, 0);
+  	
+  	gtk_notebook_append_page(GTK_NOTEBOOK(notebook), buffer_frame,
+  				 gtk_label_new(_("Advanced settings")));
+diff -rc xmms-1.2.10-orig/Output/alsa/init.c xmms-1.2.10/Output/alsa/init.c
+*** xmms-1.2.10-orig/Output/alsa/init.c	2004-01-11 17:27:26.000000000 +0100
+--- xmms-1.2.10/Output/alsa/init.c	2006-01-27 00:28:49.000000000 +0100
+***************
+*** 29,36 ****
+  	memset(&alsa_cfg, 0, sizeof (alsa_cfg));
+  	alsa_cfg.buffer_time = 500;
+  	alsa_cfg.period_time = 50;
+  	alsa_cfg.debug = 0;
+- 	alsa_cfg.mmap = 1;
+  	alsa_cfg.vol.left = 100;
+  	alsa_cfg.vol.right = 100;
+  
+--- 29,36 ----
+  	memset(&alsa_cfg, 0, sizeof (alsa_cfg));
+  	alsa_cfg.buffer_time = 500;
+  	alsa_cfg.period_time = 50;
++ 	alsa_cfg.thread_buffer_time = 3000;
+  	alsa_cfg.debug = 0;
+  	alsa_cfg.vol.left = 100;
+  	alsa_cfg.vol.right = 100;
+  
+***************
+*** 44,51 ****
+  	xmms_cfg_read_int(cfgfile, "ALSA", "mixer_card", &alsa_cfg.mixer_card);
+  	xmms_cfg_read_int(cfgfile, "ALSA", "buffer_time", &alsa_cfg.buffer_time);
+  	xmms_cfg_read_int(cfgfile, "ALSA", "period_time", &alsa_cfg.period_time);
+! 	xmms_cfg_read_boolean(cfgfile, "ALSA", "mmap", &alsa_cfg.mmap);
+! 	xmms_cfg_read_int(cfgfile, "ALSA", "period_time", &alsa_cfg.period_time);
+  	xmms_cfg_read_boolean(cfgfile, "ALSA", "soft_volume",
+  			      &alsa_cfg.soft_volume);
+  	xmms_cfg_read_int(cfgfile, "ALSA", "volume_left", &alsa_cfg.vol.left);
+--- 44,50 ----
+  	xmms_cfg_read_int(cfgfile, "ALSA", "mixer_card", &alsa_cfg.mixer_card);
+  	xmms_cfg_read_int(cfgfile, "ALSA", "buffer_time", &alsa_cfg.buffer_time);
+  	xmms_cfg_read_int(cfgfile, "ALSA", "period_time", &alsa_cfg.period_time);
+! 	xmms_cfg_read_int(cfgfile, "ALSA", "thread_buffer_time", &alsa_cfg.thread_buffer_time);
+  	xmms_cfg_read_boolean(cfgfile, "ALSA", "soft_volume",
+  			      &alsa_cfg.soft_volume);
+  	xmms_cfg_read_int(cfgfile, "ALSA", "volume_left", &alsa_cfg.vol.left);
diff --git a/pkgs/applications/audio/xmms/default.nix b/pkgs/applications/audio/xmms/default.nix
index a0e2d4729179..a7743b861ebb 100644
--- a/pkgs/applications/audio/xmms/default.nix
+++ b/pkgs/applications/audio/xmms/default.nix
@@ -7,5 +7,8 @@ stdenv.mkDerivation {
     md5 = "03a85cfc5e1877a2e1f7be4fa1d3f63c" ;
   };
 
+  # Patch borrowed from SuSE 10.0 to fix pause/continue on ALSA.
+  patches = [./alsa.patch];
+
   buildInputs = [alsaLib esound libogg libvorbis glib gtk];
 }