📄 pa_linux_alsa.c
字号:
self->hostInterleaved = !self->userInterleaved;
}
ENSURE_( snd_pcm_hw_params_set_format( pcm, hwParams, self->nativeFormat ), paUnanticipatedHostError );
ENSURE_( SetApproximateSampleRate( pcm, hwParams, sr ), paInvalidSampleRate );
ENSURE_( GetExactSampleRate( hwParams, &sr ), paUnanticipatedHostError );
/* reject if there's no sample rate within 1% of the one requested */
if( (fabs( *sampleRate - sr ) / *sampleRate) > 0.01 )
{
PA_DEBUG(("%s: Wanted %f, closest sample rate was %d\n", __FUNCTION__, sampleRate, sr ));
PA_ENSURE( paInvalidSampleRate );
}
ENSURE_( snd_pcm_hw_params_set_channels( pcm, hwParams, self->numHostChannels ), paInvalidChannelCount );
/* I think there should be at least 2 periods (even though ALSA doesn't appear to enforce this) */
dir = 0;
ENSURE_( snd_pcm_hw_params_set_periods_min( pcm, hwParams, &minPeriods, &dir ), paUnanticipatedHostError );
dir = 0;
ENSURE_( snd_pcm_hw_params_set_period_size_near( pcm, hwParams, &self->framesPerBuffer, &dir ), paUnanticipatedHostError );
/* Find an acceptable number of periods */
numPeriods = (latency * sr) / self->framesPerBuffer + 1;
dir = 0;
ENSURE_( snd_pcm_hw_params_set_periods_near( pcm, hwParams, &numPeriods, &dir ), paUnanticipatedHostError );
/* Minimum of periods should already be 2 */
PA_UNLESS( numPeriods >= 2, paInternalError );
/* Set the parameters! */
ENSURE_( snd_pcm_hw_params( pcm, hwParams ), paUnanticipatedHostError );
ENSURE_( snd_pcm_hw_params_get_buffer_size( hwParams, &self->bufferSize ), paUnanticipatedHostError );
/* Latency in seconds, one period is not counted as latency */
latency = (numPeriods - 1) * self->framesPerBuffer / sr;
/* Now software parameters... */
ENSURE_( snd_pcm_sw_params_current( pcm, swParams ), paUnanticipatedHostError );
ENSURE_( snd_pcm_sw_params_set_start_threshold( pcm, swParams, self->framesPerBuffer ), paUnanticipatedHostError );
ENSURE_( snd_pcm_sw_params_set_stop_threshold( pcm, swParams, self->bufferSize ), paUnanticipatedHostError );
/* Silence buffer in the case of underrun */
if( !primeBuffers ) /* XXX: Make sense? */
{
snd_pcm_uframes_t boundary;
ENSURE_( snd_pcm_sw_params_get_boundary( swParams, &boundary ), paUnanticipatedHostError );
ENSURE_( snd_pcm_sw_params_set_silence_threshold( pcm, swParams, 0 ), paUnanticipatedHostError );
ENSURE_( snd_pcm_sw_params_set_silence_size( pcm, swParams, boundary ), paUnanticipatedHostError );
}
ENSURE_( snd_pcm_sw_params_set_avail_min( pcm, swParams, self->framesPerBuffer ), paUnanticipatedHostError );
ENSURE_( snd_pcm_sw_params_set_xfer_align( pcm, swParams, 1 ), paUnanticipatedHostError );
ENSURE_( snd_pcm_sw_params_set_tstamp_mode( pcm, swParams, SND_PCM_TSTAMP_MMAP ), paUnanticipatedHostError );
/* Set the parameters! */
ENSURE_( snd_pcm_sw_params( pcm, swParams ), paUnanticipatedHostError );
*sampleRate = sr;
*returnedLatency = latency;
end:
return result;
error:
goto end; /* No particular action */
}
static PaError PaAlsaStream_Initialize( PaAlsaStream *self, PaAlsaHostApiRepresentation *alsaApi, const PaStreamParameters *inParams,
const PaStreamParameters *outParams, double sampleRate, unsigned long framesPerUserBuffer, PaStreamCallback callback,
PaStreamFlags streamFlags, void *userData )
{
PaError result = paNoError;
assert( self );
memset( self, 0, sizeof (PaAlsaStream) );
if( NULL != callback )
{
PaUtil_InitializeStreamRepresentation( &self->streamRepresentation,
&alsaApi->callbackStreamInterface,
callback, userData );
self->callbackMode = 1;
}
else
{
PaUtil_InitializeStreamRepresentation( &self->streamRepresentation,
&alsaApi->blockingStreamInterface,
NULL, userData );
}
self->framesPerUserBuffer = framesPerUserBuffer;
self->neverDropInput = streamFlags & paNeverDropInput;
/* XXX: Ignore paPrimeOutputBuffersUsingStreamCallback untill buffer priming is fully supported in pa_process.c */
/*
if( outParams & streamFlags & paPrimeOutputBuffersUsingStreamCallback )
self->primeBuffers = 1;
*/
memset( &self->capture, 0, sizeof (PaAlsaStreamComponent) );
memset( &self->playback, 0, sizeof (PaAlsaStreamComponent) );
if( inParams )
PA_ENSURE( PaAlsaStreamComponent_Initialize( &self->capture, alsaApi, inParams, StreamDirection_In, NULL != callback ) );
if( outParams )
PA_ENSURE( PaAlsaStreamComponent_Initialize( &self->playback, alsaApi, outParams, StreamDirection_Out, NULL != callback ) );
assert( self->capture.nfds || self->playback.nfds );
PA_UNLESS( self->pfds = (struct pollfd*)PaUtil_AllocateMemory( (self->capture.nfds +
self->playback.nfds) * sizeof (struct pollfd) ), paInsufficientMemory );
PaUtil_InitializeCpuLoadMeasurer( &self->cpuLoadMeasurer, sampleRate );
InitializeThreading( &self->threading, &self->cpuLoadMeasurer );
ASSERT_CALL_( pthread_mutex_init( &self->stateMtx, NULL ), 0 );
ASSERT_CALL_( pthread_mutex_init( &self->startMtx, NULL ), 0 );
ASSERT_CALL_( pthread_cond_init( &self->startCond, NULL ), 0 );
error:
return result;
}
/** Free resources associated with stream, and eventually stream itself.
*
* Frees allocated memory, and terminates individual StreamComponents.
*/
static void PaAlsaStream_Terminate( PaAlsaStream *self )
{
assert( self );
if( self->capture.pcm )
{
PaAlsaStreamComponent_Terminate( &self->capture );
}
if( self->playback.pcm )
{
PaAlsaStreamComponent_Terminate( &self->playback );
}
PaUtil_FreeMemory( self->pfds );
ASSERT_CALL_( pthread_mutex_destroy( &self->stateMtx ), 0 );
ASSERT_CALL_( pthread_mutex_destroy( &self->startMtx ), 0 );
ASSERT_CALL_( pthread_cond_destroy( &self->startCond ), 0 );
PaUtil_FreeMemory( self );
}
/** Calculate polling timeout
*
* @param frames Time to wait
* @return Polling timeout in milliseconds
*/
static int CalculatePollTimeout( const PaAlsaStream *stream, unsigned long frames )
{
assert( stream->streamRepresentation.streamInfo.sampleRate > 0.0 );
/* Period in msecs, rounded up */
return (int)ceil( 1000 * frames / stream->streamRepresentation.streamInfo.sampleRate );
}
/** Set up ALSA stream parameters.
*
*/
static PaError PaAlsaStream_Configure( PaAlsaStream *self, const PaStreamParameters *inParams, const PaStreamParameters
*outParams, double sampleRate, unsigned long framesPerHostBuffer, double *inputLatency, double *outputLatency,
unsigned long *maxHostBufferSize )
{
PaError result = paNoError;
double realSr = sampleRate;
if( self->capture.pcm )
PA_ENSURE( PaAlsaStreamComponent_Configure( &self->capture, inParams, framesPerHostBuffer, self->primeBuffers,
self->callbackMode, &realSr, inputLatency ) );
if( self->playback.pcm )
PA_ENSURE( PaAlsaStreamComponent_Configure( &self->playback, outParams, framesPerHostBuffer, self->primeBuffers,
self->callbackMode, &realSr, outputLatency ) );
/* Should be exact now */
self->streamRepresentation.streamInfo.sampleRate = realSr;
/* this will cause the two streams to automatically start/stop/prepare in sync.
* We only need to execute these operations on one of the pair.
* A: We don't want to do this on a blocking stream.
*/
if( self->callbackMode && self->capture.pcm && self->playback.pcm )
{
int err = snd_pcm_link( self->capture.pcm, self->playback.pcm );
if( err >= 0 )
self->pcmsSynced = 1;
else
PA_DEBUG(( "%s: Unable to sync pcms: %s\n", __FUNCTION__, snd_strerror( err ) ));
}
/* Frames per host buffer for the stream is set as a compromise between the two directions */
framesPerHostBuffer = PA_MIN( self->capture.pcm ? self->capture.framesPerBuffer : ULONG_MAX,
self->playback.pcm ? self->playback.framesPerBuffer : ULONG_MAX );
self->pollTimeout = CalculatePollTimeout( self, framesPerHostBuffer ); /* Period in msecs, rounded up */
*maxHostBufferSize = PA_MAX( self->capture.pcm ? self->capture.bufferSize : 0,
self->playback.pcm ? self->playback.bufferSize : 0 );
/* Time before watchdog unthrottles realtime thread == 1/4 of period time in msecs */
self->threading.throttledSleepTime = (unsigned long) (framesPerHostBuffer / sampleRate / 4 * 1000);
if( self->callbackMode )
{
/* If the user expects a certain number of frames per callback we will either have to rely on block adaption
* (framesPerHostBuffer is not an integer multiple of framesPerBuffer) or we can simply align the number
* of host buffer frames with what the user specified */
if( self->framesPerUserBuffer != paFramesPerBufferUnspecified )
{
/* self->alignFrames = 1; */
/* Unless the ratio between number of host and user buffer frames is an integer we will have to rely
* on block adaption */
/*
if( framesPerHostBuffer % framesPerBuffer != 0 || (self->capture.pcm && self->playback.pcm &&
self->capture.framesPerBuffer != self->playback.framesPerBuffer) )
self->useBlockAdaption = 1;
else
self->alignFrames = 1;
*/
}
}
error:
return result;
}
/* We need to determine how many frames per host buffer to use. Our
* goals are to provide the best possible performance, but also to
* most closely honor the requested latency settings. Therefore this
* decision is based on:
*
* - the period sizes that playback and/or capture support. The
* host buffer size has to be one of these.
* - the number of periods that playback and/or capture support.
*
* We want to make period_size*(num_periods-1) to be as close as possible
* to latency*rate for both playback and capture.
*
* This is one of those blocks of code that will just take a lot of
* refinement to be any good.
*
* In the full-duplex case it is possible that the routine was unable
* to find a number of frames per buffer acceptable to both devices
* TODO: Implement an algorithm to find the value closest to acceptance
* by both devices, to minimize difference between period sizes?
*/
static PaError DetermineFramesPerBuffer( const PaAlsaStream *stream, double sampleRate, const PaStreamParameters *inputParameters,
const PaStreamParameters *outputParameters, unsigned long *determinedFrames, const PaUtilHostApiRepresentation *hostApi )
{
PaError result = paNoError;
unsigned long framesPerBuffer = 0;
int numHostInputChannels = 0, numHostOutputChannels = 0;
/* XXX: Clean this up */
if( stream->capture.pcm )
{
const PaAlsaDeviceInfo *devInfo = GetDeviceInfo( hostApi, inputParameters->device );
numHostInputChannels = PA_MAX( inputParameters->channelCount, devInfo->minInputChannels );
}
if( stream->playback.pcm )
{
const PaAlsaDeviceInfo *devInfo = GetDeviceInfo( hostApi, outputParameters->device );
numHostOutputChannels = PA_MAX( outputParameters->channelCount, devInfo->minOutputChannels );
}
if( stream->capture.pcm && stream->playback.pcm )
{
snd_pcm_uframes_t desiredLatency, e;
snd_pcm_uframes_t minPeriodSize, minPlayback, minCapture, maxPeriodSize, maxPlayback, maxCapture,
optimalPeriodSize, periodSize;
int dir = 0;
unsigned int minPeriods = 2;
snd_pcm_t *pcm;
snd_pcm_hw_params_t *hwParamsPlayback, *hwParamsCapture;
snd_pcm_hw_params_alloca( &hwParamsPlayback );
snd_pcm_hw_params_alloca( &hwParamsCapture );
/* Come up with a common desired latency */
pcm = stream->playback.pcm;
snd_pcm_hw_params_any( pcm, hwParamsPlayback );
ENSURE_( SetApproximateSampleRate( pcm, hwParamsPlayback, sampleRate ), paInvalidSampleRate );
ENSURE_( snd_pcm_hw_params_set_channels( pcm, hwParamsPlayback, numHostOutputChannels ),
paBadIODeviceCombination );
ENSURE_( snd_pcm_hw_params_set_period_size_integer( pcm, hwParamsPlayback ), paUnanticipatedHostError );
ENSURE_( snd_pcm_hw_params_set_periods_integer( pcm, hwParamsPlayback ), paUnanticipatedHostError );
ENSURE_( snd_pcm_hw_params_set_periods_min( pcm, hwParamsPlayback, &minPeriods, &dir ), paUnanticipatedHostError );
ENSURE_( snd_pcm_hw_params_get_period_size_min( hwParamsPlayback, &minPlayback, &dir ), paUnanticipatedHostError );
ENSURE_( snd_pcm_hw_params_get_period_size_max( hwParamsPlayback, &maxPlayback, &dir ), paUnanticipatedHostError );
pcm = stream->capture.pcm;
ENSURE_( snd_pcm_hw_params_any( pcm, hwParamsCapture ), paUnanticipatedHostError );
ENSURE_( SetApproximateSampleRate( pcm, hwParamsCapture, sampleRate ), paBadIODeviceCombination );
ENSURE_( snd_pcm_hw_params_set_channels( pcm, hwParamsCapture, numHostInputChannels ),
paBadIODeviceCombination );
ENSURE_( snd_pcm_hw_params_set_period_size_integer( pcm, hwParamsCapture ), paUnanticipatedHostError );
ENSURE_( snd_pcm_hw_params_set_periods_integer( pcm, hwParamsCapture ), paUnanticipatedHostError );
ENSURE_( snd_pcm_hw_params_set_periods_min( pcm, hwParamsCapture, &minPeriods, &dir ), paUnanticipatedHostError );
ENSURE_( snd_pcm_hw_params_get_period_size_min( hwParamsCapture, &minCapture, &dir ), paUnanticipatedHostError );
ENSURE_( snd_pcm_hw_params_get_period_size_max( hwParamsCapture, &maxCapture, &dir ), paUnanticipatedHostError );
minPeriodSize = PA_MAX( minPlayback, minCapture );
maxPeriodSize = PA_MIN( maxPlayback, maxCapture );
desiredLatency = (snd_pcm_uframes_t) (PA_MIN( outputParameters->suggestedLatency, inputParameters->suggestedLatency )
* sampleRate);
/* Clamp desiredLatency */
{
snd_pcm_uframes_t tmp, maxBufferSize = ULONG_MAX;
ENSURE_( snd_pcm_hw_params_get_buffer_size_max( hwParamsPlayback, &maxBufferSize ), paUnanticipatedHostError );
ENSURE_( snd_pcm_hw_params_get_buffer_size_max( hwParamsCapture, &tmp ), paUnanticipatedHostError );
maxBufferSize = PA_MIN( maxBufferSize, tmp );
desiredLatency = PA_MIN( desiredLatency, maxBufferSize );
}
/* Find the closest power of 2 */
e = ilogb( minPeriodSize );
if( minPeriodSize & (minPeriodSize - 1) )
e += 1;
periodSize = (snd_pcm_uframes_t) pow( 2, e );
while( periodSize <= maxPeriodSize )
{
if( snd_pcm_hw_params_test_peri
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -