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_blocking.c @ 164:9fa11135915a

History | View | Annotate | Download (20.7 KB)

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
56
 @ingroup hostapi_src
57

58
 This file contains the implementation
59
 required for blocking I/O. It is separated from pa_mac_core.c simply to ease
60
 development.
61
*/
62

    
63
#include "pa_mac_core_blocking.h"
64
#include "pa_mac_core_internal.h"
65
#include <assert.h>
66
#ifdef MOSX_USE_NON_ATOMIC_FLAG_BITS
67
# define OSAtomicOr32( a, b ) ( (*(b)) |= (a) )
68
# define OSAtomicAnd32( a, b ) ( (*(b)) &= (a) )
69
#else
70
# include <libkern/OSAtomic.h>
71
#endif
72

    
73
/*
74
 * This function determines the size of a particular sample format.
75
 * if the format is not recognized, this returns zero.
76
 */
77
static size_t computeSampleSizeFromFormat( PaSampleFormat format )
78
{
79
   switch( format & (~paNonInterleaved) ) {
80
   case paFloat32: return 4;
81
   case paInt32: return 4;
82
   case paInt24: return 3;
83
   case paInt16: return 2;
84
   case paInt8: case paUInt8: return 1;
85
   default: return 0;
86
   }
87
}
88
/*
89
 * Same as computeSampleSizeFromFormat, except that if
90
 * the size is not a power of two, it returns the next power of two up
91
 */
92
static size_t computeSampleSizeFromFormatPow2( PaSampleFormat format )
93
{
94
   switch( format & (~paNonInterleaved) ) {
95
   case paFloat32: return 4;
96
   case paInt32: return 4;
97
   case paInt24: return 4;
98
   case paInt16: return 2;
99
   case paInt8: case paUInt8: return 1;
100
   default: return 0;
101
   }
102
}
103

    
104

    
105

    
106
/*
107
 * Functions for initializing, resetting, and destroying BLIO structures.
108
 *
109
 */
110

    
111
/**
112
 * This should be called with the relevant info when initializing a stream for callback.
113
 *
114
 * @param ringBufferSizeInFrames must be a power of 2
115
 */
116
PaError initializeBlioRingBuffers(
117
                                       PaMacBlio *blio,
118
                                       PaSampleFormat inputSampleFormat,
119
                                       PaSampleFormat outputSampleFormat,
120
                                       long ringBufferSizeInFrames,
121
                                       int inChan,
122
                                       int outChan )
