atl80: Added AtlComModuleRegisterServer implementation (based on AtlModuleRegisterSer...
[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
674         SetEvent(device->sleepev);
675         if (device->thread) {
676             WaitForSingleObject(device->thread, INFINITE);
677             CloseHandle(device->thread);
678         }
679         CloseHandle(device->sleepev);
680
681         EnterCriticalSection(&DSOUND_renderers_lock);
682         list_remove(&device->entry);
683         LeaveCriticalSection(&DSOUND_renderers_lock);
684
685         /* It is allowed to release this object even when buffers are playing */
686         if (device->buffers) {
687             WARN("%d secondary buffers not released\n", device->nrofbuffers);
688             for( i=0;i<device->nrofbuffers;i++)
689                 secondarybuffer_destroy(device->buffers[i]);
690         }
691
692         hr = DSOUND_PrimaryDestroy(device);
693         if (hr != DS_OK)
694             WARN("DSOUND_PrimaryDestroy failed\n");
695
696         if(device->client)
697             IAudioClient_Release(device->client);
698         if(device->render)
699             IAudioRenderClient_Release(device->render);
700         if(device->clock)
701             IAudioClock_Release(device->clock);
702         if(device->volume)
703             IAudioStreamVolume_Release(device->volume);
704
705         HeapFree(GetProcessHeap(), 0, device->tmp_buffer);
706         HeapFree(GetProcessHeap(), 0, device->mix_buffer);
707         HeapFree(GetProcessHeap(), 0, device->buffer);
708         RtlDeleteResource(&device->buffer_list_lock);
709         device->mixlock.DebugInfo->Spare[0] = 0;
710         DeleteCriticalSection(&device->mixlock);
711         HeapFree(GetProcessHeap(),0,device);
712         TRACE("(%p) released\n", device);
713     }
714     return ref;
715 }
716
717 BOOL DSOUND_check_supported(IAudioClient *client, DWORD rate,
718         DWORD depth, WORD channels)
719 {
720     WAVEFORMATEX fmt, *junk;
721     HRESULT hr;
722
723     fmt.wFormatTag = WAVE_FORMAT_PCM;
724     fmt.nChannels = channels;
725     fmt.nSamplesPerSec = rate;
726     fmt.wBitsPerSample = depth;
727     fmt.nBlockAlign = (channels * depth) / 8;
728     fmt.nAvgBytesPerSec = rate * fmt.nBlockAlign;
729     fmt.cbSize = 0;
730
731     hr = IAudioClient_IsFormatSupported(client, AUDCLNT_SHAREMODE_SHARED, &fmt, &junk);
732     if(SUCCEEDED(hr))
733         CoTaskMemFree(junk);
734
735     return hr == S_OK;
736 }
737
738 UINT DSOUND_create_timer(LPTIMECALLBACK cb, DWORD_PTR user)
739 {
740     UINT triggertime = DS_TIME_DEL, res = DS_TIME_RES, id;
741     TIMECAPS time;
742
743     timeGetDevCaps(&time, sizeof(TIMECAPS));
744     TRACE("Minimum timer resolution: %u, max timer: %u\n", time.wPeriodMin, time.wPeriodMax);
745     if (triggertime < time.wPeriodMin)
746         triggertime = time.wPeriodMin;
747     if (res < time.wPeriodMin)
748         res = time.wPeriodMin;
749     if (timeBeginPeriod(res) == TIMERR_NOCANDO)
750         WARN("Could not set minimum resolution, don't expect sound\n");
751     id = timeSetEvent(triggertime, res, cb, user, TIME_PERIODIC | TIME_KILL_SYNCHRONOUS);
752     if (!id)
753     {
754         WARN("Timer not created! Retrying without TIME_KILL_SYNCHRONOUS\n");
755         id = timeSetEvent(triggertime, res, cb, user, TIME_PERIODIC);
756         if (!id)
757             ERR("Could not create timer, sound playback will not occur\n");
758     }
759     return id;
760 }
761
762 HRESULT DirectSoundDevice_Initialize(DirectSoundDevice ** ppDevice, LPCGUID lpcGUID)
763 {
764     HRESULT hr = DS_OK;
765     GUID devGUID;
766     DirectSoundDevice *device;
767     IMMDevice *mmdevice;
768
769     TRACE("(%p,%s)\n",ppDevice,debugstr_guid(lpcGUID));
770
771     if (*ppDevice != NULL) {
772         WARN("already initialized\n");
773         return DSERR_ALREADYINITIALIZED;
774     }
775
776     /* Default device? */
777     if (!lpcGUID || IsEqualGUID(lpcGUID, &GUID_NULL))
778         lpcGUID = &DSDEVID_DefaultPlayback;
779
780     if(IsEqualGUID(lpcGUID, &DSDEVID_DefaultCapture) ||
781             IsEqualGUID(lpcGUID, &DSDEVID_DefaultVoiceCapture))
782         return DSERR_NODRIVER;
783
784     if (GetDeviceID(lpcGUID, &devGUID) != DS_OK) {
785         WARN("invalid parameter: lpcGUID\n");
786         return DSERR_INVALIDPARAM;
787     }
788
789     hr = get_mmdevice(eRender, &devGUID, &mmdevice);
790     if(FAILED(hr))
791         return hr;
792
793     EnterCriticalSection(&DSOUND_renderers_lock);
794
795     LIST_FOR_EACH_ENTRY(device, &DSOUND_renderers, DirectSoundDevice, entry){
796         if(IsEqualGUID(&device->guid, &devGUID)){
797             IMMDevice_Release(mmdevice);
798             DirectSoundDevice_AddRef(device);
799             *ppDevice = device;
800             LeaveCriticalSection(&DSOUND_renderers_lock);
801             return DS_OK;
802         }
803     }
804
805     hr = DirectSoundDevice_Create(&device);
806     if(FAILED(hr)){
807         WARN("DirectSoundDevice_Create failed\n");
808         IMMDevice_Release(mmdevice);
809         LeaveCriticalSection(&DSOUND_renderers_lock);
810         return hr;
811     }
812
813     device->mmdevice = mmdevice;
814     device->guid = devGUID;
815     device->sleepev = CreateEventW(0, 0, 0, 0);
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->thread = CreateThread(0, 0, DSOUND_mixthread, device, 0, 0);
874         SetThreadPriority(device->thread, THREAD_PRIORITY_TIME_CRITICAL);
875     } else
876         WARN("DSOUND_PrimaryCreate failed: %08x\n", hr);
877
878     *ppDevice = device;
879     list_add_tail(&DSOUND_renderers, &device->entry);
880
881     LeaveCriticalSection(&DSOUND_renderers_lock);
882
883     return hr;
884 }
885
886 HRESULT DirectSoundDevice_CreateSoundBuffer(
887     DirectSoundDevice * device,
888     LPCDSBUFFERDESC dsbd,
889     LPLPDIRECTSOUNDBUFFER ppdsb,
890     LPUNKNOWN lpunk,
891     BOOL from8)
892 {
893     HRESULT hres = DS_OK;
894     TRACE("(%p,%p,%p,%p)\n",device,dsbd,ppdsb,lpunk);
895
896     if (device == NULL) {
897         WARN("not initialized\n");
898         return DSERR_UNINITIALIZED;
899     }
900
901     if (dsbd == NULL) {
902         WARN("invalid parameter: dsbd == NULL\n");
903         return DSERR_INVALIDPARAM;
904     }
905
906     if (dsbd->dwSize != sizeof(DSBUFFERDESC) &&
907         dsbd->dwSize != sizeof(DSBUFFERDESC1)) {
908         WARN("invalid parameter: dsbd\n");
909         return DSERR_INVALIDPARAM;
910     }
911
912     if (ppdsb == NULL) {
913         WARN("invalid parameter: ppdsb == NULL\n");
914         return DSERR_INVALIDPARAM;
915     }
916     *ppdsb = NULL;
917
918     if (TRACE_ON(dsound)) {
919         TRACE("(structsize=%d)\n",dsbd->dwSize);
920         TRACE("(flags=0x%08x:\n",dsbd->dwFlags);
921         _dump_DSBCAPS(dsbd->dwFlags);
922         TRACE(")\n");
923         TRACE("(bufferbytes=%d)\n",dsbd->dwBufferBytes);
924         TRACE("(lpwfxFormat=%p)\n",dsbd->lpwfxFormat);
925     }
926
927     if (dsbd->dwFlags & DSBCAPS_LOCHARDWARE &&
928             !(dsbd->dwFlags & DSBCAPS_PRIMARYBUFFER)) {
929         TRACE("LOCHARDWARE is not supported, returning E_NOTIMPL\n");
930         return E_NOTIMPL;
931     }
932
933     if (dsbd->dwFlags & DSBCAPS_PRIMARYBUFFER) {
934         if (dsbd->lpwfxFormat != NULL) {
935             WARN("invalid parameter: dsbd->lpwfxFormat must be NULL for "
936                  "primary buffer\n");
937             return DSERR_INVALIDPARAM;
938         }
939
940         if (device->primary) {
941             WARN("Primary Buffer already created\n");
942             IDirectSoundBuffer_AddRef((LPDIRECTSOUNDBUFFER8)(device->primary));
943             *ppdsb = (LPDIRECTSOUNDBUFFER)(device->primary);
944         } else {
945             hres = primarybuffer_create(device, &device->primary, dsbd);
946             if (device->primary) {
947                 *ppdsb = (IDirectSoundBuffer*)&device->primary->IDirectSoundBuffer8_iface;
948                 device->primary->dsbd.dwFlags &= ~(DSBCAPS_LOCHARDWARE | DSBCAPS_LOCSOFTWARE);
949                 device->primary->dsbd.dwFlags |= DSBCAPS_LOCSOFTWARE;
950             } else
951                 WARN("primarybuffer_create() failed\n");
952         }
953     } else {
954         IDirectSoundBufferImpl * dsb;
955         WAVEFORMATEXTENSIBLE *pwfxe;
956
957         if (dsbd->lpwfxFormat == NULL) {
958             WARN("invalid parameter: dsbd->lpwfxFormat can't be NULL for "
959                  "secondary buffer\n");
960             return DSERR_INVALIDPARAM;
961         }
962         pwfxe = (WAVEFORMATEXTENSIBLE*)dsbd->lpwfxFormat;
963
964         if (pwfxe->Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE)
965         {
966             /* check if cbSize is at least 22 bytes */
967             if (pwfxe->Format.cbSize < (sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX)))
968             {
969                 WARN("Too small a cbSize %u\n", pwfxe->Format.cbSize);
970                 return DSERR_INVALIDPARAM;
971             }
972
973             /* cbSize should be 22 bytes, with one possible exception */
974             if (pwfxe->Format.cbSize > (sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX)) &&
975                 !((IsEqualGUID(&pwfxe->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM) || IsEqualGUID(&pwfxe->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)) &&
976                 pwfxe->Format.cbSize == sizeof(WAVEFORMATEXTENSIBLE)))
977             {
978                 WARN("Too big a cbSize %u\n", pwfxe->Format.cbSize);
979                 return DSERR_CONTROLUNAVAIL;
980             }
981
982             if ((!IsEqualGUID(&pwfxe->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM)) && (!IsEqualGUID(&pwfxe->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)))
983             {
984                 if (!IsEqualGUID(&pwfxe->SubFormat, &GUID_NULL))
985                     FIXME("SubFormat %s not supported right now.\n", debugstr_guid(&pwfxe->SubFormat));
986                 return DSERR_INVALIDPARAM;
987             }
988             if (pwfxe->Samples.wValidBitsPerSample > dsbd->lpwfxFormat->wBitsPerSample)
989             {
990                 WARN("Samples.wValidBitsPerSample(%d) > Format.wBitsPerSample (%d)\n", pwfxe->Samples.wValidBitsPerSample, pwfxe->Format.wBitsPerSample);
991                 return DSERR_INVALIDPARAM;
992             }
993             if (pwfxe->Samples.wValidBitsPerSample && pwfxe->Samples.wValidBitsPerSample < dsbd->lpwfxFormat->wBitsPerSample)
994             {
995                 FIXME("Non-packed formats not supported right now: %d/%d\n", pwfxe->Samples.wValidBitsPerSample, dsbd->lpwfxFormat->wBitsPerSample);
996                 return DSERR_CONTROLUNAVAIL;
997             }
998         }
999
1000         TRACE("(formattag=0x%04x,chans=%d,samplerate=%d,"
1001               "bytespersec=%d,blockalign=%d,bitspersamp=%d,cbSize=%d)\n",
1002               dsbd->lpwfxFormat->wFormatTag, dsbd->lpwfxFormat->nChannels,
1003               dsbd->lpwfxFormat->nSamplesPerSec,
1004               dsbd->lpwfxFormat->nAvgBytesPerSec,
1005               dsbd->lpwfxFormat->nBlockAlign,
1006               dsbd->lpwfxFormat->wBitsPerSample, dsbd->lpwfxFormat->cbSize);
1007
1008         if (from8 && (dsbd->dwFlags & DSBCAPS_CTRL3D) && (dsbd->lpwfxFormat->nChannels != 1)) {
1009             WARN("invalid parameter: 3D buffer format must be mono\n");
1010             return DSERR_INVALIDPARAM;
1011         }
1012
1013         hres = IDirectSoundBufferImpl_Create(device, &dsb, dsbd);
1014         if (dsb)
1015             *ppdsb = (IDirectSoundBuffer*)&dsb->IDirectSoundBuffer8_iface;
1016         else
1017             WARN("IDirectSoundBufferImpl_Create failed\n");
1018    }
1019
1020    return hres;
1021 }
1022
1023 HRESULT DirectSoundDevice_DuplicateSoundBuffer(
1024     DirectSoundDevice * device,
1025     LPDIRECTSOUNDBUFFER psb,
1026     LPLPDIRECTSOUNDBUFFER ppdsb)
1027 {
1028     HRESULT hres = DS_OK;
1029     IDirectSoundBufferImpl* dsb;
1030     TRACE("(%p,%p,%p)\n",device,psb,ppdsb);
1031
1032     if (device == NULL) {
1033         WARN("not initialized\n");
1034         return DSERR_UNINITIALIZED;
1035     }
1036
1037     if (psb == NULL) {
1038         WARN("invalid parameter: psb == NULL\n");
1039         return DSERR_INVALIDPARAM;
1040     }
1041
1042     if (ppdsb == NULL) {
1043         WARN("invalid parameter: ppdsb == NULL\n");
1044         return DSERR_INVALIDPARAM;
1045     }
1046
1047     /* make sure we have a secondary buffer */
1048     if (psb == (IDirectSoundBuffer *)&device->primary->IDirectSoundBuffer8_iface) {
1049         WARN("trying to duplicate primary buffer\n");
1050         *ppdsb = NULL;
1051         return DSERR_INVALIDCALL;
1052     }
1053
1054     /* duplicate the actual buffer implementation */
1055     hres = IDirectSoundBufferImpl_Duplicate(device, &dsb, (IDirectSoundBufferImpl*)psb);
1056     if (hres == DS_OK)
1057         *ppdsb = (IDirectSoundBuffer*)&dsb->IDirectSoundBuffer8_iface;
1058     else
1059         WARN("IDirectSoundBufferImpl_Duplicate failed\n");
1060
1061     return hres;
1062 }
1063
1064 /*
1065  * Add secondary buffer to buffer list.
1066  * Gets exclusive access to buffer for writing.
1067  */
1068 HRESULT DirectSoundDevice_AddBuffer(
1069     DirectSoundDevice * device,
1070     IDirectSoundBufferImpl * pDSB)
1071 {
1072     IDirectSoundBufferImpl **newbuffers;
1073     HRESULT hr = DS_OK;
1074
1075     TRACE("(%p, %p)\n", device, pDSB);
1076
1077     RtlAcquireResourceExclusive(&(device->buffer_list_lock), TRUE);
1078
1079     if (device->buffers)
1080         newbuffers = HeapReAlloc(GetProcessHeap(),0,device->buffers,sizeof(IDirectSoundBufferImpl*)*(device->nrofbuffers+1));
1081     else
1082         newbuffers = HeapAlloc(GetProcessHeap(),0,sizeof(IDirectSoundBufferImpl*)*(device->nrofbuffers+1));
1083
1084     if (newbuffers) {
1085         device->buffers = newbuffers;
1086         device->buffers[device->nrofbuffers] = pDSB;
1087         device->nrofbuffers++;
1088         TRACE("buffer count is now %d\n", device->nrofbuffers);
1089     } else {
1090         ERR("out of memory for buffer list! Current buffer count is %d\n", device->nrofbuffers);
1091         hr = DSERR_OUTOFMEMORY;
1092     }
1093
1094     RtlReleaseResource(&(device->buffer_list_lock));
1095
1096     return hr;
1097 }
1098
1099 /*
1100  * Remove secondary buffer from buffer list.
1101  * Gets exclusive access to buffer for writing.
1102  */
1103 void DirectSoundDevice_RemoveBuffer(DirectSoundDevice * device, IDirectSoundBufferImpl * pDSB)
1104 {
1105     int i;
1106
1107     TRACE("(%p, %p)\n", device, pDSB);
1108
1109     RtlAcquireResourceExclusive(&(device->buffer_list_lock), TRUE);
1110
1111     if (device->nrofbuffers == 1) {
1112         assert(device->buffers[0] == pDSB);
1113         HeapFree(GetProcessHeap(), 0, device->buffers);
1114         device->buffers = NULL;
1115     } else {
1116         for (i = 0; i < device->nrofbuffers; i++) {
1117             if (device->buffers[i] == pDSB) {
1118                 /* Put the last buffer of the list in the (now empty) position */
1119                 device->buffers[i] = device->buffers[device->nrofbuffers - 1];
1120                 break;
1121             }
1122         }
1123     }
1124     device->nrofbuffers--;
1125     TRACE("buffer count is now %d\n", device->nrofbuffers);
1126
1127     RtlReleaseResource(&(device->buffer_list_lock));
1128 }