]> Repos - portaudio/commitdiff
finished WASAPI implementation:
authordmitrykos <dmitrykos@0f58301d-fd10-0410-b4af-bbb618454e57>
Tue, 16 Feb 2010 09:46:08 +0000 (09:46 +0000)
committerdmitrykos <dmitrykos@0f58301d-fd10-0410-b4af-bbb618454e57>
Tue, 16 Feb 2010 09:46:08 +0000 (09:46 +0000)
 - Callback/Blocking interface
 - Playback/Capture
 - Shared/Exclusive mode
to compile you must have installed recent Windows SDK(Vista SDK or higher) and min Visual Studio 2005 SP1. WASAPI is enabled by default, to disable you must define PA_NO_WASAPI prior compilation.

include/pa_win_wasapi.h [new file with mode: 0644]
src/hostapi/wasapi/pa_win_wasapi.cpp
src/os/win/pa_win_hostapis.c

diff --git a/include/pa_win_wasapi.h b/include/pa_win_wasapi.h
new file mode 100644 (file)
index 0000000..1d26a2e
--- /dev/null
@@ -0,0 +1,242 @@
+#ifndef PA_WIN_WASAPI_H\r
+#define PA_WIN_WASAPI_H\r
+/*\r
+ * $Id:  $\r
+ * PortAudio Portable Real-Time Audio Library\r
+ * DirectSound specific extensions\r
+ *\r
+ * Copyright (c) 1999-2007 Ross Bencina and Phil Burk\r
+ *\r
+ * Permission is hereby granted, free of charge, to any person obtaining\r
+ * a copy of this software and associated documentation files\r
+ * (the "Software"), to deal in the Software without restriction,\r
+ * including without limitation the rights to use, copy, modify, merge,\r
+ * publish, distribute, sublicense, and/or sell copies of the Software,\r
+ * and to permit persons to whom the Software is furnished to do so,\r
+ * subject to the following conditions:\r
+ *\r
+ * The above copyright notice and this permission notice shall be\r
+ * included in all copies or substantial portions of the Software.\r
+ *\r
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\r
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR\r
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF\r
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\r
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r
+ */\r
+\r
+/*\r
+ * The text above constitutes the entire PortAudio license; however, \r
+ * the PortAudio community also makes the following non-binding requests:\r
+ *\r
+ * Any person wishing to distribute modifications to the Software is\r
+ * requested to send the modifications to the original developer so that\r
+ * they can be incorporated into the canonical version. It is also \r
+ * requested that these non-binding requests be included along with the \r
+ * license above.\r
+ */\r
+\r
+/** @file\r
+ @ingroup public_header\r
+ @brief WASAPI-specific PortAudio API extension header file.\r
+*/\r
+\r
+#include "portaudio.h"\r
+#include "pa_win_waveformat.h"\r
+\r
+#ifdef __cplusplus\r
+extern "C"\r
+{\r
+#endif /* __cplusplus */\r
+\r
+\r
+/* Setup flags */\r
+typedef enum PaWasapiFlags\r
+{\r
+    /* puts WASAPI into exclusive mode */\r
+    paWinWasapiExclusive                = (1 << 0),\r
+\r
+    /* allows to skip internal PA processing completely */\r
+    paWinWasapiRedirectHostProcessor    = (1 << 1),\r
+\r
+    /* assigns custom channel mask */\r
+    paWinWasapiUseChannelMask           = (1 << 2),\r
+\r
+    /* selects non-Event driven method of data read/write\r
+       Note: WASAPI Event driven core is capable of 2ms latency!!!, but Polling\r
+             method can only provide 15-20ms latency. */\r
+    paWinWasapiPolling                  = (1 << 3),\r
+}\r
+PaWasapiFlags;\r
+#define paWinWasapiExclusive             (paWinWasapiExclusive)\r
+#define paWinWasapiRedirectHostProcessor (paWinWasapiRedirectHostProcessor)\r
+#define paWinWasapiUseChannelMask        (paWinWasapiUseChannelMask)\r
+#define paWinWasapiPolling               (paWinWasapiPolling)\r
+\r
+\r
+/* Host processor. Allows to skip internal PA processing completely. \r
+   You must set paWinWasapiRedirectHostProcessor flag to PaWasapiStreamInfo::flags member\r
+   in order to have host processor redirected to your callback.\r
+   Use with caution! inputFrames and outputFrames depend solely on final device setup (buffer\r
+   size is just recommendation) but are not changing during run-time once stream is started.\r
+*/\r
+typedef void (*PaWasapiHostProcessorCallback) (void *inputBuffer,  long inputFrames,\r
+                                               void *outputBuffer, long outputFrames,\r
+                                               void *userData);\r
+\r
+/* Stream descriptor. */\r
+typedef struct PaWasapiStreamInfo \r
+{\r
+    unsigned long size;             /**< sizeof(PaWasapiStreamInfo) */\r
+    PaHostApiTypeId hostApiType;    /**< paWASAPI */\r
+    unsigned long version;          /**< 1 */\r
+\r
+    unsigned long flags;            /**< collection of PaWasapiFlags */\r
+\r
+    /* Support for WAVEFORMATEXTENSIBLE channel masks. If flags contains\r
+       paWinWasapiUseChannelMask this allows you to specify which speakers \r
+       to address in a multichannel stream. Constants for channelMask\r
+       are specified in pa_win_waveformat.h\r
+    */\r
+    PaWinWaveFormatChannelMask channelMask;\r
+\r
+    /* Delivers raw data to callback obtained from GetBuffer() methods skipping \r
+       internal PortAudio processing inventory completely. userData parameter will \r
+       be the same that was passed to Pa_OpenStream method.\r
+    */\r
+    PaWasapiHostProcessorCallback hostProcessorOutput;\r
+    PaWasapiHostProcessorCallback hostProcessorInput;\r
+} \r
+PaWasapiStreamInfo;\r
+\r
+\r
+/** Returns default sound format for device. Format is represented by PaWinWaveFormat or \r
+    WAVEFORMATEXTENSIBLE structure.\r
+\r
+ @param pFormat pointer to PaWinWaveFormat or WAVEFORMATEXTENSIBLE structure.\r
+ @param nFormatSize pize of PaWinWaveFormat or WAVEFORMATEXTENSIBLE structure in bytes.\r
+ @param nDevice device index.\r
+\r
+ @return A non-negative value indicating the number of bytes copied into format decriptor\r
+         or, a PaErrorCode (which are always negative) if PortAudio is not initialized\r
+         or an error is encountered.\r
+*/\r
+int PaWasapi_GetDeviceDefaultFormat( void *pFormat, int nFormatSize, PaDeviceIndex nDevice );\r
+\r
+\r
+/* Device role */\r
+typedef enum PaWasapiDeviceRole\r
+{\r
+    eRoleRemoteNetworkDevice = 0,\r
+    eRoleSpeakers,\r
+    eRoleLineLevel,\r
+    eRoleHeadphones,\r
+    eRoleMicrophone,\r
+    eRoleHeadset,\r
+    eRoleHandset,\r
+    eRoleUnknownDigitalPassthrough,\r
+    eRoleSPDIF,\r
+    eRoleHDMI,\r
+    eRoleUnknownFormFactor\r
+}\r
+PaWasapiDeviceRole;\r
+\r
+\r
+/** Returns device role (PaWasapiDeviceRole enum).\r
+\r
+ @param nDevice device index.\r
+\r
+ @return A non-negative value indicating device role or, a PaErrorCode (which are always negative)\r
+         if PortAudio is not initialized or an error is encountered.\r
+*/\r
+int/*PaWasapiDeviceRole*/ PaWasapi_GetDeviceRole( PaDeviceIndex nDevice );\r
+\r
+\r
+/** Boost thread priority of calling thread (MMCSS). Use it for Blocking Interface only for thread\r
+    which makes calls to Pa_WriteStream/Pa_ReadStream.\r
+\r
+ @param hTask a handle to pointer to priority task. Must be used with PaWasapi_RevertThreadPriority\r
+              method to revert thread priority to initial state.\r
+\r
+ @param nPriorityClass an Id of thread priority:    1 - Audio\r
+                                                    2 - Capture\r
+                                                    3 - Distribution\r
+                                                    4 - Games\r
+                                                    5 - Playback\r
+                                                    6 - Pro Audio\r
+                                                    7 - Window Manager\r
+\r
+ @return Error code indicating success or failure.\r
+ @see PaWasapi_RevertThreadPriority\r
+*/\r
+PaError PaWasapi_ThreadPriorityBoost( void **hTask, int nPriorityClass );\r
+\r
+\r
+/** Boost thread priority of calling thread (MMCSS). Use it for Blocking Interface only for thread\r
+    which makes calls to Pa_WriteStream/Pa_ReadStream.\r
+\r
+ @param hTask Task handle obtained by PaWasapi_BoostThreadPriority method.\r
+ @return Error code indicating success or failure.\r
+ @see PaWasapi_BoostThreadPriority\r
+*/\r
+PaError PaWasapi_ThreadPriorityRevert( void *hTask );\r
+\r
+\r
+/*\r
+    IMPORTANT:\r
+\r
+    WASAPI is implemented for Callback and Blocking interfaces. It supports Shared and Exclusive\r
+    share modes. \r
+    \r
+    Exclusive Mode:\r
+\r
+        Exclusive mode allows to deliver audio data directly to hardware bypassing\r
+        software mixing.\r
+        Exclusive mode is specified by 'paWinWasapiExclusive' flag.\r
+\r
+    Callback Interface:\r
+\r
+        Provides best audio quality with low latency. Callback interface is implemented in \r
+        two versions:\r
+\r
+        1) Event-Driven:\r
+        This is the most powerful WASAPI implementation which is capable to provides glitch-free\r
+        audio at 2ms latency in Exclusive mode. Lowest possible latency for this mode is \r
+        usually - 2ms for HD Audio class audio chips (including on-board audio, 2ms was achieved \r
+        on Realtek ALC888/S/T). For Shared mode latency can not go lower than 20ms.\r
+\r
+        2) Poll-Driven:\r
+        Polling is another 2-nd method to operate with WASAPI. It is less efficient than Event-Driven\r
+        and provides latency at around 12-13ms. Polling must be used to overcome a system bug\r
+        under Windows Vista x64 when application is WOW64(32-bit) and Event-Driven method simply times\r
+        out (event handle is never signalled on buffer completion). Please note, such Vista bug \r
+        does not exist in Windows 7 x64.\r
+        Polling is setup by speciying 'paWinWasapiPolling' flag.\r
+        Thread priority can be boosted by specifying 'paWinWasapiBlockingThreadPriorityPro' flag.\r
+\r
+    Blocking Interface:\r
+\r
+        Blocking interface is implemented but due to above described Poll-Driven method can not\r
+        deliver low latency audio. Specifying too low latency in Shared mode will result in \r
+        distorted audio although Exclusive mode adds stability.\r
+\r
+    Pa_IsFormatSupported:\r
+\r
+        To check format with correct Share Mode (Exclusive/Shared) you must supply\r
+        PaWasapiStreamInfo with flags paWinWasapiExclusive set through member of \r
+        PaStreamParameters::hostApiSpecificStreamInfo structure.\r
+\r
+    Pa_OpenStream:\r
+\r
+        To set desired Share Mode (Exclusive/Shared) you must supply\r
+        PaWasapiStreamInfo with flags paWinWasapiExclusive set through member of \r
+        PaStreamParameters::hostApiSpecificStreamInfo structure.\r
+*/\r
+\r
+#ifdef __cplusplus\r
+}\r
+#endif /* __cplusplus */\r
+\r
+#endif /* PA_WIN_WASAPI_H */                                  \r
index 498fc6c442d629146256fb025d6e2b42bada30cc..b91d812c0201c7ba420555a8cbc32a9c58ce9402 100644 (file)
@@ -1,6 +1,6 @@
 /*\r
  * Portable Audio I/O Library WASAPI implementation\r
- * Copyright (c) 2006-2007 David Viens\r
+ * Copyright (c) 2006-2010 David Viens, Dmitry Kostjuchenko\r
  *\r
  * Based on the Open Source API proposed by Ross Bencina\r
  * Copyright (c) 1999-2002 Ross Bencina, Phil Burk\r
  */\r
 \r
 /*\r
- * The text above constitutes the entire PortAudio license; however, \r
+ * The text above constitutes the entire PortAudio license; however,\r
  * the PortAudio community also makes the following non-binding requests:\r
  *\r
  * Any person wishing to distribute modifications to the Software is\r
  * requested to send the modifications to the original developer so that\r
- * they can be incorporated into the canonical version. It is also \r
- * requested that these non-binding requests be included along with the \r
+ * they can be incorporated into the canonical version. It is also\r
+ * requested that these non-binding requests be included along with the\r
  * license above.\r
  */\r
 \r
 /** @file\r
  @ingroup hostapi_src\r
  @brief WASAPI implementation of support for a host API.\r
-\r
- @note pa_wasapi currently requires VC 2005, and the latest Vista SDK\r
+ @note pa_wasapi currently requires minimum VC 2005, and the latest Vista SDK\r
 */\r
 \r
-#if _MSC_VER >= 1400\r
+#define WIN32_LEAN_AND_MEAN // exclude rare headers\r
 #include <windows.h>\r
-#include <MMReg.h>  //must be before other Wasapi headers\r
-#include <strsafe.h>\r
-#include <mmdeviceapi.h>\r
-#include <Avrt.h>\r
-#include <audioclient.h>\r
-#include <Endpointvolume.h>\r
-\r
-#include <KsMedia.h>\r
-#include <functiondiscoverykeys.h>  // PKEY_Device_FriendlyName\r
-#endif\r
-\r
 #include <process.h>\r
+#include <mmsystem.h>\r
+#include <MMReg.h>  // must be before other Wasapi headers\r
+#if _MSC_VER >= 1400\r
+       #include <strsafe.h>\r
+       #include <Avrt.h>\r
+       #include <audioclient.h>\r
+       #include <Endpointvolume.h>\r
+       #define INITGUID //<< avoid additional linkage of static libs, uneeded code will be optimized out\r
+       #include <mmdeviceapi.h>\r
+       #include <functiondiscoverykeys.h>\r
+       #undef INITGUID\r
+#endif\r
 \r
 #include "pa_util.h"\r
 #include "pa_allocation.h"\r
 #include "pa_cpuload.h"\r
 #include "pa_process.h"\r
 #include "pa_debugprint.h"\r
+#include "pa_win_wasapi.h"\r
+\r
+/* use CreateThread for CYGWIN/Windows Mobile, _beginthreadex for all others */\r
+#if !defined(__CYGWIN__) && !defined(_WIN32_WCE)\r
+#define CREATE_THREAD(PROC) (HANDLE)_beginthreadex( NULL, 0, (PROC), stream, 0, &stream->dwThreadId )\r
+#define PA_THREAD_FUNC static unsigned WINAPI\r
+#define PA_THREAD_ID unsigned\r
+#else\r
+#define CREATE_THREAD(PROC) CreateThread( NULL, 0, (PROC), stream, 0, &stream->dwThreadId )\r
+#define PA_THREAD_FUNC static DWORD WINAPI\r
+#define PA_THREAD_ID DWORD\r
+#endif\r
 \r
+// Thread function forward decl.\r
+PA_THREAD_FUNC ProcThreadEvent(void *param);\r
+PA_THREAD_FUNC ProcThreadPoll(void *param);\r
 \r
-/*\r
-  davidv : work in progress. try using with 48000 , then 44100\r
-  and shared mode FIRST.\r
- */\r
+// Availabe from Windows 7\r
+#ifndef AUDCLNT_E_BUFFER_ERROR\r
+       #define AUDCLNT_E_BUFFER_ERROR AUDCLNT_ERR(0x018)\r
+#endif\r
+#ifndef AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED\r
+       #define AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED AUDCLNT_ERR(0x019)\r
+#endif\r
+#ifndef AUDCLNT_E_INVALID_DEVICE_PERIOD\r
+       #define AUDCLNT_E_INVALID_DEVICE_PERIOD AUDCLNT_ERR(0x020)\r
+#endif\r
 \r
-#define PORTAUDIO_SHAREMODE     AUDCLNT_SHAREMODE_SHARED\r
-//#define PORTAUDIO_SHAREMODE   AUDCLNT_SHAREMODE_EXCLUSIVE\r
+#define MAX_STR_LEN 512\r
 \r
-/* use CreateThread for CYGWIN, _beginthreadex for all others */\r
-#ifndef __CYGWIN__\r
-#define CREATE_THREAD (HANDLE) _beginthreadex(NULL, 0, (unsigned (_stdcall *)(void *))ProcThread, (LPVOID) stream, 0, (unsigned *)&stream->dwThreadId)\r
-#else\r
-#define CREATE_THREAD CreateThread(NULL, 0, ProcThread, (LPVOID) stream, 0, &stream->dwThreadId)\r
+enum { S_INPUT = 0, S_OUTPUT, S_COUNT };\r
+\r
+#define PRINT(x) PA_DEBUG(x);\r
+\r
+#define PA_SKELETON_SET_LAST_HOST_ERROR( errorCode, errorText ) \\r
+    PaUtil_SetLastHostErrorInfo( paInDevelopment, errorCode, errorText )\r
+\r
+#ifndef IF_FAILED_JUMP\r
+#define IF_FAILED_JUMP(hr, label) if(FAILED(hr)) goto label;\r
 #endif\r
 \r
-/* prototypes for functions declared in this file */\r
+#define SAFE_CLOSE(h) if ((h) != NULL) { CloseHandle((h)); (h) = NULL; }\r
+#define SAFE_RELEASE(punk) if ((punk) != NULL) { (punk)->Release(); (punk) = NULL; }\r
+\r
+// Local methods\r
+void _OnStreamStop(struct PaWasapiStream *stream);\r
+void _FinishStream(struct PaWasapiStream *stream);\r
+\r
+// AVRT is the new "multimedia schedulling stuff"\r
+typedef BOOL   (WINAPI *FAvRtCreateThreadOrderingGroup)  (PHANDLE,PLARGE_INTEGER,GUID*,PLARGE_INTEGER);\r
+typedef BOOL   (WINAPI *FAvRtDeleteThreadOrderingGroup)  (HANDLE);\r
+typedef BOOL   (WINAPI *FAvRtWaitOnThreadOrderingGroup)  (HANDLE);\r
+typedef HANDLE (WINAPI *FAvSetMmThreadCharacteristics)   (LPCTSTR,LPDWORD);\r
+typedef BOOL   (WINAPI *FAvRevertMmThreadCharacteristics)(HANDLE);\r
+typedef BOOL   (WINAPI *FAvSetMmThreadPriority)          (HANDLE,AVRT_PRIORITY);\r
+\r
+static HMODULE hDInputDLL = 0;\r
+FAvRtCreateThreadOrderingGroup   pAvRtCreateThreadOrderingGroup=0;\r
+FAvRtDeleteThreadOrderingGroup   pAvRtDeleteThreadOrderingGroup=0;\r
+FAvRtWaitOnThreadOrderingGroup   pAvRtWaitOnThreadOrderingGroup=0;\r
+FAvSetMmThreadCharacteristics    pAvSetMmThreadCharacteristics=0;\r
+FAvRevertMmThreadCharacteristics pAvRevertMmThreadCharacteristics=0;\r
+FAvSetMmThreadPriority           pAvSetMmThreadPriority=0;\r
+\r
+#define setupPTR(fun, type, name)  {                                                        \\r
+                                        fun = (type) GetProcAddress(hDInputDLL,name);       \\r
+                                        if(fun == NULL) {                                   \\r
+                                            PRINT(("GetProcAddr failed for %s" ,name));     \\r
+                                            return false;                                   \\r
+                                        }                                                   \\r
+                                    }                                                       \\r
 \r
+// ------------------------------------------------------------------------------------------\r
+/* prototypes for functions declared in this file */\r
 #ifdef __cplusplus\r
 extern "C"\r
 {\r
 #endif /* __cplusplus */\r
-\r
-PaError PaWinWasapi_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );\r
-\r
+PaError PaWasapi_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );\r
 #ifdef __cplusplus\r
 }\r
 #endif /* __cplusplus */\r
+// dummy entry point for other compilers and sdks\r
+// currently built using RC1 SDK (5600)\r
+#if _MSC_VER < 1400\r
+PaError PaWasapi_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex )\r
+{\r
+    return paNoError;\r
+}\r
+#else\r
 \r
-\r
-\r
-\r
+// ------------------------------------------------------------------------------------------\r
 static void Terminate( struct PaUtilHostApiRepresentation *hostApi );\r
 static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,\r
                                   const PaStreamParameters *inputParameters,\r
@@ -125,62 +184,44 @@ static PaError WriteStream( PaStream* stream, const void *buffer, unsigned long
 static signed long GetStreamReadAvailable( PaStream* stream );\r
 static signed long GetStreamWriteAvailable( PaStream* stream );\r
 \r
-\r
-/* IMPLEMENT ME: a macro like the following one should be used for reporting\r
- host errors */\r
-#define PA_SKELETON_SET_LAST_HOST_ERROR( errorCode, errorText ) \\r
-    PaUtil_SetLastHostErrorInfo( paInDevelopment, errorCode, errorText )\r
-\r
-/* PaWinWasapiHostApiRepresentation - host api datastructure specific to this implementation */\r
-\r
-\r
-\r
-//dummy entry point for other compilers and sdks\r
-//currently built using RC1 SDK (5600)\r
-#if _MSC_VER < 1400\r
-\r
-PaError PaWinWasapi_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex ){\r
-    return paNoError;\r
-}\r
-\r
-#else\r
-\r
-\r
-\r
-\r
-#define MAX_STR_LEN 512\r
-\r
+// ------------------------------------------------------------------------------------------\r
 /*\r
- These are fields that can be gathered from IDevice\r
- and IAudioDevice PRIOR to Initialize, and done in first pass\r
- i assume that neither of these will cause the Driver to "load",\r
- but again, who knows how they implement their stuff\r
+ These are fields that can be gathered from IDevice and IAudioDevice PRIOR to Initialize, and \r
+ done in first pass i assume that neither of these will cause the Driver to "load", but again, \r
+ who knows how they implement their stuff\r
  */\r
-typedef struct PaWinWasapiDeviceInfo\r
+typedef struct PaWasapiDeviceInfo\r
 {\r
-    //hmm is it wise to keep a reference until Terminate?\r
-    //TODO Check if that interface requires the driver to be loaded!\r
-    IMMDevice * device;\r
+    // Device\r
+    IMMDevice *device;\r
 \r
-    //Fields filled from IDevice\r
-    //from GetId\r
+       // from GetId\r
     WCHAR szDeviceID[MAX_STR_LEN];\r
-    //from GetState\r
+    \r
+       // from GetState\r
     DWORD state;\r
 \r
-    //Fields filled from IMMEndpoint'sGetDataFlow\r
-    EDataFlow  flow;\r
+    // Fields filled from IMMEndpoint'sGetDataFlow\r
+    EDataFlow flow;\r
 \r
-    //Fields filled from IAudioDevice (_prior_ to Initialize)\r
-    //from GetDevicePeriod(\r
-    REFERENCE_TIME  DefaultDevicePeriod;\r
-    REFERENCE_TIME  MinimumDevicePeriod;\r
-    //from GetMixFormat\r
-    WAVEFORMATEX   *MixFormat;//needs to be CoTaskMemFree'd after use!\r
+    // Fields filled from IAudioDevice (_prior_ to Initialize)\r
+    // from GetDevicePeriod(\r
+    REFERENCE_TIME DefaultDevicePeriod;\r
+    REFERENCE_TIME MinimumDevicePeriod;\r
 \r
-} PaWinWasapiDeviceInfo;\r
+    // from GetMixFormat\r
+    // WAVEFORMATEX *MixFormat;//needs to be CoTaskMemFree'd after use!\r
 \r
+       // Default format (setup through Control Panel by user)\r
+       WAVEFORMATEXTENSIBLE DefaultFormat;\r
 \r
