diff --git a/configure b/configure index e009f6f8f5d..b12b7e34ee2 100755 --- a/configure +++ b/configure @@ -17824,6 +17824,7 @@ wine_fn_config_dll xapofx1_3 enable_xapofx1_3 wine_fn_config_dll xapofx1_4 enable_xapofx1_4 wine_fn_config_dll xapofx1_5 enable_xapofx1_5 wine_fn_config_dll xaudio2_7 enable_xaudio2_7 clean +wine_fn_config_test dlls/xaudio2_7/tests xaudio2_7_test wine_fn_config_dll xaudio2_8 enable_xaudio2_8 wine_fn_config_dll xinput1_1 enable_xinput1_1 wine_fn_config_dll xinput1_2 enable_xinput1_2 diff --git a/configure.ac b/configure.ac index 2b0b4053071..0276a1c13d5 100644 --- a/configure.ac +++ b/configure.ac @@ -3425,6 +3425,7 @@ WINE_CONFIG_DLL(xapofx1_3) WINE_CONFIG_DLL(xapofx1_4) WINE_CONFIG_DLL(xapofx1_5) WINE_CONFIG_DLL(xaudio2_7,,[clean]) +WINE_CONFIG_TEST(dlls/xaudio2_7/tests) WINE_CONFIG_DLL(xaudio2_8) WINE_CONFIG_DLL(xinput1_1) WINE_CONFIG_DLL(xinput1_2) diff --git a/dlls/xaudio2_7/tests/Makefile.in b/dlls/xaudio2_7/tests/Makefile.in new file mode 100644 index 00000000000..ba56d94a1f3 --- /dev/null +++ b/dlls/xaudio2_7/tests/Makefile.in @@ -0,0 +1,5 @@ +TESTDLL = xaudio2_7.dll +IMPORTS = ole32 + +C_SRCS = \ + xaudio2.c diff --git a/dlls/xaudio2_7/tests/xaudio2.c b/dlls/xaudio2_7/tests/xaudio2.c new file mode 100644 index 00000000000..669e5ed43ad --- /dev/null +++ b/dlls/xaudio2_7/tests/xaudio2.c @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2015 Andrew Eikum for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include + +#define COBJMACROS +#include "wine/test.h" +#include "initguid.h" +#include "xaudio2.h" + +static BOOL xaudio27; + +static HRESULT (WINAPI *pXAudio2Create)(IXAudio2 **, UINT32, XAUDIO2_PROCESSOR) = NULL; + +static void test_DeviceDetails(IXAudio27 *xa) +{ + HRESULT hr; + XAUDIO2_DEVICE_DETAILS dd; + UINT32 count, i; + + hr = IXAudio27_GetDeviceCount(xa, &count); + ok(hr == S_OK, "GetDeviceCount failed: %08x\n", hr); + + if(count == 0) + return; + + for(i = 0; i < count; ++i){ + hr = IXAudio27_GetDeviceDetails(xa, i, &dd); + ok(hr == S_OK, "GetDeviceDetails failed: %08x\n", hr); + + if(i == 0) + ok(dd.Role == GlobalDefaultDevice, "Got wrong role for index 0: 0x%x\n", dd.Role); + else + ok(dd.Role == NotDefaultDevice, "Got wrong role for index %u: 0x%x\n", i, dd.Role); + } +} + +START_TEST(xaudio2) +{ + HRESULT hr; + IXAudio27 *xa27 = NULL; + IXAudio2 *xa = NULL; + HANDLE xa28dll; + + CoInitialize(NULL); + + xa28dll = LoadLibraryA("xaudio2_8.dll"); + if(xa28dll) + pXAudio2Create = (void*)GetProcAddress(xa28dll, "XAudio2Create"); + + /* XAudio 2.7 (Jun 2010 DirectX) */ + hr = CoCreateInstance(&CLSID_XAudio2, NULL, CLSCTX_INPROC_SERVER, + &IID_IXAudio27, (void**)&xa27); + if(hr == S_OK){ + xaudio27 = TRUE; + + hr = IXAudio27_Initialize(xa27, 0, XAUDIO2_ANY_PROCESSOR); + ok(hr == S_OK, "Initialize failed: %08x\n", hr); + + test_DeviceDetails(xa27); + + IXAudio27_Release(xa27); + }else + win_skip("XAudio 2.7 not available\n"); + + /* XAudio 2.8 (Win8+) */ + if(pXAudio2Create){ + xaudio27 = FALSE; + + hr = pXAudio2Create(&xa, 0, XAUDIO2_DEFAULT_PROCESSOR); + ok(hr == S_OK, "XAudio2Create failed: %08x\n", hr); + + IXAudio2_Release(xa); + }else + win_skip("XAudio 2.8 not available\n"); + + CoUninitialize(); +} diff --git a/dlls/xaudio2_7/xaudio_dll.c b/dlls/xaudio2_7/xaudio_dll.c index 1c56152d17c..39a5a79acf3 100644 --- a/dlls/xaudio2_7/xaudio_dll.c +++ b/dlls/xaudio2_7/xaudio_dll.c @@ -1,5 +1,6 @@ /* * Copyright (c) 2015 Mark Harmstone + * Copyright (c) 2015 Andrew Eikum for CodeWeavers * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -18,6 +19,8 @@ #include +#define NONAMELESSUNION +#define NONAMELESSSTRUCT #define COBJMACROS #include "windef.h" @@ -34,6 +37,9 @@ #include "mmsystem.h" #include "xaudio2.h" +#include "devpkey.h" +#include "mmdeviceapi.h" +#include "audioclient.h" WINE_DEFAULT_DEBUG_CHANNEL(xaudio2); @@ -155,6 +161,11 @@ typedef struct { struct list source_voices; struct list submix_voices; + + IMMDeviceEnumerator *devenum; + + WCHAR **devids; + UINT32 ndevs; } IXAudio2Impl; static inline IXAudio2Impl *impl_from_IXAudio2(IXAudio2 *iface) @@ -1098,6 +1109,7 @@ static ULONG WINAPI IXAudio2Impl_Release(IXAudio2 *iface) TRACE("(%p)->(): Refcount now %u\n", This, ref); if (!ref) { + int i; XA2SourceImpl *src, *src2; XA2SubmixImpl *sub, *sub2; @@ -1113,6 +1125,12 @@ static ULONG WINAPI IXAudio2Impl_Release(IXAudio2 *iface) HeapFree(GetProcessHeap(), 0, sub); } + if(This->devenum) + IMMDeviceEnumerator_Release(This->devenum); + for(i = 0; i < This->ndevs; ++i) + CoTaskMemFree(This->devids[i]); + HeapFree(GetProcessHeap(), 0, This->devids); + DeleteCriticalSection(&This->lock); HeapFree(GetProcessHeap(), 0, This); @@ -1365,16 +1383,93 @@ static ULONG WINAPI XA27_Release(IXAudio27 *iface) static HRESULT WINAPI XA27_GetDeviceCount(IXAudio27 *iface, UINT32 *pCount) { IXAudio2Impl *This = impl_from_IXAudio27(iface); - TRACE("(%p)->(%p)\n", This, pCount); - return E_NOTIMPL; + + TRACE("%p, %p\n", This, pCount); + + *pCount = This->ndevs; + + return S_OK; } static HRESULT WINAPI XA27_GetDeviceDetails(IXAudio27 *iface, UINT32 index, XAUDIO2_DEVICE_DETAILS *pDeviceDetails) { IXAudio2Impl *This = impl_from_IXAudio27(iface); - TRACE("(%p)->(%u, %p)\n", This, index, pDeviceDetails); - return E_NOTIMPL; + HRESULT hr; + IMMDevice *dev; + IAudioClient *client; + IPropertyStore *ps; + WAVEFORMATEX *wfx; + PROPVARIANT var; + + TRACE("%p, %u, %p\n", This, index, pDeviceDetails); + + if(index >= This->ndevs) + return E_INVALIDARG; + + hr = IMMDeviceEnumerator_GetDevice(This->devenum, This->devids[index], &dev); + if(FAILED(hr)){ + WARN("GetDevice failed: %08x\n", hr); + return hr; + } + + hr = IMMDevice_Activate(dev, &IID_IAudioClient, CLSCTX_INPROC_SERVER, + NULL, (void**)&client); + if(FAILED(hr)){ + WARN("Activate failed: %08x\n", hr); + IMMDevice_Release(dev); + return hr; + } + + hr = IMMDevice_OpenPropertyStore(dev, STGM_READ, &ps); + if(FAILED(hr)){ + WARN("OpenPropertyStore failed: %08x\n", hr); + IAudioClient_Release(client); + IMMDevice_Release(dev); + return hr; + } + + PropVariantInit(&var); + + hr = IPropertyStore_GetValue(ps, (PROPERTYKEY*)&DEVPKEY_Device_FriendlyName, &var); + if(FAILED(hr)){ + WARN("GetValue failed: %08x\n", hr); + goto done; + } + + lstrcpynW(pDeviceDetails->DisplayName, var.u.pwszVal, sizeof(pDeviceDetails->DisplayName)/sizeof(WCHAR)); + + PropVariantClear(&var); + + hr = IAudioClient_GetMixFormat(client, &wfx); + if(FAILED(hr)){ + WARN("GetMixFormat failed: %08x\n", hr); + goto done; + } + + lstrcpyW(pDeviceDetails->DeviceID, This->devids[index]); + + if(index == 0) + pDeviceDetails->Role = GlobalDefaultDevice; + else + pDeviceDetails->Role = NotDefaultDevice; + + if(sizeof(WAVEFORMATEX) + wfx->cbSize > sizeof(pDeviceDetails->OutputFormat)){ + FIXME("AudioClient format is too large to fit into WAVEFORMATEXTENSIBLE!\n"); + CoTaskMemFree(wfx); + hr = E_FAIL; + goto done; + } + memcpy(&pDeviceDetails->OutputFormat, wfx, sizeof(WAVEFORMATEX) + wfx->cbSize); + + CoTaskMemFree(wfx); + +done: + IPropertyStore_Release(ps); + IAudioClient_Release(client); + IMMDevice_Release(dev); + + return hr; } static HRESULT WINAPI XA27_Initialize(IXAudio27 *iface, UINT32 flags, @@ -1429,12 +1524,16 @@ static HRESULT WINAPI XA27_CreateMasteringVoice(IXAudio27 *iface, const XAUDIO2_EFFECT_CHAIN *pEffectChain) { IXAudio2Impl *This = impl_from_IXAudio27(iface); + TRACE("(%p)->(%p, %u, %u, 0x%x, %u, %p)\n", This, ppMasteringVoice, inputChannels, inputSampleRate, flags, deviceIndex, pEffectChain); - /* TODO: Convert DeviceIndex to DeviceId */ - return IXAudio2Impl_CreateMasteringVoice(&This->IXAudio2_iface, - ppMasteringVoice, inputChannels, inputSampleRate, flags, 0, + + if(deviceIndex >= This->ndevs) + return E_INVALIDARG; + + return IXAudio2Impl_CreateMasteringVoice(&This->IXAudio2_iface, ppMasteringVoice, + inputChannels, inputSampleRate, flags, This->devids[deviceIndex], pEffectChain, AudioCategory_GameEffects); } @@ -1516,6 +1615,79 @@ static ULONG WINAPI XAudio2CF_Release(IClassFactory *iface) return 1; } +static HRESULT initialize_mmdevices(IXAudio2Impl *This) +{ + IMMDeviceCollection *devcoll; + UINT devcount; + HRESULT hr; + + if(!This->devenum){ + hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, + CLSCTX_INPROC_SERVER, &IID_IMMDeviceEnumerator, (void**)&This->devenum); + if(FAILED(hr)) + return hr; + } + + hr = IMMDeviceEnumerator_EnumAudioEndpoints(This->devenum, eRender, + DEVICE_STATE_ACTIVE, &devcoll); + if(FAILED(hr)){ + return hr; + } + + hr = IMMDeviceCollection_GetCount(devcoll, &devcount); + if(FAILED(hr)){ + IMMDeviceCollection_Release(devcoll); + return hr; + } + + if(devcount > 0){ + UINT i, count = 1; + IMMDevice *dev, *def_dev; + + /* make sure that device 0 is the default device */ + IMMDeviceEnumerator_GetDefaultAudioEndpoint(This->devenum, eRender, eConsole, &def_dev); + + This->devids = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR *) * devcount); + + for(i = 0; i < devcount; ++i){ + hr = IMMDeviceCollection_Item(devcoll, i, &dev); + if(SUCCEEDED(hr)){ + UINT idx; + + if(dev == def_dev) + idx = 0; + else{ + idx = count; + ++count; + } + + hr = IMMDevice_GetId(dev, &This->devids[idx]); + if(FAILED(hr)){ + WARN("GetId failed: %08x\n", hr); + HeapFree(GetProcessHeap(), 0, This->devids); + This->devids = NULL; + IMMDevice_Release(dev); + return hr; + } + + IMMDevice_Release(dev); + }else{ + WARN("Item failed: %08x\n", hr); + HeapFree(GetProcessHeap(), 0, This->devids); + This->devids = NULL; + IMMDeviceCollection_Release(devcoll); + return hr; + } + } + } + + IMMDeviceCollection_Release(devcoll); + + This->ndevs = devcount; + + return S_OK; +} + static HRESULT WINAPI XAudio2CF_CreateInstance(IClassFactory *iface, IUnknown *pOuter, REFIID riid, void **ppobj) { @@ -1549,8 +1721,17 @@ static HRESULT WINAPI XAudio2CF_CreateInstance(IClassFactory *iface, IUnknown *p object->lock.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": IXAudio2Impl.lock"); hr = IXAudio2_QueryInterface(&object->IXAudio2_iface, riid, ppobj); - if(FAILED(hr)) + if(FAILED(hr)){ HeapFree(GetProcessHeap(), 0, object); + return hr; + } + + hr = initialize_mmdevices(object); + if(FAILED(hr)){ + IUnknown_Release((IUnknown*)*ppobj); + return hr; + } + return hr; }