123
{
124
   void *data;
125
   int result;
126
   OSStatus err;
127

    
128
   /* zeroify things */
129
   bzero( blio, sizeof( PaMacBlio ) );
130
   /* this is redundant, but the buffers are used to check
131
      if the buffers have been initialized, so we do it explicitly. */
132
   blio->inputRingBuffer.buffer = NULL;
133
   blio->outputRingBuffer.buffer = NULL;
134

    
135
   /* initialize simple data */
136
   blio->ringBufferFrames = ringBufferSizeInFrames;
137
   blio->inputSampleFormat = inputSampleFormat;
138
   blio->inputSampleSizeActual = computeSampleSizeFromFormat(inputSampleFormat);
139
   blio->inputSampleSizePow2 = computeSampleSizeFromFormatPow2(inputSampleFormat); // FIXME: WHY?
140
   blio->outputSampleFormat = outputSampleFormat;
141
   blio->outputSampleSizeActual = computeSampleSizeFromFormat(outputSampleFormat);
142
   blio->outputSampleSizePow2 = computeSampleSizeFromFormatPow2(outputSampleFormat);
143

    
144
   blio->inChan = inChan;
145
   blio->outChan = outChan;
146
   blio->statusFlags = 0;
147
   blio->errors = paNoError;
148
#ifdef PA_MAC_BLIO_MUTEX
149
   blio->isInputEmpty = false;
150
   blio->isOutputFull = false;
151
#endif
152

    
153
   /* setup ring buffers */
154
#ifdef PA_MAC_BLIO_MUTEX
155
   result = PaMacCore_SetUnixError( pthread_mutex_init(&(blio->inputMutex),NULL), 0 );
156
   if( result )
157
      goto error;
158
   result = UNIX_ERR( pthread_cond_init( &(blio->inputCond), NULL ) );
159
   if( result )
160
      goto error;
161
   result = UNIX_ERR( pthread_mutex_init(&(blio->outputMutex),NULL) );
162
   if( result )
163
      goto error;
164
   result = UNIX_ERR( pthread_cond_init( &(blio->outputCond), NULL ) );
165
#endif
166
   if( inChan ) {
167
      data = calloc( ringBufferSizeInFrames, blio->inputSampleSizePow2 * inChan );
168
      if( !data )
169
      {
170
         result = paInsufficientMemory;
171
         goto error;
172
      }
173

    
174
      err = PaUtil_InitializeRingBuffer(
175
            &blio->inputRingBuffer,
176
            blio->inputSampleSizePow2 * inChan,
177
            ringBufferSizeInFrames,
178
            data );
179
      assert( !err );
180
   }
181
   if( outChan ) {
182
      data = calloc( ringBufferSizeInFrames, blio->outputSampleSizePow2 * outChan );
183
      if( !data )
184
      {
185
         result = paInsufficientMemory;
186
         goto error;
187
      }
188

    
189
      err = PaUtil_InitializeRingBuffer(
190
            &blio->outputRingBuffer,
191
            blio->outputSampleSizePow2 * outChan,
192
            ringBufferSizeInFrames,
193
            data );
194
      assert( !err );
195
   }
196

    
197
   result = resetBlioRingBuffers( blio );
198
   if( result )
199
      goto error;
200

    
201
   return 0;
202

    
203
 error:
204
   destroyBlioRingBuffers( blio );
205
   return result;
206
}
207

    
208
#ifdef PA_MAC_BLIO_MUTEX
209
PaError blioSetIsInputEmpty( PaMacBlio *blio, bool isEmpty )
210
{
211
   PaError result = paNoError;
212
   if( isEmpty == blio->isInputEmpty )
213
      goto done;
214

    
215
   /* we need to update the value. Here's what we do:
216
    * - Lock the mutex, so noone else can write.
217
    * - update the value.
218
    * - unlock.
219
    * - broadcast to all listeners.
220
    */
221
   result = UNIX_ERR( pthread_mutex_lock( &blio->inputMutex ) );
222
   if( result )
223
      goto done;
224
   blio->isInputEmpty = isEmpty;
225
   result = UNIX_ERR( pthread_mutex_unlock( &blio->inputMutex ) );
226
   if( result )
227
      goto done;
228
   result = UNIX_ERR( pthread_cond_broadcast( &blio->inputCond ) );
229
   if( result )
230
      goto done;
231

    
232
 done:
233
   return result;
234
}
235
PaError blioSetIsOutputFull( PaMacBlio *blio, bool isFull )
236
{
237
   PaError result = paNoError;
238
   if( isFull == blio->isOutputFull )
239
      goto done;
240

    
241
   /* we need to update the value. Here's what we do:
242
    * - Lock the mutex, so noone else can write.
243
    * - update the value.
244
    * - unlock.
245
    * - broadcast to all listeners.
246
    */
247
   result = UNIX_ERR( pthread_mutex_lock( &blio->outputMutex ) );
248
   if( result )
249
      goto done;
250
   blio->isOutputFull = isFull;
251
   result = UNIX_ERR( pthread_mutex_unlock( &blio->outputMutex ) );
252
   if( result )
253
      goto done;
254
   result = UNIX_ERR( pthread_cond_broadcast( &blio->outputCond ) );
255
   if( result )
256
      goto done;
257

    
258
 done:
259
   return result;
260
}
261
#endif
262

    
263
/* This should be called after stopping or aborting the stream, so that on next
264
   start, the buffers will be ready. */
265
PaError resetBlioRingBuffers( PaMacBlio *blio )
266
{
267
#ifdef PA_MAC__BLIO_MUTEX
268
   int result;
269
#endif
270
   blio->statusFlags = 0;
271
   if( blio->outputRingBuffer.buffer ) {
272
       PaUtil_FlushRingBuffer( &blio->outputRingBuffer );
273
       /* Fill the buffer with zeros. */
274
       bzero( blio->outputRingBuffer.buffer,
275
             blio->outputRingBuffer.bufferSize * blio->outputRingBuffer.elementSizeBytes );
276
       PaUtil_AdvanceRingBufferWriteIndex( &blio->outputRingBuffer, blio->ringBufferFrames );
277

    
278
      /* Update isOutputFull. */
279
#ifdef PA_MAC__BLIO_MUTEX
280
      result = blioSetIsOutputFull( blio, toAdvance == blio->outputRingBuffer.bufferSize );
281
      if( result )
282
         goto error;
283
#endif
284
/*
285
      printf( "------%d\n" ,  blio->outChan );
286
      printf( "------%d\n" ,  blio->outputSampleSize );
287
*/
288
   }
289
   if( blio->inputRingBuffer.buffer ) {
290
      PaUtil_FlushRingBuffer( &blio->inputRingBuffer );
291
      bzero( blio->inputRingBuffer.buffer,
292
             blio->inputRingBuffer.bufferSize * blio->inputRingBuffer.elementSizeBytes );
293
      /* Update isInputEmpty. */
294
#ifdef PA_MAC__BLIO_MUTEX
295
      result = blioSetIsInputEmpty( blio, true );
296
      if( result )
297
         goto error;
298
#endif
299
   }
300
   return paNoError;
301
#ifdef PA_MAC__BLIO_MUTEX
302
 error:
303
   return result;
304
#endif
305
}
306

    
307
/*This should be called when you are done with the blio. It can safely be called
308
  multiple times if there are no exceptions. */
309
PaError destroyBlioRingBuffers( PaMacBlio *blio )
310
{
311
   PaError result = paNoError;
312
   if( blio->inputRingBuffer.buffer ) {
313
      free( blio->inputRingBuffer.buffer );
314
#ifdef PA_MAC__BLIO_MUTEX
315
      result = UNIX_ERR( pthread_mutex_destroy( & blio->inputMutex ) );
316
      if( result ) return result;
317
      result = UNIX_ERR( pthread_cond_destroy( & blio->inputCond ) );
318
      if( result ) return result;
319
#endif
320
   }
321
   blio->inputRingBuffer.buffer = NULL;
322
   if( blio->outputRingBuffer.buffer ) {
323
      free( blio->outputRingBuffer.buffer );
324
#ifdef PA_MAC__BLIO_MUTEX
325
      result = UNIX_ERR( pthread_mutex_destroy( & blio->outputMutex ) );
326
      if( result ) return result;
327
      result = UNIX_ERR( pthread_cond_destroy( & blio->outputCond ) );
328
      if( result ) return result;
329
#endif
330
   }
331
   blio->outputRingBuffer.buffer = NULL;
332

    
333
   return result;
334
}
335

    
336
/*
337
 * this is the BlioCallback function. It expects to recieve a PaMacBlio Object
338
 * pointer as userData.
339
 *
340
 */
341
int BlioCallback( const void *input, void *output, unsigned long frameCount,
342
        const PaStreamCallbackTimeInfo* timeInfo,
343
        PaStreamCallbackFlags statusFlags,
344
        void *userData )
