dsound: Nuke old implementation
[wine/multimedia.git] / dlls / dsound / dsound.c
blob95787985b0b6696392d0daaa1acc04fa7a95d8df
1 /* DirectSound
3 * Copyright 1998 Marcus Meissner
4 * Copyright 1998 Rob Riggs
5 * Copyright 2000-2002 TransGaming Technologies, Inc.
6 * Copyright 2004 Robert Reif
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 #include "config.h"
25 #include <stdarg.h>
26 #include <stdio.h>
28 #define COBJMACROS
29 #define NONAMELESSSTRUCT
30 #define NONAMELESSUNION
31 #include "windef.h"
32 #include "winbase.h"
33 #include "winreg.h"
34 #include "winuser.h"
35 #include "winternl.h"
36 #include "mmddk.h"
37 #include "wingdi.h"
38 #include "mmreg.h"
39 #include "ks.h"
40 #include "ksmedia.h"
41 #include "wine/debug.h"
42 #include "dsound.h"
43 #include "dsound_private.h"
45 WINE_DEFAULT_DEBUG_CHANNEL(dsound);
47 #ifdef HAVE_OPENAL
49 static DS8Impl **devicelist;
50 static UINT devicelistsize;
52 static const IDirectSound8Vtbl DS8_Vtbl;
54 static inline DS8Impl *impl_from_IDirectSound8(IDirectSound8 *iface)
56 return CONTAINING_RECORD(iface, DS8Impl, IDirectSound8_iface);
59 HRESULT DSOUND_Create(REFIID riid, IDirectSound **ds)
61 HRESULT hr;
63 if(IsEqualIID(riid, &IID_IDirectSound8))
64 return E_NOINTERFACE;
65 hr = DSOUND_Create8(riid, (IDirectSound8**)ds);
66 if(hr == S_OK)
67 impl_from_IDirectSound8((IDirectSound8*)*ds)->is_8 = FALSE;
68 return hr;
71 static void DS8Impl_Destroy(DS8Impl *This);
73 static const WCHAR speakerconfigkey[] = {
74 'S','Y','S','T','E','M','\\',
75 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
76 'C','o','n','t','r','o','l','\\',
77 'M','e','d','i','a','R','e','s','o','u','r','c','e','s','\\',
78 'D','i','r','e','c','t','S','o','u','n','d','\\',
79 'S','p','e','a','k','e','r',' ','C','o','n','f','i','g','u','r','a','t','i','o','n',0
82 static const WCHAR speakerconfig[] = {
83 'S','p','e','a','k','e','r',' ','C','o','n','f','i','g','u','r','a','t','i','o','n',0
86 HRESULT DSOUND_Create8(REFIID riid, IDirectSound8 **ds)
88 DS8Impl *This;
89 HRESULT hr;
91 *ds = NULL;
92 This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*This));
93 if(!This)
94 return E_OUTOFMEMORY;
95 This->is_8 = TRUE;
96 This->IDirectSound8_iface.lpVtbl = &DS8_Vtbl;
97 This->speaker_config = DSSPEAKER_COMBINED(DSSPEAKER_5POINT1, DSSPEAKER_GEOMETRY_WIDE);
98 RegGetValueW(HKEY_LOCAL_MACHINE, speakerconfigkey, speakerconfig, RRF_RT_REG_DWORD, NULL, &This->speaker_config, NULL);
100 hr = IUnknown_QueryInterface(&This->IDirectSound8_iface, riid, (void**)ds);
101 if(FAILED(hr))
102 DS8Impl_Destroy(This);
103 else
105 void *temp;
107 EnterCriticalSection(&openal_crst);
108 if(devicelist)
109 temp = HeapReAlloc(GetProcessHeap(), 0, devicelist, sizeof(*devicelist)*(devicelistsize+1));
110 else
111 temp = HeapAlloc(GetProcessHeap(), 0, sizeof(*devicelist)*(devicelistsize+1));
112 if(temp)
114 devicelist = temp;
115 devicelist[devicelistsize++] = This;
117 LeaveCriticalSection(&openal_crst);
119 return hr;
122 static void DS8Impl_Destroy(DS8Impl *This)
124 UINT i;
126 EnterCriticalSection(&openal_crst);
127 for(i = 0;i < devicelistsize;i++)
129 if(devicelist[i] == This)
131 devicelist[i] = devicelist[--devicelistsize];
132 break;
136 if(This->deviceref && InterlockedDecrement(This->deviceref) == 0)
138 if(This->device)
139 palcCloseDevice(This->device);
141 HeapFree(GetProcessHeap(), 0, This->deviceref);
143 else if(This->deviceref && This->primary->parent == This)
145 /* If the primary is referencing this as its parent, update it to
146 * reference another handle for the device */
147 for(i = 0;i < devicelistsize;i++)
149 if(devicelist[i]->primary == This->primary)
151 This->primary->parent = devicelist[i];
152 break;
156 LeaveCriticalSection(&openal_crst);
158 HeapFree(GetProcessHeap(), 0, This);
161 static HRESULT WINAPI DS8_QueryInterface(IDirectSound8 *iface, REFIID riid, LPVOID *ppv)
163 DS8Impl *This = impl_from_IDirectSound8(iface);
165 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
167 *ppv = NULL;
168 if(IsEqualIID(riid, &IID_IUnknown) ||
169 IsEqualIID(riid, &IID_IDirectSound))
170 *ppv = &This->IDirectSound8_iface;
171 else if((IsEqualIID(riid, &IID_IDirectSound8)))
173 if(This->is_8)
174 *ppv = &This->IDirectSound8_iface;
176 else
177 FIXME("Unhandled GUID: %s\n", debugstr_guid(riid));
179 if(*ppv)
181 IUnknown_AddRef((IUnknown*)*ppv);
182 return S_OK;
185 return E_NOINTERFACE;
188 static ULONG WINAPI DS8_AddRef(IDirectSound8 *iface)
190 DS8Impl *This = impl_from_IDirectSound8(iface);
191 LONG ref;
193 ref = InterlockedIncrement(&This->ref);
194 TRACE("Reference count incremented to %d\n", ref);
196 return ref;
199 static ULONG WINAPI DS8_Release(IDirectSound8 *iface)
201 DS8Impl *This = impl_from_IDirectSound8(iface);
202 LONG ref;
204 ref = InterlockedDecrement(&This->ref);
205 TRACE("Reference count decremented to %d\n", ref);
206 if(!ref)
207 DS8Impl_Destroy(This);
209 return ref;
212 static HRESULT WINAPI DS8_CreateSoundBuffer(IDirectSound8 *iface, LPCDSBUFFERDESC desc, LPLPDIRECTSOUNDBUFFER buf, IUnknown *pUnkOuter)
214 DS8Impl *This = impl_from_IDirectSound8(iface);
215 HRESULT hr;
217 TRACE("(%p)->(%p, %p, %p)\n", iface, desc, buf, pUnkOuter);
219 if(!buf)
221 WARN("buf is null\n");
222 return DSERR_INVALIDPARAM;
224 *buf = NULL;
226 if(pUnkOuter)
228 WARN("Aggregation isn't supported\n");
229 return DSERR_NOAGGREGATION;
231 if(!desc || desc->dwSize < sizeof(DSBUFFERDESC1))
233 WARN("Invalid buffer %p/%u\n", desc, desc?desc->dwSize:0);
234 return DSERR_INVALIDPARAM;
236 if(desc->dwSize >= sizeof(DSBUFFERDESC))
238 if(!(desc->dwFlags&DSBCAPS_CTRL3D))
240 if(!IsEqualGUID(&desc->guid3DAlgorithm, &GUID_NULL))
242 WARN("Invalid 3D algorithm GUID specified for non-3D buffer: %s\n", debugstr_guid(&desc->guid3DAlgorithm));
243 return DSERR_INVALIDPARAM;
246 else
247 TRACE("Requested 3D algorithm GUID: %s\n", debugstr_guid(&desc->guid3DAlgorithm));
250 /* OpenAL doesn't support playing with 3d and panning at same time.. */
251 if((desc->dwFlags&(DSBCAPS_CTRL3D|DSBCAPS_CTRLPAN)) == (DSBCAPS_CTRL3D|DSBCAPS_CTRLPAN))
253 if(!This->is_8)
254 ERR("Cannot create buffers with 3D and panning control\n");
255 else
256 WARN("Cannot create buffers with 3D and panning control\n");
257 return DSERR_INVALIDPARAM;
260 EnterCriticalSection(&openal_crst);
261 if(!This->device)
262 hr = DSERR_UNINITIALIZED;
263 LeaveCriticalSection(&openal_crst);
265 TRACE("%08x\n", hr);
266 return hr;
269 static HRESULT WINAPI DS8_GetCaps(IDirectSound8 *iface, LPDSCAPS caps)
271 DS8Impl *This = impl_from_IDirectSound8(iface);
272 HRESULT hr = S_OK;
273 TRACE("\n");
275 EnterCriticalSection(&openal_crst);
276 if(!This->device)
277 hr = DSERR_UNINITIALIZED;
278 else if(!caps || caps->dwSize < sizeof(*caps))
279 hr = DSERR_INVALIDPARAM;
280 else
282 LONG count = This->primary->max_sources;
284 setALContext(This->primary->ctx);
285 caps->dwFlags = DSCAPS_CONTINUOUSRATE |
286 DSCAPS_PRIMARY16BIT | DSCAPS_PRIMARYSTEREO |
287 DSCAPS_PRIMARY8BIT | DSCAPS_PRIMARYMONO |
288 DSCAPS_SECONDARY16BIT | DSCAPS_SECONDARY8BIT |
289 DSCAPS_SECONDARYMONO | DSCAPS_SECONDARYSTEREO;
290 caps->dwPrimaryBuffers = 1;
291 caps->dwMinSecondarySampleRate = DSBFREQUENCY_MIN;
292 caps->dwMaxSecondarySampleRate = DSBFREQUENCY_MAX;
293 caps->dwMaxHwMixingAllBuffers =
294 caps->dwMaxHwMixingStaticBuffers =
295 caps->dwMaxHwMixingStreamingBuffers =
296 caps->dwMaxHw3DAllBuffers =
297 caps->dwMaxHw3DStaticBuffers =
298 caps->dwMaxHw3DStreamingBuffers = count;
299 count -= This->primary->nbuffers;
300 if(count < 0)
302 ERR("How did the count drop below 0?\n");
303 count = 0;
305 caps->dwFreeHwMixingAllBuffers =
306 caps->dwFreeHwMixingStaticBuffers =
307 caps->dwFreeHwMixingStreamingBuffers =
308 caps->dwFreeHw3DAllBuffers =
309 caps->dwFreeHw3DStaticBuffers =
310 caps->dwFreeHw3DStreamingBuffers = count;
311 if(palIsExtensionPresent("EAX-RAM"))
313 caps->dwTotalHwMemBytes = palGetInteger(palGetEnumValue("AL_EAX_RAM_SIZE"));
314 caps->dwFreeHwMemBytes = palGetInteger(palGetEnumValue("AL_EAX_RAM_FREE"));
316 else
318 caps->dwTotalHwMemBytes =
319 caps->dwFreeHwMemBytes = 64 * 1024 * 1024;
321 caps->dwMaxContigFreeHwMemBytes = caps->dwFreeHwMemBytes;
322 caps->dwUnlockTransferRateHwBuffers = 4096;
323 caps->dwPlayCpuOverheadSwBuffers = 0;
324 popALContext();
326 LeaveCriticalSection(&openal_crst);
328 return hr;
330 static HRESULT WINAPI DS8_DuplicateSoundBuffer(IDirectSound8 *iface, IDirectSoundBuffer *in, IDirectSoundBuffer **out)
332 DS8Impl *This = impl_from_IDirectSound8(iface);
333 HRESULT hr = S_OK;
335 TRACE("(%p)->(%p, %p)\n", iface, in, out);
337 EnterCriticalSection(&openal_crst);
338 if(!This->device)
339 hr = DSERR_UNINITIALIZED;
340 else if(!in || !out)
341 hr = DSERR_INVALIDPARAM;
342 LeaveCriticalSection(&openal_crst);
343 return hr;
346 static HRESULT WINAPI DS8_SetCooperativeLevel(IDirectSound8 *iface, HWND hwnd, DWORD level)
348 DS8Impl *This = impl_from_IDirectSound8(iface);
349 HRESULT hr = S_OK;
351 TRACE("(%p)->(%p, %u)\n", iface, hwnd, level);
353 EnterCriticalSection(&openal_crst);
354 if(!This->device)
355 hr = DSERR_UNINITIALIZED;
356 else if(level > DSSCL_WRITEPRIMARY || level < DSSCL_NORMAL)
357 hr = E_INVALIDARG;
358 if(SUCCEEDED(hr))
359 This->prio_level = level;
360 LeaveCriticalSection(&openal_crst);
362 return hr;
365 static HRESULT WINAPI DS8_Compact(IDirectSound8 *iface)
367 DS8Impl *This = impl_from_IDirectSound8(iface);
368 HRESULT hr = S_OK;
370 TRACE("(%p)->()\n", iface);
372 EnterCriticalSection(&openal_crst);
373 if(!This->device)
374 hr = DSERR_UNINITIALIZED;
375 else if(This->prio_level < DSSCL_PRIORITY)
376 hr = DSERR_PRIOLEVELNEEDED;
377 LeaveCriticalSection(&openal_crst);
379 return hr;
382 static HRESULT WINAPI DS8_GetSpeakerConfig(IDirectSound8 *iface, DWORD *config)
384 DS8Impl *This = impl_from_IDirectSound8(iface);
385 HRESULT hr = S_OK;
387 TRACE("(%p)->(%p)\n", iface, config);
389 if(!config)
390 return DSERR_INVALIDPARAM;
392 EnterCriticalSection(&openal_crst);
393 if(!This->device)
394 hr = DSERR_UNINITIALIZED;
395 else
396 *config = This->speaker_config;
397 LeaveCriticalSection(&openal_crst);
399 return hr;
402 static HRESULT WINAPI DS8_SetSpeakerConfig(IDirectSound8 *iface, DWORD config)
404 DS8Impl *This = impl_from_IDirectSound8(iface);
405 HRESULT hr;
407 TRACE("(%p)->(0x%08x)\n", iface, config);
409 EnterCriticalSection(&openal_crst);
410 if(!This->device)
411 hr = DSERR_UNINITIALIZED;
412 else
414 HKEY key;
415 DWORD geo, speaker;
417 geo = DSSPEAKER_GEOMETRY(config);
418 speaker = DSSPEAKER_CONFIG(config);
420 hr = DSERR_INVALIDPARAM;
421 if(geo && (geo < DSSPEAKER_GEOMETRY_MIN || geo > DSSPEAKER_GEOMETRY_MAX))
423 WARN("Invalid speaker angle %u\n", geo);
424 goto out;
426 if(speaker < DSSPEAKER_HEADPHONE || speaker > DSSPEAKER_7POINT1)
428 WARN("Invalid speaker config %u\n", speaker);
429 goto out;
432 hr = DSERR_GENERIC;
433 if(!RegCreateKeyExW(HKEY_LOCAL_MACHINE, speakerconfigkey, 0, NULL, 0, KEY_WRITE, NULL, &key, NULL))
435 RegSetValueExW(key, speakerconfig, 0, REG_DWORD, (const BYTE*)&config, sizeof(DWORD));
436 This->speaker_config = config;
437 RegCloseKey(key);
438 hr = S_OK;
441 out:
442 LeaveCriticalSection(&openal_crst);
444 return hr;
447 static HRESULT WINAPI DS8_Initialize(IDirectSound8 *iface, const GUID *devguid)
449 DS8Impl *This = impl_from_IDirectSound8(iface);
450 const ALCchar *drv_name = "PulseAudio default";
451 HRESULT hr;
452 UINT n;
454 TRACE("(%p)->(%s)\n", iface, debugstr_guid(devguid));
456 if(!openal_loaded || 1)
457 return DSERR_NODRIVER;
459 if(!devguid)
460 devguid = &DSDEVID_DefaultPlayback;
462 EnterCriticalSection(&openal_crst);
464 hr = DSERR_ALREADYINITIALIZED;
465 if(This->device)
466 goto out;
468 for(n = 0;n < devicelistsize;n++)
470 if(devicelist[n]->device && devicelist[n]->is_8 == This->is_8 &&
471 IsEqualGUID(&devicelist[n]->guid, &This->guid))
473 TRACE("Matched already open device %p\n", devicelist[n]);
475 This->device = devicelist[n]->device;
476 This->primary = devicelist[n]->primary;
477 This->deviceref = devicelist[n]->deviceref;
478 InterlockedIncrement(This->deviceref);
480 hr = DS_OK;
481 goto out;
485 if(!This->deviceref)
487 hr = DSERR_OUTOFMEMORY;
488 if(!(This->deviceref=HeapAlloc(GetProcessHeap(), 0, sizeof(LONG))))
489 goto out;
490 This->deviceref[0] = 1;
493 This->device = palcOpenDevice(drv_name);
494 if(!This->device)
496 palcGetError(NULL);
497 WARN("Couldn't open device %s\n", drv_name);
498 goto out;
500 TRACE("Opened device: %s\n", palcGetString(This->device, ALC_DEVICE_SPECIFIER));
502 hr = E_FAIL;
503 if(FAILED(hr))
505 palcCloseDevice(This->device);
506 This->device = NULL;
509 out:
510 LeaveCriticalSection(&openal_crst);
512 return hr;
515 /* I, Maarten Lankhorst, hereby declare this driver certified
516 * What this means.. ? An extra bit set
518 static HRESULT WINAPI DS8_VerifyCertification(IDirectSound8 *iface, DWORD *certified)
520 DS8Impl *This = impl_from_IDirectSound8(iface);
521 HRESULT hr;
523 TRACE("(%p)->(%p)\n", iface, certified);
525 if(!certified)
526 return DSERR_INVALIDPARAM;
528 EnterCriticalSection(&openal_crst);
529 hr = S_OK;
530 if(!This->device)
531 hr = DSERR_UNINITIALIZED;
532 else
533 *certified = DS_CERTIFIED;
534 LeaveCriticalSection(&openal_crst);
536 return hr;
539 static const IDirectSound8Vtbl DS8_Vtbl =
541 DS8_QueryInterface,
542 DS8_AddRef,
543 DS8_Release,
544 DS8_CreateSoundBuffer,
545 DS8_GetCaps,
546 DS8_DuplicateSoundBuffer,
547 DS8_SetCooperativeLevel,
548 DS8_Compact,
549 DS8_GetSpeakerConfig,
550 DS8_SetSpeakerConfig,
551 DS8_Initialize,
552 DS8_VerifyCertification
555 #else
557 HRESULT DSOUND_Create(REFIID riid, IDirectSound **ds)
559 return DSOUND_Create8(riid, (IDirectSound8**)ds);
562 HRESULT DSOUND_Create8(REFIID riid, IDirectSound8 **ds)
564 *ds = NULL;
566 ERR("Attempting to create a sound device without OpenAL support\n");
567 ERR("Please recompile Wine with OpenAL\n");
569 return DSERR_NODRIVER;
572 #endif
575 /*******************************************************************************
576 * DirectSoundCreate (DSOUND.1)
578 * Creates and initializes a DirectSound interface.
580 * PARAMS
581 * lpcGUID [I] Address of the GUID that identifies the sound device.
582 * ppDS [O] Address of a variable to receive the interface pointer.
583 * pUnkOuter [I] Must be NULL.
585 * RETURNS
586 * Success: DS_OK
587 * Failure: DSERR_ALLOCATED, DSERR_INVALIDPARAM, DSERR_NOAGGREGATION,
588 * DSERR_NODRIVER, DSERR_OUTOFMEMORY
590 HRESULT WINAPI DirectSoundCreate(LPCGUID lpcGUID, LPDIRECTSOUND *ppDS, IUnknown *pUnkOuter)
592 HRESULT hr;
593 LPDIRECTSOUND pDS;
595 TRACE("(%s,%p,%p)\n",debugstr_guid(lpcGUID),ppDS,pUnkOuter);
597 if (ppDS == NULL) {
598 WARN("invalid parameter: ppDS == NULL\n");
599 return DSERR_INVALIDPARAM;
602 if (pUnkOuter != NULL) {
603 WARN("invalid parameter: pUnkOuter != NULL\n");
604 *ppDS = 0;
605 return DSERR_INVALIDPARAM;
608 hr = DSOUND_Create(&IID_IDirectSound, &pDS);
609 if (hr == DS_OK) {
610 hr = IDirectSound_Initialize(pDS, lpcGUID);
611 if (hr != DS_OK) {
612 if (hr != DSERR_ALREADYINITIALIZED) {
613 IDirectSound_Release(pDS);
614 pDS = 0;
615 } else
616 hr = DS_OK;
620 *ppDS = pDS;
622 return hr;
625 HRESULT WINAPI DirectSoundCreate8(LPCGUID lpcGUID, LPDIRECTSOUND8 *ppDS, IUnknown *pUnkOuter)
627 HRESULT hr;
628 LPDIRECTSOUND8 pDS;
630 TRACE("(%s,%p,%p)\n",debugstr_guid(lpcGUID),ppDS,pUnkOuter);
632 if (ppDS == NULL) {
633 WARN("invalid parameter: ppDS == NULL\n");
634 return DSERR_INVALIDPARAM;
637 if (pUnkOuter != NULL) {
638 WARN("invalid parameter: pUnkOuter != NULL\n");
639 *ppDS = 0;
640 return DSERR_INVALIDPARAM;
643 hr = DSOUND_Create8(&IID_IDirectSound8, &pDS);
644 if (hr == DS_OK) {
645 hr = IDirectSound8_Initialize(pDS, lpcGUID);
646 if (hr != DS_OK) {
647 if (hr != DSERR_ALREADYINITIALIZED) {
648 IDirectSound8_Release(pDS);
649 pDS = 0;
650 } else
651 hr = DS_OK;
655 *ppDS = pDS;
657 return hr;