[DSOUND]
[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 "dsound_private.h"
24
25 typedef struct IDirectSoundImpl {
26 IUnknown IUnknown_inner;
27 IDirectSound8 IDirectSound8_iface;
28 IUnknown *outer_unk; /* internal */
29 LONG ref, refds, numIfaces;
30 DirectSoundDevice *device;
31 BOOL has_ds8;
32 } IDirectSoundImpl;
33
34 static const char * dumpCooperativeLevel(DWORD level)
35 {
36 #define LE(x) case x: return #x
37 switch (level) {
38 LE(DSSCL_NORMAL);
39 LE(DSSCL_PRIORITY);
40 LE(DSSCL_EXCLUSIVE);
41 LE(DSSCL_WRITEPRIMARY);
42 }
43 #undef LE
44 return wine_dbg_sprintf("Unknown(%08x)", level);
45 }
46
47 static void _dump_DSCAPS(DWORD xmask) {
48 struct {
49 DWORD mask;
50 const char *name;
51 } flags[] = {
52 #define FE(x) { x, #x },
53 FE(DSCAPS_PRIMARYMONO)
54 FE(DSCAPS_PRIMARYSTEREO)
55 FE(DSCAPS_PRIMARY8BIT)
56 FE(DSCAPS_PRIMARY16BIT)
57 FE(DSCAPS_CONTINUOUSRATE)
58 FE(DSCAPS_EMULDRIVER)
59 FE(DSCAPS_CERTIFIED)
60 FE(DSCAPS_SECONDARYMONO)
61 FE(DSCAPS_SECONDARYSTEREO)
62 FE(DSCAPS_SECONDARY8BIT)
63 FE(DSCAPS_SECONDARY16BIT)
64 #undef FE
65 };
66 unsigned int i;
67
68 for (i=0;i<sizeof(flags)/sizeof(flags[0]);i++)
69 if ((flags[i].mask & xmask) == flags[i].mask)
70 TRACE("%s ",flags[i].name);
71 }
72
73 static void _dump_DSBCAPS(DWORD xmask) {
74 struct {
75 DWORD mask;
76 const char *name;
77 } flags[] = {
78 #define FE(x) { x, #x },
79 FE(DSBCAPS_PRIMARYBUFFER)
80 FE(DSBCAPS_STATIC)
81 FE(DSBCAPS_LOCHARDWARE)
82 FE(DSBCAPS_LOCSOFTWARE)
83 FE(DSBCAPS_CTRL3D)
84 FE(DSBCAPS_CTRLFREQUENCY)
85 FE(DSBCAPS_CTRLPAN)
86 FE(DSBCAPS_CTRLVOLUME)
87 FE(DSBCAPS_CTRLPOSITIONNOTIFY)
88 FE(DSBCAPS_STICKYFOCUS)
89 FE(DSBCAPS_GLOBALFOCUS)
90 FE(DSBCAPS_GETCURRENTPOSITION2)
91 FE(DSBCAPS_MUTE3DATMAXDISTANCE)
92 #undef FE
93 };
94 unsigned int i;
95
96 for (i=0;i<sizeof(flags)/sizeof(flags[0]);i++)
97 if ((flags[i].mask & xmask) == flags[i].mask)
98 TRACE("%s ",flags[i].name);
99 }
100
101 static void directsound_destroy(IDirectSoundImpl *This)
102 {
103 if (This->device)
104 DirectSoundDevice_Release(This->device);
105 HeapFree(GetProcessHeap(),0,This);
106 TRACE("(%p) released\n", This);
107 }
108
109 /*******************************************************************************
110 * IUnknown Implementation for DirectSound
111 */
112 static inline IDirectSoundImpl *impl_from_IUnknown(IUnknown *iface)
113 {
114 return CONTAINING_RECORD(iface, IDirectSoundImpl, IUnknown_inner);
115 }
116
117 static HRESULT WINAPI IUnknownImpl_QueryInterface(IUnknown *iface, REFIID riid, void **ppv)
118 {
119 IDirectSoundImpl *This = impl_from_IUnknown(iface);
120
121 TRACE("(%p,%s,%p)\n", This, debugstr_guid(riid), ppv);
122
123 if (!ppv) {
124 WARN("invalid parameter\n");
125 return E_INVALIDARG;
126 }
127 *ppv = NULL;
128
129 if (IsEqualIID(riid, &IID_IUnknown))
130 *ppv = &This->IUnknown_inner;
131 else if (IsEqualIID(riid, &IID_IDirectSound) ||
132 (IsEqualIID(riid, &IID_IDirectSound8) && This->has_ds8))
133 *ppv = &This->IDirectSound8_iface;
134 else {
135 WARN("unknown IID %s\n", debugstr_guid(riid));
136 return E_NOINTERFACE;
137 }
138
139 IUnknown_AddRef((IUnknown*)*ppv);
140 return S_OK;
141 }
142
143 static ULONG WINAPI IUnknownImpl_AddRef(IUnknown *iface)
144 {
145 IDirectSoundImpl *This = impl_from_IUnknown(iface);
146 ULONG ref = InterlockedIncrement(&This->ref);
147
148 TRACE("(%p) ref=%d\n", This, ref);
149
150 if(ref == 1)
151 InterlockedIncrement(&This->numIfaces);
152
153 return ref;
154 }
155
156 static ULONG WINAPI IUnknownImpl_Release(IUnknown *iface)
157 {
158 IDirectSoundImpl *This = impl_from_IUnknown(iface);
159 ULONG ref = InterlockedDecrement(&This->ref);
160
161 TRACE("(%p) ref=%d\n", This, ref);
162
163 if (!ref && !InterlockedDecrement(&This->numIfaces))
164 directsound_destroy(This);
165
166 return ref;
167 }
168
169 static const IUnknownVtbl unk_vtbl =
170 {
171 IUnknownImpl_QueryInterface,
172 IUnknownImpl_AddRef,
173 IUnknownImpl_Release
174 };
175
176 /*******************************************************************************
177 * IDirectSound and IDirectSound8 Implementation
178 */
179 static inline IDirectSoundImpl *impl_from_IDirectSound8(IDirectSound8 *iface)
180 {
181 return CONTAINING_RECORD(iface, IDirectSoundImpl, IDirectSound8_iface);
182 }
183
184 static HRESULT WINAPI IDirectSound8Impl_QueryInterface(IDirectSound8 *iface, REFIID riid,
185 void **ppv)
186 {
187 IDirectSoundImpl *This = impl_from_IDirectSound8(iface);
188 TRACE("(%p,%s,%p)\n", This, debugstr_guid(riid), ppv);
189 return IUnknown_QueryInterface(This->outer_unk, riid, ppv);
190 }
191
192 static ULONG WINAPI IDirectSound8Impl_AddRef(IDirectSound8 *iface)
193 {
194 IDirectSoundImpl *This = impl_from_IDirectSound8(iface);
195 ULONG ref = InterlockedIncrement(&This->refds);
196
197 TRACE("(%p) refds=%d\n", This, ref);
198
199 if(ref == 1)
200 InterlockedIncrement(&This->numIfaces);
201
202 return ref;
203 }
204
205 static ULONG WINAPI IDirectSound8Impl_Release(IDirectSound8 *iface)
206 {
207 IDirectSoundImpl *This = impl_from_IDirectSound8(iface);
208 ULONG ref = InterlockedDecrement(&(This->refds));
209
210 TRACE("(%p) refds=%d\n", This, ref);
211
212 if (!ref && !InterlockedDecrement(&This->numIfaces))
213 directsound_destroy(This);
214
215 return ref;
216 }
217
218 static HRESULT WINAPI IDirectSound8Impl_CreateSoundBuffer(IDirectSound8 *iface,
219 const DSBUFFERDESC *dsbd, IDirectSoundBuffer **ppdsb, IUnknown *lpunk)
220 {
221 IDirectSoundImpl *This = impl_from_IDirectSound8(iface);
222 TRACE("(%p,%p,%p,%p)\n", This, dsbd, ppdsb, lpunk);
223 return DirectSoundDevice_CreateSoundBuffer(This->device, dsbd, ppdsb, lpunk, This->has_ds8);
224 }
225
226 static HRESULT WINAPI IDirectSound8Impl_GetCaps(IDirectSound8 *iface, DSCAPS *dscaps)
227 {
228 IDirectSoundImpl *This = impl_from_IDirectSound8(iface);
229
230 TRACE("(%p, %p)\n", This, dscaps);
231
232 if (!This->device) {
233 WARN("not initialized\n");
234 return DSERR_UNINITIALIZED;
235 }
236 if (!dscaps) {
237 WARN("invalid parameter: dscaps = NULL\n");
238 return DSERR_INVALIDPARAM;
239 }
240 if (dscaps->dwSize < sizeof(*dscaps)) {
241 WARN("invalid parameter: dscaps->dwSize = %d\n", dscaps->dwSize);
242 return DSERR_INVALIDPARAM;
243 }
244
245 dscaps->dwFlags = This->device->drvcaps.dwFlags;
246 dscaps->dwMinSecondarySampleRate = This->device->drvcaps.dwMinSecondarySampleRate;
247 dscaps->dwMaxSecondarySampleRate = This->device->drvcaps.dwMaxSecondarySampleRate;
248 dscaps->dwPrimaryBuffers = This->device->drvcaps.dwPrimaryBuffers;
249 dscaps->dwMaxHwMixingAllBuffers = This->device->drvcaps.dwMaxHwMixingAllBuffers;
250 dscaps->dwMaxHwMixingStaticBuffers = This->device->drvcaps.dwMaxHwMixingStaticBuffers;
251 dscaps->dwMaxHwMixingStreamingBuffers = This->device->drvcaps.dwMaxHwMixingStreamingBuffers;
252 dscaps->dwFreeHwMixingAllBuffers = This->device->drvcaps.dwFreeHwMixingAllBuffers;
253 dscaps->dwFreeHwMixingStaticBuffers = This->device->drvcaps.dwFreeHwMixingStaticBuffers;
254 dscaps->dwFreeHwMixingStreamingBuffers = This->device->drvcaps.dwFreeHwMixingStreamingBuffers;
255 dscaps->dwMaxHw3DAllBuffers = This->device->drvcaps.dwMaxHw3DAllBuffers;
256 dscaps->dwMaxHw3DStaticBuffers = This->device->drvcaps.dwMaxHw3DStaticBuffers;
257 dscaps->dwMaxHw3DStreamingBuffers = This->device->drvcaps.dwMaxHw3DStreamingBuffers;
258 dscaps->dwFreeHw3DAllBuffers = This->device->drvcaps.dwFreeHw3DAllBuffers;
259 dscaps->dwFreeHw3DStaticBuffers = This->device->drvcaps.dwFreeHw3DStaticBuffers;
260 dscaps->dwFreeHw3DStreamingBuffers = This->device->drvcaps.dwFreeHw3DStreamingBuffers;
261 dscaps->dwTotalHwMemBytes = This->device->drvcaps.dwTotalHwMemBytes;
262 dscaps->dwFreeHwMemBytes = This->device->drvcaps.dwFreeHwMemBytes;
263 dscaps->dwMaxContigFreeHwMemBytes = This->device->drvcaps.dwMaxContigFreeHwMemBytes;
264 dscaps->dwUnlockTransferRateHwBuffers = This->device->drvcaps.dwUnlockTransferRateHwBuffers;
265 dscaps->dwPlayCpuOverheadSwBuffers = This->device->drvcaps.dwPlayCpuOverheadSwBuffers;
266
267 if (TRACE_ON(dsound)) {
268 TRACE("(flags=0x%08x:\n", dscaps->dwFlags);
269 _dump_DSCAPS(dscaps->dwFlags);
270 TRACE(")\n");
271 }
272
273 return DS_OK;
274 }
275
276 static HRESULT WINAPI IDirectSound8Impl_DuplicateSoundBuffer(IDirectSound8 *iface,
277 IDirectSoundBuffer *psb, IDirectSoundBuffer **ppdsb)
278 {
279 IDirectSoundImpl *This = impl_from_IDirectSound8(iface);
280 TRACE("(%p,%p,%p)\n", This, psb, ppdsb);
281 return DirectSoundDevice_DuplicateSoundBuffer(This->device, psb, ppdsb);
282 }
283
284 static HRESULT WINAPI IDirectSound8Impl_SetCooperativeLevel(IDirectSound8 *iface, HWND hwnd,
285 DWORD level)
286 {
287 IDirectSoundImpl *This = impl_from_IDirectSound8(iface);
288 DirectSoundDevice *device = This->device;
289 DWORD oldlevel;
290 HRESULT hr = S_OK;
291
292 TRACE("(%p,%p,%s)\n", This, hwnd, dumpCooperativeLevel(level));
293
294 if (!device) {
295 WARN("not initialized\n");
296 return DSERR_UNINITIALIZED;
297 }
298
299 if (level == DSSCL_PRIORITY || level == DSSCL_EXCLUSIVE) {
300 WARN("level=%s not fully supported\n",
301 level==DSSCL_PRIORITY ? "DSSCL_PRIORITY" : "DSSCL_EXCLUSIVE");
302 }
303
304 RtlAcquireResourceExclusive(&device->buffer_list_lock, TRUE);
305 EnterCriticalSection(&device->mixlock);
306 oldlevel = device->priolevel;
307 device->priolevel = level;
308 if ((level == DSSCL_WRITEPRIMARY) != (oldlevel == DSSCL_WRITEPRIMARY)) {
309 hr = DSOUND_ReopenDevice(device, level == DSSCL_WRITEPRIMARY);
310 if (FAILED(hr))
311 device->priolevel = oldlevel;
312 else
313 DSOUND_PrimaryOpen(device);
314 }
315 LeaveCriticalSection(&device->mixlock);
316 RtlReleaseResource(&device->buffer_list_lock);
317 return hr;
318 }
319
320 static HRESULT WINAPI IDirectSound8Impl_Compact(IDirectSound8 *iface)
321 {
322 IDirectSoundImpl *This = impl_from_IDirectSound8(iface);
323
324 TRACE("(%p)\n", This);
325
326 if (!This->device) {
327 WARN("not initialized\n");
328 return DSERR_UNINITIALIZED;
329 }
330
331 if (This->device->priolevel < DSSCL_PRIORITY) {
332 WARN("incorrect priority level\n");
333 return DSERR_PRIOLEVELNEEDED;
334 }
335 return DS_OK;
336 }
337
338 static HRESULT WINAPI IDirectSound8Impl_GetSpeakerConfig(IDirectSound8 *iface, DWORD *config)
339 {
340 IDirectSoundImpl *This = impl_from_IDirectSound8(iface);
341
342 TRACE("(%p, %p)\n", This, config);
343
344 if (!This->device) {
345 WARN("not initialized\n");
346 return DSERR_UNINITIALIZED;
347 }
348 if (!config) {
349 WARN("invalid parameter: config == NULL\n");
350 return DSERR_INVALIDPARAM;
351 }
352
353 WARN("not fully functional\n");
354 *config = This->device->speaker_config;
355 return DS_OK;
356 }
357
358 static HRESULT WINAPI IDirectSound8Impl_SetSpeakerConfig(IDirectSound8 *iface, DWORD config)
359 {
360 IDirectSoundImpl *This = impl_from_IDirectSound8(iface);
361
362 TRACE("(%p,0x%08x)\n", This, config);
363
364 if (!This->device) {
365 WARN("not initialized\n");
366 return DSERR_UNINITIALIZED;
367 }
368
369 This->device->speaker_config = config;
370 WARN("not fully functional\n");
371 return DS_OK;
372 }
373
374 static HRESULT WINAPI IDirectSound8Impl_Initialize(IDirectSound8 *iface, const GUID *lpcGuid)
375 {
376 IDirectSoundImpl *This = impl_from_IDirectSound8(iface);
377 TRACE("(%p, %s)\n", This, debugstr_guid(lpcGuid));
378 return DirectSoundDevice_Initialize(&This->device, lpcGuid);
379 }
380
381 static HRESULT WINAPI IDirectSound8Impl_VerifyCertification(IDirectSound8 *iface, DWORD *certified)
382 {
383 IDirectSoundImpl *This = impl_from_IDirectSound8(iface);
384
385 TRACE("(%p, %p)\n", This, certified);
386
387 if (!This->device) {
388 WARN("not initialized\n");
389 return DSERR_UNINITIALIZED;
390 }
391
392 if (This->device->drvcaps.dwFlags & DSCAPS_CERTIFIED)
393 *certified = DS_CERTIFIED;
394 else
395 *certified = DS_UNCERTIFIED;
396
397 return DS_OK;
398 }
399
400 static const IDirectSound8Vtbl ds8_vtbl =
401 {
402 IDirectSound8Impl_QueryInterface,
403 IDirectSound8Impl_AddRef,
404 IDirectSound8Impl_Release,
405 IDirectSound8Impl_CreateSoundBuffer,
406 IDirectSound8Impl_GetCaps,
407 IDirectSound8Impl_DuplicateSoundBuffer,
408 IDirectSound8Impl_SetCooperativeLevel,
409 IDirectSound8Impl_Compact,
410 IDirectSound8Impl_GetSpeakerConfig,
411 IDirectSound8Impl_SetSpeakerConfig,
412 IDirectSound8Impl_Initialize,
413 IDirectSound8Impl_VerifyCertification
414 };
415
416 HRESULT IDirectSoundImpl_Create(IUnknown *outer_unk, REFIID riid, void **ppv, BOOL has_ds8)
417 {
418 IDirectSoundImpl *obj;
419 HRESULT hr;
420
421 TRACE("(%s, %p)\n", debugstr_guid(riid), ppv);
422
423 *ppv = NULL;
424 obj = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*obj));
425 if (!obj) {
426 WARN("out of memory\n");
427 return DSERR_OUTOFMEMORY;
428 }
429
430 setup_dsound_options();
431
432 obj->IUnknown_inner.lpVtbl = &unk_vtbl;
433 obj->IDirectSound8_iface.lpVtbl = &ds8_vtbl;
434 obj->ref = 1;
435 obj->refds = 0;
436 obj->numIfaces = 1;
437 obj->device = NULL;
438 obj->has_ds8 = has_ds8;
439
440 /* COM aggregation supported only internally */
441 if (outer_unk)
442 obj->outer_unk = outer_unk;
443 else
444 obj->outer_unk = &obj->IUnknown_inner;
445
446 hr = IUnknown_QueryInterface(&obj->IUnknown_inner, riid, ppv);
447 IUnknown_Release(&obj->IUnknown_inner);
448
449 return hr;
450 }
451
452 HRESULT DSOUND_Create(REFIID riid, void **ppv)
453 {
454 return IDirectSoundImpl_Create(NULL, riid, ppv, FALSE);
455 }
456
457 HRESULT DSOUND_Create8(REFIID riid, void **ppv)
458 {
459 return IDirectSoundImpl_Create(NULL, riid, ppv, TRUE);
460 }
461
462 /*******************************************************************************
463 * DirectSoundCreate (DSOUND.1)
464 *
465 * Creates and initializes a DirectSound interface.
466 *
467 * PARAMS
468 * lpcGUID [I] Address of the GUID that identifies the sound device.
469 * ppDS [O] Address of a variable to receive the interface pointer.
470 * pUnkOuter [I] Must be NULL.
471 *
472 * RETURNS
473 * Success: DS_OK
474 * Failure: DSERR_ALLOCATED, DSERR_INVALIDPARAM, DSERR_NOAGGREGATION,
475 * DSERR_NODRIVER, DSERR_OUTOFMEMORY
476 */
477 HRESULT WINAPI DirectSoundCreate(
478 LPCGUID lpcGUID,
479 LPDIRECTSOUND *ppDS,
480 IUnknown *pUnkOuter)
481 {
482 HRESULT hr;
483 LPDIRECTSOUND pDS;
484
485 TRACE("(%s,%p,%p)\n",debugstr_guid(lpcGUID),ppDS,pUnkOuter);
486
487 if (ppDS == NULL) {
488 WARN("invalid parameter: ppDS == NULL\n");
489 return DSERR_INVALIDPARAM;
490 }
491
492 if (pUnkOuter != NULL) {
493 WARN("invalid parameter: pUnkOuter != NULL\n");
494 *ppDS = 0;
495 return DSERR_INVALIDPARAM;
496 }
497
498 hr = DSOUND_Create(&IID_IDirectSound, (void **)&pDS);
499 if (hr == DS_OK) {
500 hr = IDirectSound_Initialize(pDS, lpcGUID);
501 if (hr != DS_OK) {
502 if (hr != DSERR_ALREADYINITIALIZED) {
503 IDirectSound_Release(pDS);
504 pDS = 0;
505 } else
506 hr = DS_OK;
507 }
508 }
509
510 *ppDS = pDS;
511
512 return hr;
513 }
514
515 /*******************************************************************************
516 * DirectSoundCreate8 (DSOUND.11)
517 *
518 * Creates and initializes a DirectSound8 interface.
519 *
520 * PARAMS
521 * lpcGUID [I] Address of the GUID that identifies the sound device.
522 * ppDS [O] Address of a variable to receive the interface pointer.
523 * pUnkOuter [I] Must be NULL.
524 *
525 * RETURNS
526 * Success: DS_OK
527 * Failure: DSERR_ALLOCATED, DSERR_INVALIDPARAM, DSERR_NOAGGREGATION,
528 * DSERR_NODRIVER, DSERR_OUTOFMEMORY
529 */
530 HRESULT WINAPI DirectSoundCreate8(
531 LPCGUID lpcGUID,
532 LPDIRECTSOUND8 *ppDS,
533 IUnknown *pUnkOuter)
534 {
535 HRESULT hr;
536 LPDIRECTSOUND8 pDS;
537
538 TRACE("(%s,%p,%p)\n",debugstr_guid(lpcGUID),ppDS,pUnkOuter);
539
540 if (ppDS == NULL) {
541 WARN("invalid parameter: ppDS == NULL\n");
542 return DSERR_INVALIDPARAM;
543 }
544
545 if (pUnkOuter != NULL) {
546 WARN("invalid parameter: pUnkOuter != NULL\n");
547 *ppDS = 0;
548 return DSERR_INVALIDPARAM;
549 }
550
551 hr = DSOUND_Create8(&IID_IDirectSound8, (void **)&pDS);
552 if (hr == DS_OK) {
553 hr = IDirectSound8_Initialize(pDS, lpcGUID);
554 if (hr != DS_OK) {
555 if (hr != DSERR_ALREADYINITIALIZED) {
556 IDirectSound8_Release(pDS);
557 pDS = 0;
558 } else
559 hr = DS_OK;
560 }
561 }
562
563 *ppDS = pDS;
564
565 return hr;
566 }
567
568 /*******************************************************************************
569 * DirectSoundDevice
570 */
571 static HRESULT DirectSoundDevice_Create(DirectSoundDevice ** ppDevice)
572 {
573 DirectSoundDevice * device;
574 TRACE("(%p)\n", ppDevice);
575
576 /* Allocate memory */
577 device = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(DirectSoundDevice));
578 if (device == NULL) {
579 WARN("out of memory\n");
580 return DSERR_OUTOFMEMORY;
581 }
582
583 device->ref = 1;
584 device->priolevel = DSSCL_NORMAL;
585 device->state = STATE_STOPPED;
586 device->speaker_config = DSSPEAKER_COMBINED(DSSPEAKER_STEREO, DSSPEAKER_GEOMETRY_WIDE);
587
588 /* 3D listener initial parameters */
589 device->ds3dl.dwSize = sizeof(DS3DLISTENER);
590 device->ds3dl.vPosition.x = 0.0;
591 device->ds3dl.vPosition.y = 0.0;
592 device->ds3dl.vPosition.z = 0.0;
593 device->ds3dl.vVelocity.x = 0.0;
594 device->ds3dl.vVelocity.y = 0.0;
595 device->ds3dl.vVelocity.z = 0.0;
596 device->ds3dl.vOrientFront.x = 0.0;
597 device->ds3dl.vOrientFront.y = 0.0;
598 device->ds3dl.vOrientFront.z = 1.0;
599 device->ds3dl.vOrientTop.x = 0.0;
600 device->ds3dl.vOrientTop.y = 1.0;
601 device->ds3dl.vOrientTop.z = 0.0;
602 device->ds3dl.flDistanceFactor = DS3D_DEFAULTDISTANCEFACTOR;
603 device->ds3dl.flRolloffFactor = DS3D_DEFAULTROLLOFFFACTOR;
604 device->ds3dl.flDopplerFactor = DS3D_DEFAULTDOPPLERFACTOR;
605
606 device->prebuf = ds_snd_queue_max;
607 device->guid = GUID_NULL;
608
609 /* Set default wave format (may need it for waveOutOpen) */
610 device->pwfx = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(WAVEFORMATEXTENSIBLE));
611 device->primary_pwfx = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(WAVEFORMATEXTENSIBLE));
612 if (!device->pwfx || !device->primary_pwfx) {
613 WARN("out of memory\n");
614 HeapFree(GetProcessHeap(),0,device->primary_pwfx);
615 HeapFree(GetProcessHeap(),0,device->pwfx);
616 HeapFree(GetProcessHeap(),0,device);
617 return DSERR_OUTOFMEMORY;
618 }
619
620 device->pwfx->wFormatTag = WAVE_FORMAT_PCM;
621 device->pwfx->nSamplesPerSec = 22050;
622 device->pwfx->wBitsPerSample = 8;
623 device->pwfx->nChannels = 2;
624 device->pwfx->nBlockAlign = device->pwfx->wBitsPerSample * device->pwfx->nChannels / 8;
625 device->pwfx->nAvgBytesPerSec = device->pwfx->nSamplesPerSec * device->pwfx->nBlockAlign;
626 device->pwfx->cbSize = 0;
627 memcpy(device->primary_pwfx, device->pwfx, sizeof(*device->pwfx));
628
629 InitializeCriticalSection(&(device->mixlock));
630 device->mixlock.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": DirectSoundDevice.mixlock");
631
632 RtlInitializeResource(&(device->buffer_list_lock));
633
634 *ppDevice = device;
635
636 return DS_OK;
637 }
638
639 static ULONG DirectSoundDevice_AddRef(DirectSoundDevice * device)
640 {
641 ULONG ref = InterlockedIncrement(&(device->ref));
642 TRACE("(%p) ref was %d\n", device, ref - 1);
643 return ref;
644 }
645
646 ULONG DirectSoundDevice_Release(DirectSoundDevice * device)
647 {
648 HRESULT hr;
649 ULONG ref = InterlockedDecrement(&(device->ref));
650 TRACE("(%p) ref was %u\n", device, ref + 1);
651 if (!ref) {
652 int i;
653
654 SetEvent(device->sleepev);
655 if (device->thread) {
656 WaitForSingleObject(device->thread, INFINITE);
657 CloseHandle(device->thread);
658 }
659 CloseHandle(device->sleepev);
660
661 EnterCriticalSection(&DSOUND_renderers_lock);
662 list_remove(&device->entry);
663 LeaveCriticalSection(&DSOUND_renderers_lock);
664
665 /* It is allowed to release this object even when buffers are playing */
666 if (device->buffers) {
667 WARN("%d secondary buffers not released\n", device->nrofbuffers);
668 for( i=0;i<device->nrofbuffers;i++)
669 secondarybuffer_destroy(device->buffers[i]);
670 }
671
672 hr = DSOUND_PrimaryDestroy(device);
673 if (hr != DS_OK)
674 WARN("DSOUND_PrimaryDestroy failed\n");
675
676 if(device->client)
677 IAudioClient_Release(device->client);
678 if(device->render)
679 IAudioRenderClient_Release(device->render);
680 if(device->clock)
681 IAudioClock_Release(device->clock);
682 if(device->volume)
683 IAudioStreamVolume_Release(device->volume);
684
685 HeapFree(GetProcessHeap(), 0, device->tmp_buffer);
686 HeapFree(GetProcessHeap(), 0, device->mix_buffer);
687 HeapFree(GetProcessHeap(), 0, device->buffer);
688 RtlDeleteResource(&device->buffer_list_lock);
689 device->mixlock.DebugInfo->Spare[0] = 0;
690 DeleteCriticalSection(&device->mixlock);
691 HeapFree(GetProcessHeap(),0,device);
692 TRACE("(%p) released\n", device);
693 }
694 return ref;
695 }
696
697 BOOL DSOUND_check_supported(IAudioClient *client, DWORD rate,
698 DWORD depth, WORD channels)
699 {
700 WAVEFORMATEX fmt, *junk;
701 HRESULT hr;
702
703 fmt.wFormatTag = WAVE_FORMAT_PCM;
704 fmt.nChannels = channels;
705 fmt.nSamplesPerSec = rate;
706 fmt.wBitsPerSample = depth;
707 fmt.nBlockAlign = (channels * depth) / 8;
708 fmt.nAvgBytesPerSec = rate * fmt.nBlockAlign;
709 fmt.cbSize = 0;
710
711 hr = IAudioClient_IsFormatSupported(client, AUDCLNT_SHAREMODE_SHARED, &fmt, &junk);
712 if(SUCCEEDED(hr))
713 CoTaskMemFree(junk);
714
715 return hr == S_OK;
716 }
717
718 UINT DSOUND_create_timer(LPTIMECALLBACK cb, DWORD_PTR user)
719 {
720 UINT triggertime = DS_TIME_DEL, res = DS_TIME_RES, id;
721 TIMECAPS time;
722
723 timeGetDevCaps(&time, sizeof(TIMECAPS));
724 TRACE("Minimum timer resolution: %u, max timer: %u\n", time.wPeriodMin, time.wPeriodMax);
725 if (triggertime < time.wPeriodMin)
726 triggertime = time.wPeriodMin;
727 if (res < time.wPeriodMin)
728 res = time.wPeriodMin;
729 if (timeBeginPeriod(res) == TIMERR_NOCANDO)
730 WARN("Could not set minimum resolution, don't expect sound\n");
731 id = timeSetEvent(triggertime, res, cb, user, TIME_PERIODIC | TIME_KILL_SYNCHRONOUS);
732 if (!id)
733 {
734 WARN("Timer not created! Retrying without TIME_KILL_SYNCHRONOUS\n");
735 id = timeSetEvent(triggertime, res, cb, user, TIME_PERIODIC);
736 if (!id)
737 ERR("Could not create timer, sound playback will not occur\n");
738 }
739 return id;
740 }
741
742 HRESULT DirectSoundDevice_Initialize(DirectSoundDevice ** ppDevice, LPCGUID lpcGUID)
743 {
744 HRESULT hr = DS_OK;
745 GUID devGUID;
746 DirectSoundDevice *device;
747 IMMDevice *mmdevice;
748
749 TRACE("(%p,%s)\n",ppDevice,debugstr_guid(lpcGUID));
750
751 if (*ppDevice != NULL) {
752 WARN("already initialized\n");
753 return DSERR_ALREADYINITIALIZED;
754 }
755
756 /* Default device? */
757 if (!lpcGUID || IsEqualGUID(lpcGUID, &GUID_NULL))
758 lpcGUID = &DSDEVID_DefaultPlayback;
759
760 if(IsEqualGUID(lpcGUID, &DSDEVID_DefaultCapture) ||
761 IsEqualGUID(lpcGUID, &DSDEVID_DefaultVoiceCapture))
762 return DSERR_NODRIVER;
763
764 if (GetDeviceID(lpcGUID, &devGUID) != DS_OK) {
765 WARN("invalid parameter: lpcGUID\n");
766 return DSERR_INVALIDPARAM;
767 }
768
769 hr = get_mmdevice(eRender, &devGUID, &mmdevice);
770 if(FAILED(hr))
771 return hr;
772
773 EnterCriticalSection(&DSOUND_renderers_lock);
774
775 LIST_FOR_EACH_ENTRY(device, &DSOUND_renderers, DirectSoundDevice, entry){
776 if(IsEqualGUID(&device->guid, &devGUID)){
777 IMMDevice_Release(mmdevice);
778 DirectSoundDevice_AddRef(device);
779 *ppDevice = device;
780 LeaveCriticalSection(&DSOUND_renderers_lock);
781 return DS_OK;
782 }
783 }
784
785 hr = DirectSoundDevice_Create(&device);
786 if(FAILED(hr)){
787 WARN("DirectSoundDevice_Create failed\n");
788 IMMDevice_Release(mmdevice);
789 LeaveCriticalSection(&DSOUND_renderers_lock);
790 return hr;
791 }
792
793 device->mmdevice = mmdevice;
794 device->guid = devGUID;
795 device->sleepev = CreateEventW(0, 0, 0, 0);
796
797 hr = DSOUND_ReopenDevice(device, FALSE);
798 if (FAILED(hr))
799 {
800 HeapFree(GetProcessHeap(), 0, device);
801 LeaveCriticalSection(&DSOUND_renderers_lock);
802 IMMDevice_Release(mmdevice);
803 WARN("DSOUND_ReopenDevice failed: %08x\n", hr);
804 return hr;
805 }
806
807 ZeroMemory(&device->drvcaps, sizeof(device->drvcaps));
808
809 if(DSOUND_check_supported(device->client, 11025, 8, 1) ||
810 DSOUND_check_supported(device->client, 22050, 8, 1) ||
811 DSOUND_check_supported(device->client, 44100, 8, 1) ||
812 DSOUND_check_supported(device->client, 48000, 8, 1) ||
813 DSOUND_check_supported(device->client, 96000, 8, 1))
814 device->drvcaps.dwFlags |= DSCAPS_PRIMARY8BIT | DSCAPS_PRIMARYMONO;
815
816 if(DSOUND_check_supported(device->client, 11025, 16, 1) ||
817 DSOUND_check_supported(device->client, 22050, 16, 1) ||
818 DSOUND_check_supported(device->client, 44100, 16, 1) ||
819 DSOUND_check_supported(device->client, 48000, 16, 1) ||
820 DSOUND_check_supported(device->client, 96000, 16, 1))
821 device->drvcaps.dwFlags |= DSCAPS_PRIMARY16BIT | DSCAPS_PRIMARYMONO;
822
823 if(DSOUND_check_supported(device->client, 11025, 8, 2) ||
824 DSOUND_check_supported(device->client, 22050, 8, 2) ||
825 DSOUND_check_supported(device->client, 44100, 8, 2) ||
826 DSOUND_check_supported(device->client, 48000, 8, 2) ||
827 DSOUND_check_supported(device->client, 96000, 8, 2))
828 device->drvcaps.dwFlags |= DSCAPS_PRIMARY8BIT | DSCAPS_PRIMARYSTEREO;
829
830 if(DSOUND_check_supported(device->client, 11025, 16, 2) ||
831 DSOUND_check_supported(device->client, 22050, 16, 2) ||
832 DSOUND_check_supported(device->client, 44100, 16, 2) ||
833 DSOUND_check_supported(device->client, 48000, 16, 2) ||
834 DSOUND_check_supported(device->client, 96000, 16, 2))
835 device->drvcaps.dwFlags |= DSCAPS_PRIMARY16BIT | DSCAPS_PRIMARYSTEREO;
836
837 /* the dsound mixer supports all of the following */
838 device->drvcaps.dwFlags |= DSCAPS_SECONDARY8BIT | DSCAPS_SECONDARY16BIT;
839 device->drvcaps.dwFlags |= DSCAPS_SECONDARYMONO | DSCAPS_SECONDARYSTEREO;
840 device->drvcaps.dwFlags |= DSCAPS_CONTINUOUSRATE;
841
842 device->drvcaps.dwPrimaryBuffers = 1;
843 device->drvcaps.dwMinSecondarySampleRate = DSBFREQUENCY_MIN;
844 device->drvcaps.dwMaxSecondarySampleRate = DSBFREQUENCY_MAX;
845 device->drvcaps.dwMaxHwMixingAllBuffers = 1;
846 device->drvcaps.dwMaxHwMixingStaticBuffers = 1;
847 device->drvcaps.dwMaxHwMixingStreamingBuffers = 1;
848
849 ZeroMemory(&device->volpan, sizeof(device->volpan));
850
851 hr = DSOUND_PrimaryCreate(device);
852 if (hr == DS_OK) {
853 device->thread = CreateThread(0, 0, DSOUND_mixthread, device, 0, 0);
854 SetThreadPriority(device->thread, THREAD_PRIORITY_TIME_CRITICAL);
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 }