]> Repos - portaudio/commitdiff
wasapi:
authordmitrykos <dmitrykos@0f58301d-fd10-0410-b4af-bbb618454e57>
Tue, 1 Jun 2010 17:26:37 +0000 (17:26 +0000)
committerdmitrykos <dmitrykos@0f58301d-fd10-0410-b4af-bbb618454e57>
Tue, 1 Jun 2010 17:26:37 +0000 (17:26 +0000)
 - implemented support for full-duplex mode (it always uses polling(pull) WASAPI behavior, if latency <15msec needed then event interface must be done)
 - fixed mono-to-stereo converter for input stream
 - mono-to-stereo converter will also automatically operate in shared mode (if WASAPI driver does not support mono for output, or only stereo for input)

src/hostapi/wasapi/pa_win_wasapi.c

index 185715098cd9339bafe5188b708be46f8ed59837..eb922bff0e0a3582a4269316767f937e9c66b63b 100644 (file)
@@ -308,7 +308,7 @@ PA_THREAD_FUNC ProcThreadPoll(void *param);
 \r
 #define MAX_STR_LEN 512\r
 \r
-enum { S_INPUT = 0, S_OUTPUT, S_COUNT };\r
+enum { S_INPUT = 0, S_OUTPUT = 1, S_COUNT = 2, S_FULLDUPLEX = 0 };\r
 \r
 #define STATIC_ARRAY_SIZE(array) (sizeof(array)/sizeof(array[0]))\r
 \r
@@ -317,6 +317,8 @@ enum { S_INPUT = 0, S_OUTPUT, S_COUNT };
 #define PA_SKELETON_SET_LAST_HOST_ERROR( errorCode, errorText ) \\r
     PaUtil_SetLastHostErrorInfo( paWASAPI, errorCode, errorText )\r
 \r
+#define PA_WASAPI__IS_FULLDUPLEX(STREAM) ((STREAM)->in.client && (STREAM)->out.client)\r
+\r
 #ifndef IF_FAILED_JUMP\r
 #define IF_FAILED_JUMP(hr, label) if(FAILED(hr)) goto label;\r
 #endif\r
@@ -884,7 +886,10 @@ static BOOL UseWOW64Workaround()
 }\r
 \r
 // ------------------------------------------------------------------------------------------\r
-#define _WASAPI_MONO_TO_STEREO_MIXER(TYPE)\\r
+typedef enum EMixerDir { MIX_DIR__1TO2, MIX_DIR__2TO1 } EMixerDir;\r
+\r
+// ------------------------------------------------------------------------------------------\r
+#define _WASAPI_MONO_TO_STEREO_MIXER_1_TO_2(TYPE)\\r
        TYPE * __restrict to   = __to;\\r
        TYPE * __restrict from = __from;\\r
        TYPE * __restrict end  = from + count;\\r
@@ -896,46 +901,58 @@ static BOOL UseWOW64Workaround()
        }\r
 \r
 // ------------------------------------------------------------------------------------------\r
-static void _MixMonoToStereo_8(void *__to, void *__from, UINT32 count)\r
-{\r
-       _WASAPI_MONO_TO_STEREO_MIXER(BYTE);\r
-}\r
-\r
-// ------------------------------------------------------------------------------------------\r
-static void _MixMonoToStereo_16(void *__to, void *__from, UINT32 count)\r
-{\r
-       _WASAPI_MONO_TO_STEREO_MIXER(short);\r
-}\r
-\r
-// ------------------------------------------------------------------------------------------\r
-static void _MixMonoToStereo_24(void *__to, void *__from, UINT32 count)\r
-{\r
-       _WASAPI_MONO_TO_STEREO_MIXER(int); // !!! int24 data is contained in 32-bit containers\r
-}\r
+#define _WASAPI_MONO_TO_STEREO_MIXER_2_TO_1(TYPE)\\r
+       TYPE * __restrict to   = (TYPE *)__to;\\r
+       TYPE * __restrict from = (TYPE *)__from;\\r
+       TYPE * __restrict end  = to + count;\\r
+       while (to != end)\\r
+       {\\r
+               *to ++ = (TYPE)((float)(from[0] + from[1]) * 0.5f);\\r
+               from += 2;\\r
+       }\r
 \r
 // ------------------------------------------------------------------------------------------\r
-static void _MixMonoToStereo_32(void *__to, void *__from, UINT32 count)\r
-{\r
-       _WASAPI_MONO_TO_STEREO_MIXER(int);\r
-}\r
+static void _MixMonoToStereo_1TO2_8(void *__to, void *__from, UINT32 count) { _WASAPI_MONO_TO_STEREO_MIXER_1_TO_2(BYTE); }\r
+static void _MixMonoToStereo_1TO2_16(void *__to, void *__from, UINT32 count) { _WASAPI_MONO_TO_STEREO_MIXER_1_TO_2(short); }\r
+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*/ }\r
+static void _MixMonoToStereo_1TO2_32(void *__to, void *__from, UINT32 count) { _WASAPI_MONO_TO_STEREO_MIXER_1_TO_2(int); }\r
+static void _MixMonoToStereo_1TO2_32f(void *__to, void *__from, UINT32 count) { _WASAPI_MONO_TO_STEREO_MIXER_1_TO_2(float); }\r
 \r
 // ------------------------------------------------------------------------------------------\r
-static void _MixMonoToStereo_32f(void *__to, void *__from, UINT32 count)\r
-{\r
-       _WASAPI_MONO_TO_STEREO_MIXER(float);\r
-}\r
+static void _MixMonoToStereo_2TO1_8(void *__to, void *__from, UINT32 count) { _WASAPI_MONO_TO_STEREO_MIXER_2_TO_1(BYTE); }\r
+static void _MixMonoToStereo_2TO1_16(void *__to, void *__from, UINT32 count) { _WASAPI_MONO_TO_STEREO_MIXER_2_TO_1(short); }\r
+static void _MixMonoToStereo_2TO1_24(void *__to, void *__from, UINT32 count) { _WASAPI_MONO_TO_STEREO_MIXER_2_TO_1(int); /* !!! int24 data is contained in 32-bit containers*/ }\r
+static void _MixMonoToStereo_2TO1_32(void *__to, void *__from, UINT32 count) { _WASAPI_MONO_TO_STEREO_MIXER_2_TO_1(int); }\r
+static void _MixMonoToStereo_2TO1_32f(void *__to, void *__from, UINT32 count) { _WASAPI_MONO_TO_STEREO_MIXER_2_TO_1(float); }\r
 \r
 // ------------------------------------------------------------------------------------------\r
-static MixMonoToStereoF _GetMonoToStereoMixer(PaSampleFormat format)\r
+static MixMonoToStereoF _GetMonoToStereoMixer(PaSampleFormat format, EMixerDir dir)\r
 {\r
-       switch (format)\r
+       switch (dir)\r
        {\r
-       case paUInt8:   return _MixMonoToStereo_8;\r
-       case paInt16:   return _MixMonoToStereo_16;\r
-       case paInt24:   return _MixMonoToStereo_24;\r
-       case paInt32:   return _MixMonoToStereo_32;\r
-       case paFloat32: return _MixMonoToStereo_32f;\r
+       case MIX_DIR__1TO2:\r
+               switch (format)\r
+               {\r
+               case paUInt8:   return _MixMonoToStereo_1TO2_8;\r
+               case paInt16:   return _MixMonoToStereo_1TO2_16;\r
+               case paInt24:   return _MixMonoToStereo_1TO2_24;\r
+               case paInt32:   return _MixMonoToStereo_1TO2_32;\r
+               case paFloat32: return _MixMonoToStereo_1TO2_32f;\r
+               }\r
+               break;\r
+\r
+       case MIX_DIR__2TO1:\r
+               switch (format)\r
+               {\r
+               case paUInt8:   return _MixMonoToStereo_2TO1_8;\r
+               case paInt16:   return _MixMonoToStereo_2TO1_16;\r
+               case paInt24:   return _MixMonoToStereo_2TO1_24;\r
+               case paInt32:   return _MixMonoToStereo_2TO1_32;\r
+               case paFloat32: return _MixMonoToStereo_2TO1_32f;\r
+               }\r
+               break;\r
        }\r
+\r
        return NULL;\r
 }\r
 \r