345
{
346
   PaMacBlio *blio = (PaMacBlio*)userData;
347
   ring_buffer_size_t framesAvailable;
348
   ring_buffer_size_t framesToTransfer;
349
   ring_buffer_size_t framesTransferred;
350

    
351
   /* set flags returned by OS: */
352
   OSAtomicOr32( statusFlags, &blio->statusFlags ) ;
353

    
354
   /* --- Handle Input Buffer --- */
355
   if( blio->inChan ) {
356
      framesAvailable = PaUtil_GetRingBufferWriteAvailable( &blio->inputRingBuffer );
357

    
358
      /* check for underflow */
359
      if( framesAvailable < frameCount )
360
      {
361
          OSAtomicOr32( paInputOverflow, &blio->statusFlags );
362
          framesToTransfer = framesAvailable;
363
      }
364
      else
365
      {
366
          framesToTransfer = (ring_buffer_size_t)frameCount;
367
      }
368

    
369
      /* Copy the data from the audio input to the application ring buffer. */
370
      /*printf( "reading %d\n", toRead );*/
371
      framesTransferred = PaUtil_WriteRingBuffer( &blio->inputRingBuffer, input, framesToTransfer );
372
      assert( framesToTransfer == framesTransferred );
373
#ifdef PA_MAC__BLIO_MUTEX
374
      /* Priority inversion. See notes below. */
375
      blioSetIsInputEmpty( blio, false );
376
#endif
377
   }
378

    
379

    
380
   /* --- Handle Output Buffer --- */
381
   if( blio->outChan ) {
382
      framesAvailable = PaUtil_GetRingBufferReadAvailable( &blio->outputRingBuffer );
383

    
384
      /* check for underflow */
385
      if( framesAvailable < frameCount )
386
      {
387
          /* zero out the end of the output buffer that we do not have data for */
388
          framesToTransfer = framesAvailable;
389

    
390
          size_t bytesPerFrame = blio->outputSampleSizeActual * blio->outChan;
391
          size_t offsetInBytes = framesToTransfer * bytesPerFrame;
392
          size_t countInBytes = (frameCount - framesToTransfer) * bytesPerFrame;
393
          bzero( ((char *)output) + offsetInBytes, countInBytes );
394

    
395
          OSAtomicOr32( paOutputUnderflow, &blio->statusFlags );
396
          framesToTransfer = framesAvailable;
397
      }
398
      else
399
      {
400
          framesToTransfer = (ring_buffer_size_t)frameCount;
401
      }
402

    
403
      /* copy the data */
404
      /*printf( "writing %d\n", toWrite );*/
405
      framesTransferred = PaUtil_ReadRingBuffer( &blio->outputRingBuffer, output, framesToTransfer );
406
      assert( framesToTransfer == framesTransferred );
407
#ifdef PA_MAC__BLIO_MUTEX
408
      /* We have a priority inversion here. However, we will only have to
409
         wait if this was true and is now false, which means we've got
410
         some room in the buffer.
411
         Hopefully problems will be minimized. */
412
      blioSetIsOutputFull( blio, false );
413
#endif
414
   }
415

    
416
   return paContinue;
417
}
418

    
419
PaError ReadStream( PaStream* stream,
420
                           void *buffer,
421
                           unsigned long framesRequested )
