]> Repos - portaudio/commitdiff
wasapi:
authordmitrykos <dmitrykos@0f58301d-fd10-0410-b4af-bbb618454e57>
Mon, 12 Jul 2010 18:41:05 +0000 (18:41 +0000)
committerdmitrykos <dmitrykos@0f58301d-fd10-0410-b4af-bbb618454e57>
Mon, 12 Jul 2010 18:41:05 +0000 (18:41 +0000)
 - reimplemented full-duplex processing to address changes to latency calculations (please note Exclusive mode due to some unknown reason will likely not be able to provide good audio quality yet)
 - fixed Pa_GetStreamInfo for WASAPI device if stream is opened as full-duplex (thanks to Reid Bishop for pointing to the bug)

src/hostapi/wasapi/pa_win_wasapi.c

index eadfd5694da17afaf1092a4158fbc2ca15de043e..9c47cc547ddc377d629b896cda162ec6a5ad6a82 100644 (file)
@@ -924,7 +924,7 @@ typedef enum EMixerDir { MIX_DIR__1TO2, MIX_DIR__2TO1 } EMixerDir;
        TYPE * __restrict end  = to + count;\\r
        while (to != end)\\r
        {\\r
-               *to ++ = (TYPE)((float)(from[0] + from[1]) * 0.70710678118654752440084436210485f/*1/sqrt(2)*/);\\r
+               *to ++ = (TYPE)((float)(from[0] + from[1]) * 0.5f);\\r
                from += 2;\\r
        }\r
 \r
@@ -2093,6 +2093,10 @@ static HRESULT CreateAudioClient(PaWasapiStream *pStream, PaWasapiSubStream *pSu
        }\r
 #endif\r
 \r
+       // For full-duplex output resize buffer to be the same as for input\r
+       if (output && fullDuplex)\r
+               framesPerLatency = pStream->in.framesPerHostCallback;\r
+\r
        // Avoid 0 frames\r
        if (framesPerLatency == 0)\r
                framesPerLatency = MakeFramesFromHns(pInfo->DefaultDevicePeriod, pSub->wavex.Format.nSamplesPerSec);\r
@@ -2663,13 +2667,16 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
 \r
        if (outputParameters && inputParameters)\r
        {\r
-               // serious problem #1\r
-               if (stream->in.period != stream->out.period)\r
+               // serious problem #1 - No, Not a problem, especially concerning Exclusive mode.\r
+               // Input device in exclusive mode somehow is getting large buffer always, thus we\r
+               // adjust Output latency to reflect it, thus period will differ but playback will be \r
+               // normal.\r
+               /*if (stream->in.period != stream->out.period)\r
                {\r
                        PRINT(("WASAPI: OpenStream: period discrepancy\n"));\r
                        LogPaError(result = paBadIODeviceCombination);\r
                        goto error;\r
-               }\r
+               }*/\r
 \r
                // serious problem #2 - No, Not a problem, as framesPerHostCallback take into account\r
                // sample size while it is not a problem for PA full-duplex, we must care of\r
@@ -2723,12 +2730,12 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
 \r
        // Set Input latency\r
     stream->streamRepresentation.streamInfo.inputLatency =\r
-            PaUtil_GetBufferProcessorInputLatency(&stream->bufferProcessor)\r
+            ((double)PaUtil_GetBufferProcessorInputLatency(&stream->bufferProcessor) / sampleRate)\r
                        + ((inputParameters)?stream->in.latency_seconds : 0);\r
 \r
        // Set Output latency\r
     stream->streamRepresentation.streamInfo.outputLatency =\r
-            PaUtil_GetBufferProcessorOutputLatency(&stream->bufferProcessor)\r
+            ((double)PaUtil_GetBufferProcessorOutputLatency(&stream->bufferProcessor) / sampleRate)\r
                        + ((outputParameters)?stream->out.latency_seconds : 0);\r
 \r
        // Set SR\r
@@ -3948,8 +3955,9 @@ PA_THREAD_FUNC ProcThreadPoll(void *param)
        }\r
        else\r
        {\r
+#if 0\r
                // Processing Loop\r
-               while (WaitForSingleObject(stream->hCloseRequest, sleep_ms) == WAIT_TIMEOUT)\r
+               while (WaitForSingleObject(stream->hCloseRequest, 1) == WAIT_TIMEOUT)\r
                {\r
                        UINT32 i_frames = 0, i_processed = 0;\r
                        BYTE *i_data = NULL, *o_data = NULL, *o_data_host = NULL;\r
@@ -4056,6 +4064,141 @@ PA_THREAD_FUNC ProcThreadPoll(void *param)
                                break;\r
                        }\r
                }\r
