📄 pa_mac_core.c
字号:
* If input and output devs are different or we are doing SR conversion,
* we also need a
* ring buffer to store inpt data while waiting for output
* data.
*/
if( (stream->outputUnit && stream->inputUnit != stream->outputUnit)
|| stream->inputSRConverter )
{
/* May want the ringSize ot initial position in
ring buffer to depend somewhat on sample rate change */
void *data;
long ringSize;
ringSize = computeRingBufferSize( inputParameters,
outputParameters,
stream->inputFramesPerBuffer,
stream->outputFramesPerBuffer,
sampleRate );
/*ringSize <<= 4; *//*16x bigger, for testing */
/*now, we need to allocate memory for the ring buffer*/
data = calloc( ringSize, szfl );
if( !data )
{
result = paInsufficientMemory;
goto error;
}
/* now we can initialize the ring buffer */
/*assert( 0 ==*/
RingBuffer_Init( &stream->inputRingBuffer,
ringSize*szfl, data ) /*)*/;
/* advance the read point a little, so we are reading from the
middle of the buffer */
if( stream->outputUnit )
RingBuffer_AdvanceWriteIndex( &stream->inputRingBuffer, ringSize*szfl / RING_BUFFER_ADVANCE_DENOMINATOR );
}
}
/* -- initialize Blio Buffer Processors -- */
if( !streamCallback )
{
long ringSize;
ringSize = computeRingBufferSize( inputParameters,
outputParameters,
stream->inputFramesPerBuffer,
stream->outputFramesPerBuffer,
sampleRate );
result = initializeBlioRingBuffers( &stream->blio,
inputParameters?inputParameters->sampleFormat:0 ,
outputParameters?outputParameters->sampleFormat:0 ,
MAX(stream->inputFramesPerBuffer,stream->outputFramesPerBuffer),
ringSize,
inputParameters?inputChannelCount:0 ,
outputParameters?outputChannelCount:0 ) ;
if( result != paNoError )
goto error;
}
/* -- initialize Buffer Processor -- */
{
unsigned long maxHostFrames = stream->inputFramesPerBuffer;
if( stream->outputFramesPerBuffer > maxHostFrames )
maxHostFrames = stream->outputFramesPerBuffer;
result = PaUtil_InitializeBufferProcessor( &stream->bufferProcessor,
inputChannelCount, inputSampleFormat,
hostInputSampleFormat,
outputChannelCount, outputSampleFormat,
hostOutputSampleFormat,
sampleRate,
streamFlags,
framesPerBuffer,
/* If sample rate conversion takes place, the buffer size
will not be known. */
maxHostFrames,
stream->inputSRConverter
? paUtilUnknownHostBufferSize
: paUtilBoundedHostBufferSize,
streamCallback ? streamCallback : BlioCallback,
streamCallback ? userData : &stream->blio );
if( result != paNoError )
goto error;
}
stream->bufferProcessorIsInitialized = TRUE;
/*
IMPLEMENT ME: initialise the following fields with estimated or actual
values.
I think this is okay the way it is br 12/1/05
maybe need to change input latency estimate if IO devs differ
*/
stream->streamRepresentation.streamInfo.inputLatency =
PaUtil_GetBufferProcessorInputLatency(&stream->bufferProcessor)/sampleRate;
stream->streamRepresentation.streamInfo.outputLatency =
PaUtil_GetBufferProcessorOutputLatency(&stream->bufferProcessor)/sampleRate;
stream->streamRepresentation.streamInfo.sampleRate = sampleRate;
stream->sampleRate = sampleRate;
stream->outDeviceSampleRate = 0;
if( stream->outputUnit ) {
Float64 rate;
UInt32 size = sizeof( rate );
result = ERR( AudioDeviceGetProperty( stream->outputDevice,
0,
FALSE,
kAudioDevicePropertyNominalSampleRate,
&size, &rate ) );
if( result )
goto error;
stream->outDeviceSampleRate = rate;
}
stream->inDeviceSampleRate = 0;
if( stream->inputUnit ) {
Float64 rate;
UInt32 size = sizeof( rate );
result = ERR( AudioDeviceGetProperty( stream->inputDevice,
0,
TRUE,
kAudioDevicePropertyNominalSampleRate,
&size, &rate ) );
if( result )
goto error;
stream->inDeviceSampleRate = rate;
}
stream->userInChan = inputChannelCount;
stream->userOutChan = outputChannelCount;
stream->isTimeSet = FALSE;
stream->state = STOPPED;
stream->xrunFlags = 0;
*s = (PaStream*)stream;
return result;
error:
CloseStream( stream );
return result;
}
PaTime GetStreamTime( PaStream *s )
{
/* FIXME: I am not at all sure this timing info stuff is right.
patest_sine_time reports negative latencies, which is wierd.*/
PaMacCoreStream *stream = (PaMacCoreStream*)s;
AudioTimeStamp timeStamp;
VVDBUG(("GetStreamTime()\n"));
if ( !stream->isTimeSet )
return (PaTime)0;
if ( stream->outputDevice ) {
AudioDeviceGetCurrentTime( stream->outputDevice, &timeStamp);
return (PaTime)(timeStamp.mSampleTime - stream->startTime.mSampleTime)/stream->outDeviceSampleRate;
} else if ( stream->inputDevice ) {
AudioDeviceGetCurrentTime( stream->inputDevice, &timeStamp);
return (PaTime)(timeStamp.mSampleTime - stream->startTime.mSampleTime)/stream->inDeviceSampleRate;
} else {
return (PaTime)0;
}
}
static void setStreamStartTime( PaStream *stream )
{
/* FIXME: I am not at all sure this timing info stuff is right.
patest_sine_time reports negative latencies, which is wierd.*/
PaMacCoreStream *s = (PaMacCoreStream *) stream;
VVDBUG(("setStreamStartTime()\n"));
if( s->outputDevice )
AudioDeviceGetCurrentTime( s->outputDevice, &s->startTime);
else if( s->inputDevice )
AudioDeviceGetCurrentTime( s->inputDevice, &s->startTime);
else
bzero( &s->startTime, sizeof( s->startTime ) );
//FIXME: we need a memory barier here
s->isTimeSet = TRUE;
}
static PaTime TimeStampToSecs(PaMacCoreStream *stream, const AudioTimeStamp* timeStamp)
{
VVDBUG(("TimeStampToSecs()\n"));
//printf( "ATS: %lu, %g, %g\n", timeStamp->mFlags, timeStamp->mSampleTime, timeStamp->mRateScalar );
if (timeStamp->mFlags & kAudioTimeStampSampleTimeValid)
return (timeStamp->mSampleTime / stream->sampleRate);
else
return 0;
}
#define RING_BUFFER_EMPTY (1000)
static OSStatus ringBufferIOProc( AudioConverterRef inAudioConverter,
UInt32*ioDataSize,
void** outData,
void*inUserData )
{
void *dummyData;
long dummySize;
RingBuffer *rb = (RingBuffer *) inUserData;
VVDBUG(("ringBufferIOProc()\n"));
assert( sizeof( UInt32 ) == sizeof( long ) );
if( RingBuffer_GetReadAvailable( rb ) == 0 ) {
*outData = NULL;
*ioDataSize = 0;
return RING_BUFFER_EMPTY;
}
RingBuffer_GetReadRegions( rb, *ioDataSize,
outData, (long *)ioDataSize,
&dummyData, &dummySize );
assert( *ioDataSize );
RingBuffer_AdvanceReadIndex( rb, *ioDataSize );
return noErr;
}
/*
* Called by the AudioUnit API to process audio from the sound card.
* This is where the magic happens.
*/
/* FEEDBACK: there is a lot of redundant code here because of how all the cases differ. This makes it hard to maintain, so if there are suggestinos for cleaning it up, I'm all ears. */
static OSStatus AudioIOProc( void *inRefCon,
AudioUnitRenderActionFlags *ioActionFlags,
const AudioTimeStamp *inTimeStamp,
UInt32 inBusNumber,
UInt32 inNumberFrames,
AudioBufferList *ioData )
{
unsigned long framesProcessed = 0;
PaStreamCallbackTimeInfo timeInfo = {0,0,0};
PaMacCoreStream *stream = (PaMacCoreStream*)inRefCon;
const bool isRender = inBusNumber == OUTPUT_ELEMENT;
int callbackResult = paContinue ;
VVDBUG(("AudioIOProc()\n"));
PaUtil_BeginCpuLoadMeasurement( &stream->cpuLoadMeasurer );
/* -----------------------------------------------------------------*\
This output may be useful for debugging,
But printing durring the callback is a bad enough idea that
this is not enabled by enableing the usual debugging calls.
\* -----------------------------------------------------------------*/
/*
static int renderCount = 0;
static int inputCount = 0;
printf( "------------------- starting reder/input\n" );
if( isRender )
printf("Render callback (%d):\t", ++renderCount);
else
printf("Input callback (%d):\t", ++inputCount);
printf( "Call totals: %d (input), %d (render)\n", inputCount, renderCount );
printf( "--- inBusNumber: %lu\n", inBusNumber );
printf( "--- inNumberFrames: %lu\n", inNumberFrames );
printf( "--- %x ioData\n", (unsigned) ioData );
if( ioData )
{
int i=0;
printf( "--- ioData.mNumBuffers %lu: \n", ioData->mNumberBuffers );
for( i=0; i<ioData->mNumberBuffers; ++i )
printf( "--- ioData buffer %d size: %lu.\n", i, ioData->mBuffers[i].mDataByteSize );
}
----------------------------------------------------------------- */
if( !stream->isTimeSet )
setStreamStartTime( stream );
if( isRender ) {
AudioTimeStamp currentTime;
timeInfo.outputBufferDacTime = TimeStampToSecs(stream, inTimeStamp);
AudioDeviceGetCurrentTime(stream->outputDevice, ¤tTime);
timeInfo.currentTime = TimeStampToSecs(stream, ¤tTime);
}
if( isRender && stream->inputUnit == stream->outputUnit )
timeInfo.inputBufferAdcTime = TimeStampToSecs(stream, inTimeStamp);
if( !isRender ) {
AudioTimeStamp currentTime;
timeInfo.inputBufferAdcTime = TimeStampToSecs(stream, inTimeStamp);
AudioDeviceGetCurrentTime(stream->inputDevice, ¤tTime);
timeInfo.currentTime = TimeStampToSecs(stream, ¤tTime);
}
//printf( "---%g, %g, %g\n", timeInfo.inputBufferAdcTime, timeInfo.currentTime, timeInfo.outputBufferDacTime );
if( isRender && stream->inputUnit == stream->outputUnit
&& !stream->inputSRConverter )
{
/* --------- Full Duplex, One Device, no SR Conversion -------
*
* This is the lowest latency case, and also the simplest.
* Input data and output data are available at the same time.
* we do not use the input SR converter or the input ring buffer.
*
*/
OSErr err = 0;
unsigned long frames;
/* -- start processing -- */
PaUtil_BeginBufferProcessing( &(stream->bufferProcessor),
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -