Reduce MAX_HWBUFFERS to 128
[dsound-openal.git] / dsound8.c
blob4d215388e8e1a81c85e6e38206f381de14f1ddcd
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 if(share->sources.maxhw_alloc + share->sources.maxsw_alloc)
152 alDeleteSources(share->sources.maxhw_alloc+share->sources.maxsw_alloc,
153 share->sources.ids);
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;
167 set_context(NULL);
168 TlsSetValue(TlsThreadPtr, NULL);
169 alcDestroyContext(share->ctx);
170 share->ctx = NULL;
171 LeaveCriticalSection(&openal_crst);
174 if(share->device)
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];
190 int extenum;
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;
201 ALchar drv_name[64];
202 DeviceShare *share;
203 IMMDevice *mmdev;
204 ALCint attrs[7];
205 void *temp;
206 HRESULT hr;
207 ALsizei i;
209 share = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*share));
210 if(!share) return DSERR_OUTOFMEMORY;
211 share->ref = 1;
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);
219 if(SUCCEEDED(hr))
221 IPropertyStore *store;
223 hr = IMMDevice_OpenPropertyStore(mmdev, STGM_READ, &store);
224 if(FAILED(hr))
225 WARN("IMMDevice_OpenPropertyStore failed: %08lx\n", hr);
226 else
228 ULONG phys_speakers = 0;
229 PROPVARIANT pv;
231 PropVariantInit(&pv);
233 hr = IPropertyStore_GetValue(store, &PKEY_AudioEndpoint_PhysicalSpeakers, &pv);
234 if(FAILED(hr))
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);
238 else
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;
257 else
258 FIXME("Unhandled physical speaker layout: 0x%08lx\n", phys_speakers);
259 #undef BIT_MATCH
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);
268 if(FAILED(hr))
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);
272 else
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);
288 mmdev = NULL;
291 InitializeCriticalSection(&share->crst);
293 hr = StringFromCLSID(guid, &guid_str);
294 if(FAILED(hr))
296 ERR("Failed to convert GUID to string\n");
297 goto fail;
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);
302 guid_str = NULL;
304 hr = DSERR_NODRIVER;
305 share->device = alcOpenDevice(drv_name);
306 if(!share->device)
308 alcGetError(NULL);
309 WARN("Couldn't open device \"%s\"\n", drv_name);
310 goto fail;
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));
317 i = 0;
318 attrs[i++] = ALC_MONO_SOURCES;
319 attrs[i++] = MAX_SOURCES;
320 attrs[i++] = ALC_STEREO_SOURCES;
321 attrs[i++] = 0;
322 if(alcIsExtensionPresent(share->device, "ALC_EXT_EFX"))
324 attrs[i++] = ALC_MAX_AUXILIARY_SENDS;
325 attrs[i++] = EAX_MAX_ACTIVE_FXSLOTS;
327 attrs[i++] = 0;
328 share->ctx = alcCreateContext(share->device, attrs);
329 if(!share->ctx)
331 ALCenum err = alcGetError(share->device);
332 ERR("Could not create context (0x%x)!\n", err);
333 goto fail;
336 share->guid = *guid;
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;
374 share->num_slots++;
376 TRACE("Allocated %d auxiliary effect slot%s\n", i, (i==1)?"":"s");
377 while(i < EAX_MAX_FXSLOTS)
378 share->auxslot[i++] = 0;
380 popALContext();
382 hr = E_OUTOFMEMORY;
383 if(share->sources.maxhw_alloc < 128)
385 ERR("Could only allocate %lu sources (minimum 128 required)\n",
386 share->sources.maxhw_alloc);
387 goto fail;
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;
400 else
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);
410 if(sharelist)
411 temp = HeapReAlloc(GetProcessHeap(), 0, sharelist, sizeof(*sharelist)*(sharelistsize+1));
412 else
413 temp = HeapAlloc(GetProcessHeap(), 0, sizeof(*sharelist)*(sharelistsize+1));
414 if(temp)
416 sharelist = temp;
417 sharelist[sharelistsize++] = share;
420 hr = E_FAIL;
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);
433 *out = share;
434 return DS_OK;
436 fail:
437 DSShare_Destroy(share);
438 return hr;
441 static ULONG DSShare_AddRef(DeviceShare *share)
443 ULONG ref = InterlockedIncrement(&share->ref);
444 return ref;
447 static ULONG DSShare_Release(DeviceShare *share)
449 ULONG ref = InterlockedDecrement(&share->ref);
450 if(ref == 0) DSShare_Destroy(share);
451 return ref;
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 /*******************************************************************************
464 * IUnknown
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);
481 ULONG ref;
483 InterlockedIncrement(&(This->ref));
484 ref = InterlockedIncrement(&(This->unkref));
485 TRACE("(%p) ref %lu\n", iface, ref);
487 return 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);
497 return ref;
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)
526 DSDevice *This;
527 HRESULT hr;
529 *ds = NULL;
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;
538 This->is_8 = is8;
540 hr = DSDevice_GetInterface(This, riid, ds);
541 if(FAILED(hr)) DSDevice_Destroy(This);
542 return hr;
545 static void DSDevice_Destroy(DSDevice *This)
547 DeviceShare *share = This->share;
549 TRACE("Destroying device instance %p\n", This);
550 if(share)
552 ALsizei i;
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];
562 break;
566 LeaveCriticalSection(&share->crst);
569 DSPrimary_Clear(&This->primary);
570 if(This->share)
571 DSShare_Release(This->share);
572 This->share = NULL;
574 HeapFree(GetProcessHeap(), 0, This);
577 static HRESULT DSDevice_GetInterface(DSDevice *This, REFIID riid, LPVOID *ppv)
579 *ppv = NULL;
580 if(IsEqualIID(riid, &IID_IUnknown))
581 *ppv = &This->IUnknown_iface;
582 else if(IsEqualIID(riid, &IID_IDirectSound8))
584 if(This->is_8)
585 *ppv = &This->IDirectSound8_iface;
587 else if(IsEqualIID(riid, &IID_IDirectSound))
588 *ppv = &This->IDirectSound_iface;
589 else
590 FIXME("Unhandled GUID: %s\n", debugstr_guid(riid));
592 if(*ppv)
594 IUnknown_AddRef((IUnknown*)*ppv);
595 return S_OK;
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);
612 LONG ref;
614 InterlockedIncrement(&This->ref);
615 ref = InterlockedIncrement(&This->dsref);
616 TRACE("(%p) ref %lu\n", iface, ref);
618 return ref;
621 static ULONG WINAPI DS8_Release(IDirectSound8 *iface)
623 DSDevice *This = impl_from_IDirectSound8(iface);
624 LONG ref;
626 ref = InterlockedDecrement(&This->dsref);
627 TRACE("(%p) ref %lu\n", iface, ref);
628 if(InterlockedDecrement(&This->ref) == 0)
629 DSDevice_Destroy(This);
631 return ref;
634 static HRESULT WINAPI DS8_CreateSoundBuffer(IDirectSound8 *iface, LPCDSBUFFERDESC desc, LPLPDIRECTSOUNDBUFFER buf, IUnknown *pUnkOuter)
636 DSDevice *This = impl_from_IDirectSound8(iface);
637 HRESULT hr;
639 TRACE("(%p)->(%p, %p, %p)\n", iface, desc, buf, pUnkOuter);
641 if(!buf)
643 WARN("buf is null\n");
644 return DSERR_INVALIDPARAM;
646 *buf = NULL;
648 if(pUnkOuter)
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;
659 if(!This->share)
661 WARN("Device not initialized\n");
662 return DSERR_UNINITIALIZED;
665 TRACE("Requested buffer:\n"
666 " Size = %lu\n"
667 " Flags = 0x%08lx\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));
682 else
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))
689 if(!This->is_8)
691 static int once = 0;
692 if(!once++)
693 FIXME("Buffers with 3D and panning control ignore panning\n");
695 else
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;
707 hr = S_OK;
708 if(IDirectSoundBuffer_AddRef(prim) == 1)
710 hr = DSPrimary_Initialize(prim, &This->IDirectSound_iface, desc);
711 if(FAILED(hr))
713 IDirectSoundBuffer_Release(prim);
714 prim = NULL;
717 *buf = prim;
719 else
721 DSBuffer *dsb;
723 hr = DSBuffer_Create(&dsb, &This->primary, NULL);
724 if(SUCCEEDED(hr))
726 hr = DSBuffer_Initialize(&dsb->IDirectSoundBuffer8_iface, &This->IDirectSound_iface, desc);
727 if(SUCCEEDED(hr))
729 dsb->bufferlost = (This->prio_level == DSSCL_WRITEPRIMARY);
730 hr = DSBuffer_GetInterface(dsb, &IID_IDirectSoundBuffer, (void**)buf);
732 if(FAILED(hr))
733 DSBuffer_Destroy(dsb);
736 LeaveCriticalSection(&This->share->crst);
738 TRACE("%08lx\n", hr);
739 return hr;
742 static HRESULT WINAPI DS8_GetCaps(IDirectSound8 *iface, LPDSCAPS caps)
744 DSDevice *This = impl_from_IDirectSound8(iface);
745 struct DSBufferGroup *bufgroup, *endgroup;
746 DWORD free_bufs;
748 TRACE("(%p)->(%p)\n", iface, caps);
750 if(!This->share)
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;
770 while(usemask)
772 int idx = CTZ64(usemask);
773 DSBuffer *buf = bufgroup->Buffers + idx;
774 usemask &= ~(U64(1) << idx);
776 if(buf->loc_status == DSBSTATUS_LOCHARDWARE)
778 if(!--free_bufs)
779 break;
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);
812 return DS_OK;
814 static HRESULT WINAPI DS8_DuplicateSoundBuffer(IDirectSound8 *iface, IDirectSoundBuffer *in, IDirectSoundBuffer **out)
816 DSDevice *This = impl_from_IDirectSound8(iface);
817 DSBuffer *buf = NULL;
818 DSBCAPS caps;
819 HRESULT hr;
821 TRACE("(%p)->(%p, %p)\n", iface, in, out);
823 if(!This->share)
825 WARN("Device not initialized\n");
826 return DSERR_UNINITIALIZED;
829 if(!in || !out)
831 WARN("Invalid pointer: in = %p, out = %p\n", in, out);
832 return DSERR_INVALIDPARAM;
834 *out = NULL;
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;
848 if(SUCCEEDED(hr))
849 hr = DSBuffer_Create(&buf, &This->primary, in);
850 if(SUCCEEDED(hr))
852 hr = DSBuffer_Initialize(&buf->IDirectSoundBuffer8_iface, NULL, NULL);
853 if(SUCCEEDED(hr))
854 hr = DSBuffer_GetInterface(buf, &IID_IDirectSoundBuffer, (void**)out);
855 if(FAILED(hr))
856 DSBuffer_Destroy(buf);
859 return hr;
862 static HRESULT WINAPI DS8_SetCooperativeLevel(IDirectSound8 *iface, HWND hwnd, DWORD level)
864 DSDevice *This = impl_from_IDirectSound8(iface);
865 HRESULT hr = S_OK;
867 TRACE("(%p)->(%p, %lu)\n", iface, hwnd, level);
869 if(!This->share)
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;
885 DWORD i, state;
887 if(This->primary.write_emu)
889 ERR("Why was there a write_emu?\n");
890 /* Delete it */
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;
898 while(usemask)
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;
909 goto out;
911 /* Mark buffer as lost */
912 buf->bufferlost = 1;
916 if(This->primary.flags)
918 /* Primary has open references.. create write_emu */
919 DSBUFFERDESC desc;
920 DSBuffer *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);
929 if(SUCCEEDED(hr))
931 hr = DSBuffer_Initialize(&emu->IDirectSoundBuffer8_iface,
932 &This->IDirectSound_iface, &desc);
933 if(SUCCEEDED(hr))
934 hr = DSBuffer_GetInterface(emu, &IID_IDirectSoundBuffer,
935 (void**)&This->primary.write_emu);
936 if(FAILED(hr))
937 DSBuffer_Destroy(emu);
941 else if(This->prio_level == DSSCL_WRITEPRIMARY && level != DSSCL_WRITEPRIMARY)
943 /* Delete it */
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;
949 if(SUCCEEDED(hr))
950 This->prio_level = level;
951 out:
952 LeaveCriticalSection(&This->share->crst);
954 return hr;
957 static HRESULT WINAPI DS8_Compact(IDirectSound8 *iface)
959 DSDevice *This = impl_from_IDirectSound8(iface);
960 HRESULT hr = S_OK;
962 TRACE("(%p)->()\n", iface);
964 if(!This->share)
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);
978 return hr;
981 static HRESULT WINAPI DS8_GetSpeakerConfig(IDirectSound8 *iface, DWORD *config)
983 DSDevice *This = impl_from_IDirectSound8(iface);
985 TRACE("(%p)->(%p)\n", iface, config);
987 if(!config)
988 return DSERR_INVALIDPARAM;
989 *config = 0;
991 if(!This->share)
993 WARN("Device not initialized\n");
994 return DSERR_UNINITIALIZED;
997 *config = This->share->speaker_config;
999 return DS_OK;
1002 static HRESULT WINAPI DS8_SetSpeakerConfig(IDirectSound8 *iface, DWORD config)
1004 DSDevice *This = impl_from_IDirectSound8(iface);
1005 DWORD geo, speaker;
1007 TRACE("(%p)->(0x%08lx)\n", iface, config);
1009 if(!This->share)
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+. */
1030 return DS_OK;
1033 static HRESULT WINAPI DS8_Initialize(IDirectSound8 *iface, const GUID *devguid)
1035 DSDevice *This = impl_from_IDirectSound8(iface);
1036 HRESULT hr;
1037 GUID guid;
1038 UINT n;
1040 TRACE("(%p)->(%s)\n", iface, debugstr_guid(devguid));
1042 if(!openal_loaded)
1043 return DSERR_NODRIVER;
1045 if(This->share)
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];
1071 break;
1075 if(!This->share)
1076 hr = DSShare_Create(&guid, &This->share);
1077 if(SUCCEEDED(hr))
1079 This->device = This->share->device;
1080 hr = DSPrimary_PreInit(&This->primary, This);
1083 if(SUCCEEDED(hr))
1085 DeviceShare *share = This->share;
1086 DSPrimary **prims;
1088 EnterCriticalSection(&share->crst);
1090 prims = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
1091 (share->nprimaries+1) * sizeof(*prims));
1092 if(!prims)
1093 hr = DSERR_OUTOFMEMORY;
1094 else
1096 ALsizei i;
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);
1109 if(FAILED(hr))
1111 if(This->share)
1112 DSShare_Release(This->share);
1113 This->share = NULL;
1116 LeaveCriticalSection(&openal_crst);
1117 return hr;
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);
1129 if(!certified)
1130 return DSERR_INVALIDPARAM;
1131 *certified = 0;
1133 if(!This->share)
1135 WARN("Device not initialized\n");
1136 return DSERR_UNINITIALIZED;
1139 *certified = DS_CERTIFIED;
1141 return DS_OK;
1144 static IDirectSound8Vtbl DS8_Vtbl = {
1145 DS8_QueryInterface,
1146 DS8_AddRef,
1147 DS8_Release,
1148 DS8_CreateSoundBuffer,
1149 DS8_GetCaps,
1150 DS8_DuplicateSoundBuffer,
1151 DS8_SetCooperativeLevel,
1152 DS8_Compact,
1153 DS8_GetSpeakerConfig,
1154 DS8_SetSpeakerConfig,
1155 DS8_Initialize,
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 = {
1227 DS_QueryInterface,
1228 DS_AddRef,
1229 DS_Release,
1230 DS_CreateSoundBuffer,
1231 DS_GetCaps,
1232 DS_DuplicateSoundBuffer,
1233 DS_SetCooperativeLevel,
1234 DS_Compact,
1235 DS_GetSpeakerConfig,
1236 DS_SetSpeakerConfig,
1237 DS_Initialize