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(void)644 int initializeXRunListenerList( void )
645 {
646 xRunListSize = 0;
647 bzero( (void *) &firstXRunListNode, sizeof(firstXRunListNode) );
648 return pthread_mutex_init( &xrunMutex, NULL );
649 }
destroyXRunListenerList(void)650 int destroyXRunListenerList( void )
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