Alsa: add a workaround for stuttering/xruns when using default device on Alsa-lib older than 1.0.16 (caused by zero 'revents' when poll() returns).
This commit is contained in:
parent
1c9b4fe4b9
commit
00502bdef0
1 changed files with 45 additions and 0 deletions
|
|
@ -85,6 +85,9 @@
|
||||||
#define SND_PCM_TSTAMP_ENABLE SND_PCM_TSTAMP_MMAP
|
#define SND_PCM_TSTAMP_ENABLE SND_PCM_TSTAMP_MMAP
|
||||||
#endif
|
#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. */
|
/* Specifies that hardware audio sample needs byte-swapping into platfom native value representation. */
|
||||||
#define paSwapEndian ((PaSampleFormat) 0x40000000) /**< @see PaSampleFormat */
|
#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)
|
#define alsa_snd_pcm_status_alloca(ptr) __alsa_snd_alloca(ptr, snd_pcm_status)
|
||||||
|
|
||||||
_PA_DEFINE_FUNC(snd_card_next);
|
_PA_DEFINE_FUNC(snd_card_next);
|
||||||
|
_PA_DEFINE_FUNC(snd_asoundlib_version);
|
||||||
_PA_DEFINE_FUNC(snd_strerror);
|
_PA_DEFINE_FUNC(snd_strerror);
|
||||||
_PA_DEFINE_FUNC(snd_output_stdio_attach);
|
_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_pcm_status_get_delay);
|
||||||
|
|
||||||
_PA_LOAD_FUNC(snd_card_next);
|
_PA_LOAD_FUNC(snd_card_next);
|
||||||
|
_PA_LOAD_FUNC(snd_asoundlib_version);
|
||||||
_PA_LOAD_FUNC(snd_strerror);
|
_PA_LOAD_FUNC(snd_strerror);
|
||||||
_PA_LOAD_FUNC(snd_output_stdio_attach);
|
_PA_LOAD_FUNC(snd_output_stdio_attach);
|
||||||
#undef _PA_LOAD_FUNC
|
#undef _PA_LOAD_FUNC
|
||||||
|
|
@ -614,6 +619,8 @@ typedef struct
|
||||||
void *nonMmapBuffer;
|
void *nonMmapBuffer;
|
||||||
unsigned int nonMmapBufferSize;
|
unsigned int nonMmapBufferSize;
|
||||||
PaDeviceIndex device; /* Keep the device index */
|
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_t *pcm;
|
||||||
snd_pcm_uframes_t bufferSize;
|
snd_pcm_uframes_t bufferSize;
|
||||||
|
|
@ -673,6 +680,7 @@ typedef struct PaAlsaHostApiRepresentation
|
||||||
PaUtilAllocationGroup *allocations;
|
PaUtilAllocationGroup *allocations;
|
||||||
|
|
||||||
PaHostApiIndex hostApiIndex;
|
PaHostApiIndex hostApiIndex;
|
||||||
|
PaUint32 alsaLibVersion; /* Retrieved from the library at run-time */
|
||||||
}
|
}
|
||||||
PaAlsaHostApiRepresentation;
|
PaAlsaHostApiRepresentation;
|
||||||
|
|
||||||
|
|
@ -713,6 +721,7 @@ static double GetStreamCpuLoad( PaStream* stream );
|
||||||
static PaError BuildDeviceList( PaAlsaHostApiRepresentation *hostApi );
|
static PaError BuildDeviceList( PaAlsaHostApiRepresentation *hostApi );
|
||||||
static int SetApproximateSampleRate( snd_pcm_t *pcm, snd_pcm_hw_params_t *hwParams, double sampleRate );
|
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 int GetExactSampleRate( snd_pcm_hw_params_t *hwParams, double *sampleRate );
|
||||||
|
static PaUint32 PaAlsaVersionNum(void);
|
||||||
|
|
||||||
/* Callback prototypes */
|
/* Callback prototypes */
|
||||||
static void *CallbackThreadFunc( void *userData );
|
static void *CallbackThreadFunc( void *userData );
|
||||||
|
|
@ -752,6 +761,7 @@ PaError PaAlsa_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex
|
||||||
sizeof(PaAlsaHostApiRepresentation) ), paInsufficientMemory );
|
sizeof(PaAlsaHostApiRepresentation) ), paInsufficientMemory );
|
||||||
PA_UNLESS( alsaHostApi->allocations = PaUtil_CreateAllocationGroup(), paInsufficientMemory );
|
PA_UNLESS( alsaHostApi->allocations = PaUtil_CreateAllocationGroup(), paInsufficientMemory );
|
||||||
alsaHostApi->hostApiIndex = hostApiIndex;
|
alsaHostApi->hostApiIndex = hostApiIndex;
|
||||||
|
alsaHostApi->alsaLibVersion = PaAlsaVersionNum();
|
||||||
|
|
||||||
*hostApi = (PaUtilHostApiRepresentation*)alsaHostApi;
|
*hostApi = (PaUtilHostApiRepresentation*)alsaHostApi;
|
||||||
(*hostApi)->info.structVersion = 1;
|
(*hostApi)->info.structVersion = 1;
|
||||||
|
|
@ -961,6 +971,24 @@ static void InitializeDeviceInfo( PaDeviceInfo *deviceInfo )
|
||||||
deviceInfo->defaultSampleRate = -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 */
|
/* Helper struct */
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
|
|
@ -1861,6 +1889,7 @@ error:
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static PaError PaAlsaStreamComponent_Initialize( PaAlsaStreamComponent *self, PaAlsaHostApiRepresentation *alsaApi,
|
static PaError PaAlsaStreamComponent_Initialize( PaAlsaStreamComponent *self, PaAlsaHostApiRepresentation *alsaApi,
|
||||||
const PaStreamParameters *params, StreamDirection streamDir, int callbackMode )
|
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 );
|
const PaAlsaDeviceInfo *devInfo = GetDeviceInfo( &alsaApi->baseHostApiRep, params->device );
|
||||||
self->numHostChannels = PA_MAX( params->channelCount, StreamDirection_In == streamDir ? devInfo->minInputChannels
|
self->numHostChannels = PA_MAX( params->channelCount, StreamDirection_In == streamDir ? devInfo->minInputChannels
|
||||||
: devInfo->minOutputChannels );
|
: devInfo->minOutputChannels );
|
||||||
|
self->deviceIsPlug = devInfo->isPlug;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
/* We're blissfully unaware of the minimum channelCount */
|
/* We're blissfully unaware of the minimum channelCount */
|
||||||
self->numHostChannels = params->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;
|
self->device = params->device;
|
||||||
|
|
||||||
|
|
@ -3681,6 +3716,16 @@ static PaError PaAlsaStreamComponent_EndPolling( PaAlsaStreamComponent* self, st
|
||||||
|
|
||||||
*shouldPoll = 0;
|
*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:
|
error:
|
||||||
return result;
|
return result;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue