]> Repos - portaudio/commitdiff
Initial revision
authorphil <phil@0f58301d-fd10-0410-b4af-bbb618454e57>
Tue, 22 Jan 2002 00:51:49 +0000 (00:51 +0000)
committerphil <phil@0f58301d-fd10-0410-b4af-bbb618454e57>
Tue, 22 Jan 2002 00:51:49 +0000 (00:51 +0000)
76 files changed:
Makefile.linux [new file with mode: 0644]
fixdir.bat [new file with mode: 0755]
pa_asio/Callback_adaptation_.pdf [new file with mode: 0644]
pa_asio/Pa_ASIO.pdf [new file with mode: 0644]
pa_asio/pa_asio.cpp [new file with mode: 0644]
pa_beos/PlaybackNode.cc [new file with mode: 0644]
pa_beos/PlaybackNode.h [new file with mode: 0644]
pa_beos/pa_beos_mk.cc [new file with mode: 0644]
pa_common/pa_host.h [new file with mode: 0644]
pa_common/pa_lib.c [new file with mode: 0644]
pa_common/pa_trace.c [new file with mode: 0644]
pa_common/pa_trace.h [new file with mode: 0644]
pa_common/portaudio.h [new file with mode: 0644]
pa_dll_switch/PaDllEntry.h [new file with mode: 0644]
pa_dll_switch/letter_from_tim_010817.txt [new file with mode: 0644]
pa_dll_switch/loadPA_DLL.cpp [new file with mode: 0644]
pa_dll_switch/pa_lib.c [new file with mode: 0644]
pa_dll_switch/portaudio.h [new file with mode: 0644]
pa_mac/pa_mac.c [new file with mode: 0644]
pa_mac_core/pa_mac_core.c [new file with mode: 0644]
pa_sgi/Makefile [new file with mode: 0644]
pa_sgi/pa_sgi.c [new file with mode: 0644]
pa_tests/debug_dual.c [new file with mode: 0644]
pa_tests/debug_multi_in.c [new file with mode: 0644]
pa_tests/debug_multi_out.c [new file with mode: 0644]
pa_tests/debug_record.c [new file with mode: 0644]
pa_tests/debug_sine.c [new file with mode: 0644]
pa_tests/debug_sine_wait.c [new file with mode: 0644]
pa_tests/debug_test1.c [new file with mode: 0644]
pa_tests/pa_devs.c [new file with mode: 0644]
pa_tests/pa_fuzz.c [new file with mode: 0644]
pa_tests/pa_minlat.c [new file with mode: 0644]
pa_tests/paqa_devs.c [new file with mode: 0644]
pa_tests/paqa_errs.c [new file with mode: 0644]
pa_tests/patest1.c [new file with mode: 0644]
pa_tests/patest_buffer.c [new file with mode: 0644]
pa_tests/patest_clip.c [new file with mode: 0644]
pa_tests/patest_dither.c [new file with mode: 0644]
pa_tests/patest_latency.c [new file with mode: 0644]
pa_tests/patest_leftright.c [new file with mode: 0644]
pa_tests/patest_longsine.c [new file with mode: 0644]
pa_tests/patest_many.c [new file with mode: 0644]
pa_tests/patest_maxsines.c [new file with mode: 0644]
pa_tests/patest_pink.c [new file with mode: 0644]
pa_tests/patest_record.c [new file with mode: 0644]
pa_tests/patest_ringmix.c [new file with mode: 0644]
pa_tests/patest_saw.c [new file with mode: 0644]
pa_tests/patest_sine.c [new file with mode: 0644]
pa_tests/patest_sine8.c [new file with mode: 0644]
pa_tests/patest_sine_formats.c [new file with mode: 0644]
pa_tests/patest_sine_time.c [new file with mode: 0644]
pa_tests/patest_stop.c [new file with mode: 0644]
pa_tests/patest_sync.c [new file with mode: 0644]
pa_tests/patest_underflow.c [new file with mode: 0644]
pa_tests/patest_wire.c [new file with mode: 0644]
pa_unix_oss/Makefile [new file with mode: 0644]
pa_unix_oss/Makefile_freebsd [new file with mode: 0644]
pa_unix_oss/low_latency_tip.txt [new file with mode: 0644]
pa_unix_oss/pa_unix_oss.c [new file with mode: 0644]
pa_unix_oss/recplay.c [new file with mode: 0644]
pa_win_ds/dsound_wrapper.c [new file with mode: 0644]
pa_win_ds/dsound_wrapper.h [new file with mode: 0644]
pa_win_ds/pa_dsound.c [new file with mode: 0644]
pa_win_ds/portaudio.def [new file with mode: 0644]
pa_win_wmme/Makefile.cygwin [new file with mode: 0644]
pa_win_wmme/pa_win_wmme.c [new file with mode: 0644]
pablio/README.txt [new file with mode: 0644]
pablio/pablio.c [new file with mode: 0644]
pablio/pablio.def [new file with mode: 0644]
pablio/pablio.h [new file with mode: 0644]
pablio/ringbuffer.c [new file with mode: 0644]
pablio/ringbuffer.h [new file with mode: 0644]
pablio/test_rw.c [new file with mode: 0644]
pablio/test_rw_echo.c [new file with mode: 0644]
pablio/test_w_saw.c [new file with mode: 0644]
pablio/test_w_saw8.c [new file with mode: 0644]

diff --git a/Makefile.linux b/Makefile.linux
new file mode 100644 (file)
index 0000000..9f67ce6
--- /dev/null
@@ -0,0 +1,46 @@
+# Make PortAudio for Linux
+# Updated 2001/08/25 Bill Eldridge bill@rfa.org
+# Updated 2001/10/16, philburk@softsynth.com, s/unix_oss/unix_oss/
+
+# A pretty bare makefile, that figures out all the test files
+# and compiles them against the library in the pa_unix_oss directory.
+
+# Do "make all" and then when happy, "make libinstall"
+# (if not happy, "make clean")
+
+# The ldconfig stuff in libinstall is the wrong way to do it - 
+# someone tell me the right way, please
+
+
+LIBS =  -lm -lpthread
+
+CDEFINES = -I../pa_common
+CFLAGS = -g
+LIBINST = /usr/local/lib
+
+TESTS:= $(wildcard pa_tests/pa*.c pa_tests/debug*.c)
+
+LIBFILES:= ./pa_common/pa_lib.c ./pa_unix_oss/pa_unix_oss.c
+
+.c.o:
+       -gcc -c -I./pa_common $< -o $*.o
+       -gcc $*.o -o $* -Lpa_unix_oss $(LIBS) -lportaudio
+
+all: sharedlib tests
+
+sharedlib:     $(LIBFILES:.c=.o)
+       gcc -shared -o ./pa_unix_oss/libportaudio.so ./pa_common/pa_lib.o ./pa_unix_oss/pa_unix_oss.o
+
+libinstall:    ./pa_unix_oss/libportaudio.so
+       @cp -f ./pa_unix_oss/libportaudio.so $(LIBINST)
+       @/sbin/ldconfig -v
+
+tests: $(TESTS:.c=.o)
+
+clean:
+       -@rm -f $(TESTS:.c=.o)
+       -@rm -f $(TESTS:.c=)
+       -@rm -f $(LIBFILES:.c=.o)
+       -@rm -f ./pa_unix_oss/libportaudio.so
+
+
diff --git a/fixdir.bat b/fixdir.bat
new file mode 100755 (executable)
index 0000000..92d6c74
--- /dev/null
@@ -0,0 +1,19 @@
+rem Use Astyle to fix style in 'C' files
+cd %1%
+
+fixlines -p *.c
+fixlines -p *.cpp
+fixlines -p *.cc
+
+astyle --style=ansi -c -o --convert-tabs --indent-preprocessor *.c
+astyle --style=ansi -c -o --convert-tabs --indent-preprocessor *.cpp
+astyle --style=ansi -c -o --convert-tabs --indent-preprocessor *.cc
+del *.orig
+@rem convert line terminators to Unix style LFs
+fixlines -u *.c
+fixlines -u *.cpp
+fixlines -u *.cc
+fixlines -u *.h
+del *.bak
+
+cd ..\
diff --git a/pa_asio/Callback_adaptation_.pdf b/pa_asio/Callback_adaptation_.pdf
new file mode 100644 (file)
index 0000000..76bf678
Binary files /dev/null and b/pa_asio/Callback_adaptation_.pdf differ
diff --git a/pa_asio/Pa_ASIO.pdf b/pa_asio/Pa_ASIO.pdf
new file mode 100644 (file)
index 0000000..ac5ecad
Binary files /dev/null and b/pa_asio/Pa_ASIO.pdf differ
diff --git a/pa_asio/pa_asio.cpp b/pa_asio/pa_asio.cpp
new file mode 100644 (file)
index 0000000..66adf48
--- /dev/null
@@ -0,0 +1,3005 @@
+/*
+ * $Id$
+ * Portable Audio I/O Library for ASIO Drivers
+ *
+ * Author: Stephane Letz
+ * Based on the Open Source API proposed by Ross Bencina
+ * Copyright (c) 2000-2001 Stephane Letz, 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
+
+        08-03-01 First version : Stephane Letz
+        08-06-01 Tweaks for PC, use C++, buffer allocation, Float32 to Int32 conversion : Phil Burk
+        08-20-01 More conversion, PA_StreamTime, Pa_GetHostError : Stephane Letz
+        08-21-01 PaUInt8 bug correction, implementation of ASIOSTFloat32LSB and ASIOSTFloat32MSB native formats : Stephane Letz
+        08-24-01 MAX_INT32_FP hack, another Uint8 fix : Stephane and Phil
+        08-27-01 Implementation of hostBufferSize < userBufferSize case, better management of the ouput buffer when
+                 the stream is stopped : Stephane Letz
+        08-28-01 Check the stream pointer for null in bufferSwitchTimeInfo, correct bug in bufferSwitchTimeInfo when 
+                     the stream is stopped : Stephane Letz
+        10-12-01 Correct the PaHost_CalcNumHostBuffers function: computes FramesPerHostBuffer to be the lowest that
+                         respect requested FramesPerUserBuffer and userBuffersPerHostBuffer : Stephane Letz
+        10-26-01 Management of hostBufferSize and userBufferSize of any size : Stephane Letz
+        10-27-01 Improve calculus of hostBufferSize to be multiple or divisor of userBufferSize if possible : Stephane and Phil
+        10-29-01 Change MAX_INT32_FP to (2147483520.0f) to prevent roundup to 0x80000000 : Phil Burk
+        10-31-01 Clear the ouput buffer and user buffers in PaHost_StartOutput, correct bug in GetFirstMultiple : Stephane Letz 
+        11-06-01 Rename functions : Stephane Letz 
+        11-08-01 New Pa_ASIO_Adaptor_Init function to init Callback adpatation variables, cleanup of Pa_ASIO_Callback_Input: Stephane Letz 
+               11-29-01 Break apart device loading to debug random failure in Pa_ASIO_QueryDeviceInfo ; Phil Burk
+               01-03-02 Desallocate all resources in PaHost_Term for cases where Pa_CloseStream is not called properly :  Stephane Letz
+        
+        TO DO :
+        
+        - Check Pa_StopSteam and Pa_AbortStream
+        - Optimization for Input only or Ouput only (really necessary ??)
+        - Opening of several streams   
+*/
+
+
+#include <stdio.h>
+#include <assert.h>
+
+#include "portaudio.h"
+#include "pa_host.h"
+#include "pa_trace.h"
+
+#include "asiosys.h"
+#include "asio.h"
+#include "asiodrivers.h"
+
+#if MAC
+#include <Math64.h>
+#else
+#include <math.h>
+#include <windows.h>
+#include <mmsystem.h>
+#endif
+
+enum {
+        // number of input and outputs supported by the host application
+        // you can change these to higher or lower values
+        kMaxInputChannels = 32,
+        kMaxOutputChannels = 32
+};
+
+/* ASIO specific device information. */
+typedef struct internalPortAudioDevice
+{       
+        PaDeviceInfo    pad_Info;
+} internalPortAudioDevice;
+
+
+/*  ASIO driver internal data storage */
+typedef struct PaHostSoundControl
+{
+        // ASIOInit()
+        ASIODriverInfo  pahsc_driverInfo;
+
+        // ASIOGetChannels()
+        int32           pahsc_NumInputChannels;         
+        int32           pahsc_NumOutputChannels;        
+
+        // ASIOGetBufferSize() - sizes in frames per buffer
+        int32           pahsc_minSize;
+        int32           pahsc_maxSize;
+        int32           pahsc_preferredSize;
+        int32           pahsc_granularity;
+
+        // ASIOGetSampleRate()
+        ASIOSampleRate pahsc_sampleRate;
+
+        // ASIOOutputReady()
+        bool           pahsc_postOutput;
+
+        // ASIOGetLatencies ()
+        int32          pahsc_inputLatency;
+        int32          pahsc_outputLatency;
+
+        // ASIOCreateBuffers ()
+        ASIOBufferInfo bufferInfos[kMaxInputChannels + kMaxOutputChannels]; // buffer info's
+
+        // ASIOGetChannelInfo()
+        ASIOChannelInfo pahsc_channelInfos[kMaxInputChannels + kMaxOutputChannels]; // channel info's
+        // The above two arrays share the same indexing, as the data in them are linked together
+
+        // Information from ASIOGetSamplePosition()
+        // data is converted to double floats for easier use, however 64 bit integer can be used, too
+        double         nanoSeconds;
+        double         samples;
+        double         tcSamples;       // time code samples
+
+        // bufferSwitchTimeInfo()
+        ASIOTime       tInfo;           // time info state
+        unsigned long  sysRefTime;      // system reference time, when bufferSwitch() was called
+
+        // Signal the end of processing in this example
+        bool           stopped;
+        
+        ASIOCallbacks   pahsc_asioCallbacks;
+        
+         
+        int32   pahsc_userInputBufferFrameOffset;   // Position in Input user buffer
+        int32   pahsc_userOutputBufferFrameOffset;  // Position in Output user buffer
+        int32   pahsc_hostOutputBufferFrameOffset;  // Position in Output ASIO buffer
+        
+        int32  past_FramesPerHostBuffer;        // Number of frames in ASIO buffer
+        
+        int32  pahsc_InputBufferOffset;         // Number of null frames for input buffer alignement
+        int32  pahsc_OutputBufferOffset;        // Number of null frames for ouput buffer alignement
+        
+#if MAC
+        UInt64   pahsc_EntryCount;
+        UInt64   pahsc_LastExitCount;
+#elif WINDOWS
+        LARGE_INTEGER      pahsc_EntryCount;
+        LARGE_INTEGER      pahsc_LastExitCount;
+#endif  
+        
+        PaTimestamp   pahsc_NumFramesDone;
+        
+        internalPortAudioStream   *past;
+        
+} PaHostSoundControl;
+
+
+//----------------------------------------------------------
+// name of the ASIO device to be used
+#if WINDOWS
+        //#define ASIO_DRIVER_NAME    "ASIO Multimedia Driver"
+        #define ASIO_DRIVER_NAME    "ASIO Sample"
+#elif MAC
+        //#define ASIO_DRIVER_NAME      "Apple Sound Manager"
+        #define ASIO_DRIVER_NAME                "ASIO Delta1010"
+        //#define ASIO_DRIVER_NAME      "DigiDesign DirectIO"
+#endif
+
+#define PRINT(x) { printf x; fflush(stdout); }
+#define ERR_RPT(x) PRINT(x)
+
+#define DBUG(x)   /* PRINT(x) */
+#define DBUGX(x)  /* PRINT(x) /**/
+
+/* We are trying to be compatible with CARBON but this has not been thoroughly tested. */
+#define CARBON_COMPATIBLE  (0)
+#define PA_MAX_DEVICE_INFO (32)
+
+#define MIN_INT8     (-0x80)
+#define MAX_INT8     (0x7F)
+
+#define MIN_INT8_FP  ((float)-0x80)
+#define MAX_INT8_FP  ((float)0x7F)
+
+#define MIN_INT16_FP ((float)-0x8000)
+#define MAX_INT16_FP ((float)0x7FFF)
+
+#define MIN_INT16    (-0x8000)
+#define MAX_INT16    (0x7FFF)
+
+#define MAX_INT32_FP (2147483520.0f)  /* 0x0x7FFFFF80 - seems safe */
+
+/************************************************************************************/
+/****************** Data ************************************************************/
+/************************************************************************************/
+static int                 sNumDevices = 0;
+static internalPortAudioDevice sDevices[PA_MAX_DEVICE_INFO] = { 0 };
+static int32               sPaHostError = 0;
+static int                 sDefaultOutputDeviceID = 0;
+static int                 sDefaultInputDeviceID = 0;
+
+PaHostSoundControl asioDriverInfo = {0};
+
+#ifdef MAC 
+static bool swap = true;
+#elif WINDOWS   
+static bool swap = false;
+#endif
+
+// Prototypes
+void bufferSwitch(long index, ASIOBool processNow);
+ASIOTime *bufferSwitchTimeInfo(ASIOTime *timeInfo, long index, ASIOBool processNow);
+void sampleRateChanged(ASIOSampleRate sRate);
+long asioMessages(long selector, long value, void* message, double* opt);
+static void Pa_StartUsageCalculation( internalPortAudioStream   *past );
+static void Pa_EndUsageCalculation( internalPortAudioStream   *past );
+
+void Pa_ASIO_Convert_Inter_Input(
+        ASIOBufferInfo* nativeBuffer, 
+        void* inputBuffer,
+        short NumInputChannels, 
+        short NumOuputChannels,
+        short framePerBuffer,
+        short hostFrameOffset,
+        short userFrameOffset,
+        ASIOSampleType nativeFormat, 
+        PaSampleFormat paFormat, 
+        PaStreamFlags flags,
+        short index);
+
+void Pa_ASIO_Convert_Inter_Output(
+        ASIOBufferInfo* nativeBuffer, 
+        void* outputBuffer,
+        short NumInputChannels, 
+        short NumOuputChannels,
+        short framePerBuffer,
+        short hostFrameOffset,
+        short userFrameOffset,
+        ASIOSampleType nativeFormat, 
+        PaSampleFormat paFormat, 
+        PaStreamFlags flags,
+        short index);
+
+ void Pa_ASIO_Clear_Output(ASIOBufferInfo* nativeBuffer, 
+        ASIOSampleType nativeFormat,
+        short NumInputChannels, 
+        short NumOuputChannels,
+        int index, 
+        int hostFrameOffset, 
+        int frames);
+
+void Pa_ASIO_Callback_Input(long index);
+void Pa_ASIO_Callback_Output(long index, long framePerBuffer);
+void Pa_ASIO_Callback_End();
+void Pa_ASIO_Clear_User_Buffers();
+
+// Some external references
+extern AsioDrivers* asioDrivers ;
+bool loadAsioDriver(char *name);
+unsigned long get_sys_reference_time();
+
+
+// For callback debugging on Mac : to be removed in the final version
+
+/*
+#define MidiSharePPC_68k
+#include <MidiShare.h>
+
+void SendVal (long val)
+{
+        MidiEvPtr ev = MidiNewEv(typeTempo);
+        if (ev) {
+                Tempo(ev) = val;
+                MidiSendIm(0,ev);
+        }
+}
+
+void MidiPrintText ( char * s)
+{
+        MidiEvPtr e;
+        long c = 0;
+
+        if (e = MidiNewEv(typeTextual)){
+                for (c = 0 ; *s ; s++,c++) MidiAddField (e ,*s);
+                MidiSendIm (0, e);
+        }
+}
+*/
+
+
+/************************************************************************************/
+/****************** Macro  ************************************************************/
+/************************************************************************************/
+
+#define SwapLong(v) ((((v)>>24)&0xFF)|(((v)>>8)&0xFF00)|(((v)&0xFF00)<<8)|(((v)&0xFF)<<24)) ;   
+#define SwapShort(v) ((((v)>>8)&0xFF)|(((v)&0xFF)<<8)) ;        
+
+#define ClipShort(v) (((v)<MIN_INT16)?MIN_INT16:(((v)>MAX_INT16)?MAX_INT16:(v)))
+#define ClipChar(v) (((v)<MIN_INT8)?MIN_INT8:(((v)>MAX_INT8)?MAX_INT8:(v)))
+#define ClipFloat(v) (((v)<-1.0f)?-1.0f:(((v)>1.0f)?1.0f:(v)))
+
+#ifndef min
+#define min(a,b) ((a)<(b)?(a):(b))
+#endif
+
+#ifndef max
+#define max(a,b) ((a)>=(b)?(a):(b))
+#endif
+
+
+// Utilities for alignement buffer size computation
+int PGCD (int a, int b) {return (b == 0) ? a : PGCD (b,a%b);}
+int PPCM (int a, int b) {return (a*b) / PGCD (a,b);}
+
+// Takes the size of host buffer and user buffer : returns the number of frames needed for buffer alignement
+int Pa_ASIO_CalcFrameShift (int M, int N)
+{
+        int res = 0;
+        for (int i = M; i < PPCM (M,N) ; i+=M) { res = max (res, i%N); }
+        return res;
+}
+
+// We have the following relation :
+// Pa_ASIO_CalcFrameShift (M,N) + M = Pa_ASIO_CalcFrameShift (N,M) + N
+                        
+/* ASIO sample type to PortAudio sample type conversion */
+static PaSampleFormat Pa_ASIO_Convert_SampleFormat(ASIOSampleType type) 
+{
+        switch (type) {
+        
+                case ASIOSTInt16MSB:
+                case ASIOSTInt16LSB:
+                case ASIOSTInt32MSB16:
+                case ASIOSTInt32LSB16:
+                        return paInt16;
+                
+                case ASIOSTFloat32MSB:
+                case ASIOSTFloat32LSB:
+                case ASIOSTFloat64MSB:
+                case ASIOSTFloat64LSB:
+                        return paFloat32;
+                
+                case ASIOSTInt32MSB:
+                case ASIOSTInt32LSB:
+                case ASIOSTInt32MSB18:          
+                case ASIOSTInt32MSB20:          
+                case ASIOSTInt32MSB24:          
+                case ASIOSTInt32LSB18:          
+                case ASIOSTInt32LSB20:          
+                case ASIOSTInt32LSB24:          
+                        return paInt32;
+                        
+                case ASIOSTInt24MSB:
+                case ASIOSTInt24LSB:
+                        return paInt24;
+                        
+                default:
+                        return paCustomFormat;
+        }
+}
+
+/* Allocate ASIO buffers, initialise channels */
+static ASIOError Pa_ASIO_CreateBuffers (PaHostSoundControl *asioDriverInfo, long InputChannels,
+                                                                          long OutputChannels, long framesPerBuffer)
+{
+        ASIOError  err;
+        int i;
+        
+        ASIOBufferInfo *info = asioDriverInfo->bufferInfos;
+        
+        // Check parameters
+        if ((InputChannels > kMaxInputChannels) || (OutputChannels > kMaxInputChannels)) return ASE_InvalidParameter;
+        
+        for(i = 0; i < InputChannels; i++, info++){
+                info->isInput = ASIOTrue;
+                info->channelNum = i;
+                info->buffers[0] = info->buffers[1] = 0;
+        }
+        
+        for(i = 0; i < OutputChannels; i++, info++){
+                info->isInput = ASIOFalse;
+                info->channelNum = i;
+                info->buffers[0] = info->buffers[1] = 0;
+        }
+        
+        // Set up the asioCallback structure and create the ASIO data buffer
+        asioDriverInfo->pahsc_asioCallbacks.bufferSwitch = &bufferSwitch;
+        asioDriverInfo->pahsc_asioCallbacks.sampleRateDidChange = &sampleRateChanged;
+        asioDriverInfo->pahsc_asioCallbacks.asioMessage = &asioMessages;
+        asioDriverInfo->pahsc_asioCallbacks.bufferSwitchTimeInfo = &bufferSwitchTimeInfo;
+        
+        DBUG(("PortAudio : ASIOCreateBuffers with size = %ld \n", framesPerBuffer));
+     
+        err =  ASIOCreateBuffers( asioDriverInfo->bufferInfos, InputChannels+OutputChannels,
+                                  framesPerBuffer, &asioDriverInfo->pahsc_asioCallbacks);
+        if (err != ASE_OK) return err;
+        
+        // Initialise buffers
+        for (i = 0; i < InputChannels + OutputChannels; i++)
+        {
+                asioDriverInfo->pahsc_channelInfos[i].channel = asioDriverInfo->bufferInfos[i].channelNum;
+                asioDriverInfo->pahsc_channelInfos[i].isInput = asioDriverInfo->bufferInfos[i].isInput;
+                err = ASIOGetChannelInfo(&asioDriverInfo->pahsc_channelInfos[i]);
+                if (err != ASE_OK) break;
+        }
+
+        err = ASIOGetLatencies(&asioDriverInfo->pahsc_inputLatency, &asioDriverInfo->pahsc_outputLatency);
+        
+        DBUG(("PortAudio : InputLatency = %ld latency = %ld msec \n", 
+                asioDriverInfo->pahsc_inputLatency,  
+                (long)((asioDriverInfo->pahsc_inputLatency*1000)/ asioDriverInfo->past->past_SampleRate)));
+        DBUG(("PortAudio : OuputLatency = %ld latency = %ld msec \n", 
+                asioDriverInfo->pahsc_outputLatency,
+                (long)((asioDriverInfo->pahsc_outputLatency*1000)/ asioDriverInfo->past->past_SampleRate)));
+        
+        return err;
+}
+
+
+/*
+ Query ASIO driver info :
+ First we get all available ASIO drivers located in the ASIO folder,
+ then try to load each one. For each loaded driver, get all needed informations.
+*/
+static PaError Pa_ASIO_QueryDeviceInfo( internalPortAudioDevice * ipad )
+{
+
+#define NUM_STANDARDSAMPLINGRATES   3   /* 11.025, 22.05, 44.1 */
+#define NUM_CUSTOMSAMPLINGRATES     9   /* must be the same number of elements as in the array below */
+#define MAX_NUMSAMPLINGRATES  (NUM_STANDARDSAMPLINGRATES+NUM_CUSTOMSAMPLINGRATES)
+
+        ASIOSampleRate possibleSampleRates[] 
+                = {8000.0, 9600.0, 11025.0, 12000.0, 16000.0, 22050.0, 24000.0, 32000.0, 44100.0, 48000.0, 88200.0, 96000.0};
+                
+        ASIOChannelInfo channelInfos;
+        long InputChannels,OutputChannels;
+        double *sampleRates;
+        char* names[PA_MAX_DEVICE_INFO] ;
+        PaDeviceInfo *dev;
+        int           i;
+        int           numDrivers;
+               ASIOError     asioError;
+        
+        /* Allocate names */
+        for (i = 0 ; i < PA_MAX_DEVICE_INFO ; i++) names[i] = (char*)PaHost_AllocateFastMemory(32);
+        
+        /* MUST BE CHECKED : to force fragments loading on Mac */
+        loadAsioDriver("dummy");
+        
+        /* Get names of all available ASIO drivers */
+        asioDrivers->getDriverNames(names,PA_MAX_DEVICE_INFO);
+        
+        /* Check all available ASIO drivers */
+#if MAC
+        numDrivers = asioDrivers->getNumFragments();
+#elif WINDOWS
+        numDrivers = asioDrivers->asioGetNumDev();
+#endif
+        DBUG(("PaASIO_QueryDeviceInfo: numDrivers = %d\n", numDrivers ));
+
+        for (int driver = 0 ; driver < numDrivers ; driver++)
+        {
+
+            #if WINDOWS
+                    asioDriverInfo.pahsc_driverInfo.asioVersion = 2; // FIXME - is this right? PLB
+                    asioDriverInfo.pahsc_driverInfo.sysRef = GetDesktopWindow(); // FIXME - is this right? PLB
+            #endif
+  
+            /* If the driver can be loaded : */
+            if ( !loadAsioDriver(names[driver]) )
+                       {
+                               DBUG(("PaASIO_QueryDeviceInfo could not loadAsioDriver %s\n", names[driver]));
+                       }
+                       else if( (asioError = ASIOInit(&asioDriverInfo.pahsc_driverInfo)) != ASE_OK )
+                       {
+                               DBUG(("PaASIO_QueryDeviceInfo: ASIOInit returned %d for %s\n", asioError, names[driver]));
+                       }
+                       else if( (ASIOGetChannels(&InputChannels, &OutputChannels) != ASE_OK))
+                       {
+                               DBUG(("PaASIO_QueryDeviceInfo could not ASIOGetChannels for %s\n", names[driver]));
+                       }
+                       else
+                       {
+                                       
+                    /* Gets the name */
+                    dev = &(ipad[driver].pad_Info);
+                    dev->name = names[driver];
+                    names[driver] = 0;
+                    
+                    /* Gets Input and Output channels number */
+                    dev->maxInputChannels = InputChannels;
+                    dev->maxOutputChannels = OutputChannels;
+                    
+                    DBUG(("PaASIO_QueryDeviceInfo: InputChannels = %d\n", InputChannels ));
+                    DBUG(("PaASIO_QueryDeviceInfo: OutputChannels = %d\n", OutputChannels ));
+                    
+                    /* Make room in case device supports all rates. */
+                    sampleRates = (double*)PaHost_AllocateFastMemory( MAX_NUMSAMPLINGRATES * sizeof(double));
+                    dev->sampleRates = sampleRates;
+                    dev->numSampleRates = 0;
+                    
+                    /* Loop through the possible sampling rates and check each to see if the device supports it. */
+                    for (int index = 0; index < MAX_NUMSAMPLINGRATES; index++) {
+                            if (ASIOCanSampleRate(possibleSampleRates[index]) != ASE_NoClock) {
+                                    DBUG(("PortAudio : possible sample rate = %d\n", (long)possibleSampleRates[index]));
+                                    dev->numSampleRates+=1;
+                                    *sampleRates = possibleSampleRates[index];
+                                    sampleRates++;
+                            }
+                    }
+                    
+                    /* We assume that all channels have the same SampleType, so check the first */
+                    channelInfos.channel = 0;
+                    channelInfos.isInput = 1;
+                    ASIOGetChannelInfo(&channelInfos);
+                    
+                    dev->nativeSampleFormats = Pa_ASIO_Convert_SampleFormat(channelInfos.type);
+                    
+                    /* unload the driver */
+                    ASIODisposeBuffers();
+                    ASIOExit();
+                    
+                    sNumDevices++;
+                }
+        }       
+        
+        /* free only unused names */
+        for (i = 0 ; i < PA_MAX_DEVICE_INFO ; i++) if (names[i]) PaHost_FreeFastMemory(names[i],32);
+        
+        return paNoError;
+}
+
+//----------------------------------------------------------------------------------
+// TAKEN FROM THE ASIO SDK: 
+void sampleRateChanged(ASIOSampleRate sRate)
+{
+        // do whatever you need to do if the sample rate changed
+        // usually this only happens during external sync.
+        // Audio processing is not stopped by the driver, actual sample rate
+        // might not have even changed, maybe only the sample rate status of an
+        // AES/EBU or S/PDIF digital input at the audio device.
+        // You might have to update time/sample related conversion routines, etc.
+}
+
+//----------------------------------------------------------------------------------
+// TAKEN FROM THE ASIO SDK: 
+long asioMessages(long selector, long value, void* message, double* opt)
+{
+        // currently the parameters "value", "message" and "opt" are not used.
+        long ret = 0;
+        switch(selector)
+        {
+                case kAsioSelectorSupported:
+                        if(value == kAsioResetRequest
+                        || value == kAsioEngineVersion
+                        || value == kAsioResyncRequest
+                        || value == kAsioLatenciesChanged
+                        // the following three were added for ASIO 2.0, you don't necessarily have to support them
+                        || value == kAsioSupportsTimeInfo
+                        || value == kAsioSupportsTimeCode
+                        || value == kAsioSupportsInputMonitor)
+                                ret = 1L;
+                        break;
+                        
+                case kAsioBufferSizeChange:
+                        //printf("kAsioBufferSizeChange \n");
+                        break;
+                        
+                case kAsioResetRequest:
+                        // defer the task and perform the reset of the driver during the next "safe" situation
+                        // You cannot reset the driver right now, as this code is called from the driver.
+                        // Reset the driver is done by completely destruct is. I.e. ASIOStop(), ASIODisposeBuffers(), Destruction
+                        // Afterwards you initialize the driver again.
+                        asioDriverInfo.stopped;  // In this sample the processing will just stop
+                        ret = 1L;
+                        break;
+                case kAsioResyncRequest:
+                        // This informs the application, that the driver encountered some non fatal data loss.
+                        // It is used for synchronization purposes of different media.
+                        // Added mainly to work around the Win16Mutex problems in Windows 95/98 with the
+                        // Windows Multimedia system, which could loose data because the Mutex was hold too long
+                        // by another thread.
+                        // However a driver can issue it in other situations, too.
+                        ret = 1L;
+                        break;
+                case kAsioLatenciesChanged:
+                        // This will inform the host application that the drivers were latencies changed.
+                        // Beware, it this does not mean that the buffer sizes have changed!
+                        // You might need to update internal delay data.
+                        ret = 1L;
+                        //printf("kAsioLatenciesChanged \n");
+                        break;
+                case kAsioEngineVersion:
+                        // return the supported ASIO version of the host application
+                        // If a host applications does not implement this selector, ASIO 1.0 is assumed
+                        // by the driver
+                        ret = 2L;
+                        break;
+                case kAsioSupportsTimeInfo:
+                        // informs the driver wether the asioCallbacks.bufferSwitchTimeInfo() callback
+                        // is supported.
+                        // For compatibility with ASIO 1.0 drivers the host application should always support
+                        // the "old" bufferSwitch method, too.
+                        ret = 1;
+                        break;
+                case kAsioSupportsTimeCode:
+                        // informs the driver wether application is interested in time code info.
+                        // If an application does not need to know about time code, the driver has less work
+                        // to do.
+                        ret = 0;
+                        break;
+        }
+        return ret;
+}
+
+
+//----------------------------------------------------------------------------------
+// conversion from 64 bit ASIOSample/ASIOTimeStamp to double float
+#if NATIVE_INT64
+        #define ASIO64toDouble(a)  (a)
+#else
+        const double twoRaisedTo32 = 4294967296.;
+        #define ASIO64toDouble(a)  ((a).lo + (a).hi * twoRaisedTo32)
+#endif
+
+
+ASIOTime *bufferSwitchTimeInfo(ASIOTime *timeInfo, long index, ASIOBool processNow)
+{       
+        // the actual processing callback.
+        // Beware that this is normally in a seperate thread, hence be sure that you take care
+        // about thread synchronization. This is omitted here for simplicity.
+        
+        static processedSamples = 0;
+        int  result = 0;
+        
+        // store the timeInfo for later use
+        asioDriverInfo.tInfo = *timeInfo;
+
+        // get the time stamp of the buffer, not necessary if no
+        // synchronization to other media is required
+        
+        if (timeInfo->timeInfo.flags & kSystemTimeValid)
+                asioDriverInfo.nanoSeconds = ASIO64toDouble(timeInfo->timeInfo.systemTime);
+        else
+                asioDriverInfo.nanoSeconds = 0;
+
+        if (timeInfo->timeInfo.flags & kSamplePositionValid)
+                asioDriverInfo.samples = ASIO64toDouble(timeInfo->timeInfo.samplePosition);
+        else
+                asioDriverInfo.samples = 0;
+
+        if (timeInfo->timeCode.flags & kTcValid)
+                asioDriverInfo.tcSamples = ASIO64toDouble(timeInfo->timeCode.timeCodeSamples);
+        else
+                asioDriverInfo.tcSamples = 0;
+
+        // get the system reference time
+        asioDriverInfo.sysRefTime = get_sys_reference_time();
+
+#if 0
+        // a few debug messages for the Windows device driver developer
+        // tells you the time when driver got its interrupt and the delay until the app receives
+        // the event notification.
+        static double last_samples = 0;
+        char tmp[128];
+        sprintf (tmp, "diff: %d / %d ms / %d ms / %d samples                 \n", asioDriverInfo.sysRefTime - (long)(asioDriverInfo.nanoSeconds / 1000000.0), asioDriverInfo.sysRefTime, (long)(asioDriverInfo.nanoSeconds / 1000000.0), (long)(asioDriverInfo.samples - last_samples));
+        OutputDebugString (tmp);
+        last_samples = asioDriverInfo.samples;
+#endif
+
+        // To avoid the callback accessing a desallocated stream
+        if( asioDriverInfo.past == NULL) return 0L;
+
+        // Keep sample position
+        asioDriverInfo.pahsc_NumFramesDone = timeInfo->timeInfo.samplePosition.lo;
+
+        /*  Has a user callback returned '1' to indicate finished at the last ASIO callback? */
+        if( asioDriverInfo.past->past_StopSoon ) {
+        
+                Pa_ASIO_Clear_Output(asioDriverInfo.bufferInfos, 
+                        asioDriverInfo.pahsc_channelInfos[0].type,
+                        asioDriverInfo.pahsc_NumInputChannels ,
+                        asioDriverInfo.pahsc_NumOutputChannels,
+                        index, 
+                        0, 
+                        asioDriverInfo.past_FramesPerHostBuffer);
+                
+                asioDriverInfo.past->past_IsActive = 0; 
+                
+                // Finally if the driver supports the ASIOOutputReady() optimization, do it here, all data are in place
+                if (asioDriverInfo.pahsc_postOutput) ASIOOutputReady();
+       
+        }else {
+                
+                /* CPU usage */
+                Pa_StartUsageCalculation(asioDriverInfo.past);
+                
+                Pa_ASIO_Callback_Input(index);
+                         
+                // Finally if the driver supports the ASIOOutputReady() optimization, do it here, all data are in place
+                if (asioDriverInfo.pahsc_postOutput) ASIOOutputReady();
+                
+                Pa_ASIO_Callback_End();
+                        
+                /* CPU usage */
+                Pa_EndUsageCalculation(asioDriverInfo.past);
+        }
+        
+        return 0L;
+}
+
+
+//----------------------------------------------------------------------------------
+void bufferSwitch(long index, ASIOBool processNow)
+{       
+        // the actual processing callback.
+        // Beware that this is normally in a seperate thread, hence be sure that you take care
+        // about thread synchronization. This is omitted here for simplicity.
+
+        // as this is a "back door" into the bufferSwitchTimeInfo a timeInfo needs to be created
+        // though it will only set the timeInfo.samplePosition and timeInfo.systemTime fields and the according flags
+        
+        ASIOTime  timeInfo;
+        memset (&timeInfo, 0, sizeof (timeInfo));
+
+        // get the time stamp of the buffer, not necessary if no
+        // synchronization to other media is required
+        if(ASIOGetSamplePosition(&timeInfo.timeInfo.samplePosition, &timeInfo.timeInfo.systemTime) == ASE_OK)
+                timeInfo.timeInfo.flags = kSystemTimeValid | kSamplePositionValid;
+                
+        // Call the real callback
+        bufferSwitchTimeInfo (&timeInfo, index, processNow);
+}
+
+//----------------------------------------------------------------------------------
+unsigned long get_sys_reference_time()
+{       
+        // get the system reference time
+        #if WINDOWS
+                return timeGetTime();
+        #elif MAC
+                static const double twoRaisedTo32 = 4294967296.;
+                UnsignedWide ys;
+                Microseconds(&ys);
+                double r = ((double)ys.hi * twoRaisedTo32 + (double)ys.lo);
+                return (unsigned long)(r / 1000.);
+        #endif
+}
+
+
+/*************************************************************
+** 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))
+inline 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;
+}
+
+// TO BE COMPLETED WITH ALL SUPPORTED PA SAMPLE TYPES
+
+//-------------------------------------------------------------------------------------------------------------------------------------------------------
+ void Input_Int16_Flot32 (ASIOBufferInfo* nativeBuffer, float *inBufPtr, int framePerBuffer, int NumInputChannels, int index, int hostFrameOffset,int userFrameOffset, bool swap)
+{
+        long temp;
+        int i,j;
+        
+        for( j=0; j<NumInputChannels; j++ ) {
+                short *asioBufPtr = &((short*)nativeBuffer[j].buffers[index])[hostFrameOffset];
+                float *userBufPtr = &inBufPtr[j+(userFrameOffset*NumInputChannels)];
+                for (i= 0; i < framePerBuffer; i++)
+                { 
+                        temp = asioBufPtr[i];
+                        if (swap) temp = SwapShort(temp);
+                        *userBufPtr = (1.0f / MAX_INT16_FP) * temp;
+                        userBufPtr += NumInputChannels;
+                }
+        }
+        
+}
+
+//-------------------------------------------------------------------------------------------------------------------------------------------------------
+ void Input_Int32_Flot32 (ASIOBufferInfo* nativeBuffer, float *inBufPtr, int framePerBuffer, int NumInputChannels, int index, int hostFrameOffset,int userFrameOffset,bool swap)
+{
+        long temp;
+        int i,j;
+        
+        for( j=0; j<NumInputChannels; j++ ) {
+                long *asioBufPtr = &((long*)nativeBuffer[j].buffers[index])[hostFrameOffset];
+                float *userBufPtr = &inBufPtr[j+(userFrameOffset*NumInputChannels)];
+                for (i= 0; i < framePerBuffer; i++)
+                { 
+                        temp = asioBufPtr[i];
+                        if (swap) temp = SwapLong(temp);
+                        *userBufPtr = (1.0f / MAX_INT32_FP) * temp;
+                        userBufPtr += NumInputChannels;
+                }
+        }
+}
+
+//-------------------------------------------------------------------------------------------------------------------------------------------------------
+// MUST BE TESTED
+ void Input_Float32_Flot32 (ASIOBufferInfo* nativeBuffer, float *inBufPtr, int framePerBuffer, int NumInputChannels, int index, int hostFrameOffset,int userFrameOffset,bool swap)
+{
+        long temp;
+        int i,j;
+        
+        for( j=0; j<NumInputChannels; j++ ) {
+                float *asioBufPtr = &((float*)nativeBuffer[j].buffers[index])[hostFrameOffset];
+                float *userBufPtr = &inBufPtr[j+(userFrameOffset*NumInputChannels)];
+                for (i= 0; i < framePerBuffer; i++)
+                { 
+                        temp = (long)asioBufPtr[i];
+                        if (swap) temp = SwapLong(temp);
+                        *userBufPtr = (float)temp;
+                        userBufPtr += NumInputChannels;
+                }
+        }
+}
+
+//-------------------------------------------------------------------------------------------------------------------------------------------------------
+ void Input_Int16_Int32 (ASIOBufferInfo* nativeBuffer, long *inBufPtr, int framePerBuffer, int NumInputChannels, int index, int hostFrameOffset,int userFrameOffset,bool swap)
+{
+        long temp;
+        int i,j;
+        
+        for( j=0; j<NumInputChannels; j++ ) {
+                short *asioBufPtr = &((short*)nativeBuffer[j].buffers[index])[hostFrameOffset];
+                long *userBufPtr = &inBufPtr[j+(userFrameOffset*NumInputChannels)];
+                for (i= 0; i < framePerBuffer; i++)
+                { 
+                        temp = asioBufPtr[i];
+                        if (swap) temp = SwapShort(temp);
+                        *userBufPtr = temp<<16;
+                        userBufPtr += NumInputChannels;
+                }
+        }
+}
+
+//-------------------------------------------------------------------------------------------------------------------------------------------------------
+ void Input_Int32_Int32 (ASIOBufferInfo* nativeBuffer, long *inBufPtr, int framePerBuffer, int NumInputChannels, int index, int hostFrameOffset,int userFrameOffset,bool swap)
+{
+        long temp;
+        int i,j;
+        
+        for( j=0; j<NumInputChannels; j++ ) {
+                long *asioBufPtr = &((long*)nativeBuffer[j].buffers[index])[hostFrameOffset];
+                long *userBufPtr = &inBufPtr[j+(userFrameOffset*NumInputChannels)];
+                for (i= 0; i < framePerBuffer; i++)
+                { 
+                        temp = asioBufPtr[i];
+                        if (swap) temp = SwapLong(temp);
+                        *userBufPtr = temp;
+                        userBufPtr += NumInputChannels;
+                }
+        }
+}
+
+//-------------------------------------------------------------------------------------------------------------------------------------------------------
+// MUST BE TESTED
+ void Input_Float32_Int32 (ASIOBufferInfo* nativeBuffer, long *inBufPtr, int framePerBuffer, int NumInputChannels, int index, int hostFrameOffset,int userFrameOffset,bool swap)
+{
+        long temp;
+        int i,j;
+
+        for( j=0; j<NumInputChannels; j++ ) {
+                float *asioBufPtr = &((float*)nativeBuffer[j].buffers[index])[hostFrameOffset];
+                long *userBufPtr = &inBufPtr[j+(userFrameOffset*NumInputChannels)];
+                for (i= 0; i < framePerBuffer; i++)
+                { 
+                        temp = (long) asioBufPtr[i];
+                        if (swap) temp = SwapLong(temp);
+                        *userBufPtr = (long)((float)temp * MAX_INT32_FP); // Is temp a value between -1.0 and 1.0 ??
+                        userBufPtr += NumInputChannels;
+                }
+        }
+}
+
+
+//-------------------------------------------------------------------------------------------------------------------------------------------------------
+ void Input_Int16_Int16 (ASIOBufferInfo* nativeBuffer, short *inBufPtr, int framePerBuffer, int NumInputChannels, int index, int hostFrameOffset,int userFrameOffset,bool swap)
+{
+        long temp;
+        int i,j;
+
+        for( j=0; j<NumInputChannels; j++ ) {
+                short *asioBufPtr = &((short*)nativeBuffer[j].buffers[index])[hostFrameOffset];
+                short *userBufPtr = &inBufPtr[j+(userFrameOffset*NumInputChannels)];
+                for (i= 0; i < framePerBuffer; i++)
+                { 
+                        temp = asioBufPtr[i];
+                        if (swap) temp = SwapShort(temp);
+                        *userBufPtr = temp;
+                        userBufPtr += NumInputChannels;
+                }
+        }
+}
+ //-------------------------------------------------------------------------------------------------------------------------------------------------------
+ void Input_Int32_Int16 (ASIOBufferInfo* nativeBuffer, short *inBufPtr, int framePerBuffer, int NumInputChannels, int index, int hostFrameOffset, int userFrameOffset,uint32 flags,bool swap)
+{
+        long temp;
+        int i,j;
+        
+        if( flags & paDitherOff )
+        {
+                for( j=0; j<NumInputChannels; j++ ) {
+                        long *asioBufPtr = &((long*)nativeBuffer[j].buffers[index])[hostFrameOffset];
+                        short *userBufPtr = &inBufPtr[j+(userFrameOffset*NumInputChannels)];
+                        for (i= 0; i < framePerBuffer; i++)
+                        { 
+                                temp = asioBufPtr[i];
+                                if (swap) temp = SwapLong(temp);
+                                *userBufPtr = temp>>16;
+                                userBufPtr += NumInputChannels;
+                        }
+                }
+        }
+        else
+        {
+                for( j=0; j<NumInputChannels; j++ ) {
+                        long *asioBufPtr = &((long*)nativeBuffer[j].buffers[index])[hostFrameOffset];
+                        short *userBufPtr = &inBufPtr[j+(userFrameOffset*NumInputChannels)];
+                        for (i= 0; i < framePerBuffer; i++)
+                        { 
+                                temp = asioBufPtr[i];
+                                if (swap) temp = SwapLong(temp);
+                                temp = (temp >> 1) + Pa_TriangularDither();
+                                temp = temp >> 15;
+                                temp = (short) ClipShort(temp);
+                                *userBufPtr = temp;
+                                userBufPtr += NumInputChannels;
+                        }
+                }
+        
+        }
+}
+
+//-------------------------------------------------------------------------------------------------------------------------------------------------------
+// MUST BE TESTED
+ void Input_Float32_Int16 (ASIOBufferInfo* nativeBuffer, short *inBufPtr, int framePerBuffer, int NumInputChannels, int index, int hostFrameOffset,int userFrameOffset,uint32 flags,bool swap)
+{
+        long temp;
+        int i,j;
+        
+        if( flags & paDitherOff )
+        {
+                for( j=0; j<NumInputChannels; j++ ) {
+                        float *asioBufPtr = &((float*)nativeBuffer[j].buffers[index])[hostFrameOffset];
+                        short *userBufPtr = &inBufPtr[j+(userFrameOffset*NumInputChannels)];
+                        for (i= 0; i < framePerBuffer; i++)
+                        { 
+                                temp = (long)asioBufPtr[i];
+                                if (swap) temp = SwapLong(temp);
+                                *userBufPtr = (short)((float)temp * MAX_INT16_FP); // Is temp a value between -1.0 and 1.0 ??
+                                userBufPtr += NumInputChannels;
+                        }
+                }
+        }
+        else
+        {
+                for( j=0; j<NumInputChannels; j++ ) {
+                        float *asioBufPtr = &((float*)nativeBuffer[j].buffers[index])[hostFrameOffset];
+                        short *userBufPtr = &inBufPtr[j+(userFrameOffset*NumInputChannels)];
+                        for (i= 0; i < framePerBuffer; i++)
+                        { 
+                                float dither  = Pa_TriangularDither()*DITHER_SCALE;
+                                temp = (long)asioBufPtr[i];
+                                if (swap) temp = SwapLong(temp);
+                                temp = (short)(((float)temp * MAX_INT16_FP) + dither);
+                                temp = ClipShort(temp);
+                                *userBufPtr = temp;
+                                userBufPtr += NumInputChannels;
+                        }
+                }
+        }
+}
+
+//-------------------------------------------------------------------------------------------------------------------------------------------------------
+ void Input_Int16_Int8 (ASIOBufferInfo* nativeBuffer, char *inBufPtr, int framePerBuffer, int NumInputChannels, int index, int hostFrameOffset,int userFrameOffset, uint32 flags,bool swap)
+{
+        long temp;
+        int i,j;
+        
+        if( flags & paDitherOff )
+        {
+                for( j=0; j<NumInputChannels; j++ ) {
+                        short *asioBufPtr = &((short*)nativeBuffer[j].buffers[index])[hostFrameOffset];
+                        char *userBufPtr = &inBufPtr[j+(userFrameOffset*NumInputChannels)];
+                        for (i= 0; i < framePerBuffer; i++)
+                        { 
+                                temp = asioBufPtr[i];
+                                if (swap) temp = SwapShort(temp);
+                                *userBufPtr = (char)(temp>>8);
+                                userBufPtr += NumInputChannels;
+                        }
+                }
+        }
+        else
+        {
+                for( j=0; j<NumInputChannels; j++ ) {
+                        short *asioBufPtr = &((short*)nativeBuffer[j].buffers[index])[hostFrameOffset];
+                        char *userBufPtr = &inBufPtr[j+(userFrameOffset*NumInputChannels)];
+                        for (i= 0; i < framePerBuffer; i++)
+                        { 
+                                temp = asioBufPtr[i];
+                                if (swap) temp = SwapShort(temp);
+                                temp += Pa_TriangularDither() >> 8;
+                                temp = ClipShort(temp);
+                                *userBufPtr = (char)(temp>>8);
+                                userBufPtr += NumInputChannels;
+                        }
+                }
+        }
+}
+
+//-------------------------------------------------------------------------------------------------------------------------------------------------------
+ void Input_Int32_Int8 (ASIOBufferInfo* nativeBuffer, char *inBufPtr, int framePerBuffer, int NumInputChannels, int index, int hostFrameOffset, int userFrameOffset, uint32 flags,bool swap)
+{
+        long temp;
+        int i,j;
+        
+        if( flags & paDitherOff )
+        {
+                for( j=0; j<NumInputChannels; j++ ) {
+                        long *asioBufPtr = &((long*)nativeBuffer[j].buffers[index])[hostFrameOffset];
+                        char *userBufPtr = &inBufPtr[j+(userFrameOffset*NumInputChannels)];
+                        for (i= 0; i < framePerBuffer; i++)
+                        { 
+                                temp = asioBufPtr[i];
+                                if (swap) temp = SwapLong(temp);
+                                *userBufPtr = (char)(temp>>24);
+                                userBufPtr += NumInputChannels;
+                        }
+                }
+        }
+        else
+        {
+                for( j=0; j<NumInputChannels; j++ ) {
+                        long *asioBufPtr = &((long*)nativeBuffer[j].buffers[index])[hostFrameOffset];
+                        char *userBufPtr = &inBufPtr[j+(userFrameOffset*NumInputChannels)];
+                        for (i= 0; i < framePerBuffer; i++)
+                        { 
+                                temp = asioBufPtr[i];
+                                if (swap) temp = SwapLong(temp);
+                                temp = temp>>16;  // Shift to get a 16 bit value, then use the 16 bits to 8 bits code (MUST BE CHECHED)
+                                temp += Pa_TriangularDither() >> 8;
+                                temp = ClipShort(temp);
+                                *userBufPtr = (char)(temp >> 8);
+                                userBufPtr += NumInputChannels;
+                        }
+                }
+        }
+}
+
+//-------------------------------------------------------------------------------------------------------------------------------------------------------
+// MUST BE TESTED
+
+ void Input_Float32_Int8 (ASIOBufferInfo* nativeBuffer, char *inBufPtr, int framePerBuffer, int NumInputChannels, int index, int hostFrameOffset,int userFrameOffset,  uint32 flags,bool swap)
+{
+        long temp;
+        int i,j;
+        
+        if( flags & paDitherOff )
+        {
+                for( j=0; j<NumInputChannels; j++ ) {
+                        long *asioBufPtr = &((long*)nativeBuffer[j].buffers[index])[hostFrameOffset];
+                        char *userBufPtr = &inBufPtr[j+(userFrameOffset*NumInputChannels)];
+                        for (i= 0; i < framePerBuffer; i++)
+                        { 
+                                temp = asioBufPtr[i];
+                                if (swap) temp = SwapLong(temp);
+                                *userBufPtr = (char)((float)temp*MAX_INT8_FP); // Is temp a value between -1.0 and 1.0 ??
+                                userBufPtr += NumInputChannels;
+                        }
+                }
+        }
+        else
+        {
+                for( j=0; j<NumInputChannels; j++ ) {
+                        long *asioBufPtr = &((long*)nativeBuffer[j].buffers[index])[hostFrameOffset];
+                        char *userBufPtr = &inBufPtr[j+(userFrameOffset*NumInputChannels)];
+                        for (i= 0; i < framePerBuffer; i++)
+                        { 
+                                float dither  = Pa_TriangularDither()*DITHER_SCALE;
+                                temp = asioBufPtr[i];
+                                if (swap) temp = SwapLong(temp);
+                                temp = (char)(((float)temp * MAX_INT8_FP) + dither);
+                                temp = ClipChar(temp);
+                                *userBufPtr = temp ;
+                                userBufPtr += NumInputChannels;
+                        }
+                }
+        }
+}
+
+//-------------------------------------------------------------------------------------------------------------------------------------------------------
+ void Input_Int16_IntU8 (ASIOBufferInfo* nativeBuffer, unsigned char *inBufPtr, int framePerBuffer, int NumInputChannels, int index, int hostFrameOffset,int userFrameOffset, uint32 flags,bool swap)
+{
+        long temp;
+        int i,j;
+
+        if( flags & paDitherOff )
+        {
+                for( j=0; j<NumInputChannels; j++ ) {
+                        short *asioBufPtr = &((short*)nativeBuffer[j].buffers[index])[hostFrameOffset];
+                        unsigned char *userBufPtr = &inBufPtr[j+(userFrameOffset*NumInputChannels)];
+                        for (i= 0; i < framePerBuffer; i++)
+                        { 
+                                temp = asioBufPtr[i];
+                                if (swap) temp = SwapShort(temp);
+                                *userBufPtr = (unsigned char)((temp>>8) + 0x80); 
+                                userBufPtr += NumInputChannels;
+                        }
+                }
+        }
+        else
+        {
+                for( j=0; j<NumInputChannels; j++ ) {
+                        short *asioBufPtr = &((short*)nativeBuffer[j].buffers[index])[hostFrameOffset];
+                        unsigned char *userBufPtr = &inBufPtr[j+(userFrameOffset*NumInputChannels)];
+                        for (i= 0; i < framePerBuffer; i++)
+                        { 
+                                temp = asioBufPtr[i];
+                                if (swap) temp = SwapShort(temp);
+                                temp += Pa_TriangularDither() >> 8;
+                                temp = ClipShort(temp);
+                                *userBufPtr = (unsigned char)((temp>>8) + 0x80); 
+                                userBufPtr += NumInputChannels;
+                        }
+                }
+        }
+}
+
+//-------------------------------------------------------------------------------------------------------------------------------------------------------
+ void Input_Int32_IntU8 (ASIOBufferInfo* nativeBuffer, unsigned char *inBufPtr, int framePerBuffer, int NumInputChannels, int index, int hostFrameOffset, int userFrameOffset,uint32 flags,bool swap)
+{
+        long temp;
+        int i,j;
+        
+        if( flags & paDitherOff )
+        {
+                for( j=0; j<NumInputChannels; j++ ) {
+                        long *asioBufPtr = &((long*)nativeBuffer[j].buffers[index])[hostFrameOffset];
+                        unsigned char *userBufPtr = &inBufPtr[j+(userFrameOffset*NumInputChannels)];
+                        for (i= 0; i < framePerBuffer; i++)
+                        { 
+                                temp = asioBufPtr[i];
+                                if (swap) temp = SwapLong(temp);
+                                *userBufPtr = (unsigned char)((temp>>24) + 0x80); 
+                                userBufPtr += NumInputChannels;
+                        }
+                }
+        }
+        else
+        {
+                for( j=0; j<NumInputChannels; j++ ) {
+                        long *asioBufPtr = &((long*)nativeBuffer[j].buffers[index])[hostFrameOffset];
+                        unsigned char *userBufPtr = &inBufPtr[j+(userFrameOffset*NumInputChannels)];
+                        for (i= 0; i < framePerBuffer; i++)
+                        { 
+                                temp = asioBufPtr[i];
+                                if (swap) temp = SwapLong(temp);
+                                temp = temp>>16; // Shift to get a 16 bit value, then use the 16 bits to 8 bits code (MUST BE CHECHED)
+                                temp += Pa_TriangularDither() >> 8;
+                                temp = ClipShort(temp);
+                                *userBufPtr = (unsigned char)((temp>>8) + 0x80); 
+                                userBufPtr += NumInputChannels;
+                        }
+                }
+        }
+}
+
+//-------------------------------------------------------------------------------------------------------------------------------------------------------
+// MUST BE TESTED
+
+ void Input_Float32_IntU8 (ASIOBufferInfo* nativeBuffer, unsigned char *inBufPtr, int framePerBuffer, int NumInputChannels, int index, int hostFrameOffset,int userFrameOffset, uint32 flags,bool swap)
+{
+        long temp;
+        int i,j;
+        
+        if( flags & paDitherOff )
+        {
+                for( j=0; j<NumInputChannels; j++ ) {
+                        float *asioBufPtr = &((float*)nativeBuffer[j].buffers[index])[hostFrameOffset];
+                        unsigned char *userBufPtr = &inBufPtr[j+(userFrameOffset*NumInputChannels)];
+                        for (i= 0; i < framePerBuffer; i++)
+                        { 
+                                temp = (float) asioBufPtr[i];
+                                if (swap) temp = SwapLong(temp);
+                                *userBufPtr = (unsigned char)(((float)temp*MAX_INT8_FP) + 0x80);
+                                userBufPtr += NumInputChannels;
+                        }
+                }
+        }
+        else
+        {       
+                for( j=0; j<NumInputChannels; j++ ) {
+                        float *asioBufPtr = &((float*)nativeBuffer[j].buffers[index])[hostFrameOffset];
+                        unsigned char *userBufPtr = &inBufPtr[j+(userFrameOffset*NumInputChannels)];
+                        for (i= 0; i < framePerBuffer; i++)
+                        { 
+                                float dither  = Pa_TriangularDither()*DITHER_SCALE;
+                                temp = (float) asioBufPtr[i];
+                                if (swap) temp = SwapLong(temp);
+                                temp = (char)(((float)temp * MAX_INT8_FP) + dither);
+                                temp = ClipChar(temp);
+                                *userBufPtr =  (unsigned char)(temp + 0x80); 
+                                userBufPtr += NumInputChannels;
+                        }
+                }
+        }
+}
+
+                
+// OUPUT 
+//-------------------------------------------------------------------------------------------------------------------------------------------------------
+ void Output_Float32_Int16 (ASIOBufferInfo* nativeBuffer, float *outBufPtr, int framePerBuffer, int NumInputChannels, int NumOuputChannels, int index, int hostFrameOffset, int userFrameOffset,uint32 flags, bool swap)
+{
+        long temp;
+        int i,j;
+
+        if( flags & paDitherOff )
+                {
+                        if( flags & paClipOff ) /* NOTHING */
+                        {
+                                for( j=0; j<NumOuputChannels; j++ ) {
+                                        short *asioBufPtr = &((short*)nativeBuffer[j+NumInputChannels].buffers[index])[hostFrameOffset];
+                                        float *userBufPtr = &outBufPtr[j+(userFrameOffset*NumOuputChannels)];
+                                        
+                                        for (i= 0; i < framePerBuffer; i++) 
+                                        {
+                                                temp = (short) (*userBufPtr * MAX_INT16_FP);
+                                                if (swap) temp = SwapShort(temp);
+                                                asioBufPtr[i] = temp;
+                                                userBufPtr += NumOuputChannels;
+                                        }
+                                }
+                        }
+                        else /* CLIP */
+                        {
+                                for( j=0; j<NumOuputChannels; j++ ) {
+                                        short *asioBufPtr = &((short*)nativeBuffer[j+NumInputChannels].buffers[index])[hostFrameOffset];
+                                        float *userBufPtr = &outBufPtr[j+(userFrameOffset*NumOuputChannels)];
+                                        
+                                        for (i= 0; i < framePerBuffer; i++) 
+                                        {
+                                                temp = (long) (*userBufPtr * MAX_INT16_FP);
+                                                temp = ClipShort(temp);
+                                                if (swap) temp = SwapShort(temp);
+                                                asioBufPtr[i] = temp;
+                                                userBufPtr += NumOuputChannels;
+                                        }
+                                }
+                        }
+                }
+                else
+                {
+                        /* If you dither then you have to clip because dithering could push the signal out of range! */
+                        for( j=0; j<NumOuputChannels; j++ ) {
+                                        short *asioBufPtr = &((short*)nativeBuffer[j+NumInputChannels].buffers[index])[hostFrameOffset];
+                                        float *userBufPtr = &outBufPtr[j+(userFrameOffset*NumOuputChannels)];
+                                        
+                                        for (i= 0; i < framePerBuffer; i++) 
+                                        {
+                                                float dither = Pa_TriangularDither()*DITHER_SCALE;
+                                                temp = (long) ((*userBufPtr * MAX_INT16_FP) + dither);
+                                                temp = ClipShort(temp);
+                                                if (swap) temp = SwapShort(temp);
+                                                asioBufPtr[i] = temp;
+                                                userBufPtr += NumOuputChannels;
+                                        }
+                                }
+                }
+}
+
+//-------------------------------------------------------------------------------------------------------------------------------------------------------
+ void Output_Float32_Int32 (ASIOBufferInfo* nativeBuffer, float *outBufPtr, int framePerBuffer, int NumInputChannels, int NumOuputChannels, int index, int hostFrameOffset, int userFrameOffset,uint32 flags,bool swap)
+{
+        long temp;
+        int i,j;
+        
+        if( flags & paClipOff )
+        {
+                for (j= 0; j < NumOuputChannels; j++) 
+                {
+                        long *asioBufPtr = &((long*)nativeBuffer[j+NumInputChannels].buffers[index])[hostFrameOffset];
+                        float *userBufPtr = &outBufPtr[j+(userFrameOffset*NumOuputChannels)];
+                        for( i=0; i<framePerBuffer; i++ )
+                        {
+                                temp = (long) (*userBufPtr * MAX_INT32_FP);
+                                if (swap) temp = SwapLong(temp);
+                                asioBufPtr[i] = temp;
+                                userBufPtr += NumOuputChannels;
+                        }
+                }
+        }
+        else // CLIP *
+        {
+                for (j= 0; j < NumOuputChannels; j++) 
+                {
+                        long *asioBufPtr = &((long*)nativeBuffer[j+NumInputChannels].buffers[index])[hostFrameOffset];
+                        float *userBufPtr = &outBufPtr[j+(userFrameOffset*NumOuputChannels)];
+                        for( i=0; i<framePerBuffer; i++ )
+                        {
+                                float temp1 = *userBufPtr;
+                                temp1 = ClipFloat(temp1);
+                                temp = (long) (temp1*MAX_INT32_FP);
+                                if (swap) temp = SwapLong(temp);
+                                asioBufPtr[i] = temp;
+                                userBufPtr += NumOuputChannels;
+                        }
+                }
+        }
+        
+}
+
+
+//-------------------------------------------------------------------------------------------------------------------------------------------------------
+// MUST BE TESTED
+
+ void Output_Float32_Float32 (ASIOBufferInfo* nativeBuffer, float *outBufPtr, int framePerBuffer, int NumInputChannels, int NumOuputChannels, int index, int hostFrameOffset, int userFrameOffset,uint32 flags,bool swap)
+{
+        long temp;
+        int i,j;
+        
+        if( flags & paClipOff )
+        {
+                for (j= 0; j < NumOuputChannels; j++) 
+                {
+                        float *asioBufPtr = &((float*)nativeBuffer[j+NumInputChannels].buffers[index])[hostFrameOffset];
+                        float *userBufPtr = &outBufPtr[j+(userFrameOffset*NumOuputChannels)];
+                        for( i=0; i<framePerBuffer; i++ )
+                        {
+                                temp = (long) *userBufPtr;
+                                if (swap) temp = SwapLong(temp);
+                                asioBufPtr[i] = (float)temp;
+                                userBufPtr += NumOuputChannels;
+                        }
+                }
+                
+        }
+        else /* CLIP */
+        {
+                for (j= 0; j < NumOuputChannels; j++) 
+                {
+                        float *asioBufPtr = &((float*)nativeBuffer[j+NumInputChannels].buffers[index])[hostFrameOffset];
+                        float *userBufPtr = &outBufPtr[j+(userFrameOffset*NumOuputChannels)];
+                        for( i=0; i<framePerBuffer; i++ )
+                        {
+                                float temp1 = *userBufPtr;
+                                temp1 = ClipFloat(temp1);  // Is is necessary??
+                                temp = (long) temp1;
+                                if (swap) temp = SwapLong(temp);
+                                asioBufPtr[i] = (float)temp;
+                                userBufPtr += NumOuputChannels;
+                        }
+                }
+        }
+        
+}
+//-------------------------------------------------------------------------------------------------------------------------------------------------------                                       
+ void Output_Int32_Int16(ASIOBufferInfo* nativeBuffer, long *outBufPtr, int framePerBuffer, int NumInputChannels, int NumOuputChannels, int index, int hostFrameOffset,int userFrameOffset,uint32 flags,bool swap)
+{
+        long temp;
+        int i,j;
+        
+        if( flags & paDitherOff )
+        {
+                for (j= 0; j < NumOuputChannels; j++) 
+                {
+                        short *asioBufPtr = &((short*)nativeBuffer[j+NumInputChannels].buffers[index])[hostFrameOffset];
+                        long *userBufPtr = &outBufPtr[j+(userFrameOffset*NumOuputChannels)];
+                        for( i=0; i<framePerBuffer; i++ )
+                        {
+                                temp = (short) ((*userBufPtr) >> 16);
+                                if (swap) temp = SwapShort(temp);
+                                asioBufPtr[i] = temp;
+                                userBufPtr += NumOuputChannels;
+                        }
+                }
+        }
+        else
+        {
+                for (j= 0; j < NumOuputChannels; j++) 
+                {
+                        short *asioBufPtr = &((short*)nativeBuffer[j+NumInputChannels].buffers[index])[hostFrameOffset];
+                        long *userBufPtr = &outBufPtr[j+(userFrameOffset*NumOuputChannels)];
+                        for( i=0; i<framePerBuffer; i++ )
+                        {
+                                temp = (*userBufPtr >> 1) + Pa_TriangularDither();
+                                temp = temp >> 15;
+                                temp = (short) ClipShort(temp);
+                                if (swap) temp = SwapShort(temp);
+                                asioBufPtr[i] = temp;
+                                userBufPtr += NumOuputChannels;
+                        }
+                }
+        }
+}
+
+//-------------------------------------------------------------------------------------------------------------------------------------------------------
+ void Output_Int32_Int32(ASIOBufferInfo* nativeBuffer, long *outBufPtr, int framePerBuffer, int NumInputChannels, int NumOuputChannels, int index, int hostFrameOffset,int userFrameOffset,uint32 flags,bool swap)
+{
+        long temp;
+        int i,j;
+        
+        for (j= 0; j < NumOuputChannels; j++) 
+        {
+                long *asioBufPtr = &((long*)nativeBuffer[j+NumInputChannels].buffers[index])[hostFrameOffset];
+                long *userBufPtr = &outBufPtr[j+(userFrameOffset*NumOuputChannels)];
+                for( i=0; i<framePerBuffer; i++ )
+                {
+                        temp = *userBufPtr;
+                        if (swap) temp = SwapLong(temp);
+                        asioBufPtr[i] = temp;
+                        userBufPtr += NumOuputChannels;
+                }
+        }
+}
+
+//-------------------------------------------------------------------------------------------------------------------------------------------------------
+// MUST BE CHECKED
+
+ void Output_Int32_Float32(ASIOBufferInfo* nativeBuffer, long *outBufPtr, int framePerBuffer, int NumInputChannels, int NumOuputChannels, int index, int hostFrameOffset,int userFrameOffset,uint32 flags,bool swap)
+{
+        long temp;
+        int i,j;
+        
+        for (j= 0; j < NumOuputChannels; j++) 
+        {
+                float *asioBufPtr = &((float*)nativeBuffer[j+NumInputChannels].buffers[index])[hostFrameOffset];
+                long *userBufPtr = &outBufPtr[j+(userFrameOffset*NumOuputChannels)];
+                for( i=0; i<framePerBuffer; i++ )
+                {
+                        temp = *userBufPtr;
+                        if (swap) temp = SwapLong(temp);
+                        asioBufPtr[i] = ((float)temp) * (1.0f / MAX_INT32_FP);
+                        userBufPtr += NumOuputChannels;
+                }
+        }
+}
+
+//-------------------------------------------------------------------------------------------------------------------------------------------------------
+ void Output_Int16_Int16(ASIOBufferInfo* nativeBuffer, short *outBufPtr, int framePerBuffer, int NumInputChannels, int NumOuputChannels, int index, int hostFrameOffset, int userFrameOffset,bool swap)
+{
+        long temp;
+        int i,j;
+
+        for (j= 0; j < NumOuputChannels; j++) 
+        {
+                short *asioBufPtr = &((short*)nativeBuffer[j+NumInputChannels].buffers[index])[hostFrameOffset];
+                short *userBufPtr = &outBufPtr[j+(userFrameOffset*NumOuputChannels)];
+                for( i=0; i<framePerBuffer; i++ )
+                {
+                        temp = *userBufPtr;
+                        if (swap) temp = SwapShort(temp);
+                        asioBufPtr[i] = temp;
+                        userBufPtr += NumOuputChannels;
+                }
+        }
+}
+
+//-------------------------------------------------------------------------------------------------------------------------------------------------------
+ void Output_Int16_Int32(ASIOBufferInfo* nativeBuffer, short *outBufPtr, int framePerBuffer, int NumInputChannels, int NumOuputChannels, int index, int hostFrameOffset,int userFrameOffset, bool swap)
+{
+        long temp;
+        int i,j;
+        
+        for (j= 0; j < NumOuputChannels; j++) 
+        {
+                long *asioBufPtr = &((long*)nativeBuffer[j+NumInputChannels].buffers[index])[hostFrameOffset];
+                short *userBufPtr = &outBufPtr[j+(userFrameOffset*NumOuputChannels)];
+                for( i=0; i<framePerBuffer; i++ )
+                {
+                        temp = (*userBufPtr)<<16;
+                        if (swap) temp = SwapLong(temp);
+                        asioBufPtr[i] = temp;
+                        userBufPtr += NumOuputChannels;
+                }
+        }
+}
+
+//-------------------------------------------------------------------------------------------------------------------------------------------------------
+// MUST BE CHECKED
+ void Output_Int16_Float32(ASIOBufferInfo* nativeBuffer, short *outBufPtr, int framePerBuffer, int NumInputChannels, int NumOuputChannels, int index, int hostFrameOffset,int userFrameOffset, bool swap)
+{
+        long temp;
+        int i,j;
+        
+        for (j= 0; j < NumOuputChannels; j++) 
+        {
+                float *asioBufPtr = &((float*)nativeBuffer[j+NumInputChannels].buffers[index])[hostFrameOffset];
+                short *userBufPtr = &outBufPtr[j+(userFrameOffset*NumOuputChannels)];
+                for( i=0; i<framePerBuffer; i++ )
+                {
+                        temp = *userBufPtr;
+                        asioBufPtr[i] = ((float)temp) * (1.0f / MAX_INT16_FP);
+                        userBufPtr += NumOuputChannels;
+                }
+        }
+}
+//-------------------------------------------------------------------------------------------------------------------------------------------------------
+ void Output_Int8_Int16(ASIOBufferInfo* nativeBuffer, char *outBufPtr, int framePerBuffer, int NumInputChannels, int NumOuputChannels, int index, int hostFrameOffset,int userFrameOffset, bool swap)
+{
+        long temp;
+        int i,j;
+
+        for (j= 0; j < NumOuputChannels; j++) 
+        {
+                short *asioBufPtr = &((short*)nativeBuffer[j+NumInputChannels].buffers[index])[hostFrameOffset];
+                char *userBufPtr = &outBufPtr[j+(userFrameOffset*NumOuputChannels)];
+                for( i=0; i<framePerBuffer; i++ )
+                {
+                        temp = (short)(*userBufPtr)<<8;
+                        if (swap) temp = SwapShort(temp);
+                        asioBufPtr[i] = temp;
+                        userBufPtr += NumOuputChannels;
+                }
+        }
+}
+
+//-------------------------------------------------------------------------------------------------------------------------------------------------------
+ void Output_Int8_Int32(ASIOBufferInfo* nativeBuffer, char *outBufPtr, int framePerBuffer, int NumInputChannels, int NumOuputChannels, int index, int hostFrameOffset,int userFrameOffset, bool swap)
+{
+        long temp;
+        int i,j;
+
+        for (j= 0; j < NumOuputChannels; j++) 
+        {
+                long *asioBufPtr = &((long*)nativeBuffer[j+NumInputChannels].buffers[index])[hostFrameOffset];
+                char *userBufPtr = &outBufPtr[j+(userFrameOffset*NumOuputChannels)];
+                for( i=0; i<framePerBuffer; i++ )
+                {
+                        temp = (short)(*userBufPtr)<<24;
+                        if (swap) temp = SwapLong(temp);
+                        asioBufPtr[i] = temp;
+                        userBufPtr += NumOuputChannels;
+                }
+        }
+}
+
+
+//-------------------------------------------------------------------------------------------------------------------------------------------------------
+// MUST BE CHECKED
+ void Output_Int8_Float32(ASIOBufferInfo* nativeBuffer, char *outBufPtr, int framePerBuffer, int NumInputChannels, int NumOuputChannels, int index, int hostFrameOffset,int userFrameOffset, bool swap)
+{
+        long temp;
+        int i,j;
+        
+        for (j= 0; j < NumOuputChannels; j++) 
+        {
+                long *asioBufPtr = &((long*)nativeBuffer[j+NumInputChannels].buffers[index])[hostFrameOffset];
+                char *userBufPtr = &outBufPtr[j+(userFrameOffset*NumOuputChannels)];
+                for( i=0; i<framePerBuffer; i++ )
+                {
+                        temp = *userBufPtr;
+                        asioBufPtr[i] = (long)(((float)temp) * (1.0f / MAX_INT8_FP));
+                        userBufPtr += NumOuputChannels;
+                }
+        }
+}
+
+//-------------------------------------------------------------------------------------------------------------------------------------------------------
+ void Output_IntU8_Int16(ASIOBufferInfo* nativeBuffer, unsigned char *outBufPtr, int framePerBuffer, int NumInputChannels, int NumOuputChannels, int index, int hostFrameOffset,int userFrameOffset, bool swap)
+{
+        long temp;
+        int i,j;
+
+        for (j= 0; j < NumOuputChannels; j++) 
+        {
+                short *asioBufPtr = &((short*)nativeBuffer[j+NumInputChannels].buffers[index])[hostFrameOffset];
+                unsigned char *userBufPtr = &outBufPtr[j+(userFrameOffset*NumOuputChannels)];
+                for( i=0; i<framePerBuffer; i++ )
+                {
+                        temp = ((short)((*userBufPtr) - 0x80)) << 8;
+                        if (swap) temp = SwapShort(temp);
+                        asioBufPtr[i] = temp;
+                        userBufPtr += NumOuputChannels;
+                }
+        }
+}       
+
+//-------------------------------------------------------------------------------------------------------------------------------------------------------
+ void Output_IntU8_Int32(ASIOBufferInfo* nativeBuffer, unsigned char *outBufPtr, int framePerBuffer, int NumInputChannels, int NumOuputChannels, int index, int hostFrameOffset,int userFrameOffset, bool swap)
+{
+        long temp;
+        int i,j;
+        
+        for (j= 0; j < NumOuputChannels; j++) 
+        {
+                long *asioBufPtr = &((long*)nativeBuffer[j+NumInputChannels].buffers[index])[hostFrameOffset];
+                unsigned char *userBufPtr = &outBufPtr[j+(userFrameOffset*NumOuputChannels)];
+                for( i=0; i<framePerBuffer; i++ )
+                {
+                        temp = ((short)((*userBufPtr) - 0x80)) << 24;
+                        if (swap) temp = SwapLong(temp);
+                        asioBufPtr[i] = temp;
+                        userBufPtr += NumOuputChannels;
+                }
+        }
+}
+
+//-------------------------------------------------------------------------------------------------------------------------------------------------------
+// MUST BE CHECKED
+
+ void Output_IntU8_Float32(ASIOBufferInfo* nativeBuffer, unsigned char *outBufPtr, int framePerBuffer, int NumInputChannels, int NumOuputChannels, int index, int hostFrameOffset,int userFrameOffset, bool swap)
+{
+        long temp;
+        int i,j;
+        
+        for (j= 0; j < NumOuputChannels; j++) 
+        {
+                float *asioBufPtr = &((float*)nativeBuffer[j+NumInputChannels].buffers[index])[hostFrameOffset];
+                unsigned char *userBufPtr = &outBufPtr[j+(userFrameOffset*NumOuputChannels)];
+                for( i=0; i<framePerBuffer; i++ )
+                {
+                        temp = ((short)((*userBufPtr) - 0x80)) << 24;
+                        asioBufPtr[i] = ((float)temp) * (1.0f / MAX_INT32_FP);
+                        userBufPtr += NumOuputChannels;
+                }
+        }
+}
+
+//-------------------------------------------------------------------------------------------------------------------------------------------------------
+ void Pa_ASIO_Clear_Output_16 (ASIOBufferInfo* nativeBuffer, int frames, int NumInputChannels, int NumOuputChannels, int index, int hostFrameOffset)
+{
+        int i,j;
+
+        for( j=0; j<NumOuputChannels; j++ ) {
+                short *asioBufPtr = &((short*)nativeBuffer[j+NumInputChannels].buffers[index])[hostFrameOffset];
+                for (i= 0; i < frames; i++) {asioBufPtr[i] = 0; }
+        }
+}
+
+//-------------------------------------------------------------------------------------------------------------------------------------------------------
+ void Pa_ASIO_Clear_Output_32 (ASIOBufferInfo* nativeBuffer, int frames, int NumInputChannels, int NumOuputChannels, int index, int hostFrameOffset)
+{
+        int i,j;
+
+        for( j=0; j<NumOuputChannels; j++ ) {
+                long *asioBufPtr = &((long*)nativeBuffer[j+NumInputChannels].buffers[index])[hostFrameOffset];
+                for (i= 0; i < frames; i++) {asioBufPtr[i] = 0; }
+        }
+}
+
+
+//-------------------------------------------------------------------------------------------------------------------------------------------------------
+void Pa_ASIO_Adaptor_Init()
+{
+       if (asioDriverInfo.past->past_FramesPerUserBuffer <= asioDriverInfo.past_FramesPerHostBuffer) {
+               asioDriverInfo.pahsc_hostOutputBufferFrameOffset = asioDriverInfo.pahsc_OutputBufferOffset;
+               asioDriverInfo.pahsc_userInputBufferFrameOffset = 0; // empty 
+               asioDriverInfo.pahsc_userOutputBufferFrameOffset = asioDriverInfo.past->past_FramesPerUserBuffer; // empty 
+       }else {
+               asioDriverInfo.pahsc_hostOutputBufferFrameOffset = 0; // empty 
+               asioDriverInfo.pahsc_userInputBufferFrameOffset = asioDriverInfo.pahsc_InputBufferOffset;
+               asioDriverInfo.pahsc_userOutputBufferFrameOffset = asioDriverInfo.past->past_FramesPerUserBuffer;       // empty 
+       }
+}
+
+
+//-------------------------------------------------------------------------------------------------------------------------------------------------------
+// FIXME : optimization for Input only or output only modes (really necessary ??)
+void Pa_ASIO_Callback_Input( long index)
+{
+        internalPortAudioStream  *past = asioDriverInfo.past;
+        long framesInputHostBuffer = asioDriverInfo.past_FramesPerHostBuffer; // number of frames available into the host input buffer
+               long framesInputUserBuffer;             // number of frames needed to complete the user input buffer
+       long framesOutputHostBuffer;    // number of frames needed to complete the host output buffer
+       long framesOuputUserBuffer;             // number of frames available into the user output buffer
+       long userResult;
+        long tmp;
+        
+         /* Fill host ASIO output with remaining frames in user output */
+        framesOutputHostBuffer = asioDriverInfo.past_FramesPerHostBuffer;
+        framesOuputUserBuffer = asioDriverInfo.past->past_FramesPerUserBuffer - asioDriverInfo.pahsc_userOutputBufferFrameOffset;
+        tmp = min(framesOutputHostBuffer, framesOuputUserBuffer);
+        framesOutputHostBuffer -= tmp;
+        Pa_ASIO_Callback_Output(index,tmp);
+        
+        /* Available frames in hostInputBuffer */
+        while (framesInputHostBuffer  > 0) {
+                
+                /* Number of frames needed to complete an user input buffer */
+                framesInputUserBuffer = asioDriverInfo.past->past_FramesPerUserBuffer - asioDriverInfo.pahsc_userInputBufferFrameOffset;
+                                
+                if (framesInputHostBuffer >= framesInputUserBuffer) {
+                
+                        /* Convert ASIO input to user input */
+                        Pa_ASIO_Convert_Inter_Input (asioDriverInfo.bufferInfos, 
+                                                    past->past_InputBuffer, 
+                                                    asioDriverInfo.pahsc_NumInputChannels ,
+                                                                       asioDriverInfo.pahsc_NumOutputChannels,
+                                                                               framesInputUserBuffer,
+                                                                       asioDriverInfo.past_FramesPerHostBuffer - framesInputHostBuffer,
+                                                                       asioDriverInfo.pahsc_userInputBufferFrameOffset,
+                                                                       asioDriverInfo.pahsc_channelInfos[0].type,
+                                                                       past->past_InputSampleFormat,
+                                                                       past->past_Flags,
+                                                                       index);
+                        
+                        /* Call PortAudio callback */
+                        userResult = asioDriverInfo.past->past_Callback(past->past_InputBuffer, past->past_OutputBuffer,
+                                past->past_FramesPerUserBuffer,past->past_FrameCount,past->past_UserData );
+               
+                               /* User callback has asked us to stop in the middle of the host buffer  */
+                               if( userResult != 0) {
+                           
+                                   /* Put 0 in the end of the output buffer */
+                                    Pa_ASIO_Clear_Output(asioDriverInfo.bufferInfos, 
+                                               asioDriverInfo.pahsc_channelInfos[0].type,
+                                               asioDriverInfo.pahsc_NumInputChannels ,
+                                               asioDriverInfo.pahsc_NumOutputChannels,
+                                               index, 
+                                               asioDriverInfo.pahsc_hostOutputBufferFrameOffset, 
+                                               asioDriverInfo.past_FramesPerHostBuffer - asioDriverInfo.pahsc_hostOutputBufferFrameOffset);
+                                   
+                                   past->past_StopSoon = 1; 
+                                   return;
+                               }
+                               
+                               
+                               /* Full user ouput buffer : write offset */
+                               asioDriverInfo.pahsc_userOutputBufferFrameOffset = 0;
+                               
+                               /*  Empty user input buffer : read offset */
+                               asioDriverInfo.pahsc_userInputBufferFrameOffset = 0;
+                               
+                               /*  Fill host ASIO output  */
+                        tmp = min (past->past_FramesPerUserBuffer,framesOutputHostBuffer);
+                        Pa_ASIO_Callback_Output(index,tmp);
+                        
+                        framesOutputHostBuffer -= tmp;
+                        framesInputHostBuffer -= framesInputUserBuffer;
+                
+                }else {
+                
+                        /* Convert ASIO input to user input */
+                        Pa_ASIO_Convert_Inter_Input (asioDriverInfo.bufferInfos, 
+                                                    past->past_InputBuffer, 
+                                                    asioDriverInfo.pahsc_NumInputChannels ,
+                                                                       asioDriverInfo.pahsc_NumOutputChannels,
+                                                                       framesInputHostBuffer,
+                                                                       asioDriverInfo.past_FramesPerHostBuffer - framesInputHostBuffer,
+                                                                       asioDriverInfo.pahsc_userInputBufferFrameOffset,
+                                                                       asioDriverInfo.pahsc_channelInfos[0].type,
+                                                                       past->past_InputSampleFormat,
+                                                                       past->past_Flags,
+                                                                       index);
+                        
+                        /* Update pahsc_userInputBufferFrameOffset */
+                        asioDriverInfo.pahsc_userInputBufferFrameOffset += framesInputHostBuffer;
+                        
+                        /* Update framesInputHostBuffer */
+                        framesInputHostBuffer = 0; 
+                }               
+        }
+
+}
+
+//-------------------------------------------------------------------------------------------------------------------------------------------------------
+void Pa_ASIO_Callback_Output(long index, long framePerBuffer)
+{
+        internalPortAudioStream *past = asioDriverInfo.past;
+        
+        if (framePerBuffer > 0) {
+                
+                /* Convert user output to ASIO ouput */
+                Pa_ASIO_Convert_Inter_Output (asioDriverInfo.bufferInfos, 
+                                            past->past_OutputBuffer,
+                                            asioDriverInfo.pahsc_NumInputChannels,
+                                               asioDriverInfo.pahsc_NumOutputChannels,
+                                               framePerBuffer,
+                                               asioDriverInfo.pahsc_hostOutputBufferFrameOffset,
+                                               asioDriverInfo.pahsc_userOutputBufferFrameOffset,
+                                               asioDriverInfo.pahsc_channelInfos[0].type,
+                                               past->past_InputSampleFormat,
+                                               past->past_Flags,
+                                               index);
+                
+                /* Update hostOuputFrameOffset */
+                asioDriverInfo.pahsc_hostOutputBufferFrameOffset += framePerBuffer;
+
+                /* Update userOutputFrameOffset */
+                asioDriverInfo.pahsc_userOutputBufferFrameOffset += framePerBuffer;
+        }
+}
+//-------------------------------------------------------------------------------------------------------------------------------------------------------
+ void Pa_ASIO_Callback_End()
+ {
+        /* Empty ASIO ouput : write offset */
+     asioDriverInfo.pahsc_hostOutputBufferFrameOffset = 0;
+ }
+
+//-------------------------------------------------------------------------------------------------------------------------------------------------------
+void Pa_ASIO_Clear_User_Buffers()
+{
+       if( asioDriverInfo.past->past_InputBuffer != NULL )
+       {
+               memset( asioDriverInfo.past->past_InputBuffer, 0, asioDriverInfo.past->past_InputBufferSize );
+       }
+       if( asioDriverInfo.past->past_OutputBuffer != NULL )
+       {
+               memset( asioDriverInfo.past->past_OutputBuffer, 0, asioDriverInfo.past->past_OutputBufferSize );
+       }
+}
+
+//-------------------------------------------------------------------------------------------------------------------------------------------------------
+ void Pa_ASIO_Clear_Output(ASIOBufferInfo* nativeBuffer, 
+        ASIOSampleType nativeFormat,
+        short NumInputChannels, 
+        short NumOuputChannels,
+        int index, 
+        int hostFrameOffset, 
+        int frames)
+{
+        
+        switch (nativeFormat) {
+        
+                case ASIOSTInt16MSB:
+                case ASIOSTInt16LSB:
+                case ASIOSTInt32MSB16:
+                case ASIOSTInt32LSB16:
+                        Pa_ASIO_Clear_Output_16(nativeBuffer, frames,  NumInputChannels, NumOuputChannels, index, hostFrameOffset);
+                        break;
+                        
+                case ASIOSTFloat64MSB:
+                case ASIOSTFloat64LSB:
+                        break;
+                        
+                case ASIOSTFloat32MSB:
+                case ASIOSTFloat32LSB:
+                case ASIOSTInt32MSB:
+                case ASIOSTInt32LSB:
+                case ASIOSTInt32MSB18:          
+                case ASIOSTInt32MSB20:          
+                case ASIOSTInt32MSB24:          
+                case ASIOSTInt32LSB18:          
+                case ASIOSTInt32LSB20:          
+                case ASIOSTInt32LSB24:          
+                        Pa_ASIO_Clear_Output_32(nativeBuffer, frames,  NumInputChannels, NumOuputChannels, index, hostFrameOffset);
+                        break;
+                        
+                case ASIOSTInt24MSB:
+                case ASIOSTInt24LSB:
+                        break;
+                        
+                default:
+                        break;
+        }
+}
+
+
+//---------------------------------------------------------------------------------------
+void Pa_ASIO_Convert_Inter_Input(
+                ASIOBufferInfo* nativeBuffer, 
+                void* inputBuffer,
+                       short NumInputChannels, 
+                       short NumOuputChannels,
+                       short framePerBuffer,
+                       short hostFrameOffset,
+                       short userFrameOffset,
+                       ASIOSampleType nativeFormat, 
+                       PaSampleFormat paFormat, 
+                       PaStreamFlags flags,
+                       short index)
+{
+                
+        if((NumInputChannels > 0) && (nativeBuffer != NULL))
+        {
+                /* Convert from native format to PA format. */
+                switch(paFormat)
+                {
+                                case paFloat32:
+                        {
+                                float *inBufPtr = (float *) inputBuffer;
+                             
+                                switch (nativeFormat) {
+                                        case ASIOSTInt16LSB:
+                                                Input_Int16_Flot32(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset, userFrameOffset, swap);
+                                                break;  
+                                        case ASIOSTInt16MSB:
+                                                Input_Int16_Flot32(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset, userFrameOffset,!swap);
+                                                break;  
+                                        case ASIOSTInt32LSB:
+                                                Input_Int32_Flot32(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset, userFrameOffset,swap);
+                                                break;
+                                        case ASIOSTInt32MSB:
+                                                Input_Int32_Flot32(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset, userFrameOffset,!swap);
+                                                break;  
+                                        case ASIOSTFloat32LSB:          // IEEE 754 32 bit float, as found on Intel x86 architecture
+                                                Input_Float32_Flot32(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset, userFrameOffset,swap);
+                                                break;  
+                                        case ASIOSTFloat32MSB:          // IEEE 754 32 bit float, as found on Intel x86 architecture
+                                                Input_Float32_Flot32(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset, userFrameOffset,!swap);
+                                                break;  
+                                        
+                                        case ASIOSTInt24LSB:            // used for 20 bits as well
+                                        case ASIOSTInt24MSB:            // used for 20 bits as well
+                                                
+                                        case ASIOSTFloat64LSB:          // IEEE 754 64 bit double float, as found on Intel x86 architecture
+                                        case ASIOSTFloat64MSB:          // IEEE 754 64 bit double float, as found on Intel x86 architecture
+
+                                        // these are used for 32 bit data buffer, with different alignment of the data inside
+                                        // 32 bit PCI bus systems can more easily used with these
+
+                                        case ASIOSTInt32LSB16:          // 32 bit data with 16 bit alignment
+                                        case ASIOSTInt32LSB18:          // 32 bit data with 18 bit alignment
+                                        case ASIOSTInt32LSB20:          // 32 bit data with 20 bit alignment
+                                        case ASIOSTInt32LSB24:          // 32 bit data with 24 bit alignment
+                                                                                                                                                                
+                                
+                                        case ASIOSTInt32MSB16:          // 32 bit data with 16 bit alignment
+                                        case ASIOSTInt32MSB18:          // 32 bit data with 18 bit alignment
+                                        case ASIOSTInt32MSB20:          // 32 bit data with 20 bit alignment
+                                        case ASIOSTInt32MSB24:          // 32 bit data with 24 bit alignment
+                                                DBUG(("Not yet implemented : please report the problem\n"));
+                                                break;
+                                }       
+                                
+                                break;
+                        }
+                        
+                case paInt32:
+                        {
+                                long *inBufPtr = (long *)inputBuffer;
+                                 
+                                switch (nativeFormat) {
+                                        case ASIOSTInt16LSB:
+                                                Input_Int16_Int32(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset,userFrameOffset, swap);
+                                                break;
+                                        case ASIOSTInt16MSB:
+                                                Input_Int16_Int32(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset,userFrameOffset, !swap);
+                                                break;
+                                        case ASIOSTInt32LSB:
+                                                Input_Int32_Int32(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset,userFrameOffset, swap);
+                                                break;
+                                        case ASIOSTInt32MSB:
+                                                Input_Int32_Int32(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset,userFrameOffset, !swap);
+                                                break;
+                                        case ASIOSTFloat32LSB:          // IEEE 754 32 bit float, as found on Intel x86 architecture
+                                                Input_Float32_Int32(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset,userFrameOffset, swap);
+                                                break;  
+                                        case ASIOSTFloat32MSB:          // IEEE 754 32 bit float, as found on Intel x86 architecture
+                                                Input_Float32_Int32(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset,userFrameOffset, !swap);
+                                                break;  
+                                        
+                                        case ASIOSTInt24LSB:            // used for 20 bits as well
+                                        case ASIOSTInt24MSB:            // used for 20 bits as well
+                                                
+                                        case ASIOSTFloat64LSB:          // IEEE 754 64 bit double float, as found on Intel x86 architecture
+                                        case ASIOSTFloat64MSB:          // IEEE 754 64 bit double float, as found on Intel x86 architecture
+
+                                        // these are used for 32 bit data buffer, with different alignment of the data inside
+                                        // 32 bit PCI bus systems can more easily used with these
+
+                                        case ASIOSTInt32LSB16:          // 32 bit data with 16 bit alignment
+                                        case ASIOSTInt32LSB18:          // 32 bit data with 18 bit alignment
+                                        case ASIOSTInt32LSB20:          // 32 bit data with 20 bit alignment
+                                        case ASIOSTInt32LSB24:          // 32 bit data with 24 bit alignment
+                                                                                                                                                                
+                                
+                                        case ASIOSTInt32MSB16:          // 32 bit data with 16 bit alignment
+                                        case ASIOSTInt32MSB18:          // 32 bit data with 18 bit alignment
+                                        case ASIOSTInt32MSB20:          // 32 bit data with 20 bit alignment
+                                        case ASIOSTInt32MSB24:          // 32 bit data with 24 bit alignment
+                                                DBUG(("Not yet implemented : please report the problem\n"));
+                                                break;
+                                        
+                                }
+                                break;
+                        }
+                        
+                case paInt16:
+                        {
+                                short *inBufPtr = (short *) inputBuffer;
+                                 
+                                switch (nativeFormat) {
+                                        case ASIOSTInt16LSB:
+                                                Input_Int16_Int16(nativeBuffer, inBufPtr, framePerBuffer , NumInputChannels, index , hostFrameOffset,userFrameOffset, swap);
+                                                break;
+                                        case ASIOSTInt16MSB:
+                                                Input_Int16_Int16(nativeBuffer, inBufPtr, framePerBuffer , NumInputChannels, index , hostFrameOffset,userFrameOffset, !swap);
+                                                break;
+                                        case ASIOSTInt32LSB:
+                                                Input_Int32_Int16(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset,userFrameOffset, flags,swap);
+                                                break;
+                                        case ASIOSTInt32MSB:
+                                                Input_Int32_Int16(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset,userFrameOffset, flags,!swap);
+                                                break;
+                                        case ASIOSTFloat32LSB:          // IEEE 754 32 bit float, as found on Intel x86 architecture
+                                                Input_Float32_Int16(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset,userFrameOffset, flags,swap);
+                                                break;  
+                                        case ASIOSTFloat32MSB:          // IEEE 754 32 bit float, as found on Intel x86 architecture
+                                                Input_Float32_Int16(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset,userFrameOffset, flags,!swap);
+                                                break;  
+                
+                                        case ASIOSTInt24LSB:            // used for 20 bits as well
+                                        case ASIOSTInt24MSB:            // used for 20 bits as well
+                                                
+                                        case ASIOSTFloat64LSB:          // IEEE 754 64 bit double float, as found on Intel x86 architecture
+                                        case ASIOSTFloat64MSB:          // IEEE 754 64 bit double float, as found on Intel x86 architecture
+
+                                        // these are used for 32 bit data buffer, with different alignment of the data inside
+                                        // 32 bit PCI bus systems can more easily used with these
+
+                                        case ASIOSTInt32LSB16:          // 32 bit data with 16 bit alignment
+                                        case ASIOSTInt32LSB18:          // 32 bit data with 18 bit alignment
+                                        case ASIOSTInt32LSB20:          // 32 bit data with 20 bit alignment
+                                        case ASIOSTInt32LSB24:          // 32 bit data with 24 bit alignment
+                                                                                                                                                                
+                                
+                                        case ASIOSTInt32MSB16:          // 32 bit data with 16 bit alignment
+                                        case ASIOSTInt32MSB18:          // 32 bit data with 18 bit alignment
+                                        case ASIOSTInt32MSB20:          // 32 bit data with 20 bit alignment
+                                        case ASIOSTInt32MSB24:          // 32 bit data with 24 bit alignment
+                                                DBUG(("Not yet implemented : please report the problem\n"));
+                                                break;
+                        
+                                }
+                                break;
+                        }
+
+                case paInt8:
+                        {
+                                /* Convert 16 bit data to 8 bit chars */
+                                
+                                char *inBufPtr = (char *) inputBuffer;
+                                
+                                switch (nativeFormat) {
+                                        case ASIOSTInt16LSB:
+                                                Input_Int16_Int8(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset,userFrameOffset,flags,swap);
+                                                break;  
+                                        case ASIOSTInt16MSB:
+                                                Input_Int16_Int8(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset,userFrameOffset, flags,!swap);
+                                                break;  
+                                        case ASIOSTInt32LSB:
+                                                Input_Int32_Int8(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset,userFrameOffset, flags,swap);
+                                                break;
+                                        case ASIOSTInt32MSB:
+                                                Input_Int32_Int8(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset,userFrameOffset, flags,!swap);
+                                                break;
+                                        case ASIOSTFloat32LSB:          // IEEE 754 32 bit float, as found on Intel x86 architecture
+                                                Input_Float32_Int8(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset,userFrameOffset, flags,swap);
+                                                break;  
+                                        case ASIOSTFloat32MSB:          // IEEE 754 32 bit float, as found on Intel x86 architecture
+                                                Input_Float32_Int8(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset,userFrameOffset, flags,!swap);
+                                                break;  
+                                        
+                                        case ASIOSTInt24LSB:            // used for 20 bits as well
+                                        case ASIOSTInt24MSB:            // used for 20 bits as well
+                                                
+                                        case ASIOSTFloat64LSB:          // IEEE 754 64 bit double float, as found on Intel x86 architecture
+                                        case ASIOSTFloat64MSB:          // IEEE 754 64 bit double float, as found on Intel x86 architecture
+
+                                        // these are used for 32 bit data buffer, with different alignment of the data inside
+                                        // 32 bit PCI bus systems can more easily used with these
+
+                                        case ASIOSTInt32LSB16:          // 32 bit data with 16 bit alignment
+                                        case ASIOSTInt32LSB18:          // 32 bit data with 18 bit alignment
+                                        case ASIOSTInt32LSB20:          // 32 bit data with 20 bit alignment
+                                        case ASIOSTInt32LSB24:          // 32 bit data with 24 bit alignment
+                                                                                                                                                                
+                                
+                                        case ASIOSTInt32MSB16:          // 32 bit data with 16 bit alignment
+                                        case ASIOSTInt32MSB18:          // 32 bit data with 18 bit alignment
+                                        case ASIOSTInt32MSB20:          // 32 bit data with 20 bit alignment
+                                        case ASIOSTInt32MSB24:          // 32 bit data with 24 bit alignment
+                                                DBUG(("Not yet implemented : please report the problem\n"));
+                                                break;  
+                                }       
+                                break;
+                        }
+
+                case paUInt8:
+                        {
+                                /* Convert 16 bit data to 8 bit unsigned chars */
+                                
+                                unsigned char *inBufPtr = (unsigned char *)inputBuffer;
+                                 
+                                switch (nativeFormat) {
+                                        case ASIOSTInt16LSB:
+                                                Input_Int16_IntU8(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset,userFrameOffset, flags,swap);
+                                                break;  
+                                        case ASIOSTInt16MSB:
+                                                Input_Int16_IntU8(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset,userFrameOffset, flags,!swap);
+                                                break;  
+                                        case ASIOSTInt32LSB:
+                                                Input_Int32_IntU8(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset,userFrameOffset,flags,swap);
+                                                break;
+                                        case ASIOSTInt32MSB:
+                                                Input_Int32_IntU8(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset,userFrameOffset, flags,!swap);
+                                                break;
+                                        case ASIOSTFloat32LSB:          // IEEE 754 32 bit float, as found on Intel x86 architecture
+                                                Input_Float32_IntU8(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset,userFrameOffset,flags,swap);
+                                                break;  
+                                        case ASIOSTFloat32MSB:          // IEEE 754 32 bit float, as found on Intel x86 architecture
+                                                Input_Float32_IntU8(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset,userFrameOffset,flags,!swap);
+                                                break;  
+                                        
+                                        case ASIOSTInt24LSB:            // used for 20 bits as well
+                                        case ASIOSTInt24MSB:            // used for 20 bits as well
+                                                
+                                        case ASIOSTFloat64LSB:          // IEEE 754 64 bit double float, as found on Intel x86 architecture
+                                        case ASIOSTFloat64MSB:          // IEEE 754 64 bit double float, as found on Intel x86 architecture
+
+                                        // these are used for 32 bit data buffer, with different alignment of the data inside
+                                        // 32 bit PCI bus systems can more easily used with these
+
+                                        case ASIOSTInt32LSB16:          // 32 bit data with 16 bit alignment
+                                        case ASIOSTInt32LSB18:          // 32 bit data with 18 bit alignment
+                                        case ASIOSTInt32LSB20:          // 32 bit data with 20 bit alignment
+                                        case ASIOSTInt32LSB24:          // 32 bit data with 24 bit alignment
+                                                                                                                                                                
+                                
+                                        case ASIOSTInt32MSB16:          // 32 bit data with 16 bit alignment
+                                        case ASIOSTInt32MSB18:          // 32 bit data with 18 bit alignment
+                                        case ASIOSTInt32MSB20:          // 32 bit data with 20 bit alignment
+                                        case ASIOSTInt32MSB24:          // 32 bit data with 24 bit alignment
+                                                DBUG(("Not yet implemented : please report the problem\n"));
+                                                break;  
+                                
+                                }       
+                                break;
+                        }
+                        
+                default:
+                        break;
+                }
+        }
+}
+
+
+//---------------------------------------------------------------------------------------
+void Pa_ASIO_Convert_Inter_Output(ASIOBufferInfo* nativeBuffer, 
+                void* outputBuffer,
+                               short NumInputChannels, 
+                               short NumOuputChannels,
+                       short framePerBuffer,
+                       short hostFrameOffset,
+                       short userFrameOffset,
+                       ASIOSampleType nativeFormat, 
+                       PaSampleFormat paFormat, 
+                       PaStreamFlags flags,
+                       short index)
+{
+   
+           if((NumOuputChannels > 0) && (nativeBuffer != NULL)) 
+        {
+                /* Convert from PA format to native format */
+                
+                switch(paFormat)
+                {
+                        case paFloat32:
+                                {
+                                        float *outBufPtr = (float *) outputBuffer;
+                                        
+                                        switch (nativeFormat) {
+                                                case ASIOSTInt16LSB:
+                                                        Output_Float32_Int16(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset, userFrameOffset, flags, swap);
+                                                        break;  
+                                                case ASIOSTInt16MSB:
+                                                        Output_Float32_Int16(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset, userFrameOffset, flags,!swap);
+                                                        break;  
+                                                case ASIOSTInt32LSB:
+                                                        Output_Float32_Int32(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset, userFrameOffset, flags,swap);
+                                                        break;
+                                                case ASIOSTInt32MSB:
+                                                        Output_Float32_Int32(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset,userFrameOffset, flags,!swap);
+                                                        break;  
+                                                case ASIOSTFloat32LSB:
+                                                        Output_Float32_Float32(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset,userFrameOffset,flags,swap);
+                                                        break;
+                                                case ASIOSTFloat32MSB:
+                                                        Output_Float32_Float32(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset,userFrameOffset, flags,!swap);
+                                                        break;  
+                                                
+                                                case ASIOSTInt24LSB:            // used for 20 bits as well
+                                                case ASIOSTInt24MSB:            // used for 20 bits as well
+                                                        
+                                                case ASIOSTFloat64LSB:          // IEEE 754 64 bit double float, as found on Intel x86 architecture
+                                                case ASIOSTFloat64MSB:          // IEEE 754 64 bit double float, as found on Intel x86 architecture
+
+                                                // these are used for 32 bit data buffer, with different alignment of the data inside
+                                                // 32 bit PCI bus systems can more easily used with these
+
+                                                case ASIOSTInt32LSB16:          // 32 bit data with 16 bit alignment
+                                                case ASIOSTInt32LSB18:          // 32 bit data with 18 bit alignment
+                                                case ASIOSTInt32LSB20:          // 32 bit data with 20 bit alignment
+                                                case ASIOSTInt32LSB24:          // 32 bit data with 24 bit alignment
+                                                                                                                                                                        
+                                        
+                                                case ASIOSTInt32MSB16:          // 32 bit data with 16 bit alignment
+                                                case ASIOSTInt32MSB18:          // 32 bit data with 18 bit alignment
+                                                case ASIOSTInt32MSB20:          // 32 bit data with 20 bit alignment
+                                                case ASIOSTInt32MSB24:          // 32 bit data with 24 bit alignment
+                                                        DBUG(("Not yet implemented : please report the problem\n"));
+                                                        break;
+                                        }       
+                                        break;
+                                }
+                                
+                        case paInt32:
+                                {
+                                        long *outBufPtr = (long *) outputBuffer;
+                                        
+                                        switch (nativeFormat) {
+                                                case ASIOSTInt16LSB:
+                                                        Output_Int32_Int16(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset,userFrameOffset, flags,swap);
+                                                        break;  
+                                                case ASIOSTInt16MSB:
+                                                        Output_Int32_Int16(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset,userFrameOffset, flags,!swap);
+                                                        break;  
+                                                case ASIOSTInt32LSB:
+                                                        Output_Int32_Int32(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset,userFrameOffset, flags,swap);
+                                                        break;
+                                                case ASIOSTInt32MSB:
+                                                        Output_Int32_Int32(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset,userFrameOffset, flags,!swap);
+                                                        break;  
+                                                case ASIOSTFloat32LSB:
+                                                        Output_Int32_Float32(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset,userFrameOffset, flags,swap);
+                                                        break;
+                                                case ASIOSTFloat32MSB:
+                                                        Output_Int32_Float32(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset,userFrameOffset, flags,!swap);
+                                                        break;  
+                                                
+                                                case ASIOSTInt24LSB:            // used for 20 bits as well
+                                                case ASIOSTInt24MSB:            // used for 20 bits as well
+                                                        
+                                                case ASIOSTFloat64LSB:          // IEEE 754 64 bit double float, as found on Intel x86 architecture
+                                                case ASIOSTFloat64MSB:          // IEEE 754 64 bit double float, as found on Intel x86 architecture
+
+                                                // these are used for 32 bit data buffer, with different alignment of the data inside
+                                                // 32 bit PCI bus systems can more easily used with these
+
+                                                case ASIOSTInt32LSB16:          // 32 bit data with 16 bit alignment
+                                                case ASIOSTInt32LSB18:          // 32 bit data with 18 bit alignment
+                                                case ASIOSTInt32LSB20:          // 32 bit data with 20 bit alignment
+                                                case ASIOSTInt32LSB24:          // 32 bit data with 24 bit alignment
+                                                                                                                                                                        
+                                        
+                                                case ASIOSTInt32MSB16:          // 32 bit data with 16 bit alignment
+                                                case ASIOSTInt32MSB18:          // 32 bit data with 18 bit alignment
+                                                case ASIOSTInt32MSB20:          // 32 bit data with 20 bit alignment
+                                                case ASIOSTInt32MSB24:          // 32 bit data with 24 bit alignment
+                                                        DBUG(("Not yet implemented : please report the problem\n"));
+                                                        break;
+                                        }       
+                                        break;
+                                }
+                                
+                        case paInt16:
+                                {
+                                        short *outBufPtr = (short *) outputBuffer;
+                                        
+                                        switch (nativeFormat) {
+                                                case ASIOSTInt16LSB:
+                                                        Output_Int16_Int16(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset,userFrameOffset, swap);
+                                                        break;  
+                                                case ASIOSTInt16MSB:
+                                                        Output_Int16_Int16(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset,userFrameOffset, !swap);
+                                                        break;  
+                                                case ASIOSTInt32LSB:
+                                                        Output_Int16_Int32(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset,userFrameOffset, swap);
+                                                        break;
+                                                case ASIOSTInt32MSB:
+                                                        Output_Int16_Int32(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset,userFrameOffset, !swap);
+                                                        break;  
+                                                case ASIOSTFloat32LSB:
+                                                        Output_Int16_Float32(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset,userFrameOffset, swap);
+                                                        break;
+                                                case ASIOSTFloat32MSB:
+                                                        Output_Int16_Float32(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset,userFrameOffset, !swap);
+                                                        break;  
+                                                
+                                                case ASIOSTInt24LSB:            // used for 20 bits as well
+                                                case ASIOSTInt24MSB:            // used for 20 bits as well
+                                                        
+                                                case ASIOSTFloat64LSB:          // IEEE 754 64 bit double float, as found on Intel x86 architecture
+                                                case ASIOSTFloat64MSB:          // IEEE 754 64 bit double float, as found on Intel x86 architecture
+
+                                                // these are used for 32 bit data buffer, with different alignment of the data inside
+                                                // 32 bit PCI bus systems can more easily used with these
+
+                                                case ASIOSTInt32LSB16:          // 32 bit data with 16 bit alignment
+                                                case ASIOSTInt32LSB18:          // 32 bit data with 18 bit alignment
+                                                case ASIOSTInt32LSB20:          // 32 bit data with 20 bit alignment
+                                                case ASIOSTInt32LSB24:          // 32 bit data with 24 bit alignment
+                                                                                                                                                                        
+                                        
+                                                case ASIOSTInt32MSB16:          // 32 bit data with 16 bit alignment
+                                                case ASIOSTInt32MSB18:          // 32 bit data with 18 bit alignment
+                                                case ASIOSTInt32MSB20:          // 32 bit data with 20 bit alignment
+                                                case ASIOSTInt32MSB24:          // 32 bit data with 24 bit alignment
+                                                        DBUG(("Not yet implemented : please report the problem\n"));
+                                                        break;
+                                
+                                        }       
+                                        break;
+                                }
+
+
+                        case paInt8:
+                                {
+                                        char *outBufPtr = (char *) outputBuffer;
+                                        
+                                        switch (nativeFormat) {
+                                                case ASIOSTInt16LSB:
+                                                        Output_Int8_Int16(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset,userFrameOffset, swap);
+                                                        break;  
+                                                case ASIOSTInt16MSB:
+                                                        Output_Int8_Int16(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset,userFrameOffset, !swap);
+                                                        break;  
+                                                case ASIOSTInt32LSB:
+                                                        Output_Int8_Int32(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset,userFrameOffset, swap);
+                                                        break;
+                                                case ASIOSTInt32MSB:
+                                                        Output_Int8_Int32(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset,userFrameOffset, !swap);
+                                                        break;  
+                                                case ASIOSTFloat32LSB:
+                                                        Output_Int8_Float32(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset,userFrameOffset, swap);
+                                                        break;
+                                                case ASIOSTFloat32MSB:
+                                                        Output_Int8_Float32(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset,userFrameOffset, !swap);
+                                                        break;  
+                                                
+                                                case ASIOSTInt24LSB:            // used for 20 bits as well
+                                                case ASIOSTInt24MSB:            // used for 20 bits as well
+                                                        
+                                                case ASIOSTFloat64LSB:          // IEEE 754 64 bit double float, as found on Intel x86 architecture
+                                                case ASIOSTFloat64MSB:          // IEEE 754 64 bit double float, as found on Intel x86 architecture
+
+                                                // these are used for 32 bit data buffer, with different alignment of the data inside
+                                                // 32 bit PCI bus systems can more easily used with these
+
+                                                case ASIOSTInt32LSB16:          // 32 bit data with 16 bit alignment
+                                                case ASIOSTInt32LSB18:          // 32 bit data with 18 bit alignment
+                                                case ASIOSTInt32LSB20:          // 32 bit data with 20 bit alignment
+                                                case ASIOSTInt32LSB24:          // 32 bit data with 24 bit alignment
+                                                                                                                                                                        
+                                        
+                                                case ASIOSTInt32MSB16:          // 32 bit data with 16 bit alignment
+                                                case ASIOSTInt32MSB18:          // 32 bit data with 18 bit alignment
+                                                case ASIOSTInt32MSB20:          // 32 bit data with 20 bit alignment
+                                                case ASIOSTInt32MSB24:          // 32 bit data with 24 bit alignment
+                                                        DBUG(("Not yet implemented : please report the problem\n"));
+                                                        break;
+                                        }       
+                                        break;
+                                }
+
+                        case paUInt8:
+                                {
+                                        unsigned char *outBufPtr = (unsigned char *) outputBuffer;
+                                        
+                                        switch (nativeFormat) {
+                                                case ASIOSTInt16LSB:
+                                                        Output_IntU8_Int16(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset,userFrameOffset, swap);
+                                                        break;  
+                                                case ASIOSTInt16MSB:
+                                                        Output_IntU8_Int16(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset,userFrameOffset, !swap);
+                                                        break;  
+                                                case ASIOSTInt32LSB:
+                                                        Output_IntU8_Int32(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset,userFrameOffset, swap);
+                                                        break;
+                                                case ASIOSTInt32MSB:
+                                                        Output_IntU8_Int32(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset,userFrameOffset, !swap);
+                                                        break;  
+                                                case ASIOSTFloat32LSB:
+                                                        Output_IntU8_Float32(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset,userFrameOffset, swap);
+                                                        break;
+                                                case ASIOSTFloat32MSB:
+                                                        Output_IntU8_Float32(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset,userFrameOffset, !swap);
+                                                        break;  
+                                                
+                                                case ASIOSTInt24LSB:            // used for 20 bits as well
+                                                case ASIOSTInt24MSB:            // used for 20 bits as well
+                                                        
+                                                case ASIOSTFloat64LSB:          // IEEE 754 64 bit double float, as found on Intel x86 architecture
+                                                case ASIOSTFloat64MSB:          // IEEE 754 64 bit double float, as found on Intel x86 architecture
+
+                                                // these are used for 32 bit data buffer, with different alignment of the data inside
+                                                // 32 bit PCI bus systems can more easily used with these
+
+                                                case ASIOSTInt32LSB16:          // 32 bit data with 16 bit alignment
+                                                case ASIOSTInt32LSB18:          // 32 bit data with 18 bit alignment
+                                                case ASIOSTInt32LSB20:          // 32 bit data with 20 bit alignment
+                                                case ASIOSTInt32LSB24:          // 32 bit data with 24 bit alignment
+                                                                                                                                                                        
+                                        
+                                                case ASIOSTInt32MSB16:          // 32 bit data with 16 bit alignment
+                                                case ASIOSTInt32MSB18:          // 32 bit data with 18 bit alignment
+                                                case ASIOSTInt32MSB20:          // 32 bit data with 20 bit alignment
+                                                case ASIOSTInt32MSB24:          // 32 bit data with 24 bit alignment
+                                                        DBUG(("Not yet implemented : please report the problem\n"));
+                                                        break;
+                                        }       
+                                        break;
+                                }
+
+                        default:
+                                break;
+                        }               
+        }
+
+}
+
+
+
+/* Load a ASIO driver corresponding to the required device */
+static PaError Pa_ASIO_loadDevice (long device) 
+{
+        PaDeviceInfo * dev = &(sDevices[device].pad_Info);
+
+        if (!loadAsioDriver((char *) dev->name)) return paHostError;
+        if (ASIOInit(&asioDriverInfo.pahsc_driverInfo) != ASE_OK) return paHostError;
+        if (ASIOGetChannels(&asioDriverInfo.pahsc_NumInputChannels, &asioDriverInfo.pahsc_NumOutputChannels) != ASE_OK) return paHostError;
+        if (ASIOGetBufferSize(&asioDriverInfo.pahsc_minSize, &asioDriverInfo.pahsc_maxSize, &asioDriverInfo.pahsc_preferredSize, &asioDriverInfo.pahsc_granularity) != ASE_OK) return paHostError;
+        
+        if(ASIOOutputReady() == ASE_OK)
+                asioDriverInfo.pahsc_postOutput = true;
+        else
+                asioDriverInfo.pahsc_postOutput = false;
+                        
+        return paNoError;
+}
+
+//---------------------------------------------------
+static int GetHighestBitPosition( unsigned long n )
+{
+        int pos = -1;
+        while( n != 0 )
+        {
+                pos++;
+                n = n >> 1;
+        }
+        return pos;
+}
+
+//-----------------------------------------------------------------------------
+static int GetFirstMultiple(long min, long val ){  return ((min + val - 1) / val) * val; }
+
+//-----------------------------------------------------------------------------
+static int GetFirstPossibleDivisor(long max, long val )
+{ 
+       for (int i = 2; i < 20; i++) {if (((val%i) == 0) && ((val/i) <= max)) return (val/i); }
+       return val;
+}
+
+//------------------------------------------------------------------------
+static int IsPowerOfTwo( unsigned long n ) { return ((n & (n-1)) == 0); }
+
+
+/*******************************************************************
+* Determine size of native ASIO audio buffer size
+* Input parameters : FramesPerUserBuffer, NumUserBuffers 
+* Output values : FramesPerHostBuffer, OutputBufferOffset or InputtBufferOffset
+*/
+
+static PaError PaHost_CalcNumHostBuffers( internalPortAudioStream *past )
+{
+        PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData;
+        long requestedBufferSize;
+        long firstMultiple, firstDivisor;
+        
+        // Compute requestedBufferSize 
+        if( past->past_NumUserBuffers < 1 ){
+                requestedBufferSize = past->past_FramesPerUserBuffer;           
+        }else{
+                requestedBufferSize = past->past_NumUserBuffers * past->past_FramesPerUserBuffer;
+        }
+        
+        // Adjust FramesPerHostBuffer using requestedBufferSize, ASIO minSize and maxSize, 
+        if (requestedBufferSize < asioDriverInfo.pahsc_minSize){
+        
+                       firstMultiple = GetFirstMultiple(asioDriverInfo.pahsc_minSize, requestedBufferSize);
+                       
+                       if (firstMultiple <= asioDriverInfo.pahsc_maxSize)
+                                       asioDriverInfo.past_FramesPerHostBuffer = firstMultiple;
+                       else
+                                       asioDriverInfo.past_FramesPerHostBuffer = asioDriverInfo.pahsc_minSize;
+                                       
+        }else if (requestedBufferSize > asioDriverInfo.pahsc_maxSize){
+               
+                       firstDivisor = GetFirstPossibleDivisor(asioDriverInfo.pahsc_maxSize, requestedBufferSize);
+                       
+                       if ((firstDivisor >= asioDriverInfo.pahsc_minSize) && (firstDivisor <= asioDriverInfo.pahsc_maxSize))
+                               asioDriverInfo.past_FramesPerHostBuffer = firstDivisor;
+                       else
+                                       asioDriverInfo.past_FramesPerHostBuffer = asioDriverInfo.pahsc_maxSize;
+        }else{
+                asioDriverInfo.past_FramesPerHostBuffer = requestedBufferSize;
+        }
+        
+        // If ASIO buffer size needs to be a power of two
+        if( asioDriverInfo.pahsc_granularity < 0 ){
+                // Needs to be a power of two.
+                
+                if( !IsPowerOfTwo( asioDriverInfo.past_FramesPerHostBuffer ) )
+                {
+                        int highestBit = GetHighestBitPosition(asioDriverInfo.past_FramesPerHostBuffer);
+                        asioDriverInfo.past_FramesPerHostBuffer = 1 << (highestBit + 1);
+                }
+        }
+        
+        DBUG(("----------------------------------\n"));
+        DBUG(("PortAudio : minSize = %ld \n",asioDriverInfo.pahsc_minSize));
+        DBUG(("PortAudio : preferredSize = %ld \n",asioDriverInfo.pahsc_preferredSize));
+        DBUG(("PortAudio : maxSize = %ld \n",asioDriverInfo.pahsc_maxSize));
+        DBUG(("PortAudio : granularity = %ld \n",asioDriverInfo.pahsc_granularity));
+        DBUG(("PortAudio : User buffer size = %d\n", asioDriverInfo.past->past_FramesPerUserBuffer ));
+        DBUG(("PortAudio : ASIO buffer size = %d\n", asioDriverInfo.past_FramesPerHostBuffer ));
+        
+        if (asioDriverInfo.past_FramesPerHostBuffer > past->past_FramesPerUserBuffer){
+        
+                // Computes the MINIMUM value of null frames shift for the output buffer alignement
+                asioDriverInfo.pahsc_OutputBufferOffset = Pa_ASIO_CalcFrameShift (asioDriverInfo.past_FramesPerHostBuffer,past->past_FramesPerUserBuffer);
+                asioDriverInfo.pahsc_InputBufferOffset = 0;
+                DBUG(("PortAudio : Minimum BufferOffset for Output = %d\n", asioDriverInfo.pahsc_OutputBufferOffset));
+        }else{
+        
+                //Computes the MINIMUM value of null frames shift for the input buffer alignement
+                asioDriverInfo.pahsc_InputBufferOffset = Pa_ASIO_CalcFrameShift (asioDriverInfo.past_FramesPerHostBuffer,past->past_FramesPerUserBuffer);
+                asioDriverInfo.pahsc_OutputBufferOffset = 0;
+                DBUG(("PortAudio : Minimum BufferOffset for Input = %d\n", asioDriverInfo.pahsc_InputBufferOffset));
+        }
+        
+        return paNoError;
+}
+
+
+/***********************************************************************/
+int Pa_CountDevices()
+{
+        PaError err ;
+        
+        if( sNumDevices <= 0 ) 
+        {
+                /* Force loading of ASIO drivers  */
+                err = Pa_ASIO_QueryDeviceInfo(sDevices);
+                if( err != paNoError ) goto error;
+        }
+        
+        return sNumDevices;
+        
+error:
+        PaHost_Term();
+        DBUG(("Pa_CountDevices: returns %d\n", err ));
+        return err;
+}
+
+/***********************************************************************/
+PaError PaHost_Init( void )
+{
+        /* Have we already initialized the device info? */
+        return  (Pa_CountDevices() < 0 ) ? paHostError : paNoError;
+}
+
+/***********************************************************************/
+PaError PaHost_Term( void )
+{       
+        int           i;
+        PaDeviceInfo *dev;
+        double       *rates;
+         PaError      result = paNoError;
+        
+        /* Free allocated sample rate arrays  and names*/
+        for( i=0; i<sNumDevices; i++ ){
+                dev =  &sDevices[i].pad_Info;
+                rates = (double *) dev->sampleRates;
+                if((rates != NULL)) PaHost_FreeFastMemory(rates, MAX_NUMSAMPLINGRATES * sizeof(double)); 
+                dev->sampleRates = NULL;
+                if(dev->name != NULL) PaHost_FreeFastMemory((void *) dev->name, 32);
+                dev->name = NULL;
+        }
+        
+        sNumDevices = 0;
+        
+         /* Dispose : if not done by Pa_CloseStream    */
+        if(ASIODisposeBuffers() != ASE_OK) result = paHostError;        
+        if(ASIOExit() != ASE_OK) result = paHostError;
+        
+        /* remove the loaded ASIO driver */
+        asioDrivers->removeCurrentDriver();
+
+        return result;
+}
+
+/***********************************************************************/
+PaError PaHost_OpenStream( internalPortAudioStream   *past )
+{
+        PaError             result = paNoError;
+        ASIOError                       err;
+        int32                           device;
+        
+        /*  TO DO : Check if a stream already runs */
+        //if (asioDriverInfo.past != NULL) return paHostError; (does not work)
+                        
+        /* Check the device number */
+        if ((past->past_InputDeviceID != paNoDevice)
+                &&(past->past_OutputDeviceID != paNoDevice)
+                &&(past->past_InputDeviceID != past->past_OutputDeviceID))
+        {
+                return paInvalidDeviceId;
+        }
+
+        /* Allocation */        
+        memset(&asioDriverInfo, 0, sizeof(PaHostSoundControl));
+        past->past_DeviceData = (void*) &asioDriverInfo;
+        
+
+        /* FIXME */
+        asioDriverInfo.past = past;
+        
+        /* load the ASIO device */
+        device = (past->past_InputDeviceID < 0) ? past->past_OutputDeviceID : past->past_InputDeviceID;
+        result = Pa_ASIO_loadDevice(device);
+        if (result != paNoError) goto error;
+                
+        /* Check ASIO parameters and input parameters */
+        if ((past->past_NumInputChannels > asioDriverInfo.pahsc_NumInputChannels) 
+                || (past->past_NumOutputChannels > asioDriverInfo.pahsc_NumOutputChannels)) {
+                result = paInvalidChannelCount;
+                goto error;
+        }
+        
+        /* Set sample rate */
+        if (ASIOSetSampleRate(past->past_SampleRate) != ASE_OK) {
+                result = paInvalidSampleRate;
+                goto error;
+        }
+        
+        /* if OK calc buffer size */
+        result = PaHost_CalcNumHostBuffers( past );
+        if (result != paNoError) goto error;
+        
+           
+        /* 
+        Allocating input and output buffers number for the real past_NumInputChannels and past_NumOutputChannels
+        optimize the data transfer.
+        */      
+        
+        asioDriverInfo.pahsc_NumInputChannels = past->past_NumInputChannels;
+        asioDriverInfo.pahsc_NumOutputChannels = past->past_NumOutputChannels;
+        
+        /* Allocate ASIO buffers and callback*/
+        err = Pa_ASIO_CreateBuffers(&asioDriverInfo,
+                asioDriverInfo.pahsc_NumInputChannels,
+                asioDriverInfo.pahsc_NumOutputChannels,
+                asioDriverInfo.past_FramesPerHostBuffer);
+    
+        if (err == ASE_OK)
+                return paNoError;
+        else if (err == ASE_NoMemory) 
+                result = paInsufficientMemory;
+        else if (err == ASE_InvalidParameter) 
+                result = paInvalidChannelCount;
+        else if (err == ASE_InvalidMode) 
+                result = paBufferTooBig;
+        else 
+                result = paHostError;
+                
+error:
+                ASIOExit();
+                return result;
+
+}
+
+/***********************************************************************/
+PaError PaHost_CloseStream( internalPortAudioStream   *past )
+{
+        PaHostSoundControl *pahsc;
+        PaError             result = paNoError;
+
+        if( past == NULL ) return paBadStreamPtr;
+        pahsc = (PaHostSoundControl *) past->past_DeviceData;
+        if( pahsc == NULL ) return paNoError;
+
+        #if PA_TRACE_START_STOP
+         AddTraceMessage( "PaHost_CloseStream: pahsc_HWaveOut ", (int) pahsc->pahsc_HWaveOut );
+        #endif
+        
+        /* Dispose */
+        if(ASIODisposeBuffers() != ASE_OK) result = paHostError;        
+        if(ASIOExit() != ASE_OK) result = paHostError;
+        
+        /* Free data and device for output. */
+        past->past_DeviceData = NULL;
+        asioDriverInfo.past = NULL;
+                
+        return result;
+}
+
+/***********************************************************************/
+PaError PaHost_StartOutput( internalPortAudioStream   *past )
+{
+        /* Clear the index 0 host output buffer */
+       Pa_ASIO_Clear_Output(asioDriverInfo.bufferInfos, 
+               asioDriverInfo.pahsc_channelInfos[0].type,
+               asioDriverInfo.pahsc_NumInputChannels,
+               asioDriverInfo.pahsc_NumOutputChannels,
+               0, 
+               0, 
+               asioDriverInfo.past_FramesPerHostBuffer);
+
+               /* Clear the index 1 host output buffer */
+       Pa_ASIO_Clear_Output(asioDriverInfo.bufferInfos, 
+               asioDriverInfo.pahsc_channelInfos[0].type,
+               asioDriverInfo.pahsc_NumInputChannels,
+               asioDriverInfo.pahsc_NumOutputChannels,
+               1, 
+               0, 
+               asioDriverInfo.past_FramesPerHostBuffer);
+               
+        Pa_ASIO_Clear_User_Buffers();
+        
+        Pa_ASIO_Adaptor_Init();
+
+        return paNoError;
+}
+
+/***********************************************************************/
+PaError PaHost_StopOutput( internalPortAudioStream   *past, int abort )
+{
+        /* Nothing to do ?? */
+        return paNoError;
+}
+
+/***********************************************************************/
+PaError PaHost_StartInput( internalPortAudioStream   *past )
+{
+        /* Nothing to do ?? */
+        return paNoError;
+}
+
+/***********************************************************************/
+PaError PaHost_StopInput( internalPortAudioStream   *past, int abort )
+{
+        /* Nothing to do */
+        return paNoError;
+}
+
+/***********************************************************************/
+PaError PaHost_StartEngine( internalPortAudioStream   *past )
+{
+           // TO DO : count of samples
+        past->past_IsActive = 1;
+        return (ASIOStart() == ASE_OK) ? paNoError : paHostError;
+}
+
+/***********************************************************************/
+PaError PaHost_StopEngine( internalPortAudioStream *past, int abort )
+{
+        // TO DO :  count of samples
+        past->past_IsActive = 0;
+        return (ASIOStop() == ASE_OK) ? paNoError : paHostError;
+}
+
+/***********************************************************************/
+// TO BE CHECKED 
+PaError PaHost_StreamActive( internalPortAudioStream   *past )
+{
+        PaHostSoundControl *pahsc;
+        if( past == NULL ) return paBadStreamPtr;
+        pahsc = (PaHostSoundControl *) past->past_DeviceData;
+        if( pahsc == NULL ) return paInternalError;
+        return (PaError) past->past_IsActive;
+}
+
+/*************************************************************************/
+PaTimestamp Pa_StreamTime( PortAudioStream *stream )
+{
+        PaHostSoundControl *pahsc;
+        internalPortAudioStream   *past = (internalPortAudioStream *) stream;
+        if( past == NULL ) return paBadStreamPtr;
+        pahsc = (PaHostSoundControl *) past->past_DeviceData;
+        return pahsc->pahsc_NumFramesDone;
+}
+
+/*************************************************************************
+ * Allocate memory that can be accessed in real-time.
+ * This may need to be held in physical memory so that it is not
+ * paged to virtual memory.
+ * This call MUST be balanced with a call to PaHost_FreeFastMemory().
+ */
+void *PaHost_AllocateFastMemory( long numBytes )
+{
+        #if MAC
+                void *addr = NewPtrClear( numBytes );
+                if( (addr == NULL) || (MemError () != 0) ) return NULL;
+                        
+                #if (CARBON_COMPATIBLE == 0)
+                if( HoldMemory( addr, numBytes ) != noErr )
+                {
+                        DisposePtr( (Ptr) addr );
+                        return NULL;
+                }
+                #endif
+                return addr;
+        #elif WINDOWS
+                void *addr = malloc( numBytes ); /* FIXME - do we need physical memory? */
+                if( addr != NULL ) memset( addr, 0, numBytes );
+                return addr;
+        #endif
+}
+
+/*************************************************************************
+ * Free memory that could be accessed in real-time.
+ * This call MUST be balanced with a call to PaHost_AllocateFastMemory().
+ */
+void PaHost_FreeFastMemory( void *addr, long numBytes )
+{
+        #if MAC
+                if( addr == NULL ) return;
+                #if CARBON_COMPATIBLE
+                (void) numBytes;
+                #else
+                UnholdMemory( addr, numBytes );
+                #endif
+                DisposePtr( (Ptr) addr );
+        #elif WINDOWS
+                if( addr != NULL ) free( addr );
+        #endif
+}
+
+
+/*************************************************************************/
+void Pa_Sleep( long msec )
+{
+        #if MAC
+                int32 sleepTime, endTime;
+                /* Convert to ticks. Round up so we sleep a MINIMUM of msec time. */
+                sleepTime = ((msec * 60) + 999) / 1000;
+                if( sleepTime < 1 ) sleepTime = 1;
+                endTime = TickCount() + sleepTime;
+                do{
+                        DBUGX(("Sleep for %d ticks.\n", sleepTime ));
+                        WaitNextEvent( 0, NULL, sleepTime, NULL );  /* Use this just to sleep without getting events. */
+                        sleepTime = endTime - TickCount();
+                } while( sleepTime > 0 );
+        #elif WINDOWS
+                Sleep( msec );
+        #endif 
+}
+
+/*************************************************************************/
+const PaDeviceInfo* Pa_GetDeviceInfo( PaDeviceID id )
+{
+        if( (id < 0) || ( id >= Pa_CountDevices()) ) return NULL;
+        return &sDevices[id].pad_Info;
+}
+
+/*************************************************************************/
+PaDeviceID Pa_GetDefaultInputDeviceID( void )
+{
+        return sDefaultInputDeviceID;
+}
+
+/*************************************************************************/
+PaDeviceID Pa_GetDefaultOutputDeviceID( void )
+{
+        return sDefaultOutputDeviceID;
+}
+
+/*************************************************************************/
+int Pa_GetMinNumBuffers( int framesPerUserBuffer, double sampleRate )
+{
+        // TO BE IMPLEMENTED : using the ASIOGetLatency call??
+        return 2;
+}
+
+/*************************************************************************/
+int32 Pa_GetHostError( void )
+{
+        int32 err = sPaHostError;
+        sPaHostError = 0;
+        return err;
+}
+
+
+#ifdef MAC                                      
+
+/**************************************************************************/
+static void Pa_StartUsageCalculation( internalPortAudioStream   *past )
+{
+        PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData;
+        UnsignedWide widePad;
+        if( pahsc == NULL ) return;
+/* Query system timer for usage analysis and to prevent overuse of CPU. */
+        Microseconds( &widePad );
+        pahsc->pahsc_EntryCount = UnsignedWideToUInt64( widePad );
+}
+/**************************************************************************/
+static void Pa_EndUsageCalculation( internalPortAudioStream   *past )
+{
+        UnsignedWide widePad;
+        UInt64    CurrentCount;
+        long      InsideCount;
+        long      TotalCount;
+        PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData;
+        if( pahsc == NULL ) return;
+/* 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)
+#define LOWPASS_COEFFICIENT_1   (0.99999 - LOWPASS_COEFFICIENT_0)
+        Microseconds( &widePad );
+        CurrentCount = UnsignedWideToUInt64( widePad );
+        if( past->past_IfLastExitValid )
+        {
+                InsideCount = (long) U64Subtract(CurrentCount, pahsc->pahsc_EntryCount);
+                TotalCount  = (long) U64Subtract(CurrentCount, pahsc->pahsc_LastExitCount);
+/* 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;
+}
+
+#elif WINDOWS
+
+/********************************* BEGIN CPU UTILIZATION MEASUREMENT ****/
+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 );
+}
+
+static void Pa_EndUsageCalculation( internalPortAudioStream   *past )
+{
+        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)
+#define LOWPASS_COEFFICIENT_1   (0.99999 - LOWPASS_COEFFICIENT_0)
+
+        PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData;
+        if( pahsc == NULL ) return;
+
+        if( QueryPerformanceCounter( &CurrentCount ) )
+        {
+                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;
+        }
+}
+
+#endif
+
+
+
+
diff --git a/pa_beos/PlaybackNode.cc b/pa_beos/PlaybackNode.cc
new file mode 100644 (file)
index 0000000..84d5453
--- /dev/null
@@ -0,0 +1,538 @@
+/*
+ * $Id$
+ * PortAudio Portable Real-Time Audio Library
+ * Latest Version at: http://www.portaudio.com
+ * BeOS Media Kit Implementation by Joshua Haberman
+ *
+ * Copyright (c) 2001 Joshua Haberman <joshua@haberman.com>
+ *
+ * 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.
+ *
+ * ---
+ *
+ * Significant portions of this file are based on sample code from Be. The
+ * Be Sample Code Licence follows:
+ *
+ *    Copyright 1991-1999, Be Incorporated.
+ *    All rights reserved.
+ *
+ *    Redistribution and use in source and binary forms, with or without
+ *    modification, are permitted provided that the following conditions
+ *    are met:
+ *
+ *    1. Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions, and the following disclaimer.
+ *
+ *    2. Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions, and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *
+ *    3. The name of the author may not be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ *    THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR
+ *    IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ *    OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ *    PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ *    DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ *    (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ *    LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ *    AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ *    TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ *    OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.    
+ */
+
+#include <stdio.h>
+
+#include <be/media/BufferGroup.h>
+#include <be/media/Buffer.h>
+#include <be/media/TimeSource.h>
+
+#include "PlaybackNode.h"
+
+#define PRINT(x) { printf x; fflush(stdout); }
+
+#ifdef DEBUG
+#define DBUG(x)  PRINT(x)
+#else
+#define DBUG(x)
+#endif
+
+
+PaPlaybackNode::PaPlaybackNode(uint32 channels, float frame_rate, uint32 frames_per_buffer,
+                               PortAudioCallback* callback, void *user_data) :
+        BMediaNode("PortAudio input node"),
+        BBufferProducer(B_MEDIA_RAW_AUDIO),
+        BMediaEventLooper(),
+        mAborted(false),
+        mRunning(false),
+        mBufferGroup(NULL),
+        mDownstreamLatency(0),
+        mStartTime(0),
+        mCallback(callback),
+        mUserData(user_data),
+        mFramesPerBuffer(frames_per_buffer)
+{
+    DBUG(("Constructor called.\n"));
+
+    mPreferredFormat.type = B_MEDIA_RAW_AUDIO;
+    mPreferredFormat.u.raw_audio.channel_count = channels;
+    mPreferredFormat.u.raw_audio.frame_rate = frame_rate;
+    mPreferredFormat.u.raw_audio.byte_order =
+        (B_HOST_IS_BENDIAN) ? B_MEDIA_BIG_ENDIAN : B_MEDIA_LITTLE_ENDIAN;
+    mPreferredFormat.u.raw_audio.buffer_size =
+        media_raw_audio_format::wildcard.buffer_size;
+
+    mOutput.destination = media_destination::null;
+    mOutput.format = mPreferredFormat;
+
+    /* The amount of time it takes for this node to produce a buffer when
+     * asked. Essentially, it is how long the user's callback takes to run.
+     * We set this to be the length of the sound data each buffer of the
+     * requested size can hold. */
+    //mInternalLatency = (bigtime_t)(1000000 * frames_per_buffer / frame_rate);
+
+    /* ACK! it seems that the mixer (at least on my machine) demands that IT
+        * specify the buffer size, so for now I'll just make a generic guess here */
+    mInternalLatency = 1000000 / 20;
+}
+
+
+
+PaPlaybackNode::~PaPlaybackNode()
+{
+    DBUG(("Destructor called.\n"));
+    Quit();   /* Stop the BMediaEventLooper thread */
+}
+
+
+/*************************
+ *
+ *  Local methods
+ *
+ */
+
+bool PaPlaybackNode::IsRunning()
+{
+    return mRunning;
+}
+
+
+PaTimestamp PaPlaybackNode::GetStreamTime()
+{
+    BTimeSource *timeSource = TimeSource();
+    PaTimestamp time = (timeSource->Now() - mStartTime) *
+                       mPreferredFormat.u.raw_audio.frame_rate / 1000000;
+    return time;
+}
+
+
+void PaPlaybackNode::SetSampleFormat(PaSampleFormat inFormat,
+                                     PaSampleFormat outFormat)
+{
+    uint32 beOutFormat;
+
+    switch(outFormat)
+    {
+    case paFloat32:
+        beOutFormat = media_raw_audio_format::B_AUDIO_FLOAT;
+        mOutputSampleWidth = 4;
+        break;
+
+    case paInt16:
+        beOutFormat = media_raw_audio_format::B_AUDIO_SHORT;
+        mOutputSampleWidth = 2;
+        break;
+
+    case paInt32:
+        beOutFormat = media_raw_audio_format::B_AUDIO_INT;
+        mOutputSampleWidth = 4;
+        break;
+
+    case paInt8:
+        beOutFormat = media_raw_audio_format::B_AUDIO_CHAR;
+        mOutputSampleWidth = 1;
+        break;
+
+    case paUInt8:
+        beOutFormat = media_raw_audio_format::B_AUDIO_UCHAR;
+        mOutputSampleWidth = 1;
+        break;
+
+    case paInt24:
+    case paPackedInt24:
+    case paCustomFormat:
+        DBUG(("Unsupported output format: %x\n", outFormat));
+        break;
+
+    default:
+        DBUG(("Unknown output format: %x\n", outFormat));
+    }
+
+    mPreferredFormat.u.raw_audio.format = beOutFormat;
+    mFramesPerBuffer * mPreferredFormat.u.raw_audio.channel_count * mOutputSampleWidth;
+}
+
+BBuffer *PaPlaybackNode::FillNextBuffer(bigtime_t time)
+{
+    /* Get a buffer from the buffer group */
+    BBuffer *buf = mBufferGroup->RequestBuffer(
+                       mOutput.format.u.raw_audio.buffer_size, BufferDuration());
+    unsigned long frames = mOutput.format.u.raw_audio.buffer_size /
+                           mOutputSampleWidth / mOutput.format.u.raw_audio.channel_count;
+    bigtime_t start_time;
+    int ret;
+
+    if( !buf )
+    {
+        DBUG(("Unable to allocate a buffer\n"));
+        return NULL;
+    }
+
+    start_time = mStartTime +
+                 (bigtime_t)((double)mSamplesSent /
+                             (double)mOutput.format.u.raw_audio.frame_rate /
+                             (double)mOutput.format.u.raw_audio.channel_count *
+                             1000000.0);
+
+    /* Now call the user callback to get the data */
+    ret = mCallback(NULL,       /* Input buffer */
+                    buf->Data(),      /* Output buffer */
+                    frames,           /* Frames per buffer */
+                    mSamplesSent / mOutput.format.u.raw_audio.channel_count, /* timestamp */
+                    mUserData);
+
+    if( ret )
+        mAborted = true;
+
+    media_header *hdr = buf->Header();
+
+    hdr->type = B_MEDIA_RAW_AUDIO;
+    hdr->size_used = mOutput.format.u.raw_audio.buffer_size;
+    hdr->time_source = TimeSource()->ID();
+    hdr->start_time = start_time;
+
+    return buf;
+}
+
+
+
+
+/*************************
+ *
+ *  BMediaNode methods
+ *
+ */
+
+BMediaAddOn *PaPlaybackNode::AddOn( int32 * ) const
+{
+    DBUG(("AddOn() called.\n"));
+    return NULL;  /* we don't provide service to outside applications */
+}
+
+
+status_t PaPlaybackNode::HandleMessage( int32 message, const void *data,
+                                        size_t size )
+{
+    DBUG(("HandleMessage() called.\n"));
+    return B_ERROR;  /* we don't define any custom messages */
+}
+
+
+
+
+/*************************
+ *
+ *  BMediaEventLooper methods
+ *
+ */
+
+void PaPlaybackNode::NodeRegistered()
+{
+    DBUG(("NodeRegistered() called.\n"));
+
+    /* Start the BMediaEventLooper thread */
+    SetPriority(B_REAL_TIME_PRIORITY);
+    Run();
+
+    /* set up as much information about our output as we can */
+    mOutput.source.port = ControlPort();
+    mOutput.source.id = 0;
+    mOutput.node = Node();
+    ::strcpy(mOutput.name, "PortAudio Playback");
+}
+
+
+void PaPlaybackNode::HandleEvent( const media_timed_event *event,
+                                  bigtime_t lateness, bool realTimeEvent )
+{
+    // DBUG(("HandleEvent() called.\n"));
+    status_t err;
+
+    switch(event->type)
+    {
+    case BTimedEventQueue::B_START:
+        DBUG(("   Handling a B_START event\n"));
+        if( RunState() != B_STARTED )
+        {
+            mStartTime = event->event_time + EventLatency();
+            mSamplesSent = 0;
+            mAborted = false;
+            mRunning = true;
+            media_timed_event firstEvent( mStartTime,
+                                          BTimedEventQueue::B_HANDLE_BUFFER );
+            EventQueue()->AddEvent( firstEvent );
+        }
+        break;
+
+    case BTimedEventQueue::B_STOP:
+        DBUG(("   Handling a B_STOP event\n"));
+        mRunning = false;
+        EventQueue()->FlushEvents( 0, BTimedEventQueue::B_ALWAYS, true,
+                                   BTimedEventQueue::B_HANDLE_BUFFER );
+        break;
+
+    case BTimedEventQueue::B_HANDLE_BUFFER:
+        //DBUG(("   Handling a B_HANDLE_BUFFER event\n"));
+
+        /* make sure we're started and connected */
+        if( RunState() != BMediaEventLooper::B_STARTED ||
+                mOutput.destination == media_destination::null )
+            break;
+
+        BBuffer *buffer = FillNextBuffer(event->event_time);
+
+        /* make sure we weren't aborted while this routine was running.
+         * this can happen in one of two ways: either the callback returned
+         * nonzero (in which case mAborted is set in FillNextBuffer() ) or
+         * the client called AbortStream */
+        if( mAborted )
+        {
+            if( buffer )
+                buffer->Recycle();
+            Stop(0, true);
+            break;
+        }
+
+        if( buffer )
+        {
+            err = SendBuffer(buffer, mOutput.destination);
+            if( err != B_OK )
+                buffer->Recycle();
+        }
+
+        mSamplesSent += mOutput.format.u.raw_audio.buffer_size / mOutputSampleWidth;
+
+        /* Now schedule the next buffer event, so we can send another
+         * buffer when this one runs out. We calculate when it should
+         * happen by calculating when the data we just sent will finish
+         * playing.
+         *
+         * NOTE, however, that the event will actually get generated
+         * earlier than we specify, to account for the latency it will
+         * take to produce the buffer. It uses the latency value we
+         * specified in SetEventLatency() to determine just how early
+         * to generate it. */
+
+        /* totalPerformanceTime includes the time represented by the buffer
+         * we just sent */
+        bigtime_t totalPerformanceTime = (bigtime_t)((double)mSamplesSent /
+                                         (double)mOutput.format.u.raw_audio.channel_count /
+                                         (double)mOutput.format.u.raw_audio.frame_rate * 1000000.0);
+
+        bigtime_t nextEventTime = mStartTime + totalPerformanceTime;
+
+        media_timed_event nextBufferEvent(nextEventTime,
+                                          BTimedEventQueue::B_HANDLE_BUFFER);
+        EventQueue()->AddEvent(nextBufferEvent);
+
+        break;
+
+    }
+}
+
+
+
+
+/*************************
+ *
+ *  BBufferProducer methods
+ *
+ */
+
+status_t PaPlaybackNode::FormatSuggestionRequested( media_type type,
+        int32 /*quality*/, media_format* format )
+{
+    /* the caller wants to know this node's preferred format and provides
+     * a suggestion, asking if we support it */
+    DBUG(("FormatSuggestionRequested() called.\n"));
+
+    if(!format)
+        return B_BAD_VALUE;
+
+    *format = mPreferredFormat;
+
+    /* we only support raw audio (a wildcard is okay too) */
+    if ( type == B_MEDIA_UNKNOWN_TYPE || type == B_MEDIA_RAW_AUDIO )
+        return B_OK;
+    else
+        return B_MEDIA_BAD_FORMAT;
+}
+
+
+status_t PaPlaybackNode::FormatProposal( const media_source& output,
+        media_format* format )
+{
+    /* This is similar to FormatSuggestionRequested(), but it is actually part
+     * of the negotiation process. We're given the opportunity to specify any
+     * properties that are wildcards (ie. properties that the other node doesn't
+     * care one way or another about) */
+    DBUG(("FormatProposal() called.\n"));
+
+    /* Make sure this proposal really applies to our output */
+    if( output != mOutput.source )
+        return B_MEDIA_BAD_SOURCE;
+
+    /* We return two things: whether we support the proposed format, and our own
+     * preferred format */
+    *format = mPreferredFormat;
+
+    if( format->type == B_MEDIA_UNKNOWN_TYPE || format->type == B_MEDIA_RAW_AUDIO )
+        return B_OK;
+    else
+        return B_MEDIA_BAD_FORMAT;
+}
+
+
+status_t PaPlaybackNode::FormatChangeRequested( const media_source& source,
+        const media_destination& destination, media_format* io_format, int32* )
+{
+    /* we refuse to change formats, supporting only 1 */
+    DBUG(("FormatChangeRequested() called.\n"));
+
+    return B_ERROR;
+}
+
+
+status_t PaPlaybackNode::GetNextOutput( int32* cookie, media_output* out_output )
+{
+    /* this is where we allow other to enumerate our outputs -- the cookie is
+     * an integer we can use to keep track of where we are in enumeration. */
+    DBUG(("GetNextOutput() called.\n"));
+
+    if( *cookie == 0 )
+    {
+        *out_output = mOutput;
+        *cookie = 1;
+        return B_OK;
+    }
+
+    return B_BAD_INDEX;
+}
+
+
+status_t PaPlaybackNode::DisposeOutputCookie( int32 cookie )
+{
+    DBUG(("DisposeOutputCookie() called.\n"));
+    return B_OK;
+}
+
+
+void PaPlaybackNode::LateNoticeReceived( const media_source& what,
+        bigtime_t how_much, bigtime_t performance_time )
+{
+    /* This function is called as notification that a buffer we sent wasn't
+     * received by the time we stamped it with -- it got there late. Basically,
+     * it means we underestimated our own latency, so we should increase it */
+    DBUG(("LateNoticeReceived() called.\n"));
+
+    if( what != mOutput.source )
+        return;
+
+    if( RunMode() == B_INCREASE_LATENCY )
+    {
+        mInternalLatency += how_much;
+        SetEventLatency( mDownstreamLatency + mInternalLatency );
+        DBUG(("Increasing latency to %Ld\n", mDownstreamLatency + mInternalLatency));
+    }
+    else
+        DBUG(("I don't know what to do with this notice!"));
+}
+
+
+void PaPlaybackNode::EnableOutput( const media_source& what, bool enabled,
+                                   int32* )
+{
+    DBUG(("EnableOutput() called.\n"));
+    /* stub -- we don't support this yet */
+}
+
+
+status_t PaPlaybackNode::PrepareToConnect( const media_source& what,
+        const media_destination& where, media_format* format,
+        media_source* out_source, char* out_name )
+{
+    /* the final stage of format negotiations. here we _must_ make specific any
+     * remaining wildcards */
+    DBUG(("PrepareToConnect() called.\n"));
+
+    /* make sure this really refers to our source */
+    if( what != mOutput.source )
+        return B_MEDIA_BAD_SOURCE;
+
+    /* make sure we're not already connected */
+    if( mOutput.destination != media_destination::null )
+        return B_MEDIA_ALREADY_CONNECTED;
+
+    if( format->type != B_MEDIA_RAW_AUDIO )
+        return B_MEDIA_BAD_FORMAT;
+
+    if( format->u.raw_audio.format != mPreferredFormat.u.raw_audio.format )
+        return B_MEDIA_BAD_FORMAT;
+
+    if( format->u.raw_audio.buffer_size ==
+            media_raw_audio_format::wildcard.buffer_size )
+    {
+        DBUG(("We were left to decide buffer size: choosing 2048"));
+        format->u.raw_audio.buffer_size = 2048;
+    }
+    else
+        DBUG(("Using consumer specified buffer size of %lu.\n",
+              format->u.raw_audio.buffer_size));
+
+    /* Reserve the connection, return the information */
+    mOutput.destination = where;
+    mOutput.format      = *format;
+    *out_source         = mOutput.source;
+    strncpy( out_name, mOutput.name, B_MEDIA_NAME_LENGTH );
+
+    return B_OK;
+}
+
+
+void PaPlaybackNode::Connect(status_t error, const media_source& source,
+                             const media_destination& destination, const media_format& format, char* io_name)
+{
+    DBUG(("Connect() called.\n"));
+
diff --git a/pa_beos/PlaybackNode.h b/pa_beos/PlaybackNode.h
new file mode 100644 (file)
index 0000000..64ee5b3
--- /dev/null
@@ -0,0 +1,108 @@
+/*
+ * $Id$
+ * PortAudio Portable Real-Time Audio Library
+ * Latest Version at: http://www.portaudio.com
+ * BeOS Media Kit Implementation by Joshua Haberman
+ *
+ * Copyright (c) 2001 Joshua Haberman <joshua@haberman.com>
+ *
+ * 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.
+ *
+ */
+
+#include <be/media/MediaRoster.h>
+#include <be/media/MediaEventLooper.h>
+#include <be/media/BufferProducer.h>
+
+#include "portaudio.h"
+
+class PaPlaybackNode :
+            public BBufferProducer,
+            public BMediaEventLooper
+{
+
+public:
+    PaPlaybackNode( uint32 channels, float frame_rate, uint32 frames_per_buffer,
+                    PortAudioCallback *callback, void *user_data );
+    ~PaPlaybackNode();
+
+
+    /* Local methods ******************************************/
+
+    BBuffer *FillNextBuffer(bigtime_t time);
+    void SetSampleFormat(PaSampleFormat inFormat, PaSampleFormat outFormat);
+    bool IsRunning();
+    PaTimestamp GetStreamTime();
+
+    /* BMediaNode methods *************************************/
+
+    BMediaAddOn* AddOn( int32 * ) const;
+    status_t HandleMessage( int32 message, const void *data, size_t size );
+
+    /* BMediaEventLooper methods ******************************/
+
+    void HandleEvent( const media_timed_event *event, bigtime_t lateness,
+                      bool realTimeEvent );
+    void NodeRegistered();
+
+    /* BBufferProducer methods ********************************/
+
+    status_t FormatSuggestionRequested( media_type type, int32 quality,
+                                        media_format* format );
+    status_t FormatProposal( const media_source& output, media_format* format );
+    status_t FormatChangeRequested( const media_source& source,
+                                    const media_destination& destination, media_format* io_format, int32* );
+
+    status_t GetNextOutput( int32* cookie, media_output* out_output );
+    status_t DisposeOutputCookie( int32 cookie );
+
+    void LateNoticeReceived( const media_source& what, bigtime_t how_much,
+                             bigtime_t performance_time );
+    void EnableOutput( const media_source& what, bool enabled, int32* _deprecated_ );
+
+    status_t PrepareToConnect( const media_source& what,
+                               const media_destination& where, media_format* format,
+                               media_source* out_source, char* out_name );
+    void Connect(status_t error, const media_source& source,
+                 const media_destination& destination, const media_format& format,
+                 char* io_name);
+    void Disconnect(const media_source& what, const media_destination& where);
+
+    status_t SetBufferGroup(const media_source& for_source, BBufferGroup* newGroup);
+
+    bool         mAborted;
+
+private:
+    media_output mOutput;
+    media_format mPreferredFormat;
+    uint32       mOutputSampleWidth, mFramesPerBuffer;
+    BBufferGroup *mBufferGroup;
+    bigtime_t    mDownstreamLatency, mInternalLatency, mStartTime;
+    uint64       mSamplesSent;
+    PortAudioCallback *mCallback;
+    void         *mUserData;
+    bool         mRunning;
+
+};
+
diff --git a/pa_beos/pa_beos_mk.cc b/pa_beos/pa_beos_mk.cc
new file mode 100644 (file)
index 0000000..287ec55
--- /dev/null
@@ -0,0 +1,441 @@
+/*
+ * $Id$
+ * PortAudio Portable Real-Time Audio Library
+ * Latest Version at: http://www.portaudio.com
+ * BeOS Media Kit Implementation by Joshua Haberman
+ *
+ * Copyright (c) 2001 Joshua Haberman <joshua@haberman.com>
+ *
+ * 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.
+ *
+ */
+
+#include <be/app/Application.h>
+#include <be/kernel/OS.h>
+#include <be/media/RealtimeAlloc.h>
+#include <be/media/MediaRoster.h>
+#include <be/media/TimeSource.h>
+
+#include <stdio.h>
+#include <string.h>
+
+#include "portaudio.h"
+#include "pa_host.h"
+
+#include "PlaybackNode.h"
+
+#define PRINT(x) { printf x; fflush(stdout); }
+
+#ifdef DEBUG
+#define DBUG(x)  PRINT(x)
+#else
+#define DBUG(x)
+#endif
+
+typedef struct PaHostSoundControl
+{
+    /* These members are common to all modes of operation */
+    media_node   pahsc_TimeSource;  /* the sound card's DAC. */
+    media_format pahsc_Format;
+
+    /* These methods are specific to playing mode */
+    media_node   pahsc_OutputNode;  /* output to the mixer */
+    media_node   pahsc_InputNode;   /* reads data from user callback -- PA specific */
+
+    media_input  pahsc_MixerInput;  /* input jack on the soundcard's mixer. */
+    media_output pahsc_PaOutput;    /* output jack from the PA node */
+
+    PaPlaybackNode *pahsc_InputNodeInstance;
+
+}
+PaHostSoundControl;
+
+/*************************************************************************/
+PaDeviceID Pa_GetDefaultOutputDeviceID( void )
+{
+    /* stub */
+    return 0;
+}
+
+/*************************************************************************/
+PaDeviceID Pa_GetDefaultInputDeviceID( void )
+{
+    /* stub */
+    return 0;
+}
+
+/*************************************************************************/
+const PaDeviceInfo* Pa_GetDeviceInfo( PaDeviceID id )
+{
+    /* stub */
+    return NULL;
+}
+
+/*************************************************************************/
+int Pa_CountDevices()
+{
+    /* stub */
+    return 1;
+}
+
+/*************************************************************************/
+PaError PaHost_Init( void )
+{
+    /* we have to create this in order to use BMediaRoster. I hope it doesn't
+     * cause problems */
+    be_app = new BApplication("application/x-vnd.portaudio-app");
+
+    return paNoError;
+}
+
+PaError PaHost_Term( void )
+{
+    delete be_app;
+    return paNoError;
+}
+
+/*************************************************************************/
+PaError PaHost_StreamActive( internalPortAudioStream   *past )
+{
+    PaHostSoundControl *pahsc = (PaHostSoundControl *)past->past_DeviceData;
+    DBUG(("IsRunning returning: %s\n",
+          pahsc->pahsc_InputNodeInstance->IsRunning() ? "true" : "false"));
+
+    return (PaError)pahsc->pahsc_InputNodeInstance->IsRunning();
+}
+
+PaError PaHost_StartOutput( internalPortAudioStream *past )
+{
+    return paNoError;
+}
+
+/*************************************************************************/
+PaError PaHost_StartInput( internalPortAudioStream *past )
+{
+    return paNoError;
+}
+
+/*************************************************************************/
+PaError PaHost_StopInput( internalPortAudioStream *past, int abort )
+{
+    return paNoError;
+}
+
+/*************************************************************************/
+PaError PaHost_StopOutput( internalPortAudioStream *past, int abort )
+{
+    return paNoError;
+}
+
+
+/*************************************************************************/
+PaError PaHost_StartEngine( internalPortAudioStream *past )
+{
+    bigtime_t very_soon, start_latency;
+    status_t err;
+    BMediaRoster *roster = BMediaRoster::Roster(&err);
+    PaHostSoundControl *pahsc = (PaHostSoundControl *)past->past_DeviceData;
+
+    /* for some reason, err indicates an error (though nothing it wrong)
+     * when the DBUG macro in pa_lib.c is enabled. It's reproducably 
+     * linked. Weird. */
+    if( !roster /* || err != B_OK */ )
+    {
+        DBUG(("No media server! err=%d, roster=%x\n", err, roster));
+        return paHostError;
+    }
+
+    /* tell the node when to start -- since there aren't any other nodes
+     * starting that we have to wait for, just tell it to start now
+     */
+
+    BTimeSource *timeSource = roster->MakeTimeSourceFor(pahsc->pahsc_TimeSource);
+    very_soon = timeSource->PerformanceTimeFor( BTimeSource::RealTime() );
+    timeSource->Release();
+
+    /* Add the latency of starting the network of nodes */
+    err = roster->GetStartLatencyFor( pahsc->pahsc_TimeSource, &start_latency );
+    very_soon += start_latency;
+
+    err = roster->StartNode( pahsc->pahsc_InputNode, very_soon );
+    /* No need to start the mixer -- it's always running */
+
+    return paNoError;
+}
+
+
+/*************************************************************************/
+PaError PaHost_StopEngine( internalPortAudioStream *past, int abort )
+{
+    PaHostSoundControl *pahsc = (PaHostSoundControl *)past->past_DeviceData;
+    BMediaRoster *roster = BMediaRoster::Roster();
+
+    if( !roster )
+    {
+        DBUG(("No media roster!\n"));
+        return paHostError;
+    }
+
+    if( !pahsc )
+        return paHostError;
+
+    /* this crashes, and I don't know why yet */
+    // if( abort )
+    //  pahsc->pahsc_InputNodeInstance->mAborted = true;
+
+    roster->StopNode(pahsc->pahsc_InputNode, 0, /* immediate = */ true);
+
+    return paNoError;
+}
+
+
+/*************************************************************************/
+PaError PaHost_OpenStream( internalPortAudioStream   *past )
+{
+    status_t err;
+    BMediaRoster *roster = BMediaRoster::Roster(&err);
+    PaHostSoundControl *pahsc;
+
+    /* Allocate and initialize host data. */
+    pahsc = (PaHostSoundControl *) PaHost_AllocateFastMemory(sizeof(PaHostSoundControl));
+    if( pahsc == NULL )
+    {
+        goto error;
+    }
+    memset( pahsc, 0, sizeof(PaHostSoundControl) );
+    past->past_DeviceData = (void *) pahsc;
+
+    if( !roster /* || err != B_OK */ )
+    {
+        /* no media server! */
+        DBUG(("No media server.\n"));
+        goto error;
+    }
+
+    if ( past->past_NumInputChannels > 0 && past->past_NumOutputChannels > 0 )
+    {
+        /* filter -- not implemented yet */
+        goto error;
+    }
+    else if ( past->past_NumInputChannels > 0 )
+    {
+        /* recorder -- not implemented yet */
+        goto error;
+    }
+    else
+    {
+        /* player ****************************************************************/
+
+        status_t err;
+        int32 num;
+
+        /* First we need to create the three components (like components in a stereo
+         * system). The mixer component is our interface to the sound card, data
+         * we write there will get played. The BePA_InputNode component is the node
+         * which represents communication with the PA client (it is what calls the
+         * client's callbacks). The time source component is the sound card's DAC,
+         * which allows us to slave the other components to it instead of the system
+         * clock. */
+        err = roster->GetAudioMixer( &pahsc->pahsc_OutputNode );
+        if( err != B_OK )
+        {
+            DBUG(("Couldn't get default mixer.\n"));
+            goto error;
+        }
+
+        err = roster->GetTimeSource( &pahsc->pahsc_TimeSource );
+        if( err != B_OK )
+        {
+            DBUG(("Couldn't get time source.\n"));
+            goto error;
+        }
+
+        pahsc->pahsc_InputNodeInstance = new PaPlaybackNode(2, 44100,
+                                         past->past_FramesPerUserBuffer, past->past_Callback, past->past_UserData );
+        pahsc->pahsc_InputNodeInstance->SetSampleFormat(0,
+                past->past_OutputSampleFormat);
+        err = roster->RegisterNode( pahsc->pahsc_InputNodeInstance );
+        if( err != B_OK )
+        {
+            DBUG(("Unable to register node.\n"));
+            goto error;
+        }
+
+        roster->GetNodeFor( pahsc->pahsc_InputNodeInstance->Node().node,
+                            &pahsc->pahsc_InputNode );
+        if( err != B_OK )
+        {
+            DBUG(("Unable to get input node.\n"));
+            goto error;
+        }
+
+        /* Now we have three components (nodes) sitting next to each other. The
+         * next step is to look at them and find their inputs and outputs so we can
+         * wire them together. */
+        err = roster->GetFreeInputsFor( pahsc->pahsc_OutputNode,
+                                        &pahsc->pahsc_MixerInput, 1, &num, B_MEDIA_RAW_AUDIO );
+        if( err != B_OK || num < 1 )
+        {
+            DBUG(("Couldn't get the mixer input.\n"));
+            goto error;
+        }
+
+        err = roster->GetFreeOutputsFor( pahsc->pahsc_InputNode,
+                                         &pahsc->pahsc_PaOutput, 1, &num, B_MEDIA_RAW_AUDIO );
+        if( err != B_OK || num < 1 )
+        {
+            DBUG(("Couldn't get PortAudio output.\n"));
+            goto error;
+        }
+
+
+        /* We've found the input and output -- the final step is to run a wire
+         * between them so they are connected. */
+
+        /* try to make the mixer input adapt to what PA sends it */
+        pahsc->pahsc_Format = pahsc->pahsc_PaOutput.format;
+        roster->Connect( pahsc->pahsc_PaOutput.source,
+                         pahsc->pahsc_MixerInput.destination, &pahsc->pahsc_Format,
+                         &pahsc->pahsc_PaOutput, &pahsc->pahsc_MixerInput );
+
+
+        /* Actually, there's one final step -- tell them all to sync to the
+         * sound card's DAC */
+        roster->SetTimeSourceFor( pahsc->pahsc_InputNode.node,
+                                  pahsc->pahsc_TimeSource.node );
+        roster->SetTimeSourceFor( pahsc->pahsc_OutputNode.node,
+                                  pahsc->pahsc_TimeSource.node );
+
+    }
+
+    return paNoError;
+
+error:
+    PaHost_CloseStream( past );
+    return paHostError;
+}
+
+/*************************************************************************/
+PaError PaHost_CloseStream( internalPortAudioStream   *past )
+{
+    PaHostSoundControl *pahsc = (PaHostSoundControl *)past->past_DeviceData;
+    status_t err;
+    BMediaRoster *roster = BMediaRoster::Roster(&err);
+
+    if( !roster )
+    {
+        DBUG(("Couldn't get media roster\n"));
+        return paHostError;
+    }
+
+    if( !pahsc )
+        return paHostError;
+
+    /* Disconnect all the connections we made when opening the stream */
+
+    roster->Disconnect(pahsc->pahsc_InputNode.node, pahsc->pahsc_PaOutput.source,
+                       pahsc->pahsc_OutputNode.node, pahsc->pahsc_MixerInput.destination);
+
+    DBUG(("Calling ReleaseNode()"));
+    roster->ReleaseNode(pahsc->pahsc_InputNode);
+
+    /* deleting the node shouldn't be necessary -- it is reference counted, and will
+     * delete itself when its references drop to zero. the call to ReleaseNode()
+     * above  should decrease its reference count */
+    pahsc->pahsc_InputNodeInstance = NULL;
+
+    return paNoError;
+}
+
+/*************************************************************************/
+PaTimestamp Pa_StreamTime( PortAudioStream *stream )
+{
+    internalPortAudioStream *past = (internalPortAudioStream *) stream;
+    PaHostSoundControl *pahsc = (PaHostSoundControl *)past->past_DeviceData;
+
+    return pahsc->pahsc_InputNodeInstance->GetStreamTime();
+}
+
+/*************************************************************************/
+void Pa_Sleep( long msec )
+{
+    /* snooze() takes microseconds */
+    snooze( msec * 1000 );
+}
+
+/*************************************************************************
+ * Allocate memory that can be accessed in real-time.
+ * This may need to be held in physical memory so that it is not
+ * paged to virtual memory.
+ * This call MUST be balanced with a call to PaHost_FreeFastMemory().
+ * Memory will be set to zero.
+ */
+void *PaHost_AllocateFastMemory( long numBytes )
+{
+    /* BeOS supports non-pagable memory through pools -- a pool is an area
+     * of physical memory that is locked. It would be best to pre-allocate
+     * that pool and then hand out memory from it, but we don't know in
+     * advance how much we'll need. So for now, we'll allocate a pool
+     * for every request we get, storing a pointer to the pool at the
+     * beginning of the allocated memory */
+    rtm_pool *pool;
+    void *addr;
+    long size = numBytes + sizeof(rtm_pool *);
+    static int counter = 0;
+    char pool_name[100];
+
+    /* Every pool needs a unique name. */
+    sprintf(pool_name, "PaPoolNumber%d", counter++);
+
+    if( rtm_create_pool( &pool, size, pool_name ) != B_OK )
+        return 0;
+
+    addr = rtm_alloc( pool, size );
+    if( addr == NULL )
+        return 0;
+
+    memset( addr, 0, numBytes );
+    *((rtm_pool **)addr) = pool;    // store the pointer to the pool
+    addr = (rtm_pool **)addr + 1;   // and return the next location in memory
+
+    return addr;
+}
+
+/*************************************************************************
+ * Free memory that could be accessed in real-time.
+ * This call MUST be balanced with a call to PaHost_AllocateFastMemory().
+ */
+void PaHost_FreeFastMemory( void *addr, long numBytes )
+{
+    rtm_pool *pool;
+
+    if( addr == NULL )
+        return;
+
+    addr = (rtm_pool **)addr - 1;
+    pool = *((rtm_pool **)addr);
+
+    rtm_free( addr );
+    rtm_delete_pool( pool );
+}
diff --git a/pa_common/pa_host.h b/pa_common/pa_host.h
new file mode 100644 (file)
index 0000000..9a35261
--- /dev/null
@@ -0,0 +1,142 @@
+#ifndef PA_HOST_H
+#define PA_HOST_H
+
+/*
+ * $Id$
+ * Host dependant internal API for PortAudio
+ *
+ * Author: Phil Burk  <philburk@softsynth.com>
+ *
+ * PortAudio Portable Real-Time Audio Library
+ * Latest Version at: http://www.softsynth.com/portaudio/
+ * DirectSound and Macintosh Implementation
+ * 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.
+ *
+ */
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif /* __cplusplus */
+
+#ifndef SUPPORT_AUDIO_CAPTURE
+#define SUPPORT_AUDIO_CAPTURE  (1)
+#endif
+
+#ifndef int32
+    typedef long int32;
+#endif
+#ifndef uint32
+    typedef unsigned long uint32;
+#endif
+#ifndef int16
+    typedef short int16;
+#endif
+#ifndef uint16
+    typedef unsigned short uint16;
+#endif
+
+#define PA_MAGIC    (0x18273645)
+
+/************************************************************************************/
+/****************** Structures ******************************************************/
+/************************************************************************************/
+
+typedef struct internalPortAudioStream
+{
+    uint32                    past_Magic;  /* ID for struct to catch bugs. */
+    /* User specified information. */
+    uint32                    past_FramesPerUserBuffer;
+    uint32                    past_NumUserBuffers;
+    double                    past_SampleRate;     /* Closest supported sample rate. */
+    int                       past_NumInputChannels;
+    int                       past_NumOutputChannels;
+    PaDeviceID                past_InputDeviceID;
+    PaDeviceID                past_OutputDeviceID;
+    PaSampleFormat            past_InputSampleFormat;
+    PaSampleFormat            past_OutputSampleFormat;
+    void                     *past_DeviceData;
+    PortAudioCallback        *past_Callback;
+    void                     *past_UserData;
+    uint32                    past_Flags;
+    /* Flags for communicating between foreground and background. */
+    volatile int              past_IsActive;      /* Background is still playing. */
+    volatile int              past_StopSoon;      /* Background should keep playing when buffers empty. */
+    volatile int              past_StopNow;       /* Background should stop playing now. */
+    /* These buffers are used when the native format does not match the user format. */
+    void                     *past_InputBuffer;
+    uint32                    past_InputBufferSize;
+    void                     *past_OutputBuffer;
+    uint32                    past_OutputBufferSize;
+    /* Measurements */
+    uint32                    past_NumCallbacks;
+    PaTimestamp               past_FrameCount;    /* Frames output to buffer. */
+    /* For measuring CPU utilization. */
+    double                    past_AverageInsideCount;
+    double                    past_AverageTotalCount;
+    double                    past_Usage;
+    int                       past_IfLastExitValid;
+}
+internalPortAudioStream;
+
+/************************************************************************************/
+/****************** Prototypes ******************************************************/
+/************************************************************************************/
+
+PaError PaHost_Init( void );
+PaError PaHost_Term( void );
+
+PaError PaHost_OpenStream( internalPortAudioStream   *past );
+PaError PaHost_CloseStream( internalPortAudioStream   *past );
+
+PaError PaHost_StartOutput( internalPortAudioStream   *past );
+PaError PaHost_StopOutput( internalPortAudioStream   *past, int abort );
+PaError PaHost_StartInput( internalPortAudioStream   *past );
+PaError PaHost_StopInput( internalPortAudioStream   *past, int abort );
+PaError PaHost_StartEngine( internalPortAudioStream   *past );
+PaError PaHost_StopEngine( internalPortAudioStream *past, int abort );
+PaError PaHost_StreamActive( internalPortAudioStream   *past );
+
+long Pa_CallConvertInt16( internalPortAudioStream   *past,
+                          short *nativeInputBuffer,
+                          short *nativeOutputBuffer );
+
+long Pa_CallConvertFloat32( internalPortAudioStream   *past,
+                            float *nativeInputBuffer,
+                            float *nativeOutputBuffer );
+
+void   *PaHost_AllocateFastMemory( long numBytes );
+void    PaHost_FreeFastMemory( void *addr, long numBytes );
+
+PaError PaHost_ValidateSampleRate( PaDeviceID id, double requestedFrameRate,
+                                   double *closestFrameRatePtr );
+int PaHost_FindClosestTableEntry( double allowableError,  const double *rateTable,
+                                  int numRates, double frameRate );
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif /* PA_HOST_H */
diff --git a/pa_common/pa_lib.c b/pa_common/pa_lib.c
new file mode 100644 (file)
index 0000000..f32c635
--- /dev/null
@@ -0,0 +1,836 @@
+/*
+ * $Id$
+ * 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
+ PLB20010820 - fix dither and shift for recording PaUInt8 format 
+*/
+
+#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;
+}
+
+/*************************************************************************/
+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;
+}
+
+
+/*************************************************************************/
+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 );
+}
+
+/*************************************************************************/
+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;
+}
+
+/*************************************************************************/
+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;
+}
+
+/*************************************************************************/
+PaError Pa_StopStream(  PortAudioStream *stream )
+{
+    return Pa_KillStream( stream, 0 );
+}
+
+/*************************************************************************/
+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;
+}
+
+/*************************************************************************/
+PaError Pa_StreamActive( PortAudioStream *stream )
+{
+    internalPortAudioStream   *past;
+    if( stream == NULL ) return paBadStreamPtr;
+    past = (internalPortAudioStream *) stream;
+    return PaHost_StreamActive( past );
+}
+
+/*************************************************************************/
+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;
+    case paDeviceUnavailable:        msg = "Device Unavailable."; 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.
+*/
+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() >> 8; /* PLB20010820 */
+                        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() >> 8; /* PLB20010820 */
+                        temp = ((temp < -0x8000) ? -0x8000 : ((temp > 0x7FFF) ? 0x7FFF : temp));
+                        inBufPtr[i] = (unsigned char)((temp>>8) + 0x80); /* PLB20010820 */
+                    }
+                }
+                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;
+}
+
+/*************************************************************************/
+PaError Pa_Initialize( void )
+{
+    if( gInitCount++ > 0 ) return paNoError;
+    ResetTraceMessages();
+    return PaHost_Init();
+}
+
+PaError Pa_Terminate( void )
+{
+    PaError result = paNoError;
+
+    if( gInitCount == 0 ) return paNoError;
+    else if( --gInitCount == 0 )
+    {
+        result = PaHost_Term();
+        DumpTraceMessages();
+    }
+    return result;
+}
+
+/*************************************************************************/
+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;
+}
+
+
diff --git a/pa_common/pa_trace.c b/pa_common/pa_trace.c
new file mode 100644 (file)
index 0000000..08e68f5
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * $Id$
+ * Portable Audio I/O Library Trace Facility
+ * Store trace information in real-time for later printing.
+ *
+ * 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.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "pa_trace.h"
+
+#if TRACE_REALTIME_EVENTS
+
+static char *traceTextArray[MAX_TRACE_RECORDS];
+static int traceIntArray[MAX_TRACE_RECORDS];
+static int traceIndex = 0;
+static int traceBlock = 0;
+
+/*********************************************************************/
+void ResetTraceMessages()
+{
+    traceIndex = 0;
+}
+
+/*********************************************************************/
+void DumpTraceMessages()
+{
+    int i;
+    int numDump = (traceIndex < MAX_TRACE_RECORDS) ? traceIndex : MAX_TRACE_RECORDS;
+
+    printf("DumpTraceMessages: traceIndex = %d\n", traceIndex );
+    for( i=0; i<numDump; i++ )
+    {
+        printf("%3d: %s = 0x%08X\n",
+               i, traceTextArray[i], traceIntArray[i] );
+    }
+    ResetTraceMessages();
+    fflush(stdout);
+}
+
+/*********************************************************************/
+void AddTraceMessage( char *msg, int data )
+{
+    if( (traceIndex == MAX_TRACE_RECORDS) && (traceBlock == 0) )
+    {
+        traceBlock = 1;
+        /*  DumpTraceMessages(); */
+    }
+    else if( traceIndex < MAX_TRACE_RECORDS )
+    {
+        traceTextArray[traceIndex] = msg;
+        traceIntArray[traceIndex] = data;
+        traceIndex++;
+    }
+}
+
+#endif
diff --git a/pa_common/pa_trace.h b/pa_common/pa_trace.h
new file mode 100644 (file)
index 0000000..e3fa8ca
--- /dev/null
@@ -0,0 +1,67 @@
+#ifndef PA_TRACE_H
+#define PA_TRACE_H
+/*
+ * $Id$
+ * Portable Audio I/O Library Trace Facility
+ * Store trace information in real-time for later printing.
+ *
+ * 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.
+ */
+
+
+#define TRACE_REALTIME_EVENTS     (0)   /* Keep log of various real-time events. */
+#define MAX_TRACE_RECORDS      (2048)
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif /* __cplusplus */
+
+
+    /************************************************************************************/
+    /****************** Prototypes ******************************************************/
+    /************************************************************************************/
+
+#if TRACE_REALTIME_EVENTS
+
+    void DumpTraceMessages();
+    void ResetTraceMessages();
+    void AddTraceMessage( char *msg, int data );
+
+#else
+
+#define AddTraceMessage(msg,data) /* noop */
+#define ResetTraceMessages() /* noop */
+#define DumpTraceMessages() /* noop */
+
+#endif
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* PA_TRACE_H */
diff --git a/pa_common/portaudio.h b/pa_common/portaudio.h
new file mode 100644 (file)
index 0000000..231e804
--- /dev/null
@@ -0,0 +1,432 @@
+#ifndef PORT_AUDIO_H
+#define PORT_AUDIO_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif /* __cplusplus */
+
+/*
+ * $Id$
+ * PortAudio Portable Real-Time Audio Library
+ * PortAudio API Header File
+ * Latest version available at: http://www.audiomulch.com/portaudio/
+ *
+ * Copyright (c) 1999-2000 Ross Bencina and 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.
+ *
+ */
+
+typedef int PaError;
+typedef enum {
+    paNoError = 0,
+
+    paHostError = -10000,
+    paInvalidChannelCount,
+    paInvalidSampleRate,
+    paInvalidDeviceId,
+    paInvalidFlag,
+    paSampleFormatNotSupported,
+    paBadIODeviceCombination,
+    paInsufficientMemory,
+    paBufferTooBig,
+    paBufferTooSmall,
+    paNullCallback,
+    paBadStreamPtr,
+    paTimedOut,
+    paInternalError,
+    paDeviceUnavailable
+} PaErrorNum;
+
+/*
+ Pa_Initialize() is the library initialisation function - call this before
+ using the library.
+*/
+
+PaError Pa_Initialize( void );
+
+/*
+ Pa_Terminate() is the library termination function - call this after
+ using the library.
+*/
+
+PaError Pa_Terminate( void );
+
+/*
+ Return host specific error.
+ This can be called after receiving a paHostError.
+*/
+long Pa_GetHostError( void );
+
+/*
+ Translate the error number into a human readable message.
+*/
+const char *Pa_GetErrorText( PaError errnum );
+
+/*
+ Sample formats
+ These are formats used to pass sound data between the callback and the
+ stream. Each device has a "native" format which may be used when optimum
+ efficiency or control over conversion is required.
+ Formats marked "always available" are supported (emulated) by all devices.
+ The floating point representation uses +1.0 and -1.0 as the respective
+ maximum and minimum.
+*/
+
+typedef unsigned long PaSampleFormat;
+#define paFloat32      ((PaSampleFormat) (1<<0)) /*always available*/
+#define paInt16        ((PaSampleFormat) (1<<1)) /*always available*/
+#define paInt32        ((PaSampleFormat) (1<<2)) /*always available*/
+#define paInt24        ((PaSampleFormat) (1<<3))
+#define paPackedInt24  ((PaSampleFormat) (1<<4))
+#define paInt8         ((PaSampleFormat) (1<<5))
+#define paUInt8        ((PaSampleFormat) (1<<6))    /* unsigned 8 bit, 128 is "ground" */
+#define paCustomFormat ((PaSampleFormat) (1<<16))
+
+/*
+ Device enumeration mechanism.
+    Device ids range from 0 to Pa_CountDevices()-1.
+ Devices may support input, output or both. Device 0 is always the "default"
+ device and should support at least stereo in and out if that is available
+ on the taget platform _even_ if this involves kludging an input/output
+ device on platforms that usually separate input from output. Other platform
+ specific devices are specified by positive device ids.
+*/
+
+typedef int PaDeviceID;
+#define paNoDevice -1
+
+typedef struct
+{
+    int structVersion;
+    const char *name;
+    int maxInputChannels;
+    int maxOutputChannels;
+    /* Number of discrete rates, or -1 if range supported. */
+    int numSampleRates;
+    /* Array of supported sample rates, or {min,max} if range supported. */
+    const double *sampleRates;
+    PaSampleFormat nativeSampleFormats;
+}
+PaDeviceInfo;
+
+
+int Pa_CountDevices();
+/*
+ Pa_GetDefaultInputDeviceID(), Pa_GetDefaultOutputDeviceID()
+ Return the default device ID or paNoDevice if there is no devices.
+ The result can be passed to Pa_OpenStream().
+ On the PC, the user can specify a default device by
+ setting an environment variable. For example, to use device #1.
+  set PA_RECOMMENDED_OUTPUT_DEVICE=1
+ The user should first determine the available device ID by using
+ the supplied application "pa_devs".
+*/
+PaDeviceID Pa_GetDefaultInputDeviceID( void );
+PaDeviceID Pa_GetDefaultOutputDeviceID( void );
+
+/*
+ PaTimestamp is used to represent a continuous sample clock with arbitrary
+ start time useful for syncronisation. The type is used in the outTime
+ argument to the callback function and the result of Pa_StreamTime()
+*/
+
+typedef double PaTimestamp;
+
+/*
+ Pa_GetDeviceInfo() returns a pointer to an immutable PaDeviceInfo structure
+ referring to the device specified by id.
+ If id is out of range the function returns NULL.
+ The returned structure is owned by the PortAudio implementation and must
+ not be manipulated or freed. The pointer is only guaranteed to be valid
+ between calls to Pa_Initialize() and Pa_Terminate().
+*/
+
+const PaDeviceInfo* Pa_GetDeviceInfo( PaDeviceID devID );
+
+/*
+ PortAudioCallback is implemented by clients of the portable audio api.
+ inputBuffer and outputBuffer are arrays of interleaved samples,
+ the format, packing and number of channels used by the buffers are
+ determined by parameters to Pa_OpenStream() (see below).
+ framesPerBuffer is the number of sample frames to be processed by the callback.
+ outTime is the time in samples when the buffer(s) processed by
+ this callback will begin being played at the audio output.
+ See also Pa_StreamTime()
+ userData is the value of a user supplied pointer passed to Pa_OpenStream()
+ intended for storing synthesis data etc.
+ return value:
+ The callback can return a nonzero value to stop the stream. This may be
+ useful in applications such as soundfile players where a specific duration
+ of output is required. However, it is not necessary to utilise this mechanism
+ as StopStream() will also terminate the stream. A callback returning a
+ nonzero value must fill the entire outputBuffer.
+ NOTE: None of the other stream functions may be called from within the
+ callback function except for Pa_GetCPULoad().
+*/
+
+typedef int (PortAudioCallback)(
+    void *inputBuffer, void *outputBuffer,
+    unsigned long framesPerBuffer,
+    PaTimestamp outTime, void *userData );
+
+
+/*
+ Stream flags
+ These flags may be supplied (ored together) in the streamFlags argument to
+ the Pa_OpenStream() function.
+ [ suggestions? ]
+*/
+
+#define   paNoFlag      (0)
+#define   paClipOff     (1<<0)   /* disable defult clipping of out of range samples */
+#define   paDitherOff   (1<<1)   /* disable default dithering */
+#define   paPlatformSpecificFlags (0x00010000)
+typedef   unsigned long PaStreamFlags;
+
+/*
+ A single PortAudioStream provides multiple channels of real-time
+ input and output audio streaming to a client application.
+ Pointers to PortAudioStream objects are passed between PortAudio functions.
+*/
+
+typedef void PortAudioStream;
+#define PaStream PortAudioStream
+
+/*
+ Pa_OpenStream() opens a stream for either input, output or both.
+ stream is the address of a PortAudioStream pointer which will receive
+ a pointer to the newly opened stream.
+ inputDevice is the id of the device used for input (see PaDeviceID above.)
+ inputDevice may be paNoDevice to indicate that an input device is not required.
+ numInputChannels is the number of channels of sound to be delivered to the
+ callback. It can range from 1 to the value of maxInputChannels in the
+ device input record for the device specified in the inputDevice parameter.
+ If inputDevice is paNoDevice numInputChannels is ignored.
+ inputSampleFormat is the format of inputBuffer provided to the callback
+ function. inputSampleFormat may be any of the formats described by the
+ PaSampleFormat enumeration (see above). PortAudio guarantees support for
+ the sound devices native formats (nativeSampleFormats in the device info
+ record) and additionally 16 and 32 bit integer and 32 bit floating point
+ formats. Support for other formats is implementation defined.
+ inputDriverInfo is a pointer to an optional driver specific data structure
+ containing additional information for device setup or stream processing.
+ inputDriverInfo is never required for correct operation. If not used
+ inputDriverInfo should be NULL.
+ outputDevice is the id of the device used for output (see PaDeviceID above.)
+ outputDevice may be paNoDevice to indicate that an output device is not required.
+ numOutputChannels is the number of channels of sound to be supplied by the
+ callback. See the definition of numInputChannels above for more details.
+ outputSampleFormat is the sample format of the outputBuffer filled by the
+ callback function. See the definition of inputSampleFormat above for more
+ details.
+ outputDriverInfo is a pointer to an optional driver specific data structure
+ containing additional information for device setup or stream processing.
+ outputDriverInfo is never required for correct operation. If not used
+ outputDriverInfo should be NULL.
+ sampleRate is the desired sampleRate for input and output
+ framesPerBuffer is the length in sample frames of all internal sample buffers
+ used for communication with platform specific audio routines. Wherever
+ possible this corresponds to the framesPerBuffer parameter passed to the
+ callback function.
+ numberOfBuffers is the number of buffers used for 
+ multibuffered communication with the platform specific audio 
+ routines. If you pass zero, then an optimum value will be 
+ chosen for you internally. This parameter is provided only 
+ as a guide - and does not imply that an implementation must 
+ use multibuffered i/o when reliable double buffering is 
+ available (such as SndPlayDoubleBuffer() on the Macintosh.) 
+ streamFlags may contain a combination of flags ORed together.
+ These flags modify the behavior of the
+ streaming process. Some flags may only be relevant to certain buffer formats.
+ callback is a pointer to a client supplied function that is responsible
+ for processing and filling input and output buffers (see above for details.)
+ userData is a client supplied pointer which is passed to the callback
+ function. It could for example, contain a pointer to instance data necessary
+ for processing the audio buffers.
+ return value:
+ Apon success Pa_OpenStream() returns PaNoError and places a pointer to a
+ valid PortAudioStream in the stream argument. The stream is inactive (stopped).
+ If a call to Pa_OpenStream() fails a nonzero error code is returned (see
+ PAError above) and the value of stream is invalid.
+*/
+
+PaError Pa_OpenStream( PortAudioStream** stream,
+                       PaDeviceID inputDevice,
+                       int numInputChannels,
+                       PaSampleFormat inputSampleFormat,
+                       void *inputDriverInfo,
+                       PaDeviceID outputDevice,
+                       int numOutputChannels,
+                       PaSampleFormat outputSampleFormat,
+                       void *outputDriverInfo,
+                       double sampleRate,
+                       unsigned long framesPerBuffer,
+                       unsigned long numberOfBuffers,
+                       PaStreamFlags streamFlags,
+                       PortAudioCallback *callback,
+                       void *userData );
+
+
+/*
+ Pa_OpenDefaultStream() is a simplified version of Pa_OpenStream() that
+ opens the default input and/or ouput devices. Most parameters have
+ identical meaning to their Pa_OpenStream() counterparts, with the following
+ exceptions:
+ If either numInputChannels or numOutputChannels is 0 the respective device
+ is not opened (same as passing paNoDevice in the device arguments to Pa_OpenStream() )
+ sampleFormat applies to both the input and output buffers.
+*/
+
+PaError Pa_OpenDefaultStream( PortAudioStream** stream,
+                              int numInputChannels,
+                              int numOutputChannels,
+                              PaSampleFormat sampleFormat,
+                              double sampleRate,
+                              unsigned long framesPerBuffer,
+                              unsigned long numberOfBuffers,
+                              PortAudioCallback *callback,
+                              void *userData );
+
+/*
+ Pa_CloseStream() closes an audio stream, flushing any pending buffers.
+*/
+
+PaError Pa_CloseStream( PortAudioStream* );
+
+/*
+  Pa_StartStream() and Pa_StopStream() begin and terminate audio processing.
+ Pa_StopStream() waits until all pending audio buffers have been played.
+    Pa_AbortStream() stops playing immediately without waiting for pending
+    buffers to complete.
+*/
+
+PaError Pa_StartStream( PortAudioStream *stream );
+
+PaError Pa_StopStream( PortAudioStream *stream );
+
+PaError Pa_AbortStream( PortAudioStream *stream );
+
+/*
+ Pa_StreamActive() returns one when the stream is playing audio,
+ zero when not playing, or a negative error number if the
+ stream is invalid.
+ The stream is active between calls to Pa_StartStream() and Pa_StopStream(),
+ but may also become inactive if the callback returns a non-zero value.
+ In the latter case, the stream is considered inactive after the last
+ buffer has finished playing.
+*/
+
+PaError Pa_StreamActive( PortAudioStream *stream );
+
+/*
+ Pa_StreamTime() returns the current output time for the stream in samples.
+ This time may be used as a time reference (for example syncronising audio to
+ MIDI).
+*/
+
+PaTimestamp Pa_StreamTime( PortAudioStream *stream );
+
+/*
+ The "CPU Load" is a fraction of total CPU time consumed by the
+ stream's audio processing.
+ A value of 0.5 would imply that PortAudio and the sound generating
+ callback was consuming roughly 50% of the available CPU time.
+ This function may be called from the callback function or the application.
+*/
+double Pa_GetCPULoad( PortAudioStream* stream );
+
+/*
+ Use Pa_GetMinNumBuffers() to determine minimum number of buffers required for
+ the current host based on minimum latency. 
+ On the PC, for the DirectSound implementation, 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 AUTOEXEC.BAT file and reboot.
+ If the environment variable is not set, then the latency will be determined
+ based on the OS. Windows NT has higher latency than Win95.
+*/
+
+int Pa_GetMinNumBuffers( int framesPerBuffer, double sampleRate );
+
+/*
+ Sleep for at least 'msec' milliseconds.
+ You may sleep longer than the requested time so don't rely
+ on this for accurate musical timing.
+*/
+void Pa_Sleep( long msec );
+
+/*
+ Return size in bytes of a single sample in a given PaSampleFormat
+ or paSampleFormatNotSupported. 
+*/
+PaError Pa_GetSampleSize( PaSampleFormat format );
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif /* PORT_AUDIO_H */
diff --git a/pa_dll_switch/PaDllEntry.h b/pa_dll_switch/PaDllEntry.h
new file mode 100644 (file)
index 0000000..e070054
--- /dev/null
@@ -0,0 +1,184 @@
+
+/*
+ * PortAudio Portable Real-Time Audio Library
+ * PortAudio DLL Header File
+ * Latest version available at: http://www.audiomulch.com/portaudio/
+ *
+ * Copyright (c) 1999-2000 Ross Bencina and 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.
+ *
+ */
+
+// changed by zplane.developement in order to generate a DLL
+
+#ifndef __PADLLENTRY_HEADER_INCLUDED__
+
+#define __PADLLENTRY_HEADER_INCLUDED__
+
+typedef int PaError;
+typedef enum {
+    paNoError = 0,
+
+    paHostError = -10000,
+    paInvalidChannelCount,
+    paInvalidSampleRate,
+    paInvalidDeviceId,
+    paInvalidFlag,
+    paSampleFormatNotSupported,
+    paBadIODeviceCombination,
+    paInsufficientMemory,
+    paBufferTooBig,
+    paBufferTooSmall,
+    paNullCallback,
+    paBadStreamPtr,
+    paTimedOut,
+    paInternalError
+} PaErrorNum;
+
+typedef unsigned long PaSampleFormat;
+#define paFloat32      ((PaSampleFormat) (1<<0)) /*always available*/
+#define paInt16        ((PaSampleFormat) (1<<1)) /*always available*/
+#define paInt32        ((PaSampleFormat) (1<<2)) /*always available*/
+#define paInt24        ((PaSampleFormat) (1<<3))
+#define paPackedInt24  ((PaSampleFormat) (1<<4))
+#define paInt8         ((PaSampleFormat) (1<<5))
+#define paUInt8        ((PaSampleFormat) (1<<6))    /* unsigned 8 bit, 128 is "ground" */
+#define paCustomFormat ((PaSampleFormat) (1<<16))
+
+
+typedef int PaDeviceID;
+#define paNoDevice -1
+
+typedef struct
+{
+    int structVersion;
+    const char *name;
+    int maxInputChannels;
+    int maxOutputChannels;
+    /* Number of discrete rates, or -1 if range supported. */
+    int numSampleRates;
+    /* Array of supported sample rates, or {min,max} if range supported. */
+    const double *sampleRates;
+    PaSampleFormat nativeSampleFormats;
+}
+PaDeviceInfo;
+
+
+typedef double PaTimestamp;
+
+
+typedef int (PortAudioCallback)(
+    void *inputBuffer, void *outputBuffer,
+    unsigned long framesPerBuffer,
+    PaTimestamp outTime, void *userData );
+
+
+#define   paNoFlag      (0)
+#define   paClipOff     (1<<0)   /* disable default clipping of out of range samples */
+#define   paDitherOff   (1<<1)   /* disable default dithering */
+#define   paPlatformSpecificFlags (0x00010000)
+typedef   unsigned long PaStreamFlags;
+
+typedef void PortAudioStream;
+#define PaStream PortAudioStream
+
+extern  PaError (__cdecl* Pa_Initialize)( void );
+
+
+
+extern  PaError (__cdecl* Pa_Terminate)( void );
+
+
+extern  long (__cdecl* Pa_GetHostError)( void );
+
+
+extern  const char* (__cdecl* Pa_GetErrorText)( PaError );
+
+
+
+extern  int (__cdecl* Pa_CountDevices)(void);
+
+extern  PaDeviceID (__cdecl* Pa_GetDefaultInputDeviceID)( void );
+
+extern  PaDeviceID (__cdecl* Pa_GetDefaultOutputDeviceID)( void );
+
+
+extern  const PaDeviceInfo* (__cdecl* Pa_GetDeviceInfo)( PaDeviceID);
+
+
+
+extern  PaError (__cdecl* Pa_OpenStream)(
+        PortAudioStream ** ,
+        PaDeviceID ,
+        int ,
+        PaSampleFormat ,
+        void *,
+        PaDeviceID ,
+        int ,
+        PaSampleFormat ,
+        void *,
+        double ,
+        unsigned long ,
+        unsigned long ,
+        unsigned long ,
+        PortAudioCallback *,
+        void * );
+
+
+
+extern  PaError (__cdecl* Pa_OpenDefaultStream)( PortAudioStream** stream,
+            int numInputChannels,
+            int numOutputChannels,
+            PaSampleFormat sampleFormat,
+            double sampleRate,
+            unsigned long framesPerBuffer,
+            unsigned long numberOfBuffers,
+            PortAudioCallback *callback,
+            void *userData );
+
+
+extern  PaError (__cdecl* Pa_CloseStream)( PortAudioStream* );
+
+
+extern  PaError (__cdecl* Pa_StartStream)( PortAudioStream *stream );
+
+extern  PaError (__cdecl* Pa_StopStream)( PortAudioStream *stream );
+
+extern  PaError (__cdecl* Pa_AbortStream)( PortAudioStream *stream );
+
+extern  PaError (__cdecl* Pa_StreamActive)( PortAudioStream *stream );
+
+extern  PaTimestamp (__cdecl* Pa_StreamTime)( PortAudioStream *stream );
+
+extern  double (__cdecl* Pa_GetCPULoad)( PortAudioStream* stream );
+
+extern  int (__cdecl* Pa_GetMinNumBuffers)( int framesPerBuffer, double sampleRate );
+
+extern  void (__cdecl* Pa_Sleep)( long msec );
+
+extern  PaError (__cdecl* Pa_GetSampleSize)( PaSampleFormat format );
+
+#endif // __PADLLENTRY_HEADER_INCLUDED__
+
diff --git a/pa_dll_switch/letter_from_tim_010817.txt b/pa_dll_switch/letter_from_tim_010817.txt
new file mode 100644 (file)
index 0000000..a535cd1
Binary files /dev/null and b/pa_dll_switch/letter_from_tim_010817.txt differ
diff --git a/pa_dll_switch/loadPA_DLL.cpp b/pa_dll_switch/loadPA_DLL.cpp
new file mode 100644 (file)
index 0000000..043eda8
--- /dev/null
@@ -0,0 +1,203 @@
+//////////////////////////////////////////////////////////////////////////
+
+
+HINSTANCE   pPaDll;
+
+/*
+ the function pointers to the PortAudio DLLs
+*/
+
+PaError (__cdecl* Pa_Initialize)( void );
+
+
+
+PaError (__cdecl* Pa_Terminate)( void );
+
+
+long (__cdecl* Pa_GetHostError)( void );
+
+
+const char* (__cdecl* Pa_GetErrorText)( PaError );
+
+
+int (__cdecl* Pa_CountDevices)(void);
+
+PaDeviceID (__cdecl* Pa_GetDefaultInputDeviceID)( void );
+
+PaDeviceID (__cdecl* Pa_GetDefaultOutputDeviceID)( void );
+
+
+const PaDeviceInfo* (__cdecl* Pa_GetDeviceInfo)( PaDeviceID);
+
+
+
+PaError (__cdecl* Pa_OpenStream)(
+    PortAudioStream ** ,
+    PaDeviceID ,
+    int ,
+    PaSampleFormat ,
+    void *,
+    PaDeviceID ,
+    int ,
+    PaSampleFormat ,
+    void *,
+    double ,
+    unsigned long ,
+    unsigned long ,
+    unsigned long ,
+    PortAudioCallback *,
+    void * );
+
+
+
+PaError (__cdecl* Pa_OpenDefaultStream)( PortAudioStream** stream,
+        int numInputChannels,
+        int numOutputChannels,
+        PaSampleFormat sampleFormat,
+        double sampleRate,
+        unsigned long framesPerBuffer,
+        unsigned long numberOfBuffers,
+        PortAudioCallback *callback,
+        void *userData );
+
+
+PaError (__cdecl* Pa_CloseStream)( PortAudioStream* );
+
+
+PaError (__cdecl* Pa_StartStream)( PortAudioStream *stream );
+
+PaError (__cdecl* Pa_StopStream)( PortAudioStream *stream );
+
+PaError (__cdecl* Pa_AbortStream)( PortAudioStream *stream );
+
+PaError (__cdecl* Pa_StreamActive)( PortAudioStream *stream );
+
+PaTimestamp (__cdecl* Pa_StreamTime)( PortAudioStream *stream );
+
+double (__cdecl* Pa_GetCPULoad)( PortAudioStream* stream );
+
+int (__cdecl* Pa_GetMinNumBuffers)( int framesPerBuffer, double sampleRate );
+
+void (__cdecl* Pa_Sleep)( long msec );
+
+PaError (__cdecl* Pa_GetSampleSize)( PaSampleFormat format );
+
+
+//////////////////////////////////////////////////////////////////////////
+
+...
+
+ZERROR AudioEngine::DirectXSupport(ZBOOL bSupDX)
+{
+    if (bSupDX)
+        if (CheckForDirectXSupport())
+            bSupportDirectX = _TRUE;
+        else
+            return _NO_SOUND;
+    else
+        bSupportDirectX  = _FALSE;
+    return _NO_ERROR;
+}
+
+
+
+ZBOOL AudioEngine::CheckForDirectXSupport()
+{
+    HMODULE pTestDXLib;
+    FARPROC pFunctionality;
+
+    pTestDXLib=LoadLibrary("DSOUND");
+    if (pTestDXLib!=NULL)  // check if there is a DirectSound
+    {
+        pFunctionality = GetProcAddress(pTestDXLib, (char*) 7);
+        if (pFunctionality!=NULL)
+        {
+            FreeLibrary(pTestDXLib);
+            return _TRUE;
+        }
+        else
+        {
+            FreeLibrary(pTestDXLib);
+            return _FALSE;
+        }
+    }
+    else
+        return _FALSE;
+}
+
+
+ZERROR AudioEngine::LoadPALib()
+{
+#ifdef _DEBUG
+    if (bSupportDirectX)
+        pPaDll  = LoadLibrary("PA_DXD");
+    else
+        pPaDll  = LoadLibrary("PA_MMED");
+#else
+    if (bSupportDirectX)
+        pPaDll  = LoadLibrary("PA_DX");
+    else
+        pPaDll  = LoadLibrary("PA_MME");
+#endif
+    if (pPaDll!=NULL)
+    {
+
+        Pa_Initialize    = (int (__cdecl*)(void))GetProcAddress(pPaDll,"Pa_Initialize");
+        Pa_Terminate    = (int (__cdecl*)(void))GetProcAddress(pPaDll,"Pa_Terminate");
+        Pa_GetHostError    = (long (__cdecl* )( void )) GetProcAddress(pPaDll,"Pa_GetHostError");
+        Pa_GetErrorText    = (const char* (__cdecl* )( PaError )) GetProcAddress(pPaDll,"Pa_GetErrorText");
+        Pa_CountDevices    = (int (__cdecl*)(void))GetProcAddress(pPaDll,"Pa_CountDevices");
+        Pa_GetDefaultInputDeviceID = (int (__cdecl*)(void))GetProcAddress(pPaDll,"Pa_GetDefaultInputDeviceID");
+        Pa_GetDefaultOutputDeviceID = (int (__cdecl*)(void))GetProcAddress(pPaDll,"Pa_GetDefaultOutputDeviceID");
+        Pa_GetDeviceInfo   = (const PaDeviceInfo* (__cdecl* )( PaDeviceID)) GetProcAddress(pPaDll,"Pa_GetDeviceInfo");
+        Pa_OpenStream    = ( PaError (__cdecl* )(
+                                 PortAudioStream ** ,
+                                 PaDeviceID ,
+                                 int ,
+                                 PaSampleFormat ,
+                                 void *,
+                                 PaDeviceID ,
+                                 int ,
+                                 PaSampleFormat ,
+                                 void *,
+                                 double ,
+                                 unsigned long ,
+                                 unsigned long ,
+                                 unsigned long ,
+                                 PortAudioCallback *,
+                                 void * )) GetProcAddress(pPaDll,"Pa_OpenStream");
+
+        Pa_OpenDefaultStream  = (PaError (__cdecl* )( PortAudioStream** ,
+                                 int ,
+                                 int ,
+                                 PaSampleFormat ,
+                                 double ,
+                                 unsigned long ,
+                                 unsigned long ,
+                                 PortAudioCallback *,
+                                 void * )) GetProcAddress(pPaDll,"Pa_OpenDefaultStream");
+        Pa_CloseStream    = (PaError (__cdecl* )( PortAudioStream* )) GetProcAddress(pPaDll,"Pa_CloseStream");
+        Pa_StartStream    = (PaError (__cdecl* )( PortAudioStream* )) GetProcAddress(pPaDll,"Pa_StartStream");
+        Pa_StopStream    = (PaError (__cdecl* )( PortAudioStream* ))GetProcAddress(pPaDll,"Pa_StopStream");
+        Pa_AbortStream    = (PaError (__cdecl* )( PortAudioStream* )) GetProcAddress(pPaDll,"Pa_AbortStream");
+        Pa_StreamActive    = (PaError (__cdecl* )( PortAudioStream* )) GetProcAddress(pPaDll,"Pa_StreamActive");
+        Pa_StreamTime    = (PaTimestamp (__cdecl* )( PortAudioStream *))GetProcAddress(pPaDll,"Pa_StreamTime");
+        Pa_GetCPULoad    = (double (__cdecl* )( PortAudioStream* ))GetProcAddress(pPaDll,"Pa_GetCPULoad");
+        Pa_GetMinNumBuffers   = (int (__cdecl* )( int , double )) GetProcAddress(pPaDll,"Pa_GetMinNumBuffers");
+        Pa_Sleep     = (void (__cdecl* )( long )) GetProcAddress(pPaDll,"Pa_Sleep");
+        Pa_GetSampleSize   = (PaError (__cdecl* )( PaSampleFormat )) GetProcAddress(pPaDll,"Pa_GetSampleSize");
+
+        return _NO_ERROR;
+    }
+    else
+        return _DLL_NOT_FOUND;
+}
+
+ZERROR AudioEngine::UnLoadPALib()
+{
+    if (pPaDll!=NULL)
+        FreeLibrary(pPaDll);
+    return _NO_ERROR;
+}
+
+...
diff --git a/pa_dll_switch/pa_lib.c b/pa_dll_switch/pa_lib.c
new file mode 100644 (file)
index 0000000..8660159
--- /dev/null
@@ -0,0 +1,827 @@
+/*
+ * 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;
+}
+
+
diff --git a/pa_dll_switch/portaudio.h b/pa_dll_switch/portaudio.h
new file mode 100644 (file)
index 0000000..9632521
--- /dev/null
@@ -0,0 +1,439 @@
+#ifndef PORT_AUDIO_H
+#define PORT_AUDIO_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif /* __cplusplus */
+
+/*
+ * PortAudio Portable Real-Time Audio Library
+ * PortAudio API Header File
+ * Latest version available at: http://www.audiomulch.com/portaudio/
+ *
+ * Copyright (c) 1999-2000 Ross Bencina and 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.
+ *
+ */
+
+// added by zplane.developement in order to generate a DLL
+
+#if defined(PA_MME_EXPORTS) || defined(PA_DX_EXPORTS)
+#define DLL_API __declspec( dllexport )
+#elif defined(_LIB) || defined(_STATIC_LINK) || defined(_STATIC_APP)
+#define DLL_API
+#else
+#define DLL_API __declspec(dllexport)
+#endif
+
+
+typedef int PaError;
+typedef enum {
+    paNoError = 0,
+
+    paHostError = -10000,
+    paInvalidChannelCount,
+    paInvalidSampleRate,
+    paInvalidDeviceId,
+    paInvalidFlag,
+    paSampleFormatNotSupported,
+    paBadIODeviceCombination,
+    paInsufficientMemory,
+    paBufferTooBig,
+    paBufferTooSmall,
+    paNullCallback,
+    paBadStreamPtr,
+    paTimedOut,
+    paInternalError
+} PaErrorNum;
+
+/*
+ Pa_Initialize() is the library initialisation function - call this before
+ using the library.
+*/
+
+DLL_API PaError Pa_Initialize( void );
+
+/*
+ Pa_Terminate() is the library termination function - call this after
+ using the library.
+*/
+
+DLL_API PaError Pa_Terminate( void );
+
+/*
+ Return host specific error.
+ This can be called after receiving a paHostError.
+*/
+DLL_API long Pa_GetHostError( void );
+
+/*
+ Translate the error number into a human readable message.
+*/
+DLL_API const char *Pa_GetErrorText( PaError errnum );
+
+/*
+ Sample formats
+ These are formats used to pass sound data between the callback and the
+ stream. Each device has a "native" format which may be used when optimum
+ efficiency or control over conversion is required.
+ Formats marked "always available" are supported (emulated) by all devices.
+ The floating point representation uses +1.0 and -1.0 as the respective
+ maximum and minimum.
+*/
+
+typedef unsigned long PaSampleFormat;
+#define paFloat32      ((PaSampleFormat) (1<<0)) /*always available*/
+#define paInt16        ((PaSampleFormat) (1<<1)) /*always available*/
+#define paInt32        ((PaSampleFormat) (1<<2)) /*always available*/
+#define paInt24        ((PaSampleFormat) (1<<3))
+#define paPackedInt24  ((PaSampleFormat) (1<<4))
+#define paInt8         ((PaSampleFormat) (1<<5))
+#define paUInt8        ((PaSampleFormat) (1<<6))    /* unsigned 8 bit, 128 is "ground" */
+#define paCustomFormat ((PaSampleFormat) (1<<16))
+
+/*
+ Device enumeration mechanism.
+    Device ids range from 0 to Pa_CountDevices()-1.
+ Devices may support input, output or both. Device 0 is always the "default"
+ device and should support at least stereo in and out if that is available
+ on the taget platform _even_ if this involves kludging an input/output
+ device on platforms that usually separate input from output. Other platform
+ specific devices are specified by positive device ids.
+*/
+
+typedef int PaDeviceID;
+#define paNoDevice -1
+
+typedef struct
+{
+    int structVersion;
+    const char *name;
+    int maxInputChannels;
+    int maxOutputChannels;
+    /* Number of discrete rates, or -1 if range supported. */
+    int numSampleRates;
+    /* Array of supported sample rates, or {min,max} if range supported. */
+    const double *sampleRates;
+    PaSampleFormat nativeSampleFormats;
+}
+PaDeviceInfo;
+
+
+DLL_API int Pa_CountDevices();
+/*
+ Pa_GetDefaultInputDeviceID(), Pa_GetDefaultOutputDeviceID()
+ Return the default device ID or paNoDevice if there is no devices.
+ The result can be passed to Pa_OpenStream().
+ On the PC, the user can specify a default device by
+ setting an environment variable. For example, to use device #1.
+  set PA_RECOMMENDED_OUTPUT_DEVICE=1
+ The user should first determine the available device ID by using
+ the supplied application "pa_devs".
+*/
+DLL_API PaDeviceID Pa_GetDefaultInputDeviceID( void );
+DLL_API PaDeviceID Pa_GetDefaultOutputDeviceID( void );
+
+/*
+ PaTimestamp is used to represent a continuous sample clock with arbitrary
+ start time useful for syncronisation. The type is used in the outTime
+ argument to the callback function and the result of Pa_StreamTime()
+*/
+
+typedef double PaTimestamp;
+
+/*
+ Pa_GetDeviceInfo() returns a pointer to an immutable PaDeviceInfo structure
+ referring to the device specified by id.
+ If id is out of range the function returns NULL.
+ The returned structure is owned by the PortAudio implementation and must
+ not be manipulated or freed. The pointer is guaranteed to be valid until
+ between calls to Pa_Initialize() and Pa_Terminate().
+*/
+
+DLL_API const PaDeviceInfo* Pa_GetDeviceInfo( PaDeviceID id );
+
+/*
+ PortAudioCallback is implemented by clients of the portable audio api.
+ inputBuffer and outputBuffer are arrays of interleaved samples,
+ the format, packing and number of channels used by the buffers are
+ determined by parameters to Pa_OpenStream() (see below).
+ framesPerBuffer is the number of sample frames to be processed by the callback.
+ outTime is the time in samples when the buffer(s) processed by
+ this callback will begin being played at the audio output.
+ See also Pa_StreamTime()
+ userData is the value of a user supplied pointer passed to Pa_OpenStream()
+ intended for storing synthesis data etc.
+ return value:
+ The callback can return a nonzero value to stop the stream. This may be
+ useful in applications such as soundfile players where a specific duration
+ of output is required. However, it is not necessary to utilise this mechanism
+ as StopStream() will also terminate the stream. A callback returning a
+ nonzero value must fill the entire outputBuffer.
+ NOTE: None of the other stream functions may be called from within the
+ callback function except for Pa_GetCPULoad().
+*/
+
+typedef int (PortAudioCallback)(
+    void *inputBuffer, void *outputBuffer,
+    unsigned long framesPerBuffer,
+    PaTimestamp outTime, void *userData );
+
+
+/*
+ Stream flags
+ These flags may be supplied (ored together) in the streamFlags argument to
+ the Pa_OpenStream() function.
+ [ suggestions? ]
+*/
+
+#define   paNoFlag      (0)
+#define   paClipOff     (1<<0)   /* disable defult clipping of out of range samples */
+#define   paDitherOff   (1<<1)   /* disable default dithering */
+#define   paPlatformSpecificFlags (0x00010000)
+typedef   unsigned long PaStreamFlags;
+
+/*
+ A single PortAudioStream provides multiple channels of real-time
+ input and output audio streaming to a client application.
+ Pointers to PortAudioStream objects are passed between PortAudio functions.
+*/
+
+typedef void PortAudioStream;
+#define PaStream PortAudioStream
+
+/*
+ Pa_OpenStream() opens a stream for either input, output or both.
+ stream is the address of a PortAudioStream pointer which will receive
+ a pointer to the newly opened stream.
+ inputDevice is the id of the device used for input (see PaDeviceID above.)
+ inputDevice may be paNoDevice to indicate that an input device is not required.
+ numInputChannels is the number of channels of sound to be delivered to the
+ callback. It can range from 1 to the value of maxInputChannels in the
+ device input record for the device specified in the inputDevice parameter.
+ If inputDevice is paNoDevice numInputChannels is ignored.
+ inputSampleFormat is the format of inputBuffer provided to the callback
+ function. inputSampleFormat may be any of the formats described by the
+ PaSampleFormat enumeration (see above). PortAudio guarantees support for
+ the sound devices native formats (nativeSampleFormats in the device info
+ record) and additionally 16 and 32 bit integer and 32 bit floating point
+ formats. Support for other formats is implementation defined.
+ inputDriverInfo is a pointer to an optional driver specific data structure
+ containing additional information for device setup or stream processing.
+ inputDriverInfo is never required for correct operation. If not used
+ inputDriverInfo should be NULL.
+ outputDevice is the id of the device used for output (see PaDeviceID above.)
+ outputDevice may be paNoDevice to indicate that an output device is not required.
+ numOutputChannels is the number of channels of sound to be supplied by the
+ callback. See the definition of numInputChannels above for more details.
+ outputSampleFormat is the sample format of the outputBuffer filled by the
+ callback function. See the definition of inputSampleFormat above for more
+ details.
+ outputDriverInfo is a pointer to an optional driver specific data structure
+ containing additional information for device setup or stream processing.
+ outputDriverInfo is never required for correct operation. If not used
+ outputDriverInfo should be NULL.
+ sampleRate is the desired sampleRate for input and output
+ framesPerBuffer is the length in sample frames of all internal sample buffers
+ used for communication with platform specific audio routines. Wherever
+ possible this corresponds to the framesPerBuffer parameter passed to the
+ callback function.
+ numberOfBuffers is the number of buffers used for multibuffered
+ communication with the platform specific audio routines. This parameter is
+ provided only as a guide - and does not imply that an implementation must
+ use multibuffered i/o when reliable double buffering is available (such as
+ SndPlayDoubleBuffer() on the Macintosh.)
+ streamFlags may contain a combination of flags ORed together.
+ These flags modify the behavior of the
+ streaming process. Some flags may only be relevant to certain buffer formats.
+ callback is a pointer to a client supplied function that is responsible
+ for processing and filling input and output buffers (see above for details.)
+ userData is a client supplied pointer which is passed to the callback
+ function. It could for example, contain a pointer to instance data necessary
+ for processing the audio buffers.
+ return value:
+ Apon success Pa_OpenStream() returns PaNoError and places a pointer to a
+ valid PortAudioStream in the stream argument. The stream is inactive (stopped).
+ If a call to Pa_OpenStream() fails a nonzero error code is returned (see
+ PAError above) and the value of stream is invalid.
+*/
+
+DLL_API PaError Pa_OpenStream( PortAudioStream** stream,
+                               PaDeviceID inputDevice,
+                               int numInputChannels,
+                               PaSampleFormat inputSampleFormat,
+                               void *inputDriverInfo,
+                               PaDeviceID outputDevice,
+                               int numOutputChannels,
+                               PaSampleFormat outputSampleFormat,
+                               void *outputDriverInfo,
+                               double sampleRate,
+                               unsigned long framesPerBuffer,
+                               unsigned long numberOfBuffers,
+                               PaStreamFlags streamFlags,
+                               PortAudioCallback *callback,
+                               void *userData );
+
+
+/*
+ Pa_OpenDefaultStream() is a simplified version of Pa_OpenStream() that
+ opens the default input and/or ouput devices. Most parameters have
+ identical meaning to their Pa_OpenStream() counterparts, with the following
+ exceptions:
+ If either numInputChannels or numOutputChannels is 0 the respective device
+ is not opened (same as passing paNoDevice in the device arguments to Pa_OpenStream() )
+ sampleFormat applies to both the input and output buffers.
+*/
+
+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 );
+
+/*
+ Pa_CloseStream() closes an audio stream, flushing any pending buffers.
+*/
+
+DLL_API PaError Pa_CloseStream( PortAudioStream* );
+
+/*
+  Pa_StartStream() and Pa_StopStream() begin and terminate audio processing.
+ When Pa_StopStream() returns, all pending audio buffers have been played.
+    Pa_AbortStream() stops playing immediately without waiting for pending
+    buffers to complete.
+*/
+
+DLL_API PaError Pa_StartStream( PortAudioStream *stream );
+
+DLL_API PaError Pa_StopStream( PortAudioStream *stream );
+
+DLL_API PaError Pa_AbortStream( PortAudioStream *stream );
+
+/*
+ Pa_StreamActive() returns one when the stream is playing audio,
+ zero when not playing, or a negative error number if the
+ stream is invalid.
+ The stream is active between calls to Pa_StartStream() and Pa_StopStream(),
+ but may also become inactive if the callback returns a non-zero value.
+ In the latter case, the stream is considered inactive after the last
+ buffer has finished playing.
+*/
+
+DLL_API PaError Pa_StreamActive( PortAudioStream *stream );
+
+/*
+ Pa_StreamTime() returns the current output time for the stream in samples.
+ This time may be used as a time reference (for example syncronising audio to
+ MIDI).
+*/
+
+DLL_API PaTimestamp Pa_StreamTime( PortAudioStream *stream );
+
+/*
+ The "CPU Load" is a fraction of total CPU time consumed by the
+ stream's audio processing.
+ A value of 0.5 would imply that PortAudio and the sound generating
+ callback was consuming roughly 50% of the available CPU time.
+ This function may be called from the callback function or the application.
+*/
+DLL_API double Pa_GetCPULoad( PortAudioStream* stream );
+
+/*
+ Use Pa_GetMinNumBuffers() to determine minimum number of buffers required for
+ the current host based on minimum latency. 
+ On the PC, for the DirectSound implementation, 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 AUTOEXEC.BAT file and reboot.
+ If the environment variable is not set, then the latency will be determined
+ based on the OS. Windows NT has higher latency than Win95.
+*/
+
+DLL_API int Pa_GetMinNumBuffers( int framesPerBuffer, double sampleRate );
+
+/*
+ Sleep for at least 'msec' milliseconds.
+ You may sleep longer than the requested time so don't rely
+ on this for accurate musical timing.
+*/
+DLL_API void Pa_Sleep( long msec );
+
+/*
+ Return size in bytes of a single sample in a given PaSampleFormat
+ or paSampleFormatNotSupported. 
+*/
+DLL_API PaError Pa_GetSampleSize( PaSampleFormat format );
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif /* PORT_AUDIO_H */
diff --git a/pa_mac/pa_mac.c b/pa_mac/pa_mac.c
new file mode 100644 (file)
index 0000000..cde9a95
--- /dev/null
@@ -0,0 +1,1567 @@
+/*
+ * $Id$
+ * Portable Audio I/O Library for Macintosh
+ *
+ * Based on the Open Source API proposed by Ross Bencina
+ * Copyright (c) 1999-2000 Phil Burk
+ *
+ * Special thanks to Chris Rolfe for his many helpful suggestions, bug fixes,
+ * and code contributions.
+ * Thanks also to Tue Haste Andersen, Alberto Ricci, Nico Wald,
+ * Roelf Toxopeus and Tom Erbe for testing the code and making
+ * numerous suggestions.
+ *
+ * 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
+   PLB20010415 - ScanInputDevices was setting sDefaultOutputDeviceID instead of sDefaultInputDeviceID
+   PLB20010415 - Device Scan was crashing for anything other than siBadSoundInDevice, but some Macs may return other errors!
+   PLB20010420 - Fix TIMEOUT in record mode.
+   PLB20010420 - Change CARBON_COMPATIBLE to TARGET_API_MAC_CARBON
+   PLB20010907 - Pass unused event to WaitNextEvent to prevent Mac OSX crash. Thanks Dominic Mazzoni.
+   PLB20010908 - Use requested number of input channels. Thanks Dominic Mazzoni.
+   PLB20011009 - Use NewSndCallBackUPP() for CARBON
+*/
+/*
+COMPATIBILITY
+This Macintosh implementation is designed for use with Mac OS 7, 8 and
+9 on PowerMacs, and OS X if compiled with CARBON
+OUTPUT
+A circular array of CmpSoundHeaders is used as a queue. For low latency situations
+there will only be two small buffers used. For higher latency, more and larger buffers
+may be used.
+To play the sound we use SndDoCommand() with bufferCmd. Each buffer is followed
+by a callbackCmd which informs us when the buffer has been processsed.
+INPUT
+The SndInput Manager SPBRecord call is used for sound input. If only
+input is used, then the PA user callback is called from the Input completion proc.
+For full-duplex, or output only operation, the PA callback is called from the
+HostBuffer output completion proc. In that case, input sound is passed to the
+callback by a simple FIFO.
+TODO:
+O- Add support for native sample data formats other than int16.
+O- Review buffer sizing. Should it be based on result of siDeviceBufferInfo query?
+O- Determine default devices somehow.
+*/
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <memory.h>
+#include <math.h>
+/* Mac specific includes */
+#include "OSUtils.h"
+#include <MacTypes.h>
+#include <Math64.h>
+#include <Errors.h>
+#include <Sound.h>
+#include <SoundInput.h>
+#include <SoundComponents.h>
+#include <Devices.h>
+#include <DateTimeUtils.h>
+#include <Timer.h>
+#include <Gestalt.h>
+#include "portaudio.h"
+#include "pa_host.h"
+#include "pa_trace.h"
+
+#ifndef FALSE
+ #define FALSE  (0)
+ #define TRUE   (!FALSE)
+#endif
+
+/* Debugging output macros. */
+#define PRINT(x) { printf x; fflush(stdout); }
+#define ERR_RPT(x) PRINT(x)
+#define DBUG(x)   /* PRINT(x) /**/
+#define DBUGX(x)  /* PRINT(x) /**/
+
+#define MAC_PHYSICAL_FRAMES_PER_BUFFER   (512)  /* Minimum number of stereo frames per SoundManager double buffer. */
+#define MAC_VIRTUAL_FRAMES_PER_BUFFER   (4096) /* Need this many when using Virtual Memory for recording. */
+#define PA_MIN_NUM_HOST_BUFFERS           (2)
+#define PA_MAX_NUM_HOST_BUFFERS           (16)   /* Do not exceed!! */
+#define PA_MAX_DEVICE_INFO                (32)
+
+/* Conversions for 16.16 fixed point code. */
+#define DoubleToUnsignedFixed(x) ((UnsignedFixed) ((x) * 65536.0))
+#define UnsignedFixedToDouble(fx) (((double)(fx)) * (1.0/(1<<16)))
+
+/************************************************************************************/
+/****************** Structures ******************************************************/
+/************************************************************************************/
+/* Use for passing buffers from input callback to output callback for processing. */
+typedef struct MultiBuffer
+{
+    char    *buffers[PA_MAX_NUM_HOST_BUFFERS];
+    int      numBuffers;
+    int      nextWrite;
+    int      nextRead;
+}
+MultiBuffer;
+
+/* Define structure to contain all Macintosh specific data. */
+typedef struct PaHostSoundControl
+{
+    UInt64                  pahsc_EntryCount;
+    UInt64                  pahsc_LastExitCount;
+    /* Use char instead of Boolean for atomic operation. */
+    volatile char           pahsc_IsRecording;   /* Recording in progress. Set by foreground. Cleared by background. */
+    volatile char           pahsc_StopRecording; /* Signal sent to background. */
+    volatile char           pahsc_IfInsideCallback;
+    /* Input */
+    SPB                     pahsc_InputParams;
+    SICompletionUPP         pahsc_InputCompletionProc;
+    MultiBuffer    pahsc_InputMultiBuffer;
+    int32     pahsc_BytesPerInputHostBuffer;
+    int32                   pahsc_InputRefNum;
+    /* Output */
+    CmpSoundHeader          pahsc_SoundHeaders[PA_MAX_NUM_HOST_BUFFERS];
+    int32                   pahsc_BytesPerOutputHostBuffer;
+    SndChannelPtr   pahsc_Channel;
+    SndCallBackUPP          pahsc_OutputCompletionProc;
+    int32                   pahsc_NumOutsQueued;
+    int32                   pahsc_NumOutsPlayed;
+    PaTimestamp             pahsc_NumFramesDone;
+    /* Init Time -------------- */
+    int32                   pahsc_NumHostBuffers;
+    int32                   pahsc_FramesPerHostBuffer;
+    int32                   pahsc_UserBuffersPerHostBuffer;
+    int32     pahsc_MinFramesPerHostBuffer; /* Can vary depending on virtual memory usage. */
+}
+PaHostSoundControl;
+
+/* Mac specific device information. */
+typedef struct internalPortAudioDevice
+{
+    long                    pad_DeviceRefNum;
+    long                    pad_DeviceBufferSize;
+    Component               pad_Identifier;
+    PaDeviceInfo            pad_Info;
+}
+internalPortAudioDevice;
+
+/************************************************************************************/
+/****************** Data ************************************************************/
+/************************************************************************************/
+static int                 sNumDevices = 0;
+static internalPortAudioDevice sDevices[PA_MAX_DEVICE_INFO] = { 0 };
+static int32               sPaHostError = 0;
+static int                 sDefaultOutputDeviceID;
+static int                 sDefaultInputDeviceID;
+
+/************************************************************************************/
+/****************** Prototypes ******************************************************/
+/************************************************************************************/
+static PaError PaMac_TimeSlice( internalPortAudioStream   *past,  int16 *macOutputBufPtr );
+static PaError PaMac_CallUserLoop( internalPortAudioStream   *past, int16 *outPtr );
+static PaError PaMac_RecordNext( internalPortAudioStream   *past );static void    StartLoadCalculation( internalPortAudioStream   *past );
+static int     PaMac_GetMinNumBuffers( int minFramesPerHostBuffer, int framesPerBuffer, double sampleRate );
+static double *PaMac_GetSampleRatesFromHandle ( int numRates, Handle h );
+static PaError PaMac_ScanInputDevices( void );
+static PaError PaMac_ScanOutputDevices( void );
+static PaError PaMac_QueryOutputDeviceInfo( Component identifier, internalPortAudioDevice *ipad );
+static PaError PaMac_QueryInputDeviceInfo( Str255 deviceName, internalPortAudioDevice *ipad );
+static void    PaMac_InitSoundHeader( internalPortAudioStream   *past, CmpSoundHeader *sndHeader );
+static void    PaMac_EndLoadCalculation( internalPortAudioStream   *past );
+static void    PaMac_PlayNext ( internalPortAudioStream *past, int index );
+static long    PaMac_FillNextOutputBuffer( internalPortAudioStream   *past, int index );
+static pascal void PaMac_InputCompletionProc(SPBPtr recParams);
+static pascal void PaMac_OutputCompletionProc (SndChannelPtr theChannel, SndCommand * theCmd);
+static PaError PaMac_BackgroundManager( internalPortAudioStream   *past, int index );
+long PaHost_GetTotalBufferFrames( internalPortAudioStream   *past );
+static int     Mac_IsVirtualMemoryOn( void );
+static void    PToCString(unsigned char* inString, char* outString);
+char *MultiBuffer_GetNextWriteBuffer( MultiBuffer *mbuf );
+char *MultiBuffer_GetNextReadBuffer( MultiBuffer *mbuf );
+int   MultiBuffer_GetNextReadIndex( MultiBuffer *mbuf );
+int   MultiBuffer_GetNextWriteIndex( MultiBuffer *mbuf );
+int   MultiBuffer_IsWriteable(  MultiBuffer *mbuf );
+int   MultiBuffer_IsReadable(  MultiBuffer *mbuf );
+void  MultiBuffer_AdvanceReadIndex(  MultiBuffer *mbuf );
+void  MultiBuffer_AdvanceWriteIndex(  MultiBuffer *mbuf );
+
+/*************************************************************************
+** Simple FIFO index control for multiple buffers.
+** Read and Write indices range from 0 to 2N-1.
+** This allows us to distinguish between full and empty.
+*/
+char *MultiBuffer_GetNextWriteBuffer( MultiBuffer *mbuf )
+{
+    return mbuf->buffers[mbuf->nextWrite % mbuf->numBuffers];
+}
+char *MultiBuffer_GetNextReadBuffer( MultiBuffer *mbuf )
+{
+    return mbuf->buffers[mbuf->nextRead % mbuf->numBuffers];
+}
+int MultiBuffer_GetNextReadIndex( MultiBuffer *mbuf )
+{
+    return mbuf->nextRead % mbuf->numBuffers;
+}
+int MultiBuffer_GetNextWriteIndex( MultiBuffer *mbuf )
+{
+    return mbuf->nextWrite % mbuf->numBuffers;
+}
+
+int MultiBuffer_IsWriteable(  MultiBuffer *mbuf )
+{
+    int bufsFull = mbuf->nextWrite - mbuf->nextRead;
+    if( bufsFull < 0 ) bufsFull += (2 * mbuf->numBuffers);
+    return (bufsFull < mbuf->numBuffers);
+}
+int MultiBuffer_IsReadable(  MultiBuffer *mbuf )
+{
+    int bufsFull = mbuf->nextWrite - mbuf->nextRead;
+    if( bufsFull < 0 ) bufsFull += (2 * mbuf->numBuffers);
+    return (bufsFull > 0);
+}
+void MultiBuffer_AdvanceReadIndex(  MultiBuffer *mbuf )
+{
+    int temp = mbuf->nextRead + 1;
+    mbuf->nextRead = (temp >= (2 * mbuf->numBuffers)) ? 0 : temp;
+}
+void MultiBuffer_AdvanceWriteIndex(  MultiBuffer *mbuf )
+{
+    int temp = mbuf->nextWrite + 1;
+    mbuf->nextWrite = (temp >= (2 * mbuf->numBuffers)) ? 0 : temp;
+}
+
+/*************************************************************************
+** String Utility by Chris Rolfe
+*/
+static void PToCString(unsigned char* inString, char* outString)
+{
+    long i;
+    for(i=0; i<inString[0]; i++)  /* convert Pascal to C string */
+        outString[i] = inString[i+1];
+    outString[i]=0;
+}
+
+/*************************************************************************/
+PaError PaHost_Term( void )
+{
+    int           i;
+    PaDeviceInfo *dev;
+    double       *rates;
+    /* Free any allocated sample rate arrays. */
+    for( i=0; i<sNumDevices; i++ )
+    {
+        dev =  &sDevices[i].pad_Info;
+        rates = (double *) dev->sampleRates;
+        if( (rates != NULL) ) free( rates ); /* MEM_011 */
+        dev->sampleRates = NULL;
+        if( dev->name != NULL ) free( (void *) dev->name ); /* MEM_010 */
+        dev->name = NULL;
+    }
+    sNumDevices = 0;
+    return paNoError;
+}
+
+/*************************************************************************
+ PaHost_Init() is the library initialization function - call this before
+    using the library.
+*/
+PaError PaHost_Init( void )
+{
+    PaError err;
+    NumVersionVariant version;
+
+    version.parts = SndSoundManagerVersion();
+    DBUG(("SndSoundManagerVersion = 0x%x\n", version.whole));
+
+    /* Have we already initialized the device info? */
+    err = (PaError) Pa_CountDevices();
+    if( err < 0 ) return err;
+    else return paNoError;
+}
+
+/*************************************************************************
+ PaMac_ScanOutputDevices() queries the properties of all output devices.
+*/
+static PaError PaMac_ScanOutputDevices( void )
+{
+    PaError       err;
+    Component     identifier=0;
+    ComponentDescription criteria = { kSoundOutputDeviceType, 0, 0, 0, 0 };
+    long       numComponents, i;
+
+    /* Search the system linked list for output components  */
+    numComponents = CountComponents (&criteria);
+    identifier = 0;
+    sDefaultOutputDeviceID = sNumDevices; /* FIXME - query somehow */
+    for (i = 0; i < numComponents; i++)
+    {
+        /* passing nil returns first matching component. */
+        identifier = FindNextComponent( identifier, &criteria);
+        sDevices[sNumDevices].pad_Identifier = identifier;
+
+        /* Set up for default OUTPUT devices. */
+        err = PaMac_QueryOutputDeviceInfo( identifier, &sDevices[sNumDevices] );
+        if( err < 0 ) return err;
+        else  sNumDevices++;
+
+    }
+
+    return paNoError;
+}
+
+/*************************************************************************
+ PaMac_ScanInputDevices() queries the properties of all input devices.
+*/
+static PaError PaMac_ScanInputDevices( void )
+{
+    Str255     deviceName;
+    int        count;
+    Handle     iconHandle;
+    PaError    err;
+    OSErr      oserr;
+    count = 1;
+    sDefaultInputDeviceID = sNumDevices; /* FIXME - query somehow */ /* PLB20010415 - was setting sDefaultOutputDeviceID */
+    while(true)
+    {
+        /* Thanks Chris Rolfe and Alberto Ricci for this trick. */
+        oserr = SPBGetIndexedDevice(count++, deviceName, &iconHandle);
+        DBUG(("PaMac_ScanInputDevices: SPBGetIndexedDevice returned %d\n", oserr ));
+#if 1
+        /* PLB20010415 - was giving error for anything other than siBadSoundInDevice, but some Macs may return other errors! */
+        if(oserr != noErr) break; /* Some type of error is expected when count > devices */
+#else
+        if(oserr == siBadSoundInDevice)
+        {  /* it's expected when count > devices */
+            oserr = noErr;
+            break;
+        }
+        if(oserr != noErr)
+        {
+            ERR_RPT(("ERROR: SPBGetIndexedDevice(%d,,) returned %d\n", count-1, oserr ));
+            sPaHostError = oserr;
+            return paHostError;
+        }
+#endif
+        DisposeHandle(iconHandle);   /* Don't need the icon */
+
+        err = PaMac_QueryInputDeviceInfo( deviceName, &sDevices[sNumDevices] );
+        DBUG(("PaMac_ScanInputDevices: PaMac_QueryInputDeviceInfo returned %d\n", err ));
+        if( err < 0 ) return err;
+        else if( err == 1 ) sNumDevices++;
+    }
+
+    return paNoError;
+}
+
+/* Sample rate info returned by using siSampleRateAvailable selector in SPBGetDeviceInfo() */
+/* Thanks to Chris Rolfe for help with this query. */
+#pragma options align=mac68k
+typedef  struct
+{
+    int16                   numRates;
+    UnsignedFixed           (**rates)[]; /* Handle created by SPBGetDeviceInfo */
+}
+SRateInfo;
+#pragma options align=reset
+
+/*************************************************************************
+** PaMac_QueryOutputDeviceInfo()
+** Query information about a named output device.
+** Clears contents of ipad and writes info based on queries.
+** Return one if OK,
+**        zero if device cannot be used,
+**        or negative error.
+*/
+static PaError PaMac_QueryOutputDeviceInfo( Component identifier, internalPortAudioDevice *ipad )
+{
+    int     len;
+    OSErr   err;
+    PaDeviceInfo *dev =  &ipad->pad_Info;
+    SRateInfo srinfo = {0};
+    int     numRates;
+    ComponentDescription tempD;
+    Handle nameH=nil, infoH=nil, iconH=nil;
+
+    memset( ipad, 0, sizeof(internalPortAudioDevice) );
+
+    dev->structVersion = 1;
+    dev->maxInputChannels = 0;
+    dev->maxOutputChannels = 2;
+    dev->nativeSampleFormats = paInt16; /* FIXME - query to see if 24 or 32 bit data can be handled. */
+
+    /* Get sample rates supported. */
+    err = GetSoundOutputInfo(identifier, siSampleRateAvailable, (Ptr) &srinfo);
+    if(err != noErr)
+    {
+        ERR_RPT(("Error in PaMac_QueryOutputDeviceInfo: GetSoundOutputInfo siSampleRateAvailable returned %d\n", err ));
+        goto error;
+    }
+    numRates = srinfo.numRates;
+    DBUG(("PaMac_QueryOutputDeviceInfo: srinfo.numRates = 0x%x\n", srinfo.numRates ));
+    if( numRates == 0 )
+    {
+        dev->numSampleRates = -1;
+        numRates = 2;
+    }
+    else
+    {
+        dev->numSampleRates = numRates;
+    }
+    dev->sampleRates = PaMac_GetSampleRatesFromHandle( numRates, (Handle) srinfo.rates );
+    /* SPBGetDeviceInfo created the handle, but it's OUR job to release it. */
+    DisposeHandle((Handle) srinfo.rates);
+
+    /* Device name */
+    /*  we pass an existing handle for the component name;
+     we don't care about the info (type, subtype, etc.) or icon, so set them to nil */
+    infoH = nil;
+    iconH = nil;
+    nameH = NewHandle(0);
+    if(nameH == nil)  return paInsufficientMemory;
+    err = GetComponentInfo(identifier, &tempD, nameH, infoH, iconH);
+    if (err)
+    {
+        ERR_RPT(("Error in PaMac_QueryOutputDeviceInfo: GetComponentInfo returned %d\n", err ));
+        goto error;
+    }
+    len = (*nameH)[0] + 1;
+    dev->name = (char *) malloc(len);  /* MEM_010 */
+    if( dev->name == NULL )
+    {
+        DisposeHandle(nameH);
+        return paInsufficientMemory;
+    }
+    else
+    {
+        PToCString((unsigned char *)(*nameH), (char *) dev->name);
+        DisposeHandle(nameH);
+    }
+
+    DBUG(("PaMac_QueryOutputDeviceInfo: dev->name = %s\n", dev->name ));
+    return paNoError;
+
+error:
+    sPaHostError = err;
+    return paHostError;
+
+}
+
+/*************************************************************************
+** PaMac_QueryInputDeviceInfo()
+** Query information about a named input device.
+** Clears contents of ipad and writes info based on queries.
+** Return one if OK,
+**        zero if device cannot be used,
+**        or negative error.
+*/
+static PaError PaMac_QueryInputDeviceInfo( Str255 deviceName, internalPortAudioDevice *ipad )
+{
+    PaError result = paNoError;
+    int     len;
+    OSErr   err;
+    long    mRefNum = 0;
+    long    tempL;
+    int16   tempS;
+    Fixed   tempF;
+    PaDeviceInfo *dev =  &ipad->pad_Info;
+    SRateInfo srinfo = {0};
+    int     numRates;
+
+    memset( ipad, 0, sizeof(internalPortAudioDevice) );
+    dev->maxOutputChannels = 0;
+
+    /* Open device based on name. If device is in use, it may not be able to open in write mode. */
+    err = SPBOpenDevice( deviceName, siWritePermission, &mRefNum);
+    if (err)
+    {
+        /* If device is in use, it may not be able to open in write mode so try read mode. */
+        err = SPBOpenDevice( deviceName, siReadPermission, &mRefNum);
+        if (err)
+        {
+            ERR_RPT(("Error in PaMac_QueryInputDeviceInfo: SPBOpenDevice returned %d\n", err ));
+            sPaHostError = err;
+            return paHostError;
+        }
+    }
+
+    /* Define macros for printing out device info. */
+#define PrintDeviceInfo(selector,var) \
+    err = SPBGetDeviceInfo(mRefNum, selector, (Ptr) &var); \
+    if (err) { \
+        DBUG(("query %s failed\n", #selector )); \
+    }\
+    else { \
+        DBUG(("query %s = 0x%x\n", #selector, var )); \
+    }
+
+    PrintDeviceInfo( siContinuous, tempS );
+    PrintDeviceInfo( siAsync, tempS );
+    PrintDeviceInfo( siNumberChannels, tempS );
+    PrintDeviceInfo( siSampleSize, tempS );
+    PrintDeviceInfo( siSampleRate, tempF );
+    PrintDeviceInfo( siChannelAvailable, tempS );
+    PrintDeviceInfo( siActiveChannels, tempL );
+    PrintDeviceInfo( siDeviceBufferInfo, tempL );
+
+    err = SPBGetDeviceInfo(mRefNum, siActiveChannels, (Ptr) &tempL);
+    if (err == 0) DBUG(("%s = 0x%x\n", "siActiveChannels", tempL ));
+    /* Can we use this device? */
+    err = SPBGetDeviceInfo(mRefNum, siAsync, (Ptr) &tempS);
+    if (err)
+    {
+        ERR_RPT(("Error in PaMac_QueryInputDeviceInfo: SPBGetDeviceInfo siAsync returned %d\n", err ));
+        goto error;
+    }
+    if( tempS == 0 ) goto useless; /* Does not support async recording so forget about it. */
+
+    err = SPBGetDeviceInfo(mRefNum, siChannelAvailable, (Ptr) &tempS);
+    if (err)
+    {
+        ERR_RPT(("Error in PaMac_QueryInputDeviceInfo: SPBGetDeviceInfo siChannelAvailable returned %d\n", err ));
+        goto error;
+    }
+    dev->maxInputChannels = tempS;
+
+    /* Get sample rates supported. */
+    err = SPBGetDeviceInfo(mRefNum, siSampleRateAvailable, (Ptr) &srinfo);
+    if (err)
+    {
+        ERR_RPT(("Error in PaMac_QueryInputDeviceInfo: SPBGetDeviceInfo siSampleRateAvailable returned %d\n", err ));
+        goto error;
+    }
+
+    numRates = srinfo.numRates;
+    DBUG(("numRates = 0x%x\n", numRates ));
+    if( numRates == 0 )
+    {
+        dev->numSampleRates = -1;
+        numRates = 2;
+    }
+    else
+    {
+        dev->numSampleRates = numRates;
+    }
+    dev->sampleRates = PaMac_GetSampleRatesFromHandle( numRates, (Handle) srinfo.rates );
+    /* SPBGetDeviceInfo created the handle, but it's OUR job to release it. */
+    DisposeHandle((Handle) srinfo.rates);
+
+    /* Get size of device buffer. */
+    err = SPBGetDeviceInfo(mRefNum, siDeviceBufferInfo, (Ptr) &tempL);
+    if (err)
+    {
+        ERR_RPT(("Error in PaMac_QueryInputDeviceInfo: SPBGetDeviceInfo siDeviceBufferInfo returned %d\n", err ));
+        goto error;
+    }
+    ipad->pad_DeviceBufferSize = tempL;
+    DBUG(("siDeviceBufferInfo = %d\n", tempL ));
+
+    /* Set format based on sample size. */
+    err = SPBGetDeviceInfo(mRefNum, siSampleSize, (Ptr) &tempS);
+    if (err)
+    {
+        ERR_RPT(("Error in PaMac_QueryInputDeviceInfo: SPBGetDeviceInfo siSampleSize returned %d\n", err ));
+        goto error;
+    }
+    switch( tempS )
+    {
+    case 0x0020:
+        dev->nativeSampleFormats = paInt32;  /* FIXME - warning, code probably won't support this! */
+        break;
+    case 0x0010:
+    default: /* FIXME - What about other formats? */
+        dev->nativeSampleFormats = paInt16;
+        break;
+    }
+    DBUG(("nativeSampleFormats = %d\n", dev->nativeSampleFormats ));
+
+    /* Device name */
+    len = deviceName[0] + 1;  /* Get length of Pascal string */
+    dev->name = (char *) malloc(len);  /* MEM_010 */
+    if( dev->name == NULL )
+    {
+        result = paInsufficientMemory;
+        goto cleanup;
+    }
+    PToCString(deviceName, (char *) dev->name);
+    DBUG(("deviceName = %s\n", dev->name ));
+    result = (PaError) 1;
+    /* All done so close up device. */
+cleanup:
+    if( mRefNum )  SPBCloseDevice(mRefNum);
+    return result;
+
+error:
+    if( mRefNum )  SPBCloseDevice(mRefNum);
+    sPaHostError = err;
+    return paHostError;
+
+useless:
+    if( mRefNum )  SPBCloseDevice(mRefNum);
+    return (PaError) 0;
+}
+
+/*************************************************************************
+** Allocate a double array and fill it with listed sample rates.
+*/
+static double * PaMac_GetSampleRatesFromHandle ( int numRates, Handle h )
+{
+    OSErr   err = noErr;
+    SInt8   hState;
+    int     i;
+    UnsignedFixed *fixedRates;
+    double *rates = (double *) malloc( numRates * sizeof(double) ); /* MEM_011 */
+    if( rates == NULL ) return NULL;
+    /* Save and restore handle state as suggested by TechNote at:
+            http://developer.apple.com/technotes/tn/tn1122.html
+    */
+    hState = HGetState (h);
+    if (!(err = MemError ()))
+    {
+        HLock (h);
+        if (!(err = MemError ( )))
+        {
+            fixedRates = (UInt32 *) *h;
+            for( i=0; i<numRates; i++ )
+            {
+                rates[i] = UnsignedFixedToDouble(fixedRates[i]);
+            }
+
+            HSetState (h,hState);
+            err = MemError ( );
+        }
+    }
+    if( err )
+    {
+        free( rates );
+        ERR_RPT(("Error in PaMac_GetSampleRatesFromHandle = %d\n", err ));
+    }
+    return rates;
+}
+
+/*************************************************************************/
+int Pa_CountDevices()
+{
+    PaError err;
+    DBUG(("Pa_CountDevices()\n"));
+    /* If no devices, go find some. */
+    if( sNumDevices <= 0 )
+    {
+        err = PaMac_ScanOutputDevices();
+        if( err != paNoError ) goto error;
+        err = PaMac_ScanInputDevices();
+        if( err != paNoError ) goto error;
+    }
+    return sNumDevices;
+
+error:
+    PaHost_Term();
+    DBUG(("Pa_CountDevices: returns %d\n", err ));
+    return err;
+
+}
+
+/*************************************************************************/
+const PaDeviceInfo* Pa_GetDeviceInfo( PaDeviceID id )
+{
+    if( (id < 0) || ( id >= Pa_CountDevices()) ) return NULL;
+    return &sDevices[id].pad_Info;
+}
+/*************************************************************************/
+PaDeviceID Pa_GetDefaultInputDeviceID( void )
+{
+    return sDefaultInputDeviceID;
+}
+
+/*************************************************************************/
+PaDeviceID Pa_GetDefaultOutputDeviceID( void )
+{
+    return sDefaultOutputDeviceID;
+}
+
+/**************************************************************************/
+static void StartLoadCalculation( internalPortAudioStream   *past )
+{
+    PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData;
+    UnsignedWide widePad;
+    if( pahsc == NULL ) return;
+    /* Query system timer for usage analysis and to prevent overuse of CPU. */
+    Microseconds( &widePad );
+    pahsc->pahsc_EntryCount = UnsignedWideToUInt64( widePad );
+}
+
+/**************************************************************************/
+static void PaMac_EndLoadCalculation( internalPortAudioStream   *past )
+{
+    UnsignedWide widePad;
+    UInt64    CurrentCount;
+    long      InsideCount;
+    long      TotalCount;
+    PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData;
+    if( pahsc == NULL ) return;
+    /* 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)
+#define LOWPASS_COEFFICIENT_1   (0.99999 - LOWPASS_COEFFICIENT_0)
+    Microseconds( &widePad );
+    CurrentCount = UnsignedWideToUInt64( widePad );
+    if( past->past_IfLastExitValid )
+    {
+        InsideCount = (long) U64Subtract(CurrentCount, pahsc->pahsc_EntryCount);
+        TotalCount  = (long) U64Subtract(CurrentCount, pahsc->pahsc_LastExitCount);
+        /* 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;
+}
+
+/***********************************************************************
+** Called by Pa_StartStream()
+*/
+PaError PaHost_StartInput( internalPortAudioStream   *past )
+{
+    PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData;
+    pahsc->pahsc_IsRecording = 0;
+    pahsc->pahsc_StopRecording = 0;
+    pahsc->pahsc_InputMultiBuffer.nextWrite = 0;
+    pahsc->pahsc_InputMultiBuffer.nextRead = 0;
+    return PaMac_RecordNext( past );
+}
+
+/***********************************************************************
+** Called by Pa_StopStream().
+** May be called during error recovery or cleanup code
+** so protect against NULL pointers.
+*/
+PaError PaHost_StopInput( internalPortAudioStream   *past, int abort )
+{
+    int32   timeOutMsec;
+    PaError result = paNoError;
+    OSErr   err = 0;
+    long    mRefNum;
+    PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData;
+    if( pahsc == NULL ) return paNoError;
+
+    (void) abort;
+
+    mRefNum = pahsc->pahsc_InputRefNum;
+
+    DBUG(("PaHost_StopInput: mRefNum = %d\n", mRefNum ));
+    if( mRefNum )
+    {
+        DBUG(("PaHost_StopInput: pahsc_IsRecording = %d\n", pahsc->pahsc_IsRecording ));
+        if( pahsc->pahsc_IsRecording )
+        {
+            /* PLB20010420 - Fix TIMEOUT in record mode. */
+            pahsc->pahsc_StopRecording = 1; /* Request that we stop recording. */
+            err = SPBStopRecording(mRefNum);
+            DBUG(("PaHost_StopInput: then pahsc_IsRecording = %d\n", pahsc->pahsc_IsRecording ));
+
+            /* Calculate timeOut longer than longest time it could take to play one buffer. */
+            timeOutMsec = (int32) ((1500.0 * pahsc->pahsc_FramesPerHostBuffer) / past->past_SampleRate);
+            /* Keep querying sound channel until it is no longer busy playing. */
+            while( !err && pahsc->pahsc_IsRecording && (timeOutMsec > 0))
+            {
+                Pa_Sleep(20);
+                timeOutMsec -= 20;
+            }
+            if( timeOutMsec <= 0 )
+            {
+                ERR_RPT(("PaHost_StopInput: timed out!\n"));
+                return paTimedOut;
+            }
+        }
+    }
+    if( err )
+    {
+        sPaHostError = err;
+        result = paHostError;
+    }
+
+    DBUG(("PaHost_StopInput: finished.\n", mRefNum ));
+    return result;
+}
+
+/***********************************************************************/
+static void PaMac_InitSoundHeader( internalPortAudioStream   *past, CmpSoundHeader *sndHeader )
+{
+    sndHeader->numChannels = past->past_NumOutputChannels;
+    sndHeader->sampleRate = DoubleToUnsignedFixed(past->past_SampleRate);
+    sndHeader->loopStart = 0;
+    sndHeader->loopEnd = 0;
+    sndHeader->encode = cmpSH;
+    sndHeader->baseFrequency = kMiddleC;
+    sndHeader->markerChunk = nil;
+    sndHeader->futureUse2 = nil;
+    sndHeader->stateVars = nil;
+    sndHeader->leftOverSamples = nil;
+    sndHeader->compressionID = 0;
+    sndHeader->packetSize = 0;
+    sndHeader->snthID = 0;
+    sndHeader->sampleSize = 8 * sizeof(int16); // FIXME - might be 24 or 32 bits some day;
+    sndHeader->sampleArea[0] = 0;
+    sndHeader->format = kSoundNotCompressed;
+}
+
+/***********************************************************************/
+PaError PaHost_StartOutput( internalPortAudioStream   *past )
+{
+    SndCommand  pauseCommand;
+    SndCommand  resumeCommand;
+    int          i;
+    OSErr         error;
+    PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData;
+    if( pahsc == NULL ) return paInternalError;
+    if( pahsc->pahsc_Channel == NULL ) return paInternalError;
+
+    past->past_StopSoon = 0;
+    past->past_IsActive = 1;
+    pahsc->pahsc_NumOutsQueued = 0;
+    pahsc->pahsc_NumOutsPlayed = 0;
+    pahsc->pahsc_NumFramesDone = 0.0;
+
+    /* Pause channel so it does not do back ground processing while we are still filling the queue. */
+    pauseCommand.cmd = pauseCmd;
+    pauseCommand.param1 = pauseCommand.param2 = 0;
+    error = SndDoCommand (pahsc->pahsc_Channel, &pauseCommand, true);
+    if (noErr != error) goto exit;
+
+    /* Queue all of the buffers so we start off full. */
+    for (i = 0; i<pahsc->pahsc_NumHostBuffers; i++)
+    {
+        PaMac_PlayNext( past, i );
+    }
+
+    /* Resume channel now that the queue is full. */
+    resumeCommand.cmd = resumeCmd;
+    resumeCommand.param1 = resumeCommand.param2 = 0;
+    error = SndDoImmediate( pahsc->pahsc_Channel, &resumeCommand );
+    if (noErr != error) goto exit;
+
+    return paNoError;
+exit:
+    past->past_IsActive = 0;
+    sPaHostError = error;
+    ERR_RPT(("Error in PaHost_StartOutput: SndDoCommand returned %d\n", error ));
+    return paHostError;
+}
+
+/*******************************************************************/
+long PaHost_GetTotalBufferFrames( internalPortAudioStream   *past )
+{
+    PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData;
+    return (long) (pahsc->pahsc_NumHostBuffers * pahsc->pahsc_FramesPerHostBuffer);
+}
+
+/***********************************************************************
+** Called by Pa_StopStream().
+** May be called during error recovery or cleanup code
+** so protect against NULL pointers.
+*/
+PaError PaHost_StopOutput( internalPortAudioStream   *past, int abort )
+{
+    int32 timeOutMsec;
+    PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData;
+    if( pahsc == NULL ) return paNoError;
+    if( pahsc->pahsc_Channel == NULL ) return paNoError;
+
+    DBUG(("PaHost_StopOutput()\n"));
+    if( past->past_IsActive == 0 ) return paNoError;
+
+    /* Set flags for callback function to see. */
+    if( abort ) past->past_StopNow = 1;
+    past->past_StopSoon = 1;
+    /* Calculate timeOut longer than longest time it could take to play all buffers. */
+    timeOutMsec = (int32) ((1500.0 * PaHost_GetTotalBufferFrames( past )) / past->past_SampleRate);
+    /* Keep querying sound channel until it is no longer busy playing. */
+    while( past->past_IsActive && (timeOutMsec > 0))
+    {
+        Pa_Sleep(20);
+        timeOutMsec -= 20;
+    }
+    if( timeOutMsec <= 0 )
+    {
+        ERR_RPT(("PaHost_StopOutput: timed out!\n"));
+        return paTimedOut;
+    }
+    else return paNoError;
+}
+
+/***********************************************************************/
+PaError PaHost_StartEngine( internalPortAudioStream   *past )
+{
+    (void) past; /* Prevent unused variable warnings. */
+    return paNoError;
+}
+
+/***********************************************************************/
+PaError PaHost_StopEngine( internalPortAudioStream   *past, int abort )
+{
+    (void) past; /* Prevent unused variable warnings. */
+    (void) abort; /* Prevent unused variable warnings. */
+    return paNoError;
+}
+/***********************************************************************/
+PaError PaHost_StreamActive( internalPortAudioStream   *past )
+{
+    PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData;
+    return (PaError) ( past->past_IsActive + pahsc->pahsc_IsRecording );
+}
+int Mac_IsVirtualMemoryOn( void )
+{
+    long  attr;
+    OSErr result = Gestalt( gestaltVMAttr, &attr );
+    DBUG(("gestaltVMAttr : 0x%x\n", attr ));
+    return ((attr >> gestaltVMHasPagingControl ) & 1);
+}
+
+/*******************************************************************
+* Determine number of WAVE Buffers
+* and how many User Buffers we can put into each WAVE buffer.
+*/
+static void PaHost_CalcNumHostBuffers( internalPortAudioStream *past )
+{
+    PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData;
+    int32  minNumBuffers;
+    int32  minFramesPerHostBuffer;
+    int32  minTotalFrames;
+    int32  userBuffersPerHostBuffer;
+    int32  framesPerHostBuffer;
+    int32  numHostBuffers;
+    /* Calculate minimum and maximum sizes based on timing and sample rate. */
+    minFramesPerHostBuffer = pahsc->pahsc_MinFramesPerHostBuffer;
+    minFramesPerHostBuffer = (minFramesPerHostBuffer + 7) & ~7;
+    DBUG(("PaHost_CalcNumHostBuffers: minFramesPerHostBuffer = %d\n", minFramesPerHostBuffer ));
+    /* Determine number of user buffers based on minimum latency. */
+    minNumBuffers = Pa_GetMinNumBuffers( past->past_FramesPerUserBuffer, past->past_SampleRate );
+    past->past_NumUserBuffers = ( minNumBuffers > past->past_NumUserBuffers ) ? minNumBuffers : past->past_NumUserBuffers;
+    DBUG(("PaHost_CalcNumHostBuffers: min past_NumUserBuffers = %d\n", past->past_NumUserBuffers ));
+    minTotalFrames = past->past_NumUserBuffers * past->past_FramesPerUserBuffer;
+    /* We cannot make the WAVE buffers too small because they may not get serviced quickly enough. */
+    if( (int32) past->past_FramesPerUserBuffer < minFramesPerHostBuffer )
+    {
+        userBuffersPerHostBuffer =
+            (minFramesPerHostBuffer + past->past_FramesPerUserBuffer - 1) /
+            past->past_FramesPerUserBuffer;
+    }
+    else
+    {
+        userBuffersPerHostBuffer = 1;
+    }
+    framesPerHostBuffer = past->past_FramesPerUserBuffer * userBuffersPerHostBuffer;
+    /* Calculate number of WAVE buffers needed. Round up to cover minTotalFrames. */
+    numHostBuffers = (minTotalFrames + framesPerHostBuffer - 1) / framesPerHostBuffer;
+    /* Make sure we have anough WAVE buffers. */
+    if( numHostBuffers < PA_MIN_NUM_HOST_BUFFERS)
+    {
+        numHostBuffers = PA_MIN_NUM_HOST_BUFFERS;
+    }
+    else
+    {
+        /* If we have too many WAVE buffers, try to put more user buffers in a wave buffer. */
+        while(numHostBuffers > PA_MAX_NUM_HOST_BUFFERS)
+        {
+            userBuffersPerHostBuffer += 1;
+            framesPerHostBuffer = past->past_FramesPerUserBuffer * userBuffersPerHostBuffer;
+            numHostBuffers = (minTotalFrames + framesPerHostBuffer - 1) / framesPerHostBuffer;
+        }
+    }
+
+    pahsc->pahsc_UserBuffersPerHostBuffer = userBuffersPerHostBuffer;
+    pahsc->pahsc_FramesPerHostBuffer = framesPerHostBuffer;
+    pahsc->pahsc_NumHostBuffers = numHostBuffers;
+    DBUG(("PaHost_CalcNumHostBuffers: pahsc_UserBuffersPerHostBuffer = %d\n", pahsc->pahsc_UserBuffersPerHostBuffer ));
+    DBUG(("PaHost_CalcNumHostBuffers: pahsc_NumHostBuffers = %d\n", pahsc->pahsc_NumHostBuffers ));
+    DBUG(("PaHost_CalcNumHostBuffers: pahsc_FramesPerHostBuffer = %d\n", pahsc->pahsc_FramesPerHostBuffer ));
+    DBUG(("PaHost_CalcNumHostBuffers: past_NumUserBuffers = %d\n", past->past_NumUserBuffers ));
+}
+
+/*******************************************************************/
+PaError PaHost_OpenStream( internalPortAudioStream   *past )
+{
+    OSErr             err;
+    PaError             result = paHostError;
+    PaHostSoundControl *pahsc;
+    int                 i;
+    /* Allocate and initialize host data. */
+    pahsc = (PaHostSoundControl *) PaHost_AllocateFastMemory(sizeof(PaHostSoundControl));
+    if( pahsc == NULL )
+    {
+        return paInsufficientMemory;
+    }
+    past->past_DeviceData = (void *) pahsc;
+
+    /* If recording, and virtual memory is turned on, then use bigger buffers to prevent glitches. */
+    if( (past->past_NumInputChannels > 0)  && Mac_IsVirtualMemoryOn() )
+    {
+        pahsc->pahsc_MinFramesPerHostBuffer = MAC_VIRTUAL_FRAMES_PER_BUFFER;
+    }
+    else
+    {
+        pahsc->pahsc_MinFramesPerHostBuffer = MAC_PHYSICAL_FRAMES_PER_BUFFER;
+    }
+
+    PaHost_CalcNumHostBuffers( past );
+
+    /* ------------------ OUTPUT */
+    if( past->past_NumOutputChannels > 0 )
+    {
+        /* Create sound channel to which we can send commands. */
+        pahsc->pahsc_Channel = 0L;
+        err = SndNewChannel(&pahsc->pahsc_Channel, sampledSynth, 0, nil); /* FIXME - use kUseOptionalOutputDevice if not default. */
+        if(err != 0)
+        {
+            ERR_RPT(("Error in PaHost_OpenStream: SndNewChannel returned 0x%x\n", err ));
+            goto error;
+        }
+
+        /* Install our callback function pointer straight into the sound channel structure */
+        /* Use new CARBON name for callback procedure. */
+#if TARGET_API_MAC_CARBON
+        pahsc->pahsc_OutputCompletionProc = NewSndCallBackUPP(PaMac_OutputCompletionProc);
+#else
+        pahsc->pahsc_OutputCompletionProc = NewSndCallBackProc(PaMac_OutputCompletionProc);
+#endif
+
+        pahsc->pahsc_Channel->callBack = pahsc->pahsc_OutputCompletionProc;
+
+        pahsc->pahsc_BytesPerOutputHostBuffer = pahsc->pahsc_FramesPerHostBuffer * past->past_NumOutputChannels * sizeof(int16);
+        for (i = 0; i<pahsc->pahsc_NumHostBuffers; i++)
+        {
+            char *buf = (char *)PaHost_AllocateFastMemory(pahsc->pahsc_BytesPerOutputHostBuffer);
+            if (buf == NULL)
+            {
+                ERR_RPT(("Error in PaHost_OpenStream: could not allocate output buffer. Size = \n", pahsc->pahsc_BytesPerOutputHostBuffer ));
+                goto memerror;
+            }
+
+            PaMac_InitSoundHeader( past, &pahsc->pahsc_SoundHeaders[i] );
+            pahsc->pahsc_SoundHeaders[i].samplePtr = buf;
+            pahsc->pahsc_SoundHeaders[i].numFrames = (unsigned long) pahsc->pahsc_FramesPerHostBuffer;
+
+        }
+    }
+#ifdef SUPPORT_AUDIO_CAPTURE
+    /* ------------------ INPUT */
+    /* Use double buffer scheme that matches output. */
+    if( past->past_NumInputChannels > 0 )
+    {
+        int16   tempS;
+        long    tempL;
+        Fixed   tempF;
+        long    mRefNum;
+        unsigned char noname = 0; /* FIXME - use real device names. */
+#if TARGET_API_MAC_CARBON
+        pahsc->pahsc_InputCompletionProc = NewSICompletionUPP((SICompletionProcPtr)PaMac_InputCompletionProc);
+#else
+        pahsc->pahsc_InputCompletionProc = NewSICompletionProc((ProcPtr)PaMac_InputCompletionProc);
+#endif
+        pahsc->pahsc_BytesPerInputHostBuffer = pahsc->pahsc_FramesPerHostBuffer * past->past_NumInputChannels * sizeof(int16);
+        for (i = 0; i<pahsc->pahsc_NumHostBuffers; i++)
+        {
+            char *buf = (char *) PaHost_AllocateFastMemory(pahsc->pahsc_BytesPerInputHostBuffer);
+            if ( buf == NULL )
+            {
+                ERR_RPT(("PaHost_OpenStream: could not allocate input  buffer. Size = \n", pahsc->pahsc_BytesPerInputHostBuffer ));
+                goto memerror;
+            }
+            pahsc->pahsc_InputMultiBuffer.buffers[i] = buf;
+        }
+        pahsc->pahsc_InputMultiBuffer.numBuffers = pahsc->pahsc_NumHostBuffers;
+
+        err = SPBOpenDevice( (const unsigned char *) &noname, siWritePermission, &mRefNum); /* FIXME - use name so we get selected device */
+        // FIXME err = SPBOpenDevice( (const unsigned char *) sDevices[past->past_InputDeviceID].pad_Info.name, siWritePermission, &mRefNum);
+        if (err) goto error;
+        pahsc->pahsc_InputRefNum = mRefNum;
+        DBUG(("PaHost_OpenStream: mRefNum = %d\n", mRefNum ));
+
+        /* Set input device characteristics. */
+        tempS = 1;
+        err = SPBSetDeviceInfo(mRefNum, siContinuous, (Ptr) &tempS);
+        if (err)
+        {
+            ERR_RPT(("Error in PaHost_OpenStream: SPBSetDeviceInfo siContinuous returned %d\n", err ));
+            goto error;
+        }
+
+        tempL = 0x03;
+        err = SPBSetDeviceInfo(mRefNum, siActiveChannels, (Ptr) &tempL);
+        if (err)
+        {
+            DBUG(("PaHost_OpenStream: setting siActiveChannels returned 0x%x. Error ignored.\n", err ));
+        }
+
+        /* PLB20010908 - Use requested number of input channels. Thanks Dominic Mazzoni. */
+        tempS = past->past_NumInputChannels;
+        err = SPBSetDeviceInfo(mRefNum, siNumberChannels, (Ptr) &tempS);
+        if (err)
+        {
+            ERR_RPT(("Error in PaHost_OpenStream: SPBSetDeviceInfo siNumberChannels returned %d\n", err ));
+            goto error;
+        }
+
+        tempF = ((unsigned long)past->past_SampleRate) << 16;
+        err = SPBSetDeviceInfo(mRefNum, siSampleRate, (Ptr) &tempF);
+        if (err)
+        {
+            ERR_RPT(("Error in PaHost_OpenStream: SPBSetDeviceInfo siSampleRate returned %d\n", err ));
+            goto error;
+        }
+
+        /* Setup record-parameter block */
+        pahsc->pahsc_InputParams.inRefNum          = mRefNum;
+        pahsc->pahsc_InputParams.milliseconds      = 0;   // not used
+        pahsc->pahsc_InputParams.completionRoutine = pahsc->pahsc_InputCompletionProc;
+        pahsc->pahsc_InputParams.interruptRoutine  = 0;
+        pahsc->pahsc_InputParams.userLong          = (long) past;
+        pahsc->pahsc_InputParams.unused1           = 0;
+    }
+#endif /* SUPPORT_AUDIO_CAPTURE */
+    DBUG(("PaHost_OpenStream: complete.\n"));
+    return paNoError;
+
+error:
+    PaHost_CloseStream( past );
+    ERR_RPT(("PaHost_OpenStream: sPaHostError =  0x%x.\n", err ));
+    sPaHostError = err;
+    return paHostError;
+
+memerror:
+    PaHost_CloseStream( past );
+    return paInsufficientMemory;
+}
+
+/***********************************************************************
+** Called by Pa_CloseStream().
+** May be called during error recovery or cleanup code
+** so protect against NULL pointers.
+*/
+PaError PaHost_CloseStream( internalPortAudioStream   *past )
+{
+    PaError result = paNoError;
+    OSErr   err = 0;
+    int     i;
+    PaHostSoundControl *pahsc;
+
+    DBUG(("PaHost_CloseStream( 0x%x )\n", past ));
+
+    if( past == NULL ) return paBadStreamPtr;
+
+    pahsc = (PaHostSoundControl *) past->past_DeviceData;
+    if( pahsc == NULL ) return paNoError;
+
+    if( past->past_NumOutputChannels > 0 )
+    {
+        /* TRUE means flush now instead of waiting for quietCmd to be processed. */
+        if( pahsc->pahsc_Channel != NULL ) SndDisposeChannel(pahsc->pahsc_Channel, TRUE);
+        {
+            for (i = 0; i<pahsc->pahsc_NumHostBuffers; i++)
+            {
+                Ptr p = (Ptr) pahsc->pahsc_SoundHeaders[i].samplePtr;
+                if( p != NULL ) PaHost_FreeFastMemory( p, pahsc->pahsc_BytesPerOutputHostBuffer );
+            }
+        }
+    }
+
+    if( past->past_NumInputChannels > 0 )
+    {
+        if( pahsc->pahsc_InputRefNum )
+        {
+            err = SPBCloseDevice(pahsc->pahsc_InputRefNum);
+            pahsc->pahsc_InputRefNum = 0;
+            if( err )
+            {
+                sPaHostError = err;
+                result = paHostError;
+            }
+        }
+        {
+            for (i = 0; i<pahsc->pahsc_InputMultiBuffer.numBuffers; i++)
+            {
+                Ptr p = (Ptr) pahsc->pahsc_InputMultiBuffer.buffers[i];
+                if( p != NULL ) PaHost_FreeFastMemory( p, pahsc->pahsc_BytesPerInputHostBuffer );
+            }
+        }
+    }
+
+    past->past_DeviceData = NULL;
+    PaHost_FreeFastMemory( pahsc, sizeof(PaHostSoundControl) );
+
+    DBUG(("PaHost_CloseStream: complete.\n", past ));
+    return result;
+}
+/*************************************************************************/
+int Pa_GetMinNumBuffers( int framesPerUserBuffer, double sampleRate )
+{
+    return PaMac_GetMinNumBuffers( MAC_VIRTUAL_FRAMES_PER_BUFFER, framesPerUserBuffer, sampleRate );
+}
+/*************************************************************************/
+static int PaMac_GetMinNumBuffers( int minFramesPerHostBuffer, int framesPerUserBuffer, double sampleRate )
+{
+    int minUserPerHost = ( minFramesPerHostBuffer + framesPerUserBuffer - 1) / framesPerUserBuffer;
+    int numBufs = PA_MIN_NUM_HOST_BUFFERS * minUserPerHost;
+    if( numBufs < PA_MIN_NUM_HOST_BUFFERS ) numBufs = PA_MIN_NUM_HOST_BUFFERS;
+    (void) sampleRate;
+    return numBufs;
+}
+
+/*************************************************************************/
+void Pa_Sleep( int32 msec )
+{
+    EventRecord   event;
+    int32 sleepTime, endTime;
+    /* Convert to ticks. Round up so we sleep a MINIMUM of msec time. */
+    sleepTime = ((msec * 60) + 999) / 1000;
+    if( sleepTime < 1 ) sleepTime = 1;
+    endTime = TickCount() + sleepTime;
+    do
+    {
+        DBUGX(("Sleep for %d ticks.\n", sleepTime ));
+        /* Use WaitNextEvent() to sleep without getting events. */
+        /* PLB20010907 - Pass unused event to WaitNextEvent instead of NULL to prevent
+         * Mac OSX crash. Thanks Dominic Mazzoni. */
+        WaitNextEvent( 0, &event, sleepTime, NULL );
+        sleepTime = endTime - TickCount();
+    }
+    while( sleepTime > 0 );
+}
+/*************************************************************************/
+int32 Pa_GetHostError( void )
+{
+    int32 err = sPaHostError;
+    sPaHostError = 0;
+    return err;
+}
+
+/*************************************************************************
+ * Allocate memory that can be accessed in real-time.
+ * This may need to be held in physical memory so that it is not
+ * paged to virtual memory.
+ * This call MUST be balanced with a call to PaHost_FreeFastMemory().
+ */
+void *PaHost_AllocateFastMemory( long numBytes )
+{
+    void *addr = NewPtrClear( numBytes );
+    if( (addr == NULL) || (MemError () != 0) ) return NULL;
+
+#if (TARGET_API_MAC_CARBON == 0)
+    if( HoldMemory( addr, numBytes ) != noErr )
+    {
+        DisposePtr( (Ptr) addr );
+        return NULL;
+    }
+#endif
+    return addr;
+}
+
+/*************************************************************************
+ * Free memory that could be accessed in real-time.
+ * This call MUST be balanced with a call to PaHost_AllocateFastMemory().
+ */
+void PaHost_FreeFastMemory( void *addr, long numBytes )
+{
+    if( addr == NULL ) return;
+#if TARGET_API_MAC_CARBON
+    (void) numBytes;
+#else
+    UnholdMemory( addr, numBytes );
+#endif
+    DisposePtr( (Ptr) addr );
+}
+
+/*************************************************************************/
+PaTimestamp Pa_StreamTime( PortAudioStream *stream )
+{
+    PaHostSoundControl *pahsc;
+    internalPortAudioStream   *past = (internalPortAudioStream *) stream;
+    if( past == NULL ) return paBadStreamPtr;
+    pahsc = (PaHostSoundControl *) past->past_DeviceData;
+    return pahsc->pahsc_NumFramesDone;
+}
+
+/**************************************************************************
+** Callback for Input, SPBRecord()
+*/
+int gRecordCounter = 0;
+int gPlayCounter = 0;
+pascal void PaMac_InputCompletionProc(SPBPtr recParams)
+{
+    PaError                    result = paNoError;
+    int                        finished = 1;
+    internalPortAudioStream   *past;
+    PaHostSoundControl        *pahsc;
+
+    gRecordCounter += 1; /* debug hack to see if engine running */
+
+    /* Get our PA data from Mac structure. */
+    past = (internalPortAudioStream *) recParams->userLong;
+    if( past == NULL ) return;
+
+    if( past->past_Magic != PA_MAGIC )
+    {
+        AddTraceMessage("PaMac_InputCompletionProc: bad MAGIC, past", (long) past );
+        AddTraceMessage("PaMac_InputCompletionProc: bad MAGIC, magic", (long) past->past_Magic );
+        goto error;
+    }
+    pahsc = (PaHostSoundControl *) past->past_DeviceData;
+    past->past_NumCallbacks += 1;
+
+    /* Have we been asked to stop recording? */
+    if( (recParams->error == abortErr) || pahsc->pahsc_StopRecording ) goto error;
+
+    /* If there are no output channels, then we need to call the user callback function from here.
+     * Otherwise we will call the user code during the output completion routine.
+     */
+    if(past->past_NumOutputChannels == 0)
+    {
+        pahsc->pahsc_NumFramesDone += pahsc->pahsc_FramesPerHostBuffer; // Advance for recording
+        result = PaMac_CallUserLoop( past, NULL );
+    }
+
+    /* Did user code ask us to stop? If not, issue another recording request. */
+    if( (result == paNoError) && (pahsc->pahsc_StopRecording == 0) )
+    {
+        result = PaMac_RecordNext( past );
+        if( result != paNoError ) pahsc->pahsc_IsRecording = 0;
+    }
+    else goto error;
+
+    return;
+
+error:
+    pahsc->pahsc_IsRecording = 0;
+    pahsc->pahsc_StopRecording = 0;
+    return;
+}
+
+/***********************************************************************
+** Called by either input or output completion proc.
+** Grabs input data if any present, and calls PA conversion code,
+** that in turn calls user code.
+*/
+static PaError PaMac_CallUserLoop( internalPortAudioStream   *past, int16 *outPtr )
+{
+    PaError             result = paNoError;
+    PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData;
+    int16              *inPtr = NULL;
+    int                 i;
+
+    /* Advance read index for sound input FIFO here, independantly of record/write process. */
+    if(past->past_NumInputChannels > 0)
+    {
+        if( MultiBuffer_IsReadable( &pahsc->pahsc_InputMultiBuffer ) )
+        {
+            inPtr = (int16 *)  MultiBuffer_GetNextReadBuffer( &pahsc->pahsc_InputMultiBuffer );
+            MultiBuffer_AdvanceReadIndex( &pahsc->pahsc_InputMultiBuffer );
+        }
+    }
+
+    /* Call user code enough times to fill buffer. */
+    if( (inPtr != NULL) || (outPtr != NULL) )
+    {
+        StartLoadCalculation( past ); /* CPU usage */
+
+        for( i=0; i<pahsc->pahsc_UserBuffersPerHostBuffer; i++ )
+        {
+            result = (PaError) Pa_CallConvertInt16( past, inPtr, outPtr );
+            if( result != 0)
+            {
+                /* Recording might be in another process, so tell it to stop with a flag. */
+                pahsc->pahsc_StopRecording = pahsc->pahsc_IsRecording;
+                break;
+            }
+            /* Advance sample pointers. */
+            if(inPtr != NULL) inPtr += past->past_FramesPerUserBuffer * past->past_NumInputChannels;
+            if(outPtr != NULL) outPtr += past->past_FramesPerUserBuffer * past->past_NumOutputChannels;
+        }
+
+        PaMac_EndLoadCalculation( past );
+    }
+    return result;
+}
+
+/***********************************************************************
+** Setup next recording buffer in FIFO and issue recording request to Snd Input Manager.
+*/
+static PaError PaMac_RecordNext( internalPortAudioStream   *past )
+{
+    PaError  result = paNoError;
+    OSErr     err;
+    PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData;
+    /* Get pointer to next buffer to record into. */
+    pahsc->pahsc_InputParams.bufferPtr  = MultiBuffer_GetNextWriteBuffer( &pahsc->pahsc_InputMultiBuffer );
+
+    /* Advance write index if there is room. Otherwise keep writing same buffer. */
+    if( MultiBuffer_IsWriteable( &pahsc->pahsc_InputMultiBuffer ) )
+    {
+        MultiBuffer_AdvanceWriteIndex( &pahsc->pahsc_InputMultiBuffer );
+    }
+
+    AddTraceMessage("PaMac_RecordNext: bufferPtr", (long) pahsc->pahsc_InputParams.bufferPtr );
+    AddTraceMessage("PaMac_RecordNext: nextWrite", pahsc->pahsc_InputMultiBuffer.nextWrite );
+
+    /* Setup parameters and issue an asynchronous recording request. */
+    pahsc->pahsc_InputParams.bufferLength      = pahsc->pahsc_BytesPerInputHostBuffer;
+    pahsc->pahsc_InputParams.count             = pahsc->pahsc_BytesPerInputHostBuffer;
+    err = SPBRecord(&pahsc->pahsc_InputParams, true);
+    if( err )
+    {
+        AddTraceMessage("PaMac_RecordNext: SPBRecord error ", err );
+        sPaHostError = err;
+        result = paHostError;
+    }
+    else
+    {
+        pahsc->pahsc_IsRecording = 1;
+    }
+    return result;
+}
+
+/**************************************************************************
+** Callback for Output Playback()
+** Return negative error, 0 to continue, 1 to stop.
+*/
+long    PaMac_FillNextOutputBuffer( internalPortAudioStream   *past, int index )
+{
+    PaHostSoundControl  *pahsc;
+    long                 result = 0;
+    int                  finished = 1;
+    char                *outPtr;
+
+    gPlayCounter += 1; /* debug hack */
+
+    past->past_NumCallbacks += 1;
+    pahsc = (PaHostSoundControl *) past->past_DeviceData;
+    if( pahsc == NULL ) return -1;
+    /* Are we nested?! */
+    if( pahsc->pahsc_IfInsideCallback ) return 0;
+    pahsc->pahsc_IfInsideCallback = 1;
+    /* Get pointer to buffer to fill. */
+    outPtr = pahsc->pahsc_SoundHeaders[index].samplePtr;
+    /* Combine with any sound input, and call user callback. */
+    result = PaMac_CallUserLoop( past, (int16 *) outPtr );
+
+    pahsc->pahsc_IfInsideCallback = 0;
+    return result;
+}
+
+/*************************************************************************************
+** Called by SoundManager when ready for another buffer.
+*/
+static pascal void PaMac_OutputCompletionProc (SndChannelPtr theChannel, SndCommand * theCallBackCmd)
+{
+    internalPortAudioStream *past;
+    PaHostSoundControl      *pahsc;
+    (void) theChannel;
+    (void) theCallBackCmd;
+
+    /* Get our data from Mac structure. */
+    past = (internalPortAudioStream *) theCallBackCmd->param2;
+    if( past == NULL ) return;
+
+    pahsc = (PaHostSoundControl *) past->past_DeviceData;
+    pahsc->pahsc_NumOutsPlayed += 1;
+    pahsc->pahsc_NumFramesDone += pahsc->pahsc_FramesPerHostBuffer;
+
+    PaMac_BackgroundManager( past, theCallBackCmd->param1 );
+}
+
+/*******************************************************************/
+static PaError PaMac_BackgroundManager( internalPortAudioStream   *past, int index )
+{
+    PaError      result = paNoError;
+    PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData;
+    /* Has someone asked us to abort by calling Pa_AbortStream()? */
+    if( past->past_StopNow )
+    {
+        SndCommand  command;
+        /* Clear the queue of any pending commands. */
+        command.cmd = flushCmd;
+        command.param1 = command.param2 = 0;
+        SndDoImmediate( pahsc->pahsc_Channel, &command );
+        /* Then stop currently playing buffer, if any. */
+        command.cmd = quietCmd;
+        SndDoImmediate( pahsc->pahsc_Channel, &command );
+        past->past_IsActive = 0;
+    }
+    /* Has someone asked us to stop by calling Pa_StopStream()
+     * OR has a user callback returned '1' to indicate finished.
+     */
+    else if( past->past_StopSoon )
+    {
+        if( (pahsc->pahsc_NumOutsQueued - pahsc->pahsc_NumOutsPlayed) <= 0 )
+        {
+            past->past_IsActive = 0; /* We're finally done. */
+        }
+    }
+    else
+    {
+        PaMac_PlayNext( past, index );
+    }
+    return result;
+}
+
+/*************************************************************************************
+** Fill next buffer with sound and queue it for playback.
+*/
+static void PaMac_PlayNext ( internalPortAudioStream *past, int index )
+{
+    OSErr                  error;
+    long                     result;
+    SndCommand               playCmd;
+    SndCommand           callbackCmd;
+    PaHostSoundControl      *pahsc = (PaHostSoundControl *) past->past_DeviceData;
+
+    /* If this was the last buffer, or abort requested, then just be done. */
+    if ( past->past_StopSoon ) goto done;
+    /* Load buffer with sound. */
+    result = PaMac_FillNextOutputBuffer ( past, index );
+    if( result > 0 ) past->past_StopSoon = 1; /* Stop generating audio but wait until buffers play. */
+    else if( result < 0 ) goto done;
+    /* Play the next buffer. */
+    playCmd.cmd = bufferCmd;
+    playCmd.param1 = 0;
+    playCmd.param2 = (long) &pahsc->pahsc_SoundHeaders[ index ];
+    error = SndDoCommand (pahsc->pahsc_Channel, &playCmd, true );
+    if( error != noErr ) goto gotError;
+    /* Ask for a callback when it is done. */
+    callbackCmd.cmd = callBackCmd;
+    callbackCmd.param1 = index;
+    callbackCmd.param2 = (long)past;
+    error = SndDoCommand (pahsc->pahsc_Channel, &callbackCmd, true );
+    if( error != noErr ) goto gotError;
+    pahsc->pahsc_NumOutsQueued += 1;
+
+    return;
+
+gotError:
+    sPaHostError = error;
+done:
+    return;
+}
diff --git a/pa_mac_core/pa_mac_core.c b/pa_mac_core/pa_mac_core.c
new file mode 100644 (file)
index 0000000..3dc4d74
--- /dev/null
@@ -0,0 +1,1159 @@
+/*
+ * $Id$
+ * pa_mac_core.c
+ * Implementation of PortAudio for Mac OS X Core Audio
+ *
+ * PortAudio Portable Real-Time Audio Library
+ * Latest Version at: http://www.portaudio.com
+ *
+ * Authors: Ross Bencina and Phil Burk
+ * Copyright (c) 1999-2000 Ross Bencina and 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.
+ *
+ * CHANGE HISTORY:
+ 3.29.2001 - Phil Burk - First pass... converted from Window MME code with help from Darren.
+ 3.30.2001 - Darren Gibbs - Added more support for dynamically querying device info.
+ 12.7.2001 - Gord Peters - Tweaks to compile on PA V17 and OS X 10.1
+ */
+#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 ********/
+#define PA_USE_TIMER_CALLBACK    (0)  /* Select between two options for background task. 0=thread, 1=timer */
+#define PA_USE_HIGH_LATENCY      (0)  /* For debugging glitches. */
+
+/* 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)
+#define PA_TRACE_START_STOP      (1)
+
+#define PA_MIN_MSEC_PER_HOST_BUFFER  (10)
+#define PA_MAX_MSEC_PER_HOST_BUFFER  (100) /* Do not exceed unless user buffer exceeds */
+#define PA_MIN_NUM_HOST_BUFFERS      (3)
+#define PA_MAX_NUM_HOST_BUFFERS      (16)  /* OK to exceed if necessary */
+#define PA_MIN_LATENCY_MSEC          (200)
+
+#define MIN_TIMEOUT_MSEC                 (1000)
+
+#define PRINT(x) { printf x; fflush(stdout); }
+#define ERR_RPT(x) PRINT(x)
+#define DBUG(x)  /* PRINT(x) */
+#define DBUGX(x) /* PRINT(x) */
+
+/************************************************* Definitions ********/
+
+/**************************************************************
+ * Structure for internal host specific stream data.
+ * This is allocated on a per stream basis.
+ */
+typedef struct PaHostSoundControl
+{
+    /* Input -------------- */
+    int                pahsc_BytesPerHostInputBuffer;
+    int                pahsc_BytesPerUserInputBuffer;    /* native buffer size in bytes */
+    /* Output -------------- */
+    AudioDeviceID      pahsc_OutputAudioDeviceID;
+    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;
+    /* Init Time -------------- */
+    int                pahsc_NumHostBuffers;
+    int                pahsc_FramesPerHostBuffer;
+    int                pahsc_UserBuffersPerHostBuffer;
+
+    //   CRITICAL_SECTION   pahsc_StreamLock;                  /* Mutext to prevent threads from colliding. */
+    int                pahsc_StreamLockInited;
+
+}
+PaHostSoundControl;
+
+/************************************************* Shared Data ********/
+/* FIXME - put Mutex around this shared data. */
+static int sNumDevices = 0;
+static PaDeviceInfo **sDevicePtrs = NULL;
+static int sDefaultInputDeviceID = paNoDevice;
+static int sDefaultOutputDeviceID = paNoDevice;
+static int sPaHostError = 0;
+static AudioDeviceID *sDeviceIDList;   // Array of AudioDeviceIDs
+
+static const char sMapperSuffixInput[] = " - Input";
+static const char sMapperSuffixOutput[] = " - Output";
+
+/************************************************* Macros ********/
+
+/* Convert external PA ID to an internal ID that includes WAVE_MAPPER */
+#define PaDeviceIdToWinId(id) (((id) < sNumInputDevices) ? (id - 1) : (id - sNumInputDevices - 1))
+
+/************************************************* Prototypes **********/
+
+static PaError Pa_QueryDevices( void );
+PaError PaHost_GetTotalBufferFrames( internalPortAudioStream   *past );
+/********************************* BEGIN CPU UTILIZATION MEASUREMENT ****/
+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 );
+}
+
+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)
+#define LOWPASS_COEFFICIENT_1   (0.99999 - LOWPASS_COEFFICIENT_0)
+
+    PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData;
+    if( pahsc == NULL ) return;
+
+    if( QueryPerformanceCounter( &CurrentCount ) )
+    {
+        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;
+    }
+#endif
+}
+
+static PaDeviceID Pa_QueryDefaultDevice( int deviceEnum )
+{
+    OSStatus err = noErr;
+    UInt32  count;
+    int          i;
+    AudioDeviceID tempDevice = kAudioDeviceUnknown;
+    PaDeviceID  defaultDeviceID;
+
+    // get the default output device for the HAL
+    // it is required to pass the size of the data to be returned
+    count = sizeof(AudioDeviceID);
+    err = AudioHardwareGetProperty( deviceEnum,  &count, (void *) &tempDevice);
+    if (err != noErr) goto Bail;
+    // find index of default device
+    defaultDeviceID = paNoDevice;
+    for( i=0; i<sNumDevices; i++ )
+    {
+        if( sDeviceIDList[i] == tempDevice )
+        {
+            defaultDeviceID = i;
+            break;
+        }
+    }
+Bail:
+    return defaultDeviceID;
+}
+
+/****************************************** END CPU UTILIZATION *******/
+
+static PaError Pa_QueryDevices( void )
+{
+    OSStatus err = noErr;
+    UInt32   outSize;
+    Boolean  outWritable;
+    int         numBytes;
+
+    // find out how many audio devices there are, if any
+    err = AudioHardwareGetPropertyInfo(kAudioHardwarePropertyDevices, &outSize, &outWritable);
+    if (err != noErr)
+        ERR_RPT(("Couldn't get info about list of audio devices\n"));
+
+    // calculate the number of device available
+    sNumDevices = outSize / sizeof(AudioDeviceID);
+
+    // Bail if there aren't any devices
+    if (sNumDevices < 1)
+        ERR_RPT(("No Devices Available\n"));
+
+    // make space for the devices we are about to get
+    sDeviceIDList = malloc(outSize);
+
+    // get an array of AudioDeviceIDs
+    err = AudioHardwareGetProperty(kAudioHardwarePropertyDevices, &outSize, (void *)sDeviceIDList);
+    if (err != noErr)
+        ERR_RPT(("Couldn't get list of audio device IDs\n"));
+
+    /* Allocate structures to hold device info pointers. */
+    numBytes = sNumDevices * sizeof(PaDeviceInfo *);
+    sDevicePtrs = PaHost_AllocateFastMemory( numBytes );
+    if( sDevicePtrs == NULL ) return paInsufficientMemory;
+
+    sDefaultInputDeviceID = Pa_QueryDefaultDevice( kAudioHardwarePropertyDefaultInputDevice );
+    sDefaultOutputDeviceID = Pa_QueryDefaultDevice( kAudioHardwarePropertyDefaultOutputDevice );
+
+    return paNoError;
+}
+
+/************************************************************************************/
+long Pa_GetHostError()
+{
+    return sPaHostError;
+}
+
+/*************************************************************************/
+int Pa_CountDevices()
+{
+    if( sNumDevices <= 0 ) Pa_Initialize();
+    return sNumDevices;
+}
+
+/*************************************************************************/
+
+/* Allocate a string containing the device name. */
+char *deviceNameFromID(AudioDeviceID deviceID )
+{
+    OSStatus err = noErr;
+    UInt32  outSize;
+    Boolean  outWritable;
+    char     *deviceName = nil;
+    // query size of name
+    err =  AudioDeviceGetPropertyInfo(deviceID, 0, false, kAudioDevicePropertyDeviceName, &outSize, &outWritable);
+    if (err == noErr)
+    {
+        deviceName = malloc( outSize + 1);
+        if( deviceName )
+        {
+            err = AudioDeviceGetProperty(deviceID, 0, false, kAudioDevicePropertyDeviceName, &outSize, deviceName);
+            // FIXME if (err == noErr)
+        }
+    }
+
+    return deviceName;
+}
+
+/*************************************************************************/
+
+void deviceDataFormatFromID(AudioDeviceID deviceID , AudioStreamBasicDescription *desc )
+{
+    OSStatus err = noErr;
+    UInt32  outSize;
+
+    outSize = sizeof(*desc);
+    err = AudioDeviceGetProperty(deviceID, 0, false, kAudioDevicePropertyStreamFormat, &outSize, desc);
+}
+
+/*************************************************************************/
+// An AudioStreamBasicDescription is passed in to query whether or not
+// the format is supported. A kAudioDeviceUnsupportedFormatError will
+// be returned if the format is not supported and kAudioHardwareNoError
+// will be returned if it is supported. AudioStreamBasicDescription
+// fields set to 0 will be ignored in the query, but otherwise values
+// must match exactly.
+
+Boolean deviceDoesSupportFormat(AudioDeviceID deviceID , AudioStreamBasicDescription *desc )
+{
+    OSStatus err = noErr;
+    UInt32  outSize;
+
+    outSize = sizeof(*desc);
+    err = AudioDeviceGetProperty(deviceID, 0, false, kAudioDevicePropertyStreamFormatSupported, &outSize, desc);
+
+    if (err == kAudioHardwareNoError)
+        return true;
+    else
+        return false;
+}
+
+/*************************************************************************/
+// return an error string
+char* coreAudioErrorString (int errCode )
+{
+    char *str;
+
+    switch (errCode)
+    {
+    case kAudioHardwareUnspecifiedError:
+        str = "kAudioHardwareUnspecifiedError";
+        break;
+    case kAudioHardwareNotRunningError:
+        str = "kAudioHardwareNotRunningError";
+        break;
+    case kAudioHardwareUnknownPropertyError:
+        str = "kAudioHardwareUnknownPropertyError";
+        break;
+    case kAudioDeviceUnsupportedFormatError:
+        str = "kAudioDeviceUnsupportedFormatError";
+        break;
+    case kAudioHardwareBadPropertySizeError:
+        str = "kAudioHardwareBadPropertySizeError";
+        break;
+    case kAudioHardwareIllegalOperationError:
+        str = "kAudioHardwareIllegalOperationError";
+        break;
+    default:
+        str = "UNKNWON ERROR!";
+        break;
+    }
+
+    return str;
+}
+
+/*************************************************************************
+** If a PaDeviceInfo structure has not already been created,
+** then allocate one and fill it in for the selected device.
+**
+** We create one extra input and one extra output device for the WAVE_MAPPER.
+** [Does anyone know how to query the default device and get its name?]
+*/
+const PaDeviceInfo* Pa_GetDeviceInfo( PaDeviceID id )
+{
+#define NUM_STANDARDSAMPLINGRATES   3   /* 11.025, 22.05, 44.1 */
+#define NUM_CUSTOMSAMPLINGRATES     4   /* must be the same number of elements as in the array below */
+#define MAX_NUMSAMPLINGRATES        (NUM_STANDARDSAMPLINGRATES+NUM_CUSTOMSAMPLINGRATES)
+
+    PaDeviceInfo *deviceInfo;
+    AudioDeviceID    devID;
+    double *sampleRates; /* non-const ptr */
+    double possibleSampleRates[] = {8000.0, 11025.0, 22050.0, 44100.0, 48000.0, 88200.0, 96000.0};
+    int index;
+    AudioStreamBasicDescription formatDesc;
+    Boolean result;
+
+    if( id < 0 || id >= sNumDevices )
+        return NULL;
+
+    if( sDevicePtrs[ id ] != NULL )
+    {
+        return sDevicePtrs[ id ];
+    }
+
+    deviceInfo = PaHost_AllocateFastMemory( sizeof(PaDeviceInfo) );
+    if( deviceInfo == NULL ) return NULL;
+
+    deviceInfo->structVersion = 1;
+    deviceInfo->maxInputChannels = 0;
+    deviceInfo->maxOutputChannels = 0;
+    deviceInfo->numSampleRates = -1;
+
+
+    // fill in format descriptor
+    if( id < sNumDevices )
+    {
+        devID = sDeviceIDList[ id ];
+
+        // Get the device name
+        deviceInfo->name = deviceNameFromID( devID );
+
+        // Figure out supported sample rates
+        // Make room in case device supports all rates.
+        sampleRates = (double*)PaHost_AllocateFastMemory( MAX_NUMSAMPLINGRATES * sizeof(double) );
+        deviceInfo->sampleRates = sampleRates;
+        deviceInfo->numSampleRates = 0;
+
+        // Loop through the possible sampling rates and check each to see if the device supports it.
+        for (index = 0; index < MAX_NUMSAMPLINGRATES; index ++)
+        {
+            memset( &formatDesc, 0, sizeof(AudioStreamBasicDescription) );
+            formatDesc.mSampleRate = possibleSampleRates[index];
+            result = deviceDoesSupportFormat( devID, &formatDesc );
+
+            if (result == true)
+            {
+                deviceInfo->numSampleRates += 1;
+                *sampleRates = possibleSampleRates[index];
+                sampleRates++;
+            }
+        }
+
+        // Get data format info from the device.
+        deviceDataFormatFromID(devID, &formatDesc);
+
+        deviceInfo->maxInputChannels = 0; // FIXME - Input and Output are separate devices!
+        deviceInfo->maxOutputChannels = formatDesc.mChannelsPerFrame;
+
+        // FIXME - where to put current sample rate?:  formatDesc.mSampleRate
+
+        // Right now the Core Audio headers only define one formatID: LinearPCM
+        // Apparently LinearPCM must be Float32 for now.
+        // FIXME -
+        switch (formatDesc.mFormatID)
+        {
+        case kAudioFormatLinearPCM:
+            deviceInfo->nativeSampleFormats = paFloat32;
+
+            // FIXME - details about the format are in these flags.
+            // formatDesc.mFormatFlags
+
+            // here are the possibilities
+            // kLinearPCMFormatFlagIsFloat   // set for floating point, clear for integer
+            // kLinearPCMFormatFlagIsBigEndian  // set for big endian, clear for little
+            // kLinearPCMFormatFlagIsSignedInteger // set for signed integer, clear for unsigned integer,
+            //    only valid if kLinearPCMFormatFlagIsFloat is clear
+            // kLinearPCMFormatFlagIsPacked   // set if the sample bits are packed as closely together as possible,
+            //    clear if they are high or low aligned within the channel
+            // kLinearPCMFormatFlagIsAlignedHigh  // set if the sample bits are placed
+
+            break;
+        default:
+            deviceInfo->nativeSampleFormats = paFloat32;  // FIXME
+            break;
+        }
+
+    }
+
+    sDevicePtrs[ id ] = deviceInfo;
+    return deviceInfo;
+
+    /* FIXME
+    error:
+        free( sampleRates );
+        free( deviceInfo );
+    */
+
+    return NULL;
+}
+
+/*************************************************************************
+** Returns recommended device ID.
+** On the PC, the recommended device can be specified by the user by
+** setting an environment variable. For example, to use device #1.
+**
+**    set PA_RECOMMENDED_OUTPUT_DEVICE=1
+**
+** The user should first determine the available device ID by using
+** the supplied application "pa_devs".
+*/
+#define PA_ENV_BUF_SIZE  (32)
+#define PA_REC_IN_DEV_ENV_NAME  ("PA_RECOMMENDED_INPUT_DEVICE")
+#define PA_REC_OUT_DEV_ENV_NAME  ("PA_RECOMMENDED_OUTPUT_DEVICE")
+
+static PaDeviceID PaHost_GetEnvDefaultDeviceID( char *envName )
+{
+#if 0
+    UInt32   hresult;
+    char    envbuf[PA_ENV_BUF_SIZE];
+    PaDeviceID recommendedID = paNoDevice;
+
+    /* Let user determine default device by setting environment variable. */
+    hresult = GetEnvironmentVariable( envName, envbuf, PA_ENV_BUF_SIZE );
+    if( (hresult > 0) && (hresult < PA_ENV_BUF_SIZE) )
+    {
+        recommendedID = atoi( envbuf );
+    }
+
+    return recommendedID;
+#endif
+    return paNoDevice;
+}
+
+static PaError Pa_MaybeQueryDevices( void )
+{
+    if( sNumDevices == 0 )
+    {
+        return Pa_QueryDevices();
+    }
+    return 0;
+}
+
+/**********************************************************************
+** Check for environment variable, else query devices and use result.
+*/
+PaDeviceID Pa_GetDefaultInputDeviceID( void )
+{
+    PaError result;
+    result = PaHost_GetEnvDefaultDeviceID( PA_REC_IN_DEV_ENV_NAME );
+    if( result < 0 )
+    {
+        result = Pa_MaybeQueryDevices();
+        if( result < 0 ) return result;
+        result = sDefaultInputDeviceID;
+    }
+    return result;
+}
+
+PaDeviceID Pa_GetDefaultOutputDeviceID( void )
+{
+    PaError result;
+    result = PaHost_GetEnvDefaultDeviceID( PA_REC_OUT_DEV_ENV_NAME );
+    if( result < 0 )
+    {
+        result = Pa_MaybeQueryDevices();
+        if( result < 0 ) return result;
+        result = sDefaultOutputDeviceID;
+    }
+    return result;
+}
+
+/**********************************************************************
+** Initialize Host dependant part of API.
+*/
+
+PaError PaHost_Init( void )
+{
+#if PA_SIMULATE_UNDERFLOW
+    PRINT(("WARNING - Underflow Simulation Enabled - Expect a Big Glitch!!!\n"));
+#endif
+    return Pa_MaybeQueryDevices();
+}
+
+/**********************************************************************
+** Fill any available output buffers and use any available
+** input buffers by calling user callback.
+*/
+static PaError Pa_TimeSlice( internalPortAudioStream   *past, const AudioBufferList*  inInputData,
+                             AudioBufferList*  outOutputData )
+{
+    PaError           result = 0;
+    char             *inBufPtr;
+    char             *outBufPtr;
+    int               gotInput = 0;
+    int               gotOutput = 0;
+    int               i;
+    int               buffersProcessed = 0;
+    int               done = 0;
+    PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData;
+    if( pahsc == NULL ) return paInternalError;
+
+    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
+
+    Pa_StartUsageCalculation( past );
+
+    /* If we are using output, then we need an empty output buffer. */
+    gotOutput = 0;
+    if( past->past_NumOutputChannels > 0 )
+    {
+        outBufPtr =  outOutputData->mBuffers[0].mData,
+                     gotOutput = 1;
+    }
+
+    /* If we are using input, then we need a full input buffer. */
+    gotInput = 0;
+    inBufPtr = NULL;
+    if(  past->past_NumInputChannels > 0  )
+    {
+        inBufPtr = inInputData->mBuffers[0].mData,
+                   gotInput = 1;
+    }
+
+    buffersProcessed += 1;
+
+    /* Each host buffer contains multiple user buffers so do them all now. */
+    for( i=0; i<pahsc->pahsc_UserBuffersPerHostBuffer; i++ )
+    {
+        if( done )
+        {
+            if( gotOutput )
+            {
+                /* 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 );
+            }
+        }
+        else
+        {
+            /* Convert 16 bit native data to user data and call user routine. */
+            result = Pa_CallConvertFloat32( past, (float *) inBufPtr, (float *) outBufPtr );
+            if( result != 0) done = 1;
+        }
+        if( gotInput ) inBufPtr += pahsc->pahsc_BytesPerUserInputBuffer;
+        if( gotOutput) outBufPtr += pahsc->pahsc_BytesPerUserOutputBuffer;
+    }
+
+    Pa_EndUsageCalculation( past );
+
+#if PA_TRACE_RUN
+    AddTraceMessage("Pa_TimeSlice: buffersProcessed ", buffersProcessed );
+#endif
+
+    return (result != 0) ? result : done;
+}
+
+OSStatus appIOProc (AudioDeviceID  inDevice, const AudioTimeStamp*  inNow, const AudioBufferList*  inInputData, const AudioTimeStamp*  inInputTime, AudioBufferList*  outOutputData, const AudioTimeStamp* inOutputTime, void* contextPtr)
+{
+
+    PaError      result = 0;
+    internalPortAudioStream *past;
+    PaHostSoundControl *pahsc;
+    past = (internalPortAudioStream *) contextPtr;
+    pahsc = (PaHostSoundControl *) past->past_DeviceData;
+
+    // Just Curious: 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 )
+    {
+        past->past_IsActive = 0; /* Will cause thread to return. */
+    }
+    /* Has someone asked us to stop by calling Pa_StopStream()
+     * OR has a user callback returned '1' to indicate finished.
+     */
+    else if( past->past_StopSoon )
+    {
+        // FIXME - pretend all done
+        past->past_IsActive = 0; /* Will cause thread to return. */
+    }
+    else
+    {
+        /* Process full input buffer and fill up empty output buffers. */
+        if( (result = Pa_TimeSlice( past, inInputData, outOutputData )) != 0)
+        {
+            /* User callback has asked us to stop. */
+#if PA_TRACE_START_STOP
+            AddTraceMessage( "Pa_OutputThreadProc: TimeSlice() returned ", result );
+#endif
+            past->past_StopSoon = 1; /* Request that audio play out then stop. */
+            result = paNoError;
+        }
+    }
+
+    // FIXME PaHost_UpdateStreamTime( pahsc );
+
+    return result;
+}
+
+static int PaHost_CalcTimeOut( internalPortAudioStream *past )
+{
+    /* Calculate timeOut longer than longest time it could take to play all buffers. */
+    int timeOut = (UInt32) (1500.0 * PaHost_GetTotalBufferFrames( past ) / past->past_SampleRate);
+    if( timeOut < MIN_TIMEOUT_MSEC ) timeOut = MIN_TIMEOUT_MSEC;
+    return timeOut;
+}
+
+/*******************************************************************/
+PaError PaHost_OpenInputStream( internalPortAudioStream   *past )
+{
+    PaError          result = paNoError;
+    PaHostSoundControl *pahsc;
+    int              bytesPerInputFrame;
+    const PaDeviceInfo  *pad;
+
+    pahsc = (PaHostSoundControl *) past->past_DeviceData;
+
+    DBUG(("PaHost_OpenStream: deviceID = 0x%x\n", past->past_InputDeviceID));
+    pad = Pa_GetDeviceInfo( past->past_InputDeviceID );
+    if( pad == NULL ) return paInternalError;
+
+    bytesPerInputFrame = Pa_GetSampleSize(pad->nativeSampleFormats) * past->past_NumInputChannels;
+    pahsc->pahsc_BytesPerUserInputBuffer = past->past_FramesPerUserBuffer * bytesPerInputFrame;
+    pahsc->pahsc_BytesPerHostInputBuffer = pahsc->pahsc_UserBuffersPerHostBuffer * pahsc->pahsc_BytesPerUserInputBuffer;
+    // FIXME
+    return result;
+}
+
+/*******************************************************************/
+PaError PaHost_OpenOutputStream( internalPortAudioStream   *past )
+{
+    PaError          result = paNoError;
+    PaHostSoundControl *pahsc;
+    const PaDeviceInfo *pad;
+    UInt32           bytesPerHostBuffer;
+    UInt32   dataSize;
+    UInt32   hardwareLatency = -1;
+    Boolean   writeable = false;
+    OSStatus         err = noErr;
+    int              bytesPerOutputFrame;
+
+    pahsc = (PaHostSoundControl *) past->past_DeviceData;
+
+    DBUG(("PaHost_OpenStream: deviceID = 0x%x\n", past->past_OutputDeviceID));
+    pad = Pa_GetDeviceInfo( past->past_OutputDeviceID );
+    if( pad == NULL ) return paInternalError;
+
+    pahsc->pahsc_OutputAudioDeviceID = sDeviceIDList[past->past_OutputDeviceID];
+
+    bytesPerOutputFrame = Pa_GetSampleSize(pad->nativeSampleFormats) * past->past_NumOutputChannels;
+    pahsc->pahsc_BytesPerUserOutputBuffer = past->past_FramesPerUserBuffer * bytesPerOutputFrame;
+    pahsc->pahsc_BytesPerHostOutputBuffer = pahsc->pahsc_UserBuffersPerHostBuffer * pahsc->pahsc_BytesPerUserOutputBuffer;
+
+    // FIXME - force host buffer to user size
+
+    // change the bufferSize of the device
+    bytesPerHostBuffer = past->past_OutputBufferSize; // FIXME
+    dataSize = sizeof(UInt32);
+    err = AudioDeviceSetProperty( pahsc->pahsc_OutputAudioDeviceID, 0, 0, false,
+                                  kAudioDevicePropertyBufferSize, dataSize, &bytesPerHostBuffer);
+    if( err != noErr )
+    {
+        ERR_RPT(("Could not force buffer size!"));
+        result = paHostError;
+        goto error;
+    }
+
+    // FIXME -- this isn't working for some reason.  I can't figure out what the error code is.
+    dataSize = sizeof(UInt32);
+    err = AudioDeviceGetProperty(pahsc->pahsc_OutputAudioDeviceID, 0, false, kAudioDevicePropertyLatency, &dataSize, &writeable);
+    if( err != noErr )
+    {
+        PRINT(("A CoreAudio error occurred: %s.\n", coreAudioErrorString( err ) ));
+    }
+
+    err = AudioDeviceGetProperty(pahsc->pahsc_OutputAudioDeviceID, 0, false, kAudioDevicePropertyLatency, &dataSize, (void *)&hardwareLatency);
+    if ( err != noErr )
+    {
+        PRINT(("A CoreAudio error occurred: %s.\n", coreAudioErrorString( err ) ));
+    }
+    else
+    {
+        PRINT(("Output Stream Hardware Latency = %d frames.\n", hardwareLatency ));
+    }
+
+    return result;
+
+error:
+    return result;
+}
+
+/*******************************************************************/
+PaError PaHost_GetTotalBufferFrames( internalPortAudioStream   *past )
+{
+    PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData;
+    return pahsc->pahsc_NumHostBuffers * pahsc->pahsc_FramesPerHostBuffer;
+}
+
+
+/*******************************************************************
+* Determine number of WAVE Buffers
+* and how many User Buffers we can put into each WAVE buffer.
+*/
+void PaHost_CalcNumHostBuffers( internalPortAudioStream *past )
+{
+    PaHostSoundControl *pahsc = past->past_DeviceData;
+    unsigned int  minNumBuffers;
+    int           minFramesPerHostBuffer;
+    int           maxFramesPerHostBuffer;
+    int           minTotalFrames;
+    int           userBuffersPerHostBuffer;
+    int           framesPerHostBuffer;
+    int           numHostBuffers;
+
+    /* Calculate minimum and maximum sizes based on timing and sample rate. */
+    minFramesPerHostBuffer = (int) (PA_MIN_MSEC_PER_HOST_BUFFER * past->past_SampleRate / 1000.0);
+    minFramesPerHostBuffer = (minFramesPerHostBuffer + 7) & ~7;
+    DBUG(("PaHost_CalcNumHostBuffers: minFramesPerHostBuffer = %d\n", minFramesPerHostBuffer ));
+
+    maxFramesPerHostBuffer = (int) (PA_MAX_MSEC_PER_HOST_BUFFER * past->past_SampleRate / 1000.0);
+    maxFramesPerHostBuffer = (maxFramesPerHostBuffer + 7) & ~7;
+    DBUG(("PaHost_CalcNumHostBuffers: maxFramesPerHostBuffer = %d\n", maxFramesPerHostBuffer ));
+
+    /* Determine number of user buffers based on minimum latency. */
+    minNumBuffers = Pa_GetMinNumBuffers( past->past_FramesPerUserBuffer, past->past_SampleRate );
+    past->past_NumUserBuffers = ( minNumBuffers > past->past_NumUserBuffers ) ? minNumBuffers : past->past_NumUserBuffers;
+    DBUG(("PaHost_CalcNumHostBuffers: min past_NumUserBuffers = %d\n", past->past_NumUserBuffers ));
+
+    minTotalFrames = past->past_NumUserBuffers * past->past_FramesPerUserBuffer;
+
+    /* We cannot make the WAVE buffers too small because they may not get serviced quickly enough. */
+    if( (int) past->past_FramesPerUserBuffer < minFramesPerHostBuffer )
+    {
+        userBuffersPerHostBuffer =
+            (minFramesPerHostBuffer + past->past_FramesPerUserBuffer - 1) /
+            past->past_FramesPerUserBuffer;
+    }
+    else
+    {
+        userBuffersPerHostBuffer = 1;
+    }
+
+
+    framesPerHostBuffer = past->past_FramesPerUserBuffer * userBuffersPerHostBuffer;
+    /* Calculate number of WAVE buffers needed. Round up to cover minTotalFrames. */
+    numHostBuffers = (minTotalFrames + framesPerHostBuffer - 1) / framesPerHostBuffer;
+
+    /* Make sure we have anough WAVE buffers. */
+    if( numHostBuffers < PA_MIN_NUM_HOST_BUFFERS)
+    {
+        numHostBuffers = PA_MIN_NUM_HOST_BUFFERS;
+    }
+    else if( (numHostBuffers > PA_MAX_NUM_HOST_BUFFERS) &&
+             ((int) past->past_FramesPerUserBuffer < (maxFramesPerHostBuffer/2) ) )
+    {
+        /* If we have too many WAVE buffers, try to put more user buffers in a wave buffer. */
+        while(numHostBuffers > PA_MAX_NUM_HOST_BUFFERS)
+        {
+            userBuffersPerHostBuffer += 1;
+            framesPerHostBuffer = past->past_FramesPerUserBuffer * userBuffersPerHostBuffer;
+            numHostBuffers = (minTotalFrames + framesPerHostBuffer - 1) / framesPerHostBuffer;
+            /* If we have gone too far, back up one. */
+            if( (framesPerHostBuffer > maxFramesPerHostBuffer) ||
+                    (numHostBuffers < PA_MAX_NUM_HOST_BUFFERS) )
+            {
+                userBuffersPerHostBuffer -= 1;
+                framesPerHostBuffer = past->past_FramesPerUserBuffer * userBuffersPerHostBuffer;
+                numHostBuffers = (minTotalFrames + framesPerHostBuffer - 1) / framesPerHostBuffer;
+                break;
+            }
+        }
+    }
+
+    pahsc->pahsc_UserBuffersPerHostBuffer = userBuffersPerHostBuffer;
+    pahsc->pahsc_FramesPerHostBuffer = framesPerHostBuffer;
+    pahsc->pahsc_NumHostBuffers = numHostBuffers;
+
+    DBUG(("PaHost_CalcNumHostBuffers: pahsc_UserBuffersPerHostBuffer = %d\n", pahsc->pahsc_UserBuffersPerHostBuffer ));
+    DBUG(("PaHost_CalcNumHostBuffers: pahsc_NumHostBuffers = %d\n", pahsc->pahsc_NumHostBuffers ));
+    DBUG(("PaHost_CalcNumHostBuffers: pahsc_FramesPerHostBuffer = %d\n", pahsc->pahsc_FramesPerHostBuffer ));
+    DBUG(("PaHost_CalcNumHostBuffers: past_NumUserBuffers = %d\n", past->past_NumUserBuffers ));
+}
+
+/*******************************************************************/
+PaError PaHost_OpenStream( internalPortAudioStream   *past )
+{
+    PaError             result = paNoError;
+    PaHostSoundControl *pahsc;
+
+    /* Allocate and initialize host data. */
+    pahsc = (PaHostSoundControl *) malloc(sizeof(PaHostSoundControl));
+    if( pahsc == NULL )
+    {
+        result = paInsufficientMemory;
+        goto error;
+    }
+    memset( pahsc, 0, sizeof(PaHostSoundControl) );
+    past->past_DeviceData = (void *) pahsc;
+
+    /* Figure out how user buffers fit into WAVE buffers. */
+    // FIXME - just force for now
+
+    pahsc->pahsc_UserBuffersPerHostBuffer = 1;
+    pahsc->pahsc_FramesPerHostBuffer = past->past_FramesPerUserBuffer;
+    pahsc->pahsc_NumHostBuffers = 2; // FIXME - dunno?!
+
+    {
+        int msecLatency = (int) ((PaHost_GetTotalBufferFrames(past) * 1000) / past->past_SampleRate);
+        PRINT(("PortAudio on OSX - Latency = %d frames, %d msec\n", PaHost_GetTotalBufferFrames(past), msecLatency ));
+    }
+
+    /* ------------------ OUTPUT */
+    if( (past->past_OutputDeviceID != paNoDevice) && (past->past_NumOutputChannels > 0) )
+    {
+        result = PaHost_OpenOutputStream( past );
+        if( result < 0 ) goto error;
+    }
+
+    /* ------------------ INPUT */
+    if( (past->past_InputDeviceID != paNoDevice) && (past->past_NumInputChannels > 0) )
+    {
+        result = PaHost_OpenInputStream( past );
+        if( result < 0 ) goto error;
+    }
+
+    return result;
+
+error:
+    PaHost_CloseStream( past );
+    return result;
+}
+
+/*************************************************************************/
+PaError PaHost_StartOutput( internalPortAudioStream *past )
+{
+    PaHostSoundControl *pahsc;
+    PaError          result = paNoError;
+    OSStatus  err = noErr;
+
+
+    pahsc = (PaHostSoundControl *) past->past_DeviceData;
+
+    if( past->past_OutputDeviceID != paNoDevice )
+    {
+        // Associate an IO proc with the device and pass a pointer to the audio data context
+        err = AudioDeviceAddIOProc(pahsc->pahsc_OutputAudioDeviceID, (AudioDeviceIOProc)appIOProc, past);
+        if (err != noErr) goto error;
+
+        // start playing sound through the device
+        err = AudioDeviceStart(pahsc->pahsc_OutputAudioDeviceID, (AudioDeviceIOProc)appIOProc);
+        if (err != noErr) goto error;
+    }
+
+error:
+    return result;
+}
+
+/*************************************************************************/
+PaError PaHost_StartInput( internalPortAudioStream *past )
+{
+    PaError          result = paNoError;
+    PaHostSoundControl *pahsc;
+    pahsc = (PaHostSoundControl *) past->past_DeviceData;
+
+    if( past->past_InputDeviceID != paNoDevice )
+    {
+        // FIXME
+    }
+    return result;
+}
+
+/*************************************************************************/
+PaError PaHost_StartEngine( internalPortAudioStream *past )
+{
+    PaError             result = paNoError;
+    PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData;
+
+#if PA_USE_TIMER_CALLBACK
+    int                 resolution;
+    int                 bufsPerTimerCallback;
+    int                 msecPerBuffer;
+#endif /* PA_USE_TIMER_CALLBACK */
+
+    past->past_StopSoon = 0;
+    past->past_StopNow = 0;
+    past->past_IsActive = 1;
+    pahsc->pahsc_FramesPlayed = 0.0;
+    pahsc->pahsc_LastPosition = 0;
+
+#if PA_TRACE_START_STOP
+    AddTraceMessage( "PaHost_StartEngine: TimeSlice() returned ", result );
+#endif
+    return result;
+}
+
+/*************************************************************************/
+PaError PaHost_StopEngine( internalPortAudioStream *past, int abort )
+{
+    int timeOut;
+    PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData;
+    if( pahsc == NULL ) return paNoError;
+
+    /* Tell background thread to stop generating more data and to let current data play out. */
+    past->past_StopSoon = 1;
+    /* If aborting, tell background thread to stop NOW! */
+    if( abort ) past->past_StopNow = 1;
+
+    timeOut = PaHost_CalcTimeOut( past );
+    past->past_IsActive = 0;
+
+    return paNoError;
+}
+
+/*************************************************************************/
+PaError PaHost_StopInput( internalPortAudioStream *past, int abort )
+{
+    PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData;
+    if( pahsc == NULL ) return paNoError;
+    (void) abort;
+
+    // FIXME
+    return paNoError;
+}
+
+/*************************************************************************/
+PaError PaHost_StopOutput( internalPortAudioStream *past, int abort )
+{
+    PaHostSoundControl *pahsc;
+    pahsc = (PaHostSoundControl *) past->past_DeviceData;
+    if( pahsc == NULL ) return paNoError;
+    (void) abort;
+
+#if PA_TRACE_START_STOP
+    AddTraceMessage( "PaHost_StopOutput: pahsc_HWaveOut ", (int) pahsc->pahsc_HWaveOut );
+#endif
+
+    // FIXME   if( pahsc->pahsc_OutputAudioDeviceID != ??? )
+    {
+        OSStatus  err = noErr;
+
+        err = AudioDeviceStop(pahsc->pahsc_OutputAudioDeviceID, (AudioDeviceIOProc)appIOProc);
+        if (err != noErr) goto Bail;
+
+        err = AudioDeviceRemoveIOProc(pahsc->pahsc_OutputAudioDeviceID, (AudioDeviceIOProc)appIOProc);
+        if (err != noErr) goto Bail;
+    }
+Bail:
+    return paNoError;
+}
+
+/*******************************************************************/
+PaError PaHost_CloseStream( internalPortAudioStream   *past )
+{
+    PaHostSoundControl *pahsc;
+
+    if( past == NULL ) return paBadStreamPtr;
+    pahsc = (PaHostSoundControl *) past->past_DeviceData;
+    if( pahsc == NULL ) return paNoError;
+
+#if PA_TRACE_START_STOP
+    AddTraceMessage( "PaHost_CloseStream: pahsc_HWaveOut ", (int) pahsc->pahsc_HWaveOut );
+#endif
+    /* Free data and device for output. */
+
+    /* Free data and device for input. */
+
+    free( pahsc );
+    past->past_DeviceData = NULL;
+
+    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 AUTOEXEC.BAT file and reboot.
+** If the environment variable is not set, then the latency will be determined
+** based on the OS. Windows NT has higher latency than Win95.
+*/
+#define PA_LATENCY_ENV_NAME  ("PA_MIN_LATENCY_MSEC")
+
+int Pa_GetMinNumBuffers( int framesPerBuffer, double sampleRate )
+{
+    int       minLatencyMsec = 0;
+    double    msecPerBuffer = (1000.0 * framesPerBuffer) / sampleRate;
+    int       minBuffers;
+
+    /* Let user determine minimal latency by setting environment variable. */
+    {
+        minLatencyMsec = PA_MIN_LATENCY_MSEC;
+    }
+
+    DBUG(("PA - Minimum Latency set to %d msec!\n", minLatencyMsec ));
+    minBuffers = (int) (1.0 + ((double)minLatencyMsec / msecPerBuffer));
+    if( minBuffers < 2 ) minBuffers = 2;
+
+    return minBuffers;
+}
+
+/*************************************************************************
+** Cleanup device info.
+*/
+PaError PaHost_Term( void )
+{
+    int i;
+
+    if( sNumDevices > 0 )
+    {
+
+        if( sDevicePtrs != NULL )
+        {
+
+            for( i=0; i<sNumDevices; i++ )
+            {
+                if( sDevicePtrs[i] != NULL )
+                {
+
+                    free( (char*)sDevicePtrs[i]->name );
+                    free( (void*)sDevicePtrs[i]->sampleRates );
+                    free( sDevicePtrs[i] );
+                }
+            }
+
+            free( sDevicePtrs );
+            sDevicePtrs = NULL;
+        }
+
+        sNumDevices = 0;
+    }
+    return paNoError;
+}
+
+/*************************************************************************/
+void Pa_Sleep( long msec )
+{
+    /*       struct timeval timeout;
+           timeout.tv_sec = msec / 1000;
+           timeout.tv_usec = (msec % 1000) * 1000;
+           select( 0, NULL, NULL, NULL, &timeout );
+    */
+    usleep( msec * 1000 );
+}
+
+/*************************************************************************
+ * Allocate memory that can be accessed in real-time.
+ * This may need to be held in physical memory so that it is not
+ * paged to virtual memory.
+ * This call MUST be balanced with a call to PaHost_FreeFastMemory().
+ */
+void *PaHost_AllocateFastMemory( long numBytes )
+{
+    void *addr = malloc( numBytes ); /* FIXME - do we need physical memory? */
+    if( addr != NULL ) memset( addr, 0, numBytes );
+    return addr;
+}
+
+/*************************************************************************
+ * Free memory that could be accessed in real-time.
+ * This call MUST be balanced with a call to PaHost_AllocateFastMemory().
+ */
+void PaHost_FreeFastMemory( void *addr, long numBytes )
+{
+    if( addr != NULL ) free( addr );
+}
+
+
+/***********************************************************************/
+PaError PaHost_StreamActive( internalPortAudioStream   *past )
+{
+    PaHostSoundControl *pahsc;
+    if( past == NULL ) return paBadStreamPtr;
+    pahsc = (PaHostSoundControl *) past->past_DeviceData;
+    if( pahsc == NULL ) return paInternalError;
+    return (PaError) past->past_IsActive;
+}
+
+/*************************************************************************/
+PaTimestamp Pa_StreamTime( PortAudioStream *stream )
+{
+    PaHostSoundControl *pahsc;
+    internalPortAudioStream   *past = (internalPortAudioStream *) stream;
+    if( past == NULL ) return paBadStreamPtr;
+    pahsc = (PaHostSoundControl *) past->past_DeviceData;
+
+    // FIXME PaHost_UpdateStreamTime( pahsc );
+    return pahsc->pahsc_FramesPlayed;
+}
diff --git a/pa_sgi/Makefile b/pa_sgi/Makefile
new file mode 100644 (file)
index 0000000..c631c9b
--- /dev/null
@@ -0,0 +1,52 @@
+# Make PortAudio for Silicon Graphics IRIX (6.2)
+# Pieter suurmond, august 17, 2001. (v15 pa_sgi sub-version #0.04)
+
+# Instead of "-lpthread", as with linux,
+# just the audio- and math-library for SGI:
+LIBS = -laudio -lm
+
+CDEFINES = -I../pa_common
+
+# Possible CFLAGS with MIPSpro compiler are: -32, -o32, -n32, -64,
+# -mips1, -mips2, -mips3, -mips4, etc. Use -g, -g2, -g3 for debugging. 
+# And use for example -O2 or -O3 for better optimization:
+CFLAGS = -O2
+PASRC = ../pa_common/pa_lib.c pa_sgi.c
+PAINC = ../pa_common/portaudio.h
+
+# Tests that work                                    (SGI Indy with R5000 @ 180MHz running IRIX 6.2). 
+#TESTC   = $(PASRC) ../pa_tests/patest_many.c      # OK
+TESTC   = $(PASRC) ../pa_tests/patest_record.c    # OK
+#TESTC   = $(PASRC) ../pa_tests/patest_latency.c   # OK
+#TESTC   = $(PASRC) ../pa_tests/patest_longsine.c  # OK but needs more than 4 buffers to do without glitches.
+#TESTC   = $(PASRC) ../pa_tests/patest_saw.c       # Seems OK (does it gracefully exit?).
+#TESTC   = $(PASRC) ../pa_tests/patest_wire.c      # OK
+#TESTC   = $(PASRC) ../pa_tests/pa_devs.c          # OK
+#TESTC   = $(PASRC) ../pa_tests/patest_sine.c      # OK
+#TESTC   = $(PASRC) ../pa_tests/patest_sine_time.c # OK
+#TESTC   = $(PASRC) ../pa_tests/patest_sine8.c     # OK
+#TESTC   = $(PASRC) ../pa_tests/patest_leftright.c # OK
+#TESTC   = $(PASRC) ../pa_tests/patest_pink.c      # OK
+#TESTC   = $(PASRC) ../pa_tests/patest_clip.c      # OK
+#TESTC   = $(PASRC) ../pa_tests/patest_stop.c      # OK
+#TESTC   = $(PASRC) ../pa_tests/patest_dither.c    # OK
+#TESTC   = $(PASRC) ../pa_tests/patest_sync.c      # BEEPS and delta's, no crashes anymore.
+
+# Tests that do not yet work.
+#TESTC   = $(PASRC) ../pa_tests/paqa_devs.c        # Heavy crash with core-dump after PaHost_OpenStream: opening 1 output channel(s) on AL default
+#TESTC   = $(PASRC) ../pa_tests/paqa_errs.c        # Heavy crash with core-dump after PaHost_OpenStream: opening 2 output channel(s) on AL default
+#TESTC   = $(PASRC) ../pa_tests/pa_fuzz.c    # THIS CRASHED MY WHOLE IRIX SYSTEM after "ENTER"! :-(
+                                             # PROCESS IN ITSELF RUNS OK, WITH LARGER BUFFSIZES THOUGH.
+                                             # OH MAYBE IT WAS BECAUSE DEBUGGING WAS ON???
+                                             # HMMM I'M NOT GONNA RUN THAT AGAIN... WAY TOO DANGEROUS!
+TESTH   = $(PAINC)
+
+all: patest
+
+#(cc may be changed to gcc)
+patest: $(TESTC) $(TESTH) Makefile
+       cc $(CFLAGS) $(TESTC) $(CDEFINES) $(LIBS) -o patest
+
+run: patest
+       ./patest
+
diff --git a/pa_sgi/pa_sgi.c b/pa_sgi/pa_sgi.c
new file mode 100644 (file)
index 0000000..094cb4d
--- /dev/null
@@ -0,0 +1,991 @@
+/*
+ * $Id$
+ * PortAudio Portable Real-Time Audio Library
+ * Latest Version at: http://www.portaudio.com
+ * SGI IRIX implementation by Pieter Suurmond, aug 17, 2001.
+ *
+ * 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.
+ */
+/*
+Modfication History:
+  8/12/2001 - Pieter Suurmond - took the v15 pa_linux_oss.c file and started to adapt for IRIX 6.2.
+  8/17/2001 - (v15 pa_sgi sub-version #0.04), wow, much is working now!  Send this to Phil & Ross. 
+              ID-stuff is nicer here now than in the linux_oss-alpha-version, v15. Was able to get 
+              rid of a number of globals (like "sNumDevices" for instance).
+TODO:
+  - Test under IRIX 6.5.
+  - Maybe use pthread instead of (platform-specific) poll/select/pollfd-calls. 
+    (As Gary Scavone more or less suggested(?)... But can I get enough process-control then?)
+  - Dynamically switch to 32 bit float as native format when appropriate (let SGI do the conversion), 
+    and maybe also the other natively supported formats? (might increase performance)
+  - Not sure whether CPU UTILIZATION MEASUREMENT (from OSS/linux) really works. Changed nothing yet,
+    seems ok, but I've not yet tested it thoroughly. (maybe utilization-code may be made _unix_common_ then?)
+  - Make sure that the fuzz-test doesn't let all IRIX crash (especially with higher buff-sizes).
+  - The minimal number of buffers setting... I do not yet fully understand it.. I now just take *4.
+REFERENCES:
+  - IRIX 6.2 man pages regarding SGI AL library.
+  - IRIS Digital MediaProgramming Guide (online books as well as man-pages come with IRIX 6.2 and 
+    may not be publically available on the internet).
+*/
+
+#include <stdio.h>              /* Standard libraries. */
+#include <stdlib.h>
+#include <malloc.h>
+#include <memory.h>
+#include <math.h>
+
+#include "portaudio.h"          /* Portaudio headers. */
+#include "pa_host.h"
+#include "pa_trace.h"
+
+#include <sys/time.h>           /* System specific (IRIX 6.2). */
+#include <sys/types.h>
+#include <sys/prctl.h>
+#include <sys/schedctl.h>
+#include <fcntl.h>              /* fcntl.h needed.             */
+#include <unistd.h>             /* For streams, ioctl(), etc.  */
+#include <signal.h>
+#include <ulocks.h>
+#include <poll.h>
+#include <limits.h>
+#include <dmedia/audio.h>
+
+/*--------------------------------------------*/
+#define PRINT(x)   { printf x; fflush(stdout); }
+#define ERR_RPT(x) PRINT(x)
+#define DBUG(x)    PRINT(x)
+#define DBUGX(x)   /* PRINT(x) */
+
+#define MAX_CHARS_DEVNAME           (16)        /* Was 32 in OSS (20 for AL but "in"/"out" is concat. */
+#define MAX_SAMPLE_RATES            (8)         /* Known from SGI AL there are 7 (was 10 in OSS v15). */
+
+typedef struct                    internalPortAudioDevice                 /* IRIX specific device info:      */
+{
+    PaDeviceID           /* NEW: */ pad_DeviceID;                           /* THIS "ID" IS NEW HERE (Pieter)! */
+    long                            pad_ALdevice;                           /* SGI-number!                     */
+    double                          pad_SampleRates[MAX_SAMPLE_RATES];      /* for pointing to from pad_Info   */
+    char                            pad_DeviceName[MAX_CHARS_DEVNAME+1];    /* +1 for \0, one more than OSS.   */
+    PaDeviceInfo                    pad_Info;                               /* pad_Info (v15) contains:        */
+    /* int            structVersion;                                           */
+    /* const char*    name;                                                    */
+    /* int            maxInputChannels, maxOutputChannels;                     */
+    /* int            numSampleRates;       Num rates, or -1 if range supprtd. */
+    /* const double*  sampleRates;          Array of supported sample rates,   */
+    /* PaSampleFormat nativeSampleFormats;  or {min,max} if range supported.   */
+    struct internalPortAudioDevice* pad_Next;                               /* Singly linked list, (NULL=end). */
+}
+internalPortAudioDevice;
+
+typedef struct      PaHostSoundControl              /* Structure to contain all SGI IRIX specific data. */
+{
+    ALport            pahsc_ALportIN,                 /* IRIX-audio-library-datatype. ALports can only be */
+    pahsc_ALportOUT;                /* unidirectional, so we sometimes need 2 of them.  */
+    int               pahsc_threadPID;                /* Sproc()-result, written by PaHost_StartEngine(). */
+    short             *pahsc_NativeInputBuffer,       /* Allocated here, in this file, if necessary.      */
+    *pahsc_NativeOutputBuffer;
+    unsigned int      pahsc_BytesPerInputBuffer,      /* Native buffer sizes in bytes, really needed here */
+    pahsc_BytesPerOutputBuffer;     /* to free FAST memory, if buffs were alloctd FAST. */
+    unsigned int      pahsc_SamplesPerInputBuffer,    /* These amounts are needed again and again in the  */
+    pahsc_SamplesPerOutputBuffer;   /* audio-thread (don't need to be kept globally).   */
+    struct itimerval  pahsc_EntryTime,                /* For measuring CPU utilization (same as linux).   */
+                pahsc_LastExitTime;
+    long              pahsc_InsideCountSum,
+    pahsc_TotalCountSum;
+}
+PaHostSoundControl;
+
+/*----------------------------- Shared Data ------------------------------------------------------*/
+static internalPortAudioDevice* sDeviceList = NULL; /* FIXME - put Mutex around this shared data. */
+static int                      sPaHostError = 0;   /* Maybe more than one process writing errs!? */
+
+/*----------------------------- BEGIN CPU UTILIZATION MEASUREMENT -----------------*/
+/*                              (copied from source pa_linux_oss/pa_linux_oss.c)   */
+static void Pa_StartUsageCalculation( internalPortAudioStream   *past )
+{
+    struct itimerval itimer;
+    PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData;
+    if( pahsc == NULL ) return;
+    /* Query system timer for usage analysis and to prevent overuse of CPU. */
+    getitimer( ITIMER_REAL, &pahsc->pahsc_EntryTime );
+}
+
+static long SubtractTime_AminusB( struct itimerval *timeA, struct itimerval *timeB )
+{
+    long secs = timeA->it_value.tv_sec - timeB->it_value.tv_sec;
+    long usecs = secs * 1000000;
+    usecs += (timeA->it_value.tv_usec - timeB->it_value.tv_usec);
+    return usecs;
+}
+
+static void Pa_EndUsageCalculation( internalPortAudioStream   *past )
+{
+    struct itimerval currentTime;
+    long  insideCount;
+    long  totalCount;       /* Measure CPU utilization during this callback. */
+
+#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 (getitimer( ITIMER_REAL, &currentTime ) == 0 )
+    {
+        if (past->past_IfLastExitValid)
+        {
+            insideCount = SubtractTime_AminusB( &pahsc->pahsc_EntryTime, &currentTime );
+            pahsc->pahsc_InsideCountSum += insideCount;
+            totalCount =  SubtractTime_AminusB( &pahsc->pahsc_LastExitTime, &currentTime );
+            pahsc->pahsc_TotalCountSum += totalCount;
+            /*  DBUG(("insideCount = %d, totalCount = %d\n", insideCount, totalCount )); */
+            /* 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!!!        */
+            if (pahsc->pahsc_InsideCountSum > 0)
+            {
+                past->past_AverageInsideCount = ((LOWPASS_COEFFICIENT_0 * past->past_AverageInsideCount) +
+                                                 (LOWPASS_COEFFICIENT_1 * pahsc->pahsc_InsideCountSum));
+                past->past_AverageTotalCount  = ((LOWPASS_COEFFICIENT_0 * past->past_AverageTotalCount) +
+                                                 (LOWPASS_COEFFICIENT_1 * pahsc->pahsc_TotalCountSum));
+                past->past_Usage = past->past_AverageInsideCount / past->past_AverageTotalCount;
+                pahsc->pahsc_InsideCountSum = 0;
+                pahsc->pahsc_TotalCountSum = 0;
+            }
+        }
+        past->past_IfLastExitValid = 1;
+    }
+    pahsc->pahsc_LastExitTime.it_value.tv_sec = 100;
+    pahsc->pahsc_LastExitTime.it_value.tv_usec = 0;
+    setitimer( ITIMER_REAL, &pahsc->pahsc_LastExitTime, NULL );
+    past->past_IfLastExitValid = 1;
+}   /*----------- END OF CPU UTILIZATION CODE (from pa_linux_oss/pa_linux_oss.c v15)--------------------*/
+
+
+/*--------------------------------------------------------------------------------------*/
+PaError translateSGIerror(void) /* Calls oserror(), may be used after an SGI AL-library */
+{                               /* call to report via ERR_RPT(), yields a PaError-num.  */
+    const char* a = "SGI AL ";  /* (Not absolutely sure errno came from THIS thread!    */
+    switch(oserror())           /* Read IRIX man-pages about the _SGI_MP_SOURCE macro.) */
+    {
+    case AL_BAD_OUT_OF_MEM:
+        ERR_RPT(("%sout of memory.\n", a));
+        return paInsufficientMemory;                   /* Known PaError.   */
+    case AL_BAD_CONFIG:
+        ERR_RPT(("%sconfiguration invalid or NULL.\n", a));
+        return paHostError;                            /* Generic PaError. */
+    case AL_BAD_CHANNELS:
+        ERR_RPT(("%schannels not 1,2 or 4.\n", a));
+        return paHostError;                            /* Generic PaError. */
+    case AL_BAD_NO_PORTS:
+        ERR_RPT(("%sout of audio ports.\n", a));
+        return paHostError;                            /* Generic PaError. */
+    case AL_BAD_DEVICE:
+        ERR_RPT(("%swrong device number.\n", a));
+        return paHostError;                            /* Generic PaError. */
+    case AL_BAD_DEVICE_ACCESS:
+        ERR_RPT(("%swrong device access.\n", a));
+        return paHostError;                            /* Generic PaError. */
+    case AL_BAD_DIRECTION:
+        ERR_RPT(("%sinvalid direction.\n", a));
+        return paHostError;                            /* Generic PaError. */
+    case AL_BAD_SAMPFMT:
+        ERR_RPT(("%sdoesn't accept sampleformat.\n", a));
+        return paHostError;                            /* Generic PaError. */
+    case AL_BAD_FLOATMAX:
+        ERR_RPT(("%smax float value is zero.\n", a));
+        return paHostError;                            /* Generic PaError. */
+    case AL_BAD_WIDTH:
+        ERR_RPT(("%sunsupported samplewidth.\n", a));
+        return paHostError;                            /* Generic PaError. */
+    case AL_BAD_QSIZE:
+        ERR_RPT(("%sinvalid queue size.\n", a));
+        return paHostError;                            /* Generic PaError. */
+    case AL_BAD_PVBUFFER:
+        ERR_RPT(("%sPVbuffer null.\n", a));
+        return paHostError;                            /* Generic PaError. */
+    case AL_BAD_BUFFERLENGTH_NEG:
+        ERR_RPT(("%snegative bufferlength.\n", a));
+        return paHostError;                            /* Generic PaError. */
+    case AL_BAD_BUFFERLENGTH_ODD:
+        ERR_RPT(("%sodd bufferlength.\n", a));
+        return paHostError;                            /* Generic PaError. */
+    case AL_BAD_PARAM:
+        ERR_RPT(("%sparameter not valid for device.\n", a));
+        return paHostError;                            /* Generic PaError. */
+    default:
+        ERR_RPT(("%sunknown error.\n", a));
+        return paHostError;                            /* Generic PaError. */
+    }
+}
+
+/*------------------------------------------------------------------------------------------*/
+/* Tries to set various rates and formats and fill in the device info structure.            */
+static PaError Pa_sgiQueryDevice(long                     ALdev,  /* (AL_DEFAULT_DEVICE)    */
+                                 PaDeviceID               id,     /* (DefaultI|ODeviceID()) */
+                                 char*                    name,   /* (for example "SGI AL") */
+                                 internalPortAudioDevice* pad)    /* Result written to pad. */
+{
+    int     format;
+    long    min, max;                           /* To catch hardware characteristics.       */
+    ALseterrorhandler(0);                       /* 0 = turn off the default error handler.  */
+    /*--------------------------------------------------------------------------------------*/
+    pad->pad_ALdevice = ALdev;                              /* Set the AL device number.    */
+    pad->pad_DeviceID = id;                                 /* Set the PA device number.    */
+    if (strlen(name) > MAX_CHARS_DEVNAME)                   /* MAX_CHARS defined above.     */
+    {
+        ERR_RPT(("Pa_QueryDevice(): name too long (%s).\n", name));
+        return paHostError;
+    }
+    strcpy(pad->pad_DeviceName, name);                      /* Write name-string.           */
+    pad->pad_Info.name = pad->pad_DeviceName;               /* Set pointer,..hmmm.          */
+    /*--------------------------------- natively supported sample formats: -----------------*/
+    pad->pad_Info.nativeSampleFormats = paInt16; /* Later also include paFloat32 | ..| etc. */
+    /* Then also choose other CallConvertXX()! */
+    /*--------------------------------- number of available i/o channels: ------------------*/
+    if (ALgetminmax(ALdev, AL_INPUT_COUNT, &min, &max))
+        return translateSGIerror();
+    pad->pad_Info.maxInputChannels = max;
+    DBUG(("Pa_QueryDevice: maxInputChannels = %d\n", pad->pad_Info.maxInputChannels))
+    if (ALgetminmax(ALdev, AL_OUTPUT_COUNT, &min, &max))
+        return translateSGIerror();
+    pad->pad_Info.maxOutputChannels = max;
+    DBUG(("Pa_QueryDevice: maxOutputChannels = %d\n", pad->pad_Info.maxOutputChannels))
+    /*--------------------------------- supported samplerates: ----------------------*/
+    pad->pad_Info.numSampleRates = 7;
+    pad->pad_Info.sampleRates = pad->pad_SampleRates;
+    pad->pad_SampleRates[0] = (double)AL_RATE_8000;     /* long -> double. */
+    pad->pad_SampleRates[1] = (double)AL_RATE_11025;
+    pad->pad_SampleRates[2] = (double)AL_RATE_16000;
+    pad->pad_SampleRates[3] = (double)AL_RATE_22050;
+    pad->pad_SampleRates[4] = (double)AL_RATE_32000;
+    pad->pad_SampleRates[5] = (double)AL_RATE_44100;
+    pad->pad_SampleRates[6] = (double)AL_RATE_48000;
+    if (ALgetminmax(ALdev, AL_INPUT_RATE, &min, &max))  /* Ask INPUT rate-max.       */
+        return translateSGIerror();                     /* double -> long.           */
+    if (max != (long)(0.5 + pad->pad_SampleRates[6]))   /* FP-compare not recommndd. */
+        goto weird;
+    if (ALgetminmax(ALdev, AL_OUTPUT_RATE, &min, &max)) /* Ask OUTPUT rate-max.      */
+        return translateSGIerror();
+    if (max != (long)(0.5 + pad->pad_SampleRates[6]))
+    {
+weird:  ERR_RPT(("Pa_sgiQueryDevice() did not confirm max samplerate (%ld)\n",max));
+        return paHostError;             /* Or make it a warning and just carry on... */
+    }
+    /*-------------------------------------------------------------------------------*/
+    return paNoError;
+}
+
+
+/*--------------------------------------------------------------------------------*/
+int Pa_CountDevices()       /* Name of this function suggests it only counts and  */
+{                           /* is NOT destructive, it however resets whole PA !   */
+    int                        numDevices = 0;        /* Let 's not do that here. */
+    internalPortAudioDevice*   currentDevice = sDeviceList;   /* COPY GLOBAL VAR. */
+#if 0                       /* Remains from linux_oss v15: Pa_Initialize(), on    */
+    if (!currentDevice)     /* its turn, calls PaHost_Init() via file pa_lib.c.   */
+        Pa_Initialize();    /* Isn't that a bit too 'rude'?        Don't be too   */
+#endif                      /* friendly to clients that forgot to initialize PA.  */
+    while (currentDevice)   /* Slower but more elegant than the sNumDevices-way:  */
+    {
+        numDevices++;
+        currentDevice = currentDevice->pad_Next;
+    }
+    return numDevices;
+}
+
+
+
+/*-------------------------------------------------------------------------------*/
+static internalPortAudioDevice *Pa_GetInternalDevice(PaDeviceID id)
+{
+    int                         numDevices = 0;
+    internalPortAudioDevice     *res = (internalPortAudioDevice*)NULL;
+    internalPortAudioDevice     *pad = sDeviceList;         /* COPY GLOBAL VAR.  */
+    while (pad)                         /* pad may be NULL, that's ok, return 0. */
+    {  /* (Added ->pad_DeviceID field to the pad-struct, Pieter, 2001.)      */
+        if (pad->pad_DeviceID == id)    /* This the device we were looking for?  */
+            res = pad;                  /* But keep on(!) counting so we don't   */
+        numDevices++;                   /* have to call Pa_CountDevices() later. */
+        pad = pad->pad_Next;            /* Advance to the next device or NULL.   */
+    }                               /* No assumptions about order of ID's in */
+    if (!res)                           /* the list.                             */
+        ERR_RPT(("Pa_GetInternalDevice() could not find specified ID (%d).\n",id));
+    if ((id < 0) || (id >= numDevices))
+    {
+        ERR_RPT(("Pa_GetInternalDevice() supplied with an illegal ID (%d).\n",id));
+#if 1                                             /* Be strict, even when found, */
+        res = (internalPortAudioDevice*)NULL;     /* do not accept illegal ID's. */
+#endif
+
+    }
+    return res;
+}
+
+/*----------------------------------------------------------------------*/
+const PaDeviceInfo* Pa_GetDeviceInfo(PaDeviceID id)
+{
+    PaDeviceInfo*             res = (PaDeviceInfo*)NULL;
+    internalPortAudioDevice*  pad = Pa_GetInternalDevice(id);  /* Call. */
+    if (pad)
+        res = &pad->pad_Info;   /* Not finding the specified ID is not  */
+    if (!res)                   /* the same as &pad->pad_Info == NULL.  */
+        ERR_RPT(("Pa_GetDeviceInfo() could not find it (ID=%d).\n", id));
+    return res;                 /* So (maybe) a second/third ERR_RPT(). */
+}
+
+/*------------------------------------------------*/
+PaDeviceID Pa_GetDefaultInputDeviceID(void)
+{
+    return 0; /* 0 is the default device ID. */
+}
+/*------------------------------------------------*/
+PaDeviceID Pa_GetDefaultOutputDeviceID(void)
+{
+    return 0;
+}
+
+/*--------------------------------------------------------------------------------------*/
+/* Build linked list with all available audio devices on this SGI machine.              */
+PaError PaHost_Init(void)                   /* Called by Pa_Initialize() from pa_lib.c. */
+{
+    internalPortAudioDevice*    pad;
+    PaError                     r = paNoError;
+    int                         audioLibFileID;
+
+    if (sDeviceList)            /* Allow re-init, only warn, no err.  */
+    {
+        ERR_RPT(("Warning: PaHost_Init() did not really re-init PA.\n"));
+        return r;
+    }
+    /*------------- ADD THE SGI DEFAULT DEVICES TO THE LIST: --------------------------------------*/
+    audioLibFileID = open("/dev/hdsp/hdsp0master", O_RDONLY);   /* Try to open Indigo style audio  */
+    if (audioLibFileID < 0)                                     /* IO port. On failure, machine    */
+    {                                                       /* has no audio ability.           */
+        ERR_RPT(("PaHost_Init(): This machine has no (Indigo-style) audio abilities.\n"));
+        return paHostError;
+    }
+    close(audioLibFileID);                              /* Allocate fast mem to hold device info.  */
+    pad = PaHost_AllocateFastMemory(sizeof(internalPortAudioDevice));
+    if (pad == NULL)
+        return paInsufficientMemory;
+    memset(pad, 0, sizeof(internalPortAudioDevice));    /* "pad->pad_Next = NULL" is more elegant. */
+    r = Pa_sgiQueryDevice(AL_DEFAULT_DEVICE,            /* Set AL device num (AL_DEFAULT_DEVICE).  */
+                          Pa_GetDefaultOutputDeviceID(),/* Set PA device num (or InputDeviceID()). */
+                          "AL default",                 /* A suitable name.                        */
+                          pad);                         /* Write args and queried info into pad.   */
+    if (r != paNoError)
+    {
+        ERR_RPT(("Pa_QueryDevice for '%s' returned: %d\n", pad->pad_DeviceName, r));
+        PaHost_FreeFastMemory(pad, sizeof(internalPortAudioDevice));   /* sDeviceList still NULL ! */
+    }
+    else
+        sDeviceList = pad;      /* First element in linked list (pad->pad_Next is already NULL).   */
+    /*------------- QUERY AND ADD MORE POSSIBLE SGI DEVICES TO THE LIST: --------------*/
+    /*---------------------------------------------------------------------------------*/
+    return r;
+}
+
+/*------------------------------------------------------------------*/
+usema_t *SendSema,  /* These variables are shared between the audio */
+*RcvSema;   /* handling process and the main process.       */
+
+/*--------------------------------------------------------------------------------------------*/
+#define MIN(a,b)    ((a)<(b)?(a):(b))   /* MIN()-function is used below.                      */
+#define kPollSEMA   0                   /* To index the pollfd-array, reads nicer than just   */
+#define kPollOUT    1                   /* numbers.                                           */
+#define kPollIN     2
+void Pa_SgiAudioProcess(void *v)        /* This function is sproc-ed by PaHost_StartEngine()  */
+{                                       /* as a separate thread. (Argument must be void*).    */
+    short                   evtLoop=1;  /* Reset by parent indirectly, or at local errors.    */
+    PaError                 result;
+    struct pollfd           PollFD[3];  /* To catch kPollSEMA-, kPollOUT- and kPollIN-events. */
+    internalPortAudioStream *past  = (internalPortAudioStream*)v;   /* Copy void-ptr-argument.*/
+    PaHostSoundControl      *pahsc = (PaHostSoundControl*)past->past_DeviceData;
+    if (pahsc == NULL)
+    {
+        sPaHostError = paInternalError; /* The only way is to signal error to shared area?!   */
+        goto skipALL;                   /* Sproc-ed threads MAY NOT RETURN paInternalError.   */
+    }
+    past->past_IsActive = 1;            /* Wasn't this already done by the calling parent?!   */
+    DBUG(("entering thread.\n"));
+    PollFD[kPollIN].fd = ALgetfd(pahsc->pahsc_ALportIN);    /* ALgetfd returns -1 on failures */
+    PollFD[kPollIN].events = POLLOUT;                       /* such as ALport not there.      */
+    /* .events = POLLOUT ??? ok? */
+    PollFD[kPollOUT].fd = ALgetfd(pahsc->pahsc_ALportOUT);
+    PollFD[kPollOUT].events = POLLOUT;                      /* .events = POLLOUT is OK.       */
+    schedctl(NDPRI, NDPHIMIN);              /* Sets non-degrading priority for this process.  */
+    PollFD[kPollSEMA].fd = usopenpollsema(SendSema, 0777);  /* To communicate with parent.    */
+    PollFD[kPollSEMA].events = POLLIN;                      /* .events = POLLIN is OK.        */
+    uspsema(SendSema);              /* Blocks until the next (first)signal is received???? */
+    do
+    {
+        /*---------------------------- SET FILLPOINTS AND WAIT UNTIL SOMETHING HAPPENS: ----------*/
+        if (pahsc->pahsc_NativeInputBuffer)         /* Then pahsc_ALportIN should also be there!  */
+            /* For input port, fill point is number of locations in the sample queue that must be */
+            /* filled in order to trigger a return from select(). (or poll())                     */
+            /* Notice IRIX docs mention number of samples as argument, not number of sampleframes.*/
+            if (ALsetfillpoint(pahsc->pahsc_ALportIN, pahsc->pahsc_SamplesPerInputBuffer))
+            {                                       /* Same amount as transferred per time.   */
+                ERR_RPT(("ALsetfillpoint() for ALportIN failed.\n"));
+                sPaHostError = paInternalError;         /* (Using exit(-1) would be a bit rude.)  */
+                goto skipIO;
+            }
+        if (pahsc->pahsc_NativeOutputBuffer)        /* Then pahsc_ALportOUT should also be there! */
+            /* For output port, fill point is number of locations that must be free in order to   */
+            /* wake up from select(). (or poll())                                                 */
+            if (ALsetfillpoint(pahsc->pahsc_ALportOUT, pahsc->pahsc_SamplesPerOutputBuffer))
+            {
+                ERR_RPT(("ALsetfillpoint() for ALportOUT failed.\n"));
+                sPaHostError = paInternalError;         /* (Using exit(-1) would be a bit rude.)  */
+                goto skipIO;
+            }                   /* poll() with timeout=-1 makes it block until a requested    */
+        poll(PollFD, 3, -1);        /* event occurs or until call is interrupted. If fd-value in  */
+        /* array <0, events is ignored and revents is set to 0.       */
+        /*---------------------------- MESSAGE-EVENT FROM PARENT THREAD: -------------------------*/
+        if (PollFD[kPollSEMA].revents & POLLIN)
+        {
+            if (past->past_StopNow || past->past_StopSoon)
+                evtLoop = 0;
+            uspsema(SendSema);  /* Decrements count of previously allocated semaphore SendSema. If count  */
+            /* is then negative, semaphore will logically block calling process until */
+            /* count is incremented due to an usvsema call made by another process.   */
+            usvsema(RcvSema);   /* Increments the count associated with RcvSema. If there are any         */
+            /* processes queued waiting for the semaphore the first one is awakened.  */
+            /* Here usvsema sends an acknowledgement to the "SendAudioCommand()".     */
+            if (past->past_StopNow)
+                goto skipIO;    /* Else go on for a little while... */
+        }
+        /*------------------------------------- FREE-EVENT FROM INPUT BUFFER: ----------------------------*/
+        if (PollFD[kPollIN].revents & POLLOUT)  /* Don't need to check (pahsc->pahsc_NativeInputBuffer):  */
+        {                                   /* if buffer was not there, ALport not there, no events!  */
+            if (ALreadsamps(pahsc->pahsc_ALportIN, (void*)pahsc->pahsc_NativeInputBuffer,
+                            pahsc->pahsc_SamplesPerInputBuffer))
+            {                           /* Here again: number of samples instead of number of frames. */
+                ERR_RPT(("ALreadsamps() failed.\n"));
+                sPaHostError = paInternalError;
+                goto skipIO;                    /* (Using exit(-1) would be a bit rude.) */
+            }
+        }
+        /*------------------------------------- USER-CALLBACK-ROUTINE: -----------------------------------*/
+        if ((PollFD[kPollIN].revents & POLLOUT) || (PollFD[kPollOUT].revents & POLLOUT))
+        { /* To be sure we that really DID input-transfer or gonna DO output-transfer, and that it is */
+            /* not just "sema"- (i.e. user)-event, or some other system-event that awakened the poll(). */
+            Pa_StartUsageCalculation(past);                         /* Convert 16 bit native data to      */
+            result = Pa_CallConvertInt16(past,                      /* user data and call user routine.   */
+                                         pahsc->pahsc_NativeInputBuffer,
+                                         pahsc->pahsc_NativeOutputBuffer);
+            Pa_EndUsageCalculation(past);
+            if (result)
+            {
+                DBUG(("Pa_CallConvertInt16() returned %d, stopping...\n", result));
+                goto skipIO;                    /* But it is apparently NOT an error!     */
+            }                               /* Just letting the userCallBack stop us. */
+        }
+        /*------------------------------------- FILLED-EVENT FROM OUTPUT BUFFER: -------------------------*/
+        if (PollFD[kPollOUT].revents & POLLOUT) /* Don't need to check (pahsc->pahsc_NativeOutputBuffer)  */
+        {                                   /* because if filedescriptor not there, no event for it.  */
+            if (ALwritesamps(pahsc->pahsc_ALportOUT, (void*)pahsc->pahsc_NativeOutputBuffer,
+                             pahsc->pahsc_SamplesPerOutputBuffer))
+            {
+                ERR_RPT(("ALwritesamps() failed.\n"));
+                sPaHostError = paInternalError;    /* Better use SEMAS also for messaging back to parent! */
+                goto skipIO;
+            }
+        }
+        /*------------------------------------------------------------------------------------------------*/
+    }
+    while (evtLoop);                      /* Set at init-time, only reset indirectly via semaphore, */
+skipIO:                                         /* or in case of error, only within this loop.            */
+    DBUG(("leaving thread.\n"));             /* Don't we need to cleanup semaphore-stuff / thread itself? */
+skipALL:                                     /* (make threadPID in the struct -1 again, for instance?)    */
+    past->past_IsActive = 0;
+    pahsc->pahsc_threadPID == -1;           /* Signal back, so StopEngine() for example won't call KILL().... hmmm doesn't work yet!  */
+    /* SHIT: when this process ends, it also KILLS of the parent!!! because sigint = kill?? */
+    exit(0);            /* Unix process. SIGCLD is sent to parent (if prctl wa called with 0 by parent). */
+}                       /* if prctl was called with 9 by parent, then when this process ends, it also    */
+/* KILLS off the parent!!!                                                       */
+
+/*--------------------------------------------------------------------------------------*/
+PaError PaHost_OpenStream(internalPortAudioStream *past)
+{
+    PaError                 result = paNoError;
+    PaHostSoundControl      *pahsc;
+    unsigned int            minNumBuffers;
+    internalPortAudioDevice *padIN, *padOUT;        /* For looking up native AL-numbers. */
+    ALconfig                sgiALconfig = NULL;     /* IRIX-datatype.   */
+    long                    pvbuf[8];               /* To get/set hardware configs.      */
+    long                    sr, ALqsize;
+    DBUG(("PaHost_OpenStream() called.\n"));        /* Alloc FASTMEM and init host data. */
+    if (!past)
+    {
+        ERR_RPT(("Streampointer NULL!\n"));
+        result = paBadStreamPtr;        goto done;
+    }
+    pahsc = (PaHostSoundControl*)PaHost_AllocateFastMemory(sizeof(PaHostSoundControl));
+    if (pahsc == NULL)
+    {
+        ERR_RPT(("FAST Memory allocation failed.\n"));  /* Pass trough some ERR_RPT-exit-  */
+        result = paInsufficientMemory;  goto done;      /* code (nothing will be freed).   */
+    }
+    memset(pahsc, 0, sizeof(PaHostSoundControl));   /* Hmmm, not too elegant, besides, the */
+    pahsc->pahsc_threadPID = -1;                    /* pahsc_threadPID must be inited to   */
+    past->past_DeviceData = (void*)pahsc;           /* -1 instead of 0! ??                 */
+    /*--------------------------------------------------- Allocate native buffers: --------*/
+    pahsc->pahsc_SamplesPerInputBuffer = past->past_FramesPerUserBuffer * /* Needed by the */
+                                         past->past_NumInputChannels;     /* audio-thread. */
+    pahsc->pahsc_BytesPerInputBuffer   = pahsc->pahsc_SamplesPerInputBuffer * sizeof(short);
+    if (past->past_NumInputChannels > 0)                       /* Assumes short = 16 bits! */
+    {
+        pahsc->pahsc_NativeInputBuffer = (short *) malloc(pahsc->pahsc_BytesPerInputBuffer);
+        if( pahsc->pahsc_NativeInputBuffer == NULL )
+        {
+            ERR_RPT(("(normal yet) Memory allocation failed.\n"));
+            result = paInsufficientMemory;
+            goto done;
+        }
+    }
+    pahsc->pahsc_SamplesPerOutputBuffer = past->past_FramesPerUserBuffer * /* Needed by the */
+                                          past->past_NumOutputChannels;    /* audio-thread. */
+    pahsc->pahsc_BytesPerOutputBuffer   = pahsc->pahsc_SamplesPerOutputBuffer * sizeof(short);
+    if (past->past_NumOutputChannels > 0)                       /* Assumes short = 16 bits! */
+    {
+        pahsc->pahsc_NativeOutputBuffer = (short *) malloc(pahsc->pahsc_BytesPerOutputBuffer);
+        if (pahsc->pahsc_NativeOutputBuffer == NULL)
+        {
+            ERR_RPT(("(normal yet) Memory allocation failed.\n"));
+            result = paInsufficientMemory;  goto done;
+        }
+    }
+    /*------------------------------------------ Manipulate hardware if necessary and allowed: --*/
+    ALseterrorhandler(0);                           /* 0 = turn off the default error handler.   */
+    pvbuf[0] = AL_INPUT_RATE;
+    pvbuf[2] = AL_INPUT_COUNT;
+    pvbuf[4] = AL_OUTPUT_RATE;              /* TO FIX: rates may be logically, not always in Hz! */
+    pvbuf[6] = AL_OUTPUT_COUNT;
+    sr = (long)(past->past_SampleRate + 0.5);   /* Common for input and output :-)               */
+    if (past->past_NumInputChannels > 0)                        /* We need to lookup the corre-  */
+    {                                                       /* sponding native AL-number(s). */
+        padIN = Pa_GetInternalDevice(past->past_InputDeviceID);
+        if (!padIN)
+        {
+            ERR_RPT(("Pa_GetInternalDevice() for input failed.\n"));
+            result = paHostError;  goto done;
+        }
+        if (ALgetparams(padIN->pad_ALdevice, &pvbuf[0], 4)) /* Although input and output will both be on */
+            goto sgiError;                                  /* the same AL-device, the AL-library might  */
+        if (pvbuf[1] != sr)                                 /* contain more than AL_DEFAULT_DEVICE in    */
+        {  /* Rate different from current harware-rate?    the future. Therefore 2 seperate queries. */
+            if (pvbuf[3] > 0)     /* Means, there's other clients using AL-input-ports */
+            {
+                ERR_RPT(("Sorry, not allowed to switch input-hardware to %ld Hz because \
+                         another process is currently using input at %ld kHz.\n", sr, pvbuf[1]));
+                result = paHostError;   goto done;
+            }
+            pvbuf[1] = sr;                  /* Then set input-rate. */
+            if (ALsetparams(padIN->pad_ALdevice, &pvbuf[0], 2))
+                goto sgiError;  /* WHETHER THIS SAMPLERATE WAS REALLY PRESENT IN OUR ARRAY OF RATES,    */
+        }
+    }
+    if (past->past_NumOutputChannels > 0)           /* CARE: padOUT/IN may NOT be NULL if Channels<=0!   */
+    {                                           /* We use padOUT/IN later on, or at least 1 of both. */
+        padOUT = Pa_GetInternalDevice(past->past_OutputDeviceID);
+        if (!padOUT)
+        {
+            ERR_RPT(("Pa_GetInternalDevice() for output failed.\n"));
+            result = paHostError;  goto done;
+        }
+        if (ALgetparams(padOUT->pad_ALdevice,&pvbuf[4], 4))
+            goto sgiError;
+        if ((past->past_NumOutputChannels > 0) && (pvbuf[5] != sr))
+        {   /* Output needed and rate different from current harware-rate. */
+            if (pvbuf[7] > 0)     /* Means, there's other clients using AL-output-ports */
+            {
+                ERR_RPT(("Sorry, not allowed to switch output-hardware to %ld Hz because \
+                         another process is currently using output at %ld kHz.\n", sr, pvbuf[5]));
+                result = paHostError;   goto done;      /* Will free again the inputbuffer */
+            }                                       /* that was just created above.    */
+            pvbuf[5] = sr;                  /* Then set output-rate. */
+            if (ALsetparams(padOUT->pad_ALdevice, &pvbuf[4], 2))
+                goto sgiError;
+        }
+    }
+    /*------------------------------------------ Construct an audio-port-configuration ----------*/
+    sgiALconfig = ALnewconfig();                    /* Change the SGI-AL-default-settings.       */
+    if (sgiALconfig == (ALconfig)0)                 /* sgiALconfig is released here after use!   */
+        goto sgiError;                              /* See that sgiALconfig is NOT released!     */
+    if (ALsetsampfmt(sgiALconfig, AL_SAMPFMT_TWOSCOMP))  /* Choose paInt16 as native i/o-format. */
+        goto sgiError;
+    if (ALsetwidth (sgiALconfig, AL_SAMPLE_16))     /* Only meaningful when sample format for    */
+        goto sgiError;                              /* config is set to two's complement format. */
+    /************************ Future versions might (dynamically) switch to 32-bit floats? *******
+    if (ALsetsampfmt(sgiALconfig, AL_SAMPFMT_FLOAT))    (Then also call another CallConvert-func.)
+        goto sgiError;
+    if (ALsetfloatmax (sgiALconfig, 1.0))       Only meaningful when sample format for config 
+        goto sgiError;                          is set to AL_SAMPFMT_FLOAT or AL_SAMPFMT_DOUBLE. */
+    /*---------- ?? --------------------*/
+    /* DBUG(("PaHost_OpenStream: pahsc_MinFramesPerHostBuffer = %d\n", pahsc->pahsc_MinFramesPerHostBuffer )); */
+    minNumBuffers = Pa_GetMinNumBuffers(past->past_FramesPerUserBuffer, past->past_SampleRate);
+    past->past_NumUserBuffers = (minNumBuffers > past->past_NumUserBuffers) ?
+                                minNumBuffers : past->past_NumUserBuffers;
+    /*------------------------------------------------ Set internal AL queuesize (in samples) ----*/
+    if (pahsc->pahsc_SamplesPerInputBuffer >= pahsc->pahsc_SamplesPerOutputBuffer)
+        ALqsize = (long)pahsc->pahsc_SamplesPerInputBuffer;
+    else                                            /* Take the largest of the two amounts.       */
+        ALqsize = (long)pahsc->pahsc_SamplesPerOutputBuffer;
+    ALqsize *= 4;                                   /* 4 times as large as amount per transfer!   */
+    if (ALsetqueuesize(sgiALconfig, ALqsize))       /* Or should we use past_NumUserBuffers here? */
+        goto sgiError;                              /* Using 2 times may give glitches... */
+    /* Have to work on ALsetqueuesize() above, fillpoints in the thread are ok now. */
+
+    /* Do ALsetchannels() later, apart per input and/or output. */
+    /*----------------------------------------------- OPEN 1 OR 2 AL-DEVICES: --------------------*/
+    if (past->past_OutputDeviceID == past->past_InputDeviceID)  /* Who SETS these devive-numbers? */
+    {
+        if ((past->past_NumOutputChannels > 0) && (past->past_NumInputChannels > 0))
+        {
+            DBUG(("PaHost_OpenStream: opening both input and output channels.\n"));
+            /*------------------------- open output port: ----------------------------------*/
+            if (ALsetchannels (sgiALconfig, (long)(past->past_NumOutputChannels)))
+                goto sgiError;                      /* Returns 0 on success, -1 on failure. */
+            pahsc->pahsc_ALportOUT = ALopenport("PA sgi out", "w", sgiALconfig);
+            if (pahsc->pahsc_ALportOUT == (ALport)0)
+                goto sgiError;
+            /*------------------------- open input port: -----------------------------------*/
+            if (ALsetchannels (sgiALconfig, (long)(past->past_NumInputChannels)))
+                goto sgiError;                      /* Returns 0 on success, -1 on failure. */
+            pahsc->pahsc_ALportIN = ALopenport("PA sgi in", "r", sgiALconfig);
+            if (pahsc->pahsc_ALportIN == (ALport)0)
+                goto sgiError;          /* For some reason the "patest_wire.c"-test crashes! */
+        }                           /* Probably due to too small buffersizes?....        */
+        else
+        {
+            ERR_RPT(("Cannot setup bidirectional stream between different devices.\n"));
+            result = paHostError;
+            goto done;
+        }
+    }
+    else /* (OutputDeviceID != InputDeviceID) */
+    {
+        if (past->past_NumOutputChannels > 0)   /* WRITE-ONLY: */
+        {
+            /*------------------------- open output port: ----------------------------------*/
+            DBUG(("PaHost_OpenStream: opening %d output channel(s) on %s.\n",
+                  past->past_NumOutputChannels, padOUT->pad_DeviceName));
+            if (ALsetchannels (sgiALconfig, (long)(past->past_NumOutputChannels)))
+                goto sgiError;                      /* Returns 0 on success, -1 on failure. */
+            pahsc->pahsc_ALportOUT = ALopenport("PA sgi out", "w", sgiALconfig);
+            if (pahsc->pahsc_ALportOUT == (ALport)0)
+                goto sgiError;
+        }
+        if (past->past_NumInputChannels > 0)   /* READ-ONLY: */
+        {
+            /*------------------------- open input port: -----------------------------------*/
+            DBUG(("PaHost_OpenStream: opening %d input channel(s) on %s.\n",
+                  past->past_NumInputChannels, padIN->pad_DeviceName));
+            if (ALsetchannels (sgiALconfig, (long)(past->past_NumInputChannels)))
+                goto sgiError;                      /* Returns 0 on success, -1 on failure. */
+            pahsc->pahsc_ALportIN = ALopenport("PA sgi in", "r", sgiALconfig);
+            if (pahsc->pahsc_ALportIN == (ALport)0)
+                goto sgiError;
+        }
+    }
+    DBUG(("PaHost_OpenStream() succeeded.\n"));
+    goto done;  /* (no errors occured) */
+sgiError:
+    result = translateSGIerror();   /* Translates SGI AL-code to PA-code and ERR_RPTs string. */
+done:
+    if (sgiALconfig)
+        ALfreeconfig(sgiALconfig);              /* We don't need that struct anymore. */
+    if (result != paNoError)
+        PaHost_CloseStream(past);               /* Frees memory (only if really allocated!).  */
+    return result;
+}
+
+/*-----------------------------------------------------*/
+PaError PaHost_StartOutput(internalPortAudioStream *past)
+{
+    return paNoError;   /* Hmm, not implemented yet? */
+}
+PaError PaHost_StartInput(internalPortAudioStream *past)
+{
+    return paNoError;
+}
+
+/*------------------------------------------------------------------------------*/
+PaError PaHost_StartEngine(internalPortAudioStream *past)
+{
+    PaHostSoundControl  *pahsc;
+    usptr_t             *arena;
+    if (!past)          /* Test argument. */
+    {
+        ERR_RPT(("PaHost_StartEngine(NULL)!\n"));
+        return paBadStreamPtr;
+    }
+    pahsc = (PaHostSoundControl*)past->past_DeviceData;
+    if (!pahsc)
+    {
+        ERR_RPT(("PaHost_StartEngine(arg): arg->past_DeviceData = NULL!\n"));
+        return paHostError;
+    }
+    past->past_StopSoon = 0;            /* Assume SGI ALport is already opened! */
+    past->past_StopNow  = 0;            /* Why don't we check pahsc for NULL?   */
+    past->past_IsActive = 1;
+
+    /* Although the pthread_create() function, as well as <pthread.h>, may be   */
+    /* available in IRIX, use sproc() on SGI to create audio-background-thread. */
+    /* (Linux/oss uses pthread_create() instead of __clone() because:           */
+    /*  - pthread_create also works for other UNIX systems like Solaris,        */
+    /*  - Java HotSpot VM crashes in pthread_setcanceltype() using __clone().)  */
+
+    usconfig(CONF_ARENATYPE, US_SHAREDONLY);    /* (From SGI-AL-examples, file  */
+    arena = usinit(tmpnam(0));                  /*  motifexample.c, function    */
+    SendSema = usnewpollsema(arena, 0);         /*  InitializeAudioProcess().)  */
+    RcvSema = usnewsema(arena, 1); /* 1= common mutual exclusion semaphore, where 1 and only 1 process
+                                                              will be permitted through a semaphore at a time.  Values > 1
+                                                              imply that up to val resources may be simultaneously used, but requests
+                                                              for more than val resources cause the calling process to block until a
+                                                              resource comes free (by a process holding a resource performing a
+                                                              usvsema().                  IS THIS usnewsema() TOO PLATFORM SPECIFIC? */
+    prctl(PR_SETEXITSIG, 0);/* No not (void*)9, but 0, which doesn't kill the parent! */
+    /* PR_SETEXITSIG controls whether all members of a share group will be
+                     signaled if any one of them leaves the share group
+                     (either via exit() or exec()).  If second
+                     argument, interpreted as an int is 0, then normal IRIX
+                     process termination rules apply, namely that the parent
+                     is sent a SIGCLD upon death of child, but no indication
+                     of death of parent is given.  If the second argument is
+                     a valid signal number then if any member
+                     of a share group leaves the share group, a signal is
+                     sent to ALL surviving members of the share group.  */
+    /* SPAWN AUDIO-CHILD: */
+    pahsc->pahsc_threadPID = sproc(Pa_SgiAudioProcess, /* Returns process ID of */
+                                   PR_SALL,            /* new process, or -1.   */
+                                   (void*)past);       /* Pass past as optional */
+    if (pahsc->pahsc_threadPID == -1)                  /* third void-ptr-arg.   */
+    {
+        ERR_RPT(("PaHost_StartEngine() failed to spawn audio-thread.\n"));
+        sPaHostError = oserror();   /* Pass native error-number to shared area. */
+        return paHostError;         /* But return the generic error-number.     */
+    }
+    return paNoError;               /* Hmmm, errno may come from other threads in same group! */
+}                                   /* ("man sproc" in IRIX6.2 to read about _SGI_MP_SOURCE.) */
+
+/*------------------------------------------------------------------------------*/
+PaError PaHost_StopEngine(internalPortAudioStream *past, int abort)
+{
+    int                 hres;
+    long                timeOut;
+    PaError             result = paNoError;
+    PaHostSoundControl  *pahsc = (PaHostSoundControl*)past->past_DeviceData;
+
+    if (pahsc == NULL)
+        return result;              /* paNoError */
+    past->past_StopSoon = 1;        /* Tell background thread to stop generating   */
+    if (abort)                      /* more and to let current data play out. If   */
+        past->past_StopNow = 1;     /* aborting, tell backgrnd thread to stop NOW! */
+
+    /*---- USE SEMAPHORE LOCK TO COMMUNICATE: -----*/
+    usvsema(SendSema);              /* Increments count associated with SendSema.  */
+    /* Wait for the response.                      */
+    uspsema(RcvSema);               /* Decrements count of previously allocated    */
+    /* semaphore specified by RcvSema.             */
+    /* Must be sure sub-process is really there,... otherwise above semas block?!  */
+    if (pahsc->pahsc_threadPID != -1)   /* Did we really init it to -1 somewhere?  */
+    {
+        /* How to JOIN a "sproc"-thread to recover memory resources????? */
+        /* hres = pthread_join( pahsc->pahsc_ThreadPID, NULL );          */
+        DBUG(("PaHost_StopEngine() is about to KILL(SIGKILL) audio-thread.\n"));
+        if (kill(pahsc->pahsc_threadPID, SIGKILL))  /* Or SIGTERM or SIGQUIT(core)  */
+        {                                       /* Returns -1 in case of error. */
+            result = paHostError;
+            sPaHostError = oserror();   /* Hmmm, other threads may also write here! */
+            ERR_RPT(("PaHost_StopEngine() failed to kill audio-thread.\n"));
+        }
+        else
+            pahsc->pahsc_threadPID = -1;    /* Notify that we've killed this thread. */
+    }
+    past->past_IsActive = 0;    /* Even when kill() failed and pahsc_threadPID still there??? */
+    return result;
+}
+
+/*---------------------------------------------------------------*/
+PaError PaHost_StopOutput(internalPortAudioStream *past, int abort)
+{
+    return paNoError;                   /* Not implemented yet? */
+}
+PaError PaHost_StopInput(internalPortAudioStream *past, int abort )
+{
+    return paNoError;
+}
+
+/*******************************************************************/
+PaError PaHost_CloseStream(internalPortAudioStream *past)
+{
+    PaHostSoundControl  *pahsc;
+    PaError             result = paNoError;
+
+    if (past == NULL)
+        return paBadStreamPtr;
+    pahsc = (PaHostSoundControl *) past->past_DeviceData;
+    if (pahsc == NULL)          /* If pahsc not NULL, past_DeviceData will be freed, and set to NULL. */
+        return result;          /* This test prevents from freeing NULL-pointers. */
+
+    if (pahsc->pahsc_ALportIN)
+    {
+        if (ALcloseport(pahsc->pahsc_ALportIN))
+            result = translateSGIerror();   /* Translates SGI AL-code to PA-code and ERR_RPTs string. */
+        else                                /* But go on anyway... to release other stuff... */
+            pahsc->pahsc_ALportIN = (ALport)0;
+    }
+    if (pahsc->pahsc_ALportOUT)
+    {
+        if (ALcloseport(pahsc->pahsc_ALportOUT))
+            result = translateSGIerror();
+        else
+            pahsc->pahsc_ALportOUT = (ALport)0;
+    }
+    if (pahsc->pahsc_NativeInputBuffer)
+    {
+        free(pahsc->pahsc_NativeInputBuffer);
+        pahsc->pahsc_NativeInputBuffer = NULL;
+    }
+    if (pahsc->pahsc_NativeOutputBuffer)
+    {
+        free(pahsc->pahsc_NativeOutputBuffer);
+        pahsc->pahsc_NativeOutputBuffer = NULL;
+    }
+    PaHost_FreeFastMemory(pahsc, sizeof(PaHostSoundControl));   /* Not just free(pahsc) because */
+    past->past_DeviceData = NULL;   /* PaHost_OpenStream(); allocated FAST (thus mpinned?) MEM. */
+    return result;
+}
+
+/*************************************************************************
+** 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 AUTOEXEC.BAT file and reboot.
+** If the environment variable is not set, then the latency will be 
+** determined based on the OS. Windows NT has higher latency than Win95.
+*/
+#define PA_LATENCY_ENV_NAME  ("PA_MIN_LATENCY_MSEC")
+
+int Pa_GetMinNumBuffers( int framesPerBuffer, double sampleRate )
+{
+    return 2;
+}
+/* Hmmm, the note above isn't appropriate for SGI I'm afraid... */
+/* Do we HAVE to do it this way under IRIX???....               */
+/*--------------------------------------------------------------*/
+
+
+/*---------------------------------------------------------------------*/
+PaError PaHost_Term(void)   /* Frees all of the linked devices.        */
+{                           /* Called by Pa_Terminate() from pa_lib.c. */
+    internalPortAudioDevice *pad = sDeviceList,
+                                   *nxt;
+    while (pad)
+    {
+        DBUG(("PaHost_Term: freeing %s\n", pad->pad_DeviceName));
+        nxt = pad->pad_Next;
+        PaHost_FreeFastMemory(pad, sizeof(internalPortAudioDevice));
+        pad = nxt;              /* PaHost_Init allocated this FAST MEM.*/
+    }
+    sDeviceList = (internalPortAudioDevice*)NULL;
+    return 0;                           /* Got rid of sNumDevices=0; */
+}
+
+/***********************************************************************/
+void Pa_Sleep( long msec )  /* Sleep requested number of milliseconds. */
+{
+#if 0
+    struct timeval timeout;
+    timeout.tv_sec = msec / 1000;
+    timeout.tv_usec = (msec % 1000) * 1000;
+    select(0, NULL, NULL, NULL, &timeout);
+#else
+    long usecs = msec * 1000;
+    usleep( usecs );
+#endif
+}
+
+/*---------------------------------------------------------------------------------------*/
+/* Allocate memory that can be accessed in real-time. This may need to be held in physi- */
+/* cal memory so that it is not paged to virtual memory. This call MUST be balanced with */
+/* a call to PaHost_FreeFastMemory().                                                    */
+void *PaHost_AllocateFastMemory(long numBytes)
+{
+    void *addr = malloc(numBytes);  /* mpin() reads into memory all pages over the given */
+    if (addr)                       /* range and locks the pages into memory. A counter  */
+    {                           /* is incremented each time the page is locked. The  */
+        if (mpin(addr, numBytes))   /* superuser can lock as many pages as it wishes,    */
+        {                       /* others are limited to the configurable PLOCK_MA.  */
+            ERR_RPT(("PaHost_AllocateFastMemory() failed to mpin() memory.\n"));
+#if 1
+            free(addr);             /* You MAY cut out these 2 lines to be less strict,  */
+            addr = NULL;            /* you then only get the warning but PA goes on...   */
+#endif                              /* Only problem then may be corresponding munpin()   */
+        }                       /* call at PaHost_FreeFastMemory(), below.           */
+        memset(addr, 0, numBytes);  /* Locks established with mlock are not inherited by */
+    }                           /* a child process after a fork. Furthermore, IRIX-  */
+    return addr;                    /* man-pages warn against mixing both mpin and mlock */
+}                                   /* in 1 piece of code, so stick to mpin()/munpin() ! */
+
+
+/*---------------------------------------------------------------------------------------*/
+/* Free memory that could be accessed in real-time. This call MUST be balanced with a    */
+/* call to PaHost_AllocateFastMemory().                                                  */
+void PaHost_FreeFastMemory(void *addr, long numBytes)
+{
+    if (addr)
+    {
+        if (munpin(addr, numBytes))     /* Will munpin() fail when it was never mpinned? */
+            ERR_RPT(("WARNING: PaHost_FreeFastMemory() failed to munpin() memory.\n"));
+        free(addr);                     /* But go on, try to release it, just warn...    */
+    }
+}
+
+/*----------------------------------------------------------*/
+PaError PaHost_StreamActive( internalPortAudioStream   *past )
+{
+    PaHostSoundControl *pahsc;
+    if (past == NULL)
+        return paBadStreamPtr;
+    pahsc = (PaHostSoundControl *) past->past_DeviceData;
+    if (pahsc == NULL)
+        return paInternalError;
+    return (PaError)(past->past_IsActive != 0);
+}
+
+/*-------------------------------------------------------------------*/
+PaTimestamp Pa_StreamTime( PortAudioStream *stream )
+{
+    internalPortAudioStream *past = (internalPortAudioStream *) stream;
+    /* FIXME - return actual frames played, not frames generated.
+    ** Need to query the output device somehow.
+    */
+    return past->past_FrameCount;
+}
diff --git a/pa_tests/debug_dual.c b/pa_tests/debug_dual.c
new file mode 100644 (file)
index 0000000..35ba032
--- /dev/null
@@ -0,0 +1,183 @@
+/*
+ * $Id$
+ * debug_dual.c
+ * Try to open TWO streams on separate cards.
+ * Play a sine sweep using the Portable Audio api for several seconds.
+ * Hacked test for debugging PA.
+ *
+ * Author: Phil Burk <philburk@softsynth.com>
+ *
+ * This program uses the PortAudio Portable Audio Library.
+ * For more information see: http://www.portaudio.com
+ * Copyright (c) 1999-2000 Ross Bencina and 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.
+ *
+ */
+#include <stdio.h>
+#include <math.h>
+#include "portaudio.h"
+#define DEV_ID_1            (13)
+#define DEV_ID_2            (15)
+#define NUM_SECONDS         (8)
+#define SLEEP_DUR           (800)
+#define SAMPLE_RATE         (44100)
+#define FRAMES_PER_BUFFER   (256)
+#if 0
+#define MIN_LATENCY_MSEC    (200)
+#define NUM_BUFFERS         ((MIN_LATENCY_MSEC * SAMPLE_RATE) / (FRAMES_PER_BUFFER * 1000))
+#else
+#define NUM_BUFFERS         (0)
+#endif
+#define MIN_FREQ            (100.0f)
+#define MAX_FREQ            (4000.0f)
+#define FREQ_SCALAR         (1.00002f)
+#define CalcPhaseIncrement(freq)  (freq/SAMPLE_RATE)
+#ifndef M_PI
+#define M_PI  (3.14159265)
+#endif
+#define TABLE_SIZE   (400)
+typedef struct
+{
+    float sine[TABLE_SIZE + 1]; // add one for guard point for interpolation
+    float phase_increment;
+    float left_phase;
+    float right_phase;
+}
+paTestData;
+/* Convert phase between and 1.0 to sine value
+ * using linear interpolation.
+ */
+float LookupSine( paTestData *data, float phase );
+float LookupSine( paTestData *data, float phase )
+{
+    float fIndex = phase*TABLE_SIZE;
+    int   index = (int) fIndex;
+    float fract = fIndex - index;
+    float lo = data->sine[index];
+    float hi = data->sine[index+1];
+    float val = lo + fract*(hi-lo);
+    return val;
+}
+/* This routine will be called by the PortAudio engine when audio is needed.
+** It may called at interrupt level on some machines so don't do anything
+** that could mess up the system like calling malloc() or free().
+*/
+static int patestCallback( void *inputBuffer, void *outputBuffer,
+                           unsigned long framesPerBuffer,
+                           PaTimestamp outTime, void *userData )
+{
+    paTestData *data = (paTestData*)userData;
+    float *out = (float*)outputBuffer;
+    unsigned long i;
+    int finished = 0;
+    (void) outTime; /* Prevent unused variable warnings. */
+    (void) inputBuffer;
+
+
+    for( i=0; i<framesPerBuffer; i++ )
+    {
+        *out++ = LookupSine(data, data->left_phase);  /* left */
+        *out++ = LookupSine(data, data->right_phase);  /* right */
+        data->left_phase += data->phase_increment;
+        if( data->left_phase >= 1.0f ) data->left_phase -= 1.0f;
+        data->right_phase += (data->phase_increment * 1.5f); /* fifth above */
+        if( data->right_phase >= 1.0f ) data->right_phase -= 1.0f;
+        /* sweep frequency then start over. */
+        data->phase_increment *= FREQ_SCALAR;
+        if( data->phase_increment > CalcPhaseIncrement(MAX_FREQ) ) data->phase_increment = CalcPhaseIncrement(MIN_FREQ);
+    }
+    return 0;
+}
+
+PaError TestStart( PortAudioStream **streamPtr, PaDeviceID devID,
+                   paTestData *data );
+/*******************************************************************/
+int main(void);
+int main(void)
+{
+    PortAudioStream *stream1, *stream2;
+    PaError err;
+    paTestData DATA1, DATA2;
+    printf("PortAudio Test: DUAL sine sweep. ask for %d buffers\n", NUM_BUFFERS );
+    err = Pa_Initialize();
+    if( err != paNoError ) goto error;
+    err = TestStart( &stream1, DEV_ID_1, &DATA1 );
+    if( err != paNoError ) goto error;
+    err = TestStart( &stream2, DEV_ID_2, &DATA2 );
+    if( err != paNoError ) goto error;
+    printf("Hit ENTER\n");
+    getchar();
+    err = Pa_StopStream( stream1 );
+    if( err != paNoError ) goto error;
+    err = Pa_StopStream( stream2 );
+    if( err != paNoError ) goto error;
+    Pa_Terminate();
+    printf("Test finished.\n");
+    return err;
+error:
+    Pa_Terminate();
+    fprintf( stderr, "An error occured while using the portaudio stream\n" );
+    fprintf( stderr, "Error number: %d\n", err );
+    fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
+    return err;
+}
+PaError TestStart( PortAudioStream **streamPtr, PaDeviceID devID, paTestData *data )
+{
+    PortAudioStream *stream;
+    PaError err;
+    int i;
+    /* initialise sinusoidal wavetable */
+    for( i=0; i<TABLE_SIZE; i++ )
+    {
+        data->sine[i] = (float) sin( ((double)i/(double)TABLE_SIZE) * M_PI * 2. );
+    }
+    data->sine[TABLE_SIZE] = data->sine[0]; // set guard point
+    data->left_phase = data->right_phase = 0.0;
+    data->phase_increment = CalcPhaseIncrement(MIN_FREQ);
+    printf("PortAudio Test: output device = %d\n", devID );
+    err = Pa_OpenStream(
+              &stream,
+              paNoDevice,
+              0,              /* no input */
+              paFloat32,  /* 32 bit floating point input */
+              NULL,
+              devID,
+              2,              /* stereo output */
+              paFloat32,      /* 32 bit floating point output */
+              NULL,
+              SAMPLE_RATE,
+              FRAMES_PER_BUFFER,
+              NUM_BUFFERS,    /* number of buffers, if zero then use default minimum */
+              paClipOff|paDitherOff, /* we won't output out of range samples so don't bother clipping them */
+              patestCallback,
+              data );
+    if( err != paNoError ) goto error;
+    err = Pa_StartStream( stream );
+    if( err != paNoError ) goto error;
+    *streamPtr = stream;
+    return 0;
+error:
+    return err;
+}
diff --git a/pa_tests/debug_multi_in.c b/pa_tests/debug_multi_in.c
new file mode 100644 (file)
index 0000000..def729c
--- /dev/null
@@ -0,0 +1,179 @@
+/*
+ * $Id$
+ * debug_multi_in.c
+ * Pass output from each of multiple channels
+ * to a stereo output using the Portable Audio api.
+ * Hacked test for debugging PA.
+ *
+ * Author: Phil Burk  http://www.softsynth.com
+ *
+ * This program uses the PortAudio Portable Audio Library.
+ * For more information see: http://www.portaudio.com
+ * Copyright (c) 1999-2000 Ross Bencina and 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.
+ *
+ */
+#include <stdio.h>
+#include <math.h>
+#include <string.h>
+#include "portaudio.h"
+//#define INPUT_DEVICE_NAME   ("EWS88 MT Interleaved Rec")
+#define OUTPUT_DEVICE       (Pa_GetDefaultOutputDeviceID())
+//#define OUTPUT_DEVICE       (18)
+#define SAMPLE_RATE         (22050)
+#define FRAMES_PER_BUFFER   (256)
+#define MIN_LATENCY_MSEC    (400)
+#define NUM_BUFFERS         ((MIN_LATENCY_MSEC * SAMPLE_RATE) / (FRAMES_PER_BUFFER * 1000))
+#ifndef M_PI
+#define M_PI  (3.14159265)
+#endif
+typedef struct
+{
+    int      liveChannel;
+    int      numChannels;
+}
+paTestData;
+/* This routine will be called by the PortAudio engine when audio is needed.
+** It may called at interrupt level on some machines so don't do anything
+** that could mess up the system like calling malloc() or free().
+*/
+static int patestCallback( void *inputBuffer, void *outputBuffer,
+                           unsigned long framesPerBuffer,
+                           PaTimestamp outTime, void *userData )
+{
+    paTestData *data = (paTestData*)userData;
+    float *out = (float*)outputBuffer;
+    float *in = (float*)inputBuffer;
+    int i;
+    int finished = 0;
+    (void) outTime; /* Prevent unused variable warnings. */
+    (void) inputBuffer;
+
+    if( in == NULL ) return 0;
+    for( i=0; i<(int)framesPerBuffer; i++ )
+    {
+        /* Copy one channel of input to output. */
+        *out++ = in[data->liveChannel];
+        *out++ = in[data->liveChannel];
+        in += data->numChannels;
+    }
+    return 0;
+}
+/*******************************************************************/
+int PaFindDeviceByName( const char *name )
+{
+    int   i;
+    int   numDevices;
+    const PaDeviceInfo *pdi;
+    int   len = strlen( name );
+    PaDeviceID   result = paNoDevice;
+    numDevices = Pa_CountDevices();
+    for( i=0; i<numDevices; i++ )
+    {
+        pdi = Pa_GetDeviceInfo( i );
+        if( strncmp( name, pdi->name, len ) == 0 )
+        {
+            result = i;
+            break;
+        }
+    }
+    return result;
+}
+/*******************************************************************/
+int main(void);
+int main(void)
+{
+    PortAudioStream *stream;
+    PaError err;
+    paTestData data;
+    int i;
+    PaDeviceID inputDevice;
+    const PaDeviceInfo *pdi;
+    printf("PortAudio Test: input signal from each channel. %d buffers\n", NUM_BUFFERS );
+    data.liveChannel = 0;
+    err = Pa_Initialize();
+    if( err != paNoError ) goto error;
+#ifdef INPUT_DEVICE_NAME
+    printf("Try to use device: %s\n", INPUT_DEVICE_NAME );
+    inputDevice = PaFindDeviceByName(INPUT_DEVICE_NAME);
+    if( inputDevice == paNoDevice )
+    {
+        printf("Could not find %s. Using default instead.\n", INPUT_DEVICE_NAME );
+        inputDevice = Pa_GetDefaultInputDeviceID();
+    }
+#else
+    printf("Using default input device.\n");
+    inputDevice = Pa_GetDefaultInputDeviceID();
+#endif
+    pdi = Pa_GetDeviceInfo( inputDevice );
+    if( pdi == NULL )
+    {
+        printf("Could not get device info!\n");
+        goto error;
+    }
+    data.numChannels = pdi->maxInputChannels;
+    printf("Input Device name is %s\n", pdi->name );
+    printf("Input Device has %d channels.\n", pdi->maxInputChannels);
+    err = Pa_OpenStream(
+              &stream,
+              inputDevice,
+              pdi->maxInputChannels,
+              paFloat32,  /* 32 bit floating point input */
+              NULL,
+              OUTPUT_DEVICE,
+              2,
+              paFloat32,  /* 32 bit floating point output */
+              NULL,
+              SAMPLE_RATE,
+              FRAMES_PER_BUFFER,  /* frames per buffer */
+              NUM_BUFFERS,    /* number of buffers, if zero then use default minimum */
+              paClipOff,      /* we won't output out of range samples so don't bother clipping them */
+              patestCallback,
+              &data );
+    if( err != paNoError ) goto error;
+    data.liveChannel = 0;
+    err = Pa_StartStream( stream );
+    if( err != paNoError ) goto error;
+    for( i=0; i<data.numChannels; i++ )
+    {
+        data.liveChannel = i;
+        printf("Channel %d being sent to output. Hit ENTER for next channel.", i );
+        fflush(stdout);
+        getchar();
+    }
+    err = Pa_StopStream( stream );
+    if( err != paNoError ) goto error;
+
+    err = Pa_CloseStream( stream );
+    Pa_Terminate();
+    printf("Test finished.\n");
+    return err;
+error:
+    Pa_Terminate();
+    fprintf( stderr, "An error occured while using the portaudio stream\n" );
+    fprintf( stderr, "Error number: %d\n", err );
+    fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
+    return err;
+}
diff --git a/pa_tests/debug_multi_out.c b/pa_tests/debug_multi_out.c
new file mode 100644 (file)
index 0000000..4817a11
--- /dev/null
@@ -0,0 +1,165 @@
+/*
+ * $Id$
+ * debug_multi.c
+ * Play a sine wave on each of multiple channels,
+ * using the Portable Audio api.
+ * Hacked test for debugging PA.
+ *
+ * Author: Phil Burk  http://www.softsynth.com
+ *
+ * This program uses the PortAudio Portable Audio Library.
+ * For more information see: http://www.portaudio.com
+ * Copyright (c) 1999-2000 Ross Bencina and 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.
+ *
+ */
+#include <stdio.h>
+#include <math.h>
+#include "portaudio.h"
+#define NUM_CHANNELS        (8)
+// #define OUTPUT_DEVICE       (Pa_GetDefaultOutputDeviceID())
+#define OUTPUT_DEVICE       (18)
+#define NUM_SECONDS         (NUM_CHANNELS*4)
+#define SAMPLE_RATE         (44100)
+#define FRAMES_PER_CHANNEL  (SAMPLE_RATE/2)
+#define FRAMES_PER_BUFFER   (256)
+#define MIN_LATENCY_MSEC    (400)
+#define NUM_BUFFERS         ((MIN_LATENCY_MSEC * SAMPLE_RATE) / (FRAMES_PER_BUFFER * 1000))
+#ifndef M_PI
+#define M_PI  (3.14159265)
+#endif
+#define TABLE_SIZE   (800)
+typedef struct
+{
+    float sine[TABLE_SIZE];
+    int   phase;
+    int   liveChannel;
+    int   count;
+    unsigned int sampsToGo;
+}
+paTestData;
+/* This routine will be called by the PortAudio engine when audio is needed.
+** It may called at interrupt level on some machines so don't do anything
+** that could mess up the system like calling malloc() or free().
+*/
+static int patestCallback( void *inputBuffer, void *outputBuffer,
+                           unsigned long framesPerBuffer,
+                           PaTimestamp outTime, void *userData )
+{
+    paTestData *data = (paTestData*)userData;
+    float *out = (float*)outputBuffer;
+    int i, j;
+    int finished = 0;
+    (void) outTime; /* Prevent unused variable warnings. */
+    (void) inputBuffer;
+
+    if( data->sampsToGo < framesPerBuffer )
+    {
+        finished = 1;
+    }
+    else
+    {
+        for( i=0; i<(int)framesPerBuffer; i++ )
+        {
+            for( j=0; j<NUM_CHANNELS; j++ )
+            {
+                /* Output sine wave only on live channel. */
+                *out++ = (j==data->liveChannel) ? data->sine[data->phase] : 0.0f;
+                /* Play each channel at a higher frequency. */
+                data->phase += 1 + data->liveChannel;
+                if( data->phase >= TABLE_SIZE ) data->phase -= TABLE_SIZE;
+            }
+            /* Switch channels every so often. */
+            if( --data->count <= 0 )
+            {
+                data->count = FRAMES_PER_CHANNEL;
+                data->liveChannel += 1;
+                if( data->liveChannel >= NUM_CHANNELS ) data->liveChannel = 0;
+            }
+        }
+        data->sampsToGo -= framesPerBuffer;
+    }
+    return finished;
+}
+/*******************************************************************/
+int main(void);
+int main(void)
+{
+    PortAudioStream *stream;
+    PaError err;
+    paTestData data;
+    int i;
+    int totalSamps;
+    printf("PortAudio Test: output sine wave. %d buffers\n", NUM_BUFFERS );
+    /* initialise sinusoidal wavetable */
+    for( i=0; i<TABLE_SIZE; i++ )
+    {
+        data.sine[i] = (float) sin( ((double)i/(double)TABLE_SIZE) * M_PI * 2. );
+    }
+    data.phase = 0;
+    data.count = FRAMES_PER_CHANNEL;
+    data.liveChannel = 0;
+    data.sampsToGo = totalSamps =  NUM_SECONDS * SAMPLE_RATE; /* Play for a few seconds. */
+    err = Pa_Initialize();
+    if( err != paNoError ) goto error;
+    err = Pa_OpenStream(
+              &stream,
+              paNoDevice, /* default input device */
+              0,              /* no input */
+              paFloat32,  /* 32 bit floating point input */
+              NULL,
+              OUTPUT_DEVICE,
+              NUM_CHANNELS,
+              paFloat32,  /* 32 bit floating point output */
+              NULL,
+              SAMPLE_RATE,
+              FRAMES_PER_BUFFER,  /* frames per buffer */
+              NUM_BUFFERS,    /* number of buffers, if zero then use default minimum */
+              paClipOff,      /* we won't output out of range samples so don't bother clipping them */
+              patestCallback,
+              &data );
+    if( err != paNoError ) goto error;
+    err = Pa_StartStream( stream );
+    if( err != paNoError ) goto error;
+    printf("Is callback being called?\n");
+    for( i=0; i<NUM_SECONDS; i++ )
+    {
+        printf("data.sampsToGo = %d\n", data.sampsToGo );
+        Pa_Sleep( 1000 );
+    }
+    /* Stop sound until ENTER hit. */
+    err = Pa_StopStream( stream );
+    if( err != paNoError ) goto error;
+    Pa_CloseStream( stream );
+    Pa_Terminate();
+    printf("Test finished.\n");
+    return err;
+error:
+    Pa_Terminate();
+    fprintf( stderr, "An error occured while using the portaudio stream\n" );
+    fprintf( stderr, "Error number: %d\n", err );
+    fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
+    return err;
+}
diff --git a/pa_tests/debug_record.c b/pa_tests/debug_record.c
new file mode 100644 (file)
index 0000000..fd4118e
--- /dev/null
@@ -0,0 +1,297 @@
+/*
+ * $Id$
+ * debug_record.c
+ * Record input into an array.
+ * Save array to a file.
+ * Based on patest_record.c but with various ugly debug hacks thrown in.
+ *
+ * Author: Phil Burk  http://www.softsynth.com
+ *
+ * This program uses the PortAudio Portable Audio Library.
+ * For more information see: http://www.portaudio.com
+ * Copyright (c) 1999-2000 Ross Bencina and 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.
+ *
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <memory.h>
+#include "portaudio.h"
+#define SAMPLE_RATE     (22050)
+#define NUM_SECONDS     (10)
+#define SLEEP_DUR_MSEC  (200)
+#define REC_BUF_FRAMES  (1<<10)
+#define NUM_REC_BUFS    (0)
+#if 0
+#define PA_SAMPLE_TYPE  paFloat32
+typedef float SAMPLE;
+#else
+#define PA_SAMPLE_TYPE  paInt16
+typedef short SAMPLE;
+#endif
+typedef struct
+{
+    long         frameIndex;  /* Index into sample array. */
+    long         maxFrameIndex;
+    long         samplesPerFrame;
+    SAMPLE      *recordedSamples;
+}
+paTestData;
+/* This routine will be called by the PortAudio engine when audio is needed.
+** It may be called at interrupt level on some machines so don't do anything
+** that could mess up the system like calling malloc() or free().
+*/
+static int recordCallback( void *inputBuffer, void *outputBuffer,
+                           unsigned long framesPerBuffer,
+                           PaTimestamp outTime, void *userData )
+{
+    paTestData *data = (paTestData*)userData;
+    SAMPLE *rptr = (SAMPLE*)inputBuffer;
+    SAMPLE *wptr = &data->recordedSamples[data->frameIndex * data->samplesPerFrame];
+    long framesToCalc;
+    unsigned long i;
+    int finished;
+    unsigned long framesLeft = data->maxFrameIndex - data->frameIndex;
+
+    (void) outputBuffer; /* Prevent unused variable warnings. */
+    (void) outTime;
+
+    if( framesLeft < framesPerBuffer )
+    {
+        framesToCalc = framesLeft;
+        finished = 1;
+    }
+    else
+    {
+        framesToCalc = framesPerBuffer;
+        finished = 0;
+    }
+    if( inputBuffer == NULL )
+    {
+        for( i=0; i<framesToCalc; i++ )
+        {
+            *wptr++ = 0;  /* left */
+            *wptr++ = 0;  /* right */
+        }
+    }
+    else
+    {
+        for( i=0; i<framesToCalc; i++ )
+        {
+            *wptr++ = *rptr++;  /* left */
+            *wptr++ = *rptr++;  /* right */
+        }
+    }
+    data->frameIndex += framesToCalc;
+    return finished;
+}
+/* This routine will be called by the PortAudio engine when audio is needed.
+** It may be called at interrupt level on some machines so don't do anything
+** that could mess up the system like calling malloc() or free().
+*/
+static int playCallback( void *inputBuffer, void *outputBuffer,
+                         unsigned long framesPerBuffer,
+                         PaTimestamp outTime, void *userData )
+{
+    paTestData *data = (paTestData*)userData;
+    SAMPLE *rptr = &data->recordedSamples[data->frameIndex * data->samplesPerFrame];
+    SAMPLE *wptr = (SAMPLE*)outputBuffer;
+    unsigned long i;
+    int finished;
+    unsigned int framesLeft = data->maxFrameIndex - data->frameIndex;
+    if( outputBuffer == NULL ) return 0;
+    (void) inputBuffer; /* Prevent unused variable warnings. */
+    (void) outTime;
+
+    if( framesLeft < framesPerBuffer )
+    {
+        /* final buffer... */
+        for( i=0; i<framesLeft; i++ )
+        {
+            *wptr++ = *rptr++;  /* left */
+            *wptr++ = *rptr++;  /* right */
+        }
+        for( ; i<framesPerBuffer; i++ )
+        {
+            *wptr++ = 0;  /* left */
+            *wptr++ = 0;  /* right */
+        }
+        data->frameIndex += framesLeft;
+        finished = 1;
+    }
+    else
+    {
+        for( i=0; i<framesPerBuffer; i++ )
+        {
+            *wptr++ = *rptr++;  /* left */
+            *wptr++ = *rptr++;  /* right */
+        }
+        data->frameIndex += framesPerBuffer;
+        finished = 0;
+    }
+    return finished;
+}
+/*******************************************************************/
+int main(void);
+int main(void)
+{
+    PortAudioStream *stream;
+    PaError    err;
+    paTestData data;
+    long       totalFrames;
+    long       numSamples;
+    long       numBytes;
+    long       i;
+    printf("patest_record.c\n"); fflush(stdout);
+
+    data.frameIndex = 0;
+    data.samplesPerFrame = 2;
+    data.maxFrameIndex = totalFrames = NUM_SECONDS*SAMPLE_RATE;
+
+    printf("totalFrames = %d\n", totalFrames ); fflush(stdout);
+    numSamples = totalFrames * data.samplesPerFrame;
+    numBytes = numSamples * sizeof(SAMPLE);
+    data.recordedSamples = (SAMPLE *) malloc( numBytes );
+    if( data.recordedSamples == NULL )
+    {
+        printf("Could not allocate record array.\n");
+        exit(1);
+    }
+    for( i=0; i<numSamples; i++ ) data.recordedSamples[i] = 0;
+    err = Pa_Initialize();
+    if( err != paNoError ) goto error;
+
+    /* Record some audio. */
+    err = Pa_OpenStream(
+              &stream,
+              Pa_GetDefaultInputDeviceID(),
+              data.samplesPerFrame,               /* stereo input */
+              PA_SAMPLE_TYPE,
+              NULL,
+              paNoDevice,
+              0,
+              PA_SAMPLE_TYPE,
+              NULL,
+              SAMPLE_RATE,
+              REC_BUF_FRAMES,            /* frames per buffer */
+              NUM_REC_BUFS,               /* number of buffers, if zero then use default minimum */
+              paClipOff,       /* we won't output out of range samples so don't bother clipping them */
+              recordCallback,
+              &data );
+    if( err != paNoError ) goto error;
+    err = Pa_StartStream( stream );
+    if( err != paNoError ) goto error;
+    printf("Now recording!\n"); fflush(stdout);
+    for( i=0; i<(NUM_SECONDS*1000/SLEEP_DUR_MSEC); i++ )
+    {
+        if( Pa_StreamActive( stream ) <= 0)
+        {
+            printf("Stream inactive!\n");
+            break;
+        }
+        if( data.maxFrameIndex <= data.frameIndex )
+        {
+            printf("Buffer recording complete.\n");
+            break;
+        }
+        Pa_Sleep(100);
+        printf("index = %d\n", data.frameIndex );
+    }
+    err = Pa_CloseStream( stream );
+    if( err != paNoError ) goto error;
+    /* Playback recorded data. */
+    data.frameIndex = 0;
+    printf("Begin playback.\n"); fflush(stdout);
+    err = Pa_OpenStream(
+              &stream,
+              paNoDevice,
+              0,               /* NO input */
+              PA_SAMPLE_TYPE,
+              NULL,
+              Pa_GetDefaultOutputDeviceID(),
+              data.samplesPerFrame,               /* stereo output */
+              PA_SAMPLE_TYPE,
+              NULL,
+              SAMPLE_RATE,
+              1024,            /* frames per buffer */
+              0,               /* number of buffers, if zero then use default minimum */
+              paClipOff,       /* we won't output out of range samples so don't bother clipping them */
+              playCallback,
+              &data );
+    if( err != paNoError ) goto error;
+    err = Pa_StartStream( stream );
+    if( err != paNoError ) goto error;
+    printf("Waiting for playback to finish.\n"); fflush(stdout);
+    for( i=0; i<(NUM_SECONDS*1000/SLEEP_DUR_MSEC); i++ )
+    {
+        Pa_Sleep(100);
+        printf("index = %d\n", data.frameIndex );
+    }
+    err = Pa_CloseStream( stream );
+    if( err != paNoError ) goto error;
+    printf("Done.\n"); fflush(stdout);
+    {
+        SAMPLE max = 0;
+        SAMPLE posVal;
+        int i;
+        for( i=0; i<numSamples; i++ )
+        {
+            posVal = data.recordedSamples[i];
+            if( posVal < 0 ) posVal = -posVal;
+            if( posVal > max ) max = posVal;
+        }
+        printf("Largest recorded sample = %d\n", max );
+    }
+    /* Write recorded data to a file. */
+#if 0
+    {
+        FILE  *fid;
+        fid = fopen("recorded.raw", "wb");
+        if( fid == NULL )
+        {
+            printf("Could not open file.");
+        }
+        else
+        {
+            fwrite( data.recordedSamples, data.samplesPerFrame * sizeof(SAMPLE), totalFrames, fid );
+            fclose( fid );
+            printf("Wrote data to 'recorded.raw'\n");
+        }
+    }
+#endif
+    free( data.recordedSamples );
+    Pa_Terminate();
+    return 0;
+error:
+    Pa_Terminate();
+    fprintf( stderr, "An error occured while using the portaudio stream\n" );
+    fprintf( stderr, "Error number: %d\n", err );
+    fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
+    if( err == paHostError )
+    {
+        fprintf( stderr, "Host Error number: %d\n", Pa_GetHostError() );
+    }
+    return -1;
+}
diff --git a/pa_tests/debug_sine.c b/pa_tests/debug_sine.c
new file mode 100644 (file)
index 0000000..b79405d
--- /dev/null
@@ -0,0 +1,192 @@
+/*
+ * $Id$
+ * debug_sine.c
+ * Play a sine sweep using the Portable Audio api for several seconds.
+ * Hacked test for debugging PA.
+ *
+ * Author: Phil Burk <philburk@softsynth.com>
+ *
+ * This program uses the PortAudio Portable Audio Library.
+ * For more information see: http://www.portaudio.com
+ * Copyright (c) 1999-2000 Ross Bencina and 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.
+ *
+ */
+#include <stdio.h>
+#include <math.h>
+#include "portaudio.h"
+#define OUTPUT_DEVICE       (Pa_GetDefaultOutputDeviceID())
+//#define OUTPUT_DEVICE       (11)
+#define NUM_SECONDS         (8)
+#define SLEEP_DUR           (800)
+#define SAMPLE_RATE         (44100)
+#define FRAMES_PER_BUFFER   (256)
+#if 1
+#define MIN_LATENCY_MSEC    (200)
+#define NUM_BUFFERS         ((MIN_LATENCY_MSEC * SAMPLE_RATE) / (FRAMES_PER_BUFFER * 1000))
+#else
+#define NUM_BUFFERS         (0)
+#endif
+#define MIN_FREQ            (100.0f)
+#define MAX_FREQ            (4000.0f)
+#define FREQ_SCALAR         (1.00002f)
+#define CalcPhaseIncrement(freq)  (freq/SAMPLE_RATE)
+#ifndef M_PI
+#define M_PI  (3.14159265)
+#endif
+#define TABLE_SIZE   (400)
+typedef struct
+{
+    float sine[TABLE_SIZE + 1]; // add one for guard point for interpolation
+    float phase_increment;
+    float left_phase;
+    float right_phase;
+    unsigned int framesToGo;
+}
+paTestData;
+/* Convert phase between and 1.0 to sine value
+ * using linear interpolation.
+ */
+float LookupSine( paTestData *data, float phase );
+float LookupSine( paTestData *data, float phase )
+{
+    float fIndex = phase*TABLE_SIZE;
+    int   index = (int) fIndex;
+    float fract = fIndex - index;
+    float lo = data->sine[index];
+    float hi = data->sine[index+1];
+    float val = lo + fract*(hi-lo);
+    return val;
+}
+/* This routine will be called by the PortAudio engine when audio is needed.
+** It may called at interrupt level on some machines so don't do anything
+** that could mess up the system like calling malloc() or free().
+*/
+static int patestCallback( void *inputBuffer, void *outputBuffer,
+                           unsigned long framesPerBuffer,
+                           PaTimestamp outTime, void *userData )
+{
+    paTestData *data = (paTestData*)userData;
+    float *out = (float*)outputBuffer;
+    int      framesToCalc;
+    int i;
+    int finished = 0;
+    (void) outTime; /* Prevent unused variable warnings. */
+    (void) inputBuffer;
+
+    if( data->framesToGo < framesPerBuffer )
+    {
+        framesToCalc = data->framesToGo;
+        data->framesToGo = 0;
+        finished = 1;
+    }
+    else
+    {
+        framesToCalc = framesPerBuffer;
+        data->framesToGo -= framesPerBuffer;
+    }
+
+    for( i=0; i<framesToCalc; i++ )
+    {
+        *out++ = LookupSine(data, data->left_phase);  /* left */
+        *out++ = LookupSine(data, data->right_phase);  /* right */
+        data->left_phase += data->phase_increment;
+        if( data->left_phase >= 1.0f ) data->left_phase -= 1.0f;
+        data->right_phase += (data->phase_increment * 1.5f); /* fifth above */
+        if( data->right_phase >= 1.0f ) data->right_phase -= 1.0f;
+        /* sweep frequency then start over. */
+        data->phase_increment *= FREQ_SCALAR;
+        if( data->phase_increment > CalcPhaseIncrement(MAX_FREQ) ) data->phase_increment = CalcPhaseIncrement(MIN_FREQ);
+    }
+    /* zero remainder of final buffer */
+    for( ; i<(int)framesPerBuffer; i++ )
+    {
+        *out++ = 0; /* left */
+        *out++ = 0; /* right */
+    }
+    return finished;
+}
+/*******************************************************************/
+int main(void);
+int main(void)
+{
+    PortAudioStream *stream;
+    PaError err;
+    paTestData data;
+    int i;
+    int totalSamps;
+    printf("PortAudio Test: output sine sweep. ask for %d buffers\n", NUM_BUFFERS );
+    /* initialise sinusoidal wavetable */
+    for( i=0; i<TABLE_SIZE; i++ )
+    {
+        data.sine[i] = (float) sin( ((double)i/(double)TABLE_SIZE) * M_PI * 2. );
+    }
+    data.sine[TABLE_SIZE] = data.sine[0]; // set guard point
+    data.left_phase = data.right_phase = 0.0;
+    data.phase_increment = CalcPhaseIncrement(MIN_FREQ);
+    data.framesToGo = totalSamps =  NUM_SECONDS * SAMPLE_RATE; /* Play for a few seconds. */
+    printf("totalSamps = %d\n", totalSamps );
+    err = Pa_Initialize();
+    if( err != paNoError ) goto error;
+    printf("PortAudio Test: output device = %d\n", OUTPUT_DEVICE );
+    err = Pa_OpenStream(
+              &stream,
+              paNoDevice,
+              0,              /* no input */
+              paFloat32,  /* 32 bit floating point input */
+              NULL,
+              OUTPUT_DEVICE,
+              2,              /* stereo output */
+              paFloat32,      /* 32 bit floating point output */
+              NULL,
+              SAMPLE_RATE,
+              FRAMES_PER_BUFFER,
+              NUM_BUFFERS,    /* number of buffers, if zero then use default minimum */
+              paClipOff|paDitherOff, /* we won't output out of range samples so don't bother clipping them */
+              patestCallback,
+              &data );
+    if( err != paNoError ) goto error;
+    err = Pa_StartStream( stream );
+    if( err != paNoError ) goto error;
+    printf("Is callback being called?\n");
+    for( i=0; i<((NUM_SECONDS+1)*1000); i+=SLEEP_DUR )
+    {
+        printf("data.framesToGo = %d\n", data.framesToGo ); fflush(stdout);
+        Pa_Sleep( SLEEP_DUR );
+    }
+    /* Stop sound until ENTER hit. */
+    printf("Call Pa_StopStream()\n");
+    err = Pa_StopStream( stream );
+    if( err != paNoError ) goto error;
+    Pa_Terminate();
+    printf("Test finished.\n");
+    return err;
+error:
+    Pa_Terminate();
+    fprintf( stderr, "An error occured while using the portaudio stream\n" );
+    fprintf( stderr, "Error number: %d\n", err );
+    fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
+    return err;
+}
diff --git a/pa_tests/debug_sine_wait.c b/pa_tests/debug_sine_wait.c
new file mode 100644 (file)
index 0000000..863ae77
--- /dev/null
@@ -0,0 +1,138 @@
+/*
+ * $Id$
+ * debug_sine_wait.c
+ * Play a sine wave using the Portable Audio api until we hit ENTER.
+ *
+ * Authors:
+ *    Ross Bencina <rossb@audiomulch.com>
+ *    Phil Burk <philburk@softsynth.com>
+ *
+ * This program uses the PortAudio Portable Audio Library.
+ * For more information see: http://www.audiomulch.com/portaudio/
+ * Copyright (c) 1999-2000 Ross Bencina and 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.
+ *
+ */
+#include <stdio.h>
+#include <math.h>
+#include "portaudio.h"
+
+#define NUM_SECONDS   (20)
+#define SAMPLE_RATE   (44100)
+#define FRAMES_PER_BUFFER  (64)
+#define NUM_BUFFERS   (16)
+
+#ifndef M_PI
+#define M_PI  (3.14159265)
+#endif
+
+#define TABLE_SIZE   (200)
+typedef struct
+{
+    float sine[TABLE_SIZE];
+    int left_phase;
+    int right_phase;
+}
+paTestData;
+
+/* This routine will be called by the PortAudio engine when audio is needed.
+** It may called at interrupt level on some machines so don't do anything
+** that could mess up the system like calling malloc() or free().
+*/
+static int patestCallback(   void *inputBuffer, void *outputBuffer,
+                             unsigned long framesPerBuffer,
+                             PaTimestamp outTime, void *userData )
+{
+    paTestData *data = (paTestData*)userData;
+    float *out = (float*)outputBuffer;
+    unsigned long i;
+    int finished = 0;
+    (void) outTime; /* Prevent unused variable warnings. */
+    (void) inputBuffer;
+    for( i=0; i<framesPerBuffer; i++ )
+    {
+        *out++ = data->sine[data->left_phase];  /* left */
+        *out++ = data->sine[data->right_phase];  /* right */
+        data->left_phase += 1;
+        if( data->left_phase >= TABLE_SIZE ) data->left_phase -= TABLE_SIZE;
+        data->right_phase += 3; /* higher pitch so we can distinguish left and right. */
+        if( data->right_phase >= TABLE_SIZE ) data->right_phase -= TABLE_SIZE;
+    }
+    return finished;
+}
+
+/*******************************************************************/
+int main(void);
+int main(void)
+{
+    PortAudioStream *stream;
+    PaError err;
+    paTestData data;
+    int i;
+    printf("PortAudio Test: output sine wave. SR = %d, BufSize = %d\n", SAMPLE_RATE, FRAMES_PER_BUFFER);
+    /* initialise sinusoidal wavetable */
+    for( i=0; i<TABLE_SIZE; i++ )
+    {
+        data.sine[i] = (float) sin( ((double)i/(double)TABLE_SIZE) * M_PI * 2. );
+    }
+    data.left_phase = data.right_phase = 0;
+    err = Pa_Initialize();
+    if( err != paNoError ) goto error;
+    err = Pa_OpenStream(
+              &stream,
+              paNoDevice,/* default input device */
+              0,              /* no input */
+              paFloat32,  /* 32 bit floating point input */
+              NULL,
+              Pa_GetDefaultOutputDeviceID(), /* default output device */
+              2,          /* stereo output */
+              paFloat32,      /* 32 bit floating point output */
+              NULL,
+              SAMPLE_RATE,
+              FRAMES_PER_BUFFER,
+              NUM_BUFFERS,    /* number of buffers, if zero then use default minimum */
+              paClipOff,      /* we won't output out of range samples so don't bother clipping them */
+              patestCallback,
+              &data );
+    if( err != paNoError ) goto error;
+    err = Pa_StartStream( stream );
+    if( err != paNoError ) goto error;
+    printf("Hit ENTER to stop.\n");
+    gets(stdin);
+
+    err = Pa_StopStream( stream );
+    if( err != paNoError ) goto error;
+    err = Pa_CloseStream( stream );
+    if( err != paNoError ) goto error;
+    Pa_Terminate();
+    printf("Test finished.\n");
+    return err;
+error:
+    Pa_Terminate();
+    fprintf( stderr, "An error occured while using the portaudio stream\n" );
+    fprintf( stderr, "Error number: %d\n", err );
+    fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
+    return err;
+}
diff --git a/pa_tests/debug_test1.c b/pa_tests/debug_test1.c
new file mode 100644 (file)
index 0000000..1321f62
--- /dev/null
@@ -0,0 +1,114 @@
+/*
+ * $Id$
+ patest1.c
+ Ring modulate the audio input with a 441hz sine wave for 20 seconds
+    using the Portable Audio api
+    Author: Ross Bencina <rossb@audiomulch.com>
+    Modifications:
+ April 5th, 2001 - PLB - Check for NULL inputBuffer.
+*/
+#include <stdio.h>
+#include <math.h>
+#include "portaudio.h"
+#ifndef M_PI
+#define M_PI  (3.14159265)
+#endif
+typedef struct
+{
+    float sine[100];
+    int phase;
+    int sampsToGo;
+}
+patest1data;
+static int patest1Callback( void *inputBuffer, void *outputBuffer,
+                            unsigned long bufferFrames,
+                            PaTimestamp outTime, void *userData )
+{
+    patest1data *data = (patest1data*)userData;
+    float *in = (float*)inputBuffer;
+    float *out = (float*)outputBuffer;
+    int framesToCalc = bufferFrames;
+    unsigned long i;
+    int finished = 0;
+    if(inputBuffer == NULL) return 0;
+    if( data->sampsToGo < bufferFrames )
+    {
+        finished = 1;
+    }
+    for( i=0; i<bufferFrames; i++ )
+    {
+        *out++ = *in++;
+        *out++ = *in++;
+        if( data->phase >= 100 )
+            data->phase = 0;
+    }
+    data->sampsToGo -= bufferFrames;
+    /* zero remainder of final buffer if not already done */
+    for( ; i<bufferFrames; i++ )
+    {
+        *out++ = 0; /* left */
+        *out++ = 0; /* right */
+    }
+    return finished;
+}
+int main(int argc, char* argv[]);
+int main(int argc, char* argv[])
+{
+    PaStream *stream;
+    PaError err;
+    patest1data data;
+    int i;
+    int inputDevice = Pa_GetDefaultInputDeviceID();
+    int outputDevice = Pa_GetDefaultOutputDeviceID();
+    /* initialise sinusoidal wavetable */
+    for( i=0; i<100; i++ )
+        data.sine[i] = sin( ((double)i/100.) * M_PI * 2. );
+    data.phase = 0;
+    data.sampsToGo = 44100 * 4;   // 20 seconds
+    /* initialise portaudio subsytem */
+    Pa_Initialize();
+    err = Pa_OpenStream(
+              &stream,
+              inputDevice,
+              2,              /* stereo input */
+              paFloat32,  /* 32 bit floating point input */
+              NULL,
+              outputDevice,
+              2,              /* stereo output */
+              paFloat32,      /* 32 bit floating point output */
+              NULL,
+              44100.,
+              //    22050,          /* half second buffers */
+              //    4,              /* four buffers */
+              512,          /* half second buffers */
+              0,              /* four buffers */
+              paClipOff,      /* we won't output out of range samples so don't bother clipping them */
+              patest1Callback,
+              &data );
+    if( err == paNoError )
+    {
+        err = Pa_StartStream( stream );
+        //       printf( "Press any key to end.\n" );
+        //       getc( stdin ); //wait for input before exiting
+        //       Pa_AbortStream( stream );
+
+        printf( "Waiting for stream to complete...\n" );
+
+        while( Pa_StreamActive( stream ) )
+            Pa_Sleep(1000); /* sleep until playback has finished */
+
+        err = Pa_CloseStream( stream );
+    }
+    else
+    {
+        fprintf( stderr, "An error occured while opening the portaudio stream\n" );
+        if( err == paHostError )
+            fprintf( stderr, "Host error number: %d\n", Pa_GetHostError() );
+        else
+            fprintf( stderr, "Error number: %d\n", err );
+    }
+    Pa_Terminate();
+    printf( "bye\n" );
+
+    return 0;
+}
diff --git a/pa_tests/pa_devs.c b/pa_tests/pa_devs.c
new file mode 100644 (file)
index 0000000..7ca2225
--- /dev/null
@@ -0,0 +1,98 @@
+/*
+ * $Id$
+ * pa_devs.c
+ * List available devices.
+ *
+ * Author: Phil Burk  http://www.softsynth.com
+ *
+ * This program uses the PortAudio Portable Audio Library.
+ * For more information see: http://www.portaudio.com
+ * Copyright (c) 1999-2000 Ross Bencina and 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.
+ *
+ */
+#include <stdio.h>
+#include <math.h>
+#include "portaudio.h"
+/*******************************************************************/
+int main(void);
+int main(void)
+{
+    int      i,j;
+    int      numDevices;
+    const    PaDeviceInfo *pdi;
+    PaError  err;
+    Pa_Initialize();
+    numDevices = Pa_CountDevices();
+    if( numDevices < 0 )
+    {
+        printf("ERROR: Pa_CountDevices returned 0x%x\n", numDevices );
+        err = numDevices;
+        goto error;
+    }
+    printf("Number of devices = %d\n", numDevices );
+    for( i=0; i<numDevices; i++ )
+    {
+        pdi = Pa_GetDeviceInfo( i );
+        printf("---------------------------------------------- #%d", i );
+        if( i == Pa_GetDefaultInputDeviceID() ) printf(" DefaultInput");
+        if( i == Pa_GetDefaultOutputDeviceID() ) printf(" DefaultOutput");
+        printf("\nName         = %s\n", pdi->name );
+        printf("Max Inputs   = %d", pdi->maxInputChannels  );
+        printf(", Max Outputs = %d\n", pdi->maxOutputChannels  );
+        if( pdi->numSampleRates == -1 )
+        {
+            printf("Sample Rate Range = %f to %f\n", pdi->sampleRates[0], pdi->sampleRates[1] );
+        }
+        else
+        {
+            printf("Sample Rates =");
+            for( j=0; j<pdi->numSampleRates; j++ )
+            {
+                printf(" %8.2f,", pdi->sampleRates[j] );
+            }
+            printf("\n");
+        }
+        printf("Native Sample Formats = ");
+        if( pdi->nativeSampleFormats & paInt8 )        printf("paInt8, ");
+        if( pdi->nativeSampleFormats & paUInt8 )       printf("paUInt8, ");
+        if( pdi->nativeSampleFormats & paInt16 )       printf("paInt16, ");
+        if( pdi->nativeSampleFormats & paInt32 )       printf("paInt32, ");
+        if( pdi->nativeSampleFormats & paFloat32 )     printf("paFloat32, ");
+        if( pdi->nativeSampleFormats & paInt24 )       printf("paInt24, ");
+        if( pdi->nativeSampleFormats & paPackedInt24 ) printf("paPackedInt24, ");
+        printf("\n");
+    }
+    Pa_Terminate();
+
+    printf("----------------------------------------------\n");
+    return 0;
+error:
+    Pa_Terminate();
+    fprintf( stderr, "An error occured while using the portaudio stream\n" );
+    fprintf( stderr, "Error number: %d\n", err );
+    fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
+    return err;
+}
diff --git a/pa_tests/pa_fuzz.c b/pa_tests/pa_fuzz.c
new file mode 100644 (file)
index 0000000..3161c74
--- /dev/null
@@ -0,0 +1,155 @@
+/*
+ * $Id$
+ * pa_fuzz.c
+ * Distort input like a fuzz boz.
+ *
+ * Author: Phil Burk  http://www.softsynth.com
+ *
+ * This program uses the PortAudio Portable Audio Library.
+ * For more information see: http://www.portaudio.com
+ * Copyright (c) 1999-2000 Ross Bencina and 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.
+ *
+ */
+#include <stdio.h>
+#include <math.h>
+#include "portaudio.h"
+/*
+** Note that many of the older ISA sound cards on PCs do NOT support
+** full duplex audio (simultaneous record and playback).
+** And some only support full duplex at lower sample rates.
+*/
+#define SAMPLE_RATE         (44100)
+#define PA_SAMPLE_TYPE      paFloat32
+#define FRAMES_PER_BUFFER   (64)
+
+typedef float SAMPLE;
+
+float CubicAmplifier( float input );
+static int fuzzCallback( void *inputBuffer, void *outputBuffer,
+                         unsigned long framesPerBuffer,
+                         PaTimestamp outTime, void *userData );
+
+/* Non-linear amplifier with soft distortion curve. */
+float CubicAmplifier( float input )
+{
+    float output, temp;
+    if( input < 0.0 )
+    {
+        temp = input + 1.0f;
+        output = (temp * temp * temp) - 1.0f;
+    }
+    else
+    {
+        temp = input - 1.0f;
+        output = (temp * temp * temp) + 1.0f;
+    }
+
+    return output;
+}
+#define FUZZ(x) CubicAmplifier(CubicAmplifier(CubicAmplifier(CubicAmplifier(x))))
+
+static int gNumNoInputs = 0;
+/* This routine will be called by the PortAudio engine when audio is needed.
+** It may be called at interrupt level on some machines so don't do anything
+** that could mess up the system like calling malloc() or free().
+*/
+static int fuzzCallback( void *inputBuffer, void *outputBuffer,
+                         unsigned long framesPerBuffer,
+                         PaTimestamp outTime, void *userData )
+{
+    SAMPLE *out = (SAMPLE*)outputBuffer;
+    SAMPLE *in = (SAMPLE*)inputBuffer;
+    unsigned int i;
+    (void) outTime; /* Prevent unused variable warnings. */
+    (void) userData;
+
+    if( inputBuffer == NULL )
+    {
+        for( i=0; i<framesPerBuffer; i++ )
+        {
+            *out++ = 0;  /* left - silent */
+            *out++ = 0;  /* right - silent */
+        }
+        gNumNoInputs += 1;
+    }
+    else
+    {
+        for( i=0; i<framesPerBuffer; i++ )
+        {
+            *out++ = FUZZ(*in++);  /* left - distorted */
+            *out++ = *in++;          /* right - clean */
+        }
+    }
+    return 0;
+}
+
+/*******************************************************************/
+int main(void);
+int main(void)
+{
+    PortAudioStream *stream;
+    PaError err;
+
+    err = Pa_Initialize();
+    if( err != paNoError ) goto error;
+
+    err = Pa_OpenStream(
+              &stream,
+              Pa_GetDefaultInputDeviceID(), /* default output device */
+              2,               /* stereo input */
+              PA_SAMPLE_TYPE,
+              NULL,
+              Pa_GetDefaultOutputDeviceID(), /* default output device */
+              2,               /* stereo output */
+              PA_SAMPLE_TYPE,
+              NULL,
+              SAMPLE_RATE,
+              FRAMES_PER_BUFFER,
+              0,             /* number of buffers, if zero then use default minimum */
+              0, // paClipOff,     /* we won't output out of range samples so don't bother clipping them */
+              fuzzCallback,
+              NULL );
+    if( err != paNoError ) goto error;
+
+    err = Pa_StartStream( stream );
+    if( err != paNoError ) goto error;
+
+    printf("Hit ENTER to stop program.\n");
+    getchar();
+    err = Pa_CloseStream( stream );
+    if( err != paNoError ) goto error;
+
+    printf("Finished. gNumNoInputs = %d\n", gNumNoInputs );
+    Pa_Terminate();
+    return 0;
+
+error:
+    Pa_Terminate();
+    fprintf( stderr, "An error occured while using the portaudio stream\n" );
+    fprintf( stderr, "Error number: %d\n", err );
+    fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
+    return -1;
+}
diff --git a/pa_tests/pa_minlat.c b/pa_tests/pa_minlat.c
new file mode 100644 (file)
index 0000000..356ccb9
--- /dev/null
@@ -0,0 +1,164 @@
+/*
+ * $Id$
+ * paminlat.c
+ * Experiment with different numbers of buffers to determine the
+ * minimum latency for a computer.
+ *
+ * Author: Phil Burk  http://www.softsynth.com
+ *
+ * This program uses the PortAudio Portable Audio Library.
+ * For more information see: http://www.portaudio.com
+ * Copyright (c) 1999-2000 Ross Bencina and 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.
+ *
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include "portaudio.h"
+#ifndef M_PI
+#define M_PI  (3.14159265)
+#endif
+#define DEFAULT_BUFFER_SIZE   (128)
+#define TABLE_SIZE   (200)
+typedef struct
+{
+    float sine[TABLE_SIZE];
+    int left_phase;
+    int right_phase;
+}
+paTestData;
+/* Very simple synthesis routine to generate two sine waves. */
+static int paminlatCallback( void *inputBuffer, void *outputBuffer,
+                             unsigned long framesPerBuffer,
+                             PaTimestamp outTime, void *userData )
+{
+    paTestData *data = (paTestData*)userData;
+    float *out = (float*)outputBuffer;
+    unsigned int i;
+    for( i=0; i<framesPerBuffer; i++ )
+    {
+        *out++ = data->sine[data->left_phase];  /* left */
+        *out++ = data->sine[data->right_phase];  /* right */
+        data->left_phase += 1;
+        if( data->left_phase >= TABLE_SIZE ) data->left_phase -= TABLE_SIZE;
+        data->right_phase += 3; /* higher pitch so we can distinguish left and right. */
+        if( data->right_phase >= TABLE_SIZE ) data->right_phase -= TABLE_SIZE;
+    }
+    return 0;
+}
+void main( int argc, char **argv );
+void main( int argc, char **argv )
+{
+    PortAudioStream *stream;
+    PaError err;
+    paTestData data;
+    int    i;
+    int    go;
+    int    numBuffers = 0;
+    int    minBuffers = 0;
+    int    framesPerBuffer;
+    double sampleRate = 44100.0;
+    char   str[256];
+    printf("paminlat - Determine minimum latency for your computer.\n");
+    printf("  usage:         paminlat {framesPerBuffer}\n");
+    printf("  for example:   paminlat 256\n");
+    printf("Adjust your stereo until you hear a smooth tone in each speaker.\n");
+    printf("Then try to find the smallest number of buffers that still sounds smooth.\n");
+    printf("Note that the sound will stop momentarily when you change the number of buffers.\n");
+    /* Get bufferSize from command line. */
+    framesPerBuffer = ( argc > 1 ) ? atol( argv[1] ) : DEFAULT_BUFFER_SIZE;
+    printf("Frames per buffer = %d\n", framesPerBuffer );
+    /* Initialise sinusoidal wavetable. */
+    for( i=0; i<TABLE_SIZE; i++ )
+    {
+        data.sine[i] = (float) sin( ((double)i/(double)TABLE_SIZE) * M_PI * 2. );
+    }
+    data.left_phase = data.right_phase = 0;
+    err = Pa_Initialize();
+    if( err != paNoError ) goto error;
+    /* Ask PortAudio for the recommended minimum number of buffers. */
+    numBuffers = minBuffers = Pa_GetMinNumBuffers( framesPerBuffer, sampleRate );
+    printf("NumBuffers set to %d based on a call to Pa_GetMinNumBuffers()\n", numBuffers );
+    /* Try different numBuffers in a loop. */
+    go = 1;
+    while( go )
+    {
+
+        printf("Latency = framesPerBuffer * numBuffers = %d * %d = %d frames = %d msecs.\n",
+               framesPerBuffer, numBuffers, framesPerBuffer*numBuffers,
+               (int)((1000 * framesPerBuffer * numBuffers) / sampleRate) );
+        err = Pa_OpenStream(
+                  &stream,
+                  paNoDevice,
+                  0,              /* no input */
+                  paFloat32,  /* 32 bit floating point input */
+                  NULL,
+                  Pa_GetDefaultOutputDeviceID(), /* default output device */
+                  2,              /* stereo output */
+                  paFloat32,      /* 32 bit floating point output */
+                  NULL,
+                  sampleRate,
+                  framesPerBuffer,/* 46 msec buffers */
+                  numBuffers,     /* number of buffers */
+                  paClipOff,      /* we won't output out of range samples so don't bother clipping them */
+                  paminlatCallback,
+                  &data );
+        if( err != paNoError ) goto error;
+        if( stream == NULL ) goto error;
+        /* Start audio. */
+        err = Pa_StartStream( stream );
+        if( err != paNoError ) goto error;
+        /* Ask user for a new number of buffers. */
+        printf("\nMove windows around to see if the sound glitches.\n");
+        printf("NumBuffers currently %d, enter new number, or 'q' to quit: ", numBuffers );
+        gets( str );
+        if( str[0] == 'q' ) go = 0;
+        else
+        {
+            numBuffers = atol( str );
+            if( numBuffers < minBuffers )
+            {
+                printf( "numBuffers below minimum of %d! Set to minimum!!!\n", minBuffers );
+                numBuffers = minBuffers;
+            }
+        }
+        /* Stop sound until ENTER hit. */
+        err = Pa_StopStream( stream );
+        if( err != paNoError ) goto error;
+        err = Pa_CloseStream( stream );
+        if( err != paNoError ) goto error;
+    }
+    printf("A good setting for latency would be somewhat higher than\n");
+    printf("the minimum latency that worked.\n");
+    printf("PortAudio: Test finished.\n");
+    Pa_Terminate();
+    return;
+error:
+    Pa_Terminate();
+    fprintf( stderr, "An error occured while using the portaudio stream\n" );
+    fprintf( stderr, "Error number: %d\n", err );
+    fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
+}
diff --git a/pa_tests/paqa_devs.c b/pa_tests/paqa_devs.c
new file mode 100644 (file)
index 0000000..c81c99a
--- /dev/null
@@ -0,0 +1,317 @@
+/*
+ * $Id$
+ * paqa_devs.c
+ * Self Testing Quality Assurance app for PortAudio
+ * Try to open each device and run through all the
+ * possible configurations.
+ *
+ * Author: Phil Burk  http://www.softsynth.com
+ *
+ * This program uses the PortAudio Portable Audio Library.
+ * For more information see: http://www.portaudio.com
+ * Copyright (c) 1999-2000 Ross Bencina and 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.
+ *
+ */
+#include <stdio.h>
+#include <math.h>
+#include "portaudio.h"
+#include "pa_trace.h"
+/****************************************** Definitions ***********/
+#define MODE_INPUT      (0)
+#define MODE_OUTPUT     (1)
+typedef struct PaQaData
+{
+    unsigned long  framesLeft;
+    int            numChannels;
+    int            bytesPerSample;
+    int            mode;
+    short          sawPhase;
+    PaSampleFormat format;
+}
+PaQaData;
+/****************************************** Prototypes ***********/
+static void TestDevices( int mode );
+static void TestFormats( int mode, PaDeviceID deviceID, double sampleRate,
+                         int numChannels );
+static int TestAdvance( int mode, PaDeviceID deviceID, double sampleRate,
+                        int numChannels, PaSampleFormat format );
+static int QaCallback( void *inputBuffer, void *outputBuffer,
+                       unsigned long framesPerBuffer,
+                       PaTimestamp outTime, void *userData );
+/****************************************** Globals ***********/
+static int gNumPassed = 0;
+static int gNumFailed = 0;
+/****************************************** Macros ***********/
+/* Print ERROR if it fails. Tally success or failure. */
+/* Odd do-while wrapper seems to be needed for some compilers. */
+#define EXPECT(_exp) \
+    do \
+    { \
+        if ((_exp)) {\
+            /* printf("SUCCESS for %s\n", #_exp ); */ \
+            gNumPassed++; \
+        } \
+        else { \
+            printf("ERROR - 0x%x - %s for %s\n", result, \
+                   ((result == 0) ? "-" : Pa_GetErrorText(result)), \
+                   #_exp ); \
+            gNumFailed++; \
+            goto error; \
+        } \
+    } while(0)
+/*******************************************************************/
+/* This routine will be called by the PortAudio engine when audio is needed.
+** It may be called at interrupt level on some machines so don't do anything
+** that could mess up the system like calling malloc() or free().
+*/
+static int QaCallback( void *inputBuffer, void *outputBuffer,
+                       unsigned long framesPerBuffer,
+                       PaTimestamp outTime, void *userData )
+{
+    unsigned long  i;
+    short          phase;
+    PaQaData *data = (PaQaData *) userData;
+    (void) inputBuffer;
+    (void) outTime;
+
+    /* Play simle sawtooth wave. */
+    if( data->mode == MODE_OUTPUT )
+    {
+        phase = data->sawPhase;
+        switch( data->format )
+        {
+        case paFloat32:
+            {
+                float *out =  (float *) outputBuffer;
+                for( i=0; i<framesPerBuffer; i++ )
+                {
+                    phase += 0x123;
+                    *out++ = (float) (phase * (1.0 / 32768.0));
+                    if( data->numChannels == 2 )
+                    {
+                        *out++ = (float) (phase * (1.0 / 32768.0));
+                    }
+                }
+            }
+            break;
+
+        case paInt32:
+            {
+                int *out =  (int *) outputBuffer;
+                for( i=0; i<framesPerBuffer; i++ )
+                {
+                    phase += 0x123;
+                    *out++ = ((int) phase ) << 16;
+                    if( data->numChannels == 2 )
+                    {
+                        *out++ = ((int) phase ) << 16;
+                    }
+                }
+            }
+            break;
+        case paInt16:
+            {
+                short *out =  (short *) outputBuffer;
+                for( i=0; i<framesPerBuffer; i++ )
+                {
+                    phase += 0x123;
+                    *out++ = phase;
+                    if( data->numChannels == 2 )
+                    {
+                        *out++ = phase;
+                    }
+                }
+            }
+            break;
+
+        default:
+            {
+                unsigned char *out =  (unsigned char *) outputBuffer;
+                unsigned long numBytes = framesPerBuffer * data->numChannels * data->bytesPerSample;
+                for( i=0; i<numBytes; i++ )
+                {
+                    *out++ = 0;
+                }
+            }
+            break;
+        }
+        data->sawPhase = phase;
+    }
+    /* Are we through yet? */
+    if( data->framesLeft > framesPerBuffer )
+    {
+        AddTraceMessage("QaCallback: running. framesLeft", data->framesLeft );
+        data->framesLeft -= framesPerBuffer;
+        return 0;
+    }
+    else
+    {
+        AddTraceMessage("QaCallback: DONE! framesLeft", data->framesLeft );
+        data->framesLeft = 0;
+        return 1;
+    }
+}
+/*******************************************************************/
+int main(void);
+int main(void)
+{
+    PaError result;
+    EXPECT( ((result=Pa_Initialize()) == 0) );
+    printf("Test OUTPUT ---------------\n");
+    TestDevices( MODE_OUTPUT );
+    printf("Test INPUT ---------------\n");
+    TestDevices( MODE_INPUT );
+error:
+    Pa_Terminate();
+    printf("QA Report: %d passed, %d failed.\n", gNumPassed, gNumFailed );
+}
+/*******************************************************************
+* Try each output device, through its full range of capabilities. */
+static void TestDevices( int mode )
+{
+    int id,jc,kr;
+    int maxChannels;
+    const PaDeviceInfo *pdi;
+    int numDevices  = Pa_CountDevices();
+    /* Iterate through all devices. */
+    for( id=0; id<numDevices; id++ )
+    {
+        pdi = Pa_GetDeviceInfo( id );
+        /* Try 1 to maxChannels on each device. */
+        maxChannels = ( mode == MODE_INPUT ) ? pdi->maxInputChannels : pdi->maxOutputChannels;
+        for( jc=1; jc<=maxChannels; jc++ )
+        {
+            printf("Name         = %s\n", pdi->name );
+            /* Try each legal sample rate. */
+            if( pdi->numSampleRates == -1 )
+            {
+                double low, high;
+                low = pdi->sampleRates[0];
+                high = pdi->sampleRates[1];
+                if( low < 8000.0 ) low = 8000.0;
+                TestFormats( mode, id, low, jc );
+#define TESTSR(sr) {if(((sr)>=low) && ((sr)<=high)) TestFormats( mode, id, (sr), jc ); }
+
+                TESTSR(11025.0);
+                TESTSR(22050.0);
+                TESTSR(44100.0);
+                TestFormats( mode, id, high, jc );
+            }
+            else
+            {
+                for( kr=0; kr<pdi->numSampleRates; kr++ )
+                {
+                    TestFormats( mode, id, pdi->sampleRates[kr], jc );
+                }
+            }
+        }
+    }
+}
+/*******************************************************************/
+static void TestFormats( int mode, PaDeviceID deviceID, double sampleRate,
+                         int numChannels )
+{
+    TestAdvance( mode, deviceID, sampleRate, numChannels, paFloat32 ); /* */
+    TestAdvance( mode, deviceID, sampleRate, numChannels, paInt16 ); /* */
+    TestAdvance( mode, deviceID, sampleRate, numChannels, paInt32 ); /* */
+    /* TestAdvance( mode, deviceID, sampleRate, numChannels, paInt24 ); */
+}
+/*******************************************************************/
+static int TestAdvance( int mode, PaDeviceID deviceID, double sampleRate,
+                        int numChannels, PaSampleFormat format )
+{
+    PortAudioStream *stream = NULL;
+    PaError result;
+    PaQaData myData;
+#define FRAMES_PER_BUFFER  (64)
+    printf("------ TestAdvance: %s, device = %d, rate = %g, numChannels = %d, format = %d -------\n",
+           ( mode == MODE_INPUT ) ? "INPUT" : "OUTPUT",
+           deviceID, sampleRate, numChannels, format);
+    /* Setup data for synthesis thread. */
+    myData.framesLeft = (unsigned long) (sampleRate * 100); /* 100 seconds */
+    myData.numChannels = numChannels;
+    myData.mode = mode;
+    myData.format = format;
+    switch( format )
+    {
+    case paFloat32:
+    case paInt32:
+    case paInt24:
+        myData.bytesPerSample = 4;
+        break;
+    case paPackedInt24:
+        myData.bytesPerSample = 3;
+        break;
+    default:
+        myData.bytesPerSample = 2;
+        break;
+    }
+    EXPECT( ((result = Pa_OpenStream(
+                           &stream,
+                           ( mode == MODE_INPUT ) ? deviceID : paNoDevice,
+                           ( mode == MODE_INPUT ) ? numChannels : 0,
+                           format,
+                           NULL,
+                           ( mode == MODE_OUTPUT ) ? deviceID : paNoDevice,
+                           ( mode == MODE_OUTPUT ) ? numChannels : 0,
+                           format,
+                           NULL,
+                           sampleRate,
+                           FRAMES_PER_BUFFER,     /* frames per buffer */
+                           0,             /* number of buffers, if zero then use default minimum */
+                           paClipOff,     /* we won't output out of range samples so don't bother clipping them */
+                           QaCallback,
+                           &myData )
+             ) == 0) );
+    if( stream )
+    {
+        PaTimestamp oldStamp, newStamp;
+        unsigned long oldFrames;
+        int minDelay = ( mode == MODE_INPUT ) ? 1000 : 400;
+        int minNumBuffers = Pa_GetMinNumBuffers( FRAMES_PER_BUFFER, sampleRate );
+        int msec = (int) ((minNumBuffers * 3 * 1000.0 * FRAMES_PER_BUFFER) / sampleRate);
+        if( msec < minDelay ) msec = minDelay;
+        printf("msec = %d\n", msec);  /**/
+        EXPECT( ((result=Pa_StartStream( stream )) == 0) );
+        /* Check to make sure PortAudio is advancing timeStamp. */
+        result = paNoError;
+        oldStamp = Pa_StreamTime(stream);
+        Pa_Sleep(msec);
+        newStamp = Pa_StreamTime(stream);
+        printf("oldStamp = %g,newStamp = %g\n", oldStamp, newStamp ); /**/
+        EXPECT( (oldStamp < newStamp) );
+        /* Check to make sure callback is decrementing framesLeft. */
+        oldFrames = myData.framesLeft;
+        Pa_Sleep(msec);
+        printf("oldFrames = %d, myData.framesLeft = %d\n", oldFrames,  myData.framesLeft ); /**/
+        EXPECT( (oldFrames > myData.framesLeft) );
+        EXPECT( ((result=Pa_CloseStream( stream )) == 0) );
+        stream = NULL;
+    }
+error:
+    if( stream != NULL ) Pa_CloseStream( stream );
+    return result;
+}
diff --git a/pa_tests/paqa_errs.c b/pa_tests/paqa_errs.c
new file mode 100644 (file)
index 0000000..37e75d7
--- /dev/null
@@ -0,0 +1,305 @@
+/*
+ * $Id$
+ * paqa_devs.c
+ * Self Testing Quality Assurance app for PortAudio
+ * Do lots of bad things to test error reporting.
+ *
+ * Author: Phil Burk  http://www.softsynth.com
+ *
+ * This program uses the PortAudio Portable Audio Library.
+ * For more information see: http://www.portaudio.com
+ * Copyright (c) 1999-2000 Ross Bencina and 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.
+ *
+ */
+#include <stdio.h>
+#include <math.h>
+#include "portaudio.h"
+/****************************************** Definitions ***********/
+#define MODE_INPUT      (0)
+#define MODE_OUTPUT     (1)
+#define FRAMES_PER_BUFFER       (64)
+#define SAMPLE_RATE        (44100.0)
+#define NUM_BUFFERS              (0)
+typedef struct PaQaData
+{
+    unsigned long  framesLeft;
+    int            numChannels;
+    int            bytesPerSample;
+    int            mode;
+}
+PaQaData;
+/****************************************** Prototypes ***********/
+static void TestDevices( int mode );
+static void TestFormats( int mode, PaDeviceID deviceID, double sampleRate,
+                         int numChannels );
+static int TestBadOpens( void );
+static int TestBadActions( void );
+static int QaCallback( void *inputBuffer, void *outputBuffer,
+                       unsigned long framesPerBuffer,
+                       PaTimestamp outTime, void *userData );
+/****************************************** Globals ***********/
+static int gNumPassed = 0;
+static int gNumFailed = 0;
+/****************************************** Macros ***********/
+/* Print ERROR if it fails. Tally success or failure. */
+/* Odd do-while wrapper seems to be needed for some compilers. */
+#define EXPECT(_exp) \
+    do \
+    { \
+        if ((_exp)) {\
+            gNumPassed++; \
+        } \
+        else { \
+            printf("\nERROR - 0x%x - %s for %s\n", result, Pa_GetErrorText(result), #_exp ); \
+            gNumFailed++; \
+            goto error; \
+        } \
+    } while(0)
+#define HOPEFOR(_exp) \
+    do \
+    { \
+        if ((_exp)) {\
+            gNumPassed++; \
+        } \
+        else { \
+            printf("\nERROR - 0x%x - %s for %s\n", result, Pa_GetErrorText(result), #_exp ); \
+            gNumFailed++; \
+        } \
+    } while(0)
+/*******************************************************************/
+/* This routine will be called by the PortAudio engine when audio is needed.
+** It may be called at interrupt level on some machines so don't do anything
+** that could mess up the system like calling malloc() or free().
+*/
+static int QaCallback( void *inputBuffer, void *outputBuffer,
+                       unsigned long framesPerBuffer,
+                       PaTimestamp outTime, void *userData )
+{
+    unsigned long i;
+    unsigned char *out = (unsigned char *) outputBuffer;
+    PaQaData *data = (PaQaData *) userData;
+    (void) inputBuffer; /* Prevent "unused variable" warnings. */
+    (void) outTime;
+
+    /* Zero out buffer so we don't hear terrible noise. */
+    if( data->mode == MODE_OUTPUT )
+    {
+        unsigned long numBytes = framesPerBuffer * data->numChannels * data->bytesPerSample;
+        for( i=0; i<numBytes; i++ )
+        {
+            *out++ = 0;
+        }
+    }
+    /* Are we through yet? */
+    if( data->framesLeft > framesPerBuffer )
+    {
+        data->framesLeft -= framesPerBuffer;
+        return 0;
+    }
+    else
+    {
+        data->framesLeft = 0;
+        return 1;
+    }
+}
+/*******************************************************************/
+int main(void);
+int main(void)
+{
+    PaError result;
+    EXPECT( ((result=Pa_Initialize()) == 0) );
+    TestBadOpens();
+    TestBadActions();
+error:
+    Pa_Terminate();
+    printf("QA Report: %d passed, %d failed.\n", gNumPassed, gNumFailed );
+    return 0;
+}
+/*******************************************************************/
+static int TestBadOpens( void )
+{
+    PortAudioStream *stream = NULL;
+    PaError result;
+    PaQaData myData;
+    /* Setup data for synthesis thread. */
+    myData.framesLeft = (unsigned long) (SAMPLE_RATE * 100); /* 100 seconds */
+    myData.numChannels = 1;
+    myData.mode = MODE_OUTPUT;
+    HOPEFOR( (/* No devices specified. */
+                 (result = Pa_OpenStream(
+                               &stream,
+                               paNoDevice, 0, paFloat32, NULL,
+                               paNoDevice, 0, paFloat32, NULL,
+                               SAMPLE_RATE, FRAMES_PER_BUFFER, NUM_BUFFERS,
+                               paClipOff,
+                               QaCallback,
+                               &myData )
+                 ) == paInvalidDeviceId) );
+    HOPEFOR( ( /* Out of range input device specified. */
+                 (result = Pa_OpenStream(
+                               &stream,
+                               Pa_CountDevices(), 0, paFloat32, NULL,
+                               paNoDevice, 0, paFloat32, NULL,
+                               SAMPLE_RATE, FRAMES_PER_BUFFER, NUM_BUFFERS,
+                               paClipOff,
+                               QaCallback,
+                               &myData )
+                 ) == paInvalidDeviceId) );
+
+    HOPEFOR( ( /* Out of range output device specified. */
+                 (result = Pa_OpenStream(
+                               &stream,
+                               paNoDevice, 0, paFloat32, NULL,
+                               Pa_CountDevices(), 0, paFloat32, NULL,
+                               SAMPLE_RATE, FRAMES_PER_BUFFER, NUM_BUFFERS,
+                               paClipOff,
+                               QaCallback,
+                               &myData )
+                 ) == paInvalidDeviceId) );
+    HOPEFOR( ( /* Zero input channels. */
+                 (result = Pa_OpenStream(
+                               &stream,
+                               Pa_GetDefaultInputDeviceID(), 0, paFloat32, NULL,
+                               paNoDevice, 0, paFloat32, NULL,
+                               SAMPLE_RATE, FRAMES_PER_BUFFER, NUM_BUFFERS,
+                               paClipOff,
+                               QaCallback,
+                               &myData )
+                 ) == paInvalidChannelCount) );
+    HOPEFOR( ( /* Zero output channels. */
+                 (result = Pa_OpenStream(
+                               &stream,
+                               paNoDevice, 0, paFloat32, NULL,
+                               Pa_GetDefaultOutputDeviceID(), 0, paFloat32, NULL,
+                               SAMPLE_RATE, FRAMES_PER_BUFFER, NUM_BUFFERS,
+                               paClipOff,
+                               QaCallback,
+                               &myData )
+                 ) == paInvalidChannelCount) );
+    HOPEFOR( ( /* Nonzero input channels but no device. */
+                 (result = Pa_OpenStream(
+                               &stream,
+                               Pa_GetDefaultInputDeviceID(), 2, paFloat32, NULL,
+                               paNoDevice, 2, paFloat32, NULL,
+                               SAMPLE_RATE, FRAMES_PER_BUFFER, NUM_BUFFERS,
+                               paClipOff,
+                               QaCallback,
+                               &myData )
+                 ) == paInvalidChannelCount) );
+
+    HOPEFOR( ( /* Nonzero output channels but no device. */
+                 (result = Pa_OpenStream(
+                               &stream,
+                               paNoDevice, 2, paFloat32, NULL,
+                               Pa_GetDefaultOutputDeviceID(), 2, paFloat32, NULL,
+                               SAMPLE_RATE, FRAMES_PER_BUFFER, NUM_BUFFERS,
+                               paClipOff,
+                               QaCallback,
+                               &myData )
+                 ) == paInvalidChannelCount) );
+    HOPEFOR( ( /* NULL stream pointer. */
+                 (result = Pa_OpenStream(
+                               NULL,
+                               paNoDevice, 0, paFloat32, NULL,
+                               Pa_GetDefaultOutputDeviceID(), 2, paFloat32, NULL,
+                               SAMPLE_RATE, FRAMES_PER_BUFFER, NUM_BUFFERS,
+                               paClipOff,
+                               QaCallback,
+                               &myData )
+                 ) == paBadStreamPtr) );
+    HOPEFOR( ( /* Low sample rate. */
+                 (result = Pa_OpenStream(
+                               &stream,
+                               paNoDevice, 0, paFloat32, NULL,
+                               Pa_GetDefaultOutputDeviceID(), 2, paFloat32, NULL,
+                               1.0, FRAMES_PER_BUFFER, NUM_BUFFERS,
+                               paClipOff,
+                               QaCallback,
+                               &myData )
+                 ) == paInvalidSampleRate) );
+    HOPEFOR( ( /* High sample rate. */
+                 (result = Pa_OpenStream(
+                               &stream,
+                               paNoDevice, 0, paFloat32, NULL,
+                               Pa_GetDefaultOutputDeviceID(), 2, paFloat32, NULL,
+                               10000000.0, FRAMES_PER_BUFFER, NUM_BUFFERS,
+                               paClipOff,
+                               QaCallback,
+                               &myData )
+                 ) == paInvalidSampleRate) );
+    HOPEFOR( ( /* NULL callback. */
+                 (result = Pa_OpenStream(
+                               &stream,
+                               paNoDevice, 0, paFloat32, NULL,
+                               Pa_GetDefaultOutputDeviceID(), 2, paFloat32, NULL,
+                               SAMPLE_RATE, FRAMES_PER_BUFFER, NUM_BUFFERS,
+                               paClipOff,
+                               NULL,
+                               &myData )
+                 ) == paNullCallback) );
+    HOPEFOR( ( /* Bad flag. */
+                 (result = Pa_OpenStream(
+                               &stream,
+                               paNoDevice, 0, paFloat32, NULL,
+                               Pa_GetDefaultOutputDeviceID(), 2, paFloat32, NULL,
+                               SAMPLE_RATE, FRAMES_PER_BUFFER, NUM_BUFFERS,
+                               (1<<3),
+                               QaCallback,
+                               &myData )
+                 ) == paInvalidFlag) );
+    if( stream != NULL ) Pa_CloseStream( stream );
+    return result;
+}
+/*******************************************************************/
+static int TestBadActions( void )
+{
+    PortAudioStream *stream = NULL;
+    PaError result;
+    PaQaData myData;
+    /* Setup data for synthesis thread. */
+    myData.framesLeft = (unsigned long) (SAMPLE_RATE * 100); /* 100 seconds */
+    myData.numChannels = 1;
+    myData.mode = MODE_OUTPUT;
+    /* Default output. */
+    EXPECT( ((result = Pa_OpenStream(
+                           &stream,
+                           paNoDevice, 0, paFloat32, NULL,
+                           Pa_GetDefaultOutputDeviceID(), 2, paFloat32, NULL,
+                           SAMPLE_RATE, FRAMES_PER_BUFFER, NUM_BUFFERS,
+                           paClipOff,
+                           QaCallback,
+                           &myData )
+             ) == 0) );
+    HOPEFOR( ((result = Pa_StartStream( NULL )) == paBadStreamPtr) );
+    HOPEFOR( ((result = Pa_StopStream( NULL )) == paBadStreamPtr) );
+    HOPEFOR( ((result = Pa_StreamActive( NULL )) == paBadStreamPtr) );
+    HOPEFOR( ((result = Pa_CloseStream( NULL )) == paBadStreamPtr) );
+    HOPEFOR( ((result = (PaError)Pa_StreamTime( NULL )) != 0) );
+    HOPEFOR( ((result = (PaError)Pa_GetCPULoad( NULL )) != 0) );
+error:
+    if( stream != NULL ) Pa_CloseStream( stream );
+    return result;
+}
diff --git a/pa_tests/patest1.c b/pa_tests/patest1.c
new file mode 100644 (file)
index 0000000..5fddd51
--- /dev/null
@@ -0,0 +1,114 @@
+/*
+ $Id$
+ patest1.c
+ Ring modulate the audio input with a sine wave for 20 seconds
+    using the Portable Audio api
+    Author: Ross Bencina <rossb@audiomulch.com>
+    Modifications:
+ April 5th, 2001 - PLB - Check for NULL inputBuffer.
+*/
+#include <stdio.h>
+#include <math.h>
+#include "portaudio.h"
+#ifndef M_PI
+#define M_PI  (3.14159265)
+#endif
+typedef struct
+{
+    float sine[100];
+    int phase;
+    int sampsToGo;
+}
+patest1data;
+static int patest1Callback( void *inputBuffer, void *outputBuffer,
+                            unsigned long bufferFrames,
+                            PaTimestamp outTime, void *userData )
+{
+    patest1data *data = (patest1data*)userData;
+    float *in = (float*)inputBuffer;
+    float *out = (float*)outputBuffer;
+    int framesToCalc = bufferFrames;
+    unsigned long i;
+    int finished = 0;
+    /* Check to see if any input data is available. */
+    if(inputBuffer == NULL) return 0;
+    if( data->sampsToGo < bufferFrames )
+    {
+        framesToCalc = data->sampsToGo;
+        finished = 1;
+    }
+    for( i=0; i<framesToCalc; i++ )
+    {
+        *out++ = *in++ * data->sine[data->phase];  /* left */
+        *out++ = *in++ * data->sine[data->phase++];  /* right */
+        if( data->phase >= 100 )
+            data->phase = 0;
+    }
+    data->sampsToGo -= framesToCalc;
+    /* zero remainder of final buffer if not already done */
+    for( ; i<bufferFrames; i++ )
+    {
+        *out++ = 0; /* left */
+        *out++ = 0; /* right */
+    }
+    return finished;
+}
+int main(int argc, char* argv[]);
+int main(int argc, char* argv[])
+{
+    PaStream *stream;
+    PaError err;
+    patest1data data;
+    int i;
+    int inputDevice = Pa_GetDefaultInputDeviceID();
+    int outputDevice = Pa_GetDefaultOutputDeviceID();
+    /* initialise sinusoidal wavetable */
+    for( i=0; i<100; i++ )
+        data.sine[i] = sin( ((double)i/100.) * M_PI * 2. );
+    data.phase = 0;
+    data.sampsToGo = 44100 * 20;   // 20 seconds
+    /* initialise portaudio subsytem */
+    Pa_Initialize();
+    err = Pa_OpenStream(
+              &stream,
+              inputDevice,
+              2,              /* stereo input */
+              paFloat32,  /* 32 bit floating point input */
+              NULL,
+              outputDevice,
+              2,              /* stereo output */
+              paFloat32,      /* 32 bit floating point output */
+              NULL,
+              44100.,
+              512,            /* small buffers */
+              0,              /* let PA determine number of buffers */
+              paClipOff,      /* we won't output out of range samples so don't bother clipping them */
+              patest1Callback,
+              &data );
+    if( err == paNoError )
+    {
+        err = Pa_StartStream( stream );
+        printf( "Press any key to end.\n" );
+        getc( stdin ); //wait for input before exiting
+        Pa_AbortStream( stream );
+
+        printf( "Waiting for stream to complete...\n" );
+
+        while( Pa_StreamActive( stream ) )
+            Pa_Sleep(1000); /* sleep until playback has finished */
+
+        err = Pa_CloseStream( stream );
+    }
+    else
+    {
+        fprintf( stderr, "An error occured while opening the portaudio stream\n" );
+        if( err == paHostError )
+            fprintf( stderr, "Host error number: %d\n", Pa_GetHostError() );
+        else
+            fprintf( stderr, "Error number: %d\n", err );
+    }
+    Pa_Terminate();
+    printf( "bye\n" );
+
+    return 0;
+}
diff --git a/pa_tests/patest_buffer.c b/pa_tests/patest_buffer.c
new file mode 100644 (file)
index 0000000..4963504
--- /dev/null
@@ -0,0 +1,181 @@
+/*
+ * $Id$
+ * patest_buffer.c
+ * Test opening streams with different buffer sizes.
+ *
+ * Author: Phil Burk  http://www.softsynth.com
+ *
+ * This program uses the PortAudio Portable Audio Library.
+ * For more information see: http://www.portaudio.com
+ * Copyright (c) 1999-2000 Ross Bencina and 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.
+ *
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include "portaudio.h"
+#define NUM_SECONDS   (1)
+#define SAMPLE_RATE   (44100)
+#ifndef M_PI
+#define M_PI  (3.14159265)
+#endif
+#define TABLE_SIZE   (200)
+
+#define BUFFER_TABLE  9
+long buffer_table[] = {200,256,500,512,600, 723, 1000, 1024, 2345};
+
+typedef struct
+{
+    short sine[TABLE_SIZE];
+    int left_phase;
+    int right_phase;
+    unsigned int sampsToGo;
+}
+paTestData;
+PaError TestOnce( int buffersize );
+
+static int patest1Callback( void *inputBuffer, void *outputBuffer,
+                            unsigned long framesPerBuffer,
+                            PaTimestamp outTime, void *userData );
+/* This routine will be called by the PortAudio engine when audio is needed.
+** It may called at interrupt level on some machines so don't do anything
+** that could mess up the system like calling malloc() or free().
+*/
+static int patest1Callback( void *inputBuffer, void *outputBuffer,
+                            unsigned long framesPerBuffer,
+                            PaTimestamp outTime, void *userData )
+{
+    paTestData *data = (paTestData*)userData;
+    short *out = (short*)outputBuffer;
+    unsigned int i;
+    int finished = 0;
+    (void) inputBuffer; /* Prevent "unused variable" warnings. */
+    (void) outTime;
+
+    if( data->sampsToGo < framesPerBuffer )
+    {
+        /* final buffer... */
+
+        for( i=0; i<data->sampsToGo; i++ )
+        {
+            *out++ = data->sine[data->left_phase];  /* left */
+            *out++ = data->sine[data->right_phase];  /* right */
+            data->left_phase += 1;
+            if( data->left_phase >= TABLE_SIZE ) data->left_phase -= TABLE_SIZE;
+            data->right_phase += 3; /* higher pitch so we can distinguish left and right. */
+            if( data->right_phase >= TABLE_SIZE ) data->right_phase -= TABLE_SIZE;
+        }
+        /* zero remainder of final buffer */
+        for( ; i<framesPerBuffer; i++ )
+        {
+            *out++ = 0; /* left */
+            *out++ = 0; /* right */
+        }
+
+        finished = 1;
+    }
+    else
+    {
+        for( i=0; i<framesPerBuffer; i++ )
+        {
+            *out++ = data->sine[data->left_phase];  /* left */
+            *out++ = data->sine[data->right_phase];  /* right */
+            data->left_phase += 1;
+            if( data->left_phase >= TABLE_SIZE ) data->left_phase -= TABLE_SIZE;
+            data->right_phase += 3; /* higher pitch so we can distinguish left and right. */
+            if( data->right_phase >= TABLE_SIZE ) data->right_phase -= TABLE_SIZE;
+        }
+        data->sampsToGo -= framesPerBuffer;
+    }
+    return finished;
+}
+/*******************************************************************/
+int main(void);
+int main(void)
+{
+    int i;
+    PaError err;
+    printf("Test opening streams with different buffer sizes\n\n");
+
+    for (i = 0 ; i < BUFFER_TABLE; i++)
+    {
+        printf("Buffer size %d\n", buffer_table[i]);
+        err = TestOnce(buffer_table[i]);
+        if( err < 0 ) return 0;
+
+    }
+}
+
+
+PaError TestOnce( int buffersize )
+{
+    PortAudioStream *stream;
+    PaError err;
+    paTestData data;
+    int i;
+    int totalSamps;
+    /* initialise sinusoidal wavetable */
+    for( i=0; i<TABLE_SIZE; i++ )
+    {
+        data.sine[i] = (short) (32767.0 * sin( ((double)i/(double)TABLE_SIZE) * M_PI * 2. ));
+    }
+    data.left_phase = data.right_phase = 0;
+    data.sampsToGo = totalSamps =  NUM_SECONDS * SAMPLE_RATE; /* Play for a few seconds. */
+    err = Pa_Initialize();
+    if( err != paNoError ) goto error;
+    err = Pa_OpenStream(
+              &stream,
+              paNoDevice,/* default input device */
+              0,              /* no input */
+              paInt16,  /* sample format */
+              NULL,
+              Pa_GetDefaultOutputDeviceID(), /* default output device */
+              2,              /* stereo output */
+              paInt16,        /* sample format */
+              NULL,
+              SAMPLE_RATE,
+              buffersize,           /* frames per buffer */
+              0,              /* number of buffers, if zero then use default minimum */
+              paClipOff,      /* we won't output out of range samples so don't bother clipping them */
+              patest1Callback,
+              &data );
+    if( err != paNoError ) goto error;
+
+    err = Pa_StartStream( stream );
+    if( err != paNoError ) goto error;
+    printf("Waiting for sound to finish.\n");
+    Pa_Sleep(1000);
+    err = Pa_CloseStream( stream );
+    if( err != paNoError ) goto error;
+    Pa_Terminate();
+    return paNoError;
+error:
+    Pa_Terminate();
+    fprintf( stderr, "An error occured while using the portaudio stream\n" );
+    fprintf( stderr, "Error number: %d\n", err );
+    fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
+    return err;
+}
diff --git a/pa_tests/patest_clip.c b/pa_tests/patest_clip.c
new file mode 100644 (file)
index 0000000..39cd1c8
--- /dev/null
@@ -0,0 +1,156 @@
+/*
+ * $Id$
+ * patest_clip.c
+ * Play a sine wave using the Portable Audio api for several seconds
+ * at an amplitude that would require clipping.
+ *
+ * Author: Phil Burk  http://www.softsynth.com
+ *
+ * This program uses the PortAudio Portable Audio Library.
+ * For more information see: http://www.portaudio.com
+ * Copyright (c) 1999-2000 Ross Bencina and 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.
+ *
+ */
+#include <stdio.h>
+#include <math.h>
+#include "portaudio.h"
+#define NUM_SECONDS   (4)
+#define SAMPLE_RATE   (44100)
+#ifndef M_PI
+#define M_PI  (3.14159265)
+#endif
+#define TABLE_SIZE   (200)
+typedef struct paTestData
+{
+    float sine[TABLE_SIZE];
+    float amplitude;
+    int left_phase;
+    int right_phase;
+}
+paTestData;
+PaError PlaySine( paTestData *data, unsigned long flags, float amplitude );
+/* This routine will be called by the PortAudio engine when audio is needed.
+** It may called at interrupt level on some machines so don't do anything
+** that could mess up the system like calling malloc() or free().
+*/
+static int sineCallback( void *inputBuffer, void *outputBuffer,
+                         unsigned long framesPerBuffer,
+                         PaTimestamp outTime, void *userData )
+{
+    paTestData *data = (paTestData*)userData;
+    float *out = (float*)outputBuffer;
+    float amplitude = data->amplitude;
+    unsigned int i;
+    (void) inputBuffer; /* Prevent "unused variable" warnings. */
+    (void) outTime;
+
+    for( i=0; i<framesPerBuffer; i++ )
+    {
+        *out++ = amplitude * data->sine[data->left_phase];  /* left */
+        *out++ = amplitude * data->sine[data->right_phase];  /* right */
+        data->left_phase += 1;
+        if( data->left_phase >= TABLE_SIZE ) data->left_phase -= TABLE_SIZE;
+        data->right_phase += 3; /* higher pitch so we can distinguish left and right. */
+        if( data->right_phase >= TABLE_SIZE ) data->right_phase -= TABLE_SIZE;
+    }
+    return 0;
+}
+/*******************************************************************/
+int main(void);
+int main(void)
+{
+    PaError err;
+    paTestData DATA;
+    int i;
+    printf("PortAudio Test: output sine wave with and without clipping.\n");
+    /* initialise sinusoidal wavetable */
+    for( i=0; i<TABLE_SIZE; i++ )
+    {
+        DATA.sine[i] = (float) sin( ((double)i/(double)TABLE_SIZE) * M_PI * 2. );
+    }
+    printf("\nHalf amplitude. Should sound like sine wave.\n"); fflush(stdout);
+    err = PlaySine( &DATA, paClipOff | paDitherOff, 0.5f );
+    if( err < 0 ) goto error;
+    printf("\nFull amplitude. Should sound like sine wave.\n"); fflush(stdout);
+    err = PlaySine( &DATA, paClipOff | paDitherOff, 0.999f );
+    if( err < 0 ) goto error;
+    printf("\nOver range with clipping and dithering turned OFF. Should sound very nasty.\n");
+    fflush(stdout);
+    err = PlaySine( &DATA, paClipOff | paDitherOff, 1.1f );
+    if( err < 0 ) goto error;
+    printf("\nOver range with clipping and dithering turned ON.  Should sound smoother than previous.\n");
+    fflush(stdout);
+    err = PlaySine( &DATA, paNoFlag, 1.1f );
+    if( err < 0 ) goto error;
+    printf("\nOver range with paClipOff but dithering ON.\n"
+           "That forces clipping ON so it should sound the same as previous.\n");
+    fflush(stdout);
+    err = PlaySine( &DATA, paClipOff, 1.1f );
+    if( err < 0 ) goto error;
+    return 0;
+error:
+    fprintf( stderr, "An error occured while using the portaudio stream\n" );
+    fprintf( stderr, "Error number: %d\n", err );
+    fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
+    return 1;
+}
+/*****************************************************************************/
+PaError PlaySine( paTestData *data, unsigned long flags, float amplitude )
+{
+    PortAudioStream *stream;
+    PaError err;
+    data->left_phase = data->right_phase = 0;
+    data->amplitude = amplitude;
+    err = Pa_Initialize();
+    if( err != paNoError ) goto error;
+    err = Pa_OpenStream(
+              &stream,
+              paNoDevice,/* default input device */
+              0,              /* no input */
+              paFloat32,  /* 32 bit floating point input */
+              NULL,
+              Pa_GetDefaultOutputDeviceID(), /* default output device */
+              2,              /* stereo output */
+              paFloat32,      /* 32 bit floating point output */
+              NULL,
+              SAMPLE_RATE,
+              1024,
+              0,              /* number of buffers, if zero then use default minimum */
+              flags,      /* we won't output out of range samples so don't bother clipping them */
+              sineCallback,
+              data );
+    if( err != paNoError ) goto error;
+    err = Pa_StartStream( stream );
+    if( err != paNoError ) goto error;
+    Pa_Sleep( NUM_SECONDS * 1000 );
+    printf("CPULoad = %8.6f\n", Pa_GetCPULoad( stream ) );
+    err = Pa_CloseStream( stream );
+    if( err != paNoError ) goto error;
+    Pa_Terminate();
+    return paNoError;
+error:
+    return err;
+}
diff --git a/pa_tests/patest_dither.c b/pa_tests/patest_dither.c
new file mode 100644 (file)
index 0000000..8878161
--- /dev/null
@@ -0,0 +1,151 @@
+/*
+ * $Id$
+ * patest_dither.c
+ * Attempt to hear difference between dithered and non-dithered signal.
+ *
+ * Author: Phil Burk  http://www.softsynth.com
+ *
+ * This program uses the PortAudio Portable Audio Library.
+ * For more information see: http://www.portaudio.com
+ * Copyright (c) 1999-2000 Ross Bencina and 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.
+ *
+ */
+#include <stdio.h>
+#include <math.h>
+#include "portaudio.h"
+#define NUM_SECONDS   (4)
+#define SAMPLE_RATE   (44100)
+#ifndef M_PI
+#define M_PI  (3.14159265)
+#endif
+#define TABLE_SIZE   (200)
+typedef struct paTestData
+{
+    float sine[TABLE_SIZE];
+    float amplitude;
+    int left_phase;
+    int right_phase;
+}
+paTestData;
+PaError PlaySine( paTestData *data, PaStreamFlags flags, float amplitude );
+static int sineCallback( void *inputBuffer, void *outputBuffer,
+                         unsigned long framesPerBuffer,
+                         PaTimestamp outTime, void *userData );
+/* This routine will be called by the PortAudio engine when audio is needed.
+** It may called at interrupt level on some machines so don't do anything
+** that could mess up the system like calling malloc() or free().
+*/
+static int sineCallback( void *inputBuffer, void *outputBuffer,
+                         unsigned long framesPerBuffer,
+                         PaTimestamp outTime, void *userData )
+{
+    paTestData *data = (paTestData*)userData;
+    float *out = (float*)outputBuffer;
+    float amplitude = data->amplitude;
+    unsigned int i;
+    (void) outTime;
+    (void) inputBuffer;
+    for( i=0; i<framesPerBuffer; i++ )
+    {
+        *out++ = amplitude * data->sine[data->left_phase];  /* left */
+        *out++ = amplitude * data->sine[data->right_phase];  /* right */
+        data->left_phase += 1;
+        if( data->left_phase >= TABLE_SIZE ) data->left_phase -= TABLE_SIZE;
+        data->right_phase += 3; /* higher pitch so we can distinguish left and right. */
+        if( data->right_phase >= TABLE_SIZE ) data->right_phase -= TABLE_SIZE;
+    }
+    return 0;
+}
+/*******************************************************************/
+int main(void);
+int main(void)
+{
+    PaError err;
+    paTestData DATA;
+    int i;
+    float amplitude = 32.0 / (1<<15);
+    printf("PortAudio Test: output EXTREMELY QUIET sine wave with and without dithering.\n");
+    /* initialise sinusoidal wavetable */
+    for( i=0; i<TABLE_SIZE; i++ )
+    {
+        DATA.sine[i] = (float) sin( ((double)i/(double)TABLE_SIZE) * M_PI * 2. );
+    }
+    printf("\nNo treatment..\n"); fflush(stdout);
+    err = PlaySine( &DATA, paClipOff | paDitherOff, amplitude );
+    if( err < 0 ) goto error;
+    printf("\nClip.\n");
+    fflush(stdout);
+    err = PlaySine( &DATA, paDitherOff, amplitude );
+    if( err < 0 ) goto error;
+    printf("\nClip and Dither.\n");
+    fflush(stdout);
+    err = PlaySine( &DATA, paNoFlag, amplitude );
+    if( err < 0 ) goto error;
+    return 0;
+error:
+    fprintf( stderr, "An error occured while using the portaudio stream\n" );
+    fprintf( stderr, "Error number: %d\n", err );
+    fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
+    return -1;
+}
+/*****************************************************************************/
+PaError PlaySine( paTestData *data, PaStreamFlags flags, float amplitude )
+{
+    PortAudioStream *stream;
+    PaError err;
+    data->left_phase = data->right_phase = 0;
+    data->amplitude = amplitude;
+    err = Pa_Initialize();
+    if( err != paNoError ) goto error;
+    err = Pa_OpenStream(
+              &stream,
+              paNoDevice,/* default input device */
+              0,              /* no input */
+              paFloat32,  /* 32 bit floating point input */
+              NULL,
+              Pa_GetDefaultOutputDeviceID(), /* default output device */
+              2,              /* stereo output */
+              paFloat32,      /* 32 bit floating point output */
+              NULL,
+              SAMPLE_RATE,
+              1024,
+              0,              /* number of buffers, if zero then use default minimum */
+              flags,      /* we won't output out of range samples so don't bother clipping them */
+              sineCallback,
+              (void *)data );
+    if( err != paNoError ) goto error;
+
+    err = Pa_StartStream( stream );
+    if( err != paNoError ) goto error;
+    Pa_Sleep( NUM_SECONDS * 1000 );
+    printf("CPULoad = %8.6f\n", Pa_GetCPULoad( stream ) );
+    err = Pa_CloseStream( stream );
+    if( err != paNoError ) goto error;
+    Pa_Terminate();
+    return paNoError;
+error:
+    return err;
+}
diff --git a/pa_tests/patest_latency.c b/pa_tests/patest_latency.c
new file mode 100644 (file)
index 0000000..2daf431
--- /dev/null
@@ -0,0 +1,171 @@
+/*
+ * $Id$
+ * Hear the latency caused by bug buffers.
+ * Play a sine wave and change frequency based on letter input.
+ *
+ * Author: Phil Burk <philburk@softsynth.com>
+ *
+ * This program uses the PortAudio Portable Audio Library.
+ * For more information see: http://www.portaudio.com
+ * Copyright (c) 1999-2000 Ross Bencina and 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.
+ *
+ */
+#include <stdio.h>
+#include <math.h>
+#include "portaudio.h"
+#define OUTPUT_DEVICE       (Pa_GetDefaultOutputDeviceID())
+#define SAMPLE_RATE         (44100)
+#define FRAMES_PER_BUFFER   (256)
+#if 1
+#define MIN_LATENCY_MSEC    (2000)
+#define NUM_BUFFERS         ((MIN_LATENCY_MSEC * SAMPLE_RATE) / (FRAMES_PER_BUFFER * 1000))
+#else
+#define NUM_BUFFERS         (0)
+#endif
+#define MIN_FREQ            (100.0f)
+#define CalcPhaseIncrement(freq)  ((freq)/SAMPLE_RATE)
+#ifndef M_PI
+#define M_PI  (3.14159265)
+#endif
+#define TABLE_SIZE   (400)
+typedef struct
+{
+    float sine[TABLE_SIZE + 1]; // add one for guard point for interpolation
+    float phase_increment;
+    float left_phase;
+    float right_phase;
+}
+paTestData;
+float LookupSine( paTestData *data, float phase );
+/* Convert phase between and 1.0 to sine value
+ * using linear interpolation.
+ */
+float LookupSine( paTestData *data, float phase )
+{
+    float fIndex = phase*TABLE_SIZE;
+    int   index = (int) fIndex;
+    float fract = fIndex - index;
+    float lo = data->sine[index];
+    float hi = data->sine[index+1];
+    float val = lo + fract*(hi-lo);
+    return val;
+}
+/* This routine will be called by the PortAudio engine when audio is needed.
+** It may called at interrupt level on some machines so don't do anything
+** that could mess up the system like calling malloc() or free().
+*/
+static int patestCallback( void *inputBuffer, void *outputBuffer,
+                           unsigned long framesPerBuffer,
+                           PaTimestamp outTime, void *userData )
+{
+    paTestData *data = (paTestData*)userData;
+    float *out = (float*)outputBuffer;
+    int i;
+    int finished = 0;
+    (void) outTime; /* Prevent unused variable warnings. */
+    (void) inputBuffer;
+
+    for( i=0; i<framesPerBuffer; i++ )
+    {
+        *out++ = LookupSine(data, data->left_phase);  /* left */
+        *out++ = LookupSine(data, data->right_phase);  /* right */
+        data->left_phase += data->phase_increment;
+        if( data->left_phase >= 1.0f ) data->left_phase -= 1.0f;
+        data->right_phase += (data->phase_increment * 1.5f); /* fifth above */
+        if( data->right_phase >= 1.0f ) data->right_phase -= 1.0f;
+    }
+    return 0;
+}
+/*******************************************************************/
+int main(void);
+int main(void)
+{
+    PortAudioStream *stream;
+    PaError err;
+    paTestData data;
+    int i;
+    int done = 0;
+    printf("PortAudio Test: output sine sweep. ask for %d buffers\n", NUM_BUFFERS );
+    /* initialise sinusoidal wavetable */
+    for( i=0; i<TABLE_SIZE; i++ )
+    {
+        data.sine[i] = (float) sin( ((double)i/(double)TABLE_SIZE) * M_PI * 2. );
+    }
+    data.sine[TABLE_SIZE] = data.sine[0]; // set guard point
+    data.left_phase = data.right_phase = 0.0;
+    data.phase_increment = CalcPhaseIncrement(MIN_FREQ);
+    err = Pa_Initialize();
+    if( err != paNoError ) goto error;
+    printf("PortAudio Test: output device = %d\n", OUTPUT_DEVICE );
+    err = Pa_OpenStream(
+              &stream,
+              paNoDevice,
+              0,              /* no input */
+              paFloat32,  /* 32 bit floating point input */
+              NULL,
+              OUTPUT_DEVICE,
+              2,              /* stereo output */
+              paFloat32,      /* 32 bit floating point output */
+              NULL,
+              SAMPLE_RATE,
+              FRAMES_PER_BUFFER,
+              NUM_BUFFERS,    /* number of buffers, if zero then use default minimum */
+              paClipOff|paDitherOff, /* we won't output out of range samples so don't bother clipping them */
+              patestCallback,
+              &data );
+    if( err != paNoError ) goto error;
+    err = Pa_StartStream( stream );
+    if( err != paNoError ) goto error;
+    printf("Play ASCII keyboard. Hit 'q' to stop. (Use RETURN key on Mac)\n");
+    while ( !done )
+    {
+        float  freq;
+        int index;
+        char c;
+        do
+        {
+            c = getchar();
+        }
+        while( c < ' '); /* Strip white space and control chars. */
+
+        if( c == 'q' ) done = 1;
+        index = c % 26;
+        freq = MIN_FREQ + (index * 40.0);
+        data.phase_increment = CalcPhaseIncrement(freq);
+    }
+    printf("Call Pa_StopStream()\n");
+    err = Pa_StopStream( stream );
+    if( err != paNoError ) goto error;
+    Pa_Terminate();
+    printf("Test finished.\n");
+    return err;
+error:
+    Pa_Terminate();
+    fprintf( stderr, "An error occured while using the portaudio stream\n" );
+    fprintf( stderr, "Error number: %d\n", err );
+    fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
+    return err;
+}
diff --git a/pa_tests/patest_leftright.c b/pa_tests/patest_leftright.c
new file mode 100644 (file)
index 0000000..7e19b17
--- /dev/null
@@ -0,0 +1,163 @@
+/*
+ * $Id$
+ * patest_leftright.c
+ * Play different tone sine waves that alternate between left and right channel.
+ * The low tone should be on the left channel.
+ *
+ * Authors:
+ *    Ross Bencina <rossb@audiomulch.com>
+ *    Phil Burk <philburk@softsynth.com>
+ *
+ * This program uses the PortAudio Portable Audio Library.
+ * For more information see: http://www.audiomulch.com/portaudio/
+ * Copyright (c) 1999-2000 Ross Bencina and 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.
+ *
+ */
+#include <stdio.h>
+#include <math.h>
+#include "portaudio.h"
+#define NUM_SECONDS   (8)
+#define SAMPLE_RATE   (44100)
+#define FRAMES_PER_BUFFER  (512)
+#ifndef M_PI
+#define M_PI  (3.14159265)
+#endif
+#define TABLE_SIZE   (200)
+typedef struct
+{
+    float sine[TABLE_SIZE];
+    int left_phase;
+    int right_phase;
+    int toggle;
+    int countDown;
+}
+paTestData;
+/* This routine will be called by the PortAudio engine when audio is needed.
+** It may called at interrupt level on some machines so don't do anything
+** that could mess up the system like calling malloc() or free().
+*/
+static int patestCallback(   void *inputBuffer, void *outputBuffer,
+                             unsigned long framesPerBuffer,
+                             PaTimestamp outTime, void *userData )
+{
+    paTestData *data = (paTestData*)userData;
+    float *out = (float*)outputBuffer;
+    unsigned long i;
+    int finished = 0;
+    (void) outTime; /* Prevent unused variable warnings. */
+    (void) inputBuffer;
+
+    for( i=0; i<framesPerBuffer; i++ )
+    {
+        if( data->toggle )
+        {
+            *out++ = data->sine[data->left_phase];  /* left */
+            *out++ = 0;  /* right */
+        }
+        else
+        {
+            *out++ = 0;  /* left */
+            *out++ = data->sine[data->right_phase];  /* right */
+        }
+
+        data->left_phase += 1;
+        if( data->left_phase >= TABLE_SIZE ) data->left_phase -= TABLE_SIZE;
+        data->right_phase += 3; /* higher pitch so we can distinguish left and right. */
+        if( data->right_phase >= TABLE_SIZE ) data->right_phase -= TABLE_SIZE;
+    }
+
+    if( data->countDown < 0 )
+    {
+        data->countDown = SAMPLE_RATE;
+        data->toggle = !data->toggle;
+    }
+    data->countDown -= framesPerBuffer;
+
+    return finished;
+}
+/*******************************************************************/
+int main(void);
+int main(void)
+{
+    PortAudioStream *stream;
+    PaError err;
+    paTestData data;
+    int i;
+    int timeout;
+    printf("PortAudio Test: output sine wave. SR = %d, BufSize = %d\n", SAMPLE_RATE, FRAMES_PER_BUFFER);
+    /* initialise sinusoidal wavetable */
+    for( i=0; i<TABLE_SIZE; i++ )
+    {
+        data.sine[i] = (float) sin( ((double)i/(double)TABLE_SIZE) * M_PI * 2. );
+    }
+    data.left_phase = data.right_phase = 0;
+    data.countDown = SAMPLE_RATE;
+
+    err = Pa_Initialize();
+    if( err != paNoError ) goto error;
+    err = Pa_OpenStream(
+              &stream,
+              paNoDevice,/* default input device */
+              0,              /* no input */
+              paFloat32,  /* 32 bit floating point input */
+              NULL,
+              Pa_GetDefaultOutputDeviceID(), /* default output device */
+              2,          /* stereo output */
+              paFloat32,      /* 32 bit floating point output */
+              NULL,
+              SAMPLE_RATE,
+              FRAMES_PER_BUFFER,            /* frames per buffer */
+              0,              /* number of buffers, if zero then use default minimum */
+              paClipOff,      /* we won't output out of range samples so don't bother clipping them */
+              patestCallback,
+              &data );
+    if( err != paNoError ) goto error;
+    err = Pa_StartStream( stream );
+    if( err != paNoError ) goto error;
+    printf("Play for several seconds.\n");
+    timeout = NUM_SECONDS * 4;
+    while( timeout > 0 )
+    {
+        printf("Countdown = %d, Toggle = %d\n", data.countDown, data.toggle );
+        fflush( stdout );
+        Pa_Sleep( 1000/4 );
+        timeout -= 1;
+    }
+
+    err = Pa_StopStream( stream );
+    if( err != paNoError ) goto error;
+    err = Pa_CloseStream( stream );
+    if( err != paNoError ) goto error;
+    Pa_Terminate();
+    printf("Test finished.\n");
+    return err;
+error:
+    Pa_Terminate();
+    fprintf( stderr, "An error occured while using the portaudio stream\n" );
+    fprintf( stderr, "Error number: %d\n", err );
+    fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
+    return err;
+}
diff --git a/pa_tests/patest_longsine.c b/pa_tests/patest_longsine.c
new file mode 100644 (file)
index 0000000..3d8bd4c
--- /dev/null
@@ -0,0 +1,124 @@
+/*
+ * $Id$
+ * patest_longsine.c
+ * Play a sine wave using the Portable Audio api forever.
+ *
+ * Author: Phil Burk  http://www.softsynth.com
+ *
+ * This program uses the PortAudio Portable Audio Library.
+ * For more information see: http://www.portaudio.com
+ * Copyright (c) 1999-2000 Ross Bencina and 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.
+ *
+ */
+#include <stdio.h>
+#include <math.h>
+#include "portaudio.h"
+#define SAMPLE_RATE   (44100)
+#ifndef M_PI
+#define M_PI  (3.14159265)
+#endif
+#define TABLE_SIZE   (200)
+typedef struct
+{
+    float sine[TABLE_SIZE];
+    int left_phase;
+    int right_phase;
+}
+paTestData;
+/* This routine will be called by the PortAudio engine when audio is needed.
+** It may called at interrupt level on some machines so don't do anything
+** that could mess up the system like calling malloc() or free().
+*/
+static int patestCallback( void *inputBuffer, void *outputBuffer,
+                           unsigned long framesPerBuffer,
+                           PaTimestamp outTime, void *userData )
+{
+    paTestData *data = (paTestData*)userData;
+    float *out = (float*)outputBuffer;
+    unsigned int i;
+    (void) outTime; /* Prevent unused variable warnings. */
+    (void) inputBuffer;
+    for( i=0; i<framesPerBuffer; i++ )
+    {
+        *out++ = data->sine[data->left_phase];  /* left */
+        *out++ = data->sine[data->right_phase];  /* right */
+        data->left_phase += 1;
+        if( data->left_phase >= TABLE_SIZE ) data->left_phase -= TABLE_SIZE;
+        data->right_phase += 3; /* higher pitch so we can distinguish left and right. */
+        if( data->right_phase >= TABLE_SIZE ) data->right_phase -= TABLE_SIZE;
+    }
+    return 0;
+}
+/*******************************************************************/
+int main(void);
+int main(void)
+{
+    PortAudioStream *stream;
+    PaError err;
+    paTestData data;
+    int i;
+    printf("PortAudio Test: output sine wave.\n");
+    /* initialise sinusoidal wavetable */
+    for( i=0; i<TABLE_SIZE; i++ )
+    {
+        data.sine[i] = (float) sin( ((double)i/(double)TABLE_SIZE) * M_PI * 2. );
+    }
+    data.left_phase = data.right_phase = 0;
+    err = Pa_Initialize();
+    if( err != paNoError ) goto error;
+    err = Pa_OpenStream(
+              &stream,
+              paNoDevice,/* default input device */
+              0,              /* no input */
+              paFloat32,  /* 32 bit floating point input */
+              NULL,
+              Pa_GetDefaultOutputDeviceID(), /* default output device */
+              2,          /* stereo output */
+              paFloat32,      /* 32 bit floating point output */
+              NULL,
+              SAMPLE_RATE,
+              256,            /* frames per buffer */
+              0,              /* number of buffers, if zero then use default minimum */
+              paClipOff,      /* we won't output out of range samples so don't bother clipping them */
+              patestCallback,
+              &data );
+    if( err != paNoError ) goto error;
+    err = Pa_StartStream( stream );
+    if( err != paNoError ) goto error;
+    printf("Hit ENTER to stop program.\n");
+    getchar();
+    err = Pa_CloseStream( stream );
+    if( err != paNoError ) goto error;
+    Pa_Terminate();
+    printf("Test finished.\n");
+    return err;
+error:
+    Pa_Terminate();
+    fprintf( stderr, "An error occured while using the portaudio stream\n" );
+    fprintf( stderr, "Error number: %d\n", err );
+    fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
+    return err;
+}
diff --git a/pa_tests/patest_many.c b/pa_tests/patest_many.c
new file mode 100644 (file)
index 0000000..18169e6
--- /dev/null
@@ -0,0 +1,194 @@
+/*
+ * $Id$
+ * patest_many.c
+ * Start and stop the PortAudio Driver multiple times.
+ *
+ * Author: Phil Burk  http://www.softsynth.com
+ *
+ * This program uses the PortAudio Portable Audio Library.
+ * For more information see: http://www.portaudio.com
+ * Copyright (c) 1999-2000 Ross Bencina and 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.
+ *
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include "portaudio.h"
+#define NUM_SECONDS   (1)
+#define SAMPLE_RATE   (44100)
+#ifndef M_PI
+#define M_PI  (3.14159265)
+#endif
+#define TABLE_SIZE   (200)
+typedef struct
+{
+    short sine[TABLE_SIZE];
+    int left_phase;
+    int right_phase;
+    unsigned int sampsToGo;
+}
+paTestData;
+PaError TestOnce( void );
+static int patest1Callback( void *inputBuffer, void *outputBuffer,
+                            unsigned long framesPerBuffer,
+                            PaTimestamp outTime, void *userData );
+/* This routine will be called by the PortAudio engine when audio is needed.
+** It may called at interrupt level on some machines so don't do anything
+** that could mess up the system like calling malloc() or free().
+*/
+static int patest1Callback( void *inputBuffer, void *outputBuffer,
+                            unsigned long framesPerBuffer,
+                            PaTimestamp outTime, void *userData )
+{
+    paTestData *data = (paTestData*)userData;
+    short *out = (short*)outputBuffer;
+    unsigned int i;
+    int finished = 0;
+    (void) inputBuffer; /* Prevent "unused variable" warnings. */
+    (void) outTime;
+
+    if( data->sampsToGo < framesPerBuffer )
+    {
+        /* final buffer... */
+
+        for( i=0; i<data->sampsToGo; i++ )
+        {
+            *out++ = data->sine[data->left_phase];  /* left */
+            *out++ = data->sine[data->right_phase];  /* right */
+            data->left_phase += 1;
+            if( data->left_phase >= TABLE_SIZE ) data->left_phase -= TABLE_SIZE;
+            data->right_phase += 3; /* higher pitch so we can distinguish left and right. */
+            if( data->right_phase >= TABLE_SIZE ) data->right_phase -= TABLE_SIZE;
+        }
+        /* zero remainder of final buffer */
+        for( ; i<framesPerBuffer; i++ )
+        {
+            *out++ = 0; /* left */
+            *out++ = 0; /* right */
+        }
+
+        finished = 1;
+    }
+    else
+    {
+        for( i=0; i<framesPerBuffer; i++ )
+        {
+            *out++ = data->sine[data->left_phase];  /* left */
+            *out++ = data->sine[data->right_phase];  /* right */
+            data->left_phase += 1;
+            if( data->left_phase >= TABLE_SIZE ) data->left_phase -= TABLE_SIZE;
+            data->right_phase += 3; /* higher pitch so we can distinguish left and right. */
+            if( data->right_phase >= TABLE_SIZE ) data->right_phase -= TABLE_SIZE;
+        }
+        data->sampsToGo -= framesPerBuffer;
+    }
+    return finished;
+}
+/*******************************************************************/
+#ifdef MACINTOSH
+int main(void);
+int main(void)
+{
+    int i;
+    PaError err;
+    int numLoops = 10;
+    printf("Loop %d times.\n", numLoops );
+    for( i=0; i<numLoops; i++ )
+    {
+        printf("Loop %d out of %d.\n", i+1, numLoops );
+        err = TestOnce();
+        if( err < 0 ) return 0;
+    }
+}
+#else
+int main(int argc, char **argv);
+int main(int argc, char **argv)
+{
+    PaError err;
+    int i, numLoops = 10;
+    if( argc > 1 )
+    {
+        numLoops = atoi(argv[1]);
+    }
+    for( i=0; i<numLoops; i++ )
+    {
+        printf("Loop %d out of %d.\n", i+1, numLoops );
+        err = TestOnce();
+        if( err < 0 ) return 1;
+    }
+    printf("Test complete.\n");
+    return 0;
+}
+#endif
+PaError TestOnce( void )
+{
+    PortAudioStream *stream;
+    PaError err;
+    paTestData data;
+    int i;
+    int totalSamps;
+    /* initialise sinusoidal wavetable */
+    for( i=0; i<TABLE_SIZE; i++ )
+    {
+        data.sine[i] = (short) (32767.0 * sin( ((double)i/(double)TABLE_SIZE) * M_PI * 2. ));
+    }
+    data.left_phase = data.right_phase = 0;
+    data.sampsToGo = totalSamps =  NUM_SECONDS * SAMPLE_RATE; /* Play for a few seconds. */
+    err = Pa_Initialize();
+    if( err != paNoError ) goto error;
+    err = Pa_OpenStream(
+              &stream,
+              paNoDevice,/* default input device */
+              0,              /* no input */
+              paInt16,  /* sample format */
+              NULL,
+              Pa_GetDefaultOutputDeviceID(), /* default output device */
+              2,              /* stereo output */
+              paInt16,        /* sample format */
+              NULL,
+              SAMPLE_RATE,
+              1024,           /* frames per buffer */
+              8,              /* number of buffers, if zero then use default minimum */
+              paClipOff,      /* we won't output out of range samples so don't bother clipping them */
+              patest1Callback,
+              &data );
+    if( err != paNoError ) goto error;
+
+    err = Pa_StartStream( stream );
+    if( err != paNoError ) goto error;
+    printf("Waiting for sound to finish.\n");
+    Pa_Sleep(1000);
+    err = Pa_CloseStream( stream );
+    if( err != paNoError ) goto error;
+    Pa_Terminate();
+    return paNoError;
+error:
+    Pa_Terminate();
+    fprintf( stderr, "An error occured while using the portaudio stream\n" );
+    fprintf( stderr, "Error number: %d\n", err );
+    fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
+    return err;
+}
diff --git a/pa_tests/patest_maxsines.c b/pa_tests/patest_maxsines.c
new file mode 100644 (file)
index 0000000..deae6c6
--- /dev/null
@@ -0,0 +1,155 @@
+/*
+ * $Id$
+ * patest_maxsines.c
+ * How many sine waves can we calculate and play in less than 80% CPU Load.
+ *
+ * Authors:
+ *    Ross Bencina <rossb@audiomulch.com>
+ *    Phil Burk <philburk@softsynth.com>
+ *
+ * This program uses the PortAudio Portable Audio Library.
+ * For more information see: http://www.audiomulch.com/portaudio/
+ * Copyright (c) 1999-2000 Ross Bencina and 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.
+ *
+ */
+#include <stdio.h>
+#include <math.h>
+#include "portaudio.h"
+
+#define MAX_SINES     (500)
+#define SAMPLE_RATE   (44100)
+#define FRAMES_PER_BUFFER  (512)
+#ifndef M_PI
+#define M_PI  (3.14159265)
+#endif
+#define TWOPI (M_PI * 2.0)
+
+typedef struct paTestData
+{
+    int numSines;
+    double phases[MAX_SINES];
+}
+paTestData;
+
+/* This routine will be called by the PortAudio engine when audio is needed.
+** It may called at interrupt level on some machines so don't do anything
+** that could mess up the system like calling malloc() or free().
+*/
+static int patestCallback(   void *inputBuffer, void *outputBuffer,
+                             unsigned long framesPerBuffer,
+                             PaTimestamp outTime, void *userData )
+{
+    paTestData *data = (paTestData*)userData;
+    float *out = (float*)outputBuffer;
+    unsigned long i;
+    int j;
+    int finished = 0;
+    (void) outTime; /* Prevent unused variable warnings. */
+    (void) inputBuffer;
+
+    for( i=0; i<framesPerBuffer; i++ )
+    {
+        float output = 0.0;
+        double phaseInc = 0.02;
+        double phase;
+        for( j=0; j<data->numSines; j++ )
+        {
+            /* Advance phase of next oscillator. */
+            phase = data->phases[j];
+            phase += phaseInc;
+            if( phase > TWOPI ) phase -= TWOPI;
+
+            phaseInc *= 1.02;
+            if( phaseInc > 0.5 ) phaseInc *= 0.5;
+
+            /* This is not a very efficient way to calc sines. */
+            output += (float) sin( phase );
+            data->phases[j] = phase;
+        }
+
+
+        *out++ = (float) (output / data->numSines);
+    }
+    return finished;
+}
+
+/*******************************************************************/
+int main(void);
+int main(void)
+{
+    PortAudioStream *stream;
+    PaError err;
+    paTestData data = {0};
+    double load;
+    printf("PortAudio Test: output sine wave. SR = %d, BufSize = %d\n", SAMPLE_RATE, FRAMES_PER_BUFFER);
+    /* initialise sinusoidal wavetable */
+
+    err = Pa_Initialize();
+    if( err != paNoError ) goto error;
+    err = Pa_OpenStream(
+              &stream,
+              paNoDevice,/* default input device */
+              0,              /* no input */
+              paFloat32,  /* 32 bit floating point input */
+              NULL,
+              Pa_GetDefaultOutputDeviceID(), /* default output device */
+              1,          /* mono output */
+              paFloat32,      /* 32 bit floating point output */
+              NULL,
+              SAMPLE_RATE,
+              FRAMES_PER_BUFFER,            /* frames per buffer */
+              0,              /* number of buffers, if zero then use default minimum */
+              paClipOff,      /* we won't output out of range samples so don't bother clipping them */
+              patestCallback,
+              &data );
+    if( err != paNoError ) goto error;
+    err = Pa_StartStream( stream );
+    if( err != paNoError ) goto error;
+
+    do
+    {
+        data.numSines++;
+        Pa_Sleep( 200 );
+
+        load = Pa_GetCPULoad( stream );
+        printf("numSines = %d, CPU load = %f\n", data.numSines, load );
+    }
+    while( load < 0.8 );
+
+    err = Pa_StopStream( stream );
+    if( err != paNoError ) goto error;
+    err = Pa_CloseStream( stream );
+    if( err != paNoError ) goto error;
+    Pa_Terminate();
+    printf("Test finished.\n");
+    return err;
+error:
+    Pa_Terminate();
+    fprintf( stderr, "An error occured while using the portaudio stream\n" );
+    fprintf( stderr, "Error number: %d\n", err );
+    fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
+    return err;
+}
diff --git a/pa_tests/patest_pink.c b/pa_tests/patest_pink.c
new file mode 100644 (file)
index 0000000..c1fdece
--- /dev/null
@@ -0,0 +1,245 @@
+/*
+ * $Id$
+ patest_pink.c
+ Generate Pink Noise using Gardner method.
+ Optimization suggested by James McCartney uses a tree
+ to select which random value to replace.
+ x x x x x x x x x x x x x x x x 
+  x   x   x   x   x   x   x   x   
+    x       x       x       x       
+     x               x               
+       x                               
+ Tree is generated by counting trailing zeros in an increasing index.
+ When the index is zero, no random number is selected.
+ *
+ * Author: Phil Burk  http://www.softsynth.com
+ *
+ * This program uses the PortAudio Portable Audio Library.
+ * For more information see: http://www.portaudio.com
+ * Copyright (c) 1999-2000 Ross Bencina and 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.
+ *
+ */
+#include <stdio.h>
+#include <math.h>
+#include "portaudio.h"
+#define PINK_MAX_RANDOM_ROWS   (30)
+#define PINK_RANDOM_BITS       (24)
+#define PINK_RANDOM_SHIFT      ((sizeof(long)*8)-PINK_RANDOM_BITS)
+typedef struct
+{
+    long      pink_Rows[PINK_MAX_RANDOM_ROWS];
+    long      pink_RunningSum;   /* Used to optimize summing of generators. */
+    int       pink_Index;        /* Incremented each sample. */
+    int       pink_IndexMask;    /* Index wrapped by ANDing with this mask. */
+    float     pink_Scalar;       /* Used to scale within range of -1.0 to +1.0 */
+}
+PinkNoise;
+/* Prototypes */
+static unsigned long GenerateRandomNumber( void );
+void InitializePinkNoise( PinkNoise *pink, int numRows );
+float GeneratePinkNoise( PinkNoise *pink );
+/************************************************************/
+/* Calculate pseudo-random 32 bit number based on linear congruential method. */
+static unsigned long GenerateRandomNumber( void )
+{
+    /* Change this seed for different random sequences. */
+    static unsigned long randSeed = 22222;
+    randSeed = (randSeed * 196314165) + 907633515;
+    return randSeed;
+}
+/************************************************************/
+/* Setup PinkNoise structure for N rows of generators. */
+void InitializePinkNoise( PinkNoise *pink, int numRows )
+{
+    int i;
+    long pmax;
+    pink->pink_Index = 0;
+    pink->pink_IndexMask = (1<<numRows) - 1;
+    /* Calculate maximum possible signed random value. Extra 1 for white noise always added. */
+    pmax = (numRows + 1) * (1<<(PINK_RANDOM_BITS-1));
+    pink->pink_Scalar = 1.0f / pmax;
+    /* Initialize rows. */
+    for( i=0; i<numRows; i++ ) pink->pink_Rows[i] = 0;
+    pink->pink_RunningSum = 0;
+}
+#define PINK_MEASURE
+#ifdef PINK_MEASURE
+float pinkMax = -999.0;
+float pinkMin =  999.0;
+#endif
+/* Generate Pink noise values between -1.0 and +1.0 */
+float GeneratePinkNoise( PinkNoise *pink )
+{
+    long newRandom;
+    long sum;
+    float output;
+    /* Increment and mask index. */
+    pink->pink_Index = (pink->pink_Index + 1) & pink->pink_IndexMask;
+    /* If index is zero, don't update any random values. */
+    if( pink->pink_Index != 0 )
+    {
+        /* Determine how many trailing zeros in PinkIndex. */
+        /* This algorithm will hang if n==0 so test first. */
+        int numZeros = 0;
+        int n = pink->pink_Index;
+        while( (n & 1) == 0 )
+        {
+            n = n >> 1;
+            numZeros++;
+        }
+        /* Replace the indexed ROWS random value.
+         * Subtract and add back to RunningSum instead of adding all the random
+         * values together. Only one changes each time.
+         */
+        pink->pink_RunningSum -= pink->pink_Rows[numZeros];
+        newRandom = ((long)GenerateRandomNumber()) >> PINK_RANDOM_SHIFT;
+        pink->pink_RunningSum += newRandom;
+        pink->pink_Rows[numZeros] = newRandom;
+    }
+
+    /* Add extra white noise value. */
+    newRandom = ((long)GenerateRandomNumber()) >> PINK_RANDOM_SHIFT;
+    sum = pink->pink_RunningSum + newRandom;
+    /* Scale to range of -1.0 to 0.9999. */
+    output = pink->pink_Scalar * sum;
+#ifdef PINK_MEASURE
+    /* Check Min/Max */
+    if( output > pinkMax ) pinkMax = output;
+    else if( output < pinkMin ) pinkMin = output;
+#endif
+    return output;
+}
+/*******************************************************************/
+#define PINK_TEST
+#ifdef PINK_TEST
+/* Context for callback routine. */
+typedef struct
+{
+    PinkNoise   leftPink;
+    PinkNoise   rightPink;
+    unsigned int sampsToGo;
+}
+paTestData;
+/* This routine will be called by the PortAudio engine when audio is needed.
+** It may called at interrupt level on some machines so don't do anything
+** that could mess up the system like calling malloc() or free().
+*/
+static int patestCallback( void *inputBuffer, void *outputBuffer,
+                           unsigned long framesPerBuffer,
+                           PaTimestamp outTime, void *userData )
+{
+    int finished;
+    int i;
+    int numFrames;
+    paTestData *data = (paTestData*)userData;
+    float *out = (float*)outputBuffer;
+    (void) inputBuffer; /* Prevent "unused variable" warnings. */
+    (void) outTime;
+
+    /* Are we almost at end. */
+    if( data->sampsToGo < framesPerBuffer )
+    {
+        numFrames = data->sampsToGo;
+        finished = 1;
+    }
+    else
+    {
+        numFrames = framesPerBuffer;
+        finished = 0;
+    }
+    for( i=0; i<numFrames; i++ )
+    {
+        *out++ = GeneratePinkNoise( &data->leftPink );
+        *out++ = GeneratePinkNoise( &data->rightPink );
+    }
+    data->sampsToGo -= numFrames;
+    return finished;
+}
+/*******************************************************************/
+int main(void);
+int main(void)
+{
+    PortAudioStream *stream;
+    PaError err;
+    paTestData data;
+    int totalSamps;
+    /* Initialize two pink noise signals with different numbers of rows. */
+    InitializePinkNoise( &data.leftPink, 12 );
+    InitializePinkNoise( &data.rightPink, 16 );
+    /* Look at a few values. */
+    {
+        int i;
+        float pink;
+        for( i=0; i<20; i++ )
+        {
+            pink = GeneratePinkNoise( &data.leftPink );
+            printf("Pink = %f\n", pink );
+        }
+    }
+    data.sampsToGo = totalSamps = 8*44100; /* Play for a few seconds. */
+    err = Pa_Initialize();
+    if( err != paNoError ) goto error;
+    /* Open a stereo PortAudio stream so we can hear the result. */
+    err = Pa_OpenStream(
+              &stream,
+              paNoDevice,
+              0,              /* no input */
+              paFloat32,  /* 32 bit floating point input */
+              NULL,
+              Pa_GetDefaultOutputDeviceID(), /* default output device */
+              2,          /* stereo output */
+              paFloat32,      /* 32 bit floating point output */
+              NULL,
+              44100.,
+              2048,           /* 46 msec buffers */
+              0,              /* number of buffers, if zero then use default minimum */
+              paClipOff,      /* we won't output out of range samples so don't bother clipping them */
+              patestCallback,
+              &data );
+    if( err != paNoError ) goto error;
+
+    err = Pa_StartStream( stream );
+    if( err != paNoError ) goto error;
+    printf("Waiting for sound to finish.\n");
+    while( Pa_StreamActive( stream ) )
+    {
+        Pa_Sleep(100); /* SPIN! */
+    }
+    err = Pa_CloseStream( stream );
+    if( err != paNoError ) goto error;
+#ifdef PINK_MEASURE
+    printf("Pink min = %f, max = %f\n", pinkMin, pinkMax );
+#endif
+    Pa_Terminate();
+    return 0;
+error:
+    Pa_Terminate();
+    fprintf( stderr, "An error occured while using the portaudio stream\n" );
+    fprintf( stderr, "Error number: %d\n", err );
+    fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
+    return 0;
+}
+#endif /* PINK_TEST */
diff --git a/pa_tests/patest_record.c b/pa_tests/patest_record.c
new file mode 100644 (file)
index 0000000..47ccec1
--- /dev/null
@@ -0,0 +1,303 @@
+/*
+ * $Id$
+ * patest_record.c
+ * Record input into an array.
+ * Save array to a file.
+ * Playback recorded data.
+ *
+ * Author: Phil Burk  http://www.softsynth.com
+ *
+ * This program uses the PortAudio Portable Audio Library.
+ * For more information see: http://www.portaudio.com
+ * Copyright (c) 1999-2000 Ross Bencina and 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.
+ *
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include "portaudio.h"
+
+/* #define SAMPLE_RATE  (17932) /* Test failure to open with this value. */
+#define SAMPLE_RATE  (44100)
+#define NUM_SECONDS     (5)
+
+/* Select sample format. */
+#if 0
+#define PA_SAMPLE_TYPE  paFloat32
+typedef float SAMPLE;
+#elif 0
+#define PA_SAMPLE_TYPE  paInt16
+typedef short SAMPLE;
+#else
+#define PA_SAMPLE_TYPE  paUInt8
+typedef unsigned char SAMPLE;
+#endif
+
+typedef struct
+{
+    int          frameIndex;  /* Index into sample array. */
+    int          maxFrameIndex;
+    int          samplesPerFrame;
+    SAMPLE      *recordedSamples;
+}
+paTestData;
+/* This routine will be called by the PortAudio engine when audio is needed.
+** It may be called at interrupt level on some machines so don't do anything
+** that could mess up the system like calling malloc() or free().
+*/
+static int recordCallback( void *inputBuffer, void *outputBuffer,
+                           unsigned long framesPerBuffer,
+                           PaTimestamp outTime, void *userData )
+{
+    paTestData *data = (paTestData*)userData;
+    SAMPLE *rptr = (SAMPLE*)inputBuffer;
+    SAMPLE *wptr = &data->recordedSamples[data->frameIndex * data->samplesPerFrame];
+    long framesToCalc;
+    long i;
+    int finished;
+    unsigned long framesLeft = data->maxFrameIndex - data->frameIndex;
+
+    (void) outputBuffer; /* Prevent unused variable warnings. */
+    (void) outTime;
+
+    if( framesLeft < framesPerBuffer )
+    {
+        framesToCalc = framesLeft;
+        finished = 1;
+    }
+    else
+    {
+        framesToCalc = framesPerBuffer;
+        finished = 0;
+    }
+    if( inputBuffer == NULL )
+    {
+        for( i=0; i<framesToCalc; i++ )
+        {
+            *wptr++ = 0;  /* left */
+            *wptr++ = 0;  /* right */
+        }
+    }
+    else
+    {
+        for( i=0; i<framesToCalc; i++ )
+        {
+            *wptr++ = *rptr++;  /* left */
+            *wptr++ = *rptr++;  /* right */
+        }
+    }
+    data->frameIndex += framesToCalc;
+    return finished;
+}
+
+/* This routine will be called by the PortAudio engine when audio is needed.
+** It may be called at interrupt level on some machines so don't do anything
+** that could mess up the system like calling malloc() or free().
+*/
+static int playCallback( void *inputBuffer, void *outputBuffer,
+                         unsigned long framesPerBuffer,
+                         PaTimestamp outTime, void *userData )
+{
+    paTestData *data = (paTestData*)userData;
+    SAMPLE *rptr = &data->recordedSamples[data->frameIndex * data->samplesPerFrame];
+    SAMPLE *wptr = (SAMPLE*)outputBuffer;
+    unsigned int i;
+    int finished;
+    unsigned int framesLeft = data->maxFrameIndex - data->frameIndex;
+    (void) inputBuffer; /* Prevent unused variable warnings. */
+    (void) outTime;
+
+    if( framesLeft < framesPerBuffer )
+    {
+        /* final buffer... */
+        for( i=0; i<framesLeft; i++ )
+        {
+            *wptr++ = *rptr++;  /* left */
+            *wptr++ = *rptr++;  /* right */
+        }
+        for( ; i<framesPerBuffer; i++ )
+        {
+            *wptr++ = 0;  /* left */
+            *wptr++ = 0;  /* right */
+        }
+        data->frameIndex += framesLeft;
+        finished = 1;
+    }
+    else
+    {
+        for( i=0; i<framesPerBuffer; i++ )
+        {
+            *wptr++ = *rptr++;  /* left */
+            *wptr++ = *rptr++;  /* right */
+        }
+        data->frameIndex += framesPerBuffer;
+        finished = 0;
+    }
+    return finished;
+}
+
+/*******************************************************************/
+int main(void);
+int main(void)
+{
+    PortAudioStream *stream;
+    PaError    err;
+    paTestData data;
+    int        i;
+    int        totalFrames;
+    int        numSamples;
+    int        numBytes;
+    SAMPLE     max, average, val;
+    printf("patest_record.c\n"); fflush(stdout);
+
+    data.maxFrameIndex = totalFrames = NUM_SECONDS * SAMPLE_RATE; /* Record for a few seconds. */
+    data.frameIndex = 0;
+    data.samplesPerFrame = 2;
+    numSamples = totalFrames * data.samplesPerFrame;
+
+    numBytes = numSamples * sizeof(SAMPLE);
+    data.recordedSamples = (SAMPLE *) malloc( numBytes );
+    if( data.recordedSamples == NULL )
+    {
+        printf("Could not allocate record array.\n");
+        exit(1);
+    }
+    for( i=0; i<numSamples; i++ ) data.recordedSamples[i] = 0;
+
+    err = Pa_Initialize();
+    if( err != paNoError ) goto error;
+
+    /* Record some audio. -------------------------------------------- */
+    err = Pa_OpenStream(
+              &stream,
+              Pa_GetDefaultInputDeviceID(),
+              data.samplesPerFrame,               /* stereo input */
+              PA_SAMPLE_TYPE,
+              NULL,
+              paNoDevice,
+              0,
+              PA_SAMPLE_TYPE,
+              NULL,
+              SAMPLE_RATE,
+              1024,            /* frames per buffer */
+              0,               /* number of buffers, if zero then use default minimum */
+              paClipOff,  /* we won't output out of range samples so don't bother clipping them */
+              recordCallback,
+              &data );
+    if( err != paNoError ) goto error;
+
+    err = Pa_StartStream( stream );
+    if( err != paNoError ) goto error;
+    printf("Now recording!!\n"); fflush(stdout);
+
+    while( Pa_StreamActive( stream ) )
+    {
+        Pa_Sleep(1000);
+        printf("index = %d\n", data.frameIndex ); fflush(stdout);
+    }
+
+    err = Pa_CloseStream( stream );
+    if( err != paNoError ) goto error;
+
+    /* Measure maximum peak amplitude. */
+    max = 0;
+    average = 0;
+    for( i=0; i<numSamples; i++ )
+    {
+        val = data.recordedSamples[i];
+        if( val < 0 ) val = -val; /* ABS */
+        if( val > max )
+        {
+            max = val;
+        }
+        average += val;
+    }
+    printf("sample max amplitude = %d\n", max );
+    average = average / numSamples;
+    printf("sample average = %d\n", average );
+
+    /* Write recorded data to a file. */
+#if 0
+    {
+        FILE  *fid;
+        fid = fopen("recorded.raw", "wb");
+        if( fid == NULL )
+        {
+            printf("Could not open file.");
+        }
+        else
+        {
+            fwrite( data.recordedSamples, data.samplesPerFrame * sizeof(SAMPLE), totalFrames, fid );
+            fclose( fid );
+            printf("Wrote data to 'recorded.raw'\n");
+        }
+    }
+#endif
+
+    /* Playback recorded data.  -------------------------------------------- */
+    data.frameIndex = 0;
+    printf("Begin playback.\n"); fflush(stdout);
+    err = Pa_OpenStream(
+              &stream,
+              paNoDevice,
+              0,               /* NO input */
+              PA_SAMPLE_TYPE,
+              NULL,
+              Pa_GetDefaultOutputDeviceID(),
+              data.samplesPerFrame,               /* stereo output */
+              PA_SAMPLE_TYPE,
+              NULL,
+              SAMPLE_RATE,
+              1024,            /* frames per buffer */
+              0,               /* number of buffers, if zero then use default minimum */
+              paClipOff,       /* we won't output out of range samples so don't bother clipping them */
+              playCallback,
+              &data );
+    if( err != paNoError ) goto error;
+
+    if( stream )
+    {
+        err = Pa_StartStream( stream );
+        if( err != paNoError ) goto error;
+        printf("Waiting for playback to finish.\n"); fflush(stdout);
+
+        while( Pa_StreamActive( stream ) ) Pa_Sleep(100);
+
+        err = Pa_CloseStream( stream );
+        if( err != paNoError ) goto error;
+        printf("Done.\n"); fflush(stdout);
+    }
+    free( data.recordedSamples );
+
+    Pa_Terminate();
+    return 0;
+
+error:
+    Pa_Terminate();
+    fprintf( stderr, "An error occured while using the portaudio stream\n" );
+    fprintf( stderr, "Error number: %d\n", err );
+    fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
+    return -1;
+}
diff --git a/pa_tests/patest_ringmix.c b/pa_tests/patest_ringmix.c
new file mode 100644 (file)
index 0000000..0bae220
--- /dev/null
@@ -0,0 +1,41 @@
+/* $Id$ */
+
+#include "stdio.h"
+#include "portaudio.h"
+/* This will be called asynchronously by the PortAudio engine. */
+static int myCallback( void *inputBuffer, void *outputBuffer,
+                       unsigned long framesPerBuffer, PaTimestamp outTime, void *userData )
+{
+    float *out = (float *) outputBuffer;
+    float *in  = (float *) inputBuffer;
+    float leftInput, rightInput;
+    unsigned int i;
+    if( inputBuffer == NULL ) return 0;
+    /* Read input buffer, process data, and fill output buffer. */
+    for( i=0; i<framesPerBuffer; i++ )
+    {
+        leftInput = *in++;      /* Get interleaved samples from input buffer. */
+        rightInput = *in++;
+        *out++ = leftInput * rightInput;            /* ring modulation */
+        *out++ = 0.5f * (leftInput + rightInput);   /* mix */
+    }
+    return 0;
+}
+/* Open a PortAudioStream to input and output audio data. */
+int main(void)
+{
+    PortAudioStream *stream;
+    Pa_Initialize();
+    Pa_OpenDefaultStream(
+        &stream,
+        2, 2,            /* stereo input and output */
+        paFloat32,  44100.0,
+        64,  0,          /* 64 frames per buffer, let PA determine numBuffers */
+        myCallback, NULL );
+    Pa_StartStream( stream );
+    Pa_Sleep( 10000 );    /* Sleep for 10 seconds while processing. */
+    Pa_StopStream( stream );
+    Pa_CloseStream( stream );
+    Pa_Terminate();
+    return 0;
+}
diff --git a/pa_tests/patest_saw.c b/pa_tests/patest_saw.c
new file mode 100644 (file)
index 0000000..4c25d2b
--- /dev/null
@@ -0,0 +1,118 @@
+/*
+ * $Id$
+ * patest_saw.c
+ * Play a simple sawtooth wave.
+ *
+ * Author: Phil Burk  http://www.softsynth.com
+ *
+ * This program uses the PortAudio Portable Audio Library.
+ * For more information see: http://www.portaudio.com
+ * Copyright (c) 1999-2000 Ross Bencina and 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.
+ *
+ */
+#include <stdio.h>
+#include <math.h>
+#include "portaudio.h"
+#define NUM_SECONDS   (4)
+#define SAMPLE_RATE   (44100)
+typedef struct
+{
+    float left_phase;
+    float right_phase;
+}
+paTestData;
+/* This routine will be called by the PortAudio engine when audio is needed.
+** It may called at interrupt level on some machines so don't do anything
+** that could mess up the system like calling malloc() or free().
+*/
+static int patestCallback( void *inputBuffer, void *outputBuffer,
+                           unsigned long framesPerBuffer,
+                           PaTimestamp outTime, void *userData )
+{
+    /* Cast data passed through stream to our structure. */
+    paTestData *data = (paTestData*)userData;
+    float *out = (float*)outputBuffer;
+    unsigned int i;
+    (void) outTime; /* Prevent unused variable warnings. */
+    (void) inputBuffer;
+
+    for( i=0; i<framesPerBuffer; i++ )
+    {
+        *out++ = data->left_phase;  /* left */
+        *out++ = data->right_phase;  /* right */
+        /* Generate simple sawtooth phaser that ranges between -1.0 and 1.0. */
+        data->left_phase += 0.01f;
+        /* When signal reaches top, drop back down. */
+        if( data->left_phase >= 1.0f ) data->left_phase -= 2.0f;
+        /* higher pitch so we can distinguish left and right. */
+        data->right_phase += 0.03f;
+        if( data->right_phase >= 1.0f ) data->right_phase -= 2.0f;
+    }
+    return 0;
+}
+/*******************************************************************/
+static paTestData data;
+int main(void);
+int main(void)
+{
+    PortAudioStream *stream;
+    PaError err;
+    printf("PortAudio Test: output sawtooth wave.\n");
+    /* Initialize our data for use by callback. */
+    data.left_phase = data.right_phase = 0.0;
+    /* Initialize library before making any other calls. */
+    err = Pa_Initialize();
+    if( err != paNoError ) goto error;
+    /* Open an audio I/O stream. */
+    err = Pa_OpenDefaultStream(
+              &stream,
+              0,              /* no input channels */
+              2,              /* stereo output */
+              paFloat32,      /* 32 bit floating point output */
+              SAMPLE_RATE,
+              256,            /* frames per buffer */
+              0,              /* number of buffers, if zero then use default minimum */
+              patestCallback,
+              &data );
+    if( err != paNoError ) goto error;
+    err = Pa_StartStream( stream );
+    if( err != paNoError ) goto error;
+    /* Sleep for several seconds. */
+    Pa_Sleep(NUM_SECONDS*1000);
+    err = Pa_StopStream( stream );
+    if( err != paNoError ) goto error;
+    err = Pa_CloseStream( stream );
+    if( err != paNoError ) goto error;
+    Pa_Terminate();
+    printf("Test finished.\n");
+    return err;
+error:
+    Pa_Terminate();
+    fprintf( stderr, "An error occured while using the portaudio stream\n" );
+    fprintf( stderr, "Error number: %d\n", err );
+    fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
+    return err;
+}
diff --git a/pa_tests/patest_sine.c b/pa_tests/patest_sine.c
new file mode 100644 (file)
index 0000000..f6d8f6a
--- /dev/null
@@ -0,0 +1,138 @@
+/*
+ * $Id$
+ * $Id$
+ * patest_sine.c
+ * Play a sine wave using the Portable Audio api for several seconds.
+ *
+ * Authors:
+ *    Ross Bencina <rossb@audiomulch.com>
+ *    Phil Burk <philburk@softsynth.com>
+ *
+ * This program uses the PortAudio Portable Audio Library.
+ * For more information see: http://www.audiomulch.com/portaudio/
+ * Copyright (c) 1999-2000 Ross Bencina and 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.
+ *
+ */
+#include <stdio.h>
+#include <math.h>
+#include "portaudio.h"
+
+#define NUM_SECONDS   (5)
+#define SAMPLE_RATE   (44100)
+#define FRAMES_PER_BUFFER  (64)
+
+#ifndef M_PI
+#define M_PI  (3.14159265)
+#endif
+
+#define TABLE_SIZE   (200)
+typedef struct
+{
+    float sine[TABLE_SIZE];
+    int left_phase;
+    int right_phase;
+}
+paTestData;
+
+/* This routine will be called by the PortAudio engine when audio is needed.
+** It may called at interrupt level on some machines so don't do anything
+** that could mess up the system like calling malloc() or free().
+*/
+static int patestCallback(   void *inputBuffer, void *outputBuffer,
+                             unsigned long framesPerBuffer,
+                             PaTimestamp outTime, void *userData )
+{
+    paTestData *data = (paTestData*)userData;
+    float *out = (float*)outputBuffer;
+    unsigned long i;
+    int finished = 0;
+    (void) outTime; /* Prevent unused variable warnings. */
+    (void) inputBuffer;
+    for( i=0; i<framesPerBuffer; i++ )
+    {
+        *out++ = data->sine[data->left_phase];  /* left */
+        *out++ = data->sine[data->right_phase];  /* right */
+        data->left_phase += 1;
+        if( data->left_phase >= TABLE_SIZE ) data->left_phase -= TABLE_SIZE;
+        data->right_phase += 3; /* higher pitch so we can distinguish left and right. */
+        if( data->right_phase >= TABLE_SIZE ) data->right_phase -= TABLE_SIZE;
+    }
+    return finished;
+}
+
+/*******************************************************************/
+int main(void);
+int main(void)
+{
+    PortAudioStream *stream;
+    PaError err;
+    paTestData data;
+    int i;
+    printf("PortAudio Test: output sine wave. SR = %d, BufSize = %d\n", SAMPLE_RATE, FRAMES_PER_BUFFER);
+    /* initialise sinusoidal wavetable */
+    for( i=0; i<TABLE_SIZE; i++ )
+    {
+        data.sine[i] = (float) sin( ((double)i/(double)TABLE_SIZE) * M_PI * 2. );
+    }
+    data.left_phase = data.right_phase = 0;
+    err = Pa_Initialize();
+    if( err != paNoError ) goto error;
+    err = Pa_OpenStream(
+              &stream,
+              paNoDevice,/* default input device */
+              0,              /* no input */
+              paFloat32,  /* 32 bit floating point input */
+              NULL,
+              Pa_GetDefaultOutputDeviceID(), /* default output device */
+              2,          /* stereo output */
+              paFloat32,      /* 32 bit floating point output */
+              NULL,
+              SAMPLE_RATE,
+              FRAMES_PER_BUFFER,
+              0,              /* number of buffers, if zero then use default minimum */
+              paClipOff,      /* we won't output out of range samples so don't bother clipping them */
+              patestCallback,
+              &data );
+    if( err != paNoError ) goto error;
+    err = Pa_StartStream( stream );
+    if( err != paNoError ) goto error;
+    printf("Play for %d seconds.\n", NUM_SECONDS );
+    Pa_Sleep( NUM_SECONDS * 1000 );
+
+    err = Pa_StopStream( stream );
+    if( err != paNoError ) goto error;
+    err = Pa_CloseStream( stream );
+    if( err != paNoError ) goto error;
+    Pa_Terminate();
+    printf("Test finished.\n");
+    return err;
+error:
+    Pa_Terminate();
+    fprintf( stderr, "An error occured while using the portaudio stream\n" );
+    fprintf( stderr, "Error number: %d\n", err );
+    fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
+    return err;
+}
diff --git a/pa_tests/patest_sine8.c b/pa_tests/patest_sine8.c
new file mode 100644 (file)
index 0000000..73154c9
--- /dev/null
@@ -0,0 +1,184 @@
+/*
+ * $Id$
+ * patest_sine8.c
+ * Play a sine wave using the Portable Audio api for several seconds.
+ * Test 8 bit data.
+ *
+ * Author: Ross Bencina <rossb@audiomulch.com>
+ *
+ * This program uses the PortAudio Portable Audio Library.
+ * For more information see: http://www.audiomulch.com/portaudio/
+ * Copyright (c) 1999-2000 Ross Bencina and 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.
+ *
+ */
+#include <stdio.h>
+#include <math.h>
+#include "portaudio.h"
+#define NUM_SECONDS   (8)
+#define SAMPLE_RATE   (44100)
+#define TEST_UNSIGNED    (1)
+#if TEST_UNSIGNED
+#define TEST_FORMAT     paUInt8
+#else
+#define TEST_FORMAT     paInt8
+#endif
+#ifndef M_PI
+#define M_PI  (3.14159265)
+#endif
+#define TABLE_SIZE   (200)
+typedef struct
+{
+#if TEST_UNSIGNED
+    unsigned char sine[TABLE_SIZE];
+#else
+    char sine[TABLE_SIZE];
+#endif
+    int left_phase;
+    int right_phase;
+    unsigned int framesToGo;
+}
+paTestData;
+/* This routine will be called by the PortAudio engine when audio is needed.
+** It may called at interrupt level on some machines so don't do anything
+** that could mess up the system like calling malloc() or free().
+*/
+static int patestCallback( void *inputBuffer, void *outputBuffer,
+                           unsigned long framesPerBuffer,
+                           PaTimestamp outTime, void *userData )
+{
+    paTestData *data = (paTestData*)userData;
+    char *out = (char*)outputBuffer;
+    int i;
+    int framesToCalc;
+    int finished = 0;
+    (void) outTime; /* Prevent unused variable warnings. */
+    (void) inputBuffer;
+
+    if( data->framesToGo < framesPerBuffer )
+    {
+        framesToCalc = data->framesToGo;
+        data->framesToGo = 0;
+        finished = 1;
+    }
+    else
+    {
+        framesToCalc = framesPerBuffer;
+        data->framesToGo -= framesPerBuffer;
+    }
+
+    for( i=0; i<framesToCalc; i++ )
+    {
+        *out++ = data->sine[data->left_phase];  /* left */
+        *out++ = data->sine[data->right_phase];  /* right */
+        data->left_phase += 1;
+        if( data->left_phase >= TABLE_SIZE ) data->left_phase -= TABLE_SIZE;
+        data->right_phase += 3; /* higher pitch so we can distinguish left and right. */
+        if( data->right_phase >= TABLE_SIZE ) data->right_phase -= TABLE_SIZE;
+    }
+    /* zero remainder of final buffer */
+    for( ; i<(int)framesPerBuffer; i++ )
+    {
+#if TEST_UNSIGNED
+        *out++ = (unsigned char) 0x80; /* left */
+        *out++ = (unsigned char) 0x80; /* right */
+#else
+        *out++ = 0; /* left */
+        *out++ = 0; /* right */
+#endif
+
+    }
+    return finished;
+}
+/*******************************************************************/
+int main(void);
+int main(void)
+{
+    PortAudioStream *stream;
+    PaError err;
+    paTestData data;
+    int i;
+    int totalSamps;
+#if TEST_UNSIGNED
+    printf("PortAudio Test: output UNsigned 8 bit sine wave.\n");
+#else
+    printf("PortAudio Test: output signed 8 bit sine wave.\n");
+#endif
+    /* initialise sinusoidal wavetable */
+    for( i=0; i<TABLE_SIZE; i++ )
+    {
+        data.sine[i] = (char) (127.0 * sin( ((double)i/(double)TABLE_SIZE) * M_PI * 2. ));
+#if TEST_UNSIGNED
+        data.sine[i] += (unsigned char) 0x80;
+#endif
+
+    }
+    data.left_phase = data.right_phase = 0;
+    data.framesToGo = totalSamps =  NUM_SECONDS * SAMPLE_RATE; /* Play for a few seconds. */
+    err = Pa_Initialize();
+    if( err != paNoError ) goto error;
+    err = Pa_OpenStream(
+              &stream,
+              paNoDevice,/* default input device */
+              0,              /* no input */
+              TEST_FORMAT,
+              NULL,
+              Pa_GetDefaultOutputDeviceID(), /* default output device */
+              2,          /* stereo output */
+              TEST_FORMAT,
+              NULL,
+              SAMPLE_RATE,
+              256,            /* frames per buffer */
+              0,              /* number of buffers, if zero then use default minimum */
+              paClipOff,      /* we won't output out of range samples so don't bother clipping them */
+              patestCallback,
+              &data );
+    if( err != paNoError ) goto error;
+    err = Pa_StartStream( stream );
+    if( err != paNoError ) goto error;
+    /* Watch until sound is halfway finished. */
+    while( Pa_StreamTime( stream ) < (totalSamps/2) ) Pa_Sleep(10);
+    /* Stop sound until ENTER hit. */
+    err = Pa_StopStream( stream );
+    if( err != paNoError ) goto error;
+    printf("Pause for 2 seconds.\n");
+    Pa_Sleep( 2000 );
+
+    err = Pa_StartStream( stream );
+    if( err != paNoError ) goto error;
+    printf("Waiting for sound to finish.\n");
+    while( Pa_StreamActive( stream ) ) Pa_Sleep(10);
+    err = Pa_CloseStream( stream );
+    if( err != paNoError ) goto error;
+    Pa_Terminate();
+    printf("Test finished.\n");
+    return err;
+error:
+    Pa_Terminate();
+    fprintf( stderr, "An error occured while using the portaudio stream\n" );
+    fprintf( stderr, "Error number: %d\n", err );
+    fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
+    return err;
+}
diff --git a/pa_tests/patest_sine_formats.c b/pa_tests/patest_sine_formats.c
new file mode 100644 (file)
index 0000000..09abb7b
--- /dev/null
@@ -0,0 +1,198 @@
+/*
+ * $Id$
+ * patest_sine_formats.c
+ * Play a sine wave using the Portable Audio api for several seconds.
+ * Test various data formats.
+ *
+ * Author: Phil Burk
+ *
+ * This program uses the PortAudio Portable Audio Library.
+ * For more information see: http://www.audiomulch.com/portaudio/
+ * Copyright (c) 1999-2000 Ross Bencina and 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.
+ *
+ */
+#include <stdio.h>
+#include <math.h>
+#include "portaudio.h"
+
+#define NUM_SECONDS        (20)
+#define SAMPLE_RATE        (44100)
+#define FRAMES_PER_BUFFER  (256)
+#define LEFT_FREQ          (SAMPLE_RATE/256.0)  /* So we hit 1.0 */
+#define RIGHT_FREQ         (500.0)
+#define AMPLITUDE          (1.0)
+
+/* Select ONE format for testing. */
+#define TEST_UINT8    (0)
+#define TEST_INT8     (0)
+#define TEST_INT16    (1)
+#define TEST_FLOAT32  (0)
+
+#if TEST_UINT8
+#define TEST_FORMAT         paUInt8
+typedef unsigned char       SAMPLE_t;
+#define SAMPLE_ZERO         (0x80)
+#define DOUBLE_TO_SAMPLE(x) (SAMPLE_ZERO + (SAMPLE_t)(127.0 * (x)))
+#define FORMAT_NAME         "Unsigned 8 Bit"
+#endif
+
+#if TEST_INT8
+#define TEST_FORMAT         paInt8
+typedef char                SAMPLE_t;
+#define SAMPLE_ZERO         (0)
+#define DOUBLE_TO_SAMPLE(x) (SAMPLE_ZERO + (SAMPLE_t)(127.0 * (x)))
+#define FORMAT_NAME         "Signed 8 Bit"
+#endif
+
+#if TEST_INT16
+#define TEST_FORMAT         paInt16
+typedef short               SAMPLE_t;
+#define SAMPLE_ZERO         (0)
+#define DOUBLE_TO_SAMPLE(x) (SAMPLE_ZERO + (SAMPLE_t)(32767.0 * (x)))
+#define FORMAT_NAME         "Signed 16 Bit"
+#endif
+
+#if TEST_FLOAT32
+#define TEST_FORMAT         paFloat32
+typedef float               SAMPLE_t;
+#define SAMPLE_ZERO         (0.0)
+#define DOUBLE_TO_SAMPLE(x) ((SAMPLE_t)(x))
+#define FORMAT_NAME         "Float 32 Bit"
+#endif
+
+#ifndef M_PI
+#define M_PI  (3.14159265)
+#endif
+
+
+typedef struct
+{
+    double left_phase;
+    double right_phase;
+    unsigned int framesToGo;
+}
+paTestData;
+/* This routine will be called by the PortAudio engine when audio is needed.
+** It may called at interrupt level on some machines so don't do anything
+** that could mess up the system like calling malloc() or free().
+*/
+static int patestCallback( void *inputBuffer, void *outputBuffer,
+                           unsigned long framesPerBuffer,
+                           PaTimestamp outTime, void *userData )
+{
+    paTestData *data = (paTestData*)userData;
+    SAMPLE_t *out = (SAMPLE_t *)outputBuffer;
+    int i;
+    int framesToCalc;
+    int finished = 0;
+    (void) outTime; /* Prevent unused variable warnings. */
+    (void) inputBuffer;
+
+    if( data->framesToGo < framesPerBuffer )
+    {
+        framesToCalc = data->framesToGo;
+        data->framesToGo = 0;
+        finished = 1;
+    }
+    else
+    {
+        framesToCalc = framesPerBuffer;
+        data->framesToGo -= framesPerBuffer;
+    }
+
+    for( i=0; i<framesToCalc; i++ )
+    {
+        data->left_phase += (LEFT_FREQ / SAMPLE_RATE);
+        if( data->left_phase > 1.0) data->left_phase -= 1.0;
+        *out++ = DOUBLE_TO_SAMPLE( AMPLITUDE * sin( (data->left_phase * M_PI * 2. )));
+
+        data->right_phase += (RIGHT_FREQ / SAMPLE_RATE);
+        if( data->right_phase > 1.0) data->right_phase -= 1.0;
+        *out++ = DOUBLE_TO_SAMPLE( AMPLITUDE * sin( (data->right_phase * M_PI * 2. )));
+    }
+    /* zero remainder of final buffer */
+    for( ; i<(int)framesPerBuffer; i++ )
+    {
+        *out++ = SAMPLE_ZERO; /* left */
+        *out++ = SAMPLE_ZERO; /* right */
+    }
+    return finished;
+}
+/*******************************************************************/
+int main(void);
+int main(void)
+{
+    PortAudioStream *stream;
+    PaError err;
+    paTestData data;
+    int totalSamps;
+
+    printf("PortAudio Test: output " FORMAT_NAME "\n");
+
+
+    data.left_phase = data.right_phase = 0.0;
+    data.framesToGo = totalSamps =  NUM_SECONDS * SAMPLE_RATE; /* Play for a few seconds. */
+    err = Pa_Initialize();
+    if( err != paNoError ) goto error;
+
+    err = Pa_OpenStream(
+              &stream,
+              paNoDevice,/* default input device */
+              0,              /* no input */
+              TEST_FORMAT,
+              NULL,
+              Pa_GetDefaultOutputDeviceID(), /* default output device */
+              2,          /* stereo output */
+              TEST_FORMAT,
+              NULL,
+              SAMPLE_RATE,
+              FRAMES_PER_BUFFER,
+              0,              /* number of buffers, if zero then use default minimum */
+              paClipOff,      /* we won't output out of range samples so don't bother clipping them */
+              patestCallback,
+              &data );
+    if( err != paNoError ) goto error;
+
+    err = Pa_StartStream( stream );
+    if( err != paNoError ) goto error;
+
+    printf("Waiting %d seconds for sound to finish.\n", NUM_SECONDS );
+    while( Pa_StreamActive( stream ) ) Pa_Sleep(10);
+
+    err = Pa_CloseStream( stream );
+    if( err != paNoError ) goto error;
+    Pa_Terminate();
+
+    printf("PortAudio Test Finished: " FORMAT_NAME "\n");
+
+    return err;
+error:
+    Pa_Terminate();
+    fprintf( stderr, "An error occured while using the portaudio stream\n" );
+    fprintf( stderr, "Error number: %d\n", err );
+    fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
+    return err;
+}
diff --git a/pa_tests/patest_sine_time.c b/pa_tests/patest_sine_time.c
new file mode 100644 (file)
index 0000000..9b9cf3b
--- /dev/null
@@ -0,0 +1,165 @@
+/*
+ * $Id$
+ * patest_sine_time.c
+ * Play a sine wave using the Portable Audio api for several seconds.
+ * Pausing in the middle.
+ * use the Pa_StreamTime() and Pa_StreamActive() calls.
+ *
+ * Authors:
+ *    Ross Bencina <rossb@audiomulch.com>
+ *    Phil Burk <philburk@softsynth.com>
+ *
+ * This program uses the PortAudio Portable Audio Library.
+ * For more information see: http://www.audiomulch.com/portaudio/
+ * Copyright (c) 1999-2000 Ross Bencina and 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.
+ *
+ */
+#include <stdio.h>
+#include <math.h>
+#include "portaudio.h"
+#define NUM_SECONDS   (8)
+#define SAMPLE_RATE   (44100)
+#define FRAMES_PER_BUFFER  (64)
+#ifndef M_PI
+#define M_PI  (3.14159265)
+#endif
+#define TABLE_SIZE   (200)
+typedef struct
+{
+    float sine[TABLE_SIZE];
+    int left_phase;
+    int right_phase;
+    int framesToGo;
+}
+paTestData;
+/* This routine will be called by the PortAudio engine when audio is needed.
+** It may called at interrupt level on some machines so don't do anything
+** that could mess up the system like calling malloc() or free().
+*/
+static int patestCallback(   void *inputBuffer, void *outputBuffer,
+                             unsigned long framesPerBuffer,
+                             PaTimestamp outTime, void *userData )
+{
+    paTestData *data = (paTestData*)userData;
+    float *out = (float*)outputBuffer;
+    int i;
+    int framesToCalc;
+    int finished = 0;
+    (void) outTime; /* Prevent unused variable warnings. */
+    (void) inputBuffer;
+
+    if( data->framesToGo < framesPerBuffer )
+    {
+        framesToCalc = data->framesToGo;
+        data->framesToGo = 0;
+        finished = 1;
+    }
+    else
+    {
+        framesToCalc = framesPerBuffer;
+        data->framesToGo -= framesPerBuffer;
+    }
+
+    for( i=0; i<framesToCalc; i++ )
+    {
+        *out++ = data->sine[data->left_phase];  /* left */
+        *out++ = data->sine[data->right_phase];  /* right */
+        data->left_phase += 1;
+        if( data->left_phase >= TABLE_SIZE ) data->left_phase -= TABLE_SIZE;
+        data->right_phase += 3; /* higher pitch so we can distinguish left and right. */
+        if( data->right_phase >= TABLE_SIZE ) data->right_phase -= TABLE_SIZE;
+    }
+    /* zero remainder of final buffer */
+    for( ; i<(int)framesPerBuffer; i++ )
+    {
+        *out++ = 0; /* left */
+        *out++ = 0; /* right */
+    }
+    return finished;
+}
+/*******************************************************************/
+int main(void);
+int main(void)
+{
+    PortAudioStream *stream;
+    PaError err;
+    paTestData data;
+    int i;
+    int totalSamps;
+    printf("PortAudio Test: output sine wave. SR = %d, BufSize = %d\n", SAMPLE_RATE, FRAMES_PER_BUFFER);
+    /* initialise sinusoidal wavetable */
+    for( i=0; i<TABLE_SIZE; i++ )
+    {
+        data.sine[i] = (float) sin( ((double)i/(double)TABLE_SIZE) * M_PI * 2. );
+    }
+    data.left_phase = data.right_phase = 0;
+    data.framesToGo = totalSamps =  NUM_SECONDS * SAMPLE_RATE; /* Play for a few seconds. */
+    err = Pa_Initialize();
+    if( err != paNoError ) goto error;
+    err = Pa_OpenStream(
+              &stream,
+              paNoDevice,/* default input device */
+              0,              /* no input */
+              paFloat32,  /* 32 bit floating point input */
+              NULL,
+              Pa_GetDefaultOutputDeviceID(), /* default output device */
+              2,          /* stereo output */
+              paFloat32,      /* 32 bit floating point output */
+              NULL,
+              SAMPLE_RATE,
+              FRAMES_PER_BUFFER,            /* frames per buffer */
+              0,              /* number of buffers, if zero then use default minimum */
+              paClipOff,      /* we won't output out of range samples so don't bother clipping them */
+              patestCallback,
+              &data );
+    if( err != paNoError ) goto error;
+    err = Pa_StartStream( stream );
+    if( err != paNoError ) goto error;
+    /* Watch until sound is halfway finished. */
+    printf("Play for %d seconds.\n", NUM_SECONDS/2 ); fflush(stdout);
+    while( Pa_StreamTime( stream ) < (totalSamps/2) ) Pa_Sleep(10);
+    /* Stop sound until ENTER hit. */
+    err = Pa_StopStream( stream );
+    if( err != paNoError ) goto error;
+    printf("Pause for 2 seconds.\n"); fflush(stdout);
+    Pa_Sleep( 2000 );
+
+    err = Pa_StartStream( stream );
+    if( err != paNoError ) goto error;
+    printf("Play until sound is finished.\n"); fflush(stdout);
+    while( Pa_StreamActive( stream ) ) Pa_Sleep(10);
+    err = Pa_CloseStream( stream );
+    if( err != paNoError ) goto error;
+    Pa_Terminate();
+    printf("Test finished.\n");
+    return err;
+error:
+    Pa_Terminate();
+    fprintf( stderr, "An error occured while using the portaudio stream\n" );
+    fprintf( stderr, "Error number: %d\n", err );
+    fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
+    return err;
+}
diff --git a/pa_tests/patest_stop.c b/pa_tests/patest_stop.c
new file mode 100644 (file)
index 0000000..4e2fca0
--- /dev/null
@@ -0,0 +1,285 @@
+/*
+ * $Id$
+ * patest_stop.c
+ *
+ * Test the three ways of stopping audio:
+ *    calling Pa_StopStream(),
+ *    calling Pa_AbortStream(),
+ *    and returning a 1 from the callback function.
+ *
+ * A long latency is set up so that you can hear the difference.
+ * Then a simple 8 note sequence is repeated twice.
+ * The program will print what you should hear.
+ *
+ * Author: Phil Burk <philburk@softsynth.com>
+ *
+ * This program uses the PortAudio Portable Audio Library.
+ * For more information see: http://www.portaudio.com
+ * Copyright (c) 1999-2000 Ross Bencina and 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.
+ *
+ */
+#include <stdio.h>
+#include <math.h>
+#include "portaudio.h"
+#define OUTPUT_DEVICE       (Pa_GetDefaultOutputDeviceID())
+#define SLEEP_DUR           (200)
+#define SAMPLE_RATE         (44100)
+#define FRAMES_PER_BUFFER   (256)
+#define LATENCY_MSEC    (3000)
+#define NUM_BUFFERS         ((LATENCY_MSEC * SAMPLE_RATE) / (FRAMES_PER_BUFFER * 1000))
+#define FRAMES_PER_NOTE     (SAMPLE_RATE/2)
+#define MAX_REPEATS         (2)
+#define FUNDAMENTAL         (400.0f / SAMPLE_RATE)
+#define NOTE_0              (FUNDAMENTAL * 1.0f / 1.0f)
+#define NOTE_1              (FUNDAMENTAL * 5.0f / 4.0f)
+#define NOTE_2              (FUNDAMENTAL * 4.0f / 3.0f)
+#define NOTE_3              (FUNDAMENTAL * 3.0f / 2.0f)
+#define NOTE_4              (FUNDAMENTAL * 2.0f / 1.0f)
+#define MODE_FINISH    (0)
+#define MODE_STOP      (1)
+#define MODE_ABORT     (2)
+#ifndef M_PI
+#define M_PI  (3.14159265)
+#endif
+#define TABLE_SIZE   (400)
+typedef struct
+{
+    float  waveform[TABLE_SIZE + 1]; // add one for guard point for interpolation
+    float  phase_increment;
+    float  phase;
+    float *tune;
+    int    notesPerTune;
+    int    frameCounter;
+    int    noteCounter;
+    int    repeatCounter;
+    PaTimestamp    outTime;
+    int    stopMode;
+    int    done;
+}
+paTestData;
+/************* Prototypes *****************************/
+int TestStopMode( paTestData *data );
+float LookupWaveform( paTestData *data, float phase );
+/******************************************************
+ * Convert phase between 0.0 and 1.0 to waveform value 
+ * using linear interpolation.
+ */
+float LookupWaveform( paTestData *data, float phase )
+{
+    float fIndex = phase*TABLE_SIZE;
+    int   index = (int) fIndex;
+    float fract = fIndex - index;
+    float lo = data->waveform[index];
+    float hi = data->waveform[index+1];
+    float val = lo + fract*(hi-lo);
+    return val;
+}
+/* This routine will be called by the PortAudio engine when audio is needed.
+** It may called at interrupt level on some machines so don't do anything
+** that could mess up the system like calling malloc() or free().
+*/
+static int patestCallback( void *inputBuffer, void *outputBuffer,
+                           unsigned long framesPerBuffer,
+                           PaTimestamp outTime, void *userData )
+{
+    paTestData *data = (paTestData*)userData;
+    float *out = (float*)outputBuffer;
+    float value;
+    unsigned int i = 0;
+    int finished = 0;
+    (void) outTime; /* Prevent unused variable warnings. */
+    (void) inputBuffer;
+
+    data->outTime = outTime;
+    if( !data->done )
+    {
+        for( i=0; i<framesPerBuffer; i++ )
+        {
+            /* Are we done with this note? */
+            if( data->frameCounter >= FRAMES_PER_NOTE )
+            {
+                data->noteCounter += 1;
+                data->frameCounter = 0;
+                /* Are we done with this tune? */
+                if( data->noteCounter >= data->notesPerTune )
+                {
+                    data->noteCounter = 0;
+                    data->repeatCounter += 1;
+                    /* Are we totally done? */
+                    if( data->repeatCounter >= MAX_REPEATS )
+                    {
+                        data->done = 1;
+                        if( data->stopMode == MODE_FINISH )
+                        {
+                            finished = 1;
+                            break;
+                        }
+                    }
+                }
+                data->phase_increment = data->tune[data->noteCounter];
+            }
+            value = LookupWaveform(data, data->phase);
+            *out++ = value;  /* left */
+            *out++ = value;  /* right */
+            data->phase += data->phase_increment;
+            if( data->phase >= 1.0f ) data->phase -= 1.0f;
+
+            data->frameCounter += 1;
+        }
+    }
+    /* zero remainder of final buffer */
+    for( ; i<framesPerBuffer; i++ )
+    {
+        *out++ = 0; /* left */
+        *out++ = 0; /* right */
+    }
+    return finished;
+}
+/*******************************************************************/
+int main(void);
+int main(void)
+{
+    paTestData DATA;
+    int i;
+    float simpleTune[] = { NOTE_0, NOTE_1, NOTE_2, NOTE_3, NOTE_4, NOTE_3, NOTE_2, NOTE_1 };
+    printf("PortAudio Test: play song and test stopping. ask for %d buffers\n", NUM_BUFFERS );
+    /* initialise sinusoidal wavetable */
+    for( i=0; i<TABLE_SIZE; i++ )
+    {
+        DATA.waveform[i] = (float) (
+                               (0.2 * sin( ((double)i/(double)TABLE_SIZE) * M_PI * 2. )) +
+                               (0.2 * sin( ((double)(3*i)/(double)TABLE_SIZE) * M_PI * 2. )) +
+                               (0.1 * sin( ((double)(5*i)/(double)TABLE_SIZE) * M_PI * 2. ))
+                           );
+    }
+    DATA.waveform[TABLE_SIZE] = DATA.waveform[0]; // set guard point
+    DATA.tune = &simpleTune[0];
+    DATA.notesPerTune = sizeof(simpleTune) / sizeof(float);
+    printf("Test MODE_FINISH - callback returns 1.\n");
+    printf("Should hear entire %d note tune repeated twice.\n", DATA.notesPerTune);
+    DATA.stopMode = MODE_FINISH;
+    if( TestStopMode( &DATA ) != paNoError )
+    {
+        printf("Test of MODE_FINISH failed!\n");
+        goto error;
+    }
+    printf("Test MODE_STOP - stop when song is done.\n");
+    printf("Should hear entire %d note tune repeated twice.\n", DATA.notesPerTune);
+    DATA.stopMode = MODE_STOP;
+    if( TestStopMode( &DATA ) != paNoError )
+    {
+        printf("Test of MODE_STOP failed!\n");
+        goto error;
+    }
+
+    printf("Test MODE_ABORT - abort immediately.\n");
+    printf("Should hear last repetition cut short by %d msec.\n", LATENCY_MSEC);
+    DATA.stopMode = MODE_ABORT;
+    if( TestStopMode( &DATA ) != paNoError )
+    {
+        printf("Test of MODE_ABORT failed!\n");
+        goto error;
+    }
+    return 0;
+error:
+    return 1;
+}
+
+int TestStopMode( paTestData *data )
+{
+    PortAudioStream *stream;
+    PaError err;
+    data->done = 0;
+    data->phase = 0.0;
+    data->frameCounter = 0;
+    data->noteCounter = 0;
+    data->repeatCounter = 0;
+    data->phase_increment = data->tune[data->noteCounter];
+    err = Pa_Initialize();
+    if( err != paNoError ) goto error;
+    err = Pa_OpenStream(
+              &stream,
+              paNoDevice,/* default input device */
+              0,              /* no input */
+              paFloat32,  /* 32 bit floating point input */
+              NULL,
+              OUTPUT_DEVICE,
+              2,          /* stereo output */
+              paFloat32,      /* 32 bit floating point output */
+              NULL,
+              SAMPLE_RATE,
+              FRAMES_PER_BUFFER,            /* frames per buffer */
+              NUM_BUFFERS,    /* number of buffers, if zero then use default minimum */
+              paClipOff,      /* we won't output out of range samples so don't bother clipping them */
+              patestCallback,
+              data );
+    if( err != paNoError ) goto error;
+    err = Pa_StartStream( stream );
+    if( err != paNoError ) goto error;
+    if( data->stopMode == MODE_FINISH )
+    {
+        while( Pa_StreamActive( stream ) )
+        {
+            /*printf("outTime = %g, note# = %d, repeat# = %d\n", data->outTime,
+             data->noteCounter, data->repeatCounter  );
+            fflush(stdout); /**/
+            Pa_Sleep( SLEEP_DUR );
+        }
+    }
+    else
+    {
+        while( data->repeatCounter < MAX_REPEATS )
+        {
+            /*printf("outTime = %g, note# = %d, repeat# = %d\n", data->outTime,
+             data->noteCounter, data->repeatCounter  );
+            fflush(stdout); /**/
+            Pa_Sleep( SLEEP_DUR );
+        }
+    }
+    if( data->stopMode == MODE_ABORT )
+    {
+        printf("Call Pa_AbortStream()\n");
+        err = Pa_AbortStream( stream );
+    }
+    else
+    {
+        printf("Call Pa_StopStream()\n");
+        err = Pa_StopStream( stream );
+    }
+    if( err != paNoError ) goto error;
+    printf("Call Pa_CloseStream()\n"); fflush(stdout);
+    err = Pa_CloseStream( stream );
+    if( err != paNoError ) goto error;
+    Pa_Terminate();
+    printf("Test finished.\n");
+    return err;
+error:
+    Pa_Terminate();
+    fprintf( stderr, "An error occured while using the portaudio stream\n" );
+    fprintf( stderr, "Error number: %d\n", err );
+    fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
+    return err;
+}
diff --git a/pa_tests/patest_sync.c b/pa_tests/patest_sync.c
new file mode 100644 (file)
index 0000000..e2eb88a
--- /dev/null
@@ -0,0 +1,227 @@
+/*
+ * $Id$
+ * patest_sync.c
+ * Test time stamping and synchronization of audio and video.
+ * A high latency is used so we can hear the difference in time.
+ * Random durations are used so we know we are hearing the right beep
+ * and not the one before or after.
+ *
+ * Sequence of events:
+ *    Foreground requests a beep.
+ *    Background randomly schedules a beep.
+ *    Foreground waits for the beep to be heard based on Pa_StreamTime().
+ *    Foreground outputs video (printf) in sync with audio.
+ *    Repeat.
+ *
+ * Author: Phil Burk  http://www.softsynth.com
+ *
+ * This program uses the PortAudio Portable Audio Library.
+ * For more information see: http://www.portaudio.com
+ * Copyright (c) 1999-2000 Ross Bencina and 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.
+ *
+ */
+#include <stdio.h>
+#include <math.h>
+#include "portaudio.h"
+#define NUM_BEEPS           (6)
+#define SAMPLE_RATE         (44100)
+#define FRAMES_PER_BUFFER   (256)
+#define BEEP_DURATION       (1000)
+#define LATENCY_MSEC        (2000)
+#define SLEEP_MSEC          (10)
+#define TIMEOUT_MSEC        ((3 * LATENCY_MSEC) / (2 * SLEEP_MSEC))
+#define NUM_BUFFERS         ((LATENCY_MSEC * SAMPLE_RATE) / (FRAMES_PER_BUFFER * 1000))
+#define STATE_BKG_IDLE    (0)
+#define STATE_BKG_PENDING (1)
+#define STATE_BKG_BEEPING (2)
+typedef struct
+{
+    float        left_phase;
+    float        right_phase;
+    int          state;
+    int          requestBeep;  /* Set by foreground, cleared by background. */
+    PaTimestamp  beepTime;
+    int          beepCount;
+}
+paTestData;
+static unsigned long GenerateRandomNumber( void );
+/************************************************************/
+/* Calculate pseudo-random 32 bit number based on linear congruential method. */
+static unsigned long GenerateRandomNumber( void )
+{
+    static unsigned long randSeed = 22222;  /* Change this for different random sequences. */
+    randSeed = (randSeed * 196314165) + 907633515;
+    return randSeed;
+}
+/* This routine will be called by the PortAudio engine when audio is needed.
+** It may called at interrupt level on some machines so don't do anything
+** that could mess up the system like calling malloc() or free().
+*/
+static int patestCallback( void *inputBuffer, void *outputBuffer,
+                           unsigned long framesPerBuffer,
+                           PaTimestamp outTime, void *userData )
+{
+    /* Cast data passed through stream to our structure. */
+    paTestData *data = (paTestData*)userData;
+    float *out = (float*)outputBuffer;
+    unsigned int i;
+    (void) inputBuffer;
+
+    for( i=0; i<framesPerBuffer; i++ )
+    {
+        switch( data->state )
+        {
+        case STATE_BKG_IDLE:
+            /* Schedule beep at some random time in the future. */
+            if( data->requestBeep )
+            {
+                int random = GenerateRandomNumber() >> 14;
+                data->beepTime = outTime + (i + random + (SAMPLE_RATE/4));
+                data->state = STATE_BKG_PENDING;
+                data->requestBeep = 0;
+                data->left_phase = data->right_phase = 0.0;
+            }
+            *out++ = 0.0;  /* left */
+            *out++ = 0.0;  /* right */
+            break;
+        case STATE_BKG_PENDING:
+            if( (outTime + i) >= data->beepTime )
+            {
+                data->state = STATE_BKG_BEEPING;
+                data->beepCount = BEEP_DURATION;
+            }
+            *out++ = 0.0;  /* left */
+            *out++ = 0.0;  /* right */
+            break;
+        case STATE_BKG_BEEPING:
+            if( data->beepCount <= 0 )
+            {
+                data->state = STATE_BKG_IDLE;
+                *out++ = 0.0;  /* left */
+                *out++ = 0.0;  /* right */
+            }
+            else
+            {
+                /* Play sawtooth wave. */
+                *out++ = data->left_phase;  /* left */
+                *out++ = data->right_phase;  /* right */
+                /* Generate simple sawtooth phaser that ranges between -1.0 and 1.0. */
+                data->left_phase += 0.01f;
+                /* When signal reaches top, drop back down. */
+                if( data->left_phase >= 1.0f ) data->left_phase -= 2.0f;
+                /* higher pitch so we can distinguish left and right. */
+                data->right_phase += 0.03f;
+                if( data->right_phase >= 1.0f ) data->right_phase -= 2.0f;
+            }
+            data->beepCount -= 1;
+            break;
+        default:
+            data->state = STATE_BKG_IDLE;
+            break;
+        }
+    }
+    return 0;
+}
+/*******************************************************************/
+int main(void);
+int main(void)
+{
+    PortAudioStream *stream;
+    PaError    err;
+    paTestData DATA;
+    int        i, timeout;
+    PaTimestamp  previousTime;
+    printf("PortAudio Test: you should see BEEP at the same time you hear it.\n");
+    printf("Wait for a few seconds random delay between BEEPs.\n");
+    printf("BEEP %d times.\n", NUM_BEEPS );
+    /* Initialize our DATA for use by callback. */
+    DATA.left_phase = DATA.right_phase = 0.0;
+    DATA.state = STATE_BKG_IDLE;
+    DATA.requestBeep = 0;
+    /* Initialize library before making any other calls. */
+    err = Pa_Initialize();
+    if( err != paNoError ) goto error;
+    /* Open an audio I/O stream. */
+    err = Pa_OpenDefaultStream(
+              &stream,
+              0,              /* no input channels */
+              2,              /* stereo output */
+              paFloat32,      /* 32 bit floating point output */
+              SAMPLE_RATE,
+              FRAMES_PER_BUFFER,
+              NUM_BUFFERS,
+              patestCallback,
+              &DATA );
+    if( err != paNoError ) goto error;
+    err = Pa_StartStream( stream );
+    if( err != paNoError ) goto error;
+    previousTime = Pa_StreamTime( stream );
+    for( i=0; i<NUM_BEEPS; i++ )
+    {
+        /* Request a beep from background. */
+        DATA.requestBeep = 1;
+        /* Wait for background to acknowledge request. */
+        timeout = TIMEOUT_MSEC;
+        while( (DATA.requestBeep == 1) && (timeout-- > 0 ) ) Pa_Sleep(SLEEP_MSEC);
+        if( timeout <= 0 )
+        {
+            fprintf( stderr, "Timed out waiting for background to acknowledge request.\n" );
+            goto error;
+        }
+        /* Wait for scheduled beep time. */
+        timeout =  TIMEOUT_MSEC + (10000/SLEEP_MSEC);
+        while( (Pa_StreamTime( stream ) < DATA.beepTime) && (timeout-- > 0 ) )
+        {
+            Pa_Sleep(SLEEP_MSEC);
+        }
+        if( timeout <= 0 )
+        {
+            fprintf( stderr, "Timed out waiting for time. Now = %g, Beep for %g.\n",
+                     Pa_StreamTime( stream ), DATA.beepTime );
+            goto error;
+        }
+        /* Beep should be sounding now so print synchronized BEEP. */
+        printf("BEEP");
+        fflush(stdout);
+        printf(" at %d, delta = %d\n",
+               (long) DATA.beepTime, (long) (DATA.beepTime - previousTime) );
+        fflush(stdout);
+        previousTime = DATA.beepTime;
+    }
+    err = Pa_StopStream( stream );
+    if( err != paNoError ) goto error;
+    err = Pa_CloseStream( stream );
+    if( err != paNoError ) goto error;
+    Pa_Terminate();
+    printf("Test finished.\n");
+    return err;
+error:
+    Pa_Terminate();
+    fprintf( stderr, "An error occured while using the portaudio stream\n" );
+    fprintf( stderr, "Error number: %d\n", err );
+    fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
+    return err;
+}
diff --git a/pa_tests/patest_underflow.c b/pa_tests/patest_underflow.c
new file mode 100644 (file)
index 0000000..ca07cf4
--- /dev/null
@@ -0,0 +1,151 @@
+/*
+ * $Id$
+ * patest_underflow.c
+ * Simulate an output buffer underflow condition.
+ * Tests whether the stream can be stopped when underflowing buffers.
+ *
+ * Authors:
+ *    Ross Bencina <rossb@audiomulch.com>
+ *    Phil Burk <philburk@softsynth.com>
+ *
+ * This program uses the PortAudio Portable Audio Library.
+ * For more information see: http://www.audiomulch.com/portaudio/
+ * Copyright (c) 1999-2000 Ross Bencina and 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.
+ *
+ */
+#include <stdio.h>
+#include <math.h>
+#include "portaudio.h"
+
+#define NUM_SECONDS   (20)
+#define SAMPLE_RATE   (44100)
+#define FRAMES_PER_BUFFER  (2048)
+#define MSEC_PER_BUFFER  ( (FRAMES_PER_BUFFER * 1000) / SAMPLE_RATE )
+
+#ifndef M_PI
+#define M_PI  (3.14159265)
+#endif
+
+#define TABLE_SIZE   (200)
+typedef struct
+{
+    float sine[TABLE_SIZE];
+    int left_phase;
+    int right_phase;
+    int sleepTime;
+}
+paTestData;
+
+/* This routine will be called by the PortAudio engine when audio is needed.
+** It may called at interrupt level on some machines so don't do anything
+** that could mess up the system like calling malloc() or free().
+*/
+static int patestCallback(   void *inputBuffer, void *outputBuffer,
+                             unsigned long framesPerBuffer,
+                             PaTimestamp outTime, void *userData )
+{
+    paTestData *data = (paTestData*)userData;
+    float *out = (float*)outputBuffer;
+    unsigned long i;
+    int finished = 0;
+    (void) outTime; /* Prevent unused variable warnings. */
+    (void) inputBuffer;
+    for( i=0; i<framesPerBuffer; i++ )
+    {
+        *out++ = data->sine[data->left_phase];  /* left */
+        *out++ = data->sine[data->right_phase];  /* right */
+        data->left_phase += 1;
+        if( data->left_phase >= TABLE_SIZE ) data->left_phase -= TABLE_SIZE;
+        data->right_phase += 3; /* higher pitch so we can distinguish left and right. */
+        if( data->right_phase >= TABLE_SIZE ) data->right_phase -= TABLE_SIZE;
+    }
+
+    /* Cause underflow to occur. */
+    if( data->sleepTime > 0 ) Pa_Sleep( data->sleepTime );
+    data->sleepTime += 1;
+
+    return finished;
+}
+
+/*******************************************************************/
+int main(void);
+int main(void)
+{
+    PortAudioStream *stream;
+    PaError err;
+    paTestData data;
+    int i;
+    printf("PortAudio Test: output sine wave. SR = %d, BufSize = %d\n", SAMPLE_RATE, FRAMES_PER_BUFFER);
+    /* initialise sinusoidal wavetable */
+    for( i=0; i<TABLE_SIZE; i++ )
+    {
+        data.sine[i] = (float) sin( ((double)i/(double)TABLE_SIZE) * M_PI * 2. );
+    }
+    data.left_phase = data.right_phase = data.sleepTime = 0;
+    err = Pa_Initialize();
+    if( err != paNoError ) goto error;
+    err = Pa_OpenStream(
+              &stream,
+              paNoDevice,/* default input device */
+              0,              /* no input */
+              paFloat32,  /* 32 bit floating point input */
+              NULL,
+              Pa_GetDefaultOutputDeviceID(), /* default output device */
+              2,          /* stereo output */
+              paFloat32,      /* 32 bit floating point output */
+              NULL,
+              SAMPLE_RATE,
+              FRAMES_PER_BUFFER,
+              0,              /* number of buffers, if zero then use default minimum */
+              paClipOff,      /* we won't output out of range samples so don't bother clipping them */
+              patestCallback,
+              &data );
+    if( err != paNoError ) goto error;
+    err = Pa_StartStream( stream );
+    if( err != paNoError ) goto error;
+
+    while( data.sleepTime < (2 * MSEC_PER_BUFFER) )
+    {
+        printf("SleepTime = %d\n", data.sleepTime );
+        Pa_Sleep( data.sleepTime );
+    }
+
+    printf("Try to stop stream.\n");
+    err = Pa_StopStream( stream ); /* */
+    err = Pa_AbortStream( stream ); /* */
+    if( err != paNoError ) goto error;
+    err = Pa_CloseStream( stream );
+    if( err != paNoError ) goto error;
+    Pa_Terminate();
+    printf("Test finished.\n");
+    return err;
+error:
+    Pa_Terminate();
+    fprintf( stderr, "An error occured while using the portaudio stream\n" );
+    fprintf( stderr, "Error number: %d\n", err );
+    fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
+    return err;
+}
diff --git a/pa_tests/patest_wire.c b/pa_tests/patest_wire.c
new file mode 100644 (file)
index 0000000..59a33a3
--- /dev/null
@@ -0,0 +1,139 @@
+/*
+ * $Id$
+ * patest_wire.c
+ *
+ * Pass input directly to output.
+ * Note that some HW devices, for example many ISA audio cards
+ * on PCs, do NOT support full duplex! For a PC, you normally need
+ * a PCI based audio card such as the SBLive.
+ *
+ * Author: Phil Burk  http://www.softsynth.com
+ *
+ * This program uses the PortAudio Portable Audio Library.
+ * For more information see: http://www.portaudio.com
+ * Copyright (c) 1999-2000 Ross Bencina and 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.
+ *
+ */
+#include <stdio.h>
+#include <math.h>
+#include "portaudio.h"
+/*
+** Note that many of the older ISA sound cards on PCs do NOT support
+** full duplex audio (simultaneous record and playback).
+** And some only support full duplex at lower sample rates.
+*/
+#define SAMPLE_RATE  (44100)
+#define FRAMES_PER_BUFFER  (64)
+#if 1
+#define PA_SAMPLE_TYPE  paFloat32
+typedef float SAMPLE;
+#else
+#define PA_SAMPLE_TYPE  paInt16
+typedef short SAMPLE;
+#endif
+static int wireCallback( void *inputBuffer, void *outputBuffer,
+                         unsigned long framesPerBuffer,
+                         PaTimestamp outTime, void *userData );
+
+/* This routine will be called by the PortAudio engine when audio is needed.
+** It may be called at interrupt level on some machines so don't do anything
+** that could mess up the system like calling malloc() or free().
+*/
+static int wireCallback( void *inputBuffer, void *outputBuffer,
+                         unsigned long framesPerBuffer,
+                         PaTimestamp outTime, void *userData )
+{
+    SAMPLE *out = (SAMPLE*)outputBuffer;
+    SAMPLE *in = (SAMPLE*)inputBuffer;
+    unsigned int i;
+    int finished = 0;
+    (void) outTime;
+
+    /* This may get called with NULL inputBuffer during initial setup. */
+    if( inputBuffer == NULL )
+    {
+        for( i=0; i<framesPerBuffer; i++ )
+        {
+            *out++ = 0;  /* left */
+            *out++ = 0;  /* right */
+        }
+    }
+    else
+    {
+        for( i=0; i<framesPerBuffer; i++ )
+        {
+            *out++ = *in++;  /* left */
+            *out++ = *in++;  /* right */
+        }
+    }
+
+    return 0;
+}
+/*******************************************************************/
+int main(void);
+int main(void)
+{
+    PortAudioStream *stream;
+    PaError err;
+    int numDevices = Pa_CountDevices();
+    err = Pa_Initialize();
+    if( err != paNoError ) goto error;
+    printf("PortAudio Test: input device  = %d\n", Pa_GetDefaultInputDeviceID() );
+    printf("PortAudio Test: output device = %d\n", Pa_GetDefaultOutputDeviceID() );
+    err = Pa_OpenStream(
+              &stream,
+              Pa_GetDefaultInputDeviceID(), /* default output device */
+              2,               /* stereo input */
+              PA_SAMPLE_TYPE,
+              NULL,
+              Pa_GetDefaultOutputDeviceID(), /* default output device */
+              2,               /* stereo output */
+              PA_SAMPLE_TYPE,
+              NULL,
+              SAMPLE_RATE,
+              FRAMES_PER_BUFFER,            /* frames per buffer */
+              0,               /* number of buffers, if zero then use default minimum */
+              paClipOff,       /* we won't output out of range samples so don't bother clipping them */
+              wireCallback,
+              NULL );          /* no data */
+    if( err != paNoError ) goto error;
+    err = Pa_StartStream( stream );
+    if( err != paNoError ) goto error;
+    printf("Full duplex sound test in progress.\n");
+    printf("Hit ENTER to exit test.\n");
+    getchar();
+    err = Pa_CloseStream( stream );
+    if( err != paNoError ) goto error;
+    Pa_Terminate();
+    printf("Full duplex sound test complete.\n"); fflush(stdout);
+    return 0;
+error:
+    Pa_Terminate();
+    fprintf( stderr, "An error occured while using the portaudio stream\n" );
+    fprintf( stderr, "Error number: %d\n", err );
+    fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
+    return -1;
+}
diff --git a/pa_unix_oss/Makefile b/pa_unix_oss/Makefile
new file mode 100644 (file)
index 0000000..9f118e1
--- /dev/null
@@ -0,0 +1,38 @@
+# Make PortAudio for Linux
+
+LIBS =  -lm -lpthread
+
+CDEFINES = -I../pa_common
+CFLAGS = -g
+PASRC = ../pa_common/pa_lib.c pa_unix_oss.c
+PAINC = ../pa_common/portaudio.h
+
+# Tests that work.
+TESTC   = $(PASRC) ../pa_tests/patest_sine.c
+#TESTC   = $(PASRC) ../pa_tests/patest_sine_time.c
+#TESTC   = $(PASRC) ../pa_tests/patest_maxsines.c
+#TESTC   = $(PASRC) ../pa_tests/patest_stop.c
+#TESTC   = $(PASRC) ../pa_tests/patest_sync.c
+#TESTC   = $(PASRC) ../pa_tests/patest_pink.c
+#TESTC   = $(PASRC) ../pa_tests/patest_leftright.c
+#TESTC   = $(PASRC) ../pa_tests/patest_clip.c
+#TESTC   = $(PASRC) ../pa_tests/patest_dither.c
+#TESTC   = $(PASRC) ../pa_tests/pa_devs.c
+#TESTC   = $(PASRC) ../pa_tests/patest_many.c
+#TESTC   = $(PASRC) ../pa_tests/patest_record.c
+#TESTC   = $(PASRC) ../pa_tests/pa_fuzz.c
+#TESTC   = $(PASRC) ../pa_tests/patest_wire.c
+#TESTC   = $(PASRC) ../pa_tests/paqa_devs.c
+
+# Tests that do not yet work.
+
+TESTH   = $(PAINC)
+
+all: patest
+
+patest: $(TESTC) $(TESTH) Makefile
+       gcc $(CFLAGS) $(TESTC) $(CDEFINES) $(LIBS) -o patest
+
+run: patest
+       ./patest
+
diff --git a/pa_unix_oss/Makefile_freebsd b/pa_unix_oss/Makefile_freebsd
new file mode 100644 (file)
index 0000000..fc8b14d
--- /dev/null
@@ -0,0 +1,36 @@
+# Make PortAudio for FreeBSD
+
+LIBS =  -lm -pthread
+
+CDEFINES = -I../pa_common
+CFLAGS = -g
+PASRC = ../pa_common/pa_lib.c pa_freebsd.c
+PAINC = ../pa_common/portaudio.h
+
+# Tests that work.
+#TESTC   = $(PASRC) ../pa_tests/patest_sine.c
+TESTC   = $(PASRC) ../pa_tests/patest_sine_time.c
+#TESTC   = $(PASRC) ../pa_tests/patest_stop.c
+#TESTC   = $(PASRC) ../pa_tests/patest_sync.c
+#TESTC   = $(PASRC) ../pa_tests/patest_pink.c
+#TESTC   = $(PASRC) ../pa_tests/patest_leftright.c
+#TESTC   = $(PASRC) ../pa_tests/patest_clip.c
+#TESTC   = $(PASRC) ../pa_tests/patest_dither.c
+#TESTC   = $(PASRC) ../pa_tests/pa_devs.c
+#TESTC   = $(PASRC) ../pa_tests/patest_many.c
+#TESTC   = $(PASRC) ../pa_tests/patest_record.c
+#TESTC   = $(PASRC) ../pa_tests/patest_wire.c
+#TESTC   = $(PASRC) ../pa_tests/paqa_devs.c
+
+# Tests that do not yet work.
+
+TESTH   = $(PAINC)
+
+all: patest
+
+patest: $(TESTC) $(TESTH) Makefile
+       gcc $(CFLAGS) $(TESTC) $(CDEFINES) $(LIBS) -o patest
+
+run: patest
+       ./patest
+
diff --git a/pa_unix_oss/low_latency_tip.txt b/pa_unix_oss/low_latency_tip.txt
new file mode 100644 (file)
index 0000000..2d982b7
Binary files /dev/null and b/pa_unix_oss/low_latency_tip.txt differ
diff --git a/pa_unix_oss/pa_unix_oss.c b/pa_unix_oss/pa_unix_oss.c
new file mode 100644 (file)
index 0000000..400de91
--- /dev/null
@@ -0,0 +1,1072 @@
+/*
+ * PortAudio Portable Real-Time Audio Library
+ * Latest Version at: http://www.portaudio.com
+ * Linux OSS Implementation by douglas repetto and Phil Burk
+ *
+ * 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.
+ *
+ */
+/*
+Modfication History
+  1/2001 - Phil Burk - initial hack for Linux
+  2/2001 - Douglas Repetto - many improvements, initial query support
+  4/2/2001 - Phil - stop/abort thread control, separate in/out native buffers
+  5/28/2001 - Phil - use pthread_create() instead of clone(). Thanks Stephen Brandon!
+       use pthread_join() after thread shutdown.
+  5/29/2001 - Phil - query for multiple devices, multiple formats,
+                     input mode and input+output mode working,
+       Pa_GetCPULoad() implemented.
+  PLB20010817 - Phil & Janos Haber - don't halt if test of sample rate fails.
+  SB20010904 - Stephen Brandon - mods needed for GNUSTEP and SndKit
+  JH20010905 - Janos Haber - FreeBSD mods
+  2001-09-22 - Heiko - (i.e. Heiko Purnhagen <purnhage@tnt.uni-hannover.de> ;-)
+                       added 24k and 16k to ratesToTry[]
+         fixed Pa_GetInternalDevice()
+         changed DEVICE_NAME_BASE from /dev/audio to /dev/dsp
+         handled SNDCTL_DSP_SPEED in Pq_QueryDevice() more graceful
+         fixed Pa_StreamTime() for paqa_errs.c
+         fixed numCannel=2 oddity and error handling in Pa_SetupDeviceFormat()
+         grep also for HP20010922 ...
+  PLB20010924 - Phil - merged Heiko's changes
+                       removed sNumDevices and potential related bugs,
+         use getenv("PA_MIN_LATENCY_MSEC") to set desired latency,
+         simplify CPU Load calculation by comparing real-time to framesPerBuffer,
+         always close device when querying even if error occurs,
+  PLB20010927 - Phil - Improved negotiation for numChannels.
+  SG20011005 - Stewart Greenhill - set numChannels back to reasonable value after query.
+  DH20010115 - David Herring - fixed uninitialized handle.
+  
+TODO
+O- change Pa_StreamTime() to query device (patest_sync.c)
+O- put semaphore lock around shared data?
+O- handle native formats better
+O- handle stereo-only device better ???
+O- what if input and output of a device capabilities differ (e.g. es1371) ???
+*/
+/*
+ PROPOSED - should we add this to "portaudio.h". Problem with 
+ Pa_QueryDevice() not having same driver name os Pa_OpenStream().
+ A PaDriverInfo structure can be passed to the underlying device
+ on the Pa_OpenStream() call. The contents and interpretation of
+ the structure is determined by the PA implementation.
+*/
+typedef struct PaDriverInfo /* PROPOSED */
+{
+    /* Size of structure. Allows driver to extend the structure without breaking existing applications. */
+    int           size;
+    /* Can be used to request a specific device name. */
+    const char   *name;
+    unsigned long data;
+}
+PaDriverInfo;
+
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <malloc.h>
+#include <memory.h>
+#include <math.h>
+#include "portaudio.h"
+#include "pa_host.h"
+#include "pa_trace.h"
+
+#include <sys/ioctl.h>
+#include <sys/time.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#ifdef __linux__
+#include <linux/soundcard.h>
+#else
+#include <machine/soundcard.h> /* JH20010905 */
+#endif
+
+#include <sched.h>
+#include <pthread.h>
+
+/* Some versions of OSS do not define AFMT_S16_NE. Assume little endian. FIXME - check CPU*/
+#ifndef AFMT_S16_NE
+ #define AFMT_S16_NE AFMT_S16_LE
+#endif
+
+#define PRINT(x)   { printf x; fflush(stdout); }
+#define ERR_RPT(x) PRINT(x)
+#define DBUG(x)    /* PRINT(x) */
+#define DBUGX(x)   /* PRINT(x) */
+
+#define BAD_DEVICE_ID (-1)
+
+#define MIN_LATENCY_MSEC   (100)
+#define MIN_TIMEOUT_MSEC   (100)
+#define MAX_TIMEOUT_MSEC   (1000)
+
+/************************************************* Definitions ********/
+#ifdef __linux__
+ #define DEVICE_NAME_BASE            "/dev/dsp"
+#else
+ #define DEVICE_NAME_BASE            "/dev/audio"
+#endif
+
+#define MAX_CHARS_DEVNAME           (32)
+#define MAX_SAMPLE_RATES            (10)
+typedef struct internalPortAudioDevice
+{
+    struct internalPortAudioDevice *pad_Next; /* Singly linked list. */
+    double          pad_SampleRates[MAX_SAMPLE_RATES]; /* for pointing to from pad_Info */
+    char            pad_DeviceName[MAX_CHARS_DEVNAME];
+    PaDeviceInfo    pad_Info;
+}
+internalPortAudioDevice;
+
+/* Define structure to contain all OSS and Linux specific data. */
+typedef struct PaHostSoundControl
+{
+    int              pahsc_OutputHandle;
+    int              pahsc_InputHandle;
+    pthread_t        pahsc_ThreadPID;
+    short           *pahsc_NativeInputBuffer;
+    short           *pahsc_NativeOutputBuffer;
+    unsigned int     pahsc_BytesPerInputBuffer;    /* native buffer size in bytes */
+    unsigned int     pahsc_BytesPerOutputBuffer;   /* native buffer size in bytes */
+    /* For measuring CPU utilization. */
+    struct timeval   pahsc_EntryTime;
+    double           pahsc_InverseMicrosPerBuffer; /* 1/Microseconds of real-time audio per user buffer. */
+}
+PaHostSoundControl;
+
+/************************************************* Shared Data ********/
+/* FIXME - put Mutex around this shared data. */
+static int sDeviceIndex = 0;
+static internalPortAudioDevice *sDeviceList = NULL;
+static int sDefaultInputDeviceID = paNoDevice;
+static int sDefaultOutputDeviceID = paNoDevice;
+static int sEnumerationError;
+static int sPaHostError = 0;
+
+/************************************************* Prototypes **********/
+
+static internalPortAudioDevice *Pa_GetInternalDevice( PaDeviceID id );
+static Pa_QueryDevices( void );
+static PaError Pa_QueryDevice( const char *deviceName, internalPortAudioDevice *pad );
+static PaError Pa_SetupDeviceFormat( int devHandle, int numChannels, int sampleRate );
+
+/********************************* BEGIN CPU UTILIZATION MEASUREMENT ****/
+static void Pa_StartUsageCalculation( internalPortAudioStream   *past )
+{
+    struct itimerval itimer;
+    PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData;
+    if( pahsc == NULL ) return;
+    /* Query system timer for usage analysis and to prevent overuse of CPU. */
+    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 )
+{
+    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( gettimeofday( &currentTime, NULL ) == 0 )
+    {
+        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);
+    }
+}
+/****************************************** END CPU UTILIZATION *******/
+
+/*********************************************************************
+ * Try to open the named device.
+ * If it opens, try to set various rates and formats and fill in 
+ * the device info structure.
+ */
+static PaError Pa_QueryDevice( const char *deviceName, internalPortAudioDevice *pad )
+{
+    int result = paHostError;
+    int numBytes;
+    int tempDevHandle;
+    int numChannels, maxNumChannels;
+    int format;
+    int numSampleRates;
+    int sampleRate;
+    int numRatesToTry;
+    int ratesToTry[9] = {96000, 48000, 44100, 32000, 24000, 22050, 16000, 11025, 8000};
+    int i;
+
+    /* douglas:
+     we have to do this querying in a slightly different order. apparently
+     some sound cards will give you different info based on their settins. 
+     e.g. a card might give you stereo at 22kHz but only mono at 44kHz.
+     the correct order for OSS is: format, channels, sample rate
+     
+    */
+    if ( (tempDevHandle = open(deviceName,O_WRONLY))  == -1 )
+    {
+        DBUG(("Pa_QueryDevice: could not open %s\n", deviceName ));
+        return paHostError;
+    }
+
+    /*  Ask OSS what formats are supported by the hardware. */
+    pad->pad_Info.nativeSampleFormats = 0;
+    if (ioctl(tempDevHandle, SNDCTL_DSP_GETFMTS, &format) == -1)
+    {
+        ERR_RPT(("Pa_QueryDevice: could not get format info\n" ));
+        goto error;
+    }
+    if( format & AFMT_U8 )     pad->pad_Info.nativeSampleFormats |= paUInt8;
+    if( format & AFMT_S16_NE ) pad->pad_Info.nativeSampleFormats |= paInt16;
+
+    /* Negotiate for the maximum number of channels for this device. PLB20010927
+     * Consider up to 16 as the upper number of channels.
+     * Variable numChannels should contain the actual upper limit after the call.
+     * Thanks to John Lazzaro and Heiko Purnhagen for suggestions.
+     */
+    maxNumChannels = 0;
+    for( numChannels = 1; numChannels <= 16; numChannels++ )
+    {
+        int temp = numChannels;
+        DBUG(("Pa_QueryDevice: use SNDCTL_DSP_CHANNELS, numChannels = %d\n", numChannels ))
+        if(ioctl(tempDevHandle, SNDCTL_DSP_CHANNELS, &temp) < 0 )
+        {
+            /* ioctl() failed so bail out if we already have stereo */
+            if( numChannels > 2 ) break;
+        }
+        else
+        {
+            /* ioctl() worked but bail out if it does not support numChannels.
+             * We don't want to leave gaps in the numChannels supported.
+             */
+            if( (numChannels > 2) && (temp != numChannels) ) break;
+            DBUG(("Pa_QueryDevice: temp = %d\n", temp ))
+            if( temp > maxNumChannels ) maxNumChannels = temp; /* Save maximum. */
+        }
+    }
+
+    /* The above negotiation may fail for an old driver so try this older technique. */
+    if( maxNumChannels < 1 )
+    {
+        int stereo = 1;
+        if(ioctl(tempDevHandle, SNDCTL_DSP_STEREO, &stereo) < 0)
+        {
+            maxNumChannels = 1;
+        }
+        else
+        {
+            maxNumChannels = (stereo) ? 2 : 1;
+        }
+        DBUG(("Pa_QueryDevice: use SNDCTL_DSP_STEREO, maxNumChannels = %d\n", maxNumChannels ))
+    }
+
+    pad->pad_Info.maxOutputChannels = maxNumChannels;
+    DBUG(("Pa_QueryDevice: maxNumChannels = %d\n", maxNumChannels))
+
+    /* During channel negotiation, the last ioctl() may have failed. This can
+     * also cause sample rate negotiation to fail. Hence the following, to return
+     * to a supported number of channels. SG20011005 */
+    {
+        int temp = maxNumChannels;
+        if( temp > 2 ) temp = 2; /* use most reasonable default value */
+        ioctl(tempDevHandle, SNDCTL_DSP_CHANNELS, &temp);
+    }
+
+    /* FIXME - for now, assume maxInputChannels = maxOutputChannels.
+     *    Eventually do separate queries for O_WRONLY and O_RDONLY
+    */
+    pad->pad_Info.maxInputChannels = pad->pad_Info.maxOutputChannels;
+
+    DBUG(("Pa_QueryDevice: maxInputChannels = %d\n",
+          pad->pad_Info.maxInputChannels))
+
+
+    /* Determine available sample rates by trying each one and seeing result.
+     */
+    numSampleRates = 0;
+    numRatesToTry = sizeof(ratesToTry)/sizeof(int);
+    for (i = 0; i < numRatesToTry; i++)
+    {
+        sampleRate = ratesToTry[i];
+
+        if (ioctl(tempDevHandle, SNDCTL_DSP_SPEED, &sampleRate) >= 0 ) /* PLB20010817 */
+        {
+            if (sampleRate == ratesToTry[i])
+            {
+                DBUG(("Pa_QueryDevice: got sample rate: %d\n", sampleRate))
+                pad->pad_SampleRates[numSampleRates] = (float)ratesToTry[i];
+                numSampleRates++;
+            }
+        }
+    }
+
+    DBUG(("Pa_QueryDevice: final numSampleRates = %d\n", numSampleRates))
+    if (numSampleRates==0)   /* HP20010922 */
+    {
+        ERR_RPT(("Pa_QueryDevice: no supported sample rate (or SNDCTL_DSP_SPEED ioctl call failed).\n" ));
+        goto error;
+    }
+
+    pad->pad_Info.numSampleRates = numSampleRates;
+    pad->pad_Info.sampleRates = pad->pad_SampleRates;
+
+    pad->pad_Info.name = deviceName;
+
+    result = paNoError;
+
+error:
+    /* We MUST close the handle here or we won't be able to reopen it later!!!  */
+    close(tempDevHandle);
+
+    return result;
+}
+
+/*********************************************************************
+ * Determines the number of available devices by trying to open
+ * each "/dev/dsp#" or "/dsp/audio#" in order until it fails.
+ * Add each working device to a singly linked list of devices.
+ */
+static PaError Pa_QueryDevices( void )
+{
+    internalPortAudioDevice *pad, *lastPad;
+    int      numBytes;
+    int      go = 1;
+    int      numDevices = 0;
+    PaError  testResult;
+    PaError  result = paNoError;
+
+    sDefaultInputDeviceID = paNoDevice;
+    sDefaultOutputDeviceID = paNoDevice;
+
+    lastPad = NULL;
+
+    while( go )
+    {
+        /* Allocate structure to hold device info. */
+        pad = PaHost_AllocateFastMemory( sizeof(internalPortAudioDevice) );
+        if( pad == NULL ) return paInsufficientMemory;
+        memset( pad, 0, sizeof(internalPortAudioDevice) );
+
+        /* Build name for device. */
+        if( numDevices == 0 )
+        {
+            sprintf( pad->pad_DeviceName, DEVICE_NAME_BASE);
+        }
+        else
+        {
+            sprintf( pad->pad_DeviceName, DEVICE_NAME_BASE "%d", numDevices );
+        }
+
+        DBUG(("Try device %s\n", pad->pad_DeviceName ));
+        testResult = Pa_QueryDevice( pad->pad_DeviceName, pad );
+        DBUG(("Pa_QueryDevice returned %d\n", testResult ));
+        if( testResult != paNoError )
+        {
+            if( lastPad == NULL )
+            {
+                result = testResult; /* No good devices! */
+            }
+            go = 0;
+            PaHost_FreeFastMemory( pad, sizeof(internalPortAudioDevice) );
+        }
+        else
+        {
+            numDevices += 1;
+            /* Add to linked list of devices. */
+            if( lastPad )
+            {
+                lastPad->pad_Next = pad;
+            }
+            else
+            {
+                sDeviceList = pad; /* First element in linked list. */
+            }
+            lastPad = pad;
+        }
+    }
+
+    return result;
+
+}
+
+/*************************************************************************/
+int Pa_CountDevices()
+{
+    int numDevices = 0;
+    internalPortAudioDevice *pad;
+
+    if( sDeviceList == NULL ) Pa_Initialize();
+    /* Count devices in list. */
+    pad = sDeviceList;
+    while( pad != NULL )
+    {
+        pad = pad->pad_Next;
+        numDevices++;
+    }
+
+    return numDevices;
+}
+
+/*************************************************************************/
+static internalPortAudioDevice *Pa_GetInternalDevice( PaDeviceID id )
+{
+    internalPortAudioDevice *pad;
+    if( (id < 0) || ( id >= Pa_CountDevices()) ) return NULL;
+    pad = sDeviceList;
+    while( id > 0 )
+    {
+        pad = pad->pad_Next;
+        id--;
+    }
+    return pad;
+}
+
+/*************************************************************************/
+const PaDeviceInfo* Pa_GetDeviceInfo( PaDeviceID id )
+{
+    internalPortAudioDevice *pad;
+    if( (id < 0) || ( id >= Pa_CountDevices()) ) return NULL;
+    pad = Pa_GetInternalDevice( id );
+    return  &pad->pad_Info ;
+}
+
+static PaError Pa_MaybeQueryDevices( void )
+{
+    if( sDeviceList == NULL )
+    {
+        return Pa_QueryDevices();
+    }
+    return 0;
+}
+
+PaDeviceID Pa_GetDefaultInputDeviceID( void )
+{
+    /* return paNoDevice; */
+    return 0;
+}
+
+PaDeviceID Pa_GetDefaultOutputDeviceID( void )
+{
+    return 0;
+}
+
+/**********************************************************************
+** Make sure that we have queried the device capabilities.
+*/
+
+PaError PaHost_Init( void )
+{
+    return Pa_MaybeQueryDevices();
+}
+
+/*******************************************************************************************/
+static PaError Pa_AudioThreadProc( internalPortAudioStream   *past )
+{
+    PaError    result = 0;
+    PaHostSoundControl             *pahsc;
+    short    bytes_read = 0;
+
+#ifdef GNUSTEP
+    GSRegisterCurrentThread(); /* SB20010904 */
+#endif
+
+#if 1
+    {
+#define SCHEDULER_POLICY SCHED_RR
+
+        struct sched_param schp;
+        memset(&schp, 0, sizeof(schp));
+        schp.sched_priority = sched_get_priority_max(SCHEDULER_POLICY);
+
+        if (sched_setscheduler(0, SCHEDULER_POLICY, &schp) != 0)
+        {
+            perror("sched_setscheduler");
+            printf("PortAudio: only superuser can use real-time priority.\n");
+        }
+        else
+        {
+            printf("PortAudio: callback priority set to level %d!\n", schp.sched_priority);
+        }
+    }
+#endif
+
+    pahsc = (PaHostSoundControl *) past->past_DeviceData;
+    if( pahsc == NULL ) return paInternalError;
+
+    past->past_IsActive = 1;
+    DBUG(("entering thread.\n"));
+
+    while( (past->past_StopNow == 0) && (past->past_StopSoon == 0) )
+    {
+
+        DBUG(("go!\n"));
+        /* Read data from device */
+        if(pahsc->pahsc_NativeInputBuffer)
+        {
+            bytes_read = read(pahsc->pahsc_InputHandle,
+                              (void *)pahsc->pahsc_NativeInputBuffer,
+                              pahsc->pahsc_BytesPerInputBuffer);
+
+            DBUG(("bytes_read: %d\n", bytes_read));
+        }
+
+        /* Convert 16 bit native data to user data and call user routine. */
+        DBUG(("converting...\n"));
+        Pa_StartUsageCalculation( past );
+        result = Pa_CallConvertInt16( past,
+                                      pahsc->pahsc_NativeInputBuffer,
+                                      pahsc->pahsc_NativeOutputBuffer );
+        Pa_EndUsageCalculation( past );
+        if( result != 0)
+        {
+            DBUG(("hmm, Pa_CallConvertInt16() says: %d. i'm bailing.\n",
+                  result));
+            break;
+        }
+
+        /* Write data to device. */
+        if( pahsc->pahsc_NativeOutputBuffer )
+        {
+
+            write(pahsc->pahsc_OutputHandle,
+                  (void *)pahsc->pahsc_NativeOutputBuffer,
+                  pahsc->pahsc_BytesPerOutputBuffer);
+        }
+    }
+
+    past->past_IsActive = 0;
+    DBUG(("leaving thread.\n"));
+
+#ifdef GNUSTEP
+    GSUnregisterCurrentThread();  /* SB20010904 */
+#endif
+    return 0;
+}
+
+/*******************************************************************************************/
+static PaError Pa_SetupDeviceFormat( int devHandle, int numChannels, int sampleRate )
+{
+    PaError result = paNoError;
+    int     tmp;
+
+    /* Set format, channels, and rate in this order to keep OSS happy. */
+    /* Set data format. FIXME - handle more native formats. */
+    tmp = AFMT_S16_NE;
+    if( ioctl(devHandle,SNDCTL_DSP_SETFMT,&tmp) == -1)
+    {
+        ERR_RPT(("Pa_SetupDeviceFormat: could not SNDCTL_DSP_SETFMT\n" ));
+        return paHostError;
+    }
+    if( tmp != AFMT_S16_NE)
+    {
+        ERR_RPT(("Pa_SetupDeviceFormat: HW does not support AFMT_S16_NE\n" ));
+        return paHostError;
+    }
+
+
+    /* Set number of channels. */
+    tmp = numChannels;
+    if (ioctl(devHandle, SNDCTL_DSP_CHANNELS, &numChannels) == -1)
+    {
+        ERR_RPT(("Pa_SetupDeviceFormat: could not SNDCTL_DSP_CHANNELS\n" ));
+        return paHostError;
+    }
+    if( tmp != numChannels)
+    {
+        ERR_RPT(("Pa_SetupDeviceFormat: HW does not support %d channels\n", numChannels ));
+        return paHostError;
+    }
+
+    /* Set playing frequency. 44100, 22050 and 11025 are safe bets. */
+    tmp = sampleRate;
+    if( ioctl(devHandle,SNDCTL_DSP_SPEED,&tmp) == -1)
+    {
+        ERR_RPT(("Pa_SetupDeviceFormat: could not SNDCTL_DSP_SPEED\n" ));
+        return paHostError;
+    }
+    if( tmp != sampleRate)
+    {
+        ERR_RPT(("Pa_SetupDeviceFormat: HW does not support %d Hz sample rate\n",sampleRate ));
+        return paHostError;
+    }
+
+    return result;
+}
+
+static int CalcHigherLogTwo( int n )
+{
+    int log2 = 0;
+    while( (1<<log2) < n ) log2++;
+    return log2;
+}
+
+/*******************************************************************************************
+** Set number of fragments and size of fragments to achieve desired latency.
+*/
+static void Pa_SetLatency( int devHandle, int numBuffers, int framesPerBuffer, int channelsPerFrame  )
+{
+    int     tmp;
+    int     numFrames , bufferSize, powerOfTwo;
+
+    /* Increase size of buffers and reduce number of buffers to reduce latency inside driver. */
+    while( numBuffers > 8 )
+    {
+        numBuffers = (numBuffers + 1) >> 1;
+        framesPerBuffer = framesPerBuffer << 1;
+    }
+
+    /* calculate size of buffers in bytes */
+    bufferSize = framesPerBuffer * channelsPerFrame * sizeof(short); /* FIXME */
+
+    /* Calculate next largest power of two */
+    powerOfTwo = CalcHigherLogTwo( bufferSize );
+    DBUG(("Pa_SetLatency: numBuffers = %d, framesPerBuffer = %d, powerOfTwo = %d\n",
+          numBuffers, framesPerBuffer, powerOfTwo ));
+
+    /* Encode info into a single int */
+    tmp=(numBuffers<<16) + powerOfTwo;
+
+    if(ioctl(devHandle,SNDCTL_DSP_SETFRAGMENT,&tmp) == -1)
+    {
+        ERR_RPT(("Pa_SetLatency: could not SNDCTL_DSP_SETFRAGMENT\n" ));
+        /* Don't return an error. Best to just continue and hope for the best. */
+        ERR_RPT(("Pa_SetLatency: numBuffers = %d, framesPerBuffer = %d, powerOfTwo = %d\n",
+                 numBuffers, framesPerBuffer, powerOfTwo ));
+    }
+}
+
+/*************************************************************************
+** 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")
+
+int Pa_GetMinNumBuffers( int framesPerBuffer, double framesPerSecond )
+{
+    int minBuffers;
+    int minLatencyMsec = 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;
+    }
+
+    minBuffers = (int) ((minLatencyMsec * framesPerSecond) / ( 1000.0 * framesPerBuffer ));
+    if( minBuffers < 2 ) minBuffers = 2;
+    return minBuffers;
+}
+
+/*******************************************************************/
+PaError PaHost_OpenStream( internalPortAudioStream   *past )
+{
+    PaError          result = paNoError;
+    PaHostSoundControl *pahsc;
+    int tmp;
+    int flags;
+    int              numBytes, maxChannels;
+    unsigned int     minNumBuffers;
+    internalPortAudioDevice *pad;
+    DBUG(("PaHost_OpenStream() called.\n" ));
+
+    /* Allocate and initialize host data. */
+    pahsc = (PaHostSoundControl *) malloc(sizeof(PaHostSoundControl));
+    if( pahsc == NULL )
+    {
+        result = paInsufficientMemory;
+        goto error;
+    }
+    memset( pahsc, 0, sizeof(PaHostSoundControl) );
+    past->past_DeviceData = (void *) pahsc;
+
+    pahsc->pahsc_OutputHandle = BAD_DEVICE_ID; /* No device currently opened. */
+    pahsc->pahsc_InputHandle = BAD_DEVICE_ID;
+
+    /* Allocate native buffers. */
+    pahsc->pahsc_BytesPerInputBuffer = past->past_FramesPerUserBuffer *
+                                       past->past_NumInputChannels * sizeof(short);
+    if( past->past_NumInputChannels > 0)
+    {
+        pahsc->pahsc_NativeInputBuffer = (short *) malloc(pahsc->pahsc_BytesPerInputBuffer);
+        if( pahsc->pahsc_NativeInputBuffer == NULL )
+        {
+            result = paInsufficientMemory;
+            goto error;
+        }
+    }
+    pahsc->pahsc_BytesPerOutputBuffer = past->past_FramesPerUserBuffer *
+                                        past->past_NumOutputChannels * sizeof(short);
+    if( past->past_NumOutputChannels > 0)
+    {
+        pahsc->pahsc_NativeOutputBuffer = (short *) malloc(pahsc->pahsc_BytesPerOutputBuffer);
+        if( pahsc->pahsc_NativeOutputBuffer == NULL )
+        {
+            result = paInsufficientMemory;
+            goto error;
+        }
+    }
+
+    /* DBUG(("PaHost_OpenStream: pahsc_MinFramesPerHostBuffer = %d\n", pahsc->pahsc_MinFramesPerHostBuffer )); */
+    minNumBuffers = Pa_GetMinNumBuffers( past->past_FramesPerUserBuffer, past->past_SampleRate );
+    past->past_NumUserBuffers = ( minNumBuffers > past->past_NumUserBuffers ) ? minNumBuffers : past->past_NumUserBuffers;
+
+    pahsc->pahsc_InverseMicrosPerBuffer = past->past_SampleRate / (1000000.0 * past->past_FramesPerUserBuffer);
+    DBUG(("pahsc_InverseMicrosPerBuffer = %g\n", pahsc->pahsc_InverseMicrosPerBuffer ));
+
+    /* ------------------------- OPEN DEVICE -----------------------*/
+
+    /* just output */
+    if (past->past_OutputDeviceID == past->past_InputDeviceID)
+    {
+
+        if ((past->past_NumOutputChannels > 0) && (past->past_NumInputChannels > 0) )
+        {
+            pad = Pa_GetInternalDevice( past->past_OutputDeviceID );
+            DBUG(("PaHost_OpenStream: attempt to open %s for O_RDWR\n", pad->pad_DeviceName ));
+            pahsc->pahsc_OutputHandle = pahsc->pahsc_InputHandle =
+                                            open(pad->pad_DeviceName,O_RDWR);
+            if(pahsc->pahsc_InputHandle==-1)
+            {
+                ERR_RPT(("PaHost_OpenStream: could not open %s for O_RDWR\n", pad->pad_DeviceName ));
+                result = paHostError;
+                goto error;
+            }
+            Pa_SetLatency( pahsc->pahsc_OutputHandle,
+                           past->past_NumUserBuffers, past->past_FramesPerUserBuffer,
+                           past->past_NumOutputChannels );
+            result = Pa_SetupDeviceFormat( pahsc->pahsc_OutputHandle,
+                                           past->past_NumOutputChannels, (int)past->past_SampleRate );
+        }
+    }
+    else
+    {
+        if (past->past_NumOutputChannels > 0)
+        {
+            pad = Pa_GetInternalDevice( past->past_OutputDeviceID );
+            DBUG(("PaHost_OpenStream: attempt to open %s for O_WRONLY\n", pad->pad_DeviceName ));
+            pahsc->pahsc_OutputHandle = open(pad->pad_DeviceName,O_WRONLY);
+            if(pahsc->pahsc_OutputHandle==-1)
+            {
+                ERR_RPT(("PaHost_OpenStream: could not open %s for O_WRONLY\n", pad->pad_DeviceName ));
+                result = paHostError;
+                goto error;
+            }
+            Pa_SetLatency( pahsc->pahsc_OutputHandle,
+                           past->past_NumUserBuffers, past->past_FramesPerUserBuffer,
+                           past->past_NumOutputChannels );
+            result = Pa_SetupDeviceFormat( pahsc->pahsc_OutputHandle,
+                                           past->past_NumOutputChannels, (int)past->past_SampleRate );
+        }
+
+        if (past->past_NumInputChannels > 0)
+        {
+            pad = Pa_GetInternalDevice( past->past_InputDeviceID );
+            DBUG(("PaHost_OpenStream: attempt to open %s for O_RDONLY\n", pad->pad_DeviceName ));
+            pahsc->pahsc_InputHandle = open(pad->pad_DeviceName,O_RDONLY);
+            if(pahsc->pahsc_InputHandle==-1)
+            {
+                ERR_RPT(("PaHost_OpenStream: could not open %s for O_RDONLY\n", pad->pad_DeviceName ));
+                result = paHostError;
+                goto error;
+            }
+            Pa_SetLatency( pahsc->pahsc_InputHandle, /* DH20010115 - was OutputHandle! */
+                           past->past_NumUserBuffers, past->past_FramesPerUserBuffer,
+                           past->past_NumInputChannels );
+            result = Pa_SetupDeviceFormat( pahsc->pahsc_InputHandle,
+                                           past->past_NumInputChannels, (int)past->past_SampleRate );
+        }
+    }
+
+
+    DBUG(("PaHost_OpenStream: SUCCESS - result = %d\n", result ));
+    return result;
+
+error:
+    ERR_RPT(("PaHost_OpenStream: ERROR - result = %d\n", result ));
+    PaHost_CloseStream( past );
+    return result;
+}
+
+/*************************************************************************/
+PaError PaHost_StartOutput( internalPortAudioStream *past )
+{
+    return paNoError;
+}
+
+/*************************************************************************/
+PaError PaHost_StartInput( internalPortAudioStream *past )
+{
+    return paNoError;
+}
+
+/*************************************************************************/
+PaError PaHost_StartEngine( internalPortAudioStream *past )
+{
+    PaHostSoundControl *pahsc;
+    PaError             result = paNoError;
+    int                 hres;
+
+    pahsc = (PaHostSoundControl *) past->past_DeviceData;
+
+    past->past_StopSoon = 0;
+    past->past_StopNow = 0;
+    past->past_IsActive = 1;
+
+    /* Use pthread_create() instead of __clone() because:
+     *   - pthread_create also works for other UNIX systems like Solaris,
+     *   - the Java HotSpot VM crashes in pthread_setcanceltype() when using __clone()
+     */
+    hres = pthread_create(&(pahsc->pahsc_ThreadPID),
+                          NULL /*pthread_attr_t * attr*/,
+                          (void*)Pa_AudioThreadProc, past);
+    if( hres != 0 )
+    {
+        result = paHostError;
+        sPaHostError = hres;
+        goto error;
+    }
+
+error:
+    return result;
+}
+
+/*************************************************************************/
+PaError PaHost_StopEngine( internalPortAudioStream *past, int abort )
+{
+    int                 hres;
+    long                timeOut;
+    PaError             result = paNoError;
+    PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData;
+
+    if( pahsc == NULL ) return paNoError;
+
+    /* Tell background thread to stop generating more data and to let current data play out. */
+    past->past_StopSoon = 1;
+    /* If aborting, tell background thread to stop NOW! */
+    if( abort ) past->past_StopNow = 1;
+
+    /* Join thread to recover memory resources. */
+    if( pahsc->pahsc_ThreadPID != -1 )
+    {
+        /* This check is needed for GNUSTEP - SB20010904 */
+        if ( !pthread_equal( pahsc->pahsc_ThreadPID, pthread_self() ) )
+        {
+            hres = pthread_join( pahsc->pahsc_ThreadPID, NULL );
+        }
+        else
+        {
+            DBUG(("Play thread was stopped from itself - can't do pthread_join()\n"));
+            hres = 0;
+        }
+
+        if( hres != 0 )
+        {
+            result = paHostError;
+            sPaHostError = hres;
+        }
+        pahsc->pahsc_ThreadPID = -1;
+    }
+
+    past->past_IsActive = 0;
+
+    return result;
+}
+
+/*************************************************************************/
+PaError PaHost_StopInput( internalPortAudioStream *past, int abort )
+{
+    return paNoError;
+}
+
+/*************************************************************************/
+PaError PaHost_StopOutput( internalPortAudioStream *past, int abort )
+{
+    return paNoError;
+}
+
+/*******************************************************************/
+PaError PaHost_CloseStream( internalPortAudioStream   *past )
+{
+    PaHostSoundControl *pahsc;
+    if( past == NULL ) return paBadStreamPtr;
+    pahsc = (PaHostSoundControl *) past->past_DeviceData;
+    if( pahsc == NULL ) return paNoError;
+
+    if( pahsc->pahsc_OutputHandle != BAD_DEVICE_ID )
+    {
+        int err;
+        DBUG(("PaHost_CloseStream: attempt to close output device handle = %d\n",
+              pahsc->pahsc_OutputHandle ));
+        err = close(pahsc->pahsc_OutputHandle);
+        if( err < 0 )
+        {
+            ERR_RPT(("PaHost_CloseStream: warning, closing output device failed.\n"));
+        }
+    }
+
+    if( (pahsc->pahsc_InputHandle != BAD_DEVICE_ID) &&
+            (pahsc->pahsc_InputHandle != pahsc->pahsc_OutputHandle) )
+    {
+        int err;
+        DBUG(("PaHost_CloseStream: attempt to close input device handle = %d\n",
+              pahsc->pahsc_InputHandle ));
+        err = close(pahsc->pahsc_InputHandle);
+        if( err < 0 )
+        {
+            ERR_RPT(("PaHost_CloseStream: warning, closing input device failed.\n"));
+        }
+    }
+    pahsc->pahsc_OutputHandle = BAD_DEVICE_ID;
+    pahsc->pahsc_InputHandle = BAD_DEVICE_ID;
+
+    if( pahsc->pahsc_NativeInputBuffer )
+    {
+        free( pahsc->pahsc_NativeInputBuffer );
+        pahsc->pahsc_NativeInputBuffer = NULL;
+    }
+    if( pahsc->pahsc_NativeOutputBuffer )
+    {
+        free( pahsc->pahsc_NativeOutputBuffer );
+        pahsc->pahsc_NativeOutputBuffer = NULL;
+    }
+
+    free( pahsc );
+    past->past_DeviceData = NULL;
+    return paNoError;
+}
+
+/*************************************************************************/
+PaError PaHost_Term( void )
+{
+    /* Free all of the linked devices. */
+    internalPortAudioDevice *pad, *nextPad;
+    pad = sDeviceList;
+    while( pad != NULL )
+    {
+        nextPad = pad->pad_Next;
+        DBUG(("PaHost_Term: freeing %s\n", pad->pad_DeviceName ));
+        PaHost_FreeFastMemory( pad, sizeof(internalPortAudioDevice) );
+        pad = nextPad;
+    }
+    sDeviceList = NULL;
+    return 0;
+}
+
+/*************************************************************************
+ * Sleep for the requested number of milliseconds.
+ */
+void Pa_Sleep( long msec )
+{
+#if 0
+    struct timeval timeout;
+    timeout.tv_sec = msec / 1000;
+    timeout.tv_usec = (msec % 1000) * 1000;
+    select( 0, NULL, NULL, NULL, &timeout );
+#else
+    long usecs = msec * 1000;
+    usleep( usecs );
+#endif
+}
+
+/*************************************************************************
+ * Allocate memory that can be accessed in real-time.
+ * This may need to be held in physical memory so that it is not
+ * paged to virtual memory.
+ * This call MUST be balanced with a call to PaHost_FreeFastMemory().
+ */
+void *PaHost_AllocateFastMemory( long numBytes )
+{
+    void *addr = malloc( numBytes ); /* FIXME - do we need physical memory? */
+    if( addr != NULL ) memset( addr, 0, numBytes );
+    return addr;
+}
+
+/*************************************************************************
+ * Free memory that could be accessed in real-time.
+ * This call MUST be balanced with a call to PaHost_AllocateFastMemory().
+ */
+void PaHost_FreeFastMemory( void *addr, long numBytes )
+{
+    if( addr != NULL ) free( addr );
+}
+
+
+/***********************************************************************/
+PaError PaHost_StreamActive( internalPortAudioStream   *past )
+{
+    PaHostSoundControl *pahsc;
+    if( past == NULL ) return paBadStreamPtr;
+    pahsc = (PaHostSoundControl *) past->past_DeviceData;
+    if( pahsc == NULL ) return paInternalError;
+    return (PaError) (past->past_IsActive != 0);
+}
+
+/***********************************************************************/
+PaTimestamp Pa_StreamTime( PortAudioStream *stream )
+{
+    internalPortAudioStream *past = (internalPortAudioStream *) stream;
+    /* FIXME - return actual frames played, not frames generated.
+    ** Need to query the output device somehow.
+    */
+    if( past == NULL ) return paBadStreamPtr;
+    return past->past_FrameCount;
+}
+
+/***********************************************************************/
+long Pa_GetHostError( void )
+{
+    return (long) sPaHostError;
+}
diff --git a/pa_unix_oss/recplay.c b/pa_unix_oss/recplay.c
new file mode 100644 (file)
index 0000000..9d4c78c
--- /dev/null
@@ -0,0 +1,114 @@
+/*
+ * recplay.c
+ * Phil Burk
+ * Minimal record and playback test.
+ * 
+ */
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#ifndef __STDC__
+/* #include <getopt.h> */
+#endif /* __STDC__ */
+#include <fcntl.h>
+#ifdef __STDC__
+#include <string.h>
+#else /* __STDC__ */
+#include <strings.h>
+#endif /* __STDC__ */
+#include <sys/soundcard.h>
+
+#define NUM_BYTES   (64*1024)
+#define BLOCK_SIZE   (4*1024)
+
+#define AUDIO "/dev/dsp"
+
+char buffer[NUM_BYTES];
+
+int audioDev = 0;
+
+main (int argc, char *argv[])
+{
+    int   numLeft;
+    char *ptr;
+    int   num;
+    int   samplesize;
+
+    /********** RECORD ********************/
+    /* Open audio device. */
+    audioDev = open (AUDIO, O_RDONLY, 0);
+    if (audioDev == -1)
+    {
+        perror (AUDIO);
+        exit (-1);
+    }
+
+    /* Set to 16 bit samples. */
+    samplesize = 16;
+    ioctl(audioDev, SNDCTL_DSP_SAMPLESIZE, &samplesize);
+    if (samplesize != 16)
+    {
+        perror("Unable to set the sample size.");
+        exit(-1);
+    }
+
+    /* Record in blocks */
+    printf("Begin recording.\n");
+    numLeft = NUM_BYTES;
+    ptr = buffer;
+    while( numLeft >= BLOCK_SIZE )
+    {
+        if ( (num = read (audioDev, ptr, BLOCK_SIZE)) < 0 )
+        {
+            perror (AUDIO);
+            exit (-1);
+        }
+        else
+        {
+            printf("Read %d bytes\n", num);
+            ptr += num;
+            numLeft -= num;
+        }
+    }
+
+    close( audioDev );
+
+    /********** PLAYBACK ********************/
+    /* Open audio device for writing. */
+    audioDev = open (AUDIO, O_WRONLY, 0);
+    if (audioDev == -1)
+    {
+        perror (AUDIO);
+        exit (-1);
+    }
+
+    /* Set to 16 bit samples. */
+    samplesize = 16;
+    ioctl(audioDev, SNDCTL_DSP_SAMPLESIZE, &samplesize);
+    if (samplesize != 16)
+    {
+        perror("Unable to set the sample size.");
+        exit(-1);
+    }
+
+    /* Play in blocks */
+    printf("Begin playing.\n");
+    numLeft = NUM_BYTES;
+    ptr = buffer;
+    while( numLeft >= BLOCK_SIZE )
+    {
+        if ( (num = write (audioDev, ptr, BLOCK_SIZE)) < 0 )
+        {
+            perror (AUDIO);
+            exit (-1);
+        }
+        else
+        {
+            printf("Wrote %d bytes\n", num);
+            ptr += num;
+            numLeft -= num;
+        }
+    }
+
+    close( audioDev );
+}
diff --git a/pa_win_ds/dsound_wrapper.c b/pa_win_ds/dsound_wrapper.c
new file mode 100644 (file)
index 0000000..e795525
--- /dev/null
@@ -0,0 +1,466 @@
+/*
+ * $Id$
+ * Simplified DirectSound interface.
+ *
+ * Author: Phil Burk & Robert Marsanyi
+ *
+ * PortAudio Portable Real-Time Audio Library
+ * For more information see: http://www.softsynth.com/portaudio/
+ * DirectSound Implementation
+ * Copyright (c) 1999-2000 Phil Burk & Robert Marsanyi
+ *
+ * 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.
+ *
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#define INITGUID     // Needed to build IID_IDirectSoundNotify. See objbase.h for info.
+#include <objbase.h>
+#include <unknwn.h>
+#include "dsound_wrapper.h"
+#include "pa_trace.h"
+
+/************************************************************************************/
+void DSW_Term( DSoundWrapper *dsw )
+{
+    // Cleanup the sound buffers
+    if (dsw->dsw_OutputBuffer)
+    {
+        IDirectSoundBuffer_Stop( dsw->dsw_OutputBuffer );
+        IDirectSoundBuffer_Release( dsw->dsw_OutputBuffer );
+        dsw->dsw_OutputBuffer = NULL;
+    }
+#if SUPPORT_AUDIO_CAPTURE
+    if (dsw->dsw_InputBuffer)
+    {
+        IDirectSoundCaptureBuffer_Stop( dsw->dsw_InputBuffer );
+        IDirectSoundCaptureBuffer_Release( dsw->dsw_InputBuffer );
+        dsw->dsw_InputBuffer = NULL;
+    }
+    if (dsw->dsw_pDirectSoundCapture)
+    {
+        IDirectSoundCapture_Release( dsw->dsw_pDirectSoundCapture );
+        dsw->dsw_pDirectSoundCapture = NULL;
+    }
+#endif /* SUPPORT_AUDIO_CAPTURE */
+    if (dsw->dsw_pDirectSound)
+    {
+        IDirectSound_Release( dsw->dsw_pDirectSound );
+        dsw->dsw_pDirectSound = NULL;
+    }
+}
+/************************************************************************************/
+HRESULT DSW_Init( DSoundWrapper *dsw )
+{
+    memset( dsw, 0, sizeof(DSoundWrapper) );
+    return 0;
+}
+/************************************************************************************/
+HRESULT DSW_InitOutputDevice( DSoundWrapper *dsw, LPGUID lpGUID )
+{
+    // Create the DS object
+    HRESULT hr = DirectSoundCreate( lpGUID, &dsw->dsw_pDirectSound, NULL );
+    if( hr != DS_OK ) return hr;
+    return hr;
+}
+
+/************************************************************************************/
+HRESULT DSW_InitOutputBuffer( DSoundWrapper *dsw, unsigned long nFrameRate, int nChannels, int bytesPerBuffer )
+{
+    DWORD          dwDataLen;
+    DWORD          playCursor;
+    HRESULT        result;
+    LPDIRECTSOUNDBUFFER pPrimaryBuffer;
+    HWND           hWnd;
+    HRESULT        hr;
+    WAVEFORMATEX   wfFormat;
+    DSBUFFERDESC   primaryDesc;
+    DSBUFFERDESC   secondaryDesc;
+    unsigned char* pDSBuffData;
+    LARGE_INTEGER  counterFrequency;
+    dsw->dsw_OutputSize = bytesPerBuffer;
+    dsw->dsw_OutputRunning = FALSE;
+    dsw->dsw_OutputUnderflows = 0;
+    dsw->dsw_FramesWritten = 0;
+    dsw->dsw_BytesPerFrame = nChannels * sizeof(short);
+    // We were using getForegroundWindow() but sometimes the ForegroundWindow may not be the
+    // applications's window. Also if that window is closed before the Buffer is closed
+    // then DirectSound can crash. (Thanks for Scott Patterson for reporting this.)
+    // So we will use GetDesktopWindow() which was suggested by Miller Puckette.
+    // hWnd = GetForegroundWindow();
+    hWnd = GetDesktopWindow();
+    // Set cooperative level to DSSCL_EXCLUSIVE so that we can get 16 bit output, 44.1 KHz.
+    // Exclusize also prevents unexpected sounds from other apps during a performance.
+    if ((hr = IDirectSound_SetCooperativeLevel( dsw->dsw_pDirectSound,
+              hWnd, DSSCL_EXCLUSIVE)) != DS_OK)
+    {
+        return hr;
+    }
+    // -----------------------------------------------------------------------
+    // Create primary buffer and set format just so we can specify our custom format.
+    // Otherwise we would be stuck with the default which might be 8 bit or 22050 Hz.
+    // Setup the primary buffer description
+    ZeroMemory(&primaryDesc, sizeof(DSBUFFERDESC));
+    primaryDesc.dwSize        = sizeof(DSBUFFERDESC);
+    primaryDesc.dwFlags       = DSBCAPS_PRIMARYBUFFER; // all panning, mixing, etc done by synth
+    primaryDesc.dwBufferBytes = 0;
+    primaryDesc.lpwfxFormat   = NULL;
+    // Create the buffer
+    if ((result = IDirectSound_CreateSoundBuffer( dsw->dsw_pDirectSound,
+                  &primaryDesc, &pPrimaryBuffer, NULL)) != DS_OK) return result;
+    // Define the buffer format
+    wfFormat.wFormatTag = WAVE_FORMAT_PCM;
+    wfFormat.nChannels = nChannels;
+    wfFormat.nSamplesPerSec = nFrameRate;
+    wfFormat.wBitsPerSample = 8 * sizeof(short);
+    wfFormat.nBlockAlign = wfFormat.nChannels * wfFormat.wBitsPerSample / 8;
+    wfFormat.nAvgBytesPerSec = wfFormat.nSamplesPerSec * wfFormat.nBlockAlign;
+    wfFormat.cbSize = 0;  /* No extended format info. */
+    // Set the primary buffer's format
+    if((result = IDirectSoundBuffer_SetFormat( pPrimaryBuffer, &wfFormat)) != DS_OK) return result;
+    // ----------------------------------------------------------------------
+    // Setup the secondary buffer description
+    ZeroMemory(&secondaryDesc, sizeof(DSBUFFERDESC));
+    secondaryDesc.dwSize = sizeof(DSBUFFERDESC);
+    secondaryDesc.dwFlags =  DSBCAPS_GLOBALFOCUS | DSBCAPS_GETCURRENTPOSITION2;
+    secondaryDesc.dwBufferBytes = bytesPerBuffer;
+    secondaryDesc.lpwfxFormat = &wfFormat;
+    // Create the secondary buffer
+    if ((result = IDirectSound_CreateSoundBuffer( dsw->dsw_pDirectSound,
+                  &secondaryDesc, &dsw->dsw_OutputBuffer, NULL)) != DS_OK) return result;
+    // Lock the DS buffer
+    if ((result = IDirectSoundBuffer_Lock( dsw->dsw_OutputBuffer, 0, dsw->dsw_OutputSize, (LPVOID*)&pDSBuffData,
+                                           &dwDataLen, NULL, 0, 0)) != DS_OK) return result;
+    // Zero the DS buffer
+    ZeroMemory(pDSBuffData, dwDataLen);
+    // Unlock the DS buffer
+    if ((result = IDirectSoundBuffer_Unlock( dsw->dsw_OutputBuffer, pDSBuffData, dwDataLen, NULL, 0)) != DS_OK) return result;
+    if( QueryPerformanceFrequency( &counterFrequency ) )
+    {
+        int framesInBuffer = bytesPerBuffer / (nChannels * sizeof(short));
+        dsw->dsw_CounterTicksPerBuffer.QuadPart = (counterFrequency.QuadPart * framesInBuffer) / nFrameRate;
+        AddTraceMessage("dsw_CounterTicksPerBuffer = %d\n", dsw->dsw_CounterTicksPerBuffer.LowPart );
+    }
+    else
+    {
+        dsw->dsw_CounterTicksPerBuffer.QuadPart = 0;
+    }
+    // Let DSound set the starting write position because if we set it to zero, it looks like the
+    // buffer is full to begin with. This causes a long pause before sound starts when using large buffers.
+    hr = IDirectSoundBuffer_GetCurrentPosition( dsw->dsw_OutputBuffer, &playCursor, &dsw->dsw_WriteOffset );
+    if( hr != DS_OK )
+    {
+        return hr;
+    }
+    dsw->dsw_FramesWritten = dsw->dsw_WriteOffset / dsw->dsw_BytesPerFrame;
+    /* printf("DSW_InitOutputBuffer: playCursor = %d, writeCursor = %d\n", playCursor, dsw->dsw_WriteOffset ); */
+    return DS_OK;
+}
+
+/************************************************************************************/
+HRESULT DSW_StartOutput( DSoundWrapper *dsw )
+{
+    HRESULT        hr;
+    QueryPerformanceCounter( &dsw->dsw_LastPlayTime );
+    dsw->dsw_LastPlayCursor = 0;
+    dsw->dsw_FramesPlayed = 0;
+    hr = IDirectSoundBuffer_SetCurrentPosition( dsw->dsw_OutputBuffer, 0 );
+    if( hr != DS_OK )
+    {
+        return hr;
+    }
+    // Start the buffer playback in a loop.
+    if( dsw->dsw_OutputBuffer != NULL )
+    {
+        hr = IDirectSoundBuffer_Play( dsw->dsw_OutputBuffer, 0, 0, DSBPLAY_LOOPING );
+        if( hr != DS_OK )
+        {
+            return hr;
+        }
+        dsw->dsw_OutputRunning = TRUE;
+    }
+
+    return 0;
+}
+/************************************************************************************/
+HRESULT DSW_StopOutput( DSoundWrapper *dsw )
+{
+    // Stop the buffer playback
+    if( dsw->dsw_OutputBuffer != NULL )
+    {
+        dsw->dsw_OutputRunning = FALSE;
+        return IDirectSoundBuffer_Stop( dsw->dsw_OutputBuffer );
+    }
+    else return 0;
+}
+
+/************************************************************************************/
+HRESULT DSW_QueryOutputSpace( DSoundWrapper *dsw, long *bytesEmpty )
+{
+    HRESULT hr;
+    DWORD   playCursor;
+    DWORD   writeCursor;
+    long    numBytesEmpty;
+    long    playWriteGap;
+    // Query to see how much room is in buffer.
+    // Note: Even though writeCursor is not used, it must be passed to prevent DirectSound from dieing
+    // under WinNT. The Microsoft documentation says we can pass NULL but apparently not.
+    // Thanks to Max Rheiner for the fix.
+    hr = IDirectSoundBuffer_GetCurrentPosition( dsw->dsw_OutputBuffer, &playCursor, &writeCursor );
+    if( hr != DS_OK )
+    {
+        return hr;
+    }
+    AddTraceMessage("playCursor", playCursor);
+    AddTraceMessage("dsw_WriteOffset", dsw->dsw_WriteOffset);
+    // Determine size of gap between playIndex and WriteIndex that we cannot write into.
+    playWriteGap = writeCursor - playCursor;
+    if( playWriteGap < 0 ) playWriteGap += dsw->dsw_OutputSize; // unwrap
+    /* DirectSound doesn't have a large enough playCursor so we cannot detect wrap-around. */
+    /* Attempt to detect playCursor wrap-around and correct it. */
+    if( dsw->dsw_OutputRunning && (dsw->dsw_CounterTicksPerBuffer.QuadPart != 0) )
+    {
+        /* How much time has elapsed since last check. */
+        LARGE_INTEGER   currentTime;
+        LARGE_INTEGER   elapsedTime;
+        long            bytesPlayed;
+        long            bytesExpected;
+        long            buffersWrapped;
+        QueryPerformanceCounter( &currentTime );
+        elapsedTime.QuadPart = currentTime.QuadPart - dsw->dsw_LastPlayTime.QuadPart;
+        dsw->dsw_LastPlayTime = currentTime;
+        /* How many bytes does DirectSound say have been played. */
+        bytesPlayed = playCursor - dsw->dsw_LastPlayCursor;
+        if( bytesPlayed < 0 ) bytesPlayed += dsw->dsw_OutputSize; // unwrap
+        dsw->dsw_LastPlayCursor = playCursor;
+        /* Calculate how many bytes we would have expected to been played by now. */
+        bytesExpected = (long) ((elapsedTime.QuadPart * dsw->dsw_OutputSize) / dsw->dsw_CounterTicksPerBuffer.QuadPart);
+        buffersWrapped = (bytesExpected - bytesPlayed) / dsw->dsw_OutputSize;
+        if( buffersWrapped > 0 )
+        {
+            AddTraceMessage("playCursor wrapped! bytesPlayed", bytesPlayed );
+            AddTraceMessage("playCursor wrapped! bytesExpected", bytesExpected );
+            playCursor += (buffersWrapped * dsw->dsw_OutputSize);
+            bytesPlayed += (buffersWrapped * dsw->dsw_OutputSize);
+        }
+        /* Maintain frame output cursor. */
+        dsw->dsw_FramesPlayed += (bytesPlayed / dsw->dsw_BytesPerFrame);
+    }
+    numBytesEmpty = playCursor - dsw->dsw_WriteOffset;
+    if( numBytesEmpty < 0 ) numBytesEmpty += dsw->dsw_OutputSize; // unwrap offset
+    /* Have we underflowed? */
+    if( numBytesEmpty > (dsw->dsw_OutputSize - playWriteGap) )
+    {
+        if( dsw->dsw_OutputRunning )
+        {
+            dsw->dsw_OutputUnderflows += 1;
+            AddTraceMessage("underflow detected! numBytesEmpty", numBytesEmpty );
+        }
+        dsw->dsw_WriteOffset = writeCursor;
+        numBytesEmpty = dsw->dsw_OutputSize - playWriteGap;
+    }
+    *bytesEmpty = numBytesEmpty;
+    return hr;
+}
+
+/************************************************************************************/
+HRESULT DSW_ZeroEmptySpace( DSoundWrapper *dsw )
+{
+    HRESULT hr;
+    LPBYTE lpbuf1 = NULL;
+    LPBYTE lpbuf2 = NULL;
+    DWORD dwsize1 = 0;
+    DWORD dwsize2 = 0;
+    long  bytesEmpty;
+    hr = DSW_QueryOutputSpace( dsw, &bytesEmpty ); // updates dsw_FramesPlayed
+    if (hr != DS_OK) return hr;
+    if( bytesEmpty == 0 ) return DS_OK;
+    // Lock free space in the DS
+    hr = IDirectSoundBuffer_Lock( dsw->dsw_OutputBuffer, dsw->dsw_WriteOffset, bytesEmpty, (void **) &lpbuf1, &dwsize1,
+                                  (void **) &lpbuf2, &dwsize2, 0);
+    if (hr == DS_OK)
+    {
+        // Copy the buffer into the DS
+        ZeroMemory(lpbuf1, dwsize1);
+        if(lpbuf2 != NULL)
+        {
+            ZeroMemory(lpbuf2, dwsize2);
+        }
+        // Update our buffer offset and unlock sound buffer
+        dsw->dsw_WriteOffset = (dsw->dsw_WriteOffset + dwsize1 + dwsize2) % dsw->dsw_OutputSize;
+        IDirectSoundBuffer_Unlock( dsw->dsw_OutputBuffer, lpbuf1, dwsize1, lpbuf2, dwsize2);
+        dsw->dsw_FramesWritten += bytesEmpty / dsw->dsw_BytesPerFrame;
+    }
+    return hr;
+}
+
+/************************************************************************************/
+HRESULT DSW_WriteBlock( DSoundWrapper *dsw, char *buf, long numBytes )
+{
+    HRESULT hr;
+    LPBYTE lpbuf1 = NULL;
+    LPBYTE lpbuf2 = NULL;
+    DWORD dwsize1 = 0;
+    DWORD dwsize2 = 0;
+    // Lock free space in the DS
+    hr = IDirectSoundBuffer_Lock( dsw->dsw_OutputBuffer, dsw->dsw_WriteOffset, numBytes, (void **) &lpbuf1, &dwsize1,
+                                  (void **) &lpbuf2, &dwsize2, 0);
+    if (hr == DS_OK)
+    {
+        // Copy the buffer into the DS
+        CopyMemory(lpbuf1, buf, dwsize1);
+        if(lpbuf2 != NULL)
+        {
+            CopyMemory(lpbuf2, buf+dwsize1, dwsize2);
+        }
+        // Update our buffer offset and unlock sound buffer
+        dsw->dsw_WriteOffset = (dsw->dsw_WriteOffset + dwsize1 + dwsize2) % dsw->dsw_OutputSize;
+        IDirectSoundBuffer_Unlock( dsw->dsw_OutputBuffer, lpbuf1, dwsize1, lpbuf2, dwsize2);
+        dsw->dsw_FramesWritten += numBytes / dsw->dsw_BytesPerFrame;
+    }
+    return hr;
+}
+
+/************************************************************************************/
+DWORD DSW_GetOutputStatus( DSoundWrapper *dsw )
+{
+    DWORD status;
+    if (IDirectSoundBuffer_GetStatus( dsw->dsw_OutputBuffer, &status ) != DS_OK)
+        return( DSERR_INVALIDPARAM );
+    else
+        return( status );
+}
+
+#if SUPPORT_AUDIO_CAPTURE
+/* These routines are used to support audio input.
+ * Do NOT compile these calls when using NT4 because it does
+ * not support the entry points.
+ */
+/************************************************************************************/
+HRESULT DSW_InitInputDevice( DSoundWrapper *dsw, LPGUID lpGUID )
+{
+    HRESULT hr = DirectSoundCaptureCreate(  lpGUID, &dsw->dsw_pDirectSoundCapture,   NULL );
+    if( hr != DS_OK ) return hr;
+    return hr;
+}
+/************************************************************************************/
+HRESULT DSW_InitInputBuffer( DSoundWrapper *dsw, unsigned long nFrameRate, int nChannels, int bytesPerBuffer )
+{
+    DSCBUFFERDESC  captureDesc;
+    WAVEFORMATEX   wfFormat;
+    HRESULT        result;
+    // Define the buffer format
+    wfFormat.wFormatTag      = WAVE_FORMAT_PCM;
+    wfFormat.nChannels       = nChannels;
+    wfFormat.nSamplesPerSec  = nFrameRate;
+    wfFormat.wBitsPerSample  = 8 * sizeof(short);
+    wfFormat.nBlockAlign     = wfFormat.nChannels * (wfFormat.wBitsPerSample / 8);
+    wfFormat.nAvgBytesPerSec = wfFormat.nSamplesPerSec * wfFormat.nBlockAlign;
+    wfFormat.cbSize          = 0;   /* No extended format info. */
+    dsw->dsw_InputSize = bytesPerBuffer;
+    // ----------------------------------------------------------------------
+    // Setup the secondary buffer description
+    ZeroMemory(&captureDesc, sizeof(DSCBUFFERDESC));
+    captureDesc.dwSize = sizeof(DSCBUFFERDESC);
+    captureDesc.dwFlags =  0;
+    captureDesc.dwBufferBytes = bytesPerBuffer;
+    captureDesc.lpwfxFormat = &wfFormat;
+    // Create the capture buffer
+    if ((result = IDirectSoundCapture_CreateCaptureBuffer( dsw->dsw_pDirectSoundCapture,
+                  &captureDesc, &dsw->dsw_InputBuffer, NULL)) != DS_OK) return result;
+    dsw->dsw_ReadOffset = 0;  // reset last read position to start of buffer
+    return DS_OK;
+}
+
+/************************************************************************************/
+HRESULT DSW_StartInput( DSoundWrapper *dsw )
+{
+    // Start the buffer playback
+    if( dsw->dsw_InputBuffer != NULL )
+    {
+        return IDirectSoundCaptureBuffer_Start( dsw->dsw_InputBuffer, DSCBSTART_LOOPING );
+    }
+    else return 0;
+}
+
+/************************************************************************************/
+HRESULT DSW_StopInput( DSoundWrapper *dsw )
+{
+    // Stop the buffer playback
+    if( dsw->dsw_InputBuffer != NULL )
+    {
+        return IDirectSoundCaptureBuffer_Stop( dsw->dsw_InputBuffer );
+    }
+    else return 0;
+}
+
+/************************************************************************************/
+HRESULT DSW_QueryInputFilled( DSoundWrapper *dsw, long *bytesFilled )
+{
+    HRESULT hr;
+    DWORD capturePos;
+    DWORD readPos;
+    long  filled;
+    // Query to see how much data is in buffer.
+    // We don't need the capture position but sometimes DirectSound doesn't handle NULLS correctly
+    // so let's pass a pointer just to be safe.
+    hr = IDirectSoundCaptureBuffer_GetCurrentPosition( dsw->dsw_InputBuffer, &capturePos, &readPos );
+    if( hr != DS_OK )
+    {
+        return hr;
+    }
+    filled = readPos - dsw->dsw_ReadOffset;
+    if( filled < 0 ) filled += dsw->dsw_InputSize; // unwrap offset
+    *bytesFilled = filled;
+    return hr;
+}
+
+/************************************************************************************/
+HRESULT DSW_ReadBlock( DSoundWrapper *dsw, char *buf, long numBytes )
+{
+    HRESULT hr;
+    LPBYTE lpbuf1 = NULL;
+    LPBYTE lpbuf2 = NULL;
+    DWORD dwsize1 = 0;
+    DWORD dwsize2 = 0;
+    // Lock free space in the DS
+    hr = IDirectSoundCaptureBuffer_Lock ( dsw->dsw_InputBuffer, dsw->dsw_ReadOffset, numBytes, (void **) &lpbuf1, &dwsize1,
+                                          (void **) &lpbuf2, &dwsize2, 0);
+    if (hr == DS_OK)
+    {
+        // Copy from DS to the buffer
+        CopyMemory( buf, lpbuf1, dwsize1);
+        if(lpbuf2 != NULL)
+        {
+            CopyMemory( buf+dwsize1, lpbuf2, dwsize2);
+        }
+        // Update our buffer offset and unlock sound buffer
+        dsw->dsw_ReadOffset = (dsw->dsw_ReadOffset + dwsize1 + dwsize2) % dsw->dsw_InputSize;
+        IDirectSoundCaptureBuffer_Unlock ( dsw->dsw_InputBuffer, lpbuf1, dwsize1, lpbuf2, dwsize2);
+    }
+    return hr;
+}
+
+#endif /* SUPPORT_AUDIO_CAPTURE */
diff --git a/pa_win_ds/dsound_wrapper.h b/pa_win_ds/dsound_wrapper.h
new file mode 100644 (file)
index 0000000..e926514
--- /dev/null
@@ -0,0 +1,106 @@
+#ifndef __DSOUND_WRAPPER_H
+#define __DSOUND_WRAPPER_H
+/*
+ * $Id$
+ * Simplified DirectSound interface.
+ *
+ * Author: Phil Burk & Robert Marsanyi
+ *
+ * For PortAudio Portable Real-Time Audio Library
+ * For more information see: http://www.softsynth.com/portaudio/
+ * DirectSound Implementation
+ * Copyright (c) 1999-2000 Phil Burk & Robert Marsanyi
+ *
+ * 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.
+ *
+ */
+#include <DSound.h>
+#if !defined(BOOL)
+#define BOOL short
+#endif
+#ifndef SUPPORT_AUDIO_CAPTURE
+#define SUPPORT_AUDIO_CAPTURE  (1)
+#endif
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif /* __cplusplus */
+
+#define DSW_NUM_POSITIONS     (4)
+#define DSW_NUM_EVENTS        (5)
+#define DSW_TERMINATION_EVENT     (DSW_NUM_POSITIONS)
+
+typedef struct
+{
+    /* Output */
+    LPDIRECTSOUND        dsw_pDirectSound;
+    LPDIRECTSOUNDBUFFER  dsw_OutputBuffer;
+    DWORD                dsw_WriteOffset;     /* last write position */
+    INT                  dsw_OutputSize;
+    INT                  dsw_BytesPerFrame;
+    /* Try to detect play buffer underflows. */
+    LARGE_INTEGER        dsw_CounterTicksPerBuffer; /* counter ticks it should take to play a full buffer */
+    LARGE_INTEGER        dsw_LastPlayTime;
+    UINT                 dsw_LastPlayCursor;
+    UINT                 dsw_OutputUnderflows;
+    BOOL                 dsw_OutputRunning;
+    /* use double which lets us can play for several thousand years with enough precision */
+    double               dsw_FramesWritten;
+    double               dsw_FramesPlayed;
+#if SUPPORT_AUDIO_CAPTURE
+    /* Input */
+    LPDIRECTSOUNDCAPTURE dsw_pDirectSoundCapture;
+    LPDIRECTSOUNDCAPTUREBUFFER   dsw_InputBuffer;
+    UINT                 dsw_ReadOffset;      /* last read position */
+    UINT                 dsw_InputSize;
+#endif /* SUPPORT_AUDIO_CAPTURE */
+
+}
+DSoundWrapper;
+HRESULT DSW_Init( DSoundWrapper *dsw );
+void DSW_Term( DSoundWrapper *dsw );
+HRESULT DSW_InitOutputBuffer( DSoundWrapper *dsw, unsigned long nFrameRate,
+                              int nChannels, int bufSize );
+HRESULT DSW_StartOutput( DSoundWrapper *dsw );
+HRESULT DSW_StopOutput( DSoundWrapper *dsw );
+DWORD   DSW_GetOutputStatus( DSoundWrapper *dsw );
+HRESULT DSW_WriteBlock( DSoundWrapper *dsw, char *buf, long numBytes );
+HRESULT DSW_ZeroEmptySpace( DSoundWrapper *dsw );
+HRESULT DSW_QueryOutputSpace( DSoundWrapper *dsw, long *bytesEmpty );
+HRESULT DSW_Enumerate( DSoundWrapper *dsw );
+
+#if SUPPORT_AUDIO_CAPTURE
+HRESULT DSW_InitInputBuffer( DSoundWrapper *dsw, unsigned long nFrameRate,
+                             int nChannels, int bufSize );
+HRESULT DSW_StartInput( DSoundWrapper *dsw );
+HRESULT DSW_StopInput( DSoundWrapper *dsw );
+HRESULT DSW_ReadBlock( DSoundWrapper *dsw, char *buf, long numBytes );
+HRESULT DSW_QueryInputFilled( DSoundWrapper *dsw, long *bytesFilled );
+#endif /* SUPPORT_AUDIO_CAPTURE */
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif  /* __DSOUND_WRAPPER_H */
diff --git a/pa_win_ds/pa_dsound.c b/pa_win_ds/pa_dsound.c
new file mode 100644 (file)
index 0000000..96f4753
--- /dev/null
@@ -0,0 +1,983 @@
+/*
+ * $Id$
+ * PortAudio Portable Real-Time Audio Library
+ * Latest Version at: http://www.softsynth.com/portaudio/
+ * DirectSound Implementation
+ *
+ * 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.
+ *
+ */
+/* Modifications
+ *    7/19/01 Mike Berry - casts for compiling with __MWERKS__ CodeWarrior
+ *    9/27/01 Phil Burk - use number of frames instead of real-time for CPULoad calculation.
+ */
+/* Compiler flags:
+ SUPPORT_AUDIO_CAPTURE - define this flag if you want to SUPPORT_AUDIO_CAPTURE
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#ifndef __MWERKS__
+#include <malloc.h>
+#include <memory.h>
+#endif //__MWERKS__
+#include <math.h>
+#include "portaudio.h"
+#include "pa_host.h"
+#include "pa_trace.h"
+#include "dsound_wrapper.h"
+#define PRINT(x) { printf x; fflush(stdout); }
+#define ERR_RPT(x) PRINT(x)
+#define DBUG(x)  /* PRINT(x) */
+#define DBUGX(x) /* PRINT(x) */
+#define PA_USE_HIGH_LATENCY   (0)
+#if PA_USE_HIGH_LATENCY
+#define PA_WIN_9X_LATENCY     (500)
+#define PA_WIN_NT_LATENCY     (600)
+#else
+#define PA_WIN_9X_LATENCY     (140)
+#define PA_WIN_NT_LATENCY     (280)
+#endif
+/* Trigger an underflow for testing purposes. Should normally be (0). */
+#define PA_SIMULATE_UNDERFLOW (0)
+#if PA_SIMULATE_UNDERFLOW
+static  gUnderCallbackCounter = 0;
+#define UNDER_START_GAP       (10)
+#define UNDER_STOP_GAP        (UNDER_START_GAP + 4)
+#endif
+/************************************************* Definitions ********/
+typedef struct internalPortAudioStream internalPortAudioStream;
+typedef struct internalPortAudioDevice
+{
+    GUID                             pad_GUID;
+    GUID                            *pad_lpGUID;
+    double                           pad_SampleRates[10]; /* for pointing to from pad_Info FIXME?!*/
+    PaDeviceInfo                     pad_Info;
+}
+internalPortAudioDevice;
+/* Define structure to contain all DirectSound and Windows specific data. */
+typedef struct PaHostSoundControl
+{
+    DSoundWrapper    pahsc_DSoundWrapper;
+    MMRESULT         pahsc_TimerID;
+    BOOL             pahsc_IfInsideCallback;  /* Test for reentrancy. */
+    short           *pahsc_NativeBuffer;
+    unsigned int     pahsc_BytesPerBuffer;    /* native buffer size in bytes */
+    double           pahsc_ValidFramesWritten;
+    int              pahsc_FramesPerDSBuffer;
+    /* For measuring CPU utilization. */
+    LARGE_INTEGER    pahsc_EntryCount;
+    double           pahsc_InverseTicksPerUserBuffer;
+}
+PaHostSoundControl;
+/************************************************* Shared Data ********/
+/* FIXME - put Mutex around this shared data. */
+static int sNumDevices = 0;
+static int sDeviceIndex = 0;
+static internalPortAudioDevice *sDevices = NULL;
+static int sDefaultInputDeviceID = paNoDevice;
+static int sDefaultOutputDeviceID = paNoDevice;
+static int sEnumerationError;
+static int sPaHostError = 0;
+/************************************************* Prototypes **********/
+static internalPortAudioDevice *Pa_GetInternalDevice( PaDeviceID id );
+static BOOL CALLBACK Pa_EnumProc(LPGUID lpGUID,
+                                 LPCTSTR lpszDesc,
+                                 LPCTSTR lpszDrvName,
+                                 LPVOID lpContext );
+static BOOL CALLBACK Pa_CountDevProc(LPGUID lpGUID,
+                                     LPCTSTR lpszDesc,
+                                     LPCTSTR lpszDrvName,
+                                     LPVOID lpContext );
+static Pa_QueryDevices( void );
+static void CALLBACK Pa_TimerCallback(UINT uID, UINT uMsg,
+                                      DWORD dwUser, DWORD dw1, DWORD dw2);
+/********************************* BEGIN CPU UTILIZATION MEASUREMENT ****/
+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 );
+}
+static void Pa_EndUsageCalculation( internalPortAudioStream   *past )
+{
+    LARGE_INTEGER CurrentCount = { 0, 0 };
+    PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData;
+    if( pahsc == NULL ) return;
+    /*
+    ** 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)
+#define LOWPASS_COEFFICIENT_1   (0.99999 - LOWPASS_COEFFICIENT_0)
+    if( QueryPerformanceCounter( &CurrentCount ) )
+    {
+        LONGLONG InsideCount = CurrentCount.QuadPart - pahsc->pahsc_EntryCount.QuadPart;
+        double newUsage = InsideCount * pahsc->pahsc_InverseTicksPerUserBuffer;
+        past->past_Usage = (LOWPASS_COEFFICIENT_0 * past->past_Usage) +
+                           (LOWPASS_COEFFICIENT_1 * newUsage);
+    }
+}
+/****************************************** END CPU UTILIZATION *******/
+static PaError Pa_QueryDevices( void )
+{
+    int numBytes;
+    sDefaultInputDeviceID = paNoDevice;
+    sDefaultOutputDeviceID = paNoDevice;
+    /* Enumerate once just to count devices. */
+    sNumDevices = 0; // for default device
+    DirectSoundEnumerate( (LPDSENUMCALLBACK)Pa_CountDevProc, NULL );
+#if SUPPORT_AUDIO_CAPTURE
+    DirectSoundCaptureEnumerate( (LPDSENUMCALLBACK)Pa_CountDevProc, NULL );
+#endif /* SUPPORT_AUDIO_CAPTURE */
+    /* Allocate structures to hold device info. */
+    numBytes = sNumDevices * sizeof(internalPortAudioDevice);
+    sDevices = (internalPortAudioDevice *)PaHost_AllocateFastMemory( numBytes ); /* MEM */
+    if( sDevices == NULL ) return paInsufficientMemory;
+    /* Enumerate again to fill in structures. */
+    sDeviceIndex = 0;
+    sEnumerationError = 0;
+    DirectSoundEnumerate( (LPDSENUMCALLBACK)Pa_EnumProc, (void *)0 );
+#if SUPPORT_AUDIO_CAPTURE
+    if( sEnumerationError != paNoError ) return sEnumerationError;
+    sEnumerationError = 0;
+    DirectSoundCaptureEnumerate( (LPDSENUMCALLBACK)Pa_EnumProc, (void *)1 );
+#endif /* SUPPORT_AUDIO_CAPTURE */
+    return sEnumerationError;
+}
+/************************************************************************************/
+long Pa_GetHostError()
+{
+    return sPaHostError;
+}
+/************************************************************************************
+** Just count devices so we know how much memory to allocate.
+*/
+static BOOL CALLBACK Pa_CountDevProc(LPGUID lpGUID,
+                                     LPCTSTR lpszDesc,
+                                     LPCTSTR lpszDrvName,
+                                     LPVOID lpContext )
+{
+    sNumDevices++;
+    return TRUE;
+}
+/************************************************************************************
+** Extract capabilities info from each device.
+*/
+static BOOL CALLBACK Pa_EnumProc(LPGUID lpGUID,
+                                 LPCTSTR lpszDesc,
+                                 LPCTSTR lpszDrvName,
+                                 LPVOID lpContext )
+{
+    HRESULT    hr;
+    LPDIRECTSOUND          lpDirectSound;
+#if SUPPORT_AUDIO_CAPTURE
+    LPDIRECTSOUNDCAPTURE   lpDirectSoundCapture;
+#endif /* SUPPORT_AUDIO_CAPTURE */
+    int        isInput  = (int) lpContext;  /* Passed from Pa_CountDevices() */
+    internalPortAudioDevice *pad;
+
+    if( sDeviceIndex >= sNumDevices )
+    {
+        sEnumerationError = paInternalError;
+        return FALSE;
+    }
+    pad = &sDevices[sDeviceIndex];
+    /* Copy GUID to static array. Set pointer. */
+    if( lpGUID == NULL )
+    {
+        pad->pad_lpGUID = NULL;
+    }
+    else
+    {
+        memcpy( &pad->pad_GUID, lpGUID, sizeof(GUID) );
+        pad->pad_lpGUID = &pad->pad_GUID;
+    }
+    pad->pad_Info.sampleRates = pad->pad_SampleRates;  /* Point to array. */
+    /* Allocate room for descriptive name. */
+    if( lpszDesc != NULL )
+    {
+        int len = strlen(lpszDesc);
+        pad->pad_Info.name = (char *)malloc( len+1 );
+        if( pad->pad_Info.name == NULL )
+        {
+            sEnumerationError = paInsufficientMemory;
+            return FALSE;
+        }
+        memcpy( (void *) pad->pad_Info.name, lpszDesc, len+1 );
+    }
+#if SUPPORT_AUDIO_CAPTURE
+    if( isInput )
+    {
+        /********** Input ******************************/
+        DSCCAPS     caps;
+        if( lpGUID == NULL ) sDefaultInputDeviceID = sDeviceIndex;
+        hr = DirectSoundCaptureCreate(  lpGUID, &lpDirectSoundCapture,   NULL );
+        if( hr != DS_OK )
+        {
+            pad->pad_Info.maxInputChannels = 0;
+            DBUG(("Cannot create Capture for %s. Result = 0x%x\n", lpszDesc, hr ));
+        }
+        else
+        {
+            /* Query device characteristics. */
+            caps.dwSize = sizeof(caps);
+            IDirectSoundCapture_GetCaps( lpDirectSoundCapture, &caps );
+            /* printf("caps.dwFormats = 0x%x\n", caps.dwFormats ); */
+            pad->pad_Info.maxInputChannels = caps.dwChannels;
+            /* Determine sample rates from flags. */
+            if( caps.dwChannels == 2 )
+            {
+                int index = 0;
+                if( caps.dwFormats & WAVE_FORMAT_1S16) pad->pad_SampleRates[index++] = 11025.0;
+                if( caps.dwFormats & WAVE_FORMAT_2S16) pad->pad_SampleRates[index++] = 22050.0;
+                if( caps.dwFormats & WAVE_FORMAT_4S16) pad->pad_SampleRates[index++] = 44100.0;
+                pad->pad_Info.numSampleRates = index;
+            }
+            else if( caps.dwChannels == 1 )
+            {
+                int index = 0;
+                if( caps.dwFormats & WAVE_FORMAT_1M16) pad->pad_SampleRates[index++] = 11025.0;
+                if( caps.dwFormats & WAVE_FORMAT_2M16) pad->pad_SampleRates[index++] = 22050.0;
+                if( caps.dwFormats & WAVE_FORMAT_4M16) pad->pad_SampleRates[index++] = 44100.0;
+                pad->pad_Info.numSampleRates = index;
+            }
+            else pad->pad_Info.numSampleRates = 0;
+            IDirectSoundCapture_Release( lpDirectSoundCapture );
+        }
+    }
+    else
+#endif /* SUPPORT_AUDIO_CAPTURE */
+
+    {
+        /********** Output ******************************/
+        DSCAPS     caps;
+        if( lpGUID == NULL ) sDefaultOutputDeviceID = sDeviceIndex;
+        /* Create interfaces for each object. */
+        hr = DirectSoundCreate(  lpGUID, &lpDirectSound,   NULL );
+        if( hr != DS_OK )
+        {
+            pad->pad_Info.maxOutputChannels = 0;
+            DBUG(("Cannot create dsound for %s. Result = 0x%x\n", lpszDesc, hr ));
+        }
+        else
+        {
+            /* Query device characteristics. */
+            caps.dwSize = sizeof(caps);
+            IDirectSound_GetCaps( lpDirectSound, &caps );
+            pad->pad_Info.maxOutputChannels = ( caps.dwFlags & DSCAPS_PRIMARYSTEREO ) ? 2 : 1;
+            /* Get sample rates. */
+            pad->pad_SampleRates[0] = (double) caps.dwMinSecondarySampleRate;
+            pad->pad_SampleRates[1] = (double) caps.dwMaxSecondarySampleRate;
+            if( caps.dwFlags & DSCAPS_CONTINUOUSRATE ) pad->pad_Info.numSampleRates = -1;
+            else if( caps.dwMinSecondarySampleRate == caps.dwMaxSecondarySampleRate )
+            {
+                if( caps.dwMinSecondarySampleRate == 0 )
+                {
+                    /*
+                    ** On my Thinkpad 380Z, DirectSoundV6 returns min-max=0 !!
+                    ** But it supports continuous sampling.
+                    ** So fake range of rates, and hope it really supports it.
+                    */
+                    pad->pad_SampleRates[0] = 11025.0f;
+                    pad->pad_SampleRates[1] = 48000.0f;
+                    pad->pad_Info.numSampleRates = -1; /* continuous range */
+
+                    DBUG(("PA - Reported rates both zero. Setting to fake values for device #%d\n", sDeviceIndex ));
+                }
+                else
+                {
+                    pad->pad_Info.numSampleRates = 1;
+                }
+            }
+            else if( (caps.dwMinSecondarySampleRate < 1000.0) && (caps.dwMaxSecondarySampleRate > 50000.0) )
+            {
+                /* The EWS88MT drivers lie, lie, lie. The say they only support two rates, 100 & 100000.
+                ** But we know that they really support a range of rates!
+                ** So when we see a ridiculous set of rates, assume it is a range.
+                */
+                pad->pad_Info.numSampleRates = -1;
+                DBUG(("PA - Sample rate range used instead of two odd values for device #%d\n", sDeviceIndex ));
+            }
+            else pad->pad_Info.numSampleRates = 2;
+            IDirectSound_Release( lpDirectSound );
+        }
+    }
+    pad->pad_Info.nativeSampleFormats = paInt16;
+    sDeviceIndex++;
+    return( TRUE );
+}
+/*************************************************************************/
+int Pa_CountDevices()
+{
+    if( sNumDevices <= 0 ) Pa_Initialize();
+    return sNumDevices;
+}
+static internalPortAudioDevice *Pa_GetInternalDevice( PaDeviceID id )
+{
+    if( (id < 0) || ( id >= Pa_CountDevices()) ) return NULL;
+    return &sDevices[id];
+}
+/*************************************************************************/
+const PaDeviceInfo* Pa_GetDeviceInfo( PaDeviceID id )
+{
+    internalPortAudioDevice *pad;
+    if( (id < 0) || ( id >= Pa_CountDevices()) ) return NULL;
+    pad = Pa_GetInternalDevice( id );
+    return  &pad->pad_Info ;
+}
+static PaError Pa_MaybeQueryDevices( void )
+{
+    if( sNumDevices == 0 )
+    {
+        return Pa_QueryDevices();
+    }
+    return 0;
+}
+/*************************************************************************
+** Returns recommended device ID.
+** On the PC, the recommended device can be specified by the user by
+** setting an environment variable. For example, to use device #1.
+**
+**    set PA_RECOMMENDED_OUTPUT_DEVICE=1
+**
+** The user should first determine the available device ID by using
+** the supplied application "pa_devs".
+*/
+#define PA_ENV_BUF_SIZE  (32)
+#define PA_REC_IN_DEV_ENV_NAME  ("PA_RECOMMENDED_INPUT_DEVICE")
+#define PA_REC_OUT_DEV_ENV_NAME  ("PA_RECOMMENDED_OUTPUT_DEVICE")
+static PaDeviceID PaHost_GetEnvDefaultDeviceID( char *envName )
+{
+    DWORD   hresult;
+    char    envbuf[PA_ENV_BUF_SIZE];
+    PaDeviceID recommendedID = paNoDevice;
+    /* Let user determine default device by setting environment variable. */
+    hresult = GetEnvironmentVariable( envName, envbuf, PA_ENV_BUF_SIZE );
+    if( (hresult > 0) && (hresult < PA_ENV_BUF_SIZE) )
+    {
+        recommendedID = atoi( envbuf );
+    }
+    return recommendedID;
+}
+PaDeviceID Pa_GetDefaultInputDeviceID( void )
+{
+    PaError result;
+    result = PaHost_GetEnvDefaultDeviceID( PA_REC_IN_DEV_ENV_NAME );
+    if( result < 0 )
+    {
+        result = Pa_MaybeQueryDevices();
+        if( result < 0 ) return result;
+        result = sDefaultInputDeviceID;
+    }
+    return result;
+}
+PaDeviceID Pa_GetDefaultOutputDeviceID( void )
+{
+    PaError result;
+    result = PaHost_GetEnvDefaultDeviceID( PA_REC_OUT_DEV_ENV_NAME );
+    if( result < 0 )
+    {
+        result = Pa_MaybeQueryDevices();
+        if( result < 0 ) return result;
+        result = sDefaultOutputDeviceID;
+    }
+    return result;
+}
+/**********************************************************************
+** Make sure that we have queried the device capabilities.
+*/
+PaError PaHost_Init( void )
+{
+#if PA_SIMULATE_UNDERFLOW
+    PRINT(("WARNING - Underflow Simulation Enabled - Expect a Big Glitch!!!\n"));
+#endif
+    return Pa_MaybeQueryDevices();
+}
+static PaError Pa_TimeSlice( internalPortAudioStream   *past )
+{
+    PaError           result = 0;
+    long              bytesEmpty = 0;
+    long              bytesFilled = 0;
+    long              bytesToXfer = 0;
+    long              numChunks;
+    HRESULT           hresult;
+    PaHostSoundControl  *pahsc;
+    short            *nativeBufPtr;
+    past->past_NumCallbacks += 1;
+    pahsc = (PaHostSoundControl *) past->past_DeviceData;
+    if( pahsc == NULL ) return paInternalError;
+    /* How much input data is available? */
+#if SUPPORT_AUDIO_CAPTURE
+    if( past->past_NumInputChannels > 0 )
+    {
+        DSW_QueryInputFilled( &pahsc->pahsc_DSoundWrapper, &bytesFilled );
+        bytesToXfer = bytesFilled;
+    }
+#endif /* SUPPORT_AUDIO_CAPTURE */
+    /* How much output room is available? */
+    if( past->past_NumOutputChannels > 0 )
+    {
+        DSW_QueryOutputSpace( &pahsc->pahsc_DSoundWrapper, &bytesEmpty );
+        bytesToXfer = bytesEmpty;
+    }
+    AddTraceMessage( "bytesEmpty ", bytesEmpty );
+    /* Choose smallest value if both are active. */
+    if( (past->past_NumInputChannels > 0) && (past->past_NumOutputChannels > 0) )
+    {
+        bytesToXfer = ( bytesFilled < bytesEmpty ) ? bytesFilled : bytesEmpty;
+    }
+    /* printf("bytesFilled = %d, bytesEmpty = %d, bytesToXfer = %d\n",
+      bytesFilled, bytesEmpty, bytesToXfer);
+    */
+    /* Quantize to multiples of a buffer. */
+    numChunks = bytesToXfer / pahsc->pahsc_BytesPerBuffer;
+    if( numChunks > (long)(past->past_NumUserBuffers/2) )
+    {
+        numChunks = (long)past->past_NumUserBuffers/2;
+    }
+    else if( numChunks < 0 )
+    {
+        numChunks = 0;
+    }
+    AddTraceMessage( "numChunks ", numChunks );
+    nativeBufPtr = pahsc->pahsc_NativeBuffer;
+    if( numChunks > 0 )
+    {
+        while( numChunks-- > 0 )
+        {
+            /* Measure usage based on time to process one user buffer. */
+            Pa_StartUsageCalculation( past );
+#if SUPPORT_AUDIO_CAPTURE
+            /* Get native data from DirectSound. */
+            if( past->past_NumInputChannels > 0 )
+            {
+                hresult = DSW_ReadBlock( &pahsc->pahsc_DSoundWrapper, (char *) nativeBufPtr, pahsc->pahsc_BytesPerBuffer );
+                if( hresult < 0 )
+                {
+                    ERR_RPT(("DirectSound ReadBlock failed, hresult = 0x%x\n",hresult));
+                    sPaHostError = hresult;
+                    break;
+                }
+            }
+#endif /* SUPPORT_AUDIO_CAPTURE */
+            /* Convert 16 bit native data to user data and call user routine. */
+            result = Pa_CallConvertInt16( past, nativeBufPtr, nativeBufPtr );
+            if( result != 0) break;
+            /* Pass native data to DirectSound. */
+            if( past->past_NumOutputChannels > 0 )
+            {
+                /* static short DEBUGHACK = 0;
+                 DEBUGHACK += 0x0049;
+                 nativeBufPtr[0] = DEBUGHACK; /* Make buzz to see if DirectSound still running. */
+                hresult = DSW_WriteBlock( &pahsc->pahsc_DSoundWrapper, (char *) nativeBufPtr, pahsc->pahsc_BytesPerBuffer );
+                if( hresult < 0 )
+                {
+                    ERR_RPT(("DirectSound WriteBlock failed, result = 0x%x\n",hresult));
+                    sPaHostError = hresult;
+                    break;
+                }
+            }
+            Pa_EndUsageCalculation( past );
+        }
+    }
+    return result;
+}
+/*******************************************************************/
+static void CALLBACK Pa_TimerCallback(UINT uID, UINT uMsg, DWORD dwUser, DWORD dw1, DWORD dw2)
+{
+    internalPortAudioStream   *past;
+    PaHostSoundControl  *pahsc;
+#if PA_SIMULATE_UNDERFLOW
+    gUnderCallbackCounter++;
+    if( (gUnderCallbackCounter >= UNDER_START_GAP) &&
+            (gUnderCallbackCounter <= UNDER_STOP_GAP) )
+    {
+        if( gUnderCallbackCounter == UNDER_START_GAP)
+        {
+            AddTraceMessage("Begin stall: gUnderCallbackCounter =======", gUnderCallbackCounter );
+        }
+        if( gUnderCallbackCounter == UNDER_STOP_GAP)
+        {
+            AddTraceMessage("End stall: gUnderCallbackCounter =======", gUnderCallbackCounter );
+        }
+        return;
+    }
+#endif
+    past = (internalPortAudioStream *) dwUser;
+    if( past == NULL ) return;
+    pahsc = (PaHostSoundControl *) past->past_DeviceData;
+    if( pahsc == NULL ) return;
+    if( !pahsc->pahsc_IfInsideCallback && past->past_IsActive )
+    {
+        if( past->past_StopNow )
+        {
+            past->past_IsActive = 0;
+        }
+        else if( past->past_StopSoon )
+        {
+            DSoundWrapper   *dsw = &pahsc->pahsc_DSoundWrapper;
+            if( past->past_NumOutputChannels > 0 )
+            {
+                DSW_ZeroEmptySpace( dsw );
+                AddTraceMessage("Pa_TimerCallback: waiting - written ", (int) dsw->dsw_FramesWritten );
+                AddTraceMessage("Pa_TimerCallback: waiting - played ", (int) dsw->dsw_FramesPlayed );
+                /* clear past_IsActive when all sound played */
+                if( dsw->dsw_FramesPlayed >= past->past_FrameCount )
+                {
+                    past->past_IsActive = 0;
+                }
+            }
+            else
+            {
+                past->past_IsActive = 0;
+            }
+        }
+        else
+        {
+            pahsc->pahsc_IfInsideCallback = 1;
+            if( Pa_TimeSlice( past ) != 0)  /* Call time slice independant of timing method. */
+            {
+                past->past_StopSoon = 1;
+            }
+            pahsc->pahsc_IfInsideCallback = 0;
+        }
+    }
+}
+/*******************************************************************/
+PaError PaHost_OpenStream( internalPortAudioStream   *past )
+{
+    HRESULT          hr;
+    PaError          result = paNoError;
+    PaHostSoundControl *pahsc;
+    int              numBytes, maxChannels;
+    unsigned int     minNumBuffers;
+    internalPortAudioDevice *pad;
+    DSoundWrapper   *dsw;
+    /* Allocate and initialize host data. */
+    pahsc = (PaHostSoundControl *) PaHost_AllocateFastMemory(sizeof(PaHostSoundControl)); /* MEM */
+    if( pahsc == NULL )
+    {
+        result = paInsufficientMemory;
+        goto error;
+    }
+    memset( pahsc, 0, sizeof(PaHostSoundControl) );
+    past->past_DeviceData = (void *) pahsc;
+    pahsc->pahsc_TimerID = 0;
+    dsw = &pahsc->pahsc_DSoundWrapper;
+    DSW_Init( dsw );
+    /* Allocate native buffer. */
+    maxChannels = ( past->past_NumOutputChannels > past->past_NumInputChannels ) ?
+                  past->past_NumOutputChannels : past->past_NumInputChannels;
+    pahsc->pahsc_BytesPerBuffer = past->past_FramesPerUserBuffer * maxChannels * sizeof(short);
+    if( maxChannels > 0 )
+    {
+        pahsc->pahsc_NativeBuffer = (short *) PaHost_AllocateFastMemory(pahsc->pahsc_BytesPerBuffer); /* MEM */
+        if( pahsc->pahsc_NativeBuffer == NULL )
+        {
+            result = paInsufficientMemory;
+            goto error;
+        }
+    }
+    else
+    {
+        result = paInvalidChannelCount;
+        goto error;
+    }
+
+    DBUG(("PaHost_OpenStream: pahsc_MinFramesPerHostBuffer = %d\n", pahsc->pahsc_MinFramesPerHostBuffer ));
+    minNumBuffers = Pa_GetMinNumBuffers( past->past_FramesPerUserBuffer, past->past_SampleRate );
+    past->past_NumUserBuffers = ( minNumBuffers > past->past_NumUserBuffers ) ? minNumBuffers : past->past_NumUserBuffers;
+    numBytes = pahsc->pahsc_BytesPerBuffer * past->past_NumUserBuffers;
+    if( numBytes < DSBSIZE_MIN )
+    {
+        result = paBufferTooSmall;
+        goto error;
+    }
+    if( numBytes > DSBSIZE_MAX )
+    {
+        result = paBufferTooBig;
+        goto error;
+    }
+    pahsc->pahsc_FramesPerDSBuffer = past->past_FramesPerUserBuffer * past->past_NumUserBuffers;
+    {
+        int msecLatency = (int) ((pahsc->pahsc_FramesPerDSBuffer * 1000) / past->past_SampleRate);
+        PRINT(("PortAudio on DirectSound - Latency = %d frames, %d msec\n", pahsc->pahsc_FramesPerDSBuffer, msecLatency ));
+    }
+    /* ------------------ OUTPUT */
+    if( (past->past_OutputDeviceID >= 0) && (past->past_NumOutputChannels > 0) )
+    {
+        DBUG(("PaHost_OpenStream: deviceID = 0x%x\n", past->past_OutputDeviceID));
+        pad = Pa_GetInternalDevice( past->past_OutputDeviceID );
+        hr = DirectSoundCreate( pad->pad_lpGUID, &dsw->dsw_pDirectSound,   NULL );
+        /* If this fails, then try each output device until we find one that works. */
+        if( hr != DS_OK )
+        {
+            int i;
+            ERR_RPT(("Creation of requested Audio Output device '%s' failed.\n",
+                     ((pad->pad_lpGUID == NULL) ? "Default" : pad->pad_Info.name) ));
+            for( i=0; i<Pa_CountDevices(); i++ )
+            {
+                pad = Pa_GetInternalDevice( i );
+                if( pad->pad_Info.maxOutputChannels >= past->past_NumOutputChannels )
+                {
+                    DBUG(("Try device '%s' instead.\n", pad->pad_Info.name ));
+                    hr = DirectSoundCreate( pad->pad_lpGUID, &dsw->dsw_pDirectSound,   NULL );
+                    if( hr == DS_OK )
+                    {
+                        ERR_RPT(("Using device '%s' instead.\n", pad->pad_Info.name ));
+                        break;
+                    }
+                }
+            }
+        }
+        if( hr != DS_OK )
+        {
+            ERR_RPT(("PortAudio: DirectSoundCreate() failed!\n"));
+            result = paHostError;
+            sPaHostError = hr;
+            goto error;
+        }
+        hr = DSW_InitOutputBuffer( dsw,
+                                   (unsigned long) (past->past_SampleRate + 0.5),
+                                   past->past_NumOutputChannels, numBytes );
+        DBUG(("DSW_InitOutputBuffer() returns %x\n", hr));
+        if( hr != DS_OK )
+        {
+            result = paHostError;
+            sPaHostError = hr;
+            goto error;
+        }
+        past->past_FrameCount = pahsc->pahsc_DSoundWrapper.dsw_FramesWritten;
+    }
+#if SUPPORT_AUDIO_CAPTURE
+    /* ------------------ INPUT */
+    if( (past->past_InputDeviceID >= 0) && (past->past_NumInputChannels > 0) )
+    {
+        pad = Pa_GetInternalDevice( past->past_InputDeviceID );
+        hr = DirectSoundCaptureCreate( pad->pad_lpGUID, &dsw->dsw_pDirectSoundCapture,   NULL );
+        /* If this fails, then try each input device until we find one that works. */
+        if( hr != DS_OK )
+        {
+            int i;
+            ERR_RPT(("Creation of requested Audio Capture device '%s' failed.\n",
+                     ((pad->pad_lpGUID == NULL) ? "Default" : pad->pad_Info.name) ));
+            for( i=0; i<Pa_CountDevices(); i++ )
+            {
+                pad = Pa_GetInternalDevice( i );
+                if( pad->pad_Info.maxInputChannels >= past->past_NumInputChannels )
+                {
+                    PRINT(("Try device '%s' instead.\n", pad->pad_Info.name ));
+                    hr = DirectSoundCaptureCreate( pad->pad_lpGUID, &dsw->dsw_pDirectSoundCapture,   NULL );
+                    if( hr == DS_OK ) break;
+                }
+            }
+        }
+        if( hr != DS_OK )
+        {
+            ERR_RPT(("PortAudio: DirectSoundCaptureCreate() failed!\n"));
+            result = paHostError;
+            sPaHostError = hr;
+            goto error;
+        }
+        hr = DSW_InitInputBuffer( dsw,
+                                  (unsigned long) (past->past_SampleRate + 0.5),
+                                  past->past_NumInputChannels, numBytes );
+        DBUG(("DSW_InitInputBuffer() returns %x\n", hr));
+        if( hr != DS_OK )
+        {
+            ERR_RPT(("PortAudio: DSW_InitInputBuffer() returns %x\n", hr));
+            result = paHostError;
+            sPaHostError = hr;
+            goto error;
+        }
+    }
+#endif /* SUPPORT_AUDIO_CAPTURE */
+    /* Calculate scalar used in CPULoad calculation. */
+    {
+        LARGE_INTEGER frequency;
+        if( QueryPerformanceFrequency( &frequency ) == 0 )
+        {
+            pahsc->pahsc_InverseTicksPerUserBuffer = 0.0;
+        }
+        else
+        {
+            pahsc->pahsc_InverseTicksPerUserBuffer = past->past_SampleRate /
+                    ( (double)frequency.QuadPart * past->past_FramesPerUserBuffer );
+            DBUG(("pahsc_InverseTicksPerUserBuffer = %g\n", pahsc->pahsc_InverseTicksPerUserBuffer ));
+        }
+    }
+    return result;
+error:
+    PaHost_CloseStream( past );
+    return result;
+}
+/*************************************************************************/
+PaError PaHost_StartOutput( internalPortAudioStream *past )
+{
+    HRESULT          hr;
+    PaHostSoundControl *pahsc;
+    PaError          result = paNoError;
+    pahsc = (PaHostSoundControl *) past->past_DeviceData;
+    /* Give user callback a chance to pre-fill buffer. */
+    result = Pa_TimeSlice( past );
+    if( result != paNoError ) return result; // FIXME - what if finished?
+    hr = DSW_StartOutput( &pahsc->pahsc_DSoundWrapper );
+    DBUG(("PaHost_StartOutput: DSW_StartOutput returned = 0x%X.\n", hr));
+    if( hr != DS_OK )
+    {
+        result = paHostError;
+        sPaHostError = hr;
+        goto error;
+    }
+error:
+    return result;
+}
+/*************************************************************************/
+PaError PaHost_StartInput( internalPortAudioStream *past )
+{
+    PaError          result = paNoError;
+#if SUPPORT_AUDIO_CAPTURE
+    HRESULT          hr;
+    PaHostSoundControl *pahsc;
+    pahsc = (PaHostSoundControl *) past->past_DeviceData;
+    hr = DSW_StartInput( &pahsc->pahsc_DSoundWrapper );
+    DBUG(("Pa_StartStream: DSW_StartInput returned = 0x%X.\n", hr));
+    if( hr != DS_OK )
+    {
+        result = paHostError;
+        sPaHostError = hr;
+        goto error;
+    }
+error:
+#endif /* SUPPORT_AUDIO_CAPTURE */
+    return result;
+}
+/*************************************************************************/
+PaError PaHost_StartEngine( internalPortAudioStream *past )
+{
+    PaHostSoundControl *pahsc;
+    PaError          result = paNoError;
+    pahsc = (PaHostSoundControl *) past->past_DeviceData;
+    past->past_StopNow = 0;
+    past->past_StopSoon = 0;
+    past->past_IsActive = 1;
+    /* Create timer that will wake us up so we can fill the DSound buffer. */
+    {
+        int msecPerBuffer;
+        int resolution;
+        int bufsPerInterrupt = past->past_NumUserBuffers/4;
+        if( bufsPerInterrupt < 1 ) bufsPerInterrupt = 1;
+        msecPerBuffer = 1000 * (bufsPerInterrupt * past->past_FramesPerUserBuffer) / (int) past->past_SampleRate;
+        if( msecPerBuffer < 10 ) msecPerBuffer = 10;
+        else if( msecPerBuffer > 100 ) msecPerBuffer = 100;
+        resolution = msecPerBuffer/4;
+        pahsc->pahsc_TimerID = timeSetEvent( msecPerBuffer, resolution, (LPTIMECALLBACK) Pa_TimerCallback,
+                                             (DWORD) past, TIME_PERIODIC );
+    }
+    if( pahsc->pahsc_TimerID == 0 )
+    {
+        past->past_IsActive = 0;
+        result = paHostError;
+        sPaHostError = 0;
+        goto error;
+    }
+error:
+    return result;
+}
+/*************************************************************************/
+PaError PaHost_StopEngine( internalPortAudioStream *past, int abort )
+{
+    int timeoutMsec;
+    PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData;
+    if( pahsc == NULL ) return paNoError;
+    if( abort ) past->past_StopNow = 1;
+    past->past_StopSoon = 1;
+    /* Set timeout at 20% beyond maximum time we might wait. */
+    timeoutMsec = (int) (1200.0 * pahsc->pahsc_FramesPerDSBuffer / past->past_SampleRate);
+    while( past->past_IsActive && (timeoutMsec > 0)  )
+    {
+        Sleep(10);
+        timeoutMsec -= 10;
+    }
+    if( pahsc->pahsc_TimerID != 0 )
+    {
+        timeKillEvent(pahsc->pahsc_TimerID);  /* Stop callback timer. */
+        pahsc->pahsc_TimerID = 0;
+    }
+    return paNoError;
+}
+/*************************************************************************/
+PaError PaHost_StopInput( internalPortAudioStream *past, int abort )
+{
+#if SUPPORT_AUDIO_CAPTURE
+    HRESULT hr;
+    PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData;
+    if( pahsc == NULL ) return paNoError;
+    (void) abort;
+    hr = DSW_StopInput( &pahsc->pahsc_DSoundWrapper );
+    DBUG(("DSW_StopInput() result is %x\n", hr));
+#endif /* SUPPORT_AUDIO_CAPTURE */
+    return paNoError;
+}
+/*************************************************************************/
+PaError PaHost_StopOutput( internalPortAudioStream *past, int abort )
+{
+    HRESULT hr;
+    PaHostSoundControl *pahsc;
+    pahsc = (PaHostSoundControl *) past->past_DeviceData;
+    if( pahsc == NULL ) return paNoError;
+    (void) abort;
+    hr = DSW_StopOutput( &pahsc->pahsc_DSoundWrapper );
+    DBUG(("DSW_StopOutput() result is %x\n", hr));
+    return paNoError;
+}
+/*******************************************************************/
+PaError PaHost_CloseStream( internalPortAudioStream   *past )
+{
+    PaHostSoundControl *pahsc;
+    if( past == NULL ) return paBadStreamPtr;
+    pahsc = (PaHostSoundControl *) past->past_DeviceData;
+    if( pahsc == NULL ) return paNoError;
+    DSW_Term( &pahsc->pahsc_DSoundWrapper );
+    if( pahsc->pahsc_NativeBuffer )
+    {
+        PaHost_FreeFastMemory( pahsc->pahsc_NativeBuffer, pahsc->pahsc_BytesPerBuffer ); /* MEM */
+        pahsc->pahsc_NativeBuffer = NULL;
+    }
+    PaHost_FreeFastMemory( pahsc, sizeof(PaHostSoundControl) ); /* MEM */
+    past->past_DeviceData = NULL;
+    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 AUTOEXEC.BAT file and reboot.
+** If the environment variable is not set, then the latency will be determined
+** based on the OS. Windows NT has higher latency than Win95.
+*/
+#define PA_LATENCY_ENV_NAME  ("PA_MIN_LATENCY_MSEC")
+int Pa_GetMinNumBuffers( int framesPerBuffer, double sampleRate )
+{
+    char      envbuf[PA_ENV_BUF_SIZE];
+    DWORD     hostVersion;
+    DWORD     hresult;
+    int       minLatencyMsec = 0;
+    double    msecPerBuffer = (1000.0 * framesPerBuffer) / sampleRate;
+    int       minBuffers;
+    /* Let user determine minimal latency by setting environment variable. */
+    hresult = GetEnvironmentVariable( PA_LATENCY_ENV_NAME, envbuf, PA_ENV_BUF_SIZE );
+    if( (hresult > 0) && (hresult < PA_ENV_BUF_SIZE) )
+    {
+        minLatencyMsec = atoi( envbuf );
+    }
+    else
+    {
+        /* Set minimal latency based on whether NT or Win95.
+         * NT has higher latency.
+         */
+        hostVersion = GetVersion();
+        /* High bit clear if NT */
+        minLatencyMsec = ( (hostVersion & 0x80000000) == 0 ) ? PA_WIN_NT_LATENCY : PA_WIN_9X_LATENCY  ;
+#if PA_USE_HIGH_LATENCY
+        PRINT(("PA - Minimum Latency set to %d msec!\n", minLatencyMsec ));
+#endif
+
+    }
+    minBuffers = (int) (1.0 + ((double)minLatencyMsec / msecPerBuffer));
+    if( minBuffers < 2 ) minBuffers = 2;
+    return minBuffers;
+}
+/*************************************************************************/
+PaError PaHost_Term( void )
+{
+    int i;
+    /* Free names allocated during enumeration. */
+    for( i=0; i<sNumDevices; i++ )
+    {
+        if( sDevices[i].pad_Info.name != NULL )
+        {
+            free( (void *) sDevices[i].pad_Info.name );
+            sDevices[i].pad_Info.name = NULL;
+        }
+    }
+    if( sDevices != NULL )
+    {
+        PaHost_FreeFastMemory( sDevices, sNumDevices * sizeof(internalPortAudioDevice) ); /* MEM */
+        sDevices = NULL;
+        sNumDevices = 0;
+    }
+    return 0;
+}
+void Pa_Sleep( long msec )
+{
+    Sleep( msec );
+}
+/*************************************************************************
+ * Allocate memory that can be accessed in real-time.
+ * This may need to be held in physical memory so that it is not
+ * paged to virtual memory.
+ * This call MUST be balanced with a call to PaHost_FreeFastMemory().
+ * Memory will be set to zero.
+ */
+void *PaHost_AllocateFastMemory( long numBytes )
+{
+    void *addr = GlobalAlloc( GPTR, numBytes ); /* FIXME - do we need physical memory? Use VirtualLock() */ /* MEM */
+    return addr;
+}
+/*************************************************************************
+ * Free memory that could be accessed in real-time.
+ * This call MUST be balanced with a call to PaHost_AllocateFastMemory().
+ */
+void PaHost_FreeFastMemory( void *addr, long numBytes )
+{
+    if( addr != NULL ) GlobalFree( addr ); /* MEM */
+}
+/***********************************************************************/
+PaError PaHost_StreamActive( internalPortAudioStream   *past )
+{
+    PaHostSoundControl *pahsc;
+    if( past == NULL ) return paBadStreamPtr;
+    pahsc = (PaHostSoundControl *) past->past_DeviceData;
+    if( pahsc == NULL ) return paInternalError;
+    return (PaError) (past->past_IsActive);
+}
+/*************************************************************************/
+PaTimestamp Pa_StreamTime( PortAudioStream *stream )
+{
+    DSoundWrapper   *dsw;
+    internalPortAudioStream   *past = (internalPortAudioStream *) stream;
+    PaHostSoundControl *pahsc;
+    if( past == NULL ) return paBadStreamPtr;
+    pahsc = (PaHostSoundControl *) past->past_DeviceData;
+    dsw = &pahsc->pahsc_DSoundWrapper;
+    return dsw->dsw_FramesPlayed;
+}
diff --git a/pa_win_ds/portaudio.def b/pa_win_ds/portaudio.def
new file mode 100644 (file)
index 0000000..8012b99
--- /dev/null
@@ -0,0 +1,28 @@
+LIBRARY      PortAudio
+DESCRIPTION  'PortAudio   Portable interface to audio HW'
+
+EXPORTS
+    ; Explicit exports can go here
+       Pa_Initialize                   @1
+       Pa_Terminate                    @2
+       Pa_GetHostError                 @3
+       Pa_GetErrorText                 @4
+       Pa_CountDevices                 @5
+       Pa_GetDefaultInputDeviceID      @6
+       Pa_GetDefaultOutputDeviceID     @7
+       Pa_GetDeviceInfo                @8
+       Pa_OpenStream                   @9
+       Pa_OpenDefaultStream            @10
+       Pa_CloseStream                  @11
+       Pa_StartStream                  @12
+       Pa_StopStream                   @13
+       Pa_StreamActive                 @14
+       Pa_StreamTime                   @15
+       Pa_GetCPULoad                   @16
+       Pa_GetMinNumBuffers             @17
+       Pa_Sleep                        @18
+
+   ;123456789012345678901234567890123456
+   ;000000000111111111122222222223333333
+
+
diff --git a/pa_win_wmme/Makefile.cygwin b/pa_win_wmme/Makefile.cygwin
new file mode 100644 (file)
index 0000000..5cb4ace
--- /dev/null
@@ -0,0 +1,34 @@
+
+# Makefile for PortAudio on cygwin
+# Contributed by Bill Eldridge on 6/13/2001
+
+ARCH= pa_win_wmme
+
+TESTS:= $(wildcard pa_tests/pa*.c pa_tests/debug*.c)
+
+.c.o:
+       -gcc -c -I./pa_common $< -o $*.o
+       -gcc $*.o -o $*.exe -L/usr/local/lib -L$(ARCH) -lportaudio.dll -lwinmm
+
+all:   sharedlib tests
+
+sharedlib: ./pa_common/pa_lib.c 
+       gcc -c -I./pa_common pa_common/pa_lib.c -o pa_common/pa_lib.o
+       gcc -c -I./pa_common pa_win_wmme/pa_win_wmme.c -o pa_win_wmme/pa_win_wmme.o
+       dlltool --export-all --output-def pa_win_wmme/pa_lib.def pa_common/pa_lib.o pa_win_wmme/pa_win_wmme.o
+       gcc -shared -Wl,--enable-auto-image-base -o pa_win_wmme/portaudio.dll -Wl,--out-implib=pa_win_wmme/libportaudio.dll.a pa_win_wmme/pa_lib.def pa_common/pa_lib.o pa_win_wmme/pa_win_wmme.o -L/usr/lib/w32api -lwinmm
+       cp pa_win_wmme/portaudio.dll /usr/local/bin
+
+tests: $(TESTS:.c=.o)
+
+sine:
+       gcc -c -I./pa_common pa_tests/patest_sine.c -o pa_tests/patest_sine.o
+       gcc pa_tests/patest_sine.o -o pa_tests/patest_sine.exe -L/usr/local/lib -lportaudio.dll -lwinmm
+
+clean:
+       -rm ./pa_tests/*.exe
+       
+nothing:
+       gcc pa_tests/patest_sine.o -L/usr/lib/w32api -L./pa_win_wmme -lportaudio.dll -lwinmm
+
+       
diff --git a/pa_win_wmme/pa_win_wmme.c b/pa_win_wmme/pa_win_wmme.c
new file mode 100644 (file)
index 0000000..2b3dea2
--- /dev/null
@@ -0,0 +1,1493 @@
+/*
+ * $Id$
+ * pa_win_wmme.c
+ * Implementation of PortAudio for Windows MultiMedia Extensions (WMME)
+ *
+ * PortAudio Portable Real-Time Audio Library
+ * Latest Version at: http://www.portaudio.com
+ *
+ * Authors: Ross Bencina and Phil Burk
+ * Copyright (c) 1999-2000 Ross Bencina and Phil Burk
+ *
+ * Permission is hereby granted, free of charge, to any person obtainingF
+ * 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.
+ *
+ */
+/*
+  All memory allocations and frees are marked with MEM for quick review.
+*/
+
+/* Modification History:
+ PLB = Phil Burk
+ JM = Julien Maillard
+ PLB20010402 - sDevicePtrs now allocates based on sizeof(pointer)
+ PLB20010413 - check for excessive numbers of channels
+ PLB20010422 - apply Mike Berry's changes for CodeWarrior on PC
+               including condition including of memory.h,
+      and explicit typecasting on memory allocation
+ PLB20010802 - use GlobalAlloc for sDevicesPtr instead of PaHost_AllocFastMemory
+ PLB20010816 - pass process instead of thread to SetPriorityClass()
+ PLB20010927 - use number of frames instead of real-time for CPULoad calculation.
+ JM20020118 - prevent hung thread when buffers underflow.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <windows.h>
+#include <mmsystem.h>
+#include <process.h>
+/* PLB20010422 - "memory.h" doesn't work on CodeWarrior for PC. Thanks Mike Berry for the mod. */
+#ifndef __MWERKS__
+#include <malloc.h>
+#include <memory.h>
+#endif /* __MWERKS__ */
+#include "portaudio.h"
+#include "pa_host.h"
+#include "pa_trace.h"
+
+/************************************************* Constants ********/
+#define PA_USE_TIMER_CALLBACK    (0)  /* Select between two options for background task. 0=thread, 1=timer */
+#define PA_USE_HIGH_LATENCY      (0)  /* For debugging glitches. */
+/* 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)
+#define PA_TRACE_START_STOP      (1)
+#if PA_USE_HIGH_LATENCY
+ #define PA_MIN_MSEC_PER_HOST_BUFFER  (100)
+ #define PA_MAX_MSEC_PER_HOST_BUFFER  (300) /* Do not exceed unless user buffer exceeds */
+ #define PA_MIN_NUM_HOST_BUFFERS      (4)
+ #define PA_MAX_NUM_HOST_BUFFERS      (16)  /* OK to exceed if necessary */
+ #define PA_WIN_9X_LATENCY            (400)
+#else
+ #define PA_MIN_MSEC_PER_HOST_BUFFER  (10)
+ #define PA_MAX_MSEC_PER_HOST_BUFFER  (100) /* Do not exceed unless user buffer exceeds */
+ #define PA_MIN_NUM_HOST_BUFFERS      (3)
+ #define PA_MAX_NUM_HOST_BUFFERS      (16)  /* OK to exceed if necessary */
+ #define PA_WIN_9X_LATENCY            (200)
+#endif
+#define MIN_TIMEOUT_MSEC                 (1000)
+/*
+** Use higher latency for NT because it is even worse at real-time
+** operation than Win9x.
+*/
+#define PA_WIN_NT_LATENCY        (PA_WIN_9X_LATENCY * 2)
+
+#if PA_SIMULATE_UNDERFLOW
+static  gUnderCallbackCounter = 0;
+#define UNDER_SLEEP_AT       (40)
+#define UNDER_SLEEP_FOR      (500)
+#endif
+
+#define PRINT(x) { printf x; fflush(stdout); }
+#define ERR_RPT(x) PRINT(x)
+#define DBUG(x)  /* PRINT(x) */
+#define DBUGX(x) /* PRINT(x) */
+/************************************************* Definitions ********/
+/**************************************************************
+ * Structure for internal host specific stream data.
+ * This is allocated on a per stream basis.
+ */
+typedef struct PaHostSoundControl
+{
+    /* Input -------------- */
+    HWAVEIN            pahsc_HWaveIn;
+    WAVEHDR           *pahsc_InputBuffers;
+    int                pahsc_CurrentInputBuffer;
+    int                pahsc_BytesPerHostInputBuffer;
+    int                pahsc_BytesPerUserInputBuffer;    /* native buffer size in bytes */
+    /* Output -------------- */
+    HWAVEOUT           pahsc_HWaveOut;
+    WAVEHDR           *pahsc_OutputBuffers;
+    int                pahsc_CurrentOutputBuffer;
+    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;
+    double             pahsc_InverseTicksPerHostBuffer;
+    /* Init Time -------------- */
+    int                pahsc_NumHostBuffers;
+    int                pahsc_FramesPerHostBuffer;
+    int                pahsc_UserBuffersPerHostBuffer;
+    CRITICAL_SECTION   pahsc_StreamLock;                  /* Mutext to prevent threads from colliding. */
+    INT                pahsc_StreamLockInited;
+#if PA_USE_TIMER_CALLBACK
+    BOOL               pahsc_IfInsideCallback;            /* Test for reentrancy. */
+    MMRESULT           pahsc_TimerID;
+#else
+    HANDLE             pahsc_AbortEvent;
+    int                pahsc_AbortEventInited;
+    HANDLE             pahsc_BufferEvent;
+    int                pahsc_BufferEventInited;
+    HANDLE             pahsc_EngineThread;
+    DWORD              pahsc_EngineThreadID;
+#endif
+}
+PaHostSoundControl;
+/************************************************* Shared Data ********/
+/* FIXME - put Mutex around this shared data. */
+static int sNumInputDevices = 0;
+static int sNumOutputDevices = 0;
+static int sNumDevices = 0;
+static PaDeviceInfo **sDevicePtrs = NULL;
+static int sDefaultInputDeviceID = paNoDevice;
+static int sDefaultOutputDeviceID = paNoDevice;
+static int sPaHostError = 0;
+static const char sMapperSuffixInput[] = " - Input";
+static const char sMapperSuffixOutput[] = " - Output";
+/************************************************* Macros ********/
+/* Convert external PA ID to an internal ID that includes WAVE_MAPPER */
+#define PaDeviceIdToWinId(id) (((id) < sNumInputDevices) ? (id - 1) : (id - sNumInputDevices - 1))
+/************************************************* Prototypes **********/
+static Pa_QueryDevices( void );
+static void CALLBACK Pa_TimerCallback(UINT uID, UINT uMsg,
+                                      DWORD dwUser, DWORD dw1, DWORD dw2);
+PaError PaHost_GetTotalBufferFrames( internalPortAudioStream   *past );
+static PaError PaHost_UpdateStreamTime( PaHostSoundControl *pahsc );
+static PaError PaHost_BackgroundManager( internalPortAudioStream   *past );
+
+/********************************* BEGIN CPU UTILIZATION MEASUREMENT ****/
+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 );
+}
+static void Pa_EndUsageCalculation( internalPortAudioStream   *past )
+{
+    LARGE_INTEGER CurrentCount = { 0, 0 };
+    PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData;
+    if( pahsc == NULL ) return;
+    /*
+    ** 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)
+#define LOWPASS_COEFFICIENT_1   (0.99999 - LOWPASS_COEFFICIENT_0)
+    if( QueryPerformanceCounter( &CurrentCount ) )
+    {
+        LONGLONG InsideCount = CurrentCount.QuadPart - pahsc->pahsc_EntryCount.QuadPart;
+        double newUsage = InsideCount * pahsc->pahsc_InverseTicksPerHostBuffer;
+        past->past_Usage = (LOWPASS_COEFFICIENT_0 * past->past_Usage) +
+                           (LOWPASS_COEFFICIENT_1 * newUsage);
+    }
+}
+/****************************************** END CPU UTILIZATION *******/
+
+static PaError Pa_QueryDevices( void )
+{
+    int numBytes;
+    /* Count the devices and add one extra for the WAVE_MAPPER */
+    sNumInputDevices = waveInGetNumDevs() + 1;
+    sDefaultInputDeviceID = 0;
+    sNumOutputDevices = waveOutGetNumDevs() + 1;
+    sDefaultOutputDeviceID = sNumInputDevices;
+    sNumDevices = sNumInputDevices + sNumOutputDevices;
+    /* Allocate structures to hold device info. */
+    /* PLB20010402 - was allocating too much memory. */
+    /* numBytes = sNumDevices * sizeof(PaDeviceInfo);  // PLB20010402 */
+    numBytes = sNumDevices * sizeof(PaDeviceInfo *); /* PLB20010402 */
+    sDevicePtrs = (PaDeviceInfo **) GlobalAlloc( GPTR, numBytes ); /* MEM */
+    if( sDevicePtrs == NULL ) return paInsufficientMemory;
+    return paNoError;
+}
+/************************************************************************************/
+long Pa_GetHostError()
+{
+    return sPaHostError;
+}
+/*************************************************************************/
+int Pa_CountDevices()
+{
+    if( sNumDevices <= 0 ) Pa_Initialize();
+    return sNumDevices;
+}
+/*************************************************************************
+** If a PaDeviceInfo structure has not already been created,
+** then allocate one and fill it in for the selected device.
+**
+** We create one extra input and one extra output device for the WAVE_MAPPER.
+** [Does anyone know how to query the default device and get its name?]
+*/
+const PaDeviceInfo* Pa_GetDeviceInfo( PaDeviceID id )
+{
+#define NUM_STANDARDSAMPLINGRATES   3   /* 11.025, 22.05, 44.1 */
+#define NUM_CUSTOMSAMPLINGRATES     5   /* must be the same number of elements as in the array below */
+#define MAX_NUMSAMPLINGRATES        (NUM_STANDARDSAMPLINGRATES+NUM_CUSTOMSAMPLINGRATES)
+    static DWORD customSamplingRates[] = { 32000, 48000, 64000, 88200, 96000 };
+    PaDeviceInfo *deviceInfo;
+    double *sampleRates; /* non-const ptr */
+    int i;
+    char *s;
+
+    if( id < 0 || id >= sNumDevices )
+        return NULL;
+    if( sDevicePtrs[ id ] != NULL )
+    {
+        return sDevicePtrs[ id ];
+    }
+    deviceInfo = (PaDeviceInfo *)GlobalAlloc( GPTR, sizeof(PaDeviceInfo) ); /* MEM */
+    if( deviceInfo == NULL ) return NULL;
+    deviceInfo->structVersion = 1;
+    deviceInfo->maxInputChannels = 0;
+    deviceInfo->maxOutputChannels = 0;
+    deviceInfo->numSampleRates = 0;
+    sampleRates = (double*)GlobalAlloc( GPTR, MAX_NUMSAMPLINGRATES * sizeof(double) ); /* MEM */
+    deviceInfo->sampleRates = sampleRates;
+    deviceInfo->nativeSampleFormats = paInt16;       /* should query for higher bit depths below */
+    if( id < sNumInputDevices )
+    {
+        /* input device */
+        int inputMmID = id - 1; /* WAVE_MAPPER is -1 so we start with WAVE_MAPPER */
+        WAVEINCAPS wic;
+        if( waveInGetDevCaps( inputMmID, &wic, sizeof( WAVEINCAPS ) ) != MMSYSERR_NOERROR )
+            goto error;
+
+        /* Append I/O suffix to WAVE_MAPPER device. */
+        if( inputMmID == WAVE_MAPPER )
+        {
+            s = (char *) GlobalAlloc( GMEM_FIXED, strlen( wic.szPname ) + 1 + sizeof(sMapperSuffixInput) ); /* MEM */
+            strcpy( s, wic.szPname );
+            strcat( s, sMapperSuffixInput );
+        }
+        else
+        {
+            s = (char *) GlobalAlloc( GMEM_FIXED, strlen( wic.szPname ) + 1 ); /* MEM */
+            strcpy( s, wic.szPname );
+        }
+        deviceInfo->name = s;
+        deviceInfo->maxInputChannels = wic.wChannels;
+        /* Sometimes a device can return a rediculously large number of channels.
+        ** This happened with an SBLive card on a Windows ME box.
+        ** If that happens, then force it to 2 channels.  PLB20010413
+        */
+        if( (deviceInfo->maxInputChannels < 1) || (deviceInfo->maxInputChannels > 256) )
+        {
+            ERR_RPT(("Pa_GetDeviceInfo: Num input channels reported as %d! Changed to 2.\n", deviceInfo->maxOutputChannels ));
+            deviceInfo->maxInputChannels = 2;
+        }
+        /* Add a sample rate to the list if we can do stereo 16 bit at that rate
+         * based on the format flags. */
+        if( wic.dwFormats & WAVE_FORMAT_1M16 ||wic.dwFormats & WAVE_FORMAT_1S16 )
+            sampleRates[ deviceInfo->numSampleRates++ ] = 11025.;
+        if( wic.dwFormats & WAVE_FORMAT_2M16 ||wic.dwFormats & WAVE_FORMAT_2S16 )
+            sampleRates[ deviceInfo->numSampleRates++ ] = 22050.;
+        if( wic.dwFormats & WAVE_FORMAT_4M16 ||wic.dwFormats & WAVE_FORMAT_4S16 )
+            sampleRates[ deviceInfo->numSampleRates++ ] = 44100.;
+        /* Add a sample rate to the list if we can do stereo 16 bit at that rate
+         * based on opening the device successfully. */
+        for( i=0; i < NUM_CUSTOMSAMPLINGRATES; i++ )
+        {
+            WAVEFORMATEX wfx;
+            wfx.wFormatTag = WAVE_FORMAT_PCM;
+            wfx.nSamplesPerSec = customSamplingRates[i];
+            wfx.wBitsPerSample = 16;
+            wfx.cbSize = 0; /* ignored */
+            wfx.nChannels = (WORD)deviceInfo->maxInputChannels;
+            wfx.nAvgBytesPerSec = wfx.nChannels * wfx.nSamplesPerSec * sizeof(short);
+            wfx.nBlockAlign = (WORD)(wfx.nChannels * sizeof(short));
+            if( waveInOpen( NULL, inputMmID, &wfx, 0, 0, WAVE_FORMAT_QUERY ) == MMSYSERR_NOERROR )
+            {
+                sampleRates[ deviceInfo->numSampleRates++ ] = customSamplingRates[i];
+            }
+        }
+
+    }
+    else if( id - sNumInputDevices < sNumOutputDevices )
+    {
+        /* output device */
+        int outputMmID = id - sNumInputDevices - 1;
+        WAVEOUTCAPS woc;
+        if( waveOutGetDevCaps( outputMmID, &woc, sizeof( WAVEOUTCAPS ) ) != MMSYSERR_NOERROR )
+            goto error;
+        /* Append I/O suffix to WAVE_MAPPER device. */
+        if( outputMmID == WAVE_MAPPER )
+        {
+            s = (char *) GlobalAlloc( GMEM_FIXED, strlen( woc.szPname ) + 1 + sizeof(sMapperSuffixOutput) );  /* MEM */
+            strcpy( s, woc.szPname );
+            strcat( s, sMapperSuffixOutput );
+        }
+        else
+        {
+            s = (char *) GlobalAlloc( GMEM_FIXED, strlen( woc.szPname ) + 1 );  /* MEM */
+            strcpy( s, woc.szPname );
+        }
+        deviceInfo->name = s;
+        deviceInfo->maxOutputChannels = woc.wChannels;
+        /* Sometimes a device can return a rediculously large number of channels.
+        ** This happened with an SBLive card on a Windows ME box.
+        ** If that happens, then force it to 2 channels. PLB20010413
+        */
+        if( (deviceInfo->maxOutputChannels < 1) || (deviceInfo->maxOutputChannels > 256) )
+        {
+            ERR_RPT(("Pa_GetDeviceInfo: Num output channels reported as %d! Changed to 2.\n", deviceInfo->maxOutputChannels ));
+            deviceInfo->maxOutputChannels = 2;
+        }
+        /* Add a sample rate to the list if we can do stereo 16 bit at that rate
+         * based on the format flags. */
+        if( woc.dwFormats & WAVE_FORMAT_1M16 ||woc.dwFormats & WAVE_FORMAT_1S16 )
+            sampleRates[ deviceInfo->numSampleRates++ ] = 11025.;
+        if( woc.dwFormats & WAVE_FORMAT_2M16 ||woc.dwFormats & WAVE_FORMAT_2S16 )
+            sampleRates[ deviceInfo->numSampleRates++ ] = 22050.;
+        if( woc.dwFormats & WAVE_FORMAT_4M16 ||woc.dwFormats & WAVE_FORMAT_4S16 )
+            sampleRates[ deviceInfo->numSampleRates++ ] = 44100.;
+        /* Add a sample rate to the list if we can do stereo 16 bit at that rate
+         * based on opening the device successfully. */
+        for( i=0; i < NUM_CUSTOMSAMPLINGRATES; i++ )
+        {
+            WAVEFORMATEX wfx;
+            wfx.wFormatTag = WAVE_FORMAT_PCM;
+            wfx.nSamplesPerSec = customSamplingRates[i];
+            wfx.wBitsPerSample = 16;
+            wfx.cbSize = 0; /* ignored */
+            wfx.nChannels = (WORD)deviceInfo->maxOutputChannels;
+            wfx.nAvgBytesPerSec = wfx.nChannels * wfx.nSamplesPerSec * sizeof(short);
+            wfx.nBlockAlign = (WORD)(wfx.nChannels * sizeof(short));
+            if( waveOutOpen( NULL, outputMmID, &wfx, 0, 0, WAVE_FORMAT_QUERY ) == MMSYSERR_NOERROR )
+            {
+                sampleRates[ deviceInfo->numSampleRates++ ] = customSamplingRates[i];
+            }
+        }
+    }
+    sDevicePtrs[ id ] = deviceInfo;
+    return deviceInfo;
+error:
+    GlobalFree( sampleRates ); /* MEM */
+    GlobalFree( deviceInfo ); /* MEM */
+
+    return NULL;
+}
+/*************************************************************************
+** Returns recommended device ID.
+** On the PC, the recommended device can be specified by the user by
+** setting an environment variable. For example, to use device #1.
+**
+**    set PA_RECOMMENDED_OUTPUT_DEVICE=1
+**
+** The user should first determine the available device ID by using
+** the supplied application "pa_devs".
+*/
+#define PA_ENV_BUF_SIZE  (32)
+#define PA_REC_IN_DEV_ENV_NAME  ("PA_RECOMMENDED_INPUT_DEVICE")
+#define PA_REC_OUT_DEV_ENV_NAME  ("PA_RECOMMENDED_OUTPUT_DEVICE")
+static PaDeviceID PaHost_GetEnvDefaultDeviceID( char *envName )
+{
+    DWORD   hresult;
+    char    envbuf[PA_ENV_BUF_SIZE];
+    PaDeviceID recommendedID = paNoDevice;
+    /* Let user determine default device by setting environment variable. */
+    hresult = GetEnvironmentVariable( envName, envbuf, PA_ENV_BUF_SIZE );
+    if( (hresult > 0) && (hresult < PA_ENV_BUF_SIZE) )
+    {
+        recommendedID = atoi( envbuf );
+    }
+    return recommendedID;
+}
+static PaError Pa_MaybeQueryDevices( void )
+{
+    if( sNumDevices == 0 )
+    {
+        return Pa_QueryDevices();
+    }
+    return 0;
+}
+/**********************************************************************
+** Check for environment variable, else query devices and use result.
+*/
+PaDeviceID Pa_GetDefaultInputDeviceID( void )
+{
+    PaError result;
+    result = PaHost_GetEnvDefaultDeviceID( PA_REC_IN_DEV_ENV_NAME );
+    if( result < 0 )
+    {
+        result = Pa_MaybeQueryDevices();
+        if( result < 0 ) return result;
+        result = sDefaultInputDeviceID;
+    }
+    return result;
+}
+PaDeviceID Pa_GetDefaultOutputDeviceID( void )
+{
+    PaError result;
+    result = PaHost_GetEnvDefaultDeviceID( PA_REC_OUT_DEV_ENV_NAME );
+    if( result < 0 )
+    {
+        result = Pa_MaybeQueryDevices();
+        if( result < 0 ) return result;
+        result = sDefaultOutputDeviceID;
+    }
+    return result;
+}
+/**********************************************************************
+** Initialize Host dependant part of API.
+*/
+PaError PaHost_Init( void )
+{
+#if PA_SIMULATE_UNDERFLOW
+    PRINT(("WARNING - Underflow Simulation Enabled - Expect a Big Glitch!!!\n"));
+#endif
+    return Pa_MaybeQueryDevices();
+}
+
+/**********************************************************************
+** Check WAVE buffers to see if they are done.
+** Fill any available output buffers and use any available
+** input buffers by calling user callback.
+**
+** This routine will loop until:
+**    user callback returns !=0 OR
+**    all output buffers are filled OR
+**    past->past_StopSoon is set OR
+**    an error occurs when calling WMME.
+**
+** Returns >0 when user requests a stop, <0 on error.
+**    
+*/
+static PaError Pa_TimeSlice( internalPortAudioStream   *past )
+{
+    PaError           result = 0;
+    long              bytesEmpty = 0;
+    long              bytesFilled = 0;
+    long              buffersEmpty = 0;
+    MMRESULT          mresult;
+    char             *inBufPtr;
+    char             *outBufPtr;
+    int               gotInput = 0;
+    int               gotOutput = 0;
+    int               i;
+    int               buffersProcessed = 0;
+    int               done = 0;
+    PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData;
+    if( pahsc == NULL ) return paInternalError;
+
+    past->past_NumCallbacks += 1;
+#if PA_TRACE_RUN
+    AddTraceMessage("Pa_TimeSlice: past_NumCallbacks ", past->past_NumCallbacks );
+#endif
+
+    /* JM20020118 - prevent hung thread when buffers underflow. */
+    /* while( !done ) /* BAD */
+    while( !done && !past->past_StopSoon ) /* GOOD */
+    {
+#if PA_SIMULATE_UNDERFLOW
+        if(gUnderCallbackCounter++ == UNDER_SLEEP_AT)
+        {
+            Sleep(UNDER_SLEEP_FOR);
+        }
+#endif
+
+        /* If we are using output, then we need an empty output buffer. */
+        gotOutput = 0;
+        outBufPtr = NULL;
+        if( past->past_NumOutputChannels > 0 )
+        {
+            if((pahsc->pahsc_OutputBuffers[ pahsc->pahsc_CurrentOutputBuffer ].dwFlags & WHDR_DONE) == 0)
+            {
+                break;  /* If none empty then bail and try again later. */
+            }
+            else
+            {
+                outBufPtr = pahsc->pahsc_OutputBuffers[ pahsc->pahsc_CurrentOutputBuffer ].lpData;
+                gotOutput = 1;
+            }
+        }
+        /* Use an input buffer if one is available. */
+        gotInput = 0;
+        inBufPtr = NULL;
+        if( ( past->past_NumInputChannels > 0 ) &&
+                (pahsc->pahsc_InputBuffers[ pahsc->pahsc_CurrentInputBuffer ].dwFlags & WHDR_DONE) )
+        {
+            inBufPtr = pahsc->pahsc_InputBuffers[ pahsc->pahsc_CurrentInputBuffer ].lpData;
+            gotInput = 1;
+#if PA_TRACE_RUN
+            AddTraceMessage("Pa_TimeSlice: got input buffer at ", (int)inBufPtr );
+            AddTraceMessage("Pa_TimeSlice: got input buffer # ", pahsc->pahsc_CurrentInputBuffer );
+#endif
+
+        }
+        /* If we can't do anything then bail out. */
+        if( !gotInput && !gotOutput ) break;
+        buffersProcessed += 1;
+        /* Each Wave buffer contains multiple user buffers so do them all now. */
+        /* Base Usage on time it took to process one host buffer. */
+        Pa_StartUsageCalculation( past );
+        for( i=0; i<pahsc->pahsc_UserBuffersPerHostBuffer; i++ )
+        {
+            if( done )
+            {
+                if( gotOutput )
+                {
+                    /* 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 );
+                }
+            }
+            else
+            {
+                /* Convert 16 bit native data to user data and call user routine. */
+                result = Pa_CallConvertInt16( past, (short *) inBufPtr, (short *) outBufPtr );
+                if( result != 0) done = 1;
+            }
+            if( gotInput ) inBufPtr += pahsc->pahsc_BytesPerUserInputBuffer;
+            if( gotOutput) outBufPtr += pahsc->pahsc_BytesPerUserOutputBuffer;
+        }
+        Pa_EndUsageCalculation( past );
+        /* Send WAVE buffer to Wave Device to be refilled. */
+        if( gotInput )
+        {
+            mresult = waveInAddBuffer( pahsc->pahsc_HWaveIn,
+                                       &pahsc->pahsc_InputBuffers[ pahsc->pahsc_CurrentInputBuffer ],
+                                       sizeof(WAVEHDR) );
+            if( mresult != MMSYSERR_NOERROR )
+            {
+                sPaHostError = mresult;
+                result = paHostError;
+                break;
+            }
+            pahsc->pahsc_CurrentInputBuffer = (pahsc->pahsc_CurrentInputBuffer+1 >= pahsc->pahsc_NumHostBuffers) ?
+                                              0 : pahsc->pahsc_CurrentInputBuffer+1;
+        }
+        /* Write WAVE buffer to Wave Device. */
+        if( gotOutput )
+        {
+#if PA_TRACE_START_STOP
+            AddTraceMessage( "Pa_TimeSlice: writing buffer ", pahsc->pahsc_CurrentOutputBuffer );
+#endif
+            mresult = waveOutWrite( pahsc->pahsc_HWaveOut,
+                                    &pahsc->pahsc_OutputBuffers[ pahsc->pahsc_CurrentOutputBuffer ],
+                                    sizeof(WAVEHDR) );
+            if( mresult != MMSYSERR_NOERROR )
+            {
+                sPaHostError = mresult;
+                result = paHostError;
+                break;
+            }
+            pahsc->pahsc_CurrentOutputBuffer = (pahsc->pahsc_CurrentOutputBuffer+1 >= pahsc->pahsc_NumHostBuffers) ?
+                                               0 : pahsc->pahsc_CurrentOutputBuffer+1;
+        }
+
+    }
+
+#if PA_TRACE_RUN
+    AddTraceMessage("Pa_TimeSlice: buffersProcessed ", buffersProcessed );
+#endif
+    return (result != 0) ? result : done;
+}
+
+/*******************************************************************/
+static PaError PaHost_BackgroundManager( internalPortAudioStream   *past )
+{
+    PaError      result = 0;
+    int          i;
+    int          numQueuedOutputBuffers = 0;
+    PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData;
+
+    /* Has someone asked us to abort by calling Pa_AbortStream()? */
+    if( past->past_StopNow )
+    {
+        past->past_IsActive = 0; /* Will cause thread to return. */
+    }
+    /* Has someone asked us to stop by calling Pa_StopStream()
+     * OR has a user callback returned '1' to indicate finished.
+     */
+    else if( past->past_StopSoon )
+    {
+        /* Poll buffer and when all have played then exit thread. */
+        /* Count how many output buffers are queued. */
+        numQueuedOutputBuffers = 0;
+        if( past->past_NumOutputChannels > 0 )
+        {
+            for( i=0; i<pahsc->pahsc_NumHostBuffers; i++ )
+            {
+                if( !( pahsc->pahsc_OutputBuffers[ i ].dwFlags & WHDR_DONE) )
+                {
+#if PA_TRACE_START_STOP
+                    AddTraceMessage( "WinMMPa_OutputThreadProc: waiting for buffer ", i );
+#endif
+                    numQueuedOutputBuffers++;
+                }
+            }
+        }
+#if PA_TRACE_START_STOP
+        AddTraceMessage( "WinMMPa_OutputThreadProc: numQueuedOutputBuffers ", numQueuedOutputBuffers );
+#endif
+        if( numQueuedOutputBuffers == 0 )
+        {
+            past->past_IsActive = 0; /* Will cause thread to return. */
+        }
+    }
+    else
+    {
+        /* Process full input buffer and fill up empty output buffers. */
+        if( (result = Pa_TimeSlice( past )) != 0)
+        {
+            /* User callback has asked us to stop. */
+#if PA_TRACE_START_STOP
+            AddTraceMessage( "WinMMPa_OutputThreadProc: TimeSlice() returned ", result );
+#endif
+            past->past_StopSoon = 1; /* Request that audio play out then stop. */
+            result = paNoError;
+        }
+    }
+
+    PaHost_UpdateStreamTime( pahsc );
+    return result;
+}
+
+#if PA_USE_TIMER_CALLBACK
+/*******************************************************************/
+static void CALLBACK Pa_TimerCallback(UINT uID, UINT uMsg, DWORD dwUser, DWORD dw1, DWORD dw2)
+{
+    internalPortAudioStream   *past;
+    PaHostSoundControl        *pahsc;
+    PaError                    result;
+    past = (internalPortAudioStream *) dwUser;
+    if( past == NULL ) return;
+    pahsc = (PaHostSoundControl *) past->past_DeviceData;
+    if( pahsc == NULL ) return;
+    if( pahsc->pahsc_IfInsideCallback )
+    {
+        if( pahsc->pahsc_TimerID != 0 )
+        {
+            timeKillEvent(pahsc->pahsc_TimerID);  /* Stop callback timer. */
+            pahsc->pahsc_TimerID = 0;
+        }
+        return;
+    }
+    pahsc->pahsc_IfInsideCallback = 1;
+    /* Manage flags and audio processing. */
+    result = PaHost_BackgroundManager( past );
+    if( result != paNoError )
+    {
+        past->past_IsActive = 0;
+    }
+    pahsc->pahsc_IfInsideCallback = 0;
+}
+#else /* PA_USE_TIMER_CALLBACK */
+/*******************************************************************/
+static DWORD WINAPI WinMMPa_OutputThreadProc( void *pArg )
+{
+    internalPortAudioStream *past;
+    PaHostSoundControl      *pahsc;
+    void        *inputBuffer=NULL;
+    HANDLE       events[2];
+    int          numEvents = 0;
+    DWORD        result = 0;
+    DWORD        waitResult;
+    DWORD        numTimeouts = 0;
+    DWORD        timeOut;
+    past = (internalPortAudioStream *) pArg;
+    pahsc = (PaHostSoundControl *) past->past_DeviceData;
+#if PA_TRACE_START_STOP
+    AddTraceMessage( "WinMMPa_OutputThreadProc: timeoutPeriod", timeoutPeriod );
+    AddTraceMessage( "WinMMPa_OutputThreadProc: past_NumUserBuffers", past->past_NumUserBuffers );
+#endif
+    /* Calculate timeOut as half the time it would take to play all buffers. */
+    timeOut = (DWORD) (500.0 * PaHost_GetTotalBufferFrames( past ) / past->past_SampleRate);
+    /* Get event(s) ready for wait. */
+    events[numEvents++] = pahsc->pahsc_BufferEvent;
+    if( pahsc->pahsc_AbortEventInited ) events[numEvents++] = pahsc->pahsc_AbortEvent;
+    /* Stay in this thread as long as we are "active". */
+    while( past->past_IsActive )
+    {
+        /*******************************************************************/
+        /******** WAIT here for an event from WMME or PA *******************/
+        /*******************************************************************/
+        waitResult = WaitForMultipleObjects( numEvents, events, FALSE, timeOut );
+        /* Error? */
+        if( waitResult == WAIT_FAILED )
+        {
+            sPaHostError = GetLastError();
+            result = paHostError;
+            past->past_IsActive = 0;
+        }
+        /* Timeout? Don't stop. Just keep polling for DONE.*/
+        else if( waitResult == WAIT_TIMEOUT )
+        {
+#if PA_TRACE_START_STOP
+            AddTraceMessage( "WinMMPa_OutputThreadProc: timed out ", numQueuedOutputBuffers );
+#endif
+            numTimeouts += 1;
+        }
+        /* Manage flags and audio processing. */
+        result = PaHost_BackgroundManager( past );
+        if( result != paNoError )
+        {
+            past->past_IsActive = 0;
+        }
+    }
+    return result;
+}
+#endif
+
+/*******************************************************************/
+PaError PaHost_OpenInputStream( internalPortAudioStream   *past )
+{
+    MMRESULT         mr;
+    PaError          result = paNoError;
+    PaHostSoundControl *pahsc;
+    int              i;
+    int              inputMmId;
+    int              bytesPerInputFrame;
+    WAVEFORMATEX     wfx;
+    const PaDeviceInfo  *pad;
+    pahsc = (PaHostSoundControl *) past->past_DeviceData;
+    DBUG(("PaHost_OpenStream: deviceID = 0x%x\n", past->past_InputDeviceID));
+    pad = Pa_GetDeviceInfo( past->past_InputDeviceID );
+    if( pad == NULL ) return paInternalError;
+    switch( pad->nativeSampleFormats  )
+    {
+    case paInt32:
+    case paFloat32:
+        bytesPerInputFrame = sizeof(float) * past->past_NumInputChannels;
+        break;
+    default:
+        bytesPerInputFrame = sizeof(short) * past->past_NumInputChannels;
+        break;
+    }
+    wfx.wFormatTag = WAVE_FORMAT_PCM;
+    wfx.nChannels = (WORD) past->past_NumInputChannels;
+    wfx.nSamplesPerSec = (DWORD) past->past_SampleRate;
+    wfx.nAvgBytesPerSec = (DWORD)(bytesPerInputFrame * past->past_SampleRate);
+    wfx.nBlockAlign = (WORD)bytesPerInputFrame;
+    wfx.wBitsPerSample = (WORD)((bytesPerInputFrame/past->past_NumInputChannels) * 8);
+    wfx.cbSize = 0;
+    inputMmId = PaDeviceIdToWinId( past->past_InputDeviceID );
+#if PA_USE_TIMER_CALLBACK
+    mr = waveInOpen( &pahsc->pahsc_HWaveIn, inputMmId, &wfx,
+                     0, 0, CALLBACK_NULL );
+#else
+    mr = waveInOpen( &pahsc->pahsc_HWaveIn, inputMmId, &wfx,
+                     (DWORD)pahsc->pahsc_BufferEvent, (DWORD) past, CALLBACK_EVENT );
+#endif
+    if( mr != MMSYSERR_NOERROR )
+    {
+        ERR_RPT(("PortAudio: PaHost_OpenInputStream() failed!\n"));
+        result = paHostError;
+        sPaHostError = mr;
+        goto error;
+    }
+    /* Allocate an array to hold the buffer pointers. */
+    pahsc->pahsc_InputBuffers = (WAVEHDR *) GlobalAlloc( GMEM_FIXED | GMEM_ZEROINIT, sizeof(WAVEHDR)*pahsc->pahsc_NumHostBuffers ); /* MEM */
+    if( pahsc->pahsc_InputBuffers == NULL )
+    {
+        result = paInsufficientMemory;
+        goto error;
+    }
+    /* Allocate each buffer. */
+    for( i=0; i<pahsc->pahsc_NumHostBuffers; i++ )
+    {
+        pahsc->pahsc_InputBuffers[i].lpData = (char *)GlobalAlloc( GMEM_FIXED, pahsc->pahsc_BytesPerHostInputBuffer ); /* MEM */
+        if( pahsc->pahsc_InputBuffers[i].lpData == NULL )
+        {
+            result = paInsufficientMemory;
+            goto error;
+        }
+        pahsc->pahsc_InputBuffers[i].dwBufferLength = pahsc->pahsc_BytesPerHostInputBuffer;
+        pahsc->pahsc_InputBuffers[i].dwUser = i;
+        if( ( mr = waveInPrepareHeader( pahsc->pahsc_HWaveIn, &pahsc->pahsc_InputBuffers[i], sizeof(WAVEHDR) )) != MMSYSERR_NOERROR )
+        {
+            result = paHostError;
+            sPaHostError = mr;
+            goto error;
+        }
+    }
+    return result;
+error:
+    return result;
+}
+/*******************************************************************/
+PaError PaHost_OpenOutputStream( internalPortAudioStream   *past )
+{
+    MMRESULT         mr;
+    PaError          result = paNoError;
+    PaHostSoundControl *pahsc;
+    int              i;
+    int              outputMmID;
+    int              bytesPerOutputFrame;
+    WAVEFORMATEX     wfx;
+    const PaDeviceInfo *pad;
+    pahsc = (PaHostSoundControl *) past->past_DeviceData;
+    DBUG(("PaHost_OpenStream: deviceID = 0x%x\n", past->past_OutputDeviceID));
+    pad = Pa_GetDeviceInfo( past->past_OutputDeviceID );
+    if( pad == NULL ) return paInternalError;
+    switch( pad->nativeSampleFormats  )
+    {
+    case paInt32:
+    case paFloat32:
+        bytesPerOutputFrame = sizeof(float) * past->past_NumOutputChannels;
+        break;
+    default:
+        bytesPerOutputFrame = sizeof(short) * past->past_NumOutputChannels;
+        break;
+    }
+    wfx.wFormatTag = WAVE_FORMAT_PCM;
+    wfx.nChannels = (WORD) past->past_NumOutputChannels;
+    wfx.nSamplesPerSec = (DWORD) past->past_SampleRate;
+    wfx.nAvgBytesPerSec = (DWORD)(bytesPerOutputFrame * past->past_SampleRate);
+    wfx.nBlockAlign = (WORD)bytesPerOutputFrame;
+    wfx.wBitsPerSample = (WORD)((bytesPerOutputFrame/past->past_NumOutputChannels) * 8);
+    wfx.cbSize = 0;
+    outputMmID = PaDeviceIdToWinId( past->past_OutputDeviceID );
+#if PA_USE_TIMER_CALLBACK
+    mr = waveOutOpen( &pahsc->pahsc_HWaveOut, outputMmID, &wfx,
+                      0, 0, CALLBACK_NULL );
+#else
+
+    pahsc->pahsc_AbortEvent = CreateEvent( NULL, TRUE, FALSE, NULL );
+    if( pahsc->pahsc_AbortEvent == NULL )
+    {
+        result = paHostError;
+        sPaHostError = GetLastError();
+        goto error;
+    }
+    pahsc->pahsc_AbortEventInited = 1;
+    mr = waveOutOpen( &pahsc->pahsc_HWaveOut, outputMmID, &wfx,
+                      (DWORD)pahsc->pahsc_BufferEvent, (DWORD) past, CALLBACK_EVENT );
+#endif
+    if( mr != MMSYSERR_NOERROR )
+    {
+        ERR_RPT(("PortAudio: PaHost_OpenOutputStream() failed!\n"));
+        result = paHostError;
+        sPaHostError = mr;
+        goto error;
+    }
+    /* Allocate an array to hold the buffer pointers. */
+    pahsc->pahsc_OutputBuffers = (WAVEHDR *) GlobalAlloc( GMEM_FIXED | GMEM_ZEROINIT, sizeof(WAVEHDR)*pahsc->pahsc_NumHostBuffers ); /* MEM */
+    if( pahsc->pahsc_OutputBuffers == NULL )
+    {
+        result = paInsufficientMemory;
+        goto error;
+    }
+    /* Allocate each buffer. */
+    for( i=0; i<pahsc->pahsc_NumHostBuffers; i++ )
+    {
+        pahsc->pahsc_OutputBuffers[i].lpData = (char *) GlobalAlloc( GMEM_FIXED, pahsc->pahsc_BytesPerHostOutputBuffer ); /* MEM */
+        if( pahsc->pahsc_OutputBuffers[i].lpData == NULL )
+        {
+            result = paInsufficientMemory;
+            goto error;
+        }
+        pahsc->pahsc_OutputBuffers[i].dwBufferLength = pahsc->pahsc_BytesPerHostOutputBuffer;
+        pahsc->pahsc_OutputBuffers[i].dwUser = i;
+        if( (mr = waveOutPrepareHeader( pahsc->pahsc_HWaveOut, &pahsc->pahsc_OutputBuffers[i], sizeof(WAVEHDR) )) != MMSYSERR_NOERROR )
+        {
+            result = paHostError;
+            sPaHostError = mr;
+            goto error;
+        }
+    }
+    return result;
+error:
+    return result;
+}
+/*******************************************************************/
+PaError PaHost_GetTotalBufferFrames( internalPortAudioStream   *past )
+{
+    PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData;
+    return pahsc->pahsc_NumHostBuffers * pahsc->pahsc_FramesPerHostBuffer;
+}
+/*******************************************************************
+* Determine number of WAVE Buffers
+* and how many User Buffers we can put into each WAVE buffer.
+*/
+static void PaHost_CalcNumHostBuffers( internalPortAudioStream *past )
+{
+    PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData;
+    unsigned int  minNumBuffers;
+    int           minFramesPerHostBuffer;
+    int           maxFramesPerHostBuffer;
+    int           minTotalFrames;
+    int           userBuffersPerHostBuffer;
+    int           framesPerHostBuffer;
+    int           numHostBuffers;
+    /* Calculate minimum and maximum sizes based on timing and sample rate. */
+    minFramesPerHostBuffer = (int) (PA_MIN_MSEC_PER_HOST_BUFFER * past->past_SampleRate / 1000.0);
+    minFramesPerHostBuffer = (minFramesPerHostBuffer + 7) & ~7;
+    DBUG(("PaHost_CalcNumHostBuffers: minFramesPerHostBuffer = %d\n", minFramesPerHostBuffer ));
+    maxFramesPerHostBuffer = (int) (PA_MAX_MSEC_PER_HOST_BUFFER * past->past_SampleRate / 1000.0);
+    maxFramesPerHostBuffer = (maxFramesPerHostBuffer + 7) & ~7;
+    DBUG(("PaHost_CalcNumHostBuffers: maxFramesPerHostBuffer = %d\n", maxFramesPerHostBuffer ));
+    /* Determine number of user buffers based on minimum latency. */
+    minNumBuffers = Pa_GetMinNumBuffers( past->past_FramesPerUserBuffer, past->past_SampleRate );
+    past->past_NumUserBuffers = ( minNumBuffers > past->past_NumUserBuffers ) ? minNumBuffers : past->past_NumUserBuffers;
+    DBUG(("PaHost_CalcNumHostBuffers: min past_NumUserBuffers = %d\n", past->past_NumUserBuffers ));
+    minTotalFrames = past->past_NumUserBuffers * past->past_FramesPerUserBuffer;
+    /* We cannot make the WAVE buffers too small because they may not get serviced quickly enough. */
+    if( (int) past->past_FramesPerUserBuffer < minFramesPerHostBuffer )
+    {
+        userBuffersPerHostBuffer =
+            (minFramesPerHostBuffer + past->past_FramesPerUserBuffer - 1) /
+            past->past_FramesPerUserBuffer;
+    }
+    else
+    {
+        userBuffersPerHostBuffer = 1;
+    }
+    framesPerHostBuffer = past->past_FramesPerUserBuffer * userBuffersPerHostBuffer;
+    /* Calculate number of WAVE buffers needed. Round up to cover minTotalFrames. */
+    numHostBuffers = (minTotalFrames + framesPerHostBuffer - 1) / framesPerHostBuffer;
+    /* Make sure we have anough WAVE buffers. */
+    if( numHostBuffers < PA_MIN_NUM_HOST_BUFFERS)
+    {
+        numHostBuffers = PA_MIN_NUM_HOST_BUFFERS;
+    }
+    else if( (numHostBuffers > PA_MAX_NUM_HOST_BUFFERS) &&
+             ((int) past->past_FramesPerUserBuffer < (maxFramesPerHostBuffer/2) ) )
+    {
+        /* If we have too many WAVE buffers, try to put more user buffers in a wave buffer. */
+        while(numHostBuffers > PA_MAX_NUM_HOST_BUFFERS)
+        {
+            userBuffersPerHostBuffer += 1;
+            framesPerHostBuffer = past->past_FramesPerUserBuffer * userBuffersPerHostBuffer;
+            numHostBuffers = (minTotalFrames + framesPerHostBuffer - 1) / framesPerHostBuffer;
+            /* If we have gone too far, back up one. */
+            if( (framesPerHostBuffer > maxFramesPerHostBuffer) ||
+                    (numHostBuffers < PA_MAX_NUM_HOST_BUFFERS) )
+            {
+                userBuffersPerHostBuffer -= 1;
+                framesPerHostBuffer = past->past_FramesPerUserBuffer * userBuffersPerHostBuffer;
+                numHostBuffers = (minTotalFrames + framesPerHostBuffer - 1) / framesPerHostBuffer;
+                break;
+            }
+        }
+    }
+
+    pahsc->pahsc_UserBuffersPerHostBuffer = userBuffersPerHostBuffer;
+    pahsc->pahsc_FramesPerHostBuffer = framesPerHostBuffer;
+    pahsc->pahsc_NumHostBuffers = numHostBuffers;
+    DBUG(("PaHost_CalcNumHostBuffers: pahsc_UserBuffersPerHostBuffer = %d\n", pahsc->pahsc_UserBuffersPerHostBuffer ));
+    DBUG(("PaHost_CalcNumHostBuffers: pahsc_NumHostBuffers = %d\n", pahsc->pahsc_NumHostBuffers ));
+    DBUG(("PaHost_CalcNumHostBuffers: pahsc_FramesPerHostBuffer = %d\n", pahsc->pahsc_FramesPerHostBuffer ));
+    DBUG(("PaHost_CalcNumHostBuffers: past_NumUserBuffers = %d\n", past->past_NumUserBuffers ));
+}
+/*******************************************************************/
+PaError PaHost_OpenStream( internalPortAudioStream   *past )
+{
+    PaError             result = paNoError;
+    PaHostSoundControl *pahsc;
+    /* Allocate and initialize host data. */
+    pahsc = (PaHostSoundControl *) PaHost_AllocateFastMemory(sizeof(PaHostSoundControl)); /* MEM */
+    if( pahsc == NULL )
+    {
+        result = paInsufficientMemory;
+        goto error;
+    }
+    memset( pahsc, 0, sizeof(PaHostSoundControl) );
+    past->past_DeviceData = (void *) pahsc;
+    /* Figure out how user buffers fit into WAVE buffers. */
+    PaHost_CalcNumHostBuffers( past );
+    {
+        int msecLatency = (int) ((PaHost_GetTotalBufferFrames(past) * 1000) / past->past_SampleRate);
+        DBUG(("PortAudio on WMME - Latency = %d frames, %d msec\n", PaHost_GetTotalBufferFrames(past), msecLatency ));
+    }
+    InitializeCriticalSection( &pahsc->pahsc_StreamLock );
+    pahsc->pahsc_StreamLockInited = 1;
+
+#if (PA_USE_TIMER_CALLBACK == 0)
+    pahsc->pahsc_BufferEventInited = 0;
+    pahsc->pahsc_BufferEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
+    if( pahsc->pahsc_BufferEvent == NULL )
+    {
+        result = paHostError;
+        sPaHostError = GetLastError();
+        goto error;
+    }
+    pahsc->pahsc_BufferEventInited = 1;
+#endif /* (PA_USE_TIMER_CALLBACK == 0) */
+    /* ------------------ OUTPUT */
+    pahsc->pahsc_BytesPerUserOutputBuffer = past->past_FramesPerUserBuffer * past->past_NumOutputChannels * sizeof(short);
+    pahsc->pahsc_BytesPerHostOutputBuffer = pahsc->pahsc_UserBuffersPerHostBuffer * pahsc->pahsc_BytesPerUserOutputBuffer;
+    if( (past->past_OutputDeviceID != paNoDevice) && (past->past_NumOutputChannels > 0) )
+    {
+        result = PaHost_OpenOutputStream( past );
+        if( result < 0 ) goto error;
+    }
+    /* ------------------ INPUT */
+    pahsc->pahsc_BytesPerUserInputBuffer = past->past_FramesPerUserBuffer * past->past_NumInputChannels * sizeof(short);
+    pahsc->pahsc_BytesPerHostInputBuffer = pahsc->pahsc_UserBuffersPerHostBuffer * pahsc->pahsc_BytesPerUserInputBuffer;
+    if( (past->past_InputDeviceID != paNoDevice) && (past->past_NumInputChannels > 0) )
+    {
+        result = PaHost_OpenInputStream( past );
+        if( result < 0 ) goto error;
+    }
+    /* Calculate scalar used in CPULoad calculation. */
+    {
+        LARGE_INTEGER frequency;
+        if( QueryPerformanceFrequency( &frequency ) == 0 )
+        {
+            pahsc->pahsc_InverseTicksPerHostBuffer = 0.0;
+        }
+        else
+        {
+            pahsc->pahsc_InverseTicksPerHostBuffer = past->past_SampleRate /
+                    ( (double)frequency.QuadPart * past->past_FramesPerUserBuffer * pahsc->pahsc_UserBuffersPerHostBuffer );
+            DBUG(("pahsc_InverseTicksPerHostBuffer = %g\n", pahsc->pahsc_InverseTicksPerHostBuffer ));
+        }
+    }
+    return result;
+error:
+    PaHost_CloseStream( past );
+    return result;
+}
+/*************************************************************************/
+PaError PaHost_StartOutput( internalPortAudioStream *past )
+{
+    MMRESULT         mr;
+    PaHostSoundControl *pahsc;
+    PaError          result = paNoError;
+    int              i;
+    pahsc = (PaHostSoundControl *) past->past_DeviceData;
+    if( past->past_OutputDeviceID != paNoDevice )
+    {
+        if( (mr = waveOutPause( pahsc->pahsc_HWaveOut )) != MMSYSERR_NOERROR )
+        {
+            result = paHostError;
+            sPaHostError = mr;
+            goto error;
+        }
+        for( i=0; i<pahsc->pahsc_NumHostBuffers; i++ )
+        {
+            ZeroMemory( pahsc->pahsc_OutputBuffers[i].lpData, pahsc->pahsc_OutputBuffers[i].dwBufferLength );
+            mr = waveOutWrite( pahsc->pahsc_HWaveOut, &pahsc->pahsc_OutputBuffers[i], sizeof(WAVEHDR) );
+            if( mr != MMSYSERR_NOERROR )
+            {
+                result = paHostError;
+                sPaHostError = mr;
+                goto error;
+            }
+            past->past_FrameCount += pahsc->pahsc_FramesPerHostBuffer;
+        }
+        pahsc->pahsc_CurrentOutputBuffer = 0;
+        if( (mr = waveOutRestart( pahsc->pahsc_HWaveOut )) != MMSYSERR_NOERROR )
+        {
+            result = paHostError;
+            sPaHostError = mr;
+            goto error;
+        }
+    }
+    DBUG(("PaHost_StartOutput: DSW_StartOutput returned = 0x%X.\n", hr));
+error:
+    return result;
+}
+/*************************************************************************/
+PaError PaHost_StartInput( internalPortAudioStream *past )
+{
+    PaError          result = paNoError;
+    MMRESULT         mr;
+    int              i;
+    PaHostSoundControl *pahsc;
+    pahsc = (PaHostSoundControl *) past->past_DeviceData;
+    if( past->past_InputDeviceID != paNoDevice )
+    {
+        for( i=0; i<pahsc->pahsc_NumHostBuffers; i++ )
+        {
+            mr = waveInAddBuffer( pahsc->pahsc_HWaveIn, &pahsc->pahsc_InputBuffers[i], sizeof(WAVEHDR) );
+            if( mr != MMSYSERR_NOERROR )
+            {
+                result = paHostError;
+                sPaHostError = mr;
+                goto error;
+            }
+        }
+        pahsc->pahsc_CurrentInputBuffer = 0;
+        mr = waveInStart( pahsc->pahsc_HWaveIn );
+        DBUG(("Pa_StartStream: waveInStart returned = 0x%X.\n", hr));
+        if( mr != MMSYSERR_NOERROR )
+        {
+            result = paHostError;
+            sPaHostError = mr;
+            goto error;
+        }
+    }
+error:
+    return result;
+}
+/*************************************************************************/
+PaError PaHost_StartEngine( internalPortAudioStream *past )
+{
+    PaError             result = paNoError;
+    PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData;
+#if PA_USE_TIMER_CALLBACK
+    int                 resolution;
+    int                 bufsPerTimerCallback;
+    int                 msecPerBuffer;
+#endif /* PA_USE_TIMER_CALLBACK */
+
+    past->past_StopSoon = 0;
+    past->past_StopNow = 0;
+    past->past_IsActive = 1;
+    pahsc->pahsc_FramesPlayed = 0.0;
+    pahsc->pahsc_LastPosition = 0;
+#if PA_TRACE_START_STOP
+    AddTraceMessage( "PaHost_StartEngine: TimeSlice() returned ", result );
+#endif
+#if PA_USE_TIMER_CALLBACK
+    /* Create timer that will wake us up so we can fill the DSound buffer. */
+    bufsPerTimerCallback = pahsc->pahsc_NumHostBuffers/4;
+    if( bufsPerTimerCallback < 1 ) bufsPerTimerCallback = 1;
+    if( bufsPerTimerCallback < 1 ) bufsPerTimerCallback = 1;
+    msecPerBuffer = (1000 * bufsPerTimerCallback *
+                     pahsc->pahsc_UserBuffersPerHostBuffer *
+                     past->past_FramesPerUserBuffer ) / (int) past->past_SampleRate;
+    if( msecPerBuffer < 10 ) msecPerBuffer = 10;
+    else if( msecPerBuffer > 100 ) msecPerBuffer = 100;
+    resolution = msecPerBuffer/4;
+    pahsc->pahsc_TimerID = timeSetEvent( msecPerBuffer, resolution,
+                                         (LPTIMECALLBACK) Pa_TimerCallback,
+                                         (DWORD) past, TIME_PERIODIC );
+    if( pahsc->pahsc_TimerID == 0 )
+    {
+        result = paHostError;
+        sPaHostError = GetLastError();;
+        goto error;
+    }
+#else /* PA_USE_TIMER_CALLBACK */
+    ResetEvent( pahsc->pahsc_AbortEvent );
+    /* Create thread that waits for audio buffers to be ready for processing. */
+    pahsc->pahsc_EngineThread = CreateThread( 0, 0, WinMMPa_OutputThreadProc, past, 0, &pahsc->pahsc_EngineThreadID );
+    if( pahsc->pahsc_EngineThread == NULL )
+    {
+        result = paHostError;
+        sPaHostError = GetLastError();;
+        goto error;
+    }
+#if PA_TRACE_START_STOP
+    AddTraceMessage( "PaHost_StartEngine: thread ", (int) pahsc->pahsc_EngineThread );
+#endif
+    /* I used to pass the thread which was failing. I now pass GetCurrentProcess().
+    ** This fix could improve latency for some applications. It could also result in CPU
+    ** starvation if the callback did too much processing.
+    ** I also added result checks, so we might see more failures at initialization.
+    ** Thanks to Alberto di Bene for spotting this.
+    */
+    if( !SetPriorityClass( GetCurrentProcess(), HIGH_PRIORITY_CLASS ) ) /* PLB20010816 */
+    {
+        result = paHostError;
+        sPaHostError = GetLastError();;
+        goto error;
+    }
+    if( !SetThreadPriority( pahsc->pahsc_EngineThread, THREAD_PRIORITY_HIGHEST ) )
+    {
+        result = paHostError;
+        sPaHostError = GetLastError();;
+        goto error;
+    }
+#endif
+error:
+    return result;
+}
+
+/*************************************************************************/
+PaError PaHost_StopEngine( internalPortAudioStream *past, int abort )
+{
+    int timeOut;
+    PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData;
+    if( pahsc == NULL ) return paNoError;
+
+    /* Tell background thread to stop generating more data and to let current data play out. */
+    past->past_StopSoon = 1;
+    /* If aborting, tell background thread to stop NOW! */
+    if( abort ) past->past_StopNow = 1;
+
+    /* Calculate timeOut longer than longest time it could take to play all buffers. */
+    timeOut = (DWORD) (1500.0 * PaHost_GetTotalBufferFrames( past ) / past->past_SampleRate);
+    if( timeOut < MIN_TIMEOUT_MSEC ) timeOut = MIN_TIMEOUT_MSEC;
+
+#if PA_USE_TIMER_CALLBACK
+    if( (past->past_OutputDeviceID != paNoDevice) &&
+            past->past_IsActive &&
+            (pahsc->pahsc_TimerID != 0) )
+    {
+        /* Wait for IsActive to drop. */
+        while( (past->past_IsActive) && (timeOut > 0) )
+        {
+            Sleep(10);
+            timeOut -= 10;
+        }
+        timeKillEvent(pahsc->pahsc_TimerID);  /* Stop callback timer. */
+        pahsc->pahsc_TimerID = 0;
+    }
+#else /* PA_USE_TIMER_CALLBACK */
+#if PA_TRACE_START_STOP
+    AddTraceMessage( "PaHost_StopEngine: thread ", (int) pahsc->pahsc_EngineThread );
+#endif
+    if( (past->past_OutputDeviceID != paNoDevice) &&
+            (past->past_IsActive) &&
+            (pahsc->pahsc_EngineThread != NULL) )
+    {
+        DWORD got;
+        /* Tell background thread to stop generating more data and to let current data play out. */
+        DBUG(("PaHost_StopEngine: waiting for background thread.\n"));
+        got = WaitForSingleObject( pahsc->pahsc_EngineThread, timeOut );
+        if( got == WAIT_TIMEOUT )
+        {
+            ERR_RPT(("PaHost_StopEngine: timed out while waiting for background thread to finish.\n"));
+            return paTimedOut;
+        }
+        CloseHandle( pahsc->pahsc_EngineThread );
+        pahsc->pahsc_EngineThread = NULL;
+    }
+#endif /* PA_USE_TIMER_CALLBACK */
+
+    past->past_IsActive = 0;
+    return paNoError;
+}
+/*************************************************************************/
+PaError PaHost_StopInput( internalPortAudioStream *past, int abort )
+{
+    MMRESULT mr;
+    PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData;
+    if( pahsc == NULL ) return paNoError;
+    (void) abort;
+    if( pahsc->pahsc_HWaveIn != NULL )
+    {
+        mr = waveInReset( pahsc->pahsc_HWaveIn );
+        if( mr != MMSYSERR_NOERROR )
+        {
+            sPaHostError = mr;
+            return paHostError;
+        }
+    }
+    return paNoError;
+}
+/*************************************************************************/
+PaError PaHost_StopOutput( internalPortAudioStream *past, int abort )
+{
+    MMRESULT mr;
+    PaHostSoundControl *pahsc;
+    pahsc = (PaHostSoundControl *) past->past_DeviceData;
+    if( pahsc == NULL ) return paNoError;
+    (void) abort;
+#if PA_TRACE_START_STOP
+    AddTraceMessage( "PaHost_StopOutput: pahsc_HWaveOut ", (int) pahsc->pahsc_HWaveOut );
+#endif
+    if( pahsc->pahsc_HWaveOut != NULL )
+    {
+        mr = waveOutReset( pahsc->pahsc_HWaveOut );
+        if( mr != MMSYSERR_NOERROR )
+        {
+            sPaHostError = mr;
+            return paHostError;
+        }
+    }
+    return paNoError;
+}
+/*******************************************************************/
+PaError PaHost_CloseStream( internalPortAudioStream   *past )
+{
+    int                 i;
+    PaHostSoundControl *pahsc;
+    if( past == NULL ) return paBadStreamPtr;
+    pahsc = (PaHostSoundControl *) past->past_DeviceData;
+    if( pahsc == NULL ) return paNoError;
+#if PA_TRACE_START_STOP
+    AddTraceMessage( "PaHost_CloseStream: pahsc_HWaveOut ", (int) pahsc->pahsc_HWaveOut );
+#endif
+    /* Free data and device for output. */
+    if( pahsc->pahsc_HWaveOut )
+    {
+        if( pahsc->pahsc_OutputBuffers )
+        {
+            for( i=0; i<pahsc->pahsc_NumHostBuffers; i++ )
+            {
+                waveOutUnprepareHeader( pahsc->pahsc_HWaveOut, &pahsc->pahsc_OutputBuffers[i], sizeof(WAVEHDR) );
+                GlobalFree( pahsc->pahsc_OutputBuffers[i].lpData ); /* MEM */
+            }
+            GlobalFree( pahsc->pahsc_OutputBuffers ); /* MEM */
+        }
+        waveOutClose( pahsc->pahsc_HWaveOut );
+    }
+    /* Free data and device for input. */
+    if( pahsc->pahsc_HWaveIn )
+    {
+        if( pahsc->pahsc_InputBuffers )
+        {
+            for( i=0; i<pahsc->pahsc_NumHostBuffers; i++ )
+            {
+                waveInUnprepareHeader( pahsc->pahsc_HWaveIn, &pahsc->pahsc_InputBuffers[i], sizeof(WAVEHDR) );
+                GlobalFree( pahsc->pahsc_InputBuffers[i].lpData ); /* MEM */
+            }
+            GlobalFree( pahsc->pahsc_InputBuffers ); /* MEM */
+        }
+        waveInClose( pahsc->pahsc_HWaveIn );
+    }
+#if (PA_USE_TIMER_CALLBACK == 0)
+    if( pahsc->pahsc_AbortEventInited ) CloseHandle( pahsc->pahsc_AbortEvent );
+    if( pahsc->pahsc_BufferEventInited ) CloseHandle( pahsc->pahsc_BufferEvent );
+#endif
+    if( pahsc->pahsc_StreamLockInited )
+        DeleteCriticalSection( &pahsc->pahsc_StreamLock );
+    PaHost_FreeFastMemory( pahsc, sizeof(PaHostSoundControl) ); /* MEM */
+    past->past_DeviceData = NULL;
+    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 AUTOEXEC.BAT file and reboot.
+** If the environment variable is not set, then the latency will be determined
+** based on the OS. Windows NT has higher latency than Win95.
+*/
+#define PA_LATENCY_ENV_NAME  ("PA_MIN_LATENCY_MSEC")
+int Pa_GetMinNumBuffers( int framesPerBuffer, double sampleRate )
+{
+    char      envbuf[PA_ENV_BUF_SIZE];
+    DWORD     hostVersion;
+    DWORD     hresult;
+    int       minLatencyMsec = 0;
+    double    msecPerBuffer = (1000.0 * framesPerBuffer) / sampleRate;
+    int       minBuffers;
+    /* Let user determine minimal latency by setting environment variable. */
+    hresult = GetEnvironmentVariable( PA_LATENCY_ENV_NAME, envbuf, PA_ENV_BUF_SIZE );
+    if( (hresult > 0) && (hresult < PA_ENV_BUF_SIZE) )
+    {
+        minLatencyMsec = atoi( envbuf );
+    }
+    else
+    {
+        /* Set minimal latency based on whether NT or Win95.
+         * NT has higher latency.
+         */
+        hostVersion = GetVersion();
+        /* High bit clear if NT */
+        minLatencyMsec = ( (hostVersion & 0x80000000) == 0 ) ? PA_WIN_NT_LATENCY : PA_WIN_9X_LATENCY  ;
+#if PA_USE_HIGH_LATENCY
+        PRINT(("PA - Minimum Latency set to %d msec!\n", minLatencyMsec ));
+#endif
+
+    }
+    DBUG(("PA - Minimum Latency set to %d msec!\n", minLatencyMsec ));
+    minBuffers = (int) (1.0 + ((double)minLatencyMsec / msecPerBuffer));
+    if( minBuffers < 2 ) minBuffers = 2;
+    return minBuffers;
+}
+/*************************************************************************
+** Cleanup device info.
+*/
+PaError PaHost_Term( void )
+{
+    int i;
+    if( sNumDevices > 0 )
+    {
+        if( sDevicePtrs != NULL )
+        {
+            for( i=0; i<sNumDevices; i++ )
+            {
+                if( sDevicePtrs[i] != NULL )
+                {
+                    GlobalFree( (char*)sDevicePtrs[i]->name ); /* MEM */
+                    GlobalFree( (void*)sDevicePtrs[i]->sampleRates ); /* MEM */
+                    GlobalFree( sDevicePtrs[i] ); /* MEM */
+                }
+            }
+            GlobalFree( sDevicePtrs ); /* MEM */
+            sDevicePtrs = NULL;
+        }
+        sNumDevices = 0;
+    }
+    return paNoError;
+}
+/*************************************************************************/
+void Pa_Sleep( long msec )
+{
+    Sleep( msec );
+}
+/*************************************************************************
+ * Allocate memory that can be accessed in real-time.
+ * This may need to be held in physical memory so that it is not
+ * paged to virtual memory.
+ * This call MUST be balanced with a call to PaHost_FreeFastMemory().
+ * Memory will be set to zero.
+ */
+void *PaHost_AllocateFastMemory( long numBytes )
+{
+    void *addr = GlobalAlloc( GPTR, numBytes ); /* FIXME - do we need physical memory? Use VirtualLock() */ /* MEM */
+    return addr;
+}
+/*************************************************************************
+ * Free memory that could be accessed in real-time.
+ * This call MUST be balanced with a call to PaHost_AllocateFastMemory().
+ */
+void PaHost_FreeFastMemory( void *addr, long numBytes )
+{
+    if( addr != NULL ) GlobalFree( addr ); /* MEM */
+}
+/***********************************************************************/
+PaError PaHost_StreamActive( internalPortAudioStream   *past )
+{
+    PaHostSoundControl *pahsc;
+    if( past == NULL ) return paBadStreamPtr;
+    pahsc = (PaHostSoundControl *) past->past_DeviceData;
+    if( pahsc == NULL ) return paInternalError;
+    return (PaError) past->past_IsActive;
+}
+/*************************************************************************
+ * This must be called periodically because mmtime.u.sample
+ * is a DWORD and can wrap and lose sync after a few hours.
+ */
+static PaError PaHost_UpdateStreamTime( PaHostSoundControl *pahsc )
+{
+    MMRESULT  mr;
+    MMTIME    mmtime;
+    mmtime.wType = TIME_SAMPLES;
+    if( pahsc->pahsc_HWaveOut != NULL )
+    {
+        mr = waveOutGetPosition( pahsc->pahsc_HWaveOut, &mmtime, sizeof(mmtime) );
+    }
+    else
+    {
+        mr = waveInGetPosition( pahsc->pahsc_HWaveIn, &mmtime, sizeof(mmtime) );
+    }
+    if( mr != MMSYSERR_NOERROR )
+    {
+        sPaHostError = mr;
+        return paHostError;
+    }
+    /* This data has two variables and is shared by foreground and background. */
+    /* So we need to make it thread safe. */
+    EnterCriticalSection( &pahsc->pahsc_StreamLock );
+    pahsc->pahsc_FramesPlayed += ((long)mmtime.u.sample) - pahsc->pahsc_LastPosition;
+    pahsc->pahsc_LastPosition = (long)mmtime.u.sample;
+    LeaveCriticalSection( &pahsc->pahsc_StreamLock );
+    return paNoError;
+}
+/*************************************************************************/
+PaTimestamp Pa_StreamTime( PortAudioStream *stream )
+{
+    PaHostSoundControl *pahsc;
+    internalPortAudioStream   *past = (internalPortAudioStream *) stream;
+    if( past == NULL ) return paBadStreamPtr;
+    pahsc = (PaHostSoundControl *) past->past_DeviceData;
+    PaHost_UpdateStreamTime( pahsc );
+    return pahsc->pahsc_FramesPlayed;
+}
diff --git a/pablio/README.txt b/pablio/README.txt
new file mode 100644 (file)
index 0000000..99c7d14
--- /dev/null
@@ -0,0 +1,39 @@
+README for PABLIO
+Portable Audio Blocking I/O Library
+Author: Phil Burk
+
+PABLIO is a simplified interface to PortAudio that provide
+read/write style blocking I/O.
+
+Please see the .DOC file for documentation.
+
+/*
+ * More information on PortAudio at: http://www.portaudio.com
+ * 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.
+ *
+ */
+
+
diff --git a/pablio/pablio.c b/pablio/pablio.c
new file mode 100644 (file)
index 0000000..452eccc
--- /dev/null
@@ -0,0 +1,307 @@
+/*
+ * $Id$
+ * pablio.c
+ * Portable Audio Blocking Input/Output utility.
+ *
+ * Author: Phil Burk, http://www.softsynth.com
+ *
+ * This program uses the PortAudio Portable Audio Library.
+ * For more information see: http://www.audiomulch.com/portaudio/
+ * Copyright (c) 1999-2000 Ross Bencina and 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.
+ *
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include "portaudio.h"
+#include "ringbuffer.h"
+#include "pablio.h"
+#include <string.h>
+
+/************************************************************************/
+/******** Constants *****************************************************/
+/************************************************************************/
+
+#define FRAMES_PER_BUFFER    (256)
+
+/************************************************************************/
+/******** Prototypes ****************************************************/
+/************************************************************************/
+
+static int blockingIOCallback( void *inputBuffer, void *outputBuffer,
+                               unsigned long framesPerBuffer,
+                               PaTimestamp outTime, void *userData );
+static PaError PABLIO_InitFIFO( RingBuffer *rbuf, long numFrames, long bytesPerFrame );
+static PaError PABLIO_TermFIFO( RingBuffer *rbuf );
+
+/************************************************************************/
+/******** Functions *****************************************************/
+/************************************************************************/
+
+/* Called from PortAudio.
+ * Read and write data only if there is room in FIFOs.
+ */
+static int blockingIOCallback( void *inputBuffer, void *outputBuffer,
+                               unsigned long framesPerBuffer,
+                               PaTimestamp outTime, void *userData )
+{
+    PABLIO_Stream *data = (PABLIO_Stream*)userData;
+    long numBytes = data->bytesPerFrame * framesPerBuffer;
+    (void) outTime;
+
+    /* This may get called with NULL inputBuffer during initial setup. */
+    if( inputBuffer != NULL )
+    {
+        RingBuffer_Write( &data->inFIFO, inputBuffer, numBytes );
+    }
+    if( outputBuffer != NULL )
+    {
+        int i;
+        int numRead = RingBuffer_Read( &data->outFIFO, outputBuffer, numBytes );
+        /* Zero out remainder of buffer if we run out of data. */
+        for( i=numRead; i<numBytes; i++ )
+        {
+            ((char *)outputBuffer)[i] = 0;
+        }
+    }
+
+    return 0;
+}
+
+/* Allocate buffer. */
+static PaError PABLIO_InitFIFO( RingBuffer *rbuf, long numFrames, long bytesPerFrame )
+{
+    long numBytes = numFrames * bytesPerFrame;
+    char *buffer = (char *) malloc( numBytes );
+    if( buffer == NULL ) return paInsufficientMemory;
+    memset( buffer, 0, numBytes );
+    return (PaError) RingBuffer_Init( rbuf, numBytes, buffer );
+}
+
+/* Free buffer. */
+static PaError PABLIO_TermFIFO( RingBuffer *rbuf )
+{
+    if( rbuf->buffer ) free( rbuf->buffer );
+    rbuf->buffer = NULL;
+    return paNoError;
+}
+
+/************************************************************
+ * Write data to ring buffer.
+ * Will not return until all the data has been written.
+ */
+long WriteAudioStream( PABLIO_Stream *aStream, void *data, long numFrames )
+{
+    long bytesWritten;
+    char *p = (char *) data;
+    long numBytes = aStream->bytesPerFrame * numFrames;
+    while( numBytes > 0)
+    {
+        bytesWritten = RingBuffer_Write( &aStream->outFIFO, p, numBytes );
+        numBytes -= bytesWritten;
+        p += bytesWritten;
+        if( numBytes > 0) Pa_Sleep(10);
+    }
+    return numFrames;
+}
+
+/************************************************************
+ * Read data from ring buffer.
+ * Will not return until all the data has been read.
+ */
+long ReadAudioStream( PABLIO_Stream *aStream, void *data, long numFrames )
+{
+    long bytesRead;
+    char *p = (char *) data;
+    long numBytes = aStream->bytesPerFrame * numFrames;
+    while( numBytes > 0)
+    {
+        bytesRead = RingBuffer_Read( &aStream->inFIFO, p, numBytes );
+        numBytes -= bytesRead;
+        p += bytesRead;
+        if( numBytes > 0) Pa_Sleep(10);
+    }
+    return numFrames;
+}
+
+/************************************************************
+ * Return the number of frames that could be written to the stream without
+ * having to wait.
+ */
+long GetAudioStreamWriteable( PABLIO_Stream *aStream )
+{
+    int bytesEmpty = RingBuffer_GetWriteAvailable( &aStream->outFIFO );
+    return bytesEmpty / aStream->bytesPerFrame;
+}
+
+/************************************************************
+ * Return the number of frames that are available to be read from the
+ * stream without having to wait.
+ */
+long GetAudioStreamReadable( PABLIO_Stream *aStream )
+{
+    int bytesFull = RingBuffer_GetReadAvailable( &aStream->inFIFO );
+    return bytesFull / aStream->bytesPerFrame;
+}
+
+/************************************************************/
+static unsigned long RoundUpToNextPowerOf2( unsigned long n )
+{
+    long numBits = 0;
+    if( ((n-1) & n) == 0) return n; /* Already Power of two. */
+    while( n > 0 )
+    {
+        n= n>>1;
+        numBits++;
+    }
+    return (1<<numBits);
+}
+
+/************************************************************
+ * Opens a PortAudio stream with default characteristics.
+ * Allocates PABLIO_Stream structure.
+ *
+ * flags parameter can be an ORed combination of:
+ *    PABLIO_READ, PABLIO_WRITE, or PABLIO_READ_WRITE,
+ *    and either PABLIO_MONO or PABLIO_STEREO
+ */
+PaError OpenAudioStream( PABLIO_Stream **rwblPtr, double sampleRate,
+                         PaSampleFormat format, long flags )
+{
+    long   bytesPerSample;
+    long   doRead = 0;
+    long   doWrite = 0;
+    PaError err;
+    PABLIO_Stream *aStream;
+    long   minNumBuffers;
+    long   numFrames;
+
+    /* Allocate PABLIO_Stream structure for caller. */
+    aStream = (PABLIO_Stream *) malloc( sizeof(PABLIO_Stream) );
+    if( aStream == NULL ) return paInsufficientMemory;
+    memset( aStream, 0, sizeof(PABLIO_Stream) );
+
+    /* Determine size of a sample. */
+    bytesPerSample = Pa_GetSampleSize( format );
+    if( bytesPerSample < 0 )
+    {
+        err = (PaError) bytesPerSample;
+        goto error;
+    }
+    aStream->samplesPerFrame = ((flags&PABLIO_MONO) != 0) ? 1 : 2;
+    aStream->bytesPerFrame = bytesPerSample * aStream->samplesPerFrame;
+
+    /* Initialize PortAudio  */
+    err = Pa_Initialize();
+    if( err != paNoError ) goto error;
+
+    /* Warning: numFrames must be larger than amount of data processed per interrupt
+     *    inside PA to prevent glitches. Just to be safe, adjust size upwards.
+     */
+    minNumBuffers = 2 * Pa_GetMinNumBuffers( FRAMES_PER_BUFFER, sampleRate );
+    numFrames = minNumBuffers * FRAMES_PER_BUFFER;
+    numFrames = RoundUpToNextPowerOf2( numFrames );
+
+    /* Initialize Ring Buffers */
+    doRead = ((flags & PABLIO_READ) != 0);
+    doWrite = ((flags & PABLIO_WRITE) != 0);
+    if(doRead)
+    {
+        err = PABLIO_InitFIFO( &aStream->inFIFO, numFrames, aStream->bytesPerFrame );
+        if( err != paNoError ) goto error;
+    }
+    if(doWrite)
+    {
+        long numBytes;
+        err = PABLIO_InitFIFO( &aStream->outFIFO, numFrames, aStream->bytesPerFrame );
+        if( err != paNoError ) goto error;
+        /* Make Write FIFO appear full initially. */
+        numBytes = RingBuffer_GetWriteAvailable( &aStream->outFIFO );
+        RingBuffer_AdvanceWriteIndex( &aStream->outFIFO, numBytes );
+    }
+
+    /* Open a PortAudio stream that we will use to communicate with the underlying
+     * audio drivers. */
+    err = Pa_OpenStream(
+              &aStream->stream,
+              (doRead ? Pa_GetDefaultInputDeviceID() : paNoDevice),
+              (doRead ? aStream->samplesPerFrame : 0 ),
+              format,
+              NULL,
+              (doWrite ? Pa_GetDefaultOutputDeviceID() : paNoDevice),
+              (doWrite ? aStream->samplesPerFrame : 0 ),
+              format,
+              NULL,
+              sampleRate,
+              FRAMES_PER_BUFFER,
+              minNumBuffers,
+              paClipOff,       /* we won't output out of range samples so don't bother clipping them */
+              blockingIOCallback,
+              aStream );
+    if( err != paNoError ) goto error;
+
+    err = Pa_StartStream( aStream->stream );
+    if( err != paNoError ) goto error;
+
+    *rwblPtr = aStream;
+    return paNoError;
+
+error:
+    CloseAudioStream( aStream );
+    *rwblPtr = NULL;
+    return err;
+}
+
+/************************************************************/
+PaError CloseAudioStream( PABLIO_Stream *aStream )
+{
+    PaError err;
+    int bytesEmpty;
+    int byteSize = aStream->outFIFO.bufferSize;
+
+    /* If we are writing data, make sure we play everything written. */
+    if( byteSize > 0 )
+    {
+        bytesEmpty = RingBuffer_GetWriteAvailable( &aStream->outFIFO );
+        while( bytesEmpty < byteSize )
+        {
+            Pa_Sleep( 10 );
+            bytesEmpty = RingBuffer_GetWriteAvailable( &aStream->outFIFO );
+        }
+    }
+
+    err = Pa_StopStream( aStream->stream );
+    if( err != paNoError ) goto error;
+    err = Pa_CloseStream( aStream->stream );
+    if( err != paNoError ) goto error;
+    Pa_Terminate();
+
+error:
+    PABLIO_TermFIFO( &aStream->inFIFO );
+    PABLIO_TermFIFO( &aStream->outFIFO );
+    free( aStream );
+    return err;
+}
diff --git a/pablio/pablio.def b/pablio/pablio.def
new file mode 100644 (file)
index 0000000..a10f952
--- /dev/null
@@ -0,0 +1,35 @@
+LIBRARY      PABLIO
+DESCRIPTION  'PABLIO   Portable Audio Blocking I/O'
+
+EXPORTS
+    ; Explicit exports can go here
+       Pa_Initialize                   @1
+       Pa_Terminate                    @2
+       Pa_GetHostError                 @3
+       Pa_GetErrorText                 @4
+       Pa_CountDevices                 @5
+       Pa_GetDefaultInputDeviceID      @6
+       Pa_GetDefaultOutputDeviceID     @7
+       Pa_GetDeviceInfo                @8
+       Pa_OpenStream                   @9
+       Pa_OpenDefaultStream            @10
+       Pa_CloseStream                  @11
+       Pa_StartStream                  @12
+       Pa_StopStream                   @13
+       Pa_StreamActive                 @14
+       Pa_StreamTime                   @15
+       Pa_GetCPULoad                   @16
+       Pa_GetMinNumBuffers             @17
+       Pa_Sleep                        @18
+
+       OpenAudioStream                 @19
+       CloseAudioStream                @20
+       WriteAudioStream                @21
+       ReadAudioStream                 @22
+
+       Pa_GetSampleSize                @23
+
+   ;123456789012345678901234567890123456
+   ;000000000111111111122222222223333333
+
+
diff --git a/pablio/pablio.h b/pablio/pablio.h
new file mode 100644 (file)
index 0000000..c1ad84a
--- /dev/null
@@ -0,0 +1,109 @@
+#ifndef _PABLIO_H
+#define _PABLIO_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif /* __cplusplus */
+
+/*
+ * $Id$
+ * PABLIO.h
+ * Portable Audio Blocking read/write utility.
+ *
+ * Author: Phil Burk, http://www.softsynth.com/portaudio/
+ *
+ * Include file for PABLIO, the Portable Audio Blocking I/O Library.
+ * PABLIO is built on top of PortAudio, the Portable Audio Library.
+ * For more information see: http://www.audiomulch.com/portaudio/
+ * Copyright (c) 1999-2000 Ross Bencina and 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.
+ *
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include "portaudio.h"
+#include "ringbuffer.h"
+#include <string.h>
+
+typedef struct
+{
+    RingBuffer   inFIFO;
+    RingBuffer   outFIFO;
+    PortAudioStream *stream;
+    int          bytesPerFrame;
+    int          samplesPerFrame;
+}
+PABLIO_Stream;
+
+/* Values for flags for OpenAudioStream(). */
+#define PABLIO_READ     (1<<0)
+#define PABLIO_WRITE    (1<<1)
+#define PABLIO_READ_WRITE    (PABLIO_READ|PABLIO_WRITE)
+#define PABLIO_MONO     (1<<2)
+#define PABLIO_STEREO   (1<<3)
+
+/************************************************************
+ * Write data to ring buffer.
+ * Will not return until all the data has been written.
+ */
+long WriteAudioStream( PABLIO_Stream *aStream, void *data, long numFrames );
+
+/************************************************************
+ * Read data from ring buffer.
+ * Will not return until all the data has been read.
+ */
+long ReadAudioStream( PABLIO_Stream *aStream, void *data, long numFrames );
+
+/************************************************************
+ * Return the number of frames that could be written to the stream without
+ * having to wait.
+ */
+long GetAudioStreamWriteable( PABLIO_Stream *aStream );
+
+/************************************************************
+ * Return the number of frames that are available to be read from the
+ * stream without having to wait.
+ */
+long GetAudioStreamReadable( PABLIO_Stream *aStream );
+
+/************************************************************
+ * Opens a PortAudio stream with default characteristics.
+ * Allocates PABLIO_Stream structure.
+ *
+ * flags parameter can be an ORed combination of:
+ *    PABLIO_READ, PABLIO_WRITE, or PABLIO_READ_WRITE,
+ *    and either PABLIO_MONO or PABLIO_STEREO
+ */
+PaError OpenAudioStream( PABLIO_Stream **aStreamPtr, double sampleRate,
+                         PaSampleFormat format, long flags );
+
+PaError CloseAudioStream( PABLIO_Stream *aStream );
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif /* _PABLIO_H */
diff --git a/pablio/ringbuffer.c b/pablio/ringbuffer.c
new file mode 100644 (file)
index 0000000..16031fe
--- /dev/null
@@ -0,0 +1,199 @@
+/*
+ * $Id$
+ * ringbuffer.c
+ * Ring Buffer utility..
+ *
+ * Author: Phil Burk, http://www.softsynth.com
+ *
+ * This program uses the PortAudio Portable Audio Library.
+ * For more information see: http://www.audiomulch.com/portaudio/
+ * Copyright (c) 1999-2000 Ross Bencina and 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.
+ *
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include "ringbuffer.h"
+#include <string.h>
+
+/***************************************************************************
+ * Initialize FIFO.
+ * numBytes must be power of 2, returns -1 if not.
+ */
+long RingBuffer_Init( RingBuffer *rbuf, long numBytes, void *dataPtr )
+{
+    if( ((numBytes-1) & numBytes) != 0) return -1; /* Not Power of two. */
+    rbuf->bufferSize = numBytes;
+    rbuf->buffer = (char *)dataPtr;
+    RingBuffer_Flush( rbuf );
+    rbuf->bigMask = (numBytes*2)-1;
+    rbuf->smallMask = (numBytes)-1;
+    return 0;
+}
+/***************************************************************************
+** Return number of bytes available for reading. */
+long RingBuffer_GetReadAvailable( RingBuffer *rbuf )
+{
+    return ( (rbuf->writeIndex - rbuf->readIndex) & rbuf->bigMask );
+}
+/***************************************************************************
+** Return number of bytes available for writing. */
+long RingBuffer_GetWriteAvailable( RingBuffer *rbuf )
+{
+    return ( rbuf->bufferSize - RingBuffer_GetReadAvailable(rbuf));
+}
+
+/***************************************************************************
+** Clear buffer. Should only be called when buffer is NOT being read. */
+void RingBuffer_Flush( RingBuffer *rbuf )
+{
+    rbuf->writeIndex = rbuf->readIndex = 0;
+}
+
+/***************************************************************************
+** Get address of region(s) to which we can write data.
+** If the region is contiguous, size2 will be zero.
+** If non-contiguous, size2 will be the size of second region.
+** Returns room available to be written or numBytes, whichever is smaller.
+*/
+long RingBuffer_GetWriteRegions( RingBuffer *rbuf, long numBytes,
+                                 void **dataPtr1, long *sizePtr1,
+                                 void **dataPtr2, long *sizePtr2 )
+{
+    long   index;
+    long   available = RingBuffer_GetWriteAvailable( rbuf );
+    if( numBytes > available ) numBytes = available;
+    /* Check to see if write is not contiguous. */
+    index = rbuf->writeIndex & rbuf->smallMask;
+    if( (index + numBytes) > rbuf->bufferSize )
+    {
+        /* Write data in two blocks that wrap the buffer. */
+        long   firstHalf = rbuf->bufferSize - index;
+        *dataPtr1 = &rbuf->buffer[index];
+        *sizePtr1 = firstHalf;
+        *dataPtr2 = &rbuf->buffer[0];
+        *sizePtr2 = numBytes - firstHalf;
+    }
+    else
+    {
+        *dataPtr1 = &rbuf->buffer[index];
+        *sizePtr1 = numBytes;
+        *dataPtr2 = NULL;
+        *sizePtr2 = 0;
+    }
+    return numBytes;
+}
+
+
+/***************************************************************************
+*/
+long RingBuffer_AdvanceWriteIndex( RingBuffer *rbuf, long numBytes )
+{
+    return rbuf->writeIndex = (rbuf->writeIndex + numBytes) & rbuf->bigMask;
+}
+
+/***************************************************************************
+** Get address of region(s) from which we can read data.
+** If the region is contiguous, size2 will be zero.
+** If non-contiguous, size2 will be the size of second region.
+** Returns room available to be written or numBytes, whichever is smaller.
+*/
+long RingBuffer_GetReadRegions( RingBuffer *rbuf, long numBytes,
+                                void **dataPtr1, long *sizePtr1,
+                                void **dataPtr2, long *sizePtr2 )
+{
+    long   index;
+    long   available = RingBuffer_GetReadAvailable( rbuf );
+    if( numBytes > available ) numBytes = available;
+    /* Check to see if read is not contiguous. */
+    index = rbuf->readIndex & rbuf->smallMask;
+    if( (index + numBytes) > rbuf->bufferSize )
+    {
+        /* Write data in two blocks that wrap the buffer. */
+        long firstHalf = rbuf->bufferSize - index;
+        *dataPtr1 = &rbuf->buffer[index];
+        *sizePtr1 = firstHalf;
+        *dataPtr2 = &rbuf->buffer[0];
+        *sizePtr2 = numBytes - firstHalf;
+    }
+    else
+    {
+        *dataPtr1 = &rbuf->buffer[index];
+        *sizePtr1 = numBytes;
+        *dataPtr2 = NULL;
+        *sizePtr2 = 0;
+    }
+    return numBytes;
+}
+/***************************************************************************
+*/
+long RingBuffer_AdvanceReadIndex( RingBuffer *rbuf, long numBytes )
+{
+    return rbuf->readIndex = (rbuf->readIndex + numBytes) & rbuf->bigMask;
+}
+
+/***************************************************************************
+** Return bytes written. */
+long RingBuffer_Write( RingBuffer *rbuf, void *data, long numBytes )
+{
+    long size1, size2, numWritten;
+    void *data1, *data2;
+    numWritten = RingBuffer_GetWriteRegions( rbuf, numBytes, &data1, &size1, &data2, &size2 );
+    if( size2 > 0 )
+    {
+
+        memcpy( data1, data, size1 );
+        data = ((char *)data) + size1;
+        memcpy( data2, data, size2 );
+    }
+    else
+    {
+        memcpy( data1, data, size1 );
+    }
+    RingBuffer_AdvanceWriteIndex( rbuf, numWritten );
+    return numWritten;
+}
+
+/***************************************************************************
+** Return bytes read. */
+long RingBuffer_Read( RingBuffer *rbuf, void *data, long numBytes )
+{
+    long size1, size2, numRead;
+    void *data1, *data2;
+    numRead = RingBuffer_GetReadRegions( rbuf, numBytes, &data1, &size1, &data2, &size2 );
+    if( size2 > 0 )
+    {
+        memcpy( data, data1, size1 );
+        data = ((char *)data) + size1;
+        memcpy( data, data2, size2 );
+    }
+    else
+    {
+        memcpy( data, data1, size1 );
+    }
+    RingBuffer_AdvanceReadIndex( rbuf, numRead );
+    return numRead;
+}
diff --git a/pablio/ringbuffer.h b/pablio/ringbuffer.h
new file mode 100644 (file)
index 0000000..dd769be
--- /dev/null
@@ -0,0 +1,101 @@
+#ifndef _RINGBUFFER_H
+#define _RINGBUFFER_H
+#ifdef __cplusplus
+extern "C"
+{
+#endif /* __cplusplus */
+
+/*
+ * $Id$
+ * ringbuffer.h
+ * Ring Buffer utility..
+ *
+ * Author: Phil Burk, http://www.softsynth.com
+ *
+ * This program is distributed with the PortAudio Portable Audio Library.
+ * For more information see: http://www.audiomulch.com/portaudio/
+ * Copyright (c) 1999-2000 Ross Bencina and 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.
+ *
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include "ringbuffer.h"
+#include <string.h>
+
+typedef struct
+{
+    long   bufferSize; /* Number of bytes in FIFO. Power of 2. Set by RingBuffer_Init. */
+    long   writeIndex; /* Index of next writable byte. Set by RingBuffer_AdvanceWriteIndex. */
+    long   readIndex;  /* Index of next readable byte. Set by RingBuffer_AdvanceReadIndex. */
+    long   bigMask;    /* Used for wrapping indices with extra bit to distinguish full/empty. */
+    long   smallMask;  /* Used for fitting indices to buffer. */
+    char *buffer;
+}
+RingBuffer;
+/*
+ * Initialize Ring Buffer.
+ * numBytes must be power of 2, returns -1 if not.
+ */
+long RingBuffer_Init( RingBuffer *rbuf, long numBytes, void *dataPtr );
+
+/* Clear buffer. Should only be called when buffer is NOT being read. */
+void RingBuffer_Flush( RingBuffer *rbuf );
+
+/* Return number of bytes available for writing. */
+long RingBuffer_GetWriteAvailable( RingBuffer *rbuf );
+/* Return number of bytes available for read. */
+long RingBuffer_GetReadAvailable( RingBuffer *rbuf );
+/* Return bytes written. */
+long RingBuffer_Write( RingBuffer *rbuf, void *data, long numBytes );
+/* Return bytes read. */
+long RingBuffer_Read( RingBuffer *rbuf, void *data, long numBytes );
+
+/* Get address of region(s) to which we can write data.
+** If the region is contiguous, size2 will be zero.
+** If non-contiguous, size2 will be the size of second region.
+** Returns room available to be written or numBytes, whichever is smaller.
+*/
+long RingBuffer_GetWriteRegions( RingBuffer *rbuf, long numBytes,
+                                 void **dataPtr1, long *sizePtr1,
+                                 void **dataPtr2, long *sizePtr2 );
+long RingBuffer_AdvanceWriteIndex( RingBuffer *rbuf, long numBytes );
+
+/* Get address of region(s) from which we can read data.
+** If the region is contiguous, size2 will be zero.
+** If non-contiguous, size2 will be the size of second region.
+** Returns room available to be written or numBytes, whichever is smaller.
+*/
+long RingBuffer_GetReadRegions( RingBuffer *rbuf, long numBytes,
+                                void **dataPtr1, long *sizePtr1,
+                                void **dataPtr2, long *sizePtr2 );
+
+long RingBuffer_AdvanceReadIndex( RingBuffer *rbuf, long numBytes );
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif /* _RINGBUFFER_H */
diff --git a/pablio/test_rw.c b/pablio/test_rw.c
new file mode 100644 (file)
index 0000000..245e19c
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+ * $Id$
+ * test_rw.c
+ * Read input from one stream and write it to another.
+ *
+ * Author: Phil Burk, http://www.softsynth.com/portaudio/
+ *
+ * This program uses PABLIO, the Portable Audio Blocking I/O Library.
+ * PABLIO is built on top of PortAudio, the Portable Audio Library.
+ * For more information see: http://www.audiomulch.com/portaudio/
+ * Copyright (c) 1999-2000 Ross Bencina and 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.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include "pablio.h"
+
+/*
+** Note that many of the older ISA sound cards on PCs do NOT support
+** full duplex audio (simultaneous record and playback).
+** And some only support full duplex at lower sample rates.
+*/
+#define SAMPLE_RATE         (22050)
+#define NUM_SECONDS             (15)
+#define SAMPLES_PER_FRAME       (2)
+
+/* Select whether we will use floats or shorts. */
+#if 1
+#define SAMPLE_TYPE  paFloat32
+typedef float SAMPLE;
+#else
+#define SAMPLE_TYPE  paInt16
+typedef short SAMPLE;
+#endif
+
+/*******************************************************************/
+int main(void);
+int main(void)
+{
+    int      i;
+    SAMPLE   samples[SAMPLES_PER_FRAME];
+    PaError  err;
+    PABLIO_Stream     *aStream;
+
+    printf("Full duplex sound test using PortAudio and RingBuffers\n");
+    fflush(stdout);
+
+    /* Open simplified blocking I/O layer on top of PortAudio. */
+    err = OpenAudioStream( &aStream, SAMPLE_RATE, SAMPLE_TYPE,
+                           (PABLIO_READ_WRITE | PABLIO_STEREO) );
+    if( err != paNoError ) goto error;
+
+    /* Process samples in the foreground. */
+    for( i=0; i<(NUM_SECONDS * SAMPLE_RATE); i++ )
+    {
+        /* Read one frame of data into sample array from audio input. */
+        ReadAudioStream( aStream, samples, 1 );
+        /* Write that same frame of data to output. */
+        /* samples[1] = (i&256) * (1.0f/256.0f); /* sawtooth */
+        WriteAudioStream( aStream, samples, 1 );
+    }
+
+    CloseAudioStream( aStream );
+
+    printf("Full duplex sound test complete.\n" );
+    fflush(stdout);
+    return 0;
+
+error:
+    Pa_Terminate();
+    fprintf( stderr, "An error occured while using the portaudio stream\n" );
+    fprintf( stderr, "Error number: %d\n", err );
+    fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
+    return -1;
+}
diff --git a/pablio/test_rw_echo.c b/pablio/test_rw_echo.c
new file mode 100644 (file)
index 0000000..9ba8c8d
--- /dev/null
@@ -0,0 +1,123 @@
+/*
+ * $Id$
+ * test_rw_echo.c
+ * Echo delayed input to output.
+ *
+ * Author: Phil Burk, http://www.softsynth.com/portaudio/
+ *
+ * This program uses PABLIO, the Portable Audio Blocking I/O Library.
+ * PABLIO is built on top of PortAudio, the Portable Audio Library.
+ *
+ * Note that if you need low latency, you should not use PABLIO.
+ * Use the PA_OpenStream callback technique which is lower level
+ * than PABLIO.
+ *
+ * For more information see: http://www.audiomulch.com/portaudio/
+ * Copyright (c) 1999-2000 Ross Bencina and 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.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include "pablio.h"
+#include <string.h>
+
+/*
+** Note that many of the older ISA sound cards on PCs do NOT support
+** full duplex audio (simultaneous record and playback).
+** And some only support full duplex at lower sample rates.
+*/
+#define SAMPLE_RATE         (22050)
+#define NUM_SECONDS            (20)
+#define SAMPLES_PER_FRAME       (2)
+
+/* Select whether we will use floats or shorts. */
+#if 1
+#define SAMPLE_TYPE  paFloat32
+typedef float SAMPLE;
+#else
+#define SAMPLE_TYPE  paInt16
+typedef short SAMPLE;
+#endif
+
+#define NUM_ECHO_FRAMES   (2*SAMPLE_RATE)
+SAMPLE   samples[NUM_ECHO_FRAMES][SAMPLES_PER_FRAME] = {0.0};
+
+/*******************************************************************/
+int main(void);
+int main(void)
+{
+    int      i;
+    PaError  err;
+    PABLIO_Stream     *aInStream;
+    PABLIO_Stream     *aOutStream;
+    int      index;
+
+    printf("Full duplex sound test using PABLIO\n");
+    fflush(stdout);
+
+    /* Open simplified blocking I/O layer on top of PortAudio. */
+    /* Open input first so it can start to fill buffers. */
+    err = OpenAudioStream( &aInStream, SAMPLE_RATE, SAMPLE_TYPE,
+                           (PABLIO_READ | PABLIO_STEREO) );
+    if( err != paNoError ) goto error;
+    /* printf("opened input\n");  fflush(stdout); /**/
+
+    err = OpenAudioStream( &aOutStream, SAMPLE_RATE, SAMPLE_TYPE,
+                           (PABLIO_WRITE | PABLIO_STEREO) );
+    if( err != paNoError ) goto error;
+    /* printf("opened output\n");  fflush(stdout); /**/
+
+    /* Process samples in the foreground. */
+    index = 0;
+    for( i=0; i<(NUM_SECONDS * SAMPLE_RATE); i++ )
+    {
+        /* Write old frame of data to output. */
+        /* samples[index][1] = (i&256) * (1.0f/256.0f); /* sawtooth */
+        WriteAudioStream( aOutStream, &samples[index][0], 1 );
+
+        /* Read one frame of data into sample array for later output. */
+        ReadAudioStream( aInStream, &samples[index][0], 1 );
+        index += 1;
+        if( index >= NUM_ECHO_FRAMES ) index = 0;
+
+        if( (i & 0xFFFF) == 0 ) printf("i = %d\n", i ); fflush(stdout); /**/
+    }
+
+    CloseAudioStream( aOutStream );
+    CloseAudioStream( aInStream );
+
+    printf("R/W echo sound test complete.\n" );
+    fflush(stdout);
+    return 0;
+
+error:
+    fprintf( stderr, "An error occured while using PortAudio\n" );
+    fprintf( stderr, "Error number: %d\n", err );
+    fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
+    return -1;
+}
diff --git a/pablio/test_w_saw.c b/pablio/test_w_saw.c
new file mode 100644 (file)
index 0000000..d5d4e9b
--- /dev/null
@@ -0,0 +1,108 @@
+/*
+ * $Id$
+ * test_w_saw.c
+ * Generate stereo sawtooth waveforms.
+ *
+ * Author: Phil Burk, http://www.softsynth.com
+ *
+ * This program uses PABLIO, the Portable Audio Blocking I/O Library.
+ * PABLIO is built on top of PortAudio, the Portable Audio Library.
+ *
+ * For more information see: http://www.audiomulch.com/portaudio/
+ * Copyright (c) 1999-2000 Ross Bencina and 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.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include "pablio.h"
+#include <string.h>
+
+#define SAMPLE_RATE         (44100)
+#define NUM_SECONDS             (6)
+#define SAMPLES_PER_FRAME       (2)
+
+#define FREQUENCY           (220.0f)
+#define PHASE_INCREMENT     (2.0f * FREQUENCY / SAMPLE_RATE)
+#define FRAMES_PER_BLOCK    (100)
+
+float   samples[FRAMES_PER_BLOCK][SAMPLES_PER_FRAME];
+float   phases[SAMPLES_PER_FRAME];
+
+/*******************************************************************/
+int main(void);
+int main(void)
+{
+    int             i,j;
+    PaError         err;
+    PABLIO_Stream  *aOutStream;
+
+    printf("Generate sawtooth waves using PABLIO.\n");
+    fflush(stdout);
+
+    /* Open simplified blocking I/O layer on top of PortAudio. */
+    err = OpenAudioStream( &aOutStream, SAMPLE_RATE, paFloat32,
+                           (PABLIO_WRITE | PABLIO_STEREO) );
+    if( err != paNoError ) goto error;
+
+    /* Initialize oscillator phases. */
+    phases[0] = 0.0;
+    phases[1] = 0.0;
+
+    for( i=0; i<(NUM_SECONDS * SAMPLE_RATE); i += FRAMES_PER_BLOCK )
+    {
+        /* Generate sawtooth waveforms in a block for efficiency. */
+        for( j=0; j<FRAMES_PER_BLOCK; j++ )
+        {
+            /* Generate a sawtooth wave by incrementing a variable. */
+            phases[0] += PHASE_INCREMENT;
+            /* The signal range is -1.0 to +1.0 so wrap around if we go over. */
+            if( phases[0] > 1.0f ) phases[0] -= 2.0f;
+            samples[j][0] = phases[0];
+
+            /* On the second channel, generate a sawtooth wave a fifth higher. */
+            phases[1] += PHASE_INCREMENT * (3.0f / 2.0f);
+            if( phases[1] > 1.0f ) phases[1] -= 2.0f;
+            samples[j][1] = phases[1];
+        }
+
+        /* Write samples to output. */
+        WriteAudioStream( aOutStream, samples, FRAMES_PER_BLOCK );
+    }
+
+    CloseAudioStream( aOutStream );
+
+    printf("Sawtooth sound test complete.\n" );
+    fflush(stdout);
+    return 0;
+
+error:
+    fprintf( stderr, "An error occured while using PABLIO\n" );
+    fprintf( stderr, "Error number: %d\n", err );
+    fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
+    return -1;
+}
diff --git a/pablio/test_w_saw8.c b/pablio/test_w_saw8.c
new file mode 100644 (file)
index 0000000..0a9d05a
--- /dev/null
@@ -0,0 +1,106 @@
+/*
+ * $Id$
+ * test_w_saw8.c
+ * Generate stereo 8 bit sawtooth waveforms.
+ *
+ * Author: Phil Burk, http://www.softsynth.com
+ *
+ * This program uses PABLIO, the Portable Audio Blocking I/O Library.
+ * PABLIO is built on top of PortAudio, the Portable Audio Library.
+ *
+ * For more information see: http://www.audiomulch.com/portaudio/
+ * Copyright (c) 1999-2000 Ross Bencina and 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.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include "pablio.h"
+#include <string.h>
+
+#define SAMPLE_RATE         (22050)
+#define NUM_SECONDS             (6)
+#define SAMPLES_PER_FRAME       (2)
+
+
+#define FRAMES_PER_BLOCK    (100)
+
+unsigned char   samples[FRAMES_PER_BLOCK][SAMPLES_PER_FRAME];
+unsigned char   phases[SAMPLES_PER_FRAME];
+
+/*******************************************************************/
+int main(void);
+int main(void)
+{
+    int             i,j;
+    PaError         err;
+    PABLIO_Stream  *aOutStream;
+
+    printf("Generate unsigned 8 bit sawtooth waves using PABLIO.\n");
+    fflush(stdout);
+
+    /* Open simplified blocking I/O layer on top of PortAudio. */
+    err = OpenAudioStream( &aOutStream, SAMPLE_RATE, paUInt8,
+                           (PABLIO_WRITE | PABLIO_STEREO) );
+    if( err != paNoError ) goto error;
+
+    /* Initialize oscillator phases to "ground" level for paUInt8. */
+    phases[0] = 128;
+    phases[1] = 128;
+
+    for( i=0; i<(NUM_SECONDS * SAMPLE_RATE); i += FRAMES_PER_BLOCK )
+    {
+        /* Generate sawtooth waveforms in a block for efficiency. */
+        for( j=0; j<FRAMES_PER_BLOCK; j++ )
+        {
+            /* Generate a sawtooth wave by incrementing a variable. */
+            phases[0] += 1;
+            /* We don't have to do anything special to wrap when using paUint8 because
+             * 8 bit arithmetic automatically wraps. */
+            samples[j][0] = phases[0];
+
+            /* On the second channel, generate a higher sawtooth wave. */
+            phases[1] += 3;
+            samples[j][1] = phases[1];
+        }
+
+        /* Write samples to output. */
+        WriteAudioStream( aOutStream, samples, FRAMES_PER_BLOCK );
+    }
+
+    CloseAudioStream( aOutStream );
+
+    printf("Sawtooth sound test complete.\n" );
+    fflush(stdout);
+    return 0;
+
+error:
+    fprintf( stderr, "An error occured while using PABLIO\n" );
+    fprintf( stderr, "Error number: %d\n", err );
+    fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
+    return -1;
+}