]> Repos - portaudio/commitdiff
Alsa: add a workaround for stuttering/xruns when using default device on Alsa-lib...
authorgineera <gineera@0f58301d-fd10-0410-b4af-bbb618454e57>
Thu, 1 Mar 2012 21:20:07 +0000 (21:20 +0000)
committergineera <gineera@0f58301d-fd10-0410-b4af-bbb618454e57>
Thu, 1 Mar 2012 21:20:07 +0000 (21:20 +0000)
src/hostapi/alsa/pa_linux_alsa.c

index 76a778588d35a925218e1c86a5d79b486af32586..84ce71894736a1b67223d06a21600ecc16f7af7c 100644 (file)
@@ -85,6 +85,9 @@
     #define SND_PCM_TSTAMP_ENABLE SND_PCM_TSTAMP_MMAP
 #endif
 
+/* Combine version elements into a single (unsigned) integer */
+#define ALSA_VERSION_INT(major, minor, subminor)  ((major << 16) | (minor << 8) | subminor)
+
 /* Specifies that hardware audio sample needs byte-swapping into platfom native value representation. */
 #define paSwapEndian ((PaSampleFormat) 0x40000000) /**< @see PaSampleFormat */
 
@@ -230,6 +233,7 @@ _PA_DEFINE_FUNC(snd_pcm_status_get_delay);
 #define alsa_snd_pcm_status_alloca(ptr) __alsa_snd_alloca(ptr, snd_pcm_status)
 
 _PA_DEFINE_FUNC(snd_card_next);
+_PA_DEFINE_FUNC(snd_asoundlib_version);
 _PA_DEFINE_FUNC(snd_strerror);
 _PA_DEFINE_FUNC(snd_output_stdio_attach);
 
@@ -508,6 +512,7 @@ _PA_LOAD_FUNC(snd_pcm_status_get_trigger_tstamp);
 _PA_LOAD_FUNC(snd_pcm_status_get_delay);
 
 _PA_LOAD_FUNC(snd_card_next);
+_PA_LOAD_FUNC(snd_asoundlib_version);
 _PA_LOAD_FUNC(snd_strerror);
 _PA_LOAD_FUNC(snd_output_stdio_attach);
 #undef _PA_LOAD_FUNC
@@ -614,6 +619,8 @@ typedef struct
     void *nonMmapBuffer;
     unsigned int nonMmapBufferSize;
     PaDeviceIndex device;     /* Keep the device index */
+    int deviceIsPlug; /* Distinguish plug types from direct 'hw:' devices */
+    int useReventFix; /* Alsa older than 1.0.16, plug devices need a fix */
 
     snd_pcm_t *pcm;
     snd_pcm_uframes_t bufferSize;
@@ -673,6 +680,7 @@ typedef struct PaAlsaHostApiRepresentation
     PaUtilAllocationGroup *allocations;
 
     PaHostApiIndex hostApiIndex;
+    PaUint32 alsaLibVersion; /* Retrieved from the library at run-time */
 }
 PaAlsaHostApiRepresentation;
 
@@ -713,6 +721,7 @@ static double GetStreamCpuLoad( PaStream* stream );
 static PaError BuildDeviceList( PaAlsaHostApiRepresentation *hostApi );
 static int SetApproximateSampleRate( snd_pcm_t *pcm, snd_pcm_hw_params_t *hwParams, double sampleRate );
 static int GetExactSampleRate( snd_pcm_hw_params_t *hwParams, double *sampleRate );
+static PaUint32 PaAlsaVersionNum(void);
 
 /* Callback prototypes */
 static void *CallbackThreadFunc( void *userData );
@@ -752,6 +761,7 @@ PaError PaAlsa_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex
                 sizeof(PaAlsaHostApiRepresentation) ), paInsufficientMemory );
     PA_UNLESS( alsaHostApi->allocations = PaUtil_CreateAllocationGroup(), paInsufficientMemory );
     alsaHostApi->hostApiIndex = hostApiIndex;
+    alsaHostApi->alsaLibVersion = PaAlsaVersionNum();
 
     *hostApi = (PaUtilHostApiRepresentation*)alsaHostApi;
     (*hostApi)->info.structVersion = 1;
@@ -961,6 +971,24 @@ static void InitializeDeviceInfo( PaDeviceInfo *deviceInfo )
     deviceInfo->defaultSampleRate = -1.;
 }
 
+
+/* Retrieve the version of the runtime Alsa-lib, as a single number equivalent to
+ * SND_LIB_VERSION.  Only a version string is available ("a.b.c") so this has to be converted.
+ * Assume 'a' and 'b' are single digits only.
+ */
+static PaUint32 PaAlsaVersionNum(void)
+{
+    char* verStr;
+    PaUint32 verNum;
+
+    verStr = (char*) alsa_snd_asoundlib_version();
+    verNum = ALSA_VERSION_INT( atoi(verStr), atoi(verStr + 2), atoi(verStr + 4) );
+    PA_DEBUG(( "ALSA version (build): " SND_LIB_VERSION_STR "\nALSA version (runtime): %s\n", verStr ));
+
+    return verNum;
+}
+
+
 /* Helper struct */
 typedef struct
 {
@@ -1861,6 +1889,7 @@ error:
     return result;
 }
 
+
 static PaError PaAlsaStreamComponent_Initialize( PaAlsaStreamComponent *self, PaAlsaHostApiRepresentation *alsaApi,
         const PaStreamParameters *params, StreamDirection streamDir, int callbackMode )
 {
@@ -1877,12 +1906,18 @@ static PaError PaAlsaStreamComponent_Initialize( PaAlsaStreamComponent *self, Pa
         const PaAlsaDeviceInfo *devInfo = GetDeviceInfo( &alsaApi->baseHostApiRep, params->device );
         self->numHostChannels = PA_MAX( params->channelCount, StreamDirection_In == streamDir ? devInfo->minInputChannels
                 : devInfo->minOutputChannels );
+        self->deviceIsPlug = devInfo->isPlug;
     }
     else
     {
         /* We're blissfully unaware of the minimum channelCount */
         self->numHostChannels = params->channelCount;
+        /* Check if device name does not start with hw: to determine if it is a 'plug' device */
+        if( strncmp( "hw:", ((PaAlsaStreamInfo *)params->hostApiSpecificStreamInfo)->deviceString, 3 ) != 0  )
+            self->deviceIsPlug = 1; /* An Alsa plug device, not a direct hw device */
     }
+    if( self->deviceIsPlug && alsaApi->alsaLibVersion < ALSA_VERSION_INT( 1, 0, 16 ) )
+        self->useReventFix = 1; /* Prior to Alsa1.0.16, plug devices may stutter without this fix */
 
     self->device = params->device;
 
@@ -3681,6 +3716,16 @@ static PaError PaAlsaStreamComponent_EndPolling( PaAlsaStreamComponent* self, st
 
         *shouldPoll = 0;
     }
+    else /* (A zero revent occurred) */
+    /* Work around an issue with Alsa older than 1.0.16 using some plugins (eg default with plug + dmix) where
+     * POLLIN or POLLOUT are zeroed by Alsa-lib if _mmap_avail() is a few frames short of avail_min at period
+     * boundary, possibly due to erratic dma interrupts at period boundary?  Treat as a valid event.
+     */
+    if( self->useReventFix )
+    {
+        self->ready = 1;
+        *shouldPoll = 0;
+    }
 
 error:
     return result;