changeset 6749:7f619a5d6eb8

added real mmap support support for -abs opt, 4 different modes
author joyping
date Thu, 18 Jul 2002 16:09:54 +0000
parents c0e8034d47e4
children 6484f8f9f111
files libao2/ao_alsa1x.c libao2/ao_alsa9.c
diffstat 2 files changed, 468 insertions(+), 52 deletions(-) [+]
line wrap: on
line diff
--- a/libao2/ao_alsa1x.c	Thu Jul 18 14:59:55 2002 +0000
+++ b/libao2/ao_alsa1x.c	Thu Jul 18 16:09:54 2002 +0000
@@ -14,6 +14,7 @@
 #include <stdlib.h>
 #include <math.h>
 #include <string.h>
+#include <sys/poll.h>
 
 #include "../config.h"
 
@@ -48,16 +49,21 @@
 static snd_pcm_sw_params_t *alsa_swparams;
 static char *alsa_device;
 
-/* possible 4096, original 8192, OUTBURST is set statically to 512 in config.h */
-static int alsa_fragsize = 4096; //OUTBURST
-static int alsa_fragcount = 8;
+/* possible 4096, original 8192 
+ * was only needed for calculating chunksize? */
+static int alsa_fragsize = 4096;
+/* 16 sets buffersize to 16 * chunksize is as default 1024
+ * which seems to be good avarge for most situations 
+ * so buffersize is 16384 frames by default */
+static int alsa_fragcount = 16;
+static int chunk_size = 1024; //is alsa_fragsize / 4
 
-static int chunk_size = -1;
 static size_t bits_per_sample, bits_per_frame;
 static size_t chunk_bytes;
 
 int ao_mmap = 0;
 int ao_noblock = 0;
+int first = 1;
 
 static int open_mode;
 static int set_block_mode;
@@ -66,13 +72,13 @@
 
 #undef BUFFERTIME
 #define SET_CHUNKSIZE
+#undef USE_POLL
 
