#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
\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
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
{\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
(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
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
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
{\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
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
}\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
//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