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