]> Repos - portaudio/commitdiff
wasapi: support Exclusive mode on UWP (use new function PaWasapi_SetDefaultInterfaceI...
authordmitrykos <dmitrykos@neutroncode.com>
Thu, 6 Sep 2018 17:45:22 +0000 (20:45 +0300)
committerdmitrykos <dmitrykos@neutroncode.com>
Thu, 6 Sep 2018 17:45:22 +0000 (20:45 +0300)
 - support for explicit 8.24 format (24-bit inside 32-bit container) by specifying paCustomFormat (see IMPORTANT notes in the header file);
 - new flag paWinWasapiExplicitSampleFormat to force explicit format and avoid fallback to a closest working;
 - new function PaWasapi_GetDeviceCurrentFormat to get device format of the opened stream;
 - support PaWasapi_ThreadPriorityXXX API on UWP to boot thread priority;
 - improved selection of the closest format in Exclusive mode.

include/pa_win_wasapi.h
src/hostapi/wasapi/pa_win_wasapi.c

index c7bfde2e152f8da46f34ecd5de27c53b03fb6be1..3c5d792c2341784a46d90067fc2531eff5c71147 100644 (file)
@@ -55,23 +55,28 @@ extern "C"
 /* Setup flags */
 typedef enum PaWasapiFlags
 {
-    /* puts WASAPI into exclusive mode */
+    /* put WASAPI into exclusive mode */
     paWinWasapiExclusive                = (1 << 0),
 
-    /* allows to skip internal PA processing completely */
+    /* allow to skip internal PA processing completely */
     paWinWasapiRedirectHostProcessor    = (1 << 1),
 
-    /* assigns custom channel mask */
+    /* assign custom channel mask */
     paWinWasapiUseChannelMask           = (1 << 2),
 
-    /* selects non-Event driven method of data read/write
+    /* select non-Event driven method of data read/write
        Note: WASAPI Event driven core is capable of 2ms latency!!!, but Polling
              method can only provide 15-20ms latency. */
     paWinWasapiPolling                  = (1 << 3),
 
-    /* forces custom thread priority setting, must be used if PaWasapiStreamInfo::threadPriority 
+    /* force custom thread priority setting, must be used if PaWasapiStreamInfo::threadPriority 
        is set to a custom value */
-    paWinWasapiThreadPriority           = (1 << 4)
+    paWinWasapiThreadPriority           = (1 << 4),
+
+    /* force explicit sample format and do not allow PA to select suitable working format, API will 
+       fail if provided sample format is not supported by audio hardware in Exclusive mode 
+       or system mixer in Shared mode */
+    paWinWasapiExplicitSampleFormat     = (1 << 5)
 }
 PaWasapiFlags;
 #define paWinWasapiExclusive             (paWinWasapiExclusive)
@@ -79,6 +84,7 @@ PaWasapiFlags;
 #define paWinWasapiUseChannelMask        (paWinWasapiUseChannelMask)
 #define paWinWasapiPolling               (paWinWasapiPolling)
 #define paWinWasapiThreadPriority        (paWinWasapiThreadPriority)
+#define paWinWasapiExplicitSampleFormat  (paWinWasapiExplicitSampleFormat)
 
 
 /* Host processor. Allows to skip internal PA processing completely. 
@@ -177,7 +183,7 @@ PaWasapiJackPortConnection;
 typedef enum PaWasapiThreadPriority
 {
     eThreadPriorityNone = 0,
-    eThreadPriorityAudio,            //!< Default for Shared mode.
+    eThreadPriorityAudio,           //!< Default for Shared mode.
     eThreadPriorityCapture,
     eThreadPriorityDistribution,
     eThreadPriorityGames,
@@ -293,9 +299,9 @@ PaWasapiStreamInfo;
 
 /** Returns pointer to WASAPI's IAudioClient object of the stream.
 
- @param pStream Pointer to PaStream.
+ @param pStream      Pointer to PaStream.
  @param pAudioClient Pointer to pointer of IAudioClient.
- @param bOutput TRUE (1) for output stream, FALSE (0) for input stream.
+ @param bOutput      TRUE (1) for output stream, FALSE (0) for input stream.
 
  @return Error code indicating success or failure.
 */
@@ -314,12 +320,28 @@ PaError PaWasapi_GetAudioClient( PaStream *pStream, void **pAudioClient, int bOu
 PaError PaWasapi_UpdateDeviceList();
 
 
+/** Returns current sound format of the device assigned to the opened stream. Format is represented by 
+    PaWinWaveFormat or WAVEFORMATEXTENSIBLE structure. Use this function to reconfirm format if 
+    PA's processor is overriden and paWinWasapiRedirectHostProcessor flag is specified.
+
+ @param pStream     Pointer to PaStream.
+ @param pFormat     Pointer to PaWinWaveFormat or WAVEFORMATEXTENSIBLE structure.
+ @param nFormatSize Size of PaWinWaveFormat or WAVEFORMATEXTENSIBLE structure in bytes.
+ @param bOutput     TRUE (1) for output stream, FALSE (0) for input stream.
+
+ @return Non-negative value indicating the number of bytes copied into format decriptor
+         or, a PaErrorCode (which are always negative) if PortAudio is not initialized
+         or an error is encountered.
+*/
+int PaWasapi_GetDeviceCurrentFormat( PaStream *pStream, void *pFormat, unsigned int nFormatSize, int bOutput );
+
+
 /** Returns default sound format for device. Format is represented by PaWinWaveFormat or 
     WAVEFORMATEXTENSIBLE structure.
 
- @param pFormat Pointer to PaWinWaveFormat or WAVEFORMATEXTENSIBLE structure.
- @param nFormatSize Size of PaWinWaveFormat or WAVEFORMATEXTENSIBLE structure in bytes.
- @param nDevice Device index.
+ @param  pFormat     Pointer to PaWinWaveFormat or WAVEFORMATEXTENSIBLE structure.
+ @param  nFormatSize Size of PaWinWaveFormat or WAVEFORMATEXTENSIBLE structure in bytes.
+ @param  nDevice     Device index.
 
  @return Non-negative value indicating the number of bytes copied into format decriptor
          or, a PaErrorCode (which are always negative) if PortAudio is not initialized
@@ -330,7 +352,7 @@ int PaWasapi_GetDeviceDefaultFormat( void *pFormat, unsigned int nFormatSize, Pa
 
 /** Returns device role (PaWasapiDeviceRole enum).
 
- @param nDevice device index.
+ @param  nDevice Device index.
 
  @return Non-negative value indicating device role or, a PaErrorCode (which are always negative)
          if PortAudio is not initialized or an error is encountered.
@@ -341,11 +363,11 @@ int/*PaWasapiDeviceRole*/ PaWasapi_GetDeviceRole( PaDeviceIndex nDevice );
 /** Boost thread priority of calling thread (MMCSS). Use it for Blocking Interface only for thread
     which makes calls to Pa_WriteStream/Pa_ReadStream.
 
- @param hTask Handle to pointer to priority task. Must be used with PaWasapi_RevertThreadPriority
-              method to revert thread priority to initial state.
+ @param  hTask Handle to pointer to priority task. Must be used with PaWasapi_RevertThreadPriority
+               method to revert thread priority to initial state.
 
- @param nPriorityClass Id of thread priority of PaWasapiThreadPriority type. Specifying 
-                       eThreadPriorityNone does nothing.
+ @param  nPriorityClass Id of thread priority of PaWasapiThreadPriority type. Specifying 
+                        eThreadPriorityNone does nothing.
 
  @return Error code indicating success or failure.
  @see    PaWasapi_RevertThreadPriority
@@ -380,10 +402,12 @@ PaError PaWasapi_GetFramesPerHostBuffer( PaStream *pStream, unsigned int *nInput
     there are any jacks associated with the provided WASAPI device.  Not all audio devices
     will support this capability.  This is valid for both input and output devices.
 
+ @note   Not available on UWP platform.
+
  @param  nDevice  device index.
  @param  jcount   Number of jacks is returned in this variable
  @return Error code indicating success or failure
- @see PaWasapi_GetJackDescription
+ @see    PaWasapi_GetJackDescription
  */
 PaError PaWasapi_GetJackCount( PaDeviceIndex nDevice, int *jcount );
 
@@ -394,15 +418,37 @@ PaError PaWasapi_GetJackCount( PaDeviceIndex nDevice, int *jcount );
     each jack from 0 to jcount can be queried with this function to get the jack
     description.
 
- @param  nDevice  device index.
- @param  jindex   Which jack to return information
- @param  KSJACK_DESCRIPTION This structure filled in on success.
+ @note   Not available on UWP platform.
+
+ @param  nDevice device index.
+ @param  jindex  Which jack to return information
+ @param  pJackDescription Pointer to PaWasapiJackDescription.
  @return Error code indicating success or failure
  @see PaWasapi_GetJackCount
  */
 PaError PaWasapi_GetJackDescription( PaDeviceIndex nDevice, int jindex, PaWasapiJackDescription *pJackDescription );
 
 
+/** Set default interface Id.
+    By default PA implementation will use DEVINTERFACE_AUDIO_RENDER and 
+    DEVINTERFACE_AUDIO_CAPTURE if device Id is not provided explicitly. These default Ids 
+    will not allow to use Exclusive mode on UWP platform and thus you must provide 
+    device Id explicitly via this API before calling Pa_OpenStream().
+    Device Ids on UWP platform are obtainable via
+    Windows::Media::Devices::MediaDevice::GetDefaultAudioRenderId() or
+    Windows::Media::Devices::MediaDevice::GetDefaultAudioCaptureId() API.
+
+ @note   UWP platform only.
+
+ @param  pId     Interface Id, pointer to the 16-bit Unicode string (WCHAR). If NULL then device Id
+                 will be reset to the default, e.g. DEVINTERFACE_AUDIO_RENDER or DEVINTERFACE_AUDIO_CAPTURE.
+ @param  bOutput TRUE (1) for output (render), FALSE (0) for input (capture).
+ @return Error code indicating success or failure. Will return paIncompatibleStreamHostApi if PA is not compiled
+         for UWP platform. If Id is longer than 4096 characters paBufferTooBig will be returned.
+*/
+PaError PaWasapi_SetDefaultInterfaceId( unsigned short *pId, int bOutput );
+
+
 /*
     IMPORTANT:
 
@@ -448,11 +494,25 @@ PaError PaWasapi_GetJackDescription( PaDeviceIndex nDevice, int jindex, PaWasapi
         deliver lowest possible latency. Specifying too low latency in Shared mode will result in 
         distorted audio although Exclusive mode adds stability.
 
+    8.24 format:
+
+        If paCustomFormat is specified as sample format then the implementation will understand it 
+        as valid 24-bits inside 32-bit container (e.g. wBitsPerSample = 32, Samples.wValidBitsPerSample = 24).
+        
+        By using paCustomFormat there will be small optimization when samples are be copied 
+        with Copy_24_To_24 by PA processor instead of conversion from packed 3-byte (24-bit) data 
+        with Int24_To_Int32.
+
     Pa_IsFormatSupported:
 
-        To check format with correct Share Mode (Exclusive/Shared) you must supply
-        PaWasapiStreamInfo with flags paWinWasapiExclusive set through member of 
-        PaStreamParameters::hostApiSpecificStreamInfo structure.
+        To check format with correct Share Mode (Exclusive/Shared) you must supply PaWasapiStreamInfo 
+        with flags paWinWasapiExclusive set through member of PaStreamParameters::hostApiSpecificStreamInfo 
+        structure. 
+        
+        If paWinWasapiExplicitSampleFormat flag is provided then implementation will not try to select 
+        suitable close format and will return an error instead of paFormatIsSupported. By specifying 
+        paWinWasapiExplicitSampleFormat flag it is possible to find out what sample formats are 
+        supported by Exclusive or Shared modes.
 
     Pa_OpenStream:
 
index 4c1890f2777709824e4aabe8d25036ece66a506b..fd57fd61ce5263aa5f6dec05d95983e6cb432101 100644 (file)
@@ -315,7 +315,7 @@ enum { WASAPI_PACKETS_PER_INPUT_BUFFER = 6 };
 #define SAFE_RELEASE(punk) if ((punk) != NULL) { (punk)->lpVtbl->Release((punk)); (punk) = NULL; }
 
 // Mixer function
-typedef void (*MixMonoToStereoF) (void *__to, void *__from, UINT32 count);
+typedef void (*MixMonoToStereoF) (void *__to, const void *__from, UINT32 count);
 
 // AVRT is the new "multimedia schedulling stuff"
 #ifndef PA_WINRT
@@ -334,6 +334,12 @@ FAvRevertMmThreadCharacteristics pAvRevertMmThreadCharacteristics = NULL;
 FAvSetMmThreadPriority           pAvSetMmThreadPriority = NULL;
 #endif
 
+#ifdef PA_WINRT
+#define PA_WASAPI_DEVICE_ID_LEN 4096
+static OLECHAR g_DefaultRenderId[PA_WASAPI_DEVICE_ID_LEN] = { 0 };
+static OLECHAR g_DefaultCaptureId[PA_WASAPI_DEVICE_ID_LEN] = { 0 };
+#endif
+
 #define _GetProc(fun, type, name)  {                                                        \
                                         fun = (type) GetProcAddress(hDInputDLL,name);       \
                                         if (fun == NULL) {                                  \
@@ -587,7 +593,7 @@ static void _StreamFinish(PaWasapiStream *stream);
 static void _StreamCleanup(PaWasapiStream *stream);
 static HRESULT _PollGetOutputFramesAvailable(PaWasapiStream *stream, UINT32 *available);
 static HRESULT _PollGetInputFramesAvailable(PaWasapiStream *stream, UINT32 *available);
-static void *PaWasapi_ReallocateMemory(void *ptr, size_t size);
+static void *PaWasapi_ReallocateMemory(void *prev, size_t size);
 static void PaWasapi_FreeMemory(void *ptr);
 
 // Local statics
@@ -629,6 +635,34 @@ static HRESULT __LogHostError(HRESULT res, const char *func, const char *file, i
        case AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED          :text ="AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED"; break;
        case AUDCLNT_E_INVALID_DEVICE_PERIOD            :text ="AUDCLNT_E_INVALID_DEVICE_PERIOD"; break;
 
+#ifdef AUDCLNT_E_INVALID_STREAM_FLAG
+       case AUDCLNT_E_INVALID_STREAM_FLAG                      :text ="AUDCLNT_E_INVALID_STREAM_FLAG"; break;
+#endif
+#ifdef AUDCLNT_E_ENDPOINT_OFFLOAD_NOT_CAPABLE
+       case AUDCLNT_E_ENDPOINT_OFFLOAD_NOT_CAPABLE     :text ="AUDCLNT_E_ENDPOINT_OFFLOAD_NOT_CAPABLE"; break;
+#endif
+#ifdef AUDCLNT_E_OUT_OF_OFFLOAD_RESOURCES
+       case AUDCLNT_E_OUT_OF_OFFLOAD_RESOURCES         :text ="AUDCLNT_E_OUT_OF_OFFLOAD_RESOURCES"; break;
+#endif
+#ifdef AUDCLNT_E_OFFLOAD_MODE_ONLY
+       case AUDCLNT_E_OFFLOAD_MODE_ONLY                        :text ="AUDCLNT_E_OFFLOAD_MODE_ONLY"; break;
+#endif
+#ifdef AUDCLNT_E_NONOFFLOAD_MODE_ONLY
+       case AUDCLNT_E_NONOFFLOAD_MODE_ONLY                     :text ="AUDCLNT_E_NONOFFLOAD_MODE_ONLY"; break;
+#endif
+#ifdef AUDCLNT_E_RESOURCES_INVALIDATED
+       case AUDCLNT_E_RESOURCES_INVALIDATED            :text ="AUDCLNT_E_RESOURCES_INVALIDATED"; break;
+#endif
+#ifdef AUDCLNT_E_RAW_MODE_UNSUPPORTED
+       case AUDCLNT_E_RAW_MODE_UNSUPPORTED                     :text ="AUDCLNT_E_RAW_MODE_UNSUPPORTED"; break;
+#endif
+#ifdef AUDCLNT_E_ENGINE_PERIODICITY_LOCKED
+       case AUDCLNT_E_ENGINE_PERIODICITY_LOCKED        :text ="AUDCLNT_E_ENGINE_PERIODICITY_LOCKED"; break;
+#endif
+#ifdef AUDCLNT_E_ENGINE_FORMAT_LOCKED
+       case AUDCLNT_E_ENGINE_FORMAT_LOCKED                     :text ="AUDCLNT_E_ENGINE_FORMAT_LOCKED"; break;
+#endif
+
        case AUDCLNT_S_BUFFER_EMPTY                 :text ="AUDCLNT_S_BUFFER_EMPTY"; break;
        case AUDCLNT_S_THREAD_ALREADY_REGISTERED    :text ="AUDCLNT_S_THREAD_ALREADY_REGISTERED"; break;
        case AUDCLNT_S_POSITION_STALLED                         :text ="AUDCLNT_S_POSITION_STALLED"; break;
@@ -746,12 +780,14 @@ static REFERENCE_TIME MakeHnsPeriod(UINT32 nFrames, DWORD nSamplesPerSec)
 
 // ------------------------------------------------------------------------------------------
 // Converts PaSampleFormat to bits per sample value
+// Note: paCustomFormat stands for 8.24 format (24-bits inside 32-bit containers)
 static WORD PaSampleFormatToBitsPerSample(PaSampleFormat format_id)
 {
        switch (format_id & ~paNonInterleaved)
        {
                case paFloat32:
                case paInt32: return 32;
+               case paCustomFormat:
                case paInt24: return 24;
                case paInt16: return 16;
                case paInt8:
@@ -761,11 +797,14 @@ static WORD PaSampleFormatToBitsPerSample(PaSampleFormat format_id)
 }
 
 // ------------------------------------------------------------------------------------------
-// Converts PaSampleFormat to bits per sample value
-/*static WORD PaSampleFormatToBytesPerSample(PaSampleFormat format_id)
+// Convert PaSampleFormat to valid sample format for I/O, e.g. if paCustomFormat is specified
+// it will be converted to paInt32, other formats pass through
+// Note: paCustomFormat stands for 8.24 format (24-bits inside 32-bit containers)
+static PaSampleFormat GetSampleFormatForIO(PaSampleFormat format_id)
 {
-       return PaSampleFormatToBitsPerSample(format_id) >> 3; // 'bits/8'
-}*/
+       return ((format_id & ~paNonInterleaved) == paCustomFormat ? 
+               (paInt32 | (format_id & paNonInterleaved ? paNonInterleaved : 0)) : format_id);
+}
 
 // ------------------------------------------------------------------------------------------
 // Converts Hns period into number of frames
@@ -843,29 +882,39 @@ static UINT32 AlignFramesPerBuffer(UINT32 nFrames, UINT32 nSamplesPerSec, UINT32
 }
 
 // ------------------------------------------------------------------------------------------
-static UINT32 GetFramesSleepTime(UINT32 nFrames, UINT32 nSamplesPerSec)
+static UINT32 GetFramesSleepTime(REFERENCE_TIME nFrames, REFERENCE_TIME nSamplesPerSec)
 {
        REFERENCE_TIME nDuration;
        if (nSamplesPerSec == 0)
                return 0;
-#define REFTIMES_PER_SEC  10000000
-#define REFTIMES_PER_MILLISEC  10000
+
+#define REFTIMES_PER_SEC       10000000LL
+#define REFTIMES_PER_MILLISEC  10000LL
+
        // Calculate the actual duration of the allocated buffer.
-       nDuration = (REFERENCE_TIME)((double)REFTIMES_PER_SEC * nFrames / nSamplesPerSec);
-       return (UINT32)(nDuration/REFTIMES_PER_MILLISEC/2);
+       nDuration = (REFTIMES_PER_SEC * nFrames) / nSamplesPerSec;
+       return (UINT32)(nDuration / REFTIMES_PER_MILLISEC);
+
+#undef REFTIMES_PER_SEC
+#undef REFTIMES_PER_MILLISEC
 }
 
 // ------------------------------------------------------------------------------------------
-static UINT32 GetFramesSleepTimeMicroseconds(UINT32 nFrames, UINT32 nSamplesPerSec)
+static UINT32 GetFramesSleepTimeMicroseconds(REFERENCE_TIME nFrames, REFERENCE_TIME nSamplesPerSec)
 {
        REFERENCE_TIME nDuration;
        if (nSamplesPerSec == 0)
                return 0;
-#define REFTIMES_PER_SEC  10000000
-#define REFTIMES_PER_MILLISEC  10000
+
+#define REFTIMES_PER_SEC       10000000LL
+#define REFTIMES_PER_MILLISEC  10000LL
+
        // Calculate the actual duration of the allocated buffer.
-       nDuration = (REFERENCE_TIME)((double)REFTIMES_PER_SEC * nFrames / nSamplesPerSec);
-       return (UINT32)(nDuration/10/2);
+       nDuration = (REFTIMES_PER_SEC * nFrames) / nSamplesPerSec;
+       return (UINT32)(nDuration / 10);
+
+#undef REFTIMES_PER_SEC
+#undef REFTIMES_PER_MILLISEC
 }
 
 // ------------------------------------------------------------------------------------------
@@ -1148,25 +1197,30 @@ static const IID *GetAudioClientIID()
 }
 
 // ------------------------------------------------------------------------------------------
