#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 */
#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);
_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
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;
PaUtilAllocationGroup *allocations;
PaHostApiIndex hostApiIndex;
+ PaUint32 alsaLibVersion; /* Retrieved from the library at run-time */
}
PaAlsaHostApiRepresentation;
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 );
sizeof(PaAlsaHostApiRepresentation) ), paInsufficientMemory );
PA_UNLESS( alsaHostApi->allocations = PaUtil_CreateAllocationGroup(), paInsufficientMemory );
alsaHostApi->hostApiIndex = hostApiIndex;
+ alsaHostApi->alsaLibVersion = PaAlsaVersionNum();
*hostApi = (PaUtilHostApiRepresentation*)alsaHostApi;
(*hostApi)->info.structVersion = 1;
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
{
return result;
}
+
static PaError PaAlsaStreamComponent_Initialize( PaAlsaStreamComponent *self, PaAlsaHostApiRepresentation *alsaApi,
const PaStreamParameters *params, StreamDirection streamDir, int callbackMode )
{
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;
*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;