]> Repos - portaudio/commitdiff
wasapi: improved audio quality stability in Shared mode if framesPerBuffer (from...
authordmitrykos <dmitrykos@0f58301d-fd10-0410-b4af-bbb618454e57>
Wed, 20 Oct 2010 17:00:54 +0000 (17:00 +0000)
committerdmitrykos <dmitrykos@0f58301d-fd10-0410-b4af-bbb618454e57>
Wed, 20 Oct 2010 17:00:54 +0000 (17:00 +0000)
src/hostapi/wasapi/pa_win_wasapi.c

index aa03748a5c4cb0a88b58b8562d395b50569ee6af..3b08abc9ba62603bc80936e2818f7c90753b6e75 100644 (file)
@@ -46,6 +46,7 @@
 #include <windows.h>\r
 #include <stdio.h>\r
 #include <process.h>\r
+#include <assert.h>\r
 #include <mmsystem.h>\r
 #include <mmreg.h>  // must be before other Wasapi headers\r
 #if defined(_MSC_VER) && (_MSC_VER >= 1400)\r
@@ -653,6 +654,11 @@ static UINT32 AlignFramesPerBuffer(UINT32 nFrames, UINT32 nSamplesPerSec, UINT32
 \r
        // align to packet size\r
        frame_bytes  = pAlignFunc(frame_bytes, HDA_PACKET_SIZE); // use ALIGN_FWD if bigger but safer period is more desired\r
+\r
+       // atlest 1 frame must be available\r
+       if (frame_bytes < HDA_PACKET_SIZE)\r
+               frame_bytes = HDA_PACKET_SIZE;\r
+\r
        nFrames      = frame_bytes / nBlockAlign;\r
        packets      = frame_bytes / HDA_PACKET_SIZE;\r
 \r
@@ -677,6 +683,19 @@ static UINT32 GetFramesSleepTime(UINT32 nFrames, UINT32 nSamplesPerSec)
        return (UINT32)(nDuration/REFTIMES_PER_MILLISEC/2);\r
 }\r
 \r
+// ------------------------------------------------------------------------------------------\r
+static UINT32 GetFramesSleepTimeMicroseconds(UINT32 nFrames, UINT32 nSamplesPerSec)\r
+{\r
+       REFERENCE_TIME nDuration;\r
+       if (nSamplesPerSec == 0)\r
+               return 0;\r
+#define REFTIMES_PER_SEC  10000000\r
+#define REFTIMES_PER_MILLISEC  10000\r
+       // Calculate the actual duration of the allocated buffer.\r
+       nDuration = (REFERENCE_TIME)((double)REFTIMES_PER_SEC * nFrames / nSamplesPerSec);\r
+       return (UINT32)(nDuration/10/2);\r
+}\r
+\r
 // ------------------------------------------------------------------------------------------\r
 static BOOL SetupAVRT()\r
 {\r
@@ -1371,20 +1390,20 @@ static void LogWAVEFORMATEXTENSIBLE(const WAVEFORMATEXTENSIBLE *in)
        {\r
        case WAVE_FORMAT_EXTENSIBLE: {\r
 \r
-               PRINT(("wFormatTag=WAVE_FORMAT_EXTENSIBLE\n"));\r
+               PRINT(("wFormatTag     =WAVE_FORMAT_EXTENSIBLE\n"));\r
 \r
                if (IsEqualGUID(&in->SubFormat, &pa_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT))\r
                {\r
-                       PRINT(("SubFormat=KSDATAFORMAT_SUBTYPE_IEEE_FLOAT\n"));\r
+                       PRINT(("SubFormat      =KSDATAFORMAT_SUBTYPE_IEEE_FLOAT\n"));\r
                }\r
                else\r
                if (IsEqualGUID(&in->SubFormat, &pa_KSDATAFORMAT_SUBTYPE_PCM))\r
                {\r
-                       PRINT(("SubFormat=KSDATAFORMAT_SUBTYPE_PCM\n"));\r
+                       PRINT(("SubFormat      =KSDATAFORMAT_SUBTYPE_PCM\n"));\r
                }\r
                else\r
                {\r
-                       PRINT(("SubFormat=CUSTOM GUID{%d:%d:%d:%d%d%d%d%d%d%d%d}\n",\r
+                       PRINT(("SubFormat      =CUSTOM GUID{%d:%d:%d:%d%d%d%d%d%d%d%d}\n",\r
                                                                                in->SubFormat.Data1,\r
                                                                                in->SubFormat.Data2,\r
                                                                                in->SubFormat.Data3,\r
@@ -1397,14 +1416,15 @@ static void LogWAVEFORMATEXTENSIBLE(const WAVEFORMATEXTENSIBLE *in)
                                                                                (int)in->SubFormat.Data4[6],\r
                                                                                (int)in->SubFormat.Data4[7]));\r
                }\r
-               PRINT(("Samples.wValidBitsPerSample=%d\n",  in->Samples.wValidBitsPerSample));\r
+               PRINT(("Samples.wValidBitsPerSample =%d\n",  in->Samples.wValidBitsPerSample));\r
                PRINT(("dwChannelMask  =0x%X\n",in->dwChannelMask));\r
 \r
                break; }\r
 \r
-       case WAVE_FORMAT_PCM:        PRINT(("wFormatTag=WAVE_FORMAT_PCM\n")); break;\r
-       case WAVE_FORMAT_IEEE_FLOAT: PRINT(("wFormatTag=WAVE_FORMAT_IEEE_FLOAT\n")); break;\r
-       default : PRINT(("wFormatTag=UNKNOWN(%d)\n",old->wFormatTag)); break;\r
+       case WAVE_FORMAT_PCM:        PRINT(("wFormatTag     =WAVE_FORMAT_PCM\n")); break;\r
+       case WAVE_FORMAT_IEEE_FLOAT: PRINT(("wFormatTag     =WAVE_FORMAT_IEEE_FLOAT\n")); break;\r
+       default: \r
+               PRINT(("wFormatTag     =UNKNOWN(%d)\n",old->wFormatTag)); break;\r
        }\r
 \r
        PRINT(("nChannels      =%d\n",old->nChannels));\r
@@ -3976,6 +3996,46 @@ static HRESULT PollGetOutputFramesAvailable(PaWasapiStream *stream, UINT32 *avai
        return hr;\r
 }\r
 \r
+// ------------------------------------------------------------------------------------------\r
+/*! \class ThreadSleepScheduler\r
+           Allows to emulate thread sleep of less than 1 millisecond under Windows. Scheduler\r
+                  calculates number of times the thread must run untill next sleep of 1 millisecond.\r
+                  It does not make thread sleeping for real number of microseconds but rather controls\r
+                  how many of imaginary microseconds the thread task can allow thread to sleep.\r
+*/\r
+typedef struct ThreadIdleScheduler\r
+{\r
+       UINT32 m_idle_microseconds; //!< number of microseconds to sleep\r
+       UINT32 m_next_sleep;        //!< next sleep round\r
+       UINT32 m_i;                                     //!< current round iterator position\r
+       UINT32 m_resolution;            //!< resolution in number of milliseconds\r
+}\r
+ThreadIdleScheduler;\r
+//! Setup scheduler.\r
+static void ThreadIdleScheduler_Setup(ThreadIdleScheduler *sched, UINT32 resolution, UINT32 microseconds)\r
+{\r
+       assert(microseconds != 0);\r
+       assert(resolution != 0);\r
+       assert((resolution * 1000) >= microseconds);\r
+\r
+       memset(sched, 0, sizeof(*sched));\r
+\r
+       sched->m_idle_microseconds = microseconds;\r
+       sched->m_resolution         = resolution;\r
+       sched->m_next_sleep         = (resolution * 1000) / microseconds;\r
+}\r
+//! Iterate and check if can sleep.\r
+static UINT32 ThreadIdleScheduler_NextSleep(ThreadIdleScheduler *sched)\r
+{\r
+       // advance and check if thread can sleep\r
+       if (++ sched->m_i == sched->m_next_sleep)\r
+       {\r
+               sched->m_i = 0;\r
+               return sched->m_resolution;\r
+       }\r
+       return 0;\r
+}\r
+\r
 // ------------------------------------------------------------------------------------------\r
 PA_THREAD_FUNC ProcThreadPoll(void *param)\r
 {\r
@@ -3984,6 +4044,7 @@ PA_THREAD_FUNC ProcThreadPoll(void *param)
     PaWasapiStream *stream = (PaWasapiStream *)param;\r
        PaWasapiHostProcessor defaultProcessor;\r
        INT32 i;\r
+       ThreadIdleScheduler scheduler;\r
 \r
        // Calculate the actual duration of the allocated buffer.\r
        DWORD sleep_ms     = 0;\r
@@ -4004,9 +4065,24 @@ PA_THREAD_FUNC ProcThreadPoll(void *param)
        {\r
                sleep_ms = (sleep_ms_in ? sleep_ms_in : sleep_ms_out);\r
        }\r
-       // Make sure not 0\r
+       // Make sure not 0, othervise use ThreadIdleScheduler\r
        if (sleep_ms == 0)\r
-               sleep_ms = 1;\r
+       {\r
+               sleep_ms_in  = GetFramesSleepTimeMicroseconds(stream->bufferProcessor.framesPerUserBuffer, stream->in.wavex.Format.nSamplesPerSec);\r
+               sleep_ms_out = GetFramesSleepTimeMicroseconds(stream->bufferProcessor.framesPerUserBuffer, stream->out.wavex.Format.nSamplesPerSec);\r
+\r
+               // Choose smallest\r
+               if ((sleep_ms_in != 0) && (sleep_ms_out != 0))\r
+                       sleep_ms = min(sleep_ms_in, sleep_ms_out);\r
+               else\r
+               {\r
+                       sleep_ms = (sleep_ms_in ? sleep_ms_in : sleep_ms_out);\r
+               }\r
+\r
+               // Setup thread sleep scheduler\r
+               ThreadIdleScheduler_Setup(&scheduler, 1, sleep_ms/* microseconds here actually */);\r
+               sleep_ms = 0;\r
+       }\r
 \r
     // Setup data processors\r
     defaultProcessor.processor = WaspiHostProcessingLoop;\r
@@ -4093,8 +4169,15 @@ PA_THREAD_FUNC ProcThreadPoll(void *param)
        if (!PA_WASAPI__IS_FULLDUPLEX(stream))\r
        {\r
                // Processing Loop\r
-               while (WaitForSingleObject(stream->hCloseRequest, sleep_ms) == WAIT_TIMEOUT)\r
+               UINT32 next_sleep = sleep_ms;\r
+               while (WaitForSingleObject(stream->hCloseRequest, next_sleep) == WAIT_TIMEOUT)\r
                {\r
+                       // Get next sleep time\r
+                       if (sleep_ms == 0)\r
+                       {\r
+                               next_sleep = ThreadIdleScheduler_NextSleep(&scheduler);\r
+                       }\r
+\r
                        for (i = 0; i < S_COUNT; ++i)\r
                        {\r
                                // Process S_INPUT/S_OUTPUT\r
@@ -4267,8 +4350,8 @@ PA_THREAD_FUNC ProcThreadPoll(void *param)
                }\r
 #else\r
                // Processing Loop\r
-               //sleep_ms = 1;\r
-               while (WaitForSingleObject(stream->hCloseRequest, sleep_ms) == WAIT_TIMEOUT)\r
+               UINT32 next_sleep = sleep_ms;\r
+               while (WaitForSingleObject(stream->hCloseRequest, next_sleep) == WAIT_TIMEOUT)\r
                {\r
                        UINT32 i_frames = 0, i_processed = 0;\r
                        BYTE *i_data = NULL, *o_data = NULL, *o_data_host = NULL;\r
@@ -4280,6 +4363,12 @@ PA_THREAD_FUNC ProcThreadPoll(void *param)
                        //if (stream->in.shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE)\r
                        //      sleep_ms = !sleep_ms;\r
 \r
+                       // Get next sleep time\r
+                       if (sleep_ms == 0)\r
+                       {\r
+                               next_sleep = ThreadIdleScheduler_NextSleep(&scheduler);\r
+                       }\r
+\r
                        // get available frames\r
                        if ((hr = PollGetOutputFramesAvailable(stream, &o_frames)) != S_OK)\r
                        {\r