422
{
423
    PaMacBlio *blio = & ((PaMacCoreStream*)stream) -> blio;
424
    char *cbuf = (char *) buffer;
425
    PaError ret = paNoError;
426
    VVDBUG(("ReadStream()\n"));
427

    
428
    while( framesRequested > 0 ) {
429
       ring_buffer_size_t framesAvailable;
430
       ring_buffer_size_t framesToTransfer;
431
       ring_buffer_size_t framesTransferred;
432
       do {
433
          framesAvailable = PaUtil_GetRingBufferReadAvailable( &blio->inputRingBuffer );
434
/*
435
          printf( "Read Buffer is %%%g full: %ld of %ld.\n",
436
                  100 * (float)avail / (float) blio->inputRingBuffer.bufferSize,
437
                  framesAvailable, blio->inputRingBuffer.bufferSize );
438
*/
439
          if( framesAvailable == 0 ) {
440
#ifdef PA_MAC_BLIO_MUTEX
441
             /**block when empty*/
442
             ret = UNIX_ERR( pthread_mutex_lock( &blio->inputMutex ) );
443
             if( ret )
444
                return ret;
445
             while( blio->isInputEmpty ) {
446
                ret = UNIX_ERR( pthread_cond_wait( &blio->inputCond, &blio->inputMutex ) );
447
                if( ret )
448
                   return ret;
449
             }
450
             ret = UNIX_ERR( pthread_mutex_unlock( &blio->inputMutex ) );
451
             if( ret )
452
                return ret;
453
#else
454
             Pa_Sleep( PA_MAC_BLIO_BUSY_WAIT_SLEEP_INTERVAL );
455
#endif
456
          }
457
       } while( framesAvailable == 0 );
458
       framesToTransfer = (ring_buffer_size_t) MIN( framesAvailable, framesRequested );
459
       framesTransferred = PaUtil_ReadRingBuffer( &blio->inputRingBuffer, (void *)cbuf, framesToTransfer );
460
       cbuf += framesTransferred * blio->inputSampleSizeActual * blio->inChan;
461
       framesRequested -= framesTransferred;
462

    
463
       if( framesToTransfer == framesAvailable ) {
464
#ifdef PA_MAC_BLIO_MUTEX
465
          /* we just emptied the buffer, so we need to mark it as empty. */
466
          ret = blioSetIsInputEmpty( blio, true );
467
          if( ret )
468
             return ret;
469
          /* of course, in the meantime, the callback may have put some sats
470
             in, so
471
             so check for that, too, to avoid a race condition. */
472
          /* FIXME - this does not seem to fix any race condition. */
473
          if( PaUtil_GetRingBufferReadAvailable( &blio->inputRingBuffer ) ) {
474
             blioSetIsInputEmpty( blio, false );
475
             /* FIXME - why check? ret has not been set? */
476
             if( ret )
477
                return ret;
478
          }
479
#endif
480
       }
481
    }
482

    
483
    /*   Report either paNoError or paInputOverflowed. */
484
    /*   may also want to report other errors, but this is non-standard. */
485
    /* FIXME should not clobber ret, use if(blio->statusFlags & paInputOverflow) */
486
    ret = blio->statusFlags & paInputOverflow;
487

    
488
    /* report underflow only once: */
489
    if( ret ) {
490
       OSAtomicAnd32( (uint32_t)(~paInputOverflow), &blio->statusFlags );
491
       ret = paInputOverflowed;
492
    }
493

    
494
    return ret;
495
}
496

    
497

    
498
PaError WriteStream( PaStream* stream,
499
                            const void *buffer,
500
                            unsigned long framesRequested )
501
{
502
    PaMacCoreStream *macStream = (PaMacCoreStream*)stream;
503
    PaMacBlio *blio = &macStream->blio;
504
    char *cbuf = (char *) buffer;
505
    PaError ret = paNoError;
506
    VVDBUG(("WriteStream()\n"));
507

    
508
    while( framesRequested > 0 && macStream->state != STOPPING ) {
509
        ring_buffer_size_t framesAvailable;
510
        ring_buffer_size_t framesToTransfer;
511
        ring_buffer_size_t framesTransferred;
512

    
513
       do {
514
          framesAvailable = PaUtil_GetRingBufferWriteAvailable( &blio->outputRingBuffer );
515
/*
516
          printf( "Write Buffer is %%%g full: %ld of %ld.\n",
517
                  100 - 100 * (float)avail / (float) blio->outputRingBuffer.bufferSize,
518
                  framesAvailable, blio->outputRingBuffer.bufferSize );
519
*/
520
          if( framesAvailable == 0 ) {
521
#ifdef PA_MAC_BLIO_MUTEX
522
             /*block while full*/
523
             ret = UNIX_ERR( pthread_mutex_lock( &blio->outputMutex ) );
524
             if( ret )
525
                return ret;
526
             while( blio->isOutputFull ) {
527
                ret = UNIX_ERR( pthread_cond_wait( &blio->outputCond, &blio->outputMutex ) );
528
                if( ret )
529
                   return ret;
530
             }
531
             ret = UNIX_ERR( pthread_mutex_unlock( &blio->outputMutex ) );
532
             if( ret )
533
                return ret;
534
#else
535
             Pa_Sleep( PA_MAC_BLIO_BUSY_WAIT_SLEEP_INTERVAL );
536
#endif
537
          }
538
       } while( framesAvailable == 0 && macStream->state != STOPPING );
539

    
540
       if( macStream->state == STOPPING )
541
       {
542
           break;
543
       }
544

    
545
       framesToTransfer = MIN( framesAvailable, framesRequested );
546
       framesTransferred = PaUtil_WriteRingBuffer( &blio->outputRingBuffer, (void *)cbuf, framesToTransfer );
547
       cbuf += framesTransferred * blio->outputSampleSizeActual * blio->outChan;
548
       framesRequested -= framesTransferred;
549

    
550
#ifdef PA_MAC_BLIO_MUTEX
551
       if( framesToTransfer == framesAvailable ) {
552
          /* we just filled up the buffer, so we need to mark it as filled. */
553
          ret = blioSetIsOutputFull( blio, true );
554
          if( ret )
555
             return ret;
556
          /* of course, in the meantime, we may have emptied the buffer, so
557
             so check for that, too, to avoid a race condition. */
558
          if( PaUtil_GetRingBufferWriteAvailable( &blio->outputRingBuffer ) ) {
559
             blioSetIsOutputFull( blio, false );
560
              /* FIXME remove or review this code, does not fix race, ret not set! */
561
             if( ret )
562
                return ret;
563
          }
564
       }
565
#endif
566
    }
567

    
568
    if ( macStream->state == STOPPING )
569
    {
570
        ret = paInternalError;
571
    }
572
    else if (ret == paNoError )
573
    {
574
        /*   Test for underflow. */
575
        ret = blio->statusFlags & paOutputUnderflow;
576

    
577
        /* report underflow only once: */
578
        if( ret )
579
        {
580
            OSAtomicAnd32( (uint32_t)(~paOutputUnderflow), &blio->statusFlags );
581
            ret = paOutputUnderflowed;
582
        }
583
    }
584

    
585
    return ret;
586
}
587

    
588
/*
589
 * Wait until the data in the buffer has finished playing.
590
 */
591
PaError waitUntilBlioWriteBufferIsEmpty( PaMacBlio *blio, double sampleRate,
592
                                        size_t framesPerBuffer )
593
{
594
    PaError result = paNoError;
595
    if( blio->outputRingBuffer.buffer ) {
596
        ring_buffer_size_t framesLeft = PaUtil_GetRingBufferReadAvailable( &blio->outputRingBuffer );
597

    
598
        /* Calculate when we should give up waiting. To be safe wait for two extra periods. */
599
        PaTime now = PaUtil_GetTime();
600
        PaTime startTime = now;
601
        PaTime timeoutTime = startTime + (framesLeft + (2 * framesPerBuffer)) / sampleRate;
602

    
603
        long msecPerBuffer = 1 + (long)( 1000.0 * framesPerBuffer / sampleRate);
604
        while( framesLeft > 0 && now < timeoutTime ) {
605
            VDBUG(( "waitUntilBlioWriteBufferIsFlushed: framesLeft = %d, framesPerBuffer = %ld\n",
606
                  framesLeft, framesPerBuffer ));
607
            Pa_Sleep( msecPerBuffer );
608
            framesLeft = PaUtil_GetRingBufferReadAvailable( &blio->outputRingBuffer );
609
            now = PaUtil_GetTime();
610
        }
611

    
612
        if( framesLeft > 0 )
613
        {
614
            VDBUG(( "waitUntilBlioWriteBufferIsFlushed: TIMED OUT - framesLeft = %d\n", framesLeft ));
615
            result = paTimedOut;
616
        }
617
    }
618
    return result;
619
}
620

    
621
signed long GetStreamReadAvailable( PaStream* stream )
622
{
623
    PaMacBlio *blio = & ((PaMacCoreStream*)stream) -> blio;
624
    VVDBUG(("GetStreamReadAvailable()\n"));
625

    
626
    return PaUtil_GetRingBufferReadAvailable( &blio->inputRingBuffer );
627
}
628

    
629

    
630
signed long GetStreamWriteAvailable( PaStream* stream )
631
{
632
    PaMacBlio *blio = & ((PaMacCoreStream*)stream) -> blio;
633
    VVDBUG(("GetStreamWriteAvailable()\n"));
634

    
635
    return PaUtil_GetRingBufferWriteAvailable( &blio->outputRingBuffer );
636
}
637