1 /*
2 * Portable Audio I/O Library
3 * Host Independant Layer
4 *
5 * Based on the Open Source API proposed by Ross Bencina
6 * Copyright (c) 1999-2000 Phil Burk
7 *
8 * Permission is hereby granted, free of charge, to any person obtaining
9 * a copy of this software and associated documentation files
10 * (the "Software"), to deal in the Software without restriction,
11 * including without limitation the rights to use, copy, modify, merge,
12 * publish, distribute, sublicense, and/or sell copies of the Software,
13 * and to permit persons to whom the Software is furnished to do so,
14 * subject to the following conditions:
15 *
16 * The above copyright notice and this permission notice shall be
17 * included in all copies or substantial portions of the Software.
18 *
19 * Any person wishing to distribute modifications to the Software is
20 * requested to send the modifications to the original developer so that
21 * they can be incorporated into the canonical version.
22 *
23 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
26 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
27 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
28 * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
29 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30 *
31 */
32
33 /* Modification History:
34 PLB20010422 - apply Mike Berry's changes for CodeWarrior on PC
35 PLB20010820 - fix dither and shift for recording PaUInt8 format
36 */
37
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #include <math.h>
42
43 /* PLB20010422 - "memory.h" doesn't work on CodeWarrior for PC. Thanks Mike Berry for the mod. */
44 #ifdef _WIN32
45 #ifndef __MWERKS__
46 #include <memory.h>
47 #endif /* __MWERKS__ */
48 #else /* !_WIN32 */
49 #include <memory.h>
50 #endif /* _WIN32 */
51
52 #if 0
53 #include "portaudio.h"
54 #include "pa_host.h"
55 #include "pa_trace.h"
56 #endif
57
58 /* The reason we might NOT want to validate the rate before opening the stream
59 * is because many DirectSound drivers lie about the rates they actually support.
60 */
61 #define PA_VALIDATE_RATE (0) /* If true validate sample rate against driver info. */
62
63 /*
64 O- maybe not allocate past_InputBuffer and past_OutputBuffer if not needed for conversion
65 */
66
67 #ifndef FALSE
68 #define FALSE (0)
69 #define TRUE (!FALSE)
70 #endif
71
72 #define PRINT(x) { printf x; fflush(stdout); }
73 #define ERR_RPT(x) PRINT(x)
74 #define DBUG(x) /* PRINT(x) */
75 #define DBUGX(x) /* PRINT(x) */
76
77 static int gInitCount = 0; /* Count number of times Pa_Initialize() called to allow nesting and overlapping. */
78
79 static PaError Pa_KillStream( PortAudioStream *stream, int abort );
80
81 /***********************************************************************/
PaHost_FindClosestTableEntry(double allowableError,const double * rateTable,int numRates,double frameRate)82 int PaHost_FindClosestTableEntry( double allowableError, const double *rateTable, int numRates, double frameRate )
83 {
84 double err, minErr = allowableError;
85 int i, bestFit = -1;
86
87 for( i=0; i<numRates; i++ )
88 {
89 err = fabs( frameRate - rateTable[i] );
90 if( err < minErr )
91 {
92 minErr = err;
93 bestFit = i;
94 }
95 }
96 return bestFit;
97 }
98
99 /**************************************************************************
100 ** Make sure sample rate is legal and also convert to enumeration for driver.
101 */
PaHost_ValidateSampleRate(PaDeviceID id,double requestedFrameRate,double * closestFrameRatePtr)102 PaError PaHost_ValidateSampleRate( PaDeviceID id, double requestedFrameRate,
103 double *closestFrameRatePtr )
104 {
105 long bestRateIndex;
106 const PaDeviceInfo *pdi;
107 pdi = Pa_GetDeviceInfo( id );
108 if( pdi == NULL )
109 {
110 return paInvalidDeviceId;
111 }
112
113 if( pdi->numSampleRates == -1 )
114 {
115 /* Is it out of range? */
116 if( (requestedFrameRate < pdi->sampleRates[0]) ||
117 (requestedFrameRate > pdi->sampleRates[1]) )
118 {
119 return paInvalidSampleRate;
120 }
121
122 *closestFrameRatePtr = requestedFrameRate;
123 }
124 else
125 {
126 bestRateIndex = PaHost_FindClosestTableEntry( 1.0, pdi->sampleRates, pdi->numSampleRates, requestedFrameRate );
127 if( bestRateIndex < 0 ) return paInvalidSampleRate;
128 *closestFrameRatePtr = pdi->sampleRates[bestRateIndex];
129 }
130 return paNoError;
131 }
132
133 /*************************************************************************/
Pa_OpenStream(PortAudioStream ** streamPtrPtr,PaDeviceID inputDeviceID,int numInputChannels,PaSampleFormat inputSampleFormat,void * inputDriverInfo,PaDeviceID outputDeviceID,int numOutputChannels,PaSampleFormat outputSampleFormat,void * outputDriverInfo,double sampleRate,unsigned long framesPerBuffer,unsigned long numberOfBuffers,unsigned long streamFlags,PortAudioCallback * callback,void * userData)134 PaError Pa_OpenStream(
135 PortAudioStream** streamPtrPtr,
136 PaDeviceID inputDeviceID,
137 int numInputChannels,
138 PaSampleFormat inputSampleFormat,
139 void *inputDriverInfo,
140 PaDeviceID outputDeviceID,
141 int numOutputChannels,
142 PaSampleFormat outputSampleFormat,
143 void *outputDriverInfo,
144 double sampleRate,
145 unsigned long framesPerBuffer,
146 unsigned long numberOfBuffers,
147 unsigned long streamFlags,
148 PortAudioCallback *callback,
149 void *userData )
150 {
151 internalPortAudioStream *past = NULL;
152 PaError result = paNoError;
153 int bitsPerInputSample;
154 int bitsPerOutputSample;
155 /* Print passed parameters. */
156 DBUG(("Pa_OpenStream( %p, %d, %d, %d, %p, /* input */ \n",
157 streamPtrPtr, inputDeviceID, numInputChannels,
158 inputSampleFormat, inputDriverInfo ));
159 DBUG((" %d, %d, %d, %p, /* output */\n",
160 outputDeviceID, numOutputChannels,
161 outputSampleFormat, outputDriverInfo ));
162 DBUG((" %g, %d, %d, 0x%x, , %p )\n",
163 sampleRate, framesPerBuffer, numberOfBuffers,
164 streamFlags, userData ));
165
166 /* Check for parameter errors. */
167 if( (streamFlags & ~(paClipOff | paDitherOff)) != 0 ) return paInvalidFlag;
168 if( streamPtrPtr == NULL ) return paBadStreamPtr;
169 if( inputDriverInfo != NULL ) return paHostError; /* REVIEW */
170 if( outputDriverInfo != NULL ) return paHostError; /* REVIEW */
171 if( (inputDeviceID < 0) && ( outputDeviceID < 0) ) return paInvalidDeviceId;
172 if( (outputDeviceID >= Pa_CountDevices()) || (inputDeviceID >= Pa_CountDevices()) )
173 {
174 return paInvalidDeviceId;
175 }
176 if( (numInputChannels <= 0) && ( numOutputChannels <= 0) ) return paInvalidChannelCount;
177
178 #if SUPPORT_AUDIO_CAPTURE
179 if( inputDeviceID >= 0 )
180 {
181 PaError size = Pa_GetSampleSize( inputSampleFormat );
182 if( size < 0 ) return size;
183 bitsPerInputSample = 8 * size;
184 if( (numInputChannels <= 0) ) return paInvalidChannelCount;
185 }
186 #else
187 if( inputDeviceID >= 0 )
188 {
189 return paInvalidChannelCount;
190 }
191 #endif /* SUPPORT_AUDIO_CAPTURE */
192 else
193 {
194 if( numInputChannels > 0 ) return paInvalidChannelCount;
195 bitsPerInputSample = 0;
196 }
197
198 if( outputDeviceID >= 0 )
199 {
200 PaError size = Pa_GetSampleSize( outputSampleFormat );
201 if( size < 0 ) return size;
202 bitsPerOutputSample = 8 * size;
203 if( (numOutputChannels <= 0) ) return paInvalidChannelCount;
204 }
205 else
206 {
207 if( numOutputChannels > 0 ) return paInvalidChannelCount;
208 bitsPerOutputSample = 0;
209 }
210
211 if( callback == NULL ) return paNullCallback;
212
213 /* Allocate and clear stream structure. */
214 past = (internalPortAudioStream *) PaHost_AllocateFastMemory( sizeof(internalPortAudioStream) );
215 if( past == NULL ) return paInsufficientMemory;
216 memset( past, 0, sizeof(internalPortAudioStream) );
217 AddTraceMessage("Pa_OpenStream: past", (long) past );
218
219 past->past_Magic = PA_MAGIC; /* Set ID to catch bugs. */
220 past->past_FramesPerUserBuffer = framesPerBuffer;
221 past->past_NumUserBuffers = numberOfBuffers; /* NOTE - PaHost_OpenStream() NMUST CHECK FOR ZERO! */
222 past->past_Callback = callback;
223 past->past_UserData = userData;
224 past->past_OutputSampleFormat = outputSampleFormat;
225 past->past_InputSampleFormat = inputSampleFormat;
226 past->past_OutputDeviceID = outputDeviceID;
227 past->past_InputDeviceID = inputDeviceID;
228 past->past_NumInputChannels = numInputChannels;
229 past->past_NumOutputChannels = numOutputChannels;
230 past->past_Flags = streamFlags;
231
232 /* Check for absurd sample rates. */
233 if( (sampleRate < 1000.0) || (sampleRate > 200000.0) )
234 {
235 result = paInvalidSampleRate;
236 goto cleanup;
237 }
238
239 /* Allocate buffers that may be used for format conversion from user to native buffers. */
240 if( numInputChannels > 0 )
241 {
242
243 #if PA_VALIDATE_RATE
244 result = PaHost_ValidateSampleRate( inputDeviceID, sampleRate, &past->past_SampleRate );
245 if( result < 0 )
246 {
247 goto cleanup;
248 }
249 #else
250 past->past_SampleRate = sampleRate;
251 #endif
252 /* Allocate single Input buffer. */
253 past->past_InputBufferSize = framesPerBuffer * numInputChannels * ((bitsPerInputSample+7) / 8);
254 past->past_InputBuffer = PaHost_AllocateFastMemory(past->past_InputBufferSize);
255 if( past->past_InputBuffer == NULL )
256 {
257 result = paInsufficientMemory;
258 goto cleanup;
259 }
260 }
261 else
262 {
263 past->past_InputBuffer = NULL;
264 }
265
266 /* Allocate single Output buffer. */
267 if( numOutputChannels > 0 )
268 {
269 #if PA_VALIDATE_RATE
270 result = PaHost_ValidateSampleRate( outputDeviceID, sampleRate, &past->past_SampleRate );
271 if( result < 0 )
272 {
273 goto cleanup;
274 }
275 #else
276 past->past_SampleRate = sampleRate;
277 #endif
278 past->past_OutputBufferSize = framesPerBuffer * numOutputChannels * ((bitsPerOutputSample+7) / 8);
279 past->past_OutputBuffer = PaHost_AllocateFastMemory(past->past_OutputBufferSize);
280 if( past->past_OutputBuffer == NULL )
281 {
282 result = paInsufficientMemory;
283 goto cleanup;
284 }
285 }
286 else
287 {
288 past->past_OutputBuffer = NULL;
289 }
290
291 result = PaHost_OpenStream( past );
292 if( result < 0 ) goto cleanup;
293
294 *streamPtrPtr = (void *) past;
295
296 return result;
297
298 cleanup:
299 if( past != NULL ) Pa_CloseStream( past );
300 *streamPtrPtr = NULL;
301 return result;
302 }
303
304
305 /*************************************************************************/
Pa_OpenDefaultStream(PortAudioStream ** stream,int numInputChannels,int numOutputChannels,PaSampleFormat sampleFormat,double sampleRate,unsigned long framesPerBuffer,unsigned long numberOfBuffers,PortAudioCallback * callback,void * userData)306 PaError Pa_OpenDefaultStream( PortAudioStream** stream,
307 int numInputChannels,
308 int numOutputChannels,
309 PaSampleFormat sampleFormat,
310 double sampleRate,
311 unsigned long framesPerBuffer,
312 unsigned long numberOfBuffers,
313 PortAudioCallback *callback,
314 void *userData )
315 {
316 return Pa_OpenStream(
317 stream,
318 ((numInputChannels > 0) ? Pa_GetDefaultInputDeviceID() : paNoDevice),
319 numInputChannels, sampleFormat, NULL,
320 ((numOutputChannels > 0) ? Pa_GetDefaultOutputDeviceID() : paNoDevice),
321 numOutputChannels, sampleFormat, NULL,
322 sampleRate, framesPerBuffer, numberOfBuffers, paNoFlag, callback, userData );
323 }
324
325 /*************************************************************************/
Pa_CloseStream(PortAudioStream * stream)326 PaError Pa_CloseStream( PortAudioStream* stream)
327 {
328 PaError result;
329 internalPortAudioStream *past;
330
331 DBUG(("Pa_CloseStream()\n"));
332 if( stream == NULL ) return paBadStreamPtr;
333 past = (internalPortAudioStream *) stream;
334
335 Pa_AbortStream( past );
336 result = PaHost_CloseStream( past );
337
338 if( past->past_InputBuffer ) PaHost_FreeFastMemory( past->past_InputBuffer, past->past_InputBufferSize );
339 if( past->past_OutputBuffer ) PaHost_FreeFastMemory( past->past_OutputBuffer, past->past_OutputBufferSize );
340 PaHost_FreeFastMemory( past, sizeof(internalPortAudioStream) );
341
342 return result;
343 }
344
345 /*************************************************************************/
Pa_StartStream(PortAudioStream * stream)346 PaError Pa_StartStream( PortAudioStream *stream )
347 {
348 PaError result = paHostError;
349 internalPortAudioStream *past;
350
351 if( stream == NULL ) return paBadStreamPtr;
352 past = (internalPortAudioStream *) stream;
353
354 past->past_FrameCount = 0.0;
355
356 if( past->past_NumInputChannels > 0 )
357 {
358 result = PaHost_StartInput( past );
359 DBUG(("Pa_StartStream: PaHost_StartInput returned = 0x%X.\n", result));
360 if( result < 0 ) goto error;
361 }
362
363 if( past->past_NumOutputChannels > 0 )
364 {
365 result = PaHost_StartOutput( past );
366 DBUG(("Pa_StartStream: PaHost_StartOutput returned = 0x%X.\n", result));
367 if( result < 0 ) goto error;
368 }
369
370 result = PaHost_StartEngine( past );
371 DBUG(("Pa_StartStream: PaHost_StartEngine returned = 0x%X.\n", result));
372 if( result < 0 ) goto error;
373
374 return paNoError;
375
376 error:
377 return result;
378 }
379
380 /*************************************************************************/
Pa_StopStream(PortAudioStream * stream)381 PaError Pa_StopStream( PortAudioStream *stream )
382 {
383 return Pa_KillStream( stream, 0 );
384 }
385
386 /*************************************************************************/
Pa_AbortStream(PortAudioStream * stream)387 PaError Pa_AbortStream( PortAudioStream *stream )
388 {
389 return Pa_KillStream( stream, 1 );
390 }
391
392 /*************************************************************************/
Pa_KillStream(PortAudioStream * stream,int abort)393 static PaError Pa_KillStream( PortAudioStream *stream, int abort )
394 {
395 PaError result = paNoError;
396 internalPortAudioStream *past;
397
398 DBUG(("Pa_StopStream().\n"));
399 if( stream == NULL ) return paBadStreamPtr;
400 past = (internalPortAudioStream *) stream;
401
402 if( (past->past_NumInputChannels > 0) || (past->past_NumOutputChannels > 0) )
403 {
404 result = PaHost_StopEngine( past, abort );
405 DBUG(("Pa_StopStream: PaHost_StopEngine returned = 0x%X.\n", result));
406 if( result < 0 ) goto error;
407 }
408
409 if( past->past_NumInputChannels > 0 )
410 {
411 result = PaHost_StopInput( past, abort );
412 DBUG(("Pa_StopStream: PaHost_StopInput returned = 0x%X.\n", result));
413 if( result != paNoError ) goto error;
414 }
415
416 if( past->past_NumOutputChannels > 0 )
417 {
418 result = PaHost_StopOutput( past, abort );
419 DBUG(("Pa_StopStream: PaHost_StopOutput returned = 0x%X.\n", result));
420 if( result != paNoError ) goto error;
421 }
422
423 error:
424 past->past_Usage = 0;
425 past->past_IfLastExitValid = 0;
426
427 return result;
428 }
429
430 /*************************************************************************/
Pa_StreamActive(PortAudioStream * stream)431 PaError Pa_StreamActive( PortAudioStream *stream )
432 {
433 internalPortAudioStream *past;
434 if( stream == NULL ) return paBadStreamPtr;
435 past = (internalPortAudioStream *) stream;
436 return PaHost_StreamActive( past );
437 }
438
439 /*************************************************************************/
Pa_GetErrorText(PaError errnum)440 const char *Pa_GetErrorText( PaError errnum )
441 {
442 const char *msg;
443
444 switch(errnum)
445 {
446 case paNoError: msg = "Success"; break;
447 case paHostError: msg = "Host error."; break;
448 case paInvalidChannelCount: msg = "Invalid number of channels."; break;
449 case paInvalidSampleRate: msg = "Invalid sample rate."; break;
450 case paInvalidDeviceId: msg = "Invalid device ID."; break;
451 case paInvalidFlag: msg = "Invalid flag."; break;
452 case paSampleFormatNotSupported: msg = "Sample format not supported"; break;
453 case paBadIODeviceCombination: msg = "Illegal combination of I/O devices."; break;
454 case paInsufficientMemory: msg = "Insufficient memory."; break;
455 case paBufferTooBig: msg = "Buffer too big."; break;
456 case paBufferTooSmall: msg = "Buffer too small."; break;
457 case paNullCallback: msg = "No callback routine specified."; break;
458 case paBadStreamPtr: msg = "Invalid stream pointer."; break;
459 case paTimedOut : msg = "Wait Timed Out."; break;
460 case paInternalError: msg = "Internal PortAudio Error."; break;
461 default: msg = "Illegal error number."; break;
462 }
463 return msg;
464 }
465
466 /*
467 Get CPU Load as a fraction of total CPU time.
468 A value of 0.5 would imply that PortAudio and the sound generating
469 callback was consuming roughly 50% of the available CPU time.
470 The amount may vary depending on CPU load.
471 This function may be called from the callback function.
472 */
Pa_GetCPULoad(PortAudioStream * stream)473 double Pa_GetCPULoad( PortAudioStream* stream)
474 {
475 internalPortAudioStream *past;
476 if( stream == NULL ) return (double) paBadStreamPtr;
477 past = (internalPortAudioStream *) stream;
478 return past->past_Usage;
479 }
480
481 /*************************************************************
482 ** Calculate 2 LSB dither signal with a triangular distribution.
483 ** Ranged properly for adding to a 32 bit integer prior to >>15.
484 */
485 #define DITHER_BITS (15)
486 #define DITHER_SCALE (1.0f / ((1<<DITHER_BITS)-1))
Pa_TriangularDither(void)487 static long Pa_TriangularDither( void )
488 {
489 static unsigned long previous = 0;
490 static unsigned long randSeed1 = 22222;
491 static unsigned long randSeed2 = 5555555;
492 long current, highPass;
493 /* Generate two random numbers. */
494 randSeed1 = (randSeed1 * 196314165) + 907633515;
495 randSeed2 = (randSeed2 * 196314165) + 907633515;
496 /* Generate triangular distribution about 0. */
497 current = (((long)randSeed1)>>(32-DITHER_BITS)) + (((long)randSeed2)>>(32-DITHER_BITS));
498 /* High pass filter to reduce audibility. */
499 highPass = current - previous;
500 previous = current;
501 return highPass;
502 }
503
504 /*************************************************************************
505 ** Called by host code.
506 ** Convert input from Int16, call user code, then convert output
507 ** to Int16 format for native use.
508 ** Assumes host native format is paInt16.
509 ** Returns result from user callback.
510 */
Pa_CallConvertInt16(internalPortAudioStream * past,short * nativeInputBuffer,short * nativeOutputBuffer)511 long Pa_CallConvertInt16( internalPortAudioStream *past,
512 short *nativeInputBuffer,
513 short *nativeOutputBuffer )
514 {
515 long temp;
516 long bytesEmpty = 0;
517 long bytesFilled = 0;
518 int userResult;
519 unsigned int i;
520 void *inputBuffer = NULL;
521 void *outputBuffer = NULL;
522
523 #if SUPPORT_AUDIO_CAPTURE
524 /* Get native data from DirectSound. */
525 if( (past->past_NumInputChannels > 0) && (nativeInputBuffer != NULL) )
526 {
527 /* Convert from native format to PA format. */
528 unsigned int samplesPerBuffer = past->past_FramesPerUserBuffer * past->past_NumInputChannels;
529 switch(past->past_InputSampleFormat)
530 {
531
532 case paFloat32:
533 {
534 float *inBufPtr = (float *) past->past_InputBuffer;
535 inputBuffer = past->past_InputBuffer;
536 for( i=0; i<samplesPerBuffer; i++ )
537 {
538 inBufPtr[i] = nativeInputBuffer[i] * (1.0f / 32767.0f);
539 }
540 break;
541 }
542
543 case paInt32:
544 {
545 /* Convert 16 bit data to 32 bit integers */
546 int *inBufPtr = (int *) past->past_InputBuffer;
547 inputBuffer = past->past_InputBuffer;
548 for( i=0; i<samplesPerBuffer; i++ )
549 {
550 inBufPtr[i] = nativeInputBuffer[i] << 16;
551 }
552 break;
553 }
554
555 case paInt16:
556 {
557 /* Already in correct format so don't copy. */
558 inputBuffer = nativeInputBuffer;
559 break;
560 }
561
562 case paInt8:
563 {
564 /* Convert 16 bit data to 8 bit chars */
565 char *inBufPtr = (char *) past->past_InputBuffer;
566 inputBuffer = past->past_InputBuffer;
567 if( past->past_Flags & paDitherOff )
568 {
569 for( i=0; i<samplesPerBuffer; i++ )
570 {
571 inBufPtr[i] = (char)(nativeInputBuffer[i] >> 8);
572 }
573 }
574 else
575 {
576 for( i=0; i<samplesPerBuffer; i++ )
577 {
578 temp = nativeInputBuffer[i];
579 temp += Pa_TriangularDither() >> 8; /* PLB20010820 */
580 temp = ((temp < -0x8000) ? -0x8000 : ((temp > 0x7FFF) ? 0x7FFF : temp));
581 inBufPtr[i] = (char)(temp >> 8);
582 }
583 }
584 break;
585 }
586
587 case paUInt8:
588 {
589 /* Convert 16 bit data to 8 bit unsigned chars */
590 unsigned char *inBufPtr = (unsigned char *) past->past_InputBuffer;
591 inputBuffer = past->past_InputBuffer;
592 if( past->past_Flags & paDitherOff )
593 {
594 for( i=0; i<samplesPerBuffer; i++ )
595 {
596 inBufPtr[i] = ((unsigned char)(nativeInputBuffer[i] >> 8)) + 0x80;
597 }
598 }
599 else
600 {
601 /* If you dither then you have to clip because dithering could push the signal out of range! */
602 for( i=0; i<samplesPerBuffer; i++ )
603 {
604 temp = nativeInputBuffer[i];
605 temp += Pa_TriangularDither() >> 8; /* PLB20010820 */
606 temp = ((temp < -0x8000) ? -0x8000 : ((temp > 0x7FFF) ? 0x7FFF : temp));
607 inBufPtr[i] = (unsigned char)((temp>>8) + 0x80); /* PLB20010820 */
608 }
609 }
610 break;
611 }
612
613 default:
614 break;
615 }
616 }
617 #endif /* SUPPORT_AUDIO_CAPTURE */
618
619 /* Are we doing output time? */
620 if( (past->past_NumOutputChannels > 0) && (nativeOutputBuffer != NULL) )
621 {
622 /* May already be in native format so just write directly to native buffer. */
623 outputBuffer = (past->past_OutputSampleFormat == paInt16) ?
624 nativeOutputBuffer : past->past_OutputBuffer;
625 }
626 /*
627 AddTraceMessage("Pa_CallConvertInt16: inputBuffer = ", (int) inputBuffer );
628 AddTraceMessage("Pa_CallConvertInt16: outputBuffer = ", (int) outputBuffer );
629 */
630 /* Call user callback routine. */
631 userResult = past->past_Callback(
632 inputBuffer,
633 outputBuffer,
634 past->past_FramesPerUserBuffer,
635 past->past_FrameCount,
636 past->past_UserData );
637
638 past->past_FrameCount += (PaTimestamp) past->past_FramesPerUserBuffer;
639
640 /* Convert to native format if necessary. */
641 if( outputBuffer != NULL )
642 {
643 unsigned int samplesPerBuffer = past->past_FramesPerUserBuffer * past->past_NumOutputChannels;
644 switch(past->past_OutputSampleFormat)
645 {
646 case paFloat32:
647 {
648 float *outBufPtr = (float *) past->past_OutputBuffer;
649 if( past->past_Flags & paDitherOff )
650 {
651 if( past->past_Flags & paClipOff ) /* NOTHING */
652 {
653 for( i=0; i<samplesPerBuffer; i++ )
654 {
655 *nativeOutputBuffer++ = (short) (outBufPtr[i] * (32767.0f));
656 }
657 }
658 else /* CLIP */
659 {
660 for( i=0; i<samplesPerBuffer; i++ )
661 {
662 temp = (long)(outBufPtr[i] * 32767.0f);
663 *nativeOutputBuffer++ = (short)((temp < -0x8000) ? -0x8000 : ((temp > 0x7FFF) ? 0x7FFF : temp));
664 }
665 }
666 }
667 else
668 {
669 /* If you dither then you have to clip because dithering could push the signal out of range! */
670 for( i=0; i<samplesPerBuffer; i++ )
671 {
672 float dither = Pa_TriangularDither()*DITHER_SCALE;
673 float dithered = (outBufPtr[i] * (32767.0f)) + dither;
674 temp = (long) (dithered);
675 *nativeOutputBuffer++ = (short)((temp < -0x8000) ? -0x8000 : ((temp > 0x7FFF) ? 0x7FFF : temp));
676 }
677 }
678 break;
679 }
680
681 case paInt32:
682 {
683 int *outBufPtr = (int *) past->past_OutputBuffer;
684 if( past->past_Flags & paDitherOff )
685 {
686 for( i=0; i<samplesPerBuffer; i++ )
687 {
688 *nativeOutputBuffer++ = (short) (outBufPtr[i] >> 16 );
689 }
690 }
691 else
692 {
693 for( i=0; i<samplesPerBuffer; i++ )
694 {
695 /* Shift one bit down before dithering so that we have room for overflow from add. */
696 temp = (outBufPtr[i] >> 1) + Pa_TriangularDither();
697 temp = temp >> 15;
698 *nativeOutputBuffer++ = (short)((temp < -0x8000) ? -0x8000 : ((temp > 0x7FFF) ? 0x7FFF : temp));
699 }
700 }
701 break;
702 }
703
704 case paInt8:
705 {
706 char *outBufPtr = (char *) past->past_OutputBuffer;
707 for( i=0; i<samplesPerBuffer; i++ )
708 {
709 *nativeOutputBuffer++ = ((short)outBufPtr[i]) << 8;
710 }
711 break;
712 }
713
714 case paUInt8:
715 {
716 unsigned char *outBufPtr = (unsigned char *) past->past_OutputBuffer;
717 for( i=0; i<samplesPerBuffer; i++ )
718 {
719 *nativeOutputBuffer++ = ((short)(outBufPtr[i] - 0x80)) << 8;
720 }
721 break;
722 }
723
724 default:
725 break;
726 }
727
728 }
729
730 return userResult;
731 }
732
733 /*************************************************************************
734 ** Called by host code.
735 ** Convert input from Float32, call user code, then convert output
736 ** to Float32 format for native use.
737 ** Assumes host native format is Float32.
738 ** Returns result from user callback.
739 ** FIXME - Unimplemented for formats other than paFloat32!!!!
740 */
Pa_CallConvertFloat32(internalPortAudioStream * past,float * nativeInputBuffer,float * nativeOutputBuffer)741 long Pa_CallConvertFloat32( internalPortAudioStream *past,
742 float *nativeInputBuffer,
743 float *nativeOutputBuffer )
744 {
745 long bytesEmpty = 0;
746 long bytesFilled = 0;
747 int userResult;
748 void *inputBuffer = NULL;
749 void *outputBuffer = NULL;
750
751 /* Get native data from DirectSound. */
752 if( (past->past_NumInputChannels > 0) && (nativeInputBuffer != NULL) )
753 {
754 inputBuffer = nativeInputBuffer; /* FIXME */
755 }
756
757 /* Are we doing output time? */
758 if( (past->past_NumOutputChannels > 0) && (nativeOutputBuffer != NULL) )
759 {
760 /* May already be in native format so just write directly to native buffer. */
761 outputBuffer = (past->past_OutputSampleFormat == paFloat32) ?
762 nativeOutputBuffer : past->past_OutputBuffer;
763 }
764 /*
765 AddTraceMessage("Pa_CallConvertInt16: inputBuffer = ", (int) inputBuffer );
766 AddTraceMessage("Pa_CallConvertInt16: outputBuffer = ", (int) outputBuffer );
767 */
768 /* Call user callback routine. */
769 userResult = past->past_Callback(
770 inputBuffer,
771 outputBuffer,
772 past->past_FramesPerUserBuffer,
773 past->past_FrameCount,
774 past->past_UserData );
775
776 past->past_FrameCount += (PaTimestamp) past->past_FramesPerUserBuffer;
777
778 /* Convert to native format if necessary. */ /* FIXME */
779 return userResult;
780 }
781
782 /*************************************************************************/
Pa_Initialize(void)783 PaError Pa_Initialize( void )
784 {
785 if( gInitCount++ > 0 ) return paNoError;
786 ResetTraceMessages();
787 return PaHost_Init();
788 }
789
Pa_Terminate(void)790 PaError Pa_Terminate( void )
791 {
792 PaError result = paNoError;
793
794 if( gInitCount == 0 ) return paNoError;
795 else if( --gInitCount == 0 )
796 {
797 result = PaHost_Term();
798 DumpTraceMessages();
799 }
800 return result;
801 }
802
803 /*************************************************************************/
Pa_GetSampleSize(PaSampleFormat format)804 PaError Pa_GetSampleSize( PaSampleFormat format )
805 {
806 int size;
807 switch(format )
808 {
809
810 case paUInt8:
811 case paInt8:
812 size = 1;
813 break;
814
815 case paInt16:
816 size = 2;
817 break;
818
819 case paPackedInt24:
820 size = 3;
821 break;
822
823 case paFloat32:
824 case paInt32:
825 case paInt24:
826 size = 4;
827 break;
828
829 default:
830 size = paSampleFormatNotSupported;
831 break;
832 }
833 return (PaError) size;
834 }
835
836
837