UINT32 streamFlags; // AUDCLNT_STREAMFLAGS_EVENTCALLBACK, ...\r
UINT32 flags;\r
\r
+ // Buffers\r
+ UINT32 buffers; //!< number of buffers used (from host side)\r
+ UINT32 framesPerBuffer; //!< number of frames per 1 buffer\r
+ BOOL userBufferAndHostMatch;\r
+\r
// Used by blocking interface:\r
UINT32 prevTime; // time ms between calls of WriteStream\r
UINT32 prevSleep; // time ms to sleep from frames written in previous call\r
// event handles for event-driven processing mode\r
HANDLE event[S_COUNT];\r
\r
+ // buffer mode\r
+ PaUtilHostBufferSizeMode bufferMode;\r
+\r
// must be volatile to avoid race condition on user query while\r
// thread is being started\r
volatile BOOL running;\r
return paFormatIsSupported;\r
}\r
\r
+// ------------------------------------------------------------------------------------------\r
+static PaUint32 PaUtil_GetFramesPerHostBuffer(PaUint32 userFramesPerBuffer, PaTime suggestedLatency, double sampleRate, PaUint32 TimerJitterMs)\r
+{\r
+ PaUint32 frames = userFramesPerBuffer + max( userFramesPerBuffer, (PaUint32)(suggestedLatency * sampleRate) );\r
+ frames += (PaUint32)((sampleRate * 0.001) * TimerJitterMs);\r
+ return frames;\r
+}\r
+\r
// ------------------------------------------------------------------------------------------\r
static HRESULT CreateAudioClient(PaWasapiSubStream *pSubStream, PaWasapiDeviceInfo *info,\r
const PaStreamParameters *params, UINT32 framesPerLatency, double sampleRate, UINT32 streamFlags,\r
- BOOL blocking, BOOL output, PaError *pa_error)\r
+ BOOL blocking, BOOL output, BOOL full_duplex, PaError *pa_error)\r
{\r
PaError error;\r
HRESULT hr = S_OK;\r
UINT32 nFrames = 0;\r
+ UINT32 userFramesPerBuffer = framesPerLatency;\r
IAudioClient *pAudioClient = NULL;\r
double suggestedLatency = 0.0;\r
\r
}\r
}\r
\r
+#if 0\r
// Correct latency to default device period (in Exclusive mode this is 10ms) if user selected 0\r
- //suggestedLatency = (params->suggestedLatency < 0.001 ? nano100ToSeconds(info->DefaultDevicePeriod) : params->suggestedLatency);\r
suggestedLatency = params->suggestedLatency;\r
-\r
- // Add latency frames\r
+ // Add suggestd latency\r
framesPerLatency += MakeFramesFromHns(SecondsTonano100(suggestedLatency), pSubStream->wavex.Format.nSamplesPerSec);\r
+#else\r
+ // Calculate host buffer size\r
+ if ((pSubStream->shareMode != AUDCLNT_SHAREMODE_EXCLUSIVE) && \r
+ (!streamFlags || ((streamFlags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK) == 0)))\r
+ {\r
+ framesPerLatency = PaUtil_GetFramesPerHostBuffer(framesPerLatency, \r
+ params->suggestedLatency, pSubStream->wavex.Format.nSamplesPerSec, 0/*,\r
+ (streamFlags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK ? 0 : 1)*/);\r
+ }\r
+ else\r
+ {\r
+ // do nothing, work 1:1 with buffer (only polling allows to work with >1)\r
+ }\r
+#endif\r
+\r
+ // Count buffers (must be at least 1)\r
+ pSubStream->buffers = (userFramesPerBuffer ? framesPerLatency / userFramesPerBuffer : 0);\r
+ if (pSubStream->buffers == 0)\r
+ pSubStream->buffers = 1;\r
+\r
+ // Determine amount of buffers to be used:\r
+ // - Exclusive mode does not allow >1 buffers be used, e.g. GetBuffer call must acquire\r
+ // max buffer size and it all must be processed.\r
+ // - Full-duplex mode will lead to period difference, thus only 1.\r
+ // - Input mode, only 1, as WASAPI allows extraction of only 1 packet.\r
+ // - For Shared mode we use safe double buffering (2 buffers).\r
+ if ((pSubStream->shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE) || full_duplex/* && output*/)\r
+ {\r
+ pSubStream->userBufferAndHostMatch = 1;\r
+ pSubStream->buffers = 1;\r
+ }\r
\r
// Avoid 0 frames\r
if (framesPerLatency == 0)\r
}\r
}\r
\r
+ // Reset buffers count to single\r
+ pSubStream->buffers = 1;\r
+\r
// Calculate period\r
pSubStream->period = MakeHnsPeriod(nFrames, pSubStream->wavex.Format.nSamplesPerSec);\r
\r
}\r
\r
// Choose processing mode\r
- stream->in.streamFlags = AUDCLNT_STREAMFLAGS_EVENTCALLBACK;\r
+ stream->in.streamFlags = (stream->in.shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE ? AUDCLNT_STREAMFLAGS_EVENTCALLBACK : 0);\r
if (paWasapi->useWOW64Workaround)\r
stream->in.streamFlags = 0; // polling interface\r
else\r
\r
// Create Audio client\r
hr = CreateAudioClient(&stream->in, info, inputParameters, framesPerBuffer/*framesPerLatency*/,\r
- sampleRate, stream->in.streamFlags, (streamCallback == NULL), FALSE, &result);\r
+ sampleRate, stream->in.streamFlags, (streamCallback == NULL), FALSE, fullDuplex, &result);\r
if (hr != S_OK)\r
{\r
LogHostError(hr);\r
// Number of frames that are required at each period\r
stream->in.framesPerHostCallback = maxBufferSize;\r
\r
+ // Calculate frames per single buffer, if buffers > 1 then always framesPerBuffer\r
+ stream->in.framesPerBuffer = \r
+ (stream->in.userBufferAndHostMatch ? stream->in.framesPerHostCallback : framesPerBuffer);\r
+\r
// Calculate buffer latency\r
buffer_latency = (PaTime)maxBufferSize / stream->in.wavex.Format.nSamplesPerSec;\r
\r
// Append buffer latency to interface latency in shared mode (see GetStreamLatency notes)\r
stream->in.latency_seconds += buffer_latency;\r
\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
+ PRINT(("WASAPI::OpenStream(input): framesPerUser[ %d ] framesPerHost[ %d ] latency[ %.02fms ] exclusive[ %s ] wow64_fix[ %s ]\n", (UINT32)framesPerBuffer, (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
}\r
\r
// Choose processing mode\r
- stream->out.streamFlags = AUDCLNT_STREAMFLAGS_EVENTCALLBACK;\r
+ stream->out.streamFlags = (stream->out.shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE ? AUDCLNT_STREAMFLAGS_EVENTCALLBACK : 0);\r
if (paWasapi->useWOW64Workaround)\r
stream->out.streamFlags = 0; // polling interface\r
else\r
\r
// Create Audio client\r
hr = CreateAudioClient(&stream->out, info, outputParameters, framesPerBuffer/*framesPerLatency*/,\r
- sampleRate, stream->out.streamFlags, (streamCallback == NULL), TRUE, &result);\r
+ sampleRate, stream->out.streamFlags, (streamCallback == NULL), TRUE, fullDuplex, &result);\r
if (hr != S_OK)\r
{\r
LogHostError(hr);\r
// Number of frames that are required at each period\r
stream->out.framesPerHostCallback = maxBufferSize;\r
\r
+ // Calculate frames per single buffer, if buffers > 1 then always framesPerBuffer\r
+ stream->out.framesPerBuffer = \r
+ (stream->out.userBufferAndHostMatch ? stream->out.framesPerHostCallback : framesPerBuffer);\r
+\r
// Calculate buffer latency\r
buffer_latency = (PaTime)maxBufferSize / stream->out.wavex.Format.nSamplesPerSec;\r
\r
// Append buffer latency to interface latency in shared mode (see GetStreamLatency notes)\r
stream->out.latency_seconds += buffer_latency;\r
\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
+ PRINT(("WASAPI::OpenStream(output): framesPerUser[ %d ] framesPerHost[ %d ] latency[ %.02fms ] exclusive[ %s ] wow64_fix[ %s ]\n", (UINT32)framesPerBuffer, (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
}*/\r
}\r
\r
- framesPerHostCallback = (outputParameters) ? stream->out.framesPerHostCallback:\r
- stream->in.framesPerHostCallback;\r
+ // Calculate frames per host for processor\r
+ framesPerHostCallback = (outputParameters ? stream->out.framesPerBuffer : stream->in.framesPerBuffer);\r
\r
// Choose correct mode of buffer processing:\r
// Exclusive/Shared non paWinWasapiPolling mode: paUtilFixedHostBufferSize - always fixed\r
- // Exclusive/Shared paWinWasapiPolling mode: paUtilBoundedHostBufferSize - may vary\r
+ // Exclusive/Shared paWinWasapiPolling mode: paUtilBoundedHostBufferSize - may vary for Exclusive or Full-duplex\r
bufferMode = paUtilFixedHostBufferSize;\r
- if (inputParameters /*&& // !!! WASAPI IAudioCaptureClient::GetBuffer extracts not number of frames but 1 packet, thus we always must adapt\r
- (!stream->in.streamFlags || ((stream->in.streamFlags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK) == 0))*/)\r
+ if (inputParameters) // !!! WASAPI IAudioCaptureClient::GetBuffer extracts not number of frames but 1 packet, thus we always must adapt\r
bufferMode = paUtilBoundedHostBufferSize;\r
else\r
- if (outputParameters &&\r
- (!stream->out.streamFlags || ((stream->out.streamFlags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK) == 0)))\r
- bufferMode = paUtilBoundedHostBufferSize;\r
+ if (outputParameters)\r
+ {\r
+ if ((stream->out.buffers == 1) && \r
+ (!stream->out.streamFlags || ((stream->out.streamFlags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK) == 0)))\r
+ bufferMode = paUtilBoundedHostBufferSize;\r
+ }\r
+ stream->bufferMode = bufferMode;\r
\r
// Initialize buffer processor\r
result = PaUtil_InitializeBufferProcessor(\r
// Set Input latency\r
stream->streamRepresentation.streamInfo.inputLatency =\r
PaUtil_GetBufferProcessorInputLatency(&stream->bufferProcessor)\r
- + ((inputParameters)?stream->in.latency_seconds :0);\r
+ + ((inputParameters)?stream->in.latency_seconds : 0);\r
\r
// Set Output latency\r
stream->streamRepresentation.streamInfo.outputLatency =\r
PaUtil_GetBufferProcessorOutputLatency(&stream->bufferProcessor)\r
- + ((outputParameters)?stream->out.latency_seconds :0);\r
+ + ((outputParameters)?stream->out.latency_seconds : 0);\r
\r
// Set SR\r
stream->streamRepresentation.streamInfo.sampleRate = sampleRate;\r
}\r
\r
// Preload buffer before start\r
- if ((hr = ProcessOutputBuffer(stream, processor, stream->out.framesPerHostCallback)) != S_OK)\r
+ if ((hr = ProcessOutputBuffer(stream, processor, stream->out.framesPerBuffer)) != S_OK)\r
{\r
LogHostError(hr);\r
goto thread_error;\r
case WAIT_OBJECT_0 + S_OUTPUT: {\r
if (stream->rclient == NULL)\r
break;\r
- ProcessOutputBuffer(stream, processor, stream->out.framesPerHostCallback);\r
+ ProcessOutputBuffer(stream, processor, stream->out.framesPerBuffer);\r
break; }\r
}\r
}\r
\r
// Calculate the actual duration of the allocated buffer.\r
DWORD sleep_ms = 0;\r
- DWORD sleep_ms_in = GetFramesSleepTime(stream->in.framesPerHostCallback, stream->in.wavex.Format.nSamplesPerSec);\r
- DWORD sleep_ms_out = GetFramesSleepTime(stream->out.framesPerHostCallback, stream->out.wavex.Format.nSamplesPerSec);\r
+ DWORD sleep_ms_in = GetFramesSleepTime(stream->in.framesPerBuffer, stream->in.wavex.Format.nSamplesPerSec);\r
+ DWORD sleep_ms_out = GetFramesSleepTime(stream->out.framesPerBuffer, stream->out.wavex.Format.nSamplesPerSec);\r
+ \r
+ // Adjust polling time\r
+ if (stream->bufferMode != paUtilFixedHostBufferSize)\r
+ {\r
+ sleep_ms_in = GetFramesSleepTime(stream->bufferProcessor.framesPerUserBuffer, stream->in.wavex.Format.nSamplesPerSec);\r
+ sleep_ms_out = GetFramesSleepTime(stream->bufferProcessor.framesPerUserBuffer, stream->out.wavex.Format.nSamplesPerSec);\r
+ }\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
+ // Make sure not 0\r
+ if (sleep_ms == 0)\r
+ sleep_ms = 1;\r
\r
// Setup data processors\r
defaultProcessor.processor = WaspiHostProcessingLoop;\r
// when in full-duplex mode as it requires input processing as well\r
if (!PA_WASAPI__IS_FULLDUPLEX(stream))\r
{\r
- if ((hr = ProcessOutputBuffer(stream, processor, stream->out.framesPerHostCallback)) != S_OK)\r
+ if ((hr = ProcessOutputBuffer(stream, processor, stream->out.framesPerBuffer)) != S_OK)\r
{\r
LogHostError(hr);\r
goto thread_error;\r
LogHostError(hr);\r
break;\r
}\r
-\r
+ \r
// output\r
- if (frames != 0)\r
- ProcessOutputBuffer(stream, processor, frames);\r
+ if (stream->bufferMode == paUtilFixedHostBufferSize)\r
+ {\r
+ if (frames >= stream->out.framesPerBuffer)\r
+ ProcessOutputBuffer(stream, processor, stream->out.framesPerBuffer);\r
+ }\r
+ else\r
+ {\r
+ if (frames != 0)\r
+ ProcessOutputBuffer(stream, processor, frames);\r
+ }\r
\r
break; }\r
}\r