+       // Formfactor\r
+       EndpointFormFactor formFactor;\r
+} \r
+PaWasapiDeviceInfo;\r
+\r
+// ------------------------------------------------------------------------------------------\r
+/* PaWasapiHostApiRepresentation - host api datastructure specific to this implementation */\r
 typedef struct\r
 {\r
     PaUtilHostApiRepresentation inheritedHostApiRep;\r
@@ -192,7 +233,7 @@ typedef struct
     /* implementation specific data goes here */\r
 \r
     //in case we later need the synch\r
-    IMMDeviceEnumerator * enumerator;\r
+    IMMDeviceEnumerator *enumerator;\r
 \r
     //this is the REAL number of devices, whether they are usefull to PA or not!\r
     UINT deviceCount;\r
@@ -200,165 +241,303 @@ typedef struct
     WCHAR defaultRenderer [MAX_STR_LEN];\r
     WCHAR defaultCapturer [MAX_STR_LEN];\r
 \r
-    PaWinWasapiDeviceInfo   *devInfo;\r
-}PaWinWasapiHostApiRepresentation;\r
-\r
-\r
-/* PaWinWasapiStream - a stream data structure specifically for this implementation */\r
+    PaWasapiDeviceInfo   *devInfo;\r
+}\r
+PaWasapiHostApiRepresentation;\r
 \r
-typedef struct PaWinWasapiSubStream{\r
+// ------------------------------------------------------------------------------------------\r
+/* PaWasapiStream - a stream data structure specifically for this implementation */\r
+typedef struct PaWasapiSubStream\r
+{\r
     IAudioClient        *client;\r
     WAVEFORMATEXTENSIBLE wavex;\r
     UINT32               bufferSize;\r
     REFERENCE_TIME       latency;\r
     REFERENCE_TIME       period;\r
-    unsigned long framesPerHostCallback; /* just an example */\r
-}PaWinWasapiSubStream;\r
+       double                           latency_seconds;\r
+    UINT32                              framesPerHostCallback;\r
+       AUDCLNT_SHAREMODE    shareMode;\r
+       UINT32               streamFlags; // AUDCLNT_STREAMFLAGS_EVENTCALLBACK, ...\r
+       UINT32               flags;\r
+\r
+       // Used by blocking interface:\r
+       UINT32               prevTime;  // time ms between calls of WriteStream\r
+       UINT32               prevSleep; // time ms to sleep from frames written in previous call\r
+}\r
+PaWasapiSubStream;\r
+\r
+// ------------------------------------------------------------------------------------------\r
+/* PaWasapiHostProcessor - redirects processing data */\r
+typedef struct PaWasapiHostProcessor\r
+{\r
+    PaWasapiHostProcessorCallback processor;\r
+    void *userData;\r
+}\r
+PaWasapiHostProcessor;\r
 \r
