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 if(share
->sources
.maxhw_alloc
+ share
->sources
.maxsw_alloc
)
152 alDeleteSources(share
->sources
.maxhw_alloc
+share
->sources
.maxsw_alloc
,
154 share
->sources
.maxhw_alloc
= share
->sources
.maxsw_alloc
= 0;
156 if(share
->auxslot
[3])
157 alDeleteAuxiliaryEffectSlots(4, share
->auxslot
);
158 else if(share
->auxslot
[2])
159 alDeleteAuxiliaryEffectSlots(3, share
->auxslot
);
160 else if(share
->auxslot
[1])
161 alDeleteAuxiliaryEffectSlots(2, share
->auxslot
);
162 else if(share
->auxslot
[0])
163 alDeleteAuxiliaryEffectSlots(1, share
->auxslot
);
164 share
->auxslot
[0] = share
->auxslot
[1] =
165 share
->auxslot
[2] = share
->auxslot
[3] = 0;
168 TlsSetValue(TlsThreadPtr
, NULL
);
169 alcDestroyContext(share
->ctx
);
171 LeaveCriticalSection(&openal_crst
);
175 alcCloseDevice(share
->device
);
176 share
->device
= NULL
;
178 DeleteCriticalSection(&share
->crst
);
180 HeapFree(GetProcessHeap(), 0, share
->primaries
);
181 HeapFree(GetProcessHeap(), 0, share
);
183 TRACE("Closed shared device %p\n", share
);
186 static HRESULT
DSShare_Create(REFIID guid
, DeviceShare
**out
)
188 static const struct {
189 const char extname
[64];
191 } extensions
[MAX_EXTENSIONS
] = {
192 { "ALC_EXT_EFX", EXT_EFX
},
193 { "AL_EXT_FLOAT32", EXT_FLOAT32
},
194 { "AL_EXT_MCFORMATS", EXT_MCFORMATS
},
195 { "AL_SOFT_deferred_updates", SOFT_DEFERRED_UPDATES
},
196 { "AL_SOFT_source_spatialize", SOFT_SOURCE_SPATIALIZE
},
197 { "AL_SOFTX_filter_gain_ex", SOFTX_FILTER_GAIN_EX
},
198 { "AL_SOFTX_map_buffer", SOFTX_MAP_BUFFER
},
200 OLECHAR
*guid_str
= NULL
;
209 share
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(*share
));
210 if(!share
) return DSERR_OUTOFMEMORY
;
212 share
->refresh
= FAKE_REFRESH_COUNT
;
213 share
->speaker_config
= DSSPEAKER_7POINT1_SURROUND
;
214 share
->vm_managermode
= DSPROPERTY_VMANAGER_MODE_DEFAULT
;
216 TRACE("Creating shared device %p\n", share
);
218 hr
= get_mmdevice(eRender
, guid
, &mmdev
);
221 IPropertyStore
*store
;
223 hr
= IMMDevice_OpenPropertyStore(mmdev
, STGM_READ
, &store
);
225 WARN("IMMDevice_OpenPropertyStore failed: %08lx\n", hr
);
228 ULONG phys_speakers
= 0;
231 PropVariantInit(&pv
);
233 hr
= IPropertyStore_GetValue(store
, &PKEY_AudioEndpoint_PhysicalSpeakers
, &pv
);
235 WARN("IPropertyStore_GetValue failed: %08lx\n", hr
);
236 else if(pv
.vt
!= VT_UI4
)
237 WARN("PKEY_AudioEndpoint_PhysicalSpeakers is not a ULONG: 0x%04x\n", pv
.vt
);
240 phys_speakers
= pv
.ulVal
;
242 #define BIT_MATCH(v, b) (((v)&(b)) == (b))
243 if(BIT_MATCH(phys_speakers
, KSAUDIO_SPEAKER_7POINT1
))
244 share
->speaker_config
= DSSPEAKER_7POINT1
;
245 else if(BIT_MATCH(phys_speakers
, KSAUDIO_SPEAKER_7POINT1_SURROUND
))
246 share
->speaker_config
= DSSPEAKER_7POINT1_SURROUND
;
247 else if(BIT_MATCH(phys_speakers
, KSAUDIO_SPEAKER_5POINT1
))
248 share
->speaker_config
= DSSPEAKER_5POINT1_BACK
;
249 else if(BIT_MATCH(phys_speakers
, KSAUDIO_SPEAKER_5POINT1_SURROUND
))
250 share
->speaker_config
= DSSPEAKER_5POINT1_SURROUND
;
251 else if(BIT_MATCH(phys_speakers
, KSAUDIO_SPEAKER_QUAD
))
252 share
->speaker_config
= DSSPEAKER_QUAD
;
253 else if(BIT_MATCH(phys_speakers
, KSAUDIO_SPEAKER_STEREO
))
254 share
->speaker_config
= DSSPEAKER_COMBINED(DSSPEAKER_STEREO
, DSSPEAKER_GEOMETRY_WIDE
);
255 else if(BIT_MATCH(phys_speakers
, KSAUDIO_SPEAKER_MONO
))
256 share
->speaker_config
= DSSPEAKER_MONO
;
258 FIXME("Unhandled physical speaker layout: 0x%08lx\n", phys_speakers
);
262 /* If the device has a stereo layout, check the formfactor to see
263 * if it's really headphones/headset.
265 if(DSSPEAKER_CONFIG(share
->speaker_config
) == DSSPEAKER_STEREO
)
267 hr
= IPropertyStore_GetValue(store
, &PKEY_AudioEndpoint_FormFactor
, &pv
);
269 WARN("IPropertyStore_GetValue failed: %08lx\n", hr
);
270 else if(pv
.vt
!= VT_UI4
)
271 WARN("PKEY_AudioEndpoint_FormFactor is not a ULONG: 0x%04x\n", pv
.vt
);
274 if(pv
.ulVal
== Headphones
|| pv
.ulVal
== Headset
)
275 share
->speaker_config
= DSSPEAKER_HEADPHONE
;
279 TRACE("Got speaker config %d:%d from physical speakers 0x%08lx\n",
280 DSSPEAKER_GEOMETRY(share
->speaker_config
),
281 DSSPEAKER_CONFIG(share
->speaker_config
), phys_speakers
);
283 PropVariantClear(&pv
);
284 IPropertyStore_Release(store
);
287 IMMDevice_Release(mmdev
);
291 InitializeCriticalSection(&share
->crst
);
293 hr
= StringFromCLSID(guid
, &guid_str
);
296 ERR("Failed to convert GUID to string\n");
299 WideCharToMultiByte(CP_UTF8
, 0, guid_str
, -1, drv_name
, sizeof(drv_name
), NULL
, NULL
);
300 drv_name
[sizeof(drv_name
)-1] = 0;
301 CoTaskMemFree(guid_str
);
305 share
->device
= alcOpenDevice(drv_name
);
309 WARN("Couldn't open device \"%s\"\n", drv_name
);
312 TRACE("Opened AL device: %s\n",
313 alcIsExtensionPresent(share
->device
, "ALC_ENUMERATE_ALL_EXT") ?
314 alcGetString(share
->device
, ALC_ALL_DEVICES_SPECIFIER
) :
315 alcGetString(share
->device
, ALC_DEVICE_SPECIFIER
));
318 attrs
[i
++] = ALC_MONO_SOURCES
;
319 attrs
[i
++] = MAX_SOURCES
;
320 attrs
[i
++] = ALC_STEREO_SOURCES
;
322 if(alcIsExtensionPresent(share
->device
, "ALC_EXT_EFX"))
324 attrs
[i
++] = ALC_MAX_AUXILIARY_SENDS
;
325 attrs
[i
++] = EAX_MAX_ACTIVE_FXSLOTS
;
328 share
->ctx
= alcCreateContext(share
->device
, attrs
);
331 ALCenum err
= alcGetError(share
->device
);
332 ERR("Could not create context (0x%x)!\n", err
);
338 setALContext(share
->ctx
);
339 alcGetIntegerv(share
->device
, ALC_REFRESH
, 1, &share
->refresh
);
340 checkALCError(share
->device
);
342 for(i
= 0;i
< MAX_EXTENSIONS
;i
++)
344 if((strncmp(extensions
[i
].extname
, "ALC", 3) == 0) ?
345 alcIsExtensionPresent(share
->device
, extensions
[i
].extname
) :
346 alIsExtensionPresent(extensions
[i
].extname
))
348 TRACE("Found %s\n", extensions
[i
].extname
);
349 BITFIELD_SET(share
->Exts
, extensions
[i
].extenum
);
353 share
->sources
.maxhw_alloc
= 0;
354 while(share
->sources
.maxhw_alloc
< MAX_SOURCES
)
356 alGenSources(1, &share
->sources
.ids
[share
->sources
.maxhw_alloc
]);
357 if(alGetError() != AL_NO_ERROR
) break;
358 share
->sources
.maxhw_alloc
++;
361 share
->num_slots
= 0;
362 if(HAS_EXTENSION(share
, EXT_EFX
))
364 alcGetIntegerv(share
->device
, ALC_MAX_AUXILIARY_SENDS
, 1, &share
->num_sends
);
365 checkALCError(share
->device
);
366 if(share
->num_sends
> EAX_MAX_ACTIVE_FXSLOTS
)
367 share
->num_sends
= EAX_MAX_ACTIVE_FXSLOTS
;
368 TRACE("Got %d auxiliary source send%s\n", share
->num_sends
, (share
->num_sends
==1)?"":"s");
370 for(i
= 0;i
< EAX_MAX_FXSLOTS
;++i
)
372 alGenAuxiliaryEffectSlots(1, &share
->auxslot
[i
]);
373 if(alGetError() != AL_NO_ERROR
) break;
376 TRACE("Allocated %d auxiliary effect slot%s\n", i
, (i
==1)?"":"s");
377 while(i
< EAX_MAX_FXSLOTS
)
378 share
->auxslot
[i
++] = 0;
383 if(share
->sources
.maxhw_alloc
< 128)
385 ERR("Could only allocate %lu sources (minimum 128 required)\n",
386 share
->sources
.maxhw_alloc
);
390 if(share
->sources
.maxhw_alloc
> MAX_HWBUFFERS
)
392 share
->sources
.maxsw_alloc
= share
->sources
.maxhw_alloc
- MAX_HWBUFFERS
;
393 share
->sources
.maxhw_alloc
= MAX_HWBUFFERS
;
395 else if(share
->sources
.maxhw_alloc
> MAX_HWBUFFERS
/2)
397 share
->sources
.maxsw_alloc
= share
->sources
.maxhw_alloc
- MAX_HWBUFFERS
/2;
398 share
->sources
.maxhw_alloc
= MAX_HWBUFFERS
/2;
402 share
->sources
.maxsw_alloc
= share
->sources
.maxhw_alloc
- MAX_HWBUFFERS
/4;
403 share
->sources
.maxhw_alloc
= MAX_HWBUFFERS
/4;
405 share
->sources
.availhw_num
= share
->sources
.maxhw_alloc
;
406 share
->sources
.availsw_num
= share
->sources
.maxsw_alloc
;
407 TRACE("Allocated %lu hardware sources and %lu software sources\n",
408 share
->sources
.maxhw_alloc
, share
->sources
.maxsw_alloc
);
411 temp
= HeapReAlloc(GetProcessHeap(), 0, sharelist
, sizeof(*sharelist
)*(sharelistsize
+1));
413 temp
= HeapAlloc(GetProcessHeap(), 0, sizeof(*sharelist
)*(sharelistsize
+1));
417 sharelist
[sharelistsize
++] = share
;
422 share
->quit_now
= FALSE
;
423 share
->timer_evt
= CreateEventA(NULL
, FALSE
, FALSE
, NULL
);
424 if(!share
->timer_evt
) goto fail
;
426 share
->queue_timer
= NULL
;
428 share
->thread_hdl
= CreateThread(NULL
, 0, DSShare_thread
, share
, 0, &share
->thread_id
);
429 if(!share
->thread_hdl
) goto fail
;
431 DSShare_starttimer(share
);
437 DSShare_Destroy(share
);
441 static ULONG
DSShare_AddRef(DeviceShare
*share
)
443 ULONG ref
= InterlockedIncrement(&share
->ref
);
447 static ULONG
DSShare_Release(DeviceShare
*share
)
449 ULONG ref
= InterlockedDecrement(&share
->ref
);
450 if(ref
== 0) DSShare_Destroy(share
);
455 static IDirectSound8Vtbl DS8_Vtbl
;
456 static IDirectSoundVtbl DS_Vtbl
;
457 static IUnknownVtbl DS8_Unknown_Vtbl
;
459 static HRESULT
DSDevice_Create(BOOL is8
, REFIID riid
, LPVOID
*ds
);
460 static void DSDevice_Destroy(DSDevice
*This
);
461 static HRESULT
DSDevice_GetInterface(DSDevice
*This
, REFIID riid
, LPVOID
*ppv
);
463 /*******************************************************************************
466 static inline DSDevice
*impl_from_IUnknown(IUnknown
*iface
)
468 return CONTAINING_RECORD(iface
, DSDevice
, IUnknown_iface
);
471 static HRESULT WINAPI
DSDevice_IUnknown_QueryInterface(IUnknown
*iface
, REFIID riid
, void **ppobj
)
473 DSDevice
*This
= impl_from_IUnknown(iface
);
474 TRACE("(%p)->(%s, %p)\n", iface
, debugstr_guid(riid
), ppobj
);
475 return DSDevice_GetInterface(This
, riid
, ppobj
);
478 static ULONG WINAPI
DSDevice_IUnknown_AddRef(IUnknown
*iface
)
480 DSDevice
*This
= impl_from_IUnknown(iface
);
483 InterlockedIncrement(&(This
->ref
));
484 ref
= InterlockedIncrement(&(This
->unkref
));
485 TRACE("(%p) ref %lu\n", iface
, ref
);
490 static ULONG WINAPI
DSDevice_IUnknown_Release(IUnknown
*iface
)
492 DSDevice
*This
= impl_from_IUnknown(iface
);
493 ULONG ref
= InterlockedDecrement(&(This
->unkref
));
494 TRACE("(%p) ref %lu\n", iface
, ref
);
495 if(InterlockedDecrement(&(This
->ref
)) == 0)
496 DSDevice_Destroy(This
);
500 static IUnknownVtbl DS8_Unknown_Vtbl
= {
501 DSDevice_IUnknown_QueryInterface
,
502 DSDevice_IUnknown_AddRef
,
503 DSDevice_IUnknown_Release
507 static inline DSDevice
*impl_from_IDirectSound8(IDirectSound8
*iface
)
509 return CONTAINING_RECORD(iface
, DSDevice
, IDirectSound8_iface
);
512 static inline DSDevice
*impl_from_IDirectSound(IDirectSound
*iface
)
514 return CONTAINING_RECORD(iface
, DSDevice
, IDirectSound_iface
);
518 HRESULT
DSOUND_Create(REFIID riid
, void **ds
)
519 { return DSDevice_Create(FALSE
, riid
, ds
); }
521 HRESULT
DSOUND_Create8(REFIID riid
, LPVOID
*ds
)
522 { return DSDevice_Create(TRUE
, riid
, ds
); }
524 static HRESULT
DSDevice_Create(BOOL is8
, REFIID riid
, LPVOID
*ds
)
530 This
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(*This
));
531 if(!This
) return DSERR_OUTOFMEMORY
;
533 TRACE("Creating device instance %p\n", This
);
534 This
->IDirectSound8_iface
.lpVtbl
= &DS8_Vtbl
;
535 This
->IDirectSound_iface
.lpVtbl
= &DS_Vtbl
;
536 This
->IUnknown_iface
.lpVtbl
= &DS8_Unknown_Vtbl
;
540 hr
= DSDevice_GetInterface(This
, riid
, ds
);
541 if(FAILED(hr
)) DSDevice_Destroy(This
);
545 static void DSDevice_Destroy(DSDevice
*This
)
547 DeviceShare
*share
= This
->share
;
549 TRACE("Destroying device instance %p\n", This
);
554 EnterCriticalSection(&share
->crst
);
556 for(i
= 0;i
< share
->nprimaries
;++i
)
558 if(share
->primaries
[i
] == &This
->primary
)
560 share
->nprimaries
-= 1;
561 share
->primaries
[i
] = share
->primaries
[share
->nprimaries
];
566 LeaveCriticalSection(&share
->crst
);
569 DSPrimary_Clear(&This
->primary
);
571 DSShare_Release(This
->share
);
574 HeapFree(GetProcessHeap(), 0, This
);
577 static HRESULT
DSDevice_GetInterface(DSDevice
*This
, REFIID riid
, LPVOID
*ppv
)
580 if(IsEqualIID(riid
, &IID_IUnknown
))
581 *ppv
= &This
->IUnknown_iface
;
582 else if(IsEqualIID(riid
, &IID_IDirectSound8
))
585 *ppv
= &This
->IDirectSound8_iface
;
587 else if(IsEqualIID(riid
, &IID_IDirectSound
))
588 *ppv
= &This
->IDirectSound_iface
;
590 FIXME("Unhandled GUID: %s\n", debugstr_guid(riid
));
594 IUnknown_AddRef((IUnknown
*)*ppv
);
598 return E_NOINTERFACE
;
602 static HRESULT WINAPI
DS8_QueryInterface(IDirectSound8
*iface
, REFIID riid
, LPVOID
*ppv
)
604 DSDevice
*This
= impl_from_IDirectSound8(iface
);
605 TRACE("(%p)->(%s, %p)\n", iface
, debugstr_guid(riid
), ppv
);
606 return DSDevice_GetInterface(This
, riid
, ppv
);
609 static ULONG WINAPI
DS8_AddRef(IDirectSound8
*iface
)
611 DSDevice
*This
= impl_from_IDirectSound8(iface
);
614 InterlockedIncrement(&This
->ref
);
615 ref
= InterlockedIncrement(&This
->dsref
);
616 TRACE("(%p) ref %lu\n", iface
, ref
);
621 static ULONG WINAPI
DS8_Release(IDirectSound8
*iface
)
623 DSDevice
*This
= impl_from_IDirectSound8(iface
);
626 ref
= InterlockedDecrement(&This
->dsref
);
627 TRACE("(%p) ref %lu\n", iface
, ref
);
628 if(InterlockedDecrement(&This
->ref
) == 0)
629 DSDevice_Destroy(This
);
634 static HRESULT WINAPI
DS8_CreateSoundBuffer(IDirectSound8
*iface
, LPCDSBUFFERDESC desc
, LPLPDIRECTSOUNDBUFFER buf
, IUnknown
*pUnkOuter
)
636 DSDevice
*This
= impl_from_IDirectSound8(iface
);
639 TRACE("(%p)->(%p, %p, %p)\n", iface
, desc
, buf
, pUnkOuter
);
643 WARN("buf is null\n");
644 return DSERR_INVALIDPARAM
;
650 WARN("Aggregation isn't supported\n");
651 return DSERR_NOAGGREGATION
;
653 if(!desc
|| desc
->dwSize
< sizeof(DSBUFFERDESC1
))
655 WARN("Invalid buffer %p/%lu\n", desc
, desc
?desc
->dwSize
:0);
656 return DSERR_INVALIDPARAM
;
661 WARN("Device not initialized\n");
662 return DSERR_UNINITIALIZED
;
665 TRACE("Requested buffer:\n"
668 " BufferBytes = %lu\n",
669 desc
->dwSize
, desc
->dwFlags
, desc
->dwBufferBytes
);
671 if(desc
->dwSize
>= sizeof(DSBUFFERDESC
))
673 if(!(desc
->dwFlags
&DSBCAPS_CTRL3D
))
675 if(!IsEqualGUID(&desc
->guid3DAlgorithm
, &GUID_NULL
))
677 /* Not fatal. Some apps pass unknown values here. */
678 WARN("Unknown 3D algorithm GUID specified for non-3D buffer: %s\n",
679 debugstr_guid(&desc
->guid3DAlgorithm
));
683 TRACE("Requested 3D algorithm GUID: %s\n", debugstr_guid(&desc
->guid3DAlgorithm
));
686 /* OpenAL doesn't support playing with 3d and panning at same time.. */
687 if((desc
->dwFlags
&(DSBCAPS_CTRL3D
|DSBCAPS_CTRLPAN
)) == (DSBCAPS_CTRL3D
|DSBCAPS_CTRLPAN
))
693 FIXME("Buffers with 3D and panning control ignore panning\n");
697 WARN("Cannot create buffers with 3D and panning control\n");
698 return DSERR_INVALIDPARAM
;
702 EnterCriticalSection(&This
->share
->crst
);
703 if((desc
->dwFlags
&DSBCAPS_PRIMARYBUFFER
))
705 IDirectSoundBuffer
*prim
= &This
->primary
.IDirectSoundBuffer_iface
;
708 if(IDirectSoundBuffer_AddRef(prim
) == 1)
710 hr
= DSPrimary_Initialize(prim
, &This
->IDirectSound_iface
, desc
);
713 IDirectSoundBuffer_Release(prim
);
723 hr
= DSBuffer_Create(&dsb
, &This
->primary
, NULL
);
726 hr
= DSBuffer_Initialize(&dsb
->IDirectSoundBuffer8_iface
, &This
->IDirectSound_iface
, desc
);
729 dsb
->bufferlost
= (This
->prio_level
== DSSCL_WRITEPRIMARY
);
730 hr
= DSBuffer_GetInterface(dsb
, &IID_IDirectSoundBuffer
, (void**)buf
);
733 DSBuffer_Destroy(dsb
);
736 LeaveCriticalSection(&This
->share
->crst
);
738 TRACE("%08lx\n", hr
);
742 static HRESULT WINAPI
DS8_GetCaps(IDirectSound8
*iface
, LPDSCAPS caps
)
744 DSDevice
*This
= impl_from_IDirectSound8(iface
);
745 struct DSBufferGroup
*bufgroup
, *endgroup
;
748 TRACE("(%p)->(%p)\n", iface
, caps
);
752 WARN("Device not initialized\n");
753 return DSERR_UNINITIALIZED
;
756 if(!caps
|| caps
->dwSize
< sizeof(*caps
))
758 WARN("Invalid DSCAPS (%p, %lu)\n", caps
, (caps
?caps
->dwSize
:0));
759 return DSERR_INVALIDPARAM
;
762 EnterCriticalSection(&This
->share
->crst
);
764 free_bufs
= This
->share
->sources
.maxhw_alloc
;
765 bufgroup
= This
->primary
.BufferGroups
;
766 endgroup
= bufgroup
+ This
->primary
.NumBufferGroups
;
767 for(;free_bufs
&& bufgroup
!= endgroup
;++bufgroup
)
769 DWORD64 usemask
= ~bufgroup
->FreeBuffers
;
772 int idx
= CTZ64(usemask
);
773 DSBuffer
*buf
= bufgroup
->Buffers
+ idx
;
774 usemask
&= ~(U64(1) << idx
);
776 if(buf
->loc_status
== DSBSTATUS_LOCHARDWARE
)
784 caps
->dwFlags
= DSCAPS_CONTINUOUSRATE
| DSCAPS_CERTIFIED
|
785 DSCAPS_PRIMARY16BIT
| DSCAPS_PRIMARYSTEREO
|
786 DSCAPS_PRIMARY8BIT
| DSCAPS_PRIMARYMONO
|
787 DSCAPS_SECONDARY16BIT
| DSCAPS_SECONDARY8BIT
|
788 DSCAPS_SECONDARYMONO
| DSCAPS_SECONDARYSTEREO
;
789 caps
->dwPrimaryBuffers
= 1;
790 caps
->dwMinSecondarySampleRate
= DSBFREQUENCY_MIN
;
791 caps
->dwMaxSecondarySampleRate
= DSBFREQUENCY_MAX
;
792 caps
->dwMaxHwMixingAllBuffers
=
793 caps
->dwMaxHwMixingStaticBuffers
=
794 caps
->dwMaxHwMixingStreamingBuffers
=
795 caps
->dwMaxHw3DAllBuffers
=
796 caps
->dwMaxHw3DStaticBuffers
=
797 caps
->dwMaxHw3DStreamingBuffers
= This
->share
->sources
.maxhw_alloc
;
798 caps
->dwFreeHwMixingAllBuffers
=
799 caps
->dwFreeHwMixingStaticBuffers
=
800 caps
->dwFreeHwMixingStreamingBuffers
=
801 caps
->dwFreeHw3DAllBuffers
=
802 caps
->dwFreeHw3DStaticBuffers
=
803 caps
->dwFreeHw3DStreamingBuffers
= free_bufs
;
804 caps
->dwTotalHwMemBytes
=
805 caps
->dwFreeHwMemBytes
= 64 * 1024 * 1024;
806 caps
->dwMaxContigFreeHwMemBytes
= caps
->dwFreeHwMemBytes
;
807 caps
->dwUnlockTransferRateHwBuffers
= 4096;
808 caps
->dwPlayCpuOverheadSwBuffers
= 0;
810 LeaveCriticalSection(&This
->share
->crst
);
814 static HRESULT WINAPI
DS8_DuplicateSoundBuffer(IDirectSound8
*iface
, IDirectSoundBuffer
*in
, IDirectSoundBuffer
**out
)
816 DSDevice
*This
= impl_from_IDirectSound8(iface
);
817 DSBuffer
*buf
= NULL
;
821 TRACE("(%p)->(%p, %p)\n", iface
, in
, out
);
825 WARN("Device not initialized\n");
826 return DSERR_UNINITIALIZED
;
831 WARN("Invalid pointer: in = %p, out = %p\n", in
, out
);
832 return DSERR_INVALIDPARAM
;
836 caps
.dwSize
= sizeof(caps
);
837 hr
= IDirectSoundBuffer_GetCaps(in
, &caps
);
838 if(SUCCEEDED(hr
) && (caps
.dwFlags
&DSBCAPS_PRIMARYBUFFER
))
840 WARN("Cannot duplicate buffer %p, which has DSBCAPS_PRIMARYBUFFER\n", in
);
841 hr
= DSERR_INVALIDPARAM
;
843 if(SUCCEEDED(hr
) && (caps
.dwFlags
&DSBCAPS_CTRLFX
))
845 WARN("Cannot duplicate buffer %p, which has DSBCAPS_CTRLFX\n", in
);
846 hr
= DSERR_INVALIDPARAM
;
849 hr
= DSBuffer_Create(&buf
, &This
->primary
, in
);
852 hr
= DSBuffer_Initialize(&buf
->IDirectSoundBuffer8_iface
, NULL
, NULL
);
854 hr
= DSBuffer_GetInterface(buf
, &IID_IDirectSoundBuffer
, (void**)out
);
856 DSBuffer_Destroy(buf
);
862 static HRESULT WINAPI
DS8_SetCooperativeLevel(IDirectSound8
*iface
, HWND hwnd
, DWORD level
)
864 DSDevice
*This
= impl_from_IDirectSound8(iface
);
867 TRACE("(%p)->(%p, %lu)\n", iface
, hwnd
, level
);
871 WARN("Device not initialized\n");
872 return DSERR_UNINITIALIZED
;
875 if(level
> DSSCL_WRITEPRIMARY
|| level
< DSSCL_NORMAL
)
877 WARN("Invalid coop level: %lu\n", level
);
878 return DSERR_INVALIDPARAM
;
881 EnterCriticalSection(&This
->share
->crst
);
882 if(level
== DSSCL_WRITEPRIMARY
&& (This
->prio_level
!= DSSCL_WRITEPRIMARY
))
884 struct DSBufferGroup
*bufgroup
= This
->primary
.BufferGroups
;
887 if(This
->primary
.write_emu
)
889 ERR("Why was there a write_emu?\n");
891 IDirectSoundBuffer_Release(This
->primary
.write_emu
);
892 This
->primary
.write_emu
= NULL
;
895 for(i
= 0;i
< This
->primary
.NumBufferGroups
;++i
)
897 DWORD64 usemask
= ~bufgroup
[i
].FreeBuffers
;
900 int idx
= CTZ64(usemask
);
901 DSBuffer
*buf
= bufgroup
[i
].Buffers
+ idx
;
902 usemask
&= ~(U64(1) << idx
);
904 if(FAILED(DSBuffer_GetStatus(&buf
->IDirectSoundBuffer8_iface
, &state
)) ||
905 (state
&DSBSTATUS_PLAYING
))
907 WARN("DSSCL_WRITEPRIMARY set with playing buffers!\n");
908 hr
= DSERR_INVALIDCALL
;
911 /* Mark buffer as lost */
916 if(This
->primary
.flags
)
918 /* Primary has open references.. create write_emu */
922 memset(&desc
, 0, sizeof(desc
));
923 desc
.dwSize
= sizeof(desc
);
924 desc
.dwFlags
= DSBCAPS_LOCHARDWARE
| (This
->primary
.flags
&DSBCAPS_CTRLPAN
);
925 desc
.dwBufferBytes
= This
->primary
.buf_size
;
926 desc
.lpwfxFormat
= &This
->primary
.format
.Format
;
928 hr
= DSBuffer_Create(&emu
, &This
->primary
, NULL
);
931 hr
= DSBuffer_Initialize(&emu
->IDirectSoundBuffer8_iface
,
932 &This
->IDirectSound_iface
, &desc
);
934 hr
= DSBuffer_GetInterface(emu
, &IID_IDirectSoundBuffer
,
935 (void**)&This
->primary
.write_emu
);
937 DSBuffer_Destroy(emu
);
941 else if(This
->prio_level
== DSSCL_WRITEPRIMARY
&& level
!= DSSCL_WRITEPRIMARY
)
944 TRACE("Nuking write_emu\n");
945 if(This
->primary
.write_emu
)
946 IDirectSoundBuffer_Release(This
->primary
.write_emu
);
947 This
->primary
.write_emu
= NULL
;
950 This
->prio_level
= level
;
952 LeaveCriticalSection(&This
->share
->crst
);
957 static HRESULT WINAPI
DS8_Compact(IDirectSound8
*iface
)
959 DSDevice
*This
= impl_from_IDirectSound8(iface
);
962 TRACE("(%p)->()\n", iface
);
966 WARN("Device not initialized\n");
967 return DSERR_UNINITIALIZED
;
970 EnterCriticalSection(&This
->share
->crst
);
971 if(This
->prio_level
< DSSCL_PRIORITY
)
973 WARN("Coop level not high enough (%lu)\n", This
->prio_level
);
974 hr
= DSERR_PRIOLEVELNEEDED
;
976 LeaveCriticalSection(&This
->share
->crst
);
981 static HRESULT WINAPI
DS8_GetSpeakerConfig(IDirectSound8
*iface
, DWORD
*config
)
983 DSDevice
*This
= impl_from_IDirectSound8(iface
);
985 TRACE("(%p)->(%p)\n", iface
, config
);
988 return DSERR_INVALIDPARAM
;
993 WARN("Device not initialized\n");
994 return DSERR_UNINITIALIZED
;
997 *config
= This
->share
->speaker_config
;
1002 static HRESULT WINAPI
DS8_SetSpeakerConfig(IDirectSound8
*iface
, DWORD config
)
1004 DSDevice
*This
= impl_from_IDirectSound8(iface
);
1007 TRACE("(%p)->(0x%08lx)\n", iface
, config
);
1011 WARN("Device not initialized\n");
1012 return DSERR_UNINITIALIZED
;
1015 geo
= DSSPEAKER_GEOMETRY(config
);
1016 speaker
= DSSPEAKER_CONFIG(config
);
1018 if(geo
&& (geo
< DSSPEAKER_GEOMETRY_MIN
|| geo
> DSSPEAKER_GEOMETRY_MAX
))
1020 WARN("Invalid speaker angle %lu\n", geo
);
1021 return DSERR_INVALIDPARAM
;
1023 if(speaker
< DSSPEAKER_HEADPHONE
|| speaker
> DSSPEAKER_7POINT1
)
1025 WARN("Invalid speaker config %lu\n", speaker
);
1026 return DSERR_INVALIDPARAM
;
1029 /* No-op on Vista+. */
1033 static HRESULT WINAPI
DS8_Initialize(IDirectSound8
*iface
, const GUID
*devguid
)
1035 DSDevice
*This
= impl_from_IDirectSound8(iface
);
1040 TRACE("(%p)->(%s)\n", iface
, debugstr_guid(devguid
));
1043 return DSERR_NODRIVER
;
1047 WARN("Device already initialized\n");
1048 return DSERR_ALREADYINITIALIZED
;
1051 if(!devguid
|| IsEqualGUID(devguid
, &GUID_NULL
))
1052 devguid
= &DSDEVID_DefaultPlayback
;
1053 else if(IsEqualGUID(devguid
, &DSDEVID_DefaultCapture
) ||
1054 IsEqualGUID(devguid
, &DSDEVID_DefaultVoiceCapture
))
1055 return DSERR_NODRIVER
;
1057 hr
= DSOAL_GetDeviceID(devguid
, &guid
);
1058 if(FAILED(hr
)) return hr
;
1060 EnterCriticalSection(&openal_crst
);
1062 TRACE("Searching shared devices for %s\n", debugstr_guid(&guid
));
1063 for(n
= 0;n
< sharelistsize
;n
++)
1065 if(IsEqualGUID(&sharelist
[n
]->guid
, &guid
))
1067 TRACE("Matched shared device %p\n", sharelist
[n
]);
1069 DSShare_AddRef(sharelist
[n
]);
1070 This
->share
= sharelist
[n
];
1076 hr
= DSShare_Create(&guid
, &This
->share
);
1079 This
->device
= This
->share
->device
;
1080 hr
= DSPrimary_PreInit(&This
->primary
, This
);
1085 DeviceShare
*share
= This
->share
;
1088 EnterCriticalSection(&share
->crst
);
1090 prims
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
,
1091 (share
->nprimaries
+1) * sizeof(*prims
));
1093 hr
= DSERR_OUTOFMEMORY
;
1097 for(i
= 0;i
< share
->nprimaries
;++i
)
1098 prims
[i
] = share
->primaries
[i
];
1099 prims
[i
] = &This
->primary
;
1101 HeapFree(GetProcessHeap(), 0, share
->primaries
);
1102 share
->primaries
= prims
;
1103 share
->nprimaries
+= 1;
1106 LeaveCriticalSection(&share
->crst
);
1112 DSShare_Release(This
->share
);
1116 LeaveCriticalSection(&openal_crst
);
1120 /* I, Maarten Lankhorst, hereby declare this driver certified
1121 * What this means.. ? An extra bit set
1123 static HRESULT WINAPI
DS8_VerifyCertification(IDirectSound8
*iface
, DWORD
*certified
)
1125 DSDevice
*This
= impl_from_IDirectSound8(iface
);
1127 TRACE("(%p)->(%p)\n", iface
, certified
);
1130 return DSERR_INVALIDPARAM
;
1135 WARN("Device not initialized\n");
1136 return DSERR_UNINITIALIZED
;
1139 *certified
= DS_CERTIFIED
;
1144 static IDirectSound8Vtbl DS8_Vtbl
= {
1148 DS8_CreateSoundBuffer
,
1150 DS8_DuplicateSoundBuffer
,
1151 DS8_SetCooperativeLevel
,
1153 DS8_GetSpeakerConfig
,
1154 DS8_SetSpeakerConfig
,
1156 DS8_VerifyCertification
1160 static HRESULT WINAPI
DS_QueryInterface(IDirectSound
*iface
, REFIID riid
, LPVOID
*ppv
)
1162 DSDevice
*This
= impl_from_IDirectSound(iface
);
1163 TRACE("(%p)->(%s, %p)\n", iface
, debugstr_guid(riid
), ppv
);
1164 return DSDevice_GetInterface(This
, riid
, ppv
);
1167 static ULONG WINAPI
DS_AddRef(IDirectSound
*iface
)
1169 DSDevice
*This
= impl_from_IDirectSound(iface
);
1170 return DS8_AddRef(&This
->IDirectSound8_iface
);
1173 static ULONG WINAPI
DS_Release(IDirectSound
*iface
)
1175 DSDevice
*This
= impl_from_IDirectSound(iface
);
1176 return DS8_Release(&This
->IDirectSound8_iface
);
1179 static HRESULT WINAPI
DS_CreateSoundBuffer(IDirectSound
*iface
, LPCDSBUFFERDESC desc
, LPLPDIRECTSOUNDBUFFER buf
, IUnknown
*pUnkOuter
)
1181 DSDevice
*This
= impl_from_IDirectSound(iface
);
1182 return DS8_CreateSoundBuffer(&This
->IDirectSound8_iface
, desc
, buf
, pUnkOuter
);
1185 static HRESULT WINAPI
DS_GetCaps(IDirectSound
*iface
, LPDSCAPS caps
)
1187 DSDevice
*This
= impl_from_IDirectSound(iface
);
1188 return DS8_GetCaps(&This
->IDirectSound8_iface
, caps
);
1190 static HRESULT WINAPI
DS_DuplicateSoundBuffer(IDirectSound
*iface
, IDirectSoundBuffer
*in
, IDirectSoundBuffer
**out
)
1192 DSDevice
*This
= impl_from_IDirectSound(iface
);
1193 return DS8_DuplicateSoundBuffer(&This
->IDirectSound8_iface
, in
, out
);
1196 static HRESULT WINAPI
DS_SetCooperativeLevel(IDirectSound
*iface
, HWND hwnd
, DWORD level
)
1198 DSDevice
*This
= impl_from_IDirectSound(iface
);
1199 return DS8_SetCooperativeLevel(&This
->IDirectSound8_iface
, hwnd
, level
);
1202 static HRESULT WINAPI
DS_Compact(IDirectSound
*iface
)
1204 DSDevice
*This
= impl_from_IDirectSound(iface
);
1205 return DS8_Compact(&This
->IDirectSound8_iface
);
1208 static HRESULT WINAPI
DS_GetSpeakerConfig(IDirectSound
*iface
, DWORD
*config
)
1210 DSDevice
*This
= impl_from_IDirectSound(iface
);
1211 return DS8_GetSpeakerConfig(&This
->IDirectSound8_iface
, config
);
1214 static HRESULT WINAPI
DS_SetSpeakerConfig(IDirectSound
*iface
, DWORD config
)
1216 DSDevice
*This
= impl_from_IDirectSound(iface
);
1217 return DS8_SetSpeakerConfig(&This
->IDirectSound8_iface
, config
);
1220 static HRESULT WINAPI
DS_Initialize(IDirectSound
*iface
, const GUID
*devguid
)
1222 DSDevice
*This
= impl_from_IDirectSound(iface
);
1223 return DS8_Initialize(&This
->IDirectSound8_iface
, devguid
);
1226 static IDirectSoundVtbl DS_Vtbl
= {
1230 DS_CreateSoundBuffer
,
1232 DS_DuplicateSoundBuffer
,
1233 DS_SetCooperativeLevel
,
1235 DS_GetSpeakerConfig
,
1236 DS_SetSpeakerConfig
,