diff --git a/src/audio/portaudio/pa_asio/ASIO-README.txt b/src/audio/portaudio/pa_asio/ASIO-README.txt
new file mode 100644
index 0000000000000000000000000000000000000000..9fb74ae16d4897fd523e82340dc60fe2abcc78fe
--- /dev/null
+++ b/src/audio/portaudio/pa_asio/ASIO-README.txt
@@ -0,0 +1,137 @@
+ASIO-README.txt
+
+This document contains information to help you compile PortAudio with 
+ASIO support. If you find any omissions or errors in this document 
+please notify Ross Bencina <rossb@audiomulch.com>.
+
+
+Building PortAudio with ASIO support
+------------------------------------
+
+To build PortAudio with ASIO support you need to compile and link with
+pa_asio.c, and files from the ASIO SDK (see below), along with the common 
+files from pa_common/ and platform specific files from pa_win/ (for Win32) 
+or pa_mac/ (for Macintosh).
+
+If you are compiling with a non-Microsoft compiler on windows, also 
+compile and link with iasiothiscallresolver.cpp (see below for 
+an explanation).
+
+For some platforms (MingW, possibly Mac), you may simply
+be able to type:
+
+./configure --with-host_os=mingw --with-winapi=asio [--with-asiodir=/usr/local/asiosdk2]
+make
+
+./configure --with-host_os=darwin --with-winapi=asio [--with-asiodir=/usr/local/asiosdk2]
+make
+
+and life will be good.
+
+
+Obtaining the ASIO SDK
+----------------------
+
+In order to build PortAudio with ASIO support, you need to download 
+the ASIO SDK (version 2.0) from Steinberg. Steinberg makes the ASIO 
+SDK available to anyone free of charge, however they do not permit its 
+source code to be distributed.
+
+NOTE: In some cases the ASIO SDK may require patching, see below 
+for further details.
+
+http://www.steinberg.net/en/ps/support/3rdparty/asio_sdk/
+
+If the above link is broken search Google for:
+"download steinberg ASIO SDK"
+
+
+
+Building the ASIO SDK on Macintosh
+----------------------------------
+
+To build the ASIO SDK on Macintosh you need to compile and link with the 
+following files from the ASIO SDK:
+
+host/asiodrivers.cpp 
+host/mac/asioshlib.cpp 
+host/mac/codefragements.cpp
+
+
+
+Building the ASIO SDK on Windows
+--------------------------------
+
+To build the ASIO SDK on Windows you need to compile and link with the 
+following files from the ASIO SDK:
+
+asio_sdk\common\asio.cpp
+asio_sdk\host\asiodrivers.cpp
+asio_sdk\host\pc\asiolist.cpp
+
+You may also need to adjust your include paths to support inclusion of 
+header files from the above directories.
+
+The ASIO SDK depends on the following COM API functions: 
+CoInitialize, CoUninitialize, CoCreateInstance, CLSIDFromString
+For compilation with MinGW you will need to link with -lole32, for
+Borland link with Import32.lib.
+
+
+
+Non-Microsoft (MSVC) Compilers on Windows including Borland and GCC
+-------------------------------------------------------------------
+
+Steinberg did not specify a calling convention in the IASIO interface 
+definition. This causes the Microsoft compiler to use the proprietary 
+thiscall convention which is not compatible with other compilers, such 
+as compilers from Borland (BCC and C++Builder) and GNU (gcc). 
+Steinberg's ASIO SDK will compile but crash on initialization if 
+compiled with a non-Microsoft compiler on Windows.
+
+PortAudio solves this problem using the iasiothiscallresolver library 
+which is included in the distribution. When building ASIO support for
+non-Microsoft compilers, be sure to compile and link with
+iasiothiscallresolver.cpp. Note that iasiothiscallresolver includes
+conditional directives which cause it to have no effect if it is
+compiled with a Microsoft compiler, or on the Macintosh.
+
+If you use configure and make (see above), this should be handled
+automatically for you.
+
+For further information about the IASIO thiscall problem see this page:
+http://www.audiomulch.com/~rossb/code/calliasio
+
+
+
+Macintosh ASIO SDK Bug Patch
+----------------------------
+
+There is a bug in the ASIO SDK that causes the Macintosh version to 
+often fail during initialization. Below is a patch that you can apply.
+
+In codefragments.cpp replace getFrontProcessDirectory function with 
+the following one (GetFrontProcess replaced by GetCurrentProcess).
+
+
+bool CodeFragments::getFrontProcessDirectory(void *specs)
+{
+	FSSpec *fss = (FSSpec *)specs;
+	ProcessInfoRec pif;
+	ProcessSerialNumber psn;
+
+	memset(&psn,0,(long)sizeof(ProcessSerialNumber));
+	//  if(GetFrontProcess(&psn) == noErr)  // wrong !!!
+	if(GetCurrentProcess(&psn) == noErr)  // correct !!!
+	{
+		pif.processName = 0;
+		pif.processAppSpec = fss;
+		pif.processInfoLength = sizeof(ProcessInfoRec);
+		if(GetProcessInformation(&psn, &pif) == noErr)
+				return true;
+	}
+	return false;
+}
+
+
+---
diff --git a/src/audio/portaudio/pa_asio/Callback_adaptation_.pdf b/src/audio/portaudio/pa_asio/Callback_adaptation_.pdf
new file mode 100644
index 0000000000000000000000000000000000000000..76bf67863524626aa079d74629c9dd9f720b0b54
Binary files /dev/null and b/src/audio/portaudio/pa_asio/Callback_adaptation_.pdf differ
diff --git a/src/audio/portaudio/pa_asio/Pa_ASIO.pdf b/src/audio/portaudio/pa_asio/Pa_ASIO.pdf
new file mode 100644
index 0000000000000000000000000000000000000000..ac5ecadbc9c861e9fae4b77f5493951504f36f4f
Binary files /dev/null and b/src/audio/portaudio/pa_asio/Pa_ASIO.pdf differ
diff --git a/src/audio/portaudio/pa_asio/iasiothiscallresolver.cpp b/src/audio/portaudio/pa_asio/iasiothiscallresolver.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..8dfefbd67b95ffc0d64194ab75ab7a05e1b1f72f
--- /dev/null
+++ b/src/audio/portaudio/pa_asio/iasiothiscallresolver.cpp
@@ -0,0 +1,563 @@
+/*
+	IASIOThiscallResolver.cpp see the comments in iasiothiscallresolver.h for
+    the top level description - this comment describes the technical details of
+    the implementation.
+
+    The latest version of this file is available from:
+    http://www.audiomulch.com/~rossb/code/calliasio
+
+    please email comments to Ross Bencina <rossb@audiomulch.com>
+
+    BACKGROUND
+
+    The IASIO interface declared in the Steinberg ASIO 2 SDK declares
+    functions with no explicit calling convention. This causes MSVC++ to default
+    to using the thiscall convention, which is a proprietary convention not
+    implemented by some non-microsoft compilers - notably borland BCC,
+    C++Builder, and gcc. MSVC++ is the defacto standard compiler used by
+    Steinberg. As a result of this situation, the ASIO sdk will compile with
+    any compiler, however attempting to execute the compiled code will cause a
+    crash due to different default calling conventions on non-Microsoft
+    compilers.
+
+    IASIOThiscallResolver solves the problem by providing an adapter class that
+    delegates to the IASIO interface using the correct calling convention
+    (thiscall). Due to the lack of support for thiscall in the Borland and GCC
+    compilers, the calls have been implemented in assembly language.
+
+    A number of macros are defined for thiscall function calls with different
+    numbers of parameters, with and without return values - it may be possible
+    to modify the format of these macros to make them work with other inline
+    assemblers.
+
+
+    THISCALL DEFINITION
+
+    A number of definitions of the thiscall calling convention are floating
+    around the internet. The following definition has been validated against
+    output from the MSVC++ compiler:
+
+    For non-vararg functions, thiscall works as follows: the object (this)
+    pointer is passed in ECX. All arguments are passed on the stack in
+    right to left order. The return value is placed in EAX. The callee
+    clears the passed arguments from the stack.
+
+
+    FINDING FUNCTION POINTERS FROM AN IASIO POINTER
+
+    The first field of a COM object is a pointer to its vtble. Thus a pointer
+    to an object implementing the IASIO interface also points to a pointer to
+    that object's vtbl. The vtble is a table of function pointers for all of
+    the virtual functions exposed by the implemented interfaces.
+
+    If we consider a variable declared as a pointer to IASO:
+
+    IASIO *theAsioDriver
+
+    theAsioDriver points to:
+
+    object implementing IASIO
+    {
+        IASIOvtbl *vtbl
+        other data
+    }
+
+    in other words, theAsioDriver points to a pointer to an IASIOvtbl
+
+    vtbl points to a table of function pointers:
+
+    IASIOvtbl ( interface IASIO : public IUnknown )
+    {
+    (IUnknown functions)
+    0   virtual HRESULT STDMETHODCALLTYPE (*QueryInterface)(REFIID riid, void **ppv) = 0;
+    4   virtual ULONG STDMETHODCALLTYPE (*AddRef)() = 0;
+    8   virtual ULONG STDMETHODCALLTYPE (*Release)() = 0;      
+
+    (IASIO functions)
+    12	virtual ASIOBool (*init)(void *sysHandle) = 0;
+    16	virtual void (*getDriverName)(char *name) = 0;
+    20	virtual long (*getDriverVersion)() = 0;
+    24	virtual void (*getErrorMessage)(char *string) = 0;
+    28	virtual ASIOError (*start)() = 0;
+    32	virtual ASIOError (*stop)() = 0;
+    36	virtual ASIOError (*getChannels)(long *numInputChannels, long *numOutputChannels) = 0;
+    40	virtual ASIOError (*getLatencies)(long *inputLatency, long *outputLatency) = 0;
+    44	virtual ASIOError (*getBufferSize)(long *minSize, long *maxSize,
+            long *preferredSize, long *granularity) = 0;
+    48	virtual ASIOError (*canSampleRate)(ASIOSampleRate sampleRate) = 0;
+    52	virtual ASIOError (*getSampleRate)(ASIOSampleRate *sampleRate) = 0;
+    56	virtual ASIOError (*setSampleRate)(ASIOSampleRate sampleRate) = 0;
+    60	virtual ASIOError (*getClockSources)(ASIOClockSource *clocks, long *numSources) = 0;
+    64	virtual ASIOError (*setClockSource)(long reference) = 0;
+    68	virtual ASIOError (*getSamplePosition)(ASIOSamples *sPos, ASIOTimeStamp *tStamp) = 0;
+    72	virtual ASIOError (*getChannelInfo)(ASIOChannelInfo *info) = 0;
+    76	virtual ASIOError (*createBuffers)(ASIOBufferInfo *bufferInfos, long numChannels,
+            long bufferSize, ASIOCallbacks *callbacks) = 0;
+    80	virtual ASIOError (*disposeBuffers)() = 0;
+    84	virtual ASIOError (*controlPanel)() = 0;
+    88	virtual ASIOError (*future)(long selector,void *opt) = 0;
+    92	virtual ASIOError (*outputReady)() = 0;
+    };
+
+    The numbers in the left column show the byte offset of each function ptr
+    from the beginning of the vtbl. These numbers are used in the code below
+    to select different functions.
+
+    In order to find the address of a particular function, theAsioDriver
+    must first be dereferenced to find the value of the vtbl pointer:
+
+    mov     eax, theAsioDriver
+    mov     edx, [theAsioDriver]  // edx now points to vtbl[0]
+
+    Then an offset must be added to the vtbl pointer to select a
+    particular function, for example vtbl+44 points to the slot containing
+    a pointer to the getBufferSize function.
+
+    Finally vtbl+x must be dereferenced to obtain the value of the function
+    pointer stored in that address:
+
+    call    [edx+44]    // call the function pointed to by
+                        // the value in the getBufferSize field of the vtbl
+
+
+    SEE ALSO
+
+    Martin Fay's OpenASIO DLL at http://www.martinfay.com solves the same
+    problem by providing a new COM interface which wraps IASIO with an
+    interface that uses portable calling conventions. OpenASIO must be compiled
+    with MSVC, and requires that you ship the OpenASIO DLL with your
+    application.
+
+    
+    ACKNOWLEDGEMENTS
+
+    Ross Bencina: worked out the thiscall details above, wrote the original
+    Borland asm macros, and a patch for asio.cpp (which is no longer needed).
+    Thanks to Martin Fay for introducing me to the issues discussed here,
+    and to Rene G. Ceballos for assisting with asm dumps from MSVC++.
+
+    Antti Silvast: converted the original calliasio to work with gcc and NASM
+    by implementing the asm code in a separate file.
+
+	Fraser Adams: modified the original calliasio containing the Borland inline
+    asm to add inline asm for gcc i.e. Intel syntax for Borland and AT&T syntax
+    for gcc. This seems a neater approach for gcc than to have a separate .asm
+    file and it means that we only need one version of the thiscall patch.
+
+    Fraser Adams: rewrote the original calliasio patch in the form of the
+    IASIOThiscallResolver class in order to avoid modifications to files from
+    the Steinberg SDK, which may have had potential licence issues.
+
+    Andrew Baldwin: contributed fixes for compatibility problems with more
+    recent versions of the gcc assembler.
+*/
+
+
+// We only need IASIOThiscallResolver at all if we are on Win32. For other
+// platforms we simply bypass the IASIOThiscallResolver definition to allow us
+// to be safely #include'd whatever the platform to keep client code portable
+#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__)
+
+
+// If microsoft compiler we can call IASIO directly so IASIOThiscallResolver
+// is not used.
+#if !defined(_MSC_VER)
+
+
+#include <new>
+#include <assert.h>
+
+// We have a mechanism in iasiothiscallresolver.h to ensure that asio.h is
+// #include'd before it in client code, we do NOT want to do this test here.
+#define iasiothiscallresolver_sourcefile 1
+#include "iasiothiscallresolver.h"
+#undef iasiothiscallresolver_sourcefile
+
+// iasiothiscallresolver.h redefines ASIOInit for clients, but we don't want
+// this macro defined in this translation unit.
+#undef ASIOInit
+
+
+// theAsioDriver is a global pointer to the current IASIO instance which the
+// ASIO SDK uses to perform all actions on the IASIO interface. We substitute
+// our own forwarding interface into this pointer.
+extern IASIO* theAsioDriver;
+
+
+// The following macros define the inline assembler for BORLAND first then gcc
+
+#if defined(__BCPLUSPLUS__) || defined(__BORLANDC__)          
+
+
+#define CALL_THISCALL_0( resultName, thisPtr, funcOffset )\
+    void *this_ = (thisPtr);                                                \
+    __asm {                                                                 \
+        mov     ecx, this_            ;                                     \
+        mov     eax, [ecx]            ;                                     \
+        call    [eax+funcOffset]      ;                                     \
+        mov     resultName, eax       ;                                     \
+    }
+
+
+#define CALL_VOID_THISCALL_1( thisPtr, funcOffset, param1 )\
+    void *this_ = (thisPtr);                                                \
+    __asm {                                                                 \
+        mov     eax, param1           ;                                     \
+        push    eax                   ;                                     \
+        mov     ecx, this_            ;                                     \
+        mov     eax, [ecx]            ;                                     \
+        call    [eax+funcOffset]      ;                                     \
+    }
+
+
+#define CALL_THISCALL_1( resultName, thisPtr, funcOffset, param1 )\
+    void *this_ = (thisPtr);                                                \
+    __asm {                                                                 \
+        mov     eax, param1           ;                                     \
+        push    eax                   ;                                     \
+        mov     ecx, this_            ;                                     \
+        mov     eax, [ecx]            ;                                     \
+        call    [eax+funcOffset]      ;                                     \
+        mov     resultName, eax       ;                                     \
+    }
+
+
+#define CALL_THISCALL_1_DOUBLE( resultName, thisPtr, funcOffset, param1 )\
+    void *this_ = (thisPtr);                                                \
+    void *doubleParamPtr_ (&param1);                                        \
+    __asm {                                                                 \
+        mov     eax, doubleParamPtr_  ;                                     \
+        push    [eax+4]               ;                                     \
+        push    [eax]                 ;                                     \
+        mov     ecx, this_            ;                                     \
+        mov     eax, [ecx]            ;                                     \
+        call    [eax+funcOffset]      ;                                     \
+        mov     resultName, eax       ;                                     \
+    }
+
+
+#define CALL_THISCALL_2( resultName, thisPtr, funcOffset, param1, param2 )\
+    void *this_ = (thisPtr);                                                \
+    __asm {                                                                 \
+        mov     eax, param2           ;                                     \
+        push    eax                   ;                                     \
+        mov     eax, param1           ;                                     \
+        push    eax                   ;                                     \
+        mov     ecx, this_            ;                                     \
+        mov     eax, [ecx]            ;                                     \
+        call    [eax+funcOffset]      ;                                     \
+        mov     resultName, eax       ;                                     \
+    }
+
+
+#define CALL_THISCALL_4( resultName, thisPtr, funcOffset, param1, param2, param3, param4 )\
+    void *this_ = (thisPtr);                                                \
+    __asm {                                                                 \
+        mov     eax, param4           ;                                     \
+        push    eax                   ;                                     \
+        mov     eax, param3           ;                                     \
+        push    eax                   ;                                     \
+        mov     eax, param2           ;                                     \
+        push    eax                   ;                                     \
+        mov     eax, param1           ;                                     \
+        push    eax                   ;                                     \
+        mov     ecx, this_            ;                                     \
+        mov     eax, [ecx]            ;                                     \
+        call    [eax+funcOffset]      ;                                     \
+        mov     resultName, eax       ;                                     \
+    }
+
+
+#elif defined(__GNUC__)
+
+
+#define CALL_THISCALL_0( resultName, thisPtr, funcOffset )                  \
+    __asm__ __volatile__ ("movl (%1), %%edx\n\t"                            \
+                          "call *"#funcOffset"(%%edx)\n\t"                  \
+                          :"=a"(resultName) /* Output Operands */           \
+                          :"c"(thisPtr)     /* Input Operands */            \
+                         );                                                 \
+
+
+#define CALL_VOID_THISCALL_1( thisPtr, funcOffset, param1 )                 \
+    __asm__ __volatile__ ("pushl %0\n\t"                                    \
+                          "movl (%1), %%edx\n\t"                            \
+                          "call *"#funcOffset"(%%edx)\n\t"                  \
+                          :                 /* Output Operands */           \
+                          :"r"(param1),     /* Input Operands */            \
+                           "c"(thisPtr)                                     \
+                         );                                                 \
+
+
+#define CALL_THISCALL_1( resultName, thisPtr, funcOffset, param1 )          \
+    __asm__ __volatile__ ("pushl %1\n\t"                                    \
+                          "movl (%2), %%edx\n\t"                            \
+                          "call *"#funcOffset"(%%edx)\n\t"                  \
+                          :"=a"(resultName) /* Output Operands */           \
+                          :"r"(param1),     /* Input Operands */            \
+                           "c"(thisPtr)                                     \
+                          );                                                \
+
+
+#define CALL_THISCALL_1_DOUBLE( resultName, thisPtr, funcOffset, param1 )   \
+    __asm__ __volatile__ ("pushl 4(%1)\n\t"                                 \
+                          "pushl (%1)\n\t"                                  \
+                          "movl (%2), %%edx\n\t"                            \
+                          "call *"#funcOffset"(%%edx);\n\t"                 \
+                          :"=a"(resultName) /* Output Operands */           \
+                          :"a"(&param1),    /* Input Operands */            \
+                           /* Note: Using "r" above instead of "a" fails */ \
+                           /* when using GCC 3.3.3, and maybe later versions*/\
+                           "c"(thisPtr)                                     \
+                          );                                                \
+
+
+#define CALL_THISCALL_2( resultName, thisPtr, funcOffset, param1, param2 )  \
+    __asm__ __volatile__ ("pushl %1\n\t"                                    \
+                          "pushl %2\n\t"                                    \
+                          "movl (%3), %%edx\n\t"                            \
+                          "call *"#funcOffset"(%%edx)\n\t"                  \
+                          :"=a"(resultName) /* Output Operands */           \
+                          :"r"(param2),     /* Input Operands */            \
+                           "r"(param1),                                     \
+                           "c"(thisPtr)                                     \
+                          );                                                \
+
+
+#define CALL_THISCALL_4( resultName, thisPtr, funcOffset, param1, param2, param3, param4 )\
+    __asm__ __volatile__ ("pushl %1\n\t"                                    \
+                          "pushl %2\n\t"                                    \
+                          "pushl %3\n\t"                                    \
+                          "pushl %4\n\t"                                    \
+                          "movl (%5), %%edx\n\t"                            \
+                          "call *"#funcOffset"(%%edx)\n\t"                  \
+                          :"=a"(resultName) /* Output Operands */           \
+                          :"r"(param4),     /* Input Operands  */           \
+                           "r"(param3),                                     \
+                           "r"(param2),                                     \
+                           "r"(param1),                                     \
+                           "c"(thisPtr)                                     \
+                          );                                                \
+
+#endif
+
+
+
+// Our static singleton instance.
+IASIOThiscallResolver IASIOThiscallResolver::instance;
+
+// Constructor called to initialize static Singleton instance above. Note that
+// it is important not to clear that_ incase it has already been set by the call
+// to placement new in ASIOInit().
+IASIOThiscallResolver::IASIOThiscallResolver()
+{
+}
+
+// Constructor called from ASIOInit() below
+IASIOThiscallResolver::IASIOThiscallResolver(IASIO* that)
+: that_( that )
+{
+}
+
+// Implement IUnknown methods as assert(false). IASIOThiscallResolver is not
+// really a COM object, just a wrapper which will work with the ASIO SDK.
+// If you wanted to use ASIO without the SDK you might want to implement COM
+// aggregation in these methods.
+HRESULT STDMETHODCALLTYPE IASIOThiscallResolver::QueryInterface(REFIID riid, void **ppv)
+{
+    (void)riid;     // suppress unused variable warning
+
+    assert( false ); // this function should never be called by the ASIO SDK.
+
+    *ppv = NULL;
+    return E_NOINTERFACE;
+}
+
+ULONG STDMETHODCALLTYPE IASIOThiscallResolver::AddRef()
+{
+    assert( false ); // this function should never be called by the ASIO SDK.
+
+    return 1;
+}
+
+ULONG STDMETHODCALLTYPE IASIOThiscallResolver::Release()
+{
+    assert( false ); // this function should never be called by the ASIO SDK.
+    
+    return 1;
+}
+
+
+// Implement the IASIO interface methods by performing the vptr manipulation
+// described above then delegating to the real implementation.
+ASIOBool IASIOThiscallResolver::init(void *sysHandle)
+{
+    ASIOBool result;
+    CALL_THISCALL_1( result, that_, 12, sysHandle );
+    return result;
+}
+
+void IASIOThiscallResolver::getDriverName(char *name)
+{
+    CALL_VOID_THISCALL_1( that_, 16, name );
+}
+
+long IASIOThiscallResolver::getDriverVersion()
+{
+    ASIOBool result;
+    CALL_THISCALL_0( result, that_, 20 );
+    return result;
+}
+
+void IASIOThiscallResolver::getErrorMessage(char *string)
+{
+     CALL_VOID_THISCALL_1( that_, 24, string );
+}
+
+ASIOError IASIOThiscallResolver::start()
+{
+    ASIOBool result;
+    CALL_THISCALL_0( result, that_, 28 );
+    return result;
+}
+
+ASIOError IASIOThiscallResolver::stop()
+{
+    ASIOBool result;
+    CALL_THISCALL_0( result, that_, 32 );
+    return result;
+}
+
+ASIOError IASIOThiscallResolver::getChannels(long *numInputChannels, long *numOutputChannels)
+{
+    ASIOBool result;
+    CALL_THISCALL_2( result, that_, 36, numInputChannels, numOutputChannels );
+    return result;
+}
+
+ASIOError IASIOThiscallResolver::getLatencies(long *inputLatency, long *outputLatency)
+{
+    ASIOBool result;
+    CALL_THISCALL_2( result, that_, 40, inputLatency, outputLatency );
+    return result;
+}
+
+ASIOError IASIOThiscallResolver::getBufferSize(long *minSize, long *maxSize,
+        long *preferredSize, long *granularity)
+{
+    ASIOBool result;
+    CALL_THISCALL_4( result, that_, 44, minSize, maxSize, preferredSize, granularity );
+    return result;
+}
+
+ASIOError IASIOThiscallResolver::canSampleRate(ASIOSampleRate sampleRate)
+{
+    ASIOBool result;
+    CALL_THISCALL_1_DOUBLE( result, that_, 48, sampleRate );
+    return result;
+}
+
+ASIOError IASIOThiscallResolver::getSampleRate(ASIOSampleRate *sampleRate)
+{
+    ASIOBool result;
+    CALL_THISCALL_1( result, that_, 52, sampleRate );
+    return result;
+}
+
+ASIOError IASIOThiscallResolver::setSampleRate(ASIOSampleRate sampleRate)
+{    
+    ASIOBool result;
+    CALL_THISCALL_1_DOUBLE( result, that_, 56, sampleRate );
+    return result;
+}
+
+ASIOError IASIOThiscallResolver::getClockSources(ASIOClockSource *clocks, long *numSources)
+{
+    ASIOBool result;
+    CALL_THISCALL_2( result, that_, 60, clocks, numSources );
+    return result;
+}
+
+ASIOError IASIOThiscallResolver::setClockSource(long reference)
+{
+    ASIOBool result;
+    CALL_THISCALL_1( result, that_, 64, reference );
+    return result;
+}
+
+ASIOError IASIOThiscallResolver::getSamplePosition(ASIOSamples *sPos, ASIOTimeStamp *tStamp)
+{
+    ASIOBool result;
+    CALL_THISCALL_2( result, that_, 68, sPos, tStamp );
+    return result;
+}
+
+ASIOError IASIOThiscallResolver::getChannelInfo(ASIOChannelInfo *info)
+{
+    ASIOBool result;
+    CALL_THISCALL_1( result, that_, 72, info );
+    return result;
+}
+
+ASIOError IASIOThiscallResolver::createBuffers(ASIOBufferInfo *bufferInfos,
+        long numChannels, long bufferSize, ASIOCallbacks *callbacks)
+{
+    ASIOBool result;
+    CALL_THISCALL_4( result, that_, 76, bufferInfos, numChannels, bufferSize, callbacks );
+    return result;
+}
+
+ASIOError IASIOThiscallResolver::disposeBuffers()
+{
+    ASIOBool result;
+    CALL_THISCALL_0( result, that_, 80 );
+    return result;
+}
+
+ASIOError IASIOThiscallResolver::controlPanel()
+{
+    ASIOBool result;
+    CALL_THISCALL_0( result, that_, 84 );
+    return result;
+}
+
+ASIOError IASIOThiscallResolver::future(long selector,void *opt)
+{
+    ASIOBool result;
+    CALL_THISCALL_2( result, that_, 88, selector, opt );
+    return result;
+}
+
+ASIOError IASIOThiscallResolver::outputReady()
+{
+    ASIOBool result;
+    CALL_THISCALL_0( result, that_, 92 );
+    return result;
+}
+
+
+// Implement our substitute ASIOInit() method
+ASIOError IASIOThiscallResolver::ASIOInit(ASIODriverInfo *info)
+{
+    // To ensure that our instance's vptr is correctly constructed, even if
+    // ASIOInit is called prior to main(), we explicitly call its constructor
+    // (potentially over the top of an existing instance). Note that this is
+    // pretty ugly, and is only safe because IASIOThiscallResolver has no
+    // destructor and contains no objects with destructors.
+    new((void*)&instance) IASIOThiscallResolver( theAsioDriver );
+
+    // Interpose between ASIO client code and the real driver.
+    theAsioDriver = &instance;
+
+    // Note that we never need to switch theAsioDriver back to point to the
+    // real driver because theAsioDriver is reset to zero in ASIOExit().
+
+    // Delegate to the real ASIOInit
+	return ::ASIOInit(info);
+}
+
+
+#endif /* !defined(_MSC_VER) */
+
+#endif /* Win32 */
+
diff --git a/src/audio/portaudio/pa_asio/iasiothiscallresolver.h b/src/audio/portaudio/pa_asio/iasiothiscallresolver.h
new file mode 100644
index 0000000000000000000000000000000000000000..2ecfed799aece45fa32ee15140f30705de9b5ec0
--- /dev/null
+++ b/src/audio/portaudio/pa_asio/iasiothiscallresolver.h
@@ -0,0 +1,197 @@
+// ****************************************************************************
+// File:			IASIOThiscallResolver.h
+// Description:     The IASIOThiscallResolver class implements the IASIO
+//					interface and acts as a proxy to the real IASIO interface by
+//                  calling through its vptr table using the thiscall calling
+//                  convention. To put it another way, we interpose
+//                  IASIOThiscallResolver between ASIO SDK code and the driver.
+//                  This is necessary because most non-Microsoft compilers don't
+//                  implement the thiscall calling convention used by IASIO.
+//
+//					iasiothiscallresolver.cpp contains the background of this
+//					problem plus a technical description of the vptr
+//                  manipulations.
+//
+//					In order to use this mechanism one simply has to add
+//					iasiothiscallresolver.cpp to the list of files to compile
+//                  and #include <iasiothiscallresolver.h>
+//
+//					Note that this #include must come after the other ASIO SDK
+//                  #includes, for example:
+//
+//					#include <windows.h>
+//					#include <asiosys.h>
+//					#include <asio.h>
+//					#include <asiodrivers.h>
+//					#include <iasiothiscallresolver.h>
+//
+//					Actually the important thing is to #include
+//                  <iasiothiscallresolver.h> after <asio.h>. We have
+//                  incorporated a test to enforce this ordering.
+//
+//					The code transparently takes care of the interposition by
+//                  using macro substitution to intercept calls to ASIOInit()
+//                  and ASIOExit(). We save the original ASIO global
+//                  "theAsioDriver" in our "that" variable, and then set
+//                  "theAsioDriver" to equal our IASIOThiscallResolver instance.
+//
+// 					Whilst this method of resolving the thiscall problem requires
+//					the addition of #include <iasiothiscallresolver.h> to client
+//                  code it has the advantage that it does not break the terms
+//                  of the ASIO licence by publishing it. We are NOT modifying
+//                  any Steinberg code here, we are merely implementing the IASIO
+//					interface in the same way that we would need to do if we
+//					wished to provide an open source ASIO driver.
+//
+//					For compilation with MinGW -lole32 needs to be added to the
+//                  linker options. For BORLAND, linking with Import32.lib is
+//                  sufficient.
+//
+//					The dependencies are with: CoInitialize, CoUninitialize,
+//					CoCreateInstance, CLSIDFromString - used by asiolist.cpp
+//					and are required on Windows whether ThiscallResolver is used
+//					or not.
+//
+//					Searching for the above strings in the root library path
+//					of your compiler should enable the correct libraries to be
+//					identified if they aren't immediately obvious.
+//
+//                  Note that the current implementation of IASIOThiscallResolver
+//                  is not COM compliant - it does not correctly implement the
+//                  IUnknown interface. Implementing it is not necessary because
+//                  it is not called by parts of the ASIO SDK which call through
+//                  theAsioDriver ptr. The IUnknown methods are implemented as
+//                  assert(false) to ensure that the code fails if they are
+//                  ever called.
+// Restrictions:	None. Public Domain & Open Source distribute freely
+//					You may use IASIOThiscallResolver commercially as well as
+//                  privately.
+//					You the user assume the responsibility for the use of the
+//					files, binary or text, and there is no guarantee or warranty,
+//					expressed or implied, including but not limited to the
+//					implied warranties of merchantability and fitness for a
+//					particular purpose. You assume all responsibility and agree
+//					to hold no entity, copyright holder or distributors liable
+//					for any loss of data or inaccurate representations of data
+//					as a result of using IASIOThiscallResolver.
+// Version:         1.4 Added separate macro CALL_THISCALL_1_DOUBLE from
+//                  Andrew Baldwin, and volatile for whole gcc asm blocks,
+//                  both for compatibility with newer gcc versions. Cleaned up
+//                  Borland asm to use one less register.
+//                  1.3 Switched to including assert.h for better compatibility.
+//                  Wrapped entire .h and .cpp contents with a check for
+//                  _MSC_VER to provide better compatibility with MS compilers.
+//                  Changed Singleton implementation to use static instance
+//                  instead of freestore allocated instance. Removed ASIOExit
+//                  macro as it is no longer needed.
+//                  1.2 Removed semicolons from ASIOInit and ASIOExit macros to
+//                  allow them to be embedded in expressions (if statements).
+//                  Cleaned up some comments. Removed combase.c dependency (it
+//                  doesn't compile with BCB anyway) by stubbing IUnknown.
+//                  1.1 Incorporated comments from Ross Bencina including things
+//					such as changing name from ThiscallResolver to
+//					IASIOThiscallResolver, tidying up the constructor, fixing
+//					a bug in IASIOThiscallResolver::ASIOExit() and improving
+//					portability through the use of conditional compilation
+//					1.0 Initial working version.
+// Created:			6/09/2003
+// Authors:         Fraser Adams
+//                  Ross Bencina
+//                  Rene G. Ceballos
+//                  Martin Fay
+//                  Antti Silvast
+//                  Andrew Baldwin
+//
+// ****************************************************************************
+
+
+#ifndef included_iasiothiscallresolver_h
+#define included_iasiothiscallresolver_h
+
+// We only need IASIOThiscallResolver at all if we are on Win32. For other
+// platforms we simply bypass the IASIOThiscallResolver definition to allow us
+// to be safely #include'd whatever the platform to keep client code portable
+#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__)
+
+
+// If microsoft compiler we can call IASIO directly so IASIOThiscallResolver
+// is not used.
+#if !defined(_MSC_VER)
+
+
+// The following is in order to ensure that this header is only included after
+// the other ASIO headers (except for the case of iasiothiscallresolver.cpp).
+// We need to do this because IASIOThiscallResolver works by eclipsing the
+// original definition of ASIOInit() with a macro (see below).
+#if !defined(iasiothiscallresolver_sourcefile)
+	#if !defined(__ASIO_H)
+	#error iasiothiscallresolver.h must be included AFTER asio.h
+	#endif
+#endif
+
+#include <windows.h>
+#include <asiodrvr.h> /* From ASIO SDK */
+
+
+class IASIOThiscallResolver : public IASIO {
+private:
+	IASIO* that_; // Points to the real IASIO
+
+	static IASIOThiscallResolver instance; // Singleton instance
+
+	// Constructors - declared private so construction is limited to
+    // our Singleton instance
+    IASIOThiscallResolver();
+	IASIOThiscallResolver(IASIO* that);
+public:
+
+    // Methods from the IUnknown interface. We don't fully implement IUnknown
+    // because the ASIO SDK never calls these methods through theAsioDriver ptr.
+    // These methods are implemented as assert(false).
+    virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppv);
+    virtual ULONG STDMETHODCALLTYPE AddRef();
+    virtual ULONG STDMETHODCALLTYPE Release();
+
+    // Methods from the IASIO interface, implemented as forwarning calls to that.
+	virtual ASIOBool init(void *sysHandle);
+	virtual void getDriverName(char *name);
+	virtual long getDriverVersion();
+	virtual void getErrorMessage(char *string);
+	virtual ASIOError start();
+	virtual ASIOError stop();
+	virtual ASIOError getChannels(long *numInputChannels, long *numOutputChannels);
+	virtual ASIOError getLatencies(long *inputLatency, long *outputLatency);
+	virtual ASIOError getBufferSize(long *minSize, long *maxSize, long *preferredSize, long *granularity);
+	virtual ASIOError canSampleRate(ASIOSampleRate sampleRate);
+	virtual ASIOError getSampleRate(ASIOSampleRate *sampleRate);
+	virtual ASIOError setSampleRate(ASIOSampleRate sampleRate);
+	virtual ASIOError getClockSources(ASIOClockSource *clocks, long *numSources);
+	virtual ASIOError setClockSource(long reference);
+	virtual ASIOError getSamplePosition(ASIOSamples *sPos, ASIOTimeStamp *tStamp);
+	virtual ASIOError getChannelInfo(ASIOChannelInfo *info);
+	virtual ASIOError createBuffers(ASIOBufferInfo *bufferInfos, long numChannels, long bufferSize, ASIOCallbacks *callbacks);
+	virtual ASIOError disposeBuffers();
+	virtual ASIOError controlPanel();
+	virtual ASIOError future(long selector,void *opt);
+	virtual ASIOError outputReady();
+
+    // Class method, see ASIOInit() macro below.
+    static ASIOError ASIOInit(ASIODriverInfo *info); // Delegates to ::ASIOInit
+};
+
+
+// Replace calls to ASIOInit with our interposing version.
+// This macro enables us to perform thiscall resolution simply by #including
+// <iasiothiscallresolver.h> after the asio #includes (this file _must_ be
+// included _after_ the asio #includes)
+
+#define ASIOInit(name) IASIOThiscallResolver::ASIOInit((name))
+
+
+#endif /* !defined(_MSC_VER) */
+
+#endif /* Win32 */
+
+#endif /* included_iasiothiscallresolver_h */
+
+
diff --git a/src/audio/portaudio/pa_asio/pa_asio.cpp b/src/audio/portaudio/pa_asio/pa_asio.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..e622ecb7436f4212dfe77210c944b056fb5fd3df
--- /dev/null
+++ b/src/audio/portaudio/pa_asio/pa_asio.cpp
@@ -0,0 +1,2901 @@
+/*
+ * $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-2002 Stephane Letz, Phil Burk, Ross Bencina
+ *
+ * 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
+        02-01-02 Cleanup, test of multiple-stream opening : Stephane Letz
+        19-02-02 New Pa_ASIO_loadDriver that calls CoInitialize on each thread on Windows : Stephane Letz
+        09-04-02 Correct error code management in PaHost_Term, removes various compiler warning : Stephane Letz
+        12-04-02 Add Mac includes for <Devices.h> and <Timer.h> : Phil Burk
+        13-04-02 Removes another compiler warning : Stephane Letz
+        30-04-02 Pa_ASIO_QueryDeviceInfo bug correction, memory allocation checking, better error handling : D Viens, P Burk, S Letz
+        12-06-02 Rehashed into new multi-api infrastructure, added support for all ASIO sample formats : Ross Bencina
+        18-06-02 Added pa_asio.h, PaAsio_GetAvailableLatencyValues() : Ross B.
+        21-06-02 Added SelectHostBufferSize() which selects host buffer size based on user latency parameters : Ross Bencina
+        ** NOTE  maintanance history is now stored in CVS **
+*/
+
+/** @file
+
+    Note that specific support for paInputUnderflow, paOutputOverflow and
+    paNeverDropInput is not necessary or possible with this driver due to the
+    synchronous full duplex double-buffered architecture of ASIO.
+
+    @todo check that CoInitialize()/CoUninitialize() are always correctly
+        paired, even in error cases.
+
+    @todo implement host api specific extension to set i/o buffer sizes in frames
+
+    @todo implement ReadStream, WriteStream, GetStreamReadAvailable, GetStreamWriteAvailable
+
+    @todo implement IsFormatSupported
+
+    @todo work out how to implement stream stoppage from callback and
+            implement IsStreamActive properly. Stream stoppage could be implemented
+            using a high-priority thread blocked on an Event which is signalled
+            by the callback. Or, we could just call ASIO stop from the callback
+            and see what happens.
+
+    @todo rigorously check asio return codes and convert to pa error codes
+
+    @todo Different channels of a multichannel stream can have different sample
+            formats, but we assume that all are the same as the first channel for now.
+            Fixing this will require the block processor to maintain per-channel
+            conversion functions - could get nasty.
+
+    @todo investigate whether the asio processNow flag needs to be honoured
+
+    @todo handle asioMessages() callbacks in a useful way, or at least document
+            what cases we don't handle.
+
+    @todo miscellaneous other FIXMEs
+
+    @todo provide an asio-specific method for setting the systems specific
+        value (aka main window handle) - check that this matches the value
+        passed to PaAsio_ShowControlPanel, or remove it entirely from
+        PaAsio_ShowControlPanel. - this would allow PaAsio_ShowControlPanel
+        to be called for the currently open stream (at present all streams
+        must be closed).
+*/
+
+
+
+#include <stdio.h>
+#include <assert.h>
+#include <string.h>
+//#include <values.h>
+
+#include <windows.h>
+#include <mmsystem.h>
+
+#include "portaudio.h"
+#include "pa_asio.h"
+#include "pa_util.h"
+#include "pa_allocation.h"
+#include "pa_hostapi.h"
+#include "pa_stream.h"
+#include "pa_cpuload.h"
+#include "pa_process.h"
+
+#include "asiosys.h"
+#include "asio.h"
+#include "asiodrivers.h"
+#include "iasiothiscallresolver.h"
+
+/*
+#if MAC
+#include <Devices.h>
+#include <Timer.h>
+#include <Math64.h>
+#else
+*/
+/*
+#include <math.h>
+#include <windows.h>
+#include <mmsystem.h>
+*/
+/*
+#endif
+*/
+
+/* external references */
+extern AsioDrivers* asioDrivers ;
+bool loadAsioDriver(char *name);
+
+
+/* We are trying to be compatible with CARBON but this has not been thoroughly tested. */
+/* not tested at all since new code was introduced. */
+#define CARBON_COMPATIBLE  (0)
+
+
+
+
+/* prototypes for functions declared in this file */
+
+extern "C" PaError PaAsio_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex );
+static void Terminate( struct PaUtilHostApiRepresentation *hostApi );
+static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
+                           PaStream** s,
+                           const PaStreamParameters *inputParameters,
+                           const PaStreamParameters *outputParameters,
+                           double sampleRate,
+                           unsigned long framesPerBuffer,
+                           PaStreamFlags streamFlags,
+                           PaStreamCallback *streamCallback,
+                           void *userData );
+static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
+                                  const PaStreamParameters *inputParameters,
+                                  const PaStreamParameters *outputParameters,
+                                  double sampleRate );
+static PaError CloseStream( PaStream* stream );
+static PaError StartStream( PaStream *stream );
+static PaError StopStream( PaStream *stream );
+static PaError AbortStream( PaStream *stream );
+static PaError IsStreamStopped( PaStream *s );
+static PaError IsStreamActive( PaStream *stream );
+static PaTime GetStreamTime( PaStream *stream );
+static double GetStreamCpuLoad( PaStream* stream );
+static PaError ReadStream( PaStream* stream, void *buffer, unsigned long frames );
+static PaError WriteStream( PaStream* stream, const void *buffer, unsigned long frames );
+static signed long GetStreamReadAvailable( PaStream* stream );
+static signed long GetStreamWriteAvailable( PaStream* stream );
+
+/* our ASIO callback functions */
+
+static void bufferSwitch(long index, ASIOBool processNow);
+static ASIOTime *bufferSwitchTimeInfo(ASIOTime *timeInfo, long index, ASIOBool processNow);
+static void sampleRateChanged(ASIOSampleRate sRate);
+static long asioMessages(long selector, long value, void* message, double* opt);
+
+static ASIOCallbacks asioCallbacks_ =
+    { bufferSwitch, sampleRateChanged, asioMessages, bufferSwitchTimeInfo };
+
+
+#define PA_ASIO_SET_LAST_HOST_ERROR( errorCode, errorText ) \
+    PaUtil_SetLastHostErrorInfo( paASIO, errorCode, errorText )
+
+
+static void PaAsio_SetLastSystemError( DWORD errorCode )
+{
+    LPVOID lpMsgBuf;
+    FormatMessage(
+        FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
+        NULL,
+        errorCode,
+        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+        (LPTSTR) &lpMsgBuf,
+        0,
+        NULL
+    );
+    PaUtil_SetLastHostErrorInfo( paASIO, errorCode, (const char*)lpMsgBuf );
+    LocalFree( lpMsgBuf );
+}
+
+#define PA_ASIO_SET_LAST_SYSTEM_ERROR( errorCode ) \
+    PaAsio_SetLastSystemError( errorCode )
+
+
+static const char* PaAsio_GetAsioErrorText( ASIOError asioError )
+{
+    const char *result;
+
+    switch( asioError ){
+        case ASE_OK:
+        case ASE_SUCCESS:           result = "Success"; break;
+        case ASE_NotPresent:        result = "Hardware input or output is not present or available"; break;
+        case ASE_HWMalfunction:     result = "Hardware is malfunctioning"; break;
+        case ASE_InvalidParameter:  result = "Input parameter invalid"; break;
+        case ASE_InvalidMode:       result = "Hardware is in a bad mode or used in a bad mode"; break;
+        case ASE_SPNotAdvancing:    result = "Hardware is not running when sample position is inquired"; break;
+        case ASE_NoClock:           result = "Sample clock or rate cannot be determined or is not present"; break;
+        case ASE_NoMemory:          result = "Not enough memory for completing the request"; break;
+        default:                    result = "Unknown ASIO error"; break;
+    }
+
+    return result;
+}
+
+
+#define PA_ASIO_SET_LAST_ASIO_ERROR( asioError ) \
+    PaUtil_SetLastHostErrorInfo( paASIO, asioError, PaAsio_GetAsioErrorText( asioError ) )
+
+
+
+
+// Atomic increment and decrement operations
+#if MAC
+	/* need to be implemented on Mac */
+	inline long PaAsio_AtomicIncrement(volatile long* v) {return ++(*const_cast<long*>(v));}
+	inline long PaAsio_AtomicDecrement(volatile long* v) {return --(*const_cast<long*>(v));}
+#elif WINDOWS
+	inline long PaAsio_AtomicIncrement(volatile long* v) {return InterlockedIncrement(const_cast<long*>(v));}
+	inline long PaAsio_AtomicDecrement(volatile long* v) {return InterlockedDecrement(const_cast<long*>(v));}
+#endif
+
+
+
+typedef struct PaAsioDriverInfo
+{
+    ASIODriverInfo asioDriverInfo;
+    long inputChannelCount, outputChannelCount;
+    long bufferMinSize, bufferMaxSize, bufferPreferredSize, bufferGranularity;
+    bool postOutput;
+}
+PaAsioDriverInfo;
+
+
+/* PaAsioHostApiRepresentation - host api datastructure specific to this implementation */
+
+typedef struct
+{
+    PaUtilHostApiRepresentation inheritedHostApiRep;
+    PaUtilStreamInterface callbackStreamInterface;
+    PaUtilStreamInterface blockingStreamInterface;
+
+    PaUtilAllocationGroup *allocations;
+
+    void *systemSpecific;
+    
+    /* the ASIO C API only allows one ASIO driver to be open at a time,
+        so we keep track of whether we have the driver open here, and
+        use this information to return errors from OpenStream if the
+        driver is already open.
+
+        openAsioDeviceIndex will be PaNoDevice if there is no device open
+        and a valid pa_asio (not global) device index otherwise.
+
+        openAsioDriverInfo is populated with the driver info for the
+        currently open device (if any)
+    */
+    PaDeviceIndex openAsioDeviceIndex;
+    PaAsioDriverInfo openAsioDriverInfo;
+}
+PaAsioHostApiRepresentation;
+
+
+/*
+    Retrieve <driverCount> driver names from ASIO, returned in a char**
+    allocated in <group>.
+*/
+static char **GetAsioDriverNames( PaUtilAllocationGroup *group, long driverCount )
+{
+    char **result = 0;
+    int i;
+
+    result =(char**)PaUtil_GroupAllocateMemory(
+            group, sizeof(char*) * driverCount );
+    if( !result )
+        goto error;
+
+    result[0] = (char*)PaUtil_GroupAllocateMemory(
+            group, 32 * driverCount );
+    if( !result[0] )
+        goto error;
+
+    for( i=0; i<driverCount; ++i )
+        result[i] = result[0] + (32 * i);
+
+    asioDrivers->getDriverNames( result, driverCount );
+
+error:
+    return result;
+}
+
+
+static PaSampleFormat AsioSampleTypeToPaNativeSampleFormat(ASIOSampleType type)
+{
+    switch (type) {
+        case ASIOSTInt16MSB:
+        case ASIOSTInt16LSB:
+                return paInt16;
+
+        case ASIOSTFloat32MSB:
+        case ASIOSTFloat32LSB:
+        case ASIOSTFloat64MSB:
+        case ASIOSTFloat64LSB:
+                return paFloat32;
+
+        case ASIOSTInt32MSB:
+        case ASIOSTInt32LSB:
+        case ASIOSTInt32MSB16:
+        case ASIOSTInt32LSB16:
+        case ASIOSTInt32MSB18:
+        case ASIOSTInt32MSB20:
+        case ASIOSTInt32MSB24:
+        case ASIOSTInt32LSB18:
+        case ASIOSTInt32LSB20:
+        case ASIOSTInt32LSB24:
+                return paInt32;
+
+        case ASIOSTInt24MSB:
+        case ASIOSTInt24LSB:
+                return paInt24;
+
+        default:
+                return paCustomFormat;
+    }
+}
+
+void AsioSampleTypeLOG(ASIOSampleType type)
+{
+    switch (type) {
+        case ASIOSTInt16MSB:  PA_DEBUG(("ASIOSTInt16MSB\n"));  break;
+        case ASIOSTInt16LSB:  PA_DEBUG(("ASIOSTInt16LSB\n"));  break;
+        case ASIOSTFloat32MSB:PA_DEBUG(("ASIOSTFloat32MSB\n"));break;
+        case ASIOSTFloat32LSB:PA_DEBUG(("ASIOSTFloat32LSB\n"));break;
+        case ASIOSTFloat64MSB:PA_DEBUG(("ASIOSTFloat64MSB\n"));break;
+        case ASIOSTFloat64LSB:PA_DEBUG(("ASIOSTFloat64LSB\n"));break;
+        case ASIOSTInt32MSB:  PA_DEBUG(("ASIOSTInt32MSB\n"));  break;
+        case ASIOSTInt32LSB:  PA_DEBUG(("ASIOSTInt32LSB\n"));  break;
+        case ASIOSTInt32MSB16:PA_DEBUG(("ASIOSTInt32MSB16\n"));break;
+        case ASIOSTInt32LSB16:PA_DEBUG(("ASIOSTInt32LSB16\n"));break;
+        case ASIOSTInt32MSB18:PA_DEBUG(("ASIOSTInt32MSB18\n"));break;
+        case ASIOSTInt32MSB20:PA_DEBUG(("ASIOSTInt32MSB20\n"));break;
+        case ASIOSTInt32MSB24:PA_DEBUG(("ASIOSTInt32MSB24\n"));break;
+        case ASIOSTInt32LSB18:PA_DEBUG(("ASIOSTInt32LSB18\n"));break;
+        case ASIOSTInt32LSB20:PA_DEBUG(("ASIOSTInt32LSB20\n"));break;
+        case ASIOSTInt32LSB24:PA_DEBUG(("ASIOSTInt32LSB24\n"));break;
+        case ASIOSTInt24MSB:  PA_DEBUG(("ASIOSTInt24MSB\n"));  break;
+        case ASIOSTInt24LSB:  PA_DEBUG(("ASIOSTInt24LSB\n"));  break;
+        default:              PA_DEBUG(("Custom Format%d\n",type));break;
+
+    }
+}
+
+static int BytesPerAsioSample( ASIOSampleType sampleType )
+{
+    switch (sampleType) {
+        case ASIOSTInt16MSB:
+        case ASIOSTInt16LSB:
+            return 2;
+
+        case ASIOSTFloat64MSB:
+        case ASIOSTFloat64LSB:
+            return 8;
+
+        case ASIOSTFloat32MSB:
+        case ASIOSTFloat32LSB:
+        case ASIOSTInt32MSB:
+        case ASIOSTInt32LSB:
+        case ASIOSTInt32MSB16:
+        case ASIOSTInt32LSB16:
+        case ASIOSTInt32MSB18:
+        case ASIOSTInt32MSB20:
+        case ASIOSTInt32MSB24:
+        case ASIOSTInt32LSB18:
+        case ASIOSTInt32LSB20:
+        case ASIOSTInt32LSB24:
+            return 4;
+
+        case ASIOSTInt24MSB:
+        case ASIOSTInt24LSB:
+            return 3;
+
+        default:
+            return 0;
+    }
+}
+
+
+static void Swap16( void *buffer, long shift, long count )
+{
+    unsigned short *p = (unsigned short*)buffer;
+    unsigned short temp;
+    (void) shift; /* unused parameter */
+
+    while( count-- )
+    {
+        temp = *p;
+        *p++ = (unsigned short)((temp<<8) | (temp>>8));
+    }
+}
+
+static void Swap24( void *buffer, long shift, long count )
+{
+    unsigned char *p = (unsigned char*)buffer;
+    unsigned char temp;
+    (void) shift; /* unused parameter */
+
+    while( count-- )
+    {
+        temp = *p;
+        *p = *(p+2);
+        *(p+2) = temp;
+        p += 3;
+    }
+}
+
+#define PA_SWAP32_( x ) ((x>>24) | ((x>>8)&0xFF00) | ((x<<8)&0xFF0000) | (x<<24));
+
+static void Swap32( void *buffer, long shift, long count )
+{
+    unsigned long *p = (unsigned long*)buffer;
+    unsigned long temp;
+    (void) shift; /* unused parameter */
+
+    while( count-- )
+    {
+        temp = *p;
+        *p++ = PA_SWAP32_( temp);
+    }
+}
+
+static void SwapShiftLeft32( void *buffer, long shift, long count )
+{
+    unsigned long *p = (unsigned long*)buffer;
+    unsigned long temp;
+
+    while( count-- )
+    {
+        temp = *p;
+        temp = PA_SWAP32_( temp);
+        *p++ = temp << shift;
+    }
+}
+
+static void ShiftRightSwap32( void *buffer, long shift, long count )
+{
+    unsigned long *p = (unsigned long*)buffer;
+    unsigned long temp;
+
+    while( count-- )
+    {
+        temp = *p >> shift;
+        *p++ = PA_SWAP32_( temp);
+    }
+}
+
+static void ShiftLeft32( void *buffer, long shift, long count )
+{
+    unsigned long *p = (unsigned long*)buffer;
+    unsigned long temp;
+
+    while( count-- )
+    {
+        temp = *p;
+        *p++ = temp << shift;
+    }
+}
+
+static void ShiftRight32( void *buffer, long shift, long count )
+{
+    unsigned long *p = (unsigned long*)buffer;
+    unsigned long temp;
+
+    while( count-- )
+    {
+        temp = *p;
+        *p++ = temp >> shift;
+    }
+}
+
+#define PA_SWAP_( x, y ) temp=x; x = y; y = temp;
+
+static void Swap64ConvertFloat64ToFloat32( void *buffer, long shift, long count )
+{
+    double *in = (double*)buffer;
+    float *out = (float*)buffer;
+    unsigned char *p;
+    unsigned char temp;
+    (void) shift; /* unused parameter */
+
+    while( count-- )
+    {
+        p = (unsigned char*)in;
+        PA_SWAP_( p[0], p[7] );
+        PA_SWAP_( p[1], p[6] );
+        PA_SWAP_( p[2], p[5] );
+        PA_SWAP_( p[3], p[4] );
+
+        *out++ = (float) (*in++);
+    }
+}
+
+static void ConvertFloat64ToFloat32( void *buffer, long shift, long count )
+{
+    double *in = (double*)buffer;
+    float *out = (float*)buffer;
+    (void) shift; /* unused parameter */
+
+    while( count-- )
+        *out++ = (float) (*in++);
+}
+
+static void ConvertFloat32ToFloat64Swap64( void *buffer, long shift, long count )
+{
+    float *in = ((float*)buffer) + (count-1);
+    double *out = ((double*)buffer) + (count-1);
+    unsigned char *p;
+    unsigned char temp;
+    (void) shift; /* unused parameter */
+
+    while( count-- )
+    {
+        *out = *in--;
+
+        p = (unsigned char*)out;
+        PA_SWAP_( p[0], p[7] );
+        PA_SWAP_( p[1], p[6] );
+        PA_SWAP_( p[2], p[5] );
+        PA_SWAP_( p[3], p[4] );
+
+        out--;
+    }
+}
+
+static void ConvertFloat32ToFloat64( void *buffer, long shift, long count )
+{
+    float *in = ((float*)buffer) + (count-1);
+    double *out = ((double*)buffer) + (count-1);
+    (void) shift; /* unused parameter */
+
+    while( count-- )
+        *out-- = *in--;
+}
+
+#ifdef MAC
+#define PA_MSB_IS_NATIVE_
+#undef PA_LSB_IS_NATIVE_
+#endif
+
+#ifdef WINDOWS
+#undef PA_MSB_IS_NATIVE_
+#define PA_LSB_IS_NATIVE_
+#endif
+
+typedef void PaAsioBufferConverter( void *, long, long );
+
+static void SelectAsioToPaConverter( ASIOSampleType type, PaAsioBufferConverter **converter, long *shift )
+{
+    *shift = 0;
+    *converter = 0;
+
+    switch (type) {
+        case ASIOSTInt16MSB:
+            /* dest: paInt16, no conversion necessary, possible byte swap*/
+            #ifdef PA_LSB_IS_NATIVE_
+                *converter = Swap16;
+            #endif
+            break;
+        case ASIOSTInt16LSB:
+            /* dest: paInt16, no conversion necessary, possible byte swap*/
+            #ifdef PA_MSB_IS_NATIVE_
+                *converter = Swap16;
+            #endif
+            break;
+        case ASIOSTFloat32MSB:
+            /* dest: paFloat32, no conversion necessary, possible byte swap*/
+            #ifdef PA_LSB_IS_NATIVE_
+                *converter = Swap32;
+            #endif
+            break;
+        case ASIOSTFloat32LSB:
+            /* dest: paFloat32, no conversion necessary, possible byte swap*/
+            #ifdef PA_MSB_IS_NATIVE_
+                *converter = Swap32;
+            #endif
+            break;
+        case ASIOSTFloat64MSB:
+            /* dest: paFloat32, in-place conversion to/from float32, possible byte swap*/
+            #ifdef PA_LSB_IS_NATIVE_
+                *converter = Swap64ConvertFloat64ToFloat32;
+            #else
+                *converter = ConvertFloat64ToFloat32;
+            #endif
+            break;
+        case ASIOSTFloat64LSB:
+            /* dest: paFloat32, in-place conversion to/from float32, possible byte swap*/
+            #ifdef PA_MSB_IS_NATIVE_
+                *converter = Swap64ConvertFloat64ToFloat32;
+            #else
+                *converter = ConvertFloat64ToFloat32;
+            #endif
+            break;
+        case ASIOSTInt32MSB:
+            /* dest: paInt32, no conversion necessary, possible byte swap */
+            #ifdef PA_LSB_IS_NATIVE_
+                *converter = Swap32;
+            #endif
+            break;
+        case ASIOSTInt32LSB:
+            /* dest: paInt32, no conversion necessary, possible byte swap */
+            #ifdef PA_MSB_IS_NATIVE_
+                *converter = Swap32;
+            #endif
+            break;
+        case ASIOSTInt32MSB16:
+            /* dest: paInt32, 16 bit shift, possible byte swap */
+            #ifdef PA_LSB_IS_NATIVE_
+                *converter = SwapShiftLeft32;
+            #else
+                *converter = ShiftLeft32;
+            #endif
+            *shift = 16;
+            break;
+        case ASIOSTInt32MSB18:
+            /* dest: paInt32, 14 bit shift, possible byte swap */
+            #ifdef PA_LSB_IS_NATIVE_
+                *converter = SwapShiftLeft32;
+            #else
+                *converter = ShiftLeft32;
+            #endif
+            *shift = 14;
+            break;
+        case ASIOSTInt32MSB20:
+            /* dest: paInt32, 12 bit shift, possible byte swap */
+            #ifdef PA_LSB_IS_NATIVE_
+                *converter = SwapShiftLeft32;
+            #else
+                *converter = ShiftLeft32;
+            #endif
+            *shift = 12;
+            break;
+        case ASIOSTInt32MSB24:
+            /* dest: paInt32, 8 bit shift, possible byte swap */
+            #ifdef PA_LSB_IS_NATIVE_
+                *converter = SwapShiftLeft32;
+            #else
+                *converter = ShiftLeft32;
+            #endif
+            *shift = 8;
+            break;
+        case ASIOSTInt32LSB16:
+            /* dest: paInt32, 16 bit shift, possible byte swap */
+            #ifdef PA_MSB_IS_NATIVE_
+                *converter = SwapShiftLeft32;
+            #else
+                *converter = ShiftLeft32;
+            #endif
+            *shift = 16;
+            break;
+        case ASIOSTInt32LSB18:
+            /* dest: paInt32, 14 bit shift, possible byte swap */
+            #ifdef PA_MSB_IS_NATIVE_
+                *converter = SwapShiftLeft32;
+            #else
+                *converter = ShiftLeft32;
+            #endif
+            *shift = 14;
+            break;
+        case ASIOSTInt32LSB20:
+            /* dest: paInt32, 12 bit shift, possible byte swap */
+            #ifdef PA_MSB_IS_NATIVE_
+                *converter = SwapShiftLeft32;
+            #else
+                *converter = ShiftLeft32;
+            #endif
+            *shift = 12;
+            break;
+        case ASIOSTInt32LSB24:
+            /* dest: paInt32, 8 bit shift, possible byte swap */
+            #ifdef PA_MSB_IS_NATIVE_
+                *converter = SwapShiftLeft32;
+            #else
+                *converter = ShiftLeft32;
+            #endif
+            *shift = 8;
+            break;
+        case ASIOSTInt24MSB:
+            /* dest: paInt24, no conversion necessary, possible byte swap */
+            #ifdef PA_LSB_IS_NATIVE_
+                *converter = Swap24;
+            #endif
+            break;
+        case ASIOSTInt24LSB:
+            /* dest: paInt24, no conversion necessary, possible byte swap */
+            #ifdef PA_MSB_IS_NATIVE_
+                *converter = Swap24;
+            #endif
+            break;
+    }
+}
+
+
+static void SelectPaToAsioConverter( ASIOSampleType type, PaAsioBufferConverter **converter, long *shift )
+{
+    *shift = 0;
+    *converter = 0;
+
+    switch (type) {
+        case ASIOSTInt16MSB:
+            /* src: paInt16, no conversion necessary, possible byte swap*/
+            #ifdef PA_LSB_IS_NATIVE_
+                *converter = Swap16;
+            #endif
+            break;
+        case ASIOSTInt16LSB:
+            /* src: paInt16, no conversion necessary, possible byte swap*/
+            #ifdef PA_MSB_IS_NATIVE_
+                *converter = Swap16;
+            #endif
+            break;
+        case ASIOSTFloat32MSB:
+            /* src: paFloat32, no conversion necessary, possible byte swap*/
+            #ifdef PA_LSB_IS_NATIVE_
+                *converter = Swap32;
+            #endif
+            break;
+        case ASIOSTFloat32LSB:
+            /* src: paFloat32, no conversion necessary, possible byte swap*/
+            #ifdef PA_MSB_IS_NATIVE_
+                *converter = Swap32;
+            #endif
+            break;
+        case ASIOSTFloat64MSB:
+            /* src: paFloat32, in-place conversion to/from float32, possible byte swap*/
+            #ifdef PA_LSB_IS_NATIVE_
+                *converter = ConvertFloat32ToFloat64Swap64;
+            #else
+                *converter = ConvertFloat32ToFloat64;
+            #endif
+            break;
+        case ASIOSTFloat64LSB:
+            /* src: paFloat32, in-place conversion to/from float32, possible byte swap*/
+            #ifdef PA_MSB_IS_NATIVE_
+                *converter = ConvertFloat32ToFloat64Swap64;
+            #else
+                *converter = ConvertFloat32ToFloat64;
+            #endif
+            break;
+        case ASIOSTInt32MSB:
+            /* src: paInt32, no conversion necessary, possible byte swap */
+            #ifdef PA_LSB_IS_NATIVE_
+                *converter = Swap32;
+            #endif
+            break;
+        case ASIOSTInt32LSB:
+            /* src: paInt32, no conversion necessary, possible byte swap */
+            #ifdef PA_MSB_IS_NATIVE_
+                *converter = Swap32;
+            #endif
+            break;
+        case ASIOSTInt32MSB16:
+            /* src: paInt32, 16 bit shift, possible byte swap */
+            #ifdef PA_LSB_IS_NATIVE_
+                *converter = ShiftRightSwap32;
+            #else
+                *converter = ShiftRight32;
+            #endif
+            *shift = 16;
+            break;
+        case ASIOSTInt32MSB18:
+            /* src: paInt32, 14 bit shift, possible byte swap */
+            #ifdef PA_LSB_IS_NATIVE_
+                *converter = ShiftRightSwap32;
+            #else
+                *converter = ShiftRight32;
+            #endif
+            *shift = 14;
+            break;
+        case ASIOSTInt32MSB20:
+            /* src: paInt32, 12 bit shift, possible byte swap */
+            #ifdef PA_LSB_IS_NATIVE_
+                *converter = ShiftRightSwap32;
+            #else
+                *converter = ShiftRight32;
+            #endif
+            *shift = 12;
+            break;
+        case ASIOSTInt32MSB24:
+            /* src: paInt32, 8 bit shift, possible byte swap */
+            #ifdef PA_LSB_IS_NATIVE_
+                *converter = ShiftRightSwap32;
+            #else
+                *converter = ShiftRight32;
+            #endif
+            *shift = 8;
+            break;
+        case ASIOSTInt32LSB16:
+            /* src: paInt32, 16 bit shift, possible byte swap */
+            #ifdef PA_MSB_IS_NATIVE_
+                *converter = ShiftRightSwap32;
+            #else
+                *converter = ShiftRight32;
+            #endif
+            *shift = 16;
+            break;
+        case ASIOSTInt32LSB18:
+            /* src: paInt32, 14 bit shift, possible byte swap */
+            #ifdef PA_MSB_IS_NATIVE_
+                *converter = ShiftRightSwap32;
+            #else
+                *converter = ShiftRight32;
+            #endif
+            *shift = 14;
+            break;
+        case ASIOSTInt32LSB20:
+            /* src: paInt32, 12 bit shift, possible byte swap */
+            #ifdef PA_MSB_IS_NATIVE_
+                *converter = ShiftRightSwap32;
+            #else
+                *converter = ShiftRight32;
+            #endif
+            *shift = 12;
+            break;
+        case ASIOSTInt32LSB24:
+            /* src: paInt32, 8 bit shift, possible byte swap */
+            #ifdef PA_MSB_IS_NATIVE_
+                *converter = ShiftRightSwap32;
+            #else
+                *converter = ShiftRight32;
+            #endif
+            *shift = 8;
+            break;
+        case ASIOSTInt24MSB:
+            /* src: paInt24, no conversion necessary, possible byte swap */
+            #ifdef PA_LSB_IS_NATIVE_
+                *converter = Swap24;
+            #endif
+            break;
+        case ASIOSTInt24LSB:
+            /* src: paInt24, no conversion necessary, possible byte swap */
+            #ifdef PA_MSB_IS_NATIVE_
+                *converter = Swap24;
+            #endif
+            break;
+    }
+}
+
+
+typedef struct PaAsioDeviceInfo
+{
+    PaDeviceInfo commonDeviceInfo;
+    long minBufferSize;
+    long maxBufferSize;
+    long preferredBufferSize;
+    long bufferGranularity;
+
+    ASIOChannelInfo *asioChannelInfos;
+}
+PaAsioDeviceInfo;
+
+
+PaError PaAsio_GetAvailableLatencyValues( PaDeviceIndex device,
+		long *minLatency, long *maxLatency, long *preferredLatency, long *granularity )
+{
+    PaError result;
+    PaUtilHostApiRepresentation *hostApi;
+    PaDeviceIndex hostApiDevice;
+
+    result = PaUtil_GetHostApiRepresentation( &hostApi, paASIO );
+
+    if( result == paNoError )
+    {
+        result = PaUtil_DeviceIndexToHostApiDeviceIndex( &hostApiDevice, device, hostApi );
+
+        if( result == paNoError )
+        {
+            PaAsioDeviceInfo *asioDeviceInfo =
+                    (PaAsioDeviceInfo*)hostApi->deviceInfos[hostApiDevice];
+
+            *minLatency = asioDeviceInfo->minBufferSize;
+            *maxLatency = asioDeviceInfo->maxBufferSize;
+            *preferredLatency = asioDeviceInfo->preferredBufferSize;
+            *granularity = asioDeviceInfo->bufferGranularity;
+        }
+    }
+
+    return result;
+}
+
+
+
+/*
+    load the asio driver named by <driverName> and return statistics about
+    the driver in info. If no error occurred, the driver will remain open
+    and must be closed by the called by calling ASIOExit() - if an error
+    is returned the driver will already be closed.
+*/
+static PaError LoadAsioDriver( const char *driverName,
+        PaAsioDriverInfo *driverInfo, void *systemSpecific )
+{
+    PaError result = paNoError;
+    ASIOError asioError;
+    int asioIsInitialized = 0;
+
+    if( !loadAsioDriver( const_cast<char*>(driverName) ) )
+    {
+        result = paUnanticipatedHostError;
+        PA_ASIO_SET_LAST_HOST_ERROR( 0, "Failed to load ASIO driver" );
+        goto error;
+    }
+
+    memset( &driverInfo->asioDriverInfo, 0, sizeof(ASIODriverInfo) );
+    driverInfo->asioDriverInfo.asioVersion = 2;
+    driverInfo->asioDriverInfo.sysRef = systemSpecific;
+    if( (asioError = ASIOInit( &driverInfo->asioDriverInfo )) != ASE_OK )
+    {
+        result = paUnanticipatedHostError;
+        PA_ASIO_SET_LAST_ASIO_ERROR( asioError );
+        goto error;
+    }
+    else
+    {
+        asioIsInitialized = 1;
+    }
+
+    if( (asioError = ASIOGetChannels(&driverInfo->inputChannelCount,
+            &driverInfo->outputChannelCount)) != ASE_OK )
+    {
+        result = paUnanticipatedHostError;
+        PA_ASIO_SET_LAST_ASIO_ERROR( asioError );
+        goto error;
+    }
+
+    if( (asioError = ASIOGetBufferSize(&driverInfo->bufferMinSize,
+            &driverInfo->bufferMaxSize, &driverInfo->bufferPreferredSize,
+            &driverInfo->bufferGranularity)) != ASE_OK )
+    {
+        result = paUnanticipatedHostError;
+        PA_ASIO_SET_LAST_ASIO_ERROR( asioError );
+        goto error;
+    }
+
+    if( ASIOOutputReady() == ASE_OK )
+        driverInfo->postOutput = true;
+    else
+        driverInfo->postOutput = false;
+
+    return result;
+
+error:
+    if( asioIsInitialized )
+        ASIOExit();
+
+    return result;
+}
+
+
+#define PA_DEFAULTSAMPLERATESEARCHORDER_COUNT_     13   /* must be the same number of elements as in the array below */
+static ASIOSampleRate defaultSampleRateSearchOrder_[]
+     = {44100.0, 48000.0, 32000.0, 24000.0, 22050.0, 88200.0, 96000.0,
+        192000.0, 16000.0, 12000.0, 11025.0, 9600.0, 8000.0 };
+
+
+PaError PaAsio_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex )
+{
+    PaError result = paNoError;
+    int i, driverCount;
+    PaAsioHostApiRepresentation *asioHostApi;
+    PaAsioDeviceInfo *deviceInfoArray;
+    char **names;
+    PaAsioDriverInfo paAsioDriverInfo;
+
+    asioHostApi = (PaAsioHostApiRepresentation*)PaUtil_AllocateMemory( sizeof(PaAsioHostApiRepresentation) );
+    if( !asioHostApi )
+    {
+        result = paInsufficientMemory;
+        goto error;
+    }
+
+    asioHostApi->allocations = PaUtil_CreateAllocationGroup();
+    if( !asioHostApi->allocations )
+    {
+        result = paInsufficientMemory;
+        goto error;
+    }
+
+    asioHostApi->systemSpecific = 0;
+    asioHostApi->openAsioDeviceIndex = paNoDevice;
+
+    *hostApi = &asioHostApi->inheritedHostApiRep;
+    (*hostApi)->info.structVersion = 1;
+
+    (*hostApi)->info.type = paASIO;
+    (*hostApi)->info.name = "ASIO";
+    (*hostApi)->info.deviceCount = 0;
+
+    #ifdef WINDOWS
+        /* use desktop window as system specific ptr */
+        asioHostApi->systemSpecific = GetDesktopWindow();
+        CoInitialize(NULL);
+    #endif
+
+    /* MUST BE CHECKED : to force fragments loading on Mac */
+    loadAsioDriver( "dummy" ); 
+
+    /* driverCount is the number of installed drivers - not necessarily
+        the number of installed physical devices. */
+    #if MAC
+        driverCount = asioDrivers->getNumFragments();
+    #elif WINDOWS
+        driverCount = asioDrivers->asioGetNumDev();
+    #endif
+
+    if( driverCount > 0 )
+    {
+        names = GetAsioDriverNames( asioHostApi->allocations, driverCount );
+        if( !names )
+        {
+            result = paInsufficientMemory;
+            goto error;
+        }
+
+
+        /* allocate enough space for all drivers, even if some aren't installed */
+
+        (*hostApi)->deviceInfos = (PaDeviceInfo**)PaUtil_GroupAllocateMemory(
+                asioHostApi->allocations, sizeof(PaDeviceInfo*) * driverCount );
+        if( !(*hostApi)->deviceInfos )
+        {
+            result = paInsufficientMemory;
+            goto error;
+        }
+
+        /* allocate all device info structs in a contiguous block */
+        deviceInfoArray = (PaAsioDeviceInfo*)PaUtil_GroupAllocateMemory(
+                asioHostApi->allocations, sizeof(PaAsioDeviceInfo) * driverCount );
+        if( !deviceInfoArray )
+        {
+            result = paInsufficientMemory;
+            goto error;
+        }
+
+        for( i=0; i < driverCount; ++i )
+        {
+            /* Attempt to load the asio driver... */
+            if( LoadAsioDriver( names[i], &paAsioDriverInfo, asioHostApi->systemSpecific ) == paNoError )
+            {
+                PaAsioDeviceInfo *asioDeviceInfo = &deviceInfoArray[ (*hostApi)->info.deviceCount ];
+                PaDeviceInfo *deviceInfo = &asioDeviceInfo->commonDeviceInfo;
+
+                deviceInfo->structVersion = 2;
+                deviceInfo->hostApi = hostApiIndex;
+
+                deviceInfo->name = names[i];
+                PA_DEBUG(("PaAsio_Initialize: drv:%d name = %s\n",  i,deviceInfo->name));
+                PA_DEBUG(("PaAsio_Initialize: drv:%d inputChannels       = %d\n", i, paAsioDriverInfo.inputChannelCount));
+                PA_DEBUG(("PaAsio_Initialize: drv:%d outputChannels      = %d\n", i, paAsioDriverInfo.outputChannelCount));
+                PA_DEBUG(("PaAsio_Initialize: drv:%d bufferMinSize       = %d\n", i, paAsioDriverInfo.bufferMinSize));
+                PA_DEBUG(("PaAsio_Initialize: drv:%d bufferMaxSize       = %d\n", i, paAsioDriverInfo.bufferMaxSize));
+                PA_DEBUG(("PaAsio_Initialize: drv:%d bufferPreferredSize = %d\n", i, paAsioDriverInfo.bufferPreferredSize));
+                PA_DEBUG(("PaAsio_Initialize: drv:%d bufferGranularity   = %d\n", i, paAsioDriverInfo.bufferGranularity));
+
+                deviceInfo->maxInputChannels  = paAsioDriverInfo.inputChannelCount;
+                deviceInfo->maxOutputChannels = paAsioDriverInfo.outputChannelCount;
+
+                deviceInfo->defaultSampleRate = 0.;
+                bool foundDefaultSampleRate = false;
+                for( int j=0; j < PA_DEFAULTSAMPLERATESEARCHORDER_COUNT_; ++j )
+                {
+                    ASIOError asioError = ASIOCanSampleRate( defaultSampleRateSearchOrder_[j] );
+                    if( asioError != ASE_NoClock && asioError != ASE_NotPresent )
+                    {
+                        deviceInfo->defaultSampleRate = defaultSampleRateSearchOrder_[j];
+                        foundDefaultSampleRate = true;
+                        break;
+                    }
+                }
+
+                PA_DEBUG(("PaAsio_Initialize: drv:%d defaultSampleRate = %f\n", i, deviceInfo->defaultSampleRate));
+
+                if( foundDefaultSampleRate ){
+
+                    /* calculate default latency values from bufferPreferredSize
+                        for default low latency, and bufferPreferredSize * 3
+                        for default high latency.
+                        use the default sample rate to convert from samples to
+                        seconds. Without knowing what sample rate the user will
+                        use this is the best we can do.
+                    */
+
+                    double defaultLowLatency =
+                            paAsioDriverInfo.bufferPreferredSize / deviceInfo->defaultSampleRate;
+
+                    deviceInfo->defaultLowInputLatency = defaultLowLatency;
+                    deviceInfo->defaultLowOutputLatency = defaultLowLatency;
+
+                    long defaultHighLatencyBufferSize =
+                            paAsioDriverInfo.bufferPreferredSize * 3;
+
+                    if( defaultHighLatencyBufferSize > paAsioDriverInfo.bufferMaxSize )
+                        defaultHighLatencyBufferSize = paAsioDriverInfo.bufferMaxSize;
+
+                    double defaultHighLatency =
+                            defaultHighLatencyBufferSize / deviceInfo->defaultSampleRate;
+
+                    if( defaultHighLatency < defaultLowLatency )
+                        defaultHighLatency = defaultLowLatency; /* just incase the driver returns something strange */ 
+                            
+                    deviceInfo->defaultHighInputLatency = defaultHighLatency;
+                    deviceInfo->defaultHighOutputLatency = defaultHighLatency;
+                    
+                }else{
+
+                    deviceInfo->defaultLowInputLatency = 0.;
+                    deviceInfo->defaultLowOutputLatency = 0.;
+                    deviceInfo->defaultHighInputLatency = 0.;
+                    deviceInfo->defaultHighOutputLatency = 0.;
+                }
+
+                PA_DEBUG(("PaAsio_Initialize: drv:%d defaultLowInputLatency = %f\n", i, deviceInfo->defaultLowInputLatency));
+                PA_DEBUG(("PaAsio_Initialize: drv:%d defaultLowOutputLatency = %f\n", i, deviceInfo->defaultLowOutputLatency));
+                PA_DEBUG(("PaAsio_Initialize: drv:%d defaultHighInputLatency = %f\n", i, deviceInfo->defaultHighInputLatency));
+                PA_DEBUG(("PaAsio_Initialize: drv:%d defaultHighOutputLatency = %f\n", i, deviceInfo->defaultHighOutputLatency));
+
+                asioDeviceInfo->minBufferSize = paAsioDriverInfo.bufferMinSize;
+                asioDeviceInfo->maxBufferSize = paAsioDriverInfo.bufferMaxSize;
+                asioDeviceInfo->preferredBufferSize = paAsioDriverInfo.bufferPreferredSize;
+                asioDeviceInfo->bufferGranularity = paAsioDriverInfo.bufferGranularity;
+
+
+                asioDeviceInfo->asioChannelInfos = (ASIOChannelInfo*)PaUtil_GroupAllocateMemory(
+                        asioHostApi->allocations,
+                        sizeof(ASIOChannelInfo) * (deviceInfo->maxInputChannels
+                                + deviceInfo->maxOutputChannels) );
+                if( !asioDeviceInfo->asioChannelInfos )
+                {
+                    result = paInsufficientMemory;
+                    goto error;
+                }
+
+                int a;
+
+                for( a=0; a < deviceInfo->maxInputChannels; ++a ){
+                    asioDeviceInfo->asioChannelInfos[a].channel = a;
+                    asioDeviceInfo->asioChannelInfos[a].isInput = ASIOTrue;
+                    ASIOError asioError = ASIOGetChannelInfo( &asioDeviceInfo->asioChannelInfos[a] );
+                    if( asioError != ASE_OK )
+                    {
+                        result = paUnanticipatedHostError;
+                        PA_ASIO_SET_LAST_ASIO_ERROR( asioError );
+                        goto error;
+                    }
+                }
+
+                for( a=0; a < deviceInfo->maxOutputChannels; ++a ){
+                    int b = deviceInfo->maxInputChannels + a;
+                    asioDeviceInfo->asioChannelInfos[b].channel = a;
+                    asioDeviceInfo->asioChannelInfos[b].isInput = ASIOFalse;
+                    ASIOError asioError = ASIOGetChannelInfo( &asioDeviceInfo->asioChannelInfos[b] );
+                    if( asioError != ASE_OK )
+                    {
+                        result = paUnanticipatedHostError;
+                        PA_ASIO_SET_LAST_ASIO_ERROR( asioError );
+                        goto error;
+                    }
+                }
+
+
+                /* unload the driver */
+                ASIOExit();
+
+                (*hostApi)->deviceInfos[ (*hostApi)->info.deviceCount ] = deviceInfo;
+                ++(*hostApi)->info.deviceCount;
+            }
+        }
+    }
+
+    if( (*hostApi)->info.deviceCount > 0 )
+    {
+        (*hostApi)->info.defaultInputDevice = 0;
+        (*hostApi)->info.defaultOutputDevice = 0;
+    }
+    else
+    {
+        (*hostApi)->info.defaultInputDevice = paNoDevice;
+        (*hostApi)->info.defaultOutputDevice = paNoDevice;
+    }
+
+
+    (*hostApi)->Terminate = Terminate;
+    (*hostApi)->OpenStream = OpenStream;
+    (*hostApi)->IsFormatSupported = IsFormatSupported;
+
+    PaUtil_InitializeStreamInterface( &asioHostApi->callbackStreamInterface, CloseStream, StartStream,
+                                      StopStream, AbortStream, IsStreamStopped, IsStreamActive,
+                                      GetStreamTime, GetStreamCpuLoad,
+                                      PaUtil_DummyRead, PaUtil_DummyWrite,
+                                      PaUtil_DummyGetReadAvailable, PaUtil_DummyGetWriteAvailable );
+
+    PaUtil_InitializeStreamInterface( &asioHostApi->blockingStreamInterface, CloseStream, StartStream,
+                                      StopStream, AbortStream, IsStreamStopped, IsStreamActive,
+                                      GetStreamTime, PaUtil_DummyGetCpuLoad,
+                                      ReadStream, WriteStream, GetStreamReadAvailable, GetStreamWriteAvailable );
+
+    return result;
+
+error:
+    if( asioHostApi )
+    {
+        if( asioHostApi->allocations )
+        {
+            PaUtil_FreeAllAllocations( asioHostApi->allocations );
+            PaUtil_DestroyAllocationGroup( asioHostApi->allocations );
+        }
+
+        PaUtil_FreeMemory( asioHostApi );
+    }
+    return result;
+}
+
+
+static void Terminate( struct PaUtilHostApiRepresentation *hostApi )
+{
+    PaAsioHostApiRepresentation *asioHostApi = (PaAsioHostApiRepresentation*)hostApi;
+
+    /*
+        IMPLEMENT ME:
+            - clean up any resources not handled by the allocation group
+    */
+
+    if( asioHostApi->allocations )
+    {
+        PaUtil_FreeAllAllocations( asioHostApi->allocations );
+        PaUtil_DestroyAllocationGroup( asioHostApi->allocations );
+    }
+
+    PaUtil_FreeMemory( asioHostApi );
+}
+
+
+static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
+                                  const PaStreamParameters *inputParameters,
+                                  const PaStreamParameters *outputParameters,
+                                  double sampleRate )
+{
+    PaError result = paNoError;
+    PaAsioHostApiRepresentation *asioHostApi = (PaAsioHostApiRepresentation*)hostApi;
+    PaAsioDriverInfo *driverInfo = &asioHostApi->openAsioDriverInfo;
+    int inputChannelCount, outputChannelCount;
+    PaSampleFormat inputSampleFormat, outputSampleFormat;
+    PaDeviceIndex asioDeviceIndex;                                  
+    ASIOError asioError;
+    
+    if( inputParameters && outputParameters )
+    {
+        /* full duplex ASIO stream must use the same device for input and output */
+
+        if( inputParameters->device != outputParameters->device )
+            return paBadIODeviceCombination;
+    }
+    
+    if( inputParameters )
+    {
+        inputChannelCount = inputParameters->channelCount;
+        inputSampleFormat = inputParameters->sampleFormat;
+
+        /* all standard sample formats are supported by the buffer adapter,
+            this implementation doesn't support any custom sample formats */
+        if( inputSampleFormat & paCustomFormat )
+            return paSampleFormatNotSupported;
+            
+        /* unless alternate device specification is supported, reject the use of
+            paUseHostApiSpecificDeviceSpecification */
+
+        if( inputParameters->device == paUseHostApiSpecificDeviceSpecification )
+            return paInvalidDevice;
+
+        asioDeviceIndex = inputParameters->device;
+
+        /* validate inputStreamInfo */
+        /** @todo do more validation here */
+        // if( inputParameters->hostApiSpecificStreamInfo )
+        //    return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */
+    }
+    else
+    {
+        inputChannelCount = 0;
+    }
+
+    if( outputParameters )
+    {
+        outputChannelCount = outputParameters->channelCount;
+        outputSampleFormat = outputParameters->sampleFormat;
+
+        /* all standard sample formats are supported by the buffer adapter,
+            this implementation doesn't support any custom sample formats */
+        if( outputSampleFormat & paCustomFormat )
+            return paSampleFormatNotSupported;
+            
+        /* unless alternate device specification is supported, reject the use of
+            paUseHostApiSpecificDeviceSpecification */
+
+        if( outputParameters->device == paUseHostApiSpecificDeviceSpecification )
+            return paInvalidDevice;
+
+        asioDeviceIndex = outputParameters->device;
+
+        /* validate outputStreamInfo */
+        /** @todo do more validation here */
+        // if( outputParameters->hostApiSpecificStreamInfo )
+        //    return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */
+    }
+    else
+    {
+        outputChannelCount = 0;
+    }
+
+
+
+    /* if an ASIO device is open we can only get format information for the currently open device */
+
+    if( asioHostApi->openAsioDeviceIndex != paNoDevice 
+			&& asioHostApi->openAsioDeviceIndex != asioDeviceIndex )
+    {
+        return paDeviceUnavailable;
+    }
+
+
+    /* NOTE: we load the driver and use its current settings
+        rather than the ones in our device info structure which may be stale */
+
+    /* open the device if it's not already open */
+    if( asioHostApi->openAsioDeviceIndex == paNoDevice )
+    {
+        result = LoadAsioDriver( asioHostApi->inheritedHostApiRep.deviceInfos[ asioDeviceIndex ]->name,
+                driverInfo, asioHostApi->systemSpecific );
+        if( result != paNoError )
+            return result;
+    }
+
+    /* check that input device can support inputChannelCount */
+    if( inputChannelCount > 0 )
+    {
+        if( inputChannelCount > driverInfo->inputChannelCount )
+        {
+            result = paInvalidChannelCount;
+            goto done;
+        }
+    }
+
+    /* check that output device can support outputChannelCount */
+    if( outputChannelCount )
+    {
+        if( outputChannelCount > driverInfo->outputChannelCount )
+        {
+            result = paInvalidChannelCount;
+            goto done;
+        }
+    }
+    
+    /* query for sample rate support */
+    asioError = ASIOCanSampleRate( sampleRate );
+    if( asioError == ASE_NoClock || asioError == ASE_NotPresent )
+    {
+        result = paInvalidSampleRate;
+        goto done;
+    }
+
+done:
+    /* close the device if it wasn't already open */
+    if( asioHostApi->openAsioDeviceIndex == paNoDevice )
+    {
+        ASIOExit(); /* not sure if we should check for errors here */
+    }
+
+    if( result == paNoError )
+        return paFormatIsSupported;
+    else
+        return result;
+}
+
+
+
+/* PaAsioStream - a stream data structure specifically for this implementation */
+
+typedef struct PaAsioStream
+{
+    PaUtilStreamRepresentation streamRepresentation;
+    PaUtilCpuLoadMeasurer cpuLoadMeasurer;
+    PaUtilBufferProcessor bufferProcessor;
+
+    PaAsioHostApiRepresentation *asioHostApi;
+    unsigned long framesPerHostCallback;
+
+    /* ASIO driver info  - these may not be needed for the life of the stream,
+        but store them here until we work out how format conversion is going
+        to work. */
+
+    ASIOBufferInfo *asioBufferInfos;
+    ASIOChannelInfo *asioChannelInfos;
+    long inputLatency, outputLatency; // actual latencies returned by asio
+
+    long inputChannelCount, outputChannelCount;
+    bool postOutput;
+
+    void **bufferPtrs; /* this is carved up for inputBufferPtrs and outputBufferPtrs */
+    void **inputBufferPtrs[2];
+    void **outputBufferPtrs[2];
+
+    PaAsioBufferConverter *inputBufferConverter;
+    long inputShift;
+    PaAsioBufferConverter *outputBufferConverter;
+    long outputShift;
+
+    volatile bool stopProcessing;
+    int stopPlayoutCount;
+    HANDLE completedBuffersPlayedEvent;
+
+    bool streamFinishedCallbackCalled;
+    volatile int isActive;
+    volatile bool zeroOutput; /* all future calls to the callback will output silence */
+
+    volatile long reenterCount;
+    volatile long reenterError;
+
+    PaStreamCallbackFlags callbackFlags;
+}
+PaAsioStream;
+
+static PaAsioStream *theAsioStream = 0; /* due to ASIO sdk limitations there can be only one stream */
+
+
+static void ZeroOutputBuffers( PaAsioStream *stream, long index )
+{
+    int i;
+
+    for( i=0; i < stream->outputChannelCount; ++i )
+    {
+        void *buffer = stream->asioBufferInfos[ i + stream->inputChannelCount ].buffers[index];
+
+        int bytesPerSample = BytesPerAsioSample( stream->asioChannelInfos[ i + stream->inputChannelCount ].type );
+
+        memset( buffer, 0, stream->framesPerHostCallback * bytesPerSample );
+    }
+}
+
+
+static unsigned long SelectHostBufferSize( unsigned long suggestedLatencyFrames,
+        PaAsioDriverInfo *driverInfo )
+{
+    unsigned long result;
+
+    if( suggestedLatencyFrames == 0 )
+    {
+        result = driverInfo->bufferPreferredSize;
+    }
+    else{
+        if( suggestedLatencyFrames <= (unsigned long)driverInfo->bufferMinSize )
+        {
+            result = driverInfo->bufferMinSize;
+        }
+        else if( suggestedLatencyFrames >= (unsigned long)driverInfo->bufferMaxSize )
+        {
+            result = driverInfo->bufferMaxSize;
+        }
+        else
+        {
+            if( driverInfo->bufferGranularity == -1 )
+            {
+                /* power-of-two */
+                result = 2;
+
+                while( result < suggestedLatencyFrames )
+                    result *= 2;
+
+                if( result < (unsigned long)driverInfo->bufferMinSize )
+                    result = driverInfo->bufferMinSize;
+
+                if( result > (unsigned long)driverInfo->bufferMaxSize )
+                    result = driverInfo->bufferMaxSize;
+            }
+            else if( driverInfo->bufferGranularity == 0 )
+            {
+                /* the documentation states that bufferGranularity should be
+                    zero when bufferMinSize, bufferMaxSize and
+                    bufferPreferredSize are the same. We assume that is the case.
+                */
+
+                result = driverInfo->bufferPreferredSize;
+            }
+            else
+            {
+                /* modulo granularity */
+
+                unsigned long remainder =
+                        suggestedLatencyFrames % driverInfo->bufferGranularity;
+
+                if( remainder == 0 )
+                {
+                    result = suggestedLatencyFrames;
+                }
+                else
+                {
+                    result = suggestedLatencyFrames
+                            + (driverInfo->bufferGranularity - remainder);
+
+                    if( result > (unsigned long)driverInfo->bufferMaxSize )
+                        result = driverInfo->bufferMaxSize;
+                }
+            }
+        }
+    }
+
+    return result;
+}
+
+
+/* returns channelSelectors if present */
+
+static PaError ValidateAsioSpecificStreamInfo(
+        const PaStreamParameters *streamParameters,
+        const PaAsioStreamInfo *streamInfo,
+        int deviceChannelCount,
+        int **channelSelectors )
+{
+	if( streamInfo )
+	{
+	    if( streamInfo->size != sizeof( PaAsioStreamInfo )
+	            || streamInfo->version != 1 )
+	    {
+	        return paIncompatibleHostApiSpecificStreamInfo;
+	    }
+
+	    if( streamInfo->flags & paAsioUseChannelSelectors )
+            *channelSelectors = streamInfo->channelSelectors;
+
+        if( !(*channelSelectors) )
+            return paIncompatibleHostApiSpecificStreamInfo;
+
+        for( int i=0; i < streamParameters->channelCount; ++i ){
+             if( (*channelSelectors)[i] < 0
+                    || (*channelSelectors)[i] >= deviceChannelCount ){
+                return paInvalidChannelCount;
+             }           
+        }
+	}
+
+	return paNoError;
+}
+
+
+/* see pa_hostapi.h for a list of validity guarantees made about OpenStream  parameters */
+
+static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
+                           PaStream** s,
+                           const PaStreamParameters *inputParameters,
+                           const PaStreamParameters *outputParameters,
+                           double sampleRate,
+                           unsigned long framesPerBuffer,
+                           PaStreamFlags streamFlags,
+                           PaStreamCallback *streamCallback,
+                           void *userData )
+{
+    PaError result = paNoError;
+    PaAsioHostApiRepresentation *asioHostApi = (PaAsioHostApiRepresentation*)hostApi;
+    PaAsioStream *stream = 0;
+    PaAsioStreamInfo *inputStreamInfo, *outputStreamInfo;
+    unsigned long framesPerHostBuffer;
+    int inputChannelCount, outputChannelCount;
+    PaSampleFormat inputSampleFormat, outputSampleFormat;
+    PaSampleFormat hostInputSampleFormat, hostOutputSampleFormat;
+    unsigned long suggestedInputLatencyFrames;
+    unsigned long suggestedOutputLatencyFrames;
+    PaDeviceIndex asioDeviceIndex;
+    ASIOError asioError;
+    int asioIsInitialized = 0;
+    int asioBuffersCreated = 0;
+    int completedBuffersPlayedEventInited = 0;
+    int i;
+    PaAsioDriverInfo *driverInfo;
+    int *inputChannelSelectors = 0;
+    int *outputChannelSelectors = 0;
+
+    /* unless we move to using lower level ASIO calls, we can only have
+        one device open at a time */
+    if( asioHostApi->openAsioDeviceIndex != paNoDevice ){
+        PA_DEBUG(("OpenStream paDeviceUnavailable\n"));
+        return paDeviceUnavailable;
+    }
+
+    if( inputParameters && outputParameters )
+    {
+        /* full duplex ASIO stream must use the same device for input and output */
+
+        if( inputParameters->device != outputParameters->device ){
+            PA_DEBUG(("OpenStream paBadIODeviceCombination\n"));
+            return paBadIODeviceCombination;
+    }
+    }
+
+    if( inputParameters )
+    {
+        inputChannelCount = inputParameters->channelCount;
+        inputSampleFormat = inputParameters->sampleFormat;
+        suggestedInputLatencyFrames = (unsigned long)((inputParameters->suggestedLatency * sampleRate)+0.5f);
+
+        /* unless alternate device specification is supported, reject the use of
+            paUseHostApiSpecificDeviceSpecification */
+        if( inputParameters->device == paUseHostApiSpecificDeviceSpecification )
+            return paInvalidDevice;
+
+        asioDeviceIndex = inputParameters->device;
+
+        PaAsioDeviceInfo *asioDeviceInfo = (PaAsioDeviceInfo*)hostApi->deviceInfos[asioDeviceIndex];
+
+        /* validate hostApiSpecificStreamInfo */
+        inputStreamInfo = (PaAsioStreamInfo*)inputParameters->hostApiSpecificStreamInfo;
+        result = ValidateAsioSpecificStreamInfo( inputParameters, inputStreamInfo,
+            asioDeviceInfo->commonDeviceInfo.maxInputChannels,
+            &inputChannelSelectors
+        );
+        if( result != paNoError ) return result;
+    }
+    else
+    {
+        inputChannelCount = 0;
+        inputSampleFormat = 0;
+        suggestedInputLatencyFrames = 0;
+    }
+
+    if( outputParameters )
+    {
+        outputChannelCount = outputParameters->channelCount;
+        outputSampleFormat = outputParameters->sampleFormat;
+        suggestedOutputLatencyFrames = (unsigned long)((outputParameters->suggestedLatency * sampleRate)+0.5f);
+
+        /* unless alternate device specification is supported, reject the use of
+            paUseHostApiSpecificDeviceSpecification */
+        if( outputParameters->device == paUseHostApiSpecificDeviceSpecification )
+            return paInvalidDevice;
+
+        asioDeviceIndex = outputParameters->device;
+
+        PaAsioDeviceInfo *asioDeviceInfo = (PaAsioDeviceInfo*)hostApi->deviceInfos[asioDeviceIndex];
+
+        /* validate hostApiSpecificStreamInfo */
+        outputStreamInfo = (PaAsioStreamInfo*)outputParameters->hostApiSpecificStreamInfo;
+        result = ValidateAsioSpecificStreamInfo( outputParameters, outputStreamInfo,
+            asioDeviceInfo->commonDeviceInfo.maxOutputChannels,
+            &outputChannelSelectors
+        );
+        if( result != paNoError ) return result;
+    }
+    else
+    {
+        outputChannelCount = 0;
+        outputSampleFormat = 0;
+        suggestedOutputLatencyFrames = 0;
+    }
+
+    driverInfo = &asioHostApi->openAsioDriverInfo;
+
+    /* NOTE: we load the driver and use its current settings
+        rather than the ones in our device info structure which may be stale */
+
+    result = LoadAsioDriver( asioHostApi->inheritedHostApiRep.deviceInfos[ asioDeviceIndex ]->name,
+            driverInfo, asioHostApi->systemSpecific );
+    if( result == paNoError )
+        asioIsInitialized = 1;
+    else{
+        PA_DEBUG(("OpenStream ERROR1\n"));
+        goto error;
+    }
+
+    /* check that input device can support inputChannelCount */
+    if( inputChannelCount > 0 )
+    {
+        if( inputChannelCount > driverInfo->inputChannelCount )
+        {
+            result = paInvalidChannelCount;
+            PA_DEBUG(("OpenStream ERROR2\n"));
+            goto error;
+        }
+    }
+
+    /* check that output device can support outputChannelCount */
+    if( outputChannelCount )
+    {
+        if( outputChannelCount > driverInfo->outputChannelCount )
+        {
+            result = paInvalidChannelCount;
+            PA_DEBUG(("OpenStream ERROR3\n"));
+            goto error;
+        }
+    }
+
+    PA_DEBUG(("before ASIOSetSampleRate(%f)\n",sampleRate));
+    asioError = ASIOSetSampleRate( sampleRate );
+    /* Set sample rate */
+    if( asioError != ASE_OK )
+    {
+        result = paInvalidSampleRate;
+        PA_DEBUG(("ERROR: ASIOSetSampleRate: %s\n", PaAsio_GetAsioErrorText(asioError) ));
+        goto error;
+    }
+    PA_DEBUG(("after ASIOSetSampleRate(%f)\n",sampleRate));
+    /*
+        IMPLEMENT ME:
+            - if a full duplex stream is requested, check that the combination
+                of input and output parameters is supported
+    */
+
+    /* validate platform specific flags */
+    if( (streamFlags & paPlatformSpecificFlags) != 0 ){
+        PA_DEBUG(("OpenStream invalid flags!!\n"));
+        return paInvalidFlag; /* unexpected platform specific flag */
+    }
+
+
+    stream = (PaAsioStream*)PaUtil_AllocateMemory( sizeof(PaAsioStream) );
+    if( !stream )
+    {
+        result = paInsufficientMemory;
+        PA_DEBUG(("OpenStream ERROR5\n"));
+        goto error;
+    }
+
+    stream->completedBuffersPlayedEvent = CreateEvent( NULL, TRUE, FALSE, NULL );
+    if( stream->completedBuffersPlayedEvent == NULL )
+    {
+        result = paUnanticipatedHostError;
+        PA_ASIO_SET_LAST_SYSTEM_ERROR( GetLastError() );
+        PA_DEBUG(("OpenStream ERROR6\n"));
+        goto error;
+    }
+    completedBuffersPlayedEventInited = 1;
+
+
+    stream->asioBufferInfos = 0; /* for deallocation in error */
+    stream->asioChannelInfos = 0; /* for deallocation in error */
+    stream->bufferPtrs = 0; /* for deallocation in error */
+
+    if( streamCallback )
+    {
+        PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation,
+                                               &asioHostApi->callbackStreamInterface, streamCallback, userData );
+    }
+    else
+    {
+        PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation,
+                                               &asioHostApi->blockingStreamInterface, streamCallback, userData );
+    }
+
+
+    PaUtil_InitializeCpuLoadMeasurer( &stream->cpuLoadMeasurer, sampleRate );
+
+
+    stream->asioBufferInfos = (ASIOBufferInfo*)PaUtil_AllocateMemory(
+            sizeof(ASIOBufferInfo) * (inputChannelCount + outputChannelCount) );
+    if( !stream->asioBufferInfos )
+    {
+        result = paInsufficientMemory;
+        PA_DEBUG(("OpenStream ERROR7\n"));
+        goto error;
+    }
+
+
+    for( i=0; i < inputChannelCount; ++i )
+    {
+        ASIOBufferInfo *info = &stream->asioBufferInfos[i];
+
+        info->isInput = ASIOTrue;
+
+        if( inputChannelSelectors ){
+            // inputChannelSelectors values have already been validated in
+            // ValidateAsioSpecificStreamInfo() above
+            info->channelNum = inputChannelSelectors[i];
+        }else{
+            info->channelNum = i;
+        }
+
+        info->buffers[0] = info->buffers[1] = 0;
+    }
+
+    for( i=0; i < outputChannelCount; ++i ){
+        ASIOBufferInfo *info = &stream->asioBufferInfos[inputChannelCount+i];
+
+        info->isInput = ASIOFalse;
+
+        if( outputChannelSelectors ){
+            // outputChannelSelectors values have already been validated in
+            // ValidateAsioSpecificStreamInfo() above
+            info->channelNum = outputChannelSelectors[i];
+        }else{
+            info->channelNum = i;
+        }
+        
+        info->buffers[0] = info->buffers[1] = 0;
+    }
+
+
+    framesPerHostBuffer = SelectHostBufferSize(
+            (( suggestedInputLatencyFrames > suggestedOutputLatencyFrames )
+                    ? suggestedInputLatencyFrames : suggestedOutputLatencyFrames),
+            driverInfo );
+
+
+	PA_DEBUG(("PaAsioOpenStream: framesPerHostBuffer :%d\n",  framesPerHostBuffer));
+
+    asioError = ASIOCreateBuffers( stream->asioBufferInfos,
+            inputChannelCount+outputChannelCount,
+            framesPerHostBuffer, &asioCallbacks_ );
+
+    if( asioError != ASE_OK
+            && framesPerHostBuffer != (unsigned long)driverInfo->bufferPreferredSize )
+    {
+        PA_DEBUG(("ERROR: ASIOCreateBuffers: %s\n", PaAsio_GetAsioErrorText(asioError) ));
+        /*
+            Some buggy drivers (like the Hoontech DSP24) give incorrect
+            [min, preferred, max] values They should work with the preferred size
+            value, thus if Pa_ASIO_CreateBuffers fails with the hostBufferSize
+            computed in SelectHostBufferSize, we try again with the preferred size.
+        */
+
+        framesPerHostBuffer = driverInfo->bufferPreferredSize;
+
+        PA_DEBUG(("PaAsioOpenStream: CORRECTED framesPerHostBuffer :%d\n",  framesPerHostBuffer));
+
+        ASIOError asioError2 = ASIOCreateBuffers( stream->asioBufferInfos,
+                inputChannelCount+outputChannelCount,
+                 framesPerHostBuffer, &asioCallbacks_ );
+        if( asioError2 == ASE_OK )
+            asioError = ASE_OK;
+    }
+
+    if( asioError != ASE_OK )
+    {
+        result = paUnanticipatedHostError;
+        PA_ASIO_SET_LAST_ASIO_ERROR( asioError );
+        PA_DEBUG(("OpenStream ERROR9\n"));
+        goto error;
+    }
+
+    asioBuffersCreated = 1;
+
+    stream->asioChannelInfos = (ASIOChannelInfo*)PaUtil_AllocateMemory(
+            sizeof(ASIOChannelInfo) * (inputChannelCount + outputChannelCount) );
+    if( !stream->asioChannelInfos )
+    {
+        result = paInsufficientMemory;
+        PA_DEBUG(("OpenStream ERROR10\n"));
+        goto error;
+    }
+
+    for( i=0; i < inputChannelCount + outputChannelCount; ++i )
+    {
+        stream->asioChannelInfos[i].channel = stream->asioBufferInfos[i].channelNum;
+        stream->asioChannelInfos[i].isInput = stream->asioBufferInfos[i].isInput;
+        asioError = ASIOGetChannelInfo( &stream->asioChannelInfos[i] );
+        if( asioError != ASE_OK )
+        {
+            result = paUnanticipatedHostError;
+            PA_ASIO_SET_LAST_ASIO_ERROR( asioError );
+            PA_DEBUG(("OpenStream ERROR11\n"));
+            goto error;
+        }
+    }
+
+    stream->bufferPtrs = (void**)PaUtil_AllocateMemory(
+            2 * sizeof(void*) * (inputChannelCount + outputChannelCount) );
+    if( !stream->bufferPtrs )
+    {
+        result = paInsufficientMemory;
+        PA_DEBUG(("OpenStream ERROR12\n"));
+        goto error;
+    }
+
+    if( inputChannelCount > 0 )
+    {
+        stream->inputBufferPtrs[0] = stream-> bufferPtrs;
+        stream->inputBufferPtrs[1] = &stream->bufferPtrs[inputChannelCount];
+
+        for( i=0; i<inputChannelCount; ++i )
+        {
+            stream->inputBufferPtrs[0][i] = stream->asioBufferInfos[i].buffers[0];
+            stream->inputBufferPtrs[1][i] = stream->asioBufferInfos[i].buffers[1];
+        }
+    }
+    else
+    {
+        stream->inputBufferPtrs[0] = 0;
+        stream->inputBufferPtrs[1] = 0;
+    }
+
+    if( outputChannelCount > 0 )
+    {
+        stream->outputBufferPtrs[0] = &stream->bufferPtrs[inputChannelCount*2];
+        stream->outputBufferPtrs[1] = &stream->bufferPtrs[inputChannelCount*2 + outputChannelCount];
+
+        for( i=0; i<outputChannelCount; ++i )
+        {
+            stream->outputBufferPtrs[0][i] = stream->asioBufferInfos[inputChannelCount+i].buffers[0];
+            stream->outputBufferPtrs[1][i] = stream->asioBufferInfos[inputChannelCount+i].buffers[1];
+        }
+    }
+    else
+    {
+        stream->outputBufferPtrs[0] = 0;
+        stream->outputBufferPtrs[1] = 0;
+    }
+
+    if( inputChannelCount > 0 )
+    {
+        /* FIXME: assume all channels use the same type for now */
+        ASIOSampleType inputType = stream->asioChannelInfos[0].type;
+
+        PA_DEBUG(("ASIO Input  type:%d",inputType));
+        AsioSampleTypeLOG(inputType);
+        hostInputSampleFormat = AsioSampleTypeToPaNativeSampleFormat( inputType );
+
+        SelectAsioToPaConverter( inputType, &stream->inputBufferConverter, &stream->inputShift );
+    }
+    else
+    {
+        hostInputSampleFormat = 0;
+        stream->inputBufferConverter = 0;
+    }
+
+    if( outputChannelCount > 0 )
+    {
+        /* FIXME: assume all channels use the same type for now */
+        ASIOSampleType outputType = stream->asioChannelInfos[inputChannelCount].type;
+
+        PA_DEBUG(("ASIO Output type:%d",outputType));
+        AsioSampleTypeLOG(outputType);
+        hostOutputSampleFormat = AsioSampleTypeToPaNativeSampleFormat( outputType );
+
+        SelectPaToAsioConverter( outputType, &stream->outputBufferConverter, &stream->outputShift );
+    }
+    else
+    {
+        hostOutputSampleFormat = 0;
+        stream->outputBufferConverter = 0;
+    }
+
+    result =  PaUtil_InitializeBufferProcessor( &stream->bufferProcessor,
+                    inputChannelCount, inputSampleFormat, hostInputSampleFormat,
+                    outputChannelCount, outputSampleFormat, hostOutputSampleFormat,
+                    sampleRate, streamFlags, framesPerBuffer,
+                    framesPerHostBuffer, paUtilFixedHostBufferSize,
+                    streamCallback, userData );
+    if( result != paNoError ){
+        PA_DEBUG(("OpenStream ERROR13\n"));
+        goto error;
+    }
+
+
+    ASIOGetLatencies( &stream->inputLatency, &stream->outputLatency );
+
+    stream->streamRepresentation.streamInfo.inputLatency =
+            (double)( PaUtil_GetBufferProcessorInputLatency(&stream->bufferProcessor)
+                + stream->inputLatency) / sampleRate;   // seconds
+    stream->streamRepresentation.streamInfo.outputLatency =
+            (double)( PaUtil_GetBufferProcessorOutputLatency(&stream->bufferProcessor)
+                + stream->outputLatency) / sampleRate; // seconds
+    stream->streamRepresentation.streamInfo.sampleRate = sampleRate;
+
+    // the code below prints the ASIO latency which doesn't include the
+    // buffer processor latency. it reports the added latency separately
+    PA_DEBUG(("PaAsio : ASIO InputLatency = %ld (%ld ms), added buffProc:%ld (%ld ms)\n",
+            stream->inputLatency,
+            (long)((stream->inputLatency*1000)/ sampleRate),  
+            PaUtil_GetBufferProcessorInputLatency(&stream->bufferProcessor),
+            (long)((PaUtil_GetBufferProcessorInputLatency(&stream->bufferProcessor)*1000)/ sampleRate)
+            ));
+
+    PA_DEBUG(("PaAsio : ASIO OuputLatency = %ld (%ld ms), added buffProc:%ld (%ld ms)\n",
+            stream->outputLatency,
+            (long)((stream->outputLatency*1000)/ sampleRate), 
+            PaUtil_GetBufferProcessorOutputLatency(&stream->bufferProcessor),
+            (long)((PaUtil_GetBufferProcessorOutputLatency(&stream->bufferProcessor)*1000)/ sampleRate)
+            ));
+
+    stream->asioHostApi = asioHostApi;
+    stream->framesPerHostCallback = framesPerHostBuffer;
+
+    stream->inputChannelCount = inputChannelCount;
+    stream->outputChannelCount = outputChannelCount;
+    stream->postOutput = driverInfo->postOutput;
+    stream->isActive = 0;
+
+    asioHostApi->openAsioDeviceIndex = asioDeviceIndex;
+
+    *s = (PaStream*)stream;
+
+    return result;
+
+error:
+    if( stream )
+    {
+        if( completedBuffersPlayedEventInited )
+            CloseHandle( stream->completedBuffersPlayedEvent );
+
+        if( stream->asioBufferInfos )
+            PaUtil_FreeMemory( stream->asioBufferInfos );
+
+        if( stream->asioChannelInfos )
+            PaUtil_FreeMemory( stream->asioChannelInfos );
+
+        if( stream->bufferPtrs )
+            PaUtil_FreeMemory( stream->bufferPtrs );
+
+        PaUtil_FreeMemory( stream );
+    }
+
+    if( asioBuffersCreated )
+        ASIODisposeBuffers();
+
+    if( asioIsInitialized )
+        ASIOExit();
+
+    return result;
+}
+
+
+/*
+    When CloseStream() is called, the multi-api layer ensures that
+    the stream has already been stopped or aborted.
+*/
+static PaError CloseStream( PaStream* s )
+{
+    PaError result = paNoError;
+    PaAsioStream *stream = (PaAsioStream*)s;
+
+    /*
+        IMPLEMENT ME:
+            - additional stream closing + cleanup
+    */
+
+    PaUtil_TerminateBufferProcessor( &stream->bufferProcessor );
+    PaUtil_TerminateStreamRepresentation( &stream->streamRepresentation );
+
+    stream->asioHostApi->openAsioDeviceIndex = paNoDevice;
+
+    CloseHandle( stream->completedBuffersPlayedEvent );
+
+    PaUtil_FreeMemory( stream->asioBufferInfos );
+    PaUtil_FreeMemory( stream->asioChannelInfos );
+    PaUtil_FreeMemory( stream->bufferPtrs );
+    PaUtil_FreeMemory( stream );
+
+    ASIODisposeBuffers();
+    ASIOExit();
+
+    return result;
+}
+
+
+static void bufferSwitch(long index, ASIOBool directProcess)
+{
+//TAKEN FROM THE ASIO SDK
+
+    // 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, directProcess );
+}
+
+
+// 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
+
+static ASIOTime *bufferSwitchTimeInfo( ASIOTime *timeInfo, long index, ASIOBool directProcess )
+{
+    // the actual processing callback.
+    // Beware that this is normally in a seperate thread, hence be sure that
+    // you take care about thread synchronization.
+
+
+    /* The SDK says the following about the directProcess flag:
+        suggests to the host whether it should immediately start processing
+        (directProcess == ASIOTrue), or whether its process should be deferred
+        because the call comes from a very low level (for instance, a high level
+        priority interrupt), and direct processing would cause timing instabilities for
+        the rest of the system. If in doubt, directProcess should be set to ASIOFalse.
+
+        We just ignore directProcess. This could cause incompatibilities with
+        drivers which really don't want the audio processing to occur in this
+        callback, but none have been identified yet.
+    */
+
+    (void) directProcess; /* suppress unused parameter warning */
+
+#if 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();
+#endif
+
+#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
+
+
+    if( !theAsioStream )
+        return 0L;
+
+    // Keep sample position
+    // FIXME: asioDriverInfo.pahsc_NumFramesDone = timeInfo->timeInfo.samplePosition.lo;
+
+
+    // protect against reentrancy
+    if( PaAsio_AtomicIncrement(&theAsioStream->reenterCount) )
+    {
+        theAsioStream->reenterError++;
+        //DBUG(("bufferSwitchTimeInfo : reentrancy detection = %d\n", asioDriverInfo.reenterError));
+        return 0L;
+    }
+
+    int buffersDone = 0;
+    
+    do
+    {
+        if( buffersDone > 0 )
+        {
+            // this is a reentered buffer, we missed processing it on time
+            // set the input overflow and output underflow flags as appropriate
+            
+            if( theAsioStream->inputChannelCount > 0 )
+                theAsioStream->callbackFlags |= paInputOverflow;
+                
+            if( theAsioStream->outputChannelCount > 0 )
+                theAsioStream->callbackFlags |= paOutputUnderflow;
+        }
+        else
+        {
+            if( theAsioStream->zeroOutput )
+            {
+                ZeroOutputBuffers( theAsioStream, index );
+
+                // Finally if the driver supports the ASIOOutputReady() optimization,
+                // do it here, all data are in place
+                if( theAsioStream->postOutput )
+                    ASIOOutputReady();
+
+                if( theAsioStream->stopProcessing )
+                {
+                    if( theAsioStream->stopPlayoutCount < 2 )
+                    {
+                        ++theAsioStream->stopPlayoutCount;
+                        if( theAsioStream->stopPlayoutCount == 2 )
+                        {
+                            theAsioStream->isActive = 0;
+                            if( theAsioStream->streamRepresentation.streamFinishedCallback != 0 )
+                                theAsioStream->streamRepresentation.streamFinishedCallback( theAsioStream->streamRepresentation.userData );
+                            theAsioStream->streamFinishedCallbackCalled = true;
+                            SetEvent( theAsioStream->completedBuffersPlayedEvent );
+                        }
+                    }
+                }
+            }
+            else
+            {
+
+#if 0
+// test code to try to detect slip conditions... these may work on some systems
+// but neither of them work on the RME Digi96
+
+// check that sample delta matches buffer size (otherwise we must have skipped
+// a buffer.
+static double last_samples = -512;
+double samples;
+//if( timeInfo->timeCode.flags & kTcValid )
+//    samples = ASIO64toDouble(timeInfo->timeCode.timeCodeSamples);
+//else
+    samples = ASIO64toDouble(timeInfo->timeInfo.samplePosition);
+int delta = samples - last_samples;
+//printf( "%d\n", delta);
+last_samples = samples;
+
+if( delta > theAsioStream->framesPerHostCallback )
+{
+    if( theAsioStream->inputChannelCount > 0 )
+        theAsioStream->callbackFlags |= paInputOverflow;
+
+    if( theAsioStream->outputChannelCount > 0 )
+        theAsioStream->callbackFlags |= paOutputUnderflow;
+}
+
+// check that the buffer index is not the previous index (which would indicate
+// that a buffer was skipped.
+static int previousIndex = 1;
+if( index == previousIndex )
+{
+    if( theAsioStream->inputChannelCount > 0 )
+        theAsioStream->callbackFlags |= paInputOverflow;
+
+    if( theAsioStream->outputChannelCount > 0 )
+        theAsioStream->callbackFlags |= paOutputUnderflow;
+}
+previousIndex = index;
+#endif
+
+                int i;
+
+                PaUtil_BeginCpuLoadMeasurement( &theAsioStream->cpuLoadMeasurer );
+
+                PaStreamCallbackTimeInfo paTimeInfo;
+
+                // asio systemTime is supposed to be measured according to the same
+                // clock as timeGetTime
+                paTimeInfo.currentTime = (ASIO64toDouble( timeInfo->timeInfo.systemTime ) * .000000001);
+
+                /* patch from Paul Boege */
+                paTimeInfo.inputBufferAdcTime = paTimeInfo.currentTime -
+                    ((double)theAsioStream->inputLatency/theAsioStream->streamRepresentation.streamInfo.sampleRate);
+
+                paTimeInfo.outputBufferDacTime = paTimeInfo.currentTime +
+                    ((double)theAsioStream->outputLatency/theAsioStream->streamRepresentation.streamInfo.sampleRate);
+
+                /* old version is buggy because the buffer processor also adds in its latency to the time parameters
+                paTimeInfo.inputBufferAdcTime = paTimeInfo.currentTime - theAsioStream->streamRepresentation.streamInfo.inputLatency;
+                paTimeInfo.outputBufferDacTime = paTimeInfo.currentTime + theAsioStream->streamRepresentation.streamInfo.outputLatency;
+                */
+#if 1
+// detect underflows by checking inter-callback time > 2 buffer period
+static double previousTime = -1;
+if( previousTime > 0 ){
+
+    double delta = paTimeInfo.currentTime - previousTime;
+
+    if( delta >= 2. * (theAsioStream->framesPerHostCallback / theAsioStream->streamRepresentation.streamInfo.sampleRate) ){
+        if( theAsioStream->inputChannelCount > 0 )
+            theAsioStream->callbackFlags |= paInputOverflow;
+
+        if( theAsioStream->outputChannelCount > 0 )
+            theAsioStream->callbackFlags |= paOutputUnderflow;
+    }
+}
+previousTime = paTimeInfo.currentTime;
+#endif
+
+                // note that the above input and output times do not need to be
+                // adjusted for the latency of the buffer processor -- the buffer
+                // processor handles that.
+
+                if( theAsioStream->inputBufferConverter )
+                {
+                    for( i=0; i<theAsioStream->inputChannelCount; i++ )
+                    {
+                        theAsioStream->inputBufferConverter( theAsioStream->inputBufferPtrs[index][i],
+                                theAsioStream->inputShift, theAsioStream->framesPerHostCallback );
+                    }
+                }
+
+                PaUtil_BeginBufferProcessing( &theAsioStream->bufferProcessor, &paTimeInfo, theAsioStream->callbackFlags );
+
+                /* reset status flags once they've been passed to the callback */
+                theAsioStream->callbackFlags = 0;
+
+                PaUtil_SetInputFrameCount( &theAsioStream->bufferProcessor, 0 /* default to host buffer size */ );
+                for( i=0; i<theAsioStream->inputChannelCount; ++i )
+                    PaUtil_SetNonInterleavedInputChannel( &theAsioStream->bufferProcessor, i, theAsioStream->inputBufferPtrs[index][i] );
+
+                PaUtil_SetOutputFrameCount( &theAsioStream->bufferProcessor, 0 /* default to host buffer size */ );
+                for( i=0; i<theAsioStream->outputChannelCount; ++i )
+                    PaUtil_SetNonInterleavedOutputChannel( &theAsioStream->bufferProcessor, i, theAsioStream->outputBufferPtrs[index][i] );
+
+                int callbackResult;
+                if( theAsioStream->stopProcessing )
+                    callbackResult = paComplete;
+                else
+                    callbackResult = paContinue;
+                unsigned long framesProcessed = PaUtil_EndBufferProcessing( &theAsioStream->bufferProcessor, &callbackResult );
+
+                if( theAsioStream->outputBufferConverter )
+                {
+                    for( i=0; i<theAsioStream->outputChannelCount; i++ )
+                    {
+                        theAsioStream->outputBufferConverter( theAsioStream->outputBufferPtrs[index][i],
+                                theAsioStream->outputShift, theAsioStream->framesPerHostCallback );
+                    }
+                }
+
+                PaUtil_EndCpuLoadMeasurement( &theAsioStream->cpuLoadMeasurer, framesProcessed );
+
+                // Finally if the driver supports the ASIOOutputReady() optimization,
+                // do it here, all data are in place
+                if( theAsioStream->postOutput )
+                    ASIOOutputReady();
+
+                if( callbackResult == paContinue )
+                {
+                    /* nothing special to do */
+                }
+                else if( callbackResult == paAbort )
+                {
+                    /* finish playback immediately  */
+                    theAsioStream->isActive = 0;
+                    if( theAsioStream->streamRepresentation.streamFinishedCallback != 0 )
+                        theAsioStream->streamRepresentation.streamFinishedCallback( theAsioStream->streamRepresentation.userData );
+                    theAsioStream->streamFinishedCallbackCalled = true;
+                    SetEvent( theAsioStream->completedBuffersPlayedEvent );
+                    theAsioStream->zeroOutput = true;
+                }
+                else /* paComplete or other non-zero value indicating complete */
+                {
+                    /* Finish playback once currently queued audio has completed. */
+                    theAsioStream->stopProcessing = true;
+
+                    if( PaUtil_IsBufferProcessorOutputEmpty( &theAsioStream->bufferProcessor ) )
+                    {
+                        theAsioStream->zeroOutput = true;
+                        theAsioStream->stopPlayoutCount = 0;
+                    }
+                }
+            }
+        }
+        
+        ++buffersDone;
+    }while( PaAsio_AtomicDecrement(&theAsioStream->reenterCount) >= 0 );
+
+    return 0L;
+}
+
+
+static void sampleRateChanged(ASIOSampleRate sRate)
+{
+    // TAKEN FROM THE ASIO SDK
+    // 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.
+
+    (void) sRate; /* unused parameter */
+    PA_DEBUG( ("sampleRateChanged : %d \n", sRate));
+}
+
+static long asioMessages(long selector, long value, void* message, double* opt)
+{
+// TAKEN FROM THE ASIO SDK
+    // currently the parameters "value", "message" and "opt" are not used.
+    long ret = 0;
+
+    (void) message; /* unused parameters */
+    (void) opt;
+
+    PA_DEBUG( ("asioMessages : %d , %d \n", selector, value));
+
+    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.
+
+            /*FIXME: commented the next line out */
+            //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;
+}
+
+
+static PaError StartStream( PaStream *s )
+{
+    PaError result = paNoError;
+    PaAsioStream *stream = (PaAsioStream*)s;
+    ASIOError asioError;
+
+    if( stream->outputChannelCount > 0 )
+    {
+        ZeroOutputBuffers( stream, 0 );
+        ZeroOutputBuffers( stream, 1 );
+    }
+
+    PaUtil_ResetBufferProcessor( &stream->bufferProcessor );
+    stream->stopProcessing = false;
+    stream->zeroOutput = false;
+
+    /* Reentrancy counter initialisation */
+    stream->reenterCount = -1;
+    stream->reenterError = 0;
+
+    stream->callbackFlags = 0;
+
+    if( ResetEvent( stream->completedBuffersPlayedEvent ) == 0 )
+    {
+        result = paUnanticipatedHostError;
+        PA_ASIO_SET_LAST_SYSTEM_ERROR( GetLastError() );
+    }
+
+    if( result == paNoError )
+    {
+        theAsioStream = stream;
+        asioError = ASIOStart();
+        if( asioError == ASE_OK )
+        {
+            stream->isActive = 1;
+            stream->streamFinishedCallbackCalled = false;
+        }
+        else
+        {
+            theAsioStream = 0;
+            result = paUnanticipatedHostError;
+            PA_ASIO_SET_LAST_ASIO_ERROR( asioError );
+        }
+    }
+
+    return result;
+}
+
+
+static PaError StopStream( PaStream *s )
+{
+    PaError result = paNoError;
+    PaAsioStream *stream = (PaAsioStream*)s;
+    ASIOError asioError;
+
+    if( stream->isActive )
+    {
+        stream->stopProcessing = true;
+
+        /* wait for the stream to finish playing out enqueued buffers.
+            timeout after four times the stream latency.
+
+            @todo should use a better time out value - if the user buffer
+            length is longer than the asio buffer size then that should
+            be taken into account.
+        */
+        if( WaitForSingleObject( theAsioStream->completedBuffersPlayedEvent,
+                (DWORD)(stream->streamRepresentation.streamInfo.outputLatency * 1000. * 4.) )
+                    == WAIT_TIMEOUT	 )
+        {
+            PA_DEBUG(("WaitForSingleObject() timed out in StopStream()\n" ));
+        }
+    }
+
+    asioError = ASIOStop();
+    if( asioError != ASE_OK )
+    {
+        result = paUnanticipatedHostError;
+        PA_ASIO_SET_LAST_ASIO_ERROR( asioError );
+    }
+
+    theAsioStream = 0;
+    stream->isActive = 0;
+
+    if( !stream->streamFinishedCallbackCalled )
+    {
+        if( stream->streamRepresentation.streamFinishedCallback != 0 )
+            stream->streamRepresentation.streamFinishedCallback( stream->streamRepresentation.userData );
+    }
+
+    return result;
+}
+
+
+static PaError AbortStream( PaStream *s )
+{
+    PaError result = paNoError;
+    PaAsioStream *stream = (PaAsioStream*)s;
+    ASIOError asioError;
+
+    stream->zeroOutput = true;
+
+    asioError = ASIOStop();
+    if( asioError != ASE_OK )
+    {
+        result = paUnanticipatedHostError;
+        PA_ASIO_SET_LAST_ASIO_ERROR( asioError );
+    }
+    else
+    {
+        // make sure that the callback is not still in-flight when ASIOStop()
+        // returns. This has been observed to happen on the Hoontech DSP24 for
+        // example.
+        int count = 2000;  // only wait for 2 seconds, rather than hanging.
+        while( theAsioStream->reenterCount != -1 && count > 0 )
+        {
+            Sleep(1);
+            --count;
+        }
+    }
+
+    /* it is questionable whether we should zero theAsioStream if ASIOStop()
+        returns an error, because the callback could still be active. We assume
+        not - this is based on the fact that ASIOStop is unlikely to fail
+        if the callback is running - it's more likely to fail because the
+        callback is not running. */
+        
+    theAsioStream = 0;
+    stream->isActive = 0;
+
+    if( !stream->streamFinishedCallbackCalled )
+    {
+        if( stream->streamRepresentation.streamFinishedCallback != 0 )
+            stream->streamRepresentation.streamFinishedCallback( stream->streamRepresentation.userData );
+    }
+
+    return result;
+}
+
+
+static PaError IsStreamStopped( PaStream *s )
+{
+    //PaAsioStream *stream = (PaAsioStream*)s;
+    (void) s; /* unused parameter */
+    return theAsioStream == 0;
+}
+
+
+static PaError IsStreamActive( PaStream *s )
+{
+    PaAsioStream *stream = (PaAsioStream*)s;
+
+    return stream->isActive;
+}
+
+
+static PaTime GetStreamTime( PaStream *s )
+{
+    (void) s; /* unused parameter */
+    return (double)timeGetTime() * .001;
+}
+
+
+static double GetStreamCpuLoad( PaStream* s )
+{
+    PaAsioStream *stream = (PaAsioStream*)s;
+
+    return PaUtil_GetCpuLoad( &stream->cpuLoadMeasurer );
+}
+
+
+/*
+    As separate stream interfaces are used for blocking and callback
+    streams, the following functions can be guaranteed to only be called
+    for blocking streams.
+*/
+
+static PaError ReadStream( PaStream* s,
+                           void *buffer,
+                           unsigned long frames )
+{
+    PaAsioStream *stream = (PaAsioStream*)s;
+
+    /* IMPLEMENT ME, see portaudio.h for required behavior*/
+    (void) stream; /* unused parameters */
+    (void) buffer;
+    (void) frames;
+
+    return paNoError;
+}
+
+
+static PaError WriteStream( PaStream* s,
+                            const void *buffer,
+                            unsigned long frames )
+{
+    PaAsioStream *stream = (PaAsioStream*)s;
+
+    /* IMPLEMENT ME, see portaudio.h for required behavior*/
+    (void) stream; /* unused parameters */
+    (void) buffer;
+    (void) frames;
+
+    return paNoError;
+}
+
+
+static signed long GetStreamReadAvailable( PaStream* s )
+{
+    PaAsioStream *stream = (PaAsioStream*)s;
+
+    /* IMPLEMENT ME, see portaudio.h for required behavior*/
+    (void) stream; /* unused parameter */
+
+    return 0;
+}
+
+
+static signed long GetStreamWriteAvailable( PaStream* s )
+{
+    PaAsioStream *stream = (PaAsioStream*)s;
+
+    /* IMPLEMENT ME, see portaudio.h for required behavior*/
+    (void) stream; /* unused parameter */
+
+    return 0;
+}
+
+
+PaError PaAsio_ShowControlPanel( PaDeviceIndex device, void* systemSpecific )
+{
+	PaError result = paNoError;
+    PaUtilHostApiRepresentation *hostApi;
+    PaDeviceIndex hostApiDevice;
+    ASIODriverInfo asioDriverInfo;
+	ASIOError asioError;
+    int asioIsInitialized = 0;
+    PaAsioHostApiRepresentation *asioHostApi;
+    PaAsioDeviceInfo *asioDeviceInfo;
+
+
+    result = PaUtil_GetHostApiRepresentation( &hostApi, paASIO );
+    if( result != paNoError )
+        goto error;
+
+    result = PaUtil_DeviceIndexToHostApiDeviceIndex( &hostApiDevice, device, hostApi );
+    if( result != paNoError )
+        goto error;
+
+    /*
+        In theory we could proceed if the currently open device was the same
+        one for which the control panel was requested, however  because the
+        window pointer is not available until this function is called we
+        currently need to call ASIOInit() again here, which of course can't be
+        done safely while a stream is open.
+    */
+
+    asioHostApi = (PaAsioHostApiRepresentation*)hostApi;
+    if( asioHostApi->openAsioDeviceIndex != paNoDevice )
+    {
+        result = paDeviceUnavailable;
+        goto error;
+    }
+
+    asioDeviceInfo = (PaAsioDeviceInfo*)hostApi->deviceInfos[hostApiDevice];
+
+    if( !loadAsioDriver( const_cast<char*>(asioDeviceInfo->commonDeviceInfo.name) ) )
+    {
+        result = paUnanticipatedHostError;
+        goto error;
+    }
+
+    /* CRUCIAL!!! */
+    memset( &asioDriverInfo, 0, sizeof(ASIODriverInfo) );
+    asioDriverInfo.asioVersion = 2;
+    asioDriverInfo.sysRef = systemSpecific;
+    asioError = ASIOInit( &asioDriverInfo );
+    if( asioError != ASE_OK )
+    {
+        result = paUnanticipatedHostError;
+        PA_ASIO_SET_LAST_ASIO_ERROR( asioError );
+        goto error;
+    }
+    else
+    {
+        asioIsInitialized = 1;
+    }
+
+PA_DEBUG(("PaAsio_ShowControlPanel: ASIOInit(): %s\n", PaAsio_GetAsioErrorText(asioError) ));
+PA_DEBUG(("asioVersion: ASIOInit(): %ld\n",   asioDriverInfo.asioVersion )); 
+PA_DEBUG(("driverVersion: ASIOInit(): %ld\n", asioDriverInfo.driverVersion )); 
+PA_DEBUG(("Name: ASIOInit(): %s\n",           asioDriverInfo.name )); 
+PA_DEBUG(("ErrorMessage: ASIOInit(): %s\n",   asioDriverInfo.errorMessage )); 
+
+    asioError = ASIOControlPanel();
+    if( asioError != ASE_OK )
+    {
+        PA_DEBUG(("PaAsio_ShowControlPanel: ASIOControlPanel(): %s\n", PaAsio_GetAsioErrorText(asioError) ));
+        result = paUnanticipatedHostError;
+        PA_ASIO_SET_LAST_ASIO_ERROR( asioError );
+        goto error;
+    }
+
+PA_DEBUG(("PaAsio_ShowControlPanel: ASIOControlPanel(): %s\n", PaAsio_GetAsioErrorText(asioError) ));
+
+    asioError = ASIOExit();
+    if( asioError != ASE_OK )
+    {
+        result = paUnanticipatedHostError;
+        PA_ASIO_SET_LAST_ASIO_ERROR( asioError );
+        asioIsInitialized = 0;
+        goto error;
+    }
+
+PA_DEBUG(("PaAsio_ShowControlPanel: ASIOExit(): %s\n", PaAsio_GetAsioErrorText(asioError) ));
+
+	return result;
+
+error:
+    if( asioIsInitialized )
+        ASIOExit();
+
+	return result;
+}
+
+
+PaError PaAsio_GetInputChannelName( PaDeviceIndex device, int channelIndex,
+        const char** channelName )
+{
+    PaError result = paNoError;
+    PaUtilHostApiRepresentation *hostApi;
+    PaDeviceIndex hostApiDevice;
+    PaAsioDeviceInfo *asioDeviceInfo;
+
+
+    result = PaUtil_GetHostApiRepresentation( &hostApi, paASIO );
+    if( result != paNoError )
+        goto error;
+
+    result = PaUtil_DeviceIndexToHostApiDeviceIndex( &hostApiDevice, device, hostApi );
+    if( result != paNoError )
+        goto error;
+
+    asioDeviceInfo = (PaAsioDeviceInfo*)hostApi->deviceInfos[hostApiDevice];
+
+    if( channelIndex < 0 || channelIndex >= asioDeviceInfo->commonDeviceInfo.maxInputChannels ){
+        result = paInvalidChannelCount;
+        goto error;
+    }
+
+    *channelName = asioDeviceInfo->asioChannelInfos[channelIndex].name;
+
+    return paNoError;
+    
+error:
+    return result;
+}
+
+
+PaError PaAsio_GetOutputChannelName( PaDeviceIndex device, int channelIndex,
+        const char** channelName )
+{
+    PaError result = paNoError;
+    PaUtilHostApiRepresentation *hostApi;
+    PaDeviceIndex hostApiDevice;
+    PaAsioDeviceInfo *asioDeviceInfo;
+
+
+    result = PaUtil_GetHostApiRepresentation( &hostApi, paASIO );
+    if( result != paNoError )
+        goto error;
+
+    result = PaUtil_DeviceIndexToHostApiDeviceIndex( &hostApiDevice, device, hostApi );
+    if( result != paNoError )
+        goto error;
+
+    asioDeviceInfo = (PaAsioDeviceInfo*)hostApi->deviceInfos[hostApiDevice];
+
+    if( channelIndex < 0 || channelIndex >= asioDeviceInfo->commonDeviceInfo.maxOutputChannels ){
+        result = paInvalidChannelCount;
+        goto error;
+    }
+
+    *channelName = asioDeviceInfo->asioChannelInfos[
+            asioDeviceInfo->commonDeviceInfo.maxInputChannels + channelIndex].name;
+
+    return paNoError;
+    
+error:
+    return result;
+}
diff --git a/src/audio/portaudio/pa_asio/pa_asio.h b/src/audio/portaudio/pa_asio/pa_asio.h
new file mode 100644
index 0000000000000000000000000000000000000000..573930572423c5b08dd942ba87fc968664569312
--- /dev/null
+++ b/src/audio/portaudio/pa_asio/pa_asio.h
@@ -0,0 +1,122 @@
+#ifndef PA_ASIO_H
+#define PA_ASIO_H
+/*
+ * $Id$
+ * PortAudio Portable Real-Time Audio Library
+ * ASIO specific extensions
+ *
+ * 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.
+ *
+ */
+
+/** @file
+ @brief ASIO-specific PortAudio API extension header file.
+*/
+
+
+#include "portaudio.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif /* __cplusplus */
+
+
+/** Retrieve legal latency settings for the specificed device, in samples.
+
+ @param device The global index of the device about which the query is being made.
+ @param minLatency A pointer to the location which will recieve the minimum latency value.
+ @param maxLatency A pointer to the location which will recieve the maximum latency value.
+ @param preferredLatency A pointer to the location which will recieve the preferred latency value.
+ @param granularity A pointer to the location which will recieve the granularity. This value 
+ determines which values between minLatency and maxLatency are available. ie the step size,
+ if granularity is -1 then available latency settings are powers of two.
+
+ @see ASIOGetBufferSize in the ASIO SDK.
+
+ @todo This function should have a better name, any suggestions?
+*/
+PaError PaAsio_GetAvailableLatencyValues( PaDeviceIndex device,
+		long *minLatency, long *maxLatency, long *preferredLatency, long *granularity );
+
+        
+/** Display the ASIO control panel for the specified device.
+
+  @param device The global index of the device whose control panel is to be displayed.
+  @param systemSpecific On Windows, the calling application's main window handle,
+  on Macintosh this value should be zero.
+*/
+PaError PaAsio_ShowControlPanel( PaDeviceIndex device, void* systemSpecific );
+
+
+
+
+/** Retrieve a pointer to a string containing the name of the specified
+ input channel. The string is valid until Pa_Terminate is called.
+
+ The string will be no longer than 32 characters including the null terminator.
+*/
+PaError PaAsio_GetInputChannelName( PaDeviceIndex device, int channelIndex,
+        const char** channelName );
+
+        
+/** Retrieve a pointer to a string containing the name of the specified
+ input channel. The string is valid until Pa_Terminate is called.
+
+ The string will be no longer than 32 characters including the null terminator.
+*/
+PaError PaAsio_GetOutputChannelName( PaDeviceIndex device, int channelIndex,
+        const char** channelName );
+
+
+#define paAsioUseChannelSelectors      (0x01)
+
+typedef struct PaAsioStreamInfo{
+    unsigned long size;             /**< sizeof(PaAsioStreamInfo) */
+    PaHostApiTypeId hostApiType;    /**< paASIO */
+    unsigned long version;          /**< 1 */
+
+    unsigned long flags;
+
+    /* Support for opening only specific channels of an ASIO device.
+        If the paAsioUseChannelSelectors flag is set, channelSelectors is a
+        pointer to an array of integers specifying the device channels to use.
+        When used, the length of the channelSelectors array must match the
+        corresponding channelCount parameter to Pa_OpenStream() otherwise a
+        crash may result.
+        The values in the selectors array must specify channels within the
+        range of supported channels for the device or paInvalidChannelCount will
+        result.
+    */
+    int *channelSelectors;
+}PaAsioStreamInfo;
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* PA_ASIO_H */
diff --git a/src/audio/portaudio/pa_beos/PlaybackNode.cc b/src/audio/portaudio/pa_beos/PlaybackNode.cc
new file mode 100644
index 0000000000000000000000000000000000000000..84d54534140620c162874a5fd452ae9eab08020c
--- /dev/null
+++ b/src/audio/portaudio/pa_beos/PlaybackNode.cc
@@ -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/src/audio/portaudio/pa_beos/PlaybackNode.h b/src/audio/portaudio/pa_beos/PlaybackNode.h
new file mode 100644
index 0000000000000000000000000000000000000000..64ee5b3bcb21090eb39d351a206365d13ff67989
--- /dev/null
+++ b/src/audio/portaudio/pa_beos/PlaybackNode.h
@@ -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/src/audio/portaudio/pa_beos/pa_beos_mk.cc b/src/audio/portaudio/pa_beos/pa_beos_mk.cc
new file mode 100644
index 0000000000000000000000000000000000000000..287ec5535cd6411d809656021eeb8db2f9be4bd7
--- /dev/null
+++ b/src/audio/portaudio/pa_beos/pa_beos_mk.cc
@@ -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/src/audio/portaudio/pa_common/pa_allocation.c b/src/audio/portaudio/pa_common/pa_allocation.c
new file mode 100644
index 0000000000000000000000000000000000000000..98239c48f00400932a826763f1b87c39cacabc0a
--- /dev/null
+++ b/src/audio/portaudio/pa_common/pa_allocation.c
@@ -0,0 +1,234 @@
+/*
+ * $Id$
+ * Portable Audio I/O Library allocation group implementation
+ * memory allocation group for tracking allocation groups
+ *
+ * Based on the Open Source API proposed by Ross Bencina
+ * Copyright (c) 1999-2002 Ross Bencina, 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.
+ */
+
+/** @file
+ @brief Allocation Group implementation.
+*/
+
+
+#include "pa_allocation.h"
+#include "pa_util.h"
+
+
+/*
+    Maintain 3 singly linked lists...
+    linkBlocks: the buffers used to allocate the links
+    spareLinks: links available for use in the allocations list
+    allocations: the buffers currently allocated using PaUtil_ContextAllocateMemory()
+
+    Link block size is doubled every time new links are allocated.
+*/
+
+
+#define PA_INITIAL_LINK_COUNT_    16
+
+struct PaUtilAllocationGroupLink
+{
+    struct PaUtilAllocationGroupLink *next;
+    void *buffer;
+};
+
+/*
+    Allocate a block of links. The first link will have it's buffer member
+    pointing to the block, and it's next member set to <nextBlock>. The remaining
+    links will have NULL buffer members, and each link will point to
+    the next link except the last, which will point to <nextSpare>
+*/
+static struct PaUtilAllocationGroupLink *AllocateLinks( long count,
+        struct PaUtilAllocationGroupLink *nextBlock,
+        struct PaUtilAllocationGroupLink *nextSpare )
+{
+    struct PaUtilAllocationGroupLink *result;
+    int i;
+    
+    result = (struct PaUtilAllocationGroupLink *)PaUtil_AllocateMemory(
+            sizeof(struct PaUtilAllocationGroupLink) * count );
+    if( result )
+    {
+        /* the block link */
+        result[0].buffer = result;
+        result[0].next = nextBlock;
+
+        /* the spare links */
+        for( i=1; i<count; ++i )
+        {
+            result[i].buffer = 0;
+            result[i].next = &result[i+1];
+        }
+        result[count-1].next = nextSpare;
+    }
+    
+    return result;
+}
+
+
+PaUtilAllocationGroup* PaUtil_CreateAllocationGroup( void )
+{
+    PaUtilAllocationGroup* result = 0;
+    struct PaUtilAllocationGroupLink *links;
+
+
+    links = AllocateLinks( PA_INITIAL_LINK_COUNT_, 0, 0 );
+    if( links != 0 )
+    {
+        result = (PaUtilAllocationGroup*)PaUtil_AllocateMemory( sizeof(PaUtilAllocationGroup) );
+        if( result )
+        {
+            result->linkCount = PA_INITIAL_LINK_COUNT_;
+            result->linkBlocks = &links[0];
+            result->spareLinks = &links[1];
+            result->allocations = 0;
+        }
+        else
+        {
+            PaUtil_FreeMemory( links );
+        }
+    }
+
+    return result;
+}
+
+
+void PaUtil_DestroyAllocationGroup( PaUtilAllocationGroup* group )
+{
+    struct PaUtilAllocationGroupLink *current = group->linkBlocks;
+    struct PaUtilAllocationGroupLink *next;
+
+    while( current )
+    {
+        next = current->next;
+        PaUtil_FreeMemory( current->buffer );
+        current = next;
+    }
+
+    PaUtil_FreeMemory( group );
+}
+
+
+void* PaUtil_GroupAllocateMemory( PaUtilAllocationGroup* group, long size )
+{
+    struct PaUtilAllocationGroupLink *links, *link;
+    void *result = 0;
+    
+    /* allocate more links if necessary */
+    if( !group->spareLinks )
+    {
+        /* double the link count on each block allocation */
+        links = AllocateLinks( group->linkCount, group->linkBlocks, group->spareLinks );
+        if( links )
+        {
+            group->linkCount += group->linkCount;
+            group->linkBlocks = &links[0];
+            group->spareLinks = &links[1];
+        }
+    }
+
+    if( group->spareLinks )
+    {
+        result = PaUtil_AllocateMemory( size );
+        if( result )
+        {
+            link = group->spareLinks;
+            group->spareLinks = link->next;
+
+            link->buffer = result;
+            link->next = group->allocations;
+
+            group->allocations = link;
+        }
+    }
+
+    return result;    
+}
+
+
+void PaUtil_GroupFreeMemory( PaUtilAllocationGroup* group, void *buffer )
+{
+    struct PaUtilAllocationGroupLink *current = group->allocations;
+    struct PaUtilAllocationGroupLink *previous = 0;
+
+    if( buffer == 0 )
+        return;
+
+    /* find the right link and remove it */
+    while( current )
+    {
+        if( current->buffer == buffer )
+        {
+            if( previous )
+            {
+                previous->next = current->next;
+            }
+            else
+            {
+                group->allocations = current->next;
+            }
+
+            current->buffer = 0;
+            current->next = group->spareLinks;
+            group->spareLinks = current;
+
+            break;
+        }
+        
+        previous = current;
+        current = current->next;
+    }
+
+    PaUtil_FreeMemory( buffer ); /* free the memory whether we found it in the list or not */
+}
+
+
+void PaUtil_FreeAllAllocations( PaUtilAllocationGroup* group )
+{
+    struct PaUtilAllocationGroupLink *current = group->allocations;
+    struct PaUtilAllocationGroupLink *previous = 0;
+
+    /* free all buffers in the allocations list */
+    while( current )
+    {
+        PaUtil_FreeMemory( current->buffer );
+        current->buffer = 0;
+
+        previous = current;
+        current = current->next;
+    }
+
+    /* link the former allocations list onto the front of the spareLinks list */
+    if( previous )
+    {
+        previous->next = group->spareLinks;
+        group->spareLinks = group->allocations;
+        group->allocations = 0;
+    }
+}
+
diff --git a/src/audio/portaudio/pa_common/pa_allocation.h b/src/audio/portaudio/pa_common/pa_allocation.h
new file mode 100644
index 0000000000000000000000000000000000000000..5bcfe75a67674da671de89962c866b6fc5c46285
--- /dev/null
+++ b/src/audio/portaudio/pa_common/pa_allocation.h
@@ -0,0 +1,95 @@
+#ifndef PA_ALLOCATION_H
+#define PA_ALLOCATION_H
+/*
+ * $Id$
+ * Portable Audio I/O Library allocation context header
+ * memory allocation context for tracking allocation groups
+ *
+ * Based on the Open Source API proposed by Ross Bencina
+ * Copyright (c) 1999-2002 Ross Bencina, 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.
+ */
+
+/** @file
+ @brief Allocation Group prototypes. An Allocation Group makes it easy to
+ allocate multiple blocks of memory and free them all simultanously.
+ 
+ An allocation group is useful for keeping track of multiple blocks
+ of memory which are allocated at the same time (such as during initialization)
+ and need to be deallocated at the same time. The allocation group maintains
+ a list of allocated blocks, and can deallocate them all simultaneously which
+ can be usefull for cleaning up after a partially initialized object fails.
+
+ The allocation group implementation is built on top of the lower
+ level allocation functions defined in pa_util.h
+*/
+
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif /* __cplusplus */
+
+
+typedef struct
+{
+    long linkCount;
+    struct PaUtilAllocationGroupLink *linkBlocks;
+    struct PaUtilAllocationGroupLink *spareLinks;
+    struct PaUtilAllocationGroupLink *allocations;
+}PaUtilAllocationGroup;
+
+
+
+/** Create an allocation group.
+*/
+PaUtilAllocationGroup* PaUtil_CreateAllocationGroup( void );
+
+/** Destroy an allocation group, but not the memory allocated through the group.
+*/
+void PaUtil_DestroyAllocationGroup( PaUtilAllocationGroup* group );
+
+/** Allocate a block of memory though an allocation group.
+*/
+void* PaUtil_GroupAllocateMemory( PaUtilAllocationGroup* group, long size );
+
+/** Free a block of memory that was previously allocated though an allocation
+ group. Calling this function is a relatively time consuming operation.
+ Under normal circumstances clients should call PaUtil_FreeAllAllocations to
+ free all allocated blocks simultaneously.
+ @see PaUtil_FreeAllAllocations
+*/
+void PaUtil_GroupFreeMemory( PaUtilAllocationGroup* group, void *buffer );
+
+/** Free all blocks of memory which have been allocated through the allocation
+ group. This function doesn't destroy the group itself.
+*/
+void PaUtil_FreeAllAllocations( PaUtilAllocationGroup* group );
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif /* PA_ALLOCATION_H */
diff --git a/src/audio/portaudio/pa_common/pa_converters.c b/src/audio/portaudio/pa_common/pa_converters.c
new file mode 100644
index 0000000000000000000000000000000000000000..501be4eba87566ecf73e399b129ad0a7cf59eca5
--- /dev/null
+++ b/src/audio/portaudio/pa_common/pa_converters.c
@@ -0,0 +1,1926 @@
+/*
+ * $Id$
+ * Portable Audio I/O Library sample conversion mechanism
+ *
+ * Based on the Open Source API proposed by Ross Bencina
+ * Copyright (c) 1999-2002 Phil Burk, Ross Bencina
+ *
+ * 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.
+ */
+
+/** @file
+ @brief Conversion functions implementations.
+ 
+ If the C9x function lrintf() is available, define PA_USE_C99_LRINTF to use it
+
+ @todo Consider whether functions which dither but don't clip should exist,
+ V18 automatically enabled clipping whenever dithering was selected. Perhaps
+ we should do the same.
+
+ @todo implement the converters marked IMPLEMENT ME: Float32_To_UInt8_Dither,
+ Float32_To_UInt8_Clip, Float32_To_UInt8_DitherClip, Int32_To_Int24_Dither,
+ Int32_To_UInt8_Dither, Int24_To_Int16_Dither, Int24_To_Int8_Dither, 
+ Int24_To_UInt8_Dither, Int16_To_Int8_Dither, Int16_To_UInt8_Dither,
+
+ @todo review the converters marked REVIEW: Float32_To_Int32,
+ Float32_To_Int32_Dither, Float32_To_Int32_Clip, Float32_To_Int32_DitherClip,
+ Int32_To_Int16_Dither, Int32_To_Int8_Dither, Int16_To_Int32
+*/
+
+
+#include "pa_converters.h"
+#include "pa_dither.h"
+#include "pa_endianness.h"
+#include "pa_types.h"
+
+
+PaSampleFormat PaUtil_SelectClosestAvailableFormat(
+        PaSampleFormat availableFormats, PaSampleFormat format )
+{
+    PaSampleFormat result;
+
+    format &= ~paNonInterleaved;
+    availableFormats &= ~paNonInterleaved;
+    
+    if( (format & availableFormats) == 0 )
+    {
+        /* NOTE: this code depends on the sample format constants being in
+            descending order of quality - ie best quality is 0
+            FIXME: should write an assert which checks that all of the
+            known constants conform to that requirement.
+        */
+
+        if( format != 0x01 )
+        {
+            /* scan for better formats */
+            result = format;
+            do
+            {
+                result >>= 1;
+            }
+            while( (result & availableFormats) == 0 && result != 0 );
+        }
+        else
+        {
+            result = 0;
+        }
+        
+        if( result == 0 ){
+            /* scan for worse formats */
+            result = format;
+            do
+            {
+                result <<= 1;
+            }
+            while( (result & availableFormats) == 0 && result != paCustomFormat );
+
+            if( (result & availableFormats) == 0 )
+                result = paSampleFormatNotSupported;
+        }
+        
+    }else{
+        result = format;
+    }
+
+    return result;
+}
+
+/* -------------------------------------------------------------------------- */
+
+#define PA_SELECT_FORMAT_( format, float32, int32, int24, int16, int8, uint8 ) \
+    switch( format & ~paNonInterleaved ){                                      \
+    case paFloat32:                                                            \
+        float32                                                                \
+    case paInt32:                                                              \
+        int32                                                                  \
+    case paInt24:                                                              \
+        int24                                                                  \
+    case paInt16:                                                              \
+        int16                                                                  \
+    case paInt8:                                                               \
+        int8                                                                   \
+    case paUInt8:                                                              \
+        uint8                                                                  \
+    default: return 0;                                                         \
+    }
+
+/* -------------------------------------------------------------------------- */
+
+#define PA_SELECT_CONVERTER_DITHER_CLIP_( flags, source, destination )         \
+    if( flags & paClipOff ){ /* no clip */                                     \
+        if( flags & paDitherOff ){ /* no dither */                             \
+            return paConverters. source ## _To_ ## destination;                \
+        }else{ /* dither */                                                    \
+            return paConverters. source ## _To_ ## destination ## _Dither;     \
+        }                                                                      \
+    }else{ /* clip */                                                          \
+        if( flags & paDitherOff ){ /* no dither */                             \
+            return paConverters. source ## _To_ ## destination ## _Clip;       \
+        }else{ /* dither */                                                    \
+            return paConverters. source ## _To_ ## destination ## _DitherClip; \
+        }                                                                      \
+    }
+
+/* -------------------------------------------------------------------------- */
+
+#define PA_SELECT_CONVERTER_DITHER_( flags, source, destination )              \
+    if( flags & paDitherOff ){ /* no dither */                                 \
+        return paConverters. source ## _To_ ## destination;                    \
+    }else{ /* dither */                                                        \
+        return paConverters. source ## _To_ ## destination ## _Dither;         \
+    }
+
+/* -------------------------------------------------------------------------- */
+
+#define PA_USE_CONVERTER_( source, destination )\
+    return paConverters. source ## _To_ ## destination;
+
+/* -------------------------------------------------------------------------- */
+
+#define PA_UNITY_CONVERSION_( wordlength )\
+    return paConverters. Copy_ ## wordlength ## _To_ ## wordlength;
+
+/* -------------------------------------------------------------------------- */
+
+PaUtilConverter* PaUtil_SelectConverter( PaSampleFormat sourceFormat,
+        PaSampleFormat destinationFormat, PaStreamFlags flags )
+{
+    PA_SELECT_FORMAT_( sourceFormat,
+                       /* paFloat32: */
+                       PA_SELECT_FORMAT_( destinationFormat,
+                                          /* paFloat32: */        PA_UNITY_CONVERSION_( 32 ),
+                                          /* paInt32: */          PA_SELECT_CONVERTER_DITHER_CLIP_( flags, Float32, Int32 ),
+                                          /* paInt24: */          PA_SELECT_CONVERTER_DITHER_CLIP_( flags, Float32, Int24 ),
+                                          /* paInt16: */          PA_SELECT_CONVERTER_DITHER_CLIP_( flags, Float32, Int16 ),
+                                          /* paInt8: */           PA_SELECT_CONVERTER_DITHER_CLIP_( flags, Float32, Int8 ),
+                                          /* paUInt8: */          PA_SELECT_CONVERTER_DITHER_CLIP_( flags, Float32, UInt8 )
+                                        ),
+                       /* paInt32: */
+                       PA_SELECT_FORMAT_( destinationFormat,
+                                          /* paFloat32: */        PA_USE_CONVERTER_( Int32, Float32 ),
+                                          /* paInt32: */          PA_UNITY_CONVERSION_( 32 ),
+                                          /* paInt24: */          PA_SELECT_CONVERTER_DITHER_( flags, Int32, Int24 ),
+                                          /* paInt16: */          PA_SELECT_CONVERTER_DITHER_( flags, Int32, Int16 ),
+                                          /* paInt8: */           PA_SELECT_CONVERTER_DITHER_( flags, Int32, Int8 ),
+                                          /* paUInt8: */          PA_SELECT_CONVERTER_DITHER_( flags, Int32, UInt8 )
+                                        ),
+                       /* paInt24: */
+                       PA_SELECT_FORMAT_( destinationFormat,
+                                          /* paFloat32: */        PA_USE_CONVERTER_( Int24, Float32 ),
+                                          /* paInt32: */          PA_USE_CONVERTER_( Int24, Int32 ),
+                                          /* paInt24: */          PA_UNITY_CONVERSION_( 24 ),
+                                          /* paInt16: */          PA_SELECT_CONVERTER_DITHER_( flags, Int24, Int16 ),
+                                          /* paInt8: */           PA_SELECT_CONVERTER_DITHER_( flags, Int24, Int8 ),
+                                          /* paUInt8: */          PA_SELECT_CONVERTER_DITHER_( flags, Int24, UInt8 )
+                                        ),
+                       /* paInt16: */
+                       PA_SELECT_FORMAT_( destinationFormat,
+                                          /* paFloat32: */        PA_USE_CONVERTER_( Int16, Float32 ),
+                                          /* paInt32: */          PA_USE_CONVERTER_( Int16, Int32 ),
+                                          /* paInt24: */          PA_USE_CONVERTER_( Int16, Int24 ),
+                                          /* paInt16: */          PA_UNITY_CONVERSION_( 16 ),
+                                          /* paInt8: */           PA_SELECT_CONVERTER_DITHER_( flags, Int16, Int8 ),
+                                          /* paUInt8: */          PA_SELECT_CONVERTER_DITHER_( flags, Int16, UInt8 )
+                                        ),
+                       /* paInt8: */
+                       PA_SELECT_FORMAT_( destinationFormat,
+                                          /* paFloat32: */        PA_USE_CONVERTER_( Int8, Float32 ),
+                                          /* paInt32: */          PA_USE_CONVERTER_( Int8, Int32 ),
+                                          /* paInt24: */          PA_USE_CONVERTER_( Int8, Int24 ),
+                                          /* paInt16: */          PA_USE_CONVERTER_( Int8, Int16 ),
+                                          /* paInt8: */           PA_UNITY_CONVERSION_( 8 ),
+                                          /* paUInt8: */          PA_USE_CONVERTER_( Int8, UInt8 )
+                                        ),
+                       /* paUInt8: */
+                       PA_SELECT_FORMAT_( destinationFormat,
+                                          /* paFloat32: */        PA_USE_CONVERTER_( UInt8, Float32 ),
+                                          /* paInt32: */          PA_USE_CONVERTER_( UInt8, Int32 ),
+                                          /* paInt24: */          PA_USE_CONVERTER_( UInt8, Int24 ),
+                                          /* paInt16: */          PA_USE_CONVERTER_( UInt8, Int16 ),
+                                          /* paInt8: */           PA_USE_CONVERTER_( UInt8, Int8 ),
+                                          /* paUInt8: */          PA_UNITY_CONVERSION_( 8 )
+                                        )
+                     )
+}
+
+/* -------------------------------------------------------------------------- */
+
+#ifdef PA_NO_STANDARD_CONVERTERS
+
+/* -------------------------------------------------------------------------- */
+
+PaUtilConverterTable paConverters = {
+    0, /* PaUtilConverter *Float32_To_Int32; */
+    0, /* PaUtilConverter *Float32_To_Int32_Dither; */
+    0, /* PaUtilConverter *Float32_To_Int32_Clip; */
+    0, /* PaUtilConverter *Float32_To_Int32_DitherClip; */
+
+    0, /* PaUtilConverter *Float32_To_Int24; */
+    0, /* PaUtilConverter *Float32_To_Int24_Dither; */
+    0, /* PaUtilConverter *Float32_To_Int24_Clip; */
+    0, /* PaUtilConverter *Float32_To_Int24_DitherClip; */
+
+    0, /* PaUtilConverter *Float32_To_Int16; */
+    0, /* PaUtilConverter *Float32_To_Int16_Dither; */
+    0, /* PaUtilConverter *Float32_To_Int16_Clip; */
+    0, /* PaUtilConverter *Float32_To_Int16_DitherClip; */
+
+    0, /* PaUtilConverter *Float32_To_Int8; */
+    0, /* PaUtilConverter *Float32_To_Int8_Dither; */
+    0, /* PaUtilConverter *Float32_To_Int8_Clip; */
+    0, /* PaUtilConverter *Float32_To_Int8_DitherClip; */
+
+    0, /* PaUtilConverter *Float32_To_UInt8; */
+    0, /* PaUtilConverter *Float32_To_UInt8_Dither; */
+    0, /* PaUtilConverter *Float32_To_UInt8_Clip; */
+    0, /* PaUtilConverter *Float32_To_UInt8_DitherClip; */
+
+    0, /* PaUtilConverter *Int32_To_Float32; */
+    0, /* PaUtilConverter *Int32_To_Int24; */
+    0, /* PaUtilConverter *Int32_To_Int24_Dither; */
+    0, /* PaUtilConverter *Int32_To_Int16; */
+    0, /* PaUtilConverter *Int32_To_Int16_Dither; */
+    0, /* PaUtilConverter *Int32_To_Int8; */
+    0, /* PaUtilConverter *Int32_To_Int8_Dither; */
+    0, /* PaUtilConverter *Int32_To_UInt8; */
+    0, /* PaUtilConverter *Int32_To_UInt8_Dither; */
+
+    0, /* PaUtilConverter *Int24_To_Float32; */
+    0, /* PaUtilConverter *Int24_To_Int32; */
+    0, /* PaUtilConverter *Int24_To_Int16; */
+    0, /* PaUtilConverter *Int24_To_Int16_Dither; */
+    0, /* PaUtilConverter *Int24_To_Int8; */
+    0, /* PaUtilConverter *Int24_To_Int8_Dither; */
+    0, /* PaUtilConverter *Int24_To_UInt8; */
+    0, /* PaUtilConverter *Int24_To_UInt8_Dither; */
+    
+    0, /* PaUtilConverter *Int16_To_Float32; */
+    0, /* PaUtilConverter *Int16_To_Int32; */
+    0, /* PaUtilConverter *Int16_To_Int24; */
+    0, /* PaUtilConverter *Int16_To_Int8; */
+    0, /* PaUtilConverter *Int16_To_Int8_Dither; */
+    0, /* PaUtilConverter *Int16_To_UInt8; */
+    0, /* PaUtilConverter *Int16_To_UInt8_Dither; */
+
+    0, /* PaUtilConverter *Int8_To_Float32; */
+    0, /* PaUtilConverter *Int8_To_Int32; */
+    0, /* PaUtilConverter *Int8_To_Int24 */
+    0, /* PaUtilConverter *Int8_To_Int16; */
+    0, /* PaUtilConverter *Int8_To_UInt8; */
+
+    0, /* PaUtilConverter *UInt8_To_Float32; */
+    0, /* PaUtilConverter *UInt8_To_Int32; */
+    0, /* PaUtilConverter *UInt8_To_Int24; */
+    0, /* PaUtilConverter *UInt8_To_Int16; */
+    0, /* PaUtilConverter *UInt8_To_Int8; */
+
+    0, /* PaUtilConverter *Copy_8_To_8; */
+    0, /* PaUtilConverter *Copy_16_To_16; */
+    0, /* PaUtilConverter *Copy_24_To_24; */
+    0  /* PaUtilConverter *Copy_32_To_32; */
+};
+
+/* -------------------------------------------------------------------------- */
+
+#else /* PA_NO_STANDARD_CONVERTERS is not defined */
+
+/* -------------------------------------------------------------------------- */
+
+#define PA_CLIP_( val, min, max )\
+    { val = ((val) < (min)) ? (min) : (((val) > (max)) ? (max) : (val)); }
+
+
+static const float const_1_div_128_ = 1.0f / 128.0f;  /* 8 bit multiplier */
+
+static const float const_1_div_32768_ = 1.0f / 32768.f; /* 16 bit multiplier */
+
+static const double const_1_div_2147483648_ = 1.0 / 2147483648.0; /* 32 bit multiplier */
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_Int32(
+    void *destinationBuffer, signed int destinationStride,
+    void *sourceBuffer, signed int sourceStride,
+    unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+    float *src = (float*)sourceBuffer;
+    signed long *dest =  (signed long*)destinationBuffer;
+    (void)ditherGenerator; /* unused parameter */
+
+    while( count-- )
+    {
+        /* REVIEW */
+#ifdef PA_USE_C99_LRINTF
+        float scaled = *src * 0x7FFFFFFF;
+        *dest = lrintf(scaled-0.5f);
+#else
+        double scaled = *src * 0x7FFFFFFF;
+        *dest = (signed long) scaled;        
+#endif
+        
+        src += sourceStride;
+        dest += destinationStride;
+    }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_Int32_Dither(
+    void *destinationBuffer, signed int destinationStride,
+    void *sourceBuffer, signed int sourceStride,
+    unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+    float *src = (float*)sourceBuffer;
+    signed long *dest =  (signed long*)destinationBuffer;
+
+    while( count-- )
+    {
+        /* REVIEW */
+#ifdef PA_USE_C99_LRINTF
+        float dither  = PaUtil_GenerateFloatTriangularDither( ditherGenerator );
+        /* use smaller scaler to prevent overflow when we add the dither */
+        float dithered = ((float)*src * (2147483646.0f)) + dither;
+        *dest = lrintf(dithered - 0.5f);
+#else
+        double dither  = PaUtil_GenerateFloatTriangularDither( ditherGenerator );
+        /* use smaller scaler to prevent overflow when we add the dither */
+        double dithered = ((double)*src * (2147483646.0)) + dither;
+        *dest = (signed long) dithered;
+#endif
+        src += sourceStride;
+        dest += destinationStride;
+    }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_Int32_Clip(
+    void *destinationBuffer, signed int destinationStride,
+    void *sourceBuffer, signed int sourceStride,
+    unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+    float *src = (float*)sourceBuffer;
+    signed long *dest =  (signed long*)destinationBuffer;
+    (void) ditherGenerator; /* unused parameter */
+    
+    while( count-- )
+    {
+        /* REVIEW */
+#ifdef PA_USE_C99_LRINTF
+        float scaled = *src * 0x7FFFFFFF;
+        PA_CLIP_( scaled, -2147483648.f, 2147483647.f  );
+        *dest = lrintf(scaled-0.5f);
+#else
+        double scaled = *src * 0x7FFFFFFF;
+        PA_CLIP_( scaled, -2147483648., 2147483647.  );
+        *dest = (signed long) scaled;
+#endif
+
+        src += sourceStride;
+        dest += destinationStride;
+    }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_Int32_DitherClip(
+    void *destinationBuffer, signed int destinationStride,
+    void *sourceBuffer, signed int sourceStride,
+    unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+    float *src = (float*)sourceBuffer;
+    signed long *dest =  (signed long*)destinationBuffer;
+
+    while( count-- )
+    {
+        /* REVIEW */
+#ifdef PA_USE_C99_LRINTF
+        float dither  = PaUtil_GenerateFloatTriangularDither( ditherGenerator );
+        /* use smaller scaler to prevent overflow when we add the dither */
+        float dithered = ((float)*src * (2147483646.0f)) + dither;
+        PA_CLIP_( dithered, -2147483648.f, 2147483647.f  );
+        *dest = lrintf(dithered-0.5f);
+#else
+        double dither  = PaUtil_GenerateFloatTriangularDither( ditherGenerator );
+        /* use smaller scaler to prevent overflow when we add the dither */
+        double dithered = ((double)*src * (2147483646.0)) + dither;
+        PA_CLIP_( dithered, -2147483648., 2147483647.  );
+        *dest = (signed long) dithered;
+#endif
+
+        src += sourceStride;
+        dest += destinationStride;
+    }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_Int24(
+    void *destinationBuffer, signed int destinationStride,
+    void *sourceBuffer, signed int sourceStride,
+    unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+    float *src = (float*)sourceBuffer;
+    unsigned char *dest = (unsigned char*)destinationBuffer;
+    signed long temp;
+
+    (void) ditherGenerator; /* unused parameter */
+    
+    while( count-- )
+    {
+        /* convert to 32 bit and drop the low 8 bits */
+        double scaled = *src * 0x7FFFFFFF;
+        temp = (signed long) scaled;
+        
+#if defined(PA_LITTLE_ENDIAN)
+        dest[0] = (unsigned char)(temp >> 8);
+        dest[1] = (unsigned char)(temp >> 16);
+        dest[2] = (unsigned char)(temp >> 24);
+#elif defined(PA_BIG_ENDIAN)
+        dest[0] = (unsigned char)(temp >> 24);
+        dest[1] = (unsigned char)(temp >> 16);
+        dest[2] = (unsigned char)(temp >> 8);
+#endif
+
+        src += sourceStride;
+        dest += destinationStride * 3;
+    }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_Int24_Dither(
+    void *destinationBuffer, signed int destinationStride,
+    void *sourceBuffer, signed int sourceStride,
+    unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+    float *src = (float*)sourceBuffer;
+    unsigned char *dest = (unsigned char*)destinationBuffer;
+    signed long temp;
+
+    while( count-- )
+    {
+        /* convert to 32 bit and drop the low 8 bits */
+
+        double dither  = PaUtil_GenerateFloatTriangularDither( ditherGenerator );
+        /* use smaller scaler to prevent overflow when we add the dither */
+        double dithered = ((double)*src * (2147483646.0)) + dither;
+        
+        temp = (signed long) dithered;
+
+#if defined(PA_LITTLE_ENDIAN)
+        dest[0] = (unsigned char)(temp >> 8);
+        dest[1] = (unsigned char)(temp >> 16);
+        dest[2] = (unsigned char)(temp >> 24);
+#elif defined(PA_BIG_ENDIAN)
+        dest[0] = (unsigned char)(temp >> 24);
+        dest[1] = (unsigned char)(temp >> 16);
+        dest[2] = (unsigned char)(temp >> 8);
+#endif
+
+        src += sourceStride;
+        dest += destinationStride * 3;
+    }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_Int24_Clip(
+    void *destinationBuffer, signed int destinationStride,
+    void *sourceBuffer, signed int sourceStride,
+    unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+    float *src = (float*)sourceBuffer;
+    unsigned char *dest = (unsigned char*)destinationBuffer;
+    signed long temp;
+
+    (void) ditherGenerator; /* unused parameter */
+    
+    while( count-- )
+    {
+        /* convert to 32 bit and drop the low 8 bits */
+        double scaled = *src * 0x7FFFFFFF;
+        PA_CLIP_( scaled, -2147483648., 2147483647.  );
+        temp = (signed long) scaled;
+
+#if defined(PA_LITTLE_ENDIAN)
+        dest[0] = (unsigned char)(temp >> 8);
+        dest[1] = (unsigned char)(temp >> 16);
+        dest[2] = (unsigned char)(temp >> 24);
+#elif defined(PA_BIG_ENDIAN)
+        dest[0] = (unsigned char)(temp >> 24);
+        dest[1] = (unsigned char)(temp >> 16);
+        dest[2] = (unsigned char)(temp >> 8);
+#endif
+
+        src += sourceStride;
+        dest += destinationStride * 3;
+    }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_Int24_DitherClip(
+    void *destinationBuffer, signed int destinationStride,
+    void *sourceBuffer, signed int sourceStride,
+    unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+    float *src = (float*)sourceBuffer;
+    unsigned char *dest = (unsigned char*)destinationBuffer;
+    signed long temp;
+    
+    while( count-- )
+    {
+        /* convert to 32 bit and drop the low 8 bits */
+        
+        double dither  = PaUtil_GenerateFloatTriangularDither( ditherGenerator );
+        /* use smaller scaler to prevent overflow when we add the dither */
+        double dithered = ((double)*src * (2147483646.0)) + dither;
+        PA_CLIP_( dithered, -2147483648., 2147483647.  );
+        
+        temp = (signed long) dithered;
+
+#if defined(PA_LITTLE_ENDIAN)
+        dest[0] = (unsigned char)(temp >> 8);
+        dest[1] = (unsigned char)(temp >> 16);
+        dest[2] = (unsigned char)(temp >> 24);
+#elif defined(PA_BIG_ENDIAN)
+        dest[0] = (unsigned char)(temp >> 24);
+        dest[1] = (unsigned char)(temp >> 16);
+        dest[2] = (unsigned char)(temp >> 8);
+#endif
+
+        src += sourceStride;
+        dest += destinationStride * 3;
+    }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_Int16(
+    void *destinationBuffer, signed int destinationStride,
+    void *sourceBuffer, signed int sourceStride,
+    unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+    float *src = (float*)sourceBuffer;
+    signed short *dest =  (signed short*)destinationBuffer;
+    (void)ditherGenerator; /* unused parameter */
+
+    while( count-- )
+    {
+#ifdef PA_USE_C99_LRINTF
+        float tempf = (*src * (32767.0f)) ;
+        *dest = lrintf(tempf-0.5f);
+#else
+        short samp = (short) (*src * (32767.0f));
+        *dest = samp;
+#endif
+
+        src += sourceStride;
+        dest += destinationStride;
+    }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_Int16_Dither(
+    void *destinationBuffer, signed int destinationStride,
+    void *sourceBuffer, signed int sourceStride,
+    unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+    float *src = (float*)sourceBuffer;
+    signed short *dest = (signed short*)destinationBuffer;
+
+    while( count-- )
+    {
+
+        float dither  = PaUtil_GenerateFloatTriangularDither( ditherGenerator );
+        /* use smaller scaler to prevent overflow when we add the dither */
+        float dithered = (*src * (32766.0f)) + dither;
+
+#ifdef PA_USE_C99_LRINTF
+        *dest = lrintf(dithered-0.5f);
+#else
+        *dest = (signed short) dithered;
+#endif
+
+        src += sourceStride;
+        dest += destinationStride;
+    }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_Int16_Clip(
+    void *destinationBuffer, signed int destinationStride,
+    void *sourceBuffer, signed int sourceStride,
+    unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+    float *src = (float*)sourceBuffer;
+    signed short *dest =  (signed short*)destinationBuffer;
+    (void)ditherGenerator; /* unused parameter */
+
+    while( count-- )
+    {
+#ifdef PA_USE_C99_LRINTF
+        long samp = lrintf((*src * (32767.0f)) -0.5f);
+#else
+        long samp = (signed long) (*src * (32767.0f));
+#endif
+        PA_CLIP_( samp, -0x8000, 0x7FFF );
+        *dest = (signed short) samp;
+
+        src += sourceStride;
+        dest += destinationStride;
+    }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_Int16_DitherClip(
+    void *destinationBuffer, signed int destinationStride,
+    void *sourceBuffer, signed int sourceStride,
+    unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+    float *src = (float*)sourceBuffer;
+    signed short *dest =  (signed short*)destinationBuffer;
+    (void)ditherGenerator; /* unused parameter */
+
+    while( count-- )
+    {
+
+        float dither  = PaUtil_GenerateFloatTriangularDither( ditherGenerator );
+        /* use smaller scaler to prevent overflow when we add the dither */
+        float dithered = (*src * (32766.0f)) + dither;
+        signed long samp = (signed long) dithered;
+        PA_CLIP_( samp, -0x8000, 0x7FFF );
+#ifdef PA_USE_C99_LRINTF
+        *dest = lrintf(samp-0.5f);
+#else
+        *dest = (signed short) samp;
+#endif
+
+        src += sourceStride;
+        dest += destinationStride;
+    }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_Int8(
+    void *destinationBuffer, signed int destinationStride,
+    void *sourceBuffer, signed int sourceStride,
+    unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+    float *src = (float*)sourceBuffer;
+    signed char *dest =  (signed char*)destinationBuffer;
+    (void)ditherGenerator; /* unused parameter */
+
+    while( count-- )
+    {
+        signed char samp = (signed char) (*src * (127.0f));
+        *dest = samp;
+
+        src += sourceStride;
+        dest += destinationStride;
+    }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_Int8_Dither(
+    void *destinationBuffer, signed int destinationStride,
+    void *sourceBuffer, signed int sourceStride,
+    unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+    float *src = (float*)sourceBuffer;
+    signed char *dest =  (signed char*)destinationBuffer;
+    (void)ditherGenerator; /* unused parameter */
+
+    while( count-- )
+    {
+        float dither  = PaUtil_GenerateFloatTriangularDither( ditherGenerator );
+        /* use smaller scaler to prevent overflow when we add the dither */
+        float dithered = (*src * (126.0f)) + dither;
+        signed long samp = (signed long) dithered;
+        *dest = (signed char) samp;
+
+        src += sourceStride;
+        dest += destinationStride;
+    }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_Int8_Clip(
+    void *destinationBuffer, signed int destinationStride,
+    void *sourceBuffer, signed int sourceStride,
+    unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+    float *src = (float*)sourceBuffer;
+    signed char *dest =  (signed char*)destinationBuffer;
+    (void)ditherGenerator; /* unused parameter */
+
+    while( count-- )
+    {
+        signed long samp = (signed long)(*src * (127.0f));
+        PA_CLIP_( samp, -0x80, 0x7F );
+        *dest = (signed char) samp;
+
+        src += sourceStride;
+        dest += destinationStride;
+    }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_Int8_DitherClip(
+    void *destinationBuffer, signed int destinationStride,
+    void *sourceBuffer, signed int sourceStride,
+    unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+    float *src = (float*)sourceBuffer;
+    signed char *dest =  (signed char*)destinationBuffer;
+    (void)ditherGenerator; /* unused parameter */
+
+    while( count-- )
+    {
+        float dither  = PaUtil_GenerateFloatTriangularDither( ditherGenerator );
+        /* use smaller scaler to prevent overflow when we add the dither */
+        float dithered = (*src * (126.0f)) + dither;
+        signed long samp = (signed long) dithered;
+        PA_CLIP_( samp, -0x80, 0x7F );
+        *dest = (signed char) samp;
+
+        src += sourceStride;
+        dest += destinationStride;
+    }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_UInt8(
+    void *destinationBuffer, signed int destinationStride,
+    void *sourceBuffer, signed int sourceStride,
+    unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+    float *src = (float*)sourceBuffer;
+    unsigned char *dest =  (unsigned char*)destinationBuffer;
+    (void)ditherGenerator; /* unused parameter */
+
+    while( count-- )
+    {
+        unsigned char samp = (unsigned char)(128 + ((unsigned char) (*src * (127.0f))));
+        *dest = samp;
+
+        src += sourceStride;
+        dest += destinationStride;
+    }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_UInt8_Dither(
+    void *destinationBuffer, signed int destinationStride,
+    void *sourceBuffer, signed int sourceStride,
+    unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+    float *src = (float*)sourceBuffer;
+    unsigned char *dest =  (unsigned char*)destinationBuffer;
+    (void)ditherGenerator; /* unused parameter */
+
+    while( count-- )
+    {
+        /* IMPLEMENT ME */
+
+        src += sourceStride;
+        dest += destinationStride;
+    }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_UInt8_Clip(
+    void *destinationBuffer, signed int destinationStride,
+    void *sourceBuffer, signed int sourceStride,
+    unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+    float *src = (float*)sourceBuffer;
+    unsigned char *dest =  (unsigned char*)destinationBuffer;
+    (void)ditherGenerator; /* unused parameter */
+
+    while( count-- )
+    {
+        /* IMPLEMENT ME */
+
+        src += sourceStride;
+        dest += destinationStride;
+    }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_UInt8_DitherClip(
+    void *destinationBuffer, signed int destinationStride,
+    void *sourceBuffer, signed int sourceStride,
+    unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+    float *src = (float*)sourceBuffer;
+    unsigned char *dest =  (unsigned char*)destinationBuffer;
+    (void)ditherGenerator; /* unused parameter */
+
+    while( count-- )
+    {
+        /* IMPLEMENT ME */
+
+        src += sourceStride;
+        dest += destinationStride;
+    }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int32_To_Float32(
+    void *destinationBuffer, signed int destinationStride,
+    void *sourceBuffer, signed int sourceStride,
+    unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+    signed long *src = (signed long*)sourceBuffer;
+    float *dest =  (float*)destinationBuffer;
+    (void)ditherGenerator; /* unused parameter */
+
+    while( count-- )
+    {
+        *dest = (float) ((double)*src * const_1_div_2147483648_);
+
+        src += sourceStride;
+        dest += destinationStride;
+    }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int32_To_Int24(
+    void *destinationBuffer, signed int destinationStride,
+    void *sourceBuffer, signed int sourceStride,
+    unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+    signed long *src    = (signed long*)sourceBuffer;
+    unsigned char *dest = (unsigned char*)destinationBuffer;
+    (void) ditherGenerator; /* unused parameter */
+    
+	while( count-- )
+    {
+		/* REVIEW */
+#if defined(PA_LITTLE_ENDIAN)
+        dest[0] = (unsigned char)(*src >> 8);
+        dest[1] = (unsigned char)(*src >> 16);
+        dest[2] = (unsigned char)(*src >> 24);
+#elif defined(PA_BIG_ENDIAN)
+        dest[0] = (unsigned char)(*src >> 24);
+        dest[1] = (unsigned char)(*src >> 16);
+        dest[2] = (unsigned char)(*src >> 8);
+#endif
+        src += sourceStride;
+        dest += destinationStride * 3;
+    }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int32_To_Int24_Dither(
+    void *destinationBuffer, signed int destinationStride,
+    void *sourceBuffer, signed int sourceStride,
+    unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+    (void) destinationBuffer; /* unused parameters */
+    (void) destinationStride; /* unused parameters */
+    (void) sourceBuffer; /* unused parameters */
+    (void) sourceStride; /* unused parameters */
+    (void) count; /* unused parameters */
+    (void) ditherGenerator; /* unused parameters */
+    /* IMPLEMENT ME */
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int32_To_Int16(
+    void *destinationBuffer, signed int destinationStride,
+    void *sourceBuffer, signed int sourceStride,
+    unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+    signed long *src = (signed long*)sourceBuffer;
+    signed short *dest =  (signed short*)destinationBuffer;
+    (void)ditherGenerator; /* unused parameter */
+
+    while( count-- )
+    {
+        *dest = (signed short) ((*src) >> 16);
+
+        src += sourceStride;
+        dest += destinationStride;
+    }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int32_To_Int16_Dither(
+    void *destinationBuffer, signed int destinationStride,
+    void *sourceBuffer, signed int sourceStride,
+    unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+    signed long *src = (signed long*)sourceBuffer;
+    signed short *dest =  (signed short*)destinationBuffer;
+    signed long dither;
+
+    while( count-- )
+    {
+        /* REVIEW */
+        dither = PaUtil_Generate16BitTriangularDither( ditherGenerator );
+        *dest = (signed short) ((((*src)>>1) + dither) >> 15);
+
+        src += sourceStride;
+        dest += destinationStride;
+    }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int32_To_Int8(
+    void *destinationBuffer, signed int destinationStride,
+    void *sourceBuffer, signed int sourceStride,
+    unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+    signed long *src = (signed long*)sourceBuffer;
+    signed char *dest =  (signed char*)destinationBuffer;
+    (void)ditherGenerator; /* unused parameter */
+
+    while( count-- )
+    {
+        *dest = (signed char) ((*src) >> 24);
+
+        src += sourceStride;
+        dest += destinationStride;
+    }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int32_To_Int8_Dither(
+    void *destinationBuffer, signed int destinationStride,
+    void *sourceBuffer, signed int sourceStride,
+    unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+    signed long *src = (signed long*)sourceBuffer;
+    signed char *dest =  (signed char*)destinationBuffer;
+    signed long dither;
+
+    while( count-- )
+    {
+        /* REVIEW */
+        dither = PaUtil_Generate16BitTriangularDither( ditherGenerator );
+        *dest = (signed char) ((((*src)>>1) + dither) >> 23);
+
+        src += sourceStride;
+        dest += destinationStride;
+    }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int32_To_UInt8(
+    void *destinationBuffer, signed int destinationStride,
+    void *sourceBuffer, signed int sourceStride,
+    unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+    signed long *src = (signed long*)sourceBuffer;
+    unsigned char *dest =  (unsigned char*)destinationBuffer;
+    (void)ditherGenerator; /* unused parameter */
+
+    while( count-- )
+    {
+		(*dest) = (unsigned char)(((*src) >> 24) + 128); 
+
+        src += sourceStride;
+        dest += destinationStride;
+    }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int32_To_UInt8_Dither(
+    void *destinationBuffer, signed int destinationStride,
+    void *sourceBuffer, signed int sourceStride,
+    unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+    signed long *src = (signed long*)sourceBuffer;
+    unsigned char *dest =  (unsigned char*)destinationBuffer;
+    (void)ditherGenerator; /* unused parameter */
+
+    while( count-- )
+    {
+        /* IMPLEMENT ME */
+
+        src += sourceStride;
+        dest += destinationStride;
+    }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int24_To_Float32(
+    void *destinationBuffer, signed int destinationStride,
+    void *sourceBuffer, signed int sourceStride,
+    unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+    unsigned char *src = (unsigned char*)sourceBuffer;
+    float *dest = (float*)destinationBuffer;
+    signed long temp;
+
+    (void) ditherGenerator; /* unused parameter */
+    
+    while( count-- )
+    {
+
+#if defined(PA_LITTLE_ENDIAN)
+        temp = (((long)src[0]) << 8);  
+        temp = temp | (((long)src[1]) << 16);
+        temp = temp | (((long)src[2]) << 24);
+#elif defined(PA_BIG_ENDIAN)
+        temp = (((long)src[0]) << 24);
+        temp = temp | (((long)src[1]) << 16);
+        temp = temp | (((long)src[2]) << 8);
+#endif
+
+        *dest = (float) ((double)temp * const_1_div_2147483648_);
+
+        src += sourceStride * 3;
+        dest += destinationStride;
+    }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int24_To_Int32(
+    void *destinationBuffer, signed int destinationStride,
+    void *sourceBuffer, signed int sourceStride,
+    unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+    unsigned char *src  = (unsigned char*)sourceBuffer;
+    signed long   *dest = (signed long*)  destinationBuffer;
+    signed long temp;
+
+    (void) ditherGenerator; /* unused parameter */
+    
+    while( count-- )
+    {
+
+#if defined(PA_LITTLE_ENDIAN)
+        temp = (((long)src[0]) << 8);  
+        temp = temp | (((long)src[1]) << 16);
+        temp = temp | (((long)src[2]) << 24);
+#elif defined(PA_BIG_ENDIAN)
+        temp = (((long)src[0]) << 24);
+        temp = temp | (((long)src[1]) << 16);
+        temp = temp | (((long)src[2]) << 8);
+#endif
+
+        *dest = temp;
+
+        src += sourceStride * 3;
+        dest += destinationStride;
+    }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int24_To_Int16(
+    void *destinationBuffer, signed int destinationStride,
+    void *sourceBuffer, signed int sourceStride,
+    unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+    unsigned char *src = (unsigned char*)sourceBuffer;
+    signed short *dest = (signed short*)destinationBuffer;
+    
+	signed short temp;
+
+    (void) ditherGenerator; /* unused parameter */
+        
+    while( count-- )
+    {
+		
+#if defined(PA_LITTLE_ENDIAN)
+		/* src[0] is discarded */
+        temp = (((signed short)src[1]));
+        temp = temp | (signed short)(((signed short)src[2]) << 8);
+#elif defined(PA_BIG_ENDIAN)
+		/* src[2] is discarded */
+        temp = (signed short)(((signed short)src[0]) << 8);
+        temp = temp | (((signed short)src[1]));
+#endif
+
+        *dest = temp;
+
+        src += sourceStride * 3;
+        dest += destinationStride;
+    }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int24_To_Int16_Dither(
+    void *destinationBuffer, signed int destinationStride,
+    void *sourceBuffer, signed int sourceStride,
+    unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+    (void) destinationBuffer; /* unused parameters */
+    (void) destinationStride; /* unused parameters */
+    (void) sourceBuffer; /* unused parameters */
+    (void) sourceStride; /* unused parameters */
+    (void) count; /* unused parameters */
+    (void) ditherGenerator; /* unused parameters */
+    /* IMPLEMENT ME */
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int24_To_Int8(
+    void *destinationBuffer, signed int destinationStride,
+    void *sourceBuffer, signed int sourceStride,
+    unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+    unsigned char *src = (unsigned char*)sourceBuffer;
+    signed char  *dest = (signed char*)destinationBuffer;
+    
+    (void) ditherGenerator; /* unused parameter */
+        
+    while( count-- )
+    {	
+	
+#if defined(PA_LITTLE_ENDIAN)
+		/* src[0] is discarded */
+		/* src[1] is discarded */
+        *dest = src[2];
+#elif defined(PA_BIG_ENDIAN)
+		/* src[2] is discarded */
+		/* src[1] is discarded */
+		*dest = src[0];
+#endif
+
+        src += sourceStride * 3;
+        dest += destinationStride;
+    }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int24_To_Int8_Dither(
+    void *destinationBuffer, signed int destinationStride,
+    void *sourceBuffer, signed int sourceStride,
+    unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+    (void) destinationBuffer; /* unused parameters */
+    (void) destinationStride; /* unused parameters */
+    (void) sourceBuffer; /* unused parameters */
+    (void) sourceStride; /* unused parameters */
+    (void) count; /* unused parameters */
+    (void) ditherGenerator; /* unused parameters */
+    /* IMPLEMENT ME */
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int24_To_UInt8(
+    void *destinationBuffer, signed int destinationStride,
+    void *sourceBuffer, signed int sourceStride,
+    unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+    unsigned char *src = (unsigned char*)sourceBuffer;
+    unsigned char *dest = (unsigned char*)destinationBuffer;
+    
+    (void) ditherGenerator; /* unused parameter */
+        
+    while( count-- )
+    {
+		
+#if defined(PA_LITTLE_ENDIAN)
+		/* src[0] is discarded */
+		/* src[1] is discarded */
+        *dest = (unsigned char)(src[2] + 128);
+#elif defined(PA_BIG_ENDIAN)
+        *dest = (unsigned char)(src[0] + 128);
+		/* src[1] is discarded */
+		/* src[2] is discarded */		
+#endif
+
+        src += sourceStride * 3;
+        dest += destinationStride;
+    }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int24_To_UInt8_Dither(
+    void *destinationBuffer, signed int destinationStride,
+    void *sourceBuffer, signed int sourceStride,
+    unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+    (void) destinationBuffer; /* unused parameters */
+    (void) destinationStride; /* unused parameters */
+    (void) sourceBuffer; /* unused parameters */
+    (void) sourceStride; /* unused parameters */
+    (void) count; /* unused parameters */
+    (void) ditherGenerator; /* unused parameters */
+    /* IMPLEMENT ME */
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int16_To_Float32(
+    void *destinationBuffer, signed int destinationStride,
+    void *sourceBuffer, signed int sourceStride,
+    unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+    signed short *src = (signed short*)sourceBuffer;
+    float *dest =  (float*)destinationBuffer;
+    (void)ditherGenerator; /* unused parameter */
+
+    while( count-- )
+    {
+        float samp = *src * const_1_div_32768_; /* FIXME: i'm concerned about this being asymetrical with float->int16 -rb */
+        *dest = samp;
+
+        src += sourceStride;
+        dest += destinationStride;
+    }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int16_To_Int32(
+    void *destinationBuffer, signed int destinationStride,
+    void *sourceBuffer, signed int sourceStride,
+    unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+    signed short *src = (signed short*)sourceBuffer;
+    signed long *dest =  (signed long*)destinationBuffer;
+    (void)ditherGenerator; /* unused parameter */
+
+    while( count-- )
+    {
+        /* REVIEW: we should consider something like
+            (*src << 16) | (*src & 0xFFFF)
+        */
+        
+        *dest = *src << 16;
+
+        src += sourceStride;
+        dest += destinationStride;
+    }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int16_To_Int24(
+    void *destinationBuffer, signed int destinationStride,
+    void *sourceBuffer, signed int sourceStride,
+    unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+    signed short *src   = (signed short*) sourceBuffer;
+    unsigned char *dest = (unsigned char*)destinationBuffer;
+    signed short temp;
+
+    (void) ditherGenerator; /* unused parameter */
+    
+    while( count-- )
+    {
+        temp = *src;
+        
+#if defined(PA_LITTLE_ENDIAN)
+        dest[0] = 0;
+        dest[1] = (unsigned char)(temp);
+        dest[2] = (unsigned char)(temp >> 8);
+#elif defined(PA_BIG_ENDIAN)
+        dest[0] = (unsigned char)(temp >> 8);
+        dest[1] = (unsigned char)(temp);
+        dest[2] = 0;
+#endif
+
+        src += sourceStride;
+        dest += destinationStride * 3;
+    }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int16_To_Int8(
+    void *destinationBuffer, signed int destinationStride,
+    void *sourceBuffer, signed int sourceStride,
+    unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+    signed short *src = (signed short*)sourceBuffer;
+    signed char *dest =  (signed char*)destinationBuffer;
+    (void)ditherGenerator; /* unused parameter */
+
+    while( count-- )
+    {
+        (*dest) = (signed char)((*src) >> 8);
+
+        src += sourceStride;
+        dest += destinationStride;
+    }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int16_To_Int8_Dither(
+    void *destinationBuffer, signed int destinationStride,
+    void *sourceBuffer, signed int sourceStride,
+    unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+    signed short *src = (signed short*)sourceBuffer;
+    signed char *dest =  (signed char*)destinationBuffer;
+    (void)ditherGenerator; /* unused parameter */
+
+    while( count-- )
+    {
+        /* IMPLEMENT ME */
+
+        src += sourceStride;
+        dest += destinationStride;
+    }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int16_To_UInt8(
+    void *destinationBuffer, signed int destinationStride,
+    void *sourceBuffer, signed int sourceStride,
+    unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+    signed short *src = (signed short*)sourceBuffer;
+    unsigned char *dest =  (unsigned char*)destinationBuffer;
+    (void)ditherGenerator; /* unused parameter */
+
+    while( count-- )
+    {
+		(*dest) = (unsigned char)(((*src) >> 8) + 128); 
+
+        src += sourceStride;
+        dest += destinationStride;
+    }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int16_To_UInt8_Dither(
+    void *destinationBuffer, signed int destinationStride,
+    void *sourceBuffer, signed int sourceStride,
+    unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+    signed short *src = (signed short*)sourceBuffer;
+    unsigned char *dest =  (unsigned char*)destinationBuffer;
+    (void)ditherGenerator; /* unused parameter */
+
+    while( count-- )
+    {
+        /* IMPLEMENT ME */
+
+        src += sourceStride;
+        dest += destinationStride;
+    }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int8_To_Float32(
+    void *destinationBuffer, signed int destinationStride,
+    void *sourceBuffer, signed int sourceStride,
+    unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+    signed char *src = (signed char*)sourceBuffer;
+    float *dest =  (float*)destinationBuffer;
+    (void)ditherGenerator; /* unused parameter */
+
+    while( count-- )
+    {
+        float samp = *src * const_1_div_128_;
+        *dest = samp;
+
+        src += sourceStride;
+        dest += destinationStride;
+    }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int8_To_Int32(
+    void *destinationBuffer, signed int destinationStride,
+    void *sourceBuffer, signed int sourceStride,
+    unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+    signed char *src = (signed char*)sourceBuffer;
+    signed long *dest =  (signed long*)destinationBuffer;
+    (void)ditherGenerator; /* unused parameter */
+
+    while( count-- )
+    {
+		(*dest) = (*src) << 24;
+
+        src += sourceStride;
+        dest += destinationStride;
+    }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int8_To_Int24(
+    void *destinationBuffer, signed int destinationStride,
+    void *sourceBuffer, signed int sourceStride,
+    unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+    signed char *src = (signed char*)sourceBuffer;
+    unsigned char *dest =  (unsigned char*)destinationBuffer;
+    (void)ditherGenerator; /* unused parameter */
+
+    while( count-- )
+    {
+
+#if defined(PA_LITTLE_ENDIAN)
+        dest[0] = 0;
+        dest[1] = 0;
+        dest[2] = (*src);
+#elif defined(PA_BIG_ENDIAN)
+        dest[0] = (*src);
+        dest[1] = 0;
+        dest[2] = 0;
+#endif
+
+        src += sourceStride;
+        dest += destinationStride * 3;
+    }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int8_To_Int16(
+    void *destinationBuffer, signed int destinationStride,
+    void *sourceBuffer, signed int sourceStride,
+    unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+    signed char *src = (signed char*)sourceBuffer;
+    signed short *dest =  (signed short*)destinationBuffer;
+    (void)ditherGenerator; /* unused parameter */
+
+    while( count-- )
+    {
+		(*dest) = (signed short)((*src) << 8);
+
+        src += sourceStride;
+        dest += destinationStride;
+    }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int8_To_UInt8(
+    void *destinationBuffer, signed int destinationStride,
+    void *sourceBuffer, signed int sourceStride,
+    unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+    signed char *src = (signed char*)sourceBuffer;
+    unsigned char *dest =  (unsigned char*)destinationBuffer;
+    (void)ditherGenerator; /* unused parameter */
+
+    while( count-- )
+    {
+        (*dest) = (unsigned char)(*src + 128);
+
+        src += sourceStride;
+        dest += destinationStride;
+    }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void UInt8_To_Float32(
+    void *destinationBuffer, signed int destinationStride,
+    void *sourceBuffer, signed int sourceStride,
+    unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+    unsigned char *src = (unsigned char*)sourceBuffer;
+    float *dest =  (float*)destinationBuffer;
+    (void)ditherGenerator; /* unused parameter */
+
+    while( count-- )
+    {
+        float samp = (*src - 128) * const_1_div_128_;
+        *dest = samp;
+
+        src += sourceStride;
+        dest += destinationStride;
+    }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void UInt8_To_Int32(
+    void *destinationBuffer, signed int destinationStride,
+    void *sourceBuffer, signed int sourceStride,
+    unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+    unsigned char *src = (unsigned char*)sourceBuffer;
+    signed long *dest =  (signed long*)destinationBuffer;
+    (void)ditherGenerator; /* unused parameter */
+
+    while( count-- )
+    {
+		(*dest) = (*src - 128) << 24;
+
+        src += sourceStride;
+        dest += destinationStride;
+    }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void UInt8_To_Int24(
+    void *destinationBuffer, signed int destinationStride,
+    void *sourceBuffer, signed int sourceStride,
+    unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+	unsigned char *src  = (unsigned char*)sourceBuffer;
+    unsigned char *dest = (unsigned char*)destinationBuffer;
+    (void) ditherGenerator; /* unused parameters */
+    
+	while( count-- )
+    {
+
+#if defined(PA_LITTLE_ENDIAN)
+        dest[0] = 0;
+        dest[1] = 0;
+        dest[2] = (unsigned char)(*src - 128);
+#elif defined(PA_BIG_ENDIAN)
+        dest[0] = (unsigned char)(*src - 128);
+        dest[1] = 0;
+        dest[2] = 0;
+#endif
+		
+        src += sourceStride;
+        dest += destinationStride * 3;    
+	}
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void UInt8_To_Int16(
+    void *destinationBuffer, signed int destinationStride,
+    void *sourceBuffer, signed int sourceStride,
+    unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+    unsigned char *src = (unsigned char*)sourceBuffer;
+    signed short *dest =  (signed short*)destinationBuffer;
+    (void)ditherGenerator; /* unused parameter */
+
+    while( count-- )
+    {
+		(*dest) = (signed short)((*src - 128) << 8);
+
+        src += sourceStride;
+        dest += destinationStride;
+    }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void UInt8_To_Int8(
+    void *destinationBuffer, signed int destinationStride,
+    void *sourceBuffer, signed int sourceStride,
+    unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+    unsigned char *src = (unsigned char*)sourceBuffer;
+    signed char  *dest = (signed char*)destinationBuffer;
+    (void)ditherGenerator; /* unused parameter */
+
+    while( count-- )
+    {
+        (*dest) = (signed char)(*src - 128);
+
+        src += sourceStride;
+        dest += destinationStride;
+    }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Copy_8_To_8(
+    void *destinationBuffer, signed int destinationStride,
+    void *sourceBuffer, signed int sourceStride,
+    unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+    unsigned char *src = (unsigned char*)sourceBuffer;
+    unsigned char *dest = (unsigned char*)destinationBuffer;
+                                                      
+    (void) ditherGenerator; /* unused parameter */
+
+    while( count-- )
+    {
+        *dest = *src;
+
+        src += sourceStride;
+        dest += destinationStride;
+    }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Copy_16_To_16(
+    void *destinationBuffer, signed int destinationStride,
+    void *sourceBuffer, signed int sourceStride,
+    unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+    PaUint16 *src = (PaUint16 *)sourceBuffer;
+    PaUint16 *dest = (PaUint16 *)destinationBuffer;
+                                                        
+    (void) ditherGenerator; /* unused parameter */
+    
+    while( count-- )
+    {
+        *dest = *src;
+
+        src += sourceStride;
+        dest += destinationStride;
+    }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Copy_24_To_24(
+    void *destinationBuffer, signed int destinationStride,
+    void *sourceBuffer, signed int sourceStride,
+    unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+    unsigned char *src = (unsigned char*)sourceBuffer;
+    unsigned char *dest = (unsigned char*)destinationBuffer;
+
+    (void) ditherGenerator; /* unused parameter */
+    
+    while( count-- )
+    {
+        dest[0] = src[0];
+        dest[1] = src[1];
+        dest[2] = src[2];
+
+        src += sourceStride * 3;
+        dest += destinationStride * 3;
+    }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Copy_32_To_32(
+    void *destinationBuffer, signed int destinationStride,
+    void *sourceBuffer, signed int sourceStride,
+    unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+    PaUint32 *dest = (PaUint32 *)destinationBuffer;
+    PaUint32 *src = (PaUint32 *)sourceBuffer;
+
+    (void) ditherGenerator; /* unused parameter */
+    
+    while( count-- )
+    {
+        *dest = *src;
+
+        src += sourceStride;
+        dest += destinationStride;
+    }
+}
+
+/* -------------------------------------------------------------------------- */
+
+PaUtilConverterTable paConverters = {
+    Float32_To_Int32,              /* PaUtilConverter *Float32_To_Int32; */
+    Float32_To_Int32_Dither,       /* PaUtilConverter *Float32_To_Int32_Dither; */
+    Float32_To_Int32_Clip,         /* PaUtilConverter *Float32_To_Int32_Clip; */
+    Float32_To_Int32_DitherClip,   /* PaUtilConverter *Float32_To_Int32_DitherClip; */
+
+    Float32_To_Int24,              /* PaUtilConverter *Float32_To_Int24; */
+    Float32_To_Int24_Dither,       /* PaUtilConverter *Float32_To_Int24_Dither; */
+    Float32_To_Int24_Clip,         /* PaUtilConverter *Float32_To_Int24_Clip; */
+    Float32_To_Int24_DitherClip,   /* PaUtilConverter *Float32_To_Int24_DitherClip; */
+    
+    Float32_To_Int16,              /* PaUtilConverter *Float32_To_Int16; */
+    Float32_To_Int16_Dither,       /* PaUtilConverter *Float32_To_Int16_Dither; */
+    Float32_To_Int16_Clip,         /* PaUtilConverter *Float32_To_Int16_Clip; */
+    Float32_To_Int16_DitherClip,   /* PaUtilConverter *Float32_To_Int16_DitherClip; */
+
+    Float32_To_Int8,               /* PaUtilConverter *Float32_To_Int8; */
+    Float32_To_Int8_Dither,        /* PaUtilConverter *Float32_To_Int8_Dither; */
+    Float32_To_Int8_Clip,          /* PaUtilConverter *Float32_To_Int8_Clip; */
+    Float32_To_Int8_DitherClip,    /* PaUtilConverter *Float32_To_Int8_DitherClip; */
+
+    Float32_To_UInt8,              /* PaUtilConverter *Float32_To_UInt8; */
+    Float32_To_UInt8_Dither,       /* PaUtilConverter *Float32_To_UInt8_Dither; */
+    Float32_To_UInt8_Clip,         /* PaUtilConverter *Float32_To_UInt8_Clip; */
+    Float32_To_UInt8_DitherClip,   /* PaUtilConverter *Float32_To_UInt8_DitherClip; */
+
+    Int32_To_Float32,              /* PaUtilConverter *Int32_To_Float32; */
+    Int32_To_Int24,                /* PaUtilConverter *Int32_To_Int24; */
+    Int32_To_Int24_Dither,         /* PaUtilConverter *Int32_To_Int24_Dither; */
+    Int32_To_Int16,                /* PaUtilConverter *Int32_To_Int16; */
+    Int32_To_Int16_Dither,         /* PaUtilConverter *Int32_To_Int16_Dither; */
+    Int32_To_Int8,                 /* PaUtilConverter *Int32_To_Int8; */
+    Int32_To_Int8_Dither,          /* PaUtilConverter *Int32_To_Int8_Dither; */
+    Int32_To_UInt8,                /* PaUtilConverter *Int32_To_UInt8; */
+    Int32_To_UInt8_Dither,         /* PaUtilConverter *Int32_To_UInt8_Dither; */
+
+    Int24_To_Float32,              /* PaUtilConverter *Int24_To_Float32; */
+    Int24_To_Int32,                /* PaUtilConverter *Int24_To_Int32; */
+    Int24_To_Int16,                /* PaUtilConverter *Int24_To_Int16; */
+    Int24_To_Int16_Dither,         /* PaUtilConverter *Int24_To_Int16_Dither; */
+    Int24_To_Int8,                 /* PaUtilConverter *Int24_To_Int8; */
+    Int24_To_Int8_Dither,          /* PaUtilConverter *Int24_To_Int8_Dither; */
+    Int24_To_UInt8,                /* PaUtilConverter *Int24_To_UInt8; */
+    Int24_To_UInt8_Dither,         /* PaUtilConverter *Int24_To_UInt8_Dither; */
+
+    Int16_To_Float32,              /* PaUtilConverter *Int16_To_Float32; */
+    Int16_To_Int32,                /* PaUtilConverter *Int16_To_Int32; */
+    Int16_To_Int24,                /* PaUtilConverter *Int16_To_Int24; */
+    Int16_To_Int8,                 /* PaUtilConverter *Int16_To_Int8; */
+    Int16_To_Int8_Dither,          /* PaUtilConverter *Int16_To_Int8_Dither; */
+    Int16_To_UInt8,                /* PaUtilConverter *Int16_To_UInt8; */
+    Int16_To_UInt8_Dither,         /* PaUtilConverter *Int16_To_UInt8_Dither; */
+
+    Int8_To_Float32,               /* PaUtilConverter *Int8_To_Float32; */
+    Int8_To_Int32,                 /* PaUtilConverter *Int8_To_Int32; */
+    Int8_To_Int24,                 /* PaUtilConverter *Int8_To_Int24 */
+    Int8_To_Int16,                 /* PaUtilConverter *Int8_To_Int16; */
+    Int8_To_UInt8,                 /* PaUtilConverter *Int8_To_UInt8; */
+
+    UInt8_To_Float32,              /* PaUtilConverter *UInt8_To_Float32; */
+    UInt8_To_Int32,                /* PaUtilConverter *UInt8_To_Int32; */
+    UInt8_To_Int24,                /* PaUtilConverter *UInt8_To_Int24; */
+    UInt8_To_Int16,                /* PaUtilConverter *UInt8_To_Int16; */
+    UInt8_To_Int8,                 /* PaUtilConverter *UInt8_To_Int8; */
+
+    Copy_8_To_8,                   /* PaUtilConverter *Copy_8_To_8; */
+    Copy_16_To_16,                 /* PaUtilConverter *Copy_16_To_16; */
+    Copy_24_To_24,                 /* PaUtilConverter *Copy_24_To_24; */
+    Copy_32_To_32                  /* PaUtilConverter *Copy_32_To_32; */
+};
+
+/* -------------------------------------------------------------------------- */
+
+#endif /* PA_NO_STANDARD_CONVERTERS */
+
+/* -------------------------------------------------------------------------- */
+
+PaUtilZeroer* PaUtil_SelectZeroer( PaSampleFormat destinationFormat )
+{
+    switch( destinationFormat & ~paNonInterleaved ){
+    case paFloat32:
+        return paZeroers.Zero32;
+    case paInt32:
+        return paZeroers.Zero32;
+    case paInt24:
+        return paZeroers.Zero24;
+    case paInt16:
+        return paZeroers.Zero16;
+    case paInt8:
+        return paZeroers.Zero8;
+    case paUInt8:
+        return paZeroers.ZeroU8;
+    default: return 0;
+    }
+}
+
+/* -------------------------------------------------------------------------- */
+
+#ifdef PA_NO_STANDARD_ZEROERS
+
+/* -------------------------------------------------------------------------- */
+
+PaUtilZeroerTable paZeroers = {
+    0,  /* PaUtilZeroer *ZeroU8; */
+    0,  /* PaUtilZeroer *Zero8; */
+    0,  /* PaUtilZeroer *Zero16; */
+    0,  /* PaUtilZeroer *Zero24; */
+    0,  /* PaUtilZeroer *Zero32; */
+};
+
+/* -------------------------------------------------------------------------- */
+
+#else /* PA_NO_STANDARD_ZEROERS is not defined */
+
+/* -------------------------------------------------------------------------- */
+
+static void ZeroU8( void *destinationBuffer, signed int destinationStride,
+        unsigned int count )
+{
+    unsigned char *dest = (unsigned char*)destinationBuffer;
+
+    while( count-- )
+    {
+        *dest = 128;
+
+        dest += destinationStride;
+    }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Zero8( void *destinationBuffer, signed int destinationStride,
+        unsigned int count )
+{
+    unsigned char *dest = (unsigned char*)destinationBuffer;
+
+    while( count-- )
+    {
+        *dest = 0;
+
+        dest += destinationStride;
+    }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Zero16( void *destinationBuffer, signed int destinationStride,
+        unsigned int count )
+{
+    PaUint16 *dest = (PaUint16 *)destinationBuffer;
+
+    while( count-- )
+    {
+        *dest = 0;
+
+        dest += destinationStride;
+    }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Zero24( void *destinationBuffer, signed int destinationStride,
+        unsigned int count )
+{
+    unsigned char *dest = (unsigned char*)destinationBuffer;
+
+    while( count-- )
+    {
+        dest[0] = 0;
+        dest[1] = 0;
+        dest[2] = 0;
+
+        dest += destinationStride * 3;
+    }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Zero32( void *destinationBuffer, signed int destinationStride,
+        unsigned int count )
+{
+    PaUint32 *dest = (PaUint32 *)destinationBuffer;
+
+    while( count-- )
+    {
+        *dest = 0;
+
+        dest += destinationStride;
+    }
+}
+
+/* -------------------------------------------------------------------------- */
+
+PaUtilZeroerTable paZeroers = {
+    ZeroU8,  /* PaUtilZeroer *ZeroU8; */
+    Zero8,  /* PaUtilZeroer *Zero8; */
+    Zero16,  /* PaUtilZeroer *Zero16; */
+    Zero24,  /* PaUtilZeroer *Zero24; */
+    Zero32,  /* PaUtilZeroer *Zero32; */
+};
+
+/* -------------------------------------------------------------------------- */
+
+#endif /* PA_NO_STANDARD_ZEROERS */
diff --git a/src/audio/portaudio/pa_common/pa_converters.h b/src/audio/portaudio/pa_common/pa_converters.h
new file mode 100644
index 0000000000000000000000000000000000000000..1d83c990bb48f3355801019aa914062d982bbefd
--- /dev/null
+++ b/src/audio/portaudio/pa_common/pa_converters.h
@@ -0,0 +1,254 @@
+#ifndef PA_CONVERTERS_H
+#define PA_CONVERTERS_H
+/*
+ * $Id$
+ * Portable Audio I/O Library sample conversion mechanism
+ *
+ * Based on the Open Source API proposed by Ross Bencina
+ * Copyright (c) 1999-2002 Phil Burk, Ross Bencina
+ *
+ * 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.
+ */
+
+/** @file
+ @brief Conversion functions used to convert buffers of samples from one
+ format to another.
+*/
+
+
+#include "portaudio.h"  /* for PaSampleFormat */
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif /* __cplusplus */
+
+
+struct PaUtilTriangularDitherGenerator;
+
+
+/** Choose an available sample format which is most appropriate for
+ representing the requested format. If the requested format is not available
+ higher quality formats are considered before lower quality formates.
+ @param availableFormats A variable containing the logical OR of all available
+ formats.
+ @param format The desired format.
+ @return The most appropriate available format for representing the requested
+ format.
+*/
+PaSampleFormat PaUtil_SelectClosestAvailableFormat(
+        PaSampleFormat availableFormats, PaSampleFormat format );
+
+
+/* high level conversions functions for use by implementations */
+
+
+/** The generic sample converter prototype. Sample converters convert count
+    samples from sourceBuffer to destinationBuffer. The actual type of the data
+    pointed to by these parameters varys for different converter functions.
+    @param destinationBuffer A pointer to the first sample of the destination.
+    @param destinationStride An offset between successive destination samples
+    expressed in samples (not bytes.) It may be negative.
+    @param sourceBuffer A pointer to the first sample of the source.
+    @param sourceStride An offset between successive source samples
+    expressed in samples (not bytes.) It may be negative.
+    @param count The number of samples to convert.
+    @param ditherState State information used to calculate dither. Converters
+    that do not perform dithering will ignore this parameter, in which case
+    NULL or invalid dither state may be passed.
+*/
+typedef void PaUtilConverter(
+    void *destinationBuffer, signed int destinationStride,
+    void *sourceBuffer, signed int sourceStride,
+    unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator );
+
+
+/** Find a sample converter function for the given source and destinations
+    formats and flags (clip and dither.)
+    @return
+    A pointer to a PaUtilConverter which will perform the requested
+    conversion, or NULL if the given format conversion is not supported.
+    For conversions where clipping or dithering is not necessary, the
+    clip and dither flags are ignored and a non-clipping or dithering
+    version is returned.
+    If the source and destination formats are the same, a function which
+    copies data of the appropriate size will be returned.
+*/
+PaUtilConverter* PaUtil_SelectConverter( PaSampleFormat sourceFormat,
+        PaSampleFormat destinationFormat, PaStreamFlags flags );
+
+
+/** The generic buffer zeroer prototype. Buffer zeroers copy count zeros to
+    destinationBuffer. The actual type of the data pointed to varys for
+    different zeroer functions.
+    @param destinationBuffer A pointer to the first sample of the destination.
+    @param destinationStride An offset between successive destination samples
+    expressed in samples (not bytes.) It may be negative.
+    @param count The number of samples to zero.
+*/
+typedef void PaUtilZeroer(
+    void *destinationBuffer, signed int destinationStride, unsigned int count );
+
+    
+/** Find a buffer zeroer function for the given destination format.
+    @return
+    A pointer to a PaUtilZeroer which will perform the requested
+    zeroing.
+*/
+PaUtilZeroer* PaUtil_SelectZeroer( PaSampleFormat destinationFormat );
+
+/*----------------------------------------------------------------------------*/
+/* low level functions and data structures which may be used for
+    substituting conversion functions */
+
+
+/** The type used to store all sample conversion functions.
+    @see paConverters;
+*/
+typedef struct{
+    PaUtilConverter *Float32_To_Int32;
+    PaUtilConverter *Float32_To_Int32_Dither;
+    PaUtilConverter *Float32_To_Int32_Clip;
+    PaUtilConverter *Float32_To_Int32_DitherClip;
+
+    PaUtilConverter *Float32_To_Int24;
+    PaUtilConverter *Float32_To_Int24_Dither;
+    PaUtilConverter *Float32_To_Int24_Clip;
+    PaUtilConverter *Float32_To_Int24_DitherClip;
+    
+    PaUtilConverter *Float32_To_Int16;
+    PaUtilConverter *Float32_To_Int16_Dither;
+    PaUtilConverter *Float32_To_Int16_Clip;
+    PaUtilConverter *Float32_To_Int16_DitherClip;
+
+    PaUtilConverter *Float32_To_Int8;
+    PaUtilConverter *Float32_To_Int8_Dither;
+    PaUtilConverter *Float32_To_Int8_Clip;
+    PaUtilConverter *Float32_To_Int8_DitherClip;
+
+    PaUtilConverter *Float32_To_UInt8;
+    PaUtilConverter *Float32_To_UInt8_Dither;
+    PaUtilConverter *Float32_To_UInt8_Clip;
+    PaUtilConverter *Float32_To_UInt8_DitherClip;
+
+    PaUtilConverter *Int32_To_Float32;
+    PaUtilConverter *Int32_To_Int24;
+    PaUtilConverter *Int32_To_Int24_Dither;
+    PaUtilConverter *Int32_To_Int16;
+    PaUtilConverter *Int32_To_Int16_Dither;
+    PaUtilConverter *Int32_To_Int8;
+    PaUtilConverter *Int32_To_Int8_Dither;
+    PaUtilConverter *Int32_To_UInt8;
+    PaUtilConverter *Int32_To_UInt8_Dither;
+
+    PaUtilConverter *Int24_To_Float32;
+    PaUtilConverter *Int24_To_Int32;
+    PaUtilConverter *Int24_To_Int16;
+    PaUtilConverter *Int24_To_Int16_Dither;
+    PaUtilConverter *Int24_To_Int8;
+    PaUtilConverter *Int24_To_Int8_Dither;
+    PaUtilConverter *Int24_To_UInt8;
+    PaUtilConverter *Int24_To_UInt8_Dither;
+
+    PaUtilConverter *Int16_To_Float32;
+    PaUtilConverter *Int16_To_Int32;
+    PaUtilConverter *Int16_To_Int24;
+    PaUtilConverter *Int16_To_Int8;
+    PaUtilConverter *Int16_To_Int8_Dither;
+    PaUtilConverter *Int16_To_UInt8;
+    PaUtilConverter *Int16_To_UInt8_Dither;
+
+    PaUtilConverter *Int8_To_Float32;
+    PaUtilConverter *Int8_To_Int32;
+    PaUtilConverter *Int8_To_Int24;
+    PaUtilConverter *Int8_To_Int16;
+    PaUtilConverter *Int8_To_UInt8;
+    
+    PaUtilConverter *UInt8_To_Float32;
+    PaUtilConverter *UInt8_To_Int32;
+    PaUtilConverter *UInt8_To_Int24;
+    PaUtilConverter *UInt8_To_Int16;
+    PaUtilConverter *UInt8_To_Int8;
+
+    PaUtilConverter *Copy_8_To_8;       /* copy without any conversion */
+    PaUtilConverter *Copy_16_To_16;     /* copy without any conversion */
+    PaUtilConverter *Copy_24_To_24;     /* copy without any conversion */
+    PaUtilConverter *Copy_32_To_32;     /* copy without any conversion */
+} PaUtilConverterTable;
+
+
+/** A table of pointers to all required converter functions.
+    PaUtil_SelectConverter() uses this table to lookup the appropriate
+    conversion functions. The fields of this structure are initialized
+    with default conversion functions. Fields may be NULL, indicating that
+    no conversion function is available. User code may substitue optimised
+    conversion functions by assigning different function pointers to
+    these fields.
+
+    @note
+    If the PA_NO_STANDARD_CONVERTERS preprocessor variable is defined,
+    PortAudio's standard converters will not be compiled, and all fields
+    of this structure will be initialized to NULL. In such cases, users
+    should supply their own conversion functions if the require PortAudio
+    to open a stream that requires sample conversion.
+
+    @see PaUtilConverterTable, PaUtilConverter, PaUtil_SelectConverter
+*/
+extern PaUtilConverterTable paConverters;
+
+
+/** The type used to store all buffer zeroing functions.
+    @see paZeroers;
+*/
+typedef struct{
+    PaUtilZeroer *ZeroU8; /* unsigned 8 bit, zero == 128 */
+    PaUtilZeroer *Zero8;
+    PaUtilZeroer *Zero16;
+    PaUtilZeroer *Zero24;
+    PaUtilZeroer *Zero32;
+} PaUtilZeroerTable;
+
+
+/** A table of pointers to all required zeroer functions.
+    PaUtil_SelectZeroer() uses this table to lookup the appropriate
+    conversion functions. The fields of this structure are initialized
+    with default conversion functions. User code may substitue optimised
+    conversion functions by assigning different function pointers to
+    these fields.
+
+    @note
+    If the PA_NO_STANDARD_ZEROERS preprocessor variable is defined,
+    PortAudio's standard zeroers will not be compiled, and all fields
+    of this structure will be initialized to NULL. In such cases, users
+    should supply their own zeroing functions for the sample sizes which
+    they intend to use.
+
+    @see PaUtilZeroerTable, PaUtilZeroer, PaUtil_SelectZeroer
+*/
+extern PaUtilZeroerTable paZeroers;
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif /* PA_CONVERTERS_H */
diff --git a/src/audio/portaudio/pa_common/pa_cpuload.c b/src/audio/portaudio/pa_common/pa_cpuload.c
new file mode 100644
index 0000000000000000000000000000000000000000..ee2147dc33fdfbda07255729acd5d8b79560f8e0
--- /dev/null
+++ b/src/audio/portaudio/pa_common/pa_cpuload.c
@@ -0,0 +1,96 @@
+/*
+ * $Id$
+ * Portable Audio I/O Library CPU Load measurement functions
+ * Portable CPU load measurement facility.
+ *
+ * Based on the Open Source API proposed by Ross Bencina
+ * Copyright (c) 2002 Ross Bencina
+ *
+ * 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.
+ */
+
+/** @file
+ @brief Functions to assist in measuring the CPU utilization of a callback
+ stream. Used to implement the Pa_GetStreamCpuLoad() function.
+
+ @todo Dynamically calculate the coefficients used to smooth the CPU Load
+ Measurements over time to provide a uniform characterisation of CPU Load
+ independent of rate at which PaUtil_BeginCpuLoadMeasurement /
+ PaUtil_EndCpuLoadMeasurement are called.
+*/
+
+
+#include "pa_cpuload.h"
+
+#include <assert.h>
+
+#include "pa_util.h"   /* for PaUtil_GetTime() */
+
+
+void PaUtil_InitializeCpuLoadMeasurer( PaUtilCpuLoadMeasurer* measurer, double sampleRate )
+{
+    assert( sampleRate > 0 );
+
+    measurer->samplingPeriod = 1. / sampleRate;
+    measurer->averageLoad = 0.;
+}
+
+void PaUtil_ResetCpuLoadMeasurer( PaUtilCpuLoadMeasurer* measurer )
+{
+    measurer->averageLoad = 0.;
+}
+
+void PaUtil_BeginCpuLoadMeasurement( PaUtilCpuLoadMeasurer* measurer )
+{
+    measurer->measurementStartTime = PaUtil_GetTime();
+}
+
+
+void PaUtil_EndCpuLoadMeasurement( PaUtilCpuLoadMeasurer* measurer, unsigned long framesProcessed )
+{
+    double measurementEndTime, secondsFor100Percent, measuredLoad;
+
+    if( framesProcessed > 0 ){
+        measurementEndTime = PaUtil_GetTime();
+
+        assert( framesProcessed > 0 );
+        secondsFor100Percent = framesProcessed * measurer->samplingPeriod;
+
+        measuredLoad = (measurementEndTime - measurer->measurementStartTime) / secondsFor100Percent;
+
+        /* Low pass filter the calculated CPU load to reduce jitter using a simple IIR low pass filter. */
+        /** FIXME @todo these coefficients shouldn't be hardwired */
+#define LOWPASS_COEFFICIENT_0   (0.9)
+#define LOWPASS_COEFFICIENT_1   (0.99999 - LOWPASS_COEFFICIENT_0)
+
+        measurer->averageLoad = (LOWPASS_COEFFICIENT_0 * measurer->averageLoad) +
+                               (LOWPASS_COEFFICIENT_1 * measuredLoad);
+    }
+}
+
+
+double PaUtil_GetCpuLoad( PaUtilCpuLoadMeasurer* measurer )
+{
+    return measurer->averageLoad;
+}
diff --git a/src/audio/portaudio/pa_common/pa_cpuload.h b/src/audio/portaudio/pa_common/pa_cpuload.h
new file mode 100644
index 0000000000000000000000000000000000000000..131ad10bd30f706048328f6fe08430fbe7ca0197
--- /dev/null
+++ b/src/audio/portaudio/pa_common/pa_cpuload.h
@@ -0,0 +1,63 @@
+#ifndef PA_CPULOAD_H
+#define PA_CPULOAD_H
+/*
+ * $Id$
+ * Portable Audio I/O Library CPU Load measurement functions
+ * Portable CPU load measurement facility.
+ *
+ * Based on the Open Source API proposed by Ross Bencina
+ * Copyright (c) 2002 Ross Bencina
+ *
+ * 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.
+ */
+
+/** @file
+ @brief Functions to assist in measuring the CPU utilization of a callback
+ stream. Used to implement the Pa_GetStreamCpuLoad() function.
+*/
+
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif /* __cplusplus */
+
+
+typedef struct {
+    double samplingPeriod;
+    double measurementStartTime;
+    double averageLoad;
+} PaUtilCpuLoadMeasurer; /**< @todo need better name than measurer */
+
+void PaUtil_InitializeCpuLoadMeasurer( PaUtilCpuLoadMeasurer* measurer, double sampleRate );
+void PaUtil_BeginCpuLoadMeasurement( PaUtilCpuLoadMeasurer* measurer );
+void PaUtil_EndCpuLoadMeasurement( PaUtilCpuLoadMeasurer* measurer, unsigned long framesProcessed );
+void PaUtil_ResetCpuLoadMeasurer( PaUtilCpuLoadMeasurer* measurer );
+double PaUtil_GetCpuLoad( PaUtilCpuLoadMeasurer* measurer );
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */     
+#endif /* PA_CPULOAD_H */
diff --git a/src/audio/portaudio/pa_common/pa_dither.c b/src/audio/portaudio/pa_common/pa_dither.c
new file mode 100644
index 0000000000000000000000000000000000000000..d44a060cd20312368973143e278d170556e77a3b
--- /dev/null
+++ b/src/audio/portaudio/pa_common/pa_dither.c
@@ -0,0 +1,204 @@
+/*
+ * $Id$
+ * Portable Audio I/O Library triangular dither generator
+ *
+ * Based on the Open Source API proposed by Ross Bencina
+ * Copyright (c) 1999-2002 Phil Burk, Ross Bencina
+ *
+ * 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.
+ */
+
+/** @file
+ @brief Functions for generating dither noise
+*/
+
+
+#include "pa_dither.h"
+#include "pa_types.h"
+
+#define PA_DITHER_BITS_   (15)
+
+
+void PaUtil_InitializeTriangularDitherState( PaUtilTriangularDitherGenerator *state )
+{
+    state->previous = 0;
+    state->randSeed1 = 22222;
+    state->randSeed2 = 5555555;
+}
+
+
+signed long PaUtil_Generate16BitTriangularDither( PaUtilTriangularDitherGenerator *state )
+{
+    signed long current, highPass;
+
+    /* Generate two random numbers. */
+    state->randSeed1 = (state->randSeed1 * 196314165) + 907633515;
+    state->randSeed2 = (state->randSeed2 * 196314165) + 907633515;
+
+    /* Generate triangular distribution about 0.
+     * Shift before adding to prevent overflow which would skew the distribution.
+     * Also shift an extra bit for the high pass filter. 
+     */
+#define DITHER_SHIFT_  ((SIZEOF_LONG*8 - PA_DITHER_BITS_) + 1)
+    current = (((signed long)state->randSeed1)>>DITHER_SHIFT_) +
+              (((signed long)state->randSeed2)>>DITHER_SHIFT_);
+
+    /* High pass filter to reduce audibility. */
+    highPass = current - state->previous;
+    state->previous = current;
+    return highPass;
+}
+
+
+/* Multiply by PA_FLOAT_DITHER_SCALE_ to get a float between -2.0 and +1.99999 */
+#define PA_FLOAT_DITHER_SCALE_  (1.0f / ((1<<PA_DITHER_BITS_)-1))
+static const float const_float_dither_scale_ = PA_FLOAT_DITHER_SCALE_;
+
+float PaUtil_GenerateFloatTriangularDither( PaUtilTriangularDitherGenerator *state )
+{
+    signed long current, highPass;
+
+    /* Generate two random numbers. */
+    state->randSeed1 = (state->randSeed1 * 196314165) + 907633515;
+    state->randSeed2 = (state->randSeed2 * 196314165) + 907633515;
+
+    /* Generate triangular distribution about 0.
+     * Shift before adding to prevent overflow which would skew the distribution.
+     * Also shift an extra bit for the high pass filter. 
+     */
+#define DITHER_SHIFT_  ((SIZEOF_LONG*8 - PA_DITHER_BITS_) + 1)
+    current = (((signed long)state->randSeed1)>>DITHER_SHIFT_) +
+              (((signed long)state->randSeed2)>>DITHER_SHIFT_);
+
+    /* High pass filter to reduce audibility. */
+    highPass = current - state->previous;
+    state->previous = current;
+    return ((float)highPass) * const_float_dither_scale_;
+}
+
+
+/*
+The following alternate dither algorithms (from musicdsp.org) could be
+considered
+*/
+
+/*Noise shaped dither  (March 2000)
+-------------------
+
+This is a simple implementation of highpass triangular-PDF dither with
+2nd-order noise shaping, for use when truncating floating point audio
+data to fixed point.
+
+The noise shaping lowers the noise floor by 11dB below 5kHz (@ 44100Hz
+sample rate) compared to triangular-PDF dither. The code below assumes
+input data is in the range +1 to -1 and doesn't check for overloads!
+
+To save time when generating dither for multiple channels you can do
+things like this:  r3=(r1 & 0x7F)<<8; instead of calling rand() again.
+
+
+
+  int   r1, r2;                //rectangular-PDF random numbers
+  float s1, s2;                //error feedback buffers
+  float s = 0.5f;              //set to 0.0f for no noise shaping
+  float w = pow(2.0,bits-1);   //word length (usually bits=16)
+  float wi= 1.0f/w;            
+  float d = wi / RAND_MAX;     //dither amplitude (2 lsb)
+  float o = wi * 0.5f;         //remove dc offset
+  float in, tmp;
+  int   out;
+
+
+//for each sample...
+
+  r2=r1;                               //can make HP-TRI dither by
+  r1=rand();                           //subtracting previous rand()
+    
+  in += s * (s1 + s1 - s2);            //error feedback
+  tmp = in + o + d * (float)(r1 - r2); //dc offset and dither 
+  
+  out = (int)(w * tmp);                //truncate downwards
+  if(tmp<0.0f) out--;                  //this is faster than floor()
+
+  s2 = s1;                            
+  s1 = in - wi * (float)out;           //error
+
+
+
+-- 
+paul.kellett@maxim.abel.co.uk
+http://www.maxim.abel.co.uk
+*/
+
+
+/*
+16-to-8-bit first-order dither
+
+Type : First order error feedforward dithering code
+References : Posted by Jon Watte
+
+Notes : 
+This is about as simple a dithering algorithm as you can implement, but it's
+likely to sound better than just truncating to N bits.
+
+Note that you might not want to carry forward the full difference for infinity.
+It's probably likely that the worst performance hit comes from the saturation
+conditionals, which can be avoided with appropriate instructions on many DSPs
+and integer SIMD type instructions, or CMOV.
+
+Last, if sound quality is paramount (such as when going from > 16 bits to 16
+bits) you probably want to use a higher-order dither function found elsewhere
+on this site. 
+
+
+Code : 
+// This code will down-convert and dither a 16-bit signed short 
+// mono signal into an 8-bit unsigned char signal, using a first 
+// order forward-feeding error term dither. 
+
+#define uchar unsigned char 
+
+void dither_one_channel_16_to_8( short * input, uchar * output, int count, int * memory ) 
+{ 
+  int m = *memory; 
+  while( count-- > 0 ) { 
+    int i = *input++; 
+    i += m; 
+    int j = i + 32768 - 128; 
+    uchar o; 
+    if( j < 0 ) { 
+      o = 0; 
+    } 
+    else if( j > 65535 ) { 
+      o = 255; 
+    } 
+    else { 
+      o = (uchar)((j>>8)&0xff); 
+    } 
+    m = ((j-32768+128)-i); 
+    *output++ = o; 
+  } 
+  *memory = m; 
+} 
+*/
diff --git a/src/audio/portaudio/pa_common/pa_dither.h b/src/audio/portaudio/pa_common/pa_dither.h
new file mode 100644
index 0000000000000000000000000000000000000000..148db275fb8b77c3dbd51e19ae68cd537e5d08d9
--- /dev/null
+++ b/src/audio/portaudio/pa_common/pa_dither.h
@@ -0,0 +1,91 @@
+#ifndef PA_DITHER_H
+#define PA_DITHER_H
+/*
+ * $Id$
+ * Portable Audio I/O Library triangular dither generator
+ *
+ * Based on the Open Source API proposed by Ross Bencina
+ * Copyright (c) 1999-2002 Phil Burk, Ross Bencina
+ *
+ * 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.
+ */
+
+/** @file
+ @brief Functions for generating dither noise
+*/
+
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif /* __cplusplus */
+
+
+/** @brief State needed to generate a dither signal */
+typedef struct PaUtilTriangularDitherGenerator{
+    unsigned long previous;
+    unsigned long randSeed1;
+    unsigned long randSeed2;
+} PaUtilTriangularDitherGenerator;
+
+
+/** @brief Initialize dither state */
+void PaUtil_InitializeTriangularDitherState( PaUtilTriangularDitherGenerator *ditherState );
+
+
+/**
+ @brief Calculate 2 LSB dither signal with a triangular distribution.
+ Ranged for adding to a 1 bit right-shifted 32 bit integer
+ prior to >>15. eg:
+<pre>
+    signed long in = *
+    signed long dither = PaUtil_Generate16BitTriangularDither( ditherState );
+    signed short out = (signed short)(((in>>1) + dither) >> 15);
+</pre>
+ @return
+ A signed long with a range of +32767 to -32768
+*/
+signed long PaUtil_Generate16BitTriangularDither( PaUtilTriangularDitherGenerator *ditherState );
+
+
+/**
+ @brief Calculate 2 LSB dither signal with a triangular distribution.
+ Ranged for adding to a pre-scaled float.
+<pre>
+    float in = *
+    float dither = PaUtil_GenerateFloatTriangularDither( ditherState );
+    // use smaller scaler to prevent overflow when we add the dither
+    signed short out = (signed short)(in*(32766.0f) + dither );
+</pre>
+ @return
+ A float with a range of -2.0 to +1.99999.
+*/
+float PaUtil_GenerateFloatTriangularDither( PaUtilTriangularDitherGenerator *ditherState );
+
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif /* PA_DITHER_H */
diff --git a/src/audio/portaudio/pa_common/pa_endianness.h b/src/audio/portaudio/pa_common/pa_endianness.h
new file mode 100644
index 0000000000000000000000000000000000000000..3a573929b36edbf657441b868039bfee700e6752
--- /dev/null
+++ b/src/audio/portaudio/pa_common/pa_endianness.h
@@ -0,0 +1,111 @@
+#ifndef PA_ENDIANNESS_H
+#define PA_ENDIANNESS_H
+/*
+ * $Id$
+ * Portable Audio I/O Library current platform endianness macros
+ *
+ * Based on the Open Source API proposed by Ross Bencina
+ * Copyright (c) 1999-2002 Phil Burk, Ross Bencina
+ *
+ * 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.
+ */
+
+/** @file
+ @brief Configure endianness symbols for the target processor.
+
+ Arrange for either the PA_LITTLE_ENDIAN or PA_BIG_ENDIAN preprocessor symbols
+ to be defined. The one that is defined reflects the endianness of the target
+ platform and may be used to implement conditional compilation of byte-order
+ dependent code.
+
+ If either PA_LITTLE_ENDIAN or PA_BIG_ENDIAN is defined already, then no attempt
+ is made to override that setting. This may be useful if you have a better way
+ of determining the platform's endianness. The autoconf mechanism uses this for
+ example.
+
+ A PA_VALIDATE_ENDIANNESS macro is provided to compare the compile time
+ and runtime endiannes and raise an assertion if they don't match.
+*/
+
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif /* __cplusplus */
+
+
+#if defined(PA_LITTLE_ENDIAN) || defined(PA_BIG_ENDIAN)
+    /* endianness define has been set externally, such as by autoconf */
+
+    #if defined(PA_LITTLE_ENDIAN) && defined(PA_BIG_ENDIAN)
+    #error both PA_LITTLE_ENDIAN and PA_BIG_ENDIAN have been defined externally to pa_endianness.h - only one endianness at a time please
+    #endif
+
+#else
+    /* endianness define has not been set externally */
+
+    /* set PA_LITTLE_ENDIAN or PA_BIG_ENDIAN by testing well known platform specific defines */
+
+    #if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__)
+
+    #define PA_LITTLE_ENDIAN /* win32, assume intel byte order */
+
+    #else
+
+#endif
+
+    #if !defined(PA_LITTLE_ENDIAN) && !defined(PA_BIG_ENDIAN)
+    /*
+     If the following error is raised, you either need to modify the code above
+     to automatically determine the endianness from other symbols defined on your
+     platform, or define either PA_LITTLE_ENDIAN or PA_BIG_ENDIAN externally.
+    */
+    #error pa_endianness.h was unable to automatically determine the endianness of the target platform
+    #endif
+    
+#endif
+
+/* PA_VALIDATE_ENDIANNESS compares the compile time and runtime endianness,
+ and raises an assertion if they don't match. <assert.h> must be included in
+ the context in which this macro is used.
+*/
+#if defined(PA_LITTLE_ENDIAN)
+    #define PA_VALIDATE_ENDIANNESS \
+    { \
+        const long nativeOne = 1; \
+        assert( "PortAudio: compile time and runtime endianness don't match" && (((char *)&nativeOne)[0]) == 1 ); \
+    }
+#elif defined(PA_BIG_ENDIAN)
+    #define PA_VALIDATE_ENDIANNESS \
+    { \
+        const long nativeOne = 1; \
+        assert( "PortAudio: compile time and runtime endianness don't match" && (((char *)&nativeOne)[0]) == 0 ); \
+    }
+#endif
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif /* PA_ENDIANNESS_H */
diff --git a/src/audio/portaudio/pa_common/pa_front.c b/src/audio/portaudio/pa_common/pa_front.c
new file mode 100644
index 0000000000000000000000000000000000000000..3b4a026b5524e31fa88d9f87cda7bf1c1fdd0ebc
--- /dev/null
+++ b/src/audio/portaudio/pa_common/pa_front.c
@@ -0,0 +1,1976 @@
+/*
+ * $Id$
+ * Portable Audio I/O Library Multi-Host API front end
+ * Validate function parameters and manage multiple host APIs.
+ *
+ * Based on the Open Source API proposed by Ross Bencina
+ * Copyright (c) 1999-2002 Ross Bencina, 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.
+ */
+
+/* doxygen index page */
+/** @mainpage
+
+PortAudio is an open-source cross-platform �C� library for audio input
+and output. It is designed to simplify the porting of audio applications
+between various platforms, and also to simplify the development of audio
+software in general by hiding the complexities of device interfacing.
+
+See the PortAudio website for further information http://www.portaudio.com/
+
+This documentation pertains to PortAudio V19, API version 2.0 which is
+currently under development. API version 2.0 differs in a number of ways from
+previous versions, please consult the enhancement proposals for further details:
+http://www.portaudio.com/docs/proposals/index.html
+
+This documentation is under construction. Things you might be interested in
+include:
+
+- The PortAudio API 2.0, as documented in portaudio.h
+
+- The <a href="todo.html">TODO List</a>
+
+Feel free to pick an item off TODO list and fix/implement it. You may want to
+enquire about status on the PortAudio mailing list first.
+*/
+
+
+/** @file
+ @brief Implements public PortAudio API, checks some errors, forwards to
+ host API implementations.
+ 
+ Implements the functions defined in the PortAudio API, checks for
+ some parameter and state inconsistencies and forwards API requests to
+ specific Host API implementations (via the interface declared in
+ pa_hostapi.h), and Streams (via the interface declared in pa_stream.h).
+
+ This file handles initialization and termination of Host API
+ implementations via initializers stored in the paHostApiInitializers
+ global variable.
+
+ Some utility functions declared in pa_util.h are implemented in this file.
+
+ All PortAudio API functions can be conditionally compiled with logging code.
+ To compile with logging, define the PA_LOG_API_CALLS precompiler symbol.
+
+    @todo Consider adding host API specific error text in Pa_GetErrorText() for
+    paUnanticipatedHostError
+
+    @todo Consider adding a new error code for when (inputParameters == NULL)
+    && (outputParameters == NULL)
+
+    @todo review whether Pa_CloseStream() should call the interface's
+    CloseStream function if aborting the stream returns an error code.
+
+    @todo Create new error codes if a NULL buffer pointer, or a
+    zero frame count is passed to Pa_ReadStream or Pa_WriteStream.
+*/
+
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <memory.h>
+#include <string.h>
+#include <assert.h> /* needed by PA_VALIDATE_ENDIANNESS */
+
+#include "portaudio.h"
+#include "pa_util.h"
+#include "pa_endianness.h"
+#include "pa_types.h"
+#include "pa_hostapi.h"
+#include "pa_stream.h"
+
+#include "pa_trace.h"
+
+
+#define PA_VERSION_  1899
+#define PA_VERSION_TEXT_ "PortAudio V19-devel"
+
+
+
+/* #define PA_LOG_API_CALLS */
+
+/*
+    The basic format for log messages is described below. If you need to
+    add any log messages, please follow this format.
+ 
+    Function entry (void function):
+ 
+        "FunctionName called.\n"
+ 
+    Function entry (non void function):
+ 
+        "FunctionName called:\n"
+        "\tParam1Type param1: param1Value\n"
+        "\tParam2Type param2: param2Value\n"      (etc...)
+ 
+ 
+    Function exit (no return value):
+ 
+        "FunctionName returned.\n"
+ 
+    Function exit (simple return value):
+ 
+        "FunctionName returned:\n"
+        "\tReturnType: returnValue\n\n"
+ 
+    If the return type is an error code, the error text is displayed in ()
+ 
+    If the return type is not an error code, but has taken a special value
+    because an error occurred, then the reason for the error is shown in []
+ 
+    If the return type is a struct ptr, the struct is dumped.
+ 
+    See the code below for examples
+*/
+
+
+int Pa_GetVersion( void )
+{
+    return PA_VERSION_;
+}
+
+
+const char* Pa_GetVersionText( void )
+{
+    return PA_VERSION_TEXT_;
+}
+
+
+
+#define PA_LAST_HOST_ERROR_TEXT_LENGTH_  1024
+
+static char lastHostErrorText_[ PA_LAST_HOST_ERROR_TEXT_LENGTH_ + 1 ] = {0};
+
+static PaHostErrorInfo lastHostErrorInfo_ = { (PaHostApiTypeId)-1, 0, lastHostErrorText_ };
+
+
+void PaUtil_SetLastHostErrorInfo( PaHostApiTypeId hostApiType, long errorCode,
+        const char *errorText )
+{
+    lastHostErrorInfo_.hostApiType = hostApiType;
+    lastHostErrorInfo_.errorCode = errorCode;
+
+    strncpy( lastHostErrorText_, errorText, PA_LAST_HOST_ERROR_TEXT_LENGTH_ );
+}
+
+
+void PaUtil_DebugPrint( const char *format, ... )
+{
+    va_list ap;
+
+    va_start( ap, format );
+    vfprintf( stderr, format, ap );
+    va_end( ap );
+
+    fflush( stderr );
+}
+
+
+static PaUtilHostApiRepresentation **hostApis_ = 0;
+static int hostApisCount_ = 0;
+static int initializationCount_ = 0;
+static int deviceCount_ = 0;
+
+PaUtilStreamRepresentation *firstOpenStream_ = NULL;
+
+
+#define PA_IS_INITIALISED_ (initializationCount_ != 0)
+
+
+static int CountHostApiInitializers( void )
+{
+    int result = 0;
+
+    while( paHostApiInitializers[ result ] != 0 )
+        ++result;
+    return result;
+}
+
+
+static void TerminateHostApis( void )
+{
+    /* terminate in reverse order from initialization */
+
+    while( hostApisCount_ > 0 )
+    {
+        --hostApisCount_;
+        hostApis_[hostApisCount_]->Terminate( hostApis_[hostApisCount_] );
+    }
+    hostApisCount_ = 0;
+    deviceCount_ = 0;
+
+    if( hostApis_ != 0 )
+        PaUtil_FreeMemory( hostApis_ );
+    hostApis_ = 0;
+}
+
+
+static PaError InitializeHostApis( void )
+{
+    PaError result = paNoError;
+    int i, initializerCount, baseDeviceIndex;
+
+    initializerCount = CountHostApiInitializers();
+
+    hostApis_ = (PaUtilHostApiRepresentation**)PaUtil_AllocateMemory(
+            sizeof(PaUtilHostApiRepresentation*) * initializerCount );
+    if( !hostApis_ )
+    {
+        result = paInsufficientMemory;
+        goto error; 
+    }
+
+    hostApisCount_ = 0;
+    deviceCount_ = 0;
+    baseDeviceIndex = 0;
+
+    for( i=0; i< initializerCount; ++i )
+    {
+        hostApis_[hostApisCount_] = NULL;
+        result = paHostApiInitializers[i]( &hostApis_[hostApisCount_], hostApisCount_ );
+        if( result != paNoError )
+            goto error;
+
+        if( hostApis_[hostApisCount_] )
+        {
+
+            hostApis_[hostApisCount_]->privatePaFrontInfo.baseDeviceIndex = baseDeviceIndex;
+
+            if( hostApis_[hostApisCount_]->info.defaultInputDevice != paNoDevice )
+                hostApis_[hostApisCount_]->info.defaultInputDevice += baseDeviceIndex;
+
+            if( hostApis_[hostApisCount_]->info.defaultOutputDevice != paNoDevice )
+                hostApis_[hostApisCount_]->info.defaultOutputDevice += baseDeviceIndex;
+
+            baseDeviceIndex += hostApis_[hostApisCount_]->info.deviceCount;
+            deviceCount_ += hostApis_[hostApisCount_]->info.deviceCount;
+
+            ++hostApisCount_;
+        }
+    }
+
+    return result;
+
+error:
+    TerminateHostApis();
+    return result;
+}
+
+
+/*
+    FindHostApi() finds the index of the host api to which
+    <device> belongs and returns it. if <hostSpecificDeviceIndex> is
+    non-null, the host specific device index is returned in it.
+    returns -1 if <device> is out of range.
+ 
+*/
+static int FindHostApi( PaDeviceIndex device, int *hostSpecificDeviceIndex )
+{
+    int i=0;
+
+    if( !PA_IS_INITIALISED_ )
+        return -1;
+
+    if( device < 0 )
+        return -1;
+
+    while( i < hostApisCount_
+            && device >= hostApis_[i]->info.deviceCount )
+    {
+
+        device -= hostApis_[i]->info.deviceCount;
+        ++i;
+    }
+
+    if( i >= hostApisCount_ )
+        return -1;
+
+    if( hostSpecificDeviceIndex )
+        *hostSpecificDeviceIndex = device;
+
+    return i;
+}
+
+
+static void AddOpenStream( PaStream* stream )
+{
+    ((PaUtilStreamRepresentation*)stream)->nextOpenStream = firstOpenStream_;
+    firstOpenStream_ = (PaUtilStreamRepresentation*)stream;
+}
+
+
+static void RemoveOpenStream( PaStream* stream )
+{
+    PaUtilStreamRepresentation *previous = NULL;
+    PaUtilStreamRepresentation *current = firstOpenStream_;
+
+    while( current != NULL )
+    {
+        if( ((PaStream*)current) == stream )
+        {
+            if( previous == NULL )
+            {
+                firstOpenStream_ = current->nextOpenStream;
+            }
+            else
+            {
+                previous->nextOpenStream = current->nextOpenStream;
+            }
+            return;
+        }
+        else
+        {
+            previous = current;
+            current = current->nextOpenStream;
+        }
+    }
+}
+
+
+static void CloseOpenStreams( void )
+{
+    /* we call Pa_CloseStream() here to ensure that the same destruction
+        logic is used for automatically closed streams */
+
+    while( firstOpenStream_ != NULL )
+        Pa_CloseStream( firstOpenStream_ );
+}
+
+
+PaError Pa_Initialize( void )
+{
+    PaError result;
+
+#ifdef PA_LOG_API_CALLS
+    PaUtil_DebugPrint( "Pa_Initialize called.\n" );
+#endif
+
+    if( PA_IS_INITIALISED_ )
+    {
+        ++initializationCount_;
+        result = paNoError;
+    }
+    else
+    {
+        PA_VALIDATE_TYPE_SIZES;
+        PA_VALIDATE_ENDIANNESS;
+        
+        PaUtil_InitializeClock();
+        PaUtil_ResetTraceMessages();
+
+        result = InitializeHostApis();
+        if( result == paNoError )
+            ++initializationCount_;
+    }
+
+#ifdef PA_LOG_API_CALLS
+    PaUtil_DebugPrint( "Pa_Initialize returned:\n" );
+    PaUtil_DebugPrint( "\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
+#endif
+
+    return result;
+}
+
+
+PaError Pa_Terminate( void )
+{
+    PaError result;
+
+#ifdef PA_LOG_API_CALLS
+    PaUtil_DebugPrint("Pa_Terminate called.\n" );
+#endif
+
+    if( PA_IS_INITIALISED_ )
+    {
+        if( --initializationCount_ == 0 )
+        {
+            CloseOpenStreams();
+
+            TerminateHostApis();
+
+            PaUtil_DumpTraceMessages();
+        }
+        result = paNoError;
+    }
+    else
+    {
+        result=  paNotInitialized;
+    }
+
+#ifdef PA_LOG_API_CALLS
+    PaUtil_DebugPrint("Pa_Terminate returned:\n" );
+    PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
+#endif
+
+    return result;
+}
+
+
+const PaHostErrorInfo* Pa_GetLastHostErrorInfo( void )
+{
+    return &lastHostErrorInfo_;
+}
+
+
+const char *Pa_GetErrorText( PaError errorCode )
+{
+    const char *result;
+
+    switch( errorCode )
+    {
+    case paNoError:                  result = "Success"; break;
+    case paNotInitialized:           result = "PortAudio not initialized"; break;
+    /** @todo could catenate the last host error text to result in the case of paUnanticipatedHostError */
+    case paUnanticipatedHostError:   result = "Unanticipated host error"; break;
+    case paInvalidChannelCount:      result = "Invalid number of channels"; break;
+    case paInvalidSampleRate:        result = "Invalid sample rate"; break;
+    case paInvalidDevice:            result = "Invalid device"; break;
+    case paInvalidFlag:              result = "Invalid flag"; break;
+    case paSampleFormatNotSupported: result = "Sample format not supported"; break;
+    case paBadIODeviceCombination:   result = "Illegal combination of I/O devices"; break;
+    case paInsufficientMemory:       result = "Insufficient memory"; break;
+    case paBufferTooBig:             result = "Buffer too big"; break;
+    case paBufferTooSmall:           result = "Buffer too small"; break;
+    case paNullCallback:             result = "No callback routine specified"; break;
+    case paBadStreamPtr:             result = "Invalid stream pointer"; break;
+    case paTimedOut:                 result = "Wait timed out"; break;
+    case paInternalError:            result = "Internal PortAudio error"; break;
+    case paDeviceUnavailable:        result = "Device unavailable"; break;
+    case paIncompatibleHostApiSpecificStreamInfo:   result = "Incompatible host API specific stream info"; break;
+    case paStreamIsStopped:          result = "Stream is stopped"; break;
+    case paStreamIsNotStopped:       result = "Stream is not stopped"; break;
+    case paInputOverflowed:          result = "Input overflowed"; break;
+    case paOutputUnderflowed:        result = "Output underflowed"; break;
+    case paHostApiNotFound:          result = "Host API not found"; break;
+    case paInvalidHostApi:           result = "Invalid host API"; break;
+    case paCanNotReadFromACallbackStream:       result = "Can't read from a callback stream"; break;
+    case paCanNotWriteToACallbackStream:        result = "Can't write to a callback stream"; break;
+    case paCanNotReadFromAnOutputOnlyStream:    result = "Can't read from an output only stream"; break;
+    case paCanNotWriteToAnInputOnlyStream:      result = "Can't write to an input only stream"; break;
+    default:                         result = "Illegal error number"; break;
+    }
+    return result;
+}
+
+
+PaHostApiIndex Pa_HostApiTypeIdToHostApiIndex( PaHostApiTypeId type )
+{
+    PaHostApiIndex result;
+    int i;
+    
+#ifdef PA_LOG_API_CALLS
+    PaUtil_DebugPrint("Pa_HostApiTypeIdToHostApiIndex called:\n" );
+    PaUtil_DebugPrint("\tPaHostApiTypeId type: %d\n", type );
+#endif
+
+    if( !PA_IS_INITIALISED_ )
+    {
+        result = paNotInitialized;
+    }
+    else
+    {
+        result = paHostApiNotFound;
+        
+        for( i=0; i < hostApisCount_; ++i )
+        {
+            if( hostApis_[i]->info.type == type )
+            {
+                result = i;
+                break;
+            }         
+        }
+    }
+
+#ifdef PA_LOG_API_CALLS
+    PaUtil_DebugPrint("Pa_HostApiTypeIdToHostApiIndex returned:\n" );
+    if( result < 0 )
+        PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
+    else
+        PaUtil_DebugPrint("\tPaHostApiIndex: %d\n\n", result );
+#endif
+
+    return result;
+}
+
+
+PaError PaUtil_GetHostApiRepresentation( struct PaUtilHostApiRepresentation **hostApi,
+        PaHostApiTypeId type )
+{
+    PaError result;
+    int i;
+    
+    if( !PA_IS_INITIALISED_ )
+    {
+        result = paNotInitialized;
+    }
+    else
+    {
+        result = paHostApiNotFound;
+                
+        for( i=0; i < hostApisCount_; ++i )
+        {
+            if( hostApis_[i]->info.type == type )
+            {
+                *hostApi = hostApis_[i];
+                result = paNoError;
+                break;
+            }
+        }
+    }
+
+    return result;
+}
+
+
+PaError PaUtil_DeviceIndexToHostApiDeviceIndex(
+        PaDeviceIndex *hostApiDevice, PaDeviceIndex device, struct PaUtilHostApiRepresentation *hostApi )
+{
+    PaError result;
+    PaDeviceIndex x;
+    
+    x = device - hostApi->privatePaFrontInfo.baseDeviceIndex;
+
+    if( x < 0 || x >= hostApi->info.deviceCount )
+    {
+        result = paInvalidDevice;
+    }
+    else
+    {
+        *hostApiDevice = x;
+        result = paNoError;
+    }
+
+    return result;
+}
+
+
+PaHostApiIndex Pa_GetHostApiCount( void )
+{
+    int result;
+
+#ifdef PA_LOG_API_CALLS
+    PaUtil_DebugPrint("Pa_GetHostApiCount called.\n" );
+#endif
+
+    if( !PA_IS_INITIALISED_ )
+    {
+        result = paNotInitialized;
+    }
+    else
+    {
+        result = hostApisCount_;
+    }
+
+#ifdef PA_LOG_API_CALLS
+    PaUtil_DebugPrint("Pa_GetHostApiCount returned:\n" );
+    if( result < 0 )
+        PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
+    else
+        PaUtil_DebugPrint("\tPaHostApiIndex %d\n\n", result );
+#endif
+
+    return result;
+}
+
+
+PaHostApiIndex Pa_GetDefaultHostApi( void )
+{
+    int result;
+
+#ifdef PA_LOG_API_CALLS
+    PaUtil_DebugPrint("Pa_GetDefaultHostApi called.\n" );
+#endif
+
+    if( !PA_IS_INITIALISED_ )
+    {
+        result = paNotInitialized;
+    }
+    else
+    {
+        result = paDefaultHostApiIndex;
+
+        /* internal consistency check: make sure that the default host api
+         index is within range */
+
+        if( result < 0 || result >= hostApisCount_ )
+        {
+            result = paInternalError;
+        }
+    }
+
+#ifdef PA_LOG_API_CALLS
+    PaUtil_DebugPrint("Pa_GetDefaultHostApi returned:\n" );
+    if( result < 0 )
+        PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
+    else
+        PaUtil_DebugPrint("\tPaHostApiIndex %d\n\n", result );
+#endif
+
+    return result;
+}
+
+
+const PaHostApiInfo* Pa_GetHostApiInfo( PaHostApiIndex hostApi )
+{
+    PaHostApiInfo *info;
+
+#ifdef PA_LOG_API_CALLS
+    PaUtil_DebugPrint("Pa_GetHostApiInfo called:\n" );
+    PaUtil_DebugPrint("\tPaHostApiIndex hostApi: %d\n", hostApi );
+#endif
+
+    if( !PA_IS_INITIALISED_ )
+    {
+        info = NULL;
+
+#ifdef PA_LOG_API_CALLS
+        PaUtil_DebugPrint("Pa_GetHostApiInfo returned:\n" );
+        PaUtil_DebugPrint("\tPaHostApiInfo*: NULL [ PortAudio not initialized ]\n\n" );
+#endif
+
+    }
+    else if( hostApi < 0 || hostApi >= hostApisCount_ )
+    {
+        info = NULL;
+        
+#ifdef PA_LOG_API_CALLS
+        PaUtil_DebugPrint("Pa_GetHostApiInfo returned:\n" );
+        PaUtil_DebugPrint("\tPaHostApiInfo*: NULL [ hostApi out of range ]\n\n" );
+#endif
+
+    }
+    else
+    {
+        info = &hostApis_[hostApi]->info;
+
+#ifdef PA_LOG_API_CALLS
+        PaUtil_DebugPrint("Pa_GetHostApiInfo returned:\n" );
+        PaUtil_DebugPrint("\tPaHostApiInfo*: 0x%p\n", info );
+        PaUtil_DebugPrint("\t{" );
+        PaUtil_DebugPrint("\t\tint structVersion: %d\n", info->structVersion );
+        PaUtil_DebugPrint("\t\tPaHostApiTypeId type: %d\n", info->type );
+        PaUtil_DebugPrint("\t\tconst char *name: %s\n\n", info->name );
+        PaUtil_DebugPrint("\t}\n\n" );
+#endif
+
+    }
+
+     return info;
+}
+
+
+PaDeviceIndex Pa_HostApiDeviceIndexToDeviceIndex( PaHostApiIndex hostApi, int hostApiDeviceIndex )
+{
+    PaDeviceIndex result;
+
+#ifdef PA_LOG_API_CALLS
+    PaUtil_DebugPrint("Pa_HostApiDeviceIndexToPaDeviceIndex called:\n" );
+    PaUtil_DebugPrint("\tPaHostApiIndex hostApi: %d\n", hostApi );
+    PaUtil_DebugPrint("\tint hostApiDeviceIndex: %d\n", hostApiDeviceIndex );
+#endif
+
+    if( !PA_IS_INITIALISED_ )
+    {
+        result = paNotInitialized;
+    }
+    else
+    {
+        if( hostApi < 0 || hostApi >= hostApisCount_ )
+        {
+            result = paInvalidHostApi;
+        }
+        else
+        {
+            if( hostApiDeviceIndex < 0 ||
+                    hostApiDeviceIndex >= hostApis_[hostApi]->info.deviceCount )
+            {
+                result = paInvalidDevice;
+            }
+            else
+            {
+                result = hostApis_[hostApi]->privatePaFrontInfo.baseDeviceIndex + hostApiDeviceIndex;
+            }
+        }
+    }
+
+#ifdef PA_LOG_API_CALLS
+    PaUtil_DebugPrint("Pa_HostApiDeviceIndexToPaDeviceIndex returned:\n" );
+    if( result < 0 )
+        PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
+    else
+        PaUtil_DebugPrint("\tPaDeviceIndex: %d\n\n", result );
+#endif
+
+    return result;
+}
+
+
+PaDeviceIndex Pa_GetDeviceCount( void )
+{
+    PaDeviceIndex result;
+
+#ifdef PA_LOG_API_CALLS
+    PaUtil_DebugPrint("Pa_GetDeviceCount called.\n" );
+#endif
+
+    if( !PA_IS_INITIALISED_ )
+    {
+        result = paNotInitialized;
+    }
+    else
+    {
+        result = deviceCount_;
+    }
+
+#ifdef PA_LOG_API_CALLS
+    PaUtil_DebugPrint("Pa_GetDeviceCount returned:\n" );
+    if( result < 0 )
+        PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
+    else
+        PaUtil_DebugPrint("\tPaDeviceIndex: %d\n\n", result );
+#endif
+
+    return result;
+}
+
+
+PaDeviceIndex Pa_GetDefaultInputDevice( void )
+{
+    PaHostApiIndex hostApi;
+    PaDeviceIndex result;
+
+#ifdef PA_LOG_API_CALLS
+    PaUtil_DebugPrint("Pa_GetDefaultInputDevice called.\n" );
+#endif
+
+    hostApi = Pa_GetDefaultHostApi();
+    if( hostApi < 0 )
+    {
+        result = paNoDevice;
+    }
+    else
+    {
+        result = hostApis_[hostApi]->info.defaultInputDevice;
+    }
+
+#ifdef PA_LOG_API_CALLS
+    PaUtil_DebugPrint("Pa_GetDefaultInputDevice returned:\n" );
+    PaUtil_DebugPrint("\tPaDeviceIndex: %d\n\n", result );
+#endif
+
+    return result;
+}
+
+
+PaDeviceIndex Pa_GetDefaultOutputDevice( void )
+{
+    PaHostApiIndex hostApi;
+    PaDeviceIndex result;
+    
+#ifdef PA_LOG_API_CALLS
+    PaUtil_DebugPrint("Pa_GetDefaultOutputDevice called.\n" );
+#endif
+
+    hostApi = Pa_GetDefaultHostApi();
+    if( hostApi < 0 )
+    {
+        result = paNoDevice;
+    }
+    else
+    {
+        result = hostApis_[hostApi]->info.defaultOutputDevice;
+    }
+
+#ifdef PA_LOG_API_CALLS
+    PaUtil_DebugPrint("Pa_GetDefaultOutputDevice returned:\n" );
+    PaUtil_DebugPrint("\tPaDeviceIndex: %d\n\n", result );
+#endif
+
+    return result;
+}
+
+
+const PaDeviceInfo* Pa_GetDeviceInfo( PaDeviceIndex device )
+{
+    int hostSpecificDeviceIndex;
+    int hostApiIndex = FindHostApi( device, &hostSpecificDeviceIndex );
+    PaDeviceInfo *result;
+
+
+#ifdef PA_LOG_API_CALLS
+    PaUtil_DebugPrint("Pa_GetDeviceInfo called:\n" );
+    PaUtil_DebugPrint("\tPaDeviceIndex device: %d\n", device );
+#endif
+
+    if( hostApiIndex < 0 )
+    {
+        result = NULL;
+
+#ifdef PA_LOG_API_CALLS
+        PaUtil_DebugPrint("Pa_GetDeviceInfo returned:\n" );
+        PaUtil_DebugPrint("\tPaDeviceInfo* NULL [ invalid device index ]\n\n" );
+#endif
+
+    }
+    else
+    {
+        result = hostApis_[hostApiIndex]->deviceInfos[ hostSpecificDeviceIndex ];
+
+#ifdef PA_LOG_API_CALLS
+        PaUtil_DebugPrint("Pa_GetDeviceInfo returned:\n" );
+        PaUtil_DebugPrint("\tPaDeviceInfo*: 0x%p:\n", result );
+        PaUtil_DebugPrint("\t{\n" );
+
+        PaUtil_DebugPrint("\t\tint structVersion: %d\n", result->structVersion );
+        PaUtil_DebugPrint("\t\tconst char *name: %s\n", result->name );
+        PaUtil_DebugPrint("\t\tPaHostApiIndex hostApi: %d\n", result->hostApi );
+        PaUtil_DebugPrint("\t\tint maxInputChannels: %d\n", result->maxInputChannels );
+        PaUtil_DebugPrint("\t\tint maxOutputChannels: %d\n", result->maxOutputChannels );
+        PaUtil_DebugPrint("\t}\n\n" );
+#endif
+
+    }
+
+    return result;
+}
+
+
+/*
+    SampleFormatIsValid() returns 1 if sampleFormat is a sample format
+    defined in portaudio.h, or 0 otherwise.
+*/
+static int SampleFormatIsValid( PaSampleFormat format )
+{
+    switch( format & ~paNonInterleaved )
+    {
+    case paFloat32: return 1;
+    case paInt16: return 1;
+    case paInt32: return 1;
+    case paInt24: return 1;
+    case paInt8: return 1;
+    case paUInt8: return 1;
+    case paCustomFormat: return 1;
+    default: return 0;
+    }
+}
+
+/*
+    NOTE: make sure this validation list is kept syncronised with the one in
+            pa_hostapi.h
+
+    ValidateOpenStreamParameters() checks that parameters to Pa_OpenStream()
+    conform to the expected values as described below. This function is
+    also designed to be used with the proposed Pa_IsFormatSupported() function.
+    
+    There are basically two types of validation that could be performed:
+    Generic conformance validation, and device capability mismatch
+    validation. This function performs only generic conformance validation.
+    Validation that would require knowledge of device capabilities is
+    not performed because of potentially complex relationships between
+    combinations of parameters - for example, even if the sampleRate
+    seems ok, it might not be for a duplex stream - we have no way of
+    checking this in an API-neutral way, so we don't try.
+ 
+    On success the function returns PaNoError and fills in hostApi,
+    hostApiInputDeviceID, and hostApiOutputDeviceID fields. On failure
+    the function returns an error code indicating the first encountered
+    parameter error.
+ 
+ 
+    If ValidateOpenStreamParameters() returns paNoError, the following
+    assertions are guaranteed to be true.
+ 
+    - at least one of inputParameters & outputParmeters is valid (not NULL)
+
+    - if inputParameters & outputParmeters are both valid, that
+        inputParameters->device & outputParmeters->device  both use the same host api
+ 
+    PaDeviceIndex inputParameters->device
+        - is within range (0 to Pa_GetDeviceCount-1) Or:
+        - is paUseHostApiSpecificDeviceSpecification and
+            inputParameters->hostApiSpecificStreamInfo is non-NULL and refers
+            to a valid host api
+
+    int inputParameters->channelCount
+        - if inputParameters->device is not paUseHostApiSpecificDeviceSpecification, channelCount is > 0
+        - upper bound is NOT validated against device capabilities
+ 
+    PaSampleFormat inputParameters->sampleFormat
+        - is one of the sample formats defined in portaudio.h
+
+    void *inputParameters->hostApiSpecificStreamInfo
+        - if supplied its hostApi field matches the input device's host Api
+ 
+    PaDeviceIndex outputParmeters->device
+        - is within range (0 to Pa_GetDeviceCount-1)
+ 
+    int outputParmeters->channelCount
+        - if inputDevice is valid, channelCount is > 0
+        - upper bound is NOT validated against device capabilities
+ 
+    PaSampleFormat outputParmeters->sampleFormat
+        - is one of the sample formats defined in portaudio.h
+        
+    void *outputParmeters->hostApiSpecificStreamInfo
+        - if supplied its hostApi field matches the output device's host Api
+ 
+    double sampleRate
+        - is not an 'absurd' rate (less than 1000. or greater than 200000.)
+        - sampleRate is NOT validated against device capabilities
+ 
+    PaStreamFlags streamFlags
+        - unused platform neutral flags are zero
+        - paNeverDropInput is only used for full-duplex callback streams with
+            variable buffer size (paFramesPerBufferUnspecified)
+*/
+static PaError ValidateOpenStreamParameters(
+    const PaStreamParameters *inputParameters,
+    const PaStreamParameters *outputParameters,
+    double sampleRate,
+    unsigned long framesPerBuffer,
+    PaStreamFlags streamFlags,
+    PaStreamCallback *streamCallback,
+    PaUtilHostApiRepresentation **hostApi,
+    PaDeviceIndex *hostApiInputDevice,
+    PaDeviceIndex *hostApiOutputDevice )
+{
+    int inputHostApiIndex  = -1, /* Surpress uninitialised var warnings: compiler does */
+        outputHostApiIndex = -1; /* not see that if inputParameters and outputParame-  */
+                                 /* ters are both nonzero, these indices are set.      */
+
+    if( (inputParameters == NULL) && (outputParameters == NULL) )
+    {
+        return paInvalidDevice; /** @todo should be a new error code "invalid device parameters" or something */
+    }
+    else
+    {
+        if( inputParameters == NULL )
+        {
+            *hostApiInputDevice = paNoDevice;
+        }
+        else if( inputParameters->device == paUseHostApiSpecificDeviceSpecification )
+        {
+            if( inputParameters->hostApiSpecificStreamInfo )
+            {
+                inputHostApiIndex = Pa_HostApiTypeIdToHostApiIndex(
+                        ((PaUtilHostApiSpecificStreamInfoHeader*)inputParameters->hostApiSpecificStreamInfo)->hostApiType );
+
+                if( inputHostApiIndex != -1 )
+                {
+                    *hostApiInputDevice = paUseHostApiSpecificDeviceSpecification;
+                    *hostApi = hostApis_[inputHostApiIndex];
+                }
+                else
+                {
+                    return paInvalidDevice;
+                }
+            }
+            else
+            {
+                return paInvalidDevice;
+            }
+        }
+        else
+        {
+            if( inputParameters->device < 0 || inputParameters->device >= deviceCount_ )
+                return paInvalidDevice;
+
+            inputHostApiIndex = FindHostApi( inputParameters->device, hostApiInputDevice );
+            if( inputHostApiIndex < 0 )
+                return paInternalError;
+
+            *hostApi = hostApis_[inputHostApiIndex];
+
+            if( inputParameters->channelCount <= 0 )
+                return paInvalidChannelCount;
+
+            if( !SampleFormatIsValid( inputParameters->sampleFormat ) )
+                return paSampleFormatNotSupported;
+
+            if( inputParameters->hostApiSpecificStreamInfo != NULL )
+            {
+                if( ((PaUtilHostApiSpecificStreamInfoHeader*)inputParameters->hostApiSpecificStreamInfo)->hostApiType
+                        != (*hostApi)->info.type )
+                    return paIncompatibleHostApiSpecificStreamInfo;
+            }
+        }
+
+        if( outputParameters == NULL )
+        {
+            *hostApiOutputDevice = paNoDevice;
+        }
+        else if( outputParameters->device == paUseHostApiSpecificDeviceSpecification  )
+        {
+            if( outputParameters->hostApiSpecificStreamInfo )
+            {
+                outputHostApiIndex = Pa_HostApiTypeIdToHostApiIndex(
+                        ((PaUtilHostApiSpecificStreamInfoHeader*)outputParameters->hostApiSpecificStreamInfo)->hostApiType );
+
+                if( outputHostApiIndex != -1 )
+                {
+                    *hostApiOutputDevice = paUseHostApiSpecificDeviceSpecification;
+                    *hostApi = hostApis_[outputHostApiIndex];
+                }
+                else
+                {
+                    return paInvalidDevice;
+                }
+            }
+            else
+            {
+                return paInvalidDevice;
+            }
+        }
+        else
+        {
+            if( outputParameters->device < 0 || outputParameters->device >= deviceCount_ )
+                return paInvalidDevice;
+
+            outputHostApiIndex = FindHostApi( outputParameters->device, hostApiOutputDevice );
+            if( outputHostApiIndex < 0 )
+                return paInternalError;
+
+            *hostApi = hostApis_[outputHostApiIndex];
+
+            if( outputParameters->channelCount <= 0 )
+                return paInvalidChannelCount;
+
+            if( !SampleFormatIsValid( outputParameters->sampleFormat ) )
+                return paSampleFormatNotSupported;
+
+            if( outputParameters->hostApiSpecificStreamInfo != NULL )
+            {
+                if( ((PaUtilHostApiSpecificStreamInfoHeader*)outputParameters->hostApiSpecificStreamInfo)->hostApiType
+                        != (*hostApi)->info.type )
+                    return paIncompatibleHostApiSpecificStreamInfo;
+            }
+        }   
+
+        if( (inputParameters != NULL) && (outputParameters != NULL) )
+        {
+            /* ensure that both devices use the same API */
+            if( inputHostApiIndex != outputHostApiIndex )
+                return paBadIODeviceCombination;
+        }
+    }
+    
+    
+    /* Check for absurd sample rates. */
+    if( (sampleRate < 1000.0) || (sampleRate > 200000.0) )
+        return paInvalidSampleRate;
+
+    if( ((streamFlags & ~paPlatformSpecificFlags) & ~(paClipOff | paDitherOff | paNeverDropInput | paPrimeOutputBuffersUsingStreamCallback ) ) != 0 )
+        return paInvalidFlag;
+
+    if( streamFlags & paNeverDropInput )
+    {
+        /* must be a callback stream */
+        if( !streamCallback )
+             return paInvalidFlag;
+
+        /* must be a full duplex stream */
+        if( (inputParameters == NULL) || (outputParameters == NULL) )
+            return paInvalidFlag;
+
+        /* must use paFramesPerBufferUnspecified */
+        if( framesPerBuffer != paFramesPerBufferUnspecified )
+            return paInvalidFlag;
+    }
+    
+    return paNoError;
+}
+
+
+PaError Pa_IsFormatSupported( const PaStreamParameters *inputParameters,
+                              const PaStreamParameters *outputParameters,
+                              double sampleRate )
+{
+    PaError result;
+    PaUtilHostApiRepresentation *hostApi;
+    PaDeviceIndex hostApiInputDevice, hostApiOutputDevice;
+    PaStreamParameters hostApiInputParameters, hostApiOutputParameters;
+    PaStreamParameters *hostApiInputParametersPtr, *hostApiOutputParametersPtr;
+
+
+#ifdef PA_LOG_API_CALLS
+    PaUtil_DebugPrint("Pa_IsFormatSupported called:\n" );
+
+    if( inputParameters == NULL ){
+        PaUtil_DebugPrint("\tPaStreamParameters *inputParameters: NULL\n" );
+    }else{
+        PaUtil_DebugPrint("\tPaStreamParameters *inputParameters: 0x%p\n", inputParameters );
+        PaUtil_DebugPrint("\tPaDeviceIndex inputParameters->device: %d\n", inputParameters->device );
+        PaUtil_DebugPrint("\tint inputParameters->channelCount: %d\n", inputParameters->channelCount );
+        PaUtil_DebugPrint("\tPaSampleFormat inputParameters->sampleFormat: %d\n", inputParameters->sampleFormat );
+        PaUtil_DebugPrint("\tPaTime inputParameters->suggestedLatency: %f\n", inputParameters->suggestedLatency );
+        PaUtil_DebugPrint("\tvoid *inputParameters->hostApiSpecificStreamInfo: 0x%p\n", inputParameters->hostApiSpecificStreamInfo );
+    }
+
+    if( outputParameters == NULL ){
+        PaUtil_DebugPrint("\tPaStreamParameters *outputParameters: NULL\n" );
+    }else{
+        PaUtil_DebugPrint("\tPaStreamParameters *outputParameters: 0x%p\n", outputParameters );
+        PaUtil_DebugPrint("\tPaDeviceIndex outputParameters->device: %d\n", outputParameters->device );
+        PaUtil_DebugPrint("\tint outputParameters->channelCount: %d\n", outputParameters->channelCount );
+        PaUtil_DebugPrint("\tPaSampleFormat outputParameters->sampleFormat: %d\n", outputParameters->sampleFormat );
+        PaUtil_DebugPrint("\tPaTime outputParameters->suggestedLatency: %f\n", outputParameters->suggestedLatency );
+        PaUtil_DebugPrint("\tvoid *outputParameters->hostApiSpecificStreamInfo: 0x%p\n", outputParameters->hostApiSpecificStreamInfo );
+    }
+    
+    PaUtil_DebugPrint("\tdouble sampleRate: %g\n", sampleRate );
+#endif
+
+    if( !PA_IS_INITIALISED_ )
+    {
+        result = paNotInitialized;
+
+#ifdef PA_LOG_API_CALLS
+        PaUtil_DebugPrint("Pa_IsFormatSupported returned:\n" );
+        PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
+#endif
+        return result;
+    }
+
+    result = ValidateOpenStreamParameters( inputParameters,
+                                           outputParameters,
+                                           sampleRate, 0, paNoFlag, 0,
+                                           &hostApi,
+                                           &hostApiInputDevice,
+                                           &hostApiOutputDevice );
+    if( result != paNoError )
+    {
+#ifdef PA_LOG_API_CALLS
+        PaUtil_DebugPrint("Pa_IsFormatSupported returned:\n" );
+        PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
+#endif
+        return result;
+    }
+    
+
+    if( inputParameters )
+    {
+        hostApiInputParameters.device = hostApiInputDevice;
+        hostApiInputParameters.channelCount = inputParameters->channelCount;
+        hostApiInputParameters.sampleFormat = inputParameters->sampleFormat;
+        hostApiInputParameters.suggestedLatency = inputParameters->suggestedLatency;
+        hostApiInputParameters.hostApiSpecificStreamInfo = inputParameters->hostApiSpecificStreamInfo;
+        hostApiInputParametersPtr = &hostApiInputParameters;
+    }
+    else
+    {
+        hostApiInputParametersPtr = NULL;
+    }
+
+    if( outputParameters )
+    {
+        hostApiOutputParameters.device = hostApiOutputDevice;
+        hostApiOutputParameters.channelCount = outputParameters->channelCount;
+        hostApiOutputParameters.sampleFormat = outputParameters->sampleFormat;
+        hostApiOutputParameters.suggestedLatency = outputParameters->suggestedLatency;
+        hostApiOutputParameters.hostApiSpecificStreamInfo = outputParameters->hostApiSpecificStreamInfo;
+        hostApiOutputParametersPtr = &hostApiOutputParameters;
+    }
+    else
+    {
+        hostApiOutputParametersPtr = NULL;
+    }
+
+    result = hostApi->IsFormatSupported( hostApi,
+                                  hostApiInputParametersPtr, hostApiOutputParametersPtr,
+                                  sampleRate );
+
+#ifdef PA_LOG_API_CALLS
+    PaUtil_DebugPrint("Pa_OpenStream returned:\n" );
+    if( result == paFormatIsSupported )
+        PaUtil_DebugPrint("\tPaError: 0 [ paFormatIsSupported ]\n\n" );
+    else
+        PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
+#endif
+
+    return result;
+}
+
+
+PaError Pa_OpenStream( PaStream** stream,
+                       const PaStreamParameters *inputParameters,
+                       const PaStreamParameters *outputParameters,
+                       double sampleRate,
+                       unsigned long framesPerBuffer,
+                       PaStreamFlags streamFlags,
+                       PaStreamCallback *streamCallback,
+                       void *userData )
+{
+    PaError result;
+    PaUtilHostApiRepresentation *hostApi;
+    PaDeviceIndex hostApiInputDevice, hostApiOutputDevice;
+    PaStreamParameters hostApiInputParameters, hostApiOutputParameters;
+    PaStreamParameters *hostApiInputParametersPtr, *hostApiOutputParametersPtr;
+
+
+#ifdef PA_LOG_API_CALLS
+    PaUtil_DebugPrint("Pa_OpenStream called:\n" );
+    PaUtil_DebugPrint("\tPaStream** stream: 0x%p\n", stream );
+
+    if( inputParameters == NULL ){
+        PaUtil_DebugPrint("\tPaStreamParameters *inputParameters: NULL\n" );
+    }else{
+        PaUtil_DebugPrint("\tPaStreamParameters *inputParameters: 0x%p\n", inputParameters );
+        PaUtil_DebugPrint("\tPaDeviceIndex inputParameters->device: %d\n", inputParameters->device );
+        PaUtil_DebugPrint("\tint inputParameters->channelCount: %d\n", inputParameters->channelCount );
+        PaUtil_DebugPrint("\tPaSampleFormat inputParameters->sampleFormat: %d\n", inputParameters->sampleFormat );
+        PaUtil_DebugPrint("\tPaTime inputParameters->suggestedLatency: %f\n", inputParameters->suggestedLatency );
+        PaUtil_DebugPrint("\tvoid *inputParameters->hostApiSpecificStreamInfo: 0x%p\n", inputParameters->hostApiSpecificStreamInfo );
+    }
+
+    if( outputParameters == NULL ){
+        PaUtil_DebugPrint("\tPaStreamParameters *outputParameters: NULL\n" );
+    }else{
+        PaUtil_DebugPrint("\tPaStreamParameters *outputParameters: 0x%p\n", outputParameters );
+        PaUtil_DebugPrint("\tPaDeviceIndex outputParameters->device: %d\n", outputParameters->device );
+        PaUtil_DebugPrint("\tint outputParameters->channelCount: %d\n", outputParameters->channelCount );
+        PaUtil_DebugPrint("\tPaSampleFormat outputParameters->sampleFormat: %d\n", outputParameters->sampleFormat );
+        PaUtil_DebugPrint("\tPaTime outputParameters->suggestedLatency: %f\n", outputParameters->suggestedLatency );
+        PaUtil_DebugPrint("\tvoid *outputParameters->hostApiSpecificStreamInfo: 0x%p\n", outputParameters->hostApiSpecificStreamInfo );
+    }
+    
+    PaUtil_DebugPrint("\tdouble sampleRate: %g\n", sampleRate );
+    PaUtil_DebugPrint("\tunsigned long framesPerBuffer: %d\n", framesPerBuffer );
+    PaUtil_DebugPrint("\tPaStreamFlags streamFlags: 0x%x\n", streamFlags );
+    PaUtil_DebugPrint("\tPaStreamCallback *streamCallback: 0x%p\n", streamCallback );
+    PaUtil_DebugPrint("\tvoid *userData: 0x%p\n", userData );
+#endif
+
+    if( !PA_IS_INITIALISED_ )
+    {
+        result = paNotInitialized;
+
+#ifdef PA_LOG_API_CALLS
+        PaUtil_DebugPrint("Pa_OpenStream returned:\n" );
+        PaUtil_DebugPrint("\t*(PaStream** stream): undefined\n" );
+        PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
+#endif
+        return result;
+    }
+
+    /* Check for parameter errors.
+        NOTE: make sure this validation list is kept syncronised with the one
+        in pa_hostapi.h
+    */
+
+    if( stream == NULL )
+    {
+        result = paBadStreamPtr;
+
+#ifdef PA_LOG_API_CALLS
+        PaUtil_DebugPrint("Pa_OpenStream returned:\n" );
+        PaUtil_DebugPrint("\t*(PaStream** stream): undefined\n" );
+        PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
+#endif
+        return result;
+    }
+
+    result = ValidateOpenStreamParameters( inputParameters,
+                                           outputParameters,
+                                           sampleRate, framesPerBuffer,
+                                           streamFlags, streamCallback,
+                                           &hostApi,
+                                           &hostApiInputDevice,
+                                           &hostApiOutputDevice );
+    if( result != paNoError )
+    {
+#ifdef PA_LOG_API_CALLS
+        PaUtil_DebugPrint("Pa_OpenStream returned:\n" );
+        PaUtil_DebugPrint("\t*(PaStream** stream): undefined\n" );
+        PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
+#endif
+        return result;
+    }
+    
+
+    if( inputParameters )
+    {
+        hostApiInputParameters.device = hostApiInputDevice;
+        hostApiInputParameters.channelCount = inputParameters->channelCount;
+        hostApiInputParameters.sampleFormat = inputParameters->sampleFormat;
+        hostApiInputParameters.suggestedLatency = inputParameters->suggestedLatency;
+        hostApiInputParameters.hostApiSpecificStreamInfo = inputParameters->hostApiSpecificStreamInfo;
+        hostApiInputParametersPtr = &hostApiInputParameters;
+    }
+    else
+    {
+        hostApiInputParametersPtr = NULL;
+    }
+
+    if( outputParameters )
+    {
+        hostApiOutputParameters.device = hostApiOutputDevice;
+        hostApiOutputParameters.channelCount = outputParameters->channelCount;
+        hostApiOutputParameters.sampleFormat = outputParameters->sampleFormat;
+        hostApiOutputParameters.suggestedLatency = outputParameters->suggestedLatency;
+        hostApiOutputParameters.hostApiSpecificStreamInfo = outputParameters->hostApiSpecificStreamInfo;
+        hostApiOutputParametersPtr = &hostApiOutputParameters;
+    }
+    else
+    {
+        hostApiOutputParametersPtr = NULL;
+    }
+
+    result = hostApi->OpenStream( hostApi, stream,
+                                  hostApiInputParametersPtr, hostApiOutputParametersPtr,
+                                  sampleRate, framesPerBuffer, streamFlags, streamCallback, userData );
+
+    if( result == paNoError )
+        AddOpenStream( *stream );
+
+
+#ifdef PA_LOG_API_CALLS
+    PaUtil_DebugPrint("Pa_OpenStream returned:\n" );
+    PaUtil_DebugPrint("\t*(PaStream** stream): 0x%p\n", *stream );
+    PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
+#endif
+
+    return result;
+}
+
+
+PaError Pa_OpenDefaultStream( PaStream** stream,
+                              int inputChannelCount,
+                              int outputChannelCount,
+                              PaSampleFormat sampleFormat,
+                              double sampleRate,
+                              unsigned long framesPerBuffer,
+                              PaStreamCallback *streamCallback,
+                              void *userData )
+{
+    PaError result;
+    PaStreamParameters hostApiInputParameters, hostApiOutputParameters;
+    PaStreamParameters *hostApiInputParametersPtr, *hostApiOutputParametersPtr;
+
+#ifdef PA_LOG_API_CALLS
+    PaUtil_DebugPrint("Pa_OpenDefaultStream called:\n" );
+    PaUtil_DebugPrint("\tPaStream** stream: 0x%p\n", stream );
+    PaUtil_DebugPrint("\tint inputChannelCount: %d\n", inputChannelCount );
+    PaUtil_DebugPrint("\tint outputChannelCount: %d\n", outputChannelCount );
+    PaUtil_DebugPrint("\tPaSampleFormat sampleFormat: %d\n", sampleFormat );
+    PaUtil_DebugPrint("\tdouble sampleRate: %g\n", sampleRate );
+    PaUtil_DebugPrint("\tunsigned long framesPerBuffer: %d\n", framesPerBuffer );
+    PaUtil_DebugPrint("\tPaStreamCallback *streamCallback: 0x%p\n", streamCallback );
+    PaUtil_DebugPrint("\tvoid *userData: 0x%p\n", userData );
+#endif
+
+
+    if( inputChannelCount > 0 )
+    {
+        hostApiInputParameters.device = Pa_GetDefaultInputDevice();
+        hostApiInputParameters.channelCount = inputChannelCount;
+        hostApiInputParameters.sampleFormat = sampleFormat;
+        /* defaultHighInputLatency is used below instead of
+           defaultLowInputLatency because it is more important for the default
+           stream to work reliably than it is for it to work with the lowest
+           latency.
+         */
+        hostApiInputParameters.suggestedLatency = 
+             Pa_GetDeviceInfo( hostApiInputParameters.device )->defaultHighInputLatency;
+        hostApiInputParameters.hostApiSpecificStreamInfo = NULL;
+        hostApiInputParametersPtr = &hostApiInputParameters;
+    }
+    else
+    {
+        hostApiInputParametersPtr = NULL;
+    }
+
+    if( outputChannelCount > 0 )
+    {
+        hostApiOutputParameters.device = Pa_GetDefaultOutputDevice();
+        hostApiOutputParameters.channelCount = outputChannelCount;
+        hostApiOutputParameters.sampleFormat = sampleFormat;
+        /* defaultHighOutputLatency is used below instead of
+           defaultLowOutputLatency because it is more important for the default
+           stream to work reliably than it is for it to work with the lowest
+           latency.
+         */
+        hostApiOutputParameters.suggestedLatency =
+             Pa_GetDeviceInfo( hostApiOutputParameters.device )->defaultHighOutputLatency;
+        hostApiOutputParameters.hostApiSpecificStreamInfo = NULL;
+        hostApiOutputParametersPtr = &hostApiOutputParameters;
+    }
+    else
+    {
+        hostApiOutputParametersPtr = NULL;
+    }
+
+
+    result = Pa_OpenStream(
+                 stream, hostApiInputParametersPtr, hostApiOutputParametersPtr,
+                 sampleRate, framesPerBuffer, paNoFlag, streamCallback, userData );
+
+#ifdef PA_LOG_API_CALLS
+    PaUtil_DebugPrint("Pa_OpenDefaultStream returned:\n" );
+    PaUtil_DebugPrint("\t*(PaStream** stream): 0x%p", *stream );
+    PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
+#endif
+
+    return result;
+}
+
+
+PaError PaUtil_ValidateStreamPointer( PaStream* stream )
+{
+    if( !PA_IS_INITIALISED_ ) return paNotInitialized;
+
+    if( stream == NULL ) return paBadStreamPtr;
+
+    if( ((PaUtilStreamRepresentation*)stream)->magic != PA_STREAM_MAGIC )
+        return paBadStreamPtr;
+
+    return paNoError;
+}
+
+
+PaError Pa_CloseStream( PaStream* stream )
+{
+    PaUtilStreamInterface *interface;
+    PaError result = PaUtil_ValidateStreamPointer( stream );
+
+#ifdef PA_LOG_API_CALLS
+    PaUtil_DebugPrint("Pa_CloseStream called:\n" );
+    PaUtil_DebugPrint("\tPaStream* stream: 0x%p\n", stream );
+#endif
+
+    /* always remove the open stream from our list, even if this function
+        eventually returns an error. Otherwise CloseOpenStreams() will
+        get stuck in an infinite loop */
+    RemoveOpenStream( stream ); /* be sure to call this _before_ closing the stream */
+
+    if( result == paNoError )
+    {
+        interface = PA_STREAM_INTERFACE(stream);
+
+        /* abort the stream if it isn't stopped */
+        result = interface->IsStopped( stream );
+        if( result == 1 )
+            result = paNoError;
+        else if( result == 0 )
+            result = interface->Abort( stream );
+
+        if( result == paNoError )                 /** @todo REVIEW: shouldn't we close anyway? */
+            result = interface->Close( stream );
+    }
+
+#ifdef PA_LOG_API_CALLS
+    PaUtil_DebugPrint("Pa_CloseStream returned:\n" );
+    PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
+#endif
+
+    return result;
+}
+
+
+PaError Pa_SetStreamFinishedCallback( PaStream *stream, PaStreamFinishedCallback* streamFinishedCallback )
+{
+    PaError result = PaUtil_ValidateStreamPointer( stream );
+
+#ifdef PA_LOG_API_CALLS
+    PaUtil_DebugPrint("Pa_SetStreamFinishedCallback called:\n" );
+    PaUtil_DebugPrint("\tPaStream* stream: 0x%p\n", stream );
+    PaUtil_DebugPrint("\tPaStreamFinishedCallback* streamFinishedCallback: 0x%p\n", streamFinishedCallback );
+#endif
+
+    if( result == paNoError )
+    {
+        result = PA_STREAM_INTERFACE(stream)->IsStopped( stream );
+        if( result == 0 )
+        {
+            result = paStreamIsNotStopped ;
+        }
+        if( result == 1 )
+        {
+            PA_STREAM_REP( stream )->streamFinishedCallback = streamFinishedCallback;
+            result = paNoError;
+        }
+    }
+
+#ifdef PA_LOG_API_CALLS
+    PaUtil_DebugPrint("Pa_SetStreamFinishedCallback returned:\n" );
+    PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
+#endif
+
+    return result;
+
+}
+
+
+PaError Pa_StartStream( PaStream *stream )
+{
+    PaError result = PaUtil_ValidateStreamPointer( stream );
+
+#ifdef PA_LOG_API_CALLS
+    PaUtil_DebugPrint("Pa_StartStream called:\n" );
+    PaUtil_DebugPrint("\tPaStream* stream: 0x%p\n", stream );
+#endif
+
+    if( result == paNoError )
+    {
+        result = PA_STREAM_INTERFACE(stream)->IsStopped( stream );
+        if( result == 0 )
+        {
+            result = paStreamIsNotStopped ;
+        }
+        else if( result == 1 )
+        {
+            result = PA_STREAM_INTERFACE(stream)->Start( stream );
+        }
+    }
+
+#ifdef PA_LOG_API_CALLS
+    PaUtil_DebugPrint("Pa_StartStream returned:\n" );
+    PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
+#endif
+
+    return result;
+}
+
+
+PaError Pa_StopStream( PaStream *stream )
+{
+    PaError result = PaUtil_ValidateStreamPointer( stream );
+
+#ifdef PA_LOG_API_CALLS
+    PaUtil_DebugPrint("Pa_StopStream called\n" );
+    PaUtil_DebugPrint("\tPaStream* stream: 0x%p\n", stream );
+#endif
+
+    if( result == paNoError )
+    {
+        result = PA_STREAM_INTERFACE(stream)->IsStopped( stream );
+        if( result == 0 )
+        {
+            result = PA_STREAM_INTERFACE(stream)->Stop( stream );
+        }
+        else if( result == 1 )
+        {
+            result = paStreamIsStopped;
+        }
+    }
+
+#ifdef PA_LOG_API_CALLS
+    PaUtil_DebugPrint("Pa_StopStream returned:\n" );
+    PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
+#endif
+
+    return result;
+}
+
+
+PaError Pa_AbortStream( PaStream *stream )
+{
+    PaError result = PaUtil_ValidateStreamPointer( stream );
+
+#ifdef PA_LOG_API_CALLS
+    PaUtil_DebugPrint("Pa_AbortStream called:\n" );
+    PaUtil_DebugPrint("\tPaStream* stream: 0x%p\n", stream );
+#endif
+
+    if( result == paNoError )
+    {
+        result = PA_STREAM_INTERFACE(stream)->IsStopped( stream );
+        if( result == 0 )
+        {
+            result = PA_STREAM_INTERFACE(stream)->Abort( stream );
+        }
+        else if( result == 1 )
+        {
+            result = paStreamIsStopped;
+        }
+    }
+
+#ifdef PA_LOG_API_CALLS
+    PaUtil_DebugPrint("Pa_AbortStream returned:\n" );
+    PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
+#endif
+
+    return result;
+}
+
+
+PaError Pa_IsStreamStopped( PaStream *stream )
+{
+    PaError result = PaUtil_ValidateStreamPointer( stream );
+
+#ifdef PA_LOG_API_CALLS
+    PaUtil_DebugPrint("Pa_IsStreamStopped called:\n" );
+    PaUtil_DebugPrint("\tPaStream* stream: 0x%p\n", stream );
+#endif
+
+    if( result == paNoError )
+        result = PA_STREAM_INTERFACE(stream)->IsStopped( stream );
+
+#ifdef PA_LOG_API_CALLS
+    PaUtil_DebugPrint("Pa_IsStreamStopped returned:\n" );
+    PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
+#endif
+
+    return result;
+}
+
+
+PaError Pa_IsStreamActive( PaStream *stream )
+{
+    PaError result = PaUtil_ValidateStreamPointer( stream );
+
+#ifdef PA_LOG_API_CALLS
+    PaUtil_DebugPrint("Pa_IsStreamActive called:\n" );
+    PaUtil_DebugPrint("\tPaStream* stream: 0x%p\n", stream );
+#endif
+
+    if( result == paNoError )
+        result = PA_STREAM_INTERFACE(stream)->IsActive( stream );
+
+#ifdef PA_LOG_API_CALLS
+    PaUtil_DebugPrint("Pa_IsStreamActive returned:\n" );
+    PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
+#endif
+
+    return result;
+}
+
+
+const PaStreamInfo* Pa_GetStreamInfo( PaStream *stream )
+{
+    PaError error = PaUtil_ValidateStreamPointer( stream );
+    const PaStreamInfo *result;
+
+#ifdef PA_LOG_API_CALLS
+    PaUtil_DebugPrint("Pa_GetStreamInfo called:\n" );
+    PaUtil_DebugPrint("\tPaStream* stream: 0x%p\n", stream );
+#endif
+
+    if( error != paNoError )
+    {
+        result = 0;
+
+#ifdef PA_LOG_API_CALLS
+        PaUtil_DebugPrint("Pa_GetStreamInfo returned:\n" );
+        PaUtil_DebugPrint("\tconst PaStreamInfo*: 0 [PaError error:%d ( %s )]\n\n", result, error, Pa_GetErrorText( error ) );
+#endif
+
+    }
+    else
+    {
+        result = &PA_STREAM_REP( stream )->streamInfo;
+
+#ifdef PA_LOG_API_CALLS
+        PaUtil_DebugPrint("Pa_GetStreamInfo returned:\n" );
+        PaUtil_DebugPrint("\tconst PaStreamInfo*: 0x%p:\n", result );
+        PaUtil_DebugPrint("\t{" );
+
+        PaUtil_DebugPrint("\t\tint structVersion: %d\n", result->structVersion );
+        PaUtil_DebugPrint("\t\tPaTime inputLatency: %f\n", result->inputLatency );
+        PaUtil_DebugPrint("\t\tPaTime outputLatency: %f\n", result->outputLatency );
+        PaUtil_DebugPrint("\t\tdouble sampleRate: %f\n", result->sampleRate );
+        PaUtil_DebugPrint("\t}\n\n" );
+#endif
+
+    }
+
+    return result;
+}
+
+
+PaTime Pa_GetStreamTime( PaStream *stream )
+{
+    PaError error = PaUtil_ValidateStreamPointer( stream );
+    PaTime result;
+
+#ifdef PA_LOG_API_CALLS
+    PaUtil_DebugPrint("Pa_GetStreamTime called:\n" );
+    PaUtil_DebugPrint("\tPaStream* stream: 0x%p\n", stream );
+#endif
+
+    if( error != paNoError )
+    {
+        result = 0;
+
+#ifdef PA_LOG_API_CALLS
+        PaUtil_DebugPrint("Pa_GetStreamTime returned:\n" );
+        PaUtil_DebugPrint("\tPaTime: 0 [PaError error:%d ( %s )]\n\n", result, error, Pa_GetErrorText( error ) );
+#endif
+
+    }
+    else
+    {
+        result = PA_STREAM_INTERFACE(stream)->GetTime( stream );
+
+#ifdef PA_LOG_API_CALLS
+        PaUtil_DebugPrint("Pa_GetStreamTime returned:\n" );
+        PaUtil_DebugPrint("\tPaTime: %g\n\n", result );
+#endif
+
+    }
+
+    return result;
+}
+
+
+double Pa_GetStreamCpuLoad( PaStream* stream )
+{
+    PaError error = PaUtil_ValidateStreamPointer( stream );
+    double result;
+
+#ifdef PA_LOG_API_CALLS
+    PaUtil_DebugPrint("Pa_GetStreamCpuLoad called:\n" );
+    PaUtil_DebugPrint("\tPaStream* stream: 0x%p\n", stream );
+#endif
+
+    if( error != paNoError )
+    {
+
+        result = 0.0;
+
+#ifdef PA_LOG_API_CALLS
+        PaUtil_DebugPrint("Pa_GetStreamCpuLoad returned:\n" );
+        PaUtil_DebugPrint("\tdouble: 0.0 [PaError error: %d ( %s )]\n\n", error, Pa_GetErrorText( error ) );
+#endif
+
+    }
+    else
+    {
+        result = PA_STREAM_INTERFACE(stream)->GetCpuLoad( stream );
+
+#ifdef PA_LOG_API_CALLS
+        PaUtil_DebugPrint("Pa_GetStreamCpuLoad returned:\n" );
+        PaUtil_DebugPrint("\tdouble: %g\n\n", result );
+#endif
+
+    }
+
+    return result;
+}
+
+
+PaError Pa_ReadStream( PaStream* stream,
+                       void *buffer,
+                       unsigned long frames )
+{
+    PaError result = PaUtil_ValidateStreamPointer( stream );
+
+#ifdef PA_LOG_API_CALLS
+    PaUtil_DebugPrint("Pa_ReadStream called:\n" );
+    PaUtil_DebugPrint("\tPaStream* stream: 0x%p\n", stream );
+#endif
+
+    if( result == paNoError )
+    {
+        if( frames == 0 )
+        {
+            result = paInternalError; /** @todo should return a different error code */
+        }
+        else if( buffer == 0 )
+        {
+            result = paInternalError; /** @todo should return a different error code */
+        }
+        else
+        {
+            result = PA_STREAM_INTERFACE(stream)->IsStopped( stream );
+            if( result == 0 )
+            {
+                result = PA_STREAM_INTERFACE(stream)->Read( stream, buffer, frames );
+            }
+            else if( result == 1 )
+            {
+                result = paStreamIsStopped;
+            }
+        }
+    }
+
+#ifdef PA_LOG_API_CALLS
+    PaUtil_DebugPrint("Pa_ReadStream returned:\n" );
+    PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
+#endif
+
+    return result;
+}
+
+
+PaError Pa_WriteStream( PaStream* stream,
+                        const void *buffer,
+                        unsigned long frames )
+{
+    PaError result = PaUtil_ValidateStreamPointer( stream );
+
+#ifdef PA_LOG_API_CALLS
+    PaUtil_DebugPrint("Pa_WriteStream called:\n" );
+    PaUtil_DebugPrint("\tPaStream* stream: 0x%p\n", stream );
+#endif
+
+    if( result == paNoError )
+    {
+        if( frames == 0 )
+        {
+            result = paInternalError; /** @todo should return a different error code */
+        }
+        else if( buffer == 0 )
+        {
+            result = paInternalError; /** @todo should return a different error code */
+        }
+        else
+        {
+            result = PA_STREAM_INTERFACE(stream)->IsStopped( stream );
+            if( result == 0 )
+            {
+                result = PA_STREAM_INTERFACE(stream)->Write( stream, buffer, frames );
+            }
+            else if( result == 1 )
+            {
+                result = paStreamIsStopped;
+            }  
+        }
+    }
+
+#ifdef PA_LOG_API_CALLS
+    PaUtil_DebugPrint("Pa_WriteStream returned:\n" );
+    PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
+#endif
+
+    return result;
+}
+
+signed long Pa_GetStreamReadAvailable( PaStream* stream )
+{
+    PaError error = PaUtil_ValidateStreamPointer( stream );
+    signed long result;
+
+#ifdef PA_LOG_API_CALLS
+    PaUtil_DebugPrint("Pa_GetStreamReadAvailable called:\n" );
+    PaUtil_DebugPrint("\tPaStream* stream: 0x%p\n", stream );
+#endif
+
+    if( error != paNoError )
+    {
+        result = 0;
+
+#ifdef PA_LOG_API_CALLS
+        PaUtil_DebugPrint("Pa_GetStreamReadAvailable returned:\n" );
+        PaUtil_DebugPrint("\tunsigned long: 0 [ PaError error: %d ( %s ) ]\n\n", error, Pa_GetErrorText( error ) );
+#endif
+
+    }
+    else
+    {
+        result = PA_STREAM_INTERFACE(stream)->GetReadAvailable( stream );
+
+#ifdef PA_LOG_API_CALLS
+        PaUtil_DebugPrint("Pa_GetStreamReadAvailable returned:\n" );
+        PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
+#endif
+
+    }
+
+    return result;
+}
+
+
+signed long Pa_GetStreamWriteAvailable( PaStream* stream )
+{
+    PaError error = PaUtil_ValidateStreamPointer( stream );
+    signed long result;
+
+#ifdef PA_LOG_API_CALLS
+    PaUtil_DebugPrint("Pa_GetStreamWriteAvailable called:\n" );
+    PaUtil_DebugPrint("\tPaStream* stream: 0x%p\n", stream );
+#endif
+
+    if( error != paNoError )
+    {
+        result = 0;
+
+#ifdef PA_LOG_API_CALLS
+        PaUtil_DebugPrint("Pa_GetStreamWriteAvailable returned:\n" );
+        PaUtil_DebugPrint("\tunsigned long: 0 [ PaError error: %d ( %s ) ]\n\n", error, Pa_GetErrorText( error ) );
+#endif
+
+    }
+    else
+    {
+        result = PA_STREAM_INTERFACE(stream)->GetWriteAvailable( stream );
+
+#ifdef PA_LOG_API_CALLS
+        PaUtil_DebugPrint("Pa_GetStreamWriteAvailable returned:\n" );
+        PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
+#endif
+
+    }
+
+    return result;
+}
+
+
+PaError Pa_GetSampleSize( PaSampleFormat format )
+{
+    int result;
+
+#ifdef PA_LOG_API_CALLS
+    PaUtil_DebugPrint("Pa_GetSampleSize called:\n" );
+    PaUtil_DebugPrint("\tPaSampleFormat format: %d\n", format );
+#endif
+
+    switch( format & ~paNonInterleaved )
+    {
+
+    case paUInt8:
+    case paInt8:
+        result = 1;
+        break;
+
+    case paInt16:
+        result = 2;
+        break;
+
+    case paInt24:
+        result = 3;
+        break;
+
+    case paFloat32:
+    case paInt32:
+        result = 4;
+        break;
+
+    default:
+        result = paSampleFormatNotSupported;
+        break;
+    }
+
+#ifdef PA_LOG_API_CALLS
+    PaUtil_DebugPrint("Pa_GetSampleSize returned:\n" );
+    if( result > 0 )
+        PaUtil_DebugPrint("\tint: %d\n\n", result );
+    else
+        PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
+#endif
+
+    return (PaError) result;
+}
+
diff --git a/src/audio/portaudio/pa_common/pa_hostapi.h b/src/audio/portaudio/pa_common/pa_hostapi.h
new file mode 100644
index 0000000000000000000000000000000000000000..fec4e4871bbd7bce7a46237a139fee10644bdc8b
--- /dev/null
+++ b/src/audio/portaudio/pa_common/pa_hostapi.h
@@ -0,0 +1,244 @@
+#ifndef PA_HOSTAPI_H
+#define PA_HOSTAPI_H
+/*
+ * $Id$
+ * Portable Audio I/O Library
+ * host api representation
+ *
+ * Based on the Open Source API proposed by Ross Bencina
+ * Copyright (c) 1999-2002 Ross Bencina, 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.
+ */
+
+/** @file
+ @brief Interface used by pa_front to virtualize functions which operate on
+ host APIs.
+*/
+
+
+#include "portaudio.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif /* __cplusplus */
+
+
+/** **FOR THE USE OF pa_front.c ONLY**
+    Do NOT use fields in this structure, they my change at any time.
+    Use functions defined in pa_util.h if you think you need functionality
+    which can be derived from here.
+*/
+typedef struct PaUtilPrivatePaFrontHostApiInfo {
+
+
+    unsigned long baseDeviceIndex;
+}PaUtilPrivatePaFrontHostApiInfo;
+
+
+/** The common header for all data structures whose pointers are passed through
+ the hostApiSpecificStreamInfo field of the PaStreamParameters structure.
+ Note that in order to keep the public PortAudio interface clean, this structure
+ is not used explicitly when declaring hostApiSpecificStreamInfo data structures.
+ However, some code in pa_front depends on the first 3 members being equivalent
+ with this structure.
+ @see PaStreamParameters
+*/
+typedef struct PaUtilHostApiSpecificStreamInfoHeader
+{
+    unsigned long size;             /**< size of whole structure including this header */
+    PaHostApiTypeId hostApiType;    /**< host API for which this data is intended */
+    unsigned long version;          /**< structure version */
+} PaUtilHostApiSpecificStreamInfoHeader;
+
+
+
+/** A structure representing the interface to a host API. Contains both
+ concrete data and pointers to functions which implement the interface.
+*/
+typedef struct PaUtilHostApiRepresentation {
+    PaUtilPrivatePaFrontHostApiInfo privatePaFrontInfo;
+
+    /** The host api implementation should populate the info field. In the
+        case of info.defaultInputDevice and info.defaultOutputDevice the
+        values stored should be 0 based indices within the host api's own
+        device index range (0 to deviceCount). These values will be converted
+        to global device indices by pa_front after PaUtilHostApiInitializer()
+        returns.
+    */
+    PaHostApiInfo info;
+
+    PaDeviceInfo** deviceInfos;
+
+    /**
+        (*Terminate)() is guaranteed to be called with a valid <hostApi>
+        parameter, which was previously returned from the same implementation's
+        initializer.
+    */
+    void (*Terminate)( struct PaUtilHostApiRepresentation *hostApi );
+
+    /**
+        The inputParameters and outputParameters pointers should not be saved
+        as they will not remain valid after OpenStream is called.
+
+        
+        The following guarantees are made about parameters to (*OpenStream)():
+
+            [NOTE: the following list up to *END PA FRONT VALIDATIONS* should be
+                kept in sync with the one for ValidateOpenStreamParameters and
+                Pa_OpenStream in pa_front.c]
+                
+            PaHostApiRepresentation *hostApi
+                - is valid for this implementation
+
+            PaStream** stream
+                - is non-null
+
+            - at least one of inputParameters & outputParmeters is valid (not NULL)
+
+            - if inputParameters & outputParmeters are both valid, that
+                inputParameters->device & outputParmeters->device  both use the same host api
+ 
+            PaDeviceIndex inputParameters->device
+                - is within range (0 to Pa_CountDevices-1) Or:
+                - is paUseHostApiSpecificDeviceSpecification and
+                    inputParameters->hostApiSpecificStreamInfo is non-NULL and refers
+                    to a valid host api
+
+            int inputParameters->numChannels
+                - if inputParameters->device is not paUseHostApiSpecificDeviceSpecification, numInputChannels is > 0
+                - upper bound is NOT validated against device capabilities
+ 
+            PaSampleFormat inputParameters->sampleFormat
+                - is one of the sample formats defined in portaudio.h
+
+            void *inputParameters->hostApiSpecificStreamInfo
+                - if supplied its hostApi field matches the input device's host Api
+ 
+            PaDeviceIndex outputParmeters->device
+                - is within range (0 to Pa_CountDevices-1)
+ 
+            int outputParmeters->numChannels
+                - if inputDevice is valid, numInputChannels is > 0
+                - upper bound is NOT validated against device capabilities
+ 
+            PaSampleFormat outputParmeters->sampleFormat
+                - is one of the sample formats defined in portaudio.h
+        
+            void *outputParmeters->hostApiSpecificStreamInfo
+                - if supplied its hostApi field matches the output device's host Api
+ 
+            double sampleRate
+                - is not an 'absurd' rate (less than 1000. or greater than 200000.)
+                - sampleRate is NOT validated against device capabilities
+ 
+            PaStreamFlags streamFlags
+                - unused platform neutral flags are zero
+                - paNeverDropInput is only used for full-duplex callback streams
+                    with variable buffer size (paFramesPerBufferUnspecified)
+
+            [*END PA FRONT VALIDATIONS*]
+
+
+        The following validations MUST be performed by (*OpenStream)():
+
+            - check that input device can support numInputChannels
+            
+            - check that input device can support inputSampleFormat, or that
+                we have the capability to convert from outputSampleFormat to
+                a native format
+
+            - if inputStreamInfo is supplied, validate its contents,
+                or return an error if no inputStreamInfo is expected
+
+            - check that output device can support numOutputChannels
+            
+            - check that output device can support outputSampleFormat, or that
+                we have the capability to convert from outputSampleFormat to
+                a native format
+
+            - if outputStreamInfo is supplied, validate its contents,
+                or return an error if no outputStreamInfo is expected
+
+            - if a full duplex stream is requested, check that the combination
+                of input and output parameters is supported
+
+            - check that the device supports sampleRate
+
+            - alter sampleRate to a close allowable rate if necessary
+
+            - validate inputLatency and outputLatency
+
+            - validate any platform specific flags, if flags are supplied they
+                must be valid.
+    */
+    PaError (*OpenStream)( struct PaUtilHostApiRepresentation *hostApi,
+                           PaStream** stream,
+                           const PaStreamParameters *inputParameters,
+                           const PaStreamParameters *outputParameters,
+                           double sampleRate,
+                           unsigned long framesPerCallback,
+                           PaStreamFlags streamFlags,
+                           PaStreamCallback *streamCallback,
+                           void *userData );
+
+
+    PaError (*IsFormatSupported)( struct PaUtilHostApiRepresentation *hostApi,
+                                  const PaStreamParameters *inputParameters,
+                                  const PaStreamParameters *outputParameters,
+                                  double sampleRate );
+} PaUtilHostApiRepresentation;
+
+
+/** Prototype for the initialization function which must be implemented by every
+ host API.
+ 
+ @see paHostApiInitializers
+*/
+typedef PaError PaUtilHostApiInitializer( PaUtilHostApiRepresentation**, PaHostApiIndex );
+
+
+/** paHostApiInitializers is a NULL-terminated array of host API initialization
+ functions. These functions are called by pa_front to initialize the host APIs
+ when the client calls Pa_Initialize().
+
+ There is a platform specific file which defines paHostApiInitializers for that
+ platform, pa_win/pa_win_hostapis.c contains the Win32 definitions for example.
+*/
+extern PaUtilHostApiInitializer *paHostApiInitializers[];
+
+
+/** The index of the default host API in the paHostApiInitializers array.
+ 
+ There is a platform specific file which defines paDefaultHostApiIndex for that
+ platform, see pa_win/pa_win_hostapis.c for example.
+*/
+extern int paDefaultHostApiIndex;
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif /* PA_HOSTAPI_H */
diff --git a/src/audio/portaudio/pa_common/pa_process.c b/src/audio/portaudio/pa_common/pa_process.c
new file mode 100644
index 0000000000000000000000000000000000000000..b58b5d791dcc27a1c719389d994e0cd634bb25e7
--- /dev/null
+++ b/src/audio/portaudio/pa_common/pa_process.c
@@ -0,0 +1,1756 @@
+/*
+ * $Id$
+ * Portable Audio I/O Library
+ * streamCallback <-> host buffer processing adapter
+ *
+ * Based on the Open Source API proposed by Ross Bencina
+ * Copyright (c) 1999-2002 Ross Bencina, 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.
+ */
+
+/** @file
+ @brief Buffer Processor implementation.
+    
+ The code in this file is not optimised yet - although it's not clear that
+ it needs to be. there may appear to be redundancies
+ that could be factored into common functions, but the redundanceis are left
+ intentionally as each appearance may have different optimisation possibilities.
+
+ The optimisations which are planned involve only converting data in-place
+ where possible, rather than copying to the temp buffer(s).
+
+ Note that in the extreme case of being able to convert in-place, and there
+ being no conversion necessary there should be some code which short-circuits
+ the operation.
+
+    @todo Consider cache tilings for intereave<->deinterleave.
+
+    @todo implement timeInfo->currentTime int PaUtil_BeginBufferProcessing()
+
+    @todo specify and implement some kind of logical policy for handling the
+        underflow and overflow stream flags when the underflow/overflow overlaps
+        multiple user buffers/callbacks.
+
+	@todo provide support for priming the buffers with data from the callback.
+        The client interface is now implemented through PaUtil_SetNoInput()
+        which sets bp->hostInputChannels[0][0].data to zero. However this is
+        currently only implemented in NonAdaptingProcess(). It shouldn't be
+        needed for AdaptingInputOnlyProcess() (no priming should ever be
+        requested for AdaptingInputOnlyProcess()).
+        Not sure if additional work should be required to make it work with
+        AdaptingOutputOnlyProcess, but it definitely is required for
+        AdaptingProcess.
+
+    @todo implement PaUtil_SetNoOutput for AdaptingProcess
+
+    @todo don't allocate temp buffers for blocking streams unless they are
+        needed. At the moment they are needed, but perhaps for host APIs
+        where the implementation passes a buffer to the host they could be
+        used.
+*/
+
+
+#include <assert.h>
+#include <string.h> /* memset() */
+
+#include "pa_process.h"
+#include "pa_util.h"
+
+
+#define PA_FRAMES_PER_TEMP_BUFFER_WHEN_HOST_BUFFER_SIZE_IS_UNKNOWN_    1024
+
+#define PA_MIN_( a, b ) ( ((a)<(b)) ? (a) : (b) )
+
+
+/* greatest common divisor - PGCD in French */
+static unsigned long GCD( unsigned long a, unsigned long b )
+{
+    return (b==0) ? a : GCD( b, a%b);
+}
+
+/* least common multiple - PPCM in French */
+static unsigned long LCM( unsigned long a, unsigned long b )
+{
+    return (a*b) / GCD(a,b);
+}
+
+#define PA_MAX_( a, b ) (((a) > (b)) ? (a) : (b))
+
+static unsigned long CalculateFrameShift( unsigned long M, unsigned long N )
+{
+    unsigned long result = 0;
+    unsigned long i;
+    unsigned long lcm;
+
+    assert( M > 0 );
+    assert( N > 0 );
+
+    lcm = LCM( M, N );
+    for( i = M; i < lcm; i += M )
+        result = PA_MAX_( result, i % N );
+
+    return result;
+}
+
+
+PaError PaUtil_InitializeBufferProcessor( PaUtilBufferProcessor* bp,
+        int inputChannelCount, PaSampleFormat userInputSampleFormat,
+        PaSampleFormat hostInputSampleFormat,
+        int outputChannelCount, PaSampleFormat userOutputSampleFormat,
+        PaSampleFormat hostOutputSampleFormat,
+        double sampleRate,
+        PaStreamFlags streamFlags,
+        unsigned long framesPerUserBuffer,
+        unsigned long framesPerHostBuffer,
+        PaUtilHostBufferSizeMode hostBufferSizeMode,
+        PaStreamCallback *streamCallback, void *userData )
+{
+    PaError result = paNoError;
+    PaError bytesPerSample;
+    unsigned long tempInputBufferSize, tempOutputBufferSize;
+
+    /* initialize buffer ptrs to zero so they can be freed if necessary in error */
+    bp->tempInputBuffer = 0;
+    bp->tempInputBufferPtrs = 0;
+    bp->tempOutputBuffer = 0;
+    bp->tempOutputBufferPtrs = 0;
+
+    bp->framesPerUserBuffer = framesPerUserBuffer;
+    bp->framesPerHostBuffer = framesPerHostBuffer;
+
+    bp->inputChannelCount = inputChannelCount;
+    bp->outputChannelCount = outputChannelCount;
+
+    bp->hostBufferSizeMode = hostBufferSizeMode;
+
+    bp->hostInputChannels[0] = bp->hostInputChannels[1] = 0;
+    bp->hostOutputChannels[0] = bp->hostOutputChannels[1] = 0;
+
+    if( framesPerUserBuffer == 0 ) /* streamCallback will accept any buffer size */
+    {
+        bp->useNonAdaptingProcess = 1;
+        bp->initialFramesInTempInputBuffer = 0;
+        bp->initialFramesInTempOutputBuffer = 0;
+
+        if( hostBufferSizeMode == paUtilFixedHostBufferSize
+                || hostBufferSizeMode == paUtilBoundedHostBufferSize )
+        {
+            bp->framesPerTempBuffer = framesPerHostBuffer;
+        }
+        else /* unknown host buffer size */
+        {
+             bp->framesPerTempBuffer = PA_FRAMES_PER_TEMP_BUFFER_WHEN_HOST_BUFFER_SIZE_IS_UNKNOWN_;
+        }
+    }
+    else
+    {
+        bp->framesPerTempBuffer = framesPerUserBuffer;
+
+        if( hostBufferSizeMode == paUtilFixedHostBufferSize
+                && framesPerHostBuffer % framesPerUserBuffer == 0 )
+        {
+            bp->useNonAdaptingProcess = 1;
+            bp->initialFramesInTempInputBuffer = 0;
+            bp->initialFramesInTempOutputBuffer = 0;
+        }
+        else
+        {
+            bp->useNonAdaptingProcess = 0;
+
+            if( inputChannelCount > 0 && outputChannelCount > 0 )
+            {
+                /* full duplex */
+                if( hostBufferSizeMode == paUtilFixedHostBufferSize )
+                {
+                    unsigned long frameShift =
+                        CalculateFrameShift( framesPerHostBuffer, framesPerUserBuffer );
+
+                    if( framesPerUserBuffer > framesPerHostBuffer )
+                    {
+                        bp->initialFramesInTempInputBuffer = frameShift;
+                        bp->initialFramesInTempOutputBuffer = 0;
+                    }
+                    else
+                    {
+                        bp->initialFramesInTempInputBuffer = 0;
+                        bp->initialFramesInTempOutputBuffer = frameShift;
+                    }
+                }
+                else /* variable host buffer size, add framesPerUserBuffer latency */
+                {
+                    bp->initialFramesInTempInputBuffer = 0;
+                    bp->initialFramesInTempOutputBuffer = framesPerUserBuffer;
+                }
+            }
+            else
+            {
+                /* half duplex */
+                bp->initialFramesInTempInputBuffer = 0;
+                bp->initialFramesInTempOutputBuffer = 0;
+            }
+        }
+    }
+
+
+    bp->framesInTempInputBuffer = bp->initialFramesInTempInputBuffer;
+    bp->framesInTempOutputBuffer = bp->initialFramesInTempOutputBuffer;
+
+    
+    if( inputChannelCount > 0 )
+    {
+        bytesPerSample = Pa_GetSampleSize( hostInputSampleFormat );
+        if( bytesPerSample > 0 )
+        {
+            bp->bytesPerHostInputSample = bytesPerSample;
+        }
+        else
+        {
+            result = bytesPerSample;
+            goto error;
+        }
+
+        bytesPerSample = Pa_GetSampleSize( userInputSampleFormat );
+        if( bytesPerSample > 0 )
+        {
+            bp->bytesPerUserInputSample = bytesPerSample;
+        }
+        else
+        {
+            result = bytesPerSample;
+            goto error;
+        }
+
+        bp->inputConverter =
+            PaUtil_SelectConverter( hostInputSampleFormat, userInputSampleFormat, streamFlags );
+
+        bp->inputZeroer = PaUtil_SelectZeroer( hostInputSampleFormat );
+            
+        bp->userInputIsInterleaved = (userInputSampleFormat & paNonInterleaved)?0:1;
+
+
+        tempInputBufferSize =
+            bp->framesPerTempBuffer * bp->bytesPerUserInputSample * inputChannelCount;
+         
+        bp->tempInputBuffer = PaUtil_AllocateMemory( tempInputBufferSize );
+        if( bp->tempInputBuffer == 0 )
+        {
+            result = paInsufficientMemory;
+            goto error;
+        }
+        
+        if( bp->framesInTempInputBuffer > 0 )
+            memset( bp->tempInputBuffer, 0, tempInputBufferSize );
+
+        if( userInputSampleFormat & paNonInterleaved )
+        {
+            bp->tempInputBufferPtrs =
+                (void **)PaUtil_AllocateMemory( sizeof(void*)*inputChannelCount );
+            if( bp->tempInputBufferPtrs == 0 )
+            {
+                result = paInsufficientMemory;
+                goto error;
+            }
+        }
+
+        bp->hostInputChannels[0] = (PaUtilChannelDescriptor*)
+                PaUtil_AllocateMemory( sizeof(PaUtilChannelDescriptor) * inputChannelCount * 2);
+        if( bp->hostInputChannels[0] == 0 )
+        {
+            result = paInsufficientMemory;
+            goto error;
+        }
+
+        bp->hostInputChannels[1] = &bp->hostInputChannels[0][inputChannelCount];
+    }
+
+    if( outputChannelCount > 0 )
+    {
+        bytesPerSample = Pa_GetSampleSize( hostOutputSampleFormat );
+        if( bytesPerSample > 0 )
+        {
+            bp->bytesPerHostOutputSample = bytesPerSample;
+        }
+        else
+        {
+            result = bytesPerSample;
+            goto error;
+        }
+
+        bytesPerSample = Pa_GetSampleSize( userOutputSampleFormat );
+        if( bytesPerSample > 0 )
+        {
+            bp->bytesPerUserOutputSample = bytesPerSample;
+        }
+        else
+        {
+            result = bytesPerSample;
+            goto error;
+        }
+
+        bp->outputConverter =
+            PaUtil_SelectConverter( userOutputSampleFormat, hostOutputSampleFormat, streamFlags );
+
+        bp->outputZeroer = PaUtil_SelectZeroer( hostOutputSampleFormat );
+
+        bp->userOutputIsInterleaved = (userOutputSampleFormat & paNonInterleaved)?0:1;
+
+        tempOutputBufferSize =
+                bp->framesPerTempBuffer * bp->bytesPerUserOutputSample * outputChannelCount;
+
+        bp->tempOutputBuffer = PaUtil_AllocateMemory( tempOutputBufferSize );
+        if( bp->tempOutputBuffer == 0 )
+        {
+            result = paInsufficientMemory;
+            goto error;
+        }
+
+        if( bp->framesInTempOutputBuffer > 0 )
+            memset( bp->tempOutputBuffer, 0, tempOutputBufferSize );
+        
+        if( userOutputSampleFormat & paNonInterleaved )
+        {
+            bp->tempOutputBufferPtrs =
+                (void **)PaUtil_AllocateMemory( sizeof(void*)*outputChannelCount );
+            if( bp->tempOutputBufferPtrs == 0 )
+            {
+                result = paInsufficientMemory;
+                goto error;
+            }
+        }
+
+        bp->hostOutputChannels[0] = (PaUtilChannelDescriptor*)
+                PaUtil_AllocateMemory( sizeof(PaUtilChannelDescriptor)*outputChannelCount * 2 );
+        if( bp->hostOutputChannels[0] == 0 )
+        {                                                                     
+            result = paInsufficientMemory;
+            goto error;
+        }
+
+        bp->hostOutputChannels[1] = &bp->hostOutputChannels[0][outputChannelCount];
+    }
+
+    PaUtil_InitializeTriangularDitherState( &bp->ditherGenerator );
+
+    bp->samplePeriod = 1. / sampleRate;
+
+    bp->streamCallback = streamCallback;
+    bp->userData = userData;
+
+    return result;
+
+error:
+    if( bp->tempInputBuffer )
+        PaUtil_FreeMemory( bp->tempInputBuffer );
+
+    if( bp->tempInputBufferPtrs )
+        PaUtil_FreeMemory( bp->tempInputBufferPtrs );
+
+    if( bp->hostInputChannels[0] )
+        PaUtil_FreeMemory( bp->hostInputChannels[0] );
+
+    if( bp->tempOutputBuffer )
+        PaUtil_FreeMemory( bp->tempOutputBuffer );
+
+    if( bp->tempOutputBufferPtrs )
+        PaUtil_FreeMemory( bp->tempOutputBufferPtrs );
+
+    if( bp->hostOutputChannels[0] )
+        PaUtil_FreeMemory( bp->hostOutputChannels[0] );
+
+    return result;
+}
+
+
+void PaUtil_TerminateBufferProcessor( PaUtilBufferProcessor* bp )
+{
+    if( bp->tempInputBuffer )
+        PaUtil_FreeMemory( bp->tempInputBuffer );
+
+    if( bp->tempInputBufferPtrs )
+        PaUtil_FreeMemory( bp->tempInputBufferPtrs );
+
+    if( bp->hostInputChannels[0] )
+        PaUtil_FreeMemory( bp->hostInputChannels[0] );
+        
+    if( bp->tempOutputBuffer )
+        PaUtil_FreeMemory( bp->tempOutputBuffer );
+
+    if( bp->tempOutputBufferPtrs )
+        PaUtil_FreeMemory( bp->tempOutputBufferPtrs );
+
+    if( bp->hostOutputChannels[0] )
+        PaUtil_FreeMemory( bp->hostOutputChannels[0] );
+}
+
+
+void PaUtil_ResetBufferProcessor( PaUtilBufferProcessor* bp )
+{
+    unsigned long tempInputBufferSize, tempOutputBufferSize;
+
+    bp->framesInTempInputBuffer = bp->initialFramesInTempInputBuffer;
+    bp->framesInTempOutputBuffer = bp->initialFramesInTempOutputBuffer;
+
+    if( bp->framesInTempInputBuffer > 0 )
+    {
+        tempInputBufferSize =
+            bp->framesPerTempBuffer * bp->bytesPerUserInputSample * bp->inputChannelCount;
+        memset( bp->tempInputBuffer, 0, tempInputBufferSize );
+    }
+
+    if( bp->framesInTempOutputBuffer > 0 )
+    {      
+        tempOutputBufferSize =
+            bp->framesPerTempBuffer * bp->bytesPerUserOutputSample * bp->outputChannelCount;
+        memset( bp->tempOutputBuffer, 0, tempOutputBufferSize );
+    }
+}
+
+
+unsigned long PaUtil_GetBufferProcessorInputLatency( PaUtilBufferProcessor* bp )
+{
+    return bp->initialFramesInTempInputBuffer;
+}
+
+
+unsigned long PaUtil_GetBufferProcessorOutputLatency( PaUtilBufferProcessor* bp )
+{
+    return bp->initialFramesInTempOutputBuffer;
+}
+
+
+void PaUtil_SetInputFrameCount( PaUtilBufferProcessor* bp,
+        unsigned long frameCount )
+{
+    if( frameCount == 0 )
+        bp->hostInputFrameCount[0] = bp->framesPerHostBuffer;
+    else
+        bp->hostInputFrameCount[0] = frameCount;
+}
+        
+
+void PaUtil_SetNoInput( PaUtilBufferProcessor* bp )
+{
+    assert( bp->inputChannelCount > 0 );
+
+    bp->hostInputChannels[0][0].data = 0;
+}
+
+
+void PaUtil_SetInputChannel( PaUtilBufferProcessor* bp,
+        unsigned int channel, void *data, unsigned int stride )
+{
+    assert( channel < bp->inputChannelCount );
+    
+    bp->hostInputChannels[0][channel].data = data;
+    bp->hostInputChannels[0][channel].stride = stride;
+}
+
+
+void PaUtil_SetInterleavedInputChannels( PaUtilBufferProcessor* bp,
+        unsigned int firstChannel, void *data, unsigned int channelCount )
+{
+    unsigned int i;
+    unsigned int channel = firstChannel;
+    unsigned char *p = (unsigned char*)data;
+
+    if( channelCount == 0 )
+        channelCount = bp->inputChannelCount;
+
+    assert( firstChannel < bp->inputChannelCount );
+    assert( firstChannel + channelCount <= bp->inputChannelCount );
+
+    for( i=0; i< channelCount; ++i )
+    {
+        bp->hostInputChannels[0][channel+i].data = p;
+        p += bp->bytesPerHostInputSample;
+        bp->hostInputChannels[0][channel+i].stride = channelCount;
+    }
+}
+
+
+void PaUtil_SetNonInterleavedInputChannel( PaUtilBufferProcessor* bp,
+        unsigned int channel, void *data )
+{
+    assert( channel < bp->inputChannelCount );
+    
+    bp->hostInputChannels[0][channel].data = data;
+    bp->hostInputChannels[0][channel].stride = 1;
+}
+
+
+void PaUtil_Set2ndInputFrameCount( PaUtilBufferProcessor* bp,
+        unsigned long frameCount )
+{
+    bp->hostInputFrameCount[1] = frameCount;
+}
+
+
+void PaUtil_Set2ndInputChannel( PaUtilBufferProcessor* bp,
+        unsigned int channel, void *data, unsigned int stride )
+{
+    assert( channel < bp->inputChannelCount );
+
+    bp->hostInputChannels[1][channel].data = data;
+    bp->hostInputChannels[1][channel].stride = stride;
+}
+
+
+void PaUtil_Set2ndInterleavedInputChannels( PaUtilBufferProcessor* bp,
+        unsigned int firstChannel, void *data, unsigned int channelCount )
+{
+    unsigned int i;
+    unsigned int channel = firstChannel;
+    unsigned char *p = (unsigned char*)data;
+
+    if( channelCount == 0 )
+        channelCount = bp->inputChannelCount;
+
+    assert( firstChannel < bp->inputChannelCount );
+    assert( firstChannel + channelCount <= bp->inputChannelCount );
+    
+    for( i=0; i< channelCount; ++i )
+    {
+        bp->hostInputChannels[1][channel+i].data = p;
+        p += bp->bytesPerHostInputSample;
+        bp->hostInputChannels[1][channel+i].stride = channelCount;
+    }
+}
+
+        
+void PaUtil_Set2ndNonInterleavedInputChannel( PaUtilBufferProcessor* bp,
+        unsigned int channel, void *data )
+{
+    assert( channel < bp->inputChannelCount );
+    
+    bp->hostInputChannels[1][channel].data = data;
+    bp->hostInputChannels[1][channel].stride = 1;
+}
+
+
+void PaUtil_SetOutputFrameCount( PaUtilBufferProcessor* bp,
+        unsigned long frameCount )
+{
+    if( frameCount == 0 )
+        bp->hostOutputFrameCount[0] = bp->framesPerHostBuffer;
+    else
+        bp->hostOutputFrameCount[0] = frameCount;
+}
+
+
+void PaUtil_SetNoOutput( PaUtilBufferProcessor* bp )
+{
+    assert( bp->outputChannelCount > 0 );
+
+    bp->hostOutputChannels[0][0].data = 0;
+}
+
+
+void PaUtil_SetOutputChannel( PaUtilBufferProcessor* bp,
+        unsigned int channel, void *data, unsigned int stride )
+{
+    assert( channel < bp->outputChannelCount );
+    
+    bp->hostOutputChannels[0][channel].data = data;
+    bp->hostOutputChannels[0][channel].stride = stride;
+}
+
+
+void PaUtil_SetInterleavedOutputChannels( PaUtilBufferProcessor* bp,
+        unsigned int firstChannel, void *data, unsigned int channelCount )
+{
+    unsigned int i;
+    unsigned int channel = firstChannel;
+    unsigned char *p = (unsigned char*)data;
+
+    if( channelCount == 0 )
+        channelCount = bp->outputChannelCount;
+
+    assert( firstChannel < bp->outputChannelCount );
+    assert( firstChannel + channelCount <= bp->outputChannelCount );
+    
+    for( i=0; i< channelCount; ++i )
+    {
+        bp->hostOutputChannels[0][channel+i].data = p;
+        p += bp->bytesPerHostOutputSample;
+        bp->hostOutputChannels[0][channel+i].stride = channelCount;
+    }
+}
+
+
+void PaUtil_SetNonInterleavedOutputChannel( PaUtilBufferProcessor* bp,
+        unsigned int channel, void *data )
+{
+    assert( channel < bp->outputChannelCount );
+
+    bp->hostOutputChannels[0][channel].data = data;
+    bp->hostOutputChannels[0][channel].stride = 1;
+}
+
+
+void PaUtil_Set2ndOutputFrameCount( PaUtilBufferProcessor* bp,
+        unsigned long frameCount )
+{
+    bp->hostOutputFrameCount[1] = frameCount;
+}
+
+
+void PaUtil_Set2ndOutputChannel( PaUtilBufferProcessor* bp,
+        unsigned int channel, void *data, unsigned int stride )
+{
+    assert( channel < bp->outputChannelCount );
+
+    bp->hostOutputChannels[1][channel].data = data;
+    bp->hostOutputChannels[1][channel].stride = stride;
+}
+
+
+void PaUtil_Set2ndInterleavedOutputChannels( PaUtilBufferProcessor* bp,
+        unsigned int firstChannel, void *data, unsigned int channelCount )
+{
+    unsigned int i;
+    unsigned int channel = firstChannel;
+    unsigned char *p = (unsigned char*)data;
+
+    if( channelCount == 0 )
+        channelCount = bp->outputChannelCount;
+
+    assert( firstChannel < bp->outputChannelCount );
+    assert( firstChannel + channelCount <= bp->outputChannelCount );
+    
+    for( i=0; i< channelCount; ++i )
+    {
+        bp->hostOutputChannels[1][channel+i].data = p;
+        p += bp->bytesPerHostOutputSample;
+        bp->hostOutputChannels[1][channel+i].stride = channelCount;
+    }
+}
+
+        
+void PaUtil_Set2ndNonInterleavedOutputChannel( PaUtilBufferProcessor* bp,
+        unsigned int channel, void *data )
+{
+    assert( channel < bp->outputChannelCount );
+    
+    bp->hostOutputChannels[1][channel].data = data;
+    bp->hostOutputChannels[1][channel].stride = 1;
+}
+
+
+void PaUtil_BeginBufferProcessing( PaUtilBufferProcessor* bp,
+        PaStreamCallbackTimeInfo* timeInfo, PaStreamCallbackFlags callbackStatusFlags )
+{
+    bp->timeInfo = timeInfo;
+
+    /* the first streamCallback will be called to process samples which are
+        currently in the input buffer before the ones starting at the timeInfo time */
+        
+    bp->timeInfo->inputBufferAdcTime -= bp->framesInTempInputBuffer * bp->samplePeriod;
+    
+    bp->timeInfo->currentTime = 0; /** FIXME: @todo time info currentTime not implemented */
+
+    /* the first streamCallback will be called to generate samples which will be
+        outputted after the frames currently in the output buffer have been
+        outputted. */
+    bp->timeInfo->outputBufferDacTime += bp->framesInTempOutputBuffer * bp->samplePeriod;
+
+    bp->callbackStatusFlags = callbackStatusFlags;
+
+    bp->hostInputFrameCount[1] = 0;
+    bp->hostOutputFrameCount[1] = 0;
+}
+
+
+/*
+    NonAdaptingProcess() is a simple buffer copying adaptor that can handle
+    both full and half duplex copies. It processes framesToProcess frames,
+    broken into blocks bp->framesPerTempBuffer long.
+    This routine can be used when the streamCallback doesn't care what length
+    the buffers are, or when framesToProcess is an integer multiple of
+    bp->framesPerTempBuffer, in which case streamCallback will always be called
+    with bp->framesPerTempBuffer samples.
+*/
+static unsigned long NonAdaptingProcess( PaUtilBufferProcessor *bp,
+        int *streamCallbackResult,
+        PaUtilChannelDescriptor *hostInputChannels,
+        PaUtilChannelDescriptor *hostOutputChannels,
+        unsigned long framesToProcess )
+{
+    void *userInput, *userOutput;
+    unsigned char *srcBytePtr, *destBytePtr;
+    unsigned int srcSampleStrideSamples; /* stride from one sample to the next within a channel, in samples */
+    unsigned int srcChannelStrideBytes; /* stride from one channel to the next, in bytes */
+    unsigned int destSampleStrideSamples; /* stride from one sample to the next within a channel, in samples */
+    unsigned int destChannelStrideBytes; /* stride from one channel to the next, in bytes */
+    unsigned int i;
+    unsigned long frameCount;
+    unsigned long framesToGo = framesToProcess;
+    unsigned long framesProcessed = 0;
+
+
+    if( *streamCallbackResult == paContinue )
+    {
+        do
+        {
+            frameCount = PA_MIN_( bp->framesPerTempBuffer, framesToGo );
+
+            /* configure user input buffer and convert input data (host -> user) */
+            if( bp->inputChannelCount == 0 )
+            {
+                /* no input */
+                userInput = 0;
+            }
+            else /* there are input channels */
+            {
+                /*
+                    could use more elaborate logic here and sometimes process
+                    buffers in-place.
+                */
+            
+                destBytePtr = (unsigned char *)bp->tempInputBuffer;
+
+                if( bp->userInputIsInterleaved )
+                {
+                    destSampleStrideSamples = bp->inputChannelCount;
+                    destChannelStrideBytes = bp->bytesPerUserInputSample;
+                    userInput = bp->tempInputBuffer;
+                }
+                else /* user input is not interleaved */
+                {
+                    destSampleStrideSamples = 1;
+                    destChannelStrideBytes = frameCount * bp->bytesPerUserInputSample;
+
+                    /* setup non-interleaved ptrs */
+                    for( i=0; i<bp->inputChannelCount; ++i )
+                    {
+                        bp->tempInputBufferPtrs[i] = ((unsigned char*)bp->tempInputBuffer) +
+                            i * bp->bytesPerUserInputSample * frameCount;
+                    }
+                
+                    userInput = bp->tempInputBufferPtrs;
+                }
+
+                if( !bp->hostInputChannels[0][0].data )
+                {
+                    /* no input was supplied (see PaUtil_SetNoInput), so
+                        zero the input buffer */
+
+                    for( i=0; i<bp->inputChannelCount; ++i )
+                    {
+                        bp->inputZeroer( destBytePtr, destSampleStrideSamples, frameCount );
+                        destBytePtr += destChannelStrideBytes;  /* skip to next destination channel */
+                    }
+                }
+                else
+                {
+                    for( i=0; i<bp->inputChannelCount; ++i )
+                    {
+                        bp->inputConverter( destBytePtr, destSampleStrideSamples,
+                                                hostInputChannels[i].data,
+                                                hostInputChannels[i].stride,
+                                                frameCount, &bp->ditherGenerator );
+
+                        destBytePtr += destChannelStrideBytes;  /* skip to next destination channel */
+
+                        /* advance src ptr for next iteration */
+                        hostInputChannels[i].data = ((unsigned char*)hostInputChannels[i].data) +
+                                frameCount * hostInputChannels[i].stride * bp->bytesPerHostInputSample;
+                    }
+                }
+            }
+
+            /* configure user output buffer */
+            if( bp->outputChannelCount == 0 )
+            {
+                /* no output */
+                userOutput = 0;
+            }
+            else /* there are output channels */
+            {
+                if( bp->userOutputIsInterleaved )
+                {
+                    userOutput = bp->tempOutputBuffer;
+                }
+                else /* user output is not interleaved */
+                {
+                    for( i = 0; i < bp->outputChannelCount; ++i )
+                    {
+                        bp->tempOutputBufferPtrs[i] = ((unsigned char*)bp->tempOutputBuffer) +
+                            i * bp->bytesPerUserOutputSample * frameCount;
+                    }
+
+                    userOutput = bp->tempOutputBufferPtrs;
+                }
+            }
+        
+            *streamCallbackResult = bp->streamCallback( userInput, userOutput,
+                    frameCount, bp->timeInfo, bp->callbackStatusFlags, bp->userData );
+
+            if( *streamCallbackResult == paAbort )
+            {
+                /* callback returned paAbort, don't advance framesProcessed
+                        and framesToGo, they will be handled below */
+            }
+            else
+            {
+                bp->timeInfo->inputBufferAdcTime += frameCount * bp->samplePeriod;
+                bp->timeInfo->outputBufferDacTime += frameCount * bp->samplePeriod;
+
+                /* convert output data (user -> host) */
+                
+                if( bp->outputChannelCount != 0 && bp->hostOutputChannels[0][0].data )
+                {
+                    /*
+                        could use more elaborate logic here and sometimes process
+                        buffers in-place.
+                    */
+            
+                    srcBytePtr = (unsigned char *)bp->tempOutputBuffer;
+
+                    if( bp->userOutputIsInterleaved )
+                    {
+                        srcSampleStrideSamples = bp->outputChannelCount;
+                        srcChannelStrideBytes = bp->bytesPerUserOutputSample;
+                    }
+                    else /* user output is not interleaved */
+                    {
+                        srcSampleStrideSamples = 1;
+                        srcChannelStrideBytes = frameCount * bp->bytesPerUserOutputSample;
+                    }
+
+                    for( i=0; i<bp->outputChannelCount; ++i )
+                    {
+                        bp->outputConverter(    hostOutputChannels[i].data,
+                                                hostOutputChannels[i].stride,
+                                                srcBytePtr, srcSampleStrideSamples,
+                                                frameCount, &bp->ditherGenerator );
+
+                        srcBytePtr += srcChannelStrideBytes;  /* skip to next source channel */
+
+                        /* advance dest ptr for next iteration */
+                        hostOutputChannels[i].data = ((unsigned char*)hostOutputChannels[i].data) +
+                                frameCount * hostOutputChannels[i].stride * bp->bytesPerHostOutputSample;
+                    }
+                }
+             
+                framesProcessed += frameCount;
+
+                framesToGo -= frameCount;
+            }
+        }
+        while( framesToGo > 0  && *streamCallbackResult == paContinue );
+    }
+
+    if( framesToGo > 0 )
+    {
+        /* zero any remaining frames output. There will only be remaining frames
+            if the callback has returned paComplete or paAbort */
+
+        frameCount = framesToGo;
+
+        if( bp->outputChannelCount != 0 && bp->hostOutputChannels[0][0].data )
+        {
+            for( i=0; i<bp->outputChannelCount; ++i )
+            {
+                bp->outputZeroer(   hostOutputChannels[i].data,
+                                    hostOutputChannels[i].stride,
+                                    frameCount );
+
+                /* advance dest ptr for next iteration */
+                hostOutputChannels[i].data = ((unsigned char*)hostOutputChannels[i].data) +
+                        frameCount * hostOutputChannels[i].stride * bp->bytesPerHostOutputSample;
+            }
+        }
+
+        framesProcessed += frameCount;
+    }
+
+    return framesProcessed;
+}
+
+
+/*
+    AdaptingInputOnlyProcess() is a half duplex input buffer processor. It
+    converts data from the input buffers into the temporary input buffer,
+    when the temporary input buffer is full, it calls the streamCallback.
+*/
+static unsigned long AdaptingInputOnlyProcess( PaUtilBufferProcessor *bp,
+        int *streamCallbackResult,
+        PaUtilChannelDescriptor *hostInputChannels,
+        unsigned long framesToProcess )
+{
+    void *userInput, *userOutput;
+    unsigned char *destBytePtr;
+    unsigned int destSampleStrideSamples; /* stride from one sample to the next within a channel, in samples */
+    unsigned int destChannelStrideBytes; /* stride from one channel to the next, in bytes */
+    unsigned int i;
+    unsigned long frameCount;
+    unsigned long framesToGo = framesToProcess;
+    unsigned long framesProcessed = 0;
+    
+    userOutput = 0;
+
+    do
+    {
+        frameCount = ( bp->framesInTempInputBuffer + framesToGo > bp->framesPerUserBuffer )
+                ? ( bp->framesPerUserBuffer - bp->framesInTempInputBuffer )
+                : framesToGo;
+
+        /* convert frameCount samples into temp buffer */
+
+        if( bp->userInputIsInterleaved )
+        {
+            destBytePtr = ((unsigned char*)bp->tempInputBuffer) +
+                    bp->bytesPerUserInputSample * bp->inputChannelCount *
+                    bp->framesInTempInputBuffer;
+                      
+            destSampleStrideSamples = bp->inputChannelCount;
+            destChannelStrideBytes = bp->bytesPerUserInputSample;
+
+            userInput = bp->tempInputBuffer;
+        }
+        else /* user input is not interleaved */
+        {
+            destBytePtr = ((unsigned char*)bp->tempInputBuffer) +
+                    bp->bytesPerUserInputSample * bp->framesInTempInputBuffer;
+
+            destSampleStrideSamples = 1;
+            destChannelStrideBytes = bp->framesPerUserBuffer * bp->bytesPerUserInputSample;
+
+            /* setup non-interleaved ptrs */
+            for( i=0; i<bp->inputChannelCount; ++i )
+            {
+                bp->tempInputBufferPtrs[i] = ((unsigned char*)bp->tempInputBuffer) +
+                    i * bp->bytesPerUserInputSample * bp->framesPerUserBuffer;
+            }
+                    
+            userInput = bp->tempInputBufferPtrs;
+        }
+
+        for( i=0; i<bp->inputChannelCount; ++i )
+        {
+            bp->inputConverter( destBytePtr, destSampleStrideSamples,
+                                    hostInputChannels[i].data,
+                                    hostInputChannels[i].stride,
+                                    frameCount, &bp->ditherGenerator );
+
+            destBytePtr += destChannelStrideBytes;  /* skip to next destination channel */
+
+            /* advance src ptr for next iteration */
+            hostInputChannels[i].data = ((unsigned char*)hostInputChannels[i].data) +
+                    frameCount * hostInputChannels[i].stride * bp->bytesPerHostInputSample;
+        }
+
+        bp->framesInTempInputBuffer += frameCount;
+
+        if( bp->framesInTempInputBuffer == bp->framesPerUserBuffer )
+        {
+            /**
+            @todo (non-critical optimisation)
+            The conditional below implements the continue/complete/abort mechanism
+            simply by continuing on iterating through the input buffer, but not
+            passing the data to the callback. With care, the outer loop could be
+            terminated earlier, thus some unneeded conversion cycles would be
+            saved.
+            */
+            if( *streamCallbackResult == paContinue )
+            {
+                bp->timeInfo->outputBufferDacTime = 0;
+
+                *streamCallbackResult = bp->streamCallback( userInput, userOutput,
+                        bp->framesPerUserBuffer, bp->timeInfo,
+                        bp->callbackStatusFlags, bp->userData );
+
+                bp->timeInfo->inputBufferAdcTime += frameCount * bp->samplePeriod;
+            }
+            
+            bp->framesInTempInputBuffer = 0;
+        }
+
+        framesProcessed += frameCount;
+
+        framesToGo -= frameCount;
+    }while( framesToGo > 0 );
+
+    return framesProcessed;
+}
+
+
+/*
+    AdaptingOutputOnlyProcess() is a half duplex output buffer processor.
+    It converts data from the temporary output buffer, to the output buffers,
+    when the temporary output buffer is empty, it calls the streamCallback.
+*/
+static unsigned long AdaptingOutputOnlyProcess( PaUtilBufferProcessor *bp,
+        int *streamCallbackResult,
+        PaUtilChannelDescriptor *hostOutputChannels,
+        unsigned long framesToProcess )
+{
+    void *userInput, *userOutput;
+    unsigned char *srcBytePtr;
+    unsigned int srcSampleStrideSamples; /* stride from one sample to the next within a channel, in samples */
+    unsigned int srcChannelStrideBytes;  /* stride from one channel to the next, in bytes */
+    unsigned int i;
+    unsigned long frameCount;
+    unsigned long framesToGo = framesToProcess;
+    unsigned long framesProcessed = 0;
+
+    do
+    {
+        if( bp->framesInTempOutputBuffer == 0 && *streamCallbackResult == paContinue )
+        {
+            userInput = 0;
+
+            /* setup userOutput */
+            if( bp->userOutputIsInterleaved )
+            {
+                userOutput = bp->tempOutputBuffer;
+            }
+            else /* user output is not interleaved */
+            {
+                for( i = 0; i < bp->outputChannelCount; ++i )
+                {
+                    bp->tempOutputBufferPtrs[i] = ((unsigned char*)bp->tempOutputBuffer) +
+                            i * bp->framesPerUserBuffer * bp->bytesPerUserOutputSample;
+                }
+
+                userOutput = bp->tempOutputBufferPtrs;
+            }
+
+            bp->timeInfo->inputBufferAdcTime = 0;
+            
+            *streamCallbackResult = bp->streamCallback( userInput, userOutput,
+                    bp->framesPerUserBuffer, bp->timeInfo,
+                    bp->callbackStatusFlags, bp->userData );
+
+            if( *streamCallbackResult == paAbort )
+            {
+                /* if the callback returned paAbort, we disregard its output */
+            }
+            else
+            {
+                bp->timeInfo->outputBufferDacTime += bp->framesPerUserBuffer * bp->samplePeriod;
+
+                bp->framesInTempOutputBuffer = bp->framesPerUserBuffer;
+            }
+        }
+
+        if( bp->framesInTempOutputBuffer > 0 )
+        {
+            /* convert frameCount frames from user buffer to host buffer */
+
+            frameCount = PA_MIN_( bp->framesInTempOutputBuffer, framesToGo );
+
+            if( bp->userOutputIsInterleaved )
+            {
+                srcBytePtr = ((unsigned char*)bp->tempOutputBuffer) +
+                        bp->bytesPerUserOutputSample * bp->outputChannelCount *
+                        (bp->framesPerUserBuffer - bp->framesInTempOutputBuffer);
+
+                srcSampleStrideSamples = bp->outputChannelCount;
+                srcChannelStrideBytes = bp->bytesPerUserOutputSample;
+            }
+            else /* user output is not interleaved */
+            {
+                srcBytePtr = ((unsigned char*)bp->tempOutputBuffer) +
+                        bp->bytesPerUserOutputSample *
+                        (bp->framesPerUserBuffer - bp->framesInTempOutputBuffer);
+                            
+                srcSampleStrideSamples = 1;
+                srcChannelStrideBytes = bp->framesPerUserBuffer * bp->bytesPerUserOutputSample;
+            }
+
+            for( i=0; i<bp->outputChannelCount; ++i )
+            {
+                bp->outputConverter(    hostOutputChannels[i].data,
+                                        hostOutputChannels[i].stride,
+                                        srcBytePtr, srcSampleStrideSamples,
+                                        frameCount, &bp->ditherGenerator );
+
+                srcBytePtr += srcChannelStrideBytes;  /* skip to next source channel */
+
+                /* advance dest ptr for next iteration */
+                hostOutputChannels[i].data = ((unsigned char*)hostOutputChannels[i].data) +
+                        frameCount * hostOutputChannels[i].stride * bp->bytesPerHostOutputSample;
+            }
+
+            bp->framesInTempOutputBuffer -= frameCount;
+        }
+        else
+        {
+            /* no more user data is available because the callback has returned
+                paComplete or paAbort. Fill the remainder of the host buffer
+                with zeros.
+            */
+
+            frameCount = framesToGo;
+
+            for( i=0; i<bp->outputChannelCount; ++i )
+            {
+                bp->outputZeroer(   hostOutputChannels[i].data,
+                                    hostOutputChannels[i].stride,
+                                    frameCount );
+
+                /* advance dest ptr for next iteration */
+                hostOutputChannels[i].data = ((unsigned char*)hostOutputChannels[i].data) +
+                        frameCount * hostOutputChannels[i].stride * bp->bytesPerHostOutputSample;
+            }
+        }
+        
+        framesProcessed += frameCount;
+        
+        framesToGo -= frameCount;
+
+    }while( framesToGo > 0 );
+
+    return framesProcessed;
+}
+
+/* CopyTempOutputBuffersToHostOutputBuffers is called from AdaptingProcess to copy frames from
+	tempOutputBuffer to hostOutputChannels. This includes data conversion
+	and interleaving. 
+*/
+static void CopyTempOutputBuffersToHostOutputBuffers( PaUtilBufferProcessor *bp)
+{
+    unsigned long maxFramesToCopy;
+    PaUtilChannelDescriptor *hostOutputChannels;
+    unsigned int frameCount;
+    unsigned char *srcBytePtr;
+    unsigned int srcSampleStrideSamples; /* stride from one sample to the next within a channel, in samples */
+    unsigned int srcChannelStrideBytes; /* stride from one channel to the next, in bytes */
+    unsigned int i;
+
+     /* copy frames from user to host output buffers */
+     while( bp->framesInTempOutputBuffer > 0 &&
+             ((bp->hostOutputFrameCount[0] + bp->hostOutputFrameCount[1]) > 0) )
+     {
+         maxFramesToCopy = bp->framesInTempOutputBuffer;
+
+         /* select the output buffer set (1st or 2nd) */
+         if( bp->hostOutputFrameCount[0] > 0 )
+         {
+             hostOutputChannels = bp->hostOutputChannels[0];
+             frameCount = PA_MIN_( bp->hostOutputFrameCount[0], maxFramesToCopy );
+         }
+         else
+         {
+             hostOutputChannels = bp->hostOutputChannels[1];
+             frameCount = PA_MIN_( bp->hostOutputFrameCount[1], maxFramesToCopy );
+         }
+
+         if( bp->userOutputIsInterleaved )
+         {
+             srcBytePtr = ((unsigned char*)bp->tempOutputBuffer) +
+                     bp->bytesPerUserOutputSample * bp->outputChannelCount *
+                     (bp->framesPerUserBuffer - bp->framesInTempOutputBuffer);
+                         
+             srcSampleStrideSamples = bp->outputChannelCount;
+             srcChannelStrideBytes = bp->bytesPerUserOutputSample;
+         }
+         else /* user output is not interleaved */
+         {
+             srcBytePtr = ((unsigned char*)bp->tempOutputBuffer) +
+                     bp->bytesPerUserOutputSample *
+                     (bp->framesPerUserBuffer - bp->framesInTempOutputBuffer);
+
+             srcSampleStrideSamples = 1;
+             srcChannelStrideBytes = bp->framesPerUserBuffer * bp->bytesPerUserOutputSample;
+         }
+
+         for( i=0; i<bp->outputChannelCount; ++i )
+         {
+             bp->outputConverter(    hostOutputChannels[i].data,
+                                     hostOutputChannels[i].stride,
+                                     srcBytePtr, srcSampleStrideSamples,
+                                     frameCount, &bp->ditherGenerator );
+
+             srcBytePtr += srcChannelStrideBytes;  /* skip to next source channel */
+
+             /* advance dest ptr for next iteration */
+             hostOutputChannels[i].data = ((unsigned char*)hostOutputChannels[i].data) +
+                     frameCount * hostOutputChannels[i].stride * bp->bytesPerHostOutputSample;
+         }
+
+         if( bp->hostOutputFrameCount[0] > 0 )
+             bp->hostOutputFrameCount[0] -= frameCount;
+         else
+             bp->hostOutputFrameCount[1] -= frameCount;
+
+         bp->framesInTempOutputBuffer -= frameCount;
+     }
+}
+
+/*
+    AdaptingProcess is a full duplex adapting buffer processor. It converts
+    data from the temporary output buffer into the host output buffers, then
+    from the host input buffers into the temporary input buffers. Calling the
+    streamCallback when necessary.
+    When processPartialUserBuffers is 0, all available input data will be
+    consumed and all available output space will be filled. When
+    processPartialUserBuffers is non-zero, as many full user buffers
+    as possible will be processed, but partial buffers will not be consumed.
+*/
+static unsigned long AdaptingProcess( PaUtilBufferProcessor *bp,
+        int *streamCallbackResult, int processPartialUserBuffers )
+{
+    void *userInput, *userOutput;
+    unsigned long framesProcessed = 0;
+    unsigned long framesAvailable;
+    unsigned long endProcessingMinFrameCount;
+    unsigned long maxFramesToCopy;
+    PaUtilChannelDescriptor *hostInputChannels, *hostOutputChannels;
+    unsigned int frameCount;
+    unsigned char *destBytePtr;
+    unsigned int destSampleStrideSamples; /* stride from one sample to the next within a channel, in samples */
+    unsigned int destChannelStrideBytes; /* stride from one channel to the next, in bytes */
+    unsigned int i, j;
+ 
+
+    framesAvailable = bp->hostInputFrameCount[0] + bp->hostInputFrameCount[1];/* this is assumed to be the same as the output buffer's frame count */
+
+    if( processPartialUserBuffers )
+        endProcessingMinFrameCount = 0;
+    else
+        endProcessingMinFrameCount = (bp->framesPerUserBuffer - 1);
+
+    /* Fill host output with remaining frames in user output (tempOutputBuffer) */
+    CopyTempOutputBuffersToHostOutputBuffers( bp );		  	
+
+    while( framesAvailable > endProcessingMinFrameCount ) 
+    {
+
+        if( bp->framesInTempOutputBuffer == 0 && *streamCallbackResult != paContinue )
+        {
+            /* the callback will not be called any more, so zero what remains
+                of the host output buffers */
+
+            for( i=0; i<2; ++i )
+            {
+                frameCount = bp->hostOutputFrameCount[i];
+                if( frameCount > 0 )
+                {
+                    hostOutputChannels = bp->hostOutputChannels[i];
+                    
+                    for( j=0; j<bp->outputChannelCount; ++j )
+                    {
+                        bp->outputZeroer(   hostOutputChannels[j].data,
+                                            hostOutputChannels[j].stride,
+                                            frameCount );
+
+                        /* advance dest ptr for next iteration  */
+                        hostOutputChannels[j].data = ((unsigned char*)hostOutputChannels[j].data) +
+                                frameCount * hostOutputChannels[j].stride * bp->bytesPerHostOutputSample;
+                    }
+                    bp->hostOutputFrameCount[i] = 0;
+                }
+            }
+        }          
+
+
+        /* copy frames from host to user input buffers */
+        while( bp->framesInTempInputBuffer < bp->framesPerUserBuffer &&
+                ((bp->hostInputFrameCount[0] + bp->hostInputFrameCount[1]) > 0) )
+        {
+            maxFramesToCopy = bp->framesPerUserBuffer - bp->framesInTempInputBuffer;
+
+            /* select the input buffer set (1st or 2nd) */
+            if( bp->hostInputFrameCount[0] > 0 )
+            {
+                hostInputChannels = bp->hostInputChannels[0];
+                frameCount = PA_MIN_( bp->hostInputFrameCount[0], maxFramesToCopy );
+            }
+            else
+            {
+                hostInputChannels = bp->hostInputChannels[1];
+                frameCount = PA_MIN_( bp->hostInputFrameCount[1], maxFramesToCopy );
+            }
+
+            /* configure conversion destination pointers */
+            if( bp->userInputIsInterleaved )
+            {
+                destBytePtr = ((unsigned char*)bp->tempInputBuffer) +
+                        bp->bytesPerUserInputSample * bp->inputChannelCount *
+                        bp->framesInTempInputBuffer;
+
+                destSampleStrideSamples = bp->inputChannelCount;
+                destChannelStrideBytes = bp->bytesPerUserInputSample;
+            }
+            else /* user input is not interleaved */
+            {
+                destBytePtr = ((unsigned char*)bp->tempInputBuffer) +
+                        bp->bytesPerUserInputSample * bp->framesInTempInputBuffer;
+
+                destSampleStrideSamples = 1;
+                destChannelStrideBytes = bp->framesPerUserBuffer * bp->bytesPerUserInputSample;
+            }
+
+            for( i=0; i<bp->inputChannelCount; ++i )
+            {
+                bp->inputConverter( destBytePtr, destSampleStrideSamples,
+                                        hostInputChannels[i].data,
+                                        hostInputChannels[i].stride,
+                                        frameCount, &bp->ditherGenerator );
+
+                destBytePtr += destChannelStrideBytes;  /* skip to next destination channel */
+
+                /* advance src ptr for next iteration */
+                hostInputChannels[i].data = ((unsigned char*)hostInputChannels[i].data) +
+                        frameCount * hostInputChannels[i].stride * bp->bytesPerHostInputSample;
+            }
+
+            if( bp->hostInputFrameCount[0] > 0 )
+                bp->hostInputFrameCount[0] -= frameCount;
+            else
+                bp->hostInputFrameCount[1] -= frameCount;
+                
+            bp->framesInTempInputBuffer += frameCount;
+
+            /* update framesAvailable and framesProcessed based on input consumed
+                unless something is very wrong this will also correspond to the
+                amount of output generated */
+            framesAvailable -= frameCount;
+            framesProcessed += frameCount;
+        }
+
+        /* call streamCallback */
+        if( bp->framesInTempInputBuffer == bp->framesPerUserBuffer &&
+            bp->framesInTempOutputBuffer == 0 )
+        {
+            if( *streamCallbackResult == paContinue )
+            {
+                /* setup userInput */
+                if( bp->userInputIsInterleaved )
+                {
+                    userInput = bp->tempInputBuffer;
+                }
+                else /* user input is not interleaved */
+                {
+                    for( i = 0; i < bp->inputChannelCount; ++i )
+                    {
+                        bp->tempInputBufferPtrs[i] = ((unsigned char*)bp->tempInputBuffer) +
+                                i * bp->framesPerUserBuffer * bp->bytesPerUserInputSample;
+                    }
+
+                    userInput = bp->tempInputBufferPtrs;
+                }
+
+                /* setup userOutput */
+                if( bp->userOutputIsInterleaved )
+                {
+                    userOutput = bp->tempOutputBuffer;
+                }
+                else /* user output is not interleaved */
+                {
+                    for( i = 0; i < bp->outputChannelCount; ++i )
+                    {
+                        bp->tempOutputBufferPtrs[i] = ((unsigned char*)bp->tempOutputBuffer) +
+                                i * bp->framesPerUserBuffer * bp->bytesPerUserOutputSample;
+                    }
+
+                    userOutput = bp->tempOutputBufferPtrs;
+                }
+
+                /* call streamCallback */
+
+                *streamCallbackResult = bp->streamCallback( userInput, userOutput,
+                        bp->framesPerUserBuffer, bp->timeInfo,
+                        bp->callbackStatusFlags, bp->userData );
+
+                bp->timeInfo->inputBufferAdcTime += bp->framesPerUserBuffer * bp->samplePeriod;
+                bp->timeInfo->outputBufferDacTime += bp->framesPerUserBuffer * bp->samplePeriod;
+
+                bp->framesInTempInputBuffer = 0;
+
+                if( *streamCallbackResult == paAbort )
+                    bp->framesInTempOutputBuffer = 0;
+                else
+                    bp->framesInTempOutputBuffer = bp->framesPerUserBuffer;
+            }
+            else
+            {
+                /* paComplete or paAbort has already been called. */
+
+                bp->framesInTempInputBuffer = 0;
+            }
+        }
+
+        /* copy frames from user (tempOutputBuffer) to host output buffers (hostOutputChannels) 
+           Means to process the user output provided by the callback. Has to be called after
+            each callback. */
+        CopyTempOutputBuffersToHostOutputBuffers( bp );		  	
+
+    }
+    
+    return framesProcessed;
+}
+
+
+unsigned long PaUtil_EndBufferProcessing( PaUtilBufferProcessor* bp, int *streamCallbackResult )
+{
+    unsigned long framesToProcess, framesToGo;
+    unsigned long framesProcessed = 0;
+    
+    if( bp->inputChannelCount != 0 && bp->outputChannelCount != 0
+            && bp->hostInputChannels[0][0].data /* input was supplied (see PaUtil_SetNoInput) */
+            && bp->hostOutputChannels[0][0].data /* output was supplied (see PaUtil_SetNoOutput) */ )
+    {
+        assert( (bp->hostInputFrameCount[0] + bp->hostInputFrameCount[1]) ==
+                (bp->hostOutputFrameCount[0] + bp->hostOutputFrameCount[1]) );
+    }
+
+    assert( *streamCallbackResult == paContinue
+            || *streamCallbackResult == paComplete
+            || *streamCallbackResult == paAbort ); /* don't forget to pass in a valid callback result value */
+
+    if( bp->useNonAdaptingProcess )
+    {
+        if( bp->inputChannelCount != 0 && bp->outputChannelCount != 0 )
+        {
+            /* full duplex non-adapting process, splice buffers if they are
+                different lengths */
+
+            framesToGo = bp->hostOutputFrameCount[0] + bp->hostOutputFrameCount[1]; /* relies on assert above for input/output equivalence */
+
+            do{
+                unsigned long noInputInputFrameCount;
+                unsigned long *hostInputFrameCount;
+                PaUtilChannelDescriptor *hostInputChannels;
+                unsigned long noOutputOutputFrameCount;
+                unsigned long *hostOutputFrameCount;
+                PaUtilChannelDescriptor *hostOutputChannels;
+                unsigned long framesProcessedThisIteration;
+
+                if( !bp->hostInputChannels[0][0].data )
+                {
+                    /* no input was supplied (see PaUtil_SetNoInput)
+                        NonAdaptingProcess knows how to deal with this
+                    */
+                    noInputInputFrameCount = framesToGo;
+                    hostInputFrameCount = &noInputInputFrameCount;
+                    hostInputChannels = 0;
+                }
+                else if( bp->hostInputFrameCount[0] != 0 )
+                {
+                    hostInputFrameCount = &bp->hostInputFrameCount[0];
+                    hostInputChannels = bp->hostInputChannels[0];
+                }
+                else
+                {
+                    hostInputFrameCount = &bp->hostInputFrameCount[1];
+                    hostInputChannels = bp->hostInputChannels[1];
+                }
+
+                if( !bp->hostOutputChannels[0][0].data )
+                {
+                    /* no output was supplied (see PaUtil_SetNoOutput)
+                        NonAdaptingProcess knows how to deal with this
+                    */
+                    noOutputOutputFrameCount = framesToGo;
+                    hostOutputFrameCount = &noOutputOutputFrameCount;
+                    hostOutputChannels = 0;
+                }
+                if( bp->hostOutputFrameCount[0] != 0 )
+                {
+                    hostOutputFrameCount = &bp->hostOutputFrameCount[0];
+                    hostOutputChannels = bp->hostOutputChannels[0];
+                }
+                else
+                {
+                    hostOutputFrameCount = &bp->hostOutputFrameCount[1];
+                    hostOutputChannels = bp->hostOutputChannels[1];
+                }
+
+                framesToProcess = PA_MIN_( *hostInputFrameCount,
+                                       *hostOutputFrameCount );
+
+                assert( framesToProcess != 0 );
+                
+                framesProcessedThisIteration = NonAdaptingProcess( bp, streamCallbackResult,
+                        hostInputChannels, hostOutputChannels,
+                        framesToProcess );                                       
+
+                *hostInputFrameCount -= framesProcessedThisIteration;
+                *hostOutputFrameCount -= framesProcessedThisIteration;
+
+                framesProcessed += framesProcessedThisIteration;
+                framesToGo -= framesProcessedThisIteration;
+                
+            }while( framesToGo > 0 );
+        }
+        else
+        {
+            /* half duplex non-adapting process, just process 1st and 2nd buffer */
+            /* process first buffer */
+
+            framesToProcess = (bp->inputChannelCount != 0)
+                            ? bp->hostInputFrameCount[0]
+                            : bp->hostOutputFrameCount[0];
+
+            framesProcessed = NonAdaptingProcess( bp, streamCallbackResult,
+                        bp->hostInputChannels[0], bp->hostOutputChannels[0],
+                        framesToProcess );
+
+            /* process second buffer if provided */
+    
+            framesToProcess = (bp->inputChannelCount != 0)
+                            ? bp->hostInputFrameCount[1]
+                            : bp->hostOutputFrameCount[1];
+            if( framesToProcess > 0 )
+            {
+                framesProcessed += NonAdaptingProcess( bp, streamCallbackResult,
+                    bp->hostInputChannels[1], bp->hostOutputChannels[1],
+                    framesToProcess );
+            }
+        }
+    }
+    else /* block adaption necessary*/
+    {
+
+        if( bp->inputChannelCount != 0 && bp->outputChannelCount != 0 )
+        {
+            /* full duplex */
+            
+            if( bp->hostBufferSizeMode == paUtilVariableHostBufferSizePartialUsageAllowed  )
+            {
+                framesProcessed = AdaptingProcess( bp, streamCallbackResult,
+                        0 /* dont process partial user buffers */ );
+            }
+            else
+            {
+                framesProcessed = AdaptingProcess( bp, streamCallbackResult,
+                        1 /* process partial user buffers */ );
+            }
+        }
+        else if( bp->inputChannelCount != 0 )
+        {
+            /* input only */
+            framesToProcess = bp->hostInputFrameCount[0];
+
+            framesProcessed = AdaptingInputOnlyProcess( bp, streamCallbackResult,
+                        bp->hostInputChannels[0], framesToProcess );
+
+            framesToProcess = bp->hostInputFrameCount[1];
+            if( framesToProcess > 0 )
+            {
+                framesProcessed += AdaptingInputOnlyProcess( bp, streamCallbackResult,
+                        bp->hostInputChannels[1], framesToProcess );
+            }
+        }
+        else
+        {
+            /* output only */
+            framesToProcess = bp->hostOutputFrameCount[0];
+
+            framesProcessed = AdaptingOutputOnlyProcess( bp, streamCallbackResult,
+                        bp->hostOutputChannels[0], framesToProcess );
+
+            framesToProcess = bp->hostOutputFrameCount[1];
+            if( framesToProcess > 0 )
+            {
+                framesProcessed += AdaptingOutputOnlyProcess( bp, streamCallbackResult,
+                        bp->hostOutputChannels[1], framesToProcess );
+            }
+        }
+    }
+
+    return framesProcessed;
+}
+
+
+int PaUtil_IsBufferProcessorOutputEmpty( PaUtilBufferProcessor* bp )
+{
+    return (bp->framesInTempOutputBuffer) ? 0 : 1;
+} 
+
+
+unsigned long PaUtil_CopyInput( PaUtilBufferProcessor* bp,
+        void **buffer, unsigned long frameCount )
+{
+    PaUtilChannelDescriptor *hostInputChannels;
+    unsigned int framesToCopy;
+    unsigned char *destBytePtr;
+    void **nonInterleavedDestPtrs;
+    unsigned int destSampleStrideSamples; /* stride from one sample to the next within a channel, in samples */
+    unsigned int destChannelStrideBytes; /* stride from one channel to the next, in bytes */
+    unsigned int i;
+
+    hostInputChannels = bp->hostInputChannels[0];
+    framesToCopy = PA_MIN_( bp->hostInputFrameCount[0], frameCount );
+
+    if( bp->userInputIsInterleaved )
+    {
+        destBytePtr = (unsigned char*)*buffer;
+        
+        destSampleStrideSamples = bp->inputChannelCount;
+        destChannelStrideBytes = bp->bytesPerUserInputSample;
+
+        for( i=0; i<bp->inputChannelCount; ++i )
+        {
+            bp->inputConverter( destBytePtr, destSampleStrideSamples,
+                                hostInputChannels[i].data,
+                                hostInputChannels[i].stride,
+                                framesToCopy, &bp->ditherGenerator );
+
+            destBytePtr += destChannelStrideBytes;  /* skip to next source channel */
+
+            /* advance dest ptr for next iteration */
+            hostInputChannels[i].data = ((unsigned char*)hostInputChannels[i].data) +
+                    framesToCopy * hostInputChannels[i].stride * bp->bytesPerHostInputSample;
+        }
+
+        /* advance callers dest pointer (buffer) */
+        *buffer = ((unsigned char *)*buffer) +
+                framesToCopy * bp->inputChannelCount * bp->bytesPerUserInputSample;
+    }
+    else
+    {
+        /* user input is not interleaved */
+        
+        nonInterleavedDestPtrs = (void**)*buffer;
+
+        destSampleStrideSamples = 1;
+        
+        for( i=0; i<bp->inputChannelCount; ++i )
+        {
+            destBytePtr = (unsigned char*)nonInterleavedDestPtrs[i];
+
+            bp->inputConverter( destBytePtr, destSampleStrideSamples,
+                                hostInputChannels[i].data,
+                                hostInputChannels[i].stride,
+                                framesToCopy, &bp->ditherGenerator );
+
+            /* advance callers dest pointer (nonInterleavedDestPtrs[i]) */
+            destBytePtr += bp->bytesPerUserInputSample * framesToCopy;
+            nonInterleavedDestPtrs[i] = destBytePtr;
+            
+            /* advance dest ptr for next iteration */
+            hostInputChannels[i].data = ((unsigned char*)hostInputChannels[i].data) +
+                    framesToCopy * hostInputChannels[i].stride * bp->bytesPerHostInputSample;
+        }
+    }
+
+    bp->hostInputFrameCount[0] -= framesToCopy;
+    
+    return framesToCopy;
+}
+
+unsigned long PaUtil_CopyOutput( PaUtilBufferProcessor* bp,
+        const void ** buffer, unsigned long frameCount )
+{
+    PaUtilChannelDescriptor *hostOutputChannels;
+    unsigned int framesToCopy;
+    unsigned char *srcBytePtr;
+    void **nonInterleavedSrcPtrs;
+    unsigned int srcSampleStrideSamples; /* stride from one sample to the next within a channel, in samples */
+    unsigned int srcChannelStrideBytes; /* stride from one channel to the next, in bytes */
+    unsigned int i;
+
+    hostOutputChannels = bp->hostOutputChannels[0];
+    framesToCopy = PA_MIN_( bp->hostOutputFrameCount[0], frameCount );
+
+    if( bp->userOutputIsInterleaved )
+    {
+        srcBytePtr = (unsigned char*)*buffer;
+        
+        srcSampleStrideSamples = bp->outputChannelCount;
+        srcChannelStrideBytes = bp->bytesPerUserOutputSample;
+
+        for( i=0; i<bp->outputChannelCount; ++i )
+        {
+            bp->outputConverter(    hostOutputChannels[i].data,
+                                    hostOutputChannels[i].stride,
+                                    srcBytePtr, srcSampleStrideSamples,
+                                    framesToCopy, &bp->ditherGenerator );
+
+            srcBytePtr += srcChannelStrideBytes;  /* skip to next source channel */
+
+            /* advance dest ptr for next iteration */
+            hostOutputChannels[i].data = ((unsigned char*)hostOutputChannels[i].data) +
+                    framesToCopy * hostOutputChannels[i].stride * bp->bytesPerHostOutputSample;
+        }
+
+        /* advance callers source pointer (buffer) */
+        *buffer = ((unsigned char *)*buffer) +
+                framesToCopy * bp->outputChannelCount * bp->bytesPerUserOutputSample;
+
+    }
+    else
+    {
+        /* user output is not interleaved */
+        
+        nonInterleavedSrcPtrs = (void**)*buffer;
+
+        srcSampleStrideSamples = 1;
+        
+        for( i=0; i<bp->outputChannelCount; ++i )
+        {
+            srcBytePtr = (unsigned char*)nonInterleavedSrcPtrs[i];
+            
+            bp->outputConverter(    hostOutputChannels[i].data,
+                                    hostOutputChannels[i].stride,
+                                    srcBytePtr, srcSampleStrideSamples,
+                                    framesToCopy, &bp->ditherGenerator );
+
+
+            /* advance callers source pointer (nonInterleavedSrcPtrs[i]) */
+            srcBytePtr += bp->bytesPerUserOutputSample * framesToCopy;
+            nonInterleavedSrcPtrs[i] = srcBytePtr;
+            
+            /* advance dest ptr for next iteration */
+            hostOutputChannels[i].data = ((unsigned char*)hostOutputChannels[i].data) +
+                    framesToCopy * hostOutputChannels[i].stride * bp->bytesPerHostOutputSample;
+        }
+    }
+
+    bp->hostOutputFrameCount[0] += framesToCopy;
+    
+    return framesToCopy;
+}
+
+
+unsigned long PaUtil_ZeroOutput( PaUtilBufferProcessor* bp, unsigned long frameCount )
+{
+    PaUtilChannelDescriptor *hostOutputChannels;
+    unsigned int framesToZero;
+    unsigned int i;
+
+    hostOutputChannels = bp->hostOutputChannels[0];
+    framesToZero = PA_MIN_( bp->hostOutputFrameCount[0], frameCount );
+
+    for( i=0; i<bp->outputChannelCount; ++i )
+    {
+        bp->outputZeroer(   hostOutputChannels[i].data,
+                            hostOutputChannels[i].stride,
+                            framesToZero );
+
+
+        /* advance dest ptr for next iteration */
+        hostOutputChannels[i].data = ((unsigned char*)hostOutputChannels[i].data) +
+                framesToZero * hostOutputChannels[i].stride * bp->bytesPerHostOutputSample;
+    }
+
+    bp->hostOutputFrameCount[0] += framesToZero;
+    
+    return framesToZero;
+}
diff --git a/src/audio/portaudio/pa_common/pa_process.h b/src/audio/portaudio/pa_common/pa_process.h
new file mode 100644
index 0000000000000000000000000000000000000000..8d13d903a4428416ec1d176f8afc548509006bb4
--- /dev/null
+++ b/src/audio/portaudio/pa_common/pa_process.h
@@ -0,0 +1,741 @@
+#ifndef PA_PROCESS_H
+#define PA_PROCESS_H
+/*
+ * $Id$
+ * Portable Audio I/O Library callback buffer processing adapters
+ *
+ * Based on the Open Source API proposed by Ross Bencina
+ * Copyright (c) 1999-2002 Phil Burk, Ross Bencina
+ *
+ * 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.
+ */
+ 
+/** @file
+ @brief Buffer Processor prototypes. A Buffer Processor performs buffer length
+ adaption, coordinates sample format conversion, and interleaves/deinterleaves
+ channels.
+
+ <h3>Overview</h3>
+
+ The "Buffer Processor" (PaUtilBufferProcessor) manages conversion of audio
+ data from host buffers to user buffers and back again. Where required, the
+ buffer processor takes care of converting between host and user sample formats,
+ interleaving and deinterleaving multichannel buffers, and adapting between host
+ and user buffers with different lengths. The buffer processor may be used with
+ full and half duplex streams, for both callback streams and blocking read/write
+ streams.
+
+ One of the important capabilities provided by the buffer processor is
+ the ability to adapt between user and host buffer sizes of different lengths
+ with minimum latency. Although this task is relatively easy to perform when
+ the host buffer size is an integer multiple of the user buffer size, the
+ problem is more complicated when this is not the case - especially for
+ full-duplex callback streams. Where necessary the adaption is implemented by
+ internally buffering some input and/or output data. The buffer adation
+ algorithm used by the buffer processor was originally implemented by
+ Stephan Letz for the ASIO version of PortAudio, and is described in his
+ Callback_adaption_.pdf which is included in the distribution.
+
+ The buffer processor performs sample conversion using the functions provided
+ by pa_converters.c.
+
+ The following sections provide an overview of how to use the buffer processor.
+ Interested readers are advised to consult the host API implementations for
+ examples of buffer processor usage.
+ 
+
+ <h4>Initialization, resetting and termination</h4>
+
+ When a stream is opened, the buffer processor should be initialized using
+ PaUtil_InitializeBufferProcessor. This function initializes internal state
+ and allocates temporary buffers as neccesary according to the supplied
+ configuration parameters. Some of the parameters correspond to those requested
+ by the user in their call to Pa_OpenStream(), others reflect the requirements
+ of the host API implementation - they indicate host buffer sizes, formats,
+ and the type of buffering which the Host API uses. The buffer processor should
+ be initialized for callback streams and blocking read/write streams.
+
+ Call PaUtil_ResetBufferProcessor to clear any sample data which is present
+ in the buffer processor before starting to use it (for example when
+ Pa_StartStream is called).
+
+ When the buffer processor is no longer used call
+ PaUtil_TerminateBufferProcessor.
+
+ 
+ <h4>Using the buffer processor for a callback stream</h4>
+
+ The buffer processor's role in a callback stream is to take host input buffers
+ process them with the stream callback, and fill host output buffers. For a
+ full duplex stream, the buffer processor handles input and output simultaneously
+ due to the requirements of the minimum-latency buffer adation algorithm.
+
+ When a host buffer becomes available, the implementation should call
+ the buffer processor to process the buffer. The buffer processor calls the
+ stream callback to consume and/or produce audio data as necessary. The buffer
+ processor will convert sample formats, interleave/deinterleave channels,
+ and slice or chunk the data to the appropriate buffer lengths according to
+ the requirements of the stream callback and the host API.
+
+ To process a host buffer (or a pair of host buffers for a full-duplex stream)
+ use the following calling sequence:
+
+ -# Call PaUtil_BeginBufferProcessing
+ -# For a stream which takes input:
+    - Call PaUtil_SetInputFrameCount with the number of frames in the host input
+        buffer.
+    - Call one of the following functions one or more times to tell the
+        buffer processor about the host input buffer(s): PaUtil_SetInputChannel,
+        PaUtil_SetInterleavedInputChannels, PaUtil_SetNonInterleavedInputChannel.
+        Which function you call will depend on whether the host buffer(s) are
+        interleaved or not.
+    - If the available host data is split accross two buffers (for example a
+        data range at the end of a circular buffer and another range at the
+        beginning of the circular buffer), also call
+        PaUtil_Set2ndInputFrameCount, PaUtil_Set2ndInputChannel,
+        PaUtil_Set2ndInterleavedInputChannels,
+        PaUtil_Set2ndNonInterleavedInputChannel as necessary to tell the buffer
+        processor about the second buffer.
+ -# For a stream which generates output:
+    - Call PaUtil_SetOutputFrameCount with the number of frames in the host
+        output buffer.
+    - Call one of the following functions one or more times to tell the
+        buffer processor about the host output buffer(s): PaUtil_SetOutputChannel,
+        PaUtil_SetInterleavedOutputChannels, PaUtil_SetNonInterleavedOutputChannel.
+        Which function you call will depend on whether the host buffer(s) are
+        interleaved or not.
+    - If the available host output buffer space is split accross two buffers
+        (for example a data range at the end of a circular buffer and another
+        range at the beginning of the circular buffer), call
+        PaUtil_Set2ndOutputFrameCount, PaUtil_Set2ndOutputChannel,
+        PaUtil_Set2ndInterleavedOutputChannels,
+        PaUtil_Set2ndNonInterleavedOutputChannel as necessary to tell the buffer
+        processor about the second buffer.
+ -# Call PaUtil_EndBufferProcessing, this function performs the actual data
+    conversion and processing.
+
+
+ <h4>Using the buffer processor for a blocking read/write stream</h4>
+
+ Blocking read/write streams use the buffer processor to convert and copy user
+ output data to a host buffer, and to convert and copy host input data to
+ the user's buffer. The buffer processor does not perform any buffer adaption.
+ When using the buffer processor in a blocking read/write stream the input and
+ output conversion are performed separately by the PaUtil_CopyInput and
+ PaUtil_CopyOutput functions.
+
+ To copy data from a host input buffer to the buffer(s) which the user supplies
+ to Pa_ReadStream, use the following calling sequence.
+
+ - Repeat the following three steps until the user buffer(s) have been filled
+    with samples from the host input buffers:
+     -# Call PaUtil_SetInputFrameCount with the number of frames in the host
+        input buffer.
+     -# Call one of the following functions one or more times to tell the
+        buffer processor about the host input buffer(s): PaUtil_SetInputChannel,
+        PaUtil_SetInterleavedInputChannels, PaUtil_SetNonInterleavedInputChannel.
+        Which function you call will depend on whether the host buffer(s) are
+        interleaved or not.
+     -# Call PaUtil_CopyInput with the user buffer pointer (or a copy of the
+        array of buffer pointers for a non-interleaved stream) passed to
+        Pa_ReadStream, along with the number of frames in the user buffer(s).
+        Be careful to pass a <i>copy</i> of the user buffer pointers to
+        PaUtil_CopyInput because PaUtil_CopyInput advances the pointers to
+        the start of the next region to copy.
+ - PaUtil_CopyInput will not copy more data than is available in the
+    host buffer(s), so the above steps need to be repeated until the user
+    buffer(s) are full.
+
+ 
+ To copy data to the host output buffer from the user buffers(s) supplied
+ to Pa_WriteStream use the following calling sequence.
+
+ - Repeat the following three steps until all frames from the user buffer(s)
+    have been copied to the host API:
+     -# Call PaUtil_SetOutputFrameCount with the number of frames in the host
+        output buffer.
+     -# Call one of the following functions one or more times to tell the
+        buffer processor about the host output buffer(s): PaUtil_SetOutputChannel,
+        PaUtil_SetInterleavedOutputChannels, PaUtil_SetNonInterleavedOutputChannel.
+        Which function you call will depend on whether the host buffer(s) are
+        interleaved or not.
+     -# Call PaUtil_CopyOutput with the user buffer pointer (or a copy of the
+        array of buffer pointers for a non-interleaved stream) passed to
+        Pa_WriteStream, along with the number of frames in the user buffer(s).
+        Be careful to pass a <i>copy</i> of the user buffer pointers to 
+        PaUtil_CopyOutput because PaUtil_CopyOutput advances the pointers to
+        the start of the next region to copy.
+ - PaUtil_CopyOutput will not copy more data than fits in the host buffer(s),
+    so the above steps need to be repeated until all user data is copied.
+*/
+
+
+#include "portaudio.h"
+#include "pa_converters.h"
+#include "pa_dither.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif /* __cplusplus */
+
+
+/** @brief Mode flag passed to PaUtil_InitializeBufferProcessor indicating the type
+ of buffering that the host API uses.
+
+ The mode used depends on whether the host API or the implementation manages
+ the buffers, and how these buffers are used (scatter gather, circular buffer).
+*/
+typedef enum {
+/** The host buffer size is a fixed known size. */
+    paUtilFixedHostBufferSize,
+
+/** The host buffer size may vary, but has a known maximum size. */
+    paUtilBoundedHostBufferSize,
+
+/** Nothing is known about the host buffer size. */
+    paUtilUnknownHostBufferSize,
+
+/** The host buffer size varies, and the client does not require the buffer
+ processor to consume all of the input and fill all of the output buffer. This
+ is useful when the implementation has access to the host API's circular buffer
+ and only needs to consume/fill some of it, not necessarily all of it, with each
+ call to the buffer processor. This is the only mode where
+ PaUtil_EndBufferProcessing() may not consume the whole buffer.
+*/
+    paUtilVariableHostBufferSizePartialUsageAllowed
+}PaUtilHostBufferSizeMode;
+
+
+/** @brief An auxilliary data structure used internally by the buffer processor
+ to represent host input and output buffers. */
+typedef struct PaUtilChannelDescriptor{
+    void *data;
+    unsigned int stride;  /**< stride in samples, not bytes */
+}PaUtilChannelDescriptor;
+
+
+/** @brief The main buffer processor data structure.
+
+ Allocate one of these, initialize it with PaUtil_InitializeBufferProcessor
+ and terminate it with PaUtil_TerminateBufferProcessor.
+*/
+typedef struct {
+    unsigned long framesPerUserBuffer;
+    unsigned long framesPerHostBuffer;
+
+    PaUtilHostBufferSizeMode hostBufferSizeMode;
+    int useNonAdaptingProcess;
+    unsigned long framesPerTempBuffer;
+
+    unsigned int inputChannelCount;
+    unsigned int bytesPerHostInputSample;
+    unsigned int bytesPerUserInputSample;
+    int userInputIsInterleaved;
+    PaUtilConverter *inputConverter;
+    PaUtilZeroer *inputZeroer;
+    
+    unsigned int outputChannelCount;
+    unsigned int bytesPerHostOutputSample;
+    unsigned int bytesPerUserOutputSample;
+    int userOutputIsInterleaved;
+    PaUtilConverter *outputConverter;
+    PaUtilZeroer *outputZeroer;
+
+    unsigned long initialFramesInTempInputBuffer;
+    unsigned long initialFramesInTempOutputBuffer;
+
+    void *tempInputBuffer;          /**< used for slips, block adaption, and conversion. */
+    void **tempInputBufferPtrs;     /**< storage for non-interleaved buffer pointers, NULL for interleaved user input */
+    unsigned long framesInTempInputBuffer; /**< frames remaining in input buffer from previous adaption iteration */
+
+    void *tempOutputBuffer;         /**< used for slips, block adaption, and conversion. */
+    void **tempOutputBufferPtrs;    /**< storage for non-interleaved buffer pointers, NULL for interleaved user output */
+    unsigned long framesInTempOutputBuffer; /**< frames remaining in input buffer from previous adaption iteration */
+
+    PaStreamCallbackTimeInfo *timeInfo;
+
+    PaStreamCallbackFlags callbackStatusFlags;
+
+    unsigned long hostInputFrameCount[2];
+    PaUtilChannelDescriptor *hostInputChannels[2]; /**< pointers to arrays of channel descriptors.
+                                                        pointers are NULL for half-duplex output processing.
+                                                        hostInputChannels[i].data is NULL when the caller
+                                                        calls PaUtil_SetNoInput()
+                                                        */
+    unsigned long hostOutputFrameCount[2];
+    PaUtilChannelDescriptor *hostOutputChannels[2]; /**< pointers to arrays of channel descriptors.
+                                                         pointers are NULL for half-duplex input processing.
+                                                         hostOutputChannels[i].data is NULL when the caller
+                                                         calls PaUtil_SetNoOutput()
+                                                         */
+
+    PaUtilTriangularDitherGenerator ditherGenerator;
+
+    double samplePeriod;
+
+    PaStreamCallback *streamCallback;
+    void *userData;
+} PaUtilBufferProcessor;
+
+
+/** @name Initialization, termination, resetting and info */
+/*@{*/
+
+/** Initialize a buffer processor's representation stored in a
+ PaUtilBufferProcessor structure. Be sure to call
+ PaUtil_TerminateBufferProcessor after finishing with a buffer processor.
+
+ @param bufferProcessor The buffer processor structure to initialize.
+
+ @param inputChannelCount The number of input channels as passed to
+ Pa_OpenStream or 0 for an output-only stream.
+
+ @param userInputSampleFormat Format of user input samples, as passed to
+ Pa_OpenStream. This parameter is ignored for ouput-only streams.
+ 
+ @param hostInputSampleFormat Format of host input samples. This parameter is
+ ignored for output-only streams. See note about host buffer interleave below.
+
+ @param outputChannelCount The number of output channels as passed to
+ Pa_OpenStream or 0 for an input-only stream.
+
+ @param userOutputSampleFormat Format of user output samples, as passed to
+ Pa_OpenStream. This parameter is ignored for input-only streams.
+ 
+ @param hostOutputSampleFormat Format of host output samples. This parameter is
+ ignored for input-only streams. See note about host buffer interleave below.
+
+ @param sampleRate Sample rate of the stream. The more accurate this is the
+ better - it is used for updating time stamps when adapting buffers.
+ 
+ @param streamFlags Stream flags as passed to Pa_OpenStream, this parameter is
+ used for selecting special sample conversion options such as clipping and
+ dithering.
+ 
+ @param framesPerUserBuffer Number of frames per user buffer, as requested
+ by the framesPerBuffer parameter to Pa_OpenStream. This parameter may be
+ zero to indicate that the user will accept any (and varying) buffer sizes.
+
+ @param framesPerHostBuffer Specifies the number of frames per host buffer
+ for the fixed buffer size mode, and the maximum number of frames
+ per host buffer for the bounded host buffer size mode. It is ignored for
+ the other modes.
+
+ @param hostBufferSizeMode A mode flag indicating the size variability of
+ host buffers that will be passed to the buffer processor. See
+ PaUtilHostBufferSizeMode for further details.
+ 
+ @param streamCallback The user stream callback passed to Pa_OpenStream.
+
+ @param userData The user data field passed to Pa_OpenStream.
+    
+ @note The interleave flag is ignored for host buffer formats. Host
+ interleave is determined by the use of different SetInput and SetOutput
+ functions.
+
+ @return An error code indicating whether the initialization was successful.
+ If the error code is not PaNoError, the buffer processor was not initialized
+ and should not be used.
+ 
+ @see Pa_OpenStream, PaUtilHostBufferSizeMode, PaUtil_TerminateBufferProcessor
+*/
+PaError PaUtil_InitializeBufferProcessor( PaUtilBufferProcessor* bufferProcessor,
+            int inputChannelCount, PaSampleFormat userInputSampleFormat,
+            PaSampleFormat hostInputSampleFormat,
+            int outputChannelCount, PaSampleFormat userOutputSampleFormat,
+            PaSampleFormat hostOutputSampleFormat,
+            double sampleRate,
+            PaStreamFlags streamFlags,
+            unsigned long framesPerUserBuffer, /* 0 indicates don't care */
+            unsigned long framesPerHostBuffer,
+            PaUtilHostBufferSizeMode hostBufferSizeMode,
+            PaStreamCallback *streamCallback, void *userData );
+
+
+/** Terminate a buffer processor's representation. Deallocates any temporary
+ buffers allocated by PaUtil_InitializeBufferProcessor.
+ 
+ @param bufferProcessor The buffer processor structure to terminate.
+
+ @see PaUtil_InitializeBufferProcessor.
+*/
+void PaUtil_TerminateBufferProcessor( PaUtilBufferProcessor* bufferProcessor );
+
+
+/** Clear any internally buffered data. If you call
+ PaUtil_InitializeBufferProcessor in your OpenStream routine, make sure you
+ call PaUtil_ResetBufferProcessor in your StartStream call.
+
+ @param bufferProcessor The buffer processor to reset.
+*/
+void PaUtil_ResetBufferProcessor( PaUtilBufferProcessor* bufferProcessor );
+
+
+/** Retrieve the input latency of a buffer processor.
+
+ @param bufferProcessor The buffer processor examine.
+
+ @return The input latency introduced by the buffer processor, in frames.
+
+ @see PaUtil_GetBufferProcessorOutputLatency
+*/
+unsigned long PaUtil_GetBufferProcessorInputLatency( PaUtilBufferProcessor* bufferProcessor );
+
+/** Retrieve the output latency of a buffer processor.
+
+ @param bufferProcessor The buffer processor examine.
+
+ @return The output latency introduced by the buffer processor, in frames.
+
+ @see PaUtil_GetBufferProcessorInputLatency
+*/
+unsigned long PaUtil_GetBufferProcessorOutputLatency( PaUtilBufferProcessor* bufferProcessor );
+
+/*@}*/
+
+
+/** @name Host buffer pointer configuration
+
+ Functions to set host input and output buffers, used by both callback streams
+ and blocking read/write streams.
+*/
+/*@{*/ 
+
+
+/** Set the number of frames in the input host buffer(s) specified by the
+ PaUtil_Set*InputChannel functions.
+
+ @param bufferProcessor The buffer processor.
+
+ @param frameCount The number of host input frames. A 0 frameCount indicates to
+ use the framesPerHostBuffer value passed to PaUtil_InitializeBufferProcessor.
+
+ @see PaUtil_SetNoInput, PaUtil_SetInputChannel,
+ PaUtil_SetInterleavedInputChannels, PaUtil_SetNonInterleavedInputChannel
+*/
+void PaUtil_SetInputFrameCount( PaUtilBufferProcessor* bufferProcessor,
+        unsigned long frameCount );
+
+        
+/** Indicate that no input is avalable. This function should be used when
+ priming the output of a full-duplex stream opened with the
+ paPrimeOutputBuffersUsingStreamCallback flag. Note that it is not necessary
+ to call this or any othe PaUtil_Set*Input* functions for ouput-only streams.
+
+ @param bufferProcessor The buffer processor.
+*/
+void PaUtil_SetNoInput( PaUtilBufferProcessor* bufferProcessor );
+
+
+/** Provide the buffer processor with a pointer to a host input channel.
+
+ @param bufferProcessor The buffer processor.
+ @param channel The channel number.
+ @param data The buffer.
+ @param stride The stride from one sample to the next, in samples. For
+ interleaved host buffers, the stride will usually be the same as the number of
+ channels in the buffer.
+*/
+void PaUtil_SetInputChannel( PaUtilBufferProcessor* bufferProcessor,
+        unsigned int channel, void *data, unsigned int stride );
+
+
+/** Provide the buffer processor with a pointer to an number of interleaved
+ host input channels.
+
+ @param bufferProcessor The buffer processor.
+ @param firstChannel The first channel number.
+ @param data The buffer.
+ @param channelCount The number of interleaved channels in the buffer. If
+ channelCount is zero, the number of channels specified to
+ PaUtil_InitializeBufferProcessor will be used.
+*/
+void PaUtil_SetInterleavedInputChannels( PaUtilBufferProcessor* bufferProcessor,
+        unsigned int firstChannel, void *data, unsigned int channelCount );
+
+
+/** Provide the buffer processor with a pointer to one non-interleaved host
+ output channel.
+
+ @param bufferProcessor The buffer processor.
+ @param channel The channel number.
+ @param data The buffer.
+*/
+void PaUtil_SetNonInterleavedInputChannel( PaUtilBufferProcessor* bufferProcessor,
+        unsigned int channel, void *data );
+
+
+/** Use for the second buffer half when the input buffer is split in two halves.
+ @see PaUtil_SetInputFrameCount
+*/
+void PaUtil_Set2ndInputFrameCount( PaUtilBufferProcessor* bufferProcessor,
+        unsigned long frameCount );
+
+/** Use for the second buffer half when the input buffer is split in two halves.
+ @see PaUtil_SetInputChannel
+*/
+void PaUtil_Set2ndInputChannel( PaUtilBufferProcessor* bufferProcessor,
+        unsigned int channel, void *data, unsigned int stride );
+
+/** Use for the second buffer half when the input buffer is split in two halves.
+ @see PaUtil_SetInterleavedInputChannels
+*/
+void PaUtil_Set2ndInterleavedInputChannels( PaUtilBufferProcessor* bufferProcessor,
+        unsigned int firstChannel, void *data, unsigned int channelCount );
+
+/** Use for the second buffer half when the input buffer is split in two halves.
+ @see PaUtil_SetNonInterleavedInputChannel
+*/
+void PaUtil_Set2ndNonInterleavedInputChannel( PaUtilBufferProcessor* bufferProcessor,
+        unsigned int channel, void *data );
+
+        
+/** Set the number of frames in the output host buffer(s) specified by the
+ PaUtil_Set*OutputChannel functions.
+
+ @param bufferProcessor The buffer processor.
+
+ @param frameCount The number of host output frames. A 0 frameCount indicates to
+ use the framesPerHostBuffer value passed to PaUtil_InitializeBufferProcessor.
+
+ @see PaUtil_SetOutputChannel, PaUtil_SetInterleavedOutputChannels,
+ PaUtil_SetNonInterleavedOutputChannel
+*/
+void PaUtil_SetOutputFrameCount( PaUtilBufferProcessor* bufferProcessor,
+        unsigned long frameCount );
+
+
+/** Indicate that the output will be discarded. This function should be used
+ when implementing the paNeverDropInput mode for full duplex streams.
+
+ @param bufferProcessor The buffer processor.
+*/
+void PaUtil_SetNoOutput( PaUtilBufferProcessor* bufferProcessor );
+
+
+/** Provide the buffer processor with a pointer to a host output channel.
+
+ @param bufferProcessor The buffer processor.
+ @param channel The channel number.
+ @param data The buffer.
+ @param stride The stride from one sample to the next, in samples. For
+ interleaved host buffers, the stride will usually be the same as the number of
+ channels in the buffer.
+*/
+void PaUtil_SetOutputChannel( PaUtilBufferProcessor* bufferProcessor,
+        unsigned int channel, void *data, unsigned int stride );
+
+
+/** Provide the buffer processor with a pointer to a number of interleaved
+ host output channels.
+
+ @param bufferProcessor The buffer processor.
+ @param firstChannel The first channel number.
+ @param data The buffer.
+ @param channelCount The number of interleaved channels in the buffer. If
+ channelCount is zero, the number of channels specified to
+ PaUtil_InitializeBufferProcessor will be used.
+*/
+void PaUtil_SetInterleavedOutputChannels( PaUtilBufferProcessor* bufferProcessor,
+        unsigned int firstChannel, void *data, unsigned int channelCount );
+
+        
+/** Provide the buffer processor with a pointer to one non-interleaved host
+ output channel.
+
+ @param bufferProcessor The buffer processor.
+ @param channel The channel number.
+ @param data The buffer.
+*/
+void PaUtil_SetNonInterleavedOutputChannel( PaUtilBufferProcessor* bufferProcessor,
+        unsigned int channel, void *data );
+
+
+/** Use for the second buffer half when the output buffer is split in two halves.
+ @see PaUtil_SetOutputFrameCount
+*/
+void PaUtil_Set2ndOutputFrameCount( PaUtilBufferProcessor* bufferProcessor,
+        unsigned long frameCount );
+
+/** Use for the second buffer half when the output buffer is split in two halves.
+ @see PaUtil_SetOutputChannel
+*/
+void PaUtil_Set2ndOutputChannel( PaUtilBufferProcessor* bufferProcessor,
+        unsigned int channel, void *data, unsigned int stride );
+
+/** Use for the second buffer half when the output buffer is split in two halves.
+ @see PaUtil_SetInterleavedOutputChannels
+*/
+void PaUtil_Set2ndInterleavedOutputChannels( PaUtilBufferProcessor* bufferProcessor,
+        unsigned int firstChannel, void *data, unsigned int channelCount );
+
+/** Use for the second buffer half when the output buffer is split in two halves.
+ @see PaUtil_SetNonInterleavedOutputChannel
+*/
+void PaUtil_Set2ndNonInterleavedOutputChannel( PaUtilBufferProcessor* bufferProcessor,
+        unsigned int channel, void *data );
+
+/*@}*/
+
+
+/** @name Buffer processing functions for callback streams
+*/
+/*@{*/
+
+/** Commence processing a host buffer (or a pair of host buffers in the
+ full-duplex case) for a callback stream.
+
+ @param bufferProcessor The buffer processor.
+
+ @param timeInfo Timing information for the first sample of the host
+ buffer(s). This information may be adjusted when buffer adaption is being
+ performed.
+
+ @param callbackStatusFlags Flags indicating whether underruns and overruns
+ have occurred since the last time the buffer processor was called.
+*/
+void PaUtil_BeginBufferProcessing( PaUtilBufferProcessor* bufferProcessor,
+        PaStreamCallbackTimeInfo* timeInfo, PaStreamCallbackFlags callbackStatusFlags );
+
+        
+/** Finish processing a host buffer (or a pair of host buffers in the
+ full-duplex case) for a callback stream.
+
+ @param bufferProcessor The buffer processor.
+ 
+ @param callbackResult On input, indicates a previous callback result, and on
+ exit, the result of the user stream callback, if it is called.
+ On entry callbackResult should contain one of { paContinue, paComplete, or
+ paAbort}. If paComplete is passed, the stream callback will not be called
+ but any audio that was generated by previous stream callbacks will be copied
+ to the output buffer(s). You can check whether the buffer processor's internal
+ buffer is empty by calling PaUtil_IsBufferProcessorOutputEmpty.
+
+ If the stream callback is called its result is stored in *callbackResult. If
+ the stream callback returns paComplete or paAbort, all output buffers will be
+ full of valid data - some of which may be zeros to acount for data that
+ wasn't generated by the terminating callback.
+
+ @return The number of frames processed. This usually corresponds to the
+ number of frames specified by the PaUtil_Set*FrameCount functions, exept in
+ the paUtilVariableHostBufferSizePartialUsageAllowed buffer size mode when a
+ smaller value may be returned.
+*/
+unsigned long PaUtil_EndBufferProcessing( PaUtilBufferProcessor* bufferProcessor,
+        int *callbackResult );
+
+
+/** Determine whether any callback generated output remains in the bufffer
+ processor's internal buffers. This method may be used to determine when to
+ continue calling PaUtil_EndBufferProcessing() after the callback has returned
+ a callbackResult of paComplete.
+
+ @param bufferProcessor The buffer processor.
+ 
+ @return Returns non-zero when callback generated output remains in the internal
+ buffer and zero (0) when there internal buffer contains no callback generated
+ data.
+*/
+int PaUtil_IsBufferProcessorOutputEmpty( PaUtilBufferProcessor* bufferProcessor );
+
+/*@}*/
+
+
+/** @name Buffer processing functions for blocking read/write streams
+*/
+/*@{*/
+
+/** Copy samples from host input channels set up by the PaUtil_Set*InputChannels
+ functions to a user supplied buffer. This function is intended for use with
+ blocking read/write streams. Copies the minimum of the number of
+ user frames (specified by the frameCount parameter) and the number of available
+ host frames (specified in a previous call to SetInputFrameCount()).
+
+ @param bufferProcessor The buffer processor.
+
+ @param buffer A pointer to the user buffer pointer, or a pointer to a pointer
+ to an array of user buffer pointers for a non-interleaved stream. It is
+ important that this parameter points to a copy of the user buffer pointers,
+ not to the actual user buffer pointers, because this function updates the
+ pointers before returning.
+
+ @param frameCount The number of frames of data in the buffer(s) pointed to by
+ the buffer parameter.
+
+ @return The number of frames copied. The buffer pointer(s) pointed to by the
+ buffer parameter are advanced to point to the frame(s) following the last one
+ filled.
+*/
+unsigned long PaUtil_CopyInput( PaUtilBufferProcessor* bufferProcessor,
+        void **buffer, unsigned long frameCount );
+
+
+/* Copy samples from a user supplied buffer to host output channels set up by
+ the PaUtil_Set*OutputChannels functions. This function is intended for use with
+ blocking read/write streams. Copies the minimum of the number of
+ user frames (specified by the frameCount parameter) and the number of
+ host frames (specified in a previous call to SetOutputFrameCount()).
+
+ @param bufferProcessor The buffer processor.
+
+ @param buffer A pointer to the user buffer pointer, or a pointer to a pointer
+ to an array of user buffer pointers for a non-interleaved stream. It is
+ important that this parameter points to a copy of the user buffer pointers,
+ not to the actual user buffer pointers, because this function updates the
+ pointers before returning.
+
+ @param frameCount The number of frames of data in the buffer(s) pointed to by
+ the buffer parameter.
+
+ @return The number of frames copied. The buffer pointer(s) pointed to by the
+ buffer parameter are advanced to point to the frame(s) following the last one
+ copied.
+*/
+unsigned long PaUtil_CopyOutput( PaUtilBufferProcessor* bufferProcessor,
+        const void ** buffer, unsigned long frameCount );
+
+
+/* Zero samples in host output channels set up by the PaUtil_Set*OutputChannels
+ functions. This function is useful for flushing streams.
+ Zeros the minimum of frameCount and the number of host frames specified in a
+ previous call to SetOutputFrameCount().
+
+ @param bufferProcessor The buffer processor.
+
+ @param frameCount The maximum number of frames to zero.
+ 
+ @return The number of frames zeroed.
+*/
+unsigned long PaUtil_ZeroOutput( PaUtilBufferProcessor* bufferProcessor,
+        unsigned long frameCount );
+
+
+/*@}*/
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif /* PA_PROCESS_H */
diff --git a/src/audio/portaudio/pa_common/pa_skeleton.c b/src/audio/portaudio/pa_common/pa_skeleton.c
new file mode 100644
index 0000000000000000000000000000000000000000..51f95c8a3201d5605baf89dece660c402e8c9a7e
--- /dev/null
+++ b/src/audio/portaudio/pa_common/pa_skeleton.c
@@ -0,0 +1,807 @@
+/*
+ * $Id$
+ * Portable Audio I/O Library skeleton implementation
+ * demonstrates how to use the common functions to implement support
+ * for a host API
+ *
+ * Based on the Open Source API proposed by Ross Bencina
+ * Copyright (c) 1999-2002 Ross Bencina, 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.
+ */
+
+/** @file
+ @brief Skeleton implementation of support for a host API.
+
+ @note This file is provided as a starting point for implementing support for
+ a new host API. IMPLEMENT ME comments are used to indicate functionality
+ which much be customised for each implementation.
+*/
+
+
+#include <string.h> /* strlen() */
+
+#include "pa_util.h"
+#include "pa_allocation.h"
+#include "pa_hostapi.h"
+#include "pa_stream.h"
+#include "pa_cpuload.h"
+#include "pa_process.h"
+
+
+/* prototypes for functions declared in this file */
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif /* __cplusplus */
+
+PaError PaSkeleton_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+
+static void Terminate( struct PaUtilHostApiRepresentation *hostApi );
+static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
+                                  const PaStreamParameters *inputParameters,
+                                  const PaStreamParameters *outputParameters,
+                                  double sampleRate );
+static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
+                           PaStream** s,
+                           const PaStreamParameters *inputParameters,
+                           const PaStreamParameters *outputParameters,
+                           double sampleRate,
+                           unsigned long framesPerBuffer,
+                           PaStreamFlags streamFlags,
+                           PaStreamCallback *streamCallback,
+                           void *userData );
+static PaError CloseStream( PaStream* stream );
+static PaError StartStream( PaStream *stream );
+static PaError StopStream( PaStream *stream );
+static PaError AbortStream( PaStream *stream );
+static PaError IsStreamStopped( PaStream *s );
+static PaError IsStreamActive( PaStream *stream );
+static PaTime GetStreamTime( PaStream *stream );
+static double GetStreamCpuLoad( PaStream* stream );
+static PaError ReadStream( PaStream* stream, void *buffer, unsigned long frames );
+static PaError WriteStream( PaStream* stream, const void *buffer, unsigned long frames );
+static signed long GetStreamReadAvailable( PaStream* stream );
+static signed long GetStreamWriteAvailable( PaStream* stream );
+
+
+/* IMPLEMENT ME: a macro like the following one should be used for reporting
+ host errors */
+#define PA_SKELETON_SET_LAST_HOST_ERROR( errorCode, errorText ) \
+    PaUtil_SetLastHostErrorInfo( paInDevelopment, errorCode, errorText )
+
+/* PaSkeletonHostApiRepresentation - host api datastructure specific to this implementation */
+
+typedef struct
+{
+    PaUtilHostApiRepresentation inheritedHostApiRep;
+    PaUtilStreamInterface callbackStreamInterface;
+    PaUtilStreamInterface blockingStreamInterface;
+
+    PaUtilAllocationGroup *allocations;
+
+    /* implementation specific data goes here */
+}
+PaSkeletonHostApiRepresentation;  /* IMPLEMENT ME: rename this */
+
+
+PaError PaSkeleton_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex )
+{
+    PaError result = paNoError;
+    int i, deviceCount;
+    PaSkeletonHostApiRepresentation *skeletonHostApi;
+    PaDeviceInfo *deviceInfoArray;
+
+    skeletonHostApi = (PaSkeletonHostApiRepresentation*)PaUtil_AllocateMemory( sizeof(PaSkeletonHostApiRepresentation) );
+    if( !skeletonHostApi )
+    {
+        result = paInsufficientMemory;
+        goto error;
+    }
+
+    skeletonHostApi->allocations = PaUtil_CreateAllocationGroup();
+    if( !skeletonHostApi->allocations )
+    {
+        result = paInsufficientMemory;
+        goto error;
+    }
+
+    *hostApi = &skeletonHostApi->inheritedHostApiRep;
+    (*hostApi)->info.structVersion = 1;
+    (*hostApi)->info.type = paInDevelopment;            /* IMPLEMENT ME: change to correct type id */
+    (*hostApi)->info.name = "skeleton implementation";  /* IMPLEMENT ME: change to correct name */
+
+    (*hostApi)->info.defaultInputDevice = paNoDevice;  /* IMPLEMENT ME */
+    (*hostApi)->info.defaultOutputDevice = paNoDevice; /* IMPLEMENT ME */
+
+    (*hostApi)->info.deviceCount = 0;  
+
+    deviceCount = 0; /* IMPLEMENT ME */
+    
+    if( deviceCount > 0 )
+    {
+        (*hostApi)->deviceInfos = (PaDeviceInfo**)PaUtil_GroupAllocateMemory(
+                skeletonHostApi->allocations, sizeof(PaDeviceInfo*) * deviceCount );
+        if( !(*hostApi)->deviceInfos )
+        {
+            result = paInsufficientMemory;
+            goto error;
+        }
+
+        /* allocate all device info structs in a contiguous block */
+        deviceInfoArray = (PaDeviceInfo*)PaUtil_GroupAllocateMemory(
+                skeletonHostApi->allocations, sizeof(PaDeviceInfo) * deviceCount );
+        if( !deviceInfoArray )
+        {
+            result = paInsufficientMemory;
+            goto error;
+        }
+
+        for( i=0; i < deviceCount; ++i )
+        {
+            PaDeviceInfo *deviceInfo = &deviceInfoArray[i];
+            deviceInfo->structVersion = 2;
+            deviceInfo->hostApi = hostApiIndex;
+            deviceInfo->name = 0; /* IMPLEMENT ME: allocate block and copy name eg:
+                deviceName = (char*)PaUtil_GroupAllocateMemory( skeletonHostApi->allocations, strlen(srcName) + 1 );
+                if( !deviceName )
+                {
+                    result = paInsufficientMemory;
+                    goto error;
+                }
+                strcpy( deviceName, srcName );
+                deviceInfo->name = deviceName;
+            */
+
+            deviceInfo->maxInputChannels = 0;  /* IMPLEMENT ME */
+            deviceInfo->maxOutputChannels = 0;  /* IMPLEMENT ME */
+            
+            deviceInfo->defaultLowInputLatency = 0.;  /* IMPLEMENT ME */
+            deviceInfo->defaultLowOutputLatency = 0.;  /* IMPLEMENT ME */
+            deviceInfo->defaultHighInputLatency = 0.;  /* IMPLEMENT ME */
+            deviceInfo->defaultHighOutputLatency = 0.;  /* IMPLEMENT ME */  
+
+            deviceInfo->defaultSampleRate = 0.; /* IMPLEMENT ME */
+            
+            (*hostApi)->deviceInfos[i] = deviceInfo;
+            ++(*hostApi)->info.deviceCount;
+        }
+    }
+
+    (*hostApi)->Terminate = Terminate;
+    (*hostApi)->OpenStream = OpenStream;
+    (*hostApi)->IsFormatSupported = IsFormatSupported;
+
+    PaUtil_InitializeStreamInterface( &skeletonHostApi->callbackStreamInterface, CloseStream, StartStream,
+                                      StopStream, AbortStream, IsStreamStopped, IsStreamActive,
+                                      GetStreamTime, GetStreamCpuLoad,
+                                      PaUtil_DummyRead, PaUtil_DummyWrite,
+                                      PaUtil_DummyGetReadAvailable, PaUtil_DummyGetWriteAvailable );
+
+    PaUtil_InitializeStreamInterface( &skeletonHostApi->blockingStreamInterface, CloseStream, StartStream,
+                                      StopStream, AbortStream, IsStreamStopped, IsStreamActive,
+                                      GetStreamTime, PaUtil_DummyGetCpuLoad,
+                                      ReadStream, WriteStream, GetStreamReadAvailable, GetStreamWriteAvailable );
+
+    return result;
+
+error:
+    if( skeletonHostApi )
+    {
+        if( skeletonHostApi->allocations )
+        {
+            PaUtil_FreeAllAllocations( skeletonHostApi->allocations );
+            PaUtil_DestroyAllocationGroup( skeletonHostApi->allocations );
+        }
+                
+        PaUtil_FreeMemory( skeletonHostApi );
+    }
+    return result;
+}
+
+
+static void Terminate( struct PaUtilHostApiRepresentation *hostApi )
+{
+    PaSkeletonHostApiRepresentation *skeletonHostApi = (PaSkeletonHostApiRepresentation*)hostApi;
+
+    /*
+        IMPLEMENT ME:
+            - clean up any resources not handled by the allocation group
+    */
+
+    if( skeletonHostApi->allocations )
+    {
+        PaUtil_FreeAllAllocations( skeletonHostApi->allocations );
+        PaUtil_DestroyAllocationGroup( skeletonHostApi->allocations );
+    }
+
+    PaUtil_FreeMemory( skeletonHostApi );
+}
+
+
+static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
+                                  const PaStreamParameters *inputParameters,
+                                  const PaStreamParameters *outputParameters,
+                                  double sampleRate )
+{
+    int inputChannelCount, outputChannelCount;
+    PaSampleFormat inputSampleFormat, outputSampleFormat;
+    
+    if( inputParameters )
+    {
+        inputChannelCount = inputParameters->channelCount;
+        inputSampleFormat = inputParameters->sampleFormat;
+
+        /* all standard sample formats are supported by the buffer adapter,
+            this implementation doesn't support any custom sample formats */
+        if( inputSampleFormat & paCustomFormat )
+            return paSampleFormatNotSupported;
+            
+        /* unless alternate device specification is supported, reject the use of
+            paUseHostApiSpecificDeviceSpecification */
+
+        if( inputParameters->device == paUseHostApiSpecificDeviceSpecification )
+            return paInvalidDevice;
+
+        /* check that input device can support inputChannelCount */
+        if( inputChannelCount > hostApi->deviceInfos[ inputParameters->device ]->maxInputChannels )
+            return paInvalidChannelCount;
+
+        /* validate inputStreamInfo */
+        if( inputParameters->hostApiSpecificStreamInfo )
+            return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */
+    }
+    else
+    {
+        inputChannelCount = 0;
+    }
+
+    if( outputParameters )
+    {
+        outputChannelCount = outputParameters->channelCount;
+        outputSampleFormat = outputParameters->sampleFormat;
+
+        /* all standard sample formats are supported by the buffer adapter,
+            this implementation doesn't support any custom sample formats */
+        if( outputSampleFormat & paCustomFormat )
+            return paSampleFormatNotSupported;
+            
+        /* unless alternate device specification is supported, reject the use of
+            paUseHostApiSpecificDeviceSpecification */
+
+        if( outputParameters->device == paUseHostApiSpecificDeviceSpecification )
+            return paInvalidDevice;
+
+        /* check that output device can support outputChannelCount */
+        if( outputChannelCount > hostApi->deviceInfos[ outputParameters->device ]->maxOutputChannels )
+            return paInvalidChannelCount;
+
+        /* validate outputStreamInfo */
+        if( outputParameters->hostApiSpecificStreamInfo )
+            return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */
+    }
+    else
+    {
+        outputChannelCount = 0;
+    }
+    
+    /*
+        IMPLEMENT ME:
+
+            - if a full duplex stream is requested, check that the combination
+                of input and output parameters is supported if necessary
+
+            - check that the device supports sampleRate
+
+        Because the buffer adapter handles conversion between all standard
+        sample formats, the following checks are only required if paCustomFormat
+        is implemented, or under some other unusual conditions.
+
+            - check that input device can support inputSampleFormat, or that
+                we have the capability to convert from inputSampleFormat to
+                a native format
+
+            - check that output device can support outputSampleFormat, or that
+                we have the capability to convert from outputSampleFormat to
+                a native format
+    */
+
+
+    /* suppress unused variable warnings */
+    (void) sampleRate;
+
+    return paFormatIsSupported;
+}
+
+/* PaSkeletonStream - a stream data structure specifically for this implementation */
+
+typedef struct PaSkeletonStream
+{ /* IMPLEMENT ME: rename this */
+    PaUtilStreamRepresentation streamRepresentation;
+    PaUtilCpuLoadMeasurer cpuLoadMeasurer;
+    PaUtilBufferProcessor bufferProcessor;
+
+    /* IMPLEMENT ME:
+            - implementation specific data goes here
+    */
+    unsigned long framesPerHostCallback; /* just an example */
+}
+PaSkeletonStream;
+
+/* see pa_hostapi.h for a list of validity guarantees made about OpenStream parameters */
+
+static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
+                           PaStream** s,
+                           const PaStreamParameters *inputParameters,
+                           const PaStreamParameters *outputParameters,
+                           double sampleRate,
+                           unsigned long framesPerBuffer,
+                           PaStreamFlags streamFlags,
+                           PaStreamCallback *streamCallback,
+                           void *userData )
+{
+    PaError result = paNoError;
+    PaSkeletonHostApiRepresentation *skeletonHostApi = (PaSkeletonHostApiRepresentation*)hostApi;
+    PaSkeletonStream *stream = 0;
+    unsigned long framesPerHostBuffer = framesPerBuffer; /* these may not be equivalent for all implementations */
+    int inputChannelCount, outputChannelCount;
+    PaSampleFormat inputSampleFormat, outputSampleFormat;
+    PaSampleFormat hostInputSampleFormat, hostOutputSampleFormat;
+
+
+    if( inputParameters )
+    {
+        inputChannelCount = inputParameters->channelCount;
+        inputSampleFormat = inputParameters->sampleFormat;
+
+        /* unless alternate device specification is supported, reject the use of
+            paUseHostApiSpecificDeviceSpecification */
+
+        if( inputParameters->device == paUseHostApiSpecificDeviceSpecification )
+            return paInvalidDevice;
+
+        /* check that input device can support inputChannelCount */
+        if( inputChannelCount > hostApi->deviceInfos[ inputParameters->device ]->maxInputChannels )
+            return paInvalidChannelCount;
+
+        /* validate inputStreamInfo */
+        if( inputParameters->hostApiSpecificStreamInfo )
+            return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */
+
+        /* IMPLEMENT ME - establish which  host formats are available */
+        hostInputSampleFormat =
+            PaUtil_SelectClosestAvailableFormat( paInt16 /* native formats */, inputSampleFormat );
+    }
+    else
+    {
+        inputChannelCount = 0;
+        inputSampleFormat = hostInputSampleFormat = paInt16; /* Surpress 'uninitialised var' warnings. */
+    }
+
+    if( outputParameters )
+    {
+        outputChannelCount = outputParameters->channelCount;
+        outputSampleFormat = outputParameters->sampleFormat;
+        
+        /* unless alternate device specification is supported, reject the use of
+            paUseHostApiSpecificDeviceSpecification */
+
+        if( outputParameters->device == paUseHostApiSpecificDeviceSpecification )
+            return paInvalidDevice;
+
+        /* check that output device can support inputChannelCount */
+        if( outputChannelCount > hostApi->deviceInfos[ outputParameters->device ]->maxOutputChannels )
+            return paInvalidChannelCount;
+
+        /* validate outputStreamInfo */
+        if( outputParameters->hostApiSpecificStreamInfo )
+            return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */
+
+        /* IMPLEMENT ME - establish which  host formats are available */
+        hostOutputSampleFormat =
+            PaUtil_SelectClosestAvailableFormat( paInt16 /* native formats */, outputSampleFormat );
+    }
+    else
+    {
+        outputChannelCount = 0;
+        outputSampleFormat = hostOutputSampleFormat = paInt16; /* Surpress 'uninitialized var' warnings. */
+    }
+
+    /*
+        IMPLEMENT ME:
+
+        ( the following two checks are taken care of by PaUtil_InitializeBufferProcessor() FIXME - checks needed? )
+
+            - check that input device can support inputSampleFormat, or that
+                we have the capability to convert from outputSampleFormat to
+                a native format
+
+            - check that output device can support outputSampleFormat, or that
+                we have the capability to convert from outputSampleFormat to
+                a native format
+
+            - if a full duplex stream is requested, check that the combination
+                of input and output parameters is supported
+
+            - check that the device supports sampleRate
+
+            - alter sampleRate to a close allowable rate if possible / necessary
+
+            - validate suggestedInputLatency and suggestedOutputLatency parameters,
+                use default values where necessary
+    */
+
+
+
+
+    /* validate platform specific flags */
+    if( (streamFlags & paPlatformSpecificFlags) != 0 )
+        return paInvalidFlag; /* unexpected platform specific flag */
+
+
+    stream = (PaSkeletonStream*)PaUtil_AllocateMemory( sizeof(PaSkeletonStream) );
+    if( !stream )
+    {
+        result = paInsufficientMemory;
+        goto error;
+    }
+
+    if( streamCallback )
+    {
+        PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation,
+                                               &skeletonHostApi->callbackStreamInterface, streamCallback, userData );
+    }
+    else
+    {
+        PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation,
+                                               &skeletonHostApi->blockingStreamInterface, streamCallback, userData );
+    }
+
+    PaUtil_InitializeCpuLoadMeasurer( &stream->cpuLoadMeasurer, sampleRate );
+
+
+    /* we assume a fixed host buffer size in this example, but the buffer processor
+        can also support bounded and unknown host buffer sizes by passing 
+        paUtilBoundedHostBufferSize or paUtilUnknownHostBufferSize instead of
+        paUtilFixedHostBufferSize below. */
+        
+    result =  PaUtil_InitializeBufferProcessor( &stream->bufferProcessor,
+              inputChannelCount, inputSampleFormat, hostInputSampleFormat,
+              outputChannelCount, outputSampleFormat, hostOutputSampleFormat,
+              sampleRate, streamFlags, framesPerBuffer,
+              framesPerHostBuffer, paUtilFixedHostBufferSize,
+              streamCallback, userData );
+    if( result != paNoError )
+        goto error;
+
+
+    /*
+        IMPLEMENT ME: initialise the following fields with estimated or actual
+        values.
+    */
+    stream->streamRepresentation.streamInfo.inputLatency =
+            PaUtil_GetBufferProcessorInputLatency(&stream->bufferProcessor);
+    stream->streamRepresentation.streamInfo.outputLatency =
+            PaUtil_GetBufferProcessorOutputLatency(&stream->bufferProcessor);
+    stream->streamRepresentation.streamInfo.sampleRate = sampleRate;
+
+    
+    /*
+        IMPLEMENT ME:
+            - additional stream setup + opening
+    */
+
+    stream->framesPerHostCallback = framesPerHostBuffer;
+
+    *s = (PaStream*)stream;
+
+    return result;
+
+error:
+    if( stream )
+        PaUtil_FreeMemory( stream );
+
+    return result;
+}
+
+/*
+    ExampleHostProcessingLoop() illustrates the kind of processing which may
+    occur in a host implementation.
+ 
+*/
+static void ExampleHostProcessingLoop( void *inputBuffer, void *outputBuffer, void *userData )
+{
+    PaSkeletonStream *stream = (PaSkeletonStream*)userData;
+    PaStreamCallbackTimeInfo timeInfo = {0,0,0}; /* IMPLEMENT ME */
+    int callbackResult;
+    unsigned long framesProcessed;
+    
+    PaUtil_BeginCpuLoadMeasurement( &stream->cpuLoadMeasurer );
+    
+    /*
+        IMPLEMENT ME:
+            - generate timing information
+            - handle buffer slips
+    */
+
+    /*
+        If you need to byte swap or shift inputBuffer to convert it into a
+        portaudio format, do it here.
+    */
+
+
+
+    PaUtil_BeginBufferProcessing( &stream->bufferProcessor, &timeInfo, 0 /* IMPLEMENT ME: pass underflow/overflow flags when necessary */ );
+
+    /*
+        depending on whether the host buffers are interleaved, non-interleaved
+        or a mixture, you will want to call PaUtil_SetInterleaved*Channels(),
+        PaUtil_SetNonInterleaved*Channel() or PaUtil_Set*Channel() here.
+    */
+    
+    PaUtil_SetInputFrameCount( &stream->bufferProcessor, 0 /* default to host buffer size */ );
+    PaUtil_SetInterleavedInputChannels( &stream->bufferProcessor,
+            0, /* first channel of inputBuffer is channel 0 */
+            inputBuffer,
+            0 ); /* 0 - use inputChannelCount passed to init buffer processor */
+
+    PaUtil_SetOutputFrameCount( &stream->bufferProcessor, 0 /* default to host buffer size */ );
+    PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor,
+            0, /* first channel of outputBuffer is channel 0 */
+            outputBuffer,
+            0 ); /* 0 - use outputChannelCount passed to init buffer processor */
+
+    /* you must pass a valid value of callback result to PaUtil_EndBufferProcessing()
+        in general you would pass paContinue for normal operation, and
+        paComplete to drain the buffer processor's internal output buffer.
+        You can check whether the buffer processor's output buffer is empty
+        using PaUtil_IsBufferProcessorOuputEmpty( bufferProcessor )
+    */
+    callbackResult = paContinue;
+    framesProcessed = PaUtil_EndBufferProcessing( &stream->bufferProcessor, &callbackResult );
+
+    
+    /*
+        If you need to byte swap or shift outputBuffer to convert it to
+        host format, do it here.
+    */
+
+    PaUtil_EndCpuLoadMeasurement( &stream->cpuLoadMeasurer, framesProcessed );
+
+
+    if( callbackResult == paContinue )
+    {
+        /* nothing special to do */
+    }
+    else if( callbackResult == paAbort )
+    {
+        /* IMPLEMENT ME - finish playback immediately  */
+
+        /* once finished, call the finished callback */
+        if( stream->streamRepresentation.streamFinishedCallback != 0 )
+            stream->streamRepresentation.streamFinishedCallback( stream->streamRepresentation.userData );
+    }
+    else
+    {
+        /* User callback has asked us to stop with paComplete or other non-zero value */
+
+        /* IMPLEMENT ME - finish playback once currently queued audio has completed  */
+
+        /* once finished, call the finished callback */
+        if( stream->streamRepresentation.streamFinishedCallback != 0 )
+            stream->streamRepresentation.streamFinishedCallback( stream->streamRepresentation.userData );
+    }
+}
+
+
+/*
+    When CloseStream() is called, the multi-api layer ensures that
+    the stream has already been stopped or aborted.
+*/
+static PaError CloseStream( PaStream* s )
+{
+    PaError result = paNoError;
+    PaSkeletonStream *stream = (PaSkeletonStream*)s;
+
+    /*
+        IMPLEMENT ME:
+            - additional stream closing + cleanup
+    */
+
+    PaUtil_TerminateBufferProcessor( &stream->bufferProcessor );
+    PaUtil_TerminateStreamRepresentation( &stream->streamRepresentation );
+    PaUtil_FreeMemory( stream );
+
+    return result;
+}
+
+
+static PaError StartStream( PaStream *s )
+{
+    PaError result = paNoError;
+    PaSkeletonStream *stream = (PaSkeletonStream*)s;
+
+    PaUtil_ResetBufferProcessor( &stream->bufferProcessor );
+
+    /* IMPLEMENT ME, see portaudio.h for required behavior */
+
+    /* suppress unused function warning. the code in ExampleHostProcessingLoop or
+       something similar should be implemented to feed samples to and from the
+       host after StartStream() is called.
+    */
+    (void) ExampleHostProcessingLoop;
+
+    return result;
+}
+
+
+static PaError StopStream( PaStream *s )
+{
+    PaError result = paNoError;
+    PaSkeletonStream *stream = (PaSkeletonStream*)s;
+
+    /* suppress unused variable warnings */
+    (void) stream;
+
+    /* IMPLEMENT ME, see portaudio.h for required behavior */
+
+    return result;
+}
+
+
+static PaError AbortStream( PaStream *s )
+{
+    PaError result = paNoError;
+    PaSkeletonStream *stream = (PaSkeletonStream*)s;
+
+    /* suppress unused variable warnings */
+    (void) stream;
+    
+    /* IMPLEMENT ME, see portaudio.h for required behavior */
+
+    return result;
+}
+
+
+static PaError IsStreamStopped( PaStream *s )
+{
+    PaSkeletonStream *stream = (PaSkeletonStream*)s;
+
+    /* suppress unused variable warnings */
+    (void) stream;
+    
+    /* IMPLEMENT ME, see portaudio.h for required behavior */
+
+    return 0;
+}
+
+
+static PaError IsStreamActive( PaStream *s )
+{
+    PaSkeletonStream *stream = (PaSkeletonStream*)s;
+
+    /* suppress unused variable warnings */
+    (void) stream;
+    
+    /* IMPLEMENT ME, see portaudio.h for required behavior */
+
+    return 0;
+}
+
+
+static PaTime GetStreamTime( PaStream *s )
+{
+    PaSkeletonStream *stream = (PaSkeletonStream*)s;
+
+    /* suppress unused variable warnings */
+    (void) stream;
+    
+    /* IMPLEMENT ME, see portaudio.h for required behavior*/
+
+    return 0;
+}
+
+
+static double GetStreamCpuLoad( PaStream* s )
+{
+    PaSkeletonStream *stream = (PaSkeletonStream*)s;
+
+    return PaUtil_GetCpuLoad( &stream->cpuLoadMeasurer );
+}
+
+
+/*
+    As separate stream interfaces are used for blocking and callback
+    streams, the following functions can be guaranteed to only be called
+    for blocking streams.
+*/
+
+static PaError ReadStream( PaStream* s,
+                           void *buffer,
+                           unsigned long frames )
+{
+    PaSkeletonStream *stream = (PaSkeletonStream*)s;
+
+    /* suppress unused variable warnings */
+    (void) buffer;
+    (void) frames;
+    (void) stream;
+    
+    /* IMPLEMENT ME, see portaudio.h for required behavior*/
+
+    return paNoError;
+}
+
+
+static PaError WriteStream( PaStream* s,
+                            const void *buffer,
+                            unsigned long frames )
+{
+    PaSkeletonStream *stream = (PaSkeletonStream*)s;
+
+    /* suppress unused variable warnings */
+    (void) buffer;
+    (void) frames;
+    (void) stream;
+    
+    /* IMPLEMENT ME, see portaudio.h for required behavior*/
+
+    return paNoError;
+}
+
+
+static signed long GetStreamReadAvailable( PaStream* s )
+{
+    PaSkeletonStream *stream = (PaSkeletonStream*)s;
+
+    /* suppress unused variable warnings */
+    (void) stream;
+    
+    /* IMPLEMENT ME, see portaudio.h for required behavior*/
+
+    return 0;
+}
+
+
+static signed long GetStreamWriteAvailable( PaStream* s )
+{
+    PaSkeletonStream *stream = (PaSkeletonStream*)s;
+
+    /* suppress unused variable warnings */
+    (void) stream;
+    
+    /* IMPLEMENT ME, see portaudio.h for required behavior*/
+
+    return 0;
+}
+
+
+
+
diff --git a/src/audio/portaudio/pa_common/pa_stream.c b/src/audio/portaudio/pa_common/pa_stream.c
new file mode 100644
index 0000000000000000000000000000000000000000..e820671bc1c5b0daf47b364d94ff0d8471f8f8d3
--- /dev/null
+++ b/src/audio/portaudio/pa_common/pa_stream.c
@@ -0,0 +1,141 @@
+/*
+ * $Id$
+ * Portable Audio I/O Library
+ * 
+ *
+ * Based on the Open Source API proposed by Ross Bencina
+ * Copyright (c) 2002 Ross Bencina
+ *
+ * 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.
+ */
+
+/** @file
+ @brief Interface used by pa_front to virtualize functions which operate on
+ streams.
+*/
+
+
+#include "pa_stream.h"
+
+
+void PaUtil_InitializeStreamInterface( PaUtilStreamInterface *streamInterface,
+                                       PaError (*Close)( PaStream* ),
+                                       PaError (*Start)( PaStream* ),
+                                       PaError (*Stop)( PaStream* ),
+                                       PaError (*Abort)( PaStream* ),
+                                       PaError (*IsStopped)( PaStream* ),
+                                       PaError (*IsActive)( PaStream* ),
+                                       PaTime (*GetTime)( PaStream* ),
+                                       double (*GetCpuLoad)( PaStream* ),
+                                       PaError (*Read)( PaStream*, void *, unsigned long ),
+                                       PaError (*Write)( PaStream*, const void *, unsigned long ),
+                                       signed long (*GetReadAvailable)( PaStream* ),
+                                       signed long (*GetWriteAvailable)( PaStream* )  )
+{
+    streamInterface->Close = Close;
+    streamInterface->Start = Start;
+    streamInterface->Stop = Stop;
+    streamInterface->Abort = Abort;
+    streamInterface->IsStopped = IsStopped;
+    streamInterface->IsActive = IsActive;
+    streamInterface->GetTime = GetTime;
+    streamInterface->GetCpuLoad = GetCpuLoad;
+    streamInterface->Read = Read;
+    streamInterface->Write = Write;
+    streamInterface->GetReadAvailable = GetReadAvailable;
+    streamInterface->GetWriteAvailable = GetWriteAvailable;
+}
+
+
+void PaUtil_InitializeStreamRepresentation( PaUtilStreamRepresentation *streamRepresentation,
+        PaUtilStreamInterface *streamInterface,
+        PaStreamCallback *streamCallback,
+        void *userData )
+{
+    streamRepresentation->magic = PA_STREAM_MAGIC;
+    streamRepresentation->nextOpenStream = 0;
+    streamRepresentation->streamInterface = streamInterface;
+    streamRepresentation->streamCallback = streamCallback;
+    streamRepresentation->streamFinishedCallback = 0;
+
+    streamRepresentation->userData = userData;
+
+    streamRepresentation->streamInfo.inputLatency = 0.;
+    streamRepresentation->streamInfo.outputLatency = 0.;
+    streamRepresentation->streamInfo.sampleRate = 0.;
+}
+
+
+void PaUtil_TerminateStreamRepresentation( PaUtilStreamRepresentation *streamRepresentation )
+{
+    streamRepresentation->magic = 0;
+}
+
+
+PaError PaUtil_DummyRead( PaStream* stream,
+                               void *buffer,
+                               unsigned long frames )
+{
+    (void)stream; /* unused parameter */
+    (void)buffer; /* unused parameter */
+    (void)frames; /* unused parameter */
+
+    return paCanNotReadFromACallbackStream;
+}
+
+
+PaError PaUtil_DummyWrite( PaStream* stream,
+                               const void *buffer,
+                               unsigned long frames )
+{
+    (void)stream; /* unused parameter */
+    (void)buffer; /* unused parameter */
+    (void)frames; /* unused parameter */
+
+    return paCanNotWriteToACallbackStream;
+}
+
+
+signed long PaUtil_DummyGetReadAvailable( PaStream* stream )
+{
+    (void)stream; /* unused parameter */
+
+    return paCanNotReadFromACallbackStream;
+}
+
+
+signed long PaUtil_DummyGetWriteAvailable( PaStream* stream )
+{
+    (void)stream; /* unused parameter */
+
+    return paCanNotWriteToACallbackStream;
+}
+
+
+double PaUtil_DummyGetCpuLoad( PaStream* stream )
+{
+    (void)stream; /* unused parameter */
+
+    return 0.0;
+}
diff --git a/src/audio/portaudio/pa_common/pa_stream.h b/src/audio/portaudio/pa_common/pa_stream.h
new file mode 100644
index 0000000000000000000000000000000000000000..aaf89cf210c6f02367b1fbc57176c73a7d64b930
--- /dev/null
+++ b/src/audio/portaudio/pa_common/pa_stream.h
@@ -0,0 +1,196 @@
+#ifndef PA_STREAM_H
+#define PA_STREAM_H
+/*
+ * $Id$
+ * Portable Audio I/O Library
+ * stream interface
+ *
+ * Based on the Open Source API proposed by Ross Bencina
+ * Copyright (c) 1999-2002 Ross Bencina, 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.
+ */
+
+/** @file
+ @brief Interface used by pa_front to virtualize functions which operate on
+ streams.
+*/
+
+
+#include "portaudio.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif /* __cplusplus */
+
+
+#define PA_STREAM_MAGIC (0x18273645)
+
+
+/** A structure representing an (abstract) interface to a host API. Contains
+ pointers to functions which implement the interface.
+
+ All PaStreamInterface functions are guaranteed to be called with a non-null,
+ valid stream parameter.
+*/
+typedef struct {
+    PaError (*Close)( PaStream* stream );
+    PaError (*Start)( PaStream *stream );
+    PaError (*Stop)( PaStream *stream );
+    PaError (*Abort)( PaStream *stream );
+    PaError (*IsStopped)( PaStream *stream );
+    PaError (*IsActive)( PaStream *stream );
+    PaTime (*GetTime)( PaStream *stream );
+    double (*GetCpuLoad)( PaStream* stream );
+    PaError (*Read)( PaStream* stream, void *buffer, unsigned long frames );
+    PaError (*Write)( PaStream* stream, const void *buffer, unsigned long frames );
+    signed long (*GetReadAvailable)( PaStream* stream );
+    signed long (*GetWriteAvailable)( PaStream* stream );
+} PaUtilStreamInterface;
+
+
+/** Initialize the fields of a PaUtilStreamInterface structure.
+*/
+void PaUtil_InitializeStreamInterface( PaUtilStreamInterface *streamInterface,
+    PaError (*Close)( PaStream* ),
+    PaError (*Start)( PaStream* ),
+    PaError (*Stop)( PaStream* ),
+    PaError (*Abort)( PaStream* ),
+    PaError (*IsStopped)( PaStream* ),
+    PaError (*IsActive)( PaStream* ),
+    PaTime (*GetTime)( PaStream* ),
+    double (*GetCpuLoad)( PaStream* ),
+    PaError (*Read)( PaStream* stream, void *buffer, unsigned long frames ),
+    PaError (*Write)( PaStream* stream, const void *buffer, unsigned long frames ),
+    signed long (*GetReadAvailable)( PaStream* stream ),
+    signed long (*GetWriteAvailable)( PaStream* stream ) );
+
+
+/** Dummy Read function for use in interfaces to a callback based streams.
+ Pass to the Read parameter of PaUtil_InitializeStreamInterface.
+ @return An error code indicating that the function has no effect
+ because the stream is a callback stream.
+*/
+PaError PaUtil_DummyRead( PaStream* stream,
+                       void *buffer,
+                       unsigned long frames );
+
+
+/** Dummy Write function for use in an interfaces to callback based streams.
+ Pass to the Write parameter of PaUtil_InitializeStreamInterface.
+ @return An error code indicating that the function has no effect
+ because the stream is a callback stream.
+*/
+PaError PaUtil_DummyWrite( PaStream* stream,
+                       const void *buffer,
+                       unsigned long frames );
+
+
+/** Dummy GetReadAvailable function for use in interfaces to callback based
+ streams. Pass to the GetReadAvailable parameter of PaUtil_InitializeStreamInterface.
+ @return An error code indicating that the function has no effect
+ because the stream is a callback stream.
+*/
+signed long PaUtil_DummyGetReadAvailable( PaStream* stream );
+
+
+/** Dummy GetWriteAvailable function for use in interfaces to callback based
+ streams. Pass to the GetWriteAvailable parameter of PaUtil_InitializeStreamInterface.
+ @return An error code indicating that the function has no effect
+ because the stream is a callback stream.
+*/
+signed long PaUtil_DummyGetWriteAvailable( PaStream* stream );
+
+
+
+/** Dummy GetCpuLoad function for use in an interface to a read/write stream.
+ Pass to the GetCpuLoad parameter of PaUtil_InitializeStreamInterface.
+ @return Returns 0.
+*/
+double PaUtil_DummyGetCpuLoad( PaStream* stream );
+
+
+/** Non host specific data for a stream. This data is used by pa_front to
+ forward to the appropriate functions in the streamInterface structure.
+*/
+typedef struct PaUtilStreamRepresentation {
+    unsigned long magic;    /**< set to PA_STREAM_MAGIC */
+    struct PaUtilStreamRepresentation *nextOpenStream; /**< field used by multi-api code */
+    PaUtilStreamInterface *streamInterface;
+    PaStreamCallback *streamCallback;
+    PaStreamFinishedCallback *streamFinishedCallback;
+    void *userData;
+    PaStreamInfo streamInfo;
+} PaUtilStreamRepresentation;
+
+
+/** Initialize a PaUtilStreamRepresentation structure.
+
+ @see PaUtil_InitializeStreamRepresentation
+*/
+void PaUtil_InitializeStreamRepresentation(
+        PaUtilStreamRepresentation *streamRepresentation,
+        PaUtilStreamInterface *streamInterface,
+        PaStreamCallback *streamCallback,
+        void *userData );
+        
+
+/** Clean up a PaUtilStreamRepresentation structure previously initialized
+ by a call to PaUtil_InitializeStreamRepresentation.
+
+ @see PaUtil_InitializeStreamRepresentation
+*/
+void PaUtil_TerminateStreamRepresentation( PaUtilStreamRepresentation *streamRepresentation );
+
+
+/** Check that the stream pointer is valid.
+
+ @return Returns paNoError if the stream pointer appears to be OK, otherwise
+ returns an error indicating the cause of failure.
+*/
+PaError PaUtil_ValidateStreamPointer( PaStream *stream );
+
+
+/** Cast an opaque stream pointer into a pointer to a PaUtilStreamRepresentation.
+
+ @see PaUtilStreamRepresentation
+*/
+#define PA_STREAM_REP( stream )\
+    ((PaUtilStreamRepresentation*) (stream) )
+
+
+/** Cast an opaque stream pointer into a pointer to a PaUtilStreamInterface.
+
+ @see PaUtilStreamRepresentation, PaUtilStreamInterface
+*/
+#define PA_STREAM_INTERFACE( stream )\
+    PA_STREAM_REP( (stream) )->streamInterface
+
+
+    
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif /* PA_STREAM_H */
diff --git a/src/audio/portaudio/pa_common/pa_trace.c b/src/audio/portaudio/pa_common/pa_trace.c
new file mode 100644
index 0000000000000000000000000000000000000000..d0dabf9eb7f308e60dee6136d23eea54d18f8c33
--- /dev/null
+++ b/src/audio/portaudio/pa_common/pa_trace.c
@@ -0,0 +1,88 @@
+/*
+ * $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.
+ */
+
+/** @file
+ @brief Event trace mechanism for debugging.
+*/
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "pa_trace.h"
+
+#if PA_TRACE_REALTIME_EVENTS
+
+static char *traceTextArray[MAX_TRACE_RECORDS];
+static int traceIntArray[MAX_TRACE_RECORDS];
+static int traceIndex = 0;
+static int traceBlock = 0;
+
+/*********************************************************************/
+void PaUtil_ResetTraceMessages()
+{
+    traceIndex = 0;
+}
+
+/*********************************************************************/
+void PaUtil_DumpTraceMessages()
+{
+    int i;
+    int messageCount = (traceIndex < PA_MAX_TRACE_RECORDS) ? traceIndex : PA_MAX_TRACE_RECORDS;
+
+    printf("DumpTraceMessages: traceIndex = %d\n", traceIndex );
+    for( i=0; i<messageCount; i++ )
+    {
+        printf("%3d: %s = 0x%08X\n",
+               i, traceTextArray[i], traceIntArray[i] );
+    }
+    ResetTraceMessages();
+    fflush(stdout);
+}
+
+/*********************************************************************/
+void PaUtil_AddTraceMessage( const char *msg, int data )
+{
+    if( (traceIndex == PA_MAX_TRACE_RECORDS) && (traceBlock == 0) )
+    {
+        traceBlock = 1;
+        /*  PaUtil_DumpTraceMessages(); */
+    }
+    else if( traceIndex < PA_MAX_TRACE_RECORDS )
+    {
+        traceTextArray[traceIndex] = msg;
+        traceIntArray[traceIndex] = data;
+        traceIndex++;
+    }
+}
+
+#endif /* TRACE_REALTIME_EVENTS */
diff --git a/src/audio/portaudio/pa_common/pa_trace.h b/src/audio/portaudio/pa_common/pa_trace.h
new file mode 100644
index 0000000000000000000000000000000000000000..256c3e6911bf887d76b5a36d9ed9a972ab34f81f
--- /dev/null
+++ b/src/audio/portaudio/pa_common/pa_trace.h
@@ -0,0 +1,70 @@
+#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.
+ */
+
+/** @file
+ @brief Event trace mechanism for debugging.
+
+ Allows data to be written to the buffer at interrupt time and dumped later.
+*/
+
+
+#define PA_TRACE_REALTIME_EVENTS     (0)   /* Keep log of various real-time events. */
+#define PA_MAX_TRACE_RECORDS      (2048)
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif /* __cplusplus */
+
+
+#if PA_TRACE_REALTIME_EVENTS
+
+void PaUtil_ResetTraceMessages();
+void PaUtil_AddTraceMessage( const char *msg, int data );
+void PaUtil_DumpTraceMessages();
+    
+#else
+
+#define PaUtil_ResetTraceMessages() /* noop */
+#define PaUtil_AddTraceMessage(msg,data) /* noop */
+#define PaUtil_DumpTraceMessages() /* noop */
+
+#endif
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* PA_TRACE_H */
diff --git a/src/audio/portaudio/pa_common/pa_types.h b/src/audio/portaudio/pa_common/pa_types.h
new file mode 100644
index 0000000000000000000000000000000000000000..343dc8cf8a69284ef906fe62c7e76f7a3eb8fb35
--- /dev/null
+++ b/src/audio/portaudio/pa_common/pa_types.h
@@ -0,0 +1,65 @@
+#ifndef PA_TYPES_H
+#define PA_TYPES_H
+
+/*
+    SIZEOF_SHORT, SIZEOF_INT and SIZEOF_LONG are set by the configure script
+    when it is used. Otherwise we default to the common 32 bit values, if your
+    platform doesn't use configure, and doesn't use the default values below
+    you will need to explicitly define these symbols in your make file.
+
+    A PA_VALIDATE_SIZES macro is provided to assert that the values set in this
+    file are correct.
+*/
+
+#ifndef SIZEOF_SHORT
+#define SIZEOF_SHORT 2
+#endif
+
+#ifndef SIZEOF_INT
+#define SIZEOF_INT 4
+#endif
+
+#ifndef SIZEOF_LONG
+#define SIZEOF_LONG 4
+#endif
+
+
+#if SIZEOF_SHORT == 2
+typedef signed short PaInt16;
+typedef unsigned short PaUint16;
+#elif SIZEOF_INT == 2
+typedef signed int PaInt16;
+typedef unsigned int PaUint16;
+#else
+#error pa_types.h was unable to determine which type to use for 16bit integers on the target platform
+#endif
+
+#if SIZEOF_SHORT == 4
+typedef signed short PaInt32;
+typedef unsigned short PaUint32;
+#elif SIZEOF_INT == 4
+typedef signed int PaInt32;
+typedef unsigned int PaUint32;
+#elif SIZEOF_LONG == 4
+typedef signed long PaInt32;
+typedef unsigned long PaUint32;
+#else
+#error pa_types.h was unable to determine which type to use for 32bit integers on the target platform
+#endif
+
+
+/* PA_VALIDATE_TYPE_SIZES compares the size of the integer types at runtime to
+ ensure that PortAudio was configured correctly, and raises an assertion if
+ they don't match the expected values. <assert.h> must be included in the
+ context in which this macro is used.
+*/
+#define PA_VALIDATE_TYPE_SIZES \
+    { \
+        assert( "PortAudio: type sizes are not correct in pa_types.h" && sizeof( PaUint16 ) == 2 ); \
+        assert( "PortAudio: type sizes are not correct in pa_types.h" && sizeof( PaInt16 ) == 2 ); \
+        assert( "PortAudio: type sizes are not correct in pa_types.h" && sizeof( PaUint32 ) == 4 ); \
+        assert( "PortAudio: type sizes are not correct in pa_types.h" && sizeof( PaInt32 ) == 4 ); \
+    }
+
+
+#endif /* PA_TYPES_H */
diff --git a/src/audio/portaudio/pa_common/pa_util.h b/src/audio/portaudio/pa_common/pa_util.h
new file mode 100644
index 0000000000000000000000000000000000000000..4d060d814a7f95891c45e5eb5b251e25af230109
--- /dev/null
+++ b/src/audio/portaudio/pa_common/pa_util.h
@@ -0,0 +1,167 @@
+#ifndef PA_UTIL_H
+#define PA_UTIL_H
+/*
+ * $Id$
+ * Portable Audio I/O Library implementation utilities header
+ * common implementation utilities and interfaces
+ *
+ * Based on the Open Source API proposed by Ross Bencina
+ * Copyright (c) 1999-2002 Ross Bencina, 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.
+ */
+
+/** @file
+    @brief Prototypes for utility functions used by PortAudio implementations.
+
+    @todo Document and adhere to the alignment guarantees provided by
+    PaUtil_AllocateMemory().
+*/
+
+
+#include "portaudio.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif /* __cplusplus */
+
+
+struct PaUtilHostApiRepresentation;
+
+
+/** Retrieve a specific host API representation. This function can be used
+ by implementations to retrieve a pointer to their representation in
+ host api specific extension functions which aren't passed a rep pointer
+ by pa_front.c.
+
+ @param hostApi A pointer to a host API represenation pointer. Apon success
+ this will receive the requested representation pointer.
+
+ @param type A valid host API type identifier.
+
+ @returns An error code. If the result is PaNoError then a pointer to the
+ requested host API representation will be stored in *hostApi. If the host API
+ specified by type is not found, this function returns paHostApiNotFound.
+*/
+PaError PaUtil_GetHostApiRepresentation( struct PaUtilHostApiRepresentation **hostApi,
+        PaHostApiTypeId type );
+
+
+/** Convert a PortAudio device index into a host API specific device index.
+ @param hostApiDevice Pointer to a device index, on success this will recieve the
+ converted device index value.
+ @param device The PortAudio device index to convert.
+ @param hostApi The host api which the index should be converted for.
+
+ @returns On success returns PaNoError and places the converted index in the
+ hostApiDevice parameter.
+*/
+PaError PaUtil_DeviceIndexToHostApiDeviceIndex(
+        PaDeviceIndex *hostApiDevice, PaDeviceIndex device,
+        struct PaUtilHostApiRepresentation *hostApi );
+
+
+/** Set the host error information returned by Pa_GetLastHostErrorInfo. This
+ function and the paUnanticipatedHostError error code should be used as a
+ last resort.  Implementors should use existing PA error codes where possible,
+ or nominate new ones. Note that at it is always better to use
+ PaUtil_SetLastHostErrorInfo() and paUnanticipatedHostError than to return an
+ ambiguous or inaccurate PaError code.
+
+ @param hostApiType  The host API which encountered the error (ie of the caller)
+
+ @param errorCode The error code returned by the native API function.
+
+ @param errorText A string describing the error. PaUtil_SetLastHostErrorInfo
+ makes a copy of the string, so it is not necessary for the pointer to remain
+ valid after the call to PaUtil_SetLastHostErrorInfo() returns.
+
+*/
+void PaUtil_SetLastHostErrorInfo( PaHostApiTypeId hostApiType, long errorCode,
+        const char *errorText );
+
+
+        
+/** PA_DEBUG() provides a simple debug message printing facility. The macro
+ passes it's argument to a printf-like function called PaUtil_DebugPrint()
+ which prints to stderr and always flushes the stream after printing.
+ Because preprocessor macros cannot directly accept variable length argument
+ lists, calls to the macro must include an additional set of parenthesis, eg:
+ PA_DEBUG(("errorno: %d", 1001 ));
+*/
+
+void PaUtil_DebugPrint( const char *format, ... );
+
+#if (0) /* set to 1 to print debug messages */
+#define PA_DEBUG(x) PaUtil_DebugPrint x ;
+#else
+#define PA_DEBUG(x)
+#endif
+
+
+/* the following functions are implemented in a platform platform specific
+ .c file
+*/
+
+/** Allocate size bytes, guaranteed to be aligned to a FIXME byte boundary */
+void *PaUtil_AllocateMemory( long size );
+
+
+/** Realease block if non-NULL. block may be NULL */
+void PaUtil_FreeMemory( void *block );
+
+
+/** Return the number of currently allocated blocks. This function can be
+ used for detecting memory leaks.
+
+ @note Allocations will only be tracked if PA_TRACK_MEMORY is #defined. If
+ it isn't, this function will always return 0.
+*/
+int PaUtil_CountCurrentlyAllocatedBlocks( void );
+
+
+/** Initialize the clock used by PaUtil_GetTime(). Call this before calling
+ PaUtil_GetTime.
+
+ @see PaUtil_GetTime
+*/
+void PaUtil_InitializeClock( void );
+
+
+/** Return the system time in seconds. Used to implement CPU load functions
+
+ @see PaUtil_InitializeClock
+*/
+double PaUtil_GetTime( void );
+
+
+/* void Pa_Sleep( long msec );  must also be implemented in per-platform .c file */
+
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif /* PA_UTIL_H */
diff --git a/src/audio/portaudio/pa_common/portaudio.h b/src/audio/portaudio/pa_common/portaudio.h
new file mode 100644
index 0000000000000000000000000000000000000000..ac071d7eac66743eb495fdeb69a25d0195ec784d
--- /dev/null
+++ b/src/audio/portaudio/pa_common/portaudio.h
@@ -0,0 +1,1123 @@
+
+#ifndef PORTAUDIO_H
+#define PORTAUDIO_H
+/*
+ * $Id$
+ * PortAudio Portable Real-Time Audio Library
+ * PortAudio API Header File
+ * Latest version available at: http://www.portaudio.com/
+ *
+ * Copyright (c) 1999-2002 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.
+ */
+
+/** @file
+ @brief The PortAudio API.
+*/
+
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif /* __cplusplus */
+
+ 
+/** Retrieve the release number of the currently running PortAudio build,
+ eg 1900.
+*/
+int Pa_GetVersion( void );
+
+
+/** Retrieve a textual description of the current PortAudio build,
+ eg "PortAudio V19-devel 13 October 2002".
+*/
+const char* Pa_GetVersionText( void );
+
+
+/** Error codes returned by PortAudio functions.
+ Note that with the exception of paNoError, all PaErrorCodes are negative.
+*/
+
+typedef int PaError;
+typedef enum PaErrorCode
+{
+    paNoError = 0,
+
+    paNotInitialized = -10000,
+    paUnanticipatedHostError,
+    paInvalidChannelCount,
+    paInvalidSampleRate,
+    paInvalidDevice,
+    paInvalidFlag,
+    paSampleFormatNotSupported,
+    paBadIODeviceCombination,
+    paInsufficientMemory,
+    paBufferTooBig,
+    paBufferTooSmall,
+    paNullCallback,
+    paBadStreamPtr,
+    paTimedOut,
+    paInternalError,
+    paDeviceUnavailable,
+    paIncompatibleHostApiSpecificStreamInfo,
+    paStreamIsStopped,
+    paStreamIsNotStopped,
+    paInputOverflowed,
+    paOutputUnderflowed,
+    paHostApiNotFound,
+    paInvalidHostApi,
+    paCanNotReadFromACallbackStream,      /**< @todo review error code name */
+    paCanNotWriteToACallbackStream,       /**< @todo review error code name */
+    paCanNotReadFromAnOutputOnlyStream,   /**< @todo review error code name */
+    paCanNotWriteToAnInputOnlyStream,     /**< @todo review error code name */
+    paIncompatibleStreamHostApi
+} PaErrorCode;
+
+
+/** Translate the supplied PortAudio error code into a human readable
+ message.
+*/
+const char *Pa_GetErrorText( PaError errorCode );
+
+
+/** Library initialization function - call this before using PortAudio.
+ This function initialises internal data structures and prepares underlying
+ host APIs for use. This function MUST be called before using any other
+ PortAudio API functions.
+
+ If Pa_Initialize() is called multiple times, each successful 
+ call must be matched with a corresponding call to Pa_Terminate(). 
+ Pairs of calls to Pa_Initialize()/Pa_Terminate() may overlap, and are not 
+ required to be fully nested.
+
+ Note that if Pa_Initialize() returns an error code, Pa_Terminate() should
+ NOT be called.
+
+ @return paNoError if successful, otherwise an error code indicating the cause
+ of failure.
+
+ @see Pa_Terminate
+*/
+PaError Pa_Initialize( void );
+
+
+/** Library termination function - call this when finished using PortAudio.
+ This function deallocates all resources allocated by PortAudio since it was
+ initializied by a call to Pa_Initialize(). In cases where Pa_Initialise() has
+ been called multiple times, each call must be matched with a corresponding call
+ to Pa_Terminate(). The final matching call to Pa_Terminate() will automatically
+ close any PortAudio streams that are still open.
+
+ Pa_Terminate() MUST be called before exiting a program which uses PortAudio.
+ Failure to do so may result in serious resource leaks, such as audio devices
+ not being available until the next reboot.
+
+ @return paNoError if successful, otherwise an error code indicating the cause
+ of failure.
+ 
+ @see Pa_Initialize
+*/
+PaError Pa_Terminate( void );
+
+
+
+/** The type used to refer to audio devices. Values of this type usually
+ range from 0 to (Pa_DeviceCount-1), and may also take on the PaNoDevice
+ and paUseHostApiSpecificDeviceSpecification values.
+
+ @see Pa_DeviceCount, paNoDevice, paUseHostApiSpecificDeviceSpecification
+*/
+typedef int PaDeviceIndex;
+
+
+/** A special PaDeviceIndex value indicating that no device is available,
+ or should be used.
+
+ @see PaDeviceIndex
+*/
+#define paNoDevice ((PaDeviceIndex)-1)
+
+
+/** A special PaDeviceIndex value indicating that the device(s) to be used
+ are specified in the host api specific stream info structure.
+
+ @see PaDeviceIndex
+*/
+#define paUseHostApiSpecificDeviceSpecification ((PaDeviceIndex)-2)
+
+
+/* Host API enumeration mechanism */
+
+/** The type used to enumerate to host APIs at runtime. Values of this type
+ range from 0 to (Pa_GetHostApiCount()-1).
+
+ @see Pa_GetHostApiCount
+*/
+typedef int PaHostApiIndex;
+
+
+/** Retrieve the number of available host APIs. Even if a host API is
+ available it may have no devices available.
+
+ @return A non-negative value indicating the number of available host APIs
+ or, a PaErrorCode (which are always negative) if PortAudio is not initialized
+ or an error is encountered.
+
+ @see PaHostApiIndex
+*/
+PaHostApiIndex Pa_GetHostApiCount( void );
+
+
+/** Retrieve the index of the default host API. The default host API will be
+ the lowest common denominator host API on the current platform and is
+ unlikely to provide the best performance.
+
+ @return A non-negative value ranging from 0 to (Pa_GetHostApiCount()-1)
+ indicating the default host API index or, a PaErrorCode (which are always
+ negative) if PortAudio is not initialized or an error is encountered.
+*/
+PaHostApiIndex Pa_GetDefaultHostApi( void );
+
+
+/** Unchanging unique identifiers for each supported host API. This type
+ is used in the PaHostApiInfo structure. The values are guaranteed to be
+ unique and to never change, thus allowing code to be written that
+ conditionally uses host API specific extensions.
+
+ New type ids will be allocated when support for a host API reaches
+ "public alpha" status, prior to that developers should use the
+ paInDevelopment type id.
+
+ @see PaHostApiInfo
+*/
+typedef enum PaHostApiTypeId
+{
+    paInDevelopment=0, /* use while developing support for a new host API */
+    paDirectSound=1,
+    paMME=2,
+    paASIO=3,
+    paSoundManager=4,
+    paCoreAudio=5,
+    paOSS=7,
+    paALSA=8,
+    paAL=9,
+    paBeOS=10,
+    paWDMKS=11,
+    paJACK=12
+} PaHostApiTypeId;
+
+
+/** A structure containing information about a particular host API. */
+
+typedef struct PaHostApiInfo
+{
+    /** this is struct version 1 */
+    int structVersion;
+    /** The well known unique identifier of this host API @see PaHostApiTypeId */
+    PaHostApiTypeId type;
+    /** A textual description of the host API for display on user interfaces. */
+    const char *name;
+
+    /**  The number of devices belonging to this host API. This field may be
+     used in conjunction with Pa_HostApiDeviceIndexToDeviceIndex() to enumerate
+     all devices for this host API.
+     @see Pa_HostApiDeviceIndexToDeviceIndex
+    */
+    int deviceCount;
+
+    /** The the default input device for this host API. The value will be a
+     device index ranging from 0 to (Pa_GetDeviceCount()-1), or paNoDevice
+     if no default input device is available.
+    */
+    PaDeviceIndex defaultInputDevice;
+
+    /** The the default output device for this host API. The value will be a
+     device index ranging from 0 to (Pa_GetDeviceCount()-1), or paNoDevice
+     if no default output device is available.
+    */
+    PaDeviceIndex defaultOutputDevice;
+    
+} PaHostApiInfo;
+
+
+/** Retrieve a pointer to a structure containing information about a specific
+ host Api.
+
+ @param hostApi A valid host API index ranging from 0 to (Pa_GetHostApiCount()-1)
+
+ @return A pointer to an immutable PaHostApiInfo structure describing
+ a specific host API. If the hostApi parameter is out of range or an error
+ is encountered, 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 PaHostApiInfo * Pa_GetHostApiInfo( PaHostApiIndex hostApi );
+
+
+/** Convert a static host API unique identifier, into a runtime
+ host API index.
+
+ @param type A unique host API identifier belonging to the PaHostApiTypeId
+ enumeration.
+
+ @return A valid PaHostApiIndex ranging from 0 to (Pa_GetHostApiCount()-1) or,
+ a PaErrorCode (which are always negative) if PortAudio is not initialized
+ or an error is encountered.
+ 
+ The paHostApiNotFound error code indicates that the host API specified by the
+ type parameter is not available.
+
+ @see PaHostApiTypeId
+*/
+PaHostApiIndex Pa_HostApiTypeIdToHostApiIndex( PaHostApiTypeId type );
+
+
+/** Convert a host-API-specific device index to standard PortAudio device index.
+ This function may be used in conjunction with the deviceCount field of
+ PaHostApiInfo to enumerate all devices for the specified host API.
+
+ @param hostApi A valid host API index ranging from 0 to (Pa_GetHostApiCount()-1)
+
+ @param hostApiDeviceIndex A valid per-host device index in the range
+ 0 to (Pa_GetHostApiInfo(hostApi)->deviceCount-1)
+
+ @return A non-negative PaDeviceIndex ranging from 0 to (Pa_GetDeviceCount()-1)
+ or, a PaErrorCode (which are always negative) if PortAudio is not initialized
+ or an error is encountered.
+
+ A paInvalidHostApi error code indicates that the host API index specified by
+ the hostApi parameter is out of range.
+
+ A paInvalidDevice error code indicates that the hostApiDeviceIndex parameter
+ is out of range.
+ 
+ @see PaHostApiInfo
+*/
+PaDeviceIndex Pa_HostApiDeviceIndexToDeviceIndex( PaHostApiIndex hostApi,
+        int hostApiDeviceIndex );
+
+
+
+/** Structure used to return information about a host error condition.
+*/
+typedef struct PaHostErrorInfo{
+    PaHostApiTypeId hostApiType;    /**< the host API which returned the error code */
+    long errorCode;                 /**< the error code returned */
+    const char *errorText;          /**< a textual description of the error if available, otherwise a zero-length string */
+}PaHostErrorInfo;
+
+
+/** Return information about the last host error encountered. The error
+ information returned by Pa_GetLastHostErrorInfo() will never be modified
+ asyncronously by errors occurring in other PortAudio owned threads
+ (such as the thread that manages the stream callback.)
+
+ This function is provided as a last resort, primarily to enhance debugging
+ by providing clients with access to all available error information.
+
+ @return A pointer to an immutable structure constaining information about
+ the host error. The values in this structure will only be valid if a
+ PortAudio function has previously returned the paUnanticipatedHostError
+ error code.
+*/
+const PaHostErrorInfo* Pa_GetLastHostErrorInfo( void );
+
+
+
+/* Device enumeration and capabilities */
+
+/** Retrieve the number of available devices. The number of available devices
+ may be zero.
+
+ @return A non-negative value indicating the number of available devices or,
+ a PaErrorCode (which are always negative) if PortAudio is not initialized
+ or an error is encountered.
+*/
+PaDeviceIndex Pa_GetDeviceCount( void );
+
+
+/** Retrieve the index of the default input device. The result can be
+ used in the inputDevice parameter to Pa_OpenStream().
+
+ @return The default input device index for the default host API, or paNoDevice
+ if no default input device is available or an error was encountered.
+*/
+PaDeviceIndex Pa_GetDefaultInputDevice( void );
+
+
+/** Retrieve the index of the default output device. The result can be
+ used in the outputDevice parameter to Pa_OpenStream().
+
+ @return The default output device index for the defualt host API, or paNoDevice
+ if no default output device is available or an error was encountered.
+
+ @note
+ On the PC, the user can specify a default device by
+ setting an environment variable. For example, to use device #1.
+<pre>
+ set PA_RECOMMENDED_OUTPUT_DEVICE=1
+</pre>
+ The user should first determine the available device ids by using
+ the supplied application "pa_devs".
+*/
+PaDeviceIndex Pa_GetDefaultOutputDevice( void );
+
+
+/** The type used to represent monotonic time in seconds that can be used
+ for syncronisation. The type is used for the outTime argument to the
+ PaStreamCallback and as the result of Pa_GetStreamTime().
+     
+ @see PaStreamCallback, Pa_GetStreamTime
+*/
+typedef double PaTime;
+
+
+/** A type used to specify one or more sample formats. Each value indicates
+ a possible format for sound data passed to and from the stream callback,
+ Pa_ReadStream and Pa_WriteStream.
+
+ The standard formats paFloat32, paInt16, paInt32, paInt24, paInt8
+ and aUInt8 are usually implemented by all implementations.
+
+ The floating point representation (paFloat32) uses +1.0 and -1.0 as the
+ maximum and minimum respectively.
+
+ paUInt8 is an unsigned 8 bit format where 128 is considered "ground"
+
+ The paNonInterleaved flag indicates that a multichannel buffer is passed
+ as a set of non-interleaved pointers.
+
+ @see Pa_OpenStream, Pa_OpenDefaultStream, PaDeviceInfo
+ @see paFloat32, paInt16, paInt32, paInt24, paInt8
+ @see paUInt8, paCustomFormat, paNonInterleaved
+*/
+typedef unsigned long PaSampleFormat;
+
+
+#define paFloat32        ((PaSampleFormat) 0x00000001) /**< @see PaSampleFormat */
+#define paInt32          ((PaSampleFormat) 0x00000002) /**< @see PaSampleFormat */
+#define paInt24          ((PaSampleFormat) 0x00000004) /**< Packed 24 bit format. @see PaSampleFormat */
+#define paInt16          ((PaSampleFormat) 0x00000008) /**< @see PaSampleFormat */
+#define paInt8           ((PaSampleFormat) 0x00000010) /**< @see PaSampleFormat */
+#define paUInt8          ((PaSampleFormat) 0x00000020) /**< @see PaSampleFormat */
+#define paCustomFormat   ((PaSampleFormat) 0x00010000)/**< @see PaSampleFormat */
+
+#define paNonInterleaved ((PaSampleFormat) 0x80000000)
+
+/** A structure providing information and capabilities of PortAudio devices.
+ Devices may support input, output or both input and output.
+*/
+typedef struct PaDeviceInfo
+{
+    int structVersion;  /* this is struct version 2 */
+    const char *name;
+    PaHostApiIndex hostApi; /* note this is a host API index, not a type id*/
+    
+    int maxInputChannels;
+    int maxOutputChannels;
+
+    /* Default latency values for interactive performance. */
+    PaTime defaultLowInputLatency;
+    PaTime defaultLowOutputLatency;
+    /* Default latency values for robust non-interactive applications (eg. playing sound files). */
+    PaTime defaultHighInputLatency;
+    PaTime defaultHighOutputLatency;
+
+    double defaultSampleRate;
+} PaDeviceInfo;
+
+
+/** Retrieve a pointer to a PaDeviceInfo structure containing information
+ about the specified device.
+ @return A pointer to an immutable PaDeviceInfo structure. If the device
+ parameter is out of range the function returns NULL.
+
+ @param device A valid device index in the range 0 to (Pa_GetDeviceCount()-1)
+
+ @note PortAudio manages the memory referenced by the returned pointer,
+ the client must not manipulate or free the memory. The pointer is only
+ guaranteed to be valid between calls to Pa_Initialize() and Pa_Terminate().
+
+ @see PaDeviceInfo, PaDeviceIndex
+*/
+const PaDeviceInfo* Pa_GetDeviceInfo( PaDeviceIndex device );
+
+
+/** Parameters for one direction (input or output) of a stream.
+*/
+typedef struct PaStreamParameters
+{
+    /** A valid device index in the range 0 to (Pa_GetDeviceCount()-1)
+     specifying the device to be used or the special constant
+     paUseHostApiSpecificDeviceSpecification which indicates that the actual
+     device(s) to use are specified in hostApiSpecificStreamInfo.
+     This field must not be set to paNoDevice.
+    */
+    PaDeviceIndex device;
+    
+    /** The number of channels of sound to be delivered to the
+     stream callback or accessed by Pa_ReadStream() or Pa_WriteStream().
+     It can range from 1 to the value of maxInputChannels in the
+     PaDeviceInfo record for the device specified by the device parameter.
+    */
+    int channelCount;
+
+    /** The sample format of the buffer provided to the stream callback,
+     a_ReadStream() or Pa_WriteStream(). It may be any of the formats described
+     by the PaSampleFormat enumeration.
+    */
+    PaSampleFormat sampleFormat;
+
+    /** The desired latency in seconds. Where practical, implementations should
+     configure their latency based on these parameters, otherwise they may
+     choose the closest viable latency instead. Unless the suggested latency
+     is greater than the absolute upper limit for the device implementations
+     shouldround the suggestedLatency up to the next practial value - ie to
+     provide an equal or higher latency than suggestedLatency whereever possibe.
+     Actual latency values for an open stream may be retrieved using the
+     inputLatency and outputLatency fields of the PaStreamInfo structure
+     returned by Pa_GetStreamInfo().
+     @see default*Latency in PaDeviceInfo, *Latency in PaStreamInfo
+    */
+    PaTime suggestedLatency;
+
+    /** An optional pointer to a host api specific data structure
+     containing additional information for device setup and/or stream processing.
+     hostApiSpecificStreamInfo is never required for correct operation,
+     if not used it should be set to NULL.
+    */
+    void *hostApiSpecificStreamInfo;
+
+} PaStreamParameters;
+
+
+/** Return code for Pa_IsFormatSupported indicating success. */
+#define paFormatIsSupported (0)
+
+/** Determine whether it would be possible to open a stream with the specified
+ parameters.
+
+ @param inputParameters A structure that describes the input parameters used to
+ open a stream. The suggestedLatency field is ignored. See PaStreamParameters
+ for a description of these parameters. inputParameters must be NULL for
+ output-only streams.
+
+ @param outputParameters A structure that describes the output parameters used
+ to open a stream. The suggestedLatency field is ignored. See PaStreamParameters
+ for a description of these parameters. outputParameters must be NULL for
+ input-only streams.
+
+ @param sampleRate The required sampleRate. For full-duplex streams it is the
+ sample rate for both input and output
+
+ @return Returns 0 if the format is supported, and an error code indicating why
+ the format is not supported otherwise. The constant paFormatIsSupported is
+ provided to compare with the return value for success.
+
+ @see paFormatIsSupported, PaStreamParameters
+*/
+PaError Pa_IsFormatSupported( const PaStreamParameters *inputParameters,
+                              const PaStreamParameters *outputParameters,
+                              double sampleRate );
+
+
+
+/* Streaming types and functions */
+
+
+/**
+ A single PaStream can provide multiple channels of real-time
+ streaming audio input and output to a client application. A stream
+ provides access to audio hardware represented by one or more
+ PaDevices. Depending on the underlying Host API, it may be possible 
+ to open multiple streams using the same device, however this behavior 
+ is implementation defined. Portable applications should assume that 
+ a PaDevice may be simultaneously used by at most one PaStream.
+
+ Pointers to PaStream objects are passed between PortAudio functions that
+ operate on streams.
+
+ @see Pa_OpenStream, Pa_OpenDefaultStream, Pa_OpenDefaultStream, Pa_CloseStream,
+ Pa_StartStream, Pa_StopStream, Pa_AbortStream, Pa_IsStreamActive,
+ Pa_GetStreamTime, Pa_GetStreamCpuLoad
+
+*/
+typedef void PaStream;
+
+
+/** Can be passed as the framesPerBuffer parameter to Pa_OpenStream()
+ or Pa_OpenDefaultStream() to indicate that the stream callback will
+ accept buffers of any size.
+*/
+#define paFramesPerBufferUnspecified  (0)
+
+
+/** Flags used to control the behavior of a stream. They are passed as
+ parameters to Pa_OpenStream or Pa_OpenDefaultStream. Multiple flags may be
+ ORed together.
+
+ @see Pa_OpenStream, Pa_OpenDefaultStream
+ @see paNoFlag, paClipOff, paDitherOff, paNeverDropInput,
+  paPrimeOutputBuffersUsingStreamCallback, paPlatformSpecificFlags
+*/
+typedef unsigned long PaStreamFlags;
+
+/** @see PaStreamFlags */
+#define   paNoFlag          ((PaStreamFlags) 0)
+
+/** Disable default clipping of out of range samples.
+ @see PaStreamFlags
+*/
+#define   paClipOff         ((PaStreamFlags) 0x00000001)
+
+/** Disable default dithering.
+ @see PaStreamFlags
+*/
+#define   paDitherOff       ((PaStreamFlags) 0x00000002)
+
+/** Flag requests that where possible a full duplex stream will not discard
+ overflowed input samples without calling the stream callback. This flag is
+ only valid for full duplex callback streams and only when used in combination
+ with the paFramesPerBufferUnspecified (0) framesPerBuffer parameter. Using
+ this flag incorrectly results in a paInvalidFlag error being returned from
+ Pa_OpenStream and Pa_OpenDefaultStream.
+
+ @see PaStreamFlags, paFramesPerBufferUnspecified
+*/
+#define   paNeverDropInput  ((PaStreamFlags) 0x00000004)
+
+/** Call the stream callback to fill initial output buffers, rather than the
+ default behavior of priming the buffers with zeros (silence). This flag has
+ no effect for input-only and blocking read/write streams.
+ 
+ @see PaStreamFlags
+*/
+#define   paPrimeOutputBuffersUsingStreamCallback ((PaStreamFlags) 0x00000008)
+
+/** A mask specifying the platform specific bits.
+ @see PaStreamFlags
+*/
+#define   paPlatformSpecificFlags ((PaStreamFlags)0xFFFF0000)
+
+/**
+ Timing information for the buffers passed to the stream callback.
+*/
+typedef struct PaStreamCallbackTimeInfo{
+    PaTime inputBufferAdcTime;
+    PaTime currentTime;
+    PaTime outputBufferDacTime;
+} PaStreamCallbackTimeInfo;
+
+
+/**
+ Flag bit constants for the statusFlags to PaStreamCallback.
+
+ @see paInputUnderflow, paInputOverflow, paOutputUnderflow, paOutputOverflow,
+ paPrimingOutput
+*/
+typedef unsigned long PaStreamCallbackFlags;
+
+/** In a stream opened with paFramesPerBufferUnspecified, indicates that
+ input data is all silence (zeros) because no real data is available. In a
+ stream opened without paFramesPerBufferUnspecified, it indicates that one or
+ more zero samples have been inserted into the input buffer to compensate
+ for an input underflow.
+ @see PaStreamCallbackFlags
+*/
+#define paInputUnderflow   ((PaStreamCallbackFlags) 0x00000001)
+
+/** In a stream opened with paFramesPerBufferUnspecified, indicates that data
+ prior to the first sample of the input buffer was discarded due to an
+ overflow, possibly because the stream callback is using too much CPU time.
+ Otherwise indicates that data prior to one or more samples in the
+ input buffer was discarded.
+ @see PaStreamCallbackFlags
+*/
+#define paInputOverflow    ((PaStreamCallbackFlags) 0x00000002)
+
+/** Indicates that output data (or a gap) was inserted, possibly because the
+ stream callback is using too much CPU time.
+ @see PaStreamCallbackFlags
+*/
+#define paOutputUnderflow  ((PaStreamCallbackFlags) 0x00000004)
+
+/** Indicates that output data will be discarded because no room is available.
+ @see PaStreamCallbackFlags
+*/
+#define paOutputOverflow   ((PaStreamCallbackFlags) 0x00000008)
+
+/** Some of all of the output data will be used to prime the stream, input
+ data may be zero.
+ @see PaStreamCallbackFlags
+*/
+#define paPrimingOutput    ((PaStreamCallbackFlags) 0x00000010)
+
+/**
+ Allowable return values for the PaStreamCallback.
+ @see PaStreamCallback
+*/
+typedef enum PaStreamCallbackResult
+{
+    paContinue=0,
+    paComplete=1,
+    paAbort=2
+} PaStreamCallbackResult;
+
+
+/**
+ Functions of type PaStreamCallback are implemented by PortAudio clients.
+ They consume, process or generate audio in response to requests from an
+ active PortAudio stream.
+     
+ @param input and @param output are arrays of interleaved samples,
+ the format, packing and number of channels used by the buffers are
+ determined by parameters to Pa_OpenStream().
+     
+ @param frameCount The number of sample frames to be processed by
+ the stream callback.
+
+ @param timeInfo The time in seconds when the first sample of the input
+ buffer was received at the audio input, the time in seconds when the first
+ sample of the output buffer will begin being played at the audio output, and
+ the time in seconds when the stream callback was called.
+ See also Pa_GetStreamTime()
+
+ @param statusFlags Flags indicating whether input and/or output buffers
+ have been inserted or will be dropped to overcome underflow or overflow
+ conditions.
+
+ @param userData The value of a user supplied pointer passed to
+ Pa_OpenStream() intended for storing synthesis data etc.
+
+ @return
+ The stream callback should return one of the values in the
+ PaStreamCallbackResult enumeration. To ensure that the callback continues
+ to be called, it should return paContinue (0). Either paComplete or paAbort
+ can be returned to finish stream processing, after either of these values is
+ returned the callback will not be called again. If paAbort is returned the
+ stream will finish as soon as possible. If paComplete is returned, the stream
+ will continue until all buffers generated by the callback have been played.
+ 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 Pa_StopStream(), Pa_AbortStream() or Pa_CloseStream() can also
+ be used to stop the stream. The callback must always fill the entire output
+ buffer irrespective of its return value.
+
+ @see Pa_OpenStream, Pa_OpenDefaultStream
+
+ @note With the exception of Pa_GetStreamCpuLoad() it is not permissable to call
+ PortAudio API functions from within the stream callback.
+*/
+typedef int PaStreamCallback(
+    const void *input, void *output,
+    unsigned long frameCount,
+    const PaStreamCallbackTimeInfo* timeInfo,
+    PaStreamCallbackFlags statusFlags,
+    void *userData );
+
+
+/** Opens a stream for either input, output or both.
+     
+ @param stream The address of a PaStream pointer which will receive
+ a pointer to the newly opened stream.
+     
+ @param inputParameters A structure that describes the input parameters used by
+ the opened stream. See PaStreamParameters for a description of these parameters.
+ inputParameters must be NULL for output-only streams.
+
+ @param outputParameters A structure that describes the output parameters used by
+ the opened stream. See PaStreamParameters for a description of these parameters.
+ outputParameters must be NULL for input-only streams.
+ 
+ @param sampleRate The desired sampleRate. For full-duplex streams it is the
+ sample rate for both input and output
+     
+ @param framesPerBuffer The number of frames passed to the stream callback
+ function, or the preferred block granularity for a blocking read/write stream.
+ The special value paFramesPerBufferUnspecified (0) may be used to request that
+ the stream callback will recieve an optimal (and possibly varying) number of
+ frames based on host requirements and the requested latency settings.
+ Note: With some host APIs, the use of non-zero framesPerBuffer for a callback
+ stream may introduce an additional layer of buffering which could introduce
+ additional latency. PortAudio guarantees that the additional latency
+ will be kept to the theoretical minimum however, it is strongly recommended
+ that a non-zero framesPerBuffer value only be used when your algorithm
+ requires a fixed number of frames per stream callback.
+ 
+ @param streamFlags Flags which modify the behaviour of the streaming process.
+ This parameter may contain a combination of flags ORed together. Some flags may
+ only be relevant to certain buffer formats.
+     
+ @param streamCallback A pointer to a client supplied function that is responsible
+ for processing and filling input and output buffers. If this parameter is NULL
+ the stream will be opened in 'blocking read/write' mode. In blocking mode,
+ the client can receive sample data using Pa_ReadStream and write sample data
+ using Pa_WriteStream, the number of samples that may be read or written
+ without blocking is returned by Pa_GetStreamReadAvailable and
+ Pa_GetStreamWriteAvailable respectively.
+
+ @param userData A client supplied pointer which is passed to the stream callback
+ function. It could for example, contain a pointer to instance data necessary
+ for processing the audio buffers. This parameter is ignored if streamCallback
+ is NULL.
+     
+ @return
+ Upon success Pa_OpenStream() returns paNoError and places a pointer to a
+ valid PaStream in the stream argument. The stream is inactive (stopped).
+ If a call to Pa_OpenStream() fails, a non-zero error code is returned (see
+ PaError for possible error codes) and the value of stream is invalid.
+
+ @see PaStreamParameters, PaStreamCallback, Pa_ReadStream, Pa_WriteStream,
+ Pa_GetStreamReadAvailable, Pa_GetStreamWriteAvailable
+*/
+PaError Pa_OpenStream( PaStream** stream,
+                       const PaStreamParameters *inputParameters,
+                       const PaStreamParameters *outputParameters,
+                       double sampleRate,
+                       unsigned long framesPerBuffer,
+                       PaStreamFlags streamFlags,
+                       PaStreamCallback *streamCallback,
+                       void *userData );
+
+
+/** A simplified version of Pa_OpenStream() that opens the default input
+ and/or output devices.
+
+ @param stream The address of a PaStream pointer which will receive
+ a pointer to the newly opened stream.
+ 
+ @param numInputChannels  The number of channels of sound that will be supplied
+ to the stream callback or returned by Pa_ReadStream. It can range from 1 to
+ the value of maxInputChannels in the PaDeviceInfo record for the default input
+ device. If 0 the stream is opened as an output-only stream.
+
+ @param numOutputChannels The number of channels of sound to be delivered to the
+ stream callback or passed to Pa_WriteStream. It can range from 1 to the value
+ of maxOutputChannels in the PaDeviceInfo record for the default output dvice.
+ If 0 the stream is opened as an output-only stream.
+
+ @param sampleFormat The sample format of both the input and output buffers
+ provided to the callback or passed to and from Pa_ReadStream and Pa_WriteStream.
+ sampleFormat may be any of the formats described by the PaSampleFormat
+ enumeration.
+ 
+ @param sampleRate Same as Pa_OpenStream parameter of the same name.
+ @param framesPerBuffer Same as Pa_OpenStream parameter of the same name.
+ @param streamCallback Same as Pa_OpenStream parameter of the same name.
+ @param userData Same as Pa_OpenStream parameter of the same name.
+
+ @return As for Pa_OpenStream
+
+ @see Pa_OpenStream, PaStreamCallback
+*/
+PaError Pa_OpenDefaultStream( PaStream** stream,
+                              int numInputChannels,
+                              int numOutputChannels,
+                              PaSampleFormat sampleFormat,
+                              double sampleRate,
+                              unsigned long framesPerBuffer,
+                              PaStreamCallback *streamCallback,
+                              void *userData );
+
+
+/** Closes an audio stream. If the audio stream is active it
+ discards any pending buffers as if Pa_AbortStream() had been called.
+*/
+PaError Pa_CloseStream( PaStream *stream );
+
+
+/** Functions of type PaStreamFinishedCallback are implemented by PortAudio 
+ clients. They can be registered with a stream using the Pa_SetStreamFinishedCallback
+ function. Once registered they are called when the stream becomes inactive
+ (ie once a call to Pa_StopStream() will not block).
+ A stream will become inactive after the stream callback returns non-zero,
+ or when Pa_StopStream or Pa_AbortStream is called. For a stream providing audio
+ output, if the stream callback returns paComplete, or Pa_StopStream is called,
+ the stream finished callback will not be called until all generated sample data
+ has been played.
+ 
+ @param userData The userData parameter supplied to Pa_OpenStream()
+
+ @see Pa_SetStreamFinishedCallback
+*/
+typedef void PaStreamFinishedCallback( void *userData );
+
+
+/** Register a stream finished callback function which will be called when the 
+ stream becomes inactive. See the description of PaStreamFinishedCallback for 
+ further details about when the callback will be called.
+
+ @param stream a pointer to a PaStream that is in the stopped state - if the
+ stream is not stopped, the stream's finished callback will remain unchanged 
+ and an error code will be returned.
+
+ @param streamFinishedCallback a pointer to a function with the same signature
+ as PaStreamFinishedCallback, that will be called when the stream becomes
+ inactive. Passing NULL for this parameter will un-register a previously
+ registered stream finished callback function.
+
+ @return on success returns paNoError, otherwise an error code indicating the cause
+ of the error.
+
+ @see PaStreamFinishedCallback
+*/
+PaError Pa_SetStreamFinishedCallback( PaStream *stream, PaStreamFinishedCallback* streamFinishedCallback ); 
+
+
+/** Commences audio processing.
+*/
+PaError Pa_StartStream( PaStream *stream );
+
+
+/** Terminates audio processing. It waits until all pending
+ audio buffers have been played before it returns.
+*/
+PaError Pa_StopStream( PaStream *stream );
+
+
+/** Terminates audio processing immediately without waiting for pending
+ buffers to complete.
+*/
+PaError Pa_AbortStream( PaStream *stream );
+
+
+/** Determine whether the stream is stopped.
+ A stream is considered to be stopped prior to a successful call to
+ Pa_StartStream and after a successful call to Pa_StopStream or Pa_AbortStream.
+ If a stream callback returns a value other than paContinue the stream is NOT
+ considered to be stopped.
+
+ @return Returns one (1) when the stream is stopped, zero (0) when
+ the stream is running or, a PaErrorCode (which are always negative) if
+ PortAudio is not initialized or an error is encountered.
+
+ @see Pa_StopStream, Pa_AbortStream, Pa_IsStreamActive
+*/
+PaError Pa_IsStreamStopped( PaStream *stream );
+
+
+/** Determine whether the stream is active.
+ A stream is active after a successful call to Pa_StartStream(), until it
+ becomes inactive either as a result of a call to Pa_StopStream() or
+ Pa_AbortStream(), or as a result of a return value other than paContinue from
+ the stream callback. In the latter case, the stream is considered inactive
+ after the last buffer has finished playing.
+
+ @return Returns one (1) when the stream is active (ie playing or recording
+ audio), zero (0) when not playing or, a PaErrorCode (which are always negative)
+ if PortAudio is not initialized or an error is encountered.
+
+ @see Pa_StopStream, Pa_AbortStream, Pa_IsStreamStopped
+*/
+PaError Pa_IsStreamActive( PaStream *stream );
+
+
+
+/** A structure containing unchanging information about an open stream.
+ @see Pa_GetStreamInfo
+*/
+
+typedef struct PaStreamInfo
+{
+    /** this is struct version 1 */
+    int structVersion;
+
+    /** The input latency of the stream in seconds. This value provides the most
+     accurate estimate of input latency available to the implementation. It may
+     differ significantly from the suggestedLatency value passed to Pa_OpenStream().
+     The value of this field will be zero (0.) for output-only streams.
+     @see PaTime
+    */
+    PaTime inputLatency;
+
+    /** The output latency of the stream in seconds. This value provides the most
+     accurate estimate of output latency available to the implementation. It may
+     differ significantly from the suggestedLatency value passed to Pa_OpenStream().
+     The value of this field will be zero (0.) for input-only streams.
+     @see PaTime
+    */
+    PaTime outputLatency;
+
+    /** The sample rate of the stream in Hertz (samples per second). In cases
+     where the hardware sample rate is inaccurate and PortAudio is aware of it,
+     the value of this field may be different from the sampleRate parameter
+     passed to Pa_OpenStream(). If information about the actual hardware sample
+     rate is not available, this field will have the same value as the sampleRate
+     parameter passed to Pa_OpenStream().
+    */
+    double sampleRate;
+    
+} PaStreamInfo;
+
+
+/** Retrieve a pointer to a PaStreamInfo structure containing information
+ about the specified stream.
+ @return A pointer to an immutable PaStreamInfo structure. If the stream
+ parameter invalid, or an error is encountered, the function returns NULL.
+
+ @param stream A pointer to an open stream previously created with Pa_OpenStream.
+
+ @note PortAudio manages the memory referenced by the returned pointer,
+ the client must not manipulate or free the memory. The pointer is only
+ guaranteed to be valid until the specified stream is closed.
+
+ @see PaStreamInfo
+*/
+const PaStreamInfo* Pa_GetStreamInfo( PaStream *stream );
+
+
+/** Determine the current time for the stream according to the same clock used
+ to generate buffer timestamps. This time may be used for syncronising other
+ events to the audio stream, for example synchronizing audio to MIDI.
+                                        
+ @return The stream's current time in seconds, or 0 if an error occurred.
+
+ @see PaTime, PaStreamCallback
+*/
+PaTime Pa_GetStreamTime( PaStream *stream );
+
+
+/** Retrieve CPU usage information for the specified stream.
+ The "CPU Load" is a fraction of total CPU time consumed by a callback stream's
+ audio processing routines including, but not limited to the client supplied
+ stream callback. This function does not work with blocking read/write streams.
+
+ This function may be called from the stream callback function or the
+ application.
+     
+ @return
+ A floating point value, typically between 0.0 and 1.0, where 1.0 indicates
+ that the stream callback is consuming the maximum number of CPU cycles possible
+ to maintain real-time operation. A value of 0.5 would imply that PortAudio and
+ the stream callback was consuming roughly 50% of the available CPU time. The
+ return value may exceed 1.0. A value of 0.0 will always be returned for a
+ blocking read/write stream, or if an error occurrs.
+*/
+double Pa_GetStreamCpuLoad( PaStream* stream );
+
+
+/** Read samples from an input stream. The function doesn't return until
+ the entire buffer has been filled - this may involve waiting for the operating
+ system to supply the data.
+
+ @param stream A pointer to an open stream previously created with Pa_OpenStream.
+ 
+ @param buffer A pointer to a buffer of sample frames. The buffer contains
+ samples in the format specified by the inputParameters->sampleFormat field
+ used to open the stream, and the number of channels specified by
+ inputParameters->numChannels. If non-interleaved samples were requested,
+ buffer is a pointer to the first element of an array of non-interleaved
+ buffer pointers, one for each channel.
+
+ @param frames The number of frames to be read into buffer. This parameter
+ is not constrained to a specific range, however high performance applications
+ will want to match this parameter to the framesPerBuffer parameter used
+ when opening the stream.
+
+ @return On success PaNoError will be returned, or PaInputOverflowed if input
+ data was discarded by PortAudio after the previous call and before this call.
+*/
+PaError Pa_ReadStream( PaStream* stream,
+                       void *buffer,
+                       unsigned long frames );
+
+
+/** Write samples to an output stream. This function doesn't return until the
+ entire buffer has been consumed - this may involve waiting for the operating
+ system to consume the data.
+
+ @param stream A pointer to an open stream previously created with Pa_OpenStream.
+
+ @param buffer A pointer to a buffer of sample frames. The buffer contains
+ samples in the format specified by the outputParameters->sampleFormat field
+ used to open the stream, and the number of channels specified by
+ outputParameters->numChannels. If non-interleaved samples were requested,
+ buffer is a pointer to the first element of an array of non-interleaved
+ buffer pointers, one for each channel.
+
+ @param frames The number of frames to be written from buffer. This parameter
+ is not constrained to a specific range, however high performance applications
+ will want to match this parameter to the framesPerBuffer parameter used
+ when opening the stream.
+
+ @return On success PaNoError will be returned, or paOutputUnderflowed if
+ additional output data was inserted after the previous call and before this
+ call.
+*/
+PaError Pa_WriteStream( PaStream* stream,
+                        const void *buffer,
+                        unsigned long frames );
+
+
+/** Retrieve the number of frames that can be read from the stream without
+ waiting.
+
+ @return Returns a non-negative value representing the maximum number of frames
+ that can be read from the stream without blocking or busy waiting or, a
+ PaErrorCode (which are always negative) if PortAudio is not initialized or an
+ error is encountered.
+*/
+signed long Pa_GetStreamReadAvailable( PaStream* stream );
+
+
+/** Retrieve the number of frames that can be written to the stream without
+ waiting.
+
+ @return Returns a non-negative value representing the maximum number of frames
+ that can be written to the stream without blocking or busy waiting or, a
+ PaErrorCode (which are always negative) if PortAudio is not initialized or an
+ error is encountered.
+*/
+signed long Pa_GetStreamWriteAvailable( PaStream* stream );
+
+
+/* Miscellaneous utilities */
+
+
+/** Retrieve the size of a given sample format in bytes.
+
+ @return The size in bytes of a single sample in the specified format,
+ or paSampleFormatNotSupported if the format is not supported.
+*/
+PaError Pa_GetSampleSize( PaSampleFormat format );
+
+
+/** Put the caller to sleep for at least 'msec' milliseconds. This function is
+ provided only as a convenience for authors of portable code (such as the tests
+ and examples in the PortAudio distribution.)
+
+ The function may sleep longer than requested so don't rely on this for accurate
+ musical timing.
+*/
+void Pa_Sleep( long msec );
+
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif /* PORTAUDIO_H */
diff --git a/src/audio/portaudio/pa_dll_switch/PaDllEntry.h b/src/audio/portaudio/pa_dll_switch/PaDllEntry.h
new file mode 100644
index 0000000000000000000000000000000000000000..e070054b39d7b2f52f9b224c8399cec857f778e7
--- /dev/null
+++ b/src/audio/portaudio/pa_dll_switch/PaDllEntry.h
@@ -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/src/audio/portaudio/pa_dll_switch/letter_from_tim_010817.txt b/src/audio/portaudio/pa_dll_switch/letter_from_tim_010817.txt
new file mode 100644
index 0000000000000000000000000000000000000000..a535cd1dae6afccc58d304b141166f7f7600dbd5
Binary files /dev/null and b/src/audio/portaudio/pa_dll_switch/letter_from_tim_010817.txt differ
diff --git a/src/audio/portaudio/pa_dll_switch/loadPA_DLL.cpp b/src/audio/portaudio/pa_dll_switch/loadPA_DLL.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..043eda87810f65848cc3c8e69bf8a90d6e05e8c2
--- /dev/null
+++ b/src/audio/portaudio/pa_dll_switch/loadPA_DLL.cpp
@@ -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/src/audio/portaudio/pa_dll_switch/pa_lib.c b/src/audio/portaudio/pa_dll_switch/pa_lib.c
new file mode 100644
index 0000000000000000000000000000000000000000..86601592c4e2117777b3a4d74fb3f4d22b26f0f2
--- /dev/null
+++ b/src/audio/portaudio/pa_dll_switch/pa_lib.c
@@ -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/src/audio/portaudio/pa_dll_switch/portaudio.h b/src/audio/portaudio/pa_dll_switch/portaudio.h
new file mode 100644
index 0000000000000000000000000000000000000000..9632521ebc44f0787c25530c4c33116200bd87ba
--- /dev/null
+++ b/src/audio/portaudio/pa_dll_switch/portaudio.h
@@ -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/src/audio/portaudio/pa_jack/pa_jack.c b/src/audio/portaudio/pa_jack/pa_jack.c
new file mode 100644
index 0000000000000000000000000000000000000000..c0691d1847bc6a2b482676b3630110615af8e160
--- /dev/null
+++ b/src/audio/portaudio/pa_jack/pa_jack.c
@@ -0,0 +1,1718 @@
+/*
+ * $Id$
+ * PortAudio Portable Real-Time Audio Library
+ * Latest Version at: http://www.portaudio.com
+ * JACK Implementation by Joshua Haberman
+ *
+ * Copyright (c) 2004 Stefan Westerfeld <stefan@space.twc.de>
+ * Copyright (c) 2004 Arve Knudsen <aknuds-1@broadpark.no>
+ * Copyright (c) 2002 Joshua Haberman <joshua@haberman.com>
+ *
+ * Based on the Open Source API proposed by Ross Bencina
+ * Copyright (c) 1999-2002 Ross Bencina, 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 <string.h>
+#include <regex.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <assert.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <errno.h>  /* EBUSY */
+#include <signal.h> /* sig_atomic_t */
+#include <math.h>
+#include <semaphore.h>
+
+#include <jack/types.h>
+#include <jack/jack.h>
+
+#include "pa_util.h"
+#include "pa_hostapi.h"
+#include "pa_stream.h"
+#include "pa_process.h"
+#include "pa_allocation.h"
+#include "pa_cpuload.h"
+#include "../pablio/ringbuffer.c"
+
+static int aErr_;
+static PaError paErr_;     /* For use with ENSURE_PA */
+static pthread_t mainThread_;
+static char *jackErr_ = NULL;
+
+#define STRINGIZE_HELPER(expr) #expr
+#define STRINGIZE(expr) STRINGIZE_HELPER(expr)
+
+/* Check PaError */
+#define ENSURE_PA(expr) \
+    do { \
+        if( (paErr_ = (expr)) < paNoError ) \
+        { \
+            if( (paErr_) == paUnanticipatedHostError && pthread_self() == mainThread_ ) \
+            { \
+                assert( jackErr_ ); \
+                PaUtil_SetLastHostErrorInfo( paJACK, -1, jackErr_ ); \
+            } \
+            PaUtil_DebugPrint(( "Expression '" #expr "' failed in '" __FILE__ "', line: " STRINGIZE( __LINE__ ) "\n" )); \
+            result = paErr_; \
+            goto error; \
+        } \
+    } while( 0 )
+
+#define UNLESS(expr, code) \
+    do { \
+        if( (expr) == 0 ) \
+        { \
+            if( (code) == paUnanticipatedHostError && pthread_self() == mainThread_ ) \
+            { \
+                assert( jackErr_ ); \
+                PaUtil_SetLastHostErrorInfo( paJACK, -1, jackErr_ ); \
+            } \
+            PaUtil_DebugPrint(( "Expression '" #expr "' failed in '" __FILE__ "', line: " STRINGIZE( __LINE__ ) "\n" )); \
+            result = (code); \
+            goto error; \
+        } \
+    } while( 0 )
+
+#define ASSERT_CALL(expr, success) \
+    aErr_ = (expr); \
+    assert( aErr_ == success );
+
+/*
+ * Functions that directly map to the PortAudio stream interface
+ */
+
+static void Terminate( struct PaUtilHostApiRepresentation *hostApi );
+static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
+                                  const PaStreamParameters *inputParameters,
+                                  const PaStreamParameters *outputParameters,
+                                  double sampleRate );
+static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
+                           PaStream** s,
+                           const PaStreamParameters *inputParameters,
+                           const PaStreamParameters *outputParameters,
+                           double sampleRate,
+                           unsigned long framesPerBuffer,
+                           PaStreamFlags streamFlags,
+                           PaStreamCallback *streamCallback,
+                           void *userData );
+static PaError CloseStream( PaStream* stream );
+static PaError StartStream( PaStream *stream );
+static PaError StopStream( PaStream *stream );
+static PaError AbortStream( PaStream *stream );
+static PaError IsStreamStopped( PaStream *s );
+static PaError IsStreamActive( PaStream *stream );
+/*static PaTime GetStreamInputLatency( PaStream *stream );*/
+/*static PaTime GetStreamOutputLatency( PaStream *stream );*/
+static PaTime GetStreamTime( PaStream *stream );
+static double GetStreamCpuLoad( PaStream* stream );
+
+
+/*
+ * Data specific to this API
+ */
+
+struct PaJackStream;
+
+typedef struct
+{
+    PaUtilHostApiRepresentation commonHostApiRep;
+    PaUtilStreamInterface callbackStreamInterface;
+    PaUtilStreamInterface blockingStreamInterface;
+
+    PaUtilAllocationGroup *deviceInfoMemory;
+
+    jack_client_t *jack_client;
+    int jack_buffer_size;
+    PaHostApiIndex hostApiIndex;
+
+    pthread_mutex_t mtx;
+    pthread_cond_t cond;
+    unsigned long inputBase, outputBase;
+
+    /* For dealing with the process thread */
+    volatile int xrun;     /* Received xrun notification from JACK? */
+    struct PaJackStream * volatile toAdd, * volatile toRemove;
+    struct PaJackStream *processQueue;
+    volatile sig_atomic_t jackIsDown;
+}
+PaJackHostApiRepresentation;
+
+/* PaJackStream - a stream data structure specifically for this implementation */
+
+typedef struct PaJackStream
+{
+    PaUtilStreamRepresentation streamRepresentation;
+    PaUtilBufferProcessor bufferProcessor;
+    PaUtilCpuLoadMeasurer cpuLoadMeasurer;
+    PaJackHostApiRepresentation *hostApi;
+
+    /* our input and output ports */
+    jack_port_t **local_input_ports;
+    jack_port_t **local_output_ports;
+
+    /* the input and output ports of the client we are connecting to */
+    jack_port_t **remote_input_ports;
+    jack_port_t **remote_output_ports;
+
+    int num_incoming_connections;
+    int num_outgoing_connections;
+
+    jack_client_t *jack_client;
+
+    /* The stream is running if it's still producing samples.
+     * The stream is active if samples it produced are still being heard.
+     */
+    volatile sig_atomic_t is_running;
+    volatile sig_atomic_t is_active;
+    /* Used to signal processing thread that stream should start or stop, respectively */
+    volatile sig_atomic_t doStart, doStop, doAbort;
+
+    jack_nframes_t t0;
+
+    PaUtilAllocationGroup *stream_memory;
+
+    /* These are useful in the process callback */
+
+    int callbackResult;
+    int isSilenced;
+    int xrun;
+
+    /* These are useful for the blocking API */
+
+    int                     isBlockingStream;
+    RingBuffer              inFIFO;
+    RingBuffer              outFIFO;
+    volatile sig_atomic_t   data_available;
+    sem_t                   data_semaphore;
+    int                     bytesPerFrame;
+    int                     samplesPerFrame;
+
+    struct PaJackStream *next;
+}
+PaJackStream;
+
+#define TRUE 1
+#define FALSE 0
+
+/*
+ * Functions specific to this API
+ */
+
+static int JackCallback( jack_nframes_t frames, void *userData );
+
+
+/*
+ *
+ * Implementation
+ *
+ */
+
+/* ---- blocking emulation layer ---- */
+
+/* Allocate buffer. */
+static PaError BlockingInitFIFO( 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 BlockingTermFIFO( RingBuffer *rbuf )
+{
+    if( rbuf->buffer ) free( rbuf->buffer );
+    rbuf->buffer = NULL;
+    return paNoError;
+}
+
+static int
+BlockingCallback( const void                      *inputBuffer,
+                  void                            *outputBuffer,
+		  unsigned long                    framesPerBuffer,
+		  const PaStreamCallbackTimeInfo*  timeInfo,
+		  PaStreamCallbackFlags            statusFlags,
+		  void                             *userData )
+{
+    struct PaJackStream *stream = (PaJackStream *)userData;
+    long numBytes = stream->bytesPerFrame * framesPerBuffer;
+
+    /* This may get called with NULL inputBuffer during initial setup. */
+    if( inputBuffer != NULL )
+    {
+        RingBuffer_Write( &stream->inFIFO, inputBuffer, numBytes );
+    }
+    if( outputBuffer != NULL )
+    {
+        int numRead = RingBuffer_Read( &stream->outFIFO, outputBuffer, numBytes );
+        /* Zero out remainder of buffer if we run out of data. */
+        memset( (char *)outputBuffer + numRead, 0, numBytes - numRead );
+    }
+
+    if( !stream->data_available )
+    {
+        stream->data_available = 1;
+        sem_post( &stream->data_semaphore );
+    }
+    return paContinue;
+}
+
+static PaError
+BlockingBegin( PaJackStream *stream, int minimum_buffer_size )
+{
+    long    doRead = 0;
+    long    doWrite = 0;
+    PaError result = paNoError;
+    long    numFrames;
+
+    doRead = stream->local_input_ports != NULL;
+    doWrite = stream->local_output_ports != NULL;
+    /* <FIXME> */
+    stream->samplesPerFrame = 2;
+    stream->bytesPerFrame = sizeof(float) * stream->samplesPerFrame;
+    /* </FIXME> */
+    numFrames = 32;
+    while (numFrames < minimum_buffer_size)
+        numFrames *= 2;
+
+    if( doRead )
+    {
+        ENSURE_PA( BlockingInitFIFO( &stream->inFIFO, numFrames, stream->bytesPerFrame ) );
+    }
+    if( doWrite )
+    {
+        long numBytes;
+
+        ENSURE_PA( BlockingInitFIFO( &stream->outFIFO, numFrames, stream->bytesPerFrame ) );
+
+        /* Make Write FIFO appear full initially. */
+        numBytes = RingBuffer_GetWriteAvailable( &stream->outFIFO );
+        RingBuffer_AdvanceWriteIndex( &stream->outFIFO, numBytes );
+    }
+
+    stream->data_available = 0;
+    sem_init( &stream->data_semaphore, 0, 0 );
+
+error:
+    return result;
+}
+
+static void
+BlockingEnd( PaJackStream *stream )
+{
+    BlockingTermFIFO( &stream->inFIFO );
+    BlockingTermFIFO( &stream->outFIFO );
+
+    sem_destroy( &stream->data_semaphore );
+}
+
+static PaError BlockingReadStream( PaStream* s, void *data, unsigned long numFrames )
+{
+    PaError result = paNoError;
+    PaJackStream *stream = (PaJackStream *)s;
+
+    long bytesRead;
+    char *p = (char *) data;
+    long numBytes = stream->bytesPerFrame * numFrames;
+    while( numBytes > 0 )
+    {
+        bytesRead = RingBuffer_Read( &stream->inFIFO, p, numBytes );
+        numBytes -= bytesRead;
+        p += bytesRead;
+        if( numBytes > 0 )
+        {
+            /* see write for an explanation */
+            if( stream->data_available )
+                stream->data_available = 0;
+            else
+                sem_wait( &stream->data_semaphore );
+        }
+    }
+
+    return result;
+}
+
+static PaError BlockingWriteStream( PaStream* s, const void *data, unsigned long numFrames )
+{
+    PaError result = paNoError;
+    PaJackStream *stream = (PaJackStream *)s;
+    long bytesWritten;
+    char *p = (char *) data;
+    long numBytes = stream->bytesPerFrame * numFrames;
+    while( numBytes > 0 )
+    {
+        bytesWritten = RingBuffer_Write( &stream->outFIFO, p, numBytes );
+        numBytes -= bytesWritten;
+        p += bytesWritten;
+        if( numBytes > 0 ) 
+        {
+            /* we use the following algorithm: 
+             *   (1) write data
+             *   (2) if some data didn't fit into the ringbuffer, set data_available to 0
+             *       to indicate to the audio that if space becomes available, we want to know
+             *   (3) retry to write data (because it might be that between (1) and (2)
+             *       new space in the buffer became available)
+             *   (4) if this failed, we are sure that the buffer is really empty and
+             *       we will definitely receive a notification when it becomes available
+             *       thus we can safely sleep
+             *
+             * if the algorithm bailed out in step (3) before, it leaks a count of 1
+             * on the semaphore; however, it doesn't matter, because if we block in (4),
+             * we also do it in a loop
+             */
+            if( stream->data_available )
+                stream->data_available = 0;
+            else
+                sem_wait( &stream->data_semaphore );
+        }
+    }
+
+    return result;
+}
+
+static signed long
+BlockingGetStreamReadAvailable( PaStream* s )
+{
+    PaJackStream *stream = (PaJackStream *)s;
+
+    int bytesFull = RingBuffer_GetReadAvailable( &stream->inFIFO );
+    return bytesFull / stream->bytesPerFrame;
+}
+
+static signed long
+BlockingGetStreamWriteAvailable( PaStream* s )
+{
+    PaJackStream *stream = (PaJackStream *)s;
+
+    int bytesEmpty = RingBuffer_GetWriteAvailable( &stream->outFIFO );
+    return bytesEmpty / stream->bytesPerFrame;
+}
+
+static PaError
+BlockingWaitEmpty( PaStream *s )
+{
+    PaJackStream *stream = (PaJackStream *)s;
+
+    while( RingBuffer_GetReadAvailable( &stream->outFIFO ) > 0 )
+    {
+        stream->data_available = 0;
+        sem_wait( &stream->data_semaphore );
+    }
+    return 0;
+}
+
+/* ---- jack driver ---- */
+
+/* BuildDeviceList():
+ *
+ * The process of determining a list of PortAudio "devices" from
+ * JACK's client/port system is fairly involved, so it is separated
+ * into its own routine.
+ */
+
+static PaError BuildDeviceList( PaJackHostApiRepresentation *jackApi )
+{
+    /* Utility macros for the repetitive process of allocating memory */
+
+    /* ... MALLOC: allocate memory as part of the device list
+     * allocation group */
+#define MALLOC(size) \
+     (PaUtil_GroupAllocateMemory( jackApi->deviceInfoMemory, (size) ))
+
+    /* JACK has no concept of a device.  To JACK, there are clients
+     * which have an arbitrary number of ports.  To make this
+     * intelligible to PortAudio clients, we will group each JACK client
+     * into a device, and make each port of that client a channel */
+
+    PaError result = paNoError;
+    PaUtilHostApiRepresentation *commonApi = &jackApi->commonHostApiRep;
+
+    const char **jack_ports = NULL;
+    char **client_names = NULL;
+    char *regex_pattern = alloca( jack_client_name_size() + 3 );
+    int port_index, client_index, i;
+    double globalSampleRate;
+    regex_t port_regex;
+    unsigned long numClients = 0, numPorts = 0;
+    char *tmp_client_name = alloca( jack_client_name_size() );
+
+    commonApi->info.defaultInputDevice = paNoDevice;
+    commonApi->info.defaultOutputDevice = paNoDevice;
+    commonApi->info.deviceCount = 0;
+
+    /* Parse the list of ports, using a regex to grab the client names */
+    ASSERT_CALL( regcomp( &port_regex, "^[^:]*", REG_EXTENDED ), 0 );
+
+    /* since we are rebuilding the list of devices, free all memory
+     * associated with the previous list */
+    PaUtil_FreeAllAllocations( jackApi->deviceInfoMemory );
+
+    /* We can only retrieve the list of clients indirectly, by first
+     * asking for a list of all ports, then parsing the port names
+     * according to the client_name:port_name convention (which is
+     * enforced by jackd)
+     * A: If jack_get_ports returns NULL, there's nothing for us to do */
+    UNLESS( (jack_ports = jack_get_ports( jackApi->jack_client, "", "", 0 )) && jack_ports[0], paNoError );
+    /* Find number of ports */
+    while( jack_ports[numPorts] )
+        ++numPorts;
+    /* At least there will be one port per client :) */
+    UNLESS( client_names = alloca( numPorts * sizeof (char *) ), paInsufficientMemory );
+
+    /* Build a list of clients from the list of ports */
+    for( numClients = 0, port_index = 0; jack_ports[port_index] != NULL; port_index++ )
+    {
+        int client_seen = FALSE;
+        regmatch_t match_info;
+        const char *port = jack_ports[port_index];
+
+        /* extract the client name from the port name, using a regex
+         * that parses the clientname:portname syntax */
+        UNLESS( !regexec( &port_regex, port, 1, &match_info, 0 ), paInternalError );
+        assert(match_info.rm_eo - match_info.rm_so < jack_client_name_size());
+        memcpy( tmp_client_name, port + match_info.rm_so,
+                match_info.rm_eo - match_info.rm_so );
+        tmp_client_name[match_info.rm_eo - match_info.rm_so] = '\0';
+
+        /* do we know about this port's client yet? */
+        for( i = 0; i < numClients; i++ )
+        {
+            if( strcmp( tmp_client_name, client_names[i] ) == 0 )
+                client_seen = TRUE;
+        }
+
+        if (client_seen)
+            continue;   /* A: Nothing to see here, move along */
+
+        UNLESS( client_names[numClients] = (char*)MALLOC(strlen(tmp_client_name) + 1), paInsufficientMemory );
+
+        /* The alsa_pcm client should go in spot 0.  If this
+         * is the alsa_pcm client AND we are NOT about to put
+         * it in spot 0 put it in spot 0 and move whatever
+         * was already in spot 0 to the end. */
+        if( strcmp( "alsa_pcm", tmp_client_name ) == 0 && numClients > 0 )
+        {
+            /* alsa_pcm goes in spot 0 */
+            strcpy( client_names[ numClients ], client_names[0] );
+            strcpy( client_names[0], tmp_client_name );
+        }
+        else
+        {
+            /* put the new client at the end of the client list */
+            strcpy( client_names[ numClients ], tmp_client_name );
+        }
+        ++numClients;
+    }
+
+    /* Now we have a list of clients, which will become the list of
+     * PortAudio devices. */
+
+    /* there is one global sample rate all clients must conform to */
+
+    globalSampleRate = jack_get_sample_rate( jackApi->jack_client );
+    UNLESS( commonApi->deviceInfos = (PaDeviceInfo**)MALLOC( sizeof(PaDeviceInfo*) *
+                                                     numClients ), paInsufficientMemory );
+
+    assert( commonApi->info.deviceCount == 0 );
+
+    /* Create a PaDeviceInfo structure for every client */
+    for( client_index = 0; client_index < numClients; client_index++ )
+    {
+        PaDeviceInfo *curDevInfo;
+        const char **clientPorts = NULL;
+
+        UNLESS( curDevInfo = (PaDeviceInfo*)MALLOC( sizeof(PaDeviceInfo) ), paInsufficientMemory );
+        UNLESS( curDevInfo->name = (char*)MALLOC( strlen(client_names[client_index]) + 1 ), paInsufficientMemory );
+        strcpy( (char *)curDevInfo->name, client_names[client_index] );
+
+        curDevInfo->structVersion = 2;
+        curDevInfo->hostApi = jackApi->hostApiIndex;
+
+        /* JACK is very inflexible: there is one sample rate the whole
+         * system must run at, and all clients must speak IEEE float. */
+        curDevInfo->defaultSampleRate = globalSampleRate;
+
+        /* To determine how many input and output channels are available,
+         * we re-query jackd with more specific parameters. */
+
+        sprintf( regex_pattern, "%s:.*", client_names[client_index] );
+
+        /* ... what are your output ports (that we could input from)? */
+        clientPorts = jack_get_ports( jackApi->jack_client, regex_pattern,
+                                     NULL, JackPortIsOutput);
+        curDevInfo->maxInputChannels = 0;
+        curDevInfo->defaultLowInputLatency = 0.;
+        curDevInfo->defaultHighInputLatency = 0.;
+        if( clientPorts )
+        {
+            jack_port_t *p = jack_port_by_name( jackApi->jack_client, clientPorts[0] );
+            curDevInfo->defaultLowInputLatency = curDevInfo->defaultHighInputLatency =
+                jack_port_get_latency( p ) / globalSampleRate;
+            free( p );
+
+            for( i = 0; clientPorts[i] != NULL; i++)
+            {
+                /* The number of ports returned is the number of output channels.
+                 * We don't care what they are, we just care how many */
+                curDevInfo->maxInputChannels++;
+            }
+            free(clientPorts);
+        }
+
+        /* ... what are your input ports (that we could output to)? */
+        clientPorts = jack_get_ports( jackApi->jack_client, regex_pattern,
+                                     NULL, JackPortIsInput);
+        curDevInfo->maxOutputChannels = 0;
+        curDevInfo->defaultLowOutputLatency = 0.;
+        curDevInfo->defaultHighOutputLatency = 0.;
+        if( clientPorts )
+        {
+            jack_port_t *p = jack_port_by_name( jackApi->jack_client, clientPorts[0] );
+            curDevInfo->defaultLowOutputLatency = curDevInfo->defaultHighOutputLatency =
+                jack_port_get_latency( p ) / globalSampleRate;
+            free( p );
+
+            for( i = 0; clientPorts[i] != NULL; i++)
+            {
+                /* The number of ports returned is the number of input channels.
+                 * We don't care what they are, we just care how many */
+                curDevInfo->maxOutputChannels++;
+            }
+            free(clientPorts);
+        }
+
+        /* Add this client to the list of devices */
+        commonApi->deviceInfos[client_index] = curDevInfo;
+        ++commonApi->info.deviceCount;
+        if( commonApi->info.defaultInputDevice == paNoDevice && curDevInfo->maxInputChannels > 0 )
+            commonApi->info.defaultInputDevice = client_index;
+        if( commonApi->info.defaultOutputDevice == paNoDevice && curDevInfo->maxOutputChannels > 0 )
+            commonApi->info.defaultOutputDevice = client_index;
+    }
+
+error:
+    regfree( &port_regex );
+    free( jack_ports );
+    return result;
+}
+#undef MALLOC
+
+static void UpdateSampleRate( PaJackStream *stream, double sampleRate )
+{
+    /* XXX: Maybe not the cleanest way of going about this? */
+    stream->cpuLoadMeasurer.samplingPeriod = stream->bufferProcessor.samplePeriod = 1. / sampleRate;
+    stream->streamRepresentation.streamInfo.sampleRate = sampleRate;
+}
+
+static void JackErrorCallback( const char *msg )
+{
+    if( pthread_self() == mainThread_ )
+    {
+        assert( msg );
+        free( jackErr_ );
+        jackErr_ = malloc( strlen( msg ) );
+        sprintf( jackErr_, msg );
+    }
+}
+
+static void JackOnShutdown( void *arg )
+{
+    PaJackHostApiRepresentation *jackApi = (PaJackHostApiRepresentation *)arg;
+    PaJackStream *stream = jackApi->processQueue;
+
+    PA_DEBUG(( "%s: JACK server is shutting down\n", __FUNCTION__ ));
+    for( ; stream; stream = stream->next )
+    {
+        stream->is_active = 0;
+    }
+
+    /* Make sure that the main thread doesn't get stuck waiting on the condition */
+    ASSERT_CALL( pthread_mutex_lock( &jackApi->mtx ), 0 );
+    jackApi->jackIsDown = 1;
+    ASSERT_CALL( pthread_cond_signal( &jackApi->cond ), 0 );
+    ASSERT_CALL( pthread_mutex_unlock( &jackApi->mtx ), 0 );
+
+}
+
+static int JackSrCb( jack_nframes_t nframes, void *arg )
+{
+    PaJackHostApiRepresentation *jackApi = (PaJackHostApiRepresentation *)arg;
+    double sampleRate = (double)nframes;
+    PaJackStream *stream = jackApi->processQueue;
+
+    /* Update all streams in process queue */
+    PA_DEBUG(( "%s: Acting on change in JACK samplerate: %f\n", __FUNCTION__, sampleRate ));
+    for( ; stream; stream = stream->next )
+    {
+        if( stream->streamRepresentation.streamInfo.sampleRate != sampleRate )
+        {
+            PA_DEBUG(( "%s: Updating samplerate\n", __FUNCTION__ ));
+            UpdateSampleRate( stream, sampleRate );
+        }
+    }
+
+    return 0;
+}
+
+static int JackXRunCb(void *arg) {
+    PaJackHostApiRepresentation *hostApi = (PaJackHostApiRepresentation *)arg;
+    assert( hostApi );
+    hostApi->xrun = TRUE;
+    PA_DEBUG(( "%s: JACK signalled xrun\n", __FUNCTION__ ));
+    return 0;
+}
+
+PaError PaJack_Initialize( PaUtilHostApiRepresentation **hostApi,
+                           PaHostApiIndex hostApiIndex )
+{
+    PaError result = paNoError;
+    PaJackHostApiRepresentation *jackHostApi;
+    int activated = 0;
+    char *clientName;
+    int written;
+    *hostApi = NULL;    /* Initialize to NULL */
+
+    UNLESS( jackHostApi = (PaJackHostApiRepresentation*)
+        PaUtil_AllocateMemory( sizeof(PaJackHostApiRepresentation) ), paInsufficientMemory );
+    jackHostApi->deviceInfoMemory = NULL;
+
+    mainThread_ = pthread_self();
+    ASSERT_CALL( pthread_mutex_init( &jackHostApi->mtx, NULL ), 0 );
+    ASSERT_CALL( pthread_cond_init( &jackHostApi->cond, NULL ), 0 );
+
+    /* Try to become a client of the JACK server.  If we cannot do
+     * this, then this API cannot be used. */
+
+    clientName = alloca( jack_client_name_size() );
+    written = snprintf( clientName, jack_client_name_size(), "PortAudio-%d", getpid() );
+    assert( written < jack_client_name_size() );
+    jackHostApi->jack_client = jack_client_new( clientName );
+    if( jackHostApi->jack_client == NULL )
+    {
+       /* the V19 development docs say that if an implementation
+        * detects that it cannot be used, it should return a NULL
+        * interface and paNoError */
+       result = paNoError;
+       goto error;
+    }
+
+    UNLESS( jackHostApi->deviceInfoMemory = PaUtil_CreateAllocationGroup(), paInsufficientMemory );
+    jackHostApi->hostApiIndex = hostApiIndex;
+
+    *hostApi = &jackHostApi->commonHostApiRep;
+    (*hostApi)->info.structVersion = 1;
+    (*hostApi)->info.type = paJACK;
+    (*hostApi)->info.name = "JACK Audio Connection Kit";
+
+    /* Build a device list by querying the JACK server */
+
+    ENSURE_PA( BuildDeviceList( jackHostApi ) );
+
+    /* Register functions */
+
+    (*hostApi)->Terminate = Terminate;
+    (*hostApi)->OpenStream = OpenStream;
+    (*hostApi)->IsFormatSupported = IsFormatSupported;
+
+    PaUtil_InitializeStreamInterface( &jackHostApi->callbackStreamInterface,
+                                      CloseStream, StartStream,
+                                      StopStream, AbortStream,
+                                      IsStreamStopped, IsStreamActive,
+                                      GetStreamTime, GetStreamCpuLoad,
+                                      PaUtil_DummyRead, PaUtil_DummyWrite,
+                                      PaUtil_DummyGetReadAvailable,
+                                      PaUtil_DummyGetWriteAvailable );
+
+    PaUtil_InitializeStreamInterface( &jackHostApi->blockingStreamInterface, CloseStream, StartStream,
+                                      StopStream, AbortStream, IsStreamStopped, IsStreamActive,
+                                      GetStreamTime, PaUtil_DummyGetCpuLoad,
+                                      BlockingReadStream, BlockingWriteStream,
+                                      BlockingGetStreamReadAvailable, BlockingGetStreamWriteAvailable );
+
+    jackHostApi->inputBase = jackHostApi->outputBase = 0;
+    jackHostApi->xrun = 0;
+    jackHostApi->toAdd = jackHostApi->toRemove = NULL;
+    jackHostApi->processQueue = NULL;
+    jackHostApi->jackIsDown = 0;
+
+    jack_on_shutdown( jackHostApi->jack_client, JackOnShutdown, jackHostApi );
+    jack_set_error_function( JackErrorCallback );
+    jackHostApi->jack_buffer_size = jack_get_buffer_size ( jackHostApi->jack_client );
+    UNLESS( !jack_set_sample_rate_callback( jackHostApi->jack_client, JackSrCb, jackHostApi ), paUnanticipatedHostError );
+    UNLESS( !jack_set_xrun_callback( jackHostApi->jack_client, JackXRunCb, jackHostApi ), paUnanticipatedHostError );
+    UNLESS( !jack_set_process_callback( jackHostApi->jack_client, JackCallback, jackHostApi ), paUnanticipatedHostError );
+    UNLESS( !jack_activate( jackHostApi->jack_client ), paUnanticipatedHostError );
+    activated = 1;
+
+    return result;
+
+error:
+    if( activated )
+        ASSERT_CALL( jack_deactivate( jackHostApi->jack_client ), 0 );
+
+    if( jackHostApi )
+    {
+        if( jackHostApi->jack_client )
+            ASSERT_CALL( jack_client_close( jackHostApi->jack_client ), 0 );
+
+        if( jackHostApi->deviceInfoMemory )
+        {
+            PaUtil_FreeAllAllocations( jackHostApi->deviceInfoMemory );
+            PaUtil_DestroyAllocationGroup( jackHostApi->deviceInfoMemory );
+        }
+
+        PaUtil_FreeMemory( jackHostApi );
+    }
+    return result;
+}
+
+
+static void Terminate( struct PaUtilHostApiRepresentation *hostApi )
+{
+    PaJackHostApiRepresentation *jackHostApi = (PaJackHostApiRepresentation*)hostApi;
+
+    /* note: this automatically disconnects all ports, since a deactivated
+     * client is not allowed to have any ports connected */
+    ASSERT_CALL( jack_deactivate( jackHostApi->jack_client ), 0 );
+
+    ASSERT_CALL( pthread_mutex_destroy( &jackHostApi->mtx ), 0 );
+    ASSERT_CALL( pthread_cond_destroy( &jackHostApi->cond ), 0 );
+
+    ASSERT_CALL( jack_client_close( jackHostApi->jack_client ), 0 );
+
+    if( jackHostApi->deviceInfoMemory )
+    {
+        PaUtil_FreeAllAllocations( jackHostApi->deviceInfoMemory );
+        PaUtil_DestroyAllocationGroup( jackHostApi->deviceInfoMemory );
+    }
+
+    PaUtil_FreeMemory( jackHostApi );
+
+    free( jackErr_ );
+}
+
+static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
+                                  const PaStreamParameters *inputParameters,
+                                  const PaStreamParameters *outputParameters,
+                                  double sampleRate )
+{
+    int inputChannelCount = 0, outputChannelCount = 0;
+    PaSampleFormat inputSampleFormat, outputSampleFormat;
+
+    if( inputParameters )
+    {
+        inputChannelCount = inputParameters->channelCount;
+        inputSampleFormat = inputParameters->sampleFormat;
+
+        /* unless alternate device specification is supported, reject the use of
+            paUseHostApiSpecificDeviceSpecification */
+
+        if( inputParameters->device == paUseHostApiSpecificDeviceSpecification )
+            return paInvalidDevice;
+
+        /* check that input device can support inputChannelCount */
+        if( inputChannelCount > hostApi->deviceInfos[ inputParameters->device ]->maxInputChannels )
+            return paInvalidChannelCount;
+
+        /* validate inputStreamInfo */
+        if( inputParameters->hostApiSpecificStreamInfo )
+            return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */
+    }
+    else
+    {
+        inputChannelCount = 0;
+    }
+
+    if( outputParameters )
+    {
+        outputChannelCount = outputParameters->channelCount;
+        outputSampleFormat = outputParameters->sampleFormat;
+
+        /* unless alternate device specification is supported, reject the use of
+            paUseHostApiSpecificDeviceSpecification */
+
+        if( outputParameters->device == paUseHostApiSpecificDeviceSpecification )
+            return paInvalidDevice;
+
+        /* check that output device can support inputChannelCount */
+        if( outputChannelCount > hostApi->deviceInfos[ outputParameters->device ]->maxOutputChannels )
+            return paInvalidChannelCount;
+
+        /* validate outputStreamInfo */
+        if( outputParameters->hostApiSpecificStreamInfo )
+            return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */
+    }
+    else
+    {
+        outputChannelCount = 0;
+    }
+
+    /*
+        The following check is not necessary for JACK.
+        
+            - if a full duplex stream is requested, check that the combination
+                of input and output parameters is supported
+
+
+        Because the buffer adapter handles conversion between all standard
+        sample formats, the following checks are only required if paCustomFormat
+        is implemented, or under some other unusual conditions.
+        
+            - check that input device can support inputSampleFormat, or that
+                we have the capability to convert from outputSampleFormat to
+                a native format
+
+            - check that output device can support outputSampleFormat, or that
+                we have the capability to convert from outputSampleFormat to
+                a native format
+    */
+
+    /* check that the device supports sampleRate */
+    
+#define ABS(x) ( (x) > 0 ? (x) : -(x) )
+    if( ABS(sampleRate - jack_get_sample_rate(((PaJackHostApiRepresentation *) hostApi)->jack_client )) > 1 )
+       return paInvalidSampleRate;
+#undef ABS
+
+    return paFormatIsSupported;
+}
+
+/* Basic stream initialization */
+static PaError InitializeStream( PaJackStream *stream, PaJackHostApiRepresentation *hostApi, int numInputChannels,
+        int numOutputChannels )
+{
+    PaError result = paNoError;
+    assert( stream );
+
+    memset( stream, 0, sizeof (PaJackStream) );
+    UNLESS( stream->stream_memory = PaUtil_CreateAllocationGroup(), paInsufficientMemory );
+    stream->jack_client = hostApi->jack_client;
+    stream->hostApi = hostApi;
+
+    if( numInputChannels > 0 )
+    {
+        UNLESS( stream->local_input_ports =
+                (jack_port_t**) PaUtil_GroupAllocateMemory( stream->stream_memory, sizeof(jack_port_t*) * numInputChannels ),
+                paInsufficientMemory );
+        memset( stream->local_input_ports, 0, sizeof(jack_port_t*) * numInputChannels );
+        UNLESS( stream->remote_output_ports =
+                (jack_port_t**) PaUtil_GroupAllocateMemory( stream->stream_memory, sizeof(jack_port_t*) * numInputChannels ),
+                paInsufficientMemory );
+        memset( stream->remote_output_ports, 0, sizeof(jack_port_t*) * numInputChannels );
+    }
+    if( numOutputChannels > 0 )
+    {
+        UNLESS( stream->local_output_ports =
+                (jack_port_t**) PaUtil_GroupAllocateMemory( stream->stream_memory, sizeof(jack_port_t*) * numOutputChannels ),
+                paInsufficientMemory );
+        memset( stream->local_output_ports, 0, sizeof(jack_port_t*) * numOutputChannels );
+        UNLESS( stream->remote_input_ports =
+                (jack_port_t**) PaUtil_GroupAllocateMemory( stream->stream_memory, sizeof(jack_port_t*) * numOutputChannels ),
+                paInsufficientMemory );
+        memset( stream->remote_input_ports, 0, sizeof(jack_port_t*) * numOutputChannels );
+    }
+
+    stream->num_incoming_connections = numInputChannels;
+    stream->num_outgoing_connections = numOutputChannels;
+
+error:
+    return result;
+}
+
+/*!
+ * Free resources associated with stream, and eventually stream itself.
+ *
+ * Frees allocated memory, and closes opened pcms.
+ */
+static void CleanUpStream( PaJackStream *stream, int terminateStreamRepresentation, int terminateBufferProcessor )
+{
+    int i;
+    assert( stream );
+
+    if( stream->isBlockingStream )
+        BlockingEnd( stream );
+
+    for( i = 0; i < stream->num_incoming_connections; ++i )
+    {
+        if( stream->local_input_ports[i] )
+            ASSERT_CALL( jack_port_unregister( stream->jack_client, stream->local_input_ports[i] ), 0 );
+        free( stream->remote_output_ports[i] );
+    }
+    for( i = 0; i < stream->num_outgoing_connections; ++i )
+    {
+        if( stream->local_output_ports[i] )
+            ASSERT_CALL( jack_port_unregister( stream->jack_client, stream->local_output_ports[i] ), 0 );
+        free( stream->remote_input_ports[i] );
+    }
+
+    if( terminateStreamRepresentation )
+        PaUtil_TerminateStreamRepresentation( &stream->streamRepresentation );
+    if( terminateBufferProcessor )
+        PaUtil_TerminateBufferProcessor( &stream->bufferProcessor );
+
+    if( stream->stream_memory )
+    {
+        PaUtil_FreeAllAllocations( stream->stream_memory );
+        PaUtil_DestroyAllocationGroup( stream->stream_memory );
+    }
+    PaUtil_FreeMemory( stream );
+}
+
+static PaError WaitCondition( PaJackHostApiRepresentation *hostApi )
+{
+    PaError result = paNoError;
+    int err = 0;
+    PaTime pt = PaUtil_GetTime();
+    struct timespec ts;
+
+    ts.tv_sec = (time_t) floor( pt + 1 );
+    ts.tv_nsec = (long) ((pt - floor( pt )) * 1000000000);
+    /* XXX: Best enclose in loop, in case of spurious wakeups? */
+    err = pthread_cond_timedwait( &hostApi->cond, &hostApi->mtx, &ts );
+
+    /* Make sure we didn't time out */
+    UNLESS( err != ETIMEDOUT, paTimedOut );
+    UNLESS( !err, paInternalError );
+
+error:
+    return result;
+}
+
+static PaError AddStream( PaJackStream *stream )
+{
+    PaError result = paNoError;
+    PaJackHostApiRepresentation *hostApi = stream->hostApi;
+    /* Add to queue of streams that should be processed */
+    ASSERT_CALL( pthread_mutex_lock( &hostApi->mtx ), 0 );
+    if( !hostApi->jackIsDown )
+    {
+        hostApi->toAdd = stream;
+        /* Unlock mutex and await signal from processing thread */
+        result = WaitCondition( stream->hostApi );
+    }
+    ASSERT_CALL( pthread_mutex_unlock( &hostApi->mtx ), 0 );
+    ENSURE_PA( result );
+
+    UNLESS( !hostApi->jackIsDown, paDeviceUnavailable );
+
+error:
+    return result;
+}
+
+/* Remove stream from processing queue */
+static PaError RemoveStream( PaJackStream *stream )
+{
+    PaError result = paNoError;
+    PaJackHostApiRepresentation *hostApi = stream->hostApi;
+
+    /* Add to queue over streams that should be processed */
+    ASSERT_CALL( pthread_mutex_lock( &hostApi->mtx ), 0 );
+    if( !hostApi->jackIsDown )
+    {
+        hostApi->toRemove = stream;
+        /* Unlock mutex and await signal from processing thread */
+        result = WaitCondition( stream->hostApi );
+    }
+    ASSERT_CALL( pthread_mutex_unlock( &hostApi->mtx ), 0 );
+    ENSURE_PA( result );
+
+error:
+    return result;
+}
+
+/* Add stream to processing queue */
+static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
+                           PaStream** s,
+                           const PaStreamParameters *inputParameters,
+                           const PaStreamParameters *outputParameters,
+                           double sampleRate,
+                           unsigned long framesPerBuffer,
+                           PaStreamFlags streamFlags,
+                           PaStreamCallback *streamCallback,
+                           void *userData )
+{
+    PaError result = paNoError;
+    PaJackHostApiRepresentation *jackHostApi = (PaJackHostApiRepresentation*)hostApi;
+    PaJackStream *stream = NULL;
+    char *port_string = alloca( jack_port_name_size() );
+    unsigned long regexSz = jack_client_name_size() + 3;
+    char *regex_pattern = alloca( regexSz );
+    const char **jack_ports = NULL;
+    /* int jack_max_buffer_size = jack_get_buffer_size( jackHostApi->jack_client ); */
+    int i;
+    int inputChannelCount, outputChannelCount;
+    const double jackSr = jack_get_sample_rate( jackHostApi->jack_client );
+    PaSampleFormat inputSampleFormat = 0, outputSampleFormat = 0;
+    int bpInitialized = 0, srInitialized = 0;   /* Initialized buffer processor and stream representation? */
+    unsigned long ofs;
+
+    /* validate platform specific flags */
+    if( (streamFlags & paPlatformSpecificFlags) != 0 )
+        return paInvalidFlag; /* unexpected platform specific flag */
+    if( (streamFlags & paPrimeOutputBuffersUsingStreamCallback) != 0 )
+    {
+        streamFlags &= ~paPrimeOutputBuffersUsingStreamCallback;
+        /*return paInvalidFlag;*/   /* This implementation does not support buffer priming */
+    }
+
+    if( framesPerBuffer != paFramesPerBufferUnspecified )
+    {
+        /* Jack operates with power of two buffers, and we don't support non-integer buffer adaption (yet) */
+        /*UNLESS( !(framesPerBuffer & (framesPerBuffer - 1)), paBufferTooBig );*/  /* TODO: Add descriptive error code? */
+    }
+
+    /* Preliminary checks */
+
+    if( inputParameters )
+    {
+        inputChannelCount = inputParameters->channelCount;
+        inputSampleFormat = inputParameters->sampleFormat;
+
+        /* unless alternate device specification is supported, reject the use of
+            paUseHostApiSpecificDeviceSpecification */
+
+        if( inputParameters->device == paUseHostApiSpecificDeviceSpecification )
+            return paInvalidDevice;
+
+        /* check that input device can support inputChannelCount */
+        if( inputChannelCount > hostApi->deviceInfos[ inputParameters->device ]->maxInputChannels )
+            return paInvalidChannelCount;
+
+        /* validate inputStreamInfo */
+        if( inputParameters->hostApiSpecificStreamInfo )
+            return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */
+    }
+    else
+    {
+        inputChannelCount = 0;
+    }
+
+    if( outputParameters )
+    {
+        outputChannelCount = outputParameters->channelCount;
+        outputSampleFormat = outputParameters->sampleFormat;
+
+        /* unless alternate device specification is supported, reject the use of
+            paUseHostApiSpecificDeviceSpecification */
+
+        if( outputParameters->device == paUseHostApiSpecificDeviceSpecification )
+            return paInvalidDevice;
+
+        /* check that output device can support inputChannelCount */
+        if( outputChannelCount > hostApi->deviceInfos[ outputParameters->device ]->maxOutputChannels )
+            return paInvalidChannelCount;
+
+        /* validate outputStreamInfo */
+        if( outputParameters->hostApiSpecificStreamInfo )
+            return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */
+    }
+    else
+    {
+        outputChannelCount = 0;
+    }
+
+    /* ... check that the sample rate exactly matches the ONE acceptable rate
+     * A: This rate isn't necessarily constant though? */
+
+#define ABS(x) ( (x) > 0 ? (x) : -(x) )
+    if( ABS(sampleRate - jackSr) > 1 )
+       return paInvalidSampleRate;
+#undef ABS
+
+    UNLESS( stream = (PaJackStream*)PaUtil_AllocateMemory( sizeof(PaJackStream) ), paInsufficientMemory );
+    ENSURE_PA( InitializeStream( stream, jackHostApi, inputChannelCount, outputChannelCount ) );
+
+    /* the blocking emulation, if necessary */
+    stream->isBlockingStream = !streamCallback;
+    if( stream->isBlockingStream )
+    {
+        float latency = 0.001; /* 1ms is the absolute minimum we support */
+        int   minimum_buffer_frames = 0;
+
+        if( inputParameters && inputParameters->suggestedLatency > latency )
+            latency = inputParameters->suggestedLatency;
+        else if( outputParameters && outputParameters->suggestedLatency > latency )
+            latency = outputParameters->suggestedLatency;
+
+        /* the latency the user asked for indicates the minimum buffer size in frames */
+        minimum_buffer_frames = (int) (latency * jack_get_sample_rate( jackHostApi->jack_client ));
+
+        /* we also need to be able to store at least three full jack buffers to avoid dropouts */
+        if( jackHostApi->jack_buffer_size * 3 > minimum_buffer_frames )
+            minimum_buffer_frames = jackHostApi->jack_buffer_size * 3;
+
+        /* setup blocking API data structures (FIXME: can fail) */
+	BlockingBegin( stream, minimum_buffer_frames );
+
+        /* install our own callback for the blocking API */
+        streamCallback = BlockingCallback;
+        userData = stream;
+
+        PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation,
+                                               &jackHostApi->blockingStreamInterface, streamCallback, userData );
+    }
+    else
+    {
+        PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation,
+                                               &jackHostApi->callbackStreamInterface, streamCallback, userData );
+    }
+    srInitialized = 1;
+    PaUtil_InitializeCpuLoadMeasurer( &stream->cpuLoadMeasurer, jackSr );
+
+    /* create the JACK ports.  We cannot connect them until audio
+     * processing begins */
+
+    /* Register a unique set of ports for this stream
+     * TODO: Robust allocation of new port names */
+
+    ofs = jackHostApi->inputBase;
+    for( i = 0; i < inputChannelCount; i++ )
+    {
+        snprintf( port_string, jack_port_name_size(), "in_%lu", ofs + i );
+        UNLESS( stream->local_input_ports[i] = jack_port_register(
+              jackHostApi->jack_client, port_string,
+              JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0 ), paInsufficientMemory );
+    }
+    jackHostApi->inputBase += inputChannelCount;
+
+    ofs = jackHostApi->outputBase;
+    for( i = 0; i < outputChannelCount; i++ )
+    {
+        snprintf( port_string, jack_port_name_size(), "out_%lu", ofs + i );
+        UNLESS( stream->local_output_ports[i] = jack_port_register(
+             jackHostApi->jack_client, port_string,
+             JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0 ), paInsufficientMemory );
+    }
+    jackHostApi->outputBase += outputChannelCount;
+
+    /* look up the jack_port_t's for the remote ports.  We could do
+     * this at stream start time, but doing it here ensures the
+     * name lookup only happens once. */
+
+    if( inputChannelCount > 0 )
+    {
+        int err = 0;
+        
+        /* ... remote output ports (that we input from) */
+        snprintf( regex_pattern, regexSz, "%s:.*", hostApi->deviceInfos[ inputParameters->device ]->name );
+        UNLESS( jack_ports = jack_get_ports( jackHostApi->jack_client, regex_pattern,
+                                     NULL, JackPortIsOutput ), paUnanticipatedHostError );
+        for( i = 0; i < inputChannelCount && jack_ports[i]; i++ )
+        {
+            if( (stream->remote_output_ports[i] = jack_port_by_name(
+                 jackHostApi->jack_client, jack_ports[i] )) == NULL ) 
+            {
+                err = 1;
+                break;
+            }
+        }
+        free( jack_ports );
+        UNLESS( !err, paInsufficientMemory );
+
+        /* Fewer ports than expected? */
+        UNLESS( i == inputChannelCount, paInternalError );
+    }
+
+    if( outputChannelCount > 0 )
+    {
+        int err = 0;
+
+        /* ... remote input ports (that we output to) */
+        snprintf( regex_pattern, regexSz, "%s:.*", hostApi->deviceInfos[ outputParameters->device ]->name );
+        UNLESS( jack_ports = jack_get_ports( jackHostApi->jack_client, regex_pattern,
+                                     NULL, JackPortIsInput ), paUnanticipatedHostError );
+        for( i = 0; i < outputChannelCount && jack_ports[i]; i++ )
+        {
+            if( (stream->remote_input_ports[i] = jack_port_by_name(
+                 jackHostApi->jack_client, jack_ports[i] )) == 0 )
+            {
+                err = 1;
+                break;
+            }
+        }
+        free( jack_ports );
+        UNLESS( !err , paInsufficientMemory );
+
+        /* Fewer ports than expected? */
+        UNLESS( i == outputChannelCount, paInternalError );
+    }
+
+    ENSURE_PA( PaUtil_InitializeBufferProcessor(
+                  &stream->bufferProcessor,
+                  inputChannelCount,
+                  inputSampleFormat,
+                  paFloat32,            /* hostInputSampleFormat */
+                  outputChannelCount,
+                  outputSampleFormat,
+                  paFloat32,            /* hostOutputSampleFormat */
+                  jackSr,
+                  streamFlags,
+                  framesPerBuffer,
+                  0,                            /* Ignored */
+                  paUtilUnknownHostBufferSize,  /* Buffer size may vary on JACK's discretion */
+                  streamCallback,
+                  userData ) );
+    bpInitialized = 1;
+
+    if( stream->num_incoming_connections > 0 )
+        stream->streamRepresentation.streamInfo.inputLatency = (jack_port_get_latency( stream->remote_output_ports[0] )
+                - jack_get_buffer_size( jackHostApi->jack_client )  /* One buffer is not counted as latency */
+            + PaUtil_GetBufferProcessorInputLatency( &stream->bufferProcessor )) / sampleRate;
+    if( stream->num_outgoing_connections > 0 )
+        stream->streamRepresentation.streamInfo.outputLatency = (jack_port_get_latency( stream->remote_input_ports[0] )
+                - jack_get_buffer_size( jackHostApi->jack_client )  /* One buffer is not counted as latency */
+            + PaUtil_GetBufferProcessorOutputLatency( &stream->bufferProcessor )) / sampleRate;
+
+    stream->streamRepresentation.streamInfo.sampleRate = jackSr;
+    stream->t0 = jack_frame_time( jackHostApi->jack_client );   /* A: Time should run from Pa_OpenStream */
+
+    ENSURE_PA( AddStream( stream ) );  /* Add to queue over opened streams */
+    
+    *s = (PaStream*)stream;
+
+    return result;
+
+error:
+    if( stream )
+        CleanUpStream( stream, srInitialized, bpInitialized );
+
+    return result;
+}
+
+/*
+    When CloseStream() is called, the multi-api layer ensures that
+    the stream has already been stopped or aborted.
+*/
+static PaError CloseStream( PaStream* s )
+{
+    PaError result = paNoError;
+    PaJackStream *stream = (PaJackStream*)s;
+
+    /* Remove this stream from the processing queue */
+    ENSURE_PA( RemoveStream( stream ) );
+
+error:
+    CleanUpStream( stream, 1, 1 );
+    return result;
+}
+
+static PaError RealProcess( PaJackStream *stream, jack_nframes_t frames )
+{
+    PaError result = paNoError;
+    PaStreamCallbackTimeInfo timeInfo = {0,0,0};
+    int chn;
+    int framesProcessed;
+    const double sr = jack_get_sample_rate( stream->jack_client );    /* Shouldn't change during the process callback */
+    PaStreamCallbackFlags cbFlags = 0;
+
+    /* If the user has returned !paContinue from the callback we'll want to flush the internal buffers,
+     * when these are empty we can finally mark the stream as inactive */
+    if( stream->callbackResult != paContinue &&
+            PaUtil_IsBufferProcessorOutputEmpty( &stream->bufferProcessor ) )
+    {
+        stream->is_active = 0;
+        if( stream->streamRepresentation.streamFinishedCallback )
+            stream->streamRepresentation.streamFinishedCallback( stream->streamRepresentation.userData );
+        PA_DEBUG(( "%s: Callback finished\n", __FUNCTION__ ));
+
+        goto end;
+    }
+
+    timeInfo.currentTime = (jack_frame_time( stream->jack_client ) - stream->t0) / sr;
+    if( stream->num_incoming_connections > 0 )
+        timeInfo.inputBufferAdcTime = timeInfo.currentTime - jack_port_get_latency( stream->remote_output_ports[0] )
+            / sr;
+    if( stream->num_outgoing_connections > 0 )
+        timeInfo.outputBufferDacTime = timeInfo.currentTime + jack_port_get_latency( stream->remote_input_ports[0] )
+            / sr;
+
+    PaUtil_BeginCpuLoadMeasurement( &stream->cpuLoadMeasurer );
+
+    if( stream->xrun )
+    {
+        /* XXX: Any way to tell which of these occurred? */
+        cbFlags = paOutputUnderflow | paInputOverflow;
+        stream->xrun = FALSE;
+    }
+    PaUtil_BeginBufferProcessing( &stream->bufferProcessor, &timeInfo,
+            cbFlags );
+
+    if( stream->num_incoming_connections > 0 )
+        PaUtil_SetInputFrameCount( &stream->bufferProcessor, frames );
+    if( stream->num_outgoing_connections > 0 )
+        PaUtil_SetOutputFrameCount( &stream->bufferProcessor, frames );
+
+    for( chn = 0; chn < stream->num_incoming_connections; chn++ )
+    {
+        jack_default_audio_sample_t *channel_buf = (jack_default_audio_sample_t*)
+            jack_port_get_buffer( stream->local_input_ports[chn],
+                    frames );
+
+        PaUtil_SetNonInterleavedInputChannel( &stream->bufferProcessor,
+                chn,
+                channel_buf );
+    }
+
+    for( chn = 0; chn < stream->num_outgoing_connections; chn++ )
+    {
+        jack_default_audio_sample_t *channel_buf = (jack_default_audio_sample_t*)
+            jack_port_get_buffer( stream->local_output_ports[chn],
+                    frames );
+
+        PaUtil_SetNonInterleavedOutputChannel( &stream->bufferProcessor,
+                chn,
+                channel_buf );
+    }
+
+    framesProcessed = PaUtil_EndBufferProcessing( &stream->bufferProcessor,
+            &stream->callbackResult );
+    /* We've specified a host buffer size mode where every frame should be consumed by the buffer processor */
+    assert( framesProcessed == frames );
+
+    PaUtil_EndCpuLoadMeasurement( &stream->cpuLoadMeasurer, framesProcessed );
+
+end:
+    return result;
+}
+
+/* Alter the processing queue if necessary */
+static PaError UpdateQueue( PaJackHostApiRepresentation *hostApi )
+{
+    PaError result = paNoError;
+    int queueModified = 0;
+    const double jackSr = jack_get_sample_rate( hostApi->jack_client );
+    int err;
+
+    if( (err = pthread_mutex_trylock( &hostApi->mtx )) != 0 )
+    {
+        assert( err == EBUSY );
+        return paNoError;
+    }
+
+    if( hostApi->toAdd )
+    {
+        if( hostApi->processQueue )
+        {
+            PaJackStream *node = hostApi->processQueue;
+            /* Advance to end of queue */
+            while( node->next )
+                node = node->next;
+
+            node->next = hostApi->toAdd;
+        }
+        else
+            hostApi->processQueue = (PaJackStream *)hostApi->toAdd;
+
+        /* If necessary, update stream state */
+        if( hostApi->toAdd->streamRepresentation.streamInfo.sampleRate != jackSr )
+            UpdateSampleRate( hostApi->toAdd, jackSr );
+
+        hostApi->toAdd = NULL;
+        queueModified = 1;
+    }
+    if( hostApi->toRemove )
+    {
+        int removed = 0;
+        PaJackStream *node = hostApi->processQueue, *prev = NULL;
+        assert( hostApi->processQueue );
+
+        while( node )
+        {
+            if( node == hostApi->toRemove )
+            {
+                if( prev )
+                    prev->next = node->next;
+                else
+                    hostApi->processQueue = (PaJackStream *)node->next;
+
+                removed = 1;
+                break;
+            }
+
+            prev = node;
+            node = node->next;
+        }
+        UNLESS( removed, paInternalError );
+        hostApi->toRemove = NULL;
+        PA_DEBUG(( "%s: Removed stream from processing queue\n", __FUNCTION__ ));
+        queueModified = 1;
+    }
+
+    if( queueModified )
+    {
+        /* Signal that we've done what was asked of us */
+        ASSERT_CALL( pthread_cond_signal( &hostApi->cond ), 0 );
+    }
+
+error:
+    ASSERT_CALL( pthread_mutex_unlock( &hostApi->mtx ), 0 );
+
+    return result;
+}
+
+static int JackCallback( jack_nframes_t frames, void *userData )
+{
+    PaError result = paNoError;
+    PaJackHostApiRepresentation *hostApi = (PaJackHostApiRepresentation *)userData;
+    PaJackStream *stream = NULL;
+    int xrun = hostApi->xrun;
+    hostApi->xrun = 0;
+
+    assert( hostApi );
+
+    ENSURE_PA( UpdateQueue( hostApi ) );
+
+    /* Process each stream */
+    stream = hostApi->processQueue;
+    for( ; stream; stream = stream->next )
+    {
+        if( xrun )  /* Don't override if already set */
+            stream->xrun = 1;
+
+        /* See if this stream is to be started */
+        if( stream->doStart )
+        {
+            /* If we can't obtain a lock, we'll try next time */
+            int err = pthread_mutex_trylock( &stream->hostApi->mtx );
+            if( !err )
+            {
+                if( stream->doStart )   /* Could potentially change before obtaining the lock */
+                {
+                    stream->is_active = 1;
+                    stream->doStart = 0;
+                    PA_DEBUG(( "%s: Starting stream\n", __FUNCTION__ ));
+                    ASSERT_CALL( pthread_cond_signal( &stream->hostApi->cond ), 0 );
+                    stream->callbackResult = paContinue;
+                    stream->isSilenced = 0;
+                }
+
+                ASSERT_CALL( pthread_mutex_unlock( &stream->hostApi->mtx ), 0 );
+            }
+            else
+                assert( err == EBUSY );
+        }
+        else if( stream->doStop || stream->doAbort )    /* Should we stop/abort stream? */
+        {
+            if( stream->callbackResult == paContinue )     /* Ok, make it stop */
+            {
+                PA_DEBUG(( "%s: Stopping stream\n", __FUNCTION__ ));
+                stream->callbackResult = stream->doStop ? paComplete : paAbort;
+            }
+        }
+
+        if( stream->is_active )
+            ENSURE_PA( RealProcess( stream, frames ) );
+        /* If we have just entered inactive state, silence output */
+        if( !stream->is_active && !stream->isSilenced )
+        {
+            int i;
+
+            /* Silence buffer after entering inactive state */
+            PA_DEBUG(( "Silencing the output\n" ));
+            for( i = 0; i < stream->num_outgoing_connections; ++i )
+            {
+                jack_default_audio_sample_t *buffer = jack_port_get_buffer( stream->local_output_ports[i], frames );
+                memset( buffer, 0, sizeof (jack_default_audio_sample_t) * frames );
+            }
+
+            stream->isSilenced = 1;
+        }
+
+        if( stream->doStop || stream->doAbort )
+        {
+            /* See if RealProcess has acted on the request */
+            if( !stream->is_active )   /* Ok, signal to the main thread that we've carried out the operation */
+            {
+                /* If we can't obtain a lock, we'll try next time */
+                int err = pthread_mutex_trylock( &stream->hostApi->mtx );
+                if( !err )
+                {
+                    stream->doStop = stream->doAbort = 0;
+                    ASSERT_CALL( pthread_cond_signal( &stream->hostApi->cond ), 0 );
+                    ASSERT_CALL( pthread_mutex_unlock( &stream->hostApi->mtx ), 0 );
+                }
+                else
+                    assert( err == EBUSY );
+            }
+        }
+    }
+
+    return 0;
+error:
+    return -1;
+}
+
+static PaError StartStream( PaStream *s )
+{
+    PaError result = paNoError;
+    PaJackStream *stream = (PaJackStream*)s;
+    int i;
+
+    /* Ready the processor */
+    PaUtil_ResetBufferProcessor( &stream->bufferProcessor );
+
+    /* connect the ports */
+
+    /* NOTE: I would rather use jack_port_connect which uses jack_port_t's
+     * instead of port names, but it is not implemented yet. */
+    if( stream->num_incoming_connections > 0 )
+    {
+        for( i = 0; i < stream->num_incoming_connections; i++ )
+            UNLESS( jack_connect( stream->jack_client,
+                    jack_port_name( stream->remote_output_ports[i] ),
+                    jack_port_name( stream->local_input_ports[i] ) ) == 0, paUnanticipatedHostError );
+    }
+
+    if( stream->num_outgoing_connections > 0 )
+    {
+        for( i = 0; i < stream->num_outgoing_connections; i++ )
+            UNLESS( jack_connect( stream->jack_client,
+                    jack_port_name( stream->local_output_ports[i] ),
+                    jack_port_name( stream->remote_input_ports[i] ) ) == 0, paUnanticipatedHostError );
+    }
+
+    stream->xrun = FALSE;
+
+    /* Enable processing */
+
+    ASSERT_CALL( pthread_mutex_lock( &stream->hostApi->mtx ), 0 );
+    stream->doStart = 1;
+
+    /* Wait for stream to be started */
+    result = WaitCondition( stream->hostApi );
+    /*
+    do
+    {
+        err = pthread_cond_timedwait( &stream->hostApi->cond, &stream->hostApi->mtx, &ts );
+    } while( !stream->is_active && !err );
+    */
+    if( result != paNoError )   /* Something went wrong, call off the stream start */
+    {
+        stream->doStart = 0;
+        stream->is_active = 0;  /* Cancel any processing */
+    }
+    ASSERT_CALL( pthread_mutex_unlock( &stream->hostApi->mtx ), 0 );
+
+    ENSURE_PA( result );
+
+    stream->is_running = TRUE;
+    PA_DEBUG(( "%s: Stream started\n", __FUNCTION__ ));
+
+error:
+    return result;
+}
+
+static PaError RealStop( PaJackStream *stream, int abort )
+{
+    PaError result = paNoError;
+    int i;
+
+    if( stream->isBlockingStream )
+        BlockingWaitEmpty ( stream );
+
+    ASSERT_CALL( pthread_mutex_lock( &stream->hostApi->mtx ), 0 );
+    if( abort )
+        stream->doAbort = 1;
+    else
+        stream->doStop = 1;
+
+    /* Wait for stream to be stopped */
+    result = WaitCondition( stream->hostApi );
+    ASSERT_CALL( pthread_mutex_unlock( &stream->hostApi->mtx ), 0 );
+    ENSURE_PA( result );
+
+    UNLESS( !stream->is_active, paInternalError );
+    
+    PA_DEBUG(( "%s: Stream stopped\n", __FUNCTION__ ));
+
+error:
+    stream->is_running = FALSE;
+
+    /* Disconnect ports belonging to this stream */
+
+    if( !stream->hostApi->jackIsDown )  /* XXX: Well? */
+    {
+        if( stream->num_incoming_connections > 0 )
+        {
+            for( i = 0; i < stream->num_incoming_connections; i++ )
+                UNLESS( !jack_disconnect( stream->jack_client,
+                            jack_port_name( stream->remote_output_ports[i] ),
+                            jack_port_name( stream->local_input_ports[i] ) ), paUnanticipatedHostError );
+        }
+        if( stream->num_outgoing_connections > 0 )
+        {
+            for( i = 0; i < stream->num_outgoing_connections; i++ )
+                UNLESS( !jack_disconnect( stream->jack_client,
+                            jack_port_name( stream->local_output_ports[i] ),
+                            jack_port_name( stream->remote_input_ports[i] ) ), paUnanticipatedHostError );
+        }
+    }
+
+    return result;
+}
+
+static PaError StopStream( PaStream *s )
+{
+    assert(s);
+    return RealStop( (PaJackStream *)s, 0 );
+}
+
+static PaError AbortStream( PaStream *s )
+{
+    assert(s);
+    return RealStop( (PaJackStream *)s, 1 );
+}
+
+static PaError IsStreamStopped( PaStream *s )
+{
+    PaJackStream *stream = (PaJackStream*)s;
+    return !stream->is_running;
+}
+
+
+static PaError IsStreamActive( PaStream *s )
+{
+    PaJackStream *stream = (PaJackStream*)s;
+    return stream->is_active;
+}
+
+
+static PaTime GetStreamTime( PaStream *s )
+{
+    PaJackStream *stream = (PaJackStream*)s;
+
+    /* A: Is this relevant?? --> TODO: what if we're recording-only? */
+    return (jack_frame_time( stream->jack_client ) - stream->t0) / (PaTime)jack_get_sample_rate( stream->jack_client );
+}
+
+
+static double GetStreamCpuLoad( PaStream* s )
+{
+    PaJackStream *stream = (PaJackStream*)s;
+    return PaUtil_GetCpuLoad( &stream->cpuLoadMeasurer );
+}
diff --git a/src/audio/portaudio/pa_linux_alsa/pa_linux_alsa.c b/src/audio/portaudio/pa_linux_alsa/pa_linux_alsa.c
new file mode 100644
index 0000000000000000000000000000000000000000..f12483ddea52366d078e40dd7e256b90b7d269be
--- /dev/null
+++ b/src/audio/portaudio/pa_linux_alsa/pa_linux_alsa.c
@@ -0,0 +1,3272 @@
+/*
+ * $Id$
+ * PortAudio Portable Real-Time Audio Library
+ * Latest Version at: http://www.portaudio.com
+ * ALSA implementation by Joshua Haberman and Arve Knudsen
+ *
+ * Copyright (c) 2002 Joshua Haberman <joshua@haberman.com>
+ * Copyright (c) 2005 Arve Knudsen <aknuds-1@broadpark.no>
+ *
+ * Based on the Open Source API proposed by Ross Bencina
+ * Copyright (c) 1999-2002 Ross Bencina, 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 ALSA_PCM_NEW_HW_PARAMS_API
+#define ALSA_PCM_NEW_SW_PARAMS_API
+#include <alsa/asoundlib.h>
+#undef ALSA_PCM_NEW_HW_PARAMS_API
+#undef ALSA_PCM_NEW_SW_PARAMS_API
+
+#include <sys/poll.h>
+#include <string.h> /* strlen() */
+#include <limits.h>
+#include <math.h>
+#include <pthread.h>
+#include <signal.h>
+#include <time.h>
+#include <sys/mman.h>
+#include <signal.h> /* For sig_atomic_t */
+
+#include "portaudio.h"
+#include "pa_util.h"
+#include "../pa_unix/pa_unix_util.h"
+#include "pa_allocation.h"
+#include "pa_hostapi.h"
+#include "pa_stream.h"
+#include "pa_cpuload.h"
+#include "pa_process.h"
+
+#include "pa_linux_alsa.h"
+
+/* Check return value of ALSA function, and map it to PaError */
+#define ENSURE_(expr, code) \
+    do { \
+        if( UNLIKELY( (aErr_ = (expr)) < 0 ) ) \
+        { \
+            /* PaUtil_SetLastHostErrorInfo should only be used in the main thread */ \
+            if( (code) == paUnanticipatedHostError && pthread_self() != callbackThread_ ) \
+            { \
+                PaUtil_SetLastHostErrorInfo( paALSA, aErr_, snd_strerror( aErr_ ) ); \
+            } \
+            PaUtil_DebugPrint(( "Expression '" #expr "' failed in '" __FILE__ "', line: " STRINGIZE( __LINE__ ) "\n" )); \
+            result = (code); \
+            goto error; \
+        } \
+    } while( 0 );
+
+#define ASSERT_CALL_(expr, success) \
+    aErr_ = (expr); \
+    assert( aErr_ == success );
+
+static int aErr_;               /* Used with ENSURE_ */
+static pthread_t callbackThread_;
+
+typedef enum
+{
+    StreamDirection_In,
+    StreamDirection_Out
+} StreamDirection;
+
+/* Threading utility struct */
+typedef struct PaAlsaThreading
+{
+    pthread_t watchdogThread;
+    pthread_t callbackThread;
+    int watchdogRunning;
+    int rtSched;
+    int rtPrio;
+    int useWatchdog;
+    unsigned long throttledSleepTime;
+    volatile PaTime callbackTime;
+    volatile PaTime callbackCpuTime;
+    PaUtilCpuLoadMeasurer *cpuLoadMeasurer;
+} PaAlsaThreading;
+
+typedef struct
+{
+    PaSampleFormat hostSampleFormat;
+    unsigned long framesPerBuffer;
+    int numUserChannels, numHostChannels;
+    int userInterleaved, hostInterleaved;
+
+    snd_pcm_t *pcm;
+    snd_pcm_uframes_t bufferSize;
+    snd_pcm_format_t nativeFormat;
+    unsigned int nfds;
+    int ready;  /* Marked ready from poll */
+    void **userBuffers;
+    snd_pcm_uframes_t offset;
+    StreamDirection streamDir;
+
+    snd_pcm_channel_area_t *channelAreas;  /* Needed for channel adaption */
+} PaAlsaStreamComponent;
+
+/* Implementation specific stream structure */
+typedef struct PaAlsaStream
+{
+    PaUtilStreamRepresentation streamRepresentation;
+    PaUtilCpuLoadMeasurer cpuLoadMeasurer;
+    PaUtilBufferProcessor bufferProcessor;
+    PaAlsaThreading threading;
+
+    unsigned long framesPerUserBuffer;
+
+    int primeBuffers;
+    int callbackMode;              /* bool: are we running in callback mode? */
+    int pcmsSynced;	            /* Have we successfully synced pcms */
+
+    /* the callback thread uses these to poll the sound device(s), waiting
+     * for data to be ready/available */
+    struct pollfd *pfds;
+    int pollTimeout;
+
+    /* Used in communication between threads */
+    volatile sig_atomic_t callback_finished; /* bool: are we in the "callback finished" state? */
+    volatile sig_atomic_t callbackAbort;    /* Drop frames? */
+    volatile sig_atomic_t callbackStop;     /* Signal a stop */
+    volatile sig_atomic_t isActive;         /* Is stream in active state? (Between StartStream and StopStream || !paContinue) */
+    pthread_mutex_t stateMtx;               /* Used to synchronize access to stream state */
+    pthread_mutex_t startMtx;               /* Used to synchronize stream start in callback mode */
+    pthread_cond_t startCond;               /* Wait untill audio is started in callback thread */
+
+    int neverDropInput;
+
+    PaTime underrun;
+    PaTime overrun;
+
+    PaAlsaStreamComponent capture, playback;
+}
+PaAlsaStream;
+
+/* PaAlsaHostApiRepresentation - host api datastructure specific to this implementation */
+
+typedef struct PaAlsaHostApiRepresentation
+{
+    PaUtilHostApiRepresentation commonHostApiRep;
+    PaUtilStreamInterface callbackStreamInterface;
+    PaUtilStreamInterface blockingStreamInterface;
+
+    PaUtilAllocationGroup *allocations;
+
+    PaHostApiIndex hostApiIndex;
+}
+PaAlsaHostApiRepresentation;
+
+typedef struct PaAlsaDeviceInfo
+{
+    PaDeviceInfo commonDeviceInfo;
+    char *alsaName;
+    int isPlug;
+    int minInputChannels;
+    int minOutputChannels;
+}
+PaAlsaDeviceInfo;
+
+/* Threading utilities */
+
+static void InitializeThreading( PaAlsaThreading *th, PaUtilCpuLoadMeasurer *clm )
+{
+    th->watchdogRunning = 0;
+    th->rtSched = 0;
+    th->callbackTime = 0;
+    th->callbackCpuTime = 0;
+    th->useWatchdog = 1;
+    th->throttledSleepTime = 0;
+    th->cpuLoadMeasurer = clm;
+
+    th->rtPrio = (sched_get_priority_max( SCHED_FIFO ) - sched_get_priority_min( SCHED_FIFO )) / 2
+            + sched_get_priority_min( SCHED_FIFO );
+}
+
+static PaError KillCallbackThread( PaAlsaThreading *th, int wait, PaError *exitResult, PaError *watchdogExitResult )
+{
+    PaError result = paNoError;
+    void *pret;
+
+    if( exitResult )
+        *exitResult = paNoError;
+    if( watchdogExitResult )
+        *watchdogExitResult = paNoError;
+
+    if( th->watchdogRunning )
+    {
+        pthread_cancel( th->watchdogThread );
+        ASSERT_CALL_( pthread_join( th->watchdogThread, &pret ), 0 );
+
+        if( pret && pret != PTHREAD_CANCELED )
+        {
+            if( watchdogExitResult )
+                *watchdogExitResult = *(PaError *) pret;
+            free( pret );
+        }
+    }
+
+    /* Only kill the thread if it isn't in the process of stopping (flushing adaptation buffers) */
+    /* TODO: Make join time out */
+    if( !wait )
+        pthread_cancel( th->callbackThread );   /* XXX: Safe to call this if the thread has exited on its own? */
+    ASSERT_CALL_( pthread_join( th->callbackThread, &pret ), 0 );
+
+    if( pret && pret != PTHREAD_CANCELED )
+    {
+        if( exitResult )
+            *exitResult = *(PaError *) pret;
+        free( pret );
+    }
+
+    return result;
+}
+
+static void OnWatchdogExit( void *userData )
+{
+    PaAlsaThreading *th = (PaAlsaThreading *) userData;
+    struct sched_param spm = { 0 };
+    assert( th );
+
+    ASSERT_CALL_( pthread_setschedparam( th->callbackThread, SCHED_OTHER, &spm ), 0 );    /* Lower before exiting */
+    PA_DEBUG(( "Watchdog exiting\n" ));
+}
+
+static PaError BoostPriority( PaAlsaThreading *th )
+{
+    PaError result = paNoError;
+    struct sched_param spm = { 0 };
+    spm.sched_priority = th->rtPrio;
+
+    assert( th );
+
+    if( pthread_setschedparam( th->callbackThread, SCHED_FIFO, &spm ) != 0 )
+    {
+        PA_UNLESS( errno == EPERM, paInternalError );  /* Lack permission to raise priority */
+        PA_DEBUG(( "Failed bumping priority\n" ));
+        result = 0;
+    }
+    else
+        result = 1; /* Success */
+error:
+    return result;
+}
+
+static void *WatchdogFunc( void *userData )
+{
+    PaError result = paNoError, *pres = NULL;
+    int err;
+    PaAlsaThreading *th = (PaAlsaThreading *) userData;
+    unsigned intervalMsec = 500;
+    const PaTime maxSeconds = 3.;   /* Max seconds between callbacks */
+    PaTime timeThen = PaUtil_GetTime(), timeNow, timeElapsed, cpuTimeThen, cpuTimeNow, cpuTimeElapsed;
+    double cpuLoad, avgCpuLoad = 0.;
+    int throttled = 0;
+
+    assert( th );
+
+    pthread_cleanup_push( &OnWatchdogExit, th );	/* Execute OnWatchdogExit when exiting */
+
+    /* Boost priority of callback thread */
+    PA_ENSURE( result = BoostPriority( th ) );
+    if( !result )
+    {
+        pthread_exit( NULL );   /* Boost failed, might as well exit */
+    }
+
+    cpuTimeThen = th->callbackCpuTime;
+    {
+        int policy;
+        struct sched_param spm = { 0 };
+        pthread_getschedparam( pthread_self(), &policy, &spm );
+        PA_DEBUG(( "%s: Watchdog priority is %d\n", __FUNCTION__, spm.sched_priority ));
+    }
+
+    while( 1 )
+    {
+        double lowpassCoeff = 0.9, lowpassCoeff1 = 0.99999 - lowpassCoeff;
+        
+        /* Test before and after in case whatever underlying sleep call isn't interrupted by pthread_cancel */
+        pthread_testcancel();
+        Pa_Sleep( intervalMsec );
+        pthread_testcancel();
+
+        if( PaUtil_GetTime() - th->callbackTime > maxSeconds )
+        {
+            PA_DEBUG(( "Watchdog: Terminating callback thread\n" ));
+            /* Tell thread to terminate */
+            err = pthread_kill( th->callbackThread, SIGKILL );
+            pthread_exit( NULL );
+        }
+
+        PA_DEBUG(( "%s: PortAudio reports CPU load: %g\n", __FUNCTION__, PaUtil_GetCpuLoad( th->cpuLoadMeasurer ) ));
+
+        /* Check if we should throttle, or unthrottle :P */
+        cpuTimeNow = th->callbackCpuTime;
+        cpuTimeElapsed = cpuTimeNow - cpuTimeThen;
+        cpuTimeThen = cpuTimeNow;
+
+        timeNow = PaUtil_GetTime();
+        timeElapsed = timeNow - timeThen;
+        timeThen = timeNow;
+        cpuLoad = cpuTimeElapsed / timeElapsed;
+        avgCpuLoad = avgCpuLoad * lowpassCoeff + cpuLoad * lowpassCoeff1;
+        /*
+        if( throttled )
+            PA_DEBUG(( "Watchdog: CPU load: %g, %g\n", avgCpuLoad, cpuTimeElapsed ));
+            */
+        if( PaUtil_GetCpuLoad( th->cpuLoadMeasurer ) > .925 )
+        {
+            static int policy;
+            static struct sched_param spm = { 0 };
+            static const struct sched_param defaultSpm = { 0 };
+            PA_DEBUG(( "%s: Throttling audio thread, priority %d\n", __FUNCTION__, spm.sched_priority ));
+
+            pthread_getschedparam( th->callbackThread, &policy, &spm );
+            if( !pthread_setschedparam( th->callbackThread, SCHED_OTHER, &defaultSpm ) )
+            {
+                throttled = 1;
+            }
+            else
+                PA_DEBUG(( "Watchdog: Couldn't lower priority of audio thread: %s\n", strerror( errno ) ));
+
+            /* Give other processes a go, before raising priority again */
+            PA_DEBUG(( "%s: Watchdog sleeping for %lu msecs before unthrottling\n", __FUNCTION__, th->throttledSleepTime ));
+            Pa_Sleep( th->throttledSleepTime );
+
+            /* Reset callback priority */
+            if( pthread_setschedparam( th->callbackThread, SCHED_FIFO, &spm ) != 0 )
+            {
+                PA_DEBUG(( "%s: Couldn't raise priority of audio thread: %s\n", __FUNCTION__, strerror( errno ) ));
+            }
+
+            if( PaUtil_GetCpuLoad( th->cpuLoadMeasurer ) >= .99 )
+                intervalMsec = 50;
+            else
+                intervalMsec = 100;
+
+            /*
+            lowpassCoeff = .97;
+            lowpassCoeff1 = .99999 - lowpassCoeff;
+            */
+        }
+        else if( throttled && avgCpuLoad < .8 )
+        {
+            intervalMsec = 500;
+            throttled = 0;
+
+            /*
+            lowpassCoeff = .9;
+            lowpassCoeff1 = .99999 - lowpassCoeff;
+            */
+        }
+    }
+
+    pthread_cleanup_pop( 1 );   /* Execute cleanup on exit */
+
+error:
+    /* Shouldn't get here in the normal case */
+
+    /* Pass on error code */
+    pres = malloc( sizeof (PaError) );
+    *pres = result;
+    
+    pthread_exit( pres );
+}
+
+static PaError CreateCallbackThread( PaAlsaThreading *th, void *(*callbackThreadFunc)( void * ), PaStream *s )
+{
+    PaError result = paNoError;
+    pthread_attr_t attr;
+    int started = 0;
+
+#if defined _POSIX_MEMLOCK && (_POSIX_MEMLOCK != -1)
+    if( th->rtSched )
+    {
+        if( mlockall( MCL_CURRENT | MCL_FUTURE ) < 0 )
+        {
+            int savedErrno = errno;             /* In case errno gets overwritten */
+            assert( savedErrno != EINVAL );     /* Most likely a programmer error */
+            PA_UNLESS( (savedErrno == EPERM), paInternalError );
+            PA_DEBUG(( "%s: Failed locking memory\n", __FUNCTION__ ));
+        }
+        else
+            PA_DEBUG(( "%s: Successfully locked memory\n", __FUNCTION__ ));
+    }
+#endif
+
+    PA_UNLESS( !pthread_attr_init( &attr ), paInternalError );
+    /* Priority relative to other processes */
+    PA_UNLESS( !pthread_attr_setscope( &attr, PTHREAD_SCOPE_SYSTEM ), paInternalError );   
+
+    PA_UNLESS( !pthread_create( &th->callbackThread, &attr, callbackThreadFunc, s ), paInternalError );
+    started = 1;
+
+    if( th->rtSched )
+    {
+        if( th->useWatchdog )
+        {
+            int err;
+            struct sched_param wdSpm = { 0 };
+            /* Launch watchdog, watchdog sets callback thread priority */
+            int prio = PA_MIN( th->rtPrio + 4, sched_get_priority_max( SCHED_FIFO ) );
+            wdSpm.sched_priority = prio;
+
+            PA_UNLESS( !pthread_attr_init( &attr ), paInternalError );
+            PA_UNLESS( !pthread_attr_setinheritsched( &attr, PTHREAD_EXPLICIT_SCHED ), paInternalError );
+            PA_UNLESS( !pthread_attr_setscope( &attr, PTHREAD_SCOPE_SYSTEM ), paInternalError );
+            PA_UNLESS( !pthread_attr_setschedpolicy( &attr, SCHED_FIFO ), paInternalError );
+            PA_UNLESS( !pthread_attr_setschedparam( &attr, &wdSpm ), paInternalError );
+            if( (err = pthread_create( &th->watchdogThread, &attr, &WatchdogFunc, th )) )
+            {
+                PA_UNLESS( err == EPERM, paInternalError );
+                /* Permission error, go on without realtime privileges */
+                PA_DEBUG(( "Failed bumping priority\n" ));
+            }
+            else
+            {
+                int policy;
+                th->watchdogRunning = 1;
+                ASSERT_CALL_( pthread_getschedparam( th->watchdogThread, &policy, &wdSpm ), 0 );
+                /* Check if priority is right, policy could potentially differ from SCHED_FIFO (but that's alright) */
+                if( wdSpm.sched_priority != prio )
+                {
+                    PA_DEBUG(( "Watchdog priority not set correctly (%d)\n", wdSpm.sched_priority ));
+                    PA_ENSURE( paInternalError );
+                }
+            }
+        }
+        else
+            PA_ENSURE( BoostPriority( th ) );
+    }
+
+end:
+    return result;
+error:
+    if( started )
+        KillCallbackThread( th, 0, NULL, NULL );
+
+    goto end;
+}
+
+static void CallbackUpdate( PaAlsaThreading *th )
+{
+    th->callbackTime = PaUtil_GetTime();
+    th->callbackCpuTime = PaUtil_GetCpuLoad( th->cpuLoadMeasurer );
+}
+
+/* prototypes for functions declared in this file */
+
+static void Terminate( struct PaUtilHostApiRepresentation *hostApi );
+static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
+                                  const PaStreamParameters *inputParameters,
+                                  const PaStreamParameters *outputParameters,
+                                  double sampleRate );
+static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
+                           PaStream** s,
+                           const PaStreamParameters *inputParameters,
+                           const PaStreamParameters *outputParameters,
+                           double sampleRate,
+                           unsigned long framesPerBuffer,
+                           PaStreamFlags streamFlags,
+                           PaStreamCallback *callback,
+                           void *userData );
+static PaError CloseStream( PaStream* stream );
+static PaError StartStream( PaStream *stream );
+static PaError StopStream( PaStream *stream );
+static PaError AbortStream( PaStream *stream );
+static PaError IsStreamStopped( PaStream *s );
+static PaError IsStreamActive( PaStream *stream );
+static PaTime GetStreamTime( PaStream *stream );
+static double GetStreamCpuLoad( PaStream* stream );
+static PaError BuildDeviceList( PaAlsaHostApiRepresentation *hostApi );
+static int SetApproximateSampleRate( snd_pcm_t *pcm, snd_pcm_hw_params_t *hwParams, double sampleRate );
+static int GetExactSampleRate( snd_pcm_hw_params_t *hwParams, double *sampleRate );
+
+/* Callback prototypes */
+static void *CallbackThreadFunc( void *userData );
+
+/* Blocking prototypes */
+static signed long GetStreamReadAvailable( PaStream* s );
+static signed long GetStreamWriteAvailable( PaStream* s );
+static PaError ReadStream( PaStream* stream, void *buffer, unsigned long frames );
+static PaError WriteStream( PaStream* stream, const void *buffer, unsigned long frames );
+
+
+static const PaAlsaDeviceInfo *GetDeviceInfo( const PaUtilHostApiRepresentation *hostApi, int device )
+{
+    return (const PaAlsaDeviceInfo *)hostApi->deviceInfos[device];
+}
+
+PaError PaAlsa_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex )
+{
+    PaError result = paNoError;
+    PaAlsaHostApiRepresentation *alsaHostApi = NULL;
+
+    PA_UNLESS( alsaHostApi = (PaAlsaHostApiRepresentation*) PaUtil_AllocateMemory(
+                sizeof(PaAlsaHostApiRepresentation) ), paInsufficientMemory );
+    PA_UNLESS( alsaHostApi->allocations = PaUtil_CreateAllocationGroup(), paInsufficientMemory );
+    alsaHostApi->hostApiIndex = hostApiIndex;
+
+    *hostApi = (PaUtilHostApiRepresentation*)alsaHostApi;
+    (*hostApi)->info.structVersion = 1;
+    (*hostApi)->info.type = paALSA;
+    (*hostApi)->info.name = "ALSA";
+
+    (*hostApi)->Terminate = Terminate;
+    (*hostApi)->OpenStream = OpenStream;
+    (*hostApi)->IsFormatSupported = IsFormatSupported;
+
+    PA_ENSURE( BuildDeviceList( alsaHostApi ) );
+
+    PaUtil_InitializeStreamInterface( &alsaHostApi->callbackStreamInterface,
+                                      CloseStream, StartStream,
+                                      StopStream, AbortStream,
+                                      IsStreamStopped, IsStreamActive,
+                                      GetStreamTime, GetStreamCpuLoad,
+                                      PaUtil_DummyRead, PaUtil_DummyWrite,
+                                      PaUtil_DummyGetReadAvailable,
+                                      PaUtil_DummyGetWriteAvailable );
+
+    PaUtil_InitializeStreamInterface( &alsaHostApi->blockingStreamInterface,
+                                      CloseStream, StartStream,
+                                      StopStream, AbortStream,
+                                      IsStreamStopped, IsStreamActive,
+                                      GetStreamTime, PaUtil_DummyGetCpuLoad,
+                                      ReadStream, WriteStream,
+                                      GetStreamReadAvailable,
+                                      GetStreamWriteAvailable );
+
+    return result;
+
+error:
+    if( alsaHostApi )
+    {
+        if( alsaHostApi->allocations )
+        {
+            PaUtil_FreeAllAllocations( alsaHostApi->allocations );
+            PaUtil_DestroyAllocationGroup( alsaHostApi->allocations );
+        }
+
+        PaUtil_FreeMemory( alsaHostApi );
+    }
+
+    return result;
+}
+
+static void Terminate( struct PaUtilHostApiRepresentation *hostApi )
+{
+    PaAlsaHostApiRepresentation *alsaHostApi = (PaAlsaHostApiRepresentation*)hostApi;
+
+    assert( hostApi );
+
+    if( alsaHostApi->allocations )
+    {
+        PaUtil_FreeAllAllocations( alsaHostApi->allocations );
+        PaUtil_DestroyAllocationGroup( alsaHostApi->allocations );
+    }
+
+    PaUtil_FreeMemory( alsaHostApi );
+    snd_config_update_free_global();
+}
+
+/*! Determine max channels and default latencies.
+ *
+ * This function provides functionality to grope an opened (might be opened for capture or playback) pcm device for 
+ * traits like max channels, suitable default latencies and default sample rate. Upon error, max channels is set to zero,
+ * and a suitable result returned. The device is closed before returning.
+ */
+static PaError GropeDevice( snd_pcm_t *pcm, int *minChannels, int *maxChannels, double *defaultLowLatency,
+        double *defaultHighLatency, double *defaultSampleRate, int isPlug )
+{
+    PaError result = paNoError;
+    snd_pcm_hw_params_t *hwParams;
+    snd_pcm_uframes_t lowLatency = 512, highLatency = 2048;
+    unsigned int minChans, maxChans;
+    double defaultSr = *defaultSampleRate;
+
+    assert( pcm );
+
+    ENSURE_( snd_pcm_nonblock( pcm, 0 ), paUnanticipatedHostError );
+
+    snd_pcm_hw_params_alloca( &hwParams );
+    snd_pcm_hw_params_any( pcm, hwParams );
+
+    if( defaultSr >= 0 )
+    {
+        /* Could be that the device opened in one mode supports samplerates that the other mode wont have,
+         * so try again .. */
+        if( SetApproximateSampleRate( pcm, hwParams, defaultSr ) < 0 )
+        {
+            defaultSr = -1.;
+            PA_DEBUG(( "%s: Original default samplerate failed, trying again ..\n", __FUNCTION__ ));
+        }
+    }
+
+    if( defaultSr < 0. )           /* Default sample rate not set */
+    {
+        unsigned int sampleRate = 44100;        /* Will contain approximate rate returned by alsa-lib */
+        ENSURE_( snd_pcm_hw_params_set_rate_near( pcm, hwParams, &sampleRate, NULL ), paUnanticipatedHostError );
+        ENSURE_( GetExactSampleRate( hwParams, &defaultSr ), paUnanticipatedHostError );
+    }
+
+    ENSURE_( snd_pcm_hw_params_get_channels_min( hwParams, &minChans ), paUnanticipatedHostError );
+    ENSURE_( snd_pcm_hw_params_get_channels_max( hwParams, &maxChans ), paUnanticipatedHostError );
+    assert( maxChans <= INT_MAX );
+    assert( maxChans > 0 );    /* Weird linking issue could cause wrong version of ALSA symbols to be called,
+                                   resulting in zeroed values */
+
+    /* XXX: Limit to sensible number (ALSA plugins accept a crazy amount of channels)? */
+    if( isPlug && maxChans > 128 )
+    {
+        maxChans = 128;
+        PA_DEBUG(( "%s: Limiting number of plugin channels to %u\n", __FUNCTION__, maxChans ));
+    }
+
+    /* TWEAKME:
+     *
+     * Giving values for default min and max latency is not
+     * straightforward.  Here are our objectives:
+     *
+     *         * for low latency, we want to give the lowest value
+     *         that will work reliably.  This varies based on the
+     *         sound card, kernel, CPU, etc.  I think it is better
+     *         to give sub-optimal latency than to give a number
+     *         too low and cause dropouts.  My conservative
+     *         estimate at this point is to base it on 4096-sample
+     *         latency at 44.1 kHz, which gives a latency of 23ms.
+     *         * for high latency we want to give a large enough
+     *         value that dropouts are basically impossible.  This
+     *         doesn't really require as much tweaking, since
+     *         providing too large a number will just cause us to
+     *         select the nearest setting that will work at stream
+     *         config time.
+     */
+    ENSURE_( snd_pcm_hw_params_set_buffer_size_near( pcm, hwParams, &lowLatency ), paUnanticipatedHostError );
+
+    /* Have to reset hwParams, to set new buffer size */
+    ENSURE_( snd_pcm_hw_params_any( pcm, hwParams ), paUnanticipatedHostError ); 
+    ENSURE_( snd_pcm_hw_params_set_buffer_size_near( pcm, hwParams, &highLatency ), paUnanticipatedHostError );
+
+    *minChannels = (int)minChans;
+    *maxChannels = (int)maxChans;
+    *defaultSampleRate = defaultSr;
+    *defaultLowLatency = (double) lowLatency / *defaultSampleRate;
+    *defaultHighLatency = (double) highLatency / *defaultSampleRate;
+
+end:
+    snd_pcm_close( pcm );
+    return result;
+
+error:
+    goto end;
+}
+
+/* Initialize device info with invalid values (maxInputChannels and maxOutputChannels are set to zero since these indicate
+ * wether input/output is available) */
+static void InitializeDeviceInfo( PaDeviceInfo *deviceInfo )
+{
+    deviceInfo->structVersion = -1;
+    deviceInfo->name = NULL;
+    deviceInfo->hostApi = -1;
+    deviceInfo->maxInputChannels = 0;
+    deviceInfo->maxOutputChannels = 0;
+    deviceInfo->defaultLowInputLatency = -1.;
+    deviceInfo->defaultLowOutputLatency = -1.;
+    deviceInfo->defaultHighInputLatency = -1.;
+    deviceInfo->defaultHighOutputLatency = -1.;
+    deviceInfo->defaultSampleRate = -1.;
+}
+
+/* Helper struct */
+typedef struct
+{
+    char *alsaName;
+    char *name;
+    int isPlug;
+    int hasPlayback;
+    int hasCapture;
+} DeviceNames;
+
+static PaError PaAlsa_StrDup( PaAlsaHostApiRepresentation *alsaApi,
+        char **dst,
+        const char *src)
+{
+    PaError result = paNoError;
+    int len = strlen( src ) + 1;
+
+    /* PA_DEBUG(("PaStrDup %s %d\n", src, len)); */
+
+    PA_UNLESS( *dst = (char *)PaUtil_GroupAllocateMemory( alsaApi->allocations, len ),
+            paInsufficientMemory );
+    strncpy( *dst, src, len );
+
+error:
+    return result;
+}
+
+/* Disregard standard plugins
+ * XXX: Might want to make the "default" plugin available, if we can make it work
+ */
+static int IgnorePlugin( const char *pluginId )
+{
+#define numIgnored 10
+    static const char *ignoredPlugins[numIgnored] = {"hw", "plughw", "plug", "default", "dsnoop", "dmix", "tee",
+        "file", "null", "shm"};
+    int i;
+
+    for( i = 0; i < numIgnored; ++i )
+    {
+        if( !strcmp( pluginId, ignoredPlugins[i] ) )
+        {
+            return 1;
+        }
+    }
+
+    return 0;
+}
+
+/* Build PaDeviceInfo list, ignore devices for which we cannot determine capabilities (possibly busy, sigh) */
+static PaError BuildDeviceList( PaAlsaHostApiRepresentation *alsaApi )
+{
+    PaUtilHostApiRepresentation *commonApi = &alsaApi->commonHostApiRep;
+    PaAlsaDeviceInfo *deviceInfoArray;
+    int cardIdx = -1, devIdx = 0;
+    snd_ctl_card_info_t *cardInfo;
+    PaError result = paNoError;
+    size_t numDeviceNames = 0, maxDeviceNames = 1, i;
+    DeviceNames *deviceNames = NULL;
+    snd_config_t *topNode = NULL;
+    snd_pcm_info_t *pcmInfo;
+    int res;
+    int blocking = SND_PCM_NONBLOCK;
+    char alsaCardName[50];
+    if( getenv( "PA_ALSA_INITIALIZE_BLOCK" ) && atoi( getenv( "PA_ALSA_INITIALIZE_BLOCK" ) ) )
+        blocking = 0;
+
+    /* These two will be set to the first working input and output device, respectively */
+    commonApi->info.defaultInputDevice = paNoDevice;
+    commonApi->info.defaultOutputDevice = paNoDevice;
+
+    /* count the devices by enumerating all the card numbers */
+
+    /* snd_card_next() modifies the integer passed to it to be:
+     *      the index of the first card if the parameter is -1
+     *      the index of the next card if the parameter is the index of a card
+     *      -1 if there are no more cards
+     *
+     * The function itself returns 0 if it succeeded. */
+    cardIdx = -1;
+    snd_ctl_card_info_alloca( &cardInfo );
+    snd_pcm_info_alloca( &pcmInfo );
+    while( snd_card_next( &cardIdx ) == 0 && cardIdx >= 0 )
+    {
+        char *cardName;
+        int devIdx = -1;
+        snd_ctl_t *ctl;
+        char buf[50];
+
+        snprintf( alsaCardName, sizeof (alsaCardName), "hw:%d", cardIdx );
+
+        /* Acquire name of card */
+        if( snd_ctl_open( &ctl, alsaCardName, 0 ) < 0 )
+            continue;   /* Unable to open card :( */
+        snd_ctl_card_info( ctl, cardInfo );
+
+        PA_ENSURE( PaAlsa_StrDup( alsaApi, &cardName, snd_ctl_card_info_get_name( cardInfo )) );
+
+        while( snd_ctl_pcm_next_device( ctl, &devIdx ) == 0 && devIdx >= 0 )
+        {
+            char *alsaDeviceName, *deviceName;
+            size_t len;
+            int hasPlayback = 0, hasCapture = 0;
+            snprintf( buf, sizeof (buf), "%s:%d,%d", "hw", cardIdx, devIdx );
+
+            /* Obtain info about this particular device */
+            snd_pcm_info_set_device( pcmInfo, devIdx );
+            snd_pcm_info_set_subdevice( pcmInfo, 0 );
+            snd_pcm_info_set_stream( pcmInfo, SND_PCM_STREAM_CAPTURE );
+            if( snd_ctl_pcm_info( ctl, pcmInfo ) >= 0 )
+                hasCapture = 1;
+            
+            snd_pcm_info_set_stream( pcmInfo, SND_PCM_STREAM_PLAYBACK );
+            if( snd_ctl_pcm_info( ctl, pcmInfo ) >= 0 )
+                hasPlayback = 1;
+
+            if( !hasPlayback && !hasCapture )
+            {
+                continue;   /* Error */
+            }
+
+            /* The length of the string written by snprintf plus terminating 0 */
+            len = snprintf( NULL, 0, "%s: %s (%s)", cardName, snd_pcm_info_get_name( pcmInfo ), buf ) + 1;
+            PA_UNLESS( deviceName = (char *)PaUtil_GroupAllocateMemory( alsaApi->allocations, len ),
+                    paInsufficientMemory );
+            snprintf( deviceName, len, "%s: %s (%s)", cardName,
+                    snd_pcm_info_get_name( pcmInfo ), buf );
+
+            ++numDeviceNames;
+            if( !deviceNames || numDeviceNames > maxDeviceNames )
+            {
+                maxDeviceNames *= 2;
+                PA_UNLESS( deviceNames = (DeviceNames *) realloc( deviceNames, maxDeviceNames * sizeof (DeviceNames) ),
+                        paInsufficientMemory );
+            }
+
+            PA_ENSURE( PaAlsa_StrDup( alsaApi, &alsaDeviceName, buf ) );
+
+            deviceNames[ numDeviceNames - 1 ].alsaName = alsaDeviceName;
+            deviceNames[ numDeviceNames - 1 ].name = deviceName;
+            deviceNames[ numDeviceNames - 1 ].isPlug = 0;
+            deviceNames[ numDeviceNames - 1 ].hasPlayback = hasPlayback;
+            deviceNames[ numDeviceNames - 1 ].hasCapture = hasCapture;
+        }
+        snd_ctl_close( ctl );
+    }
+
+    /* Iterate over plugin devices */
+    snd_config_update();
+    if( (res = snd_config_search( snd_config, "pcm", &topNode )) >= 0 )
+    {
+        snd_config_iterator_t i, next;
+
+        snd_config_for_each( i, next, topNode )
+        {
+            const char *tpStr = NULL, *idStr = NULL;
+            char *alsaDeviceName, *deviceName;
+            snd_config_t *n = snd_config_iterator_entry( i ), *tp = NULL;
+            if( snd_config_get_type( n ) != SND_CONFIG_TYPE_COMPOUND )
+                continue;
+
+            ENSURE_( snd_config_search( n, "type", &tp ), paUnanticipatedHostError );
+            ENSURE_( snd_config_get_string( tp, &tpStr ), paUnanticipatedHostError );
+
+            ENSURE_( snd_config_get_id( n, &idStr ), paUnanticipatedHostError );
+            if( IgnorePlugin( idStr ) )
+            {
+                PA_DEBUG(( "%s: Ignoring ALSA plugin device %s of type %s\n", __FUNCTION__, idStr, tpStr ));
+                continue;
+            }
+
+            PA_DEBUG(( "%s: Found plugin %s of type %s\n", __FUNCTION__, idStr, tpStr ));
+
+            PA_UNLESS( alsaDeviceName = (char*)PaUtil_GroupAllocateMemory( alsaApi->allocations,
+                                                            strlen(idStr) + 6 ), paInsufficientMemory );
+            strcpy( alsaDeviceName, idStr );
+            PA_UNLESS( deviceName = (char*)PaUtil_GroupAllocateMemory( alsaApi->allocations,
+                                                            strlen(idStr) + 1 ), paInsufficientMemory );
+            strcpy( deviceName, idStr );
+
+            ++numDeviceNames;
+            if( !deviceNames || numDeviceNames > maxDeviceNames )
+            {
+                maxDeviceNames *= 2;
+                PA_UNLESS( deviceNames = (DeviceNames *) realloc( deviceNames, maxDeviceNames * sizeof (DeviceNames) ),
+                        paInsufficientMemory );
+            }
+
+            deviceNames[numDeviceNames - 1].alsaName = alsaDeviceName;
+            deviceNames[numDeviceNames - 1].name = deviceName;
+            deviceNames[numDeviceNames - 1].isPlug = 1;
+            deviceNames[numDeviceNames - 1].hasPlayback = 1;
+            deviceNames[numDeviceNames - 1].hasCapture = 1;
+        }
+    }
+    else
+        PA_DEBUG(( "%s: Iterating over ALSA plugins failed: %s\n", __FUNCTION__, snd_strerror( res ) ));
+
+    /* allocate deviceInfo memory based on the number of devices */
+    PA_UNLESS( commonApi->deviceInfos = (PaDeviceInfo**)PaUtil_GroupAllocateMemory(
+            alsaApi->allocations, sizeof(PaDeviceInfo*) * (numDeviceNames) ), paInsufficientMemory );
+
+    /* allocate all device info structs in a contiguous block */
+    PA_UNLESS( deviceInfoArray = (PaAlsaDeviceInfo*)PaUtil_GroupAllocateMemory(
+            alsaApi->allocations, sizeof(PaAlsaDeviceInfo) * numDeviceNames ), paInsufficientMemory );
+
+    /* Loop over list of cards, filling in info, if a device is deemed unavailable (can't get name),
+     * it's ignored.
+     */
+    /* while( snd_card_next( &cardIdx ) == 0 && cardIdx >= 0 ) */
+    for( i = 0, devIdx = 0; i < numDeviceNames; ++i )
+    {
+        snd_pcm_t *pcm;
+        PaAlsaDeviceInfo *deviceInfo = &deviceInfoArray[devIdx];
+        PaDeviceInfo *commonDeviceInfo = &deviceInfo->commonDeviceInfo;
+
+        /* Zero fields */
+        InitializeDeviceInfo( commonDeviceInfo );
+
+        /* to determine device capabilities, we must open the device and query the
+         * hardware parameter configuration space */
+
+        /* Query capture */
+        if( deviceNames[i].hasCapture &&
+                snd_pcm_open( &pcm, deviceNames[i].alsaName, SND_PCM_STREAM_CAPTURE, blocking ) >= 0 )
+        {
+            if( GropeDevice( pcm, &deviceInfo->minInputChannels, &commonDeviceInfo->maxInputChannels,
+                        &commonDeviceInfo->defaultLowInputLatency, &commonDeviceInfo->defaultHighInputLatency,
+                        &commonDeviceInfo->defaultSampleRate, deviceNames[i].isPlug ) != paNoError )
+                continue;   /* Error */
+        }
+
+        /* Query playback */
+        if( deviceNames[i].hasPlayback &&
+                snd_pcm_open( &pcm, deviceNames[i].alsaName, SND_PCM_STREAM_PLAYBACK, blocking ) >= 0 )
+        {
+            if( GropeDevice( pcm, &deviceInfo->minOutputChannels, &commonDeviceInfo->maxOutputChannels,
+                        &commonDeviceInfo->defaultLowOutputLatency, &commonDeviceInfo->defaultHighOutputLatency,
+                        &commonDeviceInfo->defaultSampleRate, deviceNames[i].isPlug ) != paNoError )
+                continue;   /* Error */
+        }
+
+        commonDeviceInfo->structVersion = 2;
+        commonDeviceInfo->hostApi = alsaApi->hostApiIndex;
+        commonDeviceInfo->name = deviceNames[i].name;
+        deviceInfo->alsaName = deviceNames[i].alsaName;
+        deviceInfo->isPlug = deviceNames[i].isPlug;
+
+        /* A: Storing pointer to PaAlsaDeviceInfo object as pointer to PaDeviceInfo object.
+         * Should now be safe to add device info, unless the device supports neither capture nor playback
+         */
+        if( commonDeviceInfo->maxInputChannels > 0 || commonDeviceInfo->maxOutputChannels > 0 )
+        {
+            if( commonApi->info.defaultInputDevice == paNoDevice && commonDeviceInfo->maxInputChannels > 0 )
+                commonApi->info.defaultInputDevice = devIdx;
+            if(  commonApi->info.defaultOutputDevice == paNoDevice && commonDeviceInfo->maxOutputChannels > 0 )
+                commonApi->info.defaultOutputDevice = devIdx;
+
+            commonApi->deviceInfos[devIdx++] = (PaDeviceInfo *) deviceInfo;
+        }
+    }
+    free( deviceNames );
+
+    commonApi->info.deviceCount = devIdx;   /* Number of successfully queried devices */
+
+end:
+    return result;
+
+error:
+    goto end;   /* No particular action */
+}
+
+/* Check against known device capabilities */
+static PaError ValidateParameters( const PaStreamParameters *parameters, PaUtilHostApiRepresentation *hostApi, StreamDirection mode )
+{
+    PaError result = paNoError;
+    int maxChans;
+    const PaAlsaDeviceInfo *deviceInfo = NULL;
+    assert( parameters );
+
+    if( parameters->device != paUseHostApiSpecificDeviceSpecification )
+    {
+        assert( parameters->device < hostApi->info.deviceCount );
+        PA_UNLESS( parameters->hostApiSpecificStreamInfo == NULL, paBadIODeviceCombination );
+        deviceInfo = GetDeviceInfo( hostApi, parameters->device );
+    }
+    else
+    {
+        const PaAlsaStreamInfo *streamInfo = parameters->hostApiSpecificStreamInfo;
+
+        PA_UNLESS( parameters->device == paUseHostApiSpecificDeviceSpecification, paInvalidDevice );
+        PA_UNLESS( streamInfo->size == sizeof (PaAlsaStreamInfo) && streamInfo->version == 1,
+                paIncompatibleHostApiSpecificStreamInfo );
+
+        return paNoError;   /* Skip further checking */
+    }
+
+    assert( deviceInfo );
+    assert( parameters->hostApiSpecificStreamInfo == NULL );
+    maxChans = (StreamDirection_In == mode ? deviceInfo->commonDeviceInfo.maxInputChannels :
+        deviceInfo->commonDeviceInfo.maxOutputChannels);
+    PA_UNLESS( parameters->channelCount <= maxChans, paInvalidChannelCount );
+
+error:
+    return result;
+}
+
+/* Given an open stream, what sample formats are available? */
+
+static PaSampleFormat GetAvailableFormats( snd_pcm_t *pcm )
+{
+    PaSampleFormat available = 0;
+    snd_pcm_hw_params_t *hwParams;
+    snd_pcm_hw_params_alloca( &hwParams );
+
+    snd_pcm_hw_params_any( pcm, hwParams );
+
+    if( snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_FLOAT ) >= 0)
+        available |= paFloat32;
+
+    if( snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_S32 ) >= 0)
+        available |= paInt32;
+
+    if( snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_S24 ) >= 0)
+        available |= paInt24;
+
+    if( snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_S16 ) >= 0)
+        available |= paInt16;
+
+    if( snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_U8 ) >= 0)
+        available |= paUInt8;
+
+    if( snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_S8 ) >= 0)
+        available |= paInt8;
+
+    return available;
+}
+
+static snd_pcm_format_t Pa2AlsaFormat( PaSampleFormat paFormat )
+{
+    switch( paFormat )
+    {
+        case paFloat32:
+            return SND_PCM_FORMAT_FLOAT;
+
+        case paInt16:
+            return SND_PCM_FORMAT_S16;
+
+        case paInt24:
+            return SND_PCM_FORMAT_S24;
+
+        case paInt32:
+            return SND_PCM_FORMAT_S32;
+
+        case paInt8:
+            return SND_PCM_FORMAT_S8;
+
+        case paUInt8:
+            return SND_PCM_FORMAT_U8;
+
+        default:
+            return SND_PCM_FORMAT_UNKNOWN;
+    }
+}
+
+/** Open an ALSA pcm handle.
+ * 
+ * The device to be open can be specified in a custom PaAlsaStreamInfo struct, or it will be a device number. In case of a
+ * device number, it maybe specified through an env variable (PA_ALSA_PLUGHW) that we should open the corresponding plugin
+ * device.
+ */
+static PaError AlsaOpen( const PaUtilHostApiRepresentation *hostApi, const PaStreamParameters *params, StreamDirection
+        streamDir, snd_pcm_t **pcm )
+{
+    PaError result = paNoError;
+    int ret;
+    const char *deviceName = alloca( 50 );
+    const PaAlsaDeviceInfo *deviceInfo = NULL;
+    PaAlsaStreamInfo *streamInfo = (PaAlsaStreamInfo *)params->hostApiSpecificStreamInfo;
+
+    if( !streamInfo )
+    {
+        int usePlug = 0;
+        deviceInfo = GetDeviceInfo( hostApi, params->device );
+        
+        /* If device name starts with hw: and PA_ALSA_PLUGHW is 1, we open the plughw device instead */
+        if( !strncmp( "hw:", deviceInfo->alsaName, 3 ) && getenv( "PA_ALSA_PLUGHW" ) )
+            usePlug = atoi( getenv( "PA_ALSA_PLUGHW" ) );
+        if( usePlug )
+            snprintf( (char *) deviceName, 50, "plug%s", deviceInfo->alsaName );
+        else
+            deviceName = deviceInfo->alsaName;
+    }
+    else
+        deviceName = streamInfo->deviceString;
+
+    if( (ret = snd_pcm_open( pcm, deviceName, streamDir == StreamDirection_In ? SND_PCM_STREAM_CAPTURE : SND_PCM_STREAM_PLAYBACK,
+                    SND_PCM_NONBLOCK )) < 0 )
+    {
+        *pcm = NULL;     /* Not to be closed */
+        ENSURE_( ret, ret == -EBUSY ? paDeviceUnavailable : paBadIODeviceCombination );
+    }
+    ENSURE_( snd_pcm_nonblock( *pcm, 0 ), paUnanticipatedHostError );
+
+end:
+    return result;
+
+error:
+    goto end;
+}
+
+static PaError TestParameters( const PaUtilHostApiRepresentation *hostApi, const PaStreamParameters *parameters,
+        double sampleRate, StreamDirection streamDir )
+{
+    PaError result = paNoError;
+    snd_pcm_t *pcm = NULL;
+    PaSampleFormat availableFormats;
+    /* We are able to adapt to a number of channels less than what the device supports */
+    unsigned int numHostChannels;
+    PaSampleFormat hostFormat;
+    snd_pcm_hw_params_t *hwParams;
+    snd_pcm_hw_params_alloca( &hwParams );
+    
+    if( !parameters->hostApiSpecificStreamInfo )
+    {
+        const PaAlsaDeviceInfo *devInfo = GetDeviceInfo( hostApi, parameters->device );
+        numHostChannels = PA_MAX( parameters->channelCount, StreamDirection_In == streamDir ?
+                devInfo->minInputChannels : devInfo->minOutputChannels );
+    }
+    else
+        numHostChannels = parameters->channelCount;
+
+    PA_ENSURE( AlsaOpen( hostApi, parameters, streamDir, &pcm ) );
+
+    snd_pcm_hw_params_any( pcm, hwParams );
+
+    if( SetApproximateSampleRate( pcm, hwParams, sampleRate ) < 0 )
+    {
+        result = paInvalidSampleRate;
+        goto error;
+    }
+
+    if( snd_pcm_hw_params_set_channels( pcm, hwParams, numHostChannels ) < 0 )
+    {
+        result = paInvalidChannelCount;
+        goto error;
+    }
+
+    /* See if we can find a best possible match */
+    availableFormats = GetAvailableFormats( pcm );
+    PA_ENSURE( hostFormat = PaUtil_SelectClosestAvailableFormat( availableFormats, parameters->sampleFormat ) );
+    ENSURE_( snd_pcm_hw_params_set_format( pcm, hwParams, Pa2AlsaFormat( hostFormat ) ), paUnanticipatedHostError );
+
+    ENSURE_( snd_pcm_hw_params( pcm, hwParams ), paUnanticipatedHostError );
+
+end:
+    if( pcm )
+        snd_pcm_close( pcm );
+    return result;
+
+error:
+    goto end;
+}
+
+static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
+                                  const PaStreamParameters *inputParameters,
+                                  const PaStreamParameters *outputParameters,
+                                  double sampleRate )
+{
+    int inputChannelCount = 0, outputChannelCount = 0;
+    PaSampleFormat inputSampleFormat, outputSampleFormat;
+    PaError result = paFormatIsSupported;
+
+    if( inputParameters )
+    {
+        PA_ENSURE( ValidateParameters( inputParameters, hostApi, StreamDirection_In ) );
+
+        inputChannelCount = inputParameters->channelCount;
+        inputSampleFormat = inputParameters->sampleFormat;
+    }
+
+    if( outputParameters )
+    {
+        PA_ENSURE( ValidateParameters( outputParameters, hostApi, StreamDirection_Out ) );
+
+        outputChannelCount = outputParameters->channelCount;
+        outputSampleFormat = outputParameters->sampleFormat;
+    }
+
+    if( inputChannelCount )
+    {
+        if( (result = TestParameters( hostApi, inputParameters, sampleRate, StreamDirection_In ))
+                != paNoError )
+            goto error;
+    }
+    if ( outputChannelCount )
+    {
+        if( (result = TestParameters( hostApi, outputParameters, sampleRate, StreamDirection_Out ))
+                != paNoError )
+            goto error;
+    }
+
+    return paFormatIsSupported;
+
+error:
+    return result;
+}
+
+static PaError PaAlsaStreamComponent_Initialize( PaAlsaStreamComponent *self, PaAlsaHostApiRepresentation *alsaApi,
+        const PaStreamParameters *params, StreamDirection streamDir, int callbackMode )
+{
+    PaError result = paNoError;
+    PaSampleFormat userSampleFormat = params->sampleFormat, hostSampleFormat;
+    assert( params->channelCount > 0 );
+
+    /* Make sure things have an initial value */
+    memset( self, 0, sizeof (PaAlsaStreamComponent) );
+
+    if( NULL == params->hostApiSpecificStreamInfo )
+    {
+        const PaAlsaDeviceInfo *devInfo = GetDeviceInfo( &alsaApi->commonHostApiRep, params->device );
+        self->numHostChannels = PA_MAX( params->channelCount, StreamDirection_In == streamDir ? devInfo->minInputChannels
+                : devInfo->minOutputChannels );
+    }
+    else
+    {
+        /* We're blissfully unaware of the minimum channelCount */
+        self->numHostChannels = params->channelCount;
+    }
+
+    PA_ENSURE( AlsaOpen( &alsaApi->commonHostApiRep, params, streamDir, &self->pcm ) );
+    self->nfds = snd_pcm_poll_descriptors_count( self->pcm );
+    hostSampleFormat = PaUtil_SelectClosestAvailableFormat( GetAvailableFormats( self->pcm ), userSampleFormat );
+
+    self->hostSampleFormat = hostSampleFormat;
+    self->nativeFormat = Pa2AlsaFormat( hostSampleFormat );
+    self->hostInterleaved = self->userInterleaved = !(userSampleFormat & paNonInterleaved);
+    self->numUserChannels = params->channelCount;
+    self->streamDir = streamDir;
+
+    if( !callbackMode && !self->userInterleaved )
+    {
+        /* Pre-allocate non-interleaved user provided buffers */
+        PA_UNLESS( self->userBuffers = PaUtil_AllocateMemory( sizeof (void *) * self->numUserChannels ),
+                paInsufficientMemory );
+    }
+
+error:
+    return result;
+}
+
+static void PaAlsaStreamComponent_Terminate( PaAlsaStreamComponent *self )
+{
+    snd_pcm_close( self->pcm );
+    if( self->userBuffers )
+        PaUtil_FreeMemory( self->userBuffers );
+}
+
+/** Configure the associated ALSA pcm.
+ *
+ */
+static PaError PaAlsaStreamComponent_Configure( PaAlsaStreamComponent *self, const PaStreamParameters *params, unsigned long
+        framesPerHostBuffer, int primeBuffers, int callbackMode, double *sampleRate, PaTime *returnedLatency )
+{
+    /*
+    int numPeriods;
+
+    if( getenv("PA_NUMPERIODS") != NULL )
+        numPeriods = atoi( getenv("PA_NUMPERIODS") );
+    else
+        numPeriods = ( (latency * sampleRate) / *framesPerBuffer ) + 1;
+
+    PA_DEBUG(( "latency: %f, rate: %f, framesPerBuffer: %d\n", latency, sampleRate, *framesPerBuffer ));
+    if( numPeriods <= 1 )
+        numPeriods = 2;
+    */
+
+    /* Configuration consists of setting all of ALSA's parameters.
+     * These parameters come in two flavors: hardware parameters
+     * and software paramters.  Hardware parameters will affect
+     * the way the device is initialized, software parameters
+     * affect the way ALSA interacts with me, the user-level client.
+     */
+
+    snd_pcm_hw_params_t *hwParams;
+    snd_pcm_sw_params_t *swParams;
+    PaError result = paNoError;
+    snd_pcm_access_t accessMode, alternateAccessMode;
+    unsigned int numPeriods, minPeriods = 2;
+    int dir = 0;
+    snd_pcm_t *pcm = self->pcm;
+    PaTime latency = params->suggestedLatency;
+    double sr = *sampleRate;
+    *returnedLatency = -1.;
+
+    snd_pcm_hw_params_alloca( &hwParams );
+    snd_pcm_sw_params_alloca( &swParams );
+
+    self->framesPerBuffer = framesPerHostBuffer;
+
+    /* ... fill up the configuration space with all possibile
+     * combinations of parameters this device will accept */
+    ENSURE_( snd_pcm_hw_params_any( pcm, hwParams ), paUnanticipatedHostError );
+
+    ENSURE_( snd_pcm_hw_params_set_periods_integer( pcm, hwParams ), paUnanticipatedHostError );
+    ENSURE_( snd_pcm_hw_params_set_period_size_integer( pcm, hwParams ), paUnanticipatedHostError );
+
+    if( self->userInterleaved )
+    {
+        accessMode = SND_PCM_ACCESS_MMAP_INTERLEAVED;
+        alternateAccessMode = SND_PCM_ACCESS_MMAP_NONINTERLEAVED;
+    }
+    else
+    {
+        accessMode = SND_PCM_ACCESS_MMAP_NONINTERLEAVED;
+        alternateAccessMode = SND_PCM_ACCESS_MMAP_INTERLEAVED;
+    }
+
+    /* If requested access mode fails, try alternate mode */
+    if( snd_pcm_hw_params_set_access( pcm, hwParams, accessMode ) < 0 )
+    {
+        ENSURE_( snd_pcm_hw_params_set_access( pcm, hwParams, alternateAccessMode ), paUnanticipatedHostError );
+        /* Flip mode */
+        self->hostInterleaved = !self->userInterleaved;
+    }
+
+    ENSURE_( snd_pcm_hw_params_set_format( pcm, hwParams, self->nativeFormat ), paUnanticipatedHostError );
+
+    ENSURE_( SetApproximateSampleRate( pcm, hwParams, sr ), paInvalidSampleRate );
+    ENSURE_( GetExactSampleRate( hwParams, &sr ), paUnanticipatedHostError );
+    /* reject if there's no sample rate within 1% of the one requested */
+    if( (fabs( *sampleRate - sr ) / *sampleRate) > 0.01 )
+    {
+        PA_DEBUG(("%s: Wanted %f, closest sample rate was %d\n", __FUNCTION__, sampleRate, sr ));                 
+        PA_ENSURE( paInvalidSampleRate );
+    }
+
+    ENSURE_( snd_pcm_hw_params_set_channels( pcm, hwParams, self->numHostChannels ), paInvalidChannelCount );
+
+    /* I think there should be at least 2 periods (even though ALSA doesn't appear to enforce this) */
+    dir = 0;
+    ENSURE_( snd_pcm_hw_params_set_periods_min( pcm, hwParams, &minPeriods, &dir ), paUnanticipatedHostError );
+    dir = 0;
+    ENSURE_( snd_pcm_hw_params_set_period_size_near( pcm, hwParams, &self->framesPerBuffer, &dir ), paUnanticipatedHostError );
+    
+    /* Find an acceptable number of periods */
+    numPeriods = (latency * sr) / self->framesPerBuffer + 1;
+    dir = 0;
+    ENSURE_( snd_pcm_hw_params_set_periods_near( pcm, hwParams, &numPeriods, &dir ), paUnanticipatedHostError );
+    /* Minimum of periods should already be 2 */
+    PA_UNLESS( numPeriods >= 2, paInternalError );
+
+    /* Set the parameters! */
+    ENSURE_( snd_pcm_hw_params( pcm, hwParams ), paUnanticipatedHostError );
+    ENSURE_( snd_pcm_hw_params_get_buffer_size( hwParams, &self->bufferSize ), paUnanticipatedHostError );
+
+    /* Latency in seconds, one period is not counted as latency */
+    latency = (numPeriods - 1) * self->framesPerBuffer / sr;
+
+    /* Now software parameters... */
+    ENSURE_( snd_pcm_sw_params_current( pcm, swParams ), paUnanticipatedHostError );
+
+    ENSURE_( snd_pcm_sw_params_set_start_threshold( pcm, swParams, self->framesPerBuffer ), paUnanticipatedHostError );
+    ENSURE_( snd_pcm_sw_params_set_stop_threshold( pcm, swParams, self->bufferSize ), paUnanticipatedHostError );
+
+    /* Silence buffer in the case of underrun */
+    if( !primeBuffers ) /* XXX: Make sense? */
+    {
+        snd_pcm_uframes_t boundary;
+        ENSURE_( snd_pcm_sw_params_get_boundary( swParams, &boundary ), paUnanticipatedHostError );
+        ENSURE_( snd_pcm_sw_params_set_silence_threshold( pcm, swParams, 0 ), paUnanticipatedHostError );
+        ENSURE_( snd_pcm_sw_params_set_silence_size( pcm, swParams, boundary ), paUnanticipatedHostError );
+    }
+        
+    ENSURE_( snd_pcm_sw_params_set_avail_min( pcm, swParams, self->framesPerBuffer ), paUnanticipatedHostError );
+    ENSURE_( snd_pcm_sw_params_set_xfer_align( pcm, swParams, 1 ), paUnanticipatedHostError );
+    ENSURE_( snd_pcm_sw_params_set_tstamp_mode( pcm, swParams, SND_PCM_TSTAMP_MMAP ), paUnanticipatedHostError );
+
+    /* Set the parameters! */
+    ENSURE_( snd_pcm_sw_params( pcm, swParams ), paUnanticipatedHostError );
+
+    *sampleRate = sr;
+    *returnedLatency = latency;
+
+end:
+    return result;
+
+error:
+    goto end;   /* No particular action */
+}
+
+static PaError PaAlsaStream_Initialize( PaAlsaStream *self, PaAlsaHostApiRepresentation *alsaApi, const PaStreamParameters *inParams,
+        const PaStreamParameters *outParams, double sampleRate, unsigned long framesPerUserBuffer, PaStreamCallback callback,
+        PaStreamFlags streamFlags, void *userData )
+{
+    PaError result = paNoError;
+    assert( self );
+
+    memset( self, 0, sizeof (PaAlsaStream) );
+
+    if( NULL != callback )
+    {
+        PaUtil_InitializeStreamRepresentation( &self->streamRepresentation,
+                                               &alsaApi->callbackStreamInterface,
+                                               callback, userData );
+        self->callbackMode = 1;
+    }
+    else
+    {
+        PaUtil_InitializeStreamRepresentation( &self->streamRepresentation,
+                                               &alsaApi->blockingStreamInterface,
+                                               NULL, userData );
+    }
+
+    self->framesPerUserBuffer = framesPerUserBuffer;
+    self->neverDropInput = streamFlags & paNeverDropInput;
+    /* XXX: Ignore paPrimeOutputBuffersUsingStreamCallback untill buffer priming is fully supported in pa_process.c */
+    /*
+    if( outParams & streamFlags & paPrimeOutputBuffersUsingStreamCallback )
+        self->primeBuffers = 1;
+        */
+    memset( &self->capture, 0, sizeof (PaAlsaStreamComponent) );
+    memset( &self->playback, 0, sizeof (PaAlsaStreamComponent) );
+    if( inParams )
+        PA_ENSURE( PaAlsaStreamComponent_Initialize( &self->capture, alsaApi, inParams, StreamDirection_In, NULL != callback ) );
+    if( outParams )
+        PA_ENSURE( PaAlsaStreamComponent_Initialize( &self->playback, alsaApi, outParams, StreamDirection_Out, NULL != callback ) );
+
+    assert( self->capture.nfds || self->playback.nfds );
+
+    PA_UNLESS( self->pfds = (struct pollfd*)PaUtil_AllocateMemory( (self->capture.nfds +
+                    self->playback.nfds) * sizeof (struct pollfd) ), paInsufficientMemory );
+
+    PaUtil_InitializeCpuLoadMeasurer( &self->cpuLoadMeasurer, sampleRate );
+    InitializeThreading( &self->threading, &self->cpuLoadMeasurer );
+    ASSERT_CALL_( pthread_mutex_init( &self->stateMtx, NULL ), 0 );
+    ASSERT_CALL_( pthread_mutex_init( &self->startMtx, NULL ), 0 );
+    ASSERT_CALL_( pthread_cond_init( &self->startCond, NULL ), 0 );
+
+error:
+    return result;
+}
+
+/** Free resources associated with stream, and eventually stream itself.
+ *
+ * Frees allocated memory, and terminates individual StreamComponents.
+ */
+static void PaAlsaStream_Terminate( PaAlsaStream *self )
+{
+    assert( self );
+
+    if( self->capture.pcm )
+    {
+        PaAlsaStreamComponent_Terminate( &self->capture );
+    }
+    if( self->playback.pcm )
+    {
+        PaAlsaStreamComponent_Terminate( &self->playback );
+    }
+
+    PaUtil_FreeMemory( self->pfds );
+    ASSERT_CALL_( pthread_mutex_destroy( &self->stateMtx ), 0 );
+    ASSERT_CALL_( pthread_mutex_destroy( &self->startMtx ), 0 );
+    ASSERT_CALL_( pthread_cond_destroy( &self->startCond ), 0 );
+
+    PaUtil_FreeMemory( self );
+}
+
+/** Calculate polling timeout
+ *
+ * @param frames Time to wait
+ * @return Polling timeout in milliseconds
+ */
+static int CalculatePollTimeout( const PaAlsaStream *stream, unsigned long frames )
+{
+    assert( stream->streamRepresentation.streamInfo.sampleRate > 0.0 );
+    /* Period in msecs, rounded up */
+    return (int)ceil( 1000 * frames / stream->streamRepresentation.streamInfo.sampleRate );
+}
+
+/** Set up ALSA stream parameters.
+ *
+ */
+static PaError PaAlsaStream_Configure( PaAlsaStream *self, const PaStreamParameters *inParams, const PaStreamParameters
+        *outParams, double sampleRate, unsigned long framesPerHostBuffer, double *inputLatency, double *outputLatency,
+        unsigned long *maxHostBufferSize )
+{
+    PaError result = paNoError;
+    double realSr = sampleRate;
+
+    if( self->capture.pcm )
+        PA_ENSURE( PaAlsaStreamComponent_Configure( &self->capture, inParams, framesPerHostBuffer, self->primeBuffers,
+                    self->callbackMode, &realSr, inputLatency ) );
+    if( self->playback.pcm )
+        PA_ENSURE( PaAlsaStreamComponent_Configure( &self->playback, outParams, framesPerHostBuffer, self->primeBuffers,
+                    self->callbackMode, &realSr, outputLatency ) );
+
+    /* Should be exact now */
+    self->streamRepresentation.streamInfo.sampleRate = realSr;
+
+    /* this will cause the two streams to automatically start/stop/prepare in sync.
+     * We only need to execute these operations on one of the pair.
+     * A: We don't want to do this on a blocking stream.
+     */
+    if( self->callbackMode && self->capture.pcm && self->playback.pcm )
+    {
+        int err = snd_pcm_link( self->capture.pcm, self->playback.pcm );
+        if( err >= 0 )
+            self->pcmsSynced = 1;
+        else
+            PA_DEBUG(( "%s: Unable to sync pcms: %s\n", __FUNCTION__, snd_strerror( err ) ));
+    }
+
+    /* Frames per host buffer for the stream is set as a compromise between the two directions */
+    framesPerHostBuffer = PA_MIN( self->capture.pcm ? self->capture.framesPerBuffer : ULONG_MAX,
+            self->playback.pcm ? self->playback.framesPerBuffer : ULONG_MAX );
+    self->pollTimeout = CalculatePollTimeout( self, framesPerHostBuffer );    /* Period in msecs, rounded up */
+
+    *maxHostBufferSize = PA_MAX( self->capture.pcm ? self->capture.bufferSize : 0,
+            self->playback.pcm ? self->playback.bufferSize : 0 );
+
+    /* Time before watchdog unthrottles realtime thread == 1/4 of period time in msecs */
+    self->threading.throttledSleepTime = (unsigned long) (framesPerHostBuffer / sampleRate / 4 * 1000);
+
+    if( self->callbackMode )
+    {
+        /* If the user expects a certain number of frames per callback we will either have to rely on block adaption
+         * (framesPerHostBuffer is not an integer multiple of framesPerBuffer) or we can simply align the number
+         * of host buffer frames with what the user specified */
+        if( self->framesPerUserBuffer != paFramesPerBufferUnspecified )
+        {
+            /* self->alignFrames = 1; */
+
+            /* Unless the ratio between number of host and user buffer frames is an integer we will have to rely
+             * on block adaption */
+        /*
+            if( framesPerHostBuffer % framesPerBuffer != 0 || (self->capture.pcm && self->playback.pcm &&
+                        self->capture.framesPerBuffer != self->playback.framesPerBuffer) )
+                self->useBlockAdaption = 1;
+            else
+                self->alignFrames = 1;
+        */
+        }
+    }
+
+error:
+    return result;
+}
+
+/* We need to determine how many frames per host buffer to use.  Our
+ * goals are to provide the best possible performance, but also to
+ * most closely honor the requested latency settings.  Therefore this
+ * decision is based on:
+ *
+ *   - the period sizes that playback and/or capture support.  The
+ *     host buffer size has to be one of these.
+ *   - the number of periods that playback and/or capture support.
+ *
+ * We want to make period_size*(num_periods-1) to be as close as possible
+ * to latency*rate for both playback and capture.
+ *
+ * This is one of those blocks of code that will just take a lot of
+ * refinement to be any good.
+ *
+ * In the full-duplex case it is possible that the routine was unable
+ * to find a number of frames per buffer acceptable to both devices
+ * TODO: Implement an algorithm to find the value closest to acceptance
+ * by both devices, to minimize difference between period sizes?
+ */
+static PaError DetermineFramesPerBuffer( const PaAlsaStream *stream, double sampleRate, const PaStreamParameters *inputParameters,
+        const PaStreamParameters *outputParameters, unsigned long *determinedFrames, const PaUtilHostApiRepresentation *hostApi )
+{
+    PaError result = paNoError;
+    unsigned long framesPerBuffer = 0;
+    int numHostInputChannels = 0, numHostOutputChannels = 0;
+
+    /* XXX: Clean this up */
+    if( stream->capture.pcm )
+    {
+        const PaAlsaDeviceInfo *devInfo = GetDeviceInfo( hostApi, inputParameters->device );
+        numHostInputChannels = PA_MAX( inputParameters->channelCount, devInfo->minInputChannels );
+    }
+    if( stream->playback.pcm )
+    {
+        const PaAlsaDeviceInfo *devInfo = GetDeviceInfo( hostApi, outputParameters->device );
+        numHostOutputChannels = PA_MAX( outputParameters->channelCount, devInfo->minOutputChannels );
+    }
+
+    if( stream->capture.pcm && stream->playback.pcm )
+    {
+        snd_pcm_uframes_t desiredLatency, e;
+        snd_pcm_uframes_t minPeriodSize, minPlayback, minCapture, maxPeriodSize, maxPlayback, maxCapture,
+                          optimalPeriodSize, periodSize;
+        int dir = 0;
+        unsigned int minPeriods = 2;
+
+        snd_pcm_t *pcm;
+        snd_pcm_hw_params_t *hwParamsPlayback, *hwParamsCapture;
+
+        snd_pcm_hw_params_alloca( &hwParamsPlayback );
+        snd_pcm_hw_params_alloca( &hwParamsCapture );
+
+        /* Come up with a common desired latency */
+        pcm = stream->playback.pcm;
+        snd_pcm_hw_params_any( pcm, hwParamsPlayback );
+        ENSURE_( SetApproximateSampleRate( pcm, hwParamsPlayback, sampleRate ), paInvalidSampleRate );
+        ENSURE_( snd_pcm_hw_params_set_channels( pcm, hwParamsPlayback, numHostOutputChannels ),
+                paBadIODeviceCombination );
+
+        ENSURE_( snd_pcm_hw_params_set_period_size_integer( pcm, hwParamsPlayback ), paUnanticipatedHostError );
+        ENSURE_( snd_pcm_hw_params_set_periods_integer( pcm, hwParamsPlayback ), paUnanticipatedHostError );
+        ENSURE_( snd_pcm_hw_params_set_periods_min( pcm, hwParamsPlayback, &minPeriods, &dir ), paUnanticipatedHostError );
+        ENSURE_( snd_pcm_hw_params_get_period_size_min( hwParamsPlayback, &minPlayback, &dir ), paUnanticipatedHostError );
+        ENSURE_( snd_pcm_hw_params_get_period_size_max( hwParamsPlayback, &maxPlayback, &dir ), paUnanticipatedHostError );
+
+        pcm = stream->capture.pcm;
+        ENSURE_( snd_pcm_hw_params_any( pcm, hwParamsCapture ), paUnanticipatedHostError );
+        ENSURE_( SetApproximateSampleRate( pcm, hwParamsCapture, sampleRate ), paBadIODeviceCombination );
+        ENSURE_( snd_pcm_hw_params_set_channels( pcm, hwParamsCapture, numHostInputChannels ),
+                paBadIODeviceCombination );
+
+        ENSURE_( snd_pcm_hw_params_set_period_size_integer( pcm, hwParamsCapture ), paUnanticipatedHostError );
+        ENSURE_( snd_pcm_hw_params_set_periods_integer( pcm, hwParamsCapture ), paUnanticipatedHostError );
+        ENSURE_( snd_pcm_hw_params_set_periods_min( pcm, hwParamsCapture, &minPeriods, &dir ), paUnanticipatedHostError );
+        ENSURE_( snd_pcm_hw_params_get_period_size_min( hwParamsCapture, &minCapture, &dir ), paUnanticipatedHostError );
+        ENSURE_( snd_pcm_hw_params_get_period_size_max( hwParamsCapture, &maxCapture, &dir ), paUnanticipatedHostError );
+
+        minPeriodSize = PA_MAX( minPlayback, minCapture );
+        maxPeriodSize = PA_MIN( maxPlayback, maxCapture );
+
+        desiredLatency = (snd_pcm_uframes_t) (PA_MIN( outputParameters->suggestedLatency, inputParameters->suggestedLatency )
+                * sampleRate);
+        /* Clamp desiredLatency */
+        {
+            snd_pcm_uframes_t tmp, maxBufferSize = ULONG_MAX;
+            ENSURE_( snd_pcm_hw_params_get_buffer_size_max( hwParamsPlayback, &maxBufferSize ), paUnanticipatedHostError );
+            ENSURE_( snd_pcm_hw_params_get_buffer_size_max( hwParamsCapture, &tmp ), paUnanticipatedHostError );
+            maxBufferSize = PA_MIN( maxBufferSize, tmp );
+
+            desiredLatency = PA_MIN( desiredLatency, maxBufferSize );
+        }
+
+        /* Find the closest power of 2 */
+        e = ilogb( minPeriodSize );
+        if( minPeriodSize & (minPeriodSize - 1) )
+            e += 1;
+        periodSize = (snd_pcm_uframes_t) pow( 2, e );
+
+        while( periodSize <= maxPeriodSize )
+        {
+            if( snd_pcm_hw_params_test_period_size( stream->playback.pcm, hwParamsPlayback, periodSize, 0 ) >= 0 &&
+                    snd_pcm_hw_params_test_period_size( stream->capture.pcm, hwParamsCapture, periodSize, 0 ) >= 0 )
+                break;  /* Ok! */
+
+            periodSize *= 2;
+        }
+
+        /* 4 periods considered optimal */
+        optimalPeriodSize = PA_MAX( desiredLatency / 4, minPeriodSize );
+        optimalPeriodSize = PA_MIN( optimalPeriodSize, maxPeriodSize );
+
+        /* Find the closest power of 2 */
+        e = ilogb( optimalPeriodSize );
+        if( optimalPeriodSize & (optimalPeriodSize - 1) )
+            e += 1;
+        optimalPeriodSize = (snd_pcm_uframes_t) pow( 2, e );
+
+        while( optimalPeriodSize >= periodSize )
+        {
+            pcm = stream->playback.pcm;
+            if( snd_pcm_hw_params_test_period_size( pcm, hwParamsPlayback, optimalPeriodSize, 0 ) < 0 )
+                continue;
+
+            pcm = stream->capture.pcm;
+            if( snd_pcm_hw_params_test_period_size( pcm, hwParamsCapture, optimalPeriodSize, 0 ) >= 0 )
+                break;
+
+            optimalPeriodSize /= 2;
+        }
+
+        if( optimalPeriodSize > periodSize )
+            periodSize = optimalPeriodSize;
+
+        if( periodSize <= maxPeriodSize )
+        {
+            /* Looks good */
+            framesPerBuffer = periodSize;
+        }
+        else
+        {
+            /* Unable to find a common period size, oh well */
+            optimalPeriodSize = PA_MAX( desiredLatency / 4, minPeriodSize );
+            optimalPeriodSize = PA_MIN( optimalPeriodSize, maxPeriodSize );
+
+            /* ConfigureStream should find individual period sizes acceptable for each device */
+            framesPerBuffer = optimalPeriodSize;
+            /* PA_ENSURE( paBadIODeviceCombination ); */
+        }
+    }
+    else    /* half-duplex is a slightly simpler case */
+    {
+        unsigned long bufferSize, channels;
+        snd_pcm_t *pcm;
+        snd_pcm_hw_params_t *hwParams;
+
+        snd_pcm_hw_params_alloca( &hwParams );
+
+        if( stream->capture.pcm )
+        {
+            pcm = stream->capture.pcm;
+            bufferSize = inputParameters->suggestedLatency * sampleRate;
+            channels = numHostInputChannels;
+        }
+        else
+        {
+            pcm = stream->playback.pcm;
+            bufferSize = outputParameters->suggestedLatency * sampleRate;
+            channels = numHostOutputChannels;
+        }
+
+        ENSURE_( snd_pcm_hw_params_any( pcm, hwParams ), paUnanticipatedHostError );
+        ENSURE_( SetApproximateSampleRate( pcm, hwParams, sampleRate ), paInvalidSampleRate );
+        ENSURE_( snd_pcm_hw_params_set_channels( pcm, hwParams, channels ), paBadIODeviceCombination );
+
+        ENSURE_( snd_pcm_hw_params_set_period_size_integer( pcm, hwParams ), paUnanticipatedHostError );
+        ENSURE_( snd_pcm_hw_params_set_periods_integer( pcm, hwParams ), paUnanticipatedHostError );
+
+        /* Using 5 as a base number of periods, we try to approximate the suggested latency (+1 period),
+           finding a combination of period/buffer size which best fits these constraints */
+        framesPerBuffer = bufferSize / 4;
+        bufferSize += framesPerBuffer;   /* One period doesn't count as latency */
+        ENSURE_( snd_pcm_hw_params_set_buffer_size_near( pcm, hwParams, &bufferSize ), paUnanticipatedHostError );
+        ENSURE_( snd_pcm_hw_params_set_period_size_near( pcm, hwParams, &framesPerBuffer, NULL ), paUnanticipatedHostError );
+    }
+
+    PA_UNLESS( framesPerBuffer != 0, paInternalError );
+    *determinedFrames = framesPerBuffer;
+
+error:
+    return result;
+}
+
+static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
+                           PaStream** s,
+                           const PaStreamParameters *inputParameters,
+                           const PaStreamParameters *outputParameters,
+                           double sampleRate,
+                           unsigned long framesPerBuffer,
+                           PaStreamFlags streamFlags,
+                           PaStreamCallback *callback,
+                           void *userData )
+{
+    PaError result = paNoError;
+    PaAlsaHostApiRepresentation *alsaHostApi = (PaAlsaHostApiRepresentation*)hostApi;
+    PaAlsaStream *stream = NULL;
+    PaSampleFormat hostInputSampleFormat = 0, hostOutputSampleFormat = 0;
+    PaSampleFormat inputSampleFormat = 0, outputSampleFormat = 0;
+    int numInputChannels = 0, numOutputChannels = 0;
+    PaTime inputLatency, outputLatency;
+    unsigned long framesPerHostBuffer;
+    PaUtilHostBufferSizeMode hostBufferSizeMode = paUtilBoundedHostBufferSize;
+    unsigned long maxHostBufferSize;    /* Upper bound of host buffer size */
+
+    if( (streamFlags & paPlatformSpecificFlags) != 0 )
+        return paInvalidFlag;
+
+    if( inputParameters )
+    {
+        PA_ENSURE( ValidateParameters( inputParameters, hostApi, StreamDirection_In ) );
+
+        numInputChannels = inputParameters->channelCount;
+        inputSampleFormat = inputParameters->sampleFormat;
+    }
+    if( outputParameters )
+    {
+        PA_ENSURE( ValidateParameters( outputParameters, hostApi, StreamDirection_Out ) );
+
+        numOutputChannels = outputParameters->channelCount;
+        outputSampleFormat = outputParameters->sampleFormat;
+    }
+
+    /* XXX: Why do we support this anyway? */
+    if( framesPerBuffer == paFramesPerBufferUnspecified && getenv( "PA_ALSA_PERIODSIZE" ) != NULL )
+    {
+        PA_DEBUG(( "%s: Getting framesPerBuffer from environment\n", __FUNCTION__ ));
+        framesPerBuffer = atoi( getenv("PA_ALSA_PERIODSIZE") );
+    }
+    framesPerHostBuffer = framesPerBuffer;
+
+    PA_UNLESS( stream = (PaAlsaStream*)PaUtil_AllocateMemory( sizeof(PaAlsaStream) ), paInsufficientMemory );
+    PA_ENSURE( PaAlsaStream_Initialize( stream, alsaHostApi, inputParameters, outputParameters, sampleRate,
+                framesPerBuffer, callback, streamFlags, userData ) );
+
+    /* If the number of frames per buffer is unspecified, we have to come up with
+     * one. This is both a blessing and a curse: a blessing because we can optimize
+     * the number to best meet the requirements, but a curse because that's really
+     * hard to do well. For this reason we also support an interface where the user
+     * specifies these by setting environment variables. */
+    if( framesPerBuffer == paFramesPerBufferUnspecified )
+    {
+        PA_ENSURE( DetermineFramesPerBuffer( stream, sampleRate, inputParameters, outputParameters, &framesPerHostBuffer,
+                    hostApi ) );
+    }
+
+    PA_ENSURE( PaAlsaStream_Configure( stream, inputParameters, outputParameters, sampleRate, framesPerHostBuffer,
+                &inputLatency, &outputLatency, &maxHostBufferSize ) );
+    hostInputSampleFormat = stream->capture.hostSampleFormat;
+    hostOutputSampleFormat = stream->playback.hostSampleFormat;
+
+    if( framesPerHostBuffer != framesPerBuffer )
+    {
+        PA_DEBUG(( "%s: Number of frames per user and host buffer differs\n", __FUNCTION__ ));
+    }
+
+    PA_ENSURE( PaUtil_InitializeBufferProcessor( &stream->bufferProcessor,
+                    numInputChannels, inputSampleFormat, hostInputSampleFormat,
+                    numOutputChannels, outputSampleFormat, hostOutputSampleFormat,
+                    sampleRate, streamFlags, framesPerBuffer, maxHostBufferSize,
+                    hostBufferSizeMode, callback, userData ) );
+
+    /* Ok, buffer processor is initialized, now we can deduce it's latency */
+    if( numInputChannels > 0 )
+        stream->streamRepresentation.streamInfo.inputLatency = inputLatency + PaUtil_GetBufferProcessorInputLatency(
+                &stream->bufferProcessor );
+    if( numOutputChannels > 0 )
+        stream->streamRepresentation.streamInfo.outputLatency = outputLatency + PaUtil_GetBufferProcessorOutputLatency(
+                &stream->bufferProcessor );
+
+    *s = (PaStream*)stream;
+
+    return result;
+
+error:
+    if( stream )
+        PaAlsaStream_Terminate( stream );
+
+    return result;
+}
+
+static PaError CloseStream( PaStream* s )
+{
+    PaError result = paNoError;
+    PaAlsaStream *stream = (PaAlsaStream*)s;
+
+    PaUtil_TerminateBufferProcessor( &stream->bufferProcessor );
+    PaUtil_TerminateStreamRepresentation( &stream->streamRepresentation );
+
+    PaAlsaStream_Terminate( stream );
+
+    return result;
+}
+
+static void SilenceBuffer( PaAlsaStream *stream )
+{
+    const snd_pcm_channel_area_t *areas;
+    snd_pcm_uframes_t frames = (snd_pcm_uframes_t)snd_pcm_avail_update( stream->playback.pcm ), offset;
+
+    snd_pcm_mmap_begin( stream->playback.pcm, &areas, &offset, &frames );
+    snd_pcm_areas_silence( areas, offset, stream->playback.numHostChannels, frames, stream->playback.nativeFormat );
+    snd_pcm_mmap_commit( stream->playback.pcm, offset, frames );
+}
+
+/** Start/prepare pcm(s) for streaming.
+ *
+ * Depending on wether the stream is in callback or blocking mode, we will respectively start or simply
+ * prepare the playback pcm. If the buffer has _not_ been primed, we will in callback mode prepare and
+ * silence the buffer before starting playback. In blocking mode we simply prepare, as the playback will
+ * be started automatically as the user writes to output. 
+ *
+ * The capture pcm, however, will simply be prepared and started.
+ *
+ * PaAlsaStream::startMtx makes sure access is synchronized (useful in callback mode)
+ */
+static PaError AlsaStart( PaAlsaStream *stream, int priming )
+{
+    PaError result = paNoError;
+
+    if( stream->playback.pcm )
+    {
+        if( stream->callbackMode )
+        {
+            if( !priming )
+            {
+                /* Buffer isn't primed, so prepare and silence */
+                ENSURE_( snd_pcm_prepare( stream->playback.pcm ), paUnanticipatedHostError );
+                SilenceBuffer( stream );
+            }
+            ENSURE_( snd_pcm_start( stream->playback.pcm ), paUnanticipatedHostError );
+        }
+        else
+            ENSURE_( snd_pcm_prepare( stream->playback.pcm ), paUnanticipatedHostError );
+    }
+    if( stream->capture.pcm && !stream->pcmsSynced )
+    {
+        ENSURE_( snd_pcm_prepare( stream->capture.pcm ), paUnanticipatedHostError );
+        /* For a blocking stream we want to start capture as well, since nothing will happen otherwise */
+        ENSURE_( snd_pcm_start( stream->capture.pcm ), paUnanticipatedHostError );
+    }
+
+end:
+    return result;
+error:
+    goto end;
+}
+
+/** Utility function for determining if pcms are in running state.
+ *
+ */
+static int IsRunning( PaAlsaStream *stream )
+{
+    int result = 0;
+
+    ASSERT_CALL_( pthread_mutex_lock( &stream->stateMtx ), 0 ); /* Synchronize access to pcm state */
+    if( stream->capture.pcm )
+    {
+        snd_pcm_state_t capture_state = snd_pcm_state( stream->capture.pcm );
+
+        if( capture_state == SND_PCM_STATE_RUNNING || capture_state == SND_PCM_STATE_XRUN
+                || capture_state == SND_PCM_STATE_DRAINING )
+        {
+            result = 1;
+            goto end;
+        }
+    }
+
+    if( stream->playback.pcm )
+    {
+        snd_pcm_state_t playback_state = snd_pcm_state( stream->playback.pcm );
+
+        if( playback_state == SND_PCM_STATE_RUNNING || playback_state == SND_PCM_STATE_XRUN
+                || playback_state == SND_PCM_STATE_DRAINING )
+        {
+            result = 1;
+            goto end;
+        }
+    }
+
+end:
+    ASSERT_CALL_( pthread_mutex_unlock( &stream->stateMtx ), 0 );
+
+    return result;
+}
+
+static PaError StartStream( PaStream *s )
+{
+    PaError result = paNoError;
+    PaAlsaStream *stream = (PaAlsaStream*)s;
+    int streamStarted = 0;  /* So we can know wether we need to take the stream down */
+
+    /* Ready the processor */
+    PaUtil_ResetBufferProcessor( &stream->bufferProcessor );
+
+    /* Set now, so we can test for activity further down */
+    stream->isActive = 1;
+
+    if( stream->callbackMode )
+    {
+        int res = 0;
+        PaTime pt = PaUtil_GetTime();
+        struct timespec ts;
+
+        PA_ENSURE( CreateCallbackThread( &stream->threading, &CallbackThreadFunc, stream ) );
+        streamStarted = 1;
+
+        /* Wait for stream to be started */
+        ts.tv_sec = (time_t) floor( pt + 1 );
+        ts.tv_nsec = (long) ((pt - floor( pt )) * 1000000000);
+
+        /* Since we'll be holding a lock on the startMtx (when not waiting on the condition), IsRunning won't be checking
+         * stream state at the same time as the callback thread affects it. We also check IsStreamActive, in the unlikely
+         * case the callback thread exits in the meantime (the stream will be considered inactive after the thread exits) */
+        ASSERT_CALL_( pthread_mutex_lock( &stream->startMtx ), 0 );
+
+        /* Due to possible spurious wakeups, we enclose in a loop */
+        while( !IsRunning( stream ) && IsStreamActive( s ) && !res )
+        {
+            res = pthread_cond_timedwait( &stream->startCond, &stream->startMtx, &ts );
+        }
+        ASSERT_CALL_( pthread_mutex_unlock( &stream->startMtx ), 0 );
+
+        PA_UNLESS( !res || res == ETIMEDOUT, paInternalError );
+        PA_DEBUG(( "%s: Waited for %g seconds for stream to start\n", __FUNCTION__, PaUtil_GetTime() - pt ));
+
+        if( res == ETIMEDOUT )
+        {
+            PA_ENSURE( paTimedOut );
+        }
+    }
+    else
+    {
+        PA_ENSURE( AlsaStart( stream, 0 ) );
+        streamStarted = 1;
+    }
+
+end:
+    return result;
+error:
+    if( streamStarted )
+        AbortStream( stream );
+    stream->isActive = 0;
+    
+    goto end;
+}
+
+static PaError AlsaStop( PaAlsaStream *stream, int abort )
+{
+    PaError result = paNoError;
+
+    if( abort )
+    {
+        if( stream->playback.pcm )
+            ENSURE_( snd_pcm_drop( stream->playback.pcm ), paUnanticipatedHostError );
+        if( stream->capture.pcm && !stream->pcmsSynced )
+            ENSURE_( snd_pcm_drop( stream->capture.pcm ), paUnanticipatedHostError );
+
+        PA_DEBUG(( "Dropped frames\n" ));
+    }
+    else
+    {
+        if( stream->playback.pcm )
+            ENSURE_( snd_pcm_drain( stream->playback.pcm ), paUnanticipatedHostError );
+        if( stream->capture.pcm && !stream->pcmsSynced )
+            ENSURE_( snd_pcm_drain( stream->capture.pcm ), paUnanticipatedHostError );
+    }
+
+end:
+    return result;
+error:
+    goto end;
+}
+
+/** Stop or abort stream.
+ *
+ * If a stream is in callback mode we will have to inspect wether the background thread has
+ * finished, or we will have to take it out. In either case we join the thread before
+ * returning. In blocking mode, we simply tell ALSA to stop abruptly (abort) or finish
+ * buffers (drain)
+ *
+ * Stream will be considered inactive (!PaAlsaStream::isActive) after a call to this function
+ */
+static PaError RealStop( PaAlsaStream *stream, int abort )
+{
+    PaError result = paNoError;
+
+    /* First deal with the callback thread, cancelling and/or joining
+     * it if necessary
+     */
+    if( stream->callbackMode )
+    {
+        PaError threadRes, watchdogRes;
+        stream->callbackAbort = abort;
+
+        if( !abort )
+        {
+            PA_DEBUG(( "Stopping callback\n" ));
+            stream->callbackStop = 1;
+        }
+        PA_ENSURE( KillCallbackThread( &stream->threading, !abort, &threadRes, &watchdogRes ) );
+        if( threadRes != paNoError )
+            PA_DEBUG(( "Callback thread returned: %d\n", threadRes ));
+        if( watchdogRes != paNoError )
+            PA_DEBUG(( "Watchdog thread returned: %d\n", watchdogRes ));
+
+        stream->callbackStop = 0;   /* The deed is done */
+        stream->callback_finished = 0;
+    }
+    else
+    {
+        PA_ENSURE( AlsaStop( stream, abort ) );
+    }
+
+    stream->isActive = 0;
+
+end:
+    return result;
+
+error:
+    goto end;
+}
+
+static PaError StopStream( PaStream *s )
+{
+    return RealStop( (PaAlsaStream *) s, 0 );
+}
+
+static PaError AbortStream( PaStream *s )
+{
+    return RealStop( (PaAlsaStream * ) s, 1 );
+}
+
+/** The stream is considered stopped before StartStream, or AFTER a call to Abort/StopStream (callback
+ * returning !paContinue is not considered)
+ *
+ */
+static PaError IsStreamStopped( PaStream *s )
+{
+    PaAlsaStream *stream = (PaAlsaStream *)s;
+
+    /* callback_finished indicates we need to join callback thread (ie. in Abort/StopStream) */
+    return !IsStreamActive( s ) && !stream->callback_finished;
+}
+
+static PaError IsStreamActive( PaStream *s )
+{
+    PaAlsaStream *stream = (PaAlsaStream*)s;
+    return stream->isActive;
+}
+
+static PaTime GetStreamTime( PaStream *s )
+{
+    PaAlsaStream *stream = (PaAlsaStream*)s;
+
+    snd_timestamp_t timestamp;
+    snd_pcm_status_t *status;
+    snd_pcm_status_alloca( &status );
+
+    /* TODO: what if we have both?  does it really matter? */
+
+    /* TODO: if running in callback mode, this will mean
+     * libasound routines are being called from multiple threads.
+     * need to verify that libasound is thread-safe. */
+
+    if( stream->capture.pcm )
+    {
+        snd_pcm_status( stream->capture.pcm, status );
+    }
+    else if( stream->playback.pcm )
+    {
+        snd_pcm_status( stream->playback.pcm, status );
+    }
+
+    snd_pcm_status_get_tstamp( status, &timestamp );
+    return timestamp.tv_sec + (PaTime)timestamp.tv_usec / 1000000.0;
+}
+
+static double GetStreamCpuLoad( PaStream* s )
+{
+    PaAlsaStream *stream = (PaAlsaStream*)s;
+
+    return PaUtil_GetCpuLoad( &stream->cpuLoadMeasurer );
+}
+
+static int SetApproximateSampleRate( snd_pcm_t *pcm, snd_pcm_hw_params_t *hwParams, double sampleRate )
+{
+    unsigned long approx = (unsigned long) sampleRate;
+    int dir = 0;
+    double fraction = sampleRate - approx;
+
+    assert( pcm && hwParams );
+
+    if( fraction > 0.0 )
+    {
+        if( fraction > 0.5 )
+        {
+            ++approx;
+            dir = -1;
+        }
+        else
+            dir = 1;
+    }
+
+    return snd_pcm_hw_params_set_rate( pcm, hwParams, approx, dir );
+}
+
+/* Return exact sample rate in param sampleRate */
+static int GetExactSampleRate( snd_pcm_hw_params_t *hwParams, double *sampleRate )
+{
+    unsigned int num, den;
+    int err; 
+
+    assert( hwParams );
+
+    err = snd_pcm_hw_params_get_rate_numden( hwParams, &num, &den );
+    *sampleRate = (double) num / den;
+
+    return err;
+}
+
+/* Utility functions for blocking/callback interfaces */
+
+/* Atomic restart of stream (we don't want the intermediate state visible) */
+static PaError AlsaRestart( PaAlsaStream *stream )
+{
+    PaError result = paNoError;
+
+    ASSERT_CALL_( pthread_mutex_lock( &stream->stateMtx ), 0 );
+    PA_ENSURE( AlsaStop( stream, 0 ) );
+    PA_ENSURE( AlsaStart( stream, 0 ) );
+
+    PA_DEBUG(( "%s: Restarted audio\n", __FUNCTION__ ));
+
+error:
+    ASSERT_CALL_( pthread_mutex_unlock( &stream->stateMtx ), 0 );
+    return result;
+}
+
+/** Recover from xrun state.
+ *
+ */
+static PaError PaAlsaStream_HandleXrun( PaAlsaStream *self )
+{
+    PaError result = paNoError;
+    snd_pcm_status_t *st;
+    PaTime now = PaUtil_GetTime();
+    snd_timestamp_t t;
+
+    snd_pcm_status_alloca( &st );
+
+    if( self->playback.pcm )
+    {
+        snd_pcm_status( self->playback.pcm, st );
+        if( snd_pcm_status_get_state( st ) == SND_PCM_STATE_XRUN )
+        {
+            snd_pcm_status_get_trigger_tstamp( st, &t );
+            self->underrun = now * 1000 - ((PaTime) t.tv_sec * 1000 + (PaTime) t.tv_usec / 1000);
+        }
+    }
+    if( self->capture.pcm )
+    {
+        snd_pcm_status( self->capture.pcm, st );
+        if( snd_pcm_status_get_state( st ) == SND_PCM_STATE_XRUN )
+        {
+            snd_pcm_status_get_trigger_tstamp( st, &t );
+            self->overrun = now * 1000 - ((PaTime) t.tv_sec * 1000 + (PaTime) t.tv_usec / 1000);
+        }
+    }
+
+    PA_ENSURE( AlsaRestart( self ) );
+
+end:
+    return result;
+error:
+    goto end;
+}
+
+/** Decide if we should continue polling for specified direction, eventually adjust the poll timeout.
+ * 
+ */
+static PaError ContinuePoll( const PaAlsaStream *stream, StreamDirection streamDir, int *pollTimeout, int *continuePoll )
+{
+    PaError result = paNoError;
+    snd_pcm_sframes_t delay, margin;
+    int err;
+    const PaAlsaStreamComponent *component = NULL, *otherComponent = NULL;
+
+    *continuePoll = 1;
+
+    if( StreamDirection_In == streamDir )
+    {
+        component = &stream->capture;
+        otherComponent = &stream->playback;
+    }
+    else
+    {
+        component = &stream->playback;
+        otherComponent = &stream->capture;
+    }
+
+    /* ALSA docs say that negative delay should indicate xrun, but in my experience snd_pcm_delay returns -EPIPE */
+    if( (err = snd_pcm_delay( otherComponent->pcm, &delay )) < 0 )
+    {
+        if( err == -EPIPE )
+        {
+            /* Xrun */
+            *continuePoll = 0;
+            goto error;
+        }
+
+        ENSURE_( err, paUnanticipatedHostError );
+    }
+
+    if( StreamDirection_Out == streamDir )
+    {
+        /* Number of eligible frames before capture overrun */
+        delay = otherComponent->bufferSize - delay;
+    }
+    margin = delay - otherComponent->framesPerBuffer / 2;
+
+    if( margin < 0 )
+    {
+        PA_DEBUG(( "%s: Stopping poll for %s\n", __FUNCTION__, StreamDirection_In == streamDir ? "capture" : "playback" ));
+        *continuePoll = 0;
+    }
+    else if( margin < otherComponent->framesPerBuffer )
+    {
+        *pollTimeout = CalculatePollTimeout( stream, margin );
+        PA_DEBUG(( "%s: Trying to poll again for %s frames, pollTimeout: %d\n",
+                    __FUNCTION__, StreamDirection_In == streamDir ? "capture" : "playback", *pollTimeout ));
+    }
+
+error:
+    return result;
+}
+
+/* Callback interface */
+
+static void OnExit( void *data )
+{
+    PaAlsaStream *stream = (PaAlsaStream *) data;
+
+    assert( data );
+
+    PaUtil_ResetCpuLoadMeasurer( &stream->cpuLoadMeasurer );
+
+    stream->callback_finished = 1;  /* Let the outside world know stream was stopped in callback */
+    AlsaStop( stream, stream->callbackAbort );
+    stream->callbackAbort = 0;      /* Clear state */
+    
+    PA_DEBUG(( "OnExit: Stoppage\n" ));
+
+    /* Eventually notify user all buffers have played */
+    if( stream->streamRepresentation.streamFinishedCallback )
+        stream->streamRepresentation.streamFinishedCallback( stream->streamRepresentation.userData );
+    stream->isActive = 0;
+}
+
+static void CalculateTimeInfo( PaAlsaStream *stream, PaStreamCallbackTimeInfo *timeInfo )
+{
+    snd_pcm_status_t *capture_status, *playback_status;
+    snd_timestamp_t capture_timestamp, playback_timestamp;
+    PaTime capture_time = 0., playback_time = 0.;
+
+    snd_pcm_status_alloca( &capture_status );
+    snd_pcm_status_alloca( &playback_status );
+
+    if( stream->capture.pcm )
+    {
+        snd_pcm_sframes_t capture_delay;
+
+        snd_pcm_status( stream->capture.pcm, capture_status );
+        snd_pcm_status_get_tstamp( capture_status, &capture_timestamp );
+
+        capture_time = capture_timestamp.tv_sec +
+            ((PaTime)capture_timestamp.tv_usec / 1000000.0);
+        timeInfo->currentTime = capture_time;
+
+        capture_delay = snd_pcm_status_get_delay( capture_status );
+        timeInfo->inputBufferAdcTime = timeInfo->currentTime -
+            (PaTime)capture_delay / stream->streamRepresentation.streamInfo.sampleRate;
+    }
+    if( stream->playback.pcm )
+    {
+        snd_pcm_sframes_t playback_delay;
+
+        snd_pcm_status( stream->playback.pcm, playback_status );
+        snd_pcm_status_get_tstamp( playback_status, &playback_timestamp );
+
+        playback_time = playback_timestamp.tv_sec +
+            ((PaTime)playback_timestamp.tv_usec / 1000000.0);
+
+        if( stream->capture.pcm ) /* Full duplex */
+        {
+            /* Hmm, we have both a playback and a capture timestamp.
+             * Hopefully they are the same... */
+            if( fabs( capture_time - playback_time ) > 0.01 )
+                PA_DEBUG(("Capture time and playback time differ by %f\n", fabs(capture_time-playback_time)));
+        }
+        else
+            timeInfo->currentTime = playback_time;
+
+        playback_delay = snd_pcm_status_get_delay( playback_status );
+        timeInfo->outputBufferDacTime = timeInfo->currentTime +
+            (PaTime)playback_delay / stream->streamRepresentation.streamInfo.sampleRate;
+    }
+}
+
+/** Called after buffer processing is finished.
+ *
+ * A number of mmapped frames is committed, it is possible that an xrun has occurred in the meantime.
+ *
+ * @param numFrames The number of frames that has been processed
+ * @param xrun Return whether an xrun has occurred
+ */
+static PaError PaAlsaStreamComponent_EndProcessing( PaAlsaStreamComponent *self, unsigned long numFrames, int *xrun )
+{
+    PaError result = paNoError;
+    int res;
+
+    /* @concern FullDuplex It is possible that only one direction is marked ready after polling, and processed
+     * afterwards
+     */
+    if( !self->ready )
+        goto end;
+
+    res = snd_pcm_mmap_commit( self->pcm, self->offset, numFrames );
+    if( res == -EPIPE || res == -ESTRPIPE )
+    {
+        *xrun = 1;
+    }
+    else
+    {
+        ENSURE_( res, paUnanticipatedHostError );
+    }
+
+end:
+error:
+    return result;
+}
+
+/* Extract buffer from channel area */
+static unsigned char *ExtractAddress( const snd_pcm_channel_area_t *area, snd_pcm_uframes_t offset )
+{
+    return (unsigned char *) area->addr + (area->first + offset * area->step) / 8;
+}
+
+/** Do necessary adaption between user and host channels.
+ *
+    @concern ChannelAdaption Adapting between user and host channels can involve silencing unused channels and
+    duplicating mono information if host outputs come in pairs.
+ */
+static PaError PaAlsaStreamComponent_DoChannelAdaption( PaAlsaStreamComponent *self, PaUtilBufferProcessor *bp, int numFrames )
+{
+    PaError result = paNoError;
+    unsigned char *p;
+    int i;
+    int unusedChans = self->numHostChannels - self->numUserChannels;
+    unsigned char *src, *dst;
+    int convertMono = (self->numHostChannels % 2) == 0 && (self->numUserChannels % 2) != 0;
+
+    assert( StreamDirection_Out == self->streamDir );
+
+    if( self->hostInterleaved )
+    {
+        int swidth = snd_pcm_format_size( self->nativeFormat, 1 );
+        unsigned char *buffer = ExtractAddress( self->channelAreas, self->offset );
+
+        /* Start after the last user channel */
+        p = buffer + self->numUserChannels * swidth;
+
+        if( convertMono )
+        {
+            /* Convert the last user channel into stereo pair */
+            src = buffer + (self->numUserChannels - 1) * swidth;
+            for( i = 0; i < numFrames; ++i )
+            {
+                dst = src + swidth;
+                memcpy( dst, src, swidth );
+                src += self->numHostChannels * swidth;
+            }
+
+            /* Don't touch the channel we just wrote to */
+            p += swidth;
+            --unusedChans;
+        }
+
+        if( unusedChans > 0 )
+        {
+            /* Silence unused output channels */
+            for( i = 0; i < numFrames; ++i )
+            {
+                memset( p, 0, swidth * unusedChans );
+                p += self->numHostChannels * swidth;
+            }
+        }
+    }
+    else
+    {
+        /* We extract the last user channel */
+        if( convertMono )
+        {
+            ENSURE_( snd_pcm_area_copy( self->channelAreas + self->numUserChannels, self->offset, self->channelAreas +
+                    (self->numUserChannels - 1), self->offset, numFrames, self->nativeFormat ), paUnanticipatedHostError );
+            --unusedChans;
+        }
+        if( unusedChans > 0 )
+        {
+            snd_pcm_areas_silence( self->channelAreas + (self->numHostChannels - unusedChans), self->offset, unusedChans, numFrames,
+                    self->nativeFormat );
+        }
+    }
+
+error:
+    return result;
+}
+
+static PaError PaAlsaStream_EndProcessing( PaAlsaStream *self, unsigned long numFrames, int *xrunOccurred )
+{
+    PaError result = paNoError;
+    int xrun = 0;
+
+    if( self->capture.pcm )
+    {
+        PA_ENSURE( PaAlsaStreamComponent_EndProcessing( &self->capture, numFrames, &xrun ) );
+    }
+    if( self->playback.pcm )
+    {
+        if( self->playback.numHostChannels > self->playback.numUserChannels )
+            PA_ENSURE( PaAlsaStreamComponent_DoChannelAdaption( &self->playback, &self->bufferProcessor, numFrames ) );
+        PA_ENSURE( PaAlsaStreamComponent_EndProcessing( &self->playback, numFrames, &xrun ) );
+    }
+
+error:
+    *xrunOccurred = xrun;
+    return result;
+}
+
+/** Update the number of available frames.
+ *
+ */
+static PaError PaAlsaStreamComponent_GetAvailableFrames( PaAlsaStreamComponent *self, unsigned long *numFrames, int *xrunOccurred )
+{
+    PaError result = paNoError;
+    snd_pcm_sframes_t framesAvail = snd_pcm_avail_update( self->pcm );
+    *xrunOccurred = 0;
+
+    if( -EPIPE == framesAvail )
+    {
+        *xrunOccurred = 1;
+        framesAvail = 0;
+    }
+    else
+        ENSURE_( framesAvail, paUnanticipatedHostError );
+
+    *numFrames = framesAvail;
+
+error:
+    return result;
+}
+
+/** Fill in pollfd objects.
+ */
+static PaError PaAlsaStreamComponent_BeginPolling( PaAlsaStreamComponent *self, struct pollfd *pfds )
+{
+    PaError result = paNoError;
+    int ret = snd_pcm_poll_descriptors( self->pcm, pfds, self->nfds );
+    assert( ret == self->nfds );
+
+    self->ready = 0;
+
+    return result;
+}
+
+/** Examine results from poll().
+ *
+ * @param pfds pollfds to inspect
+ * @param shouldPoll Should we continue to poll
+ * @param xrun Has an xrun occurred
+ */
+static PaError PaAlsaStreamComponent_EndPolling( PaAlsaStreamComponent *self, struct pollfd *pfds, int *shouldPoll, int *xrun )
+{
+    PaError result = paNoError;
+    unsigned short revents;
+
+    ENSURE_( snd_pcm_poll_descriptors_revents( self->pcm, pfds, self->nfds, &revents ), paUnanticipatedHostError );
+    if( revents != 0 )
+    {
+        if( revents & POLLERR )
+        {
+            *xrun = 1;
+        }
+        else
+            self->ready = 1;
+
+        *shouldPoll = 0;
+    }
+
+error:
+    return result;
+}
+
+/** Return the number of available frames for this stream.
+ *
+ * @concern FullDuplex The minimum available for the two directions is calculated, it might be desirable to ignore
+ * one direction however (not marked ready from poll), so this is controlled by queryCapture and queryPlayback.
+ *
+ * @param queryCapture Check available for capture
+ * @param queryPlayback Check available for playback
+ * @param available The returned number of frames
+ * @param xrunOccurred Return whether an xrun has occurred
+ */
+static PaError PaAlsaStream_GetAvailableFrames( PaAlsaStream *self, int queryCapture, int queryPlayback, unsigned long
+        *available, int *xrunOccurred )
+{
+    PaError result = paNoError;
+    unsigned long captureFrames, playbackFrames;
+    *xrunOccurred = 0;
+
+    assert( queryCapture || queryPlayback );
+
+    if( queryCapture )
+    {
+        assert( self->capture.pcm );
+        PA_ENSURE( PaAlsaStreamComponent_GetAvailableFrames( &self->capture, &captureFrames, xrunOccurred ) );
+        if( *xrunOccurred )
+            goto end;
+    }
+    if( queryPlayback )
+    {
+        assert( self->playback.pcm );
+        PA_ENSURE( PaAlsaStreamComponent_GetAvailableFrames( &self->playback, &playbackFrames, xrunOccurred ) );
+        if( *xrunOccurred )
+            goto end;
+    }
+
+    if( queryCapture && queryPlayback )
+    {
+        *available = PA_MIN( captureFrames, playbackFrames );
+    }
+    else if( queryCapture )
+    {
+        *available = captureFrames;
+    }
+    else
+    {
+        *available = playbackFrames;
+    }
+
+end:
+error:
+    return result;
+}
+
+/** Wait for and report available buffer space from ALSA.
+ *
+ * Unless ALSA reports a minimum of frames available for I/O, we poll the ALSA filedescriptors for more.
+ * Both of these operations can uncover xrun conditions.
+ *
+ * @concern Xruns Both polling and querying available frames can report an xrun condition.
+ *
+ * @param framesAvail Return the number of available frames
+ * @param xrunOccurred Return whether an xrun has occurred
+ */ 
+static PaError PaAlsaStream_WaitForFrames( PaAlsaStream *self, unsigned long *framesAvail, int *xrunOccurred )
+{
+    PaError result = paNoError;
+    int pollPlayback = self->playback.pcm != NULL, pollCapture = self->capture.pcm != NULL;
+    int pollTimeout = self->pollTimeout;
+    int xrun = 0;
+
+    assert( self );
+    assert( framesAvail );
+
+    if( !self->callbackMode )
+    {
+        /* In blocking mode we will only wait if necessary */
+        PA_ENSURE( PaAlsaStream_GetAvailableFrames( self, self->capture.pcm != NULL, self->playback.pcm != NULL,
+                    framesAvail, &xrun ) );
+        if( xrun )
+        {
+            goto end;
+        }
+
+        if( *framesAvail > 0 )
+        {
+            /* Mark pcms ready from poll */
+            if( self->capture.pcm )
+                self->capture.ready = 1;
+            if( self->playback.pcm )
+                self->playback.ready = 1;
+
+            goto end;
+        }
+    }
+
+    while( pollPlayback || pollCapture )
+    {
+        int totalFds = 0;
+        struct pollfd *capturePfds = NULL, *playbackPfds = NULL;
+
+        pthread_testcancel();
+
+        if( pollCapture )
+        {
+            capturePfds = self->pfds;
+            PA_ENSURE( PaAlsaStreamComponent_BeginPolling( &self->capture, capturePfds ) );
+            totalFds += self->capture.nfds;
+        }
+        if( pollPlayback )
+        {
+            playbackPfds = self->pfds + (self->capture.pcm ? self->capture.nfds : 0);
+            PA_ENSURE( PaAlsaStreamComponent_BeginPolling( &self->playback, playbackPfds ) );
+            totalFds += self->playback.nfds;
+        }
+
+        if( poll( self->pfds, totalFds, pollTimeout ) < 0 )
+        {
+            /*  XXX: Depend on preprocessor condition? */
+            if( errno == EINTR ) {  /* gdb */
+                continue;
+            }
+
+            /* TODO: Add macro for checking system calls */
+            PA_ENSURE( paInternalError );
+        }
+
+        /* check the return status of our pfds */
+        if( pollCapture )
+        {
+            PA_ENSURE( PaAlsaStreamComponent_EndPolling( &self->capture, capturePfds, &pollCapture, &xrun ) );
+        }
+        if( pollPlayback )
+        {
+            PA_ENSURE( PaAlsaStreamComponent_EndPolling( &self->playback, playbackPfds, &pollPlayback, &xrun ) );
+        }
+        if( xrun )
+        {
+            break;
+        }
+
+        /* @concern FullDuplex If only one of two pcms is ready we may want to compromise between the two.
+         * If there is less than half a period's worth of samples left of frames in the other pcm's buffer we will
+         * stop polling.
+         */
+        if( self->capture.pcm && self->playback.pcm )
+        {
+            if( pollCapture && !pollPlayback )
+            {
+                PA_ENSURE( ContinuePoll( self, StreamDirection_In, &pollTimeout, &pollCapture ) );
+            }
+            else if( pollPlayback && !pollCapture )
+            {
+                PA_ENSURE( ContinuePoll( self, StreamDirection_Out, &pollTimeout, &pollPlayback ) );
+            }
+        }
+    }
+
+    if( !xrun )
+    {
+        /* Get the number of available frames for the pcms that are marked ready.
+         * @concern FullDuplex If only one direction is marked ready (from poll), the number of frames available for
+         * the other direction is returned. This under the assumption that input is dropped earlier if paNeverDropInput
+         * is not specified.
+         */
+        int captureReady = self->capture.pcm ? self->capture.ready : 0,
+            playbackReady = self->playback.pcm ? self->playback.ready : 0;
+        PA_ENSURE( PaAlsaStream_GetAvailableFrames( self, captureReady, playbackReady, framesAvail, &xrun ) );
+
+        if( self->capture.pcm && self->playback.pcm )
+        {
+            if( !self->playback.ready && !self->neverDropInput )
+            {
+                /* TODO: Drop input */
+            }
+        }
+    }
+
+end:
+error:
+    if( xrun )
+    {
+        /* Recover from the xrun state */
+        PA_ENSURE( PaAlsaStream_HandleXrun( self ) );
+        *framesAvail = 0;
+    }
+    *xrunOccurred = xrun;
+
+    return result;
+}
+
+/** Register per-channel ALSA buffer information with buffer processor.
+ *
+ * Mmapped buffer space is acquired from ALSA, and registered with the buffer processor. Differences between the
+ * number of host and user channels is taken into account.
+ * 
+ * @param numFrames On entrance the number of requested frames, on exit the number of contiguously accessible frames.
+ */
+static PaError PaAlsaStreamComponent_RegisterChannels( PaAlsaStreamComponent *self, PaUtilBufferProcessor *bp,
+        unsigned long *numFrames, int *xrun )
+{
+    PaError result = paNoError;
+    const snd_pcm_channel_area_t *areas, *area;
+    void (*setChannel)(PaUtilBufferProcessor *, unsigned int, void *, unsigned int) =
+        StreamDirection_In == self->streamDir ? PaUtil_SetInputChannel : PaUtil_SetOutputChannel;
+    unsigned char *buffer, *p;
+    int i;
+    unsigned long framesAvail;
+
+    /* This _must_ be called before mmap_begin */
+    PA_ENSURE( PaAlsaStreamComponent_GetAvailableFrames( self, &framesAvail, xrun ) );
+    if( *xrun )
+    {
+        *numFrames = 0;
+        goto end;
+    }
+
+    ENSURE_( snd_pcm_mmap_begin( self->pcm, &areas, &self->offset, numFrames ), paUnanticipatedHostError );
+
+    if( self->hostInterleaved )
+    {
+        int swidth = snd_pcm_format_size( self->nativeFormat, 1 );
+
+        p = buffer = ExtractAddress( areas, self->offset );
+        for( i = 0; i < self->numUserChannels; ++i )
+        {
+            /* We're setting the channels up to userChannels, but the stride will be hostChannels samples */
+            setChannel( bp, i, p, self->numHostChannels );
+            p += swidth;
+        }
+    }
+    else
+    {
+        for( i = 0; i < self->numUserChannels; ++i )
+        {
+            area = areas + i;
+            buffer = ExtractAddress( area, self->offset );
+            setChannel( bp, i, buffer, 1 );
+        }
+    }
+
+    /* @concern ChannelAdaption Buffer address is recorded so we can do some channel adaption later */
+    self->channelAreas = (snd_pcm_channel_area_t *)areas;
+
+end:
+error:
+    return result;
+}
+
+/** Initiate buffer processing.
+ *
+ * ALSA buffers are registered with the PA buffer processor and the buffer size (in frames) set.
+ *
+ * @concern FullDuplex If both directions are being processed, the minimum amount of frames for the two directions is
+ * calculated.
+ *
+ * @param numFrames On entrance the number of available frames, on exit the number of received frames
+ * @param xrunOccurred Return whether an xrun has occurred
+ */
+static PaError PaAlsaStream_SetUpBuffers( PaAlsaStream *self, unsigned long *numFrames, int *xrunOccurred )
+{
+    PaError result = paNoError;
+    unsigned long captureFrames = ULONG_MAX, playbackFrames = ULONG_MAX, commonFrames = 0;
+    int xrun = 0;
+
+    /* Extract per-channel ALSA buffer pointers and register them with the buffer processor.
+     * It is possible that a direction is not marked ready however, because it is out of sync with the other.
+     */
+    if( self->capture.pcm && self->capture.ready )
+    {
+        captureFrames = *numFrames;
+        PA_ENSURE( PaAlsaStreamComponent_RegisterChannels( &self->capture, &self->bufferProcessor, &captureFrames, 
+                    &xrun ) );
+    }
+    if( self->playback.pcm && self->playback.ready )
+    {
+        playbackFrames = *numFrames;
+        PA_ENSURE( PaAlsaStreamComponent_RegisterChannels( &self->playback, &self->bufferProcessor, &playbackFrames, 
+                    &xrun ) );
+    }
+    if( xrun )
+    {
+        /* Nothing more to do */
+        assert( 0 == commonFrames );
+        goto end;
+    }
+
+    commonFrames = PA_MIN( captureFrames, playbackFrames );
+    assert( commonFrames <= *numFrames );
+
+    /* Inform PortAudio of the number of frames we got.
+     * @concern FullDuplex We might be experiencing underflow in either end; if its an input underflow, we go on
+     * with output. If its output underflow however, depending on the paNeverDropInput flag, we may want to simply
+     * discard the excess input or call the callback with paOutputOverflow flagged.
+     */
+    if( self->capture.pcm )
+    {
+        if( self->capture.ready )
+        {
+            PaUtil_SetInputFrameCount( &self->bufferProcessor, commonFrames );
+        }
+        else
+        {
+            /* We have input underflow */
+            PaUtil_SetNoInput( &self->bufferProcessor );
+        }
+    }
+    if( self->playback.pcm )
+    {
+        if( self->playback.ready )
+        {
+            PaUtil_SetOutputFrameCount( &self->bufferProcessor, commonFrames );
+        }
+        else
+        {
+            /* We have output underflow, but keeping input data (paNeverDropInput) */
+            /* assert( self->neverDropInput ); */
+            PaUtil_SetNoOutput( &self->bufferProcessor );
+        }
+    }
+    
+end:
+    *numFrames = commonFrames;
+error:
+    if( xrun )
+    {
+        PA_ENSURE( PaAlsaStream_HandleXrun( self ) );
+        *numFrames = 0;
+    }
+    *xrunOccurred = xrun;
+
+    return result;
+}
+
+/** Callback thread's function.
+ *
+ * Roughly, the workflow can be described in the following way: The number of available frames that can be processed
+ * directly is obtained from ALSA, we then request as much directly accessible memory as possible within this amount
+ * from ALSA. The buffer memory is registered with the PA buffer processor and processing is carried out with
+ * PaUtil_EndBufferProcessing. Finally, the number of processed frames is reported to ALSA. The processing can
+ * happen in several iterations untill we have consumed the known number of available frames (or an xrun is detected).
+ */
+static void *CallbackThreadFunc( void *userData )
+{
+    PaError result = paNoError, *pres = NULL;
+    PaAlsaStream *stream = (PaAlsaStream*) userData;
+    PaStreamCallbackTimeInfo timeInfo = {0, 0, 0};
+    snd_pcm_sframes_t startThreshold = 0;
+    int callbackResult = paContinue;
+    PaStreamCallbackFlags cbFlags = 0;  /* We might want to keep state across iterations */
+    int streamStarted = 0;
+
+    assert( stream );
+
+    callbackThread_ = pthread_self();
+    /* Execute OnExit when exiting */
+    pthread_cleanup_push( &OnExit, stream );
+
+    /* Not implemented */
+    assert( !stream->primeBuffers );
+
+    /* @concern StreamStart If the output is being primed the output pcm needs to be prepared, otherwise the
+     * stream is started immediately. The latter involves signaling the waiting main thread.
+     */
+    if( stream->primeBuffers )
+    {
+        snd_pcm_sframes_t avail;
+        
+        if( stream->playback.pcm )
+            ENSURE_( snd_pcm_prepare( stream->playback.pcm ), paUnanticipatedHostError );
+        if( stream->capture.pcm && !stream->pcmsSynced )
+            ENSURE_( snd_pcm_prepare( stream->capture.pcm ), paUnanticipatedHostError );
+
+        /* We can't be certain that the whole ring buffer is available for priming, but there should be
+         * at least one period */
+        avail = snd_pcm_avail_update( stream->playback.pcm );
+        startThreshold = avail - (avail % stream->playback.framesPerBuffer);
+        assert( startThreshold >= stream->playback.framesPerBuffer );
+    }
+    else
+    {
+        ASSERT_CALL_( pthread_mutex_lock( &stream->startMtx ), 0 );
+        PA_ENSURE( AlsaStart( stream, 0 ) );    /* Buffer will be zeroed */
+        ASSERT_CALL_( pthread_cond_signal( &stream->startCond ), 0 );
+        ASSERT_CALL_( pthread_mutex_unlock( &stream->startMtx ), 0 );
+
+        streamStarted = 1;
+    }
+
+    while( 1 )
+    {
+        unsigned long framesAvail, framesGot;
+        int xrun = 0;
+
+        pthread_testcancel();
+
+        /* @concern StreamStop if the main thread has requested a stop and the stream has not been effectively
+         * stopped we signal this condition by modifying callbackResult (we'll want to flush buffered output).
+         */
+        if( stream->callbackStop && paContinue == callbackResult )
+        {
+            PA_DEBUG(( "Setting callbackResult to paComplete\n" ));
+            callbackResult = paComplete;
+        }
+
+        if( paContinue != callbackResult )
+        {
+            stream->callbackAbort = (paAbort == callbackResult);
+            if( stream->callbackAbort ||
+                    /** @concern BlockAdaption Go on if adaption buffers are empty */
+                    PaUtil_IsBufferProcessorOutputEmpty( &stream->bufferProcessor ) ) 
+                goto end;
+
+            PA_DEBUG(( "%s: Flushing buffer processor\n", __FUNCTION__ ));
+            /* There is still buffered output that needs to be processed */
+        }
+
+        /* Wait for data to become available, this comes down to polling the ALSA file descriptors untill we have
+         * a number of available frames.
+         */
+        PA_ENSURE( PaAlsaStream_WaitForFrames( stream, &framesAvail, &xrun ) );
+        if( xrun )
+        {
+            assert( 0 == framesAvail );
+            continue;
+
+            /* XXX: Report xruns to the user? A situation is conceivable where the callback is never invoked due
+             * to constant xruns, it might be desirable to notify the user of this.
+             */
+        }
+
+        /* Consume buffer space. Once we have a number of frames available for consumption we must retrieve the
+         * mmapped buffers from ALSA, this is contiguously accessible memory however, so we may receive smaller
+         * portions at a time than is available as a whole. Therefore we should be prepared to process several
+         * chunks successively. The buffers are passed to the PA buffer processor.
+         */
+        while( framesAvail > 0 )
+        {
+            xrun = 0;
+
+            pthread_testcancel();
+
+            /** @concern Xruns Under/overflows are to be reported to the callback */
+            if( stream->underrun > 0.0 )
+            {
+                cbFlags |= paOutputUnderflow;
+                stream->underrun = 0.0;
+            }
+            if( stream->overrun > 0.0 )
+            {
+                cbFlags |= paInputOverflow;
+                stream->overrun = 0.0;
+            }
+            if( stream->capture.pcm && stream->playback.pcm )
+            {
+                /** @concern FullDuplex It's possible that only one direction is being processed to avoid an
+                 * under- or overflow, this should be reported correspondingly */
+                if( !stream->capture.ready )
+                {
+                    cbFlags |= paInputUnderflow;
+                    PA_DEBUG(( "%s: Input underflow\n", __FUNCTION__ ));
+                }
+                else if( !stream->playback.ready )
+                {
+                    cbFlags |= paOutputOverflow;
+                    PA_DEBUG(( "%s: Output overflow\n", __FUNCTION__ ));
+                }
+            }
+
+            CallbackUpdate( &stream->threading );
+            CalculateTimeInfo( stream, &timeInfo );
+            PaUtil_BeginBufferProcessing( &stream->bufferProcessor, &timeInfo, cbFlags );
+            cbFlags = 0;
+
+            /* CPU load measurement should include processing activivity external to the stream callback */
+            PaUtil_BeginCpuLoadMeasurement( &stream->cpuLoadMeasurer );
+
+            framesGot = framesAvail;
+            PA_ENSURE( PaAlsaStream_SetUpBuffers( stream, &framesGot, &xrun ) );
+            framesAvail -= framesGot;
+
+            if( framesGot > 0 )
+            {
+                assert( !xrun );
+
+                PaUtil_EndBufferProcessing( &stream->bufferProcessor, &callbackResult );
+                PA_ENSURE( PaAlsaStream_EndProcessing( stream, framesGot, &xrun ) );
+            }
+            PaUtil_EndCpuLoadMeasurement( &stream->cpuLoadMeasurer, framesGot );
+
+            if( framesGot == 0 )
+            {
+                if( !xrun )
+                    PA_DEBUG(( "%s: Received less frames than reported from ALSA!\n", __FUNCTION__ ));
+
+                /* Go back to polling for more frames */
+                break;
+
+            }
+
+            if( paContinue != callbackResult )
+                break;
+        }
+    }
+
+    /* Match pthread_cleanup_push */
+    pthread_cleanup_pop( 1 );
+
+end:
+    pthread_exit( pres );
+
+error:
+    /* Pass on error code */
+    pres = malloc( sizeof (PaError) );
+    *pres = result;
+    
+    goto end;
+}
+
+/* Blocking interface */
+
+static PaError ReadStream( PaStream* s, void *buffer, unsigned long frames )
+{
+    PaError result = paNoError;
+    PaAlsaStream *stream = (PaAlsaStream*)s;
+    unsigned long framesGot, framesAvail;
+    void *userBuffer;
+    snd_pcm_t *save = stream->playback.pcm;
+
+    assert( stream );
+
+    PA_UNLESS( stream->capture.pcm, paCanNotReadFromAnOutputOnlyStream );
+
+    /* Disregard playback */
+    stream->playback.pcm = NULL;
+
+    if( stream->overrun > 0. )
+    {
+        result = paInputOverflowed;
+        stream->overrun = 0.0;
+    }
+
+    if( stream->capture.userInterleaved )
+        userBuffer = buffer;
+    else
+    {
+        /* Copy channels into local array */
+        userBuffer = stream->capture.userBuffers;
+        memcpy( userBuffer, buffer, sizeof (void *) * stream->capture.numUserChannels );
+    }
+
+    /* Start stream if in prepared state */
+    if( snd_pcm_state( stream->capture.pcm ) == SND_PCM_STATE_PREPARED )
+    {
+        ENSURE_( snd_pcm_start( stream->capture.pcm ), paUnanticipatedHostError );
+    }
+
+    while( frames > 0 )
+    {
+        int xrun = 0;
+        PA_ENSURE( PaAlsaStream_WaitForFrames( stream, &framesAvail, &xrun ) );
+        framesGot = PA_MIN( framesAvail, frames );
+
+        PA_ENSURE( PaAlsaStream_SetUpBuffers( stream, &framesGot, &xrun ) );
+        if( framesGot > 0 )
+        {
+            framesGot = PaUtil_CopyInput( &stream->bufferProcessor, &userBuffer, framesGot );
+            PA_ENSURE( PaAlsaStream_EndProcessing( stream, framesGot, &xrun ) );
+            frames -= framesGot;
+        }
+    }
+
+end:
+    stream->playback.pcm = save;
+    return result;
+error:
+    goto end;
+}
+
+static PaError WriteStream( PaStream* s, const void *buffer, unsigned long frames )
+{
+    PaError result = paNoError;
+    signed long err;
+    PaAlsaStream *stream = (PaAlsaStream*)s;
+    snd_pcm_uframes_t framesGot, framesAvail;
+    const void *userBuffer;
+    snd_pcm_t *save = stream->capture.pcm;
+    
+    assert( stream );
+
+    PA_UNLESS( stream->playback.pcm, paCanNotWriteToAnInputOnlyStream );
+
+    /* Disregard capture */
+    stream->capture.pcm = NULL;
+
+    if( stream->underrun > 0. )
+    {
+        result = paOutputUnderflowed;
+        stream->underrun = 0.0;
+    }
+
+    if( stream->playback.userInterleaved )
+        userBuffer = buffer;
+    else /* Copy channels into local array */
+    {
+        userBuffer = stream->playback.userBuffers;
+        memcpy( (void *)userBuffer, buffer, sizeof (void *) * stream->playback.numUserChannels );
+    }
+
+    while( frames > 0 )
+    {
+        int xrun = 0;
+        snd_pcm_uframes_t hwAvail;
+
+        PA_ENSURE( PaAlsaStream_WaitForFrames( stream, &framesAvail, &xrun ) );
+        framesGot = PA_MIN( framesAvail, frames );
+
+        PA_ENSURE( PaAlsaStream_SetUpBuffers( stream, &framesGot, &xrun ) );
+        if( framesGot > 0 )
+        {
+            framesGot = PaUtil_CopyOutput( &stream->bufferProcessor, &userBuffer, framesGot );
+            PA_ENSURE( PaAlsaStream_EndProcessing( stream, framesGot, &xrun ) );
+            frames -= framesGot;
+        }
+
+        /* Frames residing in buffer */
+        PA_ENSURE( err = GetStreamWriteAvailable( stream ) );
+        framesAvail = err;
+        hwAvail = stream->playback.bufferSize - framesAvail;
+
+        /* Start stream after one period of samples worth */
+        if( snd_pcm_state( stream->playback.pcm ) == SND_PCM_STATE_PREPARED &&
+                hwAvail >= stream->playback.framesPerBuffer )
+        {
+            ENSURE_( snd_pcm_start( stream->playback.pcm ), paUnanticipatedHostError );
+        }
+    }
+
+end:
+    stream->capture.pcm = save;
+    return result;
+error:
+    goto end;
+}
+
+/* Return frames available for reading. In the event of an overflow, the capture pcm will be restarted */
+static signed long GetStreamReadAvailable( PaStream* s )
+{
+    PaError result = paNoError;
+    PaAlsaStream *stream = (PaAlsaStream*)s;
+    unsigned long avail;
+    int xrun;
+
+    PA_ENSURE( PaAlsaStreamComponent_GetAvailableFrames( &stream->capture, &avail, &xrun ) );
+    if( xrun )
+    {
+        PA_ENSURE( PaAlsaStream_HandleXrun( stream ) );
+        PA_ENSURE( PaAlsaStreamComponent_GetAvailableFrames( &stream->capture, &avail, &xrun ) );
+        if( xrun )
+            PA_ENSURE( paInputOverflowed );
+    }
+
+    return (signed long)avail;
+
+error:
+    return result;
+}
+
+static signed long GetStreamWriteAvailable( PaStream* s )
+{
+    PaError result = paNoError;
+    PaAlsaStream *stream = (PaAlsaStream*)s;
+    unsigned long avail;
+    int xrun;
+
+    PA_ENSURE( PaAlsaStreamComponent_GetAvailableFrames( &stream->playback, &avail, &xrun ) );
+    if( xrun )
+    {
+        snd_pcm_sframes_t savail;
+
+        PA_ENSURE( PaAlsaStream_HandleXrun( stream ) );
+        savail = snd_pcm_avail_update( stream->playback.pcm );
+
+        /* savail should not contain -EPIPE now, since PaAlsaStream_HandleXrun will only prepare the pcm */
+        ENSURE_( savail, paUnanticipatedHostError );
+
+        avail = (unsigned long) savail;
+    }
+
+    return (signed long)avail;
+
+error:
+    return result;
+}
+
+/* Extensions */
+
+/* Initialize host api specific structure */
+void PaAlsa_InitializeStreamInfo( PaAlsaStreamInfo *info )
+{
+    info->size = sizeof (PaAlsaStreamInfo);
+    info->hostApiType = paALSA;
+    info->version = 1;
+    info->deviceString = NULL;
+}
+
+void PaAlsa_EnableRealtimeScheduling( PaStream *s, int enable )
+{
+    PaAlsaStream *stream = (PaAlsaStream *) s;
+    stream->threading.rtSched = enable;
+}
+
+void PaAlsa_EnableWatchdog( PaStream *s, int enable )
+{
+    PaAlsaStream *stream = (PaAlsaStream *) s;
+    stream->threading.useWatchdog = enable;
+}
diff --git a/src/audio/portaudio/pa_linux_alsa/pa_linux_alsa.h b/src/audio/portaudio/pa_linux_alsa/pa_linux_alsa.h
new file mode 100644
index 0000000000000000000000000000000000000000..b9d43c9291c20c5088b654bc83566821a0969409
--- /dev/null
+++ b/src/audio/portaudio/pa_linux_alsa/pa_linux_alsa.h
@@ -0,0 +1,64 @@
+#ifndef PA_LINUX_ALSA_H
+#define PA_LINUX_ALSA_H
+
+/*
+ * $Id$
+ * PortAudio Portable Real-Time Audio Library
+ * ALSA-specific extensions
+ *
+ * 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.
+ *
+ */
+
+/** @file
+ * ALSA-specific PortAudio API extension header file.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct PaAlsaStreamInfo
+{
+    unsigned long size;
+    PaHostApiTypeId hostApiType;
+    unsigned long version;
+
+    const char *deviceString;
+}
+PaAlsaStreamInfo;
+
+void PaAlsa_InitializeStreamInfo( PaAlsaStreamInfo *info );
+
+void PaAlsa_EnableRealtimeScheduling( PaStream *s, int enable );
+
+void PaAlsa_EnableWatchdog( PaStream *s, int enable );
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/audio/portaudio/pa_mac/pa_mac_hostapis.c b/src/audio/portaudio/pa_mac/pa_mac_hostapis.c
new file mode 100644
index 0000000000000000000000000000000000000000..f8306c302f4c5a669eb64e419ded5a3a91440c7d
--- /dev/null
+++ b/src/audio/portaudio/pa_mac/pa_mac_hostapis.c
@@ -0,0 +1,79 @@
+/*
+ * $Id$
+ * Portable Audio I/O Library Windows initialization table
+ *
+ * Based on the Open Source API proposed by Ross Bencina
+ * Copyright (c) 1999-2002 Ross Bencina, 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.
+ */
+
+/** @file
+Mac OS host API initialization function table.
+*/
+
+
+#include "pa_hostapi.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif /* __cplusplus */
+    
+    PaError PaSkeleton_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
+    PaError PaMacCore_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
+    PaError PaMacSm_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
+    PaError PaJack_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
+    PaError PaMacAsio_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
+    
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+
+PaUtilHostApiInitializer *paHostApiInitializers[] =
+{
+#ifdef PA_USE_COREAUDIO
+    PaMacCore_Initialize,
+#endif
+    
+#ifdef PA_USE_SM
+    PaMacSm_Initialize,
+#endif
+    
+#ifdef PA_USE_JACK
+    PaJack_Initialize,
+#endif
+
+#ifdef PA_USE_ASIO
+    PaMacAsio_Initialize,
+#endif
+    
+    PaSkeleton_Initialize, /* just for testing */
+    
+    0   /* NULL terminated array */
+};
+
+
+int paDefaultHostApiIndex = 0;
diff --git a/src/audio/portaudio/pa_mac_core/notes.txt b/src/audio/portaudio/pa_mac_core/notes.txt
new file mode 100644
index 0000000000000000000000000000000000000000..c79b90e66dc61da13b25b55a9ecaa19647bdb65c
--- /dev/null
+++ b/src/audio/portaudio/pa_mac_core/notes.txt
@@ -0,0 +1,34 @@
+Notes on Core Audio Implementation of PortAudio
+
+by Phil Burk and Darren Gibbs
+
+Document last updated March 20, 2002
+
+WHAT WORKS
+
+Output with very low latency, <10 msec.
+Half duplex input or output.
+Full duplex on the same CoreAudio device.
+The paFLoat32, paInt16, paInt8, paUInt8 sample formats.
+Pa_GetCPULoad()
+Pa_StreamTime()
+
+KNOWN BUGS OR LIMITATIONS
+
+We do not yet support simultaneous input and output on different 
+devices. Note that some CoreAudio devices like the Roland UH30 look 
+like one device but are actually two different CoreAudio devices. The 
+BuiltIn audio is typically one CoreAudio device.
+
+Mono doesn't work.
+
+DEVICE MAPPING
+
+CoreAudio devices can support both input and output. But the sample 
+rates supported may be different. So we have map one or two PortAudio 
+device to each CoreAudio device depending on whether it supports 
+input, output or both.
+
+When we query devices, we first get a list of CoreAudio devices. Then 
+we scan the list and add a PortAudio device for each CoreAudio device 
+that supports input. Then we make a scan for output devices.
diff --git a/src/audio/portaudio/pa_mac_core/pa_mac_core.c b/src/audio/portaudio/pa_mac_core/pa_mac_core.c
new file mode 100644
index 0000000000000000000000000000000000000000..9db047ebb20af18b3fa98ad17760e08a8a421f44
--- /dev/null
+++ b/src/audio/portaudio/pa_mac_core/pa_mac_core.c
@@ -0,0 +1,892 @@
+/*
+ * $Id$
+ * pa_mac_core.c
+ * Implementation of PortAudio for Mac OS X CoreAudio       
+ *                                                                                         
+ * 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.
+ *
+ */
+
+#include <CoreAudio/CoreAudio.h>
+#include <AudioToolbox/AudioToolbox.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <assert.h>
+
+#include "portaudio.h"
+#include "pa_trace.h"
+#include "pa_util.h"
+#include "pa_allocation.h"
+#include "pa_hostapi.h"
+#include "pa_stream.h"
+#include "pa_cpuload.h"
+#include "pa_process.h"
+
+// =====  constants  =====
+
+// =====  structs  =====
+#pragma mark structs
+
+// PaMacCoreHostApiRepresentation - host api datastructure specific to this implementation
+typedef struct PaMacCore_HAR
+{
+    PaUtilHostApiRepresentation inheritedHostApiRep;
+    PaUtilStreamInterface callbackStreamInterface;
+    PaUtilStreamInterface blockingStreamInterface;
+    
+    PaUtilAllocationGroup *allocations;
+    AudioDeviceID *macCoreDeviceIds;
+}
+PaMacCoreHostApiRepresentation;
+
+typedef struct PaMacCore_DI
+{
+    PaDeviceInfo inheritedDeviceInfo;
+}
+PaMacCoreDeviceInfo;
+
+// PaMacCoreStream - a stream data structure specifically for this implementation
+typedef struct PaMacCore_S
+{
+    PaUtilStreamRepresentation streamRepresentation;
+    PaUtilCpuLoadMeasurer cpuLoadMeasurer;
+    PaUtilBufferProcessor bufferProcessor;
+    
+    int primeStreamUsingCallback;
+    
+    AudioDeviceID inputDevice;
+    AudioDeviceID outputDevice;
+    
+    // Processing thread management --------------
+//    HANDLE abortEvent;
+//    HANDLE processingThread;
+//    DWORD processingThreadId;
+    
+    char throttleProcessingThreadOnOverload; // 0 -> don't throtte, non-0 -> throttle
+    int processingThreadPriority;
+    int highThreadPriority;
+    int throttledThreadPriority;
+    unsigned long throttledSleepMsecs;
+    
+    int isStopped;
+    volatile int isActive;
+    volatile int stopProcessing; // stop thread once existing buffers have been returned
+    volatile int abortProcessing; // stop thread immediately
+    
+//    DWORD allBuffersDurationMs; // used to calculate timeouts
+}
+PaMacCoreStream;
+
+// Data needed by the CoreAudio callback functions
+typedef struct PaMacCore_CD
+{
+    PaMacCoreStream *stream;
+    PaStreamCallback *callback;
+    void *userData;
+    PaUtilConverter *inputConverter;
+    PaUtilConverter *outputConverter;
+    void *inputBuffer;
+    void *outputBuffer;
+    int inputChannelCount;
+    int outputChannelCount;
+    PaSampleFormat inputSampleFormat;
+    PaSampleFormat outputSampleFormat;
+    PaUtilTriangularDitherGenerator *ditherGenerator;
+}
+PaMacClientData;
+
+// =====  CoreAudio-PortAudio bridge functions =====
+#pragma mark CoreAudio-PortAudio bridge functions
+
+// Maps CoreAudio OSStatus codes to PortAudio PaError codes
+static PaError conv_err(OSStatus error)
+{
+    PaError result;
+    
+    switch (error) {
+        case kAudioHardwareNoError:
+            result = paNoError; break;
+        case kAudioHardwareNotRunningError:
+            result = paInternalError; break;
+        case kAudioHardwareUnspecifiedError:
+            result = paInternalError; break;
+        case kAudioHardwareUnknownPropertyError:
+            result = paInternalError; break;
+        case kAudioHardwareBadPropertySizeError:
+            result = paInternalError; break;
+        case kAudioHardwareIllegalOperationError:
+            result = paInternalError; break;
+        case kAudioHardwareBadDeviceError:
+            result = paInvalidDevice; break;
+        case kAudioHardwareBadStreamError:
+            result = paBadStreamPtr; break;
+        case kAudioHardwareUnsupportedOperationError:
+            result = paInternalError; break;
+        case kAudioDeviceUnsupportedFormatError:
+            result = paSampleFormatNotSupported; break;
+        case kAudioDevicePermissionsError:
+            result = paDeviceUnavailable; break;
+        default:
+            result = paInternalError;
+    }
+    
+    return result;
+}
+
+static AudioStreamBasicDescription *InitializeStreamDescription(const PaStreamParameters *parameters, double sampleRate)
+{
+    struct AudioStreamBasicDescription *streamDescription = PaUtil_AllocateMemory(sizeof(AudioStreamBasicDescription));
+    streamDescription->mSampleRate = sampleRate;
+    streamDescription->mFormatID = kAudioFormatLinearPCM;
+    streamDescription->mFormatFlags = 0;
+    streamDescription->mFramesPerPacket = 1;
+    
+    if (parameters->sampleFormat & paNonInterleaved) {
+        streamDescription->mFormatFlags |= kLinearPCMFormatFlagIsNonInterleaved;
+        streamDescription->mChannelsPerFrame = 1;
+        streamDescription->mBytesPerFrame = Pa_GetSampleSize(parameters->sampleFormat);
+        streamDescription->mBytesPerPacket = Pa_GetSampleSize(parameters->sampleFormat);
+    }
+    else {
+        streamDescription->mChannelsPerFrame = parameters->channelCount;
+    }
+    
+    streamDescription->mBytesPerFrame = Pa_GetSampleSize(parameters->sampleFormat) * streamDescription->mChannelsPerFrame;
+    streamDescription->mBytesPerPacket = streamDescription->mBytesPerFrame * streamDescription->mFramesPerPacket;
+    
+    if (parameters->sampleFormat & paFloat32) {
+        streamDescription->mFormatFlags |= kLinearPCMFormatFlagIsFloat;
+        streamDescription->mBitsPerChannel = 32;
+    }
+    else if (parameters->sampleFormat & paInt32) {
+        streamDescription->mFormatFlags |= kLinearPCMFormatFlagIsSignedInteger;
+        streamDescription->mBitsPerChannel = 32;
+    }
+    else if (parameters->sampleFormat & paInt24) {
+        streamDescription->mFormatFlags |= kLinearPCMFormatFlagIsSignedInteger;
+        streamDescription->mBitsPerChannel = 24;
+    }
+    else if (parameters->sampleFormat & paInt16) {
+        streamDescription->mFormatFlags |= kLinearPCMFormatFlagIsSignedInteger;
+        streamDescription->mBitsPerChannel = 16;
+    }
+    else if (parameters->sampleFormat & paInt8) {
+        streamDescription->mFormatFlags |= kLinearPCMFormatFlagIsSignedInteger;
+        streamDescription->mBitsPerChannel = 8;
+    }    
+    else if (parameters->sampleFormat & paInt32) {
+        streamDescription->mBitsPerChannel = 8;
+    }
+    
+    return streamDescription;
+}
+
+static PaStreamCallbackTimeInfo *InitializeTimeInfo(const AudioTimeStamp* now, const AudioTimeStamp* inputTime, const AudioTimeStamp* outputTime)
+{
+    PaStreamCallbackTimeInfo *timeInfo = PaUtil_AllocateMemory(sizeof(PaStreamCallbackTimeInfo));
+    
+    timeInfo->inputBufferAdcTime = inputTime->mSampleTime;
+    timeInfo->currentTime = now->mSampleTime;
+    timeInfo->outputBufferDacTime = outputTime->mSampleTime;
+    
+    return timeInfo;
+}
+
+// =====  support functions  =====
+#pragma mark support functions
+
+static void CleanUp(PaMacCoreHostApiRepresentation *macCoreHostApi)
+{
+    if( macCoreHostApi->allocations )
+    {
+        PaUtil_FreeAllAllocations( macCoreHostApi->allocations );
+        PaUtil_DestroyAllocationGroup( macCoreHostApi->allocations );
+    }
+    
+    PaUtil_FreeMemory( macCoreHostApi );    
+}
+
+static PaError GetChannelInfo(PaDeviceInfo *deviceInfo, AudioDeviceID macCoreDeviceId, int isInput)
+{
+    UInt32 propSize;
+    PaError err = paNoError;
+    UInt32 i;
+    int numChannels = 0;
+    AudioBufferList *buflist;
+
+    err = conv_err(AudioDeviceGetPropertyInfo(macCoreDeviceId, 0, isInput, kAudioDevicePropertyStreamConfiguration, &propSize, NULL));
+    buflist = PaUtil_AllocateMemory(propSize);
+    err = conv_err(AudioDeviceGetProperty(macCoreDeviceId, 0, isInput, kAudioDevicePropertyStreamConfiguration, &propSize, buflist));
+    if (!err) {
+        for (i = 0; i < buflist->mNumberBuffers; ++i) {
+            numChannels += buflist->mBuffers[i].mNumberChannels;
+        }
+        int frameLatency;
+        propSize = sizeof(UInt32);
+        err = conv_err(AudioDeviceGetProperty(macCoreDeviceId, 0, isInput, kAudioDevicePropertyLatency, &propSize, &frameLatency));
+        if (!err) {
+            double secondLatency = frameLatency / deviceInfo->defaultSampleRate;
+            if (isInput) {
+                deviceInfo->maxInputChannels = numChannels;
+                deviceInfo->defaultLowInputLatency = secondLatency;
+                deviceInfo->defaultHighInputLatency = secondLatency;
+            }
+            else {
+                deviceInfo->maxOutputChannels = numChannels;
+                deviceInfo->defaultLowOutputLatency = secondLatency;
+                deviceInfo->defaultHighOutputLatency = secondLatency;
+            }
+        }
+    }
+    PaUtil_FreeMemory(buflist);
+    
+    return err;
+}
+
+static PaError InitializeDeviceInfo(PaMacCoreDeviceInfo *macCoreDeviceInfo,  AudioDeviceID macCoreDeviceId, PaHostApiIndex hostApiIndex )
+{
+    PaDeviceInfo *deviceInfo = &macCoreDeviceInfo->inheritedDeviceInfo;
+    deviceInfo->structVersion = 2;
+    deviceInfo->hostApi = hostApiIndex;
+    
+    PaError err = paNoError;
+    UInt32 propSize;
+
+    err = conv_err(AudioDeviceGetPropertyInfo(macCoreDeviceId, 0, 0, kAudioDevicePropertyDeviceName, &propSize, NULL));
+    // FIXME: this allocation should be part of the allocations group
+    char *name = PaUtil_AllocateMemory(propSize);
+    err = conv_err(AudioDeviceGetProperty(macCoreDeviceId, 0, 0, kAudioDevicePropertyDeviceName, &propSize, name));
+    if (!err) {
+        deviceInfo->name = name;
+    }
+    
+    Float64 sampleRate;
+    propSize = sizeof(Float64);
+    err = conv_err(AudioDeviceGetProperty(macCoreDeviceId, 0, 0, kAudioDevicePropertyNominalSampleRate, &propSize, &sampleRate));
+    if (!err) {
+        deviceInfo->defaultSampleRate = sampleRate;
+    }
+
+
+    // Get channel info
+    err = GetChannelInfo(deviceInfo, macCoreDeviceId, 1);
+    err = GetChannelInfo(deviceInfo, macCoreDeviceId, 0);
+
+    return err;
+}
+
+static PaError InitializeDeviceInfos( PaMacCoreHostApiRepresentation *macCoreHostApi, PaHostApiIndex hostApiIndex )
+{
+    PaError result = paNoError;
+    PaUtilHostApiRepresentation *hostApi;
+    PaMacCoreDeviceInfo *deviceInfoArray;
+
+    // initialise device counts and default devices under the assumption that there are no devices. These values are incremented below if and when devices are successfully initialized.
+    hostApi = &macCoreHostApi->inheritedHostApiRep;
+    hostApi->info.deviceCount = 0;
+    hostApi->info.defaultInputDevice = paNoDevice;
+    hostApi->info.defaultOutputDevice = paNoDevice;
+    
+    UInt32 propsize;
+    AudioHardwareGetPropertyInfo(kAudioHardwarePropertyDevices, &propsize, NULL);
+    int numDevices = propsize / sizeof(AudioDeviceID);
+    hostApi->info.deviceCount = numDevices;
+    if (numDevices > 0) {
+        hostApi->deviceInfos = (PaDeviceInfo**)PaUtil_GroupAllocateMemory(
+                                            macCoreHostApi->allocations, sizeof(PaDeviceInfo*) * numDevices );
+        if( !hostApi->deviceInfos )
+        {
+            return paInsufficientMemory;
+        }
+
+        // allocate all device info structs in a contiguous block
+        deviceInfoArray = (PaMacCoreDeviceInfo*)PaUtil_GroupAllocateMemory(
+                                macCoreHostApi->allocations, sizeof(PaMacCoreDeviceInfo) * numDevices );
+        if( !deviceInfoArray )
+        {
+            return paInsufficientMemory;
+        }
+        
+        macCoreHostApi->macCoreDeviceIds = PaUtil_GroupAllocateMemory(macCoreHostApi->allocations, propsize);
+        AudioHardwareGetProperty(kAudioHardwarePropertyDevices, &propsize, macCoreHostApi->macCoreDeviceIds);
+
+        AudioDeviceID defaultInputDevice, defaultOutputDevice;
+        propsize = sizeof(AudioDeviceID);
+        AudioHardwareGetProperty(kAudioHardwarePropertyDefaultInputDevice, &propsize, &defaultInputDevice);
+        AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice, &propsize, &defaultOutputDevice);
+        
+        UInt32 i;
+        for (i = 0; i < numDevices; ++i) {
+            if (macCoreHostApi->macCoreDeviceIds[i] == defaultInputDevice) {
+                hostApi->info.defaultInputDevice = i;
+            }
+            if (macCoreHostApi->macCoreDeviceIds[i] == defaultOutputDevice) {
+                hostApi->info.defaultOutputDevice = i;
+            }
+            InitializeDeviceInfo(&deviceInfoArray[i], macCoreHostApi->macCoreDeviceIds[i], hostApiIndex);
+            hostApi->deviceInfos[i] = &(deviceInfoArray[i].inheritedDeviceInfo);      
+        }
+    }
+
+    return result;
+}
+
+static OSStatus CheckFormat(AudioDeviceID macCoreDeviceId, const PaStreamParameters *parameters, double sampleRate, int isInput)
+{
+    UInt32 propSize = sizeof(AudioStreamBasicDescription);
+    AudioStreamBasicDescription *streamDescription = PaUtil_AllocateMemory(propSize);
+
+    streamDescription->mSampleRate = sampleRate;
+    streamDescription->mFormatID = 0;
+    streamDescription->mFormatFlags = 0;
+    streamDescription->mBytesPerPacket = 0;
+    streamDescription->mFramesPerPacket = 0;
+    streamDescription->mBytesPerFrame = 0;
+    streamDescription->mChannelsPerFrame = 0;
+    streamDescription->mBitsPerChannel = 0;
+    streamDescription->mReserved = 0;
+
+    OSStatus result = AudioDeviceGetProperty(macCoreDeviceId, 0, isInput, kAudioDevicePropertyStreamFormatSupported, &propSize, streamDescription);
+    PaUtil_FreeMemory(streamDescription);
+    return result;
+}
+
+static OSStatus CopyInputData(PaMacClientData* destination, const AudioBufferList *source, unsigned long frameCount)
+{
+    int frameSpacing, channelSpacing;
+    if (destination->inputSampleFormat & paNonInterleaved) {
+        frameSpacing = 1;
+        channelSpacing = destination->inputChannelCount;
+    }
+    else {
+        frameSpacing = destination->inputChannelCount;
+        channelSpacing = 1;
+    }
+    
+    AudioBuffer const *inputBuffer = &source->mBuffers[0];
+    void *coreAudioBuffer = inputBuffer->mData;
+    void *portAudioBuffer = destination->inputBuffer;
+    UInt32 i, streamNumber, streamChannel;
+    for (i = streamNumber = streamChannel = 0; i < destination->inputChannelCount; ++i, ++streamChannel) {
+        if (streamChannel >= inputBuffer->mNumberChannels) {
+            ++streamNumber;
+            inputBuffer = &source->mBuffers[streamNumber];
+            coreAudioBuffer = inputBuffer->mData;
+            streamChannel = 0;
+        }
+        destination->inputConverter(portAudioBuffer, frameSpacing, coreAudioBuffer, inputBuffer->mNumberChannels, frameCount, destination->ditherGenerator);
+        coreAudioBuffer += sizeof(Float32);
+        portAudioBuffer += Pa_GetSampleSize(destination->inputSampleFormat) * channelSpacing;
+    }
+}
+
+static OSStatus CopyOutputData(AudioBufferList* destination, PaMacClientData *source, unsigned long frameCount)
+{
+    int frameSpacing, channelSpacing;
+    if (source->outputSampleFormat & paNonInterleaved) {
+        frameSpacing = 1;
+        channelSpacing = source->outputChannelCount;
+    }
+    else {
+        frameSpacing = source->outputChannelCount;
+        channelSpacing = 1;
+    }
+    
+    AudioBuffer *outputBuffer = &destination->mBuffers[0];
+    void *coreAudioBuffer = outputBuffer->mData;
+    void *portAudioBuffer = source->outputBuffer;
+    UInt32 i, streamNumber, streamChannel;
+    for (i = streamNumber = streamChannel = 0; i < source->outputChannelCount; ++i, ++streamChannel) {
+        if (streamChannel >= outputBuffer->mNumberChannels) {
+            ++streamNumber;
+            outputBuffer = &destination->mBuffers[streamNumber];
+            coreAudioBuffer = outputBuffer->mData;
+            streamChannel = 0;
+        }
+        source->outputConverter(coreAudioBuffer, outputBuffer->mNumberChannels, portAudioBuffer, frameSpacing, frameCount, NULL);
+        coreAudioBuffer += sizeof(Float32);
+        portAudioBuffer += Pa_GetSampleSize(source->outputSampleFormat) * channelSpacing;
+    }    
+}
+
+static OSStatus AudioIOProc( AudioDeviceID inDevice,
+                      const AudioTimeStamp* inNow,
+                      const AudioBufferList* inInputData,
+                      const AudioTimeStamp* inInputTime,
+                      AudioBufferList* outOutputData, 
+                      const AudioTimeStamp* inOutputTime,
+                      void* inClientData)
+{
+    PaMacClientData *clientData = (PaMacClientData *)inClientData;
+    PaStreamCallbackTimeInfo *timeInfo = InitializeTimeInfo(inNow, inInputTime, inOutputTime);
+    
+    PaUtil_BeginCpuLoadMeasurement( &clientData->stream->cpuLoadMeasurer );
+    
+    AudioBuffer *outputBuffer = &outOutputData->mBuffers[0];
+    unsigned long frameCount = outputBuffer->mDataByteSize / (outputBuffer->mNumberChannels * sizeof(Float32));
+
+    if (clientData->inputBuffer) {
+        CopyInputData(clientData, inInputData, frameCount);
+    }
+    PaStreamCallbackResult result = clientData->callback(clientData->inputBuffer, clientData->outputBuffer, frameCount, timeInfo, paNoFlag, clientData->userData);
+    if (clientData->outputBuffer) {
+        CopyOutputData(outOutputData, clientData, frameCount);
+    }
+
+    PaUtil_EndCpuLoadMeasurement( &clientData->stream->cpuLoadMeasurer, frameCount );
+    
+    if (result == paComplete || result == paAbort) {
+        Pa_StopStream(clientData->stream);
+    }
+}
+
+// This is not for input-only streams, this is for streams where the input device is different from the output device
+// TODO: This needs to store the output data in a buffer, to be written to the device the next time AudioOutputProc is called
+static OSStatus AudioInputProc( AudioDeviceID inDevice,
+                         const AudioTimeStamp* inNow,
+                         const AudioBufferList* inInputData,
+                         const AudioTimeStamp* inInputTime,
+                         AudioBufferList* outOutputData, 
+                         const AudioTimeStamp* inOutputTime,
+                         void* inClientData)
+{
+    PaMacClientData *clientData = (PaMacClientData *)inClientData;
+    PaStreamCallbackTimeInfo *timeInfo = InitializeTimeInfo(inNow, inInputTime, inOutputTime);
+
+    PaUtil_BeginCpuLoadMeasurement( &clientData->stream->cpuLoadMeasurer );
+
+    AudioBuffer const *inputBuffer = &inInputData->mBuffers[0];
+    unsigned long frameCount = inputBuffer->mDataByteSize / (inputBuffer->mNumberChannels * sizeof(Float32));
+
+    CopyInputData(clientData, inInputData, frameCount);
+    clientData->callback(clientData->inputBuffer, NULL, frameCount, timeInfo, paNoFlag, clientData->userData);
+    
+    PaUtil_EndCpuLoadMeasurement( &clientData->stream->cpuLoadMeasurer, frameCount );
+}
+
+// This is not for output-only streams, this is for streams where the input device is different from the output device
+static OSStatus AudioOutputProc( AudioDeviceID inDevice,
+                          const AudioTimeStamp* inNow,
+                          const AudioBufferList* inInputData,
+                          const AudioTimeStamp* inInputTime,
+                          AudioBufferList* outOutputData, 
+                          const AudioTimeStamp* inOutputTime,
+                          void* inClientData)
+{
+    PaMacClientData *clientData = (PaMacClientData *)inClientData;
+    PaStreamCallbackTimeInfo *timeInfo = InitializeTimeInfo(inNow, inInputTime, inOutputTime);
+
+    PaUtil_BeginCpuLoadMeasurement( &clientData->stream->cpuLoadMeasurer );
+
+    AudioBuffer *outputBuffer = &outOutputData->mBuffers[0];
+    unsigned long frameCount = outputBuffer->mDataByteSize / (outputBuffer->mNumberChannels * sizeof(Float32));
+
+    clientData->callback(NULL, clientData->outputBuffer, frameCount, timeInfo, paNoFlag, clientData->userData);
+
+    CopyOutputData(outOutputData, clientData, frameCount);
+
+    PaUtil_EndCpuLoadMeasurement( &clientData->stream->cpuLoadMeasurer, frameCount );
+}
+
+static PaError SetSampleRate(AudioDeviceID device, double sampleRate, int isInput)
+{
+    PaError result = paNoError;
+    
+    double actualSampleRate;
+    UInt32 propSize = sizeof(double);
+    result = conv_err(AudioDeviceSetProperty(device, NULL, 0, isInput, kAudioDevicePropertyNominalSampleRate, propSize, &sampleRate));
+    
+    result = conv_err(AudioDeviceGetProperty(device, 0, isInput, kAudioDevicePropertyNominalSampleRate, &propSize, &actualSampleRate));
+    
+    if (result == paNoError && actualSampleRate != sampleRate) {
+        result = paInvalidSampleRate;
+    }
+    
+    return result;    
+}
+
+static PaError SetFramesPerBuffer(AudioDeviceID device, unsigned long framesPerBuffer, int isInput)
+{
+    PaError result = paNoError;
+    UInt32 preferredFramesPerBuffer = framesPerBuffer;
+    //    while (preferredFramesPerBuffer > UINT32_MAX) {
+    //        preferredFramesPerBuffer /= 2;
+    //    }
+    
+    UInt32 actualFramesPerBuffer;
+    UInt32 propSize = sizeof(UInt32);
+    result = conv_err(AudioDeviceSetProperty(device, NULL, 0, isInput, kAudioDevicePropertyBufferFrameSize, propSize, &preferredFramesPerBuffer));
+    
+    result = conv_err(AudioDeviceGetProperty(device, 0, isInput, kAudioDevicePropertyBufferFrameSize, &propSize, &actualFramesPerBuffer));
+    
+    if (result != paNoError) {
+        // do nothing
+    }
+    else if (actualFramesPerBuffer > framesPerBuffer) {
+        result = paBufferTooSmall;
+    }
+    else if (actualFramesPerBuffer < framesPerBuffer) {
+        result = paBufferTooBig;
+    }
+    
+    return result;    
+}
+    
+static PaError SetUpUnidirectionalStream(AudioDeviceID device, double sampleRate, unsigned long framesPerBuffer, int isInput)
+{
+    PaError err = paNoError;
+    err = SetSampleRate(device, sampleRate, isInput);
+    if( err == paNoError )
+        err = SetFramesPerBuffer(device, framesPerBuffer, isInput);
+    return err;
+}
+
+// =====  PortAudio functions  =====
+#pragma mark PortAudio functions
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif // __cplusplus
+    
+    PaError PaMacCore_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
+    
+#ifdef __cplusplus
+}
+#endif // __cplusplus
+
+static void Terminate( struct PaUtilHostApiRepresentation *hostApi )
+{
+    PaMacCoreHostApiRepresentation *macCoreHostApi = (PaMacCoreHostApiRepresentation*)hostApi;
+    
+    CleanUp(macCoreHostApi);
+}
+
+static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
+                                  const PaStreamParameters *inputParameters,
+                                  const PaStreamParameters *outputParameters,
+                                  double sampleRate )
+{
+    PaMacCoreHostApiRepresentation *macCoreHostApi = (PaMacCoreHostApiRepresentation*)hostApi;
+    PaDeviceInfo *deviceInfo;
+    
+    PaError result = paNoError;
+    if (inputParameters) {
+        deviceInfo = macCoreHostApi->inheritedHostApiRep.deviceInfos[inputParameters->device];
+        if (inputParameters->channelCount > deviceInfo->maxInputChannels)
+            result = paInvalidChannelCount;
+        else if (CheckFormat(macCoreHostApi->macCoreDeviceIds[inputParameters->device], inputParameters, sampleRate, 1) != kAudioHardwareNoError) {
+            result = paInvalidSampleRate;
+        }
+    }
+    if (outputParameters && result == paNoError) {
+        deviceInfo = macCoreHostApi->inheritedHostApiRep.deviceInfos[outputParameters->device];
+        if (outputParameters->channelCount > deviceInfo->maxOutputChannels)
+            result = paInvalidChannelCount;
+        else if (CheckFormat(macCoreHostApi->macCoreDeviceIds[outputParameters->device], outputParameters, sampleRate, 0) != kAudioHardwareNoError) {
+            result = paInvalidSampleRate;
+        }
+    }
+
+    return result;
+}
+
+static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
+                           PaStream** s,
+                           const PaStreamParameters *inputParameters,
+                           const PaStreamParameters *outputParameters,
+                           double sampleRate,
+                           unsigned long framesPerBuffer,
+                           PaStreamFlags streamFlags,
+                           PaStreamCallback *streamCallback,
+                           void *userData )
+{
+    PaError err = paNoError;
+    PaMacCoreHostApiRepresentation *macCoreHostApi = (PaMacCoreHostApiRepresentation *)hostApi;
+    PaMacCoreStream *stream = PaUtil_AllocateMemory(sizeof(PaMacCoreStream));
+    stream->isActive = 0;
+    stream->isStopped = 1;
+    stream->inputDevice = kAudioDeviceUnknown;
+    stream->outputDevice = kAudioDeviceUnknown;
+    
+    PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation,
+                                           ( (streamCallback)
+                                             ? &macCoreHostApi->callbackStreamInterface
+                                             : &macCoreHostApi->blockingStreamInterface ),
+                                           streamCallback, userData );
+    PaUtil_InitializeCpuLoadMeasurer( &stream->cpuLoadMeasurer, sampleRate );
+    
+    *s = (PaStream*)stream;
+    PaMacClientData *clientData = PaUtil_AllocateMemory(sizeof(PaMacClientData));
+    clientData->stream = stream;
+    clientData->callback = streamCallback;
+    clientData->userData = userData;
+    clientData->inputBuffer = 0;
+    clientData->outputBuffer = 0;
+    clientData->ditherGenerator = PaUtil_AllocateMemory(sizeof(PaUtilTriangularDitherGenerator));
+    PaUtil_InitializeTriangularDitherState(clientData->ditherGenerator);
+    
+    if (inputParameters != NULL) {
+        stream->inputDevice = macCoreHostApi->macCoreDeviceIds[inputParameters->device];
+        clientData->inputConverter = PaUtil_SelectConverter(paFloat32, inputParameters->sampleFormat, streamFlags);
+        clientData->inputBuffer = PaUtil_AllocateMemory(Pa_GetSampleSize(inputParameters->sampleFormat) * framesPerBuffer * inputParameters->channelCount);
+        clientData->inputChannelCount = inputParameters->channelCount;
+        clientData->inputSampleFormat = inputParameters->sampleFormat;
+        err = SetUpUnidirectionalStream(stream->inputDevice, sampleRate, framesPerBuffer, 1);
+    }
+    
+    if (err == paNoError && outputParameters != NULL) {
+        stream->outputDevice = macCoreHostApi->macCoreDeviceIds[outputParameters->device];
+        clientData->outputConverter = PaUtil_SelectConverter(outputParameters->sampleFormat, paFloat32, streamFlags);
+        clientData->outputBuffer = PaUtil_AllocateMemory(Pa_GetSampleSize(outputParameters->sampleFormat) * framesPerBuffer * outputParameters->channelCount);
+        clientData->outputChannelCount = outputParameters->channelCount;
+        clientData->outputSampleFormat = outputParameters->sampleFormat;
+        err = SetUpUnidirectionalStream(stream->outputDevice, sampleRate, framesPerBuffer, 0);
+    }
+
+    if (inputParameters == NULL || outputParameters == NULL || stream->inputDevice == stream->outputDevice) {
+        AudioDeviceID device = (inputParameters == NULL) ? stream->outputDevice : stream->inputDevice;
+
+        AudioDeviceAddIOProc(device, AudioIOProc, clientData);
+    }
+    else {
+        // using different devices for input and output
+        AudioDeviceAddIOProc(stream->inputDevice, AudioInputProc, clientData);
+        AudioDeviceAddIOProc(stream->outputDevice, AudioOutputProc, clientData);
+    }
+    
+    return err;
+}
+
+// Note: When CloseStream() is called, the multi-api layer ensures that the stream has already been stopped or aborted.
+static PaError CloseStream( PaStream* s )
+{
+    PaError err = paNoError;
+    PaMacCoreStream *stream = (PaMacCoreStream*)s;
+
+    PaUtil_ResetCpuLoadMeasurer( &stream->cpuLoadMeasurer );
+
+    if (stream->inputDevice != kAudioDeviceUnknown) {
+        if (stream->outputDevice == kAudioDeviceUnknown || stream->outputDevice == stream->inputDevice) {
+            err = conv_err(AudioDeviceRemoveIOProc(stream->inputDevice, AudioIOProc));
+        }
+        else {
+            err = conv_err(AudioDeviceRemoveIOProc(stream->inputDevice, AudioInputProc));
+            err = conv_err(AudioDeviceRemoveIOProc(stream->outputDevice, AudioOutputProc));
+        }
+    }
+    else {
+        err = conv_err(AudioDeviceRemoveIOProc(stream->outputDevice, AudioIOProc));
+    }
+    
+    return err;
+}
+
+
+static PaError StartStream( PaStream *s )
+{
+    PaError err = paNoError;
+    PaMacCoreStream *stream = (PaMacCoreStream*)s;
+
+    if (stream->inputDevice != kAudioDeviceUnknown) {
+        if (stream->outputDevice == kAudioDeviceUnknown || stream->outputDevice == stream->inputDevice) {
+            err = conv_err(AudioDeviceStart(stream->inputDevice, AudioIOProc));
+        }
+        else {
+            err = conv_err(AudioDeviceStart(stream->inputDevice, AudioInputProc));
+            err = conv_err(AudioDeviceStart(stream->outputDevice, AudioOutputProc));
+        }
+    }
+    else {
+        err = conv_err(AudioDeviceStart(stream->outputDevice, AudioIOProc));
+    }
+    
+    stream->isActive = 1;
+    stream->isStopped = 0;
+    return err;
+}
+
+static PaError AbortStream( PaStream *s )
+{
+    PaError err = paNoError;
+    PaMacCoreStream *stream = (PaMacCoreStream*)s;
+    
+    if (stream->inputDevice != kAudioDeviceUnknown) {
+        if (stream->outputDevice == kAudioDeviceUnknown || stream->outputDevice == stream->inputDevice) {
+            err = conv_err(AudioDeviceStop(stream->inputDevice, AudioIOProc));
+        }
+        else {
+            err = conv_err(AudioDeviceStop(stream->inputDevice, AudioInputProc));
+            err = conv_err(AudioDeviceStop(stream->outputDevice, AudioOutputProc));
+        }
+    }
+    else {
+        err = conv_err(AudioDeviceStop(stream->outputDevice, AudioIOProc));
+    }
+    
+    stream->isActive = 0;
+    stream->isStopped = 1;
+    return err;
+}    
+
+static PaError StopStream( PaStream *s )
+{
+    // TODO: this should be nicer than abort
+    return AbortStream(s);
+}
+
+static PaError IsStreamStopped( PaStream *s )
+{
+    PaMacCoreStream *stream = (PaMacCoreStream*)s;
+    
+    return stream->isStopped;
+}
+
+
+static PaError IsStreamActive( PaStream *s )
+{
+    PaMacCoreStream *stream = (PaMacCoreStream*)s;
+
+    return stream->isActive;
+}
+
+
+static PaTime GetStreamTime( PaStream *s )
+{
+    OSStatus err;
+    PaTime result;
+    PaMacCoreStream *stream = (PaMacCoreStream*)s;
+
+    AudioTimeStamp *timeStamp = PaUtil_AllocateMemory(sizeof(AudioTimeStamp));
+    if (stream->inputDevice != kAudioDeviceUnknown) {
+        err = AudioDeviceGetCurrentTime(stream->inputDevice, timeStamp);
+    }
+    else {
+        err = AudioDeviceGetCurrentTime(stream->outputDevice, timeStamp);
+    }
+    
+    result = err ? 0 : timeStamp->mSampleTime;
+    PaUtil_FreeMemory(timeStamp);
+
+    return result;
+}
+
+
+static double GetStreamCpuLoad( PaStream* s )
+{
+    PaMacCoreStream *stream = (PaMacCoreStream*)s;
+    
+    return PaUtil_GetCpuLoad( &stream->cpuLoadMeasurer );
+}
+
+
+// As separate stream interfaces are used for blocking and callback streams, the following functions can be guaranteed to only be called for blocking streams.
+
+static PaError ReadStream( PaStream* s,
+                           void *buffer,
+                           unsigned long frames )
+{
+    return paInternalError;
+}
+
+
+static PaError WriteStream( PaStream* s,
+                            const void *buffer,
+                            unsigned long frames )
+{
+    return paInternalError;
+}
+
+
+static signed long GetStreamReadAvailable( PaStream* s )
+{
+    return paInternalError;
+}
+
+
+static signed long GetStreamWriteAvailable( PaStream* s )
+{
+    return paInternalError;
+}
+
+// HostAPI-specific initialization function
+PaError PaMacCore_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex )
+{
+    PaError result = paNoError;
+    PaMacCoreHostApiRepresentation *macCoreHostApi = (PaMacCoreHostApiRepresentation *)PaUtil_AllocateMemory( sizeof(PaMacCoreHostApiRepresentation) );
+    if( !macCoreHostApi )
+    {
+        result = paInsufficientMemory;
+        goto error;
+    }
+    
+    macCoreHostApi->allocations = PaUtil_CreateAllocationGroup();
+    if( !macCoreHostApi->allocations )
+    {
+        result = paInsufficientMemory;
+        goto error;
+    }
+    
+    *hostApi = &macCoreHostApi->inheritedHostApiRep;
+    (*hostApi)->info.structVersion = 1;
+    (*hostApi)->info.type = paCoreAudio;
+    (*hostApi)->info.name = "CoreAudio";
+
+    result = InitializeDeviceInfos(macCoreHostApi, hostApiIndex);
+    if (result != paNoError) {
+        goto error;
+    }
+    
+    // Set up the proper callbacks to this HostApi's functions
+    (*hostApi)->Terminate = Terminate;
+    (*hostApi)->OpenStream = OpenStream;
+    (*hostApi)->IsFormatSupported = IsFormatSupported;
+    
+    PaUtil_InitializeStreamInterface( &macCoreHostApi->callbackStreamInterface, CloseStream, StartStream,
+                                      StopStream, AbortStream, IsStreamStopped, IsStreamActive,
+                                      GetStreamTime, GetStreamCpuLoad,
+                                      PaUtil_DummyRead, PaUtil_DummyWrite,
+                                      PaUtil_DummyGetReadAvailable, PaUtil_DummyGetWriteAvailable );
+    
+    PaUtil_InitializeStreamInterface( &macCoreHostApi->blockingStreamInterface, CloseStream, StartStream,
+                                      StopStream, AbortStream, IsStreamStopped, IsStreamActive,
+                                      GetStreamTime, PaUtil_DummyGetCpuLoad,
+                                      ReadStream, WriteStream, GetStreamReadAvailable, GetStreamWriteAvailable );
+    
+    return result;
+    
+error:
+        if( macCoreHostApi ) {
+            CleanUp(macCoreHostApi);
+        }
+    
+    return result;
+}
\ No newline at end of file
diff --git a/src/audio/portaudio/pa_mac_sm/pa_mac_sm.c b/src/audio/portaudio/pa_mac_sm/pa_mac_sm.c
new file mode 100644
index 0000000000000000000000000000000000000000..7a8e03009d25cf823c729c795708a21f3be9090b
--- /dev/null
+++ b/src/audio/portaudio/pa_mac_sm/pa_mac_sm.c
@@ -0,0 +1,1656 @@
+/*
+ * $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
+   PLB20020417 - I used to call Pa_GetMinNumBuffers() which doesn't take into account the
+                 variable minFramesPerHostBuffer. Now I call PaMac_GetMinNumBuffers() which will
+                 give lower latency when virtual memory is turned off.
+                 Thanks Kristoffer Jensen and Georgios Marentakis for spotting this bug.
+   PLB20020423 - Use new method to calculate CPU load similar to other ports. Based on num frames calculated.
+                 Fixed Pa_StreamTime(). Now estimates how many frames have played based on MicroSecond timer.
+                 Added PA_MAX_USAGE_ALLOWED to prevent Mac form hanging when CPU load approaches 100%.
+   PLB20020424 - Fixed return value in Pa_StreamTime
+*/
+
+/*
+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
+
+/* #define TARGET_API_MAC_CARBON (1) */
+
+/*
+ * Define maximum CPU load that will be allowed. User callback will
+ * be skipped if load exceeds this limit. This is to prevent the Mac
+ * from hanging when the CPU is hogged by the sound thread.
+ * On my PowerBook G3, the mac hung when I used 94% of CPU ( usage = 0.94 ).
+ */
+#define PA_MAX_USAGE_ALLOWED    (0.92)
+
+/* 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;
+	double                  pahsc_InverseMicrosPerHostBuffer; /* 1/Microseconds of real-time audio per user buffer. */
+
+    /* 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;
+    UInt64                  pahsc_WhenFramesDoneIncremented;
+    /* 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    PaMac_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;
+}
+
+/********************************* BEGIN CPU UTILIZATION MEASUREMENT ****/
+static void PaMac_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 );
+}
+
+/******************************************************************************
+** Measure fractional CPU load based on real-time it took to calculate
+** buffers worth of output.
+*/
+/**************************************************************************/
+static void PaMac_EndLoadCalculation( internalPortAudioStream   *past )
+{
+    UnsignedWide widePad;
+    UInt64    currentCount;
+    long      usecsElapsed;
+    double    newUsage;
+    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.95)
+#define LOWPASS_COEFFICIENT_1   (0.99999 - LOWPASS_COEFFICIENT_0)
+    Microseconds( &widePad );
+    currentCount = UnsignedWideToUInt64( widePad );
+
+	usecsElapsed = (long) U64Subtract(currentCount, pahsc->pahsc_EntryCount);
+	
+        /* Use inverse because it is faster than the divide. */
+	newUsage =  usecsElapsed * pahsc->pahsc_InverseMicrosPerHostBuffer;
+	
+	past->past_Usage = (LOWPASS_COEFFICIENT_0 * past->past_Usage) +
+                           (LOWPASS_COEFFICIENT_1 * newUsage);
+ 
+}
+
+/***********************************************************************
+** 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;
+}
+
+static void SetFramesDone( PaHostSoundControl *pahsc, PaTimestamp framesDone )
+{
+	UnsignedWide     now;
+ 	Microseconds( &now );
+ 	pahsc->pahsc_NumFramesDone = framesDone;
+ 	pahsc->pahsc_WhenFramesDoneIncremented = UnsignedWideToUInt64( now );
+}
+
+/***********************************************************************/
+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;
+    
+    SetFramesDone( pahsc, 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 host Buffers
+* and how many User Buffers we can put into each host 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;
+    
+    minFramesPerHostBuffer = pahsc->pahsc_MinFramesPerHostBuffer;
+    minFramesPerHostBuffer = (minFramesPerHostBuffer + 7) & ~7;
+    DBUG(("PaHost_CalcNumHostBuffers: minFramesPerHostBuffer = %d\n", minFramesPerHostBuffer ));
+    
+    /* Determine number of user buffers based on minimum latency. */
+	/* PLB20020417 I used to call Pa_GetMinNumBuffers() which doesn't take into account the
+	**    variable minFramesPerHostBuffer. Now I call PaMac_GetMinNumBuffers() which will
+	**    gove lower latency when virtual memory is turned off. */
+    /* minNumBuffers = Pa_GetMinNumBuffers( past->past_FramesPerUserBuffer, past->past_SampleRate ); WRONG */
+    minNumBuffers = PaMac_GetMinNumBuffers( minFramesPerHostBuffer, 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 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 host buffers needed. Round up to cover minTotalFrames. */
+    numHostBuffers = (minTotalFrames + framesPerHostBuffer - 1) / framesPerHostBuffer;
+    /* Make sure we have enough host buffers. */
+    if( numHostBuffers < PA_MIN_NUM_HOST_BUFFERS)
+    {
+        numHostBuffers = PA_MIN_NUM_HOST_BUFFERS;
+    }
+    else
+    {
+        /* If we have too many host buffers, try to put more user buffers in a host 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 );
+    
+    /* Setup constants for CPU load measurement. */
+    pahsc->pahsc_InverseMicrosPerHostBuffer = past->past_SampleRate / (1000000.0 * 	pahsc->pahsc_FramesPerHostBuffer);
+
+    /* ------------------ 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 )
+{
+/* We use the MAC_VIRTUAL_FRAMES_PER_BUFFER because we might be recording.
+** This routine doesn't have enough information to determine the best value
+** and is being depracated. */
+    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 )
+{
+	PaTimestamp   framesDone1;
+	PaTimestamp   framesDone2;
+	UInt64        whenIncremented;
+	UnsignedWide  now;
+	UInt64        now64;
+	long          microsElapsed;
+	long          framesElapsed;
+	
+    PaHostSoundControl *pahsc;
+    internalPortAudioStream   *past = (internalPortAudioStream *) stream;
+    if( past == NULL ) return paBadStreamPtr;
+    pahsc = (PaHostSoundControl *) past->past_DeviceData;
+    
+/* Capture information from audio thread.
+ * We have to be careful that we don't get interrupted in the middle.
+ * So we grab the pahsc_NumFramesDone twice and make sure it didn't change.
+ */
+ 	do
+ 	{
+ 		framesDone1 = pahsc->pahsc_NumFramesDone;
+ 		whenIncremented = pahsc->pahsc_WhenFramesDoneIncremented;
+ 		framesDone2 = pahsc->pahsc_NumFramesDone;
+ 	} while( framesDone1 != framesDone2 );
+ 	
+ /* Calculate how many microseconds have elapsed and convert to frames. */
+ 	Microseconds( &now );
+ 	now64 = UnsignedWideToUInt64( now );
+ 	microsElapsed = U64Subtract( now64, whenIncremented );
+ 	framesElapsed = microsElapsed * past->past_SampleRate * 0.000001;
+ 	
+	return framesDone1 + framesElapsed;
+}
+
+/**************************************************************************
+** 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)
+    {
+		SetFramesDone( pahsc,
+			pahsc->pahsc_NumFramesDone + pahsc->pahsc_FramesPerHostBuffer );
+        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) )
+    {
+        PaMac_StartLoadCalculation( past ); /* CPU usage */
+
+#ifdef PA_MAX_USAGE_ALLOWED
+	/* If CPU usage exceeds limit, skip user callback to prevent hanging CPU. */
+		if( past->past_Usage > PA_MAX_USAGE_ALLOWED )
+	    {
+	    	past->past_FrameCount += (PaTimestamp) pahsc->pahsc_FramesPerHostBuffer;
+		}
+		else
+#endif
+		{
+		
+	        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;
+
+	SetFramesDone( pahsc,
+			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/src/audio/portaudio/pa_sgi/pa_sgi.c b/src/audio/portaudio/pa_sgi/pa_sgi.c
new file mode 100644
index 0000000000000000000000000000000000000000..8b742d1ccd63e95b0873907bef1083f091e395e1
--- /dev/null
+++ b/src/audio/portaudio/pa_sgi/pa_sgi.c
@@ -0,0 +1,1417 @@
+/*
+ * $Id$
+ * PortAudio Portable Real-Time Audio Library. 
+ * Latest Version at: http://www.portaudio.com.
+ * Silicon Graphics (SGI) IRIX implementation by Pieter Suurmond.
+ *
+ * Based on the Open Source API proposed by Ross Bencina
+ * Copyright (c) 1999-2002 Ross Bencina, 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.
+ *
+ */
+/** @file
+ @brief SGI IRIX AL implementation (according to V19 API version 2.0).
+
+ @note This file started as a copy of pa_skeleton.c (v 1.1.2.35 2003/09/20), it
+ has nothing to do with the old V18 pa_sgi version: this implementation uses the
+ newer IRIX AL calls and uses pthreads instead of sproc.
+
+ On IRIX, one may type './configure' followed by 'gmake' from the portaudio root 
+ directory to build the static and shared libraries, as well as all the tests.
+
+ On IRIX 6.5, using 'make' instead of 'gmake' may cause Makefile to fail. (This 
+ happens on my machine: make does not understand syntax with 2 colons on a line,
+ like this:
+               $(TESTS): bin/%: [snip]
+
+ Maybe this is due to an old make version(?), my only solution is: use gmake.
+ Anyway, all the tests compile well now, with GCC 3.3, as well as with MIPSpro 7.2.1.
+ Tested:
+        - paqa_devs              ok, but at a certain point digital i/o fails:
+                                     TestAdvance: INPUT, device = 2, rate = 32000, numChannels = 1, format = 1
+                                     Possibly, this is an illegal sr or number of channels for digital i/o.
+        - paqa_errs              13 of the tests run ok, but 5 of them give weird results.
+        + patest1                ok.
+        + patest_buffer          ok.
+        + patest_callbackstop    ok.
+        - patest_clip            ok, but hear no difference between dithering turned OFF and ON.
+        + patest_hang            ok.
+        + patest_latency         ok.
+        + patest_leftright       ok.
+        + patest_maxsines        ok.
+        + patest_many            ok.
+        + patest_multi_sine      ok.
+        + patest_pink            ok.
+        + patest_prime           ok.
+        - patest_read_record     ok, but playback stops a little earlier than 5 seconds it seems(?).
+        + patest_record          ok.
+        + patest_ringmix         ok.
+        + patest_saw             ok.
+        + patest_sine            ok.
+        + patest_sine8           ok.
+        - patest_sine_formats    ok, FLOAT32 + INT16 + INT18 are OK, but UINT8 IS NOT OK!
+        + patest_sine_time       ok.
+        + patest_start_stop      ok, but under/overflow errors of course in the AL queue monitor.
+        + patest_stop            ok.
+        - patest_sync            ok?
+        + patest_toomanysines    ok.
+        - patest_underflow       ok? (stopping after SleepTime = 91: err=Stream is stopped)
+        - patest_wire            ok.
+        + patest_write_sine      ok.
+        + pa_devs                ok.
+                                 Ok on an Indy, in both stereo and quadrophonic mode.
+        + pa_fuzz                ok.
+        + pa_minlat              ok.
+
+ Worked on (or checked) proposals:
+ 
+  003:    Improve Latency Specification OK, but not 100% sure: plus or minus 1 host buffer?
+  004 OK: Allow Callbacks to Accept Variable Number of Frames per Buffer. 
+          Simply using a fixed host buffer size. Very roughly implemented now, the adaption
+          to limited-requested latencies and samplerate may be improved. At least this
+          implementation chooses its own internal host buffer size (no coredumps any longer).
+  005 OK: Blocking Read/Write Interface.
+  006:    Non-interleaved buffers seems OK? Covered by the buffer-processor and such?....
+  009 OK: Host error reporting should now be.
+  010 OK: State Machine and State Querying Functions.
+  011 OK: Renaming done.
+  014     Implementation Style Guidelines (sorry, my braces are not ANSI style).
+  015 OK: Callback Timestamps (During priming, though, these are still null!).
+  016 OK: Use Structs for Pa_OpenStream() Parameters.
+  019:    Notify Client When All Buffers Have Played (Ok, covered by the buffer processor?)
+  020 OK: Allow Callback to prime output stream (StartStream() should do the priming)
+          Should be tested more thoroughly for full duplex streams. (patest_prime seems ok).
+
+
+ @todo Underrun or overflow flags at some more places.
+
+ @todo Callback Timestamps during priming.
+
+ @todo Improve adaption to number of channels, samplerate and such when inventing 
+       some frames per host buffer (when client requests 0).
+
+ @todo Make a complete new version to support 'sproc'-applications.
+       Or could we manage that with some clever if-defs?
+       It must be clear which version we use (especially when using pa as lib!):
+       an irix-sproc() version or pthread version.
+
+ @todo In Makefile.in: 'make clean' does not remove lib/libportaudio.so.0.0.19.
+    
+ Note: Even when mono-output is requested, with ALv7, the audio library opens
+       a outputs stereo. One can observe this in SGI's 'Audio Queue Monitor'.
+*/
+
+#include <string.h>         /* For strlen() but also for strerror()! */
+#include <stdio.h>          /* printf() */
+#include <math.h>           /* fabs()   */
+
+#include <dmedia/audio.h>   /* IRIX AL (audio library). Link with -laudio. */
+#include <dmedia/dmedia.h>  /* IRIX DL (digital media library), solely for */
+                            /* function dmGetUST(). Link with -ldmedia.    */
+#include <errno.h>          /* To catch 'oserror' after AL-calls. */
+#include <pthread.h>        /* POSIX threads. */
+#include <unistd.h>
+
+#include "pa_util.h"
+#include "pa_allocation.h"
+#include "pa_hostapi.h"
+#include "pa_stream.h"
+#include "pa_cpuload.h"
+#include "pa_process.h"
+
+                            /* Uncomment for diagnostics: */
+#define DBUG(x) /*{ printf x; fflush(stdout); }*/
+
+
+/* prototypes for functions declared in this file */
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif /* __cplusplus */
+
+PaError PaSGI_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+
+static void Terminate( struct PaUtilHostApiRepresentation *hostApi );
+static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
+                                  const PaStreamParameters *inputParameters,
+                                  const PaStreamParameters *outputParameters,
+                                  double sampleRate );
+static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
+                           PaStream** s,
+                           const PaStreamParameters *ipp,
+                           const PaStreamParameters *opp,
+                           double sampleRate,
+                           unsigned long framesPerBuffer,
+                           PaStreamFlags streamFlags,
+                           PaStreamCallback *streamCallback,
+                           void *userData );
+static PaError CloseStream( PaStream* stream );
+static PaError StartStream( PaStream *stream );
+static PaError StopStream( PaStream *stream );
+static PaError AbortStream( PaStream *stream );
+static PaError IsStreamStopped( PaStream *s );
+static PaError IsStreamActive( PaStream *stream );
+static PaTime GetStreamTime( PaStream *stream );
+static double GetStreamCpuLoad( PaStream* stream );
+static PaError ReadStream( PaStream* stream, void *buffer, unsigned long frames );
+static PaError WriteStream( PaStream* stream, const void *buffer, unsigned long frames );
+static signed long GetStreamReadAvailable( PaStream* stream );
+static signed long GetStreamWriteAvailable( PaStream* stream );
+
+
+/* 
+    Apparently, we must use macros for reporting unanticipated host errors.    
+    Only in case we return paUnanticipatedHostError from an Portaudio call, 
+    we have to call one of the following three macro's.
+    (Constant paAL is defined in pa_common/portaudio.h. See also proposal 009.)
+
+    After an AL error, use this to translate the AL error code to human text:
+*/
+#define PA_SGI_SET_LAST_AL_ERROR() \
+    {\
+    int ee = oserror();\
+    PaUtil_SetLastHostErrorInfo(paAL, ee, alGetErrorString(ee));\
+    }
+/*
+    But after a generic IRIX error, let strerror() translate the error code from
+    the operating system and use this (strerror() gives the same as perror()):
+*/
+#define PA_SGI_SET_LAST_IRIX_ERROR() \
+    {\
+    int ee = oserror();\
+    PaUtil_SetLastHostErrorInfo(paAL, ee, strerror(ee));\
+    }
+
+/* GOT RID OF calling PaUtil_SetLastHostErrorInfo() with 0 as error number.
+- Weird samplerate difference became:  paInvalidSampleRate.
+- Failing to set AL queue size became: paInternalError
+  (Because I cannot decide between paBufferTooBig and paBufferTooSmall
+   because it may even the 'default AL queue size that failed... Or 
+   should we introduce another error-code like 'paInvalidQueueSize'?... NO)
+*/
+
+/* PaSGIHostApiRepresentation - host api datastructure specific to this implementation */
+
+typedef struct
+{
+    PaUtilHostApiRepresentation   inheritedHostApiRep;
+    PaUtilStreamInterface         callbackStreamInterface;
+    PaUtilStreamInterface         blockingStreamInterface;
+    PaUtilAllocationGroup*        allocations;
+                                                    /* implementation specific data goes here. */
+    ALvalue*                      sgiDeviceIDs;     /* Array of AL resource device numbers.    */
+ /* PaHostApiIndex                hostApiIndex;        Hu? As in the linux and oss files? */
+}
+PaSGIHostApiRepresentation;
+
+/*
+    Initialises sgiDeviceIDs array.
+*/
+PaError PaSGI_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex )
+{
+    PaError                     result = paNoError;
+    int                         e, i, deviceCount, def_in, def_out;
+    PaSGIHostApiRepresentation* SGIHostApi;
+    PaDeviceInfo*               deviceInfoArray;    
+    static const short          numParams = 4;            /* Array with name, samplerate, channels */
+    ALpv                        y[numParams];             /* and type.                             */
+    static const short          maxDevNameChars = 32;     /* Including the terminating null char.  */
+    char                        devName[maxDevNameChars]; /* Too lazy for dynamic alloc.           */
+
+    /* DBUG(("PaSGI_Initialize() started.\n")); */
+    SGIHostApi = (PaSGIHostApiRepresentation*)PaUtil_AllocateMemory(sizeof(PaSGIHostApiRepresentation));
+    if( !SGIHostApi )
+        { result = paInsufficientMemory; goto cleanup; }
+    SGIHostApi->allocations = PaUtil_CreateAllocationGroup();
+    if( !SGIHostApi->allocations )
+        { result = paInsufficientMemory; goto cleanup; }
+    *hostApi = &SGIHostApi->inheritedHostApiRep;
+    (*hostApi)->info.structVersion = 1;
+    (*hostApi)->info.type = paAL;                       /* IRIX AL type id, was paInDevelopment. */
+    (*hostApi)->info.name = "SGI IRIX AL";
+    (*hostApi)->info.defaultInputDevice  = paNoDevice;  /* Set later. */
+    (*hostApi)->info.defaultOutputDevice = paNoDevice;  /* Set later.  */
+    (*hostApi)->info.deviceCount = 0;                   /* We 'll increment in the loop below. */
+    
+    /* Determine the total number of input and output devices (thanks to Gary Scavone). */
+    deviceCount = alQueryValues(AL_SYSTEM, AL_DEVICES, 0, 0, 0, 0);
+    if (deviceCount < 0)        /* Returns -1 in case of failure. */
+        {
+        DBUG(("Failed to count devices: alQueryValues()=%d; %s.\n",
+               deviceCount, alGetErrorString(oserror())));
+        result = paDeviceUnavailable;             /* Is this an appropriate error return code? */
+        goto cleanup;
+        }
+    if (deviceCount > 0)
+        {
+        (*hostApi)->deviceInfos = (PaDeviceInfo**)PaUtil_GroupAllocateMemory(
+                                  SGIHostApi->allocations, sizeof(PaDeviceInfo*) * deviceCount);
+        if (!(*hostApi)->deviceInfos)
+            { result = paInsufficientMemory; goto cleanup; }
+
+        /* Allocate all device info structs in a contiguous block. */
+        deviceInfoArray = (PaDeviceInfo*)PaUtil_GroupAllocateMemory(
+                          SGIHostApi->allocations, sizeof(PaDeviceInfo) * deviceCount);
+        if (!deviceInfoArray)
+            { result = paInsufficientMemory; goto cleanup; }
+                                                             /* Store all AL device IDs in an array. */
+        SGIHostApi->sgiDeviceIDs = (ALvalue*)PaUtil_GroupAllocateMemory(SGIHostApi->allocations,
+                                                                        deviceCount * sizeof(ALvalue));
+        if (!SGIHostApi->sgiDeviceIDs)
+            { result = paInsufficientMemory; goto cleanup; }
+        /* Same query again, but now store all IDs in array sgiDeviceIDs (still using no qualifiers).*/
+        e = alQueryValues(AL_SYSTEM, AL_DEVICES, SGIHostApi->sgiDeviceIDs, deviceCount, 0, 0);
+        if (e != deviceCount)
+            {
+            if (e < 0)                                     /* Sure an AL error really occurred. */
+                { PA_SGI_SET_LAST_AL_ERROR() result = paUnanticipatedHostError; }
+            else                                                 /* Seems we lost some devices. */
+                { DBUG(("Number of devices suddenly changed!\n")); result = paDeviceUnavailable; }
+            goto cleanup;
+            }
+        y[0].param = AL_DEFAULT_INPUT;
+        y[1].param = AL_DEFAULT_OUTPUT;
+        e = alGetParams(AL_SYSTEM, y, 2);       /* Get params global to the AL system. */
+        if (e != 2)
+            {
+            if (e < 0)
+                {
+                PA_SGI_SET_LAST_AL_ERROR()         /* Calls oserror() and alGetErrorString(). */
+                result = paUnanticipatedHostError; /* Sure an AL error really occurred. */
+                }
+            else
+                {
+                DBUG(("Default input and/or output could not be found!\n"));
+                result = paDeviceUnavailable;   /* FIX: What if only in or out are available? */
+                }
+            goto cleanup;
+            }
+        def_in  = y[0].value.i;         /* Remember both AL devices for a while. */
+        def_out = y[1].value.i;
+        y[0].param     = AL_NAME;
+        y[0].value.ptr = devName;
+        y[0].sizeIn    = maxDevNameChars; /* Including terminating null char. */
+        y[1].param     = AL_RATE;
+        y[2].param     = AL_CHANNELS;
+        y[3].param     = AL_TYPE;       /* Subtype of AL_INPUT_DEVICE_TYPE or AL_OUTPUT_DEVICE_TYPE? */
+        for (i=0; i < deviceCount; ++i) /* Fill allocated deviceInfo structs. */
+            {
+            PaDeviceInfo *deviceInfo = &deviceInfoArray[i];
+            deviceInfo->structVersion = 2;
+            deviceInfo->hostApi = hostApiIndex; /* Retrieve name, samplerate, channels and type. */
+            e = alGetParams(SGIHostApi->sgiDeviceIDs[i].i, y, numParams);
+            if (e != numParams)
+                {
+                if (e < 0) /* Calls oserror() and alGetErrorString(). */
+                    { PA_SGI_SET_LAST_AL_ERROR() result = paUnanticipatedHostError; }
+                else
+                    { DBUG(("alGetParams() could not get all params!\n")); result = paInternalError; }
+                goto cleanup;
+                }
+            deviceInfo->name = (char*)PaUtil_GroupAllocateMemory(SGIHostApi->allocations, strlen(devName) + 1);
+            if (!deviceInfo->name)
+                { result = paInsufficientMemory; goto cleanup; }
+            strcpy((char*)deviceInfo->name, devName);
+
+            /* Determine whether the received number of channels belongs to input or output device. */
+            if (alIsSubtype(AL_INPUT_DEVICE_TYPE, y[3].value.i))
+                {
+                deviceInfo->maxInputChannels  = y[2].value.i;
+                deviceInfo->maxOutputChannels = 0;
+                }
+            else if (alIsSubtype(AL_OUTPUT_DEVICE_TYPE, y[3].value.i))
+                {
+                deviceInfo->maxInputChannels  = 0;
+                deviceInfo->maxOutputChannels = y[2].value.i;
+                }
+            else /* Should never occur. */
+                {
+                DBUG(("AL device is neither input nor output!\n"));
+                result = paInternalError;
+                goto cleanup;
+                }
+            
+            /* Determine if this device is the default (in or out). If so, assign. */
+            if (def_in == SGIHostApi->sgiDeviceIDs[i].i)
+                {
+                if ((*hostApi)->info.defaultInputDevice != paNoDevice)
+                    {
+                    DBUG(("Default input already assigned!\n"));
+                    result = paInternalError;
+                    goto cleanup;
+                    }
+                (*hostApi)->info.defaultInputDevice = i;
+                /* DBUG(("Default input assigned to pa device %d (%s).\n", i, deviceInfo->name)); */
+                }
+            else if (def_out == SGIHostApi->sgiDeviceIDs[i].i)
+                {
+                if ((*hostApi)->info.defaultOutputDevice != paNoDevice)
+                    {
+                    DBUG(("Default output already assigned!\n"));
+                    result = paInternalError;
+                    goto cleanup;
+                    }
+                (*hostApi)->info.defaultOutputDevice = i;
+                /* DBUG(("Default output assigned to pa device %d (%s).\n", i, deviceInfo->name)); */
+                }
+            /*---------------------------------------------- Default latencies set to 'reasonable' values. */
+            deviceInfo->defaultLowInputLatency   = 0.050; /* 50 milliseconds seems ok. */
+            deviceInfo->defaultLowOutputLatency  = 0.050; /* These are ALSO ABSOLUTE MINIMA in OpenStream(). */
+            deviceInfo->defaultHighInputLatency  = 0.500; /* 500 milliseconds a reasonable value? */
+            deviceInfo->defaultHighOutputLatency = 0.500; /* Ten times these are ABSOLUTE MAX in OpenStream()). */
+
+            deviceInfo->defaultSampleRate = alFixedToDouble(y[1].value.ll); /* Read current sr. */
+            (*hostApi)->deviceInfos[i] = deviceInfo;
+            ++(*hostApi)->info.deviceCount;
+            }
+        }
+    /* What if (deviceCount==0)? */
+    (*hostApi)->Terminate         = Terminate;
+    (*hostApi)->OpenStream        = OpenStream;
+    (*hostApi)->IsFormatSupported = IsFormatSupported;
+
+    PaUtil_InitializeStreamInterface(&SGIHostApi->callbackStreamInterface, CloseStream, StartStream,
+                                     StopStream, AbortStream, IsStreamStopped, IsStreamActive,
+                                     GetStreamTime, GetStreamCpuLoad,
+                                     PaUtil_DummyRead, PaUtil_DummyWrite,
+                                     PaUtil_DummyGetReadAvailable, PaUtil_DummyGetWriteAvailable );
+
+    PaUtil_InitializeStreamInterface(&SGIHostApi->blockingStreamInterface, CloseStream, StartStream,
+                                     StopStream, AbortStream, IsStreamStopped, IsStreamActive,
+                                     GetStreamTime, PaUtil_DummyGetCpuLoad,
+                                     ReadStream, WriteStream, GetStreamReadAvailable, GetStreamWriteAvailable );
+cleanup:        
+    if (result != paNoError)
+        {
+        if (SGIHostApi)
+            {
+            if (SGIHostApi->allocations)
+                {
+                PaUtil_FreeAllAllocations(SGIHostApi->allocations);
+                PaUtil_DestroyAllocationGroup(SGIHostApi->allocations);
+                }
+            PaUtil_FreeMemory(SGIHostApi);
+            }
+        }
+    return result;
+}
+
+
+static void Terminate( struct PaUtilHostApiRepresentation *hostApi )
+{
+    PaSGIHostApiRepresentation *SGIHostApi = (PaSGIHostApiRepresentation*)hostApi;
+
+    /* Clean up any resources not handled by the allocation group. */
+    if( SGIHostApi->allocations )
+    {
+        PaUtil_FreeAllAllocations( SGIHostApi->allocations );
+        PaUtil_DestroyAllocationGroup( SGIHostApi->allocations );
+    }
+    PaUtil_FreeMemory( SGIHostApi );
+}
+
+/*
+    Check if samplerate is supported for this output device. Called once
+    or twice by function IsFormatSupported() and one time by OpenStream().
+    When paUnanticipatedHostError is returned, the caller does NOT have 
+    to call PA_SGI_SET_LAST_AL_ERROR() or such.
+*/
+static PaError sr_supported(int al_device, double sr)
+{
+    int         e;
+    PaError     result;
+    ALparamInfo pinfo;
+    long long   lsr;    /* 64 bit fixed point internal AL samplerate. */
+    
+    if (alGetParamInfo(al_device, AL_RATE, &pinfo))
+        {
+        e = oserror();
+        DBUG(("alGetParamInfo(AL_RATE) failed: %s.\n", alGetErrorString(e)));
+        if (e == AL_BAD_RESOURCE)
+            result = paInvalidDevice;
+        else
+            {
+            PA_SGI_SET_LAST_AL_ERROR()        /* Sure an AL error occured. */
+            result = paUnanticipatedHostError;
+            }
+        }
+    else
+        {
+        lsr = alDoubleToFixed(sr);  /* Within the range? */
+        if ((pinfo.min.ll <= lsr) && (lsr <= pinfo.max.ll))
+            result = paFormatIsSupported;
+        else
+            result = paInvalidSampleRate;
+        }
+    /* DBUG(("sr_supported()=%d.\n", result)); */
+    return result;
+}
+
+
+/*
+    See common/portaudio.h (suggestedLatency field is ignored).
+*/
+static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
+                                  const PaStreamParameters *inputParameters,
+                                  const PaStreamParameters *outputParameters,
+                                  double sampleRate )
+{
+    PaSGIHostApiRepresentation* SGIHostApi = (PaSGIHostApiRepresentation*)hostApi;
+    int inputChannelCount, outputChannelCount, result;
+    PaSampleFormat inputSampleFormat, outputSampleFormat;
+    
+    if( inputParameters )
+    {
+        inputChannelCount = inputParameters->channelCount;
+        inputSampleFormat = inputParameters->sampleFormat;
+        /* Unless alternate device specification is supported, reject the use of
+           paUseHostApiSpecificDeviceSpecification. */
+        if( inputParameters->device == paUseHostApiSpecificDeviceSpecification )
+            return paInvalidDevice;
+        /* Check that input device can support inputChannelCount. */
+        if( inputChannelCount > hostApi->deviceInfos[ inputParameters->device ]->maxInputChannels )
+            return paInvalidChannelCount;
+        /* Validate inputStreamInfo. */
+        if( inputParameters->hostApiSpecificStreamInfo )
+            return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */
+        /* Check if samplerate is supported for this input device. */
+        result = sr_supported(SGIHostApi->sgiDeviceIDs[inputParameters->device].i, sampleRate);
+        if (result != paFormatIsSupported) /* PA_SGI_SET_LAST_AL_ERROR() may already be called. */
+            return result;
+    }
+    else
+    {
+        inputChannelCount = 0;
+    }
+    if( outputParameters ) /* As with input above. */
+    {
+        outputChannelCount = outputParameters->channelCount;
+        outputSampleFormat = outputParameters->sampleFormat;
+        if( outputParameters->device == paUseHostApiSpecificDeviceSpecification )
+            return paInvalidDevice;
+        if( outputChannelCount > hostApi->deviceInfos[ outputParameters->device ]->maxOutputChannels )
+            return paInvalidChannelCount;
+        if( outputParameters->hostApiSpecificStreamInfo )
+            return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */
+        /* Check if samplerate is supported for this output device. */
+        result = sr_supported(SGIHostApi->sgiDeviceIDs[outputParameters->device].i, sampleRate);
+        if (result != paFormatIsSupported)
+            return result;
+    }
+    else
+    {
+        outputChannelCount = 0;
+    }
+    /*  IMPLEMENT ME:
+        Because the buffer adapter handles conversion between all standard
+        sample formats, the following checks are only required if paCustomFormat
+        is implemented, or under some other unusual conditions.
+
+            - check that input device can support inputSampleFormat, or that
+              we have the capability to convert from outputSampleFormat to
+              a native format
+
+            - check that output device can support outputSampleFormat, or that
+              we have the capability to convert from outputSampleFormat to
+              a native format
+    */
+    /* suppress unused variable warnings */
+    (void) inputSampleFormat;
+    (void) outputSampleFormat;
+    return paFormatIsSupported;
+}
+
+/** Auxilary struct, embedded twice in the struct below, for inputs and outputs. */
+typedef struct PaSGIhostPortBuffer
+{
+            /** NULL means IRIX AL port closed. */
+    ALport  port;
+            /** NULL means memory not allocated. */
+    void*   buffer;
+}
+    PaSGIhostPortBuffer;
+
+/** Stream data structure specifically for this IRIX AL implementation. */
+typedef struct PaSGIStream
+{
+    PaUtilStreamRepresentation  streamRepresentation;
+    PaUtilCpuLoadMeasurer       cpuLoadMeasurer;
+    PaUtilBufferProcessor       bufferProcessor;
+    unsigned long               framesPerHostCallback;
+                                /** Allocated host buffers and AL ports. */
+    PaSGIhostPortBuffer         hostPortBuffIn,
+                                hostPortBuffOut;
+                                /** Copy of stream flags given to OpenStream(). */
+    PaStreamFlags               streamFlags;
+                                /** Stream state may be 0 or 1 or 2, but never 3. */
+    unsigned char               state;
+                                /** Requests to stop or abort may come from the parent,
+                                    or from the child itself (user callback result). */
+    unsigned char               stopAbort;
+    pthread_t                   thread;
+}
+    PaSGIStream;
+
+/** Stream can be in only one of the following three states: stopped (1), active (2), or
+    callback finshed (0). To prevent 'state 3' from occurring, Setting and testing of the
+    state bits is done atomically.
+*/
+#define PA_SGI_STREAM_FLAG_FINISHED_ (0) /* After callback finished or cancelled queued buffers. */
+#define PA_SGI_STREAM_FLAG_STOPPED_  (1) /* Set by OpenStream(), StopStream() and AbortStream(). */
+#define PA_SGI_STREAM_FLAG_ACTIVE_   (2) /* Set by StartStream. Reset by OpenStream(),           */
+                                         /* StopStream() and AbortStream().                      */
+
+/** Stop requests, via the 'stopAbort' field can be either 1, meaning 'stop' or 2, meaning 'abort'.
+    When both occur at the same time, 'abort' takes precedence, even after a first 'stop'.
+*/
+#define PA_SGI_REQ_CONT_    (0)         /* Reset by OpenStream(), StopStream and AbortStream. */
+#define PA_SGI_REQ_STOP_    (1)         /* Set by StopStream(). */
+#define PA_SGI_REQ_ABORT_   (2)         /* Set by AbortStream(). */
+
+
+/** Called by OpenStream() once or twice. First, the number of channels, sampleformat, and
+    queue size are configured. The configuration is then bound to the specified AL device. 
+    Then an AL port is opened. Finally, the samplerate of the device is altered (or at least
+    set again).
+    
+    After successful return, actual latency is written in *latency, and actual samplerate 
+    in *samplerate.
+
+    @param pa_params may be NULL and pa_params->channelCount may also be null, in both 
+           cases the function immediately returns.
+    @return paNoError if configuration was skipped or if it succeeded.
+*/
+static PaError set_sgi_device(ALvalue*                  sgiDeviceIDs,   /* Array built by PaSGI_Initialize(). */
+                              const PaStreamParameters* pa_params,      /* read device and channels. */                             
+                              double*                   latency,        /* Read and write in seconds. */
+                              
+                              PaSampleFormat            pasfmt,         /* Don't read from pa_params!. */
+                              char*                     direction,      /* "r" or "w". */
+                              char*                     name,
+                              long                      framesPerHostBuffer,
+                              double*                   samplerate,     /* Also writes back here. */
+                              PaSGIhostPortBuffer*      hostPortBuff)   /* Receive pointers here. */
+{
+    int       bytesPerFrame, sgiDevice, alErr, d, dd, iq_size, default_iq_size;
+    ALpv      pvs[2];
+    ALconfig  alc = NULL;
+    PaError   result = paNoError;
+
+    if (!pa_params)
+        goto cleanup;                  /* Not errors, just not full duplex, skip all. */
+    if (!pa_params->channelCount)
+        goto cleanup;
+    alc = alNewConfig();    /* Create default config. This defaults to stereo, 16-bit integer data. */
+    if (!alc)               /* Call alFreeConfig() later, when done with it. */
+        { result = paInsufficientMemory;  goto cleanup; }
+    /*----------------------- CONFIGURE NUMBER OF CHANNELS: ---------------------------*/
+    if (alSetChannels(alc, pa_params->channelCount))          /* Returns 0 on success. */
+        {
+        if (oserror() == AL_BAD_CHANNELS)
+            result = paInvalidChannelCount;
+        else
+            {
+            PA_SGI_SET_LAST_AL_ERROR()
+            result = paUnanticipatedHostError;
+            }
+        goto cleanup;
+        }
+    bytesPerFrame = pa_params->channelCount;          /* Is multiplied by width below. */
+    /*----------------------- CONFIGURE SAMPLE FORMAT: --------------------------------*/
+    if (pasfmt == paFloat32)
+        {
+        if (alSetSampFmt(alc, AL_SAMPFMT_FLOAT))
+            {
+            if (oserror() == AL_BAD_SAMPFMT)
+                result = paSampleFormatNotSupported;
+            else
+                {
+                PA_SGI_SET_LAST_AL_ERROR()
+                result = paUnanticipatedHostError; 
+                }
+            goto cleanup;
+            }
+        bytesPerFrame *= 4;             /* No need to set width for floats. */
+        }
+    else
+        {
+        if (alSetSampFmt(alc, AL_SAMPFMT_TWOSCOMP))
+            {
+            if (oserror() == AL_BAD_SAMPFMT)
+                result = paSampleFormatNotSupported;
+            else
+                {
+                PA_SGI_SET_LAST_AL_ERROR()
+                result = paUnanticipatedHostError;
+                }
+            goto cleanup;
+            }
+        if (pasfmt == paInt8)
+            {
+            if (alSetWidth(alc, AL_SAMPLE_8))
+                {
+                if (oserror() == AL_BAD_WIDTH)
+                    result = paSampleFormatNotSupported;
+                else
+                    {
+                    PA_SGI_SET_LAST_AL_ERROR()
+                    result = paUnanticipatedHostError;
+                    }
+                goto cleanup;
+                }
+            /* bytesPerFrame *= 1; */
+            }
+        else if (pasfmt == paInt16)
+            {
+            if (alSetWidth(alc, AL_SAMPLE_16))
+                {
+                if (oserror() == AL_BAD_WIDTH)
+                    result = paSampleFormatNotSupported;
+                else
+                    {
+                    PA_SGI_SET_LAST_AL_ERROR()
+                    result = paUnanticipatedHostError;
+                    }
+                goto cleanup;
+                }
+            bytesPerFrame *= 2;
+            }
+        else if (pasfmt == paInt24)
+            {
+            if (alSetWidth(alc, AL_SAMPLE_24))
+                {
+                if (oserror() == AL_BAD_WIDTH)
+                    result = paSampleFormatNotSupported;
+                else
+                    {
+                    PA_SGI_SET_LAST_AL_ERROR()
+                    result = paUnanticipatedHostError;
+                    }
+                goto cleanup;
+                }
+            bytesPerFrame *= 3;   /* OR 4 ???????! */
+            }
+        else return paSampleFormatNotSupported;
+        }
+    /*----------------------- SET INTERNAL AL QUEUE SIZE: -------------------------------*/
+    /*  The AL API doesn't provide a means for querying minimum and maximum buffer sizes.
+        So, if the requested size fails, try again with a value that is closer to the AL's 
+        default queue size. In this implementation, 'Portaudio latency' corresponds to
+        the AL queue size minus one buffersize:
+                                                 AL queue size - framesPerHostBuffer
+                                    PA latency = -----------------------------------
+                                                            sample rate                  */
+    default_iq_size = alGetQueueSize(alc);
+    if (default_iq_size < 0)     /* So let's first get that 'default size'. */
+        {                        /* Default internal queue size could not be determined. */
+        PA_SGI_SET_LAST_AL_ERROR()
+        result = paUnanticipatedHostError;
+        goto cleanup;
+        }
+    /* AL buffer becomes somewhat bigger than the suggested latency, notice this is   */
+    /* based on requsted samplerate, not in the actual rate, which is measured later. */
+    /* Do NOT read pa_params->suggestedLatency, but use the limited *latency param!   */
+    
+    iq_size = (int)(0.5 + ((*latency) * (*samplerate))) + (int)framesPerHostBuffer;
+                                                /* The AL buffer becomes somewhat     */
+                                                /* bigger than the suggested latency. */
+    if (iq_size < (framesPerHostBuffer << 1))   /* Make sure the minimum is twice     */
+        {                                       /* framesPerHostBuffer.               */
+        DBUG(("Setting minimum queue size.\n"));
+        iq_size = (framesPerHostBuffer << 1);
+        }
+    d = iq_size - default_iq_size;                 /* Determine whether we'll decrease */
+    while (alSetQueueSize(alc, iq_size))           /* or increase after failing.       */
+        {                                          /* Size in sample frames.           */
+        if (oserror() != AL_BAD_QSIZE)                       /* Stop at AL_BAD_CONFIG. */
+            {
+            PA_SGI_SET_LAST_AL_ERROR()
+            result = paUnanticipatedHostError;
+            goto cleanup;
+            }
+        dd = iq_size - default_iq_size;     /* Stop when even the default size failed  */
+        if (((d >= 0) && (dd <= 0)) ||      /* (dd=0) or when difference flipped sign. */
+            ((d <= 0) && (dd >= 0)) ||
+            (iq_size <= framesPerHostBuffer))  /* Also guarentee that framesPerHostBuffer */
+            {                                  /* can be subtracted (res>0) after return. */
+            DBUG(("Could not set AL queue size to %d sample frames!\n", iq_size));
+            result = paInternalError; /* FIX: PROBABLY AN INAPROPRIATE ERROR CODE HERE.   */
+            goto cleanup;             /* As inapropriate as paUnanticipatedHostError was? */
+            }
+        DBUG(("Failed to set internal queue size to %d frames, ", iq_size));
+        if (d > 0)
+            iq_size -= framesPerHostBuffer;    /* Try lesser multiple. */
+        else
+            iq_size += framesPerHostBuffer;    /* Try larger multiple. */
+        DBUG(("trying %d frames now...\n", iq_size));
+        }
+    /* Note: Actual latency is written back to *latency after meausuring actual (not
+             the requested) samplerate. See below. 
+    */
+    /*----------------------- ALLOCATE HOST BUFFER: --------------------------------------*/
+    hostPortBuff->buffer = PaUtil_AllocateMemory((long)bytesPerFrame * framesPerHostBuffer);
+    if (!hostPortBuff->buffer) /* Caller is responsible for cleanup+close after failures! */
+        { result = paInsufficientMemory; goto cleanup; }
+    /*----------------------- BIND CONFIGURATION TO DEVICE: ------------------------------*/
+    sgiDevice = sgiDeviceIDs[pa_params->device].i;
+    if (alSetDevice(alc, sgiDevice)) /* Try to switch the hardware. */
+        {
+        if (oserror() == AL_BAD_DEVICE)
+            result = paInvalidDevice;
+        else
+            {
+            PA_SGI_SET_LAST_AL_ERROR()
+            result = paUnanticipatedHostError;
+            }
+        goto cleanup;
+        }
+    /*----------------------- OPEN PORT: ----------------------------------------------*/
+    hostPortBuff->port = alOpenPort(name, direction, alc);  /* Caller is responsible   */
+    if (!hostPortBuff->port)                                /* for closing after fail. */
+        {
+        PA_SGI_SET_LAST_AL_ERROR()
+        result = paUnanticipatedHostError;
+        goto cleanup;
+        }                                                     /* Maybe set SR earlier? */
+    /*----------------------- SET SAMPLERATE: -----------------------------------------*/
+    pvs[0].param    = AL_MASTER_CLOCK;       /* Attempt to set a crystal-based sample- */
+    pvs[0].value.i  = AL_CRYSTAL_MCLK_TYPE;  /* rate on input or output device.        */
+    pvs[1].param    = AL_RATE;
+    pvs[1].value.ll = alDoubleToFixed(*samplerate);
+    if (2 != alSetParams(sgiDevice, pvs, 2))
+        {
+        DBUG(("alSetParams() failed to set samplerate to %.4f Hz!\n", *samplerate));
+        result = paInvalidSampleRate;
+        goto cleanup;
+        }
+    /*----------------------- GET ACTUAL SAMPLERATE: ---------------------------*/
+    alErr = alGetParams(sgiDevice, &pvs[1], 1); /* SEE WHAT WE REALY SET IT TO. */
+    if (alErr != 1)                             /* And return that to caller.   */
+        {
+        DBUG(("alGetParams() failed to read samplerate!\n"));
+        result = paInvalidSampleRate;
+        goto cleanup;
+        }
+    *samplerate = alFixedToDouble(pvs[1].value.ll);  /* Between 1 Hz and 1 MHz. */
+    if ((*samplerate < 1.0) || (*samplerate > 1000000.0))
+        {
+        DBUG(("alFixedToDouble() resulted a weird samplerate: %.6f Hz!\n", *samplerate));
+        result = paInvalidSampleRate;
+        goto cleanup;
+        }
+    /*----------------------- CALC ACTUAL LATENCY (based on actual SR): -----------------------*/
+    *latency = (iq_size - framesPerHostBuffer) / (*samplerate);         /* FIX:  SURE > 0!???? */
+cleanup:
+    if (alc)
+        alFreeConfig(alc); /* We no longer need configuration. */
+    return result;
+}
+
+/**
+    Called by OpenStream() if it fails and by CloseStream. Only used here, in this file.
+    Fields MUST be set to NULL or to a valid value, prior to call.
+*/
+static void streamCleanupAndClose(PaSGIStream* stream)
+{
+    if (stream->hostPortBuffIn.port)    alClosePort(stream->hostPortBuffIn.port);         /* Close AL ports.  */
+    if (stream->hostPortBuffIn.buffer)  PaUtil_FreeMemory(stream->hostPortBuffIn.buffer); /* Release buffers. */
+    if (stream->hostPortBuffOut.port)   alClosePort(stream->hostPortBuffOut.port);
+    if (stream->hostPortBuffOut.buffer) PaUtil_FreeMemory(stream->hostPortBuffOut.buffer);
+}
+
+
+/* See pa_hostapi.h for a list of validity guarantees made about OpenStream parameters. */
+static PaError OpenStream(struct PaUtilHostApiRepresentation* hostApi,
+                          PaStream**                          s,
+                          const PaStreamParameters*           ipp,
+                          const PaStreamParameters*           opp,
+                          double                              sampleRate, /* Common to both i and o. */
+                          unsigned long                       framesPerBuffer,
+                          PaStreamFlags                       streamFlags,
+                          PaStreamCallback*                   streamCallback,
+                          void*                               userData)
+{
+    PaError                     result = paNoError;
+    PaSGIHostApiRepresentation* SGIHostApi = (PaSGIHostApiRepresentation*)hostApi;
+    PaSGIStream*                stream = 0;
+    unsigned long               framesPerHostBuffer;   /* Not necessarily the same as framesPerBuffer. */
+    int                         inputChannelCount,     outputChannelCount;
+    PaSampleFormat              inputSampleFormat,     outputSampleFormat,
+                                hostInputSampleFormat, hostOutputSampleFormat;
+    double                      sr_in,                 sr_out,
+                                latency_in,            latency_out;
+    static const PaSampleFormat irixFormats = (paInt8 | paInt16 | paInt24 | paFloat32);
+    /* Constant used by PaUtil_SelectClosestAvailableFormat(). Because IRIX AL does not
+       provide a way to query for possible formats for a given device, interface or port,
+       just add together the formats we know that are supported in general by IRIX AL 
+       (at the end of the year 2003): AL_SAMPFMT_TWOSCOMP with AL_SAMPLE_8(=paInt8),
+       AL_SAMPLE_16(=paInt16) or AL_SAMPLE_24(=paInt24); AL_SAMPFMT_FLOAT(=paFloat32); 
+       AL_SAMPFMT_DOUBLE(=paFloat64); IRIX misses unsigned 8 and 32 bit signed ints.
+    */
+    DBUG(("OpenStream() started.\n"));
+    if (ipp)
+        {
+        inputChannelCount = ipp->channelCount;
+        inputSampleFormat = ipp->sampleFormat;
+        /* Unless alternate device specification is supported, reject the use of paUseHostApiSpecificDeviceSpecification. */
+        if (ipp->device == paUseHostApiSpecificDeviceSpecification) /* DEVICE CHOOSEN BY CLIENT. */
+            return paInvalidDevice;
+        /* Check that input device can support inputChannelCount. */
+        if (inputChannelCount > hostApi->deviceInfos[ipp->device]->maxInputChannels)
+            return paInvalidChannelCount;
+        /* Validate inputStreamInfo. */
+        if (ipp->hostApiSpecificStreamInfo)
+            return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */
+        hostInputSampleFormat = PaUtil_SelectClosestAvailableFormat(irixFormats, inputSampleFormat);
+        /* Check if samplerate is supported for this input device. */
+        result = sr_supported(SGIHostApi->sgiDeviceIDs[ipp->device].i, sampleRate);
+        if (result != paFormatIsSupported) /* PA_SGI_SET_LAST_AL_ERROR() may already be called. */
+            return result;
+        /* Validate input latency. Use defaults if necessary. */
+        if (ipp->suggestedLatency < hostApi->deviceInfos[ipp->device]->defaultLowInputLatency)
+            latency_in = hostApi->deviceInfos[ipp->device]->defaultLowInputLatency;         /* Low = minimum. */
+        else if (ipp->suggestedLatency > 10.0 * hostApi->deviceInfos[ipp->device]->defaultHighInputLatency)
+            latency_in = 10.0 * hostApi->deviceInfos[ipp->device]->defaultHighInputLatency; /* 10*High = max. */
+        else
+            latency_in = ipp->suggestedLatency;
+        }
+    else
+        {
+        inputChannelCount = 0;
+        inputSampleFormat = hostInputSampleFormat = paInt16; /* Surpress 'uninitialised var' warnings. */
+        latency_in = 0.0; /* Necessary? */
+        }
+    if (opp)
+        {
+        outputChannelCount = opp->channelCount;        
+        outputSampleFormat = opp->sampleFormat;
+        if (opp->device == paUseHostApiSpecificDeviceSpecification) /* Like input (above). */
+            return paInvalidDevice;
+        if (outputChannelCount > hostApi->deviceInfos[opp->device]->maxOutputChannels)
+            return paInvalidChannelCount;
+        if (opp->hostApiSpecificStreamInfo )
+            return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */
+        hostOutputSampleFormat = PaUtil_SelectClosestAvailableFormat(irixFormats, outputSampleFormat);
+        /* Check if samplerate is supported for this output device. */
+        result = sr_supported(SGIHostApi->sgiDeviceIDs[opp->device].i, sampleRate);
+        if (result != paFormatIsSupported)
+            return result;
+        /* Validate output latency. Use defaults if necessary. */
+        if (opp->suggestedLatency < hostApi->deviceInfos[opp->device]->defaultLowOutputLatency)
+            latency_out = hostApi->deviceInfos[opp->device]->defaultLowOutputLatency;         /* Low = minimum. */
+        else if (opp->suggestedLatency > 10.0 * hostApi->deviceInfos[opp->device]->defaultHighOutputLatency)
+            latency_out = 10.0 * hostApi->deviceInfos[opp->device]->defaultHighOutputLatency; /* 10*High = max. */
+        else
+            latency_out = opp->suggestedLatency;
+        }
+    else
+        {
+        outputChannelCount = 0;
+        outputSampleFormat = hostOutputSampleFormat = paInt16; /* Surpress 'uninitialised var' warning. */
+        latency_out = 0.0;
+        }
+    /*  Sure that ipp and opp will never be both NULL. */
+
+    if( (streamFlags & paPlatformSpecificFlags) != 0 )  /* Validate platform specific flags.  */
+        return paInvalidFlag;                           /* Unexpected platform specific flag. */
+
+    stream = (PaSGIStream*)PaUtil_AllocateMemory( sizeof(PaSGIStream) );
+    if (!stream)
+        { result = paInsufficientMemory; goto cleanup; }
+
+    stream->hostPortBuffIn.port    = (ALport)NULL;       /* Ports closed.   */
+    stream->hostPortBuffIn.buffer  =         NULL;       /* No buffers yet. */
+    stream->hostPortBuffOut.port   = (ALport)NULL;
+    stream->hostPortBuffOut.buffer =         NULL;
+
+    if (streamCallback)
+        PaUtil_InitializeStreamRepresentation(&stream->streamRepresentation,
+               &SGIHostApi->callbackStreamInterface, streamCallback, userData);
+    else
+        PaUtil_InitializeStreamRepresentation(&stream->streamRepresentation,
+               &SGIHostApi->blockingStreamInterface, streamCallback, userData);
+                                                     /* (NULL.) */
+    if (framesPerBuffer == paFramesPerBufferUnspecified)            /* Proposal 004. */
+        { /* Keep framesPerBuffer zero but come up with some fixed host buffer size. */
+        double  lowest_lat = 0.0; /* 0.0 to surpress uninit warning, we're sure it will end up higher. */
+        if (ipp)
+            lowest_lat = latency_in;            /* Sure > 0.0! */
+        if (opp && (latency_out < lowest_lat))
+            lowest_lat = latency_out;
+        /* So that queue size becomes approximately 5 times framesPerHostBuffer: */
+        framesPerHostBuffer = (unsigned long)((lowest_lat * sampleRate) / 4.0);
+        /* But always limit: */
+        if (framesPerHostBuffer < 64L)
+            framesPerHostBuffer = 64L;
+        else if (framesPerHostBuffer > 32768L)
+            framesPerHostBuffer = 32768L;
+        DBUG(("Decided to use a fixed host buffer size of %ld frames.\n", framesPerHostBuffer));
+        }
+    else
+        framesPerHostBuffer = framesPerBuffer; /* Then just take the requested amount. No buffer-adaption yet? */
+
+    sr_in = sr_out = sampleRate;
+    /*-------------------------------------------------------------------------------------------*/
+    result = set_sgi_device(SGIHostApi->sgiDeviceIDs, /* Needed by alSetDevice and other functs. */
+                            ipp,                      /* Reads channelCount, device but NOT latency. */
+                            &latency_in,              /* Read limited requested latency but also WRITE actual. */
+                            hostInputSampleFormat,    /* For alSetSampFmt and alSetWidth. */                            
+                            "r",                      /* "r" for reading from input port. */
+                            "portaudio in",           /* Name string. */
+                            framesPerHostBuffer,      /* As calculated or as requested by the client. */
+                            &sr_in,                   /* Receive actual s.rate after setting it. */
+                            &stream->hostPortBuffIn); /* Receives ALport and input host buffer.  */
+    if (result != paNoError) goto cleanup;
+    /*-------------------------------------------------------------------------------------------*/
+    result = set_sgi_device(SGIHostApi->sgiDeviceIDs,
+                            opp,
+                            &latency_out,
+                            hostOutputSampleFormat,
+                            "w",                      /* "w" for writing. */
+                            "portaudio out",
+                            framesPerHostBuffer,
+                            &sr_out,
+                            &stream->hostPortBuffOut);
+    if (result != paNoError) goto cleanup;
+    /*------------------------------------------------------------------------------------------*/
+    if (fabs(sr_in - sr_out) > 0.001)                         /* Make sure both are the 'same'. */
+        {
+        DBUG(("Weird samplerate difference between input and output!\n"));
+        result = paInvalidSampleRate;            /* Could not come up with a better error code. */
+        goto cleanup;
+        }                                                                 /* sr_in '==' sr_out. */
+    sampleRate = sr_in;                  /* Following fields set to estimated or actual values: */
+    stream->streamRepresentation.streamInfo.sampleRate    = sampleRate;
+    stream->streamRepresentation.streamInfo.inputLatency  = latency_in;  /* 0.0 if output only. */
+    stream->streamRepresentation.streamInfo.outputLatency = latency_out; /* 0.0 if input only.  */
+
+    PaUtil_InitializeCpuLoadMeasurer(&stream->cpuLoadMeasurer, sampleRate);
+    result = PaUtil_InitializeBufferProcessor(&stream->bufferProcessor,
+                    inputChannelCount,   inputSampleFormat,  hostInputSampleFormat,
+                    outputChannelCount,  outputSampleFormat, hostOutputSampleFormat,
+                    sampleRate,          streamFlags,
+                    framesPerBuffer,           /* As requested by OpenStream(), may be zero! */
+                    framesPerHostBuffer,       /* Use fixed number of frames per host buffer */
+                    paUtilFixedHostBufferSize, /* to keep things simple. See pa_common/pa_   */
+                    streamCallback, userData); /* process.h for more hostbuffersize options. */
+    if (result != paNoError)
+        goto cleanup;
+
+    stream->framesPerHostCallback = framesPerHostBuffer;
+    stream->streamFlags           = streamFlags;                  /* Remember priming request. */
+    stream->state                 = PA_SGI_STREAM_FLAG_STOPPED_;  /* After opening, the stream */
+    stream->stopAbort             = PA_SGI_REQ_CONT_;             /* is in the stopped state.  */
+    *s = (PaStream*)stream;         /* Pass object to caller. */
+cleanup:
+    if (result != paNoError)        /* Always set result when jumping to cleanup after failure. */
+        {
+        if (stream)
+            {
+            streamCleanupAndClose(stream); /* Frees i/o buffers and closes AL ports. */
+            PaUtil_FreeMemory(stream);
+            }
+        }
+    return result;
+}
+
+/** POSIX thread that performs the actual i/o and calls the client's callback,
+    spawned by StartStream().
+*/
+static void* PaSGIpthread(void *userData)
+{
+    PaSGIStream*              stream = (PaSGIStream*)userData;
+    int                       callbackResult = paContinue;
+    double                    nanosec_per_frame;
+    PaStreamCallbackTimeInfo  timeInfo = { 0, 0, 0 };
+
+    stream->state = PA_SGI_STREAM_FLAG_ACTIVE_;   /* Parent thread also sets active flag, but we 
+                                                     make no assumption about who does this first. */
+    nanosec_per_frame = 1000000000.0 / stream->streamRepresentation.streamInfo.sampleRate;
+    /*----------------------------------------------- OUTPUT PRIMING: -----------------------------*/
+    if (stream->hostPortBuffOut.port)              /* Somewhat less than AL queue size so the next */
+        {                                          /* output buffer will (probably) not block.     */
+        unsigned long frames_to_prime = (long)(0.5 + 
+                                        (stream->streamRepresentation.streamInfo.outputLatency
+                                         * stream->streamRepresentation.streamInfo.sampleRate));
+        if (stream->streamFlags & paPrimeOutputBuffersUsingStreamCallback)
+          {
+          PaStreamCallbackFlags cbflags = paPrimingOutput;
+          if (stream->hostPortBuffIn.port) /* Only set this flag in case of full duplex. */
+            cbflags |= paInputUnderflow;
+          DBUG(("Prime with client's callback: < %ld frames.\n", frames_to_prime));
+          while (frames_to_prime >= stream->framesPerHostCallback)  /* We will not do less (yet).  */
+            {                                                     /* TODO: Timestamps and CPU load */
+            PaUtil_BeginBufferProcessing(&stream->bufferProcessor,  /* measurement during priming. */
+                                         &timeInfo,
+                                         cbflags);             /* Pass underflow + priming flags.  */
+            if (stream->hostPortBuffIn.port)                   /* Does that provide client's call- */
+                PaUtil_SetNoInput(&stream->bufferProcessor);   /* back with silent inputbuffers?   */
+            
+            PaUtil_SetOutputFrameCount(&stream->bufferProcessor, 0);   /* 0=take host buffer size. */
+            PaUtil_SetInterleavedOutputChannels(&stream->bufferProcessor, 0,
+                                                 stream->hostPortBuffOut.buffer, 0);
+            callbackResult = paContinue;                            /* Call the client's callback. */
+            frames_to_prime -= PaUtil_EndBufferProcessing(&stream->bufferProcessor, &callbackResult);
+            if (callbackResult == paAbort)
+                {                                           /* What should we do in other cases    */
+                stream->stopAbort = PA_SGI_REQ_ABORT_;      /* where (callbackResult!=paContinue). */
+                break; /* Don't even output the samples just returned (also skip following while). */
+                }
+            else                                       /* Write interleaved samples to SGI device. */
+                alWriteFrames(stream->hostPortBuffOut.port, stream->hostPortBuffOut.buffer, 
+                              stream->framesPerHostCallback);
+            }
+          }
+        else /* Prime with silence.  */
+            {
+            DBUG(("Prime with silence: %ld frames.\n", frames_to_prime));
+            alZeroFrames(stream->hostPortBuffOut.port, frames_to_prime);
+            }
+        }
+    /*------------------------------------------------------ I/O: ---------------------------------*/
+    while (!stream->stopAbort)         /* Exit loop immediately when 'stop' or 'abort' are raised. */
+        {
+        unsigned long   framesProcessed;
+        stamp_t         fn, t, fnd, td;   /* Unsigned 64 bit. */
+        
+        PaUtil_BeginCpuLoadMeasurement( &stream->cpuLoadMeasurer );
+        /* IMPLEMENT ME: - handle buffer slips. */
+        if (stream->hostPortBuffIn.port)
+            {
+            /*  Get device sample frame number associated with next audio sample frame
+                we're going to read from this port. */
+            alGetFrameNumber(stream->hostPortBuffIn.port, &fn);
+            /*  Get some recent pair of (frame number, time) from the audio device to 
+                which our port is connected. time is 'UST' which is given in nanoseconds 
+                and shared with the other audio devices and with other media. */
+            alGetFrameTime(stream->hostPortBuffIn.port, &fnd, &td);
+            /*  Calculate UST associated with fn, the next sample frame we're going to read or
+                write. Because this is signed arithmetic, code works for both inputs and outputs. */
+            t = td + (stamp_t) ((double)(fn - fnd) * nanosec_per_frame);
+            /*  If port is not in underflow or overflow state, we can alReadFrames() or 
+                alWriteFrames() here and know that t is the time associated with the first 
+                sample frame of the buffer we read or write. */
+            timeInfo.inputBufferAdcTime = ((PaTime)t) / 1000000000.0;
+            /* Read interleaved samples from AL port (I think it will block only the first time). */
+            alReadFrames(stream->hostPortBuffIn.port, stream->hostPortBuffIn.buffer, 
+                         stream->framesPerHostCallback);
+            }
+        if (stream->hostPortBuffOut.port)
+            {
+            alGetFrameNumber(stream->hostPortBuffOut.port, &fn);
+            alGetFrameTime(stream->hostPortBuffOut.port, &fnd, &td);
+            t = td + (stamp_t) ((double)(fn - fnd) * nanosec_per_frame);
+            timeInfo.outputBufferDacTime = ((PaTime)t) / 1000000000.0;
+            }
+        dmGetUST((unsigned long long*)(&t));                /* Receive time in nanoseconds in t. */
+        timeInfo.currentTime = ((PaTime)t) / 1000000000.0;
+        
+        /* If you need to byte swap or shift inputBuffer to convert it into a pa format, do it here. */
+        PaUtil_BeginBufferProcessing(&stream->bufferProcessor,
+                                     &timeInfo,
+                                     0 /* IMPLEMENT ME: pass underflow/overflow flags when necessary */);
+                                     
+        if (stream->hostPortBuffIn.port)                    /* Equivalent to (inputChannelCount > 0) */
+            {                /* We are sure about the amount to transfer (PaUtil_Set before alRead). */
+            PaUtil_SetInputFrameCount(&stream->bufferProcessor, 0 /* 0 means take host buffer size */);
+            PaUtil_SetInterleavedInputChannels(&stream->bufferProcessor,
+                    0, /* first channel of inputBuffer is channel 0 */
+                    stream->hostPortBuffIn.buffer,
+                    0 ); /* 0 - use inputChannelCount passed to init buffer processor */
+            }
+        if (stream->hostPortBuffOut.port)
+            {
+            PaUtil_SetOutputFrameCount(&stream->bufferProcessor, 0 /* 0 means take host buffer size */);
+            PaUtil_SetInterleavedOutputChannels(&stream->bufferProcessor,
+                    0, /* first channel of outputBuffer is channel 0 */
+                    stream->hostPortBuffOut.buffer,
+                    0 ); /* 0 - use outputChannelCount passed to init buffer processor */
+            }
+        /*
+            You must pass a valid value of callback result to PaUtil_EndBufferProcessing()
+            in general you would pass paContinue for normal operation, and
+            paComplete to drain the buffer processor's internal output buffer.
+            You can check whether the buffer processor's output buffer is empty
+            using PaUtil_IsBufferProcessorOuputEmpty( bufferProcessor )
+        */
+        callbackResult = paContinue;      /* Whoops, lost this somewhere, back again in v 1.2.2.16! */
+        framesProcessed = PaUtil_EndBufferProcessing(&stream->bufferProcessor, &callbackResult);
+        /* If you need to byte swap or shift outputBuffer to convert it to host format, do it here. */
+        PaUtil_EndCpuLoadMeasurement( &stream->cpuLoadMeasurer, framesProcessed );
+        
+        if (callbackResult != paContinue)
+            {                                              /* Once finished, call the finished callback. */
+            DBUG(("SGI callbackResult = %d.\n", callbackResult));
+            if (stream->streamRepresentation.streamFinishedCallback)
+                stream->streamRepresentation.streamFinishedCallback(stream->streamRepresentation.userData);
+            if (callbackResult == paAbort)
+                {
+                stream->stopAbort = PA_SGI_REQ_ABORT_;
+                break;  /* Don't play the last buffer returned. */
+                }
+            else        /* paComplete or some other non-zero value. */
+                stream->stopAbort = PA_SGI_REQ_STOP_;
+            }
+        if (stream->hostPortBuffOut.port)                /* Write interleaved samples to SGI device */
+            alWriteFrames(stream->hostPortBuffOut.port,  /* (like unix_oss, AFTER checking callback result). */
+                          stream->hostPortBuffOut.buffer, stream->framesPerHostCallback);
+        }
+    if (stream->hostPortBuffOut.port) /* Drain output buffer(s), as long as we don't see an 'abort' request. */
+        {
+        while ((!(stream->stopAbort & PA_SGI_REQ_ABORT_)) &&    /* Assume _STOP_ is set (or meant). */
+               (alGetFilled(stream->hostPortBuffOut.port) > 1)) /* In case of ABORT we quickly leave (again). */
+            ; /* Don't provide any new (not even silent) samples, but let an underrun [almost] occur. */
+        }
+    if (callbackResult != paContinue)
+        stream->state = PA_SGI_STREAM_FLAG_FINISHED_;
+    return NULL;
+}
+
+
+/*
+    When CloseStream() is called, the multi-api layer ensures
+    that the stream has already been stopped or aborted.
+*/
+static PaError CloseStream(PaStream* s)
+{
+    PaError       result = paNoError;
+    PaSGIStream*  stream = (PaSGIStream*)s;
+
+    DBUG(("SGI CloseStream() started.\n"));
+    streamCleanupAndClose(stream); /* Releases i/o buffers and closes AL ports. */
+    PaUtil_TerminateBufferProcessor(&stream->bufferProcessor);
+    PaUtil_TerminateStreamRepresentation(&stream->streamRepresentation);
+    PaUtil_FreeMemory(stream);
+    return result;
+}
+
+
+static PaError StartStream(PaStream *s)
+{
+    PaError       result = paNoError;
+    PaSGIStream*  stream = (PaSGIStream*)s;
+
+    DBUG(("StartStream() started.\n"));
+    PaUtil_ResetBufferProcessor(&stream->bufferProcessor); /* See pa_common/pa_process.h. */
+    if (stream->bufferProcessor.streamCallback)
+        {                                       /* only when callback is used */
+        if (pthread_create(&stream->thread,
+                           NULL,                /* pthread_attr_t * attr */
+                           PaSGIpthread,        /* Function to spawn.    */
+                           (void*)stream))      /* Pass stream as arg.   */
+            {
+            PA_SGI_SET_LAST_IRIX_ERROR()        /* Let's hope oserror() tells something useful. */
+            result = paUnanticipatedHostError;
+            }
+        else
+            stream->state = PA_SGI_STREAM_FLAG_ACTIVE_;
+        }                   /* Set active before returning from this function. */
+    else
+        stream->state = PA_SGI_STREAM_FLAG_ACTIVE_; /* Apparently, setting active for blocking i/o is */
+    return result;                                  /* necessary (for patest_write_sine for example). */
+}
+
+
+static PaError StopStream( PaStream *s )
+{
+    PaError         result = paNoError;
+    PaSGIStream*    stream = (PaSGIStream*)s;
+    
+    if (stream->bufferProcessor.streamCallback) /* Only for callback streams. */
+        {
+        stream->stopAbort = PA_SGI_REQ_STOP_;   /* Signal and wait for the thread to drain outputs. */
+        if (pthread_join(stream->thread, NULL)) /* When succesful, stream->state */
+            {                                   /* is still ACTIVE, or FINISHED. */
+            PA_SGI_SET_LAST_IRIX_ERROR()
+            result = paUnanticipatedHostError;
+            }
+        else  /* Transition from ACTIVE or FINISHED to STOPPED. */
+            stream->state = PA_SGI_STREAM_FLAG_STOPPED_;
+        stream->stopAbort = PA_SGI_REQ_CONT_; /* For possible next start. */
+        }
+/*  else
+        stream->state = PA_SGI_STREAM_FLAG_STOPPED_;  Is this necessary for blocking i/o? */
+    return result;
+}
+
+
+static PaError AbortStream( PaStream *s )
+{
+    PaError result = paNoError;
+    PaSGIStream *stream = (PaSGIStream*)s;
+
+    if (stream->bufferProcessor.streamCallback) /* Only for callback streams. */
+        {
+        stream->stopAbort = PA_SGI_REQ_ABORT_;
+        if (pthread_join(stream->thread, NULL))
+            {
+            PA_SGI_SET_LAST_IRIX_ERROR()
+            result = paUnanticipatedHostError;
+            }
+        else  /* Transition from ACTIVE or FINISHED to STOPPED. */
+            stream->state = PA_SGI_STREAM_FLAG_STOPPED_;
+        stream->stopAbort = PA_SGI_REQ_CONT_; /* For possible next start. */
+        }
+/*  else
+        stream->state = PA_SGI_STREAM_FLAG_STOPPED_;  Is this necessary for blocking i/o? */
+    return result;
+}
+
+
+static PaError IsStreamStopped( PaStream *s )   /* Not just the opposite of IsStreamActive(): */
+{                                               /* in the 'callback finished' state, it       */
+                                                /* returns zero instead of nonzero.           */
+    if (((PaSGIStream*)s)->state & PA_SGI_STREAM_FLAG_STOPPED_)
+        return 1;
+    return 0;
+}
+
+
+static PaError IsStreamActive( PaStream *s )
+{
+    if (((PaSGIStream*)s)->state & PA_SGI_STREAM_FLAG_ACTIVE_)
+        return 1;
+    return 0;
+}
+
+
+static PaTime GetStreamTime( PaStream *s )
+{
+    stamp_t t;
+    
+    (void) s; /* Suppress unused argument warning. */
+    dmGetUST((unsigned long long*)(&t)); /* Receive time in nanoseconds in t. */
+    return (PaTime)t / 1000000000.0;
+}
+
+
+static double GetStreamCpuLoad( PaStream* s )
+{
+    PaSGIStream *stream = (PaSGIStream*)s;
+
+    return PaUtil_GetCpuLoad( &stream->cpuLoadMeasurer );
+}
+
+
+/*
+    As separate stream interfaces are used for blocking and callback
+    streams, the following functions can be guaranteed to only be called
+    for blocking streams.
+*/
+
+static PaError ReadStream( PaStream* s,
+                           void *buffer,
+                           unsigned long frames )
+{
+    PaSGIStream*    stream = (PaSGIStream*)s;
+    int             n;
+
+printf("stream->framesPerHostCallback=%ld.\n", stream->framesPerHostCallback);
+fflush(stdout);
+
+    while (frames)
+        {
+        if (frames > stream->framesPerHostCallback) n = stream->framesPerHostCallback;
+        else                                        n = frames;
+        /* Read interleaved samples from SGI device. */
+        alReadFrames(stream->hostPortBuffIn.port,           /* Port already opened by OpenStream(). */
+                     stream->hostPortBuffIn.buffer, n);     /* Already allocated by OpenStream().   */
+                                                            /* alReadFrames() always returns 0.     */
+        PaUtil_SetInputFrameCount(&stream->bufferProcessor, 0); /* 0 means take host buffer size */
+        PaUtil_SetInterleavedInputChannels(&stream->bufferProcessor,
+                                           0,   /* first channel of inputBuffer is channel 0 */
+                                           stream->hostPortBuffIn.buffer,
+                                           0 ); /* 0 means use inputChannelCount passed at init. */
+        /* Copy samples from host input channels set up by the PaUtil_SetInterleavedInputChannels 
+           to a user supplied buffer. */
+printf("frames=%ld, buffer=%ld\n", frames, (long)buffer);
+fflush(stdout);
+        PaUtil_CopyInput(&stream->bufferProcessor, &buffer, n);
+        frames -= n;
+        }
+printf("DONE: frames=%ld, buffer=%ld\n", frames, (long)buffer);
+    return paNoError;
+}
+
+
+static PaError WriteStream( PaStream* s,
+                            const void *buffer,
+                            unsigned long frames )
+{
+    PaSGIStream*    stream = (PaSGIStream*)s;
+    unsigned long   n;
+    while (frames)
+        {
+        PaUtil_SetOutputFrameCount(&stream->bufferProcessor, 0); /* 0 means take host buffer size */
+        PaUtil_SetInterleavedOutputChannels(&stream->bufferProcessor,
+                                            0,   /* first channel of inputBuffer is channel 0 */
+                                            stream->hostPortBuffOut.buffer,
+                                            0 ); /* 0 means use inputChannelCount passed at init. */
+        /* Copy samples from user supplied buffer to host input channels set up by
+           PaUtil_SetInterleavedOutputChannels. Copies the minimum of the number of user frames 
+           (specified by the frameCount parameter) and the number of host frames (specified in 
+           a previous call to SetOutputFrameCount()). */
+        n = PaUtil_CopyOutput(&stream->bufferProcessor, &buffer, frames);
+        /* Write interleaved samples to SGI device. */
+        alWriteFrames(stream->hostPortBuffOut.port, stream->hostPortBuffOut.buffer, n);
+        frames -= n;                                           /* alWriteFrames always returns 0. */
+        }
+    return paNoError;
+}
+
+
+static signed long GetStreamReadAvailable( PaStream* s )
+{
+    return (signed long)alGetFilled(((PaSGIStream*)s)->hostPortBuffIn.port);
+}
+
+
+static signed long GetStreamWriteAvailable( PaStream* s )
+{
+    return (signed long)alGetFillable(((PaSGIStream*)s)->hostPortBuffOut.port);
+}
+
+
+/* CVS reminder:
+   To download the 'v19-devel' branch from portaudio's CVS server for the first time, type:
+    cvs -d:pserver:anonymous@www.portaudio.com:/home/cvs checkout -r v19-devel portaudio
+   Then 'cd' to the 'portaudio' directory that should have been created.
+   To commit changes:
+    cvs -d:pserver:pieter@www.portaudio.com:/home/cvs login
+    cvs -d:pserver:pieter@www.portaudio.com:/home/cvs commit -m 'blabla.' -r v19-devel pa_sgi/pa_sgi.c
+    cvs -d:pserver:pieter@www.portaudio.com:/home/cvs logout
+   To see if someone else worked on something:
+    cvs -d:pserver:anonymous@www.portaudio.com:/home/cvs update -r v19-devel
+   To get an older revision of a certain file (without sticky business):
+    cvs -d:pserver:anonymous@www.portaudio.com:/home/cvs update -p -r 1.1.1.1.2.4 pa_tests/patest1.c >pa_tests/patest1.c-OLD
+   To see logs:
+    cvs -d:pserver:anonymous@www.portaudio.com:/home/cvs log pa_common/pa_skeleton.c
+*/
diff --git a/src/audio/portaudio/pa_tests/README.txt b/src/audio/portaudio/pa_tests/README.txt
new file mode 100644
index 0000000000000000000000000000000000000000..f3a0b822643ab8fc48a48ecdf1041c55ea05dd37
--- /dev/null
+++ b/src/audio/portaudio/pa_tests/README.txt
@@ -0,0 +1,60 @@
+This directory contains various programs to test PortAudio. The files 
+named patest_* are tests, the files named debug_* are just scratch 
+files that may or may not work.
+
+All following tests are up to date with the V19 API. They should all compile
+(without any warnings on GCC 3.3). Note that this does not necissarily mean that 
+the tests pass, just that they compile.
+
+    x- paqa_devs.c 
+    x- paqa_errs.c   (needs reviewing)
+    x- patest1.c
+    x- patest_buffer.c
+    x- patest_callbackstop.c
+    x- patest_clip.c (last test fails, dither doesn't currently force clip in V19)
+    x- patest_dither.c
+    x- patest_hang.c
+    x- patest_latency.c
+    x- patest_leftright.c
+    x- patest_longsine.c
+    x- patest_many.c
+    x- patest_maxsines.c
+	o- patest_mono.c
+    x- patest_multi_sine.c
+    x- patest_pink.c
+    x- patest_prime.c
+    x- patest_read_record.c
+    x- patest_record.c
+    x- patest_ringmix.c
+    x- patest_saw.c
+    x- patest_sine.c
+    x- patest_sine8.c
+    x- patest_sine_formats.c
+    x- patest_sine_time.c
+    x- patest_start_stop.c
+    x- patest_stop.c
+    x- patest_sync.c
+    x- patest_toomanysines.c
+	o- patest_two_rates.c
+    x- patest_underflow.c
+    x- patest_wire.c
+    x- patest_write_sine.c
+    x- pa_devs.c
+    x- pa_fuzz.c
+    x- pa_minlat.c
+
+The debug_ files are still in V18 format and may need some V19 adaption.
+Feel free to fix them, most simply require adjusting to the new API.
+
+o- pa_tests/debug_convert.c
+o- pa_tests/debug_dither_calc.c
+o- pa_tests/debug_dual.c
+o- pa_tests/debug_multi_in.c
+o- pa_tests/debug_multi_out.c
+o- pa_tests/debug_record.c
+o- pa_tests/debug_record_reuse.c
+o- pa_tests/debug_sine.c
+o- pa_tests/debug_sine_amp.c
+o- pa_tests/debug_sine_formats.c
+o- pa_tests/debug_srate.c
+o- pa_tests/debug_test1.c
diff --git a/src/audio/portaudio/pa_tests/debug_convert.c b/src/audio/portaudio/pa_tests/debug_convert.c
new file mode 100644
index 0000000000000000000000000000000000000000..a17b556e4816deb61f91a02088bfa8591f07243c
--- /dev/null
+++ b/src/audio/portaudio/pa_tests/debug_convert.c
@@ -0,0 +1,131 @@
+/*
+ * $Id$
+ * Convert tagged values.
+ *
+ * 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)
+
+#define NUM_BUFFERS         (0)
+
+typedef struct
+{
+    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;
+    short *out = (short*)outputBuffer;
+    int i;
+    int finished = 0;
+    (void) outTime; /* Prevent unused variable warnings. */
+    (void) inputBuffer;
+
+    if( data->framesToGo < framesPerBuffer )  finished = 1;
+
+    for( i=0; i<framesPerBuffer; i++ )
+    {
+        *out++ = 0x0000 + i;  /* left */
+        *out++ = 0x1000 + i;  /* right */
+    }
+    return finished;
+}
+/*******************************************************************/
+int main(void);
+int main(void)
+{
+    PortAudioStream *stream;
+    PaError err;
+    paTestData data;
+    int i;
+    int totalSamps;
+    printf("PortAudio Test: output debug values\n" );
+    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 */
+              paInt16,      /* 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/src/audio/portaudio/pa_tests/debug_dither_calc.c b/src/audio/portaudio/pa_tests/debug_dither_calc.c
new file mode 100644
index 0000000000000000000000000000000000000000..1b24760996341085d42f816a035b656484e4a84c
--- /dev/null
+++ b/src/audio/portaudio/pa_tests/debug_dither_calc.c
@@ -0,0 +1,55 @@
+/*
+ * $Id$
+ * Test Dither calculations.
+ *
+ * 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_host.h"
+
+/*******************************************************************/
+int main(void);
+int main(void)
+{
+    long max,min;
+    int   i;
+    
+    for( i=0; i<10000; i++ )
+    {
+        long dither = PaConvert_TriangularDither();
+        // printf("dither = 0x%08X\n", dither );
+        if( dither < min ) min = dither;
+        else if( dither > max ) max = dither;
+    }
+    printf("min = 0x%08X = %d, max = 0x%08X = %d\n", min, min, max, max );
+}
diff --git a/src/audio/portaudio/pa_tests/debug_dual.c b/src/audio/portaudio/pa_tests/debug_dual.c
new file mode 100644
index 0000000000000000000000000000000000000000..35ba032d0129d0169e12e97065dfea2612db3467
--- /dev/null
+++ b/src/audio/portaudio/pa_tests/debug_dual.c
@@ -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/src/audio/portaudio/pa_tests/debug_multi_in.c b/src/audio/portaudio/pa_tests/debug_multi_in.c
new file mode 100644
index 0000000000000000000000000000000000000000..def729c1b4649db31e0420a05b55f53a9fba4edc
--- /dev/null
+++ b/src/audio/portaudio/pa_tests/debug_multi_in.c
@@ -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/src/audio/portaudio/pa_tests/debug_multi_out.c b/src/audio/portaudio/pa_tests/debug_multi_out.c
new file mode 100644
index 0000000000000000000000000000000000000000..d230833203d1e67faac6a0eee6b7c66f4e429311
--- /dev/null
+++ b/src/audio/portaudio/pa_tests/debug_multi_out.c
@@ -0,0 +1,144 @@
+/*
+ * $Id$
+ * debug_multi_out.c
+ * Play a different sine wave on each channels,
+ * using the Portable Audio api.
+ *
+ * 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 OUTPUT_DEVICE       (Pa_GetDefaultOutputDeviceID())
+#define SAMPLE_RATE         (44100)
+#define FRAMES_PER_BUFFER   (256)
+#define FREQ_INCR           (300.0 / SAMPLE_RATE)
+#define MAX_CHANNELS        (64)
+
+#ifndef M_PI
+#define M_PI  (3.14159265)
+#endif
+
+typedef struct
+{
+    int      numChannels;
+    double   phases[MAX_CHANNELS];
+}
+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 frameIndex, channelIndex;
+    int finished = 0;
+    (void) outTime; /* Prevent unused variable warnings. */
+    (void) inputBuffer;
+
+    for( frameIndex=0; frameIndex<(int)framesPerBuffer; frameIndex++ )
+    {
+        for( channelIndex=0; channelIndex<data->numChannels; channelIndex++ )
+        {
+            /* Output sine wave on every channel. */
+            *out++ = (float) sin(data->phases[channelIndex]);
+
+            /* Play each channel at a higher frequency. */
+            data->phases[channelIndex] += FREQ_INCR * (4 + channelIndex);
+            if( data->phases[channelIndex] >= (2.0 * M_PI) ) data->phases[channelIndex] -= (2.0 * M_PI);
+        }
+    }
+
+    return 0;
+}
+/*******************************************************************/
+int main(void);
+int main(void)
+{
+    PortAudioStream *stream;
+    PaError err;
+    const PaDeviceInfo *pdi;
+    paTestData data = {0};
+    printf("PortAudio Test: output sine wave on each channel.\n" );
+
+    err = Pa_Initialize();
+    if( err != paNoError ) goto error;
+
+    pdi = Pa_GetDeviceInfo( OUTPUT_DEVICE );
+    data.numChannels = pdi->maxOutputChannels;
+    if( data.numChannels > MAX_CHANNELS ) data.numChannels = MAX_CHANNELS;
+    printf("Number of Channels = %d\n", data.numChannels );
+    
+    err = Pa_OpenStream(
+              &stream,
+              paNoDevice, /* default input device */
+              0,              /* no input */
+              paFloat32,  /* 32 bit floating point input */
+              NULL,
+              OUTPUT_DEVICE,
+              data.numChannels,
+              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("Hit ENTER to stop sound.\n");
+    fflush(stdout);
+    getchar();
+
+    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/src/audio/portaudio/pa_tests/debug_record.c b/src/audio/portaudio/pa_tests/debug_record.c
new file mode 100644
index 0000000000000000000000000000000000000000..57b11f581fc7d42f1c4e2839c5f3524f9521a151
--- /dev/null
+++ b/src/audio/portaudio/pa_tests/debug_record.c
@@ -0,0 +1,339 @@
+/*
+ * $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 FRAMES_PER_BUFFER  (1<<10)
+#define NUM_REC_BUFS    (0)
+
+#if 1
+#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;
+    long         numSamples;
+    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;
+}
+
+/****************************************************************/
+PaError TestRecording( paTestData *dataPtr )
+{
+    PortAudioStream *stream;
+    PaError    err;
+    int        i;
+
+    /* Record some audio. */
+    err = Pa_OpenStream(
+              &stream,
+              Pa_GetDefaultInputDeviceID(),
+              dataPtr->samplesPerFrame,               /* stereo input */
+              PA_SAMPLE_TYPE,
+              NULL,
+              paNoDevice,
+              0,
+              PA_SAMPLE_TYPE,
+              NULL,
+              SAMPLE_RATE,
+              FRAMES_PER_BUFFER,            /* 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,
+              dataPtr );
+    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( dataPtr->maxFrameIndex <= dataPtr->frameIndex )
+        {
+            printf("Buffer recording complete.\n");
+            break;
+        }
+        Pa_Sleep(100);
+        printf("index = %d\n", dataPtr->frameIndex ); fflush(stdout);
+    }
+
+    printf("Finished loop. Close stream.\n"); fflush(stdout);
+
+    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<dataPtr->numSamples; i++ )
+        {
+            posVal = dataPtr->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( dataPtr->recordedSamples, dataPtr->samplesPerFrame * sizeof(SAMPLE), totalFrames, fid );
+            fclose( fid );
+            printf("Wrote data to 'recorded.raw'\n");
+        }
+    }
+#endif
+
+error:
+    return err;
+}
+
+/****************************************************************/
+PaError TestPlayback( paTestData *dataPtr )
+{
+    PortAudioStream *stream;
+    PaError    err;
+    int        i;
+
+    /* Playback recorded data. */
+    dataPtr->frameIndex = 0;
+    printf("Begin playback.\n"); fflush(stdout);
+    err = Pa_OpenStream(
+              &stream,
+              paNoDevice,
+              0,               /* NO input */
+              PA_SAMPLE_TYPE,
+              NULL,
+              Pa_GetDefaultOutputDeviceID(),
+              dataPtr->samplesPerFrame,               /* 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 */
+              playCallback,
+              dataPtr );
+    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", dataPtr->frameIndex );
+    }
+    err = Pa_CloseStream( stream );
+    if( err != paNoError ) goto error;
+    
+error:
+    return err;
+}
+/*******************************************************************/
+int main(void);
+int main(void)
+{
+    PaError    err;
+    paTestData data;
+    long       totalFrames;
+    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);
+    data.numSamples = totalFrames * data.samplesPerFrame;
+
+    numBytes = data.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<data.numSamples; i++ ) data.recordedSamples[i] = 0;
+
+    err = Pa_Initialize();
+    if( err != paNoError ) goto error;
+
+    for( i=0; i<2; i++ )
+    {
+        err = TestRecording( &data );
+        if( err != paNoError ) goto error;
+
+        err = TestPlayback( &data );
+        if( err != paNoError ) goto error;
+    }
+
+    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/src/audio/portaudio/pa_tests/debug_record_reuse.c b/src/audio/portaudio/pa_tests/debug_record_reuse.c
new file mode 100644
index 0000000000000000000000000000000000000000..46f937d21bd24a4bfdf548feaaed70ebdb984b46
--- /dev/null
+++ b/src/audio/portaudio/pa_tests/debug_record_reuse.c
@@ -0,0 +1,351 @@
+/*
+ * $Id$
+ * debug_record_reuse.c
+ * Record input into an array.
+ * Save array to a file.
+ * Based on patest_record.c but with various ugly debug hacks thrown in.
+ * Loop twice and reuse same streams.
+ *
+ * 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     (4)
+#define SLEEP_DUR_MSEC  (200)
+#define FRAMES_PER_BUFFER  (256)
+#define NUM_REC_BUFS    (0)
+
+#if 1
+#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;
+    long         numSamples;
+    PortAudioStream *outputStream;
+    PortAudioStream *inputStream;
+    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;
+}
+
+/****************************************************************/
+PaError TestRecording( paTestData *dataPtr )
+{
+    PaError    err;
+    int        i;
+    int        lastIndex = 0;
+
+/* Open input stream if not already open. */
+    if( dataPtr->inputStream == NULL )
+    {
+        /* Record some audio. */
+        err = Pa_OpenStream(
+                  &dataPtr->inputStream,
+                  Pa_GetDefaultInputDeviceID(),
+                  dataPtr->samplesPerFrame,               /* stereo input */
+                  PA_SAMPLE_TYPE,
+                  NULL,
+                  paNoDevice,
+                  0,
+                  PA_SAMPLE_TYPE,
+                  NULL,
+                  SAMPLE_RATE,
+                  FRAMES_PER_BUFFER,            /* 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,
+                  dataPtr );
+        if( err != paNoError ) goto error;
+    }
+
+    dataPtr->frameIndex = 0;
+
+    err = Pa_StartStream( dataPtr->inputStream );
+    if( err != paNoError ) goto error;
+
+    printf("Now recording!\n"); fflush(stdout);
+    for( i=0; i<(NUM_SECONDS*1000/SLEEP_DUR_MSEC); i++ )
+    {
+        int frameIndex, delta;
+        Pa_Sleep(SLEEP_DUR_MSEC);
+
+        frameIndex = dataPtr->frameIndex;
+        if( Pa_StreamActive( dataPtr->inputStream ) <= 0)
+        {
+            printf("Stream inactive!\n");
+            break;
+        }
+        if( dataPtr->maxFrameIndex <= frameIndex )
+        {
+            printf("Buffer recording complete.\n");
+            break;
+        }
+
+        delta = frameIndex - lastIndex;
+        lastIndex = frameIndex;
+        printf("index = %d, delta = %d\n", frameIndex, delta ); fflush(stdout);
+    }
+
+    err = Pa_StopStream( dataPtr->inputStream );
+    if( err != paNoError ) goto error;
+
+    printf("Done.\n"); fflush(stdout);
+
+error:
+    return err;
+}
+
+/****************************************************************/
+PaError TestPlayback( paTestData *dataPtr )
+{
+    PaError    err;
+    int        i;
+    int        lastIndex = 0;
+
+    /* Playback recorded data. */
+    dataPtr->frameIndex = 0;
+    printf("Begin playback.\n"); fflush(stdout);
+
+/* Open output stream if not already open. */
+    if( dataPtr->outputStream == NULL )
+    {
+        err = Pa_OpenStream(
+                  &dataPtr->outputStream,
+                  paNoDevice,
+                  0,               /* NO input */
+                  PA_SAMPLE_TYPE,
+                  NULL,
+                  Pa_GetDefaultOutputDeviceID(),
+                  dataPtr->samplesPerFrame,               /* 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 */
+                  playCallback,
+                  dataPtr );
+        if( err != paNoError ) goto error;
+    }
+
+    err = Pa_StartStream( dataPtr->outputStream );
+    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++ )
+    {
+        int frameIndex, delta;
+        Pa_Sleep(SLEEP_DUR_MSEC);
+        frameIndex = dataPtr->frameIndex;
+        delta = frameIndex - lastIndex;
+        lastIndex = frameIndex;
+        printf("index = %d, delta = %d\n", frameIndex, delta ); fflush(stdout);
+    }
+
+    err = Pa_StopStream( dataPtr->outputStream );
+    if( err != paNoError ) goto error;
+    
+error:
+    return err;
+}
+/*******************************************************************/
+int main(void);
+int main(void)
+{
+    PaError    err;
+    paTestData data = { 0 };
+    long       totalFrames;
+    long       numBytes;
+    long       i;
+    printf("patest_record.c\n"); fflush(stdout);
+
+/* Set up test data structure and sample array. */
+    data.frameIndex = 0;
+    data.samplesPerFrame = 2;
+    data.maxFrameIndex = totalFrames = NUM_SECONDS*SAMPLE_RATE;
+
+    printf("totalFrames = %d\n", totalFrames ); fflush(stdout);
+    data.numSamples = totalFrames * data.samplesPerFrame;
+
+    numBytes = data.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<data.numSamples; i++ ) data.recordedSamples[i] = 0;
+
+    err = Pa_Initialize();
+    if( err != paNoError ) goto error;
+
+/* Record and playback multiple times. */
+    for( i=0; i<2; i++ )
+    {
+        err = TestRecording( &data );
+        if( err != paNoError ) goto error;
+
+        err = TestPlayback( &data );
+        if( err != paNoError ) goto error;
+    }
+
+/* Clean up. */
+    err = Pa_CloseStream( data.inputStream );
+    if( err != paNoError ) goto error;
+
+    err = Pa_CloseStream( data.outputStream );
+    if( err != paNoError ) goto error;
+
+    if( err != paNoError ) goto error;
+
+    free( data.recordedSamples );
+    Pa_Terminate();
+    
+    printf("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 ) );
+    if( err == paHostError )
+    {
+        fprintf( stderr, "Host Error number: %d\n", Pa_GetHostError() );
+    }
+    return -1;
+}
diff --git a/src/audio/portaudio/pa_tests/debug_sine.c b/src/audio/portaudio/pa_tests/debug_sine.c
new file mode 100644
index 0000000000000000000000000000000000000000..86610d3f6adc4582bcdc059730d1f59a8185f048
--- /dev/null
+++ b/src/audio/portaudio/pa_tests/debug_sine.c
@@ -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 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;
+    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/src/audio/portaudio/pa_tests/debug_sine_amp.c b/src/audio/portaudio/pa_tests/debug_sine_amp.c
new file mode 100644
index 0000000000000000000000000000000000000000..ceaf5723871e237820a905fd20b379e4b8b0ab50
--- /dev/null
+++ b/src/audio/portaudio/pa_tests/debug_sine_amp.c
@@ -0,0 +1,157 @@
+/*
+ * $Id$
+ * Play a different sine wave on each channels,
+ * using the Portable Audio api.
+ * Allos amplitude to be set interactively.
+ *
+ * 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 OUTPUT_DEVICE       (Pa_GetDefaultOutputDeviceID())
+#define SAMPLE_RATE         (44100)
+#define FRAMES_PER_BUFFER   (256)
+#define FREQ_INCR           (300.0 / SAMPLE_RATE)
+#define MAX_CHANNELS        (64)
+
+#ifndef M_PI
+#define M_PI  (3.14159265)
+#endif
+
+typedef struct
+{
+    int      numChannels;
+    double   phases[MAX_CHANNELS];
+    float    amplitude;
+}
+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 frameIndex, channelIndex;
+    (void) outTime; /* Prevent unused variable warnings. */
+    (void) inputBuffer;
+
+    for( frameIndex=0; frameIndex<(int)framesPerBuffer; frameIndex++ )
+    {
+        for( channelIndex=0; channelIndex<data->numChannels; channelIndex++ )
+        {
+            /* Output sine wave on every channel. */
+            *out++ = (float) ( data->amplitude * sin(data->phases[channelIndex]) );
+
+            /* Play each channel at a higher frequency. */
+            data->phases[channelIndex] += FREQ_INCR * (4 + channelIndex);
+            if( data->phases[channelIndex] >= (2.0 * M_PI) ) data->phases[channelIndex] -= (2.0 * M_PI);
+        }
+    }
+
+    return 0;
+}
+/*******************************************************************/
+int main(void);
+int main(void)
+{
+    char  pad[256];
+    PortAudioStream *stream;
+    PaError err;
+    const PaDeviceInfo *pdi;
+    paTestData data = {0};
+    printf("PortAudio Test: output sine wave on each channel.\n" );
+
+    err = Pa_Initialize();
+    if( err != paNoError ) goto error;
+
+    pdi = Pa_GetDeviceInfo( OUTPUT_DEVICE );
+    data.numChannels = pdi->maxOutputChannels;
+    if( data.numChannels > MAX_CHANNELS ) data.numChannels = MAX_CHANNELS;
+    printf("Number of Channels = %d\n", data.numChannels );
+    data.amplitude = 1.0;
+    
+    err = Pa_OpenStream(
+              &stream,
+              paNoDevice, /* default input device */
+              0,              /* no input */
+              paFloat32,  /* 32 bit floating point input */
+              NULL,
+              OUTPUT_DEVICE,
+              data.numChannels,
+              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
+    {
+        printf("Current amplitude = %f\n", data.amplitude );
+        printf("Enter new amplitude or 'q' to quit.\n");
+        fflush(stdout);
+        gets( pad );
+        if( pad[0] != 'q' )
+        {
+        // I tried to use atof but it seems to be broken on Mac OS X 10.1
+            float amp;
+            sscanf( pad, "%f", &amp );
+            data.amplitude = amp;
+        }
+    } while( pad[0] != 'q' );
+
+    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/src/audio/portaudio/pa_tests/debug_sine_formats.c b/src/audio/portaudio/pa_tests/debug_sine_formats.c
new file mode 100644
index 0000000000000000000000000000000000000000..a1fce567ccdde350a421c631c11d56a09f9bac10
--- /dev/null
+++ b/src/audio/portaudio/pa_tests/debug_sine_formats.c
@@ -0,0 +1,202 @@
+/*
+ * $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        (10)
+#define SAMPLE_RATE        (44100)
+#define FRAMES_PER_BUFFER  (256)
+
+#define LEFT_FREQ          (SAMPLE_RATE/512.0)  /* So we hit 1.0 */
+#define RIGHT_FREQ         (500.0)
+
+#define AMPLITUDE          (1.0)
+
+/* Select ONE format for testing. */
+#define TEST_UINT8    (1)
+#define TEST_INT8     (0)
+#define TEST_INT16    (0)
+#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"
+
+#elif 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"
+
+#elif TEST_INT16
+#define TEST_FORMAT         paInt16
+typedef short               SAMPLE_t;
+#define SAMPLE_ZERO         (0)
+#define DOUBLE_TO_SAMPLE(x) (SAMPLE_ZERO + (SAMPLE_t)(32767 * (x)))
+#define FORMAT_NAME         "Signed 16 Bit"
+
+#elif 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;
+    SAMPLE_t sample;
+    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;
+        sample = DOUBLE_TO_SAMPLE( AMPLITUDE * sin( (data->left_phase * M_PI * 2. ))); /**/
+        *out++ = sample;
+/*        *out++ = sample; /**/
+/*        *out++ = 0;  /**/
+
+        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. ))); /**/
+/*        *out++ = 0;  /* */
+    }
+    /* 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/src/audio/portaudio/pa_tests/debug_srate.c b/src/audio/portaudio/pa_tests/debug_srate.c
new file mode 100644
index 0000000000000000000000000000000000000000..07371f28e5d3623b4bc681d5c817941d0b04d343
--- /dev/null
+++ b/src/audio/portaudio/pa_tests/debug_srate.c
@@ -0,0 +1,265 @@
+/*
+ * $Id$
+ * debug_record_reuse.c
+ * Record input into an array.
+ * Save array to a file.
+ * Based on patest_record.c but with various ugly debug hacks thrown in.
+ * Loop twice and reuse same streams.
+ *
+ * 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 EWS88MT_12_REC     (1)
+#define EWS88MT_12_PLAY   (10)
+#define SBLIVE_REC         (2)
+#define SBLIVE_PLAY       (11)
+
+#if 0
+#define INPUT_DEVICE_ID    Pa_GetDefaultInputDeviceID()
+#define OUTPUT_DEVICE_ID   Pa_GetDefaultOutputDeviceID()
+#else
+#define INPUT_DEVICE_ID    (EWS88MT_12_REC)
+#define OUTPUT_DEVICE_ID   (SBLIVE_PLAY)
+#endif
+
+#define INPUT_SAMPLE_RATE     (22050.0)
+#define OUTPUT_SAMPLE_RATE    (22050.0)
+#define NUM_SECONDS               (4)
+#define SLEEP_DUR_MSEC         (1000)
+#define FRAMES_PER_BUFFER        (64)
+#define NUM_REC_BUFS              (0)
+#define SAMPLES_PER_FRAME         (2)
+
+#define PA_SAMPLE_TYPE  paInt16
+typedef short SAMPLE;
+
+typedef struct
+{
+    long             frameIndex;  /* Index into sample array. */
+}
+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;
+    (void) outputBuffer; /* Prevent unused variable warnings. */
+    (void) outTime;
+
+    if( inputBuffer != NULL )
+    {
+        data->frameIndex += framesPerBuffer;
+    }
+    return 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 playCallback( void *inputBuffer, void *outputBuffer,
+                         unsigned long framesPerBuffer,
+                         PaTimestamp outTime, void *userData )
+{
+    paTestData *data = (paTestData *) userData;
+    (void) inputBuffer; /* Prevent unused variable warnings. */
+    (void) outTime;
+
+    if( outputBuffer != NULL )
+    {
+        data->frameIndex += framesPerBuffer;
+    }
+    return 0;
+}
+
+/****************************************************************/
+PaError MeasureStreamRate( PortAudioStream *stream, paTestData *dataPtr, double *ratePtr )
+{
+    PaError    err;
+    int        i;
+    int        totalFrames = 0;
+    int        totalMSec = 0;
+
+    dataPtr->frameIndex = 0;
+
+    err = Pa_StartStream( stream );
+    if( err != paNoError ) goto error;
+
+    for( i=0; i<(NUM_SECONDS*1000/SLEEP_DUR_MSEC); i++ )
+    {
+        int delta, endIndex;
+
+        int startIndex = dataPtr->frameIndex;
+        Pa_Sleep(SLEEP_DUR_MSEC);
+        endIndex = dataPtr->frameIndex;
+
+        delta = endIndex - startIndex;
+        totalFrames += delta;
+        totalMSec += SLEEP_DUR_MSEC;
+
+        printf("index = %d, delta = %d\n", endIndex, delta ); fflush(stdout);
+    }
+
+    err = Pa_StopStream( stream );
+    if( err != paNoError ) goto error;
+
+    *ratePtr = (totalFrames * 1000.0) / totalMSec;
+
+error:
+    return err;
+}
+
+void ReportRate( double measuredRate, double expectedRate )
+{
+    double error;
+
+    error = (measuredRate - expectedRate) / expectedRate;
+    error = (error < 0 ) ? -error : error;
+
+    printf("Measured rate = %6.1f, expected rate = %6.1f\n",
+            measuredRate, expectedRate );
+    if( error > 0.1 )
+    {
+        printf("ERROR: unexpected rate! ---------------------   ERROR!\n");
+    }
+    else
+    {
+        printf("SUCCESS: rate within tolerance!\n");
+    }
+}
+
+/*******************************************************************/
+int main(void);
+int main(void)
+{
+    PaError    err;
+    paTestData data = { 0 };
+    long       i;
+    double     rate;
+    const    PaDeviceInfo *pdi;
+
+    PortAudioStream *outputStream;
+    PortAudioStream *inputStream;
+
+    err = Pa_Initialize();
+    if( err != paNoError ) goto error;
+
+
+    pdi = Pa_GetDeviceInfo( INPUT_DEVICE_ID );
+    printf("Input device  = %s\n", pdi->name );
+    pdi = Pa_GetDeviceInfo( OUTPUT_DEVICE_ID );
+    printf("Output device = %s\n", pdi->name );
+
+/* Open input stream. */
+    err = Pa_OpenStream(
+              &inputStream,
+              INPUT_DEVICE_ID,
+              SAMPLES_PER_FRAME,               /* stereo input */
+              PA_SAMPLE_TYPE,
+              NULL,
+              paNoDevice,
+              0,
+              PA_SAMPLE_TYPE,
+              NULL,
+              INPUT_SAMPLE_RATE,
+              FRAMES_PER_BUFFER,            /* 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_OpenStream(
+              &outputStream,
+              paNoDevice,
+              0,               /* NO input */
+              PA_SAMPLE_TYPE,
+              NULL,
+              OUTPUT_DEVICE_ID,
+              SAMPLES_PER_FRAME,               /* stereo output */
+              PA_SAMPLE_TYPE,
+              NULL,
+              OUTPUT_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 */
+              playCallback,
+              &data );
+    if( err != paNoError ) goto error;
+
+/* Record and playback multiple times. */
+    for( i=0; i<2; i++ )
+    {
+        printf("Measuring INPUT ------------------------- \n");
+        err = MeasureStreamRate( inputStream, &data, &rate );
+        if( err != paNoError ) goto error;
+        ReportRate( rate, INPUT_SAMPLE_RATE );
+
+        printf("Measuring OUTPUT ------------------------- \n");
+        err = MeasureStreamRate( outputStream, &data, &rate );
+        if( err != paNoError ) goto error;
+        ReportRate( rate, OUTPUT_SAMPLE_RATE );
+    }
+
+/* Clean up. */
+    err = Pa_CloseStream( inputStream );
+    if( err != paNoError ) goto error;
+
+    err = Pa_CloseStream( outputStream );
+    if( err != paNoError ) goto error;
+
+    if( err != paNoError ) goto error;
+
+    Pa_Terminate();
+    
+    printf("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 ) );
+    if( err == paHostError )
+    {
+        fprintf( stderr, "Host Error number: %d\n", Pa_GetHostError() );
+    }
+    return -1;
+}
diff --git a/src/audio/portaudio/pa_tests/debug_test1.c b/src/audio/portaudio/pa_tests/debug_test1.c
new file mode 100644
index 0000000000000000000000000000000000000000..1321f62c80efbcef2de46c9410bd358e39e0412d
--- /dev/null
+++ b/src/audio/portaudio/pa_tests/debug_test1.c
@@ -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/src/audio/portaudio/pa_tests/pa_devs.c b/src/audio/portaudio/pa_tests/pa_devs.c
new file mode 100644
index 0000000000000000000000000000000000000000..278e0275067696403664df41b38a9c9fee77b8f9
--- /dev/null
+++ b/src/audio/portaudio/pa_tests/pa_devs.c
@@ -0,0 +1,230 @@
+/** @file pa_devs.c
+    @brief List available devices, including device information.
+	@author Phil Burk http://www.softsynth.com
+
+    @note Define PA_NO_ASIO to compile this code on Windows without
+        ASIO support.
+*/
+/*
+ * $Id$
+ *
+ * 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"
+
+#ifdef WIN32
+#ifndef PA_NO_ASIO
+#include "pa_asio.h"
+#endif
+#endif
+
+/*******************************************************************/
+static void PrintSupportedStandardSampleRates(
+        const PaStreamParameters *inputParameters,
+        const PaStreamParameters *outputParameters )
+{
+    static double standardSampleRates[] = {
+        8000.0, 9600.0, 11025.0, 12000.0, 16000.0, 22050.0, 24000.0, 32000.0,
+        44100.0, 48000.0, 88200.0, 96000.0, 192000.0, -1 /* negative terminated  list */
+    };
+    int     i, printCount;
+    PaError err;
+
+    printCount = 0;
+    for( i=0; standardSampleRates[i] > 0; i++ )
+    {
+        err = Pa_IsFormatSupported( inputParameters, outputParameters, standardSampleRates[i] );
+        if( err == paFormatIsSupported )
+        {
+            if( printCount == 0 )
+            {
+                printf( "\t%8.2f", standardSampleRates[i] );
+                printCount = 1;
+            }
+            else if( printCount == 4 )
+            {
+                printf( ",\n\t%8.2f", standardSampleRates[i] );
+                printCount = 1;
+            }
+            else
+            {
+                printf( ", %8.2f", standardSampleRates[i] );
+                ++printCount;
+            }
+        }
+    }
+    if( !printCount )
+        printf( "None\n" );
+    else
+        printf( "\n" );
+}
+/*******************************************************************/
+int main(void);
+int main(void)
+{
+    int     i, numDevices, defaultDisplayed;
+    const   PaDeviceInfo *deviceInfo;
+    PaStreamParameters inputParameters, outputParameters;
+    PaError err;
+
+    
+    Pa_Initialize();
+
+    printf( "PortAudio version number = %d\nPortAudio version text = '%s'\n",
+            Pa_GetVersion(), Pa_GetVersionText() );
+
+            
+    numDevices = Pa_GetDeviceCount();
+    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++ )
+    {
+        deviceInfo = Pa_GetDeviceInfo( i );
+        printf( "--------------------------------------- device #%d\n", i );
+                
+    /* Mark global and API specific default devices */
+        defaultDisplayed = 0;
+        if( i == Pa_GetDefaultInputDevice() )
+        {
+            printf( "[ Default Input" );
+            defaultDisplayed = 1;
+        }
+        else if( i == Pa_GetHostApiInfo( deviceInfo->hostApi )->defaultInputDevice )
+        {
+            const PaHostApiInfo *hostInfo = Pa_GetHostApiInfo( deviceInfo->hostApi );
+            printf( "[ Default %s Input", hostInfo->name );
+            defaultDisplayed = 1;
+        }
+        
+        if( i == Pa_GetDefaultOutputDevice() )
+        {
+            printf( (defaultDisplayed ? "," : "[") );
+            printf( " Default Output" );
+            defaultDisplayed = 1;
+        }
+        else if( i == Pa_GetHostApiInfo( deviceInfo->hostApi )->defaultOutputDevice )
+        {
+            const PaHostApiInfo *hostInfo = Pa_GetHostApiInfo( deviceInfo->hostApi );
+            printf( (defaultDisplayed ? "," : "[") );                
+            printf( " Default %s Output", hostInfo->name );
+            defaultDisplayed = 1;
+        }
+
+        if( defaultDisplayed )
+            printf( " ]\n" );
+
+    /* print device info fields */
+        printf( "Name                        = %s\n", deviceInfo->name );
+        printf( "Host API                    = %s\n",  Pa_GetHostApiInfo( deviceInfo->hostApi )->name );
+        printf( "Max inputs = %d", deviceInfo->maxInputChannels  );
+        printf( ", Max outputs = %d\n", deviceInfo->maxOutputChannels  );
+
+        printf( "Default low input latency   = %8.3f\n", deviceInfo->defaultLowInputLatency  );
+        printf( "Default low output latency  = %8.3f\n", deviceInfo->defaultLowOutputLatency  );
+        printf( "Default high input latency  = %8.3f\n", deviceInfo->defaultHighInputLatency  );
+        printf( "Default high output latency = %8.3f\n", deviceInfo->defaultHighOutputLatency  );
+
+#ifdef WIN32
+#ifndef PA_NO_ASIO
+/* ASIO specific latency information */
+        if( Pa_GetHostApiInfo( deviceInfo->hostApi )->type == paASIO ){
+            long minLatency, maxLatency, preferredLatency, granularity;
+
+            err = PaAsio_GetAvailableLatencyValues( i,
+		            &minLatency, &maxLatency, &preferredLatency, &granularity );
+
+            printf( "ASIO minimum buffer size    = %ld\n", minLatency  );
+            printf( "ASIO maximum buffer size    = %ld\n", maxLatency  );
+            printf( "ASIO preferred buffer size  = %ld\n", preferredLatency  );
+
+            if( granularity == -1 )
+                printf( "ASIO buffer granularity     = power of 2\n" );
+            else
+                printf( "ASIO buffer granularity     = %ld\n", granularity  );
+        }
+#endif /* !PA_NO_ASIO */
+#endif /* WIN32 */
+
+        printf( "Default sample rate         = %8.2f\n", deviceInfo->defaultSampleRate );
+
+    /* poll for standard sample rates */
+        inputParameters.device = i;
+        inputParameters.channelCount = deviceInfo->maxInputChannels;
+        inputParameters.sampleFormat = paInt16;
+        inputParameters.suggestedLatency = 0; /* ignored by Pa_IsFormatSupported() */
+        inputParameters.hostApiSpecificStreamInfo = NULL;
+        
+        outputParameters.device = i;
+        outputParameters.channelCount = deviceInfo->maxOutputChannels;
+        outputParameters.sampleFormat = paInt16;
+        outputParameters.suggestedLatency = 0; /* ignored by Pa_IsFormatSupported() */
+        outputParameters.hostApiSpecificStreamInfo = NULL;
+
+        if( inputParameters.channelCount > 0 )
+        {
+            printf("Supported standard sample rates\n for half-duplex 16 bit %d channel input = \n",
+                    inputParameters.channelCount );
+            PrintSupportedStandardSampleRates( &inputParameters, NULL );
+        }
+
+        if( outputParameters.channelCount > 0 )
+        {
+            printf("Supported standard sample rates\n for half-duplex 16 bit %d channel output = \n",
+                    outputParameters.channelCount );
+            PrintSupportedStandardSampleRates( NULL, &outputParameters );
+        }
+
+        if( inputParameters.channelCount > 0 && outputParameters.channelCount > 0 )
+        {
+            printf("Supported standard sample rates\n for full-duplex 16 bit %d channel input, %d channel output = \n",
+                    inputParameters.channelCount, outputParameters.channelCount );
+            PrintSupportedStandardSampleRates( &inputParameters, &outputParameters );
+        }
+    }
+
+    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/src/audio/portaudio/pa_tests/pa_fuzz.c b/src/audio/portaudio/pa_tests/pa_fuzz.c
new file mode 100644
index 0000000000000000000000000000000000000000..127c2e3e67710cdac8e83e03436b4e74dd256c34
--- /dev/null
+++ b/src/audio/portaudio/pa_tests/pa_fuzz.c
@@ -0,0 +1,168 @@
+/** @file pa_fuzz.c
+    @brief Distort input like a fuzz boz.
+	@author Phil Burk  http://www.softsynth.com
+*/
+/*
+ * $Id$
+ *
+ * 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( const void *inputBuffer, void *outputBuffer,
+                         unsigned long framesPerBuffer,
+                         const PaStreamCallbackTimeInfo* timeInfo,
+                         PaStreamCallbackFlags statusFlags,
+                         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( const void *inputBuffer, void *outputBuffer,
+                         unsigned long framesPerBuffer,
+                         const PaStreamCallbackTimeInfo* timeInfo,
+                         PaStreamCallbackFlags statusFlags,
+                         void *userData )
+{
+    SAMPLE *out = (SAMPLE*)outputBuffer;
+    const SAMPLE *in = (const SAMPLE*)inputBuffer;
+    unsigned int i;
+    (void) timeInfo; /* Prevent unused variable warnings. */
+    (void) statusFlags;
+    (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 paContinue;
+}
+
+/*******************************************************************/
+int main(void);
+int main(void)
+{
+    PaStreamParameters inputParameters, outputParameters;
+    PaStream *stream;
+    PaError err;
+
+    err = Pa_Initialize();
+    if( err != paNoError ) goto error;
+
+
+    inputParameters.device = Pa_GetDefaultInputDevice(); /* default input device */
+    inputParameters.channelCount = 2;       /* stereo input */
+    inputParameters.sampleFormat = PA_SAMPLE_TYPE;
+    inputParameters.suggestedLatency = Pa_GetDeviceInfo( inputParameters.device )->defaultLowInputLatency;
+    inputParameters.hostApiSpecificStreamInfo = NULL;
+
+    outputParameters.device = Pa_GetDefaultOutputDevice(); /* default output device */
+    outputParameters.channelCount = 2;       /* stereo output */
+    outputParameters.sampleFormat = PA_SAMPLE_TYPE;
+    outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultLowOutputLatency;
+    outputParameters.hostApiSpecificStreamInfo = NULL;
+
+    err = Pa_OpenStream(
+              &stream,
+              &inputParameters,
+              &outputParameters,
+              SAMPLE_RATE,
+              FRAMES_PER_BUFFER,
+              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/src/audio/portaudio/pa_tests/pa_minlat.c b/src/audio/portaudio/pa_tests/pa_minlat.c
new file mode 100644
index 0000000000000000000000000000000000000000..8400dfd2799ec5a0cc6204a0a05a4fff19df8d5c
--- /dev/null
+++ b/src/audio/portaudio/pa_tests/pa_minlat.c
@@ -0,0 +1,187 @@
+/** @file pa_minlat.c
+    @brief Experiment with different numbers of buffers to determine the
+	minimum latency for a computer.
+	@author Phil Burk  http://www.softsynth.com
+*/
+/*
+ * $Id$
+ *
+ * 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 <string.h>
+#include "portaudio.h"
+
+#ifndef M_PI
+#define M_PI  (3.14159265)
+#endif
+#define TWOPI (M_PI * 2.0)
+
+#define DEFAULT_BUFFER_SIZE   (32)
+
+typedef struct
+{
+    double left_phase;
+    double right_phase;
+}
+paTestData;
+
+/* Very simple synthesis routine to generate two sine waves. */
+static int paminlatCallback( const void *inputBuffer, void *outputBuffer,
+                             unsigned long framesPerBuffer,
+                             const PaStreamCallbackTimeInfo* timeInfo,
+                             PaStreamCallbackFlags statusFlags,
+                             void *userData )
+{
+    paTestData *data = (paTestData*)userData;
+    float *out = (float*)outputBuffer;
+    unsigned int i;
+    double left_phaseInc = 0.02;
+    double right_phaseInc = 0.06;
+
+    double left_phase = data->left_phase;
+    double right_phase = data->right_phase;
+
+    for( i=0; i<framesPerBuffer; i++ )
+    {
+        left_phase += left_phaseInc;
+        if( left_phase > TWOPI ) left_phase -= TWOPI;
+        *out++ = (float) sin( left_phase );
+
+        right_phase += right_phaseInc;
+        if( right_phase > TWOPI ) right_phase -= TWOPI;
+        *out++ = (float) sin( right_phase );
+    }
+
+    data->left_phase = left_phase;
+    data->right_phase = right_phase;
+    return 0;
+}
+
+int main( int argc, char **argv );
+int main( int argc, char **argv )
+{
+    PaStreamParameters outputParameters;
+    PaStream *stream;
+    PaError err;
+    paTestData data;
+    int    go;
+    int    outLatency = 0;
+    int    minLatency = DEFAULT_BUFFER_SIZE * 2;
+    int    framesPerBuffer;
+    double sampleRate = 44100.0;
+    char   str[256];
+
+    printf("pa_minlat - Determine minimum latency for your computer.\n");
+    printf("  usage:         pa_minlat {userBufferSize}\n");
+    printf("  for example:   pa_minlat 64\n");
+    printf("Adjust your stereo until you hear a smooth tone in each speaker.\n");
+    printf("Then try to find the smallest number of frames 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 );
+
+    data.left_phase = data.right_phase = 0.0;
+
+    err = Pa_Initialize();
+    if( err != paNoError ) goto error;
+
+    outLatency = sampleRate * 200.0 / 1000.0; /* 200 msec. */
+
+    /* Try different numBuffers in a loop. */
+    go = 1;
+    while( go )
+    {
+        outputParameters.device                    = Pa_GetDefaultOutputDevice(); /* Default output device. */
+        outputParameters.channelCount              = 2;                           /* Stereo output */
+        outputParameters.sampleFormat              = paFloat32;                   /* 32 bit floating point output. */
+        outputParameters.suggestedLatency          = (double)outLatency / sampleRate; /* In seconds. */
+        outputParameters.hostApiSpecificStreamInfo = NULL;
+        
+        printf("Latency = %d frames = %6.1f msec.\n", outLatency, outputParameters.suggestedLatency * 1000.0 );
+
+        err = Pa_OpenStream(
+                  &stream,
+                  NULL, /* no input */
+                  &outputParameters,
+                  sampleRate,
+                  framesPerBuffer,
+                  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 nlatency. */
+        printf("\nMove windows around to see if the sound glitches.\n");
+        printf("Latency now %d, enter new number of frames, or 'q' to quit: ", outLatency );
+        fgets( str, 256, stdin );
+        {
+            /* Get rid of newline */
+            size_t l = strlen( str ) - 1;
+            if( str[ l ] == '\n')
+                str[ l ] = '\0';
+        }
+        if( str[0] == 'q' ) go = 0;
+        else
+        {
+            outLatency = atol( str );
+            if( outLatency < minLatency )
+            {
+                printf( "Latency below minimum of %d! Set to minimum!!!\n", minLatency );
+                outLatency = minLatency;
+            }
+        }
+        /* 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 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/src/audio/portaudio/pa_tests/paqa_devs.c b/src/audio/portaudio/pa_tests/paqa_devs.c
new file mode 100644
index 0000000000000000000000000000000000000000..f52143c1909591a1a7fadbac704a5338b7fb5e4c
--- /dev/null
+++ b/src/audio/portaudio/pa_tests/paqa_devs.c
@@ -0,0 +1,347 @@
+/** @file paqa_devs.c
+    @brief 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
+    
+    Pieter adapted to V19 API. Test now relies heavily on 
+    Pa_IsFormatSupported(). Uses same 'standard' sample rates
+    as in test pa_devs.c.
+*/
+/*
+ * $Id$
+ *
+ * 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, PaDeviceIndex deviceID, double sampleRate,
+                         int numChannels );
+static int TestAdvance( int mode, PaDeviceIndex deviceID, double sampleRate,
+                        int numChannels, PaSampleFormat format );
+static int QaCallback( const void *inputBuffer, void *outputBuffer,
+                       unsigned long framesPerBuffer,
+                       const PaStreamCallbackTimeInfo* timeInfo,
+                       PaStreamCallbackFlags statusFlags,
+                       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( const void *inputBuffer, void *outputBuffer,
+                       unsigned long framesPerBuffer,
+                       const PaStreamCallbackTimeInfo* timeInfo,
+                       PaStreamCallbackFlags statusFlags,
+                       void *userData )
+{
+    unsigned long  i;
+    short          phase;
+    PaQaData *data = (PaQaData *) userData;
+    (void) inputBuffer;
+
+    /* 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 )
+    {
+        PaUtil_AddTraceMessage("QaCallback: running. framesLeft", data->framesLeft );
+        data->framesLeft -= framesPerBuffer;
+        return 0;
+    }
+    else
+    {
+        PaUtil_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 );
+    return 0;
+}
+
+/*******************************************************************
+* Try each output device, through its full range of capabilities. */
+static void TestDevices( int mode )
+{
+    int id, jc, i;
+    int maxChannels;
+    const PaDeviceInfo *pdi;
+    static double standardSampleRates[] = {  8000.0,  9600.0, 11025.0, 12000.0,
+                                            16000.0,          22050.0, 24000.0,
+                                            32000.0,          44100.0, 48000.0,
+                                                              88200.0, 96000.0,
+                                               -1.0 }; /* Negative terminated list. */
+    int numDevices = Pa_GetDeviceCount();
+    for( id=0; id<numDevices; id++ )            /* Iterate through all devices. */
+    {
+        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 standard sample rate. */
+            for( i=0; standardSampleRates[i] > 0; i++ )
+            {
+                TestFormats( mode, (PaDeviceIndex)id, standardSampleRates[i], jc );
+            }
+        }
+    }
+}
+
+/*******************************************************************/
+static void TestFormats( int mode, PaDeviceIndex 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, PaDeviceIndex deviceID, double sampleRate,
+                        int numChannels, PaSampleFormat format )
+{
+    PaStreamParameters inputParameters, outputParameters, *ipp, *opp;
+    PaStream *stream = NULL;
+    PaError result = paNoError;
+    PaQaData myData;
+    #define FRAMES_PER_BUFFER  (64)
+    
+    /* 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;
+    }
+
+    if( mode == MODE_INPUT )
+    {
+        inputParameters.device       = deviceID;
+        inputParameters.channelCount = numChannels;
+        inputParameters.sampleFormat = format;
+        inputParameters.suggestedLatency = Pa_GetDeviceInfo( inputParameters.device )->defaultLowInputLatency;
+        inputParameters.hostApiSpecificStreamInfo = NULL;
+        ipp = &inputParameters;
+    }
+    else
+        ipp = NULL;
+    if( mode == MODE_OUTPUT )           /* Pa_GetDeviceInfo(paNoDevice) COREDUMPS!!! */
+    {
+        outputParameters.device       = deviceID;
+        outputParameters.channelCount = numChannels;
+        outputParameters.sampleFormat = format;
+        outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultLowOutputLatency;
+        outputParameters.hostApiSpecificStreamInfo = NULL;
+        opp = &outputParameters;
+    }
+    else
+        opp = NULL;
+
+    if(paFormatIsSupported == Pa_IsFormatSupported( ipp, opp, sampleRate ))
+    {
+        printf("------ TestAdvance: %s, device = %d, rate = %g, numChannels = %d, format = %lu -------\n",
+                ( mode == MODE_INPUT ) ? "INPUT" : "OUTPUT",
+                deviceID, sampleRate, numChannels, (unsigned long)format);
+        EXPECT( ((result = Pa_OpenStream( &stream,
+                                          ipp,
+                                          opp,
+                                          sampleRate,
+                                          FRAMES_PER_BUFFER,
+                                          paClipOff,  /* we won't output out of range samples so don't bother clipping them */
+                                          QaCallback,
+                                          &myData ) ) == 0) );
+        if( stream )
+        {
+            PaTime oldStamp, newStamp;
+            unsigned long oldFrames;
+            int minDelay = ( mode == MODE_INPUT ) ? 1000 : 400;
+            /* Was:
+            int minNumBuffers = Pa_GetMinNumBuffers( FRAMES_PER_BUFFER, sampleRate );
+            int msec = (int) ((minNumBuffers * 3 * 1000.0 * FRAMES_PER_BUFFER) / sampleRate);
+            */
+            int msec = (int)( 3.0 *
+                       (( mode == MODE_INPUT ) ? inputParameters.suggestedLatency : outputParameters.suggestedLatency ));
+            if( msec < minDelay ) msec = minDelay;
+            printf("msec = %d\n", msec);  /**/
+            EXPECT( ((result=Pa_StartStream( stream )) == 0) );
+            /* Check to make sure PortAudio is advancing timeStamp. */
+            oldStamp = Pa_GetStreamTime(stream);
+            Pa_Sleep(msec);
+            newStamp = Pa_GetStreamTime(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 = %lu, myData.framesLeft = %lu\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/src/audio/portaudio/pa_tests/paqa_errs.c b/src/audio/portaudio/pa_tests/paqa_errs.c
new file mode 100644
index 0000000000000000000000000000000000000000..3f9077173ba4798caaa5b1e34cb0b5bfd6ec442c
--- /dev/null
+++ b/src/audio/portaudio/pa_tests/paqa_errs.c
@@ -0,0 +1,380 @@
+/** @file paqa_errs.c
+	@brief Self Testing Quality Assurance app for PortAudio
+	Do lots of bad things to test error reporting.
+	@author Phil Burk  http://www.softsynth.com
+    Pieter Suurmond adapted to V19 API.
+*/
+/*
+ * $Id$
+ *
+ * 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)
+
+typedef struct PaQaData
+{
+    unsigned long  framesLeft;
+    int            numChannels;
+    int            bytesPerSample;
+    int            mode;
+}
+PaQaData;
+
+static int gNumPassed = 0; /* Two globals */
+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( const void*                      inputBuffer,
+                       void*                            outputBuffer,
+                       unsigned long                    framesPerBuffer,
+			           const PaStreamCallbackTimeInfo*  timeInfo,
+			           PaStreamCallbackFlags            statusFlags,
+                       void*                            userData )
+{
+    unsigned long   i;
+    unsigned char*  out = (unsigned char *) outputBuffer;
+    PaQaData*       data = (PaQaData *) userData;
+    
+    (void)inputBuffer; /* Prevent "unused variable" warnings. */
+
+    /* 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;
+    }
+}
+
+static PaDeviceIndex FindInputOnlyDevice(void)
+{
+    PaDeviceIndex result = Pa_GetDefaultInputDevice();
+    if( result != paNoDevice && Pa_GetDeviceInfo(result)->maxOutputChannels == 0 )
+        return result;
+
+    for( result = 0; result < Pa_GetDeviceCount(); ++result )
+    {
+        if( Pa_GetDeviceInfo(result)->maxOutputChannels == 0 )
+            return result;
+    }
+
+    return paNoDevice;
+}
+
+static PaDeviceIndex FindOutputOnlyDevice(void)
+{
+    PaDeviceIndex result = Pa_GetDefaultOutputDevice();
+    if( result != paNoDevice && Pa_GetDeviceInfo(result)->maxInputChannels == 0 )
+        return result;
+
+    for( result = 0; result < Pa_GetDeviceCount(); ++result )
+    {
+        if( Pa_GetDeviceInfo(result)->maxInputChannels == 0 )
+            return result;
+    }
+
+    return paNoDevice;
+}
+
+/*-------------------------------------------------------------------------------------------------*/
+static int TestBadOpens( void )
+{
+    PaStream*           stream = NULL;
+    PaError             result;
+    PaQaData            myData;
+    PaStreamParameters  ipp, opp;
+    
+    /* Setup data for synthesis thread. */
+    myData.framesLeft = (unsigned long) (SAMPLE_RATE * 100); /* 100 seconds */
+    myData.numChannels = 1;
+    myData.mode = MODE_OUTPUT;
+
+    /*----------------------------- No devices specified: */
+    ipp.device                    = opp.device                    = paNoDevice;
+    ipp.channelCount              = opp.channelCount              = 0; /* Also no channels. */
+    ipp.hostApiSpecificStreamInfo = opp.hostApiSpecificStreamInfo = NULL;
+    ipp.sampleFormat              = opp.sampleFormat              = paFloat32;
+    /* Take the low latency of the default device for all subsequent tests. */
+    ipp.suggestedLatency          = Pa_GetDeviceInfo(Pa_GetDefaultInputDevice())->defaultLowInputLatency;
+    opp.suggestedLatency          = Pa_GetDeviceInfo(Pa_GetDefaultOutputDevice())->defaultLowOutputLatency;
+    HOPEFOR(((result = Pa_OpenStream(&stream, &ipp, &opp,
+                                     SAMPLE_RATE, FRAMES_PER_BUFFER,
+                                     paClipOff, QaCallback, &myData )) == paInvalidDevice));
+
+    /*----------------------------- No devices specified #2: */
+    HOPEFOR(((result = Pa_OpenStream(&stream, NULL, NULL,
+                                     SAMPLE_RATE, FRAMES_PER_BUFFER,
+                                     paClipOff, QaCallback, &myData )) == paInvalidDevice));
+
+    /*----------------------------- Out of range input device specified: */
+    ipp.hostApiSpecificStreamInfo = opp.hostApiSpecificStreamInfo = NULL;
+    ipp.sampleFormat              = opp.sampleFormat              = paFloat32;
+    ipp.channelCount = 0;           ipp.device = Pa_GetDeviceCount(); /* And no output device, and no channels. */
+    opp.channelCount = 0;           opp.device = paNoDevice;
+    HOPEFOR(((result = Pa_OpenStream(&stream, &ipp, NULL,
+                                     SAMPLE_RATE, FRAMES_PER_BUFFER,
+                                     paClipOff, QaCallback, &myData )) == paInvalidDevice));
+
+    /*----------------------------- Out of range output device specified: */
+    ipp.hostApiSpecificStreamInfo = opp.hostApiSpecificStreamInfo = NULL;
+    ipp.sampleFormat              = opp.sampleFormat              = paFloat32;
+    ipp.channelCount = 0;           ipp.device = paNoDevice; /* And no input device, and no channels. */
+    opp.channelCount = 0;           opp.device = Pa_GetDeviceCount();
+    HOPEFOR(((result = Pa_OpenStream(&stream, NULL, &opp,
+                                     SAMPLE_RATE, FRAMES_PER_BUFFER,
+                                     paClipOff, QaCallback, &myData )) == paInvalidDevice));
+
+    /*----------------------------- Zero input channels: */
+    ipp.hostApiSpecificStreamInfo = opp.hostApiSpecificStreamInfo = NULL;
+    ipp.sampleFormat              = opp.sampleFormat              = paFloat32;
+    ipp.channelCount = 0;           ipp.device = Pa_GetDefaultInputDevice();
+    opp.channelCount = 0;           opp.device = paNoDevice;    /* And no output device, and no output channels. */   
+    HOPEFOR(((result = Pa_OpenStream(&stream, &ipp, NULL,
+                                     SAMPLE_RATE, FRAMES_PER_BUFFER,
+                                     paClipOff, QaCallback, &myData )) == paInvalidChannelCount));
+
+    /*----------------------------- Zero output channels: */
+    ipp.hostApiSpecificStreamInfo = opp.hostApiSpecificStreamInfo = NULL;
+    ipp.sampleFormat              = opp.sampleFormat              = paFloat32;
+    ipp.channelCount = 0;           ipp.device = paNoDevice; /* And no input device, and no input channels. */
+    opp.channelCount = 0;           opp.device = Pa_GetDefaultOutputDevice();
+    HOPEFOR(((result = Pa_OpenStream(&stream, NULL, &opp,
+                                     SAMPLE_RATE, FRAMES_PER_BUFFER,
+                                     paClipOff, QaCallback, &myData )) == paInvalidChannelCount));
+
+    /*----------------------------- Nonzero input and output channels but no output device: */
+    ipp.hostApiSpecificStreamInfo = opp.hostApiSpecificStreamInfo = NULL;
+    ipp.sampleFormat              = opp.sampleFormat              = paFloat32;
+    ipp.channelCount = 2;           ipp.device = Pa_GetDefaultInputDevice();        /* Both stereo. */
+    opp.channelCount = 2;           opp.device = paNoDevice;
+    HOPEFOR(((result = Pa_OpenStream(&stream, &ipp, &opp,
+                                     SAMPLE_RATE, FRAMES_PER_BUFFER,
+                                     paClipOff, QaCallback, &myData )) == paInvalidDevice));
+
+    /*----------------------------- Nonzero input and output channels but no input device: */
+    ipp.hostApiSpecificStreamInfo = opp.hostApiSpecificStreamInfo = NULL;
+    ipp.sampleFormat              = opp.sampleFormat              = paFloat32;
+    ipp.channelCount = 2;           ipp.device = paNoDevice;
+    opp.channelCount = 2;           opp.device = Pa_GetDefaultOutputDevice();
+    HOPEFOR(((result = Pa_OpenStream(&stream, &ipp, &opp,
+                                     SAMPLE_RATE, FRAMES_PER_BUFFER,
+                                     paClipOff, QaCallback, &myData )) == paInvalidDevice));
+
+    /*----------------------------- NULL stream pointer: */
+    ipp.hostApiSpecificStreamInfo = opp.hostApiSpecificStreamInfo = NULL;
+    ipp.sampleFormat              = opp.sampleFormat              = paFloat32;
+    ipp.channelCount = 0;           ipp.device = paNoDevice;           /* Output is more likely than input. */
+    opp.channelCount = 2;           opp.device = Pa_GetDefaultOutputDevice();    /* Only 2 output channels. */
+    HOPEFOR(((result = Pa_OpenStream(NULL, &ipp, &opp,
+                                     SAMPLE_RATE, FRAMES_PER_BUFFER,
+                                     paClipOff, QaCallback, &myData )) == paBadStreamPtr));
+
+    /*----------------------------- Low sample rate: */
+    ipp.hostApiSpecificStreamInfo = opp.hostApiSpecificStreamInfo = NULL;
+    ipp.sampleFormat              = opp.sampleFormat              = paFloat32;
+    ipp.channelCount = 0;           ipp.device = paNoDevice;
+    opp.channelCount = 2;           opp.device = Pa_GetDefaultOutputDevice();
+    HOPEFOR(((result = Pa_OpenStream(&stream, NULL, &opp,
+                                     1.0, FRAMES_PER_BUFFER, /* 1 cycle per second (1 Hz) is too low. */
+                                     paClipOff, QaCallback, &myData )) == paInvalidSampleRate));
+
+    /*----------------------------- High sample rate: */
+    ipp.hostApiSpecificStreamInfo = opp.hostApiSpecificStreamInfo = NULL;
+    ipp.sampleFormat              = opp.sampleFormat              = paFloat32;
+    ipp.channelCount = 0;           ipp.device = paNoDevice;
+    opp.channelCount = 2;           opp.device = Pa_GetDefaultOutputDevice();
+    HOPEFOR(((result = Pa_OpenStream(&stream, NULL, &opp,
+                                     10000000.0, FRAMES_PER_BUFFER, /* 10^6 cycles per second (10 MHz) is too high. */
+                                     paClipOff, QaCallback, &myData )) == paInvalidSampleRate));
+
+    /*----------------------------- NULL callback: */
+    /* NULL callback is valid in V19 -- it means use blocking read/write stream
+    
+    ipp.hostApiSpecificStreamInfo = opp.hostApiSpecificStreamInfo = NULL;
+    ipp.sampleFormat              = opp.sampleFormat              = paFloat32;
+    ipp.channelCount = 0;           ipp.device = paNoDevice;
+    opp.channelCount = 2;           opp.device = Pa_GetDefaultOutputDevice();
+    HOPEFOR(((result = Pa_OpenStream(&stream, NULL, &opp,
+                                     SAMPLE_RATE, FRAMES_PER_BUFFER,
+                                     paClipOff,
+                                     NULL,
+                                     &myData )) == paNullCallback));
+    */
+
+    /*----------------------------- Bad flag: */
+    ipp.hostApiSpecificStreamInfo = opp.hostApiSpecificStreamInfo = NULL;
+    ipp.sampleFormat              = opp.sampleFormat              = paFloat32;
+    ipp.channelCount = 0;           ipp.device = paNoDevice;
+    opp.channelCount = 2;           opp.device = Pa_GetDefaultOutputDevice();
+    HOPEFOR(((result = Pa_OpenStream(&stream, NULL, &opp,
+                                     SAMPLE_RATE, FRAMES_PER_BUFFER,
+                                     255,                      /* Is 8 maybe legal V19 API? */
+                                     QaCallback, &myData )) == paInvalidFlag));
+
+    /*----------------------------- using input device as output device: */
+    if( FindInputOnlyDevice() != paNoDevice )
+    {
+        ipp.hostApiSpecificStreamInfo = opp.hostApiSpecificStreamInfo = NULL;
+        ipp.sampleFormat              = opp.sampleFormat              = paFloat32;
+        ipp.channelCount = 0;           ipp.device = paNoDevice; /* And no input device, and no channels. */
+        opp.channelCount = 2;           opp.device = FindInputOnlyDevice();
+        HOPEFOR(((result = Pa_OpenStream(&stream, NULL, &opp,
+                                         SAMPLE_RATE, FRAMES_PER_BUFFER,
+                                         paClipOff, QaCallback, &myData )) == paInvalidChannelCount));
+    }
+
+    /*----------------------------- using output device as input device: */
+    if( FindOutputOnlyDevice() != paNoDevice )
+    {
+        ipp.hostApiSpecificStreamInfo = opp.hostApiSpecificStreamInfo = NULL;
+        ipp.sampleFormat              = opp.sampleFormat              = paFloat32;
+        ipp.channelCount = 2;           ipp.device = FindOutputOnlyDevice();
+        opp.channelCount = 0;           opp.device = paNoDevice;  /* And no output device, and no channels. */
+        HOPEFOR(((result = Pa_OpenStream(&stream, &ipp, NULL,
+                                         SAMPLE_RATE, FRAMES_PER_BUFFER,
+                                         paClipOff, QaCallback, &myData )) == paInvalidChannelCount));
+
+    }
+
+    if( stream != NULL ) Pa_CloseStream( stream );
+    return result;
+}
+
+/*-----------------------------------------------------------------------------------------*/
+static int TestBadActions( void )
+{
+    PaStream*           stream = NULL;
+    PaError             result;
+    PaQaData            myData;
+    PaStreamParameters  opp;
+
+    /* Setup data for synthesis thread. */
+    myData.framesLeft = (unsigned long)(SAMPLE_RATE * 100); /* 100 seconds */
+    myData.numChannels = 1;
+    myData.mode = MODE_OUTPUT;
+
+    opp.device                    = Pa_GetDefaultOutputDevice(); /* Default output. */
+    opp.channelCount              = 2;                           /* Stereo output.  */
+    opp.hostApiSpecificStreamInfo = NULL;
+    opp.sampleFormat              = paFloat32;
+    opp.suggestedLatency          = Pa_GetDeviceInfo(opp.device)->defaultLowOutputLatency;
+
+    HOPEFOR(((result = Pa_OpenStream(&stream, NULL, /* Take NULL as input parame-     */
+                                     &opp,          /* ters, meaning try only output. */
+                                     SAMPLE_RATE, FRAMES_PER_BUFFER,
+                                     paClipOff, QaCallback, &myData )) == paNoError));
+
+    HOPEFOR(((result = Pa_StartStream(NULL))    == paBadStreamPtr));
+    HOPEFOR(((result = Pa_StopStream(NULL))     == paBadStreamPtr));
+    HOPEFOR(((result = Pa_IsStreamStopped(NULL)) == paBadStreamPtr));
+    HOPEFOR(((result = Pa_IsStreamActive(NULL)) == paBadStreamPtr));
+    HOPEFOR(((result = Pa_CloseStream(NULL))    == paBadStreamPtr));
+    HOPEFOR(((result = Pa_SetStreamFinishedCallback(NULL, NULL)) == paBadStreamPtr));
+    HOPEFOR(((result = !Pa_GetStreamInfo(NULL))));
+    HOPEFOR(((result = Pa_GetStreamTime(NULL))  == 0.0));
+    HOPEFOR(((result = Pa_GetStreamCpuLoad(NULL))  == 0.0));
+    HOPEFOR(((result = Pa_ReadStream(NULL, NULL, 0))  == paBadStreamPtr));
+    HOPEFOR(((result = Pa_WriteStream(NULL, NULL, 0))  == paBadStreamPtr));
+
+    /** @todo test Pa_GetStreamReadAvailable and Pa_GetStreamWriteAvailable */
+
+    if (stream != NULL) Pa_CloseStream(stream);
+    return result;
+}
+
+/*---------------------------------------------------------------------*/
+int main(void);
+int main(void)
+{
+    PaError result;
+    
+    EXPECT(((result = Pa_Initialize()) == paNoError));
+    TestBadOpens();
+    TestBadActions();
+error:
+    Pa_Terminate();
+    printf("QA Report: %d passed, %d failed.\n", gNumPassed, gNumFailed);
+    return 0;
+}
diff --git a/src/audio/portaudio/pa_tests/patest1.c b/src/audio/portaudio/pa_tests/patest1.c
new file mode 100644
index 0000000000000000000000000000000000000000..15fdfdd3d18eefd107c9086e21bac0ac00bacd13
--- /dev/null
+++ b/src/audio/portaudio/pa_tests/patest1.c
@@ -0,0 +1,192 @@
+/** @file patest1.c
+	@brief Ring modulate the audio input with a sine wave for 20 seconds.
+	@author Ross Bencina <rossb@audiomulch.com>
+*/
+/*
+ * $Id$
+ *
+ * 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"
+
+#ifndef M_PI
+#define M_PI (3.14159265)
+#endif
+
+#define SAMPLE_RATE (44100)
+
+typedef struct
+{
+    float sine[100];
+    int phase;
+    int sampsToGo;
+}
+patest1data;
+
+static int patest1Callback( const void *inputBuffer, void *outputBuffer,
+                            unsigned long framesPerBuffer,
+                            const PaStreamCallbackTimeInfo* timeInfo,
+                            PaStreamCallbackFlags statusFlags,
+                            void *userData )
+{
+    patest1data *data = (patest1data*)userData;
+    float *in = (float*)inputBuffer;
+    float *out = (float*)outputBuffer;
+    int framesToCalc = framesPerBuffer;
+    unsigned long i = 0;
+    int finished;
+
+    if( data->sampsToGo < framesPerBuffer )
+    {
+        framesToCalc = data->sampsToGo;
+        finished = paComplete;
+    }
+    else
+    {
+        finished = paContinue;
+    }
+
+    for( ; 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<framesPerBuffer; 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;
+    PaStreamParameters      inputParameters, outputParameters;
+    const PaHostErrorInfo*  herr;
+
+    printf("patest1.c\n"); fflush(stdout);
+    printf("Ring modulate input for 20 seconds.\n"); fflush(stdout);
+    
+    /* initialise sinusoidal wavetable */
+    for( i=0; i<100; i++ )
+        data.sine[i] = sin( ((double)i/100.) * M_PI * 2. );
+    data.phase = 0;
+    data.sampsToGo = SAMPLE_RATE * 20;        /* 20 seconds. */
+
+    /* initialise portaudio subsytem */
+    err = Pa_Initialize();
+
+    inputParameters.device = Pa_GetDefaultInputDevice();    /* default input device */
+    inputParameters.channelCount = 2;                       /* stereo input */
+    inputParameters.sampleFormat = paFloat32;               /* 32 bit floating point input */
+    inputParameters.suggestedLatency = Pa_GetDeviceInfo( inputParameters.device )->defaultLowInputLatency;
+    inputParameters.hostApiSpecificStreamInfo = NULL;
+
+    outputParameters.device = Pa_GetDefaultOutputDevice();  /* default output device */
+    outputParameters.channelCount = 2;                      /* stereo output */
+    outputParameters.sampleFormat = paFloat32;              /* 32 bit floating point output */
+    outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultLowOutputLatency;
+    outputParameters.hostApiSpecificStreamInfo = NULL;
+
+    err = Pa_OpenStream(
+                        &stream,
+                        &inputParameters,
+                        &outputParameters,
+                        (double)SAMPLE_RATE, /* Samplerate in Hertz. */
+                        512,                 /* Small buffers */
+                        paClipOff,           /* We won't output out of range samples so don't bother clipping them. */
+                        patest1Callback,
+                        &data );
+    if( err != paNoError ) goto done;
+
+    err = Pa_StartStream( stream );
+    if( err != paNoError ) goto done;
+    
+    printf( "Press any key to end.\n" ); fflush(stdout);
+         
+    getc( stdin ); /* wait for input before exiting */
+
+    err = Pa_AbortStream( stream );
+    if( err != paNoError ) goto done;
+    
+    printf( "Waiting for stream to complete...\n" );
+
+    /* sleep until playback has finished */
+    while( ( err = Pa_IsStreamActive( stream ) ) == 1 ) Pa_Sleep(1000);
+    if( err < 0 ) goto done;
+
+    err = Pa_CloseStream( stream );
+    if( err != paNoError ) goto done;
+
+done:
+    Pa_Terminate();
+
+    if( err != paNoError )
+    {
+        fprintf( stderr, "An error occured while using portaudio\n" );
+        if( err == paUnanticipatedHostError )
+        {
+            fprintf( stderr, " unanticipated host error.\n");
+            herr = Pa_GetLastHostErrorInfo();
+            if (herr)
+            {
+                fprintf( stderr, " Error number: %ld\n", herr->errorCode );
+                if (herr->errorText)
+                    fprintf( stderr, " Error text: %s\n", herr->errorText );
+            }
+            else
+                fprintf( stderr, " Pa_GetLastHostErrorInfo() failed!\n" );
+        }
+        else
+        {
+            fprintf( stderr, " Error number: %d\n", err );
+            fprintf( stderr, " Error text: %s\n", Pa_GetErrorText( err ) );
+        }
+
+        err = 1;          /* Always return 0 or 1, but no other return codes. */
+    }
+
+    printf( "bye\n" );
+
+    return err;
+}
diff --git a/src/audio/portaudio/pa_tests/patest_buffer.c b/src/audio/portaudio/pa_tests/patest_buffer.c
new file mode 100644
index 0000000000000000000000000000000000000000..39c5dd21dcff946a3a63774ce537a787d2cad68e
--- /dev/null
+++ b/src/audio/portaudio/pa_tests/patest_buffer.c
@@ -0,0 +1,180 @@
+/** @file patest_buffer.c
+	@brief Test opening streams with different buffer sizes.
+	@author Phil Burk  http://www.softsynth.com
+*/
+/*
+ * $Id$
+ *
+ * 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 );
+
+/* 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( const void *inputBuffer, void *outputBuffer,
+                            unsigned long framesPerBuffer,
+                            const PaStreamCallbackTimeInfo* timeInfo,
+                            PaStreamCallbackFlags statusFlags,
+                            void *userData )
+{
+    paTestData *data = (paTestData*)userData;
+    short *out = (short*)outputBuffer;
+    unsigned int i;
+    int finished = 0;
+    (void) inputBuffer; /* Prevent "unused variable" warnings. */
+
+    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 %ld\n", buffer_table[i]);
+        err = TestOnce(buffer_table[i]);
+        if( err < 0 ) return 0;
+
+    }
+    return 0;
+}
+
+
+PaError TestOnce( int buffersize )
+{
+    PaStreamParameters outputParameters;
+    PaStream *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;
+    
+    outputParameters.device = Pa_GetDefaultOutputDevice();  /* default output device */
+    outputParameters.channelCount = 2;                      /* stereo output */
+    outputParameters.sampleFormat = paInt16;                /* 32 bit floating point output */
+    outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultLowOutputLatency;
+    outputParameters.hostApiSpecificStreamInfo = NULL;
+    err = Pa_OpenStream(
+              &stream,
+              NULL,                         /* no input */
+              &outputParameters,
+              SAMPLE_RATE,
+              buffersize,                   /* frames per buffer */
+              (paClipOff | paDitherOff),
+              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/src/audio/portaudio/pa_tests/patest_callbackstop.c b/src/audio/portaudio/pa_tests/patest_callbackstop.c
new file mode 100644
index 0000000000000000000000000000000000000000..2da642e4380df46f73c19c0f075e0fa3a57d9fbb
--- /dev/null
+++ b/src/audio/portaudio/pa_tests/patest_callbackstop.c
@@ -0,0 +1,221 @@
+/** @file patest_callbackstop.c
+	@brief Test the paComplete callback result code.
+	@author Ross Bencina <rossb@audiomulch.com>
+*/
+/*
+ * $Id$
+ *
+ * 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   (5)
+#define NUM_LOOPS     (4)
+#define SAMPLE_RATE   (44100)
+#define FRAMES_PER_BUFFER  (67)
+
+#ifndef M_PI
+#define M_PI  (3.14159265)
+#endif
+
+#define TABLE_SIZE   (200)
+typedef struct
+{
+    float sine[TABLE_SIZE];
+    int phase;
+    unsigned long generatedFramesCount;
+    volatile int callbackReturnedPaComplete;
+    volatile int callbackInvokedAfterReturningPaComplete;
+}
+TestData;
+
+/*
+   This routine will be called by the PortAudio stream 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 TestCallback( const void *input, void *output,
+                            unsigned long frameCount,
+                            const PaStreamCallbackTimeInfo* timeInfo,
+                            PaStreamCallbackFlags statusFlags,
+                            void *userData )
+{
+    TestData *data = (TestData*)userData;
+    float *out = (float*)output;
+    unsigned long i;
+    float x;
+
+    (void) input;       /* Prevent unused variable warnings. */
+    (void) timeInfo;
+    (void) statusFlags;
+
+    
+    if( data->callbackReturnedPaComplete )
+        data->callbackInvokedAfterReturningPaComplete = 1;
+
+    for( i=0; i<frameCount; i++ )
+    {
+        /* generate tone */
+        
+        x = data->sine[ data->phase++ ];
+        if( data->phase >= TABLE_SIZE )
+            data->phase -= TABLE_SIZE;
+        
+        *out++ = x;  /* left */
+        *out++ = x;  /* right */
+    }
+
+    data->generatedFramesCount += frameCount;
+    if( data->generatedFramesCount >= (NUM_SECONDS * SAMPLE_RATE) )
+    {
+        data->callbackReturnedPaComplete = 1;
+        return paComplete;
+    }
+    else
+    {
+        return paContinue;
+    }
+}
+
+/*----------------------------------------------------------------------------*/
+int main(void);
+int main(void)
+{
+    PaStreamParameters outputParameters;
+    PaStream *stream;
+    PaError err;
+    TestData data;
+    int i, j;
+
+    
+    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. );
+    }
+    
+    err = Pa_Initialize();
+    if( err != paNoError ) goto error;
+
+    outputParameters.device                    = Pa_GetDefaultOutputDevice();
+    outputParameters.channelCount              = 2;               /* stereo output */
+    outputParameters.sampleFormat              = paFloat32;       /* 32 bit floating point output */
+    outputParameters.suggestedLatency          = Pa_GetDeviceInfo( outputParameters.device )->defaultLowOutputLatency;
+    outputParameters.hostApiSpecificStreamInfo = NULL;
+
+    err = Pa_OpenStream(
+              &stream,
+              NULL, /* no input */
+              &outputParameters,
+              SAMPLE_RATE,
+              FRAMES_PER_BUFFER,
+              paClipOff,      /* output will be in-range, so no need to clip */
+              TestCallback,
+              &data );
+    if( err != paNoError ) goto error;
+
+    printf("Repeating test %d times.\n", NUM_LOOPS );
+    
+    for( i=0; i < NUM_LOOPS; ++i )
+    {
+        data.phase = 0;
+        data.generatedFramesCount = 0;
+        data.callbackReturnedPaComplete = 0;
+        data.callbackInvokedAfterReturningPaComplete = 0;
+
+        err = Pa_StartStream( stream );
+        if( err != paNoError ) goto error;
+
+        printf("Play for %d seconds.\n", NUM_SECONDS );
+
+        /* wait for the callback to complete generating NUM_SECONDS of tone */
+
+        do
+        {
+            Pa_Sleep( 500 );
+        }
+        while( !data.callbackReturnedPaComplete );
+
+        printf( "Callback returned paComplete.\n" );
+        printf( "Waiting for buffers to finish playing...\n" );
+
+        /* wait for stream to become inactive,
+           or for a timeout of approximately NUM_SECONDS
+         */
+     
+        j = 0;
+        while( (err = Pa_IsStreamActive( stream )) == 1 && j < NUM_SECONDS * 2 )
+        {
+            printf(".\n" );
+            Pa_Sleep( 500 );
+            ++j;
+        }
+
+        if( err < 0 )
+        {
+            goto error;
+        }
+        else if( err == 1 )
+        {
+            printf( "TEST FAILED: Timed out waiting for buffers to finish playing.\n" );
+        }
+        else
+        {
+            printf("Buffers finished.\n" );
+        }
+
+        if( data.callbackInvokedAfterReturningPaComplete )
+        {
+            printf( "TEST FAILED: Callback was invoked after returning paComplete.\n" );
+        }
+
+
+        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/src/audio/portaudio/pa_tests/patest_clip.c b/src/audio/portaudio/pa_tests/patest_clip.c
new file mode 100644
index 0000000000000000000000000000000000000000..1d17d11b25f9e9c1fdf0eb282ebddd1ccb2cf834
--- /dev/null
+++ b/src/audio/portaudio/pa_tests/patest_clip.c
@@ -0,0 +1,178 @@
+/** @file patest_clip.c
+	@brief Play a sine wave for several seconds at an amplitude 
+	that would require clipping.
+
+	@author Phil Burk  http://www.softsynth.com
+*/
+/*
+ * $Id$
+ *
+ * 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( const void *inputBuffer, void *outputBuffer,
+                            unsigned long framesPerBuffer,
+                            const PaStreamCallbackTimeInfo* timeInfo,
+                            PaStreamCallbackFlags statusFlags,
+                            void *userData )
+{
+    paTestData *data = (paTestData*)userData;
+    float *out = (float*)outputBuffer;
+    float amplitude = data->amplitude;
+    unsigned int i;
+    (void) inputBuffer; /* Prevent "unused variable" warnings. */
+    (void) timeInfo;
+    (void) statusFlags;
+
+    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 )
+{
+    PaStreamParameters outputParameters;
+    PaStream *stream;
+    PaError err;
+
+    data->left_phase = data->right_phase = 0;
+    data->amplitude = amplitude;
+    
+    err = Pa_Initialize();
+    if( err != paNoError ) goto error;
+
+    outputParameters.device = Pa_GetDefaultOutputDevice(); /* default output device */
+    outputParameters.channelCount = 2;       /* stereo output */
+    outputParameters.sampleFormat = paFloat32; /* 32 bit floating point output */
+    outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultLowOutputLatency;
+    outputParameters.hostApiSpecificStreamInfo = NULL;
+    
+    err = Pa_OpenStream(
+              &stream,
+              NULL, /* no input */
+              &outputParameters,
+              SAMPLE_RATE,
+              1024,
+              flags,
+              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_GetStreamCpuLoad( stream ) );
+
+    err = Pa_CloseStream( stream );
+    if( err != paNoError ) goto error;
+    
+    Pa_Terminate();
+    return paNoError;
+error:
+    return err;
+}
diff --git a/src/audio/portaudio/pa_tests/patest_dither.c b/src/audio/portaudio/pa_tests/patest_dither.c
new file mode 100644
index 0000000000000000000000000000000000000000..10f03e42b9e0a09367c3a6e4b31efc8ad32acc4c
--- /dev/null
+++ b/src/audio/portaudio/pa_tests/patest_dither.c
@@ -0,0 +1,178 @@
+/** @file patest_dither.c
+	@brief Attempt to hear difference between dithered and non-dithered signal.
+
+	This only has an effect if the native format is 16 bit.
+
+	@author Phil Burk  http://www.softsynth.com
+*/
+/*
+ * $Id$
+ *
+ * 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   (5)
+#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;
+                         
+/* 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( const void *inputBuffer, void *outputBuffer,
+                         unsigned long framesPerBuffer,
+			             const PaStreamCallbackTimeInfo *timeInfo,
+			             PaStreamCallbackFlags statusFlags, void *userData )
+{
+    paTestData *data = (paTestData*)userData;
+    float *out = (float*)outputBuffer;
+    float amplitude = data->amplitude;
+    unsigned int i;
+    (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;
+}
+
+/*****************************************************************************/
+/*
+    V18 version did not call Pa_Terminate() if Pa_Initialize() failed.
+    This V19 version ALWAYS calls Pa_Terminate(). PS.
+*/
+PaError PlaySine( paTestData *data, PaStreamFlags flags, float amplitude );
+PaError PlaySine( paTestData *data, PaStreamFlags flags, float amplitude )
+{
+    PaStream*           stream;
+    PaStreamParameters  outputParameters;
+    PaError             err;
+
+    data->left_phase = data->right_phase = 0;
+    data->amplitude  = amplitude;
+
+    err = Pa_Initialize();
+    if (err != paNoError)
+        goto done;
+
+    outputParameters.device = Pa_GetDefaultOutputDevice();  /* default output device */
+    outputParameters.channelCount = 2;                      /* stereo output */
+    outputParameters.hostApiSpecificStreamInfo = NULL;
+    outputParameters.sampleFormat = paFloat32;      /* 32 bit floating point output. */
+                                                    /* When you change this, also    */
+                                                    /* adapt the callback routine!   */ 
+    outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )
+                                        ->defaultLowOutputLatency;   /* Low latency. */
+    err = Pa_OpenStream( &stream,
+                         NULL,                              /* No input. */
+                         &outputParameters,
+                         SAMPLE_RATE,
+                         1024,                              /* frames per buffer */
+                         flags,
+                         sineCallback,
+                         (void*)data );
+    if (err != paNoError)
+        goto done;
+
+    err = Pa_StartStream( stream );
+    if (err != paNoError)
+        goto done;
+
+    Pa_Sleep( NUM_SECONDS * 1000 );
+    printf("CPULoad = %8.6f\n", Pa_GetStreamCpuLoad(stream));
+    
+    err = Pa_CloseStream( stream );
+done:
+    Pa_Sleep( 250 );  /* Just a small silence. */
+    Pa_Terminate();
+    return err;
+}
+
+
+/*******************************************************************/
+int main(void);
+int main(void)
+{
+    PaError     err;
+    paTestData  DATA;
+    int         i;
+    float       amplitude = 4.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 done;
+
+    printf("\nClip..\n");
+    fflush(stdout);
+    err = PlaySine( &DATA, paDitherOff, amplitude );
+    if( err < 0 ) goto done;
+
+    printf("\nClip and Dither..\n");
+    fflush(stdout);
+    err = PlaySine( &DATA, paNoFlag, amplitude );
+done:
+    if (err)
+        {
+        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 ) );
+        err = 1; /* Though PlaySine() already called Pa_Terminate(), */
+        }        /* we may still call Pa_GetErrorText().             */
+    else
+        printf("\n(Don't forget to turn the VOLUME DOWN after listening so carefully.)\n");
+    return err;  /* 0 or 1. */
+}
diff --git a/src/audio/portaudio/pa_tests/patest_hang.c b/src/audio/portaudio/pa_tests/patest_hang.c
new file mode 100644
index 0000000000000000000000000000000000000000..42858f2d0e0b8f07c124d94f75f23adca9be490e
--- /dev/null
+++ b/src/audio/portaudio/pa_tests/patest_hang.c
@@ -0,0 +1,152 @@
+/** @file patest_hang.c
+	@brief Play a sine then hang audio callback to test watchdog.
+	@author Ross Bencina <rossb@audiomulch.com>
+	@author Phil Burk <philburk@softsynth.com>
+*/
+/*
+ * $Id$
+ *
+ * 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 SAMPLE_RATE       (44100)
+#define FRAMES_PER_BUFFER (1024)
+#ifndef M_PI
+#define M_PI              (3.14159265)
+#endif
+#define TWOPI             (M_PI * 2.0)
+
+typedef struct paTestData
+{
+    int    sleepFor;
+    double 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( const void *inputBuffer, void *outputBuffer,
+                           unsigned long framesPerBuffer,
+                           const PaStreamCallbackTimeInfo* timeInfo,
+                           PaStreamCallbackFlags statusFlags,
+                           void *userData )
+{
+    paTestData *data = (paTestData*)userData;
+    float *out = (float*)outputBuffer;
+    unsigned long i;
+    int finished = 0;
+    double phaseInc = 0.02;
+    double phase = data->phase;
+    
+    (void) inputBuffer; /* Prevent unused argument warning. */
+
+    for( i=0; i<framesPerBuffer; i++ )
+    {
+        phase += phaseInc;
+        if( phase > TWOPI ) phase -= TWOPI;
+        /* This is not a very efficient way to calc sines. */
+        *out++ = (float) sin( phase ); /* mono */
+    }
+    
+    if( data->sleepFor > 0 )
+    {
+        Pa_Sleep( data->sleepFor );
+    }
+    
+    data->phase = phase;
+    return finished;
+}
+
+/*******************************************************************/
+int main(void);
+int main(void)
+{
+    PaStream*           stream;
+    PaStreamParameters  outputParameters;
+    PaError             err;
+    int                 i;
+    paTestData          data = {0};
+    
+    printf("PortAudio Test: output sine wave. SR = %d, BufSize = %d\n",
+        SAMPLE_RATE, FRAMES_PER_BUFFER );
+
+    err = Pa_Initialize();
+    if( err != paNoError ) goto error;
+
+    outputParameters.device = Pa_GetDefaultOutputDevice(); /* Default output device. */
+    outputParameters.channelCount = 1;                     /* Mono output. */
+    outputParameters.sampleFormat = paFloat32;             /* 32 bit floating point. */
+    outputParameters.hostApiSpecificStreamInfo = NULL;
+    outputParameters.suggestedLatency          = Pa_GetDeviceInfo(outputParameters.device)
+                                                 ->defaultLowOutputLatency;
+    err = Pa_OpenStream(&stream,
+                        NULL,                    /* No input. */
+                        &outputParameters,
+                        SAMPLE_RATE,
+                        FRAMES_PER_BUFFER,
+                        paClipOff,               /* No out of range samples. */
+                        patestCallback,
+                        &data);
+    if (err != paNoError) goto error;
+    
+    err = Pa_StartStream( stream );
+    if( err != paNoError ) goto error;
+
+    /* Gradually increase sleep time. */
+    /* Was: for( i=0; i<10000; i+= 1000 ) */
+    for(i=0; i <= 1000; i += 100)
+    {
+        printf("Sleep for %d milliseconds in audio callback.\n", i );
+        data.sleepFor = i;
+        Pa_Sleep( ((i<1000) ? 1000 : i) );
+    }
+    
+    printf("Suffer for 10 seconds.\n");
+    Pa_Sleep( 10000 );
+    
+    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/src/audio/portaudio/pa_tests/patest_in_overflow.c b/src/audio/portaudio/pa_tests/patest_in_overflow.c
new file mode 100644
index 0000000000000000000000000000000000000000..a0be2c6a1fa21a5e55ef34e28a8a5ca1d160d49a
--- /dev/null
+++ b/src/audio/portaudio/pa_tests/patest_in_overflow.c
@@ -0,0 +1,224 @@
+/** @file patest_in_overflow.c
+	@brief Count input overflows (using paInputOverflow flag) under 
+	overloaded and normal conditions.
+    This test uses the same method to overload the stream as does
+    patest_out_underflow.c -- it generates sine waves until the cpu load
+    exceeds a certain level. However this test is only concerned with
+    input and so doesn't ouput any sound.
+    
+    @author Ross Bencina <rossb@audiomulch.com>
+	@author Phil Burk <philburk@softsynth.com>
+*/
+/*
+ * $Id$
+ *
+ * This program uses the PortAudio Portable Audio Library.
+ * For more information see: http://www.audiomulch.com/portaudio/
+ * Copyright (c) 1999-2004 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 MAX_LOAD      (1.2)
+#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 sineCount;
+    double phases[MAX_SINES];
+    int countOverflows;
+    int inputOverflowCount;
+}
+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( const void *inputBuffer, void *outputBuffer,
+                           unsigned long framesPerBuffer,
+                           const PaStreamCallbackTimeInfo* timeInfo,
+                           PaStreamCallbackFlags statusFlags,
+                           void *userData )
+{
+    paTestData *data = (paTestData*)userData;
+    float out;          /* variable to hold dummy output */
+    unsigned long i;
+    int j;
+    int finished = paContinue;
+    (void) timeInfo;    /* Prevent unused variable warning. */
+    (void) inputBuffer; /* Prevent unused variable warning. */
+    (void) outputBuffer; /* Prevent unused variable warning. */
+
+    if( data->countOverflows && (statusFlags & paInputOverflow) )
+        data->inputOverflowCount++;
+
+    for( i=0; i<framesPerBuffer; i++ )
+    {
+        float output = 0.0;
+        double phaseInc = 0.02;
+        double phase;
+
+        for( j=0; j<data->sineCount; 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;
+        }
+        /* this is an input-only stream so we don't actually use the output */
+        out = (float) (output / data->sineCount);
+        (void) out; /* suppress unused variable warning*/
+    }
+
+    return finished;
+}
+
+/*******************************************************************/
+int main(void);
+int main(void)
+{
+    PaStreamParameters inputParameters;
+    PaStream *stream;
+    PaError err;
+    int safeSineCount, stressedSineCount;
+    int safeOverflowCount, stressedOverflowCount;
+    paTestData data = {0};
+    double load;
+
+
+    printf("PortAudio Test: input only, no sound output. Load callback by performing calculations, count input overflows. SR = %d, BufSize = %d. MAX_LOAD = %f\n",
+        SAMPLE_RATE, FRAMES_PER_BUFFER, (float)MAX_LOAD );
+
+    err = Pa_Initialize();
+    if( err != paNoError ) goto error;
+
+    inputParameters.device = Pa_GetDefaultInputDevice();  /* default input device */
+    inputParameters.channelCount = 1;                      /* mono output */
+    inputParameters.sampleFormat = paFloat32;              /* 32 bit floating point output */
+    inputParameters.suggestedLatency = Pa_GetDeviceInfo( inputParameters.device )->defaultLowInputLatency;
+    inputParameters.hostApiSpecificStreamInfo = NULL;
+
+    err = Pa_OpenStream(
+              &stream,
+              &inputParameters,
+              NULL,    /* no output */
+              SAMPLE_RATE,
+              FRAMES_PER_BUFFER,
+              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("Establishing load conditions...\n" );
+
+    /* Determine number of sines required to get to 50% */
+    do
+    {
+        data.sineCount++;
+        Pa_Sleep( 100 );
+
+        load = Pa_GetStreamCpuLoad( stream );
+        printf("sineCount = %d, CPU load = %f\n", data.sineCount, load );
+    }
+    while( load < 0.5 && data.sineCount < (MAX_SINES-1));
+
+    safeSineCount = data.sineCount;
+
+    /* Calculate target stress value then ramp up to that level*/
+    stressedSineCount = (int) (2.0 * data.sineCount * MAX_LOAD );
+    if( stressedSineCount > MAX_SINES )
+        stressedSineCount = MAX_SINES;
+    for( ; data.sineCount < stressedSineCount; data.sineCount++ )
+    {
+        Pa_Sleep( 100 );
+        load = Pa_GetStreamCpuLoad( stream );
+        printf("STRESSING: sineCount = %d, CPU load = %f\n", data.sineCount, load );
+    }
+    
+    printf("Counting overflows for 5 seconds.\n");
+    data.countOverflows = 1;
+    Pa_Sleep( 5000 );
+
+    stressedOverflowCount = data.inputOverflowCount;
+
+    data.countOverflows = 0;
+    data.sineCount = safeSineCount;
+
+    printf("Resuming safe load...\n");
+    Pa_Sleep( 1500 );
+    data.inputOverflowCount = 0;
+    Pa_Sleep( 1500 );
+    load = Pa_GetStreamCpuLoad( stream );
+    printf("sineCount = %d, CPU load = %f\n", data.sineCount, load );
+
+    printf("Counting overflows for 5 seconds.\n");
+    data.countOverflows = 1;
+    Pa_Sleep( 5000 );
+
+    safeOverflowCount = data.inputOverflowCount;
+    
+    printf("Stop stream.\n");
+    err = Pa_StopStream( stream );
+    if( err != paNoError ) goto error;
+    
+    err = Pa_CloseStream( stream );
+    if( err != paNoError ) goto error;
+    
+    Pa_Terminate();
+
+    if( stressedOverflowCount == 0 )
+        printf("Test failed, no input overflows detected under stress.\n");
+    else if( safeOverflowCount != 0 )
+        printf("Test failed, %d unexpected overflows detected under safe load.\n", safeOverflowCount);
+    else
+        printf("Test passed, %d expected input overflows detected under stress, 0 unexpected overflows detected under safe load.\n", stressedOverflowCount );
+
+    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/src/audio/portaudio/pa_tests/patest_latency.c b/src/audio/portaudio/pa_tests/patest_latency.c
new file mode 100644
index 0000000000000000000000000000000000000000..f4225112aec28c2419395866816f3044066dbb1f
--- /dev/null
+++ b/src/audio/portaudio/pa_tests/patest_latency.c
@@ -0,0 +1,182 @@
+/** @file
+	@brief Hear the latency caused by big buffers.
+	Play a sine wave and change frequency based on letter input.
+	@author Phil Burk <philburk@softsynth.com>, and Darren Gibbs
+*/
+/*
+ * $Id$
+ *
+ * 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_GetDefaultOutputDevice())
+#define SAMPLE_RATE         (44100)
+#define FRAMES_PER_BUFFER   (64)
+
+#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( const void *inputBuffer, void *outputBuffer,
+                           unsigned long framesPerBuffer,
+                           const PaStreamCallbackTimeInfo* timeInfo,
+                           PaStreamCallbackFlags statusFlags,
+                           void *userData )
+{
+    paTestData *data = (paTestData*)userData;
+    float *out = (float*)outputBuffer;
+    int i;
+
+    (void) inputBuffer; /* Prevent unused variable warning. */
+
+    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)
+{
+    PaStream *stream;
+    PaStreamParameters outputParameters;
+    PaError err;
+    paTestData data;
+    int i;
+    int done = 0;
+
+    printf("PortAudio Test: enter letter then hit ENTER.\n" );
+    /* initialise sinusoidal wavetable */
+    for( i=0; i<TABLE_SIZE; i++ )
+    {
+        data.sine[i] = 0.90f * (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 );
+
+    outputParameters.device = OUTPUT_DEVICE;
+    outputParameters.channelCount = 2;         /* stereo output */
+    outputParameters.sampleFormat = paFloat32; /* 32 bit floating point output */
+    outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultLowOutputLatency;
+    outputParameters.hostApiSpecificStreamInfo = NULL;
+    
+    printf("Requested output latency = %.4f seconds.\n", outputParameters.suggestedLatency );
+    printf("%d frames per buffer.\n.", FRAMES_PER_BUFFER );
+
+    err = Pa_OpenStream(
+              &stream,
+              NULL, /* no input */
+              &outputParameters,
+              SAMPLE_RATE,
+              FRAMES_PER_BUFFER,
+              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");
+    fflush(stdout);
+    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/src/audio/portaudio/pa_tests/patest_leftright.c b/src/audio/portaudio/pa_tests/patest_leftright.c
new file mode 100644
index 0000000000000000000000000000000000000000..158223649df803f72ca3abf4d414ebd13f382df8
--- /dev/null
+++ b/src/audio/portaudio/pa_tests/patest_leftright.c
@@ -0,0 +1,173 @@
+/** @file patest_leftright.c
+	@brief Play different tone sine waves that 
+		alternate between left and right channel.
+
+	The low tone should be on the left channel.
+
+	@author Ross Bencina <rossb@audiomulch.com>
+	@author Phil Burk <philburk@softsynth.com>
+*/
+/*
+ * $Id$
+ *
+ * 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( const void *inputBuffer,
+                           void *outputBuffer,
+                           unsigned long framesPerBuffer,
+                           const PaStreamCallbackTimeInfo* timeInfo,
+                           PaStreamCallbackFlags statusFlags,
+                           void *userData )
+{
+    paTestData *data = (paTestData*)userData;
+    float *out = (float*)outputBuffer;
+    unsigned long i;
+    int finished = 0;
+    /* 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)
+{
+    PaStream *stream;
+    PaStreamParameters outputParameters;
+    PaError err;
+    paTestData data;
+    int i;
+    int timeout;
+    
+    printf("Play different tone sine waves that alternate between left and right channel.\n");
+    printf("The low tone should be on the left channel.\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 = data.toggle = 0;
+    data.countDown = SAMPLE_RATE;
+
+    err = Pa_Initialize();
+    if( err != paNoError ) goto error;
+
+    outputParameters.device = Pa_GetDefaultOutputDevice(); /* default output device */
+    outputParameters.channelCount = 2;       /* stereo output */
+    outputParameters.sampleFormat = paFloat32; /* 32 bit floating point output */
+    outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultLowOutputLatency;
+    outputParameters.hostApiSpecificStreamInfo = NULL;
+
+    err = Pa_OpenStream( &stream,
+                         NULL,                  /* No input. */
+                         &outputParameters,     /* As above. */
+                         SAMPLE_RATE,
+                         FRAMES_PER_BUFFER,
+                         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 )
+    {
+        Pa_Sleep( 300 );        /*(Irix very much likes sleeps <= 1000 ms.)*/
+        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/src/audio/portaudio/pa_tests/patest_longsine.c b/src/audio/portaudio/pa_tests/patest_longsine.c
new file mode 100644
index 0000000000000000000000000000000000000000..3a6ec39ef5f38d5b0e500f53c861b0ce751e02ae
--- /dev/null
+++ b/src/audio/portaudio/pa_tests/patest_longsine.c
@@ -0,0 +1,140 @@
+/** @file patest_longsine.c
+	@brief Play a sine wave until ENTER hit.
+	@author Phil Burk  http://www.softsynth.com
+*/
+/*
+ * $Id$
+ *
+ * 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(const void*                     inputBuffer,
+                          void*                           outputBuffer,
+                          unsigned long                   framesPerBuffer,
+                          const PaStreamCallbackTimeInfo* timeInfo,
+                          PaStreamCallbackFlags           statusFlags,
+                          void*                           userData)
+{
+    paTestData *data = (paTestData*)userData;
+    float *out = (float*)outputBuffer;
+    unsigned int i;
+    (void) inputBuffer; /* Prevent unused argument warning. */
+    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)
+{
+    PaStreamParameters outputParameters;
+    PaStream *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;
+
+    outputParameters.device = Pa_GetDefaultOutputDevice(); /* default output device */
+    outputParameters.channelCount = 2;                     /* stereo output */
+    outputParameters.sampleFormat = paFloat32;             /* 32 bit floating point output */
+    outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultLowOutputLatency;
+    outputParameters.hostApiSpecificStreamInfo = NULL;
+
+    err = Pa_OpenStream( &stream,
+                         NULL,              /* No input. */
+                         &outputParameters, /* As above. */
+                         SAMPLE_RATE,
+                         256,               /* Frames per buffer. */
+                         paClipOff,         /* No out of range samples expected. */
+                         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/src/audio/portaudio/pa_tests/patest_many.c b/src/audio/portaudio/pa_tests/patest_many.c
new file mode 100644
index 0000000000000000000000000000000000000000..6a678467b86b26a7a0bb9114b130b118a9739e03
--- /dev/null
+++ b/src/audio/portaudio/pa_tests/patest_many.c
@@ -0,0 +1,198 @@
+/** @file patest_many.c
+	@brief Start and stop the PortAudio Driver multiple times.
+	@author Phil Burk  http://www.softsynth.com
+*/
+/*
+ * $Id$
+ *
+ * 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( const void *inputBuffer, void *outputBuffer,
+                            unsigned long framesPerBuffer,
+                            const PaStreamCallbackTimeInfo* timeInfo,
+                            PaStreamCallbackFlags statusFlags,
+                            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( const void *inputBuffer, void *outputBuffer,
+                            unsigned long framesPerBuffer,
+                            const PaStreamCallbackTimeInfo* timeInfo,
+                            PaStreamCallbackFlags statusFlags,
+                            void *userData )
+{
+    paTestData *data = (paTestData*)userData;
+    short *out = (short*)outputBuffer;
+    unsigned int i;
+    int finished = 0;
+    (void) inputBuffer; /* Prevent "unused variable" warnings. */
+
+    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 )
+{
+    PaStreamParameters outputParameters;
+    PaStream *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;
+
+    outputParameters.device = Pa_GetDefaultOutputDevice();  /* default output device */
+    outputParameters.channelCount = 2;                      /* stereo output */
+    outputParameters.sampleFormat = paInt16;
+    outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultLowOutputLatency;
+    outputParameters.hostApiSpecificStreamInfo = NULL;
+    err = Pa_OpenStream(
+              &stream,
+              NULL,         /* no input */
+              &outputParameters,
+              SAMPLE_RATE,
+              1024,         /* frames per buffer */
+              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/src/audio/portaudio/pa_tests/patest_maxsines.c b/src/audio/portaudio/pa_tests/patest_maxsines.c
new file mode 100644
index 0000000000000000000000000000000000000000..56592ea47d73e8dba5139f3e715895bacbb9fd56
--- /dev/null
+++ b/src/audio/portaudio/pa_tests/patest_maxsines.c
@@ -0,0 +1,204 @@
+/** @file patest_maxsines.c
+	@brief How many sine waves can we calculate and play in less than 80% CPU Load.
+	@author Ross Bencina <rossb@audiomulch.com>
+	@author Phil Burk <philburk@softsynth.com>
+*/
+/*
+ * $Id$
+ *
+ * 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 MAX_USAGE     (0.8)
+#define SAMPLE_RATE   (44100)
+#define FREQ_TO_PHASE_INC(freq)   (freq/(float)SAMPLE_RATE)
+
+#define MIN_PHASE_INC  FREQ_TO_PHASE_INC(200.0f)
+#define MAX_PHASE_INC  (MIN_PHASE_INC * (1 << 5))
+
+#define FRAMES_PER_BUFFER  (512)
+#ifndef M_PI
+#define M_PI  (3.14159265)
+#endif
+#define TWOPI (M_PI * 2.0)
+
+#define TABLE_SIZE   (512)
+
+typedef struct paTestData
+{
+    int numSines;
+    float sine[TABLE_SIZE + 1]; /* add one for guard point for interpolation */
+    float phases[MAX_SINES];
+}
+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(const void*                     inputBuffer,
+                          void*                           outputBuffer,
+                          unsigned long                   framesPerBuffer,
+                          const PaStreamCallbackTimeInfo* timeInfo,
+                          PaStreamCallbackFlags           statusFlags,
+                          void*                           userData )
+{
+    paTestData *data = (paTestData*)userData;
+    float *out = (float*)outputBuffer;
+    float outSample;
+    float scaler;
+    int numForScale;
+    unsigned long i;
+    int j;
+    int finished = 0;
+    (void) inputBuffer; /* Prevent unused argument warning. */
+
+    /* Determine amplitude scaling factor */
+    numForScale = data->numSines;
+    if( numForScale < 8 ) numForScale = 8;  /* prevent pops at beginning */
+    scaler = 1.0f / numForScale;
+    
+    for( i=0; i<framesPerBuffer; i++ )
+    {
+        float output = 0.0;
+        float phaseInc = MIN_PHASE_INC;
+        float phase;
+        for( j=0; j<data->numSines; j++ )
+        {
+            /* Advance phase of next oscillator. */
+            phase = data->phases[j];
+            phase += phaseInc;
+            if( phase >= 1.0 ) phase -= 1.0;
+
+            output += LookupSine(data, phase); 
+            data->phases[j] = phase;
+            
+            phaseInc *= 1.02f;
+            if( phaseInc > MAX_PHASE_INC ) phaseInc = MIN_PHASE_INC;
+        }
+
+        outSample = (float) (output * scaler);
+        *out++ = outSample; /* Left */
+        *out++ = outSample; /* Right */
+    }
+    return finished;
+}
+
+/*******************************************************************/
+int main(void);
+int main(void)
+{
+	int                 i;
+    PaStream*           stream;
+    PaStreamParameters  outputParameters;
+    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 */
+    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 */
+
+    err = Pa_Initialize();
+    if( err != paNoError )
+        goto error;
+    outputParameters.device                    = Pa_GetDefaultOutputDevice(); /* Default output device. */
+    outputParameters.channelCount              = 2;                           /* Stereo output. */
+    outputParameters.sampleFormat              = paFloat32;                   /* 32 bit floating point output. */
+    outputParameters.hostApiSpecificStreamInfo = NULL;
+    outputParameters.suggestedLatency          = Pa_GetDeviceInfo(outputParameters.device)
+                                                 ->defaultHighOutputLatency;
+    err = Pa_OpenStream(&stream,
+                        NULL,               /* no input */
+                        &outputParameters,
+                        SAMPLE_RATE,
+                        FRAMES_PER_BUFFER,
+                        paClipOff,          /* No out of range samples should occur. */
+                        patestCallback,
+                        &data);
+    if( err != paNoError )
+        goto error;
+
+    err = Pa_StartStream( stream );
+    if( err != paNoError )
+        goto error;
+
+    /* Play an increasing number of sine waves until we hit MAX_USAGE */
+    do  {
+        data.numSines++;
+        Pa_Sleep(200);
+        load = Pa_GetStreamCpuLoad(stream);
+        printf("numSines = %d, CPU load = %f\n", data.numSines, load );
+        fflush(stdout);
+        } while((load < MAX_USAGE) && (data.numSines < MAX_SINES));
+
+    Pa_Sleep(2000);     /* Stay for 2 seconds around 80% CPU. */
+
+    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/src/audio/portaudio/pa_tests/patest_mono.c b/src/audio/portaudio/pa_tests/patest_mono.c
new file mode 100644
index 0000000000000000000000000000000000000000..e7b57362fdf166f45173551e31d572359691ddc7
--- /dev/null
+++ b/src/audio/portaudio/pa_tests/patest_mono.c
@@ -0,0 +1,145 @@
+/*
+ * $Id$
+ * patest_sine.c
+ * Play a monophonic 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   (10)
+#define SAMPLE_RATE   (44100)
+#define AMPLITUDE     (0.8)
+#define FRAMES_PER_BUFFER  (64)
+#define OUTPUT_DEVICE Pa_GetDefaultOutputDevice()
+
+#ifndef M_PI
+#define M_PI  (3.14159265)
+#endif
+
+#define TABLE_SIZE   (200)
+typedef struct
+{
+    float sine[TABLE_SIZE];
+    int 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( const void *inputBuffer, void *outputBuffer,
+                            unsigned long framesPerBuffer,
+                            const PaStreamCallbackTimeInfo* timeInfo,
+                            PaStreamCallbackFlags statusFlags,
+                            void *userData )
+{
+    paTestData *data = (paTestData*)userData;
+    float *out = (float*)outputBuffer;
+    unsigned long i;
+    int finished = 0;
+    /* avoid unused variable warnings */
+    (void) inputBuffer;
+    (void) timeInfo;
+    (void) statusFlags;
+    for( i=0; i<framesPerBuffer; i++ )
+    {
+        *out++ = data->sine[data->phase];  /* left */
+        data->phase += 1;
+        if( data->phase >= TABLE_SIZE ) data->phase -= TABLE_SIZE;
+    }
+    return finished;
+}
+
+/*******************************************************************/
+int main(void);
+int main(void)
+{
+    PaStreamParameters outputParameters;
+    PaStream *stream;
+    PaError err;
+    paTestData data;
+    int i;
+    printf("PortAudio Test: output MONO 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) (AMPLITUDE * sin( ((double)i/(double)TABLE_SIZE) * M_PI * 2. ));
+    }
+    data.phase = 0;
+    
+    err = Pa_Initialize();
+    if( err != paNoError ) goto error;
+
+    outputParameters.device = OUTPUT_DEVICE;
+    outputParameters.channelCount = 1;       /* MONO output */
+    outputParameters.sampleFormat = paFloat32; /* 32 bit floating point output */
+    outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultLowOutputLatency;
+    outputParameters.hostApiSpecificStreamInfo = NULL;
+
+    err = Pa_OpenStream(
+              &stream,
+              NULL, /* no input */
+              &outputParameters,
+              SAMPLE_RATE,
+              FRAMES_PER_BUFFER,
+              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 ); fflush(stdout);
+    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;
+}
\ No newline at end of file
diff --git a/src/audio/portaudio/pa_tests/patest_mono_asio_channel_select.c b/src/audio/portaudio/pa_tests/patest_mono_asio_channel_select.c
new file mode 100644
index 0000000000000000000000000000000000000000..4c4ffccdfa9aab056e117027a8867a6218dc6936
--- /dev/null
+++ b/src/audio/portaudio/pa_tests/patest_mono_asio_channel_select.c
@@ -0,0 +1,155 @@
+/*
+ * $Id$
+ * patest_sine.c
+ * Play a monophonic 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"
+#include "pa_asio.h"
+
+#define NUM_SECONDS   (10)
+#define SAMPLE_RATE   (44100)
+#define AMPLITUDE     (0.8)
+#define FRAMES_PER_BUFFER  (64)
+#define OUTPUT_DEVICE Pa_GetDefaultOutputDevice()
+
+#ifndef M_PI
+#define M_PI  (3.14159265)
+#endif
+
+#define TABLE_SIZE   (200)
+typedef struct
+{
+    float sine[TABLE_SIZE];
+    int 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( const void *inputBuffer, void *outputBuffer,
+                            unsigned long framesPerBuffer,
+                            const PaStreamCallbackTimeInfo* timeInfo,
+                            PaStreamCallbackFlags statusFlags,
+                            void *userData )
+{
+    paTestData *data = (paTestData*)userData;
+    float *out = (float*)outputBuffer;
+    unsigned long i;
+    int finished = 0;
+    /* avoid unused variable warnings */
+    (void) inputBuffer;
+    (void) timeInfo;
+    (void) statusFlags;
+    for( i=0; i<framesPerBuffer; i++ )
+    {
+        *out++ = data->sine[data->phase];  /* left */
+        data->phase += 1;
+        if( data->phase >= TABLE_SIZE ) data->phase -= TABLE_SIZE;
+    }
+    return finished;
+}
+
+/*******************************************************************/
+int main(void);
+int main(void)
+{
+    PaStreamParameters outputParameters;
+    PaAsioStreamInfo asioOutputInfo;
+    PaStream *stream;
+    PaError err;
+    paTestData data;
+    int outputChannelSelectors[1];
+    int i;
+    printf("PortAudio Test: output MONO 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) (AMPLITUDE * sin( ((double)i/(double)TABLE_SIZE) * M_PI * 2. ));
+    }
+    data.phase = 0;
+    
+    err = Pa_Initialize();
+    if( err != paNoError ) goto error;
+
+    outputParameters.device = OUTPUT_DEVICE;
+    outputParameters.channelCount = 1;       /* MONO output */
+    outputParameters.sampleFormat = paFloat32; /* 32 bit floating point output */
+    outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultLowOutputLatency;
+
+    asioOutputInfo.size = sizeof(PaAsioStreamInfo);
+    asioOutputInfo.hostApiType = paASIO;
+    asioOutputInfo.version = 1;
+    asioOutputInfo.flags = paAsioUseChannelSelectors;
+    outputChannelSelectors[0] = 1; /* select the second (right) ASIO device channel */
+    asioOutputInfo.channelSelectors = outputChannelSelectors;
+    outputParameters.hostApiSpecificStreamInfo = &asioOutputInfo;
+
+    err = Pa_OpenStream(
+              &stream,
+              NULL, /* no input */
+              &outputParameters,
+              SAMPLE_RATE,
+              FRAMES_PER_BUFFER,
+              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 ); fflush(stdout);
+    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/src/audio/portaudio/pa_tests/patest_multi_sine.c b/src/audio/portaudio/pa_tests/patest_multi_sine.c
new file mode 100644
index 0000000000000000000000000000000000000000..161c88222c9151e037b6dbee3bae4b5d7cc195e0
--- /dev/null
+++ b/src/audio/portaudio/pa_tests/patest_multi_sine.c
@@ -0,0 +1,194 @@
+/** @file patest_multi_sine.c
+	@brief Play a different sine wave on each channel.
+	@author Phil Burk  http://www.softsynth.com
+*/
+/*
+ * $Id$
+ *
+ * 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)
+#define FRAMES_PER_BUFFER (256)
+#define FREQ_INCR         (300.0 / SAMPLE_RATE)
+#define MAX_CHANNELS      (64)
+
+#ifndef M_PI
+#define M_PI  (3.14159265)
+#endif
+
+typedef struct
+{
+    short   interleaved;          /* Nonzero for interleaved / zero for non-interleaved. */
+    int     numChannels;          /* Actually used. */
+    double  phases[MAX_CHANNELS]; /* Each channel gets its' own frequency. */
+}
+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(const void*                     inputBuffer,
+                          void*                           outputBuffer,
+                          unsigned long                   framesPerBuffer,
+                          const PaStreamCallbackTimeInfo* timeInfo,
+                          PaStreamCallbackFlags           statusFlags,
+                          void*                           userData)
+{
+    int         frameIndex, channelIndex;
+    float**     outputs = (float**)outputBuffer;
+    paTestData* data    = (paTestData*)userData;
+
+    (void) inputBuffer;     /* Prevent unused arg warning. */
+    if (data->interleaved)
+        {
+        float *out = (float*)outputBuffer;      /* interleaved version */
+        for( frameIndex=0; frameIndex<(int)framesPerBuffer; frameIndex++ )
+            {
+            for( channelIndex=0; channelIndex<data->numChannels; channelIndex++ )
+                {
+                /* Output sine wave on every channel. */
+                *out++ = (float) sin(data->phases[channelIndex]);
+
+                /* Play each channel at a higher frequency. */
+                data->phases[channelIndex] += FREQ_INCR * (4 + channelIndex);
+                if( data->phases[channelIndex] >= (2.0 * M_PI) ) data->phases[channelIndex] -= (2.0 * M_PI);
+                }
+            }
+        }
+    else
+        {
+        for( frameIndex=0; frameIndex<(int)framesPerBuffer; frameIndex++ )
+            {
+            for( channelIndex=0; channelIndex<data->numChannels; channelIndex++ )
+                {
+                /* Output sine wave on every channel. */
+                outputs[channelIndex][frameIndex] = (float) sin(data->phases[channelIndex]);
+
+                /* Play each channel at a higher frequency. */
+                data->phases[channelIndex] += FREQ_INCR * (4 + channelIndex);
+                if( data->phases[channelIndex] >= (2.0 * M_PI) ) data->phases[channelIndex] -= (2.0 * M_PI);
+                }
+            }
+        }
+    return 0;
+}
+
+/*******************************************************************/
+int test(short interleaved)
+{
+    PaStream*           stream;
+    PaStreamParameters  outputParameters;
+    PaError             err;
+    const PaDeviceInfo* pdi;
+    paTestData          data;
+    short               n;
+
+    outputParameters.device = Pa_GetDefaultOutputDevice();  /* Default output device, max channels. */
+    pdi = Pa_GetDeviceInfo(outputParameters.device);
+    outputParameters.channelCount = pdi->maxOutputChannels;
+    if (outputParameters.channelCount > MAX_CHANNELS)
+        outputParameters.channelCount = MAX_CHANNELS;
+    outputParameters.sampleFormat = paFloat32;              /* 32 bit floating point output */
+    outputParameters.suggestedLatency = pdi->defaultLowOutputLatency;
+    outputParameters.hostApiSpecificStreamInfo = NULL;
+    
+    data.interleaved = interleaved;
+    data.numChannels = outputParameters.channelCount;
+    for (n = 0; n < data.numChannels; n++)
+        data.phases[n] = 0.0; /* Phases wrap and maybe don't need initialisation. */
+    printf("%d ", data.numChannels);
+    if (interleaved)
+        printf("interleaved ");
+    else
+        {
+        printf(" non-interleaved ");
+        outputParameters.sampleFormat |= paNonInterleaved;
+        }
+    printf("channels.\n");
+
+    err = Pa_OpenStream(&stream,
+                        NULL,               /* No input. */
+                        &outputParameters,
+                        SAMPLE_RATE,        /* Sample rate. */
+                        FRAMES_PER_BUFFER,  /* Frames per buffer. */
+                        paClipOff,          /* Samples never out of range, no clipping. */
+                        patestCallback,
+                        &data);
+    if (err == paNoError)
+        {
+        err = Pa_StartStream(stream);
+        if (err == paNoError)
+            {
+            printf("Hit ENTER to stop this test.\n");
+            getchar();
+            err = Pa_StopStream(stream);
+            }
+        Pa_CloseStream( stream );
+        }
+    return err;    
+}
+
+
+/*******************************************************************/
+int main(void)
+{
+    PaError err;
+
+    printf("PortAudio Test: output sine wave on each channel.\n" );
+
+    err = Pa_Initialize();
+    if (err != paNoError)
+        goto done;
+
+    err = test(1);          /* 1 means interleaved. */
+    if (err != paNoError)
+        goto done;
+
+    err = test(0);          /* 0 means not interleaved. */
+    if (err != paNoError)
+        goto done;
+
+    printf("Test finished.\n");
+done:
+    if (err)
+        {
+        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));
+        }
+    Pa_Terminate();
+    return 0;
+}
diff --git a/src/audio/portaudio/pa_tests/patest_out_underflow.c b/src/audio/portaudio/pa_tests/patest_out_underflow.c
new file mode 100644
index 0000000000000000000000000000000000000000..8e6f2789777cdf91625089e56a08ca6ae7e98f48
--- /dev/null
+++ b/src/audio/portaudio/pa_tests/patest_out_underflow.c
@@ -0,0 +1,217 @@
+/** @file patest_out_underflow.c
+	@brief Count output underflows (using paOutputUnderflow flag) 
+	under overloaded and normal conditions.
+	@author Ross Bencina <rossb@audiomulch.com>
+	@author Phil Burk <philburk@softsynth.com>
+*/
+/*
+ * $Id$
+ *
+ * This program uses the PortAudio Portable Audio Library.
+ * For more information see: http://www.audiomulch.com/portaudio/
+ * Copyright (c) 1999-2004 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 MAX_LOAD      (1.2)
+#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 sineCount;
+    double phases[MAX_SINES];
+    int countUnderflows;
+    int outputUnderflowCount;
+}
+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( const void *inputBuffer, void *outputBuffer,
+                           unsigned long framesPerBuffer,
+                           const PaStreamCallbackTimeInfo* timeInfo,
+                           PaStreamCallbackFlags statusFlags,
+                           void *userData )
+{
+    paTestData *data = (paTestData*)userData;
+    float *out = (float*)outputBuffer;
+    unsigned long i;
+    int j;
+    int finished = paContinue;
+    (void) timeInfo;    /* Prevent unused variable warning. */
+    (void) inputBuffer; /* Prevent unused variable warning. */
+
+
+    if( data->countUnderflows && (statusFlags & paOutputUnderflow) )
+        data->outputUnderflowCount++;
+
+    for( i=0; i<framesPerBuffer; i++ )
+    {
+        float output = 0.0;
+        double phaseInc = 0.02;
+        double phase;
+
+        for( j=0; j<data->sineCount; 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->sineCount);
+    }
+
+    return finished;
+}
+
+/*******************************************************************/
+int main(void);
+int main(void)
+{
+    PaStreamParameters outputParameters;
+    PaStream *stream;
+    PaError err;
+    int safeSineCount, stressedSineCount;
+    int safeUnderflowCount, stressedUnderflowCount;
+    paTestData data = {0};
+    double load;
+
+
+    printf("PortAudio Test: output sine waves, count underflows. SR = %d, BufSize = %d. MAX_LOAD = %f\n",
+        SAMPLE_RATE, FRAMES_PER_BUFFER, (float)MAX_LOAD );
+
+    err = Pa_Initialize();
+    if( err != paNoError ) goto error;
+    
+    outputParameters.device = Pa_GetDefaultOutputDevice();  /* default output device */
+    outputParameters.channelCount = 1;                      /* mono output */
+    outputParameters.sampleFormat = paFloat32;              /* 32 bit floating point output */
+    outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultLowOutputLatency;
+    outputParameters.hostApiSpecificStreamInfo = NULL;
+
+    err = Pa_OpenStream(
+              &stream,
+              NULL,         /* no input */
+              &outputParameters,
+              SAMPLE_RATE,
+              FRAMES_PER_BUFFER,
+              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("Establishing load conditions...\n" );
+
+    /* Determine number of sines required to get to 50% */
+    do
+    {
+        data.sineCount++;
+        Pa_Sleep( 100 );
+
+        load = Pa_GetStreamCpuLoad( stream );
+        printf("sineCount = %d, CPU load = %f\n", data.sineCount, load );
+    }
+    while( load < 0.5 && data.sineCount < (MAX_SINES-1));
+
+    safeSineCount = data.sineCount;
+
+    /* Calculate target stress value then ramp up to that level*/
+    stressedSineCount = (int) (2.0 * data.sineCount * MAX_LOAD );
+    if( stressedSineCount > MAX_SINES )
+        stressedSineCount = MAX_SINES;
+    for( ; data.sineCount < stressedSineCount; data.sineCount++ )
+    {
+        Pa_Sleep( 100 );
+        load = Pa_GetStreamCpuLoad( stream );
+        printf("STRESSING: sineCount = %d, CPU load = %f\n", data.sineCount, load );
+    }
+    
+    printf("Counting underflows for 5 seconds.\n");
+    data.countUnderflows = 1;
+    Pa_Sleep( 5000 );
+
+    stressedUnderflowCount = data.outputUnderflowCount;
+
+    data.countUnderflows = 0;
+    data.sineCount = safeSineCount;
+
+    printf("Resuming safe load...\n");
+    Pa_Sleep( 1500 );
+    data.outputUnderflowCount = 0;
+    Pa_Sleep( 1500 );
+    load = Pa_GetStreamCpuLoad( stream );
+    printf("sineCount = %d, CPU load = %f\n", data.sineCount, load );
+
+    printf("Counting underflows for 5 seconds.\n");
+    data.countUnderflows = 1;
+    Pa_Sleep( 5000 );
+
+    safeUnderflowCount = data.outputUnderflowCount;
+    
+    printf("Stop stream.\n");
+    err = Pa_StopStream( stream );
+    if( err != paNoError ) goto error;
+    
+    err = Pa_CloseStream( stream );
+    if( err != paNoError ) goto error;
+    
+    Pa_Terminate();
+
+    if( stressedUnderflowCount == 0 )
+        printf("Test failed, no output underflows detected under stress.\n");
+    else if( safeUnderflowCount != 0 )
+        printf("Test failed, %d unexpected underflows detected under safe load.\n", safeUnderflowCount);
+    else
+        printf("Test passed, %d expected output underflows detected under stress, 0 unexpected underflows detected under safe load.\n", stressedUnderflowCount );
+
+    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/src/audio/portaudio/pa_tests/patest_pink.c b/src/audio/portaudio/pa_tests/patest_pink.c
new file mode 100644
index 0000000000000000000000000000000000000000..f2db85d9c6d4bdc91686d1be4e2799aa7c1a961d
--- /dev/null
+++ b/src/audio/portaudio/pa_tests/patest_pink.c
@@ -0,0 +1,269 @@
+/** @file patest_pink.c
+	@brief Generate Pink Noise using Gardner method.
+
+	Optimization suggested by James McCartney uses a tree
+	to select which random value to replace.
+<pre>
+	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   
+</pre>                            
+	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
+*/
+/*
+ * $Id$
+ *
+ * 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(const void*                     inputBuffer,
+                          void*                           outputBuffer,
+                          unsigned long                   framesPerBuffer,
+			              const PaStreamCallbackTimeInfo* timeInfo,
+			              PaStreamCallbackFlags           statusFlags,
+                          void*                           userData)
+{
+    int finished;
+    int i;
+    int numFrames;
+    paTestData *data = (paTestData*)userData;
+    float *out = (float*)outputBuffer;
+    (void) inputBuffer; /* Prevent "unused variable" warnings. */
+
+    /* 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)
+{
+    PaStream*           stream;
+    PaError             err;
+    paTestData          data;
+    PaStreamParameters  outputParameters;
+    int                 totalSamps;
+    static const double SR  = 44100.0;
+    static const int    FPB = 2048; /* Frames per buffer: 46 ms buffers. */
+    
+    /* 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 = (int)(60.0 * SR);   /* Play a whole minute. */
+    err = Pa_Initialize();
+    if( err != paNoError ) goto error;
+
+    /* Open a stereo PortAudio stream so we can hear the result. */
+    outputParameters.device = Pa_GetDefaultOutputDevice(); /* Take the default output device. */
+    outputParameters.channelCount = 2;                     /* Stereo output, most likely supported. */
+    outputParameters.hostApiSpecificStreamInfo = NULL;
+    outputParameters.sampleFormat = paFloat32;             /* 32 bit floating point output. */
+    outputParameters.suggestedLatency =
+                     Pa_GetDeviceInfo(outputParameters.device)->defaultLowOutputLatency;
+    err = Pa_OpenStream(&stream,
+                        NULL,                              /* No input. */
+                        &outputParameters,
+                        SR,                                /* Sample rate. */
+                        FPB,                               /* Frames per buffer. */
+                        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("Stereo pink noise for one minute...\n");
+
+    while( ( err = Pa_IsStreamActive( stream ) ) == 1 ) Pa_Sleep(100);
+    if( err < 0 ) goto error;
+
+    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/src/audio/portaudio/pa_tests/patest_prime.c b/src/audio/portaudio/pa_tests/patest_prime.c
new file mode 100644
index 0000000000000000000000000000000000000000..47d1f5f563e54a9224e823d26e27a9e2b0b71117
--- /dev/null
+++ b/src/audio/portaudio/pa_tests/patest_prime.c
@@ -0,0 +1,223 @@
+/** @file patest_prime.c
+	@brief Test stream priming mode.
+	@author Ross Bencina http://www.audiomulch.com/~rossb
+*/
+
+/*
+ * $Id$
+ *
+ * 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_util.h"
+
+#define NUM_BEEPS           (3)
+#define SAMPLE_RATE         (44100)
+#define SAMPLE_PERIOD       (1.0/44100.0)
+#define FRAMES_PER_BUFFER   (256)
+#define BEEP_DURATION       (400)
+#define IDLE_DURATION       (SAMPLE_RATE*2)      /* 2 seconds */
+#define SLEEP_MSEC          (50)
+
+#define STATE_BKG_IDLE      (0)
+#define STATE_BKG_BEEPING   (1)
+
+typedef struct
+{
+    float        leftPhase;
+    float        rightPhase;
+    int          state;
+    int          beepCountdown;
+    int          idleCountdown;
+}
+paTestData;
+
+static void InitializeTestData( paTestData *testData )
+{
+    testData->leftPhase = 0;
+    testData->rightPhase = 0;
+    testData->state = STATE_BKG_BEEPING;
+    testData->beepCountdown = BEEP_DURATION;
+    testData->idleCountdown = IDLE_DURATION;
+}
+
+/* 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( const void *inputBuffer, void *outputBuffer,
+                           unsigned long framesPerBuffer,
+			               const PaStreamCallbackTimeInfo *timeInfo,
+			               PaStreamCallbackFlags statusFlags, void *userData )
+{
+    /* Cast data passed through stream to our structure. */
+    paTestData *data = (paTestData*)userData;
+    float *out = (float*)outputBuffer;
+    unsigned int i;
+    int result = paContinue;
+
+    /* supress unused parameter warnings */
+    (void) inputBuffer;
+    (void) timeInfo;
+    (void) statusFlags;
+
+    for( i=0; i<framesPerBuffer; i++ )
+    {
+        switch( data->state )
+        {
+        case STATE_BKG_IDLE:
+            *out++ = 0.0;  /* left */
+            *out++ = 0.0;  /* right */
+            --data->idleCountdown;
+            
+            if( data->idleCountdown <= 0 ) result = paComplete;
+            break;
+
+        case STATE_BKG_BEEPING:
+            if( data->beepCountdown <= 0 )
+            {
+                data->state = STATE_BKG_IDLE;
+                *out++ = 0.0;  /* left */
+                *out++ = 0.0;  /* right */
+            }
+            else
+            {
+                /* Play sawtooth wave. */
+                *out++ = data->leftPhase;  /* left */
+                *out++ = data->rightPhase;  /* right */
+                /* Generate simple sawtooth phaser that ranges between -1.0 and 1.0. */
+                data->leftPhase += 0.01f;
+                /* When signal reaches top, drop back down. */
+                if( data->leftPhase >= 1.0f ) data->leftPhase -= 2.0f;
+                /* higher pitch so we can distinguish left and right. */
+                data->rightPhase += 0.03f;
+                if( data->rightPhase >= 1.0f ) data->rightPhase -= 2.0f;
+            }
+            --data->beepCountdown;
+            break;
+        }
+    }
+    
+    return result;
+}
+
+/*******************************************************************/
+static PaError DoTest( int flags )
+{
+    PaStream *stream;
+    PaError    err;
+    paTestData data;
+    PaStreamParameters outputParameters;
+
+    InitializeTestData( &data );       
+
+    outputParameters.device = Pa_GetDefaultOutputDevice();
+    outputParameters.channelCount = 2;
+    outputParameters.hostApiSpecificStreamInfo = NULL;
+    outputParameters.sampleFormat = paFloat32;
+    outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultHighOutputLatency;
+
+    /* Open an audio I/O stream. */
+    err = Pa_OpenStream(
+        &stream,
+        NULL,                         /* no input */
+        &outputParameters,
+        SAMPLE_RATE,
+        FRAMES_PER_BUFFER,            /* frames per buffer */
+        paClipOff | flags,      /* 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("hear \"BEEP\"\n" );
+    fflush(stdout);
+
+    while( ( err = Pa_IsStreamActive( stream ) ) == 1 ) Pa_Sleep(SLEEP_MSEC);
+    if( err < 0 ) goto error;
+
+    err = Pa_StopStream( stream );
+    if( err != paNoError ) goto error;
+
+    err = Pa_CloseStream( stream );
+    if( err != paNoError ) goto error;
+
+    return err;
+error:
+    return err;
+}
+
+/*******************************************************************/
+int main(void);
+int main(void)
+{
+    PaError    err = paNoError;
+    int        i;
+
+    /* Initialize library before making any other calls. */
+    err = Pa_Initialize();
+    if( err != paNoError ) goto error;
+    
+    printf("PortAudio Test: Testing stream playback with no priming.\n");
+    printf("PortAudio Test: you should see BEEP before you hear it.\n");
+    printf("BEEP %d times.\n", NUM_BEEPS );
+
+    for( i=0; i< NUM_BEEPS; ++i )
+    {
+        err = DoTest( 0 );
+        if( err != paNoError )
+            goto error;
+    }
+
+    printf("PortAudio Test: Testing stream playback with priming.\n");
+    printf("PortAudio Test: you should see BEEP around the same time you hear it.\n");
+    for( i=0; i< NUM_BEEPS; ++i )
+    {
+        err = DoTest( paPrimeOutputBuffersUsingStreamCallback );
+        if( err != paNoError )
+            goto error;
+    }
+
+    printf("Test finished.\n");
+
+    Pa_Terminate();
+    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/src/audio/portaudio/pa_tests/patest_read_record.c b/src/audio/portaudio/pa_tests/patest_read_record.c
new file mode 100644
index 0000000000000000000000000000000000000000..5e9ca0109e12ce076a8daa7bb674b1894bcef1d1
--- /dev/null
+++ b/src/audio/portaudio/pa_tests/patest_read_record.c
@@ -0,0 +1,229 @@
+/** @file patest_read_record.c
+	@brief Record input into an array; Save array to a file; Playback recorded
+    data. Implemented using the blocking API (Pa_ReadStream(), Pa_WriteStream() )
+	@author Phil Burk  http://www.softsynth.com
+    @author Ross Bencina rossb@audiomulch.com
+*/
+/*
+ * $Id$
+ *
+ * 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 FRAMES_PER_BUFFER (1024)
+#define NUM_SECONDS     (5)
+#define NUM_CHANNELS    (2)
+/* #define DITHER_FLAG     (paDitherOff)  */
+#define DITHER_FLAG     (0) /**/
+
+/* Select sample format. */
+#if 1
+#define PA_SAMPLE_TYPE  paFloat32
+typedef float SAMPLE;
+#define SAMPLE_SILENCE  (0.0f)
+#define PRINTF_S_FORMAT "%.8f"
+#elif 1
+#define PA_SAMPLE_TYPE  paInt16
+typedef short SAMPLE;
+#define SAMPLE_SILENCE  (0)
+#define PRINTF_S_FORMAT "%d"
+#elif 0
+#define PA_SAMPLE_TYPE  paInt8
+typedef char SAMPLE;
+#define SAMPLE_SILENCE  (0)
+#define PRINTF_S_FORMAT "%d"
+#else
+#define PA_SAMPLE_TYPE  paUInt8
+typedef unsigned char SAMPLE;
+#define SAMPLE_SILENCE  (128)
+#define PRINTF_S_FORMAT "%d"
+#endif
+
+
+/*******************************************************************/
+int main(void);
+int main(void)
+{
+    PaStreamParameters inputParameters, outputParameters;
+    PaStream *stream;
+    PaError err;
+    SAMPLE *recordedSamples;
+    int i;
+    int totalFrames;
+    int numSamples;
+    int numBytes;
+    SAMPLE max, average, val;
+    
+    
+    printf("patest_read_record.c\n"); fflush(stdout);
+
+    totalFrames = NUM_SECONDS * SAMPLE_RATE; /* Record for a few seconds. */
+    numSamples = totalFrames * NUM_CHANNELS;
+
+    numBytes = numSamples * sizeof(SAMPLE);
+    recordedSamples = (SAMPLE *) malloc( numBytes );
+    if( recordedSamples == NULL )
+    {
+        printf("Could not allocate record array.\n");
+        exit(1);
+    }
+    for( i=0; i<numSamples; i++ ) recordedSamples[i] = 0;
+
+    err = Pa_Initialize();
+    if( err != paNoError ) goto error;
+
+    inputParameters.device = Pa_GetDefaultInputDevice(); /* default input device */
+    inputParameters.channelCount = NUM_CHANNELS;
+    inputParameters.sampleFormat = PA_SAMPLE_TYPE;
+    inputParameters.suggestedLatency = Pa_GetDeviceInfo( inputParameters.device )->defaultLowInputLatency;
+    inputParameters.hostApiSpecificStreamInfo = NULL;
+
+    /* Record some audio. -------------------------------------------- */
+    err = Pa_OpenStream(
+              &stream,
+              &inputParameters,
+              NULL,                  /* &outputParameters, */
+              SAMPLE_RATE,
+              FRAMES_PER_BUFFER,
+              paClipOff,      /* we won't output out of range samples so don't bother clipping them */
+              NULL, /* no callback, use blocking API */
+              NULL ); /* no callback, so no callback userData */
+    if( err != paNoError ) goto error;
+
+    err = Pa_StartStream( stream );
+    if( err != paNoError ) goto error;
+    printf("Now recording!!\n"); fflush(stdout);
+
+    err = Pa_ReadStream( stream, recordedSamples, totalFrames );
+    if( err != paNoError ) goto error;
+    
+    err = Pa_CloseStream( stream );
+    if( err != paNoError ) goto error;
+
+    /* Measure maximum peak amplitude. */
+    max = 0;
+    average = 0;
+    for( i=0; i<numSamples; i++ )
+    {
+        val = recordedSamples[i];
+        if( val < 0 ) val = -val; /* ABS */
+        if( val > max )
+        {
+            max = val;
+        }
+        average += val;
+    }
+
+    average = average / numSamples;
+
+    printf("Sample max amplitude = "PRINTF_S_FORMAT"\n", max );
+    printf("Sample average = "PRINTF_S_FORMAT"\n", average );
+/*  Was as below. Better choose at compile time because this
+    keeps generating compiler-warnings:
+    if( PA_SAMPLE_TYPE == paFloat32 )
+    {
+        printf("sample max amplitude = %f\n", max );
+        printf("sample average = %f\n", average );
+    }
+    else
+    {
+        printf("sample max amplitude = %d\n", max );
+        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( recordedSamples, NUM_CHANNELS * sizeof(SAMPLE), totalFrames, fid );
+            fclose( fid );
+            printf("Wrote data to 'recorded.raw'\n");
+        }
+    }
+#endif
+
+    /* Playback recorded data.  -------------------------------------------- */
+    
+    outputParameters.device = Pa_GetDefaultOutputDevice(); /* default output device */
+    outputParameters.channelCount = NUM_CHANNELS;
+    outputParameters.sampleFormat =  PA_SAMPLE_TYPE;
+    outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultLowOutputLatency;
+    outputParameters.hostApiSpecificStreamInfo = NULL;
+
+    printf("Begin playback.\n"); fflush(stdout);
+    err = Pa_OpenStream(
+              &stream,
+              NULL, /* no input */
+              &outputParameters,
+              SAMPLE_RATE,
+              FRAMES_PER_BUFFER,
+              paClipOff,      /* we won't output out of range samples so don't bother clipping them */
+              NULL, /* no callback, use blocking API */
+              NULL ); /* no callback, so no callback userData */
+    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);
+
+        err = Pa_WriteStream( stream, recordedSamples, totalFrames );
+        if( err != paNoError ) goto error;
+
+        err = Pa_CloseStream( stream );
+        if( err != paNoError ) goto error;
+        printf("Done.\n"); fflush(stdout);
+    }
+    free( 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/src/audio/portaudio/pa_tests/patest_record.c b/src/audio/portaudio/pa_tests/patest_record.c
new file mode 100644
index 0000000000000000000000000000000000000000..7ba35825dbbe3c7dacf4032a2c4a0acc10ee7ca5
--- /dev/null
+++ b/src/audio/portaudio/pa_tests/patest_record.c
@@ -0,0 +1,337 @@
+/** @file patest_record.c
+	@brief Record input into an array; Save array to a file; Playback recorded data.
+	@author Phil Burk  http://www.softsynth.com
+*/
+/*
+ * $Id$
+ *
+ * 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 FRAMES_PER_BUFFER (1024)
+#define NUM_SECONDS     (5)
+#define NUM_CHANNELS    (2)
+/* #define DITHER_FLAG     (paDitherOff) */
+#define DITHER_FLAG     (0) /**/
+
+/* Select sample format. */
+#if 1
+#define PA_SAMPLE_TYPE  paFloat32
+typedef float SAMPLE;
+#define SAMPLE_SILENCE  (0.0f)
+#define PRINTF_S_FORMAT "%.8f"
+#elif 1
+#define PA_SAMPLE_TYPE  paInt16
+typedef short SAMPLE;
+#define SAMPLE_SILENCE  (0)
+#define PRINTF_S_FORMAT "%d"
+#elif 0
+#define PA_SAMPLE_TYPE  paInt8
+typedef char SAMPLE;
+#define SAMPLE_SILENCE  (0)
+#define PRINTF_S_FORMAT "%d"
+#else
+#define PA_SAMPLE_TYPE  paUInt8
+typedef unsigned char SAMPLE;
+#define SAMPLE_SILENCE  (128)
+#define PRINTF_S_FORMAT "%d"
+#endif
+
+typedef struct
+{
+    int          frameIndex;  /* Index into sample array. */
+    int          maxFrameIndex;
+    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( const void *inputBuffer, void *outputBuffer,
+                           unsigned long framesPerBuffer,
+                           const PaStreamCallbackTimeInfo* timeInfo,
+                           PaStreamCallbackFlags statusFlags,
+                           void *userData )
+{
+    paTestData *data = (paTestData*)userData;
+    const SAMPLE *rptr = (const SAMPLE*)inputBuffer;
+    SAMPLE *wptr = &data->recordedSamples[data->frameIndex * NUM_CHANNELS];
+    long framesToCalc;
+    long i;
+    int finished;
+    unsigned long framesLeft = data->maxFrameIndex - data->frameIndex;
+
+    (void) outputBuffer; /* Prevent unused variable warnings. */
+    (void) timeInfo;
+    (void) statusFlags;
+    (void) userData;
+
+    if( framesLeft < framesPerBuffer )
+    {
+        framesToCalc = framesLeft;
+        finished = paComplete;
+    }
+    else
+    {
+        framesToCalc = framesPerBuffer;
+        finished = paContinue;
+    }
+
+    if( inputBuffer == NULL )
+    {
+        for( i=0; i<framesToCalc; i++ )
+        {
+            *wptr++ = SAMPLE_SILENCE;  /* left */
+            if( NUM_CHANNELS == 2 ) *wptr++ = SAMPLE_SILENCE;  /* right */
+        }
+    }
+    else
+    {
+        for( i=0; i<framesToCalc; i++ )
+        {
+            *wptr++ = *rptr++;  /* left */
+            if( NUM_CHANNELS == 2 ) *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( const void *inputBuffer, void *outputBuffer,
+                         unsigned long framesPerBuffer,
+                         const PaStreamCallbackTimeInfo* timeInfo,
+                         PaStreamCallbackFlags statusFlags,
+                         void *userData )
+{
+    paTestData *data = (paTestData*)userData;
+    SAMPLE *rptr = &data->recordedSamples[data->frameIndex * NUM_CHANNELS];
+    SAMPLE *wptr = (SAMPLE*)outputBuffer;
+    unsigned int i;
+    int finished;
+    unsigned int framesLeft = data->maxFrameIndex - data->frameIndex;
+
+    (void) inputBuffer; /* Prevent unused variable warnings. */
+    (void) timeInfo;
+    (void) statusFlags;
+    (void) userData;
+
+    if( framesLeft < framesPerBuffer )
+    {
+        /* final buffer... */
+        for( i=0; i<framesLeft; i++ )
+        {
+            *wptr++ = *rptr++;  /* left */
+            if( NUM_CHANNELS == 2 ) *wptr++ = *rptr++;  /* right */
+        }
+        for( ; i<framesPerBuffer; i++ )
+        {
+            *wptr++ = 0;  /* left */
+            if( NUM_CHANNELS == 2 ) *wptr++ = 0;  /* right */
+        }
+        data->frameIndex += framesLeft;
+        finished = paComplete;
+    }
+    else
+    {
+        for( i=0; i<framesPerBuffer; i++ )
+        {
+            *wptr++ = *rptr++;  /* left */
+            if( NUM_CHANNELS == 2 ) *wptr++ = *rptr++;  /* right */
+        }
+        data->frameIndex += framesPerBuffer;
+        finished = paContinue;
+    }
+    return finished;
+}
+
+/*******************************************************************/
+int main(void);
+int main(void)
+{
+    PaStreamParameters  inputParameters,
+                        outputParameters;
+    PaStream*           stream;
+    PaError             err = paNoError;
+    paTestData          data;
+    int                 i;
+    int                 totalFrames;
+    int                 numSamples;
+    int                 numBytes;
+    SAMPLE              max, val;
+    double              average;
+
+    printf("patest_record.c\n"); fflush(stdout);
+
+    data.maxFrameIndex = totalFrames = NUM_SECONDS * SAMPLE_RATE; /* Record for a few seconds. */
+    data.frameIndex = 0;
+    numSamples = totalFrames * NUM_CHANNELS;
+    numBytes = numSamples * sizeof(SAMPLE);
+    data.recordedSamples = (SAMPLE *) malloc( numBytes ); /* From now on, recordedSamples is initialised. */
+    if( data.recordedSamples == NULL )
+    {
+        printf("Could not allocate record array.\n");
+        goto done;
+    }
+    for( i=0; i<numSamples; i++ ) data.recordedSamples[i] = 0;
+
+    err = Pa_Initialize();
+    if( err != paNoError ) goto done;
+
+    inputParameters.device = Pa_GetDefaultInputDevice(); /* default input device */
+    inputParameters.channelCount = 2;                    /* stereo input */
+    inputParameters.sampleFormat = PA_SAMPLE_TYPE;
+    inputParameters.suggestedLatency = Pa_GetDeviceInfo( inputParameters.device )->defaultLowInputLatency;
+    inputParameters.hostApiSpecificStreamInfo = NULL;
+
+    /* Record some audio. -------------------------------------------- */
+    err = Pa_OpenStream(
+              &stream,
+              &inputParameters,
+              NULL,                  /* &outputParameters, */
+              SAMPLE_RATE,
+              FRAMES_PER_BUFFER,
+              paClipOff,      /* we won't output out of range samples so don't bother clipping them */
+              recordCallback,
+              &data );
+    if( err != paNoError ) goto done;
+
+    err = Pa_StartStream( stream );
+    if( err != paNoError ) goto done;
+    printf("Now recording!!\n"); fflush(stdout);
+
+    while( ( err = Pa_IsStreamActive( stream ) ) == 1 )
+    {
+        Pa_Sleep(1000);
+        printf("index = %d\n", data.frameIndex ); fflush(stdout);
+    }
+    if( err < 0 ) goto done;
+
+    err = Pa_CloseStream( stream );
+    if( err != paNoError ) goto done;
+
+    /* Measure maximum peak amplitude. */
+    max = 0;
+    average = 0.0;
+    for( i=0; i<numSamples; i++ )
+    {
+        val = data.recordedSamples[i];
+        if( val < 0 ) val = -val; /* ABS */
+        if( val > max )
+        {
+            max = val;
+        }
+        average += val;
+    }
+
+    average = average / (double)numSamples;
+
+    printf("sample max amplitude = "PRINTF_S_FORMAT"\n", max );
+    printf("sample average = %lf\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, NUM_CHANNELS * sizeof(SAMPLE), totalFrames, fid );
+            fclose( fid );
+            printf("Wrote data to 'recorded.raw'\n");
+        }
+    }
+#endif
+
+    /* Playback recorded data.  -------------------------------------------- */
+    data.frameIndex = 0;
+
+    outputParameters.device = Pa_GetDefaultOutputDevice(); /* default output device */
+    outputParameters.channelCount = 2;                     /* stereo output */
+    outputParameters.sampleFormat =  PA_SAMPLE_TYPE;
+    outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultLowOutputLatency;
+    outputParameters.hostApiSpecificStreamInfo = NULL;
+
+    printf("Begin playback.\n"); fflush(stdout);
+    err = Pa_OpenStream(
+              &stream,
+              NULL, /* no input */
+              &outputParameters,
+              SAMPLE_RATE,
+              FRAMES_PER_BUFFER,
+              paClipOff,      /* we won't output out of range samples so don't bother clipping them */
+              playCallback,
+              &data );
+    if( err != paNoError ) goto done;
+
+    if( stream )
+    {
+        err = Pa_StartStream( stream );
+        if( err != paNoError ) goto done;
+        
+        printf("Waiting for playback to finish.\n"); fflush(stdout);
+
+        while( ( err = Pa_IsStreamActive( stream ) ) == 1 ) Pa_Sleep(100);
+        if( err < 0 ) goto done;
+        
+        err = Pa_CloseStream( stream );
+        if( err != paNoError ) goto done;
+        
+        printf("Done.\n"); fflush(stdout);
+    }
+
+done:
+    Pa_Terminate();
+    if( data.recordedSamples )       /* Sure it is NULL or valid. */
+        free( data.recordedSamples );
+    if( err != paNoError )
+    {
+        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 ) );
+        err = 1;          /* Always return 0 or 1, but no other return codes. */
+    }
+    return err;
+}
+
diff --git a/src/audio/portaudio/pa_tests/patest_ringmix.c b/src/audio/portaudio/pa_tests/patest_ringmix.c
new file mode 100644
index 0000000000000000000000000000000000000000..1df0623881c961eff6fd87ec66044675bacfaa30
--- /dev/null
+++ b/src/audio/portaudio/pa_tests/patest_ringmix.c
@@ -0,0 +1,79 @@
+/** @file patest_ringmix.c
+	@brief Ring modulate inputs to left output, mix inputs to right output.
+*/
+/*
+ * $Id$ 
+ *
+ * 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 "portaudio.h"
+/* This will be called asynchronously by the PortAudio engine. */
+static int myCallback( const void *inputBuffer, void *outputBuffer,
+                            unsigned long framesPerBuffer,
+                            const PaStreamCallbackTimeInfo* timeInfo,
+                            PaStreamCallbackFlags statusFlags,
+                            void *userData )
+{
+    const float *in  = (const float *) inputBuffer;
+	float *out = (float *) outputBuffer;    
+    float leftInput, rightInput;
+    unsigned int i;
+
+    /* 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)
+{
+    PaStream *stream;
+    Pa_Initialize();
+    Pa_OpenDefaultStream(
+        &stream,
+        2, 2,               /* stereo input and output */
+        paFloat32,  44100.0,
+        64,                 /* 64 frames per buffer */
+        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/src/audio/portaudio/pa_tests/patest_saw.c b/src/audio/portaudio/pa_tests/patest_saw.c
new file mode 100644
index 0000000000000000000000000000000000000000..e76e3cb92403c9496309bc4e47e3236115aafced
--- /dev/null
+++ b/src/audio/portaudio/pa_tests/patest_saw.c
@@ -0,0 +1,125 @@
+/** @file patest_saw.c
+	@brief Play a simple (aliasing) sawtooth wave.
+	@author Phil Burk  http://www.softsynth.com
+*/
+/*
+ * $Id$
+ *
+ * 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( const void *inputBuffer, void *outputBuffer,
+                           unsigned long framesPerBuffer,
+                           const PaStreamCallbackTimeInfo* timeInfo,
+                           PaStreamCallbackFlags statusFlags,
+                           void *userData )
+{
+    /* Cast data passed through stream to our structure. */
+    paTestData *data = (paTestData*)userData;
+    float *out = (float*)outputBuffer;
+    unsigned int i;
+    (void) inputBuffer; /* Prevent unused variable warning. */
+
+    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)
+{
+    PaStream *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 */
+                                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/src/audio/portaudio/pa_tests/patest_sine.c b/src/audio/portaudio/pa_tests/patest_sine.c
new file mode 100644
index 0000000000000000000000000000000000000000..c952b5805684af2e2dc2bb2d6c3a940341a60805
--- /dev/null
+++ b/src/audio/portaudio/pa_tests/patest_sine.c
@@ -0,0 +1,151 @@
+/** @file patest_sine.c
+	@brief Play a sine wave for several seconds.
+	@author Ross Bencina <rossb@audiomulch.com>
+    @author Phil Burk <philburk@softsynth.com>
+*/
+/*
+ * $Id$
+ *
+ * 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   (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( const void *inputBuffer, void *outputBuffer,
+                            unsigned long framesPerBuffer,
+                            const PaStreamCallbackTimeInfo* timeInfo,
+                            PaStreamCallbackFlags statusFlags,
+                            void *userData )
+{
+    paTestData *data = (paTestData*)userData;
+    float *out = (float*)outputBuffer;
+    unsigned long i;
+
+    (void) timeInfo; /* Prevent unused variable warnings. */
+    (void) statusFlags;
+    (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 paContinue;
+}
+
+/*******************************************************************/
+int main(void);
+int main(void)
+{
+    PaStreamParameters outputParameters;
+    PaStream *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;
+
+    outputParameters.device = Pa_GetDefaultOutputDevice(); /* default output device */
+    outputParameters.channelCount = 2;       /* stereo output */
+    outputParameters.sampleFormat = paFloat32; /* 32 bit floating point output */
+    outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultLowOutputLatency;
+    outputParameters.hostApiSpecificStreamInfo = NULL;
+
+    err = Pa_OpenStream(
+              &stream,
+              NULL, /* no input */
+              &outputParameters,
+              SAMPLE_RATE,
+              FRAMES_PER_BUFFER,
+              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/src/audio/portaudio/pa_tests/patest_sine8.c b/src/audio/portaudio/pa_tests/patest_sine8.c
new file mode 100644
index 0000000000000000000000000000000000000000..5a9d1fe72776075b09c77d1f63671e0bb4b329bb
--- /dev/null
+++ b/src/audio/portaudio/pa_tests/patest_sine8.c
@@ -0,0 +1,212 @@
+/** @file patest_sine8.c
+	@brief Test 8 bit data: play a sine wave for several seconds.
+	@author Ross Bencina <rossb@audiomulch.com>
+*/
+/*
+ * $Id$
+ *
+ * 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 TABLE_SIZE    (200)
+#define TEST_UNSIGNED (0)
+
+#if TEST_UNSIGNED
+#define TEST_FORMAT   paUInt8
+#else
+#define TEST_FORMAT   paInt8
+#endif
+
+#ifndef M_PI
+#define M_PI (3.14159265)
+#endif
+
+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( const void *inputBuffer, void *outputBuffer,
+                           unsigned long framesPerBuffer,
+                           const PaStreamCallbackTimeInfo* timeInfo,
+                           PaStreamCallbackFlags statusFlags,
+                           void *userData )
+{
+    paTestData *data = (paTestData*)userData;
+    char *out = (char*)outputBuffer;
+    int i;
+    int framesToCalc;
+    int finished = 0;
+    (void) inputBuffer; /* Prevent unused variable warnings. */
+
+    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)
+{
+    PaStreamParameters  outputParameters;
+    PaStream*           stream;
+    PaError             err;
+    paTestData          data;
+    PaTime              streamOpened;
+    int                 i, 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;
+
+    outputParameters.device = Pa_GetDefaultOutputDevice(); /* Default output device. */
+    outputParameters.channelCount = 2;                     /* Stereo output. */
+    outputParameters.sampleFormat = TEST_FORMAT;
+    outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultLowOutputLatency;
+    outputParameters.hostApiSpecificStreamInfo = NULL;
+    err = Pa_OpenStream( &stream,
+                         NULL,      /* No input. */
+                         &outputParameters,
+                         SAMPLE_RATE,
+                         256,       /* Frames per buffer. */
+                         paClipOff, /* We won't output out of range samples so don't bother clipping them. */
+                         patestCallback,
+                         &data );
+    if( err != paNoError )
+        goto error;
+
+    streamOpened = Pa_GetStreamTime( stream ); /* Time in seconds when stream was opened (approx). */
+
+    err = Pa_StartStream( stream );
+    if( err != paNoError )
+        goto error;
+
+    /* Watch until sound is halfway finished. */
+    /* (Was ( Pa_StreamTime( stream ) < (totalSamps/2) ) in V18. */
+    while( (Pa_GetStreamTime( stream ) - streamOpened) < (PaTime)NUM_SECONDS / 2.0 )
+        Pa_Sleep(10);
+
+    /* Stop sound until ENTER hit. (Hu? don't see any keyboard-input here.) */
+    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( ( err = Pa_IsStreamActive( stream ) ) == 1 )
+        Pa_Sleep(100);
+    if( err < 0 )
+        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/src/audio/portaudio/pa_tests/patest_sine_formats.c b/src/audio/portaudio/pa_tests/patest_sine_formats.c
new file mode 100644
index 0000000000000000000000000000000000000000..448a4ebe96cdfdd04e630df5389f13e49948f8f2
--- /dev/null
+++ b/src/audio/portaudio/pa_tests/patest_sine_formats.c
@@ -0,0 +1,196 @@
+/** @file patest_sine_formats.c
+	@brief Play a sine wave for several seconds. Test various data formats.
+	@author Phil Burk
+*/
+/*
+ * $Id$
+ *
+ * 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        (10)
+#define SAMPLE_RATE        (44100)
+#define FRAMES_PER_BUFFER  (512)
+#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"
+
+#elif 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"
+
+#elif TEST_INT16
+#define TEST_FORMAT         paInt16
+typedef short               SAMPLE_t;
+#define SAMPLE_ZERO         (0)
+#define DOUBLE_TO_SAMPLE(x) (SAMPLE_ZERO + (SAMPLE_t)(32767 * (x)))
+#define FORMAT_NAME         "Signed 16 Bit"
+
+#elif 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( const void *inputBuffer,
+                           void *outputBuffer,
+                           unsigned long framesPerBuffer,
+                           const PaStreamCallbackTimeInfo* timeInfo,
+                           PaStreamCallbackFlags statusFlags,
+                           void *userData )
+{
+    paTestData *data = (paTestData*)userData;
+    SAMPLE_t *out = (SAMPLE_t *)outputBuffer;
+    int i;
+    int framesToCalc;
+    int finished = 0;
+    (void) inputBuffer; /* Prevent unused variable warnings. */
+
+    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)
+{
+    PaStream *stream;
+    PaStreamParameters outputParameters;
+    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;
+
+
+    outputParameters.device           = Pa_GetDefaultOutputDevice(); /* Default output device. */
+    outputParameters.channelCount     = 2;                           /* Stereo output */
+    outputParameters.sampleFormat     = TEST_FORMAT;                 /* Selected above. */
+    outputParameters.suggestedLatency = Pa_GetDeviceInfo(outputParameters.device)->defaultLowOutputLatency;
+    outputParameters.hostApiSpecificStreamInfo = NULL;
+    err = Pa_OpenStream( &stream,
+                         NULL,                  /* No input. */
+                         &outputParameters,     /* As above. */
+                         SAMPLE_RATE,
+                         FRAMES_PER_BUFFER,
+                         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( ( err = Pa_IsStreamActive( stream ) ) == 1 ) Pa_Sleep(100);
+    if( err < 0 ) goto error;
+
+    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/src/audio/portaudio/pa_tests/patest_sine_time.c b/src/audio/portaudio/pa_tests/patest_sine_time.c
new file mode 100644
index 0000000000000000000000000000000000000000..cf45275fe1bd3c768fc15864a0a802e28201671e
--- /dev/null
+++ b/src/audio/portaudio/pa_tests/patest_sine_time.c
@@ -0,0 +1,208 @@
+/** @file patest_sine_time.c
+	@brief Play a sine wave for several seconds, pausing in the middle.
+	Uses the Pa_GetStreamTime() call.
+	@author Ross Bencina <rossb@audiomulch.com>
+	@author Phil Burk <philburk@softsynth.com>
+*/
+/*
+ * $Id$
+ *
+ * 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"
+#include "pa_util.h"
+
+#define NUM_SECONDS   (8)
+#define SAMPLE_RATE   (44100)
+#define FRAMES_PER_BUFFER  (64)
+
+#ifndef M_PI
+#define M_PI  (3.14159265)
+#endif
+#define TWOPI (M_PI * 2.0)
+
+#define TABLE_SIZE   (200)
+
+typedef struct
+{
+    double           left_phase;
+    double           right_phase;
+    volatile PaTime  outTime;
+}
+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( const void *inputBuffer, void *outputBuffer,
+                            unsigned long framesPerBuffer,
+                            const PaStreamCallbackTimeInfo* timeInfo,
+                            PaStreamCallbackFlags statusFlags,
+                            void *userData )
+{
+    paTestData *data = (paTestData*)userData;
+    float *out = (float*)outputBuffer;
+    unsigned int i;
+
+    double left_phaseInc = 0.02;
+    double right_phaseInc = 0.06;
+
+    double left_phase = data->left_phase;
+    double right_phase = data->right_phase;
+
+    (void) statusFlags; /* Prevent unused variable warnings. */
+    (void) inputBuffer;
+
+    data->outTime = timeInfo->outputBufferDacTime;
+
+    for( i=0; i<framesPerBuffer; i++ )
+    {
+        left_phase += left_phaseInc;
+        if( left_phase > TWOPI ) left_phase -= TWOPI;
+        *out++ = (float) sin( left_phase );
+
+        right_phase += right_phaseInc;
+        if( right_phase > TWOPI ) right_phase -= TWOPI;
+        *out++ = (float) sin( right_phase );
+    }
+
+    data->left_phase = left_phase;
+    data->right_phase = right_phase;
+
+    return paContinue;
+}
+
+/*******************************************************************/
+static void ReportStreamTime( PaStream *stream, paTestData *data );
+static void ReportStreamTime( PaStream *stream, paTestData *data )
+{
+    PaTime  streamTime, latency, outTime;
+    
+    streamTime = Pa_GetStreamTime( stream );
+    outTime = data->outTime;
+    if( outTime < 0.0 )
+    {
+        printf("Stream time = %8.1f\n", streamTime );
+    }
+    else
+    {
+        latency = outTime - streamTime;
+        printf("Stream time = %8.4f, outTime = %8.4f, latency = %8.4f\n",
+            streamTime, outTime, latency );
+    }
+    fflush(stdout);
+}
+
+/*******************************************************************/
+int main(void);
+int main(void)
+{
+    PaStreamParameters outputParameters;
+    PaStream *stream;
+    PaError err;
+    paTestData data;
+    PaTime startTime;
+
+    printf("PortAudio Test: output sine wave. SR = %d, BufSize = %d\n", SAMPLE_RATE, FRAMES_PER_BUFFER);
+
+    data.left_phase = data.right_phase = 0;
+
+    err = Pa_Initialize();
+    if( err != paNoError ) goto error;
+
+    outputParameters.device = Pa_GetDefaultOutputDevice(); /* default output device */
+    outputParameters.channelCount = 2;       /* stereo output */
+    outputParameters.sampleFormat = paFloat32; /* 32 bit floating point output */
+    outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultLowOutputLatency;
+    outputParameters.hostApiSpecificStreamInfo = NULL;
+
+    err = Pa_OpenStream(
+              &stream,
+              NULL, /* no input */
+              &outputParameters,
+              SAMPLE_RATE,
+              FRAMES_PER_BUFFER,
+              paClipOff,      /* we won't output out of range samples so don't bother clipping them */
+              patestCallback,
+              &data );
+    if( err != paNoError ) goto error;
+          
+    /* Watch until sound is halfway finished. */
+    printf("Play for %d seconds.\n", NUM_SECONDS/2 ); fflush(stdout);
+
+    data.outTime = -1.0; /* mark time for callback as undefined */
+    err = Pa_StartStream( stream );
+    if( err != paNoError ) goto error;
+
+    startTime = Pa_GetStreamTime( stream );
+
+    do
+    {
+        ReportStreamTime( stream, &data );
+        Pa_Sleep(100);
+    } while( (Pa_GetStreamTime( stream ) - startTime) < (NUM_SECONDS/2) );
+    
+    /* Stop sound for 2 seconds. */
+    err = Pa_StopStream( stream );
+    if( err != paNoError ) goto error;
+    
+    printf("Pause for 2 seconds.\n"); fflush(stdout);
+    Pa_Sleep( 2000 );
+
+    data.outTime = -1.0; /* mark time for callback as undefined */
+    err = Pa_StartStream( stream );
+    if( err != paNoError ) goto error;
+
+    startTime = Pa_GetStreamTime( stream );
+    
+    printf("Play until sound is finished.\n"); fflush(stdout);
+    do
+    {
+        ReportStreamTime( stream, &data );
+        Pa_Sleep(100);
+    } while( (Pa_GetStreamTime( stream ) - startTime) < (NUM_SECONDS/2) );
+    
+    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/src/audio/portaudio/pa_tests/patest_start_stop.c b/src/audio/portaudio/pa_tests/patest_start_stop.c
new file mode 100644
index 0000000000000000000000000000000000000000..03ade17210bb754acf3bce644b4a355773a1f451
--- /dev/null
+++ b/src/audio/portaudio/pa_tests/patest_start_stop.c
@@ -0,0 +1,162 @@
+/** @file patest_start_stop.c
+	@brief Play a sine wave for several seconds
+        - start and stop the stream multiple times.
+        
+	@author Ross Bencina <rossb@audiomulch.com>
+	@author Phil Burk <philburk@softsynth.com>
+*/
+/*
+ * $Id$
+ *
+ * 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_GetDefaultOutputDevice()   /* default output device */
+
+#define NUM_SECONDS   (3)
+#define NUM_LOOPS     (4)
+#define SAMPLE_RATE   (44100)
+#define FRAMES_PER_BUFFER  (400)
+
+#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( const void *inputBuffer, void *outputBuffer,
+                            unsigned long framesPerBuffer,
+                            const PaStreamCallbackTimeInfo* timeInfo,
+                            PaStreamCallbackFlags statusFlags,
+                            void *userData )
+{
+    paTestData *data = (paTestData*)userData;
+    float *out = (float*)outputBuffer;
+    unsigned long i;
+
+    (void) timeInfo; /* Prevent unused variable warnings. */
+    (void) statusFlags;
+    (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 paContinue;
+}
+
+/*******************************************************************/
+int main(void);
+int main(void)
+{
+    PaStreamParameters outputParameters;
+    PaStream *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;
+
+    outputParameters.device = OUTPUT_DEVICE;
+    outputParameters.channelCount = 2;       /* stereo output */
+    outputParameters.sampleFormat = paFloat32; /* 32 bit floating point output */
+    outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultLowOutputLatency;
+    outputParameters.hostApiSpecificStreamInfo = NULL;
+
+    err = Pa_OpenStream(
+              &stream,
+              NULL, /* no input */
+              &outputParameters,
+              SAMPLE_RATE,
+              FRAMES_PER_BUFFER,
+              paClipOff,      /* we won't output out of range samples so don't bother clipping them */
+              patestCallback,
+              &data );
+    if( err != paNoError ) goto error;
+
+    for( i=0; i<NUM_LOOPS; i++ )
+    {
+        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;
+
+        printf("Stopped.\n" );
+        Pa_Sleep( 1000 );
+    }
+
+    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/src/audio/portaudio/pa_tests/patest_stop.c b/src/audio/portaudio/pa_tests/patest_stop.c
new file mode 100644
index 0000000000000000000000000000000000000000..deb850e9cbfd405bc8e632f5b101e8805f1669cc
--- /dev/null
+++ b/src/audio/portaudio/pa_tests/patest_stop.c
@@ -0,0 +1,313 @@
+/** @file patest_stop.c
+	@brief Test different ways of stopping audio.
+
+	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>
+*/
+/*
+ * $Id$
+ *
+ * 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_GetDefaultOutputDevice())
+#define SLEEP_DUR           (200)
+#define SAMPLE_RATE         (44100)
+#define FRAMES_PER_BUFFER   (256)
+#define LATENCY_SECONDS     (3.f)
+#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;
+    PaTime 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( const void *inputBuffer, void *outputBuffer,
+                            unsigned long framesPerBuffer,
+                            const PaStreamCallbackTimeInfo* timeInfo,
+                            PaStreamCallbackFlags statusFlags,
+                            void *userData )
+{
+    paTestData *data = (paTestData*)userData;
+    float *out = (float*)outputBuffer;
+    float value;
+    unsigned int i = 0;
+    int finished = paContinue;
+
+    (void) inputBuffer;     /* Prevent unused variable warnings. */
+    (void) timeInfo;
+    (void) statusFlags;
+
+
+    /* 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 = paComplete;
+                            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 %f seconds latency\n", LATENCY_SECONDS );
+    /* 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 %f seconds.\n", LATENCY_SECONDS);
+    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 )
+{
+    PaStreamParameters outputParameters;
+    PaStream *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;
+
+    outputParameters.device = OUTPUT_DEVICE;
+    outputParameters.channelCount = 2;          /* stereo output */
+    outputParameters.sampleFormat = paFloat32;  /* 32 bit floating point output */
+    outputParameters.suggestedLatency = LATENCY_SECONDS;
+    outputParameters.hostApiSpecificStreamInfo = NULL;
+    
+    err = Pa_OpenStream(
+              &stream,
+              NULL, /* no input */
+              &outputParameters,
+              SAMPLE_RATE,
+              FRAMES_PER_BUFFER,            /* frames per buffer */
+              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( ( err = Pa_IsStreamActive( stream ) ) == 1 )
+        {
+            /*printf("outTime = %g, note# = %d, repeat# = %d\n", data->outTime,
+             data->noteCounter, data->repeatCounter  );
+            fflush(stdout); */
+            Pa_Sleep( SLEEP_DUR );
+        }
+        if( err < 0 ) goto error;
+    }
+    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/src/audio/portaudio/pa_tests/patest_stop_playout.c b/src/audio/portaudio/pa_tests/patest_stop_playout.c
new file mode 100644
index 0000000000000000000000000000000000000000..41134d581669986dd6dfd96462ff159c4e440ff7
--- /dev/null
+++ b/src/audio/portaudio/pa_tests/patest_stop_playout.c
@@ -0,0 +1,432 @@
+/** @file patest_stop_playout.c
+	@brief Test whether all queued samples are played when Pa_StopStream()
+            is used with a callback or read/write stream, or when the callback
+            returns paComplete.
+	@author Ross Bencina <rossb@audiomulch.com>
+*/
+/*
+ * $Id$
+ *
+ * This program uses the PortAudio Portable Audio Library.
+ * For more information see: http://www.portaudio.com/
+ * Copyright (c) 1999-2004 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)
+#define FRAMES_PER_BUFFER   (1024)
+
+#define TONE_SECONDS        (1)      /* long tone */
+#define TONE_FADE_SECONDS   (.04)    /* fades at start and end of long tone */
+#define GAP_SECONDS         (.25)     /* gap between long tone and blip */
+#define BLIP_SECONDS        (.035)   /* short blip */
+
+#define NUM_REPEATS         (3)
+
+#ifndef M_PI
+#define M_PI (3.14159265)
+#endif
+
+#define TABLE_SIZE          (2048)
+typedef struct
+{
+    float sine[TABLE_SIZE+1];
+
+    int repeatCount;
+    
+    double phase;
+    double lowIncrement, highIncrement;
+    
+    int gap1Length, toneLength, toneFadesLength, gap2Length, blipLength;
+    int gap1Countdown, toneCountdown, gap2Countdown, blipCountdown;
+}
+TestData;
+
+
+static void RetriggerTestSignalGenerator( TestData *data )
+{
+    data->phase = 0.;
+    data->gap1Countdown = data->gap1Length;
+    data->toneCountdown = data->toneLength;
+    data->gap2Countdown = data->gap2Length;
+    data->blipCountdown = data->blipLength;
+}
+
+
+static void ResetTestSignalGenerator( TestData *data )
+{
+    data->repeatCount = 0;
+    RetriggerTestSignalGenerator( data );
+}
+
+
+static void InitTestSignalGenerator( TestData *data )
+{
+    int signalLengthModBufferLength, 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]; /* guard point for linear interpolation */
+
+
+    
+    data->lowIncrement = (330. / SAMPLE_RATE) * TABLE_SIZE;
+    data->highIncrement = (1760. / SAMPLE_RATE) * TABLE_SIZE;
+
+    data->gap1Length = GAP_SECONDS * SAMPLE_RATE;
+    data->toneLength = TONE_SECONDS * SAMPLE_RATE;
+    data->toneFadesLength = TONE_FADE_SECONDS * SAMPLE_RATE;
+    data->gap2Length = GAP_SECONDS * SAMPLE_RATE;
+    data->blipLength = BLIP_SECONDS * SAMPLE_RATE;
+
+    /* adjust signal length to be a multiple of the buffer length */
+    signalLengthModBufferLength = (data->gap1Length + data->toneLength + data->gap2Length + data->blipLength) % FRAMES_PER_BUFFER;
+    if( signalLengthModBufferLength > 0 )
+        data->toneLength += signalLengthModBufferLength;
+
+    ResetTestSignalGenerator( data );
+}
+
+
+#define MIN( a, b ) (((a)<(b))?(a):(b))
+
+static void GenerateTestSignal( TestData *data, float *stereo, int frameCount )
+{
+    int framesGenerated = 0;
+    float output;
+    long index;
+    float fraction;
+    int count, i;
+
+    while( framesGenerated < frameCount && data->repeatCount < NUM_REPEATS )
+    {
+        if( framesGenerated < frameCount && data->gap1Countdown > 0 ){
+            count = MIN( frameCount - framesGenerated, data->gap1Countdown );
+            for( i=0; i < count; ++i )
+            {
+                *stereo++ = 0.f;
+                *stereo++ = 0.f;
+            }
+
+            data->gap1Countdown -= count;
+            framesGenerated += count;
+        }
+    
+        if( framesGenerated < frameCount && data->toneCountdown > 0 ){
+            count = MIN( frameCount - framesGenerated, data->toneCountdown );
+            for( i=0; i < count; ++i )
+            {
+                /* tone with data->lowIncrement phase increment */
+                index = (long)data->phase;
+                fraction = data->phase - index;
+                output = data->sine[ index ] + (data->sine[ index + 1 ] - data->sine[ index ]) * fraction;
+
+                data->phase += data->lowIncrement;
+                while( data->phase >= TABLE_SIZE )
+                    data->phase -= TABLE_SIZE;
+
+                /* apply fade to ends */
+
+                if( data->toneCountdown < data->toneFadesLength )
+                {
+                    /* cosine-bell fade out at end */
+                    output *= (-cos(((float)data->toneCountdown / (float)data->toneFadesLength) * M_PI) + 1.) * .5;
+                }
+                else if( data->toneCountdown > data->toneLength - data->toneFadesLength ) 
+                {
+                    /* cosine-bell fade in at start */
+                    output *= (cos(((float)(data->toneCountdown - (data->toneLength - data->toneFadesLength)) / (float)data->toneFadesLength) * M_PI) + 1.) * .5;
+                }
+
+                output *= .5; /* play tone half as loud as blip */
+            
+                *stereo++ = output;
+                *stereo++ = output;
+
+                data->toneCountdown--;
+            }                         
+
+            framesGenerated += count;
+        }
+
+        if( framesGenerated < frameCount && data->gap2Countdown > 0 ){
+            count = MIN( frameCount - framesGenerated, data->gap2Countdown );
+            for( i=0; i < count; ++i )
+            {
+                *stereo++ = 0.f;
+                *stereo++ = 0.f;
+            }
+
+            data->gap2Countdown -= count;
+            framesGenerated += count;
+        }
+
+        if( framesGenerated < frameCount && data->blipCountdown > 0 ){
+            count = MIN( frameCount - framesGenerated, data->blipCountdown );
+            for( i=0; i < count; ++i )
+            {
+                /* tone with data->highIncrement phase increment */
+                index = (long)data->phase;
+                fraction = data->phase - index;
+                output = data->sine[ index ] + (data->sine[ index + 1 ] - data->sine[ index ]) * fraction;
+
+                data->phase += data->highIncrement;
+                while( data->phase >= TABLE_SIZE )
+                    data->phase -= TABLE_SIZE;
+
+                /* cosine-bell envelope over whole blip */
+                output *= (-cos( ((float)data->blipCountdown / (float)data->blipLength) * 2. * M_PI) + 1.) * .5;
+                
+                *stereo++ = output;
+                *stereo++ = output;
+
+                data->blipCountdown--;
+            }
+
+            framesGenerated += count;
+        }
+
+
+        if( data->blipCountdown == 0 )
+        {
+            RetriggerTestSignalGenerator( data );
+            data->repeatCount++;
+        }        
+    }
+
+    if( framesGenerated < frameCount )
+    {
+        count = frameCount - framesGenerated;
+        for( i=0; i < count; ++i )
+        {
+            *stereo++ = 0.f;
+            *stereo++ = 0.f;
+        }
+    }
+}
+
+
+static int IsTestSignalFinished( TestData *data )
+{
+    if( data->repeatCount >= NUM_REPEATS )
+        return 1;
+    else
+        return 0;
+}
+
+
+static int TestCallback1( const void *inputBuffer, void *outputBuffer,
+                            unsigned long frameCount,
+                            const PaStreamCallbackTimeInfo* timeInfo,
+                            PaStreamCallbackFlags statusFlags,
+                            void *userData )
+{
+    (void) inputBuffer; /* Prevent unused variable warnings. */
+    (void) timeInfo;
+    (void) statusFlags;
+
+    GenerateTestSignal( (TestData*)userData, (float*)outputBuffer, frameCount );
+
+    if( IsTestSignalFinished( (TestData*)userData ) )
+        return paComplete;
+    else
+        return paContinue;
+}
+
+
+volatile int testCallback2Finished = 0;
+
+static int TestCallback2( const void *inputBuffer, void *outputBuffer,
+                            unsigned long frameCount,
+                            const PaStreamCallbackTimeInfo* timeInfo,
+                            PaStreamCallbackFlags statusFlags,
+                            void *userData )
+{
+    (void) inputBuffer; /* Prevent unused variable warnings. */
+    (void) timeInfo;
+    (void) statusFlags;
+
+    GenerateTestSignal( (TestData*)userData, (float*)outputBuffer, frameCount );
+
+    if( IsTestSignalFinished( (TestData*)userData ) )
+        testCallback2Finished = 1;
+   
+    return paContinue;
+}
+
+/*******************************************************************/
+int main(void);
+int main(void)
+{
+    PaStreamParameters outputParameters;
+    PaStream *stream;
+    PaError err;
+    TestData data;
+    float writeBuffer[ FRAMES_PER_BUFFER * 2 ];
+    
+    printf("PortAudio Test: check that stopping stream plays out all queued samples. SR = %d, BufSize = %d\n", SAMPLE_RATE, FRAMES_PER_BUFFER);
+
+    InitTestSignalGenerator( &data );
+    
+    err = Pa_Initialize();
+    if( err != paNoError ) goto error;
+
+    outputParameters.device = Pa_GetDefaultOutputDevice(); /* default output device */
+    outputParameters.channelCount = 2;       /* stereo output */
+    outputParameters.sampleFormat = paFloat32; /* 32 bit floating point output */
+    outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultHighOutputLatency;
+    outputParameters.hostApiSpecificStreamInfo = NULL;
+
+/* test paComplete ---------------------------------------------------------- */
+
+    ResetTestSignalGenerator( &data );
+
+    err = Pa_OpenStream(
+              &stream,
+              NULL, /* no input */
+              &outputParameters,
+              SAMPLE_RATE,
+              FRAMES_PER_BUFFER,
+              paClipOff,      /* we won't output out of range samples so don't bother clipping them */
+              TestCallback1,
+              &data );
+    if( err != paNoError ) goto error;
+
+    err = Pa_StartStream( stream );
+    if( err != paNoError ) goto error;
+
+
+    printf("\nPlaying 'tone-blip' %d times using callback, stops by returning paComplete from callback.\n", NUM_REPEATS );
+    printf("If final blip is not intact, callback+paComplete implementation may be faulty.\n\n" );
+
+    while( (err = Pa_IsStreamActive( stream )) == 1 )
+        Pa_Sleep( 5 );
+
+    if( err != 0 ) goto error;
+
+    
+    err = Pa_StopStream( stream );
+    if( err != paNoError ) goto error;
+
+    err = Pa_CloseStream( stream );
+    if( err != paNoError ) goto error;
+
+    Pa_Sleep( 500 );
+
+/* test Pa_StopStream() with callback --------------------------------------- */
+
+    ResetTestSignalGenerator( &data );
+
+    testCallback2Finished = 0;
+    
+    err = Pa_OpenStream(
+              &stream,
+              NULL, /* no input */
+              &outputParameters,
+              SAMPLE_RATE,
+              FRAMES_PER_BUFFER,
+              paClipOff,      /* we won't output out of range samples so don't bother clipping them */
+              TestCallback2,
+              &data );
+    if( err != paNoError ) goto error;
+
+    err = Pa_StartStream( stream );
+    if( err != paNoError ) goto error;
+
+
+    printf("\nPlaying 'tone-blip' %d times using callback, stops by calling Pa_StopStream.\n", NUM_REPEATS );
+    printf("If final blip is not intact, callback+Pa_StopStream implementation may be faulty.\n\n" );
+
+    /* note that polling a volatile flag is not a good way to synchronise with
+        the callback, but it's the best we can do portably. */
+    while( !testCallback2Finished )
+        Pa_Sleep( 2 );
+
+    err = Pa_StopStream( stream );
+    if( err != paNoError ) goto error;
+    
+
+    err = Pa_CloseStream( stream );
+    if( err != paNoError ) goto error;
+
+    Pa_Sleep( 500 );
+
+/* test Pa_StopStream() with Pa_WriteStream --------------------------------- */
+
+    ResetTestSignalGenerator( &data );
+
+    err = Pa_OpenStream(
+              &stream,
+              NULL, /* no input */
+              &outputParameters,
+              SAMPLE_RATE,
+              FRAMES_PER_BUFFER,
+              paClipOff,      /* we won't output out of range samples so don't bother clipping them */
+              NULL, /* no callback, use blocking API */
+              NULL ); /* no callback, so no callback userData */
+    if( err != paNoError ) goto error;
+
+    err = Pa_StartStream( stream );
+    if( err != paNoError ) goto error;
+
+
+    printf("\nPlaying 'tone-blip' %d times using Pa_WriteStream, stops by calling Pa_StopStream.\n", NUM_REPEATS );
+    printf("If final blip is not intact, Pa_WriteStream+Pa_StopStream implementation may be faulty.\n\n" );
+
+    do{
+        GenerateTestSignal( &data, writeBuffer, FRAMES_PER_BUFFER );
+        err = Pa_WriteStream( stream, writeBuffer, FRAMES_PER_BUFFER );
+        if( err != paNoError ) goto error;
+        
+    }while( !IsTestSignalFinished( &data ) );
+
+    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/src/audio/portaudio/pa_tests/patest_sync.c b/src/audio/portaudio/pa_tests/patest_sync.c
new file mode 100644
index 0000000000000000000000000000000000000000..01e471420cff3a24371bec54645acc6f352e7c0f
--- /dev/null
+++ b/src/audio/portaudio/pa_tests/patest_sync.c
@@ -0,0 +1,259 @@
+/** @file patest_sync.c
+	@brief 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 PaUtil_GetTime().
+		-# Foreground outputs video (printf) in sync with audio.
+		-# Repeat.
+	
+	@author Phil Burk  http://www.softsynth.com
+*/
+/*
+ * $Id$
+ *
+ * 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_util.h"
+#define NUM_BEEPS           (6)
+#define SAMPLE_RATE         (44100)
+#define SAMPLE_PERIOD       (1.0/44100.0)
+#define FRAMES_PER_BUFFER   (256)
+#define BEEP_DURATION       (400)
+#define LATENCY_MSEC        (2000)
+#define SLEEP_MSEC          (10)
+#define TIMEOUT_MSEC        (15000)
+
+#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. */
+    PaTime       beepTime;
+    int          beepCount;
+    double       latency;    /* For debugging. */
+}
+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 = 99887766;  /* 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( const void *inputBuffer, void *outputBuffer,
+                           unsigned long framesPerBuffer,
+			   const PaStreamCallbackTimeInfo *timeInfo,
+			   PaStreamCallbackFlags statusFlags, void *userData )
+{
+    /* Cast data passed through stream to our structure. */
+    paTestData *data = (paTestData*)userData;
+    float *out = (float*)outputBuffer;
+    unsigned int i;
+    (void) inputBuffer;
+
+    data->latency = timeInfo->outputBufferDacTime - timeInfo->currentTime;
+
+    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 = timeInfo->outputBufferDacTime + (( (double)(random + SAMPLE_RATE)) * SAMPLE_PERIOD );
+                data->state = STATE_BKG_PENDING;
+            }
+            *out++ = 0.0;  /* left */
+            *out++ = 0.0;  /* right */
+            break;
+
+        case STATE_BKG_PENDING:
+            if( (timeInfo->outputBufferDacTime + (i*SAMPLE_PERIOD)) >= data->beepTime )
+            {
+                data->state = STATE_BKG_BEEPING;
+                data->beepCount = BEEP_DURATION;
+                data->left_phase = data->right_phase = 0.0;
+            }
+            *out++ = 0.0;  /* left */
+            *out++ = 0.0;  /* right */
+            break;
+
+        case STATE_BKG_BEEPING:
+            if( data->beepCount <= 0 )
+            {
+                data->state = STATE_BKG_IDLE;
+                data->requestBeep = 0;
+                *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)
+{
+    PaStream *stream;
+    PaError    err;
+    paTestData DATA;
+    int        i, timeout;
+    PaTime     previousTime;
+    PaStreamParameters outputParameters;
+    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;
+    
+    outputParameters.device = Pa_GetDefaultOutputDevice();
+    outputParameters.channelCount = 2;
+    outputParameters.hostApiSpecificStreamInfo = NULL;
+    outputParameters.sampleFormat = paFloat32;
+    outputParameters.suggestedLatency = (double)LATENCY_MSEC / 1000;
+
+    /* Open an audio I/O stream. */
+     err = Pa_OpenStream(
+              &stream,
+	      NULL,                         /* no input */
+	      &outputParameters,
+              SAMPLE_RATE,
+              FRAMES_PER_BUFFER,            /* frames per buffer */
+              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("started\n");
+    fflush(stdout);
+
+    previousTime = Pa_GetStreamTime( 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;
+        }
+        printf("calc beep for %9.3f, latency = %6.3f\n", DATA.beepTime, DATA.latency );
+        fflush(stdout);
+
+        /* Wait for scheduled beep time. */
+        timeout =  TIMEOUT_MSEC + (10000/SLEEP_MSEC);
+        while( (Pa_GetStreamTime( stream ) < DATA.beepTime) && (timeout-- > 0 ) )
+        {
+            Pa_Sleep(SLEEP_MSEC);
+        }
+        if( timeout <= 0 )
+        {
+            fprintf( stderr, "Timed out waiting for time. Now = %9.3f, Beep for %9.3f.\n",
+                     PaUtil_GetTime(), DATA.beepTime );
+            goto error;
+        }
+
+        /* Beep should be sounding now so print synchronized BEEP. */
+        printf("hear \"BEEP\" at %9.3f, delta = %9.3f\n",
+               Pa_GetStreamTime( stream ), (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/src/audio/portaudio/pa_tests/patest_toomanysines.c b/src/audio/portaudio/pa_tests/patest_toomanysines.c
new file mode 100644
index 0000000000000000000000000000000000000000..b7b22667c69f8cef3cc3f8e011c32103f97b7e9f
--- /dev/null
+++ b/src/audio/portaudio/pa_tests/patest_toomanysines.c
@@ -0,0 +1,173 @@
+/** @file patest_toomanysines.c
+	@brief Play more sine waves than we can handle in real time as a stress test.
+	@author Ross Bencina <rossb@audiomulch.com>
+	@author Phil Burk <philburk@softsynth.com>
+*/
+/*
+ * $Id$
+ *
+ * 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 MAX_LOAD      (1.2)
+#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( const void *inputBuffer, void *outputBuffer,
+                           unsigned long framesPerBuffer,
+                           const PaStreamCallbackTimeInfo* timeInfo,
+                           PaStreamCallbackFlags statusFlags,
+                           void *userData )
+{
+    paTestData *data = (paTestData*)userData;
+    float *out = (float*)outputBuffer;
+    unsigned long i;
+    int j;
+    int finished = 0;
+    (void) inputBuffer; /* Prevent unused variable warning. */
+
+    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)
+{
+    PaStreamParameters outputParameters;
+    PaStream *stream;
+    PaError err;
+    int numStress;
+    paTestData data = {0};
+    double load;
+
+    printf("PortAudio Test: output sine wave. SR = %d, BufSize = %d. MAX_LOAD = %f\n",
+        SAMPLE_RATE, FRAMES_PER_BUFFER, MAX_LOAD );
+
+    err = Pa_Initialize();
+    if( err != paNoError ) goto error;
+    
+    outputParameters.device = Pa_GetDefaultOutputDevice();  /* default output device */
+    outputParameters.channelCount = 1;                      /* mono output */
+    outputParameters.sampleFormat = paFloat32;              /* 32 bit floating point output */
+    outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultLowOutputLatency;
+    outputParameters.hostApiSpecificStreamInfo = NULL;
+
+    err = Pa_OpenStream(
+              &stream,
+              NULL,         /* no input */
+              &outputParameters,
+              SAMPLE_RATE,
+              FRAMES_PER_BUFFER,
+              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;
+
+    /* Determine number of sines required to get to 50% */
+    do
+    {
+        data.numSines++;
+        Pa_Sleep( 100 );
+
+        load = Pa_GetStreamCpuLoad( stream );
+        printf("numSines = %d, CPU load = %f\n", data.numSines, load );
+    }
+    while( load < 0.5 );
+    
+    /* Calculate target stress value then ramp up to that level*/
+    numStress = (int) (2.0 * data.numSines * MAX_LOAD );
+    for( ; data.numSines < numStress; data.numSines++ )
+    {
+        Pa_Sleep( 200 );
+        load = Pa_GetStreamCpuLoad( stream );
+        printf("STRESSING: numSines = %d, CPU load = %f\n", data.numSines, load );
+    }
+    
+    printf("Suffer for 5 seconds.\n");
+    Pa_Sleep( 5000 );
+    
+    printf("Stop stream.\n");
+    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/src/audio/portaudio/pa_tests/patest_two_rates.c b/src/audio/portaudio/pa_tests/patest_two_rates.c
new file mode 100644
index 0000000000000000000000000000000000000000..99532411ffbc618a5d40884f05f79e4cc8eed800
--- /dev/null
+++ b/src/audio/portaudio/pa_tests/patest_two_rates.c
@@ -0,0 +1,168 @@
+/*
+ * $Id$
+ * patest_two_rates.c
+ * Play two streams at different rates to make sure they don't interfere.
+ *
+ * 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 OUTPUT_DEVICE       (Pa_GetDefaultOutputDeviceID())
+#define SAMPLE_RATE_1       (44100)
+#define SAMPLE_RATE_2       (44100)
+#define FRAMES_PER_BUFFER   (256)
+#define FREQ_INCR           (0.1)
+
+#ifndef M_PI
+#define M_PI  (3.14159265)
+#endif
+
+typedef struct
+{
+    double phase;
+    double numFrames;
+} 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 frameIndex, channelIndex;
+    int finished = 0;
+    (void) outTime; /* Prevent unused variable warnings. */
+    (void) inputBuffer;
+
+    for( frameIndex=0; frameIndex<(int)framesPerBuffer; frameIndex++ )
+    {
+        /* Generate sine wave. */
+        float value = (float) 0.3 * sin(data->phase);
+        /* Stereo - two channels. */
+        *out++ = value;
+        *out++ = value;
+
+        data->phase += FREQ_INCR;
+        if( data->phase >= (2.0 * M_PI) ) data->phase -= (2.0 * M_PI);
+    }
+
+    return 0;
+}
+
+/*******************************************************************/
+int main(void);
+int main(void)
+{
+    PaError err;
+    PortAudioStream *stream1;
+    PortAudioStream *stream2;
+    paTestData data1 = {0};
+    paTestData data2 = {0};
+    printf("PortAudio Test: two rates.\n" );
+
+    err = Pa_Initialize();
+    if( err != paNoError ) goto error;
+    
+    /* Start first stream. **********************/
+    err = Pa_OpenStream(
+              &stream1,
+              paNoDevice, /* default input device */
+              0,              /* no input */
+              paFloat32,  /* 32 bit floating point input */
+              NULL,
+              OUTPUT_DEVICE,
+              2,          /* Stereo */
+              paFloat32,  /* 32 bit floating point output */
+              NULL,
+              SAMPLE_RATE_1,
+              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,
+              &data1 );
+    if( err != paNoError ) goto error;
+
+    err = Pa_StartStream( stream1 );
+    if( err != paNoError ) goto error;
+
+    Pa_Sleep( 3 * 1000 );
+
+    /* Start second stream. **********************/
+    err = Pa_OpenStream(
+              &stream2,
+              paNoDevice, /* default input device */
+              0,              /* no input */
+              paFloat32,  /* 32 bit floating point input */
+              NULL,
+              OUTPUT_DEVICE,
+              2,          /* Stereo */
+              paFloat32,  /* 32 bit floating point output */
+              NULL,
+              SAMPLE_RATE_2,
+              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,
+              &data2 );
+    if( err != paNoError ) goto error;
+
+    err = Pa_StartStream( stream2 );
+    if( err != paNoError ) goto error;
+
+    Pa_Sleep( 3 * 1000 );
+    
+    err = Pa_StopStream( stream2 );
+    if( err != paNoError ) goto error;
+
+    Pa_Sleep( 3 * 1000 );
+    
+    err = Pa_StopStream( stream1 );
+    if( err != paNoError ) goto error;
+
+    Pa_CloseStream( stream2 );
+    Pa_CloseStream( stream1 );
+    
+    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/src/audio/portaudio/pa_tests/patest_underflow.c b/src/audio/portaudio/pa_tests/patest_underflow.c
new file mode 100644
index 0000000000000000000000000000000000000000..7ff3de6a42d64efc3702d6d2cadc0e6f97f233d3
--- /dev/null
+++ b/src/audio/portaudio/pa_tests/patest_underflow.c
@@ -0,0 +1,151 @@
+/** @file patest_underflow.c
+	@brief Simulate an output buffer underflow condition.
+	Tests whether the stream can be stopped when underflowing buffers.
+	@author Ross Bencina <rossb@audiomulch.com>
+	@author Phil Burk <philburk@softsynth.com>
+*/
+/*
+ * $Id$
+ *
+ * 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( const void *inputBuffer, void *outputBuffer,
+                           unsigned long framesPerBuffer,
+                           const PaStreamCallbackTimeInfo* timeInfo,
+                           PaStreamCallbackFlags statusFlags,
+                           void *userData )
+{
+    paTestData *data = (paTestData*)userData;
+    float *out = (float*)outputBuffer;
+    unsigned long i;
+    int finished = 0;
+    (void) inputBuffer; /* Prevent unused variable warnings. */
+    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)
+{
+    PaStreamParameters outputParameters;
+    PaStream *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;
+    
+    outputParameters.device = Pa_GetDefaultOutputDevice(); /* default output device */
+    outputParameters.channelCount = 2;       /* stereo output */
+    outputParameters.sampleFormat = paFloat32; /* 32 bit floating point output */
+    outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultLowOutputLatency;
+    outputParameters.hostApiSpecificStreamInfo = NULL;
+    err = Pa_OpenStream(
+              &stream,
+              NULL, /* no input */
+              &outputParameters,
+              SAMPLE_RATE,
+              FRAMES_PER_BUFFER,
+              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/src/audio/portaudio/pa_tests/patest_wire.c b/src/audio/portaudio/pa_tests/patest_wire.c
new file mode 100644
index 0000000000000000000000000000000000000000..d83ce22d64d28a63f28c96d53061527f99b34a8c
--- /dev/null
+++ b/src/audio/portaudio/pa_tests/patest_wire.c
@@ -0,0 +1,284 @@
+/** @file patest_wire.c
+	@brief 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
+    
+ While adapting to V19-API, I excluded configs with framesPerCallback=0
+ because of an assert in file pa_common/pa_process.c. Pieter, Oct 9, 2003.
+
+*/
+/*
+ * $Id$
+ *
+ * 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)
+
+typedef struct WireConfig_s
+{
+    int isInputInterleaved;
+    int isOutputInterleaved;
+    int numInputChannels;
+    int numOutputChannels;
+    int framesPerCallback;
+} WireConfig_t;
+
+#define USE_FLOAT_INPUT        (1)
+#define USE_FLOAT_OUTPUT       (1)
+
+/* Latencies set to defaults. */
+
+#if USE_FLOAT_INPUT
+    #define INPUT_FORMAT  paFloat32
+    typedef float INPUT_SAMPLE;
+#else
+    #define INPUT_FORMAT  paInt16
+    typedef short INPUT_SAMPLE;
+#endif
+
+#if USE_FLOAT_OUTPUT
+    #define OUTPUT_FORMAT  paFloat32
+    typedef float OUTPUT_SAMPLE;
+#else
+    #define OUTPUT_FORMAT  paInt16
+    typedef short OUTPUT_SAMPLE;
+#endif
+
+double gInOutScaler = 1.0;
+#define CONVERT_IN_TO_OUT(in)  ((OUTPUT_SAMPLE) ((in) * gInOutScaler))
+
+#define INPUT_DEVICE           (Pa_GetDefaultInputDevice())
+#define OUTPUT_DEVICE          (Pa_GetDefaultOutputDevice())
+
+static PaError TestConfiguration( WireConfig_t *config );
+
+static int wireCallback( const void *inputBuffer, void *outputBuffer,
+                         unsigned long framesPerBuffer,
+                         const PaStreamCallbackTimeInfo* timeInfo,
+                         PaStreamCallbackFlags statusFlags,
+                         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( const void *inputBuffer, void *outputBuffer,
+                         unsigned long framesPerBuffer,
+                         const PaStreamCallbackTimeInfo* timeInfo,
+                         PaStreamCallbackFlags statusFlags,
+                         void *userData )
+{
+    INPUT_SAMPLE *in;
+    OUTPUT_SAMPLE *out;
+    int inStride;
+    int outStride;
+    int inDone = 0;
+    int outDone = 0;
+    WireConfig_t *config = (WireConfig_t *) userData;
+    unsigned int i;
+    int inChannel, outChannel;
+
+    /* This may get called with NULL inputBuffer during initial setup. */
+    if( inputBuffer == NULL) return 0;
+
+    inChannel=0, outChannel=0;
+    while( !(inDone && outDone) )
+    {
+        if( config->isInputInterleaved )
+        {
+            in = ((INPUT_SAMPLE*)inputBuffer) + inChannel;
+            inStride = config->numInputChannels;
+        }
+        else
+        {
+            in = ((INPUT_SAMPLE**)inputBuffer)[inChannel];
+            inStride = 1;
+        }
+
+        if( config->isOutputInterleaved )
+        {
+            out = ((OUTPUT_SAMPLE*)outputBuffer) + outChannel;
+            outStride = config->numOutputChannels;
+        }
+        else
+        {
+            out = ((OUTPUT_SAMPLE**)outputBuffer)[outChannel];
+            outStride = 1;
+        }
+
+        for( i=0; i<framesPerBuffer; i++ )
+        {
+            *out = CONVERT_IN_TO_OUT( *in );
+            out += outStride;
+            in += inStride;
+        }
+
+        if(inChannel < (config->numInputChannels - 1)) inChannel++;
+        else inDone = 1;
+        if(outChannel < (config->numOutputChannels - 1)) outChannel++;
+        else outDone = 1;
+    }
+    return 0;
+}
+
+/*******************************************************************/
+int main(void);
+int main(void)
+{
+    PaError err;
+    WireConfig_t CONFIG;
+    WireConfig_t *config = &CONFIG;
+    int configIndex = 0;;
+
+    err = Pa_Initialize();
+    if( err != paNoError ) goto error;
+
+    printf("Please connect audio signal to input and listen for it on output!\n");
+    printf("input format = %lu\n", INPUT_FORMAT );
+    printf("output format = %lu\n", OUTPUT_FORMAT );
+    printf("input device ID  = %d\n", INPUT_DEVICE );
+    printf("output device ID = %d\n", OUTPUT_DEVICE );
+
+    if( INPUT_FORMAT == OUTPUT_FORMAT )
+    {
+        gInOutScaler = 1.0;
+    }
+    else if( (INPUT_FORMAT == paInt16) && (OUTPUT_FORMAT == paFloat32) )
+    {
+        gInOutScaler = 1.0/32768.0;
+    }
+    else if( (INPUT_FORMAT == paFloat32) && (OUTPUT_FORMAT == paInt16) )
+    {
+        gInOutScaler = 32768.0;
+    }
+
+    for( config->isInputInterleaved = 0; config->isInputInterleaved < 2; config->isInputInterleaved++ )
+    {
+        for( config->isOutputInterleaved = 0; config->isOutputInterleaved < 2; config->isOutputInterleaved++ )
+        {
+            for( config->numInputChannels = 1; config->numInputChannels < 3; config->numInputChannels++ )
+            {
+                for( config->numOutputChannels = 1; config->numOutputChannels < 3; config->numOutputChannels++ )
+                {
+                           /* If framesPerCallback = 0, assertion fails in file pa_common/pa_process.c, line 1413: EX. */
+                    for( config->framesPerCallback = 64; config->framesPerCallback < 129; config->framesPerCallback += 64 )
+                    {
+                        printf("-----------------------------------------------\n" );
+                        printf("Configuration #%d\n", configIndex++ );
+                        err = TestConfiguration( config );
+                        /* Give user a chance to bail out. */
+                        if( err == 1 )
+                        {
+                            err = paNoError;
+                            goto done;
+                        }
+                        else if( err != paNoError ) goto error;
+                    }
+               }
+            }
+        }
+    }
+
+done:
+    Pa_Terminate();
+    printf("Full duplex sound test complete.\n"); fflush(stdout);
+    printf("Hit ENTER to quit.\n");  fflush(stdout);
+    getchar();
+    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 ) );
+    printf("Hit ENTER to quit.\n");  fflush(stdout);
+    getchar();
+    return -1;
+}
+
+static PaError TestConfiguration( WireConfig_t *config )
+{
+    int c;
+    PaError err;
+    PaStream *stream;
+    PaStreamParameters inputParameters, outputParameters;
+    
+    printf("input %sinterleaved!\n", (config->isInputInterleaved ? " " : "NOT ") );
+    printf("output %sinterleaved!\n", (config->isOutputInterleaved ? " " : "NOT ") );
+    printf("input channels = %d\n", config->numInputChannels );
+    printf("output channels = %d\n", config->numOutputChannels );
+    printf("framesPerCallback = %d\n", config->framesPerCallback );
+
+    inputParameters.device = INPUT_DEVICE;              /* default input device */
+    inputParameters.channelCount = config->numInputChannels;
+    inputParameters.sampleFormat = INPUT_FORMAT | (config->isInputInterleaved ? 0 : paNonInterleaved);
+    inputParameters.suggestedLatency = Pa_GetDeviceInfo( inputParameters.device )->defaultLowInputLatency;
+    inputParameters.hostApiSpecificStreamInfo = NULL;
+
+    outputParameters.device = OUTPUT_DEVICE;            /* default output device */
+    outputParameters.channelCount = config->numOutputChannels;
+    outputParameters.sampleFormat = OUTPUT_FORMAT | (config->isOutputInterleaved ? 0 : paNonInterleaved);
+    outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultLowOutputLatency;
+    outputParameters.hostApiSpecificStreamInfo = NULL;
+
+    err = Pa_OpenStream(
+              &stream,
+              &inputParameters,
+              &outputParameters,
+              SAMPLE_RATE,
+              config->framesPerCallback, /* frames per buffer */
+              paClipOff, /* we won't output out of range samples so don't bother clipping them */
+              wireCallback,
+              config );
+    if( err != paNoError ) goto error;
+    
+    err = Pa_StartStream( stream );
+    if( err != paNoError ) goto error;
+    
+    printf("Hit ENTER for next configuration, or 'q' to quit.\n");  fflush(stdout);
+    c = getchar();
+    
+    printf("Closing stream.\n");
+    err = Pa_CloseStream( stream );
+    if( err != paNoError ) goto error;
+
+    if( c == 'q' ) return 1;
+
+error:
+    return err;
+}
diff --git a/src/audio/portaudio/pa_tests/patest_write_sine.c b/src/audio/portaudio/pa_tests/patest_write_sine.c
new file mode 100644
index 0000000000000000000000000000000000000000..f0fcba0b6e6d015d52aee02a62c7c78318ed6303
--- /dev/null
+++ b/src/audio/portaudio/pa_tests/patest_write_sine.c
@@ -0,0 +1,147 @@
+/** @file patest_write_sine.c
+	@brief Play a sine wave for several seconds using the blocking API (Pa_WriteStream())
+	@author Ross Bencina <rossb@audiomulch.com>
+    @author Phil Burk <philburk@softsynth.com>
+*/
+/*
+ * $Id$
+ *
+ * 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         (5)
+#define SAMPLE_RATE         (44100)
+#define FRAMES_PER_BUFFER   (1024)
+
+#ifndef M_PI
+#define M_PI  (3.14159265)
+#endif
+
+#define TABLE_SIZE   (200)
+
+
+int main(void);
+int main(void)
+{
+    PaStreamParameters outputParameters;
+    PaStream *stream;
+    PaError err;
+    float buffer[FRAMES_PER_BUFFER][2]; /* stereo output buffer */
+    float sine[TABLE_SIZE]; /* sine wavetable */
+    int left_phase = 0;
+    int right_phase = 0;
+    int left_inc = 1;
+    int right_inc = 3; /* higher pitch so we can distinguish left and right. */
+    int i, j, k;
+    int bufferCount;
+
+    
+    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++ )
+    {
+        sine[i] = (float) sin( ((double)i/(double)TABLE_SIZE) * M_PI * 2. );
+    }
+
+    
+    err = Pa_Initialize();
+    if( err != paNoError ) goto error;
+
+    outputParameters.device = Pa_GetDefaultOutputDevice(); /* default output device */
+    outputParameters.channelCount = 2;       /* stereo output */
+    outputParameters.sampleFormat = paFloat32; /* 32 bit floating point output */
+    outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultLowOutputLatency;
+    outputParameters.hostApiSpecificStreamInfo = NULL;
+
+    err = Pa_OpenStream(
+              &stream,
+              NULL, /* no input */
+              &outputParameters,
+              SAMPLE_RATE,
+              FRAMES_PER_BUFFER,
+              paClipOff,      /* we won't output out of range samples so don't bother clipping them */
+              NULL, /* no callback, use blocking API */
+              NULL ); /* no callback, so no callback userData */
+    if( err != paNoError ) goto error;
+
+
+    printf( "Play 3 times, higher each time.\n" );
+    
+    for( k=0; k < 3; ++k )
+    {
+        err = Pa_StartStream( stream );
+        if( err != paNoError ) goto error;
+
+        printf("Play for %d seconds.\n", NUM_SECONDS );
+
+        bufferCount = ((NUM_SECONDS * SAMPLE_RATE) / FRAMES_PER_BUFFER);
+
+        for( i=0; i < bufferCount; i++ )
+        {
+            for( j=0; j < FRAMES_PER_BUFFER; j++ )
+            {
+                buffer[j][0] = sine[left_phase];  /* left */
+                buffer[j][1] = sine[right_phase];  /* right */
+                left_phase += left_inc;
+                if( left_phase >= TABLE_SIZE ) left_phase -= TABLE_SIZE;
+                right_phase += right_inc;
+                if( right_phase >= TABLE_SIZE ) right_phase -= TABLE_SIZE;
+            }
+
+            err = Pa_WriteStream( stream, buffer, FRAMES_PER_BUFFER );
+            if( err != paNoError ) goto error;
+        }   
+
+        err = Pa_StopStream( stream );
+        if( err != paNoError ) goto error;
+
+        ++left_inc;
+        ++right_inc;
+
+        Pa_Sleep( 1000 );
+    }
+
+    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/src/audio/portaudio/pa_unix/pa_unix_hostapis.c b/src/audio/portaudio/pa_unix/pa_unix_hostapis.c
new file mode 100644
index 0000000000000000000000000000000000000000..570f596e70c1b9dde9762d0d66df968bbbbac78a
--- /dev/null
+++ b/src/audio/portaudio/pa_unix/pa_unix_hostapis.c
@@ -0,0 +1,64 @@
+/*
+ * $Id$
+ * Portable Audio I/O Library UNIX initialization table
+ *
+ * Based on the Open Source API proposed by Ross Bencina
+ * Copyright (c) 1999-2002 Ross Bencina, 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 "pa_hostapi.h"
+
+PaError PaJack_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
+PaError PaAlsa_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
+PaError PaOSS_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
+/* Added for IRIX, Pieter, oct 2, 2003: */
+PaError PaSGI_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
+
+
+PaUtilHostApiInitializer *paHostApiInitializers[] =
+    {
+#ifdef PA_USE_OSS
+        PaOSS_Initialize,
+#endif
+
+#ifdef PA_USE_ALSA
+        PaAlsa_Initialize,
+#endif
+
+#ifdef PA_USE_JACK
+        PaJack_Initialize,
+#endif
+                    /* Added for IRIX, Pieter, oct 2, 2003: */
+#ifdef PA_USE_SGI 
+        PaSGI_Initialize,
+#endif
+        0   /* NULL terminated array */
+    };
+
+int paDefaultHostApiIndex = 0;
+
+
diff --git a/src/audio/portaudio/pa_unix/pa_unix_util.c b/src/audio/portaudio/pa_unix/pa_unix_util.c
new file mode 100644
index 0000000000000000000000000000000000000000..f8cab4021c78afc6c5de2680ca47665f354992f0
--- /dev/null
+++ b/src/audio/portaudio/pa_unix/pa_unix_util.c
@@ -0,0 +1,175 @@
+/*
+ * $Id$
+ * Portable Audio I/O Library
+ * UNIX platform-specific support functions
+ *
+ * Based on the Open Source API proposed by Ross Bencina
+ * Copyright (c) 1999-2000 Ross Bencina
+ *
+ * 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 <pthread.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/time.h>
+#include <assert.h>
+#include <string.h> /* For memset */
+
+#include "pa_util.h"
+#include "pa_unix_util.h"
+
+/*
+   Track memory allocations to avoid leaks.
+ */
+
+#if PA_TRACK_MEMORY
+static int numAllocations_ = 0;
+#endif
+
+
+void *PaUtil_AllocateMemory( long size )
+{
+    void *result = malloc( size );
+
+#if PA_TRACK_MEMORY
+    if( result != NULL ) numAllocations_ += 1;
+#endif
+    return result;
+}
+
+
+void PaUtil_FreeMemory( void *block )
+{
+    if( block != NULL )
+    {
+        free( block );
+#if PA_TRACK_MEMORY
+        numAllocations_ -= 1;
+#endif
+
+    }
+}
+
+
+int PaUtil_CountCurrentlyAllocatedBlocks( void )
+{
+#if PA_TRACK_MEMORY
+    return numAllocations_;
+#else
+    return 0;
+#endif
+}
+
+
+void Pa_Sleep( long msec )
+{
+    while( msec > 999 )     /* For OpenBSD and IRIX, argument */
+        {                   /* to usleep must be < 1000000.   */
+        usleep( 999000 );
+        msec -= 999;
+        }
+    usleep( msec * 1000 );
+}
+
+/*            *** NOT USED YET: ***
+static int usePerformanceCounter_;
+static double microsecondsPerTick_;
+*/
+
+void PaUtil_InitializeClock( void )
+{
+    /* TODO */
+}
+
+
+PaTime PaUtil_GetTime( void )
+{
+    struct timeval tv;
+    gettimeofday( &tv, NULL );
+    return (PaTime) tv.tv_usec / 1000000. + tv.tv_sec;
+}
+
+PaError PaUtil_InitializeThreading( PaUtilThreading *threading )
+{
+    (void) paUtilErr_;
+    return paNoError;
+}
+
+void PaUtil_TerminateThreading( PaUtilThreading *threading )
+{
+}
+
+PaError PaUtil_StartThreading( PaUtilThreading *threading, void *(*threadRoutine)(void *), void *data )
+{
+    pthread_create( &threading->callbackThread, NULL, threadRoutine, data );
+    return paNoError;
+}
+
+PaError PaUtil_CancelThreading( PaUtilThreading *threading, int wait, PaError *exitResult )
+{
+    PaError result = paNoError;
+    void *pret;
+
+    if( exitResult )
+        *exitResult = paNoError;
+
+    /* Only kill the thread if it isn't in the process of stopping (flushing adaptation buffers) */
+    if( !wait )
+        pthread_cancel( threading->callbackThread );   /* XXX: Safe to call this if the thread has exited on its own? */
+    pthread_join( threading->callbackThread, &pret );
+
+#ifdef PTHREAD_CANCELED
+    if( pret && PTHREAD_CANCELED != pret )
+#else
+    /* !wait means the thread may have been canceled */
+    if( pret && wait )
+#endif
+    {
+        if( exitResult )
+            *exitResult = *(PaError *) pret;
+        free( pret );
+    }
+
+    return result;
+}
+
+/*
+static void *CanaryFunc( void *userData )
+{
+    const unsigned intervalMsec = 1000;
+    PaUtilThreading *th = (PaUtilThreading *) userData;
+
+    while( 1 )
+    {
+        th->canaryTime = PaUtil_GetTime();
+
+        pthread_testcancel();
+        Pa_Sleep( intervalMsec );
+    }
+
+    pthread_exit( NULL );
+}
+*/
diff --git a/src/audio/portaudio/pa_unix/pa_unix_util.h b/src/audio/portaudio/pa_unix/pa_unix_util.h
new file mode 100644
index 0000000000000000000000000000000000000000..01dda01e5f775e8ece5906ffdc29b08057c25519
--- /dev/null
+++ b/src/audio/portaudio/pa_unix/pa_unix_util.h
@@ -0,0 +1,73 @@
+#ifndef PA_UNIX_UTIL_H
+#define PA_UNIX_UTIL_H
+
+#include "pa_cpuload.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif /* __cplusplus */
+
+#define PA_MIN(x,y) ( (x) < (y) ? (x) : (y) )
+#define PA_MAX(x,y) ( (x) > (y) ? (x) : (y) )
+
+/* Utilize GCC branch prediction for error tests */
+#if defined __GNUC__ && __GNUC__ >= 3
+#define UNLIKELY(expr) __builtin_expect( (expr), 0 )
+#else
+#define UNLIKELY(expr) (expr)
+#endif
+
+#define STRINGIZE_HELPER(expr) #expr
+#define STRINGIZE(expr) STRINGIZE_HELPER(expr)
+
+#define PA_UNLESS(expr, code) \
+    do { \
+        if( UNLIKELY( (expr) == 0 ) ) \
+        { \
+            PaUtil_DebugPrint(( "Expression '" #expr "' failed in '" __FILE__ "', line: " STRINGIZE( __LINE__ ) "\n" )); \
+            result = (code); \
+            goto error; \
+        } \
+    } while (0);
+
+static PaError paUtilErr_;          /* Used with PA_ENSURE */
+
+/* Check PaError */
+#define PA_ENSURE(expr) \
+    do { \
+        if( UNLIKELY( (paUtilErr_ = (expr)) < paNoError ) ) \
+        { \
+            PaUtil_DebugPrint(( "Expression '" #expr "' failed in '" __FILE__ "', line: " STRINGIZE( __LINE__ ) "\n" )); \
+            result = paUtilErr_; \
+            goto error; \
+        } \
+    } while (0);
+
+typedef struct {
+    pthread_t callbackThread;
+} PaUtilThreading;
+
+PaError PaUtil_InitializeThreading( PaUtilThreading *threading );
+void PaUtil_TerminateThreading( PaUtilThreading *threading );
+PaError PaUtil_StartThreading( PaUtilThreading *threading, void *(*threadRoutine)(void *), void *data );
+PaError PaUtil_CancelThreading( PaUtilThreading *threading, int wait, PaError *exitResult );
+
+/* State accessed by utility functions */
+
+/*
+void PaUnix_SetRealtimeScheduling( int rt );
+
+void PaUtil_InitializeThreading( PaUtilThreading *th, PaUtilCpuLoadMeasurer *clm );
+
+PaError PaUtil_CreateCallbackThread( PaUtilThreading *th, void *(*CallbackThreadFunc)( void * ), PaStream *s );
+
+PaError PaUtil_KillCallbackThread( PaUtilThreading *th, PaError *exitResult );
+
+void PaUtil_CallbackUpdate( PaUtilThreading *th );
+*/
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif
diff --git a/src/audio/portaudio/pa_unix_oss/low_latency_tip.txt b/src/audio/portaudio/pa_unix_oss/low_latency_tip.txt
new file mode 100644
index 0000000000000000000000000000000000000000..2d982b79baa6d943c1fae0c7122d190ebf46c6f4
Binary files /dev/null and b/src/audio/portaudio/pa_unix_oss/low_latency_tip.txt differ
diff --git a/src/audio/portaudio/pa_unix_oss/pa_unix_oss.c b/src/audio/portaudio/pa_unix_oss/pa_unix_oss.c
new file mode 100644
index 0000000000000000000000000000000000000000..ef87c83b03d4f2939b53f461dca3108ca74b6932
--- /dev/null
+++ b/src/audio/portaudio/pa_unix_oss/pa_unix_oss.c
@@ -0,0 +1,1918 @@
+/*
+ * $Id$
+ * PortAudio Portable Real-Time Audio Library
+ * Latest Version at: http://www.portaudio.com
+ * OSS implementation by:
+ *   Douglas Repetto
+ *   Phil Burk
+ *   Dominic Mazzoni
+ *   Arve Knudsen
+ *
+ * Based on the Open Source API proposed by Ross Bencina
+ * Copyright (c) 1999-2002 Ross Bencina, 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 <string.h>
+#include <math.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+#include <pthread.h>
+#include <alloca.h>
+#include <malloc.h>
+#include <assert.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/poll.h>
+#include <limits.h>
+#include <semaphore.h>
+
+#ifdef __linux__
+# include <linux/soundcard.h>
+# define DEVICE_NAME_BASE            "/dev/dsp"
+#else
+# include <machine/soundcard.h> /* JH20010905 */
+# define DEVICE_NAME_BASE            "/dev/audio"
+#endif
+
+#include "portaudio.h"
+#include "pa_util.h"
+#include "pa_allocation.h"
+#include "pa_hostapi.h"
+#include "pa_stream.h"
+#include "pa_cpuload.h"
+#include "pa_process.h"
+#include "../pa_unix/pa_unix_util.h"
+
+static int sysErr_;
+static pthread_t mainThread_;
+
+/* Check return value of system call, and map it to PaError */
+#define ENSURE_(expr, code) \
+    do { \
+        if( UNLIKELY( (sysErr_ = (expr)) < 0 ) ) \
+        { \
+            /* PaUtil_SetLastHostErrorInfo should only be used in the main thread */ \
+            if( (code) == paUnanticipatedHostError && pthread_self() == mainThread_ ) \
+            { \
+                PaUtil_SetLastHostErrorInfo( paALSA, sysErr_, strerror( errno ) ); \
+            } \
+            \
+            PaUtil_DebugPrint(( "Expression '" #expr "' failed in '" __FILE__ "', line: " STRINGIZE( __LINE__ ) "\n" )); \
+            result = (code); \
+            goto error; \
+        } \
+    } while( 0 );
+
+#ifndef AFMT_S16_NE
+#define AFMT_S16_NE  Get_AFMT_S16_NE()
+/*********************************************************************
+ * Some versions of OSS do not define AFMT_S16_NE. So check CPU.
+ * PowerPC is Big Endian. X86 is Little Endian.
+ */
+static int Get_AFMT_S16_NE( void )
+{
+    long testData = 1; 
+    char *ptr = (char *) &testData;
+    int isLittle = ( *ptr == 1 ); /* Does address point to least significant byte? */
+    return isLittle ? AFMT_S16_LE : AFMT_S16_BE;
+}
+#endif
+
+/* PaOSSHostApiRepresentation - host api datastructure specific to this implementation */
+
+typedef struct
+{
+    PaUtilHostApiRepresentation inheritedHostApiRep;
+    PaUtilStreamInterface callbackStreamInterface;
+    PaUtilStreamInterface blockingStreamInterface;
+
+    PaUtilAllocationGroup *allocations;
+
+    PaHostApiIndex hostApiIndex;
+}
+PaOSSHostApiRepresentation;
+
+/** Per-direction structure for PaOssStream.
+ *
+ * Aspect StreamChannels: In case the user requests to open the same device for both capture and playback,
+ * but with different number of channels we will have to adapt between the number of user and host
+ * channels for at least one direction, since the configuration space is the same for both directions
+ * of an OSS device.
+ */
+typedef struct
+{
+    int fd;
+    const char *devName;
+    int userChannelCount, hostChannelCount;
+    int userInterleaved;
+    void *buffer;
+    PaSampleFormat userFormat, hostFormat;
+    double latency;
+    unsigned long hostFrames, numBufs;
+    void **userBuffers; /* For non-interleaved blocking */
+} PaOssStreamComponent;
+
+/** Implementation specific representation of a PaStream.
+ *
+ */
+typedef struct PaOssStream
+{
+    PaUtilStreamRepresentation streamRepresentation;
+    PaUtilCpuLoadMeasurer cpuLoadMeasurer;
+    PaUtilBufferProcessor bufferProcessor;
+
+    PaUtilThreading threading;
+
+    int sharedDevice;
+    unsigned long framesPerHostBuffer;
+    int triggered;  /* Have the devices been triggered yet (first start) */
+
+    int isActive;
+    int isStopped;
+
+    int lastPosPtr;
+    double lastStreamBytes;
+
+    int framesProcessed;
+
+    double sampleRate;
+
+    int callbackMode;
+    int callbackStop, callbackAbort;
+
+    PaOssStreamComponent *capture, *playback;
+    unsigned long pollTimeout;
+    sem_t semaphore;
+}
+PaOssStream;
+
+typedef enum {
+    StreamMode_In,
+    StreamMode_Out
+} StreamMode;
+
+/* prototypes for functions declared in this file */
+
+static void Terminate( struct PaUtilHostApiRepresentation *hostApi );
+static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
+                                  const PaStreamParameters *inputParameters,
+                                  const PaStreamParameters *outputParameters,
+                                  double sampleRate );
+static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
+                           PaStream** s,
+                           const PaStreamParameters *inputParameters,
+                           const PaStreamParameters *outputParameters,
+                           double sampleRate,
+                           unsigned long framesPerBuffer,
+                           PaStreamFlags streamFlags,
+                           PaStreamCallback *streamCallback,
+                           void *userData );
+static PaError CloseStream( PaStream* stream );
+static PaError StartStream( PaStream *stream );
+static PaError StopStream( PaStream *stream );
+static PaError AbortStream( PaStream *stream );
+static PaError IsStreamStopped( PaStream *s );
+static PaError IsStreamActive( PaStream *stream );
+static PaTime GetStreamTime( PaStream *stream );
+static double GetStreamCpuLoad( PaStream* stream );
+static PaError ReadStream( PaStream* stream, void *buffer, unsigned long frames );
+static PaError WriteStream( PaStream* stream, const void *buffer, unsigned long frames );
+static signed long GetStreamReadAvailable( PaStream* stream );
+static signed long GetStreamWriteAvailable( PaStream* stream );
+static PaError BuildDeviceList( PaOSSHostApiRepresentation *hostApi );
+
+
+/** Initialize the OSS API implementation.
+ *
+ * This function will initialize host API datastructures and query host devices for information.
+ *
+ * Aspect DeviceCapabilities: Enumeration of host API devices is initiated from here
+ *
+ * Aspect FreeResources: If an error is encountered under way we have to free each resource allocated in this function,
+ * this happens with the usual "error" label.
+ */
+PaError PaOSS_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex )
+{
+    PaError result = paNoError;
+    PaOSSHostApiRepresentation *ossHostApi = NULL;
+
+    PA_UNLESS( ossHostApi = (PaOSSHostApiRepresentation*)PaUtil_AllocateMemory( sizeof(PaOSSHostApiRepresentation) ),
+            paInsufficientMemory );
+    PA_UNLESS( ossHostApi->allocations = PaUtil_CreateAllocationGroup(), paInsufficientMemory );
+    ossHostApi->hostApiIndex = hostApiIndex;
+
+    /* Initialize host API structure */
+    *hostApi = &ossHostApi->inheritedHostApiRep;
+    (*hostApi)->info.structVersion = 1;
+    (*hostApi)->info.type = paOSS;
+    (*hostApi)->info.name = "OSS";
+    (*hostApi)->Terminate = Terminate;
+    (*hostApi)->OpenStream = OpenStream;
+    (*hostApi)->IsFormatSupported = IsFormatSupported;
+
+    PA_ENSURE( BuildDeviceList( ossHostApi ) );
+
+    PaUtil_InitializeStreamInterface( &ossHostApi->callbackStreamInterface, CloseStream, StartStream,
+                                      StopStream, AbortStream, IsStreamStopped, IsStreamActive,
+                                      GetStreamTime, GetStreamCpuLoad,
+                                      PaUtil_DummyRead, PaUtil_DummyWrite,
+                                      PaUtil_DummyGetReadAvailable,
+                                      PaUtil_DummyGetWriteAvailable );
+
+    PaUtil_InitializeStreamInterface( &ossHostApi->blockingStreamInterface, CloseStream, StartStream,
+                                      StopStream, AbortStream, IsStreamStopped, IsStreamActive,
+                                      GetStreamTime, PaUtil_DummyGetCpuLoad,
+                                      ReadStream, WriteStream, GetStreamReadAvailable, GetStreamWriteAvailable );
+
+    mainThread_ = pthread_self();
+
+    return result;
+
+error:
+    if( ossHostApi )
+    {
+        if( ossHostApi->allocations )
+        {
+            PaUtil_FreeAllAllocations( ossHostApi->allocations );
+            PaUtil_DestroyAllocationGroup( ossHostApi->allocations );
+        }
+                
+        PaUtil_FreeMemory( ossHostApi );
+    }
+    return result;
+}
+
+PaError PaUtil_InitializeDeviceInfo( PaDeviceInfo *deviceInfo, const char *name, PaHostApiIndex hostApiIndex, int maxInputChannels,
+        int maxOutputChannels, PaTime defaultLowInputLatency, PaTime defaultLowOutputLatency, PaTime defaultHighInputLatency,
+        PaTime defaultHighOutputLatency, double defaultSampleRate, PaUtilAllocationGroup *allocations  )
+{
+    PaError result = paNoError;
+    
+    deviceInfo->structVersion = 2;
+    if( allocations )
+    {
+        size_t len = strlen( name ) + 1;
+        PA_UNLESS( deviceInfo->name = PaUtil_GroupAllocateMemory( allocations, len ), paInsufficientMemory );
+        strncpy( (char *)deviceInfo->name, name, len );
+    }
+    else
+        deviceInfo->name = name;
+
+    deviceInfo->hostApi = hostApiIndex;
+    deviceInfo->maxInputChannels = maxInputChannels;
+    deviceInfo->maxOutputChannels = maxOutputChannels;
+    deviceInfo->defaultLowInputLatency = defaultLowInputLatency;
+    deviceInfo->defaultLowOutputLatency = defaultLowOutputLatency;
+    deviceInfo->defaultHighInputLatency = defaultHighInputLatency;
+    deviceInfo->defaultHighOutputLatency = defaultHighOutputLatency;
+    deviceInfo->defaultSampleRate = defaultSampleRate;
+
+error:
+    return result;
+}
+
+static PaError QueryDirection( const char *deviceName, StreamMode mode, double *defaultSampleRate, int *maxChannelCount,
+        double *defaultLowLatency, double *defaultHighLatency )
+{
+    PaError result = paNoError;
+    int numChannels, maxNumChannels;
+    int busy = 0;
+    int devHandle = -1;
+    int sr;
+    *maxChannelCount = 0;  /* Default value in case this fails */
+
+    if ( (devHandle = open( deviceName, (mode == StreamMode_In ? O_RDONLY : O_WRONLY) | O_NONBLOCK ))  < 0 )
+    {
+        if( errno == EBUSY || errno == EAGAIN )
+        {
+            PA_DEBUG(( "%s: Device %s busy\n", __FUNCTION__, deviceName ));
+        }
+        else
+        {
+            PA_DEBUG(( "%s: Can't access device: %s\n", __FUNCTION__, strerror( errno ) ));
+        }
+
+        return paDeviceUnavailable;
+    }
+
+    /* Negotiate for the maximum number of channels for this device. PLB20010927
+     * Consider up to 16 as the upper number of channels.
+     * Variable maxNumChannels 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;
+        if( ioctl( devHandle, SNDCTL_DSP_CHANNELS, &temp ) < 0 )
+        {
+            busy = EAGAIN == errno || EBUSY == errno;
+            /* ioctl() failed so bail out if we already have stereo */
+            if( maxNumChannels >= 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;
+            if( temp > maxNumChannels )
+                maxNumChannels = temp; /* Save maximum. */
+        }
+    }
+    /* A: We're able to open a device for capture if it's busy playing back and vice versa,
+     * but we can't configure anything */
+    if( 0 == maxNumChannels && busy )
+    {
+        result = paDeviceUnavailable;
+        goto error;
+    }
+
+    /* The above negotiation may fail for an old driver so try this older technique. */
+    if( maxNumChannels < 1 )
+    {
+        int stereo = 1;
+        if( ioctl( devHandle, SNDCTL_DSP_STEREO, &stereo ) < 0 )
+        {
+            maxNumChannels = 1;
+        }
+        else
+        {
+            maxNumChannels = (stereo) ? 2 : 1;
+        }
+        PA_DEBUG(( "%s: use SNDCTL_DSP_STEREO, maxNumChannels = %d\n", __FUNCTION__, 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 */
+    {
+        /* use most reasonable default value */
+        int temp = PA_MIN( maxNumChannels, 2 );
+        ENSURE_( ioctl( devHandle, SNDCTL_DSP_CHANNELS, &temp ), paUnanticipatedHostError );
+    }
+
+    /* Get supported sample rate closest to 44100 Hz */
+    if( *defaultSampleRate < 0 )
+    {
+        sr = 44100;
+        if( ioctl( devHandle, SNDCTL_DSP_SPEED, &sr ) < 0 )
+        {
+            result = paUnanticipatedHostError;
+            goto error;
+        }
+
+        *defaultSampleRate = sr;
+    }
+
+    *maxChannelCount = maxNumChannels;
+    /* TODO */
+    *defaultLowLatency = 512. / *defaultSampleRate;
+    *defaultHighLatency = 2048. / *defaultSampleRate;
+
+error:
+    if( devHandle >= 0 )
+        close( devHandle );
+
+    return result;
+}
+
+/** Query OSS device.
+ *
+ * This is where PaDeviceInfo objects are constructed and filled in with relevant information.
+ *
+ * Aspect DeviceCapabilities: The inferred device capabilities are recorded in a PaDeviceInfo object that is constructed
+ * in place.
+ */
+static PaError QueryDevice( char *deviceName, PaOSSHostApiRepresentation *ossApi, PaDeviceInfo **deviceInfo )
+{
+    PaError result = paNoError;
+    double sampleRate = -1.;
+    int maxInputChannels, maxOutputChannels;
+    PaTime defaultLowInputLatency, defaultLowOutputLatency, defaultHighInputLatency, defaultHighOutputLatency;
+    PaError tmpRes = paNoError;
+    int busy = 0;
+    *deviceInfo = NULL;
+
+    /* 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
+    */
+
+    /* Aspect StreamChannels: The number of channels supported for a device may depend on the mode it is
+     * opened in, it may have more channels available for capture than playback and vice versa. Therefore
+     * we will open the device in both read- and write-only mode to determine the supported number.
+     */
+    if( (tmpRes = QueryDirection( deviceName, StreamMode_In, &sampleRate, &maxInputChannels, &defaultLowInputLatency,
+                &defaultHighInputLatency )) != paNoError )
+    {
+        if( tmpRes != paDeviceUnavailable )
+        {
+            PA_DEBUG(( "%s: Querying device %s for capture failed!\n", __FUNCTION__, deviceName ));
+            /* PA_ENSURE( tmpRes ); */
+        }
+        ++busy;
+    }
+    if( (tmpRes = QueryDirection( deviceName, StreamMode_Out, &sampleRate, &maxOutputChannels, &defaultLowOutputLatency,
+                &defaultHighOutputLatency )) != paNoError )
+    {
+        if( tmpRes != paDeviceUnavailable )
+        {
+            PA_DEBUG(( "%s: Querying device %s for playback failed!\n", __FUNCTION__, deviceName ));
+            /* PA_ENSURE( tmpRes ); */
+        }
+        ++busy;
+    }
+    assert( 0 <= busy && busy <= 2 );
+    if( 2 == busy )     /* Both directions are unavailable to us */
+    {
+        result = paDeviceUnavailable;
+        goto error;
+    }
+
+    PA_UNLESS( *deviceInfo = PaUtil_GroupAllocateMemory( ossApi->allocations, sizeof (PaDeviceInfo) ), paInsufficientMemory );
+    PA_ENSURE( PaUtil_InitializeDeviceInfo( *deviceInfo, deviceName, ossApi->hostApiIndex, maxInputChannels, maxOutputChannels,
+                defaultLowInputLatency, defaultLowOutputLatency, defaultHighInputLatency, defaultHighOutputLatency, sampleRate,
+                ossApi->allocations ) );
+
+error:
+    return result;
+}
+
+/** Query host devices.
+ *
+ * Loop over host devices and query their capabilitiesu
+ *
+ * Aspect DeviceCapabilities: This function calls QueryDevice on each device entry and receives a filled in PaDeviceInfo object
+ * per device, these are placed in the host api representation's deviceInfos array.
+ */
+static PaError BuildDeviceList( PaOSSHostApiRepresentation *ossApi )
+{
+    PaError result = paNoError;
+    PaUtilHostApiRepresentation *commonApi = &ossApi->inheritedHostApiRep;
+    int i;
+    int numDevices = 0, maxDeviceInfos = 1;
+    PaDeviceInfo **deviceInfos = NULL;
+
+    /* These two will be set to the first working input and output device, respectively */
+    commonApi->info.defaultInputDevice = paNoDevice;
+    commonApi->info.defaultOutputDevice = paNoDevice;
+
+    /* Find devices by calling QueryDevice on each
+     * potential device names.  When we find a valid one,
+     * add it to a linked list.
+     * A: Can there only be 10 devices? */
+
+    for( i = 0; i < 10; i++ )
+    {
+       char deviceName[32];
+       PaDeviceInfo *deviceInfo;
+       int testResult;
+       struct stat stbuf;
+
+       if( i == 0 )
+          snprintf(deviceName, sizeof (deviceName), "%s", DEVICE_NAME_BASE);
+       else
+          snprintf(deviceName, sizeof (deviceName), "%s%d", DEVICE_NAME_BASE, i);
+
+       /* PA_DEBUG(("PaOSS BuildDeviceList: trying device %s\n", deviceName )); */
+       if( stat( deviceName, &stbuf ) < 0 )
+       {
+           if( ENOENT != errno )
+               PA_DEBUG(( "%s: Error stat'ing %s: %s\n", __FUNCTION__, deviceName, strerror( errno ) ));
+           continue;
+       }
+       if( (testResult = QueryDevice( deviceName, ossApi, &deviceInfo )) != paNoError )
+       {
+           if( testResult != paDeviceUnavailable )
+               PA_ENSURE( testResult );
+
+           continue;
+       }
+
+       ++numDevices;
+       if( !deviceInfos || numDevices > maxDeviceInfos )
+       {
+           maxDeviceInfos *= 2;
+           PA_UNLESS( deviceInfos = (PaDeviceInfo **) realloc( deviceInfos, maxDeviceInfos * sizeof (PaDeviceInfo *) ),
+                   paInsufficientMemory );
+       }
+       deviceInfos[numDevices - 1] = deviceInfo;
+
+       if( commonApi->info.defaultInputDevice == paNoDevice && deviceInfo->maxInputChannels > 0 )
+           commonApi->info.defaultInputDevice = i;
+       if( commonApi->info.defaultOutputDevice == paNoDevice && deviceInfo->maxOutputChannels > 0 )
+           commonApi->info.defaultOutputDevice = i;
+    }
+
+    /* Make an array of PaDeviceInfo pointers out of the linked list */
+
+    PA_DEBUG(("PaOSS %s: Total number of devices found: %d\n", __FUNCTION__, numDevices));
+
+    commonApi->deviceInfos = (PaDeviceInfo**)PaUtil_GroupAllocateMemory(
+        ossApi->allocations, sizeof(PaDeviceInfo*) * numDevices );
+    memcpy( commonApi->deviceInfos, deviceInfos, numDevices * sizeof (PaDeviceInfo *) );
+
+    commonApi->info.deviceCount = numDevices;
+
+error:
+    free( deviceInfos );
+
+    return result;
+}
+
+static void Terminate( struct PaUtilHostApiRepresentation *hostApi )
+{
+    PaOSSHostApiRepresentation *ossHostApi = (PaOSSHostApiRepresentation*)hostApi;
+
+    if( ossHostApi->allocations )
+    {
+        PaUtil_FreeAllAllocations( ossHostApi->allocations );
+        PaUtil_DestroyAllocationGroup( ossHostApi->allocations );
+    }
+
+    PaUtil_FreeMemory( ossHostApi );
+}
+
+static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
+                                  const PaStreamParameters *inputParameters,
+                                  const PaStreamParameters *outputParameters,
+                                  double sampleRate )
+{
+    PaError result = paNoError;
+    PaDeviceIndex device;
+    PaDeviceInfo *deviceInfo;
+    char *deviceName;
+    int inputChannelCount, outputChannelCount;
+    int tempDevHandle = -1;
+    int flags;
+    PaSampleFormat inputSampleFormat, outputSampleFormat;
+    
+    if( inputParameters )
+    {
+        inputChannelCount = inputParameters->channelCount;
+        inputSampleFormat = inputParameters->sampleFormat;
+
+        /* unless alternate device specification is supported, reject the use of
+            paUseHostApiSpecificDeviceSpecification */
+
+        if( inputParameters->device == paUseHostApiSpecificDeviceSpecification )
+            return paInvalidDevice;
+
+        /* check that input device can support inputChannelCount */
+        if( inputChannelCount > hostApi->deviceInfos[ inputParameters->device ]->maxInputChannels )
+            return paInvalidChannelCount;
+
+        /* validate inputStreamInfo */
+        if( inputParameters->hostApiSpecificStreamInfo )
+            return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */
+    }
+    else
+    {
+        inputChannelCount = 0;
+    }
+
+    if( outputParameters )
+    {
+        outputChannelCount = outputParameters->channelCount;
+        outputSampleFormat = outputParameters->sampleFormat;
+        
+        /* unless alternate device specification is supported, reject the use of
+            paUseHostApiSpecificDeviceSpecification */
+
+        if( outputParameters->device == paUseHostApiSpecificDeviceSpecification )
+            return paInvalidDevice;
+
+        /* check that output device can support inputChannelCount */
+        if( outputChannelCount > hostApi->deviceInfos[ outputParameters->device ]->maxOutputChannels )
+            return paInvalidChannelCount;
+
+        /* validate outputStreamInfo */
+        if( outputParameters->hostApiSpecificStreamInfo )
+            return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */
+    }
+    else
+    {
+        outputChannelCount = 0;
+    }
+
+    if (inputChannelCount == 0 && outputChannelCount == 0)
+        return paInvalidChannelCount;
+
+    /* if full duplex, make sure that they're the same device */
+
+    if (inputChannelCount > 0 && outputChannelCount > 0 &&
+        inputParameters->device != outputParameters->device)
+        return paInvalidDevice;
+
+    /* if full duplex, also make sure that they're the same number of channels */
+
+    if (inputChannelCount > 0 && outputChannelCount > 0 &&
+        inputChannelCount != outputChannelCount)
+       return paInvalidChannelCount;
+
+    /* open the device so we can do more tests */
+    
+    if( inputChannelCount > 0 )
+    {
+        result = PaUtil_DeviceIndexToHostApiDeviceIndex(&device, inputParameters->device, hostApi);
+        if (result != paNoError)
+            return result;
+    }
+    else
+    {
+        result = PaUtil_DeviceIndexToHostApiDeviceIndex(&device, outputParameters->device, hostApi);
+        if (result != paNoError)
+            return result;
+    }
+
+    deviceInfo = hostApi->deviceInfos[device];
+    deviceName = (char *)deviceInfo->name;
+    
+    flags = O_NONBLOCK;
+    if (inputChannelCount > 0 && outputChannelCount > 0)
+       flags |= O_RDWR;
+    else if (inputChannelCount > 0)
+       flags |= O_RDONLY;
+    else
+       flags |= O_WRONLY;
+
+    ENSURE_( tempDevHandle = open( deviceInfo->name, flags ), paDeviceUnavailable );
+
+    /* PaOssStream_Configure will do the rest of the checking for us */
+    /* PA_ENSURE( PaOssStream_Configure( tempDevHandle, deviceName, outputChannelCount, &sampleRate ) ); */
+
+    /* everything succeeded! */
+
+ error:
+    if( tempDevHandle >= 0 )
+        close( tempDevHandle );         
+
+    return result;
+}
+
+/** Validate stream parameters.
+ *
+ * Aspect StreamChannels: We verify that the number of channels is within the allowed range for the device
+ */
+static PaError ValidateParameters( const PaStreamParameters *parameters, const PaDeviceInfo *deviceInfo, StreamMode mode )
+{
+    int maxChans;
+
+    assert( parameters );
+
+    if( parameters->device == paUseHostApiSpecificDeviceSpecification )
+    {
+        return paInvalidDevice;
+    }
+
+    maxChans = (mode == StreamMode_In ? deviceInfo->maxInputChannels :
+        deviceInfo->maxOutputChannels);
+    if( parameters->channelCount > maxChans )
+    {
+        return paInvalidChannelCount;
+    }
+
+    return paNoError;
+}
+
+static PaError PaOssStreamComponent_Initialize( PaOssStreamComponent *component, const PaStreamParameters *parameters,
+        int callbackMode, int fd, const char *deviceName )
+{
+    PaError result = paNoError;
+    assert( component );
+
+    memset( component, 0, sizeof (PaOssStreamComponent) );
+
+    component->fd = fd;
+    component->devName = deviceName;
+    component->userChannelCount = parameters->channelCount;
+    component->userFormat = parameters->sampleFormat;
+    component->latency = parameters->suggestedLatency;
+    component->userInterleaved = !(parameters->sampleFormat & paNonInterleaved);
+
+    if( !callbackMode && !component->userInterleaved )
+    {
+        /* Pre-allocate non-interleaved user provided buffers */
+        PA_UNLESS( component->userBuffers = PaUtil_AllocateMemory( sizeof (void *) * component->userChannelCount ),
+                paInsufficientMemory );
+    }
+
+error:
+    return result;
+}
+
+static void PaOssStreamComponent_Terminate( PaOssStreamComponent *component )
+{
+    assert( component );
+
+    if( component->fd >= 0 )
+        close( component->fd );
+    if( component->buffer )
+        PaUtil_FreeMemory( component->buffer );
+
+    if( component->userBuffers )
+        PaUtil_FreeMemory( component->userBuffers );
+
+    PaUtil_FreeMemory( component );
+}
+
+static PaError ModifyBlocking( int fd, int blocking )
+{
+    PaError result = paNoError;
+    int fflags;
+
+    ENSURE_( fflags = fcntl( fd, F_GETFL ), paUnanticipatedHostError );
+
+    if( blocking )
+        fflags &= ~O_NONBLOCK;
+    else
+        fflags |= O_NONBLOCK;
+
+    ENSURE_( fcntl( fd, F_SETFL, fflags ), paUnanticipatedHostError );
+
+error:
+    return result;
+}
+
+static PaError OpenDevices( const char *idevName, const char *odevName, int *idev, int *odev )
+{
+    PaError result = paNoError;
+    int flags = O_NONBLOCK, duplex = 0;
+    int enableBits = 0;
+    *idev = *odev = -1;
+
+    if( idevName && odevName )
+    {
+        duplex = 1;
+        flags |= O_RDWR;
+    }
+    else if( idevName )
+        flags |= O_RDONLY;
+    else
+        flags |= O_WRONLY;
+
+    /* open first in nonblocking mode, in case it's busy...
+     * A: then unset the non-blocking attribute */
+    assert( flags & O_NONBLOCK );
+    if( idevName )
+    {
+        ENSURE_( *idev = open( idevName, flags ), paDeviceUnavailable );
+        PA_ENSURE( ModifyBlocking( *idev, 1 ) ); /* Blocking */
+
+        /* Initially disable */
+        enableBits = ~PCM_ENABLE_INPUT;
+        ENSURE_( ioctl( *idev, SNDCTL_DSP_SETTRIGGER, &enableBits ), paUnanticipatedHostError );
+    }
+    if( odevName )
+    {
+        if( !idevName )
+        {
+            ENSURE_( *odev = open( odevName, flags ), paDeviceUnavailable );
+            PA_ENSURE( ModifyBlocking( *odev, 1 ) ); /* Blocking */
+
+            /* Initially disable */
+            enableBits = ~PCM_ENABLE_OUTPUT;
+            ENSURE_( ioctl( *odev, SNDCTL_DSP_SETTRIGGER, &enableBits ), paUnanticipatedHostError );
+        }
+        else
+        {
+            ENSURE_( *odev = dup( *idev ), paUnanticipatedHostError );
+        }
+    }
+
+error:
+    return result;
+}
+
+static PaError PaOssStream_Initialize( PaOssStream *stream, const PaStreamParameters *inputParameters, const PaStreamParameters *outputParameters,
+        PaStreamCallback callback, void *userData, PaStreamFlags streamFlags,
+        PaOSSHostApiRepresentation *ossApi )
+{
+    PaError result = paNoError;
+    int idev, odev;
+    PaUtilHostApiRepresentation *hostApi = &ossApi->inheritedHostApiRep;
+    const char *idevName = NULL, *odevName = NULL;
+
+    assert( stream );
+
+    memset( stream, 0, sizeof (PaOssStream) );
+    stream->isStopped = 1;
+
+    PA_ENSURE( PaUtil_InitializeThreading( &stream->threading ) );
+
+    if( inputParameters && outputParameters )
+    {
+        if( inputParameters->device == outputParameters->device )
+            stream->sharedDevice = 1;
+    }
+
+    if( inputParameters )
+        idevName = hostApi->deviceInfos[inputParameters->device]->name;
+    if( outputParameters )
+        odevName = hostApi->deviceInfos[outputParameters->device]->name;
+    PA_ENSURE( OpenDevices( idevName, odevName, &idev, &odev ) );
+    if( inputParameters )
+    {
+        PA_UNLESS( stream->capture = PaUtil_AllocateMemory( sizeof (PaOssStreamComponent) ), paInsufficientMemory );
+        PA_ENSURE( PaOssStreamComponent_Initialize( stream->capture, inputParameters, callback != NULL, idev, idevName ) );
+    }
+    if( outputParameters )
+    {
+        PA_UNLESS( stream->playback = PaUtil_AllocateMemory( sizeof (PaOssStreamComponent) ), paInsufficientMemory );
+        PA_ENSURE( PaOssStreamComponent_Initialize( stream->playback, outputParameters, callback != NULL, odev, odevName ) );
+    }
+
+    if( callback != NULL )
+    {
+        PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation,
+                                               &ossApi->callbackStreamInterface, callback, userData );
+        stream->callbackMode = 1;
+    }
+    else
+    {
+        PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation,
+                                               &ossApi->blockingStreamInterface, callback, userData );
+    }    
+
+    ENSURE_( sem_init( &stream->semaphore, 0, 0 ), paInternalError );
+
+error:
+    return result;
+}
+
+static void PaOssStream_Terminate( PaOssStream *stream )
+{
+    assert( stream );
+
+    PaUtil_TerminateStreamRepresentation( &stream->streamRepresentation );
+    PaUtil_TerminateThreading( &stream->threading );
+
+    if( stream->capture )
+        PaOssStreamComponent_Terminate( stream->capture );
+    if( stream->playback )
+        PaOssStreamComponent_Terminate( stream->playback );
+
+    sem_destroy( &stream->semaphore );
+
+    PaUtil_FreeMemory( stream );
+}
+
+/** Translate from PA format to OSS native.
+ *
+ */
+static PaError Pa2OssFormat( PaSampleFormat paFormat, int *ossFormat )
+{
+    switch( paFormat )
+    {
+        case paUInt8:
+            *ossFormat = AFMT_U8;
+            break;
+        case paInt8:
+            *ossFormat = AFMT_S8;
+            break;
+        case paInt16:
+            *ossFormat = AFMT_S16_NE;
+            break;
+        default:
+            return paInternalError;     /* This shouldn't happen */
+    }
+
+    return paNoError;
+}
+
+/** Return the PA-compatible formats that this device can support.
+ *
+ */
+static PaError GetAvailableFormats( PaOssStreamComponent *component, PaSampleFormat *availableFormats )
+{
+    PaError result = paNoError;
+    int mask = 0;
+    PaSampleFormat frmts = 0;
+
+    ENSURE_( ioctl( component->fd, SNDCTL_DSP_GETFMTS, &mask ), paUnanticipatedHostError );
+    if( mask & AFMT_U8 )
+        frmts |= paUInt8;
+    if( mask & AFMT_S8 )
+        frmts |= paInt8;
+    if( mask & AFMT_S16_NE )
+        frmts |= paInt16;
+    else
+        result = paSampleFormatNotSupported;
+    
+    *availableFormats = frmts;
+
+error:
+    return result;
+}
+
+static unsigned int PaOssStreamComponent_FrameSize( PaOssStreamComponent *component )
+{
+    return Pa_GetSampleSize( component->hostFormat ) * component->hostChannelCount;
+}
+
+/** Buffer size in bytes.
+ *
+ */
+static unsigned long PaOssStreamComponent_BufferSize( PaOssStreamComponent *component )
+{
+    return PaOssStreamComponent_FrameSize( component ) * component->hostFrames * component->numBufs;
+}
+
+static int CalcHigherLogTwo( int n )
+{
+    int log2 = 0;
+    while( (1<<log2) < n ) log2++;
+    return log2;
+}
+
+static PaError PaOssStreamComponent_Configure( PaOssStreamComponent *component, double sampleRate, unsigned long framesPerBuffer,
+        StreamMode streamMode, PaOssStreamComponent *master )
+{
+    PaError result = paNoError;
+    int temp, nativeFormat;
+    int sr = (int)sampleRate;
+    PaSampleFormat availableFormats, hostFormat;
+    int chans = component->userChannelCount;
+    int frgmt;
+    int numBufs;
+    int bytesPerBuf;
+    double bufSz;
+    unsigned long fragSz;
+    audio_buf_info bufInfo;
+
+    /* We may have a situation where only one component (the master) is configured, if both point to the same device.
+     * In that case, the second component will copy settings from the other */
+    if( !master )
+    {
+        /* Aspect BufferSettings: If framesPerBuffer is unspecified we have to infer a suitable fragment size.
+         * The hardware need not respect the requested fragment size, so we may have to adapt.
+         */
+        if( framesPerBuffer == paFramesPerBufferUnspecified )
+        { 
+            bufSz = component->latency * sampleRate;
+            fragSz = bufSz / 4;
+        }
+        else
+        {
+            fragSz = framesPerBuffer;
+            bufSz = component->latency * sampleRate + fragSz; /* Latency + 1 buffer */
+        }
+
+        PA_ENSURE( GetAvailableFormats( component, &availableFormats ) );
+        hostFormat = PaUtil_SelectClosestAvailableFormat( availableFormats, component->userFormat );
+
+        /* OSS demands at least 2 buffers, and 16 bytes per buffer */
+        numBufs = PA_MAX( bufSz / fragSz, 2 );
+        bytesPerBuf = PA_MAX( fragSz * Pa_GetSampleSize( hostFormat ) * chans, 16 );
+
+        /* The fragment parameters are encoded like this:
+         * Most significant byte: number of fragments
+         * Least significant byte: exponent of fragment size (i.e., for 256, 8)
+         */
+        frgmt = (numBufs << 16) + (CalcHigherLogTwo( bytesPerBuf ) & 0xffff);
+        ENSURE_( ioctl( component->fd, SNDCTL_DSP_SETFRAGMENT, &frgmt ), paUnanticipatedHostError );
+
+        /* A: according to the OSS programmer's guide parameters should be set in this order:
+         * format, channels, rate */
+
+        /* This format should be deemed good before we get this far */
+        PA_ENSURE( Pa2OssFormat( hostFormat, &temp ) );
+        nativeFormat = temp;
+        ENSURE_( ioctl( component->fd, SNDCTL_DSP_SETFMT, &temp ), paUnanticipatedHostError );
+        PA_UNLESS( temp == nativeFormat, paInternalError );
+
+        /* try to set the number of channels */
+        ENSURE_( ioctl( component->fd, SNDCTL_DSP_CHANNELS, &chans ), paSampleFormatNotSupported );   /* XXX: Should be paInvalidChannelCount? */
+        /* It's possible that the minimum number of host channels is greater than what the user requested */
+        PA_UNLESS( chans >= component->userChannelCount, paInvalidChannelCount );
+
+        /* try to set the sample rate */
+        ENSURE_( ioctl( component->fd, SNDCTL_DSP_SPEED, &sr ), paInvalidSampleRate );
+
+        /* reject if there's no sample rate within 1% of the one requested */
+        if( (fabs( sampleRate - sr ) / sampleRate) > 0.01 )
+        {
+            PA_DEBUG(("%s: Wanted %f, closest sample rate was %d\n", __FUNCTION__, sampleRate, sr ));                 
+            PA_ENSURE( paInvalidSampleRate );
+        }
+
+        ENSURE_( ioctl( component->fd, streamMode == StreamMode_In ? SNDCTL_DSP_GETISPACE : SNDCTL_DSP_GETOSPACE, &bufInfo ),
+                paUnanticipatedHostError );
+        component->numBufs = bufInfo.fragstotal;
+
+        /* This needs to be the last ioctl call before the first read/write, according to the OSS programmer's guide */
+        ENSURE_( ioctl( component->fd, SNDCTL_DSP_GETBLKSIZE, &bytesPerBuf ), paUnanticipatedHostError );
+
+        component->hostFrames = bytesPerBuf / Pa_GetSampleSize( hostFormat ) / chans;
+        component->hostChannelCount = chans;
+        component->hostFormat = hostFormat;
+    }
+    else
+    {
+        component->hostFormat = master->hostFormat;
+        component->hostFrames = master->hostFrames;
+        component->hostChannelCount = master->hostChannelCount;
+        component->numBufs = master->numBufs;
+    }
+
+    PA_UNLESS( component->buffer = PaUtil_AllocateMemory( PaOssStreamComponent_BufferSize( component ) ),
+            paInsufficientMemory );
+
+error:
+    return result;
+}
+
+static PaError PaOssStreamComponent_Read( PaOssStreamComponent *component, unsigned long *frames )
+{
+    PaError result = paNoError;
+    size_t len = *frames * PaOssStreamComponent_FrameSize( component );
+    ssize_t bytesRead;
+
+    ENSURE_( bytesRead = read( component->fd, component->buffer, len ), paUnanticipatedHostError );
+    *frames = bytesRead / PaOssStreamComponent_FrameSize( component );
+
+error:
+    return result;
+}
+
+static PaError PaOssStreamComponent_Write( PaOssStreamComponent *component, unsigned long *frames )
+{
+    PaError result = paNoError;
+    size_t len = *frames * PaOssStreamComponent_FrameSize( component );
+    ssize_t bytesWritten;
+
+    ENSURE_( bytesWritten = write( component->fd, component->buffer, len ), paUnanticipatedHostError );
+    *frames = bytesWritten / PaOssStreamComponent_FrameSize( component );
+
+error:
+    return result;
+}
+
+/** Configure the stream according to input/output parameters.
+ *
+ * Aspect StreamChannels: The minimum number of channels supported by the device may exceed that requested by
+ * the user, if so we'll record the actual number of host channels and adapt later.
+ */
+static PaError PaOssStream_Configure( PaOssStream *stream, double sampleRate, unsigned long framesPerBuffer,
+        double *inputLatency, double *outputLatency )
+{
+    PaError result = paNoError;
+    int duplex = stream->capture && stream->playback;
+    unsigned long framesPerHostBuffer = 0;
+
+    /* We should request full duplex first thing after opening the device */
+    if( duplex && stream->sharedDevice )
+        ENSURE_( ioctl( stream->capture->fd, SNDCTL_DSP_SETDUPLEX, 0 ), paUnanticipatedHostError );
+
+    if( stream->capture )
+    {
+        PaOssStreamComponent *component = stream->capture;
+        PaOssStreamComponent_Configure( component, sampleRate, framesPerBuffer, StreamMode_In, NULL );
+
+        assert( component->hostChannelCount > 0 );
+        assert( component->hostFrames > 0 );
+
+        *inputLatency = component->hostFrames * (component->numBufs - 1) / sampleRate;
+    }
+    if( stream->playback )
+    {
+        PaOssStreamComponent *component = stream->playback, *master = stream->sharedDevice ? stream->capture : NULL;
+        PA_ENSURE( PaOssStreamComponent_Configure( component, sampleRate, framesPerBuffer, StreamMode_Out,
+                    master ) );
+
+        assert( component->hostChannelCount > 0 );
+        assert( component->hostFrames > 0 );
+
+        *outputLatency = component->hostFrames * (component->numBufs - 1) / sampleRate;
+    }
+
+    if( duplex )
+        framesPerHostBuffer = PA_MIN( stream->capture->hostFrames, stream->playback->hostFrames );
+    else if( stream->capture )
+        framesPerHostBuffer = stream->capture->hostFrames;
+    else if( stream->playback )
+        framesPerHostBuffer = stream->playback->hostFrames;
+
+    stream->framesPerHostBuffer = framesPerHostBuffer;
+    stream->pollTimeout = (int) ceil( 1e6 * framesPerHostBuffer / sampleRate );    /* Period in usecs, rounded up */
+
+    stream->streamRepresentation.streamInfo.sampleRate = sampleRate;
+
+error:
+    return result;
+}
+
+/* see pa_hostapi.h for a list of validity guarantees made about OpenStream parameters */
+
+/** Open a PA OSS stream.
+ *
+ * Aspect StreamChannels: The number of channels is specified per direction (in/out), and can differ between the
+ * two. However, OSS doesn't support separate configuration spaces for capture and playback so if both
+ * directions are the same device we will demand the same number of channels. The number of channels can range
+ * from 1 to the maximum supported by the device.
+ *
+ * Aspect BufferSettings: If framesPerBuffer != paFramesPerBufferUnspecified the number of frames per callback
+ * must reflect this, in addition the host latency per device should approximate the corresponding
+ * suggestedLatency. Based on these constraints we need to determine a number of frames per host buffer that
+ * both capture and playback can agree on (they can be different devices), the buffer processor can adapt
+ * between host and user buffer size, but the ratio should preferably be integral.
+ */
+static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
+                           PaStream** s,
+                           const PaStreamParameters *inputParameters,
+                           const PaStreamParameters *outputParameters,
+                           double sampleRate,
+                           unsigned long framesPerBuffer,
+                           PaStreamFlags streamFlags,
+                           PaStreamCallback *streamCallback,
+                           void *userData )
+{
+    PaError result = paNoError;
+    PaOSSHostApiRepresentation *ossHostApi = (PaOSSHostApiRepresentation*)hostApi;
+    PaOssStream *stream = NULL;
+    int inputChannelCount = 0, outputChannelCount = 0;
+    PaSampleFormat inputSampleFormat = 0, outputSampleFormat = 0, inputHostFormat = 0, outputHostFormat = 0;
+    const PaDeviceInfo *inputDeviceInfo = 0, *outputDeviceInfo = 0;
+    int bpInitialized = 0;
+    double inLatency, outLatency;
+
+    /* validate platform specific flags */
+    if( (streamFlags & paPlatformSpecificFlags) != 0 )
+        return paInvalidFlag; /* unexpected platform specific flag */
+
+    if( inputParameters )
+    {
+        /* unless alternate device specification is supported, reject the use of
+            paUseHostApiSpecificDeviceSpecification */
+        inputDeviceInfo = hostApi->deviceInfos[inputParameters->device];
+        PA_ENSURE( ValidateParameters( inputParameters, inputDeviceInfo, StreamMode_In ) );
+
+        inputChannelCount = inputParameters->channelCount;
+        inputSampleFormat = inputParameters->sampleFormat;
+    }
+    if( outputParameters )
+    {
+        outputDeviceInfo = hostApi->deviceInfos[outputParameters->device];
+        PA_ENSURE( ValidateParameters( outputParameters, outputDeviceInfo, StreamMode_Out ) );
+
+        outputChannelCount = outputParameters->channelCount;
+        outputSampleFormat = outputParameters->sampleFormat;
+    }
+
+    /* Aspect StreamChannels: We currently demand that number of input and output channels are the same, if the same
+     * device is opened for both directions
+     */
+    if( inputChannelCount > 0 && outputChannelCount > 0 )
+    {
+        if( inputParameters->device == outputParameters->device )
+        {
+            if( inputParameters->channelCount != outputParameters->channelCount )
+                return paInvalidChannelCount;
+        }
+    }
+    
+    /* allocate and do basic initialization of the stream structure */
+    PA_UNLESS( stream = (PaOssStream*)PaUtil_AllocateMemory( sizeof(PaOssStream) ), paInsufficientMemory );
+    PaOssStream_Initialize( stream, inputParameters, outputParameters, streamCallback, userData, streamFlags, ossHostApi );
+
+    PA_ENSURE( PaOssStream_Configure( stream, sampleRate, framesPerBuffer, &inLatency, &outLatency ) );
+
+    PaUtil_InitializeCpuLoadMeasurer( &stream->cpuLoadMeasurer, sampleRate );
+        
+    if( inputParameters )
+    {
+        inputHostFormat = stream->capture->hostFormat;
+        stream->streamRepresentation.streamInfo.inputLatency = inLatency +
+            PaUtil_GetBufferProcessorInputLatency( &stream->bufferProcessor ) / sampleRate;
+    }
+    if( outputParameters )
+    {
+        outputHostFormat = stream->playback->hostFormat;
+        stream->streamRepresentation.streamInfo.outputLatency = outLatency +
+            PaUtil_GetBufferProcessorOutputLatency( &stream->bufferProcessor ) / sampleRate;
+    }
+
+    /* Initialize buffer processor with fixed host buffer size.
+     * Aspect StreamSampleFormat: Here we commit the user and host sample formats, PA infrastructure will
+     * convert between the two.
+     */
+    PA_ENSURE( PaUtil_InitializeBufferProcessor( &stream->bufferProcessor,
+              inputChannelCount, inputSampleFormat, inputHostFormat, outputChannelCount, outputSampleFormat,
+              outputHostFormat, sampleRate, streamFlags, framesPerBuffer, stream->framesPerHostBuffer,
+              paUtilFixedHostBufferSize, streamCallback, userData ) );
+    bpInitialized = 1;
+
+    *s = (PaStream*)stream;
+
+    return result;
+
+error:
+    if( bpInitialized )
+        PaUtil_TerminateBufferProcessor( &stream->bufferProcessor );
+    if( stream )
+        PaOssStream_Terminate( stream );
+
+    return result;
+}
+
+/*! Poll on I/O filedescriptors.
+
+  Poll till we've determined there's data for read or write. In the full-duplex case,
+  we don't want to hang around forever waiting for either input or output frames, so
+  whenever we have a timed out filedescriptor we check if we're nearing under/overrun
+  for the other direction (critical limit set at one buffer). If so, we exit the waiting
+  state, and go on with what we got. We align the number of frames on a host buffer
+  boundary because it is possible that the buffer size differs for the two directions and
+  the host buffer size is a compromise between the two.
+  */
+static PaError PaOssStream_WaitForFrames( PaOssStream *stream, unsigned long *frames )
+{
+    PaError result = paNoError;
+    int pollPlayback = 0, pollCapture = 0;
+    int captureAvail = INT_MAX, playbackAvail = INT_MAX, commonAvail;
+    audio_buf_info bufInfo;
+    /* int ofs = 0, nfds = stream->nfds; */
+    fd_set readFds, writeFds;
+    int nfds = 0;
+    struct timeval selectTimeval = {0, 0};
+    unsigned long timeout = stream->pollTimeout;    /* In usecs */
+    int captureFd = -1, playbackFd = -1;
+
+    assert( stream );
+    assert( frames );
+
+    if( stream->capture )
+    {
+        pollCapture = 1;
+        captureFd = stream->capture->fd;
+        /* stream->capture->pfd->events = POLLIN; */
+    }
+    if( stream->playback )
+    {
+        pollPlayback = 1;
+        playbackFd = stream->playback->fd;
+        /* stream->playback->pfd->events = POLLOUT; */
+    }
+
+    FD_ZERO( &readFds );
+    FD_ZERO( &writeFds );
+
+    while( pollPlayback || pollCapture )
+    {
+        pthread_testcancel();
+
+        /* select may modify the timeout parameter */
+        selectTimeval.tv_usec = timeout;
+        nfds = 0;
+
+        if( pollCapture )
+        {
+            FD_SET( captureFd, &readFds );
+            nfds = captureFd + 1;
+        }
+        if( pollPlayback )
+        {
+            FD_SET( playbackFd, &writeFds );
+            nfds = PA_MAX( nfds, playbackFd + 1 );
+        }
+        ENSURE_( select( nfds, &readFds, &writeFds, NULL, &selectTimeval ), paUnanticipatedHostError );
+        /*
+        if( poll( stream->pfds + ofs, nfds, stream->pollTimeout ) < 0 )
+        {
+
+            ENSURE_( -1, paUnanticipatedHostError );
+        }
+        */
+        pthread_testcancel();
+
+        if( pollCapture )
+        {
+            if( FD_ISSET( captureFd, &readFds ) )
+            {
+                FD_CLR( captureFd, &readFds );
+                pollCapture = 0;
+            }
+            /*
+            if( stream->capture->pfd->revents & POLLIN )
+            {
+                --nfds;
+                ++ofs;
+                pollCapture = 0;
+            }
+            */
+            else if( stream->playback ) /* Timed out, go on with playback? */ 
+            {
+                /*PA_DEBUG(( "%s: Trying to poll again for capture frames, pollTimeout: %d\n",
+                            __FUNCTION__, stream->pollTimeout ));*/
+            }
+        }
+        if( pollPlayback )
+        {
+            if( FD_ISSET( playbackFd, &writeFds ) )
+            {
+                FD_CLR( playbackFd, &writeFds );
+                pollPlayback = 0;
+            }
+            /*
+            if( stream->playback->pfd->revents & POLLOUT )
+            {
+                --nfds;
+                pollPlayback = 0;
+            }
+            */
+            else if( stream->capture )  /* Timed out, go on with capture? */
+            {
+                /*PA_DEBUG(( "%s: Trying to poll again for playback frames, pollTimeout: %d\n\n",
+                            __FUNCTION__, stream->pollTimeout ));*/
+            }
+        }
+    }
+
+    if( stream->capture )
+    {
+        ENSURE_( ioctl( captureFd, SNDCTL_DSP_GETISPACE, &bufInfo ), paUnanticipatedHostError );
+        captureAvail = bufInfo.fragments * stream->capture->hostFrames;
+        if( !captureAvail )
+            PA_DEBUG(( "%s: captureAvail: 0\n", __FUNCTION__ ));
+
+        captureAvail = captureAvail == 0 ? INT_MAX : captureAvail;      /* Disregard if zero */
+    }
+    if( stream->playback )
+    {
+        ENSURE_( ioctl( playbackFd, SNDCTL_DSP_GETOSPACE, &bufInfo ), paUnanticipatedHostError );
+        playbackAvail = bufInfo.fragments * stream->playback->hostFrames;
+        if( !playbackAvail )
+        {
+            PA_DEBUG(( "%s: playbackAvail: 0\n", __FUNCTION__ ));
+        }
+
+        playbackAvail = playbackAvail == 0 ? INT_MAX : playbackAvail;      /* Disregard if zero */
+    }
+
+    commonAvail = PA_MIN( captureAvail, playbackAvail );
+    if( commonAvail == INT_MAX )
+        commonAvail = 0;
+    commonAvail -= commonAvail % stream->framesPerHostBuffer;
+
+    assert( commonAvail != INT_MAX );
+    assert( commonAvail >= 0 );
+    *frames = commonAvail;
+
+error:
+    return result;
+}
+
+/** Prepare stream for capture/playback.
+ *
+ * In order to synchronize capture and playback properly we use the SETTRIGGER command.
+ */
+static PaError PaOssStream_Prepare( PaOssStream *stream )
+{
+    PaError result = paNoError;
+    int enableBits = 0;
+
+    if( stream->triggered )
+        return result;
+
+    if( stream->playback )
+    {
+        size_t bufSz = PaOssStreamComponent_BufferSize( stream->playback );
+        memset( stream->playback->buffer, 0, bufSz );
+
+        /* Looks like we have to turn off blocking before we try this, but if we don't fill the buffer
+         * OSS will complain. */
+        PA_ENSURE( ModifyBlocking( stream->playback->fd, 0 ) );
+        while (1)
+        {
+            if( write( stream->playback->fd, stream->playback->buffer, bufSz ) < 0 )
+                break;
+        }
+        PA_ENSURE( ModifyBlocking( stream->playback->fd, 1 ) );
+    }
+
+    if( stream->sharedDevice )
+    {
+        enableBits = PCM_ENABLE_INPUT | PCM_ENABLE_OUTPUT;
+        ENSURE_( ioctl( stream->capture->fd, SNDCTL_DSP_SETTRIGGER, &enableBits ), paUnanticipatedHostError );
+    }
+    else
+    {
+        if( stream->capture )
+        {
+            enableBits = PCM_ENABLE_INPUT;
+            ENSURE_( ioctl( stream->capture->fd, SNDCTL_DSP_SETTRIGGER, &enableBits ), paUnanticipatedHostError );
+        }
+        if( stream->playback )
+        {
+            enableBits = PCM_ENABLE_OUTPUT;
+            ENSURE_( ioctl( stream->playback->fd, SNDCTL_DSP_SETTRIGGER, &enableBits ), paUnanticipatedHostError );
+        }
+    }
+
+    /* Ok, we have triggered the stream */
+    stream->triggered = 1;
+    
+error:
+    return result;
+}
+
+/** Stop audio processing
+ *
+ */
+static PaError PaOssStream_Stop( PaOssStream *stream, int abort )
+{
+    PaError result = paNoError;
+
+    /* Looks like the only safe way to stop audio without reopening the device is SNDCTL_DSP_POST.
+     * Also disable capture/playback till the stream is started again */
+    if( stream->capture )
+    {
+        ENSURE_( ioctl( stream->capture->fd, SNDCTL_DSP_POST, 0 ), paUnanticipatedHostError );
+    }
+    if( stream->playback && !stream->sharedDevice )
+    {
+        ENSURE_( ioctl( stream->playback->fd, SNDCTL_DSP_POST, 0 ), paUnanticipatedHostError );
+    }
+
+error:
+    return result;
+}
+
+/** Clean up after thread exit.
+ *
+ * Aspect StreamState: If the user has registered a streamFinishedCallback it will be called here
+ */
+static void OnExit( void *data )
+{
+    PaOssStream *stream = (PaOssStream *) data;
+    assert( data );
+
+    PaUtil_ResetCpuLoadMeasurer( &stream->cpuLoadMeasurer );
+
+    PaOssStream_Stop( stream, stream->callbackAbort );
+    
+    PA_DEBUG(( "OnExit: Stoppage\n" ));
+
+    /* Eventually notify user all buffers have played */
+    if( stream->streamRepresentation.streamFinishedCallback )
+        stream->streamRepresentation.streamFinishedCallback( stream->streamRepresentation.userData );
+
+    stream->callbackAbort = 0;      /* Clear state */
+    stream->isActive = 0;
+}
+
+static PaError SetUpBuffers( PaOssStream *stream, unsigned long framesAvail )
+{
+    PaError result = paNoError;
+
+    if( stream->capture )
+    {
+        PaUtil_SetInterleavedInputChannels( &stream->bufferProcessor, 0, stream->capture->buffer,
+                stream->capture->hostChannelCount );
+        PaUtil_SetInputFrameCount( &stream->bufferProcessor, framesAvail );
+    }
+    if( stream->playback )
+    {
+        PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor, 0, stream->playback->buffer,
+                stream->playback->hostChannelCount );
+        PaUtil_SetOutputFrameCount( &stream->bufferProcessor, framesAvail );
+    }
+
+    return result;
+}
+
+/** Thread procedure for callback processing.
+ *
+ * Aspect StreamState: StartStream will wait on this to initiate audio processing, useful in case the
+ * callback should be used for buffer priming. When the stream is cancelled a separate function will
+ * take care of the transition to the Callback Finished state (the stream isn't considered Stopped
+ * before StopStream() or AbortStream() are called).
+ */
+static void *PaOSS_AudioThreadProc( void *userData )
+{
+    PaError result = paNoError;
+    PaOssStream *stream = (PaOssStream*)userData;
+    unsigned long framesAvail, framesProcessed;
+    int callbackResult = paContinue;
+    int triggered = stream->triggered;  /* See if SNDCTL_DSP_TRIGGER has been issued already */
+    int initiateProcessing = triggered;    /* Already triggered? */
+    PaStreamCallbackFlags cbFlags = 0;  /* We might want to keep state across iterations */
+    
+    /*
+#if ( SOUND_VERSION > 0x030904 )
+        audio_errinfo errinfo;
+#endif
+*/
+    
+    assert( stream );
+
+    pthread_cleanup_push( &OnExit, stream );	/* Execute OnExit when exiting */
+
+    /* The first time the stream is started we use SNDCTL_DSP_TRIGGER to accurately start capture and
+     * playback in sync, when the stream is restarted after being stopped we simply start by reading/
+     * writing.
+     */
+    PA_ENSURE( PaOssStream_Prepare( stream ) );
+
+    /* If we are to initiate processing implicitly by reading/writing data, we start off in blocking mode */
+    if( initiateProcessing )
+    {
+        /* Make sure devices are in blocking mode */
+        if( stream->capture )
+            ModifyBlocking( stream->capture->fd, 1 );
+        if( stream->playback )
+            ModifyBlocking( stream->playback->fd, 1 );
+    }
+
+    while( 1 )
+    {
+        PaStreamCallbackTimeInfo timeInfo = {0,0,0}; /* TODO: IMPLEMENT ME */
+
+        pthread_testcancel();
+
+        if( stream->callbackStop && callbackResult == paContinue )
+        {
+            PA_DEBUG(( "Setting callbackResult to paComplete\n" ));
+            callbackResult = paComplete;
+        }
+
+        /* Aspect StreamState: Because of the messy OSS scheme we can't explicitly trigger device start unless
+         * the stream has been recently started, we will have to go right ahead and read/write in blocking
+         * fashion to trigger operation. Therefore we begin with processing one host buffer before we switch
+         * to non-blocking mode.
+         */
+        if( !initiateProcessing )
+        {
+            PA_ENSURE( PaOssStream_WaitForFrames( stream, &framesAvail ) );  /* Wait on available frames */
+            assert( framesAvail % stream->framesPerHostBuffer == 0 );
+        }
+        else
+        {
+            framesAvail = stream->framesPerHostBuffer;
+        }
+
+        while( framesAvail > 0 )
+        {
+            unsigned long frames = framesAvail;
+
+            pthread_testcancel();
+
+            PaUtil_BeginCpuLoadMeasurement( &stream->cpuLoadMeasurer );
+
+            /* Read data */
+            if ( stream->capture )
+            {
+                PA_ENSURE( PaOssStreamComponent_Read( stream->capture, &frames ) );
+                assert( frames == framesAvail );
+            }
+
+#if ( SOUND_VERSION >= 0x030904 )
+            /*
+               Check with OSS to see if there have been any under/overruns
+               since last time we checked.
+               */
+            /*
+            if( ioctl( stream->deviceHandle, SNDCTL_DSP_GETERROR, &errinfo ) >= 0 )
+            {
+                if( errinfo.play_underruns )
+                    cbFlags |= paOutputUnderflow ;
+                if( errinfo.record_underruns )
+                    cbFlags |= paInputUnderflow ;
+            }
+            else
+                PA_DEBUG(( "SNDCTL_DSP_GETERROR command failed: %s\n", strerror( errno ) ));
+                */
+#endif
+
+            PaUtil_BeginBufferProcessing( &stream->bufferProcessor, &timeInfo,
+                    cbFlags );
+            cbFlags = 0;
+            PA_ENSURE( SetUpBuffers( stream, framesAvail ) );
+
+            framesProcessed = PaUtil_EndBufferProcessing( &stream->bufferProcessor,
+                    &callbackResult );
+            assert( framesProcessed == framesAvail );
+            PaUtil_EndCpuLoadMeasurement( &stream->cpuLoadMeasurer, framesProcessed );
+
+            if ( stream->playback )
+            {
+                frames = framesAvail;
+
+                PA_ENSURE( PaOssStreamComponent_Write( stream->playback, &frames ) );
+                assert( frames == framesAvail );
+
+                /* TODO: handle bytesWritten != bytesRequested (slippage?) */
+            }
+
+            framesAvail -= framesProcessed;
+            stream->framesProcessed += framesProcessed;
+
+            if( callbackResult != paContinue )
+                break;
+        }
+
+        if( initiateProcessing || !triggered )
+        {
+            /* Non-blocking */
+            if( stream->capture )
+                PA_ENSURE( ModifyBlocking( stream->capture->fd, 0 ) );
+            if( stream->playback && !stream->sharedDevice )
+                PA_ENSURE( ModifyBlocking( stream->playback->fd, 0 ) );
+
+            initiateProcessing = 0;
+            sem_post( &stream->semaphore );
+        }
+
+        if( callbackResult != paContinue )
+        {
+            stream->callbackAbort = callbackResult == paAbort;
+            if( stream->callbackAbort || PaUtil_IsBufferProcessorOutputEmpty( &stream->bufferProcessor ) )
+                break;
+        }
+    }
+
+    pthread_cleanup_pop( 1 );
+
+error:
+    pthread_exit( NULL );
+}
+
+/** Close the stream.
+ *
+ */
+static PaError CloseStream( PaStream* s )
+{
+    PaError result = paNoError;
+    PaOssStream *stream = (PaOssStream*)s;
+
+    assert( stream );
+
+    PaUtil_TerminateBufferProcessor( &stream->bufferProcessor );
+    PaOssStream_Terminate( stream );
+
+    return result;
+}
+
+/** Start the stream.
+ *
+ * Aspect StreamState: After returning, the stream shall be in the Active state, implying that an eventual
+ * callback will be repeatedly called in a separate thread. If a separate thread is started this function
+ * will block untill it has started processing audio, otherwise audio processing is started directly.
+ */
+static PaError StartStream( PaStream *s )
+{
+    PaError result = paNoError;
+    PaOssStream *stream = (PaOssStream*)s;
+
+    stream->isActive = 1;
+    stream->isStopped = 0;
+    stream->lastPosPtr = 0;
+    stream->lastStreamBytes = 0;
+    stream->framesProcessed = 0;
+
+    /* only use the thread for callback streams */
+    if( stream->bufferProcessor.streamCallback )
+    {
+        PA_ENSURE( PaUtil_StartThreading( &stream->threading, &PaOSS_AudioThreadProc, stream ) );
+        sem_wait( &stream->semaphore );
+    }
+    else
+        PA_ENSURE( PaOssStream_Prepare( stream ) );
+
+error:
+    return result;
+}
+
+static PaError RealStop( PaOssStream *stream, int abort )
+{
+    PaError result = paNoError;
+
+    if( stream->callbackMode )
+    {
+        if( abort )
+            stream->callbackAbort = 1;
+        else
+            stream->callbackStop = 1;
+
+        PA_ENSURE( PaUtil_CancelThreading( &stream->threading, !abort, NULL ) );
+
+        stream->callbackStop = stream->callbackAbort = 0;
+    }
+    else
+        PA_ENSURE( PaOssStream_Stop( stream, abort ) );
+
+    stream->isStopped = 1;
+
+error:
+    return result;
+}
+
+/** Stop the stream.
+ *
+ * Aspect StreamState: This will cause the stream to transition to the Stopped state, playing all enqueued
+ * buffers.
+ */
+static PaError StopStream( PaStream *s )
+{
+    return RealStop( (PaOssStream *)s, 0 );
+}
+
+/** Abort the stream.
+ *
+ * Aspect StreamState: This will cause the stream to transition to the Stopped state, discarding all enqueued
+ * buffers. Note that the buffers are not currently correctly discarded, this is difficult without closing
+ * the OSS device.
+ */
+static PaError AbortStream( PaStream *s )
+{
+    return RealStop( (PaOssStream *)s, 1 );
+}
+
+/** Is the stream in the Stopped state.
+ *
+ */
+static PaError IsStreamStopped( PaStream *s )
+{
+    PaOssStream *stream = (PaOssStream*)s;
+
+    return (stream->isStopped);
+}
+
+/** Is the stream in the Active state.
+ *
+ */
+static PaError IsStreamActive( PaStream *s )
+{
+    PaOssStream *stream = (PaOssStream*)s;
+
+    return (stream->isActive);
+}
+
+static PaTime GetStreamTime( PaStream *s )
+{
+    PaOssStream *stream = (PaOssStream*)s;
+    count_info info;
+    int delta;
+
+    if( stream->playback ) {
+        if( ioctl( stream->playback->fd, SNDCTL_DSP_GETOPTR, &info) == 0 ) {
+            delta = ( info.bytes - stream->lastPosPtr ) & 0x000FFFFF;
+            return ( stream->lastStreamBytes + delta) / PaOssStreamComponent_FrameSize( stream->playback ) / stream->sampleRate;
+        }
+    }
+    else {
+        if (ioctl( stream->capture->fd, SNDCTL_DSP_GETIPTR, &info) == 0) {
+            delta = (info.bytes - stream->lastPosPtr) & 0x000FFFFF;
+            return ( stream->lastStreamBytes + delta) / PaOssStreamComponent_FrameSize( stream->capture ) / stream->sampleRate;
+        }
+    }
+
+    /* the ioctl failed, but we can still give a coarse estimate */
+
+    return stream->framesProcessed / stream->sampleRate;
+}
+
+
+static double GetStreamCpuLoad( PaStream* s )
+{
+    PaOssStream *stream = (PaOssStream*)s;
+
+    return PaUtil_GetCpuLoad( &stream->cpuLoadMeasurer );
+}
+
+
+/*
+    As separate stream interfaces are used for blocking and callback
+    streams, the following functions can be guaranteed to only be called
+    for blocking streams.
+*/
+
+
+static PaError ReadStream( PaStream* s,
+                           void *buffer,
+                           unsigned long frames )
+{
+    PaOssStream *stream = (PaOssStream*)s;
+    int bytesRequested, bytesRead;
+    unsigned long framesRequested;
+    void *userBuffer;
+
+    /* If user input is non-interleaved, PaUtil_CopyInput will manipulate the channel pointers,
+     * so we copy the user provided pointers */
+    if( stream->bufferProcessor.userInputIsInterleaved )
+        userBuffer = buffer;
+    else /* Copy channels into local array */
+    {
+        userBuffer = stream->capture->userBuffers;
+        memcpy( (void *)userBuffer, buffer, sizeof (void *) * stream->capture->userChannelCount );
+    }
+
+    while( frames )
+    {
+        framesRequested = PA_MIN( frames, stream->capture->hostFrames );
+
+	bytesRequested = framesRequested * PaOssStreamComponent_FrameSize( stream->capture );
+	bytesRead = read( stream->capture->fd, stream->capture->buffer, bytesRequested );
+	if ( bytesRequested != bytesRead )
+	    return paUnanticipatedHostError;
+
+	PaUtil_SetInputFrameCount( &stream->bufferProcessor, stream->capture->hostFrames );
+	PaUtil_SetInterleavedInputChannels( &stream->bufferProcessor, 0, stream->capture->buffer, stream->capture->hostChannelCount );
+        PaUtil_CopyInput( &stream->bufferProcessor, &userBuffer, framesRequested );
+	frames -= framesRequested;
+    }
+    return paNoError;
+}
+
+
+static PaError WriteStream( PaStream* s,
+                            const void *buffer,
+                            unsigned long frames )
+{
+    PaOssStream *stream = (PaOssStream*)s;
+    int bytesRequested, bytesWritten;
+    unsigned long framesConverted;
+    const void *userBuffer;
+
+    /* If user output is non-interleaved, PaUtil_CopyOutput will manipulate the channel pointers,
+     * so we copy the user provided pointers */
+    if( stream->bufferProcessor.userOutputIsInterleaved )
+        userBuffer = buffer;
+    else /* Copy channels into local array */
+    {
+        userBuffer = stream->playback->userBuffers;
+        memcpy( (void *)userBuffer, buffer, sizeof (void *) * stream->playback->userChannelCount );
+    }
+
+    while( frames )
+    {
+	PaUtil_SetOutputFrameCount( &stream->bufferProcessor, stream->playback->hostFrames );
+	PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor, 0, stream->playback->buffer, stream->playback->hostChannelCount );
+
+	framesConverted = PaUtil_CopyOutput( &stream->bufferProcessor, &userBuffer, frames );
+	frames -= framesConverted;
+
+	bytesRequested = framesConverted * PaOssStreamComponent_FrameSize( stream->playback );
+	bytesWritten = write( stream->playback->fd, stream->playback->buffer, bytesRequested );
+
+	if ( bytesRequested != bytesWritten )
+	    return paUnanticipatedHostError;
+    }
+    return paNoError;
+}
+
+
+static signed long GetStreamReadAvailable( PaStream* s )
+{
+    PaOssStream *stream = (PaOssStream*)s;
+    audio_buf_info info;
+
+    if( ioctl( stream->capture->fd, SNDCTL_DSP_GETISPACE, &info ) < 0 )
+        return paUnanticipatedHostError;
+    return info.fragments * stream->capture->hostFrames;
+}
+
+
+/* TODO: Compute number of allocated bytes somewhere else, can we use ODELAY with capture */
+static signed long GetStreamWriteAvailable( PaStream* s )
+{
+    PaOssStream *stream = (PaOssStream*)s;
+    int delay = 0;
+
+    if( ioctl( stream->playback->fd, SNDCTL_DSP_GETODELAY, &delay ) < 0 )
+        return paUnanticipatedHostError;
+    
+    return (PaOssStreamComponent_BufferSize( stream->playback ) - delay) / PaOssStreamComponent_FrameSize( stream->playback );
+}
+
diff --git a/src/audio/portaudio/pa_unix_oss/recplay.c b/src/audio/portaudio/pa_unix_oss/recplay.c
new file mode 100644
index 0000000000000000000000000000000000000000..9d4c78cfa3073a7367c7d3d24dcc5db63e556728
--- /dev/null
+++ b/src/audio/portaudio/pa_unix_oss/recplay.c
@@ -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/src/audio/portaudio/pa_win/dev-cpp/Makefile-dll b/src/audio/portaudio/pa_win/dev-cpp/Makefile-dll
new file mode 100644
index 0000000000000000000000000000000000000000..856af638e304b0913f369f6b2ec2fb5901dda4d1
--- /dev/null
+++ b/src/audio/portaudio/pa_win/dev-cpp/Makefile-dll
@@ -0,0 +1,78 @@
+# Project: portaudio-dll
+# Makefile created by Dev-C++ 4.9.8.2
+
+CPP  = g++.exe
+CC   = gcc.exe
+WINDRES = windres.exe
+RES  = 
+OBJ  = ./pa_skeleton.o ./pa_stream.o ./pa_trace.o ./pa_allocation.o ./pa_converters.o ./pa_cpuload.o ./pa_dither.o ./pa_front.o ./pa_process.o ./pa_asio.o ./pa_win_util.o ./pa_win_hostapis.o ./pa_win_ds.o ./dsound_wrapper.o ./pa_win_wmme.o ./iasiothiscallresolver.o $(RES)
+LINKOBJ  = ./pa_skeleton.o ./pa_stream.o ./pa_trace.o ./pa_allocation.o ./pa_converters.o ./pa_cpuload.o ./pa_dither.o ./pa_front.o ./pa_process.o ./pa_asio.o ./pa_win_util.o ./pa_win_hostapis.o ./pa_win_ds.o ./dsound_wrapper.o ./pa_win_wmme.o ./iasiothiscallresolver.o $(RES)
+LIBS =  -L"C:/Dev-CPP/lib" -fmessage-length=0 --no-export-all-symbols --add-stdcall-alias ../../../asiosdk2/asiosdk2.a -lkernel32 -luser32 -lgdi32 -lwinspool -lcomdlg32 -ladvapi32 -lshell32 -lole32 -loleaut32 -luuid -lodbc32 -lodbccp32 -lwinmm -O3 -s 
+INCS =  -I"C:/Dev-CPP/include"  -I"../../../asiosdk2"  -I"../../../asiosdk2/common"  -I"../../../asiosdk2/host"  -I"../../../asiosdk2/host/pc"  -I"../../pa_common" 
+CXXINCS =  -I"C:/Dev-CPP/include/c++"  -I"C:/Dev-CPP/include/c++/mingw32"  -I"C:/Dev-CPP/include/c++/backward"  -I"C:/Dev-CPP/include"  -I"../../../asiosdk2"  -I"../../../asiosdk2/common"  -I"../../../asiosdk2/host"  -I"../../../asiosdk2/host/pc"  -I"../../pa_common" 
+BIN  = portaudio-dll.dll
+CXXFLAGS = $(CXXINCS)-O3   -fmessage-length=0 -Wall
+CFLAGS = $(INCS)-DBUILDING_DLL=1 -O3   -fmessage-length=0 -Wall
+
+.PHONY: all all-before all-after clean clean-custom
+
+all: all-before portaudio-dll.dll all-after
+
+
+clean: clean-custom
+	rm -f $(OBJ) $(BIN)
+
+DLLWRAP=dllwrap.exe
+DEFFILE=libportaudio-dll.def
+STATICLIB=libportaudio-dll.a
+
+$(BIN): $(LINKOBJ)
+	$(DLLWRAP) --output-def $(DEFFILE) --driver-name c++ --implib $(STATICLIB) $(LINKOBJ) $(LIBS) -o $(BIN)
+
+./pa_skeleton.o: ../../pa_common/pa_skeleton.c
+	$(CPP) -c ../../pa_common/pa_skeleton.c -o ./pa_skeleton.o $(CXXFLAGS)
+
+./pa_stream.o: ../../pa_common/pa_stream.c
+	$(CPP) -c ../../pa_common/pa_stream.c -o ./pa_stream.o $(CXXFLAGS)
+
+./pa_trace.o: ../../pa_common/pa_trace.c
+	$(CPP) -c ../../pa_common/pa_trace.c -o ./pa_trace.o $(CXXFLAGS)
+
+./pa_allocation.o: ../../pa_common/pa_allocation.c
+	$(CPP) -c ../../pa_common/pa_allocation.c -o ./pa_allocation.o $(CXXFLAGS)
+
+./pa_converters.o: ../../pa_common/pa_converters.c
+	$(CPP) -c ../../pa_common/pa_converters.c -o ./pa_converters.o $(CXXFLAGS)
+
+./pa_cpuload.o: ../../pa_common/pa_cpuload.c
+	$(CPP) -c ../../pa_common/pa_cpuload.c -o ./pa_cpuload.o $(CXXFLAGS)
+
+./pa_dither.o: ../../pa_common/pa_dither.c
+	$(CPP) -c ../../pa_common/pa_dither.c -o ./pa_dither.o $(CXXFLAGS)
+
+./pa_front.o: ../../pa_common/pa_front.c
+	$(CPP) -c ../../pa_common/pa_front.c -o ./pa_front.o $(CXXFLAGS)
+
+./pa_process.o: ../../pa_common/pa_process.c
+	$(CPP) -c ../../pa_common/pa_process.c -o ./pa_process.o $(CXXFLAGS)
+
+./pa_asio.o: ../../pa_asio/pa_asio.cpp
+	$(CPP) -c ../../pa_asio/pa_asio.cpp -o ./pa_asio.o $(CXXFLAGS)
+
+./pa_win_util.o: ../pa_win_util.c
+	$(CPP) -c ../pa_win_util.c -o ./pa_win_util.o $(CXXFLAGS)
+
+./pa_win_hostapis.o: ../pa_win_hostapis.c
+	$(CPP) -c ../pa_win_hostapis.c -o ./pa_win_hostapis.o $(CXXFLAGS)
+
+./pa_win_ds.o: ../../pa_win_ds/pa_win_ds.c
+	$(CPP) -c ../../pa_win_ds/pa_win_ds.c -o ./pa_win_ds.o $(CXXFLAGS)
+
+./dsound_wrapper.o: ../../pa_win_ds/dsound_wrapper.c
+	$(CPP) -c ../../pa_win_ds/dsound_wrapper.c -o ./dsound_wrapper.o $(CXXFLAGS)
+
+./pa_win_wmme.o: ../../pa_win_wmme/pa_win_wmme.c
+	$(CPP) -c ../../pa_win_wmme/pa_win_wmme.c -o ./pa_win_wmme.o $(CXXFLAGS)
+
+./iasiothiscallresolver.o: ../../pa_asio/iasiothiscallresolver.cpp
+	$(CPP) -c ../../pa_asio/iasiothiscallresolver.cpp -o ./iasiothiscallresolver.o $(CXXFLAGS)
diff --git a/src/audio/portaudio/pa_win/dev-cpp/Makefile-static b/src/audio/portaudio/pa_win/dev-cpp/Makefile-static
new file mode 100644
index 0000000000000000000000000000000000000000..2a1647ddc5f3a60f24312d60168da26357965e50
--- /dev/null
+++ b/src/audio/portaudio/pa_win/dev-cpp/Makefile-static
@@ -0,0 +1,75 @@
+# Project: portaudio-static
+# Makefile created by Dev-C++ 4.9.8.2
+
+CPP  = g++.exe
+CC   = gcc.exe
+WINDRES = windres.exe
+RES  = 
+OBJ  = ./pa_skeleton.o ./pa_stream.o ./pa_trace.o ./pa_allocation.o ./pa_converters.o ./pa_cpuload.o ./pa_dither.o ./pa_front.o ./pa_process.o ./pa_asio.o ./pa_win_util.o ./pa_win_hostapis.o ./pa_win_ds.o ./dsound_wrapper.o ./pa_win_wmme.o ./iasiothiscallresolver.o $(RES)
+LINKOBJ  = ./pa_skeleton.o ./pa_stream.o ./pa_trace.o ./pa_allocation.o ./pa_converters.o ./pa_cpuload.o ./pa_dither.o ./pa_front.o ./pa_process.o ./pa_asio.o ./pa_win_util.o ./pa_win_hostapis.o ./pa_win_ds.o ./dsound_wrapper.o ./pa_win_wmme.o ./iasiothiscallresolver.o $(RES)
+LIBS =  -L"C:/Dev-CPP/lib" -fmessage-length=0 -O3 -s 
+INCS =  -I"C:/Dev-CPP/include"  -I"../../../asiosdk2"  -I"../../../asiosdk2/common"  -I"../../../asiosdk2/host"  -I"../../../asiosdk2/host/pc"  -I"../../pa_common" 
+CXXINCS =  -I"C:/Dev-CPP/include/c++"  -I"C:/Dev-CPP/include/c++/mingw32"  -I"C:/Dev-CPP/include/c++/backward"  -I"C:/Dev-CPP/include"  -I"../../../asiosdk2"  -I"../../../asiosdk2/common"  -I"../../../asiosdk2/host"  -I"../../../asiosdk2/host/pc"  -I"../../pa_common" 
+BIN  = portaudio-static.a
+CXXFLAGS = $(CXXINCS)-O3   -fmessage-length=0 -Wall
+CFLAGS = $(INCS)-O3   -fmessage-length=0 -Wall
+
+.PHONY: all all-before all-after clean clean-custom
+
+all: all-before portaudio-static.a all-after
+
+
+clean: clean-custom
+	rm -f $(OBJ) $(BIN)
+
+$(BIN): $(LINKOBJ)
+	ar r $(BIN) $(LINKOBJ)
+	ranlib $(BIN)
+
+./pa_skeleton.o: ../../pa_common/pa_skeleton.c
+	$(CPP) -c ../../pa_common/pa_skeleton.c -o ./pa_skeleton.o $(CXXFLAGS)
+
+./pa_stream.o: ../../pa_common/pa_stream.c
+	$(CPP) -c ../../pa_common/pa_stream.c -o ./pa_stream.o $(CXXFLAGS)
+
+./pa_trace.o: ../../pa_common/pa_trace.c
+	$(CPP) -c ../../pa_common/pa_trace.c -o ./pa_trace.o $(CXXFLAGS)
+
+./pa_allocation.o: ../../pa_common/pa_allocation.c
+	$(CPP) -c ../../pa_common/pa_allocation.c -o ./pa_allocation.o $(CXXFLAGS)
+
+./pa_converters.o: ../../pa_common/pa_converters.c
+	$(CPP) -c ../../pa_common/pa_converters.c -o ./pa_converters.o $(CXXFLAGS)
+
+./pa_cpuload.o: ../../pa_common/pa_cpuload.c
+	$(CPP) -c ../../pa_common/pa_cpuload.c -o ./pa_cpuload.o $(CXXFLAGS)
+
+./pa_dither.o: ../../pa_common/pa_dither.c
+	$(CPP) -c ../../pa_common/pa_dither.c -o ./pa_dither.o $(CXXFLAGS)
+
+./pa_front.o: ../../pa_common/pa_front.c
+	$(CPP) -c ../../pa_common/pa_front.c -o ./pa_front.o $(CXXFLAGS)
+
+./pa_process.o: ../../pa_common/pa_process.c
+	$(CPP) -c ../../pa_common/pa_process.c -o ./pa_process.o $(CXXFLAGS)
+
+./pa_asio.o: ../../pa_asio/pa_asio.cpp
+	$(CPP) -c ../../pa_asio/pa_asio.cpp -o ./pa_asio.o $(CXXFLAGS)
+
+./pa_win_util.o: ../pa_win_util.c
+	$(CPP) -c ../pa_win_util.c -o ./pa_win_util.o $(CXXFLAGS)
+
+./pa_win_hostapis.o: ../pa_win_hostapis.c
+	$(CPP) -c ../pa_win_hostapis.c -o ./pa_win_hostapis.o $(CXXFLAGS)
+
+./pa_win_ds.o: ../../pa_win_ds/pa_win_ds.c
+	$(CPP) -c ../../pa_win_ds/pa_win_ds.c -o ./pa_win_ds.o $(CXXFLAGS)
+
+./dsound_wrapper.o: ../../pa_win_ds/dsound_wrapper.c
+	$(CPP) -c ../../pa_win_ds/dsound_wrapper.c -o ./dsound_wrapper.o $(CXXFLAGS)
+
+./pa_win_wmme.o: ../../pa_win_wmme/pa_win_wmme.c
+	$(CPP) -c ../../pa_win_wmme/pa_win_wmme.c -o ./pa_win_wmme.o $(CXXFLAGS)
+
+./iasiothiscallresolver.o: ../../pa_asio/iasiothiscallresolver.cpp
+	$(CPP) -c ../../pa_asio/iasiothiscallresolver.cpp -o ./iasiothiscallresolver.o $(CXXFLAGS)
diff --git a/src/audio/portaudio/pa_win/dev-cpp/portaudio-dll.dev b/src/audio/portaudio/pa_win/dev-cpp/portaudio-dll.dev
new file mode 100644
index 0000000000000000000000000000000000000000..086e109eef88fc09f125091a4908bca902b87d23
--- /dev/null
+++ b/src/audio/portaudio/pa_win/dev-cpp/portaudio-dll.dev
@@ -0,0 +1,209 @@
+[Project]
+FileName=portaudio-dll.dev
+Name=portaudio-dll
+UnitCount=16
+Type=3
+Ver=1
+ObjFiles=
+Includes=..\..\..\asiosdk2;..\..\..\asiosdk2\common;..\..\..\asiosdk2\host;..\..\..\asiosdk2\host\pc;..\..\pa_common
+Libs=
+PrivateResource=
+ResourceIncludes=
+MakeIncludes=
+Compiler=-DBUILDING_DLL=1_@@_-O3_@@_
+CppCompiler=-O3_@@_
+Linker=--no-export-all-symbols --add-stdcall-alias_@@_../../../asiosdk2/asiosdk2.a_@@_-lkernel32 -luser32 -lgdi32 -lwinspool -lcomdlg32 -ladvapi32 -lshell32 -lole32 -loleaut32 -luuid -lodbc32 -lodbccp32 -lwinmm_@@_-O3 -s_@@_
+IsCpp=1
+Icon=
+ExeOutput=.
+ObjectOutput=.
+OverrideOutput=0
+OverrideOutputName=portaudio.a
+HostApplication=
+Folders=
+CommandLine=
+IncludeVersionInfo=0
+SupportXPThemes=0
+CompilerSet=0
+CompilerSettings=0000000000000000000
+UseCustomMakefile=0
+CustomMakefile=
+
+[Unit1]
+FileName=..\..\pa_common\pa_skeleton.c
+CompileCpp=1
+Folder=portaudio
+Compile=1
+Link=1
+Priority=1000
+OverrideBuildCmd=0
+BuildCmd=$(CC) -c pa_skeleton.c -o ./pa_skeleton.o $(CFLAGS)
+
+[Unit2]
+FileName=..\..\pa_common\pa_stream.c
+CompileCpp=1
+Folder=portaudio
+Compile=1
+Link=1
+Priority=1000
+OverrideBuildCmd=0
+BuildCmd=$(CC) -c pa_stream.c -o ./pa_stream.o $(CFLAGS)
+
+[Unit3]
+FileName=..\..\pa_common\pa_trace.c
+CompileCpp=1
+Folder=portaudio
+Compile=1
+Link=1
+Priority=1000
+OverrideBuildCmd=0
+BuildCmd=$(CC) -c pa_trace.c -o ./pa_trace.o $(CFLAGS)
+
+[Unit4]
+FileName=..\..\pa_common\pa_allocation.c
+CompileCpp=1
+Folder=portaudio
+Compile=1
+Link=1
+Priority=1000
+OverrideBuildCmd=0
+BuildCmd=$(CC) -c pa_allocation.c -o ./pa_allocation.o $(CFLAGS)
+
+[Unit5]
+FileName=..\..\pa_common\pa_converters.c
+CompileCpp=1
+Folder=portaudio
+Compile=1
+Link=1
+Priority=1000
+OverrideBuildCmd=0
+BuildCmd=$(CC) -c pa_converters.c -o ./pa_converters.o $(CFLAGS)
+
+[Unit6]
+FileName=..\..\pa_common\pa_cpuload.c
+CompileCpp=1
+Folder=portaudio
+Compile=1
+Link=1
+Priority=1000
+OverrideBuildCmd=0
+BuildCmd=$(CC) -c pa_cpuload.c -o ./pa_cpuload.o $(CFLAGS)
+
+[Unit7]
+FileName=..\..\pa_common\pa_dither.c
+CompileCpp=1
+Folder=portaudio
+Compile=1
+Link=1
+Priority=1000
+OverrideBuildCmd=0
+BuildCmd=$(CC) -c pa_dither.c -o ./pa_dither.o $(CFLAGS)
+
+[Unit8]
+FileName=..\..\pa_common\pa_front.c
+CompileCpp=1
+Folder=portaudio
+Compile=1
+Link=1
+Priority=1000
+OverrideBuildCmd=0
+BuildCmd=$(CC) -c pa_front.c -o ./pa_front.o $(CFLAGS)
+
+[Unit9]
+FileName=..\..\pa_common\pa_process.c
+CompileCpp=1
+Folder=portaudio
+Compile=1
+Link=1
+Priority=1000
+OverrideBuildCmd=0
+BuildCmd=$(CC) -c pa_process.c -o ./pa_process.o $(CFLAGS)
+
+[VersionInfo]
+Major=0
+Minor=1
+Release=1
+Build=1
+LanguageID=1033
+CharsetID=1252
+CompanyName=
+FileVersion=
+FileDescription=Developed using the Dev-C++ IDE
+InternalName=
+LegalCopyright=
+LegalTrademarks=
+OriginalFilename=
+ProductName=
+ProductVersion=
+AutoIncBuildNr=0
+
+[Unit10]
+FileName=..\..\pa_asio\pa_asio.cpp
+CompileCpp=1
+Folder=portaudio
+Compile=1
+Link=1
+Priority=1000
+OverrideBuildCmd=0
+BuildCmd=$(CPP) -c pa_asio.cpp -o ./pa_asio.o $(CXXFLAGS)
+
+[Unit11]
+FileName=..\pa_win_util.c
+CompileCpp=1
+Folder=portaudio
+Compile=1
+Link=1
+Priority=1000
+OverrideBuildCmd=0
+BuildCmd=$(CC) -c pa_win_util.c -o ./pa_win_util.o $(CFLAGS)
+
+[Unit12]
+FileName=..\pa_win_hostapis.c
+CompileCpp=1
+Folder=portaudio
+Compile=1
+Link=1
+Priority=1000
+OverrideBuildCmd=0
+BuildCmd=$(CC) -c pa_win_hostapis.c -o ./pa_win_hostapis.o $(CFLAGS)
+
+[Unit13]
+FileName=..\..\pa_win_ds\pa_win_ds.c
+CompileCpp=1
+Folder=portaudio
+Compile=1
+Link=1
+Priority=1000
+OverrideBuildCmd=0
+BuildCmd=$(CC) -c pa_win_ds.c -o ./pa_win_ds.o $(CFLAGS)
+
+[Unit14]
+FileName=..\..\pa_win_ds\dsound_wrapper.c
+CompileCpp=1
+Folder=portaudio
+Compile=1
+Link=1
+Priority=1000
+OverrideBuildCmd=0
+BuildCmd=$(CC) -c dsound_wrapper.c -o ./dsound_wrapper.o $(CFLAGS)
+
+[Unit15]
+FileName=..\..\pa_win_wmme\pa_win_wmme.c
+CompileCpp=1
+Folder=portaudio
+Compile=1
+Link=1
+Priority=1000
+OverrideBuildCmd=0
+BuildCmd=$(CC) -c pa_win_wmme.c -o ./pa_win_wmme.o $(CFLAGS)
+
+[Unit16]
+FileName=..\..\pa_asio\iasiothiscallresolver.cpp
+CompileCpp=1
+Folder=portaudio
+Compile=1
+Link=1
+Priority=1000
+OverrideBuildCmd=0
+BuildCmd=
+
diff --git a/src/audio/portaudio/pa_win/dev-cpp/portaudio-static.dev b/src/audio/portaudio/pa_win/dev-cpp/portaudio-static.dev
new file mode 100644
index 0000000000000000000000000000000000000000..2aae584df4a7a706f526a9296fcafe6783cd646c
--- /dev/null
+++ b/src/audio/portaudio/pa_win/dev-cpp/portaudio-static.dev
@@ -0,0 +1,209 @@
+[Project]
+FileName=portaudio-static.dev
+Name=portaudio-static
+UnitCount=16
+Type=2
+Ver=1
+ObjFiles=
+Includes=..\..\..\asiosdk2;..\..\..\asiosdk2\common;..\..\..\asiosdk2\host;..\..\..\asiosdk2\host\pc;..\..\pa_common
+Libs=
+PrivateResource=
+ResourceIncludes=
+MakeIncludes=
+Compiler=-O3_@@_
+CppCompiler=-O3_@@_
+Linker=-O3 -s_@@_
+IsCpp=1
+Icon=
+ExeOutput=.
+ObjectOutput=.
+OverrideOutput=0
+OverrideOutputName=portaudio.a
+HostApplication=
+Folders=
+CommandLine=
+IncludeVersionInfo=0
+SupportXPThemes=0
+CompilerSet=0
+CompilerSettings=0000000000000000000
+UseCustomMakefile=0
+CustomMakefile=
+
+[Unit1]
+FileName=..\..\pa_common\pa_skeleton.c
+CompileCpp=1
+Folder=portaudio
+Compile=1
+Link=1
+Priority=1000
+OverrideBuildCmd=0
+BuildCmd=$(CC) -c pa_skeleton.c -o ./pa_skeleton.o $(CFLAGS)
+
+[Unit2]
+FileName=..\..\pa_common\pa_stream.c
+CompileCpp=1
+Folder=portaudio
+Compile=1
+Link=1
+Priority=1000
+OverrideBuildCmd=0
+BuildCmd=$(CC) -c pa_stream.c -o ./pa_stream.o $(CFLAGS)
+
+[Unit3]
+FileName=..\..\pa_common\pa_trace.c
+CompileCpp=1
+Folder=portaudio
+Compile=1
+Link=1
+Priority=1000
+OverrideBuildCmd=0
+BuildCmd=$(CC) -c pa_trace.c -o ./pa_trace.o $(CFLAGS)
+
+[Unit4]
+FileName=..\..\pa_common\pa_allocation.c
+CompileCpp=1
+Folder=portaudio
+Compile=1
+Link=1
+Priority=1000
+OverrideBuildCmd=0
+BuildCmd=$(CC) -c pa_allocation.c -o ./pa_allocation.o $(CFLAGS)
+
+[Unit5]
+FileName=..\..\pa_common\pa_converters.c
+CompileCpp=1
+Folder=portaudio
+Compile=1
+Link=1
+Priority=1000
+OverrideBuildCmd=0
+BuildCmd=$(CC) -c pa_converters.c -o ./pa_converters.o $(CFLAGS)
+
+[Unit6]
+FileName=..\..\pa_common\pa_cpuload.c
+CompileCpp=1
+Folder=portaudio
+Compile=1
+Link=1
+Priority=1000
+OverrideBuildCmd=0
+BuildCmd=$(CC) -c pa_cpuload.c -o ./pa_cpuload.o $(CFLAGS)
+
+[Unit7]
+FileName=..\..\pa_common\pa_dither.c
+CompileCpp=1
+Folder=portaudio
+Compile=1
+Link=1
+Priority=1000
+OverrideBuildCmd=0
+BuildCmd=$(CC) -c pa_dither.c -o ./pa_dither.o $(CFLAGS)
+
+[Unit8]
+FileName=..\..\pa_common\pa_front.c
+CompileCpp=1
+Folder=portaudio
+Compile=1
+Link=1
+Priority=1000
+OverrideBuildCmd=0
+BuildCmd=$(CC) -c pa_front.c -o ./pa_front.o $(CFLAGS)
+
+[Unit9]
+FileName=..\..\pa_common\pa_process.c
+CompileCpp=1
+Folder=portaudio
+Compile=1
+Link=1
+Priority=1000
+OverrideBuildCmd=0
+BuildCmd=$(CC) -c pa_process.c -o ./pa_process.o $(CFLAGS)
+
+[VersionInfo]
+Major=0
+Minor=1
+Release=1
+Build=1
+LanguageID=1033
+CharsetID=1252
+CompanyName=
+FileVersion=
+FileDescription=Developed using the Dev-C++ IDE
+InternalName=
+LegalCopyright=
+LegalTrademarks=
+OriginalFilename=
+ProductName=
+ProductVersion=
+AutoIncBuildNr=0
+
+[Unit10]
+FileName=..\..\pa_asio\pa_asio.cpp
+CompileCpp=1
+Folder=portaudio
+Compile=1
+Link=1
+Priority=1000
+OverrideBuildCmd=0
+BuildCmd=$(CPP) -c pa_asio.cpp -o ./pa_asio.o $(CXXFLAGS)
+
+[Unit11]
+FileName=..\..\pa_win\pa_win_util.c
+CompileCpp=1
+Folder=portaudio
+Compile=1
+Link=1
+Priority=1000
+OverrideBuildCmd=0
+BuildCmd=$(CC) -c pa_win_util.c -o ./pa_win_util.o $(CFLAGS)
+
+[Unit12]
+FileName=..\..\pa_win\pa_win_hostapis.c
+CompileCpp=1
+Folder=portaudio
+Compile=1
+Link=1
+Priority=1000
+OverrideBuildCmd=0
+BuildCmd=$(CC) -c pa_win_hostapis.c -o ./pa_win_hostapis.o $(CFLAGS)
+
+[Unit13]
+FileName=..\..\pa_win_ds\pa_win_ds.c
+CompileCpp=1
+Folder=portaudio
+Compile=1
+Link=1
+Priority=1000
+OverrideBuildCmd=0
+BuildCmd=$(CC) -c pa_win_ds.c -o ./pa_win_ds.o $(CFLAGS)
+
+[Unit14]
+FileName=..\..\pa_win_ds\dsound_wrapper.c
+CompileCpp=1
+Folder=portaudio
+Compile=1
+Link=1
+Priority=1000
+OverrideBuildCmd=0
+BuildCmd=$(CC) -c dsound_wrapper.c -o ./dsound_wrapper.o $(CFLAGS)
+
+[Unit15]
+FileName=..\..\pa_win_wmme\pa_win_wmme.c
+CompileCpp=1
+Folder=portaudio
+Compile=1
+Link=1
+Priority=1000
+OverrideBuildCmd=0
+BuildCmd=$(CC) -c pa_win_wmme.c -o ./pa_win_wmme.o $(CFLAGS)
+
+[Unit16]
+FileName=..\..\pa_asio\iasiothiscallresolver.cpp
+CompileCpp=1
+Folder=portaudio
+Compile=1
+Link=1
+Priority=1000
+OverrideBuildCmd=0
+BuildCmd=
+
diff --git a/src/audio/portaudio/pa_win/dev-cpp/readme.txt b/src/audio/portaudio/pa_win/dev-cpp/readme.txt
new file mode 100644
index 0000000000000000000000000000000000000000..07108a7dde89bb7b848294b822f0af5586d4b3ec
--- /dev/null
+++ b/src/audio/portaudio/pa_win/dev-cpp/readme.txt
@@ -0,0 +1,23 @@
+From: "Peter L Jones" 
+Sent: Wednesday, September 17, 2003 5:18 AM
+Subject: Dev-C++ project files
+
+I attach two project files intended for portaudio/pa_win/dev-cpp (i.e. in
+parallel with the msvc directory), if you want them.  One is for a static
+library build and one for a DLL.  I've used the static library (in building
+a single monolithic DLL) but I can't guarantee the DLL version will build a
+working library (I think it's mostly there, though!).
+
+I also attach the resulting makefiles, which may be of use to other MinGW
+users.
+
+They're rooted in the directory given above and drop their object and
+library files in the same place.  They assume the asiosdk2 files are in the
+same directory as portaudio/ in a sub-directory called asiosdk2/.  Oh!  The
+DLL is built against a static asiosdk2.a library... maybe not the best way
+to do it...  I ought to figure out how to link against a "home made" dll in
+Dev-C++, I guess ;-)
+
+Cheers,
+
+-- Peter
diff --git a/src/audio/portaudio/pa_win/msvc/Makefile.msvc b/src/audio/portaudio/pa_win/msvc/Makefile.msvc
new file mode 100644
index 0000000000000000000000000000000000000000..2f1a104e7ab2bacae05a29a436270faebc098a4d
--- /dev/null
+++ b/src/audio/portaudio/pa_win/msvc/Makefile.msvc
@@ -0,0 +1,159 @@
+#  Portaudio v1.9-devel VC6 DLL makefile 1.0
+#  
+#  David Viens, davidv@plogue.com
+#  (im far from a VC6 makefile expert, so please bear with me :)
+#  
+#  For more info, look at readme.txt
+#
+#if you keep the ASIODIR as ".", it will use the SDK files that direclty in "pa_win/msvc" dir
+ASIODIR=.
+ASIOINC=/I ".\host" /I ".\host\pc" /I ".\common" 
+#
+
+LIBZ=kernel32.lib user32.lib gdi32.lib wininet.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib winmm.lib dsound.lib dxguid.lib 
+
+CPP=cl.exe
+LINK32=link.exe 
+
+#release
+CFLAGS=/nologo /MD  /W3 /GX /O2 /Ob2 /I "src" /I "Win32" /I "$(MSVCDir)\Include" /D "WIN32" /D "NDEBUG"  /D "_USRDLL"   /YX /FD
+DLL_LINK_FLAGS= /nologo /dll /incremental:no /libpath:"$(MSVCDir)\Lib" $(LIBZ) /pdb:"portaudio.pdb" /implib:".\portaudio.lib"    /machine:I386 /out:"portaudio.dll" 
+
+
+COMMONINC=/I "..\..\pa_common" /I "."
+
+#====================================================================
+# Targets
+
+ALL : portaudio.dll
+
+CLEAN :
+    -@erase "*.obj"
+
+#====================================================================
+
+
+LINK32_OBJS= \
+	".\pa_allocation.obj" \
+	".\pa_converters.obj" \
+	".\pa_x86_plain_converters.obj" \
+#   ".\pa_cppHelp.obj" \
+	".\pa_cpuload.obj" \
+	".\pa_dither.obj" \
+	".\pa_front.obj" \
+	".\pa_process.obj" \
+	".\pa_skeleton.obj" \
+	".\pa_stream.obj" \
+	".\pa_trace.obj" \
+#
+	".\pa_win_hostapis.obj" \
+	".\pa_win_util.obj" \
+#
+	".\pa_win_wmme.obj" \
+#
+	".\pa_win_ds.obj" \
+	".\dsound_wrapper.obj" \
+#
+	".\pa_asio.obj" \
+#
+	".\asio.obj" \
+	".\ASIOConvertSamples.obj" \
+	".\asiodrivers.obj" \
+	".\asiolist.obj" \
+	".\combase.obj" \
+	".\debugmessage.obj" \
+#	".\dllentry.obj" \
+	".\register.obj" 
+
+
+portaudio.dll : $(LINK32_OBJS) ".\portaudio.def"
+    $(LINK32) $(DLL_LINK_FLAGS) /def:".\portaudio.def" $(LINK32_OBJS)
+
+#====================================================================
+# asio files (need to agree to steinberg agreement)
+# this makefile assumes all files have being copied in the pa_win/msvc dir (for now)
+# see readme.txt for details
+
+".\asio.obj" : ".\common\asio.cpp" 
+    $(CPP) $(CFLAGS) $(ASIOINC) /Fo".\asio.obj" /c ".\common\asio.cpp"
+    
+".\ASIOConvertSamples.obj" : ".\host\ASIOConvertSamples.cpp" 
+    $(CPP) $(CFLAGS) $(ASIOINC) /Fo".\ASIOConvertSamples.obj" /c ".\host\ASIOConvertSamples.cpp"
+    
+".\asiodrivers.obj" : ".\host\asiodrivers.cpp" 
+    $(CPP) $(CFLAGS) $(ASIOINC) /Fo".\asiodrivers.obj" /c ".\host\asiodrivers.cpp"
+    
+".\asiolist.obj" : ".\host\pc\asiolist.cpp" 
+    $(CPP) $(CFLAGS) $(ASIOINC) /Fo".\asiolist.obj" /c ".\host\pc\asiolist.cpp"
+    
+".\combase.obj" : ".\common\combase.cpp" 
+    $(CPP) $(CFLAGS) $(ASIOINC) /Fo".\combase.obj" /c ".\common\combase.cpp"
+    
+".\debugmessage.obj" : ".\common\debugmessage.cpp" 
+    $(CPP) $(CFLAGS) $(ASIOINC) /Fo".\debugmessage.obj" /c ".\common\debugmessage.cpp"
+    
+".\register.obj" : ".\common\register.cpp" 
+    $(CPP) $(CFLAGS) $(ASIOINC) /Fo".\register.obj" /c ".\common\register.cpp"
+
+#====================================================================
+#  Portaudio Common
+# 
+".\pa_allocation.obj" : "..\..\pa_common\pa_allocation.c" 
+    $(CPP) $(CFLAGS) $(COMMONINC) /Fo".\pa_allocation.obj" /c "..\..\pa_common\pa_allocation.c"
+
+".\pa_converters.obj" : "..\..\pa_common\pa_converters.c" 
+    $(CPP) $(CFLAGS) $(COMMONINC) /Fo".\pa_converters.obj" /c "..\..\pa_common\pa_converters.c"
+
+".\pa_cppHelp.obj" : "..\..\pa_common\pa_cppHelp.cpp" 
+    $(CPP) $(CFLAGS) $(COMMONINC) /Fo".\pa_cppHelp.obj" /c "..\..\pa_common\pa_cppHelp.cpp"
+
+".\pa_cpuload.obj" : "..\..\pa_common\pa_cpuload.c" 
+    $(CPP) $(CFLAGS) $(COMMONINC) /Fo".\pa_cpuload.obj" /c "..\..\pa_common\pa_cpuload.c"
+
+".\pa_dither.obj" : "..\..\pa_common\pa_dither.c" 
+    $(CPP) $(CFLAGS) $(COMMONINC) /Fo".\pa_dither.obj" /c "..\..\pa_common\pa_dither.c"
+
+".\pa_front.obj" : "..\..\pa_common\pa_front.c" 
+    $(CPP) $(CFLAGS) $(COMMONINC) /Fo".\pa_front.obj" /c "..\..\pa_common\pa_front.c"
+
+".\pa_process.obj" : "..\..\pa_common\pa_process.c" 
+    $(CPP) $(CFLAGS) $(COMMONINC) /Fo".\pa_process.obj" /c "..\..\pa_common\pa_process.c"
+
+".\pa_skeleton.obj" : "..\..\pa_common\pa_skeleton.c" 
+    $(CPP) $(CFLAGS) $(COMMONINC) /Fo".\pa_skeleton.obj" /c "..\..\pa_common\pa_skeleton.c"
+
+".\pa_stream.obj" : "..\..\pa_common\pa_stream.c" 
+    $(CPP) $(CFLAGS) $(COMMONINC) /Fo".\pa_stream.obj" /c "..\..\pa_common\pa_stream.c"
+    
+".\pa_trace.obj" : "..\..\pa_common\pa_trace.c" 
+    $(CPP) $(CFLAGS) $(COMMONINC) /Fo".\pa_trace.obj" /c "..\..\pa_common\pa_trace.c"
+
+#====================================================================
+#  Portaudio implementations
+# 
+
+".\pa_win_hostapis.obj" : "..\..\pa_win\pa_win_hostapis.c" 
+    $(CPP) $(CFLAGS) $(COMMONINC) /Fo".\pa_win_hostapis.obj" /c "..\..\pa_win\pa_win_hostapis.c"
+    
+".\pa_win_util.obj" : "..\..\pa_win\pa_win_util.c" 
+    $(CPP) $(CFLAGS) $(COMMONINC) /Fo".\pa_win_util.obj" /c "..\..\pa_win\pa_win_util.c"
+
+".\pa_x86_plain_converters.obj" : "..\..\pa_win\pa_x86_plain_converters.c" 
+    $(CPP) $(CFLAGS) $(COMMONINC) /Fo".\pa_x86_plain_converters.obj" /c "..\..\pa_win\pa_x86_plain_converters.c"
+
+".\pa_asio.obj" : "..\..\pa_asio\pa_asio.cpp" 
+    $(CPP) $(CFLAGS) $(ASIOINC) $(COMMONINC) /Fo".\pa_asio.obj" /c "..\..\pa_asio\pa_asio.cpp"
+
+".\pa_win_wmme.obj" : "..\..\pa_win_wmme\pa_win_wmme.c" 
+    $(CPP) $(CFLAGS) $(COMMONINC) /Fo".\pa_win_wmme.obj" /c "..\..\pa_win_wmme\pa_win_wmme.c"
+
+".\pa_win_ds.obj" : "..\..\pa_win_ds\pa_win_ds.c" 
+    $(CPP) $(CFLAGS) $(COMMONINC) /Fo".\pa_win_ds.obj" /c "..\..\pa_win_ds\pa_win_ds.c"
+
+".\dsound_wrapper.obj" : "..\..\pa_win_ds\dsound_wrapper.c" 
+    $(CPP) $(CFLAGS) $(COMMONINC) /Fo".\dsound_wrapper.obj" /c "..\..\pa_win_ds\dsound_wrapper.c"
+
+
+# End of Makefile
+#====================================================================
+#
\ No newline at end of file
diff --git a/src/audio/portaudio/pa_win/msvc/clean.bat b/src/audio/portaudio/pa_win/msvc/clean.bat
new file mode 100755
index 0000000000000000000000000000000000000000..601c0d314d394840b67aa400f19ec35317a38788
--- /dev/null
+++ b/src/audio/portaudio/pa_win/msvc/clean.bat
@@ -0,0 +1,7 @@
+del *.obj
+del *.dll
+del *.lib
+del *.exp
+del *.pch
+del *.idb
+
diff --git a/src/audio/portaudio/pa_win/msvc/make.bat b/src/audio/portaudio/pa_win/msvc/make.bat
new file mode 100755
index 0000000000000000000000000000000000000000..34d6aed48aa80aead2fdbfba063c704cff0acada
--- /dev/null
+++ b/src/audio/portaudio/pa_win/msvc/make.bat
@@ -0,0 +1,8 @@
+CALL C:\progra~1\micros~2\VC98\bin\vcvars32
+del *.dll
+del *.lib
+nmake Makefile.msvc
+del *.obj
+del *.exp
+del *.pch
+del *.idb
diff --git a/src/audio/portaudio/pa_win/msvc/portaudio.def b/src/audio/portaudio/pa_win/msvc/portaudio.def
new file mode 100644
index 0000000000000000000000000000000000000000..97a1c3502b7f123a977a3d28d2d7f5a23a7c502f
--- /dev/null
+++ b/src/audio/portaudio/pa_win/msvc/portaudio.def
@@ -0,0 +1,43 @@
+LIBRARY  portaudio.dll
+EXPORTS
+
+;
+Pa_GetVersion                       @1
+Pa_GetVersionText                   @2
+Pa_GetErrorText                     @3                 
+Pa_Initialize                       @4
+Pa_Terminate                        @5
+Pa_GetHostApiCount                  @6
+Pa_GetDefaultHostApi                @7
+Pa_GetHostApiInfo                   @8
+Pa_HostApiTypeIdToHostApiIndex      @9
+Pa_HostApiDeviceIndexToDeviceIndex  @10
+Pa_GetLastHostErrorInfo             @11
+Pa_GetDeviceCount                   @12
+Pa_GetDefaultInputDevice            @13
+Pa_GetDefaultOutputDevice           @14
+Pa_GetDeviceInfo                    @15
+Pa_IsFormatSupported                @16
+Pa_OpenStream                       @17
+Pa_OpenDefaultStream                @18
+Pa_CloseStream                      @19
+Pa_SetStreamFinishedCallback        @20
+Pa_StartStream                      @21
+Pa_StopStream                       @22
+Pa_AbortStream                      @23
+Pa_IsStreamStopped                  @24
+Pa_IsStreamActive                   @25
+Pa_GetStreamInfo                    @26
+Pa_GetStreamTime                    @27
+Pa_GetStreamCpuLoad                 @28
+Pa_ReadStream                       @29
+Pa_WriteStream                      @30
+Pa_GetStreamReadAvailable           @31
+Pa_GetStreamWriteAvailable          @32
+Pa_GetSampleSize                    @33
+Pa_Sleep                            @34
+PaAsio_GetAvailableLatencyValues    @50
+PaAsio_ShowControlPanel             @51
+PaUtil_InitializeX86PlainConverters @52
+PaAsio_GetInputChannelName          @53
+PaAsio_GetOutputChannelName         @54
\ No newline at end of file
diff --git a/src/audio/portaudio/pa_win/msvc/readme.txt b/src/audio/portaudio/pa_win/msvc/readme.txt
new file mode 100644
index 0000000000000000000000000000000000000000..19ced4c4c21186b56841988872c161122d877ced
--- /dev/null
+++ b/src/audio/portaudio/pa_win/msvc/readme.txt
@@ -0,0 +1,56 @@
+Hello
+
+  This is a small list of steps in order to build portaudio
+(Currently v19-devel) into a VC6 DLL and lib file.
+This DLL contains all 3 current win32 PA APIS (MM/DS/ASIO)
+
+1)Copy the source dirs that comes with the ASIO SDK inside pa_win\msvc
+  so you should now have:
+  
+  pa_win\msvc\common
+  pa_win\msvc\host
+  pa_win\msvc\host\sample
+  pa_win\msvc\host\pc
+  pa_win\msvc\host\mac (not needed)
+  
+  You dont need "driver"
+  
+
+2)execure "make.bat", this assumes VC6 is installed in 
+     C:\Program Files\Microsoft Visual Studio\
+ 
+ if its not, 
+  
+  Open a command Prompt and execute "vcvars32.bat" which sets the environment
+  so that you can use Microsoft's "nmake"  
+  EX: C:\Program Files\Microsoft Visual Studio\VC98\Bin\vcvars32.bat
+  or (C:\progra~1\micros~2\VC98\bin\vcvars32) dumb de dumb
+  
+  You should now have seen a line that said:
+  "Setting environment for using Microsoft Visual C++ tools."
+  While in pa_win\msvc , type "nmake makefile.msvc"  
+  this _should_ create portaudio.dll and portaudio.lib 
+ 
+3)Now in any VC6 project, in which you require portaudio,
+  you can just link with portaudio.lib, and of course include the 
+  relevant headers
+  (portaudio.h, and/or pa_asio.h , pa_x86_plain_converters.h) See (*)
+  
+4) Your new exe should now use portaudio.dll.
+
+
+Have fun!
+
+(*): you may want to add/remove some DLL entry points.
+Right now those 3 entries are _not_ from portaudio.h
+
+(from portaudio.def)
+(...)
+PaAsio_GetAvailableLatencyValues    @50
+PaAsio_ShowControlPanel             @51
+PaUtil_InitializeX86PlainConverters @52
+
+
+-----
+last update April 16th 2003
+David Viens, davidv@plogue.com
\ No newline at end of file
diff --git a/src/audio/portaudio/pa_win/msvc/setenv.bat b/src/audio/portaudio/pa_win/msvc/setenv.bat
new file mode 100755
index 0000000000000000000000000000000000000000..f4c815a7832d09b8a5bc972d7ece24e02dc567ca
--- /dev/null
+++ b/src/audio/portaudio/pa_win/msvc/setenv.bat
@@ -0,0 +1 @@
+C:\progra~1\micros~2\VC98\bin\vcvars32
diff --git a/src/audio/portaudio/pa_win/pa_win_hostapis.c b/src/audio/portaudio/pa_win/pa_win_hostapis.c
new file mode 100644
index 0000000000000000000000000000000000000000..5d5ac87f3a5fcea0f240d3674d53783be2882080
--- /dev/null
+++ b/src/audio/portaudio/pa_win/pa_win_hostapis.c
@@ -0,0 +1,86 @@
+/*
+ * $Id$
+ * Portable Audio I/O Library Windows initialization table
+ *
+ * Based on the Open Source API proposed by Ross Bencina
+ * Copyright (c) 1999-2002 Ross Bencina, 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.
+ */
+
+/** @file
+    Win32 host API initialization function table.
+
+    @todo Consider using PA_USE_WMME etc instead of PA_NO_WMME. This is what
+    the Unix version does, we should consider being consistent.
+*/
+
+
+#include "pa_hostapi.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif /* __cplusplus */
+
+PaError PaSkeleton_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
+PaError PaWinMme_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
+PaError PaWinDs_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
+PaError PaAsio_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
+PaError PaWinWdm_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+
+PaUtilHostApiInitializer *paHostApiInitializers[] =
+    {
+
+#ifndef PA_NO_WMME
+        PaWinMme_Initialize,
+#endif
+
+#ifndef PA_NO_DS
+        PaWinDs_Initialize,
+#endif
+
+#ifndef PA_NO_ASIO
+        PaAsio_Initialize,
+#endif
+
+/*
+#ifndef PA_NO_WDMKS
+        PaWinWdm_Initialize,
+#endif
+*/
+
+        PaSkeleton_Initialize, /* just for testing */
+
+        0   /* NULL terminated array */
+    };
+
+
+int paDefaultHostApiIndex = 0;
+
diff --git a/src/audio/portaudio/pa_win/pa_win_util.c b/src/audio/portaudio/pa_win/pa_win_util.c
new file mode 100644
index 0000000000000000000000000000000000000000..32d0dbb9867e795b2e1b255eb93c429ab106d5bf
--- /dev/null
+++ b/src/audio/portaudio/pa_win/pa_win_util.c
@@ -0,0 +1,134 @@
+/*
+ * $Id$
+ * Portable Audio I/O Library
+ * Win32 platform-specific support functions
+ *
+ * Based on the Open Source API proposed by Ross Bencina
+ * Copyright (c) 1999-2000 Ross Bencina
+ *
+ * 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.
+ */
+
+/** @file
+ Win32 platform-specific support functions.
+
+    @todo Implement workaround for QueryPerformanceCounter() skipping forward
+    bug. (see msdn kb Q274323).
+*/
+ 
+#include <windows.h>
+#include <mmsystem.h> /* for timeGetTime() */
+
+#include "pa_util.h"
+
+
+/*
+   Track memory allocations to avoid leaks.
+ */
+
+#if PA_TRACK_MEMORY
+static int numAllocations_ = 0;
+#endif
+
+
+void *PaUtil_AllocateMemory( long size )
+{
+    void *result = GlobalAlloc( GPTR, size );
+
+#if PA_TRACK_MEMORY
+    if( result != NULL ) numAllocations_ += 1;
+#endif
+    return result;
+}
+
+
+void PaUtil_FreeMemory( void *block )
+{
+    if( block != NULL )
+    {
+        GlobalFree( block );
+#if PA_TRACK_MEMORY
+        numAllocations_ -= 1;
+#endif
+
+    }
+}
+
+
+int PaUtil_CountCurrentlyAllocatedBlocks( void )
+{
+#if PA_TRACK_MEMORY
+    return numAllocations_;
+#else
+    return 0;
+#endif
+}
+
+
+void Pa_Sleep( long msec )
+{
+    Sleep( msec );
+}
+
+static int usePerformanceCounter_;
+static double secondsPerTick_;
+
+void PaUtil_InitializeClock( void )
+{
+    LARGE_INTEGER ticksPerSecond;
+
+    if( QueryPerformanceFrequency( &ticksPerSecond ) != 0 )
+    {
+        usePerformanceCounter_ = 1;
+        secondsPerTick_ = 1.0 / (double)ticksPerSecond.QuadPart;
+    }
+    else
+    {
+        usePerformanceCounter_ = 0;
+    }
+}
+
+
+double PaUtil_GetTime( void )
+{
+    LARGE_INTEGER time;
+
+    if( usePerformanceCounter_ )
+    {
+        /* FIXME:
+            according to this knowledge-base article, QueryPerformanceCounter
+            can skip forward by seconds!
+            http://support.microsoft.com/default.aspx?scid=KB;EN-US;Q274323&
+
+            it may be better to use the rtdsc instruction using inline asm,
+            however then a method is needed to calculate a ticks/seconds ratio.
+        */
+        QueryPerformanceCounter( &time );
+        return time.QuadPart * secondsPerTick_;
+    }
+    else
+    {
+        return timeGetTime() * .001;
+    }
+}
diff --git a/src/audio/portaudio/pa_win/pa_x86_plain_converters.c b/src/audio/portaudio/pa_win/pa_x86_plain_converters.c
new file mode 100644
index 0000000000000000000000000000000000000000..98442a8c61500b4a8fdc9bf28c34a2ce2d75b430
--- /dev/null
+++ b/src/audio/portaudio/pa_win/pa_x86_plain_converters.c
@@ -0,0 +1,1167 @@
+#include "pa_x86_plain_converters.h"
+
+#include "pa_converters.h"
+#include "pa_dither.h"
+
+/*
+    plain intel assemby versions of standard pa converter functions.
+
+    the main reason these versions are faster than the equivalent C versions
+    is that float -> int casting is expensive in C on x86 because the rounding
+    mode needs to be changed for every cast. these versions only set
+    the rounding mode once outside the loop.
+
+    small additional speed gains are made by the way that clamping is
+    implemented.
+
+TODO:
+    o- inline dither code
+    o- implement Dither only (no-clip) versions
+    o- implement int8 and uint8 versions
+    o- test thouroughly
+
+    o- the packed 24 bit functions could benefit from unrolling and avoiding
+        byte and word sized register access.
+*/
+
+/* -------------------------------------------------------------------------- */
+
+/*
+#define PA_CLIP_( val, min, max )\
+    { val = ((val) < (min)) ? (min) : (((val) > (max)) ? (max) : (val)); }
+*/
+
+/*
+    the following notes were used to determine whether a floating point
+    value should be saturated (ie >1 or <-1) by loading it into an integer
+    register. these should be rewritten so that they make sense.
+
+    an ieee floating point value
+
+    1.xxxxxxxxxxxxxxxxxxxx?
+
+
+    is less than  or equal to 1 and greater than or equal to -1 either:
+
+        if the mantissa is 0 and the unbiased exponent is 0
+
+        OR
+
+        if the unbiased exponent < 0
+
+    this translates to:
+
+        if the mantissa is 0 and the biased exponent is 7F
+
+        or
+
+        if the biased exponent is less than 7F
+
+
+    therefore the value is greater than 1 or less than -1 if
+
+        the mantissa is not 0 and the biased exponent is 7F
+
+        or
+
+        if the biased exponent is greater than 7F
+
+
+    in other words, if we mask out the sign bit, the value is
+    greater than 1 or less than -1 if its integer representation is greater than:
+
+    0 01111111 0000 0000 0000 0000 0000 000
+
+    0011 1111 1000 0000 0000 0000 0000 0000 => 0x3F800000
+*/
+
+/* -------------------------------------------------------------------------- */
+
+static const short fpuControlWord_ = 0x033F; /*round to nearest, 64 bit precision, all exceptions masked*/
+static const double int32Scaler_ = 0x7FFFFFFF;
+static const double ditheredInt32Scaler_ = 0x7FFFFFFE;
+static const double int24Scaler_ = 0x7FFFFF;
+static const double ditheredInt24Scaler_ = 0x7FFFFE;
+static const double int16Scaler_ = 0x7FFF;
+static const double ditheredInt16Scaler_ = 0x7FFE;
+
+#define PA_DITHER_BITS_   (15)
+/* Multiply by PA_FLOAT_DITHER_SCALE_ to get a float between -2.0 and +1.99999 */
+#define PA_FLOAT_DITHER_SCALE_  (1.0 / ((1<<PA_DITHER_BITS_)-1))
+static const float const_float_dither_scale_ = PA_FLOAT_DITHER_SCALE_;
+#define PA_DITHER_SHIFT_  ((32 - PA_DITHER_BITS_) + 1)
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_Int32(
+    void *destinationBuffer, signed int destinationStride,
+    void *sourceBuffer, signed int sourceStride,
+    unsigned int count, PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+/*
+    float *src = (float*)sourceBuffer;
+    signed long *dest =  (signed long*)destinationBuffer;
+    (void)ditherGenerator; // unused parameter
+
+    while( count-- )
+    {
+        // REVIEW
+        double scaled = *src * 0x7FFFFFFF;
+        *dest = (signed long) scaled;
+
+        src += sourceStride;
+        dest += destinationStride;
+    }
+*/
+
+    short savedFpuControlWord;
+
+    (void) ditherGenerator; /* unused parameter */
+
+
+    __asm{
+        // esi -> source ptr
+        // eax -> source byte stride
+        // edi -> destination ptr
+        // ebx -> destination byte stride
+        // ecx -> source end ptr
+        // edx -> temp
+
+        mov     esi, sourceBuffer
+
+        mov     edx, 4                  // sizeof float32 and int32
+        mov     eax, sourceStride
+        imul    eax, edx
+
+        mov     ecx, count
+        imul    ecx, eax
+        add     ecx, esi
+    
+        mov     edi, destinationBuffer
+        
+        mov     ebx, destinationStride
+        imul    ebx, edx
+
+        fwait
+        fstcw   savedFpuControlWord
+        fldcw   fpuControlWord_
+
+        fld     int32Scaler_             // stack:  (int)0x7FFFFFFF
+
+    Float32_To_Int32_loop:
+
+        // load unscaled value into st(0)
+        fld     dword ptr [esi]         // stack:  value, (int)0x7FFFFFFF
+        add     esi, eax                // increment source ptr
+        //lea     esi, [esi+eax]
+        fmul    st(0), st(1)            // st(0) *= st(1), stack:  value*0x7FFFFFFF, (int)0x7FFFFFFF
+        /*
+            note: we could store to a temporary qword here which would cause
+            wraparound distortion instead of int indefinite 0x10. that would
+            be more work, and given that not enabling clipping is only advisable
+            when you know that your signal isn't going to clip it isn't worth it.
+        */
+        fistp   dword ptr [edi]         // pop st(0) into dest, stack:  (int)0x7FFFFFFF
+
+        add     edi, ebx                // increment destination ptr
+        //lea     edi, [edi+ebx]
+
+        cmp     esi, ecx                // has src ptr reached end?
+        jne     Float32_To_Int32_loop
+
+        ffree   st(0)
+        fincstp
+
+        fwait
+        fnclex
+        fldcw   savedFpuControlWord
+    }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_Int32_Clip(
+    void *destinationBuffer, signed int destinationStride,
+    void *sourceBuffer, signed int sourceStride,
+    unsigned int count, PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+/*
+    float *src = (float*)sourceBuffer;
+    signed long *dest =  (signed long*)destinationBuffer;
+    (void) ditherGenerator; // unused parameter
+
+    while( count-- )
+    {
+        // REVIEW
+        double scaled = *src * 0x7FFFFFFF;
+        PA_CLIP_( scaled, -2147483648., 2147483647.  );
+        *dest = (signed long) scaled;
+
+        src += sourceStride;
+        dest += destinationStride;
+    }
+*/
+
+    short savedFpuControlWord;
+
+    (void) ditherGenerator; /* unused parameter */
+
+    __asm{
+        // esi -> source ptr
+        // eax -> source byte stride
+        // edi -> destination ptr
+        // ebx -> destination byte stride
+        // ecx -> source end ptr
+        // edx -> temp
+
+        mov     esi, sourceBuffer
+
+        mov     edx, 4                  // sizeof float32 and int32
+        mov     eax, sourceStride
+        imul    eax, edx
+
+        mov     ecx, count
+        imul    ecx, eax
+        add     ecx, esi
+    
+        mov     edi, destinationBuffer
+        
+        mov     ebx, destinationStride
+        imul    ebx, edx
+
+        fwait
+        fstcw   savedFpuControlWord
+        fldcw   fpuControlWord_
+
+        fld     int32Scaler_             // stack:  (int)0x7FFFFFFF
+
+    Float32_To_Int32_Clip_loop:
+
+        mov     edx, dword ptr [esi]    // load floating point value into integer register
+
+        and     edx, 0x7FFFFFFF         // mask off sign
+        cmp     edx, 0x3F800000         // greater than 1.0 or less than -1.0
+
+        jg      Float32_To_Int32_Clip_clamp
+
+        // load unscaled value into st(0)
+        fld     dword ptr [esi]         // stack:  value, (int)0x7FFFFFFF
+        add     esi, eax                // increment source ptr
+        //lea     esi, [esi+eax]
+        fmul    st(0), st(1)            // st(0) *= st(1), stack:  value*0x7FFFFFFF, (int)0x7FFFFFFF
+        fistp   dword ptr [edi]         // pop st(0) into dest, stack:  (int)0x7FFFFFFF
+        jmp     Float32_To_Int32_Clip_stored
+    
+    Float32_To_Int32_Clip_clamp:
+        mov     edx, dword ptr [esi]    // load floating point value into integer register
+        shr     edx, 31                 // move sign bit into bit 0
+        add     esi, eax                // increment source ptr
+        //lea     esi, [esi+eax]
+        add     edx, 0x7FFFFFFF         // convert to maximum range integers
+        mov     dword ptr [edi], edx
+
+    Float32_To_Int32_Clip_stored:
+
+        //add     edi, ebx                // increment destination ptr
+        lea     edi, [edi+ebx]
+
+        cmp     esi, ecx                // has src ptr reached end?
+        jne     Float32_To_Int32_Clip_loop
+
+        ffree   st(0)
+        fincstp
+
+        fwait
+        fnclex
+        fldcw   savedFpuControlWord
+    }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_Int32_DitherClip(
+    void *destinationBuffer, signed int destinationStride,
+    void *sourceBuffer, signed int sourceStride,
+    unsigned int count, PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+    /*
+    float *src = (float*)sourceBuffer;
+    signed long *dest =  (signed long*)destinationBuffer;
+
+    while( count-- )
+    {
+        // REVIEW
+        double dither  = PaUtil_GenerateFloatTriangularDither( ditherGenerator );
+        // use smaller scaler to prevent overflow when we add the dither
+        double dithered = ((double)*src * (2147483646.0)) + dither;
+        PA_CLIP_( dithered, -2147483648., 2147483647.  );
+        *dest = (signed long) dithered;
+
+
+        src += sourceStride;
+        dest += destinationStride;
+    }
+    */
+
+    short savedFpuControlWord;
+
+    // spill storage:
+    signed long sourceByteStride;
+    signed long highpassedDither;
+
+    // dither state:
+    unsigned long ditherPrevious = ditherGenerator->previous;
+    unsigned long ditherRandSeed1 = ditherGenerator->randSeed1;
+    unsigned long ditherRandSeed2 = ditherGenerator->randSeed2;
+                    
+    __asm{
+        // esi -> source ptr
+        // eax -> source byte stride
+        // edi -> destination ptr
+        // ebx -> destination byte stride
+        // ecx -> source end ptr
+        // edx -> temp
+
+        mov     esi, sourceBuffer
+
+        mov     edx, 4                  // sizeof float32 and int32
+        mov     eax, sourceStride
+        imul    eax, edx
+
+        mov     ecx, count
+        imul    ecx, eax
+        add     ecx, esi
+    
+        mov     edi, destinationBuffer
+        
+        mov     ebx, destinationStride
+        imul    ebx, edx
+
+        fwait
+        fstcw   savedFpuControlWord
+        fldcw   fpuControlWord_
+
+        fld     ditheredInt32Scaler_    // stack:  int scaler
+
+    Float32_To_Int32_DitherClip_loop:
+
+        mov     edx, dword ptr [esi]    // load floating point value into integer register
+
+        and     edx, 0x7FFFFFFF         // mask off sign
+        cmp     edx, 0x3F800000         // greater than 1.0 or less than -1.0
+
+        jg      Float32_To_Int32_DitherClip_clamp
+
+        // load unscaled value into st(0)
+        fld     dword ptr [esi]         // stack:  value, int scaler
+        add     esi, eax                // increment source ptr
+        //lea     esi, [esi+eax]
+        fmul    st(0), st(1)            // st(0) *= st(1), stack:  value*(int scaler), int scaler
+
+        /*
+        // call PaUtil_GenerateFloatTriangularDither with C calling convention
+        mov     sourceByteStride, eax   // save eax
+        mov     sourceEnd, ecx          // save ecx
+        push    ditherGenerator         // pass ditherGenerator parameter on stack
+	    call    PaUtil_GenerateFloatTriangularDither  // stack:  dither, value*(int scaler), int scaler
+	    pop     edx                     // clear parameter off stack
+        mov     ecx, sourceEnd          // restore ecx
+        mov     eax, sourceByteStride   // restore eax
+        */
+
+    // generate dither
+        mov     sourceByteStride, eax   // save eax
+        mov     edx, 196314165
+        mov     eax, ditherRandSeed1
+        mul     edx                     // eax:edx = eax * 196314165
+        //add     eax, 907633515
+        lea     eax, [eax+907633515]
+        mov     ditherRandSeed1, eax
+        mov     edx, 196314165
+        mov     eax, ditherRandSeed2
+        mul     edx                     // eax:edx = eax * 196314165
+        //add     eax, 907633515
+        lea     eax, [eax+907633515]
+        mov     edx, ditherRandSeed1
+        shr     edx, PA_DITHER_SHIFT_
+        mov     ditherRandSeed2, eax
+        shr     eax, PA_DITHER_SHIFT_
+        //add     eax, edx                // eax -> current
+        lea     eax, [eax+edx]
+        mov     edx, ditherPrevious
+        neg     edx
+        lea     edx, [eax+edx]          // highpass = current - previous
+        mov     highpassedDither, edx
+        mov     ditherPrevious, eax     // previous = current
+        mov     eax, sourceByteStride   // restore eax
+        fild    highpassedDither
+        fmul    const_float_dither_scale_
+    // end generate dither, dither signal in st(0)
+    
+        faddp   st(1), st(0)            // stack: dither + value*(int scaler), int scaler
+        fistp   dword ptr [edi]         // pop st(0) into dest, stack:  int scaler
+        jmp     Float32_To_Int32_DitherClip_stored
+    
+    Float32_To_Int32_DitherClip_clamp:
+        mov     edx, dword ptr [esi]    // load floating point value into integer register
+        shr     edx, 31                 // move sign bit into bit 0
+        add     esi, eax                // increment source ptr
+        //lea     esi, [esi+eax]
+        add     edx, 0x7FFFFFFF         // convert to maximum range integers
+        mov     dword ptr [edi], edx
+
+    Float32_To_Int32_DitherClip_stored:
+
+        //add     edi, ebx              // increment destination ptr
+        lea     edi, [edi+ebx]
+
+        cmp     esi, ecx                // has src ptr reached end?
+        jne     Float32_To_Int32_DitherClip_loop
+
+        ffree   st(0)
+        fincstp
+
+        fwait
+        fnclex
+        fldcw   savedFpuControlWord
+    }
+
+    ditherGenerator->previous = ditherPrevious;
+    ditherGenerator->randSeed1 = ditherRandSeed1;
+    ditherGenerator->randSeed2 = ditherRandSeed2;
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_Int24(
+    void *destinationBuffer, signed int destinationStride,
+    void *sourceBuffer, signed int sourceStride,
+    unsigned int count, PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+/*
+    float *src = (float*)sourceBuffer;
+    unsigned char *dest = (unsigned char*)destinationBuffer;
+    signed long temp;
+
+    (void) ditherGenerator; // unused parameter
+    
+    while( count-- )
+    {
+        // convert to 32 bit and drop the low 8 bits
+        double scaled = *src * 0x7FFFFFFF;
+        temp = (signed long) scaled;
+
+        dest[0] = (unsigned char)(temp >> 8);
+        dest[1] = (unsigned char)(temp >> 16);
+        dest[2] = (unsigned char)(temp >> 24);
+
+        src += sourceStride;
+        dest += destinationStride * 3;
+    }
+*/
+
+    short savedFpuControlWord;
+    
+    signed long tempInt32;
+
+    (void) ditherGenerator; /* unused parameter */
+                 
+    __asm{
+        // esi -> source ptr
+        // eax -> source byte stride
+        // edi -> destination ptr
+        // ebx -> destination byte stride
+        // ecx -> source end ptr
+        // edx -> temp
+
+        mov     esi, sourceBuffer
+
+        mov     edx, 4                  // sizeof float32
+        mov     eax, sourceStride
+        imul    eax, edx
+
+        mov     ecx, count
+        imul    ecx, eax
+        add     ecx, esi
+
+        mov     edi, destinationBuffer
+
+        mov     edx, 3                  // sizeof int24
+        mov     ebx, destinationStride
+        imul    ebx, edx
+
+        fwait
+        fstcw   savedFpuControlWord
+        fldcw   fpuControlWord_
+
+        fld     int24Scaler_             // stack:  (int)0x7FFFFF
+
+    Float32_To_Int24_loop:
+
+        // load unscaled value into st(0)
+        fld     dword ptr [esi]         // stack:  value, (int)0x7FFFFF
+        add     esi, eax                // increment source ptr
+        //lea     esi, [esi+eax]
+        fmul    st(0), st(1)            // st(0) *= st(1), stack:  value*0x7FFFFF, (int)0x7FFFFF
+        fistp   tempInt32               // pop st(0) into tempInt32, stack:  (int)0x7FFFFF
+        mov     edx, tempInt32
+
+        mov     byte ptr [edi], DL
+        shr     edx, 8
+        //mov     byte ptr [edi+1], DL
+        //mov     byte ptr [edi+2], DH
+        mov     word ptr [edi+1], DX
+
+        //add     edi, ebx                // increment destination ptr
+        lea     edi, [edi+ebx]
+
+        cmp     esi, ecx                // has src ptr reached end?
+        jne     Float32_To_Int24_loop
+
+        ffree   st(0)
+        fincstp
+
+        fwait
+        fnclex
+        fldcw   savedFpuControlWord
+    }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_Int24_Clip(
+    void *destinationBuffer, signed int destinationStride,
+    void *sourceBuffer, signed int sourceStride,
+    unsigned int count, PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+/*
+    float *src = (float*)sourceBuffer;
+    unsigned char *dest = (unsigned char*)destinationBuffer;
+    signed long temp;
+
+    (void) ditherGenerator; // unused parameter
+    
+    while( count-- )
+    {
+        // convert to 32 bit and drop the low 8 bits
+        double scaled = *src * 0x7FFFFFFF;
+        PA_CLIP_( scaled, -2147483648., 2147483647.  );
+        temp = (signed long) scaled;
+
+        dest[0] = (unsigned char)(temp >> 8);
+        dest[1] = (unsigned char)(temp >> 16);
+        dest[2] = (unsigned char)(temp >> 24);
+
+        src += sourceStride;
+        dest += destinationStride * 3;
+    }
+*/
+
+    short savedFpuControlWord;
+    
+    signed long tempInt32;
+
+    (void) ditherGenerator; /* unused parameter */
+                 
+    __asm{
+        // esi -> source ptr
+        // eax -> source byte stride
+        // edi -> destination ptr
+        // ebx -> destination byte stride
+        // ecx -> source end ptr
+        // edx -> temp
+
+        mov     esi, sourceBuffer
+
+        mov     edx, 4                  // sizeof float32
+        mov     eax, sourceStride
+        imul    eax, edx
+
+        mov     ecx, count
+        imul    ecx, eax
+        add     ecx, esi
+
+        mov     edi, destinationBuffer
+
+        mov     edx, 3                  // sizeof int24
+        mov     ebx, destinationStride
+        imul    ebx, edx
+
+        fwait
+        fstcw   savedFpuControlWord
+        fldcw   fpuControlWord_
+
+        fld     int24Scaler_             // stack:  (int)0x7FFFFF
+
+    Float32_To_Int24_Clip_loop:
+
+        mov     edx, dword ptr [esi]    // load floating point value into integer register
+
+        and     edx, 0x7FFFFFFF         // mask off sign
+        cmp     edx, 0x3F800000         // greater than 1.0 or less than -1.0
+
+        jg      Float32_To_Int24_Clip_clamp
+
+        // load unscaled value into st(0)
+        fld     dword ptr [esi]         // stack:  value, (int)0x7FFFFF
+        add     esi, eax                // increment source ptr
+        //lea     esi, [esi+eax]
+        fmul    st(0), st(1)            // st(0) *= st(1), stack:  value*0x7FFFFF, (int)0x7FFFFF
+        fistp   tempInt32               // pop st(0) into tempInt32, stack:  (int)0x7FFFFF
+        mov     edx, tempInt32
+        jmp     Float32_To_Int24_Clip_store
+    
+    Float32_To_Int24_Clip_clamp:
+        mov     edx, dword ptr [esi]    // load floating point value into integer register
+        shr     edx, 31                 // move sign bit into bit 0
+        add     esi, eax                // increment source ptr
+        //lea     esi, [esi+eax]
+        add     edx, 0x7FFFFF           // convert to maximum range integers
+
+    Float32_To_Int24_Clip_store:
+
+        mov     byte ptr [edi], DL
+        shr     edx, 8
+        //mov     byte ptr [edi+1], DL
+        //mov     byte ptr [edi+2], DH
+        mov     word ptr [edi+1], DX
+
+        //add     edi, ebx                // increment destination ptr
+        lea     edi, [edi+ebx]
+
+        cmp     esi, ecx                // has src ptr reached end?
+        jne     Float32_To_Int24_Clip_loop
+
+        ffree   st(0)
+        fincstp
+
+        fwait
+        fnclex
+        fldcw   savedFpuControlWord
+    }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_Int24_DitherClip(
+    void *destinationBuffer, signed int destinationStride,
+    void *sourceBuffer, signed int sourceStride,
+    unsigned int count, PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+/*
+    float *src = (float*)sourceBuffer;
+    unsigned char *dest = (unsigned char*)destinationBuffer;
+    signed long temp;
+    
+    while( count-- )
+    {
+        // convert to 32 bit and drop the low 8 bits
+
+        // FIXME: the dither amplitude here appears to be too small by 8 bits
+        double dither  = PaUtil_GenerateFloatTriangularDither( ditherGenerator );
+        // use smaller scaler to prevent overflow when we add the dither
+        double dithered = ((double)*src * (2147483646.0)) + dither;
+        PA_CLIP_( dithered, -2147483648., 2147483647.  );
+        
+        temp = (signed long) dithered;
+
+        dest[0] = (unsigned char)(temp >> 8);
+        dest[1] = (unsigned char)(temp >> 16);
+        dest[2] = (unsigned char)(temp >> 24);
+
+        src += sourceStride;
+        dest += destinationStride * 3;
+    }
+*/
+
+    short savedFpuControlWord;
+
+    // spill storage:
+    signed long sourceByteStride;
+    signed long highpassedDither;
+
+    // dither state:
+    unsigned long ditherPrevious = ditherGenerator->previous;
+    unsigned long ditherRandSeed1 = ditherGenerator->randSeed1;
+    unsigned long ditherRandSeed2 = ditherGenerator->randSeed2;
+    
+    signed long tempInt32;
+                 
+    __asm{
+        // esi -> source ptr
+        // eax -> source byte stride
+        // edi -> destination ptr
+        // ebx -> destination byte stride
+        // ecx -> source end ptr
+        // edx -> temp
+
+        mov     esi, sourceBuffer
+
+        mov     edx, 4                  // sizeof float32
+        mov     eax, sourceStride
+        imul    eax, edx
+
+        mov     ecx, count
+        imul    ecx, eax
+        add     ecx, esi
+
+        mov     edi, destinationBuffer
+
+        mov     edx, 3                  // sizeof int24
+        mov     ebx, destinationStride
+        imul    ebx, edx
+
+        fwait
+        fstcw   savedFpuControlWord
+        fldcw   fpuControlWord_
+
+        fld     ditheredInt24Scaler_    // stack:  int scaler
+
+    Float32_To_Int24_DitherClip_loop:
+
+        mov     edx, dword ptr [esi]    // load floating point value into integer register
+
+        and     edx, 0x7FFFFFFF         // mask off sign
+        cmp     edx, 0x3F800000         // greater than 1.0 or less than -1.0
+
+        jg      Float32_To_Int24_DitherClip_clamp
+
+        // load unscaled value into st(0)
+        fld     dword ptr [esi]         // stack:  value, int scaler
+        add     esi, eax                // increment source ptr
+        //lea     esi, [esi+eax]
+        fmul    st(0), st(1)            // st(0) *= st(1), stack:  value*(int scaler), int scaler
+
+    /*
+        // call PaUtil_GenerateFloatTriangularDither with C calling convention
+        mov     sourceByteStride, eax   // save eax
+        mov     sourceEnd, ecx          // save ecx
+        push    ditherGenerator         // pass ditherGenerator parameter on stack
+	    call    PaUtil_GenerateFloatTriangularDither  // stack:  dither, value*(int scaler), int scaler
+	    pop     edx                     // clear parameter off stack
+        mov     ecx, sourceEnd          // restore ecx
+        mov     eax, sourceByteStride   // restore eax
+    */
+    
+    // generate dither
+        mov     sourceByteStride, eax   // save eax
+        mov     edx, 196314165
+        mov     eax, ditherRandSeed1
+        mul     edx                     // eax:edx = eax * 196314165
+        //add     eax, 907633515
+        lea     eax, [eax+907633515]
+        mov     ditherRandSeed1, eax
+        mov     edx, 196314165
+        mov     eax, ditherRandSeed2
+        mul     edx                     // eax:edx = eax * 196314165
+        //add     eax, 907633515
+        lea     eax, [eax+907633515]
+        mov     edx, ditherRandSeed1
+        shr     edx, PA_DITHER_SHIFT_
+        mov     ditherRandSeed2, eax
+        shr     eax, PA_DITHER_SHIFT_
+        //add     eax, edx                // eax -> current
+        lea     eax, [eax+edx]
+        mov     edx, ditherPrevious
+        neg     edx
+        lea     edx, [eax+edx]          // highpass = current - previous
+        mov     highpassedDither, edx
+        mov     ditherPrevious, eax     // previous = current
+        mov     eax, sourceByteStride   // restore eax
+        fild    highpassedDither
+        fmul    const_float_dither_scale_
+    // end generate dither, dither signal in st(0)
+
+        faddp   st(1), st(0)            // stack: dither * value*(int scaler), int scaler
+        fistp   tempInt32               // pop st(0) into tempInt32, stack:  int scaler
+        mov     edx, tempInt32
+        jmp     Float32_To_Int24_DitherClip_store
+    
+    Float32_To_Int24_DitherClip_clamp:
+        mov     edx, dword ptr [esi]    // load floating point value into integer register
+        shr     edx, 31                 // move sign bit into bit 0
+        add     esi, eax                // increment source ptr
+        //lea     esi, [esi+eax]
+        add     edx, 0x7FFFFF           // convert to maximum range integers
+
+    Float32_To_Int24_DitherClip_store:
+
+        mov     byte ptr [edi], DL
+        shr     edx, 8
+        //mov     byte ptr [edi+1], DL
+        //mov     byte ptr [edi+2], DH
+        mov     word ptr [edi+1], DX
+
+        //add     edi, ebx                // increment destination ptr
+        lea     edi, [edi+ebx]
+
+        cmp     esi, ecx                // has src ptr reached end?
+        jne     Float32_To_Int24_DitherClip_loop
+
+        ffree   st(0)
+        fincstp
+
+        fwait
+        fnclex
+        fldcw   savedFpuControlWord
+    }
+
+    ditherGenerator->previous = ditherPrevious;
+    ditherGenerator->randSeed1 = ditherRandSeed1;
+    ditherGenerator->randSeed2 = ditherRandSeed2;
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_Int16(
+    void *destinationBuffer, signed int destinationStride,
+    void *sourceBuffer, signed int sourceStride,
+    unsigned int count, PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+/*
+    float *src = (float*)sourceBuffer;
+    signed short *dest =  (signed short*)destinationBuffer;
+    (void)ditherGenerator; // unused parameter
+
+    while( count-- )
+    {
+
+        short samp = (short) (*src * (32767.0f));
+        *dest = samp;
+
+        src += sourceStride;
+        dest += destinationStride;
+    }
+*/
+
+    short savedFpuControlWord;
+   
+    (void) ditherGenerator; /* unused parameter */
+
+    __asm{
+        // esi -> source ptr
+        // eax -> source byte stride
+        // edi -> destination ptr
+        // ebx -> destination byte stride
+        // ecx -> source end ptr
+        // edx -> temp
+
+        mov     esi, sourceBuffer
+
+        mov     edx, 4                  // sizeof float32
+        mov     eax, sourceStride
+        imul    eax, edx                // source byte stride
+
+        mov     ecx, count
+        imul    ecx, eax
+        add     ecx, esi                // source end ptr = count * source byte stride + source ptr
+
+        mov     edi, destinationBuffer
+
+        mov     edx, 2                  // sizeof int16
+        mov     ebx, destinationStride
+        imul    ebx, edx                // destination byte stride
+
+        fwait
+        fstcw   savedFpuControlWord
+        fldcw   fpuControlWord_
+
+        fld     int16Scaler_            // stack:  (int)0x7FFF
+
+    Float32_To_Int16_loop:
+
+        // load unscaled value into st(0)
+        fld     dword ptr [esi]         // stack:  value, (int)0x7FFF
+        add     esi, eax                // increment source ptr
+        //lea     esi, [esi+eax]
+        fmul    st(0), st(1)            // st(0) *= st(1), stack:  value*0x7FFF, (int)0x7FFF
+        fistp   word ptr [edi]          // store scaled int into dest, stack:  (int)0x7FFF
+
+        add     edi, ebx                // increment destination ptr
+        //lea     edi, [edi+ebx]
+        
+        cmp     esi, ecx                // has src ptr reached end?
+        jne     Float32_To_Int16_loop
+
+        ffree   st(0)
+        fincstp
+
+        fwait
+        fnclex
+        fldcw   savedFpuControlWord
+    }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_Int16_Clip(
+    void *destinationBuffer, signed int destinationStride,
+    void *sourceBuffer, signed int sourceStride,
+    unsigned int count, PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+/*
+    float *src = (float*)sourceBuffer;
+    signed short *dest =  (signed short*)destinationBuffer;
+    (void)ditherGenerator; // unused parameter
+
+    while( count-- )
+    {
+        long samp = (signed long) (*src * (32767.0f));
+        PA_CLIP_( samp, -0x8000, 0x7FFF );
+        *dest = (signed short) samp;
+
+        src += sourceStride;
+        dest += destinationStride;
+    }
+*/
+
+    short savedFpuControlWord;
+   
+    (void) ditherGenerator; /* unused parameter */
+
+    __asm{
+        // esi -> source ptr
+        // eax -> source byte stride
+        // edi -> destination ptr
+        // ebx -> destination byte stride
+        // ecx -> source end ptr
+        // edx -> temp
+
+        mov     esi, sourceBuffer
+
+        mov     edx, 4                  // sizeof float32
+        mov     eax, sourceStride
+        imul    eax, edx                // source byte stride
+
+        mov     ecx, count
+        imul    ecx, eax
+        add     ecx, esi                // source end ptr = count * source byte stride + source ptr
+
+        mov     edi, destinationBuffer
+
+        mov     edx, 2                  // sizeof int16
+        mov     ebx, destinationStride
+        imul    ebx, edx                // destination byte stride
+
+        fwait
+        fstcw   savedFpuControlWord
+        fldcw   fpuControlWord_
+
+        fld     int16Scaler_            // stack:  (int)0x7FFF
+
+    Float32_To_Int16_Clip_loop:
+
+        mov     edx, dword ptr [esi]    // load floating point value into integer register
+
+        and     edx, 0x7FFFFFFF         // mask off sign
+        cmp     edx, 0x3F800000         // greater than 1.0 or less than -1.0
+
+        jg      Float32_To_Int16_Clip_clamp
+
+        // load unscaled value into st(0)
+        fld     dword ptr [esi]         // stack:  value, (int)0x7FFF
+        add     esi, eax                // increment source ptr
+        //lea     esi, [esi+eax]
+        fmul    st(0), st(1)            // st(0) *= st(1), stack:  value*0x7FFF, (int)0x7FFF
+        fistp   word ptr [edi]          // store scaled int into dest, stack:  (int)0x7FFF
+        jmp     Float32_To_Int16_Clip_stored
+    
+    Float32_To_Int16_Clip_clamp:
+        mov     edx, dword ptr [esi]    // load floating point value into integer register
+        shr     edx, 31                 // move sign bit into bit 0
+        add     esi, eax                // increment source ptr
+        //lea     esi, [esi+eax]
+        add     dx, 0x7FFF              // convert to maximum range integers
+        mov     word ptr [edi], dx      // store clamped into into dest
+
+    Float32_To_Int16_Clip_stored:
+
+        add     edi, ebx                // increment destination ptr
+        //lea     edi, [edi+ebx]
+        
+        cmp     esi, ecx                // has src ptr reached end?
+        jne     Float32_To_Int16_Clip_loop
+
+        ffree   st(0)
+        fincstp
+
+        fwait
+        fnclex
+        fldcw   savedFpuControlWord
+    }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_Int16_DitherClip(
+    void *destinationBuffer, signed int destinationStride,
+    void *sourceBuffer, signed int sourceStride,
+    unsigned int count, PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+/*
+    float *src = (float*)sourceBuffer;
+    signed short *dest =  (signed short*)destinationBuffer;
+    (void)ditherGenerator; // unused parameter
+
+    while( count-- )
+    {
+
+        float dither  = PaUtil_GenerateFloatTriangularDither( ditherGenerator );
+        // use smaller scaler to prevent overflow when we add the dither 
+        float dithered = (*src * (32766.0f)) + dither;
+        signed long samp = (signed long) dithered;
+        PA_CLIP_( samp, -0x8000, 0x7FFF );
+        *dest = (signed short) samp;
+
+        src += sourceStride;
+        dest += destinationStride;
+    }
+*/
+
+    short savedFpuControlWord;
+
+    // spill storage:
+    signed long sourceByteStride;
+    signed long highpassedDither;
+
+    // dither state:
+    unsigned long ditherPrevious = ditherGenerator->previous;
+    unsigned long ditherRandSeed1 = ditherGenerator->randSeed1;
+    unsigned long ditherRandSeed2 = ditherGenerator->randSeed2;
+
+    __asm{
+        // esi -> source ptr
+        // eax -> source byte stride
+        // edi -> destination ptr
+        // ebx -> destination byte stride
+        // ecx -> source end ptr
+        // edx -> temp
+
+        mov     esi, sourceBuffer
+
+        mov     edx, 4                  // sizeof float32
+        mov     eax, sourceStride
+        imul    eax, edx                // source byte stride
+
+        mov     ecx, count
+        imul    ecx, eax
+        add     ecx, esi                // source end ptr = count * source byte stride + source ptr
+
+        mov     edi, destinationBuffer
+
+        mov     edx, 2                  // sizeof int16
+        mov     ebx, destinationStride
+        imul    ebx, edx                // destination byte stride
+
+        fwait
+        fstcw   savedFpuControlWord
+        fldcw   fpuControlWord_
+
+        fld     ditheredInt16Scaler_    // stack:  int scaler
+
+    Float32_To_Int16_DitherClip_loop:
+
+        mov     edx, dword ptr [esi]    // load floating point value into integer register
+
+        and     edx, 0x7FFFFFFF         // mask off sign
+        cmp     edx, 0x3F800000         // greater than 1.0 or less than -1.0
+
+        jg      Float32_To_Int16_DitherClip_clamp
+
+        // load unscaled value into st(0)
+        fld     dword ptr [esi]         // stack:  value, int scaler
+        add     esi, eax                // increment source ptr
+        //lea     esi, [esi+eax]
+        fmul    st(0), st(1)            // st(0) *= st(1), stack:  value*(int scaler), int scaler
+
+        /*
+        // call PaUtil_GenerateFloatTriangularDither with C calling convention
+        mov     sourceByteStride, eax   // save eax
+        mov     sourceEnd, ecx          // save ecx
+        push    ditherGenerator         // pass ditherGenerator parameter on stack
+	    call    PaUtil_GenerateFloatTriangularDither  // stack:  dither, value*(int scaler), int scaler
+	    pop     edx                     // clear parameter off stack
+        mov     ecx, sourceEnd          // restore ecx
+        mov     eax, sourceByteStride   // restore eax
+        */
+
+    // generate dither
+        mov     sourceByteStride, eax   // save eax
+        mov     edx, 196314165
+        mov     eax, ditherRandSeed1
+        mul     edx                     // eax:edx = eax * 196314165
+        //add     eax, 907633515
+        lea     eax, [eax+907633515]
+        mov     ditherRandSeed1, eax
+        mov     edx, 196314165
+        mov     eax, ditherRandSeed2
+        mul     edx                     // eax:edx = eax * 196314165
+        //add     eax, 907633515
+        lea     eax, [eax+907633515]
+        mov     edx, ditherRandSeed1
+        shr     edx, PA_DITHER_SHIFT_
+        mov     ditherRandSeed2, eax
+        shr     eax, PA_DITHER_SHIFT_
+        //add     eax, edx                // eax -> current
+        lea     eax, [eax+edx]            // current = randSeed1>>x + randSeed2>>x
+        mov     edx, ditherPrevious
+        neg     edx
+        lea     edx, [eax+edx]          // highpass = current - previous
+        mov     highpassedDither, edx
+        mov     ditherPrevious, eax     // previous = current
+        mov     eax, sourceByteStride   // restore eax
+        fild    highpassedDither
+        fmul    const_float_dither_scale_
+    // end generate dither, dither signal in st(0)
+        
+        faddp   st(1), st(0)            // stack: dither * value*(int scaler), int scaler
+        fistp   word ptr [edi]          // store scaled int into dest, stack:  int scaler
+        jmp     Float32_To_Int16_DitherClip_stored
+    
+    Float32_To_Int16_DitherClip_clamp:
+        mov     edx, dword ptr [esi]    // load floating point value into integer register
+        shr     edx, 31                 // move sign bit into bit 0
+        add     esi, eax                // increment source ptr
+        //lea     esi, [esi+eax]
+        add     dx, 0x7FFF              // convert to maximum range integers
+        mov     word ptr [edi], dx      // store clamped into into dest
+
+    Float32_To_Int16_DitherClip_stored:
+
+        add     edi, ebx                // increment destination ptr
+        //lea     edi, [edi+ebx]
+        
+        cmp     esi, ecx                // has src ptr reached end?
+        jne     Float32_To_Int16_DitherClip_loop
+
+        ffree   st(0)
+        fincstp
+
+        fwait
+        fnclex
+        fldcw   savedFpuControlWord
+    }
+
+    ditherGenerator->previous = ditherPrevious;
+    ditherGenerator->randSeed1 = ditherRandSeed1;
+    ditherGenerator->randSeed2 = ditherRandSeed2;
+}
+
+/* -------------------------------------------------------------------------- */
+
+void PaUtil_InitializeX86PlainConverters( void )
+{
+    paConverters.Float32_To_Int32 = Float32_To_Int32;
+    paConverters.Float32_To_Int32_Clip = Float32_To_Int32_Clip;
+    paConverters.Float32_To_Int32_DitherClip = Float32_To_Int32_DitherClip;
+
+    paConverters.Float32_To_Int24 = Float32_To_Int24;
+    paConverters.Float32_To_Int24_Clip = Float32_To_Int24_Clip;
+    paConverters.Float32_To_Int24_DitherClip = Float32_To_Int24_DitherClip;
+    
+    paConverters.Float32_To_Int16 = Float32_To_Int16;
+    paConverters.Float32_To_Int16_Clip = Float32_To_Int16_Clip;
+    paConverters.Float32_To_Int16_DitherClip = Float32_To_Int16_DitherClip;
+}
+
+/* -------------------------------------------------------------------------- */
diff --git a/src/audio/portaudio/pa_win/pa_x86_plain_converters.h b/src/audio/portaudio/pa_win/pa_x86_plain_converters.h
new file mode 100644
index 0000000000000000000000000000000000000000..f56c710fda5885470185effb670de69882dfdb81
--- /dev/null
+++ b/src/audio/portaudio/pa_win/pa_x86_plain_converters.h
@@ -0,0 +1,19 @@
+#ifndef PA_X86_PLAIN_CONVERTERS_H
+#define PA_X86_PLAIN_CONVERTERS_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif /* __cplusplus */
+
+
+/**
+ @brief Install optimised converter functions suitable for all IA32 processors
+*/
+void PaUtil_InitializeX86PlainConverters( void );
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif /* PA_X86_PLAIN_CONVERTERS_H */
diff --git a/src/audio/portaudio/pa_win_ds/dsound_wrapper.c b/src/audio/portaudio/pa_win_ds/dsound_wrapper.c
new file mode 100644
index 0000000000000000000000000000000000000000..e29019a8d5c275e965558b30cd11f9b8aa5718e4
--- /dev/null
+++ b/src/audio/portaudio/pa_win_ds/dsound_wrapper.c
@@ -0,0 +1,616 @@
+/*
+ * $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>
+
+#include "dsound_wrapper.h"
+#include "pa_trace.h"
+
+/*
+    Rather than linking with dxguid.a or using "#define INITGUID" to force a
+    header file to instantiate the required GUID(s), we define them directly
+    below.
+*/
+#include <initguid.h> // needed for the DEFINE_GUID macro
+DEFINE_GUID(IID_IDirectSoundNotify, 0xb0210783, 0x89cd, 0x11d0, 0xaf, 0x8, 0x0, 0xa0, 0xc9, 0x25, 0xcd, 0x16);
+
+
+/************************************************************************************/
+DSoundEntryPoints dswDSoundEntryPoints = { 0, 0, 0, 0, 0, 0, 0 };
+/************************************************************************************/
+static HRESULT WINAPI DummyDirectSoundCreate(LPGUID lpcGuidDevice, LPDIRECTSOUND *ppDS, LPUNKNOWN pUnkOuter)
+{
+    (void)lpcGuidDevice; /* unused parameter */
+    (void)ppDS; /* unused parameter */
+    (void)pUnkOuter; /* unused parameter */
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI DummyDirectSoundEnumerateW(LPDSENUMCALLBACKW lpDSEnumCallback, LPVOID lpContext)
+{
+    (void)lpDSEnumCallback; /* unused parameter */
+    (void)lpContext; /* unused parameter */
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI DummyDirectSoundEnumerateA(LPDSENUMCALLBACKA lpDSEnumCallback, LPVOID lpContext)
+{
+    (void)lpDSEnumCallback; /* unused parameter */
+    (void)lpContext; /* unused parameter */
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI DummyDirectSoundCaptureCreate(LPGUID lpcGUID, LPDIRECTSOUNDCAPTURE *lplpDSC, LPUNKNOWN pUnkOuter)
+{
+    (void)lpcGUID; /* unused parameter */
+    (void)lplpDSC; /* unused parameter */
+    (void)pUnkOuter; /* unused parameter */
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI DummyDirectSoundCaptureEnumerateW(LPDSENUMCALLBACKW lpDSCEnumCallback, LPVOID lpContext)
+{
+    (void)lpDSCEnumCallback; /* unused parameter */
+    (void)lpContext; /* unused parameter */
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI DummyDirectSoundCaptureEnumerateA(LPDSENUMCALLBACKA lpDSCEnumCallback, LPVOID lpContext)
+{
+    (void)lpDSCEnumCallback; /* unused parameter */
+    (void)lpContext; /* unused parameter */
+    return E_NOTIMPL;
+}
+/************************************************************************************/
+void DSW_InitializeDSoundEntryPoints(void)
+{
+    dswDSoundEntryPoints.hInstance_ = LoadLibrary("dsound.dll");
+    if( dswDSoundEntryPoints.hInstance_ != NULL )
+    {
+        dswDSoundEntryPoints.DirectSoundCreate =
+                (HRESULT (WINAPI *)(LPGUID, LPDIRECTSOUND *, LPUNKNOWN))
+                GetProcAddress( dswDSoundEntryPoints.hInstance_, "DirectSoundCreate" );
+        if( dswDSoundEntryPoints.DirectSoundCreate == NULL )
+            dswDSoundEntryPoints.DirectSoundCreate = DummyDirectSoundCreate;
+
+        dswDSoundEntryPoints.DirectSoundEnumerateW =
+                (HRESULT (WINAPI *)(LPDSENUMCALLBACKW, LPVOID))
+                GetProcAddress( dswDSoundEntryPoints.hInstance_, "DirectSoundEnumerateW" );
+        if( dswDSoundEntryPoints.DirectSoundEnumerateW == NULL )
+            dswDSoundEntryPoints.DirectSoundEnumerateW = DummyDirectSoundEnumerateW;
+
+        dswDSoundEntryPoints.DirectSoundEnumerateA =
+                (HRESULT (WINAPI *)(LPDSENUMCALLBACKA, LPVOID))
+                GetProcAddress( dswDSoundEntryPoints.hInstance_, "DirectSoundEnumerateA" );
+        if( dswDSoundEntryPoints.DirectSoundEnumerateA == NULL )
+            dswDSoundEntryPoints.DirectSoundEnumerateA = DummyDirectSoundEnumerateA;
+
+        dswDSoundEntryPoints.DirectSoundCaptureCreate =
+                (HRESULT (WINAPI *)(LPGUID, LPDIRECTSOUNDCAPTURE *, LPUNKNOWN))
+                GetProcAddress( dswDSoundEntryPoints.hInstance_, "DirectSoundCaptureCreate" );
+        if( dswDSoundEntryPoints.DirectSoundCaptureCreate == NULL )
+            dswDSoundEntryPoints.DirectSoundCaptureCreate = DummyDirectSoundCaptureCreate;
+
+        dswDSoundEntryPoints.DirectSoundCaptureEnumerateW =
+                (HRESULT (WINAPI *)(LPDSENUMCALLBACKW, LPVOID))
+                GetProcAddress( dswDSoundEntryPoints.hInstance_, "DirectSoundCaptureEnumerateW" );
+        if( dswDSoundEntryPoints.DirectSoundCaptureEnumerateW == NULL )
+            dswDSoundEntryPoints.DirectSoundCaptureEnumerateW = DummyDirectSoundCaptureEnumerateW;
+
+        dswDSoundEntryPoints.DirectSoundCaptureEnumerateA =
+                (HRESULT (WINAPI *)(LPDSENUMCALLBACKA, LPVOID))
+                GetProcAddress( dswDSoundEntryPoints.hInstance_, "DirectSoundCaptureEnumerateA" );
+        if( dswDSoundEntryPoints.DirectSoundCaptureEnumerateA == NULL )
+            dswDSoundEntryPoints.DirectSoundCaptureEnumerateA = DummyDirectSoundCaptureEnumerateA;
+    }
+    else
+    {
+        /* initialize with dummy entry points to make live easy when ds isn't present */
+        dswDSoundEntryPoints.DirectSoundCreate = DummyDirectSoundCreate;
+        dswDSoundEntryPoints.DirectSoundEnumerateW = DummyDirectSoundEnumerateW;
+        dswDSoundEntryPoints.DirectSoundEnumerateA = DummyDirectSoundEnumerateA;
+        dswDSoundEntryPoints.DirectSoundCaptureCreate = DummyDirectSoundCaptureCreate;
+        dswDSoundEntryPoints.DirectSoundCaptureEnumerateW = DummyDirectSoundCaptureEnumerateW;
+        dswDSoundEntryPoints.DirectSoundCaptureEnumerateA = DummyDirectSoundCaptureEnumerateA;
+    }
+}
+/************************************************************************************/
+void DSW_TerminateDSoundEntryPoints(void)
+{
+    if( dswDSoundEntryPoints.hInstance_ != NULL )
+    {
+        FreeLibrary( dswDSoundEntryPoints.hInstance_ );
+        dswDSoundEntryPoints.hInstance_ = NULL;
+        /* ensure that we crash reliably if the entry points arent initialised */
+        dswDSoundEntryPoints.DirectSoundCreate = 0;
+        dswDSoundEntryPoints.DirectSoundEnumerateW = 0;
+        dswDSoundEntryPoints.DirectSoundEnumerateA = 0;
+        dswDSoundEntryPoints.DirectSoundCaptureCreate = 0;
+        dswDSoundEntryPoints.DirectSoundCaptureEnumerateW = 0;
+        dswDSoundEntryPoints.DirectSoundCaptureEnumerateA = 0;
+    }
+}
+/************************************************************************************/
+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 (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;
+    }
+
+    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 = dswDSoundEntryPoints.DirectSoundCreate( lpGUID, &dsw->dsw_pDirectSound, NULL );
+    if( hr != DS_OK ) return hr;
+    return hr;
+}
+
+/************************************************************************************/
+HRESULT DSW_InitOutputBuffer( DSoundWrapper *dsw, unsigned long nFrameRate, WORD 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_BytesPerOutputFrame = 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();
+    //
+    //  FIXME: The example code I have on the net creates a hidden window that
+    //      is managed by our code - I think we should do that - one hidden
+    //      window for the whole of Pa_DS
+    //
+    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 = (WORD)(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;
+    }
+    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_BytesPerOutputFrame;
+    /* 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_QueryOutputFilled( DSoundWrapper *dsw, long *bytesFilledPtr )
+{
+    HRESULT hr;
+    DWORD   playCursor;
+    DWORD   writeCursor;
+    long    bytesFilled;
+    // Query to see where play position is.
+    // We don't need the writeCursor but sometimes DirectSound doesn't handle NULLS correctly
+    // so let's pass a pointer just to be safe.
+    hr = IDirectSoundBuffer_GetCurrentPosition( dsw->dsw_OutputBuffer, &playCursor, &writeCursor );
+    if( hr != DS_OK )
+    {
+        return hr;
+    }
+    bytesFilled = dsw->dsw_WriteOffset - playCursor;
+    if( bytesFilled < 0 ) bytesFilled += dsw->dsw_OutputSize; // unwrap offset
+    *bytesFilledPtr = bytesFilled;
+    return hr;
+}
+
+/************************************************************************************
+ * Determine how much space can be safely written to in DS buffer.
+ * Detect underflows and overflows.
+ * Does not allow writing into safety gap maintained by DirectSound.
+ */
+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.
+    hr = IDirectSoundBuffer_GetCurrentPosition( dsw->dsw_OutputBuffer, &playCursor, &writeCursor );
+    if( hr != DS_OK )
+    {
+        return hr;
+    }
+    // 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 )
+        {
+            playCursor += (buffersWrapped * dsw->dsw_OutputSize);
+            bytesPlayed += (buffersWrapped * dsw->dsw_OutputSize);
+        }
+        /* Maintain frame output cursor. */
+        dsw->dsw_FramesPlayed += (bytesPlayed / dsw->dsw_BytesPerOutputFrame);
+    }
+    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;
+        }
+        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_BytesPerOutputFrame;
+    }
+    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_BytesPerOutputFrame;
+    }
+    return hr;
+}
+
+/************************************************************************************/
+DWORD DSW_GetOutputStatus( DSoundWrapper *dsw )
+{
+    DWORD status;
+    if (IDirectSoundBuffer_GetStatus( dsw->dsw_OutputBuffer, &status ) != DS_OK)
+        return( DSERR_INVALIDPARAM );
+    else
+        return( status );
+}
+
+/* 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 = dswDSoundEntryPoints.DirectSoundCaptureCreate(  lpGUID, &dsw->dsw_pDirectSoundCapture,   NULL );
+    if( hr != DS_OK ) return hr;
+    return hr;
+}
+/************************************************************************************/
+HRESULT DSW_InitInputBuffer( DSoundWrapper *dsw, unsigned long nFrameRate, WORD nChannels, int bytesPerBuffer )
+{
+    DSCBUFFERDESC  captureDesc;
+    WAVEFORMATEX   wfFormat;
+    HRESULT        result;
+    
+    dsw->dsw_BytesPerInputFrame = nChannels * sizeof(short);
+
+    // Define the buffer format
+    wfFormat.wFormatTag      = WAVE_FORMAT_PCM;
+    wfFormat.nChannels       = nChannels;
+    wfFormat.nSamplesPerSec  = nFrameRate;
+    wfFormat.wBitsPerSample  = 8 * sizeof(short);
+    wfFormat.nBlockAlign     = (WORD)(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;
+}
+
diff --git a/src/audio/portaudio/pa_win_ds/dsound_wrapper.h b/src/audio/portaudio/pa_win_ds/dsound_wrapper.h
new file mode 100644
index 0000000000000000000000000000000000000000..713f4a71deb5ee818c9d56237bb4fd6c96ba8288
--- /dev/null
+++ b/src/audio/portaudio/pa_win_ds/dsound_wrapper.h
@@ -0,0 +1,130 @@
+#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.
+ *
+ */
+
+/* on Borland compilers, WIN32 doesn't seem to be defined by default, which
+    breaks DSound.h. Adding the define here fixes the problem. - rossb. */
+#ifdef __BORLANDC__
+#if !defined(WIN32)
+#define WIN32
+#endif
+#endif
+
+/*
+  We are only using DX3 in here, no need to polute the namespace - davidv
+*/
+#define DIRECTSOUND_VERSION 0x0300
+
+#include <DSound.h>
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif /* __cplusplus */
+
+
+typedef struct
+{
+    HINSTANCE hInstance_;
+    
+    HRESULT (WINAPI *DirectSoundCreate)(LPGUID, LPDIRECTSOUND *, LPUNKNOWN);
+    HRESULT (WINAPI *DirectSoundEnumerateW)(LPDSENUMCALLBACKW, LPVOID);
+    HRESULT (WINAPI *DirectSoundEnumerateA)(LPDSENUMCALLBACKA, LPVOID);
+
+    HRESULT (WINAPI *DirectSoundCaptureCreate)(LPGUID, LPDIRECTSOUNDCAPTURE *, LPUNKNOWN);
+    HRESULT (WINAPI *DirectSoundCaptureEnumerateW)(LPDSENUMCALLBACKW, LPVOID);
+    HRESULT (WINAPI *DirectSoundCaptureEnumerateA)(LPDSENUMCALLBACKA, LPVOID);
+}DSoundEntryPoints;
+
+extern DSoundEntryPoints dswDSoundEntryPoints;
+
+void DSW_InitializeDSoundEntryPoints(void);
+void DSW_TerminateDSoundEntryPoints(void);
+
+#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_BytesPerOutputFrame;
+    /* 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;
+/* Input */
+    INT                  dsw_BytesPerInputFrame;
+    LPDIRECTSOUNDCAPTURE dsw_pDirectSoundCapture;
+    LPDIRECTSOUNDCAPTUREBUFFER   dsw_InputBuffer;
+    UINT                 dsw_ReadOffset;      /* last read position */
+    UINT                 dsw_InputSize;
+} DSoundWrapper;
+
+HRESULT DSW_Init( DSoundWrapper *dsw );
+void DSW_Term( DSoundWrapper *dsw );
+HRESULT DSW_InitOutputBuffer( DSoundWrapper *dsw, unsigned long nFrameRate,
+                              WORD 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 );
+
+HRESULT DSW_InitInputBuffer( DSoundWrapper *dsw, unsigned long nFrameRate,
+                             WORD 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 );
+HRESULT DSW_QueryOutputFilled( DSoundWrapper *dsw, long *bytesFilled );
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif  /* __DSOUND_WRAPPER_H */
diff --git a/src/audio/portaudio/pa_win_ds/pa_win_ds.c b/src/audio/portaudio/pa_win_ds/pa_win_ds.c
new file mode 100644
index 0000000000000000000000000000000000000000..69e53a06574c3a8fdcbdcc7e4a737ee9b2f44b79
--- /dev/null
+++ b/src/audio/portaudio/pa_win_ds/pa_win_ds.c
@@ -0,0 +1,1822 @@
+/*
+ * $Id$
+ * Portable Audio I/O Library DirectSound implementation
+ *
+ * Based on the Open Source API proposed by Ross Bencina
+ * Copyright (c) 1999-2002 Ross Bencina, 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.
+ */
+
+/** @file
+
+    @todo implement paInputOverflow callback status flag
+    
+    @todo implement paNeverDropInput.
+
+    @todo implement host api specific extension to set i/o buffer sizes in frames
+
+    @todo implement initialisation of PaDeviceInfo default*Latency fields (currently set to 0.)
+
+    @todo implement ReadStream, WriteStream, GetStreamReadAvailable, GetStreamWriteAvailable
+
+    @todo audit handling of DirectSound result codes - in many cases we could convert a HRESULT into
+        a native portaudio error code. Standard DirectSound result codes are documented at msdn.
+
+    @todo implement IsFormatSupported
+
+    @todo check that CoInitialize() CoUninitialize() are always correctly
+        paired, even in error cases.
+
+    @todo call PaUtil_SetLastHostErrorInfo with a specific error string (currently just "DSound error").
+
+    @todo make sure all buffers have been played before stopping the stream
+        when the stream callback returns paComplete
+
+    old TODOs from phil, need to work out if these have been done:
+        O- fix "patest_stop.c"
+*/
+
+#include <stdio.h>
+#include <string.h> /* strlen() */
+
+#include "pa_util.h"
+#include "pa_allocation.h"
+#include "pa_hostapi.h"
+#include "pa_stream.h"
+#include "pa_cpuload.h"
+#include "pa_process.h"
+
+#include "dsound_wrapper.h"
+
+#if (defined(WIN32) && (defined(_MSC_VER) && (_MSC_VER >= 1200))) /* MSC version 6 and above */
+#pragma comment( lib, "dsound.lib" )
+#pragma comment( lib, "winmm.lib" )
+#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 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
+
+#define PA_WIN_WDM_LATENCY       (120)
+
+#define SECONDS_PER_MSEC      (0.001)
+#define MSEC_PER_SECOND       (1000)
+
+/* prototypes for functions declared in this file */
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif /* __cplusplus */
+
+PaError PaWinDs_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+static void Terminate( struct PaUtilHostApiRepresentation *hostApi );
+static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
+                           PaStream** s,
+                           const PaStreamParameters *inputParameters,
+                           const PaStreamParameters *outputParameters,
+                           double sampleRate,
+                           unsigned long framesPerBuffer,
+                           PaStreamFlags streamFlags,
+                           PaStreamCallback *streamCallback,
+                           void *userData );
+static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
+                                  const PaStreamParameters *inputParameters,
+                                  const PaStreamParameters *outputParameters,
+                                  double sampleRate );
+static PaError CloseStream( PaStream* stream );
+static PaError StartStream( PaStream *stream );
+static PaError StopStream( PaStream *stream );
+static PaError AbortStream( PaStream *stream );
+static PaError IsStreamStopped( PaStream *s );
+static PaError IsStreamActive( PaStream *stream );
+static PaTime GetStreamTime( PaStream *stream );
+static double GetStreamCpuLoad( PaStream* stream );
+static PaError ReadStream( PaStream* stream, void *buffer, unsigned long frames );
+static PaError WriteStream( PaStream* stream, const void *buffer, unsigned long frames );
+static signed long GetStreamReadAvailable( PaStream* stream );
+static signed long GetStreamWriteAvailable( PaStream* stream );
+
+
+/* FIXME: should convert hr to a string */
+#define PA_DS_SET_LAST_DIRECTSOUND_ERROR( hr ) \
+    PaUtil_SetLastHostErrorInfo( paDirectSound, hr, "DirectSound error" )
+
+/************************************************* DX Prototypes **********/
+static BOOL CALLBACK CollectGUIDsProc(LPGUID lpGUID,
+                                     LPCTSTR lpszDesc,
+                                     LPCTSTR lpszDrvName,
+                                     LPVOID lpContext );
+
+/************************************************************************************/
+/********************** Structures **************************************************/
+/************************************************************************************/
+/* PaWinDsHostApiRepresentation - host api datastructure specific to this implementation */
+
+typedef struct PaWinDsDeviceInfo
+{
+    GUID                             guid;
+    GUID                            *lpGUID;
+    double                           sampleRates[3];
+} PaWinDsDeviceInfo;
+
+typedef struct
+{
+    PaUtilHostApiRepresentation inheritedHostApiRep;
+    PaUtilStreamInterface    callbackStreamInterface;
+    PaUtilStreamInterface    blockingStreamInterface;
+
+    PaUtilAllocationGroup   *allocations;
+
+    /* implementation specific data goes here */
+    PaWinDsDeviceInfo       *winDsDeviceInfos;
+
+} PaWinDsHostApiRepresentation;
+
+/* PaWinDsStream - a stream data structure specifically for this implementation */
+
+typedef struct PaWinDsStream
+{
+    PaUtilStreamRepresentation streamRepresentation;
+    PaUtilCpuLoadMeasurer cpuLoadMeasurer;
+    PaUtilBufferProcessor bufferProcessor;
+
+/* DirectSound specific data. */
+    DSoundWrapper    directSoundWrapper;
+    MMRESULT         timerID;
+    BOOL             ifInsideCallback;  /* Test for reentrancy. */
+    int              framesPerDSBuffer;
+    double           framesWritten;
+    double           secondsPerHostByte; /* Used to optimize latency calculation for outTime */
+
+    PaStreamCallbackFlags callbackFlags;
+    
+/* FIXME - move all below to PaUtilStreamRepresentation */
+    volatile int     isStarted;
+    volatile int     isActive;
+    volatile int     stopProcessing; /* stop thread once existing buffers have been returned */
+    volatile int     abortProcessing; /* stop thread immediately */
+} PaWinDsStream;
+
+
+/************************************************************************************
+** Duplicate the input string using the allocations allocator.
+** A NULL string is converted to a zero length string.
+** If memory cannot be allocated, NULL is returned.
+**/
+static char *DuplicateDeviceNameString( PaUtilAllocationGroup *allocations, const char* src )
+{
+    char *result = 0;
+    
+    if( src != NULL )
+    {
+        size_t len = strlen(src);
+        result = (char*)PaUtil_GroupAllocateMemory( allocations, (long)(len + 1) );
+        if( result )
+            memcpy( (void *) result, src, len+1 );
+    }
+    else
+    {
+        result = (char*)PaUtil_GroupAllocateMemory( allocations, 1 );
+        if( result )
+            result[0] = '\0';
+    }
+
+    return result;
+}
+
+/************************************************************************************
+** DSDeviceNameAndGUID, DSDeviceNameAndGUIDVector used for collecting preliminary
+** information during device enumeration.
+*/
+typedef struct DSDeviceNameAndGUID{
+    char *name; // allocated from parent's allocations, never deleted by this structure
+    GUID guid;
+    LPGUID lpGUID;
+} DSDeviceNameAndGUID;
+
+typedef struct DSDeviceNameAndGUIDVector{
+    PaUtilAllocationGroup *allocations;
+    PaError enumerationError;
+
+    int count;
+    int free;
+    DSDeviceNameAndGUID *items; // Allocated using LocalAlloc()
+} DSDeviceNameAndGUIDVector;
+
+static PaError InitializeDSDeviceNameAndGUIDVector(
+        DSDeviceNameAndGUIDVector *guidVector, PaUtilAllocationGroup *allocations )
+{
+    PaError result = paNoError;
+
+    guidVector->allocations = allocations;
+    guidVector->enumerationError = paNoError;
+
+    guidVector->count = 0;
+    guidVector->free = 8;
+    guidVector->items = (DSDeviceNameAndGUID*)LocalAlloc( LMEM_FIXED, sizeof(DSDeviceNameAndGUID) * guidVector->free );
+    if( guidVector->items == NULL )
+        result = paInsufficientMemory;
+    
+    return result;
+}
+
+static PaError ExpandDSDeviceNameAndGUIDVector( DSDeviceNameAndGUIDVector *guidVector )
+{
+    PaError result = paNoError;
+    DSDeviceNameAndGUID *newItems;
+    int i;
+    
+    /* double size of vector */
+    int size = guidVector->count + guidVector->free;
+    guidVector->free += size;
+
+    newItems = (DSDeviceNameAndGUID*)LocalAlloc( LMEM_FIXED, sizeof(DSDeviceNameAndGUID) * size * 2 );
+    if( newItems == NULL )
+    {
+        result = paInsufficientMemory;
+    }
+    else
+    {
+        for( i=0; i < guidVector->count; ++i )
+        {
+            newItems[i].name = guidVector->items[i].name;
+            if( guidVector->items[i].lpGUID == NULL )
+            {
+                newItems[i].lpGUID = NULL;
+            }
+            else
+            {
+                newItems[i].lpGUID = &newItems[i].guid;
+                memcpy( &newItems[i].guid, guidVector->items[i].lpGUID, sizeof(GUID) );;
+            }
+        }
+
+        LocalFree( guidVector->items );
+        guidVector->items = newItems;
+    }                                
+
+    return result;
+}
+
+/*
+    it's safe to call DSDeviceNameAndGUIDVector multiple times
+*/
+static PaError TerminateDSDeviceNameAndGUIDVector( DSDeviceNameAndGUIDVector *guidVector )
+{
+    PaError result = paNoError;
+
+    if( guidVector->items != NULL )
+    {
+        if( LocalFree( guidVector->items ) != NULL )
+            result = paInsufficientMemory;              /** @todo this isn't the correct error to return from a deallocation failure */
+
+        guidVector->items = NULL;
+    }
+
+    return result;
+}
+
+/************************************************************************************
+** Collect preliminary device information during DirectSound enumeration 
+*/
+static BOOL CALLBACK CollectGUIDsProc(LPGUID lpGUID,
+                                     LPCTSTR lpszDesc,
+                                     LPCTSTR lpszDrvName,
+                                     LPVOID lpContext )
+{
+    DSDeviceNameAndGUIDVector *namesAndGUIDs = (DSDeviceNameAndGUIDVector*)lpContext;
+    PaError error;
+
+    (void) lpszDrvName; /* unused variable */
+
+    if( namesAndGUIDs->free == 0 )
+    {
+        error = ExpandDSDeviceNameAndGUIDVector( namesAndGUIDs );
+        if( error != paNoError )
+        {
+            namesAndGUIDs->enumerationError = error;
+            return FALSE;
+        }
+    }
+    
+    /* Set GUID pointer, copy GUID to storage in DSDeviceNameAndGUIDVector. */
+    if( lpGUID == NULL )
+    {
+        namesAndGUIDs->items[namesAndGUIDs->count].lpGUID = NULL;
+    }
+    else
+    {
+        namesAndGUIDs->items[namesAndGUIDs->count].lpGUID =
+                &namesAndGUIDs->items[namesAndGUIDs->count].guid;
+      
+        memcpy( &namesAndGUIDs->items[namesAndGUIDs->count].guid, lpGUID, sizeof(GUID) );
+    }
+
+    namesAndGUIDs->items[namesAndGUIDs->count].name =
+            DuplicateDeviceNameString( namesAndGUIDs->allocations, lpszDesc );
+    if( namesAndGUIDs->items[namesAndGUIDs->count].name == NULL )
+    {
+        namesAndGUIDs->enumerationError = paInsufficientMemory;
+        return FALSE;
+    }
+
+    ++namesAndGUIDs->count;
+    --namesAndGUIDs->free;
+    
+    return TRUE;
+}
+
+
+#define PA_DEFAULTSAMPLERATESEARCHORDER_COUNT_  (13) /* must match array length below */
+static double defaultSampleRateSearchOrder_[] =
+    { 44100.0, 48000.0, 32000.0, 24000.0, 22050.0, 88200.0, 96000.0, 192000.0,
+        16000.0, 12000.0, 11025.0, 9600.0, 8000.0 };
+
+
+/************************************************************************************
+** Extract capabilities from an output device, and add it to the device info list
+** if successful. This function assumes that there is enough room in the
+** device info list to accomodate all entries.
+**
+** The device will not be added to the device list if any errors are encountered.
+*/
+static PaError AddOutputDeviceInfoFromDirectSound(
+        PaWinDsHostApiRepresentation *winDsHostApi, char *name, LPGUID lpGUID )
+{
+    PaUtilHostApiRepresentation  *hostApi = &winDsHostApi->inheritedHostApiRep;
+    PaDeviceInfo                 *deviceInfo = hostApi->deviceInfos[hostApi->info.deviceCount];
+    PaWinDsDeviceInfo            *winDsDeviceInfo = &winDsHostApi->winDsDeviceInfos[hostApi->info.deviceCount];
+    HRESULT                       hr;
+    LPDIRECTSOUND                 lpDirectSound;
+    DSCAPS                        caps;
+    int                           deviceOK = TRUE;
+    PaError                       result = paNoError;
+    int                           i;
+    
+    /* Copy GUID to the device info structure. Set pointer. */
+    if( lpGUID == NULL )
+    {
+        winDsDeviceInfo->lpGUID = NULL;
+    }
+    else
+    {
+        memcpy( &winDsDeviceInfo->guid, lpGUID, sizeof(GUID) );
+        winDsDeviceInfo->lpGUID = &winDsDeviceInfo->guid;
+    }
+
+    
+    /* Create a DirectSound object for the specified GUID
+        Note that using CoCreateInstance doesn't work on windows CE.
+    */
+    hr = dswDSoundEntryPoints.DirectSoundCreate( lpGUID, &lpDirectSound, NULL );
+
+    /** try using CoCreateInstance because DirectSoundCreate was hanging under
+        some circumstances - note this was probably related to the
+        #define BOOL short bug which has now been fixed
+        @todo delete this comment and the following code once we've ensured
+        there is no bug.
+    */
+    /*
+    hr = CoCreateInstance( &CLSID_DirectSound, NULL, CLSCTX_INPROC_SERVER,
+            &IID_IDirectSound, (void**)&lpDirectSound );
+
+    if( hr == S_OK )
+    {
+        hr = IDirectSound_Initialize( lpDirectSound, lpGUID );
+    }
+    */
+    
+    if( hr != DS_OK )
+    {
+        DBUG(("Cannot create DirectSound for %s. Result = 0x%x\n", name, hr ));
+        deviceOK = FALSE;
+    }
+    else
+    {
+        /* Query device characteristics. */
+        memset( &caps, 0, sizeof(caps) ); 
+        caps.dwSize = sizeof(caps);
+        hr = IDirectSound_GetCaps( lpDirectSound, &caps );
+        if( hr != DS_OK )
+        {
+            DBUG(("Cannot GetCaps() for DirectSound device %s. Result = 0x%x\n", name, hr ));
+            deviceOK = FALSE;
+        }
+        else
+        {
+
+#ifndef PA_NO_WMME
+            if( caps.dwFlags & DSCAPS_EMULDRIVER )
+            {
+                /* If WMME supported, then reject Emulated drivers because they are lousy. */
+                deviceOK = FALSE;
+            }
+#endif
+
+            if( deviceOK )
+            {
+                deviceInfo->maxInputChannels = 0;
+                /* Mono or stereo device? */
+                deviceInfo->maxOutputChannels = ( caps.dwFlags & DSCAPS_PRIMARYSTEREO ) ? 2 : 1;
+
+                deviceInfo->defaultLowInputLatency = 0.;    /** @todo IMPLEMENT ME */
+                deviceInfo->defaultLowOutputLatency = 0.;   /** @todo IMPLEMENT ME */
+                deviceInfo->defaultHighInputLatency = 0.;   /** @todo IMPLEMENT ME */
+                deviceInfo->defaultHighOutputLatency = 0.;  /** @todo IMPLEMENT ME */
+                
+                /* initialize defaultSampleRate */
+                
+                if( caps.dwFlags & DSCAPS_CONTINUOUSRATE )
+                {
+                    /* initialize to caps.dwMaxSecondarySampleRate incase none of the standard rates match */
+                    deviceInfo->defaultSampleRate = caps.dwMaxSecondarySampleRate;
+
+                    for( i = 0; i < PA_DEFAULTSAMPLERATESEARCHORDER_COUNT_; ++i )
+                    {
+                        if( defaultSampleRateSearchOrder_[i] >= caps.dwMinSecondarySampleRate
+                                && defaultSampleRateSearchOrder_[i] <= caps.dwMaxSecondarySampleRate ){
+
+                            deviceInfo->defaultSampleRate = defaultSampleRateSearchOrder_[i];
+                            break;
+                        }
+                    }
+                }
+                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.
+                        */
+                        deviceInfo->defaultSampleRate = 44100.0f;
+
+                        DBUG(("PA - Reported rates both zero. Setting to fake values for device #%d\n", sDeviceIndex ));
+                    }
+                    else
+                    {
+	                    deviceInfo->defaultSampleRate = caps.dwMaxSecondarySampleRate;
+                    }
+                }
+                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.
+                    */
+                  deviceInfo->defaultSampleRate = 44100.0f;
+                  DBUG(("PA - Sample rate range used instead of two odd values for device #%d\n", sDeviceIndex ));
+                }
+                else deviceInfo->defaultSampleRate = caps.dwMaxSecondarySampleRate;
+
+
+                //printf( "min %d max %d\n", caps.dwMinSecondarySampleRate, caps.dwMaxSecondarySampleRate );
+                // dwFlags | DSCAPS_CONTINUOUSRATE 
+            }
+        }
+
+        IDirectSound_Release( lpDirectSound );
+    }
+
+    if( deviceOK )
+    {
+        deviceInfo->name = name;
+
+        if( lpGUID == NULL )
+            hostApi->info.defaultOutputDevice = hostApi->info.deviceCount;
+            
+        hostApi->info.deviceCount++;
+    }
+
+    return result;
+}
+
+
+/************************************************************************************
+** Extract capabilities from an input device, and add it to the device info list
+** if successful. This function assumes that there is enough room in the
+** device info list to accomodate all entries.
+**
+** The device will not be added to the device list if any errors are encountered.
+*/
+static PaError AddInputDeviceInfoFromDirectSoundCapture(
+        PaWinDsHostApiRepresentation *winDsHostApi, char *name, LPGUID lpGUID )
+{
+    PaUtilHostApiRepresentation  *hostApi = &winDsHostApi->inheritedHostApiRep;
+    PaDeviceInfo                 *deviceInfo = hostApi->deviceInfos[hostApi->info.deviceCount];
+    PaWinDsDeviceInfo            *winDsDeviceInfo = &winDsHostApi->winDsDeviceInfos[hostApi->info.deviceCount];
+    HRESULT                       hr;
+    LPDIRECTSOUNDCAPTURE          lpDirectSoundCapture;
+    DSCCAPS                       caps;
+    int                           deviceOK = TRUE;
+    PaError                       result = paNoError;
+    
+    /* Copy GUID to the device info structure. Set pointer. */
+    if( lpGUID == NULL )
+    {
+        winDsDeviceInfo->lpGUID = NULL;
+    }
+    else
+    {
+        winDsDeviceInfo->lpGUID = &winDsDeviceInfo->guid;
+        memcpy( &winDsDeviceInfo->guid, lpGUID, sizeof(GUID) );
+    }
+
+
+    hr = dswDSoundEntryPoints.DirectSoundCaptureCreate( lpGUID, &lpDirectSoundCapture, NULL );
+
+    /** try using CoCreateInstance because DirectSoundCreate was hanging under
+        some circumstances - note this was probably related to the
+        #define BOOL short bug which has now been fixed
+        @todo delete this comment and the following code once we've ensured
+        there is no bug.
+    */
+    /*
+    hr = CoCreateInstance( &CLSID_DirectSoundCapture, NULL, CLSCTX_INPROC_SERVER,
+            &IID_IDirectSoundCapture, (void**)&lpDirectSoundCapture );
+    */
+    if( hr != DS_OK )
+    {
+        DBUG(("Cannot create Capture for %s. Result = 0x%x\n", name, hr ));
+        deviceOK = FALSE;
+    }
+    else
+    {
+        /* Query device characteristics. */
+        memset( &caps, 0, sizeof(caps) );
+        caps.dwSize = sizeof(caps);
+        hr = IDirectSoundCapture_GetCaps( lpDirectSoundCapture, &caps );
+        if( hr != DS_OK )
+        {
+            DBUG(("Cannot GetCaps() for Capture device %s. Result = 0x%x\n", name, hr ));
+            deviceOK = FALSE;
+        }
+        else
+        {
+#ifndef PA_NO_WMME
+            if( caps.dwFlags & DSCAPS_EMULDRIVER )
+            {
+                /* If WMME supported, then reject Emulated drivers because they are lousy. */
+                deviceOK = FALSE;
+            }
+#endif
+
+            if( deviceOK )
+            {
+                deviceInfo->maxInputChannels = caps.dwChannels;
+                deviceInfo->maxOutputChannels = 0;
+
+                deviceInfo->defaultLowInputLatency = 0.;    /** @todo IMPLEMENT ME */
+                deviceInfo->defaultLowOutputLatency = 0.;   /** @todo IMPLEMENT ME */
+                deviceInfo->defaultHighInputLatency = 0.;   /** @todo IMPLEMENT ME */
+                deviceInfo->defaultHighOutputLatency = 0.;  /** @todo IMPLEMENT ME */
+
+/*  constants from a WINE patch by Francois Gouget, see:
+    http://www.winehq.com/hypermail/wine-patches/2003/01/0290.html
+
+    ---
+    Date: Fri, 14 May 2004 10:38:12 +0200 (CEST)
+    From: Francois Gouget <fgouget@ ... .fr>
+    To: Ross Bencina <rbencina@ ... .au>
+    Subject: Re: Permission to use wine 48/96 wave patch in BSD licensed library
+
+    [snip]
+
+    I give you permission to use the patch below under the BSD license.
+    http://www.winehq.com/hypermail/wine-patches/2003/01/0290.html
+
+    [snip]
+*/
+#ifndef WAVE_FORMAT_48M08
+#define WAVE_FORMAT_48M08      0x00001000    /* 48     kHz, Mono,   8-bit  */
+#define WAVE_FORMAT_48S08      0x00002000    /* 48     kHz, Stereo, 8-bit  */
+#define WAVE_FORMAT_48M16      0x00004000    /* 48     kHz, Mono,   16-bit */
+#define WAVE_FORMAT_48S16      0x00008000    /* 48     kHz, Stereo, 16-bit */
+#define WAVE_FORMAT_96M08      0x00010000    /* 96     kHz, Mono,   8-bit  */
+#define WAVE_FORMAT_96S08      0x00020000    /* 96     kHz, Stereo, 8-bit  */
+#define WAVE_FORMAT_96M16      0x00040000    /* 96     kHz, Mono,   16-bit */
+#define WAVE_FORMAT_96S16      0x00080000    /* 96     kHz, Stereo, 16-bit */
+#endif
+
+                /* defaultSampleRate */
+                if( caps.dwChannels == 2 )
+                {
+                    if( caps.dwFormats & WAVE_FORMAT_4S16 )
+                        deviceInfo->defaultSampleRate = 44100.0;
+                    else if( caps.dwFormats & WAVE_FORMAT_48S16 )
+                        deviceInfo->defaultSampleRate = 48000.0;
+                    else if( caps.dwFormats & WAVE_FORMAT_2S16 )
+                        deviceInfo->defaultSampleRate = 22050.0;
+                    else if( caps.dwFormats & WAVE_FORMAT_1S16 )
+                        deviceInfo->defaultSampleRate = 11025.0;
+                    else if( caps.dwFormats & WAVE_FORMAT_96S16 )
+                        deviceInfo->defaultSampleRate = 96000.0;
+                    else
+                        deviceInfo->defaultSampleRate = 0.;
+                }
+                else if( caps.dwChannels == 1 )
+                {
+                    if( caps.dwFormats & WAVE_FORMAT_4M16 )
+                        deviceInfo->defaultSampleRate = 44100.0;
+                    else if( caps.dwFormats & WAVE_FORMAT_48M16 )
+                        deviceInfo->defaultSampleRate = 48000.0;
+                    else if( caps.dwFormats & WAVE_FORMAT_2M16 )
+                        deviceInfo->defaultSampleRate = 22050.0;
+                    else if( caps.dwFormats & WAVE_FORMAT_1M16 )
+                        deviceInfo->defaultSampleRate = 11025.0;
+                    else if( caps.dwFormats & WAVE_FORMAT_96M16 )
+                        deviceInfo->defaultSampleRate = 96000.0;
+                    else
+                        deviceInfo->defaultSampleRate = 0.;
+                }
+                else deviceInfo->defaultSampleRate = 0.;
+            }
+        }
+        
+        IDirectSoundCapture_Release( lpDirectSoundCapture );
+    }
+
+    if( deviceOK )
+    {
+        deviceInfo->name = name;
+
+        if( lpGUID == NULL )
+            hostApi->info.defaultInputDevice = hostApi->info.deviceCount;
+
+        hostApi->info.deviceCount++;
+    }
+
+    return result;
+}
+
+
+/***********************************************************************************/
+PaError PaWinDs_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex )
+{
+    PaError result = paNoError;
+    int i, deviceCount;
+    PaWinDsHostApiRepresentation *winDsHostApi;
+    DSDeviceNameAndGUIDVector inputNamesAndGUIDs, outputNamesAndGUIDs;
+    PaDeviceInfo *deviceInfoArray;
+
+    HRESULT hr = CoInitialize(NULL);        /** @todo: should uninitialize too */
+    if( FAILED(hr) ){
+        return paUnanticipatedHostError;
+    }            
+
+    /* initialise guid vectors so they can be safely deleted on error */
+    inputNamesAndGUIDs.items = NULL;
+    outputNamesAndGUIDs.items = NULL;
+
+    DSW_InitializeDSoundEntryPoints();
+
+    winDsHostApi = (PaWinDsHostApiRepresentation*)PaUtil_AllocateMemory( sizeof(PaWinDsHostApiRepresentation) );
+    if( !winDsHostApi )
+    {
+        result = paInsufficientMemory;
+        goto error;
+    }
+
+    winDsHostApi->allocations = PaUtil_CreateAllocationGroup();
+    if( !winDsHostApi->allocations )
+    {
+        result = paInsufficientMemory;
+        goto error;
+    }
+
+    *hostApi = &winDsHostApi->inheritedHostApiRep;
+    (*hostApi)->info.structVersion = 1;
+    (*hostApi)->info.type = paDirectSound;
+    (*hostApi)->info.name = "Windows DirectSound";
+    
+    (*hostApi)->info.deviceCount = 0;
+    (*hostApi)->info.defaultInputDevice = paNoDevice;
+    (*hostApi)->info.defaultOutputDevice = paNoDevice;
+
+    
+/* DSound - enumerate devices to count them and to gather their GUIDs */
+
+
+    result = InitializeDSDeviceNameAndGUIDVector( &inputNamesAndGUIDs, winDsHostApi->allocations );
+    if( result != paNoError )
+        goto error;
+
+    result = InitializeDSDeviceNameAndGUIDVector( &outputNamesAndGUIDs, winDsHostApi->allocations );
+    if( result != paNoError )
+        goto error;
+
+    dswDSoundEntryPoints.DirectSoundCaptureEnumerate( (LPDSENUMCALLBACK)CollectGUIDsProc, (void *)&inputNamesAndGUIDs );
+
+    dswDSoundEntryPoints.DirectSoundEnumerate( (LPDSENUMCALLBACK)CollectGUIDsProc, (void *)&outputNamesAndGUIDs );
+
+    if( inputNamesAndGUIDs.enumerationError != paNoError )
+    {
+        result = inputNamesAndGUIDs.enumerationError;
+        goto error;
+    }
+
+    if( outputNamesAndGUIDs.enumerationError != paNoError )
+    {
+        result = outputNamesAndGUIDs.enumerationError;
+        goto error;
+    }
+
+    deviceCount = inputNamesAndGUIDs.count + outputNamesAndGUIDs.count;
+
+    if( deviceCount > 0 )
+    {
+        /* allocate array for pointers to PaDeviceInfo structs */
+        (*hostApi)->deviceInfos = (PaDeviceInfo**)PaUtil_GroupAllocateMemory(
+                winDsHostApi->allocations, sizeof(PaDeviceInfo*) * deviceCount );
+        if( !(*hostApi)->deviceInfos )
+        {
+            result = paInsufficientMemory;
+            goto error;
+        }
+
+        /* allocate all PaDeviceInfo structs in a contiguous block */
+        deviceInfoArray = (PaDeviceInfo*)PaUtil_GroupAllocateMemory(
+                winDsHostApi->allocations, sizeof(PaDeviceInfo) * deviceCount );
+        if( !deviceInfoArray )
+        {
+            result = paInsufficientMemory;
+            goto error;
+        }
+
+        /* allocate all DSound specific info structs in a contiguous block */
+        winDsHostApi->winDsDeviceInfos = (PaWinDsDeviceInfo*)PaUtil_GroupAllocateMemory(
+                winDsHostApi->allocations, sizeof(PaWinDsDeviceInfo) * deviceCount );
+        if( !winDsHostApi->winDsDeviceInfos )
+        {
+            result = paInsufficientMemory;
+            goto error;
+        }
+
+        for( i=0; i < deviceCount; ++i )
+        {
+            PaDeviceInfo *deviceInfo = &deviceInfoArray[i];
+            deviceInfo->structVersion = 2;
+            deviceInfo->hostApi = hostApiIndex;
+            deviceInfo->name = 0;
+            (*hostApi)->deviceInfos[i] = deviceInfo;
+        }
+
+        for( i=0; i< inputNamesAndGUIDs.count; ++i )
+        {
+            result = AddInputDeviceInfoFromDirectSoundCapture( winDsHostApi,
+                    inputNamesAndGUIDs.items[i].name,
+                    inputNamesAndGUIDs.items[i].lpGUID );
+            if( result != paNoError )
+                goto error;
+        }
+
+        for( i=0; i< outputNamesAndGUIDs.count; ++i )
+        {
+            result = AddOutputDeviceInfoFromDirectSound( winDsHostApi,
+                    outputNamesAndGUIDs.items[i].name,
+                    outputNamesAndGUIDs.items[i].lpGUID );
+            if( result != paNoError )
+                goto error;
+        }
+    }    
+
+    result = TerminateDSDeviceNameAndGUIDVector( &inputNamesAndGUIDs );
+    if( result != paNoError )
+        goto error;
+
+    result = TerminateDSDeviceNameAndGUIDVector( &outputNamesAndGUIDs );
+    if( result != paNoError )
+        goto error;
+
+    
+    (*hostApi)->Terminate = Terminate;
+    (*hostApi)->OpenStream = OpenStream;
+    (*hostApi)->IsFormatSupported = IsFormatSupported;
+
+    PaUtil_InitializeStreamInterface( &winDsHostApi->callbackStreamInterface, CloseStream, StartStream,
+                                      StopStream, AbortStream, IsStreamStopped, IsStreamActive,
+                                      GetStreamTime, GetStreamCpuLoad,
+                                      PaUtil_DummyRead, PaUtil_DummyWrite,
+                                      PaUtil_DummyGetReadAvailable, PaUtil_DummyGetWriteAvailable );
+
+    PaUtil_InitializeStreamInterface( &winDsHostApi->blockingStreamInterface, CloseStream, StartStream,
+                                      StopStream, AbortStream, IsStreamStopped, IsStreamActive,
+                                      GetStreamTime, PaUtil_DummyGetCpuLoad,
+                                      ReadStream, WriteStream, GetStreamReadAvailable, GetStreamWriteAvailable );
+
+    return result;
+
+error:
+    if( winDsHostApi )
+    {
+        if( winDsHostApi->allocations )
+        {
+            PaUtil_FreeAllAllocations( winDsHostApi->allocations );
+            PaUtil_DestroyAllocationGroup( winDsHostApi->allocations );
+        }
+                
+        PaUtil_FreeMemory( winDsHostApi );
+    }
+
+    TerminateDSDeviceNameAndGUIDVector( &inputNamesAndGUIDs );
+    TerminateDSDeviceNameAndGUIDVector( &outputNamesAndGUIDs );
+
+    return result;
+}
+
+
+/***********************************************************************************/
+static void Terminate( struct PaUtilHostApiRepresentation *hostApi )
+{
+    PaWinDsHostApiRepresentation *winDsHostApi = (PaWinDsHostApiRepresentation*)hostApi;
+
+    /*
+        IMPLEMENT ME:
+            - clean up any resources not handled by the allocation group
+    */
+
+    if( winDsHostApi->allocations )
+    {
+        PaUtil_FreeAllAllocations( winDsHostApi->allocations );
+        PaUtil_DestroyAllocationGroup( winDsHostApi->allocations );
+    }
+
+    PaUtil_FreeMemory( winDsHostApi );
+
+    DSW_TerminateDSoundEntryPoints();
+
+    CoUninitialize();
+}
+
+
+/* Set minimal latency based on whether NT or Win95.
+ * NT has higher latency.
+ */
+static int PaWinDS_GetMinSystemLatency( void )
+{
+    int minLatencyMsec;
+    /* Set minimal latency based on whether NT or other OS.
+     * NT has higher latency.
+     */
+    OSVERSIONINFO osvi;
+	osvi.dwOSVersionInfoSize = sizeof( osvi );
+	GetVersionEx( &osvi );
+    DBUG(("PA - PlatformId = 0x%x\n", osvi.dwPlatformId ));
+    DBUG(("PA - MajorVersion = 0x%x\n", osvi.dwMajorVersion ));
+    DBUG(("PA - MinorVersion = 0x%x\n", osvi.dwMinorVersion ));
+    /* Check for NT */
+	if( (osvi.dwMajorVersion == 4) && (osvi.dwPlatformId == 2) )
+	{
+		minLatencyMsec = PA_WIN_NT_LATENCY;
+	}
+	else if(osvi.dwMajorVersion >= 5)
+	{
+		minLatencyMsec = PA_WIN_WDM_LATENCY;
+	}
+	else
+	{
+		minLatencyMsec = PA_WIN_9X_LATENCY;
+	}
+    return minLatencyMsec;
+}
+
+/***********************************************************************************/
+static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
+                                  const PaStreamParameters *inputParameters,
+                                  const PaStreamParameters *outputParameters,
+                                  double sampleRate )
+{
+    int inputChannelCount, outputChannelCount;
+    PaSampleFormat inputSampleFormat, outputSampleFormat;
+    
+    if( inputParameters )
+    {
+        inputChannelCount = inputParameters->channelCount;
+        inputSampleFormat = inputParameters->sampleFormat;
+
+        /* unless alternate device specification is supported, reject the use of
+            paUseHostApiSpecificDeviceSpecification */
+
+        if( inputParameters->device == paUseHostApiSpecificDeviceSpecification )
+            return paInvalidDevice;
+
+        /* check that input device can support inputChannelCount */
+        if( inputChannelCount > hostApi->deviceInfos[ inputParameters->device ]->maxInputChannels )
+            return paInvalidChannelCount;
+
+        /* validate inputStreamInfo */
+        if( inputParameters->hostApiSpecificStreamInfo )
+            return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */
+    }
+    else
+    {
+        inputChannelCount = 0;
+    }
+
+    if( outputParameters )
+    {
+        outputChannelCount = outputParameters->channelCount;
+        outputSampleFormat = outputParameters->sampleFormat;
+        
+        /* unless alternate device specification is supported, reject the use of
+            paUseHostApiSpecificDeviceSpecification */
+
+        if( outputParameters->device == paUseHostApiSpecificDeviceSpecification )
+            return paInvalidDevice;
+
+        /* check that output device can support inputChannelCount */
+        if( outputChannelCount > hostApi->deviceInfos[ outputParameters->device ]->maxOutputChannels )
+            return paInvalidChannelCount;
+
+        /* validate outputStreamInfo */
+        if( outputParameters->hostApiSpecificStreamInfo )
+            return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */
+    }
+    else
+    {
+        outputChannelCount = 0;
+    }
+    
+    /*
+        IMPLEMENT ME:
+
+            - if a full duplex stream is requested, check that the combination
+                of input and output parameters is supported if necessary
+
+            - check that the device supports sampleRate
+
+        Because the buffer adapter handles conversion between all standard
+        sample formats, the following checks are only required if paCustomFormat
+        is implemented, or under some other unusual conditions.
+
+            - check that input device can support inputSampleFormat, or that
+                we have the capability to convert from outputSampleFormat to
+                a native format
+
+            - check that output device can support outputSampleFormat, or that
+                we have the capability to convert from outputSampleFormat to
+                a native format
+    */
+
+    return paFormatIsSupported;
+}
+
+
+/*************************************************************************
+** 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")
+#define PA_ENV_BUF_SIZE  (32)
+
+static int PaWinDs_GetMinLatencyFrames( double sampleRate )
+{
+    char      envbuf[PA_ENV_BUF_SIZE];
+    DWORD     hresult;
+    int       minLatencyMsec = 0;
+
+    /* 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
+    {
+        minLatencyMsec = PaWinDS_GetMinSystemLatency();
+#if PA_USE_HIGH_LATENCY
+        PRINT(("PA - Minimum Latency set to %d msec!\n", minLatencyMsec ));
+#endif
+
+    }
+
+    return (int) (minLatencyMsec * sampleRate * SECONDS_PER_MSEC);
+}
+
+/***********************************************************************************/
+/* see pa_hostapi.h for a list of validity guarantees made about OpenStream parameters */
+
+static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
+                           PaStream** s,
+                           const PaStreamParameters *inputParameters,
+                           const PaStreamParameters *outputParameters,
+                           double sampleRate,
+                           unsigned long framesPerBuffer,
+                           PaStreamFlags streamFlags,
+                           PaStreamCallback *streamCallback,
+                           void *userData )
+{
+    PaError result = paNoError;
+    PaWinDsHostApiRepresentation *winDsHostApi = (PaWinDsHostApiRepresentation*)hostApi;
+    PaWinDsStream *stream = 0;
+    int inputChannelCount, outputChannelCount;
+    PaSampleFormat inputSampleFormat, outputSampleFormat;
+    PaSampleFormat hostInputSampleFormat, hostOutputSampleFormat;
+    unsigned long suggestedInputLatencyFrames, suggestedOutputLatencyFrames;
+
+    if( inputParameters )
+    {
+        inputChannelCount = inputParameters->channelCount;
+        inputSampleFormat = inputParameters->sampleFormat;
+        suggestedInputLatencyFrames = (unsigned long)(inputParameters->suggestedLatency * sampleRate);
+
+        /* IDEA: the following 3 checks could be performed by default by pa_front
+            unless some flag indicated otherwise */
+            
+        /* unless alternate device specification is supported, reject the use of
+            paUseHostApiSpecificDeviceSpecification */
+        if( inputParameters->device == paUseHostApiSpecificDeviceSpecification )
+            return paInvalidDevice;
+
+        /* check that input device can support inputChannelCount */
+        if( inputChannelCount > hostApi->deviceInfos[ inputParameters->device ]->maxInputChannels )
+            return paInvalidChannelCount;
+            
+        /* validate hostApiSpecificStreamInfo */
+        if( inputParameters->hostApiSpecificStreamInfo )
+            return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */
+    }
+    else
+    {
+        inputChannelCount = 0;
+        suggestedInputLatencyFrames = 0;
+    }
+
+
+    if( outputParameters )
+    {
+        outputChannelCount = outputParameters->channelCount;
+        outputSampleFormat = outputParameters->sampleFormat;
+        suggestedOutputLatencyFrames = (unsigned long)(outputParameters->suggestedLatency * sampleRate);
+
+        /* unless alternate device specification is supported, reject the use of
+            paUseHostApiSpecificDeviceSpecification */
+        if( outputParameters->device == paUseHostApiSpecificDeviceSpecification )
+            return paInvalidDevice;
+
+        /* check that output device can support inputChannelCount */
+        if( outputChannelCount > hostApi->deviceInfos[ outputParameters->device ]->maxOutputChannels )
+            return paInvalidChannelCount;
+
+        /* validate hostApiSpecificStreamInfo */
+        if( outputParameters->hostApiSpecificStreamInfo )
+            return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */            
+    }
+    else
+    {
+        outputChannelCount = 0;
+        suggestedOutputLatencyFrames = 0;
+    }
+
+
+    /*
+        IMPLEMENT ME:
+
+        ( the following two checks are taken care of by PaUtil_InitializeBufferProcessor() )
+
+            - check that input device can support inputSampleFormat, or that
+                we have the capability to convert from outputSampleFormat to
+                a native format
+
+            - check that output device can support outputSampleFormat, or that
+                we have the capability to convert from outputSampleFormat to
+                a native format
+
+            - if a full duplex stream is requested, check that the combination
+                of input and output parameters is supported
+
+            - check that the device supports sampleRate
+
+            - alter sampleRate to a close allowable rate if possible / necessary
+
+            - validate suggestedInputLatency and suggestedOutputLatency parameters,
+                use default values where necessary
+    */
+
+
+    /* validate platform specific flags */
+    if( (streamFlags & paPlatformSpecificFlags) != 0 )
+        return paInvalidFlag; /* unexpected platform specific flag */
+
+
+    stream = (PaWinDsStream*)PaUtil_AllocateMemory( sizeof(PaWinDsStream) );
+    if( !stream )
+    {
+        result = paInsufficientMemory;
+        goto error;
+    }
+
+    if( streamCallback )
+    {
+        PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation,
+                                               &winDsHostApi->callbackStreamInterface, streamCallback, userData );
+    }
+    else
+    {
+        PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation,
+                                               &winDsHostApi->blockingStreamInterface, streamCallback, userData );
+    }
+    
+    PaUtil_InitializeCpuLoadMeasurer( &stream->cpuLoadMeasurer, sampleRate );
+
+
+    if( inputParameters )
+    {
+        /* IMPLEMENT ME - establish which  host formats are available */
+        hostInputSampleFormat =
+            PaUtil_SelectClosestAvailableFormat( paInt16 /* native formats */, inputParameters->sampleFormat );
+    }
+
+    if( outputParameters )
+    {
+        /* IMPLEMENT ME - establish which  host formats are available */
+        hostOutputSampleFormat =
+            PaUtil_SelectClosestAvailableFormat( paInt16 /* native formats */, outputParameters->sampleFormat );
+    }
+    
+    result =  PaUtil_InitializeBufferProcessor( &stream->bufferProcessor,
+                    inputChannelCount, inputSampleFormat, hostInputSampleFormat,
+                    outputChannelCount, outputSampleFormat, hostOutputSampleFormat,
+                    sampleRate, streamFlags, framesPerBuffer,
+                    framesPerBuffer, /* ignored in paUtilVariableHostBufferSizePartialUsageAllowed mode. */
+                /* This next mode is required because DS can split the host buffer when it wraps around. */
+                    paUtilVariableHostBufferSizePartialUsageAllowed,
+                    streamCallback, userData );
+    if( result != paNoError )
+        goto error;
+
+
+    stream->streamRepresentation.streamInfo.inputLatency =
+            PaUtil_GetBufferProcessorInputLatency(&stream->bufferProcessor);   /* FIXME: not initialised anywhere else */
+    stream->streamRepresentation.streamInfo.outputLatency =
+            PaUtil_GetBufferProcessorOutputLatency(&stream->bufferProcessor);    /* FIXME: not initialised anywhere else */
+    stream->streamRepresentation.streamInfo.sampleRate = sampleRate;
+
+    
+/* DirectSound specific initialization */
+    {
+        HRESULT          hr;
+        int              bytesPerDirectSoundBuffer;
+        DSoundWrapper   *dsw;
+        int              userLatencyFrames;
+        int              minLatencyFrames;
+
+        stream->timerID = 0;
+        dsw = &stream->directSoundWrapper;
+        DSW_Init( dsw );
+
+    /* Get system minimum latency. */
+        minLatencyFrames = PaWinDs_GetMinLatencyFrames( sampleRate );
+
+    /* Let user override latency by passing latency parameter. */
+        userLatencyFrames = (suggestedInputLatencyFrames > suggestedOutputLatencyFrames)
+                    ? suggestedInputLatencyFrames
+                    : suggestedOutputLatencyFrames;
+        if( userLatencyFrames > 0 ) minLatencyFrames = userLatencyFrames;
+
+    /* Calculate stream->framesPerDSBuffer depending on framesPerBuffer */
+        if( framesPerBuffer == paFramesPerBufferUnspecified )
+        {
+        /* App support variable framesPerBuffer */
+            stream->framesPerDSBuffer = minLatencyFrames;
+
+            stream->streamRepresentation.streamInfo.outputLatency = (double)(minLatencyFrames - 1) / sampleRate;
+        }
+        else
+        {
+        /* Round up to number of buffers needed to guarantee that latency. */
+            int numUserBuffers = (minLatencyFrames + framesPerBuffer - 1) / framesPerBuffer;
+            if( numUserBuffers < 1 ) numUserBuffers = 1;
+            numUserBuffers += 1; /* So we have latency worth of buffers ahead of current buffer. */
+            stream->framesPerDSBuffer = framesPerBuffer * numUserBuffers;
+
+            stream->streamRepresentation.streamInfo.outputLatency = (double)(framesPerBuffer * (numUserBuffers-1)) / sampleRate;
+        }
+
+        {
+            /** @todo REVIEW: this calculation seems incorrect to me - rossb. */
+            int msecLatency = (int) ((stream->framesPerDSBuffer * MSEC_PER_SECOND) / sampleRate);
+            PRINT(("PortAudio on DirectSound - Latency = %d frames, %d msec\n", stream->framesPerDSBuffer, msecLatency ));
+        }
+
+
+        /* ------------------ OUTPUT */
+        if( outputParameters )
+        {
+            /*
+            PaDeviceInfo *deviceInfo = hostApi->deviceInfos[ outputParameters->device ];
+            DBUG(("PaHost_OpenStream: deviceID = 0x%x\n", outputParameters->device));
+            */
+            
+            bytesPerDirectSoundBuffer = stream->framesPerDSBuffer * outputParameters->channelCount * sizeof(short);
+            if( bytesPerDirectSoundBuffer < DSBSIZE_MIN )
+            {
+                result = paBufferTooSmall;
+                goto error;
+            }
+            else if( bytesPerDirectSoundBuffer > DSBSIZE_MAX )
+            {
+                result = paBufferTooBig;
+                goto error;
+            }
+
+
+            hr = dswDSoundEntryPoints.DirectSoundCreate( winDsHostApi->winDsDeviceInfos[outputParameters->device].lpGUID,
+                &dsw->dsw_pDirectSound,   NULL );
+            if( hr != DS_OK )
+            {
+                ERR_RPT(("PortAudio: DirectSoundCreate() failed!\n"));
+                result = paUnanticipatedHostError;
+                PA_DS_SET_LAST_DIRECTSOUND_ERROR( hr );
+                goto error;
+            }
+            hr = DSW_InitOutputBuffer( dsw,
+                                       (unsigned long) (sampleRate + 0.5),
+                                       (WORD)outputParameters->channelCount, bytesPerDirectSoundBuffer );
+            DBUG(("DSW_InitOutputBuffer() returns %x\n", hr));
+            if( hr != DS_OK )
+            {
+                result = paUnanticipatedHostError;
+                PA_DS_SET_LAST_DIRECTSOUND_ERROR( hr );
+                goto error;
+            }
+            /* Calculate value used in latency calculation to avoid real-time divides. */
+            stream->secondsPerHostByte = 1.0 /
+                (stream->bufferProcessor.bytesPerHostOutputSample *
+                outputChannelCount * sampleRate);
+        }
+
+        /* ------------------ INPUT */
+        if( inputParameters )
+        {
+            /*
+            PaDeviceInfo *deviceInfo = hostApi->deviceInfos[ inputParameters->device ];
+            DBUG(("PaHost_OpenStream: deviceID = 0x%x\n", inputParameters->device));
+            */
+            
+            bytesPerDirectSoundBuffer = stream->framesPerDSBuffer * inputParameters->channelCount * sizeof(short);
+            if( bytesPerDirectSoundBuffer < DSBSIZE_MIN )
+            {
+                result = paBufferTooSmall;
+                goto error;
+            }
+            else if( bytesPerDirectSoundBuffer > DSBSIZE_MAX )
+            {
+                result = paBufferTooBig;
+                goto error;
+            }
+
+            hr = dswDSoundEntryPoints.DirectSoundCaptureCreate( winDsHostApi->winDsDeviceInfos[inputParameters->device].lpGUID,
+                &dsw->dsw_pDirectSoundCapture,   NULL );
+            if( hr != DS_OK )
+            {
+                ERR_RPT(("PortAudio: DirectSoundCaptureCreate() failed!\n"));
+                result = paUnanticipatedHostError;
+                PA_DS_SET_LAST_DIRECTSOUND_ERROR( hr );
+                goto error;
+            }
+            hr = DSW_InitInputBuffer( dsw,
+                                      (unsigned long) (sampleRate + 0.5),
+                                      (WORD)inputParameters->channelCount, bytesPerDirectSoundBuffer );
+            DBUG(("DSW_InitInputBuffer() returns %x\n", hr));
+            if( hr != DS_OK )
+            {
+                ERR_RPT(("PortAudio: DSW_InitInputBuffer() returns %x\n", hr));
+                result = paUnanticipatedHostError;
+                PA_DS_SET_LAST_DIRECTSOUND_ERROR( hr );
+                goto error;
+            }
+        }
+
+    }
+
+    *s = (PaStream*)stream;
+
+    return result;
+
+error:
+    if( stream )
+        PaUtil_FreeMemory( stream );
+
+    return result;
+}
+
+
+/***********************************************************************************/
+static PaError Pa_TimeSlice( PaWinDsStream *stream )
+{
+    PaError           result = 0;   /* FIXME: this should be declared int and this function should also return that type (same as stream callback return type)*/
+    DSoundWrapper    *dsw;
+    long              numFrames = 0;
+    long              bytesEmpty = 0;
+    long              bytesFilled = 0;
+    long              bytesToXfer = 0;
+    long              framesToXfer = 0;
+    long              numInFramesReady = 0;
+    long              numOutFramesReady = 0;
+    long              bytesProcessed;
+    HRESULT           hresult;
+    double            outputLatency = 0;
+    PaStreamCallbackTimeInfo timeInfo = {0,0,0}; /** @todo implement inputBufferAdcTime */
+    
+/* Input */
+    LPBYTE            lpInBuf1 = NULL;
+    LPBYTE            lpInBuf2 = NULL;
+    DWORD             dwInSize1 = 0;
+    DWORD             dwInSize2 = 0;
+/* Output */
+    LPBYTE            lpOutBuf1 = NULL;
+    LPBYTE            lpOutBuf2 = NULL;
+    DWORD             dwOutSize1 = 0;
+    DWORD             dwOutSize2 = 0;
+
+    dsw = &stream->directSoundWrapper;
+
+    /* How much input data is available? */
+    if( stream->bufferProcessor.inputChannelCount > 0 )
+    {
+        DSW_QueryInputFilled( dsw, &bytesFilled );
+        framesToXfer = numInFramesReady = bytesFilled / dsw->dsw_BytesPerInputFrame;
+        outputLatency = ((double)bytesFilled) * stream->secondsPerHostByte;
+
+        /** @todo Check for overflow */
+    }
+
+    /* How much output room is available? */
+    if( stream->bufferProcessor.outputChannelCount > 0 )
+    {
+        UINT previousUnderflowCount = dsw->dsw_OutputUnderflows;
+        DSW_QueryOutputSpace( dsw, &bytesEmpty );
+        framesToXfer = numOutFramesReady = bytesEmpty / dsw->dsw_BytesPerOutputFrame;
+
+        /* Check for underflow */
+        if( dsw->dsw_OutputUnderflows != previousUnderflowCount )
+            stream->callbackFlags |= paOutputUnderflow;
+    }
+
+    if( (numInFramesReady > 0) && (numOutFramesReady > 0) )
+    {
+        framesToXfer = (numOutFramesReady < numInFramesReady) ? numOutFramesReady : numInFramesReady;
+    }
+
+    if( framesToXfer > 0 )
+    {
+
+        PaUtil_BeginCpuLoadMeasurement( &stream->cpuLoadMeasurer );
+
+    /* The outputBufferDacTime parameter should indicates the time at which
+        the first sample of the output buffer is heard at the DACs. */
+        timeInfo.currentTime = PaUtil_GetTime();
+        timeInfo.outputBufferDacTime = timeInfo.currentTime + outputLatency;
+
+
+        PaUtil_BeginBufferProcessing( &stream->bufferProcessor, &timeInfo, stream->callbackFlags );
+        stream->callbackFlags = 0;
+        
+    /* Input */
+        if( stream->bufferProcessor.inputChannelCount > 0 )
+        {
+            bytesToXfer = framesToXfer * dsw->dsw_BytesPerInputFrame;
+            hresult = IDirectSoundCaptureBuffer_Lock ( dsw->dsw_InputBuffer,
+                dsw->dsw_ReadOffset, bytesToXfer,
+                (void **) &lpInBuf1, &dwInSize1,
+                (void **) &lpInBuf2, &dwInSize2, 0);
+            if (hresult != DS_OK)
+            {
+                ERR_RPT(("DirectSound IDirectSoundCaptureBuffer_Lock failed, hresult = 0x%x\n",hresult));
+                result = paUnanticipatedHostError;
+                PA_DS_SET_LAST_DIRECTSOUND_ERROR( hresult );
+                goto error2;
+            }
+
+            numFrames = dwInSize1 / dsw->dsw_BytesPerInputFrame;
+            PaUtil_SetInputFrameCount( &stream->bufferProcessor, numFrames );
+            PaUtil_SetInterleavedInputChannels( &stream->bufferProcessor, 0, lpInBuf1, 0 );
+        /* Is input split into two regions. */
+            if( dwInSize2 > 0 )
+            {
+                numFrames = dwInSize2 / dsw->dsw_BytesPerInputFrame;
+                PaUtil_Set2ndInputFrameCount( &stream->bufferProcessor, numFrames );
+                PaUtil_Set2ndInterleavedInputChannels( &stream->bufferProcessor, 0, lpInBuf2, 0 );
+            }
+        }
+
+    /* Output */
+        if( stream->bufferProcessor.outputChannelCount > 0 )
+        {
+            bytesToXfer = framesToXfer * dsw->dsw_BytesPerOutputFrame;
+            hresult = IDirectSoundBuffer_Lock ( dsw->dsw_OutputBuffer,
+                dsw->dsw_WriteOffset, bytesToXfer,
+                (void **) &lpOutBuf1, &dwOutSize1,
+                (void **) &lpOutBuf2, &dwOutSize2, 0);
+            if (hresult != DS_OK)
+            {
+                ERR_RPT(("DirectSound IDirectSoundBuffer_Lock failed, hresult = 0x%x\n",hresult));
+                result = paUnanticipatedHostError;
+                PA_DS_SET_LAST_DIRECTSOUND_ERROR( hresult );
+                goto error1;
+            }
+
+            numFrames = dwOutSize1 / dsw->dsw_BytesPerOutputFrame;
+            PaUtil_SetOutputFrameCount( &stream->bufferProcessor, numFrames );
+            PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor, 0, lpOutBuf1, 0 );
+
+        /* Is output split into two regions. */
+            if( dwOutSize2 > 0 )
+            {
+                numFrames = dwOutSize2 / dsw->dsw_BytesPerOutputFrame;
+                PaUtil_Set2ndOutputFrameCount( &stream->bufferProcessor, numFrames );
+                PaUtil_Set2ndInterleavedOutputChannels( &stream->bufferProcessor, 0, lpOutBuf2, 0 );
+            }
+        }
+
+        result = paContinue;
+        numFrames = PaUtil_EndBufferProcessing( &stream->bufferProcessor, &result );
+        stream->framesWritten += numFrames;
+        
+        if( stream->bufferProcessor.outputChannelCount > 0 )
+        {
+        /* FIXME: an underflow could happen here */
+
+        /* Update our buffer offset and unlock sound buffer */
+            bytesProcessed = numFrames * dsw->dsw_BytesPerOutputFrame;
+            dsw->dsw_WriteOffset = (dsw->dsw_WriteOffset + bytesProcessed) % dsw->dsw_OutputSize;
+            IDirectSoundBuffer_Unlock( dsw->dsw_OutputBuffer, lpOutBuf1, dwOutSize1, lpOutBuf2, dwOutSize2);
+            dsw->dsw_FramesWritten += numFrames;
+        }
+
+error1:
+        if( stream->bufferProcessor.inputChannelCount > 0 )
+        {
+        /* FIXME: an overflow could happen here */
+
+        /* Update our buffer offset and unlock sound buffer */
+            bytesProcessed = numFrames * dsw->dsw_BytesPerInputFrame;
+            dsw->dsw_ReadOffset = (dsw->dsw_ReadOffset + bytesProcessed) % dsw->dsw_InputSize;
+            IDirectSoundCaptureBuffer_Unlock( dsw->dsw_InputBuffer, lpInBuf1, dwInSize1, lpInBuf2, dwInSize2);
+        }
+error2:
+
+        PaUtil_EndCpuLoadMeasurement( &stream->cpuLoadMeasurer, numFrames );
+
+    }
+    
+    return result;
+}
+/*******************************************************************/
+static void CALLBACK Pa_TimerCallback(UINT uID, UINT uMsg, DWORD dwUser, DWORD dw1, DWORD dw2)
+{
+    PaWinDsStream *stream;
+
+    /* suppress unused variable warnings */
+    (void) uID;
+    (void) uMsg;
+    (void) dw1;
+    (void) dw2;
+    
+    stream = (PaWinDsStream *) dwUser;
+    if( stream == NULL ) return;
+
+    if( stream->isActive )
+    {
+        if( stream->abortProcessing )
+        {
+            stream->isActive = 0;
+        }
+        else if( stream->stopProcessing )
+        {
+            DSoundWrapper   *dsw = &stream->directSoundWrapper;
+            if( stream->bufferProcessor.outputChannelCount > 0 )
+            {
+                DSW_ZeroEmptySpace( dsw );
+                /* clear isActive when all sound played */
+                if( dsw->dsw_FramesPlayed >= stream->framesWritten )
+                {
+                    stream->isActive = 0;
+                }
+            }
+            else
+            {
+                stream->isActive = 0;
+            }
+        }
+        else
+        {
+            if( Pa_TimeSlice( stream ) != 0)  /* Call time slice independant of timing method. */
+            {
+                /* FIXME implement handling of paComplete and paAbort if possible */
+                stream->stopProcessing = 1;
+            }
+        }
+
+        if( !stream->isActive ){
+            if( stream->streamRepresentation.streamFinishedCallback != 0 )
+                stream->streamRepresentation.streamFinishedCallback( stream->streamRepresentation.userData );
+        }
+    }
+}
+
+/***********************************************************************************
+    When CloseStream() is called, the multi-api layer ensures that
+    the stream has already been stopped or aborted.
+*/
+static PaError CloseStream( PaStream* s )
+{
+    PaError result = paNoError;
+    PaWinDsStream *stream = (PaWinDsStream*)s;
+
+    DSW_Term( &stream->directSoundWrapper );
+
+    PaUtil_TerminateBufferProcessor( &stream->bufferProcessor );
+    PaUtil_TerminateStreamRepresentation( &stream->streamRepresentation );
+    PaUtil_FreeMemory( stream );
+
+    return result;
+}
+
+/***********************************************************************************/
+static PaError StartStream( PaStream *s )
+{
+    PaError          result = paNoError;
+    PaWinDsStream   *stream = (PaWinDsStream*)s;
+    HRESULT          hr;
+
+    PaUtil_ResetBufferProcessor( &stream->bufferProcessor );
+    
+    if( stream->bufferProcessor.inputChannelCount > 0 )
+    {
+        hr = DSW_StartInput( &stream->directSoundWrapper );
+        DBUG(("StartStream: DSW_StartInput returned = 0x%X.\n", hr));
+        if( hr != DS_OK )
+        {
+            result = paUnanticipatedHostError;
+            PA_DS_SET_LAST_DIRECTSOUND_ERROR( hr );
+            goto error;
+        }
+    }
+
+    stream->framesWritten = 0;
+    stream->callbackFlags = 0;
+
+    stream->abortProcessing = 0;
+    stream->stopProcessing = 0;
+    stream->isActive = 1;
+
+    if( stream->bufferProcessor.outputChannelCount > 0 )
+    {
+        /* Give user callback a chance to pre-fill buffer. REVIEW - i thought we weren't pre-filling, rb. */
+        result = Pa_TimeSlice( stream );
+        if( result != paNoError ) return result; // FIXME - what if finished?
+
+        hr = DSW_StartOutput( &stream->directSoundWrapper );
+        DBUG(("PaHost_StartOutput: DSW_StartOutput returned = 0x%X.\n", hr));
+        if( hr != DS_OK )
+        {
+            result = paUnanticipatedHostError;
+            PA_DS_SET_LAST_DIRECTSOUND_ERROR( hr );
+            goto error;
+        }
+    }
+
+
+    /* Create timer that will wake us up so we can fill the DSound buffer. */
+    {
+        int resolution;
+        int framesPerWakeup = stream->framesPerDSBuffer / 4;
+        int msecPerWakeup = MSEC_PER_SECOND * framesPerWakeup / (int) stream->streamRepresentation.streamInfo.sampleRate;
+        if( msecPerWakeup < 10 ) msecPerWakeup = 10;
+        else if( msecPerWakeup > 100 ) msecPerWakeup = 100;
+        resolution = msecPerWakeup/4;
+        stream->timerID = timeSetEvent( msecPerWakeup, resolution, (LPTIMECALLBACK) Pa_TimerCallback,
+                                             (DWORD) stream, TIME_PERIODIC );
+    }
+    if( stream->timerID == 0 )
+    {
+        stream->isActive = 0;
+        result = paUnanticipatedHostError;
+        PA_DS_SET_LAST_DIRECTSOUND_ERROR( hr );
+        goto error;
+    }
+
+    stream->isStarted = TRUE;
+
+error:
+    return result;
+}
+
+
+/***********************************************************************************/
+static PaError StopStream( PaStream *s )
+{
+    PaError result = paNoError;
+    PaWinDsStream *stream = (PaWinDsStream*)s;
+    HRESULT          hr;
+    int timeoutMsec;
+
+    stream->stopProcessing = 1;
+    /* Set timeout at 20% beyond maximum time we might wait. */
+    timeoutMsec = (int) (1200.0 * stream->framesPerDSBuffer / stream->streamRepresentation.streamInfo.sampleRate);
+    while( stream->isActive && (timeoutMsec > 0)  )
+    {
+        Sleep(10);
+        timeoutMsec -= 10;
+    }
+    if( stream->timerID != 0 )
+    {
+        timeKillEvent(stream->timerID);  /* Stop callback timer. */
+        stream->timerID = 0;
+    }
+
+
+    if( stream->bufferProcessor.outputChannelCount > 0 )
+    {
+        hr = DSW_StopOutput( &stream->directSoundWrapper );
+    }
+
+    if( stream->bufferProcessor.inputChannelCount > 0 )
+    {
+        hr = DSW_StopInput( &stream->directSoundWrapper );
+    }
+
+    stream->isStarted = FALSE;
+
+    return result;
+}
+
+
+/***********************************************************************************/
+static PaError AbortStream( PaStream *s )
+{
+    PaWinDsStream *stream = (PaWinDsStream*)s;
+
+    stream->abortProcessing = 1;
+    return StopStream( s );
+}
+
+
+/***********************************************************************************/
+static PaError IsStreamStopped( PaStream *s )
+{
+    PaWinDsStream *stream = (PaWinDsStream*)s;
+
+    return !stream->isStarted;
+}
+
+
+/***********************************************************************************/
+static PaError IsStreamActive( PaStream *s )
+{
+    PaWinDsStream *stream = (PaWinDsStream*)s;
+
+    return stream->isActive;
+}
+
+/***********************************************************************************/
+static PaTime GetStreamTime( PaStream *s )
+{
+    /* suppress unused variable warnings */
+    (void) s;
+
+    
+/*
+    new behavior for GetStreamTime is to return a stream based seconds clock
+    used for the outTime parameter to the callback.
+    FIXME: delete this comment when the other unnecessary related code has
+    been cleaned from this file.
+
+    PaWinDsStream *stream = (PaWinDsStream*)s;
+    DSoundWrapper   *dsw;
+    dsw = &stream->directSoundWrapper;
+    return dsw->dsw_FramesPlayed;
+*/
+    return PaUtil_GetTime();
+}
+
+
+/***********************************************************************************/
+static double GetStreamCpuLoad( PaStream* s )
+{
+    PaWinDsStream *stream = (PaWinDsStream*)s;
+
+    return PaUtil_GetCpuLoad( &stream->cpuLoadMeasurer );
+}
+
+
+
+/***********************************************************************************
+    As separate stream interfaces are used for blocking and callback
+    streams, the following functions can be guaranteed to only be called
+    for blocking streams.
+*/
+
+static PaError ReadStream( PaStream* s,
+                           void *buffer,
+                           unsigned long frames )
+{
+    PaWinDsStream *stream = (PaWinDsStream*)s;
+
+    /* suppress unused variable warnings */
+    (void) buffer;
+    (void) frames;
+    (void) stream;
+
+    /* IMPLEMENT ME, see portaudio.h for required behavior*/
+
+    return paNoError;
+}
+
+
+/***********************************************************************************/
+static PaError WriteStream( PaStream* s,
+                            const void *buffer,
+                            unsigned long frames )
+{
+    PaWinDsStream *stream = (PaWinDsStream*)s;
+
+    /* suppress unused variable warnings */
+    (void) buffer;
+    (void) frames;
+    (void) stream;
+
+    /* IMPLEMENT ME, see portaudio.h for required behavior*/
+
+    return paNoError;
+}
+
+
+/***********************************************************************************/
+static signed long GetStreamReadAvailable( PaStream* s )
+{
+    PaWinDsStream *stream = (PaWinDsStream*)s;
+
+    /* suppress unused variable warnings */
+    (void) stream;
+
+    /* IMPLEMENT ME, see portaudio.h for required behavior*/
+
+    return 0;
+}
+
+
+/***********************************************************************************/
+static signed long GetStreamWriteAvailable( PaStream* s )
+{
+    PaWinDsStream *stream = (PaWinDsStream*)s;
+
+    /* suppress unused variable warnings */
+    (void) stream;
+    
+    /* IMPLEMENT ME, see portaudio.h for required behavior*/
+
+    return 0;
+}
+
+
+
diff --git a/src/audio/portaudio/pa_win_wdmks/pa_win_wdmks.c b/src/audio/portaudio/pa_win_wdmks/pa_win_wdmks.c
new file mode 100644
index 0000000000000000000000000000000000000000..18ffb28cf0a60d0f776e12d93fc074a0399d166f
--- /dev/null
+++ b/src/audio/portaudio/pa_win_wdmks/pa_win_wdmks.c
@@ -0,0 +1,3179 @@
+/*
+ * $Id$
+ * PortAudio Windows WDM-KS interface
+ *
+ * Author: Andrew Baldwin
+ * Based on the Open Source API proposed by Ross Bencina
+ * Copyright (c) 1999-2004 Andrew Baldwin, Ross Bencina, 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.
+ */
+
+/** @file
+ @brief Portaudio WDM-KS host API.
+
+ @note This is the implementation of the Portaudio host API using the
+ Windows WDM/Kernel Streaming API in order to enable very low latency
+ playback and recording on all modern Windows platforms (e.g. 2K, XP)
+ Note: This API accesses the device drivers below the usual KMIXER
+ component which is normally used to enable multi-client mixing and
+ format conversion. That means that it will lock out all other users
+ of a device for the duration of active stream using those devices
+*/
+
+#include <stdio.h>
+
+/* Debugging/tracing support */
+
+#define PA_LOGE_
+#define PA_LOGL_
+
+#ifdef __GNUC__
+    #include <initguid.h>
+	#define _WIN32_WINNT 0x0501
+	#define WINVER 0x0501
+#endif
+
+#include <string.h> /* strlen() */
+#include <assert.h>
+
+#include "pa_util.h"
+#include "pa_allocation.h"
+#include "pa_hostapi.h"
+#include "pa_stream.h"
+#include "pa_cpuload.h"
+#include "pa_process.h"
+#include "portaudio.h"
+
+#include <windows.h>
+#include <winioctl.h>
+
+
+#ifdef __GNUC__
+	#undef PA_LOGE_
+	#define PA_LOGE_ PA_DEBUG(("%s {\n",__FUNCTION__))
+	#undef PA_LOGL_
+	#define PA_LOGL_ PA_DEBUG(("} %s\n",__FUNCTION__))
+    /* These defines are set in order to allow the WIndows DirectX
+     * headers to compile with a GCC compiler such as MinGW
+     * NOTE: The headers may generate a few warning in GCC, but
+     * they should compile */
+    #define _INC_MMSYSTEM
+    #define _INC_MMREG
+    #define _NTRTL_ /* Turn off default definition of DEFINE_GUIDEX */
+    #define DEFINE_GUID_THUNK(name,guid) DEFINE_GUID(name,guid)
+    #define DEFINE_GUIDEX(n) DEFINE_GUID_THUNK( n, STATIC_##n )
+    #if !defined( DEFINE_WAVEFORMATEX_GUID )
+        #define DEFINE_WAVEFORMATEX_GUID(x) (USHORT)(x), 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71
+    #endif
+    #define  WAVE_FORMAT_ADPCM      0x0002
+    #define  WAVE_FORMAT_IEEE_FLOAT 0x0003
+    #define  WAVE_FORMAT_ALAW       0x0006
+    #define  WAVE_FORMAT_MULAW      0x0007
+    #define  WAVE_FORMAT_MPEG       0x0050
+    #define  WAVE_FORMAT_DRM        0x0009
+	#define DYNAMIC_GUID_THUNK(l,w1,w2,b1,b2,b3,b4,b5,b6,b7,b8) {l,w1,w2,{b1,b2,b3,b4,b5,b6,b7,b8}}
+	#define DYNAMIC_GUID(data) DYNAMIC_GUID_THUNK(data)
+#endif
+
+#ifdef _MSC_VER
+	#define DYNAMIC_GUID(data) {data}
+    #define _INC_MMREG
+    #define _NTRTL_ /* Turn off default definition of DEFINE_GUIDEX */
+	#undef DEFINE_GUID
+    #define DEFINE_GUID(n,data) EXTERN_C const GUID n = {data}
+    #define DEFINE_GUID_THUNK(n,data) DEFINE_GUID(n,data)
+    #define DEFINE_GUIDEX(n) DEFINE_GUID_THUNK(n, STATIC_##n)
+    #if !defined( DEFINE_WAVEFORMATEX_GUID )
+        #define DEFINE_WAVEFORMATEX_GUID(x) (USHORT)(x), 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71
+    #endif
+    #define  WAVE_FORMAT_ADPCM      0x0002
+    #define  WAVE_FORMAT_IEEE_FLOAT 0x0003
+    #define  WAVE_FORMAT_ALAW       0x0006
+    #define  WAVE_FORMAT_MULAW      0x0007
+    #define  WAVE_FORMAT_MPEG       0x0050
+    #define  WAVE_FORMAT_DRM        0x0009
+#endif
+
+#include <ks.h>
+#include <ksmedia.h>
+#include <tchar.h>
+#include <assert.h>
+#include <stdio.h>
+
+/* These next definitions allow the use of the KSUSER DLL */
+typedef KSDDKAPI DWORD WINAPI KSCREATEPIN(HANDLE, PKSPIN_CONNECT, ACCESS_MASK, PHANDLE);
+extern HMODULE      DllKsUser;
+extern KSCREATEPIN* FunctionKsCreatePin;
+
+/* Forward definition to break circular type reference between pin and filter */
+struct __PaWinWdmFilter;
+typedef struct __PaWinWdmFilter PaWinWdmFilter;
+
+/* The Pin structure
+ * A pin is an input or output node, e.g. for audio flow */
+typedef struct __PaWinWdmPin
+{
+    HANDLE                      handle;
+    PaWinWdmFilter*             parentFilter;
+    unsigned long               pinId;
+    KSPIN_CONNECT*              pinConnect;
+    unsigned long               pinConnectSize;
+    KSDATAFORMAT_WAVEFORMATEX*  ksDataFormatWfx;
+    KSPIN_COMMUNICATION         communication;
+    KSDATARANGE*                dataRanges;
+    KSMULTIPLE_ITEM*            dataRangesItem;
+    KSPIN_DATAFLOW              dataFlow;
+    KSPIN_CINSTANCES            instances;
+    unsigned long               frameSize;
+    int                         maxChannels;
+    unsigned long               formats;
+    int                         bestSampleRate;
+}
+PaWinWdmPin;
+
+/* The Filter structure
+ * A filter has a number of pins and a "friendly name" */
+struct __PaWinWdmFilter
+{
+    HANDLE  handle;
+    int pinCount;
+    PaWinWdmPin** pins;
+    TCHAR filterName[MAX_PATH];
+    TCHAR friendlyName[MAX_PATH];
+    int maxInputChannels;
+    int maxOutputChannels;
+    unsigned long formats;
+    int usageCount;
+    int bestSampleRate;
+};
+
+/* PaWinWdmHostApiRepresentation - host api datastructure specific to this implementation */
+typedef struct __PaWinWdmHostApiRepresentation
+{
+    PaUtilHostApiRepresentation inheritedHostApiRep;
+    PaUtilStreamInterface callbackStreamInterface;
+    PaUtilStreamInterface blockingStreamInterface;
+
+    PaUtilAllocationGroup *allocations;
+    PaWinWdmFilter** filters;
+    int filterCount;
+}
+PaWinWdmHostApiRepresentation;
+
+typedef struct __PaWinWdmDeviceInfo
+{
+    PaDeviceInfo inheritedDeviceInfo;
+    PaWinWdmFilter* filter;
+}
+PaWinWdmDeviceInfo;
+
+typedef struct __DATAPACKET
+{
+    KSSTREAM_HEADER Header;
+    OVERLAPPED      Signal;
+} DATAPACKET;
+
+/* PaWinWdmStream - a stream data structure specifically for this implementation */
+typedef struct __PaWinWdmStream
+{
+    PaUtilStreamRepresentation streamRepresentation;
+    PaUtilCpuLoadMeasurer cpuLoadMeasurer;
+    PaUtilBufferProcessor bufferProcessor;
+
+    PaWinWdmPin*   recordingPin;
+    PaWinWdmPin*   playbackPin;
+    char*     hostBuffer;
+    unsigned long  framesPerHostIBuffer;
+    unsigned long  framesPerHostOBuffer;
+    int        bytesPerInputFrame;
+    int        bytesPerOutputFrame;
+    int       streamStarted;
+    int       streamActive;
+    int        streamStop;
+    int        streamAbort;
+    int       oldProcessPriority;
+    HANDLE       streamThread;
+    HANDLE       events[5]; /* 2 play + 2 record packets + abort events */
+    DATAPACKET   packets[4]; /* 2 play + 2 record */
+    PaStreamFlags   streamFlags;
+    /* These values handle the case where the user wants to use fewer
+     * channels than the device has */
+    int             userInputChannels;
+    int             deviceInputChannels;
+    int             userOutputChannels;
+    int             deviceOutputChannels;
+    int             inputSampleSize;
+    int             outputSampleSize;
+}
+PaWinWdmStream;
+
+#include <setupapi.h>
+
+HMODULE      DllKsUser = NULL;
+KSCREATEPIN* FunctionKsCreatePin = NULL;
+
+/* prototypes for functions declared in this file */
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif /* __cplusplus */
+
+PaError PaWinWdm_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+/* Low level I/O functions */
+static PaError WdmSyncIoctl(HANDLE handle,
+    unsigned long ioctlNumber,
+    void* inBuffer,
+    unsigned long inBufferCount,
+    void* outBuffer,
+    unsigned long outBufferCount,
+    unsigned long* bytesReturned);
+static PaError WdmGetPropertySimple(HANDLE handle,
+    const GUID* const guidPropertySet,
+    unsigned long property,
+    void* value,
+    unsigned long valueCount,
+    void* instance,
+    unsigned long instanceCount);
+static PaError WdmSetPropertySimple(HANDLE handle,
+    const GUID* const guidPropertySet,
+    unsigned long property,
+    void* value,
+    unsigned long valueCount,
+    void* instance,
+    unsigned long instanceCount);
+static PaError WdmGetPinPropertySimple(HANDLE  handle,
+    unsigned long pinId,
+    const GUID* const guidPropertySet,
+    unsigned long property,
+    void* value,
+    unsigned long valueCount);
+static PaError WdmGetPinPropertyMulti(HANDLE  handle,
+    unsigned long pinId,
+    const GUID* const guidPropertySet,
+    unsigned long property,
+    KSMULTIPLE_ITEM** ksMultipleItem);
+
+/** Pin management functions */
+static PaWinWdmPin* PinNew(PaWinWdmFilter* parentFilter, unsigned long pinId, PaError* error);
+static void PinFree(PaWinWdmPin* pin);
+static void PinClose(PaWinWdmPin* pin);
+static PaError PinInstantiate(PaWinWdmPin* pin);
+/*static PaError PinGetState(PaWinWdmPin* pin, KSSTATE* state); NOT USED */
+static PaError PinSetState(PaWinWdmPin* pin, KSSTATE state);
+static PaError PinSetFormat(PaWinWdmPin* pin, const WAVEFORMATEX* format);
+static PaError PinIsFormatSupported(PaWinWdmPin* pin, const WAVEFORMATEX* format);
+
+/* Filter management functions */
+static PaWinWdmFilter* FilterNew(
+    TCHAR* filterName,
+    TCHAR* friendlyName,
+    PaError* error);
+static void FilterFree(PaWinWdmFilter* filter);
+static PaWinWdmPin* FilterCreateRenderPin(
+    PaWinWdmFilter* filter,
+    const WAVEFORMATEX* wfex,
+    PaError* error);
+static PaWinWdmPin* FilterFindViableRenderPin(
+    PaWinWdmFilter* filter,
+    const WAVEFORMATEX* wfex,
+    PaError* error);
+static PaError FilterCanCreateRenderPin(
+    PaWinWdmFilter* filter,
+    const WAVEFORMATEX* wfex);
+static PaWinWdmPin* FilterCreateCapturePin(
+    PaWinWdmFilter* filter,
+    const WAVEFORMATEX* wfex,
+    PaError* error);
+static PaWinWdmPin* FilterFindViableCapturePin(
+    PaWinWdmFilter* filter,
+    const WAVEFORMATEX* wfex,
+    PaError* error);
+static PaError FilterCanCreateCapturePin(
+    PaWinWdmFilter* filter,
+    const WAVEFORMATEX* pwfx);
+static PaError FilterUse(
+    PaWinWdmFilter* filter);
+static void FilterRelease(
+    PaWinWdmFilter* filter);
+
+/* Interface functions */
+static void Terminate( struct PaUtilHostApiRepresentation *hostApi );
+static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
+                                  const PaStreamParameters *inputParameters,
+                                  const PaStreamParameters *outputParameters,
+                                  double sampleRate );
+static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
+                           PaStream** s,
+                           const PaStreamParameters *inputParameters,
+                           const PaStreamParameters *outputParameters,
+                           double sampleRate,
+                           unsigned long framesPerBuffer,
+                           PaStreamFlags streamFlags,
+                           PaStreamCallback *streamCallback,
+                           void *userData );
+static PaError CloseStream( PaStream* stream );
+static PaError StartStream( PaStream *stream );
+static PaError StopStream( PaStream *stream );
+static PaError AbortStream( PaStream *stream );
+static PaError IsStreamStopped( PaStream *s );
+static PaError IsStreamActive( PaStream *stream );
+static PaTime GetStreamTime( PaStream *stream );
+static double GetStreamCpuLoad( PaStream* stream );
+static PaError ReadStream( PaStream* stream, void *buffer, unsigned long frames );
+static PaError WriteStream( PaStream* stream, const void *buffer, unsigned long frames );
+static signed long GetStreamReadAvailable( PaStream* stream );
+static signed long GetStreamWriteAvailable( PaStream* stream );
+
+/* Utility functions */
+static unsigned long GetWfexSize(const WAVEFORMATEX* wfex);
+static PaError BuildFilterList(PaWinWdmHostApiRepresentation* wdmHostApi);
+static BOOL PinWrite(HANDLE h, DATAPACKET* p);
+static BOOL PinRead(HANDLE h, DATAPACKET* p);
+static void DuplicateFirstChannelInt16(void* buffer, int channels, int samples);
+static void DuplicateFirstChannelInt24(void* buffer, int channels, int samples);
+static DWORD WINAPI ProcessingThread(LPVOID pParam);
+
+/* Function bodies */
+
+static unsigned long GetWfexSize(const WAVEFORMATEX* wfex)
+{
+    if ( wfex->wFormatTag == WAVE_FORMAT_PCM )
+    {
+        return sizeof( WAVEFORMATEX );
+    }
+    else
+    {
+        return (sizeof( WAVEFORMATEX ) + wfex->cbSize);
+    }
+}
+
+/*
+Low level pin/filter access functions
+*/
+static PaError WdmSyncIoctl(HANDLE handle,
+    unsigned long ioctlNumber,
+    void* inBuffer,
+    unsigned long inBufferCount,
+    void* outBuffer,
+    unsigned long outBufferCount,
+    unsigned long* bytesReturned)
+{
+    PaError result = paNoError;
+    OVERLAPPED overlapped;
+    int boolResult;
+    unsigned long dummyBytesReturned;
+    unsigned long error;
+
+    if (!bytesReturned)
+    {
+        /* User a dummy as the caller hasn't supplied one */
+        bytesReturned = &dummyBytesReturned;
+    }
+
+	  FillMemory((void *)&overlapped,sizeof(overlapped),0);
+    overlapped.hEvent = CreateEvent(NULL,FALSE,FALSE,NULL);
+    if ( !overlapped.hEvent )
+    {
+      	result = paInsufficientMemory;
+        goto error;
+    }
+    overlapped.hEvent = (HANDLE)((DWORD_PTR)overlapped.hEvent | 0x1);
+
+    boolResult = DeviceIoControl(handle, ioctlNumber, inBuffer, inBufferCount,
+					outBuffer, outBufferCount, bytesReturned, &overlapped);
+	  if ( !boolResult )
+    {
+        error = GetLastError();
+        if ( error == ERROR_IO_PENDING )
+        {
+            error = WaitForSingleObject(overlapped.hEvent,INFINITE);
+            if ( error != WAIT_OBJECT_0 )
+            {
+                result = paUnanticipatedHostError;
+                goto error;
+            }
+        }
+        else if ((( error == ERROR_INSUFFICIENT_BUFFER ) ||
+        		  ( error == ERROR_MORE_DATA )) &&
+            	  ( ioctlNumber == IOCTL_KS_PROPERTY ) &&
+            	  ( outBufferCount == 0 ))
+        {
+            boolResult = TRUE;
+        }
+        else
+        {
+            result = paUnanticipatedHostError;
+        }
+    }
+    if ( !boolResult )
+        *bytesReturned = 0;
+
+error:
+    if ( overlapped.hEvent )
+    {
+		    CloseHandle( overlapped.hEvent );
+	  }
+	  return result;
+}
+
+static PaError WdmGetPropertySimple(HANDLE handle,
+    const GUID* const guidPropertySet,
+	  unsigned long property,
+	  void* value,
+	  unsigned long valueCount,
+	  void* instance,
+	  unsigned long instanceCount)
+{
+    PaError result;
+    KSPROPERTY* ksProperty;
+    unsigned long propertyCount;
+
+    propertyCount = sizeof(KSPROPERTY) + instanceCount;
+   	ksProperty = (KSPROPERTY*)PaUtil_AllocateMemory( propertyCount );
+    if( !ksProperty )
+    {
+    	  return paInsufficientMemory;
+    }
+
+	  FillMemory((void*)ksProperty,sizeof(ksProperty),0);
+    ksProperty->Set = *guidPropertySet;
+    ksProperty->Id = property;
+    ksProperty->Flags = KSPROPERTY_TYPE_GET;
+
+	  if ( instance )
+	  {
+		    memcpy( (void*)(((char*)ksProperty)+sizeof(KSPROPERTY)), instance, instanceCount );
+	  }
+
+	  result = WdmSyncIoctl(
+                handle,
+                IOCTL_KS_PROPERTY,
+                ksProperty,
+                propertyCount,
+                value,
+                valueCount,
+                NULL);
+
+	  PaUtil_FreeMemory( ksProperty );
+    return result;
+}
+
+static PaError WdmSetPropertySimple(
+	  HANDLE handle,
+    const GUID* const guidPropertySet,
+    unsigned long property,
+    void* value,
+    unsigned long valueCount,
+    void* instance,
+    unsigned long instanceCount)
+{
+    PaError result;
+    KSPROPERTY* ksProperty;
+    unsigned long propertyCount  = 0;
+
+    propertyCount = sizeof(KSPROPERTY) + instanceCount;
+   	ksProperty = (KSPROPERTY*)PaUtil_AllocateMemory( propertyCount );
+    if( !ksProperty )
+    {
+      	return paInsufficientMemory;
+    }
+
+    ksProperty->Set = *guidPropertySet;
+    ksProperty->Id = property;
+    ksProperty->Flags = KSPROPERTY_TYPE_SET;
+
+    if ( instance )
+    {
+        memcpy((void*)((char*)ksProperty + sizeof(KSPROPERTY)), instance, instanceCount);
+    }
+
+    result = WdmSyncIoctl(
+                handle,
+                IOCTL_KS_PROPERTY,
+                ksProperty,
+                propertyCount,
+                value,
+                valueCount,
+                NULL);
+
+	  PaUtil_FreeMemory( ksProperty );
+    return result;
+}
+
+static PaError WdmGetPinPropertySimple(
+	  HANDLE  handle,
+    unsigned long pinId,
+    const GUID* const guidPropertySet,
+    unsigned long property,
+    void* value,
+    unsigned long valueCount)
+{
+    PaError result;
+
+    KSP_PIN ksPProp;
+    ksPProp.Property.Set = *guidPropertySet;
+    ksPProp.Property.Id = property;
+    ksPProp.Property.Flags = KSPROPERTY_TYPE_GET;
+    ksPProp.PinId = pinId;
+    ksPProp.Reserved = 0;
+
+    result = WdmSyncIoctl(
+        handle,
+        IOCTL_KS_PROPERTY,
+        &ksPProp,
+        sizeof(KSP_PIN),
+        value,
+        valueCount,
+        NULL);
+
+    return result;
+}
+
+static PaError WdmGetPinPropertyMulti(
+	  HANDLE	handle,
+    unsigned long pinId,
+    const GUID* const guidPropertySet,
+    unsigned long property,
+    KSMULTIPLE_ITEM** ksMultipleItem)
+{
+    PaError result;
+    unsigned long   multipleItemSize = 0;
+    KSP_PIN ksPProp;
+
+    ksPProp.Property.Set = *guidPropertySet;
+    ksPProp.Property.Id = property;
+    ksPProp.Property.Flags = KSPROPERTY_TYPE_GET;
+    ksPProp.PinId = pinId;
+    ksPProp.Reserved = 0;
+
+    result = WdmSyncIoctl(
+	        handle,
+	        IOCTL_KS_PROPERTY,
+	        &ksPProp.Property,
+	        sizeof(KSP_PIN),
+	        NULL,
+	        0,
+	        &multipleItemSize);
+	  if ( result != paNoError )
+	  {
+		    return result;
+	  }
+
+	  *ksMultipleItem = (KSMULTIPLE_ITEM*)PaUtil_AllocateMemory( multipleItemSize );
+    if( !*ksMultipleItem )
+    {
+       	return paInsufficientMemory;
+    }
+
+    result = WdmSyncIoctl(
+           handle,
+           IOCTL_KS_PROPERTY,
+           &ksPProp,
+           sizeof(KSP_PIN),
+           (void*)*ksMultipleItem,
+           multipleItemSize,
+           NULL);
+
+    if ( result != paNoError )
+    {
+		    PaUtil_FreeMemory( ksMultipleItem );
+    }
+
+    return result;
+}
+
+
+/*
+Create a new pin object belonging to a filter
+The pin object holds all the configuration information about the pin
+before it is opened, and then the handle of the pin after is opened
+*/
+static PaWinWdmPin* PinNew(PaWinWdmFilter* parentFilter, unsigned long pinId, PaError* error)
+{
+    PaWinWdmPin* pin;
+    PaError result;
+    unsigned long i;
+    KSMULTIPLE_ITEM* item = NULL;
+    KSIDENTIFIER* identifier;
+    KSDATARANGE* dataRange;
+
+    PA_LOGE_;
+    PA_DEBUG(("Creating pin %d:\n",pinId));
+
+    /* Allocate the new PIN object */
+    pin = (PaWinWdmPin*)PaUtil_AllocateMemory( sizeof(PaWinWdmPin) );
+    if( !pin )
+    {
+        result = paInsufficientMemory;
+        goto error;
+    }
+
+    /* Zero the pin object */
+    /* memset( (void*)pin, 0, sizeof(PaWinWdmPin) ); */
+
+    pin->parentFilter = parentFilter;
+    pin->pinId = pinId;
+
+    /* Allocate a connect structure */
+    pin->pinConnectSize = sizeof(KSPIN_CONNECT) + sizeof(KSDATAFORMAT_WAVEFORMATEX);
+    pin->pinConnect = (KSPIN_CONNECT*)PaUtil_AllocateMemory( pin->pinConnectSize );
+    if( !pin->pinConnect )
+    {
+        result = paInsufficientMemory;
+        goto error;
+    }
+
+    /* Configure the connect structure with default values */
+    pin->pinConnect->Interface.Set              = KSINTERFACESETID_Standard;
+    pin->pinConnect->Interface.Id               = KSINTERFACE_STANDARD_STREAMING;
+    pin->pinConnect->Interface.Flags            = 0;
+    pin->pinConnect->Medium.Set                 = KSMEDIUMSETID_Standard;
+    pin->pinConnect->Medium.Id                  = KSMEDIUM_TYPE_ANYINSTANCE;
+    pin->pinConnect->Medium.Flags               = 0;
+    pin->pinConnect->PinId                      = pinId;
+    pin->pinConnect->PinToHandle                = NULL;
+    pin->pinConnect->Priority.PriorityClass     = KSPRIORITY_NORMAL;
+    pin->pinConnect->Priority.PrioritySubClass  = 1;
+    pin->ksDataFormatWfx = (KSDATAFORMAT_WAVEFORMATEX*)(pin->pinConnect + 1);
+    pin->ksDataFormatWfx->DataFormat.FormatSize = sizeof(KSDATAFORMAT_WAVEFORMATEX);
+    pin->ksDataFormatWfx->DataFormat.Flags = 0;
+    pin->ksDataFormatWfx->DataFormat.Reserved = 0;
+    pin->ksDataFormatWfx->DataFormat.MajorFormat = KSDATAFORMAT_TYPE_AUDIO;
+    pin->ksDataFormatWfx->DataFormat.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
+    pin->ksDataFormatWfx->DataFormat.Specifier = KSDATAFORMAT_SPECIFIER_WAVEFORMATEX;
+
+    pin->frameSize = 0; /* Unknown until we instantiate pin */
+
+    /* Get the COMMUNICATION property */
+    result = WdmGetPinPropertySimple(
+        parentFilter->handle,
+        pinId,
+        &KSPROPSETID_Pin,
+        KSPROPERTY_PIN_COMMUNICATION,
+        &pin->communication,
+        sizeof(KSPIN_COMMUNICATION));
+    if ( result != paNoError )
+        goto error;
+
+    if ( /*(pin->communication != KSPIN_COMMUNICATION_SOURCE) &&*/
+         (pin->communication != KSPIN_COMMUNICATION_SINK) &&
+         (pin->communication != KSPIN_COMMUNICATION_BOTH) )
+    {
+        PA_DEBUG(("Not source/sink\n"));
+        result = paInvalidDevice;
+        goto error;
+    }
+
+    /* Get dataflow information */
+    result = WdmGetPinPropertySimple(
+        parentFilter->handle,
+        pinId,
+        &KSPROPSETID_Pin,
+        KSPROPERTY_PIN_DATAFLOW,
+        &pin->dataFlow,
+        sizeof(KSPIN_DATAFLOW));
+
+    if ( result != paNoError )
+        goto error;
+
+    /* Get the INTERFACE property list */
+    result = WdmGetPinPropertyMulti(
+        parentFilter->handle,
+        pinId,
+        &KSPROPSETID_Pin,
+        KSPROPERTY_PIN_INTERFACES,
+        &item);
+
+    if ( result != paNoError )
+        goto error;
+
+    identifier = (KSIDENTIFIER*)(item+1);
+
+    /* Check that at least one interface is STANDARD_STREAMING */
+    result = paUnanticipatedHostError;
+    for ( i = 0; i < item->Count; i++ )
+    {
+        if ( !memcmp( (void*)&identifier[i].Set, (void*)&KSINTERFACESETID_Standard, sizeof( GUID ) ) &&
+            ( identifier[i].Id == KSINTERFACE_STANDARD_STREAMING ) )
+        {
+            result = paNoError;
+            break;
+        }
+    }
+
+    if ( result != paNoError )
+    {
+        PA_DEBUG(("No standard streaming\n"));
+        goto error;
+    }
+
+    /* Don't need interfaces any more */
+    PaUtil_FreeMemory( item );
+    item = NULL;
+
+    /* Get the MEDIUM properties list */
+    result = WdmGetPinPropertyMulti(
+        parentFilter->handle,
+        pinId,
+        &KSPROPSETID_Pin,
+        KSPROPERTY_PIN_MEDIUMS,
+        &item);
+
+    if ( result != paNoError )
+        goto error;
+
+    identifier = (KSIDENTIFIER*)(item+1); /* Not actually necessary... */
+
+    /* Check that at least one medium is STANDARD_DEVIO */
+    result = paUnanticipatedHostError;
+    for ( i = 0; i < item->Count; i++ )
+    {
+        if ( !memcmp( (void*)&identifier[i].Set, (void*)&KSMEDIUMSETID_Standard, sizeof( GUID ) ) &&
+           ( identifier[i].Id == KSMEDIUM_STANDARD_DEVIO ) )
+        {
+            result = paNoError;
+            break;
+        }
+    }
+
+    if ( result != paNoError )
+    {
+        PA_DEBUG(("No standard devio\n"));
+        goto error;
+    }
+    /* Don't need mediums any more */
+    PaUtil_FreeMemory( item );
+    item = NULL;
+
+    /* Get DATARANGES */
+    result = WdmGetPinPropertyMulti(
+        parentFilter->handle,
+        pinId,
+        &KSPROPSETID_Pin,
+        KSPROPERTY_PIN_DATARANGES,
+        &pin->dataRangesItem);
+
+    if ( result != paNoError )
+        goto error;
+
+    pin->dataRanges = (KSDATARANGE*)(pin->dataRangesItem +1);
+
+    /* Check that at least one datarange supports audio */
+    result = paUnanticipatedHostError;
+    dataRange = pin->dataRanges;
+    pin->maxChannels = 0;
+    pin->bestSampleRate = 0;
+		pin->formats = 0;
+    for ( i = 0; i <pin->dataRangesItem->Count; i++)
+    {
+        PA_DEBUG(("DR major format %x\n",*(unsigned long*)(&(dataRange->MajorFormat))));
+        /* Check that subformat is WAVEFORMATEX, PCM or WILDCARD */
+        if (
+            IS_VALID_WAVEFORMATEX_GUID(&dataRange->SubFormat) ||
+            !memcmp((void*)&dataRange->SubFormat, (void*)&KSDATAFORMAT_SUBTYPE_PCM, sizeof ( GUID ) ) ||
+            ( !memcmp((void*)&dataRange->SubFormat, (void*)&KSDATAFORMAT_SUBTYPE_WILDCARD, sizeof ( GUID ) ) &&
+            ( !memcmp((void*)&dataRange->MajorFormat, (void*)&KSDATAFORMAT_TYPE_AUDIO, sizeof ( GUID ) ) ) ) )
+        {
+            result = paNoError;
+            /* Record the maximum possible channels with this pin */
+            PA_DEBUG(("MaxChannel: %d\n",pin->maxChannels));
+            if ((int)((KSDATARANGE_AUDIO*)dataRange)->MaximumChannels > pin->maxChannels)
+            {
+                pin->maxChannels = ((KSDATARANGE_AUDIO*)dataRange)->MaximumChannels;
+                /*PA_DEBUG(("MaxChannel: %d\n",pin->maxChannels));*/
+            }
+            /* Record the formats (bit depths) that are supported */
+            if (((KSDATARANGE_AUDIO*)dataRange)->MinimumBitsPerSample <= 16)
+            {
+                pin->formats |= paInt16;
+                PA_DEBUG(("Format 16 bit supported\n"));
+            }
+            if (((KSDATARANGE_AUDIO*)dataRange)->MaximumBitsPerSample >= 24)
+            {
+                pin->formats |= paInt24;
+                PA_DEBUG(("Format 24 bit supported\n"));
+            }
+            if (
+                ( pin->bestSampleRate != 48000) &&
+                (((KSDATARANGE_AUDIO*)dataRange)->MaximumSampleFrequency >= 48000) &&
+                (((KSDATARANGE_AUDIO*)dataRange)->MinimumSampleFrequency <= 48000) )
+            {
+                pin->bestSampleRate = 48000;
+                PA_DEBUG(("48kHz supported\n"));
+            }
+            else if (
+                ( pin->bestSampleRate != 48000) && ( pin->bestSampleRate != 44100 ) &&
+                (((KSDATARANGE_AUDIO*)dataRange)->MaximumSampleFrequency >= 44100) &&
+                (((KSDATARANGE_AUDIO*)dataRange)->MinimumSampleFrequency <= 44100) )
+            {
+                pin->bestSampleRate = 44100;
+                PA_DEBUG(("44.1kHz supported\n"));
+            }
+            else
+            {
+                pin->bestSampleRate = ((KSDATARANGE_AUDIO*)dataRange)->MaximumSampleFrequency;
+            }
+        }
+        dataRange = (KSDATARANGE*)( ((char*)dataRange) + dataRange->FormatSize);
+    }
+
+    if ( result != paNoError )
+        goto error;
+
+    /* Get instance information */
+    result = WdmGetPinPropertySimple(
+        parentFilter->handle,
+        pinId,
+        &KSPROPSETID_Pin,
+        KSPROPERTY_PIN_CINSTANCES,
+        &pin->instances,
+        sizeof(KSPIN_CINSTANCES));
+
+    if ( result != paNoError )
+        goto error;
+
+    /* Success */
+    *error = paNoError;
+    PA_DEBUG(("Pin created successfully\n"));
+    PA_LOGL_;
+    return pin;
+
+error:
+    /*
+    Error cleanup
+    */
+    PaUtil_FreeMemory( item );
+    if ( pin )
+    {
+        PaUtil_FreeMemory( pin->pinConnect );
+        PaUtil_FreeMemory( pin->dataRangesItem );
+        PaUtil_FreeMemory( pin );
+    }
+    *error = result;
+    PA_LOGL_;
+    return NULL;
+}
+
+/*
+Safely free all resources associated with the pin
+*/
+static void PinFree(PaWinWdmPin* pin)
+{
+    PA_LOGE_;
+    if ( pin )
+    {
+        PinClose(pin);
+        if ( pin->pinConnect )
+        {
+            PaUtil_FreeMemory( pin->pinConnect );
+        }
+        if ( pin->dataRangesItem )
+        {
+            PaUtil_FreeMemory( pin->dataRangesItem );
+        }
+        PaUtil_FreeMemory( pin );
+    }
+    PA_LOGL_;
+}
+
+/*
+If the pin handle is open, close it
+*/
+static void PinClose(PaWinWdmPin* pin)
+{
+    PA_LOGE_;
+    if ( pin == NULL )
+    {
+        PA_DEBUG(("Closing NULL pin!"));
+        PA_LOGL_;
+        return;
+    }
+    if ( pin->handle != NULL )
+    {
+        PinSetState( pin, KSSTATE_PAUSE );
+        PinSetState( pin, KSSTATE_STOP );
+        CloseHandle( pin->handle );
+        pin->handle = NULL;
+        FilterRelease(pin->parentFilter);
+    }
+    PA_LOGL_;
+}
+
+/*
+Set the state of this (instantiated) pin
+*/
+static PaError PinSetState(PaWinWdmPin* pin, KSSTATE state)
+{
+    PaError result;
+
+    PA_LOGE_;
+    if ( pin == NULL )
+        return paInternalError;
+    if ( pin->handle == NULL )
+        return paInternalError;
+
+    result = WdmSetPropertySimple(
+        pin->handle,
+        &KSPROPSETID_Connection,
+        KSPROPERTY_CONNECTION_STATE,
+        &state,
+        sizeof(state),
+        NULL,
+        0);
+    PA_LOGL_;
+    return result;
+}
+
+static PaError PinInstantiate(PaWinWdmPin* pin)
+{
+    PaError result;
+    unsigned long createResult;
+    KSALLOCATOR_FRAMING ksaf;
+    KSALLOCATOR_FRAMING_EX ksafex;
+
+    PA_LOGE_;
+
+    if ( pin == NULL )
+        return paInternalError;
+    if (!pin->pinConnect)
+        return paInternalError;
+
+    FilterUse(pin->parentFilter);
+
+    createResult = FunctionKsCreatePin(
+        pin->parentFilter->handle,
+        pin->pinConnect,
+        GENERIC_WRITE | GENERIC_READ,
+        &pin->handle
+        );
+
+    PA_DEBUG(("Pin create result = %x\n",createResult));
+    if ( createResult != ERROR_SUCCESS )
+    {
+        FilterRelease(pin->parentFilter);
+        pin->handle = NULL;
+        return paInvalidDevice;
+    }
+
+    result = WdmGetPropertySimple(
+        pin->handle,
+        &KSPROPSETID_Connection,
+        KSPROPERTY_CONNECTION_ALLOCATORFRAMING,
+        &ksaf,
+        sizeof(ksaf),
+        NULL,
+        0);
+
+    if ( result != paNoError )
+    {
+        result = WdmGetPropertySimple(
+            pin->handle,
+            &KSPROPSETID_Connection,
+            KSPROPERTY_CONNECTION_ALLOCATORFRAMING_EX,
+            &ksafex,
+            sizeof(ksafex),
+            NULL,
+            0);
+        if ( result == paNoError )
+        {
+            pin->frameSize = ksafex.FramingItem[0].FramingRange.Range.MinFrameSize;
+        }
+    }
+    else
+    {
+        pin->frameSize = ksaf.FrameSize;
+    }
+
+    PA_LOGL_;
+
+    return paNoError;
+}
+
+/* NOT USED
+static PaError PinGetState(PaWinWdmPin* pin, KSSTATE* state)
+{
+    PaError result;
+
+    if ( state == NULL )
+        return paInternalError;
+    if ( pin == NULL )
+        return paInternalError;
+    if ( pin->handle == NULL )
+        return paInternalError;
+
+    result = WdmGetPropertySimple(
+        pin->handle,
+        KSPROPSETID_Connection,
+        KSPROPERTY_CONNECTION_STATE,
+        state,
+        sizeof(KSSTATE),
+        NULL,
+        0);
+
+    return result;
+}
+*/
+static PaError PinSetFormat(PaWinWdmPin* pin, const WAVEFORMATEX* format)
+{
+    unsigned long size;
+    void* newConnect;
+
+    PA_LOGE_;
+
+    if ( pin == NULL )
+        return paInternalError;
+    if ( format == NULL )
+        return paInternalError;
+
+    size = GetWfexSize(format) + sizeof(KSPIN_CONNECT) + sizeof(KSDATAFORMAT_WAVEFORMATEX) - sizeof(WAVEFORMATEX);
+
+    if ( pin->pinConnectSize != size )
+    {
+        newConnect = PaUtil_AllocateMemory( size );
+        if ( newConnect == NULL )
+            return paInsufficientMemory;
+        memcpy( newConnect, (void*)pin->pinConnect, min(pin->pinConnectSize,size) );
+        PaUtil_FreeMemory( pin->pinConnect );
+        pin->pinConnect = (KSPIN_CONNECT*)newConnect;
+        pin->pinConnectSize = size;
+        pin->ksDataFormatWfx = (KSDATAFORMAT_WAVEFORMATEX*)((KSPIN_CONNECT*)newConnect + 1);
+        pin->ksDataFormatWfx->DataFormat.FormatSize = size - sizeof(KSPIN_CONNECT);
+    }
+
+    memcpy( (void*)&(pin->ksDataFormatWfx->WaveFormatEx), format, GetWfexSize(format) );
+    pin->ksDataFormatWfx->DataFormat.SampleSize = (unsigned short)(format->nChannels * (format->wBitsPerSample / 8));
+
+    PA_LOGL_;
+
+    return paNoError;
+}
+
+static PaError PinIsFormatSupported(PaWinWdmPin* pin, const WAVEFORMATEX* format)
+{
+    KSDATARANGE_AUDIO* dataRange;
+    unsigned long count;
+    GUID guid = DYNAMIC_GUID( DEFINE_WAVEFORMATEX_GUID(format->wFormatTag) );
+    PaError result = paInvalidDevice;
+
+    PA_LOGE_;
+
+    if ( format->wFormatTag == WAVE_FORMAT_EXTENSIBLE )
+    {
+        guid = ((WAVEFORMATEXTENSIBLE*)format)->SubFormat;
+    }
+    dataRange = (KSDATARANGE_AUDIO*)pin->dataRanges;
+    for (count = 0; count<pin->dataRangesItem->Count; count++)
+    {
+        if (( !memcmp(&(dataRange->DataRange.MajorFormat),&KSDATAFORMAT_TYPE_AUDIO,sizeof(GUID)) ) ||
+           ( !memcmp(&(dataRange->DataRange.MajorFormat),&KSDATAFORMAT_TYPE_WILDCARD,sizeof(GUID)) ))
+        {
+            /* This is an audio or wildcard datarange... */
+            if (( !memcmp(&(dataRange->DataRange.SubFormat),&KSDATAFORMAT_SUBTYPE_WILDCARD,sizeof(GUID)) ) ||
+                ( !memcmp(&(dataRange->DataRange.SubFormat),&guid,sizeof(GUID)) ))
+            {
+                if (( !memcmp(&(dataRange->DataRange.Specifier),&KSDATAFORMAT_SPECIFIER_WILDCARD,sizeof(GUID)) ) ||
+                  ( !memcmp(&(dataRange->DataRange.Specifier),&KSDATAFORMAT_SPECIFIER_WAVEFORMATEX,sizeof(GUID) )))
+                {
+
+                    PA_DEBUG(("Pin:%x, DataRange:%d\n",(void*)pin,count));
+                    PA_DEBUG(("\tFormatSize:%d, SampleSize:%d\n",dataRange->DataRange.FormatSize,dataRange->DataRange.SampleSize));
+                    PA_DEBUG(("\tMaxChannels:%d\n",dataRange->MaximumChannels));
+                    PA_DEBUG(("\tBits:%d-%d\n",dataRange->MinimumBitsPerSample,dataRange->MaximumBitsPerSample));
+                    PA_DEBUG(("\tSampleRate:%d-%d\n",dataRange->MinimumSampleFrequency,dataRange->MaximumSampleFrequency));
+
+                    if ( dataRange->MaximumChannels < format->nChannels )
+                    {
+                        result = paInvalidChannelCount;
+                        continue;
+                    }
+                    if ( dataRange->MinimumBitsPerSample > format->wBitsPerSample )
+                    {
+                        result = paSampleFormatNotSupported;
+                        continue;
+                    }
+                    if ( dataRange->MaximumBitsPerSample < format->wBitsPerSample )
+                    {
+                        result = paSampleFormatNotSupported;
+                        continue;
+                    }
+                    if ( dataRange->MinimumSampleFrequency > format->nSamplesPerSec )
+                    {
+                        result = paInvalidSampleRate;
+                        continue;
+                    }
+                    if ( dataRange->MaximumSampleFrequency < format->nSamplesPerSec )
+                    {
+                        result = paInvalidSampleRate;
+                        continue;
+                    }
+                    /* Success! */
+                    PA_LOGL_;
+                    return paNoError;
+                }
+            }
+        }
+        dataRange = (KSDATARANGE_AUDIO*)( ((char*)dataRange) + dataRange->DataRange.FormatSize);
+    }
+
+    PA_LOGL_;
+
+    return result;
+}
+
+/**
+ * Create a new filter object
+ */
+static PaWinWdmFilter* FilterNew(TCHAR* filterName, TCHAR* friendlyName, PaError* error)
+{
+    PaWinWdmFilter* filter;
+    PaError result;
+    int pinId;
+    int valid;
+
+
+    /* Allocate the new filter object */
+    filter = (PaWinWdmFilter*)PaUtil_AllocateMemory( sizeof(PaWinWdmFilter) );
+    if( !filter )
+    {
+        result = paInsufficientMemory;
+        goto error;
+    }
+
+    /* Zero the filter object - done by AllocateMemory */
+    /* memset( (void*)filter, 0, sizeof(PaWinWdmFilter) ); */
+
+    /* Copy the filter name */
+    _tcsncpy(filter->filterName, filterName, MAX_PATH);
+
+    /* Copy the friendly name */
+    _tcsncpy(filter->friendlyName, friendlyName, MAX_PATH);
+
+    /* Open the filter handle */
+    result = FilterUse(filter);
+    if ( result != paNoError )
+    {
+        goto error;
+    }
+
+    /* Get pin count */
+    result = WdmGetPinPropertySimple
+        (
+        filter->handle,
+        0,
+        &KSPROPSETID_Pin,
+        KSPROPERTY_PIN_CTYPES,
+        &filter->pinCount,
+        sizeof(filter->pinCount)
+        );
+
+    if ( result != paNoError)
+    {
+        goto error;
+    }
+
+    /* Allocate pointer array to hold the pins */
+    filter->pins = (PaWinWdmPin**)PaUtil_AllocateMemory( sizeof(PaWinWdmPin*) * filter->pinCount );
+    if( !filter->pins )
+    {
+        result = paInsufficientMemory;
+        goto error;
+    }
+
+    /* Create all the pins we can */
+    filter->maxInputChannels = 0;
+    filter->maxOutputChannels = 0;
+    filter->bestSampleRate = 0;
+
+    valid = 0;
+    for (pinId = 0; pinId < filter->pinCount; pinId++)
+    {
+        /* Create the pin with this Id */
+        PaWinWdmPin* newPin;
+        newPin = PinNew(filter, pinId, &result);
+        if ( result == paInsufficientMemory )
+            goto error;
+        if ( newPin != NULL )
+        {
+            filter->pins[pinId] = newPin;
+            valid = 1;
+
+            /* Get the max output channel count */
+            if (( newPin->dataFlow == KSPIN_DATAFLOW_IN ) &&
+                (( newPin->communication == KSPIN_COMMUNICATION_SINK) ||
+                 ( newPin->communication == KSPIN_COMMUNICATION_BOTH)))
+            {
+                if (newPin->maxChannels > filter->maxOutputChannels)
+                    filter->maxOutputChannels = newPin->maxChannels;
+                filter->formats |= newPin->formats;
+            }
+            /* Get the max input channel count */
+            if (( newPin->dataFlow == KSPIN_DATAFLOW_OUT ) &&
+                (( newPin->communication == KSPIN_COMMUNICATION_SINK) ||
+                 ( newPin->communication == KSPIN_COMMUNICATION_BOTH)))
+            {
+                if (newPin->maxChannels > filter->maxInputChannels)
+                    filter->maxInputChannels = newPin->maxChannels;
+                filter->formats |= newPin->formats;
+            }
+
+            if (newPin->bestSampleRate > filter->bestSampleRate)
+            {
+                filter->bestSampleRate = newPin->bestSampleRate;
+            }
+        }
+    }
+
+    if (( filter->maxInputChannels == 0) && ( filter->maxOutputChannels == 0))
+    {
+        /* No input or output... not valid */
+        valid = 0;
+    }
+
+    if ( !valid )
+    {
+        /* No valid pin was found on this filter so we destroy it */
+        result = paDeviceUnavailable;
+        goto error;
+    }
+
+    /* Close the filter handle for now
+     * It will be opened later when needed */
+    FilterRelease(filter);
+
+    *error = paNoError;
+    return filter;
+
+error:
+    /*
+    Error cleanup
+    */
+    if ( filter )
+    {
+        for ( pinId = 0; pinId < filter->pinCount; pinId++ )
+            PinFree(filter->pins[pinId]);
+        PaUtil_FreeMemory( filter->pins );
+        if ( filter->handle )
+            CloseHandle( filter->handle );
+        PaUtil_FreeMemory( filter );
+    }
+    *error = result;
+    return NULL;
+}
+
+/**
+ * Free a previously created filter
+ */
+static void FilterFree(PaWinWdmFilter* filter)
+{
+    int pinId;
+    PA_LOGL_;
+    if ( filter )
+    {
+        for ( pinId = 0; pinId < filter->pinCount; pinId++ )
+            PinFree(filter->pins[pinId]);
+        PaUtil_FreeMemory( filter->pins );
+        if ( filter->handle )
+            CloseHandle( filter->handle );
+        PaUtil_FreeMemory( filter );
+    }
+    PA_LOGE_;
+}
+
+/**
+ * Reopen the filter handle if necessary so it can be used
+ **/
+static PaError FilterUse(PaWinWdmFilter* filter)
+{
+    assert( filter );
+
+    PA_LOGE_;
+    if ( filter->handle == NULL )
+    {
+        /* Open the filter */
+        filter->handle = CreateFile(
+            filter->filterName,
+            GENERIC_READ | GENERIC_WRITE,
+            0,
+            NULL,
+            OPEN_EXISTING,
+            FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
+            NULL);
+
+        if ( filter->handle == NULL )
+        {
+            return paDeviceUnavailable;
+        }
+    }
+    filter->usageCount++;
+    PA_LOGL_;
+    return paNoError;
+}
+
+/**
+ * Release the filter handle if nobody is using it
+ **/
+static void FilterRelease(PaWinWdmFilter* filter)
+{
+    assert( filter );
+    assert( filter->usageCount > 0 );
+
+    PA_LOGE_;
+    filter->usageCount--;
+    if ( filter->usageCount == 0 )
+    {
+        if ( filter->handle != NULL )
+        {
+            CloseHandle( filter->handle );
+            filter->handle = NULL;
+        }
+    }
+    PA_LOGL_;
+}
+
+/**
+ * Create a render (playback) Pin using the supplied format
+ **/
+static PaWinWdmPin* FilterCreateRenderPin(PaWinWdmFilter* filter,
+    const WAVEFORMATEX* wfex,
+    PaError* error)
+{
+    PaError result;
+    PaWinWdmPin* pin;
+
+    assert( filter );
+
+    pin = FilterFindViableRenderPin(filter,wfex,&result);
+    if (!pin)
+    {
+        goto error;
+    }
+    result = PinSetFormat(pin,wfex);
+    if ( result != paNoError )
+    {
+        goto error;
+    }
+    result = PinInstantiate(pin);
+    if ( result != paNoError )
+    {
+        goto error;
+    }
+
+    *error = paNoError;
+    return pin;
+
+error:
+    *error = result;
+    return NULL;
+}
+
+/**
+ * Find a pin that supports the given format
+ **/
+static PaWinWdmPin* FilterFindViableRenderPin(PaWinWdmFilter* filter,
+    const WAVEFORMATEX* wfex,
+    PaError* error)
+{
+    int pinId;
+    PaWinWdmPin*  pin;
+    PaError result = paDeviceUnavailable;
+    *error = paNoError;
+
+    assert( filter );
+
+    for ( pinId = 0; pinId<filter->pinCount; pinId++ )
+    {
+        pin = filter->pins[pinId];
+        if ( pin != NULL )
+        {
+            if (( pin->dataFlow == KSPIN_DATAFLOW_IN ) &&
+                (( pin->communication == KSPIN_COMMUNICATION_SINK) ||
+                 ( pin->communication == KSPIN_COMMUNICATION_BOTH)))
+            {
+                result = PinIsFormatSupported( pin, wfex );
+                if ( result == paNoError )
+                {
+                    return pin;
+                }
+            }
+        }
+    }
+
+    *error = result;
+    return NULL;
+}
+
+/**
+ * Check if there is a pin that should playback
+ * with the supplied format
+ **/
+static PaError FilterCanCreateRenderPin(PaWinWdmFilter* filter,
+    const WAVEFORMATEX* wfex)
+{
+    PaWinWdmPin* pin;
+    PaError result;
+
+    assert ( filter );
+
+    pin = FilterFindViableRenderPin(filter,wfex,&result);
+    /* result will be paNoError if pin found
+     * or else an error code indicating what is wrong with the format
+     **/
+    return result;
+}
+
+/**
+ * Create a capture (record) Pin using the supplied format
+ **/
+static PaWinWdmPin* FilterCreateCapturePin(PaWinWdmFilter* filter,
+    const WAVEFORMATEX* wfex,
+    PaError* error)
+{
+    PaError result;
+    PaWinWdmPin* pin;
+
+    assert( filter );
+
+    pin = FilterFindViableCapturePin(filter,wfex,&result);
+    if (!pin)
+    {
+        goto error;
+    }
+
+    result = PinSetFormat(pin,wfex);
+    if ( result != paNoError )
+    {
+        goto error;
+    }
+
+    result = PinInstantiate(pin);
+    if ( result != paNoError )
+    {
+        goto error;
+    }
+
+    *error = paNoError;
+    return pin;
+
+error:
+    *error = result;
+    return NULL;
+}
+
+/**
+ * Find a capture pin that supports the given format
+ **/
+static PaWinWdmPin* FilterFindViableCapturePin(PaWinWdmFilter* filter,
+    const WAVEFORMATEX* wfex,
+    PaError* error)
+{
+    int pinId;
+    PaWinWdmPin*  pin;
+    PaError result = paDeviceUnavailable;
+    *error = paNoError;
+
+    assert( filter );
+
+    for ( pinId = 0; pinId<filter->pinCount; pinId++ )
+    {
+        pin = filter->pins[pinId];
+        if ( pin != NULL )
+        {
+            if (( pin->dataFlow == KSPIN_DATAFLOW_OUT ) &&
+                (( pin->communication == KSPIN_COMMUNICATION_SINK) ||
+                 ( pin->communication == KSPIN_COMMUNICATION_BOTH)))
+            {
+                result = PinIsFormatSupported( pin, wfex );
+                if ( result == paNoError )
+                {
+                    return pin;
+                }
+            }
+        }
+    }
+
+    *error = result;
+    return NULL;
+}
+
+/**
+ * Check if there is a pin that should playback
+ * with the supplied format
+ **/
+static PaError FilterCanCreateCapturePin(PaWinWdmFilter* filter,
+    const WAVEFORMATEX* wfex)
+{
+    PaWinWdmPin* pin;
+    PaError result;
+
+    assert ( filter );
+
+    pin = FilterFindViableCapturePin(filter,wfex,&result);
+    /* result will be paNoError if pin found
+     * or else an error code indicating what is wrong with the format
+     **/
+    return result;
+}
+
+/**
+ * Build the list of available filters
+ */
+static PaError BuildFilterList(PaWinWdmHostApiRepresentation* wdmHostApi)
+{
+    PaError result = paNoError;
+    HDEVINFO handle = NULL;
+    int device;
+    int invalidDevices;
+    int slot;
+    SP_DEVICE_INTERFACE_DATA interfaceData;
+    SP_DEVICE_INTERFACE_DATA aliasData;
+    SP_DEVINFO_DATA devInfoData;
+    int noError;
+    const int sizeInterface = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA) + (MAX_PATH * sizeof(WCHAR));
+    unsigned char interfaceDetailsArray[sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA) + (MAX_PATH * sizeof(WCHAR))];
+    SP_DEVICE_INTERFACE_DETAIL_DATA* devInterfaceDetails = (SP_DEVICE_INTERFACE_DETAIL_DATA*)interfaceDetailsArray;
+    TCHAR friendlyName[MAX_PATH];
+    HKEY hkey;
+    DWORD sizeFriendlyName;
+    DWORD type;
+    PaWinWdmFilter* newFilter;
+    GUID* category = (GUID*)&KSCATEGORY_AUDIO;
+    GUID* alias_render = (GUID*)&KSCATEGORY_RENDER;
+    GUID* alias_capture = (GUID*)&KSCATEGORY_CAPTURE;
+    DWORD hasAlias;
+
+    PA_LOGE_;
+
+    devInterfaceDetails->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
+
+    /* Open a handle to search for devices (filters) */
+    handle = SetupDiGetClassDevs(category,NULL,NULL,DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
+    if ( handle == NULL )
+    {
+        return paUnanticipatedHostError;
+    }
+    PA_DEBUG(("Setup called\n"));
+
+    /* First let's count the number of devices so we can allocate a list */
+    invalidDevices = 0;
+    for ( device = 0;;device++ )
+    {
+        interfaceData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
+        interfaceData.Reserved = 0;
+        aliasData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
+        aliasData.Reserved = 0;
+        noError = SetupDiEnumDeviceInterfaces(handle,NULL,category,device,&interfaceData);
+        PA_DEBUG(("Enum called\n"));
+        if ( !noError )
+            break; /* No more devices */
+
+        /* Check this one has the render or capture alias */
+        hasAlias = 0;
+        noError = SetupDiGetDeviceInterfaceAlias(handle,&interfaceData,alias_render,&aliasData);
+        PA_DEBUG(("noError = %d\n",noError));
+        if (noError)
+        {
+            if (aliasData.Flags && (!(aliasData.Flags & SPINT_REMOVED)))
+            {
+                PA_DEBUG(("Device %d has render alias\n",device));
+                hasAlias |= 1; /* Has render alias */
+            }
+            else
+            {
+                PA_DEBUG(("Device %d has no render alias\n",device));
+            }
+        }
+        noError = SetupDiGetDeviceInterfaceAlias(handle,&interfaceData,alias_capture,&aliasData);
+        if (noError)
+        {
+            if (aliasData.Flags && (!(aliasData.Flags & SPINT_REMOVED)))
+            {
+                PA_DEBUG(("Device %d has capture alias\n",device));
+                hasAlias |= 2; /* Has capture alias */
+            }
+            else
+            {
+                PA_DEBUG(("Device %d has no capture alias\n",device));
+            }
+        }
+        if (!hasAlias)
+            invalidDevices++; /* This was not a valid capture or render audio device */
+
+    }
+    /* Remember how many there are */
+    wdmHostApi->filterCount = device-invalidDevices;
+
+    PA_DEBUG(("Interfaces found: %d\n",device-invalidDevices));
+
+    /* Now allocate the list of pointers to devices */
+    wdmHostApi->filters  = (PaWinWdmFilter**)PaUtil_AllocateMemory( sizeof(PaWinWdmFilter*) * device );
+    if( !wdmHostApi->filters )
+    {
+        if (handle != NULL)
+            SetupDiDestroyDeviceInfoList(handle);
+        return paInsufficientMemory;
+    }
+
+    /* Now create filter objects for each interface found */
+    slot = 0;
+    for ( device = 0;;device++ )
+    {
+        interfaceData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
+        interfaceData.Reserved = 0;
+        aliasData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
+        aliasData.Reserved = 0;
+        devInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
+        devInfoData.Reserved = 0;
+
+        noError = SetupDiEnumDeviceInterfaces(handle,NULL,category,device,&interfaceData);
+        if ( !noError )
+            break; /* No more devices */
+
+        /* Check this one has the render or capture alias */
+        hasAlias = 0;
+        noError = SetupDiGetDeviceInterfaceAlias(handle,&interfaceData,alias_render,&aliasData);
+        if (noError)
+        {
+            if (aliasData.Flags && (!(aliasData.Flags & SPINT_REMOVED)))
+            {
+                PA_DEBUG(("Device %d has render alias\n",device));
+                hasAlias |= 1; /* Has render alias */
+            }
+        }
+        noError = SetupDiGetDeviceInterfaceAlias(handle,&interfaceData,alias_capture,&aliasData);
+        if (noError)
+        {
+            if (aliasData.Flags && (!(aliasData.Flags & SPINT_REMOVED)))
+            {
+                PA_DEBUG(("Device %d has capture alias\n",device));
+                hasAlias |= 2; /* Has capture alias */
+            }
+        }
+        if (!hasAlias)
+            continue; /* This was not a valid capture or render audio device */
+
+        noError = SetupDiGetDeviceInterfaceDetail(handle,&interfaceData,devInterfaceDetails,sizeInterface,NULL,&devInfoData);
+        if ( noError )
+        {
+            /* Try to get the "friendly name" for this interface */
+            sizeFriendlyName = sizeof(friendlyName);
+            /* Fix contributed by Ben Allison
+             * Removed KEY_SET_VALUE from flags on following call
+             * as its causes failure when running without admin rights
+             * and it was not required */
+            hkey=SetupDiOpenDeviceInterfaceRegKey(handle,&interfaceData,0,KEY_QUERY_VALUE);
+            if (hkey!=INVALID_HANDLE_VALUE)
+            {
+                noError = RegQueryValueEx(hkey,TEXT("FriendlyName"),0,&type,(BYTE*)friendlyName,&sizeFriendlyName);
+                if ( noError == ERROR_SUCCESS )
+                {
+                    PA_DEBUG(("Interface %d, Name: %s\n",device,friendlyName));
+                    RegCloseKey(hkey);
+                }
+                else
+                {
+                    friendlyName[0] = 0;
+                }
+            }
+            newFilter = FilterNew(devInterfaceDetails->DevicePath,friendlyName,&result);
+            if ( result == paNoError )
+            {
+                PA_DEBUG(("Filter created\n"));
+                wdmHostApi->filters[slot] = newFilter;
+                slot++;
+            }
+            else
+            {
+                PA_DEBUG(("Filter NOT created\n"));
+                /* As there are now less filters than we initially thought
+                 * we must reduce the count by one */
+                wdmHostApi->filterCount--;
+            }
+        }
+    }
+
+    /* Clean up */
+    if (handle != NULL)
+        SetupDiDestroyDeviceInfoList(handle);
+
+    return paNoError;
+}
+
+PaError PaWinWdm_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex )
+{
+    PaError result = paNoError;
+    int i, deviceCount;
+    PaWinWdmHostApiRepresentation *wdmHostApi;
+    PaWinWdmDeviceInfo *deviceInfoArray;
+    PaWinWdmFilter* pFilter;
+    PaWinWdmDeviceInfo *wdmDeviceInfo;
+    PaDeviceInfo *deviceInfo;
+
+	  PA_LOGE_;
+
+	  /*
+	  Attempt to load the KSUSER.DLL without which we cannot create pins
+	  We will unload this on termination
+	  */
+	  if (DllKsUser == NULL)
+	  {
+		DllKsUser = LoadLibrary(TEXT("ksuser.dll"));
+		if (DllKsUser == NULL)
+		  	goto error;
+	  }
+
+	  FunctionKsCreatePin = (KSCREATEPIN*)GetProcAddress(DllKsUser, "KsCreatePin");
+    if (FunctionKsCreatePin == NULL)
+    	  goto error;
+
+    wdmHostApi = (PaWinWdmHostApiRepresentation*)PaUtil_AllocateMemory( sizeof(PaWinWdmHostApiRepresentation) );
+    if( !wdmHostApi )
+    {
+        result = paInsufficientMemory;
+        goto error;
+    }
+
+    wdmHostApi->allocations = PaUtil_CreateAllocationGroup();
+    if( !wdmHostApi->allocations )
+    {
+        result = paInsufficientMemory;
+        goto error;
+    }
+
+    result = BuildFilterList( wdmHostApi );
+    if ( result != paNoError )
+    {
+        goto error;
+    }
+    deviceCount = wdmHostApi->filterCount;
+
+    *hostApi = &wdmHostApi->inheritedHostApiRep;
+    (*hostApi)->info.structVersion = 1;
+    (*hostApi)->info.type = paWDMKS;
+    (*hostApi)->info.name = "Windows WDM-KS";
+
+    (*hostApi)->info.defaultOutputDevice = paNoDevice;
+
+    if( deviceCount > 0 )
+    {
+        (*hostApi)->deviceInfos = (PaDeviceInfo**)PaUtil_GroupAllocateMemory(
+               wdmHostApi->allocations, sizeof(PaWinWdmDeviceInfo*) * deviceCount );
+        if( !(*hostApi)->deviceInfos )
+        {
+            result = paInsufficientMemory;
+            goto error;
+        }
+
+        /* allocate all device info structs in a contiguous block */
+        deviceInfoArray = (PaWinWdmDeviceInfo*)PaUtil_GroupAllocateMemory(
+                wdmHostApi->allocations, sizeof(PaWinWdmDeviceInfo) * deviceCount );
+        if( !deviceInfoArray )
+        {
+            result = paInsufficientMemory;
+            goto error;
+        }
+
+        for( i=0; i < deviceCount; ++i )
+        {
+            wdmDeviceInfo = &deviceInfoArray[i];
+            deviceInfo = &wdmDeviceInfo->inheritedDeviceInfo;
+	          pFilter = wdmHostApi->filters[i];
+            if ( pFilter == NULL )
+                continue;
+            wdmDeviceInfo->filter = pFilter;
+            deviceInfo->structVersion = 2;
+            deviceInfo->hostApi = hostApiIndex;
+            deviceInfo->name = (char*)pFilter->friendlyName;
+            PA_DEBUG(("Device found name: %s\n",(char*)pFilter->friendlyName));
+            deviceInfo->maxInputChannels = pFilter->maxInputChannels;
+            if (deviceInfo->maxInputChannels > 0)
+            {
+                /* Set the default input device to the first device we find with
+                 * more than zero input channels
+                 **/
+                if ((*hostApi)->info.defaultInputDevice == paNoDevice)
+                {
+                    (*hostApi)->info.defaultInputDevice = i;
+                }
+            }
+
+            deviceInfo->maxOutputChannels = pFilter->maxOutputChannels;
+            if (deviceInfo->maxOutputChannels > 0)
+            {
+                /* Set the default output device to the first device we find with
+                 * more than zero output channels
+                 **/
+                if ((*hostApi)->info.defaultOutputDevice == paNoDevice)
+                {
+                    (*hostApi)->info.defaultOutputDevice = i;
+                }
+            }
+
+            /* These low values are not very useful because
+             * a) The lowest latency we end up with can depend on many factors such
+             *    as the device buffer sizes/granularities, sample rate, channels and format
+             * b) We cannot know the device buffer sizes until we try to open/use it at
+             *    a particular setting
+             * So: we give 512x48000Hz frames as the default low input latency
+             **/
+            deviceInfo->defaultLowInputLatency = (512.0/48000.0);
+            deviceInfo->defaultLowOutputLatency = (512.0/48000.0);
+            deviceInfo->defaultHighInputLatency = (4096.0/48000.0);
+            deviceInfo->defaultHighOutputLatency = (4096.0/48000.0);
+            deviceInfo->defaultSampleRate = (double)(pFilter->bestSampleRate);
+
+            (*hostApi)->deviceInfos[i] = deviceInfo;
+            ++(*hostApi)->info.deviceCount;
+        }
+    }
+
+    (*hostApi)->info.deviceCount = deviceCount;
+
+    (*hostApi)->Terminate = Terminate;
+    (*hostApi)->OpenStream = OpenStream;
+    (*hostApi)->IsFormatSupported = IsFormatSupported;
+
+    PaUtil_InitializeStreamInterface( &wdmHostApi->callbackStreamInterface, CloseStream, StartStream,
+                                      StopStream, AbortStream, IsStreamStopped, IsStreamActive,
+                                      GetStreamTime, GetStreamCpuLoad,
+                                      PaUtil_DummyRead, PaUtil_DummyWrite,
+                                      PaUtil_DummyGetReadAvailable, PaUtil_DummyGetWriteAvailable );
+
+    PaUtil_InitializeStreamInterface( &wdmHostApi->blockingStreamInterface, CloseStream, StartStream,
+                                      StopStream, AbortStream, IsStreamStopped, IsStreamActive,
+                                      GetStreamTime, PaUtil_DummyGetCpuLoad,
+                                      ReadStream, WriteStream, GetStreamReadAvailable, GetStreamWriteAvailable );
+
+	  PA_LOGL_;
+    return result;
+
+error:
+	  if ( DllKsUser != NULL )
+	  {
+		    FreeLibrary( DllKsUser );
+		    DllKsUser = NULL;
+	  }
+
+    if( wdmHostApi )
+    {
+        PaUtil_FreeMemory( wdmHostApi->filters );
+        if( wdmHostApi->allocations )
+        {
+            PaUtil_FreeAllAllocations( wdmHostApi->allocations );
+            PaUtil_DestroyAllocationGroup( wdmHostApi->allocations );
+        }
+        PaUtil_FreeMemory( wdmHostApi );
+    }
+    PA_LOGL_;
+    return result;
+}
+
+
+static void Terminate( struct PaUtilHostApiRepresentation *hostApi )
+{
+    PaWinWdmHostApiRepresentation *wdmHostApi = (PaWinWdmHostApiRepresentation*)hostApi;
+    int i;
+	  PA_LOGE_;
+
+	  if( wdmHostApi->filters )
+	  {
+        for ( i=0; i<wdmHostApi->filterCount; i++)
+        {
+            if ( wdmHostApi->filters[i] != NULL )
+            {
+                FilterFree( wdmHostApi->filters[i] );
+                wdmHostApi->filters[i] = NULL;
+            }
+        }
+	  }
+    PaUtil_FreeMemory( wdmHostApi->filters );
+    if( wdmHostApi->allocations )
+    {
+        PaUtil_FreeAllAllocations( wdmHostApi->allocations );
+        PaUtil_DestroyAllocationGroup( wdmHostApi->allocations );
+    }
+    PaUtil_FreeMemory( wdmHostApi );
+    PA_LOGL_;
+}
+
+static void FillWFEXT( WAVEFORMATEXTENSIBLE* pwfext, PaSampleFormat sampleFormat, double sampleRate, int channelCount)
+	  {
+	  PA_LOGE_;
+	  PA_DEBUG(( "sampleFormat = %lx\n" , sampleFormat ));
+	  PA_DEBUG(( "sampleRate = %f\n" , sampleRate ));
+	  PA_DEBUG(( "chanelCount = %d\n", channelCount ));
+
+    pwfext->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
+    pwfext->Format.nChannels = channelCount;
+    pwfext->Format.nSamplesPerSec = (int)sampleRate;
+    if (channelCount == 1)
+      	pwfext->dwChannelMask = KSAUDIO_SPEAKER_DIRECTOUT;
+    else
+    	  pwfext->dwChannelMask = KSAUDIO_SPEAKER_STEREO;
+    if (sampleFormat == paFloat32)
+    {
+        pwfext->Format.nBlockAlign = channelCount * 4;
+        pwfext->Format.wBitsPerSample = 32;
+        pwfext->Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX);
+        pwfext->Samples.wValidBitsPerSample = 32;
+        pwfext->SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
+    }
+	  else if (sampleFormat == paInt32)
+    {
+        pwfext->Format.nBlockAlign = channelCount * 4;
+        pwfext->Format.wBitsPerSample = 32;
+        pwfext->Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX);
+        pwfext->Samples.wValidBitsPerSample = 32;
+        pwfext->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
+    }
+	  else if (sampleFormat == paInt24)
+    {
+        pwfext->Format.nBlockAlign = channelCount * 3;
+        pwfext->Format.wBitsPerSample = 24;
+        pwfext->Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX);
+        pwfext->Samples.wValidBitsPerSample = 24;
+        pwfext->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
+    }
+	  else if (sampleFormat == paInt16)
+    {
+        pwfext->Format.nBlockAlign = channelCount * 2;
+        pwfext->Format.wBitsPerSample = 16;
+        pwfext->Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX);
+        pwfext->Samples.wValidBitsPerSample = 16;
+        pwfext->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
+    }
+    pwfext->Format.nAvgBytesPerSec = pwfext->Format.nSamplesPerSec * pwfext->Format.nBlockAlign;
+
+    PA_LOGL_;
+}
+
+static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
+                                  const PaStreamParameters *inputParameters,
+                                  const PaStreamParameters *outputParameters,
+                                  double sampleRate )
+{
+    int inputChannelCount, outputChannelCount;
+    PaSampleFormat inputSampleFormat, outputSampleFormat;
+    PaWinWdmHostApiRepresentation *wdmHostApi = (PaWinWdmHostApiRepresentation*)hostApi;
+    PaWinWdmFilter* pFilter;
+    int result = paFormatIsSupported;
+    WAVEFORMATEXTENSIBLE wfx;
+
+   	PA_LOGE_;
+
+    if( inputParameters )
+    {
+        inputChannelCount = inputParameters->channelCount;
+        inputSampleFormat = inputParameters->sampleFormat;
+
+        /* all standard sample formats are supported by the buffer adapter,
+            this implementation doesn't support any custom sample formats */
+        if( inputSampleFormat & paCustomFormat )
+            return paSampleFormatNotSupported;
+
+        /* unless alternate device specification is supported, reject the use of
+            paUseHostApiSpecificDeviceSpecification */
+
+        if( inputParameters->device == paUseHostApiSpecificDeviceSpecification )
+            return paInvalidDevice;
+
+        /* check that input device can support inputChannelCount */
+        if( inputChannelCount > hostApi->deviceInfos[ inputParameters->device ]->maxInputChannels )
+            return paInvalidChannelCount;
+
+        /* validate inputStreamInfo */
+        if( inputParameters->hostApiSpecificStreamInfo )
+            return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */
+
+        /* Check that the input format is supported */
+        FillWFEXT(&wfx,paInt16,sampleRate,inputChannelCount);
+
+        pFilter = wdmHostApi->filters[inputParameters->device];
+	      result = FilterCanCreateCapturePin(pFilter,(const WAVEFORMATEX*)&wfx);
+        if ( result != paNoError )
+        {
+            /* Try a WAVE_FORMAT_PCM instead */
+            wfx.Format.wFormatTag = WAVE_FORMAT_PCM;
+            wfx.Format.cbSize = 0;
+            wfx.Samples.wValidBitsPerSample = 0;
+            wfx.dwChannelMask = 0;
+            wfx.SubFormat = GUID_NULL;
+            result = FilterCanCreateCapturePin(pFilter,(const WAVEFORMATEX*)&wfx);
+            if ( result != paNoError )
+                 return result;
+        }
+    }
+    else
+    {
+        inputChannelCount = 0;
+    }
+
+    if( outputParameters )
+    {
+        outputChannelCount = outputParameters->channelCount;
+        outputSampleFormat = outputParameters->sampleFormat;
+
+        /* all standard sample formats are supported by the buffer adapter,
+            this implementation doesn't support any custom sample formats */
+        if( outputSampleFormat & paCustomFormat )
+            return paSampleFormatNotSupported;
+
+        /* unless alternate device specification is supported, reject the use of
+            paUseHostApiSpecificDeviceSpecification */
+
+        if( outputParameters->device == paUseHostApiSpecificDeviceSpecification )
+            return paInvalidDevice;
+
+        /* check that output device can support outputChannelCount */
+        if( outputChannelCount > hostApi->deviceInfos[ outputParameters->device ]->maxOutputChannels )
+            return paInvalidChannelCount;
+
+        /* validate outputStreamInfo */
+        if( outputParameters->hostApiSpecificStreamInfo )
+            return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */
+
+        /* Check that the output format is supported */
+        FillWFEXT(&wfx,paInt16,sampleRate,outputChannelCount);
+
+        pFilter = wdmHostApi->filters[outputParameters->device];
+	      result = FilterCanCreateRenderPin(pFilter,(const WAVEFORMATEX*)&wfx);
+        if ( result != paNoError )
+        {
+            /* Try a WAVE_FORMAT_PCM instead */
+            wfx.Format.wFormatTag = WAVE_FORMAT_PCM;
+            wfx.Format.cbSize = 0;
+            wfx.Samples.wValidBitsPerSample = 0;
+            wfx.dwChannelMask = 0;
+            wfx.SubFormat = GUID_NULL;
+            result = FilterCanCreateRenderPin(pFilter,(const WAVEFORMATEX*)&wfx);
+            if ( result != paNoError )
+                 return result;
+        }
+
+    }
+    else
+    {
+        outputChannelCount = 0;
+    }
+
+    /*
+        IMPLEMENT ME:
+
+            - if a full duplex stream is requested, check that the combination
+                of input and output parameters is supported if necessary
+
+            - check that the device supports sampleRate
+
+        Because the buffer adapter handles conversion between all standard
+        sample formats, the following checks are only required if paCustomFormat
+        is implemented, or under some other unusual conditions.
+
+            - check that input device can support inputSampleFormat, or that
+                we have the capability to convert from inputSampleFormat to
+                a native format
+
+            - check that output device can support outputSampleFormat, or that
+                we have the capability to convert from outputSampleFormat to
+                a native format
+    */
+	  if ((inputChannelCount == 0)&&(outputChannelCount == 0))
+		    result = paSampleFormatNotSupported; /* Not right error */
+
+	  PA_LOGL_;
+	  return result;
+}
+
+/* see pa_hostapi.h for a list of validity guarantees made about OpenStream parameters */
+
+static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
+                           PaStream** s,
+                           const PaStreamParameters *inputParameters,
+                           const PaStreamParameters *outputParameters,
+                           double sampleRate,
+                           unsigned long framesPerBuffer,
+                           PaStreamFlags streamFlags,
+                           PaStreamCallback *streamCallback,
+                           void *userData )
+{
+    PaError result = paNoError;
+    PaWinWdmHostApiRepresentation *wdmHostApi = (PaWinWdmHostApiRepresentation*)hostApi;
+    PaWinWdmStream *stream = 0;
+    /* unsigned long framesPerHostBuffer; these may not be equivalent for all implementations */
+    PaSampleFormat inputSampleFormat, outputSampleFormat;
+    PaSampleFormat hostInputSampleFormat, hostOutputSampleFormat;
+    int userInputChannels,userOutputChannels;
+	  int size;
+    PaWinWdmFilter* pFilter;
+    WAVEFORMATEXTENSIBLE wfx;
+
+	  PA_LOGE_;
+	  PA_DEBUG(("sampleRate = %f;",sampleRate));
+	  PA_DEBUG(("framesPerBuffer = %lu;",framesPerBuffer));
+
+    if( inputParameters )
+    {
+        userInputChannels = inputParameters->channelCount;
+        inputSampleFormat = inputParameters->sampleFormat;
+
+        /* unless alternate device specification is supported, reject the use of
+            paUseHostApiSpecificDeviceSpecification */
+
+        if( inputParameters->device == paUseHostApiSpecificDeviceSpecification )
+            return paInvalidDevice;
+
+        /* check that input device can support stream->userInputChannels */
+        if( userInputChannels > hostApi->deviceInfos[ inputParameters->device ]->maxInputChannels )
+            return paInvalidChannelCount;
+
+        /* validate inputStreamInfo */
+        if( inputParameters->hostApiSpecificStreamInfo )
+            return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */
+
+    }
+    else
+    {
+        userInputChannels = 0;
+        inputSampleFormat = hostInputSampleFormat = paInt16; /* Surpress 'uninitialised var' warnings. */
+    }
+
+    if( outputParameters )
+    {
+        userOutputChannels = outputParameters->channelCount;
+        outputSampleFormat = outputParameters->sampleFormat;
+
+        /* unless alternate device specification is supported, reject the use of
+            paUseHostApiSpecificDeviceSpecification */
+
+        if( outputParameters->device == paUseHostApiSpecificDeviceSpecification )
+            return paInvalidDevice;
+
+        /* check that output device can support stream->userInputChannels */
+        if( userOutputChannels > hostApi->deviceInfos[ outputParameters->device ]->maxOutputChannels )
+            return paInvalidChannelCount;
+
+        /* validate outputStreamInfo */
+        if( outputParameters->hostApiSpecificStreamInfo )
+            return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */
+
+    }
+    else
+    {
+        userOutputChannels = 0;
+        outputSampleFormat = hostOutputSampleFormat = paInt16; /* Surpress 'uninitialized var' warnings. */
+    }
+
+    /* validate platform specific flags */
+    if( (streamFlags & paPlatformSpecificFlags) != 0 )
+        return paInvalidFlag; /* unexpected platform specific flag */
+
+    stream = (PaWinWdmStream*)PaUtil_AllocateMemory( sizeof(PaWinWdmStream) );
+    if( !stream )
+    {
+        result = paInsufficientMemory;
+        goto error;
+    }
+    /* Zero the stream object */
+    /* memset((void*)stream,0,sizeof(PaWinWdmStream)); */
+
+    if( streamCallback )
+    {
+        PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation,
+                                               &wdmHostApi->callbackStreamInterface, streamCallback, userData );
+    }
+    else
+    {
+        PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation,
+                                               &wdmHostApi->blockingStreamInterface, streamCallback, userData );
+    }
+
+    PaUtil_InitializeCpuLoadMeasurer( &stream->cpuLoadMeasurer, sampleRate );
+
+    /* Instantiate the input pin if necessary */
+    if (userInputChannels > 0)
+    {
+        stream->userInputChannels = userInputChannels;
+        pFilter = wdmHostApi->filters[inputParameters->device];
+        hostInputSampleFormat =
+            PaUtil_SelectClosestAvailableFormat( pFilter->formats, inputSampleFormat );
+        if ( hostInputSampleFormat == paInt16 )
+            stream->inputSampleSize = 2;
+        else
+            stream->inputSampleSize = 3;
+        FillWFEXT(&wfx,hostInputSampleFormat,sampleRate,stream->userInputChannels);
+        stream->bytesPerInputFrame = wfx.Format.nBlockAlign;
+        stream->recordingPin = FilterCreateCapturePin(pFilter,(const WAVEFORMATEX*)&wfx,&result);
+        stream->deviceInputChannels = stream->userInputChannels;
+        if (result != paNoError)
+        {
+            /* Try a WAVE_FORMAT_PCM instead */
+            wfx.Format.wFormatTag = WAVE_FORMAT_PCM;
+            wfx.Format.cbSize = 0;
+            wfx.Samples.wValidBitsPerSample = 0;
+            wfx.dwChannelMask = 0;
+            wfx.SubFormat = GUID_NULL;
+            stream->recordingPin = FilterCreateCapturePin(pFilter,(const WAVEFORMATEX*)&wfx,&result);
+            if ( result != paNoError )
+            {
+                /* Some or all KS devices can only handle the exact number of channels
+                 * they specify. But PortAudio clients expect to be able to
+                 * at least specify mono I/O on a multi-channel device
+                 * If this is the case, then we will do the channel mapping internally
+                 **/
+                if ( stream->userInputChannels < pFilter->maxInputChannels )
+                {
+                    FillWFEXT(&wfx,hostInputSampleFormat,sampleRate,pFilter->maxInputChannels);
+                    stream->bytesPerInputFrame = wfx.Format.nBlockAlign;
+                    stream->recordingPin = FilterCreateCapturePin(pFilter,(const WAVEFORMATEX*)&wfx,&result);
+                    stream->deviceInputChannels = pFilter->maxInputChannels;
+                    if ( result != paNoError )
+                    {
+                        /* Try a WAVE_FORMAT_PCM instead */
+                        wfx.Format.wFormatTag = WAVE_FORMAT_PCM;
+                        wfx.Format.cbSize = 0;
+                        wfx.Samples.wValidBitsPerSample = 0;
+                        wfx.dwChannelMask = 0;
+                        wfx.SubFormat = GUID_NULL;
+                        stream->recordingPin = FilterCreateCapturePin(pFilter,(const WAVEFORMATEX*)&wfx,&result);
+                    }
+                }
+            }
+        }
+        if (stream->recordingPin == NULL)
+        {
+            goto error;
+        }
+        stream->recordingPin->frameSize /= stream->bytesPerInputFrame;
+        PA_DEBUG(("Pin output frames: %d\n",stream->recordingPin->frameSize));
+    }
+    else
+    {
+        stream->recordingPin = NULL;
+        stream->bytesPerInputFrame = 0;
+    }
+
+    /* Instantiate the output pin if necessary */
+    if (userOutputChannels > 0)
+    {
+        stream->userOutputChannels = userOutputChannels;
+        pFilter = wdmHostApi->filters[outputParameters->device];
+        hostOutputSampleFormat =
+            PaUtil_SelectClosestAvailableFormat( pFilter->formats/*paInt16*/, outputSampleFormat );
+        if ( hostOutputSampleFormat == paInt16 )
+            stream->outputSampleSize = 2;
+        else
+            stream->outputSampleSize = 3;
+        FillWFEXT(&wfx,hostOutputSampleFormat,sampleRate,stream->userOutputChannels);
+        stream->bytesPerOutputFrame = wfx.Format.nBlockAlign;
+        stream->playbackPin = FilterCreateRenderPin(pFilter,(WAVEFORMATEX*)&wfx,&result);
+        stream->deviceOutputChannels = stream->userOutputChannels;
+        if (result != paNoError)
+        {
+            /* Try a WAVE_FORMAT_PCM instead */
+            wfx.Format.wFormatTag = WAVE_FORMAT_PCM;
+            wfx.Format.cbSize = 0;
+            wfx.Samples.wValidBitsPerSample = 0;
+            wfx.dwChannelMask = 0;
+            wfx.SubFormat = GUID_NULL;
+            stream->playbackPin = FilterCreateRenderPin(pFilter,(WAVEFORMATEX*)&wfx,&result);
+            if ( result != paNoError )
+            {
+                /* Some or all KS devices can only handle the exact number of channels
+                 * they specify. But PortAudio clients expect to be able to
+                 * at least specify mono I/O on a multi-channel device
+                 * If this is the case, then we will do the channel mapping internally
+                 **/
+                if ( stream->userOutputChannels < pFilter->maxOutputChannels )
+                {
+                    FillWFEXT(&wfx,hostOutputSampleFormat,sampleRate,pFilter->maxOutputChannels);
+                    stream->bytesPerOutputFrame = wfx.Format.nBlockAlign;
+                    stream->playbackPin = FilterCreateRenderPin(pFilter,(const WAVEFORMATEX*)&wfx,&result);
+                    stream->deviceOutputChannels = pFilter->maxOutputChannels;
+                    if ( result != paNoError )
+                    {
+                        /* Try a WAVE_FORMAT_PCM instead */
+                        wfx.Format.wFormatTag = WAVE_FORMAT_PCM;
+                        wfx.Format.cbSize = 0;
+                        wfx.Samples.wValidBitsPerSample = 0;
+                        wfx.dwChannelMask = 0;
+                        wfx.SubFormat = GUID_NULL;
+                        stream->playbackPin = FilterCreateRenderPin(pFilter,(const WAVEFORMATEX*)&wfx,&result);
+                    }
+                }
+            }
+        }
+        if (stream->playbackPin == NULL)
+        {
+            goto error;
+        }
+        stream->playbackPin->frameSize /= stream->bytesPerOutputFrame;
+        PA_DEBUG(("Pin output frames: %d\n",stream->playbackPin->frameSize));
+    }
+    else
+    {
+        stream->playbackPin = NULL;
+        stream->bytesPerOutputFrame = 0;
+    }
+
+   	/* Calculate the framesPerHostXxxxBuffer size based upon the suggested latency values */
+
+	  /* Record the buffer length */
+	  if (inputParameters)
+	  {
+        /* Calculate the frames from the user's value - add a bit to round up */
+		    stream->framesPerHostIBuffer = (unsigned long)((inputParameters->suggestedLatency*sampleRate)+0.0001);
+        if (stream->framesPerHostIBuffer > (unsigned long)sampleRate)
+        { /* Upper limit is 1 second */
+      	    stream->framesPerHostIBuffer = (unsigned long)sampleRate;
+        }
+        /* Uncomment the following code to make the device-reported
+         * frame size the lower limit*/
+        /*
+        else if (stream->framesPerHostIBuffer < stream->recordingPin->frameSize)
+    		{
+      	    stream->framesPerHostIBuffer = stream->recordingPin->frameSize;
+        }
+        */
+        PA_DEBUG(("Input frames chosen:%ld\n",stream->framesPerHostIBuffer));
+    }
+
+	  if (outputParameters)
+	  {
+        /* Calculate the frames from the user's value - add a bit to round up */
+        stream->framesPerHostOBuffer = (unsigned long)((outputParameters->suggestedLatency*sampleRate)+0.0001);
+        if (stream->framesPerHostOBuffer > (unsigned long)sampleRate)
+        { /* Upper limit is 1 second */
+      			stream->framesPerHostOBuffer = (unsigned long)sampleRate;
+        }
+        /* Uncomment the following code to make the device-reported
+         * frame size the lower limit*/
+        /*
+        else if (stream->framesPerHostOBuffer < stream->playbackPin->frameSize)
+        {
+      	    stream->framesPerHostOBuffer = stream->playbackPin->frameSize;
+    		}
+        */
+        PA_DEBUG(("Output frames chosen:%ld\n",stream->framesPerHostOBuffer));
+  	}
+
+	  /* Host buffer size is bounded to the largest of the input and output
+	  frame sizes */
+
+    result =  PaUtil_InitializeBufferProcessor( &stream->bufferProcessor,
+              stream->userInputChannels, inputSampleFormat, hostInputSampleFormat,
+              stream->userOutputChannels, outputSampleFormat, hostOutputSampleFormat,
+              sampleRate, streamFlags, framesPerBuffer,
+              max(stream->framesPerHostOBuffer,stream->framesPerHostIBuffer),
+              paUtilBoundedHostBufferSize,
+              streamCallback, userData );
+    if( result != paNoError )
+        goto error;
+
+    stream->streamRepresentation.streamInfo.inputLatency =
+            ((double)stream->framesPerHostIBuffer) / sampleRate;
+    stream->streamRepresentation.streamInfo.outputLatency =
+            ((double)stream->framesPerHostOBuffer) / sampleRate;
+    stream->streamRepresentation.streamInfo.sampleRate = sampleRate;
+
+
+  	PA_DEBUG(("BytesPerInputFrame = %d\n",stream->bytesPerInputFrame));
+  	PA_DEBUG(("BytesPerOutputFrame = %d\n",stream->bytesPerOutputFrame));
+
+  	/* Allocate all the buffers for host I/O */
+  	size = 2 * (stream->framesPerHostIBuffer*stream->bytesPerInputFrame +  stream->framesPerHostOBuffer*stream->bytesPerOutputFrame);
+  	PA_DEBUG(("Buffer size = %d\n",size));
+  	stream->hostBuffer = (char*)PaUtil_AllocateMemory(size);
+    PA_DEBUG(("Buffer allocated\n"));
+    if( !stream->hostBuffer )
+    {
+        PA_DEBUG(("Cannot allocate host buffer!\n"));
+        result = paInsufficientMemory;
+        goto error;
+    }
+    PA_DEBUG(("Buffer start = %p\n",stream->hostBuffer));
+    /* memset(stream->hostBuffer,0,size); */
+
+  	/* Set up the packets */
+    stream->events[0] = CreateEvent(NULL, FALSE, FALSE, NULL);
+    ResetEvent(stream->events[0]); /* Record buffer 1 */
+    stream->events[1] = CreateEvent(NULL, FALSE, FALSE, NULL);
+    ResetEvent(stream->events[1]); /* Record buffer 2 */
+    stream->events[2] = CreateEvent(NULL, FALSE, FALSE, NULL);
+    ResetEvent(stream->events[2]); /* Play buffer 1 */
+    stream->events[3] = CreateEvent(NULL, FALSE, FALSE, NULL);
+    ResetEvent(stream->events[3]); /* Play buffer 2 */
+    stream->events[4] = CreateEvent(NULL, FALSE, FALSE, NULL);
+    ResetEvent(stream->events[4]); /* Abort event */
+  	if (stream->userInputChannels > 0)
+  	{
+    		DATAPACKET *p = &(stream->packets[0]);
+    		p->Signal.hEvent = stream->events[0];
+        p->Header.Data = stream->hostBuffer;
+        p->Header.FrameExtent = stream->framesPerHostIBuffer*stream->bytesPerInputFrame;
+        p->Header.DataUsed = 0;
+        p->Header.Size = sizeof(p->Header);
+        p->Header.PresentationTime.Numerator = 1;
+        p->Header.PresentationTime.Denominator = 1;
+    		p = &(stream->packets[1]);
+    		p->Signal.hEvent = stream->events[1];
+        p->Header.Data = stream->hostBuffer + stream->framesPerHostIBuffer*stream->bytesPerInputFrame;
+        p->Header.FrameExtent = stream->framesPerHostIBuffer*stream->bytesPerInputFrame;
+        p->Header.DataUsed = 0;
+        p->Header.Size = sizeof(p->Header);
+        p->Header.PresentationTime.Numerator = 1;
+        p->Header.PresentationTime.Denominator = 1;
+    }
+	  if (stream->userOutputChannels > 0)
+	  {
+    		DATAPACKET *p = &(stream->packets[2]);
+    		p->Signal.hEvent = stream->events[2];
+        p->Header.Data = stream->hostBuffer + 2*stream->framesPerHostIBuffer*stream->bytesPerInputFrame;
+        p->Header.FrameExtent = stream->framesPerHostOBuffer*stream->bytesPerOutputFrame;
+        p->Header.DataUsed = stream->framesPerHostOBuffer*stream->bytesPerOutputFrame;
+        p->Header.Size = sizeof(p->Header);
+        p->Header.PresentationTime.Numerator = 1;
+        p->Header.PresentationTime.Denominator = 1;
+    		p = &(stream->packets[3]);
+    		p->Signal.hEvent = stream->events[3];
+        p->Header.Data = stream->hostBuffer + 2*stream->framesPerHostIBuffer*stream->bytesPerInputFrame + stream->framesPerHostOBuffer*stream->bytesPerOutputFrame;
+        p->Header.FrameExtent = stream->framesPerHostOBuffer*stream->bytesPerOutputFrame;
+        p->Header.DataUsed = stream->framesPerHostOBuffer*stream->bytesPerOutputFrame;
+        p->Header.Size = sizeof(p->Header);
+        p->Header.PresentationTime.Numerator = 1;
+        p->Header.PresentationTime.Denominator = 1;
+  	}
+
+	  stream->streamStarted = 0;
+	  stream->streamActive = 0;
+	  stream->streamStop = 0;
+	  stream->streamAbort = 0;
+	  stream->streamFlags = streamFlags;
+	  stream->oldProcessPriority = REALTIME_PRIORITY_CLASS;
+
+    *s = (PaStream*)stream;
+
+	  PA_LOGL_;
+    return result;
+
+error:
+	  size = 5;
+	  while (size--)
+	  {
+		    if (stream->events[size] != NULL)
+		    {
+			      CloseHandle(stream->events[size]);
+			      stream->events[size] = NULL;
+		    }
+	  }
+	  if (stream->hostBuffer)
+        PaUtil_FreeMemory( stream->hostBuffer );
+
+	  if (stream->playbackPin)
+		    PinClose(stream->playbackPin);
+	  if (stream->recordingPin)
+		    PinClose(stream->recordingPin);
+
+    if( stream )
+        PaUtil_FreeMemory( stream );
+
+	  PA_LOGL_;
+    return result;
+}
+
+/*
+    When CloseStream() is called, the multi-api layer ensures that
+    the stream has already been stopped or aborted.
+*/
+static PaError CloseStream( PaStream* s )
+{
+    PaError result = paNoError;
+    PaWinWdmStream *stream = (PaWinWdmStream*)s;
+	  int size;
+
+	  PA_LOGE_;
+
+	  assert(!stream->streamStarted);
+	  assert(!stream->streamActive);
+
+    PaUtil_TerminateBufferProcessor( &stream->bufferProcessor );
+    PaUtil_TerminateStreamRepresentation( &stream->streamRepresentation );
+	  size = 5;
+	  while (size--)
+	  {
+		    if (stream->events[size] != NULL)
+		    {
+			      CloseHandle(stream->events[size]);
+			      stream->events[size] = NULL;
+		    }
+	  }
+	  if (stream->hostBuffer)
+        PaUtil_FreeMemory( stream->hostBuffer );
+
+	  if (stream->playbackPin)
+		    PinClose(stream->playbackPin);
+	  if (stream->recordingPin)
+		    PinClose(stream->recordingPin);
+
+    PaUtil_FreeMemory( stream );
+
+	  PA_LOGL_;
+    return result;
+}
+
+/*
+Write the supplied packet to the pin
+Asynchronous
+Should return false on success
+*/
+static BOOL PinWrite(HANDLE h, DATAPACKET* p)
+{
+    unsigned long cbReturned = 0;
+    return DeviceIoControl(h,IOCTL_KS_WRITE_STREAM,NULL,0,
+    						&p->Header,p->Header.Size,&cbReturned,&p->Signal);
+}
+
+/*
+Read to the supplied packet from the pin
+Asynchronous
+Should return false on success
+*/
+static BOOL PinRead(HANDLE h, DATAPACKET* p)
+{
+    unsigned long cbReturned = 0;
+    return DeviceIoControl(h,IOCTL_KS_READ_STREAM,NULL,0,
+    						&p->Header,p->Header.Size,&cbReturned,&p->Signal);
+}
+
+/*
+Copy the first interleaved channel of 16 bit data to the other channels
+*/
+static void DuplicateFirstChannelInt16(void* buffer, int channels, int samples)
+{
+    unsigned short* data = (unsigned short*)buffer;
+    int channel;
+    unsigned short sourceSample;
+    while ( samples-- )
+    {
+        sourceSample = *data++;
+        channel = channels-1;
+        while ( channel-- )
+        {
+            *data++ = sourceSample;
+        }
+    }
+}
+
+/*
+Copy the first interleaved channel of 24 bit data to the other channels
+*/
+static void DuplicateFirstChannelInt24(void* buffer, int channels, int samples)
+{
+    unsigned char* data = (unsigned char*)buffer;
+    int channel;
+    unsigned char sourceSample[3];
+    while ( samples-- )
+    {
+        sourceSample[0] = data[0];
+        sourceSample[1] = data[1];
+        sourceSample[2] = data[2];
+        data += 3;
+        channel = channels-1;
+        while ( channel-- )
+        {
+            data[0] = sourceSample[0];
+            data[1] = sourceSample[1];
+            data[2] = sourceSample[2];
+            data += 3;
+        }
+    }
+}
+
+static DWORD WINAPI ProcessingThread(LPVOID pParam)
+{
+    PaWinWdmStream *stream = (PaWinWdmStream*)pParam;
+    PaStreamCallbackTimeInfo ti;
+    int cbResult = paContinue;
+    int inbuf = 0;
+    int outbuf = 0;
+    int pending = 0;
+    PaError result;
+    unsigned long   wait;
+    unsigned long   eventSignaled;
+    int		fillPlaybuf = 0;
+    int		emptyRecordbuf = 0;
+    int 	framesProcessed;
+    unsigned long 	timeout;
+    int i;
+    int doChannelCopy;
+    int priming = 0;
+    PaStreamCallbackFlags underover = 0;
+
+    PA_LOGE_;
+
+    ti.inputBufferAdcTime = 0.0;
+    ti.currentTime = 0.0;
+    ti.outputBufferDacTime = 0.0;
+
+	  /* Get double buffering going */
+
+    /* Submit buffers */
+    if (stream->playbackPin)
+    {
+      	result = PinSetState(stream->playbackPin, KSSTATE_RUN);
+
+      	PA_DEBUG(("play state run = %d;",(int)result));
+      	SetEvent(stream->events[outbuf+2]);
+      	outbuf = (outbuf+1)&1;
+      	SetEvent(stream->events[outbuf+2]);
+      	outbuf = (outbuf+1)&1;
+      	pending += 2;
+        priming += 4;
+    }
+    if (stream->recordingPin)
+    {
+  		  result = PinSetState(stream->recordingPin, KSSTATE_RUN);
+
+      	PA_DEBUG(("recording state run = %d;",(int)result));
+      	PinRead(stream->recordingPin->handle,&stream->packets[inbuf]);
+      	inbuf = (inbuf+1)&1; // Increment and wrap
+      	PinRead(stream->recordingPin->handle,&stream->packets[inbuf]);
+      	inbuf = (inbuf+1)&1; // Increment and wrap
+      	/* FIXME - do error checking */
+      	pending += 2;
+    }
+    PA_DEBUG(("Out buffer len:%f\n",(2000*stream->framesPerHostOBuffer) / stream->streamRepresentation.streamInfo.sampleRate));
+    PA_DEBUG(("In buffer len:%f\n",(2000*stream->framesPerHostIBuffer) / stream->streamRepresentation.streamInfo.sampleRate));
+    timeout = max(
+    ((2000*(DWORD)stream->framesPerHostOBuffer) / (DWORD)stream->streamRepresentation.streamInfo.sampleRate),
+    ((2000*(DWORD)stream->framesPerHostIBuffer) / (DWORD)stream->streamRepresentation.streamInfo.sampleRate)
+    );
+    timeout = max(timeout,1);
+    PA_DEBUG(("Timeout = %ld ",timeout));
+
+    while(!stream->streamAbort)
+    {
+      	fillPlaybuf = 0;
+      	emptyRecordbuf = 0;
+
+      	/* Wait for next input or output buffer to be finished with*/
+    		assert(pending>0);
+
+    		if (stream->streamStop)
+    		{
+    			  PA_DEBUG(("ss1:pending=%d ",pending));
+    		}
+        wait = WaitForMultipleObjects(5, stream->events, FALSE, 0);
+        if ( wait == WAIT_TIMEOUT )
+        {
+            /* No (under|over)flow has ocurred */
+            wait = WaitForMultipleObjects(5, stream->events, FALSE, timeout);
+            eventSignaled = wait - WAIT_OBJECT_0;
+        }
+        else
+        {
+            eventSignaled = wait - WAIT_OBJECT_0;
+            if ( eventSignaled < 2 )
+            {
+                underover |= paInputOverflow;
+                PA_DEBUG(("Input overflow\n"));
+            }
+            else if (( eventSignaled < 4 )&&(!priming))
+            {
+                underover |= paOutputUnderflow;
+                PA_DEBUG(("Output underflow\n"));
+            }
+        }
+
+    		if (stream->streamStop)
+    		{
+    			  PA_DEBUG(("ss2:wait=%ld",wait));
+    		}
+        if (wait == WAIT_FAILED)
+      	{
+          	PA_DEBUG(("Wait fail = %ld! ",wait));
+            break;
+    	  }
+        if (wait == WAIT_TIMEOUT)
+        {
+            continue;
+        }
+
+    		if (eventSignaled < 2)
+		    { /* Recording input buffer has been filled */
+            PA_DEBUG(("R"));
+      			if (stream->playbackPin)
+            {
+                /* First check if also the next playback buffer has been signaled */
+        				wait = WaitForSingleObject(stream->events[outbuf+2],0);
+        				if (wait == WAIT_OBJECT_0)
+        				{
+                    /* Yes, so do both buffers at same time */
+                    fillPlaybuf = 1;
+      			        pending--;
+                    /* Was this an underflow situation? */
+                    if ( underover )
+                        underover |= paOutputUnderflow; /* Yes! */
+        				}
+       			}
+            emptyRecordbuf = 1;
+            pending--;
+    		}
+    		else if (eventSignaled < 4)
+    		{ /* Playback output buffer has been emptied */
+      			if (stream->recordingPin)
+      			{
+        				/* First check if also the next recording buffer has been signaled */
+        				wait = WaitForSingleObject(stream->events[inbuf],0);
+        				if (wait == WAIT_OBJECT_0)
+        				{ /* Yes, so do both buffers at same time */
+        				    emptyRecordbuf = 1;
+        			      pending--;
+                    /* Was this an overflow situation? */
+                    if ( underover )
+                        underover |= paInputOverflow; /* Yes! */
+        				}
+      			}
+      			fillPlaybuf = 1;
+  	        pending--;
+    		}
+    		else
+    		{
+            /* Abort event! */
+            assert(stream->streamAbort); /* Should have been set */
+      			PA_DEBUG(("ABORTING "));
+      			break;
+    		}
+    		ResetEvent(stream->events[eventSignaled]);
+
+    		if (stream->streamStop)
+    		{
+      			PA_DEBUG(("Stream stop! pending=%d",pending));
+      			cbResult = paComplete; /* Stop, but play remaining buffers */
+    		}
+
+    		/* Do necessary buffer processing (which will invoke user callback if necessary */
+        doChannelCopy = 0;
+    		if (cbResult==paContinue)
+    		{
+  	        PaUtil_BeginCpuLoadMeasurement( &stream->cpuLoadMeasurer );
+    		    PaUtil_BeginBufferProcessing(&stream->bufferProcessor,&ti,underover);
+            underover = 0; /* Reset the (under|over)flow status */
+    		    if (fillPlaybuf)
+    		    {
+        				PaUtil_SetOutputFrameCount(&stream->bufferProcessor,0);
+                if ( stream->userOutputChannels == 1 )
+                {
+                    /* Write the single user channel to the first interleaved block */
+                    PaUtil_SetOutputChannel(&stream->bufferProcessor,0,stream->packets[outbuf+2].Header.Data,stream->deviceOutputChannels);
+                    /* We will do a copy to the other channels after the data has been written */
+                    doChannelCopy = 1;
+                }
+                else
+                {
+                    for (i=0;i<stream->userOutputChannels;i++)
+                    {
+                    /* Only write the user output channels. Leave the rest blank */
+        				    PaUtil_SetOutputChannel(&stream->bufferProcessor,i,((unsigned char*)(stream->packets[outbuf+2].Header.Data))+(i*stream->outputSampleSize),stream->deviceOutputChannels);
+                    }
+                }
+    		    }
+    		    if (emptyRecordbuf)
+    		    {
+        				PaUtil_SetInputFrameCount(&stream->bufferProcessor,stream->packets[inbuf].Header.DataUsed/stream->bytesPerInputFrame);
+                for (i=0;i<stream->userInputChannels;i++)
+                {
+                    /* Only read as many channels as the user wants */
+                    PaUtil_SetInputChannel(&stream->bufferProcessor,i,((unsigned char*)(stream->packets[inbuf].Header.Data))+(i*stream->inputSampleSize),stream->deviceInputChannels);
+                }
+    		    }
+    		    framesProcessed = PaUtil_EndBufferProcessing(&stream->bufferProcessor,&cbResult);
+            if ( doChannelCopy )
+            {
+                /* Copy the first output channel to the other channels */
+                if ( stream->outputSampleSize == 2 )
+                {
+                    DuplicateFirstChannelInt16(stream->packets[outbuf+2].Header.Data,stream->deviceOutputChannels,stream->framesPerHostOBuffer);
+                }
+                else if ( stream->outputSampleSize == 3 )
+                {
+                    DuplicateFirstChannelInt24(stream->packets[outbuf+2].Header.Data,stream->deviceOutputChannels,stream->framesPerHostOBuffer);
+                }
+                else
+                    assert(0); /* Unsupported format! */
+            }
+  	        PaUtil_EndCpuLoadMeasurement( &stream->cpuLoadMeasurer, framesProcessed );
+    		}
+    		else
+    		{
+      			fillPlaybuf = 0;
+      			emptyRecordbuf = 0;
+    		}
+  	    /*
+  	    if (cbResult != paContinue)
+  	    {
+  	    	PA_DEBUG(("cbResult=%d, pending=%d:",cbResult,pending));
+  	    }
+  	    */
+  	    /* Submit buffers */
+  	    if ((fillPlaybuf)&&(cbResult!=paAbort))
+  	    {
+    	    	if (!PinWrite(stream->playbackPin->handle,&stream->packets[outbuf+2]))
+    	    	outbuf = (outbuf+1)&1; /* Increment and wrap */
+    	    	pending++;
+            if ( priming )
+                priming--; /* Have to prime twice */
+  	    }
+  	    if ((emptyRecordbuf)&&(cbResult==paContinue))
+  	    {
+      			stream->packets[inbuf].Header.DataUsed = 0; /* Reset for reuse */
+    	    	PinRead(stream->recordingPin->handle,&stream->packets[inbuf]);
+    	    	inbuf = (inbuf+1)&1; /* Increment and wrap */
+    	    	pending++;
+  	    }
+  	    if (pending==0)
+  	    {
+    	    	PA_DEBUG(("pending==0 finished...;"));
+    	    	break;
+  	    }
+  	    if ((!stream->playbackPin)&&(cbResult!=paContinue))
+  	    {
+    	    	PA_DEBUG(("record only cbResult=%d...;",cbResult));
+    	    	break;
+  	    }
+    }
+
+    PA_DEBUG(("Finished thread"));
+
+    /* Finished, either normally or aborted */
+    if (stream->playbackPin)
+    {
+        result = PinSetState(stream->playbackPin, KSSTATE_PAUSE);
+        result = PinSetState(stream->playbackPin, KSSTATE_STOP);
+    }
+    if (stream->recordingPin)
+    {
+        result = PinSetState(stream->recordingPin, KSSTATE_PAUSE);
+        result = PinSetState(stream->recordingPin, KSSTATE_STOP);
+    }
+
+    stream->streamActive = 0;
+
+    if ((!stream->streamStop)&&(!stream->streamAbort))
+    {
+  	    /* Invoke the user stream finished callback */
+  	    /* Only do it from here if not being stopped/aborted by user */
+  	    if( stream->streamRepresentation.streamFinishedCallback != 0 )
+      	    stream->streamRepresentation.streamFinishedCallback( stream->streamRepresentation.userData );
+    }
+  	stream->streamStop = 0;
+	  stream->streamAbort = 0;
+
+	  /* Reset process priority if necessary */
+	  if (stream->oldProcessPriority != REALTIME_PRIORITY_CLASS)
+	  {
+		    SetPriorityClass(GetCurrentProcess(),stream->oldProcessPriority);
+    		stream->oldProcessPriority = REALTIME_PRIORITY_CLASS;
+	  }
+
+    PA_LOGL_;
+    ExitThread(0);
+	return 0;
+}
+
+static PaError StartStream( PaStream *s )
+{
+    PaError result = paNoError;
+    PaWinWdmStream *stream = (PaWinWdmStream*)s;
+  	DWORD   dwID;
+  	BOOL ret;
+  	int size;
+
+  	PA_LOGE_;
+
+  	stream->streamStop = 0;
+  	stream->streamAbort = 0;
+  	size = 5;
+  	while (size--)
+  	{
+    		if (stream->events[size] != NULL)
+    		{
+      			ResetEvent(stream->events[size]);
+    		}
+  	}
+
+    PaUtil_ResetBufferProcessor( &stream->bufferProcessor );
+
+  	stream->oldProcessPriority = GetPriorityClass(GetCurrentProcess());
+    /* Uncomment the following line to enable dynamic boosting of the process
+     * priority to real time for best low latency support
+     * Disabled by default because RT processes can easily block the OS */
+  	/*ret = SetPriorityClass(GetCurrentProcess(),REALTIME_PRIORITY_CLASS);
+  	PA_DEBUG(("Class ret = %d;",ret));*/
+
+  	stream->streamStarted = 1;
+  	stream->streamThread = CreateThread(NULL, 0, ProcessingThread, stream, 0, &dwID);
+  	if (stream->streamThread == NULL)
+  	{
+    		stream->streamStarted = 0;
+    		result = paInsufficientMemory;
+    		goto end;
+  	}
+  	ret = SetThreadPriority(stream->streamThread,THREAD_PRIORITY_TIME_CRITICAL);
+  	PA_DEBUG(("Priority ret = %d;",ret));
+    /* Make the stream active */
+    stream->streamActive = 1;
+
+end:
+  	PA_LOGL_;
+    return result;
+}
+
+
+static PaError StopStream( PaStream *s )
+{
+    PaError result = paNoError;
+    PaWinWdmStream *stream = (PaWinWdmStream*)s;
+    int doCb = 0;
+
+  	PA_LOGE_;
+
+  	if (stream->streamActive)
+  	{
+    		doCb = 1;
+    		stream->streamStop = 1;
+    		while (stream->streamActive)
+    		{
+      			PA_DEBUG(("W."));
+      			Sleep(10); /* Let thread sleep for 10 msec */
+    		}
+  	}
+
+  	PA_DEBUG(("Terminating thread"));
+  	if (stream->streamStarted && stream->streamThread)
+  	{
+    		TerminateThread(stream->streamThread,0);
+    		stream->streamThread = NULL;
+  	}
+
+  	stream->streamStarted = 0;
+
+  	if (stream->oldProcessPriority != REALTIME_PRIORITY_CLASS)
+  	{
+    		SetPriorityClass(GetCurrentProcess(),stream->oldProcessPriority);
+    		stream->oldProcessPriority = REALTIME_PRIORITY_CLASS;
+  	}
+
+    if (doCb)
+    {
+      	/* Do user callback now after all state has been reset */
+      	/* This means it should be safe for the called function */
+      	/* to invoke e.g. StartStream */
+	      if( stream->streamRepresentation.streamFinishedCallback != 0 )
+    	     stream->streamRepresentation.streamFinishedCallback( stream->streamRepresentation.userData );
+    }
+
+  	PA_LOGL_;
+    return result;
+}
+
+static PaError AbortStream( PaStream *s )
+{
+    PaError result = paNoError;
+    PaWinWdmStream *stream = (PaWinWdmStream*)s;
+    int doCb = 0;
+
+  	PA_LOGE_;
+
+	  if (stream->streamActive)
+	  {
+		    doCb = 1;
+    		stream->streamAbort = 1;
+    		SetEvent(stream->events[4]); /* Signal immediately */
+    		while (stream->streamActive)
+    		{
+      			Sleep(10);
+    		}
+  	}
+
+	  if (stream->streamStarted && stream->streamThread)
+	  {
+    		TerminateThread(stream->streamThread,0);
+    		stream->streamThread = NULL;
+  	}
+
+    stream->streamStarted = 0;
+
+  	if (stream->oldProcessPriority != REALTIME_PRIORITY_CLASS)
+  	{
+    		SetPriorityClass(GetCurrentProcess(),stream->oldProcessPriority);
+    		stream->oldProcessPriority = REALTIME_PRIORITY_CLASS;
+  	}
+
+    if (doCb)
+    {
+      	/* Do user callback now after all state has been reset */
+    		/* This means it should be safe for the called function */
+    		/* to invoke e.g. StartStream */
+  	    if( stream->streamRepresentation.streamFinishedCallback != 0 )
+      	    stream->streamRepresentation.streamFinishedCallback( stream->streamRepresentation.userData );
+    }
+
+  	stream->streamActive = 0;
+  	stream->streamStarted = 0;
+
+  	PA_LOGL_;
+    return result;
+}
+
+
+static PaError IsStreamStopped( PaStream *s )
+{
+    PaWinWdmStream *stream = (PaWinWdmStream*)s;
+  	int result = 0;
+
+  	PA_LOGE_;
+
+    if (!stream->streamStarted)
+    		result = 1;
+
+  	PA_LOGL_;
+    return result;
+}
+
+
+static PaError IsStreamActive( PaStream *s )
+{
+    PaWinWdmStream *stream = (PaWinWdmStream*)s;
+  	int result = 0;
+
+  	PA_LOGE_;
+
+  	if (stream->streamActive)
+    		result = 1;
+
+  	PA_LOGL_;
+    return result;
+}
+
+
+static PaTime GetStreamTime( PaStream* s )
+{
+  	PA_LOGE_;
+    PA_LOGL_;
+    (void)s;
+    return PaUtil_GetTime();
+}
+
+
+static double GetStreamCpuLoad( PaStream* s )
+{
+    PaWinWdmStream *stream = (PaWinWdmStream*)s;
+  	double result;
+  	PA_LOGE_;
+    result = PaUtil_GetCpuLoad( &stream->cpuLoadMeasurer );
+  	PA_LOGL_;
+  	return result;
+}
+
+
+/*
+    As separate stream interfaces are used for blocking and callback
+    streams, the following functions can be guaranteed to only be called
+    for blocking streams.
+*/
+
+static PaError ReadStream( PaStream* s,
+                           void *buffer,
+                           unsigned long frames )
+{
+    PaWinWdmStream *stream = (PaWinWdmStream*)s;
+
+  	PA_LOGE_;
+
+    /* suppress unused variable warnings */
+    (void) buffer;
+    (void) frames;
+    (void) stream;
+
+    /* IMPLEMENT ME, see portaudio.h for required behavior*/
+  	PA_LOGL_;
+    return paNoError;
+}
+
+
+static PaError WriteStream( PaStream* s,
+                            const void *buffer,
+                            unsigned long frames )
+{
+    PaWinWdmStream *stream = (PaWinWdmStream*)s;
+
+  	PA_LOGE_;
+
+    /* suppress unused variable warnings */
+    (void) buffer;
+    (void) frames;
+    (void) stream;
+
+    /* IMPLEMENT ME, see portaudio.h for required behavior*/
+  	PA_LOGL_;
+    return paNoError;
+}
+
+
+static signed long GetStreamReadAvailable( PaStream* s )
+{
+    PaWinWdmStream *stream = (PaWinWdmStream*)s;
+
+  	PA_LOGE_;
+
+    /* suppress unused variable warnings */
+    (void) stream;
+
+    /* IMPLEMENT ME, see portaudio.h for required behavior*/
+  	PA_LOGL_;
+    return 0;
+}
+
+
+static signed long GetStreamWriteAvailable( PaStream* s )
+{
+    PaWinWdmStream *stream = (PaWinWdmStream*)s;
+
+  	PA_LOGE_;
+    /* suppress unused variable warnings */
+    (void) stream;
+
+    /* IMPLEMENT ME, see portaudio.h for required behavior*/
+  	PA_LOGL_;
+    return 0;
+}
+
+
+
+
diff --git a/src/audio/portaudio/pa_win_wdmks/readme.txt b/src/audio/portaudio/pa_win_wdmks/readme.txt
new file mode 100644
index 0000000000000000000000000000000000000000..2fc6c75c609cb96299e238eb0dc8a122b231caef
--- /dev/null
+++ b/src/audio/portaudio/pa_win_wdmks/readme.txt
@@ -0,0 +1,71 @@
+Notes about WDM-KS host API
+---------------------------
+
+Status history
+--------------
+5th September 2004:
+This is the first public version of the code. It should be considered
+an alpha release with zero guarantee not to crash on any particular 
+system. So far it has only been tested in the author's development
+environment, which means a Win2k/SP2 PIII laptop with integrated 
+SoundMAX driver and USB Tascam US-428 compiled with both MinGW
+(GCC 3.3) and MSVC++6 using the MS DirectX 9 SDK.
+It has been most widely tested with the MinGW build, with most of the
+test programs (particularly paqa_devs and paqa_errs) passing.
+There are some notable failures: patest_out_underflow and both of the
+blocking I/O tests (as blocking I/O is not implemented).
+At this point the code needs to be tested with a much wider variety 
+of configurations and feedback provided from testers regarding
+both working and failing cases.
+
+What is the WDM-KS host API?
+----------------------------
+PortAudio for Windows currently has 3 functional host implementations.
+MME uses the oldest Windows audio API which does not offer good
+play/record latency. 
+DirectX improves this, but still imposes a penalty
+of 10s of milliseconds due to the system mixing of streams from
+multiple applications. 
+ASIO offers very good latency, but requires special drivers which are
+not always available for cheaper audio hardware. Also, when ASIO 
+drivers are available, they are not always so robust because they 
+bypass all of the standardised Windows device driver architecture 
+and hit the hardware their own way.
+Alternatively there are a couple of free (but closed source) ASIO 
+implementations which connect to the lower level Windows 
+"Kernel Streaming" API, but again these require special installation 
+by the user, and can be limited in functionality or difficult to use. 
+
+This is where the PortAudio "WDM-KS" host implementation comes in.
+It directly connects PortAudio to the same Kernel Streaming API which
+those ASIO bridges use. This avoids the mixing penatly of DirectX, 
+giving at least as good latency as any ASIO driver, but it has the
+advantage of working with ANY Windows audio hardware which is available
+through the normal MME/DirectX routes without the user requiring 
+any additional device drivers to be installed, and allowing all 
+device selection to be done through the normal PortAudio API.
+
+Note that in general you should only be using this host API if your 
+application has a real requirement for very low latency audio (<20ms), 
+either because you are generating sounds in real-time based upon 
+user input, or you a processing recorded audio in real time.
+
+The only thing to be aware of is that using the KS interface will
+block that device from being used by the rest of system through
+the higher level APIs, or conversely, if the system is using
+a device, the KS API will not be able to use it. MS recommend that
+you should keep the device open only when your application has focus.
+In PortAudio terms, this means having a stream Open on a WDMKS device.
+
+Usage
+-----
+To add the WDMKS backend to your program which is already using 
+PortAudio, you must undefine PA_NO_WDMKS from your build file,
+and include the pa_win_wdmks\pa_win_wdmks.c into your build.
+The file should compile in both C and C++.
+You will need a DirectX SDK installed on your system for the
+ks.h and ksmedia.h header files.
+You will need to link to the system "setupapi" library.
+Note that if you use MinGW, you will get more warnings from 
+the DX header files when using GCC(C), and still a few warnings
+with G++(CPP).
\ No newline at end of file
diff --git a/src/audio/portaudio/pa_win_wmme/pa_win_wmme.c b/src/audio/portaudio/pa_win_wmme/pa_win_wmme.c
new file mode 100644
index 0000000000000000000000000000000000000000..86b6cdf143e3be3bee5ebd4a77afd62f4a42d3a9
--- /dev/null
+++ b/src/audio/portaudio/pa_win_wmme/pa_win_wmme.c
@@ -0,0 +1,3623 @@
+/*
+ * $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 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:
+ PLB = Phil Burk
+ JM = Julien Maillard
+ RDB = Ross Bencina
+ 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 conditional inclusion 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.
+ PLB20020321 - detect Win XP versus NT, 9x; fix DBUG typo; removed init of CurrentCount
+ RDB20020411 - various renaming cleanups, factored streamData alloc and cpu usage init
+ RDB20020417 - stopped counting WAVE_MAPPER when there were no real devices
+               refactoring, renaming and fixed a few edge case bugs
+ RDB20020531 - converted to V19 framework
+ ** NOTE  maintanance history is now stored in CVS **
+*/
+
+/** @file
+	
+	@todo Fix buffer catch up code, can sometimes get stuck (perhaps fixed now,
+            needs to be reviewed and tested.)
+
+    @todo implement paInputUnderflow, paOutputOverflow streamCallback statusFlags, paNeverDropInput.
+
+    @todo BUG: PA_MME_SET_LAST_WAVEIN/OUT_ERROR is used in functions which may
+                be called asynchronously from the callback thread. this is bad.
+
+    @todo implement inputBufferAdcTime in callback thread
+
+    @todo review/fix error recovery and cleanup in marked functions
+
+    @todo implement timeInfo for stream priming
+
+    @todo handle the case where the callback returns paAbort or paComplete during stream priming.
+
+    @todo review input overflow and output underflow handling in ReadStream and WriteStream
+
+Non-critical stuff for the future:
+
+    @todo Investigate supporting host buffer formats > 16 bits
+    
+    @todo define UNICODE and _UNICODE in the project settings and see what breaks
+
+*/
+
+/*
+    How it works:
+
+    For both callback and blocking read/write streams we open the MME devices
+    in CALLBACK_EVENT mode. In this mode, MME signals an Event object whenever
+    it has finished with a buffer (either filled it for input, or played it
+    for output). Where necessary we block waiting for Event objects using
+    WaitMultipleObjects().
+
+    When implementing a PA callback stream, we set up a high priority thread
+    which waits on the MME buffer Events and drains/fills the buffers when
+    they are ready.
+
+    When implementing a PA blocking read/write stream, we simply wait on these
+    Events (when necessary) inside the ReadStream() and WriteStream() functions.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <windows.h>
+#include <mmsystem.h>
+#include <process.h>
+#include <assert.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_trace.h"
+#include "pa_util.h"
+#include "pa_allocation.h"
+#include "pa_hostapi.h"
+#include "pa_stream.h"
+#include "pa_cpuload.h"
+#include "pa_process.h"
+
+#include "pa_win_wmme.h"
+
+#if (defined(WIN32) && (defined(_MSC_VER) && (_MSC_VER >= 1200))) /* MSC version 6 and above */
+#pragma comment(lib, "winmm.lib")
+#endif
+
+/************************************************* Constants ********/
+
+#define PA_MME_USE_HIGH_DEFAULT_LATENCY_    (0)  /* For debugging glitches. */
+
+#if PA_MME_USE_HIGH_DEFAULT_LATENCY_
+ #define PA_MME_WIN_9X_DEFAULT_LATENCY_                     (0.4)
+ #define PA_MME_MIN_HOST_OUTPUT_BUFFER_COUNT_               (4)
+ #define PA_MME_MIN_HOST_INPUT_BUFFER_COUNT_FULL_DUPLEX_	(4)
+ #define PA_MME_MIN_HOST_INPUT_BUFFER_COUNT_HALF_DUPLEX_	(4)
+ #define PA_MME_MIN_HOST_BUFFER_FRAMES_WHEN_UNSPECIFIED_	(16)
+ #define PA_MME_MAX_HOST_BUFFER_SECS_				        (0.3)       /* Do not exceed unless user buffer exceeds */
+ #define PA_MME_MAX_HOST_BUFFER_BYTES_				        (32 * 1024) /* Has precedence over PA_MME_MAX_HOST_BUFFER_SECS_, some drivers are known to crash with buffer sizes > 32k */
+#else
+ #define PA_MME_WIN_9X_DEFAULT_LATENCY_                     (0.2)
+ #define PA_MME_MIN_HOST_OUTPUT_BUFFER_COUNT_               (2)
+ #define PA_MME_MIN_HOST_INPUT_BUFFER_COUNT_FULL_DUPLEX_	(3)
+ #define PA_MME_MIN_HOST_INPUT_BUFFER_COUNT_HALF_DUPLEX_	(2)
+ #define PA_MME_MIN_HOST_BUFFER_FRAMES_WHEN_UNSPECIFIED_	(16)
+ #define PA_MME_MAX_HOST_BUFFER_SECS_				        (0.1)       /* Do not exceed unless user buffer exceeds */
+ #define PA_MME_MAX_HOST_BUFFER_BYTES_				        (32 * 1024) /* Has precedence over PA_MME_MAX_HOST_BUFFER_SECS_, some drivers are known to crash with buffer sizes > 32k */
+#endif
+
+/* Use higher latency for NT because it is even worse at real-time
+   operation than Win9x.
+*/
+#define PA_MME_WIN_NT_DEFAULT_LATENCY_      (PA_MME_WIN_9X_DEFAULT_LATENCY_ * 2)
+#define PA_MME_WIN_WDM_DEFAULT_LATENCY_     (PA_MME_WIN_9X_DEFAULT_LATENCY_)
+
+
+#define PA_MME_MIN_TIMEOUT_MSEC_        (1000)
+
+static const char constInputMapperSuffix_[] = " - Input";
+static const char constOutputMapperSuffix_[] = " - Output";
+
+/********************************************************************/
+
+typedef struct PaWinMmeStream PaWinMmeStream;     /* forward declaration */
+
+/* prototypes for functions declared in this file */
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif /* __cplusplus */
+
+PaError PaWinMme_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+static void Terminate( struct PaUtilHostApiRepresentation *hostApi );
+static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
+                           PaStream** stream,
+                           const PaStreamParameters *inputParameters,
+                           const PaStreamParameters *outputParameters,
+                           double sampleRate,
+                           unsigned long framesPerBuffer,
+                           PaStreamFlags streamFlags,
+                           PaStreamCallback *streamCallback,
+                           void *userData );
+static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
+                                  const PaStreamParameters *inputParameters,
+                                  const PaStreamParameters *outputParameters,
+                                  double sampleRate );
+static PaError CloseStream( PaStream* stream );
+static PaError StartStream( PaStream *stream );
+static PaError StopStream( PaStream *stream );
+static PaError AbortStream( PaStream *stream );
+static PaError IsStreamStopped( PaStream *s );
+static PaError IsStreamActive( PaStream *stream );
+static PaTime GetStreamTime( PaStream *stream );
+static double GetStreamCpuLoad( PaStream* stream );
+static PaError ReadStream( PaStream* stream, void *buffer, unsigned long frames );
+static PaError WriteStream( PaStream* stream, const void *buffer, unsigned long frames );
+static signed long GetStreamReadAvailable( PaStream* stream );
+static signed long GetStreamWriteAvailable( PaStream* stream );
+
+
+/* macros for setting last host error information */
+
+#ifdef UNICODE
+
+#define PA_MME_SET_LAST_WAVEIN_ERROR( mmresult ) \
+    {                                                                   \
+        wchar_t mmeErrorTextWide[ MAXERRORLENGTH ];                     \
+        char mmeErrorText[ MAXERRORLENGTH ];                            \
+        waveInGetErrorText( mmresult, mmeErrorTextWide, MAXERRORLENGTH );   \
+        WideCharToMultiByte( CP_ACP, WC_COMPOSITECHECK | WC_DEFAULTCHAR,\
+            mmeErrorTextWide, -1, mmeErrorText, MAXERRORLENGTH, NULL, NULL );  \
+        PaUtil_SetLastHostErrorInfo( paMME, mmresult, mmeErrorText );   \
+    }
+
+#define PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult ) \
+    {                                                                   \
+        wchar_t mmeErrorTextWide[ MAXERRORLENGTH ];                     \
+        char mmeErrorText[ MAXERRORLENGTH ];                            \
+        waveOutGetErrorText( mmresult, mmeErrorTextWide, MAXERRORLENGTH );  \
+        WideCharToMultiByte( CP_ACP, WC_COMPOSITECHECK | WC_DEFAULTCHAR,\
+            mmeErrorTextWide, -1, mmeErrorText, MAXERRORLENGTH, NULL, NULL );  \
+        PaUtil_SetLastHostErrorInfo( paMME, mmresult, mmeErrorText );   \
+    }
+    
+#else /* !UNICODE */
+
+#define PA_MME_SET_LAST_WAVEIN_ERROR( mmresult ) \
+    {                                                                   \
+        char mmeErrorText[ MAXERRORLENGTH ];                            \
+        waveInGetErrorText( mmresult, mmeErrorText, MAXERRORLENGTH );   \
+        PaUtil_SetLastHostErrorInfo( paMME, mmresult, mmeErrorText );   \
+    }
+
+#define PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult ) \
+    {                                                                   \
+        char mmeErrorText[ MAXERRORLENGTH ];                            \
+        waveOutGetErrorText( mmresult, mmeErrorText, MAXERRORLENGTH );  \
+        PaUtil_SetLastHostErrorInfo( paMME, mmresult, mmeErrorText );   \
+    }
+
+#endif /* UNICODE */
+
+
+static void PaMme_SetLastSystemError( DWORD errorCode )
+{
+    char *lpMsgBuf;
+    FormatMessage(
+        FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
+        NULL,
+        errorCode,
+        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+        (LPTSTR) &lpMsgBuf,
+        0,
+        NULL
+    );
+    PaUtil_SetLastHostErrorInfo( paMME, errorCode, lpMsgBuf );
+    LocalFree( lpMsgBuf );
+}
+
+#define PA_MME_SET_LAST_SYSTEM_ERROR( errorCode ) \
+    PaMme_SetLastSystemError( errorCode )
+
+
+/* PaError returning wrappers for some commonly used win32 functions
+    note that we allow passing a null ptr to have no effect.
+*/
+
+static PaError CreateEventWithPaError( HANDLE *handle,
+        LPSECURITY_ATTRIBUTES lpEventAttributes,
+        BOOL bManualReset,
+        BOOL bInitialState,
+        LPCTSTR lpName )
+{
+    PaError result = paNoError;
+
+    *handle = NULL;
+    
+    *handle = CreateEvent( lpEventAttributes, bManualReset, bInitialState, lpName );
+    if( *handle == NULL )
+    {
+        result = paUnanticipatedHostError;
+        PA_MME_SET_LAST_SYSTEM_ERROR( GetLastError() );
+    }
+
+    return result;
+}
+
+
+static PaError ResetEventWithPaError( HANDLE handle )
+{
+    PaError result = paNoError;
+
+    if( handle )
+    {
+        if( ResetEvent( handle ) == 0 )
+        {
+            result = paUnanticipatedHostError;
+            PA_MME_SET_LAST_SYSTEM_ERROR( GetLastError() );
+        }
+    }
+
+    return result;
+}
+
+
+static PaError CloseHandleWithPaError( HANDLE handle )
+{
+    PaError result = paNoError;
+    
+    if( handle )
+    {
+        if( CloseHandle( handle ) == 0 )
+        {
+            result = paUnanticipatedHostError;
+            PA_MME_SET_LAST_SYSTEM_ERROR( GetLastError() );
+        }
+    }
+    
+    return result;
+}
+
+
+/* PaWinMmeHostApiRepresentation - host api datastructure specific to this implementation */
+
+typedef struct
+{
+    PaUtilHostApiRepresentation inheritedHostApiRep;
+    PaUtilStreamInterface callbackStreamInterface;
+    PaUtilStreamInterface blockingStreamInterface;
+
+    PaUtilAllocationGroup *allocations;
+    
+    int inputDeviceCount, outputDeviceCount;
+
+    /** winMmeDeviceIds is an array of WinMme device ids.
+        fields in the range [0, inputDeviceCount) are input device ids,
+        and [inputDeviceCount, inputDeviceCount + outputDeviceCount) are output
+        device ids.
+     */ 
+    UINT *winMmeDeviceIds;
+}
+PaWinMmeHostApiRepresentation;
+
+
+typedef struct
+{
+    PaDeviceInfo inheritedDeviceInfo;
+    DWORD dwFormats; /**<< standard formats bitmask from the WAVEINCAPS and WAVEOUTCAPS structures */
+}
+PaWinMmeDeviceInfo;
+
+
+/*************************************************************************
+ * 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 PaDeviceIndex GetEnvDefaultDeviceID( char *envName )
+{
+    PaDeviceIndex recommendedIndex = paNoDevice;
+    DWORD   hresult;
+    char    envbuf[PA_ENV_BUF_SIZE_];
+
+#ifndef WIN32_PLATFORM_PSPC /* no GetEnvironmentVariable on PocketPC */
+
+    /* 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_) )
+    {
+        recommendedIndex = atoi( envbuf );
+    }
+#endif
+
+    return recommendedIndex;
+}
+
+
+static void InitializeDefaultDeviceIdsFromEnv( PaWinMmeHostApiRepresentation *hostApi )
+{
+    PaDeviceIndex device;
+
+    /* input */
+    device = GetEnvDefaultDeviceID( PA_REC_IN_DEV_ENV_NAME_ );
+    if( device != paNoDevice &&
+            ( device >= 0 && device < hostApi->inheritedHostApiRep.info.deviceCount ) &&
+            hostApi->inheritedHostApiRep.deviceInfos[ device ]->maxInputChannels > 0 )
+    {
+        hostApi->inheritedHostApiRep.info.defaultInputDevice = device;
+    }
+
+    /* output */
+    device = GetEnvDefaultDeviceID( PA_REC_OUT_DEV_ENV_NAME_ );
+    if( device != paNoDevice &&
+            ( device >= 0 && device < hostApi->inheritedHostApiRep.info.deviceCount ) &&
+            hostApi->inheritedHostApiRep.deviceInfos[ device ]->maxOutputChannels > 0 )
+    {
+        hostApi->inheritedHostApiRep.info.defaultOutputDevice = device;
+    }
+}
+
+
+/** Convert external PA ID to a windows multimedia device ID
+*/
+static UINT LocalDeviceIndexToWinMmeDeviceId( PaWinMmeHostApiRepresentation *hostApi, PaDeviceIndex device )
+{
+    assert( device >= 0 && device < hostApi->inputDeviceCount + hostApi->outputDeviceCount );
+
+	return hostApi->winMmeDeviceIds[ device ];
+}
+
+
+static PaError QueryInputWaveFormatEx( int deviceId, WAVEFORMATEX *waveFormatEx )
+{
+    MMRESULT mmresult;
+    
+    switch( mmresult = waveInOpen( NULL, deviceId, waveFormatEx, 0, 0, WAVE_FORMAT_QUERY ) )
+    {
+        case MMSYSERR_NOERROR:
+            return paNoError;
+        case MMSYSERR_ALLOCATED:    /* Specified resource is already allocated. */
+            return paDeviceUnavailable;
+        case MMSYSERR_NODRIVER:	    /* No device driver is present. */
+            return paDeviceUnavailable;
+        case MMSYSERR_NOMEM:	    /* Unable to allocate or lock memory. */
+            return paInsufficientMemory;
+        case WAVERR_BADFORMAT:      /* Attempted to open with an unsupported waveform-audio format. */
+            return paSampleFormatNotSupported;
+                    
+        case MMSYSERR_BADDEVICEID:	/* Specified device identifier is out of range. */
+            /* falls through */
+        default:
+            PA_MME_SET_LAST_WAVEIN_ERROR( mmresult );
+            return paUnanticipatedHostError;
+    }
+}
+
+
+static PaError QueryOutputWaveFormatEx( int deviceId, WAVEFORMATEX *waveFormatEx )
+{
+    MMRESULT mmresult;
+    
+    switch( mmresult = waveOutOpen( NULL, deviceId, waveFormatEx, 0, 0, WAVE_FORMAT_QUERY ) )
+    {
+        case MMSYSERR_NOERROR:
+            return paNoError;
+        case MMSYSERR_ALLOCATED:    /* Specified resource is already allocated. */
+            return paDeviceUnavailable;
+        case MMSYSERR_NODRIVER:	    /* No device driver is present. */
+            return paDeviceUnavailable;
+        case MMSYSERR_NOMEM:	    /* Unable to allocate or lock memory. */
+            return paInsufficientMemory;
+        case WAVERR_BADFORMAT:      /* Attempted to open with an unsupported waveform-audio format. */
+            return paSampleFormatNotSupported;
+                    
+        case MMSYSERR_BADDEVICEID:	/* Specified device identifier is out of range. */
+            /* falls through */
+        default:
+            PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult );
+            return paUnanticipatedHostError;
+    }
+}
+
+
+static PaError QueryFormatSupported( PaDeviceInfo *deviceInfo,
+        PaError (*waveFormatExQueryFunction)(int, WAVEFORMATEX*),
+        int winMmeDeviceId, int channels, double sampleRate )
+{
+    PaWinMmeDeviceInfo *winMmeDeviceInfo = (PaWinMmeDeviceInfo*)deviceInfo;
+    WAVEFORMATEX waveFormatEx;
+    
+    if( sampleRate == 11025.0
+        && ( (channels == 1 && (winMmeDeviceInfo->dwFormats & WAVE_FORMAT_1M16))
+            || (channels == 2 && (winMmeDeviceInfo->dwFormats & WAVE_FORMAT_1S16)) ) ){
+
+        return paNoError;
+    }
+
+    if( sampleRate == 22050.0
+        && ( (channels == 1 && (winMmeDeviceInfo->dwFormats & WAVE_FORMAT_2M16))
+            || (channels == 2 && (winMmeDeviceInfo->dwFormats & WAVE_FORMAT_2S16)) ) ){
+
+        return paNoError;
+    }
+
+    if( sampleRate == 44100.0
+        && ( (channels == 1 && (winMmeDeviceInfo->dwFormats & WAVE_FORMAT_4M16))
+            || (channels == 2 && (winMmeDeviceInfo->dwFormats & WAVE_FORMAT_4S16)) ) ){
+
+        return paNoError;
+    }
+
+    waveFormatEx.wFormatTag = WAVE_FORMAT_PCM;
+    waveFormatEx.nChannels = (WORD)channels;
+    waveFormatEx.nSamplesPerSec = (DWORD)sampleRate;
+    waveFormatEx.nAvgBytesPerSec = waveFormatEx.nSamplesPerSec * channels * sizeof(short);
+    waveFormatEx.nBlockAlign = (WORD)(channels * sizeof(short));
+    waveFormatEx.wBitsPerSample = 16;
+    waveFormatEx.cbSize = 0;
+
+    return waveFormatExQueryFunction( winMmeDeviceId, &waveFormatEx );
+}
+
+
+#define PA_DEFAULTSAMPLERATESEARCHORDER_COUNT_  (13) /* must match array length below */
+static double defaultSampleRateSearchOrder_[] =
+    { 44100.0, 48000.0, 32000.0, 24000.0, 22050.0, 88200.0, 96000.0, 192000.0,
+        16000.0, 12000.0, 11025.0, 9600.0, 8000.0 };
+
+static void DetectDefaultSampleRate( PaWinMmeDeviceInfo *winMmeDeviceInfo, int winMmeDeviceId,
+        PaError (*waveFormatExQueryFunction)(int, WAVEFORMATEX*), int maxChannels )
+{
+    PaDeviceInfo *deviceInfo = &winMmeDeviceInfo->inheritedDeviceInfo;
+    int i;
+    
+    deviceInfo->defaultSampleRate = 0.;
+
+    for( i=0; i < PA_DEFAULTSAMPLERATESEARCHORDER_COUNT_; ++i )
+    {
+        double sampleRate = defaultSampleRateSearchOrder_[ i ]; 
+        PaError paerror = QueryFormatSupported( deviceInfo, waveFormatExQueryFunction, winMmeDeviceId, maxChannels, sampleRate );
+        if( paerror == paNoError )
+        {
+            deviceInfo->defaultSampleRate = sampleRate;
+            break;
+        }
+    }
+}
+
+
+static PaError InitializeInputDeviceInfo( PaWinMmeHostApiRepresentation *winMmeHostApi,
+        PaWinMmeDeviceInfo *winMmeDeviceInfo, UINT winMmeInputDeviceId, int *success )
+{
+    PaError result = paNoError;
+    char *deviceName; /* non-const ptr */
+    MMRESULT mmresult;
+    WAVEINCAPS wic;
+    PaDeviceInfo *deviceInfo = &winMmeDeviceInfo->inheritedDeviceInfo;
+    
+    *success = 0;
+
+    mmresult = waveInGetDevCaps( winMmeInputDeviceId, &wic, sizeof( WAVEINCAPS ) );
+    if( mmresult == MMSYSERR_NOMEM )
+    {
+        result = paInsufficientMemory;
+        goto error;
+    }
+    else if( mmresult != MMSYSERR_NOERROR )
+    {
+        /* instead of returning paUnanticipatedHostError we return
+            paNoError, but leave success set as 0. This allows
+            Pa_Initialize to just ignore this device, without failing
+            the entire initialisation process.
+        */
+        return paNoError;
+    }           
+
+    if( winMmeInputDeviceId == WAVE_MAPPER )
+    {
+        /* Append I/O suffix to WAVE_MAPPER device. */
+        deviceName = (char *)PaUtil_GroupAllocateMemory(
+                    winMmeHostApi->allocations, strlen( wic.szPname ) + 1 + sizeof(constInputMapperSuffix_) );
+        if( !deviceName )
+        {
+            result = paInsufficientMemory;
+            goto error;
+        }
+        strcpy( deviceName, wic.szPname );
+        strcat( deviceName, constInputMapperSuffix_ );
+    }
+    else
+    {
+        deviceName = (char*)PaUtil_GroupAllocateMemory(
+                    winMmeHostApi->allocations, strlen( wic.szPname ) + 1 );
+        if( !deviceName )
+        {
+            result = paInsufficientMemory;
+            goto error;
+        }
+        strcpy( deviceName, wic.szPname  );
+    }
+    deviceInfo->name = deviceName;
+
+    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) )
+    {
+        PA_DEBUG(("Pa_GetDeviceInfo: Num input channels reported as %d! Changed to 2.\n", deviceInfo->maxInputChannels ));
+        deviceInfo->maxInputChannels = 2;
+    }
+
+    winMmeDeviceInfo->dwFormats = wic.dwFormats;
+
+    DetectDefaultSampleRate( winMmeDeviceInfo, winMmeInputDeviceId,
+            QueryInputWaveFormatEx, deviceInfo->maxInputChannels );
+
+    *success = 1;
+    
+error:
+    return result;
+}
+
+
+static PaError InitializeOutputDeviceInfo( PaWinMmeHostApiRepresentation *winMmeHostApi,
+        PaWinMmeDeviceInfo *winMmeDeviceInfo, UINT winMmeOutputDeviceId, int *success )
+{
+    PaError result = paNoError;
+    char *deviceName; /* non-const ptr */
+    MMRESULT mmresult;
+    WAVEOUTCAPS woc;
+    PaDeviceInfo *deviceInfo = &winMmeDeviceInfo->inheritedDeviceInfo;
+    
+    *success = 0;
+
+    mmresult = waveOutGetDevCaps( winMmeOutputDeviceId, &woc, sizeof( WAVEOUTCAPS ) );
+    if( mmresult == MMSYSERR_NOMEM )
+    {
+        result = paInsufficientMemory;
+        goto error;
+    }
+    else if( mmresult != MMSYSERR_NOERROR )
+    {
+        /* instead of returning paUnanticipatedHostError we return
+            paNoError, but leave success set as 0. This allows
+            Pa_Initialize to just ignore this device, without failing
+            the entire initialisation process.
+        */
+        return paNoError;
+    }
+
+    if( winMmeOutputDeviceId == WAVE_MAPPER )
+    {
+        /* Append I/O suffix to WAVE_MAPPER device. */
+        deviceName = (char *)PaUtil_GroupAllocateMemory(
+                    winMmeHostApi->allocations, strlen( woc.szPname ) + 1 + sizeof(constOutputMapperSuffix_) );
+        if( !deviceName )
+        {
+            result = paInsufficientMemory;
+            goto error;
+        }
+        strcpy( deviceName, woc.szPname );
+        strcat( deviceName, constOutputMapperSuffix_ );
+    }
+    else
+    {
+        deviceName = (char*)PaUtil_GroupAllocateMemory(
+                    winMmeHostApi->allocations, strlen( woc.szPname ) + 1 );
+        if( !deviceName )
+        {
+            result = paInsufficientMemory;
+            goto error;
+        }
+        strcpy( deviceName, woc.szPname  );
+    }
+    deviceInfo->name = deviceName;
+
+    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.
+     * It also happens on Win XP!
+     */
+    if( (deviceInfo->maxOutputChannels < 1) || (deviceInfo->maxOutputChannels > 256) )
+    {
+        PA_DEBUG(("Pa_GetDeviceInfo: Num output channels reported as %d! Changed to 2.\n", deviceInfo->maxOutputChannels ));
+        deviceInfo->maxOutputChannels = 2;
+    }
+
+    winMmeDeviceInfo->dwFormats = woc.dwFormats;
+
+    DetectDefaultSampleRate( winMmeDeviceInfo, winMmeOutputDeviceId,
+            QueryOutputWaveFormatEx, deviceInfo->maxOutputChannels );
+
+    *success = 1;
+    
+error:
+    return result;
+}
+
+
+static void GetDefaultLatencies( PaTime *defaultLowLatency, PaTime *defaultHighLatency )
+{
+    OSVERSIONINFO osvi;
+    osvi.dwOSVersionInfoSize = sizeof( osvi );
+	GetVersionEx( &osvi );
+
+    /* Check for NT */
+    if( (osvi.dwMajorVersion == 4) && (osvi.dwPlatformId == 2) )
+    {
+        *defaultLowLatency = PA_MME_WIN_NT_DEFAULT_LATENCY_;
+    }
+    else if(osvi.dwMajorVersion >= 5)
+    {
+        *defaultLowLatency  = PA_MME_WIN_WDM_DEFAULT_LATENCY_;
+    }
+    else
+    {
+        *defaultLowLatency  = PA_MME_WIN_9X_DEFAULT_LATENCY_;
+    }     
+
+    *defaultHighLatency = *defaultLowLatency * 2;
+}
+
+
+PaError PaWinMme_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex )
+{
+    PaError result = paNoError;
+    int i;
+    PaWinMmeHostApiRepresentation *winMmeHostApi;
+    int inputDeviceCount, outputDeviceCount, maximumPossibleDeviceCount;
+    PaWinMmeDeviceInfo *deviceInfoArray;
+    int deviceInfoInitializationSucceeded;
+    PaTime defaultLowLatency, defaultHighLatency;
+
+    winMmeHostApi = (PaWinMmeHostApiRepresentation*)PaUtil_AllocateMemory( sizeof(PaWinMmeHostApiRepresentation) );
+    if( !winMmeHostApi )
+    {
+        result = paInsufficientMemory;
+        goto error;
+    }
+
+    winMmeHostApi->allocations = PaUtil_CreateAllocationGroup();
+    if( !winMmeHostApi->allocations )
+    {
+        result = paInsufficientMemory;
+        goto error;
+    }
+
+    *hostApi = &winMmeHostApi->inheritedHostApiRep;
+    (*hostApi)->info.structVersion = 1;
+    (*hostApi)->info.type = paMME;
+    (*hostApi)->info.name = "MME";
+
+    
+    /* initialise device counts and default devices under the assumption that
+        there are no devices. These values are incremented below if and when
+        devices are successfully initialized.
+    */
+    (*hostApi)->info.deviceCount = 0;
+    (*hostApi)->info.defaultInputDevice = paNoDevice;
+    (*hostApi)->info.defaultOutputDevice = paNoDevice;
+    winMmeHostApi->inputDeviceCount = 0;
+    winMmeHostApi->outputDeviceCount = 0;
+
+
+    maximumPossibleDeviceCount = 0;
+
+    inputDeviceCount = waveInGetNumDevs();
+    if( inputDeviceCount > 0 )
+    	maximumPossibleDeviceCount += inputDeviceCount + 1;	/* assume there is a WAVE_MAPPER */
+
+    outputDeviceCount = waveOutGetNumDevs();
+    if( outputDeviceCount > 0 )
+	    maximumPossibleDeviceCount += outputDeviceCount + 1;	/* assume there is a WAVE_MAPPER */
+
+
+    if( maximumPossibleDeviceCount > 0 ){
+
+        (*hostApi)->deviceInfos = (PaDeviceInfo**)PaUtil_GroupAllocateMemory(
+                winMmeHostApi->allocations, sizeof(PaDeviceInfo*) * maximumPossibleDeviceCount );
+        if( !(*hostApi)->deviceInfos )
+        {
+            result = paInsufficientMemory;
+            goto error;
+        }
+
+        /* allocate all device info structs in a contiguous block */
+        deviceInfoArray = (PaWinMmeDeviceInfo*)PaUtil_GroupAllocateMemory(
+                winMmeHostApi->allocations, sizeof(PaWinMmeDeviceInfo) * maximumPossibleDeviceCount );
+        if( !deviceInfoArray )
+        {
+            result = paInsufficientMemory;
+            goto error;
+        }
+
+        winMmeHostApi->winMmeDeviceIds = (UINT*)PaUtil_GroupAllocateMemory(
+                winMmeHostApi->allocations, sizeof(int) * maximumPossibleDeviceCount );
+        if( !winMmeHostApi->winMmeDeviceIds )
+        {
+            result = paInsufficientMemory;
+            goto error;
+        }
+
+        GetDefaultLatencies( &defaultLowLatency, &defaultHighLatency );
+
+        if( inputDeviceCount > 0 ){
+            /* -1 is the WAVE_MAPPER */
+            for( i = -1; i < inputDeviceCount; ++i ){
+                UINT winMmeDeviceId = (UINT)((i==-1) ? WAVE_MAPPER : i);
+                PaWinMmeDeviceInfo *wmmeDeviceInfo = &deviceInfoArray[ (*hostApi)->info.deviceCount ];
+                PaDeviceInfo *deviceInfo = &wmmeDeviceInfo->inheritedDeviceInfo;
+                deviceInfo->structVersion = 2;
+                deviceInfo->hostApi = hostApiIndex;
+
+                deviceInfo->maxInputChannels = 0;
+                deviceInfo->maxOutputChannels = 0;
+
+                deviceInfo->defaultLowInputLatency = defaultLowLatency;
+                deviceInfo->defaultLowOutputLatency = defaultLowLatency;
+                deviceInfo->defaultHighInputLatency = defaultHighLatency;
+                deviceInfo->defaultHighOutputLatency = defaultHighLatency;
+
+                result = InitializeInputDeviceInfo( winMmeHostApi, wmmeDeviceInfo,
+                        winMmeDeviceId, &deviceInfoInitializationSucceeded );
+                if( result != paNoError )
+                    goto error;
+
+                if( deviceInfoInitializationSucceeded ){
+                    if( (*hostApi)->info.defaultInputDevice == paNoDevice )
+                        (*hostApi)->info.defaultInputDevice = (*hostApi)->info.deviceCount;
+
+                    winMmeHostApi->winMmeDeviceIds[ (*hostApi)->info.deviceCount ] = winMmeDeviceId;
+                    (*hostApi)->deviceInfos[ (*hostApi)->info.deviceCount ] = deviceInfo;
+
+                    winMmeHostApi->inputDeviceCount++;
+                    (*hostApi)->info.deviceCount++;
+                }
+            }
+        }
+
+        if( outputDeviceCount > 0 ){
+            /* -1 is the WAVE_MAPPER */
+            for( i = -1; i < outputDeviceCount; ++i ){
+                UINT winMmeDeviceId = (UINT)((i==-1) ? WAVE_MAPPER : i);
+                PaWinMmeDeviceInfo *wmmeDeviceInfo = &deviceInfoArray[ (*hostApi)->info.deviceCount ];
+                PaDeviceInfo *deviceInfo = &wmmeDeviceInfo->inheritedDeviceInfo;
+                deviceInfo->structVersion = 2;
+                deviceInfo->hostApi = hostApiIndex;
+
+                deviceInfo->maxInputChannels = 0;
+                deviceInfo->maxOutputChannels = 0;
+
+                deviceInfo->defaultLowInputLatency = defaultLowLatency;
+                deviceInfo->defaultLowOutputLatency = defaultLowLatency;
+                deviceInfo->defaultHighInputLatency = defaultHighLatency;
+                deviceInfo->defaultHighOutputLatency = defaultHighLatency; 
+
+                result = InitializeOutputDeviceInfo( winMmeHostApi, wmmeDeviceInfo,
+                        winMmeDeviceId, &deviceInfoInitializationSucceeded );
+                if( result != paNoError )
+                    goto error;
+
+                if( deviceInfoInitializationSucceeded ){
+                    if( (*hostApi)->info.defaultOutputDevice == paNoDevice )
+                        (*hostApi)->info.defaultOutputDevice = (*hostApi)->info.deviceCount;
+
+                    winMmeHostApi->winMmeDeviceIds[ (*hostApi)->info.deviceCount ] = winMmeDeviceId;
+                    (*hostApi)->deviceInfos[ (*hostApi)->info.deviceCount ] = deviceInfo;
+
+                    winMmeHostApi->outputDeviceCount++;
+                    (*hostApi)->info.deviceCount++;
+                }
+            }
+        }
+    }
+    
+
+    InitializeDefaultDeviceIdsFromEnv( winMmeHostApi );
+
+    (*hostApi)->Terminate = Terminate;
+    (*hostApi)->OpenStream = OpenStream;
+    (*hostApi)->IsFormatSupported = IsFormatSupported;
+
+    PaUtil_InitializeStreamInterface( &winMmeHostApi->callbackStreamInterface, CloseStream, StartStream,
+                                      StopStream, AbortStream, IsStreamStopped, IsStreamActive,
+                                      GetStreamTime, GetStreamCpuLoad,
+                                      PaUtil_DummyRead, PaUtil_DummyWrite,
+                                      PaUtil_DummyGetReadAvailable, PaUtil_DummyGetWriteAvailable );
+
+    PaUtil_InitializeStreamInterface( &winMmeHostApi->blockingStreamInterface, CloseStream, StartStream,
+                                      StopStream, AbortStream, IsStreamStopped, IsStreamActive,
+                                      GetStreamTime, PaUtil_DummyGetCpuLoad,
+                                      ReadStream, WriteStream, GetStreamReadAvailable, GetStreamWriteAvailable );
+
+    return result;
+
+error:
+    if( winMmeHostApi )
+    {
+        if( winMmeHostApi->allocations )
+        {
+            PaUtil_FreeAllAllocations( winMmeHostApi->allocations );
+            PaUtil_DestroyAllocationGroup( winMmeHostApi->allocations );
+        }
+        
+        PaUtil_FreeMemory( winMmeHostApi );
+    }
+
+    return result;
+}
+
+
+static void Terminate( struct PaUtilHostApiRepresentation *hostApi )
+{
+    PaWinMmeHostApiRepresentation *winMmeHostApi = (PaWinMmeHostApiRepresentation*)hostApi;
+
+    if( winMmeHostApi->allocations )
+    {
+        PaUtil_FreeAllAllocations( winMmeHostApi->allocations );
+        PaUtil_DestroyAllocationGroup( winMmeHostApi->allocations );
+    }
+
+    PaUtil_FreeMemory( winMmeHostApi );
+}
+
+
+static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
+                                  const PaStreamParameters *inputParameters,
+                                  const PaStreamParameters *outputParameters,
+                                  double sampleRate )
+{
+    PaWinMmeHostApiRepresentation *winMmeHostApi = (PaWinMmeHostApiRepresentation*)hostApi;
+    PaDeviceInfo *inputDeviceInfo, *outputDeviceInfo;
+    int inputChannelCount, outputChannelCount;
+    int inputMultipleDeviceChannelCount, outputMultipleDeviceChannelCount;
+    PaSampleFormat inputSampleFormat, outputSampleFormat;
+    PaWinMmeStreamInfo *inputStreamInfo, *outputStreamInfo;
+    UINT winMmeInputDeviceId, winMmeOutputDeviceId;
+    unsigned int i;
+    PaError paerror;
+
+    /* The calls to QueryFormatSupported below are intended to detect invalid
+        sample rates. If we assume that the channel count and format are OK,
+        then the only thing that could fail is the sample rate. This isn't
+        strictly true, but I can't think of a better way to test that the
+        sample rate is valid.
+    */  
+    
+    if( inputParameters )
+    {
+        inputChannelCount = inputParameters->channelCount;
+        inputSampleFormat = inputParameters->sampleFormat;
+        inputStreamInfo = inputParameters->hostApiSpecificStreamInfo;
+        
+        /* all standard sample formats are supported by the buffer adapter,
+             this implementation doesn't support any custom sample formats */
+        if( inputSampleFormat & paCustomFormat )
+            return paSampleFormatNotSupported;
+
+        if( inputParameters->device == paUseHostApiSpecificDeviceSpecification
+                && inputStreamInfo && (inputStreamInfo->flags & paWinMmeUseMultipleDevices) )
+        {
+            inputMultipleDeviceChannelCount = 0;
+            for( i=0; i< inputStreamInfo->deviceCount; ++i )
+            {
+                inputMultipleDeviceChannelCount += inputStreamInfo->devices[i].channelCount;
+                    
+                inputDeviceInfo = hostApi->deviceInfos[ inputStreamInfo->devices[i].device ];
+
+                /* check that input device can support inputChannelCount */
+                if( inputStreamInfo->devices[i].channelCount <= 0
+                        || inputStreamInfo->devices[i].channelCount > inputDeviceInfo->maxInputChannels )
+                    return paInvalidChannelCount;
+
+                /* test for valid sample rate, see comment above */
+                winMmeInputDeviceId = LocalDeviceIndexToWinMmeDeviceId( winMmeHostApi, inputStreamInfo->devices[i].device );
+                paerror = QueryFormatSupported( inputDeviceInfo, QueryInputWaveFormatEx, winMmeInputDeviceId, inputStreamInfo->devices[i].channelCount, sampleRate );
+                if( paerror != paNoError )
+                    return paInvalidSampleRate;
+            }
+                
+            if( inputMultipleDeviceChannelCount != inputChannelCount )
+                return paIncompatibleHostApiSpecificStreamInfo;                  
+        }
+        else
+        {
+            if( inputStreamInfo && (inputStreamInfo->flags & paWinMmeUseMultipleDevices) )
+                return paIncompatibleHostApiSpecificStreamInfo; /* paUseHostApiSpecificDeviceSpecification was not supplied as the input device */
+
+            inputDeviceInfo = hostApi->deviceInfos[ inputParameters->device ];
+
+            /* check that input device can support inputChannelCount */
+            if( inputChannelCount > inputDeviceInfo->maxInputChannels )
+                return paInvalidChannelCount;
+
+            /* test for valid sample rate, see comment above */
+            winMmeInputDeviceId = LocalDeviceIndexToWinMmeDeviceId( winMmeHostApi, inputParameters->device );
+            paerror = QueryFormatSupported( inputDeviceInfo, QueryInputWaveFormatEx, winMmeInputDeviceId, inputChannelCount, sampleRate );
+            if( paerror != paNoError )
+                return paInvalidSampleRate;
+        }
+    }
+
+    if( outputParameters )
+    {
+        outputChannelCount = outputParameters->channelCount;
+        outputSampleFormat = outputParameters->sampleFormat;
+        outputStreamInfo = outputParameters->hostApiSpecificStreamInfo;
+
+        /* all standard sample formats are supported by the buffer adapter,
+            this implementation doesn't support any custom sample formats */
+        if( outputSampleFormat & paCustomFormat )
+            return paSampleFormatNotSupported;
+
+        if( outputParameters->device == paUseHostApiSpecificDeviceSpecification
+                && outputStreamInfo && (outputStreamInfo->flags & paWinMmeUseMultipleDevices) )
+        {
+            outputMultipleDeviceChannelCount = 0;
+            for( i=0; i< outputStreamInfo->deviceCount; ++i )
+            {
+                outputMultipleDeviceChannelCount += outputStreamInfo->devices[i].channelCount;
+                    
+                outputDeviceInfo = hostApi->deviceInfos[ outputStreamInfo->devices[i].device ];
+
+                /* check that output device can support outputChannelCount */
+                if( outputStreamInfo->devices[i].channelCount <= 0
+                        || outputStreamInfo->devices[i].channelCount > outputDeviceInfo->maxOutputChannels )
+                    return paInvalidChannelCount;
+
+                /* test for valid sample rate, see comment above */
+                winMmeOutputDeviceId = LocalDeviceIndexToWinMmeDeviceId( winMmeHostApi, outputStreamInfo->devices[i].device );
+                paerror = QueryFormatSupported( outputDeviceInfo, QueryOutputWaveFormatEx, winMmeOutputDeviceId, outputStreamInfo->devices[i].channelCount, sampleRate );
+                if( paerror != paNoError )
+                    return paInvalidSampleRate;
+            }
+                
+            if( outputMultipleDeviceChannelCount != outputChannelCount )
+                return paIncompatibleHostApiSpecificStreamInfo;            
+        }
+        else
+        {
+            if( outputStreamInfo && (outputStreamInfo->flags & paWinMmeUseMultipleDevices) )
+                return paIncompatibleHostApiSpecificStreamInfo; /* paUseHostApiSpecificDeviceSpecification was not supplied as the output device */
+
+            outputDeviceInfo = hostApi->deviceInfos[ outputParameters->device ];
+
+            /* check that output device can support outputChannelCount */
+            if( outputChannelCount > outputDeviceInfo->maxOutputChannels )
+                return paInvalidChannelCount;
+
+            /* test for valid sample rate, see comment above */
+            winMmeOutputDeviceId = LocalDeviceIndexToWinMmeDeviceId( winMmeHostApi, outputParameters->device );
+            paerror = QueryFormatSupported( outputDeviceInfo, QueryOutputWaveFormatEx, winMmeOutputDeviceId, outputChannelCount, sampleRate );
+            if( paerror != paNoError )
+                return paInvalidSampleRate;
+        }
+    }
+    
+    /*
+            - if a full duplex stream is requested, check that the combination
+                of input and output parameters is supported
+
+            - check that the device supports sampleRate
+
+            for mme all we can do is test that the input and output devices
+            support the requested sample rate and number of channels. we
+            cannot test for full duplex compatibility.
+    */                                             
+
+    return paFormatIsSupported;
+}
+
+
+
+static void SelectBufferSizeAndCount( unsigned long baseBufferSize,
+    unsigned long requestedLatency,
+    unsigned long baseBufferCount, unsigned long minimumBufferCount,
+    unsigned long maximumBufferSize, unsigned long *hostBufferSize,
+    unsigned long *hostBufferCount )
+{
+    unsigned long sizeMultiplier, bufferCount, latency;
+    unsigned long nextLatency, nextBufferSize;
+    int baseBufferSizeIsPowerOfTwo;
+    
+    sizeMultiplier = 1;
+    bufferCount = baseBufferCount;
+
+    /* count-1 below because latency is always determined by one less
+        than the total number of buffers.
+    */
+    latency = (baseBufferSize * sizeMultiplier) * (bufferCount-1);
+
+    if( latency > requestedLatency )
+    {
+
+        /* reduce number of buffers without falling below suggested latency */
+
+        nextLatency = (baseBufferSize * sizeMultiplier) * (bufferCount-2);
+        while( bufferCount > minimumBufferCount && nextLatency >= requestedLatency )
+        {
+            --bufferCount;
+            nextLatency = (baseBufferSize * sizeMultiplier) * (bufferCount-2);
+        }
+
+    }else if( latency < requestedLatency ){
+
+        baseBufferSizeIsPowerOfTwo = (! (baseBufferSize & (baseBufferSize - 1)));
+        if( baseBufferSizeIsPowerOfTwo ){
+
+            /* double size of buffers without exceeding requestedLatency */
+
+            nextBufferSize = (baseBufferSize * (sizeMultiplier*2));
+            nextLatency = nextBufferSize * (bufferCount-1);
+            while( nextBufferSize <= maximumBufferSize
+                    && nextLatency < requestedLatency )
+            {
+                sizeMultiplier *= 2;
+                nextBufferSize = (baseBufferSize * (sizeMultiplier*2));
+                nextLatency = nextBufferSize * (bufferCount-1);
+            }   
+
+        }else{
+
+            /* increase size of buffers upto first excess of requestedLatency */
+
+            nextBufferSize = (baseBufferSize * (sizeMultiplier+1));
+            nextLatency = nextBufferSize * (bufferCount-1);
+            while( nextBufferSize <= maximumBufferSize
+                    && nextLatency < requestedLatency )
+            {
+                ++sizeMultiplier;
+                nextBufferSize = (baseBufferSize * (sizeMultiplier+1));
+                nextLatency = nextBufferSize * (bufferCount-1);
+            }
+
+            if( nextLatency < requestedLatency )
+                ++sizeMultiplier;            
+        }
+
+        /* increase number of buffers until requestedLatency is reached */
+
+        latency = (baseBufferSize * sizeMultiplier) * (bufferCount-1);
+        while( latency < requestedLatency )
+        {
+            ++bufferCount;
+            latency = (baseBufferSize * sizeMultiplier) * (bufferCount-1);
+        }
+    }
+
+    *hostBufferSize = baseBufferSize * sizeMultiplier;
+    *hostBufferCount = bufferCount;
+}
+
+
+static void ReselectBufferCount( unsigned long bufferSize,
+    unsigned long requestedLatency,
+    unsigned long baseBufferCount, unsigned long minimumBufferCount,
+    unsigned long *hostBufferCount )
+{
+    unsigned long bufferCount, latency;
+    unsigned long nextLatency;
+
+    bufferCount = baseBufferCount;
+
+    /* count-1 below because latency is always determined by one less
+        than the total number of buffers.
+    */
+    latency = bufferSize * (bufferCount-1);
+
+    if( latency > requestedLatency )
+    {
+        /* reduce number of buffers without falling below suggested latency */
+
+        nextLatency = bufferSize * (bufferCount-2);
+        while( bufferCount > minimumBufferCount && nextLatency >= requestedLatency )
+        {
+            --bufferCount;
+            nextLatency = bufferSize * (bufferCount-2);
+        }
+
+    }else if( latency < requestedLatency ){
+
+        /* increase number of buffers until requestedLatency is reached */
+
+        latency = bufferSize * (bufferCount-1);
+        while( latency < requestedLatency )
+        {
+            ++bufferCount;
+            latency = bufferSize * (bufferCount-1);
+        }                                                         
+    }
+
+    *hostBufferCount = bufferCount;
+}
+
+
+/* CalculateBufferSettings() fills the framesPerHostInputBuffer, hostInputBufferCount,
+   framesPerHostOutputBuffer and hostOutputBufferCount parameters based on the values
+   of the other parameters.
+*/
+
+static PaError CalculateBufferSettings(
+        unsigned long *framesPerHostInputBuffer, unsigned long *hostInputBufferCount,
+        unsigned long *framesPerHostOutputBuffer, unsigned long *hostOutputBufferCount,
+        int inputChannelCount, PaSampleFormat hostInputSampleFormat,
+        PaTime suggestedInputLatency, PaWinMmeStreamInfo *inputStreamInfo,
+        int outputChannelCount, PaSampleFormat hostOutputSampleFormat,
+        PaTime suggestedOutputLatency, PaWinMmeStreamInfo *outputStreamInfo,
+        double sampleRate, unsigned long framesPerBuffer )
+{
+    PaError result = paNoError;
+    int effectiveInputChannelCount, effectiveOutputChannelCount;
+    int hostInputFrameSize = 0;
+    unsigned int i;
+    
+    if( inputChannelCount > 0 )
+    {
+        int hostInputSampleSize = Pa_GetSampleSize( hostInputSampleFormat );
+        if( hostInputSampleSize < 0 )
+        {
+            result = hostInputSampleSize;
+            goto error;
+        }
+
+        if( inputStreamInfo
+                && ( inputStreamInfo->flags & paWinMmeUseMultipleDevices ) )
+        {
+            /* set effectiveInputChannelCount to the largest number of
+                channels on any one device.
+            */
+            effectiveInputChannelCount = 0;
+            for( i=0; i< inputStreamInfo->deviceCount; ++i )
+            {
+                if( inputStreamInfo->devices[i].channelCount > effectiveInputChannelCount )
+                    effectiveInputChannelCount = inputStreamInfo->devices[i].channelCount;
+            }
+        }
+        else
+        {
+            effectiveInputChannelCount = inputChannelCount;
+        }
+
+        hostInputFrameSize = hostInputSampleSize * effectiveInputChannelCount;
+
+        if( inputStreamInfo
+                && ( inputStreamInfo->flags & paWinMmeUseLowLevelLatencyParameters ) )
+        {
+            if( inputStreamInfo->bufferCount <= 0
+                    || inputStreamInfo->framesPerBuffer <= 0 )
+            {
+                result = paIncompatibleHostApiSpecificStreamInfo;
+                goto error;
+            }
+
+            *framesPerHostInputBuffer = inputStreamInfo->framesPerBuffer;
+            *hostInputBufferCount = inputStreamInfo->bufferCount;
+        }
+        else
+        {
+            unsigned long hostBufferSizeBytes, hostBufferCount;
+            unsigned long minimumBufferCount = (outputChannelCount > 0)
+                    ? PA_MME_MIN_HOST_INPUT_BUFFER_COUNT_FULL_DUPLEX_
+                    : PA_MME_MIN_HOST_INPUT_BUFFER_COUNT_HALF_DUPLEX_;
+
+            unsigned long maximumBufferSize = (long) ((PA_MME_MAX_HOST_BUFFER_SECS_ * sampleRate) * hostInputFrameSize);
+            if( maximumBufferSize > PA_MME_MAX_HOST_BUFFER_BYTES_ )
+                maximumBufferSize = PA_MME_MAX_HOST_BUFFER_BYTES_;
+
+            /* compute the following in bytes, then convert back to frames */
+
+            SelectBufferSizeAndCount(
+                ((framesPerBuffer == paFramesPerBufferUnspecified)
+                    ? PA_MME_MIN_HOST_BUFFER_FRAMES_WHEN_UNSPECIFIED_
+                    : framesPerBuffer ) * hostInputFrameSize, /* baseBufferSize */
+                ((unsigned long)(suggestedInputLatency * sampleRate)) * hostInputFrameSize, /* suggestedLatency */
+                4, /* baseBufferCount */
+                minimumBufferCount, maximumBufferSize,
+                &hostBufferSizeBytes, &hostBufferCount );
+
+            *framesPerHostInputBuffer = hostBufferSizeBytes / hostInputFrameSize;
+            *hostInputBufferCount = hostBufferCount;
+        }
+    }
+    else
+    {
+        *framesPerHostInputBuffer = 0;
+        *hostInputBufferCount = 0;
+    }
+
+    if( outputChannelCount > 0 )
+    {
+        if( outputStreamInfo
+                && ( outputStreamInfo->flags & paWinMmeUseLowLevelLatencyParameters ) )
+        {
+            if( outputStreamInfo->bufferCount <= 0
+                    || outputStreamInfo->framesPerBuffer <= 0 )
+            {
+                result = paIncompatibleHostApiSpecificStreamInfo;
+                goto error;
+            }
+
+            *framesPerHostOutputBuffer = outputStreamInfo->framesPerBuffer;
+            *hostOutputBufferCount = outputStreamInfo->bufferCount;
+
+            
+            if( inputChannelCount > 0 ) /* full duplex */
+            {
+                if( *framesPerHostInputBuffer != *framesPerHostOutputBuffer )
+                {
+                    if( inputStreamInfo
+                            && ( inputStreamInfo->flags & paWinMmeUseLowLevelLatencyParameters ) )
+                    { 
+                        /* a custom StreamInfo was used for specifying both input
+                            and output buffer sizes, the larger buffer size
+                            must be a multiple of the smaller buffer size */
+
+                        if( *framesPerHostInputBuffer < *framesPerHostOutputBuffer )
+                        {
+                            if( *framesPerHostOutputBuffer % *framesPerHostInputBuffer != 0 )
+                            {
+                                result = paIncompatibleHostApiSpecificStreamInfo;
+                                goto error;
+                            }
+                        }
+                        else
+                        {
+                            assert( *framesPerHostInputBuffer > *framesPerHostOutputBuffer );
+                            if( *framesPerHostInputBuffer % *framesPerHostOutputBuffer != 0 )
+                            {
+                                result = paIncompatibleHostApiSpecificStreamInfo;
+                                goto error;
+                            }
+                        }                        
+                    }
+                    else
+                    {
+                        /* a custom StreamInfo was not used for specifying the input buffer size,
+                            so use the output buffer size, and approximately the same latency. */
+
+                        *framesPerHostInputBuffer = *framesPerHostOutputBuffer;
+                        *hostInputBufferCount = (((unsigned long)(suggestedInputLatency * sampleRate)) / *framesPerHostInputBuffer) + 1;
+
+                        if( *hostInputBufferCount < PA_MME_MIN_HOST_INPUT_BUFFER_COUNT_FULL_DUPLEX_ )
+                            *hostInputBufferCount = PA_MME_MIN_HOST_INPUT_BUFFER_COUNT_FULL_DUPLEX_;
+                    }
+                }
+            }
+        }
+        else
+        {
+            unsigned long hostBufferSizeBytes, hostBufferCount;
+            unsigned long minimumBufferCount = PA_MME_MIN_HOST_OUTPUT_BUFFER_COUNT_;
+            unsigned long maximumBufferSize;
+            int hostOutputFrameSize;
+            int hostOutputSampleSize;
+
+            hostOutputSampleSize = Pa_GetSampleSize( hostOutputSampleFormat );
+            if( hostOutputSampleSize < 0 )
+            {
+                result = hostOutputSampleSize;
+                goto error;
+            }
+
+            if( outputStreamInfo
+                && ( outputStreamInfo->flags & paWinMmeUseMultipleDevices ) )
+            {
+                /* set effectiveOutputChannelCount to the largest number of
+                    channels on any one device.
+                */
+                effectiveOutputChannelCount = 0;
+                for( i=0; i< outputStreamInfo->deviceCount; ++i )
+                {
+                    if( outputStreamInfo->devices[i].channelCount > effectiveOutputChannelCount )
+                        effectiveOutputChannelCount = outputStreamInfo->devices[i].channelCount;
+                }
+            }
+            else
+            {
+                effectiveOutputChannelCount = outputChannelCount;
+            }
+
+            hostOutputFrameSize = hostOutputSampleSize * effectiveOutputChannelCount;
+            
+            maximumBufferSize = (long) ((PA_MME_MAX_HOST_BUFFER_SECS_ * sampleRate) * hostOutputFrameSize);
+            if( maximumBufferSize > PA_MME_MAX_HOST_BUFFER_BYTES_ )
+                maximumBufferSize = PA_MME_MAX_HOST_BUFFER_BYTES_;
+
+
+            /* compute the following in bytes, then convert back to frames */
+
+            SelectBufferSizeAndCount(
+                ((framesPerBuffer == paFramesPerBufferUnspecified)
+                    ? PA_MME_MIN_HOST_BUFFER_FRAMES_WHEN_UNSPECIFIED_
+                    : framesPerBuffer ) * hostOutputFrameSize, /* baseBufferSize */
+                ((unsigned long)(suggestedOutputLatency * sampleRate)) * hostOutputFrameSize, /* suggestedLatency */
+                4, /* baseBufferCount */
+                minimumBufferCount,
+                maximumBufferSize,
+                &hostBufferSizeBytes, &hostBufferCount );
+
+            *framesPerHostOutputBuffer = hostBufferSizeBytes / hostOutputFrameSize;
+            *hostOutputBufferCount = hostBufferCount;
+
+
+            if( inputChannelCount > 0 )
+            {
+                /* ensure that both input and output buffer sizes are the same.
+                    if they don't match at this stage, choose the smallest one
+                    and use that for input and output
+                */
+
+                if( *framesPerHostOutputBuffer != *framesPerHostInputBuffer )
+                {
+                    if( framesPerHostInputBuffer < framesPerHostOutputBuffer )
+                    {
+                        unsigned long framesPerHostBuffer = *framesPerHostInputBuffer;
+                        
+                        minimumBufferCount = PA_MME_MIN_HOST_OUTPUT_BUFFER_COUNT_;
+                        ReselectBufferCount(
+                            framesPerHostBuffer * hostOutputFrameSize, /* bufferSize */
+                            ((unsigned long)(suggestedOutputLatency * sampleRate)) * hostOutputFrameSize, /* suggestedLatency */
+                            4, /* baseBufferCount */
+                            minimumBufferCount,
+                            &hostBufferCount );
+
+                        *framesPerHostOutputBuffer = framesPerHostBuffer;
+                        *hostOutputBufferCount = hostBufferCount;
+                    }
+                    else
+                    {
+                        unsigned long framesPerHostBuffer = *framesPerHostOutputBuffer;
+                        
+                        minimumBufferCount = PA_MME_MIN_HOST_INPUT_BUFFER_COUNT_FULL_DUPLEX_;
+                        ReselectBufferCount(
+                            framesPerHostBuffer * hostInputFrameSize, /* bufferSize */
+                            ((unsigned long)(suggestedInputLatency * sampleRate)) * hostInputFrameSize, /* suggestedLatency */
+                            4, /* baseBufferCount */
+                            minimumBufferCount,
+                            &hostBufferCount );
+
+                        *framesPerHostInputBuffer = framesPerHostBuffer;
+                        *hostInputBufferCount = hostBufferCount;
+                    }
+                }   
+            }
+        }
+    }
+    else
+    {
+        *framesPerHostOutputBuffer = 0;
+        *hostOutputBufferCount = 0;
+    }
+
+error:
+    return result;
+}
+
+
+typedef struct
+{
+    HANDLE bufferEvent;
+    void *waveHandles;
+    unsigned int deviceCount;
+    /* unsigned int channelCount; */
+    WAVEHDR **waveHeaders;                  /* waveHeaders[device][buffer] */
+    unsigned int bufferCount;
+    unsigned int currentBufferIndex;
+    unsigned int framesPerBuffer;
+    unsigned int framesUsedInCurrentBuffer;
+}PaWinMmeSingleDirectionHandlesAndBuffers;
+
+/* prototypes for functions operating on PaWinMmeSingleDirectionHandlesAndBuffers */
+
+static void InitializeSingleDirectionHandlesAndBuffers( PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers );
+static PaError InitializeWaveHandles( PaWinMmeHostApiRepresentation *winMmeHostApi,
+        PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers,
+        unsigned long bytesPerHostSample,
+        double sampleRate, PaWinMmeDeviceAndChannelCount *devices,
+        unsigned int deviceCount, int isInput );
+static PaError TerminateWaveHandles( PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers, int isInput, int currentlyProcessingAnError );
+static PaError InitializeWaveHeaders( PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers,
+        unsigned long hostBufferCount,
+        PaSampleFormat hostSampleFormat,
+        unsigned long framesPerHostBuffer,
+        PaWinMmeDeviceAndChannelCount *devices,
+        int isInput );
+static void TerminateWaveHeaders( PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers, int isInput );
+
+
+static void InitializeSingleDirectionHandlesAndBuffers( PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers )
+{
+    handlesAndBuffers->bufferEvent = 0;
+    handlesAndBuffers->waveHandles = 0;
+    handlesAndBuffers->deviceCount = 0;
+    handlesAndBuffers->waveHeaders = 0;
+    handlesAndBuffers->bufferCount = 0;
+}    
+
+static PaError InitializeWaveHandles( PaWinMmeHostApiRepresentation *winMmeHostApi,
+        PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers,
+        unsigned long bytesPerHostSample,
+        double sampleRate, PaWinMmeDeviceAndChannelCount *devices,
+        unsigned int deviceCount, int isInput )
+{
+    PaError result;
+    MMRESULT mmresult;
+    unsigned long bytesPerFrame;
+    WAVEFORMATEX wfx;
+    signed int i;
+
+    /* for error cleanup we expect that InitializeSingleDirectionHandlesAndBuffers()
+        has already been called to zero some fields */       
+
+    result = CreateEventWithPaError( &handlesAndBuffers->bufferEvent, NULL, FALSE, FALSE, NULL );
+    if( result != paNoError ) goto error;
+
+    if( isInput )
+        handlesAndBuffers->waveHandles = (void*)PaUtil_AllocateMemory( sizeof(HWAVEIN) * deviceCount );
+    else
+        handlesAndBuffers->waveHandles = (void*)PaUtil_AllocateMemory( sizeof(HWAVEOUT) * deviceCount );
+    if( !handlesAndBuffers->waveHandles )
+    {
+        result = paInsufficientMemory;
+        goto error;
+    }
+
+    handlesAndBuffers->deviceCount = deviceCount;
+
+    for( i = 0; i < (signed int)deviceCount; ++i )
+    {
+        if( isInput )
+            ((HWAVEIN*)handlesAndBuffers->waveHandles)[i] = 0;
+        else
+            ((HWAVEOUT*)handlesAndBuffers->waveHandles)[i] = 0;
+    }
+
+    wfx.wFormatTag = WAVE_FORMAT_PCM;
+    wfx.nSamplesPerSec = (DWORD) sampleRate;
+    wfx.cbSize = 0;
+    
+    for( i = 0; i < (signed int)deviceCount; ++i )
+    {
+        UINT winMmeDeviceId;
+
+        winMmeDeviceId = LocalDeviceIndexToWinMmeDeviceId( winMmeHostApi, devices[i].device );
+        wfx.nChannels = (WORD)devices[i].channelCount;
+
+        bytesPerFrame = wfx.nChannels * bytesPerHostSample;
+
+        wfx.nAvgBytesPerSec = (DWORD)(bytesPerFrame * sampleRate);
+        wfx.nBlockAlign = (WORD)bytesPerFrame;
+        wfx.wBitsPerSample = (WORD)((bytesPerFrame/wfx.nChannels) * 8);
+
+        /* REVIEW: consider not firing an event for input when a full duplex
+            stream is being used. this would probably depend on the
+            neverDropInput flag. */
+
+        if( isInput )
+            mmresult = waveInOpen( &((HWAVEIN*)handlesAndBuffers->waveHandles)[i], winMmeDeviceId, &wfx,
+                               (DWORD)handlesAndBuffers->bufferEvent, (DWORD)0, CALLBACK_EVENT );
+        else
+            mmresult = waveOutOpen( &((HWAVEOUT*)handlesAndBuffers->waveHandles)[i], winMmeDeviceId, &wfx,
+                                (DWORD)handlesAndBuffers->bufferEvent, (DWORD)0, CALLBACK_EVENT );
+
+        if( mmresult != MMSYSERR_NOERROR )
+        {
+            switch( mmresult )
+            {
+                case MMSYSERR_ALLOCATED:    /* Specified resource is already allocated. */
+                    result = paDeviceUnavailable;
+                    break;
+                case MMSYSERR_NODRIVER:	    /* No device driver is present. */
+                    result = paDeviceUnavailable;
+                    break;
+                case MMSYSERR_NOMEM:	    /* Unable to allocate or lock memory. */
+                    result = paInsufficientMemory;
+                    break;
+
+                case MMSYSERR_BADDEVICEID:	/* Specified device identifier is out of range. */
+                    /* falls through */
+                case WAVERR_BADFORMAT:      /* Attempted to open with an unsupported waveform-audio format. */
+                    /* falls through */
+                default:
+                    result = paUnanticipatedHostError;
+                    if( isInput )
+                    {
+                        PA_MME_SET_LAST_WAVEIN_ERROR( mmresult );
+                    }
+                    else
+                    {
+                        PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult );
+                    }
+            }
+            goto error;
+        }
+    }
+
+    return result;
+
+error:
+    TerminateWaveHandles( handlesAndBuffers, isInput, 1 /* currentlyProcessingAnError */ );
+
+    return result;
+}
+
+
+static PaError TerminateWaveHandles( PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers, int isInput, int currentlyProcessingAnError )
+{
+    PaError result = paNoError;
+    MMRESULT mmresult;
+    signed int i;
+    
+    if( handlesAndBuffers->waveHandles )
+    {
+        for( i = handlesAndBuffers->deviceCount-1; i >= 0; --i )
+        {
+            if( isInput )
+            {
+                if( ((HWAVEIN*)handlesAndBuffers->waveHandles)[i] )
+                    mmresult = waveInClose( ((HWAVEIN*)handlesAndBuffers->waveHandles)[i] );
+            }
+            else
+            {
+                if( ((HWAVEOUT*)handlesAndBuffers->waveHandles)[i] )
+                    mmresult = waveOutClose( ((HWAVEOUT*)handlesAndBuffers->waveHandles)[i] );
+            }
+
+            if( mmresult != MMSYSERR_NOERROR &&
+                !currentlyProcessingAnError ) /* don't update the error state if we're already processing an error */
+            {
+                result = paUnanticipatedHostError;
+                if( isInput )
+                {
+                    PA_MME_SET_LAST_WAVEIN_ERROR( mmresult );
+                }
+                else
+                {
+                    PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult );
+                }
+                /* note that we don't break here, we try to continue closing devices */
+            }
+        }
+
+        PaUtil_FreeMemory( handlesAndBuffers->waveHandles );
+        handlesAndBuffers->waveHandles = 0;
+    }
+
+    if( handlesAndBuffers->bufferEvent )
+    {
+        result = CloseHandleWithPaError( handlesAndBuffers->bufferEvent );
+        handlesAndBuffers->bufferEvent = 0;
+    }
+    
+    return result;
+}
+
+
+static PaError InitializeWaveHeaders( PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers,
+        unsigned long hostBufferCount,
+        PaSampleFormat hostSampleFormat,
+        unsigned long framesPerHostBuffer,
+        PaWinMmeDeviceAndChannelCount *devices,
+        int isInput )
+{
+    PaError result = paNoError;
+    MMRESULT mmresult;
+    WAVEHDR *deviceWaveHeaders;
+    signed int i, j;
+
+    /* for error cleanup we expect that InitializeSingleDirectionHandlesAndBuffers()
+        has already been called to zero some fields */
+        
+
+    /* allocate an array of pointers to arrays of wave headers, one array of
+        wave headers per device */
+    handlesAndBuffers->waveHeaders = (WAVEHDR**)PaUtil_AllocateMemory( sizeof(WAVEHDR*) * handlesAndBuffers->deviceCount );
+    if( !handlesAndBuffers->waveHeaders )
+    {
+        result = paInsufficientMemory;
+        goto error;
+    }
+    
+    for( i = 0; i < (signed int)handlesAndBuffers->deviceCount; ++i )
+        handlesAndBuffers->waveHeaders[i] = 0;
+
+    handlesAndBuffers->bufferCount = hostBufferCount;
+
+    for( i = 0; i < (signed int)handlesAndBuffers->deviceCount; ++i )
+    {
+        int bufferBytes = Pa_GetSampleSize( hostSampleFormat ) *
+                framesPerHostBuffer * devices[i].channelCount;
+        if( bufferBytes < 0 )
+        {
+            result = paInternalError;
+            goto error;
+        }
+
+        /* Allocate an array of wave headers for device i */
+        deviceWaveHeaders = (WAVEHDR *) PaUtil_AllocateMemory( sizeof(WAVEHDR)*hostBufferCount );
+        if( !deviceWaveHeaders )
+        {
+            result = paInsufficientMemory;
+            goto error;
+        }
+
+        for( j=0; j < (signed int)hostBufferCount; ++j )
+            deviceWaveHeaders[j].lpData = 0;
+
+        handlesAndBuffers->waveHeaders[i] = deviceWaveHeaders;
+
+        /* Allocate a buffer for each wave header */
+        for( j=0; j < (signed int)hostBufferCount; ++j )
+        {
+            deviceWaveHeaders[j].lpData = (char *)PaUtil_AllocateMemory( bufferBytes );
+            if( !deviceWaveHeaders[j].lpData )
+            {
+                result = paInsufficientMemory;
+                goto error;
+            }
+            deviceWaveHeaders[j].dwBufferLength = bufferBytes;
+            deviceWaveHeaders[j].dwUser = 0xFFFFFFFF; /* indicates that *PrepareHeader() has not yet been called, for error clean up code */
+
+            if( isInput )
+            {
+                mmresult = waveInPrepareHeader( ((HWAVEIN*)handlesAndBuffers->waveHandles)[i], &deviceWaveHeaders[j], sizeof(WAVEHDR) );
+                if( mmresult != MMSYSERR_NOERROR )
+                {
+                    result = paUnanticipatedHostError;
+                    PA_MME_SET_LAST_WAVEIN_ERROR( mmresult );
+                    goto error;
+                }
+            }
+            else /* output */
+            {
+                mmresult = waveOutPrepareHeader( ((HWAVEOUT*)handlesAndBuffers->waveHandles)[i], &deviceWaveHeaders[j], sizeof(WAVEHDR) );
+                if( mmresult != MMSYSERR_NOERROR )
+                {
+                    result = paUnanticipatedHostError;
+                    PA_MME_SET_LAST_WAVEIN_ERROR( mmresult );
+                    goto error;
+                }
+            }
+            deviceWaveHeaders[j].dwUser = devices[i].channelCount;
+        }
+    }
+
+    return result;
+
+error:
+    TerminateWaveHeaders( handlesAndBuffers, isInput );
+    
+    return result;
+}
+
+
+static void TerminateWaveHeaders( PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers, int isInput )
+{
+    signed int i, j;
+    WAVEHDR *deviceWaveHeaders;
+    
+    if( handlesAndBuffers->waveHeaders )
+    {
+        for( i = handlesAndBuffers->deviceCount-1; i >= 0 ; --i )
+        {
+            deviceWaveHeaders = handlesAndBuffers->waveHeaders[i];  /* wave headers for device i */
+            if( deviceWaveHeaders )
+            {
+                for( j = handlesAndBuffers->bufferCount-1; j >= 0; --j )
+                {
+                    if( deviceWaveHeaders[j].lpData )
+                    {
+                        if( deviceWaveHeaders[j].dwUser != 0xFFFFFFFF )
+                        {
+                            if( isInput )
+                                waveInUnprepareHeader( ((HWAVEIN*)handlesAndBuffers->waveHandles)[i], &deviceWaveHeaders[j], sizeof(WAVEHDR) );
+                            else
+                                waveOutUnprepareHeader( ((HWAVEOUT*)handlesAndBuffers->waveHandles)[i], &deviceWaveHeaders[j], sizeof(WAVEHDR) );
+                        }
+
+                        PaUtil_FreeMemory( deviceWaveHeaders[j].lpData );
+                    }
+                }
+
+                PaUtil_FreeMemory( deviceWaveHeaders );
+            }
+        }
+
+        PaUtil_FreeMemory( handlesAndBuffers->waveHeaders );
+        handlesAndBuffers->waveHeaders = 0;
+    }
+}
+
+
+
+/* PaWinMmeStream - a stream data structure specifically for this implementation */
+/* note that struct PaWinMmeStream is typedeffed to PaWinMmeStream above. */
+struct PaWinMmeStream
+{
+    PaUtilStreamRepresentation streamRepresentation;
+    PaUtilCpuLoadMeasurer cpuLoadMeasurer;
+    PaUtilBufferProcessor bufferProcessor;
+
+    int primeStreamUsingCallback;
+
+    PaWinMmeSingleDirectionHandlesAndBuffers input;
+    PaWinMmeSingleDirectionHandlesAndBuffers output;
+
+    /* Processing thread management -------------- */
+    HANDLE abortEvent;
+    HANDLE processingThread;
+    DWORD processingThreadId;
+
+    char throttleProcessingThreadOnOverload; /* 0 -> don't throtte, non-0 -> throttle */
+    int processingThreadPriority;
+    int highThreadPriority;
+    int throttledThreadPriority;
+    unsigned long throttledSleepMsecs;
+
+    int isStopped;
+    volatile int isActive;
+    volatile int stopProcessing; /* stop thread once existing buffers have been returned */
+    volatile int abortProcessing; /* stop thread immediately */
+
+    DWORD allBuffersDurationMs; /* used to calculate timeouts */
+};
+
+/* updates deviceCount if PaWinMmeUseMultipleDevices is used */
+
+static PaError ValidateWinMmeSpecificStreamInfo(
+        const PaStreamParameters *streamParameters,
+        const PaWinMmeStreamInfo *streamInfo,
+        char *throttleProcessingThreadOnOverload,
+        unsigned long *deviceCount )
+{
+	if( streamInfo )
+	{
+	    if( streamInfo->size != sizeof( PaWinMmeStreamInfo )
+	            || streamInfo->version != 1 )
+	    {
+	        return paIncompatibleHostApiSpecificStreamInfo;
+	    }
+
+	    if( streamInfo->flags & paWinMmeDontThrottleOverloadedProcessingThread )
+	        *throttleProcessingThreadOnOverload = 0;
+            
+	    if( streamInfo->flags & paWinMmeUseMultipleDevices )
+	    {
+	        if( streamParameters->device != paUseHostApiSpecificDeviceSpecification )
+	            return paInvalidDevice;
+	
+			*deviceCount = streamInfo->deviceCount;
+		}	
+	}
+
+	return paNoError;
+}
+
+static PaError RetrieveDevicesFromStreamParameters(
+        struct PaUtilHostApiRepresentation *hostApi,
+        const PaStreamParameters *streamParameters,
+        const PaWinMmeStreamInfo *streamInfo,
+        PaWinMmeDeviceAndChannelCount *devices,
+        unsigned long deviceCount )
+{
+    PaError result = paNoError;
+    unsigned int i;
+    int totalChannelCount;
+    PaDeviceIndex hostApiDevice;
+    
+	if( streamInfo && streamInfo->flags & paWinMmeUseMultipleDevices )
+	{
+		totalChannelCount = 0;
+	    for( i=0; i < deviceCount; ++i )
+	    {
+	        /* validate that the device number is within range */
+	        result = PaUtil_DeviceIndexToHostApiDeviceIndex( &hostApiDevice,
+	                        streamInfo->devices[i].device, hostApi );
+	        if( result != paNoError )
+	            return result;
+	        
+	        devices[i].device = hostApiDevice;
+	        devices[i].channelCount = streamInfo->devices[i].channelCount;
+	
+	        totalChannelCount += devices[i].channelCount;
+	    }
+	
+	    if( totalChannelCount != streamParameters->channelCount )
+	    {
+	        /* channelCount must match total channels specified by multiple devices */
+	        return paInvalidChannelCount; /* REVIEW use of this error code */
+	    }
+	}	
+	else
+	{		
+	    devices[0].device = streamParameters->device;
+	    devices[0].channelCount = streamParameters->channelCount;
+	}
+
+    return result;
+}
+
+static PaError ValidateInputChannelCounts(
+        struct PaUtilHostApiRepresentation *hostApi,
+        PaWinMmeDeviceAndChannelCount *devices,
+        unsigned long deviceCount )
+{
+    unsigned int i;
+
+	for( i=0; i < deviceCount; ++i )
+	{
+		if( devices[i].channelCount < 1 || devices[i].channelCount
+					> hostApi->deviceInfos[ devices[i].device ]->maxInputChannels )
+        	return paInvalidChannelCount;
+	}
+
+    return paNoError;
+}
+
+static PaError ValidateOutputChannelCounts(
+        struct PaUtilHostApiRepresentation *hostApi,
+        PaWinMmeDeviceAndChannelCount *devices,
+        unsigned long deviceCount )
+{
+    unsigned int i;
+
+	for( i=0; i < deviceCount; ++i )
+	{
+		if( devices[i].channelCount < 1 || devices[i].channelCount
+					> hostApi->deviceInfos[ devices[i].device ]->maxOutputChannels )
+        	return paInvalidChannelCount;
+	}
+
+    return paNoError;
+}
+
+
+/* the following macros are intended to improve the readability of the following code */
+#define PA_IS_INPUT_STREAM_( stream ) ( stream ->input.waveHandles )
+#define PA_IS_OUTPUT_STREAM_( stream ) ( stream ->output.waveHandles )
+#define PA_IS_FULL_DUPLEX_STREAM_( stream ) ( stream ->input.waveHandles && stream ->output.waveHandles )
+#define PA_IS_HALF_DUPLEX_STREAM_( stream ) ( !(stream ->input.waveHandles && stream ->output.waveHandles) )
+
+static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
+                           PaStream** s,
+                           const PaStreamParameters *inputParameters,
+                           const PaStreamParameters *outputParameters,
+                           double sampleRate,
+                           unsigned long framesPerBuffer,
+                           PaStreamFlags streamFlags,
+                           PaStreamCallback *streamCallback,
+                           void *userData )
+{
+    PaError result;
+    PaWinMmeHostApiRepresentation *winMmeHostApi = (PaWinMmeHostApiRepresentation*)hostApi;
+    PaWinMmeStream *stream = 0;
+    int bufferProcessorIsInitialized = 0;
+    int streamRepresentationIsInitialized = 0;
+    PaSampleFormat hostInputSampleFormat, hostOutputSampleFormat;
+    int inputChannelCount, outputChannelCount;
+    PaSampleFormat inputSampleFormat, outputSampleFormat;
+    double suggestedInputLatency, suggestedOutputLatency;
+    PaWinMmeStreamInfo *inputStreamInfo, *outputStreamInfo;
+    unsigned long framesPerHostInputBuffer;
+    unsigned long hostInputBufferCount;
+    unsigned long framesPerHostOutputBuffer;
+    unsigned long hostOutputBufferCount;
+    unsigned long framesPerBufferProcessorCall;
+    PaWinMmeDeviceAndChannelCount *inputDevices = 0;  /* contains all devices and channel counts as local host api ids, even when PaWinMmeUseMultipleDevices is not used */
+    unsigned long inputDeviceCount = 0;            
+    PaWinMmeDeviceAndChannelCount *outputDevices = 0;
+    unsigned long outputDeviceCount = 0;                /* contains all devices and channel counts as local host api ids, even when PaWinMmeUseMultipleDevices is not used */
+    char throttleProcessingThreadOnOverload = 1;
+
+    
+    if( inputParameters )
+    {
+		inputChannelCount = inputParameters->channelCount;
+        inputSampleFormat = inputParameters->sampleFormat;
+        suggestedInputLatency = inputParameters->suggestedLatency;
+
+      	inputDeviceCount = 1;
+
+		/* validate input hostApiSpecificStreamInfo */
+        inputStreamInfo = (PaWinMmeStreamInfo*)inputParameters->hostApiSpecificStreamInfo;
+		result = ValidateWinMmeSpecificStreamInfo( inputParameters, inputStreamInfo,
+				&throttleProcessingThreadOnOverload,
+				&inputDeviceCount );
+		if( result != paNoError ) return result;
+
+		inputDevices = (PaWinMmeDeviceAndChannelCount*)alloca( sizeof(PaWinMmeDeviceAndChannelCount) * inputDeviceCount );
+        if( !inputDevices ) return paInsufficientMemory;
+
+		result = RetrieveDevicesFromStreamParameters( hostApi, inputParameters, inputStreamInfo, inputDevices, inputDeviceCount );
+		if( result != paNoError ) return result;
+
+		result = ValidateInputChannelCounts( hostApi, inputDevices, inputDeviceCount );
+		if( result != paNoError ) return result;
+
+        hostInputSampleFormat =
+            PaUtil_SelectClosestAvailableFormat( paInt16 /* native formats */, inputSampleFormat );
+	}
+    else
+    {
+        inputChannelCount = 0;
+        inputSampleFormat = 0;
+        suggestedInputLatency = 0.;
+        inputStreamInfo = 0;
+        hostInputSampleFormat = 0;
+    }
+
+
+    if( outputParameters )
+    {
+        outputChannelCount = outputParameters->channelCount;
+        outputSampleFormat = outputParameters->sampleFormat;
+        suggestedOutputLatency = outputParameters->suggestedLatency;
+
+        outputDeviceCount = 1;
+
+		/* validate output hostApiSpecificStreamInfo */
+        outputStreamInfo = (PaWinMmeStreamInfo*)outputParameters->hostApiSpecificStreamInfo;
+		result = ValidateWinMmeSpecificStreamInfo( outputParameters, outputStreamInfo,
+				&throttleProcessingThreadOnOverload,
+				&outputDeviceCount );
+		if( result != paNoError ) return result;
+
+		outputDevices = (PaWinMmeDeviceAndChannelCount*)alloca( sizeof(PaWinMmeDeviceAndChannelCount) * outputDeviceCount );
+        if( !outputDevices ) return paInsufficientMemory;
+
+		result = RetrieveDevicesFromStreamParameters( hostApi, outputParameters, outputStreamInfo, outputDevices, outputDeviceCount );
+		if( result != paNoError ) return result;
+
+		result = ValidateOutputChannelCounts( hostApi, outputDevices, outputDeviceCount );
+		if( result != paNoError ) return result;
+
+        hostOutputSampleFormat =
+            PaUtil_SelectClosestAvailableFormat( paInt16 /* native formats */, outputSampleFormat );
+    }
+    else
+    {
+        outputChannelCount = 0;
+        outputSampleFormat = 0;
+        outputStreamInfo = 0;
+        hostOutputSampleFormat = 0;
+        suggestedOutputLatency = 0.;
+    }
+
+
+    /*
+        IMPLEMENT ME:
+            - alter sampleRate to a close allowable rate if possible / necessary
+    */
+
+
+    /* validate platform specific flags */
+    if( (streamFlags & paPlatformSpecificFlags) != 0 )
+        return paInvalidFlag; /* unexpected platform specific flag */
+
+
+    result = CalculateBufferSettings( &framesPerHostInputBuffer, &hostInputBufferCount,
+                &framesPerHostOutputBuffer, &hostOutputBufferCount,
+                inputChannelCount, hostInputSampleFormat, suggestedInputLatency, inputStreamInfo,
+                outputChannelCount, hostOutputSampleFormat, suggestedOutputLatency, outputStreamInfo,
+                sampleRate, framesPerBuffer );
+    if( result != paNoError ) goto error;
+
+
+    stream = (PaWinMmeStream*)PaUtil_AllocateMemory( sizeof(PaWinMmeStream) );
+    if( !stream )
+    {
+        result = paInsufficientMemory;
+        goto error;
+    }
+
+    InitializeSingleDirectionHandlesAndBuffers( &stream->input );
+    InitializeSingleDirectionHandlesAndBuffers( &stream->output );
+
+    stream->abortEvent = 0;
+    stream->processingThread = 0;
+
+    stream->throttleProcessingThreadOnOverload = throttleProcessingThreadOnOverload;
+
+    PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation,
+                                           ( (streamCallback)
+                                            ? &winMmeHostApi->callbackStreamInterface
+                                            : &winMmeHostApi->blockingStreamInterface ),
+                                           streamCallback, userData );
+    streamRepresentationIsInitialized = 1;
+
+    PaUtil_InitializeCpuLoadMeasurer( &stream->cpuLoadMeasurer, sampleRate );
+
+
+    if( inputParameters && outputParameters ) /* full duplex */
+    {
+        if( framesPerHostInputBuffer < framesPerHostOutputBuffer )
+        {
+            assert( (framesPerHostOutputBuffer % framesPerHostInputBuffer) == 0 ); /* CalculateBufferSettings() should guarantee this condition */
+
+            framesPerBufferProcessorCall = framesPerHostInputBuffer;
+        }
+        else
+        {
+            assert( (framesPerHostInputBuffer % framesPerHostOutputBuffer) == 0 ); /* CalculateBufferSettings() should guarantee this condition */
+            
+            framesPerBufferProcessorCall = framesPerHostOutputBuffer;
+        }
+    }
+    else if( inputParameters )
+    {
+        framesPerBufferProcessorCall = framesPerHostInputBuffer;
+    }
+    else if( outputParameters )
+    {
+        framesPerBufferProcessorCall = framesPerHostOutputBuffer;
+    }
+
+    stream->input.framesPerBuffer = framesPerHostInputBuffer;
+    stream->output.framesPerBuffer = framesPerHostOutputBuffer;
+
+    result =  PaUtil_InitializeBufferProcessor( &stream->bufferProcessor,
+                    inputChannelCount, inputSampleFormat, hostInputSampleFormat,
+                    outputChannelCount, outputSampleFormat, hostOutputSampleFormat,
+                    sampleRate, streamFlags, framesPerBuffer,
+                    framesPerBufferProcessorCall, paUtilFixedHostBufferSize,
+                    streamCallback, userData );
+    if( result != paNoError ) goto error;
+    
+    bufferProcessorIsInitialized = 1;
+
+    stream->streamRepresentation.streamInfo.inputLatency =
+            (double)(PaUtil_GetBufferProcessorInputLatency(&stream->bufferProcessor)
+                +(framesPerHostInputBuffer * (hostInputBufferCount-1))) / sampleRate;
+    stream->streamRepresentation.streamInfo.outputLatency =
+            (double)(PaUtil_GetBufferProcessorOutputLatency(&stream->bufferProcessor)
+                +(framesPerHostOutputBuffer * (hostOutputBufferCount-1))) / sampleRate;
+    stream->streamRepresentation.streamInfo.sampleRate = sampleRate;
+
+    stream->primeStreamUsingCallback = ( (streamFlags&paPrimeOutputBuffersUsingStreamCallback) && streamCallback ) ? 1 : 0;
+
+    /* time to sleep when throttling due to >100% cpu usage.
+        -a quater of a buffer's duration */
+    stream->throttledSleepMsecs =
+            (unsigned long)(stream->bufferProcessor.framesPerHostBuffer *
+             stream->bufferProcessor.samplePeriod * .25 * 1000);
+
+    stream->isStopped = 1;
+    stream->isActive = 0;
+
+
+    /* for maximum compatibility with multi-device multichannel drivers,
+        we first open all devices, then we prepare all buffers, finally
+        we start all devices ( in StartStream() ). teardown in reverse order.
+    */
+
+    if( inputParameters )
+    {
+        result = InitializeWaveHandles( winMmeHostApi, &stream->input,
+                stream->bufferProcessor.bytesPerHostInputSample, sampleRate,
+                inputDevices, inputDeviceCount, 1 /* isInput */ );
+        if( result != paNoError ) goto error;
+    }
+    
+    if( outputParameters )
+    {
+        result = InitializeWaveHandles( winMmeHostApi, &stream->output,
+                stream->bufferProcessor.bytesPerHostOutputSample, sampleRate,
+                outputDevices, outputDeviceCount, 0 /* isInput */ );
+        if( result != paNoError ) goto error;
+    }
+
+    if( inputParameters )
+    {
+        result = InitializeWaveHeaders( &stream->input, hostInputBufferCount,
+                hostInputSampleFormat, framesPerHostInputBuffer, inputDevices, 1 /* isInput */ );
+        if( result != paNoError ) goto error;
+    }
+
+    if( outputParameters )
+    {
+        result = InitializeWaveHeaders( &stream->output, hostOutputBufferCount,
+                hostOutputSampleFormat, framesPerHostOutputBuffer, outputDevices, 0 /* not isInput */ );
+        if( result != paNoError ) goto error;
+
+        stream->allBuffersDurationMs = (DWORD) (1000.0 * (framesPerHostOutputBuffer * stream->output.bufferCount) / sampleRate);
+    }
+    else
+    {
+        stream->allBuffersDurationMs = (DWORD) (1000.0 * (framesPerHostInputBuffer * stream->input.bufferCount) / sampleRate);
+    }
+
+    
+    if( streamCallback )
+    {
+        /* abort event is only needed for callback streams */
+        result = CreateEventWithPaError( &stream->abortEvent, NULL, TRUE, FALSE, NULL );
+        if( result != paNoError ) goto error;
+    }
+
+    *s = (PaStream*)stream;
+
+    return result;
+
+error:
+
+    if( stream )
+    {
+        if( stream->abortEvent )
+            CloseHandle( stream->abortEvent );
+            
+        TerminateWaveHeaders( &stream->output, 0 /* not isInput */ );
+        TerminateWaveHeaders( &stream->input, 1 /* isInput */ );
+
+        TerminateWaveHandles( &stream->output, 0 /* not isInput */, 1 /* currentlyProcessingAnError */ );
+        TerminateWaveHandles( &stream->input, 1 /* isInput */, 1 /* currentlyProcessingAnError */ );
+
+        if( bufferProcessorIsInitialized )
+            PaUtil_TerminateBufferProcessor( &stream->bufferProcessor );
+
+        if( streamRepresentationIsInitialized )
+            PaUtil_TerminateStreamRepresentation( &stream->streamRepresentation );
+
+        PaUtil_FreeMemory( stream );
+    }
+
+    return result;
+}
+
+
+/* return non-zero if all current buffers are done */
+static int BuffersAreDone( WAVEHDR **waveHeaders, unsigned int deviceCount, int bufferIndex )
+{
+    unsigned int i;
+    
+    for( i=0; i < deviceCount; ++i )
+    {
+        if( !(waveHeaders[i][ bufferIndex ].dwFlags & WHDR_DONE) )
+        {
+            return 0;
+        }         
+    }
+
+    return 1;
+}
+
+static int CurrentInputBuffersAreDone( PaWinMmeStream *stream )
+{
+    return BuffersAreDone( stream->input.waveHeaders, stream->input.deviceCount, stream->input.currentBufferIndex );
+}
+
+static int CurrentOutputBuffersAreDone( PaWinMmeStream *stream )
+{
+    return BuffersAreDone( stream->output.waveHeaders, stream->output.deviceCount, stream->output.currentBufferIndex );
+}
+
+
+/* return non-zero if any buffers are queued */
+static int NoBuffersAreQueued( PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers )
+{
+    unsigned int i, j;
+
+    if( handlesAndBuffers->waveHandles )
+    {
+        for( i=0; i < handlesAndBuffers->bufferCount; ++i )
+        {
+            for( j=0; j < handlesAndBuffers->deviceCount; ++j )
+            {
+                if( !( handlesAndBuffers->waveHeaders[ j ][ i ].dwFlags & WHDR_DONE) )
+                {
+                    return 0;
+                }
+            }
+        }
+    }
+
+    return 1;
+}
+
+
+#define PA_CIRCULAR_INCREMENT_( current, max )\
+    ( (((current) + 1) >= (max)) ? (0) : (current+1) )
+
+#define PA_CIRCULAR_DECREMENT_( current, max )\
+    ( ((current) == 0) ? ((max)-1) : (current-1) )
+    
+
+static signed long GetAvailableFrames( PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers )
+{
+    signed long result = 0;
+    unsigned int i;
+    
+    if( BuffersAreDone( handlesAndBuffers->waveHeaders, handlesAndBuffers->deviceCount, handlesAndBuffers->currentBufferIndex ) )
+    {
+        /* we could calculate the following in O(1) if we kept track of the
+            last done buffer */
+        result = handlesAndBuffers->framesPerBuffer - handlesAndBuffers->framesUsedInCurrentBuffer;
+
+        i = PA_CIRCULAR_INCREMENT_( handlesAndBuffers->currentBufferIndex, handlesAndBuffers->bufferCount );
+        while( i != handlesAndBuffers->currentBufferIndex )
+        {
+            if( BuffersAreDone( handlesAndBuffers->waveHeaders, handlesAndBuffers->deviceCount, i ) )
+            {
+                result += handlesAndBuffers->framesPerBuffer;
+                i = PA_CIRCULAR_INCREMENT_( i, handlesAndBuffers->bufferCount );
+            }
+            else
+                break;
+        }
+    }
+
+    return result;
+}
+
+
+static PaError AdvanceToNextInputBuffer( PaWinMmeStream *stream )
+{
+    PaError result = paNoError;
+    MMRESULT mmresult;
+    unsigned int i;
+
+    for( i=0; i < stream->input.deviceCount; ++i )
+    {
+        mmresult = waveInAddBuffer( ((HWAVEIN*)stream->input.waveHandles)[i],
+                                    &stream->input.waveHeaders[i][ stream->input.currentBufferIndex ],
+                                    sizeof(WAVEHDR) );
+        if( mmresult != MMSYSERR_NOERROR )
+        {
+            result = paUnanticipatedHostError;
+            PA_MME_SET_LAST_WAVEIN_ERROR( mmresult );
+        }
+    }
+
+    stream->input.currentBufferIndex =
+            PA_CIRCULAR_INCREMENT_( stream->input.currentBufferIndex, stream->input.bufferCount );
+
+    stream->input.framesUsedInCurrentBuffer = 0;
+
+    return result;
+}
+
+
+static PaError AdvanceToNextOutputBuffer( PaWinMmeStream *stream )
+{
+    PaError result = paNoError;
+    MMRESULT mmresult;
+    unsigned int i;
+
+    for( i=0; i < stream->output.deviceCount; ++i )
+    {
+        mmresult = waveOutWrite( ((HWAVEOUT*)stream->output.waveHandles)[i],
+                                 &stream->output.waveHeaders[i][ stream->output.currentBufferIndex ],
+                                 sizeof(WAVEHDR) );
+        if( mmresult != MMSYSERR_NOERROR )
+        {
+            result = paUnanticipatedHostError;
+            PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult );
+        }
+    }
+
+    stream->output.currentBufferIndex =
+            PA_CIRCULAR_INCREMENT_( stream->output.currentBufferIndex, stream->output.bufferCount );
+
+    stream->output.framesUsedInCurrentBuffer = 0;
+    
+    return result;
+}
+
+
+/* requeue all but the most recent input with the driver. Used for catching
+    up after a total input buffer underrun */
+static PaError CatchUpInputBuffers( PaWinMmeStream *stream )
+{
+    PaError result = paNoError;
+    unsigned int i;
+    
+    for( i=0; i < stream->input.bufferCount - 1; ++i )
+    {
+        result = AdvanceToNextInputBuffer( stream );
+        if( result != paNoError )
+            break;
+    }
+
+    return result;
+}
+
+
+/* take the most recent output and duplicate it to all other output buffers
+    and requeue them. Used for catching up after a total output buffer underrun.
+*/
+static PaError CatchUpOutputBuffers( PaWinMmeStream *stream )
+{
+    PaError result = paNoError;
+    unsigned int i, j;
+    unsigned int previousBufferIndex =
+            PA_CIRCULAR_DECREMENT_( stream->output.currentBufferIndex, stream->output.bufferCount );
+
+    for( i=0; i < stream->output.bufferCount - 1; ++i )
+    {
+        for( j=0; j < stream->output.deviceCount; ++j )
+        {
+            if( stream->output.waveHeaders[j][ stream->output.currentBufferIndex ].lpData
+                    != stream->output.waveHeaders[j][ previousBufferIndex ].lpData )
+            {
+                CopyMemory( stream->output.waveHeaders[j][ stream->output.currentBufferIndex ].lpData,
+                            stream->output.waveHeaders[j][ previousBufferIndex ].lpData,
+                            stream->output.waveHeaders[j][ stream->output.currentBufferIndex ].dwBufferLength );
+            }
+        }
+
+        result = AdvanceToNextOutputBuffer( stream );
+        if( result != paNoError )
+            break;
+    }
+
+    return result;
+}
+
+
+static DWORD WINAPI ProcessingThreadProc( void *pArg )
+{
+    PaWinMmeStream *stream = (PaWinMmeStream *)pArg;
+    HANDLE events[3];
+    int eventCount = 0;
+    DWORD result = paNoError;
+    DWORD waitResult;
+    DWORD timeout = (unsigned long)(stream->allBuffersDurationMs * 0.5);
+    int hostBuffersAvailable;
+    signed int hostInputBufferIndex, hostOutputBufferIndex;
+    PaStreamCallbackFlags statusFlags;
+    int callbackResult;
+    int done = 0;
+    unsigned int channel, i;
+    unsigned long framesProcessed;
+    
+    /* prepare event array for call to WaitForMultipleObjects() */
+    if( stream->input.bufferEvent )
+        events[eventCount++] = stream->input.bufferEvent;
+    if( stream->output.bufferEvent )
+        events[eventCount++] = stream->output.bufferEvent;
+    events[eventCount++] = stream->abortEvent;
+
+    statusFlags = 0; /** @todo support paInputUnderflow, paOutputOverflow and paNeverDropInput */
+    
+    /* loop until something causes us to stop */
+    do{
+        /* wait for MME to signal that a buffer is available, or for
+            the PA abort event to be signaled.
+
+          When this indicates that one or more buffers are available
+          NoBuffersAreQueued() and Current*BuffersAreDone are used below to
+          poll for additional done buffers. NoBuffersAreQueued() will fail
+          to identify an underrun/overflow if the driver doesn't mark all done
+          buffers prior to signalling the event. Some drivers do this
+          (eg RME Digi96, and others don't eg VIA PC 97 input). This isn't a
+          huge problem, it just means that we won't always be able to detect
+          underflow/overflow.
+        */
+        waitResult = WaitForMultipleObjects( eventCount, events, FALSE /* wait all = FALSE */, timeout );
+        if( waitResult == WAIT_FAILED )
+        {
+            result = paUnanticipatedHostError;
+            /** @todo FIXME/REVIEW: can't return host error info from an asyncronous thread */
+            done = 1;
+        }
+        else if( waitResult == WAIT_TIMEOUT )
+        {
+            /* if a timeout is encountered, continue */
+        }
+
+        if( stream->abortProcessing )
+        {
+            /* Pa_AbortStream() has been called, stop processing immediately */
+            done = 1;
+        }
+        else if( stream->stopProcessing )
+        {
+            /* Pa_StopStream() has been called or the user callback returned
+                non-zero, processing will continue until all output buffers
+                are marked as done. The stream will stop immediately if it
+                is input-only.
+            */
+
+            if( PA_IS_OUTPUT_STREAM_(stream) )
+            {
+                if( NoBuffersAreQueued( &stream->output ) )
+                    done = 1; /* Will cause thread to return. */
+            }
+            else
+            {
+                /* input only stream */
+                done = 1; /* Will cause thread to return. */
+            }
+        }
+        else
+        {
+            hostBuffersAvailable = 1;
+
+            /* process all available host buffers */
+            do
+            {
+                hostInputBufferIndex = -1;
+                hostOutputBufferIndex = -1;
+                
+                if( PA_IS_INPUT_STREAM_(stream) )
+                {
+                    if( CurrentInputBuffersAreDone( stream ) )
+                    {
+                        if( NoBuffersAreQueued( &stream->input ) )
+                        {
+                            /** @todo
+                               if all of the other buffers are also ready then
+                               we discard all but the most recent. This is an
+                               input buffer overflow. FIXME: these buffers should
+                               be passed to the callback in a paNeverDropInput
+                               stream.
+
+                               note that it is also possible for an input overflow
+                               to happen while the callback is processing a buffer.
+                               that is handled further down.
+                            */
+                            result = CatchUpInputBuffers( stream );
+                            if( result != paNoError )
+                                done = 1;
+
+                            statusFlags |= paInputOverflow;
+                        }
+
+                        hostInputBufferIndex = stream->input.currentBufferIndex;
+                    }
+                }
+
+                if( PA_IS_OUTPUT_STREAM_(stream) )
+                {
+                    if( CurrentOutputBuffersAreDone( stream ) )
+                    {
+                        /* ok, we have an output buffer */
+                        
+                        if( NoBuffersAreQueued( &stream->output ) )
+                        {
+                            /*
+                            if all of the other buffers are also ready, catch up by copying
+                            the most recently generated buffer into all but one of the output
+                            buffers.
+
+                            note that this catch up code only handles the case where all
+                            buffers have been played out due to this thread not having
+                            woken up at all. a more common case occurs when this thread
+                            is woken up, processes one buffer, but takes too long, and as
+                            a result all the other buffers have become un-queued. that
+                            case is handled further down.
+                            */
+
+                            result = CatchUpOutputBuffers( stream );
+                            if( result != paNoError )
+                                done = 1;
+
+                            statusFlags |= paOutputUnderflow;
+                        }
+
+                        hostOutputBufferIndex = stream->output.currentBufferIndex;
+                    }
+                }
+
+               
+                if( (PA_IS_FULL_DUPLEX_STREAM_(stream) && hostInputBufferIndex != -1 && hostOutputBufferIndex != -1) ||
+                        (PA_IS_HALF_DUPLEX_STREAM_(stream) && ( hostInputBufferIndex != -1 || hostOutputBufferIndex != -1 ) ) )
+                {
+                    PaStreamCallbackTimeInfo timeInfo = {0,0,0}; /** @todo implement inputBufferAdcTime */
+
+
+                    if( PA_IS_OUTPUT_STREAM_(stream) )
+                    {
+                        /* set timeInfo.currentTime and calculate timeInfo.outputBufferDacTime
+                            from the current wave out position */
+                        MMTIME mmtime;
+                        double timeBeforeGetPosition, timeAfterGetPosition;
+                        double time;
+                        long framesInBufferRing; 		
+                        long writePosition;
+                        long playbackPosition;
+                        HWAVEOUT firstWaveOutDevice = ((HWAVEOUT*)stream->output.waveHandles)[0];
+                        
+                        mmtime.wType = TIME_SAMPLES;
+                        timeBeforeGetPosition = PaUtil_GetTime();
+                        waveOutGetPosition( firstWaveOutDevice, &mmtime, sizeof(MMTIME) );
+                        timeAfterGetPosition = PaUtil_GetTime();
+
+                        timeInfo.currentTime = timeAfterGetPosition;
+
+                        /* approximate time at which wave out position was measured
+                            as half way between timeBeforeGetPosition and timeAfterGetPosition */
+                        time = timeBeforeGetPosition + (timeAfterGetPosition - timeBeforeGetPosition) * .5;
+                        
+                        framesInBufferRing = stream->output.bufferCount * stream->bufferProcessor.framesPerHostBuffer;
+                        playbackPosition = mmtime.u.sample % framesInBufferRing;
+
+                        writePosition = stream->output.currentBufferIndex * stream->bufferProcessor.framesPerHostBuffer
+                                + stream->output.framesUsedInCurrentBuffer;
+                       
+                        if( playbackPosition >= writePosition ){
+                            timeInfo.outputBufferDacTime =
+                                    time + ((double)( writePosition + (framesInBufferRing - playbackPosition) ) * stream->bufferProcessor.samplePeriod );
+                        }else{
+                            timeInfo.outputBufferDacTime =
+                                    time + ((double)( writePosition - playbackPosition ) * stream->bufferProcessor.samplePeriod );
+                        }
+                    }
+
+
+                    PaUtil_BeginCpuLoadMeasurement( &stream->cpuLoadMeasurer );
+
+                    PaUtil_BeginBufferProcessing( &stream->bufferProcessor, &timeInfo, statusFlags  );
+
+                    /* reset status flags once they have been passed to the buffer processor */
+                    statusFlags = 0;
+
+                    if( PA_IS_INPUT_STREAM_(stream) )
+                    {
+                        PaUtil_SetInputFrameCount( &stream->bufferProcessor, 0 /* default to host buffer size */ );
+
+                        channel = 0;
+                        for( i=0; i<stream->input.deviceCount; ++i )
+                        {
+                             /* we have stored the number of channels in the buffer in dwUser */
+                            int channelCount = stream->input.waveHeaders[i][ hostInputBufferIndex ].dwUser;
+                            
+                            PaUtil_SetInterleavedInputChannels( &stream->bufferProcessor, channel,
+                                    stream->input.waveHeaders[i][ hostInputBufferIndex ].lpData +
+                                        stream->input.framesUsedInCurrentBuffer * channelCount *
+                                        stream->bufferProcessor.bytesPerHostInputSample,
+                                    channelCount );
+                                    
+
+                            channel += channelCount;
+                        }
+                    }
+
+                    if( PA_IS_OUTPUT_STREAM_(stream) )
+                    {
+                        PaUtil_SetOutputFrameCount( &stream->bufferProcessor, 0 /* default to host buffer size */ );
+                        
+                        channel = 0;
+                        for( i=0; i<stream->output.deviceCount; ++i )
+                        {
+                            /* we have stored the number of channels in the buffer in dwUser */
+                            int channelCount = stream->output.waveHeaders[i][ hostOutputBufferIndex ].dwUser;
+
+                            PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor, channel,
+                                    stream->output.waveHeaders[i][ hostOutputBufferIndex ].lpData +
+                                        stream->output.framesUsedInCurrentBuffer * channelCount *
+                                        stream->bufferProcessor.bytesPerHostOutputSample,
+                                    channelCount );
+
+                            channel += channelCount;
+                        }
+                    }
+
+                    callbackResult = paContinue;
+                    framesProcessed = PaUtil_EndBufferProcessing( &stream->bufferProcessor, &callbackResult );
+
+                    stream->input.framesUsedInCurrentBuffer += framesProcessed;
+                    stream->output.framesUsedInCurrentBuffer += framesProcessed;
+
+                    PaUtil_EndCpuLoadMeasurement( &stream->cpuLoadMeasurer, framesProcessed );
+
+                    if( callbackResult == paContinue )
+                    {
+                        /* nothing special to do */
+                    }
+                    else if( callbackResult == paAbort )
+                    {
+                        stream->abortProcessing = 1;
+                        done = 1;
+                        /** @todo FIXME: should probably reset the output device immediately once the callback returns paAbort */
+                        result = paNoError;
+                    }
+                    else
+                    {
+                        /* User callback has asked us to stop with paComplete or other non-zero value */
+                        stream->stopProcessing = 1; /* stop once currently queued audio has finished */
+                        result = paNoError;
+                    }
+
+
+                    if( PA_IS_INPUT_STREAM_(stream)
+                            && stream->stopProcessing == 0 && stream->abortProcessing == 0
+                            && stream->input.framesUsedInCurrentBuffer == stream->input.framesPerBuffer )
+                    {
+                        if( NoBuffersAreQueued( &stream->input ) )
+                        {
+                            /** @todo need to handle PaNeverDropInput here where necessary */
+                            result = CatchUpInputBuffers( stream );
+                            if( result != paNoError )
+                                done = 1;
+
+                            statusFlags |= paInputOverflow;
+                        }
+
+                        result = AdvanceToNextInputBuffer( stream );
+                        if( result != paNoError )
+                            done = 1;
+                    }
+
+                    
+                    if( PA_IS_OUTPUT_STREAM_(stream) && !stream->abortProcessing )
+                    {
+                        if( stream->stopProcessing &&
+                                stream->output.framesUsedInCurrentBuffer < stream->output.framesPerBuffer )
+                        {
+                            /* zero remaining samples in output output buffer and flush */
+
+                            stream->output.framesUsedInCurrentBuffer += PaUtil_ZeroOutput( &stream->bufferProcessor,
+                                    stream->output.framesPerBuffer - stream->output.framesUsedInCurrentBuffer );
+
+                            /* we send the entire buffer to the output devices, but we could
+                                just send a partial buffer, rather than zeroing the unused
+                                samples.
+                            */
+                        }
+
+                        if( stream->output.framesUsedInCurrentBuffer == stream->output.framesPerBuffer )
+                        {
+                            /* check for underflow before enquing the just-generated buffer,
+                                but recover from underflow after enquing it. This ensures
+                                that the most recent audio segment is repeated */
+                            int outputUnderflow = NoBuffersAreQueued( &stream->output );
+
+                            result = AdvanceToNextOutputBuffer( stream );
+                            if( result != paNoError )
+                                done = 1;
+
+                            if( outputUnderflow && !done && !stream->stopProcessing )
+                            {
+                                /* Recover from underflow in the case where the
+                                    underflow occured while processing the buffer
+                                    we just finished */
+
+                                result = CatchUpOutputBuffers( stream );
+                                if( result != paNoError )
+                                    done = 1;
+
+                                statusFlags |= paOutputUnderflow;
+                            }
+                        }
+                    }
+                    
+                    if( stream->throttleProcessingThreadOnOverload != 0 )
+                    {
+                        if( stream->stopProcessing || stream->abortProcessing )
+                        {
+                            if( stream->processingThreadPriority != stream->highThreadPriority )
+                            {
+                                SetThreadPriority( stream->processingThread, stream->highThreadPriority );
+                                stream->processingThreadPriority = stream->highThreadPriority;
+                            }
+                        }
+                        else if( PaUtil_GetCpuLoad( &stream->cpuLoadMeasurer ) > 1. )
+                        {
+                            if( stream->processingThreadPriority != stream->throttledThreadPriority )
+                            {
+                                SetThreadPriority( stream->processingThread, stream->throttledThreadPriority );
+                                stream->processingThreadPriority = stream->throttledThreadPriority;
+                            }
+
+                            /* sleep to give other processes a go */
+                            Sleep( stream->throttledSleepMsecs );
+                        }
+                        else
+                        {
+                            if( stream->processingThreadPriority != stream->highThreadPriority )
+                            {
+                                SetThreadPriority( stream->processingThread, stream->highThreadPriority );
+                                stream->processingThreadPriority = stream->highThreadPriority;
+                            }
+                        }
+                    }
+                }
+                else
+                {
+                    hostBuffersAvailable = 0;
+                }
+            }
+            while( hostBuffersAvailable &&
+                    stream->stopProcessing == 0 &&
+                    stream->abortProcessing == 0 &&
+                    !done );
+        }
+    }
+    while( !done );
+
+    stream->isActive = 0;
+
+    if( stream->streamRepresentation.streamFinishedCallback != 0 )
+        stream->streamRepresentation.streamFinishedCallback( stream->streamRepresentation.userData );
+
+    PaUtil_ResetCpuLoadMeasurer( &stream->cpuLoadMeasurer );
+    
+    return result;
+}
+
+
+/*
+    When CloseStream() is called, the multi-api layer ensures that
+    the stream has already been stopped or aborted.
+*/
+static PaError CloseStream( PaStream* s )
+{
+    PaError result;
+    PaWinMmeStream *stream = (PaWinMmeStream*)s;
+
+    result = CloseHandleWithPaError( stream->abortEvent );
+    if( result != paNoError ) goto error;
+    
+    TerminateWaveHeaders( &stream->output, 0 /* not isInput */ );
+    TerminateWaveHeaders( &stream->input, 1 /* isInput */ );
+
+    TerminateWaveHandles( &stream->output, 0 /* not isInput */, 0 /* not currentlyProcessingAnError */ );
+    TerminateWaveHandles( &stream->input, 1 /* isInput */, 0 /* not currentlyProcessingAnError */ );
+    
+    PaUtil_TerminateBufferProcessor( &stream->bufferProcessor );
+    PaUtil_TerminateStreamRepresentation( &stream->streamRepresentation );
+    PaUtil_FreeMemory( stream );
+
+error:
+    /** @todo REVIEW: what is the best way to clean up a stream if an error is detected? */
+    return result;
+}
+
+
+static PaError StartStream( PaStream *s )
+{
+    PaError result;
+    PaWinMmeStream *stream = (PaWinMmeStream*)s;
+    MMRESULT mmresult;
+    unsigned int i, j;
+    int callbackResult;
+	unsigned int channel;
+ 	unsigned long framesProcessed;
+	PaStreamCallbackTimeInfo timeInfo = {0,0,0}; /** @todo implement this for stream priming */
+    
+    PaUtil_ResetBufferProcessor( &stream->bufferProcessor );
+    
+    if( PA_IS_INPUT_STREAM_(stream) )
+    {
+        for( i=0; i<stream->input.bufferCount; ++i )
+        {
+            for( j=0; j<stream->input.deviceCount; ++j )
+            {
+                mmresult = waveInAddBuffer( ((HWAVEIN*)stream->input.waveHandles)[j], &stream->input.waveHeaders[j][i], sizeof(WAVEHDR) );
+                if( mmresult != MMSYSERR_NOERROR )
+                {
+                    result = paUnanticipatedHostError;
+                    PA_MME_SET_LAST_WAVEIN_ERROR( mmresult );
+                    goto error;
+                }
+            }
+        }
+        stream->input.currentBufferIndex = 0;
+        stream->input.framesUsedInCurrentBuffer = 0;
+    }
+
+    if( PA_IS_OUTPUT_STREAM_(stream) )
+    {
+        for( i=0; i<stream->output.deviceCount; ++i )
+        {
+            if( (mmresult = waveOutPause( ((HWAVEOUT*)stream->output.waveHandles)[i] )) != MMSYSERR_NOERROR )
+            {
+                result = paUnanticipatedHostError;
+                PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult );
+                goto error;
+            }
+        }
+
+        for( i=0; i<stream->output.bufferCount; ++i )
+        {
+            if( stream->primeStreamUsingCallback )
+            {
+
+                stream->output.framesUsedInCurrentBuffer = 0;
+                do{
+
+                    PaUtil_BeginBufferProcessing( &stream->bufferProcessor,
+                            &timeInfo,
+                            paPrimingOutput | ((stream->input.bufferCount > 0 ) ? paInputUnderflow : 0));
+
+                    if( stream->input.bufferCount > 0 )
+                        PaUtil_SetNoInput( &stream->bufferProcessor );
+
+                    PaUtil_SetOutputFrameCount( &stream->bufferProcessor, 0 /* default to host buffer size */ );
+
+                    channel = 0;
+                    for( j=0; j<stream->output.deviceCount; ++j )
+                    {
+                        /* we have stored the number of channels in the buffer in dwUser */
+                        int channelCount = stream->output.waveHeaders[j][i].dwUser;
+
+                        PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor, channel,
+                                stream->output.waveHeaders[j][i].lpData +
+                                stream->output.framesUsedInCurrentBuffer * channelCount *
+                                stream->bufferProcessor.bytesPerHostOutputSample,
+                                channelCount );
+
+                        /* we have stored the number of channels in the buffer in dwUser */
+                        channel += channelCount;
+                    }
+
+                    callbackResult = paContinue;
+                    framesProcessed = PaUtil_EndBufferProcessing( &stream->bufferProcessor, &callbackResult );
+                    stream->output.framesUsedInCurrentBuffer += framesProcessed;
+
+                    if( callbackResult != paContinue )
+                    {
+                        /** @todo fix this, what do we do if callback result is non-zero during stream
+                            priming?
+
+                            for complete: play out primed waveHeaders as usual
+                            for abort: clean up immediately.
+                       */
+                    }
+
+                }while( stream->output.framesUsedInCurrentBuffer != stream->output.framesPerBuffer );
+
+            }
+            else
+            {
+                for( j=0; j<stream->output.deviceCount; ++j )
+                {
+                    ZeroMemory( stream->output.waveHeaders[j][i].lpData, stream->output.waveHeaders[j][i].dwBufferLength );
+                }
+            }   
+
+            /* we queue all channels of a single buffer frame (accross all
+                devices, because some multidevice multichannel drivers work
+                better this way */
+            for( j=0; j<stream->output.deviceCount; ++j )
+            {
+                mmresult = waveOutWrite( ((HWAVEOUT*)stream->output.waveHandles)[j], &stream->output.waveHeaders[j][i], sizeof(WAVEHDR) );
+                if( mmresult != MMSYSERR_NOERROR )
+                {
+                    result = paUnanticipatedHostError;
+                    PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult );
+                    goto error;
+                }
+            }
+        }
+        stream->output.currentBufferIndex = 0;
+        stream->output.framesUsedInCurrentBuffer = 0;
+    }
+
+
+    stream->isStopped = 0;
+    stream->isActive = 1;
+    stream->stopProcessing = 0;
+    stream->abortProcessing = 0;
+
+    result = ResetEventWithPaError( stream->input.bufferEvent );
+    if( result != paNoError ) goto error;
+
+    result = ResetEventWithPaError( stream->output.bufferEvent );
+    if( result != paNoError ) goto error;
+    
+    
+    if( stream->streamRepresentation.streamCallback )
+    {
+        /* callback stream */
+
+        result = ResetEventWithPaError( stream->abortEvent );
+        if( result != paNoError ) goto error;
+
+        /* Create thread that waits for audio buffers to be ready for processing. */
+        stream->processingThread = CreateThread( 0, 0, ProcessingThreadProc, stream, 0, &stream->processingThreadId );
+        if( !stream->processingThread )
+        {
+            result = paUnanticipatedHostError;
+            PA_MME_SET_LAST_SYSTEM_ERROR( GetLastError() );
+            goto error;
+        }
+
+        /** @todo could have mme specific stream parameters to allow the user
+            to set the callback thread priorities */
+        stream->highThreadPriority = THREAD_PRIORITY_TIME_CRITICAL;
+        stream->throttledThreadPriority = THREAD_PRIORITY_NORMAL;
+
+        if( !SetThreadPriority( stream->processingThread, stream->highThreadPriority ) )
+        {
+            result = paUnanticipatedHostError;
+            PA_MME_SET_LAST_SYSTEM_ERROR( GetLastError() );
+            goto error;
+        }
+        stream->processingThreadPriority = stream->highThreadPriority;
+    }
+    else
+    {
+        /* blocking read/write stream */
+
+    }
+
+    if( PA_IS_INPUT_STREAM_(stream) )
+    {
+        for( i=0; i < stream->input.deviceCount; ++i )
+        {
+            mmresult = waveInStart( ((HWAVEIN*)stream->input.waveHandles)[i] );
+            PA_DEBUG(("Pa_StartStream: waveInStart returned = 0x%X.\n", mmresult));
+            if( mmresult != MMSYSERR_NOERROR )
+            {
+                result = paUnanticipatedHostError;
+                PA_MME_SET_LAST_WAVEIN_ERROR( mmresult );
+                goto error;
+            }
+        }
+    }
+
+    if( PA_IS_OUTPUT_STREAM_(stream) )
+    {
+        for( i=0; i < stream->output.deviceCount; ++i )
+        {
+            if( (mmresult = waveOutRestart( ((HWAVEOUT*)stream->output.waveHandles)[i] )) != MMSYSERR_NOERROR )
+            {
+                result = paUnanticipatedHostError;
+                PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult );
+                goto error;
+            }
+        }
+    }
+
+    return result;
+
+error:
+    /** @todo FIXME: implement recovery as best we can
+    This should involve rolling back to a state as-if this function had never been called
+    */
+    return result;
+}
+
+
+static PaError StopStream( PaStream *s )
+{
+    PaError result = paNoError;
+    PaWinMmeStream *stream = (PaWinMmeStream*)s;
+    int timeout;
+    DWORD waitResult;
+    MMRESULT mmresult;
+    signed int hostOutputBufferIndex;
+    unsigned int channel, waitCount, i;                  
+    
+    /** @todo
+        REVIEW: the error checking in this function needs review. the basic
+        idea is to return from this function in a known state - for example
+        there is no point avoiding calling waveInReset just because
+        the thread times out.
+    */
+
+    if( stream->processingThread )
+    {
+        /* callback stream */
+
+        /* Tell processing thread to stop generating more data and to let current data play out. */
+        stream->stopProcessing = 1;
+
+        /* Calculate timeOut longer than longest time it could take to return all buffers. */
+        timeout = (int)(stream->allBuffersDurationMs * 1.5);
+        if( timeout < PA_MME_MIN_TIMEOUT_MSEC_ )
+            timeout = PA_MME_MIN_TIMEOUT_MSEC_;
+
+        PA_DEBUG(("WinMME StopStream: waiting for background thread.\n"));
+
+        waitResult = WaitForSingleObject( stream->processingThread, timeout );
+        if( waitResult == WAIT_TIMEOUT )
+        {
+            /* try to abort */
+            stream->abortProcessing = 1;
+            SetEvent( stream->abortEvent );
+            waitResult = WaitForSingleObject( stream->processingThread, timeout );
+            if( waitResult == WAIT_TIMEOUT )
+            {
+                PA_DEBUG(("WinMME StopStream: timed out while waiting for background thread to finish.\n"));
+                result = paTimedOut;
+            }
+        }
+
+        CloseHandle( stream->processingThread );
+        stream->processingThread = NULL;
+    }
+    else
+    {
+        /* blocking read / write stream */
+
+        if( PA_IS_OUTPUT_STREAM_(stream) )
+        {
+            if( stream->output.framesUsedInCurrentBuffer > 0 )
+            {
+                /* there are still unqueued frames in the current buffer, so flush them */
+
+                hostOutputBufferIndex = stream->output.currentBufferIndex;
+
+                PaUtil_SetOutputFrameCount( &stream->bufferProcessor,
+                        stream->output.framesPerBuffer - stream->output.framesUsedInCurrentBuffer );
+                
+                channel = 0;
+                for( i=0; i<stream->output.deviceCount; ++i )
+                {
+                    /* we have stored the number of channels in the buffer in dwUser */
+                    int channelCount = stream->output.waveHeaders[i][ hostOutputBufferIndex ].dwUser;
+
+                    PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor, channel,
+                            stream->output.waveHeaders[i][ hostOutputBufferIndex ].lpData +
+                                stream->output.framesUsedInCurrentBuffer * channelCount *
+                                stream->bufferProcessor.bytesPerHostOutputSample,
+                            channelCount );
+
+                    channel += channelCount;
+                }
+
+                PaUtil_ZeroOutput( &stream->bufferProcessor,
+                        stream->output.framesPerBuffer - stream->output.framesUsedInCurrentBuffer );
+
+                /* we send the entire buffer to the output devices, but we could
+                    just send a partial buffer, rather than zeroing the unused
+                    samples.
+                */
+                AdvanceToNextOutputBuffer( stream );
+            }
+            
+
+            timeout = (stream->allBuffersDurationMs / stream->output.bufferCount) + 1;
+            if( timeout < PA_MME_MIN_TIMEOUT_MSEC_ )
+                timeout = PA_MME_MIN_TIMEOUT_MSEC_;
+
+            waitCount = 0;
+            while( !NoBuffersAreQueued( &stream->output ) && waitCount <= stream->output.bufferCount )
+            {
+                /* wait for MME to signal that a buffer is available */
+                waitResult = WaitForSingleObject( stream->output.bufferEvent, timeout );
+                if( waitResult == WAIT_FAILED )
+                {
+                    break;
+                }
+                else if( waitResult == WAIT_TIMEOUT )
+                {
+                    /* keep waiting */
+                }
+
+                ++waitCount;
+            }
+        }
+    }
+
+    if( PA_IS_OUTPUT_STREAM_(stream) )
+    {
+        for( i =0; i < stream->output.deviceCount; ++i )
+        {
+            mmresult = waveOutReset( ((HWAVEOUT*)stream->output.waveHandles)[i] );
+            if( mmresult != MMSYSERR_NOERROR )
+            {
+                result = paUnanticipatedHostError;
+                PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult );
+            }
+        }
+    }
+
+    if( PA_IS_INPUT_STREAM_(stream) )
+    {
+        for( i=0; i < stream->input.deviceCount; ++i )
+        {
+            mmresult = waveInReset( ((HWAVEIN*)stream->input.waveHandles)[i] );
+            if( mmresult != MMSYSERR_NOERROR )
+            {
+                result = paUnanticipatedHostError;
+                PA_MME_SET_LAST_WAVEIN_ERROR( mmresult );
+            }
+        }
+    }
+
+    stream->isStopped = 1;
+    stream->isActive = 0;
+
+    return result;
+}
+
+
+static PaError AbortStream( PaStream *s )
+{
+    PaError result = paNoError;
+    PaWinMmeStream *stream = (PaWinMmeStream*)s;
+    int timeout;
+    DWORD waitResult;
+    MMRESULT mmresult;
+    unsigned int i;
+    
+    /** @todo
+        REVIEW: the error checking in this function needs review. the basic
+        idea is to return from this function in a known state - for example
+        there is no point avoiding calling waveInReset just because
+        the thread times out.
+    */
+
+    if( stream->processingThread )
+    {
+        /* callback stream */
+        
+        /* Tell processing thread to abort immediately */
+        stream->abortProcessing = 1;
+        SetEvent( stream->abortEvent );
+    }
+
+
+    if( PA_IS_OUTPUT_STREAM_(stream) )
+    {
+        for( i =0; i < stream->output.deviceCount; ++i )
+        {
+            mmresult = waveOutReset( ((HWAVEOUT*)stream->output.waveHandles)[i] );
+            if( mmresult != MMSYSERR_NOERROR )
+            {
+                PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult );
+                return paUnanticipatedHostError;
+            }
+        }
+    }
+
+    if( PA_IS_INPUT_STREAM_(stream) )
+    {
+        for( i=0; i < stream->input.deviceCount; ++i )
+        {
+            mmresult = waveInReset( ((HWAVEIN*)stream->input.waveHandles)[i] );
+            if( mmresult != MMSYSERR_NOERROR )
+            {
+                PA_MME_SET_LAST_WAVEIN_ERROR( mmresult );
+                return paUnanticipatedHostError;
+            }
+        }
+    }
+
+
+    if( stream->processingThread )
+    {
+        /* callback stream */
+        
+        PA_DEBUG(("WinMME AbortStream: waiting for background thread.\n"));
+
+        /* Calculate timeOut longer than longest time it could take to return all buffers. */
+        timeout = (int)(stream->allBuffersDurationMs * 1.5);
+        if( timeout < PA_MME_MIN_TIMEOUT_MSEC_ )
+            timeout = PA_MME_MIN_TIMEOUT_MSEC_;
+            
+        waitResult = WaitForSingleObject( stream->processingThread, timeout );
+        if( waitResult == WAIT_TIMEOUT )
+        {
+            PA_DEBUG(("WinMME AbortStream: timed out while waiting for background thread to finish.\n"));
+            return paTimedOut;
+        }
+
+        CloseHandle( stream->processingThread );
+        stream->processingThread = NULL;
+    }
+
+    stream->isStopped = 1;
+    stream->isActive = 0;
+
+    return result;
+}
+
+
+static PaError IsStreamStopped( PaStream *s )
+{
+    PaWinMmeStream *stream = (PaWinMmeStream*)s;
+
+    return stream->isStopped;
+}
+
+
+static PaError IsStreamActive( PaStream *s )
+{
+    PaWinMmeStream *stream = (PaWinMmeStream*)s;
+
+    return stream->isActive;
+}
+
+
+static PaTime GetStreamTime( PaStream *s )
+{
+    (void) s; /* unused parameter */
+    
+    return PaUtil_GetTime();
+}
+
+
+static double GetStreamCpuLoad( PaStream* s )
+{
+    PaWinMmeStream *stream = (PaWinMmeStream*)s;
+
+    return PaUtil_GetCpuLoad( &stream->cpuLoadMeasurer );
+}
+
+
+/*
+    As separate stream interfaces are used for blocking and callback
+    streams, the following functions can be guaranteed to only be called
+    for blocking streams.
+*/
+
+static PaError ReadStream( PaStream* s,
+                           void *buffer,
+                           unsigned long frames )
+{
+    PaError result = paNoError;
+    PaWinMmeStream *stream = (PaWinMmeStream*)s;
+    void *userBuffer;
+    unsigned long framesRead = 0;
+    unsigned long framesProcessed;
+    signed int hostInputBufferIndex;
+    DWORD waitResult;
+    DWORD timeout = (unsigned long)(stream->allBuffersDurationMs * 0.5);
+    unsigned int channel, i;
+    
+    if( PA_IS_INPUT_STREAM_(stream) )
+    {
+        /* make a local copy of the user buffer pointer(s). this is necessary
+            because PaUtil_CopyInput() advances these pointers every time
+            it is called.
+        */
+        if( stream->bufferProcessor.userInputIsInterleaved )
+        {
+            userBuffer = buffer;
+        }
+        else
+        {
+            userBuffer = alloca( sizeof(void*) * stream->bufferProcessor.inputChannelCount );
+            if( !userBuffer )
+                return paInsufficientMemory;
+            for( i = 0; i<stream->bufferProcessor.inputChannelCount; ++i )
+                ((void**)userBuffer)[i] = ((void**)buffer)[i];
+        }
+        
+        do{
+            if( CurrentInputBuffersAreDone( stream ) )
+            {
+                if( NoBuffersAreQueued( &stream->input ) )
+                {
+                    /** @todo REVIEW: consider what to do if the input overflows.
+                        do we requeue all of the buffers? should we be running
+                        a thread to make sure they are always queued? */
+
+                    result = paInputOverflowed;
+                }
+
+                hostInputBufferIndex = stream->input.currentBufferIndex;
+
+                PaUtil_SetInputFrameCount( &stream->bufferProcessor,
+                        stream->input.framesPerBuffer - stream->input.framesUsedInCurrentBuffer );
+                
+                channel = 0;
+                for( i=0; i<stream->input.deviceCount; ++i )
+                {
+                    /* we have stored the number of channels in the buffer in dwUser */
+                    int channelCount = stream->input.waveHeaders[i][ hostInputBufferIndex ].dwUser;
+
+                    PaUtil_SetInterleavedInputChannels( &stream->bufferProcessor, channel,
+                            stream->input.waveHeaders[i][ hostInputBufferIndex ].lpData +
+                                stream->input.framesUsedInCurrentBuffer * channelCount *
+                                stream->bufferProcessor.bytesPerHostInputSample,
+                            channelCount );
+
+                    channel += channelCount;
+                }
+                
+                framesProcessed = PaUtil_CopyInput( &stream->bufferProcessor, &userBuffer, frames - framesRead );
+
+                stream->input.framesUsedInCurrentBuffer += framesProcessed;
+                if( stream->input.framesUsedInCurrentBuffer == stream->input.framesPerBuffer )
+                {
+                    result = AdvanceToNextInputBuffer( stream );
+                    if( result != paNoError )
+                        break;
+                }
+
+                framesRead += framesProcessed;      
+
+            }else{
+                /* wait for MME to signal that a buffer is available */
+                waitResult = WaitForSingleObject( stream->input.bufferEvent, timeout );
+                if( waitResult == WAIT_FAILED )
+                {
+                    result = paUnanticipatedHostError;
+                    break;
+                }
+                else if( waitResult == WAIT_TIMEOUT )
+                {
+                    /* if a timeout is encountered, continue,
+                        perhaps we should give up eventually
+                    */
+                }         
+            }
+        }while( framesRead < frames );
+    }
+    else
+    {
+        result = paCanNotReadFromAnOutputOnlyStream;
+    }
+
+    return result;
+}
+
+
+static PaError WriteStream( PaStream* s,
+                            const void *buffer,
+                            unsigned long frames )
+{
+    PaError result = paNoError;
+    PaWinMmeStream *stream = (PaWinMmeStream*)s;
+    const void *userBuffer;
+    unsigned long framesWritten = 0;
+    unsigned long framesProcessed;
+    signed int hostOutputBufferIndex;
+    DWORD waitResult;
+    DWORD timeout = (unsigned long)(stream->allBuffersDurationMs * 0.5);
+    unsigned int channel, i;
+
+        
+    if( PA_IS_OUTPUT_STREAM_(stream) )
+    {
+        /* make a local copy of the user buffer pointer(s). this is necessary
+            because PaUtil_CopyOutput() advances these pointers every time
+            it is called.
+        */
+        if( stream->bufferProcessor.userOutputIsInterleaved )
+        {
+            userBuffer = buffer;
+        }
+        else
+        {
+            userBuffer = alloca( sizeof(void*) * stream->bufferProcessor.outputChannelCount );
+            if( !userBuffer )
+                return paInsufficientMemory;
+            for( i = 0; i<stream->bufferProcessor.outputChannelCount; ++i )
+                ((const void**)userBuffer)[i] = ((const void**)buffer)[i];
+        }
+
+        do{
+            if( CurrentOutputBuffersAreDone( stream ) )
+            {
+                if( NoBuffersAreQueued( &stream->output ) )
+                {
+                    /** @todo REVIEW: consider what to do if the output
+                    underflows. do we requeue all the existing buffers with
+                    zeros? should we run a separate thread to keep the buffers
+                    enqueued at all times? */
+
+                    result = paOutputUnderflowed;
+                }
+
+                hostOutputBufferIndex = stream->output.currentBufferIndex;
+
+                PaUtil_SetOutputFrameCount( &stream->bufferProcessor,
+                        stream->output.framesPerBuffer - stream->output.framesUsedInCurrentBuffer );
+                
+                channel = 0;
+                for( i=0; i<stream->output.deviceCount; ++i )
+                {
+                    /* we have stored the number of channels in the buffer in dwUser */
+                    int channelCount = stream->output.waveHeaders[i][ hostOutputBufferIndex ].dwUser;
+
+                    PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor, channel,
+                            stream->output.waveHeaders[i][ hostOutputBufferIndex ].lpData +
+                                stream->output.framesUsedInCurrentBuffer * channelCount *
+                                stream->bufferProcessor.bytesPerHostOutputSample,
+                            channelCount );
+
+                    channel += channelCount;
+                }
+                
+                framesProcessed = PaUtil_CopyOutput( &stream->bufferProcessor, &userBuffer, frames - framesWritten );
+
+                stream->output.framesUsedInCurrentBuffer += framesProcessed;
+                if( stream->output.framesUsedInCurrentBuffer == stream->output.framesPerBuffer )
+                {
+                    result = AdvanceToNextOutputBuffer( stream );
+                    if( result != paNoError )
+                        break;
+                }
+
+                framesWritten += framesProcessed;
+            }
+            else
+            {
+                /* wait for MME to signal that a buffer is available */
+                waitResult = WaitForSingleObject( stream->output.bufferEvent, timeout );
+                if( waitResult == WAIT_FAILED )
+                {
+                    result = paUnanticipatedHostError;
+                    break;
+                }
+                else if( waitResult == WAIT_TIMEOUT )
+                {
+                    /* if a timeout is encountered, continue,
+                        perhaps we should give up eventually
+                    */
+                }             
+            }        
+        }while( framesWritten < frames );
+    }
+    else
+    {
+        result = paCanNotWriteToAnInputOnlyStream;
+    }
+    
+    return result;
+}
+
+
+static signed long GetStreamReadAvailable( PaStream* s )
+{
+    PaWinMmeStream *stream = (PaWinMmeStream*)s;
+    
+    if( PA_IS_INPUT_STREAM_(stream) )
+        return GetAvailableFrames( &stream->input );
+    else
+        return paCanNotReadFromAnOutputOnlyStream;
+}
+
+
+static signed long GetStreamWriteAvailable( PaStream* s )
+{
+    PaWinMmeStream *stream = (PaWinMmeStream*)s;
+    
+    if( PA_IS_OUTPUT_STREAM_(stream) )
+        return GetAvailableFrames( &stream->output );
+    else
+        return paCanNotWriteToAnInputOnlyStream;
+}
+
+
+/* NOTE: the following functions are MME-stream specific, and are called directly
+    by client code. We need to check for many more error conditions here because
+    we don't have the benefit of pa_front.c's parameter checking.
+*/
+
+static PaError GetWinMMEStreamPointer( PaWinMmeStream **stream, PaStream *s )
+{
+    PaError result;
+    PaUtilHostApiRepresentation *hostApi;
+    PaWinMmeHostApiRepresentation *winMmeHostApi;
+    
+    result = PaUtil_ValidateStreamPointer( s );
+    if( result != paNoError )
+        return result;
+
+    result = PaUtil_GetHostApiRepresentation( &hostApi, paMME );
+    if( result != paNoError )
+        return result;
+
+    winMmeHostApi = (PaWinMmeHostApiRepresentation*)hostApi;
+    
+    /* note, the following would be easier if there was a generic way of testing
+        that a stream belongs to a specific host API */
+    
+    if( PA_STREAM_REP( s )->streamInterface == &winMmeHostApi->callbackStreamInterface
+            || PA_STREAM_REP( s )->streamInterface == &winMmeHostApi->blockingStreamInterface )
+    {
+        /* s is a WinMME stream */
+        *stream = (PaWinMmeStream *)s;
+        return paNoError;
+    }
+    else
+    {
+        return paIncompatibleStreamHostApi;
+    }
+}
+
+
+int PaWinMME_GetStreamInputHandleCount( PaStream* s )
+{
+    PaWinMmeStream *stream;
+    PaError result = GetWinMMEStreamPointer( &stream, s );
+
+    if( result == paNoError )
+        return (PA_IS_INPUT_STREAM_(stream)) ? stream->input.deviceCount : 0;
+    else
+        return result;
+}
+
+
+HWAVEIN PaWinMME_GetStreamInputHandle( PaStream* s, int handleIndex )
+{
+    PaWinMmeStream *stream;
+    PaError result = GetWinMMEStreamPointer( &stream, s );
+
+    if( result == paNoError
+            && PA_IS_INPUT_STREAM_(stream)
+            && handleIndex >= 0
+            && (unsigned int)handleIndex < stream->input.deviceCount )
+        return ((HWAVEIN*)stream->input.waveHandles)[handleIndex];
+    else
+        return 0;
+}
+
+
+int PaWinMME_GetStreamOutputHandleCount( PaStream* s)
+{
+    PaWinMmeStream *stream;
+    PaError result = GetWinMMEStreamPointer( &stream, s );
+
+    if( result == paNoError )
+        return (PA_IS_OUTPUT_STREAM_(stream)) ? stream->output.deviceCount : 0;
+    else
+        return result;
+}
+
+
+HWAVEOUT PaWinMME_GetStreamOutputHandle( PaStream* s, int handleIndex )
+{
+    PaWinMmeStream *stream;
+    PaError result = GetWinMMEStreamPointer( &stream, s );
+
+    if( result == paNoError
+            && PA_IS_OUTPUT_STREAM_(stream)
+            && handleIndex >= 0
+            && (unsigned int)handleIndex < stream->output.deviceCount )
+        return ((HWAVEOUT*)stream->output.waveHandles)[handleIndex];
+    else
+        return 0;
+}
+
+
+
+
+
diff --git a/src/audio/portaudio/pa_win_wmme/pa_win_wmme.h b/src/audio/portaudio/pa_win_wmme/pa_win_wmme.h
new file mode 100644
index 0000000000000000000000000000000000000000..458f88f99ffb75de17bd88861c8c299a4ae534fb
--- /dev/null
+++ b/src/audio/portaudio/pa_win_wmme/pa_win_wmme.h
@@ -0,0 +1,160 @@
+#ifndef PA_WIN_WMME_H
+#define PA_WIN_WMME_H
+/*
+ * $Id$
+ * PortAudio Portable Real-Time Audio Library
+ * MME specific extensions
+ *
+ * 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.
+ *
+ */
+
+/** @file
+ @brief WMME-specific PortAudio API extension header file.
+*/
+
+
+#include "portaudio.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif /* __cplusplus */
+
+
+#define paWinMmeUseLowLevelLatencyParameters            (0x01)
+#define paWinMmeUseMultipleDevices                      (0x02)  /* use mme specific multiple device feature */
+
+
+/* By default, the mme implementation drops the processing thread's priority
+    to THREAD_PRIORITY_NORMAL and sleeps the thread if the CPU load exceeds 100%
+    This flag disables any priority throttling. The processing thread will always
+    run at THREAD_PRIORITY_TIME_CRITICAL.
+*/
+#define paWinMmeDontThrottleOverloadedProcessingThread  (0x08)
+
+
+typedef struct PaWinMmeDeviceAndChannelCount{
+    PaDeviceIndex device;
+    int channelCount;
+}PaWinMmeDeviceAndChannelCount;
+
+
+typedef struct PaWinMmeStreamInfo{
+    unsigned long size;             /**< sizeof(PaWinMmeStreamInfo) */
+    PaHostApiTypeId hostApiType;    /**< paMME */
+    unsigned long version;          /**< 1 */
+
+    unsigned long flags;
+
+    /* low-level latency setting support
+        These settings control the number and size of host buffers in order
+        to set latency. They will be used instead of the generic parameters
+        to Pa_OpenStream() if flags contains the PaWinMmeUseLowLevelLatencyParameters
+        flag.
+
+        If PaWinMmeStreamInfo structures with PaWinMmeUseLowLevelLatencyParameters
+        are supplied for both input and output in a full duplex stream, then the
+        input and output framesPerBuffer must be the same, or the larger of the
+        two must be a multiple of the smaller, otherwise a
+        paIncompatibleHostApiSpecificStreamInfo error will be returned from
+        Pa_OpenStream().
+    */
+    unsigned long framesPerBuffer;
+    unsigned long bufferCount;  /* formerly numBuffers */ 
+
+    /* multiple devices per direction support
+        If flags contains the PaWinMmeUseMultipleDevices flag,
+        this functionality will be used, otherwise the device parameter to
+        Pa_OpenStream() will be used instead.
+        If devices are specified here, the corresponding device parameter
+        to Pa_OpenStream() should be set to paUseHostApiSpecificDeviceSpecification,
+        otherwise an paInvalidDevice error will result.
+        The total number of channels accross all specified devices
+        must agree with the corresponding channelCount parameter to
+        Pa_OpenStream() otherwise a paInvalidChannelCount error will result.
+    */
+    PaWinMmeDeviceAndChannelCount *devices;
+    unsigned long deviceCount;
+
+}PaWinMmeStreamInfo;
+
+
+/** Retrieve the number of wave in handles used by a PortAudio WinMME stream.
+ Returns zero if the stream is output only.
+
+ @return A non-negative value indicating the number of wave in handles
+ or, a PaErrorCode (which are always negative) if PortAudio is not initialized
+ or an error is encountered.
+
+ @see PaWinMME_GetStreamInputHandle
+*/
+int PaWinMME_GetStreamInputHandleCount( PaStream* stream );
+
+
+/** Retrieve a wave in handle used by a PortAudio WinMME stream.
+
+ @param stream The stream to query.
+ @param handleIndex The zero based index of the wave in handle to retrieve. This
+    should be in the range [0, PaWinMME_GetStreamInputHandle(stream)-1].
+
+ @return A valid wave in handle, or NULL if an error occurred.
+
+ @see PaWinMME_GetStreamInputHandle
+*/
+HWAVEIN PaWinMME_GetStreamInputHandle( PaStream* stream, int handleIndex );
+
+
+/** Retrieve the number of wave out handles used by a PortAudio WinMME stream.
+ Returns zero if the stream is input only.
+ 
+ @return A non-negative value indicating the number of wave out handles
+ or, a PaErrorCode (which are always negative) if PortAudio is not initialized
+ or an error is encountered.
+
+ @see PaWinMME_GetStreamOutputHandle
+*/
+int PaWinMME_GetStreamOutputHandleCount( PaStream* stream );
+
+
+/** Retrieve a wave out handle used by a PortAudio WinMME stream.
+
+ @param stream The stream to query.
+ @param handleIndex The zero based index of the wave out handle to retrieve.
+    This should be in the range [0, PaWinMME_GetStreamOutputHandleCount(stream)-1].
+
+ @return A valid wave out handle, or NULL if an error occurred.
+
+ @see PaWinMME_GetStreamOutputHandleCount
+*/
+HWAVEOUT PaWinMME_GetStreamOutputHandle( PaStream* stream, int handleIndex );
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* PA_WIN_WMME_H */                                  
diff --git a/src/audio/portaudio/pablio/README.txt b/src/audio/portaudio/pablio/README.txt
new file mode 100644
index 0000000000000000000000000000000000000000..99c7d146ed8e0d29cc8942e624aa286687e809da
--- /dev/null
+++ b/src/audio/portaudio/pablio/README.txt
@@ -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/src/audio/portaudio/pablio/pablio.c b/src/audio/portaudio/pablio/pablio.c
new file mode 100644
index 0000000000000000000000000000000000000000..452eccc4d12c02e5333f8e8d30c7e7c1b4da1802
--- /dev/null
+++ b/src/audio/portaudio/pablio/pablio.c
@@ -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/src/audio/portaudio/pablio/pablio.def b/src/audio/portaudio/pablio/pablio.def
new file mode 100644
index 0000000000000000000000000000000000000000..a10f95291575fdabbc2a688d103658e851fc93d5
--- /dev/null
+++ b/src/audio/portaudio/pablio/pablio.def
@@ -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/src/audio/portaudio/pablio/pablio.h b/src/audio/portaudio/pablio/pablio.h
new file mode 100644
index 0000000000000000000000000000000000000000..c1ad84a8367ef4e54a55bcefaa092a7cfab91a49
--- /dev/null
+++ b/src/audio/portaudio/pablio/pablio.h
@@ -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/src/audio/portaudio/pablio/ringbuffer.c b/src/audio/portaudio/pablio/ringbuffer.c
new file mode 100644
index 0000000000000000000000000000000000000000..16031fef67d663137db8dcfa7085b9800e7258d0
--- /dev/null
+++ b/src/audio/portaudio/pablio/ringbuffer.c
@@ -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/src/audio/portaudio/pablio/ringbuffer.h b/src/audio/portaudio/pablio/ringbuffer.h
new file mode 100644
index 0000000000000000000000000000000000000000..dd769bee6db35a348e2c37eea8ae255266d6643e
--- /dev/null
+++ b/src/audio/portaudio/pablio/ringbuffer.h
@@ -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/src/audio/portaudio/pablio/test_rw.c b/src/audio/portaudio/pablio/test_rw.c
new file mode 100644
index 0000000000000000000000000000000000000000..7f7e886ac0e53f9f4324ec4db2ad511c66c20dde
--- /dev/null
+++ b/src/audio/portaudio/pablio/test_rw.c
@@ -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          (44100)
+#define NUM_SECONDS              (5)
+#define SAMPLES_PER_FRAME        (2)
+#define FRAMES_PER_BLOCK        (64)
+
+/* 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 * FRAMES_PER_BLOCK];
+    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 += FRAMES_PER_BLOCK )
+    {
+        /* Read one block of data into sample array from audio input. */
+        ReadAudioStream( aStream, samples, FRAMES_PER_BLOCK );
+        /* Write that same block of data to output. */
+        WriteAudioStream( aStream, samples, FRAMES_PER_BLOCK );
+    }
+
+    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/src/audio/portaudio/pablio/test_rw_echo.c b/src/audio/portaudio/pablio/test_rw_echo.c
new file mode 100644
index 0000000000000000000000000000000000000000..9ba8c8d12667b6ccd1f6319e4e301f504e1a5cd9
--- /dev/null
+++ b/src/audio/portaudio/pablio/test_rw_echo.c
@@ -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/src/audio/portaudio/pablio/test_w_saw.c b/src/audio/portaudio/pablio/test_w_saw.c
new file mode 100644
index 0000000000000000000000000000000000000000..d5d4e9be539cb39334c6d3571fa0f59148aac998
--- /dev/null
+++ b/src/audio/portaudio/pablio/test_w_saw.c
@@ -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/src/audio/portaudio/pablio/test_w_saw8.c b/src/audio/portaudio/pablio/test_w_saw8.c
new file mode 100644
index 0000000000000000000000000000000000000000..0a9d05abd2f0ba12aae14b8ee36a9a876edf2ce2
--- /dev/null
+++ b/src/audio/portaudio/pablio/test_w_saw8.c
@@ -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;
+}