@@ -1670,7 +1687,15 @@ static PaError GetClosestFormat(IAudioClient *myClient, double sampleRate,
 \r
                // Validate Channel count\r
                if ((WORD)params->channelCount != outWavex->Format.nChannels)\r
-                       return paInvalidChannelCount;\r
+               {\r
+                       // If mono, then driver does not support 1 channel, we use internal workaround\r
+                       // of tiny software mixing functionality, e.g. we provide to user buffer 1 channel\r
+                       // but then mix into 2 for device buffer\r
+                       if ((params->channelCount == 1) && (outWavex->Format.nChannels == 2))\r
+                               return paFormatIsSupported;\r
+                       else\r
+                               return paInvalidChannelCount;\r
+               }\r
 \r
                // Validate Sample format\r
                if ((bitsPerSample = PaSampleFormatToBitsPerSample(params->sampleFormat)) == 0)\r
@@ -1924,7 +1949,7 @@ static HRESULT CreateAudioClient(PaWasapiSubStream *pSubStream, PaWasapiDeviceIn
                }\r
 \r
                // select mixer\r
-               pSubStream->monoMixer = _GetMonoToStereoMixer(params->sampleFormat);\r
+               pSubStream->monoMixer = _GetMonoToStereoMixer(params->sampleFormat, (info->flow == eRender ? MIX_DIR__1TO2 : MIX_DIR__2TO1));\r
                if (pSubStream->monoMixer == NULL)\r
                {\r
                        LogHostError(hr = AUDCLNT_E_UNSUPPORTED_FORMAT);\r
@@ -2074,7 +2099,7 @@ static const REFERENCE_TIME MAX_BUFFER_POLL_DURATION  = 2000 * 10000;
                        }\r
 \r
                        // select mixer\r
-                       pSubStream->monoMixer = _GetMonoToStereoMixer(params->sampleFormat);\r
+                       pSubStream->monoMixer = _GetMonoToStereoMixer(params->sampleFormat, (info->flow == eRender ? MIX_DIR__1TO2 : MIX_DIR__2TO1));\r
                        if (pSubStream->monoMixer == NULL)\r
                        {\r
                                LogHostError(hr = AUDCLNT_E_UNSUPPORTED_FORMAT);\r
@@ -2223,10 +2248,15 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
                stream->in.streamFlags = AUDCLNT_STREAMFLAGS_EVENTCALLBACK;\r
                if (paWasapi->useWOW64Workaround)\r
                        stream->in.streamFlags = 0; // polling interface\r
+               else\r
                if (streamCallback == NULL)\r
                        stream->in.streamFlags = 0; // polling interface\r
+               else\r
                if ((inputStreamInfo != NULL) && (inputStreamInfo->flags & paWinWasapiPolling))\r
                        stream->in.streamFlags = 0; // polling interface\r
+               else\r
+               if (outputParameters != NULL)\r
+                       stream->in.streamFlags = 0; // polling interface is implemented for full-duplex mode also\r
 \r
                // Create Audio client\r
                hr = CreateAudioClient(&stream->in, info, inputParameters, 0/*framesPerLatency*/,\r
@@ -2293,7 +2323,7 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
                // Append buffer latency to interface latency in shared mode (see GetStreamLatency notes)\r
                stream->in.latency_seconds += buffer_latency;\r
 \r
-               PRINT(("PaWASAPIOpenStream(input): framesPerHostCallback[ %d ] latency[ %.02fms ] exclusive[ %s ] wow64_fix[ %s ]\n", (UINT32)stream->in.framesPerHostCallback, (float)(stream->in.latency_seconds*1000.0f), (stream->in.shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE ? "YES" : "NO"), (paWasapi->useWOW64Workaround ? "YES" : "NO")));\r
+               PRINT(("WASAPI::OpenStream(input): framesPerHostCallback[ %d ] latency[ %.02fms ] exclusive[ %s ] wow64_fix[ %s ]\n", (UINT32)stream->in.framesPerHostCallback, (float)(stream->in.latency_seconds*1000.0f), (stream->in.shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE ? "YES" : "NO"), (paWasapi->useWOW64Workaround ? "YES" : "NO")));\r
        }\r
     else\r
     {\r
@@ -2332,10 +2362,15 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
                stream->out.streamFlags = AUDCLNT_STREAMFLAGS_EVENTCALLBACK;\r
                if (paWasapi->useWOW64Workaround)\r
                        stream->out.streamFlags = 0; // polling interface\r
+               else\r
                if (streamCallback == NULL)\r
                        stream->out.streamFlags = 0; // polling interface\r
+               else\r
                if ((outputStreamInfo != NULL) && (outputStreamInfo->flags & paWinWasapiPolling))\r
                        stream->out.streamFlags = 0; // polling interface\r
+               else\r
+               if (inputParameters != NULL)\r
+                       stream->out.streamFlags = 0; // polling interface is implemented for full-duplex mode also\r
 \r
                // Create Audio client\r
                hr = CreateAudioClient(&stream->out, info, outputParameters, 0/*framesPerLatency*/,\r
@@ -2402,7 +2437,7 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
                // Append buffer latency to interface latency in shared mode (see GetStreamLatency notes)\r
                stream->out.latency_seconds += buffer_latency;\r
 \r
-               PRINT(("PaWASAPIOpenStream(output): framesPerHostCallback[ %d ] latency[ %.02fms ] exclusive[ %s ] wow64_fix[ %s ]\n", (UINT32)stream->out.framesPerHostCallback, (float)(stream->out.latency_seconds*1000.0f), (stream->out.shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE ? "YES" : "NO"), (paWasapi->useWOW64Workaround ? "YES" : "NO")));\r
+               PRINT(("WASAPI::OpenStream(output): framesPerHostCallback[ %d ] latency[ %.02fms ] exclusive[ %s ] wow64_fix[ %s ]\n", (UINT32)stream->out.framesPerHostCallback, (float)(stream->out.latency_seconds*1000.0f), (stream->out.shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE ? "YES" : "NO"), (paWasapi->useWOW64Workaround ? "YES" : "NO")));\r
        }\r
     else\r
     {\r
@@ -2410,6 +2445,10 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
         outputSampleFormat = hostOutputSampleFormat = paInt16; /* Surpress 'uninitialized var' warnings. */\r
     }\r
 \r
+       // og full-duplex\r
+       if ((inputParameters != NULL) && (outputParameters != NULL))\r
+               PRINT(("WASAPI::OpenStream: full-duplex mode\n"));\r
+\r
        // paWinWasapiPolling must be on/or not on both streams\r
        if ((inputParameters != NULL) && (outputParameters != NULL))\r
        {\r
@@ -3232,7 +3271,7 @@ PaError PaWasapi_ThreadPriorityRevert(void *hTask)
 }\r
 \r
 // ------------------------------------------------------------------------------------------\r
-static HRESULT FillOutputBuffer(PaWasapiStream *stream, PaWasapiHostProcessor *processor, UINT32 frames)\r
+static HRESULT ProcessOutputBuffer(PaWasapiStream *stream, PaWasapiHostProcessor *processor, UINT32 frames)\r
 {\r
        HRESULT hr;\r
        BYTE *data = NULL;\r
@@ -3277,11 +3316,9 @@ static HRESULT FillOutputBuffer(PaWasapiStream *stream, PaWasapiHostProcessor *p
                // expand buffer (one way only for better performancedue to no calls to realloc)\r
                UINT32 mono_frames_size = frames * __DIV_8(stream->out.wavex.Format.wBitsPerSample);\r
                if (mono_frames_size > stream->out.monoBufferSize)\r
-               {\r
-                       stream->out.monoBuffer = realloc(stream->out.monoBuffer, mono_frames_size);\r
-               }\r
+                       stream->out.monoBuffer = realloc(stream->out.monoBuffer, (stream->out.monoBufferSize = mono_frames_size));\r
 \r
-               // feed to user\r
+               // process\r
                processor[S_OUTPUT].processor(NULL, 0, (BYTE *)stream->out.monoBuffer, frames, processor[S_OUTPUT].userData);\r
 \r
                // mix 1 to 2 channels\r
@@ -3302,7 +3339,7 @@ static HRESULT FillOutputBuffer(PaWasapiStream *stream, PaWasapiHostProcessor *p
 }\r
 \r
 // ------------------------------------------------------------------------------------------\r
-static HRESULT PollInputBuffer(PaWasapiStream *stream, PaWasapiHostProcessor *processor)\r
+static HRESULT ProcessInputBuffer(PaWasapiStream *stream, PaWasapiHostProcessor *processor)\r
 {\r
        HRESULT hr;\r
        UINT32 frames;\r
@@ -3333,15 +3370,13 @@ static HRESULT PollInputBuffer(PaWasapiStream *stream, PaWasapiHostProcessor *pr
                        // expand buffer (one way only for better performancedue to no calls to realloc)\r
                        UINT32 mono_frames_size = frames * __DIV_8(stream->in.wavex.Format.wBitsPerSample);\r
                        if (mono_frames_size > stream->in.monoBufferSize)\r
-                       {\r
-                               stream->in.monoBuffer = realloc(stream->in.monoBuffer, mono_frames_size);\r
-                       }\r
-\r
-                       // feed to user\r
-                       processor[S_INPUT].processor((BYTE *)stream->in.monoBuffer, frames, NULL, 0, processor[S_INPUT].userData);\r
+                               stream->in.monoBuffer = realloc(stream->in.monoBuffer, (stream->in.monoBufferSize = mono_frames_size));\r
 \r
                        // mix 1 to 2 channels\r
-                       stream->in.monoMixer(data, stream->in.monoBuffer, frames);\r
+                       stream->in.monoMixer(stream->in.monoBuffer, data, frames);\r
+\r
+                       // process\r
+                       processor[S_INPUT].processor((BYTE *)stream->in.monoBuffer, frames, NULL, 0, processor[S_INPUT].userData);\r
 \r
 #undef __DIV_8\r
                }\r
@@ -3480,7 +3515,7 @@ PA_THREAD_FUNC ProcThreadEvent(void *param)
                }\r
 \r
                // Preload buffer before start\r
-               if ((hr = FillOutputBuffer(stream, processor, stream->out.framesPerHostCallback)) != S_OK)\r
+               if ((hr = ProcessOutputBuffer(stream, processor, stream->out.framesPerHostCallback)) != S_OK)\r
                {\r
                        LogHostError(hr);\r
                        goto thread_error;\r
@@ -3524,14 +3559,14 @@ PA_THREAD_FUNC ProcThreadEvent(void *param)
                case WAIT_OBJECT_0 + S_INPUT: {\r
             if (stream->cclient == NULL)\r
                 break;\r
-                       PollInputBuffer(stream, processor);\r
+                       ProcessInputBuffer(stream, processor);\r
                        break; }\r
 \r
                // Output stream\r
                case WAIT_OBJECT_0 + S_OUTPUT: {\r
             if (stream->rclient == NULL)\r
                 break;\r
-                       FillOutputBuffer(stream, processor, stream->out.framesPerHostCallback);\r
+                       ProcessOutputBuffer(stream, processor, stream->out.framesPerHostCallback);\r
                        break; }\r
                }\r
        }\r
@@ -3558,6 +3593,27 @@ thread_error:
        goto thread_end;\r
 }\r
 \r
+// ------------------------------------------------------------------------------------------\r
+static HRESULT PollGetOutputFramesAvailable(PaWasapiStream *stream, UINT32 *available)\r
+{\r
+       HRESULT hr;\r
+       UINT32 frames  = stream->out.framesPerHostCallback, \r
+                  padding = 0;\r
+\r
+       (*available) = 0;\r
+\r
+       // get read position\r
+       if ((hr = IAudioClient_GetCurrentPadding(stream->out.client, &padding)) != S_OK)\r
+               return LogHostError(hr);\r
+\r
+       // get available\r
+       frames -= padding;\r
+       \r
+       // set\r
+       (*available) = frames;\r
+       return hr;\r
+}\r
+\r
 // ------------------------------------------------------------------------------------------\r
 PA_THREAD_FUNC ProcThreadPoll(void *param)\r
 {\r
@@ -3619,11 +3675,15 @@ PA_THREAD_FUNC ProcThreadPoll(void *param)
                        }\r
                }\r
 \r
-               // Preload buffer (obligatory, othervise ->Start() will fail)\r
-               if ((hr = FillOutputBuffer(stream, processor, stream->out.framesPerHostCallback)) != S_OK)\r
+               // Preload buffer (obligatory, othervise ->Start() will fail), avoid processing\r
+               // when in full-duplex mode as it requires input processing as well\r
+               if (!PA_WASAPI__IS_FULLDUPLEX(stream))\r
                {\r
-                       LogHostError(hr);\r
-                       goto thread_error;\r
+                       if ((hr = ProcessOutputBuffer(stream, processor, stream->out.framesPerHostCallback)) != S_OK)\r
+                       {\r
+                               LogHostError(hr);\r
+                               goto thread_error;\r
+                       }\r
                }\r
 \r
                // Start\r
@@ -3640,46 +3700,136 @@ PA_THREAD_FUNC ProcThreadPoll(void *param)
        // Notify: thread started\r
        SetEvent(stream->hThreadStart);\r
 \r
-       // Processing Loop\r
-       while (WaitForSingleObject(stream->hCloseRequest, sleep_ms) == WAIT_TIMEOUT)\r
-    {\r
-               for (i = 0; i < S_COUNT; ++i)\r
+       if (!PA_WASAPI__IS_FULLDUPLEX(stream))\r
+       {\r
+               // Processing Loop\r
+               while (WaitForSingleObject(stream->hCloseRequest, sleep_ms) == WAIT_TIMEOUT)\r
                {\r
-                       // Process S_INPUT/S_OUTPUT\r
-                       switch (i)\r
+                       for (i = 0; i < S_COUNT; ++i)\r
                        {\r
-                       // Input stream\r
-                       case S_INPUT: {\r
-                               if (stream->cclient == NULL)\r
-                                       break;\r
-                               PollInputBuffer(stream, processor);\r
-                               break; }\r
+                               // Process S_INPUT/S_OUTPUT\r
+                               switch (i)\r
+                               {\r
+                               // Input stream\r
+                               case S_INPUT: {\r
+                                       if (stream->cclient == NULL)\r
+                                               break;\r
+                                       ProcessInputBuffer(stream, processor);\r
+                                       break; }\r
+\r
+                               // Output stream\r
+                               case S_OUTPUT: {\r
+\r
+                                       UINT32 frames;\r
+                                       if (stream->rclient == NULL)\r
+                                               break;\r
+\r
+                                       // get available frames\r
+                                       if ((hr = PollGetOutputFramesAvailable(stream, &frames)) != S_OK)\r
+                                       {\r
+                                               LogHostError(hr);\r
+                                               break;\r
+                                       }\r
+\r
+                                       // output\r
+                                       if (frames != 0)\r
+                                               ProcessOutputBuffer(stream, processor, frames);\r
+\r
+                                       break; }\r
+                               }\r
+                       }\r
+               }\r
+       }\r
+       else\r
+       {\r
+               // Processing Loop\r
+               while (WaitForSingleObject(stream->hCloseRequest, sleep_ms) == WAIT_TIMEOUT)\r
+               {\r
+                       UINT32 i_frames = 0, i_processed = 0;\r
+                       BYTE *i_data = NULL, *o_data = NULL;\r
+                       DWORD i_flags = 0;\r
+                       UINT32 o_frames = 0;\r
 \r
-                       // Output stream\r
-                       case S_OUTPUT: {\r
+                       // get host input buffer\r
+                       if ((hr = IAudioCaptureClient_GetBuffer(stream->cclient, &i_data, &i_frames, &i_flags, NULL, NULL)) != S_OK)\r
+                       {\r
+                               if (hr == AUDCLNT_S_BUFFER_EMPTY)\r
+                                       continue; // no data in capture buffer\r
 \r
-                               UINT32 frames, padding;\r
+                               LogHostError(hr);\r
+                               break;\r
+                       }\r
 \r
-                               if (stream->rclient == NULL)\r
-                                       break;\r
+                       // get available frames\r
+                       if ((hr = PollGetOutputFramesAvailable(stream, &o_frames)) != S_OK)\r
+                       {\r
+                               LogHostError(hr);\r
+                               break;\r
+                       }\r
 \r
-                               frames = stream->out.framesPerHostCallback;\r
+                       // process equal ammount of frames\r
+                       if (o_frames >= i_frames)\r
+                       {\r
+                               // process input ammount of frames\r
+                               UINT32 o_processed = i_frames;\r
 \r
-                               // Get Read position\r
-                               padding = 0;\r
-                               hr = IAudioClient_GetCurrentPadding(stream->out.client, &padding);\r
-                               if (hr != S_OK)\r
+                               // get host output buffer\r
+                               if ((hr = IAudioRenderClient_GetBuffer(stream->rclient, o_processed, &o_data)) == S_OK)\r
                                {\r
-                                       LogHostError(hr);\r
-                                       break;\r
-                               }\r
+                                       // processed amount of i_frames\r
+                                       i_processed = i_frames;\r
 \r
-                               // Fill buffer\r
-                               frames -= padding;\r
-                               if (frames != 0)\r
-                                       FillOutputBuffer(stream, processor, frames);\r
+                                       // convert output mono\r
+                                       if (stream->out.monoMixer)\r
+                                       {\r
+                                               #define __DIV_8(v)  ((v) >> 3) //!< (v / 8)\r
+                                               UINT32 mono_frames_size = o_processed * __DIV_8(stream->out.wavex.Format.wBitsPerSample);\r
+                                               #undef __DIV_8\r
+                                               // expand buffer (one way only for better performance due to no calls to realloc)\r
+                                               if (mono_frames_size > stream->out.monoBufferSize)\r
+                                                       stream->out.monoBuffer = realloc(stream->out.monoBuffer, (stream->out.monoBufferSize = mono_frames_size));\r
+                                       }\r
+\r
+                                       // convert input mono\r
+                                       if (stream->in.monoMixer)\r
+                                       {\r
+                                               #define __DIV_8(v)  ((v) >> 3) //!< (v / 8)\r
+                                               UINT32 mono_frames_size = i_processed * __DIV_8(stream->in.wavex.Format.wBitsPerSample);\r
+                                               #undef __DIV_8\r
+                                               // expand buffer (one way only for better performance due to no calls to realloc)\r
+                                               if (mono_frames_size > stream->in.monoBufferSize)\r
+                                                       stream->in.monoBuffer = realloc(stream->in.monoBuffer, (stream->in.monoBufferSize = mono_frames_size));\r
+\r
+                                               // mix 2 to 1 input channels\r
+                                               stream->in.monoMixer(stream->in.monoBuffer, i_data, i_processed);\r
+\r
+                                               // replace buffer pointer\r
+                                               i_data = (BYTE *)stream->in.monoBuffer;\r
+                                       }\r
+\r
+                                       // process\r
+                                       processor[S_FULLDUPLEX].processor(i_data, i_processed, o_data, o_processed, processor[S_FULLDUPLEX].userData);\r
+\r
+                                       // mix 1 to 2 output channels\r
+                                       if (stream->out.monoBuffer)\r
+                                               stream->out.monoMixer(o_data, stream->out.monoBuffer, o_processed);\r
+\r
+                                       // release host output buffer\r
+                                       if ((hr = IAudioRenderClient_ReleaseBuffer(stream->rclient, o_processed, 0)) != S_OK)\r
+                                               LogHostError(hr);\r
+                               }\r
+                               else\r
+                               {\r
+                                       if (stream->out.shareMode != AUDCLNT_SHAREMODE_SHARED)\r
+                                               LogHostError(hr); // be silent in shared mode, try again next time\r
+                               }\r
+                       }\r
 \r
-                               break; }\r
+                       // release host input buffer\r
+                       if ((hr = IAudioCaptureClient_ReleaseBuffer(stream->cclient, i_processed)) != S_OK)\r
+                       {\r
+                               LogHostError(hr);\r
+                               break;\r
                        }\r
                }\r
        }\r