]> Repos - portaudio/commitdiff
wasapi:
authordmitrykos <dmitrykos@0f58301d-fd10-0410-b4af-bbb618454e57>
Thu, 3 Feb 2011 15:15:44 +0000 (15:15 +0000)
committerdmitrykos <dmitrykos@0f58301d-fd10-0410-b4af-bbb618454e57>
Thu, 3 Feb 2011 15:15:44 +0000 (15:15 +0000)
 - fixed blocking interface, Input and Output
 - improved Exclusive Input device latency tunning
 - applied path provided by Jean-François Charron (D-BOX Technologies Inc.) which improves handling of COM objects in multi-threaded environment and initialization stage which could under certain conditions return paNoError event when initialization failed

src/hostapi/wasapi/pa_win_wasapi.c

index bf70d91b173f4be672fdb0f413d3c727aa53aa41..9211320d8ab8b5616f54f3459da9e987b95070e5 100644 (file)
@@ -71,8 +71,9 @@
 #include "pa_stream.h"\r
 #include "pa_cpuload.h"\r
 #include "pa_process.h"\r
-#include "pa_debugprint.h"\r
 #include "pa_win_wasapi.h"\r
+#include "pa_debugprint.h"\r
+#include "pa_ringbuffer.h"\r
 \r
 #ifndef NTDDI_VERSION\r
  \r
@@ -222,6 +223,11 @@ PA_THREAD_FUNC ProcThreadPoll(void *param);
 \r
 enum { S_INPUT = 0, S_OUTPUT = 1, S_COUNT = 2, S_FULLDUPLEX = 0 };\r
 \r
+// Number of packets which compose single contignous buffer. With trial and error it was calculated\r
+// that WASAPI Input sub-system uses 6 packets per whole buffer. Please provide more information\r
+// or corrections if available.\r
+enum { WASAPI_PACKETS_PER_INPUT_BUFFER = 6 };\r
+\r
 #define STATIC_ARRAY_SIZE(array) (sizeof(array)/sizeof(array[0]))\r
 \r
 #define PRINT(x) PA_DEBUG(x);\r
@@ -229,12 +235,16 @@ enum { S_INPUT = 0, S_OUTPUT = 1, S_COUNT = 2, S_FULLDUPLEX = 0 };
 #define PA_SKELETON_SET_LAST_HOST_ERROR( errorCode, errorText ) \\r
     PaUtil_SetLastHostErrorInfo( paWASAPI, errorCode, errorText )\r
 \r
-#define PA_WASAPI__IS_FULLDUPLEX(STREAM) ((STREAM)->in.client && (STREAM)->out.client)\r
+#define PA_WASAPI__IS_FULLDUPLEX(STREAM) ((STREAM)->in.clientProc && (STREAM)->out.clientProc)\r
 \r
 #ifndef IF_FAILED_JUMP\r
 #define IF_FAILED_JUMP(hr, label) if(FAILED(hr)) goto label;\r
 #endif\r
 \r
+#ifndef IF_FAILED_INTERNAL_ERROR_JUMP\r
+#define IF_FAILED_INTERNAL_ERROR_JUMP(hr, error, label) if(FAILED(hr)) { error = paInternalError; goto label; }\r
+#endif\r
+\r
 #define SAFE_CLOSE(h) if ((h) != NULL) { CloseHandle((h)); (h) = NULL; }\r
 #define SAFE_RELEASE(punk) if ((punk) != NULL) { (punk)->lpVtbl->Release((punk)); (punk) = NULL; }\r
 \r
