📄 pa_unix_oss.c
字号:
*
* Aspect StreamState: If the user has registered a streamFinishedCallback it will be called here
*/
static void OnExit( void *data )
{
PaOssStream *stream = (PaOssStream *) data;
assert( data );
PaUtil_ResetCpuLoadMeasurer( &stream->cpuLoadMeasurer );
PaOssStream_Stop( stream, stream->callbackAbort );
PA_DEBUG(( "OnExit: Stoppage\n" ));
/* Eventually notify user all buffers have played */
if( stream->streamRepresentation.streamFinishedCallback )
stream->streamRepresentation.streamFinishedCallback( stream->streamRepresentation.userData );
stream->callbackAbort = 0; /* Clear state */
stream->isActive = 0;
}
static PaError SetUpBuffers( PaOssStream *stream, unsigned long framesAvail )
{
PaError result = paNoError;
if( stream->capture )
{
PaUtil_SetInterleavedInputChannels( &stream->bufferProcessor, 0, stream->capture->buffer,
stream->capture->hostChannelCount );
PaUtil_SetInputFrameCount( &stream->bufferProcessor, framesAvail );
}
if( stream->playback )
{
PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor, 0, stream->playback->buffer,
stream->playback->hostChannelCount );
PaUtil_SetOutputFrameCount( &stream->bufferProcessor, framesAvail );
}
return result;
}
/** Thread procedure for callback processing.
*
* Aspect StreamState: StartStream will wait on this to initiate audio processing, useful in case the
* callback should be used for buffer priming. When the stream is cancelled a separate function will
* take care of the transition to the Callback Finished state (the stream isn't considered Stopped
* before StopStream() or AbortStream() are called).
*/
static void *PaOSS_AudioThreadProc( void *userData )
{
PaError result = paNoError;
PaOssStream *stream = (PaOssStream*)userData;
unsigned long framesAvail=0, framesProcessed;
int callbackResult = paContinue;
int triggered = stream->triggered; /* See if SNDCTL_DSP_TRIGGER has been issued already */
int initiateProcessing = triggered; /* Already triggered? */
PaStreamCallbackFlags cbFlags = 0; /* We might want to keep state across iterations */
/*
#if ( SOUND_VERSION > 0x030904 )
audio_errinfo errinfo;
#endif
*/
assert( stream );
pthread_cleanup_push( &OnExit, stream ); /* Execute OnExit when exiting */
/* The first time the stream is started we use SNDCTL_DSP_TRIGGER to accurately start capture and
* playback in sync, when the stream is restarted after being stopped we simply start by reading/
* writing.
*/
PA_ENSURE( PaOssStream_Prepare( stream ) );
/* If we are to initiate processing implicitly by reading/writing data, we start off in blocking mode */
if( initiateProcessing )
{
/* Make sure devices are in blocking mode */
if( stream->capture )
ModifyBlocking( stream->capture->fd, 1 );
if( stream->playback )
ModifyBlocking( stream->playback->fd, 1 );
}
while( 1 )
{
PaStreamCallbackTimeInfo timeInfo = {0,0,0}; /* TODO: IMPLEMENT ME */
pthread_testcancel();
if( stream->callbackStop && callbackResult == paContinue )
{
PA_DEBUG(( "Setting callbackResult to paComplete\n" ));
callbackResult = paComplete;
}
/* Aspect StreamState: Because of the messy OSS scheme we can't explicitly trigger device start unless
* the stream has been recently started, we will have to go right ahead and read/write in blocking
* fashion to trigger operation. Therefore we begin with processing one host buffer before we switch
* to non-blocking mode.
*/
if( !initiateProcessing )
{
PA_ENSURE( PaOssStream_WaitForFrames( stream, &framesAvail ) ); /* Wait on available frames */
assert( framesAvail % stream->framesPerHostBuffer == 0 );
}
else
{
framesAvail = stream->framesPerHostBuffer;
}
while( framesAvail > 0 )
{
unsigned long frames = framesAvail;
pthread_testcancel();
PaUtil_BeginCpuLoadMeasurement( &stream->cpuLoadMeasurer );
/* Read data */
if ( stream->capture )
{
PA_ENSURE( PaOssStreamComponent_Read( stream->capture, &frames ) );
assert( frames == framesAvail );
}
#if ( SOUND_VERSION >= 0x030904 )
/*
Check with OSS to see if there have been any under/overruns
since last time we checked.
*/
/*
if( ioctl( stream->deviceHandle, SNDCTL_DSP_GETERROR, &errinfo ) >= 0 )
{
if( errinfo.play_underruns )
cbFlags |= paOutputUnderflow ;
if( errinfo.record_underruns )
cbFlags |= paInputUnderflow ;
}
else
PA_DEBUG(( "SNDCTL_DSP_GETERROR command failed: %s\n", strerror( errno ) ));
*/
#endif
PaUtil_BeginBufferProcessing( &stream->bufferProcessor, &timeInfo,
cbFlags );
cbFlags = 0;
PA_ENSURE( SetUpBuffers( stream, framesAvail ) );
framesProcessed = PaUtil_EndBufferProcessing( &stream->bufferProcessor,
&callbackResult );
assert( framesProcessed == framesAvail );
PaUtil_EndCpuLoadMeasurement( &stream->cpuLoadMeasurer, framesProcessed );
if ( stream->playback )
{
frames = framesAvail;
PA_ENSURE( PaOssStreamComponent_Write( stream->playback, &frames ) );
assert( frames == framesAvail );
/* TODO: handle bytesWritten != bytesRequested (slippage?) */
}
framesAvail -= framesProcessed;
stream->framesProcessed += framesProcessed;
if( callbackResult != paContinue )
break;
}
if( initiateProcessing || !triggered )
{
/* Non-blocking */
if( stream->capture )
PA_ENSURE( ModifyBlocking( stream->capture->fd, 0 ) );
if( stream->playback && !stream->sharedDevice )
PA_ENSURE( ModifyBlocking( stream->playback->fd, 0 ) );
initiateProcessing = 0;
sem_post( &stream->semaphore );
}
if( callbackResult != paContinue )
{
stream->callbackAbort = callbackResult == paAbort;
if( stream->callbackAbort || PaUtil_IsBufferProcessorOutputEmpty( &stream->bufferProcessor ) )
break;
}
}
pthread_cleanup_pop( 1 );
error:
pthread_exit( NULL );
}
/** Close the stream.
*
*/
static PaError CloseStream( PaStream* s )
{
PaError result = paNoError;
PaOssStream *stream = (PaOssStream*)s;
assert( stream );
PaUtil_TerminateBufferProcessor( &stream->bufferProcessor );
PaOssStream_Terminate( stream );
return result;
}
/** Start the stream.
*
* Aspect StreamState: After returning, the stream shall be in the Active state, implying that an eventual
* callback will be repeatedly called in a separate thread. If a separate thread is started this function
* will block untill it has started processing audio, otherwise audio processing is started directly.
*/
static PaError StartStream( PaStream *s )
{
PaError result = paNoError;
PaOssStream *stream = (PaOssStream*)s;
stream->isActive = 1;
stream->isStopped = 0;
stream->lastPosPtr = 0;
stream->lastStreamBytes = 0;
stream->framesProcessed = 0;
/* only use the thread for callback streams */
if( stream->bufferProcessor.streamCallback )
{
PA_ENSURE( PaUtil_StartThreading( &stream->threading, &PaOSS_AudioThreadProc, stream ) );
sem_wait( &stream->semaphore );
}
else
PA_ENSURE( PaOssStream_Prepare( stream ) );
error:
return result;
}
static PaError RealStop( PaOssStream *stream, int abort )
{
PaError result = paNoError;
if( stream->callbackMode )
{
if( abort )
stream->callbackAbort = 1;
else
stream->callbackStop = 1;
PA_ENSURE( PaUtil_CancelThreading( &stream->threading, !abort, NULL ) );
stream->callbackStop = stream->callbackAbort = 0;
}
else
PA_ENSURE( PaOssStream_Stop( stream, abort ) );
stream->isStopped = 1;
error:
return result;
}
/** Stop the stream.
*
* Aspect StreamState: This will cause the stream to transition to the Stopped state, playing all enqueued
* buffers.
*/
static PaError StopStream( PaStream *s )
{
return RealStop( (PaOssStream *)s, 0 );
}
/** Abort the stream.
*
* Aspect StreamState: This will cause the stream to transition to the Stopped state, discarding all enqueued
* buffers. Note that the buffers are not currently correctly discarded, this is difficult without closing
* the OSS device.
*/
static PaError AbortStream( PaStream *s )
{
return RealStop( (PaOssStream *)s, 1 );
}
/** Is the stream in the Stopped state.
*
*/
static PaError IsStreamStopped( PaStream *s )
{
PaOssStream *stream = (PaOssStream*)s;
return (stream->isStopped);
}
/** Is the stream in the Active state.
*
*/
static PaError IsStreamActive( PaStream *s )
{
PaOssStream *stream = (PaOssStream*)s;
return (stream->isActive);
}
static PaTime GetStreamTime( PaStream *s )
{
PaOssStream *stream = (PaOssStream*)s;
count_info info;
int delta;
if( stream->playback ) {
if( ioctl( stream->playback->fd, SNDCTL_DSP_GETOPTR, &info) == 0 ) {
delta = ( info.bytes - stream->lastPosPtr ) & 0x000FFFFF;
return ( stream->lastStreamBytes + delta) / PaOssStreamComponent_FrameSize( stream->playback ) / stream->sampleRate;
}
}
else {
if (ioctl( stream->capture->fd, SNDCTL_DSP_GETIPTR, &info) == 0) {
delta = (info.bytes - stream->lastPosPtr) & 0x000FFFFF;
return ( stream->lastStreamBytes + delta) / PaOssStreamComponent_FrameSize( stream->capture ) / stream->sampleRate;
}
}
/* the ioctl failed, but we can still give a coarse estimate */
return stream->framesProcessed / stream->sampleRate;
}
static double GetStreamCpuLoad( PaStream* s )
{
PaOssStream *stream = (PaOssStream*)s;
return PaUtil_GetCpuLoad( &stream->cpuLoadMeasurer );
}
/*
As separate stream interfaces are used for blocking and callback
streams, the following functions can be guaranteed to only be called
for blocking streams.
*/
static PaError ReadStream( PaStream* s,
void *buffer,
unsigned long frames )
{
PaOssStream *stream = (PaOssStream*)s;
int bytesRequested, bytesRead;
unsigned long framesRequested;
void *userBuffer;
/* If user input is non-interleaved, PaUtil_CopyInput will manipulate the channel pointers,
* so we copy the user provided pointers */
if( stream->bufferProcessor.userInputIsInterleaved )
userBuffer = buffer;
else /* Copy channels into local array */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -