// this is the REAL number of devices, whether they are usefull to PA or not!
UINT32 deviceCount;
- WCHAR defaultRenderer[MAX_STR_LEN];
- WCHAR defaultCapturer[MAX_STR_LEN];
-
PaWasapiDeviceInfo *devInfo;
// is TRUE when WOW64 Vista/7 Workaround is needed
timer->granularity = 0;
return FALSE;
}
+#else
+ (void)timer;
+ (void)granularity;
#endif
return TRUE;
PRINT(("RestoreSystemTimer: timeEndPeriod(1) failed!\n"));
}
}
+#else
+ (void)timer;
#endif
}
//! Get high-resolution time in milliseconds (using QPC by default).
static inline LONGLONG SystemTimer_GetTime(SystemTimer *timer)
{
+ (void)timer;
+
// QPC: https://docs.microsoft.com/en-us/windows/win32/sysinfo/acquiring-high-resolution-time-stamps
if (g_SystemTimerUseQpc)
{
}
// ------------------------------------------------------------------------------------------
-static PaError CreateDeviceList(PaWasapiHostApiRepresentation *paWasapi, PaHostApiIndex hostApiIndex)
+static void FillBaseDeviceInfo(PaDeviceInfo *deviceInfo, PaHostApiIndex hostApiIndex)
+{
+ deviceInfo->structVersion = 2;
+ deviceInfo->hostApi = hostApiIndex;
+}
+
+// ------------------------------------------------------------------------------------------
+static PaError FillInactiveDeviceInfo(PaWasapiHostApiRepresentation *paWasapi, PaDeviceInfo *deviceInfo)
+{
+ if (deviceInfo->name == NULL)
+ deviceInfo->name = (char *)PaUtil_GroupAllocateMemory(paWasapi->allocations, 1);
+
+ if (deviceInfo->name != NULL)
+ {
+ ((char *)deviceInfo->name)[0] = 0;
+ }
+ else
+ return paInsufficientMemory;
+
+ return paNoError;
+}
+
+// ------------------------------------------------------------------------------------------
+static PaError FillDeviceInfo(PaWasapiHostApiRepresentation *paWasapi, void *pEndPoints, INT32 index, const WCHAR *defaultRenderer,
+ const WCHAR *defaultCapturer, PaDeviceInfo *deviceInfo, PaWasapiDeviceInfo *wasapiDeviceInfo)
{
+ HRESULT hr;
+ PaError result;
PaUtilHostApiRepresentation *hostApi = (PaUtilHostApiRepresentation *)paWasapi;
- PaError result = paNoError;
- PaDeviceInfo *deviceInfoArray;
- HRESULT hr = S_OK;
- UINT i;
+
#ifndef PA_WINRT
- IMMDeviceCollection* pEndPoints = NULL;
- IMMDeviceEnumerator *pEnumerator = NULL;
-#endif
- IAudioClient *tmpClient;
+ hr = IMMDeviceCollection_Item((IMMDeviceCollection *)pEndPoints, index, &wasapiDeviceInfo->device);
+ IF_FAILED_INTERNAL_ERROR_JUMP(hr, result, error);
- // Make sure device list empty
- if ((paWasapi->deviceCount != 0) || (hostApi->info.deviceCount != 0))
- return paInternalError;
+ // getting ID
+ {
+ WCHAR *pszDeviceId;
-#ifndef PA_WINRT
- hr = CoCreateInstance(&pa_CLSID_IMMDeviceEnumerator, NULL, CLSCTX_INPROC_SERVER,
- &pa_IID_IMMDeviceEnumerator, (void **)&pEnumerator);
-
- // We need to set the result to a value otherwise we will return paNoError
- // [IF_FAILED_JUMP(hResult, error);]
+ hr = IMMDevice_GetId(wasapiDeviceInfo->device, &pszDeviceId);
+ IF_FAILED_INTERNAL_ERROR_JUMP(hr, result, error);
+
+ wcsncpy(wasapiDeviceInfo->szDeviceID, pszDeviceId, MAX_STR_LEN - 1);
+ CoTaskMemFree(pszDeviceId);
+
+ if ((defaultCapturer != NULL) && (lstrcmpW(wasapiDeviceInfo->szDeviceID, defaultCapturer) == 0))
+ hostApi->info.defaultInputDevice = hostApi->info.deviceCount;
+
+ if ((defaultRenderer != NULL) && (lstrcmpW(wasapiDeviceInfo->szDeviceID, defaultRenderer) == 0))
+ hostApi->info.defaultOutputDevice = hostApi->info.deviceCount;
+ }
+
+ hr = IMMDevice_GetState(wasapiDeviceInfo->device, &wasapiDeviceInfo->state);
IF_FAILED_INTERNAL_ERROR_JUMP(hr, result, error);
- // getting default device ids in the eMultimedia "role"
+ if (wasapiDeviceInfo->state != DEVICE_STATE_ACTIVE)
+ {
+ PRINT(("WASAPI device: %d is not currently available (state:%d)\n", index, wasapiDeviceInfo->state));
+ }
+
+ // Get basic device info
{
+ IPropertyStore *pProperty;
+ IMMEndpoint *endpoint;
+ PROPVARIANT value;
+
+ hr = IMMDevice_OpenPropertyStore(wasapiDeviceInfo->device, STGM_READ, &pProperty);
+ IF_FAILED_INTERNAL_ERROR_JUMP(hr, result, error);
+
+ // "Friendly" Name
{
- IMMDevice *defaultRenderer = NULL;
- hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(pEnumerator, eRender, eMultimedia, &defaultRenderer);
- if (hr != S_OK)
+ PropVariantInit(&value);
+
+ hr = IPropertyStore_GetValue(pProperty, &PKEY_Device_FriendlyName, &value);
+ IF_FAILED_INTERNAL_ERROR_JUMP(hr, result, error);
+
+ if ((deviceInfo->name = (char *)PaUtil_GroupAllocateMemory(paWasapi->allocations, MAX_STR_LEN + 1)) == NULL)
{
- if (hr != E_NOTFOUND) {
- // We need to set the result to a value otherwise we will return paNoError
- // [IF_FAILED_JUMP(hResult, error);]
- IF_FAILED_INTERNAL_ERROR_JUMP(hr, result, error);
- }
+ result = paInsufficientMemory;
+ PropVariantClear(&value);
+ goto error;
}
+ if (value.pwszVal)
+ WideCharToMultiByte(CP_UTF8, 0, value.pwszVal, (int)wcslen(value.pwszVal), (char *)deviceInfo->name, MAX_STR_LEN - 1, 0, 0);
else
- {
- WCHAR *pszDeviceId = NULL;
- hr = IMMDevice_GetId(defaultRenderer, &pszDeviceId);
- // We need to set the result to a value otherwise we will return paNoError
- // [IF_FAILED_JUMP(hResult, error);]
- IF_FAILED_INTERNAL_ERROR_JUMP(hr, result, error);
- wcsncpy(paWasapi->defaultRenderer, pszDeviceId, MAX_STR_LEN-1);
- CoTaskMemFree(pszDeviceId);
- IMMDevice_Release(defaultRenderer);
- }
+ _snprintf((char *)deviceInfo->name, MAX_STR_LEN - 1, "baddev%d", index);
+
+ PropVariantClear(&value);
+
+ PA_DEBUG(("WASAPI:%d| name[%s]\n", index, deviceInfo->name));
}
+ // Default format
{
- IMMDevice *defaultCapturer = NULL;
- hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(pEnumerator, eCapture, eMultimedia, &defaultCapturer);
- if (hr != S_OK)
- {
- if (hr != E_NOTFOUND) {
- // We need to set the result to a value otherwise we will return paNoError
- // [IF_FAILED_JUMP(hResult, error);]
- IF_FAILED_INTERNAL_ERROR_JUMP(hr, result, error);
- }
- }
- else
- {
- WCHAR *pszDeviceId = NULL;
- hr = IMMDevice_GetId(defaultCapturer, &pszDeviceId);
- // We need to set the result to a value otherwise we will return paNoError
- // [IF_FAILED_JUMP(hResult, error);]
- IF_FAILED_INTERNAL_ERROR_JUMP(hr, result, error);
- wcsncpy(paWasapi->defaultCapturer, pszDeviceId, MAX_STR_LEN-1);
- CoTaskMemFree(pszDeviceId);
- IMMDevice_Release(defaultCapturer);
- }
+ PropVariantInit(&value);
+
+ hr = IPropertyStore_GetValue(pProperty, &PKEY_AudioEngine_DeviceFormat, &value);
+ IF_FAILED_INTERNAL_ERROR_JUMP(hr, result, error);
+
+ memcpy(&wasapiDeviceInfo->DefaultFormat, value.blob.pBlobData, min(sizeof(wasapiDeviceInfo->DefaultFormat), value.blob.cbSize));
+
+ PropVariantClear(&value);
}
- }
- hr = IMMDeviceEnumerator_EnumAudioEndpoints(pEnumerator, eAll, DEVICE_STATE_ACTIVE, &pEndPoints);
- // We need to set the result to a value otherwise we will return paNoError
- // [IF_FAILED_JUMP(hResult, error);]
- IF_FAILED_INTERNAL_ERROR_JUMP(hr, result, error);
+ // Form factor
+ {
+ PropVariantInit(&value);
- hr = IMMDeviceCollection_GetCount(pEndPoints, &paWasapi->deviceCount);
- // We need to set the result to a value otherwise we will return paNoError
- // [IF_FAILED_JUMP(hResult, error);]
- IF_FAILED_INTERNAL_ERROR_JUMP(hr, result, error);
+ hr = IPropertyStore_GetValue(pProperty, &PKEY_AudioEndpoint_FormFactor, &value);
+ IF_FAILED_INTERNAL_ERROR_JUMP(hr, result, error);
+
+ // set
+ #if defined(DUMMYUNIONNAME) && defined(NONAMELESSUNION)
+ // avoid breaking strict-aliasing rules in such line: (EndpointFormFactor)(*((UINT *)(((WORD *)&value.wReserved3)+1)));
+ UINT v;
+ memcpy(&v, (((WORD *)&value.wReserved3) + 1), sizeof(v));
+ wasapiDeviceInfo->formFactor = (EndpointFormFactor)v;
+ #else
+ wasapiDeviceInfo->formFactor = (EndpointFormFactor)value.uintVal;
+ #endif
+
+ PA_DEBUG(("WASAPI:%d| form-factor[%d]\n", index, wasapiDeviceInfo->formFactor));
+
+ PropVariantClear(&value);
+ }
+
+ // Data flow (Renderer or Capture)
+ hr = IMMDevice_QueryInterface(wasapiDeviceInfo->device, &pa_IID_IMMEndpoint, (void **)&endpoint);
+ if (SUCCEEDED(hr))
+ {
+ hr = IMMEndpoint_GetDataFlow(endpoint, &wasapiDeviceInfo->flow);
+ SAFE_RELEASE(endpoint);
+ }
+ SAFE_RELEASE(pProperty);
+ }
#else
- // Determine number of available devices by activating AudioClient for render and capture data flows
- if (SUCCEEDED(ActivateAudioInterface_WINRT(eRender, GetAudioClientIID(), &tmpClient)))
+ (void)pEndPoints;
+ (void)defaultRenderer;
+ (void)defaultCapturer;
+#endif
+
+ // Getting a temporary IAudioClient for more fields
+ // we make sure NOT to call Initialize yet!
{
- paWasapi->deviceCount++;
+ IAudioClient *tmpClient;
+ WAVEFORMATEX *mixFormat;
+
+ #ifdef PA_WINRT
+ // Set flow as ActivateAudioInterface depends on it and selects corresponding
+ // direction for the Audio Client
+ wasapiDeviceInfo->flow = (index == 0 ? eRender : eCapture);
+ #endif
+
+ // Create temp Audio Client instance to query additional details
+ hr = ActivateAudioInterface(wasapiDeviceInfo, NULL, &tmpClient);
+ IF_FAILED_INTERNAL_ERROR_JUMP(hr, result, error);
+
+ // Get latency
+ hr = IAudioClient_GetDevicePeriod(tmpClient, &wasapiDeviceInfo->DefaultDevicePeriod, &wasapiDeviceInfo->MinimumDevicePeriod);
+ if (FAILED(hr))
+ {
+ PA_DEBUG(("WASAPI:%d| failed getting min/default periods by IAudioClient::GetDevicePeriod() with error[%08X], will use 30000/100000 hns\n", index, (UINT32)hr));
+
+ // assign WASAPI common values
+ wasapiDeviceInfo->DefaultDevicePeriod = 100000;
+ wasapiDeviceInfo->MinimumDevicePeriod = 30000;
+
+ // ignore error, let continue further without failing with paInternalError
+ hr = S_OK;
+ }
+
+ // Get mix format
+ hr = IAudioClient_GetMixFormat(tmpClient, &mixFormat);
+ if (SUCCEEDED(hr))
+ {
+ memcpy(&wasapiDeviceInfo->MixFormat, mixFormat, min(sizeof(wasapiDeviceInfo->MixFormat), (sizeof(*mixFormat) + mixFormat->cbSize)));
+ CoTaskMemFree(mixFormat);
+ }
+
+ // Register WINRT device
+ #ifdef PA_WINRT
+ if (SUCCEEDED(hr))
+ {
+ // Default device
+ if (index == 0)
+ hostApi->info.defaultOutputDevice = hostApi->info.deviceCount;
+ else
+ hostApi->info.defaultInputDevice = hostApi->info.deviceCount;
+
+ // State
+ wasapiDeviceInfo->state = DEVICE_STATE_ACTIVE;
+
+ // Default format is always a mix format
+ wasapiDeviceInfo->DefaultFormat = wasapiDeviceInfo->MixFormat;
+
+ // Form-factor
+ wasapiDeviceInfo->formFactor = UnknownFormFactor;
+
+ // Name
+ if ((deviceInfo->name = (char *)PaUtil_GroupAllocateMemory(paWasapi->allocations, MAX_STR_LEN + 1)) == NULL)
+ {
+ SAFE_RELEASE(tmpClient);
+ result = paInsufficientMemory;
+ goto error;
+ }
+ _snprintf((char *)deviceInfo->name, MAX_STR_LEN - 1, "WASAPI_%s:%d", (index == 0 ? "Output" : "Input"), index);
+ PA_DEBUG(("WASAPI:%d| name[%s]\n", index, deviceInfo->name));
+ }
+ #endif
+
+ // Release tmp client
SAFE_RELEASE(tmpClient);
+
+ if (hr != S_OK)
+ {
+ //davidv: this happened with my hardware, previously for that same device in DirectSound:
+ //Digital Output (Realtek AC'97 Audio)'s GUID: {0x38f2cf50,0x7b4c,0x4740,0x86,0xeb,0xd4,0x38,0x66,0xd8,0xc8, 0x9f}
+ //so something must be _really_ wrong with this device, TODO handle this better. We kind of need GetMixFormat
+ LogHostError(hr);
+ result = paInternalError;
+ goto error;
+ }
}
- if (SUCCEEDED(ActivateAudioInterface_WINRT(eCapture, GetAudioClientIID(), &tmpClient)))
- {
- paWasapi->deviceCount++;
- SAFE_RELEASE(tmpClient);
+
+ // Fill basic device data
+ deviceInfo->maxInputChannels = 0;
+ deviceInfo->maxOutputChannels = 0;
+ deviceInfo->defaultSampleRate = wasapiDeviceInfo->MixFormat.Format.nSamplesPerSec;
+ switch (wasapiDeviceInfo->flow)
+ {
+ case eRender: {
+ deviceInfo->maxOutputChannels = wasapiDeviceInfo->MixFormat.Format.nChannels;
+ deviceInfo->defaultHighOutputLatency = nano100ToSeconds(wasapiDeviceInfo->DefaultDevicePeriod);
+ deviceInfo->defaultLowOutputLatency = nano100ToSeconds(wasapiDeviceInfo->MinimumDevicePeriod);
+ PA_DEBUG(("WASAPI:%d| def.SR[%d] max.CH[%d] latency{hi[%f] lo[%f]}\n", index, (UINT32)deviceInfo->defaultSampleRate,
+ deviceInfo->maxOutputChannels, (float)deviceInfo->defaultHighOutputLatency, (float)deviceInfo->defaultLowOutputLatency));
+ break;}
+ case eCapture: {
+ deviceInfo->maxInputChannels = wasapiDeviceInfo->MixFormat.Format.nChannels;
+ deviceInfo->defaultHighInputLatency = nano100ToSeconds(wasapiDeviceInfo->DefaultDevicePeriod);
+ deviceInfo->defaultLowInputLatency = nano100ToSeconds(wasapiDeviceInfo->MinimumDevicePeriod);
+ PA_DEBUG(("WASAPI:%d| def.SR[%d] max.CH[%d] latency{hi[%f] lo[%f]}\n", index, (UINT32)deviceInfo->defaultSampleRate,
+ deviceInfo->maxInputChannels, (float)deviceInfo->defaultHighInputLatency, (float)deviceInfo->defaultLowInputLatency));
+ break; }
+ default:
+ PRINT(("WASAPI:%d| bad Data Flow!\n", index));
+ result = paInternalError;
+ goto error;
}
-#endif
- paWasapi->devInfo = (PaWasapiDeviceInfo *)PaUtil_GroupAllocateMemory(
- paWasapi->allocations, sizeof(PaWasapiDeviceInfo) * paWasapi->deviceCount);
- if (paWasapi->devInfo == NULL)
+ return paNoError;
+
+error:
+
+ PRINT(("WASAPI: failed filling device info for device index[%d] - error[%d|%s]\n", index, result, Pa_GetErrorText(result)));
+
+ return result;
+}
+
+// ------------------------------------------------------------------------------------------
+static PaDeviceInfo *AllocateDeviceListMemory(PaWasapiHostApiRepresentation *paWasapi)
+{
+ PaUtilHostApiRepresentation *hostApi = (PaUtilHostApiRepresentation *)paWasapi;
+ PaDeviceInfo *deviceInfoArray = NULL;
+
+ if ((paWasapi->devInfo = (PaWasapiDeviceInfo *)PaUtil_GroupAllocateMemory(paWasapi->allocations,
+ sizeof(PaWasapiDeviceInfo) * paWasapi->deviceCount)) == NULL)
{
- result = paInsufficientMemory;
- goto error;
+ return NULL;
}
memset(paWasapi->devInfo, 0, sizeof(PaWasapiDeviceInfo) * paWasapi->deviceCount);
- if (paWasapi->deviceCount > 0)
+ if (paWasapi->deviceCount != 0)
{
UINT32 deviceCount = paWasapi->deviceCount;
#if defined(PA_WASAPI_MAX_CONST_DEVICE_COUNT) && (PA_WASAPI_MAX_CONST_DEVICE_COUNT > 0)
deviceCount = PA_WASAPI_MAX_CONST_DEVICE_COUNT;
#endif
- hostApi->deviceInfos = (PaDeviceInfo **)PaUtil_GroupAllocateMemory(
- paWasapi->allocations, sizeof(PaDeviceInfo *) * deviceCount);
- if (hostApi->deviceInfos == NULL)
+ if ((hostApi->deviceInfos = (PaDeviceInfo **)PaUtil_GroupAllocateMemory(paWasapi->allocations,
+ sizeof(PaDeviceInfo *) * deviceCount)) == NULL)
{
- result = paInsufficientMemory;
- goto error;
+ return NULL;
}
+ for (UINT32 i = 0; i < deviceCount; ++i)
+ hostApi->deviceInfos[i] = NULL;
- /* allocate all device info structs in a contiguous block */
- deviceInfoArray = (PaDeviceInfo *)PaUtil_GroupAllocateMemory(
- paWasapi->allocations, sizeof(PaDeviceInfo) * deviceCount);
- if (deviceInfoArray == NULL)
+ // Allocate all device info structs in a contiguous block
+ if ((deviceInfoArray = (PaDeviceInfo *)PaUtil_GroupAllocateMemory(paWasapi->allocations,
+ sizeof(PaDeviceInfo) * deviceCount)) == NULL)
{
- result = paInsufficientMemory;
- goto error;
+ return NULL;
}
memset(deviceInfoArray, 0, sizeof(PaDeviceInfo) * deviceCount);
+ }
- for (i = 0; i < paWasapi->deviceCount; ++i)
- {
- PaDeviceInfo *deviceInfo = &deviceInfoArray[i];
- deviceInfo->structVersion = 2;
- deviceInfo->hostApi = hostApiIndex;
-
- PA_DEBUG(("WASAPI: device idx: %02d\n", i));
- PA_DEBUG(("WASAPI: ---------------\n"));
-
- #ifndef PA_WINRT
- hr = IMMDeviceCollection_Item(pEndPoints, i, &paWasapi->devInfo[i].device);
- // We need to set the result to a value otherwise we will return paNoError
- // [IF_FAILED_JUMP(hResult, error);]
- IF_FAILED_INTERNAL_ERROR_JUMP(hr, result, error);
+ return deviceInfoArray;
+}
- // getting ID
- {
- WCHAR *pszDeviceId = NULL;
- hr = IMMDevice_GetId(paWasapi->devInfo[i].device, &pszDeviceId);
- // We need to set the result to a value otherwise we will return paNoError
- // [IF_FAILED_JUMP(hr, error);]
- IF_FAILED_INTERNAL_ERROR_JUMP(hr, result, error);
- wcsncpy(paWasapi->devInfo[i].szDeviceID, pszDeviceId, MAX_STR_LEN-1);
- CoTaskMemFree(pszDeviceId);
+// ------------------------------------------------------------------------------------------
+static PaError CreateDeviceList(PaWasapiHostApiRepresentation *paWasapi, PaHostApiIndex hostApiIndex)
+{
+ PaUtilHostApiRepresentation *hostApi = (PaUtilHostApiRepresentation *)paWasapi;
+ PaError result = paNoError;
+ PaDeviceInfo *deviceInfoArray;
+ UINT i;
+ WCHAR *defaultRenderer = NULL;
+ WCHAR *defaultCapturer = NULL;
+#ifndef PA_WINRT
+ HRESULT hr;
+ IMMDeviceCollection *pEndPoints = NULL;
+ IMMDeviceEnumerator *pEnumerator = NULL;
+#else
+ void *pEndPoints = NULL;
+ IAudioClient *tmpClient;
+#endif
- if (lstrcmpW(paWasapi->devInfo[i].szDeviceID, paWasapi->defaultCapturer) == 0)
- {// we found the default input!
- hostApi->info.defaultInputDevice = hostApi->info.deviceCount;
- }
- if (lstrcmpW(paWasapi->devInfo[i].szDeviceID, paWasapi->defaultRenderer) == 0)
- {// we found the default output!
- hostApi->info.defaultOutputDevice = hostApi->info.deviceCount;
- }
- }
+ // Make sure device list empty
+ if ((paWasapi->deviceCount != 0) || (hostApi->info.deviceCount != 0))
+ return paInternalError;
- hr = IMMDevice_GetState(paWasapi->devInfo[i].device, &paWasapi->devInfo[i].state);
- // We need to set the result to a value otherwise we will return paNoError
- // [IF_FAILED_JUMP(hResult, error);]
- IF_FAILED_INTERNAL_ERROR_JUMP(hr, result, error);
+#ifndef PA_WINRT
+ hr = CoCreateInstance(&pa_CLSID_IMMDeviceEnumerator, NULL, CLSCTX_INPROC_SERVER,
+ &pa_IID_IMMDeviceEnumerator, (void **)&pEnumerator);
+ IF_FAILED_INTERNAL_ERROR_JUMP(hr, result, error);
- if (paWasapi->devInfo[i].state != DEVICE_STATE_ACTIVE)
- {
- PRINT(("WASAPI device: %d is not currently available (state:%d)\n", i, paWasapi->devInfo[i].state));
- }
+ // Get default render and capture devices
+ {
+ IMMDevice *device;
+ hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(pEnumerator, eRender, eMultimedia, &device);
+ if (hr != S_OK)
+ {
+ if (hr != E_NOTFOUND)
{
- IPropertyStore *pProperty;
- hr = IMMDevice_OpenPropertyStore(paWasapi->devInfo[i].device, STGM_READ, &pProperty);
- // We need to set the result to a value otherwise we will return paNoError
- // [IF_FAILED_JUMP(hResult, error);]
IF_FAILED_INTERNAL_ERROR_JUMP(hr, result, error);
-
- // "Friendly" Name
- {
- PROPVARIANT value;
- PropVariantInit(&value);
- hr = IPropertyStore_GetValue(pProperty, &PKEY_Device_FriendlyName, &value);
- // We need to set the result to a value otherwise we will return paNoError
- // [IF_FAILED_JUMP(hResult, error);]
- IF_FAILED_INTERNAL_ERROR_JUMP(hr, result, error);
- if ((deviceInfo->name = (char *)PaUtil_GroupAllocateMemory(paWasapi->allocations, MAX_STR_LEN + 1)) == NULL)
- {
- result = paInsufficientMemory;
- goto error;
- }
- if (value.pwszVal)
- WideCharToMultiByte(CP_UTF8, 0, value.pwszVal, (int)wcslen(value.pwszVal), (char *)deviceInfo->name, MAX_STR_LEN - 1, 0, 0);
- else
- _snprintf((char *)deviceInfo->name, MAX_STR_LEN - 1, "baddev%d", i);
- PropVariantClear(&value);
- PA_DEBUG(("WASAPI:%d| name[%s]\n", i, deviceInfo->name));
- }
-
- // Default format
- {
- PROPVARIANT value;
- PropVariantInit(&value);
- hr = IPropertyStore_GetValue(pProperty, &PKEY_AudioEngine_DeviceFormat, &value);
- // We need to set the result to a value otherwise we will return paNoError
- // [IF_FAILED_JUMP(hResult, error);]
- IF_FAILED_INTERNAL_ERROR_JUMP(hr, result, error);
- memcpy(&paWasapi->devInfo[i].DefaultFormat, value.blob.pBlobData, min(sizeof(paWasapi->devInfo[i].DefaultFormat), value.blob.cbSize));
- // cleanup
- PropVariantClear(&value);
- }
-
- // Formfactor
- {
- PROPVARIANT value;
- PropVariantInit(&value);
- hr = IPropertyStore_GetValue(pProperty, &PKEY_AudioEndpoint_FormFactor, &value);
- // We need to set the result to a value otherwise we will return paNoError
- // [IF_FAILED_JUMP(hResult, error);]
- IF_FAILED_INTERNAL_ERROR_JUMP(hr, result, error);
- // set
- #if defined(DUMMYUNIONNAME) && defined(NONAMELESSUNION)
- // avoid breaking strict-aliasing rules in such line: (EndpointFormFactor)(*((UINT *)(((WORD *)&value.wReserved3)+1)));
- UINT v;
- memcpy(&v, (((WORD *)&value.wReserved3)+1), sizeof(v));
- paWasapi->devInfo[i].formFactor = (EndpointFormFactor)v;
- #else
- paWasapi->devInfo[i].formFactor = (EndpointFormFactor)value.uintVal;
- #endif
- PA_DEBUG(("WASAPI:%d| form-factor[%d]\n", i, paWasapi->devInfo[i].formFactor));
- // cleanup
- PropVariantClear(&value);
- }
-
- SAFE_RELEASE(pProperty);
}
-
- // Endpoint data
- {
- IMMEndpoint *endpoint = NULL;
- hr = IMMDevice_QueryInterface(paWasapi->devInfo[i].device, &pa_IID_IMMEndpoint, (void **)&endpoint);
- if (SUCCEEDED(hr))
- {
- hr = IMMEndpoint_GetDataFlow(endpoint, &paWasapi->devInfo[i].flow);
- SAFE_RELEASE(endpoint);
- }
- }
- #endif
+ }
+ else
+ {
+ hr = IMMDevice_GetId(device, &defaultRenderer);
+ IMMDevice_Release(device);
+ IF_FAILED_INTERNAL_ERROR_JUMP(hr, result, error);
+ }
- // Getting a temporary IAudioClient for more fields
- // we make sure NOT to call Initialize yet!
+ hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(pEnumerator, eCapture, eMultimedia, &device);
+ if (hr != S_OK)
+ {
+ if (hr != E_NOTFOUND)
{
- #ifdef PA_WINRT
- // Set flow as ActivateAudioInterface depends on it and selects corresponding
- // direction for the Audio Client
- paWasapi->devInfo[i].flow = (i == 0 ? eRender : eCapture);
- #endif
-
- // Create temp Audio Client instance to query additional details
- hr = ActivateAudioInterface(&paWasapi->devInfo[i], NULL, &tmpClient);
- // We need to set the result to a value otherwise we will return paNoError
- // [IF_FAILED_JUMP(hResult, error);]
IF_FAILED_INTERNAL_ERROR_JUMP(hr, result, error);
+ }
+ }
+ else
+ {
+ hr = IMMDevice_GetId(device, &defaultCapturer);
+ IMMDevice_Release(device);
+ IF_FAILED_INTERNAL_ERROR_JUMP(hr, result, error);
+ }
+ }
- // Get latency
- hr = IAudioClient_GetDevicePeriod(tmpClient,
- &paWasapi->devInfo[i].DefaultDevicePeriod,
- &paWasapi->devInfo[i].MinimumDevicePeriod);
- if (FAILED(hr))
- {
- PA_DEBUG(("WASAPI:%d| failed getting min/default periods by IAudioClient::GetDevicePeriod() with error[%08X], will use 30000/100000 hns\n", i, (UINT32)hr));
+ // Get all currently active devices
+ hr = IMMDeviceEnumerator_EnumAudioEndpoints(pEnumerator, eAll, DEVICE_STATE_ACTIVE, &pEndPoints);
+ IF_FAILED_INTERNAL_ERROR_JUMP(hr, result, error);
- // assign WASAPI common values
- paWasapi->devInfo[i].DefaultDevicePeriod = 100000;
- paWasapi->devInfo[i].MinimumDevicePeriod = 30000;
+ // Get device count
+ hr = IMMDeviceCollection_GetCount(pEndPoints, &paWasapi->deviceCount);
+ IF_FAILED_INTERNAL_ERROR_JUMP(hr, result, error);
+#else
+ // Determine number of available devices by activating AudioClient for render and capture data flows
+ if (SUCCEEDED(ActivateAudioInterface_WINRT(eRender, GetAudioClientIID(), &tmpClient)))
+ {
+ paWasapi->deviceCount++;
+ SAFE_RELEASE(tmpClient);
+ }
+ if (SUCCEEDED(ActivateAudioInterface_WINRT(eCapture, GetAudioClientIID(), &tmpClient)))
+ {
+ paWasapi->deviceCount++;
+ SAFE_RELEASE(tmpClient);
+ }
+#endif
- // ignore error, let continue further without failing with paInternalError
- hr = S_OK;
- }
-
- // Get mix format
- {
- WAVEFORMATEX *mixFormat;
+ // Allocate memory for the device list
+ if ((deviceInfoArray = AllocateDeviceListMemory(paWasapi)) == NULL)
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
- hr = IAudioClient_GetMixFormat(tmpClient, &mixFormat);
- if (SUCCEEDED(hr))
- {
- memcpy(&paWasapi->devInfo[i].MixFormat, mixFormat, min(sizeof(paWasapi->devInfo[i].MixFormat), (sizeof(*mixFormat) + mixFormat->cbSize)));
- CoTaskMemFree(mixFormat);
- }
- }
+ // Fill WASAPI device info
+ for (i = 0; i < paWasapi->deviceCount; ++i)
+ {
+ PaDeviceInfo *deviceInfo = &deviceInfoArray[i];
- // Register WINRT device
- #ifdef PA_WINRT
- if (SUCCEEDED(hr))
- {
- // Default device
- if (i == 0)
- hostApi->info.defaultOutputDevice = hostApi->info.deviceCount;
- else
- hostApi->info.defaultInputDevice = hostApi->info.deviceCount;
+ PA_DEBUG(("WASAPI: device idx: %02d\n", i));
+ PA_DEBUG(("WASAPI: ---------------\n"));
- // State
- paWasapi->devInfo[i].state = DEVICE_STATE_ACTIVE;
+ FillBaseDeviceInfo(deviceInfo, hostApiIndex);
- // Default format is always a mix format
- paWasapi->devInfo[i].DefaultFormat = paWasapi->devInfo[i].MixFormat;
+ if ((result = FillDeviceInfo(paWasapi, pEndPoints, i, defaultRenderer, defaultCapturer,
+ deviceInfo, &paWasapi->devInfo[i])) != paNoError)
+ {
+ // Faulty device is made inactive
+ if ((result = FillInactiveDeviceInfo(paWasapi, deviceInfo)) != paNoError)
+ goto error;
+ }
- // Form-factor
- paWasapi->devInfo[i].formFactor = UnknownFormFactor;
+ hostApi->deviceInfos[i] = deviceInfo;
+ ++hostApi->info.deviceCount;
+ }
- // Name
- if ((deviceInfo->name = (char *)PaUtil_GroupAllocateMemory(paWasapi->allocations, MAX_STR_LEN + 1)) == NULL)
- {
- SAFE_RELEASE(tmpClient);
- result = paInsufficientMemory;
- goto error;
- }
- _snprintf((char *)deviceInfo->name, MAX_STR_LEN - 1, "WASAPI_%s:%d", (i == 0 ? "Output" : "Input"), i);
- PA_DEBUG(("WASAPI:%d| name[%s]\n", i, deviceInfo->name));
- }
- #endif
+ // Fill the remaining slots with inactive device info
+#if defined(PA_WASAPI_MAX_CONST_DEVICE_COUNT) && (PA_WASAPI_MAX_CONST_DEVICE_COUNT > 0)
+ if (hostApi->info.deviceCount < PA_WASAPI_MAX_CONST_DEVICE_COUNT)
+ {
+ for (i = hostApi->info.deviceCount; i < PA_WASAPI_MAX_CONST_DEVICE_COUNT; ++i)
+ {
+ PaDeviceInfo *deviceInfo = &deviceInfoArray[i];
- // Release tmp client
- SAFE_RELEASE(tmpClient);
+ FillBaseDeviceInfo(deviceInfo, hostApiIndex);
- if (hr != S_OK)
- {
- //davidv: this happened with my hardware, previously for that same device in DirectSound:
- //Digital Output (Realtek AC'97 Audio)'s GUID: {0x38f2cf50,0x7b4c,0x4740,0x86,0xeb,0xd4,0x38,0x66,0xd8,0xc8, 0x9f}
- //so something must be _really_ wrong with this device, TODO handle this better. We kind of need GetMixFormat
- LogHostError(hr);
- // We need to set the result to a value otherwise we will return paNoError
- result = paInternalError;
- goto error;
- }
- }
-
- // we can now fill in portaudio device data
- deviceInfo->maxInputChannels = 0;
- deviceInfo->maxOutputChannels = 0;
- deviceInfo->defaultSampleRate = paWasapi->devInfo[i].MixFormat.Format.nSamplesPerSec;
- switch (paWasapi->devInfo[i].flow)
- {
- case eRender: {
- deviceInfo->maxOutputChannels = paWasapi->devInfo[i].MixFormat.Format.nChannels;
- deviceInfo->defaultHighOutputLatency = nano100ToSeconds(paWasapi->devInfo[i].DefaultDevicePeriod);
- deviceInfo->defaultLowOutputLatency = nano100ToSeconds(paWasapi->devInfo[i].MinimumDevicePeriod);
- PA_DEBUG(("WASAPI:%d| def.SR[%d] max.CH[%d] latency{hi[%f] lo[%f]}\n", i, (UINT32)deviceInfo->defaultSampleRate,
- deviceInfo->maxOutputChannels, (float)deviceInfo->defaultHighOutputLatency, (float)deviceInfo->defaultLowOutputLatency));
- break;}
- case eCapture: {
- deviceInfo->maxInputChannels = paWasapi->devInfo[i].MixFormat.Format.nChannels;
- deviceInfo->defaultHighInputLatency = nano100ToSeconds(paWasapi->devInfo[i].DefaultDevicePeriod);
- deviceInfo->defaultLowInputLatency = nano100ToSeconds(paWasapi->devInfo[i].MinimumDevicePeriod);
- PA_DEBUG(("WASAPI:%d| def.SR[%d] max.CH[%d] latency{hi[%f] lo[%f]}\n", i, (UINT32)deviceInfo->defaultSampleRate,
- deviceInfo->maxInputChannels, (float)deviceInfo->defaultHighInputLatency, (float)deviceInfo->defaultLowInputLatency));
- break; }
- default:
- PRINT(("WASAPI:%d| bad Data Flow!\n", i));
- // We need to set the result to a value otherwise we will return paNoError
- result = paInternalError;
- //continue; // do not skip from list, allow to initialize
- break;
- }
+ if ((result = FillInactiveDeviceInfo(paWasapi, deviceInfo)) != paNoError)
+ goto error;
hostApi->deviceInfos[i] = deviceInfo;
++hostApi->info.deviceCount;
}
+ }
+#endif
- #if defined(PA_WASAPI_MAX_CONST_DEVICE_COUNT) && (PA_WASAPI_MAX_CONST_DEVICE_COUNT > 0)
- if (hostApi->info.deviceCount < PA_WASAPI_MAX_CONST_DEVICE_COUNT)
- {
- for (i = hostApi->info.deviceCount; i < PA_WASAPI_MAX_CONST_DEVICE_COUNT; ++i)
- {
- PaDeviceInfo *deviceInfo = &deviceInfoArray[i];
- deviceInfo->structVersion = 2;
- deviceInfo->hostApi = hostApiIndex;
+ // Clear any non-fatal errors
+ result = paNoError;
- if ((deviceInfo->name = (char *)PaUtil_GroupAllocateMemory(paWasapi->allocations, 1)) == NULL)
- {
- result = paInsufficientMemory;
- goto error;
- }
- ((char *)deviceInfo->name)[0] = 0;
+ PRINT(("WASAPI: device list ok - found %d devices\n", paWasapi->deviceCount));
- hostApi->deviceInfos[i] = deviceInfo;
- ++hostApi->info.deviceCount;
- }
- }
- #endif
- }
+done:
#ifndef PA_WINRT
+ CoTaskMemFree(defaultRenderer);
+ CoTaskMemFree(defaultCapturer);
SAFE_RELEASE(pEndPoints);
SAFE_RELEASE(pEnumerator);
#endif
- PRINT(("WASAPI: device list created ok\n"));
-
- return paNoError;
+ return result;
error:
-
- PRINT(("WASAPI: failed %s error[%d|%s]\n", __FUNCTION__, result, Pa_GetErrorText(result)));
-
-#ifndef PA_WINRT
- SAFE_RELEASE(pEndPoints);
- SAFE_RELEASE(pEnumerator);
-#endif
-
+
// Safety if error was not set so that we do not think initialize was a success
if (result == paNoError)
result = paInternalError;
- return result;
+ PRINT(("WASAPI: failed to create device list - error[%d|%s]\n", result, Pa_GetErrorText(result)));
+
+ goto done;
}
// ------------------------------------------------------------------------------------------
PA_THREAD_FUNC ProcThreadEvent(void *param)
{
PaWasapiHostProcessor processor[S_COUNT];
- HRESULT hr;
+ HRESULT hr = S_OK;
DWORD dwResult;
PaWasapiStream *stream = (PaWasapiStream *)param;
PaWasapiHostProcessor defaultProcessor;
PA_THREAD_FUNC ProcThreadPoll(void *param)
{
PaWasapiHostProcessor processor[S_COUNT];
- HRESULT hr;
+ HRESULT hr = S_OK;
PaWasapiStream *stream = (PaWasapiStream *)param;
PaWasapiHostProcessor defaultProcessor;
INT32 i;