* Olivier Tristan for feedback and testing
* Glenn Zelniker and Z-Systems engineering for sponsoring the Blocking I/O
* interface.
- *
+ *
*
* Based on the Open Source API proposed by Ross Bencina
* Copyright (c) 1999-2002 Ross Bencina, Phil Burk
*/
/*
- * The text above constitutes the entire PortAudio license; however,
+ * The text above constitutes the entire PortAudio license; however,
* the PortAudio community also makes the following non-binding requests:
*
* Any person wishing to distribute modifications to the Software is
* requested to send the modifications to the original developer so that
- * they can be incorporated into the canonical version. It is also
- * requested that these non-binding requests be included along with the
+ * they can be incorporated into the canonical version. It is also
+ * requested that these non-binding requests be included along with the
* license above.
*/
extern "C"
{
#endif /* __cplusplus */
-
+
/* This is a reasonable size for a small buffer based on experience. */
#define PA_MAC_SMALL_BUFFER_SIZE (64)
-
+
/* prototypes for functions declared in this file */
PaError PaMacCore_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
PaMacAUHAL *macCoreHostApi = (PaMacAUHAL*)hostApi;
AudioDeviceID hostApiDevice = macCoreHostApi->devIds[device];
CFStringRef nameRef;
-
+
/* First try with CFString */
UInt32 size = sizeof(nameRef);
error = AudioDeviceGetProperty( hostApiDevice,
{
if( !ensureChannelNameSize( size ) )
return NULL;
-
+
error = AudioDeviceGetProperty( hostApiDevice,
channelIndex + 1,
input,
kAudioDevicePropertyChannelName,
&size,
channelName );
-
-
+
+
if( !error )
return channelName;
}
-
+
/* as a last-ditch effort, we use the device name and append the channel number. */
nameRef = CFStringCreateWithFormat( NULL, NULL, CFSTR( "%s: %d"), hostApi->deviceInfos[device]->name, channelIndex + 1 );
-
-
+
+
size = CFStringGetMaximumSizeForEncoding(CFStringGetLength(nameRef), kCFStringEncodingUTF8);;
if( !ensureChannelNameSize( size ) )
{
CFStringGetCString( nameRef, channelName, size+1, kCFStringEncodingUTF8 );
CFRelease( nameRef );
}
-
+
return channelName;
}
-
+
PaError PaMacCore_GetBufferSizeRange( PaDeviceIndex device,
long *minBufferSizeFrames, long *maxBufferSizeFrames )
{
PaError result;
PaUtilHostApiRepresentation *hostApi;
-
+
result = PaUtil_GetHostApiRepresentation( &hostApi, paCoreAudio );
-
+
if( result == paNoError )
{
PaDeviceIndex hostApiDeviceIndex;
AudioDeviceID macCoreDeviceId = macCoreHostApi->devIds[hostApiDeviceIndex];
AudioValueRange audioRange;
UInt32 propSize = sizeof( audioRange );
-
+
// return the size range for the output scope unless we only have inputs
Boolean isInput = 0;
if( macCoreHostApi->inheritedHostApiRep.deviceInfos[hostApiDeviceIndex]->maxOutputChannels == 0 )
isInput = 1;
-
+
result = WARNING(AudioDeviceGetProperty( macCoreDeviceId, 0, isInput, kAudioDevicePropertyBufferFrameSizeRange, &propSize, &audioRange ) );
*minBufferSizeFrames = audioRange.mMinimum;
*maxBufferSizeFrames = audioRange.mMaximum;
}
}
-
+
return result;
}
break;
}
}
- }
+ }
if( 0 != AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice,
&size,
&auhalHostApi->defaultOut) ) {
break;
}
}
- }
+ }
VDBUG( ( "Default in : %ld\n", auhalHostApi->defaultIn ) );
VDBUG( ( "Default out: %ld\n", auhalHostApi->defaultOut ) );
UInt32 bufferFrames;
UInt32 safetyOffset;
AudioStreamID streamIDs[128];
-
- printf("\n======= latency query : macCoreDeviceId = %d, isInput %d =======\n", (int)macCoreDeviceId, isInput );
-
+
+ printf("\n======= latency query : macCoreDeviceId = %d, isInput %d =======\n", (int)macCoreDeviceId, isInput );
+
propSize = sizeof(UInt32);
err = WARNING(AudioDeviceGetProperty(macCoreDeviceId, 0, isInput, kAudioDevicePropertyBufferFrameSize, &propSize, &bufferFrames));
printf("kAudioDevicePropertyBufferFrameSize: err = %d, propSize = %d, value = %d\n", err, propSize, bufferFrames );
-
+
propSize = sizeof(UInt32);
err = WARNING(AudioDeviceGetProperty(macCoreDeviceId, 0, isInput, kAudioDevicePropertySafetyOffset, &propSize, &safetyOffset));
printf("kAudioDevicePropertySafetyOffset: err = %d, propSize = %d, value = %d\n", err, propSize, safetyOffset );
-
+
propSize = sizeof(UInt32);
err = WARNING(AudioDeviceGetProperty(macCoreDeviceId, 0, isInput, kAudioDevicePropertyLatency, &propSize, &deviceLatency));
printf("kAudioDevicePropertyLatency: err = %d, propSize = %d, value = %d\n", err, propSize, deviceLatency );
-
+
AudioValueRange audioRange;
propSize = sizeof( audioRange );
err = WARNING(AudioDeviceGetProperty( macCoreDeviceId, 0, isInput, kAudioDevicePropertyBufferFrameSizeRange, &propSize, &audioRange ) );
printf("kAudioDevicePropertyBufferFrameSizeRange: err = %d, propSize = %u, minimum = %g\n", err, propSize, audioRange.mMinimum);
printf("kAudioDevicePropertyBufferFrameSizeRange: err = %d, propSize = %u, maximum = %g\n", err, propSize, audioRange.mMaximum );
-
+
/* Get the streams from the device and query their latency. */
propSize = sizeof(streamIDs);
err = WARNING(AudioDeviceGetProperty(macCoreDeviceId, 0, isInput, kAudioDevicePropertyStreams, &propSize, &streamIDs[0]));
for( i=0; i<numStreams; i++ )
{
printf("Stream #%d = %d---------------------- \n", i, streamIDs[i] );
-
+
propSize = sizeof(UInt32);
err = WARNING(AudioStreamGetProperty(streamIDs[i], 0, kAudioStreamPropertyLatency, &propSize, &streamLatency));
printf(" kAudioStreamPropertyLatency: err = %d, propSize = %d, value = %d\n", err, propSize, streamLatency );
UInt32 streamLatency;
UInt32 safetyOffset;
AudioStreamID streamIDs[1];
-
+
// To get stream latency we have to get a streamID from the device.
// We are only going to look at the first stream so only fetch one stream.
propSize = sizeof(streamIDs);
err = WARNING(AudioDeviceGetProperty(macCoreDeviceId, 0, isInput, kAudioDevicePropertyStreams, &propSize, &streamIDs[0]));
if( err != paNoError ) goto error;
if( propSize == sizeof(AudioStreamID) )
- {
+ {
propSize = sizeof(UInt32);
err = WARNING(AudioStreamGetProperty(streamIDs[0], 0, kAudioStreamPropertyLatency, &propSize, &streamLatency));
}
-
+
propSize = sizeof(UInt32);
err = WARNING(AudioDeviceGetProperty(macCoreDeviceId, 0, isInput, kAudioDevicePropertySafetyOffset, &propSize, &safetyOffset));
if( err != paNoError ) goto error;
-
+
propSize = sizeof(UInt32);
err = WARNING(AudioDeviceGetProperty(macCoreDeviceId, 0, isInput, kAudioDevicePropertyLatency, &propSize, &deviceLatency));
if( err != paNoError ) goto error;
UInt32 bufferFrames = 0;
UInt32 fixedLatency = 0;
UInt32 clippedMinBufferSize = 0;
-
+
//DumpDeviceProperties( macCoreDeviceId, isInput );
-
+
PaError err = CalculateFixedDeviceLatency( macCoreDeviceId, isInput, &fixedLatency );
if( err != paNoError ) goto error;
-
+
// For low latency use a small fixed size buffer clipped to the device range.
err = ClipToDeviceBufferSize( macCoreDeviceId, isInput, PA_MAC_SMALL_BUFFER_SIZE, &clippedMinBufferSize );
if( err != paNoError ) goto error;
-
+
// For high latency use the default device buffer size.
propSize = sizeof(UInt32);
err = WARNING(AudioDeviceGetProperty(macCoreDeviceId, 0, isInput, kAudioDevicePropertyBufferFrameSize, &propSize, &bufferFrames));
if( err != paNoError ) goto error;
-
+
*lowLatencyFramesPtr = fixedLatency + clippedMinBufferSize;
*highLatencyFramesPtr = fixedLatency + bufferFrames;
-
+
return err;
error:
return err;
UInt32 i;
int numChannels = 0;
AudioBufferList *buflist = NULL;
-
+
VVDBUG(("GetChannelInfo()\n"));
/* Get the number of channels from the stream configuration.
deviceInfo->maxInputChannels = numChannels;
else
deviceInfo->maxOutputChannels = numChannels;
-
+
if (numChannels > 0) /* do not try to retrieve the latency if there are no channels. */
{
/* Get the latency. Don't fail if we can't get this. */
deviceInfo->defaultLowInputLatency = .01;
deviceInfo->defaultHighInputLatency = .10;
deviceInfo->defaultLowOutputLatency = .01;
- deviceInfo->defaultHighOutputLatency = .10;
+ deviceInfo->defaultHighOutputLatency = .10;
UInt32 lowLatencyFrames = 0;
UInt32 highLatencyFrames = 0;
err = CalculateDefaultDeviceLatencies( macCoreDeviceId, isInput, &lowLatencyFrames, &highLatencyFrames );
if( err == 0 )
{
-
+
double lowLatencySeconds = lowLatencyFrames / deviceInfo->defaultSampleRate;
double highLatencySeconds = highLatencyFrames / deviceInfo->defaultSampleRate;
if (isInput)
deviceInfo->structVersion = 2;
deviceInfo->hostApi = hostApiIndex;
-
+
/* Get the device name using CFString */
propSize = sizeof(nameRef);
err = ERR(AudioDeviceGetProperty(macCoreDeviceId, 0, 0, kAudioDevicePropertyDeviceNameCFString, &propSize, &nameRef));
int unixErr;
VVDBUG(("PaMacCore_Initialize(): hostApiIndex=%d\n", hostApiIndex));
-
+
SInt32 major;
SInt32 minor;
Gestalt(gestaltSystemVersionMajor, &major);
Gestalt(gestaltSystemVersionMinor, &minor);
-
+
// Starting with 10.6 systems, the HAL notification thread is created internally
if (major == 10 && minor >= 6) {
CFRunLoopRef theRunLoop = NULL;
goto error;
}
}
-
+
unixErr = initializeXRunListenerList();
if( 0 != unixErr ) {
return UNIX_ERR(unixErr);
(*hostApi)->info.defaultInputDevice = paNoDevice;
(*hostApi)->info.defaultOutputDevice = paNoDevice;
- (*hostApi)->info.deviceCount = 0;
+ (*hostApi)->info.deviceCount = 0;
if( auhalHostApi->devCount > 0 )
{
PaUtil_FreeAllAllocations( auhalHostApi->allocations );
PaUtil_DestroyAllocationGroup( auhalHostApi->allocations );
}
-
+
PaUtil_FreeMemory( auhalHostApi );
}
return result;
outputParameters ? outputParameters->channelCount : -1,
outputParameters ? outputParameters->sampleFormat : -1,
(float) sampleRate ));
-
+
/** These first checks are standard PA checks. We do some fancier checks
later. */
if( inputParameters )
this implementation doesn't support any custom sample formats */
if( inputSampleFormat & paCustomFormat )
return paSampleFormatNotSupported;
-
+
/* unless alternate device specification is supported, reject the use of
paUseHostApiSpecificDeviceSpecification */
this implementation doesn't support any custom sample formats */
if( outputSampleFormat & paCustomFormat )
return paSampleFormatNotSupported;
-
+
/* unless alternate device specification is supported, reject the use of
paUseHostApiSpecificDeviceSpecification */
{
outputChannelCount = 0;
}
-
+
/* FEEDBACK */
/* I think the only way to check a given format SR combo is */
/* to try opening it. This could be disruptive, is that Okay? */
if( err != paNoError && err != paInvalidSampleRate )
DBUG( ( "OpenStream @ %g returned: %d: %s\n",
(float) sampleRate, err, Pa_GetErrorText( err ) ) );
- if( err )
+ if( err )
return err;
err = CloseStream( s );
if( err ) {
Float64 inputHardwareLatency = 0.0;
Float64 outputSoftwareLatency = 0.0;
Float64 outputHardwareLatency = 0.0;
-
+
if( stream->inputUnit != NULL )
{
inputSoftwareLatency = CalculateSoftwareLatencyFromProperties( stream, &stream->inputProperties );
inputHardwareLatency = CalculateHardwareLatencyFromProperties( stream, &stream->inputProperties );
- }
+ }
if( stream->outputUnit != NULL )
{
outputSoftwareLatency = CalculateSoftwareLatencyFromProperties( stream, &stream->outputProperties );
outputHardwareLatency = CalculateHardwareLatencyFromProperties( stream, &stream->outputProperties );
- }
-
+ }
+
/* We only need a mutex around setting these variables as a group. */
pthread_mutex_lock( &stream->timingInformationMutex );
stream->timestampOffsetCombined = inputSoftwareLatency + outputSoftwareLatency;
static OSStatus UpdateSampleRateFromDeviceProperty( PaMacCoreStream *stream, AudioDeviceID deviceID, Boolean isInput, AudioDevicePropertyID sampleRatePropertyID )
{
PaMacCoreDeviceProperties * deviceProperties = isInput ? &stream->inputProperties : &stream->outputProperties;
-
+
Float64 sampleRate = 0.0;
UInt32 propSize = sizeof(Float64);
OSStatus osErr = AudioDeviceGetProperty( deviceID, 0, isInput, sampleRatePropertyID, &propSize, &sampleRate);
static OSStatus AudioDevicePropertyActualSampleRateListenerProc( AudioDeviceID inDevice, UInt32 inChannel, Boolean isInput, AudioDevicePropertyID inPropertyID, void *inClientData )
{
PaMacCoreStream *stream = (PaMacCoreStream*)inClientData;
-
+
// Make sure the callback is operating on a stream that is still valid!
assert( stream->streamRepresentation.magic == PA_STREAM_MAGIC );
{
OSStatus osErr = noErr;
PaMacCoreStream *stream = (PaMacCoreStream*)inClientData;
-
+
// Make sure the callback is operating on a stream that is still valid!
assert( stream->streamRepresentation.magic == PA_STREAM_MAGIC );
-
+
PaMacCoreDeviceProperties *deviceProperties = isInput ? &stream->inputProperties : &stream->outputProperties;
UInt32 *valuePtr = NULL;
switch( inPropertyID )
case kAudioDevicePropertySafetyOffset:
valuePtr = &deviceProperties->safetyOffset;
break;
-
+
case kAudioDevicePropertyLatency:
valuePtr = &deviceProperties->deviceLatency;
break;
-
+
case kAudioDevicePropertyBufferFrameSize:
valuePtr = &deviceProperties->bufferFrameSize;
- break;
+ break;
}
if( valuePtr != NULL )
{
{
OSStatus osErr = noErr;
PaMacCoreDeviceProperties *deviceProperties = isInput ? &stream->inputProperties : &stream->outputProperties;
-
+
if( (osErr = QueryUInt32DeviceProperty( deviceID, isInput,
kAudioDevicePropertyLatency, &deviceProperties->deviceLatency )) != noErr ) return osErr;
if( (osErr = QueryUInt32DeviceProperty( deviceID, isInput,
kAudioDevicePropertyBufferFrameSize, &deviceProperties->bufferFrameSize )) != noErr ) return osErr;
if( (osErr = QueryUInt32DeviceProperty( deviceID, isInput,
kAudioDevicePropertySafetyOffset, &deviceProperties->safetyOffset )) != noErr ) return osErr;
-
- AudioDeviceAddPropertyListener( deviceID, 0, isInput, kAudioDevicePropertyActualSampleRate,
+
+ AudioDeviceAddPropertyListener( deviceID, 0, isInput, kAudioDevicePropertyActualSampleRate,
AudioDevicePropertyActualSampleRateListenerProc, stream );
-
- AudioDeviceAddPropertyListener( deviceID, 0, isInput, kAudioStreamPropertyLatency,
+
+ AudioDeviceAddPropertyListener( deviceID, 0, isInput, kAudioStreamPropertyLatency,
AudioDevicePropertyGenericListenerProc, stream );
- AudioDeviceAddPropertyListener( deviceID, 0, isInput, kAudioDevicePropertyBufferFrameSize,
+ AudioDeviceAddPropertyListener( deviceID, 0, isInput, kAudioDevicePropertyBufferFrameSize,
AudioDevicePropertyGenericListenerProc, stream );
- AudioDeviceAddPropertyListener( deviceID, 0, isInput, kAudioDevicePropertySafetyOffset,
+ AudioDeviceAddPropertyListener( deviceID, 0, isInput, kAudioDevicePropertySafetyOffset,
AudioDevicePropertyGenericListenerProc, stream );
-
+
return osErr;
}
static void CleanupDevicePropertyListeners( PaMacCoreStream *stream, AudioDeviceID deviceID, Boolean isInput )
-{
- AudioDeviceRemovePropertyListener( deviceID, 0, isInput, kAudioDevicePropertyActualSampleRate,
+{
+ AudioDeviceRemovePropertyListener( deviceID, 0, isInput, kAudioDevicePropertyActualSampleRate,
AudioDevicePropertyActualSampleRateListenerProc );
-
- AudioDeviceRemovePropertyListener( deviceID, 0, isInput, kAudioDevicePropertyLatency,
- AudioDevicePropertyGenericListenerProc );
- AudioDeviceRemovePropertyListener( deviceID, 0, isInput, kAudioDevicePropertyBufferFrameSize,
+
+ AudioDeviceRemovePropertyListener( deviceID, 0, isInput, kAudioDevicePropertyLatency,
+ AudioDevicePropertyGenericListenerProc );
+ AudioDeviceRemovePropertyListener( deviceID, 0, isInput, kAudioDevicePropertyBufferFrameSize,
AudioDevicePropertyGenericListenerProc );
- AudioDeviceRemovePropertyListener( deviceID, 0, isInput, kAudioDevicePropertySafetyOffset,
+ AudioDeviceRemovePropertyListener( deviceID, 0, isInput, kAudioDevicePropertySafetyOffset,
AudioDevicePropertyGenericListenerProc );
}
const double sampleRate,
void *refCon )
{
- ComponentDescription desc;
- Component comp;
+ AudioComponentDescription desc;
+ AudioComponent comp;
/*An Apple TN suggests using CAStreamBasicDescription, but that is C++*/
AudioStreamBasicDescription desiredFormat;
OSStatus result = noErr;
outChannelMap = ((PaMacCoreStreamInfo*)outStreamParams->hostApiSpecificStreamInfo)
->channelMap;
outChannelMapSize = ((PaMacCoreStreamInfo*)outStreamParams->hostApiSpecificStreamInfo)
- ->channelMapSize;
+ ->channelMapSize;
}
/* Override user's flags here, if desired for testing. */
desc.componentFlags = 0;
desc.componentFlagsMask = 0;
/* -- find the component -- */
- comp = FindNextComponent( NULL, &desc );
+ comp = AudioComponentFindNext( NULL, &desc );
if( !comp )
{
DBUG( ( "AUHAL component not found." ) );
return paUnanticipatedHostError;
}
/* -- open it -- */
- result = OpenAComponent( comp, audioUnit );
+ result = AudioComponentInstanceNew( comp, audioUnit );
if( result )
{
DBUG( ( "Failed to open AUHAL component." ) );
(float)sourceFormat.mSampleRate,
(float)desiredFormat.mSampleRate ) );
/* create our converter */
- ERR_WRAP( AudioConverterNew(
+ ERR_WRAP( AudioConverterNew(
&sourceFormat,
&desiredFormat,
srConverter ) );
#undef ERR_WRAP
error:
- CloseComponent( *audioUnit );
+ AudioComponentInstanceDispose( *audioUnit );
*audioUnit = NULL;
if( result )
return PaMacCore_SetError( result, line, 1 );
double sampleRate,
UInt32 requestedFramesPerBuffer )
{
- UInt32 resultBufferSizeFrames = 0;
+ UInt32 resultBufferSizeFrames = 0;
// Use maximum of suggested input and output latencies.
if( inputParameters )
{
// Calculate a buffer size assuming we are double buffered.
SInt32 variableLatencyFrames = suggestedLatencyFrames - fixedInputLatency;
// Prevent negative latency.
- variableLatencyFrames = MAX( variableLatencyFrames, 0 );
+ variableLatencyFrames = MAX( variableLatencyFrames, 0 );
resultBufferSizeFrames = MAX( resultBufferSizeFrames, (UInt32) variableLatencyFrames );
}
if( outputParameters )
- {
+ {
UInt32 suggestedLatencyFrames = outputParameters->suggestedLatency * sampleRate;
SInt32 variableLatencyFrames = suggestedLatencyFrames - fixedOutputLatency;
variableLatencyFrames = MAX( variableLatencyFrames, 0 );
resultBufferSizeFrames = MAX( resultBufferSizeFrames, (UInt32) variableLatencyFrames );
}
-
+
// can't have zero frames. code to round up to next user buffer requires non-zero
resultBufferSizeFrames = MAX( resultBufferSizeFrames, 1 );
-
+
if( requestedFramesPerBuffer != paFramesPerBufferUnspecified )
{
// make host buffer the next highest integer multiple of user frames per buffer
UInt32 n = (resultBufferSizeFrames + requestedFramesPerBuffer - 1) / requestedFramesPerBuffer;
resultBufferSizeFrames = n * requestedFramesPerBuffer;
-
+
// FIXME: really we should be searching for a multiple of requestedFramesPerBuffer
// that is >= suggested latency and also fits within device buffer min/max
-
+
}else{
VDBUG( ("Block Size unspecified. Based on Latency, the user wants a Block Size near: %ld.\n",
resultBufferSizeFrames ) );
}
-
+
// Clip to the capabilities of the device.
if( inputParameters )
{
UInt32 inputLatencyFrames = 0;
UInt32 outputLatencyFrames = 0;
UInt32 suggestedLatencyFramesPerBuffer = requestedFramesPerBuffer;
-
+
VVDBUG(("OpenStream(): in chan=%d, in fmt=%ld, out chan=%d, out fmt=%ld SR=%g, FPB=%ld\n",
inputParameters ? inputParameters->channelCount : -1,
inputParameters ? inputParameters->sampleFormat : -1,
(float) sampleRate,
requestedFramesPerBuffer ));
VDBUG( ("Opening Stream.\n") );
-
+
/* These first few bits of code are from paSkeleton with few modifications. */
if( inputParameters )
{
{
return paSampleFormatNotSupported;
}
-
+
/* unless alternate device specification is supported, reject the use of
paUseHostApiSpecificDeviceSpecification */
{
outputChannelCount = outputParameters->channelCount;
outputSampleFormat = outputParameters->sampleFormat;
-
+
/* @todo Blocking read/write on Mac is not yet supported. */
if( !streamCallback && outputSampleFormat & paNonInterleaved )
{
return paSampleFormatNotSupported;
}
-
+
/* unless alternate device specification is supported, reject the use of
paUseHostApiSpecificDeviceSpecification */
been touched.
*/
bzero( stream, sizeof( PaMacCoreStream ) );
-
+
/*
stream->blio.inputRingBuffer.buffer = NULL;
stream->blio.outputRingBuffer.buffer = NULL;
PaUtil_InitializeCpuLoadMeasurer( &stream->cpuLoadMeasurer, sampleRate );
-
+
if( inputParameters )
{
CalculateFixedDeviceLatency( auhalHostApi->devIds[inputParameters->device], true, &fixedInputLatency );
inputLatencyFrames += fixedInputLatency;
}
if( outputParameters )
- {
+ {
CalculateFixedDeviceLatency( auhalHostApi->devIds[outputParameters->device], false, &fixedOutputLatency );
outputLatencyFrames += fixedOutputLatency;
}
-
+
suggestedLatencyFramesPerBuffer = CalculateOptimalBufferSize( auhalHostApi, inputParameters, outputParameters,
fixedInputLatency, fixedOutputLatency,
sampleRate, requestedFramesPerBuffer );
stream->inputFramesPerBuffer = inputFramesPerBuffer;
stream->outputFramesPerBuffer = outputFramesPerBuffer;
}
-
+
inputLatencyFrames += stream->inputFramesPerBuffer;
outputLatencyFrames += stream->outputFramesPerBuffer;
-
+
if( stream->inputUnit ) {
const size_t szfl = sizeof(float);
/* setup the AudioBufferList used for input */
result = paInsufficientMemory;
goto error;
}
-
+
/*
* If input and output devs are different or we are doing SR conversion,
* we also need a ring buffer to store input data while waiting for
middle of the buffer */
if( stream->outputUnit )
PaUtil_AdvanceRingBufferWriteIndex( &stream->inputRingBuffer, ringSize / RING_BUFFER_ADVANCE_DENOMINATOR );
-
+
// Just adds to input latency between input device and PA full duplex callback.
inputLatencyFrames += ringSize;
}
outputParameters ? outputChannelCount : 0 ) ;
if( result != paNoError )
goto error;
-
+
inputLatencyFrames += ringSize;
outputLatencyFrames += ringSize;
-
+
}
/* -- initialize Buffer Processor -- */
stream->bufferProcessorIsInitialized = TRUE;
// Calculate actual latency from the sum of individual latencies.
- if( inputParameters )
+ if( inputParameters )
{
inputLatencyFrames += PaUtil_GetBufferProcessorInputLatencyFrames(&stream->bufferProcessor);
stream->streamRepresentation.streamInfo.inputLatency = inputLatencyFrames / sampleRate;
{
stream->streamRepresentation.streamInfo.inputLatency = 0.0;
}
-
- if( outputParameters )
+
+ if( outputParameters )
{
outputLatencyFrames += PaUtil_GetBufferProcessorOutputLatencyFrames(&stream->bufferProcessor);
stream->streamRepresentation.streamInfo.outputLatency = outputLatencyFrames / sampleRate;
{
stream->streamRepresentation.streamInfo.outputLatency = 0.0;
}
-
+
stream->streamRepresentation.streamInfo.sampleRate = sampleRate;
stream->sampleRate = sampleRate;
-
+
stream->userInChan = inputChannelCount;
stream->userOutChan = outputChannelCount;
if( stream->outputUnit )
{
Boolean isInput = FALSE;
-
+
// Start with the current values for the device properties.
// Init with nominal sample rate. Use actual sample rate where available
-
- result = ERR( UpdateSampleRateFromDeviceProperty(
+
+ result = ERR( UpdateSampleRateFromDeviceProperty(
stream, stream->outputDevice, isInput, kAudioDevicePropertyNominalSampleRate ) );
if( result )
goto error; /* fail if we can't even get a nominal device sample rate */
-
+
UpdateSampleRateFromDeviceProperty( stream, stream->outputDevice, isInput, kAudioDevicePropertyActualSampleRate );
-
+
SetupDevicePropertyListeners( stream, stream->outputDevice, isInput );
}
if( stream->inputUnit )
{
Boolean isInput = TRUE;
-
+
// as above
- result = ERR( UpdateSampleRateFromDeviceProperty(
+ result = ERR( UpdateSampleRateFromDeviceProperty(
stream, stream->inputDevice, isInput, kAudioDevicePropertyNominalSampleRate ) );
if( result )
goto error;
-
+
UpdateSampleRateFromDeviceProperty( stream, stream->inputDevice, isInput, kAudioDevicePropertyActualSampleRate );
-
+
SetupDevicePropertyListeners( stream, stream->inputDevice, isInput );
}
UpdateTimeStampOffsets( stream );
PaTime GetStreamTime( PaStream *s )
{
- return HOST_TIME_TO_PA_TIME( AudioGetCurrentHostTime() );
+ return HOST_TIME_TO_PA_TIME( AudioGetCurrentHostTime() );
}
#define RING_BUFFER_EMPTY (1000)
-static OSStatus ringBufferIOProc( AudioConverterRef inAudioConverter,
- UInt32*ioDataSize,
- void** outData,
+static OSStatus ringBufferIOProc( AudioConverterRef inAudioConverter,
+ UInt32*ioDataSize,
+ void** outData,
void*inUserData )
{
void *dummyData;
assert( ( (*ioDataSize) / rb->elementSizeBytes ) * rb->elementSizeBytes == (*ioDataSize) ) ;
(*ioDataSize) /= rb->elementSizeBytes ;
PaUtil_GetRingBufferReadRegions( rb, *ioDataSize,
- outData, (ring_buffer_size_t *)ioDataSize,
+ outData, (ring_buffer_size_t *)ioDataSize,
&dummyData, &dummySize );
assert( *ioDataSize );
PaUtil_AdvanceRingBufferReadIndex( rb, *ioDataSize );
const bool isRender = inBusNumber == OUTPUT_ELEMENT;
int callbackResult = paContinue ;
double hostTimeStampInPaTime = HOST_TIME_TO_PA_TIME(inTimeStamp->mHostTime);
-
+
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
----------------------------------------------------------------- */
/* compute PaStreamCallbackTimeInfo */
-
+
if( pthread_mutex_trylock( &stream->timingInformationMutex ) == 0 ){
/* snapshot the ioproc copy of timing information */
stream->timestampOffsetCombined_ioProcCopy = stream->timestampOffsetCombined;
stream->timestampOffsetOutputDevice_ioProcCopy = stream->timestampOffsetOutputDevice;
pthread_mutex_unlock( &stream->timingInformationMutex );
}
-
- /* For timeInfo.currentTime we could calculate current time backwards from the HAL audio
- output time to give a more accurate impression of the current timeslice but it doesn't
+
+ /* For timeInfo.currentTime we could calculate current time backwards from the HAL audio
+ output time to give a more accurate impression of the current timeslice but it doesn't
seem worth it at the moment since other PA host APIs don't do any better.
*/
timeInfo.currentTime = HOST_TIME_TO_PA_TIME( AudioGetCurrentHostTime() );
-
+
/*
For an input HAL AU, inTimeStamp is the time the samples are received from the hardware,
- for an output HAL AU inTimeStamp is the time the samples are sent to the hardware.
+ for an output HAL AU inTimeStamp is the time the samples are sent to the hardware.
PA expresses timestamps in terms of when the samples enter the ADC or leave the DAC
so we add or subtract kAudioDevicePropertyLatency below.
*/
-
+
/* FIXME: not sure what to do below if the host timestamps aren't valid (kAudioTimeStampHostTimeValid isn't set)
Could ask on CA mailing list if it is possible for it not to be set. If so, could probably grab a now timestamp
at the top and compute from there (modulo scheduling jitter) or ask on mailing list for other options. */
-
+
if( isRender )
{
if( stream->inputUnit ) /* full duplex */
// Ross and Phil agreed that the following calculation is correct based on an email from Jeff Moore:
// http://osdir.com/ml/coreaudio-api/2009-07/msg00140.html
// Basically the difference between the Apple output timestamp and the PA timestamp is kAudioDevicePropertyLatency.
- timeInfo.inputBufferAdcTime = hostTimeStampInPaTime -
+ timeInfo.inputBufferAdcTime = hostTimeStampInPaTime -
(stream->timestampOffsetCombined_ioProcCopy + stream->timestampOffsetInputDevice_ioProcCopy);
timeInfo.outputBufferDacTime = hostTimeStampInPaTime + stream->timestampOffsetOutputDevice_ioProcCopy;
}
else /* full duplex with ring-buffer from a separate input AUHAL ioproc */
{
/* FIXME: take the ring buffer latency into account */
- timeInfo.inputBufferAdcTime = hostTimeStampInPaTime -
+ timeInfo.inputBufferAdcTime = hostTimeStampInPaTime -
(stream->timestampOffsetCombined_ioProcCopy + stream->timestampOffsetInputDevice_ioProcCopy);
timeInfo.outputBufferDacTime = hostTimeStampInPaTime + stream->timestampOffsetOutputDevice_ioProcCopy;
}
}
else /* input only */
{
- timeInfo.inputBufferAdcTime = hostTimeStampInPaTime - stream->timestampOffsetInputDevice_ioProcCopy;
+ timeInfo.inputBufferAdcTime = hostTimeStampInPaTime - stream->timestampOffsetInputDevice_ioProcCopy;
timeInfo.outputBufferDacTime = 0;
}
-
+
//printf( "---%g, %g, %g\n", timeInfo.inputBufferAdcTime, timeInfo.currentTime, timeInfo.outputBufferDacTime );
if( isRender && stream->inputUnit == stream->outputUnit
* -- OR Simplex Output
*
* This case handles output data as in the full duplex case,
- * and, if there is input data, reads it off the ring buffer
+ * and, if there is input data, reads it off the ring buffer
* and into the PA buffer processor. If sample rate conversion
* is required on input, that is done here as well.
*/
audio converter. */
int inChan = stream->inputAudioBufferList.mBuffers[0].mNumberChannels;
long bytesPerFrame = flsz * inChan;
-
+
if( stream->inputSRConverter )
{
OSStatus err;
UInt32 size;
float data[ inChan * frames ];
size = sizeof( data );
- err = AudioConverterFillBuffer(
+ err = AudioConverterFillBuffer(
stream->inputSRConverter,
ringBufferIOProc,
&stream->inputRingBuffer,
&callbackResult );
PaUtil_AdvanceRingBufferReadIndex(&stream->inputRingBuffer, size1 );
} else if( framesReadable < frames ) {
-
+
long sizeBytes1 = size1 * bytesPerFrame;
long sizeBytes2 = size2 * bytesPerFrame;
/*we underflowed. take what data we can, zero the rest.*/
unsigned char data[ frames * bytesPerFrame ];
if( size1 > 0 )
- {
+ {
memcpy( data, data1, sizeBytes1 );
}
if( size2 > 0 )
long f;
size = sizeof( data );
- err = AudioConverterFillBuffer(
+ err = AudioConverterFillBuffer(
stream->inputSRConverter,
ringBufferIOProc,
&stream->inputRingBuffer,
VDBUG( ( "Closing stream.\n" ) );
if( stream ) {
-
+
if( stream->outputUnit )
{
Boolean isInput = FALSE;
CleanupDevicePropertyListeners( stream, stream->outputDevice, isInput );
}
-
+
if( stream->inputUnit )
{
Boolean isInput = TRUE;
CleanupDevicePropertyListeners( stream, stream->inputDevice, isInput );
}
-
+
if( stream->outputUnit ) {
int count = removeFromXRunListenerList( stream );
if( count == 0 )
}
if( stream->outputUnit && stream->outputUnit != stream->inputUnit ) {
AudioUnitUninitialize( stream->outputUnit );
- CloseComponent( stream->outputUnit );
+ AudioComponentInstanceDispose( stream->outputUnit );
}
stream->outputUnit = NULL;
if( stream->inputUnit )
{
AudioUnitUninitialize( stream->inputUnit );
- CloseComponent( stream->inputUnit );
+ AudioComponentInstanceDispose( stream->inputUnit );
stream->inputUnit = NULL;
}
if( stream->inputRingBuffer.buffer )
return result;
if( stream->bufferProcessorIsInitialized )
PaUtil_TerminateBufferProcessor( &stream->bufferProcessor );
-
+
if( stream->timingInformationMutexIsInitialized )
pthread_mutex_destroy( &stream->timingInformationMutex );
if( stream->outputUnit && stream->outputUnit != stream->inputUnit ) {
ERR_WRAP( AudioOutputUnitStart(stream->outputUnit) );
}
-
+
return paNoError;
#undef ERR_WRAP
}
// it's not clear from appl's docs that this really waits
// until all data is flushed.
-static ComponentResult BlockWhileAudioUnitIsRunning( AudioUnit audioUnit, AudioUnitElement element )
+static OSStatus BlockWhileAudioUnitIsRunning( AudioUnit audioUnit, AudioUnitElement element )
{
Boolean isRunning = 1;
while( isRunning ) {
UInt32 s = sizeof( isRunning );
- ComponentResult err = AudioUnitGetProperty( audioUnit, kAudioOutputUnitProperty_IsRunning, kAudioUnitScope_Global, element, &isRunning, &s );
+ OSStatus err = AudioUnitGetProperty( audioUnit, kAudioOutputUnitProperty_IsRunning, kAudioUnitScope_Global, element, &isRunning, &s );
if( err )
return err;
Pa_Sleep( 100 );