Partially handle EAX buffer properties
[dsound-openal.git] / dsound8.c
blobb0a34504f0020b87a91d7dc79fa1470539ac3b33
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(!share->SupportedExt[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->nsources)
150 alDeleteSources(share->nsources, share->sources);
151 share->nsources = 0;
153 if(share->auxslot)
154 share->ExtAL.DeleteAuxiliaryEffectSlots(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 OLECHAR *guid_str = NULL;
177 ALchar drv_name[64];
178 DeviceShare *share;
179 void *temp;
180 HRESULT hr;
182 share = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*share));
183 if(!share) return DSERR_OUTOFMEMORY;
184 share->ref = 1;
185 share->refresh = FAKE_REFRESH_COUNT;
187 InitializeCriticalSection(&share->crst);
189 hr = StringFromCLSID(guid, &guid_str);
190 if(FAILED(hr))
192 ERR("Failed to convert GUID to string\n");
193 goto fail;
195 WideCharToMultiByte(CP_UTF8, 0, guid_str, -1, drv_name, sizeof(drv_name), NULL, NULL);
196 drv_name[sizeof(drv_name)-1] = 0;
197 CoTaskMemFree(guid_str);
198 guid_str = NULL;
200 hr = DSERR_NODRIVER;
201 share->device = alcOpenDevice(drv_name);
202 if(!share->device)
204 alcGetError(NULL);
205 WARN("Couldn't open device \"%s\"\n", drv_name);
206 goto fail;
208 TRACE("Opened device: %s\n",
209 alcIsExtensionPresent(share->device, "ALC_ENUMERATE_ALL_EXT") ?
210 alcGetString(share->device, ALC_ALL_DEVICES_SPECIFIER) :
211 alcGetString(share->device, ALC_DEVICE_SPECIFIER));
213 share->ctx = alcCreateContext(share->device, NULL);
214 if(!share->ctx)
216 ALCenum err = alcGetError(share->device);
217 ERR("Could not create context (0x%x)!\n", err);
218 goto fail;
221 memcpy(&share->guid, guid, sizeof(GUID));
223 setALContext(share->ctx);
224 alcGetIntegerv(share->device, ALC_REFRESH, 1, &share->refresh);
225 checkALCError(share->device);
227 if(alIsExtensionPresent("AL_EXT_FLOAT32"))
229 TRACE("Found AL_EXT_FLOAT32\n");
230 share->SupportedExt[EXT_FLOAT32] = AL_TRUE;
232 if(alIsExtensionPresent("AL_EXT_MCFORMATS"))
234 TRACE("Found AL_EXT_MCFORMATS\n");
235 share->SupportedExt[EXT_MCFORMATS] = AL_TRUE;
237 if(alIsExtensionPresent("AL_SOFT_deferred_updates"))
239 TRACE("Found AL_SOFT_deferred_updates\n");
240 share->ExtAL.DeferUpdatesSOFT = alGetProcAddress("alDeferUpdatesSOFT");
241 share->ExtAL.ProcessUpdatesSOFT = alGetProcAddress("alProcessUpdatesSOFT");
242 share->SupportedExt[SOFT_DEFERRED_UPDATES] = AL_TRUE;
244 if(alIsExtensionPresent("AL_SOFTX_map_buffer"))
246 TRACE("Found AL_SOFTX_map_buffer\n");
247 share->ExtAL.BufferStorageSOFT = alGetProcAddress("alBufferStorageSOFT");
248 share->ExtAL.MapBufferSOFT = alGetProcAddress("alMapBufferSOFT");
249 share->ExtAL.UnmapBufferSOFT = alGetProcAddress("alUnmapBufferSOFT");
250 share->ExtAL.FlushMappedBufferSOFT = alGetProcAddress("alFlushMappedBufferSOFT");
251 share->SupportedExt[SOFTX_MAP_BUFFER] = AL_TRUE;
254 if(alcIsExtensionPresent(share->device, "ALC_EXT_EFX"))
256 #define LOAD_FUNC(x) (share->ExtAL.x = alGetProcAddress("al"#x))
257 LOAD_FUNC(GenFilters);
258 LOAD_FUNC(DeleteFilters);
259 LOAD_FUNC(Filteri);
260 LOAD_FUNC(Filterf);
262 LOAD_FUNC(GenEffects);
263 LOAD_FUNC(DeleteEffects);
264 LOAD_FUNC(Effecti);
265 LOAD_FUNC(Effectf);
267 LOAD_FUNC(GenAuxiliaryEffectSlots);
268 LOAD_FUNC(DeleteAuxiliaryEffectSlots);
269 LOAD_FUNC(AuxiliaryEffectSloti);
270 #undef LOAD_FUNC
271 share->SupportedExt[EXT_EFX] = AL_TRUE;
273 share->ExtAL.GenAuxiliaryEffectSlots(1, &share->auxslot);
276 share->max_sources = 0;
277 while(share->max_sources < MAX_SOURCES)
279 alGenSources(1, &share->sources[share->max_sources]);
280 if(alGetError() != AL_NO_ERROR)
281 break;
282 share->max_sources++;
284 popALContext();
285 /* As long as we have at least 64 sources, keep it a multiple of 64. */
286 if(share->max_sources > 64)
287 share->max_sources &= ~63u;
288 share->nsources = share->max_sources;
290 if(sharelist)
291 temp = HeapReAlloc(GetProcessHeap(), 0, sharelist, sizeof(*sharelist)*(sharelistsize+1));
292 else
293 temp = HeapAlloc(GetProcessHeap(), 0, sizeof(*sharelist)*(sharelistsize+1));
294 if(temp)
296 sharelist = temp;
297 sharelist[sharelistsize++] = share;
300 hr = E_FAIL;
302 share->quit_now = FALSE;
303 share->timer_evt = CreateEventA(NULL, FALSE, FALSE, NULL);
304 if(!share->timer_evt) goto fail;
306 share->queue_timer = NULL;
308 share->thread_hdl = CreateThread(NULL, 0, DSShare_thread, share, 0, &share->thread_id);
309 if(!share->thread_hdl) goto fail;
311 DSShare_starttimer(share);
313 *out = share;
314 return DS_OK;
316 fail:
317 DSShare_Destroy(share);
318 return hr;
321 static ULONG DSShare_AddRef(DeviceShare *share)
323 ULONG ref = InterlockedIncrement(&share->ref);
324 return ref;
327 static ULONG DSShare_Release(DeviceShare *share)
329 ULONG ref = InterlockedDecrement(&share->ref);
330 if(ref == 0) DSShare_Destroy(share);
331 return ref;
335 static const IDirectSound8Vtbl DS8_Vtbl;
336 static const IDirectSoundVtbl DS_Vtbl;
337 static const IUnknownVtbl DS8_Unknown_Vtbl;
339 static void DS8Impl_Destroy(DS8Impl *This);
340 static HRESULT WINAPI DS8_QueryInterface(IDirectSound8 *iface, REFIID riid, LPVOID *ppv);
342 /*******************************************************************************
343 * IUnknown
345 static inline DS8Impl *impl_from_IUnknown(IUnknown *iface)
347 return CONTAINING_RECORD(iface, DS8Impl, IUnknown_iface);
350 static HRESULT WINAPI DS8Impl_IUnknown_QueryInterface(IUnknown *iface, REFIID riid, void **ppobj)
352 DS8Impl *This = impl_from_IUnknown(iface);
353 return DS8_QueryInterface(&This->IDirectSound8_iface, riid, ppobj);
356 static ULONG WINAPI DS8Impl_IUnknown_AddRef(IUnknown *iface)
358 DS8Impl *This = impl_from_IUnknown(iface);
359 ULONG ref;
361 InterlockedIncrement(&(This->ref));
362 ref = InterlockedIncrement(&(This->unkref));
363 TRACE("(%p) ref was %lu\n", This, ref - 1);
365 return ref;
368 static ULONG WINAPI DS8Impl_IUnknown_Release(IUnknown *iface)
370 DS8Impl *This = impl_from_IUnknown(iface);
371 ULONG ref = InterlockedDecrement(&(This->unkref));
372 TRACE("(%p) ref was %lu\n", This, ref + 1);
373 if(InterlockedDecrement(&(This->ref)) == 0)
374 DS8Impl_Destroy(This);
375 return ref;
378 static const IUnknownVtbl DS8_Unknown_Vtbl = {
379 DS8Impl_IUnknown_QueryInterface,
380 DS8Impl_IUnknown_AddRef,
381 DS8Impl_IUnknown_Release
385 static inline DS8Impl *impl_from_IDirectSound8(IDirectSound8 *iface)
387 return CONTAINING_RECORD(iface, DS8Impl, IDirectSound8_iface);
390 static inline DS8Impl *impl_from_IDirectSound(IDirectSound *iface)
392 return CONTAINING_RECORD(iface, DS8Impl, IDirectSound_iface);
395 HRESULT DSOUND_Create(REFIID riid, void **ds)
397 HRESULT hr;
399 hr = DSOUND_Create8(&IID_IDirectSound, ds);
400 if(SUCCEEDED(hr))
402 DS8Impl *impl = impl_from_IDirectSound(*ds);
403 impl->is_8 = FALSE;
405 if(!IsEqualIID(riid, &IID_IDirectSound))
407 hr = IDirectSound_QueryInterface(&impl->IDirectSound_iface, riid, ds);
408 IDirectSound_Release(&impl->IDirectSound_iface);
411 return hr;
415 static const WCHAR speakerconfigkey[] =
416 L"SYSTEM\\CurrentControlSet\\Control\\MediaResources\\DirectSound\\Speaker Configuration";
417 static const WCHAR speakerconfig[] = L"Speaker Configuration";
419 HRESULT DSOUND_Create8(REFIID riid, LPVOID *ds)
421 DS8Impl *This;
422 HKEY regkey;
423 HRESULT hr;
425 *ds = NULL;
426 This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*This));
427 if(!This) return DSERR_OUTOFMEMORY;
429 This->IDirectSound8_iface.lpVtbl = &DS8_Vtbl;
430 This->IDirectSound_iface.lpVtbl = &DS_Vtbl;
431 This->IUnknown_iface.lpVtbl = &DS8_Unknown_Vtbl;
433 This->is_8 = TRUE;
434 This->speaker_config = DSSPEAKER_COMBINED(DSSPEAKER_5POINT1, DSSPEAKER_GEOMETRY_WIDE);
436 if(RegOpenKeyExW(HKEY_LOCAL_MACHINE, speakerconfigkey, 0, KEY_READ, &regkey) == ERROR_SUCCESS)
438 DWORD type, conf, confsize = sizeof(DWORD);
440 if(RegQueryValueExW(regkey, speakerconfig, NULL, &type, (BYTE*)&conf, &confsize) == ERROR_SUCCESS)
442 if(type == REG_DWORD)
443 This->speaker_config = conf;
446 RegCloseKey(regkey);
448 /*RegGetValueW(HKEY_LOCAL_MACHINE, speakerconfigkey, speakerconfig, RRF_RT_REG_DWORD, NULL, &This->speaker_config, NULL);*/
450 hr = IDirectSound8_QueryInterface(&This->IDirectSound8_iface, riid, ds);
451 if(FAILED(hr))
452 DS8Impl_Destroy(This);
453 return hr;
456 static void DS8Impl_Destroy(DS8Impl *This)
458 DeviceShare *share = This->share;
460 if(share)
462 ALsizei i;
464 EnterCriticalSection(&share->crst);
466 for(i = 0;i < share->nprimaries;++i)
468 if(share->primaries[i] == &This->primary)
470 share->nprimaries -= 1;
471 share->primaries[i] = share->primaries[share->nprimaries];
472 break;
476 LeaveCriticalSection(&share->crst);
479 DS8Primary_Clear(&This->primary);
480 if(This->share)
481 DSShare_Release(This->share);
482 This->share = NULL;
484 HeapFree(GetProcessHeap(), 0, This);
488 static HRESULT WINAPI DS8_QueryInterface(IDirectSound8 *iface, REFIID riid, LPVOID *ppv)
490 DS8Impl *This = impl_from_IDirectSound8(iface);
492 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
494 *ppv = NULL;
495 if(IsEqualIID(riid, &IID_IUnknown))
496 *ppv = &This->IUnknown_iface;
497 else if(IsEqualIID(riid, &IID_IDirectSound8))
499 if(This->is_8)
500 *ppv = &This->IDirectSound8_iface;
502 else if(IsEqualIID(riid, &IID_IDirectSound))
503 *ppv = &This->IDirectSound_iface;
504 else
505 FIXME("Unhandled GUID: %s\n", debugstr_guid(riid));
507 if(*ppv)
509 IUnknown_AddRef((IUnknown*)*ppv);
510 return S_OK;
513 return E_NOINTERFACE;
516 static ULONG WINAPI DS8_AddRef(IDirectSound8 *iface)
518 DS8Impl *This = impl_from_IDirectSound8(iface);
519 LONG ref;
521 InterlockedIncrement(&This->ref);
522 ref = InterlockedIncrement(&This->dsref);
523 TRACE("Reference count incremented to %ld\n", ref);
525 return ref;
528 static ULONG WINAPI DS8_Release(IDirectSound8 *iface)
530 DS8Impl *This = impl_from_IDirectSound8(iface);
531 LONG ref;
533 ref = InterlockedDecrement(&This->dsref);
534 TRACE("Reference count decremented to %ld\n", ref);
535 if(InterlockedDecrement(&This->ref) == 0)
536 DS8Impl_Destroy(This);
538 return ref;
541 static HRESULT WINAPI DS8_CreateSoundBuffer(IDirectSound8 *iface, LPCDSBUFFERDESC desc, LPLPDIRECTSOUNDBUFFER buf, IUnknown *pUnkOuter)
543 DS8Impl *This = impl_from_IDirectSound8(iface);
544 HRESULT hr;
546 TRACE("(%p)->(%p, %p, %p)\n", iface, desc, buf, pUnkOuter);
548 if(!buf)
550 WARN("buf is null\n");
551 return DSERR_INVALIDPARAM;
553 *buf = NULL;
555 if(pUnkOuter)
557 WARN("Aggregation isn't supported\n");
558 return DSERR_NOAGGREGATION;
560 if(!desc || desc->dwSize < sizeof(DSBUFFERDESC1))
562 WARN("Invalid buffer %p/%lu\n", desc, desc?desc->dwSize:0);
563 return DSERR_INVALIDPARAM;
566 if(!This->share)
568 WARN("Device not initialized\n");
569 return DSERR_UNINITIALIZED;
572 TRACE("Requested buffer:\n"
573 " Size = %lu\n"
574 " Flags = 0x%08lx\n"
575 " BufferBytes = %lu\n",
576 desc->dwSize, desc->dwFlags, desc->dwBufferBytes);
578 if(desc->dwSize >= sizeof(DSBUFFERDESC))
580 if(!(desc->dwFlags&DSBCAPS_CTRL3D))
582 if(!IsEqualGUID(&desc->guid3DAlgorithm, &GUID_NULL))
584 WARN("Invalid 3D algorithm GUID specified for non-3D buffer: %s\n", debugstr_guid(&desc->guid3DAlgorithm));
585 return DSERR_INVALIDPARAM;
588 else
589 TRACE("Requested 3D algorithm GUID: %s\n", debugstr_guid(&desc->guid3DAlgorithm));
592 /* OpenAL doesn't support playing with 3d and panning at same time.. */
593 if((desc->dwFlags&(DSBCAPS_CTRL3D|DSBCAPS_CTRLPAN)) == (DSBCAPS_CTRL3D|DSBCAPS_CTRLPAN))
595 if(!This->is_8)
597 static int once = 0;
598 if(!once++)
599 FIXME("Buffers with 3D and panning control ignore panning\n");
601 else
603 WARN("Cannot create buffers with 3D and panning control\n");
604 return DSERR_INVALIDPARAM;
608 EnterCriticalSection(This->primary.crst);
609 if((desc->dwFlags&DSBCAPS_PRIMARYBUFFER))
611 IDirectSoundBuffer *prim = &This->primary.IDirectSoundBuffer_iface;
613 hr = S_OK;
614 if(IDirectSoundBuffer_AddRef(prim) == 1)
616 hr = DS8Primary_Initialize(prim, &This->IDirectSound_iface, desc);
617 if(FAILED(hr))
619 IDirectSoundBuffer_Release(prim);
620 prim = NULL;
623 *buf = prim;
625 else
627 DS8Buffer *dsb;
629 hr = DS8Buffer_Create(&dsb, &This->primary, NULL, FALSE);
630 if(SUCCEEDED(hr))
632 hr = DS8Buffer_Initialize(&dsb->IDirectSoundBuffer8_iface, &This->IDirectSound_iface, desc);
633 if(FAILED(hr))
634 IDirectSoundBuffer8_Release(&dsb->IDirectSoundBuffer8_iface);
635 else
637 dsb->bufferlost = (This->prio_level == DSSCL_WRITEPRIMARY);
638 *buf = (IDirectSoundBuffer*)&dsb->IDirectSoundBuffer8_iface;
642 LeaveCriticalSection(This->primary.crst);
644 TRACE("%08lx\n", hr);
645 return hr;
648 static HRESULT WINAPI DS8_GetCaps(IDirectSound8 *iface, LPDSCAPS caps)
650 DS8Impl *This = impl_from_IDirectSound8(iface);
651 DWORD free_bufs, i;
653 TRACE("(%p)->(%p)\n", iface, caps);
655 if(!This->share)
657 WARN("Device not initialized\n");
658 return DSERR_UNINITIALIZED;
661 if(!caps || caps->dwSize < sizeof(*caps))
663 WARN("Invalid DSCAPS (%p, %lu)\n", caps, (caps?caps->dwSize:0));
664 return DSERR_INVALIDPARAM;
667 EnterCriticalSection(&This->share->crst);
669 free_bufs = 0;
670 for(i = 0;i < This->primary.NumBufferGroups;i++)
671 free_bufs += POPCNT64(This->primary.BufferGroups[i].FreeBuffers);
673 caps->dwFlags = DSCAPS_CONTINUOUSRATE | DSCAPS_CERTIFIED |
674 DSCAPS_PRIMARY16BIT | DSCAPS_PRIMARYSTEREO |
675 DSCAPS_PRIMARY8BIT | DSCAPS_PRIMARYMONO |
676 DSCAPS_SECONDARY16BIT | DSCAPS_SECONDARY8BIT |
677 DSCAPS_SECONDARYMONO | DSCAPS_SECONDARYSTEREO;
678 caps->dwPrimaryBuffers = 1;
679 caps->dwMinSecondarySampleRate = DSBFREQUENCY_MIN;
680 caps->dwMaxSecondarySampleRate = DSBFREQUENCY_MAX;
681 caps->dwMaxHwMixingAllBuffers =
682 caps->dwMaxHwMixingStaticBuffers =
683 caps->dwMaxHwMixingStreamingBuffers =
684 caps->dwMaxHw3DAllBuffers =
685 caps->dwMaxHw3DStaticBuffers =
686 caps->dwMaxHw3DStreamingBuffers = This->share->max_sources;
687 caps->dwFreeHwMixingAllBuffers =
688 caps->dwFreeHwMixingStaticBuffers =
689 caps->dwFreeHwMixingStreamingBuffers =
690 caps->dwFreeHw3DAllBuffers =
691 caps->dwFreeHw3DStaticBuffers =
692 caps->dwFreeHw3DStreamingBuffers = free_bufs;
693 caps->dwTotalHwMemBytes =
694 caps->dwFreeHwMemBytes = 64 * 1024 * 1024;
695 caps->dwMaxContigFreeHwMemBytes = caps->dwFreeHwMemBytes;
696 caps->dwUnlockTransferRateHwBuffers = 4096;
697 caps->dwPlayCpuOverheadSwBuffers = 0;
699 LeaveCriticalSection(&This->share->crst);
701 return DS_OK;
703 static HRESULT WINAPI DS8_DuplicateSoundBuffer(IDirectSound8 *iface, IDirectSoundBuffer *in, IDirectSoundBuffer **out)
705 DS8Impl *This = impl_from_IDirectSound8(iface);
706 DS8Buffer *buf;
707 DSBCAPS caps;
708 HRESULT hr;
710 TRACE("(%p)->(%p, %p)\n", iface, in, out);
712 if(!This->share)
714 WARN("Device not initialized\n");
715 return DSERR_UNINITIALIZED;
718 if(!in || !out)
720 WARN("Invalid pointer: in = %p, out = %p\n", in, out);
721 return DSERR_INVALIDPARAM;
723 *out = NULL;
725 caps.dwSize = sizeof(caps);
726 hr = IDirectSoundBuffer_GetCaps(in, &caps);
727 if(SUCCEEDED(hr) && (caps.dwFlags&DSBCAPS_PRIMARYBUFFER))
729 WARN("Cannot duplicate buffer %p, which has DSBCAPS_PRIMARYBUFFER\n", in);
730 hr = DSERR_INVALIDPARAM;
732 if(SUCCEEDED(hr) && (caps.dwFlags&DSBCAPS_CTRLFX))
734 WARN("Cannot duplicate buffer %p, which has DSBCAPS_CTRLFX\n", in);
735 hr = DSERR_INVALIDPARAM;
737 if(SUCCEEDED(hr))
738 hr = DS8Buffer_Create(&buf, &This->primary, in, FALSE);
739 if(SUCCEEDED(hr))
741 *out = (IDirectSoundBuffer*)&buf->IDirectSoundBuffer8_iface;
742 hr = DS8Buffer_Initialize(&buf->IDirectSoundBuffer8_iface, NULL, NULL);
744 if(SUCCEEDED(hr))
746 /* According to MSDN volume isn't copied */
747 if((caps.dwFlags&DSBCAPS_CTRLPAN))
749 LONG pan;
750 if(SUCCEEDED(IDirectSoundBuffer_GetPan(in, &pan)))
751 IDirectSoundBuffer_SetPan(*out, pan);
753 if((caps.dwFlags&DSBCAPS_CTRLFREQUENCY))
755 DWORD freq;
756 if(SUCCEEDED(IDirectSoundBuffer_GetFrequency(in, &freq)))
757 IDirectSoundBuffer_SetFrequency(*out, freq);
759 if((caps.dwFlags&DSBCAPS_CTRL3D))
761 IDirectSound3DBuffer *buf3d;
762 DS3DBUFFER DS3DBuffer;
763 HRESULT subhr;
765 subhr = IDirectSound_QueryInterface(in, &IID_IDirectSound3DBuffer, (void**)&buf3d);
766 if(SUCCEEDED(subhr))
768 DS3DBuffer.dwSize = sizeof(DS3DBuffer);
769 subhr = IDirectSound3DBuffer_GetAllParameters(buf3d, &DS3DBuffer);
770 IDirectSound3DBuffer_Release(buf3d);
772 if(SUCCEEDED(subhr))
773 subhr = IDirectSoundBuffer_QueryInterface(*out, &IID_IDirectSound3DBuffer, (void**)&buf3d);
774 if(SUCCEEDED(subhr))
776 subhr = IDirectSound3DBuffer_SetAllParameters(buf3d, &DS3DBuffer, DS3D_IMMEDIATE);
777 IDirectSound3DBuffer_Release(buf3d);
781 if(FAILED(hr))
783 if(*out)
784 IDirectSoundBuffer_Release(*out);
785 *out = NULL;
788 return hr;
791 static HRESULT WINAPI DS8_SetCooperativeLevel(IDirectSound8 *iface, HWND hwnd, DWORD level)
793 DS8Impl *This = impl_from_IDirectSound8(iface);
794 HRESULT hr = S_OK;
796 TRACE("(%p)->(%p, %lu)\n", iface, hwnd, level);
798 if(!This->share)
800 WARN("Device not initialized\n");
801 return DSERR_UNINITIALIZED;
804 if(level > DSSCL_WRITEPRIMARY || level < DSSCL_NORMAL)
806 WARN("Invalid coop level: %lu\n", level);
807 return DSERR_INVALIDPARAM;
810 EnterCriticalSection(This->primary.crst);
811 if(level == DSSCL_WRITEPRIMARY && (This->prio_level != DSSCL_WRITEPRIMARY))
813 struct DSBufferGroup *bufgroup = This->primary.BufferGroups;
814 DWORD i, state;
816 if(This->primary.write_emu)
818 ERR("Why was there a write_emu?\n");
819 /* Delete it */
820 IDirectSoundBuffer8_Release(This->primary.write_emu);
821 This->primary.write_emu = NULL;
824 for(i = 0;i < This->primary.NumBufferGroups;++i)
826 DWORD64 usemask = ~bufgroup[i].FreeBuffers;
827 while(usemask)
829 int idx = CTZ64(usemask);
830 DS8Buffer *buf = bufgroup[i].Buffers + idx;
831 usemask &= ~(U64(1) << idx);
833 if(FAILED(IDirectSoundBuffer_GetStatus(&buf->IDirectSoundBuffer8_iface, &state)) ||
834 (state&DSBSTATUS_PLAYING))
836 WARN("DSSCL_WRITEPRIMARY set with playing buffers!\n");
837 hr = DSERR_INVALIDCALL;
838 goto out;
840 /* Mark buffer as lost */
841 buf->bufferlost = 1;
845 if(This->primary.flags)
847 /* Primary has open references.. create write_emu */
848 DSBUFFERDESC desc;
849 DS8Buffer *emu;
851 memset(&desc, 0, sizeof(desc));
852 desc.dwSize = sizeof(desc);
853 desc.dwFlags = DSBCAPS_LOCHARDWARE | (This->primary.flags&DSBCAPS_CTRLPAN);
854 desc.dwBufferBytes = This->primary.buf_size;
855 desc.lpwfxFormat = &This->primary.format.Format;
857 hr = DS8Buffer_Create(&emu, &This->primary, NULL, TRUE);
858 if(SUCCEEDED(hr))
860 This->primary.write_emu = &emu->IDirectSoundBuffer8_iface;
861 hr = IDirectSoundBuffer8_Initialize(This->primary.write_emu, &This->IDirectSound_iface, &desc);
862 if(FAILED(hr))
864 IDirectSoundBuffer8_Release(This->primary.write_emu);
865 This->primary.write_emu = NULL;
870 else if(This->prio_level == DSSCL_WRITEPRIMARY && level != DSSCL_WRITEPRIMARY)
872 /* Delete it */
873 TRACE("Nuking write_emu\n");
874 if(This->primary.write_emu)
875 IDirectSoundBuffer8_Release(This->primary.write_emu);
876 This->primary.write_emu = NULL;
878 if(SUCCEEDED(hr))
879 This->prio_level = level;
880 out:
881 LeaveCriticalSection(This->primary.crst);
883 return hr;
886 static HRESULT WINAPI DS8_Compact(IDirectSound8 *iface)
888 DS8Impl *This = impl_from_IDirectSound8(iface);
889 HRESULT hr = S_OK;
891 TRACE("(%p)->()\n", iface);
893 if(!This->share)
895 WARN("Device not initialized\n");
896 return DSERR_UNINITIALIZED;
899 EnterCriticalSection(&This->share->crst);
900 if(This->prio_level < DSSCL_PRIORITY)
902 WARN("Coop level not high enough (%lu)\n", This->prio_level);
903 hr = DSERR_PRIOLEVELNEEDED;
905 LeaveCriticalSection(&This->share->crst);
907 return hr;
910 static HRESULT WINAPI DS8_GetSpeakerConfig(IDirectSound8 *iface, DWORD *config)
912 DS8Impl *This = impl_from_IDirectSound8(iface);
913 HRESULT hr = S_OK;
915 TRACE("(%p)->(%p)\n", iface, config);
917 if(!config)
918 return DSERR_INVALIDPARAM;
919 *config = 0;
921 if(!This->share)
923 WARN("Device not initialized\n");
924 return DSERR_UNINITIALIZED;
927 EnterCriticalSection(&This->share->crst);
928 *config = This->speaker_config;
929 LeaveCriticalSection(&This->share->crst);
931 return hr;
934 static HRESULT WINAPI DS8_SetSpeakerConfig(IDirectSound8 *iface, DWORD config)
936 DS8Impl *This = impl_from_IDirectSound8(iface);
937 DWORD geo, speaker;
938 HKEY key;
939 HRESULT hr;
941 TRACE("(%p)->(0x%08lx)\n", iface, config);
943 if(!This->share)
945 WARN("Device not initialized\n");
946 return DSERR_UNINITIALIZED;
949 geo = DSSPEAKER_GEOMETRY(config);
950 speaker = DSSPEAKER_CONFIG(config);
952 if(geo && (geo < DSSPEAKER_GEOMETRY_MIN || geo > DSSPEAKER_GEOMETRY_MAX))
954 WARN("Invalid speaker angle %lu\n", geo);
955 return DSERR_INVALIDPARAM;
957 if(speaker < DSSPEAKER_HEADPHONE || speaker > DSSPEAKER_7POINT1)
959 WARN("Invalid speaker config %lu\n", speaker);
960 return DSERR_INVALIDPARAM;
963 EnterCriticalSection(&This->share->crst);
965 hr = DSERR_GENERIC;
966 if(!RegCreateKeyExW(HKEY_LOCAL_MACHINE, speakerconfigkey, 0, NULL, 0, KEY_WRITE, NULL, &key, NULL))
968 RegSetValueExW(key, speakerconfig, 0, REG_DWORD, (const BYTE*)&config, sizeof(DWORD));
969 This->speaker_config = config;
970 RegCloseKey(key);
971 hr = S_OK;
974 LeaveCriticalSection(&This->share->crst);
975 return hr;
978 static HRESULT WINAPI DS8_Initialize(IDirectSound8 *iface, const GUID *devguid)
980 DS8Impl *This = impl_from_IDirectSound8(iface);
981 HRESULT hr;
982 GUID guid;
983 UINT n;
985 TRACE("(%p)->(%s)\n", iface, debugstr_guid(devguid));
987 if(!openal_loaded)
988 return DSERR_NODRIVER;
990 if(This->share)
992 WARN("Device already initialized\n");
993 return DSERR_ALREADYINITIALIZED;
996 if(!devguid || IsEqualGUID(devguid, &GUID_NULL))
997 devguid = &DSDEVID_DefaultPlayback;
998 else if(IsEqualGUID(devguid, &DSDEVID_DefaultCapture) ||
999 IsEqualGUID(devguid, &DSDEVID_DefaultVoiceCapture))
1000 return DSERR_NODRIVER;
1002 hr = GetDeviceID(devguid, &guid);
1003 if(FAILED(hr)) return hr;
1005 EnterCriticalSection(&openal_crst);
1007 for(n = 0;n < sharelistsize;n++)
1009 if(IsEqualGUID(&sharelist[n]->guid, &guid))
1011 TRACE("Matched already open device %p\n", sharelist[n]->device);
1013 This->share = sharelist[n];
1014 DSShare_AddRef(This->share);
1015 break;
1019 if(!This->share)
1020 hr = DSShare_Create(&guid, &This->share);
1021 if(SUCCEEDED(hr))
1023 This->device = This->share->device;
1024 hr = DS8Primary_PreInit(&This->primary, This);
1027 if(SUCCEEDED(hr))
1029 DeviceShare *share = This->share;
1030 DS8Primary **prims;
1032 EnterCriticalSection(&share->crst);
1034 prims = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
1035 (share->nprimaries+1) * sizeof(*prims));
1036 if(!prims)
1037 hr = DSERR_OUTOFMEMORY;
1038 else
1040 ALsizei i;
1041 for(i = 0;i < share->nprimaries;++i)
1042 prims[i] = share->primaries[i];
1043 prims[i] = &This->primary;
1045 HeapFree(GetProcessHeap(), 0, share->primaries);
1046 share->primaries = prims;
1047 share->nprimaries += 1;
1050 LeaveCriticalSection(&share->crst);
1053 if(FAILED(hr))
1055 if(This->share)
1056 DSShare_Release(This->share);
1057 This->share = NULL;
1060 LeaveCriticalSection(&openal_crst);
1061 return hr;
1064 /* I, Maarten Lankhorst, hereby declare this driver certified
1065 * What this means.. ? An extra bit set
1067 static HRESULT WINAPI DS8_VerifyCertification(IDirectSound8 *iface, DWORD *certified)
1069 DS8Impl *This = impl_from_IDirectSound8(iface);
1071 TRACE("(%p)->(%p)\n", iface, certified);
1073 if(!certified)
1074 return DSERR_INVALIDPARAM;
1075 *certified = 0;
1077 if(!This->share)
1079 WARN("Device not initialized\n");
1080 return DSERR_UNINITIALIZED;
1083 *certified = DS_CERTIFIED;
1085 return DS_OK;
1088 static const IDirectSound8Vtbl DS8_Vtbl = {
1089 DS8_QueryInterface,
1090 DS8_AddRef,
1091 DS8_Release,
1092 DS8_CreateSoundBuffer,
1093 DS8_GetCaps,
1094 DS8_DuplicateSoundBuffer,
1095 DS8_SetCooperativeLevel,
1096 DS8_Compact,
1097 DS8_GetSpeakerConfig,
1098 DS8_SetSpeakerConfig,
1099 DS8_Initialize,
1100 DS8_VerifyCertification
1104 static HRESULT WINAPI DS_QueryInterface(IDirectSound *iface, REFIID riid, LPVOID *ppv)
1106 DS8Impl *This = impl_from_IDirectSound(iface);
1107 return DS8_QueryInterface(&This->IDirectSound8_iface, riid, ppv);
1110 static ULONG WINAPI DS_AddRef(IDirectSound *iface)
1112 DS8Impl *This = impl_from_IDirectSound(iface);
1113 return DS8_AddRef(&This->IDirectSound8_iface);
1116 static ULONG WINAPI DS_Release(IDirectSound *iface)
1118 DS8Impl *This = impl_from_IDirectSound(iface);
1119 return DS8_Release(&This->IDirectSound8_iface);
1122 static HRESULT WINAPI DS_CreateSoundBuffer(IDirectSound *iface, LPCDSBUFFERDESC desc, LPLPDIRECTSOUNDBUFFER buf, IUnknown *pUnkOuter)
1124 DS8Impl *This = impl_from_IDirectSound(iface);
1125 return DS8_CreateSoundBuffer(&This->IDirectSound8_iface, desc, buf, pUnkOuter);
1128 static HRESULT WINAPI DS_GetCaps(IDirectSound *iface, LPDSCAPS caps)
1130 DS8Impl *This = impl_from_IDirectSound(iface);
1131 return DS8_GetCaps(&This->IDirectSound8_iface, caps);
1133 static HRESULT WINAPI DS_DuplicateSoundBuffer(IDirectSound *iface, IDirectSoundBuffer *in, IDirectSoundBuffer **out)
1135 DS8Impl *This = impl_from_IDirectSound(iface);
1136 return DS8_DuplicateSoundBuffer(&This->IDirectSound8_iface, in, out);
1139 static HRESULT WINAPI DS_SetCooperativeLevel(IDirectSound *iface, HWND hwnd, DWORD level)
1141 DS8Impl *This = impl_from_IDirectSound(iface);
1142 return DS8_SetCooperativeLevel(&This->IDirectSound8_iface, hwnd, level);
1145 static HRESULT WINAPI DS_Compact(IDirectSound *iface)
1147 DS8Impl *This = impl_from_IDirectSound(iface);
1148 return DS8_Compact(&This->IDirectSound8_iface);
1151 static HRESULT WINAPI DS_GetSpeakerConfig(IDirectSound *iface, DWORD *config)
1153 DS8Impl *This = impl_from_IDirectSound(iface);
1154 return DS8_GetSpeakerConfig(&This->IDirectSound8_iface, config);
1157 static HRESULT WINAPI DS_SetSpeakerConfig(IDirectSound *iface, DWORD config)
1159 DS8Impl *This = impl_from_IDirectSound(iface);
1160 return DS8_SetSpeakerConfig(&This->IDirectSound8_iface, config);
1163 static HRESULT WINAPI DS_Initialize(IDirectSound *iface, const GUID *devguid)
1165 DS8Impl *This = impl_from_IDirectSound(iface);
1166 return DS8_Initialize(&This->IDirectSound8_iface, devguid);
1169 static const IDirectSoundVtbl DS_Vtbl = {
1170 DS_QueryInterface,
1171 DS_AddRef,
1172 DS_Release,
1173 DS_CreateSoundBuffer,
1174 DS_GetCaps,
1175 DS_DuplicateSoundBuffer,
1176 DS_SetCooperativeLevel,
1177 DS_Compact,
1178 DS_GetSpeakerConfig,
1179 DS_SetSpeakerConfig,
1180 DS_Initialize