Use the correct type instead of a macro alias
[dsound-openal.git] / dsound8.c
blob2f85a3f64eddda6a377e26084bee394fbd42f6dd
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>
30 #include "dsound_private.h"
32 #ifndef DSSPEAKER_7POINT1
33 #define DSSPEAKER_7POINT1 7
34 #endif
37 static DWORD CALLBACK DSShare_thread(void *dwUser)
39 DeviceShare *share = (DeviceShare*)dwUser;
40 BYTE *scratch_mem = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, 2048);
41 ALsizei i;
43 SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
45 TRACE("Shared device (%p) message loop start\n", share);
46 while(WaitForSingleObject(share->timer_evt, INFINITE) == WAIT_OBJECT_0 && !share->quit_now)
48 EnterCriticalSection(&share->crst);
49 setALContext(share->ctx);
51 for(i = 0;i < share->nprimaries;++i)
53 DS8Primary_triggernots(share->primaries[i]);
54 if(!BITFIELD_TEST(share->Exts, SOFTX_MAP_BUFFER))
55 DS8Primary_streamfeeder(share->primaries[i], scratch_mem);
58 popALContext();
59 LeaveCriticalSection(&share->crst);
61 TRACE("Shared device (%p) message loop quit\n", share);
63 HeapFree(GetProcessHeap(), 0, scratch_mem);
64 scratch_mem = NULL;
66 if(local_contexts)
68 set_context(NULL);
69 TlsSetValue(TlsThreadPtr, NULL);
72 return 0;
75 static void CALLBACK DSShare_timer(void *arg, BOOLEAN unused)
77 (void)unused;
78 SetEvent((HANDLE)arg);
81 static void DSShare_starttimer(DeviceShare *share)
83 DWORD triggertime;
85 if(share->queue_timer)
86 return;
88 triggertime = 1000 / share->refresh * 2 / 3;
89 TRACE("Calling timer every %lu ms for %d refreshes per second\n",
90 triggertime, share->refresh);
92 CreateTimerQueueTimer(&share->queue_timer, NULL, DSShare_timer, share->timer_evt,
93 triggertime, triggertime, WT_EXECUTEINTIMERTHREAD);
98 static DeviceShare **sharelist;
99 static UINT sharelistsize;
101 static void DSShare_Destroy(DeviceShare *share)
103 UINT i;
105 EnterCriticalSection(&openal_crst);
106 for(i = 0;i < sharelistsize;i++)
108 if(sharelist[i] == share)
110 sharelist[i] = sharelist[--sharelistsize];
111 if(sharelistsize == 0)
113 HeapFree(GetProcessHeap(), 0, sharelist);
114 sharelist = NULL;
116 break;
119 LeaveCriticalSection(&openal_crst);
121 if(share->queue_timer)
122 DeleteTimerQueueTimer(NULL, share->queue_timer, INVALID_HANDLE_VALUE);
123 share->queue_timer = NULL;
125 if(share->thread_hdl)
127 InterlockedExchange(&share->quit_now, TRUE);
128 SetEvent(share->timer_evt);
130 if(WaitForSingleObject(share->thread_hdl, 1000) != WAIT_OBJECT_0)
131 ERR("Thread wait timed out\n");
133 CloseHandle(share->thread_hdl);
134 share->thread_hdl = NULL;
137 if(share->timer_evt)
138 CloseHandle(share->timer_evt);
139 share->timer_evt = NULL;
141 if(share->ctx)
143 /* Calling setALContext is not appropriate here, since we *have* to
144 * unset the context before destroying it
146 EnterCriticalSection(&openal_crst);
147 set_context(share->ctx);
149 if(share->sources.max_alloc)
150 alDeleteSources(share->sources.max_alloc, share->sources.ids);
151 share->sources.max_alloc = 0;
153 if(share->auxslot)
154 alDeleteAuxiliaryEffectSlots(1, &share->auxslot);
155 share->auxslot = 0;
157 set_context(NULL);
158 TlsSetValue(TlsThreadPtr, NULL);
159 alcDestroyContext(share->ctx);
160 share->ctx = NULL;
161 LeaveCriticalSection(&openal_crst);
164 if(share->device)
165 alcCloseDevice(share->device);
166 share->device = NULL;
168 DeleteCriticalSection(&share->crst);
170 HeapFree(GetProcessHeap(), 0, share->primaries);
171 HeapFree(GetProcessHeap(), 0, share);
174 static HRESULT DSShare_Create(REFIID guid, DeviceShare **out)
176 static const struct {
177 const char extname[64];
178 int extenum;
179 } extensions[MAX_EXTENSIONS] = {
180 { "ALC_EXT_EFX", EXT_EFX },
181 { "AL_EXT_FLOAT32", EXT_FLOAT32 },
182 { "AL_EXT_MCFORMATS", EXT_MCFORMATS },
183 { "AL_SOFT_deferred_updates", SOFT_DEFERRED_UPDATES },
184 { "AL_SOFT_source_spatialize", SOFT_SOURCE_SPATIALIZE },
185 { "AL_SOFTX_map_buffer", SOFTX_MAP_BUFFER },
187 OLECHAR *guid_str = NULL;
188 ALchar drv_name[64];
189 DeviceShare *share;
190 void *temp;
191 HRESULT hr;
192 size_t i;
194 share = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*share));
195 if(!share) return DSERR_OUTOFMEMORY;
196 share->ref = 1;
197 share->refresh = FAKE_REFRESH_COUNT;
199 InitializeCriticalSection(&share->crst);
201 hr = StringFromCLSID(guid, &guid_str);
202 if(FAILED(hr))
204 ERR("Failed to convert GUID to string\n");
205 goto fail;
207 WideCharToMultiByte(CP_UTF8, 0, guid_str, -1, drv_name, sizeof(drv_name), NULL, NULL);
208 drv_name[sizeof(drv_name)-1] = 0;
209 CoTaskMemFree(guid_str);
210 guid_str = NULL;
212 hr = DSERR_NODRIVER;
213 share->device = alcOpenDevice(drv_name);
214 if(!share->device)
216 alcGetError(NULL);
217 WARN("Couldn't open device \"%s\"\n", drv_name);
218 goto fail;
220 TRACE("Opened device: %s\n",
221 alcIsExtensionPresent(share->device, "ALC_ENUMERATE_ALL_EXT") ?
222 alcGetString(share->device, ALC_ALL_DEVICES_SPECIFIER) :
223 alcGetString(share->device, ALC_DEVICE_SPECIFIER));
225 share->ctx = alcCreateContext(share->device, NULL);
226 if(!share->ctx)
228 ALCenum err = alcGetError(share->device);
229 ERR("Could not create context (0x%x)!\n", err);
230 goto fail;
233 memcpy(&share->guid, guid, sizeof(GUID));
235 setALContext(share->ctx);
236 alcGetIntegerv(share->device, ALC_REFRESH, 1, &share->refresh);
237 checkALCError(share->device);
239 for(i = 0;i < MAX_EXTENSIONS;i++)
241 if((strncmp(extensions[i].extname, "ALC", 3) == 0) ?
242 alcIsExtensionPresent(share->device, extensions[i].extname) :
243 alIsExtensionPresent(extensions[i].extname))
245 TRACE("Found %s\n", extensions[i].extname);
246 BITFIELD_SET(share->Exts, extensions[i].extenum);
250 if(BITFIELD_TEST(share->Exts, EXT_EFX))
251 alGenAuxiliaryEffectSlots(1, &share->auxslot);
253 share->sources.max_alloc = 0;
254 while(share->sources.max_alloc < MAX_SOURCES)
256 alGenSources(1, &share->sources.ids[share->sources.max_alloc]);
257 if(alGetError() != AL_NO_ERROR)
258 break;
259 share->sources.max_alloc++;
261 popALContext();
262 /* As long as we have at least 64 sources, keep it a multiple of 64. */
263 if(share->sources.max_alloc > 64)
264 share->sources.max_alloc &= ~63u;
265 share->sources.avail_num = share->sources.max_alloc;
267 if(sharelist)
268 temp = HeapReAlloc(GetProcessHeap(), 0, sharelist, sizeof(*sharelist)*(sharelistsize+1));
269 else
270 temp = HeapAlloc(GetProcessHeap(), 0, sizeof(*sharelist)*(sharelistsize+1));
271 if(temp)
273 sharelist = temp;
274 sharelist[sharelistsize++] = share;
277 hr = E_FAIL;
279 share->quit_now = FALSE;
280 share->timer_evt = CreateEventA(NULL, FALSE, FALSE, NULL);
281 if(!share->timer_evt) goto fail;
283 share->queue_timer = NULL;
285 share->thread_hdl = CreateThread(NULL, 0, DSShare_thread, share, 0, &share->thread_id);
286 if(!share->thread_hdl) goto fail;
288 DSShare_starttimer(share);
290 *out = share;
291 return DS_OK;
293 fail:
294 DSShare_Destroy(share);
295 return hr;
298 static ULONG DSShare_AddRef(DeviceShare *share)
300 ULONG ref = InterlockedIncrement(&share->ref);
301 return ref;
304 static ULONG DSShare_Release(DeviceShare *share)
306 ULONG ref = InterlockedDecrement(&share->ref);
307 if(ref == 0) DSShare_Destroy(share);
308 return ref;
312 static const IDirectSound8Vtbl DS8_Vtbl;
313 static const IDirectSoundVtbl DS_Vtbl;
314 static const IUnknownVtbl DS8_Unknown_Vtbl;
316 static void DS8Impl_Destroy(DS8Impl *This);
317 static HRESULT WINAPI DS8_QueryInterface(IDirectSound8 *iface, REFIID riid, LPVOID *ppv);
319 /*******************************************************************************
320 * IUnknown
322 static inline DS8Impl *impl_from_IUnknown(IUnknown *iface)
324 return CONTAINING_RECORD(iface, DS8Impl, IUnknown_iface);
327 static HRESULT WINAPI DS8Impl_IUnknown_QueryInterface(IUnknown *iface, REFIID riid, void **ppobj)
329 DS8Impl *This = impl_from_IUnknown(iface);
330 return DS8_QueryInterface(&This->IDirectSound8_iface, riid, ppobj);
333 static ULONG WINAPI DS8Impl_IUnknown_AddRef(IUnknown *iface)
335 DS8Impl *This = impl_from_IUnknown(iface);
336 ULONG ref;
338 InterlockedIncrement(&(This->ref));
339 ref = InterlockedIncrement(&(This->unkref));
340 TRACE("(%p) ref was %lu\n", This, ref - 1);
342 return ref;
345 static ULONG WINAPI DS8Impl_IUnknown_Release(IUnknown *iface)
347 DS8Impl *This = impl_from_IUnknown(iface);
348 ULONG ref = InterlockedDecrement(&(This->unkref));
349 TRACE("(%p) ref was %lu\n", This, ref + 1);
350 if(InterlockedDecrement(&(This->ref)) == 0)
351 DS8Impl_Destroy(This);
352 return ref;
355 static const IUnknownVtbl DS8_Unknown_Vtbl = {
356 DS8Impl_IUnknown_QueryInterface,
357 DS8Impl_IUnknown_AddRef,
358 DS8Impl_IUnknown_Release
362 static inline DS8Impl *impl_from_IDirectSound8(IDirectSound8 *iface)
364 return CONTAINING_RECORD(iface, DS8Impl, IDirectSound8_iface);
367 static inline DS8Impl *impl_from_IDirectSound(IDirectSound *iface)
369 return CONTAINING_RECORD(iface, DS8Impl, IDirectSound_iface);
372 HRESULT DSOUND_Create(REFIID riid, void **ds)
374 HRESULT hr;
376 hr = DSOUND_Create8(&IID_IDirectSound, ds);
377 if(SUCCEEDED(hr))
379 DS8Impl *impl = impl_from_IDirectSound(*ds);
380 impl->is_8 = FALSE;
382 if(!IsEqualIID(riid, &IID_IDirectSound))
384 hr = IDirectSound_QueryInterface(&impl->IDirectSound_iface, riid, ds);
385 IDirectSound_Release(&impl->IDirectSound_iface);
388 return hr;
392 static const WCHAR speakerconfigkey[] =
393 L"SYSTEM\\CurrentControlSet\\Control\\MediaResources\\DirectSound\\Speaker Configuration";
394 static const WCHAR speakerconfig[] = L"Speaker Configuration";
396 HRESULT DSOUND_Create8(REFIID riid, LPVOID *ds)
398 DS8Impl *This;
399 HKEY regkey;
400 HRESULT hr;
402 *ds = NULL;
403 This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*This));
404 if(!This) return DSERR_OUTOFMEMORY;
406 This->IDirectSound8_iface.lpVtbl = &DS8_Vtbl;
407 This->IDirectSound_iface.lpVtbl = &DS_Vtbl;
408 This->IUnknown_iface.lpVtbl = &DS8_Unknown_Vtbl;
410 This->is_8 = TRUE;
411 This->speaker_config = DSSPEAKER_COMBINED(DSSPEAKER_5POINT1, DSSPEAKER_GEOMETRY_WIDE);
413 if(RegOpenKeyExW(HKEY_LOCAL_MACHINE, speakerconfigkey, 0, KEY_READ, &regkey) == ERROR_SUCCESS)
415 DWORD type, conf, confsize = sizeof(DWORD);
417 if(RegQueryValueExW(regkey, speakerconfig, NULL, &type, (BYTE*)&conf, &confsize) == ERROR_SUCCESS)
419 if(type == REG_DWORD)
420 This->speaker_config = conf;
423 RegCloseKey(regkey);
425 /*RegGetValueW(HKEY_LOCAL_MACHINE, speakerconfigkey, speakerconfig, RRF_RT_REG_DWORD, NULL, &This->speaker_config, NULL);*/
427 hr = IDirectSound8_QueryInterface(&This->IDirectSound8_iface, riid, ds);
428 if(FAILED(hr))
429 DS8Impl_Destroy(This);
430 return hr;
433 static void DS8Impl_Destroy(DS8Impl *This)
435 DeviceShare *share = This->share;
437 if(share)
439 ALsizei i;
441 EnterCriticalSection(&share->crst);
443 for(i = 0;i < share->nprimaries;++i)
445 if(share->primaries[i] == &This->primary)
447 share->nprimaries -= 1;
448 share->primaries[i] = share->primaries[share->nprimaries];
449 break;
453 LeaveCriticalSection(&share->crst);
456 DS8Primary_Clear(&This->primary);
457 if(This->share)
458 DSShare_Release(This->share);
459 This->share = NULL;
461 HeapFree(GetProcessHeap(), 0, This);
465 static HRESULT WINAPI DS8_QueryInterface(IDirectSound8 *iface, REFIID riid, LPVOID *ppv)
467 DS8Impl *This = impl_from_IDirectSound8(iface);
469 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
471 *ppv = NULL;
472 if(IsEqualIID(riid, &IID_IUnknown))
473 *ppv = &This->IUnknown_iface;
474 else if(IsEqualIID(riid, &IID_IDirectSound8))
476 if(This->is_8)
477 *ppv = &This->IDirectSound8_iface;
479 else if(IsEqualIID(riid, &IID_IDirectSound))
480 *ppv = &This->IDirectSound_iface;
481 else
482 FIXME("Unhandled GUID: %s\n", debugstr_guid(riid));
484 if(*ppv)
486 IUnknown_AddRef((IUnknown*)*ppv);
487 return S_OK;
490 return E_NOINTERFACE;
493 static ULONG WINAPI DS8_AddRef(IDirectSound8 *iface)
495 DS8Impl *This = impl_from_IDirectSound8(iface);
496 LONG ref;
498 InterlockedIncrement(&This->ref);
499 ref = InterlockedIncrement(&This->dsref);
500 TRACE("Reference count incremented to %ld\n", ref);
502 return ref;
505 static ULONG WINAPI DS8_Release(IDirectSound8 *iface)
507 DS8Impl *This = impl_from_IDirectSound8(iface);
508 LONG ref;
510 ref = InterlockedDecrement(&This->dsref);
511 TRACE("Reference count decremented to %ld\n", ref);
512 if(InterlockedDecrement(&This->ref) == 0)
513 DS8Impl_Destroy(This);
515 return ref;
518 static HRESULT WINAPI DS8_CreateSoundBuffer(IDirectSound8 *iface, LPCDSBUFFERDESC desc, LPLPDIRECTSOUNDBUFFER buf, IUnknown *pUnkOuter)
520 DS8Impl *This = impl_from_IDirectSound8(iface);
521 HRESULT hr;
523 TRACE("(%p)->(%p, %p, %p)\n", iface, desc, buf, pUnkOuter);
525 if(!buf)
527 WARN("buf is null\n");
528 return DSERR_INVALIDPARAM;
530 *buf = NULL;
532 if(pUnkOuter)
534 WARN("Aggregation isn't supported\n");
535 return DSERR_NOAGGREGATION;
537 if(!desc || desc->dwSize < sizeof(DSBUFFERDESC1))
539 WARN("Invalid buffer %p/%lu\n", desc, desc?desc->dwSize:0);
540 return DSERR_INVALIDPARAM;
543 if(!This->share)
545 WARN("Device not initialized\n");
546 return DSERR_UNINITIALIZED;
549 TRACE("Requested buffer:\n"
550 " Size = %lu\n"
551 " Flags = 0x%08lx\n"
552 " BufferBytes = %lu\n",
553 desc->dwSize, desc->dwFlags, desc->dwBufferBytes);
555 if(desc->dwSize >= sizeof(DSBUFFERDESC))
557 if(!(desc->dwFlags&DSBCAPS_CTRL3D))
559 if(!IsEqualGUID(&desc->guid3DAlgorithm, &GUID_NULL))
561 WARN("Invalid 3D algorithm GUID specified for non-3D buffer: %s\n", debugstr_guid(&desc->guid3DAlgorithm));
562 return DSERR_INVALIDPARAM;
565 else
566 TRACE("Requested 3D algorithm GUID: %s\n", debugstr_guid(&desc->guid3DAlgorithm));
569 /* OpenAL doesn't support playing with 3d and panning at same time.. */
570 if((desc->dwFlags&(DSBCAPS_CTRL3D|DSBCAPS_CTRLPAN)) == (DSBCAPS_CTRL3D|DSBCAPS_CTRLPAN))
572 if(!This->is_8)
574 static int once = 0;
575 if(!once++)
576 FIXME("Buffers with 3D and panning control ignore panning\n");
578 else
580 WARN("Cannot create buffers with 3D and panning control\n");
581 return DSERR_INVALIDPARAM;
585 EnterCriticalSection(This->primary.crst);
586 if((desc->dwFlags&DSBCAPS_PRIMARYBUFFER))
588 IDirectSoundBuffer *prim = &This->primary.IDirectSoundBuffer_iface;
590 hr = S_OK;
591 if(IDirectSoundBuffer_AddRef(prim) == 1)
593 hr = DS8Primary_Initialize(prim, &This->IDirectSound_iface, desc);
594 if(FAILED(hr))
596 IDirectSoundBuffer_Release(prim);
597 prim = NULL;
600 *buf = prim;
602 else
604 DS8Buffer *dsb;
606 hr = DS8Buffer_Create(&dsb, &This->primary, NULL, FALSE);
607 if(SUCCEEDED(hr))
609 hr = DS8Buffer_Initialize(&dsb->IDirectSoundBuffer8_iface, &This->IDirectSound_iface, desc);
610 if(FAILED(hr))
611 IDirectSoundBuffer8_Release(&dsb->IDirectSoundBuffer8_iface);
612 else
614 dsb->bufferlost = (This->prio_level == DSSCL_WRITEPRIMARY);
615 *buf = (IDirectSoundBuffer*)&dsb->IDirectSoundBuffer8_iface;
619 LeaveCriticalSection(This->primary.crst);
621 TRACE("%08lx\n", hr);
622 return hr;
625 static HRESULT WINAPI DS8_GetCaps(IDirectSound8 *iface, LPDSCAPS caps)
627 DS8Impl *This = impl_from_IDirectSound8(iface);
628 DWORD free_bufs, i;
630 TRACE("(%p)->(%p)\n", iface, caps);
632 if(!This->share)
634 WARN("Device not initialized\n");
635 return DSERR_UNINITIALIZED;
638 if(!caps || caps->dwSize < sizeof(*caps))
640 WARN("Invalid DSCAPS (%p, %lu)\n", caps, (caps?caps->dwSize:0));
641 return DSERR_INVALIDPARAM;
644 EnterCriticalSection(&This->share->crst);
646 free_bufs = 0;
647 for(i = 0;i < This->primary.NumBufferGroups;i++)
648 free_bufs += POPCNT64(This->primary.BufferGroups[i].FreeBuffers);
650 caps->dwFlags = DSCAPS_CONTINUOUSRATE | DSCAPS_CERTIFIED |
651 DSCAPS_PRIMARY16BIT | DSCAPS_PRIMARYSTEREO |
652 DSCAPS_PRIMARY8BIT | DSCAPS_PRIMARYMONO |
653 DSCAPS_SECONDARY16BIT | DSCAPS_SECONDARY8BIT |
654 DSCAPS_SECONDARYMONO | DSCAPS_SECONDARYSTEREO;
655 caps->dwPrimaryBuffers = 1;
656 caps->dwMinSecondarySampleRate = DSBFREQUENCY_MIN;
657 caps->dwMaxSecondarySampleRate = DSBFREQUENCY_MAX;
658 caps->dwMaxHwMixingAllBuffers =
659 caps->dwMaxHwMixingStaticBuffers =
660 caps->dwMaxHwMixingStreamingBuffers =
661 caps->dwMaxHw3DAllBuffers =
662 caps->dwMaxHw3DStaticBuffers =
663 caps->dwMaxHw3DStreamingBuffers = This->share->sources.max_alloc;
664 caps->dwFreeHwMixingAllBuffers =
665 caps->dwFreeHwMixingStaticBuffers =
666 caps->dwFreeHwMixingStreamingBuffers =
667 caps->dwFreeHw3DAllBuffers =
668 caps->dwFreeHw3DStaticBuffers =
669 caps->dwFreeHw3DStreamingBuffers = free_bufs;
670 caps->dwTotalHwMemBytes =
671 caps->dwFreeHwMemBytes = 64 * 1024 * 1024;
672 caps->dwMaxContigFreeHwMemBytes = caps->dwFreeHwMemBytes;
673 caps->dwUnlockTransferRateHwBuffers = 4096;
674 caps->dwPlayCpuOverheadSwBuffers = 0;
676 LeaveCriticalSection(&This->share->crst);
678 return DS_OK;
680 static HRESULT WINAPI DS8_DuplicateSoundBuffer(IDirectSound8 *iface, IDirectSoundBuffer *in, IDirectSoundBuffer **out)
682 DS8Impl *This = impl_from_IDirectSound8(iface);
683 DS8Buffer *buf;
684 DSBCAPS caps;
685 HRESULT hr;
687 TRACE("(%p)->(%p, %p)\n", iface, in, out);
689 if(!This->share)
691 WARN("Device not initialized\n");
692 return DSERR_UNINITIALIZED;
695 if(!in || !out)
697 WARN("Invalid pointer: in = %p, out = %p\n", in, out);
698 return DSERR_INVALIDPARAM;
700 *out = NULL;
702 caps.dwSize = sizeof(caps);
703 hr = IDirectSoundBuffer_GetCaps(in, &caps);
704 if(SUCCEEDED(hr) && (caps.dwFlags&DSBCAPS_PRIMARYBUFFER))
706 WARN("Cannot duplicate buffer %p, which has DSBCAPS_PRIMARYBUFFER\n", in);
707 hr = DSERR_INVALIDPARAM;
709 if(SUCCEEDED(hr) && (caps.dwFlags&DSBCAPS_CTRLFX))
711 WARN("Cannot duplicate buffer %p, which has DSBCAPS_CTRLFX\n", in);
712 hr = DSERR_INVALIDPARAM;
714 if(SUCCEEDED(hr))
715 hr = DS8Buffer_Create(&buf, &This->primary, in, FALSE);
716 if(SUCCEEDED(hr))
718 *out = (IDirectSoundBuffer*)&buf->IDirectSoundBuffer8_iface;
719 hr = DS8Buffer_Initialize(&buf->IDirectSoundBuffer8_iface, NULL, NULL);
721 if(SUCCEEDED(hr))
723 /* According to MSDN volume isn't copied */
724 if((caps.dwFlags&DSBCAPS_CTRLPAN))
726 LONG pan;
727 if(SUCCEEDED(IDirectSoundBuffer_GetPan(in, &pan)))
728 IDirectSoundBuffer_SetPan(*out, pan);
730 if((caps.dwFlags&DSBCAPS_CTRLFREQUENCY))
732 DWORD freq;
733 if(SUCCEEDED(IDirectSoundBuffer_GetFrequency(in, &freq)))
734 IDirectSoundBuffer_SetFrequency(*out, freq);
736 if((caps.dwFlags&DSBCAPS_CTRL3D))
738 IDirectSound3DBuffer *buf3d;
739 DS3DBUFFER DS3DBuffer;
740 HRESULT subhr;
742 subhr = IDirectSound_QueryInterface(in, &IID_IDirectSound3DBuffer, (void**)&buf3d);
743 if(SUCCEEDED(subhr))
745 DS3DBuffer.dwSize = sizeof(DS3DBuffer);
746 subhr = IDirectSound3DBuffer_GetAllParameters(buf3d, &DS3DBuffer);
747 IDirectSound3DBuffer_Release(buf3d);
749 if(SUCCEEDED(subhr))
750 subhr = IDirectSoundBuffer_QueryInterface(*out, &IID_IDirectSound3DBuffer, (void**)&buf3d);
751 if(SUCCEEDED(subhr))
753 subhr = IDirectSound3DBuffer_SetAllParameters(buf3d, &DS3DBuffer, DS3D_IMMEDIATE);
754 IDirectSound3DBuffer_Release(buf3d);
758 if(FAILED(hr))
760 if(*out)
761 IDirectSoundBuffer_Release(*out);
762 *out = NULL;
765 return hr;
768 static HRESULT WINAPI DS8_SetCooperativeLevel(IDirectSound8 *iface, HWND hwnd, DWORD level)
770 DS8Impl *This = impl_from_IDirectSound8(iface);
771 HRESULT hr = S_OK;
773 TRACE("(%p)->(%p, %lu)\n", iface, hwnd, level);
775 if(!This->share)
777 WARN("Device not initialized\n");
778 return DSERR_UNINITIALIZED;
781 if(level > DSSCL_WRITEPRIMARY || level < DSSCL_NORMAL)
783 WARN("Invalid coop level: %lu\n", level);
784 return DSERR_INVALIDPARAM;
787 EnterCriticalSection(This->primary.crst);
788 if(level == DSSCL_WRITEPRIMARY && (This->prio_level != DSSCL_WRITEPRIMARY))
790 struct DSBufferGroup *bufgroup = This->primary.BufferGroups;
791 DWORD i, state;
793 if(This->primary.write_emu)
795 ERR("Why was there a write_emu?\n");
796 /* Delete it */
797 IDirectSoundBuffer8_Release(This->primary.write_emu);
798 This->primary.write_emu = NULL;
801 for(i = 0;i < This->primary.NumBufferGroups;++i)
803 DWORD64 usemask = ~bufgroup[i].FreeBuffers;
804 while(usemask)
806 int idx = CTZ64(usemask);
807 DS8Buffer *buf = bufgroup[i].Buffers + idx;
808 usemask &= ~(U64(1) << idx);
810 if(FAILED(IDirectSoundBuffer_GetStatus(&buf->IDirectSoundBuffer8_iface, &state)) ||
811 (state&DSBSTATUS_PLAYING))
813 WARN("DSSCL_WRITEPRIMARY set with playing buffers!\n");
814 hr = DSERR_INVALIDCALL;
815 goto out;
817 /* Mark buffer as lost */
818 buf->bufferlost = 1;
822 if(This->primary.flags)
824 /* Primary has open references.. create write_emu */
825 DSBUFFERDESC desc;
826 DS8Buffer *emu;
828 memset(&desc, 0, sizeof(desc));
829 desc.dwSize = sizeof(desc);
830 desc.dwFlags = DSBCAPS_LOCHARDWARE | (This->primary.flags&DSBCAPS_CTRLPAN);
831 desc.dwBufferBytes = This->primary.buf_size;
832 desc.lpwfxFormat = &This->primary.format.Format;
834 hr = DS8Buffer_Create(&emu, &This->primary, NULL, TRUE);
835 if(SUCCEEDED(hr))
837 This->primary.write_emu = &emu->IDirectSoundBuffer8_iface;
838 hr = IDirectSoundBuffer8_Initialize(This->primary.write_emu, &This->IDirectSound_iface, &desc);
839 if(FAILED(hr))
841 IDirectSoundBuffer8_Release(This->primary.write_emu);
842 This->primary.write_emu = NULL;
847 else if(This->prio_level == DSSCL_WRITEPRIMARY && level != DSSCL_WRITEPRIMARY)
849 /* Delete it */
850 TRACE("Nuking write_emu\n");
851 if(This->primary.write_emu)
852 IDirectSoundBuffer8_Release(This->primary.write_emu);
853 This->primary.write_emu = NULL;
855 if(SUCCEEDED(hr))
856 This->prio_level = level;
857 out:
858 LeaveCriticalSection(This->primary.crst);
860 return hr;
863 static HRESULT WINAPI DS8_Compact(IDirectSound8 *iface)
865 DS8Impl *This = impl_from_IDirectSound8(iface);
866 HRESULT hr = S_OK;
868 TRACE("(%p)->()\n", iface);
870 if(!This->share)
872 WARN("Device not initialized\n");
873 return DSERR_UNINITIALIZED;
876 EnterCriticalSection(&This->share->crst);
877 if(This->prio_level < DSSCL_PRIORITY)
879 WARN("Coop level not high enough (%lu)\n", This->prio_level);
880 hr = DSERR_PRIOLEVELNEEDED;
882 LeaveCriticalSection(&This->share->crst);
884 return hr;
887 static HRESULT WINAPI DS8_GetSpeakerConfig(IDirectSound8 *iface, DWORD *config)
889 DS8Impl *This = impl_from_IDirectSound8(iface);
890 HRESULT hr = S_OK;
892 TRACE("(%p)->(%p)\n", iface, config);
894 if(!config)
895 return DSERR_INVALIDPARAM;
896 *config = 0;
898 if(!This->share)
900 WARN("Device not initialized\n");
901 return DSERR_UNINITIALIZED;
904 EnterCriticalSection(&This->share->crst);
905 *config = This->speaker_config;
906 LeaveCriticalSection(&This->share->crst);
908 return hr;
911 static HRESULT WINAPI DS8_SetSpeakerConfig(IDirectSound8 *iface, DWORD config)
913 DS8Impl *This = impl_from_IDirectSound8(iface);
914 DWORD geo, speaker;
915 HKEY key;
916 HRESULT hr;
918 TRACE("(%p)->(0x%08lx)\n", iface, config);
920 if(!This->share)
922 WARN("Device not initialized\n");
923 return DSERR_UNINITIALIZED;
926 geo = DSSPEAKER_GEOMETRY(config);
927 speaker = DSSPEAKER_CONFIG(config);
929 if(geo && (geo < DSSPEAKER_GEOMETRY_MIN || geo > DSSPEAKER_GEOMETRY_MAX))
931 WARN("Invalid speaker angle %lu\n", geo);
932 return DSERR_INVALIDPARAM;
934 if(speaker < DSSPEAKER_HEADPHONE || speaker > DSSPEAKER_7POINT1)
936 WARN("Invalid speaker config %lu\n", speaker);
937 return DSERR_INVALIDPARAM;
940 EnterCriticalSection(&This->share->crst);
942 hr = DSERR_GENERIC;
943 if(!RegCreateKeyExW(HKEY_LOCAL_MACHINE, speakerconfigkey, 0, NULL, 0, KEY_WRITE, NULL, &key, NULL))
945 RegSetValueExW(key, speakerconfig, 0, REG_DWORD, (const BYTE*)&config, sizeof(DWORD));
946 This->speaker_config = config;
947 RegCloseKey(key);
948 hr = S_OK;
951 LeaveCriticalSection(&This->share->crst);
952 return hr;
955 static HRESULT WINAPI DS8_Initialize(IDirectSound8 *iface, const GUID *devguid)
957 DS8Impl *This = impl_from_IDirectSound8(iface);
958 HRESULT hr;
959 GUID guid;
960 UINT n;
962 TRACE("(%p)->(%s)\n", iface, debugstr_guid(devguid));
964 if(!openal_loaded)
965 return DSERR_NODRIVER;
967 if(This->share)
969 WARN("Device already initialized\n");
970 return DSERR_ALREADYINITIALIZED;
973 if(!devguid || IsEqualGUID(devguid, &GUID_NULL))
974 devguid = &DSDEVID_DefaultPlayback;
975 else if(IsEqualGUID(devguid, &DSDEVID_DefaultCapture) ||
976 IsEqualGUID(devguid, &DSDEVID_DefaultVoiceCapture))
977 return DSERR_NODRIVER;
979 hr = GetDeviceID(devguid, &guid);
980 if(FAILED(hr)) return hr;
982 EnterCriticalSection(&openal_crst);
984 for(n = 0;n < sharelistsize;n++)
986 if(IsEqualGUID(&sharelist[n]->guid, &guid))
988 TRACE("Matched already open device %p\n", sharelist[n]->device);
990 This->share = sharelist[n];
991 DSShare_AddRef(This->share);
992 break;
996 if(!This->share)
997 hr = DSShare_Create(&guid, &This->share);
998 if(SUCCEEDED(hr))
1000 This->device = This->share->device;
1001 hr = DS8Primary_PreInit(&This->primary, This);
1004 if(SUCCEEDED(hr))
1006 DeviceShare *share = This->share;
1007 DS8Primary **prims;
1009 EnterCriticalSection(&share->crst);
1011 prims = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
1012 (share->nprimaries+1) * sizeof(*prims));
1013 if(!prims)
1014 hr = DSERR_OUTOFMEMORY;
1015 else
1017 ALsizei i;
1018 for(i = 0;i < share->nprimaries;++i)
1019 prims[i] = share->primaries[i];
1020 prims[i] = &This->primary;
1022 HeapFree(GetProcessHeap(), 0, share->primaries);
1023 share->primaries = prims;
1024 share->nprimaries += 1;
1027 LeaveCriticalSection(&share->crst);
1030 if(FAILED(hr))
1032 if(This->share)
1033 DSShare_Release(This->share);
1034 This->share = NULL;
1037 LeaveCriticalSection(&openal_crst);
1038 return hr;
1041 /* I, Maarten Lankhorst, hereby declare this driver certified
1042 * What this means.. ? An extra bit set
1044 static HRESULT WINAPI DS8_VerifyCertification(IDirectSound8 *iface, DWORD *certified)
1046 DS8Impl *This = impl_from_IDirectSound8(iface);
1048 TRACE("(%p)->(%p)\n", iface, certified);
1050 if(!certified)
1051 return DSERR_INVALIDPARAM;
1052 *certified = 0;
1054 if(!This->share)
1056 WARN("Device not initialized\n");
1057 return DSERR_UNINITIALIZED;
1060 *certified = DS_CERTIFIED;
1062 return DS_OK;
1065 static const IDirectSound8Vtbl DS8_Vtbl = {
1066 DS8_QueryInterface,
1067 DS8_AddRef,
1068 DS8_Release,
1069 DS8_CreateSoundBuffer,
1070 DS8_GetCaps,
1071 DS8_DuplicateSoundBuffer,
1072 DS8_SetCooperativeLevel,
1073 DS8_Compact,
1074 DS8_GetSpeakerConfig,
1075 DS8_SetSpeakerConfig,
1076 DS8_Initialize,
1077 DS8_VerifyCertification
1081 static HRESULT WINAPI DS_QueryInterface(IDirectSound *iface, REFIID riid, LPVOID *ppv)
1083 DS8Impl *This = impl_from_IDirectSound(iface);
1084 return DS8_QueryInterface(&This->IDirectSound8_iface, riid, ppv);
1087 static ULONG WINAPI DS_AddRef(IDirectSound *iface)
1089 DS8Impl *This = impl_from_IDirectSound(iface);
1090 return DS8_AddRef(&This->IDirectSound8_iface);
1093 static ULONG WINAPI DS_Release(IDirectSound *iface)
1095 DS8Impl *This = impl_from_IDirectSound(iface);
1096 return DS8_Release(&This->IDirectSound8_iface);
1099 static HRESULT WINAPI DS_CreateSoundBuffer(IDirectSound *iface, LPCDSBUFFERDESC desc, LPLPDIRECTSOUNDBUFFER buf, IUnknown *pUnkOuter)
1101 DS8Impl *This = impl_from_IDirectSound(iface);
1102 return DS8_CreateSoundBuffer(&This->IDirectSound8_iface, desc, buf, pUnkOuter);
1105 static HRESULT WINAPI DS_GetCaps(IDirectSound *iface, LPDSCAPS caps)
1107 DS8Impl *This = impl_from_IDirectSound(iface);
1108 return DS8_GetCaps(&This->IDirectSound8_iface, caps);
1110 static HRESULT WINAPI DS_DuplicateSoundBuffer(IDirectSound *iface, IDirectSoundBuffer *in, IDirectSoundBuffer **out)
1112 DS8Impl *This = impl_from_IDirectSound(iface);
1113 return DS8_DuplicateSoundBuffer(&This->IDirectSound8_iface, in, out);
1116 static HRESULT WINAPI DS_SetCooperativeLevel(IDirectSound *iface, HWND hwnd, DWORD level)
1118 DS8Impl *This = impl_from_IDirectSound(iface);
1119 return DS8_SetCooperativeLevel(&This->IDirectSound8_iface, hwnd, level);
1122 static HRESULT WINAPI DS_Compact(IDirectSound *iface)
1124 DS8Impl *This = impl_from_IDirectSound(iface);
1125 return DS8_Compact(&This->IDirectSound8_iface);
1128 static HRESULT WINAPI DS_GetSpeakerConfig(IDirectSound *iface, DWORD *config)
1130 DS8Impl *This = impl_from_IDirectSound(iface);
1131 return DS8_GetSpeakerConfig(&This->IDirectSound8_iface, config);
1134 static HRESULT WINAPI DS_SetSpeakerConfig(IDirectSound *iface, DWORD config)
1136 DS8Impl *This = impl_from_IDirectSound(iface);
1137 return DS8_SetSpeakerConfig(&This->IDirectSound8_iface, config);
1140 static HRESULT WINAPI DS_Initialize(IDirectSound *iface, const GUID *devguid)
1142 DS8Impl *This = impl_from_IDirectSound(iface);
1143 return DS8_Initialize(&This->IDirectSound8_iface, devguid);
1146 static const IDirectSoundVtbl DS_Vtbl = {
1147 DS_QueryInterface,
1148 DS_AddRef,
1149 DS_Release,
1150 DS_CreateSoundBuffer,
1151 DS_GetCaps,
1152 DS_DuplicateSoundBuffer,
1153 DS_SetCooperativeLevel,
1154 DS_Compact,
1155 DS_GetSpeakerConfig,
1156 DS_SetSpeakerConfig,
1157 DS_Initialize