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
32 #include "dsound_private.h"
34 #ifndef DSSPEAKER_7POINT1
35 #define DSSPEAKER_7POINT1 7
39 static DWORD CALLBACK
DSShare_thread(void *dwUser
)
41 DeviceShare
*share
= (DeviceShare
*)dwUser
;
42 BYTE
*scratch_mem
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, 2048);
45 SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL
);
47 TRACE("Shared device (%p) message loop start\n", share
);
48 while(WaitForSingleObject(share
->timer_evt
, INFINITE
) == WAIT_OBJECT_0
&& !share
->quit_now
)
50 EnterCriticalSection(&share
->crst
);
51 setALContext(share
->ctx
);
53 for(i
= 0;i
< share
->nprimaries
;++i
)
55 DSPrimary_triggernots(share
->primaries
[i
]);
56 if(!HAS_EXTENSION(share
, SOFTX_MAP_BUFFER
))
57 DSPrimary_streamfeeder(share
->primaries
[i
], scratch_mem
);
61 LeaveCriticalSection(&share
->crst
);
63 TRACE("Shared device (%p) message loop quit\n", share
);
65 HeapFree(GetProcessHeap(), 0, scratch_mem
);
71 TlsSetValue(TlsThreadPtr
, NULL
);
77 static void CALLBACK
DSShare_timer(void *arg
, BOOLEAN unused
)
80 SetEvent((HANDLE
)arg
);
83 static void DSShare_starttimer(DeviceShare
*share
)
87 if(share
->queue_timer
)
90 triggertime
= 1000 / share
->refresh
* 2 / 3;
91 TRACE("Calling timer every %lu ms for %d refreshes per second\n",
92 triggertime
, share
->refresh
);
94 CreateTimerQueueTimer(&share
->queue_timer
, NULL
, DSShare_timer
, share
->timer_evt
,
95 triggertime
, triggertime
, WT_EXECUTEINTIMERTHREAD
);
100 static DeviceShare
**sharelist
;
101 static UINT sharelistsize
;
103 static void DSShare_Destroy(DeviceShare
*share
)
107 EnterCriticalSection(&openal_crst
);
108 for(i
= 0;i
< sharelistsize
;i
++)
110 if(sharelist
[i
] == share
)
112 sharelist
[i
] = sharelist
[--sharelistsize
];
113 if(sharelistsize
== 0)
115 HeapFree(GetProcessHeap(), 0, sharelist
);
121 LeaveCriticalSection(&openal_crst
);
123 if(share
->queue_timer
)
124 DeleteTimerQueueTimer(NULL
, share
->queue_timer
, INVALID_HANDLE_VALUE
);
125 share
->queue_timer
= NULL
;
127 if(share
->thread_hdl
)
129 InterlockedExchange(&share
->quit_now
, TRUE
);
130 SetEvent(share
->timer_evt
);
132 if(WaitForSingleObject(share
->thread_hdl
, 1000) != WAIT_OBJECT_0
)
133 ERR("Thread wait timed out\n");
135 CloseHandle(share
->thread_hdl
);
136 share
->thread_hdl
= NULL
;
140 CloseHandle(share
->timer_evt
);
141 share
->timer_evt
= NULL
;
145 /* Calling setALContext is not appropriate here, since we *have* to
146 * unset the context before destroying it
148 EnterCriticalSection(&openal_crst
);
149 set_context(share
->ctx
);
151 share
->sources
.maxhw_alloc
= share
->sources
.maxsw_alloc
= 0;
154 TlsSetValue(TlsThreadPtr
, NULL
);
155 alcDestroyContext(share
->ctx
);
157 LeaveCriticalSection(&openal_crst
);
161 alcCloseDevice(share
->device
);
162 share
->device
= NULL
;
164 DeleteCriticalSection(&share
->crst
);
166 HeapFree(GetProcessHeap(), 0, share
->primaries
);
167 HeapFree(GetProcessHeap(), 0, share
);
169 TRACE("Closed shared device %p\n", share
);
172 static HRESULT
DSShare_Create(REFIID guid
, DeviceShare
**out
)
174 static const struct {
175 const char extname
[64];
177 } extensions
[MAX_EXTENSIONS
] = {
178 { "EAX5.0", EXT_EAX
},
179 { "AL_EXT_FLOAT32", EXT_FLOAT32
},
180 { "AL_EXT_MCFORMATS", EXT_MCFORMATS
},
181 { "AL_SOFT_deferred_updates", SOFT_DEFERRED_UPDATES
},
182 { "AL_SOFT_source_spatialize", SOFT_SOURCE_SPATIALIZE
},
183 { "AL_SOFTX_map_buffer", SOFTX_MAP_BUFFER
},
185 OLECHAR
*guid_str
= NULL
;
194 share
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(*share
));
195 if(!share
) return DSERR_OUTOFMEMORY
;
197 share
->refresh
= FAKE_REFRESH_COUNT
;
198 share
->speaker_config
= DSSPEAKER_7POINT1_SURROUND
;
199 share
->vm_managermode
= DSPROPERTY_VMANAGER_MODE_DEFAULT
;
201 TRACE("Creating shared device %p\n", share
);
203 cohr
= get_mmdevice(eRender
, guid
, &mmdev
);
205 hr
= DSERR_INVALIDPARAM
;
208 IPropertyStore
*store
;
210 hr
= IMMDevice_OpenPropertyStore(mmdev
, STGM_READ
, &store
);
212 WARN("IMMDevice_OpenPropertyStore failed: %08lx\n", hr
);
215 ULONG phys_speakers
= 0;
218 PropVariantInit(&pv
);
220 hr
= IPropertyStore_GetValue(store
, &PKEY_AudioEndpoint_PhysicalSpeakers
, &pv
);
222 WARN("IPropertyStore_GetValue failed: %08lx\n", hr
);
223 else if(pv
.vt
!= VT_UI4
)
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
);
249 /* If the device has a stereo layout, check the formfactor to see
250 * if it's really headphones/headset.
252 if(DSSPEAKER_CONFIG(share
->speaker_config
) == DSSPEAKER_STEREO
)
254 hr
= IPropertyStore_GetValue(store
, &PKEY_AudioEndpoint_FormFactor
, &pv
);
256 WARN("IPropertyStore_GetValue failed: %08lx\n", hr
);
257 else if(pv
.vt
!= VT_UI4
)
258 WARN("PKEY_AudioEndpoint_FormFactor is not a ULONG: 0x%04x\n", pv
.vt
);
261 if(pv
.ulVal
== Headphones
|| pv
.ulVal
== Headset
)
262 share
->speaker_config
= DSSPEAKER_HEADPHONE
;
266 TRACE("Got speaker config %d:%d from physical speakers 0x%08lx\n",
267 DSSPEAKER_GEOMETRY(share
->speaker_config
),
268 DSSPEAKER_CONFIG(share
->speaker_config
), phys_speakers
);
270 PropVariantClear(&pv
);
271 IPropertyStore_Release(store
);
274 release_mmdevice(mmdev
, cohr
);
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 AL 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
));
305 attrs
[i
++] = ALC_MONO_SOURCES
;
306 attrs
[i
++] = MAX_SOURCES
;
307 attrs
[i
++] = ALC_STEREO_SOURCES
;
310 share
->ctx
= alcCreateContext(share
->device
, attrs
);
313 ALCenum err
= alcGetError(share
->device
);
314 ERR("Could not create context (0x%x)!\n", err
);
320 setALContext(share
->ctx
);
321 alcGetIntegerv(share
->device
, ALC_REFRESH
, 1, &share
->refresh
);
322 checkALCError(share
->device
);
324 for(i
= 0;i
< MAX_EXTENSIONS
;i
++)
326 if((strncmp(extensions
[i
].extname
, "ALC", 3) == 0) ?
327 alcIsExtensionPresent(share
->device
, extensions
[i
].extname
) :
328 alIsExtensionPresent(extensions
[i
].extname
))
330 TRACE("Found %s\n", extensions
[i
].extname
);
331 BITFIELD_SET(share
->Exts
, extensions
[i
].extenum
);
335 alcGetIntegerv(share
->device
, ALC_MONO_SOURCES
, 1, &attrs
[0]);
336 alcGetIntegerv(share
->device
, ALC_STEREO_SOURCES
, 1, &attrs
[1]);
337 checkALCError(share
->device
);
338 share
->sources
.maxhw_alloc
= (DWORD
)attrs
[0] + (DWORD
)attrs
[1];
342 if(share
->sources
.maxhw_alloc
> MAX_SOURCES
)
343 share
->sources
.maxhw_alloc
= MAX_SOURCES
;
344 else if(share
->sources
.maxhw_alloc
< 128)
346 ERR("Could only allocate %lu sources (minimum 128 required)\n",
347 share
->sources
.maxhw_alloc
);
351 if(share
->sources
.maxhw_alloc
> MAX_HWBUFFERS
)
353 share
->sources
.maxsw_alloc
= share
->sources
.maxhw_alloc
- MAX_HWBUFFERS
;
354 share
->sources
.maxhw_alloc
= MAX_HWBUFFERS
;
356 else if(share
->sources
.maxhw_alloc
> MAX_HWBUFFERS
/2)
358 share
->sources
.maxsw_alloc
= share
->sources
.maxhw_alloc
- MAX_HWBUFFERS
/2;
359 share
->sources
.maxhw_alloc
= MAX_HWBUFFERS
/2;
363 share
->sources
.maxsw_alloc
= share
->sources
.maxhw_alloc
- MAX_HWBUFFERS
/4;
364 share
->sources
.maxhw_alloc
= MAX_HWBUFFERS
/4;
366 share
->sources
.availhw_num
= share
->sources
.maxhw_alloc
;
367 share
->sources
.availsw_num
= share
->sources
.maxsw_alloc
;
368 TRACE("Allocated %lu hardware sources and %lu software sources\n",
369 share
->sources
.maxhw_alloc
, share
->sources
.maxsw_alloc
);
372 temp
= HeapReAlloc(GetProcessHeap(), 0, sharelist
, sizeof(*sharelist
)*(sharelistsize
+1));
374 temp
= HeapAlloc(GetProcessHeap(), 0, sizeof(*sharelist
)*(sharelistsize
+1));
378 sharelist
[sharelistsize
++] = share
;
383 share
->quit_now
= FALSE
;
384 share
->timer_evt
= CreateEventA(NULL
, FALSE
, FALSE
, NULL
);
385 if(!share
->timer_evt
) goto fail
;
387 share
->queue_timer
= NULL
;
389 share
->thread_hdl
= CreateThread(NULL
, 0, DSShare_thread
, share
, 0, &share
->thread_id
);
390 if(!share
->thread_hdl
) goto fail
;
392 DSShare_starttimer(share
);
398 DSShare_Destroy(share
);
402 static ULONG
DSShare_AddRef(DeviceShare
*share
)
404 ULONG ref
= InterlockedIncrement(&share
->ref
);
408 static ULONG
DSShare_Release(DeviceShare
*share
)
410 ULONG ref
= InterlockedDecrement(&share
->ref
);
411 if(ref
== 0) DSShare_Destroy(share
);
416 static IDirectSound8Vtbl DS8_Vtbl
;
417 static IDirectSoundVtbl DS_Vtbl
;
418 static IUnknownVtbl DS8_Unknown_Vtbl
;
420 static HRESULT
DSDevice_Create(BOOL is8
, REFIID riid
, LPVOID
*ds
);
421 static void DSDevice_Destroy(DSDevice
*This
);
422 static HRESULT
DSDevice_GetInterface(DSDevice
*This
, REFIID riid
, LPVOID
*ppv
);
424 /*******************************************************************************
427 static inline DSDevice
*impl_from_IUnknown(IUnknown
*iface
)
429 return CONTAINING_RECORD(iface
, DSDevice
, IUnknown_iface
);
432 static HRESULT WINAPI
DSDevice_IUnknown_QueryInterface(IUnknown
*iface
, REFIID riid
, void **ppobj
)
434 DSDevice
*This
= impl_from_IUnknown(iface
);
435 TRACE("(%p)->(%s, %p)\n", iface
, debugstr_guid(riid
), ppobj
);
436 return DSDevice_GetInterface(This
, riid
, ppobj
);
439 static ULONG WINAPI
DSDevice_IUnknown_AddRef(IUnknown
*iface
)
441 DSDevice
*This
= impl_from_IUnknown(iface
);
444 InterlockedIncrement(&(This
->ref
));
445 ref
= InterlockedIncrement(&(This
->unkref
));
446 TRACE("(%p) ref %lu\n", iface
, ref
);
451 static ULONG WINAPI
DSDevice_IUnknown_Release(IUnknown
*iface
)
453 DSDevice
*This
= impl_from_IUnknown(iface
);
454 ULONG ref
= InterlockedDecrement(&(This
->unkref
));
455 TRACE("(%p) ref %lu\n", iface
, ref
);
456 if(InterlockedDecrement(&(This
->ref
)) == 0)
457 DSDevice_Destroy(This
);
461 static IUnknownVtbl DS8_Unknown_Vtbl
= {
462 DSDevice_IUnknown_QueryInterface
,
463 DSDevice_IUnknown_AddRef
,
464 DSDevice_IUnknown_Release
468 static inline DSDevice
*impl_from_IDirectSound8(IDirectSound8
*iface
)
470 return CONTAINING_RECORD(iface
, DSDevice
, IDirectSound8_iface
);
473 static inline DSDevice
*impl_from_IDirectSound(IDirectSound
*iface
)
475 return CONTAINING_RECORD(iface
, DSDevice
, IDirectSound_iface
);
479 HRESULT
DSOUND_Create(REFIID riid
, void **ds
)
480 { return DSDevice_Create(FALSE
, riid
, ds
); }
482 HRESULT
DSOUND_Create8(REFIID riid
, LPVOID
*ds
)
483 { return DSDevice_Create(TRUE
, riid
, ds
); }
485 static HRESULT
DSDevice_Create(BOOL is8
, REFIID riid
, LPVOID
*ds
)
491 This
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(*This
));
492 if(!This
) return DSERR_OUTOFMEMORY
;
494 TRACE("Creating device instance %p\n", This
);
495 This
->IDirectSound8_iface
.lpVtbl
= &DS8_Vtbl
;
496 This
->IDirectSound_iface
.lpVtbl
= &DS_Vtbl
;
497 This
->IUnknown_iface
.lpVtbl
= &DS8_Unknown_Vtbl
;
501 hr
= DSDevice_GetInterface(This
, riid
, ds
);
502 if(FAILED(hr
)) DSDevice_Destroy(This
);
506 static void DSDevice_Destroy(DSDevice
*This
)
508 DeviceShare
*share
= This
->share
;
510 TRACE("Destroying device instance %p\n", This
);
515 EnterCriticalSection(&share
->crst
);
517 for(i
= 0;i
< share
->nprimaries
;++i
)
519 if(share
->primaries
[i
] == &This
->primary
)
521 share
->nprimaries
-= 1;
522 share
->primaries
[i
] = share
->primaries
[share
->nprimaries
];
527 LeaveCriticalSection(&share
->crst
);
530 DSPrimary_Clear(&This
->primary
);
532 DSShare_Release(This
->share
);
535 HeapFree(GetProcessHeap(), 0, This
);
538 static HRESULT
DSDevice_GetInterface(DSDevice
*This
, REFIID riid
, LPVOID
*ppv
)
541 if(IsEqualIID(riid
, &IID_IUnknown
))
542 *ppv
= &This
->IUnknown_iface
;
543 else if(IsEqualIID(riid
, &IID_IDirectSound8
))
546 *ppv
= &This
->IDirectSound8_iface
;
548 else if(IsEqualIID(riid
, &IID_IDirectSound
))
549 *ppv
= &This
->IDirectSound_iface
;
551 FIXME("Unhandled GUID: %s\n", debugstr_guid(riid
));
555 IUnknown_AddRef((IUnknown
*)*ppv
);
559 return E_NOINTERFACE
;
563 static HRESULT WINAPI
DS8_QueryInterface(IDirectSound8
*iface
, REFIID riid
, LPVOID
*ppv
)
565 DSDevice
*This
= impl_from_IDirectSound8(iface
);
566 TRACE("(%p)->(%s, %p)\n", iface
, debugstr_guid(riid
), ppv
);
567 return DSDevice_GetInterface(This
, riid
, ppv
);
570 static ULONG WINAPI
DS8_AddRef(IDirectSound8
*iface
)
572 DSDevice
*This
= impl_from_IDirectSound8(iface
);
575 InterlockedIncrement(&This
->ref
);
576 ref
= InterlockedIncrement(&This
->dsref
);
577 TRACE("(%p) ref %lu\n", iface
, ref
);
582 static ULONG WINAPI
DS8_Release(IDirectSound8
*iface
)
584 DSDevice
*This
= impl_from_IDirectSound8(iface
);
587 ref
= InterlockedDecrement(&This
->dsref
);
588 TRACE("(%p) ref %lu\n", iface
, ref
);
589 if(InterlockedDecrement(&This
->ref
) == 0)
590 DSDevice_Destroy(This
);
595 static HRESULT WINAPI
DS8_CreateSoundBuffer(IDirectSound8
*iface
, LPCDSBUFFERDESC desc
, LPLPDIRECTSOUNDBUFFER buf
, IUnknown
*pUnkOuter
)
597 DSDevice
*This
= impl_from_IDirectSound8(iface
);
600 TRACE("(%p)->(%p, %p, %p)\n", iface
, desc
, buf
, pUnkOuter
);
604 WARN("buf is null\n");
605 return DSERR_INVALIDPARAM
;
611 WARN("Aggregation isn't supported\n");
612 return DSERR_NOAGGREGATION
;
614 if(!desc
|| desc
->dwSize
< sizeof(DSBUFFERDESC1
))
616 WARN("Invalid buffer %p/%lu\n", desc
, desc
?desc
->dwSize
:0);
617 return DSERR_INVALIDPARAM
;
622 WARN("Device not initialized\n");
623 return DSERR_UNINITIALIZED
;
626 TRACE("Requested buffer:\n"
629 " BufferBytes = %lu\n",
630 desc
->dwSize
, desc
->dwFlags
, desc
->dwBufferBytes
);
632 if(desc
->dwSize
>= sizeof(DSBUFFERDESC
))
634 if(!(desc
->dwFlags
&DSBCAPS_CTRL3D
))
636 if(!IsEqualGUID(&desc
->guid3DAlgorithm
, &GUID_NULL
))
638 /* Not fatal. Some apps pass unknown values here. */
639 WARN("Unknown 3D algorithm GUID specified for non-3D buffer: %s\n",
640 debugstr_guid(&desc
->guid3DAlgorithm
));
644 TRACE("Requested 3D algorithm GUID: %s\n", debugstr_guid(&desc
->guid3DAlgorithm
));
647 /* OpenAL doesn't support playing with 3d and panning at same time.. */
648 if((desc
->dwFlags
&(DSBCAPS_CTRL3D
|DSBCAPS_CTRLPAN
)) == (DSBCAPS_CTRL3D
|DSBCAPS_CTRLPAN
))
654 FIXME("Buffers with 3D and panning control ignore panning\n");
658 WARN("Cannot create buffers with 3D and panning control\n");
659 return DSERR_INVALIDPARAM
;
663 EnterCriticalSection(&This
->share
->crst
);
664 if((desc
->dwFlags
&DSBCAPS_PRIMARYBUFFER
))
666 IDirectSoundBuffer
*prim
= &This
->primary
.IDirectSoundBuffer_iface
;
669 if(IDirectSoundBuffer_AddRef(prim
) == 1)
671 hr
= DSPrimary_Initialize(prim
, &This
->IDirectSound_iface
, desc
);
674 IDirectSoundBuffer_Release(prim
);
684 hr
= DSBuffer_Create(&dsb
, &This
->primary
, NULL
);
687 hr
= DSBuffer_Initialize(&dsb
->IDirectSoundBuffer8_iface
, &This
->IDirectSound_iface
, desc
);
690 dsb
->bufferlost
= (This
->prio_level
== DSSCL_WRITEPRIMARY
);
691 hr
= DSBuffer_GetInterface(dsb
, &IID_IDirectSoundBuffer
, (void**)buf
);
694 DSBuffer_Destroy(dsb
);
697 LeaveCriticalSection(&This
->share
->crst
);
699 TRACE("%08lx\n", hr
);
703 static HRESULT WINAPI
DS8_GetCaps(IDirectSound8
*iface
, LPDSCAPS caps
)
705 DSDevice
*This
= impl_from_IDirectSound8(iface
);
706 struct DSBufferGroup
*bufgroup
, *endgroup
;
709 TRACE("(%p)->(%p)\n", iface
, caps
);
713 WARN("Device not initialized\n");
714 return DSERR_UNINITIALIZED
;
717 if(!caps
|| caps
->dwSize
< sizeof(*caps
))
719 WARN("Invalid DSCAPS (%p, %lu)\n", caps
, (caps
?caps
->dwSize
:0));
720 return DSERR_INVALIDPARAM
;
723 EnterCriticalSection(&This
->share
->crst
);
725 free_bufs
= This
->share
->sources
.maxhw_alloc
;
726 bufgroup
= This
->primary
.BufferGroups
;
727 endgroup
= bufgroup
+ This
->primary
.NumBufferGroups
;
728 for(;free_bufs
&& bufgroup
!= endgroup
;++bufgroup
)
730 DWORD64 usemask
= ~bufgroup
->FreeBuffers
;
733 int idx
= CTZ64(usemask
);
734 DSBuffer
*buf
= bufgroup
->Buffers
+ idx
;
735 usemask
&= ~(U64(1) << idx
);
737 if(buf
->loc_status
== DSBSTATUS_LOCHARDWARE
)
745 caps
->dwFlags
= DSCAPS_CONTINUOUSRATE
| DSCAPS_CERTIFIED
|
746 DSCAPS_PRIMARY16BIT
| DSCAPS_PRIMARYSTEREO
|
747 DSCAPS_PRIMARY8BIT
| DSCAPS_PRIMARYMONO
|
748 DSCAPS_SECONDARY16BIT
| DSCAPS_SECONDARY8BIT
|
749 DSCAPS_SECONDARYMONO
| DSCAPS_SECONDARYSTEREO
;
750 caps
->dwPrimaryBuffers
= 1;
751 caps
->dwMinSecondarySampleRate
= DSBFREQUENCY_MIN
;
752 caps
->dwMaxSecondarySampleRate
= DSBFREQUENCY_MAX
;
753 caps
->dwMaxHwMixingAllBuffers
=
754 caps
->dwMaxHwMixingStaticBuffers
=
755 caps
->dwMaxHwMixingStreamingBuffers
=
756 caps
->dwMaxHw3DAllBuffers
=
757 caps
->dwMaxHw3DStaticBuffers
=
758 caps
->dwMaxHw3DStreamingBuffers
= This
->share
->sources
.maxhw_alloc
;
759 caps
->dwFreeHwMixingAllBuffers
=
760 caps
->dwFreeHwMixingStaticBuffers
=
761 caps
->dwFreeHwMixingStreamingBuffers
=
762 caps
->dwFreeHw3DAllBuffers
=
763 caps
->dwFreeHw3DStaticBuffers
=
764 caps
->dwFreeHw3DStreamingBuffers
= free_bufs
;
765 caps
->dwTotalHwMemBytes
=
766 caps
->dwFreeHwMemBytes
= 64 * 1024 * 1024;
767 caps
->dwMaxContigFreeHwMemBytes
= caps
->dwFreeHwMemBytes
;
768 caps
->dwUnlockTransferRateHwBuffers
= 4096;
769 caps
->dwPlayCpuOverheadSwBuffers
= 0;
771 LeaveCriticalSection(&This
->share
->crst
);
775 static HRESULT WINAPI
DS8_DuplicateSoundBuffer(IDirectSound8
*iface
, IDirectSoundBuffer
*in
, IDirectSoundBuffer
**out
)
777 DSDevice
*This
= impl_from_IDirectSound8(iface
);
778 DSBuffer
*buf
= NULL
;
782 TRACE("(%p)->(%p, %p)\n", iface
, in
, out
);
786 WARN("Device not initialized\n");
787 return DSERR_UNINITIALIZED
;
792 WARN("Invalid pointer: in = %p, out = %p\n", in
, out
);
793 return DSERR_INVALIDPARAM
;
797 caps
.dwSize
= sizeof(caps
);
798 hr
= IDirectSoundBuffer_GetCaps(in
, &caps
);
799 if(SUCCEEDED(hr
) && (caps
.dwFlags
&DSBCAPS_PRIMARYBUFFER
))
801 WARN("Cannot duplicate buffer %p, which has DSBCAPS_PRIMARYBUFFER\n", in
);
802 hr
= DSERR_INVALIDPARAM
;
804 if(SUCCEEDED(hr
) && (caps
.dwFlags
&DSBCAPS_CTRLFX
))
806 WARN("Cannot duplicate buffer %p, which has DSBCAPS_CTRLFX\n", in
);
807 hr
= DSERR_INVALIDPARAM
;
810 hr
= DSBuffer_Create(&buf
, &This
->primary
, in
);
813 hr
= DSBuffer_Initialize(&buf
->IDirectSoundBuffer8_iface
, NULL
, NULL
);
815 hr
= DSBuffer_GetInterface(buf
, &IID_IDirectSoundBuffer
, (void**)out
);
817 DSBuffer_Destroy(buf
);
823 static HRESULT WINAPI
DS8_SetCooperativeLevel(IDirectSound8
*iface
, HWND hwnd
, DWORD level
)
825 DSDevice
*This
= impl_from_IDirectSound8(iface
);
828 TRACE("(%p)->(%p, %lu)\n", iface
, hwnd
, level
);
832 WARN("Device not initialized\n");
833 return DSERR_UNINITIALIZED
;
836 if(level
> DSSCL_WRITEPRIMARY
|| level
< DSSCL_NORMAL
)
838 WARN("Invalid coop level: %lu\n", level
);
839 return DSERR_INVALIDPARAM
;
842 EnterCriticalSection(&This
->share
->crst
);
843 if(level
== DSSCL_WRITEPRIMARY
&& (This
->prio_level
!= DSSCL_WRITEPRIMARY
))
845 struct DSBufferGroup
*bufgroup
= This
->primary
.BufferGroups
;
848 if(This
->primary
.write_emu
)
850 ERR("Why was there a write_emu?\n");
852 IDirectSoundBuffer_Release(This
->primary
.write_emu
);
853 This
->primary
.write_emu
= NULL
;
856 for(i
= 0;i
< This
->primary
.NumBufferGroups
;++i
)
858 DWORD64 usemask
= ~bufgroup
[i
].FreeBuffers
;
861 int idx
= CTZ64(usemask
);
862 DSBuffer
*buf
= bufgroup
[i
].Buffers
+ idx
;
863 usemask
&= ~(U64(1) << idx
);
865 if(FAILED(DSBuffer_GetStatus(&buf
->IDirectSoundBuffer8_iface
, &state
)) ||
866 (state
&DSBSTATUS_PLAYING
))
868 WARN("DSSCL_WRITEPRIMARY set with playing buffers!\n");
869 hr
= DSERR_INVALIDCALL
;
872 /* Mark buffer as lost */
877 if(This
->primary
.flags
)
879 /* Primary has open references.. create write_emu */
883 memset(&desc
, 0, sizeof(desc
));
884 desc
.dwSize
= sizeof(desc
);
885 desc
.dwFlags
= DSBCAPS_LOCHARDWARE
| (This
->primary
.flags
&DSBCAPS_CTRLPAN
);
886 desc
.dwBufferBytes
= This
->primary
.buf_size
;
887 desc
.lpwfxFormat
= &This
->primary
.format
.Format
;
889 hr
= DSBuffer_Create(&emu
, &This
->primary
, NULL
);
892 hr
= DSBuffer_Initialize(&emu
->IDirectSoundBuffer8_iface
,
893 &This
->IDirectSound_iface
, &desc
);
895 hr
= DSBuffer_GetInterface(emu
, &IID_IDirectSoundBuffer
,
896 (void**)&This
->primary
.write_emu
);
898 DSBuffer_Destroy(emu
);
902 else if(This
->prio_level
== DSSCL_WRITEPRIMARY
&& level
!= DSSCL_WRITEPRIMARY
)
905 TRACE("Nuking write_emu\n");
906 if(This
->primary
.write_emu
)
907 IDirectSoundBuffer_Release(This
->primary
.write_emu
);
908 This
->primary
.write_emu
= NULL
;
911 This
->prio_level
= level
;
913 LeaveCriticalSection(&This
->share
->crst
);
918 static HRESULT WINAPI
DS8_Compact(IDirectSound8
*iface
)
920 DSDevice
*This
= impl_from_IDirectSound8(iface
);
923 TRACE("(%p)->()\n", iface
);
927 WARN("Device not initialized\n");
928 return DSERR_UNINITIALIZED
;
931 EnterCriticalSection(&This
->share
->crst
);
932 if(This
->prio_level
< DSSCL_PRIORITY
)
934 WARN("Coop level not high enough (%lu)\n", This
->prio_level
);
935 hr
= DSERR_PRIOLEVELNEEDED
;
937 LeaveCriticalSection(&This
->share
->crst
);
942 static HRESULT WINAPI
DS8_GetSpeakerConfig(IDirectSound8
*iface
, DWORD
*config
)
944 DSDevice
*This
= impl_from_IDirectSound8(iface
);
946 TRACE("(%p)->(%p)\n", iface
, config
);
949 return DSERR_INVALIDPARAM
;
954 WARN("Device not initialized\n");
955 return DSERR_UNINITIALIZED
;
958 *config
= This
->share
->speaker_config
;
963 static HRESULT WINAPI
DS8_SetSpeakerConfig(IDirectSound8
*iface
, DWORD config
)
965 DSDevice
*This
= impl_from_IDirectSound8(iface
);
968 TRACE("(%p)->(0x%08lx)\n", iface
, config
);
972 WARN("Device not initialized\n");
973 return DSERR_UNINITIALIZED
;
976 geo
= DSSPEAKER_GEOMETRY(config
);
977 speaker
= DSSPEAKER_CONFIG(config
);
979 if(geo
&& (geo
< DSSPEAKER_GEOMETRY_MIN
|| geo
> DSSPEAKER_GEOMETRY_MAX
))
981 WARN("Invalid speaker angle %lu\n", geo
);
982 return DSERR_INVALIDPARAM
;
984 if(speaker
< DSSPEAKER_HEADPHONE
|| speaker
> DSSPEAKER_7POINT1
)
986 WARN("Invalid speaker config %lu\n", speaker
);
987 return DSERR_INVALIDPARAM
;
990 /* No-op on Vista+. */
994 static HRESULT WINAPI
DS8_Initialize(IDirectSound8
*iface
, const GUID
*devguid
)
996 DSDevice
*This
= impl_from_IDirectSound8(iface
);
1001 TRACE("(%p)->(%s)\n", iface
, debugstr_guid(devguid
));
1004 return DSERR_NODRIVER
;
1008 WARN("Device already initialized\n");
1009 return DSERR_ALREADYINITIALIZED
;
1012 if(!devguid
|| IsEqualGUID(devguid
, &GUID_NULL
))
1013 devguid
= &DSDEVID_DefaultPlayback
;
1014 else if(IsEqualGUID(devguid
, &DSDEVID_DefaultCapture
) ||
1015 IsEqualGUID(devguid
, &DSDEVID_DefaultVoiceCapture
))
1016 return DSERR_NODRIVER
;
1018 hr
= DSOAL_GetDeviceID(devguid
, &guid
);
1019 if(FAILED(hr
)) return hr
;
1021 EnterCriticalSection(&openal_crst
);
1023 TRACE("Searching shared devices for %s\n", debugstr_guid(&guid
));
1024 for(n
= 0;n
< sharelistsize
;n
++)
1026 if(IsEqualGUID(&sharelist
[n
]->guid
, &guid
))
1028 TRACE("Matched shared device %p\n", sharelist
[n
]);
1030 DSShare_AddRef(sharelist
[n
]);
1031 This
->share
= sharelist
[n
];
1037 hr
= DSShare_Create(&guid
, &This
->share
);
1040 This
->device
= This
->share
->device
;
1041 hr
= DSPrimary_PreInit(&This
->primary
, This
);
1046 DeviceShare
*share
= This
->share
;
1049 EnterCriticalSection(&share
->crst
);
1051 prims
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
,
1052 (share
->nprimaries
+1) * sizeof(*prims
));
1054 hr
= DSERR_OUTOFMEMORY
;
1058 for(i
= 0;i
< share
->nprimaries
;++i
)
1059 prims
[i
] = share
->primaries
[i
];
1060 prims
[i
] = &This
->primary
;
1062 HeapFree(GetProcessHeap(), 0, share
->primaries
);
1063 share
->primaries
= prims
;
1064 share
->nprimaries
+= 1;
1067 LeaveCriticalSection(&share
->crst
);
1073 DSShare_Release(This
->share
);
1077 LeaveCriticalSection(&openal_crst
);
1081 /* I, Maarten Lankhorst, hereby declare this driver certified
1082 * What this means.. ? An extra bit set
1084 static HRESULT WINAPI
DS8_VerifyCertification(IDirectSound8
*iface
, DWORD
*certified
)
1086 DSDevice
*This
= impl_from_IDirectSound8(iface
);
1088 TRACE("(%p)->(%p)\n", iface
, certified
);
1091 return DSERR_INVALIDPARAM
;
1096 WARN("Device not initialized\n");
1097 return DSERR_UNINITIALIZED
;
1100 *certified
= DS_CERTIFIED
;
1105 static IDirectSound8Vtbl DS8_Vtbl
= {
1109 DS8_CreateSoundBuffer
,
1111 DS8_DuplicateSoundBuffer
,
1112 DS8_SetCooperativeLevel
,
1114 DS8_GetSpeakerConfig
,
1115 DS8_SetSpeakerConfig
,
1117 DS8_VerifyCertification
1121 static HRESULT WINAPI
DS_QueryInterface(IDirectSound
*iface
, REFIID riid
, LPVOID
*ppv
)
1123 DSDevice
*This
= impl_from_IDirectSound(iface
);
1124 TRACE("(%p)->(%s, %p)\n", iface
, debugstr_guid(riid
), ppv
);
1125 return DSDevice_GetInterface(This
, riid
, ppv
);
1128 static ULONG WINAPI
DS_AddRef(IDirectSound
*iface
)
1130 DSDevice
*This
= impl_from_IDirectSound(iface
);
1131 return DS8_AddRef(&This
->IDirectSound8_iface
);
1134 static ULONG WINAPI
DS_Release(IDirectSound
*iface
)
1136 DSDevice
*This
= impl_from_IDirectSound(iface
);
1137 return DS8_Release(&This
->IDirectSound8_iface
);
1140 static HRESULT WINAPI
DS_CreateSoundBuffer(IDirectSound
*iface
, LPCDSBUFFERDESC desc
, LPLPDIRECTSOUNDBUFFER buf
, IUnknown
*pUnkOuter
)
1142 DSDevice
*This
= impl_from_IDirectSound(iface
);
1143 return DS8_CreateSoundBuffer(&This
->IDirectSound8_iface
, desc
, buf
, pUnkOuter
);
1146 static HRESULT WINAPI
DS_GetCaps(IDirectSound
*iface
, LPDSCAPS caps
)
1148 DSDevice
*This
= impl_from_IDirectSound(iface
);
1149 return DS8_GetCaps(&This
->IDirectSound8_iface
, caps
);
1151 static HRESULT WINAPI
DS_DuplicateSoundBuffer(IDirectSound
*iface
, IDirectSoundBuffer
*in
, IDirectSoundBuffer
**out
)
1153 DSDevice
*This
= impl_from_IDirectSound(iface
);
1154 return DS8_DuplicateSoundBuffer(&This
->IDirectSound8_iface
, in
, out
);
1157 static HRESULT WINAPI
DS_SetCooperativeLevel(IDirectSound
*iface
, HWND hwnd
, DWORD level
)
1159 DSDevice
*This
= impl_from_IDirectSound(iface
);
1160 return DS8_SetCooperativeLevel(&This
->IDirectSound8_iface
, hwnd
, level
);
1163 static HRESULT WINAPI
DS_Compact(IDirectSound
*iface
)
1165 DSDevice
*This
= impl_from_IDirectSound(iface
);
1166 return DS8_Compact(&This
->IDirectSound8_iface
);
1169 static HRESULT WINAPI
DS_GetSpeakerConfig(IDirectSound
*iface
, DWORD
*config
)
1171 DSDevice
*This
= impl_from_IDirectSound(iface
);
1172 return DS8_GetSpeakerConfig(&This
->IDirectSound8_iface
, config
);
1175 static HRESULT WINAPI
DS_SetSpeakerConfig(IDirectSound
*iface
, DWORD config
)
1177 DSDevice
*This
= impl_from_IDirectSound(iface
);
1178 return DS8_SetSpeakerConfig(&This
->IDirectSound8_iface
, config
);
1181 static HRESULT WINAPI
DS_Initialize(IDirectSound
*iface
, const GUID
*devguid
)
1183 DSDevice
*This
= impl_from_IDirectSound(iface
);
1184 return DS8_Initialize(&This
->IDirectSound8_iface
, devguid
);
1187 static IDirectSoundVtbl DS_Vtbl
= {
1191 DS_CreateSoundBuffer
,
1193 DS_DuplicateSoundBuffer
,
1194 DS_SetCooperativeLevel
,
1196 DS_GetSpeakerConfig
,
1197 DS_SetSpeakerConfig
,