[xiph-commits] r18775 - trunk/ao/src/plugins/alsa

xiphmont at svn.xiph.org xiphmont at svn.xiph.org
Mon Jan 21 12:26:08 PST 2013


Author: xiphmont
Date: 2013-01-21 12:26:08 -0800 (Mon, 21 Jan 2013)
New Revision: 18775

Modified:
   trunk/ao/src/plugins/alsa/ao_alsa.c
Log:
Pulse Audio has a major problem with any drain() call taking a minimum
of 2s whether there's audio to drain or not.  Add a workaround to the
ALSA plugin to eliminate this behavior when used with Pulse's ALSA
emulation.

(Rather than draining, we query the stream depth, wait, then spike the
stream explicitly)



Modified: trunk/ao/src/plugins/alsa/ao_alsa.c
===================================================================
--- trunk/ao/src/plugins/alsa/ao_alsa.c	2013-01-21 10:19:37 UTC (rev 18774)
+++ trunk/ao/src/plugins/alsa/ao_alsa.c	2013-01-21 20:26:08 UTC (rev 18775)
@@ -92,17 +92,19 @@
 
 typedef struct ao_alsa_internal
 {
-	snd_pcm_t *pcm_handle;
-	unsigned int buffer_time;
-	unsigned int period_time;
-	snd_pcm_uframes_t period_size;
-	int sample_size;
-	snd_pcm_format_t bitformat;
+        snd_pcm_t *pcm_handle;
+        unsigned int buffer_time;
+        unsigned int period_time;
+        snd_pcm_uframes_t period_size;
+        int sample_size;
+        unsigned int sample_rate;
+        snd_pcm_format_t bitformat;
         char *pad_24_to_32;
-	char *dev;
+        char *dev;
         int id;
-	ao_alsa_writei_t * writei;
-	snd_pcm_access_t access_mask;
+        ao_alsa_writei_t * writei;
+        snd_pcm_access_t access_mask;
+        int static_delay;
 } ao_alsa_internal;
 
 
@@ -222,7 +224,7 @@
 	ao_alsa_internal *internal  = (ao_alsa_internal *) device->internal;
 	snd_pcm_hw_params_t   *params;
 	int err;
-	unsigned int rate = format->rate;
+	unsigned int rate = internal->sample_rate = format->rate;
 
 	/* allocate the hardware parameter structure */
 	snd_pcm_hw_params_alloca(&params);
@@ -276,8 +278,8 @@
 
 	/* calculate a period time of one half sample time */
 	if ((internal->period_time == 0) && (rate > 0))
-		internal->period_time =
-			1000000 * AO_ALSA_SAMPLE_XFER / rate;
+          internal->period_time =
+            1000000 * AO_ALSA_SAMPLE_XFER / rate;
 
 	/* set the time per hardware sample transfer */
 	err = snd_pcm_hw_params_set_period_time_near(internal->pcm_handle,
@@ -333,7 +335,7 @@
 
 	/* allow transfers to start when there is one period */
 	err = snd_pcm_sw_params_set_start_threshold(internal->pcm_handle,
-			params, internal->period_size);
+                                                    params, internal->period_size);
 	if (err < 0){
           adebug("snd_pcm_sw_params_set_start_threshold() failed.\n");
           return err;
@@ -341,7 +343,7 @@
 
 	/* require a minimum of one full transfer in the buffer */
 	err = snd_pcm_sw_params_set_avail_min(internal->pcm_handle, params,
-			internal->period_size);
+                                              internal->period_size);
 	if (err < 0){
           adebug("snd_pcm_sw_params_set_avail_min() failed.\n");
           return err;
@@ -524,6 +526,14 @@
 	}
 
         adebug("Using ALSA device '%s'\n",internal->dev);
+        {
+          snd_pcm_sframes_t sframes;
+          if(snd_pcm_delay (internal->pcm_handle, &sframes)){
+            internal->static_delay=0;
+          }else{
+            internal->static_delay=sframes;
+          }
+        }
 
 	/* alsa's endinness will be the same as the application's */
 	if (format->bits > 8)
@@ -657,7 +667,31 @@
 	if (device) {
           if ((internal = (ao_alsa_internal *) device->internal)) {
             if (internal->pcm_handle) {
-              snd_pcm_drain(internal->pcm_handle);
+
+              /* this is a PulseAudio ALSA emulation bug workaround;
+                 snd_pcm_drain always takes about 2 seconds, even if
+                 there's nothing to drain.  Rather than wait for no
+                 reason, determine the current playback depth, wait
+                 that long, then kill the stream.  Remove this code
+                 once Pulse gets fixed. */
+
+              snd_pcm_sframes_t sframes;
+              if(snd_pcm_delay (internal->pcm_handle, &sframes)){
+                snd_pcm_drain(internal->pcm_handle);
+              }else{
+                double s = (double)(sframes - internal->static_delay)/internal->sample_rate;
+                if(s>0){
+                  struct timespec sleep,wake;
+                  sleep.tv_sec = (int)s;
+                  sleep.tv_nsec = (s-sleep.tv_sec)*1000000000;
+                  while(nanosleep(&sleep,&wake)<0){
+                    if(errno==EINTR)
+                      sleep=wake;
+                    else
+                      break;
+                  }
+                }
+              }
               snd_pcm_close(internal->pcm_handle);
               internal->pcm_handle=NULL;
             }



More information about the commits mailing list