From: Paul Gofman pgofman@codeweavers.com
--- dlls/xaudio2_7/xaudio_dll.c | 2 ++ 1 file changed, 2 insertions(+)
diff --git a/dlls/xaudio2_7/xaudio_dll.c b/dlls/xaudio2_7/xaudio_dll.c index 43537589823..1035f0b21b1 100644 --- a/dlls/xaudio2_7/xaudio_dll.c +++ b/dlls/xaudio2_7/xaudio_dll.c @@ -1617,6 +1617,7 @@ static HRESULT WINAPI IXAudio2Impl_CreateSourceVoice(IXAudio2 *iface, free_voice_sends(faudio_sends); if(FAILED(hr)){ LeaveCriticalSection(&This->lock); + free_effect_chain(src->effect_chain); return hr; } src->in_use = TRUE; @@ -1671,6 +1672,7 @@ static HRESULT WINAPI IXAudio2Impl_CreateSubmixVoice(IXAudio2 *iface, free_voice_sends(faudio_sends); if(FAILED(hr)){ LeaveCriticalSection(&sub->lock); + free_effect_chain(sub->effect_chain); return hr; } sub->in_use = TRUE;
From: Paul Gofman pgofman@codeweavers.com
--- dlls/xaudio2_7/tests/xaudio2.c | 20 ++++++++++++++++++++ dlls/xaudio2_7/xaudio_dll.c | 7 +++++++ dlls/xaudio2_8/xaudio2_8.spec | 1 + dlls/xaudio2_9/xaudio2_9.spec | 1 + include/xaudio2.idl | 1 + 5 files changed, 30 insertions(+)
diff --git a/dlls/xaudio2_7/tests/xaudio2.c b/dlls/xaudio2_7/tests/xaudio2.c index 52850343da2..006d3b6b463 100644 --- a/dlls/xaudio2_7/tests/xaudio2.c +++ b/dlls/xaudio2_7/tests/xaudio2.c @@ -1279,6 +1279,22 @@ static void test_setchannelvolumes(IXAudio2 *xa) IXAudio2MasteringVoice_DestroyVoice(master); }
+#if XAUDIO2_VER >= 8 +static void test_XAudio2CreateWithVersionInfo(void) +{ + IXAudio2 *audio; + HRESULT hr; + + hr = XAudio2CreateWithVersionInfo(&audio, 0, XAUDIO2_DEFAULT_PROCESSOR, 0); + ok(hr == S_OK, "got %#lx.\n", hr); + IXAudio2_Release(audio); + + hr = XAudio2CreateWithVersionInfo(&audio, 0, XAUDIO2_DEFAULT_PROCESSOR, ~0); + ok(hr == S_OK, "got %#lx.\n", hr); + IXAudio2_Release(audio); +} +#endif + static UINT32 check_has_devices(IXAudio2 *xa) { HRESULT hr; @@ -1305,6 +1321,10 @@ START_TEST(xaudio2) if (!(audio = create_xaudio2())) return;
+#if XAUDIO2_VER >= 8 + test_XAudio2CreateWithVersionInfo(); +#endif + test_interfaces(audio);
if (check_has_devices(audio)) diff --git a/dlls/xaudio2_7/xaudio_dll.c b/dlls/xaudio2_7/xaudio_dll.c index 1035f0b21b1..6eac068f986 100644 --- a/dlls/xaudio2_7/xaudio_dll.c +++ b/dlls/xaudio2_7/xaudio_dll.c @@ -2014,4 +2014,11 @@ HRESULT WINAPI XAudio2Create(IXAudio2 **ppxa2, UINT32 flags, XAUDIO2_PROCESSOR p
return S_OK; } + +HRESULT WINAPI XAudio2CreateWithVersionInfo(IXAudio2 **ppxa2, UINT32 flags, XAUDIO2_PROCESSOR proc, DWORD version) +{ + TRACE( "%p %#x %#x %#lx.\n", ppxa2, flags, proc, version ); + + return XAudio2Create(ppxa2, flags, proc); +} #endif /* XAUDIO2_VER >= 8 */ diff --git a/dlls/xaudio2_8/xaudio2_8.spec b/dlls/xaudio2_8/xaudio2_8.spec index 78bf05bdb61..a1ee50d4f73 100644 --- a/dlls/xaudio2_8/xaudio2_8.spec +++ b/dlls/xaudio2_8/xaudio2_8.spec @@ -4,3 +4,4 @@ 4 cdecl -ordinal CreateFX(ptr ptr ptr long) 5 cdecl -ordinal X3DAudioCalculate(ptr ptr ptr long ptr) 6 cdecl -ordinal X3DAudioInitialize(long float ptr) +7 stdcall -ordinal XAudio2CreateWithVersionInfo(ptr long long long) diff --git a/dlls/xaudio2_9/xaudio2_9.spec b/dlls/xaudio2_9/xaudio2_9.spec index 78bf05bdb61..a1ee50d4f73 100644 --- a/dlls/xaudio2_9/xaudio2_9.spec +++ b/dlls/xaudio2_9/xaudio2_9.spec @@ -4,3 +4,4 @@ 4 cdecl -ordinal CreateFX(ptr ptr ptr long) 5 cdecl -ordinal X3DAudioCalculate(ptr ptr ptr long ptr) 6 cdecl -ordinal X3DAudioInitialize(long float ptr) +7 stdcall -ordinal XAudio2CreateWithVersionInfo(ptr long long long) diff --git a/include/xaudio2.idl b/include/xaudio2.idl index ac63757720b..2ecc006176c 100644 --- a/include/xaudio2.idl +++ b/include/xaudio2.idl @@ -603,4 +603,5 @@ cpp_quote("#endif")
#if XAUDIO2_VER >= 8 cpp_quote("HRESULT WINAPI XAudio2Create(IXAudio2** pxaudio2, UINT32 flags, XAUDIO2_PROCESSOR processor);") +cpp_quote("HRESULT WINAPI XAudio2CreateWithVersionInfo(IXAudio2** pxaudio2, UINT32 flags, XAUDIO2_PROCESSOR processor, DWORD version);") #endif
From: Paul Gofman pgofman@codeweavers.com
--- dlls/xaudio2_7/tests/xaudio2.c | 160 +++++++++++++++++++++++++++++++++ dlls/xaudio2_7/xaudio_dll.c | 87 ++++++++++++++---- 2 files changed, 230 insertions(+), 17 deletions(-)
diff --git a/dlls/xaudio2_7/tests/xaudio2.c b/dlls/xaudio2_7/tests/xaudio2.c index 006d3b6b463..1165db2a1f6 100644 --- a/dlls/xaudio2_7/tests/xaudio2.c +++ b/dlls/xaudio2_7/tests/xaudio2.c @@ -842,6 +842,126 @@ static void test_looping(IXAudio2 *xa) HeapFree(GetProcessHeap(), 0, (void*)buf.pAudioData); }
+struct test_xapo +{ + IXAPO IXAPO_iface; +}; + +static struct test_xapo test_xapo; +static BOOL test_xapo_supports_ixapo; +static BOOL test_xapo_supports_ixapo27; + +static HRESULT WINAPI test_xapo_QueryInterface(IXAPO *iface, REFIID riid, void **ppvObject) +{ + if (IsEqualGUID(riid, &IID_IUnknown)) + *ppvObject = &test_xapo.IXAPO_iface; + else if (test_xapo_supports_ixapo && IsEqualGUID(riid, &IID_IXAPO)) + *ppvObject = &test_xapo.IXAPO_iface; + else if (test_xapo_supports_ixapo27 && IsEqualGUID(riid, &IID_IXAPO27)) + *ppvObject = &test_xapo.IXAPO_iface; + else + *ppvObject = NULL; + + if(*ppvObject) + return S_OK; + + return E_NOINTERFACE; +} + +static ULONG WINAPI test_xapo_AddRef(IXAPO *iface) +{ + return 1; +} + +static ULONG WINAPI test_xapo_Release(IXAPO *iface) +{ + return 1; +} + +static HRESULT WINAPI test_xapo_GetRegistrationProperties(IXAPO *iface, + XAPO_REGISTRATION_PROPERTIES **props) +{ + XAPO_REGISTRATION_PROPERTIES *p; + + *props = CoTaskMemAlloc(sizeof(**props)); + memset(*props, 0, sizeof(**props)); + p = *props; + p->MinInputBufferCount = 1; + p->MaxInputBufferCount = 1000; + p->MinOutputBufferCount = 1; + p->MaxOutputBufferCount = 1000; + return S_OK; +} + +static HRESULT WINAPI test_xapo_IsInputFormatSupported(IXAPO *iface, + const WAVEFORMATEX *output_fmt, const WAVEFORMATEX *input_fmt, + WAVEFORMATEX **supported_fmt) +{ + return S_OK; +} + +static HRESULT WINAPI test_xapo_IsOutputFormatSupported(IXAPO *iface, + const WAVEFORMATEX *input_fmt, const WAVEFORMATEX *output_fmt, + WAVEFORMATEX **supported_fmt) +{ + return S_OK; +} + +static HRESULT WINAPI test_xapo_Initialize(IXAPO *iface, const void *data, + UINT32 data_len) +{ + return S_OK; +} + +static void WINAPI test_xapo_Reset(IXAPO *iface) +{ +} + +static HRESULT WINAPI test_xapo_LockForProcess(IXAPO *iface, UINT32 in_params_count, + const XAPO_LOCKFORPROCESS_BUFFER_PARAMETERS *in_params, + UINT32 out_params_count, + const XAPO_LOCKFORPROCESS_BUFFER_PARAMETERS *out_params) +{ + return S_OK; +} + +static void WINAPI test_xapo_UnlockForProcess(IXAPO *iface) +{ +} + +static void WINAPI test_xapo_Process(IXAPO *iface, UINT32 in_params_count, + const XAPO_PROCESS_BUFFER_PARAMETERS *in_params, + UINT32 out_params_count, + XAPO_PROCESS_BUFFER_PARAMETERS *out_params, BOOL enabled) +{ +} + +static UINT32 WINAPI test_xapo_CalcInputFrames(IXAPO *iface, UINT32 output_frames) +{ + return 0; +} + +static UINT32 WINAPI test_xapo_CalcOutputFrames(IXAPO *iface, UINT32 input_frames) +{ + return 0; +} + +static const IXAPOVtbl test_xapo_vtbl = { + test_xapo_QueryInterface, + test_xapo_AddRef, + test_xapo_Release, + test_xapo_GetRegistrationProperties, + test_xapo_IsInputFormatSupported, + test_xapo_IsOutputFormatSupported, + test_xapo_Initialize, + test_xapo_Reset, + test_xapo_LockForProcess, + test_xapo_UnlockForProcess, + test_xapo_Process, + test_xapo_CalcInputFrames, + test_xapo_CalcOutputFrames, +}; + static void test_submix(IXAudio2 *xa) { HRESULT hr; @@ -850,6 +970,8 @@ static void test_submix(IXAudio2 *xa) IXAudio2SubmixVoice *sub, *sub2; XAUDIO2_SEND_DESCRIPTOR send_desc = { 0 }; XAUDIO2_VOICE_SENDS sends = { 1, &send_desc }; + XAUDIO2_EFFECT_DESCRIPTOR effect; + XAUDIO2_EFFECT_CHAIN chain;
check_refcount((IUnknown *)xa, 1);
@@ -858,6 +980,44 @@ static void test_submix(IXAudio2 *xa) hr = create_mastering_voice(xa, 2, &master); ok(hr == S_OK, "CreateMasteringVoice failed: %08lx\n", hr);
+ effect.InitialState = TRUE; + effect.OutputChannels = 2; + effect.pEffect = NULL; + + chain.EffectCount = 1; + chain.pEffectDescriptors = &effect; + + if (XAUDIO2_VER >= 8) + { + /* These tests with invalid effect crash on earlier xaudio2 versions. */ + hr = IXAudio2MasteringVoice_SetEffectChain(master, NULL); + ok(!hr, "got %#lx\n", hr); + hr = IXAudio2MasteringVoice_SetEffectChain(master, &chain); + ok(hr == XAUDIO2_E_INVALID_CALL, "got %#lx\n", hr); + + hr = IXAudio2_CreateSubmixVoice(xa, &sub, 2, 44100, 0, 0, NULL, &chain); + ok(hr == XAUDIO2_E_INVALID_CALL, "got %#lx\n", hr); + test_xapo.IXAPO_iface.lpVtbl = &test_xapo_vtbl; + + test_xapo_supports_ixapo = FALSE; + test_xapo_supports_ixapo27 = FALSE; + effect.pEffect = (IUnknown *)&test_xapo.IXAPO_iface; + hr = IXAudio2_CreateSubmixVoice(xa, &sub, 2, 44100, 0, 0, NULL, &chain); + ok(hr == XAUDIO2_E_INVALID_CALL, "got %#lx\n", hr); + + test_xapo_supports_ixapo27 = TRUE; + hr = IXAudio2_CreateSubmixVoice(xa, &sub, 2, 44100, 0, 0, NULL, &chain); + ok(hr == XAUDIO2_E_INVALID_CALL, "got %#lx\n", hr); + hr = IXAudio2MasteringVoice_SetEffectChain(master, &chain); + ok(hr == XAUDIO2_E_INVALID_CALL, "got %#lx\n", hr); + + test_xapo_supports_ixapo = TRUE; + test_xapo_supports_ixapo27 = FALSE; + hr = IXAudio2_CreateSubmixVoice(xa, &sub, 2, 44100, 0, 0, NULL, &chain); + ok(!hr, "got %#lx.\n", hr); + IXAudio2SubmixVoice_DestroyVoice(sub); + } + hr = IXAudio2_CreateSubmixVoice(xa, &sub, 2, 44100, 0, 0, NULL, NULL); ok(hr == S_OK, "CreateSubmixVoice failed: %08lx\n", hr);
diff --git a/dlls/xaudio2_7/xaudio_dll.c b/dlls/xaudio2_7/xaudio_dll.c index 6eac068f986..fe9351e8b4a 100644 --- a/dlls/xaudio2_7/xaudio_dll.c +++ b/dlls/xaudio2_7/xaudio_dll.c @@ -272,6 +272,11 @@ static XA2XAPOImpl *wrap_xapo(IUnknown *unk) #if XAUDIO2_VER <= 7 hr = IUnknown_QueryInterface(unk, &IID_IXAPO27, (void**)&xapo); #else + if (!unk) + { + WARN("NULL XAPO interface.\n"); + return NULL; + } hr = IUnknown_QueryInterface(unk, &IID_IXAPO, (void**)&xapo); #endif if(FAILED(hr)){ @@ -301,9 +306,20 @@ static XA2XAPOImpl *wrap_xapo(IUnknown *unk) return ret; }
+static void free_effect_chain(FAudioEffectChain *chain) +{ + int i; + if(!chain) + return; + for(i = 0; i < chain->EffectCount; ++i) + XAPO_Release(chain->pEffectDescriptors[i].pEffect); + free(chain); +} + FAudioEffectChain *wrap_effect_chain(const XAUDIO2_EFFECT_CHAIN *pEffectChain) { FAudioEffectChain *ret; + XA2XAPOImpl *xapo; int i;
if(!pEffectChain) @@ -315,7 +331,16 @@ FAudioEffectChain *wrap_effect_chain(const XAUDIO2_EFFECT_CHAIN *pEffectChain) ret->pEffectDescriptors = (void*)(ret + 1);
for(i = 0; i < ret->EffectCount; ++i){ - ret->pEffectDescriptors[i].pEffect = &wrap_xapo(pEffectChain->pEffectDescriptors[i].pEffect)->FAPO_vtbl; + xapo = wrap_xapo(pEffectChain->pEffectDescriptors[i].pEffect); +#if XAUDIO2_VER >= 8 + if (!xapo) + { + ret->EffectCount = i; + free_effect_chain(ret); + return NULL; + } +#endif + ret->pEffectDescriptors[i].pEffect = &xapo->FAPO_vtbl; ret->pEffectDescriptors[i].InitialState = pEffectChain->pEffectDescriptors[i].InitialState; ret->pEffectDescriptors[i].OutputChannels = pEffectChain->pEffectDescriptors[i].OutputChannels; } @@ -323,16 +348,6 @@ FAudioEffectChain *wrap_effect_chain(const XAUDIO2_EFFECT_CHAIN *pEffectChain) return ret; }
-static void free_effect_chain(FAudioEffectChain *chain) -{ - int i; - if(!chain) - return; - for(i = 0; i < chain->EffectCount; ++i) - XAPO_Release(chain->pEffectDescriptors[i].pEffect); - free(chain); -} - /* Send Wrapping */
static FAudioVoiceSends *wrap_voice_sends(const XAUDIO2_VOICE_SENDS *sends) @@ -560,12 +575,18 @@ static HRESULT WINAPI XA2SRC_SetEffectChain(IXAudio2SourceVoice *iface, const XAUDIO2_EFFECT_CHAIN *pEffectChain) { XA2VoiceImpl *This = impl_from_IXAudio2SourceVoice(iface); + FAudioEffectChain *chain; HRESULT hr;
TRACE("%p, %p\n", This, pEffectChain);
+ chain = wrap_effect_chain(pEffectChain); +#if XAUDIO2_VER >= 8 + if (pEffectChain && !chain) + return XAUDIO2_E_INVALID_CALL; +#endif free_effect_chain(This->effect_chain); - This->effect_chain = wrap_effect_chain(pEffectChain); + This->effect_chain = chain;
hr = FAudioVoice_SetEffectChain(This->faudio_voice, This->effect_chain);
@@ -922,12 +943,18 @@ static HRESULT WINAPI XA2SUB_SetEffectChain(IXAudio2SubmixVoice *iface, const XAUDIO2_EFFECT_CHAIN *pEffectChain) { XA2VoiceImpl *This = impl_from_IXAudio2SubmixVoice(iface); + FAudioEffectChain *chain; HRESULT hr;
TRACE("%p, %p\n", This, pEffectChain);
+ chain = wrap_effect_chain(pEffectChain); +#if XAUDIO2_VER >= 8 + if (pEffectChain && !chain) + return XAUDIO2_E_INVALID_CALL; +#endif free_effect_chain(This->effect_chain); - This->effect_chain = wrap_effect_chain(pEffectChain); + This->effect_chain = chain;
hr = FAudioVoice_SetEffectChain(This->faudio_voice, This->effect_chain);
@@ -1167,12 +1194,18 @@ static HRESULT WINAPI XA2M_SetEffectChain(IXAudio2MasteringVoice *iface, const XAUDIO2_EFFECT_CHAIN *pEffectChain) { XA2VoiceImpl *This = impl_from_IXAudio2MasteringVoice(iface); + FAudioEffectChain *chain; HRESULT hr;
TRACE("%p, %p\n", This, pEffectChain);
+ chain = wrap_effect_chain(pEffectChain); +#if XAUDIO2_VER >= 8 + if (pEffectChain && !chain) + return XAUDIO2_E_INVALID_CALL; +#endif free_effect_chain(This->effect_chain); - This->effect_chain = wrap_effect_chain(pEffectChain); + This->effect_chain = chain;
hr = FAudioVoice_SetEffectChain(This->faudio_voice, This->effect_chain);
@@ -1586,11 +1619,17 @@ static HRESULT WINAPI IXAudio2Impl_CreateSourceVoice(IXAudio2 *iface, XA2VoiceImpl *src; HRESULT hr; FAudioVoiceSends *faudio_sends; + FAudioEffectChain *chain;
TRACE("(%p)->(%p, %p, 0x%x, %f, %p, %p, %p)\n", This, ppSourceVoice, pSourceFormat, flags, maxFrequencyRatio, pCallback, pSendList, pEffectChain);
+ chain = wrap_effect_chain(pEffectChain); +#if XAUDIO2_VER >= 8 + if (pEffectChain && !chain) + return XAUDIO2_E_INVALID_CALL; +#endif EnterCriticalSection(&This->lock);
LIST_FOR_EACH_ENTRY(src, &This->voices, XA2VoiceImpl, entry){ @@ -1607,7 +1646,7 @@ static HRESULT WINAPI IXAudio2Impl_CreateSourceVoice(IXAudio2 *iface,
LeaveCriticalSection(&This->lock);
- src->effect_chain = wrap_effect_chain(pEffectChain); + src->effect_chain = chain; faudio_sends = wrap_voice_sends(pSendList);
hr = FAudio_CreateSourceVoice(This->faudio, &src->faudio_voice, @@ -1642,11 +1681,18 @@ static HRESULT WINAPI IXAudio2Impl_CreateSubmixVoice(IXAudio2 *iface, IXAudio2Impl *This = impl_from_IXAudio2(iface); XA2VoiceImpl *sub; FAudioVoiceSends *faudio_sends; + FAudioEffectChain *chain;
TRACE("(%p)->(%p, %u, %u, 0x%x, %u, %p, %p)\n", This, ppSubmixVoice, inputChannels, inputSampleRate, flags, processingStage, pSendList, pEffectChain);
+ chain = wrap_effect_chain(pEffectChain); +#if XAUDIO2_VER >= 8 + if (pEffectChain && !chain) + return XAUDIO2_E_INVALID_CALL; +#endif + EnterCriticalSection(&This->lock);
LIST_FOR_EACH_ENTRY(sub, &This->voices, XA2VoiceImpl, entry){ @@ -1663,7 +1709,7 @@ static HRESULT WINAPI IXAudio2Impl_CreateSubmixVoice(IXAudio2 *iface,
LeaveCriticalSection(&This->lock);
- sub->effect_chain = wrap_effect_chain(pEffectChain); + sub->effect_chain = chain; faudio_sends = wrap_voice_sends(pSendList);
hr = FAudio_CreateSubmixVoice(This->faudio, &sub->faudio_voice, inputChannels, @@ -1701,10 +1747,17 @@ static HRESULT WINAPI IXAudio2Impl_CreateMasteringVoice(IXAudio2 *iface, ) { IXAudio2Impl *This = impl_from_IXAudio2(iface); + FAudioEffectChain *chain;
TRACE("(%p)->(%p, %u, %u, 0x%x, %p)\n", This, ppMasteringVoice, inputChannels, inputSampleRate, flags, pEffectChain);
+ chain = wrap_effect_chain(pEffectChain); +#if XAUDIO2_VER >= 8 + if (pEffectChain && !chain) + return XAUDIO2_E_INVALID_CALL; +#endif + EnterCriticalSection(&This->lock);
*ppMasteringVoice = &This->mst.IXAudio2MasteringVoice_iface; @@ -1719,7 +1772,7 @@ static HRESULT WINAPI IXAudio2Impl_CreateMasteringVoice(IXAudio2 *iface,
LeaveCriticalSection(&This->lock);
- This->mst.effect_chain = wrap_effect_chain(pEffectChain); + This->mst.effect_chain = chain;
#if XAUDIO2_VER >= 8 TRACE("device id %s, category %#x\n", debugstr_w(deviceId), streamCategory);
This helps Renegade X which crashes when native is preferred for dinput8 (while dinput8 in this case is not a Windows dinput8 but some hooker shipped with the game).
Besides the need in XAudio2CreateWithVersionInfo, the game supplies an effect to CreateSubmixVoice which supports IXAPO27 but not IXAPO. That results in crash in Wine's xaudio currently (any version), as well as Windows xaudio2_7, but xaudio2_8 handles such case and returns an error instead of crashing (this behaviour is covered by the test). Doing so helps the game.
This merge request was approved by Huw Davies.