aboutsummaryrefslogtreecommitdiff
path: root/pd/portaudio/src/hostapi/alsa/pa_linux_alsa.c
diff options
context:
space:
mode:
Diffstat (limited to 'pd/portaudio/src/hostapi/alsa/pa_linux_alsa.c')
-rw-r--r--pd/portaudio/src/hostapi/alsa/pa_linux_alsa.c250
1 files changed, 190 insertions, 60 deletions
diff --git a/pd/portaudio/src/hostapi/alsa/pa_linux_alsa.c b/pd/portaudio/src/hostapi/alsa/pa_linux_alsa.c
index 06b17ac1..6b4a7b9f 100644
--- a/pd/portaudio/src/hostapi/alsa/pa_linux_alsa.c
+++ b/pd/portaudio/src/hostapi/alsa/pa_linux_alsa.c
@@ -1,11 +1,12 @@
/*
- * $Id: pa_linux_alsa.c 1236 2007-06-24 20:39:26Z aknudsen $
+ * $Id: pa_linux_alsa.c 1415 2009-06-03 18:57:56Z aknudsen $
* PortAudio Portable Real-Time Audio Library
* Latest Version at: http://www.portaudio.com
* ALSA implementation by Joshua Haberman and Arve Knudsen
*
* Copyright (c) 2002 Joshua Haberman <joshua@haberman.com>
- * Copyright (c) 2005-2006 Arve Knudsen <aknuds-1@broadpark.no>
+ * Copyright (c) 2005-2009 Arve Knudsen <arve.knudsen@gmail.com>
+ * Copyright (c) 2008 Kevin Kofler <kevin.kofler@chello.at>
*
* Based on the Open Source API proposed by Ross Bencina
* Copyright (c) 1999-2002 Ross Bencina, Phil Burk
@@ -62,6 +63,7 @@
#include <sys/mman.h>
#include <signal.h> /* For sig_atomic_t */
+#include "portaudio.h"
#include "pa_util.h"
#include "pa_unix_util.h"
#include "pa_allocation.h"
@@ -98,6 +100,7 @@
static int aErr_; /* Used with ENSURE_ */
static int numPeriods_ = 4;
+static int busyRetries_ = 100;
int PaAlsa_SetNumPeriods( int numPeriods )
{
@@ -117,6 +120,8 @@ typedef struct
unsigned long framesPerBuffer;
int numUserChannels, numHostChannels;
int userInterleaved, hostInterleaved;
+ int canMmap;
+ void *nonMmapBuffer;
PaDeviceIndex device; /* Keep the device index */
snd_pcm_t *pcm;
@@ -320,7 +325,7 @@ static void Terminate( struct PaUtilHostApiRepresentation *hostApi )
* and a suitable result returned. The device is closed before returning.
*/
static PaError GropeDevice( snd_pcm_t* pcm, int isPlug, StreamDirection mode, int openBlocking,
- PaAlsaDeviceInfo* devInfo, int* canMmap )
+ PaAlsaDeviceInfo* devInfo )
{
PaError result = paNoError;
snd_pcm_hw_params_t *hwParams;
@@ -353,9 +358,6 @@ static PaError GropeDevice( snd_pcm_t* pcm, int isPlug, StreamDirection mode, in
snd_pcm_hw_params_alloca( &hwParams );
snd_pcm_hw_params_any( pcm, hwParams );
- *canMmap = snd_pcm_hw_params_test_access( pcm, hwParams, SND_PCM_ACCESS_MMAP_INTERLEAVED ) >= 0 ||
- snd_pcm_hw_params_test_access( pcm, hwParams, SND_PCM_ACCESS_MMAP_NONINTERLEAVED ) >= 0;
-
if( defaultSr >= 0 )
{
/* Could be that the device opened in one mode supports samplerates that the other mode wont have,
@@ -538,7 +540,7 @@ static int IgnorePlugin( const char *pluginId )
**/
static int OpenPcm( snd_pcm_t **pcmp, const char *name, snd_pcm_stream_t stream, int mode, int waitOnBusy )
{
- int tries = 0, maxTries = waitOnBusy ? 100 : 0;
+ int tries = 0, maxTries = waitOnBusy ? busyRetries_ : 0;
int ret = snd_pcm_open( pcmp, name, stream, mode );
for( tries = 0; tries < maxTries && -EBUSY == ret; ++tries )
{
@@ -565,7 +567,6 @@ static PaError FillInDevInfo( PaAlsaHostApiRepresentation *alsaApi, HwDevInfo* d
PaError result = 0;
PaDeviceInfo *baseDeviceInfo = &devInfo->baseDeviceInfo;
snd_pcm_t *pcm;
- int canMmap = -1;
PaUtilHostApiRepresentation *baseApi = &alsaApi->baseHostApiRep;
/* Zero fields */
@@ -579,8 +580,7 @@ static PaError FillInDevInfo( PaAlsaHostApiRepresentation *alsaApi, HwDevInfo* d
OpenPcm( &pcm, deviceName->alsaName, SND_PCM_STREAM_CAPTURE, blocking, 0 )
>= 0 )
{
- if( GropeDevice( pcm, deviceName->isPlug, StreamDirection_In, blocking, devInfo,
- &canMmap ) != paNoError )
+ if( GropeDevice( pcm, deviceName->isPlug, StreamDirection_In, blocking, devInfo ) != paNoError )
{
/* Error */
PA_DEBUG(("%s: Failed groping %s for capture\n", __FUNCTION__, deviceName->alsaName));
@@ -593,8 +593,7 @@ static PaError FillInDevInfo( PaAlsaHostApiRepresentation *alsaApi, HwDevInfo* d
OpenPcm( &pcm, deviceName->alsaName, SND_PCM_STREAM_PLAYBACK, blocking, 0 )
>= 0 )
{
- if( GropeDevice( pcm, deviceName->isPlug, StreamDirection_Out, blocking, devInfo,
- &canMmap ) != paNoError )
+ if( GropeDevice( pcm, deviceName->isPlug, StreamDirection_Out, blocking, devInfo ) != paNoError )
{
/* Error */
PA_DEBUG(("%s: Failed groping %s for playback\n", __FUNCTION__, deviceName->alsaName));
@@ -602,12 +601,6 @@ static PaError FillInDevInfo( PaAlsaHostApiRepresentation *alsaApi, HwDevInfo* d
}
}
- if( 0 == canMmap )
- {
- PA_DEBUG(("%s: Device %s doesn't support mmap\n", __FUNCTION__, deviceName->alsaName));
- goto end;
- }
-
baseDeviceInfo->structVersion = 2;
baseDeviceInfo->hostApi = alsaApi->hostApiIndex;
baseDeviceInfo->name = deviceName->name;
@@ -620,8 +613,12 @@ static PaError FillInDevInfo( PaAlsaHostApiRepresentation *alsaApi, HwDevInfo* d
if( baseDeviceInfo->maxInputChannels > 0 || baseDeviceInfo->maxOutputChannels > 0 )
{
/* Make device default if there isn't already one or it is the ALSA "default" device */
- if( baseApi->info.defaultInputDevice == paNoDevice && baseDeviceInfo->maxInputChannels > 0 )
+ if( (baseApi->info.defaultInputDevice == paNoDevice || !strcmp(deviceName->alsaName,
+ "default" )) && baseDeviceInfo->maxInputChannels > 0 )
+ {
baseApi->info.defaultInputDevice = *devIdx;
+ PA_DEBUG(("Default input device: %s\n", deviceName->name));
+ }
if( (baseApi->info.defaultOutputDevice == paNoDevice || !strcmp(deviceName->alsaName,
"default" )) && baseDeviceInfo->maxOutputChannels > 0 )
{
@@ -1192,6 +1189,8 @@ static PaError PaAlsaStreamComponent_Initialize( PaAlsaStreamComponent *self, Pa
self->hostInterleaved = self->userInterleaved = !(userSampleFormat & paNonInterleaved);
self->numUserChannels = params->channelCount;
self->streamDir = streamDir;
+ self->canMmap = 0;
+ self->nonMmapBuffer = NULL;
if( !callbackMode && !self->userInterleaved )
{
@@ -1234,6 +1233,7 @@ static PaError PaAlsaStreamComponent_InitialConfigure( PaAlsaStreamComponent *se
PaError result = paNoError;
snd_pcm_access_t accessMode, alternateAccessMode;
+ snd_pcm_access_t rwAccessMode, alternateRwAccessMode;
int dir = 0;
snd_pcm_t *pcm = self->pcm;
double sr = *sampleRate;
@@ -1253,32 +1253,40 @@ static PaError PaAlsaStreamComponent_InitialConfigure( PaAlsaStreamComponent *se
if( self->userInterleaved )
{
accessMode = SND_PCM_ACCESS_MMAP_INTERLEAVED;
+ rwAccessMode = SND_PCM_ACCESS_RW_INTERLEAVED;
alternateAccessMode = SND_PCM_ACCESS_MMAP_NONINTERLEAVED;
+ alternateRwAccessMode = SND_PCM_ACCESS_RW_NONINTERLEAVED;
}
else
{
accessMode = SND_PCM_ACCESS_MMAP_NONINTERLEAVED;
+ rwAccessMode = SND_PCM_ACCESS_RW_NONINTERLEAVED;
alternateAccessMode = SND_PCM_ACCESS_MMAP_INTERLEAVED;
+ alternateRwAccessMode = SND_PCM_ACCESS_RW_INTERLEAVED;
}
/* If requested access mode fails, try alternate mode */
+ self->canMmap = 1;
if( snd_pcm_hw_params_set_access( pcm, hwParams, accessMode ) < 0 )
{
- int err = 0;
- if( (err = snd_pcm_hw_params_set_access( pcm, hwParams, alternateAccessMode )) < 0)
+ if( snd_pcm_hw_params_set_access( pcm, hwParams, rwAccessMode ) >= 0 )
+ self->canMmap = 0;
+ else
{
- result = paUnanticipatedHostError;
- if( -EINVAL == err )
- {
- PaUtil_SetLastHostErrorInfo( paALSA, err, "PA ALSA requires that a device supports mmap access" );
- }
- else
+ if( snd_pcm_hw_params_set_access( pcm, hwParams, alternateAccessMode ) < 0 )
{
- PaUtil_SetLastHostErrorInfo( paALSA, err, snd_strerror( err ) );
+ int err = 0;
+ if( (err = snd_pcm_hw_params_set_access( pcm, hwParams, alternateRwAccessMode )) >= 0)
+ self->canMmap = 0;
+ else
+ {
+ result = paUnanticipatedHostError;
+ PaUtil_SetLastHostErrorInfo( paALSA, err, snd_strerror( err ) );
+ goto error;
+ }
}
- goto error;
+ /* Flip mode */
+ self->hostInterleaved = !self->userInterleaved;
}
- /* Flip mode */
- self->hostInterleaved = !self->userInterleaved;
}
ENSURE_( snd_pcm_hw_params_set_format( pcm, hwParams, self->nativeFormat ), paUnanticipatedHostError );
@@ -1356,7 +1364,10 @@ static PaError PaAlsaStreamComponent_FinishConfigure( PaAlsaStreamComponent *sel
ENSURE_( snd_pcm_sw_params_set_avail_min( self->pcm, swParams, self->framesPerBuffer ), paUnanticipatedHostError );
ENSURE_( snd_pcm_sw_params_set_xfer_align( self->pcm, swParams, 1 ), paUnanticipatedHostError );
- ENSURE_( snd_pcm_sw_params_set_tstamp_mode( self->pcm, swParams, SND_PCM_TSTAMP_MMAP ), paUnanticipatedHostError );
+#ifndef SND_PCM_TSTAMP_ENABLE /* old versions of ALSA called this something different */
+#define SND_PCM_TSTAMP_ENABLE SND_PCM_TSTAMP_MMAP
+#endif
+ ENSURE_( snd_pcm_sw_params_set_tstamp_mode( self->pcm, swParams, SND_PCM_TSTAMP_ENABLE ), paUnanticipatedHostError );
/* Set the parameters! */
ENSURE_( snd_pcm_sw_params( self->pcm, swParams ), paUnanticipatedHostError );
@@ -1584,6 +1595,10 @@ static PaError PaAlsaStreamComponent_DetermineFramesPerBuffer( PaAlsaStreamCompo
}
}
+ /* non-mmap mode needs a reasonably-sized buffer or it'll stutter */
+ if( !self->canMmap && framesPerHostBuffer < 2048 )
+ framesPerHostBuffer = 2048;
+
assert( framesPerHostBuffer > 0 );
{
snd_pcm_uframes_t min = 0, max = 0;
@@ -1725,12 +1740,15 @@ static PaError PaAlsaStream_DetermineFramesPerBuffer( PaAlsaStream* self, double
while( optimalPeriodSize >= periodSize )
{
- if( snd_pcm_hw_params_test_period_size( self->capture.pcm, hwParamsCapture, optimalPeriodSize, 0 ) < 0 )
- continue;
- if( snd_pcm_hw_params_test_period_size( self->playback.pcm, hwParamsPlayback, optimalPeriodSize, 0 ) >= 0 )
+ if( snd_pcm_hw_params_test_period_size( self->capture.pcm, hwParamsCapture, optimalPeriodSize, 0 )
+ >= 0 && snd_pcm_hw_params_test_period_size( self->playback.pcm, hwParamsPlayback,
+ optimalPeriodSize, 0 ) >= 0 )
+ {
break;
+ }
optimalPeriodSize /= 2;
}
+
if( optimalPeriodSize > periodSize )
periodSize = optimalPeriodSize;
@@ -1823,12 +1841,13 @@ static PaError PaAlsaStream_DetermineFramesPerBuffer( PaAlsaStream* self, double
PA_UNLESS( framesPerHostBuffer != 0, paInternalError );
self->maxFramesPerHostBuffer = framesPerHostBuffer;
- if( !accurate )
+ if( !self->playback.canMmap || !accurate )
{
/* Don't know the exact size per host buffer */
*hostBufferSizeMode = paUtilBoundedHostBufferSize;
/* Raise upper bound */
- ++self->maxFramesPerHostBuffer;
+ if( !accurate )
+ ++self->maxFramesPerHostBuffer;
}
error:
@@ -1987,11 +2006,11 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
/* Ok, buffer processor is initialized, now we can deduce it's latency */
if( numInputChannels > 0 )
- stream->streamRepresentation.streamInfo.inputLatency = inputLatency + PaUtil_GetBufferProcessorInputLatency(
- &stream->bufferProcessor );
+ stream->streamRepresentation.streamInfo.inputLatency = inputLatency + (PaTime)(
+ PaUtil_GetBufferProcessorInputLatency( &stream->bufferProcessor ) / sampleRate);
if( numOutputChannels > 0 )
- stream->streamRepresentation.streamInfo.outputLatency = outputLatency + PaUtil_GetBufferProcessorOutputLatency(
- &stream->bufferProcessor );
+ stream->streamRepresentation.streamInfo.outputLatency = outputLatency + (PaTime)(
+ PaUtil_GetBufferProcessorOutputLatency( &stream->bufferProcessor ) / sampleRate);
*s = (PaStream*)stream;
@@ -2051,9 +2070,11 @@ static PaError AlsaStart( PaAlsaStream *stream, int priming )
{
/* Buffer isn't primed, so prepare and silence */
ENSURE_( snd_pcm_prepare( stream->playback.pcm ), paUnanticipatedHostError );
- SilenceBuffer( stream );
+ if( stream->playback.canMmap )
+ SilenceBuffer( stream );
}
- ENSURE_( snd_pcm_start( stream->playback.pcm ), paUnanticipatedHostError );
+ if( stream->playback.canMmap )
+ ENSURE_( snd_pcm_start( stream->playback.pcm ), paUnanticipatedHostError );
}
else
ENSURE_( snd_pcm_prepare( stream->playback.pcm ), paUnanticipatedHostError );
@@ -2151,7 +2172,9 @@ error:
static PaError AlsaStop( PaAlsaStream *stream, int abort )
{
PaError result = paNoError;
- /* XXX: Seems that draining the dmix device may trigger a race condition in ALSA */
+ /* XXX: snd_pcm_drain tends to lock up, avoid it until we find out more */
+ abort = 1;
+ /*
if( stream->capture.pcm && !strcmp( Pa_GetDeviceInfo( stream->capture.device )->name,
"dmix" ) )
{
@@ -2162,6 +2185,7 @@ static PaError AlsaStop( PaAlsaStream *stream, int abort )
{
abort = 1;
}
+ */
if( abort )
{
@@ -2379,6 +2403,7 @@ static PaError PaAlsaStream_HandleXrun( PaAlsaStream *self )
snd_pcm_status_t *st;
PaTime now = PaUtil_GetTime();
snd_timestamp_t t;
+ int errplayback = 0, errcapture = 0;
snd_pcm_status_alloca( &st );
@@ -2389,6 +2414,7 @@ static PaError PaAlsaStream_HandleXrun( PaAlsaStream *self )
{
snd_pcm_status_get_trigger_tstamp( st, &t );
self->underrun = now * 1000 - ((PaTime) t.tv_sec * 1000 + (PaTime) t.tv_usec / 1000);
+ errplayback = snd_pcm_recover( self->playback.pcm, -EPIPE, 0 );
}
}
if( self->capture.pcm )
@@ -2398,10 +2424,12 @@ static PaError PaAlsaStream_HandleXrun( PaAlsaStream *self )
{
snd_pcm_status_get_trigger_tstamp( st, &t );
self->overrun = now * 1000 - ((PaTime) t.tv_sec * 1000 + (PaTime) t.tv_usec / 1000);
+ errcapture = snd_pcm_recover( self->capture.pcm, -EPIPE, 0 );
}
}
- PA_ENSURE( AlsaRestart( self ) );
+ if( errplayback || errcapture )
+ PA_ENSURE( AlsaRestart( self ) );
end:
return result;
@@ -2552,7 +2580,7 @@ static void CalculateTimeInfo( PaAlsaStream *stream, PaStreamCallbackTimeInfo *t
static PaError PaAlsaStreamComponent_EndProcessing( PaAlsaStreamComponent *self, unsigned long numFrames, int *xrun )
{
PaError result = paNoError;
- int res;
+ int res = 0;
/* @concern FullDuplex It is possible that only one direction is marked ready after polling, and processed
* afterwards
@@ -2560,7 +2588,34 @@ static PaError PaAlsaStreamComponent_EndProcessing( PaAlsaStreamComponent *self,
if( !self->ready )
goto end;
- res = snd_pcm_mmap_commit( self->pcm, self->offset, numFrames );
+ if( !self->canMmap && StreamDirection_Out == self->streamDir )
+ {
+ /* Play sound */
+ if( self->hostInterleaved )
+ res = snd_pcm_writei( self->pcm, self->nonMmapBuffer, numFrames );
+ else
+ {
+ void *bufs[self->numHostChannels];
+ int bufsize = snd_pcm_format_size( self->nativeFormat, self->framesPerBuffer + 1 );
+ unsigned char *buffer = self->nonMmapBuffer;
+ int i;
+ for( i = 0; i < self->numHostChannels; ++i )
+ {
+ bufs[i] = buffer;
+ buffer += bufsize;
+ }
+ res = snd_pcm_writen( self->pcm, bufs, numFrames );
+ }
+ }
+
+ if( self->canMmap )
+ res = snd_pcm_mmap_commit( self->pcm, self->offset, numFrames );
+ else
+ {
+ free( self->nonMmapBuffer );
+ self->nonMmapBuffer = NULL;
+ }
+
if( res == -EPIPE || res == -ESTRPIPE )
{
*xrun = 1;
@@ -2600,7 +2655,7 @@ static PaError PaAlsaStreamComponent_DoChannelAdaption( PaAlsaStreamComponent *s
if( self->hostInterleaved )
{
int swidth = snd_pcm_format_size( self->nativeFormat, 1 );
- unsigned char *buffer = ExtractAddress( self->channelAreas, self->offset );
+ unsigned char *buffer = self->canMmap ? ExtractAddress( self->channelAreas, self->offset ) : self->nonMmapBuffer;
/* Start after the last user channel */
p = buffer + self->numUserChannels * swidth;
@@ -2980,13 +3035,23 @@ static PaError PaAlsaStreamComponent_RegisterChannels( PaAlsaStreamComponent* se
goto end;
}
- ENSURE_( snd_pcm_mmap_begin( self->pcm, &areas, &self->offset, numFrames ), paUnanticipatedHostError );
+ if( self->canMmap )
+ {
+ ENSURE_( snd_pcm_mmap_begin( self->pcm, &areas, &self->offset, numFrames ), paUnanticipatedHostError );
+ /* @concern ChannelAdaption Buffer address is recorded so we can do some channel adaption later */
+ self->channelAreas = (snd_pcm_channel_area_t *)areas;
+ }
+ else
+ {
+ free( self->nonMmapBuffer );
+ self->nonMmapBuffer = calloc( self->numHostChannels, snd_pcm_format_size( self->nativeFormat, self->framesPerBuffer + 1 ) );
+ }
if( self->hostInterleaved )
{
int swidth = snd_pcm_format_size( self->nativeFormat, 1 );
- p = buffer = ExtractAddress( areas, self->offset );
+ p = buffer = self->canMmap ? ExtractAddress( areas, self->offset ) : self->nonMmapBuffer;
for( i = 0; i < self->numUserChannels; ++i )
{
/* We're setting the channels up to userChannels, but the stride will be hostChannels samples */
@@ -2996,16 +3061,52 @@ static PaError PaAlsaStreamComponent_RegisterChannels( PaAlsaStreamComponent* se
}
else
{
- for( i = 0; i < self->numUserChannels; ++i )
+ if( self->canMmap )
+ for( i = 0; i < self->numUserChannels; ++i )
+ {
+ area = areas + i;
+ buffer = ExtractAddress( area, self->offset );
+ setChannel( bp, i, buffer, 1 );
+ }
+ else
{
- area = areas + i;
- buffer = ExtractAddress( area, self->offset );
- setChannel( bp, i, buffer, 1 );
+ int bufsize = snd_pcm_format_size( self->nativeFormat, self->framesPerBuffer + 1 );
+ buffer = self->nonMmapBuffer;
+ for( i = 0; i < self->numUserChannels; ++i )
+ {
+ setChannel( bp, i, buffer, 1 );
+ buffer += bufsize;
+ }
}
}
- /* @concern ChannelAdaption Buffer address is recorded so we can do some channel adaption later */
- self->channelAreas = (snd_pcm_channel_area_t *)areas;
+ if( !self->canMmap && StreamDirection_In == self->streamDir )
+ {
+ /* Read sound */
+ int res;
+ if( self->hostInterleaved )
+ res = snd_pcm_readi( self->pcm, self->nonMmapBuffer, *numFrames );
+ else
+ {
+ void *bufs[self->numHostChannels];
+ int bufsize = snd_pcm_format_size( self->nativeFormat, self->framesPerBuffer + 1 );
+ unsigned char *buffer = self->nonMmapBuffer;
+ int i;
+ for( i = 0; i < self->numHostChannels; ++i )
+ {
+ bufs[i] = buffer;
+ buffer += bufsize;
+ }
+ res = snd_pcm_readn( self->pcm, bufs, *numFrames );
+ }
+ if( res == -EPIPE || res == -ESTRPIPE )
+ {
+ *xrun = 1;
+ *numFrames = 0;
+ free( self->nonMmapBuffer );
+ self->nonMmapBuffer = NULL;
+ }
+ }
end:
error:
@@ -3519,10 +3620,31 @@ void PaAlsa_EnableWatchdog( PaStream *s, int enable )
}
#endif
+static PaError GetAlsaStreamPointer( PaStream* s, PaAlsaStream** stream )
+{
+ PaError result = paNoError;
+ PaUtilHostApiRepresentation* hostApi;
+ PaAlsaHostApiRepresentation* alsaHostApi;
+
+ PA_ENSURE( PaUtil_ValidateStreamPointer( s ) );
+ PA_ENSURE( PaUtil_GetHostApiRepresentation( &hostApi, paALSA ) );
+ alsaHostApi = (PaAlsaHostApiRepresentation*)hostApi;
+
+ PA_UNLESS( PA_STREAM_REP( s )->streamInterface == &alsaHostApi->callbackStreamInterface
+ || PA_STREAM_REP( s )->streamInterface == &alsaHostApi->blockingStreamInterface,
+ paIncompatibleStreamHostApi );
+
+ *stream = (PaAlsaStream*)s;
+error:
+ return paNoError;
+}
+
PaError PaAlsa_GetStreamInputCard(PaStream* s, int* card) {
- PaAlsaStream *stream = (PaAlsaStream *) s;
- snd_pcm_info_t* pcmInfo;
+ PaAlsaStream *stream;
PaError result = paNoError;
+ snd_pcm_info_t* pcmInfo;
+
+ PA_ENSURE( GetAlsaStreamPointer( s, &stream ) );
/* XXX: More descriptive error? */
PA_UNLESS( stream->capture.pcm, paDeviceUnavailable );
@@ -3536,9 +3658,11 @@ error:
}
PaError PaAlsa_GetStreamOutputCard(PaStream* s, int* card) {
- PaAlsaStream *stream = (PaAlsaStream *) s;
- snd_pcm_info_t* pcmInfo;
+ PaAlsaStream *stream;
PaError result = paNoError;
+ snd_pcm_info_t* pcmInfo;
+
+ PA_ENSURE( GetAlsaStreamPointer( s, &stream ) );
/* XXX: More descriptive error? */
PA_UNLESS( stream->playback.pcm, paDeviceUnavailable );
@@ -3550,3 +3674,9 @@ PaError PaAlsa_GetStreamOutputCard(PaStream* s, int* card) {
error:
return result;
}
+
+PaError PaAlsa_SetRetriesBusy( int retries )
+{
+ busyRetries_ = retries;
+ return paNoError;
+}