2
* Implementation of the PortAudio API for Apple AUHAL
4
* PortAudio Portable Real-Time Audio Library
5
* Latest Version at: http://www.portaudio.com
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)
10
* Dominic's code was based on code by Phil Burk, Darren Gibbs,
11
* Gord Peters, Stephane Letz, and Greg Pfiel.
13
* The following people also deserve acknowledgements:
15
* Olivier Tristan for feedback and testing
16
* Glenn Zelniker and Z-Systems engineering for sponsoring the Blocking I/O
20
* Based on the Open Source API proposed by Ross Bencina
21
* Copyright (c) 1999-2002 Ross Bencina, Phil Burk
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:
31
* The above copyright notice and this permission notice shall be
32
* included in all copies or substantial portions of the Software.
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.
44
* The text above constitutes the entire PortAudio license; however,
45
* the PortAudio community also makes the following non-binding requests:
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
58
This file contains the implementation
59
required for blocking I/O. It is separated from pa_mac_core.c simply to ease
63
#include "pa_mac_core_blocking.h"
64
#include "pa_mac_core_internal.h"
66
#ifdef MOSX_USE_NON_ATOMIC_FLAG_BITS
67
# define OSAtomicOr32( a, b ) ( (*(b)) |= (a) )
68
# define OSAtomicAnd32( a, b ) ( (*(b)) &= (a) )
70
# include <libkern/OSAtomic.h>
74
* This fnuction determines the size of a particular sample format.
75
* if the format is not recognized, this returns zero.
77
static size_t computeSampleSizeFromFormat( PaSampleFormat format )
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;
89
* Same as computeSampleSizeFromFormat, except that if
90
* the size is not a power of two, it returns the next power of two up
92
static size_t computeSampleSizeFromFormatPow2( PaSampleFormat format )
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;
107
* Functions for initializing, resetting, and destroying BLIO structures.
111
/* This should be called with the relevant info when initializing a stream for
113
PaError initializeBlioRingBuffers(
115
PaSampleFormat inputSampleFormat,
116
PaSampleFormat outputSampleFormat,
117
size_t framesPerBuffer,
127
bzero( blio, sizeof( PaMacBlio ) );
128
/* this is redundant, but the buffers are used to check
129
if the bufffers have been initialized, so we do it explicitly. */
130
blio->inputRingBuffer.buffer = NULL;
131
blio->outputRingBuffer.buffer = NULL;
133
/* initialize simple data */
134
blio->ringBufferFrames = ringBufferSize;
135
blio->inputSampleFormat = inputSampleFormat;
136
blio->inputSampleSizeActual = computeSampleSizeFromFormat(inputSampleFormat);
137
blio->inputSampleSizePow2 = computeSampleSizeFromFormatPow2(inputSampleFormat);
138
blio->outputSampleFormat = outputSampleFormat;
139
blio->outputSampleSizeActual = computeSampleSizeFromFormat(outputSampleFormat);
140
blio->outputSampleSizePow2 = computeSampleSizeFromFormatPow2(outputSampleFormat);
142
blio->framesPerBuffer = framesPerBuffer;
143
blio->inChan = inChan;
144
blio->outChan = outChan;
145
blio->statusFlags = 0;
146
blio->errors = paNoError;
147
#ifdef PA_MAC_BLIO_MUTEX
148
blio->isInputEmpty = false;
149
blio->isOutputFull = false;
152
/* setup ring buffers */
153
#ifdef PA_MAC_BLIO_MUTEX
154
result = PaMacCore_SetUnixError( pthread_mutex_init(&(blio->inputMutex),NULL), 0 );
157
result = UNIX_ERR( pthread_cond_init( &(blio->inputCond), NULL ) );
160
result = UNIX_ERR( pthread_mutex_init(&(blio->outputMutex),NULL) );
163
result = UNIX_ERR( pthread_cond_init( &(blio->outputCond), NULL ) );
166
data = calloc( ringBufferSize, blio->inputSampleSizePow2*inChan );
169
result = paInsufficientMemory;
173
err = PaUtil_InitializeRingBuffer(
174
&blio->inputRingBuffer,
175
1, ringBufferSize*blio->inputSampleSizePow2*inChan,
180
data = calloc( ringBufferSize, blio->outputSampleSizePow2*outChan );
183
result = paInsufficientMemory;
187
err = PaUtil_InitializeRingBuffer(
188
&blio->outputRingBuffer,
189
1, ringBufferSize*blio->outputSampleSizePow2*outChan,
194
result = resetBlioRingBuffers( blio );
201
destroyBlioRingBuffers( blio );
205
#ifdef PA_MAC_BLIO_MUTEX
206
PaError blioSetIsInputEmpty( PaMacBlio *blio, bool isEmpty )
208
PaError result = paNoError;
209
if( isEmpty == blio->isInputEmpty )
212
/* we need to update the value. Here's what we do:
213
* - Lock the mutex, so noone else can write.
214
* - update the value.
216
* - broadcast to all listeners.
218
result = UNIX_ERR( pthread_mutex_lock( &blio->inputMutex ) );
221
blio->isInputEmpty = isEmpty;
222
result = UNIX_ERR( pthread_mutex_unlock( &blio->inputMutex ) );
225
result = UNIX_ERR( pthread_cond_broadcast( &blio->inputCond ) );
232
PaError blioSetIsOutputFull( PaMacBlio *blio, bool isFull )
234
PaError result = paNoError;
235
if( isFull == blio->isOutputFull )
238
/* we need to update the value. Here's what we do:
239
* - Lock the mutex, so noone else can write.
240
* - update the value.
242
* - broadcast to all listeners.
244
result = UNIX_ERR( pthread_mutex_lock( &blio->outputMutex ) );
247
blio->isOutputFull = isFull;
248
result = UNIX_ERR( pthread_mutex_unlock( &blio->outputMutex ) );
251
result = UNIX_ERR( pthread_cond_broadcast( &blio->outputCond ) );
260
/* This should be called after stopping or aborting the stream, so that on next
261
start, the buffers will be ready. */
262
PaError resetBlioRingBuffers( PaMacBlio *blio )
264
#ifdef PA_MAC__BLIO_MUTEX
267
blio->statusFlags = 0;
268
if( blio->outputRingBuffer.buffer ) {
269
PaUtil_FlushRingBuffer( &blio->outputRingBuffer );
270
bzero( blio->outputRingBuffer.buffer,
271
blio->outputRingBuffer.bufferSize );
273
PaUtil_AdvanceRingBufferWriteIndex( &blio->outputRingBuffer, blio->ringBufferFrames*blio->outputSampleSizeActual*blio->outChan );
274
//PaUtil_AdvanceRingBufferWriteIndex( &blio->outputRingBuffer, blio->outputRingBuffer.bufferSize );
276
/* Update isOutputFull. */
277
#ifdef PA_MAC__BLIO_MUTEX
278
result = blioSetIsOutputFull( blio, toAdvance == blio->outputRingBuffer.bufferSize );
283
printf( "------%d\n" , blio->framesPerBuffer );
284
printf( "------%d\n" , blio->outChan );
285
printf( "------%d\n" , blio->outputSampleSize );
286
printf( "------%d\n" , blio->framesPerBuffer*blio->outChan*blio->outputSampleSize );
289
if( blio->inputRingBuffer.buffer ) {
290
PaUtil_FlushRingBuffer( &blio->inputRingBuffer );
291
bzero( blio->inputRingBuffer.buffer,
292
blio->inputRingBuffer.bufferSize );
293
/* Update isInputEmpty. */
294
#ifdef PA_MAC__BLIO_MUTEX
295
result = blioSetIsInputEmpty( blio, true );
301
#ifdef PA_MAC__BLIO_MUTEX
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 )
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;
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;
331
blio->outputRingBuffer.buffer = NULL;
337
* this is the BlioCallback function. It expects to recieve a PaMacBlio Object
338
* pointer as userData.
341
int BlioCallback( const void *input, void *output, unsigned long frameCount,
342
const PaStreamCallbackTimeInfo* timeInfo,
343
PaStreamCallbackFlags statusFlags,
346
PaMacBlio *blio = (PaMacBlio*)userData;
353
/* set flags returned by OS: */
354
OSAtomicOr32( statusFlags, &blio->statusFlags ) ;
356
/* --- Handle Input Buffer --- */
358
avail = PaUtil_GetRingBufferWriteAvailable( &blio->inputRingBuffer );
360
/* check for underflow */
361
if( avail < frameCount * blio->inputSampleSizeActual * blio->inChan )
362
OSAtomicOr32( paInputOverflow, &blio->statusFlags );
364
toRead = MIN( avail, frameCount * blio->inputSampleSizeActual * blio->inChan );
367
/*printf( "reading %d\n", toRead );*/
368
read = PaUtil_WriteRingBuffer( &blio->inputRingBuffer, input, toRead );
369
assert( toRead == read );
370
#ifdef PA_MAC__BLIO_MUTEX
371
/* Priority inversion. See notes below. */
372
blioSetIsInputEmpty( blio, false );
377
/* --- Handle Output Buffer --- */
378
if( blio->outChan ) {
379
avail = PaUtil_GetRingBufferReadAvailable( &blio->outputRingBuffer );
381
/* check for underflow */
382
if( avail < frameCount * blio->outputSampleSizeActual * blio->outChan )
383
OSAtomicOr32( paOutputUnderflow, &blio->statusFlags );
385
toWrite = MIN( avail, frameCount * blio->outputSampleSizeActual * blio->outChan );
387
if( toWrite != frameCount * blio->outputSampleSizeActual * blio->outChan )
388
bzero( ((char *)output)+toWrite,
389
frameCount * blio->outputSampleSizeActual * blio->outChan - toWrite );
391
/*printf( "writing %d\n", toWrite );*/
392
written = PaUtil_ReadRingBuffer( &blio->outputRingBuffer, output, toWrite );
393
assert( toWrite == written );
394
#ifdef PA_MAC__BLIO_MUTEX
395
/* We have a priority inversion here. However, we will only have to
396
wait if this was true and is now false, which means we've got
397
some room in the buffer.
398
Hopefully problems will be minimized. */
399
blioSetIsOutputFull( blio, false );
406
PaError ReadStream( PaStream* stream,
408
unsigned long frames )
410
PaMacBlio *blio = & ((PaMacCoreStream*)stream) -> blio;
411
char *cbuf = (char *) buffer;
412
PaError ret = paNoError;
413
VVDBUG(("ReadStream()\n"));
415
while( frames > 0 ) {
419
avail = PaUtil_GetRingBufferReadAvailable( &blio->inputRingBuffer );
421
printf( "Read Buffer is %%%g full: %ld of %ld.\n",
422
100 * (float)avail / (float) blio->inputRingBuffer.bufferSize,
423
avail, blio->inputRingBuffer.bufferSize );
426
#ifdef PA_MAC_BLIO_MUTEX
427
/**block when empty*/
428
ret = UNIX_ERR( pthread_mutex_lock( &blio->inputMutex ) );
431
while( blio->isInputEmpty ) {
432
ret = UNIX_ERR( pthread_cond_wait( &blio->inputCond, &blio->inputMutex ) );
436
ret = UNIX_ERR( pthread_mutex_unlock( &blio->inputMutex ) );
440
Pa_Sleep( PA_MAC_BLIO_BUSY_WAIT_SLEEP_INTERVAL );
443
} while( avail == 0 );
444
toRead = MIN( avail, frames * blio->inputSampleSizeActual * blio->inChan );
445
toRead -= toRead % blio->inputSampleSizeActual * blio->inChan ;
446
PaUtil_ReadRingBuffer( &blio->inputRingBuffer, (void *)cbuf, toRead );
448
frames -= toRead / ( blio->inputSampleSizeActual * blio->inChan );
450
if( toRead == avail ) {
451
#ifdef PA_MAC_BLIO_MUTEX
452
/* we just emptied the buffer, so we need to mark it as empty. */
453
ret = blioSetIsInputEmpty( blio, true );
456
/* of course, in the meantime, the callback may have put some sats
458
so check for that, too, to avoid a race condition. */
459
if( PaUtil_GetRingBufferReadAvailable( &blio->inputRingBuffer ) ) {
460
blioSetIsInputEmpty( blio, false );
468
/* Report either paNoError or paInputOverflowed. */
469
/* may also want to report other errors, but this is non-standard. */
470
ret = blio->statusFlags & paInputOverflow;
472
/* report underflow only once: */
474
OSAtomicAnd32( (uint32_t)(~paInputOverflow), &blio->statusFlags );
475
ret = paInputOverflowed;
482
PaError WriteStream( PaStream* stream,
484
unsigned long frames )
486
PaMacBlio *blio = & ((PaMacCoreStream*)stream) -> blio;
487
char *cbuf = (char *) buffer;
488
PaError ret = paNoError;
489
VVDBUG(("WriteStream()\n"));
491
while( frames > 0 ) {
496
avail = PaUtil_GetRingBufferWriteAvailable( &blio->outputRingBuffer );
498
printf( "Write Buffer is %%%g full: %ld of %ld.\n",
499
100 - 100 * (float)avail / (float) blio->outputRingBuffer.bufferSize,
500
avail, blio->outputRingBuffer.bufferSize );
503
#ifdef PA_MAC_BLIO_MUTEX
505
ret = UNIX_ERR( pthread_mutex_lock( &blio->outputMutex ) );
508
while( blio->isOutputFull ) {
509
ret = UNIX_ERR( pthread_cond_wait( &blio->outputCond, &blio->outputMutex ) );
513
ret = UNIX_ERR( pthread_mutex_unlock( &blio->outputMutex ) );
517
Pa_Sleep( PA_MAC_BLIO_BUSY_WAIT_SLEEP_INTERVAL );
520
} while( avail == 0 );
522
toWrite = MIN( avail, frames * blio->outputSampleSizeActual * blio->outChan );
523
toWrite -= toWrite % blio->outputSampleSizeActual * blio->outChan ;
524
PaUtil_WriteRingBuffer( &blio->outputRingBuffer, (void *)cbuf, toWrite );
526
frames -= toWrite / ( blio->outputSampleSizeActual * blio->outChan );
528
#ifdef PA_MAC_BLIO_MUTEX
529
if( toWrite == avail ) {
530
/* we just filled up the buffer, so we need to mark it as filled. */
531
ret = blioSetIsOutputFull( blio, true );
534
/* of course, in the meantime, we may have emptied the buffer, so
535
so check for that, too, to avoid a race condition. */
536
if( PaUtil_GetRingBufferWriteAvailable( &blio->outputRingBuffer ) ) {
537
blioSetIsOutputFull( blio, false );
545
/* Report either paNoError or paOutputUnderflowed. */
546
/* may also want to report other errors, but this is non-standard. */
547
ret = blio->statusFlags & paOutputUnderflow;
549
/* report underflow only once: */
551
OSAtomicAnd32( (uint32_t)(~paOutputUnderflow), &blio->statusFlags );
552
ret = paOutputUnderflowed;
561
void waitUntilBlioWriteBufferIsFlushed( PaMacBlio *blio )
563
if( blio->outputRingBuffer.buffer ) {
564
long avail = PaUtil_GetRingBufferWriteAvailable( &blio->outputRingBuffer );
565
while( avail != blio->outputRingBuffer.bufferSize ) {
567
Pa_Sleep( PA_MAC_BLIO_BUSY_WAIT_SLEEP_INTERVAL );
568
avail = PaUtil_GetRingBufferWriteAvailable( &blio->outputRingBuffer );
574
signed long GetStreamReadAvailable( PaStream* stream )
576
PaMacBlio *blio = & ((PaMacCoreStream*)stream) -> blio;
577
VVDBUG(("GetStreamReadAvailable()\n"));
579
return PaUtil_GetRingBufferReadAvailable( &blio->inputRingBuffer )
580
/ ( blio->inputSampleSizeActual * blio->inChan );
584
signed long GetStreamWriteAvailable( PaStream* stream )
586
PaMacBlio *blio = & ((PaMacCoreStream*)stream) -> blio;
587
VVDBUG(("GetStreamWriteAvailable()\n"));
589
return PaUtil_GetRingBufferWriteAvailable( &blio->outputRingBuffer )
590
/ ( blio->outputSampleSizeActual * blio->outChan );