-typedef enum EMixerDir { MIX_DIR__1TO2, MIX_DIR__2TO1, MIX_DIR__2TO1_L } EMixerDir;
+typedef enum EMixDirection
+{ 
+       MIX_DIR__1TO2,   //!< mix one channel to L and R
+       MIX_DIR__2TO1,   //!< mix L and R channels to one channel
+       MIX_DIR__2TO1_L  //!< mix only L channel (of total 2 channels) to one channel
+} 
+EMixDirection;
 
 // ------------------------------------------------------------------------------------------
 #define _WASAPI_MONO_TO_STEREO_MIXER_1_TO_2(TYPE)\
-       TYPE * __restrict to   = __to;\
-       TYPE * __restrict from = __from;\
-       TYPE * __restrict end  = from + count;\
+       TYPE * __restrict to = (TYPE *)__to;\
+       const TYPE * __restrict from = (const TYPE *)__from;\
+       const TYPE * __restrict end = from + count;\
        while (from != end)\
        {\
-               *to ++ = *from;\
-               *to ++ = *from;\
-               ++ from;\
+               to[0] = to[1] = *from ++;\
+               to += 2;\
        }
 
 // ------------------------------------------------------------------------------------------
 #define _WASAPI_MONO_TO_STEREO_MIXER_2_TO_1_FLT32(TYPE)\
