To check out this repository please hg clone the following URL, or open the URL using EasyMercurial or your preferred Mercurial client.

The primary repository for this project is hosted at https://github.com/sonic-visualiser/sv-dependency-builds .
This repository is a read-only copy which is updated automatically every hour.

Statistics Download as Zip
| Branch: | Tag: | Revision:

root / src / portaudio_20161030_catalina_patch / src / hostapi / coreaudio / pa_mac_core_utilities.c @ 164:9fa11135915a

History | View | Annotate | Download (24.2 KB)

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

    
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
 */
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
 */
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

    
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. */
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
 */
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
*/
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

    
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

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

    
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

    
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