@@ -353,10 +363,10 @@ PaWasapiDeviceInfo;
 typedef struct\r
 {\r
     PaUtilHostApiRepresentation inheritedHostApiRep;\r
-    PaUtilStreamInterface callbackStreamInterface;\r
-    PaUtilStreamInterface blockingStreamInterface;\r
+    PaUtilStreamInterface       callbackStreamInterface;\r
+    PaUtilStreamInterface       blockingStreamInterface;\r
 \r
-    PaUtilAllocationGroup *allocations;\r
+    PaUtilAllocationGroup      *allocations;\r
 \r
     /* implementation specific data goes here */\r
 \r
@@ -395,12 +405,15 @@ PaWasapiAudioClientParams;
 /* PaWasapiStream - a stream data structure specifically for this implementation */\r
 typedef struct PaWasapiSubStream\r
 {\r
-    IAudioClient        *client;\r
+    IAudioClient        *clientParent;\r
+       IStream                         *clientStream;\r
+       IAudioClient            *clientProc;\r
+\r
     WAVEFORMATEXTENSIBLE wavex;\r
     UINT32               bufferSize;\r
-    REFERENCE_TIME       device_latency;\r
+    REFERENCE_TIME       deviceLatency;\r
     REFERENCE_TIME       period;\r
-       double                           latency_seconds;\r
+       double                           latencySeconds;\r
     UINT32                              framesPerHostCallback;\r
        AUDCLNT_SHAREMODE    shareMode;\r
        UINT32               streamFlags; // AUDCLNT_STREAMFLAGS_EVENTCALLBACK, ...\r
@@ -412,15 +425,14 @@ typedef struct PaWasapiSubStream
        UINT32               framesPerBuffer;   //!< number of frames per 1 buffer\r
        BOOL                 userBufferAndHostMatch;\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
        // Used for Mono >> Stereo workaround, if driver does not support it\r
        // (in Exclusive mode WASAPI usually refuses to operate with Mono (1-ch)\r
        void                *monoBuffer;         //!< pointer to buffer\r
        UINT32               monoBufferSize; //!< buffer size in bytes\r
        MixMonoToStereoF     monoMixer;          //!< pointer to mixer function\r
+\r
+       PaUtilRingBuffer    *tailBuffer;       //!< buffer with trailing sample for blocking mode operations (only for Input)\r
+       void                *tailBufferMemory; //!< tail buffer memory region\r
 }\r
 PaWasapiSubStream;\r
 \r
@@ -438,18 +450,22 @@ typedef struct PaWasapiStream
 {\r
        /* IMPLEMENT ME: rename this */\r
     PaUtilStreamRepresentation streamRepresentation;\r
-    PaUtilCpuLoadMeasurer cpuLoadMeasurer;\r
-    PaUtilBufferProcessor bufferProcessor;\r
+    PaUtilCpuLoadMeasurer      cpuLoadMeasurer;\r
+    PaUtilBufferProcessor      bufferProcessor;\r
 \r
     // input\r
-       PaWasapiSubStream in;\r
-    IAudioCaptureClient *cclient;\r
-    IAudioEndpointVolume *inVol;\r
+       PaWasapiSubStream          in;\r
+    IAudioCaptureClient       *captureClientParent;\r
+       IStream                   *captureClientStream;\r
+       IAudioCaptureClient       *captureClient;\r
+    IAudioEndpointVolume      *inVol;\r
 \r
        // output\r
-       PaWasapiSubStream out;\r
-    IAudioRenderClient  *rclient;\r
-       IAudioEndpointVolume *outVol;\r
+       PaWasapiSubStream          out;\r
+    IAudioRenderClient        *renderClientParent;\r
+       IStream                   *renderClientStream;\r
+       IAudioRenderClient        *renderClient;\r
+       IAudioEndpointVolume      *outVol;\r
 \r
        // event handles for event-driven processing mode\r
        HANDLE event[S_COUNT];\r
@@ -487,9 +503,13 @@ typedef struct PaWasapiStream
 PaWasapiStream;\r
 \r
 // Local stream methods\r
-static void _OnStreamStop(PaWasapiStream *stream);\r
-static void _FinishStream(PaWasapiStream *stream);\r
-static void _CleanupStream(PaWasapiStream *stream);\r
+void _StreamOnStop(PaWasapiStream *stream);\r
+void _StreamFinish(PaWasapiStream *stream);\r
+void _StreamCleanup(PaWasapiStream *stream);\r
+HRESULT _PollGetOutputFramesAvailable(PaWasapiStream *stream, UINT32 *available);\r
+HRESULT _PollGetInputFramesAvailable(PaWasapiStream *stream, UINT32 *available);\r
+void *PaWasapi_ReallocateMemory(void *ptr, size_t size);\r
+void PaWasapi_FreeMemory(void *ptr);\r
 \r
 // Local statics\r
 static volatile BOOL  g_WasapiCOMInit    = FALSE;\r
@@ -557,6 +577,46 @@ static PaError __LogPaError(PaError err, const char *func, const char *file, int
        return err;\r
 }\r
 \r
+// ------------------------------------------------------------------------------------------\r
+/*! \class ThreadSleepScheduler\r
+           Allows to emulate thread sleep of less than 1 millisecond under Windows. Scheduler\r
+                  calculates number of times the thread must run untill next sleep of 1 millisecond.\r
+                  It does not make thread sleeping for real number of microseconds but rather controls\r
+                  how many of imaginary microseconds the thread task can allow thread to sleep.\r
+*/\r
+typedef struct ThreadIdleScheduler\r
+{\r
+       UINT32 m_idle_microseconds; //!< number of microseconds to sleep\r
+       UINT32 m_next_sleep;        //!< next sleep round\r
+       UINT32 m_i;                                     //!< current round iterator position\r
+       UINT32 m_resolution;            //!< resolution in number of milliseconds\r
+}\r
+ThreadIdleScheduler;\r
+//! Setup scheduler.\r
+static void ThreadIdleScheduler_Setup(ThreadIdleScheduler *sched, UINT32 resolution, UINT32 microseconds)\r
+{\r
+       assert(microseconds != 0);\r
+       assert(resolution != 0);\r
+       assert((resolution * 1000) >= microseconds);\r
+\r
+       memset(sched, 0, sizeof(*sched));\r
+\r
+       sched->m_idle_microseconds = microseconds;\r
+       sched->m_resolution        = resolution;\r
+       sched->m_next_sleep        = (resolution * 1000) / microseconds;\r
+}\r
+//! Iterate and check if can sleep.\r
+static UINT32 ThreadIdleScheduler_NextSleep(ThreadIdleScheduler *sched)\r
+{\r
+       // advance and check if thread can sleep\r
+       if (++ sched->m_i == sched->m_next_sleep)\r
+       {\r
+               sched->m_i = 0;\r
+               return sched->m_resolution;\r
+       }\r
+       return 0;\r
+}\r
+\r
 // ------------------------------------------------------------------------------------------\r
 /*static double nano100ToMillis(REFERENCE_TIME ref)\r
 {\r
@@ -657,6 +717,16 @@ static UINT32 ALIGN_FWD(UINT32 v, UINT32 align)
        return v + (align - remainder);\r
 }\r
 \r
+// ------------------------------------------------------------------------------------------\r
+// Get next value power of 2\r
+UINT32 ALIGN_NEXT_POW2(UINT32 v)\r
+{\r
+       UINT32 v2 = 1;\r
+       while (v > (v2 <<= 1)) { }\r
+       v = v2;\r
+       return v;\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
@@ -1032,7 +1102,10 @@ PaError PaWasapi_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiInd
     paWasapi->enumerator = NULL;\r
     hr = CoCreateInstance(&pa_CLSID_IMMDeviceEnumerator, NULL, CLSCTX_INPROC_SERVER,\r
              &pa_IID_IMMDeviceEnumerator, (void **)&paWasapi->enumerator);\r
-    IF_FAILED_JUMP(hr, error);\r
+    \r
+       // We need to set the result to a value otherwise we will return paNoError\r
+       // [IF_FAILED_JUMP(hResult, error);]\r
+       IF_FAILED_INTERNAL_ERROR_JUMP(hr, result, error);\r
 \r
     // getting default device ids in the eMultimedia "role"\r
     {\r
@@ -1041,14 +1114,19 @@ PaError PaWasapi_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiInd
             hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(paWasapi->enumerator, eRender, eMultimedia, &defaultRenderer);\r
             if (hr != S_OK)\r
                        {\r
-                               if (hr != E_NOTFOUND)\r
-                                       IF_FAILED_JUMP(hr, error);\r
+                               if (hr != E_NOTFOUND) {\r
+                                       // We need to set the result to a value otherwise we will return paNoError\r
+                                       // [IF_FAILED_JUMP(hResult, error);]\r
+                                       IF_FAILED_INTERNAL_ERROR_JUMP(hr, result, error);\r
+                               }\r
                        }\r
                        else\r
                        {\r
                                WCHAR *pszDeviceId = NULL;\r
                                hr = IMMDevice_GetId(defaultRenderer, &pszDeviceId);\r
-                               IF_FAILED_JUMP(hr, error);\r
+                               // We need to set the result to a value otherwise we will return paNoError\r
+                               // [IF_FAILED_JUMP(hResult, error);]\r
+                               IF_FAILED_INTERNAL_ERROR_JUMP(hr, result, error);\r
                                wcsncpy(paWasapi->defaultRenderer, pszDeviceId, MAX_STR_LEN-1);\r
                                CoTaskMemFree(pszDeviceId);\r
                                IMMDevice_Release(defaultRenderer);\r
@@ -1060,14 +1138,19 @@ PaError PaWasapi_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiInd
             hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(paWasapi->enumerator, eCapture, eMultimedia, &defaultCapturer);\r
             if (hr != S_OK)\r
                        {\r
-                               if (hr != E_NOTFOUND)\r
-                                       IF_FAILED_JUMP(hr, error);\r
+                               if (hr != E_NOTFOUND) {\r
+                                       // We need to set the result to a value otherwise we will return paNoError\r
+                                       // [IF_FAILED_JUMP(hResult, error);]\r
+                                       IF_FAILED_INTERNAL_ERROR_JUMP(hr, result, error);\r
+                               }\r
                        }\r
                        else\r
                        {\r
                                WCHAR *pszDeviceId = NULL;\r
                                hr = IMMDevice_GetId(defaultCapturer, &pszDeviceId);\r
-                               IF_FAILED_JUMP(hr, error);\r
+                               // We need to set the result to a value otherwise we will return paNoError\r
+                               // [IF_FAILED_JUMP(hResult, error);]\r
+                               IF_FAILED_INTERNAL_ERROR_JUMP(hr, result, error);\r
                                wcsncpy(paWasapi->defaultCapturer, pszDeviceId, MAX_STR_LEN-1);\r
                                CoTaskMemFree(pszDeviceId);\r
                                IMMDevice_Release(defaultCapturer);\r
@@ -1076,12 +1159,16 @@ PaError PaWasapi_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiInd
     }\r
 \r
     hr = IMMDeviceEnumerator_EnumAudioEndpoints(paWasapi->enumerator, eAll, DEVICE_STATE_ACTIVE, &pEndPoints);\r
-    IF_FAILED_JUMP(hr, error);\r
+       // We need to set the result to a value otherwise we will return paNoError\r
+       // [IF_FAILED_JUMP(hResult, error);]\r
+       IF_FAILED_INTERNAL_ERROR_JUMP(hr, result, error);\r
 \r
     hr = IMMDeviceCollection_GetCount(pEndPoints, &paWasapi->deviceCount);\r
-    IF_FAILED_JUMP(hr, error);\r
+       // We need to set the result to a value otherwise we will return paNoError\r
+       // [IF_FAILED_JUMP(hResult, error);]\r
+       IF_FAILED_INTERNAL_ERROR_JUMP(hr, result, error);\r
 \r
-    paWasapi->devInfo = (PaWasapiDeviceInfo *)malloc(sizeof(PaWasapiDeviceInfo) * paWasapi->deviceCount);\r
+    paWasapi->devInfo = (PaWasapiDeviceInfo *)PaUtil_AllocateMemory(sizeof(PaWasapiDeviceInfo) * paWasapi->deviceCount);\r
        for (i = 0; i < paWasapi->deviceCount; ++i)\r
                memset(&paWasapi->devInfo[i], 0, sizeof(PaWasapiDeviceInfo));\r
 \r
@@ -1115,13 +1202,17 @@ PaError PaWasapi_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiInd
                        PA_DEBUG(("WASAPI: ---------------\n"));\r
 \r
             hr = IMMDeviceCollection_Item(pEndPoints, i, &paWasapi->devInfo[i].device);\r
-            IF_FAILED_JUMP(hr, error);\r
+                       // We need to set the result to a value otherwise we will return paNoError\r
+                       // [IF_FAILED_JUMP(hResult, error);]\r
+                       IF_FAILED_INTERNAL_ERROR_JUMP(hr, result, error);\r
 \r
             // getting ID\r
             {\r
                 WCHAR *pszDeviceId = NULL;\r
                 hr = IMMDevice_GetId(paWasapi->devInfo[i].device, &pszDeviceId);\r
-                IF_FAILED_JUMP(hr, error);\r
+                               // We need to set the result to a value otherwise we will return paNoError\r
+                               // [IF_FAILED_JUMP(hr, error);]\r
+                               IF_FAILED_INTERNAL_ERROR_JUMP(hr, result, error);\r
                 wcsncpy(paWasapi->devInfo[i].szDeviceID, pszDeviceId, MAX_STR_LEN-1);\r
                 CoTaskMemFree(pszDeviceId);\r
 \r
@@ -1136,7 +1227,9 @@ PaError PaWasapi_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiInd
             }\r
 \r
             hr = IMMDevice_GetState(paWasapi->devInfo[i].device, &paWasapi->devInfo[i].state);\r
-            IF_FAILED_JUMP(hr, error);\r
+                       // We need to set the result to a value otherwise we will return paNoError\r
+                       // [IF_FAILED_JUMP(hResult, error);]\r
+                       IF_FAILED_INTERNAL_ERROR_JUMP(hr, result, error);\r
 \r
             if (paWasapi->devInfo[i].state != DEVICE_STATE_ACTIVE)\r
                        {\r
@@ -1146,7 +1239,9 @@ PaError PaWasapi_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiInd
             {\r
                 IPropertyStore *pProperty;\r
                 hr = IMMDevice_OpenPropertyStore(paWasapi->devInfo[i].device, STGM_READ, &pProperty);\r
-                IF_FAILED_JUMP(hr, error);\r
+                               // We need to set the result to a value otherwise we will return paNoError\r
+                               // [IF_FAILED_JUMP(hResult, error);]\r
+                               IF_FAILED_INTERNAL_ERROR_JUMP(hr, result, error);\r
 \r
                 // "Friendly" Name\r
                 {\r
@@ -1154,7 +1249,9 @@ PaError PaWasapi_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiInd
                     PROPVARIANT value;\r
                     PropVariantInit(&value);\r
                     hr = IPropertyStore_GetValue(pProperty, &PKEY_Device_FriendlyName, &value);\r
-                    IF_FAILED_JUMP(hr, error);\r
+                                       // We need to set the result to a value otherwise we will return paNoError\r
+                                       // [IF_FAILED_JUMP(hResult, error);]\r
+                                       IF_FAILED_INTERNAL_ERROR_JUMP(hr, result, error);\r
                     deviceInfo->name = NULL;\r
                     deviceName = (char *)PaUtil_GroupAllocateMemory(paWasapi->allocations, MAX_STR_LEN + 1);\r
                     if (deviceName == NULL)\r
@@ -1176,7 +1273,9 @@ PaError PaWasapi_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiInd
                     PROPVARIANT value;\r
                     PropVariantInit(&value);\r
                     hr = IPropertyStore_GetValue(pProperty, &PKEY_AudioEngine_DeviceFormat, &value);\r
-                    IF_FAILED_JUMP(hr, error);\r
+                                       // We need to set the result to a value otherwise we will return paNoError\r
+                                       // [IF_FAILED_JUMP(hResult, error);]\r
+                                       IF_FAILED_INTERNAL_ERROR_JUMP(hr, result, error);\r
                                        memcpy(&paWasapi->devInfo[i].DefaultFormat, value.blob.pBlobData, min(sizeof(paWasapi->devInfo[i].DefaultFormat), value.blob.cbSize));\r
                     // cleanup\r
                     PropVariantClear(&value);\r
@@ -1187,7 +1286,9 @@ PaError PaWasapi_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiInd
                     PROPVARIANT value;\r
                     PropVariantInit(&value);\r
                     hr = IPropertyStore_GetValue(pProperty, &PKEY_AudioEndpoint_FormFactor, &value);\r
-                    IF_FAILED_JUMP(hr, error);\r
+                                       // We need to set the result to a value otherwise we will return paNoError\r
+                                       // [IF_FAILED_JUMP(hResult, error);]\r
+                                       IF_FAILED_INTERNAL_ERROR_JUMP(hr, result, error);\r
                                        // set\r
                                        #if defined(DUMMYUNIONNAME) && defined(NONAMELESSUNION)\r
                                                // avoid breaking strict-aliasing rules in such line: (EndpointFormFactor)(*((UINT *)(((WORD *)&value.wReserved3)+1)));\r
@@ -1224,12 +1325,16 @@ PaError PaWasapi_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiInd
 \r
                 hr = IMMDevice_Activate(paWasapi->devInfo[i].device, &pa_IID_IAudioClient,\r
                                        CLSCTX_INPROC_SERVER, NULL, (void **)&tmpClient);\r
-                IF_FAILED_JUMP(hr, error);\r
+                               // We need to set the result to a value otherwise we will return paNoError\r
+                               // [IF_FAILED_JUMP(hResult, error);]\r
+                               IF_FAILED_INTERNAL_ERROR_JUMP(hr, result, error);\r
 \r
                 hr = IAudioClient_GetDevicePeriod(tmpClient,\r
                     &paWasapi->devInfo[i].DefaultDevicePeriod,\r
                     &paWasapi->devInfo[i].MinimumDevicePeriod);\r
-                IF_FAILED_JUMP(hr, error);\r
+                               // We need to set the result to a value otherwise we will return paNoError\r
+                               // [IF_FAILED_JUMP(hResult, error);]\r
+                               IF_FAILED_INTERNAL_ERROR_JUMP(hr, result, error);\r
 \r
                 //hr = tmpClient->GetMixFormat(&paWasapi->devInfo[i].MixFormat);\r
 \r
@@ -1242,6 +1347,8 @@ PaError PaWasapi_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiInd
                                        //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(hr);\r
+                                       // We need to set the result to a value otherwise we will return paNoError\r
+                                       result = paInternalError;\r
                                        goto error;\r
                                }\r
             }\r
@@ -1268,6 +1375,8 @@ PaError PaWasapi_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiInd
                                break; }\r
             default:\r
                 PRINT(("WASAPI:%d| bad Data Flow!\n", i));\r
+                               // We need to set the result to a value otherwise we will return paNoError\r
+                               result = paInternalError;\r
                 //continue; // do not skip from list, allow to initialize\r
             break;\r
             }\r
@@ -1310,6 +1419,11 @@ error:
 \r
        Terminate((PaUtilHostApiRepresentation *)paWasapi);\r
 \r
+       // Safety if error was not set so that we do not think initialize was a success\r
+       if (result == paNoError) {\r
+               result = paInternalError;\r
+       }\r
+\r
     return result;\r
 }\r
 \r
@@ -1332,7 +1446,7 @@ static void Terminate( PaUtilHostApiRepresentation *hostApi )
                //if (info->MixFormat)\r
         //    CoTaskMemFree(info->MixFormat);\r
     }\r
-    free(paWasapi->devInfo);\r
+    PaUtil_FreeMemory(paWasapi->devInfo);\r
 \r
     if (paWasapi->allocations)\r
        {\r
@@ -2038,7 +2152,6 @@ static HRESULT CreateAudioClient(PaWasapiStream *pStream, PaWasapiSubStream *pSu
        const UINT32 userFramesPerBuffer = framesPerLatency;\r
     IAudioClient *audioClient       = NULL;\r
 \r
-\r
        // Validate parameters\r
     if (!pSub || !pInfo || !params)\r
         return E_POINTER;\r
@@ -2124,6 +2237,13 @@ static HRESULT CreateAudioClient(PaWasapiStream *pStream, PaWasapiSubStream *pSu
        if (framesPerLatency == 0)\r
                framesPerLatency = MakeFramesFromHns(pInfo->DefaultDevicePeriod, pSub->wavex.Format.nSamplesPerSec);\r
 \r
+       //! Exclusive Input stream renders data in 6 packets, we must set then the size of\r
+       //! single packet, total buffer size, e.g. required latency will be PacketSize * 6\r
+       if (!output && (pSub->shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE))\r
+       {\r
+               framesPerLatency /= WASAPI_PACKETS_PER_INPUT_BUFFER;\r
+       }\r
+\r
        // Calculate aligned period\r
        _CalculateAlignedPeriod(pSub, &framesPerLatency, ALIGN_BWD);\r
 \r
@@ -2345,8 +2465,8 @@ static HRESULT CreateAudioClient(PaWasapiStream *pStream, PaWasapiSubStream *pSu
     }\r
 \r
     // Set client\r
-       pSub->client = audioClient;\r
-    IAudioClient_AddRef(pSub->client);\r
+       pSub->clientParent = audioClient;\r
+    IAudioClient_AddRef(pSub->clientParent);\r
 \r
        // Recalculate buffers count\r
        _RecalculateBuffersCount(pSub,\r
@@ -2389,7 +2509,7 @@ static PaError ActivateAudioClientOutput(PaWasapiStream *stream)
         return paInvalidDevice;*/\r
 \r
     // Get max possible buffer size to check if it is not less than that we request\r
-    hr = IAudioClient_GetBufferSize(stream->out.client, &maxBufferSize);\r
+    hr = IAudioClient_GetBufferSize(stream->out.clientParent, &maxBufferSize);\r
     if (hr != S_OK)\r
        {\r
                LogHostError(hr);\r
@@ -2402,14 +2522,14 @@ static PaError ActivateAudioClientOutput(PaWasapiStream *stream)
 \r
        // Get interface latency (actually uneeded as we calculate latency from the size\r
        // of maxBufferSize).\r
-    hr = IAudioClient_GetStreamLatency(stream->out.client, &stream->out.device_latency);\r
+    hr = IAudioClient_GetStreamLatency(stream->out.clientParent, &stream->out.deviceLatency);\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.device_latency);\r
+       //stream->out.latencySeconds = nano100ToSeconds(stream->out.deviceLatency);\r
 \r
     // Number of frames that are required at each period\r
        stream->out.framesPerHostCallback = maxBufferSize;\r
@@ -2422,9 +2542,9 @@ static PaError ActivateAudioClientOutput(PaWasapiStream *stream)
        buffer_latency = (PaTime)maxBufferSize / stream->out.wavex.Format.nSamplesPerSec;\r
 \r
        // Append buffer latency to interface latency in shared mode (see GetStreamLatency notes)\r
-       stream->out.latency_seconds = buffer_latency;\r
+       stream->out.latencySeconds = buffer_latency;\r
 \r
-       PRINT(("WASAPI::OpenStream(output): framesPerUser[ %d ] framesPerHost[ %d ] latency[ %.02fms ] exclusive[ %s ] wow64_fix[ %s ] mode[ %s ]\n", (UINT32)framesPerBuffer, (UINT32)stream->out.framesPerHostCallback, (float)(stream->out.latency_seconds*1000.0f), (stream->out.shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE ? "YES" : "NO"), (stream->out.params.wow64_workaround ? "YES" : "NO"), (stream->out.streamFlags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK ? "EVENT" : "POLL")));\r
+       PRINT(("WASAPI::OpenStream(output): framesPerUser[ %d ] framesPerHost[ %d ] latency[ %.02fms ] exclusive[ %s ] wow64_fix[ %s ] mode[ %s ]\n", (UINT32)framesPerBuffer, (UINT32)stream->out.framesPerHostCallback, (float)(stream->out.latencySeconds*1000.0f), (stream->out.shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE ? "YES" : "NO"), (stream->out.params.wow64_workaround ? "YES" : "NO"), (stream->out.streamFlags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK ? "EVENT" : "POLL")));\r
 \r
        return paNoError;\r
 \r
@@ -2441,7 +2561,7 @@ static PaError ActivateAudioClientInput(PaWasapiStream *stream)
 \r
        UINT32 maxBufferSize   = 0;\r
        PaTime buffer_latency  = 0;\r
-       UINT32 framesPerBuffer = stream->out.params.frames_per_buffer;\r
+       UINT32 framesPerBuffer = stream->in.params.frames_per_buffer;\r
 \r
        // Create Audio client\r
        hr = CreateAudioClient(stream, &stream->in, FALSE, &result);\r
@@ -2461,7 +2581,7 @@ static PaError ActivateAudioClientInput(PaWasapiStream *stream)
         return paInvalidDevice;*/\r
 \r
     // Get max possible buffer size to check if it is not less than that we request\r
-    hr = IAudioClient_GetBufferSize(stream->in.client, &maxBufferSize);\r
+    hr = IAudioClient_GetBufferSize(stream->in.clientParent, &maxBufferSize);\r
     if (hr != S_OK)\r
        {\r
                LogHostError(hr);\r
@@ -2474,14 +2594,14 @@ static PaError ActivateAudioClientInput(PaWasapiStream *stream)
 \r
        // Get interface latency (actually uneeded as we calculate latency from the size\r
        // of maxBufferSize).\r
-    hr = IAudioClient_GetStreamLatency(stream->in.client, &stream->in.device_latency);\r
+    hr = IAudioClient_GetStreamLatency(stream->in.clientParent, &stream->in.deviceLatency);\r
     if (hr != S_OK)\r
        {\r
                LogHostError(hr);\r
                LogPaError(result = paInvalidDevice);\r
                goto error;\r
        }\r
-       //stream->in.latency_seconds = nano100ToSeconds(stream->in.device_latency);\r
+       //stream->in.latencySeconds = nano100ToSeconds(stream->in.deviceLatency);\r
 \r
     // Number of frames that are required at each period\r
        stream->in.framesPerHostCallback = maxBufferSize;\r
@@ -2494,9 +2614,9 @@ static PaError ActivateAudioClientInput(PaWasapiStream *stream)
        buffer_latency = (PaTime)maxBufferSize / stream->in.wavex.Format.nSamplesPerSec;\r
 \r
        // Append buffer latency to interface latency in shared mode (see GetStreamLatency notes)\r
-       stream->in.latency_seconds = buffer_latency;\r
+       stream->in.latencySeconds = buffer_latency;\r
 \r
-       PRINT(("WASAPI::OpenStream(input): framesPerUser[ %d ] framesPerHost[ %d ] latency[ %.02fms ] exclusive[ %s ] wow64_fix[ %s ] mode[ %s ]\n", (UINT32)framesPerBuffer, (UINT32)stream->in.framesPerHostCallback, (float)(stream->in.latency_seconds*1000.0f), (stream->in.shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE ? "YES" : "NO"), (stream->in.params.wow64_workaround ? "YES" : "NO"), (stream->in.streamFlags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK ? "EVENT" : "POLL")));\r
+       PRINT(("WASAPI::OpenStream(input): framesPerUser[ %d ] framesPerHost[ %d ] latency[ %.02fms ] exclusive[ %s ] wow64_fix[ %s ] mode[ %s ]\n", (UINT32)framesPerBuffer, (UINT32)stream->in.framesPerHostCallback, (float)(stream->in.latencySeconds*1000.0f), (stream->in.shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE ? "YES" : "NO"), (stream->in.params.wow64_workaround ? "YES" : "NO"), (stream->in.streamFlags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK ? "EVENT" : "POLL")));\r
 \r
        return paNoError;\r
 \r
@@ -2643,6 +2763,46 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
             stream->hostProcessOverrideInput.processor = inputStreamInfo->hostProcessorInput;\r
             stream->hostProcessOverrideInput.userData = userData;\r
         }\r
+\r
+               // Only get IAudioCaptureClient input once here instead of getting it at multiple places based on the use\r
+               hr = IAudioClient_GetService(stream->in.clientParent, &pa_IID_IAudioCaptureClient, (void **)&stream->captureClientParent);\r
+               if (hr != S_OK)\r
+               {\r
+                       LogHostError(hr);\r
+                       LogPaError(result = paUnanticipatedHostError);\r
+                       goto error;\r
+               }\r
+\r
+               // Create ring buffer for blocking mode (It is needed because we fetch Input packets, not frames,\r
+               // and thus we have to save partial packet if such remains unread)\r
+               if (stream->in.params.blocking == TRUE)\r
+               {\r
+                       UINT32 bufferFrames = ALIGN_NEXT_POW2((stream->in.framesPerHostCallback / WASAPI_PACKETS_PER_INPUT_BUFFER) * 2);\r
+                       UINT32 frameSize    = stream->in.wavex.Format.nBlockAlign;\r
+\r
+                       // buffer\r
+                       if ((stream->in.tailBuffer = PaUtil_AllocateMemory(sizeof(PaUtilRingBuffer))) == NULL)\r
+                       {\r
+                               LogPaError(result = paInsufficientMemory);\r
+                               goto error;\r
+                       }\r
+                       memset(stream->in.tailBuffer, 0, sizeof(PaUtilRingBuffer));\r
+\r
+                       // buffer memory region\r
+                       stream->in.tailBufferMemory = PaUtil_AllocateMemory(frameSize * bufferFrames);\r
+                       if (stream->in.tailBufferMemory == NULL)\r
+                       {\r
+                               LogPaError(result = paInsufficientMemory);\r
+                               goto error;\r
+                       }\r
+\r
+                       // initialize\r
+                       if (PaUtil_InitializeRingBuffer(stream->in.tailBuffer, frameSize, bufferFrames, stream->in.tailBufferMemory) != 0)\r
+                       {\r
+                               LogPaError(result = paInternalError);\r
+                               goto error;\r
+                       }\r
+               }\r
        }\r
     else\r
     {\r
@@ -2723,6 +2883,15 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
             stream->hostProcessOverrideOutput.processor = outputStreamInfo->hostProcessorOutput;\r
             stream->hostProcessOverrideOutput.userData = userData;\r
         }\r
+\r
+               // Only get IAudioCaptureClient output once here instead of getting it at multiple places based on the use\r
+               hr = IAudioClient_GetService(stream->out.clientParent, &pa_IID_IAudioRenderClient, (void **)&stream->renderClientParent);\r
+               if (hr != S_OK)\r
+               {\r
+                       LogHostError(hr);\r
+                       LogPaError(result = paUnanticipatedHostError);\r
+                       goto error;\r
+               }\r
        }\r
     else\r
     {\r
@@ -2836,12 +3005,12 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
        // Set Input latency\r
     stream->streamRepresentation.streamInfo.inputLatency =\r
             ((double)PaUtil_GetBufferProcessorInputLatency(&stream->bufferProcessor) / sampleRate)\r
-                       + ((inputParameters)?stream->in.latency_seconds : 0);\r
+                       + ((inputParameters)?stream->in.latencySeconds : 0);\r
 \r
        // Set Output latency\r
     stream->streamRepresentation.streamInfo.outputLatency =\r
             ((double)PaUtil_GetBufferProcessorOutputLatency(&stream->bufferProcessor) / sampleRate)\r
-                       + ((outputParameters)?stream->out.latency_seconds : 0);\r
+                       + ((outputParameters)?stream->out.latencySeconds : 0);\r
 \r
        // Set SR\r
     stream->streamRepresentation.streamInfo.sampleRate = sampleRate;\r
@@ -2866,24 +3035,29 @@ static PaError CloseStream( PaStream* s )
        // abort active stream\r
        if (IsStreamActive(s))\r
        {\r
-               if ((result = AbortStream(s)) != paNoError)\r
-                       return result;\r
+               result = AbortStream(s);\r
        }\r
 \r
-    SAFE_RELEASE(stream->cclient);\r
-    SAFE_RELEASE(stream->rclient);\r
-    SAFE_RELEASE(stream->out.client);\r
-    SAFE_RELEASE(stream->in.client);\r
+    SAFE_RELEASE(stream->captureClientParent);\r
+    SAFE_RELEASE(stream->renderClientParent);\r
+    SAFE_RELEASE(stream->out.clientParent);\r
+    SAFE_RELEASE(stream->in.clientParent);\r
        SAFE_RELEASE(stream->inVol);\r
        SAFE_RELEASE(stream->outVol);\r
 \r
        CloseHandle(stream->event[S_INPUT]);\r
        CloseHandle(stream->event[S_OUTPUT]);\r
 \r
-       _CleanupStream(stream);\r
+       _StreamCleanup(stream);\r
 \r
-       free(stream->in.monoBuffer);\r
-       free(stream->out.monoBuffer);\r
+       PaWasapi_FreeMemory(stream->in.monoBuffer);\r
+       PaWasapi_FreeMemory(stream->out.monoBuffer);\r
+\r
+       PaUtil_FreeMemory(stream->in.tailBuffer);\r
+       PaUtil_FreeMemory(stream->in.tailBufferMemory);\r
+\r
+       PaUtil_FreeMemory(stream->out.tailBuffer);\r
+       PaUtil_FreeMemory(stream->out.tailBufferMemory);\r
 \r
     PaUtil_TerminateBufferProcessor(&stream->bufferProcessor);\r
     PaUtil_TerminateStreamRepresentation(&stream->streamRepresentation);\r
@@ -2892,11 +3066,163 @@ static PaError CloseStream( PaStream* s )
     return result;\r
 }\r
 \r
+// ------------------------------------------------------------------------------------------\r
+HRESULT UnmarshalSubStreamComPointers(PaWasapiSubStream *substream) \r
+{\r
+       HRESULT hResult = S_OK;\r
+       HRESULT hFirstBadResult = S_OK;\r
+       substream->clientProc = NULL;\r
+\r
+       // IAudioClient\r
+       hResult = CoGetInterfaceAndReleaseStream(substream->clientStream, &pa_IID_IAudioClient, (LPVOID*)&substream->clientProc);\r
+       substream->clientStream = NULL;\r
+       if (hResult != S_OK) \r
+       {\r
+               hFirstBadResult = (hFirstBadResult == S_OK) ? hResult : hFirstBadResult;\r
+       }\r
+\r
+       return hFirstBadResult;\r
+}\r
+\r
+// ------------------------------------------------------------------------------------------\r
+HRESULT UnmarshalStreamComPointers(PaWasapiStream *stream) \r
+{\r
+       HRESULT hResult = S_OK;\r
+       HRESULT hFirstBadResult = S_OK;\r
+       stream->captureClient = NULL;\r
+       stream->renderClient = NULL;\r
+       stream->in.clientProc = NULL;\r
+       stream->out.clientProc = NULL;\r
+\r
+       if (NULL != stream->in.clientParent) \r
+       {\r
+               // SubStream pointers\r
+               hResult = UnmarshalSubStreamComPointers(&stream->in);\r
+               if (hResult != S_OK) \r
+               {\r
+                       hFirstBadResult = (hFirstBadResult == S_OK) ? hResult : hFirstBadResult;\r
+               }\r
+\r
+               // IAudioCaptureClient\r
+               hResult = CoGetInterfaceAndReleaseStream(stream->captureClientStream, &pa_IID_IAudioCaptureClient, (LPVOID*)&stream->captureClient);\r
+               stream->captureClientStream = NULL;\r
+               if (hResult != S_OK) \r
+               {\r
+                       hFirstBadResult = (hFirstBadResult == S_OK) ? hResult : hFirstBadResult;\r
+               }\r
+       }\r
+\r
+       if (NULL != stream->out.clientParent) \r
+       {\r
+               // SubStream pointers\r
+               hResult = UnmarshalSubStreamComPointers(&stream->out);\r
+               if (hResult != S_OK) \r
+               {\r
+                       hFirstBadResult = (hFirstBadResult == S_OK) ? hResult : hFirstBadResult;\r
+               }\r
+\r
+               // IAudioRenderClient\r
+               hResult = CoGetInterfaceAndReleaseStream(stream->renderClientStream, &pa_IID_IAudioRenderClient, (LPVOID*)&stream->renderClient);\r
+               stream->renderClientStream = NULL;\r
+               if (hResult != S_OK) \r
+               {\r
+                       hFirstBadResult = (hFirstBadResult == S_OK) ? hResult : hFirstBadResult;\r
+               }\r
+       }\r
+\r
+       return hFirstBadResult;\r
+}\r
+\r
+// -----------------------------------------------------------------------------------------\r
+void ReleaseUnmarshaledSubComPointers(PaWasapiSubStream *substream) \r
+{\r
+       SAFE_RELEASE(substream->clientProc);\r
+}\r
+\r
+// -----------------------------------------------------------------------------------------\r
+void ReleaseUnmarshaledComPointers(PaWasapiStream *stream) \r
+{\r
+       // Release AudioClient services first\r
+       SAFE_RELEASE(stream->captureClient);\r
+       SAFE_RELEASE(stream->renderClient);\r
+\r
+       // Release AudioClients\r
+       ReleaseUnmarshaledSubComPointers(&stream->in);\r
+       ReleaseUnmarshaledSubComPointers(&stream->out);\r
+}\r
+\r
+// ------------------------------------------------------------------------------------------\r
+HRESULT MarshalSubStreamComPointers(PaWasapiSubStream *substream) \r
+{\r
+       HRESULT hResult;\r
+       substream->clientStream = NULL;\r
+\r
+       // IAudioClient\r
+       hResult = CoMarshalInterThreadInterfaceInStream(&pa_IID_IAudioClient, (LPUNKNOWN)substream->clientParent, &substream->clientStream);\r
+       if (hResult != S_OK)\r
+               goto marshal_sub_error;\r
+\r
+       return hResult;\r
+\r
+       // If marshaling error occurred, make sure to release everything.\r
+marshal_sub_error:\r
+\r
+       UnmarshalSubStreamComPointers(substream);\r
+       ReleaseUnmarshaledSubComPointers(substream);\r
+       return hResult;\r
+}\r
+\r
+// ------------------------------------------------------------------------------------------\r
+HRESULT MarshalStreamComPointers(PaWasapiStream *stream) \r
+{\r
+       HRESULT hResult = S_OK;\r
+       stream->captureClientStream = NULL;\r
+       stream->in.clientStream = NULL;\r
+       stream->renderClientStream = NULL;\r
+       stream->out.clientStream = NULL;\r
+\r
+       if (NULL != stream->in.clientParent) \r
+       {\r
+               // SubStream pointers\r
+               hResult = MarshalSubStreamComPointers(&stream->in);\r
+               if (hResult != S_OK) \r
+                       goto marshal_error;\r
+\r
+               // IAudioCaptureClient\r
+               hResult = CoMarshalInterThreadInterfaceInStream(&pa_IID_IAudioCaptureClient, (LPUNKNOWN)stream->captureClientParent, &stream->captureClientStream);\r
+               if (hResult != S_OK) \r
+                       goto marshal_error;\r
+       }\r
+\r
+       if (NULL != stream->out.clientParent) \r
+       {\r
+               // SubStream pointers\r
+               hResult = MarshalSubStreamComPointers(&stream->out);\r
+               if (hResult != S_OK) \r
+                       goto marshal_error;\r
+\r
+               // IAudioRenderClient\r
+               hResult = CoMarshalInterThreadInterfaceInStream(&pa_IID_IAudioRenderClient, (LPUNKNOWN)stream->renderClientParent, &stream->renderClientStream);\r
+               if (hResult != S_OK) \r
+                       goto marshal_error;\r
+       }\r
+\r
+       return hResult;\r
+\r
+       // If marshaling error occurred, make sure to release everything.\r
+marshal_error:\r
+\r
+       UnmarshalStreamComPointers(stream);\r
+       ReleaseUnmarshaledComPointers(stream);\r
+       return hResult;\r
+}\r
+\r
 // ------------------------------------------------------------------------------------------\r
 static PaError StartStream( PaStream *s )\r
 {\r
        HRESULT hr;\r
     PaWasapiStream *stream = (PaWasapiStream*)s;\r
+       PaError result = paNoError;\r
 \r
        // check if stream is active already\r
        if (IsStreamActive(s))\r
@@ -2905,10 +3231,14 @@ static PaError StartStream( PaStream *s )
     PaUtil_ResetBufferProcessor(&stream->bufferProcessor);\r
 \r
        // Cleanup handles (may be necessary if stream was stopped by itself due to error)\r
-       _CleanupStream(stream);\r
+       _StreamCleanup(stream);\r
 \r
        // Create close event\r
-       stream->hCloseRequest = CreateEvent(NULL, TRUE, FALSE, NULL);\r
+       if ((stream->hCloseRequest = CreateEvent(NULL, TRUE, FALSE, NULL)) == NULL) \r
+       {\r
+               result = paInsufficientMemory;\r
+               goto start_error;\r
+       }\r
 \r
        // Create thread\r
        if (!stream->bBlocking)\r
@@ -2916,78 +3246,118 @@ static PaError StartStream( PaStream *s )
                // Create thread events\r
                stream->hThreadStart = CreateEvent(NULL, TRUE, FALSE, NULL);\r
                stream->hThreadExit  = CreateEvent(NULL, TRUE, FALSE, NULL);\r
+               if ((stream->hThreadStart == NULL) || (stream->hThreadExit == NULL))\r
+               {\r
+                       result = paInsufficientMemory;\r
+                       goto start_error;\r
+               }\r
+\r
+               // Marshal WASAPI interface pointers for safe use in thread created below.\r
+               if ((hr = MarshalStreamComPointers(stream)) != S_OK) \r
+               {\r
+                       PRINT(("Failed marshaling stream COM pointers."));\r
+                       result = paUnanticipatedHostError;\r
+                       goto nonblocking_start_error;\r
+               }\r
 \r
-               if ((stream->in.client && (stream->in.streamFlags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK)) ||\r
-                       (stream->out.client && (stream->out.streamFlags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK)))\r
+               if ((stream->in.clientParent  && (stream->in.streamFlags  & AUDCLNT_STREAMFLAGS_EVENTCALLBACK)) ||\r
+                       (stream->out.clientParent && (stream->out.streamFlags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK)))\r
                {\r
-                       if ((stream->hThread = CREATE_THREAD(ProcThreadEvent)) == NULL)\r
-                          return paUnanticipatedHostError;\r
+                       if ((stream->hThread = CREATE_THREAD(ProcThreadEvent)) == NULL) \r
+                       {\r
+                               PRINT(("Failed creating thread: ProcThreadEvent."));\r
+                               result = paUnanticipatedHostError;\r
+                               goto nonblocking_start_error;\r
+                       }\r
                }\r
                else\r
                {\r
-                       if ((stream->hThread = CREATE_THREAD(ProcThreadPoll)) == NULL)\r
-                          return paUnanticipatedHostError;\r
+                       if ((stream->hThread = CREATE_THREAD(ProcThreadPoll)) == NULL) \r
+                       {\r
+                               PRINT(("Failed creating thread: ProcThreadPoll."));\r
+                               result = paUnanticipatedHostError;\r
+                               goto nonblocking_start_error;\r
+                       }\r
                }\r
 \r
                // Wait for thread to start\r
-               if (WaitForSingleObject(stream->hThreadStart, 60*1000) == WAIT_TIMEOUT)\r
-                       return paUnanticipatedHostError;\r
+               if (WaitForSingleObject(stream->hThreadStart, 60*1000) == WAIT_TIMEOUT) \r
+               {\r
+                       PRINT(("Failed starting thread: timeout."));\r
+                       result = paUnanticipatedHostError;\r
+                       goto nonblocking_start_error;\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
+               if (stream->out.clientParent != NULL) \r
                {\r
-                       if ((hr = IAudioClient_GetService(stream->in.client, &pa_IID_IAudioCaptureClient, (void **)&stream->cclient)) != S_OK)\r
+                       if ((stream->hBlockingOpStreamWR = CreateEvent(NULL, TRUE, TRUE, NULL)) == NULL) \r
                        {\r
-                               LogHostError(hr);\r
-                               return paUnanticipatedHostError;\r
+                               result = paInsufficientMemory;\r
+                               goto start_error;\r
                        }\r
-\r
-                       if ((hr = IAudioClient_Start(stream->in.client)) != S_OK)\r
+               }\r
+               if (stream->in.clientParent != NULL) \r
+               {\r
+                       if ((stream->hBlockingOpStreamRD = CreateEvent(NULL, TRUE, TRUE, NULL)) == NULL) \r
                        {\r
-                               LogHostError(hr);\r
-                               return paUnanticipatedHostError;\r
+                               result = paInsufficientMemory;\r
+                               goto start_error;\r
                        }\r
                }\r
 \r
-\r
-               // Initialize event & start OUTPUT stream\r
-               if (stream->out.client)\r
+               // Initialize event & start INPUT stream\r
+               if (stream->in.clientParent != NULL)\r
                {\r
-                       if ((hr = IAudioClient_GetService(stream->out.client, &pa_IID_IAudioRenderClient, (void **)&stream->rclient)) != S_OK)\r
+                       if ((hr = IAudioClient_Start(stream->in.clientParent)) != S_OK)\r
                        {\r
                                LogHostError(hr);\r
-                               return paUnanticipatedHostError;\r
+                               result = paUnanticipatedHostError;\r
+                               goto start_error;\r
                        }\r
+               }\r
 \r
+               // Initialize event & start OUTPUT stream\r
+               if (stream->out.clientParent != NULL)\r
+               {\r
                        // Start\r
-                       if ((hr = IAudioClient_Start(stream->out.client)) != S_OK)\r
+                       if ((hr = IAudioClient_Start(stream->out.clientParent)) != S_OK)\r
                        {\r
                                LogHostError(hr);\r
-                               return paUnanticipatedHostError;\r
+                               result = paUnanticipatedHostError;\r
+                               goto start_error;\r
                        }\r
                }\r
 \r
-               // Signal: stream running\r
-               stream->running = TRUE;\r
+               // Set parent to working pointers to use shared functions.\r
+               stream->captureClient  = stream->captureClientParent;\r
+               stream->renderClient   = stream->renderClientParent;\r
+               stream->in.clientProc  = stream->in.clientParent;\r
+               stream->out.clientProc = stream->out.clientParent;\r
 \r
-               // Set current time\r
-               stream->out.prevTime  = timeGetTime();\r
-               stream->out.prevSleep = 0;\r
+               // Signal: stream running.\r
+               stream->running = TRUE;\r
        }\r
 \r
-    return paNoError;\r
+    return result;\r
+\r
+nonblocking_start_error:\r
+\r
+       // Set hThreadExit event to prevent blocking during cleanup\r
+       SetEvent(stream->hThreadExit);\r
+       UnmarshalStreamComPointers(stream);\r
+       ReleaseUnmarshaledComPointers(stream);\r
+\r
+start_error:\r
+\r
+       StopStream(s);\r
+       return result;\r
 }\r
 \r
 // ------------------------------------------------------------------------------------------\r
-static void _FinishStream(PaWasapiStream *stream)\r
+void _StreamFinish(PaWasapiStream *stream)\r
 {\r
        // Issue command to thread to stop processing and wait for thread exit\r
        if (!stream->bBlocking)\r
@@ -2998,23 +3368,23 @@ static void _FinishStream(PaWasapiStream *stream)
        // 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
+               if (stream->out.clientParent)\r
                        SignalObjectAndWait(stream->hCloseRequest, stream->hBlockingOpStreamWR, INFINITE, TRUE);\r
-               if (stream->out.client)\r
+               if (stream->out.clientParent)\r
                        SignalObjectAndWait(stream->hCloseRequest, stream->hBlockingOpStreamRD, INFINITE, TRUE);\r
 \r
                // Process stop\r
-               _OnStreamStop(stream);\r
+               _StreamOnStop(stream);\r
        }\r
 \r
        // Cleanup handles\r
-       _CleanupStream(stream);\r
+       _StreamCleanup(stream);\r
 \r
     stream->running = FALSE;\r
 }\r
 \r
 // ------------------------------------------------------------------------------------------\r
-static void _CleanupStream(PaWasapiStream *stream)\r
+void _StreamCleanup(PaWasapiStream *stream)\r
 {\r
        // Close thread handles to allow restart\r
        SAFE_CLOSE(stream->hThread);\r
@@ -3029,7 +3399,7 @@ static void _CleanupStream(PaWasapiStream *stream)
 static PaError StopStream( PaStream *s )\r
 {\r
        // Finish stream\r
-       _FinishStream((PaWasapiStream *)s);\r
+       _StreamFinish((PaWasapiStream *)s);\r
     return paNoError;\r
 }\r
 \r
@@ -3037,7 +3407,7 @@ static PaError StopStream( PaStream *s )
 static PaError AbortStream( PaStream *s )\r
 {\r
        // Finish stream\r
-       _FinishStream((PaWasapiStream *)s);\r
+       _StreamFinish((PaWasapiStream *)s);\r
     return paNoError;\r
 }\r
 \r
@@ -3061,11 +3431,6 @@ static PaTime GetStreamTime( PaStream *s )
     /* suppress unused variable warnings */\r
     (void) stream;\r
 \r
-    /* IMPLEMENT ME, see portaudio.h for required behavior*/\r
-\r
-       //this is lame ds and mme does the same thing, quite useless method imho\r
-       //why dont we fetch the time in the pa callbacks?\r
-       //at least its doing to be clocked to something\r
     return PaUtil_GetTime();\r
 }\r
 \r
@@ -3076,28 +3441,32 @@ static double GetStreamCpuLoad( PaStream* s )
 }\r
 \r
 // ------------------------------------------------------------------------------------------\r
-/* NOT TESTED */\r
-static PaError ReadStream( PaStream* s, void *_buffer, unsigned long _frames )\r
+static PaError ReadStream( PaStream* s, void *_buffer, unsigned long frames )\r
 {\r
     PaWasapiStream *stream = (PaWasapiStream*)s;\r
 \r
        HRESULT hr = S_OK;\r
-       UINT32 frames;\r
        BYTE *user_buffer = (BYTE *)_buffer;\r
        BYTE *wasapi_buffer = NULL;\r
        DWORD flags = 0;\r
-       UINT32 i;\r
+       UINT32 i, available, sleep = 0;\r
+       unsigned long processed;\r
+       ThreadIdleScheduler sched;\r
 \r
        // validate\r
        if (!stream->running)\r
                return paStreamIsStopped;\r
-       if (stream->cclient == NULL)\r
+       if (stream->captureClient == NULL)\r
                return paBadStreamPtr;\r
 \r
        // Notify blocking op has begun\r
        ResetEvent(stream->hBlockingOpStreamRD);\r
 \r
-    // make a local copy of the user buffer pointer(s), this is necessary\r
+       // Use thread scheduling for 500 microseconds (emulated) when wait time for frames is less than\r
+       // 1 milliseconds, emulation helps to normalize CPU consumption and avoids too busy waiting\r
+       ThreadIdleScheduler_Setup(&sched, 1, 250/* microseconds */);\r
+\r
+    // Make a local copy of the user buffer pointer(s), this is necessary\r
        // because PaUtil_CopyOutput() advances these pointers every time it is called\r
     if (!stream->bufferProcessor.userInputIsInterleaved)\r
     {\r
@@ -3109,64 +3478,140 @@ static PaError ReadStream( PaStream* s, void *_buffer, unsigned long _frames )
             ((BYTE **)user_buffer)[i] = ((BYTE **)_buffer)[i];\r
     }\r
 \r
-       while (_frames != 0)\r
+       // Findout if there are tail frames, flush them all before reading hardware\r
+       if ((available = PaUtil_GetRingBufferReadAvailable(stream->in.tailBuffer)) != 0)\r
        {\r
-               UINT32 processed, processed_size;\r
+               UINT32 buf1_size = 0, buf2_size = 0, read, desired;\r
+               void *buf1 = NULL, *buf2 = NULL;\r
+\r
+               // Limit desired to amount of requested frames\r
+               desired = available;\r
+               if (desired > frames)\r
+                       desired = frames;\r
+               \r
+               // Get pointers to read regions\r
+               read = PaUtil_GetRingBufferReadRegions(stream->in.tailBuffer, desired, &buf1, &buf1_size, &buf2, &buf2_size);\r
+\r
+               if (buf1 != NULL)\r
+               {\r
+                       // Register available frames to processor\r
+                       PaUtil_SetInputFrameCount(&stream->bufferProcessor, buf1_size);\r
 \r
-               // Get the available data in the shared buffer.\r
-               if ((hr = IAudioCaptureClient_GetBuffer(stream->cclient, &wasapi_buffer, &frames, &flags, NULL, NULL)) != S_OK)\r
+                       // Register host buffer pointer to processor\r
+                       PaUtil_SetInterleavedInputChannels(&stream->bufferProcessor, 0, buf1, stream->bufferProcessor.inputChannelCount);\r
+\r
+                       // Copy user data to host buffer (with conversion if applicable)\r
+                       processed = PaUtil_CopyInput(&stream->bufferProcessor, (void **)&user_buffer, buf1_size);\r
+                       frames -= processed;\r
+               }\r
+\r
+               if (buf2 != NULL)\r
                {\r
-                       if (hr == AUDCLNT_S_BUFFER_EMPTY)\r
+                       // Register available frames to processor\r
+                       PaUtil_SetInputFrameCount(&stream->bufferProcessor, buf2_size);\r
+\r
+                       // Register host buffer pointer to processor\r
+                       PaUtil_SetInterleavedInputChannels(&stream->bufferProcessor, 0, buf2, stream->bufferProcessor.inputChannelCount);\r
+\r
+                       // Copy user data to host buffer (with conversion if applicable)\r
+                       processed = PaUtil_CopyInput(&stream->bufferProcessor, (void **)&user_buffer, buf2_size);\r
+                       frames -= processed;\r
+               }\r
+\r
+               // Advance\r
+               PaUtil_AdvanceRingBufferReadIndex(stream->in.tailBuffer, read);\r
+       }\r
+\r
+       // Read hardware\r
+       while (frames != 0)\r
+       {\r
+               // Check if blocking call must be interrupted\r
+               if (WaitForSingleObject(stream->hCloseRequest, sleep) != WAIT_TIMEOUT)\r
+                       break;\r
+\r
+               // Get available frames (must be finding out available frames before call to IAudioCaptureClient_GetBuffer\r
+               // othervise audio glitches will occur inExclusive mode as it seems that WASAPI has some scheduling/\r
+               // processing problems when such busy polling with IAudioCaptureClient_GetBuffer occurs)\r
+               if ((hr = _PollGetInputFramesAvailable(stream, &available)) != S_OK)\r
+               {\r
+                       LogHostError(hr);\r
+                       return paUnanticipatedHostError;\r
+               }\r
+\r
+               // Wait for more frames to become available\r
+               if (available == 0)\r
+               {\r
+                       // Exclusive mode may require latency of 1 millisecond, thus we shall sleep\r
+                       // around 500 microseconds (emulated) to collect packets in time\r
+                       if (stream->in.shareMode != AUDCLNT_SHAREMODE_EXCLUSIVE)\r
                        {\r
-                               // Check if blocking call must be interrupted\r
-                               if (WaitForSingleObject(stream->hCloseRequest, 1) != WAIT_TIMEOUT)\r
-                                       break;\r
+                               UINT32 sleep_frames = (frames < stream->in.framesPerHostCallback ? frames : stream->in.framesPerHostCallback);\r
+\r
+                               sleep  = GetFramesSleepTime(sleep_frames, stream->in.wavex.Format.nSamplesPerSec);\r
+                               sleep /= 4; // wait only for 1/4 of the buffer\r
+\r
+                               // WASAPI input provides packets, thus expiring packet will result in bad audio\r
+                               // limit waiting time to 2 seconds (will always work for smallest buffer in Shared)\r
+                               if (sleep > 2)\r
+                                       sleep = 2;\r
+\r
+                               // Avoid busy waiting, schedule next 1 millesecond wait\r
+                               if (sleep == 0)\r
+                                       sleep = ThreadIdleScheduler_NextSleep(&sched);\r
+                       }\r
+                       else\r
+                       {\r
+                               if ((sleep = ThreadIdleScheduler_NextSleep(&sched)) != 0)\r
+                               {\r
+                                       Sleep(sleep);\r
+                                       sleep = 0;\r
+                               }\r
                        }\r
 \r
-                       return LogHostError(hr);\r
-                       goto stream_rd_end;\r
+                       continue;\r
                }\r
 \r
-               // Detect silence\r
-               // if (flags & AUDCLNT_BUFFERFLAGS_SILENT)\r
-               //      data = NULL;\r
+               // Get the available data in the shared buffer.\r
+               if ((hr = IAudioCaptureClient_GetBuffer(stream->captureClient, &wasapi_buffer, &available, &flags, NULL, NULL)) != S_OK)\r
+               {\r
+                       // Buffer size is too small, waiting\r
+                       if (hr != AUDCLNT_S_BUFFER_EMPTY)\r
+                       {\r
+                               LogHostError(hr);\r
+                               goto end;\r
+                       }\r
 \r
-               // Check if frames <= _frames\r
-               if (frames > _frames)\r
-                       frames = _frames;\r
+                       continue;\r
+               }\r
 \r
                // Register available frames to processor\r
-        PaUtil_SetInputFrameCount(&stream->bufferProcessor, frames);\r
+        PaUtil_SetInputFrameCount(&stream->bufferProcessor, available);\r
 \r
                // Register host buffer pointer to processor\r
-        PaUtil_SetInterleavedInputChannels(&stream->bufferProcessor, 0, wasapi_buffer, stream->bufferProcessor.inputChannelCount);\r
+        PaUtil_SetInterleavedInputChannels(&stream->bufferProcessor, 0, wasapi_buffer, stream->bufferProcessor.inputChannelCount);\r
 \r
                // Copy user data to host buffer (with conversion if applicable)\r
                processed = PaUtil_CopyInput(&stream->bufferProcessor, (void **)&user_buffer, frames);\r
+               frames -= processed;\r
 \r
-               // Advance user buffer to consumed portion\r
-               processed_size = processed * stream->in.wavex.Format.nBlockAlign;\r
-               if (stream->bufferProcessor.userInputIsInterleaved)\r
-               {\r
-                       user_buffer += processed_size;\r
-               }\r
-               else\r
+               // Save tail into buffer\r
+               if ((frames == 0) && (available > processed))\r
                {\r
-                       for (i = 0; i < stream->bufferProcessor.inputChannelCount; ++i)\r
-                               ((BYTE **)user_buffer)[i] = ((BYTE **)user_buffer)[i] + processed_size;\r
+                       UINT32 bytes_processed = processed * stream->in.wavex.Format.nBlockAlign;\r
+                       UINT32 frames_to_save  = available - processed;\r
+\r
+                       PaUtil_WriteRingBuffer(stream->in.tailBuffer, wasapi_buffer + bytes_processed, frames_to_save);\r
                }\r
 \r
                // Release host buffer\r
-               if ((hr = IAudioCaptureClient_ReleaseBuffer(stream->cclient, processed)) != S_OK)\r
+               if ((hr = IAudioCaptureClient_ReleaseBuffer(stream->captureClient, available)) != S_OK)\r
                {\r
                        LogHostError(hr);\r
-                       goto stream_rd_end;\r
+                       goto end;\r
                }\r
-\r
-               _frames -= processed;\r
        }\r
 \r
-stream_rd_end:\r
+end:\r
 \r
        // Notify blocking op has ended\r
        SetEvent(stream->hBlockingOpStreamRD);\r
@@ -3175,58 +3620,32 @@ stream_rd_end:
 }\r
 \r
 // ------------------------------------------------------------------------------------------\r
-static PaError WriteStream( PaStream* s, const void *_buffer, unsigned long _frames )\r
+static PaError WriteStream( PaStream* s, const void *_buffer, unsigned long frames )\r
 {\r
     PaWasapiStream *stream = (PaWasapiStream*)s;\r
 \r
-       UINT32 frames;\r
+       //UINT32 frames;\r
        const BYTE *user_buffer = (const BYTE *)_buffer;\r
        BYTE *wasapi_buffer;\r
        HRESULT hr = S_OK;\r
-       UINT32 next_rev_sleep, blocks, block_sleep_ms;\r
-       UINT32 i;\r
+       UINT32 i, available, sleep = 0;\r
+       unsigned long processed;\r
+       ThreadIdleScheduler sched;\r
 \r
        // validate\r
        if (!stream->running)\r
                return paStreamIsStopped;\r
-       if (stream->rclient == NULL)\r
+       if (stream->renderClient == NULL)\r
                return paBadStreamPtr;\r
 \r
        // Notify blocking op has begun\r
        ResetEvent(stream->hBlockingOpStreamWR);\r
 \r
-       // Calculate sleep time for next call\r
-       {\r
-               UINT32 remainder = 0;\r
-               UINT32 sleep_ms = 0;\r
-               DWORD elapsed_ms;\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
-               // Sleep for remainder\r
-               elapsed_ms = timeGetTime() - stream->out.prevTime;\r
-               if (sleep_ms >= elapsed_ms)\r
-                       sleep_ms -= elapsed_ms;\r
+       // Use thread scheduling for 500 microseconds (emulated) when wait time for frames is less than\r
+       // 1 milliseconds, emulation helps to normalize CPU consumption and avoids too busy waiting\r
+       ThreadIdleScheduler_Setup(&sched, 1, 500/* microseconds */);\r
 \r
-               next_rev_sleep = sleep_ms;\r
-       }\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
-    // make a local copy of the user buffer pointer(s), this is necessary\r
+    // Make a local copy of the user buffer pointer(s), this is necessary\r
        // because PaUtil_CopyOutput() advances these pointers every time it is called\r
     if (!stream->bufferProcessor.userOutputIsInterleaved)\r
     {\r
@@ -3238,91 +3657,75 @@ static PaError WriteStream( PaStream* s, const void *_buffer, unsigned long _fra
             ((const BYTE **)user_buffer)[i] = ((const BYTE **)_buffer)[i];\r
     }\r
 \r
-       // Feed engine\r
-       for (i = 0; i < blocks; ++i)\r
+       // Blocking (potentially, untill 'frames' are consumed) loop\r
+       while (frames != 0)\r
        {\r
-               UINT32 available, processed;\r
-\r
-               // Get block frames\r
-               frames = stream->out.framesPerHostCallback;\r
-               if (frames > _frames)\r
-                       frames = _frames;\r
+               // Check if blocking call must be interrupted\r
+               if (WaitForSingleObject(stream->hCloseRequest, sleep) != WAIT_TIMEOUT)\r
+                       break;\r
 \r
-               if (i)\r
-                       Sleep(block_sleep_ms);\r
+               // Get frames available\r
+               if ((hr = _PollGetOutputFramesAvailable(stream, &available)) != S_OK)\r
+               {\r
+                       LogHostError(hr);\r
+                       goto end;\r
+               }\r
 \r
-               while (frames != 0)\r
+               // Wait for more frames to become available\r
+               if (available == 0)\r
                {\r
-                       UINT32 padding = 0;\r
-                       UINT32 processed_size;\r
+                       UINT32 sleep_frames = (frames < stream->out.framesPerHostCallback ? frames : stream->out.framesPerHostCallback);\r
 \r
-                       // Check if blocking call must be interrupted\r
-                       if (WaitForSingleObject(stream->hCloseRequest, 0) != WAIT_TIMEOUT)\r
-                               break;\r
+                       sleep  = GetFramesSleepTime(sleep_frames, stream->out.wavex.Format.nSamplesPerSec);\r
+                       sleep /= 2; // wait only for half of the buffer\r
 \r
-                       // Get Read position\r
-                       hr = IAudioClient_GetCurrentPadding(stream->out.client, &padding);\r
-                       if (hr != S_OK)\r
-                       {\r
-                               LogHostError(hr);\r
-                               goto stream_wr_end;\r
-                       }\r
+                       // Avoid busy waiting, schedule next 1 millesecond wait\r
+                       if (sleep == 0)\r
+                               sleep = ThreadIdleScheduler_NextSleep(&sched);\r
 \r
-                       // Calculate frames available\r
-                       if (frames >= padding)\r
-                               available = frames - padding;\r
-                       else\r
-                               available = frames;\r
+                       continue;\r
+               }\r
 \r
-                       // Get pointer to host buffer\r
-                       if ((hr = IAudioRenderClient_GetBuffer(stream->rclient, available, &wasapi_buffer)) != 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
+               // Keep in 'frmaes' range\r
+               if (available > frames)\r
+                       available = frames;\r
 \r
-                       // Register available frames to processor\r
-            PaUtil_SetOutputFrameCount(&stream->bufferProcessor, available);\r
+               // Get pointer to host buffer\r
+               if ((hr = IAudioRenderClient_GetBuffer(stream->renderClient, available, &wasapi_buffer)) != S_OK)\r
+               {\r
+                       // Buffer size is too big, waiting\r
+                       if (hr == AUDCLNT_E_BUFFER_TOO_LARGE)\r
+                               continue;\r
 \r
-                       // Register host buffer pointer to processor\r
-            PaUtil_SetInterleavedOutputChannels(&stream->bufferProcessor, 0, wasapi_buffer,    stream->bufferProcessor.outputChannelCount);\r
+                       LogHostError(hr);\r
+                       goto end;\r
+               }\r
 \r
-                       // Copy user data to host buffer (with conversion if applicable)\r
-                       processed = PaUtil_CopyOutput(&stream->bufferProcessor, (const void **)&user_buffer, available);\r
+               // Keep waiting again (on Vista it was noticed that WASAPI could SOMETIMES return NULL pointer \r
+               // to buffer without returning AUDCLNT_E_BUFFER_TOO_LARGE instead)\r
+               if (wasapi_buffer == NULL)\r
+                       continue;\r
 \r
-                       // Advance user buffer to consumed portion\r
-                       processed_size = processed * stream->out.wavex.Format.nBlockAlign;\r
-                       if (stream->bufferProcessor.userOutputIsInterleaved)\r
-                       {\r
-                               user_buffer += processed_size;\r
-                       }\r
-                       else\r
-                       {\r
-                               for (i = 0; i < stream->bufferProcessor.outputChannelCount; ++i)\r
-                                       ((const BYTE **)user_buffer)[i] = ((const BYTE **)user_buffer)[i] + processed_size;\r
-                       }\r
+               // Register available frames to processor\r
+        PaUtil_SetOutputFrameCount(&stream->bufferProcessor, available);\r
 \r
-                       // Release host buffer\r
-                       if ((hr = IAudioRenderClient_ReleaseBuffer(stream->rclient, processed, 0)) != S_OK)\r
-                       {\r
-                               LogHostError(hr);\r
-                               goto stream_wr_end;\r
-                       }\r
+               // Register host buffer pointer to processor\r
+        PaUtil_SetInterleavedOutputChannels(&stream->bufferProcessor, 0, wasapi_buffer,        stream->bufferProcessor.outputChannelCount);\r
 \r
-                       // Deduct frames\r
-                       frames -= processed;\r
-               }\r
+               // Copy user data to host buffer (with conversion if applicable), this call will advance\r
+               // pointer 'user_buffer' to consumed portion of data\r
+               processed = PaUtil_CopyOutput(&stream->bufferProcessor, (const void **)&user_buffer, frames);\r
+               frames -= processed;\r
 \r
-               _frames -= frames;\r
+               // Release host buffer\r
+               if ((hr = IAudioRenderClient_ReleaseBuffer(stream->renderClient, available, 0)) != S_OK)\r
+               {\r
+                       LogHostError(hr);\r
+                       goto end;\r
+               }\r
        }\r
 \r
-stream_wr_end:\r
-\r
-       // Set prev time\r
-       stream->out.prevTime = timeGetTime();\r
+end:\r
 \r
        // Notify blocking op has ended\r
        SetEvent(stream->hBlockingOpStreamWR);\r
@@ -3330,58 +3733,61 @@ stream_wr_end:
        return (hr != S_OK ? paUnanticipatedHostError : paNoError);\r
 }\r
 \r
+unsigned long PaUtil_GetOutputFrameCount( PaUtilBufferProcessor* bp )\r
+{\r
+       return bp->hostOutputFrameCount[0];\r
+}\r
+\r
 // ------------------------------------------------------------------------------------------\r
-/* NOT TESTED */\r
 static signed long GetStreamReadAvailable( PaStream* s )\r
 {\r
     PaWasapiStream *stream = (PaWasapiStream*)s;\r
+\r
        HRESULT hr;\r
-       UINT32 pending = 0;\r
+       UINT32  available = 0;\r
 \r
        // validate\r
        if (!stream->running)\r
                return paStreamIsStopped;\r
-       if (stream->cclient == NULL)\r
+       if (stream->captureClient == NULL)\r
                return paBadStreamPtr;\r
 \r
-       hr = IAudioClient_GetCurrentPadding(stream->in.client, &pending);\r
-       if (hr != S_OK)\r
+       // available in hardware buffer\r
+       if ((hr = _PollGetInputFramesAvailable(stream, &available)) != S_OK)\r
        {\r
                LogHostError(hr);\r
                return paUnanticipatedHostError;\r
        }\r
 \r
-    return (long)pending;\r
+       // available in software tail buffer\r
+       available += PaUtil_GetRingBufferReadAvailable(stream->in.tailBuffer);\r
+\r
+    return available;\r
 }\r
 \r
 // ------------------------------------------------------------------------------------------\r
 static signed long GetStreamWriteAvailable( PaStream* s )\r
 {\r
     PaWasapiStream *stream = (PaWasapiStream*)s;\r
-\r
-       UINT32 frames = stream->out.framesPerHostCallback;\r
        HRESULT hr;\r
-       UINT32 padding = 0;\r
+       UINT32  available = 0;\r
 \r
        // validate\r
        if (!stream->running)\r
                return paStreamIsStopped;\r
-       if (stream->rclient == NULL)\r
+       if (stream->renderClient == NULL)\r
                return paBadStreamPtr;\r
 \r
-       hr = IAudioClient_GetCurrentPadding(stream->out.client, &padding);\r
-       if (hr != S_OK)\r
+       if ((hr = _PollGetOutputFramesAvailable(stream, &available)) != S_OK)\r
        {\r
                LogHostError(hr);\r
                return paUnanticipatedHostError;\r
        }\r
 \r
-       // Calculate\r
-       frames -= padding;\r
-\r
-    return frames;\r
+       return (signed long)available;\r
 }\r
 \r
+\r
 // ------------------------------------------------------------------------------------------\r
 static void WaspiHostProcessingLoop( void *inputBuffer,  long inputFrames,\r
                                      void *outputBuffer, long outputFrames,\r
@@ -3404,24 +3810,24 @@ static void WaspiHostProcessingLoop( void *inputBuffer,  long inputFrames,
     */\r
        timeInfo.currentTime = PaUtil_GetTime();\r
        // Query input latency\r
-       if (stream->in.client != NULL)\r
+       if (stream->in.clientProc != NULL)\r
        {\r
                PaTime pending_time;\r
-               if ((hr = IAudioClient_GetCurrentPadding(stream->in.client, &pending)) == S_OK)\r
+               if ((hr = IAudioClient_GetCurrentPadding(stream->in.clientProc, &pending)) == S_OK)\r
                        pending_time = (PaTime)pending / (PaTime)stream->in.wavex.Format.nSamplesPerSec;\r
                else\r
-                       pending_time = (PaTime)stream->in.latency_seconds;\r
+                       pending_time = (PaTime)stream->in.latencySeconds;\r
 \r
                timeInfo.inputBufferAdcTime = timeInfo.currentTime + pending_time;\r
        }\r
        // Query output current latency\r
-       if (stream->out.client != NULL)\r
+       if (stream->out.clientProc != NULL)\r
        {\r
                PaTime pending_time;\r
-               if ((hr = IAudioClient_GetCurrentPadding(stream->out.client, &pending)) == S_OK)\r
+               if ((hr = IAudioClient_GetCurrentPadding(stream->out.clientProc, &pending)) == S_OK)\r
                        pending_time = (PaTime)pending / (PaTime)stream->out.wavex.Format.nSamplesPerSec;\r
                else\r
-                       pending_time = (PaTime)stream->out.latency_seconds;\r
+                       pending_time = (PaTime)stream->out.latencySeconds;\r
 \r
                timeInfo.outputBufferDacTime = timeInfo.currentTime + pending_time;\r
        }\r
@@ -3811,13 +4217,48 @@ error:
 }\r
 \r
 // ------------------------------------------------------------------------------------------\r
-static HRESULT ProcessOutputBuffer(PaWasapiStream *stream, PaWasapiHostProcessor *processor, UINT32 frames)\r
+HRESULT _PollGetOutputFramesAvailable(PaWasapiStream *stream, UINT32 *available)\r
+{\r
+       HRESULT hr;\r
+       UINT32 frames  = stream->out.framesPerHostCallback,\r
+                  padding = 0;\r
+\r
+       (*available) = 0;\r
+\r
+       // get read position\r
+       if ((hr = IAudioClient_GetCurrentPadding(stream->out.clientProc, &padding)) != S_OK)\r
+               return LogHostError(hr);\r
+\r
+       // get available\r
+       frames -= padding;\r
+\r
+       // set\r
+       (*available) = frames;\r
+       return hr;\r
+}\r
+\r
+// ------------------------------------------------------------------------------------------\r
+HRESULT _PollGetInputFramesAvailable(PaWasapiStream *stream, UINT32 *available)\r
+{\r
+       HRESULT hr;\r
+\r
+       (*available) = 0;\r
+\r
+       // GetCurrentPadding() has opposite meaning to Output stream \r
+       if ((hr = IAudioClient_GetCurrentPadding(stream->in.clientProc, available)) != S_OK)\r
+               return LogHostError(hr);\r
+\r
+       return hr;\r
+}\r
+\r
+// ------------------------------------------------------------------------------------------\r
+HRESULT ProcessOutputBuffer(PaWasapiStream *stream, PaWasapiHostProcessor *processor, UINT32 frames)\r
 {\r
        HRESULT hr;\r
        BYTE *data = NULL;\r
 \r
        // Get buffer\r
-       if ((hr = IAudioRenderClient_GetBuffer(stream->rclient, frames, &data)) != S_OK)\r
+       if ((hr = IAudioRenderClient_GetBuffer(stream->renderClient, frames, &data)) != S_OK)\r
        {\r
                if (stream->out.shareMode == AUDCLNT_SHAREMODE_SHARED)\r
                {\r
@@ -3828,7 +4269,7 @@ static HRESULT ProcessOutputBuffer(PaWasapiStream *stream, PaWasapiHostProcessor
 #if 0\r
                        // Get Read position\r
                        UINT32 padding = 0;\r
-                       hr = stream->out.client->GetCurrentPadding(&padding);\r
+                       hr = IAudioClient_GetCurrentPadding(stream->out.clientProc, &padding);\r
                        if (hr != S_OK)\r
                                return LogHostError(hr);\r
 \r
@@ -3837,7 +4278,7 @@ static HRESULT ProcessOutputBuffer(PaWasapiStream *stream, PaWasapiHostProcessor
                        if (frames == 0)\r
                                return S_OK;\r
 \r
-                       if ((hr = stream->rclient->GetBuffer(frames, &data)) != S_OK)\r
+                       if ((hr = IAudioRenderClient_GetBuffer(stream->renderClient, frames, &data)) != S_OK)\r
                                return LogHostError(hr);\r
 #else\r
                        if (hr == AUDCLNT_E_BUFFER_TOO_LARGE)\r
@@ -3851,10 +4292,10 @@ static HRESULT ProcessOutputBuffer(PaWasapiStream *stream, PaWasapiHostProcessor
        // Process data\r
        if (stream->out.monoMixer != NULL)\r
        {\r
-               // expand buffer (one way only for better performancedue to no calls to realloc)\r
+               // expand buffer\r
                UINT32 mono_frames_size = frames * (stream->out.wavex.Format.wBitsPerSample / 8);\r
                if (mono_frames_size > stream->out.monoBufferSize)\r
-                       stream->out.monoBuffer = realloc(stream->out.monoBuffer, (stream->out.monoBufferSize = mono_frames_size));\r
+                       stream->out.monoBuffer = PaWasapi_ReallocateMemory(stream->out.monoBuffer, (stream->out.monoBufferSize = mono_frames_size));\r
 \r
                // process\r
                processor[S_OUTPUT].processor(NULL, 0, (BYTE *)stream->out.monoBuffer, frames, processor[S_OUTPUT].userData);\r
@@ -3868,14 +4309,14 @@ static HRESULT ProcessOutputBuffer(PaWasapiStream *stream, PaWasapiHostProcessor
        }\r
 \r
        // Release buffer\r
-       if ((hr = IAudioRenderClient_ReleaseBuffer(stream->rclient, frames, 0)) != S_OK)\r
+       if ((hr = IAudioRenderClient_ReleaseBuffer(stream->renderClient, frames, 0)) != S_OK)\r
                LogHostError(hr);\r
 \r
        return hr;\r
 }\r
 \r
 // ------------------------------------------------------------------------------------------\r
-static HRESULT ProcessInputBuffer(PaWasapiStream *stream, PaWasapiHostProcessor *processor)\r
+HRESULT ProcessInputBuffer(PaWasapiStream *stream, PaWasapiHostProcessor *processor)\r
 {\r
        HRESULT hr = S_OK;\r
        UINT32 frames;\r
@@ -3888,13 +4329,22 @@ static HRESULT ProcessInputBuffer(PaWasapiStream *stream, PaWasapiHostProcessor
                if (WaitForSingleObject(stream->hCloseRequest, 0) != WAIT_TIMEOUT)\r
                        break;\r
 \r
+               // Findout if any frames available\r
+               frames = 0;\r
+               if ((hr = _PollGetInputFramesAvailable(stream, &frames)) != S_OK)\r
+                       return hr;\r
+\r
+               // Empty/consumed buffer\r
+               if (frames == 0)\r
+                       break;\r
+\r
                // Get the available data in the shared buffer.\r
-               if ((hr = IAudioCaptureClient_GetBuffer(stream->cclient, &data, &frames, &flags, NULL, NULL)) != S_OK)\r
+               if ((hr = IAudioCaptureClient_GetBuffer(stream->captureClient, &data, &frames, &flags, NULL, NULL)) != S_OK)\r
                {\r
                        if (hr == AUDCLNT_S_BUFFER_EMPTY)\r
                        {\r
                                hr = S_OK;\r
-                               break; // capture buffer exhausted\r
+                               break; // Empty/consumed buffer\r
                        }\r
 \r
                        return LogHostError(hr);\r
@@ -3908,10 +4358,10 @@ static HRESULT ProcessInputBuffer(PaWasapiStream *stream, PaWasapiHostProcessor
                // Process data\r
                if (stream->in.monoMixer != NULL)\r
                {\r
-                       // expand buffer (one way only for better performancedue to no calls to realloc)\r
+                       // expand buffer\r
                        UINT32 mono_frames_size = frames * (stream->in.wavex.Format.wBitsPerSample / 8);\r
                        if (mono_frames_size > stream->in.monoBufferSize)\r
-                               stream->in.monoBuffer = realloc(stream->in.monoBuffer, (stream->in.monoBufferSize = mono_frames_size));\r
+                               stream->in.monoBuffer = PaWasapi_ReallocateMemory(stream->in.monoBuffer, (stream->in.monoBufferSize = mono_frames_size));\r
 \r
                        // mix 1 to 2 channels\r
                        stream->in.monoMixer(stream->in.monoBuffer, data, frames);\r
@@ -3925,25 +4375,33 @@ static HRESULT ProcessInputBuffer(PaWasapiStream *stream, PaWasapiHostProcessor
                }\r
 \r
                // Release buffer\r
-               if ((hr = IAudioCaptureClient_ReleaseBuffer(stream->cclient, frames)) != S_OK)\r
+               if ((hr = IAudioCaptureClient_ReleaseBuffer(stream->captureClient, frames)) != S_OK)\r
                        return LogHostError(hr);\r
 \r
-               break;\r
+               //break;\r
        }\r
 \r
        return hr;\r
 }\r
 \r
 // ------------------------------------------------------------------------------------------\r
-void _OnStreamStop(PaWasapiStream *stream)\r
+void _StreamOnStop(PaWasapiStream *stream)\r
 {\r
-       // Stop INPUT client\r
-       if (stream->in.client != NULL)\r
-               IAudioClient_Stop(stream->in.client);\r
-\r
-       // Stop OUTPUT client\r
-       if (stream->out.client != NULL)\r
-               IAudioClient_Stop(stream->out.client);\r
+       // Stop INPUT/OUTPUT clients\r
+       if (!stream->bBlocking) \r
+       {\r
+               if (stream->in.clientProc != NULL)\r
+                       IAudioClient_Stop(stream->in.clientProc);\r
+               if (stream->out.clientProc != NULL)\r
+                       IAudioClient_Stop(stream->out.clientProc);\r
+       } \r
+       else \r
+       {\r
+               if (stream->in.clientParent != NULL)\r
+                       IAudioClient_Stop(stream->in.clientParent);\r
+               if (stream->out.clientParent != NULL)\r
+                       IAudioClient_Stop(stream->out.clientParent);\r
+       }\r
 \r
        // Restore thread priority\r
        if (stream->hAvTask != NULL)\r
@@ -3952,10 +4410,6 @@ void _OnStreamStop(PaWasapiStream *stream)
                stream->hAvTask = NULL;\r
        }\r
 \r
-       // Release Render/Capture clients (if Exclusive mode was used it will release devices to other applications)\r
-    SAFE_RELEASE(stream->cclient);\r
-    SAFE_RELEASE(stream->rclient);\r
-\r
     // Notify\r
     if (stream->streamRepresentation.streamFinishedCallback != NULL)\r
         stream->streamRepresentation.streamFinishedCallback(stream->streamRepresentation.userData);\r
@@ -3970,10 +4424,34 @@ PA_THREAD_FUNC ProcThreadEvent(void *param)
     PaWasapiStream *stream = (PaWasapiStream *)param;\r
        PaWasapiHostProcessor defaultProcessor;\r
        BOOL set_event[S_COUNT] = { FALSE, FALSE };\r
+       BOOL bWaitAllEvents = FALSE;\r
+       BOOL bThreadComInitialized = FALSE;\r
+\r
+       /*\r
+       If COM is already initialized CoInitialize will either return\r
+       FALSE, or RPC_E_CHANGED_MODE if it was initialized in a different\r
+       threading mode. In either case we shouldn't consider it an error\r
+       but we need to be careful to not call CoUninitialize() if \r
+       RPC_E_CHANGED_MODE was returned.\r
+       */\r
+       hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);\r
+       if (FAILED(hr) && (hr != RPC_E_CHANGED_MODE))\r
+       {\r
+               PRINT(("WASAPI: failed ProcThreadEvent CoInitialize"));\r
+               return paUnanticipatedHostError;\r
+       }\r
+       if (hr != RPC_E_CHANGED_MODE)\r
+               bThreadComInitialized = TRUE;\r
+\r
+       // Unmarshal stream pointers for safe COM operation\r
+       hr = UnmarshalStreamComPointers(stream);\r
+       if (hr != S_OK) {\r
+               PRINT(("Error unmarshaling stream COM pointers. HRESULT: %i\n", hr));\r
+               goto thread_end;\r
+       }\r
 \r
        // Waiting on all events in case of Full-Duplex/Exclusive mode.\r
-       BOOL bWaitAllEvents = FALSE;\r
-       if ((stream->in.client != NULL) && (stream->out.client != NULL))\r
+       if ((stream->in.clientProc != NULL) && (stream->out.clientProc != NULL))\r
        {\r
                bWaitAllEvents = (stream->in.shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE) &&\r
                        (stream->out.shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE);\r
@@ -4006,22 +4484,12 @@ PA_THREAD_FUNC ProcThreadEvent(void *param)
        }\r
 \r
        // Initialize event & start INPUT stream\r
-       if (stream->in.client)\r
+       if (stream->in.clientProc)\r
        {\r
                // Create & set handle\r
                if (set_event[S_INPUT])\r
                {\r
-                       if ((hr = IAudioClient_SetEventHandle(stream->in.client, stream->event[S_INPUT])) != S_OK)\r
-                       {\r
-                               LogHostError(hr);\r
-                               goto thread_error;\r
-                       }\r
-               }\r
-\r
-               // Create Capture client\r
-               if (stream->cclient == NULL)\r
-               {\r
-                       if ((hr = IAudioClient_GetService(stream->in.client, &pa_IID_IAudioCaptureClient, (void **)&stream->cclient)) != S_OK)\r
+                       if ((hr = IAudioClient_SetEventHandle(stream->in.clientProc, stream->event[S_INPUT])) != S_OK)\r
                        {\r
                                LogHostError(hr);\r
                                goto thread_error;\r
@@ -4029,7 +4497,7 @@ PA_THREAD_FUNC ProcThreadEvent(void *param)
                }\r
 \r
                // Start\r
-               if ((hr = IAudioClient_Start(stream->in.client)) != S_OK)\r
+               if ((hr = IAudioClient_Start(stream->in.clientProc)) != S_OK)\r
                {\r
                        LogHostError(hr);\r
                        goto thread_error;\r
@@ -4037,22 +4505,12 @@ PA_THREAD_FUNC ProcThreadEvent(void *param)
        }\r
 \r
        // Initialize event & start OUTPUT stream\r
-       if (stream->out.client)\r
+       if (stream->out.clientProc)\r
        {\r
                // Create & set handle\r
                if (set_event[S_OUTPUT])\r
                {\r
-                       if ((hr = IAudioClient_SetEventHandle(stream->out.client, stream->event[S_OUTPUT])) != S_OK)\r
-                       {\r
-                               LogHostError(hr);\r
-                               goto thread_error;\r
-                       }\r
-               }\r
-\r
-               // Create Render client\r
-               if (stream->rclient == NULL)\r
-               {\r
-                       if ((hr = IAudioClient_GetService(stream->out.client, &pa_IID_IAudioRenderClient, (void **)&stream->rclient)) != S_OK)\r
+                       if ((hr = IAudioClient_SetEventHandle(stream->out.clientProc, stream->event[S_OUTPUT])) != S_OK)\r
                        {\r
                                LogHostError(hr);\r
                                goto thread_error;\r
@@ -4067,7 +4525,7 @@ PA_THREAD_FUNC ProcThreadEvent(void *param)
                }\r
 \r
                // Start\r
-               if ((hr = IAudioClient_Start(stream->out.client)) != S_OK)\r
+               if ((hr = IAudioClient_Start(stream->out.clientProc)) != S_OK)\r
                {\r
                        LogHostError(hr);\r
                        goto thread_error;\r
@@ -4103,7 +4561,7 @@ PA_THREAD_FUNC ProcThreadEvent(void *param)
                // Input stream\r
                case WAIT_OBJECT_0 + S_INPUT: {\r
 \r
-            if (stream->cclient == NULL)\r
+            if (stream->captureClient == NULL)\r
                 break;\r
 \r
                        if ((hr = ProcessInputBuffer(stream, processor)) != S_OK)\r
@@ -4117,7 +4575,7 @@ PA_THREAD_FUNC ProcThreadEvent(void *param)
                // Output stream\r
                case WAIT_OBJECT_0 + S_OUTPUT: {\r
 \r
-            if (stream->rclient == NULL)\r
+            if (stream->renderClient == NULL)\r
                 break;\r
 \r
                        if ((hr = ProcessOutputBuffer(stream, processor, stream->out.framesPerBuffer)) != S_OK)\r
@@ -4133,14 +4591,21 @@ PA_THREAD_FUNC ProcThreadEvent(void *param)
 thread_end:\r
 \r
        // Process stop\r
-       _OnStreamStop(stream);\r
+       _StreamOnStop(stream);\r
 \r
-       // Notify: thread exited\r
-       SetEvent(stream->hThreadExit);\r
+       // Release unmarshaled COM pointers\r
+       ReleaseUnmarshaledComPointers(stream);\r
+\r
+       // Cleanup COM for this thread\r
+       if (bThreadComInitialized == TRUE)\r
+               CoUninitialize();\r
 \r
        // Notify: not running\r
        stream->running = FALSE;\r
 \r
+       // Notify: thread exited\r
+       SetEvent(stream->hThreadExit);\r
+\r
        return 0;\r
 \r
 thread_error:\r
@@ -4152,67 +4617,6 @@ thread_error:
        goto thread_end;\r
 }\r
 \r
-// ------------------------------------------------------------------------------------------\r
-static HRESULT PollGetOutputFramesAvailable(PaWasapiStream *stream, UINT32 *available)\r
-{\r
-       HRESULT hr;\r
-       UINT32 frames  = stream->out.framesPerHostCallback,\r
-                  padding = 0;\r
-\r
-       (*available) = 0;\r
-\r
-       // get read position\r
-       if ((hr = IAudioClient_GetCurrentPadding(stream->out.client, &padding)) != S_OK)\r
-               return LogHostError(hr);\r
-\r
-       // get available\r
-       frames -= padding;\r
-\r
-       // set\r
-       (*available) = frames;\r
-       return hr;\r
-}\r
-\r
-// ------------------------------------------------------------------------------------------\r
-/*! \class ThreadSleepScheduler\r
-           Allows to emulate thread sleep of less than 1 millisecond under Windows. Scheduler\r
-                  calculates number of times the thread must run untill next sleep of 1 millisecond.\r
-                  It does not make thread sleeping for real number of microseconds but rather controls\r
-                  how many of imaginary microseconds the thread task can allow thread to sleep.\r
-*/\r
-typedef struct ThreadIdleScheduler\r
-{\r
-       UINT32 m_idle_microseconds; //!< number of microseconds to sleep\r
-       UINT32 m_next_sleep;        //!< next sleep round\r
-       UINT32 m_i;                                     //!< current round iterator position\r
-       UINT32 m_resolution;            //!< resolution in number of milliseconds\r
-}\r
-ThreadIdleScheduler;\r
-//! Setup scheduler.\r
-static void ThreadIdleScheduler_Setup(ThreadIdleScheduler *sched, UINT32 resolution, UINT32 microseconds)\r
-{\r
-       assert(microseconds != 0);\r
-       assert(resolution != 0);\r
-       assert((resolution * 1000) >= microseconds);\r
-\r
-       memset(sched, 0, sizeof(*sched));\r
-\r
-       sched->m_idle_microseconds = microseconds;\r
-       sched->m_resolution         = resolution;\r
-       sched->m_next_sleep         = (resolution * 1000) / microseconds;\r
-}\r
-//! Iterate and check if can sleep.\r
-static UINT32 ThreadIdleScheduler_NextSleep(ThreadIdleScheduler *sched)\r
-{\r
-       // advance and check if thread can sleep\r
-       if (++ sched->m_i == sched->m_next_sleep)\r
-       {\r
-               sched->m_i = 0;\r
-               return sched->m_resolution;\r
-       }\r
-       return 0;\r
-}\r
-\r
 // ------------------------------------------------------------------------------------------\r
 PA_THREAD_FUNC ProcThreadPoll(void *param)\r
 {\r
@@ -4225,13 +4629,49 @@ PA_THREAD_FUNC ProcThreadPoll(void *param)
 \r
        // Calculate the actual duration of the allocated buffer.\r
        DWORD sleep_ms     = 0;\r
-       DWORD sleep_ms_in  = GetFramesSleepTime(stream->in.framesPerBuffer, stream->in.wavex.Format.nSamplesPerSec);\r
-       DWORD sleep_ms_out = GetFramesSleepTime(stream->out.framesPerBuffer, stream->out.wavex.Format.nSamplesPerSec);\r
+       DWORD sleep_ms_in;\r
+       DWORD sleep_ms_out;\r
+\r
+       BOOL bThreadComInitialized = FALSE;\r
+\r
+       /*\r
+       If COM is already initialized CoInitialize will either return\r
+       FALSE, or RPC_E_CHANGED_MODE if it was initialized in a different\r
+       threading mode. In either case we shouldn't consider it an error\r
+       but we need to be careful to not call CoUninitialize() if \r
+       RPC_E_CHANGED_MODE was returned.\r
+       */\r
+       hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);\r
+       if (FAILED(hr) && (hr != RPC_E_CHANGED_MODE))\r
+       {\r
+               PRINT(("WASAPI: failed ProcThreadPoll CoInitialize"));\r
+               return paUnanticipatedHostError;\r
+       }\r
+       if (hr != RPC_E_CHANGED_MODE)\r
+               bThreadComInitialized = TRUE;\r
 \r
-       // Adjust polling time\r
+       // Unmarshal stream pointers for safe COM operation\r
+       hr = UnmarshalStreamComPointers(stream);\r
+       if (hr != S_OK) \r
+       {\r
+               PRINT(("Error unmarshaling stream COM pointers. HRESULT: %i\n", hr));\r
+               return 0;\r
+       }\r
+\r
+       // Calculate timeout for next polling attempt.\r
+       sleep_ms_in  = GetFramesSleepTime(stream->in.framesPerHostCallback/WASAPI_PACKETS_PER_INPUT_BUFFER, stream->in.wavex.Format.nSamplesPerSec);\r
+       sleep_ms_out = GetFramesSleepTime(stream->out.framesPerBuffer, stream->out.wavex.Format.nSamplesPerSec);\r
+\r
+       // WASAPI Input packets tend to expire very easily, let's limit sleep time to 2 milliseconds\r
+       // for all cases. Please propose better solution if any.\r
+       if (sleep_ms_in > 2)\r
+               sleep_ms_in = 2;\r
+\r
+       // Adjust polling time for non-paUtilFixedHostBufferSize. Input stream is not adjustable as it is being\r
+       // polled according its packet length.\r
        if (stream->bufferMode != paUtilFixedHostBufferSize)\r
        {\r
-               sleep_ms_in  = GetFramesSleepTime(stream->bufferProcessor.framesPerUserBuffer, stream->in.wavex.Format.nSamplesPerSec);\r
+               //sleep_ms_in = GetFramesSleepTime(stream->bufferProcessor.framesPerUserBuffer, stream->in.wavex.Format.nSamplesPerSec);\r
                sleep_ms_out = GetFramesSleepTime(stream->bufferProcessor.framesPerUserBuffer, stream->out.wavex.Format.nSamplesPerSec);\r
        }\r
 \r
@@ -4245,7 +4685,7 @@ PA_THREAD_FUNC ProcThreadPoll(void *param)
        // Make sure not 0, othervise use ThreadIdleScheduler\r
        if (sleep_ms == 0)\r
        {\r
-               sleep_ms_in  = GetFramesSleepTimeMicroseconds(stream->bufferProcessor.framesPerUserBuffer, stream->in.wavex.Format.nSamplesPerSec);\r
+               sleep_ms_in  = GetFramesSleepTimeMicroseconds(stream->in.framesPerHostCallback/WASAPI_PACKETS_PER_INPUT_BUFFER, stream->in.wavex.Format.nSamplesPerSec);\r
                sleep_ms_out = GetFramesSleepTimeMicroseconds(stream->bufferProcessor.framesPerUserBuffer, stream->out.wavex.Format.nSamplesPerSec);\r
 \r
                // Choose smallest\r
@@ -4271,43 +4711,24 @@ PA_THREAD_FUNC ProcThreadPoll(void *param)
        PaWasapi_ThreadPriorityBoost((void **)&stream->hAvTask, stream->nThreadPriority);\r
 \r
        // Initialize event & start INPUT stream\r
-       if (stream->in.client)\r
+       if (stream->in.clientProc)\r
        {\r
-               if (stream->cclient == NULL)\r
-               {\r
-                       if ((hr = IAudioClient_GetService(stream->in.client, &pa_IID_IAudioCaptureClient, (void **)&stream->cclient)) != S_OK)\r
-                       {\r
-                               LogHostError(hr);\r
-                               goto thread_error;\r
-                       }\r
-               }\r
-\r
-               if ((hr = IAudioClient_Start(stream->in.client)) != S_OK)\r
+               if ((hr = IAudioClient_Start(stream->in.clientProc)) != S_OK)\r
                {\r
                        LogHostError(hr);\r
                        goto thread_error;\r
                }\r
        }\r
 \r
-\r
        // Initialize event & start OUTPUT stream\r
-       if (stream->out.client)\r
+       if (stream->out.clientProc)\r
        {\r
-               if (stream->rclient == NULL)\r
-               {\r
-                       if ((hr = IAudioClient_GetService(stream->out.client, &pa_IID_IAudioRenderClient, (void **)&stream->rclient)) != S_OK)\r
-                       {\r
-                               LogHostError(hr);\r
-                               goto thread_error;\r
-                       }\r
-               }\r
-\r
                // Preload buffer (obligatory, othervise ->Start() will fail), avoid processing\r
                // when in full-duplex mode as it requires input processing as well\r
                if (!PA_WASAPI__IS_FULLDUPLEX(stream))\r
                {\r
                        UINT32 frames = 0;\r
-                       if ((hr = PollGetOutputFramesAvailable(stream, &frames)) == S_OK)\r
+                       if ((hr = _PollGetOutputFramesAvailable(stream, &frames)) == S_OK)\r
             {\r
                                if (stream->bufferMode == paUtilFixedHostBufferSize)\r
                                {\r
@@ -4330,7 +4751,7 @@ PA_THREAD_FUNC ProcThreadPoll(void *param)
                }\r
 \r
                // Start\r
-               if ((hr = IAudioClient_Start(stream->out.client)) != S_OK)\r
+               if ((hr = IAudioClient_Start(stream->out.clientProc)) != S_OK)\r
                {\r
                        LogHostError(hr);\r
                        goto thread_error;\r
@@ -4363,7 +4784,7 @@ PA_THREAD_FUNC ProcThreadPoll(void *param)
                                // Input stream\r
                                case S_INPUT: {\r
 \r
-                                       if (stream->cclient == NULL)\r
+                                       if (stream->captureClient == NULL)\r
                                                break;\r
 \r
                                        if ((hr = ProcessInputBuffer(stream, processor)) != S_OK)\r
@@ -4378,11 +4799,11 @@ PA_THREAD_FUNC ProcThreadPoll(void *param)
                                case S_OUTPUT: {\r
 \r
                                        UINT32 frames;\r
-                                       if (stream->rclient == NULL)\r
+                                       if (stream->renderClient == NULL)\r
                                                break;\r
 \r
                                        // get available frames\r
-                                       if ((hr = PollGetOutputFramesAvailable(stream, &frames)) != S_OK)\r
+                                       if ((hr = _PollGetOutputFramesAvailable(stream, &frames)) != S_OK)\r
                                        {\r
                                                LogHostError(hr);\r
                                                goto thread_error;\r
@@ -4429,7 +4850,7 @@ PA_THREAD_FUNC ProcThreadPoll(void *param)
                        UINT32 o_frames = 0;\r
 \r
                        // get host input buffer\r
-                       if ((hr = IAudioCaptureClient_GetBuffer(stream->cclient, &i_data, &i_frames, &i_flags, NULL, NULL)) != S_OK)\r
+                       if ((hr = IAudioCaptureClient_GetBuffer(stream->captureClient, &i_data, &i_frames, &i_flags, NULL, NULL)) != S_OK)\r
                        {\r
                                if (hr == AUDCLNT_S_BUFFER_EMPTY)\r
                                        continue; // no data in capture buffer\r
@@ -4439,7 +4860,7 @@ PA_THREAD_FUNC ProcThreadPoll(void *param)
                        }\r
 \r
                        // get available frames\r
-                       if ((hr = PollGetOutputFramesAvailable(stream, &o_frames)) != S_OK)\r
+                       if ((hr = _PollGetOutputFramesAvailable(stream, &o_frames)) != S_OK)\r
                        {\r
                                LogHostError(hr);\r
                                break;\r
@@ -4452,7 +4873,7 @@ PA_THREAD_FUNC ProcThreadPoll(void *param)
                                UINT32 o_processed = i_frames;\r
 \r
                                // get host output buffer\r
-                               if ((hr = IAudioRenderClient_GetBuffer(stream->rclient, o_processed, &o_data)) == S_OK)\r
+                               if ((hr = IAudioRenderClient_GetBuffer(stream->procRCClient, o_processed, &o_data)) == S_OK)\r
                                {\r
                                        // processed amount of i_frames\r
                                        i_processed = i_frames;\r
@@ -4462,10 +4883,10 @@ PA_THREAD_FUNC ProcThreadPoll(void *param)
                                        if (stream->out.monoMixer)\r
                                        {\r
                                                UINT32 mono_frames_size = o_processed * (stream->out.wavex.Format.wBitsPerSample / 8);\r
-                                               // expand buffer (one way only for better performance due to no calls to realloc)\r
+                                               // expand buffer\r
                                                if (mono_frames_size > stream->out.monoBufferSize)\r
                                                {\r
-                                                       stream->out.monoBuffer = realloc(stream->out.monoBuffer, (stream->out.monoBufferSize = mono_frames_size));\r
+                                                       stream->out.monoBuffer = PaWasapi_ReallocateMemory(stream->out.monoBuffer, (stream->out.monoBufferSize = mono_frames_size));\r
                                                        if (stream->out.monoBuffer == NULL)\r
                                                        {\r
                                                                LogPaError(paInsufficientMemory);\r
@@ -4481,10 +4902,10 @@ PA_THREAD_FUNC ProcThreadPoll(void *param)
                                        if (stream->in.monoMixer)\r
                                        {\r
                                                UINT32 mono_frames_size = i_processed * (stream->in.wavex.Format.wBitsPerSample / 8);\r
-                                               // expand buffer (one way only for better performance due to no calls to realloc)\r
+                                               // expand buffer\r
                                                if (mono_frames_size > stream->in.monoBufferSize)\r
                                                {\r
-                                                       stream->in.monoBuffer = realloc(stream->in.monoBuffer, (stream->in.monoBufferSize = mono_frames_size));\r
+                                                       stream->in.monoBuffer = PaWasapi_ReallocateMemory(stream->in.monoBuffer, (stream->in.monoBufferSize = mono_frames_size));\r
                                                        if (stream->in.monoBuffer == NULL)\r
                                                        {\r
                                                                LogPaError(paInsufficientMemory);\r
@@ -4507,7 +4928,7 @@ PA_THREAD_FUNC ProcThreadPoll(void *param)
                                                stream->out.monoMixer(o_data_host, stream->out.monoBuffer, o_processed);\r
 \r
                                        // release host output buffer\r
-                                       if ((hr = IAudioRenderClient_ReleaseBuffer(stream->rclient, o_processed, 0)) != S_OK)\r
+                                       if ((hr = IAudioRenderClient_ReleaseBuffer(stream->renderClient, o_processed, 0)) != S_OK)\r
                                                LogHostError(hr);\r
                                }\r
                                else\r
@@ -4518,7 +4939,7 @@ PA_THREAD_FUNC ProcThreadPoll(void *param)
                        }\r
 \r
                        // release host input buffer\r
-                       if ((hr = IAudioCaptureClient_ReleaseBuffer(stream->cclient, i_processed)) != S_OK)\r
+                       if ((hr = IAudioCaptureClient_ReleaseBuffer(stream->captureClient, i_processed)) != S_OK)\r
                        {\r
                                LogHostError(hr);\r
                                break;\r
@@ -4546,7 +4967,7 @@ PA_THREAD_FUNC ProcThreadPoll(void *param)
                        }\r
 \r
                        // get available frames\r
-                       if ((hr = PollGetOutputFramesAvailable(stream, &o_frames)) != S_OK)\r
+                       if ((hr = _PollGetOutputFramesAvailable(stream, &o_frames)) != S_OK)\r
                        {\r
                                LogHostError(hr);\r
                                break;\r
@@ -4555,7 +4976,7 @@ PA_THREAD_FUNC ProcThreadPoll(void *param)
                        while (o_frames != 0)\r
                        {\r
                                // get host input buffer\r
-                               if ((hr = IAudioCaptureClient_GetBuffer(stream->cclient, &i_data, &i_frames, &i_flags, NULL, NULL)) != S_OK)\r
+                               if ((hr = IAudioCaptureClient_GetBuffer(stream->captureClient, &i_data, &i_frames, &i_flags, NULL, NULL)) != S_OK)\r
                                {\r
                                        if (hr == AUDCLNT_S_BUFFER_EMPTY)\r
                                                break; // no data in capture buffer\r
@@ -4574,7 +4995,7 @@ PA_THREAD_FUNC ProcThreadPoll(void *param)
                                        UINT32 o_processed = i_frames;\r
 \r
                                        // get host output buffer\r
-                                       if ((hr = IAudioRenderClient_GetBuffer(stream->rclient, o_processed, &o_data)) == S_OK)\r
+                                       if ((hr = IAudioRenderClient_GetBuffer(stream->renderClient, o_processed, &o_data)) == S_OK)\r
                                        {\r
                                                // processed amount of i_frames\r
                                                i_processed = i_frames;\r
@@ -4584,10 +5005,10 @@ PA_THREAD_FUNC ProcThreadPoll(void *param)
                                                if (stream->out.monoMixer)\r
                                                {\r
                                                        UINT32 mono_frames_size = o_processed * (stream->out.wavex.Format.wBitsPerSample / 8);\r
-                                                       // expand buffer (one way only for better performance due to no calls to realloc)\r
+                                                       // expand buffer\r
                                                        if (mono_frames_size > stream->out.monoBufferSize)\r
                                                        {\r
-                                                               stream->out.monoBuffer = realloc(stream->out.monoBuffer, (stream->out.monoBufferSize = mono_frames_size));\r
+                                                               stream->out.monoBuffer = PaWasapi_ReallocateMemory(stream->out.monoBuffer, (stream->out.monoBufferSize = mono_frames_size));\r
                                                                if (stream->out.monoBuffer == NULL)\r
                                                                {\r
                                                                        LogPaError(paInsufficientMemory);\r
@@ -4603,10 +5024,10 @@ PA_THREAD_FUNC ProcThreadPoll(void *param)
                                                if (stream->in.monoMixer)\r
                                                {\r
                                                        UINT32 mono_frames_size = i_processed * (stream->in.wavex.Format.wBitsPerSample / 8);\r
-                                                       // expand buffer (one way only for better performance due to no calls to realloc)\r
+                                                       // expand buffer\r
                                                        if (mono_frames_size > stream->in.monoBufferSize)\r
                                                        {\r
-                                                               stream->in.monoBuffer = realloc(stream->in.monoBuffer, (stream->in.monoBufferSize = mono_frames_size));\r
+                                                               stream->in.monoBuffer = PaWasapi_ReallocateMemory(stream->in.monoBuffer, (stream->in.monoBufferSize = mono_frames_size));\r
                                                                if (stream->in.monoBuffer == NULL)\r
                                                                {\r
                                                                        LogPaError(paInsufficientMemory);\r
@@ -4629,7 +5050,7 @@ PA_THREAD_FUNC ProcThreadPoll(void *param)
                                                        stream->out.monoMixer(o_data_host, stream->out.monoBuffer, o_processed);\r
 \r
                                                // release host output buffer\r
-                                               if ((hr = IAudioRenderClient_ReleaseBuffer(stream->rclient, o_processed, 0)) != S_OK)\r
+                                               if ((hr = IAudioRenderClient_ReleaseBuffer(stream->renderClient, o_processed, 0)) != S_OK)\r
                                                        LogHostError(hr);\r
 \r
                                                o_frames -= o_processed;\r
@@ -4649,7 +5070,7 @@ PA_THREAD_FUNC ProcThreadPoll(void *param)
 fd_release_buffer_in:\r
 \r
                                // release host input buffer\r
-                               if ((hr = IAudioCaptureClient_ReleaseBuffer(stream->cclient, i_processed)) != S_OK)\r
+                               if ((hr = IAudioCaptureClient_ReleaseBuffer(stream->captureClient, i_processed)) != S_OK)\r
                                {\r
                                        LogHostError(hr);\r
                                        break;\r
@@ -4666,7 +5087,14 @@ fd_release_buffer_in:
 thread_end:\r
 \r
        // Process stop\r
-       _OnStreamStop(stream);\r
+       _StreamOnStop(stream);\r
+\r
+       // Release unmarshaled COM pointers\r
+       ReleaseUnmarshaledComPointers(stream);\r
+\r
+       // Cleanup COM for this thread\r
+       if (bThreadComInitialized == TRUE)\r
+               CoUninitialize();\r
 \r
        // Notify: not running\r
        stream->running = FALSE;\r
@@ -4685,6 +5113,18 @@ thread_error:
        goto thread_end;\r
 }\r
 \r
+// ------------------------------------------------------------------------------------------\r
+void *PaWasapi_ReallocateMemory(void *ptr, size_t size)\r
+{\r
+       return realloc(ptr, size);\r
+}\r
+\r
+// ------------------------------------------------------------------------------------------\r
+void PaWasapi_FreeMemory(void *ptr)\r
+{\r
+       free(ptr);\r
+}\r
+\r
 //#endif //VC 2005\r
 \r
 \r