return nFrames;\r
}\r
\r
+// Aligning function type\r
+typedef UINT32 (*ALIGN_FUNC) (UINT32 v, UINT32 align);\r
+\r
// ------------------------------------------------------------------------------------------\r
// Aligns 'v' backwards\r
static UINT32 ALIGN_BWD(UINT32 v, UINT32 align)\r
// Aligns WASAPI buffer to 128 byte packet boundary. HD Audio will fail to play if buffer\r
// is misaligned. This problem was solved in Windows 7 were AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED\r
// is thrown although we must align for Vista anyway.\r
-static UINT32 AlignFramesPerBuffer(UINT32 nFrames, UINT32 nSamplesPerSec, UINT32 nBlockAlign)\r
+static UINT32 AlignFramesPerBuffer(UINT32 nFrames, UINT32 nSamplesPerSec, UINT32 nBlockAlign, \r
+ ALIGN_FUNC pAlignFunc)\r
{\r
#define HDA_PACKET_SIZE (128)\r
\r
long packets;\r
\r
// align to packet size\r
- frame_bytes = ALIGN_FWD(frame_bytes, HDA_PACKET_SIZE); // use ALIGN_FWD if bigger but safer period is more desired\r
+ frame_bytes = pAlignFunc(frame_bytes, HDA_PACKET_SIZE); // use ALIGN_FWD if bigger but safer period is more desired\r
nFrames = frame_bytes / nBlockAlign;\r
packets = frame_bytes / HDA_PACKET_SIZE;\r
\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, BOOL full_duplex, PaError *pa_error)\r
+static void _RecalculateBuffersCount(PaWasapiSubStream *sub, UINT32 userFramesPerBuffer, UINT32 framesPerLatency, BOOL fullDuplex)\r
+{\r
+ // Count buffers (must be at least 1)\r
+ sub->buffers = (userFramesPerBuffer ? framesPerLatency / userFramesPerBuffer : 0);\r
+ if (sub->buffers == 0)\r
+ sub->buffers = 1;\r
+\r
+ // Determine amount of buffers used:\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 double buffering.\r
+ if ((sub->shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE) || fullDuplex)\r
+ {\r
+ // Exclusive mode does not allow >1 buffers be used for Event interface, e.g. GetBuffer \r
+ // call must acquire max buffer size and it all must be processed.\r
+ if (sub->streamFlags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK)\r
+ sub->userBufferAndHostMatch = 1;\r
+ \r
+ // Use paUtilBoundedHostBufferSize because exclusive mode will starve and produce\r
+ // bad quality of audio\r
+ sub->buffers = 1;\r
+ }\r
+}\r
+\r
+// ------------------------------------------------------------------------------------------\r
+static void _CalculateAlignedPeriod(PaWasapiSubStream *pSub, UINT32 *nFramesPerLatency, \r
+ ALIGN_FUNC pAlignFunc)\r
+{\r
+ // Align frames to HD Audio packet size of 128 bytes for Exclusive mode only.\r
+ // Not aligning on Windows Vista will cause Event timeout, although Windows 7 will\r
+ // return AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED error to realign buffer. Aligning is necessary\r
+ // for Exclusive mode only! when audio data is feeded directly to hardware.\r
+ if (pSub->shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE)\r
+ {\r
+ (*nFramesPerLatency) = AlignFramesPerBuffer((*nFramesPerLatency),\r
+ pSub->wavex.Format.nSamplesPerSec, pSub->wavex.Format.nBlockAlign, pAlignFunc);\r
+ }\r
+\r
+ // Calculate period\r
+ pSub->period = MakeHnsPeriod((*nFramesPerLatency), pSub->wavex.Format.nSamplesPerSec);\r
+}\r
+\r
+// ------------------------------------------------------------------------------------------\r
+static HRESULT CreateAudioClient(PaWasapiSubStream *pSub, PaWasapiDeviceInfo *pInfo,\r
+ const PaStreamParameters *params, UINT32 framesPerLatency, double sampleRate,\r
+ BOOL blocking, BOOL output, BOOL fullDuplex, 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
+ HRESULT hr;\r
+ const UINT32 userFramesPerBuffer = framesPerLatency;\r
+ IAudioClient *audioClient = NULL;\r
\r
- if (!pSubStream || !info || !params)\r
+ // Validate parameters\r
+ if (!pSub || !pInfo || !params)\r
return E_POINTER;\r
if ((UINT32)sampleRate == 0)\r
return E_INVALIDARG;\r
\r
// Get the audio client\r
- hr = IMMDevice_Activate(info->device, &pa_IID_IAudioClient, CLSCTX_ALL, NULL, (void **)&pAudioClient);\r
+ hr = IMMDevice_Activate(pInfo->device, &pa_IID_IAudioClient, CLSCTX_ALL, NULL, (void **)&audioClient);\r
if (hr != S_OK)\r
{\r
LogHostError(hr);\r
}\r
\r
// Get closest format\r
- if ((error = GetClosestFormat(pAudioClient, sampleRate, params, pSubStream->shareMode, &pSubStream->wavex, output)) != paFormatIsSupported)\r
+ if ((error = GetClosestFormat(audioClient, sampleRate, params, pSub->shareMode, &pSub->wavex, output)) != paFormatIsSupported)\r
{\r
if (pa_error)\r
(*pa_error) = error;\r
}\r
\r
// Check for Mono >> Stereo workaround\r
- if ((params->channelCount == 1) && (pSubStream->wavex.Format.nChannels == 2))\r
+ if ((params->channelCount == 1) && (pSub->wavex.Format.nChannels == 2))\r
{\r
if (blocking)\r
{\r
}\r
\r
// select mixer\r
- pSubStream->monoMixer = _GetMonoToStereoMixer(params->sampleFormat, (info->flow == eRender ? MIX_DIR__1TO2 : MIX_DIR__2TO1));\r
- if (pSubStream->monoMixer == NULL)\r
+ pSub->monoMixer = _GetMonoToStereoMixer(params->sampleFormat, (pInfo->flow == eRender ? MIX_DIR__1TO2 : MIX_DIR__2TO1));\r
+ if (pSub->monoMixer == NULL)\r
{\r
LogHostError(hr = AUDCLNT_E_UNSUPPORTED_FORMAT);\r
goto done; // fail, no mixer for format\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;\r
// Add suggestd latency\r
- framesPerLatency += MakeFramesFromHns(SecondsTonano100(suggestedLatency), pSubStream->wavex.Format.nSamplesPerSec);\r
+ framesPerLatency += MakeFramesFromHns(SecondsTonano100(params->suggestedLatency), pSub->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
+ if ((pSub->shareMode != AUDCLNT_SHAREMODE_EXCLUSIVE) && \r
+ (!pSub->streamFlags || ((pSub->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
+ framesPerLatency = PaUtil_GetFramesPerHostBuffer(userFramesPerBuffer, \r
+ params->suggestedLatency, pSub->wavex.Format.nSamplesPerSec, 0/*,\r
+ (pSub->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
+ REFERENCE_TIME overall;\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
+ // Work 1:1 with user buffer (only polling allows to use >1)\r
+ framesPerLatency += MakeFramesFromHns(SecondsTonano100(params->suggestedLatency), pSub->wavex.Format.nSamplesPerSec);\r
+ \r
+ // Use Polling if overall latency is > 5ms as it allows to use 100% CPU in a callback,\r
+ // or user specified latency parameter\r
+ overall = MakeHnsPeriod(framesPerLatency, pSub->wavex.Format.nSamplesPerSec);\r
+ if ((overall > 50000) || (params->suggestedLatency > 0))\r
+ {\r
+ framesPerLatency = PaUtil_GetFramesPerHostBuffer(userFramesPerBuffer, \r
+ params->suggestedLatency, pSub->wavex.Format.nSamplesPerSec, 0/*,\r
+ (streamFlags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK ? 0 : 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
+ // Use Polling interface\r
+ pSub->streamFlags &= ~AUDCLNT_STREAMFLAGS_EVENTCALLBACK;\r
+ }\r
}\r
+#endif\r
\r
// Avoid 0 frames\r
if (framesPerLatency == 0)\r
- framesPerLatency = MakeFramesFromHns(info->DefaultDevicePeriod, pSubStream->wavex.Format.nSamplesPerSec);\r
+ framesPerLatency = MakeFramesFromHns(pInfo->DefaultDevicePeriod, pSub->wavex.Format.nSamplesPerSec);\r
\r
- // Align frames to HD Audio packet size of 128 bytes for Exclusive mode only.\r
- // Not aligning on Windows Vista will cause Event timeout, although Windows 7 will\r
- // return AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED error to realign buffer. Aligning is necessary\r
- // for Exclusive mode only! when audio data is feeded directly to hardware.\r
- if (pSubStream->shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE)\r
+ // Calculate aligned period\r
+ _CalculateAlignedPeriod(pSub, &framesPerLatency, ALIGN_FWD);\r
+\r
+ /*! Enforce min/max period for device in Shared mode to avoid bad audio quality.\r
+ Avoid doing so for Exclusive mode as alignment will suffer.\r
+ */\r
+ if (pSub->shareMode == AUDCLNT_SHAREMODE_SHARED)\r
{\r
- framesPerLatency = AlignFramesPerBuffer(framesPerLatency,\r
- pSubStream->wavex.Format.nSamplesPerSec, pSubStream->wavex.Format.nBlockAlign);\r
+ if (pSub->period < pInfo->DefaultDevicePeriod)\r
+ pSub->period = pInfo->DefaultDevicePeriod;\r
}\r
-\r
- // Calculate period\r
- pSubStream->period = MakeHnsPeriod(framesPerLatency, pSubStream->wavex.Format.nSamplesPerSec);\r
-\r
- // Enforce min/max period for device in Shared mode to avoid distorted sound.\r
- // Avoid doing so for Exclusive mode as alignment will suffer. Exclusive mode processes\r
- // big buffers without problem. Push Exclusive beyond limits if possible.\r
- if (pSubStream->shareMode == AUDCLNT_SHAREMODE_SHARED)\r
+ else\r
{\r
- if (pSubStream->period < info->DefaultDevicePeriod)\r
- pSubStream->period = info->DefaultDevicePeriod;\r
+ if (pSub->period < pInfo->MinimumDevicePeriod)\r
+ pSub->period = pInfo->MinimumDevicePeriod;\r
}\r
\r
- // Windows 7 does not allow to set latency lower than minimal device period and will\r
- // return error: AUDCLNT_E_INVALID_DEVICE_PERIOD. Under Vista though this error is not implemented\r
- // allowing to hack WASAPI device with lower period resulting in possibly lower latency.\r
- if (GetWindowsVersion() & WINDOWS_7_SERVER2008R2_AND_UP)\r
+ /*! Windows 7 does not allow to set latency lower than minimal device period and will\r
+ return error: AUDCLNT_E_INVALID_DEVICE_PERIOD. Under Vista we enforce the same behavior\r
+ manually for unified behavior on all platforms.\r
+ */\r
{\r
- if (pSubStream->period < info->MinimumDevicePeriod)\r
- pSubStream->period = info->MinimumDevicePeriod;\r
-\r
- /*\r
- AUDCLNT_E_BUFFER_SIZE_ERROR: Applies to Windows 7 and later.\r
+ /*! AUDCLNT_E_BUFFER_SIZE_ERROR: Applies to Windows 7 and later.\r
Indicates that the buffer duration value requested by an exclusive-mode client is \r
out of range. The requested duration value for pull mode must not be greater than \r
500 milliseconds; for push mode the duration value must not be greater than 2 seconds.\r
*/\r
- if (pSubStream->shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE)\r
+ if (pSub->shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE)\r
{\r
-static const REFERENCE_TIME MAX_BUFFER_EVENT_DURATION = 500 * 10000;\r
-static const REFERENCE_TIME MAX_BUFFER_POLL_DURATION = 2000 * 10000;\r
+ static const REFERENCE_TIME MAX_BUFFER_EVENT_DURATION = 500 * 10000;\r
+ static const REFERENCE_TIME MAX_BUFFER_POLL_DURATION = 2000 * 10000;\r
\r
- if (streamFlags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK) // pull mode, max 500ms\r
+ if (pSub->streamFlags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK) // pull mode, max 500ms\r
{\r
- if (pSubStream->period > MAX_BUFFER_EVENT_DURATION)\r
- pSubStream->period = MAX_BUFFER_EVENT_DURATION;\r
+ if (pSub->period > MAX_BUFFER_EVENT_DURATION)\r
+ {\r
+ pSub->period = MAX_BUFFER_EVENT_DURATION;\r
+\r
+ // Recalculate aligned period\r
+ framesPerLatency = MakeFramesFromHns(pSub->period, pSub->wavex.Format.nSamplesPerSec);\r
+ _CalculateAlignedPeriod(pSub, &framesPerLatency, ALIGN_BWD);\r
+ }\r
}\r
- else // push mode, max 2000ms\r
+ else // push mode, max 2000ms\r
{\r
- if (pSubStream->period > MAX_BUFFER_POLL_DURATION)\r
- pSubStream->period = MAX_BUFFER_POLL_DURATION;\r
+ if (pSub->period > MAX_BUFFER_POLL_DURATION)\r
+ {\r
+ pSub->period = MAX_BUFFER_POLL_DURATION;\r
+\r
+ // Recalculate aligned period\r
+ framesPerLatency = MakeFramesFromHns(pSub->period, pSub->wavex.Format.nSamplesPerSec);\r
+ _CalculateAlignedPeriod(pSub, &framesPerLatency, ALIGN_BWD);\r
+ }\r
}\r
}\r
}\r
\r
// Open the stream and associate it with an audio session\r
- hr = IAudioClient_Initialize(pAudioClient,\r
- pSubStream->shareMode,\r
- streamFlags/*AUDCLNT_STREAMFLAGS_EVENTCALLBACK*/,\r
- pSubStream->period,\r
- (pSubStream->shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE ? pSubStream->period : 0),\r
- &pSubStream->wavex.Format,\r
+ hr = IAudioClient_Initialize(audioClient,\r
+ pSub->shareMode,\r
+ pSub->streamFlags,\r
+ pSub->period,\r
+ (pSub->shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE ? pSub->period : 0),\r
+ &pSub->wavex.Format,\r
NULL);\r
\r
- // This would mean too low latency\r
+ /*! WASAPI is tricky on large device buffer, sometimes 2000ms can be allocated sometimes\r
+ less. There is no known guaranteed level thus we make subsequent tries by decreasing \r
+ buffer by 100ms per try.\r
+ */\r
+ while ((hr == E_OUTOFMEMORY) && (pSub->period > (100 * 10000)))\r
+ {\r
+ PRINT(("WASAPI: CreateAudioClient: decreasing buffer size to %d milliseconds\n", (pSub->period / 10000)));\r
+\r
+ // Decrease by 100ms and try again\r
+ pSub->period -= (100 * 10000);\r
+\r
+ // Recalculate aligned period\r
+ framesPerLatency = MakeFramesFromHns(pSub->period, pSub->wavex.Format.nSamplesPerSec);\r
+ _CalculateAlignedPeriod(pSub, &framesPerLatency, ALIGN_BWD);\r
+\r
+ // Release the previous allocations\r
+ SAFE_RELEASE(audioClient);\r
+\r
+ // Create a new audio client\r
+ hr = IMMDevice_Activate(pInfo->device, &pa_IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&audioClient);\r
+ if (hr != S_OK)\r
+ {\r
+ LogHostError(hr);\r
+ goto done;\r
+ }\r
+\r
+ // Open the stream and associate it with an audio session\r
+ hr = IAudioClient_Initialize(audioClient,\r
+ pSub->shareMode,\r
+ pSub->streamFlags,\r
+ pSub->period,\r
+ (pSub->shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE ? pSub->period : 0),\r
+ &pSub->wavex.Format,\r
+ NULL);\r
+ }\r
+\r
+ /*! WASAPI buffer size failure. Fallback to using default size.\r
+ */\r
if (hr == AUDCLNT_E_BUFFER_SIZE_ERROR)\r
{\r
- PRINT(("WASAPI: CreateAudioClient: correcting buffer size to device minimum\n"));\r
+ // Use default\r
+ pSub->period = pInfo->DefaultDevicePeriod;\r
\r
- // User was trying to set lowest possible, so set lowest device possible\r
- pSubStream->period = info->MinimumDevicePeriod;\r
+ PRINT(("WASAPI: CreateAudioClient: correcting buffer size to device default\n"));\r
\r
// Release the previous allocations\r
- SAFE_RELEASE(pAudioClient);\r
+ SAFE_RELEASE(audioClient);\r
\r
// Create a new audio client\r
- hr = IMMDevice_Activate(info->device, &pa_IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&pAudioClient);\r
+ hr = IMMDevice_Activate(pInfo->device, &pa_IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&audioClient);\r
if (hr != S_OK)\r
{\r
LogHostError(hr);\r
}\r
\r
// Open the stream and associate it with an audio session\r
- hr = IAudioClient_Initialize(pAudioClient,\r
- pSubStream->shareMode,\r
- streamFlags/*AUDCLNT_STREAMFLAGS_EVENTCALLBACK*/,\r
- pSubStream->period,\r
- (pSubStream->shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE ? pSubStream->period : 0),\r
- &pSubStream->wavex.Format,\r
+ hr = IAudioClient_Initialize(audioClient,\r
+ pSub->shareMode,\r
+ pSub->streamFlags,\r
+ pSub->period,\r
+ (pSub->shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE ? pSub->period : 0),\r
+ &pSub->wavex.Format,\r
NULL);\r
}\r
\r
- // If the requested buffer size is not aligned...\r
+ /*! If the requested buffer size is not aligned. Can be triggered by Windows 7 and up.\r
+ Should not be be triggered ever as we do align buffers always with _CalculateAlignedPeriod.\r
+ */\r
if (hr == AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED)\r
{\r
- PRINT(("WASAPI: CreateAudioClient: aligning buffer size\n"));\r
+ UINT32 frames = 0;\r
\r
// Get the next aligned frame\r
- hr = IAudioClient_GetBufferSize(pAudioClient, &nFrames);\r
+ hr = IAudioClient_GetBufferSize(audioClient, &frames);\r
if (hr != S_OK)\r
{\r
LogHostError(hr);\r
goto done;\r
}\r
\r
+ PRINT(("WASAPI: CreateAudioClient: aligning buffer size to % frames\n", frames));\r
+\r
// Release the previous allocations\r
- SAFE_RELEASE(pAudioClient);\r
+ SAFE_RELEASE(audioClient);\r
\r
// Create a new audio client\r
- hr = IMMDevice_Activate(info->device, &pa_IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&pAudioClient);\r
+ hr = IMMDevice_Activate(pInfo->device, &pa_IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&audioClient);\r
if (hr != S_OK)\r
{\r
LogHostError(hr);\r
}\r
\r
// Get closest format\r
- if ((error = GetClosestFormat(pAudioClient, sampleRate, params, pSubStream->shareMode, &pSubStream->wavex, output)) != paFormatIsSupported)\r
+ if ((error = GetClosestFormat(audioClient, sampleRate, params, pSub->shareMode, &pSub->wavex, output)) != paFormatIsSupported)\r
{\r
if (pa_error)\r
(*pa_error) = error;\r
}\r
\r
// Check for Mono >> Stereo workaround\r
- if ((params->channelCount == 1) && (pSubStream->wavex.Format.nChannels == 2))\r
+ if ((params->channelCount == 1) && (pSub->wavex.Format.nChannels == 2))\r
{\r
if (blocking)\r
{\r
goto done; // fail, blocking mode not supported\r
}\r
\r
- // select mixer\r
- pSubStream->monoMixer = _GetMonoToStereoMixer(params->sampleFormat, (info->flow == eRender ? MIX_DIR__1TO2 : MIX_DIR__2TO1));\r
- if (pSubStream->monoMixer == NULL)\r
+ // Select mixer\r
+ pSub->monoMixer = _GetMonoToStereoMixer(params->sampleFormat, (pInfo->flow == eRender ? MIX_DIR__1TO2 : MIX_DIR__2TO1));\r
+ if (pSub->monoMixer == NULL)\r
{\r
LogHostError(hr = AUDCLNT_E_UNSUPPORTED_FORMAT);\r
goto done; // fail, no mixer for format\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
- // Windows 7 does not allow to set latency lower than minimal device period and will\r
- // return error: AUDCLNT_E_INVALID_DEVICE_PERIOD. Under Vista though this error is not implemented\r
- // allowing to hack WASAPI device with lower period resulting in possibly lower latency.\r
- if (GetWindowsVersion() & WINDOWS_7_SERVER2008R2_AND_UP)\r
- {\r
- if (pSubStream->period < info->MinimumDevicePeriod)\r
- pSubStream->period = info->MinimumDevicePeriod;\r
- }\r
+ pSub->period = MakeHnsPeriod(frames, pSub->wavex.Format.nSamplesPerSec);\r
\r
// Open the stream and associate it with an audio session\r
- hr = IAudioClient_Initialize(pAudioClient,\r
- pSubStream->shareMode,\r
- streamFlags/*AUDCLNT_STREAMFLAGS_EVENTCALLBACK*/,\r
- pSubStream->period,\r
- (pSubStream->shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE ? pSubStream->period : 0),\r
- &pSubStream->wavex.Format,\r
+ hr = IAudioClient_Initialize(audioClient,\r
+ pSub->shareMode,\r
+ pSub->streamFlags,\r
+ pSub->period,\r
+ (pSub->shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE ? pSub->period : 0),\r
+ &pSub->wavex.Format,\r
NULL);\r
if (hr != S_OK)\r
{\r
goto done;\r
}\r
\r
- // Set result\r
- pSubStream->client = pAudioClient;\r
- IAudioClient_AddRef(pSubStream->client);\r
+ // Set client\r
+ pSub->client = audioClient;\r
+ IAudioClient_AddRef(pSub->client);\r
+\r
+ // Recalculate buffers count\r
+ _RecalculateBuffersCount(pSub, \r
+ userFramesPerBuffer, \r
+ MakeFramesFromHns(pSub->period, pSub->wavex.Format.nSamplesPerSec), \r
+ fullDuplex);\r
\r
done:\r
\r
// Clean up\r
- SAFE_RELEASE(pAudioClient);\r
+ SAFE_RELEASE(audioClient);\r
return hr;\r
}\r
\r
\r
// Create Audio client\r
hr = CreateAudioClient(&stream->in, info, inputParameters, framesPerBuffer/*framesPerLatency*/,\r
- sampleRate, stream->in.streamFlags, (streamCallback == NULL), FALSE, fullDuplex, &result);\r
+ sampleRate, (streamCallback == NULL), FALSE, fullDuplex, &result);\r
if (hr != S_OK)\r
{\r
LogHostError(hr);\r
\r
// Create Audio client\r
hr = CreateAudioClient(&stream->out, info, outputParameters, framesPerBuffer/*framesPerLatency*/,\r
- sampleRate, stream->out.streamFlags, (streamCallback == NULL), TRUE, fullDuplex, &result);\r
+ sampleRate, (streamCallback == NULL), TRUE, fullDuplex, &result);\r
if (hr != S_OK)\r
{\r
LogHostError(hr);\r