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