Commit 6b5425c5 authored by David Reid's avatar David Reid

Finish initial work on adding support for UWP.

This currently untested and only works when compiling as C++.
parent eaafdf88
......@@ -2466,6 +2466,11 @@ const IID g_malIID_IAudioClient_Instance = {0x1CB9AD4C, 0xDBFA, 0x4C32,
const IID g_malIID_IAudioRenderClient_Instance = {0xF294ACFC, 0x3146, 0x4483, {0xA7, 0xBF, 0xAD, 0xDC, 0xA7, 0xC2, 0x60, 0xE2}}; // F294ACFC-3146-4483-A7BF-ADDCA7C260E2 = __uuidof(IAudioRenderClient)
const IID g_malIID_IAudioCaptureClient_Instance = {0xC8ADBD64, 0xE71E, 0x48A0, {0xA4, 0xDE, 0x18, 0x5C, 0x39, 0x5C, 0xD3, 0x17}}; // C8ADBD64-E71E-48A0-A4DE-185C395CD317 = __uuidof(IAudioCaptureClient)
#ifndef MAL_WIN32_DESKTOP
const IID g_malIID_DEVINTERFACE_AUDIO_RENDER = {0xE6327CAD, 0xDCEC, 0x4949, {0xAE, 0x8A, 0x99, 0x1E, 0x97, 0x6A, 0x79, 0xD2}}; // E6327CAD-DCEC-4949-AE8A-991E976A79D2
const IID g_malIID_DEVINTERFACE_AUDIO_CAPTURE = {0x2EEF81BE, 0x33FA, 0x4800, {0x96, 0x70, 0x1C, 0xD4, 0x74, 0x97, 0x2C, 0x3F}}; // 2EEF81BE-33FA-4800-9670-1CD474972C3F
#endif
#ifdef __cplusplus
#define g_malCLSID_MMDeviceEnumerator g_malCLSID_MMDeviceEnumerator_Instance
#define g_malIID_IMMDeviceEnumerator g_malIID_IMMDeviceEnumerator_Instance
......@@ -2547,6 +2552,18 @@ const IID g_malIID_IAudioCaptureClient_Instance = {0xC8ADBD64, 0xE71E, 0x48A0,
#else
#define IMMDevice_Activate(p, a, b, c, d) ((IMMDevice*)p)->lpVtbl->Activate((IMMDevice*)p, a, b, c, d)
#endif
#else
// IActivateAudioInterfaceAsyncOperation
#ifdef __cplusplus
#define IActivateAudioInterfaceAsyncOperation_Release(p) ((IActivateAudioInterfaceAsyncOperation*)p)->Release()
#else
#define IActivateAudioInterfaceAsyncOperation_Release(p) ((IActivateAudioInterfaceAsyncOperation*)p)->lpVtbl->Release((IActivateAudioInterfaceAsyncOperation*)p)
#endif
#ifdef __cplusplus
#define IActivateAudioInterfaceAsyncOperation_GetActivateResult(p, a, b) ((IActivateAudioInterfaceAsyncOperation*)p)->GetActivateResult(a, b)
#else
#define IActivateAudioInterfaceAsyncOperation_GetActivateResult(p, a, b) ((IActivateAudioInterfaceAsyncOperation*)p)->lpVtbl->GetActivateResult((IActivateAudioInterfaceAsyncOperation*)p, a, b)
#endif
#endif
// IPropertyStore
......@@ -2647,6 +2664,19 @@ const IID g_malIID_IAudioCaptureClient_Instance = {0xC8ADBD64, 0xE71E, 0x48A0,
#define IAudioCaptureClient_ReleaseBuffer(p, a) ((IAudioCaptureClient*)p)->lpVtbl->ReleaseBuffer((IAudioCaptureClient*)p, a)
#endif
#ifdef MAL_WIN32_DESKTOP
#define mal_CoInitializeEx(pContext, pvReserved, dwCoInit) ((MAL_PFN_CoInitializeEx)pContext->win32.CoInitializeEx)(pvReserved, dwCoInit)
#define mal_CoUninitialize(pContext) ((MAL_PFN_CoUninitialize)pContext->win32.CoUninitialize)()
#define mal_CoCreateInstance(pContext, rclsid, pUnkOuter, dwClsContext, riid, ppv) ((MAL_PFN_CoCreateInstance)pContext->win32.CoCreateInstance)(rclsid, pUnkOuter, dwClsContext, riid, ppv)
#define mal_CoTaskMemFree(pContext, pv) ((MAL_PFN_CoTaskMemFree)pContext->win32.CoTaskMemFree)(pv)
#define mal_PropVariantClear(pContext, pvar) ((MAL_PFN_PropVariantClear)pContext->win32.PropVariantClear)(pvar)
#else
#define mal_CoInitializeEx(pContext, pvReserved, dwCoInit) CoInitializeEx(pvReserved, dwCoInit)
#define mal_CoUninitialize(pContext) CoUninitialize()
#define mal_CoCreateInstance(pContext, rclsid, pUnkOuter, dwClsContext, riid, ppv) CoCreateInstance(rclsid, pUnkOuter, dwClsContext, riid, ppv)
#define mal_CoTaskMemFree(pContext, pv) CoTaskMemFree(pv)
#define mal_PropVariantClear(pContext, pvar) PropVariantClear(pvar)
#endif
mal_result mal_context_init__wasapi(mal_context* pContext)
{
......@@ -2685,8 +2715,9 @@ static mal_result mal_enumerate_devices__wasapi(mal_context* pContext, mal_devic
mal_uint32 infoSize = *pCount;
*pCount = 0;
#ifdef MAL_WIN32_DESKTOP
IMMDeviceEnumerator* pDeviceEnumerator;
HRESULT hr = ((MAL_PFN_CoCreateInstance)pContext->win32.CoCreateInstance)(g_malCLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, g_malIID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator);
HRESULT hr = mal_CoCreateInstance(pContext, g_malCLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, g_malIID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator);
if (FAILED(hr)) {
return MAL_WASAPI_FAILED_TO_CREATE_DEVICE_ENUMERATOR;
}
......@@ -2719,7 +2750,7 @@ static mal_result mal_enumerate_devices__wasapi(mal_context* pContext, mal_devic
if (SUCCEEDED(hr)) {
size_t idlen = wcslen(id);
if (idlen+sizeof(wchar_t) > sizeof(pInfo->id.wasapi)) {
((MAL_PFN_CoTaskMemFree)pContext->win32.CoTaskMemFree)(id);
mal_CoTaskMemFree(pContext, id);
mal_assert(MAL_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.
continue;
}
......@@ -2727,7 +2758,7 @@ static mal_result mal_enumerate_devices__wasapi(mal_context* pContext, mal_devic
memcpy(pInfo->id.wasapi, id, idlen * sizeof(wchar_t));
pInfo->id.wasapi[idlen] = '\0';
((MAL_PFN_CoTaskMemFree)pContext->win32.CoTaskMemFree)(id);
mal_CoTaskMemFree(pContext, id);
}
// Description / Friendly Name.
......@@ -2739,7 +2770,7 @@ static mal_result mal_enumerate_devices__wasapi(mal_context* pContext, mal_devic
hr = IPropertyStore_GetValue(pProperties, g_malPKEY_Device_FriendlyName, &varName);
if (SUCCEEDED(hr)) {
WideCharToMultiByte(CP_UTF8, 0, varName.pwszVal, -1, pInfo->name, sizeof(pInfo->name), 0, FALSE);
((MAL_PFN_PropVariantClear)pContext->win32.PropVariantClear)(&varName);
mal_PropVariantClear(pContext, &varName);
}
IPropertyStore_Release(pProperties);
......@@ -2751,6 +2782,23 @@ static mal_result mal_enumerate_devices__wasapi(mal_context* pContext, mal_devic
}
IMMDeviceCollection_Release(pDeviceCollection);
#else
// The MMDevice API is only supported on desktop applications. For now, while I'm still figuring out how to properly enumerate
// over devices without using MMDevice, I'm restricting devices to defaults.
if (infoSize > 0) {
if (type == mal_device_type_playback) {
mal_copy_memory(pInfo->id.wasapi, &g_malIID_DEVINTERFACE_AUDIO_RENDER, sizeof(g_malIID_DEVINTERFACE_AUDIO_RENDER));
mal_strncpy_s(pInfo->name, sizeof(pInfo->name), "Default Playback Device", (size_t)-1);
} else {
mal_copy_memory(pInfo->id.wasapi, &g_malIID_DEVINTERFACE_AUDIO_CAPTURE, sizeof(g_malIID_DEVINTERFACE_AUDIO_CAPTURE));
mal_strncpy_s(pInfo->name, sizeof(pInfo->name), "Default Capture Device", (size_t)-1);
}
}
pInfo += 1;
*pCount += 1;
#endif
return MAL_SUCCESS;
}
......@@ -2761,11 +2809,9 @@ static void mal_device_uninit__wasapi(mal_device* pDevice)
if (pDevice->wasapi.pRenderClient) {
IAudioRenderClient_Release(pDevice->wasapi.pRenderClient);
}
if (pDevice->wasapi.pCaptureClient) {
IAudioCaptureClient_Release(pDevice->wasapi.pCaptureClient);
}
if (pDevice->wasapi.pAudioClient) {
IAudioClient_Release(pDevice->wasapi.pAudioClient);
}
......@@ -2809,7 +2855,7 @@ static mal_result mal_device__find_best_format__wasapi(mal_device* pDevice, WAVE
if (pBestFormatTemp != NULL) {
mal_copy_memory(pBestFormat, pBestFormatTemp, sizeof(*pBestFormat));
((MAL_PFN_CoTaskMemFree)pDevice->pContext->win32.CoTaskMemFree)(pBestFormatTemp);
mal_CoTaskMemFree(pDevice->pContext, pBestFormatTemp);
} else {
mal_copy_memory(pBestFormat, &wf, sizeof(*pBestFormat));
}
......@@ -2817,6 +2863,55 @@ static mal_result mal_device__find_best_format__wasapi(mal_device* pDevice, WAVE
return MAL_SUCCESS;
}
#ifndef MAL_WIN32_DESKTOP
#ifdef __cplusplus
#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
static mal_result mal_device_init__wasapi(mal_context* pContext, mal_device_type type, mal_device_id* pDeviceID, mal_device_config* pConfig, mal_device* pDevice)
{
(void)pContext;
......@@ -2824,8 +2919,12 @@ static mal_result mal_device_init__wasapi(mal_context* pContext, mal_device_type
mal_assert(pDevice != NULL);
mal_zero_object(&pDevice->wasapi);
HRESULT hr;
mal_result result = MAL_SUCCESS;
#ifdef MAL_WIN32_DESKTOP
IMMDeviceEnumerator* pDeviceEnumerator;
HRESULT hr = ((MAL_PFN_CoCreateInstance)pContext->win32.CoCreateInstance)(g_malCLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, g_malIID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator);
hr = mal_CoCreateInstance(pContext, g_malCLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, g_malIID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator);
if (FAILED(hr)) {
mal_device_uninit__wasapi(pDevice);
return mal_post_error(pDevice, "[WASAPI] Failed to create IMMDeviceEnumerator.", MAL_WASAPI_FAILED_TO_CREATE_DEVICE_ENUMERATOR);
......@@ -2856,11 +2955,72 @@ static mal_result mal_device_init__wasapi(mal_context* pContext, mal_device_type
return mal_post_error(pDevice, "[WASAPI] Failed to activate device.", MAL_WASAPI_FAILED_TO_ACTIVATE_DEVICE);
}
IMMDevice_Release(pMMDevice);
#else
IID iid;
if (pDeviceID != NULL) {
mal_copy_memory(&iid, pDeviceID->wasapi, sizeof(iid));
} else {
if (type == mal_device_type_playback) {
iid = g_malIID_DEVINTERFACE_AUDIO_RENDER;
} else {
iid = g_malIID_DEVINTERFACE_AUDIO_CAPTURE;
}
}
LPOLESTR iidStr;
hr = StringFromIID(iid, &iidStr);
if (FAILED(hr)) {
mal_device_uninit__wasapi(pDevice);
return mal_post_error(pDevice, "[WASAPI] Failed to convert device IID to string for ActivateAudioInterfaceAsync(). Out of memory.", MAL_OUT_OF_MEMORY);
}
malCompletionHandler completionHandler;
result = completionHandler.Init();
if (result != MAL_SUCCESS) {
mal_CoTaskMemFree(pContext, iidStr);
mal_device_uninit__wasapi(pDevice);
return mal_post_error(pDevice, "[WASAPI] Failed to create event for waiting for ActivateAudioInterfaceAsync().", MAL_WASAPI_FAILED_TO_ACTIVATE_DEVICE);
}
IActivateAudioInterfaceAsyncOperation *pAsyncOp;
hr = ActivateAudioInterfaceAsync(iidStr, g_malIID_IAudioClient, NULL, &completionHandler, &pAsyncOp);
if (FAILED(hr)) {
completionHandler.Uninit();
mal_CoTaskMemFree(pContext, iidStr);
mal_device_uninit__wasapi(pDevice);
return mal_post_error(pDevice, "[WASAPI] ActivateAudioInterfaceAsync() failed.", MAL_WASAPI_FAILED_TO_ACTIVATE_DEVICE);
}
mal_CoTaskMemFree(pContext, iidStr);
// Wait for the async operation for finish.
completionHandler.Wait();
completionHandler.Uninit();
HRESULT activateResult;
IUnknown* pActivatedInterface;
hr = IActivateAudioInterfaceAsyncOperation_GetActivateResult(pAsyncOp, &activateResult, &pActivatedInterface);
if (FAILED(hr) || FAILED(activateResult)) {
IActivateAudioInterfaceAsyncOperation_Release(pAsyncOp);
mal_device_uninit__wasapi(pDevice);
return mal_post_error(pDevice, "[WASAPI] Failed to activate device.", MAL_WASAPI_FAILED_TO_ACTIVATE_DEVICE);
}
// Here is where we grab the IAudioClient interface.
hr = pActivatedInterface->QueryInterface(g_malIID_IAudioClient, &pDevice->wasapi.pAudioClient);
if (FAILED(hr)) {
IActivateAudioInterfaceAsyncOperation_Release(pAsyncOp);
mal_device_uninit__wasapi(pDevice);
return mal_post_error(pDevice, "[WASAPI] Failed to query IAudioClient interface.", MAL_WASAPI_FAILED_TO_ACTIVATE_DEVICE);
}
IActivateAudioInterfaceAsyncOperation_Release(pAsyncOp);
#endif
REFERENCE_TIME bufferDurationInMicroseconds = ((mal_uint64)pConfig->bufferSizeInFrames * 1000 * 1000) / pConfig->sampleRate;
WAVEFORMATEXTENSIBLE wf;
mal_result result = mal_device__find_best_format__wasapi(pDevice, &wf);
result = mal_device__find_best_format__wasapi(pDevice, &wf);
if (result != MAL_SUCCESS) {
mal_device_uninit__wasapi(pDevice);
return mal_post_error(pDevice, "[WASAPI] Failed to find best device mix format.", MAL_WASAPI_FAILED_TO_ACTIVATE_DEVICE);
......@@ -6975,7 +7135,7 @@ mal_thread_result MAL_THREADCALL mal_worker_thread(void* pData)
mal_assert(pDevice != NULL);
#ifdef MAL_WIN32
((MAL_PFN_CoInitializeEx)pDevice->pContext->win32.CoInitializeEx)(NULL, COINIT_MULTITHREADED);
mal_CoInitializeEx(pDevice->pContext, NULL, COINIT_MULTITHREADED);
#endif
// This is only used to prevent posting onStop() when the device is first initialized.
......@@ -7034,7 +7194,7 @@ mal_thread_result MAL_THREADCALL mal_worker_thread(void* pData)
mal_event_signal(&pDevice->stopEvent); // <-- Is this still needed?
#ifdef MAL_WIN32
((MAL_PFN_CoUninitialize)pDevice->pContext->win32.CoUninitialize)();
mal_CoUninitialize(pDevice->pContext);
#endif
return (mal_thread_result)0;
......@@ -7052,7 +7212,7 @@ mal_bool32 mal_device__is_initialized(mal_device* pDevice)
#ifdef MAL_WIN32
mal_result mal_context_uninit_backend_apis__win32(mal_context* pContext)
{
((MAL_PFN_CoUninitialize)pContext->win32.CoUninitialize)();
mal_CoUninitialize(pContext);
mal_dlclose(pContext->win32.hOle32DLL);
return MAL_SUCCESS;
......@@ -7060,6 +7220,7 @@ mal_result mal_context_uninit_backend_apis__win32(mal_context* pContext)
mal_result mal_context_init_backend_apis__win32(mal_context* pContext)
{
#ifdef MAL_WIN32_DESKTOP
// Ole32.dll
pContext->win32.hOle32DLL = mal_dlopen("ole32.dll");
if (pContext->win32.hOle32DLL == NULL) {
......@@ -7071,10 +7232,9 @@ mal_result mal_context_init_backend_apis__win32(mal_context* pContext)
pContext->win32.CoCreateInstance = (mal_proc)mal_dlsym(pContext->win32.hOle32DLL, "CoCreateInstance");
pContext->win32.CoTaskMemFree = (mal_proc)mal_dlsym(pContext->win32.hOle32DLL, "CoTaskMemFree");
pContext->win32.PropVariantClear = (mal_proc)mal_dlsym(pContext->win32.hOle32DLL, "PropVariantClear");
#endif
((MAL_PFN_CoInitializeEx)pContext->win32.CoInitializeEx)(NULL, COINIT_MULTITHREADED);
mal_CoInitializeEx(pContext, NULL, COINIT_MULTITHREADED);
return MAL_SUCCESS;
}
#else
......@@ -7128,9 +7288,9 @@ mal_result mal_context_init(mal_backend backends[], mal_uint32 backendCount, mal
}
static mal_backend defaultBackends[] = {
mal_backend_wasapi,
mal_backend_winmm,
mal_backend_dsound,
mal_backend_wasapi,
mal_backend_alsa,
mal_backend_oss,
mal_backend_opensl,
......
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