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