[PATCH 1/4 (try2)] xaudio2: Implement device activation
Andrew Eikum
aeikum at codeweavers.com
Tue Aug 25 14:28:37 CDT 2015
This adds a build-time dependency on openal, and a run-time dependency
on openal-soft's ALC_SOFT_loopback extension.
---
try2: Improve AL API usage, thanks KittyCat.
configure.ac | 3 +-
dlls/xaudio2_7/Makefile.in | 1 +
dlls/xaudio2_7/xaudio_dll.c | 274 +++++++++++++++++++++++++++++++++++++++++++-
3 files changed, 274 insertions(+), 4 deletions(-)
diff --git a/configure.ac b/configure.ac
index ed1052c..f29d3c7 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1738,8 +1738,9 @@ then
AC_DEFINE_UNQUOTED(HAVE_OPENAL,1,[Define to 1 if OpenAL is available])],,)
fi
WINE_NOTICE_WITH(openal,[test "x$ac_cv_lib_openal" != xyes],
- [libopenal ${notice_platform}development files not found (or too old), OpenAL won't be supported.])
+ [libopenal ${notice_platform}development files not found (or too old), OpenAL and XAudio2 won't be supported])
test "x$ac_cv_lib_openal" = xyes || enable_openal32=${enable_openal32:-no}
+test "x$ac_cv_lib_openal" = xyes || enable_xaudio2_7=${enable_xaudio2_7:-no}
dnl **** Check for libkstat ****
if test "$ac_cv_header_kstat_h" = "yes"
diff --git a/dlls/xaudio2_7/Makefile.in b/dlls/xaudio2_7/Makefile.in
index 6bdf362..5a09c82 100644
--- a/dlls/xaudio2_7/Makefile.in
+++ b/dlls/xaudio2_7/Makefile.in
@@ -1,5 +1,6 @@
MODULE = xaudio2_7.dll
IMPORTS = advapi32 kernel32 ole32 user32 uuid
+EXTRALIBS = $(OPENAL_LIBS)
C_SRCS = \
xaudio_dll.c
diff --git a/dlls/xaudio2_7/xaudio_dll.c b/dlls/xaudio2_7/xaudio_dll.c
index 6bb48e6..52ac747 100644
--- a/dlls/xaudio2_7/xaudio_dll.c
+++ b/dlls/xaudio2_7/xaudio_dll.c
@@ -41,8 +41,14 @@
#include "mmdeviceapi.h"
#include "audioclient.h"
+#include <AL/al.h>
+#include <AL/alc.h>
+#include <AL/alext.h>
+
WINE_DEFAULT_DEBUG_CHANNEL(xaudio2);
+static ALCdevice *(ALC_APIENTRY *palcLoopbackOpenDeviceSOFT)(const ALCchar*);
+
static HINSTANCE instance;
static void dump_fmt(const WAVEFORMATEX *fmt)
@@ -86,6 +92,13 @@ BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD reason, void *pReserved)
case DLL_PROCESS_ATTACH:
instance = hinstDLL;
DisableThreadLibraryCalls( hinstDLL );
+
+ if(!alcIsExtensionPresent(NULL, "ALC_SOFT_loopback") ||
+ !(palcLoopbackOpenDeviceSOFT = alcGetProcAddress(NULL, "alcLoopbackOpenDeviceSOFT"))){
+ ERR("XAudio2 requires the ALC_SOFT_loopback extension (OpenAL-Soft >= 1.14)\n");
+ return FALSE;
+ }
+
break;
}
return TRUE;
@@ -173,6 +186,14 @@ struct _IXAudio2Impl {
WCHAR **devids;
UINT32 ndevs;
+
+ IAudioClient *aclient;
+ IAudioRenderClient *render;
+
+ WAVEFORMATEXTENSIBLE fmt;
+
+ ALCdevice *al_device;
+ ALCcontext *al_ctx;
};
static inline IXAudio2Impl *impl_from_IXAudio2(IXAudio2 *iface)
@@ -190,6 +211,32 @@ IXAudio2Impl *impl_from_IXAudio2MasteringVoice(IXAudio2MasteringVoice *iface)
return CONTAINING_RECORD(iface, IXAudio2Impl, IXAudio2MasteringVoice_iface);
}
+static DWORD get_channel_mask(unsigned int channels)
+{
+ switch(channels){
+ case 0:
+ return 0;
+ case 1:
+ return KSAUDIO_SPEAKER_MONO;
+ case 2:
+ return KSAUDIO_SPEAKER_STEREO;
+ case 3:
+ return KSAUDIO_SPEAKER_STEREO | SPEAKER_LOW_FREQUENCY;
+ case 4:
+ return KSAUDIO_SPEAKER_QUAD; /* not _SURROUND */
+ case 5:
+ return KSAUDIO_SPEAKER_QUAD | SPEAKER_LOW_FREQUENCY;
+ case 6:
+ return KSAUDIO_SPEAKER_5POINT1; /* not 5POINT1_SURROUND */
+ case 7:
+ return KSAUDIO_SPEAKER_5POINT1 | SPEAKER_BACK_CENTER;
+ case 8:
+ return KSAUDIO_SPEAKER_7POINT1_SURROUND; /* Vista deprecates 7POINT1 */
+ }
+ FIXME("Unknown speaker configuration: %u\n", channels);
+ return 0;
+}
+
static void WINAPI XA2SRC_GetVoiceDetails(IXAudio2SourceVoice *iface,
XAUDIO2_VOICE_DETAILS *pVoiceDetails)
{
@@ -891,7 +938,29 @@ static void WINAPI XA2M_GetOutputMatrix(IXAudio2MasteringVoice *iface,
static void WINAPI XA2M_DestroyVoice(IXAudio2MasteringVoice *iface)
{
IXAudio2Impl *This = impl_from_IXAudio2MasteringVoice(iface);
+
TRACE("%p\n", This);
+
+ EnterCriticalSection(&This->lock);
+
+ if(!This->aclient){
+ LeaveCriticalSection(&This->lock);
+ return;
+ }
+
+ IAudioRenderClient_Release(This->render);
+ This->render = NULL;
+
+ IAudioClient_Release(This->aclient);
+ This->aclient = NULL;
+
+ alcCloseDevice(This->al_device);
+ This->al_device = NULL;
+
+ alcDestroyContext(This->al_ctx);
+ This->al_ctx = NULL;
+
+ LeaveCriticalSection(&This->lock);
}
/* not present in XAudio2 2.7 */
@@ -1164,6 +1233,8 @@ static ULONG WINAPI IXAudio2Impl_Release(IXAudio2 *iface)
HeapFree(GetProcessHeap(), 0, sub);
}
+ IXAudio2MasteringVoice_DestroyVoice(&This->IXAudio2MasteringVoice_iface);
+
if(This->devenum)
IMMDeviceEnumerator_Release(This->devenum);
for(i = 0; i < This->ndevs; ++i)
@@ -1322,6 +1393,28 @@ static HRESULT WINAPI IXAudio2Impl_CreateSubmixVoice(IXAudio2 *iface,
return S_OK;
}
+static ALenum al_get_loopback_format(const WAVEFORMATEXTENSIBLE *fmt)
+{
+ if(fmt->Format.wFormatTag == WAVE_FORMAT_PCM ||
+ (fmt->Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
+ IsEqualGUID(&fmt->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))){
+ switch(fmt->Format.wBitsPerSample){
+ case 8:
+ return ALC_UNSIGNED_BYTE_SOFT;
+ case 16:
+ return ALC_SHORT_SOFT;
+ case 32:
+ return ALC_INT_SOFT;
+ }
+ }else if(fmt->Format.wFormatTag == WAVE_FORMAT_IEEE_FLOAT ||
+ (fmt->Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
+ IsEqualGUID(&fmt->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT))){
+ if(fmt->Format.wBitsPerSample == 32)
+ return ALC_FLOAT_SOFT;
+ }
+ return 0;
+}
+
static HRESULT WINAPI IXAudio2Impl_CreateMasteringVoice(IXAudio2 *iface,
IXAudio2MasteringVoice **ppMasteringVoice, UINT32 inputChannels,
UINT32 inputSampleRate, UINT32 flags, const WCHAR *deviceId,
@@ -1329,8 +1422,12 @@ static HRESULT WINAPI IXAudio2Impl_CreateMasteringVoice(IXAudio2 *iface,
AUDIO_STREAM_CATEGORY streamCategory)
{
IXAudio2Impl *This = impl_from_IXAudio2(iface);
+ IMMDevice *dev;
+ HRESULT hr;
+ WAVEFORMATEX *fmt;
+ ALCint attrs[7];
- FIXME("(%p)->(%p, %u, %u, 0x%x, %s, %p, 0x%x): stub!\n", This,
+ TRACE("(%p)->(%p, %u, %u, 0x%x, %s, %p, 0x%x)\n", This,
ppMasteringVoice, inputChannels, inputSampleRate, flags,
wine_dbgstr_w(deviceId), pEffectChain, streamCategory);
@@ -1340,12 +1437,183 @@ static HRESULT WINAPI IXAudio2Impl_CreateMasteringVoice(IXAudio2 *iface,
if(pEffectChain)
WARN("Effect chain is unimplemented\n");
- /* TODO: Initialize mmdevice */
+ EnterCriticalSection(&This->lock);
/* there can only be one Mastering Voice, so just build it into XA2 */
+ if(This->aclient){
+ LeaveCriticalSection(&This->lock);
+ return XAUDIO2_E_INVALID_CALL;
+ }
+
+ if(!deviceId){
+ if(This->ndevs == 0){
+ LeaveCriticalSection(&This->lock);
+ return ERROR_NOT_FOUND;
+ }
+ deviceId = This->devids[0];
+ }
+
+ hr = IMMDeviceEnumerator_GetDevice(This->devenum, deviceId, &dev);
+ if(FAILED(hr)){
+ WARN("GetDevice failed: %08x\n", hr);
+ hr = XAUDIO2_E_DEVICE_INVALIDATED;
+ goto exit;
+ }
+
+ hr = IMMDevice_Activate(dev, &IID_IAudioClient,
+ CLSCTX_INPROC_SERVER, NULL, (void**)&This->aclient);
+ if(FAILED(hr)){
+ WARN("Activate(IAudioClient) failed: %08x\n", hr);
+ IMMDevice_Release(dev);
+ hr = XAUDIO2_E_DEVICE_INVALIDATED;
+ goto exit;
+ }
+
+ IMMDevice_Release(dev);
+
+ hr = IAudioClient_GetMixFormat(This->aclient, &fmt);
+ if(FAILED(hr)){
+ WARN("GetMixFormat failed: %08x\n", hr);
+ hr = XAUDIO2_E_DEVICE_INVALIDATED;
+ goto exit;
+ }
+
+ if(sizeof(WAVEFORMATEX) + fmt->cbSize > sizeof(WAVEFORMATEXTENSIBLE)){
+ FIXME("Mix format doesn't fit into WAVEFORMATEXTENSIBLE!\n");
+ hr = XAUDIO2_E_DEVICE_INVALIDATED;
+ goto exit;
+ }
+
+ if(inputChannels == XAUDIO2_DEFAULT_CHANNELS)
+ inputChannels = fmt->nChannels;
+ if(inputSampleRate == XAUDIO2_DEFAULT_SAMPLERATE)
+ inputSampleRate = fmt->nSamplesPerSec;
+
+ memcpy(&This->fmt, fmt, sizeof(WAVEFORMATEX) + fmt->cbSize);
+ This->fmt.Format.nChannels = inputChannels;
+ This->fmt.Format.nSamplesPerSec = inputSampleRate;
+ This->fmt.Format.nBlockAlign = This->fmt.Format.nChannels * This->fmt.Format.wBitsPerSample / 8;
+ This->fmt.Format.nAvgBytesPerSec = This->fmt.Format.nSamplesPerSec * This->fmt.Format.nBlockAlign;
+ This->fmt.dwChannelMask = get_channel_mask(This->fmt.Format.nChannels);
+
+ CoTaskMemFree(fmt);
+ fmt = NULL;
+
+ hr = IAudioClient_IsFormatSupported(This->aclient,
+ AUDCLNT_SHAREMODE_SHARED, &This->fmt.Format, &fmt);
+ if(hr == S_FALSE){
+ if(sizeof(WAVEFORMATEX) + fmt->cbSize > sizeof(WAVEFORMATEXTENSIBLE)){
+ FIXME("Mix format doesn't fit into WAVEFORMATEXTENSIBLE!\n");
+ hr = XAUDIO2_E_DEVICE_INVALIDATED;
+ goto exit;
+ }
+ memcpy(&This->fmt, fmt, sizeof(WAVEFORMATEX) + fmt->cbSize);
+ }
+
+ CoTaskMemFree(fmt);
+
+ hr = IAudioClient_Initialize(This->aclient, AUDCLNT_SHAREMODE_SHARED,
+ AUDCLNT_STREAMFLAGS_EVENTCALLBACK, inputSampleRate /* 1s buffer */,
+ 0, &This->fmt.Format, NULL);
+ if(FAILED(hr)){
+ WARN("Initialize failed: %08x\n", hr);
+ hr = XAUDIO2_E_DEVICE_INVALIDATED;
+ goto exit;
+ }
+
+ hr = IAudioClient_GetService(This->aclient, &IID_IAudioRenderClient,
+ (void**)&This->render);
+ if(FAILED(hr)){
+ WARN("GetService(IAudioRenderClient) failed: %08x\n", hr);
+ hr = XAUDIO2_E_DEVICE_INVALIDATED;
+ goto exit;
+ }
+
+ /* setup openal context */
+ attrs[0] = ALC_FORMAT_CHANNELS_SOFT;
+ switch(inputChannels){
+ case 1:
+ attrs[1] = ALC_MONO_SOFT;
+ break;
+ case 2:
+ attrs[1] = ALC_STEREO_SOFT;
+ break;
+ case 4:
+ attrs[1] = ALC_QUAD_SOFT;
+ break;
+ case 6:
+ attrs[1] = ALC_5POINT1_SOFT;
+ break;
+ case 7:
+ attrs[1] = ALC_6POINT1_SOFT;
+ break;
+ case 8:
+ attrs[1] = ALC_7POINT1_SOFT;
+ break;
+ default:
+ WARN("OpenAL doesn't support %u channels\n", inputChannels);
+ LeaveCriticalSection(&This->lock);
+ return AUDCLNT_E_UNSUPPORTED_FORMAT;
+ }
+ attrs[2] = ALC_FREQUENCY;
+ attrs[3] = inputSampleRate;
+ attrs[4] = ALC_FORMAT_TYPE_SOFT;
+ attrs[5] = al_get_loopback_format(&This->fmt);
+ attrs[6] = 0;
+
+ if(!attrs[5]){
+ WARN("OpenAL can't output samples in this format\n");
+ hr = XAUDIO2_E_DEVICE_INVALIDATED;
+ goto exit;
+ }
+
+ This->al_device = palcLoopbackOpenDeviceSOFT(NULL);
+ if(!This->al_device){
+ WARN("alcLoopbackOpenDeviceSOFT failed\n");
+ hr = XAUDIO2_E_DEVICE_INVALIDATED;
+ goto exit;
+ }
+
+ This->al_ctx = alcCreateContext(This->al_device, attrs);
+ if(!This->al_ctx){
+ WARN("alcCreateContext failed\n");
+ hr = XAUDIO2_E_DEVICE_INVALIDATED;
+ goto exit;
+ }
+
+ if(alcMakeContextCurrent(This->al_ctx) == ALC_FALSE){
+ WARN("alcMakeContextCurrent failed\n");
+ hr = XAUDIO2_E_DEVICE_INVALIDATED;
+ goto exit;
+ }
+
+ IAudioClient_Start(This->aclient);
+
*ppMasteringVoice = &This->IXAudio2MasteringVoice_iface;
- return S_OK;
+exit:
+ if(FAILED(hr)){
+ if(This->render){
+ IAudioRenderClient_Release(This->render);
+ This->render = NULL;
+ }
+ if(This->aclient){
+ IAudioClient_Release(This->aclient);
+ This->aclient = NULL;
+ }
+ if(This->al_ctx){
+ alcDestroyContext(This->al_ctx);
+ This->al_ctx = NULL;
+ }
+ if(This->al_device){
+ alcCloseDevice(This->al_device);
+ This->al_device = NULL;
+ }
+ }
+
+ LeaveCriticalSection(&This->lock);
+
+ return hr;
}
static HRESULT WINAPI IXAudio2Impl_StartEngine(IXAudio2 *iface)
--
2.5.0
More information about the wine-patches
mailing list