📄 pa_linux_alsa.c
字号:
PA_UNLESS( self->pfds = (struct pollfd*)PaUtil_AllocateMemory( (self->capture.nfds + self->playback.nfds) * sizeof (struct pollfd) ), paInsufficientMemory ); PaUtil_InitializeCpuLoadMeasurer( &self->cpuLoadMeasurer, sampleRate ); ASSERT_CALL_( PaUnixMutex_Initialize( &self->stateMtx ), paNoError );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_( PaUnixMutex_Terminate( &self->stateMtx ), paNoError ); 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 );}/** Determine size per host buffer. * * During this method call, the component's framesPerBuffer attribute gets computed, and the corresponding period size * gets configured for the device. * @param accurate: If the configured period size is non-integer, this will be set to 0. */static PaError PaAlsaStreamComponent_DetermineFramesPerBuffer( PaAlsaStreamComponent* self, const PaStreamParameters* params, unsigned long framesPerUserBuffer, double sampleRate, snd_pcm_hw_params_t* hwParams, int* accurate ){ PaError result = paNoError; unsigned long bufferSize = params->suggestedLatency * sampleRate, framesPerHostBuffer; int dir = 0; { snd_pcm_uframes_t tmp; snd_pcm_hw_params_get_buffer_size_min( hwParams, &tmp ); bufferSize = PA_MAX( bufferSize, tmp ); snd_pcm_hw_params_get_buffer_size_max( hwParams, &tmp ); bufferSize = PA_MIN( bufferSize, tmp ); } assert( bufferSize > 0 ); if( framesPerUserBuffer != paFramesPerBufferUnspecified ) { /* Preferably the host buffer size should be a multiple of the user buffer size */ if( bufferSize > framesPerUserBuffer ) { snd_pcm_uframes_t remainder = bufferSize % framesPerUserBuffer; if( remainder > framesPerUserBuffer / 2. ) bufferSize += framesPerUserBuffer - remainder; else bufferSize -= remainder; assert( bufferSize % framesPerUserBuffer == 0 ); } else if( framesPerUserBuffer % bufferSize != 0 ) { /* Find a good compromise between user specified latency and buffer size */ if( bufferSize > framesPerUserBuffer * .75 ) { bufferSize = framesPerUserBuffer; } else { snd_pcm_uframes_t newSz = framesPerUserBuffer; while( newSz / 2 >= bufferSize ) { if( framesPerUserBuffer % (newSz / 2) != 0 ) { /* No use dividing any further */ break; } newSz /= 2; } bufferSize = newSz; } assert( framesPerUserBuffer % bufferSize == 0 ); } } /* Using 5 as a base number of periods, we try to approximate the suggested latency (+1 period), finding a combination of period/buffer size which best fits these constraints */ { unsigned numPeriods = 4, maxPeriods = 0; /* It may be that the device only supports 2 periods for instance */ dir = 0; ENSURE_( snd_pcm_hw_params_get_periods_max( hwParams, &maxPeriods, &dir ), paUnanticipatedHostError ); assert( maxPeriods > 1 ); /* One period is not counted as latency */ maxPeriods -= 1; numPeriods = PA_MIN( maxPeriods, numPeriods ); if( framesPerUserBuffer != paFramesPerBufferUnspecified ) { framesPerHostBuffer = framesPerUserBuffer; if( framesPerHostBuffer < bufferSize ) { while( bufferSize / framesPerHostBuffer > numPeriods ) { framesPerHostBuffer *= 2; } } else { while( bufferSize / framesPerHostBuffer < numPeriods ) { if( framesPerUserBuffer % (framesPerHostBuffer / 2) != 0 ) { /* Can't be divided any further */ break; } framesPerHostBuffer /= 2; } } if( framesPerHostBuffer < framesPerUserBuffer ) { assert( framesPerUserBuffer % framesPerHostBuffer == 0 ); if( snd_pcm_hw_params_test_period_size( self->pcm, hwParams, framesPerHostBuffer, 0 ) < 0 ) { if( snd_pcm_hw_params_test_period_size( self->pcm, hwParams, framesPerHostBuffer * 2, 0 ) == 0 ) framesPerHostBuffer *= 2; else if( snd_pcm_hw_params_test_period_size( self->pcm, hwParams, framesPerHostBuffer / 2, 0 ) == 0 ) framesPerHostBuffer /= 2; } } else { assert( framesPerHostBuffer % framesPerUserBuffer == 0 ); if( snd_pcm_hw_params_test_period_size( self->pcm, hwParams, framesPerHostBuffer, 0 ) < 0 ) { if( snd_pcm_hw_params_test_period_size( self->pcm, hwParams, framesPerHostBuffer + framesPerUserBuffer, 0 ) == 0 ) framesPerHostBuffer += framesPerUserBuffer; else if( snd_pcm_hw_params_test_period_size( self->pcm, hwParams, framesPerHostBuffer - framesPerUserBuffer, 0 ) == 0 ) framesPerHostBuffer -= framesPerUserBuffer; } } } else { framesPerHostBuffer = bufferSize / numPeriods; } } assert( framesPerHostBuffer > 0 ); { snd_pcm_uframes_t min = 0, max = 0; ENSURE_( snd_pcm_hw_params_get_period_size_min( hwParams, &min, NULL ), paUnanticipatedHostError ); ENSURE_( snd_pcm_hw_params_get_period_size_max( hwParams, &max, NULL ), paUnanticipatedHostError ); if( framesPerHostBuffer < min ) { PA_DEBUG(( "%s: The determined period size (%lu) is less than minimum (%lu)\n", __FUNCTION__, framesPerHostBuffer, min )); framesPerHostBuffer = min; } else if( framesPerHostBuffer > max ) { PA_DEBUG(( "%s: The determined period size (%lu) is greater than maximum (%lu)\n", __FUNCTION__, framesPerHostBuffer, max )); framesPerHostBuffer = max; } assert( framesPerHostBuffer >= min && framesPerHostBuffer <= max ); dir = 0; ENSURE_( snd_pcm_hw_params_set_period_size_near( self->pcm, hwParams, &framesPerHostBuffer, &dir ), paUnanticipatedHostError ); if( dir != 0 ) { PA_DEBUG(( "%s: The configured period size is non-integer.\n", __FUNCTION__, dir )); *accurate = 0; } } self->framesPerBuffer = framesPerHostBuffer;error: return result;}/* We need to determine how many frames per host buffer (period) to use. Our * goals are to provide the best possible performance, but also to * honor the requested latency settings as closely as we can. 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 method will determine suitable period sizes for capture and playback handles, and report the maximum number of * frames per host buffer. The latter is relevant, in case we should be so unfortunate that the period size differs * between capture and playback. If this should happen, the stream's hostBufferSizeMode attribute will be set to * paUtilBoundedHostBufferSize, because the best we can do is limit the size of individual host buffers to the upper * bound. The size of host buffers scheduled for processing should only matter if the user has specified a buffer size, * but when he/she does we must strive for an optimal configuration. By default we'll opt for a fixed host buffer size, * which should be fine if the period size is the same for capture and playback. In general, if there is a specified user * buffer size, this method tries it best to determine a period size which is a multiple of the user buffer size. * * The framesPerBuffer attributes of the individual capture and playback components of the stream are set to corresponding * values determined here. Since these should be reported as * * 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? * * @param determinedFramesPerHostBuffer: The determined host buffer size. */static PaError PaAlsaStream_DetermineFramesPerBuffer( PaAlsaStream* self, double sampleRate, const PaStreamParameters* inputParameters, const PaStreamParameters* outputParameters, unsigned long framesPerUserBuffer, snd_pcm_hw_params_t* hwParamsCapture, snd_pcm_hw_params_t* hwParamsPlayback, PaUtilHostBufferSizeMode* hostBufferSizeMode ){ PaError result = paNoError; unsigned long framesPerHostBuffer = 0; int dir = 0; int accurate = 1; if( self->capture.pcm && self->playback.pcm ) { if( framesPerUserBuffer == paFramesPerBufferUnspecified ) { snd_pcm_uframes_t desiredLatency, e, minPeriodSize, maxPeriodSize, optimalPeriodSize, periodSize, minCapture, minPlayback, maxCapture, maxPlayback; /* Come up with a common desired latency */ dir = 0; ENSURE_( snd_pcm_hw_params_get_period_size_min( hwParamsCapture, &minCapture, &dir ), paUnanticipatedHostError ); dir = 0; ENSURE_( snd_pcm_hw_params_get_period_size_min( hwParamsPlayback, &minPlayback, &dir ), paUnanticipatedHostError ); dir = 0; ENSURE_( snd_pcm_hw_params_get_period_size_max( hwParamsCapture, &maxCapture, &dir ), paUnanticipatedHostError ); dir = 0; ENSURE_( snd_pcm_hw_params_get_period_size_max( hwParamsPlayback, &maxPlayback, &dir ), paUnanticipatedHostError ); minPeriodSize = PA_MAX( minPlayback, minCapture ); maxPeriodSize = PA_MIN( maxPlayback, maxCapture ); PA_UNLESS( minPeriodSize <= maxPeriodSize, paBadIODeviceCombination ); desiredLatency = (snd_pcm_uframes_t)(PA_MIN( outputParameters->suggestedLatency, inputParameters->suggestedLatency ) * sampleRate); /* Clamp desiredLatency */ { snd_pcm_uframes_t maxBufferSize; snd_pcm_uframes_t maxBufferSizeCapture, maxBufferSizePlayback; ENSURE_( snd_pcm_hw_params_get_buffer_size_max( hwParamsCapture, &maxBufferSizeCapture ), paUnanticipatedHostError ); ENSURE_( snd_pcm_hw_params_get_buffer_size_max( hwParamsPlayback, &maxBufferSizePlayback ), paUnanticipatedHostError ); maxBufferSize = PA_MIN( maxBufferSizeCapture, maxBufferSizePlayback ); 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_period_size( self->playback.pcm, hwParamsPlayback, periodSize, 0 ) >= 0 && snd_pcm_hw_params_test_period_size( self->capture.pcm, hwParamsCapture, periodSize, 0 ) >= 0 ) break; /* Ok! */ periodSize *= 2; } /* 4 periods considered optimal */ optimalPeriodSize = PA_MAX( desiredLatency / 4, minPeriodSize ); optimalPeriodSize = PA_MIN( optimalPeriodSize, maxPeriodSize ); /* Find the closest power of 2 */ e = ilogb( optimalPeriodSize ); if( optimalPeriodSize & (optimalPeriodSize - 1) ) e += 1; optimalPeriodSize = (snd_pcm_uframes_t)pow( 2, e ); 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 ) break; optimalPeriodSize /= 2; } if( optimalPeriodSize > periodSize ) periodSize = optimalPeriodSize; if( periodSize <= maxPeriodSize ) { /* Looks good, the periodSize _should_ be acceptable by both devices */ ENSURE_( snd_pcm_hw_params_set_period_size( self->capture.pcm, hwParamsCapture, periodSize, 0 ), paUnanticipatedHostError ); ENSURE_( snd_pcm_hw_params_set_period_size( self->playback.pcm, hwParamsPlayback, periodSize, 0 ), paUnanticipatedHostError ); self->capture.framesPerBuffer = self->playback.framesPerBuffer = periodSize; framesPerHostBuffer = periodSize; } else { /* Unable to find a common period size, oh well */ optimalPeriodSize = PA_MAX( desiredLatency / 4, minPeriodSize ); optimalPeriodSize = PA_MIN( optimalPeriodSize, maxPeriodSize ); self->capture.framesPerBuffer = optimalPeriodSize; dir = 0; ENSURE_( snd_pcm_hw_params_set_period_size_near( self->capture.pcm, hwParamsCapture, &self->capture.framesPerBuffer, &dir ), paUnanticipatedHostError ); self->playback.framesPerBuffer = optimalPeriodSize; dir = 0; ENSURE_( snd_pcm_hw_params_set_period_size_
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -