Attempt to improve handling of EAX1 volume settings
[dsound-openal.git] / dsound8.c
blobaa6ea53f91a7cd03d2561670ee05dd5ab816179b
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 DS8Primary_triggernots(share->primaries[i]);
55 if(!BITFIELD_TEST(share->Exts, SOFTX_MAP_BUFFER))
56 DS8Primary_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.max_alloc)
151 alDeleteSources(share->sources.max_alloc, share->sources.ids);
152 share->sources.max_alloc = 0;
154 if(share->auxslot)
155 alDeleteAuxiliaryEffectSlots(1, &share->auxslot);
156 share->auxslot = 0;
158 set_context(NULL);
159 TlsSetValue(TlsThreadPtr, NULL);
160 alcDestroyContext(share->ctx);
161 share->ctx = NULL;
162 LeaveCriticalSection(&openal_crst);
165 if(share->device)
166 alcCloseDevice(share->device);
167 share->device = NULL;
169 DeleteCriticalSection(&share->crst);
171 HeapFree(GetProcessHeap(), 0, share->primaries);
172 HeapFree(GetProcessHeap(), 0, share);
175 static HRESULT DSShare_Create(REFIID guid, DeviceShare **out)
177 static const struct {
178 const char extname[64];
179 int extenum;
180 } extensions[MAX_EXTENSIONS] = {
181 { "ALC_EXT_EFX", EXT_EFX },
182 { "AL_EXT_FLOAT32", EXT_FLOAT32 },
183 { "AL_EXT_MCFORMATS", EXT_MCFORMATS },
184 { "AL_SOFT_deferred_updates", SOFT_DEFERRED_UPDATES },
185 { "AL_SOFT_source_spatialize", SOFT_SOURCE_SPATIALIZE },
186 { "AL_SOFTX_map_buffer", SOFTX_MAP_BUFFER },
188 OLECHAR *guid_str = NULL;
189 ALchar drv_name[64];
190 DeviceShare *share;
191 IMMDevice *mmdev;
192 void *temp;
193 HRESULT hr;
194 size_t i;
196 share = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*share));
197 if(!share) return DSERR_OUTOFMEMORY;
198 share->ref = 1;
199 share->refresh = FAKE_REFRESH_COUNT;
201 share->speaker_config = DSSPEAKER_7POINT1_SURROUND;
203 hr = get_mmdevice(eRender, guid, &mmdev);
204 if(SUCCEEDED(hr))
206 IPropertyStore *store;
208 hr = IMMDevice_OpenPropertyStore(mmdev, STGM_READ, &store);
209 if(FAILED(hr))
210 WARN("IMMDevice_OpenPropertyStore failed: %08lx\n", hr);
211 else
213 ULONG phys_speakers = 0;
214 PROPVARIANT pv;
216 PropVariantInit(&pv);
218 hr = IPropertyStore_GetValue(store, &PKEY_AudioEndpoint_PhysicalSpeakers, &pv);
219 if(FAILED(hr))
220 WARN("IPropertyStore_GetValue failed: %08lx\n", hr);
221 else
223 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
250 /* If the device has a stereo layout, check the formfactor to see
251 * if it's really headphones/headset.
253 if(DSSPEAKER_CONFIG(share->speaker_config) == DSSPEAKER_STEREO)
255 hr = IPropertyStore_GetValue(store, &PKEY_AudioEndpoint_FormFactor, &pv);
256 if(FAILED(hr))
257 WARN("IPropertyStore_GetValue failed: %08lx\n", hr);
258 else
260 if(pv.vt != VT_UI4)
261 WARN("PKEY_AudioEndpoint_FormFactor is not a ULONG: 0x%04x\n", pv.vt);
262 else
264 if(pv.ulVal == Headphones || pv.ulVal == Headset)
265 share->speaker_config = DSSPEAKER_HEADPHONE;
270 PropVariantClear(&pv);
271 IPropertyStore_Release(store);
274 IMMDevice_Release(mmdev);
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 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 share->ctx = alcCreateContext(share->device, NULL);
305 if(!share->ctx)
307 ALCenum err = alcGetError(share->device);
308 ERR("Could not create context (0x%x)!\n", err);
309 goto fail;
312 memcpy(&share->guid, guid, sizeof(GUID));
314 setALContext(share->ctx);
315 alcGetIntegerv(share->device, ALC_REFRESH, 1, &share->refresh);
316 checkALCError(share->device);
318 for(i = 0;i < MAX_EXTENSIONS;i++)
320 if((strncmp(extensions[i].extname, "ALC", 3) == 0) ?
321 alcIsExtensionPresent(share->device, extensions[i].extname) :
322 alIsExtensionPresent(extensions[i].extname))
324 TRACE("Found %s\n", extensions[i].extname);
325 BITFIELD_SET(share->Exts, extensions[i].extenum);
329 if(BITFIELD_TEST(share->Exts, EXT_EFX))
330 alGenAuxiliaryEffectSlots(1, &share->auxslot);
332 share->sources.max_alloc = 0;
333 while(share->sources.max_alloc < MAX_SOURCES)
335 alGenSources(1, &share->sources.ids[share->sources.max_alloc]);
336 if(alGetError() != AL_NO_ERROR)
337 break;
338 share->sources.max_alloc++;
340 popALContext();
341 /* As long as we have at least 64 sources, keep it a multiple of 64. */
342 if(share->sources.max_alloc > 64)
343 share->sources.max_alloc &= ~63u;
344 share->sources.avail_num = share->sources.max_alloc;
346 if(sharelist)
347 temp = HeapReAlloc(GetProcessHeap(), 0, sharelist, sizeof(*sharelist)*(sharelistsize+1));
348 else
349 temp = HeapAlloc(GetProcessHeap(), 0, sizeof(*sharelist)*(sharelistsize+1));
350 if(temp)
352 sharelist = temp;
353 sharelist[sharelistsize++] = share;
356 hr = E_FAIL;
358 share->quit_now = FALSE;
359 share->timer_evt = CreateEventA(NULL, FALSE, FALSE, NULL);
360 if(!share->timer_evt) goto fail;
362 share->queue_timer = NULL;
364 share->thread_hdl = CreateThread(NULL, 0, DSShare_thread, share, 0, &share->thread_id);
365 if(!share->thread_hdl) goto fail;
367 DSShare_starttimer(share);
369 *out = share;
370 return DS_OK;
372 fail:
373 DSShare_Destroy(share);
374 return hr;
377 static ULONG DSShare_AddRef(DeviceShare *share)
379 ULONG ref = InterlockedIncrement(&share->ref);
380 return ref;
383 static ULONG DSShare_Release(DeviceShare *share)
385 ULONG ref = InterlockedDecrement(&share->ref);
386 if(ref == 0) DSShare_Destroy(share);
387 return ref;
391 static const IDirectSound8Vtbl DS8_Vtbl;
392 static const IDirectSoundVtbl DS_Vtbl;
393 static const IUnknownVtbl DS8_Unknown_Vtbl;
395 static void DS8Impl_Destroy(DS8Impl *This);
396 static HRESULT WINAPI DS8_QueryInterface(IDirectSound8 *iface, REFIID riid, LPVOID *ppv);
398 /*******************************************************************************
399 * IUnknown
401 static inline DS8Impl *impl_from_IUnknown(IUnknown *iface)
403 return CONTAINING_RECORD(iface, DS8Impl, IUnknown_iface);
406 static HRESULT WINAPI DS8Impl_IUnknown_QueryInterface(IUnknown *iface, REFIID riid, void **ppobj)
408 DS8Impl *This = impl_from_IUnknown(iface);
409 return DS8_QueryInterface(&This->IDirectSound8_iface, riid, ppobj);
412 static ULONG WINAPI DS8Impl_IUnknown_AddRef(IUnknown *iface)
414 DS8Impl *This = impl_from_IUnknown(iface);
415 ULONG ref;
417 InterlockedIncrement(&(This->ref));
418 ref = InterlockedIncrement(&(This->unkref));
419 TRACE("(%p) ref was %lu\n", This, ref - 1);
421 return ref;
424 static ULONG WINAPI DS8Impl_IUnknown_Release(IUnknown *iface)
426 DS8Impl *This = impl_from_IUnknown(iface);
427 ULONG ref = InterlockedDecrement(&(This->unkref));
428 TRACE("(%p) ref was %lu\n", This, ref + 1);
429 if(InterlockedDecrement(&(This->ref)) == 0)
430 DS8Impl_Destroy(This);
431 return ref;
434 static const IUnknownVtbl DS8_Unknown_Vtbl = {
435 DS8Impl_IUnknown_QueryInterface,
436 DS8Impl_IUnknown_AddRef,
437 DS8Impl_IUnknown_Release
441 static inline DS8Impl *impl_from_IDirectSound8(IDirectSound8 *iface)
443 return CONTAINING_RECORD(iface, DS8Impl, IDirectSound8_iface);
446 static inline DS8Impl *impl_from_IDirectSound(IDirectSound *iface)
448 return CONTAINING_RECORD(iface, DS8Impl, IDirectSound_iface);
451 HRESULT DSOUND_Create(REFIID riid, void **ds)
453 HRESULT hr;
455 hr = DSOUND_Create8(&IID_IDirectSound, ds);
456 if(SUCCEEDED(hr))
458 DS8Impl *impl = impl_from_IDirectSound(*ds);
459 impl->is_8 = FALSE;
461 if(!IsEqualIID(riid, &IID_IDirectSound))
463 hr = IDirectSound_QueryInterface(&impl->IDirectSound_iface, riid, ds);
464 IDirectSound_Release(&impl->IDirectSound_iface);
467 return hr;
471 HRESULT DSOUND_Create8(REFIID riid, LPVOID *ds)
473 DS8Impl *This;
474 HRESULT hr;
476 *ds = NULL;
477 This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*This));
478 if(!This) return DSERR_OUTOFMEMORY;
480 This->IDirectSound8_iface.lpVtbl = &DS8_Vtbl;
481 This->IDirectSound_iface.lpVtbl = &DS_Vtbl;
482 This->IUnknown_iface.lpVtbl = &DS8_Unknown_Vtbl;
484 This->is_8 = TRUE;
486 hr = IDirectSound8_QueryInterface(&This->IDirectSound8_iface, riid, ds);
487 if(FAILED(hr)) DS8Impl_Destroy(This);
488 return hr;
491 static void DS8Impl_Destroy(DS8Impl *This)
493 DeviceShare *share = This->share;
495 if(share)
497 ALsizei i;
499 EnterCriticalSection(&share->crst);
501 for(i = 0;i < share->nprimaries;++i)
503 if(share->primaries[i] == &This->primary)
505 share->nprimaries -= 1;
506 share->primaries[i] = share->primaries[share->nprimaries];
507 break;
511 LeaveCriticalSection(&share->crst);
514 DS8Primary_Clear(&This->primary);
515 if(This->share)
516 DSShare_Release(This->share);
517 This->share = NULL;
519 HeapFree(GetProcessHeap(), 0, This);
523 static HRESULT WINAPI DS8_QueryInterface(IDirectSound8 *iface, REFIID riid, LPVOID *ppv)
525 DS8Impl *This = impl_from_IDirectSound8(iface);
527 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
529 *ppv = NULL;
530 if(IsEqualIID(riid, &IID_IUnknown))
531 *ppv = &This->IUnknown_iface;
532 else if(IsEqualIID(riid, &IID_IDirectSound8))
534 if(This->is_8)
535 *ppv = &This->IDirectSound8_iface;
537 else if(IsEqualIID(riid, &IID_IDirectSound))
538 *ppv = &This->IDirectSound_iface;
539 else
540 FIXME("Unhandled GUID: %s\n", debugstr_guid(riid));
542 if(*ppv)
544 IUnknown_AddRef((IUnknown*)*ppv);
545 return S_OK;
548 return E_NOINTERFACE;
551 static ULONG WINAPI DS8_AddRef(IDirectSound8 *iface)
553 DS8Impl *This = impl_from_IDirectSound8(iface);
554 LONG ref;
556 InterlockedIncrement(&This->ref);
557 ref = InterlockedIncrement(&This->dsref);
558 TRACE("Reference count incremented to %ld\n", ref);
560 return ref;
563 static ULONG WINAPI DS8_Release(IDirectSound8 *iface)
565 DS8Impl *This = impl_from_IDirectSound8(iface);
566 LONG ref;
568 ref = InterlockedDecrement(&This->dsref);
569 TRACE("Reference count decremented to %ld\n", ref);
570 if(InterlockedDecrement(&This->ref) == 0)
571 DS8Impl_Destroy(This);
573 return ref;
576 static HRESULT WINAPI DS8_CreateSoundBuffer(IDirectSound8 *iface, LPCDSBUFFERDESC desc, LPLPDIRECTSOUNDBUFFER buf, IUnknown *pUnkOuter)
578 DS8Impl *This = impl_from_IDirectSound8(iface);
579 HRESULT hr;
581 TRACE("(%p)->(%p, %p, %p)\n", iface, desc, buf, pUnkOuter);
583 if(!buf)
585 WARN("buf is null\n");
586 return DSERR_INVALIDPARAM;
588 *buf = NULL;
590 if(pUnkOuter)
592 WARN("Aggregation isn't supported\n");
593 return DSERR_NOAGGREGATION;
595 if(!desc || desc->dwSize < sizeof(DSBUFFERDESC1))
597 WARN("Invalid buffer %p/%lu\n", desc, desc?desc->dwSize:0);
598 return DSERR_INVALIDPARAM;
601 if(!This->share)
603 WARN("Device not initialized\n");
604 return DSERR_UNINITIALIZED;
607 TRACE("Requested buffer:\n"
608 " Size = %lu\n"
609 " Flags = 0x%08lx\n"
610 " BufferBytes = %lu\n",
611 desc->dwSize, desc->dwFlags, desc->dwBufferBytes);
613 if(desc->dwSize >= sizeof(DSBUFFERDESC))
615 if(!(desc->dwFlags&DSBCAPS_CTRL3D))
617 if(!IsEqualGUID(&desc->guid3DAlgorithm, &GUID_NULL))
619 WARN("Invalid 3D algorithm GUID specified for non-3D buffer: %s\n", debugstr_guid(&desc->guid3DAlgorithm));
620 return DSERR_INVALIDPARAM;
623 else
624 TRACE("Requested 3D algorithm GUID: %s\n", debugstr_guid(&desc->guid3DAlgorithm));
627 /* OpenAL doesn't support playing with 3d and panning at same time.. */
628 if((desc->dwFlags&(DSBCAPS_CTRL3D|DSBCAPS_CTRLPAN)) == (DSBCAPS_CTRL3D|DSBCAPS_CTRLPAN))
630 if(!This->is_8)
632 static int once = 0;
633 if(!once++)
634 FIXME("Buffers with 3D and panning control ignore panning\n");
636 else
638 WARN("Cannot create buffers with 3D and panning control\n");
639 return DSERR_INVALIDPARAM;
643 EnterCriticalSection(&This->share->crst);
644 if((desc->dwFlags&DSBCAPS_PRIMARYBUFFER))
646 IDirectSoundBuffer *prim = &This->primary.IDirectSoundBuffer_iface;
648 hr = S_OK;
649 if(IDirectSoundBuffer_AddRef(prim) == 1)
651 hr = DS8Primary_Initialize(prim, &This->IDirectSound_iface, desc);
652 if(FAILED(hr))
654 IDirectSoundBuffer_Release(prim);
655 prim = NULL;
658 *buf = prim;
660 else
662 DS8Buffer *dsb;
664 hr = DS8Buffer_Create(&dsb, &This->primary, NULL, FALSE);
665 if(SUCCEEDED(hr))
667 hr = DS8Buffer_Initialize(&dsb->IDirectSoundBuffer8_iface, &This->IDirectSound_iface, desc);
668 if(FAILED(hr))
669 IDirectSoundBuffer8_Release(&dsb->IDirectSoundBuffer8_iface);
670 else
672 dsb->bufferlost = (This->prio_level == DSSCL_WRITEPRIMARY);
673 *buf = (IDirectSoundBuffer*)&dsb->IDirectSoundBuffer8_iface;
677 LeaveCriticalSection(&This->share->crst);
679 TRACE("%08lx\n", hr);
680 return hr;
683 static HRESULT WINAPI DS8_GetCaps(IDirectSound8 *iface, LPDSCAPS caps)
685 DS8Impl *This = impl_from_IDirectSound8(iface);
686 DWORD free_bufs, i;
688 TRACE("(%p)->(%p)\n", iface, caps);
690 if(!This->share)
692 WARN("Device not initialized\n");
693 return DSERR_UNINITIALIZED;
696 if(!caps || caps->dwSize < sizeof(*caps))
698 WARN("Invalid DSCAPS (%p, %lu)\n", caps, (caps?caps->dwSize:0));
699 return DSERR_INVALIDPARAM;
702 EnterCriticalSection(&This->share->crst);
704 free_bufs = 0;
705 for(i = 0;i < This->primary.NumBufferGroups;i++)
706 free_bufs += POPCNT64(This->primary.BufferGroups[i].FreeBuffers);
708 caps->dwFlags = DSCAPS_CONTINUOUSRATE | DSCAPS_CERTIFIED |
709 DSCAPS_PRIMARY16BIT | DSCAPS_PRIMARYSTEREO |
710 DSCAPS_PRIMARY8BIT | DSCAPS_PRIMARYMONO |
711 DSCAPS_SECONDARY16BIT | DSCAPS_SECONDARY8BIT |
712 DSCAPS_SECONDARYMONO | DSCAPS_SECONDARYSTEREO;
713 caps->dwPrimaryBuffers = 1;
714 caps->dwMinSecondarySampleRate = DSBFREQUENCY_MIN;
715 caps->dwMaxSecondarySampleRate = DSBFREQUENCY_MAX;
716 caps->dwMaxHwMixingAllBuffers =
717 caps->dwMaxHwMixingStaticBuffers =
718 caps->dwMaxHwMixingStreamingBuffers =
719 caps->dwMaxHw3DAllBuffers =
720 caps->dwMaxHw3DStaticBuffers =
721 caps->dwMaxHw3DStreamingBuffers = This->share->sources.max_alloc;
722 caps->dwFreeHwMixingAllBuffers =
723 caps->dwFreeHwMixingStaticBuffers =
724 caps->dwFreeHwMixingStreamingBuffers =
725 caps->dwFreeHw3DAllBuffers =
726 caps->dwFreeHw3DStaticBuffers =
727 caps->dwFreeHw3DStreamingBuffers = free_bufs;
728 caps->dwTotalHwMemBytes =
729 caps->dwFreeHwMemBytes = 64 * 1024 * 1024;
730 caps->dwMaxContigFreeHwMemBytes = caps->dwFreeHwMemBytes;
731 caps->dwUnlockTransferRateHwBuffers = 4096;
732 caps->dwPlayCpuOverheadSwBuffers = 0;
734 LeaveCriticalSection(&This->share->crst);
736 return DS_OK;
738 static HRESULT WINAPI DS8_DuplicateSoundBuffer(IDirectSound8 *iface, IDirectSoundBuffer *in, IDirectSoundBuffer **out)
740 DS8Impl *This = impl_from_IDirectSound8(iface);
741 DS8Buffer *buf;
742 DSBCAPS caps;
743 HRESULT hr;
745 TRACE("(%p)->(%p, %p)\n", iface, in, out);
747 if(!This->share)
749 WARN("Device not initialized\n");
750 return DSERR_UNINITIALIZED;
753 if(!in || !out)
755 WARN("Invalid pointer: in = %p, out = %p\n", in, out);
756 return DSERR_INVALIDPARAM;
758 *out = NULL;
760 caps.dwSize = sizeof(caps);
761 hr = IDirectSoundBuffer_GetCaps(in, &caps);
762 if(SUCCEEDED(hr) && (caps.dwFlags&DSBCAPS_PRIMARYBUFFER))
764 WARN("Cannot duplicate buffer %p, which has DSBCAPS_PRIMARYBUFFER\n", in);
765 hr = DSERR_INVALIDPARAM;
767 if(SUCCEEDED(hr) && (caps.dwFlags&DSBCAPS_CTRLFX))
769 WARN("Cannot duplicate buffer %p, which has DSBCAPS_CTRLFX\n", in);
770 hr = DSERR_INVALIDPARAM;
772 if(SUCCEEDED(hr))
773 hr = DS8Buffer_Create(&buf, &This->primary, in, FALSE);
774 if(SUCCEEDED(hr))
776 *out = (IDirectSoundBuffer*)&buf->IDirectSoundBuffer8_iface;
777 hr = DS8Buffer_Initialize(&buf->IDirectSoundBuffer8_iface, NULL, NULL);
779 if(SUCCEEDED(hr))
781 /* According to MSDN volume isn't copied */
782 if((caps.dwFlags&DSBCAPS_CTRLPAN))
784 LONG pan;
785 if(SUCCEEDED(IDirectSoundBuffer_GetPan(in, &pan)))
786 IDirectSoundBuffer_SetPan(*out, pan);
788 if((caps.dwFlags&DSBCAPS_CTRLFREQUENCY))
790 DWORD freq;
791 if(SUCCEEDED(IDirectSoundBuffer_GetFrequency(in, &freq)))
792 IDirectSoundBuffer_SetFrequency(*out, freq);
794 if((caps.dwFlags&DSBCAPS_CTRL3D))
796 IDirectSound3DBuffer *buf3d;
797 DS3DBUFFER DS3DBuffer;
798 HRESULT subhr;
800 subhr = IDirectSound_QueryInterface(in, &IID_IDirectSound3DBuffer, (void**)&buf3d);
801 if(SUCCEEDED(subhr))
803 DS3DBuffer.dwSize = sizeof(DS3DBuffer);
804 subhr = IDirectSound3DBuffer_GetAllParameters(buf3d, &DS3DBuffer);
805 IDirectSound3DBuffer_Release(buf3d);
807 if(SUCCEEDED(subhr))
808 subhr = IDirectSoundBuffer_QueryInterface(*out, &IID_IDirectSound3DBuffer, (void**)&buf3d);
809 if(SUCCEEDED(subhr))
811 subhr = IDirectSound3DBuffer_SetAllParameters(buf3d, &DS3DBuffer, DS3D_IMMEDIATE);
812 IDirectSound3DBuffer_Release(buf3d);
816 if(FAILED(hr))
818 if(*out)
819 IDirectSoundBuffer_Release(*out);
820 *out = NULL;
823 return hr;
826 static HRESULT WINAPI DS8_SetCooperativeLevel(IDirectSound8 *iface, HWND hwnd, DWORD level)
828 DS8Impl *This = impl_from_IDirectSound8(iface);
829 HRESULT hr = S_OK;
831 TRACE("(%p)->(%p, %lu)\n", iface, hwnd, level);
833 if(!This->share)
835 WARN("Device not initialized\n");
836 return DSERR_UNINITIALIZED;
839 if(level > DSSCL_WRITEPRIMARY || level < DSSCL_NORMAL)
841 WARN("Invalid coop level: %lu\n", level);
842 return DSERR_INVALIDPARAM;
845 EnterCriticalSection(&This->share->crst);
846 if(level == DSSCL_WRITEPRIMARY && (This->prio_level != DSSCL_WRITEPRIMARY))
848 struct DSBufferGroup *bufgroup = This->primary.BufferGroups;
849 DWORD i, state;
851 if(This->primary.write_emu)
853 ERR("Why was there a write_emu?\n");
854 /* Delete it */
855 IDirectSoundBuffer8_Release(This->primary.write_emu);
856 This->primary.write_emu = NULL;
859 for(i = 0;i < This->primary.NumBufferGroups;++i)
861 DWORD64 usemask = ~bufgroup[i].FreeBuffers;
862 while(usemask)
864 int idx = CTZ64(usemask);
865 DS8Buffer *buf = bufgroup[i].Buffers + idx;
866 usemask &= ~(U64(1) << idx);
868 if(FAILED(IDirectSoundBuffer_GetStatus(&buf->IDirectSoundBuffer8_iface, &state)) ||
869 (state&DSBSTATUS_PLAYING))
871 WARN("DSSCL_WRITEPRIMARY set with playing buffers!\n");
872 hr = DSERR_INVALIDCALL;
873 goto out;
875 /* Mark buffer as lost */
876 buf->bufferlost = 1;
880 if(This->primary.flags)
882 /* Primary has open references.. create write_emu */
883 DSBUFFERDESC desc;
884 DS8Buffer *emu;
886 memset(&desc, 0, sizeof(desc));
887 desc.dwSize = sizeof(desc);
888 desc.dwFlags = DSBCAPS_LOCHARDWARE | (This->primary.flags&DSBCAPS_CTRLPAN);
889 desc.dwBufferBytes = This->primary.buf_size;
890 desc.lpwfxFormat = &This->primary.format.Format;
892 hr = DS8Buffer_Create(&emu, &This->primary, NULL, TRUE);
893 if(SUCCEEDED(hr))
895 This->primary.write_emu = &emu->IDirectSoundBuffer8_iface;
896 hr = IDirectSoundBuffer8_Initialize(This->primary.write_emu, &This->IDirectSound_iface, &desc);
897 if(FAILED(hr))
899 IDirectSoundBuffer8_Release(This->primary.write_emu);
900 This->primary.write_emu = NULL;
905 else if(This->prio_level == DSSCL_WRITEPRIMARY && level != DSSCL_WRITEPRIMARY)
907 /* Delete it */
908 TRACE("Nuking write_emu\n");
909 if(This->primary.write_emu)
910 IDirectSoundBuffer8_Release(This->primary.write_emu);
911 This->primary.write_emu = NULL;
913 if(SUCCEEDED(hr))
914 This->prio_level = level;
915 out:
916 LeaveCriticalSection(&This->share->crst);
918 return hr;
921 static HRESULT WINAPI DS8_Compact(IDirectSound8 *iface)
923 DS8Impl *This = impl_from_IDirectSound8(iface);
924 HRESULT hr = S_OK;
926 TRACE("(%p)->()\n", iface);
928 if(!This->share)
930 WARN("Device not initialized\n");
931 return DSERR_UNINITIALIZED;
934 EnterCriticalSection(&This->share->crst);
935 if(This->prio_level < DSSCL_PRIORITY)
937 WARN("Coop level not high enough (%lu)\n", This->prio_level);
938 hr = DSERR_PRIOLEVELNEEDED;
940 LeaveCriticalSection(&This->share->crst);
942 return hr;
945 static HRESULT WINAPI DS8_GetSpeakerConfig(IDirectSound8 *iface, DWORD *config)
947 DS8Impl *This = impl_from_IDirectSound8(iface);
949 TRACE("(%p)->(%p)\n", iface, config);
951 if(!config)
952 return DSERR_INVALIDPARAM;
953 *config = 0;
955 if(!This->share)
957 WARN("Device not initialized\n");
958 return DSERR_UNINITIALIZED;
961 *config = This->share->speaker_config;
963 return DS_OK;
966 static HRESULT WINAPI DS8_SetSpeakerConfig(IDirectSound8 *iface, DWORD config)
968 DS8Impl *This = impl_from_IDirectSound8(iface);
969 DWORD geo, speaker;
971 TRACE("(%p)->(0x%08lx)\n", iface, config);
973 if(!This->share)
975 WARN("Device not initialized\n");
976 return DSERR_UNINITIALIZED;
979 geo = DSSPEAKER_GEOMETRY(config);
980 speaker = DSSPEAKER_CONFIG(config);
982 if(geo && (geo < DSSPEAKER_GEOMETRY_MIN || geo > DSSPEAKER_GEOMETRY_MAX))
984 WARN("Invalid speaker angle %lu\n", geo);
985 return DSERR_INVALIDPARAM;
987 if(speaker < DSSPEAKER_HEADPHONE || speaker > DSSPEAKER_7POINT1)
989 WARN("Invalid speaker config %lu\n", speaker);
990 return DSERR_INVALIDPARAM;
993 /* No-op on Vista+. */
994 return DS_OK;
997 static HRESULT WINAPI DS8_Initialize(IDirectSound8 *iface, const GUID *devguid)
999 DS8Impl *This = impl_from_IDirectSound8(iface);
1000 HRESULT hr;
1001 GUID guid;
1002 UINT n;
1004 TRACE("(%p)->(%s)\n", iface, debugstr_guid(devguid));
1006 if(!openal_loaded)
1007 return DSERR_NODRIVER;
1009 if(This->share)
1011 WARN("Device already initialized\n");
1012 return DSERR_ALREADYINITIALIZED;
1015 if(!devguid || IsEqualGUID(devguid, &GUID_NULL))
1016 devguid = &DSDEVID_DefaultPlayback;
1017 else if(IsEqualGUID(devguid, &DSDEVID_DefaultCapture) ||
1018 IsEqualGUID(devguid, &DSDEVID_DefaultVoiceCapture))
1019 return DSERR_NODRIVER;
1021 hr = GetDeviceID(devguid, &guid);
1022 if(FAILED(hr)) return hr;
1024 EnterCriticalSection(&openal_crst);
1026 for(n = 0;n < sharelistsize;n++)
1028 if(IsEqualGUID(&sharelist[n]->guid, &guid))
1030 TRACE("Matched already open device %p\n", sharelist[n]->device);
1032 This->share = sharelist[n];
1033 DSShare_AddRef(This->share);
1034 break;
1038 if(!This->share)
1039 hr = DSShare_Create(&guid, &This->share);
1040 if(SUCCEEDED(hr))
1042 This->device = This->share->device;
1043 hr = DS8Primary_PreInit(&This->primary, This);
1046 if(SUCCEEDED(hr))
1048 DeviceShare *share = This->share;
1049 DS8Primary **prims;
1051 EnterCriticalSection(&share->crst);
1053 prims = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
1054 (share->nprimaries+1) * sizeof(*prims));
1055 if(!prims)
1056 hr = DSERR_OUTOFMEMORY;
1057 else
1059 ALsizei i;
1060 for(i = 0;i < share->nprimaries;++i)
1061 prims[i] = share->primaries[i];
1062 prims[i] = &This->primary;
1064 HeapFree(GetProcessHeap(), 0, share->primaries);
1065 share->primaries = prims;
1066 share->nprimaries += 1;
1069 LeaveCriticalSection(&share->crst);
1072 if(FAILED(hr))
1074 if(This->share)
1075 DSShare_Release(This->share);
1076 This->share = NULL;
1079 LeaveCriticalSection(&openal_crst);
1080 return hr;
1083 /* I, Maarten Lankhorst, hereby declare this driver certified
1084 * What this means.. ? An extra bit set
1086 static HRESULT WINAPI DS8_VerifyCertification(IDirectSound8 *iface, DWORD *certified)
1088 DS8Impl *This = impl_from_IDirectSound8(iface);
1090 TRACE("(%p)->(%p)\n", iface, certified);
1092 if(!certified)
1093 return DSERR_INVALIDPARAM;
1094 *certified = 0;
1096 if(!This->share)
1098 WARN("Device not initialized\n");
1099 return DSERR_UNINITIALIZED;
1102 *certified = DS_CERTIFIED;
1104 return DS_OK;
1107 static const IDirectSound8Vtbl DS8_Vtbl = {
1108 DS8_QueryInterface,
1109 DS8_AddRef,
1110 DS8_Release,
1111 DS8_CreateSoundBuffer,
1112 DS8_GetCaps,
1113 DS8_DuplicateSoundBuffer,
1114 DS8_SetCooperativeLevel,
1115 DS8_Compact,
1116 DS8_GetSpeakerConfig,
1117 DS8_SetSpeakerConfig,
1118 DS8_Initialize,
1119 DS8_VerifyCertification
1123 static HRESULT WINAPI DS_QueryInterface(IDirectSound *iface, REFIID riid, LPVOID *ppv)
1125 DS8Impl *This = impl_from_IDirectSound(iface);
1126 return DS8_QueryInterface(&This->IDirectSound8_iface, riid, ppv);
1129 static ULONG WINAPI DS_AddRef(IDirectSound *iface)
1131 DS8Impl *This = impl_from_IDirectSound(iface);
1132 return DS8_AddRef(&This->IDirectSound8_iface);
1135 static ULONG WINAPI DS_Release(IDirectSound *iface)
1137 DS8Impl *This = impl_from_IDirectSound(iface);
1138 return DS8_Release(&This->IDirectSound8_iface);
1141 static HRESULT WINAPI DS_CreateSoundBuffer(IDirectSound *iface, LPCDSBUFFERDESC desc, LPLPDIRECTSOUNDBUFFER buf, IUnknown *pUnkOuter)
1143 DS8Impl *This = impl_from_IDirectSound(iface);
1144 return DS8_CreateSoundBuffer(&This->IDirectSound8_iface, desc, buf, pUnkOuter);
1147 static HRESULT WINAPI DS_GetCaps(IDirectSound *iface, LPDSCAPS caps)
1149 DS8Impl *This = impl_from_IDirectSound(iface);
1150 return DS8_GetCaps(&This->IDirectSound8_iface, caps);
1152 static HRESULT WINAPI DS_DuplicateSoundBuffer(IDirectSound *iface, IDirectSoundBuffer *in, IDirectSoundBuffer **out)
1154 DS8Impl *This = impl_from_IDirectSound(iface);
1155 return DS8_DuplicateSoundBuffer(&This->IDirectSound8_iface, in, out);
1158 static HRESULT WINAPI DS_SetCooperativeLevel(IDirectSound *iface, HWND hwnd, DWORD level)
1160 DS8Impl *This = impl_from_IDirectSound(iface);
1161 return DS8_SetCooperativeLevel(&This->IDirectSound8_iface, hwnd, level);
1164 static HRESULT WINAPI DS_Compact(IDirectSound *iface)
1166 DS8Impl *This = impl_from_IDirectSound(iface);
1167 return DS8_Compact(&This->IDirectSound8_iface);
1170 static HRESULT WINAPI DS_GetSpeakerConfig(IDirectSound *iface, DWORD *config)
1172 DS8Impl *This = impl_from_IDirectSound(iface);
1173 return DS8_GetSpeakerConfig(&This->IDirectSound8_iface, config);
1176 static HRESULT WINAPI DS_SetSpeakerConfig(IDirectSound *iface, DWORD config)
1178 DS8Impl *This = impl_from_IDirectSound(iface);
1179 return DS8_SetSpeakerConfig(&This->IDirectSound8_iface, config);
1182 static HRESULT WINAPI DS_Initialize(IDirectSound *iface, const GUID *devguid)
1184 DS8Impl *This = impl_from_IDirectSound(iface);
1185 return DS8_Initialize(&This->IDirectSound8_iface, devguid);
1188 static const IDirectSoundVtbl DS_Vtbl = {
1189 DS_QueryInterface,
1190 DS_AddRef,
1191 DS_Release,
1192 DS_CreateSoundBuffer,
1193 DS_GetCaps,
1194 DS_DuplicateSoundBuffer,
1195 DS_SetCooperativeLevel,
1196 DS_Compact,
1197 DS_GetSpeakerConfig,
1198 DS_SetSpeakerConfig,
1199 DS_Initialize