/[pcsx2_0.9.7]/trunk/3rdparty/portaudio/src/hostapi/coreaudio/pa_mac_core_blocking.c
ViewVC logotype

Contents of /trunk/3rdparty/portaudio/src/hostapi/coreaudio/pa_mac_core_blocking.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 31 - (show annotations) (download)
Tue Sep 7 03:24:11 2010 UTC (9 years, 5 months ago) by william
File MIME type: text/plain
File size: 19329 byte(s)
committing r3113 initial commit again...
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 fnuction 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 ) {
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 ) {
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 /* This should be called with the relevant info when initializing a stream for
112 callback. */
113 PaError initializeBlioRingBuffers(
114 PaMacBlio *blio,
115 PaSampleFormat inputSampleFormat,
116 PaSampleFormat outputSampleFormat,
117 size_t framesPerBuffer,
118 long ringBufferSize,
119 int inChan,
120 int outChan )
121 {
122 void *data;
123 int result;
124 OSStatus err;
125
126 /* zeroify things */
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;
132
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);
141
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;
150 #endif
151
152 /* setup ring buffers */
153 #ifdef PA_MAC_BLIO_MUTEX
154 result = PaMacCore_SetUnixError( pthread_mutex_init(&(blio->inputMutex),NULL), 0 );
155 if( result )
156 goto error;
157 result = UNIX_ERR( pthread_cond_init( &(blio->inputCond), NULL ) );
158 if( result )
159 goto error;
160 result = UNIX_ERR( pthread_mutex_init(&(blio->outputMutex),NULL) );
161 if( result )
162 goto error;
163 result = UNIX_ERR( pthread_cond_init( &(blio->outputCond), NULL ) );
164 #endif
165 if( inChan ) {
166 data = calloc( ringBufferSize, blio->inputSampleSizePow2*inChan );
167 if( !data )
168 {
169 result = paInsufficientMemory;
170 goto error;
171 }
172
173 err = PaUtil_InitializeRingBuffer(
174 &blio->inputRingBuffer,
175 1, ringBufferSize*blio->inputSampleSizePow2*inChan,
176 data );
177 assert( !err );
178 }
179 if( outChan ) {
180 data = calloc( ringBufferSize, blio->outputSampleSizePow2*outChan );
181 if( !data )
182 {
183 result = paInsufficientMemory;
184 goto error;
185 }
186
187 err = PaUtil_InitializeRingBuffer(
188 &blio->outputRingBuffer,
189 1, ringBufferSize*blio->outputSampleSizePow2*outChan,
190 data );
191 assert( !err );
192 }
193
194 result = resetBlioRingBuffers( blio );
195 if( result )
196 goto error;
197
198 return 0;
199
200 error:
201 destroyBlioRingBuffers( blio );
202 return result;
203 }
204
205 #ifdef PA_MAC_BLIO_MUTEX
206 PaError blioSetIsInputEmpty( PaMacBlio *blio, bool isEmpty )
207 {
208 PaError result = paNoError;
209 if( isEmpty == blio->isInputEmpty )
210 goto done;
211
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.
215 * - unlock.
216 * - broadcast to all listeners.
217 */
218 result = UNIX_ERR( pthread_mutex_lock( &blio->inputMutex ) );
219 if( result )
220 goto done;
221 blio->isInputEmpty = isEmpty;
222 result = UNIX_ERR( pthread_mutex_unlock( &blio->inputMutex ) );
223 if( result )
224 goto done;
225 result = UNIX_ERR( pthread_cond_broadcast( &blio->inputCond ) );
226 if( result )
227 goto done;
228
229 done:
230 return result;
231 }
232 PaError blioSetIsOutputFull( PaMacBlio *blio, bool isFull )
233 {
234 PaError result = paNoError;
235 if( isFull == blio->isOutputFull )
236 goto done;
237
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.
241 * - unlock.
242 * - broadcast to all listeners.
243 */
244 result = UNIX_ERR( pthread_mutex_lock( &blio->outputMutex ) );
245 if( result )
246 goto done;
247 blio->isOutputFull = isFull;
248 result = UNIX_ERR( pthread_mutex_unlock( &blio->outputMutex ) );
249 if( result )
250 goto done;
251 result = UNIX_ERR( pthread_cond_broadcast( &blio->outputCond ) );
252 if( result )
253 goto done;
254
255 done:
256 return result;
257 }
258 #endif
259
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 )
263 {
264 #ifdef PA_MAC__BLIO_MUTEX
265 int result;
266 #endif
267 blio->statusFlags = 0;
268 if( blio->outputRingBuffer.buffer ) {
269 PaUtil_FlushRingBuffer( &blio->outputRingBuffer );
270 bzero( blio->outputRingBuffer.buffer,
271 blio->outputRingBuffer.bufferSize );
272 /* Advance buffer */
273 PaUtil_AdvanceRingBufferWriteIndex( &blio->outputRingBuffer, blio->ringBufferFrames*blio->outputSampleSizeActual*blio->outChan );
274 //PaUtil_AdvanceRingBufferWriteIndex( &blio->outputRingBuffer, blio->outputRingBuffer.bufferSize );
275
276 /* Update isOutputFull. */
277 #ifdef PA_MAC__BLIO_MUTEX
278 result = blioSetIsOutputFull( blio, toAdvance == blio->outputRingBuffer.bufferSize );
279 if( result )
280 goto error;
281 #endif
282 /*
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 );
287 */
288 }
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 );
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 long avail;
348 long toRead;
349 long toWrite;
350 long read;
351 long written;
352
353 /* set flags returned by OS: */
354 OSAtomicOr32( statusFlags, &blio->statusFlags ) ;
355
356 /* --- Handle Input Buffer --- */
357 if( blio->inChan ) {
358 avail = PaUtil_GetRingBufferWriteAvailable( &blio->inputRingBuffer );
359
360 /* check for underflow */
361 if( avail < frameCount * blio->inputSampleSizeActual * blio->inChan )
362 OSAtomicOr32( paInputOverflow, &blio->statusFlags );
363
364 toRead = MIN( avail, frameCount * blio->inputSampleSizeActual * blio->inChan );
365
366 /* copy the data */
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 );
373 #endif
374 }
375
376
377 /* --- Handle Output Buffer --- */
378 if( blio->outChan ) {
379 avail = PaUtil_GetRingBufferReadAvailable( &blio->outputRingBuffer );
380
381 /* check for underflow */
382 if( avail < frameCount * blio->outputSampleSizeActual * blio->outChan )
383 OSAtomicOr32( paOutputUnderflow, &blio->statusFlags );
384
385 toWrite = MIN( avail, frameCount * blio->outputSampleSizeActual * blio->outChan );
386
387 if( toWrite != frameCount * blio->outputSampleSizeActual * blio->outChan )
388 bzero( ((char *)output)+toWrite,
389 frameCount * blio->outputSampleSizeActual * blio->outChan - toWrite );
390 /* copy the data */
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 );
400 #endif
401 }
402
403 return paContinue;
404 }
405
406 PaError ReadStream( PaStream* stream,
407 void *buffer,
408 unsigned long frames )
409 {
410 PaMacBlio *blio = & ((PaMacCoreStream*)stream) -> blio;
411 char *cbuf = (char *) buffer;
412 PaError ret = paNoError;
413 VVDBUG(("ReadStream()\n"));
414
415 while( frames > 0 ) {
416 long avail;
417 long toRead;
418 do {
419 avail = PaUtil_GetRingBufferReadAvailable( &blio->inputRingBuffer );
420 /*
421 printf( "Read Buffer is %%%g full: %ld of %ld.\n",
422 100 * (float)avail / (float) blio->inputRingBuffer.bufferSize,
423 avail, blio->inputRingBuffer.bufferSize );
424 */
425 if( avail == 0 ) {
426 #ifdef PA_MAC_BLIO_MUTEX
427 /**block when empty*/
428 ret = UNIX_ERR( pthread_mutex_lock( &blio->inputMutex ) );
429 if( ret )
430 return ret;
431 while( blio->isInputEmpty ) {
432 ret = UNIX_ERR( pthread_cond_wait( &blio->inputCond, &blio->inputMutex ) );
433 if( ret )
434 return ret;
435 }
436 ret = UNIX_ERR( pthread_mutex_unlock( &blio->inputMutex ) );
437 if( ret )
438 return ret;
439 #else
440 Pa_Sleep( PA_MAC_BLIO_BUSY_WAIT_SLEEP_INTERVAL );
441 #endif
442 }
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 );
447 cbuf += toRead;
448 frames -= toRead / ( blio->inputSampleSizeActual * blio->inChan );
449
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 );
454 if( ret )
455 return ret;
456 /* of course, in the meantime, the callback may have put some sats
457 in, so
458 so check for that, too, to avoid a race condition. */
459 if( PaUtil_GetRingBufferReadAvailable( &blio->inputRingBuffer ) ) {
460 blioSetIsInputEmpty( blio, false );
461 if( ret )
462 return ret;
463 }
464 #endif
465 }
466 }
467
468 /* Report either paNoError or paInputOverflowed. */
469 /* may also want to report other errors, but this is non-standard. */
470 ret = blio->statusFlags & paInputOverflow;
471
472 /* report underflow only once: */
473 if( ret ) {
474 OSAtomicAnd32( (uint32_t)(~paInputOverflow), &blio->statusFlags );
475 ret = paInputOverflowed;
476 }
477
478 return ret;
479 }
480
481
482 PaError WriteStream( PaStream* stream,
483 const void *buffer,
484 unsigned long frames )
485 {
486 PaMacBlio *blio = & ((PaMacCoreStream*)stream) -> blio;
487 char *cbuf = (char *) buffer;
488 PaError ret = paNoError;
489 VVDBUG(("WriteStream()\n"));
490
491 while( frames > 0 ) {
492 long avail = 0;
493 long toWrite;
494
495 do {
496 avail = PaUtil_GetRingBufferWriteAvailable( &blio->outputRingBuffer );
497 /*
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 );
501 */
502 if( avail == 0 ) {
503 #ifdef PA_MAC_BLIO_MUTEX
504 /*block while full*/
505 ret = UNIX_ERR( pthread_mutex_lock( &blio->outputMutex ) );
506 if( ret )
507 return ret;
508 while( blio->isOutputFull ) {
509 ret = UNIX_ERR( pthread_cond_wait( &blio->outputCond, &blio->outputMutex ) );
510 if( ret )
511 return ret;
512 }
513 ret = UNIX_ERR( pthread_mutex_unlock( &blio->outputMutex ) );
514 if( ret )
515 return ret;
516 #else
517 Pa_Sleep( PA_MAC_BLIO_BUSY_WAIT_SLEEP_INTERVAL );
518 #endif
519 }
520 } while( avail == 0 );
521
522 toWrite = MIN( avail, frames * blio->outputSampleSizeActual * blio->outChan );
523 toWrite -= toWrite % blio->outputSampleSizeActual * blio->outChan ;
524 PaUtil_WriteRingBuffer( &blio->outputRingBuffer, (void *)cbuf, toWrite );
525 cbuf += toWrite;
526 frames -= toWrite / ( blio->outputSampleSizeActual * blio->outChan );
527
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 );
532 if( ret )
533 return ret;
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 );
538 if( ret )
539 return ret;
540 }
541 }
542 #endif
543 }
544
545 /* Report either paNoError or paOutputUnderflowed. */
546 /* may also want to report other errors, but this is non-standard. */
547 ret = blio->statusFlags & paOutputUnderflow;
548
549 /* report underflow only once: */
550 if( ret ) {
551 OSAtomicAnd32( (uint32_t)(~paOutputUnderflow), &blio->statusFlags );
552 ret = paOutputUnderflowed;
553 }
554
555 return ret;
556 }
557
558 /*
559 *
560 */
561 void waitUntilBlioWriteBufferIsFlushed( PaMacBlio *blio )
562 {
563 if( blio->outputRingBuffer.buffer ) {
564 long avail = PaUtil_GetRingBufferWriteAvailable( &blio->outputRingBuffer );
565 while( avail != blio->outputRingBuffer.bufferSize ) {
566 if( avail == 0 )
567 Pa_Sleep( PA_MAC_BLIO_BUSY_WAIT_SLEEP_INTERVAL );
568 avail = PaUtil_GetRingBufferWriteAvailable( &blio->outputRingBuffer );
569 }
570 }
571 }
572
573
574 signed long GetStreamReadAvailable( PaStream* stream )
575 {
576 PaMacBlio *blio = & ((PaMacCoreStream*)stream) -> blio;
577 VVDBUG(("GetStreamReadAvailable()\n"));
578
579 return PaUtil_GetRingBufferReadAvailable( &blio->inputRingBuffer )
580 / ( blio->inputSampleSizeActual * blio->inChan );
581 }
582
583
584 signed long GetStreamWriteAvailable( PaStream* stream )
585 {
586 PaMacBlio *blio = & ((PaMacCoreStream*)stream) -> blio;
587 VVDBUG(("GetStreamWriteAvailable()\n"));
588
589 return PaUtil_GetRingBufferWriteAvailable( &blio->outputRingBuffer )
590 / ( blio->outputSampleSizeActual * blio->outChan );
591 }
592

  ViewVC Help
Powered by ViewVC 1.1.22