📄 pa_unix_oss.c
字号:
assert( component );
if( component->fd >= 0 )
close( component->fd );
if( component->buffer )
PaUtil_FreeMemory( component->buffer );
if( component->userBuffers )
PaUtil_FreeMemory( component->userBuffers );
PaUtil_FreeMemory( component );
}
static PaError ModifyBlocking( int fd, int blocking )
{
PaError result = paNoError;
int fflags;
ENSURE_( fflags = fcntl( fd, F_GETFL ), paUnanticipatedHostError );
if( blocking )
fflags &= ~O_NONBLOCK;
else
fflags |= O_NONBLOCK;
ENSURE_( fcntl( fd, F_SETFL, fflags ), paUnanticipatedHostError );
error:
return result;
}
static PaError OpenDevices( const char *idevName, const char *odevName, int *idev, int *odev )
{
PaError result = paNoError;
int flags = O_NONBLOCK, duplex = 0;
int enableBits = 0;
*idev = *odev = -1;
if( idevName && odevName )
{
duplex = 1;
flags |= O_RDWR;
}
else if( idevName )
flags |= O_RDONLY;
else
flags |= O_WRONLY;
/* open first in nonblocking mode, in case it's busy...
* A: then unset the non-blocking attribute */
assert( flags & O_NONBLOCK );
if( idevName )
{
ENSURE_( *idev = open( idevName, flags ), paDeviceUnavailable );
PA_ENSURE( ModifyBlocking( *idev, 1 ) ); /* Blocking */
/* Initially disable */
enableBits = ~PCM_ENABLE_INPUT;
ENSURE_( ioctl( *idev, SNDCTL_DSP_SETTRIGGER, &enableBits ), paUnanticipatedHostError );
}
if( odevName )
{
if( !idevName )
{
ENSURE_( *odev = open( odevName, flags ), paDeviceUnavailable );
PA_ENSURE( ModifyBlocking( *odev, 1 ) ); /* Blocking */
/* Initially disable */
enableBits = ~PCM_ENABLE_OUTPUT;
ENSURE_( ioctl( *odev, SNDCTL_DSP_SETTRIGGER, &enableBits ), paUnanticipatedHostError );
}
else
{
ENSURE_( *odev = dup( *idev ), paUnanticipatedHostError );
}
}
error:
return result;
}
static PaError PaOssStream_Initialize( PaOssStream *stream, const PaStreamParameters *inputParameters, const PaStreamParameters *outputParameters,
PaStreamCallback callback, void *userData, PaStreamFlags streamFlags,
PaOSSHostApiRepresentation *ossApi )
{
PaError result = paNoError;
int idev, odev;
PaUtilHostApiRepresentation *hostApi = &ossApi->inheritedHostApiRep;
const char *idevName = NULL, *odevName = NULL;
assert( stream );
memset( stream, 0, sizeof (PaOssStream) );
stream->isStopped = 1;
PA_ENSURE( PaUtil_InitializeThreading( &stream->threading ) );
if( inputParameters && outputParameters )
{
if( inputParameters->device == outputParameters->device )
stream->sharedDevice = 1;
}
if( inputParameters )
idevName = hostApi->deviceInfos[inputParameters->device]->name;
if( outputParameters )
odevName = hostApi->deviceInfos[outputParameters->device]->name;
PA_ENSURE( OpenDevices( idevName, odevName, &idev, &odev ) );
if( inputParameters )
{
PA_UNLESS( stream->capture = PaUtil_AllocateMemory( sizeof (PaOssStreamComponent) ), paInsufficientMemory );
PA_ENSURE( PaOssStreamComponent_Initialize( stream->capture, inputParameters, callback != NULL, idev, idevName ) );
}
if( outputParameters )
{
PA_UNLESS( stream->playback = PaUtil_AllocateMemory( sizeof (PaOssStreamComponent) ), paInsufficientMemory );
PA_ENSURE( PaOssStreamComponent_Initialize( stream->playback, outputParameters, callback != NULL, odev, odevName ) );
}
if( callback != NULL )
{
PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation,
&ossApi->callbackStreamInterface, callback, userData );
stream->callbackMode = 1;
}
else
{
PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation,
&ossApi->blockingStreamInterface, callback, userData );
}
ENSURE_( sem_init( &stream->semaphore, 0, 0 ), paInternalError );
error:
return result;
}
static void PaOssStream_Terminate( PaOssStream *stream )
{
assert( stream );
PaUtil_TerminateStreamRepresentation( &stream->streamRepresentation );
PaUtil_TerminateThreading( &stream->threading );
if( stream->capture )
PaOssStreamComponent_Terminate( stream->capture );
if( stream->playback )
PaOssStreamComponent_Terminate( stream->playback );
sem_destroy( &stream->semaphore );
PaUtil_FreeMemory( stream );
}
/** Translate from PA format to OSS native.
*
*/
static PaError Pa2OssFormat( PaSampleFormat paFormat, int *ossFormat )
{
switch( paFormat )
{
case paUInt8:
*ossFormat = AFMT_U8;
break;
case paInt8:
*ossFormat = AFMT_S8;
break;
case paInt16:
*ossFormat = AFMT_S16_NE;
break;
default:
return paInternalError; /* This shouldn't happen */
}
return paNoError;
}
/** Return the PA-compatible formats that this device can support.
*
*/
static PaError GetAvailableFormats( PaOssStreamComponent *component, PaSampleFormat *availableFormats )
{
PaError result = paNoError;
int mask = 0;
PaSampleFormat frmts = 0;
ENSURE_( ioctl( component->fd, SNDCTL_DSP_GETFMTS, &mask ), paUnanticipatedHostError );
if( mask & AFMT_U8 )
frmts |= paUInt8;
if( mask & AFMT_S8 )
frmts |= paInt8;
if( mask & AFMT_S16_NE )
frmts |= paInt16;
else
result = paSampleFormatNotSupported;
*availableFormats = frmts;
error:
return result;
}
static unsigned int PaOssStreamComponent_FrameSize( PaOssStreamComponent *component )
{
return Pa_GetSampleSize( component->hostFormat ) * component->hostChannelCount;
}
/** Buffer size in bytes.
*
*/
static unsigned long PaOssStreamComponent_BufferSize( PaOssStreamComponent *component )
{
return PaOssStreamComponent_FrameSize( component ) * component->hostFrames * component->numBufs;
}
static int CalcHigherLogTwo( int n )
{
int log2 = 0;
while( (1<<log2) < n ) log2++;
return log2;
}
static PaError PaOssStreamComponent_Configure( PaOssStreamComponent *component, double sampleRate, unsigned long framesPerBuffer,
StreamMode streamMode, PaOssStreamComponent *master )
{
PaError result = paNoError;
int temp, nativeFormat;
int sr = (int)sampleRate;
PaSampleFormat availableFormats = 0, hostFormat;
int chans = component->userChannelCount;
int frgmt;
int numBufs;
int bytesPerBuf;
double bufSz;
unsigned long fragSz;
audio_buf_info bufInfo;
/* We may have a situation where only one component (the master) is configured, if both point to the same device.
* In that case, the second component will copy settings from the other */
if( !master )
{
/* Aspect BufferSettings: If framesPerBuffer is unspecified we have to infer a suitable fragment size.
* The hardware need not respect the requested fragment size, so we may have to adapt.
*/
if( framesPerBuffer == paFramesPerBufferUnspecified )
{
bufSz = component->latency * sampleRate;
fragSz = bufSz / 4;
}
else
{
fragSz = framesPerBuffer;
bufSz = component->latency * sampleRate + fragSz; /* Latency + 1 buffer */
}
PA_ENSURE( GetAvailableFormats( component, &availableFormats ) );
hostFormat = PaUtil_SelectClosestAvailableFormat( availableFormats, component->userFormat );
/* OSS demands at least 2 buffers, and 16 bytes per buffer */
numBufs = PA_MAX( bufSz / fragSz, 2 );
bytesPerBuf = PA_MAX( fragSz * Pa_GetSampleSize( hostFormat ) * chans, 16 );
/* The fragment parameters are encoded like this:
* Most significant byte: number of fragments
* Least significant byte: exponent of fragment size (i.e., for 256, 8)
*/
frgmt = (numBufs << 16) + (CalcHigherLogTwo( bytesPerBuf ) & 0xffff);
ENSURE_( ioctl( component->fd, SNDCTL_DSP_SETFRAGMENT, &frgmt ), paUnanticipatedHostError );
/* A: according to the OSS programmer's guide parameters should be set in this order:
* format, channels, rate */
/* This format should be deemed good before we get this far */
PA_ENSURE( Pa2OssFormat( hostFormat, &temp ) );
nativeFormat = temp;
ENSURE_( ioctl( component->fd, SNDCTL_DSP_SETFMT, &temp ), paUnanticipatedHostError );
PA_UNLESS( temp == nativeFormat, paInternalError );
/* try to set the number of channels */
ENSURE_( ioctl( component->fd, SNDCTL_DSP_CHANNELS, &chans ), paSampleFormatNotSupported ); /* XXX: Should be paInvalidChannelCount? */
/* It's possible that the minimum number of host channels is greater than what the user requested */
PA_UNLESS( chans >= component->userChannelCount, paInvalidChannelCount );
/* try to set the sample rate */
ENSURE_( ioctl( component->fd, SNDCTL_DSP_SPEED, &sr ), paInvalidSampleRate );
/* 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_( ioctl( component->fd, streamMode == StreamMode_In ? SNDCTL_DSP_GETISPACE : SNDCTL_DSP_GETOSPACE, &bufInfo ),
paUnanticipatedHostError );
component->numBufs = bufInfo.fragstotal;
/* This needs to be the last ioctl call before the first read/write, according to the OSS programmer's guide */
ENSURE_( ioctl( component->fd, SNDCTL_DSP_GETBLKSIZE, &bytesPerBuf ), paUnanticipatedHostError );
component->hostFrames = bytesPerBuf / Pa_GetSampleSize( hostFormat ) / chans;
component->hostChannelCount = chans;
component->hostFormat = hostFormat;
}
else
{
component->hostFormat = master->hostFormat;
component->hostFrames = master->hostFrames;
component->hostChannelCount = master->hostChannelCount;
component->numBufs = master->numBufs;
}
PA_UNLESS( component->buffer = PaUtil_AllocateMemory( PaOssStreamComponent_BufferSize( component ) ),
paInsufficientMemory );
error:
return result;
}
static PaError PaOssStreamComponent_Read( PaOssStreamComponent *component, unsigned long *frames )
{
PaError result = paNoError;
size_t len = *frames * PaOssStreamComponent_FrameSize( component );
ssize_t bytesRead;
ENSURE_( bytesRead = read( component->fd, component->buffer, len ), paUnanticipatedHostError );
*frames = bytesRead / PaOssStreamComponent_FrameSize( component );
error:
return result;
}
static PaError PaOssStreamComponent_Write( PaOssStreamComponent *component, unsigned long *frames )
{
PaError result = paNoError;
size_t len = *frames * PaOssStreamComponent_FrameSize( component );
ssize_t bytesWritten;
ENSURE_( bytesWritten = write( component->fd, component->buffer, len ), paUnanticipatedHostError );
*frames = bytesWritten / PaOssStreamComponent_FrameSize( component );
error:
return result;
}
/** Configure the stream according to input/output parameters.
*
* Aspect StreamChannels: The minimum number of channels supported by the device may exceed that requested by
* the user, if so we'll record the actual number of host channels and adapt later.
*/
static PaError PaOssStream_Configure( PaOssStream *stream, double sampleRate, unsigned long framesPerBuffer,
double *inputLatency, double *outputLatency )
{
PaError result = paNoError;
int duplex = stream->capture && stream->playback;
unsigned long framesPerHostBuffer = 0;
/* We should request full duplex first thing after opening the device */
if( duplex && stream->sharedDevice )
ENSURE_( ioctl( stream->capture->fd, SNDCTL_DSP_SETDUPLEX, 0 ), paUnanticipatedHostError );
if( stream->capture )
{
PaOssStreamComponent *component = stream->capture;
PaOssStreamComponent_Configure( component, sampleRate, framesPerBuffer, StreamMode_In, NULL );
assert( component->hostChannelCount > 0 );
assert( component->hostFrames > 0 );
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -