1 /*
2  * Implementation of the PortAudio API for Apple AUHAL
3  *
4  * PortAudio Portable Real-Time Audio Library
5  * Latest Version at: http://www.portaudio.com
6  *
7  * Written by Bjorn Roche of XO Audio LLC, from PA skeleton code.
8  * Portions copied from code by Dominic Mazzoni (who wrote a HAL implementation)
9  *
10  * Dominic's code was based on code by Phil Burk, Darren Gibbs,
11  * Gord Peters, Stephane Letz, and Greg Pfiel.
12  *
13  * The following people also deserve acknowledgements:
14  *
15  * Olivier Tristan for feedback and testing
16  * Glenn Zelniker and Z-Systems engineering for sponsoring the Blocking I/O
17  * interface.
18  *
19  *
20  * Based on the Open Source API proposed by Ross Bencina
21  * Copyright (c) 1999-2002 Ross Bencina, Phil Burk
22  *
23  * Permission is hereby granted, free of charge, to any person obtaining
24  * a copy of this software and associated documentation files
25  * (the "Software"), to deal in the Software without restriction,
26  * including without limitation the rights to use, copy, modify, merge,
27  * publish, distribute, sublicense, and/or sell copies of the Software,
28  * and to permit persons to whom the Software is furnished to do so,
29  * subject to the following conditions:
30  *
31  * The above copyright notice and this permission notice shall be
32  * included in all copies or substantial portions of the Software.
33  *
34  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
35  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
36  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
37  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
38  * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
39  * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
40  * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
41  */
42 
43 /*
44  * The text above constitutes the entire PortAudio license; however,
45  * the PortAudio community also makes the following non-binding requests:
46  *
47  * Any person wishing to distribute modifications to the Software is
48  * requested to send the modifications to the original developer so that
49  * they can be incorporated into the canonical version. It is also
50  * requested that these non-binding requests be included along with the
51  * license above.
52  */
53 
54 /**
55  @file pa_mac_core
56  @ingroup hostapi_src
57  @author Bjorn Roche
58  @brief AUHAL implementation of PortAudio
59 */
60 
61 /* FIXME: not all error conditions call PaUtil_SetLastHostErrorInfo()
62  * PaMacCore_SetError() will do this.
63  */
64 
65 #include "pa_mac_core_internal.h"
66 
67 #include <string.h> /* strlen(), memcmp() etc. */
68 #include <libkern/OSAtomic.h>
69 
70 #include "pa_mac_core.h"
71 #include "pa_mac_core_utilities.h"
72 #include "pa_mac_core_blocking.h"
73 
74 
75 #ifdef __cplusplus
76 extern "C"
77 {
78 #endif /* __cplusplus */
79 
80 /* This is a reasonable size for a small buffer based on experience. */
81 #define PA_MAC_SMALL_BUFFER_SIZE    (64)
82 
83 /* prototypes for functions declared in this file */
84 PaError PaMacCore_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
85 
86 /*
87  * Function declared in pa_mac_core.h. Sets up a PaMacCoreStreamInfoStruct
88  * with the requested flags and initializes channel map.
89  */
PaMacCore_SetupStreamInfo(PaMacCoreStreamInfo * data,const unsigned long flags)90 void PaMacCore_SetupStreamInfo(  PaMacCoreStreamInfo *data, const unsigned long flags )
91 {
92    bzero( data, sizeof( PaMacCoreStreamInfo ) );
93    data->size = sizeof( PaMacCoreStreamInfo );
94    data->hostApiType = paCoreAudio;
95    data->version = 0x01;
96    data->flags = flags;
97    data->channelMap = NULL;
98    data->channelMapSize = 0;
99 }
100 
101 /*
102  * Function declared in pa_mac_core.h. Adds channel mapping to a PaMacCoreStreamInfoStruct
103  */
PaMacCore_SetupChannelMap(PaMacCoreStreamInfo * data,const SInt32 * const channelMap,const unsigned long channelMapSize)104 void PaMacCore_SetupChannelMap( PaMacCoreStreamInfo *data, const SInt32 * const channelMap, const unsigned long channelMapSize )
105 {
106    data->channelMap = channelMap;
107    data->channelMapSize = channelMapSize;
108 }
109 static char *channelName = NULL;
110 static int channelNameSize = 0;
ensureChannelNameSize(int size)111 static bool ensureChannelNameSize( int size )
112 {
113    if( size >= channelNameSize ) {
114       free( channelName );
115       channelName = (char *) malloc( ( channelNameSize = size ) + 1 );
116       if( !channelName ) {
117          channelNameSize = 0;
118          return false;
119       }
120    }
121    return true;
122 }
123 /*
124  * Function declared in pa_mac_core.h. retrives channel names.
125  */
PaMacCore_GetChannelName(int device,int channelIndex,bool input)126 const char *PaMacCore_GetChannelName( int device, int channelIndex, bool input )
127 {
128 	struct PaUtilHostApiRepresentation *hostApi;
129 	PaError err;
130 	OSStatus error;
131 	err = PaUtil_GetHostApiRepresentation( &hostApi, paCoreAudio );
132 	assert(err == paNoError);
133 	if( err != paNoError )
134 		return NULL;
135 	PaMacAUHAL *macCoreHostApi = (PaMacAUHAL*)hostApi;
136 	AudioDeviceID hostApiDevice = macCoreHostApi->devIds[device];
137 	CFStringRef nameRef;
138 
139 	/* First try with CFString */
140 	UInt32 size = sizeof(nameRef);
141 	error = AudioDeviceGetProperty( hostApiDevice,
142 								   channelIndex + 1,
143 								   input,
144 								   kAudioDevicePropertyChannelNameCFString,
145 								   &size,
146 								   &nameRef );
147 	if( error )
148 	{
149 		/* try the C String */
150 		size = 0;
151 		error = AudioDeviceGetPropertyInfo( hostApiDevice,
152 										   channelIndex + 1,
153 										   input,
154 										   kAudioDevicePropertyChannelName,
155 										   &size,
156 										   NULL);
157 		if( !error )
158 		{
159 			if( !ensureChannelNameSize( size ) )
160 				return NULL;
161 
162 			error = AudioDeviceGetProperty( hostApiDevice,
163 										   channelIndex + 1,
164 										   input,
165 										   kAudioDevicePropertyChannelName,
166 										   &size,
167 										   channelName );
168 
169 
170 			if( !error )
171 				return channelName;
172 		}
173 
174 		/* as a last-ditch effort, we use the device name and append the channel number. */
175 		nameRef = CFStringCreateWithFormat( NULL, NULL, CFSTR( "%s: %d"), hostApi->deviceInfos[device]->name, channelIndex + 1 );
176 
177 
178 		size = CFStringGetMaximumSizeForEncoding(CFStringGetLength(nameRef), kCFStringEncodingUTF8);;
179 		if( !ensureChannelNameSize( size ) )
180 		{
181 			CFRelease( nameRef );
182 			return NULL;
183 		}
184 		CFStringGetCString( nameRef, channelName, size+1, kCFStringEncodingUTF8 );
185 		CFRelease( nameRef );
186 	}
187 	else
188 	{
189 		size = CFStringGetMaximumSizeForEncoding(CFStringGetLength(nameRef), kCFStringEncodingUTF8);;
190 		if( !ensureChannelNameSize( size ) )
191 		{
192 			CFRelease( nameRef );
193 			return NULL;
194 		}
195 		CFStringGetCString( nameRef, channelName, size+1, kCFStringEncodingUTF8 );
196 		CFRelease( nameRef );
197 	}
198 
199 	return channelName;
200 }
201 
202 
PaMacCore_GetBufferSizeRange(PaDeviceIndex device,long * minBufferSizeFrames,long * maxBufferSizeFrames)203 PaError PaMacCore_GetBufferSizeRange( PaDeviceIndex device,
204                                       long *minBufferSizeFrames, long *maxBufferSizeFrames )
205 {
206     PaError result;
207     PaUtilHostApiRepresentation *hostApi;
208 
209     result = PaUtil_GetHostApiRepresentation( &hostApi, paCoreAudio );
210 
211     if( result == paNoError )
212     {
213         PaDeviceIndex hostApiDeviceIndex;
214         result = PaUtil_DeviceIndexToHostApiDeviceIndex( &hostApiDeviceIndex, device, hostApi );
215         if( result == paNoError )
216         {
217             PaMacAUHAL *macCoreHostApi = (PaMacAUHAL*)hostApi;
218             AudioDeviceID macCoreDeviceId = macCoreHostApi->devIds[hostApiDeviceIndex];
219             AudioValueRange audioRange;
220             UInt32 propSize = sizeof( audioRange );
221 
222             // return the size range for the output scope unless we only have inputs
223             Boolean isInput = 0;
224             if( macCoreHostApi->inheritedHostApiRep.deviceInfos[hostApiDeviceIndex]->maxOutputChannels == 0 )
225                 isInput = 1;
226 
227             result = WARNING(AudioDeviceGetProperty( macCoreDeviceId, 0, isInput, kAudioDevicePropertyBufferFrameSizeRange, &propSize, &audioRange ) );
228 
229             *minBufferSizeFrames = audioRange.mMinimum;
230             *maxBufferSizeFrames = audioRange.mMaximum;
231         }
232     }
233 
234     return result;
235 }
236 
237 
PaMacCore_GetStreamInputDevice(PaStream * s)238 AudioDeviceID PaMacCore_GetStreamInputDevice( PaStream* s )
239 {
240     PaMacCoreStream *stream = (PaMacCoreStream*)s;
241     VVDBUG(("PaMacCore_GetStreamInputHandle()\n"));
242 
243     return ( stream->inputDevice );
244 }
245 
PaMacCore_GetStreamOutputDevice(PaStream * s)246 AudioDeviceID PaMacCore_GetStreamOutputDevice( PaStream* s )
247 {
248     PaMacCoreStream *stream = (PaMacCoreStream*)s;
249     VVDBUG(("PaMacCore_GetStreamOutputHandle()\n"));
250 
251     return ( stream->outputDevice );
252 }
253 
254 #ifdef __cplusplus
255 }
256 #endif /* __cplusplus */
257 
258 #define RING_BUFFER_ADVANCE_DENOMINATOR (4)
259 
260 static void Terminate( struct PaUtilHostApiRepresentation *hostApi );
261 static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
262                                   const PaStreamParameters *inputParameters,
263                                   const PaStreamParameters *outputParameters,
264                                   double sampleRate );
265 static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
266                            PaStream** s,
267                            const PaStreamParameters *inputParameters,
268                            const PaStreamParameters *outputParameters,
269                            double sampleRate,
270                            unsigned long framesPerBuffer,
271                            PaStreamFlags streamFlags,
272                            PaStreamCallback *streamCallback,
273                            void *userData );
274 static PaError CloseStream( PaStream* stream );
275 static PaError StartStream( PaStream *stream );
276 static PaError StopStream( PaStream *stream );
277 static PaError AbortStream( PaStream *stream );
278 static PaError IsStreamStopped( PaStream *s );
279 static PaError IsStreamActive( PaStream *stream );
280 static PaTime GetStreamTime( PaStream *stream );
281 static OSStatus AudioIOProc( void *inRefCon,
282                                AudioUnitRenderActionFlags *ioActionFlags,
283                                const AudioTimeStamp *inTimeStamp,
284                                UInt32 inBusNumber,
285                                UInt32 inNumberFrames,
286                                AudioBufferList *ioData );
287 static double GetStreamCpuLoad( PaStream* stream );
288 
289 static PaError GetChannelInfo( PaMacAUHAL *auhalHostApi,
290                                PaDeviceInfo *deviceInfo,
291                                AudioDeviceID macCoreDeviceId,
292                                int isInput);
293 
294 static PaError OpenAndSetupOneAudioUnit(
295                                    const PaMacCoreStream *stream,
296                                    const PaStreamParameters *inStreamParams,
297                                    const PaStreamParameters *outStreamParams,
298                                    const UInt32 requestedFramesPerBuffer,
299                                    UInt32 *actualInputFramesPerBuffer,
300                                    UInt32 *actualOutputFramesPerBuffer,
301                                    const PaMacAUHAL *auhalHostApi,
302                                    AudioUnit *audioUnit,
303                                    AudioConverterRef *srConverter,
304                                    AudioDeviceID *audioDevice,
305                                    const double sampleRate,
306                                    void *refCon );
307 
308 /* for setting errors. */
309 #define PA_AUHAL_SET_LAST_HOST_ERROR( errorCode, errorText ) \
310     PaUtil_SetLastHostErrorInfo( paCoreAudio, errorCode, errorText )
311 
312 /*
313  * Callback called when starting or stopping a stream.
314  */
startStopCallback(void * inRefCon,AudioUnit ci,AudioUnitPropertyID inID,AudioUnitScope inScope,AudioUnitElement inElement)315 static void startStopCallback(
316    void *               inRefCon,
317    AudioUnit            ci,
318    AudioUnitPropertyID  inID,
319    AudioUnitScope       inScope,
320    AudioUnitElement     inElement )
321 {
322    PaMacCoreStream *stream = (PaMacCoreStream *) inRefCon;
323    UInt32 isRunning;
324    UInt32 size = sizeof( isRunning );
325    OSStatus err;
326    err = AudioUnitGetProperty( ci, kAudioOutputUnitProperty_IsRunning, inScope, inElement, &isRunning, &size );
327    assert( !err );
328    if( err )
329       isRunning = false; //it's very unclear what to do in case of error here. There's no real way to notify the user, and crashing seems unreasonable.
330    if( isRunning )
331       return; //We are only interested in when we are stopping
332    // -- if we are using 2 I/O units, we only need one notification!
333    if( stream->inputUnit && stream->outputUnit && stream->inputUnit != stream->outputUnit && ci == stream->inputUnit )
334       return;
335    PaStreamFinishedCallback *sfc = stream->streamRepresentation.streamFinishedCallback;
336    if( stream->state == STOPPING )
337       stream->state = STOPPED ;
338    if( sfc )
339       sfc( stream->streamRepresentation.userData );
340 }
341 
342 
343 /*currently, this is only used in initialization, but it might be modified
344   to be used when the list of devices changes.*/
gatherDeviceInfo(PaMacAUHAL * auhalHostApi)345 static PaError gatherDeviceInfo(PaMacAUHAL *auhalHostApi)
346 {
347     UInt32 size;
348     UInt32 propsize;
349     VVDBUG(("gatherDeviceInfo()\n"));
350     /* -- free any previous allocations -- */
351     if( auhalHostApi->devIds )
352         PaUtil_GroupFreeMemory(auhalHostApi->allocations, auhalHostApi->devIds);
353     auhalHostApi->devIds = NULL;
354 
355     /* -- figure out how many devices there are -- */
356     AudioHardwareGetPropertyInfo( kAudioHardwarePropertyDevices,
357                                   &propsize,
358                                   NULL );
359     auhalHostApi->devCount = propsize / sizeof( AudioDeviceID );
360 
361     VDBUG( ( "Found %ld device(s).\n", auhalHostApi->devCount ) );
362 
363     /* -- copy the device IDs -- */
364     auhalHostApi->devIds = (AudioDeviceID *)PaUtil_GroupAllocateMemory(
365                              auhalHostApi->allocations,
366                              propsize );
367     if( !auhalHostApi->devIds )
368         return paInsufficientMemory;
369     AudioHardwareGetProperty( kAudioHardwarePropertyDevices,
370                                   &propsize,
371                                   auhalHostApi->devIds );
372 #ifdef MAC_CORE_VERBOSE_DEBUG
373     {
374        int i;
375        for( i=0; i<auhalHostApi->devCount; ++i )
376           printf( "Device %d\t: %ld\n", i, auhalHostApi->devIds[i] );
377     }
378 #endif
379 
380     size = sizeof(AudioDeviceID);
381     auhalHostApi->defaultIn  = kAudioDeviceUnknown;
382     auhalHostApi->defaultOut = kAudioDeviceUnknown;
383 
384     /* determine the default device. */
385     /* I am not sure how these calls to AudioHardwareGetProperty()
386        could fail, but in case they do, we use the first available
387        device as the default. */
388     if( 0 != AudioHardwareGetProperty(kAudioHardwarePropertyDefaultInputDevice,
389                      &size,
390                      &auhalHostApi->defaultIn) ) {
391        int i;
392        auhalHostApi->defaultIn  = kAudioDeviceUnknown;
393        VDBUG(("Failed to get default input device from OS."));
394        VDBUG((" I will substitute the first available input Device."));
395        for( i=0; i<auhalHostApi->devCount; ++i ) {
396           PaDeviceInfo devInfo;
397           if( 0 != GetChannelInfo( auhalHostApi, &devInfo,
398                                    auhalHostApi->devIds[i], TRUE ) )
399              if( devInfo.maxInputChannels ) {
400                 auhalHostApi->defaultIn = auhalHostApi->devIds[i];
401                 break;
402              }
403        }
404     }
405     if( 0 != AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice,
406                      &size,
407                      &auhalHostApi->defaultOut) ) {
408        int i;
409        auhalHostApi->defaultIn  = kAudioDeviceUnknown;
410        VDBUG(("Failed to get default output device from OS."));
411        VDBUG((" I will substitute the first available output Device."));
412        for( i=0; i<auhalHostApi->devCount; ++i ) {
413           PaDeviceInfo devInfo;
414           if( 0 != GetChannelInfo( auhalHostApi, &devInfo,
415                                    auhalHostApi->devIds[i], FALSE ) )
416              if( devInfo.maxOutputChannels ) {
417                 auhalHostApi->defaultOut = auhalHostApi->devIds[i];
418                 break;
419              }
420        }
421     }
422 
423     VDBUG( ( "Default in : %ld\n", auhalHostApi->defaultIn  ) );
424     VDBUG( ( "Default out: %ld\n", auhalHostApi->defaultOut ) );
425 
426     return paNoError;
427 }
428 
429 /* =================================================================================================== */
430 /**
431  * @internal
432  * @brief Clip the desired size against the allowed IO buffer size range for the device.
433  */
ClipToDeviceBufferSize(AudioDeviceID macCoreDeviceId,int isInput,UInt32 desiredSize,UInt32 * allowedSize)434 static PaError ClipToDeviceBufferSize( AudioDeviceID macCoreDeviceId,
435 									int isInput, UInt32 desiredSize, UInt32 *allowedSize )
436 {
437 	UInt32 resultSize = desiredSize;
438 	AudioValueRange audioRange;
439 	UInt32 propSize = sizeof( audioRange );
440 	PaError err = WARNING(AudioDeviceGetProperty( macCoreDeviceId, 0, isInput, kAudioDevicePropertyBufferFrameSizeRange, &propSize, &audioRange ) );
441 	resultSize = MAX( resultSize, audioRange.mMinimum );
442 	resultSize = MIN( resultSize, audioRange.mMaximum );
443 	*allowedSize = resultSize;
444 	return err;
445 }
446 
447 /* =================================================================================================== */
448 #if 0
449 static void DumpDeviceProperties( AudioDeviceID macCoreDeviceId,
450                           int isInput )
451 {
452     PaError err;
453     int i;
454     UInt32 propSize;
455     UInt32 deviceLatency;
456     UInt32 streamLatency;
457     UInt32 bufferFrames;
458     UInt32 safetyOffset;
459     AudioStreamID streamIDs[128];
460 
461     printf("\n======= latency query : macCoreDeviceId = %d, isInput %d =======\n", (int)macCoreDeviceId, isInput );
462 
463     propSize = sizeof(UInt32);
464     err = WARNING(AudioDeviceGetProperty(macCoreDeviceId, 0, isInput, kAudioDevicePropertyBufferFrameSize, &propSize, &bufferFrames));
465     printf("kAudioDevicePropertyBufferFrameSize: err = %d, propSize = %d, value = %d\n", err, propSize, bufferFrames );
466 
467     propSize = sizeof(UInt32);
468     err = WARNING(AudioDeviceGetProperty(macCoreDeviceId, 0, isInput, kAudioDevicePropertySafetyOffset, &propSize, &safetyOffset));
469     printf("kAudioDevicePropertySafetyOffset: err = %d, propSize = %d, value = %d\n", err, propSize, safetyOffset );
470 
471     propSize = sizeof(UInt32);
472     err = WARNING(AudioDeviceGetProperty(macCoreDeviceId, 0, isInput, kAudioDevicePropertyLatency, &propSize, &deviceLatency));
473     printf("kAudioDevicePropertyLatency: err = %d, propSize = %d, value = %d\n", err, propSize, deviceLatency );
474 
475     AudioValueRange audioRange;
476     propSize = sizeof( audioRange );
477     err = WARNING(AudioDeviceGetProperty( macCoreDeviceId, 0, isInput, kAudioDevicePropertyBufferFrameSizeRange, &propSize, &audioRange ) );
478     printf("kAudioDevicePropertyBufferFrameSizeRange: err = %d, propSize = %u, minimum = %g\n", err, propSize, audioRange.mMinimum);
479     printf("kAudioDevicePropertyBufferFrameSizeRange: err = %d, propSize = %u, maximum = %g\n", err, propSize, audioRange.mMaximum );
480 
481     /* Get the streams from the device and query their latency. */
482     propSize = sizeof(streamIDs);
483     err  = WARNING(AudioDeviceGetProperty(macCoreDeviceId, 0, isInput, kAudioDevicePropertyStreams, &propSize, &streamIDs[0]));
484     int numStreams = propSize / sizeof(AudioStreamID);
485     for( i=0; i<numStreams; i++ )
486     {
487         printf("Stream #%d = %d---------------------- \n", i, streamIDs[i] );
488 
489         propSize = sizeof(UInt32);
490         err  = WARNING(AudioStreamGetProperty(streamIDs[i], 0, kAudioStreamPropertyLatency, &propSize, &streamLatency));
491         printf("  kAudioStreamPropertyLatency: err = %d, propSize = %d, value = %d\n", err, propSize, streamLatency );
492     }
493 }
494 #endif
495 
496 /* =================================================================================================== */
497 /**
498  * @internal
499  * Calculate the fixed latency from the system and the device.
500  * Sum of kAudioStreamPropertyLatency +
501  *        kAudioDevicePropertySafetyOffset +
502  *        kAudioDevicePropertyLatency
503  *
504  * Some useful info from Jeff Moore on latency.
505  * http://osdir.com/ml/coreaudio-api/2010-01/msg00046.html
506  * http://osdir.com/ml/coreaudio-api/2009-07/msg00140.html
507  */
CalculateFixedDeviceLatency(AudioDeviceID macCoreDeviceId,int isInput,UInt32 * fixedLatencyPtr)508 static PaError CalculateFixedDeviceLatency( AudioDeviceID macCoreDeviceId, int isInput, UInt32 *fixedLatencyPtr )
509 {
510     PaError err;
511     UInt32 propSize;
512     UInt32 deviceLatency;
513     UInt32 streamLatency;
514     UInt32 safetyOffset;
515     AudioStreamID streamIDs[1];
516 
517     // To get stream latency we have to get a streamID from the device.
518     // We are only going to look at the first stream so only fetch one stream.
519     propSize = sizeof(streamIDs);
520     err  = WARNING(AudioDeviceGetProperty(macCoreDeviceId, 0, isInput, kAudioDevicePropertyStreams, &propSize, &streamIDs[0]));
521     if( err != paNoError ) goto error;
522     if( propSize == sizeof(AudioStreamID) )
523     {
524         propSize = sizeof(UInt32);
525         err  = WARNING(AudioStreamGetProperty(streamIDs[0], 0, kAudioStreamPropertyLatency, &propSize, &streamLatency));
526     }
527 
528     propSize = sizeof(UInt32);
529     err = WARNING(AudioDeviceGetProperty(macCoreDeviceId, 0, isInput, kAudioDevicePropertySafetyOffset, &propSize, &safetyOffset));
530     if( err != paNoError ) goto error;
531 
532     propSize = sizeof(UInt32);
533     err = WARNING(AudioDeviceGetProperty(macCoreDeviceId, 0, isInput, kAudioDevicePropertyLatency, &propSize, &deviceLatency));
534     if( err != paNoError ) goto error;
535 
536     *fixedLatencyPtr = deviceLatency + streamLatency + safetyOffset;
537     return err;
538 error:
539     return err;
540 }
541 
542 /* =================================================================================================== */
CalculateDefaultDeviceLatencies(AudioDeviceID macCoreDeviceId,int isInput,UInt32 * lowLatencyFramesPtr,UInt32 * highLatencyFramesPtr)543 static PaError CalculateDefaultDeviceLatencies( AudioDeviceID macCoreDeviceId,
544                                                int isInput, UInt32 *lowLatencyFramesPtr,
545                                                UInt32 *highLatencyFramesPtr )
546 {
547     UInt32 propSize;
548     UInt32 bufferFrames = 0;
549     UInt32 fixedLatency = 0;
550     UInt32 clippedMinBufferSize = 0;
551 
552     //DumpDeviceProperties( macCoreDeviceId, isInput );
553 
554     PaError err = CalculateFixedDeviceLatency( macCoreDeviceId, isInput, &fixedLatency );
555     if( err != paNoError ) goto error;
556 
557     // For low latency use a small fixed size buffer clipped to the device range.
558     err = ClipToDeviceBufferSize( macCoreDeviceId, isInput, PA_MAC_SMALL_BUFFER_SIZE, &clippedMinBufferSize );
559     if( err != paNoError ) goto error;
560 
561     // For high latency use the default device buffer size.
562     propSize = sizeof(UInt32);
563     err = WARNING(AudioDeviceGetProperty(macCoreDeviceId, 0, isInput, kAudioDevicePropertyBufferFrameSize, &propSize, &bufferFrames));
564     if( err != paNoError ) goto error;
565 
566     *lowLatencyFramesPtr = fixedLatency + clippedMinBufferSize;
567     *highLatencyFramesPtr = fixedLatency + bufferFrames;
568 
569     return err;
570 error:
571     return err;
572 }
573 
574 /* =================================================================================================== */
575 
GetChannelInfo(PaMacAUHAL * auhalHostApi,PaDeviceInfo * deviceInfo,AudioDeviceID macCoreDeviceId,int isInput)576 static PaError GetChannelInfo( PaMacAUHAL *auhalHostApi,
577                                PaDeviceInfo *deviceInfo,
578                                AudioDeviceID macCoreDeviceId,
579                                int isInput)
580 {
581     UInt32 propSize;
582     PaError err = paNoError;
583     UInt32 i;
584     int numChannels = 0;
585     AudioBufferList *buflist = NULL;
586 
587     VVDBUG(("GetChannelInfo()\n"));
588 
589     /* Get the number of channels from the stream configuration.
590        Fail if we can't get this. */
591 
592     err = ERR(AudioDeviceGetPropertyInfo(macCoreDeviceId, 0, isInput, kAudioDevicePropertyStreamConfiguration, &propSize, NULL));
593     if (err)
594         return err;
595 
596     buflist = PaUtil_AllocateMemory(propSize);
597     if( !buflist )
598        return paInsufficientMemory;
599     err = ERR(AudioDeviceGetProperty(macCoreDeviceId, 0, isInput, kAudioDevicePropertyStreamConfiguration, &propSize, buflist));
600     if (err)
601         goto error;
602 
603     for (i = 0; i < buflist->mNumberBuffers; ++i)
604         numChannels += buflist->mBuffers[i].mNumberChannels;
605 
606     if (isInput)
607         deviceInfo->maxInputChannels = numChannels;
608     else
609         deviceInfo->maxOutputChannels = numChannels;
610 
611     if (numChannels > 0) /* do not try to retrieve the latency if there are no channels. */
612     {
613         /* Get the latency.  Don't fail if we can't get this. */
614         /* default to something reasonable */
615         deviceInfo->defaultLowInputLatency = .01;
616         deviceInfo->defaultHighInputLatency = .10;
617         deviceInfo->defaultLowOutputLatency = .01;
618         deviceInfo->defaultHighOutputLatency = .10;
619         UInt32 lowLatencyFrames = 0;
620         UInt32 highLatencyFrames = 0;
621         err = CalculateDefaultDeviceLatencies( macCoreDeviceId, isInput, &lowLatencyFrames, &highLatencyFrames );
622         if( err == 0 )
623         {
624 
625             double lowLatencySeconds = lowLatencyFrames / deviceInfo->defaultSampleRate;
626             double highLatencySeconds = highLatencyFrames / deviceInfo->defaultSampleRate;
627             if (isInput)
628             {
629                 deviceInfo->defaultLowInputLatency = lowLatencySeconds;
630                 deviceInfo->defaultHighInputLatency = highLatencySeconds;
631             }
632             else
633             {
634                 deviceInfo->defaultLowOutputLatency = lowLatencySeconds;
635                 deviceInfo->defaultHighOutputLatency = highLatencySeconds;
636             }
637         }
638     }
639     PaUtil_FreeMemory( buflist );
640     return paNoError;
641  error:
642     PaUtil_FreeMemory( buflist );
643     return err;
644 }
645 
646 /* =================================================================================================== */
InitializeDeviceInfo(PaMacAUHAL * auhalHostApi,PaDeviceInfo * deviceInfo,AudioDeviceID macCoreDeviceId,PaHostApiIndex hostApiIndex)647 static PaError InitializeDeviceInfo( PaMacAUHAL *auhalHostApi,
648                                      PaDeviceInfo *deviceInfo,
649                                      AudioDeviceID macCoreDeviceId,
650                                      PaHostApiIndex hostApiIndex )
651 {
652     Float64 sampleRate;
653     char *name;
654     PaError err = paNoError;
655 	CFStringRef nameRef;
656     UInt32 propSize;
657 
658     VVDBUG(("InitializeDeviceInfo(): macCoreDeviceId=%ld\n", macCoreDeviceId));
659 
660     memset(deviceInfo, 0, sizeof(PaDeviceInfo));
661 
662     deviceInfo->structVersion = 2;
663     deviceInfo->hostApi = hostApiIndex;
664 
665     /* Get the device name using CFString */
666 	propSize = sizeof(nameRef);
667     err = ERR(AudioDeviceGetProperty(macCoreDeviceId, 0, 0, kAudioDevicePropertyDeviceNameCFString, &propSize, &nameRef));
668     if (err)
669     {
670 		/* Get the device name using c string.  Fail if we can't get it. */
671 		err = ERR(AudioDeviceGetPropertyInfo(macCoreDeviceId, 0, 0, kAudioDevicePropertyDeviceName, &propSize, NULL));
672 		if (err)
673 			return err;
674 
675 		name = PaUtil_GroupAllocateMemory(auhalHostApi->allocations,propSize+1);
676 		if ( !name )
677 			return paInsufficientMemory;
678 		err = ERR(AudioDeviceGetProperty(macCoreDeviceId, 0, 0, kAudioDevicePropertyDeviceName, &propSize, name));
679 		if (err)
680 			return err;
681 	}
682 	else
683 	{
684 		/* valid CFString so we just allocate a c string big enough to contain the data */
685 		propSize = CFStringGetMaximumSizeForEncoding(CFStringGetLength(nameRef), kCFStringEncodingUTF8);
686 		name = PaUtil_GroupAllocateMemory(auhalHostApi->allocations, propSize+1);
687 		if ( !name )
688 		{
689 			CFRelease(nameRef);
690 			return paInsufficientMemory;
691 		}
692 		CFStringGetCString(nameRef, name, propSize+1, kCFStringEncodingUTF8);
693 		CFRelease(nameRef);
694 	}
695     deviceInfo->name = name;
696 
697     /* Try to get the default sample rate.  Don't fail if we can't get this. */
698     propSize = sizeof(Float64);
699     err = ERR(AudioDeviceGetProperty(macCoreDeviceId, 0, 0, kAudioDevicePropertyNominalSampleRate, &propSize, &sampleRate));
700     if (err)
701         deviceInfo->defaultSampleRate = 0.0;
702     else
703         deviceInfo->defaultSampleRate = sampleRate;
704 
705     /* Get the maximum number of input and output channels.  Fail if we can't get this. */
706 
707     err = GetChannelInfo(auhalHostApi, deviceInfo, macCoreDeviceId, 1);
708     if (err)
709         return err;
710 
711     err = GetChannelInfo(auhalHostApi, deviceInfo, macCoreDeviceId, 0);
712     if (err)
713         return err;
714 
715     return paNoError;
716 }
717 
PaMacCore_Initialize(PaUtilHostApiRepresentation ** hostApi,PaHostApiIndex hostApiIndex)718 PaError PaMacCore_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex )
719 {
720     PaError result = paNoError;
721     int i;
722     PaMacAUHAL *auhalHostApi = NULL;
723     PaDeviceInfo *deviceInfoArray;
724     int unixErr;
725 
726     VVDBUG(("PaMacCore_Initialize(): hostApiIndex=%d\n", hostApiIndex));
727 
728 	SInt32 major;
729 	SInt32 minor;
730 	Gestalt(gestaltSystemVersionMajor, &major);
731 	Gestalt(gestaltSystemVersionMinor, &minor);
732 
733 	// Starting with 10.6 systems, the HAL notification thread is created internally
734 	if (major == 10 && minor >= 6) {
735 		CFRunLoopRef theRunLoop = NULL;
736 		AudioObjectPropertyAddress theAddress = { kAudioHardwarePropertyRunLoop, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster };
737 		OSStatus osErr = AudioObjectSetPropertyData (kAudioObjectSystemObject, &theAddress, 0, NULL, sizeof(CFRunLoopRef), &theRunLoop);
738 		if (osErr != noErr) {
739 			goto error;
740 		}
741 	}
742 
743     unixErr = initializeXRunListenerList();
744     if( 0 != unixErr ) {
745        return UNIX_ERR(unixErr);
746     }
747 
748     auhalHostApi = (PaMacAUHAL*)PaUtil_AllocateMemory( sizeof(PaMacAUHAL) );
749     if( !auhalHostApi )
750     {
751         result = paInsufficientMemory;
752         goto error;
753     }
754 
755     auhalHostApi->allocations = PaUtil_CreateAllocationGroup();
756     if( !auhalHostApi->allocations )
757     {
758         result = paInsufficientMemory;
759         goto error;
760     }
761 
762     auhalHostApi->devIds = NULL;
763     auhalHostApi->devCount = 0;
764 
765     /* get the info we need about the devices */
766     result = gatherDeviceInfo( auhalHostApi );
767     if( result != paNoError )
768        goto error;
769 
770     *hostApi = &auhalHostApi->inheritedHostApiRep;
771     (*hostApi)->info.structVersion = 1;
772     (*hostApi)->info.type = paCoreAudio;
773     (*hostApi)->info.name = "Core Audio";
774 
775     (*hostApi)->info.defaultInputDevice = paNoDevice;
776     (*hostApi)->info.defaultOutputDevice = paNoDevice;
777 
778     (*hostApi)->info.deviceCount = 0;
779 
780     if( auhalHostApi->devCount > 0 )
781     {
782         (*hostApi)->deviceInfos = (PaDeviceInfo**)PaUtil_GroupAllocateMemory(
783                 auhalHostApi->allocations, sizeof(PaDeviceInfo*) * auhalHostApi->devCount);
784         if( !(*hostApi)->deviceInfos )
785         {
786             result = paInsufficientMemory;
787             goto error;
788         }
789 
790         /* allocate all device info structs in a contiguous block */
791         deviceInfoArray = (PaDeviceInfo*)PaUtil_GroupAllocateMemory(
792                 auhalHostApi->allocations, sizeof(PaDeviceInfo) * auhalHostApi->devCount );
793         if( !deviceInfoArray )
794         {
795             result = paInsufficientMemory;
796             goto error;
797         }
798 
799         for( i=0; i < auhalHostApi->devCount; ++i )
800         {
801             int err;
802             err = InitializeDeviceInfo( auhalHostApi, &deviceInfoArray[i],
803                                       auhalHostApi->devIds[i],
804                                       hostApiIndex );
805             if (err == paNoError)
806             { /* copy some info and set the defaults */
807                 (*hostApi)->deviceInfos[(*hostApi)->info.deviceCount] = &deviceInfoArray[i];
808                 if (auhalHostApi->devIds[i] == auhalHostApi->defaultIn)
809                     (*hostApi)->info.defaultInputDevice = (*hostApi)->info.deviceCount;
810                 if (auhalHostApi->devIds[i] == auhalHostApi->defaultOut)
811                     (*hostApi)->info.defaultOutputDevice = (*hostApi)->info.deviceCount;
812                 (*hostApi)->info.deviceCount++;
813             }
814             else
815             { /* there was an error. we need to shift the devices down, so we ignore this one */
816                 int j;
817                 auhalHostApi->devCount--;
818                 for( j=i; j<auhalHostApi->devCount; ++j )
819                    auhalHostApi->devIds[j] = auhalHostApi->devIds[j+1];
820                 i--;
821             }
822         }
823     }
824 
825     (*hostApi)->Terminate = Terminate;
826     (*hostApi)->OpenStream = OpenStream;
827     (*hostApi)->IsFormatSupported = IsFormatSupported;
828 
829     PaUtil_InitializeStreamInterface( &auhalHostApi->callbackStreamInterface,
830                                       CloseStream, StartStream,
831                                       StopStream, AbortStream, IsStreamStopped,
832                                       IsStreamActive,
833                                       GetStreamTime, GetStreamCpuLoad,
834                                       PaUtil_DummyRead, PaUtil_DummyWrite,
835                                       PaUtil_DummyGetReadAvailable,
836                                       PaUtil_DummyGetWriteAvailable );
837 
838     PaUtil_InitializeStreamInterface( &auhalHostApi->blockingStreamInterface,
839                                       CloseStream, StartStream,
840                                       StopStream, AbortStream, IsStreamStopped,
841                                       IsStreamActive,
842                                       GetStreamTime, PaUtil_DummyGetCpuLoad,
843                                       ReadStream, WriteStream,
844                                       GetStreamReadAvailable,
845                                       GetStreamWriteAvailable );
846 
847     return result;
848 
849 error:
850     if( auhalHostApi )
851     {
852         if( auhalHostApi->allocations )
853         {
854             PaUtil_FreeAllAllocations( auhalHostApi->allocations );
855             PaUtil_DestroyAllocationGroup( auhalHostApi->allocations );
856         }
857 
858         PaUtil_FreeMemory( auhalHostApi );
859     }
860     return result;
861 }
862 
863 
Terminate(struct PaUtilHostApiRepresentation * hostApi)864 static void Terminate( struct PaUtilHostApiRepresentation *hostApi )
865 {
866     int unixErr;
867 
868     PaMacAUHAL *auhalHostApi = (PaMacAUHAL*)hostApi;
869 
870     VVDBUG(("Terminate()\n"));
871 
872     unixErr = destroyXRunListenerList();
873     if( 0 != unixErr )
874        UNIX_ERR(unixErr);
875 
876     /*
877         IMPLEMENT ME:
878             - clean up any resources not handled by the allocation group
879         TODO: Double check that everything is handled by alloc group
880     */
881 
882     if( auhalHostApi->allocations )
883     {
884         PaUtil_FreeAllAllocations( auhalHostApi->allocations );
885         PaUtil_DestroyAllocationGroup( auhalHostApi->allocations );
886     }
887 
888     PaUtil_FreeMemory( auhalHostApi );
889 }
890 
891 
IsFormatSupported(struct PaUtilHostApiRepresentation * hostApi,const PaStreamParameters * inputParameters,const PaStreamParameters * outputParameters,double sampleRate)892 static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
893                                   const PaStreamParameters *inputParameters,
894                                   const PaStreamParameters *outputParameters,
895                                   double sampleRate )
896 {
897     int inputChannelCount, outputChannelCount;
898     PaSampleFormat inputSampleFormat, outputSampleFormat;
899 
900     VVDBUG(("IsFormatSupported(): in chan=%d, in fmt=%ld, out chan=%d, out fmt=%ld sampleRate=%g\n",
901                 inputParameters  ? inputParameters->channelCount  : -1,
902                 inputParameters  ? inputParameters->sampleFormat  : -1,
903                 outputParameters ? outputParameters->channelCount : -1,
904                 outputParameters ? outputParameters->sampleFormat : -1,
905                 (float) sampleRate ));
906 
907     /** These first checks are standard PA checks. We do some fancier checks
908         later. */
909     if( inputParameters )
910     {
911         inputChannelCount = inputParameters->channelCount;
912         inputSampleFormat = inputParameters->sampleFormat;
913 
914         /* all standard sample formats are supported by the buffer adapter,
915             this implementation doesn't support any custom sample formats */
916         if( inputSampleFormat & paCustomFormat )
917             return paSampleFormatNotSupported;
918 
919         /* unless alternate device specification is supported, reject the use of
920             paUseHostApiSpecificDeviceSpecification */
921 
922         if( inputParameters->device == paUseHostApiSpecificDeviceSpecification )
923             return paInvalidDevice;
924 
925         /* check that input device can support inputChannelCount */
926         if( inputChannelCount > hostApi->deviceInfos[ inputParameters->device ]->maxInputChannels )
927             return paInvalidChannelCount;
928     }
929     else
930     {
931         inputChannelCount = 0;
932     }
933 
934     if( outputParameters )
935     {
936         outputChannelCount = outputParameters->channelCount;
937         outputSampleFormat = outputParameters->sampleFormat;
938 
939         /* all standard sample formats are supported by the buffer adapter,
940             this implementation doesn't support any custom sample formats */
941         if( outputSampleFormat & paCustomFormat )
942             return paSampleFormatNotSupported;
943 
944         /* unless alternate device specification is supported, reject the use of
945             paUseHostApiSpecificDeviceSpecification */
946 
947         if( outputParameters->device == paUseHostApiSpecificDeviceSpecification )
948             return paInvalidDevice;
949 
950         /* check that output device can support outputChannelCount */
951         if( outputChannelCount > hostApi->deviceInfos[ outputParameters->device ]->maxOutputChannels )
952             return paInvalidChannelCount;
953 
954     }
955     else
956     {
957         outputChannelCount = 0;
958     }
959 
960     /* FEEDBACK */
961     /*        I think the only way to check a given format SR combo is     */
962     /*        to try opening it. This could be disruptive, is that Okay?   */
963     /*        The alternative is to just read off available sample rates,  */
964     /*        but this will not work %100 of the time (eg, a device that   */
965     /*        supports N output at one rate but only N/2 at a higher rate.)*/
966 
967     /* The following code opens the device with the requested parameters to
968        see if it works. */
969     {
970        PaError err;
971        PaStream *s;
972        err = OpenStream( hostApi, &s, inputParameters, outputParameters,
973                            sampleRate, 1024, 0, (PaStreamCallback *)1, NULL );
974        if( err != paNoError && err != paInvalidSampleRate )
975           DBUG( ( "OpenStream @ %g returned: %d: %s\n",
976                   (float) sampleRate, err, Pa_GetErrorText( err ) ) );
977        if( err )
978           return err;
979        err = CloseStream( s );
980        if( err ) {
981           /* FEEDBACK: is this more serious? should we assert? */
982           DBUG( ( "WARNING: could not close Stream. %d: %s\n",
983                   err, Pa_GetErrorText( err ) ) );
984        }
985     }
986 
987     return paFormatIsSupported;
988 }
989 
990 /* ================================================================================= */
InitializeDeviceProperties(PaMacCoreDeviceProperties * deviceProperties)991 static void InitializeDeviceProperties( PaMacCoreDeviceProperties *deviceProperties )
992 {
993     memset( deviceProperties, 0, sizeof(PaMacCoreDeviceProperties) );
994     deviceProperties->sampleRate = 1.0; // Better than random. Overwritten by actual values later on.
995     deviceProperties->samplePeriod = 1.0 / deviceProperties->sampleRate;
996 }
997 
CalculateSoftwareLatencyFromProperties(PaMacCoreStream * stream,PaMacCoreDeviceProperties * deviceProperties)998 static Float64 CalculateSoftwareLatencyFromProperties( PaMacCoreStream *stream, PaMacCoreDeviceProperties *deviceProperties )
999 {
1000     UInt32 latencyFrames = deviceProperties->bufferFrameSize + deviceProperties->deviceLatency + deviceProperties->safetyOffset;
1001     return latencyFrames * deviceProperties->samplePeriod; // same as dividing by sampleRate but faster
1002 }
1003 
CalculateHardwareLatencyFromProperties(PaMacCoreStream * stream,PaMacCoreDeviceProperties * deviceProperties)1004 static Float64 CalculateHardwareLatencyFromProperties( PaMacCoreStream *stream, PaMacCoreDeviceProperties *deviceProperties )
1005 {
1006     return deviceProperties->deviceLatency * deviceProperties->samplePeriod; // same as dividing by sampleRate but faster
1007 }
1008 
1009 /* Calculate values used to convert Apple timestamps into PA timestamps
1010  * from the device properties. The final results of this calculation
1011  * will be used in the audio callback function.
1012  */
UpdateTimeStampOffsets(PaMacCoreStream * stream)1013 static void UpdateTimeStampOffsets( PaMacCoreStream *stream )
1014 {
1015     Float64 inputSoftwareLatency = 0.0;
1016     Float64 inputHardwareLatency = 0.0;
1017     Float64 outputSoftwareLatency = 0.0;
1018     Float64 outputHardwareLatency = 0.0;
1019 
1020     if( stream->inputUnit != NULL )
1021     {
1022         inputSoftwareLatency = CalculateSoftwareLatencyFromProperties( stream, &stream->inputProperties );
1023         inputHardwareLatency = CalculateHardwareLatencyFromProperties( stream, &stream->inputProperties );
1024     }
1025     if( stream->outputUnit != NULL )
1026     {
1027         outputSoftwareLatency = CalculateSoftwareLatencyFromProperties( stream, &stream->outputProperties );
1028         outputHardwareLatency = CalculateHardwareLatencyFromProperties( stream, &stream->outputProperties );
1029     }
1030 
1031     /* We only need a mutex around setting these variables as a group. */
1032 	pthread_mutex_lock( &stream->timingInformationMutex );
1033     stream->timestampOffsetCombined = inputSoftwareLatency + outputSoftwareLatency;
1034     stream->timestampOffsetInputDevice = inputHardwareLatency;
1035     stream->timestampOffsetOutputDevice = outputHardwareLatency;
1036 	pthread_mutex_unlock( &stream->timingInformationMutex );
1037 }
1038 
1039 /* ================================================================================= */
1040 
1041 /* can be used to update from nominal or actual sample rate */
UpdateSampleRateFromDeviceProperty(PaMacCoreStream * stream,AudioDeviceID deviceID,Boolean isInput,AudioDevicePropertyID sampleRatePropertyID)1042 static OSStatus UpdateSampleRateFromDeviceProperty( PaMacCoreStream *stream, AudioDeviceID deviceID, Boolean isInput, AudioDevicePropertyID sampleRatePropertyID )
1043 {
1044     PaMacCoreDeviceProperties * deviceProperties = isInput ? &stream->inputProperties : &stream->outputProperties;
1045 
1046 	Float64 sampleRate = 0.0;
1047 	UInt32 propSize = sizeof(Float64);
1048     OSStatus osErr = AudioDeviceGetProperty( deviceID, 0, isInput, sampleRatePropertyID, &propSize, &sampleRate);
1049 	if( (osErr == noErr) && (sampleRate > 1000.0) ) /* avoid divide by zero if there's an error */
1050 	{
1051         deviceProperties->sampleRate = sampleRate;
1052         deviceProperties->samplePeriod = 1.0 / sampleRate;
1053     }
1054     return osErr;
1055 }
1056 
AudioDevicePropertyActualSampleRateListenerProc(AudioDeviceID inDevice,UInt32 inChannel,Boolean isInput,AudioDevicePropertyID inPropertyID,void * inClientData)1057 static OSStatus AudioDevicePropertyActualSampleRateListenerProc( AudioDeviceID inDevice, UInt32 inChannel, Boolean isInput, AudioDevicePropertyID inPropertyID, void *inClientData )
1058 {
1059 	PaMacCoreStream *stream = (PaMacCoreStream*)inClientData;
1060 
1061     // Make sure the callback is operating on a stream that is still valid!
1062     assert( stream->streamRepresentation.magic == PA_STREAM_MAGIC );
1063 
1064 	OSStatus osErr = UpdateSampleRateFromDeviceProperty( stream, inDevice, isInput, kAudioDevicePropertyActualSampleRate );
1065     if( osErr == noErr )
1066     {
1067         UpdateTimeStampOffsets( stream );
1068     }
1069     return osErr;
1070 }
1071 
1072 /* ================================================================================= */
QueryUInt32DeviceProperty(AudioDeviceID deviceID,Boolean isInput,AudioDevicePropertyID propertyID,UInt32 * outValue)1073 static OSStatus QueryUInt32DeviceProperty( AudioDeviceID deviceID, Boolean isInput, AudioDevicePropertyID propertyID, UInt32 *outValue )
1074 {
1075 	UInt32 propertyValue = 0;
1076 	UInt32 propertySize = sizeof(UInt32);
1077 	OSStatus osErr = AudioDeviceGetProperty( deviceID, 0, isInput, propertyID, &propertySize, &propertyValue);
1078 	if( osErr == noErr )
1079 	{
1080         *outValue = propertyValue;
1081 	}
1082     return osErr;
1083 }
1084 
AudioDevicePropertyGenericListenerProc(AudioDeviceID inDevice,UInt32 inChannel,Boolean isInput,AudioDevicePropertyID inPropertyID,void * inClientData)1085 static OSStatus AudioDevicePropertyGenericListenerProc( AudioDeviceID inDevice, UInt32 inChannel, Boolean isInput, AudioDevicePropertyID inPropertyID, void *inClientData )
1086 {
1087     OSStatus osErr = noErr;
1088 	PaMacCoreStream *stream = (PaMacCoreStream*)inClientData;
1089 
1090     // Make sure the callback is operating on a stream that is still valid!
1091     assert( stream->streamRepresentation.magic == PA_STREAM_MAGIC );
1092 
1093     PaMacCoreDeviceProperties *deviceProperties = isInput ? &stream->inputProperties : &stream->outputProperties;
1094     UInt32 *valuePtr = NULL;
1095     switch( inPropertyID )
1096     {
1097         case kAudioDevicePropertySafetyOffset:
1098             valuePtr = &deviceProperties->safetyOffset;
1099             break;
1100 
1101         case kAudioDevicePropertyLatency:
1102             valuePtr = &deviceProperties->deviceLatency;
1103             break;
1104 
1105         case kAudioDevicePropertyBufferFrameSize:
1106             valuePtr = &deviceProperties->bufferFrameSize;
1107             break;
1108     }
1109     if( valuePtr != NULL )
1110     {
1111         osErr = QueryUInt32DeviceProperty( inDevice, isInput, inPropertyID, valuePtr );
1112         if( osErr == noErr )
1113         {
1114             UpdateTimeStampOffsets( stream );
1115         }
1116     }
1117     return osErr;
1118 }
1119 
1120 /* ================================================================================= */
1121 /*
1122  * Setup listeners in case device properties change during the run. */
SetupDevicePropertyListeners(PaMacCoreStream * stream,AudioDeviceID deviceID,Boolean isInput)1123 static OSStatus SetupDevicePropertyListeners( PaMacCoreStream *stream, AudioDeviceID deviceID, Boolean isInput )
1124 {
1125     OSStatus osErr = noErr;
1126     PaMacCoreDeviceProperties *deviceProperties = isInput ? &stream->inputProperties : &stream->outputProperties;
1127 
1128     if( (osErr = QueryUInt32DeviceProperty( deviceID, isInput,
1129                                            kAudioDevicePropertyLatency, &deviceProperties->deviceLatency )) != noErr ) return osErr;
1130     if( (osErr = QueryUInt32DeviceProperty( deviceID, isInput,
1131                                            kAudioDevicePropertyBufferFrameSize, &deviceProperties->bufferFrameSize )) != noErr ) return osErr;
1132     if( (osErr = QueryUInt32DeviceProperty( deviceID, isInput,
1133                                            kAudioDevicePropertySafetyOffset, &deviceProperties->safetyOffset )) != noErr ) return osErr;
1134 
1135     AudioDeviceAddPropertyListener( deviceID, 0, isInput, kAudioDevicePropertyActualSampleRate,
1136                                    AudioDevicePropertyActualSampleRateListenerProc, stream );
1137 
1138     AudioDeviceAddPropertyListener( deviceID, 0, isInput, kAudioStreamPropertyLatency,
1139                                    AudioDevicePropertyGenericListenerProc, stream );
1140     AudioDeviceAddPropertyListener( deviceID, 0, isInput, kAudioDevicePropertyBufferFrameSize,
1141                                    AudioDevicePropertyGenericListenerProc, stream );
1142     AudioDeviceAddPropertyListener( deviceID, 0, isInput, kAudioDevicePropertySafetyOffset,
1143                                    AudioDevicePropertyGenericListenerProc, stream );
1144 
1145     return osErr;
1146 }
1147 
CleanupDevicePropertyListeners(PaMacCoreStream * stream,AudioDeviceID deviceID,Boolean isInput)1148 static void CleanupDevicePropertyListeners( PaMacCoreStream *stream, AudioDeviceID deviceID, Boolean isInput )
1149 {
1150     AudioDeviceRemovePropertyListener( deviceID, 0, isInput, kAudioDevicePropertyActualSampleRate,
1151                                    AudioDevicePropertyActualSampleRateListenerProc );
1152 
1153     AudioDeviceRemovePropertyListener( deviceID, 0, isInput, kAudioDevicePropertyLatency,
1154                                    AudioDevicePropertyGenericListenerProc );
1155     AudioDeviceRemovePropertyListener( deviceID, 0, isInput, kAudioDevicePropertyBufferFrameSize,
1156                                    AudioDevicePropertyGenericListenerProc );
1157     AudioDeviceRemovePropertyListener( deviceID, 0, isInput, kAudioDevicePropertySafetyOffset,
1158                                    AudioDevicePropertyGenericListenerProc );
1159 }
1160 
1161 /* ================================================================================= */
OpenAndSetupOneAudioUnit(const PaMacCoreStream * stream,const PaStreamParameters * inStreamParams,const PaStreamParameters * outStreamParams,const UInt32 requestedFramesPerBuffer,UInt32 * actualInputFramesPerBuffer,UInt32 * actualOutputFramesPerBuffer,const PaMacAUHAL * auhalHostApi,AudioUnit * audioUnit,AudioConverterRef * srConverter,AudioDeviceID * audioDevice,const double sampleRate,void * refCon)1162 static PaError OpenAndSetupOneAudioUnit(
1163                                    const PaMacCoreStream *stream,
1164                                    const PaStreamParameters *inStreamParams,
1165                                    const PaStreamParameters *outStreamParams,
1166                                    const UInt32 requestedFramesPerBuffer,
1167                                    UInt32 *actualInputFramesPerBuffer,
1168                                    UInt32 *actualOutputFramesPerBuffer,
1169                                    const PaMacAUHAL *auhalHostApi,
1170                                    AudioUnit *audioUnit,
1171                                    AudioConverterRef *srConverter,
1172                                    AudioDeviceID *audioDevice,
1173                                    const double sampleRate,
1174                                    void *refCon )
1175 {
1176     ComponentDescription desc;
1177     Component comp;
1178     /*An Apple TN suggests using CAStreamBasicDescription, but that is C++*/
1179     AudioStreamBasicDescription desiredFormat;
1180     OSStatus result = noErr;
1181     PaError paResult = paNoError;
1182     int line = 0;
1183     UInt32 callbackKey;
1184     AURenderCallbackStruct rcbs;
1185     unsigned long macInputStreamFlags  = paMacCorePlayNice;
1186     unsigned long macOutputStreamFlags = paMacCorePlayNice;
1187     SInt32 const *inChannelMap = NULL;
1188     SInt32 const *outChannelMap = NULL;
1189     unsigned long inChannelMapSize = 0;
1190     unsigned long outChannelMapSize = 0;
1191 
1192     VVDBUG(("OpenAndSetupOneAudioUnit(): in chan=%d, in fmt=%ld, out chan=%d, out fmt=%ld, requestedFramesPerBuffer=%ld\n",
1193                 inStreamParams  ? inStreamParams->channelCount  : -1,
1194                 inStreamParams  ? inStreamParams->sampleFormat  : -1,
1195                 outStreamParams ? outStreamParams->channelCount : -1,
1196                 outStreamParams ? outStreamParams->sampleFormat : -1,
1197                 requestedFramesPerBuffer ));
1198 
1199     /* -- handle the degenerate case  -- */
1200     if( !inStreamParams && !outStreamParams ) {
1201        *audioUnit = NULL;
1202        *audioDevice = kAudioDeviceUnknown;
1203        return paNoError;
1204     }
1205 
1206     /* -- get the user's api specific info, if they set any -- */
1207     if( inStreamParams && inStreamParams->hostApiSpecificStreamInfo )
1208     {
1209        macInputStreamFlags=
1210             ((PaMacCoreStreamInfo*)inStreamParams->hostApiSpecificStreamInfo)
1211                   ->flags;
1212        inChannelMap = ((PaMacCoreStreamInfo*)inStreamParams->hostApiSpecificStreamInfo)
1213                   ->channelMap;
1214        inChannelMapSize = ((PaMacCoreStreamInfo*)inStreamParams->hostApiSpecificStreamInfo)
1215                   ->channelMapSize;
1216     }
1217     if( outStreamParams && outStreamParams->hostApiSpecificStreamInfo )
1218     {
1219        macOutputStreamFlags=
1220             ((PaMacCoreStreamInfo*)outStreamParams->hostApiSpecificStreamInfo)
1221                   ->flags;
1222        outChannelMap = ((PaMacCoreStreamInfo*)outStreamParams->hostApiSpecificStreamInfo)
1223                   ->channelMap;
1224        outChannelMapSize = ((PaMacCoreStreamInfo*)outStreamParams->hostApiSpecificStreamInfo)
1225                   ->channelMapSize;
1226     }
1227     /* Override user's flags here, if desired for testing. */
1228 
1229     /*
1230      * The HAL AU is a Mac OS style "component".
1231      * the first few steps deal with that.
1232      * Later steps work on a combination of Mac OS
1233      * components and the slightly lower level
1234      * HAL.
1235      */
1236 
1237     /* -- describe the output type AudioUnit -- */
1238     /*  Note: for the default AudioUnit, we could use the
1239      *  componentSubType value kAudioUnitSubType_DefaultOutput;
1240      *  but I don't think that's relevant here.
1241      */
1242     desc.componentType         = kAudioUnitType_Output;
1243     desc.componentSubType      = kAudioUnitSubType_HALOutput;
1244     desc.componentManufacturer = kAudioUnitManufacturer_Apple;
1245     desc.componentFlags        = 0;
1246     desc.componentFlagsMask    = 0;
1247     /* -- find the component -- */
1248     comp = FindNextComponent( NULL, &desc );
1249     if( !comp )
1250     {
1251        DBUG( ( "AUHAL component not found." ) );
1252        *audioUnit = NULL;
1253        *audioDevice = kAudioDeviceUnknown;
1254        return paUnanticipatedHostError;
1255     }
1256     /* -- open it -- */
1257     result = OpenAComponent( comp, audioUnit );
1258     if( result )
1259     {
1260        DBUG( ( "Failed to open AUHAL component." ) );
1261        *audioUnit = NULL;
1262        *audioDevice = kAudioDeviceUnknown;
1263        return ERR( result );
1264     }
1265     /* -- prepare a little error handling logic / hackery -- */
1266 #define ERR_WRAP(mac_err) do { result = mac_err ; line = __LINE__ ; if ( result != noErr ) goto error ; } while(0)
1267 
1268     /* -- if there is input, we have to explicitly enable input -- */
1269     if( inStreamParams )
1270     {
1271        UInt32 enableIO = 1;
1272        ERR_WRAP( AudioUnitSetProperty( *audioUnit,
1273                  kAudioOutputUnitProperty_EnableIO,
1274                  kAudioUnitScope_Input,
1275                  INPUT_ELEMENT,
1276                  &enableIO,
1277                  sizeof(enableIO) ) );
1278     }
1279     /* -- if there is no output, we must explicitly disable output -- */
1280     if( !outStreamParams )
1281     {
1282        UInt32 enableIO = 0;
1283        ERR_WRAP( AudioUnitSetProperty( *audioUnit,
1284                  kAudioOutputUnitProperty_EnableIO,
1285                  kAudioUnitScope_Output,
1286                  OUTPUT_ELEMENT,
1287                  &enableIO,
1288                  sizeof(enableIO) ) );
1289     }
1290 
1291     /* -- set the devices -- */
1292     /* make sure input and output are the same device if we are doing input and
1293        output. */
1294     if( inStreamParams && outStreamParams )
1295     {
1296        assert( outStreamParams->device == inStreamParams->device );
1297     }
1298     if( inStreamParams )
1299     {
1300        *audioDevice = auhalHostApi->devIds[inStreamParams->device] ;
1301        ERR_WRAP( AudioUnitSetProperty( *audioUnit,
1302                     kAudioOutputUnitProperty_CurrentDevice,
1303                     kAudioUnitScope_Global,
1304                     INPUT_ELEMENT,
1305                     audioDevice,
1306                     sizeof(AudioDeviceID) ) );
1307     }
1308     if( outStreamParams && outStreamParams != inStreamParams )
1309     {
1310        *audioDevice = auhalHostApi->devIds[outStreamParams->device] ;
1311        ERR_WRAP( AudioUnitSetProperty( *audioUnit,
1312                     kAudioOutputUnitProperty_CurrentDevice,
1313                     kAudioUnitScope_Global,
1314                     OUTPUT_ELEMENT,
1315                     audioDevice,
1316                     sizeof(AudioDeviceID) ) );
1317     }
1318     /* -- add listener for dropouts -- */
1319     result = AudioDeviceAddPropertyListener( *audioDevice,
1320                                              0,
1321                                              outStreamParams ? false : true,
1322                                              kAudioDeviceProcessorOverload,
1323                                              xrunCallback,
1324                                              addToXRunListenerList( (void *)stream ) ) ;
1325     if( result == kAudioHardwareIllegalOperationError ) {
1326        // -- already registered, we're good
1327     } else {
1328        // -- not already registered, just check for errors
1329        ERR_WRAP( result );
1330     }
1331     /* -- listen for stream start and stop -- */
1332     ERR_WRAP( AudioUnitAddPropertyListener( *audioUnit,
1333                                             kAudioOutputUnitProperty_IsRunning,
1334                                             startStopCallback,
1335                                             (void *)stream ) );
1336 
1337     /* -- set format -- */
1338     bzero( &desiredFormat, sizeof(desiredFormat) );
1339     desiredFormat.mFormatID         = kAudioFormatLinearPCM ;
1340     desiredFormat.mFormatFlags      = kAudioFormatFlagsNativeFloatPacked;
1341     desiredFormat.mFramesPerPacket  = 1;
1342     desiredFormat.mBitsPerChannel   = sizeof( float ) * 8;
1343 
1344     result = 0;
1345     /*  set device format first, but only touch the device if the user asked */
1346     if( inStreamParams ) {
1347        /*The callback never calls back if we don't set the FPB */
1348        /*This seems wierd, because I would think setting anything on the device
1349          would be disruptive.*/
1350        paResult = setBestFramesPerBuffer( *audioDevice, FALSE,
1351                                           requestedFramesPerBuffer,
1352                                           actualInputFramesPerBuffer );
1353        if( paResult ) goto error;
1354        if( macInputStreamFlags & paMacCoreChangeDeviceParameters ) {
1355           bool requireExact;
1356           requireExact=macInputStreamFlags & paMacCoreFailIfConversionRequired;
1357           paResult = setBestSampleRateForDevice( *audioDevice, FALSE,
1358                                                  requireExact, sampleRate );
1359           if( paResult ) goto error;
1360        }
1361        if( actualInputFramesPerBuffer && actualOutputFramesPerBuffer )
1362           *actualOutputFramesPerBuffer = *actualInputFramesPerBuffer ;
1363     }
1364     if( outStreamParams && !inStreamParams ) {
1365        /*The callback never calls back if we don't set the FPB */
1366        /*This seems wierd, because I would think setting anything on the device
1367          would be disruptive.*/
1368        paResult = setBestFramesPerBuffer( *audioDevice, TRUE,
1369                                           requestedFramesPerBuffer,
1370                                           actualOutputFramesPerBuffer );
1371        if( paResult ) goto error;
1372        if( macOutputStreamFlags & paMacCoreChangeDeviceParameters ) {
1373           bool requireExact;
1374           requireExact=macOutputStreamFlags & paMacCoreFailIfConversionRequired;
1375           paResult = setBestSampleRateForDevice( *audioDevice, TRUE,
1376                                                  requireExact, sampleRate );
1377           if( paResult ) goto error;
1378        }
1379     }
1380 
1381     /* -- set the quality of the output converter -- */
1382     if( outStreamParams ) {
1383        UInt32 value = kAudioConverterQuality_Max;
1384        switch( macOutputStreamFlags & 0x0700 ) {
1385        case 0x0100: /*paMacCore_ConversionQualityMin:*/
1386           value=kRenderQuality_Min;
1387           break;
1388        case 0x0200: /*paMacCore_ConversionQualityLow:*/
1389           value=kRenderQuality_Low;
1390           break;
1391        case 0x0300: /*paMacCore_ConversionQualityMedium:*/
1392           value=kRenderQuality_Medium;
1393           break;
1394        case 0x0400: /*paMacCore_ConversionQualityHigh:*/
1395           value=kRenderQuality_High;
1396           break;
1397        }
1398        ERR_WRAP( AudioUnitSetProperty( *audioUnit,
1399                     kAudioUnitProperty_RenderQuality,
1400                     kAudioUnitScope_Global,
1401                     OUTPUT_ELEMENT,
1402                     &value,
1403                     sizeof(value) ) );
1404     }
1405     /* now set the format on the Audio Units. */
1406     if( outStreamParams )
1407     {
1408        desiredFormat.mSampleRate    =sampleRate;
1409        desiredFormat.mBytesPerPacket=sizeof(float)*outStreamParams->channelCount;
1410        desiredFormat.mBytesPerFrame =sizeof(float)*outStreamParams->channelCount;
1411        desiredFormat.mChannelsPerFrame = outStreamParams->channelCount;
1412        ERR_WRAP( AudioUnitSetProperty( *audioUnit,
1413                             kAudioUnitProperty_StreamFormat,
1414                             kAudioUnitScope_Input,
1415                             OUTPUT_ELEMENT,
1416                             &desiredFormat,
1417                             sizeof(AudioStreamBasicDescription) ) );
1418     }
1419     if( inStreamParams )
1420     {
1421        AudioStreamBasicDescription sourceFormat;
1422        UInt32 size = sizeof( AudioStreamBasicDescription );
1423 
1424        /* keep the sample rate of the device, or we confuse AUHAL */
1425        ERR_WRAP( AudioUnitGetProperty( *audioUnit,
1426                             kAudioUnitProperty_StreamFormat,
1427                             kAudioUnitScope_Input,
1428                             INPUT_ELEMENT,
1429                             &sourceFormat,
1430                             &size ) );
1431        desiredFormat.mSampleRate = sourceFormat.mSampleRate;
1432        desiredFormat.mBytesPerPacket=sizeof(float)*inStreamParams->channelCount;
1433        desiredFormat.mBytesPerFrame =sizeof(float)*inStreamParams->channelCount;
1434        desiredFormat.mChannelsPerFrame = inStreamParams->channelCount;
1435        ERR_WRAP( AudioUnitSetProperty( *audioUnit,
1436                             kAudioUnitProperty_StreamFormat,
1437                             kAudioUnitScope_Output,
1438                             INPUT_ELEMENT,
1439                             &desiredFormat,
1440                             sizeof(AudioStreamBasicDescription) ) );
1441     }
1442     /* set the maximumFramesPerSlice */
1443     /* not doing this causes real problems
1444        (eg. the callback might not be called). The idea of setting both this
1445        and the frames per buffer on the device is that we'll be most likely
1446        to actually get the frame size we requested in the callback with the
1447        minimum latency. */
1448     if( outStreamParams ) {
1449        UInt32 size = sizeof( *actualOutputFramesPerBuffer );
1450        ERR_WRAP( AudioUnitSetProperty( *audioUnit,
1451                             kAudioUnitProperty_MaximumFramesPerSlice,
1452                             kAudioUnitScope_Input,
1453                             OUTPUT_ELEMENT,
1454                             actualOutputFramesPerBuffer,
1455                             sizeof(*actualOutputFramesPerBuffer) ) );
1456        ERR_WRAP( AudioUnitGetProperty( *audioUnit,
1457                             kAudioUnitProperty_MaximumFramesPerSlice,
1458                             kAudioUnitScope_Global,
1459                             OUTPUT_ELEMENT,
1460                             actualOutputFramesPerBuffer,
1461                             &size ) );
1462     }
1463     if( inStreamParams ) {
1464        /*UInt32 size = sizeof( *actualInputFramesPerBuffer );*/
1465        ERR_WRAP( AudioUnitSetProperty( *audioUnit,
1466                             kAudioUnitProperty_MaximumFramesPerSlice,
1467                             kAudioUnitScope_Output,
1468                             INPUT_ELEMENT,
1469                             actualInputFramesPerBuffer,
1470                             sizeof(*actualInputFramesPerBuffer) ) );
1471 /* Don't know why this causes problems
1472        ERR_WRAP( AudioUnitGetProperty( *audioUnit,
1473                             kAudioUnitProperty_MaximumFramesPerSlice,
1474                             kAudioUnitScope_Global, //Output,
1475                             INPUT_ELEMENT,
1476                             actualInputFramesPerBuffer,
1477                             &size ) );
1478 */
1479     }
1480 
1481     /* -- if we have input, we may need to setup an SR converter -- */
1482     /* even if we got the sample rate we asked for, we need to do
1483        the conversion in case another program changes the underlying SR. */
1484     /* FIXME: I think we need to monitor stream and change the converter if the incoming format changes. */
1485     if( inStreamParams ) {
1486        AudioStreamBasicDescription desiredFormat;
1487        AudioStreamBasicDescription sourceFormat;
1488        UInt32 sourceSize = sizeof( sourceFormat );
1489        bzero( &desiredFormat, sizeof(desiredFormat) );
1490        desiredFormat.mSampleRate       = sampleRate;
1491        desiredFormat.mFormatID         = kAudioFormatLinearPCM ;
1492        desiredFormat.mFormatFlags      = kAudioFormatFlagsNativeFloatPacked;
1493        desiredFormat.mFramesPerPacket  = 1;
1494        desiredFormat.mBitsPerChannel   = sizeof( float ) * 8;
1495        desiredFormat.mBytesPerPacket=sizeof(float)*inStreamParams->channelCount;
1496        desiredFormat.mBytesPerFrame =sizeof(float)*inStreamParams->channelCount;
1497        desiredFormat.mChannelsPerFrame = inStreamParams->channelCount;
1498 
1499        /* get the source format */
1500        ERR_WRAP( AudioUnitGetProperty(
1501                          *audioUnit,
1502                          kAudioUnitProperty_StreamFormat,
1503                          kAudioUnitScope_Output,
1504                          INPUT_ELEMENT,
1505                          &sourceFormat,
1506                          &sourceSize ) );
1507 
1508        if( desiredFormat.mSampleRate != sourceFormat.mSampleRate )
1509        {
1510           UInt32 value = kAudioConverterQuality_Max;
1511           switch( macInputStreamFlags & 0x0700 ) {
1512           case 0x0100: /*paMacCore_ConversionQualityMin:*/
1513              value=kAudioConverterQuality_Min;
1514              break;
1515           case 0x0200: /*paMacCore_ConversionQualityLow:*/
1516              value=kAudioConverterQuality_Low;
1517              break;
1518           case 0x0300: /*paMacCore_ConversionQualityMedium:*/
1519              value=kAudioConverterQuality_Medium;
1520              break;
1521           case 0x0400: /*paMacCore_ConversionQualityHigh:*/
1522              value=kAudioConverterQuality_High;
1523              break;
1524           }
1525           VDBUG(( "Creating sample rate converter for input"
1526                   " to convert from %g to %g\n",
1527                   (float)sourceFormat.mSampleRate,
1528                   (float)desiredFormat.mSampleRate ) );
1529           /* create our converter */
1530           ERR_WRAP( AudioConverterNew(
1531                              &sourceFormat,
1532                              &desiredFormat,
1533                              srConverter ) );
1534           /* Set quality */
1535           ERR_WRAP( AudioConverterSetProperty(
1536                              *srConverter,
1537                              kAudioConverterSampleRateConverterQuality,
1538                              sizeof( value ),
1539                              &value ) );
1540        }
1541     }
1542     /* -- set IOProc (callback) -- */
1543     callbackKey = outStreamParams ? kAudioUnitProperty_SetRenderCallback
1544                                   : kAudioOutputUnitProperty_SetInputCallback ;
1545     rcbs.inputProc = AudioIOProc;
1546     rcbs.inputProcRefCon = refCon;
1547     ERR_WRAP( AudioUnitSetProperty(
1548                                *audioUnit,
1549                                callbackKey,
1550                                kAudioUnitScope_Output,
1551                                outStreamParams ? OUTPUT_ELEMENT : INPUT_ELEMENT,
1552                                &rcbs,
1553                                sizeof(rcbs)) );
1554 
1555     if( inStreamParams && outStreamParams && *srConverter )
1556            ERR_WRAP( AudioUnitSetProperty(
1557                                *audioUnit,
1558                                kAudioOutputUnitProperty_SetInputCallback,
1559                                kAudioUnitScope_Output,
1560                                INPUT_ELEMENT,
1561                                &rcbs,
1562                                sizeof(rcbs)) );
1563 
1564     /* channel mapping. */
1565     if(inChannelMap)
1566     {
1567         UInt32 mapSize = inChannelMapSize *sizeof(SInt32);
1568 
1569         //for each channel of desired input, map the channel from
1570         //the device's output channel.
1571         ERR_WRAP( AudioUnitSetProperty(*audioUnit,
1572                                 kAudioOutputUnitProperty_ChannelMap,
1573                                 kAudioUnitScope_Output,
1574                                 INPUT_ELEMENT,
1575                                 inChannelMap,
1576                                 mapSize));
1577     }
1578     if(outChannelMap)
1579     {
1580         UInt32 mapSize = outChannelMapSize *sizeof(SInt32);
1581 
1582         //for each channel of desired output, map the channel from
1583         //the device's output channel.
1584         ERR_WRAP(AudioUnitSetProperty(*audioUnit,
1585                                 kAudioOutputUnitProperty_ChannelMap,
1586                                 kAudioUnitScope_Output,
1587                                 OUTPUT_ELEMENT,
1588                                 outChannelMap,
1589                                 mapSize));
1590     }
1591     /* initialize the audio unit */
1592     ERR_WRAP( AudioUnitInitialize(*audioUnit) );
1593 
1594     if( inStreamParams && outStreamParams )
1595     {
1596         VDBUG( ("Opened device %ld for input and output.\n", *audioDevice ) );
1597     }
1598     else if( inStreamParams )
1599     {
1600         VDBUG( ("Opened device %ld for input.\n", *audioDevice ) );
1601     }
1602     else if( outStreamParams )
1603     {
1604         VDBUG( ("Opened device %ld for output.\n", *audioDevice ) );
1605     }
1606     return paNoError;
1607 #undef ERR_WRAP
1608 
1609     error:
1610        CloseComponent( *audioUnit );
1611        *audioUnit = NULL;
1612        if( result )
1613           return PaMacCore_SetError( result, line, 1 );
1614        return paResult;
1615 }
1616 
1617 /* =================================================================================================== */
1618 
CalculateOptimalBufferSize(PaMacAUHAL * auhalHostApi,const PaStreamParameters * inputParameters,const PaStreamParameters * outputParameters,UInt32 fixedInputLatency,UInt32 fixedOutputLatency,double sampleRate,UInt32 requestedFramesPerBuffer)1619 static UInt32 CalculateOptimalBufferSize( PaMacAUHAL *auhalHostApi,
1620                                   const PaStreamParameters *inputParameters,
1621                                   const PaStreamParameters *outputParameters,
1622                                   UInt32 fixedInputLatency,
1623                                   UInt32 fixedOutputLatency,
1624                                   double sampleRate,
1625                                   UInt32 requestedFramesPerBuffer )
1626 {
1627     UInt32 resultBufferSizeFrames = 0;
1628     // Use maximum of suggested input and output latencies.
1629     if( inputParameters )
1630     {
1631         UInt32 suggestedLatencyFrames = inputParameters->suggestedLatency * sampleRate;
1632         // Calculate a buffer size assuming we are double buffered.
1633         SInt32 variableLatencyFrames = suggestedLatencyFrames - fixedInputLatency;
1634         // Prevent negative latency.
1635         variableLatencyFrames = MAX( variableLatencyFrames, 0 );
1636         resultBufferSizeFrames = MAX( resultBufferSizeFrames, (UInt32) variableLatencyFrames );
1637     }
1638     if( outputParameters )
1639     {
1640         UInt32 suggestedLatencyFrames = outputParameters->suggestedLatency * sampleRate;
1641         SInt32 variableLatencyFrames = suggestedLatencyFrames - fixedOutputLatency;
1642         variableLatencyFrames = MAX( variableLatencyFrames, 0 );
1643         resultBufferSizeFrames = MAX( resultBufferSizeFrames, (UInt32) variableLatencyFrames );
1644     }
1645 
1646     // can't have zero frames. code to round up to next user buffer requires non-zero
1647     resultBufferSizeFrames = MAX( resultBufferSizeFrames, 1 );
1648 
1649     if( requestedFramesPerBuffer != paFramesPerBufferUnspecified )
1650     {
1651         // make host buffer the next highest integer multiple of user frames per buffer
1652         UInt32 n = (resultBufferSizeFrames + requestedFramesPerBuffer - 1) / requestedFramesPerBuffer;
1653         resultBufferSizeFrames = n * requestedFramesPerBuffer;
1654 
1655 
1656         // FIXME: really we should be searching for a multiple of requestedFramesPerBuffer
1657         // that is >= suggested latency and also fits within device buffer min/max
1658 
1659     }else{
1660     	VDBUG( ("Block Size unspecified. Based on Latency, the user wants a Block Size near: %ld.\n",
1661             resultBufferSizeFrames ) );
1662     }
1663 
1664     // Clip to the capabilities of the device.
1665     if( inputParameters )
1666     {
1667         ClipToDeviceBufferSize( auhalHostApi->devIds[inputParameters->device],
1668                                true, // In the old code isInput was false!
1669                                resultBufferSizeFrames, &resultBufferSizeFrames );
1670     }
1671     if( outputParameters )
1672     {
1673         ClipToDeviceBufferSize( auhalHostApi->devIds[outputParameters->device],
1674                                false, resultBufferSizeFrames, &resultBufferSizeFrames );
1675     }
1676     VDBUG(("After querying hardware, setting block size to %ld.\n", resultBufferSizeFrames));
1677 
1678     return resultBufferSizeFrames;
1679 }
1680 
1681 /* =================================================================================================== */
1682 /* see pa_hostapi.h for a list of validity guarantees made about OpenStream parameters */
OpenStream(struct PaUtilHostApiRepresentation * hostApi,PaStream ** s,const PaStreamParameters * inputParameters,const PaStreamParameters * outputParameters,double sampleRate,unsigned long requestedFramesPerBuffer,PaStreamFlags streamFlags,PaStreamCallback * streamCallback,void * userData)1683 static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
1684                            PaStream** s,
1685                            const PaStreamParameters *inputParameters,
1686                            const PaStreamParameters *outputParameters,
1687                            double sampleRate,
1688                            unsigned long requestedFramesPerBuffer,
1689                            PaStreamFlags streamFlags,
1690                            PaStreamCallback *streamCallback,
1691                            void *userData )
1692 {
1693     PaError result = paNoError;
1694     PaMacAUHAL *auhalHostApi = (PaMacAUHAL*)hostApi;
1695     PaMacCoreStream *stream = 0;
1696     int inputChannelCount, outputChannelCount;
1697     PaSampleFormat inputSampleFormat, outputSampleFormat;
1698     PaSampleFormat hostInputSampleFormat, hostOutputSampleFormat;
1699     UInt32 fixedInputLatency = 0;
1700     UInt32 fixedOutputLatency = 0;
1701     // Accumulate contributions to latency in these variables.
1702     UInt32 inputLatencyFrames = 0;
1703     UInt32 outputLatencyFrames = 0;
1704     UInt32 suggestedLatencyFramesPerBuffer = requestedFramesPerBuffer;
1705 
1706     VVDBUG(("OpenStream(): in chan=%d, in fmt=%ld, out chan=%d, out fmt=%ld SR=%g, FPB=%ld\n",
1707                 inputParameters  ? inputParameters->channelCount  : -1,
1708                 inputParameters  ? inputParameters->sampleFormat  : -1,
1709                 outputParameters ? outputParameters->channelCount : -1,
1710                 outputParameters ? outputParameters->sampleFormat : -1,
1711                 (float) sampleRate,
1712                 requestedFramesPerBuffer ));
1713     VDBUG( ("Opening Stream.\n") );
1714 
1715     /* These first few bits of code are from paSkeleton with few modifications. */
1716     if( inputParameters )
1717     {
1718         inputChannelCount = inputParameters->channelCount;
1719         inputSampleFormat = inputParameters->sampleFormat;
1720 
1721 		/* @todo Blocking read/write on Mac is not yet supported. */
1722 		if( !streamCallback && inputSampleFormat & paNonInterleaved )
1723 		{
1724 			return paSampleFormatNotSupported;
1725 		}
1726 
1727         /* unless alternate device specification is supported, reject the use of
1728             paUseHostApiSpecificDeviceSpecification */
1729 
1730         if( inputParameters->device == paUseHostApiSpecificDeviceSpecification )
1731             return paInvalidDevice;
1732 
1733         /* check that input device can support inputChannelCount */
1734         if( inputChannelCount > hostApi->deviceInfos[ inputParameters->device ]->maxInputChannels )
1735             return paInvalidChannelCount;
1736 
1737         /* Host supports interleaved float32 */
1738         hostInputSampleFormat = paFloat32;
1739     }
1740     else
1741     {
1742         inputChannelCount = 0;
1743         inputSampleFormat = hostInputSampleFormat = paFloat32; /* Surpress 'uninitialised var' warnings. */
1744     }
1745 
1746     if( outputParameters )
1747     {
1748         outputChannelCount = outputParameters->channelCount;
1749         outputSampleFormat = outputParameters->sampleFormat;
1750 
1751 		/* @todo Blocking read/write on Mac is not yet supported. */
1752 		if( !streamCallback && outputSampleFormat & paNonInterleaved )
1753 		{
1754 			return paSampleFormatNotSupported;
1755 		}
1756 
1757         /* unless alternate device specification is supported, reject the use of
1758             paUseHostApiSpecificDeviceSpecification */
1759 
1760         if( outputParameters->device == paUseHostApiSpecificDeviceSpecification )
1761             return paInvalidDevice;
1762 
1763         /* check that output device can support inputChannelCount */
1764         if( outputChannelCount > hostApi->deviceInfos[ outputParameters->device ]->maxOutputChannels )
1765             return paInvalidChannelCount;
1766 
1767         /* Host supports interleaved float32 */
1768         hostOutputSampleFormat = paFloat32;
1769     }
1770     else
1771     {
1772         outputChannelCount = 0;
1773         outputSampleFormat = hostOutputSampleFormat = paFloat32; /* Surpress 'uninitialized var' warnings. */
1774     }
1775 
1776     /* validate platform specific flags */
1777     if( (streamFlags & paPlatformSpecificFlags) != 0 )
1778         return paInvalidFlag; /* unexpected platform specific flag */
1779 
1780     stream = (PaMacCoreStream*)PaUtil_AllocateMemory( sizeof(PaMacCoreStream) );
1781     if( !stream )
1782     {
1783         result = paInsufficientMemory;
1784         goto error;
1785     }
1786 
1787     /* If we fail after this point, we my be left in a bad state, with
1788        some data structures setup and others not. So, first thing we
1789        do is initialize everything so that if we fail, we know what hasn't
1790        been touched.
1791      */
1792     bzero( stream, sizeof( PaMacCoreStream ) );
1793 
1794     /*
1795     stream->blio.inputRingBuffer.buffer = NULL;
1796     stream->blio.outputRingBuffer.buffer = NULL;
1797     stream->blio.inputSampleFormat = inputParameters?inputParameters->sampleFormat:0;
1798     stream->blio.inputSampleSize = computeSampleSizeFromFormat(stream->blio.inputSampleFormat);
1799     stream->blio.outputSampleFormat=outputParameters?outputParameters->sampleFormat:0;
1800     stream->blio.outputSampleSize = computeSampleSizeFromFormat(stream->blio.outputSampleFormat);
1801     */
1802 
1803     /* assert( streamCallback ) ; */ /* only callback mode is implemented */
1804     if( streamCallback )
1805     {
1806         PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation,
1807                                         &auhalHostApi->callbackStreamInterface,
1808                                         streamCallback, userData );
1809     }
1810     else
1811     {
1812         PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation,
1813                                         &auhalHostApi->blockingStreamInterface,
1814                                         BlioCallback, &stream->blio );
1815     }
1816 
1817     PaUtil_InitializeCpuLoadMeasurer( &stream->cpuLoadMeasurer, sampleRate );
1818 
1819 
1820     if( inputParameters )
1821     {
1822         CalculateFixedDeviceLatency( auhalHostApi->devIds[inputParameters->device], true, &fixedInputLatency );
1823         inputLatencyFrames += fixedInputLatency;
1824     }
1825     if( outputParameters )
1826     {
1827         CalculateFixedDeviceLatency( auhalHostApi->devIds[outputParameters->device], false, &fixedOutputLatency );
1828         outputLatencyFrames += fixedOutputLatency;
1829 
1830     }
1831 
1832     suggestedLatencyFramesPerBuffer = CalculateOptimalBufferSize( auhalHostApi, inputParameters, outputParameters,
1833                                                                  fixedInputLatency, fixedOutputLatency,
1834                                                                  sampleRate, requestedFramesPerBuffer );
1835     if( requestedFramesPerBuffer == paFramesPerBufferUnspecified )
1836 	{
1837         requestedFramesPerBuffer = suggestedLatencyFramesPerBuffer;
1838     }
1839 
1840     /* -- Now we actually open and setup streams. -- */
1841     if( inputParameters && outputParameters && outputParameters->device == inputParameters->device )
1842     { /* full duplex. One device. */
1843        UInt32 inputFramesPerBuffer  = (UInt32) stream->inputFramesPerBuffer;
1844        UInt32 outputFramesPerBuffer = (UInt32) stream->outputFramesPerBuffer;
1845        result = OpenAndSetupOneAudioUnit( stream,
1846                                           inputParameters,
1847                                           outputParameters,
1848                                           suggestedLatencyFramesPerBuffer,
1849                                           &inputFramesPerBuffer,
1850                                           &outputFramesPerBuffer,
1851                                           auhalHostApi,
1852                                           &(stream->inputUnit),
1853                                           &(stream->inputSRConverter),
1854                                           &(stream->inputDevice),
1855                                           sampleRate,
1856                                           stream );
1857        stream->inputFramesPerBuffer = inputFramesPerBuffer;
1858        stream->outputFramesPerBuffer = outputFramesPerBuffer;
1859        stream->outputUnit = stream->inputUnit;
1860        stream->outputDevice = stream->inputDevice;
1861        if( result != paNoError )
1862            goto error;
1863     }
1864     else
1865     { /* full duplex, different devices OR simplex */
1866        UInt32 outputFramesPerBuffer = (UInt32) stream->outputFramesPerBuffer;
1867        UInt32 inputFramesPerBuffer  = (UInt32) stream->inputFramesPerBuffer;
1868        result = OpenAndSetupOneAudioUnit( stream,
1869                                           NULL,
1870                                           outputParameters,
1871                                           suggestedLatencyFramesPerBuffer,
1872                                           NULL,
1873                                           &outputFramesPerBuffer,
1874                                           auhalHostApi,
1875                                           &(stream->outputUnit),
1876                                           NULL,
1877                                           &(stream->outputDevice),
1878                                           sampleRate,
1879                                           stream );
1880        if( result != paNoError )
1881            goto error;
1882        result = OpenAndSetupOneAudioUnit( stream,
1883                                           inputParameters,
1884                                           NULL,
1885                                           suggestedLatencyFramesPerBuffer,
1886                                           &inputFramesPerBuffer,
1887                                           NULL,
1888                                           auhalHostApi,
1889                                           &(stream->inputUnit),
1890                                           &(stream->inputSRConverter),
1891                                           &(stream->inputDevice),
1892                                           sampleRate,
1893                                           stream );
1894        if( result != paNoError )
1895            goto error;
1896        stream->inputFramesPerBuffer = inputFramesPerBuffer;
1897        stream->outputFramesPerBuffer = outputFramesPerBuffer;
1898     }
1899 
1900     inputLatencyFrames += stream->inputFramesPerBuffer;
1901     outputLatencyFrames += stream->outputFramesPerBuffer;
1902 
1903     if( stream->inputUnit ) {
1904        const size_t szfl = sizeof(float);
1905        /* setup the AudioBufferList used for input */
1906        bzero( &stream->inputAudioBufferList, sizeof( AudioBufferList ) );
1907        stream->inputAudioBufferList.mNumberBuffers = 1;
1908        stream->inputAudioBufferList.mBuffers[0].mNumberChannels
1909                  = inputChannelCount;
1910        stream->inputAudioBufferList.mBuffers[0].mDataByteSize
1911                  = stream->inputFramesPerBuffer*inputChannelCount*szfl;
1912        stream->inputAudioBufferList.mBuffers[0].mData
1913                  = (float *) calloc(
1914                                stream->inputFramesPerBuffer*inputChannelCount,
1915                                szfl );
1916        if( !stream->inputAudioBufferList.mBuffers[0].mData )
1917        {
1918           result = paInsufficientMemory;
1919           goto error;
1920        }
1921 
1922        /*
1923         * If input and output devs are different or we are doing SR conversion,
1924         * we also need a ring buffer to store input data while waiting for
1925         * output data.
1926         */
1927        if( (stream->outputUnit && (stream->inputUnit != stream->outputUnit))
1928            || stream->inputSRConverter )
1929        {
1930           /* May want the ringSize or initial position in
1931              ring buffer to depend somewhat on sample rate change */
1932 
1933           void *data;
1934           long ringSize;
1935 
1936           ringSize = computeRingBufferSize( inputParameters,
1937                                             outputParameters,
1938                                             stream->inputFramesPerBuffer,
1939                                             stream->outputFramesPerBuffer,
1940                                             sampleRate );
1941           /*ringSize <<= 4; *//*16x bigger, for testing */
1942 
1943 
1944           /*now, we need to allocate memory for the ring buffer*/
1945           data = calloc( ringSize, szfl*inputParameters->channelCount );
1946           if( !data )
1947           {
1948              result = paInsufficientMemory;
1949              goto error;
1950           }
1951 
1952           /* now we can initialize the ring buffer */
1953           result = PaUtil_InitializeRingBuffer( &stream->inputRingBuffer, szfl*inputParameters->channelCount, ringSize, data );
1954           if( result != 0 )
1955           {
1956               /* The only reason this should fail is if ringSize is not a power of 2, which we do not anticipate happening. */
1957               result = paUnanticipatedHostError;
1958               free(data);
1959               goto error;
1960           }
1961 
1962           /* advance the read point a little, so we are reading from the
1963              middle of the buffer */
1964           if( stream->outputUnit )
1965              PaUtil_AdvanceRingBufferWriteIndex( &stream->inputRingBuffer, ringSize / RING_BUFFER_ADVANCE_DENOMINATOR );
1966 
1967            // Just adds to input latency between input device and PA full duplex callback.
1968            inputLatencyFrames += ringSize;
1969        }
1970     }
1971 
1972     /* -- initialize Blio Buffer Processors -- */
1973     if( !streamCallback )
1974     {
1975        long ringSize;
1976 
1977        ringSize = computeRingBufferSize( inputParameters,
1978                                          outputParameters,
1979                                          stream->inputFramesPerBuffer,
1980                                          stream->outputFramesPerBuffer,
1981                                          sampleRate );
1982        result = initializeBlioRingBuffers( &stream->blio,
1983               inputParameters ? inputParameters->sampleFormat : 0,
1984               outputParameters ? outputParameters->sampleFormat : 0,
1985               ringSize,
1986               inputParameters ? inputChannelCount : 0,
1987               outputParameters ? outputChannelCount : 0 ) ;
1988        if( result != paNoError )
1989           goto error;
1990 
1991         inputLatencyFrames += ringSize;
1992         outputLatencyFrames += ringSize;
1993 
1994     }
1995 
1996     /* -- initialize Buffer Processor -- */
1997     {
1998        unsigned long maxHostFrames = stream->inputFramesPerBuffer;
1999        if( stream->outputFramesPerBuffer > maxHostFrames )
2000           maxHostFrames = stream->outputFramesPerBuffer;
2001        result = PaUtil_InitializeBufferProcessor( &stream->bufferProcessor,
2002                  inputChannelCount, inputSampleFormat,
2003                  hostInputSampleFormat,
2004                  outputChannelCount, outputSampleFormat,
2005                  hostOutputSampleFormat,
2006                  sampleRate,
2007                  streamFlags,
2008                  requestedFramesPerBuffer,
2009                  /* If sample rate conversion takes place, the buffer size
2010                     will not be known. */
2011                  maxHostFrames,
2012                  stream->inputSRConverter
2013                               ? paUtilUnknownHostBufferSize
2014                               : paUtilBoundedHostBufferSize,
2015                  streamCallback ? streamCallback : BlioCallback,
2016                  streamCallback ? userData : &stream->blio );
2017        if( result != paNoError )
2018            goto error;
2019     }
2020     stream->bufferProcessorIsInitialized = TRUE;
2021 
2022     // Calculate actual latency from the sum of individual latencies.
2023     if( inputParameters )
2024     {
2025         inputLatencyFrames += PaUtil_GetBufferProcessorInputLatencyFrames(&stream->bufferProcessor);
2026         stream->streamRepresentation.streamInfo.inputLatency = inputLatencyFrames / sampleRate;
2027     }
2028     else
2029     {
2030         stream->streamRepresentation.streamInfo.inputLatency = 0.0;
2031     }
2032 
2033     if( outputParameters )
2034     {
2035         outputLatencyFrames += PaUtil_GetBufferProcessorOutputLatencyFrames(&stream->bufferProcessor);
2036         stream->streamRepresentation.streamInfo.outputLatency = outputLatencyFrames / sampleRate;
2037     }
2038     else
2039     {
2040         stream->streamRepresentation.streamInfo.outputLatency = 0.0;
2041     }
2042 
2043     stream->streamRepresentation.streamInfo.sampleRate = sampleRate;
2044 
2045     stream->sampleRate = sampleRate;
2046 
2047     stream->userInChan  = inputChannelCount;
2048     stream->userOutChan = outputChannelCount;
2049 
2050     // Setup property listeners for timestamp and latency calculations.
2051 	pthread_mutex_init( &stream->timingInformationMutex, NULL );
2052 	stream->timingInformationMutexIsInitialized = 1;
2053     InitializeDeviceProperties( &stream->inputProperties );     // zeros the struct. doesn't actually init it to useful values
2054     InitializeDeviceProperties( &stream->outputProperties );    // zeros the struct. doesn't actually init it to useful values
2055 	if( stream->outputUnit )
2056     {
2057         Boolean isInput = FALSE;
2058 
2059         // Start with the current values for the device properties.
2060         // Init with nominal sample rate. Use actual sample rate where available
2061 
2062         result = ERR( UpdateSampleRateFromDeviceProperty(
2063                 stream, stream->outputDevice, isInput, kAudioDevicePropertyNominalSampleRate )  );
2064         if( result )
2065             goto error; /* fail if we can't even get a nominal device sample rate */
2066 
2067         UpdateSampleRateFromDeviceProperty( stream, stream->outputDevice, isInput, kAudioDevicePropertyActualSampleRate );
2068 
2069         SetupDevicePropertyListeners( stream, stream->outputDevice, isInput );
2070     }
2071 	if( stream->inputUnit )
2072     {
2073         Boolean isInput = TRUE;
2074 
2075         // as above
2076         result = ERR( UpdateSampleRateFromDeviceProperty(
2077                 stream, stream->inputDevice, isInput, kAudioDevicePropertyNominalSampleRate )  );
2078         if( result )
2079             goto error;
2080 
2081         UpdateSampleRateFromDeviceProperty( stream, stream->inputDevice, isInput, kAudioDevicePropertyActualSampleRate );
2082 
2083         SetupDevicePropertyListeners( stream, stream->inputDevice, isInput );
2084 	}
2085     UpdateTimeStampOffsets( stream );
2086     // Setup timestamp copies to be used by audio callback.
2087     stream->timestampOffsetCombined_ioProcCopy = stream->timestampOffsetCombined;
2088     stream->timestampOffsetInputDevice_ioProcCopy = stream->timestampOffsetInputDevice;
2089     stream->timestampOffsetOutputDevice_ioProcCopy = stream->timestampOffsetOutputDevice;
2090 
2091     stream->state = STOPPED;
2092     stream->xrunFlags = 0;
2093 
2094     *s = (PaStream*)stream;
2095 
2096     return result;
2097 
2098 error:
2099     CloseStream( stream );
2100     return result;
2101 }
2102 
2103 
2104 #define HOST_TIME_TO_PA_TIME( x ) ( AudioConvertHostTimeToNanos( (x) ) * 1.0E-09) /* convert to nanoseconds and then to seconds */
2105 
GetStreamTime(PaStream * s)2106 PaTime GetStreamTime( PaStream *s )
2107 {
2108 	return HOST_TIME_TO_PA_TIME( AudioGetCurrentHostTime() );
2109 }
2110 
2111 #define RING_BUFFER_EMPTY (1000)
2112 
ringBufferIOProc(AudioConverterRef inAudioConverter,UInt32 * ioDataSize,void ** outData,void * inUserData)2113 static OSStatus ringBufferIOProc( AudioConverterRef inAudioConverter,
2114                              UInt32*ioDataSize,
2115                              void** outData,
2116                              void*inUserData )
2117 {
2118    void *dummyData;
2119    ring_buffer_size_t dummySize;
2120    PaUtilRingBuffer *rb = (PaUtilRingBuffer *) inUserData;
2121 
2122    VVDBUG(("ringBufferIOProc()\n"));
2123 
2124    if( PaUtil_GetRingBufferReadAvailable( rb ) == 0 ) {
2125       *outData = NULL;
2126       *ioDataSize = 0;
2127       return RING_BUFFER_EMPTY;
2128    }
2129    assert(sizeof(UInt32) == sizeof(ring_buffer_size_t));
2130    assert( ( (*ioDataSize) / rb->elementSizeBytes ) * rb->elementSizeBytes == (*ioDataSize) ) ;
2131    (*ioDataSize) /= rb->elementSizeBytes ;
2132    PaUtil_GetRingBufferReadRegions( rb, *ioDataSize,
2133                                     outData, (ring_buffer_size_t *)ioDataSize,
2134                                     &dummyData, &dummySize );
2135    assert( *ioDataSize );
2136    PaUtil_AdvanceRingBufferReadIndex( rb, *ioDataSize );
2137    (*ioDataSize) *= rb->elementSizeBytes ;
2138 
2139    return noErr;
2140 }
2141 
2142 /*
2143  * Called by the AudioUnit API to process audio from the sound card.
2144  * This is where the magic happens.
2145  */
2146 /* 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. */
AudioIOProc(void * inRefCon,AudioUnitRenderActionFlags * ioActionFlags,const AudioTimeStamp * inTimeStamp,UInt32 inBusNumber,UInt32 inNumberFrames,AudioBufferList * ioData)2147 static OSStatus AudioIOProc( void *inRefCon,
2148                                AudioUnitRenderActionFlags *ioActionFlags,
2149                                const AudioTimeStamp *inTimeStamp,
2150                                UInt32 inBusNumber,
2151                                UInt32 inNumberFrames,
2152                                AudioBufferList *ioData )
2153 {
2154    unsigned long framesProcessed     = 0;
2155    PaStreamCallbackTimeInfo timeInfo = {0,0,0};
2156    PaMacCoreStream *stream           = (PaMacCoreStream*)inRefCon;
2157    const bool isRender               = inBusNumber == OUTPUT_ELEMENT;
2158    int callbackResult                = paContinue ;
2159    double hostTimeStampInPaTime      = HOST_TIME_TO_PA_TIME(inTimeStamp->mHostTime);
2160 
2161    VVDBUG(("AudioIOProc()\n"));
2162 
2163    PaUtil_BeginCpuLoadMeasurement( &stream->cpuLoadMeasurer );
2164 
2165    /* -----------------------------------------------------------------*\
2166       This output may be useful for debugging,
2167       But printing durring the callback is a bad enough idea that
2168       this is not enabled by enableing the usual debugging calls.
2169    \* -----------------------------------------------------------------*/
2170    /*
2171    static int renderCount = 0;
2172    static int inputCount = 0;
2173    printf( "-------------------  starting reder/input\n" );
2174    if( isRender )
2175       printf("Render callback (%d):\t", ++renderCount);
2176    else
2177       printf("Input callback  (%d):\t", ++inputCount);
2178    printf( "Call totals: %d (input), %d (render)\n", inputCount, renderCount );
2179 
2180    printf( "--- inBusNumber: %lu\n", inBusNumber );
2181    printf( "--- inNumberFrames: %lu\n", inNumberFrames );
2182    printf( "--- %x ioData\n", (unsigned) ioData );
2183    if( ioData )
2184    {
2185       int i=0;
2186       printf( "--- ioData.mNumBuffers %lu: \n", ioData->mNumberBuffers );
2187       for( i=0; i<ioData->mNumberBuffers; ++i )
2188          printf( "--- ioData buffer %d size: %lu.\n", i, ioData->mBuffers[i].mDataByteSize );
2189    }
2190       ----------------------------------------------------------------- */
2191 
2192 	/* compute PaStreamCallbackTimeInfo */
2193 
2194 	if( pthread_mutex_trylock( &stream->timingInformationMutex ) == 0 ){
2195 		/* snapshot the ioproc copy of timing information */
2196 		stream->timestampOffsetCombined_ioProcCopy = stream->timestampOffsetCombined;
2197 		stream->timestampOffsetInputDevice_ioProcCopy = stream->timestampOffsetInputDevice;
2198 		stream->timestampOffsetOutputDevice_ioProcCopy = stream->timestampOffsetOutputDevice;
2199 		pthread_mutex_unlock( &stream->timingInformationMutex );
2200 	}
2201 
2202 	/* For timeInfo.currentTime we could calculate current time backwards from the HAL audio
2203 	 output time to give a more accurate impression of the current timeslice but it doesn't
2204 	 seem worth it at the moment since other PA host APIs don't do any better.
2205 	 */
2206 	timeInfo.currentTime = HOST_TIME_TO_PA_TIME( AudioGetCurrentHostTime() );
2207 
2208 	/*
2209 	 For an input HAL AU, inTimeStamp is the time the samples are received from the hardware,
2210 	 for an output HAL AU inTimeStamp is the time the samples are sent to the hardware.
2211 	 PA expresses timestamps in terms of when the samples enter the ADC or leave the DAC
2212 	 so we add or subtract kAudioDevicePropertyLatency below.
2213 	 */
2214 
2215 	/* FIXME: not sure what to do below if the host timestamps aren't valid (kAudioTimeStampHostTimeValid isn't set)
2216 	 Could ask on CA mailing list if it is possible for it not to be set. If so, could probably grab a now timestamp
2217 	 at the top and compute from there (modulo scheduling jitter) or ask on mailing list for other options. */
2218 
2219 	if( isRender )
2220 	{
2221 		if( stream->inputUnit ) /* full duplex */
2222 		{
2223 			if( stream->inputUnit == stream->outputUnit ) /* full duplex AUHAL IOProc */
2224 			{
2225                 // Ross and Phil agreed that the following calculation is correct based on an email from Jeff Moore:
2226                 // http://osdir.com/ml/coreaudio-api/2009-07/msg00140.html
2227                 // Basically the difference between the Apple output timestamp and the PA timestamp is kAudioDevicePropertyLatency.
2228 				timeInfo.inputBufferAdcTime = hostTimeStampInPaTime -
2229                     (stream->timestampOffsetCombined_ioProcCopy + stream->timestampOffsetInputDevice_ioProcCopy);
2230  				timeInfo.outputBufferDacTime = hostTimeStampInPaTime + stream->timestampOffsetOutputDevice_ioProcCopy;
2231 			}
2232 			else /* full duplex with ring-buffer from a separate input AUHAL ioproc */
2233 			{
2234 				/* FIXME: take the ring buffer latency into account */
2235 				timeInfo.inputBufferAdcTime = hostTimeStampInPaTime -
2236                     (stream->timestampOffsetCombined_ioProcCopy + stream->timestampOffsetInputDevice_ioProcCopy);
2237 				timeInfo.outputBufferDacTime = hostTimeStampInPaTime + stream->timestampOffsetOutputDevice_ioProcCopy;
2238 			}
2239 		}
2240 		else /* output only */
2241 		{
2242 			timeInfo.inputBufferAdcTime = 0;
2243 			timeInfo.outputBufferDacTime = hostTimeStampInPaTime + stream->timestampOffsetOutputDevice_ioProcCopy;
2244 		}
2245 	}
2246 	else /* input only */
2247 	{
2248 		timeInfo.inputBufferAdcTime = hostTimeStampInPaTime - stream->timestampOffsetInputDevice_ioProcCopy;
2249 		timeInfo.outputBufferDacTime = 0;
2250 	}
2251 
2252    //printf( "---%g, %g, %g\n", timeInfo.inputBufferAdcTime, timeInfo.currentTime, timeInfo.outputBufferDacTime );
2253 
2254    if( isRender && stream->inputUnit == stream->outputUnit
2255                 && !stream->inputSRConverter )
2256    {
2257       /* --------- Full Duplex, One Device, no SR Conversion -------
2258        *
2259        * This is the lowest latency case, and also the simplest.
2260        * Input data and output data are available at the same time.
2261        * we do not use the input SR converter or the input ring buffer.
2262        *
2263        */
2264       OSStatus err = 0;
2265        unsigned long frames;
2266        long bytesPerFrame = sizeof( float ) * ioData->mBuffers[0].mNumberChannels;
2267 
2268       /* -- start processing -- */
2269       PaUtil_BeginBufferProcessing( &(stream->bufferProcessor),
2270                                     &timeInfo,
2271                                     stream->xrunFlags );
2272       stream->xrunFlags = 0; //FIXME: this flag also gets set outside by a callback, which calls the xrunCallback function. It should be in the same thread as the main audio callback, but the apple docs just use the word "usually" so it may be possible to loose an xrun notification, if that callback happens here.
2273 
2274       /* -- compute frames. do some checks -- */
2275       assert( ioData->mNumberBuffers == 1 );
2276       assert( ioData->mBuffers[0].mNumberChannels == stream->userOutChan );
2277 
2278       frames = ioData->mBuffers[0].mDataByteSize / bytesPerFrame;
2279       /* -- copy and process input data -- */
2280       err= AudioUnitRender(stream->inputUnit,
2281                     ioActionFlags,
2282                     inTimeStamp,
2283                     INPUT_ELEMENT,
2284                     inNumberFrames,
2285                     &stream->inputAudioBufferList );
2286       if(err != noErr)
2287       {
2288         goto stop_stream;
2289       }
2290 
2291       PaUtil_SetInputFrameCount( &(stream->bufferProcessor), frames );
2292       PaUtil_SetInterleavedInputChannels( &(stream->bufferProcessor),
2293                           0,
2294                           stream->inputAudioBufferList.mBuffers[0].mData,
2295                           stream->inputAudioBufferList.mBuffers[0].mNumberChannels);
2296       /* -- Copy and process output data -- */
2297       PaUtil_SetOutputFrameCount( &(stream->bufferProcessor), frames );
2298       PaUtil_SetInterleavedOutputChannels( &(stream->bufferProcessor),
2299                                         0,
2300                                         ioData->mBuffers[0].mData,
2301                                         ioData->mBuffers[0].mNumberChannels);
2302       /* -- complete processing -- */
2303       framesProcessed =
2304                  PaUtil_EndBufferProcessing( &(stream->bufferProcessor),
2305                                              &callbackResult );
2306    }
2307    else if( isRender )
2308    {
2309       /* -------- Output Side of Full Duplex (Separate Devices or SR Conversion)
2310        *       -- OR Simplex Output
2311        *
2312        * This case handles output data as in the full duplex case,
2313        * and, if there is input data, reads it off the ring buffer
2314        * and into the PA buffer processor. If sample rate conversion
2315        * is required on input, that is done here as well.
2316        */
2317        unsigned long frames;
2318        long bytesPerFrame = sizeof( float ) * ioData->mBuffers[0].mNumberChannels;
2319 
2320       /* Sometimes, when stopping a duplex stream we get erroneous
2321          xrun flags, so if this is our last run, clear the flags. */
2322       int xrunFlags = stream->xrunFlags;
2323 /*
2324       if( xrunFlags & paInputUnderflow )
2325          printf( "input underflow.\n" );
2326       if( xrunFlags & paInputOverflow )
2327          printf( "input overflow.\n" );
2328 */
2329       if( stream->state == STOPPING || stream->state == CALLBACK_STOPPED )
2330          xrunFlags = 0;
2331 
2332       /* -- start processing -- */
2333       PaUtil_BeginBufferProcessing( &(stream->bufferProcessor),
2334                                     &timeInfo,
2335                                     xrunFlags );
2336       stream->xrunFlags = 0; /* FEEDBACK: we only send flags to Buf Proc once */
2337 
2338       /* -- Copy and process output data -- */
2339       assert( ioData->mNumberBuffers == 1 );
2340       frames = ioData->mBuffers[0].mDataByteSize / bytesPerFrame;
2341       assert( ioData->mBuffers[0].mNumberChannels == stream->userOutChan );
2342       PaUtil_SetOutputFrameCount( &(stream->bufferProcessor), frames );
2343       PaUtil_SetInterleavedOutputChannels( &(stream->bufferProcessor),
2344                                      0,
2345                                      ioData->mBuffers[0].mData,
2346                                      ioData->mBuffers[0].mNumberChannels);
2347 
2348       /* -- copy and process input data, and complete processing -- */
2349       if( stream->inputUnit ) {
2350          const int flsz = sizeof( float );
2351          /* Here, we read the data out of the ring buffer, through the
2352             audio converter. */
2353          int inChan = stream->inputAudioBufferList.mBuffers[0].mNumberChannels;
2354          long bytesPerFrame = flsz * inChan;
2355 
2356          if( stream->inputSRConverter )
2357          {
2358                OSStatus err;
2359                UInt32 size;
2360                float data[ inChan * frames ];
2361                size = sizeof( data );
2362                err = AudioConverterFillBuffer(
2363                              stream->inputSRConverter,
2364                              ringBufferIOProc,
2365                              &stream->inputRingBuffer,
2366                              &size,
2367                              (void *)&data );
2368                if( err == RING_BUFFER_EMPTY )
2369                { /* the ring buffer callback underflowed */
2370                   err = 0;
2371                   bzero( ((char *)data) + size, sizeof(data)-size );
2372                   /* The ring buffer can underflow normally when the stream is stopping.
2373                    * So only report an error if the stream is active. */
2374                   if( stream->state == ACTIVE )
2375                   {
2376                       stream->xrunFlags |= paInputUnderflow;
2377                   }
2378                }
2379                ERR( err );
2380                if(err != noErr)
2381                {
2382                  goto stop_stream;
2383                }
2384 
2385                PaUtil_SetInputFrameCount( &(stream->bufferProcessor), frames );
2386                PaUtil_SetInterleavedInputChannels( &(stream->bufferProcessor),
2387                                    0,
2388                                    data,
2389                                    inChan );
2390                framesProcessed =
2391                     PaUtil_EndBufferProcessing( &(stream->bufferProcessor),
2392                                                 &callbackResult );
2393          }
2394          else
2395          {
2396             /* Without the AudioConverter is actually a bit more complex
2397                because we have to do a little buffer processing that the
2398                AudioConverter would otherwise handle for us. */
2399             void *data1, *data2;
2400             ring_buffer_size_t size1, size2;
2401             ring_buffer_size_t framesReadable = PaUtil_GetRingBufferReadRegions( &stream->inputRingBuffer,
2402                                              frames,
2403                                              &data1, &size1,
2404                                              &data2, &size2 );
2405             if( size1 == frames ) {
2406                /* simplest case: all in first buffer */
2407                PaUtil_SetInputFrameCount( &(stream->bufferProcessor), frames );
2408                PaUtil_SetInterleavedInputChannels( &(stream->bufferProcessor),
2409                                    0,
2410                                    data1,
2411                                    inChan );
2412                framesProcessed =
2413                     PaUtil_EndBufferProcessing( &(stream->bufferProcessor),
2414                                                 &callbackResult );
2415                PaUtil_AdvanceRingBufferReadIndex(&stream->inputRingBuffer, size1 );
2416             } else if( framesReadable < frames ) {
2417 
2418                 long sizeBytes1 = size1 * bytesPerFrame;
2419                 long sizeBytes2 = size2 * bytesPerFrame;
2420                /*we underflowed. take what data we can, zero the rest.*/
2421                unsigned char data[ frames * bytesPerFrame ];
2422                if( size1 > 0 )
2423                {
2424                    memcpy( data, data1, sizeBytes1 );
2425                }
2426                if( size2 > 0 )
2427                {
2428                    memcpy( data+sizeBytes1, data2, sizeBytes2 );
2429                }
2430                bzero( data+sizeBytes1+sizeBytes2, (frames*bytesPerFrame) - sizeBytes1 - sizeBytes2 );
2431 
2432                PaUtil_SetInputFrameCount( &(stream->bufferProcessor), frames );
2433                PaUtil_SetInterleavedInputChannels( &(stream->bufferProcessor),
2434                                    0,
2435                                    data,
2436                                    inChan );
2437                framesProcessed =
2438                     PaUtil_EndBufferProcessing( &(stream->bufferProcessor),
2439                                                 &callbackResult );
2440                PaUtil_AdvanceRingBufferReadIndex( &stream->inputRingBuffer,
2441                                                   framesReadable );
2442                /* flag underflow */
2443                stream->xrunFlags |= paInputUnderflow;
2444             } else {
2445                /*we got all the data, but split between buffers*/
2446                PaUtil_SetInputFrameCount( &(stream->bufferProcessor), size1 );
2447                PaUtil_SetInterleavedInputChannels( &(stream->bufferProcessor),
2448                                    0,
2449                                    data1,
2450                                    inChan );
2451                PaUtil_Set2ndInputFrameCount( &(stream->bufferProcessor), size2 );
2452                PaUtil_Set2ndInterleavedInputChannels( &(stream->bufferProcessor),
2453                                    0,
2454                                    data2,
2455                                    inChan );
2456                framesProcessed =
2457                     PaUtil_EndBufferProcessing( &(stream->bufferProcessor),
2458                                                 &callbackResult );
2459                PaUtil_AdvanceRingBufferReadIndex(&stream->inputRingBuffer, framesReadable );
2460             }
2461          }
2462       } else {
2463          framesProcessed =
2464                  PaUtil_EndBufferProcessing( &(stream->bufferProcessor),
2465                                              &callbackResult );
2466       }
2467 
2468    }
2469    else
2470    {
2471       /* ------------------ Input
2472        *
2473        * First, we read off the audio data and put it in the ring buffer.
2474        * if this is an input-only stream, we need to process it more,
2475        * otherwise, we let the output case deal with it.
2476        */
2477       OSStatus err = 0;
2478       int chan = stream->inputAudioBufferList.mBuffers[0].mNumberChannels ;
2479       /* FIXME: looping here may not actually be necessary, but it was something I tried in testing. */
2480       do {
2481          err= AudioUnitRender(stream->inputUnit,
2482                  ioActionFlags,
2483                  inTimeStamp,
2484                  INPUT_ELEMENT,
2485                  inNumberFrames,
2486                  &stream->inputAudioBufferList );
2487          if( err == -10874 )
2488             inNumberFrames /= 2;
2489       } while( err == -10874 && inNumberFrames > 1 );
2490       ERR( err );
2491       if(err != noErr)
2492       {
2493           goto stop_stream;
2494       }
2495 
2496       if( stream->inputSRConverter || stream->outputUnit )
2497       {
2498          /* If this is duplex or we use a converter, put the data
2499             into the ring buffer. */
2500           ring_buffer_size_t framesWritten = PaUtil_WriteRingBuffer( &stream->inputRingBuffer,
2501                                             stream->inputAudioBufferList.mBuffers[0].mData,
2502                                             inNumberFrames );
2503          if( framesWritten != inNumberFrames )
2504          {
2505              stream->xrunFlags |= paInputOverflow ;
2506          }
2507       }
2508       else
2509       {
2510          /* for simplex input w/o SR conversion,
2511             just pop the data into the buffer processor.*/
2512          PaUtil_BeginBufferProcessing( &(stream->bufferProcessor),
2513                               &timeInfo,
2514                               stream->xrunFlags );
2515          stream->xrunFlags = 0;
2516 
2517          PaUtil_SetInputFrameCount( &(stream->bufferProcessor), inNumberFrames);
2518          PaUtil_SetInterleavedInputChannels( &(stream->bufferProcessor),
2519                              0,
2520                              stream->inputAudioBufferList.mBuffers[0].mData,
2521                              chan );
2522          framesProcessed =
2523               PaUtil_EndBufferProcessing( &(stream->bufferProcessor),
2524                                           &callbackResult );
2525       }
2526       if( !stream->outputUnit && stream->inputSRConverter )
2527       {
2528          /* ------------------ Simplex Input w/ SR Conversion
2529           *
2530           * if this is a simplex input stream, we need to read off the buffer,
2531           * do our sample rate conversion and pass the results to the buffer
2532           * processor.
2533           * The logic here is complicated somewhat by the fact that we don't
2534           * know how much data is available, so we loop on reasonably sized
2535           * chunks, and let the BufferProcessor deal with the rest.
2536           *
2537           */
2538          /* This might be too big or small depending on SR conversion. */
2539          float data[ chan * inNumberFrames ];
2540          OSStatus err;
2541          do
2542          { /* Run the buffer processor until we are out of data. */
2543             UInt32 size;
2544             long f;
2545 
2546             size = sizeof( data );
2547             err = AudioConverterFillBuffer(
2548                           stream->inputSRConverter,
2549                           ringBufferIOProc,
2550                           &stream->inputRingBuffer,
2551                           &size,
2552                           (void *)data );
2553             if( err != RING_BUFFER_EMPTY )
2554                ERR( err );
2555             if( err != noErr && err != RING_BUFFER_EMPTY )
2556             {
2557                 goto stop_stream;
2558             }
2559 
2560 
2561             f = size / ( chan * sizeof(float) );
2562             PaUtil_SetInputFrameCount( &(stream->bufferProcessor), f );
2563             if( f )
2564             {
2565                PaUtil_BeginBufferProcessing( &(stream->bufferProcessor),
2566                                              &timeInfo,
2567                                              stream->xrunFlags );
2568                stream->xrunFlags = 0;
2569 
2570                PaUtil_SetInterleavedInputChannels( &(stream->bufferProcessor),
2571                                 0,
2572                                 data,
2573                                 chan );
2574                framesProcessed =
2575                     PaUtil_EndBufferProcessing( &(stream->bufferProcessor),
2576                                                 &callbackResult );
2577             }
2578          } while( callbackResult == paContinue && !err );
2579       }
2580    }
2581 
2582     // Should we return successfully or fall through to stopping the stream?
2583     if( callbackResult == paContinue )
2584     {
2585         PaUtil_EndCpuLoadMeasurement( &stream->cpuLoadMeasurer, framesProcessed );
2586         return noErr;
2587     }
2588 
2589 stop_stream:
2590     stream->state = CALLBACK_STOPPED ;
2591     if( stream->outputUnit )
2592         AudioOutputUnitStop(stream->outputUnit);
2593     if( stream->inputUnit )
2594         AudioOutputUnitStop(stream->inputUnit);
2595 
2596     PaUtil_EndCpuLoadMeasurement( &stream->cpuLoadMeasurer, framesProcessed );
2597     return noErr;
2598 }
2599 
2600 /*
2601     When CloseStream() is called, the multi-api layer ensures that
2602     the stream has already been stopped or aborted.
2603 */
CloseStream(PaStream * s)2604 static PaError CloseStream( PaStream* s )
2605 {
2606     /* This may be called from a failed OpenStream.
2607        Therefore, each piece of info is treated seperately. */
2608     PaError result = paNoError;
2609     PaMacCoreStream *stream = (PaMacCoreStream*)s;
2610 
2611     VVDBUG(("CloseStream()\n"));
2612     VDBUG( ( "Closing stream.\n" ) );
2613 
2614     if( stream ) {
2615 
2616 		if( stream->outputUnit )
2617         {
2618             Boolean isInput = FALSE;
2619             CleanupDevicePropertyListeners( stream, stream->outputDevice, isInput );
2620 		}
2621 
2622 		if( stream->inputUnit )
2623         {
2624             Boolean isInput = TRUE;
2625             CleanupDevicePropertyListeners( stream, stream->inputDevice, isInput );
2626 		}
2627 
2628        if( stream->outputUnit ) {
2629           int count = removeFromXRunListenerList( stream );
2630           if( count == 0 )
2631              AudioDeviceRemovePropertyListener( stream->outputDevice,
2632                                                 0,
2633                                                 false,
2634                                                 kAudioDeviceProcessorOverload,
2635                                                 xrunCallback );
2636        }
2637        if( stream->inputUnit && stream->outputUnit != stream->inputUnit ) {
2638           int count = removeFromXRunListenerList( stream );
2639           if( count == 0 )
2640              AudioDeviceRemovePropertyListener( stream->inputDevice,
2641                                                 0,
2642                                                 true,
2643                                                 kAudioDeviceProcessorOverload,
2644                                                 xrunCallback );
2645        }
2646        if( stream->outputUnit && stream->outputUnit != stream->inputUnit ) {
2647           AudioUnitUninitialize( stream->outputUnit );
2648           CloseComponent( stream->outputUnit );
2649        }
2650        stream->outputUnit = NULL;
2651        if( stream->inputUnit )
2652        {
2653           AudioUnitUninitialize( stream->inputUnit );
2654           CloseComponent( stream->inputUnit );
2655           stream->inputUnit = NULL;
2656        }
2657        if( stream->inputRingBuffer.buffer )
2658           free( (void *) stream->inputRingBuffer.buffer );
2659        stream->inputRingBuffer.buffer = NULL;
2660        /*TODO: is there more that needs to be done on error
2661                from AudioConverterDispose?*/
2662        if( stream->inputSRConverter )
2663           ERR( AudioConverterDispose( stream->inputSRConverter ) );
2664        stream->inputSRConverter = NULL;
2665        if( stream->inputAudioBufferList.mBuffers[0].mData )
2666           free( stream->inputAudioBufferList.mBuffers[0].mData );
2667        stream->inputAudioBufferList.mBuffers[0].mData = NULL;
2668 
2669        result = destroyBlioRingBuffers( &stream->blio );
2670        if( result )
2671           return result;
2672        if( stream->bufferProcessorIsInitialized )
2673           PaUtil_TerminateBufferProcessor( &stream->bufferProcessor );
2674 
2675        if( stream->timingInformationMutexIsInitialized )
2676           pthread_mutex_destroy( &stream->timingInformationMutex );
2677 
2678        PaUtil_TerminateStreamRepresentation( &stream->streamRepresentation );
2679        PaUtil_FreeMemory( stream );
2680     }
2681 
2682     return result;
2683 }
2684 
StartStream(PaStream * s)2685 static PaError StartStream( PaStream *s )
2686 {
2687     PaMacCoreStream *stream = (PaMacCoreStream*)s;
2688     OSStatus result = noErr;
2689     VVDBUG(("StartStream()\n"));
2690     VDBUG( ( "Starting stream.\n" ) );
2691 
2692 #define ERR_WRAP(mac_err) do { result = mac_err ; if ( result != noErr ) return ERR(result) ; } while(0)
2693 
2694     /*FIXME: maybe want to do this on close/abort for faster start? */
2695     PaUtil_ResetBufferProcessor( &stream->bufferProcessor );
2696     if(  stream->inputSRConverter )
2697        ERR_WRAP( AudioConverterReset( stream->inputSRConverter ) );
2698 
2699     /* -- start -- */
2700     stream->state = ACTIVE;
2701     if( stream->inputUnit ) {
2702        ERR_WRAP( AudioOutputUnitStart(stream->inputUnit) );
2703     }
2704     if( stream->outputUnit && stream->outputUnit != stream->inputUnit ) {
2705        ERR_WRAP( AudioOutputUnitStart(stream->outputUnit) );
2706     }
2707 
2708     return paNoError;
2709 #undef ERR_WRAP
2710 }
2711 
2712 // it's not clear from appl's docs that this really waits
2713 // until all data is flushed.
BlockWhileAudioUnitIsRunning(AudioUnit audioUnit,AudioUnitElement element)2714 static ComponentResult BlockWhileAudioUnitIsRunning( AudioUnit audioUnit, AudioUnitElement element )
2715 {
2716     Boolean isRunning = 1;
2717     while( isRunning ) {
2718        UInt32 s = sizeof( isRunning );
2719        ComponentResult err = AudioUnitGetProperty( audioUnit, kAudioOutputUnitProperty_IsRunning, kAudioUnitScope_Global, element,  &isRunning, &s );
2720        if( err )
2721           return err;
2722        Pa_Sleep( 100 );
2723     }
2724     return noErr;
2725 }
2726 
FinishStoppingStream(PaMacCoreStream * stream)2727 static PaError FinishStoppingStream( PaMacCoreStream *stream )
2728 {
2729     OSStatus result = noErr;
2730     PaError paErr;
2731 
2732 #define ERR_WRAP(mac_err) do { result = mac_err ; if ( result != noErr ) return ERR(result) ; } while(0)
2733     /* -- stop and reset -- */
2734     if( stream->inputUnit == stream->outputUnit && stream->inputUnit )
2735     {
2736        ERR_WRAP( AudioOutputUnitStop(stream->inputUnit) );
2737        ERR_WRAP( BlockWhileAudioUnitIsRunning(stream->inputUnit,0) );
2738        ERR_WRAP( BlockWhileAudioUnitIsRunning(stream->inputUnit,1) );
2739        ERR_WRAP( AudioUnitReset(stream->inputUnit, kAudioUnitScope_Global, 1) );
2740        ERR_WRAP( AudioUnitReset(stream->inputUnit, kAudioUnitScope_Global, 0) );
2741     }
2742     else
2743     {
2744        if( stream->inputUnit )
2745        {
2746           ERR_WRAP(AudioOutputUnitStop(stream->inputUnit) );
2747           ERR_WRAP( BlockWhileAudioUnitIsRunning(stream->inputUnit,1) );
2748           ERR_WRAP(AudioUnitReset(stream->inputUnit,kAudioUnitScope_Global,1));
2749        }
2750        if( stream->outputUnit )
2751        {
2752           ERR_WRAP(AudioOutputUnitStop(stream->outputUnit));
2753           ERR_WRAP( BlockWhileAudioUnitIsRunning(stream->outputUnit,0) );
2754           ERR_WRAP(AudioUnitReset(stream->outputUnit,kAudioUnitScope_Global,0));
2755        }
2756     }
2757     if( stream->inputRingBuffer.buffer ) {
2758        PaUtil_FlushRingBuffer( &stream->inputRingBuffer );
2759        bzero( (void *)stream->inputRingBuffer.buffer,
2760               stream->inputRingBuffer.bufferSize );
2761        /* advance the write point a little, so we are reading from the
2762           middle of the buffer. We'll need extra at the end because
2763           testing has shown that this helps. */
2764        if( stream->outputUnit )
2765           PaUtil_AdvanceRingBufferWriteIndex( &stream->inputRingBuffer,
2766                                               stream->inputRingBuffer.bufferSize
2767                                               / RING_BUFFER_ADVANCE_DENOMINATOR );
2768     }
2769 
2770     stream->xrunFlags = 0;
2771     stream->state = STOPPED;
2772 
2773     paErr = resetBlioRingBuffers( &stream->blio );
2774     if( paErr )
2775        return paErr;
2776 
2777     VDBUG( ( "Stream Stopped.\n" ) );
2778     return paNoError;
2779 #undef ERR_WRAP
2780 }
2781 
2782 /* Block until buffer is empty then stop the stream. */
StopStream(PaStream * s)2783 static PaError StopStream( PaStream *s )
2784 {
2785     PaError paErr;
2786     PaMacCoreStream *stream = (PaMacCoreStream*)s;
2787     VVDBUG(("StopStream()\n"));
2788 
2789     /* Tell WriteStream to stop filling the buffer. */
2790     stream->state = STOPPING;
2791 
2792     if( stream->userOutChan > 0 ) /* Does this stream do output? */
2793     {
2794         size_t maxHostFrames = MAX( stream->inputFramesPerBuffer, stream->outputFramesPerBuffer );
2795         VDBUG( ("Waiting for write buffer to be drained.\n") );
2796         paErr = waitUntilBlioWriteBufferIsEmpty( &stream->blio, stream->sampleRate,
2797                                                 maxHostFrames );
2798         VDBUG( ( "waitUntilBlioWriteBufferIsEmpty returned %d\n", paErr ) );
2799     }
2800     return FinishStoppingStream( stream );
2801 }
2802 
2803 /* Immediately stop the stream. */
AbortStream(PaStream * s)2804 static PaError AbortStream( PaStream *s )
2805 {
2806     PaMacCoreStream *stream = (PaMacCoreStream*)s;
2807     VDBUG( ( "AbortStream()\n" ) );
2808     stream->state = STOPPING;
2809     return FinishStoppingStream( stream );
2810 }
2811 
2812 
IsStreamStopped(PaStream * s)2813 static PaError IsStreamStopped( PaStream *s )
2814 {
2815     PaMacCoreStream *stream = (PaMacCoreStream*)s;
2816     VVDBUG(("IsStreamStopped()\n"));
2817 
2818     return stream->state == STOPPED ? 1 : 0;
2819 }
2820 
2821 
IsStreamActive(PaStream * s)2822 static PaError IsStreamActive( PaStream *s )
2823 {
2824     PaMacCoreStream *stream = (PaMacCoreStream*)s;
2825     VVDBUG(("IsStreamActive()\n"));
2826     return ( stream->state == ACTIVE || stream->state == STOPPING );
2827 }
2828 
2829 
GetStreamCpuLoad(PaStream * s)2830 static double GetStreamCpuLoad( PaStream* s )
2831 {
2832     PaMacCoreStream *stream = (PaMacCoreStream*)s;
2833     VVDBUG(("GetStreamCpuLoad()\n"));
2834 
2835     return PaUtil_GetCpuLoad( &stream->cpuLoadMeasurer );
2836 }
2837