Commit adef2adf authored by David Reid's avatar David Reid

WASAPI: Refactoring to device enumeration.

Public issue https://github.com/dr-soft/miniaudio/issues/126
parent c67175c3
...@@ -2515,6 +2515,11 @@ typedef struct ...@@ -2515,6 +2515,11 @@ typedef struct
ma_uint32 maxChannels; ma_uint32 maxChannels;
ma_uint32 minSampleRate; ma_uint32 minSampleRate;
ma_uint32 maxSampleRate; ma_uint32 maxSampleRate;
struct
{
ma_bool32 isDefault;
} _private;
} ma_device_info; } ma_device_info;
typedef struct typedef struct
...@@ -9520,6 +9525,89 @@ static ma_result ma_context_get_device_info_from_IAudioClient__wasapi(ma_context ...@@ -9520,6 +9525,89 @@ static ma_result ma_context_get_device_info_from_IAudioClient__wasapi(ma_context
} }
#ifdef MA_WIN32_DESKTOP #ifdef MA_WIN32_DESKTOP
static ma_EDataFlow ma_device_type_to_EDataFlow(ma_device_type deviceType)
{
if (deviceType == ma_device_type_playback) {
return ma_eRender;
} else if (deviceType == ma_device_type_capture) {
return ma_eCapture;
} else {
MA_ASSERT(MA_FALSE);
return ma_eRender; /* Should never hit this. */
}
}
static ma_result ma_context_create_IMMDeviceEnumerator__wasapi(ma_context* pContext, ma_IMMDeviceEnumerator** ppDeviceEnumerator)
{
HRESULT hr;
ma_IMMDeviceEnumerator* pDeviceEnumerator;
MA_ASSERT(pContext != NULL);
MA_ASSERT(ppDeviceEnumerator != NULL);
hr = ma_CoCreateInstance(pContext, MA_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, MA_IID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator);
if (FAILED(hr)) {
return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create device enumerator.", MA_ERROR);
}
*ppDeviceEnumerator = pDeviceEnumerator;
return MA_SUCCESS;
}
static LPWSTR ma_context_get_default_device_id_from_IMMDeviceEnumerator__wasapi(ma_context* pContext, ma_IMMDeviceEnumerator* pDeviceEnumerator, ma_device_type deviceType)
{
HRESULT hr;
ma_IMMDevice* pMMDefaultDevice = NULL;
LPWSTR pDefaultDeviceID = NULL;
ma_EDataFlow dataFlow;
ma_ERole role;
MA_ASSERT(pContext != NULL);
MA_ASSERT(pDeviceEnumerator != NULL);
/* Grab the EDataFlow type from the device type. */
dataFlow = ma_device_type_to_EDataFlow(deviceType);
/* The role is always eConsole, but we may make this configurable later. */
role = ma_eConsole;
hr = ma_IMMDeviceEnumerator_GetDefaultAudioEndpoint(pDeviceEnumerator, dataFlow, role, &pMMDefaultDevice);
if (FAILED(hr)) {
return NULL;
}
hr = ma_IMMDevice_GetId(pMMDefaultDevice, &pDefaultDeviceID);
ma_IMMDevice_Release(pMMDefaultDevice);
pMMDefaultDevice = NULL;
if (FAILED(hr)) {
return NULL;
}
return pDefaultDeviceID;
}
static LPWSTR ma_context_get_default_device_id__wasapi(ma_context* pContext, ma_device_type deviceType) /* Free the returned pointer with ma_CoTaskMemFree() */
{
ma_result result;
ma_IMMDeviceEnumerator* pDeviceEnumerator;
LPWSTR pDefaultDeviceID = NULL;
MA_ASSERT(pContext != NULL);
result = ma_context_create_IMMDeviceEnumerator__wasapi(pContext, &pDeviceEnumerator);
if (result != MA_SUCCESS) {
return NULL;
}
pDefaultDeviceID = ma_context_get_default_device_id_from_IMMDeviceEnumerator__wasapi(pContext, pDeviceEnumerator, deviceType);
ma_IMMDeviceEnumerator_Release(pDeviceEnumerator);
return pDefaultDeviceID;
}
static ma_result ma_context_get_MMDevice__wasapi(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_IMMDevice** ppMMDevice) static ma_result ma_context_get_MMDevice__wasapi(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_IMMDevice** ppMMDevice)
{ {
ma_IMMDeviceEnumerator* pDeviceEnumerator; ma_IMMDeviceEnumerator* pDeviceEnumerator;
...@@ -9547,9 +9635,9 @@ static ma_result ma_context_get_MMDevice__wasapi(ma_context* pContext, ma_device ...@@ -9547,9 +9635,9 @@ static ma_result ma_context_get_MMDevice__wasapi(ma_context* pContext, ma_device
return MA_SUCCESS; return MA_SUCCESS;
} }
static ma_result ma_context_get_device_info_from_MMDevice__wasapi(ma_context* pContext, ma_IMMDevice* pMMDevice, ma_share_mode shareMode, ma_bool32 onlySimpleInfo, ma_device_info* pInfo) static ma_result ma_context_get_device_info_from_MMDevice__wasapi(ma_context* pContext, ma_IMMDevice* pMMDevice, ma_share_mode shareMode, LPWSTR pDefaultDeviceID, ma_bool32 onlySimpleInfo, ma_device_info* pInfo)
{ {
LPWSTR id; LPWSTR pDeviceID;
HRESULT hr; HRESULT hr;
MA_ASSERT(pContext != NULL); MA_ASSERT(pContext != NULL);
...@@ -9557,19 +9645,26 @@ static ma_result ma_context_get_device_info_from_MMDevice__wasapi(ma_context* pC ...@@ -9557,19 +9645,26 @@ static ma_result ma_context_get_device_info_from_MMDevice__wasapi(ma_context* pC
MA_ASSERT(pInfo != NULL); MA_ASSERT(pInfo != NULL);
/* ID. */ /* ID. */
hr = ma_IMMDevice_GetId(pMMDevice, &id); hr = ma_IMMDevice_GetId(pMMDevice, &pDeviceID);
if (SUCCEEDED(hr)) { if (SUCCEEDED(hr)) {
size_t idlen = wcslen(id); size_t idlen = wcslen(pDeviceID);
if (idlen+1 > ma_countof(pInfo->id.wasapi)) { if (idlen+1 > ma_countof(pInfo->id.wasapi)) {
ma_CoTaskMemFree(pContext, id); ma_CoTaskMemFree(pContext, pDeviceID);
MA_ASSERT(MA_FALSE); /* NOTE: If this is triggered, please report it. It means the format of the ID must haved change and is too long to fit in our fixed sized buffer. */ MA_ASSERT(MA_FALSE); /* NOTE: If this is triggered, please report it. It means the format of the ID must haved change and is too long to fit in our fixed sized buffer. */
return MA_ERROR; return MA_ERROR;
} }
MA_COPY_MEMORY(pInfo->id.wasapi, id, idlen * sizeof(wchar_t)); MA_COPY_MEMORY(pInfo->id.wasapi, pDeviceID, idlen * sizeof(wchar_t));
pInfo->id.wasapi[idlen] = '\0'; pInfo->id.wasapi[idlen] = '\0';
ma_CoTaskMemFree(pContext, id); if (pDefaultDeviceID != NULL) {
if (wcscmp(pDeviceID, pDefaultDeviceID) == 0) {
/* It's a default device. */
pInfo->_private.isDefault = MA_TRUE;
}
}
ma_CoTaskMemFree(pContext, pDeviceID);
} }
{ {
...@@ -9607,45 +9702,65 @@ static ma_result ma_context_get_device_info_from_MMDevice__wasapi(ma_context* pC ...@@ -9607,45 +9702,65 @@ static ma_result ma_context_get_device_info_from_MMDevice__wasapi(ma_context* pC
return MA_SUCCESS; return MA_SUCCESS;
} }
static ma_result ma_context_enumerate_device_collection__wasapi(ma_context* pContext, ma_IMMDeviceCollection* pDeviceCollection, ma_device_type deviceType, ma_enum_devices_callback_proc callback, void* pUserData) static ma_result ma_context_enumerate_devices_by_type__wasapi(ma_context* pContext, ma_IMMDeviceEnumerator* pDeviceEnumerator, ma_device_type deviceType, ma_enum_devices_callback_proc callback, void* pUserData)
{ {
ma_result result = MA_SUCCESS;
UINT deviceCount; UINT deviceCount;
HRESULT hr; HRESULT hr;
ma_uint32 iDevice; ma_uint32 iDevice;
LPWSTR pDefaultDeviceID = NULL;
ma_IMMDeviceCollection* pDeviceCollection = NULL;
MA_ASSERT(pContext != NULL); MA_ASSERT(pContext != NULL);
MA_ASSERT(callback != NULL); MA_ASSERT(callback != NULL);
hr = ma_IMMDeviceCollection_GetCount(pDeviceCollection, &deviceCount); /* Grab the default device. We use this to know whether or not flag the returned device info as being the default. */
if (FAILED(hr)) { pDefaultDeviceID = ma_context_get_default_device_id_from_IMMDeviceEnumerator__wasapi(pContext, pDeviceEnumerator, deviceType);
return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to get playback device count.", MA_NO_DEVICE);
}
for (iDevice = 0; iDevice < deviceCount; ++iDevice) { /* We need to enumerate the devices which returns a device collection. */
ma_device_info deviceInfo; hr = ma_IMMDeviceEnumerator_EnumAudioEndpoints(pDeviceEnumerator, ma_device_type_to_EDataFlow(deviceType), MA_MM_DEVICE_STATE_ACTIVE, &pDeviceCollection);
ma_IMMDevice* pMMDevice; if (SUCCEEDED(hr)) {
hr = ma_IMMDeviceCollection_GetCount(pDeviceCollection, &deviceCount);
if (FAILED(hr)) {
result = ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to get device count.", MA_NO_DEVICE);
goto done;
}
for (iDevice = 0; iDevice < deviceCount; ++iDevice) {
ma_device_info deviceInfo;
ma_IMMDevice* pMMDevice;
MA_ZERO_OBJECT(&deviceInfo); MA_ZERO_OBJECT(&deviceInfo);
hr = ma_IMMDeviceCollection_Item(pDeviceCollection, iDevice, &pMMDevice); hr = ma_IMMDeviceCollection_Item(pDeviceCollection, iDevice, &pMMDevice);
if (SUCCEEDED(hr)) { if (SUCCEEDED(hr)) {
ma_result result = ma_context_get_device_info_from_MMDevice__wasapi(pContext, pMMDevice, ma_share_mode_shared, MA_TRUE, &deviceInfo); /* MA_TRUE = onlySimpleInfo. */ result = ma_context_get_device_info_from_MMDevice__wasapi(pContext, pMMDevice, ma_share_mode_shared, pDefaultDeviceID, MA_TRUE, &deviceInfo); /* MA_TRUE = onlySimpleInfo. */
ma_IMMDevice_Release(pMMDevice); ma_IMMDevice_Release(pMMDevice);
if (result == MA_SUCCESS) { if (result == MA_SUCCESS) {
ma_bool32 cbResult = callback(pContext, deviceType, &deviceInfo, pUserData); ma_bool32 cbResult = callback(pContext, deviceType, &deviceInfo, pUserData);
if (cbResult == MA_FALSE) { if (cbResult == MA_FALSE) {
break; break;
}
} }
} }
} }
} }
return MA_SUCCESS; done:
if (pDefaultDeviceID != NULL) {
ma_CoTaskMemFree(pContext, pDefaultDeviceID);
pDefaultDeviceID = NULL;
}
if (pDeviceCollection != NULL) {
ma_IMMDeviceCollection_Release(pDeviceCollection);
pDeviceCollection = NULL;
}
return result;
} }
#endif
#ifdef MA_WIN32_DESKTOP
static ma_result ma_context_get_IAudioClient_Desktop__wasapi(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_IAudioClient** ppAudioClient, ma_IMMDevice** ppMMDevice) static ma_result ma_context_get_IAudioClient_Desktop__wasapi(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_IAudioClient** ppAudioClient, ma_IMMDevice** ppMMDevice)
{ {
ma_result result; ma_result result;
...@@ -9764,26 +9879,14 @@ static ma_result ma_context_enumerate_devices__wasapi(ma_context* pContext, ma_e ...@@ -9764,26 +9879,14 @@ static ma_result ma_context_enumerate_devices__wasapi(ma_context* pContext, ma_e
/* Desktop */ /* Desktop */
HRESULT hr; HRESULT hr;
ma_IMMDeviceEnumerator* pDeviceEnumerator; ma_IMMDeviceEnumerator* pDeviceEnumerator;
ma_IMMDeviceCollection* pDeviceCollection;
hr = ma_CoCreateInstance(pContext, MA_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, MA_IID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator); hr = ma_CoCreateInstance(pContext, MA_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, MA_IID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator);
if (FAILED(hr)) { if (FAILED(hr)) {
return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create device enumerator.", MA_FAILED_TO_OPEN_BACKEND_DEVICE); return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create device enumerator.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
} }
/* Playback. */ ma_context_enumerate_devices_by_type__wasapi(pContext, pDeviceEnumerator, ma_device_type_playback, callback, pUserData);
hr = ma_IMMDeviceEnumerator_EnumAudioEndpoints(pDeviceEnumerator, ma_eRender, MA_MM_DEVICE_STATE_ACTIVE, &pDeviceCollection); ma_context_enumerate_devices_by_type__wasapi(pContext, pDeviceEnumerator, ma_device_type_capture, callback, pUserData);
if (SUCCEEDED(hr)) {
ma_context_enumerate_device_collection__wasapi(pContext, pDeviceCollection, ma_device_type_playback, callback, pUserData);
ma_IMMDeviceCollection_Release(pDeviceCollection);
}
/* Capture. */
hr = ma_IMMDeviceEnumerator_EnumAudioEndpoints(pDeviceEnumerator, ma_eCapture, MA_MM_DEVICE_STATE_ACTIVE, &pDeviceCollection);
if (SUCCEEDED(hr)) {
ma_context_enumerate_device_collection__wasapi(pContext, pDeviceCollection, ma_device_type_capture, callback, pUserData);
ma_IMMDeviceCollection_Release(pDeviceCollection);
}
ma_IMMDeviceEnumerator_Release(pDeviceEnumerator); ma_IMMDeviceEnumerator_Release(pDeviceEnumerator);
#else #else
...@@ -9803,6 +9906,7 @@ static ma_result ma_context_enumerate_devices__wasapi(ma_context* pContext, ma_e ...@@ -9803,6 +9906,7 @@ static ma_result ma_context_enumerate_devices__wasapi(ma_context* pContext, ma_e
ma_device_info deviceInfo; ma_device_info deviceInfo;
MA_ZERO_OBJECT(&deviceInfo); MA_ZERO_OBJECT(&deviceInfo);
ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
deviceInfo._private.isDefault = MA_TRUE;
cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData); cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);
} }
...@@ -9811,6 +9915,7 @@ static ma_result ma_context_enumerate_devices__wasapi(ma_context* pContext, ma_e ...@@ -9811,6 +9915,7 @@ static ma_result ma_context_enumerate_devices__wasapi(ma_context* pContext, ma_e
ma_device_info deviceInfo; ma_device_info deviceInfo;
MA_ZERO_OBJECT(&deviceInfo); MA_ZERO_OBJECT(&deviceInfo);
ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
deviceInfo._private.isDefault = MA_TRUE;
cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData); cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);
} }
} }
...@@ -9822,17 +9927,27 @@ static ma_result ma_context_enumerate_devices__wasapi(ma_context* pContext, ma_e ...@@ -9822,17 +9927,27 @@ static ma_result ma_context_enumerate_devices__wasapi(ma_context* pContext, ma_e
static ma_result ma_context_get_device_info__wasapi(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_share_mode shareMode, ma_device_info* pDeviceInfo) static ma_result ma_context_get_device_info__wasapi(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_share_mode shareMode, ma_device_info* pDeviceInfo)
{ {
#ifdef MA_WIN32_DESKTOP #ifdef MA_WIN32_DESKTOP
ma_IMMDevice* pMMDevice = NULL;
ma_result result; ma_result result;
ma_IMMDevice* pMMDevice = NULL;
LPWSTR pDefaultDeviceID = NULL;
result = ma_context_get_MMDevice__wasapi(pContext, deviceType, pDeviceID, &pMMDevice); result = ma_context_get_MMDevice__wasapi(pContext, deviceType, pDeviceID, &pMMDevice);
if (result != MA_SUCCESS) { if (result != MA_SUCCESS) {
return result; return result;
} }
result = ma_context_get_device_info_from_MMDevice__wasapi(pContext, pMMDevice, shareMode, MA_FALSE, pDeviceInfo); /* MA_FALSE = !onlySimpleInfo. */ /* We need the default device ID so we can set the isDefault flag in the device info. */
pDefaultDeviceID = ma_context_get_default_device_id__wasapi(pContext, deviceType);
result = ma_context_get_device_info_from_MMDevice__wasapi(pContext, pMMDevice, shareMode, pDefaultDeviceID, MA_FALSE, pDeviceInfo); /* MA_FALSE = !onlySimpleInfo. */
if (pDefaultDeviceID != NULL) {
ma_CoTaskMemFree(pContext, pDefaultDeviceID);
pDefaultDeviceID = NULL;
}
ma_IMMDevice_Release(pMMDevice); ma_IMMDevice_Release(pMMDevice);
return result; return result;
#else #else
ma_IAudioClient* pAudioClient; ma_IAudioClient* pAudioClient;
...@@ -9857,6 +9972,8 @@ static ma_result ma_context_get_device_info__wasapi(ma_context* pContext, ma_dev ...@@ -9857,6 +9972,8 @@ static ma_result ma_context_get_device_info__wasapi(ma_context* pContext, ma_dev
result = ma_context_get_device_info_from_IAudioClient__wasapi(pContext, NULL, pAudioClient, shareMode, pDeviceInfo); result = ma_context_get_device_info_from_IAudioClient__wasapi(pContext, NULL, pAudioClient, shareMode, pDeviceInfo);
pDeviceInfo->_private.isDefault = MA_TRUE; /* UWP only supports default devices. */
ma_IAudioClient_Release(pAudioClient); ma_IAudioClient_Release(pAudioClient);
return result; return result;
#endif #endif
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment