Merge pull request #325 from PortAudio/winrt

wasapi: Refactored device list creation, if faulty audio device is re…
This commit is contained in:
Dmitry Kostjuchenko 2020-10-10 00:43:32 +03:00 committed by GitHub
commit 6a90e596b6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -472,9 +472,6 @@ typedef struct
// 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
@ -801,6 +798,9 @@ static BOOL SystemTimer_SetGranularity(SystemTimer *timer, UINT32 granularity)
timer->granularity = 0;
return FALSE;
}
#else
(void)timer;
(void)granularity;
#endif
return TRUE;
@ -817,6 +817,8 @@ static void SystemTimer_RestoreGranularity(SystemTimer *timer)
PRINT(("RestoreSystemTimer: timeEndPeriod(1) failed!\n"));
}
}
#else
(void)timer;
#endif
}
@ -829,6 +831,8 @@ static void SystemTimer_InitializeTimeGetter()
//! 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)
{
@ -1738,93 +1742,376 @@ static void NotifyStateChanged(PaWasapiStream *stream, UINT32 flags, HRESULT hr)
stream->fnStateHandler((PaStream *)stream, flags, hr, stream->pStateHandlerUserData);
}
// ------------------------------------------------------------------------------------------
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;
#ifndef PA_WINRT
hr = IMMDeviceCollection_Item((IMMDeviceCollection *)pEndPoints, index, &wasapiDeviceInfo->device);
IF_FAILED_INTERNAL_ERROR_JUMP(hr, result, error);
// getting ID
{
WCHAR *pszDeviceId;
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);
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
{
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)
{
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
_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
{
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);
}
// Form factor
{
PropVariantInit(&value);
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
(void)pEndPoints;
(void)defaultRenderer;
(void)defaultCapturer;
#endif
// Getting a temporary IAudioClient for more fields
// we make sure NOT to call Initialize yet!
{
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;
}
}
// 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;
}
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)
{
return NULL;
}
memset(paWasapi->devInfo, 0, sizeof(PaWasapiDeviceInfo) * paWasapi->deviceCount);
if (paWasapi->deviceCount != 0)
{
UINT32 deviceCount = paWasapi->deviceCount;
#if defined(PA_WASAPI_MAX_CONST_DEVICE_COUNT) && (PA_WASAPI_MAX_CONST_DEVICE_COUNT > 0)
if (deviceCount < PA_WASAPI_MAX_CONST_DEVICE_COUNT)
deviceCount = PA_WASAPI_MAX_CONST_DEVICE_COUNT;
#endif
if ((hostApi->deviceInfos = (PaDeviceInfo **)PaUtil_GroupAllocateMemory(paWasapi->allocations,
sizeof(PaDeviceInfo *) * deviceCount)) == NULL)
{
return NULL;
}
for (UINT32 i = 0; i < deviceCount; ++i)
hostApi->deviceInfos[i] = NULL;
// Allocate all device info structs in a contiguous block
if ((deviceInfoArray = (PaDeviceInfo *)PaUtil_GroupAllocateMemory(paWasapi->allocations,
sizeof(PaDeviceInfo) * deviceCount)) == NULL)
{
return NULL;
}
memset(deviceInfoArray, 0, sizeof(PaDeviceInfo) * deviceCount);
}
return deviceInfoArray;
}
// ------------------------------------------------------------------------------------------
static PaError CreateDeviceList(PaWasapiHostApiRepresentation *paWasapi, PaHostApiIndex hostApiIndex)
{
PaUtilHostApiRepresentation *hostApi = (PaUtilHostApiRepresentation *)paWasapi;
PaError result = paNoError;
PaDeviceInfo *deviceInfoArray;
HRESULT hr = S_OK;
UINT i;
WCHAR *defaultRenderer = NULL;
WCHAR *defaultCapturer = NULL;
#ifndef PA_WINRT
IMMDeviceCollection* pEndPoints = NULL;
HRESULT hr;
IMMDeviceCollection *pEndPoints = NULL;
IMMDeviceEnumerator *pEnumerator = NULL;
#endif
#else
void *pEndPoints = NULL;
IAudioClient *tmpClient;
#endif
// Make sure device list empty
if ((paWasapi->deviceCount != 0) || (hostApi->info.deviceCount != 0))
return paInternalError;
#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 = CoCreateInstance(&pa_CLSID_IMMDeviceEnumerator, NULL, CLSCTX_INPROC_SERVER,
&pa_IID_IMMDeviceEnumerator, (void **)&pEnumerator);
IF_FAILED_INTERNAL_ERROR_JUMP(hr, result, error);
// getting default device ids in the eMultimedia "role"
// Get default render and capture devices
{
IMMDevice *device;
hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(pEnumerator, eRender, eMultimedia, &device);
if (hr != S_OK)
{
IMMDevice *defaultRenderer = NULL;
hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(pEnumerator, eRender, eMultimedia, &defaultRenderer);
if (hr != S_OK)
if (hr != E_NOTFOUND)
{
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(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);
}
}
else
{
IMMDevice *defaultCapturer = NULL;
hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(pEnumerator, eCapture, eMultimedia, &defaultCapturer);
if (hr != S_OK)
hr = IMMDevice_GetId(device, &defaultRenderer);
IMMDevice_Release(device);
IF_FAILED_INTERNAL_ERROR_JUMP(hr, result, error);
}
hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(pEnumerator, eCapture, eMultimedia, &device);
if (hr != S_OK)
{
if (hr != E_NOTFOUND)
{
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);
}
}
else
{
hr = IMMDevice_GetId(device, &defaultCapturer);
IMMDevice_Release(device);
IF_FAILED_INTERNAL_ERROR_JUMP(hr, result, error);
}
}
// Get all currently active devices
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);
// Get device count
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);
#else
// Determine number of available devices by activating AudioClient for render and capture data flows
if (SUCCEEDED(ActivateAudioInterface_WINRT(eRender, GetAudioClientIID(), &tmpClient)))
@ -1839,331 +2126,79 @@ static PaError CreateDeviceList(PaWasapiHostApiRepresentation *paWasapi, PaHostA
}
#endif
paWasapi->devInfo = (PaWasapiDeviceInfo *)PaUtil_GroupAllocateMemory(
paWasapi->allocations, sizeof(PaWasapiDeviceInfo) * paWasapi->deviceCount);
if (paWasapi->devInfo == NULL)
// Allocate memory for the device list
if ((deviceInfoArray = AllocateDeviceListMemory(paWasapi)) == NULL)
{
result = paInsufficientMemory;
goto error;
}
memset(paWasapi->devInfo, 0, sizeof(PaWasapiDeviceInfo) * paWasapi->deviceCount);
if (paWasapi->deviceCount > 0)
// Fill WASAPI device info
for (i = 0; i < paWasapi->deviceCount; ++i)
{
UINT32 deviceCount = paWasapi->deviceCount;
#if defined(PA_WASAPI_MAX_CONST_DEVICE_COUNT) && (PA_WASAPI_MAX_CONST_DEVICE_COUNT > 0)
if (deviceCount < PA_WASAPI_MAX_CONST_DEVICE_COUNT)
deviceCount = PA_WASAPI_MAX_CONST_DEVICE_COUNT;
#endif
PaDeviceInfo *deviceInfo = &deviceInfoArray[i];
hostApi->deviceInfos = (PaDeviceInfo **)PaUtil_GroupAllocateMemory(
paWasapi->allocations, sizeof(PaDeviceInfo *) * deviceCount);
if (hostApi->deviceInfos == NULL)
PA_DEBUG(("WASAPI: device idx: %02d\n", i));
PA_DEBUG(("WASAPI: ---------------\n"));
FillBaseDeviceInfo(deviceInfo, hostApiIndex);
if ((result = FillDeviceInfo(paWasapi, pEndPoints, i, defaultRenderer, defaultCapturer,
deviceInfo, &paWasapi->devInfo[i])) != paNoError)
{
result = paInsufficientMemory;
goto error;
// Faulty device is made inactive
if ((result = FillInactiveDeviceInfo(paWasapi, deviceInfo)) != paNoError)
goto error;
}
/* allocate all device info structs in a contiguous block */
deviceInfoArray = (PaDeviceInfo *)PaUtil_GroupAllocateMemory(
paWasapi->allocations, sizeof(PaDeviceInfo) * deviceCount);
if (deviceInfoArray == NULL)
hostApi->deviceInfos[i] = deviceInfo;
++hostApi->info.deviceCount;
}
// 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)
{
result = paInsufficientMemory;
goto error;
}
memset(deviceInfoArray, 0, sizeof(PaDeviceInfo) * deviceCount);
PaDeviceInfo *deviceInfo = &deviceInfoArray[i];
for (i = 0; i < paWasapi->deviceCount; ++i)
{
PaDeviceInfo *deviceInfo = &deviceInfoArray[i];
deviceInfo->structVersion = 2;
deviceInfo->hostApi = hostApiIndex;
FillBaseDeviceInfo(deviceInfo, 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);
// 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);
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;
}
}
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);
if (paWasapi->devInfo[i].state != DEVICE_STATE_ACTIVE)
{
PRINT(("WASAPI device: %d is not currently available (state:%d)\n", i, paWasapi->devInfo[i].state));
}
{
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
// Getting a temporary IAudioClient for more fields
// we make sure NOT to call Initialize yet!
{
#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);
// 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));
// assign WASAPI common values
paWasapi->devInfo[i].DefaultDevicePeriod = 100000;
paWasapi->devInfo[i].MinimumDevicePeriod = 30000;
// ignore error, let continue further without failing with paInternalError
hr = S_OK;
}
// Get mix format
{
WAVEFORMATEX *mixFormat;
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);
}
}
// 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;
// State
paWasapi->devInfo[i].state = DEVICE_STATE_ACTIVE;
// Default format is always a mix format
paWasapi->devInfo[i].DefaultFormat = paWasapi->devInfo[i].MixFormat;
// Form-factor
paWasapi->devInfo[i].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", (i == 0 ? "Output" : "Input"), i);
PA_DEBUG(("WASAPI:%d| name[%s]\n", i, 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);
// 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;
}
#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;
if ((deviceInfo->name = (char *)PaUtil_GroupAllocateMemory(paWasapi->allocations, 1)) == NULL)
{
result = paInsufficientMemory;
goto error;
}
((char *)deviceInfo->name)[0] = 0;
hostApi->deviceInfos[i] = deviceInfo;
++hostApi->info.deviceCount;
}
}
#endif
}
#endif
// Clear any non-fatal errors
result = paNoError;
PRINT(("WASAPI: device list ok - found %d devices\n", paWasapi->deviceCount));
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;
}
// ------------------------------------------------------------------------------------------
@ -5637,7 +5672,7 @@ static void FinishComPointers(PaWasapiStream *stream, BOOL threadComInitialized)
PA_THREAD_FUNC ProcThreadEvent(void *param)
{
PaWasapiHostProcessor processor[S_COUNT];
HRESULT hr;
HRESULT hr = S_OK;
DWORD dwResult;
PaWasapiStream *stream = (PaWasapiStream *)param;
PaWasapiHostProcessor defaultProcessor;
@ -5931,7 +5966,7 @@ static inline INT32 GetNextSleepTime(SystemTimer *timer, ThreadIdleScheduler *sc
PA_THREAD_FUNC ProcThreadPoll(void *param)
{
PaWasapiHostProcessor processor[S_COUNT];
HRESULT hr;
HRESULT hr = S_OK;
PaWasapiStream *stream = (PaWasapiStream *)param;
PaWasapiHostProcessor defaultProcessor;
INT32 i;