msvcp90: Fixed _Getcat return type.
[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 <stdarg.h>
24 #include <stdio.h>
25
26 #define COBJMACROS
27 #define NONAMELESSSTRUCT
28 #define NONAMELESSUNION
29 #include "windef.h"
30 #include "winbase.h"
31 #include "winuser.h"
32 #include "winternl.h"
33 #include "mmddk.h"
34 #include "wingdi.h"
35 #include "mmreg.h"
36 #include "ks.h"
37 #include "ksmedia.h"
38 #include "wine/debug.h"
39 #include "dsound.h"
40 #include "dsound_private.h"
41
42 WINE_DEFAULT_DEBUG_CHANNEL(dsound);
43
44 typedef struct IDirectSoundImpl {
45     IUnknown            IUnknown_inner;
46     IDirectSound8       IDirectSound8_iface;
47     IUnknown           *outer_unk;      /* internal */
48     LONG                ref, refds, numIfaces;
49     DirectSoundDevice  *device;
50     BOOL                has_ds8;
51 } IDirectSoundImpl;
52
53 static const char * dumpCooperativeLevel(DWORD level)
54 {
55 #define LE(x) case x: return #x
56     switch (level) {
57         LE(DSSCL_NORMAL);
58         LE(DSSCL_PRIORITY);
59         LE(DSSCL_EXCLUSIVE);
60         LE(DSSCL_WRITEPRIMARY);
61     }
62 #undef LE
63     return wine_dbg_sprintf("Unknown(%08x)", level);
64 }
65
66 static void _dump_DSCAPS(DWORD xmask) {
67     struct {
68         DWORD   mask;
69         const char    *name;
70     } flags[] = {
71 #define FE(x) { x, #x },
72         FE(DSCAPS_PRIMARYMONO)
73         FE(DSCAPS_PRIMARYSTEREO)
74         FE(DSCAPS_PRIMARY8BIT)
75         FE(DSCAPS_PRIMARY16BIT)
76         FE(DSCAPS_CONTINUOUSRATE)
77         FE(DSCAPS_EMULDRIVER)
78         FE(DSCAPS_CERTIFIED)
79         FE(DSCAPS_SECONDARYMONO)
80         FE(DSCAPS_SECONDARYSTEREO)
81         FE(DSCAPS_SECONDARY8BIT)
82         FE(DSCAPS_SECONDARY16BIT)
83 #undef FE
84     };
85     unsigned int     i;
86
87     for (i=0;i<sizeof(flags)/sizeof(flags[0]);i++)
88         if ((flags[i].mask & xmask) == flags[i].mask)
89             TRACE("%s ",flags[i].name);
90 }
91
92 static void _dump_DSBCAPS(DWORD xmask) {
93     struct {
94         DWORD   mask;
95         const char    *name;
96     } flags[] = {
97 #define FE(x) { x, #x },
98         FE(DSBCAPS_PRIMARYBUFFER)
99         FE(DSBCAPS_STATIC)
100         FE(DSBCAPS_LOCHARDWARE)
101         FE(DSBCAPS_LOCSOFTWARE)
102         FE(DSBCAPS_CTRL3D)
103         FE(DSBCAPS_CTRLFREQUENCY)
104         FE(DSBCAPS_CTRLPAN)
105         FE(DSBCAPS_CTRLVOLUME)
106         FE(DSBCAPS_CTRLPOSITIONNOTIFY)
107         FE(DSBCAPS_STICKYFOCUS)
108         FE(DSBCAPS_GLOBALFOCUS)
109         FE(DSBCAPS_GETCURRENTPOSITION2)
110         FE(DSBCAPS_MUTE3DATMAXDISTANCE)
111 #undef FE
112     };
113     unsigned int     i;
114
115     for (i=0;i<sizeof(flags)/sizeof(flags[0]);i++)
116         if ((flags[i].mask & xmask) == flags[i].mask)
117             TRACE("%s ",flags[i].name);
118 }
119
120 static void directsound_destroy(IDirectSoundImpl *This)
121 {
122     if (This->device)
123         DirectSoundDevice_Release(This->device);
124     HeapFree(GetProcessHeap(),0,This);
125     TRACE("(%p) released\n", This);
126 }
127
128 /*******************************************************************************
129  *      IUnknown Implementation for DirectSound
130  */
131 static inline IDirectSoundImpl *impl_from_IUnknown(IUnknown *iface)
132 {
133     return CONTAINING_RECORD(iface, IDirectSoundImpl, IUnknown_inner);
134 }
135
136 static HRESULT WINAPI IUnknownImpl_QueryInterface(IUnknown *iface, REFIID riid, void **ppv)
137 {
138     IDirectSoundImpl *This = impl_from_IUnknown(iface);
139
140     TRACE("(%p,%s,%p)\n", This, debugstr_guid(riid), ppv);
141
142     if (!ppv) {
143         WARN("invalid parameter\n");
144         return E_INVALIDARG;
145     }
146     *ppv = NULL;
147
148     if (IsEqualIID(riid, &IID_IUnknown))
149         *ppv = &This->IUnknown_inner;
150     else if (IsEqualIID(riid, &IID_IDirectSound) ||
151             (IsEqualIID(riid, &IID_IDirectSound8) && This->has_ds8))
152         *ppv = &This->IDirectSound8_iface;
153     else {
154         WARN("unknown IID %s\n", debugstr_guid(riid));
155         return E_NOINTERFACE;
156     }
157
158     IUnknown_AddRef((IUnknown*)*ppv);
159     return S_OK;
160 }
161
162 static ULONG WINAPI IUnknownImpl_AddRef(IUnknown *iface)
163 {
164     IDirectSoundImpl *This = impl_from_IUnknown(iface);
165     ULONG ref = InterlockedIncrement(&This->ref);
166
167     TRACE("(%p) ref=%d\n", This, ref);
168
169     if(ref == 1)
170         InterlockedIncrement(&This->numIfaces);
171
172     return ref;
173 }
174
175 static ULONG WINAPI IUnknownImpl_Release(IUnknown *iface)
176 {
177     IDirectSoundImpl *This = impl_from_IUnknown(iface);
178     ULONG ref = InterlockedDecrement(&This->ref);
179
180     TRACE("(%p) ref=%d\n", This, ref);
181
182     if (!ref && !InterlockedDecrement(&This->numIfaces))
183         directsound_destroy(This);
184
185     return ref;
186 }
187
188 static const IUnknownVtbl unk_vtbl =
189 {
190     IUnknownImpl_QueryInterface,
191     IUnknownImpl_AddRef,
192     IUnknownImpl_Release
193 };
194
195 /*******************************************************************************
196  *      IDirectSound and IDirectSound8 Implementation
197  */
198 static inline IDirectSoundImpl *impl_from_IDirectSound8(IDirectSound8 *iface)
199 {
200     return CONTAINING_RECORD(iface, IDirectSoundImpl, IDirectSound8_iface);
201 }
202
203 static HRESULT WINAPI IDirectSound8Impl_QueryInterface(IDirectSound8 *iface, REFIID riid,
204         void **ppv)
205 {
206     IDirectSoundImpl *This = impl_from_IDirectSound8(iface);
207     TRACE("(%p,%s,%p)\n", This, debugstr_guid(riid), ppv);
208     return IUnknown_QueryInterface(This->outer_unk, riid, ppv);
209 }
210
211 static ULONG WINAPI IDirectSound8Impl_AddRef(IDirectSound8 *iface)
212 {
213     IDirectSoundImpl *This = impl_from_IDirectSound8(iface);
214     ULONG ref = InterlockedIncrement(&This->refds);
215
216     TRACE("(%p) refds=%d\n", This, ref);
217
218     if(ref == 1)
219         InterlockedIncrement(&This->numIfaces);
220
221     return ref;
222 }
223
224 static ULONG WINAPI IDirectSound8Impl_Release(IDirectSound8 *iface)
225 {
226     IDirectSoundImpl *This = impl_from_IDirectSound8(iface);
227     ULONG ref = InterlockedDecrement(&(This->refds));
228
229     TRACE("(%p) refds=%d\n", This, ref);
230
231     if (!ref && !InterlockedDecrement(&This->numIfaces))
232         directsound_destroy(This);
233
234     return ref;
235 }
236
237 static HRESULT WINAPI IDirectSound8Impl_CreateSoundBuffer(IDirectSound8 *iface,
238         const DSBUFFERDESC *dsbd, IDirectSoundBuffer **ppdsb, IUnknown *lpunk)
239 {
240     IDirectSoundImpl *This = impl_from_IDirectSound8(iface);
241     TRACE("(%p,%p,%p,%p)\n", This, dsbd, ppdsb, lpunk);
242     return DirectSoundDevice_CreateSoundBuffer(This->device, dsbd, ppdsb, lpunk, This->has_ds8);
243 }
244
245 static HRESULT WINAPI IDirectSound8Impl_GetCaps(IDirectSound8 *iface, DSCAPS *dscaps)
246 {
247     IDirectSoundImpl *This = impl_from_IDirectSound8(iface);
248
249     TRACE("(%p, %p)\n", This, dscaps);
250
251     if (!This->device) {
252         WARN("not initialized\n");
253         return DSERR_UNINITIALIZED;
254     }
255     if (!dscaps) {
256         WARN("invalid parameter: dscaps = NULL\n");
257         return DSERR_INVALIDPARAM;
258     }
259     if (dscaps->dwSize < sizeof(*dscaps)) {
260         WARN("invalid parameter: dscaps->dwSize = %d\n", dscaps->dwSize);
261         return DSERR_INVALIDPARAM;
262     }
263
264     dscaps->dwFlags                        = This->device->drvcaps.dwFlags;
265     dscaps->dwMinSecondarySampleRate       = This->device->drvcaps.dwMinSecondarySampleRate;
266     dscaps->dwMaxSecondarySampleRate       = This->device->drvcaps.dwMaxSecondarySampleRate;
267     dscaps->dwPrimaryBuffers               = This->device->drvcaps.dwPrimaryBuffers;
268     dscaps->dwMaxHwMixingAllBuffers        = This->device->drvcaps.dwMaxHwMixingAllBuffers;
269     dscaps->dwMaxHwMixingStaticBuffers     = This->device->drvcaps.dwMaxHwMixingStaticBuffers;
270     dscaps->dwMaxHwMixingStreamingBuffers  = This->device->drvcaps.dwMaxHwMixingStreamingBuffers;
271     dscaps->dwFreeHwMixingAllBuffers       = This->device->drvcaps.dwFreeHwMixingAllBuffers;
272     dscaps->dwFreeHwMixingStaticBuffers    = This->device->drvcaps.dwFreeHwMixingStaticBuffers;
273     dscaps->dwFreeHwMixingStreamingBuffers = This->device->drvcaps.dwFreeHwMixingStreamingBuffers;
274     dscaps->dwMaxHw3DAllBuffers            = This->device->drvcaps.dwMaxHw3DAllBuffers;
275     dscaps->dwMaxHw3DStaticBuffers         = This->device->drvcaps.dwMaxHw3DStaticBuffers;
276     dscaps->dwMaxHw3DStreamingBuffers      = This->device->drvcaps.dwMaxHw3DStreamingBuffers;
277     dscaps->dwFreeHw3DAllBuffers           = This->device->drvcaps.dwFreeHw3DAllBuffers;
278     dscaps->dwFreeHw3DStaticBuffers        = This->device->drvcaps.dwFreeHw3DStaticBuffers;
279     dscaps->dwFreeHw3DStreamingBuffers     = This->device->drvcaps.dwFreeHw3DStreamingBuffers;
280     dscaps->dwTotalHwMemBytes              = This->device->drvcaps.dwTotalHwMemBytes;
281     dscaps->dwFreeHwMemBytes               = This->device->drvcaps.dwFreeHwMemBytes;
282     dscaps->dwMaxContigFreeHwMemBytes      = This->device->drvcaps.dwMaxContigFreeHwMemBytes;
283     dscaps->dwUnlockTransferRateHwBuffers  = This->device->drvcaps.dwUnlockTransferRateHwBuffers;
284     dscaps->dwPlayCpuOverheadSwBuffers     = This->device->drvcaps.dwPlayCpuOverheadSwBuffers;
285
286     if (TRACE_ON(dsound)) {
287         TRACE("(flags=0x%08x:\n", dscaps->dwFlags);
288         _dump_DSCAPS(dscaps->dwFlags);
289         TRACE(")\n");
290     }
291
292     return DS_OK;
293 }
294
295 static HRESULT WINAPI IDirectSound8Impl_DuplicateSoundBuffer(IDirectSound8 *iface,
296         IDirectSoundBuffer *psb, IDirectSoundBuffer **ppdsb)
297 {
298     IDirectSoundImpl *This = impl_from_IDirectSound8(iface);
299     TRACE("(%p,%p,%p)\n", This, psb, ppdsb);
300     return DirectSoundDevice_DuplicateSoundBuffer(This->device, psb, ppdsb);
301 }
302
303 static HRESULT WINAPI IDirectSound8Impl_SetCooperativeLevel(IDirectSound8 *iface, HWND hwnd,
304         DWORD level)
305 {
306     IDirectSoundImpl *This = impl_from_IDirectSound8(iface);
307
308     TRACE("(%p,%p,%s)\n", This, hwnd, dumpCooperativeLevel(level));
309
310     if (!This->device) {
311         WARN("not initialized\n");
312         return DSERR_UNINITIALIZED;
313     }
314
315     if (level == DSSCL_PRIORITY || level == DSSCL_EXCLUSIVE) {
316         WARN("level=%s not fully supported\n",
317                 level == DSSCL_PRIORITY ? "DSSCL_PRIORITY" : "DSSCL_EXCLUSIVE");
318     }
319
320     This->device->priolevel = level;
321     return DS_OK;
322 }
323
324 static HRESULT WINAPI IDirectSound8Impl_Compact(IDirectSound8 *iface)
325 {
326     IDirectSoundImpl *This = impl_from_IDirectSound8(iface);
327
328     TRACE("(%p)\n", This);
329
330     if (!This->device) {
331         WARN("not initialized\n");
332         return DSERR_UNINITIALIZED;
333     }
334
335     if (This->device->priolevel < DSSCL_PRIORITY) {
336         WARN("incorrect priority level\n");
337         return DSERR_PRIOLEVELNEEDED;
338     }
339     return DS_OK;
340 }
341
342 static HRESULT WINAPI IDirectSound8Impl_GetSpeakerConfig(IDirectSound8 *iface, DWORD *config)
343 {
344     IDirectSoundImpl *This = impl_from_IDirectSound8(iface);
345
346     TRACE("(%p, %p)\n", This, config);
347
348     if (!This->device) {
349         WARN("not initialized\n");
350         return DSERR_UNINITIALIZED;
351     }
352     if (!config) {
353         WARN("invalid parameter: config == NULL\n");
354         return DSERR_INVALIDPARAM;
355     }
356
357     WARN("not fully functional\n");
358     *config = This->device->speaker_config;
359     return DS_OK;
360 }
361
362 static HRESULT WINAPI IDirectSound8Impl_SetSpeakerConfig(IDirectSound8 *iface, DWORD config)
363 {
364     IDirectSoundImpl *This = impl_from_IDirectSound8(iface);
365
366     TRACE("(%p,0x%08x)\n", This, config);
367
368     if (!This->device) {
369         WARN("not initialized\n");
370         return DSERR_UNINITIALIZED;
371     }
372
373     This->device->speaker_config = config;
374     WARN("not fully functional\n");
375     return DS_OK;
376 }
377
378 static HRESULT WINAPI IDirectSound8Impl_Initialize(IDirectSound8 *iface, const GUID *lpcGuid)
379 {
380     IDirectSoundImpl *This = impl_from_IDirectSound8(iface);
381     TRACE("(%p, %s)\n", This, debugstr_guid(lpcGuid));
382     return DirectSoundDevice_Initialize(&This->device, lpcGuid);
383 }
384
385 static HRESULT WINAPI IDirectSound8Impl_VerifyCertification(IDirectSound8 *iface, DWORD *certified)
386 {
387     IDirectSoundImpl *This = impl_from_IDirectSound8(iface);
388
389     TRACE("(%p, %p)\n", This, certified);
390
391     if (!This->device) {
392         WARN("not initialized\n");
393         return DSERR_UNINITIALIZED;
394     }
395
396     if (This->device->drvcaps.dwFlags & DSCAPS_CERTIFIED)
397         *certified = DS_CERTIFIED;
398     else
399         *certified = DS_UNCERTIFIED;
400
401     return DS_OK;
402 }
403
404 static const IDirectSound8Vtbl ds8_vtbl =
405 {
406     IDirectSound8Impl_QueryInterface,
407     IDirectSound8Impl_AddRef,
408     IDirectSound8Impl_Release,
409     IDirectSound8Impl_CreateSoundBuffer,
410     IDirectSound8Impl_GetCaps,
411     IDirectSound8Impl_DuplicateSoundBuffer,
412     IDirectSound8Impl_SetCooperativeLevel,
413     IDirectSound8Impl_Compact,
414     IDirectSound8Impl_GetSpeakerConfig,
415     IDirectSound8Impl_SetSpeakerConfig,
416     IDirectSound8Impl_Initialize,
417     IDirectSound8Impl_VerifyCertification
418 };
419
420 HRESULT IDirectSoundImpl_Create(IUnknown *outer_unk, REFIID riid, void **ppv, BOOL has_ds8)
421 {
422     IDirectSoundImpl *obj;
423     HRESULT hr;
424
425     TRACE("(%s, %p)\n", debugstr_guid(riid), ppv);
426
427     *ppv = NULL;
428     obj = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*obj));
429     if (!obj) {
430         WARN("out of memory\n");
431         return DSERR_OUTOFMEMORY;
432     }
433
434     setup_dsound_options();
435
436     obj->IUnknown_inner.lpVtbl = &unk_vtbl;
437     obj->IDirectSound8_iface.lpVtbl = &ds8_vtbl;
438     obj->ref = 1;
439     obj->refds = 0;
440     obj->numIfaces = 1;
441     obj->device = NULL;
442     obj->has_ds8 = has_ds8;
443
444     /* COM aggregation supported only internally */
445     if (outer_unk)
446         obj->outer_unk = outer_unk;
447     else
448         obj->outer_unk = &obj->IUnknown_inner;
449
450     hr = IUnknown_QueryInterface(&obj->IUnknown_inner, riid, ppv);
451     IUnknown_Release(&obj->IUnknown_inner);
452
453     return hr;
454 }
455
456 HRESULT DSOUND_Create(REFIID riid, void **ppv)
457 {
458     return IDirectSoundImpl_Create(NULL, riid, ppv, FALSE);
459 }
460
461 HRESULT DSOUND_Create8(REFIID riid, void **ppv)
462 {
463     return IDirectSoundImpl_Create(NULL, riid, ppv, TRUE);
464 }
465
466 /*******************************************************************************
467  *              DirectSoundCreate (DSOUND.1)
468  *
469  *  Creates and initializes a DirectSound interface.
470  *
471  *  PARAMS
472  *     lpcGUID   [I] Address of the GUID that identifies the sound device.
473  *     ppDS      [O] Address of a variable to receive the interface pointer.
474  *     pUnkOuter [I] Must be NULL.
475  *
476  *  RETURNS
477  *     Success: DS_OK
478  *     Failure: DSERR_ALLOCATED, DSERR_INVALIDPARAM, DSERR_NOAGGREGATION,
479  *              DSERR_NODRIVER, DSERR_OUTOFMEMORY
480  */
481 HRESULT WINAPI DirectSoundCreate(
482     LPCGUID lpcGUID,
483     LPDIRECTSOUND *ppDS,
484     IUnknown *pUnkOuter)
485 {
486     HRESULT hr;
487     LPDIRECTSOUND pDS;
488
489     TRACE("(%s,%p,%p)\n",debugstr_guid(lpcGUID),ppDS,pUnkOuter);
490
491     if (ppDS == NULL) {
492         WARN("invalid parameter: ppDS == NULL\n");
493         return DSERR_INVALIDPARAM;
494     }
495
496     if (pUnkOuter != NULL) {
497         WARN("invalid parameter: pUnkOuter != NULL\n");
498         *ppDS = 0;
499         return DSERR_INVALIDPARAM;
500     }
501
502     hr = DSOUND_Create(&IID_IDirectSound, (void **)&pDS);
503     if (hr == DS_OK) {
504         hr = IDirectSound_Initialize(pDS, lpcGUID);
505         if (hr != DS_OK) {
506             if (hr != DSERR_ALREADYINITIALIZED) {
507                 IDirectSound_Release(pDS);
508                 pDS = 0;
509             } else
510                 hr = DS_OK;
511         }
512     }
513
514     *ppDS = pDS;
515
516     return hr;
517 }
518
519 /*******************************************************************************
520  *        DirectSoundCreate8 (DSOUND.11)
521  *
522  *  Creates and initializes a DirectSound8 interface.
523  *
524  *  PARAMS
525  *     lpcGUID   [I] Address of the GUID that identifies the sound device.
526  *     ppDS      [O] Address of a variable to receive the interface pointer.
527  *     pUnkOuter [I] Must be NULL.
528  *
529  *  RETURNS
530  *     Success: DS_OK
531  *     Failure: DSERR_ALLOCATED, DSERR_INVALIDPARAM, DSERR_NOAGGREGATION,
532  *              DSERR_NODRIVER, DSERR_OUTOFMEMORY
533  */
534 HRESULT WINAPI DirectSoundCreate8(
535     LPCGUID lpcGUID,
536     LPDIRECTSOUND8 *ppDS,
537     IUnknown *pUnkOuter)
538 {
539     HRESULT hr;
540     LPDIRECTSOUND8 pDS;
541
542     TRACE("(%s,%p,%p)\n",debugstr_guid(lpcGUID),ppDS,pUnkOuter);
543
544     if (ppDS == NULL) {
545         WARN("invalid parameter: ppDS == NULL\n");
546         return DSERR_INVALIDPARAM;
547     }
548
549     if (pUnkOuter != NULL) {
550         WARN("invalid parameter: pUnkOuter != NULL\n");
551         *ppDS = 0;
552         return DSERR_INVALIDPARAM;
553     }
554
555     hr = DSOUND_Create8(&IID_IDirectSound8, (void **)&pDS);
556     if (hr == DS_OK) {
557         hr = IDirectSound8_Initialize(pDS, lpcGUID);
558         if (hr != DS_OK) {
559             if (hr != DSERR_ALREADYINITIALIZED) {
560                 IDirectSound8_Release(pDS);
561                 pDS = 0;
562             } else
563                 hr = DS_OK;
564         }
565     }
566
567     *ppDS = pDS;
568
569     return hr;
570 }
571
572 /*******************************************************************************
573  *        DirectSoundDevice
574  */
575 static HRESULT DirectSoundDevice_Create(DirectSoundDevice ** ppDevice)
576 {
577     DirectSoundDevice * device;
578     TRACE("(%p)\n", ppDevice);
579
580     /* Allocate memory */
581     device = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(DirectSoundDevice));
582     if (device == NULL) {
583         WARN("out of memory\n");
584         return DSERR_OUTOFMEMORY;
585     }
586
587     device->ref            = 1;
588     device->priolevel      = DSSCL_NORMAL;
589     device->state          = STATE_STOPPED;
590     device->speaker_config = DSSPEAKER_COMBINED(DSSPEAKER_STEREO, DSSPEAKER_GEOMETRY_WIDE);
591
592     /* 3D listener initial parameters */
593     device->ds3dl.dwSize   = sizeof(DS3DLISTENER);
594     device->ds3dl.vPosition.x = 0.0;
595     device->ds3dl.vPosition.y = 0.0;
596     device->ds3dl.vPosition.z = 0.0;
597     device->ds3dl.vVelocity.x = 0.0;
598     device->ds3dl.vVelocity.y = 0.0;
599     device->ds3dl.vVelocity.z = 0.0;
600     device->ds3dl.vOrientFront.x = 0.0;
601     device->ds3dl.vOrientFront.y = 0.0;
602     device->ds3dl.vOrientFront.z = 1.0;
603     device->ds3dl.vOrientTop.x = 0.0;
604     device->ds3dl.vOrientTop.y = 1.0;
605     device->ds3dl.vOrientTop.z = 0.0;
606     device->ds3dl.flDistanceFactor = DS3D_DEFAULTDISTANCEFACTOR;
607     device->ds3dl.flRolloffFactor = DS3D_DEFAULTROLLOFFFACTOR;
608     device->ds3dl.flDopplerFactor = DS3D_DEFAULTDOPPLERFACTOR;
609
610     device->prebuf = ds_snd_queue_max;
611     device->guid = GUID_NULL;
612
613     /* Set default wave format (may need it for waveOutOpen) */
614     device->pwfx = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(WAVEFORMATEX));
615     if (device->pwfx == NULL) {
616         WARN("out of memory\n");
617         HeapFree(GetProcessHeap(),0,device);
618         return DSERR_OUTOFMEMORY;
619     }
620
621     device->pwfx->wFormatTag = WAVE_FORMAT_PCM;
622     device->pwfx->nSamplesPerSec = ds_default_sample_rate;
623     device->pwfx->wBitsPerSample = ds_default_bits_per_sample;
624     device->pwfx->nChannels = 2;
625     device->pwfx->nBlockAlign = device->pwfx->wBitsPerSample * device->pwfx->nChannels / 8;
626     device->pwfx->nAvgBytesPerSec = device->pwfx->nSamplesPerSec * device->pwfx->nBlockAlign;
627     device->pwfx->cbSize = 0;
628
629     InitializeCriticalSection(&(device->mixlock));
630     device->mixlock.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": DirectSoundDevice.mixlock");
631
632     RtlInitializeResource(&(device->buffer_list_lock));
633
634    *ppDevice = device;
635
636     return DS_OK;
637 }
638
639 static ULONG DirectSoundDevice_AddRef(DirectSoundDevice * device)
640 {
641     ULONG ref = InterlockedIncrement(&(device->ref));
642     TRACE("(%p) ref was %d\n", device, ref - 1);
643     return ref;
644 }
645
646 ULONG DirectSoundDevice_Release(DirectSoundDevice * device)
647 {
648     HRESULT hr;
649     ULONG ref = InterlockedDecrement(&(device->ref));
650     TRACE("(%p) ref was %u\n", device, ref + 1);
651     if (!ref) {
652         int i;
653         timeKillEvent(device->timerID);
654         timeEndPeriod(DS_TIME_RES);
655
656         /* The kill event should have allowed the timer process to expire
657          * but try to grab the lock just in case. Can't hold lock because
658          * secondarybuffer_destroy also grabs the lock */
659         RtlAcquireResourceShared(&(device->buffer_list_lock), TRUE);
660         RtlReleaseResource(&(device->buffer_list_lock));
661
662         EnterCriticalSection(&DSOUND_renderers_lock);
663         list_remove(&device->entry);
664         LeaveCriticalSection(&DSOUND_renderers_lock);
665
666         /* It is allowed to release this object even when buffers are playing */
667         if (device->buffers) {
668             WARN("%d secondary buffers not released\n", device->nrofbuffers);
669             for( i=0;i<device->nrofbuffers;i++)
670                 secondarybuffer_destroy(device->buffers[i]);
671         }
672
673         hr = DSOUND_PrimaryDestroy(device);
674         if (hr != DS_OK)
675             WARN("DSOUND_PrimaryDestroy failed\n");
676
677         if(device->client)
678             IAudioClient_Release(device->client);
679         if(device->render)
680             IAudioRenderClient_Release(device->render);
681         if(device->clock)
682             IAudioClock_Release(device->clock);
683         if(device->volume)
684             IAudioStreamVolume_Release(device->volume);
685
686         HeapFree(GetProcessHeap(), 0, device->tmp_buffer);
687         HeapFree(GetProcessHeap(), 0, device->mix_buffer);
688         HeapFree(GetProcessHeap(), 0, device->buffer);
689         RtlDeleteResource(&device->buffer_list_lock);
690         device->mixlock.DebugInfo->Spare[0] = 0;
691         DeleteCriticalSection(&device->mixlock);
692         HeapFree(GetProcessHeap(),0,device);
693         TRACE("(%p) released\n", device);
694     }
695     return ref;
696 }
697
698 BOOL DSOUND_check_supported(IAudioClient *client, DWORD rate,
699         DWORD depth, WORD channels)
700 {
701     WAVEFORMATEX fmt, *junk;
702     HRESULT hr;
703
704     fmt.wFormatTag = WAVE_FORMAT_PCM;
705     fmt.nChannels = channels;
706     fmt.nSamplesPerSec = rate;
707     fmt.wBitsPerSample = depth;
708     fmt.nBlockAlign = (channels * depth) / 8;
709     fmt.nAvgBytesPerSec = rate * fmt.nBlockAlign;
710     fmt.cbSize = 0;
711
712     hr = IAudioClient_IsFormatSupported(client, AUDCLNT_SHAREMODE_SHARED, &fmt, &junk);
713     if(SUCCEEDED(hr))
714         CoTaskMemFree(junk);
715
716     return hr == S_OK;
717 }
718
719 UINT DSOUND_create_timer(LPTIMECALLBACK cb, DWORD_PTR user)
720 {
721     UINT triggertime = DS_TIME_DEL, res = DS_TIME_RES, id;
722     TIMECAPS time;
723
724     timeGetDevCaps(&time, sizeof(TIMECAPS));
725     TRACE("Minimum timer resolution: %u, max timer: %u\n", time.wPeriodMin, time.wPeriodMax);
726     if (triggertime < time.wPeriodMin)
727         triggertime = time.wPeriodMin;
728     if (res < time.wPeriodMin)
729         res = time.wPeriodMin;
730     if (timeBeginPeriod(res) == TIMERR_NOCANDO)
731         WARN("Could not set minimum resolution, don't expect sound\n");
732     id = timeSetEvent(triggertime, res, cb, user, TIME_PERIODIC | TIME_KILL_SYNCHRONOUS);
733     if (!id)
734     {
735         WARN("Timer not created! Retrying without TIME_KILL_SYNCHRONOUS\n");
736         id = timeSetEvent(triggertime, res, cb, user, TIME_PERIODIC);
737         if (!id)
738             ERR("Could not create timer, sound playback will not occur\n");
739     }
740     return id;
741 }
742
743 HRESULT DirectSoundDevice_Initialize(DirectSoundDevice ** ppDevice, LPCGUID lpcGUID)
744 {
745     HRESULT hr = DS_OK;
746     GUID devGUID;
747     DirectSoundDevice *device;
748     IMMDevice *mmdevice;
749
750     TRACE("(%p,%s)\n",ppDevice,debugstr_guid(lpcGUID));
751
752     if (*ppDevice != NULL) {
753         WARN("already initialized\n");
754         return DSERR_ALREADYINITIALIZED;
755     }
756
757     /* Default device? */
758     if (!lpcGUID || IsEqualGUID(lpcGUID, &GUID_NULL))
759         lpcGUID = &DSDEVID_DefaultPlayback;
760
761     if(IsEqualGUID(lpcGUID, &DSDEVID_DefaultCapture) ||
762             IsEqualGUID(lpcGUID, &DSDEVID_DefaultVoiceCapture))
763         return DSERR_NODRIVER;
764
765     if (GetDeviceID(lpcGUID, &devGUID) != DS_OK) {
766         WARN("invalid parameter: lpcGUID\n");
767         return DSERR_INVALIDPARAM;
768     }
769
770     hr = get_mmdevice(eRender, &devGUID, &mmdevice);
771     if(FAILED(hr))
772         return hr;
773
774     EnterCriticalSection(&DSOUND_renderers_lock);
775
776     LIST_FOR_EACH_ENTRY(device, &DSOUND_renderers, DirectSoundDevice, entry){
777         if(IsEqualGUID(&device->guid, &devGUID)){
778             IMMDevice_Release(mmdevice);
779             DirectSoundDevice_AddRef(device);
780             *ppDevice = device;
781             LeaveCriticalSection(&DSOUND_renderers_lock);
782             return DS_OK;
783         }
784     }
785
786     hr = DirectSoundDevice_Create(&device);
787     if(FAILED(hr)){
788         WARN("DirectSoundDevice_Create failed\n");
789         IMMDevice_Release(mmdevice);
790         LeaveCriticalSection(&DSOUND_renderers_lock);
791         return hr;
792     }
793
794     device->mmdevice = mmdevice;
795     device->guid = devGUID;
796
797     hr = DSOUND_ReopenDevice(device, FALSE);
798     if (FAILED(hr))
799     {
800         HeapFree(GetProcessHeap(), 0, device);
801         LeaveCriticalSection(&DSOUND_renderers_lock);
802         IMMDevice_Release(mmdevice);
803         WARN("DSOUND_ReopenDevice failed: %08x\n", hr);
804         return hr;
805     }
806
807     ZeroMemory(&device->drvcaps, sizeof(device->drvcaps));
808
809     if(DSOUND_check_supported(device->client, 11025, 8, 1) ||
810             DSOUND_check_supported(device->client, 22050, 8, 1) ||
811             DSOUND_check_supported(device->client, 44100, 8, 1) ||
812             DSOUND_check_supported(device->client, 48000, 8, 1) ||
813             DSOUND_check_supported(device->client, 96000, 8, 1))
814         device->drvcaps.dwFlags |= DSCAPS_PRIMARY8BIT | DSCAPS_PRIMARYMONO;
815
816     if(DSOUND_check_supported(device->client, 11025, 16, 1) ||
817             DSOUND_check_supported(device->client, 22050, 16, 1) ||
818             DSOUND_check_supported(device->client, 44100, 16, 1) ||
819             DSOUND_check_supported(device->client, 48000, 16, 1) ||
820             DSOUND_check_supported(device->client, 96000, 16, 1))
821         device->drvcaps.dwFlags |= DSCAPS_PRIMARY16BIT | DSCAPS_PRIMARYMONO;
822
823     if(DSOUND_check_supported(device->client, 11025, 8, 2) ||
824             DSOUND_check_supported(device->client, 22050, 8, 2) ||
825             DSOUND_check_supported(device->client, 44100, 8, 2) ||
826             DSOUND_check_supported(device->client, 48000, 8, 2) ||
827             DSOUND_check_supported(device->client, 96000, 8, 2))
828         device->drvcaps.dwFlags |= DSCAPS_PRIMARY8BIT | DSCAPS_PRIMARYSTEREO;
829
830     if(DSOUND_check_supported(device->client, 11025, 16, 2) ||
831             DSOUND_check_supported(device->client, 22050, 16, 2) ||
832             DSOUND_check_supported(device->client, 44100, 16, 2) ||
833             DSOUND_check_supported(device->client, 48000, 16, 2) ||
834             DSOUND_check_supported(device->client, 96000, 16, 2))
835         device->drvcaps.dwFlags |= DSCAPS_PRIMARY16BIT | DSCAPS_PRIMARYSTEREO;
836
837     /* the dsound mixer supports all of the following */
838     device->drvcaps.dwFlags |= DSCAPS_SECONDARY8BIT | DSCAPS_SECONDARY16BIT;
839     device->drvcaps.dwFlags |= DSCAPS_SECONDARYMONO | DSCAPS_SECONDARYSTEREO;
840     device->drvcaps.dwFlags |= DSCAPS_CONTINUOUSRATE;
841
842     device->drvcaps.dwPrimaryBuffers = 1;
843     device->drvcaps.dwMinSecondarySampleRate = DSBFREQUENCY_MIN;
844     device->drvcaps.dwMaxSecondarySampleRate = DSBFREQUENCY_MAX;
845     device->drvcaps.dwMaxHwMixingAllBuffers = 1;
846     device->drvcaps.dwMaxHwMixingStaticBuffers = 1;
847     device->drvcaps.dwMaxHwMixingStreamingBuffers = 1;
848
849     ZeroMemory(&device->volpan, sizeof(device->volpan));
850
851     hr = DSOUND_PrimaryCreate(device);
852     if (hr == DS_OK)
853         device->timerID = DSOUND_create_timer(DSOUND_timer, (DWORD_PTR)device);
854     else
855         WARN("DSOUND_PrimaryCreate failed: %08x\n", hr);
856
857     *ppDevice = device;
858     list_add_tail(&DSOUND_renderers, &device->entry);
859
860     LeaveCriticalSection(&DSOUND_renderers_lock);
861
862     return hr;
863 }
864
865 HRESULT DirectSoundDevice_CreateSoundBuffer(
866     DirectSoundDevice * device,
867     LPCDSBUFFERDESC dsbd,
868     LPLPDIRECTSOUNDBUFFER ppdsb,
869     LPUNKNOWN lpunk,
870     BOOL from8)
871 {
872     HRESULT hres = DS_OK;
873     TRACE("(%p,%p,%p,%p)\n",device,dsbd,ppdsb,lpunk);
874
875     if (device == NULL) {
876         WARN("not initialized\n");
877         return DSERR_UNINITIALIZED;
878     }
879
880     if (dsbd == NULL) {
881         WARN("invalid parameter: dsbd == NULL\n");
882         return DSERR_INVALIDPARAM;
883     }
884
885     if (dsbd->dwSize != sizeof(DSBUFFERDESC) &&
886         dsbd->dwSize != sizeof(DSBUFFERDESC1)) {
887         WARN("invalid parameter: dsbd\n");
888         return DSERR_INVALIDPARAM;
889     }
890
891     if (ppdsb == NULL) {
892         WARN("invalid parameter: ppdsb == NULL\n");
893         return DSERR_INVALIDPARAM;
894     }
895     *ppdsb = NULL;
896
897     if (TRACE_ON(dsound)) {
898         TRACE("(structsize=%d)\n",dsbd->dwSize);
899         TRACE("(flags=0x%08x:\n",dsbd->dwFlags);
900         _dump_DSBCAPS(dsbd->dwFlags);
901         TRACE(")\n");
902         TRACE("(bufferbytes=%d)\n",dsbd->dwBufferBytes);
903         TRACE("(lpwfxFormat=%p)\n",dsbd->lpwfxFormat);
904     }
905
906     if (dsbd->dwFlags & DSBCAPS_LOCHARDWARE &&
907             !(dsbd->dwFlags & DSBCAPS_PRIMARYBUFFER)) {
908         TRACE("LOCHARDWARE is not supported, returning E_NOTIMPL\n");
909         return E_NOTIMPL;
910     }
911
912     if (dsbd->dwFlags & DSBCAPS_PRIMARYBUFFER) {
913         if (dsbd->lpwfxFormat != NULL) {
914             WARN("invalid parameter: dsbd->lpwfxFormat must be NULL for "
915                  "primary buffer\n");
916             return DSERR_INVALIDPARAM;
917         }
918
919         if (device->primary) {
920             WARN("Primary Buffer already created\n");
921             IDirectSoundBuffer_AddRef((LPDIRECTSOUNDBUFFER8)(device->primary));
922             *ppdsb = (LPDIRECTSOUNDBUFFER)(device->primary);
923         } else {
924             hres = primarybuffer_create(device, &device->primary, dsbd);
925             if (device->primary) {
926                 *ppdsb = (IDirectSoundBuffer*)&device->primary->IDirectSoundBuffer8_iface;
927                 device->primary->dsbd.dwFlags &= ~(DSBCAPS_LOCHARDWARE | DSBCAPS_LOCSOFTWARE);
928                 device->primary->dsbd.dwFlags |= DSBCAPS_LOCSOFTWARE;
929             } else
930                 WARN("primarybuffer_create() failed\n");
931         }
932     } else {
933         IDirectSoundBufferImpl * dsb;
934         WAVEFORMATEXTENSIBLE *pwfxe;
935
936         if (dsbd->lpwfxFormat == NULL) {
937             WARN("invalid parameter: dsbd->lpwfxFormat can't be NULL for "
938                  "secondary buffer\n");
939             return DSERR_INVALIDPARAM;
940         }
941         pwfxe = (WAVEFORMATEXTENSIBLE*)dsbd->lpwfxFormat;
942
943         if (pwfxe->Format.wBitsPerSample != 16 && pwfxe->Format.wBitsPerSample != 8 && pwfxe->Format.wFormatTag != WAVE_FORMAT_EXTENSIBLE)
944         {
945             WARN("wBitsPerSample=%d needs a WAVEFORMATEXTENSIBLE\n", dsbd->lpwfxFormat->wBitsPerSample);
946             return DSERR_CONTROLUNAVAIL;
947         }
948         if (pwfxe->Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE)
949         {
950             /* check if cbSize is at least 22 bytes */
951             if (pwfxe->Format.cbSize < (sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX)))
952             {
953                 WARN("Too small a cbSize %u\n", pwfxe->Format.cbSize);
954                 return DSERR_INVALIDPARAM;
955             }
956
957             /* cbSize should be 22 bytes, with one possible exception */
958             if (pwfxe->Format.cbSize > (sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX)) &&
959                 !((IsEqualGUID(&pwfxe->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM) || IsEqualGUID(&pwfxe->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)) &&
960                 pwfxe->Format.cbSize == sizeof(WAVEFORMATEXTENSIBLE)))
961             {
962                 WARN("Too big a cbSize %u\n", pwfxe->Format.cbSize);
963                 return DSERR_CONTROLUNAVAIL;
964             }
965
966             if ((!IsEqualGUID(&pwfxe->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM)) && (!IsEqualGUID(&pwfxe->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)))
967             {
968                 if (!IsEqualGUID(&pwfxe->SubFormat, &GUID_NULL))
969                     FIXME("SubFormat %s not supported right now.\n", debugstr_guid(&pwfxe->SubFormat));
970                 return DSERR_INVALIDPARAM;
971             }
972             if (pwfxe->Samples.wValidBitsPerSample > dsbd->lpwfxFormat->wBitsPerSample)
973             {
974                 WARN("Samples.wValidBitsPerSample(%d) > Format.wBitsPerSample (%d)\n", pwfxe->Samples.wValidBitsPerSample, pwfxe->Format.wBitsPerSample);
975                 return DSERR_INVALIDPARAM;
976             }
977             if (pwfxe->Samples.wValidBitsPerSample && pwfxe->Samples.wValidBitsPerSample < dsbd->lpwfxFormat->wBitsPerSample)
978             {
979                 FIXME("Non-packed formats not supported right now: %d/%d\n", pwfxe->Samples.wValidBitsPerSample, dsbd->lpwfxFormat->wBitsPerSample);
980                 return DSERR_CONTROLUNAVAIL;
981             }
982         }
983
984         TRACE("(formattag=0x%04x,chans=%d,samplerate=%d,"
985               "bytespersec=%d,blockalign=%d,bitspersamp=%d,cbSize=%d)\n",
986               dsbd->lpwfxFormat->wFormatTag, dsbd->lpwfxFormat->nChannels,
987               dsbd->lpwfxFormat->nSamplesPerSec,
988               dsbd->lpwfxFormat->nAvgBytesPerSec,
989               dsbd->lpwfxFormat->nBlockAlign,
990               dsbd->lpwfxFormat->wBitsPerSample, dsbd->lpwfxFormat->cbSize);
991
992         if (from8 && (dsbd->dwFlags & DSBCAPS_CTRL3D) && (dsbd->lpwfxFormat->nChannels != 1)) {
993             WARN("invalid parameter: 3D buffer format must be mono\n");
994             return DSERR_INVALIDPARAM;
995         }
996
997         hres = IDirectSoundBufferImpl_Create(device, &dsb, dsbd);
998         if (dsb)
999             *ppdsb = (IDirectSoundBuffer*)&dsb->IDirectSoundBuffer8_iface;
1000         else
1001             WARN("IDirectSoundBufferImpl_Create failed\n");
1002    }
1003
1004    return hres;
1005 }
1006
1007 HRESULT DirectSoundDevice_DuplicateSoundBuffer(
1008     DirectSoundDevice * device,
1009     LPDIRECTSOUNDBUFFER psb,
1010     LPLPDIRECTSOUNDBUFFER ppdsb)
1011 {
1012     HRESULT hres = DS_OK;
1013     IDirectSoundBufferImpl* dsb;
1014     TRACE("(%p,%p,%p)\n",device,psb,ppdsb);
1015
1016     if (device == NULL) {
1017         WARN("not initialized\n");
1018         return DSERR_UNINITIALIZED;
1019     }
1020
1021     if (psb == NULL) {
1022         WARN("invalid parameter: psb == NULL\n");
1023         return DSERR_INVALIDPARAM;
1024     }
1025
1026     if (ppdsb == NULL) {
1027         WARN("invalid parameter: ppdsb == NULL\n");
1028         return DSERR_INVALIDPARAM;
1029     }
1030
1031     /* make sure we have a secondary buffer */
1032     if (psb == (IDirectSoundBuffer *)&device->primary->IDirectSoundBuffer8_iface) {
1033         WARN("trying to duplicate primary buffer\n");
1034         *ppdsb = NULL;
1035         return DSERR_INVALIDCALL;
1036     }
1037
1038     /* duplicate the actual buffer implementation */
1039     hres = IDirectSoundBufferImpl_Duplicate(device, &dsb, (IDirectSoundBufferImpl*)psb);
1040     if (hres == DS_OK)
1041         *ppdsb = (IDirectSoundBuffer*)&dsb->IDirectSoundBuffer8_iface;
1042     else
1043         WARN("IDirectSoundBufferImpl_Duplicate failed\n");
1044
1045     return hres;
1046 }
1047
1048 /*
1049  * Add secondary buffer to buffer list.
1050  * Gets exclusive access to buffer for writing.
1051  */
1052 HRESULT DirectSoundDevice_AddBuffer(
1053     DirectSoundDevice * device,
1054     IDirectSoundBufferImpl * pDSB)
1055 {
1056     IDirectSoundBufferImpl **newbuffers;
1057     HRESULT hr = DS_OK;
1058
1059     TRACE("(%p, %p)\n", device, pDSB);
1060
1061     RtlAcquireResourceExclusive(&(device->buffer_list_lock), TRUE);
1062
1063     if (device->buffers)
1064         newbuffers = HeapReAlloc(GetProcessHeap(),0,device->buffers,sizeof(IDirectSoundBufferImpl*)*(device->nrofbuffers+1));
1065     else
1066         newbuffers = HeapAlloc(GetProcessHeap(),0,sizeof(IDirectSoundBufferImpl*)*(device->nrofbuffers+1));
1067
1068     if (newbuffers) {
1069         device->buffers = newbuffers;
1070         device->buffers[device->nrofbuffers] = pDSB;
1071         device->nrofbuffers++;
1072         TRACE("buffer count is now %d\n", device->nrofbuffers);
1073     } else {
1074         ERR("out of memory for buffer list! Current buffer count is %d\n", device->nrofbuffers);
1075         hr = DSERR_OUTOFMEMORY;
1076     }
1077
1078     RtlReleaseResource(&(device->buffer_list_lock));
1079
1080     return hr;
1081 }
1082
1083 /*
1084  * Remove secondary buffer from buffer list.
1085  * Gets exclusive access to buffer for writing.
1086  */
1087 HRESULT DirectSoundDevice_RemoveBuffer(
1088     DirectSoundDevice * device,
1089     IDirectSoundBufferImpl * pDSB)
1090 {
1091     int i;
1092     HRESULT hr = DS_OK;
1093
1094     TRACE("(%p, %p)\n", device, pDSB);
1095
1096     RtlAcquireResourceExclusive(&(device->buffer_list_lock), TRUE);
1097
1098     for (i = 0; i < device->nrofbuffers; i++)
1099         if (device->buffers[i] == pDSB)
1100             break;
1101
1102     if (i < device->nrofbuffers) {
1103         /* Put the last buffer of the list in the (now empty) position */
1104         device->buffers[i] = device->buffers[device->nrofbuffers - 1];
1105         device->nrofbuffers--;
1106         device->buffers = HeapReAlloc(GetProcessHeap(),0,device->buffers,sizeof(LPDIRECTSOUNDBUFFER8)*device->nrofbuffers);
1107         TRACE("buffer count is now %d\n", device->nrofbuffers);
1108     }
1109
1110     if (device->nrofbuffers == 0) {
1111         HeapFree(GetProcessHeap(),0,device->buffers);
1112         device->buffers = NULL;
1113     }
1114
1115     RtlReleaseResource(&(device->buffer_list_lock));
1116
1117     return hr;
1118 }