Added paInt16, paInt8, format using new "pa_common/pa_convert.c" file.
Return error if opened in mono mode cuz not supported.
Add support for Pa_GetCPULoad();
Fixed timestamp in callback and Pa_StreamTime() (Thanks n++k for the advice!)
Check for invalid sample rates and return an error.
Check for getenv("PA_MIN_LATEWNCY_MSEC") to set latency externally.
Better error checking for invalid channel counts and invalid devices.
This commit is contained in:
parent
cf24fdd1c0
commit
b47db27181
2 changed files with 218 additions and 127 deletions
|
|
@ -2,14 +2,16 @@ Notes on Core Audio Implementation of PortAudio
|
|||
|
||||
by Phil Burk and Darren Gibbs
|
||||
|
||||
Document last updated Feb 7, 2002
|
||||
Document last updated March 20, 2002
|
||||
|
||||
WHAT WORKS
|
||||
|
||||
Output with very low latency, <10 msec.
|
||||
Half duplex input or output.
|
||||
Full duplex on the same CoreAudio device.
|
||||
The paFLoat32 sample format.
|
||||
The paFLoat32, paInt16, paInt8, paUInt8 sample formats.
|
||||
Pa_GetCPULoad()
|
||||
Pa_StreamTime()
|
||||
|
||||
KNOWN BUGS OR LIMITATIONS
|
||||
|
||||
|
|
@ -18,10 +20,7 @@ devices. Note that some CoreAudio devices like the Roland UH30 look
|
|||
like one device but are actually two different CoreAudio devices. The
|
||||
BuiltIn audio is typically one CoreAudio device.
|
||||
|
||||
User formats other than the native paFloat32 are not yet supported.
|
||||
|
||||
If you request a non-supported sample rate then you just get a
|
||||
supported rate with no error messages.
|
||||
Mono doesn't work.
|
||||
|
||||
DEVICE MAPPING
|
||||
|
||||
|
|
|
|||
|
|
@ -39,29 +39,32 @@
|
|||
12.7.2001 - Gord Peters - Tweaks to compile on PA V17 and OS X 10.1
|
||||
2.7.2002 - Darren and Phil - fixed isInput so GetProperty works better,
|
||||
fixed device queries for numChannels and sampleRates,
|
||||
one CoreAudio device now maps to separate input and output PaDevices,
|
||||
audio input works if using same CoreAudio device (some HW devices make separate CoreAudio devices).
|
||||
one CoreAudio device now maps to separate input and output PaDevices,
|
||||
audio input works if using same CoreAudio device (some HW devices make separate CoreAudio devices).
|
||||
2.22.2002 - Stephane Letz - Explicit cast needed for compilation with Code Warrior 7
|
||||
|
||||
3.19.2002 - Phil Burk - Added paInt16, paInt8, format using new "pa_common/pa_convert.c" file.
|
||||
Return error if opened in mono mode cuz not supported.
|
||||
Add support for Pa_GetCPULoad();
|
||||
Fixed timestamp in callback and Pa_StreamTime() (Thanks n++k for the advice!)
|
||||
Check for invalid sample rates and return an error.
|
||||
Check for getenv("PA_MIN_LATEWNCY_MSEC") to set latency externally.
|
||||
Better error checking for invalid channel counts and invalid devices.
|
||||
|
||||
TODO:
|
||||
O- conversion for other user formats besides paFloat32
|
||||
O- how do mono output?
|
||||
O- FIFO between input and output callbacks if different devices, like in pa_mac.c
|
||||
O- debug mono output
|
||||
O- error check for unsupported sample rates
|
||||
O- BUG: if ask for 22050 when only 44100 supported then get 44100 and no error message
|
||||
*/
|
||||
*/
|
||||
|
||||
#include <CoreServices/CoreServices.h>
|
||||
#include <CoreAudio/CoreAudio.h>
|
||||
#include <sys/time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "portaudio.h"
|
||||
#include "pa_host.h"
|
||||
#include "pa_trace.h"
|
||||
|
||||
/************************************************* Constants ********/
|
||||
/* Switches for debugging. */
|
||||
#define PA_SIMULATE_UNDERFLOW (0) /* Set to one to force an underflow of the output buffer. */
|
||||
|
||||
/* To trace program, enable TRACE_REALTIME_EVENTS in pa_trace.h */
|
||||
#define PA_TRACE_RUN (0)
|
||||
|
|
@ -75,8 +78,8 @@ O- BUG: if ask for 22050 when only 44100 supported then get 44100 and no error m
|
|||
#define DBUG(x) /* PRINT(x) */
|
||||
#define DBUGX(x) /* PRINT(x) */
|
||||
|
||||
// define value of isInput passed to CodeAudio routines
|
||||
#define IS_INPUT (true)
|
||||
// define value of isInput passed to CoreAudio routines
|
||||
#define IS_INPUT (true)
|
||||
#define IS_OUTPUT (false)
|
||||
|
||||
/**************************************************************
|
||||
|
|
@ -87,24 +90,15 @@ typedef struct PaHostSoundControl
|
|||
{
|
||||
AudioDeviceID pahsc_AudioDeviceID; // Must be the same for input and output for now.
|
||||
/* Input -------------- */
|
||||
int pahsc_BytesPerHostInputBuffer;
|
||||
int pahsc_BytesPerUserInputBuffer; /* native buffer size in bytes */
|
||||
int pahsc_BytesPerUserNativeInputBuffer; /* native buffer size in bytes per user chunk */
|
||||
/* Output -------------- */
|
||||
int pahsc_BytesPerHostOutputBuffer;
|
||||
int pahsc_BytesPerUserOutputBuffer; /* native buffer size in bytes */
|
||||
/* Run Time -------------- */
|
||||
PaTimestamp pahsc_FramesPlayed;
|
||||
long pahsc_LastPosition; /* used to track frames played. */
|
||||
/* For measuring CPU utilization. */
|
||||
// LARGE_INTEGER pahsc_EntryCount;
|
||||
// LARGE_INTEGER pahsc_LastExitCount;
|
||||
int pahsc_BytesPerUserNativeOutputBuffer; /* native buffer size in bytes per user chunk */
|
||||
/* Init Time -------------- */
|
||||
int pahsc_FramesPerHostBuffer;
|
||||
int pahsc_UserBuffersPerHostBuffer;
|
||||
|
||||
// CRITICAL_SECTION pahsc_StreamLock; /* Mutext to prevent threads from colliding. */
|
||||
int pahsc_StreamLockInited;
|
||||
|
||||
int pahsc_UserBuffersPerHostBuffer;
|
||||
/* For measuring CPU utilization. */
|
||||
struct timeval pahsc_EntryTime;
|
||||
double pahsc_InverseMicrosPerBuffer; /* 1/Microseconds of real-time audio per user buffer. */
|
||||
}
|
||||
PaHostSoundControl;
|
||||
|
||||
|
|
@ -136,10 +130,13 @@ static AudioDeviceID *sCoreDeviceIDs; // Array of Core AudioDeviceIDs
|
|||
static const char sMapperSuffixInput[] = " - Input";
|
||||
static const char sMapperSuffixOutput[] = " - Output";
|
||||
|
||||
/************************************************* Macros ********/
|
||||
/* We index the input devices first, then the output devices. */
|
||||
#define LOWEST_INPUT_DEVID (0)
|
||||
#define HIGHEST_INPUT_DEVID (sNumInputDevices - 1)
|
||||
#define LOWEST_OUTPUT_DEVID (sNumInputDevices)
|
||||
#define HIGHEST_OUTPUT_DEVID (sNumPaDevices - 1)
|
||||
|
||||
/* Convert external PA ID to an internal ID that includes WAVE_MAPPER */
|
||||
#define PaDeviceIdToWinId(id) (((id) < sNumInputDevices) ? (id - 1) : (id - sNumInputDevices - 1))
|
||||
/************************************************* Macros ********/
|
||||
|
||||
/************************************************* Prototypes **********/
|
||||
|
||||
|
|
@ -159,46 +156,44 @@ static void Pa_StartUsageCalculation( internalPortAudioStream *past )
|
|||
PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData;
|
||||
if( pahsc == NULL ) return;
|
||||
/* Query system timer for usage analysis and to prevent overuse of CPU. */
|
||||
// QueryPerformanceCounter( &pahsc->pahsc_EntryCount );
|
||||
gettimeofday( &pahsc->pahsc_EntryTime, NULL );
|
||||
}
|
||||
|
||||
static long SubtractTime_AminusB( struct timeval *timeA, struct timeval *timeB )
|
||||
{
|
||||
long secs = timeA->tv_sec - timeB->tv_sec;
|
||||
long usecs = secs * 1000000;
|
||||
usecs += (timeA->tv_usec - timeB->tv_usec);
|
||||
return usecs;
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
** Measure fractional CPU load based on real-time it took to calculate
|
||||
** buffers worth of output.
|
||||
*/
|
||||
static void Pa_EndUsageCalculation( internalPortAudioStream *past )
|
||||
{
|
||||
#if 0
|
||||
// LARGE_INTEGER CurrentCount = { 0, 0 };
|
||||
// LONGLONG InsideCount;
|
||||
// LONGLONG TotalCount;
|
||||
/*
|
||||
** Measure CPU utilization during this callback. Note that this calculation
|
||||
** assumes that we had the processor the whole time.
|
||||
*/
|
||||
#define LOWPASS_COEFFICIENT_0 (0.9)
|
||||
struct timeval currentTime;
|
||||
long usecsElapsed;
|
||||
double newUsage;
|
||||
|
||||
#define LOWPASS_COEFFICIENT_0 (0.95)
|
||||
#define LOWPASS_COEFFICIENT_1 (0.99999 - LOWPASS_COEFFICIENT_0)
|
||||
|
||||
PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData;
|
||||
if( pahsc == NULL ) return;
|
||||
|
||||
if( QueryPerformanceCounter( &CurrentCount ) )
|
||||
if( gettimeofday( ¤tTime, NULL ) == 0 )
|
||||
{
|
||||
if( past->past_IfLastExitValid )
|
||||
{
|
||||
InsideCount = CurrentCount.QuadPart - pahsc->pahsc_EntryCount.QuadPart;
|
||||
TotalCount = CurrentCount.QuadPart - pahsc->pahsc_LastExitCount.QuadPart;
|
||||
/* Low pass filter the result because sometimes we get called several times in a row.
|
||||
* That can cause the TotalCount to be very low which can cause the usage to appear
|
||||
* unnaturally high. So we must filter numerator and denominator separately!!!
|
||||
*/
|
||||
past->past_AverageInsideCount = (( LOWPASS_COEFFICIENT_0 * past->past_AverageInsideCount) +
|
||||
(LOWPASS_COEFFICIENT_1 * InsideCount));
|
||||
past->past_AverageTotalCount = (( LOWPASS_COEFFICIENT_0 * past->past_AverageTotalCount) +
|
||||
(LOWPASS_COEFFICIENT_1 * TotalCount));
|
||||
past->past_Usage = past->past_AverageInsideCount / past->past_AverageTotalCount;
|
||||
}
|
||||
pahsc->pahsc_LastExitCount = CurrentCount;
|
||||
past->past_IfLastExitValid = 1;
|
||||
usecsElapsed = SubtractTime_AminusB( ¤tTime, &pahsc->pahsc_EntryTime );
|
||||
/* Use inverse because it is faster than the divide. */
|
||||
newUsage = usecsElapsed * pahsc->pahsc_InverseMicrosPerBuffer;
|
||||
|
||||
past->past_Usage = (LOWPASS_COEFFICIENT_0 * past->past_Usage) +
|
||||
(LOWPASS_COEFFICIENT_1 * newUsage);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
/****************************************** END CPU UTILIZATION *******/
|
||||
|
||||
/************************************************************************/
|
||||
static PaDeviceID Pa_QueryDefaultInputDevice( void )
|
||||
|
|
@ -211,12 +206,13 @@ static PaDeviceID Pa_QueryDefaultInputDevice( void )
|
|||
|
||||
// get the default output device for the HAL
|
||||
// it is required to pass the size of the data to be returned
|
||||
count = sizeof(AudioDeviceID);
|
||||
count = sizeof(tempDeviceID);
|
||||
err = AudioHardwareGetProperty( kAudioHardwarePropertyDefaultInputDevice, &count, (void *) &tempDeviceID);
|
||||
if (err != noErr) goto Bail;
|
||||
|
||||
// scan input devices to see which one matches this device
|
||||
defaultDeviceID = paNoDevice;
|
||||
for( i=0; i<sNumInputDevices; i++ )
|
||||
for( i=LOWEST_INPUT_DEVID; i<=HIGHEST_INPUT_DEVID; i++ )
|
||||
{
|
||||
DBUG(("Pa_QueryDefaultInputDevice: i = %d, aDevId = %d\n", i, sDeviceInfos[i].audioDeviceID ));
|
||||
if( sDeviceInfos[i].audioDeviceID == tempDeviceID )
|
||||
|
|
@ -240,12 +236,13 @@ static PaDeviceID Pa_QueryDefaultOutputDevice( void )
|
|||
|
||||
// get the default output device for the HAL
|
||||
// it is required to pass the size of the data to be returned
|
||||
count = sizeof(AudioDeviceID);
|
||||
count = sizeof(tempDeviceID);
|
||||
err = AudioHardwareGetProperty( kAudioHardwarePropertyDefaultOutputDevice, &count, (void *) &tempDeviceID);
|
||||
if (err != noErr) goto Bail;
|
||||
// scan input devices to see which one matches this device
|
||||
|
||||
// scan output devices to see which one matches this device
|
||||
defaultDeviceID = paNoDevice;
|
||||
for( i=sNumInputDevices; i<sNumPaDevices; i++ )
|
||||
for( i=LOWEST_OUTPUT_DEVID; i<=HIGHEST_OUTPUT_DEVID; i++ )
|
||||
{
|
||||
DBUG(("Pa_QueryDefaultOutputDevice: i = %d, aDevId = %d\n", i, sDeviceInfos[i].audioDeviceID ));
|
||||
if( sDeviceInfos[i].audioDeviceID == tempDeviceID )
|
||||
|
|
@ -258,8 +255,7 @@ Bail:
|
|||
return defaultDeviceID;
|
||||
}
|
||||
|
||||
/****************************************** END CPU UTILIZATION *******/
|
||||
|
||||
/******************************************************************/
|
||||
static PaError Pa_QueryDevices( void )
|
||||
{
|
||||
OSStatus err = noErr;
|
||||
|
|
@ -301,6 +297,7 @@ static PaError Pa_QueryDevices( void )
|
|||
PaHost_ScanDevices( IS_OUTPUT );
|
||||
sNumOutputDevices = sNumPaDevices - sNumInputDevices;
|
||||
|
||||
// Figure out which of the devices that we scanned is the default device.
|
||||
sDefaultInputDeviceID = Pa_QueryDefaultInputDevice();
|
||||
sDefaultOutputDeviceID = Pa_QueryDefaultOutputDevice();
|
||||
|
||||
|
|
@ -321,14 +318,14 @@ int Pa_CountDevices()
|
|||
}
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
/* Allocate a string containing the device name. */
|
||||
char *deviceNameFromID(AudioDeviceID deviceID, Boolean isInput )
|
||||
static char *PaHost_DeviceNameFromID(AudioDeviceID deviceID, Boolean isInput )
|
||||
{
|
||||
OSStatus err = noErr;
|
||||
UInt32 outSize;
|
||||
Boolean outWritable;
|
||||
char *deviceName = nil;
|
||||
|
||||
// query size of name
|
||||
err = AudioDeviceGetPropertyInfo(deviceID, 0, isInput, kAudioDevicePropertyDeviceName, &outSize, &outWritable);
|
||||
if (err == noErr)
|
||||
|
|
@ -345,14 +342,6 @@ char *deviceNameFromID(AudioDeviceID deviceID, Boolean isInput )
|
|||
return deviceName;
|
||||
}
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
OSStatus deviceDataFormatFromID(AudioDeviceID deviceID , AudioStreamBasicDescription *desc, Boolean isInput )
|
||||
{
|
||||
UInt32 outSize = sizeof(*desc);
|
||||
return AudioDeviceGetProperty(deviceID, 0, isInput, kAudioDevicePropertyStreamFormat, &outSize, desc);
|
||||
}
|
||||
|
||||
/*************************************************************************/
|
||||
// An AudioStreamBasicDescription is passed in to query whether or not
|
||||
// the format is supported. A kAudioDeviceUnsupportedFormatError will
|
||||
|
|
@ -460,6 +449,7 @@ static int PaHost_QueryDeviceInfo( PaHostDeviceInfo *hostDeviceInfo, int coreDev
|
|||
{
|
||||
OSErr err;
|
||||
int index;
|
||||
UInt32 outSize;
|
||||
AudioStreamBasicDescription formatDesc;
|
||||
Boolean result;
|
||||
AudioDeviceID devID;
|
||||
|
|
@ -503,7 +493,9 @@ static int PaHost_QueryDeviceInfo( PaHostDeviceInfo *hostDeviceInfo, int coreDev
|
|||
if( deviceInfo->numSampleRates == 0 ) goto error;
|
||||
|
||||
// Get data format info from the device.
|
||||
err = deviceDataFormatFromID(devID, &formatDesc, isInput );
|
||||
outSize = sizeof(formatDesc);
|
||||
err = AudioDeviceGetProperty(devID, 0, isInput, kAudioDevicePropertyStreamFormat, &outSize, &formatDesc);
|
||||
|
||||
// If no channels supported, then not a very good device.
|
||||
if( (err != noErr) || (formatDesc.mChannelsPerFrame == 0) ) goto error;
|
||||
|
||||
|
|
@ -543,9 +535,8 @@ static int PaHost_QueryDeviceInfo( PaHostDeviceInfo *hostDeviceInfo, int coreDev
|
|||
break;
|
||||
}
|
||||
|
||||
|
||||
// Get the device name
|
||||
deviceInfo->name = deviceNameFromID( devID, isInput );
|
||||
deviceInfo->name = PaHost_DeviceNameFromID( devID, isInput );
|
||||
return 1;
|
||||
|
||||
error:
|
||||
|
|
@ -630,9 +621,6 @@ PaDeviceID Pa_GetDefaultOutputDeviceID( void )
|
|||
|
||||
PaError PaHost_Init( void )
|
||||
{
|
||||
#if PA_SIMULATE_UNDERFLOW
|
||||
PRINT(("WARNING - Underflow Simulation Enabled - Expect a Big Glitch!!!\n"));
|
||||
#endif
|
||||
return Pa_MaybeQueryDevices();
|
||||
}
|
||||
|
||||
|
|
@ -644,8 +632,8 @@ static PaError Pa_TimeSlice( internalPortAudioStream *past, const AudioBufferL
|
|||
AudioBufferList* outOutputData )
|
||||
{
|
||||
PaError result = 0;
|
||||
char *inBufPtr;
|
||||
char *outBufPtr;
|
||||
char *inputNativeBufferfPtr;
|
||||
char *outputNativeBufferfPtr;
|
||||
int gotInput = 0;
|
||||
int gotOutput = 0;
|
||||
int i;
|
||||
|
|
@ -656,13 +644,6 @@ static PaError Pa_TimeSlice( internalPortAudioStream *past, const AudioBufferL
|
|||
|
||||
past->past_NumCallbacks += 1;
|
||||
|
||||
#if PA_SIMULATE_UNDERFLOW
|
||||
if(gUnderCallbackCounter++ == UNDER_SLEEP_AT)
|
||||
{
|
||||
Sleep(UNDER_SLEEP_FOR);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if PA_TRACE_RUN
|
||||
AddTraceMessage("Pa_TimeSlice: past_NumCallbacks ", past->past_NumCallbacks );
|
||||
#endif
|
||||
|
|
@ -673,16 +654,16 @@ static PaError Pa_TimeSlice( internalPortAudioStream *past, const AudioBufferL
|
|||
gotOutput = 0;
|
||||
if( past->past_NumOutputChannels > 0 )
|
||||
{
|
||||
outBufPtr = (char*)outOutputData->mBuffers[0].mData,
|
||||
outputNativeBufferfPtr = (char*)outOutputData->mBuffers[0].mData,
|
||||
gotOutput = 1;
|
||||
}
|
||||
|
||||
/* If we are using input, then we need a full input buffer. */
|
||||
gotInput = 0;
|
||||
inBufPtr = NULL;
|
||||
inputNativeBufferfPtr = NULL;
|
||||
if( past->past_NumInputChannels > 0 )
|
||||
{
|
||||
inBufPtr = (char*)inInputData->mBuffers[0].mData,
|
||||
inputNativeBufferfPtr = (char*)inInputData->mBuffers[0].mData,
|
||||
gotInput = 1;
|
||||
}
|
||||
|
||||
|
|
@ -697,17 +678,17 @@ static PaError Pa_TimeSlice( internalPortAudioStream *past, const AudioBufferL
|
|||
{
|
||||
/* Clear remainder of wave buffer if we are waiting for stop. */
|
||||
AddTraceMessage("Pa_TimeSlice: zero rest of wave buffer ", i );
|
||||
memset( outBufPtr, 0, pahsc->pahsc_BytesPerUserOutputBuffer );
|
||||
memset( outputNativeBufferfPtr, 0, pahsc->pahsc_BytesPerUserNativeOutputBuffer );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Convert 16 bit native data to user data and call user routine. */
|
||||
result = Pa_CallConvertFloat32( past, (float *) inBufPtr, (float *) outBufPtr );
|
||||
/* Convert 32 bit native data to user data and call user routine. */
|
||||
result = PaConvert_Process( past, inputNativeBufferfPtr, outputNativeBufferfPtr );
|
||||
if( result != 0) done = 1;
|
||||
}
|
||||
if( gotInput ) inBufPtr += pahsc->pahsc_BytesPerUserInputBuffer;
|
||||
if( gotOutput) outBufPtr += pahsc->pahsc_BytesPerUserOutputBuffer;
|
||||
if( gotInput ) inputNativeBufferfPtr += pahsc->pahsc_BytesPerUserNativeInputBuffer;
|
||||
if( gotOutput) outputNativeBufferfPtr += pahsc->pahsc_BytesPerUserNativeOutputBuffer;
|
||||
}
|
||||
|
||||
Pa_EndUsageCalculation( past );
|
||||
|
|
@ -731,7 +712,7 @@ OSStatus appIOProc (AudioDeviceID inDevice, const AudioTimeStamp* inNow,
|
|||
past = (internalPortAudioStream *) contextPtr;
|
||||
pahsc = (PaHostSoundControl *) past->past_DeviceData;
|
||||
|
||||
// Just Curious: printf("Num input Buffers: %d; Num output Buffers: %d.\n", inInputData->mNumberBuffers, outOutputData->mNumberBuffers);
|
||||
// printf("Num input Buffers: %d; Num output Buffers: %d.\n", inInputData->mNumberBuffers, outOutputData->mNumberBuffers);
|
||||
|
||||
/* Has someone asked us to abort by calling Pa_AbortStream()? */
|
||||
if( past->past_StopNow )
|
||||
|
|
@ -748,6 +729,12 @@ OSStatus appIOProc (AudioDeviceID inDevice, const AudioTimeStamp* inNow,
|
|||
}
|
||||
else
|
||||
{
|
||||
/* use time stamp from CoreAudio if valid */
|
||||
if( inOutputTime->mFlags & kAudioTimeStampSampleTimeValid)
|
||||
{
|
||||
past->past_FrameCount = inOutputTime->mSampleTime;
|
||||
}
|
||||
|
||||
/* Process full input buffer and fill up empty output buffers. */
|
||||
if( (result = Pa_TimeSlice( past, inInputData, outOutputData )) != 0)
|
||||
{
|
||||
|
|
@ -786,21 +773,43 @@ PaError PaHost_OpenInputStream( internalPortAudioStream *past )
|
|||
int bytesPerInputFrame;
|
||||
|
||||
pahsc = (PaHostSoundControl *) past->past_DeviceData;
|
||||
|
||||
|
||||
DBUG(("PaHost_OpenStream: deviceID = 0x%x\n", past->past_InputDeviceID));
|
||||
if( (past->past_InputDeviceID < 0) || (past->past_InputDeviceID >= sNumPaDevices) )
|
||||
if( (past->past_InputDeviceID < LOWEST_INPUT_DEVID) ||
|
||||
(past->past_InputDeviceID > HIGHEST_INPUT_DEVID) )
|
||||
{
|
||||
return paInvalidDeviceId;
|
||||
}
|
||||
hostDeviceInfo = &sDeviceInfos[past->past_InputDeviceID];
|
||||
|
||||
// calculate buffer sizes in bytes
|
||||
bytesPerInputFrame = Pa_GetSampleSize(past->past_InputSampleFormat) * past->past_NumInputChannels;
|
||||
pahsc->pahsc_BytesPerUserInputBuffer = past->past_FramesPerUserBuffer * bytesPerInputFrame;
|
||||
pahsc->pahsc_BytesPerHostInputBuffer = pahsc->pahsc_FramesPerHostBuffer * bytesPerInputFrame;
|
||||
if( past->past_NumInputChannels != hostDeviceInfo->paInfo.maxInputChannels )
|
||||
{
|
||||
#if 1
|
||||
return paInvalidChannelCount; // FIXME - how support mono?
|
||||
#else
|
||||
FIXME - should this be set on a stream basis? Is it possible to change?
|
||||
/* Attempt to set number of channels. */
|
||||
AudioStreamBasicDescription formatDesc;
|
||||
OSStatus err;
|
||||
memset( &formatDesc, 0, sizeof(AudioStreamBasicDescription) );
|
||||
formatDesc.mChannelsPerFrame = past->past_NumInputChannels;
|
||||
err = AudioDeviceSetProperty(pahsc->pahsc_AudioDeviceID, 0, 0,
|
||||
IS_INPUT, kAudioDevicePropertyStreamFormat, sizeof(formatDesc), &formatDesc);
|
||||
|
||||
if (err != kAudioHardwareNoError)
|
||||
{
|
||||
result = paInvalidChannelCount;
|
||||
goto error;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// calculate native buffer sizes in bytes
|
||||
bytesPerInputFrame = Pa_GetSampleSize(paFloat32) * past->past_NumInputChannels;
|
||||
pahsc->pahsc_BytesPerUserNativeInputBuffer = past->past_FramesPerUserBuffer * bytesPerInputFrame;
|
||||
bytesPerHostBuffer = pahsc->pahsc_FramesPerHostBuffer * bytesPerInputFrame;
|
||||
|
||||
// Change the bufferSize of the device! Is this per device or just for our stream?
|
||||
bytesPerHostBuffer = pahsc->pahsc_BytesPerHostInputBuffer;
|
||||
dataSize = sizeof(UInt32);
|
||||
err = AudioDeviceSetProperty( hostDeviceInfo->audioDeviceID, 0, 0, IS_INPUT,
|
||||
kAudioDevicePropertyBufferSize, dataSize, &bytesPerHostBuffer);
|
||||
|
|
@ -811,6 +820,9 @@ PaError PaHost_OpenInputStream( internalPortAudioStream *past )
|
|||
goto error;
|
||||
}
|
||||
|
||||
// setup conversion procedure
|
||||
result = PaConvert_SetupInput( past, paFloat32 );
|
||||
|
||||
return result;
|
||||
|
||||
error:
|
||||
|
|
@ -827,24 +839,45 @@ PaError PaHost_OpenOutputStream( internalPortAudioStream *past )
|
|||
UInt32 dataSize;
|
||||
OSStatus err = noErr;
|
||||
int bytesPerOutputFrame;
|
||||
|
||||
|
||||
pahsc = (PaHostSoundControl *) past->past_DeviceData;
|
||||
|
||||
|
||||
DBUG(("PaHost_OpenStream: deviceID = 0x%x\n", past->past_OutputDeviceID));
|
||||
if( (past->past_OutputDeviceID < 0) || (past->past_OutputDeviceID >= sNumPaDevices) )
|
||||
if( (past->past_OutputDeviceID < LOWEST_OUTPUT_DEVID) ||
|
||||
(past->past_OutputDeviceID > HIGHEST_OUTPUT_DEVID) )
|
||||
{
|
||||
return paInvalidDeviceId;
|
||||
}
|
||||
hostDeviceInfo = &sDeviceInfos[past->past_OutputDeviceID];
|
||||
|
||||
if( past->past_NumOutputChannels != hostDeviceInfo->paInfo.maxOutputChannels )
|
||||
{
|
||||
#if 1
|
||||
return paInvalidChannelCount; // FIXME - how support mono?
|
||||
#else
|
||||
/* Attempt to set number of channels. */
|
||||
AudioStreamBasicDescription formatDesc;
|
||||
OSStatus err;
|
||||
memset( &formatDesc, 0, sizeof(AudioStreamBasicDescription) );
|
||||
formatDesc.mChannelsPerFrame = past->past_NumOutputChannels;
|
||||
err = AudioDeviceSetProperty(pahsc->pahsc_AudioDeviceID, 0, 0,
|
||||
IS_OUTPUT, kAudioDevicePropertyStreamFormat, sizeof(formatDesc), &formatDesc);
|
||||
|
||||
if (err != kAudioHardwareNoError)
|
||||
{
|
||||
result = paInvalidChannelCount;
|
||||
goto error;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// calculate buffer sizes in bytes
|
||||
bytesPerOutputFrame = Pa_GetSampleSize(past->past_OutputSampleFormat) * past->past_NumOutputChannels;
|
||||
pahsc->pahsc_BytesPerUserOutputBuffer = past->past_FramesPerUserBuffer * bytesPerOutputFrame;
|
||||
pahsc->pahsc_BytesPerHostOutputBuffer = pahsc->pahsc_FramesPerHostBuffer * bytesPerOutputFrame;
|
||||
bytesPerOutputFrame = Pa_GetSampleSize(paFloat32) * past->past_NumOutputChannels;
|
||||
pahsc->pahsc_BytesPerUserNativeOutputBuffer = past->past_FramesPerUserBuffer * bytesPerOutputFrame;
|
||||
bytesPerHostBuffer = pahsc->pahsc_FramesPerHostBuffer * bytesPerOutputFrame;
|
||||
|
||||
// Change the bufferSize of the device! Is this per device or just for our stream?
|
||||
bytesPerHostBuffer = pahsc->pahsc_BytesPerHostOutputBuffer;
|
||||
dataSize = sizeof(UInt32);
|
||||
dataSize = sizeof(bytesPerHostBuffer);
|
||||
err = AudioDeviceSetProperty( hostDeviceInfo->audioDeviceID, 0, 0, IS_OUTPUT,
|
||||
kAudioDevicePropertyBufferSize, dataSize, &bytesPerHostBuffer);
|
||||
if( err != noErr )
|
||||
|
|
@ -853,7 +886,10 @@ PaError PaHost_OpenOutputStream( internalPortAudioStream *past )
|
|||
result = paHostError;
|
||||
goto error;
|
||||
}
|
||||
|
||||
|
||||
// setup conversion procedure
|
||||
result = PaConvert_SetupOutput( past, paFloat32 );
|
||||
|
||||
return result;
|
||||
|
||||
error:
|
||||
|
|
@ -931,8 +967,11 @@ PaError PaHost_OpenStream( internalPortAudioStream *past )
|
|||
|
||||
{
|
||||
int msecLatency = (int) ((PaHost_GetTotalBufferFrames(past) * 1000) / past->past_SampleRate);
|
||||
PRINT(("PortAudio on OSX - Latency = %d frames, %d msec\n", PaHost_GetTotalBufferFrames(past), msecLatency ));
|
||||
PRINT(("PortAudio on OS X - Latency = %d frames, %d msec\n", PaHost_GetTotalBufferFrames(past), msecLatency ));
|
||||
}
|
||||
|
||||
/* Setup constants for CPU load measurement. */
|
||||
pahsc->pahsc_InverseMicrosPerBuffer = past->past_SampleRate / (1000000.0 * past->past_FramesPerUserBuffer);
|
||||
|
||||
/* ------------------ OUTPUT */
|
||||
if( (past->past_OutputDeviceID != paNoDevice) && (past->past_NumOutputChannels > 0) )
|
||||
|
|
@ -950,6 +989,22 @@ PaError PaHost_OpenStream( internalPortAudioStream *past )
|
|||
pahsc->pahsc_AudioDeviceID = sDeviceInfos[past->past_InputDeviceID].audioDeviceID;
|
||||
}
|
||||
|
||||
/* Attempt to set device sample rate. */
|
||||
{
|
||||
AudioStreamBasicDescription formatDesc;
|
||||
OSStatus err;
|
||||
memset( &formatDesc, 0, sizeof(AudioStreamBasicDescription) );
|
||||
formatDesc.mSampleRate = past->past_SampleRate;
|
||||
err = AudioDeviceSetProperty(pahsc->pahsc_AudioDeviceID, 0, 0,
|
||||
IS_OUTPUT, kAudioDevicePropertyStreamFormat, sizeof(formatDesc), &formatDesc);
|
||||
|
||||
if (err != kAudioHardwareNoError)
|
||||
{
|
||||
result = paInvalidSampleRate;
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
error:
|
||||
|
|
@ -979,9 +1034,7 @@ PaError PaHost_StartEngine( internalPortAudioStream *past )
|
|||
past->past_StopSoon = 0;
|
||||
past->past_StopNow = 0;
|
||||
past->past_IsActive = 1;
|
||||
pahsc->pahsc_FramesPlayed = 0.0;
|
||||
pahsc->pahsc_LastPosition = 0;
|
||||
|
||||
|
||||
// Associate an IO proc with the device and pass a pointer to the audio data context
|
||||
err = AudioDeviceAddIOProc(pahsc->pahsc_AudioDeviceID, (AudioDeviceIOProc)appIOProc, past);
|
||||
if (err != noErr) goto error;
|
||||
|
|
@ -1061,6 +1114,38 @@ PaError PaHost_CloseStream( internalPortAudioStream *past )
|
|||
return paNoError;
|
||||
}
|
||||
|
||||
/*************************************************************************
|
||||
** Determine minimum number of buffers required for this host based
|
||||
** on minimum latency. Latency can be optionally set by user by setting
|
||||
** an environment variable. For example, to set latency to 200 msec, put:
|
||||
**
|
||||
** set PA_MIN_LATENCY_MSEC=200
|
||||
**
|
||||
** in the cshrc file.
|
||||
*/
|
||||
#define PA_LATENCY_ENV_NAME ("PA_MIN_LATENCY_MSEC")
|
||||
|
||||
#if 1
|
||||
int Pa_GetMinNumBuffers( int framesPerBuffer, double framesPerSecond )
|
||||
{
|
||||
int minBuffers;
|
||||
int denominator;
|
||||
int minLatencyMsec = PA_MIN_LATENCY_MSEC;
|
||||
char *minLatencyText = getenv(PA_LATENCY_ENV_NAME);
|
||||
if( minLatencyText != NULL )
|
||||
{
|
||||
PRINT(("PA_MIN_LATENCY_MSEC = %s\n", minLatencyText ));
|
||||
minLatencyMsec = atoi( minLatencyText );
|
||||
if( minLatencyMsec < 1 ) minLatencyMsec = 1;
|
||||
else if( minLatencyMsec > 5000 ) minLatencyMsec = 5000;
|
||||
}
|
||||
|
||||
denominator = 1000.0 * framesPerBuffer;
|
||||
minBuffers = (int) (((minLatencyMsec * framesPerSecond) + denominator - 1) / denominator );
|
||||
if( minBuffers < 1 ) minBuffers = 1;
|
||||
return minBuffers;
|
||||
}
|
||||
#else
|
||||
/*************************************************************************
|
||||
** Determine minimum number of buffers required for this host based
|
||||
** on minimum latency.
|
||||
|
|
@ -1081,6 +1166,7 @@ int Pa_GetMinNumBuffers( int framesPerUserBuffer, double sampleRate )
|
|||
|
||||
return minUserBuffers;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*************************************************************************
|
||||
** Cleanup device info.
|
||||
|
|
@ -1151,11 +1237,17 @@ PaError PaHost_StreamActive( internalPortAudioStream *past )
|
|||
/*************************************************************************/
|
||||
PaTimestamp Pa_StreamTime( PortAudioStream *stream )
|
||||
{
|
||||
AudioTimeStamp timeStamp;
|
||||
PaTimestamp streamTime;
|
||||
PaHostSoundControl *pahsc;
|
||||
internalPortAudioStream *past = (internalPortAudioStream *) stream;
|
||||
if( past == NULL ) return paBadStreamPtr;
|
||||
pahsc = (PaHostSoundControl *) past->past_DeviceData;
|
||||
|
||||
AudioDeviceGetCurrentTime(pahsc->pahsc_AudioDeviceID, &timeStamp);
|
||||
|
||||
streamTime = ( timeStamp.mFlags & kAudioTimeStampSampleTimeValid) ?
|
||||
timeStamp.mSampleTime : past->past_FrameCount;
|
||||
|
||||
// FIXME PaHost_UpdateStreamTime( pahsc );
|
||||
return pahsc->pahsc_FramesPlayed;
|
||||
return streamTime;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue