winspool: Close read end of pipe in parent so we can detect child termination.
[wine/wine-gecko.git] / dlls / dsound / dsound.c
blobac85ba225366b23ba07743083671bf23e87d1d29
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 <assert.h>
24 #include <stdarg.h>
25 #include <stdio.h>
27 #define COBJMACROS
28 #define NONAMELESSSTRUCT
29 #define NONAMELESSUNION
30 #include "windef.h"
31 #include "winbase.h"
32 #include "winuser.h"
33 #include "winternl.h"
34 #include "mmddk.h"
35 #include "wingdi.h"
36 #include "mmreg.h"
37 #include "ks.h"
38 #include "ksmedia.h"
39 #include "wine/debug.h"
40 #include "dsound.h"
41 #include "dsound_private.h"
43 WINE_DEFAULT_DEBUG_CHANNEL(dsound);
45 typedef struct IDirectSoundImpl {
46 IUnknown IUnknown_inner;
47 IDirectSound8 IDirectSound8_iface;
48 IUnknown *outer_unk; /* internal */
49 LONG ref, refds, numIfaces;
50 DirectSoundDevice *device;
51 BOOL has_ds8;
52 } IDirectSoundImpl;
54 static const char * dumpCooperativeLevel(DWORD level)
56 #define LE(x) case x: return #x
57 switch (level) {
58 LE(DSSCL_NORMAL);
59 LE(DSSCL_PRIORITY);
60 LE(DSSCL_EXCLUSIVE);
61 LE(DSSCL_WRITEPRIMARY);
63 #undef LE
64 return wine_dbg_sprintf("Unknown(%08x)", level);
67 static void _dump_DSCAPS(DWORD xmask) {
68 struct {
69 DWORD mask;
70 const char *name;
71 } flags[] = {
72 #define FE(x) { x, #x },
73 FE(DSCAPS_PRIMARYMONO)
74 FE(DSCAPS_PRIMARYSTEREO)
75 FE(DSCAPS_PRIMARY8BIT)
76 FE(DSCAPS_PRIMARY16BIT)
77 FE(DSCAPS_CONTINUOUSRATE)
78 FE(DSCAPS_EMULDRIVER)
79 FE(DSCAPS_CERTIFIED)
80 FE(DSCAPS_SECONDARYMONO)
81 FE(DSCAPS_SECONDARYSTEREO)
82 FE(DSCAPS_SECONDARY8BIT)
83 FE(DSCAPS_SECONDARY16BIT)
84 #undef FE
86 unsigned int i;
88 for (i=0;i<sizeof(flags)/sizeof(flags[0]);i++)
89 if ((flags[i].mask & xmask) == flags[i].mask)
90 TRACE("%s ",flags[i].name);
93 static void _dump_DSBCAPS(DWORD xmask) {
94 struct {
95 DWORD mask;
96 const char *name;
97 } flags[] = {
98 #define FE(x) { x, #x },
99 FE(DSBCAPS_PRIMARYBUFFER)
100 FE(DSBCAPS_STATIC)
101 FE(DSBCAPS_LOCHARDWARE)
102 FE(DSBCAPS_LOCSOFTWARE)
103 FE(DSBCAPS_CTRL3D)
104 FE(DSBCAPS_CTRLFREQUENCY)
105 FE(DSBCAPS_CTRLPAN)
106 FE(DSBCAPS_CTRLVOLUME)
107 FE(DSBCAPS_CTRLPOSITIONNOTIFY)
108 FE(DSBCAPS_STICKYFOCUS)
109 FE(DSBCAPS_GLOBALFOCUS)
110 FE(DSBCAPS_GETCURRENTPOSITION2)
111 FE(DSBCAPS_MUTE3DATMAXDISTANCE)
112 #undef FE
114 unsigned int i;
116 for (i=0;i<sizeof(flags)/sizeof(flags[0]);i++)
117 if ((flags[i].mask & xmask) == flags[i].mask)
118 TRACE("%s ",flags[i].name);
121 static void directsound_destroy(IDirectSoundImpl *This)
123 if (This->device)
124 DirectSoundDevice_Release(This->device);
125 HeapFree(GetProcessHeap(),0,This);
126 TRACE("(%p) released\n", This);
129 /*******************************************************************************
130 * IUnknown Implementation for DirectSound
132 static inline IDirectSoundImpl *impl_from_IUnknown(IUnknown *iface)
134 return CONTAINING_RECORD(iface, IDirectSoundImpl, IUnknown_inner);
137 static HRESULT WINAPI IUnknownImpl_QueryInterface(IUnknown *iface, REFIID riid, void **ppv)
139 IDirectSoundImpl *This = impl_from_IUnknown(iface);
141 TRACE("(%p,%s,%p)\n", This, debugstr_guid(riid), ppv);
143 if (!ppv) {
144 WARN("invalid parameter\n");
145 return E_INVALIDARG;
147 *ppv = NULL;
149 if (IsEqualIID(riid, &IID_IUnknown))
150 *ppv = &This->IUnknown_inner;
151 else if (IsEqualIID(riid, &IID_IDirectSound) ||
152 (IsEqualIID(riid, &IID_IDirectSound8) && This->has_ds8))
153 *ppv = &This->IDirectSound8_iface;
154 else {
155 WARN("unknown IID %s\n", debugstr_guid(riid));
156 return E_NOINTERFACE;
159 IUnknown_AddRef((IUnknown*)*ppv);
160 return S_OK;
163 static ULONG WINAPI IUnknownImpl_AddRef(IUnknown *iface)
165 IDirectSoundImpl *This = impl_from_IUnknown(iface);
166 ULONG ref = InterlockedIncrement(&This->ref);
168 TRACE("(%p) ref=%d\n", This, ref);
170 if(ref == 1)
171 InterlockedIncrement(&This->numIfaces);
173 return ref;
176 static ULONG WINAPI IUnknownImpl_Release(IUnknown *iface)
178 IDirectSoundImpl *This = impl_from_IUnknown(iface);
179 ULONG ref = InterlockedDecrement(&This->ref);
181 TRACE("(%p) ref=%d\n", This, ref);
183 if (!ref && !InterlockedDecrement(&This->numIfaces))
184 directsound_destroy(This);
186 return ref;
189 static const IUnknownVtbl unk_vtbl =
191 IUnknownImpl_QueryInterface,
192 IUnknownImpl_AddRef,
193 IUnknownImpl_Release
196 /*******************************************************************************
197 * IDirectSound and IDirectSound8 Implementation
199 static inline IDirectSoundImpl *impl_from_IDirectSound8(IDirectSound8 *iface)
201 return CONTAINING_RECORD(iface, IDirectSoundImpl, IDirectSound8_iface);
204 static HRESULT WINAPI IDirectSound8Impl_QueryInterface(IDirectSound8 *iface, REFIID riid,
205 void **ppv)
207 IDirectSoundImpl *This = impl_from_IDirectSound8(iface);
208 TRACE("(%p,%s,%p)\n", This, debugstr_guid(riid), ppv);
209 return IUnknown_QueryInterface(This->outer_unk, riid, ppv);
212 static ULONG WINAPI IDirectSound8Impl_AddRef(IDirectSound8 *iface)
214 IDirectSoundImpl *This = impl_from_IDirectSound8(iface);
215 ULONG ref = InterlockedIncrement(&This->refds);
217 TRACE("(%p) refds=%d\n", This, ref);
219 if(ref == 1)
220 InterlockedIncrement(&This->numIfaces);
222 return ref;
225 static ULONG WINAPI IDirectSound8Impl_Release(IDirectSound8 *iface)
227 IDirectSoundImpl *This = impl_from_IDirectSound8(iface);
228 ULONG ref = InterlockedDecrement(&(This->refds));
230 TRACE("(%p) refds=%d\n", This, ref);
232 if (!ref && !InterlockedDecrement(&This->numIfaces))
233 directsound_destroy(This);
235 return ref;
238 static HRESULT WINAPI IDirectSound8Impl_CreateSoundBuffer(IDirectSound8 *iface,
239 const DSBUFFERDESC *dsbd, IDirectSoundBuffer **ppdsb, IUnknown *lpunk)
241 IDirectSoundImpl *This = impl_from_IDirectSound8(iface);
242 TRACE("(%p,%p,%p,%p)\n", This, dsbd, ppdsb, lpunk);
243 return DirectSoundDevice_CreateSoundBuffer(This->device, dsbd, ppdsb, lpunk, This->has_ds8);
246 static HRESULT WINAPI IDirectSound8Impl_GetCaps(IDirectSound8 *iface, DSCAPS *dscaps)
248 IDirectSoundImpl *This = impl_from_IDirectSound8(iface);
250 TRACE("(%p, %p)\n", This, dscaps);
252 if (!This->device) {
253 WARN("not initialized\n");
254 return DSERR_UNINITIALIZED;
256 if (!dscaps) {
257 WARN("invalid parameter: dscaps = NULL\n");
258 return DSERR_INVALIDPARAM;
260 if (dscaps->dwSize < sizeof(*dscaps)) {
261 WARN("invalid parameter: dscaps->dwSize = %d\n", dscaps->dwSize);
262 return DSERR_INVALIDPARAM;
265 dscaps->dwFlags = This->device->drvcaps.dwFlags;
266 dscaps->dwMinSecondarySampleRate = This->device->drvcaps.dwMinSecondarySampleRate;
267 dscaps->dwMaxSecondarySampleRate = This->device->drvcaps.dwMaxSecondarySampleRate;
268 dscaps->dwPrimaryBuffers = This->device->drvcaps.dwPrimaryBuffers;
269 dscaps->dwMaxHwMixingAllBuffers = This->device->drvcaps.dwMaxHwMixingAllBuffers;
270 dscaps->dwMaxHwMixingStaticBuffers = This->device->drvcaps.dwMaxHwMixingStaticBuffers;
271 dscaps->dwMaxHwMixingStreamingBuffers = This->device->drvcaps.dwMaxHwMixingStreamingBuffers;
272 dscaps->dwFreeHwMixingAllBuffers = This->device->drvcaps.dwFreeHwMixingAllBuffers;
273 dscaps->dwFreeHwMixingStaticBuffers = This->device->drvcaps.dwFreeHwMixingStaticBuffers;
274 dscaps->dwFreeHwMixingStreamingBuffers = This->device->drvcaps.dwFreeHwMixingStreamingBuffers;
275 dscaps->dwMaxHw3DAllBuffers = This->device->drvcaps.dwMaxHw3DAllBuffers;
276 dscaps->dwMaxHw3DStaticBuffers = This->device->drvcaps.dwMaxHw3DStaticBuffers;
277 dscaps->dwMaxHw3DStreamingBuffers = This->device->drvcaps.dwMaxHw3DStreamingBuffers;
278 dscaps->dwFreeHw3DAllBuffers = This->device->drvcaps.dwFreeHw3DAllBuffers;
279 dscaps->dwFreeHw3DStaticBuffers = This->device->drvcaps.dwFreeHw3DStaticBuffers;
280 dscaps->dwFreeHw3DStreamingBuffers = This->device->drvcaps.dwFreeHw3DStreamingBuffers;
281 dscaps->dwTotalHwMemBytes = This->device->drvcaps.dwTotalHwMemBytes;
282 dscaps->dwFreeHwMemBytes = This->device->drvcaps.dwFreeHwMemBytes;
283 dscaps->dwMaxContigFreeHwMemBytes = This->device->drvcaps.dwMaxContigFreeHwMemBytes;
284 dscaps->dwUnlockTransferRateHwBuffers = This->device->drvcaps.dwUnlockTransferRateHwBuffers;
285 dscaps->dwPlayCpuOverheadSwBuffers = This->device->drvcaps.dwPlayCpuOverheadSwBuffers;
287 if (TRACE_ON(dsound)) {
288 TRACE("(flags=0x%08x:\n", dscaps->dwFlags);
289 _dump_DSCAPS(dscaps->dwFlags);
290 TRACE(")\n");
293 return DS_OK;
296 static HRESULT WINAPI IDirectSound8Impl_DuplicateSoundBuffer(IDirectSound8 *iface,
297 IDirectSoundBuffer *psb, IDirectSoundBuffer **ppdsb)
299 IDirectSoundImpl *This = impl_from_IDirectSound8(iface);
300 TRACE("(%p,%p,%p)\n", This, psb, ppdsb);
301 return DirectSoundDevice_DuplicateSoundBuffer(This->device, psb, ppdsb);
304 static HRESULT WINAPI IDirectSound8Impl_SetCooperativeLevel(IDirectSound8 *iface, HWND hwnd,
305 DWORD level)
307 IDirectSoundImpl *This = impl_from_IDirectSound8(iface);
308 DirectSoundDevice *device = This->device;
309 DWORD oldlevel;
310 HRESULT hr = S_OK;
312 TRACE("(%p,%p,%s)\n", This, hwnd, dumpCooperativeLevel(level));
314 if (!device) {
315 WARN("not initialized\n");
316 return DSERR_UNINITIALIZED;
319 if (level == DSSCL_PRIORITY || level == DSSCL_EXCLUSIVE) {
320 WARN("level=%s not fully supported\n",
321 level==DSSCL_PRIORITY ? "DSSCL_PRIORITY" : "DSSCL_EXCLUSIVE");
324 RtlAcquireResourceExclusive(&device->buffer_list_lock, TRUE);
325 EnterCriticalSection(&device->mixlock);
326 oldlevel = device->priolevel;
327 device->priolevel = level;
328 if ((level == DSSCL_WRITEPRIMARY) != (oldlevel == DSSCL_WRITEPRIMARY)) {
329 hr = DSOUND_ReopenDevice(device, level == DSSCL_WRITEPRIMARY);
330 if (FAILED(hr))
331 device->priolevel = oldlevel;
332 else
333 DSOUND_PrimaryOpen(device);
335 LeaveCriticalSection(&device->mixlock);
336 RtlReleaseResource(&device->buffer_list_lock);
337 return hr;
340 static HRESULT WINAPI IDirectSound8Impl_Compact(IDirectSound8 *iface)
342 IDirectSoundImpl *This = impl_from_IDirectSound8(iface);
344 TRACE("(%p)\n", This);
346 if (!This->device) {
347 WARN("not initialized\n");
348 return DSERR_UNINITIALIZED;
351 if (This->device->priolevel < DSSCL_PRIORITY) {
352 WARN("incorrect priority level\n");
353 return DSERR_PRIOLEVELNEEDED;
355 return DS_OK;
358 static HRESULT WINAPI IDirectSound8Impl_GetSpeakerConfig(IDirectSound8 *iface, DWORD *config)
360 IDirectSoundImpl *This = impl_from_IDirectSound8(iface);
362 TRACE("(%p, %p)\n", This, config);
364 if (!This->device) {
365 WARN("not initialized\n");
366 return DSERR_UNINITIALIZED;
368 if (!config) {
369 WARN("invalid parameter: config == NULL\n");
370 return DSERR_INVALIDPARAM;
373 WARN("not fully functional\n");
374 *config = This->device->speaker_config;
375 return DS_OK;
378 static HRESULT WINAPI IDirectSound8Impl_SetSpeakerConfig(IDirectSound8 *iface, DWORD config)
380 IDirectSoundImpl *This = impl_from_IDirectSound8(iface);
382 TRACE("(%p,0x%08x)\n", This, config);
384 if (!This->device) {
385 WARN("not initialized\n");
386 return DSERR_UNINITIALIZED;
389 This->device->speaker_config = config;
390 WARN("not fully functional\n");
391 return DS_OK;
394 static HRESULT WINAPI IDirectSound8Impl_Initialize(IDirectSound8 *iface, const GUID *lpcGuid)
396 IDirectSoundImpl *This = impl_from_IDirectSound8(iface);
397 TRACE("(%p, %s)\n", This, debugstr_guid(lpcGuid));
398 return DirectSoundDevice_Initialize(&This->device, lpcGuid);
401 static HRESULT WINAPI IDirectSound8Impl_VerifyCertification(IDirectSound8 *iface, DWORD *certified)
403 IDirectSoundImpl *This = impl_from_IDirectSound8(iface);
405 TRACE("(%p, %p)\n", This, certified);
407 if (!This->device) {
408 WARN("not initialized\n");
409 return DSERR_UNINITIALIZED;
412 if (This->device->drvcaps.dwFlags & DSCAPS_CERTIFIED)
413 *certified = DS_CERTIFIED;
414 else
415 *certified = DS_UNCERTIFIED;
417 return DS_OK;
420 static const IDirectSound8Vtbl ds8_vtbl =
422 IDirectSound8Impl_QueryInterface,
423 IDirectSound8Impl_AddRef,
424 IDirectSound8Impl_Release,
425 IDirectSound8Impl_CreateSoundBuffer,
426 IDirectSound8Impl_GetCaps,
427 IDirectSound8Impl_DuplicateSoundBuffer,
428 IDirectSound8Impl_SetCooperativeLevel,
429 IDirectSound8Impl_Compact,
430 IDirectSound8Impl_GetSpeakerConfig,
431 IDirectSound8Impl_SetSpeakerConfig,
432 IDirectSound8Impl_Initialize,
433 IDirectSound8Impl_VerifyCertification
436 HRESULT IDirectSoundImpl_Create(IUnknown *outer_unk, REFIID riid, void **ppv, BOOL has_ds8)
438 IDirectSoundImpl *obj;
439 HRESULT hr;
441 TRACE("(%s, %p)\n", debugstr_guid(riid), ppv);
443 *ppv = NULL;
444 obj = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*obj));
445 if (!obj) {
446 WARN("out of memory\n");
447 return DSERR_OUTOFMEMORY;
450 setup_dsound_options();
452 obj->IUnknown_inner.lpVtbl = &unk_vtbl;
453 obj->IDirectSound8_iface.lpVtbl = &ds8_vtbl;
454 obj->ref = 1;
455 obj->refds = 0;
456 obj->numIfaces = 1;
457 obj->device = NULL;
458 obj->has_ds8 = has_ds8;
460 /* COM aggregation supported only internally */
461 if (outer_unk)
462 obj->outer_unk = outer_unk;
463 else
464 obj->outer_unk = &obj->IUnknown_inner;
466 hr = IUnknown_QueryInterface(&obj->IUnknown_inner, riid, ppv);
467 IUnknown_Release(&obj->IUnknown_inner);
469 return hr;
472 HRESULT DSOUND_Create(REFIID riid, void **ppv)
474 return IDirectSoundImpl_Create(NULL, riid, ppv, FALSE);
477 HRESULT DSOUND_Create8(REFIID riid, void **ppv)
479 return IDirectSoundImpl_Create(NULL, riid, ppv, TRUE);
482 /*******************************************************************************
483 * DirectSoundCreate (DSOUND.1)
485 * Creates and initializes a DirectSound interface.
487 * PARAMS
488 * lpcGUID [I] Address of the GUID that identifies the sound device.
489 * ppDS [O] Address of a variable to receive the interface pointer.
490 * pUnkOuter [I] Must be NULL.
492 * RETURNS
493 * Success: DS_OK
494 * Failure: DSERR_ALLOCATED, DSERR_INVALIDPARAM, DSERR_NOAGGREGATION,
495 * DSERR_NODRIVER, DSERR_OUTOFMEMORY
497 HRESULT WINAPI DirectSoundCreate(
498 LPCGUID lpcGUID,
499 LPDIRECTSOUND *ppDS,
500 IUnknown *pUnkOuter)
502 HRESULT hr;
503 LPDIRECTSOUND pDS;
505 TRACE("(%s,%p,%p)\n",debugstr_guid(lpcGUID),ppDS,pUnkOuter);
507 if (ppDS == NULL) {
508 WARN("invalid parameter: ppDS == NULL\n");
509 return DSERR_INVALIDPARAM;
512 if (pUnkOuter != NULL) {
513 WARN("invalid parameter: pUnkOuter != NULL\n");
514 *ppDS = 0;
515 return DSERR_INVALIDPARAM;
518 hr = DSOUND_Create(&IID_IDirectSound, (void **)&pDS);
519 if (hr == DS_OK) {
520 hr = IDirectSound_Initialize(pDS, lpcGUID);
521 if (hr != DS_OK) {
522 if (hr != DSERR_ALREADYINITIALIZED) {
523 IDirectSound_Release(pDS);
524 pDS = 0;
525 } else
526 hr = DS_OK;
530 *ppDS = pDS;
532 return hr;
535 /*******************************************************************************
536 * DirectSoundCreate8 (DSOUND.11)
538 * Creates and initializes a DirectSound8 interface.
540 * PARAMS
541 * lpcGUID [I] Address of the GUID that identifies the sound device.
542 * ppDS [O] Address of a variable to receive the interface pointer.
543 * pUnkOuter [I] Must be NULL.
545 * RETURNS
546 * Success: DS_OK
547 * Failure: DSERR_ALLOCATED, DSERR_INVALIDPARAM, DSERR_NOAGGREGATION,
548 * DSERR_NODRIVER, DSERR_OUTOFMEMORY
550 HRESULT WINAPI DirectSoundCreate8(
551 LPCGUID lpcGUID,
552 LPDIRECTSOUND8 *ppDS,
553 IUnknown *pUnkOuter)
555 HRESULT hr;
556 LPDIRECTSOUND8 pDS;
558 TRACE("(%s,%p,%p)\n",debugstr_guid(lpcGUID),ppDS,pUnkOuter);
560 if (ppDS == NULL) {
561 WARN("invalid parameter: ppDS == NULL\n");
562 return DSERR_INVALIDPARAM;
565 if (pUnkOuter != NULL) {
566 WARN("invalid parameter: pUnkOuter != NULL\n");
567 *ppDS = 0;
568 return DSERR_INVALIDPARAM;
571 hr = DSOUND_Create8(&IID_IDirectSound8, (void **)&pDS);
572 if (hr == DS_OK) {
573 hr = IDirectSound8_Initialize(pDS, lpcGUID);
574 if (hr != DS_OK) {
575 if (hr != DSERR_ALREADYINITIALIZED) {
576 IDirectSound8_Release(pDS);
577 pDS = 0;
578 } else
579 hr = DS_OK;
583 *ppDS = pDS;
585 return hr;
588 /*******************************************************************************
589 * DirectSoundDevice
591 static HRESULT DirectSoundDevice_Create(DirectSoundDevice ** ppDevice)
593 DirectSoundDevice * device;
594 TRACE("(%p)\n", ppDevice);
596 /* Allocate memory */
597 device = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(DirectSoundDevice));
598 if (device == NULL) {
599 WARN("out of memory\n");
600 return DSERR_OUTOFMEMORY;
603 device->ref = 1;
604 device->priolevel = DSSCL_NORMAL;
605 device->state = STATE_STOPPED;
606 device->speaker_config = DSSPEAKER_COMBINED(DSSPEAKER_STEREO, DSSPEAKER_GEOMETRY_WIDE);
608 /* 3D listener initial parameters */
609 device->ds3dl.dwSize = sizeof(DS3DLISTENER);
610 device->ds3dl.vPosition.x = 0.0;
611 device->ds3dl.vPosition.y = 0.0;
612 device->ds3dl.vPosition.z = 0.0;
613 device->ds3dl.vVelocity.x = 0.0;
614 device->ds3dl.vVelocity.y = 0.0;
615 device->ds3dl.vVelocity.z = 0.0;
616 device->ds3dl.vOrientFront.x = 0.0;
617 device->ds3dl.vOrientFront.y = 0.0;
618 device->ds3dl.vOrientFront.z = 1.0;
619 device->ds3dl.vOrientTop.x = 0.0;
620 device->ds3dl.vOrientTop.y = 1.0;
621 device->ds3dl.vOrientTop.z = 0.0;
622 device->ds3dl.flDistanceFactor = DS3D_DEFAULTDISTANCEFACTOR;
623 device->ds3dl.flRolloffFactor = DS3D_DEFAULTROLLOFFFACTOR;
624 device->ds3dl.flDopplerFactor = DS3D_DEFAULTDOPPLERFACTOR;
626 device->prebuf = ds_snd_queue_max;
627 device->guid = GUID_NULL;
629 /* Set default wave format (may need it for waveOutOpen) */
630 device->pwfx = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(WAVEFORMATEXTENSIBLE));
631 device->primary_pwfx = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(WAVEFORMATEXTENSIBLE));
632 if (!device->pwfx || !device->primary_pwfx) {
633 WARN("out of memory\n");
634 HeapFree(GetProcessHeap(),0,device->primary_pwfx);
635 HeapFree(GetProcessHeap(),0,device->pwfx);
636 HeapFree(GetProcessHeap(),0,device);
637 return DSERR_OUTOFMEMORY;
640 device->pwfx->wFormatTag = WAVE_FORMAT_PCM;
641 device->pwfx->nSamplesPerSec = 22050;
642 device->pwfx->wBitsPerSample = 8;
643 device->pwfx->nChannels = 2;
644 device->pwfx->nBlockAlign = device->pwfx->wBitsPerSample * device->pwfx->nChannels / 8;
645 device->pwfx->nAvgBytesPerSec = device->pwfx->nSamplesPerSec * device->pwfx->nBlockAlign;
646 device->pwfx->cbSize = 0;
647 memcpy(device->primary_pwfx, device->pwfx, sizeof(*device->pwfx));
649 InitializeCriticalSection(&(device->mixlock));
650 device->mixlock.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": DirectSoundDevice.mixlock");
652 RtlInitializeResource(&(device->buffer_list_lock));
654 *ppDevice = device;
656 return DS_OK;
659 static ULONG DirectSoundDevice_AddRef(DirectSoundDevice * device)
661 ULONG ref = InterlockedIncrement(&(device->ref));
662 TRACE("(%p) ref was %d\n", device, ref - 1);
663 return ref;
666 ULONG DirectSoundDevice_Release(DirectSoundDevice * device)
668 HRESULT hr;
669 ULONG ref = InterlockedDecrement(&(device->ref));
670 TRACE("(%p) ref was %u\n", device, ref + 1);
671 if (!ref) {
672 int i;
673 timeKillEvent(device->timerID);
674 timeEndPeriod(DS_TIME_RES);
676 /* The kill event should have allowed the timer process to expire
677 * but try to grab the lock just in case. Can't hold lock because
678 * secondarybuffer_destroy also grabs the lock */
679 RtlAcquireResourceShared(&(device->buffer_list_lock), TRUE);
680 RtlReleaseResource(&(device->buffer_list_lock));
682 EnterCriticalSection(&DSOUND_renderers_lock);
683 list_remove(&device->entry);
684 LeaveCriticalSection(&DSOUND_renderers_lock);
686 /* It is allowed to release this object even when buffers are playing */
687 if (device->buffers) {
688 WARN("%d secondary buffers not released\n", device->nrofbuffers);
689 for( i=0;i<device->nrofbuffers;i++)
690 secondarybuffer_destroy(device->buffers[i]);
693 hr = DSOUND_PrimaryDestroy(device);
694 if (hr != DS_OK)
695 WARN("DSOUND_PrimaryDestroy failed\n");
697 if(device->client)
698 IAudioClient_Release(device->client);
699 if(device->render)
700 IAudioRenderClient_Release(device->render);
701 if(device->clock)
702 IAudioClock_Release(device->clock);
703 if(device->volume)
704 IAudioStreamVolume_Release(device->volume);
706 HeapFree(GetProcessHeap(), 0, device->tmp_buffer);
707 HeapFree(GetProcessHeap(), 0, device->mix_buffer);
708 HeapFree(GetProcessHeap(), 0, device->buffer);
709 RtlDeleteResource(&device->buffer_list_lock);
710 device->mixlock.DebugInfo->Spare[0] = 0;
711 DeleteCriticalSection(&device->mixlock);
712 HeapFree(GetProcessHeap(),0,device);
713 TRACE("(%p) released\n", device);
715 return ref;
718 BOOL DSOUND_check_supported(IAudioClient *client, DWORD rate,
719 DWORD depth, WORD channels)
721 WAVEFORMATEX fmt, *junk;
722 HRESULT hr;
724 fmt.wFormatTag = WAVE_FORMAT_PCM;
725 fmt.nChannels = channels;
726 fmt.nSamplesPerSec = rate;
727 fmt.wBitsPerSample = depth;
728 fmt.nBlockAlign = (channels * depth) / 8;
729 fmt.nAvgBytesPerSec = rate * fmt.nBlockAlign;
730 fmt.cbSize = 0;
732 hr = IAudioClient_IsFormatSupported(client, AUDCLNT_SHAREMODE_SHARED, &fmt, &junk);
733 if(SUCCEEDED(hr))
734 CoTaskMemFree(junk);
736 return hr == S_OK;
739 UINT DSOUND_create_timer(LPTIMECALLBACK cb, DWORD_PTR user)
741 UINT triggertime = DS_TIME_DEL, res = DS_TIME_RES, id;
742 TIMECAPS time;
744 timeGetDevCaps(&time, sizeof(TIMECAPS));
745 TRACE("Minimum timer resolution: %u, max timer: %u\n", time.wPeriodMin, time.wPeriodMax);
746 if (triggertime < time.wPeriodMin)
747 triggertime = time.wPeriodMin;
748 if (res < time.wPeriodMin)
749 res = time.wPeriodMin;
750 if (timeBeginPeriod(res) == TIMERR_NOCANDO)
751 WARN("Could not set minimum resolution, don't expect sound\n");
752 id = timeSetEvent(triggertime, res, cb, user, TIME_PERIODIC | TIME_KILL_SYNCHRONOUS);
753 if (!id)
755 WARN("Timer not created! Retrying without TIME_KILL_SYNCHRONOUS\n");
756 id = timeSetEvent(triggertime, res, cb, user, TIME_PERIODIC);
757 if (!id)
758 ERR("Could not create timer, sound playback will not occur\n");
760 return id;
763 HRESULT DirectSoundDevice_Initialize(DirectSoundDevice ** ppDevice, LPCGUID lpcGUID)
765 HRESULT hr = DS_OK;
766 GUID devGUID;
767 DirectSoundDevice *device;
768 IMMDevice *mmdevice;
770 TRACE("(%p,%s)\n",ppDevice,debugstr_guid(lpcGUID));
772 if (*ppDevice != NULL) {
773 WARN("already initialized\n");
774 return DSERR_ALREADYINITIALIZED;
777 /* Default device? */
778 if (!lpcGUID || IsEqualGUID(lpcGUID, &GUID_NULL))
779 lpcGUID = &DSDEVID_DefaultPlayback;
781 if(IsEqualGUID(lpcGUID, &DSDEVID_DefaultCapture) ||
782 IsEqualGUID(lpcGUID, &DSDEVID_DefaultVoiceCapture))
783 return DSERR_NODRIVER;
785 if (GetDeviceID(lpcGUID, &devGUID) != DS_OK) {
786 WARN("invalid parameter: lpcGUID\n");
787 return DSERR_INVALIDPARAM;
790 hr = get_mmdevice(eRender, &devGUID, &mmdevice);
791 if(FAILED(hr))
792 return hr;
794 EnterCriticalSection(&DSOUND_renderers_lock);
796 LIST_FOR_EACH_ENTRY(device, &DSOUND_renderers, DirectSoundDevice, entry){
797 if(IsEqualGUID(&device->guid, &devGUID)){
798 IMMDevice_Release(mmdevice);
799 DirectSoundDevice_AddRef(device);
800 *ppDevice = device;
801 LeaveCriticalSection(&DSOUND_renderers_lock);
802 return DS_OK;
806 hr = DirectSoundDevice_Create(&device);
807 if(FAILED(hr)){
808 WARN("DirectSoundDevice_Create failed\n");
809 IMMDevice_Release(mmdevice);
810 LeaveCriticalSection(&DSOUND_renderers_lock);
811 return hr;
814 device->mmdevice = mmdevice;
815 device->guid = devGUID;
817 hr = DSOUND_ReopenDevice(device, FALSE);
818 if (FAILED(hr))
820 HeapFree(GetProcessHeap(), 0, device);
821 LeaveCriticalSection(&DSOUND_renderers_lock);
822 IMMDevice_Release(mmdevice);
823 WARN("DSOUND_ReopenDevice failed: %08x\n", hr);
824 return hr;
827 ZeroMemory(&device->drvcaps, sizeof(device->drvcaps));
829 if(DSOUND_check_supported(device->client, 11025, 8, 1) ||
830 DSOUND_check_supported(device->client, 22050, 8, 1) ||
831 DSOUND_check_supported(device->client, 44100, 8, 1) ||
832 DSOUND_check_supported(device->client, 48000, 8, 1) ||
833 DSOUND_check_supported(device->client, 96000, 8, 1))
834 device->drvcaps.dwFlags |= DSCAPS_PRIMARY8BIT | DSCAPS_PRIMARYMONO;
836 if(DSOUND_check_supported(device->client, 11025, 16, 1) ||
837 DSOUND_check_supported(device->client, 22050, 16, 1) ||
838 DSOUND_check_supported(device->client, 44100, 16, 1) ||
839 DSOUND_check_supported(device->client, 48000, 16, 1) ||
840 DSOUND_check_supported(device->client, 96000, 16, 1))
841 device->drvcaps.dwFlags |= DSCAPS_PRIMARY16BIT | DSCAPS_PRIMARYMONO;
843 if(DSOUND_check_supported(device->client, 11025, 8, 2) ||
844 DSOUND_check_supported(device->client, 22050, 8, 2) ||
845 DSOUND_check_supported(device->client, 44100, 8, 2) ||
846 DSOUND_check_supported(device->client, 48000, 8, 2) ||
847 DSOUND_check_supported(device->client, 96000, 8, 2))
848 device->drvcaps.dwFlags |= DSCAPS_PRIMARY8BIT | DSCAPS_PRIMARYSTEREO;
850 if(DSOUND_check_supported(device->client, 11025, 16, 2) ||
851 DSOUND_check_supported(device->client, 22050, 16, 2) ||
852 DSOUND_check_supported(device->client, 44100, 16, 2) ||
853 DSOUND_check_supported(device->client, 48000, 16, 2) ||
854 DSOUND_check_supported(device->client, 96000, 16, 2))
855 device->drvcaps.dwFlags |= DSCAPS_PRIMARY16BIT | DSCAPS_PRIMARYSTEREO;
857 /* the dsound mixer supports all of the following */
858 device->drvcaps.dwFlags |= DSCAPS_SECONDARY8BIT | DSCAPS_SECONDARY16BIT;
859 device->drvcaps.dwFlags |= DSCAPS_SECONDARYMONO | DSCAPS_SECONDARYSTEREO;
860 device->drvcaps.dwFlags |= DSCAPS_CONTINUOUSRATE;
862 device->drvcaps.dwPrimaryBuffers = 1;
863 device->drvcaps.dwMinSecondarySampleRate = DSBFREQUENCY_MIN;
864 device->drvcaps.dwMaxSecondarySampleRate = DSBFREQUENCY_MAX;
865 device->drvcaps.dwMaxHwMixingAllBuffers = 1;
866 device->drvcaps.dwMaxHwMixingStaticBuffers = 1;
867 device->drvcaps.dwMaxHwMixingStreamingBuffers = 1;
869 ZeroMemory(&device->volpan, sizeof(device->volpan));
871 hr = DSOUND_PrimaryCreate(device);
872 if (hr == DS_OK)
873 device->timerID = DSOUND_create_timer(DSOUND_timer, (DWORD_PTR)device);
874 else
875 WARN("DSOUND_PrimaryCreate failed: %08x\n", hr);
877 *ppDevice = device;
878 list_add_tail(&DSOUND_renderers, &device->entry);
880 LeaveCriticalSection(&DSOUND_renderers_lock);
882 return hr;
885 HRESULT DirectSoundDevice_CreateSoundBuffer(
886 DirectSoundDevice * device,
887 LPCDSBUFFERDESC dsbd,
888 LPLPDIRECTSOUNDBUFFER ppdsb,
889 LPUNKNOWN lpunk,
890 BOOL from8)
892 HRESULT hres = DS_OK;
893 TRACE("(%p,%p,%p,%p)\n",device,dsbd,ppdsb,lpunk);
895 if (device == NULL) {
896 WARN("not initialized\n");
897 return DSERR_UNINITIALIZED;
900 if (dsbd == NULL) {
901 WARN("invalid parameter: dsbd == NULL\n");
902 return DSERR_INVALIDPARAM;
905 if (dsbd->dwSize != sizeof(DSBUFFERDESC) &&
906 dsbd->dwSize != sizeof(DSBUFFERDESC1)) {
907 WARN("invalid parameter: dsbd\n");
908 return DSERR_INVALIDPARAM;
911 if (ppdsb == NULL) {
912 WARN("invalid parameter: ppdsb == NULL\n");
913 return DSERR_INVALIDPARAM;
915 *ppdsb = NULL;
917 if (TRACE_ON(dsound)) {
918 TRACE("(structsize=%d)\n",dsbd->dwSize);
919 TRACE("(flags=0x%08x:\n",dsbd->dwFlags);
920 _dump_DSBCAPS(dsbd->dwFlags);
921 TRACE(")\n");
922 TRACE("(bufferbytes=%d)\n",dsbd->dwBufferBytes);
923 TRACE("(lpwfxFormat=%p)\n",dsbd->lpwfxFormat);
926 if (dsbd->dwFlags & DSBCAPS_LOCHARDWARE &&
927 !(dsbd->dwFlags & DSBCAPS_PRIMARYBUFFER)) {
928 TRACE("LOCHARDWARE is not supported, returning E_NOTIMPL\n");
929 return E_NOTIMPL;
932 if (dsbd->dwFlags & DSBCAPS_PRIMARYBUFFER) {
933 if (dsbd->lpwfxFormat != NULL) {
934 WARN("invalid parameter: dsbd->lpwfxFormat must be NULL for "
935 "primary buffer\n");
936 return DSERR_INVALIDPARAM;
939 if (device->primary) {
940 WARN("Primary Buffer already created\n");
941 IDirectSoundBuffer_AddRef((LPDIRECTSOUNDBUFFER8)(device->primary));
942 *ppdsb = (LPDIRECTSOUNDBUFFER)(device->primary);
943 } else {
944 hres = primarybuffer_create(device, &device->primary, dsbd);
945 if (device->primary) {
946 *ppdsb = (IDirectSoundBuffer*)&device->primary->IDirectSoundBuffer8_iface;
947 device->primary->dsbd.dwFlags &= ~(DSBCAPS_LOCHARDWARE | DSBCAPS_LOCSOFTWARE);
948 device->primary->dsbd.dwFlags |= DSBCAPS_LOCSOFTWARE;
949 } else
950 WARN("primarybuffer_create() failed\n");
952 } else {
953 IDirectSoundBufferImpl * dsb;
954 WAVEFORMATEXTENSIBLE *pwfxe;
956 if (dsbd->lpwfxFormat == NULL) {
957 WARN("invalid parameter: dsbd->lpwfxFormat can't be NULL for "
958 "secondary buffer\n");
959 return DSERR_INVALIDPARAM;
961 pwfxe = (WAVEFORMATEXTENSIBLE*)dsbd->lpwfxFormat;
963 if (pwfxe->Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE)
965 /* check if cbSize is at least 22 bytes */
966 if (pwfxe->Format.cbSize < (sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX)))
968 WARN("Too small a cbSize %u\n", pwfxe->Format.cbSize);
969 return DSERR_INVALIDPARAM;
972 /* cbSize should be 22 bytes, with one possible exception */
973 if (pwfxe->Format.cbSize > (sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX)) &&
974 !((IsEqualGUID(&pwfxe->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM) || IsEqualGUID(&pwfxe->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)) &&
975 pwfxe->Format.cbSize == sizeof(WAVEFORMATEXTENSIBLE)))
977 WARN("Too big a cbSize %u\n", pwfxe->Format.cbSize);
978 return DSERR_CONTROLUNAVAIL;
981 if ((!IsEqualGUID(&pwfxe->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM)) && (!IsEqualGUID(&pwfxe->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)))
983 if (!IsEqualGUID(&pwfxe->SubFormat, &GUID_NULL))
984 FIXME("SubFormat %s not supported right now.\n", debugstr_guid(&pwfxe->SubFormat));
985 return DSERR_INVALIDPARAM;
987 if (pwfxe->Samples.wValidBitsPerSample > dsbd->lpwfxFormat->wBitsPerSample)
989 WARN("Samples.wValidBitsPerSample(%d) > Format.wBitsPerSample (%d)\n", pwfxe->Samples.wValidBitsPerSample, pwfxe->Format.wBitsPerSample);
990 return DSERR_INVALIDPARAM;
992 if (pwfxe->Samples.wValidBitsPerSample && pwfxe->Samples.wValidBitsPerSample < dsbd->lpwfxFormat->wBitsPerSample)
994 FIXME("Non-packed formats not supported right now: %d/%d\n", pwfxe->Samples.wValidBitsPerSample, dsbd->lpwfxFormat->wBitsPerSample);
995 return DSERR_CONTROLUNAVAIL;
999 TRACE("(formattag=0x%04x,chans=%d,samplerate=%d,"
1000 "bytespersec=%d,blockalign=%d,bitspersamp=%d,cbSize=%d)\n",
1001 dsbd->lpwfxFormat->wFormatTag, dsbd->lpwfxFormat->nChannels,
1002 dsbd->lpwfxFormat->nSamplesPerSec,
1003 dsbd->lpwfxFormat->nAvgBytesPerSec,
1004 dsbd->lpwfxFormat->nBlockAlign,
1005 dsbd->lpwfxFormat->wBitsPerSample, dsbd->lpwfxFormat->cbSize);
1007 if (from8 && (dsbd->dwFlags & DSBCAPS_CTRL3D) && (dsbd->lpwfxFormat->nChannels != 1)) {
1008 WARN("invalid parameter: 3D buffer format must be mono\n");
1009 return DSERR_INVALIDPARAM;
1012 hres = IDirectSoundBufferImpl_Create(device, &dsb, dsbd);
1013 if (dsb)
1014 *ppdsb = (IDirectSoundBuffer*)&dsb->IDirectSoundBuffer8_iface;
1015 else
1016 WARN("IDirectSoundBufferImpl_Create failed\n");
1019 return hres;
1022 HRESULT DirectSoundDevice_DuplicateSoundBuffer(
1023 DirectSoundDevice * device,
1024 LPDIRECTSOUNDBUFFER psb,
1025 LPLPDIRECTSOUNDBUFFER ppdsb)
1027 HRESULT hres = DS_OK;
1028 IDirectSoundBufferImpl* dsb;
1029 TRACE("(%p,%p,%p)\n",device,psb,ppdsb);
1031 if (device == NULL) {
1032 WARN("not initialized\n");
1033 return DSERR_UNINITIALIZED;
1036 if (psb == NULL) {
1037 WARN("invalid parameter: psb == NULL\n");
1038 return DSERR_INVALIDPARAM;
1041 if (ppdsb == NULL) {
1042 WARN("invalid parameter: ppdsb == NULL\n");
1043 return DSERR_INVALIDPARAM;
1046 /* make sure we have a secondary buffer */
1047 if (psb == (IDirectSoundBuffer *)&device->primary->IDirectSoundBuffer8_iface) {
1048 WARN("trying to duplicate primary buffer\n");
1049 *ppdsb = NULL;
1050 return DSERR_INVALIDCALL;
1053 /* duplicate the actual buffer implementation */
1054 hres = IDirectSoundBufferImpl_Duplicate(device, &dsb, (IDirectSoundBufferImpl*)psb);
1055 if (hres == DS_OK)
1056 *ppdsb = (IDirectSoundBuffer*)&dsb->IDirectSoundBuffer8_iface;
1057 else
1058 WARN("IDirectSoundBufferImpl_Duplicate failed\n");
1060 return hres;
1064 * Add secondary buffer to buffer list.
1065 * Gets exclusive access to buffer for writing.
1067 HRESULT DirectSoundDevice_AddBuffer(
1068 DirectSoundDevice * device,
1069 IDirectSoundBufferImpl * pDSB)
1071 IDirectSoundBufferImpl **newbuffers;
1072 HRESULT hr = DS_OK;
1074 TRACE("(%p, %p)\n", device, pDSB);
1076 RtlAcquireResourceExclusive(&(device->buffer_list_lock), TRUE);
1078 if (device->buffers)
1079 newbuffers = HeapReAlloc(GetProcessHeap(),0,device->buffers,sizeof(IDirectSoundBufferImpl*)*(device->nrofbuffers+1));
1080 else
1081 newbuffers = HeapAlloc(GetProcessHeap(),0,sizeof(IDirectSoundBufferImpl*)*(device->nrofbuffers+1));
1083 if (newbuffers) {
1084 device->buffers = newbuffers;
1085 device->buffers[device->nrofbuffers] = pDSB;
1086 device->nrofbuffers++;
1087 TRACE("buffer count is now %d\n", device->nrofbuffers);
1088 } else {
1089 ERR("out of memory for buffer list! Current buffer count is %d\n", device->nrofbuffers);
1090 hr = DSERR_OUTOFMEMORY;
1093 RtlReleaseResource(&(device->buffer_list_lock));
1095 return hr;
1099 * Remove secondary buffer from buffer list.
1100 * Gets exclusive access to buffer for writing.
1102 void DirectSoundDevice_RemoveBuffer(DirectSoundDevice * device, IDirectSoundBufferImpl * pDSB)
1104 int i;
1106 TRACE("(%p, %p)\n", device, pDSB);
1108 RtlAcquireResourceExclusive(&(device->buffer_list_lock), TRUE);
1110 if (device->nrofbuffers == 1) {
1111 assert(device->buffers[0] == pDSB);
1112 HeapFree(GetProcessHeap(), 0, device->buffers);
1113 device->buffers = NULL;
1114 } else {
1115 for (i = 0; i < device->nrofbuffers; i++) {
1116 if (device->buffers[i] == pDSB) {
1117 /* Put the last buffer of the list in the (now empty) position */
1118 device->buffers[i] = device->buffers[device->nrofbuffers - 1];
1119 break;
1123 device->nrofbuffers--;
1124 TRACE("buffer count is now %d\n", device->nrofbuffers);
1126 RtlReleaseResource(&(device->buffer_list_lock));