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