-snd_pcm_t *
-
-spdif_init(char *pcm_name)
+snd_pcm_t *spdif_init(char *pcm_name)
 {
 	//char *pcm_name = "hw:0,2"; /* first card second device */
 	static snd_aes_iec958_t spdif;
+	static snd_aes_iec958_t spdif_test;
 	snd_pcm_info_t 	*info;
 	snd_pcm_t *handler;
 	snd_pcm_format_t format = SND_PCM_FORMAT_S16_LE;
@@ -96,10 +102,15 @@
         printf("device: %d, subdevice: %d\n", snd_pcm_info_get_device(info),
                 snd_pcm_info_get_subdevice(info));                              
 	{
+
         snd_ctl_elem_value_t *ctl;
         snd_ctl_t *ctl_handler;
+	snd_ctl_elem_id_t *elem_id;
+	unsigned int elem_device;
+	const char *elem_name;
         char ctl_name[12];
         int ctl_card;
+	//elem_id = 36;
 
 	spdif.status[0] = IEC958_AES0_NONAUDIO |
 			  IEC958_AES0_CON_EMPHASIS_NONE;
@@ -109,16 +120,18 @@
 	spdif.status[3] = IEC958_AES3_CON_FS_48000;
 
         snd_ctl_elem_value_alloca(&ctl);
-        snd_ctl_elem_value_set_interface(ctl, SND_CTL_ELEM_IFACE_PCM);
+	snd_ctl_elem_id_alloca(&elem_id); 
+	snd_ctl_elem_value_set_interface(ctl, SND_CTL_ELEM_IFACE_PCM); //SND_CTL_ELEM_IFACE_MIXER
         snd_ctl_elem_value_set_device(ctl, snd_pcm_info_get_device(info));
         snd_ctl_elem_value_set_subdevice(ctl, snd_pcm_info_get_subdevice(info));
-        snd_ctl_elem_value_set_name(ctl, SND_CTL_NAME_IEC958("", PLAYBACK,PCM_STREAM));
-        snd_ctl_elem_value_set_iec958(ctl, &spdif);
+        snd_ctl_elem_value_set_name(ctl,SND_CTL_NAME_IEC958("", PLAYBACK, PCM_STREAM)); //SWITCH
+	snd_ctl_elem_value_set_iec958(ctl, &spdif);
         ctl_card = snd_pcm_info_get_card(info);
         if (ctl_card < 0) {
            fprintf(stderr, "Unable to setup the IEC958 (S/PDIF) interface - PCM has no assigned card");
            goto __diga_end;
         }
+
        sprintf(ctl_name, "hw:%d", ctl_card);
        printf("hw:%d\n", ctl_card);
        if ((err = snd_ctl_open(&ctl_handler, ctl_name, 0)) < 0) {
@@ -126,9 +139,19 @@
           goto __diga_end;
        }
        if ((err = snd_ctl_elem_write(ctl_handler, ctl)) < 0) {
-        fprintf(stderr, "Unable to update the IEC958 control: %s\n", snd_strerror(err));
+	 //fprintf(stderr, "Unable to update the IEC958 control: %s\n", snd_strerror(err));
+	 printf("alsa-spdif-init: cant set spdif-trough automatically\n");
         goto __diga_end;
        }
+       	//test area
+       /* elem_device = snd_ctl_elem_id_get_device(elem_id); */
+       /* elem_name = snd_ctl_elem_value_get_name(ctl);  */
+       /* snd_ctl_elem_value_get_iec958(ctl, &spdif); */
+       /* printf("spdif = %i, device = %i\n", &spdif, elem_device); */
+       /* printf("name = %s\n", elem_name); */
+	//end test area
+
+
       snd_ctl_close(ctl_handler);
       __diga_end:                                                       
 
@@ -318,7 +341,7 @@
     ao_data.format = format;
     ao_data.channels = channels;
     ao_data.outburst = OUTBURST;
-    ao_data.buffersize = MAX_OUTBURST; // was 16384
+    //ao_data.buffersize = MAX_OUTBURST; // was 16384
 
     switch (format)
     {
@@ -489,7 +512,53 @@
       set_block_mode = 0;
       str_block_mode = "block-mode";
     }
-      
+    //cvs cosmetics fix
+    //sets buff/chunksize if its set manually
+    if (ao_data.buffersize) {
+      switch (ao_data.buffersize)
+	{
+	case 1:
+	  alsa_fragcount = 16;
+	  chunk_size = 512;
+	  if (verbose) {
+	    printf("alsa-init: buffersize set manually to 8192\n");
+	    printf("alsa-init: chunksize set manually to 512\n");
+	  }
+	  break;
+	case 2:
+	  alsa_fragcount = 8;
+	  chunk_size = 1024;
+	  if (verbose) {
+	    printf("alsa-init: buffersize set manually to 8192\n");
+	    printf("alsa-init: chunksize set manually to 1024\n");
+	  }
+	  break;
+	case 3:
+	  alsa_fragcount = 32;
+	  chunk_size = 512;
+	  if (verbose) {
+	    printf("alsa-init: buffersize set manually to 16384\n");
+	    printf("alsa-init: chunksize set manually to 512\n");
+	  }
+	  break;
+	case 4:
+	  alsa_fragcount = 16;
+	  chunk_size = 1024;
+	  if (verbose) {
+	    printf("alsa-init: buffersize set manually to 16384\n");
+	    printf("alsa-init: chunksize set manually to 1024\n");
+	  }
+	  break;
+	default:
+	  alsa_fragcount = 16;
+	  if (ao_mmap)
+	    chunk_size = 512;
+	  else
+	    chunk_size = 1024;
+	  break;
+	}
+    }
+
     if (!alsa_handler) {
       //modes = 0, SND_PCM_NONBLOCK, SND_PCM_ASYNC
       if ((err = snd_pcm_open(&alsa_handler, alsa_device, SND_PCM_STREAM_PLAYBACK, open_mode)) < 0)
@@ -537,7 +606,6 @@
 	printf("alsa-init: mmap set\n");
       } else {
 	err = snd_pcm_hw_params_set_access(alsa_handler, alsa_hwparams,SND_PCM_ACCESS_RW_INTERLEAVED);
-	printf("alsa-init: interleave set\n");
       }
       if (err < 0) {
 	printf("alsa-init: unable to set access type: %s\n", snd_strerror(err));
@@ -594,8 +662,6 @@
 #ifdef SET_CHUNKSIZE
       {
 	//set chunksize
-	chunk_size = alsa_fragsize / 4;
-
 	if ((err = snd_pcm_hw_params_set_period_size(alsa_handler, alsa_hwparams, chunk_size, 0)) < 0)
 	  {
 	    printf("alsa-init: unable to set periodsize: %s\n", snd_strerror(err));
@@ -653,7 +719,7 @@
 
 	  //set min available frames to consider pcm ready (4)
 	  //increased for nonblock-mode should be set dynamically later
-	  if ((err = snd_pcm_sw_params_set_avail_min(alsa_handler, alsa_swparams, chunk_size)) < 0)
+	  if ((err = snd_pcm_sw_params_set_avail_min(alsa_handler, alsa_swparams, 4)) < 0)
 	    {
 	      printf("alsa-init: unable to set avail_min %s\n",snd_strerror(err));
 	      return(0);
@@ -776,6 +842,22 @@
     }
 }
 
+#ifdef USE_POLL
+static int wait_for_poll(snd_pcm_t *handle, struct pollfd *ufds, unsigned int count)
+{
+  unsigned short revents;
+
+  while (1) {
+    poll(ufds, count, -1);
+    snd_pcm_poll_descriptors_revents(handle, ufds, count, &revents);
+    if (revents & POLLERR)
+      return -EIO;
+    if (revents & POLLOUT)
+      return 0;
+  }
+} 
+#endif
+
 #ifndef timersub
 #define timersub(a, b, result) \
 do { \
@@ -819,6 +901,17 @@
   return(1); /* ok, data should be accepted again */
 }
 
+static int play(void* data, int len, int flags)
+{
+  int result;
+  if (ao_mmap)
+    result = play_mmap(data, len);
+  else
+    result = play_normal(data, len);
+
+  return result;
+}
+
 /*
     plays 'len' bytes of 'data'
     returns: number of bytes played
@@ -826,10 +919,10 @@
     thanxs for marius <marius@rospot.com> for giving us the light ;)
 */
 
-static int play(void* data, int len, int flags)
+static int play_normal(void* data, int len)
 {
 
-  //ao_data.bps is always 4 cause its set to channels * 2 by alsa_format??
+  //ao_data.bps is always 4 for 2 chn S16_LE
   int num_frames = len / ao_data.bps;
   signed short *output_samples=data;
   snd_pcm_sframes_t res = 0;
@@ -843,11 +936,7 @@
 
   while (num_frames > 0) {
 
-      if (ao_mmap) {
-	res = snd_pcm_mmap_writei(alsa_handler, (void *)output_samples, num_frames);
-      } else {
-	res = snd_pcm_writei(alsa_handler, (void *)output_samples, num_frames);
-      }
+    res = snd_pcm_writei(alsa_handler, (void *)output_samples, num_frames);
 
       if (res == -EAGAIN) {
 	snd_pcm_wait(alsa_handler, 1000);
@@ -886,6 +975,115 @@
   return res < 0 ? (int)res : len;
 }
 
+/* mmap-mode mainly based on descriptions by Joshua Haberman <joshua@haberman.com>
+ * 'An overview of the ALSA API' http://people.debian.org/~joshua/x66.html
+ * and some help by Paul Davis <pbd@op.net> */
+
+static int play_mmap(void* data, int len)
+{
+  snd_pcm_sframes_t commitres, frames_available;
+  snd_pcm_uframes_t frames_transmit, size, offset;
+  const snd_pcm_channel_area_t *area;
+  void *outbuffer;
+  int err, result;
+
+#ifdef USE_POLL //seems not really be needed
+  struct pollfd *ufds;
+  int count;
+
+  count = snd_pcm_poll_descriptors_count (alsa_handler);
+  ufds = malloc(sizeof(struct pollfd) * count);
+  snd_pcm_poll_descriptors(alsa_handler, ufds, count);
+
+  //first wait_for_poll
+    if (err = (wait_for_poll(alsa_handler, ufds, count) < 0)) {
+      if (snd_pcm_state(alsa_handler) == SND_PCM_STATE_XRUN || 
+	  snd_pcm_state(alsa_handler) == SND_PCM_STATE_SUSPENDED) {
+        xrun("play");
+      }
+    }
+#endif
+
+  outbuffer = alloca(ao_data.buffersize);
+
+  //don't trust get_space() ;)
+  frames_available = snd_pcm_avail_update(alsa_handler) * ao_data.bps;
+  if (frames_available < 0)
+    xrun("play");
+
+  if (frames_available < 4) {
+    if (first) {
+      first = 0;
+      snd_pcm_start(alsa_handler);
+    }
+    else { //FIXME should break and return 0?
+      snd_pcm_wait(alsa_handler, -1);
+      first = 1;
+    }
+  }
+
+  /* len is simply the available bufferspace got by get_space() 
+   * but real avail_buffer in frames is ab/ao_data.bps */
+  size = len / ao_data.bps;
+
+  //if (verbose)
+  //printf("len: %i size %i, f_avail %i, bps %i ...\n", len, size, frames_available, ao_data.bps);
+
+  frames_transmit = size;
+
+  /* prepare areas and set sw-pointers
+   * frames_transmit returns the real available buffer-size
+   * sometimes != frames_available cause of ringbuffer 'emulation' */
+  snd_pcm_mmap_begin(alsa_handler, &area, &offset, &frames_transmit);
+
+  /* this is specific to interleaved streams (or non-interleaved
+   * streams with only one channel) */
+  outbuffer = ((char *) area->addr + (area->first + area->step * offset) / 8); //8
+
+  //write data
+  memcpy(outbuffer, data, (frames_transmit * ao_data.bps));
+
+  commitres = snd_pcm_mmap_commit(alsa_handler, offset, frames_transmit);
+
+  if (commitres < 0 || commitres != frames_transmit) {
+    if (snd_pcm_state(alsa_handler) == SND_PCM_STATE_XRUN || 
+	snd_pcm_state(alsa_handler) == SND_PCM_STATE_SUSPENDED) {
+      xrun("play");
+    }
+  }
+
+  //if (verbose)
+  //printf("mmap ft: %i, cres: %i\n", frames_transmit, commitres);
+
+  /* 	err = snd_pcm_area_copy(&area, offset, &data, offset, len, alsa_format); */
+  /* 	if (err < 0) { */
+  /* 	  printf("area-copy-error\n"); */
+  /* 	  return 0; */
+  /* 	} */
+
+
+  //calculate written frames!
+  result = commitres * ao_data.bps;
+
+
+  /* if (verbose) { */
+  /* if (len == result) */
+  /* printf("result: %i, frames written: %i ...\n", result, frames_transmit); */
+  /* else */
+  /* printf("result: %i, frames written: %i, result != len ...\n", result, frames_transmit); */
+  /* } */
+
+  //mplayer doesn't like -result
+  if (result < 0)
+    result = 0;
+
+#ifdef USE_POLL
+  free(ufds);
+#endif
+
+  return result;
+}
+
 /* how many byes are free in the buffer */
 static int get_space()
 {
@@ -912,8 +1110,14 @@
     case SND_PCM_STATE_OPEN:
       str_status = "open";
     case SND_PCM_STATE_PREPARED:
-      if (str_status != "open")
+      if (str_status != "open") {
 	str_status = "prepared";
+	first = 1;
+	ret = snd_pcm_status_get_avail(status) * ao_data.bps;
+	if (ret == 0) //ugly workaround for hang in mmap-mode
+	  ret = 10;
+	break;
+      }
     case SND_PCM_STATE_RUNNING:
       ret = snd_pcm_status_get_avail(status) * ao_data.bps;
       //avail_frames = snd_pcm_avail_update(alsa_handler) * ao_data.bps;
@@ -928,11 +1132,15 @@
     case SND_PCM_STATE_XRUN:
       xrun("space");
       str_status = "xrun";
+      first = 1;
       ret = 0;
       break;
     default:
       str_status = "undefined";
-      ret = 0;
+      ret = snd_pcm_status_get_avail(status) * ao_data.bps;
+      if (ret <= 0) {
+	xrun("space");
+      }
     }
 
     if (verbose && str_status != "running")
--- a/libao2/ao_alsa9.c	Thu Jul 18 14:59:55 2002 +0000
+++ b/libao2/ao_alsa9.c	Thu Jul 18 16:09:54 2002 +0000
@@ -14,6 +14,7 @@
 #include <stdlib.h>
 #include <math.h>
 #include <string.h>
+#include <sys/poll.h>
 
 #include "../config.h"
 
@@ -48,16 +49,21 @@
 static snd_pcm_sw_params_t *alsa_swparams;
 static char *alsa_device;
 
-/* possible 4096, original 8192, OUTBURST is set statically to 512 in config.h */
-static int alsa_fragsize = 4096; //OUTBURST
-static int alsa_fragcount = 8;
+/* possible 4096, original 8192 
+ * was only needed for calculating chunksize? */
+static int alsa_fragsize = 4096;
+/* 16 sets buffersize to 16 * chunksize is as default 1024
+ * which seems to be good avarge for most situations 
+ * so buffersize is 16384 frames by default */
+static int alsa_fragcount = 16;
+static int chunk_size = 1024; //is alsa_fragsize / 4
 
-static int chunk_size = -1;
 static size_t bits_per_sample, bits_per_frame;
 static size_t chunk_bytes;
 
 int ao_mmap = 0;
 int ao_noblock = 0;
+int first = 1;
 
 static int open_mode;
 static int set_block_mode;
@@ -66,13 +72,13 @@
 
 #undef BUFFERTIME
 #define SET_CHUNKSIZE
+#undef USE_POLL
 
-snd_pcm_t *
-
-spdif_init(char *pcm_name)
+snd_pcm_t *spdif_init(char *pcm_name)
 {
 	//char *pcm_name = "hw:0,2"; /* first card second device */
 	static snd_aes_iec958_t spdif;
+	static snd_aes_iec958_t spdif_test;
 	snd_pcm_info_t 	*info;
 	snd_pcm_t *handler;
 	snd_pcm_format_t format = SND_PCM_FORMAT_S16_LE;
@@ -96,10 +102,15 @@
         printf("device: %d, subdevice: %d\n", snd_pcm_info_get_device(info),
                 snd_pcm_info_get_subdevice(info));                              
 	{
+
         snd_ctl_elem_value_t *ctl;
         snd_ctl_t *ctl_handler;
+	snd_ctl_elem_id_t *elem_id;
+	unsigned int elem_device;
+	const char *elem_name;
         char ctl_name[12];
         int ctl_card;
+	//elem_id = 36;
 
 	spdif.status[0] = IEC958_AES0_NONAUDIO |
 			  IEC958_AES0_CON_EMPHASIS_NONE;
@@ -109,16 +120,18 @@
 	spdif.status[3] = IEC958_AES3_CON_FS_48000;
 
         snd_ctl_elem_value_alloca(&ctl);
-        snd_ctl_elem_value_set_interface(ctl, SND_CTL_ELEM_IFACE_PCM);
+	snd_ctl_elem_id_alloca(&elem_id); 
+	snd_ctl_elem_value_set_interface(ctl, SND_CTL_ELEM_IFACE_PCM); //SND_CTL_ELEM_IFACE_MIXER
         snd_ctl_elem_value_set_device(ctl, snd_pcm_info_get_device(info));
         snd_ctl_elem_value_set_subdevice(ctl, snd_pcm_info_get_subdevice(info));
-        snd_ctl_elem_value_set_name(ctl, SND_CTL_NAME_IEC958("", PLAYBACK,PCM_STREAM));
-        snd_ctl_elem_value_set_iec958(ctl, &spdif);
+        snd_ctl_elem_value_set_name(ctl,SND_CTL_NAME_IEC958("", PLAYBACK, PCM_STREAM)); //SWITCH
+	snd_ctl_elem_value_set_iec958(ctl, &spdif);
         ctl_card = snd_pcm_info_get_card(info);
         if (ctl_card < 0) {
            fprintf(stderr, "Unable to setup the IEC958 (S/PDIF) interface - PCM has no assigned card");
            goto __diga_end;
         }
+
        sprintf(ctl_name, "hw:%d", ctl_card);
        printf("hw:%d\n", ctl_card);
        if ((err = snd_ctl_open(&ctl_handler, ctl_name, 0)) < 0) {
@@ -126,9 +139,19 @@
           goto __diga_end;
        }
        if ((err = snd_ctl_elem_write(ctl_handler, ctl)) < 0) {
-        fprintf(stderr, "Unable to update the IEC958 control: %s\n", snd_strerror(err));
+	 //fprintf(stderr, "Unable to update the IEC958 control: %s\n", snd_strerror(err));
+	 printf("alsa-spdif-init: cant set spdif-trough automatically\n");
         goto __diga_end;
        }
+       	//test area
+       /* elem_device = snd_ctl_elem_id_get_device(elem_id); */
+       /* elem_name = snd_ctl_elem_value_get_name(ctl);  */
+       /* snd_ctl_elem_value_get_iec958(ctl, &spdif); */
+       /* printf("spdif = %i, device = %i\n", &spdif, elem_device); */
+       /* printf("name = %s\n", elem_name); */
+	//end test area
+
+
       snd_ctl_close(ctl_handler);
       __diga_end:                                                       
 
@@ -318,7 +341,7 @@
     ao_data.format = format;
     ao_data.channels = channels;
     ao_data.outburst = OUTBURST;
-    ao_data.buffersize = MAX_OUTBURST; // was 16384
+    //ao_data.buffersize = MAX_OUTBURST; // was 16384
 
     switch (format)
     {
@@ -489,7 +512,53 @@
       set_block_mode = 0;
       str_block_mode = "block-mode";
     }
-      
+    //cvs cosmetics fix
+    //sets buff/chunksize if its set manually
+    if (ao_data.buffersize) {
+      switch (ao_data.buffersize)
+	{
+	case 1:
+	  alsa_fragcount = 16;
+	  chunk_size = 512;
+	  if (verbose) {
+	    printf("alsa-init: buffersize set manually to 8192\n");
+	    printf("alsa-init: chunksize set manually to 512\n");
+	  }
+	  break;
+	case 2:
+	  alsa_fragcount = 8;
+	  chunk_size = 1024;
+	  if (verbose) {
+	    printf("alsa-init: buffersize set manually to 8192\n");
+	    printf("alsa-init: chunksize set manually to 1024\n");
+	  }
+	  break;
+	case 3:
+	  alsa_fragcount = 32;
+	  chunk_size = 512;
+	  if (verbose) {
+	    printf("alsa-init: buffersize set manually to 16384\n");
+	    printf("alsa-init: chunksize set manually to 512\n");
+	  }
+	  break;
+	case 4:
+	  alsa_fragcount = 16;
+	  chunk_size = 1024;
+	  if (verbose) {
+	    printf("alsa-init: buffersize set manually to 16384\n");
+	    printf("alsa-init: chunksize set manually to 1024\n");
+	  }
+	  break;
+	default:
+	  alsa_fragcount = 16;
+	  if (ao_mmap)
+	    chunk_size = 512;
+	  else
+	    chunk_size = 1024;
+	  break;
+	}
+    }
+
     if (!alsa_handler) {
       //modes = 0, SND_PCM_NONBLOCK, SND_PCM_ASYNC
       if ((err = snd_pcm_open(&alsa_handler, alsa_device, SND_PCM_STREAM_PLAYBACK, open_mode)) < 0)
@@ -537,7 +606,6 @@
 	printf("alsa-init: mmap set\n");
       } else {
 	err = snd_pcm_hw_params_set_access(alsa_handler, alsa_hwparams,SND_PCM_ACCESS_RW_INTERLEAVED);
-	printf("alsa-init: interleave set\n");
       }
       if (err < 0) {
 	printf("alsa-init: unable to set access type: %s\n", snd_strerror(err));
@@ -594,8 +662,6 @@
 #ifdef SET_CHUNKSIZE
       {
 	//set chunksize
-	chunk_size = alsa_fragsize / 4;
-
 	if ((err = snd_pcm_hw_params_set_period_size(alsa_handler, alsa_hwparams, chunk_size, 0)) < 0)
 	  {
 	    printf("alsa-init: unable to set periodsize: %s\n", snd_strerror(err));
@@ -653,7 +719,7 @@
 
 	  //set min available frames to consider pcm ready (4)
 	  //increased for nonblock-mode should be set dynamically later
-	  if ((err = snd_pcm_sw_params_set_avail_min(alsa_handler, alsa_swparams, chunk_size)) < 0)
+	  if ((err = snd_pcm_sw_params_set_avail_min(alsa_handler, alsa_swparams, 4)) < 0)
 	    {
 	      printf("alsa-init: unable to set avail_min %s\n",snd_strerror(err));
 	      return(0);
@@ -776,6 +842,22 @@
     }
 }
 
+#ifdef USE_POLL
+static int wait_for_poll(snd_pcm_t *handle, struct pollfd *ufds, unsigned int count)
+{
+  unsigned short revents;
+
+  while (1) {
+    poll(ufds, count, -1);
+    snd_pcm_poll_descriptors_revents(handle, ufds, count, &revents);
+    if (revents & POLLERR)
+      return -EIO;
+    if (revents & POLLOUT)
+      return 0;
+  }
+} 
+#endif
+
 #ifndef timersub
 #define timersub(a, b, result) \
 do { \
@@ -819,6 +901,17 @@
   return(1); /* ok, data should be accepted again */
 }
 
+static int play(void* data, int len, int flags)
+{
+  int result;
+  if (ao_mmap)
+    result = play_mmap(data, len);
+  else
+    result = play_normal(data, len);
+
+  return result;
+}
+
 /*
     plays 'len' bytes of 'data'
     returns: number of bytes played
@@ -826,10 +919,10 @@
     thanxs for marius <marius@rospot.com> for giving us the light ;)
 */
 
-static int play(void* data, int len, int flags)
+static int play_normal(void* data, int len)
 {
 
-  //ao_data.bps is always 4 cause its set to channels * 2 by alsa_format??
+  //ao_data.bps is always 4 for 2 chn S16_LE
   int num_frames = len / ao_data.bps;
   signed short *output_samples=data;
   snd_pcm_sframes_t res = 0;
@@ -843,11 +936,7 @@
 
   while (num_frames > 0) {
 
-      if (ao_mmap) {
-	res = snd_pcm_mmap_writei(alsa_handler, (void *)output_samples, num_frames);
-      } else {
-	res = snd_pcm_writei(alsa_handler, (void *)output_samples, num_frames);
-      }
+    res = snd_pcm_writei(alsa_handler, (void *)output_samples, num_frames);
 
       if (res == -EAGAIN) {
 	snd_pcm_wait(alsa_handler, 1000);
@@ -886,6 +975,115 @@
   return res < 0 ? (int)res : len;
 }
 
+/* mmap-mode mainly based on descriptions by Joshua Haberman <joshua@haberman.com>
+ * 'An overview of the ALSA API' http://people.debian.org/~joshua/x66.html
+ * and some help by Paul Davis <pbd@op.net> */
+
+static int play_mmap(void* data, int len)
+{
+  snd_pcm_sframes_t commitres, frames_available;
+  snd_pcm_uframes_t frames_transmit, size, offset;
+  const snd_pcm_channel_area_t *area;
+  void *outbuffer;
+  int err, result;
+
+#ifdef USE_POLL //seems not really be needed
+  struct pollfd *ufds;
+  int count;
+
+  count = snd_pcm_poll_descriptors_count (alsa_handler);
+  ufds = malloc(sizeof(struct pollfd) * count);
+  snd_pcm_poll_descriptors(alsa_handler, ufds, count);
+
+  //first wait_for_poll
+    if (err = (wait_for_poll(alsa_handler, ufds, count) < 0)) {
+      if (snd_pcm_state(alsa_handler) == SND_PCM_STATE_XRUN || 
+	  snd_pcm_state(alsa_handler) == SND_PCM_STATE_SUSPENDED) {
+        xrun("play");
+      }
+    }
+#endif
+
+  outbuffer = alloca(ao_data.buffersize);
+
+  //don't trust get_space() ;)
+  frames_available = snd_pcm_avail_update(alsa_handler) * ao_data.bps;
+  if (frames_available < 0)
+    xrun("play");
+
+  if (frames_available < 4) {
+    if (first) {
+      first = 0;
+      snd_pcm_start(alsa_handler);
+    }
+    else { //FIXME should break and return 0?
+      snd_pcm_wait(alsa_handler, -1);
+      first = 1;
+    }
+  }
+
+  /* len is simply the available bufferspace got by get_space() 
+   * but real avail_buffer in frames is ab/ao_data.bps */
+  size = len / ao_data.bps;
+
+  //if (verbose)
+  //printf("len: %i size %i, f_avail %i, bps %i ...\n", len, size, frames_available, ao_data.bps);
+
+  frames_transmit = size;
+
+  /* prepare areas and set sw-pointers
+   * frames_transmit returns the real available buffer-size
+   * sometimes != frames_available cause of ringbuffer 'emulation' */
+  snd_pcm_mmap_begin(alsa_handler, &area, &offset, &frames_transmit);
+
+  /* this is specific to interleaved streams (or non-interleaved
+   * streams with only one channel) */
+  outbuffer = ((char *) area->addr + (area->first + area->step * offset) / 8); //8
+
+  //write data
+  memcpy(outbuffer, data, (frames_transmit * ao_data.bps));
+
+  commitres = snd_pcm_mmap_commit(alsa_handler, offset, frames_transmit);
+
+  if (commitres < 0 || commitres != frames_transmit) {
+    if (snd_pcm_state(alsa_handler) == SND_PCM_STATE_XRUN || 
+	snd_pcm_state(alsa_handler) == SND_PCM_STATE_SUSPENDED) {
+      xrun("play");
+    }
+  }
+
+  //if (verbose)
+  //printf("mmap ft: %i, cres: %i\n", frames_transmit, commitres);
+
+  /* 	err = snd_pcm_area_copy(&area, offset, &data, offset, len, alsa_format); */
+  /* 	if (err < 0) { */
+  /* 	  printf("area-copy-error\n"); */
+  /* 	  return 0; */
+  /* 	} */
+
+
+  //calculate written frames!
+  result = commitres * ao_data.bps;
+
+
+  /* if (verbose) { */
+  /* if (len == result) */
+  /* printf("result: %i, frames written: %i ...\n", result, frames_transmit); */
+  /* else */
+  /* printf("result: %i, frames written: %i, result != len ...\n", result, frames_transmit); */
+  /* } */
+
+  //mplayer doesn't like -result
+  if (result < 0)
+    result = 0;
+
+#ifdef USE_POLL
+  free(ufds);
+#endif
+
+  return result;
+}
+
 /* how many byes are free in the buffer */
 static int get_space()
 {
@@ -912,8 +1110,14 @@
     case SND_PCM_STATE_OPEN:
       str_status = "open";
     case SND_PCM_STATE_PREPARED:
-      if (str_status != "open")
+      if (str_status != "open") {
 	str_status = "prepared";
+	first = 1;
+	ret = snd_pcm_status_get_avail(status) * ao_data.bps;
+	if (ret == 0) //ugly workaround for hang in mmap-mode
+	  ret = 10;
+	break;
+      }
     case SND_PCM_STATE_RUNNING:
       ret = snd_pcm_status_get_avail(status) * ao_data.bps;
       //avail_frames = snd_pcm_avail_update(alsa_handler) * ao_data.bps;
@@ -928,11 +1132,15 @@
     case SND_PCM_STATE_XRUN:
       xrun("space");
       str_status = "xrun";
+      first = 1;
       ret = 0;
       break;
     default:
       str_status = "undefined";
-      ret = 0;
+      ret = snd_pcm_status_get_avail(status) * ao_data.bps;
+      if (ret <= 0) {
+	xrun("space");
+      }
     }
 
     if (verbose && str_status != "running")