Report the EAX last error as settable
[dsound-openal.git] / dsound8.c
blob89c6a14c374e082d22013820af725261665f7232
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 <ksmedia.h>
31 #include "dsound_private.h"
33 #ifndef DSSPEAKER_7POINT1
34 #define DSSPEAKER_7POINT1 7
35 #endif
38 static DWORD CALLBACK DSShare_thread(void *dwUser)
40 DeviceShare *share = (DeviceShare*)dwUser;
41 BYTE *scratch_mem = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, 2048);
42 ALsizei i;
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 DSPrimary_triggernots(share->primaries[i]);
55 if(!HAS_EXTENSION(share, SOFTX_MAP_BUFFER))
56 DSPrimary_streamfeeder(share->primaries[i], scratch_mem);
59 popALContext();
60 LeaveCriticalSection(&share->crst);
62 TRACE("Shared device (%p) message loop quit\n", share);
64 HeapFree(GetProcessHeap(), 0, scratch_mem);
65 scratch_mem = NULL;
67 if(local_contexts)
69 set_context(NULL);
70 TlsSetValue(TlsThreadPtr, NULL);
73 return 0;
76 static void CALLBACK DSShare_timer(void *arg, BOOLEAN unused)
78 (void)unused;
79 SetEvent((HANDLE)arg);
82 static void DSShare_starttimer(DeviceShare *share)
84 DWORD triggertime;
86 if(share->queue_timer)
87 return;
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)
104 UINT i;
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);
115 sharelist = NULL;
117 break;
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;
138 if(share->timer_evt)
139 CloseHandle(share->timer_evt);
140 share->timer_evt = NULL;
142 if(share->ctx)
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.maxhw_alloc + share->sources.maxsw_alloc)
151 alDeleteSources(share->sources.maxhw_alloc+share->sources.maxsw_alloc,
152 share->sources.ids);
153 share->sources.maxhw_alloc = share->sources.maxsw_alloc = 0;
155 if(share->auxslot[3])
156 alDeleteAuxiliaryEffectSlots(4, share->auxslot);
157 else if(share->auxslot[2])
158 alDeleteAuxiliaryEffectSlots(3, share->auxslot);
159 else if(share->auxslot[1])
160 alDeleteAuxiliaryEffectSlots(2, share->auxslot);
161 else if(share->auxslot[0])
162 alDeleteAuxiliaryEffectSlots(1, share->auxslot);
163 share->auxslot[0] = share->auxslot[1] =
164 share->auxslot[2] = share->auxslot[3] = 0;
166 set_context(NULL);
167 TlsSetValue(TlsThreadPtr, NULL);
168 alcDestroyContext(share->ctx);
169 share->ctx = NULL;
170 LeaveCriticalSection(&openal_crst);
173 if(share->device)
174 alcCloseDevice(share->device);
175 share->device = NULL;
177 DeleteCriticalSection(&share->crst);
179 HeapFree(GetProcessHeap(), 0, share->primaries);
180 HeapFree(GetProcessHeap(), 0, share);
182 TRACE("Closed shared device %p\n", share);
185 static HRESULT DSShare_Create(REFIID guid, DeviceShare **out)
187 static const struct {
188 const char extname[64];
189 int extenum;
190 } extensions[MAX_EXTENSIONS] = {
191 { "ALC_EXT_EFX", EXT_EFX },
192 { "AL_EXT_FLOAT32", EXT_FLOAT32 },
193 { "AL_EXT_MCFORMATS", EXT_MCFORMATS },
194 { "AL_SOFT_deferred_updates", SOFT_DEFERRED_UPDATES },
195 { "AL_SOFT_source_spatialize", SOFT_SOURCE_SPATIALIZE },
196 { "AL_SOFTX_filter_gain_ex", SOFTX_FILTER_GAIN_EX },
197 { "AL_SOFTX_map_buffer", SOFTX_MAP_BUFFER },
199 OLECHAR *guid_str = NULL;
200 ALchar drv_name[64];
201 DeviceShare *share;
202 IMMDevice *mmdev;
203 ALCint attrs[3];
204 void *temp;
205 HRESULT hr;
206 ALsizei i;
208 share = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*share));
209 if(!share) return DSERR_OUTOFMEMORY;
210 share->ref = 1;
211 share->refresh = FAKE_REFRESH_COUNT;
212 share->speaker_config = DSSPEAKER_7POINT1_SURROUND;
214 TRACE("Creating shared device %p\n", share);
216 hr = get_mmdevice(eRender, guid, &mmdev);
217 if(SUCCEEDED(hr))
219 IPropertyStore *store;
221 hr = IMMDevice_OpenPropertyStore(mmdev, STGM_READ, &store);
222 if(FAILED(hr))
223 WARN("IMMDevice_OpenPropertyStore failed: %08lx\n", hr);
224 else
226 ULONG phys_speakers = 0;
227 PROPVARIANT pv;
229 PropVariantInit(&pv);
231 hr = IPropertyStore_GetValue(store, &PKEY_AudioEndpoint_PhysicalSpeakers, &pv);
232 if(FAILED(hr))
233 WARN("IPropertyStore_GetValue failed: %08lx\n", hr);
234 else if(pv.vt != VT_UI4)
235 WARN("PKEY_AudioEndpoint_PhysicalSpeakers is not a ULONG: 0x%04x\n", pv.vt);
236 else
238 phys_speakers = pv.ulVal;
240 #define BIT_MATCH(v, b) (((v)&(b)) == (b))
241 if(BIT_MATCH(phys_speakers, KSAUDIO_SPEAKER_7POINT1))
242 share->speaker_config = DSSPEAKER_7POINT1;
243 else if(BIT_MATCH(phys_speakers, KSAUDIO_SPEAKER_7POINT1_SURROUND))
244 share->speaker_config = DSSPEAKER_7POINT1_SURROUND;
245 else if(BIT_MATCH(phys_speakers, KSAUDIO_SPEAKER_5POINT1))
246 share->speaker_config = DSSPEAKER_5POINT1_BACK;
247 else if(BIT_MATCH(phys_speakers, KSAUDIO_SPEAKER_5POINT1_SURROUND))
248 share->speaker_config = DSSPEAKER_5POINT1_SURROUND;
249 else if(BIT_MATCH(phys_speakers, KSAUDIO_SPEAKER_QUAD))
250 share->speaker_config = DSSPEAKER_QUAD;
251 else if(BIT_MATCH(phys_speakers, KSAUDIO_SPEAKER_STEREO))
252 share->speaker_config = DSSPEAKER_COMBINED(DSSPEAKER_STEREO, DSSPEAKER_GEOMETRY_WIDE);
253 else if(BIT_MATCH(phys_speakers, KSAUDIO_SPEAKER_MONO))
254 share->speaker_config = DSSPEAKER_MONO;
255 else
256 FIXME("Unhandled physical speaker layout: 0x%08lx\n", phys_speakers);
257 #undef BIT_MATCH
260 /* If the device has a stereo layout, check the formfactor to see
261 * if it's really headphones/headset.
263 if(DSSPEAKER_CONFIG(share->speaker_config) == DSSPEAKER_STEREO)
265 hr = IPropertyStore_GetValue(store, &PKEY_AudioEndpoint_FormFactor, &pv);
266 if(FAILED(hr))
267 WARN("IPropertyStore_GetValue failed: %08lx\n", hr);
268 else if(pv.vt != VT_UI4)
269 WARN("PKEY_AudioEndpoint_FormFactor is not a ULONG: 0x%04x\n", pv.vt);
270 else
272 if(pv.ulVal == Headphones || pv.ulVal == Headset)
273 share->speaker_config = DSSPEAKER_HEADPHONE;
277 TRACE("Got speaker config %d:%d from physical speakers 0x%08lx\n",
278 DSSPEAKER_GEOMETRY(share->speaker_config),
279 DSSPEAKER_CONFIG(share->speaker_config), phys_speakers);
281 PropVariantClear(&pv);
282 IPropertyStore_Release(store);
285 IMMDevice_Release(mmdev);
286 mmdev = NULL;
289 InitializeCriticalSection(&share->crst);
291 hr = StringFromCLSID(guid, &guid_str);
292 if(FAILED(hr))
294 ERR("Failed to convert GUID to string\n");
295 goto fail;
297 WideCharToMultiByte(CP_UTF8, 0, guid_str, -1, drv_name, sizeof(drv_name), NULL, NULL);
298 drv_name[sizeof(drv_name)-1] = 0;
299 CoTaskMemFree(guid_str);
300 guid_str = NULL;
302 hr = DSERR_NODRIVER;
303 share->device = alcOpenDevice(drv_name);
304 if(!share->device)
306 alcGetError(NULL);
307 WARN("Couldn't open device \"%s\"\n", drv_name);
308 goto fail;
310 TRACE("Opened AL device: %s\n",
311 alcIsExtensionPresent(share->device, "ALC_ENUMERATE_ALL_EXT") ?
312 alcGetString(share->device, ALC_ALL_DEVICES_SPECIFIER) :
313 alcGetString(share->device, ALC_DEVICE_SPECIFIER));
315 attrs[0] = ALC_MONO_SOURCES;
316 attrs[1] = MAX_SOURCES;
317 attrs[2] = 0;
318 share->ctx = alcCreateContext(share->device, attrs);
319 if(!share->ctx)
321 ALCenum err = alcGetError(share->device);
322 ERR("Could not create context (0x%x)!\n", err);
323 goto fail;
326 share->guid = *guid;
328 setALContext(share->ctx);
329 alcGetIntegerv(share->device, ALC_REFRESH, 1, &share->refresh);
330 checkALCError(share->device);
332 for(i = 0;i < MAX_EXTENSIONS;i++)
334 if((strncmp(extensions[i].extname, "ALC", 3) == 0) ?
335 alcIsExtensionPresent(share->device, extensions[i].extname) :
336 alIsExtensionPresent(extensions[i].extname))
338 TRACE("Found %s\n", extensions[i].extname);
339 BITFIELD_SET(share->Exts, extensions[i].extenum);
343 share->sources.maxhw_alloc = 0;
344 while(share->sources.maxhw_alloc < MAX_SOURCES)
346 alGenSources(1, &share->sources.ids[share->sources.maxhw_alloc]);
347 if(alGetError() != AL_NO_ERROR) break;
348 share->sources.maxhw_alloc++;
351 if(HAS_EXTENSION(share, EXT_EFX))
353 alcGetIntegerv(share->device, ALC_MAX_AUXILIARY_SENDS, 1, &share->num_sends);
354 checkALCError(share->device);
355 if(share->num_sends > EAX_MAX_ACTIVE_FXSLOTS)
356 share->num_sends = EAX_MAX_ACTIVE_FXSLOTS;
357 TRACE("Got %d auxiliary source send%s\n", share->num_sends, (share->num_sends==1)?"":"s");
359 for(i = 0;i < EAX_MAX_FXSLOTS;++i)
361 alGenAuxiliaryEffectSlots(1, &share->auxslot[i]);
362 if(alGetError() != AL_NO_ERROR) break;
364 TRACE("Allocated %d auxiliary effect slot%s\n", i, (i==1)?"":"s");
365 while(i < EAX_MAX_FXSLOTS)
366 share->auxslot[i++] = 0;
368 popALContext();
370 hr = E_OUTOFMEMORY;
371 if(share->sources.maxhw_alloc < 128)
373 ERR("Could only allocate %lu sources (minimum 128 required)\n",
374 share->sources.maxhw_alloc);
375 goto fail;
378 if(share->sources.maxhw_alloc > MAX_HWBUFFERS)
380 share->sources.maxsw_alloc = share->sources.maxhw_alloc - MAX_HWBUFFERS;
381 share->sources.maxhw_alloc = MAX_HWBUFFERS;
383 else if(share->sources.maxhw_alloc > MAX_HWBUFFERS/2)
385 share->sources.maxsw_alloc = share->sources.maxhw_alloc - MAX_HWBUFFERS/2;
386 share->sources.maxhw_alloc = MAX_HWBUFFERS/2;
388 else
390 share->sources.maxsw_alloc = share->sources.maxhw_alloc - MAX_HWBUFFERS/4;
391 share->sources.maxhw_alloc = MAX_HWBUFFERS/4;
393 share->sources.availhw_num = share->sources.maxhw_alloc;
394 share->sources.availsw_num = share->sources.maxsw_alloc;
395 TRACE("Allocated %lu hardware sources and %lu software sources\n",
396 share->sources.maxhw_alloc, share->sources.maxsw_alloc);
398 if(sharelist)
399 temp = HeapReAlloc(GetProcessHeap(), 0, sharelist, sizeof(*sharelist)*(sharelistsize+1));
400 else
401 temp = HeapAlloc(GetProcessHeap(), 0, sizeof(*sharelist)*(sharelistsize+1));
402 if(temp)
404 sharelist = temp;
405 sharelist[sharelistsize++] = share;
408 hr = E_FAIL;
410 share->quit_now = FALSE;
411 share->timer_evt = CreateEventA(NULL, FALSE, FALSE, NULL);
412 if(!share->timer_evt) goto fail;
414 share->queue_timer = NULL;
416 share->thread_hdl = CreateThread(NULL, 0, DSShare_thread, share, 0, &share->thread_id);
417 if(!share->thread_hdl) goto fail;
419 DSShare_starttimer(share);
421 *out = share;
422 return DS_OK;
424 fail:
425 DSShare_Destroy(share);
426 return hr;
429 static ULONG DSShare_AddRef(DeviceShare *share)
431 ULONG ref = InterlockedIncrement(&share->ref);
432 return ref;
435 static ULONG DSShare_Release(DeviceShare *share)
437 ULONG ref = InterlockedDecrement(&share->ref);
438 if(ref == 0) DSShare_Destroy(share);
439 return ref;
443 static const IDirectSound8Vtbl DS8_Vtbl;
444 static const IDirectSoundVtbl DS_Vtbl;
445 static const IUnknownVtbl DS8_Unknown_Vtbl;
447 static HRESULT DSDevice_Create(BOOL is8, REFIID riid, LPVOID *ds);
448 static void DSDevice_Destroy(DSDevice *This);
449 static HRESULT DSDevice_GetInterface(DSDevice *This, REFIID riid, LPVOID *ppv);
451 /*******************************************************************************
452 * IUnknown
454 static inline DSDevice *impl_from_IUnknown(IUnknown *iface)
456 return CONTAINING_RECORD(iface, DSDevice, IUnknown_iface);
459 static HRESULT WINAPI DSDevice_IUnknown_QueryInterface(IUnknown *iface, REFIID riid, void **ppobj)
461 DSDevice *This = impl_from_IUnknown(iface);
462 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppobj);
463 return DSDevice_GetInterface(This, riid, ppobj);
466 static ULONG WINAPI DSDevice_IUnknown_AddRef(IUnknown *iface)
468 DSDevice *This = impl_from_IUnknown(iface);
469 ULONG ref;
471 InterlockedIncrement(&(This->ref));
472 ref = InterlockedIncrement(&(This->unkref));
473 TRACE("(%p) ref %lu\n", iface, ref);
475 return ref;
478 static ULONG WINAPI DSDevice_IUnknown_Release(IUnknown *iface)
480 DSDevice *This = impl_from_IUnknown(iface);
481 ULONG ref = InterlockedDecrement(&(This->unkref));
482 TRACE("(%p) ref %lu\n", iface, ref);
483 if(InterlockedDecrement(&(This->ref)) == 0)
484 DSDevice_Destroy(This);
485 return ref;
488 static const IUnknownVtbl DS8_Unknown_Vtbl = {
489 DSDevice_IUnknown_QueryInterface,
490 DSDevice_IUnknown_AddRef,
491 DSDevice_IUnknown_Release
495 static inline DSDevice *impl_from_IDirectSound8(IDirectSound8 *iface)
497 return CONTAINING_RECORD(iface, DSDevice, IDirectSound8_iface);
500 static inline DSDevice *impl_from_IDirectSound(IDirectSound *iface)
502 return CONTAINING_RECORD(iface, DSDevice, IDirectSound_iface);
506 HRESULT DSOUND_Create(REFIID riid, void **ds)
507 { return DSDevice_Create(FALSE, riid, ds); }
509 HRESULT DSOUND_Create8(REFIID riid, LPVOID *ds)
510 { return DSDevice_Create(TRUE, riid, ds); }
512 static HRESULT DSDevice_Create(BOOL is8, REFIID riid, LPVOID *ds)
514 DSDevice *This;
515 HRESULT hr;
517 *ds = NULL;
518 This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*This));
519 if(!This) return DSERR_OUTOFMEMORY;
521 TRACE("Creating device instance %p\n", This);
522 This->IDirectSound8_iface.lpVtbl = &DS8_Vtbl;
523 This->IDirectSound_iface.lpVtbl = &DS_Vtbl;
524 This->IUnknown_iface.lpVtbl = &DS8_Unknown_Vtbl;
526 This->is_8 = is8;
528 hr = DSDevice_GetInterface(This, riid, ds);
529 if(FAILED(hr)) DSDevice_Destroy(This);
530 return hr;
533 static void DSDevice_Destroy(DSDevice *This)
535 DeviceShare *share = This->share;
537 TRACE("Destroying device instance %p\n", This);
538 if(share)
540 ALsizei i;
542 EnterCriticalSection(&share->crst);
544 for(i = 0;i < share->nprimaries;++i)
546 if(share->primaries[i] == &This->primary)
548 share->nprimaries -= 1;
549 share->primaries[i] = share->primaries[share->nprimaries];
550 break;
554 LeaveCriticalSection(&share->crst);
557 DSPrimary_Clear(&This->primary);
558 if(This->share)
559 DSShare_Release(This->share);
560 This->share = NULL;
562 HeapFree(GetProcessHeap(), 0, This);
565 static HRESULT DSDevice_GetInterface(DSDevice *This, REFIID riid, LPVOID *ppv)
567 *ppv = NULL;
568 if(IsEqualIID(riid, &IID_IUnknown))
569 *ppv = &This->IUnknown_iface;
570 else if(IsEqualIID(riid, &IID_IDirectSound8))
572 if(This->is_8)
573 *ppv = &This->IDirectSound8_iface;
575 else if(IsEqualIID(riid, &IID_IDirectSound))
576 *ppv = &This->IDirectSound_iface;
577 else
578 FIXME("Unhandled GUID: %s\n", debugstr_guid(riid));
580 if(*ppv)
582 IUnknown_AddRef((IUnknown*)*ppv);
583 return S_OK;
586 return E_NOINTERFACE;
590 static HRESULT WINAPI DS8_QueryInterface(IDirectSound8 *iface, REFIID riid, LPVOID *ppv)
592 DSDevice *This = impl_from_IDirectSound8(iface);
593 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
594 return DSDevice_GetInterface(This, riid, ppv);
597 static ULONG WINAPI DS8_AddRef(IDirectSound8 *iface)
599 DSDevice *This = impl_from_IDirectSound8(iface);
600 LONG ref;
602 InterlockedIncrement(&This->ref);
603 ref = InterlockedIncrement(&This->dsref);
604 TRACE("(%p) ref %lu\n", iface, ref);
606 return ref;
609 static ULONG WINAPI DS8_Release(IDirectSound8 *iface)
611 DSDevice *This = impl_from_IDirectSound8(iface);
612 LONG ref;
614 ref = InterlockedDecrement(&This->dsref);
615 TRACE("(%p) ref %lu\n", iface, ref);
616 if(InterlockedDecrement(&This->ref) == 0)
617 DSDevice_Destroy(This);
619 return ref;
622 static HRESULT WINAPI DS8_CreateSoundBuffer(IDirectSound8 *iface, LPCDSBUFFERDESC desc, LPLPDIRECTSOUNDBUFFER buf, IUnknown *pUnkOuter)
624 DSDevice *This = impl_from_IDirectSound8(iface);
625 HRESULT hr;
627 TRACE("(%p)->(%p, %p, %p)\n", iface, desc, buf, pUnkOuter);
629 if(!buf)
631 WARN("buf is null\n");
632 return DSERR_INVALIDPARAM;
634 *buf = NULL;
636 if(pUnkOuter)
638 WARN("Aggregation isn't supported\n");
639 return DSERR_NOAGGREGATION;
641 if(!desc || desc->dwSize < sizeof(DSBUFFERDESC1))
643 WARN("Invalid buffer %p/%lu\n", desc, desc?desc->dwSize:0);
644 return DSERR_INVALIDPARAM;
647 if(!This->share)
649 WARN("Device not initialized\n");
650 return DSERR_UNINITIALIZED;
653 TRACE("Requested buffer:\n"
654 " Size = %lu\n"
655 " Flags = 0x%08lx\n"
656 " BufferBytes = %lu\n",
657 desc->dwSize, desc->dwFlags, desc->dwBufferBytes);
659 if(desc->dwSize >= sizeof(DSBUFFERDESC))
661 if(!(desc->dwFlags&DSBCAPS_CTRL3D))
663 if(!IsEqualGUID(&desc->guid3DAlgorithm, &GUID_NULL))
665 WARN("Invalid 3D algorithm GUID specified for non-3D buffer: %s\n", debugstr_guid(&desc->guid3DAlgorithm));
666 return DSERR_INVALIDPARAM;
669 else
670 TRACE("Requested 3D algorithm GUID: %s\n", debugstr_guid(&desc->guid3DAlgorithm));
673 /* OpenAL doesn't support playing with 3d and panning at same time.. */
674 if((desc->dwFlags&(DSBCAPS_CTRL3D|DSBCAPS_CTRLPAN)) == (DSBCAPS_CTRL3D|DSBCAPS_CTRLPAN))
676 if(!This->is_8)
678 static int once = 0;
679 if(!once++)
680 FIXME("Buffers with 3D and panning control ignore panning\n");
682 else
684 WARN("Cannot create buffers with 3D and panning control\n");
685 return DSERR_INVALIDPARAM;
689 EnterCriticalSection(&This->share->crst);
690 if((desc->dwFlags&DSBCAPS_PRIMARYBUFFER))
692 IDirectSoundBuffer *prim = &This->primary.IDirectSoundBuffer_iface;
694 hr = S_OK;
695 if(IDirectSoundBuffer_AddRef(prim) == 1)
697 hr = DSPrimary_Initialize(prim, &This->IDirectSound_iface, desc);
698 if(FAILED(hr))
700 IDirectSoundBuffer_Release(prim);
701 prim = NULL;
704 *buf = prim;
706 else
708 DSBuffer *dsb;
710 hr = DSBuffer_Create(&dsb, &This->primary, NULL);
711 if(SUCCEEDED(hr))
713 hr = DSBuffer_Initialize(&dsb->IDirectSoundBuffer8_iface, &This->IDirectSound_iface, desc);
714 if(SUCCEEDED(hr))
716 dsb->bufferlost = (This->prio_level == DSSCL_WRITEPRIMARY);
717 hr = DSBuffer_GetInterface(dsb, &IID_IDirectSoundBuffer, (void**)buf);
719 if(FAILED(hr))
720 DSBuffer_Destroy(dsb);
723 LeaveCriticalSection(&This->share->crst);
725 TRACE("%08lx\n", hr);
726 return hr;
729 static HRESULT WINAPI DS8_GetCaps(IDirectSound8 *iface, LPDSCAPS caps)
731 DSDevice *This = impl_from_IDirectSound8(iface);
732 struct DSBufferGroup *bufgroup, *endgroup;
733 DWORD free_bufs;
735 TRACE("(%p)->(%p)\n", iface, caps);
737 if(!This->share)
739 WARN("Device not initialized\n");
740 return DSERR_UNINITIALIZED;
743 if(!caps || caps->dwSize < sizeof(*caps))
745 WARN("Invalid DSCAPS (%p, %lu)\n", caps, (caps?caps->dwSize:0));
746 return DSERR_INVALIDPARAM;
749 EnterCriticalSection(&This->share->crst);
751 free_bufs = This->share->sources.maxhw_alloc;
752 bufgroup = This->primary.BufferGroups;
753 endgroup = bufgroup + This->primary.NumBufferGroups;
754 for(;free_bufs && bufgroup != endgroup;++bufgroup)
756 DWORD64 usemask = ~bufgroup->FreeBuffers;
757 while(usemask)
759 int idx = CTZ64(usemask);
760 DSBuffer *buf = bufgroup->Buffers + idx;
761 usemask &= ~(U64(1) << idx);
763 if(buf->loc_status == DSBSTATUS_LOCHARDWARE)
765 if(!--free_bufs)
766 break;
771 caps->dwFlags = DSCAPS_CONTINUOUSRATE | DSCAPS_CERTIFIED |
772 DSCAPS_PRIMARY16BIT | DSCAPS_PRIMARYSTEREO |
773 DSCAPS_PRIMARY8BIT | DSCAPS_PRIMARYMONO |
774 DSCAPS_SECONDARY16BIT | DSCAPS_SECONDARY8BIT |
775 DSCAPS_SECONDARYMONO | DSCAPS_SECONDARYSTEREO;
776 caps->dwPrimaryBuffers = 1;
777 caps->dwMinSecondarySampleRate = DSBFREQUENCY_MIN;
778 caps->dwMaxSecondarySampleRate = DSBFREQUENCY_MAX;
779 caps->dwMaxHwMixingAllBuffers =
780 caps->dwMaxHwMixingStaticBuffers =
781 caps->dwMaxHwMixingStreamingBuffers =
782 caps->dwMaxHw3DAllBuffers =
783 caps->dwMaxHw3DStaticBuffers =
784 caps->dwMaxHw3DStreamingBuffers = This->share->sources.maxhw_alloc;
785 caps->dwFreeHwMixingAllBuffers =
786 caps->dwFreeHwMixingStaticBuffers =
787 caps->dwFreeHwMixingStreamingBuffers =
788 caps->dwFreeHw3DAllBuffers =
789 caps->dwFreeHw3DStaticBuffers =
790 caps->dwFreeHw3DStreamingBuffers = free_bufs;
791 caps->dwTotalHwMemBytes =
792 caps->dwFreeHwMemBytes = 64 * 1024 * 1024;
793 caps->dwMaxContigFreeHwMemBytes = caps->dwFreeHwMemBytes;
794 caps->dwUnlockTransferRateHwBuffers = 4096;
795 caps->dwPlayCpuOverheadSwBuffers = 0;
797 LeaveCriticalSection(&This->share->crst);
799 return DS_OK;
801 static HRESULT WINAPI DS8_DuplicateSoundBuffer(IDirectSound8 *iface, IDirectSoundBuffer *in, IDirectSoundBuffer **out)
803 DSDevice *This = impl_from_IDirectSound8(iface);
804 DSBuffer *buf = NULL;
805 DSBCAPS caps;
806 HRESULT hr;
808 TRACE("(%p)->(%p, %p)\n", iface, in, out);
810 if(!This->share)
812 WARN("Device not initialized\n");
813 return DSERR_UNINITIALIZED;
816 if(!in || !out)
818 WARN("Invalid pointer: in = %p, out = %p\n", in, out);
819 return DSERR_INVALIDPARAM;
821 *out = NULL;
823 caps.dwSize = sizeof(caps);
824 hr = IDirectSoundBuffer_GetCaps(in, &caps);
825 if(SUCCEEDED(hr) && (caps.dwFlags&DSBCAPS_PRIMARYBUFFER))
827 WARN("Cannot duplicate buffer %p, which has DSBCAPS_PRIMARYBUFFER\n", in);
828 hr = DSERR_INVALIDPARAM;
830 if(SUCCEEDED(hr) && (caps.dwFlags&DSBCAPS_CTRLFX))
832 WARN("Cannot duplicate buffer %p, which has DSBCAPS_CTRLFX\n", in);
833 hr = DSERR_INVALIDPARAM;
835 if(SUCCEEDED(hr))
836 hr = DSBuffer_Create(&buf, &This->primary, in);
837 if(SUCCEEDED(hr))
839 hr = DSBuffer_Initialize(&buf->IDirectSoundBuffer8_iface, NULL, NULL);
840 if(SUCCEEDED(hr))
841 hr = DSBuffer_GetInterface(buf, &IID_IDirectSoundBuffer, (void**)out);
842 if(FAILED(hr))
843 DSBuffer_Destroy(buf);
846 return hr;
849 static HRESULT WINAPI DS8_SetCooperativeLevel(IDirectSound8 *iface, HWND hwnd, DWORD level)
851 DSDevice *This = impl_from_IDirectSound8(iface);
852 HRESULT hr = S_OK;
854 TRACE("(%p)->(%p, %lu)\n", iface, hwnd, level);
856 if(!This->share)
858 WARN("Device not initialized\n");
859 return DSERR_UNINITIALIZED;
862 if(level > DSSCL_WRITEPRIMARY || level < DSSCL_NORMAL)
864 WARN("Invalid coop level: %lu\n", level);
865 return DSERR_INVALIDPARAM;
868 EnterCriticalSection(&This->share->crst);
869 if(level == DSSCL_WRITEPRIMARY && (This->prio_level != DSSCL_WRITEPRIMARY))
871 struct DSBufferGroup *bufgroup = This->primary.BufferGroups;
872 DWORD i, state;
874 if(This->primary.write_emu)
876 ERR("Why was there a write_emu?\n");
877 /* Delete it */
878 IDirectSoundBuffer_Release(This->primary.write_emu);
879 This->primary.write_emu = NULL;
882 for(i = 0;i < This->primary.NumBufferGroups;++i)
884 DWORD64 usemask = ~bufgroup[i].FreeBuffers;
885 while(usemask)
887 int idx = CTZ64(usemask);
888 DSBuffer *buf = bufgroup[i].Buffers + idx;
889 usemask &= ~(U64(1) << idx);
891 if(FAILED(DSBuffer_GetStatus(&buf->IDirectSoundBuffer8_iface, &state)) ||
892 (state&DSBSTATUS_PLAYING))
894 WARN("DSSCL_WRITEPRIMARY set with playing buffers!\n");
895 hr = DSERR_INVALIDCALL;
896 goto out;
898 /* Mark buffer as lost */
899 buf->bufferlost = 1;
903 if(This->primary.flags)
905 /* Primary has open references.. create write_emu */
906 DSBUFFERDESC desc;
907 DSBuffer *emu;
909 memset(&desc, 0, sizeof(desc));
910 desc.dwSize = sizeof(desc);
911 desc.dwFlags = DSBCAPS_LOCHARDWARE | (This->primary.flags&DSBCAPS_CTRLPAN);
912 desc.dwBufferBytes = This->primary.buf_size;
913 desc.lpwfxFormat = &This->primary.format.Format;
915 hr = DSBuffer_Create(&emu, &This->primary, NULL);
916 if(SUCCEEDED(hr))
918 hr = DSBuffer_Initialize(&emu->IDirectSoundBuffer8_iface,
919 &This->IDirectSound_iface, &desc);
920 if(SUCCEEDED(hr))
921 hr = DSBuffer_GetInterface(emu, &IID_IDirectSoundBuffer,
922 (void**)&This->primary.write_emu);
923 if(FAILED(hr))
924 DSBuffer_Destroy(emu);
928 else if(This->prio_level == DSSCL_WRITEPRIMARY && level != DSSCL_WRITEPRIMARY)
930 /* Delete it */
931 TRACE("Nuking write_emu\n");
932 if(This->primary.write_emu)
933 IDirectSoundBuffer_Release(This->primary.write_emu);
934 This->primary.write_emu = NULL;
936 if(SUCCEEDED(hr))
937 This->prio_level = level;
938 out:
939 LeaveCriticalSection(&This->share->crst);
941 return hr;
944 static HRESULT WINAPI DS8_Compact(IDirectSound8 *iface)
946 DSDevice *This = impl_from_IDirectSound8(iface);
947 HRESULT hr = S_OK;
949 TRACE("(%p)->()\n", iface);
951 if(!This->share)
953 WARN("Device not initialized\n");
954 return DSERR_UNINITIALIZED;
957 EnterCriticalSection(&This->share->crst);
958 if(This->prio_level < DSSCL_PRIORITY)
960 WARN("Coop level not high enough (%lu)\n", This->prio_level);
961 hr = DSERR_PRIOLEVELNEEDED;
963 LeaveCriticalSection(&This->share->crst);
965 return hr;
968 static HRESULT WINAPI DS8_GetSpeakerConfig(IDirectSound8 *iface, DWORD *config)
970 DSDevice *This = impl_from_IDirectSound8(iface);
972 TRACE("(%p)->(%p)\n", iface, config);
974 if(!config)
975 return DSERR_INVALIDPARAM;
976 *config = 0;
978 if(!This->share)
980 WARN("Device not initialized\n");
981 return DSERR_UNINITIALIZED;
984 *config = This->share->speaker_config;
986 return DS_OK;
989 static HRESULT WINAPI DS8_SetSpeakerConfig(IDirectSound8 *iface, DWORD config)
991 DSDevice *This = impl_from_IDirectSound8(iface);
992 DWORD geo, speaker;
994 TRACE("(%p)->(0x%08lx)\n", iface, config);
996 if(!This->share)
998 WARN("Device not initialized\n");
999 return DSERR_UNINITIALIZED;
1002 geo = DSSPEAKER_GEOMETRY(config);
1003 speaker = DSSPEAKER_CONFIG(config);
1005 if(geo && (geo < DSSPEAKER_GEOMETRY_MIN || geo > DSSPEAKER_GEOMETRY_MAX))
1007 WARN("Invalid speaker angle %lu\n", geo);
1008 return DSERR_INVALIDPARAM;
1010 if(speaker < DSSPEAKER_HEADPHONE || speaker > DSSPEAKER_7POINT1)
1012 WARN("Invalid speaker config %lu\n", speaker);
1013 return DSERR_INVALIDPARAM;
1016 /* No-op on Vista+. */
1017 return DS_OK;
1020 static HRESULT WINAPI DS8_Initialize(IDirectSound8 *iface, const GUID *devguid)
1022 DSDevice *This = impl_from_IDirectSound8(iface);
1023 HRESULT hr;
1024 GUID guid;
1025 UINT n;
1027 TRACE("(%p)->(%s)\n", iface, debugstr_guid(devguid));
1029 if(!openal_loaded)
1030 return DSERR_NODRIVER;
1032 if(This->share)
1034 WARN("Device already initialized\n");
1035 return DSERR_ALREADYINITIALIZED;
1038 if(!devguid || IsEqualGUID(devguid, &GUID_NULL))
1039 devguid = &DSDEVID_DefaultPlayback;
1040 else if(IsEqualGUID(devguid, &DSDEVID_DefaultCapture) ||
1041 IsEqualGUID(devguid, &DSDEVID_DefaultVoiceCapture))
1042 return DSERR_NODRIVER;
1044 hr = GetDeviceID(devguid, &guid);
1045 if(FAILED(hr)) return hr;
1047 EnterCriticalSection(&openal_crst);
1049 TRACE("Searching shared devices for %s\n", debugstr_guid(&guid));
1050 for(n = 0;n < sharelistsize;n++)
1052 if(IsEqualGUID(&sharelist[n]->guid, &guid))
1054 TRACE("Matched shared device %p\n", sharelist[n]);
1056 DSShare_AddRef(sharelist[n]);
1057 This->share = sharelist[n];
1058 break;
1062 if(!This->share)
1063 hr = DSShare_Create(&guid, &This->share);
1064 if(SUCCEEDED(hr))
1066 This->device = This->share->device;
1067 hr = DSPrimary_PreInit(&This->primary, This);
1070 if(SUCCEEDED(hr))
1072 DeviceShare *share = This->share;
1073 DSPrimary **prims;
1075 EnterCriticalSection(&share->crst);
1077 prims = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
1078 (share->nprimaries+1) * sizeof(*prims));
1079 if(!prims)
1080 hr = DSERR_OUTOFMEMORY;
1081 else
1083 ALsizei i;
1084 for(i = 0;i < share->nprimaries;++i)
1085 prims[i] = share->primaries[i];
1086 prims[i] = &This->primary;
1088 HeapFree(GetProcessHeap(), 0, share->primaries);
1089 share->primaries = prims;
1090 share->nprimaries += 1;
1093 LeaveCriticalSection(&share->crst);
1096 if(FAILED(hr))
1098 if(This->share)
1099 DSShare_Release(This->share);
1100 This->share = NULL;
1103 LeaveCriticalSection(&openal_crst);
1104 return hr;
1107 /* I, Maarten Lankhorst, hereby declare this driver certified
1108 * What this means.. ? An extra bit set
1110 static HRESULT WINAPI DS8_VerifyCertification(IDirectSound8 *iface, DWORD *certified)
1112 DSDevice *This = impl_from_IDirectSound8(iface);
1114 TRACE("(%p)->(%p)\n", iface, certified);
1116 if(!certified)
1117 return DSERR_INVALIDPARAM;
1118 *certified = 0;
1120 if(!This->share)
1122 WARN("Device not initialized\n");
1123 return DSERR_UNINITIALIZED;
1126 *certified = DS_CERTIFIED;
1128 return DS_OK;
1131 static const IDirectSound8Vtbl DS8_Vtbl = {
1132 DS8_QueryInterface,
1133 DS8_AddRef,
1134 DS8_Release,
1135 DS8_CreateSoundBuffer,
1136 DS8_GetCaps,
1137 DS8_DuplicateSoundBuffer,
1138 DS8_SetCooperativeLevel,
1139 DS8_Compact,
1140 DS8_GetSpeakerConfig,
1141 DS8_SetSpeakerConfig,
1142 DS8_Initialize,
1143 DS8_VerifyCertification
1147 static HRESULT WINAPI DS_QueryInterface(IDirectSound *iface, REFIID riid, LPVOID *ppv)
1149 DSDevice *This = impl_from_IDirectSound(iface);
1150 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
1151 return DSDevice_GetInterface(This, riid, ppv);
1154 static ULONG WINAPI DS_AddRef(IDirectSound *iface)
1156 DSDevice *This = impl_from_IDirectSound(iface);
1157 return DS8_AddRef(&This->IDirectSound8_iface);
1160 static ULONG WINAPI DS_Release(IDirectSound *iface)
1162 DSDevice *This = impl_from_IDirectSound(iface);
1163 return DS8_Release(&This->IDirectSound8_iface);
1166 static HRESULT WINAPI DS_CreateSoundBuffer(IDirectSound *iface, LPCDSBUFFERDESC desc, LPLPDIRECTSOUNDBUFFER buf, IUnknown *pUnkOuter)
1168 DSDevice *This = impl_from_IDirectSound(iface);
1169 return DS8_CreateSoundBuffer(&This->IDirectSound8_iface, desc, buf, pUnkOuter);
1172 static HRESULT WINAPI DS_GetCaps(IDirectSound *iface, LPDSCAPS caps)
1174 DSDevice *This = impl_from_IDirectSound(iface);
1175 return DS8_GetCaps(&This->IDirectSound8_iface, caps);
1177 static HRESULT WINAPI DS_DuplicateSoundBuffer(IDirectSound *iface, IDirectSoundBuffer *in, IDirectSoundBuffer **out)
1179 DSDevice *This = impl_from_IDirectSound(iface);
1180 return DS8_DuplicateSoundBuffer(&This->IDirectSound8_iface, in, out);
1183 static HRESULT WINAPI DS_SetCooperativeLevel(IDirectSound *iface, HWND hwnd, DWORD level)
1185 DSDevice *This = impl_from_IDirectSound(iface);
1186 return DS8_SetCooperativeLevel(&This->IDirectSound8_iface, hwnd, level);
1189 static HRESULT WINAPI DS_Compact(IDirectSound *iface)
1191 DSDevice *This = impl_from_IDirectSound(iface);
1192 return DS8_Compact(&This->IDirectSound8_iface);
1195 static HRESULT WINAPI DS_GetSpeakerConfig(IDirectSound *iface, DWORD *config)
1197 DSDevice *This = impl_from_IDirectSound(iface);
1198 return DS8_GetSpeakerConfig(&This->IDirectSound8_iface, config);
1201 static HRESULT WINAPI DS_SetSpeakerConfig(IDirectSound *iface, DWORD config)
1203 DSDevice *This = impl_from_IDirectSound(iface);
1204 return DS8_SetSpeakerConfig(&This->IDirectSound8_iface, config);
1207 static HRESULT WINAPI DS_Initialize(IDirectSound *iface, const GUID *devguid)
1209 DSDevice *This = impl_from_IDirectSound(iface);
1210 return DS8_Initialize(&This->IDirectSound8_iface, devguid);
1213 static const IDirectSoundVtbl DS_Vtbl = {
1214 DS_QueryInterface,
1215 DS_AddRef,
1216 DS_Release,
1217 DS_CreateSoundBuffer,
1218 DS_GetCaps,
1219 DS_DuplicateSoundBuffer,
1220 DS_SetCooperativeLevel,
1221 DS_Compact,
1222 DS_GetSpeakerConfig,
1223 DS_SetSpeakerConfig,
1224 DS_Initialize