wasapi: Refactored device list creation, if faulty audio device is reported as active by WASAPI then it will not break device list creation, silenced warnings when compiling for UWP platform.
This commit is contained in:
parent
aa6ff975af
commit
522f982158
1 changed files with 381 additions and 346 deletions
|
|
@ -472,9 +472,6 @@ typedef struct
|
||||||
// this is the REAL number of devices, whether they are usefull to PA or not!
|
// this is the REAL number of devices, whether they are usefull to PA or not!
|
||||||
UINT32 deviceCount;
|
UINT32 deviceCount;
|
||||||
|
|
||||||
WCHAR defaultRenderer[MAX_STR_LEN];
|
|
||||||
WCHAR defaultCapturer[MAX_STR_LEN];
|
|
||||||
|
|
||||||
PaWasapiDeviceInfo *devInfo;
|
PaWasapiDeviceInfo *devInfo;
|
||||||
|
|
||||||
// is TRUE when WOW64 Vista/7 Workaround is needed
|
// is TRUE when WOW64 Vista/7 Workaround is needed
|
||||||
|
|
@ -801,6 +798,9 @@ static BOOL SystemTimer_SetGranularity(SystemTimer *timer, UINT32 granularity)
|
||||||
timer->granularity = 0;
|
timer->granularity = 0;
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
#else
|
||||||
|
(void)timer;
|
||||||
|
(void)granularity;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
|
|
@ -817,6 +817,8 @@ static void SystemTimer_RestoreGranularity(SystemTimer *timer)
|
||||||
PRINT(("RestoreSystemTimer: timeEndPeriod(1) failed!\n"));
|
PRINT(("RestoreSystemTimer: timeEndPeriod(1) failed!\n"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#else
|
||||||
|
(void)timer;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -829,6 +831,8 @@ static void SystemTimer_InitializeTimeGetter()
|
||||||
//! Get high-resolution time in milliseconds (using QPC by default).
|
//! Get high-resolution time in milliseconds (using QPC by default).
|
||||||
static inline LONGLONG SystemTimer_GetTime(SystemTimer *timer)
|
static inline LONGLONG SystemTimer_GetTime(SystemTimer *timer)
|
||||||
{
|
{
|
||||||
|
(void)timer;
|
||||||
|
|
||||||
// QPC: https://docs.microsoft.com/en-us/windows/win32/sysinfo/acquiring-high-resolution-time-stamps
|
// QPC: https://docs.microsoft.com/en-us/windows/win32/sysinfo/acquiring-high-resolution-time-stamps
|
||||||
if (g_SystemTimerUseQpc)
|
if (g_SystemTimerUseQpc)
|
||||||
{
|
{
|
||||||
|
|
@ -1738,19 +1742,324 @@ static void NotifyStateChanged(PaWasapiStream *stream, UINT32 flags, HRESULT hr)
|
||||||
stream->fnStateHandler((PaStream *)stream, flags, hr, stream->pStateHandlerUserData);
|
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)
|
static PaError CreateDeviceList(PaWasapiHostApiRepresentation *paWasapi, PaHostApiIndex hostApiIndex)
|
||||||
{
|
{
|
||||||
PaUtilHostApiRepresentation *hostApi = (PaUtilHostApiRepresentation *)paWasapi;
|
PaUtilHostApiRepresentation *hostApi = (PaUtilHostApiRepresentation *)paWasapi;
|
||||||
PaError result = paNoError;
|
PaError result = paNoError;
|
||||||
PaDeviceInfo *deviceInfoArray;
|
PaDeviceInfo *deviceInfoArray;
|
||||||
HRESULT hr = S_OK;
|
|
||||||
UINT i;
|
UINT i;
|
||||||
|
WCHAR *defaultRenderer = NULL;
|
||||||
|
WCHAR *defaultCapturer = NULL;
|
||||||
#ifndef PA_WINRT
|
#ifndef PA_WINRT
|
||||||
|
HRESULT hr;
|
||||||
IMMDeviceCollection *pEndPoints = NULL;
|
IMMDeviceCollection *pEndPoints = NULL;
|
||||||
IMMDeviceEnumerator *pEnumerator = NULL;
|
IMMDeviceEnumerator *pEnumerator = NULL;
|
||||||
#endif
|
#else
|
||||||
|
void *pEndPoints = NULL;
|
||||||
IAudioClient *tmpClient;
|
IAudioClient *tmpClient;
|
||||||
|
#endif
|
||||||
|
|
||||||
// Make sure device list empty
|
// Make sure device list empty
|
||||||
if ((paWasapi->deviceCount != 0) || (hostApi->info.deviceCount != 0))
|
if ((paWasapi->deviceCount != 0) || (hostApi->info.deviceCount != 0))
|
||||||
|
|
@ -1759,72 +2068,50 @@ static PaError CreateDeviceList(PaWasapiHostApiRepresentation *paWasapi, PaHostA
|
||||||
#ifndef PA_WINRT
|
#ifndef PA_WINRT
|
||||||
hr = CoCreateInstance(&pa_CLSID_IMMDeviceEnumerator, NULL, CLSCTX_INPROC_SERVER,
|
hr = CoCreateInstance(&pa_CLSID_IMMDeviceEnumerator, NULL, CLSCTX_INPROC_SERVER,
|
||||||
&pa_IID_IMMDeviceEnumerator, (void **)&pEnumerator);
|
&pa_IID_IMMDeviceEnumerator, (void **)&pEnumerator);
|
||||||
|
|
||||||
// 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_FAILED_INTERNAL_ERROR_JUMP(hr, result, error);
|
||||||
|
|
||||||
// getting default device ids in the eMultimedia "role"
|
// Get default render and capture devices
|
||||||
{
|
{
|
||||||
{
|
IMMDevice *device;
|
||||||
IMMDevice *defaultRenderer = NULL;
|
|
||||||
hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(pEnumerator, eRender, eMultimedia, &defaultRenderer);
|
hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(pEnumerator, eRender, eMultimedia, &device);
|
||||||
if (hr != S_OK)
|
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);
|
IF_FAILED_INTERNAL_ERROR_JUMP(hr, result, error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
WCHAR *pszDeviceId = NULL;
|
hr = IMMDevice_GetId(device, &defaultRenderer);
|
||||||
hr = IMMDevice_GetId(defaultRenderer, &pszDeviceId);
|
IMMDevice_Release(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);
|
IF_FAILED_INTERNAL_ERROR_JUMP(hr, result, error);
|
||||||
wcsncpy(paWasapi->defaultRenderer, pszDeviceId, MAX_STR_LEN-1);
|
|
||||||
CoTaskMemFree(pszDeviceId);
|
|
||||||
IMMDevice_Release(defaultRenderer);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(pEnumerator, eCapture, eMultimedia, &device);
|
||||||
IMMDevice *defaultCapturer = NULL;
|
|
||||||
hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(pEnumerator, eCapture, eMultimedia, &defaultCapturer);
|
|
||||||
if (hr != S_OK)
|
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);
|
IF_FAILED_INTERNAL_ERROR_JUMP(hr, result, error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
WCHAR *pszDeviceId = NULL;
|
hr = IMMDevice_GetId(device, &defaultCapturer);
|
||||||
hr = IMMDevice_GetId(defaultCapturer, &pszDeviceId);
|
IMMDevice_Release(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);
|
IF_FAILED_INTERNAL_ERROR_JUMP(hr, result, error);
|
||||||
wcsncpy(paWasapi->defaultCapturer, pszDeviceId, MAX_STR_LEN-1);
|
|
||||||
CoTaskMemFree(pszDeviceId);
|
|
||||||
IMMDevice_Release(defaultCapturer);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get all currently active devices
|
||||||
hr = IMMDeviceEnumerator_EnumAudioEndpoints(pEnumerator, eAll, DEVICE_STATE_ACTIVE, &pEndPoints);
|
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);
|
IF_FAILED_INTERNAL_ERROR_JUMP(hr, result, error);
|
||||||
|
|
||||||
|
// Get device count
|
||||||
hr = IMMDeviceCollection_GetCount(pEndPoints, &paWasapi->deviceCount);
|
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);
|
IF_FAILED_INTERNAL_ERROR_JUMP(hr, result, error);
|
||||||
|
|
||||||
#else
|
#else
|
||||||
// Determine number of available devices by activating AudioClient for render and capture data flows
|
// Determine number of available devices by activating AudioClient for render and capture data flows
|
||||||
if (SUCCEEDED(ActivateAudioInterface_WINRT(eRender, GetAudioClientIID(), &tmpClient)))
|
if (SUCCEEDED(ActivateAudioInterface_WINRT(eRender, GetAudioClientIID(), &tmpClient)))
|
||||||
|
|
@ -1839,331 +2126,79 @@ static PaError CreateDeviceList(PaWasapiHostApiRepresentation *paWasapi, PaHostA
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
paWasapi->devInfo = (PaWasapiDeviceInfo *)PaUtil_GroupAllocateMemory(
|
// Allocate memory for the device list
|
||||||
paWasapi->allocations, sizeof(PaWasapiDeviceInfo) * paWasapi->deviceCount);
|
if ((deviceInfoArray = AllocateDeviceListMemory(paWasapi)) == NULL)
|
||||||
if (paWasapi->devInfo == NULL)
|
|
||||||
{
|
|
||||||
result = paInsufficientMemory;
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
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
|
|
||||||
|
|
||||||
hostApi->deviceInfos = (PaDeviceInfo **)PaUtil_GroupAllocateMemory(
|
|
||||||
paWasapi->allocations, sizeof(PaDeviceInfo *) * deviceCount);
|
|
||||||
if (hostApi->deviceInfos == NULL)
|
|
||||||
{
|
{
|
||||||
result = paInsufficientMemory;
|
result = paInsufficientMemory;
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* allocate all device info structs in a contiguous block */
|
// Fill WASAPI device info
|
||||||
deviceInfoArray = (PaDeviceInfo *)PaUtil_GroupAllocateMemory(
|
|
||||||
paWasapi->allocations, sizeof(PaDeviceInfo) * deviceCount);
|
|
||||||
if (deviceInfoArray == NULL)
|
|
||||||
{
|
|
||||||
result = paInsufficientMemory;
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
memset(deviceInfoArray, 0, sizeof(PaDeviceInfo) * deviceCount);
|
|
||||||
|
|
||||||
for (i = 0; i < paWasapi->deviceCount; ++i)
|
for (i = 0; i < paWasapi->deviceCount; ++i)
|
||||||
{
|
{
|
||||||
PaDeviceInfo *deviceInfo = &deviceInfoArray[i];
|
PaDeviceInfo *deviceInfo = &deviceInfoArray[i];
|
||||||
deviceInfo->structVersion = 2;
|
|
||||||
deviceInfo->hostApi = hostApiIndex;
|
|
||||||
|
|
||||||
PA_DEBUG(("WASAPI: device idx: %02d\n", i));
|
PA_DEBUG(("WASAPI: device idx: %02d\n", i));
|
||||||
PA_DEBUG(("WASAPI: ---------------\n"));
|
PA_DEBUG(("WASAPI: ---------------\n"));
|
||||||
|
|
||||||
#ifndef PA_WINRT
|
FillBaseDeviceInfo(deviceInfo, hostApiIndex);
|
||||||
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
|
if ((result = FillDeviceInfo(paWasapi, pEndPoints, i, defaultRenderer, defaultCapturer,
|
||||||
|
deviceInfo, &paWasapi->devInfo[i])) != paNoError)
|
||||||
{
|
{
|
||||||
WCHAR *pszDeviceId = NULL;
|
// Faulty device is made inactive
|
||||||
hr = IMMDevice_GetId(paWasapi->devInfo[i].device, &pszDeviceId);
|
if ((result = FillInactiveDeviceInfo(paWasapi, deviceInfo)) != paNoError)
|
||||||
// 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;
|
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
hostApi->deviceInfos[i] = deviceInfo;
|
hostApi->deviceInfos[i] = deviceInfo;
|
||||||
++hostApi->info.deviceCount;
|
++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 defined(PA_WASAPI_MAX_CONST_DEVICE_COUNT) && (PA_WASAPI_MAX_CONST_DEVICE_COUNT > 0)
|
||||||
if (hostApi->info.deviceCount < PA_WASAPI_MAX_CONST_DEVICE_COUNT)
|
if (hostApi->info.deviceCount < PA_WASAPI_MAX_CONST_DEVICE_COUNT)
|
||||||
{
|
{
|
||||||
for (i = hostApi->info.deviceCount; i < PA_WASAPI_MAX_CONST_DEVICE_COUNT; ++i)
|
for (i = hostApi->info.deviceCount; i < PA_WASAPI_MAX_CONST_DEVICE_COUNT; ++i)
|
||||||
{
|
{
|
||||||
PaDeviceInfo *deviceInfo = &deviceInfoArray[i];
|
PaDeviceInfo *deviceInfo = &deviceInfoArray[i];
|
||||||
deviceInfo->structVersion = 2;
|
|
||||||
deviceInfo->hostApi = hostApiIndex;
|
|
||||||
|
|
||||||
if ((deviceInfo->name = (char *)PaUtil_GroupAllocateMemory(paWasapi->allocations, 1)) == NULL)
|
FillBaseDeviceInfo(deviceInfo, hostApiIndex);
|
||||||
{
|
|
||||||
result = paInsufficientMemory;
|
if ((result = FillInactiveDeviceInfo(paWasapi, deviceInfo)) != paNoError)
|
||||||
goto error;
|
goto error;
|
||||||
}
|
|
||||||
((char *)deviceInfo->name)[0] = 0;
|
|
||||||
|
|
||||||
hostApi->deviceInfos[i] = deviceInfo;
|
hostApi->deviceInfos[i] = deviceInfo;
|
||||||
++hostApi->info.deviceCount;
|
++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
|
#ifndef PA_WINRT
|
||||||
|
CoTaskMemFree(defaultRenderer);
|
||||||
|
CoTaskMemFree(defaultCapturer);
|
||||||
SAFE_RELEASE(pEndPoints);
|
SAFE_RELEASE(pEndPoints);
|
||||||
SAFE_RELEASE(pEnumerator);
|
SAFE_RELEASE(pEnumerator);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
PRINT(("WASAPI: device list created ok\n"));
|
return result;
|
||||||
|
|
||||||
return paNoError;
|
|
||||||
|
|
||||||
error:
|
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
|
// Safety if error was not set so that we do not think initialize was a success
|
||||||
if (result == paNoError)
|
if (result == paNoError)
|
||||||
result = paInternalError;
|
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)
|
PA_THREAD_FUNC ProcThreadEvent(void *param)
|
||||||
{
|
{
|
||||||
PaWasapiHostProcessor processor[S_COUNT];
|
PaWasapiHostProcessor processor[S_COUNT];
|
||||||
HRESULT hr;
|
HRESULT hr = S_OK;
|
||||||
DWORD dwResult;
|
DWORD dwResult;
|
||||||
PaWasapiStream *stream = (PaWasapiStream *)param;
|
PaWasapiStream *stream = (PaWasapiStream *)param;
|
||||||
PaWasapiHostProcessor defaultProcessor;
|
PaWasapiHostProcessor defaultProcessor;
|
||||||
|
|
@ -5931,7 +5966,7 @@ static inline INT32 GetNextSleepTime(SystemTimer *timer, ThreadIdleScheduler *sc
|
||||||
PA_THREAD_FUNC ProcThreadPoll(void *param)
|
PA_THREAD_FUNC ProcThreadPoll(void *param)
|
||||||
{
|
{
|
||||||
PaWasapiHostProcessor processor[S_COUNT];
|
PaWasapiHostProcessor processor[S_COUNT];
|
||||||
HRESULT hr;
|
HRESULT hr = S_OK;
|
||||||
PaWasapiStream *stream = (PaWasapiStream *)param;
|
PaWasapiStream *stream = (PaWasapiStream *)param;
|
||||||
PaWasapiHostProcessor defaultProcessor;
|
PaWasapiHostProcessor defaultProcessor;
|
||||||
INT32 i;
|
INT32 i;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue