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 NONAMELESSSTRUCT
28 #define NONAMELESSUNION
38 #include "wine/debug.h"
40 #include "dsound_private.h"
42 WINE_DEFAULT_DEBUG_CHANNEL(dsound
);
44 typedef struct IDirectSoundImpl
{
45 IUnknown IUnknown_iface
; /* Separate refcount, not for COM aggregation */
46 IDirectSound8 IDirectSound8_iface
;
47 LONG ref
, refds
, numIfaces
;
48 DirectSoundDevice
*device
;
52 const char * dumpCooperativeLevel(DWORD level
)
54 #define LE(x) case x: return #x
59 LE(DSSCL_WRITEPRIMARY
);
62 return wine_dbg_sprintf("Unknown(%08x)", level
);
65 static void _dump_DSCAPS(DWORD xmask
) {
70 #define FE(x) { x, #x },
71 FE(DSCAPS_PRIMARYMONO
)
72 FE(DSCAPS_PRIMARYSTEREO
)
73 FE(DSCAPS_PRIMARY8BIT
)
74 FE(DSCAPS_PRIMARY16BIT
)
75 FE(DSCAPS_CONTINUOUSRATE
)
78 FE(DSCAPS_SECONDARYMONO
)
79 FE(DSCAPS_SECONDARYSTEREO
)
80 FE(DSCAPS_SECONDARY8BIT
)
81 FE(DSCAPS_SECONDARY16BIT
)
86 for (i
=0;i
<sizeof(flags
)/sizeof(flags
[0]);i
++)
87 if ((flags
[i
].mask
& xmask
) == flags
[i
].mask
)
88 TRACE("%s ",flags
[i
].name
);
91 static void _dump_DSBCAPS(DWORD xmask
) {
96 #define FE(x) { x, #x },
97 FE(DSBCAPS_PRIMARYBUFFER
)
99 FE(DSBCAPS_LOCHARDWARE
)
100 FE(DSBCAPS_LOCSOFTWARE
)
102 FE(DSBCAPS_CTRLFREQUENCY
)
104 FE(DSBCAPS_CTRLVOLUME
)
105 FE(DSBCAPS_CTRLPOSITIONNOTIFY
)
106 FE(DSBCAPS_STICKYFOCUS
)
107 FE(DSBCAPS_GLOBALFOCUS
)
108 FE(DSBCAPS_GETCURRENTPOSITION2
)
109 FE(DSBCAPS_MUTE3DATMAXDISTANCE
)
114 for (i
=0;i
<sizeof(flags
)/sizeof(flags
[0]);i
++)
115 if ((flags
[i
].mask
& xmask
) == flags
[i
].mask
)
116 TRACE("%s ",flags
[i
].name
);
119 static void directsound_destroy(IDirectSoundImpl
*This
)
122 DirectSoundDevice_Release(This
->device
);
123 HeapFree(GetProcessHeap(),0,This
);
124 TRACE("(%p) released\n", This
);
127 /*******************************************************************************
128 * IUnknown Implementation for DirectSound
130 static inline IDirectSoundImpl
*impl_from_IUnknown(IUnknown
*iface
)
132 return CONTAINING_RECORD(iface
, IDirectSoundImpl
, IUnknown_iface
);
135 static HRESULT WINAPI
IUnknownImpl_QueryInterface(IUnknown
*iface
, REFIID riid
, void **ppv
)
137 IDirectSoundImpl
*This
= impl_from_IUnknown(iface
);
139 TRACE("(%p,%s,%p)\n", This
, debugstr_guid(riid
), ppv
);
142 WARN("invalid parameter\n");
147 if (IsEqualIID(riid
, &IID_IUnknown
))
148 *ppv
= &This
->IUnknown_iface
;
149 else if (IsEqualIID(riid
, &IID_IDirectSound
) ||
150 (IsEqualIID(riid
, &IID_IDirectSound8
) && This
->has_ds8
))
151 *ppv
= &This
->IDirectSound8_iface
;
153 WARN("unknown IID %s\n", debugstr_guid(riid
));
154 return E_NOINTERFACE
;
157 IUnknown_AddRef((IUnknown
*)*ppv
);
161 static ULONG WINAPI
IUnknownImpl_AddRef(IUnknown
*iface
)
163 IDirectSoundImpl
*This
= impl_from_IUnknown(iface
);
164 ULONG ref
= InterlockedIncrement(&This
->ref
);
166 TRACE("(%p) ref=%d\n", This
, ref
);
169 InterlockedIncrement(&This
->numIfaces
);
174 static ULONG WINAPI
IUnknownImpl_Release(IUnknown
*iface
)
176 IDirectSoundImpl
*This
= impl_from_IUnknown(iface
);
177 ULONG ref
= InterlockedDecrement(&This
->ref
);
179 TRACE("(%p) ref=%d\n", This
, ref
);
181 if (!ref
&& !InterlockedDecrement(&This
->numIfaces
))
182 directsound_destroy(This
);
187 static const IUnknownVtbl unk_vtbl
=
189 IUnknownImpl_QueryInterface
,
194 /*******************************************************************************
195 * IDirectSound and IDirectSound8 Implementation
197 static inline IDirectSoundImpl
*impl_from_IDirectSound8(IDirectSound8
*iface
)
199 return CONTAINING_RECORD(iface
, IDirectSoundImpl
, IDirectSound8_iface
);
202 static HRESULT WINAPI
IDirectSound8Impl_QueryInterface(IDirectSound8
*iface
, REFIID riid
,
205 IDirectSoundImpl
*This
= impl_from_IDirectSound8(iface
);
206 TRACE("(%p,%s,%p)\n", This
, debugstr_guid(riid
), ppv
);
207 return IUnknown_QueryInterface(&This
->IUnknown_iface
, riid
, ppv
);
210 static ULONG WINAPI
IDirectSound8Impl_AddRef(IDirectSound8
*iface
)
212 IDirectSoundImpl
*This
= impl_from_IDirectSound8(iface
);
213 ULONG ref
= InterlockedIncrement(&This
->refds
);
215 TRACE("(%p) refds=%d\n", This
, ref
);
218 InterlockedIncrement(&This
->numIfaces
);
223 static ULONG WINAPI
IDirectSound8Impl_Release(IDirectSound8
*iface
)
225 IDirectSoundImpl
*This
= impl_from_IDirectSound8(iface
);
226 ULONG ref
= InterlockedDecrement(&(This
->refds
));
228 TRACE("(%p) refds=%d\n", This
, ref
);
230 if (!ref
&& !InterlockedDecrement(&This
->numIfaces
))
231 directsound_destroy(This
);
236 static HRESULT WINAPI
IDirectSound8Impl_CreateSoundBuffer(IDirectSound8
*iface
,
237 const DSBUFFERDESC
*dsbd
, IDirectSoundBuffer
**ppdsb
, IUnknown
*lpunk
)
239 IDirectSoundImpl
*This
= impl_from_IDirectSound8(iface
);
240 TRACE("(%p,%p,%p,%p)\n", This
, dsbd
, ppdsb
, lpunk
);
241 return DirectSoundDevice_CreateSoundBuffer(This
->device
, dsbd
, ppdsb
, lpunk
, This
->has_ds8
);
244 static HRESULT WINAPI
IDirectSound8Impl_GetCaps(IDirectSound8
*iface
, DSCAPS
*lpDSCaps
)
246 IDirectSoundImpl
*This
= impl_from_IDirectSound8(iface
);
247 TRACE("(%p,%p)\n", This
, lpDSCaps
);
248 return DirectSoundDevice_GetCaps(This
->device
, lpDSCaps
);
251 static HRESULT WINAPI
IDirectSound8Impl_DuplicateSoundBuffer(IDirectSound8
*iface
,
252 IDirectSoundBuffer
*psb
, IDirectSoundBuffer
**ppdsb
)
254 IDirectSoundImpl
*This
= impl_from_IDirectSound8(iface
);
255 TRACE("(%p,%p,%p)\n", This
, psb
, ppdsb
);
256 return DirectSoundDevice_DuplicateSoundBuffer(This
->device
, psb
, ppdsb
);
259 static HRESULT WINAPI
IDirectSound8Impl_SetCooperativeLevel(IDirectSound8
*iface
, HWND hwnd
,
262 IDirectSoundImpl
*This
= impl_from_IDirectSound8(iface
);
263 TRACE("(%p,%p,%s)\n", This
, hwnd
, dumpCooperativeLevel(level
));
264 return DirectSoundDevice_SetCooperativeLevel(This
->device
, hwnd
, level
);
267 static HRESULT WINAPI
IDirectSound8Impl_Compact(IDirectSound8
*iface
)
269 IDirectSoundImpl
*This
= impl_from_IDirectSound8(iface
);
270 TRACE("(%p)\n", This
);
271 return DirectSoundDevice_Compact(This
->device
);
274 static HRESULT WINAPI
IDirectSound8Impl_GetSpeakerConfig(IDirectSound8
*iface
,
275 DWORD
*lpdwSpeakerConfig
)
277 IDirectSoundImpl
*This
= impl_from_IDirectSound8(iface
);
278 TRACE("(%p, %p)\n", This
, lpdwSpeakerConfig
);
279 return DirectSoundDevice_GetSpeakerConfig(This
->device
, lpdwSpeakerConfig
);
282 static HRESULT WINAPI
IDirectSound8Impl_SetSpeakerConfig(IDirectSound8
*iface
, DWORD config
)
284 IDirectSoundImpl
*This
= impl_from_IDirectSound8(iface
);
285 TRACE("(%p,0x%08x)\n", This
, config
);
286 return DirectSoundDevice_SetSpeakerConfig(This
->device
, config
);
289 static HRESULT WINAPI
IDirectSound8Impl_Initialize(IDirectSound8
*iface
, const GUID
*lpcGuid
)
291 IDirectSoundImpl
*This
= impl_from_IDirectSound8(iface
);
292 TRACE("(%p, %s)\n", This
, debugstr_guid(lpcGuid
));
293 return DirectSoundDevice_Initialize(&This
->device
, lpcGuid
);
296 static HRESULT WINAPI
IDirectSound8Impl_VerifyCertification(IDirectSound8
*iface
,
299 IDirectSoundImpl
*This
= impl_from_IDirectSound8(iface
);
300 TRACE("(%p, %p)\n", This
, pdwCertified
);
301 return DirectSoundDevice_VerifyCertification(This
->device
, pdwCertified
);
304 static const IDirectSound8Vtbl ds8_vtbl
=
306 IDirectSound8Impl_QueryInterface
,
307 IDirectSound8Impl_AddRef
,
308 IDirectSound8Impl_Release
,
309 IDirectSound8Impl_CreateSoundBuffer
,
310 IDirectSound8Impl_GetCaps
,
311 IDirectSound8Impl_DuplicateSoundBuffer
,
312 IDirectSound8Impl_SetCooperativeLevel
,
313 IDirectSound8Impl_Compact
,
314 IDirectSound8Impl_GetSpeakerConfig
,
315 IDirectSound8Impl_SetSpeakerConfig
,
316 IDirectSound8Impl_Initialize
,
317 IDirectSound8Impl_VerifyCertification
320 static HRESULT
IDirectSoundImpl_Create(REFIID riid
, void **ppv
, BOOL has_ds8
)
322 IDirectSoundImpl
*obj
;
325 TRACE("(%s, %p)\n", debugstr_guid(riid
), ppv
);
328 obj
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(*obj
));
330 WARN("out of memory\n");
331 return DSERR_OUTOFMEMORY
;
334 setup_dsound_options();
336 obj
->IUnknown_iface
.lpVtbl
= &unk_vtbl
;
337 obj
->IDirectSound8_iface
.lpVtbl
= &ds8_vtbl
;
342 obj
->has_ds8
= has_ds8
;
344 hr
= IUnknown_QueryInterface(&obj
->IUnknown_iface
, riid
, ppv
);
345 IUnknown_Release(&obj
->IUnknown_iface
);
350 HRESULT
DSOUND_Create(REFIID riid
, void **ppv
)
352 return IDirectSoundImpl_Create(riid
, ppv
, FALSE
);
355 HRESULT
DSOUND_Create8(REFIID riid
, void **ppv
)
357 return IDirectSoundImpl_Create(riid
, ppv
, TRUE
);
360 /*******************************************************************************
361 * DirectSoundCreate (DSOUND.1)
363 * Creates and initializes a DirectSound interface.
366 * lpcGUID [I] Address of the GUID that identifies the sound device.
367 * ppDS [O] Address of a variable to receive the interface pointer.
368 * pUnkOuter [I] Must be NULL.
372 * Failure: DSERR_ALLOCATED, DSERR_INVALIDPARAM, DSERR_NOAGGREGATION,
373 * DSERR_NODRIVER, DSERR_OUTOFMEMORY
375 HRESULT WINAPI
DirectSoundCreate(
383 TRACE("(%s,%p,%p)\n",debugstr_guid(lpcGUID
),ppDS
,pUnkOuter
);
386 WARN("invalid parameter: ppDS == NULL\n");
387 return DSERR_INVALIDPARAM
;
390 if (pUnkOuter
!= NULL
) {
391 WARN("invalid parameter: pUnkOuter != NULL\n");
393 return DSERR_INVALIDPARAM
;
396 hr
= DSOUND_Create(&IID_IDirectSound
, (void **)&pDS
);
398 hr
= IDirectSound_Initialize(pDS
, lpcGUID
);
400 if (hr
!= DSERR_ALREADYINITIALIZED
) {
401 IDirectSound_Release(pDS
);
413 /*******************************************************************************
414 * DirectSoundCreate8 (DSOUND.11)
416 * Creates and initializes a DirectSound8 interface.
419 * lpcGUID [I] Address of the GUID that identifies the sound device.
420 * ppDS [O] Address of a variable to receive the interface pointer.
421 * pUnkOuter [I] Must be NULL.
425 * Failure: DSERR_ALLOCATED, DSERR_INVALIDPARAM, DSERR_NOAGGREGATION,
426 * DSERR_NODRIVER, DSERR_OUTOFMEMORY
428 HRESULT WINAPI
DirectSoundCreate8(
430 LPDIRECTSOUND8
*ppDS
,
436 TRACE("(%s,%p,%p)\n",debugstr_guid(lpcGUID
),ppDS
,pUnkOuter
);
439 WARN("invalid parameter: ppDS == NULL\n");
440 return DSERR_INVALIDPARAM
;
443 if (pUnkOuter
!= NULL
) {
444 WARN("invalid parameter: pUnkOuter != NULL\n");
446 return DSERR_INVALIDPARAM
;
449 hr
= DSOUND_Create8(&IID_IDirectSound8
, (void **)&pDS
);
451 hr
= IDirectSound8_Initialize(pDS
, lpcGUID
);
453 if (hr
!= DSERR_ALREADYINITIALIZED
) {
454 IDirectSound8_Release(pDS
);
466 /*******************************************************************************
469 static HRESULT
DirectSoundDevice_Create(DirectSoundDevice
** ppDevice
)
471 DirectSoundDevice
* device
;
472 TRACE("(%p)\n", ppDevice
);
474 /* Allocate memory */
475 device
= HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY
,sizeof(DirectSoundDevice
));
476 if (device
== NULL
) {
477 WARN("out of memory\n");
478 return DSERR_OUTOFMEMORY
;
482 device
->priolevel
= DSSCL_NORMAL
;
483 device
->state
= STATE_STOPPED
;
484 device
->speaker_config
= DSSPEAKER_COMBINED(DSSPEAKER_STEREO
, DSSPEAKER_GEOMETRY_WIDE
);
486 /* 3D listener initial parameters */
487 device
->ds3dl
.dwSize
= sizeof(DS3DLISTENER
);
488 device
->ds3dl
.vPosition
.x
= 0.0;
489 device
->ds3dl
.vPosition
.y
= 0.0;
490 device
->ds3dl
.vPosition
.z
= 0.0;
491 device
->ds3dl
.vVelocity
.x
= 0.0;
492 device
->ds3dl
.vVelocity
.y
= 0.0;
493 device
->ds3dl
.vVelocity
.z
= 0.0;
494 device
->ds3dl
.vOrientFront
.x
= 0.0;
495 device
->ds3dl
.vOrientFront
.y
= 0.0;
496 device
->ds3dl
.vOrientFront
.z
= 1.0;
497 device
->ds3dl
.vOrientTop
.x
= 0.0;
498 device
->ds3dl
.vOrientTop
.y
= 1.0;
499 device
->ds3dl
.vOrientTop
.z
= 0.0;
500 device
->ds3dl
.flDistanceFactor
= DS3D_DEFAULTDISTANCEFACTOR
;
501 device
->ds3dl
.flRolloffFactor
= DS3D_DEFAULTROLLOFFFACTOR
;
502 device
->ds3dl
.flDopplerFactor
= DS3D_DEFAULTDOPPLERFACTOR
;
504 device
->guid
= GUID_NULL
;
506 /* Set default wave format (may need it for waveOutOpen) */
507 device
->pwfx
= HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY
,sizeof(WAVEFORMATEXTENSIBLE
));
508 device
->primary_pwfx
= HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY
,sizeof(WAVEFORMATEX
));
509 if (!device
->pwfx
|| !device
->primary_pwfx
) {
510 WARN("out of memory\n");
511 HeapFree(GetProcessHeap(),0,device
->primary_pwfx
);
512 HeapFree(GetProcessHeap(),0,device
->pwfx
);
513 HeapFree(GetProcessHeap(),0,device
);
514 return DSERR_OUTOFMEMORY
;
517 device
->pwfx
->wFormatTag
= WAVE_FORMAT_PCM
;
518 device
->pwfx
->nSamplesPerSec
= 22050;
519 device
->pwfx
->wBitsPerSample
= 8;
520 device
->pwfx
->nChannels
= 2;
521 device
->pwfx
->nBlockAlign
= device
->pwfx
->wBitsPerSample
* device
->pwfx
->nChannels
/ 8;
522 device
->pwfx
->nAvgBytesPerSec
= device
->pwfx
->nSamplesPerSec
* device
->pwfx
->nBlockAlign
;
523 device
->pwfx
->cbSize
= 0;
524 memcpy(device
->primary_pwfx
, device
->pwfx
, sizeof(*device
->pwfx
));
526 InitializeCriticalSection(&(device
->mixlock
));
527 device
->mixlock
.DebugInfo
->Spare
[0] = (DWORD_PTR
)(__FILE__
": DirectSoundDevice.mixlock");
529 RtlInitializeResource(&(device
->buffer_list_lock
));
536 static ULONG
DirectSoundDevice_AddRef(DirectSoundDevice
* device
)
538 ULONG ref
= InterlockedIncrement(&(device
->ref
));
539 TRACE("(%p) ref was %d\n", device
, ref
- 1);
543 ULONG
DirectSoundDevice_Release(DirectSoundDevice
* device
)
546 ULONG ref
= InterlockedDecrement(&(device
->ref
));
547 TRACE("(%p) ref was %u\n", device
, ref
+ 1);
551 SetEvent(device
->sleepev
);
552 CloseHandle(device
->sleepev
);
553 if (device
->thread
) {
554 WaitForSingleObject(device
->thread
, INFINITE
);
555 CloseHandle(device
->thread
);
558 EnterCriticalSection(&DSOUND_renderers_lock
);
559 list_remove(&device
->entry
);
560 LeaveCriticalSection(&DSOUND_renderers_lock
);
562 /* It is allowed to release this object even when buffers are playing */
563 if (device
->buffers
) {
564 WARN("%d secondary buffers not released\n", device
->nrofbuffers
);
565 for( i
=0;i
<device
->nrofbuffers
;i
++)
566 secondarybuffer_destroy(device
->buffers
[i
]);
569 hr
= DSOUND_PrimaryDestroy(device
);
571 WARN("DSOUND_PrimaryDestroy failed\n");
574 IAudioClient_Release(device
->client
);
576 IAudioRenderClient_Release(device
->render
);
578 IAudioClock_Release(device
->clock
);
580 IAudioStreamVolume_Release(device
->volume
);
582 HeapFree(GetProcessHeap(), 0, device
->tmp_buffer
);
583 HeapFree(GetProcessHeap(), 0, device
->mix_buffer
);
584 HeapFree(GetProcessHeap(), 0, device
->buffer
);
585 RtlDeleteResource(&device
->buffer_list_lock
);
586 device
->mixlock
.DebugInfo
->Spare
[0] = 0;
587 DeleteCriticalSection(&device
->mixlock
);
588 HeapFree(GetProcessHeap(),0,device
);
589 TRACE("(%p) released\n", device
);
594 HRESULT
DirectSoundDevice_GetCaps(
595 DirectSoundDevice
* device
,
598 TRACE("(%p,%p)\n",device
,lpDSCaps
);
600 if (device
== NULL
) {
601 WARN("not initialized\n");
602 return DSERR_UNINITIALIZED
;
605 if (lpDSCaps
== NULL
) {
606 WARN("invalid parameter: lpDSCaps = NULL\n");
607 return DSERR_INVALIDPARAM
;
610 /* check if there is enough room */
611 if (lpDSCaps
->dwSize
< sizeof(*lpDSCaps
)) {
612 WARN("invalid parameter: lpDSCaps->dwSize = %d\n", lpDSCaps
->dwSize
);
613 return DSERR_INVALIDPARAM
;
616 lpDSCaps
->dwFlags
= device
->drvcaps
.dwFlags
;
617 if (TRACE_ON(dsound
)) {
618 TRACE("(flags=0x%08x:\n",lpDSCaps
->dwFlags
);
619 _dump_DSCAPS(lpDSCaps
->dwFlags
);
622 lpDSCaps
->dwMinSecondarySampleRate
= device
->drvcaps
.dwMinSecondarySampleRate
;
623 lpDSCaps
->dwMaxSecondarySampleRate
= device
->drvcaps
.dwMaxSecondarySampleRate
;
624 lpDSCaps
->dwPrimaryBuffers
= device
->drvcaps
.dwPrimaryBuffers
;
625 lpDSCaps
->dwMaxHwMixingAllBuffers
= device
->drvcaps
.dwMaxHwMixingAllBuffers
;
626 lpDSCaps
->dwMaxHwMixingStaticBuffers
= device
->drvcaps
.dwMaxHwMixingStaticBuffers
;
627 lpDSCaps
->dwMaxHwMixingStreamingBuffers
= device
->drvcaps
.dwMaxHwMixingStreamingBuffers
;
628 lpDSCaps
->dwFreeHwMixingAllBuffers
= device
->drvcaps
.dwFreeHwMixingAllBuffers
;
629 lpDSCaps
->dwFreeHwMixingStaticBuffers
= device
->drvcaps
.dwFreeHwMixingStaticBuffers
;
630 lpDSCaps
->dwFreeHwMixingStreamingBuffers
= device
->drvcaps
.dwFreeHwMixingStreamingBuffers
;
631 lpDSCaps
->dwMaxHw3DAllBuffers
= device
->drvcaps
.dwMaxHw3DAllBuffers
;
632 lpDSCaps
->dwMaxHw3DStaticBuffers
= device
->drvcaps
.dwMaxHw3DStaticBuffers
;
633 lpDSCaps
->dwMaxHw3DStreamingBuffers
= device
->drvcaps
.dwMaxHw3DStreamingBuffers
;
634 lpDSCaps
->dwFreeHw3DAllBuffers
= device
->drvcaps
.dwFreeHw3DAllBuffers
;
635 lpDSCaps
->dwFreeHw3DStaticBuffers
= device
->drvcaps
.dwFreeHw3DStaticBuffers
;
636 lpDSCaps
->dwFreeHw3DStreamingBuffers
= device
->drvcaps
.dwFreeHw3DStreamingBuffers
;
637 lpDSCaps
->dwTotalHwMemBytes
= device
->drvcaps
.dwTotalHwMemBytes
;
638 lpDSCaps
->dwFreeHwMemBytes
= device
->drvcaps
.dwFreeHwMemBytes
;
639 lpDSCaps
->dwMaxContigFreeHwMemBytes
= device
->drvcaps
.dwMaxContigFreeHwMemBytes
;
640 lpDSCaps
->dwUnlockTransferRateHwBuffers
= device
->drvcaps
.dwUnlockTransferRateHwBuffers
;
641 lpDSCaps
->dwPlayCpuOverheadSwBuffers
= device
->drvcaps
.dwPlayCpuOverheadSwBuffers
;
646 BOOL
DSOUND_check_supported(IAudioClient
*client
, DWORD rate
,
647 DWORD depth
, WORD channels
)
649 WAVEFORMATEX fmt
, *junk
;
652 fmt
.wFormatTag
= WAVE_FORMAT_PCM
;
653 fmt
.nChannels
= channels
;
654 fmt
.nSamplesPerSec
= rate
;
655 fmt
.wBitsPerSample
= depth
;
656 fmt
.nBlockAlign
= (channels
* depth
) / 8;
657 fmt
.nAvgBytesPerSec
= rate
* fmt
.nBlockAlign
;
660 hr
= IAudioClient_IsFormatSupported(client
, AUDCLNT_SHAREMODE_SHARED
, &fmt
, &junk
);
667 UINT
DSOUND_create_timer(LPTIMECALLBACK cb
, DWORD_PTR user
)
669 UINT triggertime
= DS_TIME_DEL
, res
= DS_TIME_RES
, id
;
672 timeGetDevCaps(&time
, sizeof(TIMECAPS
));
673 TRACE("Minimum timer resolution: %u, max timer: %u\n", time
.wPeriodMin
, time
.wPeriodMax
);
674 if (triggertime
< time
.wPeriodMin
)
675 triggertime
= time
.wPeriodMin
;
676 if (res
< time
.wPeriodMin
)
677 res
= time
.wPeriodMin
;
678 if (timeBeginPeriod(res
) == TIMERR_NOCANDO
)
679 WARN("Could not set minimum resolution, don't expect sound\n");
680 id
= timeSetEvent(triggertime
, res
, cb
, user
, TIME_PERIODIC
| TIME_KILL_SYNCHRONOUS
);
683 WARN("Timer not created! Retrying without TIME_KILL_SYNCHRONOUS\n");
684 id
= timeSetEvent(triggertime
, res
, cb
, user
, TIME_PERIODIC
);
686 ERR("Could not create timer, sound playback will not occur\n");
691 HRESULT
DirectSoundDevice_Initialize(DirectSoundDevice
** ppDevice
, LPCGUID lpcGUID
)
695 DirectSoundDevice
*device
;
698 TRACE("(%p,%s)\n",ppDevice
,debugstr_guid(lpcGUID
));
700 if (*ppDevice
!= NULL
) {
701 WARN("already initialized\n");
702 return DSERR_ALREADYINITIALIZED
;
705 /* Default device? */
706 if (!lpcGUID
|| IsEqualGUID(lpcGUID
, &GUID_NULL
))
707 lpcGUID
= &DSDEVID_DefaultPlayback
;
709 if(IsEqualGUID(lpcGUID
, &DSDEVID_DefaultCapture
) ||
710 IsEqualGUID(lpcGUID
, &DSDEVID_DefaultVoiceCapture
))
711 return DSERR_NODRIVER
;
713 if (GetDeviceID(lpcGUID
, &devGUID
) != DS_OK
) {
714 WARN("invalid parameter: lpcGUID\n");
715 return DSERR_INVALIDPARAM
;
718 hr
= get_mmdevice(eRender
, &devGUID
, &mmdevice
);
722 EnterCriticalSection(&DSOUND_renderers_lock
);
724 LIST_FOR_EACH_ENTRY(device
, &DSOUND_renderers
, DirectSoundDevice
, entry
){
725 if(IsEqualGUID(&device
->guid
, &devGUID
)){
726 IMMDevice_Release(mmdevice
);
727 DirectSoundDevice_AddRef(device
);
729 LeaveCriticalSection(&DSOUND_renderers_lock
);
734 hr
= DirectSoundDevice_Create(&device
);
736 WARN("DirectSoundDevice_Create failed\n");
737 IMMDevice_Release(mmdevice
);
738 LeaveCriticalSection(&DSOUND_renderers_lock
);
742 device
->mmdevice
= mmdevice
;
743 device
->guid
= devGUID
;
744 device
->sleepev
= CreateEventW(0, 0, 0, 0);
746 hr
= DSOUND_ReopenDevice(device
, FALSE
);
749 HeapFree(GetProcessHeap(), 0, device
);
750 LeaveCriticalSection(&DSOUND_renderers_lock
);
751 IMMDevice_Release(mmdevice
);
752 WARN("DSOUND_ReopenDevice failed: %08x\n", hr
);
756 ZeroMemory(&device
->drvcaps
, sizeof(device
->drvcaps
));
758 if(DSOUND_check_supported(device
->client
, 11025, 8, 1) ||
759 DSOUND_check_supported(device
->client
, 22050, 8, 1) ||
760 DSOUND_check_supported(device
->client
, 44100, 8, 1) ||
761 DSOUND_check_supported(device
->client
, 48000, 8, 1) ||
762 DSOUND_check_supported(device
->client
, 96000, 8, 1))
763 device
->drvcaps
.dwFlags
|= DSCAPS_PRIMARY8BIT
| DSCAPS_PRIMARYMONO
;
765 if(DSOUND_check_supported(device
->client
, 11025, 16, 1) ||
766 DSOUND_check_supported(device
->client
, 22050, 16, 1) ||
767 DSOUND_check_supported(device
->client
, 44100, 16, 1) ||
768 DSOUND_check_supported(device
->client
, 48000, 16, 1) ||
769 DSOUND_check_supported(device
->client
, 96000, 16, 1))
770 device
->drvcaps
.dwFlags
|= DSCAPS_PRIMARY16BIT
| DSCAPS_PRIMARYMONO
;
772 if(DSOUND_check_supported(device
->client
, 11025, 8, 2) ||
773 DSOUND_check_supported(device
->client
, 22050, 8, 2) ||
774 DSOUND_check_supported(device
->client
, 44100, 8, 2) ||
775 DSOUND_check_supported(device
->client
, 48000, 8, 2) ||
776 DSOUND_check_supported(device
->client
, 96000, 8, 2))
777 device
->drvcaps
.dwFlags
|= DSCAPS_PRIMARY8BIT
| DSCAPS_PRIMARYSTEREO
;
779 if(DSOUND_check_supported(device
->client
, 11025, 16, 2) ||
780 DSOUND_check_supported(device
->client
, 22050, 16, 2) ||
781 DSOUND_check_supported(device
->client
, 44100, 16, 2) ||
782 DSOUND_check_supported(device
->client
, 48000, 16, 2) ||
783 DSOUND_check_supported(device
->client
, 96000, 16, 2))
784 device
->drvcaps
.dwFlags
|= DSCAPS_PRIMARY16BIT
| DSCAPS_PRIMARYSTEREO
;
786 /* the dsound mixer supports all of the following */
787 device
->drvcaps
.dwFlags
|= DSCAPS_SECONDARY8BIT
| DSCAPS_SECONDARY16BIT
;
788 device
->drvcaps
.dwFlags
|= DSCAPS_SECONDARYMONO
| DSCAPS_SECONDARYSTEREO
;
789 device
->drvcaps
.dwFlags
|= DSCAPS_CONTINUOUSRATE
;
791 device
->drvcaps
.dwPrimaryBuffers
= 1;
792 device
->drvcaps
.dwMinSecondarySampleRate
= DSBFREQUENCY_MIN
;
793 device
->drvcaps
.dwMaxSecondarySampleRate
= DSBFREQUENCY_MAX
;
794 device
->drvcaps
.dwMaxHwMixingAllBuffers
= 1;
795 device
->drvcaps
.dwMaxHwMixingStaticBuffers
= 1;
796 device
->drvcaps
.dwMaxHwMixingStreamingBuffers
= 1;
798 ZeroMemory(&device
->volpan
, sizeof(device
->volpan
));
800 hr
= DSOUND_PrimaryCreate(device
);
802 device
->thread
= CreateThread(0, 0, DSOUND_mixthread
, device
, 0, 0);
803 SetThreadPriority(device
->thread
, THREAD_PRIORITY_TIME_CRITICAL
);
805 WARN("DSOUND_PrimaryCreate failed: %08x\n", hr
);
808 list_add_tail(&DSOUND_renderers
, &device
->entry
);
810 LeaveCriticalSection(&DSOUND_renderers_lock
);
815 HRESULT
DirectSoundDevice_CreateSoundBuffer(
816 DirectSoundDevice
* device
,
817 LPCDSBUFFERDESC dsbd
,
818 LPLPDIRECTSOUNDBUFFER ppdsb
,
822 HRESULT hres
= DS_OK
;
823 TRACE("(%p,%p,%p,%p)\n",device
,dsbd
,ppdsb
,lpunk
);
825 if (device
== NULL
) {
826 WARN("not initialized\n");
827 return DSERR_UNINITIALIZED
;
831 WARN("invalid parameter: dsbd == NULL\n");
832 return DSERR_INVALIDPARAM
;
835 if (dsbd
->dwSize
!= sizeof(DSBUFFERDESC
) &&
836 dsbd
->dwSize
!= sizeof(DSBUFFERDESC1
)) {
837 WARN("invalid parameter: dsbd\n");
838 return DSERR_INVALIDPARAM
;
842 WARN("invalid parameter: ppdsb == NULL\n");
843 return DSERR_INVALIDPARAM
;
847 if (TRACE_ON(dsound
)) {
848 TRACE("(structsize=%d)\n",dsbd
->dwSize
);
849 TRACE("(flags=0x%08x:\n",dsbd
->dwFlags
);
850 _dump_DSBCAPS(dsbd
->dwFlags
);
852 TRACE("(bufferbytes=%d)\n",dsbd
->dwBufferBytes
);
853 TRACE("(lpwfxFormat=%p)\n",dsbd
->lpwfxFormat
);
856 if (dsbd
->dwFlags
& DSBCAPS_LOCHARDWARE
&&
857 !(dsbd
->dwFlags
& DSBCAPS_PRIMARYBUFFER
)) {
858 TRACE("LOCHARDWARE is not supported, returning E_NOTIMPL\n");
862 if (dsbd
->dwFlags
& DSBCAPS_PRIMARYBUFFER
) {
863 if (dsbd
->lpwfxFormat
!= NULL
) {
864 WARN("invalid parameter: dsbd->lpwfxFormat must be NULL for "
866 return DSERR_INVALIDPARAM
;
869 if (device
->primary
) {
870 WARN("Primary Buffer already created\n");
871 IDirectSoundBuffer_AddRef((LPDIRECTSOUNDBUFFER8
)(device
->primary
));
872 *ppdsb
= (LPDIRECTSOUNDBUFFER
)(device
->primary
);
874 hres
= primarybuffer_create(device
, &device
->primary
, dsbd
);
875 if (device
->primary
) {
876 *ppdsb
= (IDirectSoundBuffer
*)&device
->primary
->IDirectSoundBuffer8_iface
;
877 device
->primary
->dsbd
.dwFlags
&= ~(DSBCAPS_LOCHARDWARE
| DSBCAPS_LOCSOFTWARE
);
878 device
->primary
->dsbd
.dwFlags
|= DSBCAPS_LOCSOFTWARE
;
880 WARN("primarybuffer_create() failed\n");
883 IDirectSoundBufferImpl
* dsb
;
884 WAVEFORMATEXTENSIBLE
*pwfxe
;
886 if (dsbd
->lpwfxFormat
== NULL
) {
887 WARN("invalid parameter: dsbd->lpwfxFormat can't be NULL for "
888 "secondary buffer\n");
889 return DSERR_INVALIDPARAM
;
891 pwfxe
= (WAVEFORMATEXTENSIBLE
*)dsbd
->lpwfxFormat
;
893 if (pwfxe
->Format
.wBitsPerSample
!= 16 && pwfxe
->Format
.wBitsPerSample
!= 8 && pwfxe
->Format
.wFormatTag
!= WAVE_FORMAT_EXTENSIBLE
)
895 WARN("wBitsPerSample=%d needs a WAVEFORMATEXTENSIBLE\n", dsbd
->lpwfxFormat
->wBitsPerSample
);
896 return DSERR_CONTROLUNAVAIL
;
898 if (pwfxe
->Format
.wFormatTag
== WAVE_FORMAT_EXTENSIBLE
)
900 /* check if cbSize is at least 22 bytes */
901 if (pwfxe
->Format
.cbSize
< (sizeof(WAVEFORMATEXTENSIBLE
) - sizeof(WAVEFORMATEX
)))
903 WARN("Too small a cbSize %u\n", pwfxe
->Format
.cbSize
);
904 return DSERR_INVALIDPARAM
;
907 /* cbSize should be 22 bytes, with one possible exception */
908 if (pwfxe
->Format
.cbSize
> (sizeof(WAVEFORMATEXTENSIBLE
) - sizeof(WAVEFORMATEX
)) &&
909 !((IsEqualGUID(&pwfxe
->SubFormat
, &KSDATAFORMAT_SUBTYPE_PCM
) || IsEqualGUID(&pwfxe
->SubFormat
, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
)) &&
910 pwfxe
->Format
.cbSize
== sizeof(WAVEFORMATEXTENSIBLE
)))
912 WARN("Too big a cbSize %u\n", pwfxe
->Format
.cbSize
);
913 return DSERR_CONTROLUNAVAIL
;
916 if ((!IsEqualGUID(&pwfxe
->SubFormat
, &KSDATAFORMAT_SUBTYPE_PCM
)) && (!IsEqualGUID(&pwfxe
->SubFormat
, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
)))
918 if (!IsEqualGUID(&pwfxe
->SubFormat
, &GUID_NULL
))
919 FIXME("SubFormat %s not supported right now.\n", debugstr_guid(&pwfxe
->SubFormat
));
920 return DSERR_INVALIDPARAM
;
922 if (pwfxe
->Samples
.wValidBitsPerSample
> dsbd
->lpwfxFormat
->wBitsPerSample
)
924 WARN("Samples.wValidBitsPerSample(%d) > Format.wBitsPerSample (%d)\n", pwfxe
->Samples
.wValidBitsPerSample
, pwfxe
->Format
.wBitsPerSample
);
925 return DSERR_INVALIDPARAM
;
927 if (pwfxe
->Samples
.wValidBitsPerSample
&& pwfxe
->Samples
.wValidBitsPerSample
< dsbd
->lpwfxFormat
->wBitsPerSample
)
929 FIXME("Non-packed formats not supported right now: %d/%d\n", pwfxe
->Samples
.wValidBitsPerSample
, dsbd
->lpwfxFormat
->wBitsPerSample
);
930 return DSERR_CONTROLUNAVAIL
;
934 TRACE("(formattag=0x%04x,chans=%d,samplerate=%d,"
935 "bytespersec=%d,blockalign=%d,bitspersamp=%d,cbSize=%d)\n",
936 dsbd
->lpwfxFormat
->wFormatTag
, dsbd
->lpwfxFormat
->nChannels
,
937 dsbd
->lpwfxFormat
->nSamplesPerSec
,
938 dsbd
->lpwfxFormat
->nAvgBytesPerSec
,
939 dsbd
->lpwfxFormat
->nBlockAlign
,
940 dsbd
->lpwfxFormat
->wBitsPerSample
, dsbd
->lpwfxFormat
->cbSize
);
942 if (from8
&& (dsbd
->dwFlags
& DSBCAPS_CTRL3D
) && (dsbd
->lpwfxFormat
->nChannels
!= 1)) {
943 WARN("invalid parameter: 3D buffer format must be mono\n");
944 return DSERR_INVALIDPARAM
;
947 hres
= IDirectSoundBufferImpl_Create(device
, &dsb
, dsbd
);
949 *ppdsb
= (IDirectSoundBuffer
*)&dsb
->IDirectSoundBuffer8_iface
;
951 WARN("IDirectSoundBufferImpl_Create failed\n");
957 HRESULT
DirectSoundDevice_DuplicateSoundBuffer(
958 DirectSoundDevice
* device
,
959 LPDIRECTSOUNDBUFFER psb
,
960 LPLPDIRECTSOUNDBUFFER ppdsb
)
962 HRESULT hres
= DS_OK
;
963 IDirectSoundBufferImpl
* dsb
;
964 TRACE("(%p,%p,%p)\n",device
,psb
,ppdsb
);
966 if (device
== NULL
) {
967 WARN("not initialized\n");
968 return DSERR_UNINITIALIZED
;
972 WARN("invalid parameter: psb == NULL\n");
973 return DSERR_INVALIDPARAM
;
977 WARN("invalid parameter: ppdsb == NULL\n");
978 return DSERR_INVALIDPARAM
;
981 /* make sure we have a secondary buffer */
982 if (psb
== (IDirectSoundBuffer
*)&device
->primary
->IDirectSoundBuffer8_iface
) {
983 WARN("trying to duplicate primary buffer\n");
985 return DSERR_INVALIDCALL
;
988 /* duplicate the actual buffer implementation */
989 hres
= IDirectSoundBufferImpl_Duplicate(device
, &dsb
, (IDirectSoundBufferImpl
*)psb
);
991 *ppdsb
= (IDirectSoundBuffer
*)&dsb
->IDirectSoundBuffer8_iface
;
993 WARN("IDirectSoundBufferImpl_Duplicate failed\n");
998 HRESULT
DirectSoundDevice_SetCooperativeLevel(
999 DirectSoundDevice
* device
,
1005 TRACE("(%p,%p,%s)\n",device
,hwnd
,dumpCooperativeLevel(level
));
1007 if (device
== NULL
) {
1008 WARN("not initialized\n");
1009 return DSERR_UNINITIALIZED
;
1012 if (level
==DSSCL_PRIORITY
|| level
==DSSCL_EXCLUSIVE
) {
1013 WARN("level=%s not fully supported\n",
1014 level
==DSSCL_PRIORITY
? "DSSCL_PRIORITY" : "DSSCL_EXCLUSIVE");
1017 RtlAcquireResourceExclusive(&device
->buffer_list_lock
, TRUE
);
1018 EnterCriticalSection(&device
->mixlock
);
1019 oldlevel
= device
->priolevel
;
1020 device
->priolevel
= level
;
1021 if ((level
== DSSCL_WRITEPRIMARY
) != (oldlevel
== DSSCL_WRITEPRIMARY
)) {
1022 hr
= DSOUND_ReopenDevice(device
, 0);
1024 device
->priolevel
= oldlevel
;
1026 DSOUND_PrimaryOpen(device
);
1028 LeaveCriticalSection(&device
->mixlock
);
1029 RtlReleaseResource(&device
->buffer_list_lock
);
1033 HRESULT
DirectSoundDevice_Compact(
1034 DirectSoundDevice
* device
)
1036 TRACE("(%p)\n", device
);
1038 if (device
== NULL
) {
1039 WARN("not initialized\n");
1040 return DSERR_UNINITIALIZED
;
1043 if (device
->priolevel
< DSSCL_PRIORITY
) {
1044 WARN("incorrect priority level\n");
1045 return DSERR_PRIOLEVELNEEDED
;
1051 HRESULT
DirectSoundDevice_GetSpeakerConfig(
1052 DirectSoundDevice
* device
,
1053 LPDWORD lpdwSpeakerConfig
)
1055 TRACE("(%p, %p)\n", device
, lpdwSpeakerConfig
);
1057 if (device
== NULL
) {
1058 WARN("not initialized\n");
1059 return DSERR_UNINITIALIZED
;
1062 if (lpdwSpeakerConfig
== NULL
) {
1063 WARN("invalid parameter: lpdwSpeakerConfig == NULL\n");
1064 return DSERR_INVALIDPARAM
;
1067 WARN("not fully functional\n");
1068 *lpdwSpeakerConfig
= device
->speaker_config
;
1072 HRESULT
DirectSoundDevice_SetSpeakerConfig(
1073 DirectSoundDevice
* device
,
1076 TRACE("(%p,0x%08x)\n",device
,config
);
1078 if (device
== NULL
) {
1079 WARN("not initialized\n");
1080 return DSERR_UNINITIALIZED
;
1083 device
->speaker_config
= config
;
1084 WARN("not fully functional\n");
1088 HRESULT
DirectSoundDevice_VerifyCertification(
1089 DirectSoundDevice
* device
,
1090 LPDWORD pdwCertified
)
1092 TRACE("(%p, %p)\n",device
,pdwCertified
);
1094 if (device
== NULL
) {
1095 WARN("not initialized\n");
1096 return DSERR_UNINITIALIZED
;
1099 if (device
->drvcaps
.dwFlags
& DSCAPS_CERTIFIED
)
1100 *pdwCertified
= DS_CERTIFIED
;
1102 *pdwCertified
= DS_UNCERTIFIED
;
1108 * Add secondary buffer to buffer list.
1109 * Gets exclusive access to buffer for writing.
1111 HRESULT
DirectSoundDevice_AddBuffer(
1112 DirectSoundDevice
* device
,
1113 IDirectSoundBufferImpl
* pDSB
)
1115 IDirectSoundBufferImpl
**newbuffers
;
1118 TRACE("(%p, %p)\n", device
, pDSB
);
1120 RtlAcquireResourceExclusive(&(device
->buffer_list_lock
), TRUE
);
1122 if (device
->buffers
)
1123 newbuffers
= HeapReAlloc(GetProcessHeap(),0,device
->buffers
,sizeof(IDirectSoundBufferImpl
*)*(device
->nrofbuffers
+1));
1125 newbuffers
= HeapAlloc(GetProcessHeap(),0,sizeof(IDirectSoundBufferImpl
*)*(device
->nrofbuffers
+1));
1128 device
->buffers
= newbuffers
;
1129 device
->buffers
[device
->nrofbuffers
] = pDSB
;
1130 device
->nrofbuffers
++;
1131 TRACE("buffer count is now %d\n", device
->nrofbuffers
);
1133 ERR("out of memory for buffer list! Current buffer count is %d\n", device
->nrofbuffers
);
1134 hr
= DSERR_OUTOFMEMORY
;
1137 RtlReleaseResource(&(device
->buffer_list_lock
));
1143 * Remove secondary buffer from buffer list.
1144 * Gets exclusive access to buffer for writing.
1146 HRESULT
DirectSoundDevice_RemoveBuffer(
1147 DirectSoundDevice
* device
,
1148 IDirectSoundBufferImpl
* pDSB
)
1153 TRACE("(%p, %p)\n", device
, pDSB
);
1155 RtlAcquireResourceExclusive(&(device
->buffer_list_lock
), TRUE
);
1157 for (i
= 0; i
< device
->nrofbuffers
; i
++)
1158 if (device
->buffers
[i
] == pDSB
)
1161 if (i
< device
->nrofbuffers
) {
1162 /* Put the last buffer of the list in the (now empty) position */
1163 device
->buffers
[i
] = device
->buffers
[device
->nrofbuffers
- 1];
1164 device
->nrofbuffers
--;
1165 device
->buffers
= HeapReAlloc(GetProcessHeap(),0,device
->buffers
,sizeof(LPDIRECTSOUNDBUFFER8
)*device
->nrofbuffers
);
1166 TRACE("buffer count is now %d\n", device
->nrofbuffers
);
1169 if (device
->nrofbuffers
== 0) {
1170 HeapFree(GetProcessHeap(),0,device
->buffers
);
1171 device
->buffers
= NULL
;
1174 RtlReleaseResource(&(device
->buffer_list_lock
));