shell32: Indentation fix.
[wine] / dlls / dsound / dsound.c
1 /* DirectSound
2  *
3  * Copyright 1998 Marcus Meissner
4  * Copyright 1998 Rob Riggs
5  * Copyright 2000-2002 TransGaming Technologies, Inc.
6  * Copyright 2004 Robert Reif
7  *
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.
12  *
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.
17  *
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
21  */
22
23 #include <assert.h>
24 #include <stdarg.h>
25 #include <stdio.h>
26
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"
42
43 WINE_DEFAULT_DEBUG_CHANNEL(dsound);
44
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;
53
54 static const char * dumpCooperativeLevel(DWORD level)
55 {
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);
62     }
63 #undef LE
64     return wine_dbg_sprintf("Unknown(%08x)", level);
65 }
66
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
85     };
86     unsigned int     i;
87
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);
91 }
92
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
113     };
114     unsigned int     i;
115
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);
119 }
120
121 static void directsound_destroy(IDirectSoundImpl *This)
122 {
123     if (This->device)
124         DirectSoundDevice_Release(This->device);
125     HeapFree(GetProcessHeap(),0,This);
126     TRACE("(%p) released\n", This);
127 }
128
129 /*******************************************************************************
130  *      IUnknown Implementation for DirectSound
131  */
132 static inline IDirectSoundImpl *impl_from_IUnknown(IUnknown *iface)
133 {
134     return CONTAINING_RECORD(iface, IDirectSoundImpl, IUnknown_inner);
135 }
136
137 static HRESULT WINAPI IUnknownImpl_QueryInterface(IUnknown *iface, REFIID riid, void **ppv)
138 {
139     IDirectSoundImpl *This = impl_from_IUnknown(iface);
140
141     TRACE("(%p,%s,%p)\n", This, debugstr_guid(riid), ppv);
142
143     if (!ppv) {
144         WARN("invalid parameter\n");
145         return E_INVALIDARG;
146     }
147     *ppv = NULL;
148
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;
157     }
158
159     IUnknown_AddRef((IUnknown*)*ppv);
160     return S_OK;
161 }
162
163 static ULONG WINAPI IUnknownImpl_AddRef(IUnknown *iface)
164 {
165     IDirectSoundImpl *This = impl_from_IUnknown(iface);
166     ULONG ref = InterlockedIncrement(&This->ref);
167
168     TRACE("(%p) ref=%d\n", This, ref);
169
170     if(ref == 1)
171         InterlockedIncrement(&This->numIfaces);
172
173     return ref;
174 }
175
176 static ULONG WINAPI IUnknownImpl_Release(IUnknown *iface)
177 {
178     IDirectSoundImpl *This = impl_from_IUnknown(iface);
179     ULONG ref = InterlockedDecrement(&This->ref);
180
181     TRACE("(%p) ref=%d\n", This, ref);
182
183     if (!ref && !InterlockedDecrement(&This->numIfaces))
184         directsound_destroy(This);
185
186     return ref;
187 }
188
189 static const IUnknownVtbl unk_vtbl =
190 {
191     IUnknownImpl_QueryInterface,
192     IUnknownImpl_AddRef,
193     IUnknownImpl_Release
194 };
195
196 /*******************************************************************************
197  *      IDirectSound and IDirectSound8 Implementation
198  */
199 static inline IDirectSoundImpl *impl_from_IDirectSound8(IDirectSound8 *iface)
200 {
201     return CONTAINING_RECORD(iface, IDirectSoundImpl, IDirectSound8_iface);
202 }
203
204 static HRESULT WINAPI IDirectSound8Impl_QueryInterface(IDirectSound8 *iface, REFIID riid,
205         void **ppv)
206 {
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);
210 }
211
212 static ULONG WINAPI IDirectSound8Impl_AddRef(IDirectSound8 *iface)
213 {
214     IDirectSoundImpl *This = impl_from_IDirectSound8(iface);
215     ULONG ref = InterlockedIncrement(&This->refds);
216
217     TRACE("(%p) refds=%d\n", This, ref);
218
219     if(ref == 1)
220         InterlockedIncrement(&This->numIfaces);
221
222     return ref;
223 }
224
225 static ULONG WINAPI IDirectSound8Impl_Release(IDirectSound8 *iface)
226 {
227     IDirectSoundImpl *This = impl_from_IDirectSound8(iface);
228     ULONG ref = InterlockedDecrement(&(This->refds));
229
230     TRACE("(%p) refds=%d\n", This, ref);
231
232     if (!ref && !InterlockedDecrement(&This->numIfaces))
233         directsound_destroy(This);
234
235     return ref;
236 }
237
238 static HRESULT WINAPI IDirectSound8Impl_CreateSoundBuffer(IDirectSound8 *iface,
239         const DSBUFFERDESC *dsbd, IDirectSoundBuffer **ppdsb, IUnknown *lpunk)
240 {
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);
244 }
245
246 static HRESULT WINAPI IDirectSound8Impl_GetCaps(IDirectSound8 *iface, DSCAPS *dscaps)
247 {
248     IDirectSoundImpl *This = impl_from_IDirectSound8(iface);
249
250     TRACE("(%p, %p)\n", This, dscaps);
251
252     if (!This->device) {
253         WARN("not initialized\n");
254         return DSERR_UNINITIALIZED;
255     }
256     if (!dscaps) {
257         WARN("invalid parameter: dscaps = NULL\n");
258         return DSERR_INVALIDPARAM;
259     }
260     if (dscaps->dwSize < sizeof(*dscaps)) {
261         WARN("invalid parameter: dscaps->dwSize = %d\n", dscaps->dwSize);
262         return DSERR_INVALIDPARAM;
263     }
264
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;
286
287     if (TRACE_ON(dsound)) {
288         TRACE("(flags=0x%08x:\n", dscaps->dwFlags);
289         _dump_DSCAPS(dscaps->dwFlags);
290         TRACE(")\n");
291     }
292
293     return DS_OK;
294 }
295
296 static HRESULT WINAPI IDirectSound8Impl_DuplicateSoundBuffer(IDirectSound8 *iface,
297         IDirectSoundBuffer *psb, IDirectSoundBuffer **ppdsb)
298 {
299     IDirectSoundImpl *This = impl_from_IDirectSound8(iface);
300     TRACE("(%p,%p,%p)\n", This, psb, ppdsb);
301     return DirectSoundDevice_DuplicateSoundBuffer(This->device, psb, ppdsb);
302 }
303
304 static HRESULT WINAPI IDirectSound8Impl_SetCooperativeLevel(IDirectSound8 *iface, HWND hwnd,
305         DWORD level)
306 {
307     IDirectSoundImpl *This = impl_from_IDirectSound8(iface);
308     DirectSoundDevice *device = This->device;
309     DWORD oldlevel;
310     HRESULT hr = S_OK;
311
312     TRACE("(%p,%p,%s)\n", This, hwnd, dumpCooperativeLevel(level));
313
314     if (!device) {
315         WARN("not initialized\n");
316         return DSERR_UNINITIALIZED;
317     }
318
319     if (level == DSSCL_PRIORITY || level == DSSCL_EXCLUSIVE) {
320         WARN("level=%s not fully supported\n",
321              level==DSSCL_PRIORITY ? "DSSCL_PRIORITY" : "DSSCL_EXCLUSIVE");
322     }
323
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);
334     }
335     LeaveCriticalSection(&device->mixlock);
336     RtlReleaseResource(&device->buffer_list_lock);
337     return hr;
338 }
339
340 static HRESULT WINAPI IDirectSound8Impl_Compact(IDirectSound8 *iface)
341 {
342     IDirectSoundImpl *This = impl_from_IDirectSound8(iface);
343
344     TRACE("(%p)\n", This);
345
346     if (!This->device) {
347         WARN("not initialized\n");
348         return DSERR_UNINITIALIZED;
349     }
350
351     if (This->device->priolevel < DSSCL_PRIORITY) {
352         WARN("incorrect priority level\n");
353         return DSERR_PRIOLEVELNEEDED;
354     }
355     return DS_OK;
356 }
357
358 static HRESULT WINAPI IDirectSound8Impl_GetSpeakerConfig(IDirectSound8 *iface, DWORD *config)
359 {
360     IDirectSoundImpl *This = impl_from_IDirectSound8(iface);
361
362     TRACE("(%p, %p)\n", This, config);
363
364     if (!This->device) {
365         WARN("not initialized\n");
366         return DSERR_UNINITIALIZED;
367     }
368     if (!config) {
369         WARN("invalid parameter: config == NULL\n");
370         return DSERR_INVALIDPARAM;
371     }
372
373     WARN("not fully functional\n");
374     *config = This->device->speaker_config;
375     return DS_OK;
376 }
377
378 static HRESULT WINAPI IDirectSound8Impl_SetSpeakerConfig(IDirectSound8 *iface, DWORD config)
379 {
380     IDirectSoundImpl *This = impl_from_IDirectSound8(iface);
381
382     TRACE("(%p,0x%08x)\n", This, config);
383
384     if (!This->device) {
385         WARN("not initialized\n");
386         return DSERR_UNINITIALIZED;
387     }
388
389     This->device->speaker_config = config;
390     WARN("not fully functional\n");
391     return DS_OK;
392 }
393
394 static HRESULT WINAPI IDirectSound8Impl_Initialize(IDirectSound8 *iface, const GUID *lpcGuid)
395 {
396     IDirectSoundImpl *This = impl_from_IDirectSound8(iface);
397     TRACE("(%p, %s)\n", This, debugstr_guid(lpcGuid));
398     return DirectSoundDevice_Initialize(&This->device, lpcGuid);
399 }
400
401 static HRESULT WINAPI IDirectSound8Impl_VerifyCertification(IDirectSound8 *iface, DWORD *certified)
402 {
403     IDirectSoundImpl *This = impl_from_IDirectSound8(iface);
404
405     TRACE("(%p, %p)\n", This, certified);
406
407     if (!This->device) {
408         WARN("not initialized\n");
409         return DSERR_UNINITIALIZED;
410     }
411
412     if (This->device->drvcaps.dwFlags & DSCAPS_CERTIFIED)
413         *certified = DS_CERTIFIED;
414     else
415         *certified = DS_UNCERTIFIED;
416
417     return DS_OK;
418 }
419
420 static const IDirectSound8Vtbl ds8_vtbl =
421 {
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
434 };
435
436 HRESULT IDirectSoundImpl_Create(IUnknown *outer_unk, REFIID riid, void **ppv, BOOL has_ds8)
437 {
438     IDirectSoundImpl *obj;
439     HRESULT hr;
440
441     TRACE("(%s, %p)\n", debugstr_guid(riid), ppv);
442
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;
448     }
449
450     setup_dsound_options();
451
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;
459
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;
465
466     hr = IUnknown_QueryInterface(&obj->IUnknown_inner, riid, ppv);
467     IUnknown_Release(&obj->IUnknown_inner);
468
469     return hr;
470 }
471
472 HRESULT DSOUND_Create(REFIID riid, void **ppv)
473 {
474     return IDirectSoundImpl_Create(NULL, riid, ppv, FALSE);
475 }
476
477 HRESULT DSOUND_Create8(REFIID riid, void **ppv)
478 {
479     return IDirectSoundImpl_Create(NULL, riid, ppv, TRUE);
480 }
481
482 /*******************************************************************************
483  *              DirectSoundCreate (DSOUND.1)
484  *
485  *  Creates and initializes a DirectSound interface.
486  *
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.
491  *
492  *  RETURNS
493  *     Success: DS_OK
494  *     Failure: DSERR_ALLOCATED, DSERR_INVALIDPARAM, DSERR_NOAGGREGATION,
495  *              DSERR_NODRIVER, DSERR_OUTOFMEMORY
496  */
497 HRESULT WINAPI DirectSoundCreate(
498     LPCGUID lpcGUID,
499     LPDIRECTSOUND *ppDS,
500     IUnknown *pUnkOuter)
501 {
502     HRESULT hr;
503     LPDIRECTSOUND pDS;
504
505     TRACE("(%s,%p,%p)\n",debugstr_guid(lpcGUID),ppDS,pUnkOuter);
506
507     if (ppDS == NULL) {
508         WARN("invalid parameter: ppDS == NULL\n");
509         return DSERR_INVALIDPARAM;
510     }
511
512     if (pUnkOuter != NULL) {
513         WARN("invalid parameter: pUnkOuter != NULL\n");
514         *ppDS = 0;
515         return DSERR_INVALIDPARAM;
516     }
517
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;
527         }
528     }
529
530     *ppDS = pDS;
531
532     return hr;
533 }
534
535 /*******************************************************************************
536  *        DirectSoundCreate8 (DSOUND.11)
537  *
538  *  Creates and initializes a DirectSound8 interface.
539  *
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.
544  *
545  *  RETURNS
546  *     Success: DS_OK
547  *     Failure: DSERR_ALLOCATED, DSERR_INVALIDPARAM, DSERR_NOAGGREGATION,
548  *              DSERR_NODRIVER, DSERR_OUTOFMEMORY
549  */
550 HRESULT WINAPI DirectSoundCreate8(
551     LPCGUID lpcGUID,
552     LPDIRECTSOUND8 *ppDS,
553     IUnknown *pUnkOuter)
554 {
555     HRESULT hr;
556     LPDIRECTSOUND8 pDS;
557
558     TRACE("(%s,%p,%p)\n",debugstr_guid(lpcGUID),ppDS,pUnkOuter);
559
560     if (ppDS == NULL) {
561         WARN("invalid parameter: ppDS == NULL\n");
562         return DSERR_INVALIDPARAM;
563     }
564
565     if (pUnkOuter != NULL) {
566         WARN("invalid parameter: pUnkOuter != NULL\n");
567         *ppDS = 0;
568         return DSERR_INVALIDPARAM;
569     }
570
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;
580         }
581     }
582
583     *ppDS = pDS;
584
585     return hr;
586 }
587
588 /*******************************************************************************
589  *        DirectSoundDevice
590  */
591 static HRESULT DirectSoundDevice_Create(DirectSoundDevice ** ppDevice)
592 {
593     DirectSoundDevice * device;
594     TRACE("(%p)\n", ppDevice);
595
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;
601     }
602
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);
607
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;
625
626     device->prebuf = ds_snd_queue_max;
627     device->guid = GUID_NULL;
628
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;
638     }
639
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));
648
649     InitializeCriticalSection(&(device->mixlock));
650     device->mixlock.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": DirectSoundDevice.mixlock");
651
652     RtlInitializeResource(&(device->buffer_list_lock));
653
654    *ppDevice = device;
655
656     return DS_OK;
657 }
658
659 static ULONG DirectSoundDevice_AddRef(DirectSoundDevice * device)
660 {
661     ULONG ref = InterlockedIncrement(&(device->ref));
662     TRACE("(%p) ref was %d\n", device, ref - 1);
663     return ref;
664 }
665
666 ULONG DirectSoundDevice_Release(DirectSoundDevice * device)
667 {
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);
675
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));
681
682         EnterCriticalSection(&DSOUND_renderers_lock);
683         list_remove(&device->entry);
684         LeaveCriticalSection(&DSOUND_renderers_lock);
685
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]);
691         }
692
693         hr = DSOUND_PrimaryDestroy(device);
694         if (hr != DS_OK)
695             WARN("DSOUND_PrimaryDestroy failed\n");
696
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);
705
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);
714     }
715     return ref;
716 }
717
718 BOOL DSOUND_check_supported(IAudioClient *client, DWORD rate,
719         DWORD depth, WORD channels)
720 {
721     WAVEFORMATEX fmt, *junk;
722     HRESULT hr;
723
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;
731
732     hr = IAudioClient_IsFormatSupported(client, AUDCLNT_SHAREMODE_SHARED, &fmt, &junk);
733     if(SUCCEEDED(hr))
734         CoTaskMemFree(junk);
735
736     return hr == S_OK;
737 }
738
739 UINT DSOUND_create_timer(LPTIMECALLBACK cb, DWORD_PTR user)
740 {
741     UINT triggertime = DS_TIME_DEL, res = DS_TIME_RES, id;
742     TIMECAPS time;
743
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)
754     {
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");
759     }
760     return id;
761 }
762
763 HRESULT DirectSoundDevice_Initialize(DirectSoundDevice ** ppDevice, LPCGUID lpcGUID)
764 {
765     HRESULT hr = DS_OK;
766     GUID devGUID;
767     DirectSoundDevice *device;
768     IMMDevice *mmdevice;
769
770     TRACE("(%p,%s)\n",ppDevice,debugstr_guid(lpcGUID));
771
772     if (*ppDevice != NULL) {
773         WARN("already initialized\n");
774         return DSERR_ALREADYINITIALIZED;
775     }
776
777     /* Default device? */
778     if (!lpcGUID || IsEqualGUID(lpcGUID, &GUID_NULL))
779         lpcGUID = &DSDEVID_DefaultPlayback;
780
781     if(IsEqualGUID(lpcGUID, &DSDEVID_DefaultCapture) ||
782             IsEqualGUID(lpcGUID, &DSDEVID_DefaultVoiceCapture))
783         return DSERR_NODRIVER;
784
785     if (GetDeviceID(lpcGUID, &devGUID) != DS_OK) {
786         WARN("invalid parameter: lpcGUID\n");
787         return DSERR_INVALIDPARAM;
788     }
789
790     hr = get_mmdevice(eRender, &devGUID, &mmdevice);
791     if(FAILED(hr))
792         return hr;
793
794     EnterCriticalSection(&DSOUND_renderers_lock);
795
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;
803         }
804     }
805
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;
812     }
813
814     device->mmdevice = mmdevice;
815     device->guid = devGUID;
816
817     hr = DSOUND_ReopenDevice(device, FALSE);
818     if (FAILED(hr))
819     {
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;
825     }
826
827     ZeroMemory(&device->drvcaps, sizeof(device->drvcaps));
828
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;
835
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;
842
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;
849
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;
856
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;
861
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;
868
869     ZeroMemory(&device->volpan, sizeof(device->volpan));
870
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);
876
877     *ppDevice = device;
878     list_add_tail(&DSOUND_renderers, &device->entry);
879
880     LeaveCriticalSection(&DSOUND_renderers_lock);
881
882     return hr;
883 }
884
885 HRESULT DirectSoundDevice_CreateSoundBuffer(
886     DirectSoundDevice * device,
887     LPCDSBUFFERDESC dsbd,
888     LPLPDIRECTSOUNDBUFFER ppdsb,
889     LPUNKNOWN lpunk,
890     BOOL from8)
891 {
892     HRESULT hres = DS_OK;
893     TRACE("(%p,%p,%p,%p)\n",device,dsbd,ppdsb,lpunk);
894
895     if (device == NULL) {
896         WARN("not initialized\n");
897         return DSERR_UNINITIALIZED;
898     }
899
900     if (dsbd == NULL) {
901         WARN("invalid parameter: dsbd == NULL\n");
902         return DSERR_INVALIDPARAM;
903     }
904
905     if (dsbd->dwSize != sizeof(DSBUFFERDESC) &&
906         dsbd->dwSize != sizeof(DSBUFFERDESC1)) {
907         WARN("invalid parameter: dsbd\n");
908         return DSERR_INVALIDPARAM;
909     }
910
911     if (ppdsb == NULL) {
912         WARN("invalid parameter: ppdsb == NULL\n");
913         return DSERR_INVALIDPARAM;
914     }
915     *ppdsb = NULL;
916
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);
924     }
925
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;
930     }
931
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;
937         }
938
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");
951         }
952     } else {
953         IDirectSoundBufferImpl * dsb;
954         WAVEFORMATEXTENSIBLE *pwfxe;
955
956         if (dsbd->lpwfxFormat == NULL) {
957             WARN("invalid parameter: dsbd->lpwfxFormat can't be NULL for "
958                  "secondary buffer\n");
959             return DSERR_INVALIDPARAM;
960         }
961         pwfxe = (WAVEFORMATEXTENSIBLE*)dsbd->lpwfxFormat;
962
963         if (pwfxe->Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE)
964         {
965             /* check if cbSize is at least 22 bytes */
966             if (pwfxe->Format.cbSize < (sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX)))
967             {
968                 WARN("Too small a cbSize %u\n", pwfxe->Format.cbSize);
969                 return DSERR_INVALIDPARAM;
970             }
971
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)))
976             {
977                 WARN("Too big a cbSize %u\n", pwfxe->Format.cbSize);
978                 return DSERR_CONTROLUNAVAIL;
979             }
980
981             if ((!IsEqualGUID(&pwfxe->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM)) && (!IsEqualGUID(&pwfxe->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)))
982             {
983                 if (!IsEqualGUID(&pwfxe->SubFormat, &GUID_NULL))
984                     FIXME("SubFormat %s not supported right now.\n", debugstr_guid(&pwfxe->SubFormat));
985                 return DSERR_INVALIDPARAM;
986             }
987             if (pwfxe->Samples.wValidBitsPerSample > dsbd->lpwfxFormat->wBitsPerSample)
988             {
989                 WARN("Samples.wValidBitsPerSample(%d) > Format.wBitsPerSample (%d)\n", pwfxe->Samples.wValidBitsPerSample, pwfxe->Format.wBitsPerSample);
990                 return DSERR_INVALIDPARAM;
991             }
992             if (pwfxe->Samples.wValidBitsPerSample && pwfxe->Samples.wValidBitsPerSample < dsbd->lpwfxFormat->wBitsPerSample)
993             {
994                 FIXME("Non-packed formats not supported right now: %d/%d\n", pwfxe->Samples.wValidBitsPerSample, dsbd->lpwfxFormat->wBitsPerSample);
995                 return DSERR_CONTROLUNAVAIL;
996             }
997         }
998
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);
1006
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;
1010         }
1011
1012         hres = IDirectSoundBufferImpl_Create(device, &dsb, dsbd);
1013         if (dsb)
1014             *ppdsb = (IDirectSoundBuffer*)&dsb->IDirectSoundBuffer8_iface;
1015         else
1016             WARN("IDirectSoundBufferImpl_Create failed\n");
1017    }
1018
1019    return hres;
1020 }
1021
1022 HRESULT DirectSoundDevice_DuplicateSoundBuffer(
1023     DirectSoundDevice * device,
1024     LPDIRECTSOUNDBUFFER psb,
1025     LPLPDIRECTSOUNDBUFFER ppdsb)
1026 {
1027     HRESULT hres = DS_OK;
1028     IDirectSoundBufferImpl* dsb;
1029     TRACE("(%p,%p,%p)\n",device,psb,ppdsb);
1030
1031     if (device == NULL) {
1032         WARN("not initialized\n");
1033         return DSERR_UNINITIALIZED;
1034     }
1035
1036     if (psb == NULL) {
1037         WARN("invalid parameter: psb == NULL\n");
1038         return DSERR_INVALIDPARAM;
1039     }
1040
1041     if (ppdsb == NULL) {
1042         WARN("invalid parameter: ppdsb == NULL\n");
1043         return DSERR_INVALIDPARAM;
1044     }
1045
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;
1051     }
1052
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");
1059
1060     return hres;
1061 }
1062
1063 /*
1064  * Add secondary buffer to buffer list.
1065  * Gets exclusive access to buffer for writing.
1066  */
1067 HRESULT DirectSoundDevice_AddBuffer(
1068     DirectSoundDevice * device,
1069     IDirectSoundBufferImpl * pDSB)
1070 {
1071     IDirectSoundBufferImpl **newbuffers;
1072     HRESULT hr = DS_OK;
1073
1074     TRACE("(%p, %p)\n", device, pDSB);
1075
1076     RtlAcquireResourceExclusive(&(device->buffer_list_lock), TRUE);
1077
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));
1082
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;
1091     }
1092
1093     RtlReleaseResource(&(device->buffer_list_lock));
1094
1095     return hr;
1096 }
1097
1098 /*
1099  * Remove secondary buffer from buffer list.
1100  * Gets exclusive access to buffer for writing.
1101  */
1102 void DirectSoundDevice_RemoveBuffer(DirectSoundDevice * device, IDirectSoundBufferImpl * pDSB)
1103 {
1104     int i;
1105
1106     TRACE("(%p, %p)\n", device, pDSB);
1107
1108     RtlAcquireResourceExclusive(&(device->buffer_list_lock), TRUE);
1109
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;
1120             }
1121         }
1122     }
1123     device->nrofbuffers--;
1124     TRACE("buffer count is now %d\n", device->nrofbuffers);
1125
1126     RtlReleaseResource(&(device->buffer_list_lock));
1127 }