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
31 #include "dsound_private.h"
33 #ifndef DSSPEAKER_7POINT1
34 #define DSSPEAKER_7POINT1 7
38 static DWORD CALLBACK
DSShare_thread(void *dwUser
)
40 DeviceShare
*share
= (DeviceShare
*)dwUser
;
41 BYTE
*scratch_mem
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, 2048);
44 SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL
);
46 TRACE("Shared device (%p) message loop start\n", share
);
47 while(WaitForSingleObject(share
->timer_evt
, INFINITE
) == WAIT_OBJECT_0
&& !share
->quit_now
)
49 EnterCriticalSection(&share
->crst
);
50 setALContext(share
->ctx
);
52 for(i
= 0;i
< share
->nprimaries
;++i
)
54 DS8Primary_triggernots(share
->primaries
[i
]);
55 if(!BITFIELD_TEST(share
->Exts
, SOFTX_MAP_BUFFER
))
56 DS8Primary_streamfeeder(share
->primaries
[i
], scratch_mem
);
60 LeaveCriticalSection(&share
->crst
);
62 TRACE("Shared device (%p) message loop quit\n", share
);
64 HeapFree(GetProcessHeap(), 0, scratch_mem
);
70 TlsSetValue(TlsThreadPtr
, NULL
);
76 static void CALLBACK
DSShare_timer(void *arg
, BOOLEAN unused
)
79 SetEvent((HANDLE
)arg
);
82 static void DSShare_starttimer(DeviceShare
*share
)
86 if(share
->queue_timer
)
89 triggertime
= 1000 / share
->refresh
* 2 / 3;
90 TRACE("Calling timer every %lu ms for %d refreshes per second\n",
91 triggertime
, share
->refresh
);
93 CreateTimerQueueTimer(&share
->queue_timer
, NULL
, DSShare_timer
, share
->timer_evt
,
94 triggertime
, triggertime
, WT_EXECUTEINTIMERTHREAD
);
99 static DeviceShare
**sharelist
;
100 static UINT sharelistsize
;
102 static void DSShare_Destroy(DeviceShare
*share
)
106 EnterCriticalSection(&openal_crst
);
107 for(i
= 0;i
< sharelistsize
;i
++)
109 if(sharelist
[i
] == share
)
111 sharelist
[i
] = sharelist
[--sharelistsize
];
112 if(sharelistsize
== 0)
114 HeapFree(GetProcessHeap(), 0, sharelist
);
120 LeaveCriticalSection(&openal_crst
);
122 if(share
->queue_timer
)
123 DeleteTimerQueueTimer(NULL
, share
->queue_timer
, INVALID_HANDLE_VALUE
);
124 share
->queue_timer
= NULL
;
126 if(share
->thread_hdl
)
128 InterlockedExchange(&share
->quit_now
, TRUE
);
129 SetEvent(share
->timer_evt
);
131 if(WaitForSingleObject(share
->thread_hdl
, 1000) != WAIT_OBJECT_0
)
132 ERR("Thread wait timed out\n");
134 CloseHandle(share
->thread_hdl
);
135 share
->thread_hdl
= NULL
;
139 CloseHandle(share
->timer_evt
);
140 share
->timer_evt
= NULL
;
144 /* Calling setALContext is not appropriate here, since we *have* to
145 * unset the context before destroying it
147 EnterCriticalSection(&openal_crst
);
148 set_context(share
->ctx
);
150 if(share
->sources
.max_alloc
)
151 alDeleteSources(share
->sources
.max_alloc
, share
->sources
.ids
);
152 share
->sources
.max_alloc
= 0;
155 alDeleteAuxiliaryEffectSlots(1, &share
->auxslot
);
159 TlsSetValue(TlsThreadPtr
, NULL
);
160 alcDestroyContext(share
->ctx
);
162 LeaveCriticalSection(&openal_crst
);
166 alcCloseDevice(share
->device
);
167 share
->device
= NULL
;
169 DeleteCriticalSection(&share
->crst
);
171 HeapFree(GetProcessHeap(), 0, share
->primaries
);
172 HeapFree(GetProcessHeap(), 0, share
);
175 static HRESULT
DSShare_Create(REFIID guid
, DeviceShare
**out
)
177 static const struct {
178 const char extname
[64];
180 } extensions
[MAX_EXTENSIONS
] = {
181 { "ALC_EXT_EFX", EXT_EFX
},
182 { "AL_EXT_FLOAT32", EXT_FLOAT32
},
183 { "AL_EXT_MCFORMATS", EXT_MCFORMATS
},
184 { "AL_SOFT_deferred_updates", SOFT_DEFERRED_UPDATES
},
185 { "AL_SOFT_source_spatialize", SOFT_SOURCE_SPATIALIZE
},
186 { "AL_SOFTX_map_buffer", SOFTX_MAP_BUFFER
},
188 OLECHAR
*guid_str
= NULL
;
196 share
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(*share
));
197 if(!share
) return DSERR_OUTOFMEMORY
;
199 share
->refresh
= FAKE_REFRESH_COUNT
;
201 share
->speaker_config
= DSSPEAKER_7POINT1_SURROUND
;
203 hr
= get_mmdevice(eRender
, guid
, &mmdev
);
206 IPropertyStore
*store
;
208 hr
= IMMDevice_OpenPropertyStore(mmdev
, STGM_READ
, &store
);
210 WARN("IMMDevice_OpenPropertyStore failed: %08lx\n", hr
);
213 ULONG phys_speakers
= 0;
216 PropVariantInit(&pv
);
218 hr
= IPropertyStore_GetValue(store
, &PKEY_AudioEndpoint_PhysicalSpeakers
, &pv
);
220 WARN("IPropertyStore_GetValue failed: %08lx\n", hr
);
224 WARN("PKEY_AudioEndpoint_PhysicalSpeakers is not a ULONG: 0x%04x\n", pv
.vt
);
227 phys_speakers
= pv
.ulVal
;
229 #define BIT_MATCH(v, b) (((v)&(b)) == (b))
230 if(BIT_MATCH(phys_speakers
, KSAUDIO_SPEAKER_7POINT1
))
231 share
->speaker_config
= DSSPEAKER_7POINT1
;
232 else if(BIT_MATCH(phys_speakers
, KSAUDIO_SPEAKER_7POINT1_SURROUND
))
233 share
->speaker_config
= DSSPEAKER_7POINT1_SURROUND
;
234 else if(BIT_MATCH(phys_speakers
, KSAUDIO_SPEAKER_5POINT1
))
235 share
->speaker_config
= DSSPEAKER_5POINT1_BACK
;
236 else if(BIT_MATCH(phys_speakers
, KSAUDIO_SPEAKER_5POINT1_SURROUND
))
237 share
->speaker_config
= DSSPEAKER_5POINT1_SURROUND
;
238 else if(BIT_MATCH(phys_speakers
, KSAUDIO_SPEAKER_QUAD
))
239 share
->speaker_config
= DSSPEAKER_QUAD
;
240 else if(BIT_MATCH(phys_speakers
, KSAUDIO_SPEAKER_STEREO
))
241 share
->speaker_config
= DSSPEAKER_COMBINED(DSSPEAKER_STEREO
, DSSPEAKER_GEOMETRY_WIDE
);
242 else if(BIT_MATCH(phys_speakers
, KSAUDIO_SPEAKER_MONO
))
243 share
->speaker_config
= DSSPEAKER_MONO
;
245 FIXME("Unhandled physical speaker layout: 0x%08lx\n", phys_speakers
);
250 /* If the device has a stereo layout, check the formfactor to see
251 * if it's really headphones/headset.
253 if(DSSPEAKER_CONFIG(share
->speaker_config
) == DSSPEAKER_STEREO
)
255 hr
= IPropertyStore_GetValue(store
, &PKEY_AudioEndpoint_FormFactor
, &pv
);
257 WARN("IPropertyStore_GetValue failed: %08lx\n", hr
);
261 WARN("PKEY_AudioEndpoint_FormFactor is not a ULONG: 0x%04x\n", pv
.vt
);
264 if(pv
.ulVal
== Headphones
|| pv
.ulVal
== Headset
)
265 share
->speaker_config
= DSSPEAKER_HEADPHONE
;
270 PropVariantClear(&pv
);
271 IPropertyStore_Release(store
);
274 IMMDevice_Release(mmdev
);
278 InitializeCriticalSection(&share
->crst
);
280 hr
= StringFromCLSID(guid
, &guid_str
);
283 ERR("Failed to convert GUID to string\n");
286 WideCharToMultiByte(CP_UTF8
, 0, guid_str
, -1, drv_name
, sizeof(drv_name
), NULL
, NULL
);
287 drv_name
[sizeof(drv_name
)-1] = 0;
288 CoTaskMemFree(guid_str
);
292 share
->device
= alcOpenDevice(drv_name
);
296 WARN("Couldn't open device \"%s\"\n", drv_name
);
299 TRACE("Opened device: %s\n",
300 alcIsExtensionPresent(share
->device
, "ALC_ENUMERATE_ALL_EXT") ?
301 alcGetString(share
->device
, ALC_ALL_DEVICES_SPECIFIER
) :
302 alcGetString(share
->device
, ALC_DEVICE_SPECIFIER
));
304 share
->ctx
= alcCreateContext(share
->device
, NULL
);
307 ALCenum err
= alcGetError(share
->device
);
308 ERR("Could not create context (0x%x)!\n", err
);
312 memcpy(&share
->guid
, guid
, sizeof(GUID
));
314 setALContext(share
->ctx
);
315 alcGetIntegerv(share
->device
, ALC_REFRESH
, 1, &share
->refresh
);
316 checkALCError(share
->device
);
318 for(i
= 0;i
< MAX_EXTENSIONS
;i
++)
320 if((strncmp(extensions
[i
].extname
, "ALC", 3) == 0) ?
321 alcIsExtensionPresent(share
->device
, extensions
[i
].extname
) :
322 alIsExtensionPresent(extensions
[i
].extname
))
324 TRACE("Found %s\n", extensions
[i
].extname
);
325 BITFIELD_SET(share
->Exts
, extensions
[i
].extenum
);
329 if(BITFIELD_TEST(share
->Exts
, EXT_EFX
))
330 alGenAuxiliaryEffectSlots(1, &share
->auxslot
);
332 share
->sources
.max_alloc
= 0;
333 while(share
->sources
.max_alloc
< MAX_SOURCES
)
335 alGenSources(1, &share
->sources
.ids
[share
->sources
.max_alloc
]);
336 if(alGetError() != AL_NO_ERROR
)
338 share
->sources
.max_alloc
++;
341 /* As long as we have at least 64 sources, keep it a multiple of 64. */
342 if(share
->sources
.max_alloc
> 64)
343 share
->sources
.max_alloc
&= ~63u;
344 share
->sources
.avail_num
= share
->sources
.max_alloc
;
347 temp
= HeapReAlloc(GetProcessHeap(), 0, sharelist
, sizeof(*sharelist
)*(sharelistsize
+1));
349 temp
= HeapAlloc(GetProcessHeap(), 0, sizeof(*sharelist
)*(sharelistsize
+1));
353 sharelist
[sharelistsize
++] = share
;
358 share
->quit_now
= FALSE
;
359 share
->timer_evt
= CreateEventA(NULL
, FALSE
, FALSE
, NULL
);
360 if(!share
->timer_evt
) goto fail
;
362 share
->queue_timer
= NULL
;
364 share
->thread_hdl
= CreateThread(NULL
, 0, DSShare_thread
, share
, 0, &share
->thread_id
);
365 if(!share
->thread_hdl
) goto fail
;
367 DSShare_starttimer(share
);
373 DSShare_Destroy(share
);
377 static ULONG
DSShare_AddRef(DeviceShare
*share
)
379 ULONG ref
= InterlockedIncrement(&share
->ref
);
383 static ULONG
DSShare_Release(DeviceShare
*share
)
385 ULONG ref
= InterlockedDecrement(&share
->ref
);
386 if(ref
== 0) DSShare_Destroy(share
);
391 static const IDirectSound8Vtbl DS8_Vtbl
;
392 static const IDirectSoundVtbl DS_Vtbl
;
393 static const IUnknownVtbl DS8_Unknown_Vtbl
;
395 static void DS8Impl_Destroy(DS8Impl
*This
);
396 static HRESULT WINAPI
DS8_QueryInterface(IDirectSound8
*iface
, REFIID riid
, LPVOID
*ppv
);
398 /*******************************************************************************
401 static inline DS8Impl
*impl_from_IUnknown(IUnknown
*iface
)
403 return CONTAINING_RECORD(iface
, DS8Impl
, IUnknown_iface
);
406 static HRESULT WINAPI
DS8Impl_IUnknown_QueryInterface(IUnknown
*iface
, REFIID riid
, void **ppobj
)
408 DS8Impl
*This
= impl_from_IUnknown(iface
);
409 return DS8_QueryInterface(&This
->IDirectSound8_iface
, riid
, ppobj
);
412 static ULONG WINAPI
DS8Impl_IUnknown_AddRef(IUnknown
*iface
)
414 DS8Impl
*This
= impl_from_IUnknown(iface
);
417 InterlockedIncrement(&(This
->ref
));
418 ref
= InterlockedIncrement(&(This
->unkref
));
419 TRACE("(%p) ref was %lu\n", This
, ref
- 1);
424 static ULONG WINAPI
DS8Impl_IUnknown_Release(IUnknown
*iface
)
426 DS8Impl
*This
= impl_from_IUnknown(iface
);
427 ULONG ref
= InterlockedDecrement(&(This
->unkref
));
428 TRACE("(%p) ref was %lu\n", This
, ref
+ 1);
429 if(InterlockedDecrement(&(This
->ref
)) == 0)
430 DS8Impl_Destroy(This
);
434 static const IUnknownVtbl DS8_Unknown_Vtbl
= {
435 DS8Impl_IUnknown_QueryInterface
,
436 DS8Impl_IUnknown_AddRef
,
437 DS8Impl_IUnknown_Release
441 static inline DS8Impl
*impl_from_IDirectSound8(IDirectSound8
*iface
)
443 return CONTAINING_RECORD(iface
, DS8Impl
, IDirectSound8_iface
);
446 static inline DS8Impl
*impl_from_IDirectSound(IDirectSound
*iface
)
448 return CONTAINING_RECORD(iface
, DS8Impl
, IDirectSound_iface
);
451 HRESULT
DSOUND_Create(REFIID riid
, void **ds
)
455 hr
= DSOUND_Create8(&IID_IDirectSound
, ds
);
458 DS8Impl
*impl
= impl_from_IDirectSound(*ds
);
461 if(!IsEqualIID(riid
, &IID_IDirectSound
))
463 hr
= IDirectSound_QueryInterface(&impl
->IDirectSound_iface
, riid
, ds
);
464 IDirectSound_Release(&impl
->IDirectSound_iface
);
471 HRESULT
DSOUND_Create8(REFIID riid
, LPVOID
*ds
)
477 This
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(*This
));
478 if(!This
) return DSERR_OUTOFMEMORY
;
480 This
->IDirectSound8_iface
.lpVtbl
= &DS8_Vtbl
;
481 This
->IDirectSound_iface
.lpVtbl
= &DS_Vtbl
;
482 This
->IUnknown_iface
.lpVtbl
= &DS8_Unknown_Vtbl
;
486 hr
= IDirectSound8_QueryInterface(&This
->IDirectSound8_iface
, riid
, ds
);
487 if(FAILED(hr
)) DS8Impl_Destroy(This
);
491 static void DS8Impl_Destroy(DS8Impl
*This
)
493 DeviceShare
*share
= This
->share
;
499 EnterCriticalSection(&share
->crst
);
501 for(i
= 0;i
< share
->nprimaries
;++i
)
503 if(share
->primaries
[i
] == &This
->primary
)
505 share
->nprimaries
-= 1;
506 share
->primaries
[i
] = share
->primaries
[share
->nprimaries
];
511 LeaveCriticalSection(&share
->crst
);
514 DS8Primary_Clear(&This
->primary
);
516 DSShare_Release(This
->share
);
519 HeapFree(GetProcessHeap(), 0, This
);
523 static HRESULT WINAPI
DS8_QueryInterface(IDirectSound8
*iface
, REFIID riid
, LPVOID
*ppv
)
525 DS8Impl
*This
= impl_from_IDirectSound8(iface
);
527 TRACE("(%p)->(%s, %p)\n", iface
, debugstr_guid(riid
), ppv
);
530 if(IsEqualIID(riid
, &IID_IUnknown
))
531 *ppv
= &This
->IUnknown_iface
;
532 else if(IsEqualIID(riid
, &IID_IDirectSound8
))
535 *ppv
= &This
->IDirectSound8_iface
;
537 else if(IsEqualIID(riid
, &IID_IDirectSound
))
538 *ppv
= &This
->IDirectSound_iface
;
540 FIXME("Unhandled GUID: %s\n", debugstr_guid(riid
));
544 IUnknown_AddRef((IUnknown
*)*ppv
);
548 return E_NOINTERFACE
;
551 static ULONG WINAPI
DS8_AddRef(IDirectSound8
*iface
)
553 DS8Impl
*This
= impl_from_IDirectSound8(iface
);
556 InterlockedIncrement(&This
->ref
);
557 ref
= InterlockedIncrement(&This
->dsref
);
558 TRACE("Reference count incremented to %ld\n", ref
);
563 static ULONG WINAPI
DS8_Release(IDirectSound8
*iface
)
565 DS8Impl
*This
= impl_from_IDirectSound8(iface
);
568 ref
= InterlockedDecrement(&This
->dsref
);
569 TRACE("Reference count decremented to %ld\n", ref
);
570 if(InterlockedDecrement(&This
->ref
) == 0)
571 DS8Impl_Destroy(This
);
576 static HRESULT WINAPI
DS8_CreateSoundBuffer(IDirectSound8
*iface
, LPCDSBUFFERDESC desc
, LPLPDIRECTSOUNDBUFFER buf
, IUnknown
*pUnkOuter
)
578 DS8Impl
*This
= impl_from_IDirectSound8(iface
);
581 TRACE("(%p)->(%p, %p, %p)\n", iface
, desc
, buf
, pUnkOuter
);
585 WARN("buf is null\n");
586 return DSERR_INVALIDPARAM
;
592 WARN("Aggregation isn't supported\n");
593 return DSERR_NOAGGREGATION
;
595 if(!desc
|| desc
->dwSize
< sizeof(DSBUFFERDESC1
))
597 WARN("Invalid buffer %p/%lu\n", desc
, desc
?desc
->dwSize
:0);
598 return DSERR_INVALIDPARAM
;
603 WARN("Device not initialized\n");
604 return DSERR_UNINITIALIZED
;
607 TRACE("Requested buffer:\n"
610 " BufferBytes = %lu\n",
611 desc
->dwSize
, desc
->dwFlags
, desc
->dwBufferBytes
);
613 if(desc
->dwSize
>= sizeof(DSBUFFERDESC
))
615 if(!(desc
->dwFlags
&DSBCAPS_CTRL3D
))
617 if(!IsEqualGUID(&desc
->guid3DAlgorithm
, &GUID_NULL
))
619 WARN("Invalid 3D algorithm GUID specified for non-3D buffer: %s\n", debugstr_guid(&desc
->guid3DAlgorithm
));
620 return DSERR_INVALIDPARAM
;
624 TRACE("Requested 3D algorithm GUID: %s\n", debugstr_guid(&desc
->guid3DAlgorithm
));
627 /* OpenAL doesn't support playing with 3d and panning at same time.. */
628 if((desc
->dwFlags
&(DSBCAPS_CTRL3D
|DSBCAPS_CTRLPAN
)) == (DSBCAPS_CTRL3D
|DSBCAPS_CTRLPAN
))
634 FIXME("Buffers with 3D and panning control ignore panning\n");
638 WARN("Cannot create buffers with 3D and panning control\n");
639 return DSERR_INVALIDPARAM
;
643 EnterCriticalSection(&This
->share
->crst
);
644 if((desc
->dwFlags
&DSBCAPS_PRIMARYBUFFER
))
646 IDirectSoundBuffer
*prim
= &This
->primary
.IDirectSoundBuffer_iface
;
649 if(IDirectSoundBuffer_AddRef(prim
) == 1)
651 hr
= DS8Primary_Initialize(prim
, &This
->IDirectSound_iface
, desc
);
654 IDirectSoundBuffer_Release(prim
);
664 hr
= DS8Buffer_Create(&dsb
, &This
->primary
, NULL
, FALSE
);
667 hr
= DS8Buffer_Initialize(&dsb
->IDirectSoundBuffer8_iface
, &This
->IDirectSound_iface
, desc
);
669 IDirectSoundBuffer8_Release(&dsb
->IDirectSoundBuffer8_iface
);
672 dsb
->bufferlost
= (This
->prio_level
== DSSCL_WRITEPRIMARY
);
673 *buf
= (IDirectSoundBuffer
*)&dsb
->IDirectSoundBuffer8_iface
;
677 LeaveCriticalSection(&This
->share
->crst
);
679 TRACE("%08lx\n", hr
);
683 static HRESULT WINAPI
DS8_GetCaps(IDirectSound8
*iface
, LPDSCAPS caps
)
685 DS8Impl
*This
= impl_from_IDirectSound8(iface
);
688 TRACE("(%p)->(%p)\n", iface
, caps
);
692 WARN("Device not initialized\n");
693 return DSERR_UNINITIALIZED
;
696 if(!caps
|| caps
->dwSize
< sizeof(*caps
))
698 WARN("Invalid DSCAPS (%p, %lu)\n", caps
, (caps
?caps
->dwSize
:0));
699 return DSERR_INVALIDPARAM
;
702 EnterCriticalSection(&This
->share
->crst
);
705 for(i
= 0;i
< This
->primary
.NumBufferGroups
;i
++)
706 free_bufs
+= POPCNT64(This
->primary
.BufferGroups
[i
].FreeBuffers
);
708 caps
->dwFlags
= DSCAPS_CONTINUOUSRATE
| DSCAPS_CERTIFIED
|
709 DSCAPS_PRIMARY16BIT
| DSCAPS_PRIMARYSTEREO
|
710 DSCAPS_PRIMARY8BIT
| DSCAPS_PRIMARYMONO
|
711 DSCAPS_SECONDARY16BIT
| DSCAPS_SECONDARY8BIT
|
712 DSCAPS_SECONDARYMONO
| DSCAPS_SECONDARYSTEREO
;
713 caps
->dwPrimaryBuffers
= 1;
714 caps
->dwMinSecondarySampleRate
= DSBFREQUENCY_MIN
;
715 caps
->dwMaxSecondarySampleRate
= DSBFREQUENCY_MAX
;
716 caps
->dwMaxHwMixingAllBuffers
=
717 caps
->dwMaxHwMixingStaticBuffers
=
718 caps
->dwMaxHwMixingStreamingBuffers
=
719 caps
->dwMaxHw3DAllBuffers
=
720 caps
->dwMaxHw3DStaticBuffers
=
721 caps
->dwMaxHw3DStreamingBuffers
= This
->share
->sources
.max_alloc
;
722 caps
->dwFreeHwMixingAllBuffers
=
723 caps
->dwFreeHwMixingStaticBuffers
=
724 caps
->dwFreeHwMixingStreamingBuffers
=
725 caps
->dwFreeHw3DAllBuffers
=
726 caps
->dwFreeHw3DStaticBuffers
=
727 caps
->dwFreeHw3DStreamingBuffers
= free_bufs
;
728 caps
->dwTotalHwMemBytes
=
729 caps
->dwFreeHwMemBytes
= 64 * 1024 * 1024;
730 caps
->dwMaxContigFreeHwMemBytes
= caps
->dwFreeHwMemBytes
;
731 caps
->dwUnlockTransferRateHwBuffers
= 4096;
732 caps
->dwPlayCpuOverheadSwBuffers
= 0;
734 LeaveCriticalSection(&This
->share
->crst
);
738 static HRESULT WINAPI
DS8_DuplicateSoundBuffer(IDirectSound8
*iface
, IDirectSoundBuffer
*in
, IDirectSoundBuffer
**out
)
740 DS8Impl
*This
= impl_from_IDirectSound8(iface
);
745 TRACE("(%p)->(%p, %p)\n", iface
, in
, out
);
749 WARN("Device not initialized\n");
750 return DSERR_UNINITIALIZED
;
755 WARN("Invalid pointer: in = %p, out = %p\n", in
, out
);
756 return DSERR_INVALIDPARAM
;
760 caps
.dwSize
= sizeof(caps
);
761 hr
= IDirectSoundBuffer_GetCaps(in
, &caps
);
762 if(SUCCEEDED(hr
) && (caps
.dwFlags
&DSBCAPS_PRIMARYBUFFER
))
764 WARN("Cannot duplicate buffer %p, which has DSBCAPS_PRIMARYBUFFER\n", in
);
765 hr
= DSERR_INVALIDPARAM
;
767 if(SUCCEEDED(hr
) && (caps
.dwFlags
&DSBCAPS_CTRLFX
))
769 WARN("Cannot duplicate buffer %p, which has DSBCAPS_CTRLFX\n", in
);
770 hr
= DSERR_INVALIDPARAM
;
773 hr
= DS8Buffer_Create(&buf
, &This
->primary
, in
, FALSE
);
776 *out
= (IDirectSoundBuffer
*)&buf
->IDirectSoundBuffer8_iface
;
777 hr
= DS8Buffer_Initialize(&buf
->IDirectSoundBuffer8_iface
, NULL
, NULL
);
781 /* According to MSDN volume isn't copied */
782 if((caps
.dwFlags
&DSBCAPS_CTRLPAN
))
785 if(SUCCEEDED(IDirectSoundBuffer_GetPan(in
, &pan
)))
786 IDirectSoundBuffer_SetPan(*out
, pan
);
788 if((caps
.dwFlags
&DSBCAPS_CTRLFREQUENCY
))
791 if(SUCCEEDED(IDirectSoundBuffer_GetFrequency(in
, &freq
)))
792 IDirectSoundBuffer_SetFrequency(*out
, freq
);
794 if((caps
.dwFlags
&DSBCAPS_CTRL3D
))
796 IDirectSound3DBuffer
*buf3d
;
797 DS3DBUFFER DS3DBuffer
;
800 subhr
= IDirectSound_QueryInterface(in
, &IID_IDirectSound3DBuffer
, (void**)&buf3d
);
803 DS3DBuffer
.dwSize
= sizeof(DS3DBuffer
);
804 subhr
= IDirectSound3DBuffer_GetAllParameters(buf3d
, &DS3DBuffer
);
805 IDirectSound3DBuffer_Release(buf3d
);
808 subhr
= IDirectSoundBuffer_QueryInterface(*out
, &IID_IDirectSound3DBuffer
, (void**)&buf3d
);
811 subhr
= IDirectSound3DBuffer_SetAllParameters(buf3d
, &DS3DBuffer
, DS3D_IMMEDIATE
);
812 IDirectSound3DBuffer_Release(buf3d
);
819 IDirectSoundBuffer_Release(*out
);
826 static HRESULT WINAPI
DS8_SetCooperativeLevel(IDirectSound8
*iface
, HWND hwnd
, DWORD level
)
828 DS8Impl
*This
= impl_from_IDirectSound8(iface
);
831 TRACE("(%p)->(%p, %lu)\n", iface
, hwnd
, level
);
835 WARN("Device not initialized\n");
836 return DSERR_UNINITIALIZED
;
839 if(level
> DSSCL_WRITEPRIMARY
|| level
< DSSCL_NORMAL
)
841 WARN("Invalid coop level: %lu\n", level
);
842 return DSERR_INVALIDPARAM
;
845 EnterCriticalSection(&This
->share
->crst
);
846 if(level
== DSSCL_WRITEPRIMARY
&& (This
->prio_level
!= DSSCL_WRITEPRIMARY
))
848 struct DSBufferGroup
*bufgroup
= This
->primary
.BufferGroups
;
851 if(This
->primary
.write_emu
)
853 ERR("Why was there a write_emu?\n");
855 IDirectSoundBuffer8_Release(This
->primary
.write_emu
);
856 This
->primary
.write_emu
= NULL
;
859 for(i
= 0;i
< This
->primary
.NumBufferGroups
;++i
)
861 DWORD64 usemask
= ~bufgroup
[i
].FreeBuffers
;
864 int idx
= CTZ64(usemask
);
865 DS8Buffer
*buf
= bufgroup
[i
].Buffers
+ idx
;
866 usemask
&= ~(U64(1) << idx
);
868 if(FAILED(IDirectSoundBuffer_GetStatus(&buf
->IDirectSoundBuffer8_iface
, &state
)) ||
869 (state
&DSBSTATUS_PLAYING
))
871 WARN("DSSCL_WRITEPRIMARY set with playing buffers!\n");
872 hr
= DSERR_INVALIDCALL
;
875 /* Mark buffer as lost */
880 if(This
->primary
.flags
)
882 /* Primary has open references.. create write_emu */
886 memset(&desc
, 0, sizeof(desc
));
887 desc
.dwSize
= sizeof(desc
);
888 desc
.dwFlags
= DSBCAPS_LOCHARDWARE
| (This
->primary
.flags
&DSBCAPS_CTRLPAN
);
889 desc
.dwBufferBytes
= This
->primary
.buf_size
;
890 desc
.lpwfxFormat
= &This
->primary
.format
.Format
;
892 hr
= DS8Buffer_Create(&emu
, &This
->primary
, NULL
, TRUE
);
895 This
->primary
.write_emu
= &emu
->IDirectSoundBuffer8_iface
;
896 hr
= IDirectSoundBuffer8_Initialize(This
->primary
.write_emu
, &This
->IDirectSound_iface
, &desc
);
899 IDirectSoundBuffer8_Release(This
->primary
.write_emu
);
900 This
->primary
.write_emu
= NULL
;
905 else if(This
->prio_level
== DSSCL_WRITEPRIMARY
&& level
!= DSSCL_WRITEPRIMARY
)
908 TRACE("Nuking write_emu\n");
909 if(This
->primary
.write_emu
)
910 IDirectSoundBuffer8_Release(This
->primary
.write_emu
);
911 This
->primary
.write_emu
= NULL
;
914 This
->prio_level
= level
;
916 LeaveCriticalSection(&This
->share
->crst
);
921 static HRESULT WINAPI
DS8_Compact(IDirectSound8
*iface
)
923 DS8Impl
*This
= impl_from_IDirectSound8(iface
);
926 TRACE("(%p)->()\n", iface
);
930 WARN("Device not initialized\n");
931 return DSERR_UNINITIALIZED
;
934 EnterCriticalSection(&This
->share
->crst
);
935 if(This
->prio_level
< DSSCL_PRIORITY
)
937 WARN("Coop level not high enough (%lu)\n", This
->prio_level
);
938 hr
= DSERR_PRIOLEVELNEEDED
;
940 LeaveCriticalSection(&This
->share
->crst
);
945 static HRESULT WINAPI
DS8_GetSpeakerConfig(IDirectSound8
*iface
, DWORD
*config
)
947 DS8Impl
*This
= impl_from_IDirectSound8(iface
);
949 TRACE("(%p)->(%p)\n", iface
, config
);
952 return DSERR_INVALIDPARAM
;
957 WARN("Device not initialized\n");
958 return DSERR_UNINITIALIZED
;
961 *config
= This
->share
->speaker_config
;
966 static HRESULT WINAPI
DS8_SetSpeakerConfig(IDirectSound8
*iface
, DWORD config
)
968 DS8Impl
*This
= impl_from_IDirectSound8(iface
);
971 TRACE("(%p)->(0x%08lx)\n", iface
, config
);
975 WARN("Device not initialized\n");
976 return DSERR_UNINITIALIZED
;
979 geo
= DSSPEAKER_GEOMETRY(config
);
980 speaker
= DSSPEAKER_CONFIG(config
);
982 if(geo
&& (geo
< DSSPEAKER_GEOMETRY_MIN
|| geo
> DSSPEAKER_GEOMETRY_MAX
))
984 WARN("Invalid speaker angle %lu\n", geo
);
985 return DSERR_INVALIDPARAM
;
987 if(speaker
< DSSPEAKER_HEADPHONE
|| speaker
> DSSPEAKER_7POINT1
)
989 WARN("Invalid speaker config %lu\n", speaker
);
990 return DSERR_INVALIDPARAM
;
993 /* No-op on Vista+. */
997 static HRESULT WINAPI
DS8_Initialize(IDirectSound8
*iface
, const GUID
*devguid
)
999 DS8Impl
*This
= impl_from_IDirectSound8(iface
);
1004 TRACE("(%p)->(%s)\n", iface
, debugstr_guid(devguid
));
1007 return DSERR_NODRIVER
;
1011 WARN("Device already initialized\n");
1012 return DSERR_ALREADYINITIALIZED
;
1015 if(!devguid
|| IsEqualGUID(devguid
, &GUID_NULL
))
1016 devguid
= &DSDEVID_DefaultPlayback
;
1017 else if(IsEqualGUID(devguid
, &DSDEVID_DefaultCapture
) ||
1018 IsEqualGUID(devguid
, &DSDEVID_DefaultVoiceCapture
))
1019 return DSERR_NODRIVER
;
1021 hr
= GetDeviceID(devguid
, &guid
);
1022 if(FAILED(hr
)) return hr
;
1024 EnterCriticalSection(&openal_crst
);
1026 for(n
= 0;n
< sharelistsize
;n
++)
1028 if(IsEqualGUID(&sharelist
[n
]->guid
, &guid
))
1030 TRACE("Matched already open device %p\n", sharelist
[n
]->device
);
1032 This
->share
= sharelist
[n
];
1033 DSShare_AddRef(This
->share
);
1039 hr
= DSShare_Create(&guid
, &This
->share
);
1042 This
->device
= This
->share
->device
;
1043 hr
= DS8Primary_PreInit(&This
->primary
, This
);
1048 DeviceShare
*share
= This
->share
;
1051 EnterCriticalSection(&share
->crst
);
1053 prims
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
,
1054 (share
->nprimaries
+1) * sizeof(*prims
));
1056 hr
= DSERR_OUTOFMEMORY
;
1060 for(i
= 0;i
< share
->nprimaries
;++i
)
1061 prims
[i
] = share
->primaries
[i
];
1062 prims
[i
] = &This
->primary
;
1064 HeapFree(GetProcessHeap(), 0, share
->primaries
);
1065 share
->primaries
= prims
;
1066 share
->nprimaries
+= 1;
1069 LeaveCriticalSection(&share
->crst
);
1075 DSShare_Release(This
->share
);
1079 LeaveCriticalSection(&openal_crst
);
1083 /* I, Maarten Lankhorst, hereby declare this driver certified
1084 * What this means.. ? An extra bit set
1086 static HRESULT WINAPI
DS8_VerifyCertification(IDirectSound8
*iface
, DWORD
*certified
)
1088 DS8Impl
*This
= impl_from_IDirectSound8(iface
);
1090 TRACE("(%p)->(%p)\n", iface
, certified
);
1093 return DSERR_INVALIDPARAM
;
1098 WARN("Device not initialized\n");
1099 return DSERR_UNINITIALIZED
;
1102 *certified
= DS_CERTIFIED
;
1107 static const IDirectSound8Vtbl DS8_Vtbl
= {
1111 DS8_CreateSoundBuffer
,
1113 DS8_DuplicateSoundBuffer
,
1114 DS8_SetCooperativeLevel
,
1116 DS8_GetSpeakerConfig
,
1117 DS8_SetSpeakerConfig
,
1119 DS8_VerifyCertification
1123 static HRESULT WINAPI
DS_QueryInterface(IDirectSound
*iface
, REFIID riid
, LPVOID
*ppv
)
1125 DS8Impl
*This
= impl_from_IDirectSound(iface
);
1126 return DS8_QueryInterface(&This
->IDirectSound8_iface
, riid
, ppv
);
1129 static ULONG WINAPI
DS_AddRef(IDirectSound
*iface
)
1131 DS8Impl
*This
= impl_from_IDirectSound(iface
);
1132 return DS8_AddRef(&This
->IDirectSound8_iface
);
1135 static ULONG WINAPI
DS_Release(IDirectSound
*iface
)
1137 DS8Impl
*This
= impl_from_IDirectSound(iface
);
1138 return DS8_Release(&This
->IDirectSound8_iface
);
1141 static HRESULT WINAPI
DS_CreateSoundBuffer(IDirectSound
*iface
, LPCDSBUFFERDESC desc
, LPLPDIRECTSOUNDBUFFER buf
, IUnknown
*pUnkOuter
)
1143 DS8Impl
*This
= impl_from_IDirectSound(iface
);
1144 return DS8_CreateSoundBuffer(&This
->IDirectSound8_iface
, desc
, buf
, pUnkOuter
);
1147 static HRESULT WINAPI
DS_GetCaps(IDirectSound
*iface
, LPDSCAPS caps
)
1149 DS8Impl
*This
= impl_from_IDirectSound(iface
);
1150 return DS8_GetCaps(&This
->IDirectSound8_iface
, caps
);
1152 static HRESULT WINAPI
DS_DuplicateSoundBuffer(IDirectSound
*iface
, IDirectSoundBuffer
*in
, IDirectSoundBuffer
**out
)
1154 DS8Impl
*This
= impl_from_IDirectSound(iface
);
1155 return DS8_DuplicateSoundBuffer(&This
->IDirectSound8_iface
, in
, out
);
1158 static HRESULT WINAPI
DS_SetCooperativeLevel(IDirectSound
*iface
, HWND hwnd
, DWORD level
)
1160 DS8Impl
*This
= impl_from_IDirectSound(iface
);
1161 return DS8_SetCooperativeLevel(&This
->IDirectSound8_iface
, hwnd
, level
);
1164 static HRESULT WINAPI
DS_Compact(IDirectSound
*iface
)
1166 DS8Impl
*This
= impl_from_IDirectSound(iface
);
1167 return DS8_Compact(&This
->IDirectSound8_iface
);
1170 static HRESULT WINAPI
DS_GetSpeakerConfig(IDirectSound
*iface
, DWORD
*config
)
1172 DS8Impl
*This
= impl_from_IDirectSound(iface
);
1173 return DS8_GetSpeakerConfig(&This
->IDirectSound8_iface
, config
);
1176 static HRESULT WINAPI
DS_SetSpeakerConfig(IDirectSound
*iface
, DWORD config
)
1178 DS8Impl
*This
= impl_from_IDirectSound(iface
);
1179 return DS8_SetSpeakerConfig(&This
->IDirectSound8_iface
, config
);
1182 static HRESULT WINAPI
DS_Initialize(IDirectSound
*iface
, const GUID
*devguid
)
1184 DS8Impl
*This
= impl_from_IDirectSound(iface
);
1185 return DS8_Initialize(&This
->IDirectSound8_iface
, devguid
);
1188 static const IDirectSoundVtbl DS_Vtbl
= {
1192 DS_CreateSoundBuffer
,
1194 DS_DuplicateSoundBuffer
,
1195 DS_SetCooperativeLevel
,
1197 DS_GetSpeakerConfig
,
1198 DS_SetSpeakerConfig
,