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
29 #define NONAMELESSSTRUCT
30 #define NONAMELESSUNION
41 #include "wine/debug.h"
43 #include "dsound_private.h"
45 WINE_DEFAULT_DEBUG_CHANNEL(dsound
);
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
)
63 if(IsEqualIID(riid
, &IID_IDirectSound8
))
65 hr
= DSOUND_Create8(riid
, (IDirectSound8
**)ds
);
67 impl_from_IDirectSound8((IDirectSound8
*)*ds
)->is_8
= FALSE
;
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
)
92 This
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(*This
));
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
);
102 DS8Impl_Destroy(This
);
107 EnterCriticalSection(&openal_crst
);
109 temp
= HeapReAlloc(GetProcessHeap(), 0, devicelist
, sizeof(*devicelist
)*(devicelistsize
+1));
111 temp
= HeapAlloc(GetProcessHeap(), 0, sizeof(*devicelist
)*(devicelistsize
+1));
115 devicelist
[devicelistsize
++] = This
;
117 LeaveCriticalSection(&openal_crst
);
122 static void DS8Impl_Destroy(DS8Impl
*This
)
126 EnterCriticalSection(&openal_crst
);
127 for(i
= 0;i
< devicelistsize
;i
++)
129 if(devicelist
[i
] == This
)
131 devicelist
[i
] = devicelist
[--devicelistsize
];
136 if(This
->deviceref
&& InterlockedDecrement(This
->deviceref
) == 0)
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
];
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
);
168 if(IsEqualIID(riid
, &IID_IUnknown
) ||
169 IsEqualIID(riid
, &IID_IDirectSound
))
170 *ppv
= &This
->IDirectSound8_iface
;
171 else if((IsEqualIID(riid
, &IID_IDirectSound8
)))
174 *ppv
= &This
->IDirectSound8_iface
;
177 FIXME("Unhandled GUID: %s\n", debugstr_guid(riid
));
181 IUnknown_AddRef((IUnknown
*)*ppv
);
185 return E_NOINTERFACE
;
188 static ULONG WINAPI
DS8_AddRef(IDirectSound8
*iface
)
190 DS8Impl
*This
= impl_from_IDirectSound8(iface
);
193 ref
= InterlockedIncrement(&This
->ref
);
194 TRACE("Reference count incremented to %d\n", ref
);
199 static ULONG WINAPI
DS8_Release(IDirectSound8
*iface
)
201 DS8Impl
*This
= impl_from_IDirectSound8(iface
);
204 ref
= InterlockedDecrement(&This
->ref
);
205 TRACE("Reference count decremented to %d\n", ref
);
207 DS8Impl_Destroy(This
);
212 static HRESULT WINAPI
DS8_CreateSoundBuffer(IDirectSound8
*iface
, LPCDSBUFFERDESC desc
, LPLPDIRECTSOUNDBUFFER buf
, IUnknown
*pUnkOuter
)
214 DS8Impl
*This
= impl_from_IDirectSound8(iface
);
217 TRACE("(%p)->(%p, %p, %p)\n", iface
, desc
, buf
, pUnkOuter
);
221 WARN("buf is null\n");
222 return DSERR_INVALIDPARAM
;
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
;
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
))
254 ERR("Cannot create buffers with 3D and panning control\n");
256 WARN("Cannot create buffers with 3D and panning control\n");
257 return DSERR_INVALIDPARAM
;
260 EnterCriticalSection(&openal_crst
);
262 hr
= DSERR_UNINITIALIZED
;
263 LeaveCriticalSection(&openal_crst
);
269 static HRESULT WINAPI
DS8_GetCaps(IDirectSound8
*iface
, LPDSCAPS caps
)
271 DS8Impl
*This
= impl_from_IDirectSound8(iface
);
275 EnterCriticalSection(&openal_crst
);
277 hr
= DSERR_UNINITIALIZED
;
278 else if(!caps
|| caps
->dwSize
< sizeof(*caps
))
279 hr
= DSERR_INVALIDPARAM
;
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
;
302 ERR("How did the count drop below 0?\n");
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"));
318 caps
->dwTotalHwMemBytes
=
319 caps
->dwFreeHwMemBytes
= 64 * 1024 * 1024;
321 caps
->dwMaxContigFreeHwMemBytes
= caps
->dwFreeHwMemBytes
;
322 caps
->dwUnlockTransferRateHwBuffers
= 4096;
323 caps
->dwPlayCpuOverheadSwBuffers
= 0;
326 LeaveCriticalSection(&openal_crst
);
330 static HRESULT WINAPI
DS8_DuplicateSoundBuffer(IDirectSound8
*iface
, IDirectSoundBuffer
*in
, IDirectSoundBuffer
**out
)
332 DS8Impl
*This
= impl_from_IDirectSound8(iface
);
335 TRACE("(%p)->(%p, %p)\n", iface
, in
, out
);
337 EnterCriticalSection(&openal_crst
);
339 hr
= DSERR_UNINITIALIZED
;
341 hr
= DSERR_INVALIDPARAM
;
342 LeaveCriticalSection(&openal_crst
);
346 static HRESULT WINAPI
DS8_SetCooperativeLevel(IDirectSound8
*iface
, HWND hwnd
, DWORD level
)
348 DS8Impl
*This
= impl_from_IDirectSound8(iface
);
351 TRACE("(%p)->(%p, %u)\n", iface
, hwnd
, level
);
353 EnterCriticalSection(&openal_crst
);
355 hr
= DSERR_UNINITIALIZED
;
356 else if(level
> DSSCL_WRITEPRIMARY
|| level
< DSSCL_NORMAL
)
359 This
->prio_level
= level
;
360 LeaveCriticalSection(&openal_crst
);
365 static HRESULT WINAPI
DS8_Compact(IDirectSound8
*iface
)
367 DS8Impl
*This
= impl_from_IDirectSound8(iface
);
370 TRACE("(%p)->()\n", iface
);
372 EnterCriticalSection(&openal_crst
);
374 hr
= DSERR_UNINITIALIZED
;
375 else if(This
->prio_level
< DSSCL_PRIORITY
)
376 hr
= DSERR_PRIOLEVELNEEDED
;
377 LeaveCriticalSection(&openal_crst
);
382 static HRESULT WINAPI
DS8_GetSpeakerConfig(IDirectSound8
*iface
, DWORD
*config
)
384 DS8Impl
*This
= impl_from_IDirectSound8(iface
);
387 TRACE("(%p)->(%p)\n", iface
, config
);
390 return DSERR_INVALIDPARAM
;
392 EnterCriticalSection(&openal_crst
);
394 hr
= DSERR_UNINITIALIZED
;
396 *config
= This
->speaker_config
;
397 LeaveCriticalSection(&openal_crst
);
402 static HRESULT WINAPI
DS8_SetSpeakerConfig(IDirectSound8
*iface
, DWORD config
)
404 DS8Impl
*This
= impl_from_IDirectSound8(iface
);
407 TRACE("(%p)->(0x%08x)\n", iface
, config
);
409 EnterCriticalSection(&openal_crst
);
411 hr
= DSERR_UNINITIALIZED
;
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
);
426 if(speaker
< DSSPEAKER_HEADPHONE
|| speaker
> DSSPEAKER_7POINT1
)
428 WARN("Invalid speaker config %u\n", speaker
);
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
;
442 LeaveCriticalSection(&openal_crst
);
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";
454 TRACE("(%p)->(%s)\n", iface
, debugstr_guid(devguid
));
456 if(!openal_loaded
|| 1)
457 return DSERR_NODRIVER
;
460 devguid
= &DSDEVID_DefaultPlayback
;
462 EnterCriticalSection(&openal_crst
);
464 hr
= DSERR_ALREADYINITIALIZED
;
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
);
487 hr
= DSERR_OUTOFMEMORY
;
488 if(!(This
->deviceref
=HeapAlloc(GetProcessHeap(), 0, sizeof(LONG
))))
490 This
->deviceref
[0] = 1;
493 This
->device
= palcOpenDevice(drv_name
);
497 WARN("Couldn't open device %s\n", drv_name
);
500 TRACE("Opened device: %s\n", palcGetString(This
->device
, ALC_DEVICE_SPECIFIER
));
505 palcCloseDevice(This
->device
);
510 LeaveCriticalSection(&openal_crst
);
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
);
523 TRACE("(%p)->(%p)\n", iface
, certified
);
526 return DSERR_INVALIDPARAM
;
528 EnterCriticalSection(&openal_crst
);
531 hr
= DSERR_UNINITIALIZED
;
533 *certified
= DS_CERTIFIED
;
534 LeaveCriticalSection(&openal_crst
);
539 static const IDirectSound8Vtbl DS8_Vtbl
=
544 DS8_CreateSoundBuffer
,
546 DS8_DuplicateSoundBuffer
,
547 DS8_SetCooperativeLevel
,
549 DS8_GetSpeakerConfig
,
550 DS8_SetSpeakerConfig
,
552 DS8_VerifyCertification
557 HRESULT
DSOUND_Create(REFIID riid
, IDirectSound
**ds
)
559 return DSOUND_Create8(riid
, (IDirectSound8
**)ds
);
562 HRESULT
DSOUND_Create8(REFIID riid
, IDirectSound8
**ds
)
566 ERR("Attempting to create a sound device without OpenAL support\n");
567 ERR("Please recompile Wine with OpenAL\n");
569 return DSERR_NODRIVER
;
575 /*******************************************************************************
576 * DirectSoundCreate (DSOUND.1)
578 * Creates and initializes a DirectSound interface.
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.
587 * Failure: DSERR_ALLOCATED, DSERR_INVALIDPARAM, DSERR_NOAGGREGATION,
588 * DSERR_NODRIVER, DSERR_OUTOFMEMORY
590 HRESULT WINAPI
DirectSoundCreate(LPCGUID lpcGUID
, LPDIRECTSOUND
*ppDS
, IUnknown
*pUnkOuter
)
595 TRACE("(%s,%p,%p)\n",debugstr_guid(lpcGUID
),ppDS
,pUnkOuter
);
598 WARN("invalid parameter: ppDS == NULL\n");
599 return DSERR_INVALIDPARAM
;
602 if (pUnkOuter
!= NULL
) {
603 WARN("invalid parameter: pUnkOuter != NULL\n");
605 return DSERR_INVALIDPARAM
;
608 hr
= DSOUND_Create(&IID_IDirectSound
, &pDS
);
610 hr
= IDirectSound_Initialize(pDS
, lpcGUID
);
612 if (hr
!= DSERR_ALREADYINITIALIZED
) {
613 IDirectSound_Release(pDS
);
625 HRESULT WINAPI
DirectSoundCreate8(LPCGUID lpcGUID
, LPDIRECTSOUND8
*ppDS
, IUnknown
*pUnkOuter
)
630 TRACE("(%s,%p,%p)\n",debugstr_guid(lpcGUID
),ppDS
,pUnkOuter
);
633 WARN("invalid parameter: ppDS == NULL\n");
634 return DSERR_INVALIDPARAM
;
637 if (pUnkOuter
!= NULL
) {
638 WARN("invalid parameter: pUnkOuter != NULL\n");
640 return DSERR_INVALIDPARAM
;
643 hr
= DSOUND_Create8(&IID_IDirectSound8
, &pDS
);
645 hr
= IDirectSound8_Initialize(pDS
, lpcGUID
);
647 if (hr
!= DSERR_ALREADYINITIALIZED
) {
648 IDirectSound8_Release(pDS
);