1 /*
2  * Helper and utility functions for pa_mac_core.c (Apple AUHAL implementation)
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
56  @ingroup hostapi_src
57 */
58 
59 #include "pa_mac_core_utilities.h"
60 #include "pa_mac_core_internal.h"
61 #include <libkern/OSAtomic.h>
62 #include <strings.h>
63 #include <pthread.h>
64 #include <sys/time.h>
65 
PaMacCore_SetUnixError(int err,int line)66 PaError PaMacCore_SetUnixError( int err, int line )
67 {
68    PaError ret;
69    const char *errorText;
70 
71    if( err == 0 )
72    {
73       return paNoError;
74    }
75 
76    ret = paNoError;
77    errorText = strerror( err );
78 
79    /** Map Unix error to PaError. Pretty much the only one that maps
80        is ENOMEM. */
81    if( err == ENOMEM )
82       ret = paInsufficientMemory;
83    else
84       ret = paInternalError;
85 
86    DBUG(("%d on line %d: msg='%s'\n", err, line, errorText));
87    PaUtil_SetLastHostErrorInfo( paCoreAudio, err, errorText );
88 
89    return ret;
90 }
91 
92 /*
93  * Translates MacOS generated errors into PaErrors
94  */
PaMacCore_SetError(OSStatus error,int line,int isError)95 PaError PaMacCore_SetError(OSStatus error, int line, int isError)
96 {
97     /*FIXME: still need to handle possible ComponentResult values.*/
98     /*       unfortunately, they don't seem to be documented anywhere.*/
99     PaError result;
100     const char *errorType;
101     const char *errorText;
102 
103     switch (error) {
104     case kAudioHardwareNoError:
105         return paNoError;
106     case kAudioHardwareNotRunningError:
107         errorText = "Audio Hardware Not Running";
108         result = paInternalError; break;
109     case kAudioHardwareUnspecifiedError:
110         errorText = "Unspecified Audio Hardware Error";
111         result = paInternalError; break;
112     case kAudioHardwareUnknownPropertyError:
113         errorText = "Audio Hardware: Unknown Property";
114         result = paInternalError; break;
115     case kAudioHardwareBadPropertySizeError:
116         errorText = "Audio Hardware: Bad Property Size";
117         result = paInternalError; break;
118     case kAudioHardwareIllegalOperationError:
119         errorText = "Audio Hardware: Illegal Operation";
120         result = paInternalError; break;
121     case kAudioHardwareBadDeviceError:
122         errorText = "Audio Hardware: Bad Device";
123         result = paInvalidDevice; break;
124     case kAudioHardwareBadStreamError:
125         errorText = "Audio Hardware: BadStream";
126         result = paBadStreamPtr; break;
127     case kAudioHardwareUnsupportedOperationError:
128         errorText = "Audio Hardware: Unsupported Operation";
129         result = paInternalError; break;
130     case kAudioDeviceUnsupportedFormatError:
131         errorText = "Audio Device: Unsupported Format";
132         result = paSampleFormatNotSupported; break;
133     case kAudioDevicePermissionsError:
134         errorText = "Audio Device: Permissions Error";
135         result = paDeviceUnavailable; break;
136     /* Audio Unit Errors: http://developer.apple.com/documentation/MusicAudio/Reference/CoreAudio/audio_units/chapter_5_section_3.html */
137     case kAudioUnitErr_InvalidProperty:
138         errorText = "Audio Unit: Invalid Property";
139         result = paInternalError; break;
140     case kAudioUnitErr_InvalidParameter:
141         errorText = "Audio Unit: Invalid Parameter";
142         result = paInternalError; break;
143     case kAudioUnitErr_NoConnection:
144         errorText = "Audio Unit: No Connection";
145         result = paInternalError; break;
146     case kAudioUnitErr_FailedInitialization:
147         errorText = "Audio Unit: Initialization Failed";
148         result = paInternalError; break;
149     case kAudioUnitErr_TooManyFramesToProcess:
150         errorText = "Audio Unit: Too Many Frames";
151         result = paInternalError; break;
152     case kAudioUnitErr_IllegalInstrument:
153         errorText = "Audio Unit: Illegal Instrument";
154         result = paInternalError; break;
155     case kAudioUnitErr_InstrumentTypeNotFound:
156         errorText = "Audio Unit: Instrument Type Not Found";
157         result = paInternalError; break;
158     case kAudioUnitErr_InvalidFile:
159         errorText = "Audio Unit: Invalid File";
160         result = paInternalError; break;
161     case kAudioUnitErr_UnknownFileType:
162         errorText = "Audio Unit: Unknown File Type";
163         result = paInternalError; break;
164     case kAudioUnitErr_FileNotSpecified:
165         errorText = "Audio Unit: File Not Specified";
166         result = paInternalError; break;
167     case kAudioUnitErr_FormatNotSupported:
168         errorText = "Audio Unit: Format Not Supported";
169         result = paInternalError; break;
170     case kAudioUnitErr_Uninitialized:
171         errorText = "Audio Unit: Unitialized";
172         result = paInternalError; break;
173     case kAudioUnitErr_InvalidScope:
174         errorText = "Audio Unit: Invalid Scope";
175         result = paInternalError; break;
176     case kAudioUnitErr_PropertyNotWritable:
177         errorText = "Audio Unit: PropertyNotWritable";
178         result = paInternalError; break;
179     case kAudioUnitErr_InvalidPropertyValue:
180         errorText = "Audio Unit: Invalid Property Value";
181         result = paInternalError; break;
182     case kAudioUnitErr_PropertyNotInUse:
183         errorText = "Audio Unit: Property Not In Use";
184         result = paInternalError; break;
185     case kAudioUnitErr_Initialized:
186         errorText = "Audio Unit: Initialized";
187         result = paInternalError; break;
188     case kAudioUnitErr_InvalidOfflineRender:
189         errorText = "Audio Unit: Invalid Offline Render";
190         result = paInternalError; break;
191     case kAudioUnitErr_Unauthorized:
192         errorText = "Audio Unit: Unauthorized";
193         result = paInternalError; break;
194     case kAudioUnitErr_CannotDoInCurrentContext:
195         errorText = "Audio Unit: cannot do in current context";
196         result = paInternalError; break;
197     default:
198         errorText = "Unknown Error";
199         result = paInternalError;
200     }
201 
202     if (isError)
203         errorType = "Error";
204     else
205         errorType = "Warning";
206 
207     char str[20];
208     // see if it appears to be a 4-char-code
209     *(UInt32 *)(str + 1) = CFSwapInt32HostToBig(error);
210     if (isprint(str[1]) && isprint(str[2]) && isprint(str[3]) && isprint(str[4]))
211     {
212         str[0] = str[5] = '\'';
213         str[6] = '\0';
214     } else {
215         // no, format it as an integer
216         sprintf(str, "%d", (int)error);
217     }
218 
219     DBUG(("%s on line %d: err='%s', msg=%s\n", errorType, line, str, errorText));
220 
221     PaUtil_SetLastHostErrorInfo( paCoreAudio, error, errorText );
222 
223     return result;
224 }
225 
226 /*
227  * This function computes an appropriate ring buffer size given
228  * a requested latency (in seconds), sample rate and framesPerBuffer.
229  *
230  * The returned ringBufferSize is computed using the following
231  * constraints:
232  *   - it must be at least 4.
233  *   - it must be at least 3x framesPerBuffer.
234  *   - it must be at least 2x the suggestedLatency.
235  *   - it must be a power of 2.
236  * This function attempts to compute the minimum such size.
237  *
238  * FEEDBACK: too liberal/conservative/another way?
239  */
computeRingBufferSize(const PaStreamParameters * inputParameters,const PaStreamParameters * outputParameters,long inputFramesPerBuffer,long outputFramesPerBuffer,double sampleRate)240 long computeRingBufferSize( const PaStreamParameters *inputParameters,
241                                    const PaStreamParameters *outputParameters,
242                                    long inputFramesPerBuffer,
243                                    long outputFramesPerBuffer,
244                                    double sampleRate )
245 {
246    long ringSize;
247    int index;
248    int i;
249    double latency ;
250    long framesPerBuffer ;
251 
252    VVDBUG(( "computeRingBufferSize()\n" ));
253 
254    assert( inputParameters || outputParameters );
255 
256    if( outputParameters && inputParameters )
257    {
258       latency = MAX( inputParameters->suggestedLatency, outputParameters->suggestedLatency );
259       framesPerBuffer = MAX( inputFramesPerBuffer, outputFramesPerBuffer );
260    }
261    else if( outputParameters )
262    {
263       latency = outputParameters->suggestedLatency;
264       framesPerBuffer = outputFramesPerBuffer ;
265    }
266    else /* we have inputParameters  */
267    {
268       latency = inputParameters->suggestedLatency;
269       framesPerBuffer = inputFramesPerBuffer ;
270    }
271 
272    ringSize = (long) ( latency * sampleRate * 2 + .5);
273    VDBUG( ( "suggested latency : %d\n", (int) (latency*sampleRate) ) );
274    if( ringSize < framesPerBuffer * 3 )
275       ringSize = framesPerBuffer * 3 ;
276    VDBUG(("framesPerBuffer:%d\n",(int)framesPerBuffer));
277    VDBUG(("Ringbuffer size (1): %d\n", (int)ringSize ));
278 
279    /* make sure it's at least 4 */
280    ringSize = MAX( ringSize, 4 );
281 
282    /* round up to the next power of 2 */
283    index = -1;
284    for( i=0; i<sizeof(long)*8; ++i )
285       if( ringSize >> i & 0x01 )
286          index = i;
287    assert( index > 0 );
288    if( ringSize <= ( 0x01 << index ) )
289       ringSize = 0x01 << index ;
290    else
291       ringSize = 0x01 << ( index + 1 );
292 
293    VDBUG(( "Final Ringbuffer size (2): %d\n", (int)ringSize ));
294    return ringSize;
295 }
296 
297 
298 /*
299  * Durring testing of core audio, I found that serious crashes could occur
300  * if properties such as sample rate were changed multiple times in rapid
301  * succession. The function below could be used to with a condition variable.
302  * to prevent propertychanges from happening until the last property
303  * change is acknowledged. Instead, I implemented a busy-wait, which is simpler
304  * to implement b/c in second round of testing (nov '09) property changes occured
305  * quickly and so there was no real way to test the condition variable implementation.
306  * therefore, this function is not used, but it is aluded to in commented code below,
307  * since it represents a theoretically better implementation.
308  */
309 
propertyProc(AudioDeviceID inDevice,UInt32 inChannel,Boolean isInput,AudioDevicePropertyID inPropertyID,void * inClientData)310 OSStatus propertyProc(
311     AudioDeviceID inDevice,
312     UInt32 inChannel,
313     Boolean isInput,
314     AudioDevicePropertyID inPropertyID,
315     void* inClientData )
316 {
317    // this is where we would set the condition variable
318    return noErr;
319 }
320 
321 /* sets the value of the given property and waits for the change to
322    be acknowledged, and returns the final value, which is not guaranteed
323    by this function to be the same as the desired value. Obviously, this
324    function can only be used for data whose input and output are the
325    same size and format, and their size and format are known in advance.
326    whether or not the call succeeds, if the data is successfully read,
327    it is returned in outPropertyData. If it is not read successfully,
328    outPropertyData is zeroed, which may or may not be useful in
329    determining if the property was read. */
AudioDeviceSetPropertyNowAndWaitForChange(AudioDeviceID inDevice,UInt32 inChannel,Boolean isInput,AudioDevicePropertyID inPropertyID,UInt32 inPropertyDataSize,const void * inPropertyData,void * outPropertyData)330 PaError AudioDeviceSetPropertyNowAndWaitForChange(
331     AudioDeviceID inDevice,
332     UInt32 inChannel,
333     Boolean isInput,
334     AudioDevicePropertyID inPropertyID,
335     UInt32 inPropertyDataSize,
336     const void *inPropertyData,
337     void *outPropertyData )
338 {
339    OSStatus macErr;
340    UInt32 outPropertyDataSize = inPropertyDataSize;
341 
342    /* First, see if it already has that value. If so, return. */
343    macErr = AudioDeviceGetProperty( inDevice, inChannel,
344                                  isInput, inPropertyID,
345                                  &outPropertyDataSize, outPropertyData );
346    if( macErr ) {
347       memset( outPropertyData, 0, inPropertyDataSize );
348       goto failMac;
349    }
350    if( inPropertyDataSize!=outPropertyDataSize )
351       return paInternalError;
352    if( 0==memcmp( outPropertyData, inPropertyData, outPropertyDataSize ) )
353       return paNoError;
354 
355    /* Ideally, we'd use a condition variable to determine changes.
356       we could set that up here. */
357 
358    /* If we were using a cond variable, we'd do something useful here,
359       but for now, this is just to make 10.6 happy. */
360    macErr = AudioDeviceAddPropertyListener( inDevice, inChannel, isInput,
361                                    inPropertyID, propertyProc,
362                                    NULL );
363    if( macErr )
364       /* we couldn't add a listener. */
365       goto failMac;
366 
367    /* set property */
368    macErr  = AudioDeviceSetProperty( inDevice, NULL, inChannel,
369                                  isInput, inPropertyID,
370                                  inPropertyDataSize, inPropertyData );
371    if( macErr )
372       goto failMac;
373 
374    /* busy-wait up to 30 seconds for the property to change */
375    /* busy-wait is justified here only because the correct alternative (condition variable)
376       was hard to test, since most of the waiting ended up being for setting rather than
377       getting in OS X 10.5. This was not the case in earlier OS versions. */
378    struct timeval tv1, tv2;
379    gettimeofday( &tv1, NULL );
380    memcpy( &tv2, &tv1, sizeof( struct timeval ) );
381    while( tv2.tv_sec - tv1.tv_sec < 30 ) {
382       /* now read the property back out */
383       macErr = AudioDeviceGetProperty( inDevice, inChannel,
384                                     isInput, inPropertyID,
385                                     &outPropertyDataSize, outPropertyData );
386       if( macErr ) {
387          memset( outPropertyData, 0, inPropertyDataSize );
388          goto failMac;
389       }
390       /* and compare... */
391       if( 0==memcmp( outPropertyData, inPropertyData, outPropertyDataSize ) ) {
392          AudioDeviceRemovePropertyListener( inDevice, inChannel, isInput, inPropertyID, propertyProc );
393          return paNoError;
394       }
395       /* No match yet, so let's sleep and try again. */
396       Pa_Sleep( 100 );
397       gettimeofday( &tv2, NULL );
398    }
399    DBUG( ("Timeout waiting for device setting.\n" ) );
400 
401    AudioDeviceRemovePropertyListener( inDevice, inChannel, isInput, inPropertyID, propertyProc );
402    return paNoError;
403 
404  failMac:
405    AudioDeviceRemovePropertyListener( inDevice, inChannel, isInput, inPropertyID, propertyProc );
406    return ERR( macErr );
407 }
408 
409 /*
410  * Sets the sample rate the HAL device.
411  * if requireExact: set the sample rate or fail.
412  *
413  * otherwise      : set the exact sample rate.
414  *             If that fails, check for available sample rates, and choose one
415  *             higher than the requested rate. If there isn't a higher one,
416  *             just use the highest available.
417  */
setBestSampleRateForDevice(const AudioDeviceID device,const bool isOutput,const bool requireExact,const Float64 desiredSrate)418 PaError setBestSampleRateForDevice( const AudioDeviceID device,
419                                     const bool isOutput,
420                                     const bool requireExact,
421                                     const Float64 desiredSrate )
422 {
423    const bool isInput = isOutput ? 0 : 1;
424    Float64 srate;
425    UInt32 propsize = sizeof( Float64 );
426    OSErr err;
427    AudioValueRange *ranges;
428    int i=0;
429    Float64 max  = -1; /*the maximum rate available*/
430    Float64 best = -1; /*the lowest sample rate still greater than desired rate*/
431    VDBUG(("Setting sample rate for device %ld to %g.\n",device,(float)desiredSrate));
432 
433    /* -- try setting the sample rate -- */
434    srate = 0;
435    err = AudioDeviceSetPropertyNowAndWaitForChange(
436                                  device, 0, isInput,
437                                  kAudioDevicePropertyNominalSampleRate,
438                                  propsize, &desiredSrate, &srate );
439 
440    /* -- if the rate agrees, and was changed, we are done -- */
441    if( srate != 0 && srate == desiredSrate )
442       return paNoError;
443    /* -- if the rate agrees, and we got no errors, we are done -- */
444    if( !err && srate == desiredSrate )
445       return paNoError;
446    /* -- we've failed if the rates disagree and we are setting input -- */
447    if( requireExact )
448       return paInvalidSampleRate;
449 
450    /* -- generate a list of available sample rates -- */
451    err = AudioDeviceGetPropertyInfo( device, 0, isInput,
452                                 kAudioDevicePropertyAvailableNominalSampleRates,
453                                 &propsize, NULL );
454    if( err )
455       return ERR( err );
456    ranges = (AudioValueRange *)calloc( 1, propsize );
457    if( !ranges )
458       return paInsufficientMemory;
459    err = AudioDeviceGetProperty( device, 0, isInput,
460                                 kAudioDevicePropertyAvailableNominalSampleRates,
461                                 &propsize, ranges );
462    if( err )
463    {
464       free( ranges );
465       return ERR( err );
466    }
467    VDBUG(("Requested sample rate of %g was not available.\n", (float)desiredSrate));
468    VDBUG(("%lu Available Sample Rates are:\n",propsize/sizeof(AudioValueRange)));
469 #ifdef MAC_CORE_VERBOSE_DEBUG
470    for( i=0; i<propsize/sizeof(AudioValueRange); ++i )
471       VDBUG( ("\t%g-%g\n",
472               (float) ranges[i].mMinimum,
473               (float) ranges[i].mMaximum ) );
474 #endif
475    VDBUG(("-----\n"));
476 
477    /* -- now pick the best available sample rate -- */
478    for( i=0; i<propsize/sizeof(AudioValueRange); ++i )
479    {
480       if( ranges[i].mMaximum > max ) max = ranges[i].mMaximum;
481       if( ranges[i].mMinimum > desiredSrate ) {
482          if( best < 0 )
483             best = ranges[i].mMinimum;
484          else if( ranges[i].mMinimum < best )
485             best = ranges[i].mMinimum;
486       }
487    }
488    if( best < 0 )
489       best = max;
490    VDBUG( ("Maximum Rate %g. best is %g.\n", max, best ) );
491    free( ranges );
492 
493    /* -- set the sample rate -- */
494    propsize = sizeof( best );
495    srate = 0;
496    err = AudioDeviceSetPropertyNowAndWaitForChange(
497                                  device, 0, isInput,
498                                  kAudioDevicePropertyNominalSampleRate,
499                                  propsize, &best, &srate );
500 
501    /* -- if the set rate matches, we are done -- */
502    if( srate != 0 && srate == best )
503       return paNoError;
504 
505    if( err )
506       return ERR( err );
507 
508    /* -- otherwise, something wierd happened: we didn't set the rate, and we got no errors. Just bail. */
509    return paInternalError;
510 }
511 
512 
513 /*
514    Attempts to set the requestedFramesPerBuffer. If it can't set the exact
515    value, it settles for something smaller if available. If nothing smaller
516    is available, it uses the smallest available size.
517    actualFramesPerBuffer will be set to the actual value on successful return.
518    OK to pass NULL to actualFramesPerBuffer.
519    The logic is very simmilar too setBestSampleRate only failure here is
520    not usually catastrophic.
521 */
setBestFramesPerBuffer(const AudioDeviceID device,const bool isOutput,UInt32 requestedFramesPerBuffer,UInt32 * actualFramesPerBuffer)522 PaError setBestFramesPerBuffer( const AudioDeviceID device,
523                                 const bool isOutput,
524                                 UInt32 requestedFramesPerBuffer,
525                                 UInt32 *actualFramesPerBuffer )
526 {
527     UInt32 afpb;
528     const bool isInput = !isOutput;
529     UInt32 propsize = sizeof(UInt32);
530     OSErr err;
531     AudioValueRange range;
532 
533     if( actualFramesPerBuffer == NULL )
534     {
535         actualFramesPerBuffer = &afpb;
536     }
537 
538     /* -- try and set exact FPB -- */
539     err = AudioDeviceSetProperty( device, NULL, 0, isInput,
540                                  kAudioDevicePropertyBufferFrameSize,
541                                  propsize, &requestedFramesPerBuffer);
542     err = AudioDeviceGetProperty( device, 0, isInput,
543                            kAudioDevicePropertyBufferFrameSize,
544                            &propsize, actualFramesPerBuffer);
545     if( err )
546     {
547         return ERR( err );
548     }
549     // Did we get the size we asked for?
550     if( *actualFramesPerBuffer == requestedFramesPerBuffer )
551     {
552         return paNoError; /* we are done */
553     }
554 
555     // Clip requested value against legal range for the device.
556     propsize = sizeof(AudioValueRange);
557     err = AudioDeviceGetProperty( device, 0, isInput,
558                                 kAudioDevicePropertyBufferFrameSizeRange,
559                                 &propsize, &range );
560     if( err )
561     {
562       return ERR( err );
563     }
564     if( requestedFramesPerBuffer < range.mMinimum )
565     {
566         requestedFramesPerBuffer = range.mMinimum;
567     }
568     else if( requestedFramesPerBuffer > range.mMaximum )
569     {
570         requestedFramesPerBuffer = range.mMaximum;
571     }
572 
573    /* --- set the buffer size (ignore errors) -- */
574     propsize = sizeof( UInt32 );
575    err = AudioDeviceSetProperty( device, NULL, 0, isInput,
576                                  kAudioDevicePropertyBufferFrameSize,
577                                  propsize, &requestedFramesPerBuffer );
578    /* --- read the property to check that it was set -- */
579    err = AudioDeviceGetProperty( device, 0, isInput,
580                                  kAudioDevicePropertyBufferFrameSize,
581                                  &propsize, actualFramesPerBuffer );
582 
583    if( err )
584       return ERR( err );
585 
586    return paNoError;
587 }
588 
589 /**********************
590  *
591  * XRun stuff
592  *
593  **********************/
594 
595 struct PaMacXRunListNode_s {
596    PaMacCoreStream *stream;
597    struct PaMacXRunListNode_s *next;
598 } ;
599 
600 typedef struct PaMacXRunListNode_s PaMacXRunListNode;
601 
602 /** Always empty, so that it can always be the one returned by
603     addToXRunListenerList. note that it's not a pointer. */
604 static PaMacXRunListNode firstXRunListNode;
605 static int xRunListSize;
606 static pthread_mutex_t xrunMutex;
607 
xrunCallback(AudioDeviceID inDevice,UInt32 inChannel,Boolean isInput,AudioDevicePropertyID inPropertyID,void * inClientData)608 OSStatus xrunCallback(
609     AudioDeviceID inDevice,
610     UInt32 inChannel,
611     Boolean isInput,
612     AudioDevicePropertyID inPropertyID,
613     void* inClientData)
614 {
615    PaMacXRunListNode *node = (PaMacXRunListNode *) inClientData;
616 
617    int ret = pthread_mutex_trylock( &xrunMutex ) ;
618 
619    if( ret == 0 ) {
620 
621       node = node->next ; //skip the first node
622 
623       for( ; node; node=node->next ) {
624          PaMacCoreStream *stream = node->stream;
625 
626          if( stream->state != ACTIVE )
627             continue; //if the stream isn't active, we don't care if the device is dropping
628 
629          if( isInput ) {
630             if( stream->inputDevice == inDevice )
631                OSAtomicOr32( paInputOverflow, &stream->xrunFlags );
632          } else {
633             if( stream->outputDevice == inDevice )
634                OSAtomicOr32( paOutputUnderflow, &stream->xrunFlags );
635          }
636       }
637 
638       pthread_mutex_unlock( &xrunMutex );
639    }
640 
641    return 0;
642 }
643 
initializeXRunListenerList()644 int initializeXRunListenerList()
645 {
646    xRunListSize = 0;
647    bzero( (void *) &firstXRunListNode, sizeof(firstXRunListNode) );
648    return pthread_mutex_init( &xrunMutex, NULL );
649 }
destroyXRunListenerList()650 int destroyXRunListenerList()
651 {
652    PaMacXRunListNode *node;
653    node = firstXRunListNode.next;
654    while( node ) {
655       PaMacXRunListNode *tmp = node;
656       node = node->next;
657       free( tmp );
658    }
659    xRunListSize = 0;
660    return pthread_mutex_destroy( &xrunMutex );
661 }
662 
addToXRunListenerList(void * stream)663 void *addToXRunListenerList( void *stream )
664 {
665    pthread_mutex_lock( &xrunMutex );
666    PaMacXRunListNode *newNode;
667    // setup new node:
668    newNode = (PaMacXRunListNode *) malloc( sizeof( PaMacXRunListNode ) );
669    newNode->stream = (PaMacCoreStream *) stream;
670    newNode->next = firstXRunListNode.next;
671    // insert:
672    firstXRunListNode.next = newNode;
673    pthread_mutex_unlock( &xrunMutex );
674 
675    return &firstXRunListNode;
676 }
677 
removeFromXRunListenerList(void * stream)678 int removeFromXRunListenerList( void *stream )
679 {
680    pthread_mutex_lock( &xrunMutex );
681    PaMacXRunListNode *node, *prev;
682    prev = &firstXRunListNode;
683    node = firstXRunListNode.next;
684    while( node ) {
685       if( node->stream == stream ) {
686          //found it:
687          --xRunListSize;
688          prev->next = node->next;
689          free( node );
690          pthread_mutex_unlock( &xrunMutex );
691          return xRunListSize;
692       }
693       prev = prev->next;
694       node = node->next;
695    }
696 
697    pthread_mutex_unlock( &xrunMutex );
698    // failure
699    return xRunListSize;
700 }
701 
702