1 /* DirectSound COM interface
3 * Copyright 2009 Maarten Lankhorst
5 * Some code taken from the original dsound-openal implementation
6 * Copyright 2007-2009 Chris Robinson
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
30 #include "dsound_private.h"
32 #ifndef DSSPEAKER_7POINT1
33 #define DSSPEAKER_7POINT1 7
37 static DWORD CALLBACK
DSShare_thread(void *dwUser
)
39 DeviceShare
*share
= (DeviceShare
*)dwUser
;
40 BYTE
*scratch_mem
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, 2048);
43 SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL
);
45 TRACE("Shared device (%p) message loop start\n", share
);
46 while(WaitForSingleObject(share
->timer_evt
, INFINITE
) == WAIT_OBJECT_0
&& !share
->quit_now
)
48 EnterCriticalSection(&share
->crst
);
49 setALContext(share
->ctx
);
51 for(i
= 0;i
< share
->nprimaries
;++i
)
53 DS8Primary_triggernots(share
->primaries
[i
]);
54 if(!share
->SupportedExt
[SOFTX_MAP_BUFFER
])
55 DS8Primary_streamfeeder(share
->primaries
[i
], scratch_mem
);
59 LeaveCriticalSection(&share
->crst
);
61 TRACE("Shared device (%p) message loop quit\n", share
);
63 HeapFree(GetProcessHeap(), 0, scratch_mem
);
69 TlsSetValue(TlsThreadPtr
, NULL
);
75 static void CALLBACK
DSShare_timer(void *arg
, BOOLEAN unused
)
78 SetEvent((HANDLE
)arg
);
81 static void DSShare_starttimer(DeviceShare
*share
)
85 if(share
->queue_timer
)
88 triggertime
= 1000 / share
->refresh
* 2 / 3;
89 TRACE("Calling timer every %lu ms for %d refreshes per second\n",
90 triggertime
, share
->refresh
);
92 CreateTimerQueueTimer(&share
->queue_timer
, NULL
, DSShare_timer
, share
->timer_evt
,
93 triggertime
, triggertime
, WT_EXECUTEINTIMERTHREAD
);
98 static DeviceShare
**sharelist
;
99 static UINT sharelistsize
;
101 static void DSShare_Destroy(DeviceShare
*share
)
105 EnterCriticalSection(&openal_crst
);
106 for(i
= 0;i
< sharelistsize
;i
++)
108 if(sharelist
[i
] == share
)
110 sharelist
[i
] = sharelist
[--sharelistsize
];
111 if(sharelistsize
== 0)
113 HeapFree(GetProcessHeap(), 0, sharelist
);
119 LeaveCriticalSection(&openal_crst
);
121 if(share
->queue_timer
)
122 DeleteTimerQueueTimer(NULL
, share
->queue_timer
, INVALID_HANDLE_VALUE
);
123 share
->queue_timer
= NULL
;
125 if(share
->thread_hdl
)
127 InterlockedExchange(&share
->quit_now
, TRUE
);
128 SetEvent(share
->timer_evt
);
130 if(WaitForSingleObject(share
->thread_hdl
, 1000) != WAIT_OBJECT_0
)
131 ERR("Thread wait timed out\n");
133 CloseHandle(share
->thread_hdl
);
134 share
->thread_hdl
= NULL
;
138 CloseHandle(share
->timer_evt
);
139 share
->timer_evt
= NULL
;
143 /* Calling setALContext is not appropriate here, since we *have* to
144 * unset the context before destroying it
146 EnterCriticalSection(&openal_crst
);
147 set_context(share
->ctx
);
150 alDeleteSources(share
->nsources
, share
->sources
);
154 share
->ExtAL
.DeleteAuxiliaryEffectSlots(1, &share
->auxslot
);
158 TlsSetValue(TlsThreadPtr
, NULL
);
159 alcDestroyContext(share
->ctx
);
161 LeaveCriticalSection(&openal_crst
);
165 alcCloseDevice(share
->device
);
166 share
->device
= NULL
;
168 DeleteCriticalSection(&share
->crst
);
170 HeapFree(GetProcessHeap(), 0, share
->primaries
);
171 HeapFree(GetProcessHeap(), 0, share
);
174 static HRESULT
DSShare_Create(REFIID guid
, DeviceShare
**out
)
176 OLECHAR
*guid_str
= NULL
;
182 share
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(*share
));
183 if(!share
) return DSERR_OUTOFMEMORY
;
185 share
->refresh
= FAKE_REFRESH_COUNT
;
187 InitializeCriticalSection(&share
->crst
);
189 hr
= StringFromCLSID(guid
, &guid_str
);
192 ERR("Failed to convert GUID to string\n");
195 WideCharToMultiByte(CP_UTF8
, 0, guid_str
, -1, drv_name
, sizeof(drv_name
), NULL
, NULL
);
196 drv_name
[sizeof(drv_name
)-1] = 0;
197 CoTaskMemFree(guid_str
);
201 share
->device
= alcOpenDevice(drv_name
);
205 WARN("Couldn't open device \"%s\"\n", drv_name
);
208 TRACE("Opened device: %s\n",
209 alcIsExtensionPresent(share
->device
, "ALC_ENUMERATE_ALL_EXT") ?
210 alcGetString(share
->device
, ALC_ALL_DEVICES_SPECIFIER
) :
211 alcGetString(share
->device
, ALC_DEVICE_SPECIFIER
));
213 share
->ctx
= alcCreateContext(share
->device
, NULL
);
216 ALCenum err
= alcGetError(share
->device
);
217 ERR("Could not create context (0x%x)!\n", err
);
221 memcpy(&share
->guid
, guid
, sizeof(GUID
));
223 setALContext(share
->ctx
);
224 alcGetIntegerv(share
->device
, ALC_REFRESH
, 1, &share
->refresh
);
225 checkALCError(share
->device
);
227 if(alIsExtensionPresent("AL_EXT_FLOAT32"))
229 TRACE("Found AL_EXT_FLOAT32\n");
230 share
->SupportedExt
[EXT_FLOAT32
] = AL_TRUE
;
232 if(alIsExtensionPresent("AL_EXT_MCFORMATS"))
234 TRACE("Found AL_EXT_MCFORMATS\n");
235 share
->SupportedExt
[EXT_MCFORMATS
] = AL_TRUE
;
237 if(alIsExtensionPresent("AL_SOFT_deferred_updates"))
239 TRACE("Found AL_SOFT_deferred_updates\n");
240 share
->ExtAL
.DeferUpdatesSOFT
= alGetProcAddress("alDeferUpdatesSOFT");
241 share
->ExtAL
.ProcessUpdatesSOFT
= alGetProcAddress("alProcessUpdatesSOFT");
242 share
->SupportedExt
[SOFT_DEFERRED_UPDATES
] = AL_TRUE
;
244 if(alIsExtensionPresent("AL_SOFTX_map_buffer"))
246 TRACE("Found AL_SOFTX_map_buffer\n");
247 share
->ExtAL
.BufferStorageSOFT
= alGetProcAddress("alBufferStorageSOFT");
248 share
->ExtAL
.MapBufferSOFT
= alGetProcAddress("alMapBufferSOFT");
249 share
->ExtAL
.UnmapBufferSOFT
= alGetProcAddress("alUnmapBufferSOFT");
250 share
->ExtAL
.FlushMappedBufferSOFT
= alGetProcAddress("alFlushMappedBufferSOFT");
251 share
->SupportedExt
[SOFTX_MAP_BUFFER
] = AL_TRUE
;
254 if(alcIsExtensionPresent(share
->device
, "ALC_EXT_EFX"))
256 #define LOAD_FUNC(x) (share->ExtAL.x = alGetProcAddress("al"#x))
257 LOAD_FUNC(GenFilters
);
258 LOAD_FUNC(DeleteFilters
);
262 LOAD_FUNC(GenEffects
);
263 LOAD_FUNC(DeleteEffects
);
267 LOAD_FUNC(GenAuxiliaryEffectSlots
);
268 LOAD_FUNC(DeleteAuxiliaryEffectSlots
);
269 LOAD_FUNC(AuxiliaryEffectSloti
);
271 share
->SupportedExt
[EXT_EFX
] = AL_TRUE
;
273 share
->ExtAL
.GenAuxiliaryEffectSlots(1, &share
->auxslot
);
276 share
->max_sources
= 0;
277 while(share
->max_sources
< MAX_SOURCES
)
279 alGenSources(1, &share
->sources
[share
->max_sources
]);
280 if(alGetError() != AL_NO_ERROR
)
282 share
->max_sources
++;
285 /* As long as we have at least 64 sources, keep it a multiple of 64. */
286 if(share
->max_sources
> 64)
287 share
->max_sources
&= ~63u;
288 share
->nsources
= share
->max_sources
;
291 temp
= HeapReAlloc(GetProcessHeap(), 0, sharelist
, sizeof(*sharelist
)*(sharelistsize
+1));
293 temp
= HeapAlloc(GetProcessHeap(), 0, sizeof(*sharelist
)*(sharelistsize
+1));
297 sharelist
[sharelistsize
++] = share
;
302 share
->quit_now
= FALSE
;
303 share
->timer_evt
= CreateEventA(NULL
, FALSE
, FALSE
, NULL
);
304 if(!share
->timer_evt
) goto fail
;
306 share
->queue_timer
= NULL
;
308 share
->thread_hdl
= CreateThread(NULL
, 0, DSShare_thread
, share
, 0, &share
->thread_id
);
309 if(!share
->thread_hdl
) goto fail
;
311 DSShare_starttimer(share
);
317 DSShare_Destroy(share
);
321 static ULONG
DSShare_AddRef(DeviceShare
*share
)
323 ULONG ref
= InterlockedIncrement(&share
->ref
);
327 static ULONG
DSShare_Release(DeviceShare
*share
)
329 ULONG ref
= InterlockedDecrement(&share
->ref
);
330 if(ref
== 0) DSShare_Destroy(share
);
335 static const IDirectSound8Vtbl DS8_Vtbl
;
336 static const IDirectSoundVtbl DS_Vtbl
;
337 static const IUnknownVtbl DS8_Unknown_Vtbl
;
339 static void DS8Impl_Destroy(DS8Impl
*This
);
340 static HRESULT WINAPI
DS8_QueryInterface(IDirectSound8
*iface
, REFIID riid
, LPVOID
*ppv
);
342 /*******************************************************************************
345 static inline DS8Impl
*impl_from_IUnknown(IUnknown
*iface
)
347 return CONTAINING_RECORD(iface
, DS8Impl
, IUnknown_iface
);
350 static HRESULT WINAPI
DS8Impl_IUnknown_QueryInterface(IUnknown
*iface
, REFIID riid
, void **ppobj
)
352 DS8Impl
*This
= impl_from_IUnknown(iface
);
353 return DS8_QueryInterface(&This
->IDirectSound8_iface
, riid
, ppobj
);
356 static ULONG WINAPI
DS8Impl_IUnknown_AddRef(IUnknown
*iface
)
358 DS8Impl
*This
= impl_from_IUnknown(iface
);
361 InterlockedIncrement(&(This
->ref
));
362 ref
= InterlockedIncrement(&(This
->unkref
));
363 TRACE("(%p) ref was %lu\n", This
, ref
- 1);
368 static ULONG WINAPI
DS8Impl_IUnknown_Release(IUnknown
*iface
)
370 DS8Impl
*This
= impl_from_IUnknown(iface
);
371 ULONG ref
= InterlockedDecrement(&(This
->unkref
));
372 TRACE("(%p) ref was %lu\n", This
, ref
+ 1);
373 if(InterlockedDecrement(&(This
->ref
)) == 0)
374 DS8Impl_Destroy(This
);
378 static const IUnknownVtbl DS8_Unknown_Vtbl
= {
379 DS8Impl_IUnknown_QueryInterface
,
380 DS8Impl_IUnknown_AddRef
,
381 DS8Impl_IUnknown_Release
385 static inline DS8Impl
*impl_from_IDirectSound8(IDirectSound8
*iface
)
387 return CONTAINING_RECORD(iface
, DS8Impl
, IDirectSound8_iface
);
390 static inline DS8Impl
*impl_from_IDirectSound(IDirectSound
*iface
)
392 return CONTAINING_RECORD(iface
, DS8Impl
, IDirectSound_iface
);
395 HRESULT
DSOUND_Create(REFIID riid
, void **ds
)
399 hr
= DSOUND_Create8(&IID_IDirectSound
, ds
);
402 DS8Impl
*impl
= impl_from_IDirectSound(*ds
);
405 if(!IsEqualIID(riid
, &IID_IDirectSound
))
407 hr
= IDirectSound_QueryInterface(&impl
->IDirectSound_iface
, riid
, ds
);
408 IDirectSound_Release(&impl
->IDirectSound_iface
);
415 static const WCHAR speakerconfigkey
[] =
416 L
"SYSTEM\\CurrentControlSet\\Control\\MediaResources\\DirectSound\\Speaker Configuration";
417 static const WCHAR speakerconfig
[] = L
"Speaker Configuration";
419 HRESULT
DSOUND_Create8(REFIID riid
, LPVOID
*ds
)
426 This
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(*This
));
427 if(!This
) return DSERR_OUTOFMEMORY
;
429 This
->IDirectSound8_iface
.lpVtbl
= &DS8_Vtbl
;
430 This
->IDirectSound_iface
.lpVtbl
= &DS_Vtbl
;
431 This
->IUnknown_iface
.lpVtbl
= &DS8_Unknown_Vtbl
;
434 This
->speaker_config
= DSSPEAKER_COMBINED(DSSPEAKER_5POINT1
, DSSPEAKER_GEOMETRY_WIDE
);
436 if(RegOpenKeyExW(HKEY_LOCAL_MACHINE
, speakerconfigkey
, 0, KEY_READ
, ®key
) == ERROR_SUCCESS
)
438 DWORD type
, conf
, confsize
= sizeof(DWORD
);
440 if(RegQueryValueExW(regkey
, speakerconfig
, NULL
, &type
, (BYTE
*)&conf
, &confsize
) == ERROR_SUCCESS
)
442 if(type
== REG_DWORD
)
443 This
->speaker_config
= conf
;
448 /*RegGetValueW(HKEY_LOCAL_MACHINE, speakerconfigkey, speakerconfig, RRF_RT_REG_DWORD, NULL, &This->speaker_config, NULL);*/
450 hr
= IDirectSound8_QueryInterface(&This
->IDirectSound8_iface
, riid
, ds
);
452 DS8Impl_Destroy(This
);
456 static void DS8Impl_Destroy(DS8Impl
*This
)
458 DeviceShare
*share
= This
->share
;
464 EnterCriticalSection(&share
->crst
);
466 for(i
= 0;i
< share
->nprimaries
;++i
)
468 if(share
->primaries
[i
] == &This
->primary
)
470 share
->nprimaries
-= 1;
471 share
->primaries
[i
] = share
->primaries
[share
->nprimaries
];
476 LeaveCriticalSection(&share
->crst
);
479 DS8Primary_Clear(&This
->primary
);
481 DSShare_Release(This
->share
);
484 HeapFree(GetProcessHeap(), 0, This
);
488 static HRESULT WINAPI
DS8_QueryInterface(IDirectSound8
*iface
, REFIID riid
, LPVOID
*ppv
)
490 DS8Impl
*This
= impl_from_IDirectSound8(iface
);
492 TRACE("(%p)->(%s, %p)\n", iface
, debugstr_guid(riid
), ppv
);
495 if(IsEqualIID(riid
, &IID_IUnknown
))
496 *ppv
= &This
->IUnknown_iface
;
497 else if(IsEqualIID(riid
, &IID_IDirectSound8
))
500 *ppv
= &This
->IDirectSound8_iface
;
502 else if(IsEqualIID(riid
, &IID_IDirectSound
))
503 *ppv
= &This
->IDirectSound_iface
;
505 FIXME("Unhandled GUID: %s\n", debugstr_guid(riid
));
509 IUnknown_AddRef((IUnknown
*)*ppv
);
513 return E_NOINTERFACE
;
516 static ULONG WINAPI
DS8_AddRef(IDirectSound8
*iface
)
518 DS8Impl
*This
= impl_from_IDirectSound8(iface
);
521 InterlockedIncrement(&This
->ref
);
522 ref
= InterlockedIncrement(&This
->dsref
);
523 TRACE("Reference count incremented to %ld\n", ref
);
528 static ULONG WINAPI
DS8_Release(IDirectSound8
*iface
)
530 DS8Impl
*This
= impl_from_IDirectSound8(iface
);
533 ref
= InterlockedDecrement(&This
->dsref
);
534 TRACE("Reference count decremented to %ld\n", ref
);
535 if(InterlockedDecrement(&This
->ref
) == 0)
536 DS8Impl_Destroy(This
);
541 static HRESULT WINAPI
DS8_CreateSoundBuffer(IDirectSound8
*iface
, LPCDSBUFFERDESC desc
, LPLPDIRECTSOUNDBUFFER buf
, IUnknown
*pUnkOuter
)
543 DS8Impl
*This
= impl_from_IDirectSound8(iface
);
546 TRACE("(%p)->(%p, %p, %p)\n", iface
, desc
, buf
, pUnkOuter
);
550 WARN("buf is null\n");
551 return DSERR_INVALIDPARAM
;
557 WARN("Aggregation isn't supported\n");
558 return DSERR_NOAGGREGATION
;
560 if(!desc
|| desc
->dwSize
< sizeof(DSBUFFERDESC1
))
562 WARN("Invalid buffer %p/%lu\n", desc
, desc
?desc
->dwSize
:0);
563 return DSERR_INVALIDPARAM
;
568 WARN("Device not initialized\n");
569 return DSERR_UNINITIALIZED
;
572 TRACE("Requested buffer:\n"
575 " BufferBytes = %lu\n",
576 desc
->dwSize
, desc
->dwFlags
, desc
->dwBufferBytes
);
578 if(desc
->dwSize
>= sizeof(DSBUFFERDESC
))
580 if(!(desc
->dwFlags
&DSBCAPS_CTRL3D
))
582 if(!IsEqualGUID(&desc
->guid3DAlgorithm
, &GUID_NULL
))
584 WARN("Invalid 3D algorithm GUID specified for non-3D buffer: %s\n", debugstr_guid(&desc
->guid3DAlgorithm
));
585 return DSERR_INVALIDPARAM
;
589 TRACE("Requested 3D algorithm GUID: %s\n", debugstr_guid(&desc
->guid3DAlgorithm
));
592 /* OpenAL doesn't support playing with 3d and panning at same time.. */
593 if((desc
->dwFlags
&(DSBCAPS_CTRL3D
|DSBCAPS_CTRLPAN
)) == (DSBCAPS_CTRL3D
|DSBCAPS_CTRLPAN
))
599 FIXME("Buffers with 3D and panning control ignore panning\n");
603 WARN("Cannot create buffers with 3D and panning control\n");
604 return DSERR_INVALIDPARAM
;
608 EnterCriticalSection(This
->primary
.crst
);
609 if((desc
->dwFlags
&DSBCAPS_PRIMARYBUFFER
))
611 IDirectSoundBuffer
*prim
= &This
->primary
.IDirectSoundBuffer_iface
;
614 if(IDirectSoundBuffer_AddRef(prim
) == 1)
616 hr
= DS8Primary_Initialize(prim
, &This
->IDirectSound_iface
, desc
);
619 IDirectSoundBuffer_Release(prim
);
629 hr
= DS8Buffer_Create(&dsb
, &This
->primary
, NULL
, FALSE
);
632 hr
= DS8Buffer_Initialize(&dsb
->IDirectSoundBuffer8_iface
, &This
->IDirectSound_iface
, desc
);
634 IDirectSoundBuffer8_Release(&dsb
->IDirectSoundBuffer8_iface
);
637 dsb
->bufferlost
= (This
->prio_level
== DSSCL_WRITEPRIMARY
);
638 *buf
= (IDirectSoundBuffer
*)&dsb
->IDirectSoundBuffer8_iface
;
642 LeaveCriticalSection(This
->primary
.crst
);
644 TRACE("%08lx\n", hr
);
648 static HRESULT WINAPI
DS8_GetCaps(IDirectSound8
*iface
, LPDSCAPS caps
)
650 DS8Impl
*This
= impl_from_IDirectSound8(iface
);
653 TRACE("(%p)->(%p)\n", iface
, caps
);
657 WARN("Device not initialized\n");
658 return DSERR_UNINITIALIZED
;
661 if(!caps
|| caps
->dwSize
< sizeof(*caps
))
663 WARN("Invalid DSCAPS (%p, %lu)\n", caps
, (caps
?caps
->dwSize
:0));
664 return DSERR_INVALIDPARAM
;
667 EnterCriticalSection(&This
->share
->crst
);
670 for(i
= 0;i
< This
->primary
.NumBufferGroups
;i
++)
671 free_bufs
+= POPCNT64(This
->primary
.BufferGroups
[i
].FreeBuffers
);
673 caps
->dwFlags
= DSCAPS_CONTINUOUSRATE
| DSCAPS_CERTIFIED
|
674 DSCAPS_PRIMARY16BIT
| DSCAPS_PRIMARYSTEREO
|
675 DSCAPS_PRIMARY8BIT
| DSCAPS_PRIMARYMONO
|
676 DSCAPS_SECONDARY16BIT
| DSCAPS_SECONDARY8BIT
|
677 DSCAPS_SECONDARYMONO
| DSCAPS_SECONDARYSTEREO
;
678 caps
->dwPrimaryBuffers
= 1;
679 caps
->dwMinSecondarySampleRate
= DSBFREQUENCY_MIN
;
680 caps
->dwMaxSecondarySampleRate
= DSBFREQUENCY_MAX
;
681 caps
->dwMaxHwMixingAllBuffers
=
682 caps
->dwMaxHwMixingStaticBuffers
=
683 caps
->dwMaxHwMixingStreamingBuffers
=
684 caps
->dwMaxHw3DAllBuffers
=
685 caps
->dwMaxHw3DStaticBuffers
=
686 caps
->dwMaxHw3DStreamingBuffers
= This
->share
->max_sources
;
687 caps
->dwFreeHwMixingAllBuffers
=
688 caps
->dwFreeHwMixingStaticBuffers
=
689 caps
->dwFreeHwMixingStreamingBuffers
=
690 caps
->dwFreeHw3DAllBuffers
=
691 caps
->dwFreeHw3DStaticBuffers
=
692 caps
->dwFreeHw3DStreamingBuffers
= free_bufs
;
693 caps
->dwTotalHwMemBytes
=
694 caps
->dwFreeHwMemBytes
= 64 * 1024 * 1024;
695 caps
->dwMaxContigFreeHwMemBytes
= caps
->dwFreeHwMemBytes
;
696 caps
->dwUnlockTransferRateHwBuffers
= 4096;
697 caps
->dwPlayCpuOverheadSwBuffers
= 0;
699 LeaveCriticalSection(&This
->share
->crst
);
703 static HRESULT WINAPI
DS8_DuplicateSoundBuffer(IDirectSound8
*iface
, IDirectSoundBuffer
*in
, IDirectSoundBuffer
**out
)
705 DS8Impl
*This
= impl_from_IDirectSound8(iface
);
710 TRACE("(%p)->(%p, %p)\n", iface
, in
, out
);
714 WARN("Device not initialized\n");
715 return DSERR_UNINITIALIZED
;
720 WARN("Invalid pointer: in = %p, out = %p\n", in
, out
);
721 return DSERR_INVALIDPARAM
;
725 caps
.dwSize
= sizeof(caps
);
726 hr
= IDirectSoundBuffer_GetCaps(in
, &caps
);
727 if(SUCCEEDED(hr
) && (caps
.dwFlags
&DSBCAPS_PRIMARYBUFFER
))
729 WARN("Cannot duplicate buffer %p, which has DSBCAPS_PRIMARYBUFFER\n", in
);
730 hr
= DSERR_INVALIDPARAM
;
732 if(SUCCEEDED(hr
) && (caps
.dwFlags
&DSBCAPS_CTRLFX
))
734 WARN("Cannot duplicate buffer %p, which has DSBCAPS_CTRLFX\n", in
);
735 hr
= DSERR_INVALIDPARAM
;
738 hr
= DS8Buffer_Create(&buf
, &This
->primary
, in
, FALSE
);
741 *out
= (IDirectSoundBuffer
*)&buf
->IDirectSoundBuffer8_iface
;
742 hr
= DS8Buffer_Initialize(&buf
->IDirectSoundBuffer8_iface
, NULL
, NULL
);
746 /* According to MSDN volume isn't copied */
747 if((caps
.dwFlags
&DSBCAPS_CTRLPAN
))
750 if(SUCCEEDED(IDirectSoundBuffer_GetPan(in
, &pan
)))
751 IDirectSoundBuffer_SetPan(*out
, pan
);
753 if((caps
.dwFlags
&DSBCAPS_CTRLFREQUENCY
))
756 if(SUCCEEDED(IDirectSoundBuffer_GetFrequency(in
, &freq
)))
757 IDirectSoundBuffer_SetFrequency(*out
, freq
);
759 if((caps
.dwFlags
&DSBCAPS_CTRL3D
))
761 IDirectSound3DBuffer
*buf3d
;
762 DS3DBUFFER DS3DBuffer
;
765 subhr
= IDirectSound_QueryInterface(in
, &IID_IDirectSound3DBuffer
, (void**)&buf3d
);
768 DS3DBuffer
.dwSize
= sizeof(DS3DBuffer
);
769 subhr
= IDirectSound3DBuffer_GetAllParameters(buf3d
, &DS3DBuffer
);
770 IDirectSound3DBuffer_Release(buf3d
);
773 subhr
= IDirectSoundBuffer_QueryInterface(*out
, &IID_IDirectSound3DBuffer
, (void**)&buf3d
);
776 subhr
= IDirectSound3DBuffer_SetAllParameters(buf3d
, &DS3DBuffer
, DS3D_IMMEDIATE
);
777 IDirectSound3DBuffer_Release(buf3d
);
784 IDirectSoundBuffer_Release(*out
);
791 static HRESULT WINAPI
DS8_SetCooperativeLevel(IDirectSound8
*iface
, HWND hwnd
, DWORD level
)
793 DS8Impl
*This
= impl_from_IDirectSound8(iface
);
796 TRACE("(%p)->(%p, %lu)\n", iface
, hwnd
, level
);
800 WARN("Device not initialized\n");
801 return DSERR_UNINITIALIZED
;
804 if(level
> DSSCL_WRITEPRIMARY
|| level
< DSSCL_NORMAL
)
806 WARN("Invalid coop level: %lu\n", level
);
807 return DSERR_INVALIDPARAM
;
810 EnterCriticalSection(This
->primary
.crst
);
811 if(level
== DSSCL_WRITEPRIMARY
&& (This
->prio_level
!= DSSCL_WRITEPRIMARY
))
813 struct DSBufferGroup
*bufgroup
= This
->primary
.BufferGroups
;
816 if(This
->primary
.write_emu
)
818 ERR("Why was there a write_emu?\n");
820 IDirectSoundBuffer8_Release(This
->primary
.write_emu
);
821 This
->primary
.write_emu
= NULL
;
824 for(i
= 0;i
< This
->primary
.NumBufferGroups
;++i
)
826 DWORD64 usemask
= ~bufgroup
[i
].FreeBuffers
;
829 int idx
= CTZ64(usemask
);
830 DS8Buffer
*buf
= bufgroup
[i
].Buffers
+ idx
;
831 usemask
&= ~(U64(1) << idx
);
833 if(FAILED(IDirectSoundBuffer_GetStatus(&buf
->IDirectSoundBuffer8_iface
, &state
)) ||
834 (state
&DSBSTATUS_PLAYING
))
836 WARN("DSSCL_WRITEPRIMARY set with playing buffers!\n");
837 hr
= DSERR_INVALIDCALL
;
840 /* Mark buffer as lost */
845 if(This
->primary
.flags
)
847 /* Primary has open references.. create write_emu */
851 memset(&desc
, 0, sizeof(desc
));
852 desc
.dwSize
= sizeof(desc
);
853 desc
.dwFlags
= DSBCAPS_LOCHARDWARE
| (This
->primary
.flags
&DSBCAPS_CTRLPAN
);
854 desc
.dwBufferBytes
= This
->primary
.buf_size
;
855 desc
.lpwfxFormat
= &This
->primary
.format
.Format
;
857 hr
= DS8Buffer_Create(&emu
, &This
->primary
, NULL
, TRUE
);
860 This
->primary
.write_emu
= &emu
->IDirectSoundBuffer8_iface
;
861 hr
= IDirectSoundBuffer8_Initialize(This
->primary
.write_emu
, &This
->IDirectSound_iface
, &desc
);
864 IDirectSoundBuffer8_Release(This
->primary
.write_emu
);
865 This
->primary
.write_emu
= NULL
;
870 else if(This
->prio_level
== DSSCL_WRITEPRIMARY
&& level
!= DSSCL_WRITEPRIMARY
)
873 TRACE("Nuking write_emu\n");
874 if(This
->primary
.write_emu
)
875 IDirectSoundBuffer8_Release(This
->primary
.write_emu
);
876 This
->primary
.write_emu
= NULL
;
879 This
->prio_level
= level
;
881 LeaveCriticalSection(This
->primary
.crst
);
886 static HRESULT WINAPI
DS8_Compact(IDirectSound8
*iface
)
888 DS8Impl
*This
= impl_from_IDirectSound8(iface
);
891 TRACE("(%p)->()\n", iface
);
895 WARN("Device not initialized\n");
896 return DSERR_UNINITIALIZED
;
899 EnterCriticalSection(&This
->share
->crst
);
900 if(This
->prio_level
< DSSCL_PRIORITY
)
902 WARN("Coop level not high enough (%lu)\n", This
->prio_level
);
903 hr
= DSERR_PRIOLEVELNEEDED
;
905 LeaveCriticalSection(&This
->share
->crst
);
910 static HRESULT WINAPI
DS8_GetSpeakerConfig(IDirectSound8
*iface
, DWORD
*config
)
912 DS8Impl
*This
= impl_from_IDirectSound8(iface
);
915 TRACE("(%p)->(%p)\n", iface
, config
);
918 return DSERR_INVALIDPARAM
;
923 WARN("Device not initialized\n");
924 return DSERR_UNINITIALIZED
;
927 EnterCriticalSection(&This
->share
->crst
);
928 *config
= This
->speaker_config
;
929 LeaveCriticalSection(&This
->share
->crst
);
934 static HRESULT WINAPI
DS8_SetSpeakerConfig(IDirectSound8
*iface
, DWORD config
)
936 DS8Impl
*This
= impl_from_IDirectSound8(iface
);
941 TRACE("(%p)->(0x%08lx)\n", iface
, config
);
945 WARN("Device not initialized\n");
946 return DSERR_UNINITIALIZED
;
949 geo
= DSSPEAKER_GEOMETRY(config
);
950 speaker
= DSSPEAKER_CONFIG(config
);
952 if(geo
&& (geo
< DSSPEAKER_GEOMETRY_MIN
|| geo
> DSSPEAKER_GEOMETRY_MAX
))
954 WARN("Invalid speaker angle %lu\n", geo
);
955 return DSERR_INVALIDPARAM
;
957 if(speaker
< DSSPEAKER_HEADPHONE
|| speaker
> DSSPEAKER_7POINT1
)
959 WARN("Invalid speaker config %lu\n", speaker
);
960 return DSERR_INVALIDPARAM
;
963 EnterCriticalSection(&This
->share
->crst
);
966 if(!RegCreateKeyExW(HKEY_LOCAL_MACHINE
, speakerconfigkey
, 0, NULL
, 0, KEY_WRITE
, NULL
, &key
, NULL
))
968 RegSetValueExW(key
, speakerconfig
, 0, REG_DWORD
, (const BYTE
*)&config
, sizeof(DWORD
));
969 This
->speaker_config
= config
;
974 LeaveCriticalSection(&This
->share
->crst
);
978 static HRESULT WINAPI
DS8_Initialize(IDirectSound8
*iface
, const GUID
*devguid
)
980 DS8Impl
*This
= impl_from_IDirectSound8(iface
);
985 TRACE("(%p)->(%s)\n", iface
, debugstr_guid(devguid
));
988 return DSERR_NODRIVER
;
992 WARN("Device already initialized\n");
993 return DSERR_ALREADYINITIALIZED
;
996 if(!devguid
|| IsEqualGUID(devguid
, &GUID_NULL
))
997 devguid
= &DSDEVID_DefaultPlayback
;
998 else if(IsEqualGUID(devguid
, &DSDEVID_DefaultCapture
) ||
999 IsEqualGUID(devguid
, &DSDEVID_DefaultVoiceCapture
))
1000 return DSERR_NODRIVER
;
1002 hr
= GetDeviceID(devguid
, &guid
);
1003 if(FAILED(hr
)) return hr
;
1005 EnterCriticalSection(&openal_crst
);
1007 for(n
= 0;n
< sharelistsize
;n
++)
1009 if(IsEqualGUID(&sharelist
[n
]->guid
, &guid
))
1011 TRACE("Matched already open device %p\n", sharelist
[n
]->device
);
1013 This
->share
= sharelist
[n
];
1014 DSShare_AddRef(This
->share
);
1020 hr
= DSShare_Create(&guid
, &This
->share
);
1023 This
->device
= This
->share
->device
;
1024 hr
= DS8Primary_PreInit(&This
->primary
, This
);
1029 DeviceShare
*share
= This
->share
;
1032 EnterCriticalSection(&share
->crst
);
1034 prims
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
,
1035 (share
->nprimaries
+1) * sizeof(*prims
));
1037 hr
= DSERR_OUTOFMEMORY
;
1041 for(i
= 0;i
< share
->nprimaries
;++i
)
1042 prims
[i
] = share
->primaries
[i
];
1043 prims
[i
] = &This
->primary
;
1045 HeapFree(GetProcessHeap(), 0, share
->primaries
);
1046 share
->primaries
= prims
;
1047 share
->nprimaries
+= 1;
1050 LeaveCriticalSection(&share
->crst
);
1056 DSShare_Release(This
->share
);
1060 LeaveCriticalSection(&openal_crst
);
1064 /* I, Maarten Lankhorst, hereby declare this driver certified
1065 * What this means.. ? An extra bit set
1067 static HRESULT WINAPI
DS8_VerifyCertification(IDirectSound8
*iface
, DWORD
*certified
)
1069 DS8Impl
*This
= impl_from_IDirectSound8(iface
);
1071 TRACE("(%p)->(%p)\n", iface
, certified
);
1074 return DSERR_INVALIDPARAM
;
1079 WARN("Device not initialized\n");
1080 return DSERR_UNINITIALIZED
;
1083 *certified
= DS_CERTIFIED
;
1088 static const IDirectSound8Vtbl DS8_Vtbl
= {
1092 DS8_CreateSoundBuffer
,
1094 DS8_DuplicateSoundBuffer
,
1095 DS8_SetCooperativeLevel
,
1097 DS8_GetSpeakerConfig
,
1098 DS8_SetSpeakerConfig
,
1100 DS8_VerifyCertification
1104 static HRESULT WINAPI
DS_QueryInterface(IDirectSound
*iface
, REFIID riid
, LPVOID
*ppv
)
1106 DS8Impl
*This
= impl_from_IDirectSound(iface
);
1107 return DS8_QueryInterface(&This
->IDirectSound8_iface
, riid
, ppv
);
1110 static ULONG WINAPI
DS_AddRef(IDirectSound
*iface
)
1112 DS8Impl
*This
= impl_from_IDirectSound(iface
);
1113 return DS8_AddRef(&This
->IDirectSound8_iface
);
1116 static ULONG WINAPI
DS_Release(IDirectSound
*iface
)
1118 DS8Impl
*This
= impl_from_IDirectSound(iface
);
1119 return DS8_Release(&This
->IDirectSound8_iface
);
1122 static HRESULT WINAPI
DS_CreateSoundBuffer(IDirectSound
*iface
, LPCDSBUFFERDESC desc
, LPLPDIRECTSOUNDBUFFER buf
, IUnknown
*pUnkOuter
)
1124 DS8Impl
*This
= impl_from_IDirectSound(iface
);
1125 return DS8_CreateSoundBuffer(&This
->IDirectSound8_iface
, desc
, buf
, pUnkOuter
);
1128 static HRESULT WINAPI
DS_GetCaps(IDirectSound
*iface
, LPDSCAPS caps
)
1130 DS8Impl
*This
= impl_from_IDirectSound(iface
);
1131 return DS8_GetCaps(&This
->IDirectSound8_iface
, caps
);
1133 static HRESULT WINAPI
DS_DuplicateSoundBuffer(IDirectSound
*iface
, IDirectSoundBuffer
*in
, IDirectSoundBuffer
**out
)
1135 DS8Impl
*This
= impl_from_IDirectSound(iface
);
1136 return DS8_DuplicateSoundBuffer(&This
->IDirectSound8_iface
, in
, out
);
1139 static HRESULT WINAPI
DS_SetCooperativeLevel(IDirectSound
*iface
, HWND hwnd
, DWORD level
)
1141 DS8Impl
*This
= impl_from_IDirectSound(iface
);
1142 return DS8_SetCooperativeLevel(&This
->IDirectSound8_iface
, hwnd
, level
);
1145 static HRESULT WINAPI
DS_Compact(IDirectSound
*iface
)
1147 DS8Impl
*This
= impl_from_IDirectSound(iface
);
1148 return DS8_Compact(&This
->IDirectSound8_iface
);
1151 static HRESULT WINAPI
DS_GetSpeakerConfig(IDirectSound
*iface
, DWORD
*config
)
1153 DS8Impl
*This
= impl_from_IDirectSound(iface
);
1154 return DS8_GetSpeakerConfig(&This
->IDirectSound8_iface
, config
);
1157 static HRESULT WINAPI
DS_SetSpeakerConfig(IDirectSound
*iface
, DWORD config
)
1159 DS8Impl
*This
= impl_from_IDirectSound(iface
);
1160 return DS8_SetSpeakerConfig(&This
->IDirectSound8_iface
, config
);
1163 static HRESULT WINAPI
DS_Initialize(IDirectSound
*iface
, const GUID
*devguid
)
1165 DS8Impl
*This
= impl_from_IDirectSound(iface
);
1166 return DS8_Initialize(&This
->IDirectSound8_iface
, devguid
);
1169 static const IDirectSoundVtbl DS_Vtbl
= {
1173 DS_CreateSoundBuffer
,
1175 DS_DuplicateSoundBuffer
,
1176 DS_SetCooperativeLevel
,
1178 DS_GetSpeakerConfig
,
1179 DS_SetSpeakerConfig
,