Remove an errant early-out check
[dsound-openal.git] / dsound8.c
blob7170e2ea61d8d7ea0df9ef65195192bec9546597
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
23 #define CONST_VTABLE
24 #include <stdarg.h>
25 #include <string.h>
27 #include <windows.h>
28 #include <dsound.h>
29 #include <ks.h>
30 #include <ksmedia.h>
32 #include "dsound_private.h"
34 #ifndef DSSPEAKER_7POINT1
35 #define DSSPEAKER_7POINT1 7
36 #endif
39 static DWORD CALLBACK DSShare_thread(void *dwUser)
41 DeviceShare *share = (DeviceShare*)dwUser;
42 BYTE *scratch_mem = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, 2048);
43 ALsizei i;
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);
60 popALContext();
61 LeaveCriticalSection(&share->crst);
63 TRACE("Shared device (%p) message loop quit\n", share);
65 HeapFree(GetProcessHeap(), 0, scratch_mem);
66 scratch_mem = NULL;
68 if(local_contexts)
70 set_context(NULL);
71 TlsSetValue(TlsThreadPtr, NULL);
74 return 0;
77 static void CALLBACK DSShare_timer(void *arg, BOOLEAN unused)
79 (void)unused;
80 SetEvent((HANDLE)arg);
83 static void DSShare_starttimer(DeviceShare *share)
85 DWORD triggertime;
87 if(share->queue_timer)
88 return;
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)
105 UINT i;
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);
116 sharelist = NULL;
118 break;
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;
139 if(share->timer_evt)
140 CloseHandle(share->timer_evt);
141 share->timer_evt = NULL;
143 if(share->ctx)
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;
153 set_context(NULL);
154 TlsSetValue(TlsThreadPtr, NULL);
155 alcDestroyContext(share->ctx);
156 share->ctx = NULL;
157 LeaveCriticalSection(&openal_crst);
160 if(share->device)
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];
176 int extenum;
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;
186 ALchar drv_name[64];
187 DeviceShare *share;
188 IMMDevice *mmdev;
189 ALCint attrs[7];
190 void *temp;
191 HRESULT hr, cohr;
192 ALsizei i;
194 share = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*share));
195 if(!share) return DSERR_OUTOFMEMORY;
196 share->ref = 1;
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);
204 if(!mmdev)
205 hr = DSERR_INVALIDPARAM;
206 else
208 IPropertyStore *store;
210 hr = IMMDevice_OpenPropertyStore(mmdev, STGM_READ, &store);
211 if(FAILED(hr))
212 WARN("IMMDevice_OpenPropertyStore failed: %08lx\n", hr);
213 else
215 ULONG phys_speakers = 0;
216 PROPVARIANT pv;
218 PropVariantInit(&pv);
220 hr = IPropertyStore_GetValue(store, &PKEY_AudioEndpoint_PhysicalSpeakers, &pv);
221 if(FAILED(hr))
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);
225 else
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;
244 else
245 FIXME("Unhandled physical speaker layout: 0x%08lx\n", phys_speakers);
246 #undef BIT_MATCH
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);
255 if(FAILED(hr))
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);
259 else
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);
275 mmdev = NULL;
278 InitializeCriticalSection(&share->crst);
280 hr = StringFromCLSID(guid, &guid_str);
281 if(FAILED(hr))
283 ERR("Failed to convert GUID to string\n");
284 goto fail;
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);
289 guid_str = NULL;
291 hr = DSERR_NODRIVER;
292 share->device = alcOpenDevice(drv_name);
293 if(!share->device)
295 alcGetError(NULL);
296 WARN("Couldn't open device \"%s\"\n", drv_name);
297 goto fail;
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));
304 i = 0;
305 attrs[i++] = ALC_MONO_SOURCES;
306 attrs[i++] = MAX_SOURCES;
307 attrs[i++] = ALC_STEREO_SOURCES;
308 attrs[i++] = 0;
309 attrs[i++] = 0;
310 share->ctx = alcCreateContext(share->device, attrs);
311 if(!share->ctx)
313 ALCenum err = alcGetError(share->device);
314 ERR("Could not create context (0x%x)!\n", err);
315 goto fail;
318 share->guid = *guid;
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];
339 popALContext();
341 hr = E_OUTOFMEMORY;
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);
348 goto fail;
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;
361 else
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);
371 if(sharelist)
372 temp = HeapReAlloc(GetProcessHeap(), 0, sharelist, sizeof(*sharelist)*(sharelistsize+1));
373 else
374 temp = HeapAlloc(GetProcessHeap(), 0, sizeof(*sharelist)*(sharelistsize+1));
375 if(temp)
377 sharelist = temp;
378 sharelist[sharelistsize++] = share;
381 hr = E_FAIL;
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);
394 *out = share;
395 return DS_OK;
397 fail:
398 DSShare_Destroy(share);
399 return hr;
402 static ULONG DSShare_AddRef(DeviceShare *share)
404 ULONG ref = InterlockedIncrement(&share->ref);
405 return ref;
408 static ULONG DSShare_Release(DeviceShare *share)
410 ULONG ref = InterlockedDecrement(&share->ref);
411 if(ref == 0) DSShare_Destroy(share);
412 return ref;
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 /*******************************************************************************
425 * IUnknown
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);
442 ULONG ref;
444 InterlockedIncrement(&(This->ref));
445 ref = InterlockedIncrement(&(This->unkref));
446 TRACE("(%p) ref %lu\n", iface, ref);
448 return 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);
458 return ref;
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)
487 DSDevice *This;
488 HRESULT hr;
490 *ds = NULL;
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;
499 This->is_8 = is8;
501 hr = DSDevice_GetInterface(This, riid, ds);
502 if(FAILED(hr)) DSDevice_Destroy(This);
503 return hr;
506 static void DSDevice_Destroy(DSDevice *This)
508 DeviceShare *share = This->share;
510 TRACE("Destroying device instance %p\n", This);
511 if(share)
513 ALsizei i;
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];
523 break;
527 LeaveCriticalSection(&share->crst);
530 DSPrimary_Clear(&This->primary);
531 if(This->share)
532 DSShare_Release(This->share);
533 This->share = NULL;
535 HeapFree(GetProcessHeap(), 0, This);
538 static HRESULT DSDevice_GetInterface(DSDevice *This, REFIID riid, LPVOID *ppv)
540 *ppv = NULL;
541 if(IsEqualIID(riid, &IID_IUnknown))
542 *ppv = &This->IUnknown_iface;
543 else if(IsEqualIID(riid, &IID_IDirectSound8))
545 if(This->is_8)
546 *ppv = &This->IDirectSound8_iface;
548 else if(IsEqualIID(riid, &IID_IDirectSound))
549 *ppv = &This->IDirectSound_iface;
550 else
551 FIXME("Unhandled GUID: %s\n", debugstr_guid(riid));
553 if(*ppv)
555 IUnknown_AddRef((IUnknown*)*ppv);
556 return S_OK;
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);
573 LONG ref;
575 InterlockedIncrement(&This->ref);
576 ref = InterlockedIncrement(&This->dsref);
577 TRACE("(%p) ref %lu\n", iface, ref);
579 return ref;
582 static ULONG WINAPI DS8_Release(IDirectSound8 *iface)
584 DSDevice *This = impl_from_IDirectSound8(iface);
585 LONG ref;
587 ref = InterlockedDecrement(&This->dsref);
588 TRACE("(%p) ref %lu\n", iface, ref);
589 if(InterlockedDecrement(&This->ref) == 0)
590 DSDevice_Destroy(This);
592 return ref;
595 static HRESULT WINAPI DS8_CreateSoundBuffer(IDirectSound8 *iface, LPCDSBUFFERDESC desc, LPLPDIRECTSOUNDBUFFER buf, IUnknown *pUnkOuter)
597 DSDevice *This = impl_from_IDirectSound8(iface);
598 HRESULT hr;
600 TRACE("(%p)->(%p, %p, %p)\n", iface, desc, buf, pUnkOuter);
602 if(!buf)
604 WARN("buf is null\n");
605 return DSERR_INVALIDPARAM;
607 *buf = NULL;
609 if(pUnkOuter)
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;
620 if(!This->share)
622 WARN("Device not initialized\n");
623 return DSERR_UNINITIALIZED;
626 TRACE("Requested buffer:\n"
627 " Size = %lu\n"
628 " Flags = 0x%08lx\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));
643 else
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))
650 if(!This->is_8)
652 static int once = 0;
653 if(!once++)
654 FIXME("Buffers with 3D and panning control ignore panning\n");
656 else
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;
668 hr = S_OK;
669 if(IDirectSoundBuffer_AddRef(prim) == 1)
671 hr = DSPrimary_Initialize(prim, &This->IDirectSound_iface, desc);
672 if(FAILED(hr))
674 IDirectSoundBuffer_Release(prim);
675 prim = NULL;
678 *buf = prim;
680 else
682 DSBuffer *dsb;
684 hr = DSBuffer_Create(&dsb, &This->primary, NULL);
685 if(SUCCEEDED(hr))
687 hr = DSBuffer_Initialize(&dsb->IDirectSoundBuffer8_iface, &This->IDirectSound_iface, desc);
688 if(SUCCEEDED(hr))
690 dsb->bufferlost = (This->prio_level == DSSCL_WRITEPRIMARY);
691 hr = DSBuffer_GetInterface(dsb, &IID_IDirectSoundBuffer, (void**)buf);
693 if(FAILED(hr))
694 DSBuffer_Destroy(dsb);
697 LeaveCriticalSection(&This->share->crst);
699 TRACE("%08lx\n", hr);
700 return hr;
703 static HRESULT WINAPI DS8_GetCaps(IDirectSound8 *iface, LPDSCAPS caps)
705 DSDevice *This = impl_from_IDirectSound8(iface);
706 struct DSBufferGroup *bufgroup, *endgroup;
707 DWORD free_bufs;
709 TRACE("(%p)->(%p)\n", iface, caps);
711 if(!This->share)
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;
731 while(usemask)
733 int idx = CTZ64(usemask);
734 DSBuffer *buf = bufgroup->Buffers + idx;
735 usemask &= ~(U64(1) << idx);
737 if(buf->loc_status == DSBSTATUS_LOCHARDWARE)
739 if(!--free_bufs)
740 break;
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);
773 return DS_OK;
775 static HRESULT WINAPI DS8_DuplicateSoundBuffer(IDirectSound8 *iface, IDirectSoundBuffer *in, IDirectSoundBuffer **out)
777 DSDevice *This = impl_from_IDirectSound8(iface);
778 DSBuffer *buf = NULL;
779 DSBCAPS caps;
780 HRESULT hr;
782 TRACE("(%p)->(%p, %p)\n", iface, in, out);
784 if(!This->share)
786 WARN("Device not initialized\n");
787 return DSERR_UNINITIALIZED;
790 if(!in || !out)
792 WARN("Invalid pointer: in = %p, out = %p\n", in, out);
793 return DSERR_INVALIDPARAM;
795 *out = NULL;
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;
809 if(SUCCEEDED(hr))
810 hr = DSBuffer_Create(&buf, &This->primary, in);
811 if(SUCCEEDED(hr))
813 hr = DSBuffer_Initialize(&buf->IDirectSoundBuffer8_iface, NULL, NULL);
814 if(SUCCEEDED(hr))
815 hr = DSBuffer_GetInterface(buf, &IID_IDirectSoundBuffer, (void**)out);
816 if(FAILED(hr))
817 DSBuffer_Destroy(buf);
820 return hr;
823 static HRESULT WINAPI DS8_SetCooperativeLevel(IDirectSound8 *iface, HWND hwnd, DWORD level)
825 DSDevice *This = impl_from_IDirectSound8(iface);
826 HRESULT hr = S_OK;
828 TRACE("(%p)->(%p, %lu)\n", iface, hwnd, level);
830 if(!This->share)
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;
846 DWORD i, state;
848 if(This->primary.write_emu)
850 ERR("Why was there a write_emu?\n");
851 /* Delete it */
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;
859 while(usemask)
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;
870 goto out;
872 /* Mark buffer as lost */
873 buf->bufferlost = 1;
877 if(This->primary.flags)
879 /* Primary has open references.. create write_emu */
880 DSBUFFERDESC desc;
881 DSBuffer *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);
890 if(SUCCEEDED(hr))
892 hr = DSBuffer_Initialize(&emu->IDirectSoundBuffer8_iface,
893 &This->IDirectSound_iface, &desc);
894 if(SUCCEEDED(hr))
895 hr = DSBuffer_GetInterface(emu, &IID_IDirectSoundBuffer,
896 (void**)&This->primary.write_emu);
897 if(FAILED(hr))
898 DSBuffer_Destroy(emu);
902 else if(This->prio_level == DSSCL_WRITEPRIMARY && level != DSSCL_WRITEPRIMARY)
904 /* Delete it */
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;
910 if(SUCCEEDED(hr))
911 This->prio_level = level;
912 out:
913 LeaveCriticalSection(&This->share->crst);
915 return hr;
918 static HRESULT WINAPI DS8_Compact(IDirectSound8 *iface)
920 DSDevice *This = impl_from_IDirectSound8(iface);
921 HRESULT hr = S_OK;
923 TRACE("(%p)->()\n", iface);
925 if(!This->share)
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);
939 return hr;
942 static HRESULT WINAPI DS8_GetSpeakerConfig(IDirectSound8 *iface, DWORD *config)
944 DSDevice *This = impl_from_IDirectSound8(iface);
946 TRACE("(%p)->(%p)\n", iface, config);
948 if(!config)
949 return DSERR_INVALIDPARAM;
950 *config = 0;
952 if(!This->share)
954 WARN("Device not initialized\n");
955 return DSERR_UNINITIALIZED;
958 *config = This->share->speaker_config;
960 return DS_OK;
963 static HRESULT WINAPI DS8_SetSpeakerConfig(IDirectSound8 *iface, DWORD config)
965 DSDevice *This = impl_from_IDirectSound8(iface);
966 DWORD geo, speaker;
968 TRACE("(%p)->(0x%08lx)\n", iface, config);
970 if(!This->share)
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+. */
991 return DS_OK;
994 static HRESULT WINAPI DS8_Initialize(IDirectSound8 *iface, const GUID *devguid)
996 DSDevice *This = impl_from_IDirectSound8(iface);
997 HRESULT hr;
998 GUID guid;
999 UINT n;
1001 TRACE("(%p)->(%s)\n", iface, debugstr_guid(devguid));
1003 if(!openal_loaded)
1004 return DSERR_NODRIVER;
1006 if(This->share)
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];
1032 break;
1036 if(!This->share)
1037 hr = DSShare_Create(&guid, &This->share);
1038 if(SUCCEEDED(hr))
1040 This->device = This->share->device;
1041 hr = DSPrimary_PreInit(&This->primary, This);
1044 if(SUCCEEDED(hr))
1046 DeviceShare *share = This->share;
1047 DSPrimary **prims;
1049 EnterCriticalSection(&share->crst);
1051 prims = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
1052 (share->nprimaries+1) * sizeof(*prims));
1053 if(!prims)
1054 hr = DSERR_OUTOFMEMORY;
1055 else
1057 ALsizei i;
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);
1070 if(FAILED(hr))
1072 if(This->share)
1073 DSShare_Release(This->share);
1074 This->share = NULL;
1077 LeaveCriticalSection(&openal_crst);
1078 return hr;
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);
1090 if(!certified)
1091 return DSERR_INVALIDPARAM;
1092 *certified = 0;
1094 if(!This->share)
1096 WARN("Device not initialized\n");
1097 return DSERR_UNINITIALIZED;
1100 *certified = DS_CERTIFIED;
1102 return DS_OK;
1105 static IDirectSound8Vtbl DS8_Vtbl = {
1106 DS8_QueryInterface,
1107 DS8_AddRef,
1108 DS8_Release,
1109 DS8_CreateSoundBuffer,
1110 DS8_GetCaps,
1111 DS8_DuplicateSoundBuffer,
1112 DS8_SetCooperativeLevel,
1113 DS8_Compact,
1114 DS8_GetSpeakerConfig,
1115 DS8_SetSpeakerConfig,
1116 DS8_Initialize,
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 = {
1188 DS_QueryInterface,
1189 DS_AddRef,
1190 DS_Release,
1191 DS_CreateSoundBuffer,
1192 DS_GetCaps,
1193 DS_DuplicateSoundBuffer,
1194 DS_SetCooperativeLevel,
1195 DS_Compact,
1196 DS_GetSpeakerConfig,
1197 DS_SetSpeakerConfig,
1198 DS_Initialize