-typedef struct PaWinWasapiStream\r
-{ /* IMPLEMENT ME: rename this */\r
+// ------------------------------------------------------------------------------------------\r
+typedef struct PaWasapiStream\r
+{ \r
+       /* IMPLEMENT ME: rename this */\r
     PaUtilStreamRepresentation streamRepresentation;\r
     PaUtilCpuLoadMeasurer cpuLoadMeasurer;\r
     PaUtilBufferProcessor bufferProcessor;\r
 \r
-    /* IMPLEMENT ME:\r
-            - implementation specific data goes here\r
-    */\r
-\r
-\r
-    //input\r
-       PaWinWasapiSubStream in;\r
+    // input\r
+       PaWasapiSubStream in;\r
     IAudioCaptureClient *cclient;\r
     IAudioEndpointVolume *inVol;\r
-       //output\r
-       PaWinWasapiSubStream out;\r
+       \r
+       // output\r
+       PaWasapiSubStream out;\r
     IAudioRenderClient  *rclient;\r
        IAudioEndpointVolume *outVol;\r
 \r
-    bool running;\r
-    bool closeRequest;\r
+       // must be volatile to avoid race condition on user query while\r
+       // thread is being started\r
+    volatile bool running;\r
 \r
-    DWORD dwThreadId;\r
+    PA_THREAD_ID dwThreadId;\r
     HANDLE hThread;\r
-       HANDLE hNotificationEvent; \r
+       HANDLE hCloseRequest;\r
+       HANDLE hThreadDone;\r
+       HANDLE hBlockingOpStreamRD;\r
+       HANDLE hBlockingOpStreamWR;\r
 \r
-    GUID  session;\r
+    // Host callback Output overrider\r
+       PaWasapiHostProcessor hostProcessOverrideOutput;\r
 \r
-}PaWinWasapiStream;\r
+    // Host callback Input overrider\r
+       PaWasapiHostProcessor hostProcessOverrideInput;\r
 \r
-#define PRINT(x) PA_DEBUG(x);\r
+       // Defines blocking/callback interface used\r
+       bool bBlocking;\r
 \r
-void\r
-logAUDCLNT_E(HRESULT res){\r
-\r
-    char *text = 0;\r
-    switch(res){\r
-        case S_OK: return; break;\r
-        case E_POINTER                              :text ="E_POINTER"; break;\r
-        case E_INVALIDARG                           :text ="E_INVALIDARG"; break;\r
-\r
-        case AUDCLNT_E_NOT_INITIALIZED              :text ="AUDCLNT_E_NOT_INITIALIZED"; break;\r
-        case AUDCLNT_E_ALREADY_INITIALIZED          :text ="AUDCLNT_E_ALREADY_INITIALIZED"; break;\r
-        case AUDCLNT_E_WRONG_ENDPOINT_TYPE          :text ="AUDCLNT_E_WRONG_ENDPOINT_TYPE"; break;\r
-        case AUDCLNT_E_DEVICE_INVALIDATED           :text ="AUDCLNT_E_DEVICE_INVALIDATED"; break;\r
-        case AUDCLNT_E_NOT_STOPPED                  :text ="AUDCLNT_E_NOT_STOPPED"; break;\r
-        case AUDCLNT_E_BUFFER_TOO_LARGE             :text ="AUDCLNT_E_BUFFER_TOO_LARGE"; break;\r
-        case AUDCLNT_E_OUT_OF_ORDER                 :text ="AUDCLNT_E_OUT_OF_ORDER"; break;\r
-        case AUDCLNT_E_UNSUPPORTED_FORMAT           :text ="AUDCLNT_E_UNSUPPORTED_FORMAT"; break;\r
-        case AUDCLNT_E_INVALID_SIZE                 :text ="AUDCLNT_E_INVALID_SIZE"; break;\r
-        case AUDCLNT_E_DEVICE_IN_USE                :text ="AUDCLNT_E_DEVICE_IN_USE"; break;\r
-        case AUDCLNT_E_BUFFER_OPERATION_PENDING     :text ="AUDCLNT_E_BUFFER_OPERATION_PENDING"; break;\r
-        case AUDCLNT_E_THREAD_NOT_REGISTERED        :text ="AUDCLNT_E_THREAD_NOT_REGISTERED"; break;      \r
-               case AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED   :text ="AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED"; break;\r
-        case AUDCLNT_E_ENDPOINT_CREATE_FAILED       :text ="AUDCLNT_E_ENDPOINT_CREATE_FAILED"; break;\r
-        case AUDCLNT_E_SERVICE_NOT_RUNNING          :text ="AUDCLNT_E_SERVICE_NOT_RUNNING"; break;\r
-     //  case AUDCLNT_E_CPUUSAGE_EXCEEDED            :text ="AUDCLNT_E_CPUUSAGE_EXCEEDED"; break;\r
-     //Header error?\r
-        case AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED     :text ="AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED"; break;\r
-        case AUDCLNT_E_EXCLUSIVE_MODE_ONLY          :text ="AUDCLNT_E_EXCLUSIVE_MODE_ONLY"; break;\r
-        case AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL :text ="AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL"; break;\r
-        case AUDCLNT_E_EVENTHANDLE_NOT_SET          :text ="AUDCLNT_E_EVENTHANDLE_NOT_SET"; break;\r
-        case AUDCLNT_E_INCORRECT_BUFFER_SIZE        :text ="AUDCLNT_E_INCORRECT_BUFFER_SIZE"; break;\r
-        case AUDCLNT_E_BUFFER_SIZE_ERROR            :text ="AUDCLNT_E_BUFFER_SIZE_ERROR"; break;\r
-        case AUDCLNT_S_BUFFER_EMPTY                 :text ="AUDCLNT_S_BUFFER_EMPTY"; break;\r
-        case AUDCLNT_S_THREAD_ALREADY_REGISTERED    :text ="AUDCLNT_S_THREAD_ALREADY_REGISTERED"; break;\r
-        default:\r
-            text =" dunno!";\r
-            return ;\r
-        break;\r
+       // Av Task (MM thread management)\r
+       HANDLE hAvTask;\r
+}\r
+PaWasapiStream;\r
 \r
+// ------------------------------------------------------------------------------------------\r
+#define LogHostError(HRES) __LogHostError(HRES, __FILE__, __LINE__)\r
+static HRESULT __LogHostError(HRESULT res, const char *file, int line)\r
+{\r
+    const char *text = NULL;\r
+    switch (res)\r
+       {\r
+       case S_OK: return res;\r
+       case E_POINTER                              :text ="E_POINTER"; break;\r
+       case E_INVALIDARG                           :text ="E_INVALIDARG"; break;\r
+\r
+       case AUDCLNT_E_NOT_INITIALIZED              :text ="AUDCLNT_E_NOT_INITIALIZED"; break;\r
+       case AUDCLNT_E_ALREADY_INITIALIZED          :text ="AUDCLNT_E_ALREADY_INITIALIZED"; break;\r
+       case AUDCLNT_E_WRONG_ENDPOINT_TYPE          :text ="AUDCLNT_E_WRONG_ENDPOINT_TYPE"; break;\r
+       case AUDCLNT_E_DEVICE_INVALIDATED           :text ="AUDCLNT_E_DEVICE_INVALIDATED"; break;\r
+       case AUDCLNT_E_NOT_STOPPED                  :text ="AUDCLNT_E_NOT_STOPPED"; break;\r
+       case AUDCLNT_E_BUFFER_TOO_LARGE             :text ="AUDCLNT_E_BUFFER_TOO_LARGE"; break;\r
+       case AUDCLNT_E_OUT_OF_ORDER                 :text ="AUDCLNT_E_OUT_OF_ORDER"; break;\r
+       case AUDCLNT_E_UNSUPPORTED_FORMAT           :text ="AUDCLNT_E_UNSUPPORTED_FORMAT"; break;\r
+       case AUDCLNT_E_INVALID_SIZE                 :text ="AUDCLNT_E_INVALID_SIZE"; break;\r
+       case AUDCLNT_E_DEVICE_IN_USE                :text ="AUDCLNT_E_DEVICE_IN_USE"; break;\r
+       case AUDCLNT_E_BUFFER_OPERATION_PENDING     :text ="AUDCLNT_E_BUFFER_OPERATION_PENDING"; break;\r
+       case AUDCLNT_E_THREAD_NOT_REGISTERED        :text ="AUDCLNT_E_THREAD_NOT_REGISTERED"; break;\r
+       case AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED   :text ="AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED"; break;\r
+       case AUDCLNT_E_ENDPOINT_CREATE_FAILED       :text ="AUDCLNT_E_ENDPOINT_CREATE_FAILED"; break;\r
+       case AUDCLNT_E_SERVICE_NOT_RUNNING          :text ="AUDCLNT_E_SERVICE_NOT_RUNNING"; break;\r
+       case AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED     :text ="AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED"; break;\r
+       case AUDCLNT_E_EXCLUSIVE_MODE_ONLY          :text ="AUDCLNT_E_EXCLUSIVE_MODE_ONLY"; break;\r
+       case AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL :text ="AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL"; break;\r
+       case AUDCLNT_E_EVENTHANDLE_NOT_SET          :text ="AUDCLNT_E_EVENTHANDLE_NOT_SET"; break;\r
+       case AUDCLNT_E_INCORRECT_BUFFER_SIZE        :text ="AUDCLNT_E_INCORRECT_BUFFER_SIZE"; break;\r
+       case AUDCLNT_E_BUFFER_SIZE_ERROR            :text ="AUDCLNT_E_BUFFER_SIZE_ERROR"; break;\r
+       case AUDCLNT_E_CPUUSAGE_EXCEEDED            :text ="AUDCLNT_E_CPUUSAGE_EXCEEDED"; break;        \r
+       case AUDCLNT_E_BUFFER_ERROR                                     :text ="AUDCLNT_E_BUFFER_ERROR"; break;\r
+       case AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED          :text ="AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED"; break;\r
+       case AUDCLNT_E_INVALID_DEVICE_PERIOD            :text ="AUDCLNT_E_INVALID_DEVICE_PERIOD"; break;\r
+\r
+       case AUDCLNT_S_BUFFER_EMPTY                 :text ="AUDCLNT_S_BUFFER_EMPTY"; break;\r
+       case AUDCLNT_S_THREAD_ALREADY_REGISTERED    :text ="AUDCLNT_S_THREAD_ALREADY_REGISTERED"; break;\r
+       case AUDCLNT_S_POSITION_STALLED                         :text ="AUDCLNT_S_POSITION_STALLED"; break;\r
+\r
+       default:\r
+               text = "UNKNOWN ERROR";\r
     }\r
-    PRINT(("WASAPI ERROR HRESULT: 0x%X : %s\n",res,text));\r
+       PRINT(("WASAPI ERROR HRESULT: 0x%X : %s\n [FILE: %s {LINE: %d}]\n",res, text, file, line));\r
+       PA_SKELETON_SET_LAST_HOST_ERROR(res, text);\r
+       return res;\r
 }\r
 \r
-inline double\r
-nano100ToMillis(const REFERENCE_TIME &ref){\r
+// ------------------------------------------------------------------------------------------\r
+#define LogPaError(PAERR) __LogPaError(PAERR, __FILE__, __LINE__)\r
+static PaError __LogPaError(PaError err, const char *file, int line)\r
+{\r
+       if (err == paNoError) \r
+               return err;\r
+    const char *text = Pa_GetErrorText(err);\r
+       PRINT(("WASAPI ERROR PAERROR: %i : %s\n [FILE: %s {LINE: %d}]\n",err,text,file,line));\r
+       return err;\r
+}\r
+\r
+// ------------------------------------------------------------------------------------------\r
+static inline double nano100ToMillis(const REFERENCE_TIME &ref)\r
+{\r
     //  1 nano = 0.000000001 seconds\r
     //100 nano = 0.0000001   seconds\r
     //100 nano = 0.0001   milliseconds\r
     return ((double)ref)*0.0001;\r
 }\r
 \r
-inline double\r
-nano100ToSeconds(const REFERENCE_TIME &ref){\r
+// ------------------------------------------------------------------------------------------\r
+static inline double nano100ToSeconds(const REFERENCE_TIME &ref)\r
+{\r
     //  1 nano = 0.000000001 seconds\r
     //100 nano = 0.0000001   seconds\r
     //100 nano = 0.0001   milliseconds\r
     return ((double)ref)*0.0000001;\r
 }\r
 \r
-#ifndef IF_FAILED_JUMP\r
-#define IF_FAILED_JUMP(hr, label) if(FAILED(hr)) goto label;\r
-#endif\r
+// ------------------------------------------------------------------------------------------\r
+static inline REFERENCE_TIME MillisTonano100(const double &ref)\r
+{\r
+    //  1 nano = 0.000000001 seconds\r
+    //100 nano = 0.0000001   seconds\r
+    //100 nano = 0.0001   milliseconds\r
+    return (REFERENCE_TIME)(ref/0.0001);\r
+}\r
+\r
+// ------------------------------------------------------------------------------------------\r
+static inline REFERENCE_TIME SecondsTonano100(const double &ref)\r
+{\r
+    //  1 nano = 0.000000001 seconds\r
+    //100 nano = 0.0000001   seconds\r
+    //100 nano = 0.0001   milliseconds\r
+    return (REFERENCE_TIME)(ref/0.0000001);\r
+}\r
 \r
+// ------------------------------------------------------------------------------------------\r
+// Makes Hns period from frames and sample rate\r
+static inline REFERENCE_TIME MakeHnsPeriod(UINT32 nFrames, DWORD nSamplesPerSec)\r
+{\r
+       return (REFERENCE_TIME)((10000.0 * 1000 / nSamplesPerSec * nFrames) + 0.5);\r
+}\r
 \r
+// ------------------------------------------------------------------------------------------\r
+// Converts PaSampleFormat to bits per sample value\r
+static inline WORD PaSampleFormatToBitsPerSample(PaSampleFormat format_id)\r
+{\r
+       switch (format_id & ~paNonInterleaved)\r
+       {\r
+               case paFloat32:\r
+               case paInt32: return 32;\r
+               case paInt24: return 24;\r
+               case paInt16: return 16;\r
+               case paInt8:\r
+               case paUInt8: return 8;\r
+       }\r
+       return 0;\r
+}\r
 \r
-//AVRT is the new "multimedia schedulling stuff"\r
+// ------------------------------------------------------------------------------------------\r
+// Converts PaSampleFormat to bits per sample value\r
+static inline WORD PaSampleFormatToBytesPerSample(PaSampleFormat format_id)\r
+{\r
+       return PaSampleFormatToBitsPerSample(format_id) >> 3; // 'bits/8'\r
+}\r
 \r
-typedef BOOL   (WINAPI *FAvRtCreateThreadOrderingGroup) (PHANDLE,PLARGE_INTEGER,GUID*,PLARGE_INTEGER);\r
-typedef BOOL   (WINAPI *FAvRtDeleteThreadOrderingGroup) (HANDLE);\r
-typedef BOOL   (WINAPI *FAvRtWaitOnThreadOrderingGroup) (HANDLE);\r
-typedef HANDLE (WINAPI *FAvSetMmThreadCharacteristics)  (LPCTSTR,LPDWORD);\r
-typedef BOOL   (WINAPI *FAvSetMmThreadPriority)         (HANDLE,AVRT_PRIORITY);\r
+// ------------------------------------------------------------------------------------------\r
+// Converts Hns period into number of frames\r
+static inline UINT32 MakeFramesFromHns(REFERENCE_TIME hnsPeriod, UINT32 nSamplesPerSec)\r
+{\r
+    UINT32 nFrames = (UINT32)( // frames =\r
+        1.0 * hnsPeriod *              // hns *\r
+        nSamplesPerSec /               // (frames / s) /\r
+        1000 /                                 // (ms / s) /\r
+        10000                                  // (hns / s) /\r
+        + 0.5                                  // rounding\r
+    );\r
+       return nFrames;\r
+}\r
 \r
-HMODULE  hDInputDLL = 0;\r
-FAvRtCreateThreadOrderingGroup pAvRtCreateThreadOrderingGroup=0;\r
-FAvRtDeleteThreadOrderingGroup pAvRtDeleteThreadOrderingGroup=0;\r
-FAvRtWaitOnThreadOrderingGroup pAvRtWaitOnThreadOrderingGroup=0;\r
-FAvSetMmThreadCharacteristics  pAvSetMmThreadCharacteristics=0;\r
-FAvSetMmThreadPriority         pAvSetMmThreadPriority=0;\r
+// ------------------------------------------------------------------------------------------\r
+// Aligns v backwards\r
+static inline UINT32 ALIGN_BWD(UINT32 v, UINT32 align)\r
+{\r
+       return ((v - (align ? v % align : 0)));\r
+}\r
 \r
+// ------------------------------------------------------------------------------------------\r
+// Aligns WASAPI buffer to 128 byte packet boundary. HD Audio will fail to play if buffer\r
+// is misaligned. This problem was solved in Windows 7 were AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED\r
+// is thrown although we must align for Vista anyway.\r
+static UINT32 AlignFramesPerBuffer(UINT32 nFrames, UINT32 nSamplesPerSec, UINT32 nBlockAlign)\r
+{\r
+#define HDA_PACKET_SIZE 128\r
 \r
+       long packets_total = nSamplesPerSec * nBlockAlign / HDA_PACKET_SIZE;\r
+       long frame_bytes   = nFrames * nBlockAlign;\r
+       \r
+       // align to packet size\r
+       frame_bytes        = ALIGN_BWD(frame_bytes, HDA_PACKET_SIZE);\r
+       nFrames            = frame_bytes / nBlockAlign;\r
+       long packets       = frame_bytes / HDA_PACKET_SIZE;\r
+       \r
+       // align to packets count\r
+       while (packets && ((packets_total % packets) != 0))\r
+       {\r
+               --packets;\r
+       }\r
+       \r
+       frame_bytes = packets * HDA_PACKET_SIZE;\r
+       nFrames     = frame_bytes / nBlockAlign;\r
 \r
-#define setupPTR(fun, type, name)  {                                                        \\r
-                                        fun = (type) GetProcAddress(hDInputDLL,name);       \\r
-                                        if(fun == NULL) {                                   \\r
-                                            PRINT(("GetProcAddr failed for %s" ,name));     \\r
-                                            return false;                                   \\r
-                                        }                                                   \\r
-                                    }                                                       \\r
+       return nFrames;\r
+}\r
 \r
-bool\r
-setupAVRT(){\r
+// ------------------------------------------------------------------------------------------\r
+static __forceinline UINT32 GetFramesSleepTime(UINT32 nFrames, UINT32 nSamplesPerSec)\r
+{\r
+#define REFTIMES_PER_SEC  10000000\r
+#define REFTIMES_PER_MILLISEC  10000\r
+       // Calculate the actual duration of the allocated buffer.\r
+       REFERENCE_TIME nDuration = (REFERENCE_TIME)((double)REFTIMES_PER_SEC * nFrames / nSamplesPerSec);\r
+       return (UINT32)(nDuration/REFTIMES_PER_MILLISEC/2);\r
+}\r
 \r
+// ------------------------------------------------------------------------------------------\r
+static bool SetupAVRT()\r
+{\r
     hDInputDLL = LoadLibraryA("avrt.dll");\r
     if(hDInputDLL == NULL)\r
         return false;\r
 \r
-    setupPTR(pAvRtCreateThreadOrderingGroup, FAvRtCreateThreadOrderingGroup, "AvRtCreateThreadOrderingGroup");\r
-    setupPTR(pAvRtDeleteThreadOrderingGroup, FAvRtDeleteThreadOrderingGroup, "AvRtDeleteThreadOrderingGroup");\r
-    setupPTR(pAvRtWaitOnThreadOrderingGroup, FAvRtWaitOnThreadOrderingGroup, "AvRtWaitOnThreadOrderingGroup");\r
-    setupPTR(pAvSetMmThreadCharacteristics,  FAvSetMmThreadCharacteristics,  "AvSetMmThreadCharacteristicsA");\r
-    setupPTR(pAvSetMmThreadPriority,         FAvSetMmThreadPriority,         "AvSetMmThreadPriority");\r
-\r
-    return true;\r
+    setupPTR(pAvRtCreateThreadOrderingGroup,  FAvRtCreateThreadOrderingGroup,  "AvRtCreateThreadOrderingGroup");\r
+    setupPTR(pAvRtDeleteThreadOrderingGroup,  FAvRtDeleteThreadOrderingGroup,  "AvRtDeleteThreadOrderingGroup");\r
+    setupPTR(pAvRtWaitOnThreadOrderingGroup,  FAvRtWaitOnThreadOrderingGroup,  "AvRtWaitOnThreadOrderingGroup");\r
+    setupPTR(pAvSetMmThreadCharacteristics,   FAvSetMmThreadCharacteristics,   "AvSetMmThreadCharacteristicsA");\r
+       setupPTR(pAvRevertMmThreadCharacteristics,FAvRevertMmThreadCharacteristics,"AvRevertMmThreadCharacteristics");\r
+    setupPTR(pAvSetMmThreadPriority,          FAvSetMmThreadPriority,          "AvSetMmThreadPriority");\r
+    \r
+       return pAvRtCreateThreadOrderingGroup && \r
+               pAvRtDeleteThreadOrderingGroup && \r
+               pAvRtWaitOnThreadOrderingGroup && \r
+               pAvSetMmThreadCharacteristics && \r
+               pAvRevertMmThreadCharacteristics && \r
+               pAvSetMmThreadPriority;\r
 }\r
 \r
+// ------------------------------------------------------------------------------------------\r
+static void CloseAVRT()\r
+{\r
+       if (hDInputDLL != NULL)\r
+               FreeLibrary(hDInputDLL);\r
+       hDInputDLL = NULL;\r
+}\r
 \r
-\r
-PaError PaWinWasapi_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex )\r
+// ------------------------------------------------------------------------------------------\r
+PaError PaWasapi_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex )\r
 {\r
-    if (!setupAVRT()){\r
+    if (!SetupAVRT())\r
+       {\r
         PRINT(("Windows WASAPI : No AVRT! (not VISTA?)"));\r
         return paNoError;\r
     }\r
@@ -366,111 +545,121 @@ PaError PaWinWasapi_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApi
     CoInitialize(NULL);\r
 \r
     PaError result = paNoError;\r
-    PaWinWasapiHostApiRepresentation *paWasapi;\r
+    PaWasapiHostApiRepresentation *paWasapi;\r
     PaDeviceInfo *deviceInfoArray;\r
 \r
-    paWasapi = (PaWinWasapiHostApiRepresentation*)PaUtil_AllocateMemory( sizeof(PaWinWasapiHostApiRepresentation) );\r
-    if( !paWasapi ){\r
+    paWasapi = (PaWasapiHostApiRepresentation*)PaUtil_AllocateMemory( sizeof(PaWasapiHostApiRepresentation) );\r
+    if (paWasapi == NULL)\r
+       {\r
         result = paInsufficientMemory;\r
         goto error;\r
     }\r
 \r
     paWasapi->allocations = PaUtil_CreateAllocationGroup();\r
-    if( !paWasapi->allocations ){\r
+    if (paWasapi->allocations == NULL)\r
+       {\r
         result = paInsufficientMemory;\r
         goto error;\r
     }\r
 \r
     *hostApi = &paWasapi->inheritedHostApiRep;\r
-    (*hostApi)->info.structVersion = 1;\r
-    (*hostApi)->info.type = paWASAPI;\r
-    (*hostApi)->info.name = "Windows WASAPI";\r
-    (*hostApi)->info.deviceCount = 0;   //so far, we must investigate each\r
-    (*hostApi)->info.defaultInputDevice  = paNoDevice;  /* IMPLEMENT ME */\r
-    (*hostApi)->info.defaultOutputDevice = paNoDevice; /* IMPLEMENT ME */\r
-\r
+    (*hostApi)->info.structVersion              = 1;\r
+    (*hostApi)->info.type                               = paWASAPI;\r
+    (*hostApi)->info.name                               = "Windows WASAPI";\r
+    (*hostApi)->info.deviceCount                = 0;\r
+    (*hostApi)->info.defaultInputDevice         = paNoDevice;\r
+    (*hostApi)->info.defaultOutputDevice = paNoDevice;\r
 \r
     HRESULT hResult = S_OK;\r
-    IMMDeviceCollection* spEndpoints=0;\r
+    IMMDeviceCollection* pEndPoints=0;\r
     paWasapi->enumerator = 0;\r
 \r
-    hResult = CoCreateInstance(\r
-             __uuidof(MMDeviceEnumerator), NULL,CLSCTX_INPROC_SERVER,\r
+    hResult = CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL,CLSCTX_INPROC_SERVER,\r
              __uuidof(IMMDeviceEnumerator),\r
              (void**)&paWasapi->enumerator);\r
-\r
     IF_FAILED_JUMP(hResult, error);\r
 \r
-    //getting default device ids in the eMultimedia "role"\r
+    // getting default device ids in the eMultimedia "role"\r
     {\r
         {\r
             IMMDevice* defaultRenderer=0;\r
             hResult = paWasapi->enumerator->GetDefaultAudioEndpoint(eRender, eMultimedia, &defaultRenderer);\r
-            IF_FAILED_JUMP(hResult, error);\r
-            WCHAR* pszDeviceId = NULL;\r
-            hResult = defaultRenderer->GetId(&pszDeviceId);\r
-            IF_FAILED_JUMP(hResult, error);\r
-            StringCchCopyW(paWasapi->defaultRenderer, MAX_STR_LEN-1, pszDeviceId);\r
-            CoTaskMemFree(pszDeviceId);\r
-            defaultRenderer->Release();\r
+            if (hResult != S_OK)\r
+                       {\r
+                               if (hResult != E_NOTFOUND)\r
+                                       IF_FAILED_JUMP(hResult, error);\r
+                       }\r
+                       else\r
+                       {\r
+                               WCHAR* pszDeviceId = NULL;\r
+                               hResult = defaultRenderer->GetId(&pszDeviceId);\r
+                               IF_FAILED_JUMP(hResult, error);\r
+                               StringCchCopyW(paWasapi->defaultRenderer, MAX_STR_LEN-1, pszDeviceId);\r
+                               CoTaskMemFree(pszDeviceId);\r
+                               defaultRenderer->Release();\r
+                       }\r
         }\r
 \r
         {\r
             IMMDevice* defaultCapturer=0;\r
             hResult = paWasapi->enumerator->GetDefaultAudioEndpoint(eCapture, eMultimedia, &defaultCapturer);\r
-            IF_FAILED_JUMP(hResult, error);\r
-            WCHAR* pszDeviceId = NULL;\r
-            hResult = defaultCapturer->GetId(&pszDeviceId);\r
-            IF_FAILED_JUMP(hResult, error);\r
-            StringCchCopyW(paWasapi->defaultCapturer, MAX_STR_LEN-1, pszDeviceId);\r
-            CoTaskMemFree(pszDeviceId);\r
-            defaultCapturer->Release();\r
+            if (hResult != S_OK)\r
+                       {\r
+                               if (hResult != E_NOTFOUND)\r
+                                       IF_FAILED_JUMP(hResult, error);\r
+                       }\r
+                       else\r
+                       {\r
+                               WCHAR* pszDeviceId = NULL;\r
+                               hResult = defaultCapturer->GetId(&pszDeviceId);\r
+                               IF_FAILED_JUMP(hResult, error);\r
+                               StringCchCopyW(paWasapi->defaultCapturer, MAX_STR_LEN-1, pszDeviceId);\r
+                               CoTaskMemFree(pszDeviceId);\r
+                               defaultCapturer->Release();\r
+                       }\r
         }\r
     }\r
 \r
-\r
-    hResult = paWasapi->enumerator->EnumAudioEndpoints(eAll, DEVICE_STATE_ACTIVE, &spEndpoints);\r
+    hResult = paWasapi->enumerator->EnumAudioEndpoints(eAll, DEVICE_STATE_ACTIVE, &pEndPoints);\r
     IF_FAILED_JUMP(hResult, error);\r
 \r
-    hResult = spEndpoints->GetCount(&paWasapi->deviceCount);\r
+    hResult = pEndPoints->GetCount(&paWasapi->deviceCount);\r
     IF_FAILED_JUMP(hResult, error);\r
 \r
-    paWasapi->devInfo = new PaWinWasapiDeviceInfo[paWasapi->deviceCount];\r
-    {\r
-        for (size_t step=0;step<paWasapi->deviceCount;++step)\r
-            memset(&paWasapi->devInfo[step],0,sizeof(PaWinWasapiDeviceInfo));\r
-    }\r
-\r
-\r
+    paWasapi->devInfo = new PaWasapiDeviceInfo[paWasapi->deviceCount];\r
+       for (UINT i = 0; i < paWasapi->deviceCount; ++i)\r
+               memset(&paWasapi->devInfo[i], 0, sizeof(PaWasapiDeviceInfo));\r
 \r
-    if( paWasapi->deviceCount > 0 )\r
+    if (paWasapi->deviceCount > 0)\r
     {\r
         (*hostApi)->deviceInfos = (PaDeviceInfo**)PaUtil_GroupAllocateMemory(\r
-                paWasapi->allocations, sizeof(PaDeviceInfo*) * paWasapi->deviceCount );\r
-        if( !(*hostApi)->deviceInfos ){\r
+                paWasapi->allocations, sizeof(PaDeviceInfo*) * paWasapi->deviceCount);\r
+        if (!(*hostApi)->deviceInfos)\r
+               {\r
             result = paInsufficientMemory;\r
             goto error;\r
         }\r
 \r
         /* allocate all device info structs in a contiguous block */\r
         deviceInfoArray = (PaDeviceInfo*)PaUtil_GroupAllocateMemory(\r
-                paWasapi->allocations, sizeof(PaDeviceInfo) * paWasapi->deviceCount );\r
-        if( !deviceInfoArray ){\r
+                paWasapi->allocations, sizeof(PaDeviceInfo) * paWasapi->deviceCount);\r
+        if (!deviceInfoArray)\r
+               {\r
             result = paInsufficientMemory;\r
             goto error;\r
         }\r
 \r
-        for( UINT i=0; i < paWasapi->deviceCount; ++i ){\r
-\r
+        for (UINT i = 0; i < paWasapi->deviceCount; ++i)\r
+               {\r
                        PA_DEBUG(("i:%d\n",i));\r
-            PaDeviceInfo *deviceInfo = &deviceInfoArray[i];\r
+            PaDeviceInfo *deviceInfo  = &deviceInfoArray[i];\r
             deviceInfo->structVersion = 2;\r
-            deviceInfo->hostApi = hostApiIndex;\r
+            deviceInfo->hostApi       = hostApiIndex;\r
 \r
-            hResult = spEndpoints->Item(i, &paWasapi->devInfo[i].device);\r
+            hResult = pEndPoints->Item(i, &paWasapi->devInfo[i].device);\r
             IF_FAILED_JUMP(hResult, error);\r
 \r
-            //getting ID\r
+            // getting ID\r
             {\r
                 WCHAR* pszDeviceId = NULL;\r
                 hResult = paWasapi->devInfo[i].device->GetId(&pszDeviceId);\r
@@ -478,169 +667,153 @@ PaError PaWinWasapi_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApi
                 StringCchCopyW(paWasapi->devInfo[i].szDeviceID, MAX_STR_LEN-1, pszDeviceId);\r
                 CoTaskMemFree(pszDeviceId);\r
 \r
-                if (lstrcmpW(paWasapi->devInfo[i].szDeviceID, paWasapi->defaultCapturer)==0){\r
-                    //we found the default input!\r
+                if (lstrcmpW(paWasapi->devInfo[i].szDeviceID, paWasapi->defaultCapturer) == 0)\r
+                               {// we found the default input!\r
                     (*hostApi)->info.defaultInputDevice = (*hostApi)->info.deviceCount;\r
                 }\r
-                if (lstrcmpW(paWasapi->devInfo[i].szDeviceID, paWasapi->defaultRenderer)==0){\r
-                    //we found the default output!\r
+                if (lstrcmpW(paWasapi->devInfo[i].szDeviceID, paWasapi->defaultRenderer) == 0)\r
+                               {// we found the default output!\r
                     (*hostApi)->info.defaultOutputDevice = (*hostApi)->info.deviceCount;\r
                 }\r
             }\r
 \r
-            DWORD state=0;\r
+            DWORD state = 0;\r
             hResult = paWasapi->devInfo[i].device->GetState(&paWasapi->devInfo[i].state);\r
             IF_FAILED_JUMP(hResult, error);\r
 \r
-            if (paWasapi->devInfo[i].state != DEVICE_STATE_ACTIVE){\r
+            if (paWasapi->devInfo[i].state != DEVICE_STATE_ACTIVE)\r
+                       {\r
                 PRINT(("WASAPI device:%d is not currently available (state:%d)\n",i,state));\r
-                //spDevice->Release();\r
-                //continue;\r
             }\r
 \r
             {\r
-                IPropertyStore* spProperties;\r
-                hResult = paWasapi->devInfo[i].device->OpenPropertyStore(STGM_READ, &spProperties);\r
+                IPropertyStore* pProperty;\r
+                hResult = paWasapi->devInfo[i].device->OpenPropertyStore(STGM_READ, &pProperty);\r
                 IF_FAILED_JUMP(hResult, error);\r
 \r
-                //getting "Friendly" Name\r
+                // "Friendly" Name\r
                 {\r
                     PROPVARIANT value;\r
                     PropVariantInit(&value);\r
-                    hResult = spProperties->GetValue(PKEY_Device_FriendlyName, &value);\r
+                    hResult = pProperty->GetValue(PKEY_Device_FriendlyName, &value);\r
                     IF_FAILED_JUMP(hResult, error);\r
-                    deviceInfo->name = 0;\r
-                    char* deviceName = (char*)PaUtil_GroupAllocateMemory( paWasapi->allocations, MAX_STR_LEN + 1 );\r
-                    if( !deviceName ){\r
+                    deviceInfo->name = NULL;\r
+                    char *deviceName = (char *)PaUtil_GroupAllocateMemory( paWasapi->allocations, MAX_STR_LEN + 1 );\r
+                    if (!deviceName)\r
+                                       {\r
                         result = paInsufficientMemory;\r
                         goto error;\r
                     }\r
                                        if (value.pwszVal)\r
-                                               wcstombs(deviceName,   value.pwszVal,MAX_STR_LEN-1); //todo proper size \r
-                                       else{\r
+                                               wcstombs(deviceName,   value.pwszVal,MAX_STR_LEN-1);\r
+                                       else\r
                                                _snprintf_s(deviceName,MAX_STR_LEN-1,MAX_STR_LEN-1,"baddev%d",i);\r
-                                       }\r
 \r
                     deviceInfo->name = deviceName;\r
                     PropVariantClear(&value);\r
                 }\r
 \r
-#if 0\r
-                DWORD numProps = 0;\r
-                hResult = spProperties->GetCount(&numProps);\r
-                IF_FAILED_JUMP(hResult, error);\r
+                // Default format\r
                 {\r
-                    for (DWORD i=0;i<numProps;++i){\r
-                        PROPERTYKEY pkey;\r
-                        hResult = spProperties->GetAt(i,&pkey);\r
-\r
-                        PROPVARIANT value;\r
-                        PropVariantInit(&value);\r
-                        hResult = spProperties->GetValue(pkey, &value);\r
-\r
-                        switch(value.vt){\r
-                            case 11:\r
-                                PRINT(("property*%u*\n",value.ulVal));\r
-                            break;\r
-                            case 19:\r
-                                PRINT(("property*%d*\n",value.boolVal));\r
-                            break;\r
-                            case 31:\r
-                            {\r
-                                char temp[512];\r
-                                wcstombs(temp,    value.pwszVal,MAX_STR_LEN-1);\r
-                                PRINT(("property*%s*\n",temp));\r
-                            }\r
-                            break;\r
-                            default:break;\r
-                        }\r
-\r
-                        PropVariantClear(&value);\r
-                    }\r
+                    PROPVARIANT value;\r
+                    PropVariantInit(&value);\r
+                    hResult = pProperty->GetValue(PKEY_AudioEngine_DeviceFormat, &value);\r
+                    IF_FAILED_JUMP(hResult, error);\r
+                                       // get\r
+                                       WAVEFORMATEXTENSIBLE format = { 0 };\r
+                                       memcpy(&format, value.blob.pBlobData, min(sizeof(format), value.blob.cbSize));\r
+                                       // set\r
+                                       paWasapi->devInfo[i].DefaultFormat = format;\r
+                    // cleanup\r
+                    PropVariantClear(&value);\r
+                }\r
+\r
+                // Formfactor\r
+                {\r
+                    PROPVARIANT value;\r
+                    PropVariantInit(&value);\r
+                    hResult = pProperty->GetValue(PKEY_AudioEndpoint_FormFactor, &value);\r
+                    IF_FAILED_JUMP(hResult, error);\r
+                                       // set\r
+                                       paWasapi->devInfo[i].formFactor = (EndpointFormFactor)value.uintVal;\r
+                    // cleanup\r
+                    PropVariantClear(&value);\r
                 }\r
-#endif\r
 \r
-                /*  These look interresting... but they are undocumented\r
-                PKEY_AudioEndpoint_FormFactor\r
-                PKEY_AudioEndpoint_ControlPanelPageProvider\r
-                PKEY_AudioEndpoint_Association\r
-                PKEY_AudioEndpoint_PhysicalSpeakerConfig\r
-                PKEY_AudioEngine_DeviceFormat\r
-                */\r
-                spProperties->Release();\r
+                               pProperty->Release();\r
             }\r
 \r
 \r
-            //getting the Endpoint data\r
+            // Endpoint data\r
             {\r
-                IMMEndpoint *endpoint=0;\r
-                hResult = paWasapi->devInfo[i].device->QueryInterface(__uuidof(IMMEndpoint),(void **)&endpoint);\r
-                if (SUCCEEDED(hResult)){\r
+                IMMEndpoint *endpoint = NULL;\r
+                hResult = paWasapi->devInfo[i].device->QueryInterface(__uuidof(IMMEndpoint), (void **)&endpoint);\r
+                if (SUCCEEDED(hResult))\r
+                               {\r
                     hResult = endpoint->GetDataFlow(&paWasapi->devInfo[i].flow);\r
                     endpoint->Release();\r
                 }\r
             }\r
 \r
-            //Getting a temporary IAudioDevice for more fields\r
-            //we make sure NOT to call Initialize yet!\r
+            // Getting a temporary IAudioClient for more fields\r
+            // we make sure NOT to call Initialize yet!\r
             {\r
-                IAudioClient *myClient=0;\r
+                IAudioClient *tmpClient = NULL;\r
 \r
-                hResult = paWasapi->devInfo[i].device->Activate(__uuidof(IAudioClient), CLSCTX_INPROC_SERVER, NULL, (void**)&myClient);\r
+                hResult = paWasapi->devInfo[i].device->Activate(__uuidof(IAudioClient), \r
+                                       CLSCTX_INPROC_SERVER, NULL, (void**)&tmpClient);\r
                 IF_FAILED_JUMP(hResult, error);\r
 \r
-                hResult = myClient->GetDevicePeriod(\r
+                hResult = tmpClient->GetDevicePeriod(\r
                     &paWasapi->devInfo[i].DefaultDevicePeriod,\r
                     &paWasapi->devInfo[i].MinimumDevicePeriod);\r
                 IF_FAILED_JUMP(hResult, error);\r
 \r
-                hResult = myClient->GetMixFormat(&paWasapi->devInfo[i].MixFormat);\r
+                //hResult = tmpClient->GetMixFormat(&paWasapi->devInfo[i].MixFormat);\r
 \r
-                               if (hResult != S_OK){\r
-                                       /*davidv: this happened with my hardware, previously for that same device in DirectSound:\r
-                                         Digital Output (Realtek AC'97 Audio)'s GUID: {0x38f2cf50,0x7b4c,0x4740,0x86,0xeb,0xd4,0x38,0x66,0xd8,0xc8, 0x9f} \r
-                                         so something must be _really_ wrong with this device, TODO handle this better. We kind of need GetMixFormat*/\r
-                                       logAUDCLNT_E(hResult);\r
+                               // Release client\r
+                               tmpClient->Release();\r
+\r
+                               if (hResult != S_OK)\r
+                               {\r
+                                       //davidv: this happened with my hardware, previously for that same device in DirectSound:\r
+                                       //Digital Output (Realtek AC'97 Audio)'s GUID: {0x38f2cf50,0x7b4c,0x4740,0x86,0xeb,0xd4,0x38,0x66,0xd8,0xc8, 0x9f}\r
+                                       //so something must be _really_ wrong with this device, TODO handle this better. We kind of need GetMixFormat\r
+                                       LogHostError(hResult);\r
                                        goto error;\r
                                }\r
-\r
-                myClient->Release();\r
             }\r
 \r
-            //we can now fill in portaudio device data\r
+            // we can now fill in portaudio device data\r
             deviceInfo->maxInputChannels  = 0;  //for now\r
             deviceInfo->maxOutputChannels = 0;  //for now\r
-\r
-            switch(paWasapi->devInfo[i].flow){\r
-                case eRender:\r
-                    //hum not exaclty maximum, more like "default"\r
-                    deviceInfo->maxOutputChannels = paWasapi->devInfo[i].MixFormat->nChannels;\r
-\r
-                    deviceInfo->defaultHighOutputLatency = nano100ToSeconds(paWasapi->devInfo[i].DefaultDevicePeriod);\r
-                    deviceInfo->defaultLowOutputLatency  = nano100ToSeconds(paWasapi->devInfo[i].MinimumDevicePeriod);\r
-                break;\r
-                case eCapture:\r
-                    //hum not exaclty maximum, more like "default"\r
-                    deviceInfo->maxInputChannels  = paWasapi->devInfo[i].MixFormat->nChannels;\r
-\r
-                    deviceInfo->defaultHighInputLatency = nano100ToSeconds(paWasapi->devInfo[i].DefaultDevicePeriod);\r
-                    deviceInfo->defaultLowInputLatency  = nano100ToSeconds(paWasapi->devInfo[i].MinimumDevicePeriod);\r
-                break;\r
-                default:\r
-                    PRINT(("WASAPI device:%d bad Data FLow! \n",i));\r
-                    goto error;\r
-                break;\r
+            switch (paWasapi->devInfo[i].flow)\r
+                       {\r
+            case eRender:\r
+                // WASAPI accepts exact channels count\r
+                deviceInfo->maxOutputChannels           = paWasapi->devInfo[i].DefaultFormat.Format.nChannels;\r
+                deviceInfo->defaultHighOutputLatency = nano100ToSeconds(paWasapi->devInfo[i].DefaultDevicePeriod);\r
+                deviceInfo->defaultLowOutputLatency  = nano100ToSeconds(paWasapi->devInfo[i].MinimumDevicePeriod);\r
+            break;\r
+            case eCapture:\r
+                // WASAPI accepts exact channels count\r
+                deviceInfo->maxInputChannels           = paWasapi->devInfo[i].DefaultFormat.Format.nChannels;\r
+                deviceInfo->defaultHighInputLatency = nano100ToSeconds(paWasapi->devInfo[i].DefaultDevicePeriod);\r
+                deviceInfo->defaultLowInputLatency  = nano100ToSeconds(paWasapi->devInfo[i].MinimumDevicePeriod);\r
+            break;\r
+            default:\r
+                PRINT(("WASAPI device:%d bad Data FLow! \n",i));\r
+                goto error;\r
+            break;\r
             }\r
 \r
-            deviceInfo->defaultSampleRate = (double)paWasapi->devInfo[i].MixFormat->nSamplesPerSec;\r
+                       deviceInfo->defaultSampleRate = paWasapi->devInfo[i].DefaultFormat.Format.nSamplesPerSec;\r
 \r
             (*hostApi)->deviceInfos[i] = deviceInfo;\r
             ++(*hostApi)->info.deviceCount;\r
         }\r
     }\r
 \r
-    spEndpoints->Release();\r
-\r
     (*hostApi)->Terminate = Terminate;\r
     (*hostApi)->OpenStream = OpenStream;\r
     (*hostApi)->IsFormatSupported = IsFormatSupported;\r
@@ -656,144 +829,177 @@ PaError PaWinWasapi_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApi
                                       GetStreamTime, PaUtil_DummyGetCpuLoad,\r
                                       ReadStream, WriteStream, GetStreamReadAvailable, GetStreamWriteAvailable );\r
 \r
+    SAFE_RELEASE(pEndPoints);\r
+\r
     return result;\r
 \r
 error:\r
 \r
-    if (spEndpoints)\r
-        spEndpoints->Release();\r
-\r
-    if (paWasapi->enumerator)\r
-        paWasapi->enumerator->Release();\r
-\r
-    if( paWasapi )\r
-    {\r
-        if( paWasapi->allocations )\r
-        {\r
-            PaUtil_FreeAllAllocations( paWasapi->allocations );\r
-            PaUtil_DestroyAllocationGroup( paWasapi->allocations );\r
-        }\r
-\r
-        PaUtil_FreeMemory( paWasapi );\r
-    }\r
+    SAFE_RELEASE(pEndPoints);\r
+       \r
+       Terminate((PaUtilHostApiRepresentation *)paWasapi);\r
+       \r
     return result;\r
 }\r
 \r
-\r
-static void Terminate( struct PaUtilHostApiRepresentation *hostApi )\r
+// ------------------------------------------------------------------------------------------\r
+static void Terminate( PaUtilHostApiRepresentation *hostApi )\r
 {\r
-    PaWinWasapiHostApiRepresentation *paWasapi = (PaWinWasapiHostApiRepresentation*)hostApi;\r
-\r
-    paWasapi->enumerator->Release();\r
+    PaWasapiHostApiRepresentation *paWasapi = (PaWasapiHostApiRepresentation*)hostApi;\r
+       if (paWasapi == NULL)\r
+               return;\r
 \r
-    for (UINT i=0;i<paWasapi->deviceCount;++i){\r
-        PaWinWasapiDeviceInfo *info = &paWasapi->devInfo[i];\r
+       // Release IMMDeviceEnumerator\r
+    SAFE_RELEASE(paWasapi->enumerator);\r
 \r
-        if (info->device)\r
-            info->device->Release();\r
+    for (UINT i = 0; i < paWasapi->deviceCount; ++i)\r
+       {\r
+        PaWasapiDeviceInfo *info = &paWasapi->devInfo[i];\r
+        SAFE_RELEASE(info->device);\r
 \r
-        if (info->MixFormat)\r
-            CoTaskMemFree(info->MixFormat);\r
+               //if (info->MixFormat)\r
+        //    CoTaskMemFree(info->MixFormat);\r
     }\r
     delete [] paWasapi->devInfo;\r
 \r
     CoUninitialize();\r
 \r
-    if( paWasapi->allocations ){\r
-        PaUtil_FreeAllAllocations( paWasapi->allocations );\r
-        PaUtil_DestroyAllocationGroup( paWasapi->allocations );\r
+    if (paWasapi->allocations)\r
+       {\r
+        PaUtil_FreeAllAllocations(paWasapi->allocations);\r
+        PaUtil_DestroyAllocationGroup(paWasapi->allocations);\r
     }\r
 \r
-    PaUtil_FreeMemory( paWasapi );\r
+    PaUtil_FreeMemory(paWasapi);\r
+\r
+       // Close AVRT\r
+       CloseAVRT();\r
 }\r
 \r
-static void\r
-LogWAVEFORMATEXTENSIBLE(const WAVEFORMATEXTENSIBLE *in){\r
+// ------------------------------------------------------------------------------------------\r
+static inline PaWasapiHostApiRepresentation *GetHostApi(PaError *_error = NULL)\r
+{\r
+       PaError error;\r
+\r
+       PaUtilHostApiRepresentation *pApi;\r
+       if ((error = PaUtil_GetHostApiRepresentation(&pApi, paWASAPI)) != paNoError)\r
+       {\r
+               if (_error != NULL)\r
+                       (*_error) = error;\r
 \r
-    const WAVEFORMATEX *old = (WAVEFORMATEX *)in;\r
+               return NULL;\r
+       }\r
+       return (PaWasapiHostApiRepresentation *)pApi;\r
+}\r
 \r
-       switch (old->wFormatTag){\r
-               case WAVE_FORMAT_EXTENSIBLE:{\r
+// ------------------------------------------------------------------------------------------\r
+int PaWasapi_GetDeviceDefaultFormat( void *pFormat, int nFormatSize, PaDeviceIndex nDevice )\r
+{\r
+       if (pFormat == NULL)\r
+               return paBadBufferPtr;\r
+       if (nFormatSize <= 0)\r
+               return paBufferTooSmall;\r
+       \r
+       PaError ret;\r
 \r
-                       PRINT(("wFormatTag=WAVE_FORMAT_EXTENSIBLE\n"));\r
+       // Get API\r
+       PaWasapiHostApiRepresentation *paWasapi = GetHostApi(&ret);\r
+       if (paWasapi == NULL)\r
+               return ret;\r
 \r
-                       if (in->SubFormat == KSDATAFORMAT_SUBTYPE_IEEE_FLOAT){\r
-                               PRINT(("SubFormat=KSDATAFORMAT_SUBTYPE_IEEE_FLOAT\n"));\r
-                       }\r
-                       else if (in->SubFormat == KSDATAFORMAT_SUBTYPE_PCM){\r
-                               PRINT(("SubFormat=KSDATAFORMAT_SUBTYPE_PCM\n"));\r
-                       }\r
-                       else{\r
-                               PRINT(("SubFormat=CUSTOM GUID{%d:%d:%d:%d%d%d%d%d%d%d%d}\n",    \r
-                                                                                       in->SubFormat.Data1,\r
-                                                                                       in->SubFormat.Data2,\r
-                                                                                       in->SubFormat.Data3,\r
-                                                                                       (int)in->SubFormat.Data4[0],\r
-                                                                                       (int)in->SubFormat.Data4[1],\r
-                                                                                       (int)in->SubFormat.Data4[2],\r
-                                                                                       (int)in->SubFormat.Data4[3],\r
-                                                                                       (int)in->SubFormat.Data4[4],\r
-                                                                                       (int)in->SubFormat.Data4[5],\r
-                                                                                       (int)in->SubFormat.Data4[6],\r
-                                                                                       (int)in->SubFormat.Data4[7]));\r
-                       }\r
-                       PRINT(("Samples.wValidBitsPerSample=%d\n",  in->Samples.wValidBitsPerSample));\r
-                       PRINT(("dwChannelMask=0x%X\n",in->dwChannelMask));\r
-               }break;\r
-               \r
-               case WAVE_FORMAT_PCM:        PRINT(("wFormatTag=WAVE_FORMAT_PCM\n")); break;\r
-               case WAVE_FORMAT_IEEE_FLOAT: PRINT(("wFormatTag=WAVE_FORMAT_IEEE_FLOAT\n")); break;\r
-               default : PRINT(("wFormatTag=UNKNOWN(%d)\n",old->wFormatTag)); break;\r
-       }\r
+       if (nDevice >= (int)paWasapi->deviceCount)\r
+               return paInvalidDevice;\r
 \r
-       PRINT(("nChannels      =%d\n",old->nChannels)); \r
-       PRINT(("nSamplesPerSec =%d\n",old->nSamplesPerSec));  \r
-       PRINT(("nAvgBytesPerSec=%d\n",old->nAvgBytesPerSec));  \r
-       PRINT(("nBlockAlign    =%d\n",old->nBlockAlign));  \r
-       PRINT(("wBitsPerSample =%d\n",old->wBitsPerSample));  \r
-       PRINT(("cbSize         =%d\n",old->cbSize));  \r
+       int size = min(nFormatSize, sizeof(paWasapi->devInfo[ nDevice ].DefaultFormat));\r
+       memcpy(pFormat, &paWasapi->devInfo[ nDevice ].DefaultFormat, size);\r
+\r
+       return size;\r
 }\r
 \r
+// ------------------------------------------------------------------------------------------\r
+int PaWasapi_GetDeviceRole( PaDeviceIndex nDevice )\r
+{\r
+       PaError ret;\r
 \r
+       // Get API\r
+       PaWasapiHostApiRepresentation *paWasapi = GetHostApi(&ret);\r
+       if (paWasapi == NULL)\r
+               return ret;\r
 \r
-/*\r
- WAVEFORMATXXX is always interleaved\r
- */\r
-static PaSampleFormat\r
-waveformatToPaFormat(const WAVEFORMATEXTENSIBLE *in){\r
+       if (nDevice >= (int)paWasapi->deviceCount)\r
+               return paInvalidDevice;\r
 \r
-    const WAVEFORMATEX *old = (WAVEFORMATEX*)in;\r
+       return paWasapi->devInfo[ nDevice ].formFactor;\r
+}\r
 \r
-    switch (old->wFormatTag){\r
+// ------------------------------------------------------------------------------------------\r
+static void LogWAVEFORMATEXTENSIBLE(const WAVEFORMATEXTENSIBLE *in)\r
+{\r
+    const WAVEFORMATEX *old = (WAVEFORMATEX *)in;\r
 \r
-        case WAVE_FORMAT_EXTENSIBLE:\r
-        {\r
-            if (in->SubFormat == KSDATAFORMAT_SUBTYPE_IEEE_FLOAT){\r
-                if (in->Samples.wValidBitsPerSample == 32)\r
-                    return paFloat32;\r
-                else\r
-                    return paCustomFormat;\r
-            }\r
-            else if (in->SubFormat == KSDATAFORMAT_SUBTYPE_PCM){\r
-                switch (old->wBitsPerSample){\r
-                    case 32: return paInt32; break;\r
-                    case 24: return paInt24;break;\r
-                    case  8: return paUInt8;break;\r
-                    case 16: return paInt16;break;\r
-                    default: return paCustomFormat;break;\r
-                }\r
-            }\r
+       switch (old->wFormatTag)\r
+       {\r
+       case WAVE_FORMAT_EXTENSIBLE: {\r
+\r
+               PRINT(("wFormatTag=WAVE_FORMAT_EXTENSIBLE\n"));\r
+\r
+               if (in->SubFormat == KSDATAFORMAT_SUBTYPE_IEEE_FLOAT){\r
+                       PRINT(("SubFormat=KSDATAFORMAT_SUBTYPE_IEEE_FLOAT\n"));\r
+               }\r
+               else if (in->SubFormat == KSDATAFORMAT_SUBTYPE_PCM){\r
+                       PRINT(("SubFormat=KSDATAFORMAT_SUBTYPE_PCM\n"));\r
+               }\r
+               else{\r
+                       PRINT(("SubFormat=CUSTOM GUID{%d:%d:%d:%d%d%d%d%d%d%d%d}\n",\r
+                                                                               in->SubFormat.Data1,\r
+                                                                               in->SubFormat.Data2,\r
+                                                                               in->SubFormat.Data3,\r
+                                                                               (int)in->SubFormat.Data4[0],\r
+                                                                               (int)in->SubFormat.Data4[1],\r
+                                                                               (int)in->SubFormat.Data4[2],\r
+                                                                               (int)in->SubFormat.Data4[3],\r
+                                                                               (int)in->SubFormat.Data4[4],\r
+                                                                               (int)in->SubFormat.Data4[5],\r
+                                                                               (int)in->SubFormat.Data4[6],\r
+                                                                               (int)in->SubFormat.Data4[7]));\r
+               }\r
+               PRINT(("Samples.wValidBitsPerSample=%d\n",  in->Samples.wValidBitsPerSample));\r
+               PRINT(("dwChannelMask  =0x%X\n",in->dwChannelMask));\r
+\r
+               break; }\r
+\r
+       case WAVE_FORMAT_PCM:        PRINT(("wFormatTag=WAVE_FORMAT_PCM\n")); break;\r
+       case WAVE_FORMAT_IEEE_FLOAT: PRINT(("wFormatTag=WAVE_FORMAT_IEEE_FLOAT\n")); break;\r
+       default : PRINT(("wFormatTag=UNKNOWN(%d)\n",old->wFormatTag)); break;\r
+       }\r
+\r
+       PRINT(("nChannels      =%d\n",old->nChannels));\r
+       PRINT(("nSamplesPerSec =%d\n",old->nSamplesPerSec));\r
+       PRINT(("nAvgBytesPerSec=%d\n",old->nAvgBytesPerSec));\r
+       PRINT(("nBlockAlign    =%d\n",old->nBlockAlign));\r
+       PRINT(("wBitsPerSample =%d\n",old->wBitsPerSample));\r
+       PRINT(("cbSize         =%d\n",old->cbSize));\r
+}\r
+\r
+// ------------------------------------------------------------------------------------------\r
+static PaSampleFormat waveformatToPaFormat(const WAVEFORMATEXTENSIBLE *in)\r
+{\r
+    const WAVEFORMATEX *old = (WAVEFORMATEX*)in;\r
+\r
+    switch (old->wFormatTag)\r
+       {\r
+    case WAVE_FORMAT_EXTENSIBLE:\r
+    {\r
+        if (in->SubFormat == KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)\r
+               {\r
+            if (in->Samples.wValidBitsPerSample == 32)\r
+                return paFloat32;\r
             else\r
                 return paCustomFormat;\r
         }\r
-        break;\r
-\r
-        case WAVE_FORMAT_IEEE_FLOAT:\r
-            return paFloat32;\r
-        break;\r
-\r
-        case WAVE_FORMAT_PCM:\r
-        {\r
+        else \r
+               if (in->SubFormat == KSDATAFORMAT_SUBTYPE_PCM)\r
+               {\r
             switch (old->wBitsPerSample){\r
                 case 32: return paInt32; break;\r
                 case 24: return paInt24;break;\r
@@ -802,92 +1008,110 @@ waveformatToPaFormat(const WAVEFORMATEXTENSIBLE *in){
                 default: return paCustomFormat;break;\r
             }\r
         }\r
-        break;\r
-\r
-        default:\r
+        else\r
             return paCustomFormat;\r
-        break;\r
+    }\r
+    break;\r
+\r
+    case WAVE_FORMAT_IEEE_FLOAT:\r
+        return paFloat32;\r
+    break;\r
+\r
+    case WAVE_FORMAT_PCM:\r
+    {\r
+        switch (old->wBitsPerSample)\r
+               {\r
+            case 32: return paInt32; break;\r
+            case 24: return paInt24;break;\r
+            case  8: return paUInt8;break;\r
+            case 16: return paInt16;break;\r
+            default: return paCustomFormat;break;\r
+        }\r
+    }\r
+    break;\r
+\r
+    default:\r
+        return paCustomFormat;\r
+    break;\r
     }\r
 \r
     return paCustomFormat;\r
 }\r
 \r
+// ------------------------------------------------------------------------------------------\r
+static PaError MakeWaveFormatFromParams(WAVEFORMATEXTENSIBLE *wavex, const PaStreamParameters *params,\r
+                                                                       double sampleRate)\r
+{\r
+       PaWasapiStreamInfo *streamInfo = (PaWasapiStreamInfo *)params->hostApiSpecificStreamInfo;\r
 \r
+       // Get user assigned channel mask\r
+       DWORD channelMask = 0;\r
+       if ((streamInfo != NULL) && (streamInfo->flags & paWinWasapiUseChannelMask))\r
+               channelMask = streamInfo->channelMask;\r
 \r
-static PaError\r
-waveformatFromParams(WAVEFORMATEXTENSIBLE*wavex,\r
-                          const PaStreamParameters * params,\r
-                          double sampleRate){\r
-\r
-    size_t bytesPerSample = 0;\r
-    switch( params->sampleFormat & ~paNonInterleaved ){\r
-        case paFloat32:\r
-        case paInt32: bytesPerSample=4;break;\r
-        case paInt16: bytesPerSample=2;break;\r
-        case paInt24: bytesPerSample=3;break;\r
-        case paInt8:\r
-        case paUInt8: bytesPerSample=1;break;\r
-        case paCustomFormat:\r
-        default: return paSampleFormatNotSupported;break;\r
-    }\r
+       // Convert PaSampleFormat to bits per sample\r
+    WORD bitsPerSample;\r
+       if ((bitsPerSample = PaSampleFormatToBitsPerSample(params->sampleFormat)) == 0)\r
+               return paSampleFormatNotSupported;\r
 \r
-    memset(wavex,0,sizeof(WAVEFORMATEXTENSIBLE));\r
+    memset(wavex, 0, sizeof(*wavex));\r
 \r
     WAVEFORMATEX *old    = (WAVEFORMATEX *)wavex;\r
-    old->nChannels       = (WORD)params->channelCount;\r
+    old->nChannels       = params->channelCount;\r
     old->nSamplesPerSec  = (DWORD)sampleRate;\r
-    old->wBitsPerSample  = (WORD)(bytesPerSample*8);\r
-    old->nAvgBytesPerSec = (DWORD)(old->nSamplesPerSec * old->nChannels * bytesPerSample);\r
-    old->nBlockAlign     = (WORD)(old->nChannels * bytesPerSample);\r
+       if ((old->wBitsPerSample = bitsPerSample) > 16)\r
+       {\r
+               old->wBitsPerSample = 32; // 20 or 24 bits must go in 32 bit containers (ints)\r
+       }\r
+    old->nBlockAlign     = (old->nChannels * (old->wBitsPerSample/8));\r
+    old->nAvgBytesPerSec = (old->nSamplesPerSec * old->nBlockAlign);\r
 \r
     //WAVEFORMATEX\r
-    if (params->channelCount <=2 && (bytesPerSample == 2 || bytesPerSample == 1)){\r
-        old->cbSize          = 0;\r
-        old->wFormatTag      = WAVE_FORMAT_PCM;\r
+    /*if ((params->channelCount <= 2) && ((bitsPerSample == 16) || (bitsPerSample == 8)))\r
+       {\r
+        old->cbSize            = 0;\r
+        old->wFormatTag        = WAVE_FORMAT_PCM;\r
     }\r
     //WAVEFORMATEXTENSIBLE\r
-    else{\r
+    else*/\r
+       {\r
         old->wFormatTag = WAVE_FORMAT_EXTENSIBLE;\r
-\r
-        old->cbSize = sizeof (WAVEFORMATEXTENSIBLE) - sizeof (WAVEFORMATEX);\r
+        old->cbSize            = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);\r
 \r
         if ((params->sampleFormat & ~paNonInterleaved) == paFloat32)\r
             wavex->SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;\r
         else\r
             wavex->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;\r
 \r
-        wavex->Samples.wValidBitsPerSample = old->wBitsPerSample; //no extra padding!\r
-\r
-        switch(params->channelCount){\r
-            case 1:  wavex->dwChannelMask = SPEAKER_FRONT_CENTER; break;\r
-            case 2:  wavex->dwChannelMask = 0x1 | 0x2; break;\r
-            case 4:  wavex->dwChannelMask = 0x1 | 0x2 | 0x10 | 0x20; break;\r
-            case 6:  wavex->dwChannelMask = 0x1 | 0x2 | 0x4 | 0x8 | 0x10 | 0x20; break;\r
-            case 8:  wavex->dwChannelMask = 0x1 | 0x2 | 0x4 | 0x8 | 0x10 | 0x20 | 0x40 | 0x80; break;\r
-            default: wavex->dwChannelMask = 0; break;\r
-        }\r
-    }\r
+        wavex->Samples.wValidBitsPerSample = bitsPerSample; //no extra padding!\r
 \r
+               // Set channel mask\r
+               if (channelMask != 0)\r
+               {\r
+                       wavex->dwChannelMask = channelMask;\r
+               }\r
+               else\r
+               {\r
+                       switch (params->channelCount)\r
+                       {\r
+                       case 1:  wavex->dwChannelMask = KSAUDIO_SPEAKER_MONO; break;\r
+                       case 2:  wavex->dwChannelMask = KSAUDIO_SPEAKER_STEREO; break;\r
+                       case 4:  wavex->dwChannelMask = KSAUDIO_SPEAKER_QUAD; break;\r
+                       case 6:  wavex->dwChannelMask = KSAUDIO_SPEAKER_5POINT1; break;\r
+                       case 8:  wavex->dwChannelMask = KSAUDIO_SPEAKER_7POINT1; break;\r
+                       default: wavex->dwChannelMask = 0; break;\r
+                       }\r
+               }\r
+       }\r
     return paNoError;\r
 }\r
 \r
-\r
-\r
-\r
-\r
-\r
-/*\r
-#define paFloat32        ((PaSampleFormat) 0x00000001) \r
-#define paInt32          ((PaSampleFormat) 0x00000002)\r
-#define paInt24          ((PaSampleFormat) 0x00000004) \r
-#define paInt16          ((PaSampleFormat) 0x00000008) \r
-*/\r
-//lifted from pa_wdmks\r
-static void wasapiFillWFEXT( WAVEFORMATEXTENSIBLE* pwfext, PaSampleFormat sampleFormat, double sampleRate, int channelCount)\r
-{\r
-    PA_DEBUG(( "sampleFormat = %lx\n" , sampleFormat ));\r
-    PA_DEBUG(( "sampleRate = %f\n" , sampleRate ));\r
-    PA_DEBUG(( "chanelCount = %d\n", channelCount ));\r
+// ------------------------------------------------------------------------------------------\r
+static void wasapiFillWFEXT( WAVEFORMATEXTENSIBLE* pwfext, PaSampleFormat sampleFormat, double sampleRate, int channelCount)\r
+{\r
+    PA_DEBUG(( "sampleFormat = %lx\n" , sampleFormat ));\r
+    PA_DEBUG(( "sampleRate = %f\n" , sampleRate ));\r
+    PA_DEBUG(( "chanelCount = %d\n", channelCount ));\r
 \r
     pwfext->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;\r
     pwfext->Format.nChannels = channelCount;\r
@@ -914,8 +1138,8 @@ static void wasapiFillWFEXT( WAVEFORMATEXTENSIBLE* pwfext, PaSampleFormat sample
     }\r
     else if(sampleFormat == paInt24)\r
     {\r
-        pwfext->Format.nBlockAlign = channelCount * 3;\r
-        pwfext->Format.wBitsPerSample = 24;\r
+        pwfext->Format.nBlockAlign = channelCount * 4;\r
+        pwfext->Format.wBitsPerSample = 32; // 24-bit in 32-bit int container\r
         pwfext->Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX);\r
         pwfext->Samples.wValidBitsPerSample = 24;\r
         pwfext->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;\r
@@ -931,221 +1155,375 @@ static void wasapiFillWFEXT( WAVEFORMATEXTENSIBLE* pwfext, PaSampleFormat sample
     pwfext->Format.nAvgBytesPerSec = pwfext->Format.nSamplesPerSec * pwfext->Format.nBlockAlign;\r
 }\r
 \r
-\r
-\r
-/*\r
-#define FORMATTESTS 4\r
-const int BestToWorst[FORMATTESTS]={paFloat32,paInt32,paInt24,paInt16};\r
-*/\r
-\r
-#define FORMATTESTS 3\r
-const int BestToWorst[FORMATTESTS]={paFloat32,paInt24,paInt16};\r
-\r
-\r
-static PaError\r
-GetClosestFormat(IAudioClient * myClient, double sampleRate,const  PaStreamParameters * params, \r
-                                AUDCLNT_SHAREMODE *shareMode, WAVEFORMATEXTENSIBLE *outWavex)\r
+// ------------------------------------------------------------------------------------------\r
+static PaError GetClosestFormat(IAudioClient *myClient, double sampleRate, \r
+       const PaStreamParameters *params, AUDCLNT_SHAREMODE shareMode, WAVEFORMATEXTENSIBLE *outWavex)\r
 {\r
-       //TODO we should try exclusive first and shared after\r
-       *shareMode = PORTAUDIO_SHAREMODE;\r
-\r
        PaError answer = paInvalidSampleRate;\r
 \r
-    waveformatFromParams(outWavex,params,sampleRate);\r
-       WAVEFORMATEX *sharedClosestMatch=0;\r
-       HRESULT hResult=!S_OK;\r
-\r
-       if (*shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE)\r
-               hResult = myClient->IsFormatSupported(AUDCLNT_SHAREMODE_EXCLUSIVE,&outWavex->Format,NULL);\r
-       else\r
-               hResult = myClient->IsFormatSupported(AUDCLNT_SHAREMODE_SHARED,   &outWavex->Format,&sharedClosestMatch);\r
+    MakeWaveFormatFromParams(outWavex,params,sampleRate);\r
+       WAVEFORMATEX *sharedClosestMatch = 0;\r
+       HRESULT hResult = !S_OK;\r
 \r
+       hResult = myClient->IsFormatSupported(shareMode, &outWavex->Format, (shareMode == AUDCLNT_SHAREMODE_SHARED ? &sharedClosestMatch : NULL));\r
        if (hResult == S_OK)\r
                answer = paFormatIsSupported;\r
-    else if (sharedClosestMatch){\r
-        WAVEFORMATEXTENSIBLE* ext = (WAVEFORMATEXTENSIBLE*)sharedClosestMatch;\r
-               \r
-               int closestMatchSR = (int)sharedClosestMatch->nSamplesPerSec;\r
+    else\r
+       if (sharedClosestMatch)\r
+       {\r
+        WAVEFORMATEXTENSIBLE *ext = (WAVEFORMATEXTENSIBLE*)sharedClosestMatch;\r
 \r
+               GUID subf_guid = GUID_NULL;\r
                if (sharedClosestMatch->wFormatTag == WAVE_FORMAT_EXTENSIBLE)\r
-                       memcpy(outWavex,sharedClosestMatch,sizeof(WAVEFORMATEXTENSIBLE));\r
+               {\r
+                       memcpy(outWavex, sharedClosestMatch, sizeof(WAVEFORMATEXTENSIBLE));\r
+                       subf_guid = ext->SubFormat;\r
+               }\r
                else\r
-                       memcpy(outWavex,sharedClosestMatch,sizeof(WAVEFORMATEX));\r
+                       memcpy(outWavex, sharedClosestMatch, sizeof(WAVEFORMATEX));\r
 \r
         CoTaskMemFree(sharedClosestMatch);\r
 \r
-               if ((int)sampleRate == closestMatchSR)\r
+               // Make supported by default\r
                answer = paFormatIsSupported;\r
-               else\r
-                       answer = paInvalidSampleRate;\r
-       \r
-       }else {\r
 \r
+               // Validate SampleRate\r
+               if ((DWORD)sampleRate != outWavex->Format.nSamplesPerSec)\r
+                       return paInvalidSampleRate;\r
+\r
+               // Validate Channel count\r
+               if ((WORD)params->channelCount != outWavex->Format.nChannels)\r
+                       return paInvalidChannelCount;\r
+\r
+               // Validate Sample format\r
+               WORD bitsPerSample;\r
+               if ((bitsPerSample = PaSampleFormatToBitsPerSample(params->sampleFormat)) == 0)\r
+                       return paSampleFormatNotSupported;\r
+\r
+               // Validate Sample format: bit size (WASAPI does not limit 'bit size')\r
+               //if (bitsPerSample != outWavex->Format.wBitsPerSample)\r
+               //      return paSampleFormatNotSupported;\r
+\r
+               // Validate Sample format: paFloat32 (WASAPI does not limit 'bit type')\r
+               //if ((params->sampleFormat == paFloat32) && (subf_guid != KSDATAFORMAT_SUBTYPE_IEEE_FLOAT))\r
+               //      return paSampleFormatNotSupported;\r
+\r
+               // Validate Sample format: paInt32 (WASAPI does not limit 'bit type')\r
+               //if ((params->sampleFormat == paInt32) && (subf_guid != KSDATAFORMAT_SUBTYPE_PCM))\r
+               //      return paSampleFormatNotSupported;\r
+       }\r
+       else\r
+       {\r
                //it doesnt suggest anything?? ok lets show it the MENU!\r
 \r
+#define FORMATTESTS 3\r
+static const int BestToWorst[FORMATTESTS]={ paFloat32, paInt24, paInt16 };\r
+\r
                //ok fun time as with pa_win_mme, we know only a refusal of the user-requested\r
                //sampleRate+num Channel is disastrous, as the portaudio buffer processor converts between anything\r
-               //so lets only use the number \r
-               for (int i=0;i<FORMATTESTS;++i){\r
-                       WAVEFORMATEXTENSIBLE ext;\r
-                       wasapiFillWFEXT(&ext,BestToWorst[i],sampleRate,params->channelCount);           \r
-                       if (*shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE)\r
-                               hResult = myClient->IsFormatSupported(AUDCLNT_SHAREMODE_EXCLUSIVE,&ext.Format,NULL);\r
-                       else\r
-                               hResult = myClient->IsFormatSupported(AUDCLNT_SHAREMODE_SHARED,   &ext.Format,&sharedClosestMatch);\r
-\r
-                       if (hResult == S_OK){\r
+               //so lets only use the number\r
+               for (int i = 0; i < FORMATTESTS; ++i)\r
+               {\r
+                       WAVEFORMATEXTENSIBLE ext = { 0 };\r
+                       wasapiFillWFEXT(&ext,BestToWorst[i],sampleRate,params->channelCount);\r
+                       \r
+                       hResult = myClient->IsFormatSupported(shareMode, &ext.Format, (shareMode == AUDCLNT_SHAREMODE_SHARED ? &sharedClosestMatch : NULL));\r
+                       if (hResult == S_OK)\r
+                       {\r
                                memcpy(outWavex,&ext,sizeof(WAVEFORMATEXTENSIBLE));\r
                                answer = paFormatIsSupported;\r
                                break;\r
                        }\r
                }\r
 \r
-               if (answer!=paFormatIsSupported) {\r
-                       //try MIX format?\r
-                       //why did it HAVE to come to this ....\r
-                       WAVEFORMATEX pcm16WaveFormat;\r
-                       memset(&pcm16WaveFormat,0,sizeof(WAVEFORMATEX));\r
-                       pcm16WaveFormat.wFormatTag = WAVE_FORMAT_PCM; \r
-                       pcm16WaveFormat.nChannels = 2; \r
-                       pcm16WaveFormat.nSamplesPerSec = (DWORD)sampleRate; \r
-                       pcm16WaveFormat.nBlockAlign = 4; \r
-                       pcm16WaveFormat.nAvgBytesPerSec = pcm16WaveFormat.nSamplesPerSec*pcm16WaveFormat.nBlockAlign; \r
-                       pcm16WaveFormat.wBitsPerSample = 16; \r
-                       pcm16WaveFormat.cbSize = 0;\r
-\r
-                       if (*shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE)\r
-                               hResult = myClient->IsFormatSupported(AUDCLNT_SHAREMODE_EXCLUSIVE,&pcm16WaveFormat,NULL);\r
-                       else\r
-                               hResult = myClient->IsFormatSupported(AUDCLNT_SHAREMODE_SHARED,   &pcm16WaveFormat,&sharedClosestMatch);\r
-\r
-                       if (hResult == S_OK){\r
+               if (answer != paFormatIsSupported)\r
+               {\r
+                       // try MIX format?\r
+                       // why did it HAVE to come to this ....\r
+                       WAVEFORMATEX pcm16WaveFormat = { 0 };\r
+                       pcm16WaveFormat.wFormatTag              = WAVE_FORMAT_PCM;\r
+                       pcm16WaveFormat.nChannels               = 2;\r
+                       pcm16WaveFormat.nSamplesPerSec  = (DWORD)sampleRate;\r
+                       pcm16WaveFormat.nBlockAlign             = 4;\r
+                       pcm16WaveFormat.nAvgBytesPerSec = pcm16WaveFormat.nSamplesPerSec*pcm16WaveFormat.nBlockAlign;\r
+                       pcm16WaveFormat.wBitsPerSample  = 16;\r
+                       pcm16WaveFormat.cbSize                  = 0;\r
+\r
+                       hResult = myClient->IsFormatSupported(shareMode, &pcm16WaveFormat, (shareMode == AUDCLNT_SHAREMODE_SHARED ? &sharedClosestMatch : NULL));\r
+                       if (hResult == S_OK)\r
+                       {\r
                                memcpy(outWavex,&pcm16WaveFormat,sizeof(WAVEFORMATEX));\r
                                answer = paFormatIsSupported;\r
                        }\r
                }\r
 \r
-               logAUDCLNT_E(hResult);\r
+               LogHostError(hResult);\r
        }\r
 \r
        return answer;\r
 }\r
 \r
-\r
-static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,\r
-                                  const  PaStreamParameters *inputParameters,\r
-                                  const  PaStreamParameters *outputParameters,\r
-                                  double sampleRate )\r
+// ------------------------------------------------------------------------------------------\r
+static PaError IsStreamParamsValid(struct PaUtilHostApiRepresentation *hostApi,\r
+                                   const  PaStreamParameters *inputParameters,\r
+                                   const  PaStreamParameters *outputParameters,\r
+                                   double sampleRate)\r
 {\r
-\r
-    int inputChannelCount, outputChannelCount;\r
-    PaSampleFormat inputSampleFormat, outputSampleFormat;\r
-\r
-    if( inputParameters )\r
+    if (inputParameters != NULL)\r
     {\r
-        inputChannelCount = inputParameters->channelCount;\r
-        inputSampleFormat = inputParameters->sampleFormat;\r
-\r
         /* all standard sample formats are supported by the buffer adapter,\r
             this implementation doesn't support any custom sample formats */\r
-        if( inputSampleFormat & paCustomFormat )\r
+               if (inputParameters->sampleFormat & paCustomFormat)\r
             return paSampleFormatNotSupported;\r
 \r
         /* unless alternate device specification is supported, reject the use of\r
             paUseHostApiSpecificDeviceSpecification */\r
-\r
-        if( inputParameters->device == paUseHostApiSpecificDeviceSpecification )\r
+        if (inputParameters->device == paUseHostApiSpecificDeviceSpecification)\r
             return paInvalidDevice;\r
 \r
         /* check that input device can support inputChannelCount */\r
-        if( inputChannelCount > hostApi->deviceInfos[ inputParameters->device ]->maxInputChannels )\r
+        if (inputParameters->channelCount > hostApi->deviceInfos[ inputParameters->device ]->maxInputChannels)\r
             return paInvalidChannelCount;\r
 \r
         /* validate inputStreamInfo */\r
-        if( inputParameters->hostApiSpecificStreamInfo )\r
-            return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */\r
-\r
-\r
-        PaWinWasapiHostApiRepresentation *paWasapi = (PaWinWasapiHostApiRepresentation*)hostApi;\r
-\r
-\r
-               IAudioClient *myClient=0;\r
-               HRESULT hResult = paWasapi->devInfo[inputParameters->device].device->Activate(\r
-                       __uuidof(IAudioClient), CLSCTX_INPROC_SERVER, NULL, (void**)&myClient);\r
-               if (hResult != S_OK){\r
-                       logAUDCLNT_E(hResult);\r
-                       return paInvalidDevice;\r
+        if (inputParameters->hostApiSpecificStreamInfo)\r
+               {\r
+                       PaWasapiStreamInfo *inputStreamInfo = (PaWasapiStreamInfo *)inputParameters->hostApiSpecificStreamInfo;\r
+               if ((inputStreamInfo->size != sizeof(PaWasapiStreamInfo)) ||\r
+                   (inputStreamInfo->version != 1) ||\r
+                (inputStreamInfo->hostApiType != paWASAPI))\r
+               {\r
+                   return paIncompatibleHostApiSpecificStreamInfo;\r
+               }\r
                }\r
 \r
-        WAVEFORMATEXTENSIBLE wavex;\r
-               AUDCLNT_SHAREMODE shareMode;\r
-               PaError answer = GetClosestFormat(myClient,sampleRate,inputParameters,&shareMode,&wavex);\r
-               myClient->Release();\r
-\r
-               if (answer !=paFormatIsSupported)\r
-                       return answer;\r
-    }\r
-    else\r
-    {\r
-        inputChannelCount = 0;\r
+        return paNoError;\r
     }\r
 \r
-    if( outputParameters )\r
+    if (outputParameters != NULL)\r
     {\r
-        outputChannelCount = outputParameters->channelCount;\r
-        outputSampleFormat = outputParameters->sampleFormat;\r
-\r
         /* all standard sample formats are supported by the buffer adapter,\r
             this implementation doesn't support any custom sample formats */\r
-        if( outputSampleFormat & paCustomFormat )\r
+        if (outputParameters->sampleFormat & paCustomFormat)\r
             return paSampleFormatNotSupported;\r
 \r
         /* unless alternate device specification is supported, reject the use of\r
             paUseHostApiSpecificDeviceSpecification */\r
-\r
-        if( outputParameters->device == paUseHostApiSpecificDeviceSpecification )\r
+        if (outputParameters->device == paUseHostApiSpecificDeviceSpecification)\r
             return paInvalidDevice;\r
 \r
         /* check that output device can support outputChannelCount */\r
-        if( outputChannelCount > hostApi->deviceInfos[ outputParameters->device ]->maxOutputChannels )\r
+        if (outputParameters->channelCount > hostApi->deviceInfos[ outputParameters->device ]->maxOutputChannels)\r
             return paInvalidChannelCount;\r
 \r
         /* validate outputStreamInfo */\r
-        if( outputParameters->hostApiSpecificStreamInfo )\r
-            return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */\r
+        if(outputParameters->hostApiSpecificStreamInfo)\r
+        {\r
+                       PaWasapiStreamInfo *outputStreamInfo = (PaWasapiStreamInfo *)outputParameters->hostApiSpecificStreamInfo;\r
+               if ((outputStreamInfo->size != sizeof(PaWasapiStreamInfo)) ||\r
+                   (outputStreamInfo->version != 1) ||\r
+                (outputStreamInfo->hostApiType != paWASAPI))\r
+               {\r
+                   return paIncompatibleHostApiSpecificStreamInfo;\r
+               }\r
+        }\r
 \r
+               return paNoError;\r
+    }\r
 \r
-        PaWinWasapiHostApiRepresentation *paWasapi = (PaWinWasapiHostApiRepresentation*)hostApi;\r
+       return (inputParameters || outputParameters ? paNoError : paInternalError);\r
+}\r
 \r
-               IAudioClient *myClient=0;\r
-               HRESULT hResult = paWasapi->devInfo[outputParameters->device].device->Activate(\r
-                       __uuidof(IAudioClient), CLSCTX_INPROC_SERVER, NULL, (void**)&myClient);\r
-               if (hResult != S_OK){\r
-                       logAUDCLNT_E(hResult);\r
+// ------------------------------------------------------------------------------------------\r
+static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,\r
+                                  const  PaStreamParameters *inputParameters,\r
+                                  const  PaStreamParameters *outputParameters,\r
+                                  double sampleRate )\r
+{\r
+       IAudioClient *tmpClient = NULL;\r
+       PaWasapiHostApiRepresentation *paWasapi = (PaWasapiHostApiRepresentation*)hostApi;\r
+       PaWasapiStreamInfo *inputStreamInfo = NULL, *outputStreamInfo = NULL;\r
+\r
+       // Validate PaStreamParameters\r
+       PaError error;\r
+       if ((error = IsStreamParamsValid(hostApi, inputParameters, outputParameters, sampleRate)) != paNoError)\r
+               return error;\r
+\r
+    if (inputParameters != NULL)\r
+    {\r
+               inputStreamInfo = (PaWasapiStreamInfo *)inputParameters->hostApiSpecificStreamInfo;\r
+\r
+               AUDCLNT_SHAREMODE shareMode = AUDCLNT_SHAREMODE_SHARED;\r
+               if (inputStreamInfo && (inputStreamInfo->flags & paWinWasapiExclusive))\r
+                       shareMode  = AUDCLNT_SHAREMODE_EXCLUSIVE;\r
+\r
+               HRESULT hResult = paWasapi->devInfo[inputParameters->device].device->Activate(\r
+                       __uuidof(IAudioClient), CLSCTX_INPROC_SERVER, NULL, (void**)&tmpClient);\r
+               if (hResult != S_OK)\r
+               {\r
+                       LogHostError(hResult);\r
                        return paInvalidDevice;\r
                }\r
 \r
         WAVEFORMATEXTENSIBLE wavex;\r
-               AUDCLNT_SHAREMODE shareMode;\r
-               PaError answer = GetClosestFormat(myClient,sampleRate,outputParameters,&shareMode,&wavex);\r
-               myClient->Release();\r
+               PaError answer = GetClosestFormat(tmpClient, sampleRate, inputParameters, shareMode, &wavex);\r
+               tmpClient->Release();\r
 \r
-               if (answer !=paFormatIsSupported)\r
-                       return answer;          \r
+               if (answer != paFormatIsSupported)\r
+                       return answer;\r
     }\r
-    else\r
+\r
+    if (outputParameters != NULL)\r
     {\r
-        outputChannelCount = 0;\r
-    }\r
+        outputStreamInfo = (PaWasapiStreamInfo *)outputParameters->hostApiSpecificStreamInfo;\r
+\r
+               AUDCLNT_SHAREMODE shareMode = AUDCLNT_SHAREMODE_SHARED;\r
+               if (outputStreamInfo && (outputStreamInfo->flags & paWinWasapiExclusive))\r
+                       shareMode  = AUDCLNT_SHAREMODE_EXCLUSIVE;\r
+\r
+               HRESULT hResult = paWasapi->devInfo[outputParameters->device].device->Activate(\r
+                       __uuidof(IAudioClient), CLSCTX_INPROC_SERVER, NULL, (void**)&tmpClient);\r
+               if (hResult != S_OK)\r
+               {\r
+                       LogHostError(hResult);\r
+                       return paInvalidDevice;\r
+               }\r
 \r
+        WAVEFORMATEXTENSIBLE wavex;\r
+               PaError answer = GetClosestFormat(tmpClient, sampleRate, outputParameters, shareMode, &wavex);\r
+               tmpClient->Release();\r
+\r
+               if (answer != paFormatIsSupported)\r
+                       return answer;\r
+    }\r
 \r
     return paFormatIsSupported;\r
 }\r
 \r
+// ------------------------------------------------------------------------------------------\r
+static HRESULT CreateAudioClient(PaWasapiSubStream *pSubStream, PaWasapiDeviceInfo *info,\r
+       const PaStreamParameters *params, UINT32 framesPerBuffer, double sampleRate, UINT32 streamFlags, \r
+       PaError *pa_error)\r
+{\r
+    if (!pSubStream || !info || !params)\r
+        return E_POINTER;\r
+    if ((sampleRate == 0) || (framesPerBuffer == 0))\r
+        return E_INVALIDARG;\r
+\r
+       PaError error;\r
+    HRESULT hr                                 = S_OK;\r
+    UINT32 nFrames                             = 0;\r
+    IAudioClient *pAudioClient = NULL;\r
+\r
+    // Get the audio client\r
+    hr = info->device->Activate(__uuidof(IAudioClient), CLSCTX_ALL, NULL, (void**)&pAudioClient);\r
+       if (hr != S_OK)\r
+       {\r
+               LogHostError(hr);\r
+               goto done;\r
+       }\r
+\r
+       // Get closest format\r
+       if ((error = GetClosestFormat(pAudioClient, sampleRate, params, pSubStream->shareMode, &pSubStream->wavex)) != paFormatIsSupported)\r
+       {\r
+               if (pa_error)\r
+                       (*pa_error) = error;\r
+               return AUDCLNT_E_UNSUPPORTED_FORMAT;\r
+       }\r
 \r
+       // Align frames\r
+       framesPerBuffer = AlignFramesPerBuffer(framesPerBuffer, \r
+               pSubStream->wavex.Format.nSamplesPerSec, pSubStream->wavex.Format.nBlockAlign);\r
 \r
-/* see pa_hostapi.h for a list of validity guarantees made about OpenStream parameters */\r
+    // Calculate period\r
+       pSubStream->period  = MakeHnsPeriod(framesPerBuffer, pSubStream->wavex.Format.nSamplesPerSec);\r
+       pSubStream->period += ::SecondsTonano100(params->suggestedLatency);\r
+       \r
+       // Let's do not enforce min, it is even a bug as MinimumDevicePeriod will make buffer not aligned\r
+       // on Vista\r
+       //pSubStream->period  = (pSubStream->period < info->MinimumDevicePeriod ? info->MinimumDevicePeriod : pSubStream->period);\r
+\r
+    // Open the stream and associate it with an audio session\r
+    hr = pAudioClient->Initialize( \r
+        pSubStream->shareMode,\r
+        streamFlags/*AUDCLNT_STREAMFLAGS_EVENTCALLBACK*/, \r
+               pSubStream->period,\r
+               (pSubStream->shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE ? pSubStream->period : 0),\r
+               &pSubStream->wavex.Format, \r
+        NULL);\r
+\r
+    // If the requested buffer size is not aligned...\r
+    if (hr == AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED)\r
+    {  \r
+               PRINT(("WASAPI: CreateAudioClient: aligning buffer size"));\r
+\r
+        // Get the next aligned frame\r
+        hr = pAudioClient->GetBufferSize(&nFrames);\r
+               if (hr != S_OK)\r
+               {\r
+                       LogHostError(hr);\r
+                       goto done;\r
+               }\r
+        \r
+        // Release the previous allocations\r
+        SAFE_RELEASE(pAudioClient);\r
+         \r
+        // Create a new audio client\r
+        hr = info->device->Activate(__uuidof(IAudioClient), CLSCTX_ALL, NULL, (void**)&pAudioClient);\r
+       if (hr != S_OK)\r
+               {\r
+                       LogHostError(hr);\r
+                       goto done;\r
+               }\r
+\r
+               // Get closest format\r
+               if (GetClosestFormat(pAudioClient, sampleRate, params, pSubStream->shareMode, &pSubStream->wavex) != paFormatIsSupported)\r
+                       return AUDCLNT_E_UNSUPPORTED_FORMAT;\r
 \r
+               // Calculate period\r
+               pSubStream->period = MakeHnsPeriod(nFrames, pSubStream->wavex.Format.nSamplesPerSec);\r
+    \r
+        // Open the stream and associate it with an audio session\r
+        hr = pAudioClient->Initialize( \r
+            pSubStream->shareMode,\r
+            streamFlags/*AUDCLNT_STREAMFLAGS_EVENTCALLBACK*/, \r
+                       pSubStream->period,\r
+                       (pSubStream->shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE ? pSubStream->period : 0), \r
+            &pSubStream->wavex.Format, \r
+            NULL);\r
+       if (hr != S_OK)\r
+               {\r
+                       LogHostError(hr);\r
+                       goto done;\r
+               }\r
+    }\r
+    else\r
+       if (hr != S_OK)\r
+    {\r
+               LogHostError(hr);\r
+               goto done;\r
+    }\r
+\r
+    // Get the next aligned frame\r
+    hr = pAudioClient->GetBufferSize(&nFrames);\r
+       if (hr != S_OK)\r
+       {\r
+               LogHostError(hr);\r
+               goto done;\r
+       }\r
+    \r
+    // Set result\r
+       pSubStream->client = pAudioClient;\r
+    pSubStream->client->AddRef();\r
+\r
+done:\r
+\r
+    // Clean up\r
+    SAFE_RELEASE(pAudioClient);\r
+    return hr;\r
+}\r
+\r
+// ------------------------------------------------------------------------------------------\r
 static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,\r
                            PaStream** s,\r
                            const PaStreamParameters *inputParameters,\r
@@ -1157,95 +1535,113 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
                            void *userData )\r
 {\r
     PaError result = paNoError;\r
-    PaWinWasapiHostApiRepresentation *paWasapi = (PaWinWasapiHostApiRepresentation*)hostApi;\r
-    PaWinWasapiStream *stream = 0;\r
+    PaWasapiHostApiRepresentation *paWasapi = (PaWasapiHostApiRepresentation*)hostApi;\r
+    PaWasapiStream *stream;\r
     int inputChannelCount, outputChannelCount;\r
     PaSampleFormat inputSampleFormat, outputSampleFormat;\r
     PaSampleFormat hostInputSampleFormat, hostOutputSampleFormat;\r
+       PaWasapiStreamInfo *inputStreamInfo = NULL, *outputStreamInfo = NULL;\r
 \r
+       // validate PaStreamParameters\r
+       if ((result = IsStreamParamsValid(hostApi, inputParameters, outputParameters, sampleRate)) != paNoError)\r
+               return LogPaError(result);\r
 \r
-    stream = (PaWinWasapiStream*)PaUtil_AllocateMemory( sizeof(PaWinWasapiStream) );\r
-    if( !stream ){\r
-        result = paInsufficientMemory;\r
+    // Validate platform specific flags\r
+    if ((streamFlags & paPlatformSpecificFlags) != 0) \r
+       {\r
+               LogPaError(result = paInvalidFlag); /* unexpected platform specific flag */\r
+               goto error;\r
+       }\r
+\r
+       // Allocate memory for PaWasapiStream\r
+    if ((stream = (PaWasapiStream *)PaUtil_AllocateMemory(sizeof(PaWasapiStream))) == NULL)\r
+       {\r
+        LogPaError(result = paInsufficientMemory);\r
         goto error;\r
     }\r
 \r
-    if( inputParameters )\r
+       // Try create device: Input\r
+       if (inputParameters != NULL)\r
     {\r
         inputChannelCount = inputParameters->channelCount;\r
         inputSampleFormat = inputParameters->sampleFormat;\r
+               inputStreamInfo   = (PaWasapiStreamInfo *)inputParameters->hostApiSpecificStreamInfo;\r
+        PaWasapiDeviceInfo *info = &paWasapi->devInfo[inputParameters->device];\r
+               stream->in.flags  = inputStreamInfo->flags;\r
+\r
+               // Select Exclusive/Shared mode\r
+               stream->in.shareMode = AUDCLNT_SHAREMODE_SHARED;\r
+        if ((inputStreamInfo != NULL) && (inputStreamInfo->flags & paWinWasapiExclusive))\r
+                       stream->in.shareMode = AUDCLNT_SHAREMODE_EXCLUSIVE;\r
+\r
+               // Choose processing mode\r
+               stream->in.streamFlags = AUDCLNT_STREAMFLAGS_EVENTCALLBACK;\r
+               if (streamCallback == NULL)\r
+                       stream->in.streamFlags = 0; // polling interface\r
+               if ((inputStreamInfo != NULL) && (inputStreamInfo->flags & paWinWasapiPolling))\r
+                       stream->in.streamFlags = 0; // polling interface\r
+\r
+               // Create Audio client\r
+               HRESULT hr = CreateAudioClient(&stream->in, info, inputParameters, framesPerBuffer, \r
+                       sampleRate, stream->in.streamFlags, &result);\r
+        if (hr != S_OK)\r
+               {\r
+            LogHostError(hr);\r
+                       if (hr != AUDCLNT_E_UNSUPPORTED_FORMAT)\r
+                               result = paInvalidDevice;\r
+\r
+                       LogPaError(result);\r
+                       goto error;\r
+        }\r
+               LogWAVEFORMATEXTENSIBLE(&stream->in.wavex);\r
 \r
-        /* unless alternate device specification is supported, reject the use of\r
-            paUseHostApiSpecificDeviceSpecification */\r
-\r
-        if( inputParameters->device == paUseHostApiSpecificDeviceSpecification )\r
-            return paInvalidDevice;\r
-\r
-        /* check that input device can support inputChannelCount */\r
-        if( inputChannelCount > hostApi->deviceInfos[ inputParameters->device ]->maxInputChannels )\r
-            return paInvalidChannelCount;\r
-\r
-        /* validate inputStreamInfo */\r
-        if( inputParameters->hostApiSpecificStreamInfo )\r
-            return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */\r
-\r
-\r
-        PaWinWasapiDeviceInfo &info = paWasapi->devInfo[inputParameters->device];\r
-\r
-        HRESULT hResult = info.device->Activate(\r
-            __uuidof(IAudioClient), CLSCTX_INPROC_SERVER, NULL,\r
-            (void**)&stream->in.client);\r
-\r
-        if (hResult != S_OK)\r
-            return paInvalidDevice;\r
-\r
-        hResult = info.device->Activate(\r
+               // Create volume mgr\r
+               stream->inVol = NULL;\r
+        /*hResult = info->device->Activate(\r
             __uuidof(IAudioEndpointVolume), CLSCTX_INPROC_SERVER, NULL,\r
             (void**)&stream->inVol);\r
-\r
         if (hResult != S_OK)\r
-            return paInvalidDevice;\r
-       \r
-               AUDCLNT_SHAREMODE shareMode;\r
-               PaError answer = GetClosestFormat(stream->in.client,sampleRate,inputParameters,&shareMode,&stream->in.wavex);\r
-               \r
-               if (answer !=paFormatIsSupported)\r
-                       return answer;\r
-\r
-        //stream->out.period = info.DefaultDevicePeriod;\r
-        stream->in.period = info.MinimumDevicePeriod;\r
+            return paInvalidDevice;*/\r
 \r
-        hResult = stream->in.client->Initialize(\r
-            shareMode,\r
-            0,  //no flags\r
-            stream->in.period,\r
-            0,//stream->out.period,\r
-            (WAVEFORMATEX*)&stream->in.wavex,\r
-            &stream->session\r
-            );\r
-\r
-        if (hResult != S_OK){\r
-            logAUDCLNT_E(hResult);\r
-            return paInvalidDevice;\r
+        // Set user-side custom host processor\r
+        if ((inputStreamInfo != NULL) &&\r
+            (inputStreamInfo->flags & paWinWasapiRedirectHostProcessor))\r
+        {\r
+            stream->hostProcessOverrideInput.processor = inputStreamInfo->hostProcessorInput;\r
+            stream->hostProcessOverrideInput.userData = userData;\r
         }\r
 \r
-        hResult = stream->in.client->GetBufferSize(&stream->in.bufferSize);\r
-        if (hResult != S_OK)\r
+        // Get max possible buffer size to check if it is not less than that we request\r
+        UINT32 maxBufferSize;\r
+        hr = stream->in.client->GetBufferSize(&maxBufferSize);\r
+        if (hr != S_OK)\r
+               {\r
+                       LogHostError(hr);\r
             return paInvalidDevice;\r
+               }\r
 \r
-        hResult = stream->in.client->GetStreamLatency(&stream->in.latency);\r
-        if (hResult != S_OK)\r
+        // Correct buffer to max size if it maxed out result of GetBufferSize\r
+               stream->in.bufferSize = (framesPerBuffer > maxBufferSize ? maxBufferSize : framesPerBuffer);\r
+\r
+               // Get stream latency\r
+        hr = stream->in.client->GetStreamLatency(&stream->in.latency);\r
+        if (hr != S_OK)\r
+               {\r
+                       LogHostError(hr);\r
             return paInvalidDevice;\r
+               }\r
+               stream->in.latency_seconds = nano100ToSeconds(stream->in.latency);\r
 \r
-        double periodsPerSecond = 1.0/nano100ToSeconds(stream->in.period);\r
-        double samplesPerPeriod = (double)(stream->in.wavex.Format.nSamplesPerSec)/periodsPerSecond;\r
+        // Number of samples that are required at each period\r
+               stream->in.framesPerHostCallback = \r
+                       (stream->in.shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE ? maxBufferSize : stream->in.bufferSize);\r
 \r
-        //this is the number of samples that are required at each period\r
-        stream->in.framesPerHostCallback = (unsigned long)samplesPerPeriod;//unrelated to channels\r
+               // Get closest format\r
+        hostInputSampleFormat = PaUtil_SelectClosestAvailableFormat( waveformatToPaFormat(&stream->in.wavex), inputSampleFormat );\r
 \r
-        /* IMPLEMENT ME - establish which  host formats are available */\r
-        hostInputSampleFormat =\r
-            PaUtil_SelectClosestAvailableFormat( waveformatToPaFormat(&stream->in.wavex), inputSampleFormat );\r
+               UINT32 latency = (1000 * maxBufferSize) / stream->in.wavex.Format.nSamplesPerSec + (UINT32)nano100ToMillis(stream->in.latency);\r
+               PRINT(("PaWASAPIOpenStream: (input) framesPerHostCallback: %d\n", (UINT32)stream->in.framesPerHostCallback));\r
+               PRINT(("PaWASAPIOpenStream: (input) latency: %d\n", stream->in.latency));\r
        }\r
     else\r
     {\r
@@ -1253,98 +1649,90 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
         inputSampleFormat = hostInputSampleFormat = paInt16; /* Surpress 'uninitialised var' warnings. */\r
     }\r
 \r
-    if( outputParameters )\r
+       // Try create device: Output\r
+    if (outputParameters != NULL)\r
     {\r
         outputChannelCount = outputParameters->channelCount;\r
         outputSampleFormat = outputParameters->sampleFormat;\r
-\r
-        /* unless alternate device specification is supported, reject the use of\r
-            paUseHostApiSpecificDeviceSpecification */\r
-\r
-        if( outputParameters->device == paUseHostApiSpecificDeviceSpecification )\r
-            return paInvalidDevice;\r
-\r
-        /* check that output device can support inputChannelCount */\r
-        if( outputChannelCount > hostApi->deviceInfos[ outputParameters->device ]->maxOutputChannels )\r
-            return paInvalidChannelCount;\r
-\r
-        /* validate outputStreamInfo */\r
-        if( outputParameters->hostApiSpecificStreamInfo )\r
-            return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */\r
-\r
-\r
-        PaWinWasapiDeviceInfo &info = paWasapi->devInfo[outputParameters->device];\r
-\r
-        HRESULT hResult = info.device->Activate(\r
-            __uuidof(IAudioClient), CLSCTX_INPROC_SERVER, NULL,\r
-            (void**)&stream->out.client);\r
-\r
-        if (hResult != S_OK)\r
-            return paInvalidDevice;\r
-\r
-               AUDCLNT_SHAREMODE shareMode;\r
-               PaError answer = GetClosestFormat(stream->out.client,sampleRate,outputParameters,&shareMode,&stream->out.wavex);\r
-               \r
-               if (answer !=paFormatIsSupported)\r
-                       return answer;\r
-               LogWAVEFORMATEXTENSIBLE(&stream->out.wavex);\r
-\r
-       // stream->out.period = info.DefaultDevicePeriod;\r
-        stream->out.period = info.MinimumDevicePeriod;\r
-\r
-               /*For an exclusive-mode stream that uses event-driven buffering, \r
-               the caller must specify nonzero values for hnsPeriodicity and hnsBufferDuration, \r
-               and the values of these two parameters must be equal */\r
-               if (shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE){\r
-                       hResult = stream->out.client->Initialize(\r
-                               shareMode,\r
-                               AUDCLNT_STREAMFLAGS_EVENTCALLBACK, \r
-                               stream->out.period,\r
-                               stream->out.period,\r
-                               (WAVEFORMATEX*)&stream->out.wavex,\r
-                               &stream->session\r
-                               );\r
-               }\r
-               else{\r
-                       hResult = stream->out.client->Initialize(\r
-                               shareMode,\r
-                               AUDCLNT_STREAMFLAGS_EVENTCALLBACK, \r
-                               0,\r
-                               0,\r
-                               (WAVEFORMATEX*)&stream->out.wavex,\r
-                               &stream->session\r
-                               );\r
-               }\r
-       \r
-\r
-        if (hResult != S_OK){\r
-            logAUDCLNT_E(hResult);\r
-            return paInvalidDevice;\r
+               outputStreamInfo   = (PaWasapiStreamInfo *)outputParameters->hostApiSpecificStreamInfo;\r
+               PaWasapiDeviceInfo *info = &paWasapi->devInfo[outputParameters->device];\r
+               stream->out.flags  = outputStreamInfo->flags;\r
+\r
+               // Select Exclusive/Shared mode\r
+               stream->out.shareMode = AUDCLNT_SHAREMODE_SHARED;\r
+        if ((outputStreamInfo != NULL) && (outputStreamInfo->flags & paWinWasapiExclusive))\r
+                       stream->out.shareMode = AUDCLNT_SHAREMODE_EXCLUSIVE;\r
+\r
+               // Choose processing mode\r
+               stream->out.streamFlags = AUDCLNT_STREAMFLAGS_EVENTCALLBACK;\r
+               if (streamCallback == NULL)\r
+                       stream->out.streamFlags = 0; // polling interface\r
+               if ((outputStreamInfo != NULL) && (outputStreamInfo->flags & paWinWasapiPolling))\r
+                       stream->out.streamFlags = 0; // polling interface\r
+\r
+               // Create Audio client\r
+               HRESULT hr = CreateAudioClient(&stream->out, info, outputParameters, framesPerBuffer, \r
+                       sampleRate, stream->out.streamFlags, &result);\r
+        if (hr != S_OK)\r
+               {\r
+            LogHostError(hr);\r
+                       if (hr != AUDCLNT_E_UNSUPPORTED_FORMAT)\r
+                               result = paInvalidDevice;\r
+\r
+                       LogPaError(result);\r
+                       goto error;\r
         }\r
+               LogWAVEFORMATEXTENSIBLE(&stream->out.wavex);\r
 \r
-        hResult = info.device->Activate(\r
+               // Activate volume\r
+               stream->outVol = NULL;\r
+        /*hResult = info->device->Activate(\r
             __uuidof(IAudioEndpointVolume), CLSCTX_INPROC_SERVER, NULL,\r
             (void**)&stream->outVol);\r
-\r
         if (hResult != S_OK)\r
-            return paInvalidDevice;\r
+            return paInvalidDevice;*/\r
 \r
-        hResult = stream->out.client->GetBufferSize(&stream->out.bufferSize);\r
-        if (hResult != S_OK)\r
-            return paInvalidDevice;\r
+       // Set user-side custom host processor\r
+        if ((outputStreamInfo != NULL) &&\r
+            (outputStreamInfo->flags & paWinWasapiRedirectHostProcessor))\r
+        {\r
+            stream->hostProcessOverrideOutput.processor = outputStreamInfo->hostProcessorOutput;\r
+            stream->hostProcessOverrideOutput.userData = userData;\r
+        }\r
 \r
-        hResult = stream->out.client->GetStreamLatency(&stream->out.latency);\r
-        if (hResult != S_OK)\r
-            return paInvalidDevice;\r
-               \r
-        double periodsPerSecond = 1.0/nano100ToSeconds(stream->out.period);\r
-        double samplesPerPeriod = (double)(stream->out.wavex.Format.nSamplesPerSec)/periodsPerSecond;\r
+        // Get max possible buffer size to check if it is not less than that we request\r
+        UINT32 maxBufferSize;\r
+        hr = stream->out.client->GetBufferSize(&maxBufferSize);\r
+        if (hr != S_OK)\r
+               {\r
+                       LogHostError(hr);\r
+                       LogPaError(result = paInvalidDevice);\r
+                       goto error;\r
+               }\r
+\r
+        // Correct buffer to max size if it maxed out result of GetBufferSize\r
+               stream->out.bufferSize = (framesPerBuffer > maxBufferSize ? maxBufferSize : framesPerBuffer);\r
+\r
+               // Get stream latency\r
+        hr = stream->out.client->GetStreamLatency(&stream->out.latency);\r
+        if (hr != S_OK)\r
+               {\r
+                       LogHostError(hr);\r
+                       LogPaError(result = paInvalidDevice);\r
+                       goto error;\r
+               }\r
+               stream->out.latency_seconds = nano100ToSeconds(stream->out.latency);\r
 \r
-        //this is the number of samples that are required at each period\r
-        stream->out.framesPerHostCallback = stream->out.bufferSize; //(unsigned long)samplesPerPeriod;//unrelated to channels\r
+        // Number of samples that are required at each period\r
+               stream->out.framesPerHostCallback = \r
+                       (stream->out.shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE ? maxBufferSize : stream->out.bufferSize);\r
 \r
-        /* IMPLEMENT ME - establish which  host formats are available */\r
+        // Get closest format\r
         hostOutputSampleFormat = PaUtil_SelectClosestAvailableFormat( waveformatToPaFormat(&stream->out.wavex), outputSampleFormat );\r
+\r
+               UINT32 latency = (1000 * maxBufferSize) / stream->out.wavex.Format.nSamplesPerSec + (UINT32)nano100ToMillis(stream->out.latency);\r
+               PRINT(("PaWASAPIOpenStream: (output) framesPerHostCallback: %d\n", (UINT32)stream->out.framesPerHostCallback));\r
+               PRINT(("PaWASAPIOpenStream: (output) latency: %d\n", latency));\r
     }\r
     else\r
     {\r
@@ -1352,135 +1740,111 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
         outputSampleFormat = hostOutputSampleFormat = paInt16; /* Surpress 'uninitialized var' warnings. */\r
     }\r
 \r
+       // paWinWasapiPolling must be on/or not on both streams\r
+       if ((inputParameters != NULL) && (outputParameters != NULL))\r
+       {\r
+               if ((inputStreamInfo != NULL) && (outputStreamInfo != NULL))\r
+               {\r
+                       if (((inputStreamInfo->flags & paWinWasapiPolling) && \r
+                               !(outputStreamInfo->flags & paWinWasapiPolling)) \r
+                               ||\r
+                               (!(inputStreamInfo->flags & paWinWasapiPolling) && \r
+                                (outputStreamInfo->flags & paWinWasapiPolling)))\r
+                       {\r
+                               LogPaError(result = paInvalidFlag);\r
+                               goto error;\r
+                       }\r
+               }\r
+       }\r
 \r
-\r
-    /*\r
-        IMPLEMENT ME:\r
-\r
-        ( the following two checks are taken care of by PaUtil_InitializeBufferProcessor() FIXME - checks needed? )\r
-\r
-            - check that input device can support inputSampleFormat, or that\r
-                we have the capability to convert from outputSampleFormat to\r
-                a native format\r
-\r
-            - check that output device can support outputSampleFormat, or that\r
-                we have the capability to convert from outputSampleFormat to\r
-                a native format\r
-\r
-            - if a full duplex stream is requested, check that the combination\r
-                of input and output parameters is supported\r
-\r
-            - check that the device supports sampleRate\r
-\r
-            - alter sampleRate to a close allowable rate if possible / necessary\r
-\r
-            - validate suggestedInputLatency and suggestedOutputLatency parameters,\r
-                use default values where necessary\r
-    */\r
-\r
-\r
-\r
-    /* validate platform specific flags */\r
-    if( (streamFlags & paPlatformSpecificFlags) != 0 )\r
-        return paInvalidFlag; /* unexpected platform specific flag */\r
-\r
-\r
-\r
-    if( streamCallback )\r
+    if (streamCallback)\r
     {\r
-        PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation,\r
-                                               &paWasapi->callbackStreamInterface, streamCallback, userData );\r
+               stream->bBlocking = false;\r
+        PaUtil_InitializeStreamRepresentation(&stream->streamRepresentation,\r
+                                              &paWasapi->callbackStreamInterface, streamCallback, userData);\r
     }\r
     else\r
     {\r
-        PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation,\r
-                                               &paWasapi->blockingStreamInterface, streamCallback, userData );\r
+               stream->bBlocking = true;\r
+        PaUtil_InitializeStreamRepresentation(&stream->streamRepresentation,\r
+                                              &paWasapi->blockingStreamInterface, streamCallback, userData);\r
     }\r
 \r
-    PaUtil_InitializeCpuLoadMeasurer( &stream->cpuLoadMeasurer, sampleRate );\r
-\r
+    PaUtil_InitializeCpuLoadMeasurer(&stream->cpuLoadMeasurer, sampleRate);\r
 \r
-       if (outputParameters && inputParameters){\r
-\r
-               //serious problem #1\r
-               if (stream->in.period != stream->out.period){\r
-                       PRINT(("OpenStream: period discrepancy\n"));\r
+       if (outputParameters && inputParameters)\r
+       {\r
+               // serious problem #1\r
+               if (stream->in.period != stream->out.period)\r
+               {\r
+                       PRINT(("WASAPI: OpenStream: period discrepancy\n"));\r
                        goto error;\r
                }\r
 \r
-               //serious problem #2\r
-               if (stream->out.framesPerHostCallback != stream->in.framesPerHostCallback){\r
-                       PRINT(("OpenStream: framesPerHostCallback discrepancy\n"));\r
+               // serious problem #2\r
+               if (stream->out.framesPerHostCallback != stream->in.framesPerHostCallback)\r
+               {\r
+                       PRINT(("WASAPI: OpenStream: framesPerHostCallback discrepancy\n"));\r
                        goto error;\r
                }\r
        }\r
 \r
-       unsigned long framesPerHostCallback = (outputParameters)?\r
-               stream->out.framesPerHostCallback: \r
+       ULONG framesPerHostCallback = (outputParameters) ? stream->out.framesPerHostCallback:\r
                stream->in.framesPerHostCallback;\r
 \r
     /* we assume a fixed host buffer size in this example, but the buffer processor\r
         can also support bounded and unknown host buffer sizes by passing\r
         paUtilBoundedHostBufferSize or paUtilUnknownHostBufferSize instead of\r
-        paUtilFixedHostBufferSize below. */\r
-\r
-    result =  PaUtil_InitializeBufferProcessor( &stream->bufferProcessor,\r
+        paUtilFixedHostBufferSize below.\r
+       */\r
+    result =  PaUtil_InitializeBufferProcessor(&stream->bufferProcessor,\r
               inputChannelCount, inputSampleFormat, hostInputSampleFormat,\r
               outputChannelCount, outputSampleFormat, hostOutputSampleFormat,\r
               sampleRate, streamFlags, framesPerBuffer,\r
               framesPerHostCallback, paUtilFixedHostBufferSize,\r
-              streamCallback, userData );\r
-    if( result != paNoError )\r
+              streamCallback, userData);\r
+    if (result != paNoError)\r
+       {\r
+               LogPaError(result);\r
         goto error;\r
+       }\r
 \r
-\r
-    /*\r
-        IMPLEMENT ME: initialise the following fields with estimated or actual\r
-        values.\r
-    */\r
+       // Set Input latency\r
     stream->streamRepresentation.streamInfo.inputLatency =\r
             PaUtil_GetBufferProcessorInputLatency(&stream->bufferProcessor)\r
-                       + ((inputParameters)?nano100ToSeconds(stream->in.latency) :0);\r
+                       + ((inputParameters)?stream->in.latency_seconds :0);\r
 \r
+       // Set Output latency\r
     stream->streamRepresentation.streamInfo.outputLatency =\r
             PaUtil_GetBufferProcessorOutputLatency(&stream->bufferProcessor)\r
-                       + ((outputParameters)?nano100ToSeconds(stream->out.latency) :0);\r
+                       + ((outputParameters)?stream->out.latency_seconds :0);\r
 \r
     stream->streamRepresentation.streamInfo.sampleRate = sampleRate;\r
 \r
-\r
-    *s = (PaStream*)stream;\r
-\r
+    (*s) = (PaStream *)stream;\r
 \r
     return result;\r
 \r
 error:\r
-    if( stream )\r
-        PaUtil_FreeMemory( stream );\r
+\r
+    if (stream)\r
+        PaUtil_FreeMemory(stream);\r
 \r
     return result;\r
 }\r
 \r
-\r
-\r
-/*\r
-    When CloseStream() is called, the multi-api layer ensures that\r
-    the stream has already been stopped or aborted.\r
-*/\r
-\r
-#define SAFE_RELEASE(punk)  \\r
-              if ((punk) != NULL)  \\r
-                { (punk)->Release(); (punk) = NULL; }\r
-\r
+// ------------------------------------------------------------------------------------------\r
 static PaError CloseStream( PaStream* s )\r
 {\r
     PaError result = paNoError;\r
-    PaWinWasapiStream *stream = (PaWinWasapiStream*)s;\r
+    PaWasapiStream *stream = (PaWasapiStream*)s;\r
 \r
-    /*\r
-        IMPLEMENT ME:\r
-            - additional stream closing + cleanup\r
-    */\r
+       // abort active stream\r
+       if (IsStreamActive(s))\r
+       {\r
+               if ((result = AbortStream(s)) != paNoError)\r
+                       return result;\r
+       }\r
 \r
     SAFE_RELEASE(stream->out.client);\r
     SAFE_RELEASE(stream->in.client);\r
@@ -1488,105 +1852,180 @@ static PaError CloseStream( PaStream* s )
     SAFE_RELEASE(stream->rclient);\r
        SAFE_RELEASE(stream->inVol);\r
        SAFE_RELEASE(stream->outVol);\r
-    CloseHandle(stream->hThread);\r
-       CloseHandle(stream->hNotificationEvent);\r
 \r
-    PaUtil_TerminateBufferProcessor( &stream->bufferProcessor );\r
-    PaUtil_TerminateStreamRepresentation( &stream->streamRepresentation );\r
-    PaUtil_FreeMemory( stream );\r
+    SAFE_CLOSE(stream->hThread);\r
+       SAFE_CLOSE(stream->hThreadDone);\r
+       SAFE_CLOSE(stream->hCloseRequest);\r
+       SAFE_CLOSE(stream->hBlockingOpStreamRD);\r
+       SAFE_CLOSE(stream->hBlockingOpStreamWR);\r
+\r
+    PaUtil_TerminateBufferProcessor(&stream->bufferProcessor);\r
+    PaUtil_TerminateStreamRepresentation(&stream->streamRepresentation);\r
+    PaUtil_FreeMemory(stream);\r
 \r
     return result;\r
 }\r
 \r
-DWORD WINAPI ProcThread(void *client);\r
-\r
+// ------------------------------------------------------------------------------------------\r
 static PaError StartStream( PaStream *s )\r
 {\r
     PaError result = paNoError;\r
-    PaWinWasapiStream *stream = (PaWinWasapiStream*)s;\r
+       HRESULT hr;\r
+    PaWasapiStream *stream = (PaWasapiStream*)s;\r
 \r
-    PaUtil_ResetBufferProcessor( &stream->bufferProcessor );\r
-       \r
-       HRESULT hResult=S_OK;\r
+       // check if stream is active already\r
+       if (IsStreamActive(s))\r
+               return paStreamIsNotStopped;\r
 \r
-       if (stream->out.client){\r
-               hResult = stream->out.client->GetService(__uuidof(IAudioRenderClient),(void**)&stream->rclient);\r
-               logAUDCLNT_E(hResult);\r
-               if (hResult!=S_OK)\r
-                       return paUnanticipatedHostError;\r
-       }\r
-       \r
-       if (stream->in.client){\r
-        hResult = stream->in.client->GetService(__uuidof(IAudioCaptureClient),(void**)&stream->cclient);\r
-               logAUDCLNT_E(hResult);\r
-               if (hResult!=S_OK)\r
-                       return paUnanticipatedHostError;\r
+    PaUtil_ResetBufferProcessor(&stream->bufferProcessor);\r
+\r
+       // Create close event\r
+       stream->hCloseRequest = CreateEvent(NULL, TRUE, FALSE, NULL);\r
+\r
+       // Create thread\r
+       if (!stream->bBlocking)\r
+       {\r
+               // Create thread done event\r
+               stream->hThreadDone = CreateEvent(NULL, TRUE, FALSE, NULL);\r
+\r
+               if ((stream->in.client && stream->in.streamFlags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK) || \r
+                       (stream->out.client && stream->out.streamFlags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK))\r
+               {\r
+                       if ((stream->hThread = CREATE_THREAD(ProcThreadEvent)) == NULL)\r
+                          return paUnanticipatedHostError;\r
+               }\r
+               else\r
+               {\r
+                       if ((stream->hThread = CREATE_THREAD(ProcThreadPoll)) == NULL)\r
+                          return paUnanticipatedHostError;\r
+               }\r
        }\r
+       else\r
+       {\r
+               // Create blocking operation events (non-signaled event means - blocking operation is pending)\r
+               if (stream->out.client)\r
+                       stream->hBlockingOpStreamWR = CreateEvent(NULL, TRUE, TRUE, NULL);\r
+               if (stream->in.client)\r
+                       stream->hBlockingOpStreamRD = CreateEvent(NULL, TRUE, TRUE, NULL);\r
+\r
+               // Initialize event & start INPUT stream\r
+               if (stream->in.client)\r
+               {\r
+                       if ((hr = stream->in.client->GetService(__uuidof(IAudioCaptureClient), (void**)&stream->cclient)) != S_OK)\r
+                       {\r
+                               LogHostError(hr);\r
+                               return paUnanticipatedHostError;\r
+                       }\r
 \r
-    // Create a thread for this client.\r
-    stream->hThread = CREATE_THREAD;\r
-        \r
-    if (stream->hThread == NULL)\r
-        return paUnanticipatedHostError;\r
+                       if ((hr = stream->in.client->Start()) != S_OK)\r
+                       {\r
+                               LogHostError(hr);\r
+                               return paUnanticipatedHostError;\r
+                       }\r
+               }\r
+\r
+\r
+               // Initialize event & start OUTPUT stream\r
+               if (stream->out.client)\r
+               {\r
+                       if ((hr = stream->out.client->GetService(__uuidof(IAudioRenderClient), (void**)&stream->rclient)) != S_OK)\r
+                       {\r
+                               LogHostError(hr);\r
+                               return paUnanticipatedHostError;\r
+                       }\r
+\r
+                       // Start\r
+                       if ((hr = stream->out.client->Start()) != S_OK)\r
+                       {\r
+                               LogHostError(hr);\r
+                               return paUnanticipatedHostError;\r
+                       }\r
+               }\r
+\r
+               // Signal: stream running\r
+               stream->running = true;\r
+\r
+               // Set current time\r
+               stream->out.prevTime  = ::timeGetTime();\r
+               stream->out.prevSleep = 0;\r
+       }\r
 \r
     return paNoError;\r
 }\r
 \r
+// ------------------------------------------------------------------------------------------\r
+static void _FinishStream(PaWasapiStream *stream)\r
+{\r
+       // Issue command to thread to stop processing and wait for thread exit\r
+       if (!stream->bBlocking)\r
+       {\r
+               SignalObjectAndWait(stream->hCloseRequest, stream->hThreadDone, INFINITE, FALSE);\r
+       }\r
+       else\r
+       // Blocking mode does not own thread\r
+       {\r
+               // Signal close event and wait for each of 2 blocking operations to complete\r
+               if (stream->out.client)\r
+                       SignalObjectAndWait(stream->hCloseRequest, stream->hBlockingOpStreamWR, INFINITE, TRUE);\r
+               if (stream->out.client)\r
+                       SignalObjectAndWait(stream->hCloseRequest, stream->hBlockingOpStreamRD, INFINITE, TRUE);\r
+\r
+               // Process stop\r
+               _OnStreamStop(stream);\r
+       }\r
+\r
+       // Close thread handles to allow restart\r
+       SAFE_CLOSE(stream->hThread);\r
+       SAFE_CLOSE(stream->hThreadDone);\r
+       SAFE_CLOSE(stream->hCloseRequest);\r
+       SAFE_CLOSE(stream->hBlockingOpStreamRD);\r
+       SAFE_CLOSE(stream->hBlockingOpStreamWR);\r
 \r
+    stream->running = false;\r
+}\r
+\r
+// ------------------------------------------------------------------------------------------\r
 static PaError StopStream( PaStream *s )\r
 {\r
     PaError result = paNoError;\r
-    PaWinWasapiStream *stream = (PaWinWasapiStream*)s;\r
 \r
-    /* suppress unused variable warnings */\r
-    stream->closeRequest = true;\r
-    //todo something MUCH better than this\r
-    while(stream->closeRequest)\r
-        Sleep(100);\r
-\r
-    /* IMPLEMENT ME, see portaudio.h for required behavior */\r
-\r
-    stream->running = false;\r
+       // Finish stream\r
+       _FinishStream((PaWasapiStream *)s);\r
 \r
     return result;\r
 }\r
 \r
-\r
+// ------------------------------------------------------------------------------------------\r
 static PaError AbortStream( PaStream *s )\r
 {\r
     PaError result = paNoError;\r
-    PaWinWasapiStream *stream = (PaWinWasapiStream*)s;\r
-\r
-    /* suppress unused variable warnings */\r
-    stream->closeRequest = true;\r
-    //todo something MUCH better than this\r
-    while(stream->closeRequest)\r
-        Sleep(100);\r
 \r
-    /* IMPLEMENT ME, see portaudio.h for required behavior */\r
+       // Finish stream\r
+       _FinishStream((PaWasapiStream *)s);\r
 \r
     return result;\r
 }\r
 \r
-\r
+// ------------------------------------------------------------------------------------------\r
 static PaError IsStreamStopped( PaStream *s )\r
 {\r
-    PaWinWasapiStream *stream = (PaWinWasapiStream*)s;\r
-\r
-    return !stream->running;\r
+    PaWasapiStream *stream = (PaWasapiStream*)s;\r
+       return (stream ? !stream->running : paBadStreamPtr);\r
 }\r
 \r
-\r
+// ------------------------------------------------------------------------------------------\r
 static PaError IsStreamActive( PaStream *s )\r
 {\r
-    PaWinWasapiStream *stream = (PaWinWasapiStream*)s;\r
-    return stream->running;\r
+    PaWasapiStream *stream = (PaWasapiStream*)s;\r
+    return (stream ? stream->running : paBadStreamPtr);\r
 }\r
 \r
-\r
+// ------------------------------------------------------------------------------------------\r
 static PaTime GetStreamTime( PaStream *s )\r
 {\r
-    PaWinWasapiStream *stream = (PaWinWasapiStream*)s;\r
+    PaWasapiStream *stream = (PaWasapiStream*)s;\r
+       if (stream == NULL)\r
+               return paBadStreamPtr;\r
 \r
     /* suppress unused variable warnings */\r
     (void) stream;\r
@@ -1599,113 +2038,311 @@ static PaTime GetStreamTime( PaStream *s )
     return PaUtil_GetTime();\r
 }\r
 \r
-\r
+// ------------------------------------------------------------------------------------------\r
 static double GetStreamCpuLoad( PaStream* s )\r
 {\r
-    PaWinWasapiStream *stream = (PaWinWasapiStream*)s;\r
-\r
-    return PaUtil_GetCpuLoad( &stream->cpuLoadMeasurer );\r
+    PaWasapiStream *stream = (PaWasapiStream *)s;\r
+       return (stream ? PaUtil_GetCpuLoad( &stream->cpuLoadMeasurer ) : 0.0);\r
 }\r
 \r
+// ------------------------------------------------------------------------------------------\r
+/* NOT TESTED */\r
+static PaError ReadStream( PaStream* s, void *_buffer, unsigned long _frames )\r
+{\r
+    PaWasapiStream *stream = (PaWasapiStream*)s;\r
+       if (stream == NULL)\r
+               return paBadStreamPtr;\r
+\r
+       HRESULT hr = S_OK;\r
+       UINT32 frames;\r
+       BYTE *buffer = (BYTE *)_buffer;\r
+       BYTE *data = NULL;\r
+       DWORD flags = 0;\r
+\r
+       // Notify blocking op has begun\r
+       ResetEvent(stream->hBlockingOpStreamRD);\r
+\r
+       while (_frames != 0)\r
+       {\r
+               // Get the available data in the shared buffer.\r
+               if ((hr = stream->cclient->GetBuffer(&data, &frames, &flags, NULL, NULL)) != S_OK)\r
+               {\r
+                       if (hr == AUDCLNT_S_BUFFER_EMPTY)\r
+                       {\r
+                               // Check if blocking call must be interrupted\r
+                               if (WaitForSingleObject(stream->hCloseRequest, 1) != WAIT_TIMEOUT)\r
+                                       break;\r
+                       }\r
 \r
-/*\r
-    As separate stream interfaces are used for blocking and callback\r
-    streams, the following functions can be guaranteed to only be called\r
-    for blocking streams.\r
-*/\r
+                       return LogHostError(hr);\r
+                       goto stream_rd_end;\r
+               }\r
 \r
-static PaError ReadStream( PaStream* s,\r
-                           void *buffer,\r
-                           unsigned long frames )\r
-{\r
-    PaWinWasapiStream *stream = (PaWinWasapiStream*)s;\r
+               // Detect silence\r
+               // if (flags & AUDCLNT_BUFFERFLAGS_SILENT)\r
+               //      data = NULL;\r
 \r
-    /* suppress unused variable warnings */\r
-    (void) buffer;\r
-    (void) frames;\r
-    (void) stream;\r
+               // Check if frames <= _frames\r
+               if (frames > _frames)\r
+                       frames = _frames;\r
 \r
-    /* IMPLEMENT ME, see portaudio.h for required behavior*/\r
+               // Copy\r
+               UINT32 buffer_size = frames * stream->in.wavex.Format.nBlockAlign;\r
+               memcpy(buffer, data, buffer_size);\r
+               buffer += buffer_size;\r
+        \r
+               // Release buffer\r
+               if ((hr = stream->cclient->ReleaseBuffer(frames)) != S_OK)\r
+               {\r
+                       LogHostError(hr);\r
+                       goto stream_rd_end;\r
+               }\r
 \r
-    return paNoError;\r
-}\r
+               _frames -= frames;\r
+       }\r
+\r
+stream_rd_end:\r
 \r
+       // Notify blocking op has ended\r
+       SetEvent(stream->hBlockingOpStreamRD);\r
 \r
-static PaError WriteStream( PaStream* s,\r
-                            const void *buffer,\r
-                            unsigned long frames )\r
+       return (hr != S_OK ? paUnanticipatedHostError : paNoError);\r
+}\r
+\r
+// ------------------------------------------------------------------------------------------\r
+static PaError WriteStream( PaStream* s, const void *_buffer, unsigned long _frames )\r
 {\r
-    PaWinWasapiStream *stream = (PaWinWasapiStream*)s;\r
+    PaWasapiStream *stream = (PaWasapiStream*)s;\r
+\r
+       if (stream == NULL)\r
+               return paBadStreamPtr;\r
+\r
+       if (!stream->running)\r
+               return paStreamIsStopped;\r
+\r
+       if (stream->rclient == NULL)\r
+               return paBadStreamPtr;\r
+\r
+       UINT32 frames;\r
+       const BYTE *buffer = (BYTE *)_buffer;\r
+       BYTE *data;\r
+       HRESULT hr;\r
+\r
+       // Notify blocking op has begun\r
+       ResetEvent(stream->hBlockingOpStreamWR);\r
+\r
+       // Calculate sleep time for next call\r
+       UINT32 next_rev_sleep, blocks, block_sleep_ms;\r
+       {\r
+               UINT32 remainder = 0;\r
+               UINT32 sleep_ms = 0;\r
+               blocks = _frames / stream->out.framesPerHostCallback;\r
+               block_sleep_ms = GetFramesSleepTime(stream->out.framesPerHostCallback, stream->out.wavex.Format.nSamplesPerSec);\r
+               if (blocks == 0) \r
+               {\r
+                       blocks = 1;\r
+                       sleep_ms = GetFramesSleepTime(_frames, stream->out.wavex.Format.nSamplesPerSec); // partial\r
+               }\r
+               else\r
+               {\r
+                       remainder = _frames - blocks * stream->out.framesPerHostCallback;\r
+                       sleep_ms = block_sleep_ms; // full\r
+               }\r
 \r
-    /* suppress unused variable warnings */\r
-    (void) buffer;\r
-    (void) frames;\r
-    (void) stream;\r
+               // Sleep for remainder\r
+               DWORD elapsed_ms = ::timeGetTime() - stream->out.prevTime;\r
+               if (sleep_ms >= elapsed_ms)\r
+                       sleep_ms -= elapsed_ms;\r
 \r
-    /* IMPLEMENT ME, see portaudio.h for required behavior*/\r
+               next_rev_sleep = sleep_ms;\r
+       }\r
 \r
-    return paNoError;\r
-}\r
+       // Sleep diff from last call\r
+       if (stream->out.prevSleep)\r
+               Sleep(stream->out.prevSleep);\r
+       stream->out.prevSleep = next_rev_sleep;\r
+\r
+       // Feed engine\r
+       for (UINT32 i = 0; i < blocks; ++i)\r
+       {\r
+               // Get block frames\r
+               frames = stream->out.framesPerHostCallback;\r
+               if (frames > _frames)\r
+                       frames = _frames;\r
+\r
+               if (i)\r
+                       Sleep(block_sleep_ms);\r
+\r
+               UINT32 available;\r
+               while (frames != 0)\r
+               {\r
+                       // Check if blocking call must be interrupted\r
+                       if (WaitForSingleObject(stream->hCloseRequest, 0) != WAIT_TIMEOUT)\r
+                               break;\r
+\r
+                       // Get Read position\r
+                       UINT32 padding = 0;\r
+                       hr = stream->out.client->GetCurrentPadding(&padding);\r
+                       if (hr != S_OK)\r
+                       {\r
+                               LogHostError(hr);\r
+                               goto stream_wr_end;\r
+                       }\r
+\r
+                       // Get frames available\r
+                       if (frames >= padding)\r
+                               available = frames - padding;\r
+                       else\r
+                               available = frames;\r
+\r
+                       // Get buffer\r
+                       if ((hr = stream->rclient->GetBuffer(available, &data)) != S_OK)\r
+                       {\r
+                               // Buffer size is too big, waiting\r
+                               if (hr == AUDCLNT_E_BUFFER_TOO_LARGE)\r
+                                       continue; \r
+                               LogHostError(hr);\r
+                               goto stream_wr_end;\r
+                       }\r
+\r
+                       // Copy\r
+                       UINT32 buffer_size = available * stream->out.wavex.Format.nBlockAlign;\r
+                       memcpy(data, buffer, buffer_size);\r
+                       buffer += buffer_size;\r
+\r
+                       // Release buffer\r
+                       if ((hr = stream->rclient->ReleaseBuffer(available, 0)) != S_OK)\r
+                       {\r
+                               LogHostError(hr);\r
+                               goto stream_wr_end;\r
+                       }\r
+\r
+                       frames -= available;\r
+               }\r
+\r
+               _frames -= frames;\r
+       }\r
 \r
+stream_wr_end:\r
 \r
+       // Set prev time\r
+       stream->out.prevTime = ::timeGetTime();\r
+\r
+       // Notify blocking op has ended\r
+       SetEvent(stream->hBlockingOpStreamWR);\r
+\r
+       return (hr != S_OK ? paUnanticipatedHostError : paNoError);\r
+}\r
+\r
+// ------------------------------------------------------------------------------------------\r
+/* NOT TESTED */\r
 static signed long GetStreamReadAvailable( PaStream* s )\r
 {\r
-    PaWinWasapiStream *stream = (PaWinWasapiStream*)s;\r
+    PaWasapiStream *stream = (PaWasapiStream*)s;\r
 \r
-    /* suppress unused variable warnings */\r
-    (void) stream;\r
+       if (stream == NULL)\r
+               return paBadStreamPtr;\r
 \r
-    /* IMPLEMENT ME, see portaudio.h for required behavior*/\r
+       if (!stream->running)\r
+               return paStreamIsStopped;\r
 \r
-    return 0;\r
-}\r
+       if (stream->cclient == NULL)\r
+               return paBadStreamPtr;\r
+\r
+       HRESULT hr;\r
 \r
+       UINT32 pending = 0;\r
+       hr = stream->in.client->GetCurrentPadding(&pending);\r
+       if (hr != S_OK)\r
+       {\r
+               LogHostError(hr);\r
+               return paUnanticipatedHostError;\r
+       }\r
 \r
+    return (long)pending;\r
+}\r
+\r
+// ------------------------------------------------------------------------------------------\r
 static signed long GetStreamWriteAvailable( PaStream* s )\r
 {\r
-    PaWinWasapiStream *stream = (PaWinWasapiStream*)s;\r
+    PaWasapiStream *stream = (PaWasapiStream*)s;\r
 \r
-    /* suppress unused variable warnings */\r
-    (void) stream;\r
+       if (stream == NULL)\r
+               return paBadStreamPtr;\r
 \r
-    /* IMPLEMENT ME, see portaudio.h for required behavior*/\r
+       if (!stream->running)\r
+               return paStreamIsStopped;\r
 \r
-    return 0;\r
-}\r
+       if (stream->rclient == NULL)\r
+               return paBadStreamPtr;\r
 \r
+       UINT32 frames = stream->out.framesPerHostCallback;\r
+       HRESULT hr;\r
 \r
+       UINT32 padding = 0;\r
+       hr = stream->out.client->GetCurrentPadding(&padding);\r
+       if (hr != S_OK)\r
+       {\r
+               LogHostError(hr);\r
+               return paUnanticipatedHostError;\r
+       }\r
 \r
-/*\r
-    ExampleHostProcessingLoop() illustrates the kind of processing which may\r
-    occur in a host implementation.\r
+       // Calculate\r
+       frames -= padding;\r
 \r
-*/\r
+    return frames;\r
+}\r
+\r
+// ------------------------------------------------------------------------------------------\r
 static void WaspiHostProcessingLoop( void *inputBuffer,  long inputFrames,\r
                                      void *outputBuffer, long outputFrames,\r
                                      void *userData )\r
 {\r
-    PaWinWasapiStream *stream = (PaWinWasapiStream*)userData;\r
-    PaStreamCallbackTimeInfo timeInfo = {0,0,0}; /* IMPLEMENT ME */\r
+    PaWasapiStream *stream = (PaWasapiStream*)userData;\r
+    PaStreamCallbackTimeInfo timeInfo = {0,0,0};\r
+       PaStreamCallbackFlags flags = 0;\r
     int callbackResult;\r
     unsigned long framesProcessed;\r
+       HRESULT hResult;\r
+       UINT32 pending;\r
 \r
     PaUtil_BeginCpuLoadMeasurement( &stream->cpuLoadMeasurer );\r
 \r
-\r
     /*\r
-        IMPLEMENT ME:\r
+               Pa_GetStreamTime:\r
             - generate timing information\r
             - handle buffer slips\r
     */\r
+       timeInfo.currentTime = PaUtil_GetTime();\r
+       // Query input latency\r
+       if (stream->in.client != NULL)\r
+       {\r
+               PaTime pending_time;\r
+               if ((hResult = stream->in.client->GetCurrentPadding(&pending)) == S_OK)\r
+                       pending_time = ((PaTime)pending / (PaTime)stream->in.wavex.Format.nSamplesPerSec) + stream->in.latency_seconds;\r
+               else\r
+                       pending_time = (PaTime)stream->in.framesPerHostCallback / (PaTime)stream->in.wavex.Format.nSamplesPerSec;\r
+\r
+               timeInfo.inputBufferAdcTime = timeInfo.currentTime + pending_time;\r
+       }\r
+       // Query output current latency\r
+       if (stream->out.client != NULL)\r
+       {\r
+               PaTime pending_time;\r
+               if ((hResult = stream->out.client->GetCurrentPadding(&pending)) == S_OK)\r
+                       pending_time = ((PaTime)pending / (PaTime)stream->out.wavex.Format.nSamplesPerSec) + stream->out.latency_seconds;\r
+               else\r
+                       pending_time = (PaTime)stream->out.framesPerHostCallback / (PaTime)stream->out.wavex.Format.nSamplesPerSec;\r
+\r
+               timeInfo.outputBufferDacTime = timeInfo.currentTime + pending_time;\r
+       }\r
 \r
     /*\r
         If you need to byte swap or shift inputBuffer to convert it into a\r
         portaudio format, do it here.\r
     */\r
 \r
-\r
-\r
-    PaUtil_BeginBufferProcessing( &stream->bufferProcessor, &timeInfo, 0 /* IMPLEMENT ME: pass underflow/overflow flags when necessary */ );\r
+    PaUtil_BeginBufferProcessing( &stream->bufferProcessor, &timeInfo, flags );\r
 \r
     /*\r
         depending on whether the host buffers are interleaved, non-interleaved\r
@@ -1713,7 +2350,7 @@ static void WaspiHostProcessingLoop( void *inputBuffer,  long inputFrames,
         PaUtil_SetNonInterleaved*Channel() or PaUtil_Set*Channel() here.\r
     */\r
 \r
-    if( stream->bufferProcessor.inputChannelCount > 0 )\r
+    if (stream->bufferProcessor.inputChannelCount > 0)\r
     {\r
         PaUtil_SetInputFrameCount( &stream->bufferProcessor, inputFrames );\r
         PaUtil_SetInterleavedInputChannels( &stream->bufferProcessor,\r
@@ -1722,7 +2359,7 @@ static void WaspiHostProcessingLoop( void *inputBuffer,  long inputFrames,
             0 ); /* 0 - use inputChannelCount passed to init buffer processor */\r
     }\r
 \r
-    if( stream->bufferProcessor.outputChannelCount > 0 )\r
+    if (stream->bufferProcessor.outputChannelCount > 0)\r
     {\r
         PaUtil_SetOutputFrameCount( &stream->bufferProcessor, outputFrames);\r
         PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor,\r
@@ -1740,144 +2377,399 @@ static void WaspiHostProcessingLoop( void *inputBuffer,  long inputFrames,
     callbackResult = paContinue;\r
     framesProcessed = PaUtil_EndBufferProcessing( &stream->bufferProcessor, &callbackResult );\r
 \r
-\r
     /*\r
         If you need to byte swap or shift outputBuffer to convert it to\r
         host format, do it here.\r
     */\r
 \r
-    PaUtil_EndCpuLoadMeasurement( &stream->cpuLoadMeasurer, framesProcessed );\r
-\r
+       PaUtil_EndCpuLoadMeasurement( &stream->cpuLoadMeasurer, framesProcessed );\r
 \r
-    if( callbackResult == paContinue )\r
+    if (callbackResult == paContinue)\r
     {\r
         /* nothing special to do */\r
     }\r
-    else if( callbackResult == paAbort )\r
+    else \r
+       if (callbackResult == paAbort)\r
     {\r
-        /* IMPLEMENT ME - finish playback immediately  */\r
-\r
-        /* once finished, call the finished callback */\r
-        if( stream->streamRepresentation.streamFinishedCallback != 0 )\r
-            stream->streamRepresentation.streamFinishedCallback( stream->streamRepresentation.userData );\r
+               // stop stream\r
+        SetEvent(stream->hCloseRequest);\r
     }\r
     else\r
     {\r
-        /* User callback has asked us to stop with paComplete or other non-zero value */\r
-\r
-        /* IMPLEMENT ME - finish playback once currently queued audio has completed  */\r
-\r
-        /* once finished, call the finished callback */\r
-        if( stream->streamRepresentation.streamFinishedCallback != 0 )\r
-            stream->streamRepresentation.streamFinishedCallback( stream->streamRepresentation.userData );\r
+               // stop stream\r
+        SetEvent(stream->hCloseRequest);\r
     }\r
 }\r
 \r
+// ------------------------------------------------------------------------------------------\r
+HANDLE MMCSS_activate(const char *name)\r
+{\r
+    DWORD task_idx = 0;\r
+    HANDLE hTask = pAvSetMmThreadCharacteristics(name, &task_idx);\r
+    if (hTask == NULL)\r
+       {\r
+        PRINT(("WASAPI: AvSetMmThreadCharacteristics failed!\n"));\r
+    }\r
 \r
-void \r
-MMCSS_activate(){\r
+    /*BOOL priority_ok = pAvSetMmThreadPriority(hTask, AVRT_PRIORITY_NORMAL);\r
+    if (priority_ok == FALSE)\r
+       {\r
+        PRINT(("WASAPI: AvSetMmThreadPriority failed!\n"));\r
+    }*/\r
 \r
-    DWORD stuff=0;\r
-    HANDLE thCarac = pAvSetMmThreadCharacteristics("Pro Audio",&stuff);\r
-    if (!thCarac){\r
-        PRINT(("AvSetMmThreadCharacteristics failed!\n"));\r
+       // debug\r
+    {\r
+        int    cur_priority              = GetThreadPriority(GetCurrentThread());\r
+        DWORD  cur_priority_class = GetPriorityClass(GetCurrentProcess());\r
+               PRINT(("WASAPI: thread[ priority-0x%X class-0x%X ]\n", cur_priority, cur_priority_class));\r
     }\r
 \r
-    BOOL prio = pAvSetMmThreadPriority(thCarac,AVRT_PRIORITY_NORMAL);\r
-    if (!prio){\r
-        PRINT(("AvSetMmThreadPriority failed!\n"));\r
-    }\r
+       return hTask;\r
+}\r
 \r
-       //debug\r
-    {\r
-        HANDLE hh       = GetCurrentThread();\r
-        int  currprio   = GetThreadPriority(hh);\r
-        DWORD currclass = GetPriorityClass(GetCurrentProcess());\r
-        PRINT(("currprio 0x%X currclass 0x%X\n",currprio,currclass));\r
+// ------------------------------------------------------------------------------------------\r
+void MMCSS_deactivate(HANDLE hTask)\r
+{\r
+       if (!hTask)\r
+               return;\r
+\r
+       if (pAvRevertMmThreadCharacteristics(hTask) == FALSE)\r
+       {\r
+        PRINT(("WASAPI: AvRevertMmThreadCharacteristics failed!\n"));\r
     }\r
 }\r
 \r
+// ------------------------------------------------------------------------------------------\r
+PaError PaWasapi_ThreadPriorityBoost(void **hTask, int nPriorityClass)\r
+{\r
+       if (hTask == NULL)\r
+               return paUnanticipatedHostError;\r
+\r
+       static const char *mmcs_name[7] = \r
+       {\r
+               "Audio",\r
+               "Capture",\r
+               "Distribution",\r
+               "Games",\r
+               "Playback",\r
+               "Pro Audio",\r
+               "Window Manager"\r
+       };\r
+       if ((UINT32)nPriorityClass >= 7)\r
+               return paUnanticipatedHostError;\r
+\r
+       HANDLE task = MMCSS_activate(mmcs_name[nPriorityClass]);\r
+       if (task == NULL)\r
+               return paUnanticipatedHostError;\r
+\r
+       (*hTask) = task;\r
+       return paNoError;\r
+}\r
 \r
-DWORD WINAPI\r
-ProcThread(void* param){\r
-       HRESULT hResult;\r
-       MMCSS_activate();\r
+// ------------------------------------------------------------------------------------------\r
+PaError PaWasapi_ThreadPriorityRevert(void *hTask)\r
+{\r
+       if (hTask == NULL)\r
+               return paUnanticipatedHostError;\r
 \r
-    PaWinWasapiStream *stream = (PaWinWasapiStream*)param;\r
+       MMCSS_deactivate((HANDLE)hTask);\r
 \r
-       stream->hNotificationEvent = CreateEvent(NULL, \r
-                                                FALSE,  //bManualReset are we sure??\r
-                                                                                        FALSE, \r
-                                                                                        "PAWASA");\r
-       hResult = stream->out.client->SetEventHandle(stream->hNotificationEvent);\r
-       if (hResult != S_OK)\r
-               logAUDCLNT_E(hResult);\r
+       return paNoError;\r
+}\r
 \r
-       if (stream->out.client){\r
-               hResult = stream->out.client->Start();\r
-               if (hResult != S_OK)\r
-                       logAUDCLNT_E(hResult);\r
+// ------------------------------------------------------------------------------------------\r
+static __forceinline HRESULT FillOutputBuffer(PaWasapiStream *stream, PaWasapiHostProcessor *processor, UINT32 frames)\r
+{\r
+       HRESULT hr;\r
+       BYTE *data    = NULL;\r
+       DWORD flags   = 0;\r
+\r
+       // Get buffer\r
+       if ((hr = stream->rclient->GetBuffer(frames, &data)) != S_OK)\r
+               return LogHostError(hr);\r
+\r
+       // Process data\r
+       processor[S_OUTPUT].processor(NULL, 0, data, frames, processor[S_OUTPUT].userData);\r
+\r
+       // Release buffer\r
+       if ((hr = stream->rclient->ReleaseBuffer(frames, 0)) != S_OK)\r
+               LogHostError(hr);\r
+\r
+       return hr;\r
+}\r
+\r
+// ------------------------------------------------------------------------------------------\r
+static __forceinline HRESULT PollInputBuffer(PaWasapiStream *stream, PaWasapiHostProcessor *processor)\r
+{\r
+       HRESULT hr;\r
+       UINT32 frames;\r
+       BYTE *data = NULL;\r
+       DWORD flags = 0;\r
+\r
+       for (;;)\r
+       {\r
+               // Get the available data in the shared buffer.\r
+               if ((hr = stream->cclient->GetBuffer(&data, &frames, &flags, NULL, NULL)) != S_OK)\r
+               {\r
+                       if (hr == AUDCLNT_S_BUFFER_EMPTY)\r
+                               break; // capture buffer exhausted\r
+\r
+                       return LogHostError(hr);\r
+                       break;\r
+               }\r
+\r
+               // Detect silence\r
+               // if (flags & AUDCLNT_BUFFERFLAGS_SILENT)\r
+               //      data = NULL;\r
+\r
+               // Process data\r
+        processor[S_INPUT].processor(data, frames, NULL, 0, processor[S_INPUT].userData);\r
+\r
+               // Release buffer\r
+               if ((hr = stream->cclient->ReleaseBuffer(frames)) != S_OK)\r
+                       return LogHostError(hr);\r
+       }\r
+\r
+       return hr;\r
+}\r
+\r
+// ------------------------------------------------------------------------------------------\r
+void _OnStreamStop(PaWasapiStream *stream)\r
+{\r
+       // Stop INPUT client\r
+       if (stream->in.client != NULL)\r
+               stream->in.client->Stop();\r
+       // Stop OUTPUT client\r
+       if (stream->out.client != NULL)\r
+               stream->out.client->Stop();\r
+\r
+       // Restore thread priority\r
+       if (stream->hAvTask != NULL)\r
+       {\r
+               MMCSS_deactivate(stream->hAvTask);\r
+               stream->hAvTask = NULL;\r
        }\r
 \r
+    // Notify\r
+    if (stream->streamRepresentation.streamFinishedCallback != NULL)\r
+        stream->streamRepresentation.streamFinishedCallback(stream->streamRepresentation.userData);\r
+}\r
+\r
+// ------------------------------------------------------------------------------------------\r
+PA_THREAD_FUNC ProcThreadEvent(void *param)\r
+{\r
+       HANDLE event[S_COUNT];\r
+    PaWasapiHostProcessor processor[S_COUNT];\r
+       HRESULT hr;\r
+       DWORD dwResult;\r
+       BOOL bInitOutput = FALSE;\r
+    PaWasapiStream *stream = (PaWasapiStream *)param;\r
+\r
+    // Setup data processors\r
+    PaWasapiHostProcessor defaultProcessor;\r
+    defaultProcessor.processor = WaspiHostProcessingLoop;\r
+    defaultProcessor.userData  = stream;\r
+    processor[S_INPUT] = (stream->hostProcessOverrideInput.processor != NULL ? stream->hostProcessOverrideInput : defaultProcessor);\r
+    processor[S_OUTPUT] = (stream->hostProcessOverrideOutput.processor != NULL ? stream->hostProcessOverrideOutput : defaultProcessor);\r
+\r
+       // Initialize event to NULL first\r
+       event[S_INPUT]  = CreateEvent(NULL, FALSE, FALSE, NULL);\r
+       event[S_OUTPUT] = CreateEvent(NULL, FALSE, FALSE, NULL);\r
+\r
+       // Check on low resources and disallow any processing further\r
+       if (!event[S_INPUT] || !event[S_OUTPUT])\r
+       {\r
+               PRINT(("WASAPI Thread: failed creating one or two event handles\n"));\r
+               goto thread_end;\r
+       }\r
+\r
+       // Boost thread priority\r
+       stream->hAvTask = MMCSS_activate("Pro Audio");\r
+\r
+       // Initialize event & start INPUT stream\r
+       if (stream->in.client)\r
+       {\r
+               if ((hr = stream->in.client->SetEventHandle(event[S_INPUT])) != S_OK)\r
+                       LogHostError(hr);\r
+\r
+               if ((hr = stream->in.client->GetService(__uuidof(IAudioCaptureClient),(void**)&stream->cclient)) != S_OK)\r
+                       LogHostError(hr);\r
+\r
+               if ((hr = stream->in.client->Start()) != S_OK)\r
+                       LogHostError(hr);\r
+       }\r
+\r
+       // Initialize event & start OUTPUT stream\r
+       if (stream->out.client)\r
+       {\r
+               if ((hr = stream->out.client->SetEventHandle(event[S_OUTPUT])) != S_OK)\r
+                       LogHostError(hr);\r
+\r
+               if ((hr = stream->out.client->GetService(__uuidof(IAudioRenderClient),(void**)&stream->rclient)) != S_OK)\r
+                       LogHostError(hr);\r
+\r
+               // Preload buffer before start\r
+               if ((hr = FillOutputBuffer(stream, processor, stream->out.framesPerHostCallback)) != S_OK)\r
+                       LogHostError(hr);\r
+\r
+               // Start\r
+               if ((hr = stream->out.client->Start()) != S_OK)\r
+                       LogHostError(hr);\r
+       }\r
+\r
+       // Signal: stream running\r
        stream->running = true;\r
-       bool bOne = false;\r
-\r
-       while( !stream->closeRequest ) \r
-    { \r
-           //lets wait but have a 1 second timeout\r
-        DWORD dwResult = WaitForSingleObject(stream->hNotificationEvent, 1000);\r
-        switch( dwResult ) {\r
-               case WAIT_OBJECT_0: {\r
-\r
-                       unsigned long usingBS = stream->out.framesPerHostCallback;\r
-\r
-                       BYTE* indata  = 0;\r
-                       BYTE* outdata = 0;\r
-\r
-                       hResult = stream->rclient->GetBuffer(usingBS, &outdata);\r
-\r
-                       if (hResult != S_OK || !outdata) {\r
-                               //logAUDCLNT_E(hResult);\r
-                               //most probably shared mode and hResult=AUDCLNT_E_BUFFER_TOO_LARGE\r
-                               UINT32 padding = 0;\r
-                               hResult = stream->out.client->GetCurrentPadding(&padding);\r
-                               if (padding == 0)\r
-                                       break;  \r
-                               usingBS = usingBS-padding;\r
-                               if (usingBS == 0)\r
-                                       break;//huh?\r
-                               hResult = stream->rclient->GetBuffer(usingBS, &outdata);\r
-                               if (hResult != S_OK)//what can we do NOW??\r
-                                       break;\r
-                               //logAUDCLNT_E(hResult);                        \r
-                       }\r
-       \r
-                       WaspiHostProcessingLoop(indata, usingBS ,outdata, usingBS, stream);\r
 \r
-                       hResult = stream->rclient->ReleaseBuffer(usingBS, 0);\r
-                       if (hResult != S_OK)\r
-                               logAUDCLNT_E(hResult);\r
+       // Processing Loop\r
+       for (;;)\r
+    {\r
+           // 2 sec timeout\r
+        dwResult = WaitForMultipleObjects(S_COUNT, event, FALSE, 2000);\r
+\r
+               // Check for close event (after wait for buffers to avoid any calls to user\r
+               // callback when hCloseRequest was set)\r
+               if (WaitForSingleObject(stream->hCloseRequest, 0) != WAIT_TIMEOUT)\r
+                       break;\r
+\r
+               // Process S_INPUT/S_OUTPUT\r
+               switch (dwResult)\r
+               {\r
+               case WAIT_TIMEOUT: {\r
+                       PRINT(("WASAPI Thread: WAIT_TIMEOUT - probably bad audio driver or Vista x64 bug: use paWinWasapiPolling instead\n"));\r
+                       goto processing_stop;\r
+                       //SetEvent(stream->hCloseRequest);\r
+                       break; }\r
+\r
+               // Input stream\r
+               case WAIT_OBJECT_0 + S_INPUT: {\r
+            if (stream->cclient == NULL)\r
+                break;\r
+                       PollInputBuffer(stream, processor);\r
+                       break; }\r
 \r
-                        /*     This was suggested, but in my tests it doesnt seem to improve the \r
-                locking behaviour some drivers have running in exclusive mode.\r
-                if(!ResetEvent(stream->hNotificationEvent)){\r
-                                       logAUDCLNT_E(hResult);\r
-                               }\r
-             */\r
+               // Output stream\r
+               case WAIT_OBJECT_0 + S_OUTPUT: {\r
+            if (stream->rclient == NULL)\r
+                break;\r
+                       FillOutputBuffer(stream, processor, stream->out.framesPerHostCallback);\r
+                       break; }\r
+               }\r
+       }\r
 \r
-               } \r
-               break;\r
+processing_stop:\r
 \r
-        }\r
-    }\r
-       stream->out.client->Stop();\r
-    stream->closeRequest = false;\r
-    \r
-       return 0; \r
+       // Process stop\r
+       _OnStreamStop(stream);\r
+\r
+thread_end:\r
+\r
+       // Close event\r
+       CloseHandle(event[S_INPUT]);\r
+       CloseHandle(event[S_OUTPUT]);\r
+\r
+       // Notify thread is exiting\r
+       SetEvent(stream->hThreadDone);\r
+\r
+       return 0;\r
 }\r
 \r
+// ------------------------------------------------------------------------------------------\r
+PA_THREAD_FUNC ProcThreadPoll(void *param)\r
+{\r
+    PaWasapiHostProcessor processor[S_COUNT];\r
+       HRESULT hr;\r
+       DWORD dwResult = S_OUTPUT;\r
+       BOOL bInitOutput = FALSE;\r
+    PaWasapiStream *stream = (PaWasapiStream *)param;\r
+\r
+       // Calculate the actual duration of the allocated buffer.\r
+       DWORD sleep_ms = GetFramesSleepTime(stream->out.framesPerHostCallback, stream->out.wavex.Format.nSamplesPerSec);\r
+\r
+    // Setup data processors\r
+    PaWasapiHostProcessor defaultProcessor;\r
+    defaultProcessor.processor = WaspiHostProcessingLoop;\r
+    defaultProcessor.userData  = stream;\r
+    processor[S_INPUT] = (stream->hostProcessOverrideInput.processor != NULL ? stream->hostProcessOverrideInput : defaultProcessor);\r
+    processor[S_OUTPUT] = (stream->hostProcessOverrideOutput.processor != NULL ? stream->hostProcessOverrideOutput : defaultProcessor);\r
+\r
+       // Boost thread priority\r
+       stream->hAvTask = MMCSS_activate("Pro Audio");\r
+\r
+       // Initialize event & start INPUT stream\r
+       if (stream->in.client)\r
+       {\r
+               if ((hr = stream->in.client->GetService(__uuidof(IAudioCaptureClient),(void**)&stream->cclient)) != S_OK)\r
+                       LogHostError(hr);\r
+\r
+               if ((hr = stream->in.client->Start()) != S_OK)\r
+                       LogHostError(hr);\r
+       }\r
+\r
+\r
+       // Initialize event & start OUTPUT stream\r
+       if (stream->out.client)\r
+       {\r
+               if ((hr = stream->out.client->GetService(__uuidof(IAudioRenderClient),(void**)&stream->rclient)) != S_OK)\r
+                       LogHostError(hr);\r
+\r
+               // Preload buffer (obligatory, othervise ->Start() will fail)\r
+               if ((hr = FillOutputBuffer(stream, processor, stream->out.framesPerHostCallback)) != S_OK)\r
+                       LogHostError(hr);\r
+\r
+               // Start\r
+               if ((hr = stream->out.client->Start()) != S_OK)\r
+                       LogHostError(hr);\r
+       }\r
+\r
+       // Signal: stream running\r
+       stream->running = true;\r
+\r
+       // Processing Loop\r
+       while (WaitForSingleObject(stream->hCloseRequest, sleep_ms) == WAIT_TIMEOUT)\r
+    {\r
+               // Process S_INPUT/S_OUTPUT\r
+               switch (dwResult)\r
+               {\r
+               // Input stream\r
+               case S_INPUT: {\r
+            if (stream->cclient == NULL)\r
+                break;\r
+                       PollInputBuffer(stream, processor);\r
+                       break; }\r
+\r
+               // Output stream\r
+               case S_OUTPUT: {\r
+\r
+            if (stream->rclient == NULL)\r
+                break;\r
+\r
+                       UINT32 frames = stream->out.framesPerHostCallback;\r
+                       BYTE *data    = NULL;\r
+                       DWORD flags   = 0;\r
+\r
+                       // Get Read position\r
+                       UINT32 padding = 0;\r
+                       hr = stream->out.client->GetCurrentPadding(&padding);\r
+                       if (hr != S_OK || padding == 0)\r
+                               break;\r
+\r
+                       // Fill buffer\r
+                       frames -= padding;\r
+                       if (frames != 0)\r
+                               FillOutputBuffer(stream, processor, frames);\r
+\r
+                       break; }\r
+               }\r
+       }\r
+\r
+       // Process stop\r
+       _OnStreamStop(stream);\r
 \r
+       // Notify thread is exiting\r
+       SetEvent(stream->hThreadDone);\r
 \r
+       return 0;\r
+}\r
 \r
 #endif //VC 2005\r
 \r
@@ -1885,23 +2777,23 @@ ProcThread(void* param){
 \r
 \r
 #if 0\r
-                       if(bFirst) {                    \r
+                       if(bFirst) {\r
                                float masteur;\r
-                               hResult = stream->outVol->GetMasterVolumeLevelScalar(&masteur);\r
-                               if (hResult != S_OK)\r
-                                       logAUDCLNT_E(hResult);\r
+                               hr = stream->outVol->GetMasterVolumeLevelScalar(&masteur);\r
+                               if (hr != S_OK)\r
+                                       LogHostError(hr);\r
                                float chan1, chan2;\r
-                               hResult = stream->outVol->GetChannelVolumeLevelScalar(0, &chan1);\r
-                               if (hResult != S_OK)\r
-                                       logAUDCLNT_E(hResult);\r
-                               hResult = stream->outVol->GetChannelVolumeLevelScalar(1, &chan2);\r
-                               if (hResult != S_OK)\r
-                                       logAUDCLNT_E(hResult);\r
+                               hr = stream->outVol->GetChannelVolumeLevelScalar(0, &chan1);\r
+                               if (hr != S_OK)\r
+                                       LogHostError(hr);\r
+                               hr = stream->outVol->GetChannelVolumeLevelScalar(1, &chan2);\r
+                               if (hr != S_OK)\r
+                                       LogHostError(hr);\r
 \r
                                BOOL bMute;\r
-                               hResult = stream->outVol->GetMute(&bMute);\r
-                               if (hResult != S_OK)\r
-                                       logAUDCLNT_E(hResult);\r
+                               hr = stream->outVol->GetMute(&bMute);\r
+                               if (hr != S_OK)\r
+                                       LogHostError(hr);\r
 \r
                                stream->outVol->SetMasterVolumeLevelScalar(0.5, NULL);\r
                                stream->outVol->SetChannelVolumeLevelScalar(0, 0.5, NULL);\r
@@ -1909,4 +2801,4 @@ ProcThread(void* param){
                                stream->outVol->SetMute(FALSE, NULL);\r
                                bFirst = false;\r
                        }\r
-#endif
\ No newline at end of file
+#endif\r
index 9dd2c0d06f09dbf0bbaf8105eadf76d856cae7b0..916403b5b0f14b0779f42a3a4aea01cfab9795d2 100644 (file)
@@ -58,7 +58,7 @@ PaError PaWinMme_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiInd
 PaError PaWinDs_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
 PaError PaAsio_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
 PaError PaWinWdm_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
-PaError PaWinWasapi_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
+PaError PaWasapi_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
 
 #ifdef __cplusplus
 }
@@ -80,17 +80,17 @@ PaUtilHostApiInitializer *paHostApiInitializers[] =
         PaAsio_Initialize,
 #endif
 
-/*
 #ifndef PA_NO_WASAPI
-               PaWinWasapi_Initialize,
+               PaWasapi_Initialize,
 #endif
 
+/*
 #ifndef PA_NO_WDMKS
        PaWinWdm_Initialize,
 #endif
 */
 
-        PaSkeleton_Initialize, /* just for testing */
+        //PaSkeleton_Initialize, /* just for testing */
 
         0   /* NULL terminated array */
     };