Commit 76905d98 authored by David Reid's avatar David Reid

Start work on adding support for detailed device info retrieval.

parent 8e61ea0e
......@@ -691,6 +691,7 @@ typedef enum
mal_format_s24 = 3, // Tightly packed. 3 bytes per sample.
mal_format_s32 = 4,
mal_format_f32 = 5,
mal_format_count
} mal_format;
typedef enum
......@@ -756,8 +757,18 @@ typedef struct
mal_device_id id;
char name[256];
// Detailed info. As much of this is filled as possible with mal_context_get_device_info().
// TODO: Implement me.
// Detailed info. As much of this is filled as possible with mal_context_get_device_info(). If anything here is set to 0 it means
// the backend does not have a reliable way of determining the value. Note that you are allowed to initialize a device with settings
// outside of this range, but it just means the data will be converted using mini_al's data conversion pipeline before sending the
// data to/from the device.
//
// These will be set to 0 when returned by mal_context_enumerate_devices() or mal_context_get_devices().
mal_uint32 formatCount;
mal_format formats[mal_format_count];
mal_uint32 minChannels;
mal_uint32 maxChannels;
mal_uint32 minSampleRate;
mal_uint32 maxSampleRate;
} mal_device_info;
typedef struct
......@@ -3922,6 +3933,17 @@ mal_result mal_context_get_device_info__null(mal_context* pContext, mal_device_t
mal_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), "NULL Capture Device", (size_t)-1);
}
// Support everything on the null backend.
pDeviceInfo->formatCount = mal_format_count;
for (mal_uint32 iFormat = 0; iFormat < pDeviceInfo->formatCount; ++iFormat) {
pDeviceInfo->formats[iFormat] = (mal_format)iFormat;
}
pDeviceInfo->minChannels = 1;
pDeviceInfo->maxChannels = MAL_MAX_CHANNELS;
pDeviceInfo->minSampleRate = MAL_SAMPLE_RATE_8000;
pDeviceInfo->maxSampleRate = MAL_SAMPLE_RATE_384000;
return MAL_SUCCESS;
}
......@@ -4307,12 +4329,12 @@ void mal_channel_mask_to_channel_map__win32(DWORD dwChannelMask, mal_uint32 chan
#define mal_is_guid_equal(a, b) IsEqualGUID((const GUID*)a, (const GUID*)b)
#endif
mal_format mal_format_from_WAVEFORMATEX(WAVEFORMATEX* pWF)
mal_format mal_format_from_WAVEFORMATEX(const WAVEFORMATEX* pWF)
{
mal_assert(pWF != NULL);
if (pWF->wFormatTag == WAVE_FORMAT_EXTENSIBLE) {
WAVEFORMATEXTENSIBLE* pWFEX = (WAVEFORMATEXTENSIBLE*)pWF;
const WAVEFORMATEXTENSIBLE* pWFEX = (const WAVEFORMATEXTENSIBLE*)pWF;
if (mal_is_guid_equal(&pWFEX->SubFormat, &MAL_GUID_KSDATAFORMAT_SUBTYPE_PCM)) {
if (pWFEX->Samples.wValidBitsPerSample == 32) {
return mal_format_s32;
......@@ -4697,6 +4719,61 @@ HRESULT mal_IAudioCaptureClient_GetBuffer(mal_IAudioCaptureClient* pThis, BYTE**
HRESULT mal_IAudioCaptureClient_ReleaseBuffer(mal_IAudioCaptureClient* pThis, UINT32 numFramesRead) { return pThis->lpVtbl->ReleaseBuffer(pThis, numFramesRead); }
HRESULT mal_IAudioCaptureClient_GetNextPacketSize(mal_IAudioCaptureClient* pThis, UINT32* pNumFramesInNextPacket) { return pThis->lpVtbl->GetNextPacketSize(pThis, pNumFramesInNextPacket); }
// This is the part that's preventing mini_al from being compiled as C with UWP. We need to implement IActivateAudioInterfaceCompletionHandler
// in C which is quite annoying.
#ifndef MAL_WIN32_DESKTOP
#ifdef __cplusplus
#include <mmdeviceapi.h>
#include <wrl\implements.h>
class malCompletionHandler : public Microsoft::WRL::RuntimeClass< Microsoft::WRL::RuntimeClassFlags< Microsoft::WRL::ClassicCom >, Microsoft::WRL::FtmBase, IActivateAudioInterfaceCompletionHandler >
{
public:
malCompletionHandler()
: m_hEvent(NULL)
{
}
mal_result Init()
{
m_hEvent = CreateEventA(NULL, FALSE, FALSE, NULL);
if (m_hEvent == NULL) {
return MAL_ERROR;
}
return MAL_SUCCESS;
}
void Uninit()
{
if (m_hEvent != NULL) {
CloseHandle(m_hEvent);
}
}
void Wait()
{
WaitForSingleObject(m_hEvent, INFINITE);
}
HRESULT STDMETHODCALLTYPE ActivateCompleted(IActivateAudioInterfaceAsyncOperation *activateOperation)
{
(void)activateOperation;
SetEvent(m_hEvent);
return S_OK;
}
private:
HANDLE m_hEvent; // This is created in Init(), deleted in Uninit(), waited on in Wait() and signaled in ActivateCompleted().
};
#else
#error "The UWP build is currently only supported in C++."
#endif
#endif // !MAL_WIN32_DESKTOP
mal_bool32 mal_context_is_device_id_equal__wasapi(mal_context* pContext, const mal_device_id* pID0, const mal_device_id* pID1)
{
mal_assert(pContext != NULL);
......@@ -4707,6 +4784,227 @@ mal_bool32 mal_context_is_device_id_equal__wasapi(mal_context* pContext, const m
return memcmp(pID0->wasapi, pID1->wasapi, sizeof(pID0->wasapi)) == 0;
}
void mal_set_device_info_from_WAVEFORMATEX(const WAVEFORMATEX* pWF, mal_device_info* pInfo)
{
mal_assert(pWF != NULL);
mal_assert(pInfo != NULL);
pInfo->formatCount = 1;
pInfo->formats[0] = mal_format_from_WAVEFORMATEX(pWF);
pInfo->minChannels = pWF->nChannels;
pInfo->maxChannels = pWF->nChannels;
pInfo->minSampleRate = pWF->nSamplesPerSec;
pInfo->maxSampleRate = pWF->nSamplesPerSec;
}
#ifndef MAL_WIN32_DESKTOP
mal_result mal_context_get_IAudioClient_UWP__wasapi(mal_context* pContext, mal_device_type deviceType, const mal_device_id* pDeviceID, mal_IAudioClient** ppAudioClient, IUnknown** ppActivatedInterface)
{
mal_assert(pContext != NULL);
mal_assert(ppAudioClient != NULL);
mal_IActivateAudioInterfaceAsyncOperation *pAsyncOp = NULL;
malCompletionHandler completionHandler;
IID iid;
if (pDeviceID != NULL) {
mal_copy_memory(&iid, pDeviceID->wasapi, sizeof(iid));
} else {
if (deviceType == mal_device_type_playback) {
iid = MAL_IID_DEVINTERFACE_AUDIO_RENDER;
} else {
iid = MAL_IID_DEVINTERFACE_AUDIO_CAPTURE;
}
}
LPOLESTR iidStr;
HRESULT hr = StringFromIID(iid, &iidStr);
if (FAILED(hr)) {
return mal_context_post_error(pContext, NULL, "[WASAPI] Failed to convert device IID to string for ActivateAudioInterfaceAsync(). Out of memory.", MAL_OUT_OF_MEMORY);
}
mal_result result = completionHandler.Init();
if (result != MAL_SUCCESS) {
mal_CoTaskMemFree(pContext, iidStr);
return mal_context_post_error(pContext, NULL, "[WASAPI] Failed to create event for waiting for ActivateAudioInterfaceAsync().", MAL_FAILED_TO_OPEN_BACKEND_DEVICE);
}
hr = ActivateAudioInterfaceAsync(iidStr, MAL_IID_IAudioClient, NULL, (IActivateAudioInterfaceCompletionHandler*)&completionHandler, (IActivateAudioInterfaceAsyncOperation**)&pAsyncOp);
if (FAILED(hr)) {
completionHandler.Uninit();
mal_CoTaskMemFree(pContext, iidStr);
return mal_context_post_error(pContext, NULL, "[WASAPI] ActivateAudioInterfaceAsync() failed.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE);
}
mal_CoTaskMemFree(pContext, iidStr);
// Wait for the async operation for finish.
completionHandler.Wait();
completionHandler.Uninit();
HRESULT activateResult;
IUnknown* pActivatedInterface;
hr = mal_IActivateAudioInterfaceAsyncOperation_GetActivateResult(pAsyncOp, &activateResult, &pActivatedInterface);
mal_IActivateAudioInterfaceAsyncOperation_Release(pAsyncOp);
if (FAILED(hr) || FAILED(activateResult)) {
return mal_context_post_error(pContext, NULL, "[WASAPI] Failed to activate device.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE);
}
// Here is where we grab the IAudioClient interface.
hr = pActivatedInterface->QueryInterface(MAL_IID_IAudioClient, (void**)ppAudioClient);
if (FAILED(hr)) {
return mal_context_post_error(pContext, NULL, "[WASAPI] Failed to query IAudioClient interface.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE);
}
if (ppActivatedInterface) {
*ppActivatedInterface = pActivatedInterface;
} else {
pActivatedInterface->Release();
}
return MAL_SUCCESS;
}
#endif
mal_result mal_context_get_device_info_from_IAudioClient__wasapi(mal_context* pContext, /*mal_IMMDevice**/void* pMMDevice, mal_IAudioClient* pAudioClient, mal_share_mode shareMode, mal_device_info* pInfo)
{
mal_assert(pAudioClient != NULL);
mal_assert(pInfo != NULL);
// We use a different technique to retrieve the device information depending on whether or not we are using shared or exclusive mode.
if (shareMode == mal_share_mode_shared) {
// Shared Mode. We use GetMixFormat() here.
WAVEFORMATEX* pWF = NULL;
HRESULT hr = mal_IAudioClient_GetMixFormat((mal_IAudioClient*)pAudioClient, (WAVEFORMATEX**)&pWF);
if (SUCCEEDED(hr)) {
mal_set_device_info_from_WAVEFORMATEX(pWF, pInfo);
return MAL_SUCCESS;
} else {
return mal_context_post_error(pContext, NULL, "[WASAPI] Failed to retrieve mix format for device info retrieval.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE);
}
} else {
// Exlcusive Mode. We repeatedly call IsFormatSupported() here. This is not currently support on UWP.
#ifdef MAL_WIN32_DESKTOP
// The first thing to do is get the format from PKEY_AudioEngine_DeviceFormat. This should give us a channel count we assume is
// correct which will simplify our searching.
mal_IPropertyStore *pProperties;
HRESULT hr = mal_IMMDevice_OpenPropertyStore((mal_IMMDevice*)pMMDevice, STGM_READ, &pProperties);
if (SUCCEEDED(hr)) {
PROPVARIANT var;
mal_PropVariantInit(&var);
hr = mal_IPropertyStore_GetValue(pProperties, &MAL_PKEY_AudioEngine_DeviceFormat, &var);
if (SUCCEEDED(hr)) {
WAVEFORMATEX* pWF = (WAVEFORMATEX*)var.blob.pBlobData;
mal_set_device_info_from_WAVEFORMATEX(pWF, pInfo);
// In my testing, the format returned by PKEY_AudioEngine_DeviceFormat is suitable for exclusive mode so we check this format
// first. If this fails, fall back to a search.
hr = mal_IAudioClient_IsFormatSupported((mal_IAudioClient*)pAudioClient, MAL_AUDCLNT_SHAREMODE_EXCLUSIVE, pWF, NULL);
mal_PropVariantClear(pContext, &var);
if (FAILED(hr)) {
// The format returned by PKEY_AudioEngine_DeviceFormat is not supported, so fall back to a search. We assume the channel
// count returned by MAL_PKEY_AudioEngine_DeviceFormat is valid and correct. For simplicity we're only returning one format.
mal_uint32 channels = pInfo->minChannels;
mal_format formatsToSearch[] = {
mal_format_s16,
mal_format_s24,
//mal_format_s24_32,
mal_format_f32,
mal_format_s32,
mal_format_u8
};
mal_channel defaultChannelMap[MAL_MAX_CHANNELS];
mal_get_standard_channel_map(mal_standard_channel_map_microsoft, channels, defaultChannelMap);
WAVEFORMATEXTENSIBLE wf;
mal_zero_object(&wf);
wf.Format.cbSize = sizeof(wf);
wf.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
wf.Format.nChannels = (WORD)channels;
wf.dwChannelMask = mal_channel_map_to_channel_mask__win32(defaultChannelMap, channels);
mal_bool32 found = MAL_FALSE;
for (mal_uint32 iFormat = 0; iFormat < mal_countof(formatsToSearch); ++iFormat) {
mal_format format = formatsToSearch[iFormat];
wf.Format.wBitsPerSample = (WORD)mal_get_bytes_per_sample(format)*8;
wf.Format.nBlockAlign = (wf.Format.nChannels * wf.Format.wBitsPerSample) / 8;
wf.Format.nAvgBytesPerSec = wf.Format.nBlockAlign * wf.Format.nSamplesPerSec;
wf.Samples.wValidBitsPerSample = /*(format == mal_format_s24_32) ? 24 :*/ wf.Format.wBitsPerSample;
if (format == mal_format_f32) {
wf.SubFormat = MAL_GUID_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
} else {
wf.SubFormat = MAL_GUID_KSDATAFORMAT_SUBTYPE_PCM;
}
for (mal_uint32 iSampleRate = 0; iSampleRate < mal_countof(g_malStandardSampleRatePriorities); ++iSampleRate) {
wf.Format.nSamplesPerSec = g_malStandardSampleRatePriorities[iSampleRate];
hr = mal_IAudioClient_IsFormatSupported((mal_IAudioClient*)pAudioClient, MAL_AUDCLNT_SHAREMODE_EXCLUSIVE, (WAVEFORMATEX*)&wf, NULL);
if (SUCCEEDED(hr)) {
mal_set_device_info_from_WAVEFORMATEX((WAVEFORMATEX*)&wf, pInfo);
found = MAL_TRUE;
break;
}
}
if (found) {
break;
}
}
if (!found) {
mal_IPropertyStore_Release(pProperties);
return mal_context_post_error(pContext, NULL, "[WASAPI] Failed to find suitable device format for device info retrieval.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE);
}
}
} else {
mal_IPropertyStore_Release(pProperties);
return mal_context_post_error(pContext, NULL, "[WASAPI] Failed to retrieve device format for device info retrieval.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE);
}
} else {
return mal_context_post_error(pContext, NULL, "[WASAPI] Failed to open property store for device info retrieval.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE);
}
return MAL_SUCCESS;
#else
// Exclusive mode not fully supported in UWP right now.
return MAL_ERROR;
#endif
}
}
#ifdef MAL_WIN32_DESKTOP
mal_result mal_context_get_MMDevice__wasapi(mal_context* pContext, mal_device_type deviceType, const mal_device_id* pDeviceID, mal_IMMDevice** ppMMDevice)
{
mal_assert(pContext != NULL);
mal_assert(ppMMDevice != NULL);
mal_IMMDeviceEnumerator* pDeviceEnumerator;
HRESULT hr = mal_CoCreateInstance(pContext, MAL_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, MAL_IID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator);
if (FAILED(hr)) {
return mal_context_post_error(pContext, NULL, "[WASAPI] Failed to create IMMDeviceEnumerator.", MAL_FAILED_TO_INIT_BACKEND);
}
if (pDeviceID == NULL) {
hr = mal_IMMDeviceEnumerator_GetDefaultAudioEndpoint(pDeviceEnumerator, (deviceType == mal_device_type_playback) ? mal_eRender : mal_eCapture, mal_eConsole, ppMMDevice);
} else {
hr = mal_IMMDeviceEnumerator_GetDevice(pDeviceEnumerator, pDeviceID->wasapi, ppMMDevice);
}
mal_IMMDeviceEnumerator_Release(pDeviceEnumerator);
if (FAILED(hr)) {
return mal_context_post_error(pContext, NULL, "[WASAPI] Failed to retrieve IMMDevice.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE);
}
return MAL_SUCCESS;
}
mal_result mal_context_get_device_info_from_MMDevice__wasapi(mal_context* pContext, mal_IMMDevice* pMMDevice, mal_share_mode shareMode, mal_bool32 onlySimpleInfo, mal_device_info* pInfo)
{
mal_assert(pContext != NULL);
......@@ -4730,31 +5028,36 @@ mal_result mal_context_get_device_info_from_MMDevice__wasapi(mal_context* pConte
mal_CoTaskMemFree(pContext, id);
}
mal_IPropertyStore *pProperties;
hr = mal_IMMDevice_OpenPropertyStore(pMMDevice, STGM_READ, &pProperties);
if (SUCCEEDED(hr)) {
PROPVARIANT var;
// Description / Friendly Name
mal_PropVariantInit(&var);
hr = mal_IPropertyStore_GetValue(pProperties, &MAL_PKEY_Device_FriendlyName, &var);
{
mal_IPropertyStore *pProperties;
hr = mal_IMMDevice_OpenPropertyStore(pMMDevice, STGM_READ, &pProperties);
if (SUCCEEDED(hr)) {
WideCharToMultiByte(CP_UTF8, 0, var.pwszVal, -1, pInfo->name, sizeof(pInfo->name), 0, FALSE);
mal_PropVariantClear(pContext, &var);
}
PROPVARIANT var;
// Description / Friendly Name
mal_PropVariantInit(&var);
hr = mal_IPropertyStore_GetValue(pProperties, &MAL_PKEY_Device_FriendlyName, &var);
if (SUCCEEDED(hr)) {
WideCharToMultiByte(CP_UTF8, 0, var.pwszVal, -1, pInfo->name, sizeof(pInfo->name), 0, FALSE);
mal_PropVariantClear(pContext, &var);
}
// Format
if (!onlySimpleInfo) {
// TODO:
// - Get MAL_PKEY_AudioEngine_DeviceFormat for the channel count
// - Open the device
// - If shared mode, call GetMixFormat()
// - If exclusive mode, loop over most common formats (s16, s24, f32), then each of the standard sample rates.
// - If anything fails, don't allow exclusive mode for this device.
(void)shareMode;
mal_IPropertyStore_Release(pProperties);
}
}
mal_IPropertyStore_Release(pProperties);
// Format
if (!onlySimpleInfo) {
mal_IAudioClient* pAudioClient;
hr = mal_IMMDevice_Activate(pMMDevice, &MAL_IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&pAudioClient);
if (SUCCEEDED(hr)) {
mal_result result = mal_context_get_device_info_from_IAudioClient__wasapi(pContext, pMMDevice, pAudioClient, shareMode, pInfo);
mal_IAudioClient_Release(pAudioClient);
return result;
} else {
return mal_context_post_error(pContext, NULL, "[WASAPI] Failed to activate audio client for device info retrieval.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE);
}
}
return MAL_SUCCESS;
......@@ -4792,6 +5095,7 @@ mal_result mal_context_enumerate_device_collection__wasapi(mal_context* pContext
return MAL_SUCCESS;
}
#endif
mal_result mal_context_enumerate_devices__wasapi(mal_context* pContext, mal_enum_devices_callback_proc callback, void* pUserData)
{
......@@ -4857,28 +5161,41 @@ mal_result mal_context_enumerate_devices__wasapi(mal_context* pContext, mal_enum
mal_result mal_context_get_device_info__wasapi(mal_context* pContext, mal_device_type deviceType, const mal_device_id* pDeviceID, mal_share_mode shareMode, mal_device_info* pDeviceInfo)
{
mal_IMMDeviceEnumerator* pDeviceEnumerator;
HRESULT hr = mal_CoCreateInstance(pContext, MAL_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, MAL_IID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator);
if (FAILED(hr)) {
return mal_context_post_error(pContext, NULL, "[WASAPI] Failed to create IMMDeviceEnumerator.", MAL_FAILED_TO_INIT_BACKEND);
#ifdef MAL_WIN32_DESKTOP
mal_IMMDevice* pMMDevice = NULL;
mal_result result = mal_context_get_MMDevice__wasapi(pContext, deviceType, pDeviceID, &pMMDevice);
if (result != MAL_SUCCESS) {
return result;
}
mal_IMMDevice* pMMDevice = NULL;
if (pDeviceID == NULL) {
hr = mal_IMMDeviceEnumerator_GetDefaultAudioEndpoint(pDeviceEnumerator, (deviceType == mal_device_type_playback) ? mal_eRender : mal_eCapture, mal_eConsole, &pMMDevice);
result = mal_context_get_device_info_from_MMDevice__wasapi(pContext, pMMDevice, shareMode, MAL_FALSE, pDeviceInfo); // MAL_FALSE = !onlySimpleInfo.
mal_IMMDevice_Release(pMMDevice);
return result;
#else
// UWP currently only uses default devices.
if (deviceType == mal_device_type_playback) {
mal_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MAL_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
} else {
hr = mal_IMMDeviceEnumerator_GetDevice(pDeviceEnumerator, pDeviceID->wasapi, &pMMDevice);
mal_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MAL_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
}
mal_IMMDeviceEnumerator_Release(pDeviceEnumerator);
if (FAILED(hr)) {
return mal_context_post_error(pContext, NULL, "[WASAPI] Failed to retrieve IMMDevice.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE);
// Not currently supporting exclusive mode on UWP.
if (shareMode == mal_share_mode_exclusive) {
return MAL_ERROR;
}
mal_result result = mal_context_get_device_info_from_MMDevice__wasapi(pContext, pMMDevice, shareMode, MAL_FALSE, pDeviceInfo); // MAL_FALSE = !onlySimpleInfo.
mal_IAudioClient* pAudioClient;
mal_result result = mal_context_get_IAudioClient_UWP__wasapi(pContext, deviceType, pDeviceID, &pAudioClient, NULL);
if (result != MAL_SUCCESS) {
return result;
}
mal_IMMDevice_Release(pMMDevice);
result = mal_context_get_device_info_from_IAudioClient__wasapi(pContext, NULL, pAudioClient, shareMode, pDeviceInfo);
mal_IAudioClient_Release(pAudioClient);
return result;
#endif
}
......@@ -4947,59 +5264,6 @@ void mal_device_uninit__wasapi(mal_device* pDevice)
}
}
// This is the part that's preventing mini_al from being compiled as C with UWP. We need to implement IActivateAudioInterfaceCompletionHandler
// in C which is quite annoying.
#ifndef MAL_WIN32_DESKTOP
#ifdef __cplusplus
#include <mmdeviceapi.h>
#include <wrl\implements.h>
class malCompletionHandler : public Microsoft::WRL::RuntimeClass< Microsoft::WRL::RuntimeClassFlags< Microsoft::WRL::ClassicCom >, Microsoft::WRL::FtmBase, IActivateAudioInterfaceCompletionHandler >
{
public:
malCompletionHandler()
: m_hEvent(NULL)
{
}
mal_result Init()
{
m_hEvent = CreateEventA(NULL, FALSE, FALSE, NULL);
if (m_hEvent == NULL) {
return MAL_ERROR;
}
return MAL_SUCCESS;
}
void Uninit()
{
if (m_hEvent != NULL) {
CloseHandle(m_hEvent);
}
}
void Wait()
{
WaitForSingleObject(m_hEvent, INFINITE);
}
HRESULT STDMETHODCALLTYPE ActivateCompleted(IActivateAudioInterfaceAsyncOperation *activateOperation)
{
(void)activateOperation;
SetEvent(m_hEvent);
return S_OK;
}
private:
HANDLE m_hEvent; // This is created in Init(), deleted in Uninit(), waited on in Wait() and signaled in ActivateCompleted().
};
#else
#error "The UWP build is currently only supported in C++."
#endif
#endif // !MAL_WIN32_DESKTOP
mal_result mal_device_init__wasapi(mal_context* pContext, mal_device_type type, mal_device_id* pDeviceID, const mal_device_config* pConfig, mal_device* pDevice)
{
(void)pContext;
......@@ -5016,90 +5280,20 @@ mal_result mal_device_init__wasapi(mal_context* pContext, mal_device_type type,
#ifdef MAL_WIN32_DESKTOP
mal_IMMDevice* pMMDevice = NULL;
mal_IMMDeviceEnumerator* pDeviceEnumerator;
hr = mal_CoCreateInstance(pContext, MAL_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, MAL_IID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator);
if (FAILED(hr)) {
errorMsg = "[WASAPI] Failed to create IMMDeviceEnumerator.", result = MAL_FAILED_TO_OPEN_BACKEND_DEVICE;
goto done;
}
if (pDeviceID == NULL) {
hr = mal_IMMDeviceEnumerator_GetDefaultAudioEndpoint(pDeviceEnumerator, (type == mal_device_type_playback) ? mal_eRender : mal_eCapture, mal_eConsole, &pMMDevice);
} else {
hr = mal_IMMDeviceEnumerator_GetDevice(pDeviceEnumerator, pDeviceID->wasapi, &pMMDevice);
}
if (FAILED(hr)) {
mal_IMMDeviceEnumerator_Release(pDeviceEnumerator);
errorMsg = "[WASAPI] Failed to create backend device.", result = MAL_FAILED_TO_OPEN_BACKEND_DEVICE;
result = mal_context_get_MMDevice__wasapi(pContext, type, pDeviceID, &pMMDevice);
if (result != MAL_SUCCESS) {
goto done;
}
mal_IMMDeviceEnumerator_Release(pDeviceEnumerator);
hr = mal_IMMDevice_Activate(pMMDevice, &MAL_IID_IAudioClient, CLSCTX_ALL, NULL, &pDevice->wasapi.pAudioClient);
if (FAILED(hr)) {
errorMsg = "[WASAPI] Failed to activate device.", result = MAL_FAILED_TO_OPEN_BACKEND_DEVICE;
goto done;
}
#else
mal_IActivateAudioInterfaceAsyncOperation *pAsyncOp = NULL;
malCompletionHandler completionHandler;
IID iid;
if (pDeviceID != NULL) {
mal_copy_memory(&iid, pDeviceID->wasapi, sizeof(iid));
} else {
if (type == mal_device_type_playback) {
iid = MAL_IID_DEVINTERFACE_AUDIO_RENDER;
} else {
iid = MAL_IID_DEVINTERFACE_AUDIO_CAPTURE;
}
}
LPOLESTR iidStr;
hr = StringFromIID(iid, &iidStr);
if (FAILED(hr)) {
errorMsg = "[WASAPI] Failed to convert device IID to string for ActivateAudioInterfaceAsync(). Out of memory.", result = MAL_OUT_OF_MEMORY;
goto done;
}
result = completionHandler.Init();
IUnknown* pActivatedInterface = NULL;
result = mal_context_get_IAudioClient_UWP__wasapi(pContext, type, pDeviceID, (mal_IAudioClient**)&pDevice->wasapi.pAudioClient, &pActivatedInterface);
if (result != MAL_SUCCESS) {
mal_CoTaskMemFree(pContext, iidStr);
errorMsg = "[WASAPI] Failed to create event for waiting for ActivateAudioInterfaceAsync().", result = MAL_FAILED_TO_OPEN_BACKEND_DEVICE;
goto done;
}
hr = ActivateAudioInterfaceAsync(iidStr, MAL_IID_IAudioClient, NULL, (IActivateAudioInterfaceCompletionHandler*)&completionHandler, (IActivateAudioInterfaceAsyncOperation**)&pAsyncOp);
if (FAILED(hr)) {
completionHandler.Uninit();
mal_CoTaskMemFree(pContext, iidStr);
errorMsg = "[WASAPI] ActivateAudioInterfaceAsync() failed.", result = MAL_FAILED_TO_OPEN_BACKEND_DEVICE;
goto done;
}
mal_CoTaskMemFree(pContext, iidStr);
// Wait for the async operation for finish.
completionHandler.Wait();
completionHandler.Uninit();
HRESULT activateResult;
IUnknown* pActivatedInterface;
hr = mal_IActivateAudioInterfaceAsyncOperation_GetActivateResult(pAsyncOp, &activateResult, &pActivatedInterface);
if (FAILED(hr) || FAILED(activateResult)) {
errorMsg = "[WASAPI] Failed to activate device.", result = MAL_FAILED_TO_OPEN_BACKEND_DEVICE;
goto done;
}
// Here is where we grab the IAudioClient interface.
hr = pActivatedInterface->QueryInterface(MAL_IID_IAudioClient, &pDevice->wasapi.pAudioClient);
if (FAILED(hr)) {
errorMsg = "[WASAPI] Failed to query IAudioClient interface.", result = MAL_FAILED_TO_OPEN_BACKEND_DEVICE;
goto done;
}
#endif
......@@ -5332,8 +5526,8 @@ done:
mal_IMMDevice_Release(pMMDevice);
}
#else
if (pAsyncOp != NULL) {
mal_IActivateAudioInterfaceAsyncOperation_Release(pAsyncOp);
if (pActivatedInterface != NULL) {
pActivatedInterface->Release();
}
#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