📄 pa_linux_alsa.c
字号:
snd_pcm_hw_params_alloca( &hwParamsPlayback ); snd_pcm_hw_params_alloca( &hwParamsCapture ); /* Come up with a common desired latency */ pcm = stream->pcm_playback; snd_pcm_hw_params_any( pcm, hwParamsPlayback ); ENSURE( SetApproximateSampleRate( pcm, hwParamsPlayback, sampleRate ), paBadIODeviceCombination ); ENSURE( snd_pcm_hw_params_set_channels( pcm, hwParamsPlayback, outputParameters->channelCount ), 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_get_period_size_min( hwParamsPlayback, &minPlayback, &dir ), paUnanticipatedHostError ); ENSURE( snd_pcm_hw_params_get_period_size_max( hwParamsPlayback, &maxPlayback, &dir ), paUnanticipatedHostError ); pcm = stream->pcm_capture; ENSURE( snd_pcm_hw_params_any( pcm, hwParamsCapture ), paUnanticipatedHostError ); ENSURE( SetApproximateSampleRate( pcm, hwParamsCapture, sampleRate ), paBadIODeviceCombination ); ENSURE( snd_pcm_hw_params_set_channels( pcm, hwParamsCapture, inputParameters->channelCount ), 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_get_period_size_min( hwParamsCapture, &minCapture, &dir ), paUnanticipatedHostError ); ENSURE( snd_pcm_hw_params_get_period_size_max( hwParamsCapture, &maxCapture, &dir ), paUnanticipatedHostError ); minPeriodSize = MAX( minPlayback, minCapture ); maxPeriodSize = MIN( maxPlayback, maxCapture ); desiredLatency = (snd_pcm_uframes_t) (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, &tmp ), paUnanticipatedHostError ); maxBufferSize = MIN( maxBufferSize, tmp ); ENSURE( snd_pcm_hw_params_get_buffer_size_max( hwParamsCapture, &tmp ), paUnanticipatedHostError ); maxBufferSize = MIN( maxBufferSize, tmp ); desiredLatency = 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( stream->pcm_playback, hwParamsPlayback, periodSize, 0 ) >= 0 && snd_pcm_hw_params_test_period_size( stream->pcm_capture, hwParamsCapture, periodSize, 0 ) >= 0 ) break; /* Ok! */ periodSize *= 2; } /* 4 periods considered optimal */ optimalPeriodSize = MAX( desiredLatency / 4, minPeriodSize ); optimalPeriodSize = 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 ) { pcm = stream->pcm_playback; if( snd_pcm_hw_params_test_period_size( pcm, hwParamsPlayback, optimalPeriodSize, 0 ) < 0 ) continue; pcm = stream->pcm_capture; if( snd_pcm_hw_params_test_period_size( pcm, hwParamsCapture, optimalPeriodSize, 0 ) >= 0 ) break; optimalPeriodSize /= 2; } if( optimalPeriodSize >= periodSize ) periodSize = optimalPeriodSize; if( periodSize <= maxPeriodSize ) { /* Looks good */ framesPerHostBuffer = periodSize; } else /* XXX: Some more descriptive error code might be appropriate */ PA_ENSURE( paBadIODeviceCombination ); } else { /* half-duplex is a slightly simpler case */ unsigned long desiredLatency, channels; snd_pcm_uframes_t minPeriodSize; unsigned int numPeriods; snd_pcm_t *pcm; snd_pcm_hw_params_t *hwParams; snd_pcm_hw_params_alloca( &hwParams ); if( stream->pcm_capture ) { pcm = stream->pcm_capture; desiredLatency = inputParameters->suggestedLatency * sampleRate; channels = inputParameters->channelCount; } else { pcm = stream->pcm_playback; desiredLatency = outputParameters->suggestedLatency * sampleRate; channels = outputParameters->channelCount; } ENSURE( snd_pcm_hw_params_any( pcm, hwParams ), paUnanticipatedHostError ); ENSURE( SetApproximateSampleRate( pcm, hwParams, sampleRate ), paBadIODeviceCombination ); ENSURE( snd_pcm_hw_params_set_channels( pcm, hwParams, channels ), paBadIODeviceCombination ); ENSURE( snd_pcm_hw_params_set_period_size_integer( pcm, hwParams ), paUnanticipatedHostError ); ENSURE( snd_pcm_hw_params_set_periods_integer( pcm, hwParams ), paUnanticipatedHostError ); /* Using 5 as a base number of buffers, we try to approximate the suggested latency (+1 period), finding a period size which best fits these constraints */ ENSURE( snd_pcm_hw_params_get_period_size_min( hwParams, &minPeriodSize, 0 ), paUnanticipatedHostError ); numPeriods = MIN( desiredLatency / minPeriodSize, 4 ) + 1; ENSURE( snd_pcm_hw_params_set_periods_near( pcm, hwParams, &numPeriods, 0 ), paUnanticipatedHostError ); framesPerHostBuffer = desiredLatency / (numPeriods - 1); ENSURE( snd_pcm_hw_params_set_period_size_near( pcm, hwParams, &framesPerHostBuffer, 0 ), paUnanticipatedHostError ); } } } else { framesPerHostBuffer = framesPerBuffer; } /* Will fill in correct values later */ stream->streamRepresentation.streamInfo.inputLatency = 0.; stream->streamRepresentation.streamInfo.outputLatency = 0.; if( numInputChannels > 0 ) { int interleaved = !(inputSampleFormat & paNonInterleaved); PaSampleFormat plain_format = hostInputSampleFormat & ~paNonInterleaved; inputLatency = inputParameters->suggestedLatency; /* Real latency in seconds returned from ConfigureStream */ PA_ENSURE( ConfigureStream( stream->pcm_capture, numInputChannels, &interleaved, &sampleRate, plain_format, framesPerHostBuffer, &stream->captureBufferSize, &inputLatency, 0, stream->callback_mode ) ); stream->capture_interleaved = interleaved; } if( numOutputChannels > 0 ) { int interleaved = !(outputSampleFormat & paNonInterleaved); PaSampleFormat plain_format = hostOutputSampleFormat & ~paNonInterleaved; int primeBuffers = streamFlags & paPrimeOutputBuffersUsingStreamCallback; outputLatency = outputParameters->suggestedLatency; /* Real latency in seconds returned from ConfigureStream */ PA_ENSURE( ConfigureStream( stream->pcm_playback, numOutputChannels, &interleaved, &sampleRate, plain_format, framesPerHostBuffer, &stream->playbackBufferSize, &outputLatency, primeBuffers, stream->callback_mode ) ); /* If the user wants to prime the buffer before stream start, the start threshold will equal buffer size */ if( primeBuffers ) stream->startThreshold = stream->playbackBufferSize; stream->playback_interleaved = interleaved; } /* Should be exact now */ stream->streamRepresentation.streamInfo.sampleRate = sampleRate; PA_ENSURE( PaUtil_InitializeBufferProcessor( &stream->bufferProcessor, numInputChannels, inputSampleFormat, hostInputSampleFormat, numOutputChannels, outputSampleFormat, hostOutputSampleFormat, sampleRate, streamFlags, framesPerBuffer, framesPerHostBuffer, paUtilFixedHostBufferSize, callback, userData ) ); /* Ok, buffer processor is initialized, now we can deduce it's latency */ if( numInputChannels > 0 ) stream->streamRepresentation.streamInfo.inputLatency = inputLatency + PaUtil_GetBufferProcessorInputLatency( &stream->bufferProcessor ); if( numOutputChannels > 0 ) stream->streamRepresentation.streamInfo.outputLatency = outputLatency + PaUtil_GetBufferProcessorOutputLatency( &stream->bufferProcessor ); /* 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( stream->callback_mode && stream->pcm_capture && stream->pcm_playback && snd_pcm_link( stream->pcm_capture, stream->pcm_playback ) >= 0 ) stream->pcmsSynced = 1; UNLESS( stream->pfds = (struct pollfd*)PaUtil_AllocateMemory( (stream->capture_nfds + stream->playback_nfds) * sizeof(struct pollfd) ), paInsufficientMemory ); stream->frames_per_period = framesPerHostBuffer; stream->capture_channels = numInputChannels; stream->playback_channels = numOutputChannels; stream->pollTimeout = (int) ceil( 1000 * stream->frames_per_period/sampleRate ); /* Period in msecs, rounded up */ *s = (PaStream*)stream; return result;error: if( stream ) CleanUpStream( stream ); return result;}/* When CloseStream() is called, the multi-api layer ensures that the stream has already been stopped or aborted.*/static PaError CloseStream( PaStream* s ){ PaError result = paNoError; PaAlsaStream *stream = (PaAlsaStream*)s; PaUtil_TerminateBufferProcessor( &stream->bufferProcessor ); PaUtil_TerminateStreamRepresentation( &stream->streamRepresentation ); CleanUpStream( stream ); return result;}static void SilenceBuffer( PaAlsaStream *stream ){ const snd_pcm_channel_area_t *areas; snd_pcm_uframes_t frames = snd_pcm_avail_update( stream->pcm_playback ); snd_pcm_mmap_begin( stream->pcm_playback, &areas, &stream->playback_offset, &frames ); snd_pcm_areas_silence( areas, stream->playback_offset, stream->playback_channels, frames, stream->playbackNativeFormat ); snd_pcm_mmap_commit( stream->pcm_playback, stream->playback_offset, frames );}/*! \brief Start/prepare pcm(s) for streaming * * Depending on wether the stream is in callback or blocking mode, we will respectively start or simply * prepare the playback pcm. If the buffer has _not_ been primed, we will in callback mode prepare and * silence the buffer before starting playback. In blocking mode we simply prepare, as the playback will * be started automatically as the user writes to output. * * The capture pcm, however, will simply be prepared and started. * * PaAlsaStream::startMtx makes sure access is synchronized (useful in callback mode) */static PaError AlsaStart( PaAlsaStream *stream, int priming ){ PaError result = paNoError; if( stream->pcm_playback ) { if( stream->callback_mode ) { /* We're not priming buffer, so prepare and silence */ if( !priming ) { ENSURE( snd_pcm_prepare( stream->pcm_playback ), paUnanticipatedHostError ); SilenceBuffer( stream ); } ENSURE( snd_pcm_start( stream->pcm_playback ), paUnanticipatedHostError ); } else ENSURE( snd_pcm_prepare( stream->pcm_playback ), paUnanticipatedHostError ); } if( stream->pcm_capture && !stream->pcmsSynced ) { ENSURE( snd_pcm_prepare( stream->pcm_capture ), paUnanticipatedHostError ); /* We want to start capture for a blocking stream as well, since nothing will happen otherwise */ ENSURE( snd_pcm_start( stream->pcm_capture ), paUnanticipatedHostError ); }end: return result;error: goto end;}/*! \brief Utility function for determining if pcms are in running state * */static int IsRunning( PaAlsaStream *stream ){ int result = 0; pthread_mutex_lock( &stream->stateMtx ); /* Synchronize access to pcm state */ if( stream->pcm_capture ) { snd_pcm_state_t capture_state = snd_pcm_state( stream->pcm_capture ); if( capture_state == SND_PCM_STATE_RUNNING || capture_state == SND_PCM_STATE_XRUN || capture_state == SND_PCM_STATE_DRAINING ) { result = 1; goto end; } } if( stream->pcm_playback ) { snd_pcm_state_t playback_state = snd_pcm_state( stream->pcm_playback ); if( playback_state == SND_PCM_STATE_RUNNING || playback_state == SND_PCM_STATE_XRUN || playback_state == SND_PCM_STATE_DRAINING ) { result = 1; goto end; } }end: pthread_mutex_unlock( &stream->stateMtx ); return result;}static PaError StartStream( PaStream *s ){ PaError result = paNoError; PaAlsaStream *stream = (PaAlsaStream*)s; /* Ready the processor */ PaUtil_ResetBufferProcessor( &stream->bufferProcessor ); /* Set now, so we can test for activity further down */ stream->isActive = 1; if( stream->callback_mode ) { int res = 0; PaTime pt = PaUtil_GetTime(); struct timespec ts; pthread_attr_t attr; UNLESS( !pthread_attr_init( &attr ), paInternalError ); /* Priority relative to other processes */ UNLESS( !pthread_attr_setscope( &attr, PTHREAD_SCOPE_SYSTEM ), paInternalError ); UNLESS( !pthread_create( &stream->callback_thread, &attr, &CallbackThread, stream ), paInternalError );
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -