]> Repos - portaudio/commitdiff
wasapi:
authordmitrykos <dmitrykos@0f58301d-fd10-0410-b4af-bbb618454e57>
Thu, 8 Jul 2010 21:03:08 +0000 (21:03 +0000)
committerdmitrykos <dmitrykos@0f58301d-fd10-0410-b4af-bbb618454e57>
Thu, 8 Jul 2010 21:03:08 +0000 (21:03 +0000)
 - fixed Exclusive/Event mode if latency used exceeds 100ms resulting in bad audio
 - fixed Exclusive mode if overall stream latency exceeds 1400-2000ms resulting in E_OUTOFMEMORY even under Vista, now PA WASAPI will gradually decrease overall latency until acceptable level
 - applied device period aligning if period was corrected
 - improved robustness of audio client creation

src/hostapi/wasapi/pa_win_wasapi.c

index f5dd0ae83ea2926e1ca29c2fe201714206f049b4..f7e054fb8d5627d7a106a9ba2c920f19b57806b6 100644 (file)
@@ -708,6 +708,9 @@ static UINT32 MakeFramesFromHns(REFERENCE_TIME hnsPeriod, UINT32 nSamplesPerSec)
        return nFrames;\r
 }\r
 \r
+// Aligning function type\r
+typedef UINT32 (*ALIGN_FUNC) (UINT32 v, UINT32 align);\r
+\r
 // ------------------------------------------------------------------------------------------\r
 // Aligns 'v' backwards\r
 static UINT32 ALIGN_BWD(UINT32 v, UINT32 align)\r
@@ -729,7 +732,8 @@ static UINT32 ALIGN_FWD(UINT32 v, UINT32 align)
 // Aligns WASAPI buffer to 128 byte packet boundary. HD Audio will fail to play if buffer\r
 // is misaligned. This problem was solved in Windows 7 were AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED\r
 // is thrown although we must align for Vista anyway.\r
-static UINT32 AlignFramesPerBuffer(UINT32 nFrames, UINT32 nSamplesPerSec, UINT32 nBlockAlign)\r
+static UINT32 AlignFramesPerBuffer(UINT32 nFrames, UINT32 nSamplesPerSec, UINT32 nBlockAlign, \r
+                                                                  ALIGN_FUNC pAlignFunc)\r
 {\r
 #define HDA_PACKET_SIZE (128)\r
 \r
@@ -737,7 +741,7 @@ static UINT32 AlignFramesPerBuffer(UINT32 nFrames, UINT32 nSamplesPerSec, UINT32
        long packets;\r
 \r
        // align to packet size\r
-       frame_bytes  = ALIGN_FWD(frame_bytes, HDA_PACKET_SIZE); // use ALIGN_FWD if bigger but safer period is more desired\r
+       frame_bytes  = pAlignFunc(frame_bytes, HDA_PACKET_SIZE); // use ALIGN_FWD if bigger but safer period is more desired\r
        nFrames      = frame_bytes / nBlockAlign;\r
        packets      = frame_bytes / HDA_PACKET_SIZE;\r
 \r
@@ -1937,24 +1941,66 @@ static PaUint32 PaUtil_GetFramesPerHostBuffer(PaUint32 userFramesPerBuffer, PaTi
 }\r
 \r
 // ------------------------------------------------------------------------------------------\r
-static HRESULT CreateAudioClient(PaWasapiSubStream *pSubStream, PaWasapiDeviceInfo *info,\r
-       const PaStreamParameters *params, UINT32 framesPerLatency, double sampleRate, UINT32 streamFlags,\r
-       BOOL blocking, BOOL output, BOOL full_duplex, PaError *pa_error)\r
+static void _RecalculateBuffersCount(PaWasapiSubStream *sub, UINT32 userFramesPerBuffer, UINT32 framesPerLatency, BOOL fullDuplex)\r
+{\r
+       // Count buffers (must be at least 1)\r
+       sub->buffers = (userFramesPerBuffer ? framesPerLatency / userFramesPerBuffer : 0);\r
+       if (sub->buffers == 0)\r
+               sub->buffers = 1;\r
+\r
+       // Determine amount of buffers used:\r
+       // - Full-duplex mode will lead to period difference, thus only 1.\r
+       // - Input mode, only 1, as WASAPI allows extraction of only 1 packet.\r
+       // - For Shared mode we use double buffering.\r
+       if ((sub->shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE) || fullDuplex)\r
+       {\r
+               // Exclusive mode does not allow >1 buffers be used for Event interface, e.g. GetBuffer \r
+               // call must acquire max buffer size and it all must be processed.\r
+               if (sub->streamFlags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK)\r
+                       sub->userBufferAndHostMatch = 1;\r
+               \r
+               // Use paUtilBoundedHostBufferSize because exclusive mode will starve and produce\r
+               // bad quality of audio\r
+               sub->buffers = 1;\r
+       }\r
+}\r
+\r
+// ------------------------------------------------------------------------------------------\r
+static void _CalculateAlignedPeriod(PaWasapiSubStream *pSub, UINT32 *nFramesPerLatency, \r
+                                                                       ALIGN_FUNC pAlignFunc)\r
+{\r
+       // Align frames to HD Audio packet size of 128 bytes for Exclusive mode only.\r
+       // Not aligning on Windows Vista will cause Event timeout, although Windows 7 will\r
+       // return AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED error to realign buffer. Aligning is necessary\r
+       // for Exclusive mode only! when audio data is feeded directly to hardware.\r
+       if (pSub->shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE)\r
+       {\r
+               (*nFramesPerLatency) = AlignFramesPerBuffer((*nFramesPerLatency),\r
+                       pSub->wavex.Format.nSamplesPerSec, pSub->wavex.Format.nBlockAlign, pAlignFunc);\r
+       }\r
+\r
+       // Calculate period\r
+       pSub->period = MakeHnsPeriod((*nFramesPerLatency), pSub->wavex.Format.nSamplesPerSec);\r
+}\r
+\r
+// ------------------------------------------------------------------------------------------\r
+static HRESULT CreateAudioClient(PaWasapiSubStream *pSub, PaWasapiDeviceInfo *pInfo,\r
+       const PaStreamParameters *params, UINT32 framesPerLatency, double sampleRate,\r
+       BOOL blocking, BOOL output, BOOL fullDuplex, PaError *pa_error)\r
 {\r
        PaError error;\r
-    HRESULT hr                                 = S_OK;\r
-    UINT32 nFrames                             = 0;\r
-       UINT32 userFramesPerBuffer  = framesPerLatency;\r
-    IAudioClient *pAudioClient = NULL;\r
-       double suggestedLatency     = 0.0;\r
+    HRESULT hr;\r
+       const UINT32 userFramesPerBuffer = framesPerLatency;\r
+    IAudioClient *audioClient       = NULL;\r
 \r
-    if (!pSubStream || !info || !params)\r
+       // Validate parameters\r
+    if (!pSub || !pInfo || !params)\r
         return E_POINTER;\r
        if ((UINT32)sampleRate == 0)\r
         return E_INVALIDARG;\r
 \r
     // Get the audio client\r
-    hr = IMMDevice_Activate(info->device, &pa_IID_IAudioClient, CLSCTX_ALL, NULL, (void **)&pAudioClient);\r
+    hr = IMMDevice_Activate(pInfo->device, &pa_IID_IAudioClient, CLSCTX_ALL, NULL, (void **)&audioClient);\r
        if (hr != S_OK)\r
        {\r
                LogHostError(hr);\r
@@ -1962,7 +2008,7 @@ static HRESULT CreateAudioClient(PaWasapiSubStream *pSubStream, PaWasapiDeviceIn
        }\r
 \r
        // Get closest format\r
-       if ((error = GetClosestFormat(pAudioClient, sampleRate, params, pSubStream->shareMode, &pSubStream->wavex, output)) != paFormatIsSupported)\r
+       if ((error = GetClosestFormat(audioClient, sampleRate, params, pSub->shareMode, &pSub->wavex, output)) != paFormatIsSupported)\r
        {\r
                if (pa_error)\r
                        (*pa_error) = error;\r
@@ -1972,7 +2018,7 @@ static HRESULT CreateAudioClient(PaWasapiSubStream *pSubStream, PaWasapiDeviceIn
        }\r
 \r
        // Check for Mono >> Stereo workaround\r
-       if ((params->channelCount == 1) && (pSubStream->wavex.Format.nChannels == 2))\r
+       if ((params->channelCount == 1) && (pSub->wavex.Format.nChannels == 2))\r
        {\r
                if (blocking)\r
                {\r
@@ -1981,8 +2027,8 @@ static HRESULT CreateAudioClient(PaWasapiSubStream *pSubStream, PaWasapiDeviceIn
                }\r
 \r
                // select mixer\r
-               pSubStream->monoMixer = _GetMonoToStereoMixer(params->sampleFormat, (info->flow == eRender ? MIX_DIR__1TO2 : MIX_DIR__2TO1));\r
-               if (pSubStream->monoMixer == NULL)\r
+               pSub->monoMixer = _GetMonoToStereoMixer(params->sampleFormat, (pInfo->flow == eRender ? MIX_DIR__1TO2 : MIX_DIR__2TO1));\r
+               if (pSub->monoMixer == NULL)\r
                {\r
                        LogHostError(hr = AUDCLNT_E_UNSUPPORTED_FORMAT);\r
                        goto done; // fail, no mixer for format\r
@@ -1990,122 +2036,159 @@ static HRESULT CreateAudioClient(PaWasapiSubStream *pSubStream, PaWasapiDeviceIn
        }\r
 \r
 #if 0\r
-       // Correct latency to default device period (in Exclusive mode this is 10ms) if user selected 0\r
-       suggestedLatency = params->suggestedLatency;\r
        // Add suggestd latency\r
-       framesPerLatency += MakeFramesFromHns(SecondsTonano100(suggestedLatency), pSubStream->wavex.Format.nSamplesPerSec);\r
+       framesPerLatency += MakeFramesFromHns(SecondsTonano100(params->suggestedLatency), pSub->wavex.Format.nSamplesPerSec);\r
 #else\r
        // Calculate host buffer size\r
-       if ((pSubStream->shareMode != AUDCLNT_SHAREMODE_EXCLUSIVE) && \r
-               (!streamFlags || ((streamFlags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK) == 0)))\r
+       if ((pSub->shareMode != AUDCLNT_SHAREMODE_EXCLUSIVE) && \r
+               (!pSub->streamFlags || ((pSub->streamFlags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK) == 0)))\r
        {\r
-               framesPerLatency = PaUtil_GetFramesPerHostBuffer(framesPerLatency\r
-                       params->suggestedLatency, pSubStream->wavex.Format.nSamplesPerSec, 0/*,\r
-                       (streamFlags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK ? 0 : 1)*/);\r
+               framesPerLatency = PaUtil_GetFramesPerHostBuffer(userFramesPerBuffer\r
+                       params->suggestedLatency, pSub->wavex.Format.nSamplesPerSec, 0/*,\r
+                       (pSub->streamFlags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK ? 0 : 1)*/);\r
        }\r
        else\r
        {\r
-               // do nothing, work 1:1 with buffer (only polling allows to work with >1)\r
-       }\r
-#endif\r
+               REFERENCE_TIME overall;\r
 \r
-       // Count buffers (must be at least 1)\r
-       pSubStream->buffers = (userFramesPerBuffer ? framesPerLatency / userFramesPerBuffer : 0);\r
-       if (pSubStream->buffers == 0)\r
-               pSubStream->buffers = 1;\r
+               // Work 1:1 with user buffer (only polling allows to use >1)\r
+               framesPerLatency += MakeFramesFromHns(SecondsTonano100(params->suggestedLatency), pSub->wavex.Format.nSamplesPerSec);\r
+               \r
+               // Use Polling if overall latency is > 5ms as it allows to use 100% CPU in a callback,\r
+               // or user specified latency parameter\r
+               overall = MakeHnsPeriod(framesPerLatency, pSub->wavex.Format.nSamplesPerSec);\r
+               if ((overall > 50000) || (params->suggestedLatency > 0))\r
+               {\r
+                       framesPerLatency = PaUtil_GetFramesPerHostBuffer(userFramesPerBuffer, \r
+                               params->suggestedLatency, pSub->wavex.Format.nSamplesPerSec, 0/*,\r
+                               (streamFlags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK ? 0 : 1)*/);\r
 \r
-       // Determine amount of buffers to be used:\r
-       // - Exclusive mode does not allow >1 buffers be used, e.g. GetBuffer call must acquire\r
-       //   max buffer size and it all must be processed.\r
-       // - Full-duplex mode will lead to period difference, thus only 1.\r
-       // - Input mode, only 1, as WASAPI allows extraction of only 1 packet.\r
-       // - For Shared mode we use safe double buffering (2 buffers).\r
-       if ((pSubStream->shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE) || full_duplex/* && output*/)\r
-       {\r
-               pSubStream->userBufferAndHostMatch = 1;\r
-               pSubStream->buffers = 1;\r
+                       // Use Polling interface\r
+                       pSub->streamFlags &= ~AUDCLNT_STREAMFLAGS_EVENTCALLBACK;\r
+               }\r
        }\r
+#endif\r
 \r
        // Avoid 0 frames\r
        if (framesPerLatency == 0)\r
-               framesPerLatency = MakeFramesFromHns(info->DefaultDevicePeriod, pSubStream->wavex.Format.nSamplesPerSec);\r
+               framesPerLatency = MakeFramesFromHns(pInfo->DefaultDevicePeriod, pSub->wavex.Format.nSamplesPerSec);\r
 \r
-       // Align frames to HD Audio packet size of 128 bytes for Exclusive mode only.\r
-       // Not aligning on Windows Vista will cause Event timeout, although Windows 7 will\r
-       // return AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED error to realign buffer. Aligning is necessary\r
-       // for Exclusive mode only! when audio data is feeded directly to hardware.\r
-       if (pSubStream->shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE)\r
+       // Calculate aligned period\r
+       _CalculateAlignedPeriod(pSub, &framesPerLatency, ALIGN_FWD);\r
+\r
+       /*! Enforce min/max period for device in Shared mode to avoid bad audio quality.\r
+        Avoid doing so for Exclusive mode as alignment will suffer.\r
+       */\r
+       if (pSub->shareMode == AUDCLNT_SHAREMODE_SHARED)\r
        {\r
-               framesPerLatency = AlignFramesPerBuffer(framesPerLatency,\r
-                       pSubStream->wavex.Format.nSamplesPerSec, pSubStream->wavex.Format.nBlockAlign);\r
+               if (pSub->period < pInfo->DefaultDevicePeriod)\r
+                       pSub->period = pInfo->DefaultDevicePeriod;\r
        }\r
-\r
-    // Calculate period\r
-       pSubStream->period = MakeHnsPeriod(framesPerLatency, pSubStream->wavex.Format.nSamplesPerSec);\r
-\r
-       // Enforce min/max period for device in Shared mode to avoid distorted sound.\r
-       // Avoid doing so for Exclusive mode as alignment will suffer. Exclusive mode processes\r
-       // big buffers without problem. Push Exclusive beyond limits if possible.\r
-       if (pSubStream->shareMode == AUDCLNT_SHAREMODE_SHARED)\r
+       else\r
        {\r
-               if (pSubStream->period < info->DefaultDevicePeriod)\r
-                       pSubStream->period = info->DefaultDevicePeriod;\r
+               if (pSub->period < pInfo->MinimumDevicePeriod)\r
+                       pSub->period = pInfo->MinimumDevicePeriod;\r
        }\r
 \r
-       // Windows 7 does not allow to set latency lower than minimal device period and will\r
-       // return error: AUDCLNT_E_INVALID_DEVICE_PERIOD. Under Vista though this error is not implemented\r
-       // allowing to hack WASAPI device with lower period resulting in possibly lower latency.\r
-       if (GetWindowsVersion() & WINDOWS_7_SERVER2008R2_AND_UP)\r
+       /*! Windows 7 does not allow to set latency lower than minimal device period and will\r
+           return error: AUDCLNT_E_INVALID_DEVICE_PERIOD. Under Vista we enforce the same behavior\r
+           manually for unified behavior on all platforms.\r
+       */\r
        {\r
-               if (pSubStream->period < info->MinimumDevicePeriod)\r
-                       pSubStream->period = info->MinimumDevicePeriod;\r
-\r
-               /*\r
-                       AUDCLNT_E_BUFFER_SIZE_ERROR: Applies to Windows 7 and later.\r
+               /*!     AUDCLNT_E_BUFFER_SIZE_ERROR: Applies to Windows 7 and later.\r
                        Indicates that the buffer duration value requested by an exclusive-mode client is \r
                        out of range. The requested duration value for pull mode must not be greater than \r
                        500 milliseconds; for push mode the duration value must not be greater than 2 seconds.\r
                */\r
-               if (pSubStream->shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE)\r
+               if (pSub->shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE)\r
                {\r
-static const REFERENCE_TIME MAX_BUFFER_EVENT_DURATION = 500  * 10000;\r
-static const REFERENCE_TIME MAX_BUFFER_POLL_DURATION  = 2000 * 10000;\r
+                       static const REFERENCE_TIME MAX_BUFFER_EVENT_DURATION = 500  * 10000;\r
+                       static const REFERENCE_TIME MAX_BUFFER_POLL_DURATION  = 2000 * 10000;\r
 \r
-                       if (streamFlags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK) // pull mode, max 500ms\r
+                       if (pSub->streamFlags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK)      // pull mode, max 500ms\r
                        {\r
-                               if (pSubStream->period > MAX_BUFFER_EVENT_DURATION)\r
-                                       pSubStream->period = MAX_BUFFER_EVENT_DURATION;\r
+                               if (pSub->period > MAX_BUFFER_EVENT_DURATION)\r
+                               {\r
+                                       pSub->period = MAX_BUFFER_EVENT_DURATION;\r
+\r
+                                       // Recalculate aligned period\r
+                                       framesPerLatency = MakeFramesFromHns(pSub->period, pSub->wavex.Format.nSamplesPerSec);\r
+                                       _CalculateAlignedPeriod(pSub, &framesPerLatency, ALIGN_BWD);\r
+                               }\r
                        }\r
-                       else                                                                                             // push mode, max 2000ms\r
+                       else                                                                                                            // push mode, max 2000ms\r
                        {\r
-                               if (pSubStream->period > MAX_BUFFER_POLL_DURATION)\r
-                                       pSubStream->period = MAX_BUFFER_POLL_DURATION;\r
+                               if (pSub->period > MAX_BUFFER_POLL_DURATION)\r
+                               {\r
+                                       pSub->period = MAX_BUFFER_POLL_DURATION;\r
+\r
+                                       // Recalculate aligned period\r
+                                       framesPerLatency = MakeFramesFromHns(pSub->period, pSub->wavex.Format.nSamplesPerSec);\r
+                                       _CalculateAlignedPeriod(pSub, &framesPerLatency, ALIGN_BWD);\r
+                               }\r
                        }\r
                }\r
        }\r
 \r
        // Open the stream and associate it with an audio session\r
-    hr = IAudioClient_Initialize(pAudioClient,\r
-        pSubStream->shareMode,\r
-        streamFlags/*AUDCLNT_STREAMFLAGS_EVENTCALLBACK*/,\r
-               pSubStream->period,\r
-               (pSubStream->shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE ? pSubStream->period : 0),\r
-               &pSubStream->wavex.Format,\r
+    hr = IAudioClient_Initialize(audioClient,\r
+        pSub->shareMode,\r
+        pSub->streamFlags,\r
+               pSub->period,\r
+               (pSub->shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE ? pSub->period : 0),\r
+               &pSub->wavex.Format,\r
         NULL);\r
 \r
-       // This would mean too low latency\r
+       /*! WASAPI is tricky on large device buffer, sometimes 2000ms can be allocated sometimes\r
+           less. There is no known guaranteed level thus we make subsequent tries by decreasing \r
+               buffer by 100ms per try.\r
+       */\r
+       while ((hr == E_OUTOFMEMORY) && (pSub->period > (100 * 10000)))\r
+       {\r
+               PRINT(("WASAPI: CreateAudioClient: decreasing buffer size to %d milliseconds\n", (pSub->period / 10000)));\r
+\r
+               // Decrease by 100ms and try again\r
+               pSub->period -= (100 * 10000);\r
+\r
+               // Recalculate aligned period\r
+               framesPerLatency = MakeFramesFromHns(pSub->period, pSub->wavex.Format.nSamplesPerSec);\r
+               _CalculateAlignedPeriod(pSub, &framesPerLatency, ALIGN_BWD);\r
+\r
+        // Release the previous allocations\r
+        SAFE_RELEASE(audioClient);\r
+\r
+        // Create a new audio client\r
+        hr = IMMDevice_Activate(pInfo->device, &pa_IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&audioClient);\r
+       if (hr != S_OK)\r
+               {\r
+                       LogHostError(hr);\r
+                       goto done;\r
+               }\r
+\r
+               // Open the stream and associate it with an audio session\r
+               hr = IAudioClient_Initialize(audioClient,\r
+                       pSub->shareMode,\r
+                       pSub->streamFlags,\r
+                       pSub->period,\r
+                       (pSub->shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE ? pSub->period : 0),\r
+                       &pSub->wavex.Format,\r
+                       NULL);\r
+       }\r
+\r
+       /*! WASAPI buffer size failure. Fallback to using default size.\r
+       */\r
        if (hr == AUDCLNT_E_BUFFER_SIZE_ERROR)\r
        {\r
-               PRINT(("WASAPI: CreateAudioClient: correcting buffer size to device minimum\n"));\r
+               // Use default\r
+               pSub->period = pInfo->DefaultDevicePeriod;\r
 \r
-               // User was trying to set lowest possible, so set lowest device possible\r
-               pSubStream->period = info->MinimumDevicePeriod;\r
+               PRINT(("WASAPI: CreateAudioClient: correcting buffer size to device default\n"));\r
 \r
         // Release the previous allocations\r
-        SAFE_RELEASE(pAudioClient);\r
+        SAFE_RELEASE(audioClient);\r
 \r
         // Create a new audio client\r
-        hr = IMMDevice_Activate(info->device, &pa_IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&pAudioClient);\r
+        hr = IMMDevice_Activate(pInfo->device, &pa_IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&audioClient);\r
        if (hr != S_OK)\r
                {\r
                        LogHostError(hr);\r
@@ -2113,33 +2196,37 @@ static const REFERENCE_TIME MAX_BUFFER_POLL_DURATION  = 2000 * 10000;
                }\r
 \r
                // Open the stream and associate it with an audio session\r
-               hr = IAudioClient_Initialize(pAudioClient,\r
-                       pSubStream->shareMode,\r
-                       streamFlags/*AUDCLNT_STREAMFLAGS_EVENTCALLBACK*/,\r
-                       pSubStream->period,\r
-                       (pSubStream->shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE ? pSubStream->period : 0),\r
-                       &pSubStream->wavex.Format,\r
+               hr = IAudioClient_Initialize(audioClient,\r
+                       pSub->shareMode,\r
+                       pSub->streamFlags,\r
+                       pSub->period,\r
+                       (pSub->shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE ? pSub->period : 0),\r
+                       &pSub->wavex.Format,\r
                        NULL);\r
        }\r
 \r
-    // If the requested buffer size is not aligned...\r
+    /*! If the requested buffer size is not aligned. Can be triggered by Windows 7 and up.\r
+           Should not be be triggered ever as we do align buffers always with _CalculateAlignedPeriod.\r
+       */\r
     if (hr == AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED)\r
     {\r
-               PRINT(("WASAPI: CreateAudioClient: aligning buffer size\n"));\r
+               UINT32 frames = 0;\r
 \r
         // Get the next aligned frame\r
-        hr = IAudioClient_GetBufferSize(pAudioClient, &nFrames);\r
+        hr = IAudioClient_GetBufferSize(audioClient, &frames);\r
                if (hr != S_OK)\r
                {\r
                        LogHostError(hr);\r
                        goto done;\r
                }\r
 \r
+               PRINT(("WASAPI: CreateAudioClient: aligning buffer size to % frames\n", frames));\r
+\r
         // Release the previous allocations\r
-        SAFE_RELEASE(pAudioClient);\r
+        SAFE_RELEASE(audioClient);\r
 \r
         // Create a new audio client\r
-        hr = IMMDevice_Activate(info->device, &pa_IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&pAudioClient);\r
+        hr = IMMDevice_Activate(pInfo->device, &pa_IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&audioClient);\r
        if (hr != S_OK)\r
                {\r
                        LogHostError(hr);\r
@@ -2147,7 +2234,7 @@ static const REFERENCE_TIME MAX_BUFFER_POLL_DURATION  = 2000 * 10000;
                }\r
 \r
                // Get closest format\r
-               if ((error = GetClosestFormat(pAudioClient, sampleRate, params, pSubStream->shareMode, &pSubStream->wavex, output)) != paFormatIsSupported)\r
+               if ((error = GetClosestFormat(audioClient, sampleRate, params, pSub->shareMode, &pSub->wavex, output)) != paFormatIsSupported)\r
                {\r
                        if (pa_error)\r
                                (*pa_error) = error;\r
@@ -2157,7 +2244,7 @@ static const REFERENCE_TIME MAX_BUFFER_POLL_DURATION  = 2000 * 10000;
                }\r
 \r
                // Check for Mono >> Stereo workaround\r
-               if ((params->channelCount == 1) && (pSubStream->wavex.Format.nChannels == 2))\r
+               if ((params->channelCount == 1) && (pSub->wavex.Format.nChannels == 2))\r
                {\r
                        if (blocking)\r
                        {\r
@@ -2165,37 +2252,25 @@ static const REFERENCE_TIME MAX_BUFFER_POLL_DURATION  = 2000 * 10000;
                                goto done; // fail, blocking mode not supported\r
                        }\r
 \r
-                       // select mixer\r
-                       pSubStream->monoMixer = _GetMonoToStereoMixer(params->sampleFormat, (info->flow == eRender ? MIX_DIR__1TO2 : MIX_DIR__2TO1));\r
-                       if (pSubStream->monoMixer == NULL)\r
+                       // Select mixer\r
+                       pSub->monoMixer = _GetMonoToStereoMixer(params->sampleFormat, (pInfo->flow == eRender ? MIX_DIR__1TO2 : MIX_DIR__2TO1));\r
+                       if (pSub->monoMixer == NULL)\r
                        {\r
                                LogHostError(hr = AUDCLNT_E_UNSUPPORTED_FORMAT);\r
                                goto done; // fail, no mixer for format\r
                        }\r
                }\r
 \r
-               // Reset buffers count to single\r
-               pSubStream->buffers = 1;\r
-\r
                // Calculate period\r
-               pSubStream->period = MakeHnsPeriod(nFrames, pSubStream->wavex.Format.nSamplesPerSec);\r
-\r
-               // Windows 7 does not allow to set latency lower than minimal device period and will\r
-               // return error: AUDCLNT_E_INVALID_DEVICE_PERIOD. Under Vista though this error is not implemented\r
-               // allowing to hack WASAPI device with lower period resulting in possibly lower latency.\r
-               if (GetWindowsVersion() & WINDOWS_7_SERVER2008R2_AND_UP)\r
-               {\r
-                       if (pSubStream->period < info->MinimumDevicePeriod)\r
-                               pSubStream->period = info->MinimumDevicePeriod;\r
-               }\r
+               pSub->period = MakeHnsPeriod(frames, pSub->wavex.Format.nSamplesPerSec);\r
 \r
         // Open the stream and associate it with an audio session\r
-        hr = IAudioClient_Initialize(pAudioClient,\r
-            pSubStream->shareMode,\r
-            streamFlags/*AUDCLNT_STREAMFLAGS_EVENTCALLBACK*/,\r
-                       pSubStream->period,\r
-                       (pSubStream->shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE ? pSubStream->period : 0),\r
-            &pSubStream->wavex.Format,\r
+        hr = IAudioClient_Initialize(audioClient,\r
+            pSub->shareMode,\r
+            pSub->streamFlags,\r
+                       pSub->period,\r
+                       (pSub->shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE ? pSub->period : 0),\r
+            &pSub->wavex.Format,\r
             NULL);\r
        if (hr != S_OK)\r
                {\r
@@ -2210,14 +2285,20 @@ static const REFERENCE_TIME MAX_BUFFER_POLL_DURATION  = 2000 * 10000;
                goto done;\r
     }\r
 \r
-    // Set result\r
-       pSubStream->client = pAudioClient;\r
-    IAudioClient_AddRef(pSubStream->client);\r
+    // Set client\r
+       pSub->client = audioClient;\r
+    IAudioClient_AddRef(pSub->client);\r
+\r
+       // Recalculate buffers count\r
+       _RecalculateBuffersCount(pSub, \r
+               userFramesPerBuffer, \r
+               MakeFramesFromHns(pSub->period, pSub->wavex.Format.nSamplesPerSec), \r
+               fullDuplex);\r
 \r
 done:\r
 \r
     // Clean up\r
-    SAFE_RELEASE(pAudioClient);\r
+    SAFE_RELEASE(audioClient);\r
     return hr;\r
 }\r
 \r
@@ -2331,7 +2412,7 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
 \r
                // Create Audio client\r
                hr = CreateAudioClient(&stream->in, info, inputParameters, framesPerBuffer/*framesPerLatency*/,\r
-                       sampleRate, stream->in.streamFlags, (streamCallback == NULL), FALSE, fullDuplex, &result);\r
+                       sampleRate, (streamCallback == NULL), FALSE, fullDuplex, &result);\r
         if (hr != S_OK)\r
                {\r
             LogHostError(hr);\r
@@ -2449,7 +2530,7 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
 \r
                // Create Audio client\r
                hr = CreateAudioClient(&stream->out, info, outputParameters, framesPerBuffer/*framesPerLatency*/,\r
-                       sampleRate, stream->out.streamFlags, (streamCallback == NULL), TRUE, fullDuplex, &result);\r
+                       sampleRate, (streamCallback == NULL), TRUE, fullDuplex, &result);\r
         if (hr != S_OK)\r
                {\r
             LogHostError(hr);\r