From 30e298db0ab2dff1545a11e2272bdaad73301e51 Mon Sep 17 00:00:00 2001 From: David Ludwig Date: Thu, 22 Nov 2012 22:34:50 -0500 Subject: [PATCH] WinRT: got the XAudio2 backend compiling (but not running, yet) --- VisualC/SDL/SDL_VS2012_WinRT.vcxproj | 30 +++- include/SDL_config_windowsrt.h | 2 +- src/audio/xaudio2/SDL_xaudio2.c | 135 +++++++++++++++++- .../xaudio2/SDL_xaudio2_winrthelpers.cpp | 41 ++++++ src/audio/xaudio2/SDL_xaudio2_winrthelpers.h | 40 ++++++ src/core/windows/SDL_windows.h | 2 + 6 files changed, 238 insertions(+), 12 deletions(-) create mode 100644 src/audio/xaudio2/SDL_xaudio2_winrthelpers.cpp create mode 100644 src/audio/xaudio2/SDL_xaudio2_winrthelpers.h diff --git a/VisualC/SDL/SDL_VS2012_WinRT.vcxproj b/VisualC/SDL/SDL_VS2012_WinRT.vcxproj index cddcbdff9..73d04e207 100644 --- a/VisualC/SDL/SDL_VS2012_WinRT.vcxproj +++ b/VisualC/SDL/SDL_VS2012_WinRT.vcxproj @@ -37,6 +37,22 @@ + + CompileAsCpp + CompileAsCpp + CompileAsCpp + CompileAsCpp + CompileAsCpp + CompileAsCpp + + + true + true + true + true + true + true + @@ -206,6 +222,8 @@ + + @@ -384,7 +402,7 @@ Console false false - d2d1.lib; d3d11.lib; dxgi.lib; ole32.lib; windowscodecs.lib; dwrite.lib; kernel32.lib;%(AdditionalDependencies) + xaudio2.lib;d2d1.lib;d3d11.lib;dxgi.lib;ole32.lib;windowscodecs.lib;dwrite.lib;kernel32.lib;%(AdditionalDependencies) @@ -398,7 +416,7 @@ Console false false - d2d1.lib; d3d11.lib; dxgi.lib; ole32.lib; windowscodecs.lib; dwrite.lib; kernel32.lib;%(AdditionalDependencies) + xaudio2.lib;d2d1.lib;d3d11.lib;dxgi.lib;ole32.lib;windowscodecs.lib;dwrite.lib;kernel32.lib;%(AdditionalDependencies) @@ -412,7 +430,7 @@ Console false false - d2d1.lib; d3d11.lib; dxgi.lib; ole32.lib; windowscodecs.lib; dwrite.lib; kernel32.lib;%(AdditionalDependencies) + xaudio2.lib;d2d1.lib;d3d11.lib;dxgi.lib;ole32.lib;windowscodecs.lib;dwrite.lib;kernel32.lib;%(AdditionalDependencies) @@ -426,7 +444,7 @@ Console false false - d2d1.lib; d3d11.lib; dxgi.lib; ole32.lib; windowscodecs.lib; dwrite.lib; kernel32.lib;%(AdditionalDependencies) + xaudio2.lib;d2d1.lib;d3d11.lib;dxgi.lib;ole32.lib;windowscodecs.lib;dwrite.lib;kernel32.lib;%(AdditionalDependencies) @@ -440,7 +458,7 @@ Console false false - d2d1.lib; d3d11.lib; dxgi.lib; ole32.lib; windowscodecs.lib; dwrite.lib; kernel32.lib;%(AdditionalDependencies) + xaudio2.lib;d2d1.lib;d3d11.lib;dxgi.lib;ole32.lib;windowscodecs.lib;dwrite.lib;kernel32.lib;%(AdditionalDependencies) @@ -454,7 +472,7 @@ Console false false - d2d1.lib; d3d11.lib; dxgi.lib; ole32.lib; windowscodecs.lib; dwrite.lib; kernel32.lib;%(AdditionalDependencies) + xaudio2.lib;d2d1.lib;d3d11.lib;dxgi.lib;ole32.lib;windowscodecs.lib;dwrite.lib;kernel32.lib;%(AdditionalDependencies) diff --git a/include/SDL_config_windowsrt.h b/include/SDL_config_windowsrt.h index 3f12a130c..fb7fdb3b9 100644 --- a/include/SDL_config_windowsrt.h +++ b/include/SDL_config_windowsrt.h @@ -144,7 +144,7 @@ typedef unsigned int uintptr_t; #endif /* Enable various audio drivers */ -//#define SDL_AUDIO_DRIVER_XAUDIO2 1 // TODO, WinRT: see if SDL's XAudio2 driver can compile +#define SDL_AUDIO_DRIVER_XAUDIO2 1 #define SDL_AUDIO_DRIVER_DISK 1 #define SDL_AUDIO_DRIVER_DUMMY 1 diff --git a/src/audio/xaudio2/SDL_xaudio2.c b/src/audio/xaudio2/SDL_xaudio2.c index d42d89638..f2302f575 100644 --- a/src/audio/xaudio2/SDL_xaudio2.c +++ b/src/audio/xaudio2/SDL_xaudio2.c @@ -18,22 +18,90 @@ misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ + +/* WinRT NOTICE: + + A number of changes were warranted to SDL's XAudio2 backend in order to + get it compiling for Windows RT. + + When compiling for WinRT, XAudio2.h requires that it be compiled in a C++ + file, and not a straight C file. Trying to compile it as C leads to lots + of errors, at least with MSVC 2012 and Windows SDK 8.0, as of Nov 22, 2012. + To address this specific issue, a few changes were made to SDL_xaudio2.c: + + 1. SDL_xaudio2.c is compiled as a C++ file in WinRT builds. Exported + symbols, namely XAUDIO2_bootstrap, uses 'extern "C"' to make sure the + rest of SDL can access it. Non-WinRT builds continue to compile + SDL_xaudio2.c as a C file. + 2. A macro redefines variables named 'this' to '_this', to prevent compiler + errors (C2355 in Visual C++) related to 'this' being a reserverd keyword. + This hack may need to be altered in the future, particularly if C++'s + 'this' keyword needs to be used (within SDL_xaudio2.c). At the time + WinRT support was initially added to SDL's XAudio2 backend, this + capability was not needed. + 3. The C-style macros to invoke XAudio2's COM-based methods were + rewritten to be C++-friendly. These are provided in the file, + SDL_xaudio2_winrthelpers.h. + 4. IXAudio2::CreateSourceVoice, when used in C++, requires its callbacks to + be specified via a C++ class. SDL's XAudio2 backend was written with + C-style callbacks. A class to bridge these two interfaces, + SDL_XAudio2VoiceCallback, was written to make XAudio2 happy. Its methods + just call SDL's existing, C-style callbacks. + 5. Multiple checks for the __cplusplus macro were made, in appropriate + places. + + + A few additional changes to SDL's XAudio2 backend were warranted by API + changes to Windows. Many, but not all of these are documented by Microsoft + at: + http://blogs.msdn.com/b/chuckw/archive/2012/04/02/xaudio2-and-windows-8-consumer-preview.aspx + + 1. Windows' thread synchronization function, CreateSemaphore, was removed + from Windows RT. SDL's semaphore API was substituted instead. + 2. The method calls, IXAudio2::GetDeviceCount and IXAudio2::GetDeviceDetails + were removed from the XAudio2 API. Microsoft is telling developers to + use APIs in Windows::Foundation instead. + For SDL, the missing methods were reimplemented using the APIs Microsoft + said to use. + 3. CoInitialize and CoUninitialize are not available in Windows RT. + These calls were removed, as COM will have been initialized earlier, + at least by the call to the WinRT app's main function + (aka 'int main(Platform::Array^)). (DLudwig: + This was my understanding of how WinRT: the 'main' function uses + a tag of [MTAThread], which should initialize COM. My understanding + of COM is somewhat limited, and I may be incorrect here.) + 4. IXAudio2::CreateMasteringVoice changed its integer-based 'DeviceIndex' + argument to a string-based one, 'szDeviceId'. In Windows RT, the + string-based argument will be used. +*/ + #include "SDL_config.h" #if SDL_AUDIO_DRIVER_XAUDIO2 +#ifdef __cplusplus +extern "C" { +#endif #include "../../core/windows/SDL_windows.h" #include "SDL_audio.h" #include "../SDL_audio_c.h" #include "../SDL_sysaudio.h" #include "SDL_assert.h" +#ifdef __cplusplus +} +#endif +#if defined(__WINRT__) +# define SDL_XAUDIO2_HAS_SDK 1 +#endif +#if defined(__WIN32__) #include /* XAudio2 exists as of the March 2008 DirectX SDK */ #if (!defined(_DXSDK_BUILD_MAJOR) || (_DXSDK_BUILD_MAJOR < 1284)) # pragma message("Your DirectX SDK is too old. Disabling XAudio2 support.") #else # define SDL_XAUDIO2_HAS_SDK 1 #endif +#endif #ifdef SDL_XAUDIO2_HAS_SDK @@ -43,12 +111,17 @@ /* Hidden "this" pointer for the audio functions */ #define _THIS SDL_AudioDevice *this +#ifdef __cplusplus +#define this _this +#include "SDL_xaudio2_winrthelpers.h" +#endif + struct SDL_PrivateAudioData { IXAudio2 *ixa2; IXAudio2SourceVoice *source; IXAudio2MasteringVoice *mastering; - HANDLE semaphore; + SDL_sem * semaphore; Uint8 *mixbuf; int mixlen; Uint8 *nextbuf; @@ -102,7 +175,7 @@ VoiceCBOnBufferEnd(THIS_ void *data) { /* Just signal the SDL audio thread and get out of XAudio2's way. */ SDL_AudioDevice *this = (SDL_AudioDevice *) data; - ReleaseSemaphore(this->hidden->semaphore, 1, NULL); + SDL_SemPost(this->hidden->semaphore); } static void STDMETHODCALLTYPE @@ -119,6 +192,33 @@ static void STDMETHODCALLTYPE VoiceCBOnVoiceProcessPassEnd(THIS) {} static void STDMETHODCALLTYPE VoiceCBOnBufferStart(THIS_ void *data) {} static void STDMETHODCALLTYPE VoiceCBOnLoopEnd(THIS_ void *data) {} +#if defined(__cplusplus) +class SDL_XAudio2VoiceCallback : public IXAudio2VoiceCallback +{ +public: + STDMETHOD_(void, OnBufferEnd)(void *pBufferContext) { + VoiceCBOnBufferEnd(pBufferContext); + } + STDMETHOD_(void, OnBufferStart)(void *pBufferContext) { + VoiceCBOnBufferEnd(pBufferContext); + } + STDMETHOD_(void, OnLoopEnd)(void *pBufferContext) { + VoiceCBOnLoopEnd(pBufferContext); + } + STDMETHOD_(void, OnStreamEnd)() { + VoiceCBOnStreamEnd(); + } + STDMETHOD_(void, OnVoiceError)(void *pBufferContext, HRESULT Error) { + VoiceCBOnVoiceError(pBufferContext, Error); + } + STDMETHOD_(void, OnVoiceProcessingPassEnd)() { + VoiceCBOnVoiceProcessPassEnd(); + } + STDMETHOD_(void, OnVoiceProcessingPassStart)(UINT32 BytesRequired) { + VoiceCBOnVoiceProcessPassStart(BytesRequired); + } +}; +#endif static Uint8 * XAUDIO2_GetDeviceBuf(_THIS) @@ -168,7 +268,7 @@ static void XAUDIO2_WaitDevice(_THIS) { if (this->enabled) { - WaitForSingleObject(this->hidden->semaphore, INFINITE); + SDL_SemWait(this->hidden->semaphore); } } @@ -181,7 +281,7 @@ XAUDIO2_WaitDone(_THIS) IXAudio2SourceVoice_Discontinuity(source); IXAudio2SourceVoice_GetState(source, &state); while (state.BuffersQueued > 0) { - WaitForSingleObject(this->hidden->semaphore, INFINITE); + SDL_SemWait(this->hidden->semaphore); IXAudio2SourceVoice_GetState(source, &state); } } @@ -230,8 +330,15 @@ XAUDIO2_OpenDevice(_THIS, const char *devname, int iscapture) SDL_AudioFormat test_format = SDL_FirstAudioFormat(this->spec.format); IXAudio2 *ixa2 = NULL; IXAudio2SourceVoice *source = NULL; +#if defined(__WINRT__) + WCHAR devId[256]; +#else UINT32 devId = 0; /* 0 == system default device. */ +#endif +#if defined(__cplusplus) + static SDL_XAudio2VoiceCallback callbacks; +#else static IXAudio2VoiceCallbackVtbl callbacks_vtable = { VoiceCBOnVoiceProcessPassStart, VoiceCBOnVoiceProcessPassEnd, @@ -243,6 +350,11 @@ XAUDIO2_OpenDevice(_THIS, const char *devname, int iscapture) }; static IXAudio2VoiceCallback callbacks = { &callbacks_vtable }; +#endif // ! defined(__cplusplus) + +#if defined(__WINRT__) + SDL_zero(devId); +#endif if (iscapture) { SDL_SetError("XAudio2: capture devices unsupported."); @@ -269,7 +381,11 @@ XAUDIO2_OpenDevice(_THIS, const char *devname, int iscapture) const int match = (SDL_strcmp(str, devname) == 0); SDL_free(str); if (match) { +#if defined(__WINRT__) + wcsncpy_s(devId, ARRAYSIZE(devId), details.DeviceID, _TRUNCATE); +#else devId = i; +#endif break; } } @@ -294,7 +410,7 @@ XAUDIO2_OpenDevice(_THIS, const char *devname, int iscapture) SDL_memset(this->hidden, 0, (sizeof *this->hidden)); this->hidden->ixa2 = ixa2; - this->hidden->semaphore = CreateSemaphore(NULL, 1, 2, NULL); + this->hidden->semaphore = SDL_CreateSemaphore(1); if (this->hidden->semaphore == NULL) { XAUDIO2_CloseDevice(this); SDL_SetError("XAudio2: CreateSemaphore() failed!"); @@ -395,7 +511,9 @@ XAUDIO2_OpenDevice(_THIS, const char *devname, int iscapture) static void XAUDIO2_Deinitialize(void) { +#if defined(__WIN32__) WIN_CoUninitialize(); +#endif } #endif /* SDL_XAUDIO2_HAS_SDK */ @@ -410,13 +528,17 @@ XAUDIO2_Init(SDL_AudioDriverImpl * impl) #else /* XAudio2Create() is a macro that uses COM; we don't load the .dll */ IXAudio2 *ixa2 = NULL; +#if defined(__WIN32__) if (FAILED(WIN_CoInitialize())) { SDL_SetError("XAudio2: CoInitialize() failed"); return 0; } +#endif if (XAudio2Create(&ixa2, 0, XAUDIO2_DEFAULT_PROCESSOR) != S_OK) { +#if defined(__WIN32__) WIN_CoUninitialize(); +#endif SDL_SetError("XAudio2: XAudio2Create() failed"); return 0; /* not available. */ } @@ -436,6 +558,9 @@ XAUDIO2_Init(SDL_AudioDriverImpl * impl) #endif } +#if defined(__cplusplus) +extern "C" +#endif AudioBootStrap XAUDIO2_bootstrap = { "xaudio2", "XAudio2", XAUDIO2_Init, 0 }; diff --git a/src/audio/xaudio2/SDL_xaudio2_winrthelpers.cpp b/src/audio/xaudio2/SDL_xaudio2_winrthelpers.cpp new file mode 100644 index 000000000..2fbf63c8b --- /dev/null +++ b/src/audio/xaudio2/SDL_xaudio2_winrthelpers.cpp @@ -0,0 +1,41 @@ + +#include +#include "SDL_xaudio2_winrthelpers.h" + +using Windows::Devices::Enumeration::DeviceClass; +using Windows::Devices::Enumeration::DeviceInformation; +using Windows::Devices::Enumeration::DeviceInformationCollection; + +HRESULT IXAudio2_GetDeviceCount(IXAudio2 * ixa2, UINT32 * devcount) +{ + auto operation = DeviceInformation::FindAllAsync(DeviceClass::AudioRender); + while (operation->Status != Windows::Foundation::AsyncStatus::Completed) + { + } + + DeviceInformationCollection^ devices = operation->GetResults(); + *devcount = devices->Size; + return S_OK; +} + +HRESULT IXAudio2_GetDeviceDetails(IXAudio2 * unused, UINT32 index, XAUDIO2_DEVICE_DETAILS * details) +{ + auto operation = DeviceInformation::FindAllAsync(DeviceClass::AudioRender); + while (operation->Status != Windows::Foundation::AsyncStatus::Completed) + { + } + + DeviceInformationCollection^ devices = operation->GetResults(); + if (index >= devices->Size) + { + return XAUDIO2_E_INVALID_CALL; + } + + DeviceInformation^ d = devices->GetAt(index); + if (details) + { + wcsncpy_s(details->DeviceID, ARRAYSIZE(details->DeviceID), d->Id->Data(), _TRUNCATE); + wcsncpy_s(details->DisplayName, ARRAYSIZE(details->DisplayName), d->Name->Data(), _TRUNCATE); + } + return S_OK; +} diff --git a/src/audio/xaudio2/SDL_xaudio2_winrthelpers.h b/src/audio/xaudio2/SDL_xaudio2_winrthelpers.h new file mode 100644 index 000000000..a72804faa --- /dev/null +++ b/src/audio/xaudio2/SDL_xaudio2_winrthelpers.h @@ -0,0 +1,40 @@ + +#pragma once + +// +// Re-implementation of methods removed from XAudio2 (in Windows RT): +// + +typedef struct XAUDIO2_DEVICE_DETAILS +{ + WCHAR DeviceID[256]; + WCHAR DisplayName[256]; + /* Other fields exist in the pre-Windows 8 version of this struct, however + they weren't used by SDL, so they weren't added. + */ +} XAUDIO2_DEVICE_DETAILS; + +HRESULT IXAudio2_GetDeviceCount(IXAudio2 * unused, UINT32 * devcount); +HRESULT IXAudio2_GetDeviceDetails(IXAudio2 * unused, UINT32 index, XAUDIO2_DEVICE_DETAILS * details); + + +// +// C-style macros to call XAudio2's methods in C++: +// + +#define IXAudio2_CreateMasteringVoice(A, B, C, D, E, F, G) (A)->CreateMasteringVoice((B), (C), (D), (E), (F), (G)) +#define IXAudio2_CreateSourceVoice(A, B, C, D, E, F, G, H) (A)->CreateSourceVoice((B), (C), (D), (E), (F), (G), (H)) +#define IXAudio2_QueryInterface(A, B, C) (A)->QueryInterface((B), (C)) +#define IXAudio2_Release(A) (A)->Release() +#define IXAudio2_StartEngine(A) (A)->StartEngine() +#define IXAudio2_StopEngine(A) (A)->StopEngine() + +#define IXAudio2MasteringVoice_DestroyVoice(A) (A)->DestroyVoice() + +#define IXAudio2SourceVoice_DestroyVoice(A) (A)->DestroyVoice() +#define IXAudio2SourceVoice_Discontinuity(A) (A)->Discontinuity() +#define IXAudio2SourceVoice_FlushSourceBuffers(A) (A)->FlushSourceBuffers() +#define IXAudio2SourceVoice_GetState(A, B) (A)->GetState((B)) +#define IXAudio2SourceVoice_Start(A, B, C) (A)->Start((B), (C)) +#define IXAudio2SourceVoice_Stop(A, B, C) (A)->Stop((B), (C)) +#define IXAudio2SourceVoice_SubmitSourceBuffer(A, B, C) (A)->SubmitSourceBuffer((B), (C)) diff --git a/src/core/windows/SDL_windows.h b/src/core/windows/SDL_windows.h index c7c6f9576..d4a7ecff9 100644 --- a/src/core/windows/SDL_windows.h +++ b/src/core/windows/SDL_windows.h @@ -24,6 +24,7 @@ #ifndef _INCLUDED_WINDOWS_H #define _INCLUDED_WINDOWS_H +#if defined(__WIN32__) #define WIN32_LEAN_AND_MEAN #define STRICT #ifndef UNICODE @@ -31,6 +32,7 @@ #endif #undef _WIN32_WINNT #define _WIN32_WINNT 0x501 /* Need 0x410 for AlphaBlend() and 0x500 for EnumDisplayDevices(), 0x501 for raw input */ +#endif #include