-       TYPE * __restrict to   = (TYPE *)__to;\
-       TYPE * __restrict from = (TYPE *)__from;\
-       TYPE * __restrict end  = to + count;\
+       TYPE * __restrict to = (TYPE *)__to;\
+       const TYPE * __restrict from = (const TYPE *)__from;\
+       const TYPE * __restrict end = to + count;\
        while (to != end)\
        {\
                *to ++ = (TYPE)((float)(from[0] + from[1]) * 0.5f);\
@@ -1175,9 +1229,9 @@ typedef enum EMixerDir { MIX_DIR__1TO2, MIX_DIR__2TO1, MIX_DIR__2TO1_L } EMixerD
 
 // ------------------------------------------------------------------------------------------
 #define _WASAPI_MONO_TO_STEREO_MIXER_2_TO_1_INT32(TYPE)\
-       TYPE * __restrict to   = (TYPE *)__to;\
-       TYPE * __restrict from = (TYPE *)__from;\
-       TYPE * __restrict end  = to + count;\
+       TYPE * __restrict to = (TYPE *)__to;\
+       const TYPE * __restrict from = (const TYPE *)__from;\
+       const TYPE * __restrict end = to + count;\
        while (to != end)\
        {\
                *to ++ = (TYPE)(((INT32)from[0] + (INT32)from[1]) >> 1);\
@@ -1186,9 +1240,9 @@ typedef enum EMixerDir { MIX_DIR__1TO2, MIX_DIR__2TO1, MIX_DIR__2TO1_L } EMixerD
 
 // ------------------------------------------------------------------------------------------
 #define _WASAPI_MONO_TO_STEREO_MIXER_2_TO_1_INT64(TYPE)\
-       TYPE * __restrict to   = (TYPE *)__to;\
-       TYPE * __restrict from = (TYPE *)__from;\
-       TYPE * __restrict end  = to + count;\
+       TYPE * __restrict to = (TYPE *)__to;\
+       const TYPE * __restrict from = (const TYPE *)__from;\
+       const TYPE * __restrict end = to + count;\
        while (to != end)\
        {\
                *to ++ = (TYPE)(((INT64)from[0] + (INT64)from[1]) >> 1);\
@@ -1197,9 +1251,9 @@ typedef enum EMixerDir { MIX_DIR__1TO2, MIX_DIR__2TO1, MIX_DIR__2TO1_L } EMixerD
 
 // ------------------------------------------------------------------------------------------
 #define _WASAPI_MONO_TO_STEREO_MIXER_2_TO_1_L(TYPE)\
-       TYPE * __restrict to   = (TYPE *)__to;\
-       TYPE * __restrict from = (TYPE *)__from;\
-       TYPE * __restrict end  = to + count;\
+       TYPE * __restrict to = (TYPE *)__to;\
+       const TYPE * __restrict from = (const TYPE *)__from;\
+       const TYPE * __restrict end = to + count;\
        while (to != end)\
        {\
                *to ++ = from[0];\
@@ -1207,28 +1261,28 @@ typedef enum EMixerDir { MIX_DIR__1TO2, MIX_DIR__2TO1, MIX_DIR__2TO1_L } EMixerD
        }
 
 // ------------------------------------------------------------------------------------------
-static void _MixMonoToStereo_1TO2_8(void *__to, void *__from, UINT32 count) { _WASAPI_MONO_TO_STEREO_MIXER_1_TO_2(BYTE); }
-static void _MixMonoToStereo_1TO2_16(void *__to, void *__from, UINT32 count) { _WASAPI_MONO_TO_STEREO_MIXER_1_TO_2(short); }
-static void _MixMonoToStereo_1TO2_24(void *__to, void *__from, UINT32 count) { _WASAPI_MONO_TO_STEREO_MIXER_1_TO_2(int); /* !!! int24 data is contained in 32-bit containers*/ }
-static void _MixMonoToStereo_1TO2_32(void *__to, void *__from, UINT32 count) { _WASAPI_MONO_TO_STEREO_MIXER_1_TO_2(int); }
-static void _MixMonoToStereo_1TO2_32f(void *__to, void *__from, UINT32 count) { _WASAPI_MONO_TO_STEREO_MIXER_1_TO_2(float); }
+static void _MixMonoToStereo_1TO2_8(void *__to, const void *__from, UINT32 count) { _WASAPI_MONO_TO_STEREO_MIXER_1_TO_2(BYTE); }
+static void _MixMonoToStereo_1TO2_16(void *__to, const void *__from, UINT32 count) { _WASAPI_MONO_TO_STEREO_MIXER_1_TO_2(short); }
+static void _MixMonoToStereo_1TO2_24(void *__to, const void *__from, UINT32 count) { _WASAPI_MONO_TO_STEREO_MIXER_1_TO_2(int); /* !!! int24 data is contained in 32-bit containers*/ }
+static void _MixMonoToStereo_1TO2_32(void *__to, const void *__from, UINT32 count) { _WASAPI_MONO_TO_STEREO_MIXER_1_TO_2(int); }
+static void _MixMonoToStereo_1TO2_32f(void *__to, const void *__from, UINT32 count) { _WASAPI_MONO_TO_STEREO_MIXER_1_TO_2(float); }
 
 // ------------------------------------------------------------------------------------------
-static void _MixMonoToStereo_2TO1_8(void *__to, void *__from, UINT32 count) { _WASAPI_MONO_TO_STEREO_MIXER_2_TO_1_INT32(BYTE); }
-static void _MixMonoToStereo_2TO1_16(void *__to, void *__from, UINT32 count) { _WASAPI_MONO_TO_STEREO_MIXER_2_TO_1_INT32(short); }
-static void _MixMonoToStereo_2TO1_24(void *__to, void *__from, UINT32 count) { _WASAPI_MONO_TO_STEREO_MIXER_2_TO_1_INT32(int); /* !!! int24 data is contained in 32-bit containers*/ }
-static void _MixMonoToStereo_2TO1_32(void *__to, void *__from, UINT32 count) { _WASAPI_MONO_TO_STEREO_MIXER_2_TO_1_INT64(int); }
-static void _MixMonoToStereo_2TO1_32f(void *__to, void *__from, UINT32 count) { _WASAPI_MONO_TO_STEREO_MIXER_2_TO_1_FLT32(float); }
+static void _MixMonoToStereo_2TO1_8(void *__to, const void *__from, UINT32 count) { _WASAPI_MONO_TO_STEREO_MIXER_2_TO_1_INT32(BYTE); }
+static void _MixMonoToStereo_2TO1_16(void *__to, const void *__from, UINT32 count) { _WASAPI_MONO_TO_STEREO_MIXER_2_TO_1_INT32(short); }
+static void _MixMonoToStereo_2TO1_24(void *__to, const void *__from, UINT32 count) { _WASAPI_MONO_TO_STEREO_MIXER_2_TO_1_INT32(int); /* !!! int24 data is contained in 32-bit containers*/ }
+static void _MixMonoToStereo_2TO1_32(void *__to, const void *__from, UINT32 count) { _WASAPI_MONO_TO_STEREO_MIXER_2_TO_1_INT64(int); }
+static void _MixMonoToStereo_2TO1_32f(void *__to, const void *__from, UINT32 count) { _WASAPI_MONO_TO_STEREO_MIXER_2_TO_1_FLT32(float); }
 
 // ------------------------------------------------------------------------------------------
-static void _MixMonoToStereo_2TO1_8_L(void *__to, void *__from, UINT32 count) { _WASAPI_MONO_TO_STEREO_MIXER_2_TO_1_L(BYTE); }
-static void _MixMonoToStereo_2TO1_16_L(void *__to, void *__from, UINT32 count) { _WASAPI_MONO_TO_STEREO_MIXER_2_TO_1_L(short); }
-static void _MixMonoToStereo_2TO1_24_L(void *__to, void *__from, UINT32 count) { _WASAPI_MONO_TO_STEREO_MIXER_2_TO_1_L(int); /* !!! int24 data is contained in 32-bit containers*/ }
-static void _MixMonoToStereo_2TO1_32_L(void *__to, void *__from, UINT32 count) { _WASAPI_MONO_TO_STEREO_MIXER_2_TO_1_L(int); }
-static void _MixMonoToStereo_2TO1_32f_L(void *__to, void *__from, UINT32 count) { _WASAPI_MONO_TO_STEREO_MIXER_2_TO_1_L(float); }
+static void _MixMonoToStereo_2TO1_8_L(void *__to, const void *__from, UINT32 count) { _WASAPI_MONO_TO_STEREO_MIXER_2_TO_1_L(BYTE); }
+static void _MixMonoToStereo_2TO1_16_L(void *__to, const void *__from, UINT32 count) { _WASAPI_MONO_TO_STEREO_MIXER_2_TO_1_L(short); }
+static void _MixMonoToStereo_2TO1_24_L(void *__to, const void *__from, UINT32 count) { _WASAPI_MONO_TO_STEREO_MIXER_2_TO_1_L(int); /* !!! int24 data is contained in 32-bit containers*/ }
+static void _MixMonoToStereo_2TO1_32_L(void *__to, const void *__from, UINT32 count) { _WASAPI_MONO_TO_STEREO_MIXER_2_TO_1_L(int); }
+static void _MixMonoToStereo_2TO1_32f_L(void *__to, const void *__from, UINT32 count) { _WASAPI_MONO_TO_STEREO_MIXER_2_TO_1_L(float); }
 
 // ------------------------------------------------------------------------------------------
-static MixMonoToStereoF _GetMonoToStereoMixer(PaSampleFormat format, EMixerDir dir)
+static MixMonoToStereoF _GetMonoToStereoMixer(PaSampleFormat format, EMixDirection dir)
 {
        switch (dir)
        {
@@ -1390,16 +1444,23 @@ static HRESULT ActivateAudioInterface_WINRT(EDataFlow flow, const IID *iid, void
        IActivateAudioInterfaceAsyncOperation *asyncOp = NULL;
        IActivateAudioInterfaceCompletionHandler *handler = CreateActivateAudioInterfaceCompletionHandler(iid, obj);
        PaActivateAudioInterfaceCompletionHandler *handlerImpl = (PaActivateAudioInterfaceCompletionHandler *)handler;
-       OLECHAR devicePath[PA_WASAPI_DEVICE_PATH_LEN] = { 0 };
+       OLECHAR tmp[PA_WASAPI_DEVICE_PATH_LEN] = { 0 };
+       OLECHAR *devicePath = tmp;
 
        // Get device path in form L"{DEVICE_GUID}"
        switch (flow)
        {
        case eRender:
-               StringFromGUID2(&DEVINTERFACE_AUDIO_RENDER, devicePath, PA_WASAPI_DEVICE_PATH_LEN - 1);
+               if (g_DefaultRenderId[0] != 0)
+                       devicePath = g_DefaultRenderId;
+               else
+                       StringFromGUID2(&DEVINTERFACE_AUDIO_RENDER, devicePath, PA_WASAPI_DEVICE_PATH_LEN - 1);
                break;
        case eCapture:
-               StringFromGUID2(&DEVINTERFACE_AUDIO_CAPTURE, devicePath, PA_WASAPI_DEVICE_PATH_LEN - 1);
+               if (g_DefaultCaptureId[0] != 0)
+                       devicePath = g_DefaultCaptureId;
+               else
+                       StringFromGUID2(&DEVINTERFACE_AUDIO_CAPTURE, devicePath, PA_WASAPI_DEVICE_PATH_LEN - 1);
                break;
        default:
                return S_FALSE;
@@ -1758,7 +1819,7 @@ static PaError CreateDeviceList(PaWasapiHostApiRepresentation *paWasapi, PaHostA
                                        paWasapi->devInfo[i].state = DEVICE_STATE_ACTIVE;
 
                                        // Default format
-                                       memcpy(&paWasapi->devInfo[i].DefaultFormat, mixFormat, min(sizeof(paWasapi->devInfo[i].DefaultFormat), sizeof(*mixFormat)));
+                                       memcpy(&paWasapi->devInfo[i].DefaultFormat, mixFormat, min(sizeof(paWasapi->devInfo[i].DefaultFormat), (sizeof(*mixFormat) + mixFormat->cbSize)));
                                        CoTaskMemFree(mixFormat);
 
                                        // Form-factor
@@ -2049,6 +2110,24 @@ PaError PaWasapi_UpdateDeviceList()
 }
 #endif
 
+// ------------------------------------------------------------------------------------------
+int PaWasapi_GetDeviceCurrentFormat( PaStream *pStream, void *pFormat, unsigned int nFormatSize, int bOutput )
+{
+       UINT32 size;
+       WAVEFORMATEXTENSIBLE *format;
+
+       PaWasapiStream *stream = (PaWasapiStream *)pStream;
+       if (stream == NULL)
+               return paBadStreamPtr;
+       
+       format = (bOutput == TRUE ? &stream->out.wavex : &stream->in.wavex);
+
+       size = min(nFormatSize, (UINT32)sizeof(*format));
+       memcpy(pFormat, format, size);
+
+       return size;
+}
+
 // ------------------------------------------------------------------------------------------
 int PaWasapi_GetDeviceDefaultFormat( void *pFormat, unsigned int nFormatSize, PaDeviceIndex nDevice )
 {
@@ -2075,7 +2154,7 @@ int PaWasapi_GetDeviceDefaultFormat( void *pFormat, unsigned int nFormatSize, Pa
        // Validate index
        if ((UINT32)index >= paWasapi->deviceCount)
                return paInvalidDevice;
-
+       
        size = min(nFormatSize, (UINT32)sizeof(paWasapi->devInfo[ index ].DefaultFormat));
        memcpy(pFormat, &paWasapi->devInfo[ index ].DefaultFormat, size);
 
@@ -2219,37 +2298,45 @@ static PaSampleFormat WaveToPaFormat(const WAVEFORMATEXTENSIBLE *in)
 
 // ------------------------------------------------------------------------------------------
 static PaError MakeWaveFormatFromParams(WAVEFORMATEXTENSIBLE *wavex, const PaStreamParameters *params,
-                                                                       double sampleRate)
+    double sampleRate, BOOL packedOnly)
 {
        WORD bitsPerSample;
        WAVEFORMATEX *old;
        DWORD channelMask = 0;
+       BOOL useExtensible = (params->channelCount > 2); // format is always forced for >2 channels format
        PaWasapiStreamInfo *streamInfo = (PaWasapiStreamInfo *)params->hostApiSpecificStreamInfo;
 
-       // Get user assigned channel mask
-       if ((streamInfo != NULL) && (streamInfo->flags & paWinWasapiUseChannelMask))
-               channelMask = streamInfo->channelMask;
-
-       // Convert PaSampleFormat to bits per sample
+       // Convert PaSampleFormat to valid data bits
        if ((bitsPerSample = PaSampleFormatToBitsPerSample(params->sampleFormat)) == 0)
                return paSampleFormatNotSupported;
 
+       // Use user assigned channel mask
+       if ((streamInfo != NULL) && (streamInfo->flags & paWinWasapiUseChannelMask))
+       {
+               channelMask   = streamInfo->channelMask;
+               useExtensible = TRUE;
+       }
+
     memset(wavex, 0, sizeof(*wavex));
 
-    old                                         = (WAVEFORMATEX *)wavex;
-    old->nChannels       = (WORD)params->channelCount;
-    old->nSamplesPerSec  = (DWORD)sampleRate;
-       if ((old->wBitsPerSample = bitsPerSample) > 16)
+    old                                        = (WAVEFORMATEX *)wavex;
+    old->nChannels      = (WORD)params->channelCount;
+    old->nSamplesPerSec = (DWORD)sampleRate;
+       old->wBitsPerSample = bitsPerSample;
+
+       // according to MSDN for WAVEFORMATEX structure for WAVE_FORMAT_PCM:
+       // "If wFormatTag is WAVE_FORMAT_PCM, then wBitsPerSample should be equal to 8 or 16."
+       if ((bitsPerSample != 8) && (bitsPerSample != 16))
        {
-               old->wBitsPerSample = 32; // 20 or 24 bits must go in 32 bit containers (ints)
+               // Normally 20 or 24 bits must go in 32 bit containers (ints) but in Exclusive mode some devices require
+               // packed version of the format, e.g. for example 24-bit in 3-bytes
+               old->wBitsPerSample = (packedOnly ? bitsPerSample : 32);
+               useExtensible       = TRUE;
        }
-    old->nBlockAlign     = (old->nChannels * (old->wBitsPerSample/8));
-    old->nAvgBytesPerSec = (old->nSamplesPerSec * old->nBlockAlign);
 
     // WAVEFORMATEX
-    if ((params->channelCount <= 2) && ((bitsPerSample == 16) || (bitsPerSample == 8)))
+    if (!useExtensible)
        {
-        old->cbSize            = 0;
         old->wFormatTag        = WAVE_FORMAT_PCM;
     }
     // WAVEFORMATEXTENSIBLE
@@ -2263,7 +2350,7 @@ static PaError MakeWaveFormatFromParams(WAVEFORMATEXTENSIBLE *wavex, const PaStr
         else
             wavex->SubFormat = pa_KSDATAFORMAT_SUBTYPE_PCM;
 
-        wavex->Samples.wValidBitsPerSample = bitsPerSample; //no extra padding!
+        wavex->Samples.wValidBitsPerSample = bitsPerSample;
 
                // Set channel mask
                if (channelMask != 0)
@@ -2299,67 +2386,83 @@ static PaError MakeWaveFormatFromParams(WAVEFORMATEXTENSIBLE *wavex, const PaStr
                        }
                }
        }
+
+    old->nBlockAlign     = old->nChannels * (old->wBitsPerSample / 8);
+    old->nAvgBytesPerSec = old->nSamplesPerSec * old->nBlockAlign;
+
     return paNoError;
 }
 
 // ------------------------------------------------------------------------------------------
-/*static void wasapiFillWFEXT( WAVEFORMATEXTENSIBLE* pwfext, PaSampleFormat sampleFormat, double sampleRate, int channelCount)
+static HRESULT GetAlternativeSampleFormatExclusive(IAudioClient *client, double sampleRate, 
+       const PaStreamParameters *params, WAVEFORMATEXTENSIBLE *outWavex, BOOL packedSampleFormatOnly)
 {
-    PA_DEBUG(( "sampleFormat = %lx\n" , sampleFormat ));
-    PA_DEBUG(( "sampleRate = %f\n" , sampleRate ));
-    PA_DEBUG(( "chanelCount = %d\n", channelCount ));
-
-    pwfext->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
-    pwfext->Format.nChannels = (WORD)channelCount;
-    pwfext->Format.nSamplesPerSec = (DWORD)sampleRate;
-    if(channelCount == 1)
-        pwfext->dwChannelMask = PAWIN_SPEAKER_DIRECTOUT;
-    else
-        pwfext->dwChannelMask = PAWIN_SPEAKER_STEREO;
-    if(sampleFormat == paFloat32)
-    {
-        pwfext->Format.nBlockAlign = (WORD)(channelCount * 4);
-        pwfext->Format.wBitsPerSample = 32;
-        pwfext->Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX);
-        pwfext->Samples.wValidBitsPerSample = 32;
-        pwfext->SubFormat = pa_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
-    }
-    else if(sampleFormat == paInt32)
-    {
-        pwfext->Format.nBlockAlign = (WORD)(channelCount * 4);
-        pwfext->Format.wBitsPerSample = 32;
-        pwfext->Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX);
-        pwfext->Samples.wValidBitsPerSample = 32;
-        pwfext->SubFormat = pa_KSDATAFORMAT_SUBTYPE_PCM;
-    }
-    else if(sampleFormat == paInt24)
-    {
-        pwfext->Format.nBlockAlign = (WORD)(channelCount * 4);
-        pwfext->Format.wBitsPerSample = 32; // 24-bit in 32-bit int container
-        pwfext->Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX);
-        pwfext->Samples.wValidBitsPerSample = 24;
-        pwfext->SubFormat = pa_KSDATAFORMAT_SUBTYPE_PCM;
-    }
-    else if(sampleFormat == paInt16)
-    {
-        pwfext->Format.nBlockAlign = (WORD)(channelCount * 2);
-        pwfext->Format.wBitsPerSample = 16;
-        pwfext->Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX);
-        pwfext->Samples.wValidBitsPerSample = 16;
-        pwfext->SubFormat = pa_KSDATAFORMAT_SUBTYPE_PCM;
-    }
-    pwfext->Format.nAvgBytesPerSec = pwfext->Format.nSamplesPerSec * pwfext->Format.nBlockAlign;
-}*/
+       HRESULT hr = !S_OK;
+       AUDCLNT_SHAREMODE shareMode = AUDCLNT_SHAREMODE_EXCLUSIVE;
+       WAVEFORMATEXTENSIBLE testFormat;
+       PaStreamParameters testParams;
+       int i;
+       static const PaSampleFormat bestToWorst[] = { paInt32, paInt24, paFloat32, paInt16 };
+
+       // Try combination Stereo (2 channels) and then we will use our custom mono-stereo mixer
+       if (params->channelCount == 1)
+       {
+               testParams = (*params);
+               testParams.channelCount = 2;
+
+               if (MakeWaveFormatFromParams(&testFormat, &testParams, sampleRate, packedSampleFormatOnly) == paNoError)
+               {
+                       if ((hr = IAudioClient_IsFormatSupported(client, shareMode, &testFormat.Format, NULL)) == S_OK)
+                       {
+                               (*outWavex) = testFormat;
+                               return hr;
+                       }
+               }
+
+               // Try selecting suitable sample type
+               for (i = 0; i < STATIC_ARRAY_SIZE(bestToWorst); ++i)
+               {
+                       testParams.sampleFormat = bestToWorst[i];
+
+                       if (MakeWaveFormatFromParams(&testFormat, &testParams, sampleRate, packedSampleFormatOnly) == paNoError)
+                       {
+                               if ((hr = IAudioClient_IsFormatSupported(client, shareMode, &testFormat.Format, NULL)) == S_OK)
+                               {
+                                       (*outWavex) = testFormat;
+                                       return hr;
+                               }
+                       }
+               }
+       }
+
+       // Try selecting suitable sample type
+       testParams = (*params);
+       for (i = 0; i < STATIC_ARRAY_SIZE(bestToWorst); ++i)
+       {
+               testParams.sampleFormat = bestToWorst[i];
+
+               if (MakeWaveFormatFromParams(&testFormat, &testParams, sampleRate, packedSampleFormatOnly) == paNoError)
+               {
+                       if ((hr = IAudioClient_IsFormatSupported(client, shareMode, &testFormat.Format, NULL)) == S_OK)
+                       {
+                               (*outWavex) = testFormat;
+                               return hr;
+                       }
+               }
+       }
+       
+       return hr;
+}
 
 // ------------------------------------------------------------------------------------------
-static PaError GetClosestFormat(IAudioClient *myClient, double sampleRate,
-       const PaStreamParameters *_params, AUDCLNT_SHAREMODE shareMode, WAVEFORMATEXTENSIBLE *outWavex,
-       BOOL output)
+static PaError GetClosestFormat(IAudioClient *client, double sampleRate, const PaStreamParameters *_params, 
+       AUDCLNT_SHAREMODE shareMode, WAVEFORMATEXTENSIBLE *outWavex, BOOL output)
 {
-       PaError answer                   = paInvalidSampleRate;
+       PaWasapiStreamInfo *streamInfo   = (PaWasapiStreamInfo *)_params->hostApiSpecificStreamInfo;
        WAVEFORMATEX *sharedClosestMatch = NULL;
        HRESULT hr                       = !S_OK;
-       PaStreamParameters params       = (*_params);
+       PaStreamParameters params        = (*_params);
+       const BOOL explicitFormat        = ((streamInfo->flags & paWinWasapiExplicitSampleFormat) == paWinWasapiExplicitSampleFormat);
        (void)output;
 
        /* It was not noticed that 24-bit Input producing no output while device accepts this format.
@@ -2370,30 +2473,34 @@ static PaError GetClosestFormat(IAudioClient *myClient, double sampleRate,
        /*if ((params.sampleFormat == paInt24) && (output == FALSE))
                params.sampleFormat = paFloat32;*/ // <<< The silence was due to missing Int32_To_Int24_Dither implementation
 
-    MakeWaveFormatFromParams(outWavex, &params, sampleRate);
+       // Try standard approach, e.g. if data is > 16 bits it will be packed into 32-bit containers
+    MakeWaveFormatFromParams(outWavex, &params, sampleRate, FALSE);
+       hr = IAudioClient_IsFormatSupported(client, shareMode, &outWavex->Format, (shareMode == AUDCLNT_SHAREMODE_SHARED ? &sharedClosestMatch : NULL));
+       
+       // Exclusive mode can require packed format for some devices
+       if ((hr != S_OK) && (shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE))
+       {
+               // Enforce packed only format, e.g. data bits will not be packed into 32-bit containers in any case
+           MakeWaveFormatFromParams(outWavex, &params, sampleRate, TRUE);
+               hr = IAudioClient_IsFormatSupported(client, shareMode, &outWavex->Format, NULL);        
+       }
 
-       hr = IAudioClient_IsFormatSupported(myClient, shareMode, &outWavex->Format, (shareMode == AUDCLNT_SHAREMODE_SHARED ? &sharedClosestMatch : NULL));
        if (hr == S_OK)
-               answer = paFormatIsSupported;
-    else
-       if (sharedClosestMatch)
+       {
+               return paFormatIsSupported;
+       }
+       else
+       if (sharedClosestMatch != NULL)
        {
                WORD bitsPerSample;
-        WAVEFORMATEXTENSIBLE *ext = (WAVEFORMATEXTENSIBLE*)sharedClosestMatch;
 
-               GUID subf_guid = GUID_NULL;
                if (sharedClosestMatch->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
-               {
                        memcpy(outWavex, sharedClosestMatch, sizeof(WAVEFORMATEXTENSIBLE));
-                       subf_guid = ext->SubFormat;
-               }
                else
                        memcpy(outWavex, sharedClosestMatch, sizeof(WAVEFORMATEX));
 
-        CoTaskMemFree(sharedClosestMatch);
-
-               // Make supported by default
-               answer = paFormatIsSupported;
+               CoTaskMemFree(sharedClosestMatch);
+               sharedClosestMatch = NULL;
 
                // Validate SampleRate
                if ((DWORD)sampleRate != outWavex->Format.nSamplesPerSec)
@@ -2415,86 +2522,30 @@ static PaError GetClosestFormat(IAudioClient *myClient, double sampleRate,
                if ((bitsPerSample = PaSampleFormatToBitsPerSample(params.sampleFormat)) == 0)
                        return paSampleFormatNotSupported;
 
-               // Validate Sample format: bit size (WASAPI does not limit 'bit size')
-               //if (bitsPerSample != outWavex->Format.wBitsPerSample)
-               //      return paSampleFormatNotSupported;
-
-               // Validate Sample format: paFloat32 (WASAPI does not limit 'bit type')
-               //if ((params->sampleFormat == paFloat32) && (subf_guid != KSDATAFORMAT_SUBTYPE_IEEE_FLOAT))
-               //      return paSampleFormatNotSupported;
-
-               // Validate Sample format: paInt32 (WASAPI does not limit 'bit type')
-               //if ((params->sampleFormat == paInt32) && (subf_guid != KSDATAFORMAT_SUBTYPE_PCM))
-               //      return paSampleFormatNotSupported;
+               // Accepted format
+               return paFormatIsSupported;
        }
        else
+       if ((shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE) && !explicitFormat)
        {
-               static const int BestToWorst[] = { paFloat32, paInt24, paInt16 };
-               int i;
-
-               // Try combination stereo and we will use built-in mono-stereo mixer then
-               if (params.channelCount == 1)
-               {
-                       WAVEFORMATEXTENSIBLE stereo = { 0 };
+               // Try standard approach, e.g. if data is > 16 bits it will be packed into 32-bit containers
+               if ((hr = GetAlternativeSampleFormatExclusive(client, sampleRate, &params, outWavex, FALSE)) == S_OK)
+                       return paFormatIsSupported;
 
-                       PaStreamParameters stereo_params = params;
-                       stereo_params.channelCount = 2;
+               // Enforce packed only format, e.g. data bits will not be packed into 32-bit containers in any case
+               if ((hr = GetAlternativeSampleFormatExclusive(client, sampleRate, &params, outWavex, TRUE)) == S_OK)
+                       return paFormatIsSupported;
 
-                       MakeWaveFormatFromParams(&stereo, &stereo_params, sampleRate);
-
-                       hr = IAudioClient_IsFormatSupported(myClient, shareMode, &stereo.Format, (shareMode == AUDCLNT_SHAREMODE_SHARED ? &sharedClosestMatch : NULL));
-                       if (hr == S_OK)
-                       {
-                               memcpy(outWavex, &stereo, sizeof(WAVEFORMATEXTENSIBLE));
-                               CoTaskMemFree(sharedClosestMatch);
-                               return (answer = paFormatIsSupported);
-                       }
-
-                       // Try selecting suitable sample type
-                       for (i = 0; i < STATIC_ARRAY_SIZE(BestToWorst); ++i)
-                       {
-                               WAVEFORMATEXTENSIBLE sample = { 0 };
-
-                               PaStreamParameters sample_params = stereo_params;
-                               sample_params.sampleFormat = BestToWorst[i];
-
-                               MakeWaveFormatFromParams(&sample, &sample_params, sampleRate);
-
-                               hr = IAudioClient_IsFormatSupported(myClient, shareMode, &sample.Format, (shareMode == AUDCLNT_SHAREMODE_SHARED ? &sharedClosestMatch : NULL));
-                               if (hr == S_OK)
-                               {
-                                       memcpy(outWavex, &sample, sizeof(WAVEFORMATEXTENSIBLE));
-                                       CoTaskMemFree(sharedClosestMatch);
-                                       return (answer = paFormatIsSupported);
-                               }
-                       }
-               }
-
-               // Try selecting suitable sample type
-               for (i = 0; i < STATIC_ARRAY_SIZE(BestToWorst); ++i)
-               {
-                       WAVEFORMATEXTENSIBLE spfmt = { 0 };
-
-                       PaStreamParameters spfmt_params = params;
-                       spfmt_params.sampleFormat = BestToWorst[i];
-
-                       MakeWaveFormatFromParams(&spfmt, &spfmt_params, sampleRate);
-
-                       hr = IAudioClient_IsFormatSupported(myClient, shareMode, &spfmt.Format, (shareMode == AUDCLNT_SHAREMODE_SHARED ? &sharedClosestMatch : NULL));
-                       if (hr == S_OK)
-                       {
-                               memcpy(outWavex, &spfmt, sizeof(WAVEFORMATEXTENSIBLE));
-                               CoTaskMemFree(sharedClosestMatch);
-                               answer = paFormatIsSupported;
-                               break;
-                       }
-               }
-
-               // Nothing helped
+               // Log failure
+               LogHostError(hr);
+       }
+       else
+       {
+               // Exclusive mode and requested strict format, WASAPI did not accept this sample format
                LogHostError(hr);
        }
 
-       return answer;
+       return paInvalidSampleRate;
 }
 
 // ------------------------------------------------------------------------------------------
@@ -2512,8 +2563,9 @@ static PaError IsStreamParamsValid(struct PaUtilHostApiRepresentation *hostApi,
     {
         /* all standard sample formats are supported by the buffer adapter,
             this implementation doesn't support any custom sample formats */
-               if (inputParameters->sampleFormat & paCustomFormat)
-            return paSampleFormatNotSupported;
+               // Note: paCustomFormat is now 8.24 (24-bits in 32-bit containers)
+               //if (inputParameters->sampleFormat & paCustomFormat)
+        //    return paSampleFormatNotSupported;
 
         /* unless alternate device specification is supported, reject the use of
             paUseHostApiSpecificDeviceSpecification */
@@ -2543,8 +2595,9 @@ static PaError IsStreamParamsValid(struct PaUtilHostApiRepresentation *hostApi,
     {
         /* all standard sample formats are supported by the buffer adapter,
             this implementation doesn't support any custom sample formats */
-        if (outputParameters->sampleFormat & paCustomFormat)
-            return paSampleFormatNotSupported;
+               // Note: paCustomFormat is now 8.24 (24-bits in 32-bit containers)
+        //if (outputParameters->sampleFormat & paCustomFormat)
+        //    return paSampleFormatNotSupported;
 
         /* unless alternate device specification is supported, reject the use of
             paUseHostApiSpecificDeviceSpecification */
@@ -2901,7 +2954,8 @@ static HRESULT CreateAudioClient(PaWasapiStream *pStream, PaWasapiSubStream *pSu
                hr = IAudioClient2_SetClientProperties((IAudioClient2 *)audioClient, (AudioClientProperties *)&audioProps);
                if (hr != S_OK)
                {
-                       PRINT(("WASAPI: IAudioClient2_SetClientProperties(Category = %d, Options = %d) failed with error = %08X\n", audioProps.eCategory, audioProps.Options, (UINT32)hr));
+                       PRINT(("WASAPI: IAudioClient2_SetClientProperties(IsOffload = %d, Category = %d, Options = %d) failed\n", audioProps.bIsOffload, audioProps.eCategory, audioProps.Options));
+                       LogHostError(hr);
                }
                else
                {
@@ -2918,6 +2972,56 @@ static HRESULT CreateAudioClient(PaWasapiStream *pStream, PaWasapiSubStream *pSu
                (pSub->shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE ? pSub->period : 0),
                &pSub->wavex.Format,
         NULL);
+       
+       // Check if buffer size is the one we requested in Exclusive mode, for UAC1 USB DACs WASAPI allocates internal
+       // buffer equal to 8 times of pSub->period that has to be corrected in order to match the requested latency
+       if ((hr == S_OK) && (pSub->shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE))
+       {
+               UINT32 maxBufferSize = 0;
+
+               hr = IAudioClient_GetBufferSize(audioClient, &maxBufferSize);
+               if (hr != S_OK)
+               {
+                       (*pa_error) = paInvalidDevice;
+                       LogHostError(hr);
+                       goto done;
+               }
+
+               // For Exclusive mode for UAC1 devices maxBufferSize will be framesPerLatency * 8 but check any difference
+               // to be able to guarantee the latency user requested and also resulted framesPerLatency may be bigger than
+               // 2 seconds that will cause audio client not operational (GetCurrentPadding() will return always 0)
+               if (maxBufferSize >= (framesPerLatency * 2))
+               {
+                       UINT32 ratio = maxBufferSize / framesPerLatency;
+
+                       PRINT(("WASAPI: CreateAudioClient: detected %d times larger buffer than requested, correct to match user latency\n", ratio));
+                       
+                       // Get new aligned frames lowered by calculated ratio
+                       framesPerLatency = MakeFramesFromHns(pSub->period / ratio, pSub->wavex.Format.nSamplesPerSec);
+                       _CalculateAlignedPeriod(pSub, &framesPerLatency, ALIGN_BWD);
+                       
+                       // Release previous client
+                       SAFE_RELEASE(audioClient);
+
+                       // Create a new audio client
+                       hr = ActivateAudioInterface(pInfo, &audioClient);
+               if (hr != S_OK)
+                       {
+                               (*pa_error) = paInsufficientMemory;
+                               LogHostError(hr);
+                               goto done;
+                       }
+
+                       // Open the stream and associate it with an audio session
+                       hr = IAudioClient_Initialize(audioClient,
+                               pSub->shareMode,
+                               pSub->streamFlags,
+                               pSub->period,
+                               (pSub->shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE ? pSub->period : 0),
+                               &pSub->wavex.Format,
+                               NULL);
+               }
+       }
 
        /*! WASAPI is tricky on large device buffer, sometimes 2000ms can be allocated sometimes
            less. There is no known guaranteed level thus we make subsequent tries by decreasing
@@ -3257,6 +3361,7 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
        ULONG framesPerHostCallback;
        PaUtilHostBufferSizeMode bufferMode;
        const BOOL fullDuplex = ((inputParameters != NULL) && (outputParameters != NULL));
+       BOOL useInputBufferProcessor = (inputParameters != NULL), useOutputBufferProcessor = (outputParameters != NULL);
 
        // validate PaStreamParameters
        if ((result = IsStreamParamsValid(hostApi, inputParameters, outputParameters, sampleRate)) != paNoError)
@@ -3303,7 +3408,7 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
        if (inputParameters != NULL)
     {
         inputChannelCount = inputParameters->channelCount;
-        inputSampleFormat = inputParameters->sampleFormat;
+        inputSampleFormat = GetSampleFormatForIO(inputParameters->sampleFormat);
         info              = &paWasapi->devInfo[inputParameters->device];
 
                // default Shared Mode
@@ -3336,6 +3441,9 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
                                        (inputStreamInfo->threadPriority <= eThreadPriorityWindowManager))
                                        stream->nThreadPriority = inputStreamInfo->threadPriority;
                        }
+
+                       // redirect processing to custom user callback, ignore PA buffer processor
+                       useInputBufferProcessor = !(inputStreamInfo->flags & paWinWasapiRedirectHostProcessor);
                }
 
                // Choose processing mode
@@ -3370,7 +3478,7 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
         }
 
                // Get closest format
-        hostInputSampleFormat = PaUtil_SelectClosestAvailableFormat( WaveToPaFormat(&stream->in.wavex), inputSampleFormat );
+        hostInputSampleFormat = PaUtil_SelectClosestAvailableFormat(WaveToPaFormat(&stream->in.wavex), inputSampleFormat);
 
         // Set user-side custom host processor
         if ((inputStreamInfo != NULL) &&
@@ -3430,7 +3538,7 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
     if (outputParameters != NULL)
     {
         outputChannelCount = outputParameters->channelCount;
-        outputSampleFormat = outputParameters->sampleFormat;
+        outputSampleFormat = GetSampleFormatForIO(outputParameters->sampleFormat);
                info               = &paWasapi->devInfo[outputParameters->device];
 
                // default Shared Mode
@@ -3463,6 +3571,9 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
                                        (outputStreamInfo->threadPriority <= eThreadPriorityWindowManager))
                                        stream->nThreadPriority = outputStreamInfo->threadPriority;
                        }
+
+                       // redirect processing to custom user callback, ignore PA buffer processor
+                       useOutputBufferProcessor = !(outputStreamInfo->flags & paWinWasapiRedirectHostProcessor);
                }
 
                // Choose processing mode
@@ -3497,7 +3608,7 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
         }
 
                // Get closest format
-        hostOutputSampleFormat = PaUtil_SelectClosestAvailableFormat( WaveToPaFormat(&stream->out.wavex), outputSampleFormat );
+        hostOutputSampleFormat = PaUtil_SelectClosestAvailableFormat(WaveToPaFormat(&stream->out.wavex), outputSampleFormat);
 
         // Set user-side custom host processor
         if ((outputStreamInfo != NULL) &&
@@ -3604,36 +3715,39 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
        stream->bufferMode = bufferMode;
 
     // Initialize buffer processor
-    result =  PaUtil_InitializeBufferProcessor(
-               &stream->bufferProcessor,
-        inputChannelCount,
-               inputSampleFormat,
-               hostInputSampleFormat,
-        outputChannelCount,
-               outputSampleFormat,
-               hostOutputSampleFormat,
-        sampleRate,
-               streamFlags,
-               framesPerBuffer,
-        framesPerHostCallback,
-               bufferMode,
-        streamCallback,
-               userData);
-    if (result != paNoError)
+       if (useInputBufferProcessor || useOutputBufferProcessor)
        {
-               LogPaError(result);
-        goto error;
+               result =  PaUtil_InitializeBufferProcessor(
+                       &stream->bufferProcessor,
+                       inputChannelCount,
+                       inputSampleFormat,
+                       hostInputSampleFormat,
+                       outputChannelCount,
+                       outputSampleFormat,
+                       hostOutputSampleFormat,
+                       sampleRate,
+                       streamFlags,
+                       framesPerBuffer,
+                       framesPerHostCallback,
+                       bufferMode,
+                       streamCallback,
+                       userData);
+               if (result != paNoError)
+               {
+                       LogPaError(result);
+                       goto error;
+               }
        }
 
        // Set Input latency
     stream->streamRepresentation.streamInfo.inputLatency =
-            ((double)PaUtil_GetBufferProcessorInputLatencyFrames(&stream->bufferProcessor) / sampleRate)
-                       + ((inputParameters)?stream->in.latencySeconds : 0);
+            (useInputBufferProcessor ? PaUtil_GetBufferProcessorInputLatencyFrames(&stream->bufferProcessor) / sampleRate : 0)
+                       + (inputParameters != NULL ? stream->in.latencySeconds : 0);
 
        // Set Output latency
     stream->streamRepresentation.streamInfo.outputLatency =
-            ((double)PaUtil_GetBufferProcessorOutputLatencyFrames(&stream->bufferProcessor) / sampleRate)
-                       + ((outputParameters)?stream->out.latencySeconds : 0);
+            (useOutputBufferProcessor ? PaUtil_GetBufferProcessorOutputLatencyFrames(&stream->bufferProcessor) / sampleRate : 0)
+                       + (outputParameters != NULL ? stream->out.latencySeconds : 0);
 
        // Set SR
     stream->streamRepresentation.streamInfo.sampleRate = sampleRate;
@@ -4353,7 +4467,7 @@ static PaError WriteStream( PaStream* s, const void *_buffer, unsigned long fram
                        continue;
                }
 
-               // Keep in 'frmaes' range
+               // Keep in 'frames' range
                if (available > frames)
                        available = frames;
 
@@ -4564,14 +4678,31 @@ static void WaspiHostProcessingLoop( void *inputBuffer,  long inputFrames,
 }
 
 // ------------------------------------------------------------------------------------------
-HANDLE MMCSS_activate(const char *name)
-{
 #ifndef PA_WINRT
+static PaError MMCSS_activate(PaWasapiThreadPriority nPriorityClass, HANDLE *ret)
+{
+       static const char *mmcs_name[] =
+       {
+               NULL,
+               "Audio",
+               "Capture",
+               "Distribution",
+               "Games",
+               "Playback",
+               "Pro Audio",
+               "Window Manager"
+       };
+
     DWORD task_idx = 0;
-    HANDLE hTask = pAvSetMmThreadCharacteristics(name, &task_idx);
-    if (hTask == NULL)
+    HANDLE hTask;
+       
+       if ((UINT32)nPriorityClass >= STATIC_ARRAY_SIZE(mmcs_name))
+               return paUnanticipatedHostError;
+       
+    if ((hTask = pAvSetMmThreadCharacteristics(mmcs_name[nPriorityClass], &task_idx)) == NULL)
        {
-        PRINT(("WASAPI: AvSetMmThreadCharacteristics failed!\n"));
+        PRINT(("WASAPI: AvSetMmThreadCharacteristics failed: error[%d]\n", GetLastError()));
+               return paUnanticipatedHostError;
     }
 
     /*BOOL priority_ok = pAvSetMmThreadPriority(hTask, AVRT_PRIORITY_NORMAL);
@@ -4587,55 +4718,61 @@ HANDLE MMCSS_activate(const char *name)
                PRINT(("WASAPI: thread[ priority-0x%X class-0x%X ]\n", cur_priority, cur_priority_class));
     }
 
-       return hTask;
-#else
-       (void)name;
-       return NULL;
-#endif
+       (*ret) = hTask;
+       return paNoError;
 }
+#endif
 
 // ------------------------------------------------------------------------------------------
-void MMCSS_deactivate(HANDLE hTask)
-{
-       if (!hTask)
-               return;
-
 #ifndef PA_WINRT
+static void MMCSS_deactivate(HANDLE hTask)
+{
        if (pAvRevertMmThreadCharacteristics(hTask) == FALSE)
        {
         PRINT(("WASAPI: AvRevertMmThreadCharacteristics failed!\n"));
     }
-#endif
 }
+#endif
 
 // ------------------------------------------------------------------------------------------
 PaError PaWasapi_ThreadPriorityBoost(void **hTask, PaWasapiThreadPriority nPriorityClass)
 {
-       static const char *mmcs_name[] =
-       {
-               NULL,
-               "Audio",
-               "Capture",
-               "Distribution",
-               "Games",
-               "Playback",
-               "Pro Audio",
-               "Window Manager"
-       };
        HANDLE task;
+       PaError ret;
 
        if (hTask == NULL)
                return paUnanticipatedHostError;
 
-       if ((UINT32)nPriorityClass >= STATIC_ARRAY_SIZE(mmcs_name))
-               return paUnanticipatedHostError;
+#ifndef PA_WINRT
+       if ((ret = MMCSS_activate(nPriorityClass, &task)) != paNoError)
+               return ret;
+#else
+       switch (nPriorityClass)
+       {
+       case eThreadPriorityAudio:
+       case eThreadPriorityProAudio: {
+
+               // Save previous thread priority
+               intptr_t priority_prev = GetThreadPriority(GetCurrentThread());
+
+               // Try set new thread priority
+               if (SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST) == FALSE)
+                       return paUnanticipatedHostError;
 
-       task = MMCSS_activate(mmcs_name[nPriorityClass]);
-       if (task == NULL)
+               // Memorize prev priority (pretend to be non NULL pointer by adding 0x80000000 mask)
+               task = (HANDLE)(priority_prev | 0x80000000);
+
+               ret = paNoError;
+
+               break; }
+
+       default:
                return paUnanticipatedHostError;
+       }
+#endif
 
        (*hTask) = task;
-       return paNoError;
+       return ret;
 }
 
 // ------------------------------------------------------------------------------------------
@@ -4644,7 +4781,13 @@ PaError PaWasapi_ThreadPriorityRevert(void *hTask)
        if (hTask == NULL)
                return paUnanticipatedHostError;
 
+#ifndef PA_WINRT
        MMCSS_deactivate((HANDLE)hTask);
+#else
+       // Revert previous priority by removing 0x80000000 mask
+       if (SetThreadPriority(GetCurrentThread(), (int)((intptr_t)hTask & ~0x80000000)) == FALSE)
+               return paUnanticipatedHostError;
+#endif
 
        return paNoError;
 }
@@ -4927,6 +5070,48 @@ PaError PaWasapi_GetAudioClient(PaStream *pStream, void **pAudioClient, int bOut
        return paNoError;
 }
 
+// ------------------------------------------------------------------------------------------
+PaError PaWasapi_SetDefaultInterfaceId( unsigned short *pId, int bOutput )
+{
+#ifdef PA_WINRT
+
+       // Validate Id length
+       if (pId != NULL)
+       {
+               for (int i = 0; pId[i] != 0; ++i)
+               {
+                       if (i >= PA_WASAPI_DEVICE_ID_LEN)
+                               return paBufferTooBig;
+               }
+       }
+
+       // Set Id (or reset to all 0 if NULL is provided)
+       if (bOutput)
+       {
+               memset(g_DefaultRenderId, 0, sizeof(g_DefaultRenderId));
+               if (pId != NULL)
+               {
+                       for (int i = 0; (pId[i] != 0) && (i < STATIC_ARRAY_SIZE(g_DefaultRenderId)); ++i)
+                               g_DefaultRenderId[i] = pId[i];
+               }
+       }
+       else
+       {
+               memset(g_DefaultCaptureId, 0, sizeof(g_DefaultCaptureId));
+               if (pId != NULL)
+               {
+                       for (int i = 0; (pId[i] != 0) && (i < STATIC_ARRAY_SIZE(g_DefaultCaptureId)); ++i)
+                               g_DefaultCaptureId[i] = pId[i];
+               }
+       }
+
+       return paNoError;
+
+#else
+       return paIncompatibleStreamHostApi;
+#endif
+}
+
 // ------------------------------------------------------------------------------------------
 HRESULT _PollGetOutputFramesAvailable(PaWasapiStream *stream, UINT32 *available)
 {
@@ -4963,7 +5148,7 @@ HRESULT _PollGetInputFramesAvailable(PaWasapiStream *stream, UINT32 *available)
 }
 
 // ------------------------------------------------------------------------------------------
-HRESULT ProcessOutputBuffer(PaWasapiStream *stream, PaWasapiHostProcessor *processor, UINT32 frames)
+static HRESULT ProcessOutputBuffer(PaWasapiStream *stream, PaWasapiHostProcessor *processor, UINT32 frames)
 {
        HRESULT hr;
        BYTE *data = NULL;
@@ -5006,7 +5191,15 @@ HRESULT ProcessOutputBuffer(PaWasapiStream *stream, PaWasapiHostProcessor *proce
                // expand buffer
                UINT32 mono_frames_size = frames * (stream->out.wavex.Format.wBitsPerSample / 8);
                if (mono_frames_size > stream->out.monoBufferSize)
+               {
                        stream->out.monoBuffer = PaWasapi_ReallocateMemory(stream->out.monoBuffer, (stream->out.monoBufferSize = mono_frames_size));
+                       if (stream->out.monoBuffer == NULL)
+                       {
+                               hr = E_OUTOFMEMORY;
+                               LogHostError(hr);
+                               return hr;
+                       }
+               }
 
                // process
                processor[S_OUTPUT].processor(NULL, 0, (BYTE *)stream->out.monoBuffer, frames, processor[S_OUTPUT].userData);
@@ -5072,7 +5265,15 @@ HRESULT ProcessInputBuffer(PaWasapiStream *stream, PaWasapiHostProcessor *proces
                        // expand buffer
                        UINT32 mono_frames_size = frames * (stream->in.wavex.Format.wBitsPerSample / 8);
                        if (mono_frames_size > stream->in.monoBufferSize)
+                       {
                                stream->in.monoBuffer = PaWasapi_ReallocateMemory(stream->in.monoBuffer, (stream->in.monoBufferSize = mono_frames_size));
+                               if (stream->out.monoBuffer == NULL)
+                               {
+                                       hr = E_OUTOFMEMORY;
+                                       LogHostError(hr);
+                                       return hr;
+                               }
+                       }
 
                        // mix 1 to 2 channels
                        stream->in.monoMixer(stream->in.monoBuffer, data, frames);
@@ -5370,7 +5571,7 @@ PA_THREAD_FUNC ProcThreadPoll(void *param)
        }
 
        // Calculate timeout for next polling attempt.
-       sleep_ms_in  = GetFramesSleepTime(stream->in.framesPerHostCallback/WASAPI_PACKETS_PER_INPUT_BUFFER, stream->in.wavex.Format.nSamplesPerSec);
+       sleep_ms_in  = GetFramesSleepTime(stream->in.framesPerHostCallback / WASAPI_PACKETS_PER_INPUT_BUFFER, stream->in.wavex.Format.nSamplesPerSec);
        sleep_ms_out = GetFramesSleepTime(stream->out.framesPerBuffer, stream->out.wavex.Format.nSamplesPerSec);
 
        // WASAPI Input packets tend to expire very easily, let's limit sleep time to 2 milliseconds
@@ -5382,30 +5583,27 @@ PA_THREAD_FUNC ProcThreadPoll(void *param)
        // polled according its packet length.
        if (stream->bufferMode != paUtilFixedHostBufferSize)
        {
-               //sleep_ms_in = GetFramesSleepTime(stream->bufferProcessor.framesPerUserBuffer, stream->in.wavex.Format.nSamplesPerSec);
-               sleep_ms_out = GetFramesSleepTime(stream->bufferProcessor.framesPerUserBuffer, stream->out.wavex.Format.nSamplesPerSec);
+               //sleep_ms_in = GetFramesSleepTime((stream->bufferProcessor.framesPerUserBuffer ? stream->bufferProcessor.framesPerUserBuffer : stream->in.params.frames_per_buffer), stream->in.wavex.Format.nSamplesPerSec);
+               sleep_ms_out = GetFramesSleepTime((stream->bufferProcessor.framesPerUserBuffer ? stream->bufferProcessor.framesPerUserBuffer : stream->out.params.frames_per_buffer), stream->out.wavex.Format.nSamplesPerSec);
        }
 
        // Choose smallest
        if ((sleep_ms_in != 0) && (sleep_ms_out != 0))
                sleep_ms = min(sleep_ms_in, sleep_ms_out);
        else
-       {
                sleep_ms = (sleep_ms_in ? sleep_ms_in : sleep_ms_out);
-       }
+
        // Make sure not 0, othervise use ThreadIdleScheduler
        if (sleep_ms == 0)
        {
-               sleep_ms_in  = GetFramesSleepTimeMicroseconds(stream->in.framesPerHostCallback/WASAPI_PACKETS_PER_INPUT_BUFFER, stream->in.wavex.Format.nSamplesPerSec);
-               sleep_ms_out = GetFramesSleepTimeMicroseconds(stream->bufferProcessor.framesPerUserBuffer, stream->out.wavex.Format.nSamplesPerSec);
+               sleep_ms_in  = GetFramesSleepTimeMicroseconds(stream->in.framesPerHostCallback / WASAPI_PACKETS_PER_INPUT_BUFFER, stream->in.wavex.Format.nSamplesPerSec);
+               sleep_ms_out = GetFramesSleepTimeMicroseconds((stream->bufferProcessor.framesPerUserBuffer ? stream->bufferProcessor.framesPerUserBuffer : stream->out.params.frames_per_buffer), stream->out.wavex.Format.nSamplesPerSec);
 
                // Choose smallest
                if ((sleep_ms_in != 0) && (sleep_ms_out != 0))
                        sleep_ms = min(sleep_ms_in, sleep_ms_out);
                else
-               {
                        sleep_ms = (sleep_ms_in ? sleep_ms_in : sleep_ms_out);
-               }
 
                // Setup thread sleep scheduler
                ThreadIdleScheduler_Setup(&scheduler, 1, sleep_ms/* microseconds here */);
@@ -5457,6 +5655,12 @@ PA_THREAD_FUNC ProcThreadPoll(void *param)
                                {
                                        if (frames != 0)
                                        {
+                                               // USB DACs report large buffer in Exclusive mode and if it is filled fully will stuck in
+                                               // non playing state, e.g. IAudioClient_GetCurrentPadding() will start reporting max buffer size
+                                               // constantly, thus preload data size equal to the user buffer to allow process going
+                                               if ((stream->out.shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE) && (frames > stream->out.framesPerBuffer))
+                                                       frames = stream->out.framesPerBuffer;
+
                                                if ((hr = ProcessOutputBuffer(stream, processor, frames)) != S_OK)
                                                {
                                                        LogHostError(hr); // not fatal, just log
@@ -5851,9 +6055,15 @@ thread_error:
 }
 
 // ------------------------------------------------------------------------------------------
-void *PaWasapi_ReallocateMemory(void *ptr, size_t size)
+void *PaWasapi_ReallocateMemory(void *prev, size_t size)
 {
-       return realloc(ptr, size);
+       void *ret = realloc(prev, size);
+       if (ret == NULL)
+       {
+               PaWasapi_FreeMemory(prev);
+               return NULL;
+       }
+       return ret;
 }
 
 // ------------------------------------------------------------------------------------------