+#else\r
+               // Processing Loop\r
+               //sleep_ms = 1;\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, *o_data_host = NULL;\r
+                       DWORD i_flags = 0;\r
+                       UINT32 o_frames = 0;\r
+                       //BOOL repeat = FALSE;\r
+\r
+                       // going below 1 msec resolution, switching between 1 ms and no waiting\r
+                       //if (stream->in.shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE)\r
+                       //      sleep_ms = !sleep_ms;\r
+\r
+                       // get available frames\r
+                       if ((hr = PollGetOutputFramesAvailable(stream, &o_frames)) != S_OK)\r
+                       {\r
+                               LogHostError(hr);\r
+                               break;\r
+                       }\r
+\r
+                       while (o_frames != 0)\r
+                       {\r
+                               // get host input buffer\r
+                               if ((hr = IAudioCaptureClient_GetBuffer(stream->cclient, &i_data, &i_frames, &i_flags, NULL, NULL)) != S_OK)\r
+                               {\r
+                                       if (hr == AUDCLNT_S_BUFFER_EMPTY)\r
+                                               break; // no data in capture buffer\r
+\r
+                                       LogHostError(hr);\r
+                                       break;\r
+                               }\r
+\r
+                               //PA_DEBUG(("full-duplex: o_frames[%d] i_frames[%d] repeat[%d]\n", o_frames, i_frames, repeat));\r
+                               //repeat = TRUE;\r
+\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 host output buffer\r
+                                       if ((hr = IAudioRenderClient_GetBuffer(stream->rclient, o_processed, &o_data)) == S_OK)\r
+                                       {\r
+                                               // processed amount of i_frames\r
+                                               i_processed = i_frames;\r
+                                               o_data_host = o_data;\r
+\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
+                                                       {\r
+                                                               stream->out.monoBuffer = realloc(stream->out.monoBuffer, (stream->out.monoBufferSize = mono_frames_size));\r
+                                                               if (stream->out.monoBuffer == NULL)\r
+                                                               {\r
+                                                                       LogPaError(paInsufficientMemory);\r
+                                                                       goto thread_error;\r
+                                                               }\r
+                                                       }\r
+\r
+                                                       // replace buffer pointer\r
+                                                       o_data = (BYTE *)stream->out.monoBuffer;\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
+                                                       {\r
+                                                               stream->in.monoBuffer = realloc(stream->in.monoBuffer, (stream->in.monoBufferSize = mono_frames_size));\r
+                                                               if (stream->in.monoBuffer == NULL)\r
+                                                               {\r
+                                                                       LogPaError(paInsufficientMemory);\r
+                                                                       goto thread_error;\r
+                                                               }\r
+                                                       }\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_host, stream->out.monoBuffer, o_processed);\r
+\r
+                                               // release host output buffer\r
+                                               if ((hr = IAudioRenderClient_ReleaseBuffer(stream->rclient, o_processed, 0)) != S_OK)\r
+                                                       LogHostError(hr);\r
+\r
+                                               o_frames -= o_processed;\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
+                               else\r
+                               {\r
+                                       i_processed = 0;\r
+                                       goto fd_release_buffer_in;\r
+                               }\r
+\r
+fd_release_buffer_in:\r
+\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
+                               // break processing, input hasn't been accumulated yet\r
+                               if (i_processed == 0)\r
+                                       break;\r
+                       }\r
+               }\r
+#endif\r
        }\r
 \r
 thread_end:\r