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
27 #define WIN32_NO_STATUS
29 #define COM_NO_WINDOWS_H
31 #define NONAMELESSSTRUCT
32 #define NONAMELESSUNION
34 //#include "winbase.h"
35 //#include "winuser.h"
41 //#include "ksmedia.h"
42 #include <wine/debug.h>
44 #include "dsound_private.h"
46 WINE_DEFAULT_DEBUG_CHANNEL(dsound
);
48 typedef struct IDirectSoundImpl
{
49 IUnknown IUnknown_inner
;
50 IDirectSound8 IDirectSound8_iface
;
51 IUnknown
*outer_unk
; /* internal */
52 LONG ref
, refds
, numIfaces
;
53 DirectSoundDevice
*device
;
57 static const char * dumpCooperativeLevel(DWORD level
)
59 #define LE(x) case x: return #x
64 LE(DSSCL_WRITEPRIMARY
);
67 return wine_dbg_sprintf("Unknown(%08x)", level
);
70 static void _dump_DSCAPS(DWORD xmask
) {
75 #define FE(x) { x, #x },
76 FE(DSCAPS_PRIMARYMONO
)
77 FE(DSCAPS_PRIMARYSTEREO
)
78 FE(DSCAPS_PRIMARY8BIT
)
79 FE(DSCAPS_PRIMARY16BIT
)
80 FE(DSCAPS_CONTINUOUSRATE
)
83 FE(DSCAPS_SECONDARYMONO
)
84 FE(DSCAPS_SECONDARYSTEREO
)
85 FE(DSCAPS_SECONDARY8BIT
)
86 FE(DSCAPS_SECONDARY16BIT
)
91 for (i
=0;i
<sizeof(flags
)/sizeof(flags
[0]);i
++)
92 if ((flags
[i
].mask
& xmask
) == flags
[i
].mask
)
93 TRACE("%s ",flags
[i
].name
);
96 static void _dump_DSBCAPS(DWORD xmask
) {
101 #define FE(x) { x, #x },
102 FE(DSBCAPS_PRIMARYBUFFER
)
104 FE(DSBCAPS_LOCHARDWARE
)
105 FE(DSBCAPS_LOCSOFTWARE
)
107 FE(DSBCAPS_CTRLFREQUENCY
)
109 FE(DSBCAPS_CTRLVOLUME
)
110 FE(DSBCAPS_CTRLPOSITIONNOTIFY
)
111 FE(DSBCAPS_STICKYFOCUS
)
112 FE(DSBCAPS_GLOBALFOCUS
)
113 FE(DSBCAPS_GETCURRENTPOSITION2
)
114 FE(DSBCAPS_MUTE3DATMAXDISTANCE
)
119 for (i
=0;i
<sizeof(flags
)/sizeof(flags
[0]);i
++)
120 if ((flags
[i
].mask
& xmask
) == flags
[i
].mask
)
121 TRACE("%s ",flags
[i
].name
);
124 static void directsound_destroy(IDirectSoundImpl
*This
)
127 DirectSoundDevice_Release(This
->device
);
128 HeapFree(GetProcessHeap(),0,This
);
129 TRACE("(%p) released\n", This
);
132 /*******************************************************************************
133 * IUnknown Implementation for DirectSound
135 static inline IDirectSoundImpl
*impl_from_IUnknown(IUnknown
*iface
)
137 return CONTAINING_RECORD(iface
, IDirectSoundImpl
, IUnknown_inner
);
140 static HRESULT WINAPI
IUnknownImpl_QueryInterface(IUnknown
*iface
, REFIID riid
, void **ppv
)
142 IDirectSoundImpl
*This
= impl_from_IUnknown(iface
);
144 TRACE("(%p,%s,%p)\n", This
, debugstr_guid(riid
), ppv
);
147 WARN("invalid parameter\n");
152 if (IsEqualIID(riid
, &IID_IUnknown
))
153 *ppv
= &This
->IUnknown_inner
;
154 else if (IsEqualIID(riid
, &IID_IDirectSound
) ||
155 (IsEqualIID(riid
, &IID_IDirectSound8
) && This
->has_ds8
))
156 *ppv
= &This
->IDirectSound8_iface
;
158 WARN("unknown IID %s\n", debugstr_guid(riid
));
159 return E_NOINTERFACE
;
162 IUnknown_AddRef((IUnknown
*)*ppv
);
166 static ULONG WINAPI
IUnknownImpl_AddRef(IUnknown
*iface
)
168 IDirectSoundImpl
*This
= impl_from_IUnknown(iface
);
169 ULONG ref
= InterlockedIncrement(&This
->ref
);
171 TRACE("(%p) ref=%d\n", This
, ref
);
174 InterlockedIncrement(&This
->numIfaces
);
179 static ULONG WINAPI
IUnknownImpl_Release(IUnknown
*iface
)
181 IDirectSoundImpl
*This
= impl_from_IUnknown(iface
);
182 ULONG ref
= InterlockedDecrement(&This
->ref
);
184 TRACE("(%p) ref=%d\n", This
, ref
);
186 if (!ref
&& !InterlockedDecrement(&This
->numIfaces
))
187 directsound_destroy(This
);
192 static const IUnknownVtbl unk_vtbl
=
194 IUnknownImpl_QueryInterface
,
199 /*******************************************************************************
200 * IDirectSound and IDirectSound8 Implementation
202 static inline IDirectSoundImpl
*impl_from_IDirectSound8(IDirectSound8
*iface
)
204 return CONTAINING_RECORD(iface
, IDirectSoundImpl
, IDirectSound8_iface
);
207 static HRESULT WINAPI
IDirectSound8Impl_QueryInterface(IDirectSound8
*iface
, REFIID riid
,
210 IDirectSoundImpl
*This
= impl_from_IDirectSound8(iface
);
211 TRACE("(%p,%s,%p)\n", This
, debugstr_guid(riid
), ppv
);
212 return IUnknown_QueryInterface(This
->outer_unk
, riid
, ppv
);
215 static ULONG WINAPI
IDirectSound8Impl_AddRef(IDirectSound8
*iface
)
217 IDirectSoundImpl
*This
= impl_from_IDirectSound8(iface
);
218 ULONG ref
= InterlockedIncrement(&This
->refds
);
220 TRACE("(%p) refds=%d\n", This
, ref
);
223 InterlockedIncrement(&This
->numIfaces
);
228 static ULONG WINAPI
IDirectSound8Impl_Release(IDirectSound8
*iface
)
230 IDirectSoundImpl
*This
= impl_from_IDirectSound8(iface
);
231 ULONG ref
= InterlockedDecrement(&(This
->refds
));
233 TRACE("(%p) refds=%d\n", This
, ref
);
235 if (!ref
&& !InterlockedDecrement(&This
->numIfaces
))
236 directsound_destroy(This
);
241 static HRESULT WINAPI
IDirectSound8Impl_CreateSoundBuffer(IDirectSound8
*iface
,
242 const DSBUFFERDESC
*dsbd
, IDirectSoundBuffer
**ppdsb
, IUnknown
*lpunk
)
244 IDirectSoundImpl
*This
= impl_from_IDirectSound8(iface
);
245 TRACE("(%p,%p,%p,%p)\n", This
, dsbd
, ppdsb
, lpunk
);
246 return DirectSoundDevice_CreateSoundBuffer(This
->device
, dsbd
, ppdsb
, lpunk
, This
->has_ds8
);
249 static HRESULT WINAPI
IDirectSound8Impl_GetCaps(IDirectSound8
*iface
, DSCAPS
*dscaps
)
251 IDirectSoundImpl
*This
= impl_from_IDirectSound8(iface
);
253 TRACE("(%p, %p)\n", This
, dscaps
);
256 WARN("not initialized\n");
257 return DSERR_UNINITIALIZED
;
260 WARN("invalid parameter: dscaps = NULL\n");
261 return DSERR_INVALIDPARAM
;
263 if (dscaps
->dwSize
< sizeof(*dscaps
)) {
264 WARN("invalid parameter: dscaps->dwSize = %d\n", dscaps
->dwSize
);
265 return DSERR_INVALIDPARAM
;
268 dscaps
->dwFlags
= This
->device
->drvcaps
.dwFlags
;
269 dscaps
->dwMinSecondarySampleRate
= This
->device
->drvcaps
.dwMinSecondarySampleRate
;
270 dscaps
->dwMaxSecondarySampleRate
= This
->device
->drvcaps
.dwMaxSecondarySampleRate
;
271 dscaps
->dwPrimaryBuffers
= This
->device
->drvcaps
.dwPrimaryBuffers
;
272 dscaps
->dwMaxHwMixingAllBuffers
= This
->device
->drvcaps
.dwMaxHwMixingAllBuffers
;
273 dscaps
->dwMaxHwMixingStaticBuffers
= This
->device
->drvcaps
.dwMaxHwMixingStaticBuffers
;
274 dscaps
->dwMaxHwMixingStreamingBuffers
= This
->device
->drvcaps
.dwMaxHwMixingStreamingBuffers
;
275 dscaps
->dwFreeHwMixingAllBuffers
= This
->device
->drvcaps
.dwFreeHwMixingAllBuffers
;
276 dscaps
->dwFreeHwMixingStaticBuffers
= This
->device
->drvcaps
.dwFreeHwMixingStaticBuffers
;
277 dscaps
->dwFreeHwMixingStreamingBuffers
= This
->device
->drvcaps
.dwFreeHwMixingStreamingBuffers
;
278 dscaps
->dwMaxHw3DAllBuffers
= This
->device
->drvcaps
.dwMaxHw3DAllBuffers
;
279 dscaps
->dwMaxHw3DStaticBuffers
= This
->device
->drvcaps
.dwMaxHw3DStaticBuffers
;
280 dscaps
->dwMaxHw3DStreamingBuffers
= This
->device
->drvcaps
.dwMaxHw3DStreamingBuffers
;
281 dscaps
->dwFreeHw3DAllBuffers
= This
->device
->drvcaps
.dwFreeHw3DAllBuffers
;
282 dscaps
->dwFreeHw3DStaticBuffers
= This
->device
->drvcaps
.dwFreeHw3DStaticBuffers
;
283 dscaps
->dwFreeHw3DStreamingBuffers
= This
->device
->drvcaps
.dwFreeHw3DStreamingBuffers
;
284 dscaps
->dwTotalHwMemBytes
= This
->device
->drvcaps
.dwTotalHwMemBytes
;
285 dscaps
->dwFreeHwMemBytes
= This
->device
->drvcaps
.dwFreeHwMemBytes
;
286 dscaps
->dwMaxContigFreeHwMemBytes
= This
->device
->drvcaps
.dwMaxContigFreeHwMemBytes
;
287 dscaps
->dwUnlockTransferRateHwBuffers
= This
->device
->drvcaps
.dwUnlockTransferRateHwBuffers
;
288 dscaps
->dwPlayCpuOverheadSwBuffers
= This
->device
->drvcaps
.dwPlayCpuOverheadSwBuffers
;
290 if (TRACE_ON(dsound
)) {
291 TRACE("(flags=0x%08x:\n", dscaps
->dwFlags
);
292 _dump_DSCAPS(dscaps
->dwFlags
);
299 static HRESULT WINAPI
IDirectSound8Impl_DuplicateSoundBuffer(IDirectSound8
*iface
,
300 IDirectSoundBuffer
*psb
, IDirectSoundBuffer
**ppdsb
)
302 IDirectSoundImpl
*This
= impl_from_IDirectSound8(iface
);
303 TRACE("(%p,%p,%p)\n", This
, psb
, ppdsb
);
304 return DirectSoundDevice_DuplicateSoundBuffer(This
->device
, psb
, ppdsb
);
307 static HRESULT WINAPI
IDirectSound8Impl_SetCooperativeLevel(IDirectSound8
*iface
, HWND hwnd
,
310 IDirectSoundImpl
*This
= impl_from_IDirectSound8(iface
);
311 DirectSoundDevice
*device
= This
->device
;
315 TRACE("(%p,%p,%s)\n", This
, hwnd
, dumpCooperativeLevel(level
));
318 WARN("not initialized\n");
319 return DSERR_UNINITIALIZED
;
322 if (level
== DSSCL_PRIORITY
|| level
== DSSCL_EXCLUSIVE
) {
323 WARN("level=%s not fully supported\n",
324 level
==DSSCL_PRIORITY
? "DSSCL_PRIORITY" : "DSSCL_EXCLUSIVE");
327 RtlAcquireResourceExclusive(&device
->buffer_list_lock
, TRUE
);
328 EnterCriticalSection(&device
->mixlock
);
329 oldlevel
= device
->priolevel
;
330 device
->priolevel
= level
;
331 if ((level
== DSSCL_WRITEPRIMARY
) != (oldlevel
== DSSCL_WRITEPRIMARY
)) {
332 hr
= DSOUND_ReopenDevice(device
, level
== DSSCL_WRITEPRIMARY
);
334 device
->priolevel
= oldlevel
;
336 DSOUND_PrimaryOpen(device
);
338 LeaveCriticalSection(&device
->mixlock
);
339 RtlReleaseResource(&device
->buffer_list_lock
);
343 static HRESULT WINAPI
IDirectSound8Impl_Compact(IDirectSound8
*iface
)
345 IDirectSoundImpl
*This
= impl_from_IDirectSound8(iface
);
347 TRACE("(%p)\n", This
);
350 WARN("not initialized\n");
351 return DSERR_UNINITIALIZED
;
354 if (This
->device
->priolevel
< DSSCL_PRIORITY
) {
355 WARN("incorrect priority level\n");
356 return DSERR_PRIOLEVELNEEDED
;
361 static HRESULT WINAPI
IDirectSound8Impl_GetSpeakerConfig(IDirectSound8
*iface
, DWORD
*config
)
363 IDirectSoundImpl
*This
= impl_from_IDirectSound8(iface
);
365 TRACE("(%p, %p)\n", This
, config
);
368 WARN("not initialized\n");
369 return DSERR_UNINITIALIZED
;
372 WARN("invalid parameter: config == NULL\n");
373 return DSERR_INVALIDPARAM
;
376 WARN("not fully functional\n");
377 *config
= This
->device
->speaker_config
;
381 static HRESULT WINAPI
IDirectSound8Impl_SetSpeakerConfig(IDirectSound8
*iface
, DWORD config
)
383 IDirectSoundImpl
*This
= impl_from_IDirectSound8(iface
);
385 TRACE("(%p,0x%08x)\n", This
, config
);
388 WARN("not initialized\n");
389 return DSERR_UNINITIALIZED
;
392 This
->device
->speaker_config
= config
;
393 WARN("not fully functional\n");
397 static HRESULT WINAPI
IDirectSound8Impl_Initialize(IDirectSound8
*iface
, const GUID
*lpcGuid
)
399 IDirectSoundImpl
*This
= impl_from_IDirectSound8(iface
);
400 TRACE("(%p, %s)\n", This
, debugstr_guid(lpcGuid
));
401 return DirectSoundDevice_Initialize(&This
->device
, lpcGuid
);
404 static HRESULT WINAPI
IDirectSound8Impl_VerifyCertification(IDirectSound8
*iface
, DWORD
*certified
)
406 IDirectSoundImpl
*This
= impl_from_IDirectSound8(iface
);
408 TRACE("(%p, %p)\n", This
, certified
);
411 WARN("not initialized\n");
412 return DSERR_UNINITIALIZED
;
415 if (This
->device
->drvcaps
.dwFlags
& DSCAPS_CERTIFIED
)
416 *certified
= DS_CERTIFIED
;
418 *certified
= DS_UNCERTIFIED
;
423 static const IDirectSound8Vtbl ds8_vtbl
=
425 IDirectSound8Impl_QueryInterface
,
426 IDirectSound8Impl_AddRef
,
427 IDirectSound8Impl_Release
,
428 IDirectSound8Impl_CreateSoundBuffer
,
429 IDirectSound8Impl_GetCaps
,
430 IDirectSound8Impl_DuplicateSoundBuffer
,
431 IDirectSound8Impl_SetCooperativeLevel
,
432 IDirectSound8Impl_Compact
,
433 IDirectSound8Impl_GetSpeakerConfig
,
434 IDirectSound8Impl_SetSpeakerConfig
,
435 IDirectSound8Impl_Initialize
,
436 IDirectSound8Impl_VerifyCertification
439 HRESULT
IDirectSoundImpl_Create(IUnknown
*outer_unk
, REFIID riid
, void **ppv
, BOOL has_ds8
)
441 IDirectSoundImpl
*obj
;
444 TRACE("(%s, %p)\n", debugstr_guid(riid
), ppv
);
447 obj
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(*obj
));
449 WARN("out of memory\n");
450 return DSERR_OUTOFMEMORY
;
453 setup_dsound_options();
455 obj
->IUnknown_inner
.lpVtbl
= &unk_vtbl
;
456 obj
->IDirectSound8_iface
.lpVtbl
= &ds8_vtbl
;
461 obj
->has_ds8
= has_ds8
;
463 /* COM aggregation supported only internally */
465 obj
->outer_unk
= outer_unk
;
467 obj
->outer_unk
= &obj
->IUnknown_inner
;
469 hr
= IUnknown_QueryInterface(&obj
->IUnknown_inner
, riid
, ppv
);
470 IUnknown_Release(&obj
->IUnknown_inner
);
475 HRESULT
DSOUND_Create(REFIID riid
, void **ppv
)
477 return IDirectSoundImpl_Create(NULL
, riid
, ppv
, FALSE
);
480 HRESULT
DSOUND_Create8(REFIID riid
, void **ppv
)
482 return IDirectSoundImpl_Create(NULL
, riid
, ppv
, TRUE
);
485 /*******************************************************************************
486 * DirectSoundCreate (DSOUND.1)
488 * Creates and initializes a DirectSound interface.
491 * lpcGUID [I] Address of the GUID that identifies the sound device.
492 * ppDS [O] Address of a variable to receive the interface pointer.
493 * pUnkOuter [I] Must be NULL.
497 * Failure: DSERR_ALLOCATED, DSERR_INVALIDPARAM, DSERR_NOAGGREGATION,
498 * DSERR_NODRIVER, DSERR_OUTOFMEMORY
500 HRESULT WINAPI
DirectSoundCreate(
508 TRACE("(%s,%p,%p)\n",debugstr_guid(lpcGUID
),ppDS
,pUnkOuter
);
511 WARN("invalid parameter: ppDS == NULL\n");
512 return DSERR_INVALIDPARAM
;
515 if (pUnkOuter
!= NULL
) {
516 WARN("invalid parameter: pUnkOuter != NULL\n");
518 return DSERR_INVALIDPARAM
;
521 hr
= DSOUND_Create(&IID_IDirectSound
, (void **)&pDS
);
523 hr
= IDirectSound_Initialize(pDS
, lpcGUID
);
525 if (hr
!= DSERR_ALREADYINITIALIZED
) {
526 IDirectSound_Release(pDS
);
538 /*******************************************************************************
539 * DirectSoundCreate8 (DSOUND.11)
541 * Creates and initializes a DirectSound8 interface.
544 * lpcGUID [I] Address of the GUID that identifies the sound device.
545 * ppDS [O] Address of a variable to receive the interface pointer.
546 * pUnkOuter [I] Must be NULL.
550 * Failure: DSERR_ALLOCATED, DSERR_INVALIDPARAM, DSERR_NOAGGREGATION,
551 * DSERR_NODRIVER, DSERR_OUTOFMEMORY
553 HRESULT WINAPI
DirectSoundCreate8(
555 LPDIRECTSOUND8
*ppDS
,
561 TRACE("(%s,%p,%p)\n",debugstr_guid(lpcGUID
),ppDS
,pUnkOuter
);
564 WARN("invalid parameter: ppDS == NULL\n");
565 return DSERR_INVALIDPARAM
;
568 if (pUnkOuter
!= NULL
) {
569 WARN("invalid parameter: pUnkOuter != NULL\n");
571 return DSERR_INVALIDPARAM
;
574 hr
= DSOUND_Create8(&IID_IDirectSound8
, (void **)&pDS
);
576 hr
= IDirectSound8_Initialize(pDS
, lpcGUID
);
578 if (hr
!= DSERR_ALREADYINITIALIZED
) {
579 IDirectSound8_Release(pDS
);
591 /*******************************************************************************
594 static HRESULT
DirectSoundDevice_Create(DirectSoundDevice
** ppDevice
)
596 DirectSoundDevice
* device
;
597 TRACE("(%p)\n", ppDevice
);
599 /* Allocate memory */
600 device
= HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY
,sizeof(DirectSoundDevice
));
601 if (device
== NULL
) {
602 WARN("out of memory\n");
603 return DSERR_OUTOFMEMORY
;
607 device
->priolevel
= DSSCL_NORMAL
;
608 device
->state
= STATE_STOPPED
;
609 device
->speaker_config
= DSSPEAKER_COMBINED(DSSPEAKER_STEREO
, DSSPEAKER_GEOMETRY_WIDE
);
611 /* 3D listener initial parameters */
612 device
->ds3dl
.dwSize
= sizeof(DS3DLISTENER
);
613 device
->ds3dl
.vPosition
.x
= 0.0;
614 device
->ds3dl
.vPosition
.y
= 0.0;
615 device
->ds3dl
.vPosition
.z
= 0.0;
616 device
->ds3dl
.vVelocity
.x
= 0.0;
617 device
->ds3dl
.vVelocity
.y
= 0.0;
618 device
->ds3dl
.vVelocity
.z
= 0.0;
619 device
->ds3dl
.vOrientFront
.x
= 0.0;
620 device
->ds3dl
.vOrientFront
.y
= 0.0;
621 device
->ds3dl
.vOrientFront
.z
= 1.0;
622 device
->ds3dl
.vOrientTop
.x
= 0.0;
623 device
->ds3dl
.vOrientTop
.y
= 1.0;
624 device
->ds3dl
.vOrientTop
.z
= 0.0;
625 device
->ds3dl
.flDistanceFactor
= DS3D_DEFAULTDISTANCEFACTOR
;
626 device
->ds3dl
.flRolloffFactor
= DS3D_DEFAULTROLLOFFFACTOR
;
627 device
->ds3dl
.flDopplerFactor
= DS3D_DEFAULTDOPPLERFACTOR
;
629 device
->prebuf
= ds_snd_queue_max
;
630 device
->guid
= GUID_NULL
;
632 /* Set default wave format (may need it for waveOutOpen) */
633 device
->pwfx
= HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY
,sizeof(WAVEFORMATEXTENSIBLE
));
634 device
->primary_pwfx
= HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY
,sizeof(WAVEFORMATEXTENSIBLE
));
635 if (!device
->pwfx
|| !device
->primary_pwfx
) {
636 WARN("out of memory\n");
637 HeapFree(GetProcessHeap(),0,device
->primary_pwfx
);
638 HeapFree(GetProcessHeap(),0,device
->pwfx
);
639 HeapFree(GetProcessHeap(),0,device
);
640 return DSERR_OUTOFMEMORY
;
643 device
->pwfx
->wFormatTag
= WAVE_FORMAT_PCM
;
644 device
->pwfx
->nSamplesPerSec
= 22050;
645 device
->pwfx
->wBitsPerSample
= 8;
646 device
->pwfx
->nChannels
= 2;
647 device
->pwfx
->nBlockAlign
= device
->pwfx
->wBitsPerSample
* device
->pwfx
->nChannels
/ 8;
648 device
->pwfx
->nAvgBytesPerSec
= device
->pwfx
->nSamplesPerSec
* device
->pwfx
->nBlockAlign
;
649 device
->pwfx
->cbSize
= 0;
650 memcpy(device
->primary_pwfx
, device
->pwfx
, sizeof(*device
->pwfx
));
652 InitializeCriticalSection(&(device
->mixlock
));
653 device
->mixlock
.DebugInfo
->Spare
[0] = (DWORD_PTR
)(__FILE__
": DirectSoundDevice.mixlock");
655 RtlInitializeResource(&(device
->buffer_list_lock
));
662 static ULONG
DirectSoundDevice_AddRef(DirectSoundDevice
* device
)
664 ULONG ref
= InterlockedIncrement(&(device
->ref
));
665 TRACE("(%p) ref was %d\n", device
, ref
- 1);
669 ULONG
DirectSoundDevice_Release(DirectSoundDevice
* device
)
672 ULONG ref
= InterlockedDecrement(&(device
->ref
));
673 TRACE("(%p) ref was %u\n", device
, ref
+ 1);
677 SetEvent(device
->sleepev
);
678 if (device
->thread
) {
679 WaitForSingleObject(device
->thread
, INFINITE
);
680 CloseHandle(device
->thread
);
682 CloseHandle(device
->sleepev
);
684 EnterCriticalSection(&DSOUND_renderers_lock
);
685 list_remove(&device
->entry
);
686 LeaveCriticalSection(&DSOUND_renderers_lock
);
688 /* It is allowed to release this object even when buffers are playing */
689 if (device
->buffers
) {
690 WARN("%d secondary buffers not released\n", device
->nrofbuffers
);
691 for( i
=0;i
<device
->nrofbuffers
;i
++)
692 secondarybuffer_destroy(device
->buffers
[i
]);
695 hr
= DSOUND_PrimaryDestroy(device
);
697 WARN("DSOUND_PrimaryDestroy failed\n");
700 IAudioClient_Release(device
->client
);
702 IAudioRenderClient_Release(device
->render
);
704 IAudioClock_Release(device
->clock
);
706 IAudioStreamVolume_Release(device
->volume
);
708 HeapFree(GetProcessHeap(), 0, device
->tmp_buffer
);
709 HeapFree(GetProcessHeap(), 0, device
->mix_buffer
);
710 HeapFree(GetProcessHeap(), 0, device
->buffer
);
711 RtlDeleteResource(&device
->buffer_list_lock
);
712 device
->mixlock
.DebugInfo
->Spare
[0] = 0;
713 DeleteCriticalSection(&device
->mixlock
);
714 HeapFree(GetProcessHeap(),0,device
);
715 TRACE("(%p) released\n", device
);
720 BOOL
DSOUND_check_supported(IAudioClient
*client
, DWORD rate
,
721 DWORD depth
, WORD channels
)
723 WAVEFORMATEX fmt
, *junk
;
726 fmt
.wFormatTag
= WAVE_FORMAT_PCM
;
727 fmt
.nChannels
= channels
;
728 fmt
.nSamplesPerSec
= rate
;
729 fmt
.wBitsPerSample
= depth
;
730 fmt
.nBlockAlign
= (channels
* depth
) / 8;
731 fmt
.nAvgBytesPerSec
= rate
* fmt
.nBlockAlign
;
734 hr
= IAudioClient_IsFormatSupported(client
, AUDCLNT_SHAREMODE_SHARED
, &fmt
, &junk
);
741 UINT
DSOUND_create_timer(LPTIMECALLBACK cb
, DWORD_PTR user
)
743 UINT triggertime
= DS_TIME_DEL
, res
= DS_TIME_RES
, id
;
746 timeGetDevCaps(&time
, sizeof(TIMECAPS
));
747 TRACE("Minimum timer resolution: %u, max timer: %u\n", time
.wPeriodMin
, time
.wPeriodMax
);
748 if (triggertime
< time
.wPeriodMin
)
749 triggertime
= time
.wPeriodMin
;
750 if (res
< time
.wPeriodMin
)
751 res
= time
.wPeriodMin
;
752 if (timeBeginPeriod(res
) == TIMERR_NOCANDO
)
753 WARN("Could not set minimum resolution, don't expect sound\n");
754 id
= timeSetEvent(triggertime
, res
, cb
, user
, TIME_PERIODIC
| TIME_KILL_SYNCHRONOUS
);
757 WARN("Timer not created! Retrying without TIME_KILL_SYNCHRONOUS\n");
758 id
= timeSetEvent(triggertime
, res
, cb
, user
, TIME_PERIODIC
);
760 ERR("Could not create timer, sound playback will not occur\n");
765 HRESULT
DirectSoundDevice_Initialize(DirectSoundDevice
** ppDevice
, LPCGUID lpcGUID
)
769 DirectSoundDevice
*device
;
772 TRACE("(%p,%s)\n",ppDevice
,debugstr_guid(lpcGUID
));
774 if (*ppDevice
!= NULL
) {
775 WARN("already initialized\n");
776 return DSERR_ALREADYINITIALIZED
;
779 /* Default device? */
780 if (!lpcGUID
|| IsEqualGUID(lpcGUID
, &GUID_NULL
))
781 lpcGUID
= &DSDEVID_DefaultPlayback
;
783 if(IsEqualGUID(lpcGUID
, &DSDEVID_DefaultCapture
) ||
784 IsEqualGUID(lpcGUID
, &DSDEVID_DefaultVoiceCapture
))
785 return DSERR_NODRIVER
;
787 if (GetDeviceID(lpcGUID
, &devGUID
) != DS_OK
) {
788 WARN("invalid parameter: lpcGUID\n");
789 return DSERR_INVALIDPARAM
;
792 hr
= get_mmdevice(eRender
, &devGUID
, &mmdevice
);
796 EnterCriticalSection(&DSOUND_renderers_lock
);
798 LIST_FOR_EACH_ENTRY(device
, &DSOUND_renderers
, DirectSoundDevice
, entry
){
799 if(IsEqualGUID(&device
->guid
, &devGUID
)){
800 IMMDevice_Release(mmdevice
);
801 DirectSoundDevice_AddRef(device
);
803 LeaveCriticalSection(&DSOUND_renderers_lock
);
808 hr
= DirectSoundDevice_Create(&device
);
810 WARN("DirectSoundDevice_Create failed\n");
811 IMMDevice_Release(mmdevice
);
812 LeaveCriticalSection(&DSOUND_renderers_lock
);
816 device
->mmdevice
= mmdevice
;
817 device
->guid
= devGUID
;
818 device
->sleepev
= CreateEventW(0, 0, 0, 0);
820 hr
= DSOUND_ReopenDevice(device
, FALSE
);
823 HeapFree(GetProcessHeap(), 0, device
);
824 LeaveCriticalSection(&DSOUND_renderers_lock
);
825 IMMDevice_Release(mmdevice
);
826 WARN("DSOUND_ReopenDevice failed: %08x\n", hr
);
830 ZeroMemory(&device
->drvcaps
, sizeof(device
->drvcaps
));
832 if(DSOUND_check_supported(device
->client
, 11025, 8, 1) ||
833 DSOUND_check_supported(device
->client
, 22050, 8, 1) ||
834 DSOUND_check_supported(device
->client
, 44100, 8, 1) ||
835 DSOUND_check_supported(device
->client
, 48000, 8, 1) ||
836 DSOUND_check_supported(device
->client
, 96000, 8, 1))
837 device
->drvcaps
.dwFlags
|= DSCAPS_PRIMARY8BIT
| DSCAPS_PRIMARYMONO
;
839 if(DSOUND_check_supported(device
->client
, 11025, 16, 1) ||
840 DSOUND_check_supported(device
->client
, 22050, 16, 1) ||
841 DSOUND_check_supported(device
->client
, 44100, 16, 1) ||
842 DSOUND_check_supported(device
->client
, 48000, 16, 1) ||
843 DSOUND_check_supported(device
->client
, 96000, 16, 1))
844 device
->drvcaps
.dwFlags
|= DSCAPS_PRIMARY16BIT
| DSCAPS_PRIMARYMONO
;
846 if(DSOUND_check_supported(device
->client
, 11025, 8, 2) ||
847 DSOUND_check_supported(device
->client
, 22050, 8, 2) ||
848 DSOUND_check_supported(device
->client
, 44100, 8, 2) ||
849 DSOUND_check_supported(device
->client
, 48000, 8, 2) ||
850 DSOUND_check_supported(device
->client
, 96000, 8, 2))
851 device
->drvcaps
.dwFlags
|= DSCAPS_PRIMARY8BIT
| DSCAPS_PRIMARYSTEREO
;
853 if(DSOUND_check_supported(device
->client
, 11025, 16, 2) ||
854 DSOUND_check_supported(device
->client
, 22050, 16, 2) ||
855 DSOUND_check_supported(device
->client
, 44100, 16, 2) ||
856 DSOUND_check_supported(device
->client
, 48000, 16, 2) ||
857 DSOUND_check_supported(device
->client
, 96000, 16, 2))
858 device
->drvcaps
.dwFlags
|= DSCAPS_PRIMARY16BIT
| DSCAPS_PRIMARYSTEREO
;
860 /* the dsound mixer supports all of the following */
861 device
->drvcaps
.dwFlags
|= DSCAPS_SECONDARY8BIT
| DSCAPS_SECONDARY16BIT
;
862 device
->drvcaps
.dwFlags
|= DSCAPS_SECONDARYMONO
| DSCAPS_SECONDARYSTEREO
;
863 device
->drvcaps
.dwFlags
|= DSCAPS_CONTINUOUSRATE
;
865 device
->drvcaps
.dwPrimaryBuffers
= 1;
866 device
->drvcaps
.dwMinSecondarySampleRate
= DSBFREQUENCY_MIN
;
867 device
->drvcaps
.dwMaxSecondarySampleRate
= DSBFREQUENCY_MAX
;
868 device
->drvcaps
.dwMaxHwMixingAllBuffers
= 1;
869 device
->drvcaps
.dwMaxHwMixingStaticBuffers
= 1;
870 device
->drvcaps
.dwMaxHwMixingStreamingBuffers
= 1;
872 ZeroMemory(&device
->volpan
, sizeof(device
->volpan
));
874 hr
= DSOUND_PrimaryCreate(device
);
876 device
->thread
= CreateThread(0, 0, DSOUND_mixthread
, device
, 0, 0);
877 SetThreadPriority(device
->thread
, THREAD_PRIORITY_TIME_CRITICAL
);
879 WARN("DSOUND_PrimaryCreate failed: %08x\n", hr
);
882 list_add_tail(&DSOUND_renderers
, &device
->entry
);
884 LeaveCriticalSection(&DSOUND_renderers_lock
);
889 HRESULT
DirectSoundDevice_CreateSoundBuffer(
890 DirectSoundDevice
* device
,
891 LPCDSBUFFERDESC dsbd
,
892 LPLPDIRECTSOUNDBUFFER ppdsb
,
896 HRESULT hres
= DS_OK
;
897 TRACE("(%p,%p,%p,%p)\n",device
,dsbd
,ppdsb
,lpunk
);
899 if (device
== NULL
) {
900 WARN("not initialized\n");
901 return DSERR_UNINITIALIZED
;
905 WARN("invalid parameter: dsbd == NULL\n");
906 return DSERR_INVALIDPARAM
;
909 if (dsbd
->dwSize
!= sizeof(DSBUFFERDESC
) &&
910 dsbd
->dwSize
!= sizeof(DSBUFFERDESC1
)) {
911 WARN("invalid parameter: dsbd\n");
912 return DSERR_INVALIDPARAM
;
916 WARN("invalid parameter: ppdsb == NULL\n");
917 return DSERR_INVALIDPARAM
;
921 if (TRACE_ON(dsound
)) {
922 TRACE("(structsize=%d)\n",dsbd
->dwSize
);
923 TRACE("(flags=0x%08x:\n",dsbd
->dwFlags
);
924 _dump_DSBCAPS(dsbd
->dwFlags
);
926 TRACE("(bufferbytes=%d)\n",dsbd
->dwBufferBytes
);
927 TRACE("(lpwfxFormat=%p)\n",dsbd
->lpwfxFormat
);
930 if (dsbd
->dwFlags
& DSBCAPS_LOCHARDWARE
&&
931 !(dsbd
->dwFlags
& DSBCAPS_PRIMARYBUFFER
)) {
932 TRACE("LOCHARDWARE is not supported, returning E_NOTIMPL\n");
936 if (dsbd
->dwFlags
& DSBCAPS_PRIMARYBUFFER
) {
937 if (dsbd
->lpwfxFormat
!= NULL
) {
938 WARN("invalid parameter: dsbd->lpwfxFormat must be NULL for "
940 return DSERR_INVALIDPARAM
;
943 if (device
->primary
) {
944 WARN("Primary Buffer already created\n");
945 IDirectSoundBuffer_AddRef((LPDIRECTSOUNDBUFFER8
)(device
->primary
));
946 *ppdsb
= (LPDIRECTSOUNDBUFFER
)(device
->primary
);
948 hres
= primarybuffer_create(device
, &device
->primary
, dsbd
);
949 if (device
->primary
) {
950 *ppdsb
= (IDirectSoundBuffer
*)&device
->primary
->IDirectSoundBuffer8_iface
;
951 device
->primary
->dsbd
.dwFlags
&= ~(DSBCAPS_LOCHARDWARE
| DSBCAPS_LOCSOFTWARE
);
952 device
->primary
->dsbd
.dwFlags
|= DSBCAPS_LOCSOFTWARE
;
954 WARN("primarybuffer_create() failed\n");
957 IDirectSoundBufferImpl
* dsb
;
958 WAVEFORMATEXTENSIBLE
*pwfxe
;
960 if (dsbd
->lpwfxFormat
== NULL
) {
961 WARN("invalid parameter: dsbd->lpwfxFormat can't be NULL for "
962 "secondary buffer\n");
963 return DSERR_INVALIDPARAM
;
965 pwfxe
= (WAVEFORMATEXTENSIBLE
*)dsbd
->lpwfxFormat
;
967 if (pwfxe
->Format
.wFormatTag
== WAVE_FORMAT_EXTENSIBLE
)
969 /* check if cbSize is at least 22 bytes */
970 if (pwfxe
->Format
.cbSize
< (sizeof(WAVEFORMATEXTENSIBLE
) - sizeof(WAVEFORMATEX
)))
972 WARN("Too small a cbSize %u\n", pwfxe
->Format
.cbSize
);
973 return DSERR_INVALIDPARAM
;
976 /* cbSize should be 22 bytes, with one possible exception */
977 if (pwfxe
->Format
.cbSize
> (sizeof(WAVEFORMATEXTENSIBLE
) - sizeof(WAVEFORMATEX
)) &&
978 !((IsEqualGUID(&pwfxe
->SubFormat
, &KSDATAFORMAT_SUBTYPE_PCM
) || IsEqualGUID(&pwfxe
->SubFormat
, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
)) &&
979 pwfxe
->Format
.cbSize
== sizeof(WAVEFORMATEXTENSIBLE
)))
981 WARN("Too big a cbSize %u\n", pwfxe
->Format
.cbSize
);
982 return DSERR_CONTROLUNAVAIL
;
985 if ((!IsEqualGUID(&pwfxe
->SubFormat
, &KSDATAFORMAT_SUBTYPE_PCM
)) && (!IsEqualGUID(&pwfxe
->SubFormat
, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
)))
987 if (!IsEqualGUID(&pwfxe
->SubFormat
, &GUID_NULL
))
988 FIXME("SubFormat %s not supported right now.\n", debugstr_guid(&pwfxe
->SubFormat
));
989 return DSERR_INVALIDPARAM
;
991 if (pwfxe
->Samples
.wValidBitsPerSample
> dsbd
->lpwfxFormat
->wBitsPerSample
)
993 WARN("Samples.wValidBitsPerSample(%d) > Format.wBitsPerSample (%d)\n", pwfxe
->Samples
.wValidBitsPerSample
, pwfxe
->Format
.wBitsPerSample
);
994 return DSERR_INVALIDPARAM
;
996 if (pwfxe
->Samples
.wValidBitsPerSample
&& pwfxe
->Samples
.wValidBitsPerSample
< dsbd
->lpwfxFormat
->wBitsPerSample
)
998 FIXME("Non-packed formats not supported right now: %d/%d\n", pwfxe
->Samples
.wValidBitsPerSample
, dsbd
->lpwfxFormat
->wBitsPerSample
);
999 return DSERR_CONTROLUNAVAIL
;
1003 TRACE("(formattag=0x%04x,chans=%d,samplerate=%d,"
1004 "bytespersec=%d,blockalign=%d,bitspersamp=%d,cbSize=%d)\n",
1005 dsbd
->lpwfxFormat
->wFormatTag
, dsbd
->lpwfxFormat
->nChannels
,
1006 dsbd
->lpwfxFormat
->nSamplesPerSec
,
1007 dsbd
->lpwfxFormat
->nAvgBytesPerSec
,
1008 dsbd
->lpwfxFormat
->nBlockAlign
,
1009 dsbd
->lpwfxFormat
->wBitsPerSample
, dsbd
->lpwfxFormat
->cbSize
);
1011 if (from8
&& (dsbd
->dwFlags
& DSBCAPS_CTRL3D
) && (dsbd
->lpwfxFormat
->nChannels
!= 1)) {
1012 WARN("invalid parameter: 3D buffer format must be mono\n");
1013 return DSERR_INVALIDPARAM
;
1016 hres
= IDirectSoundBufferImpl_Create(device
, &dsb
, dsbd
);
1018 *ppdsb
= (IDirectSoundBuffer
*)&dsb
->IDirectSoundBuffer8_iface
;
1020 WARN("IDirectSoundBufferImpl_Create failed\n");
1026 HRESULT
DirectSoundDevice_DuplicateSoundBuffer(
1027 DirectSoundDevice
* device
,
1028 LPDIRECTSOUNDBUFFER psb
,
1029 LPLPDIRECTSOUNDBUFFER ppdsb
)
1031 HRESULT hres
= DS_OK
;
1032 IDirectSoundBufferImpl
* dsb
;
1033 TRACE("(%p,%p,%p)\n",device
,psb
,ppdsb
);
1035 if (device
== NULL
) {
1036 WARN("not initialized\n");
1037 return DSERR_UNINITIALIZED
;
1041 WARN("invalid parameter: psb == NULL\n");
1042 return DSERR_INVALIDPARAM
;
1045 if (ppdsb
== NULL
) {
1046 WARN("invalid parameter: ppdsb == NULL\n");
1047 return DSERR_INVALIDPARAM
;
1050 /* make sure we have a secondary buffer */
1051 if (psb
== (IDirectSoundBuffer
*)&device
->primary
->IDirectSoundBuffer8_iface
) {
1052 WARN("trying to duplicate primary buffer\n");
1054 return DSERR_INVALIDCALL
;
1057 /* duplicate the actual buffer implementation */
1058 hres
= IDirectSoundBufferImpl_Duplicate(device
, &dsb
, (IDirectSoundBufferImpl
*)psb
);
1060 *ppdsb
= (IDirectSoundBuffer
*)&dsb
->IDirectSoundBuffer8_iface
;
1062 WARN("IDirectSoundBufferImpl_Duplicate failed\n");
1068 * Add secondary buffer to buffer list.
1069 * Gets exclusive access to buffer for writing.
1071 HRESULT
DirectSoundDevice_AddBuffer(
1072 DirectSoundDevice
* device
,
1073 IDirectSoundBufferImpl
* pDSB
)
1075 IDirectSoundBufferImpl
**newbuffers
;
1078 TRACE("(%p, %p)\n", device
, pDSB
);
1080 RtlAcquireResourceExclusive(&(device
->buffer_list_lock
), TRUE
);
1082 if (device
->buffers
)
1083 newbuffers
= HeapReAlloc(GetProcessHeap(),0,device
->buffers
,sizeof(IDirectSoundBufferImpl
*)*(device
->nrofbuffers
+1));
1085 newbuffers
= HeapAlloc(GetProcessHeap(),0,sizeof(IDirectSoundBufferImpl
*)*(device
->nrofbuffers
+1));
1088 device
->buffers
= newbuffers
;
1089 device
->buffers
[device
->nrofbuffers
] = pDSB
;
1090 device
->nrofbuffers
++;
1091 TRACE("buffer count is now %d\n", device
->nrofbuffers
);
1093 ERR("out of memory for buffer list! Current buffer count is %d\n", device
->nrofbuffers
);
1094 hr
= DSERR_OUTOFMEMORY
;
1097 RtlReleaseResource(&(device
->buffer_list_lock
));
1103 * Remove secondary buffer from buffer list.
1104 * Gets exclusive access to buffer for writing.
1106 void DirectSoundDevice_RemoveBuffer(DirectSoundDevice
* device
, IDirectSoundBufferImpl
* pDSB
)
1110 TRACE("(%p, %p)\n", device
, pDSB
);
1112 RtlAcquireResourceExclusive(&(device
->buffer_list_lock
), TRUE
);
1114 if (device
->nrofbuffers
== 1) {
1115 assert(device
->buffers
[0] == pDSB
);
1116 HeapFree(GetProcessHeap(), 0, device
->buffers
);
1117 device
->buffers
= NULL
;
1119 for (i
= 0; i
< device
->nrofbuffers
; i
++) {
1120 if (device
->buffers
[i
] == pDSB
) {
1121 /* Put the last buffer of the list in the (now empty) position */
1122 device
->buffers
[i
] = device
->buffers
[device
->nrofbuffers
- 1];
1127 device
->nrofbuffers
--;
1128 TRACE("buffer count is now %d\n", device
->nrofbuffers
);
1130 RtlReleaseResource(&(device
->buffer_list_lock
));