mirror of
https://github.com/saitohirga/WSJT-X.git
synced 2024-11-19 10:32:02 -05:00
8d353a5b3b
git-svn-id: svn+ssh://svn.code.sf.net/p/wsjt/wsjt/trunk@189 ab8295b8-cf94-4d9e-aec4-7959e3be5d79
828 lines
28 KiB
C
828 lines
28 KiB
C
/*
|
|
* Portable Audio I/O Library
|
|
* Host Independant Layer
|
|
*
|
|
* Based on the Open Source API proposed by Ross Bencina
|
|
* Copyright (c) 1999-2000 Phil Burk
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining
|
|
* a copy of this software and associated documentation files
|
|
* (the "Software"), to deal in the Software without restriction,
|
|
* including without limitation the rights to use, copy, modify, merge,
|
|
* publish, distribute, sublicense, and/or sell copies of the Software,
|
|
* and to permit persons to whom the Software is furnished to do so,
|
|
* subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be
|
|
* included in all copies or substantial portions of the Software.
|
|
*
|
|
* Any person wishing to distribute modifications to the Software is
|
|
* requested to send the modifications to the original developer so that
|
|
* they can be incorporated into the canonical version.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
|
|
* ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
|
|
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
*
|
|
*/
|
|
|
|
/* Modification History:
|
|
PLB20010422 - apply Mike Berry's changes for CodeWarrior on PC
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <math.h>
|
|
|
|
/* PLB20010422 - "memory.h" doesn't work on CodeWarrior for PC. Thanks Mike Berry for the mod. */
|
|
#ifdef _WIN32
|
|
#ifndef __MWERKS__
|
|
#include <memory.h>
|
|
#endif /* __MWERKS__ */
|
|
#else /* !_WIN32 */
|
|
#include <memory.h>
|
|
#endif /* _WIN32 */
|
|
|
|
#include "portaudio.h"
|
|
#include "pa_host.h"
|
|
#include "pa_trace.h"
|
|
|
|
/* The reason we might NOT want to validate the rate before opening the stream
|
|
* is because many DirectSound drivers lie about the rates they actually support.
|
|
*/
|
|
#define PA_VALIDATE_RATE (0) /* If true validate sample rate against driver info. */
|
|
|
|
/*
|
|
O- maybe not allocate past_InputBuffer and past_OutputBuffer if not needed for conversion
|
|
*/
|
|
|
|
#ifndef FALSE
|
|
#define FALSE (0)
|
|
#define TRUE (!FALSE)
|
|
#endif
|
|
|
|
#define PRINT(x) { printf x; fflush(stdout); }
|
|
#define ERR_RPT(x) PRINT(x)
|
|
#define DBUG(x) /* PRINT(x) */
|
|
#define DBUGX(x) /* PRINT(x) */
|
|
|
|
static int gInitCount = 0; /* Count number of times Pa_Initialize() called to allow nesting and overlapping. */
|
|
|
|
static PaError Pa_KillStream( PortAudioStream *stream, int abort );
|
|
|
|
/***********************************************************************/
|
|
int PaHost_FindClosestTableEntry( double allowableError, const double *rateTable, int numRates, double frameRate )
|
|
{
|
|
double err, minErr = allowableError;
|
|
int i, bestFit = -1;
|
|
|
|
for( i=0; i<numRates; i++ )
|
|
{
|
|
err = fabs( frameRate - rateTable[i] );
|
|
if( err < minErr )
|
|
{
|
|
minErr = err;
|
|
bestFit = i;
|
|
}
|
|
}
|
|
return bestFit;
|
|
}
|
|
|
|
/**************************************************************************
|
|
** Make sure sample rate is legal and also convert to enumeration for driver.
|
|
*/
|
|
PaError PaHost_ValidateSampleRate( PaDeviceID id, double requestedFrameRate,
|
|
double *closestFrameRatePtr )
|
|
{
|
|
long bestRateIndex;
|
|
const PaDeviceInfo *pdi;
|
|
pdi = Pa_GetDeviceInfo( id );
|
|
if( pdi == NULL ) return paInvalidDeviceId;
|
|
|
|
if( pdi->numSampleRates == -1 )
|
|
{
|
|
/* Is it out of range? */
|
|
if( (requestedFrameRate < pdi->sampleRates[0]) ||
|
|
(requestedFrameRate > pdi->sampleRates[1]) )
|
|
{
|
|
return paInvalidSampleRate;
|
|
}
|
|
|
|
*closestFrameRatePtr = requestedFrameRate;
|
|
}
|
|
else
|
|
{
|
|
bestRateIndex = PaHost_FindClosestTableEntry( 1.0, pdi->sampleRates, pdi->numSampleRates, requestedFrameRate );
|
|
if( bestRateIndex < 0 ) return paInvalidSampleRate;
|
|
*closestFrameRatePtr = pdi->sampleRates[bestRateIndex];
|
|
}
|
|
return paNoError;
|
|
}
|
|
|
|
/*************************************************************************/
|
|
DLL_API PaError 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 )
|
|
{
|
|
internalPortAudioStream *past = NULL;
|
|
PaError result = paNoError;
|
|
int bitsPerInputSample;
|
|
int bitsPerOutputSample;
|
|
/* Print passed parameters. */
|
|
DBUG(("Pa_OpenStream( %p, %d, %d, %d, %p, /* input */ \n",
|
|
streamPtrPtr, inputDeviceID, numInputChannels,
|
|
inputSampleFormat, inputDriverInfo ));
|
|
DBUG((" %d, %d, %d, %p, /* output */\n",
|
|
outputDeviceID, numOutputChannels,
|
|
outputSampleFormat, outputDriverInfo ));
|
|
DBUG((" %g, %d, %d, 0x%x, , %p )\n",
|
|
sampleRate, framesPerBuffer, numberOfBuffers,
|
|
streamFlags, userData ));
|
|
|
|
/* Check for parameter errors. */
|
|
if( (streamFlags & ~(paClipOff | paDitherOff)) != 0 ) return paInvalidFlag;
|
|
if( streamPtrPtr == NULL ) return paBadStreamPtr;
|
|
if( inputDriverInfo != NULL ) return paHostError; /* REVIEW */
|
|
if( outputDriverInfo != NULL ) return paHostError; /* REVIEW */
|
|
if( (inputDeviceID < 0) && ( outputDeviceID < 0) ) return paInvalidDeviceId;
|
|
if( (outputDeviceID >= Pa_CountDevices()) || (inputDeviceID >= Pa_CountDevices()) ) return paInvalidDeviceId;
|
|
if( (numInputChannels <= 0) && ( numOutputChannels <= 0) ) return paInvalidChannelCount;
|
|
|
|
#if SUPPORT_AUDIO_CAPTURE
|
|
if( inputDeviceID >= 0 )
|
|
{
|
|
PaError size = Pa_GetSampleSize( inputSampleFormat );
|
|
if( size < 0 ) return size;
|
|
bitsPerInputSample = 8 * size;
|
|
if( (numInputChannels <= 0) ) return paInvalidChannelCount;
|
|
}
|
|
#else
|
|
if( inputDeviceID >= 0 )
|
|
{
|
|
return paInvalidChannelCount;
|
|
}
|
|
#endif /* SUPPORT_AUDIO_CAPTURE */
|
|
else
|
|
{
|
|
if( numInputChannels > 0 ) return paInvalidChannelCount;
|
|
bitsPerInputSample = 0;
|
|
}
|
|
|
|
if( outputDeviceID >= 0 )
|
|
{
|
|
PaError size = Pa_GetSampleSize( outputSampleFormat );
|
|
if( size < 0 ) return size;
|
|
bitsPerOutputSample = 8 * size;
|
|
if( (numOutputChannels <= 0) ) return paInvalidChannelCount;
|
|
}
|
|
else
|
|
{
|
|
if( numOutputChannels > 0 ) return paInvalidChannelCount;
|
|
bitsPerOutputSample = 0;
|
|
}
|
|
|
|
if( callback == NULL ) return paNullCallback;
|
|
|
|
/* Allocate and clear stream structure. */
|
|
past = (internalPortAudioStream *) PaHost_AllocateFastMemory( sizeof(internalPortAudioStream) );
|
|
if( past == NULL ) return paInsufficientMemory;
|
|
memset( past, 0, sizeof(internalPortAudioStream) );
|
|
AddTraceMessage("Pa_OpenStream: past", (long) past );
|
|
|
|
past->past_Magic = PA_MAGIC; /* Set ID to catch bugs. */
|
|
past->past_FramesPerUserBuffer = framesPerBuffer;
|
|
past->past_NumUserBuffers = numberOfBuffers; /* NOTE - PaHost_OpenStream() NMUST CHECK FOR ZERO! */
|
|
past->past_Callback = callback;
|
|
past->past_UserData = userData;
|
|
past->past_OutputSampleFormat = outputSampleFormat;
|
|
past->past_InputSampleFormat = inputSampleFormat;
|
|
past->past_OutputDeviceID = outputDeviceID;
|
|
past->past_InputDeviceID = inputDeviceID;
|
|
past->past_NumInputChannels = numInputChannels;
|
|
past->past_NumOutputChannels = numOutputChannels;
|
|
past->past_Flags = streamFlags;
|
|
|
|
/* Check for absurd sample rates. */
|
|
if( (sampleRate < 1000.0) || (sampleRate > 200000.0) )
|
|
{
|
|
result = paInvalidSampleRate;
|
|
goto cleanup;
|
|
}
|
|
|
|
/* Allocate buffers that may be used for format conversion from user to native buffers. */
|
|
if( numInputChannels > 0 )
|
|
{
|
|
|
|
#if PA_VALIDATE_RATE
|
|
result = PaHost_ValidateSampleRate( inputDeviceID, sampleRate, &past->past_SampleRate );
|
|
if( result < 0 )
|
|
{
|
|
goto cleanup;
|
|
}
|
|
#else
|
|
past->past_SampleRate = sampleRate;
|
|
#endif
|
|
/* Allocate single Input buffer. */
|
|
past->past_InputBufferSize = framesPerBuffer * numInputChannels * ((bitsPerInputSample+7) / 8);
|
|
past->past_InputBuffer = PaHost_AllocateFastMemory(past->past_InputBufferSize);
|
|
if( past->past_InputBuffer == NULL )
|
|
{
|
|
result = paInsufficientMemory;
|
|
goto cleanup;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
past->past_InputBuffer = NULL;
|
|
}
|
|
|
|
/* Allocate single Output buffer. */
|
|
if( numOutputChannels > 0 )
|
|
{
|
|
#if PA_VALIDATE_RATE
|
|
result = PaHost_ValidateSampleRate( outputDeviceID, sampleRate, &past->past_SampleRate );
|
|
if( result < 0 )
|
|
{
|
|
goto cleanup;
|
|
}
|
|
#else
|
|
past->past_SampleRate = sampleRate;
|
|
#endif
|
|
past->past_OutputBufferSize = framesPerBuffer * numOutputChannels * ((bitsPerOutputSample+7) / 8);
|
|
past->past_OutputBuffer = PaHost_AllocateFastMemory(past->past_OutputBufferSize);
|
|
if( past->past_OutputBuffer == NULL )
|
|
{
|
|
result = paInsufficientMemory;
|
|
goto cleanup;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
past->past_OutputBuffer = NULL;
|
|
}
|
|
|
|
result = PaHost_OpenStream( past );
|
|
if( result < 0 ) goto cleanup;
|
|
|
|
*streamPtrPtr = (void *) past;
|
|
|
|
return result;
|
|
|
|
cleanup:
|
|
if( past != NULL ) Pa_CloseStream( past );
|
|
*streamPtrPtr = NULL;
|
|
return result;
|
|
}
|
|
|
|
|
|
/*************************************************************************/
|
|
DLL_API PaError Pa_OpenDefaultStream( PortAudioStream** stream,
|
|
int numInputChannels,
|
|
int numOutputChannels,
|
|
PaSampleFormat sampleFormat,
|
|
double sampleRate,
|
|
unsigned long framesPerBuffer,
|
|
unsigned long numberOfBuffers,
|
|
PortAudioCallback *callback,
|
|
void *userData )
|
|
{
|
|
return Pa_OpenStream(
|
|
stream,
|
|
((numInputChannels > 0) ? Pa_GetDefaultInputDeviceID() : paNoDevice),
|
|
numInputChannels, sampleFormat, NULL,
|
|
((numOutputChannels > 0) ? Pa_GetDefaultOutputDeviceID() : paNoDevice),
|
|
numOutputChannels, sampleFormat, NULL,
|
|
sampleRate, framesPerBuffer, numberOfBuffers, paNoFlag, callback, userData );
|
|
}
|
|
|
|
/*************************************************************************/
|
|
DLL_API PaError Pa_CloseStream( PortAudioStream* stream)
|
|
{
|
|
PaError result;
|
|
internalPortAudioStream *past;
|
|
|
|
DBUG(("Pa_CloseStream()\n"));
|
|
if( stream == NULL ) return paBadStreamPtr;
|
|
past = (internalPortAudioStream *) stream;
|
|
|
|
Pa_AbortStream( past );
|
|
result = PaHost_CloseStream( past );
|
|
|
|
if( past->past_InputBuffer ) PaHost_FreeFastMemory( past->past_InputBuffer, past->past_InputBufferSize );
|
|
if( past->past_OutputBuffer ) PaHost_FreeFastMemory( past->past_OutputBuffer, past->past_OutputBufferSize );
|
|
PaHost_FreeFastMemory( past, sizeof(internalPortAudioStream) );
|
|
|
|
return result;
|
|
}
|
|
|
|
/*************************************************************************/
|
|
DLL_API PaError Pa_StartStream( PortAudioStream *stream )
|
|
{
|
|
PaError result = paHostError;
|
|
internalPortAudioStream *past;
|
|
|
|
if( stream == NULL ) return paBadStreamPtr;
|
|
past = (internalPortAudioStream *) stream;
|
|
|
|
past->past_FrameCount = 0.0;
|
|
|
|
if( past->past_NumInputChannels > 0 )
|
|
{
|
|
result = PaHost_StartInput( past );
|
|
DBUG(("Pa_StartStream: PaHost_StartInput returned = 0x%X.\n", result));
|
|
if( result < 0 ) goto error;
|
|
}
|
|
|
|
if( past->past_NumOutputChannels > 0 )
|
|
{
|
|
result = PaHost_StartOutput( past );
|
|
DBUG(("Pa_StartStream: PaHost_StartOutput returned = 0x%X.\n", result));
|
|
if( result < 0 ) goto error;
|
|
}
|
|
|
|
result = PaHost_StartEngine( past );
|
|
DBUG(("Pa_StartStream: PaHost_StartEngine returned = 0x%X.\n", result));
|
|
if( result < 0 ) goto error;
|
|
|
|
return paNoError;
|
|
|
|
error:
|
|
return result;
|
|
}
|
|
|
|
/*************************************************************************/
|
|
DLL_API PaError Pa_StopStream( PortAudioStream *stream )
|
|
{
|
|
return Pa_KillStream( stream, 0 );
|
|
}
|
|
|
|
/*************************************************************************/
|
|
DLL_API PaError Pa_AbortStream( PortAudioStream *stream )
|
|
{
|
|
return Pa_KillStream( stream, 1 );
|
|
}
|
|
|
|
/*************************************************************************/
|
|
static PaError Pa_KillStream( PortAudioStream *stream, int abort )
|
|
{
|
|
PaError result = paNoError;
|
|
internalPortAudioStream *past;
|
|
|
|
DBUG(("Pa_StopStream().\n"));
|
|
if( stream == NULL ) return paBadStreamPtr;
|
|
past = (internalPortAudioStream *) stream;
|
|
|
|
if( (past->past_NumInputChannels > 0) || (past->past_NumOutputChannels > 0) )
|
|
{
|
|
result = PaHost_StopEngine( past, abort );
|
|
DBUG(("Pa_StopStream: PaHost_StopEngine returned = 0x%X.\n", result));
|
|
if( result < 0 ) goto error;
|
|
}
|
|
|
|
if( past->past_NumInputChannels > 0 )
|
|
{
|
|
result = PaHost_StopInput( past, abort );
|
|
DBUG(("Pa_StopStream: PaHost_StopInput returned = 0x%X.\n", result));
|
|
if( result != paNoError ) goto error;
|
|
}
|
|
|
|
if( past->past_NumOutputChannels > 0 )
|
|
{
|
|
result = PaHost_StopOutput( past, abort );
|
|
DBUG(("Pa_StopStream: PaHost_StopOutput returned = 0x%X.\n", result));
|
|
if( result != paNoError ) goto error;
|
|
}
|
|
|
|
error:
|
|
past->past_Usage = 0;
|
|
past->past_IfLastExitValid = 0;
|
|
|
|
return result;
|
|
}
|
|
|
|
/*************************************************************************/
|
|
DLL_API PaError Pa_StreamActive( PortAudioStream *stream )
|
|
{
|
|
internalPortAudioStream *past;
|
|
if( stream == NULL ) return paBadStreamPtr;
|
|
past = (internalPortAudioStream *) stream;
|
|
return PaHost_StreamActive( past );
|
|
}
|
|
|
|
/*************************************************************************/
|
|
DLL_API const char *Pa_GetErrorText( PaError errnum )
|
|
{
|
|
const char *msg;
|
|
|
|
switch(errnum)
|
|
{
|
|
case paNoError: msg = "Success"; break;
|
|
case paHostError: msg = "Host error."; break;
|
|
case paInvalidChannelCount: msg = "Invalid number of channels."; break;
|
|
case paInvalidSampleRate: msg = "Invalid sample rate."; break;
|
|
case paInvalidDeviceId: msg = "Invalid device ID."; break;
|
|
case paInvalidFlag: msg = "Invalid flag."; break;
|
|
case paSampleFormatNotSupported: msg = "Sample format not supported"; break;
|
|
case paBadIODeviceCombination: msg = "Illegal combination of I/O devices."; break;
|
|
case paInsufficientMemory: msg = "Insufficient memory."; break;
|
|
case paBufferTooBig: msg = "Buffer too big."; break;
|
|
case paBufferTooSmall: msg = "Buffer too small."; break;
|
|
case paNullCallback: msg = "No callback routine specified."; break;
|
|
case paBadStreamPtr: msg = "Invalid stream pointer."; break;
|
|
case paTimedOut : msg = "Wait Timed Out."; break;
|
|
case paInternalError: msg = "Internal PortAudio Error."; break;
|
|
default: msg = "Illegal error number."; break;
|
|
}
|
|
return msg;
|
|
}
|
|
|
|
/*
|
|
Get CPU Load as a fraction of total CPU time.
|
|
A value of 0.5 would imply that PortAudio and the sound generating
|
|
callback was consuming roughly 50% of the available CPU time.
|
|
The amount may vary depending on CPU load.
|
|
This function may be called from the callback function.
|
|
*/
|
|
DLL_API double Pa_GetCPULoad( PortAudioStream* stream)
|
|
{
|
|
internalPortAudioStream *past;
|
|
if( stream == NULL ) return (double) paBadStreamPtr;
|
|
past = (internalPortAudioStream *) stream;
|
|
return past->past_Usage;
|
|
}
|
|
|
|
/*************************************************************
|
|
** Calculate 2 LSB dither signal with a triangular distribution.
|
|
** Ranged properly for adding to a 32 bit integer prior to >>15.
|
|
*/
|
|
#define DITHER_BITS (15)
|
|
#define DITHER_SCALE (1.0f / ((1<<DITHER_BITS)-1))
|
|
static long Pa_TriangularDither( void )
|
|
{
|
|
static unsigned long previous = 0;
|
|
static unsigned long randSeed1 = 22222;
|
|
static unsigned long randSeed2 = 5555555;
|
|
long current, highPass;
|
|
/* Generate two random numbers. */
|
|
randSeed1 = (randSeed1 * 196314165) + 907633515;
|
|
randSeed2 = (randSeed2 * 196314165) + 907633515;
|
|
/* Generate triangular distribution about 0. */
|
|
current = (((long)randSeed1)>>(32-DITHER_BITS)) + (((long)randSeed2)>>(32-DITHER_BITS));
|
|
/* High pass filter to reduce audibility. */
|
|
highPass = current - previous;
|
|
previous = current;
|
|
return highPass;
|
|
}
|
|
|
|
/*************************************************************************
|
|
** Called by host code.
|
|
** Convert input from Int16, call user code, then convert output
|
|
** to Int16 format for native use.
|
|
** Assumes host native format is paInt16.
|
|
** Returns result from user callback.
|
|
*/
|
|
long Pa_CallConvertInt16( internalPortAudioStream *past,
|
|
short *nativeInputBuffer,
|
|
short *nativeOutputBuffer )
|
|
{
|
|
long temp;
|
|
long bytesEmpty = 0;
|
|
long bytesFilled = 0;
|
|
int userResult;
|
|
unsigned int i;
|
|
void *inputBuffer = NULL;
|
|
void *outputBuffer = NULL;
|
|
|
|
#if SUPPORT_AUDIO_CAPTURE
|
|
/* Get native data from DirectSound. */
|
|
if( (past->past_NumInputChannels > 0) && (nativeInputBuffer != NULL) )
|
|
{
|
|
/* Convert from native format to PA format. */
|
|
unsigned int samplesPerBuffer = past->past_FramesPerUserBuffer * past->past_NumInputChannels;
|
|
switch(past->past_InputSampleFormat)
|
|
{
|
|
|
|
case paFloat32:
|
|
{
|
|
float *inBufPtr = (float *) past->past_InputBuffer;
|
|
inputBuffer = past->past_InputBuffer;
|
|
for( i=0; i<samplesPerBuffer; i++ )
|
|
{
|
|
inBufPtr[i] = nativeInputBuffer[i] * (1.0f / 32767.0f);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case paInt32:
|
|
{
|
|
/* Convert 16 bit data to 32 bit integers */
|
|
int *inBufPtr = (int *) past->past_InputBuffer;
|
|
inputBuffer = past->past_InputBuffer;
|
|
for( i=0; i<samplesPerBuffer; i++ )
|
|
{
|
|
inBufPtr[i] = nativeInputBuffer[i] << 16;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case paInt16:
|
|
{
|
|
/* Already in correct format so don't copy. */
|
|
inputBuffer = nativeInputBuffer;
|
|
break;
|
|
}
|
|
|
|
case paInt8:
|
|
{
|
|
/* Convert 16 bit data to 8 bit chars */
|
|
char *inBufPtr = (char *) past->past_InputBuffer;
|
|
inputBuffer = past->past_InputBuffer;
|
|
if( past->past_Flags & paDitherOff )
|
|
{
|
|
for( i=0; i<samplesPerBuffer; i++ )
|
|
{
|
|
inBufPtr[i] = (char)(nativeInputBuffer[i] >> 8);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for( i=0; i<samplesPerBuffer; i++ )
|
|
{
|
|
temp = nativeInputBuffer[i];
|
|
temp += Pa_TriangularDither() >> 7;
|
|
temp = ((temp < -0x8000) ? -0x8000 : ((temp > 0x7FFF) ? 0x7FFF : temp));
|
|
inBufPtr[i] = (char)(temp >> 8);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
case paUInt8:
|
|
{
|
|
/* Convert 16 bit data to 8 bit unsigned chars */
|
|
unsigned char *inBufPtr = (unsigned char *) past->past_InputBuffer;
|
|
inputBuffer = past->past_InputBuffer;
|
|
if( past->past_Flags & paDitherOff )
|
|
{
|
|
for( i=0; i<samplesPerBuffer; i++ )
|
|
{
|
|
inBufPtr[i] = ((unsigned char)(nativeInputBuffer[i] >> 8)) + 0x80;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* If you dither then you have to clip because dithering could push the signal out of range! */
|
|
for( i=0; i<samplesPerBuffer; i++ )
|
|
{
|
|
temp = nativeInputBuffer[i];
|
|
temp += Pa_TriangularDither() >> 7;
|
|
temp = ((temp < -0x8000) ? -0x8000 : ((temp > 0x7FFF) ? 0x7FFF : temp));
|
|
inBufPtr[i] = (unsigned char)(temp + 0x80);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
#endif /* SUPPORT_AUDIO_CAPTURE */
|
|
|
|
/* Are we doing output time? */
|
|
if( (past->past_NumOutputChannels > 0) && (nativeOutputBuffer != NULL) )
|
|
{
|
|
/* May already be in native format so just write directly to native buffer. */
|
|
outputBuffer = (past->past_OutputSampleFormat == paInt16) ?
|
|
nativeOutputBuffer : past->past_OutputBuffer;
|
|
}
|
|
/*
|
|
AddTraceMessage("Pa_CallConvertInt16: inputBuffer = ", (int) inputBuffer );
|
|
AddTraceMessage("Pa_CallConvertInt16: outputBuffer = ", (int) outputBuffer );
|
|
*/
|
|
/* Call user callback routine. */
|
|
userResult = past->past_Callback(
|
|
inputBuffer,
|
|
outputBuffer,
|
|
past->past_FramesPerUserBuffer,
|
|
past->past_FrameCount,
|
|
past->past_UserData );
|
|
|
|
past->past_FrameCount += (PaTimestamp) past->past_FramesPerUserBuffer;
|
|
|
|
/* Convert to native format if necessary. */
|
|
if( outputBuffer != NULL )
|
|
{
|
|
unsigned int samplesPerBuffer = past->past_FramesPerUserBuffer * past->past_NumOutputChannels;
|
|
switch(past->past_OutputSampleFormat)
|
|
{
|
|
case paFloat32:
|
|
{
|
|
float *outBufPtr = (float *) past->past_OutputBuffer;
|
|
if( past->past_Flags & paDitherOff )
|
|
{
|
|
if( past->past_Flags & paClipOff ) /* NOTHING */
|
|
{
|
|
for( i=0; i<samplesPerBuffer; i++ )
|
|
{
|
|
*nativeOutputBuffer++ = (short) (outBufPtr[i] * (32767.0f));
|
|
}
|
|
}
|
|
else /* CLIP */
|
|
{
|
|
for( i=0; i<samplesPerBuffer; i++ )
|
|
{
|
|
temp = (long)(outBufPtr[i] * 32767.0f);
|
|
*nativeOutputBuffer++ = (short)((temp < -0x8000) ? -0x8000 : ((temp > 0x7FFF) ? 0x7FFF : temp));
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* If you dither then you have to clip because dithering could push the signal out of range! */
|
|
for( i=0; i<samplesPerBuffer; i++ )
|
|
{
|
|
float dither = Pa_TriangularDither()*DITHER_SCALE;
|
|
float dithered = (outBufPtr[i] * (32767.0f)) + dither;
|
|
temp = (long) (dithered);
|
|
*nativeOutputBuffer++ = (short)((temp < -0x8000) ? -0x8000 : ((temp > 0x7FFF) ? 0x7FFF : temp));
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
case paInt32:
|
|
{
|
|
int *outBufPtr = (int *) past->past_OutputBuffer;
|
|
if( past->past_Flags & paDitherOff )
|
|
{
|
|
for( i=0; i<samplesPerBuffer; i++ )
|
|
{
|
|
*nativeOutputBuffer++ = (short) (outBufPtr[i] >> 16 );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for( i=0; i<samplesPerBuffer; i++ )
|
|
{
|
|
/* Shift one bit down before dithering so that we have room for overflow from add. */
|
|
temp = (outBufPtr[i] >> 1) + Pa_TriangularDither();
|
|
temp = temp >> 15;
|
|
*nativeOutputBuffer++ = (short)((temp < -0x8000) ? -0x8000 : ((temp > 0x7FFF) ? 0x7FFF : temp));
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
case paInt8:
|
|
{
|
|
char *outBufPtr = (char *) past->past_OutputBuffer;
|
|
for( i=0; i<samplesPerBuffer; i++ )
|
|
{
|
|
*nativeOutputBuffer++ = ((short)outBufPtr[i]) << 8;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case paUInt8:
|
|
{
|
|
unsigned char *outBufPtr = (unsigned char *) past->past_OutputBuffer;
|
|
for( i=0; i<samplesPerBuffer; i++ )
|
|
{
|
|
*nativeOutputBuffer++ = ((short)(outBufPtr[i] - 0x80)) << 8;
|
|
}
|
|
break;
|
|
}
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
return userResult;
|
|
}
|
|
|
|
/*************************************************************************
|
|
** Called by host code.
|
|
** Convert input from Float32, call user code, then convert output
|
|
** to Float32 format for native use.
|
|
** Assumes host native format is Float32.
|
|
** Returns result from user callback.
|
|
** FIXME - Unimplemented for formats other than paFloat32!!!!
|
|
*/
|
|
long Pa_CallConvertFloat32( internalPortAudioStream *past,
|
|
float *nativeInputBuffer,
|
|
float *nativeOutputBuffer )
|
|
{
|
|
long bytesEmpty = 0;
|
|
long bytesFilled = 0;
|
|
int userResult;
|
|
void *inputBuffer = NULL;
|
|
void *outputBuffer = NULL;
|
|
|
|
/* Get native data from DirectSound. */
|
|
if( (past->past_NumInputChannels > 0) && (nativeInputBuffer != NULL) )
|
|
{
|
|
inputBuffer = nativeInputBuffer; // FIXME
|
|
}
|
|
|
|
/* Are we doing output time? */
|
|
if( (past->past_NumOutputChannels > 0) && (nativeOutputBuffer != NULL) )
|
|
{
|
|
/* May already be in native format so just write directly to native buffer. */
|
|
outputBuffer = (past->past_OutputSampleFormat == paFloat32) ?
|
|
nativeOutputBuffer : past->past_OutputBuffer;
|
|
}
|
|
/*
|
|
AddTraceMessage("Pa_CallConvertInt16: inputBuffer = ", (int) inputBuffer );
|
|
AddTraceMessage("Pa_CallConvertInt16: outputBuffer = ", (int) outputBuffer );
|
|
*/
|
|
/* Call user callback routine. */
|
|
userResult = past->past_Callback(
|
|
inputBuffer,
|
|
outputBuffer,
|
|
past->past_FramesPerUserBuffer,
|
|
past->past_FrameCount,
|
|
past->past_UserData );
|
|
|
|
past->past_FrameCount += (PaTimestamp) past->past_FramesPerUserBuffer;
|
|
|
|
/* Convert to native format if necessary. */ // FIXME
|
|
return userResult;
|
|
}
|
|
|
|
/*************************************************************************/
|
|
DLL_API PaError Pa_Initialize( void )
|
|
{
|
|
if( gInitCount++ > 0 ) return paNoError;
|
|
ResetTraceMessages();
|
|
return PaHost_Init();
|
|
}
|
|
|
|
DLL_API PaError Pa_Terminate( void )
|
|
{
|
|
PaError result = paNoError;
|
|
|
|
if( gInitCount == 0 ) return paNoError;
|
|
else if( --gInitCount == 0 )
|
|
{
|
|
result = PaHost_Term();
|
|
DumpTraceMessages();
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/*************************************************************************/
|
|
DLL_API PaError Pa_GetSampleSize( PaSampleFormat format )
|
|
{
|
|
int size;
|
|
switch(format )
|
|
{
|
|
|
|
case paUInt8:
|
|
case paInt8:
|
|
size = 1;
|
|
break;
|
|
|
|
case paInt16:
|
|
size = 2;
|
|
break;
|
|
|
|
case paPackedInt24:
|
|
size = 3;
|
|
break;
|
|
|
|
case paFloat32:
|
|
case paInt32:
|
|
case paInt24:
|
|
size = 4;
|
|
break;
|
|
|
|
default:
|
|
size = paSampleFormatNotSupported;
|
|
break;
|
|
}
|
|
return (PaError) size;
|
|
}
|
|
|
|
|