]> Repos - portaudio/commitdiff
Added paInt16, paInt8, format using new "pa_common/pa_convert.c" file.
authorphilburk <philburk@0f58301d-fd10-0410-b4af-bbb618454e57>
Thu, 21 Mar 2002 00:39:42 +0000 (00:39 +0000)
committerphilburk <philburk@0f58301d-fd10-0410-b4af-bbb618454e57>
Thu, 21 Mar 2002 00:39:42 +0000 (00:39 +0000)
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.

pa_mac_core/notes.txt
pa_mac_core/pa_mac_core.c

index 7e8835bda75983f1d62463484f7c9152834f943d..c79b90e66dc61da13b25b55a9ecaa19647bdb65c 100644 (file)
@@ -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
 
index 43fab187e983ca7117bb5731e638c1c5a584eeaa..f3843820bd810b29c2ed069ed11f3fb2b570a490 100644 (file)
  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( &currentTime, 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( &currentTime, &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;
 }