3 * Copyright 1998 Marcus Meissner
4 * Copyright 1998 Rob Riggs
5 * Copyright 2000-2002 TransGaming Technologies, Inc.
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 * Most thread locking is complete. There may be a few race
23 * conditions still lurking.
25 * Tested with a Soundblaster clone, a Gravis UltraSound Classic,
26 * and a Turtle Beach Tropez+.
29 * Implement SetCooperativeLevel properly (need to address focus issues)
30 * Implement DirectSound3DBuffers (stubs in place)
31 * Use hardware 3D support if available
32 * Add critical section locking inside Release and AddRef methods
33 * Handle static buffers - put those in hardware, non-static not in hardware
34 * Hardware DuplicateSoundBuffer
35 * Proper volume calculation, and setting volume in HEL primary buffer
36 * Optimize WINMM and negotiate fragment size, decrease DS_HEL_MARGIN
40 #include "wine/port.h"
44 #include <sys/types.h>
45 #include <sys/fcntl.h>
51 #include <math.h> /* Insomnia - pow() function */
63 #include "wine/windef16.h"
64 #include "wine/winbase16.h"
65 #include "wine/debug.h"
68 #include "dsound_private.h"
70 WINE_DEFAULT_DEBUG_CHANNEL(dsound
);
72 /* these are eligible for tuning... they must be high on slow machines... */
73 /* some stuff may get more responsive with lower values though... */
74 #define DS_EMULDRIVER 0 /* some games (Quake 2, UT) refuse to accept
75 emulated dsound devices. set to 0 ! */
76 #define DS_HEL_MARGIN 5 /* HEL only: number of waveOut fragments ahead to mix in new buffers
77 * (keep this close or equal to DS_HEL_QUEUE for best results) */
78 #define DS_HEL_QUEUE 5 /* HEL only: number of waveOut fragments ahead to queue to driver
79 * (this will affect HEL sound reliability and latency) */
81 #define DS_SND_QUEUE_MAX 28 /* max number of fragments to prebuffer */
82 #define DS_SND_QUEUE_MIN 12 /* min number of fragments to prebuffer */
84 IDirectSoundImpl
* dsound
= NULL
;
86 HRESULT
mmErr(UINT err
)
89 case MMSYSERR_NOERROR
:
91 case MMSYSERR_ALLOCATED
:
92 return DSERR_ALLOCATED
;
94 case MMSYSERR_INVALHANDLE
:
95 case WAVERR_STILLPLAYING
:
96 return DSERR_GENERIC
; /* FIXME */
97 case MMSYSERR_NODRIVER
:
98 return DSERR_NODRIVER
;
100 return DSERR_OUTOFMEMORY
;
101 case MMSYSERR_INVALPARAM
:
102 case WAVERR_BADFORMAT
:
103 case WAVERR_UNPREPARED
:
104 return DSERR_INVALIDPARAM
;
105 case MMSYSERR_NOTSUPPORTED
:
106 return DSERR_UNSUPPORTED
;
108 FIXME("Unknown MMSYS error %d\n",err
);
109 return DSERR_GENERIC
;
113 int ds_emuldriver
= DS_EMULDRIVER
;
114 int ds_hel_margin
= DS_HEL_MARGIN
;
115 int ds_hel_queue
= DS_HEL_QUEUE
;
116 int ds_snd_queue_max
= DS_SND_QUEUE_MAX
;
117 int ds_snd_queue_min
= DS_SND_QUEUE_MIN
;
118 int ds_hw_accel
= DS_HW_ACCEL_FULL
;
119 int ds_default_playback
= 0;
120 int ds_default_capture
= 0;
123 * Get a config key from either the app-specific or the default config
126 inline static DWORD
get_config_key( HKEY defkey
, HKEY appkey
, const char *name
,
127 char *buffer
, DWORD size
)
129 if (appkey
&& !RegQueryValueExA( appkey
, name
, 0, NULL
, buffer
, &size
)) return 0;
130 return RegQueryValueExA( defkey
, name
, 0, NULL
, buffer
, &size
);
135 * Setup the dsound options.
138 void setup_dsound_options(void)
140 char buffer
[MAX_PATH
+1];
141 HKEY hkey
, appkey
= 0;
143 buffer
[MAX_PATH
]='\0';
145 if (RegCreateKeyExA( HKEY_LOCAL_MACHINE
, "Software\\Wine\\Wine\\Config\\dsound", 0, NULL
,
146 REG_OPTION_VOLATILE
, KEY_ALL_ACCESS
, NULL
, &hkey
, NULL
))
148 ERR("Cannot create config registry key\n" );
152 if (GetModuleFileNameA( 0, buffer
, MAX_PATH
))
156 if (!RegOpenKeyA( HKEY_LOCAL_MACHINE
, "Software\\Wine\\Wine\\Config\\AppDefaults", &tmpkey
))
158 char appname
[MAX_PATH
+16];
159 char *p
= strrchr( buffer
, '\\' );
161 appname
[MAX_PATH
]='\0';
162 strncpy(appname
,p
+1,MAX_PATH
);
163 strcat(appname
,"\\dsound");
164 TRACE("appname = [%s] \n",appname
);
165 if (RegOpenKeyA( tmpkey
, appname
, &appkey
)) appkey
= 0;
166 RegCloseKey( tmpkey
);
173 if (!get_config_key( hkey
, appkey
, "EmulDriver", buffer
, MAX_PATH
))
174 ds_emuldriver
= strcmp(buffer
, "N");
176 if (!get_config_key( hkey
, appkey
, "HELmargin", buffer
, MAX_PATH
))
177 ds_hel_margin
= atoi(buffer
);
179 if (!get_config_key( hkey
, appkey
, "HELqueue", buffer
, MAX_PATH
))
180 ds_hel_queue
= atoi(buffer
);
182 if (!get_config_key( hkey
, appkey
, "SndQueueMax", buffer
, MAX_PATH
))
183 ds_snd_queue_max
= atoi(buffer
);
185 if (!get_config_key( hkey
, appkey
, "SndQueueMin", buffer
, MAX_PATH
))
186 ds_snd_queue_min
= atoi(buffer
);
188 if (!get_config_key( hkey
, appkey
, "HardwareAcceleration", buffer
, MAX_PATH
)) {
189 if (strcmp(buffer
, "Full") == 0)
190 ds_hw_accel
= DS_HW_ACCEL_FULL
;
191 else if (strcmp(buffer
, "Standard") == 0)
192 ds_hw_accel
= DS_HW_ACCEL_STANDARD
;
193 else if (strcmp(buffer
, "Basic") == 0)
194 ds_hw_accel
= DS_HW_ACCEL_BASIC
;
195 else if (strcmp(buffer
, "Emulation") == 0)
196 ds_hw_accel
= DS_HW_ACCEL_EMULATION
;
199 if (!get_config_key( hkey
, appkey
, "DefaultPlayback", buffer
, MAX_PATH
))
200 ds_default_playback
= atoi(buffer
);
202 if (!get_config_key( hkey
, appkey
, "DefaultCapture", buffer
, MAX_PATH
))
203 ds_default_capture
= atoi(buffer
);
205 if (appkey
) RegCloseKey( appkey
);
208 if (ds_emuldriver
!= DS_EMULDRIVER
)
209 WARN("ds_emuldriver = %d (default=%d)\n",ds_emuldriver
, DS_EMULDRIVER
);
210 if (ds_hel_margin
!= DS_HEL_MARGIN
)
211 WARN("ds_hel_margin = %d (default=%d)\n",ds_hel_margin
, DS_HEL_MARGIN
);
212 if (ds_hel_queue
!= DS_HEL_QUEUE
)
213 WARN("ds_hel_queue = %d (default=%d)\n",ds_hel_queue
, DS_HEL_QUEUE
);
214 if (ds_snd_queue_max
!= DS_SND_QUEUE_MAX
)
215 WARN("ds_snd_queue_max = %d (default=%d)\n",ds_snd_queue_max
,DS_SND_QUEUE_MAX
);
216 if (ds_snd_queue_min
!= DS_SND_QUEUE_MIN
)
217 WARN("ds_snd_queue_min = %d (default=%d)\n",ds_snd_queue_min
,DS_SND_QUEUE_MIN
);
218 if (ds_hw_accel
!= DS_HW_ACCEL_FULL
)
219 WARN("ds_hw_accel = %s (default=Full)\n",
220 ds_hw_accel
==DS_HW_ACCEL_FULL
? "Full" :
221 ds_hw_accel
==DS_HW_ACCEL_STANDARD
? "Standard" :
222 ds_hw_accel
==DS_HW_ACCEL_BASIC
? "Basic" :
223 ds_hw_accel
==DS_HW_ACCEL_EMULATION
? "Emulation" :
225 if (ds_default_playback
!= 0)
226 WARN("ds_default_playback = %d (default=0)\n",ds_default_playback
);
227 if (ds_default_capture
!= 0)
228 WARN("ds_default_capture = %d (default=0)\n",ds_default_playback
);
233 /***************************************************************************
234 * GetDeviceID [DSOUND.9]
236 * Retrieves unique identifier of default device specified
239 * pGuidSrc [I] Address of device GUID.
240 * pGuidDest [O] Address to receive unique device GUID.
244 * Failure: DSERR_INVALIDPARAM
247 * pGuidSrc is a valid device GUID or DSDEVID_DefaultPlayback,
248 * DSDEVID_DefaultCapture, DSDEVID_DefaultVoicePlayback, or
249 * DSDEVID_DefaultVoiceCapture.
250 * Returns pGuidSrc if pGuidSrc is a valid device or the device
251 * GUID for the specified constants.
253 HRESULT WINAPI
GetDeviceID(LPCGUID pGuidSrc
, LPGUID pGuidDest
)
255 if ( ( pGuidSrc
== NULL
) || (pGuidDest
== NULL
) ) {
256 WARN("invalid parameter\n");
257 return DSERR_INVALIDPARAM
;
260 if ( IsEqualGUID( &DSDEVID_DefaultPlayback
, pGuidSrc
) ||
261 IsEqualGUID( &DSDEVID_DefaultVoicePlayback
, pGuidSrc
) ) {
263 int err
= mmErr(waveOutMessage((HWAVEOUT
)ds_default_playback
,DRV_QUERYDSOUNDGUID
,(DWORD
)&guid
,0));
265 memcpy(pGuidDest
, &guid
, sizeof(GUID
));
270 if ( IsEqualGUID( &DSDEVID_DefaultCapture
, pGuidSrc
) ||
271 IsEqualGUID( &DSDEVID_DefaultVoiceCapture
, pGuidSrc
) ) {
273 int err
= mmErr(waveInMessage((HWAVEIN
)ds_default_capture
,DRV_QUERYDSOUNDGUID
,(DWORD
)&guid
,0));
275 memcpy(pGuidDest
, &guid
, sizeof(GUID
));
280 memcpy(pGuidDest
, pGuidSrc
, sizeof(GUID
));
286 /***************************************************************************
287 * DirectSoundEnumerateA [DSOUND.2]
289 * Enumerate all DirectSound drivers installed in the system
292 * lpDSEnumCallback [I] Address of callback function.
293 * lpContext [I] Address of user defined context passed to callback function.
297 * Failure: DSERR_INVALIDPARAM
299 HRESULT WINAPI
DirectSoundEnumerateA(
300 LPDSENUMCALLBACKA lpDSEnumCallback
,
308 TRACE("lpDSEnumCallback = %p, lpContext = %p\n",
309 lpDSEnumCallback
, lpContext
);
311 if (lpDSEnumCallback
== NULL
) {
312 WARN("invalid parameter: lpDSEnumCallback == NULL\n");
313 return DSERR_INVALIDPARAM
;
316 devs
= waveOutGetNumDevs();
318 if (GetDeviceID(&DSDEVID_DefaultPlayback
, &guid
) == DS_OK
) {
320 for (wod
= 0; wod
< devs
; ++wod
) {
321 err
= mmErr(waveOutMessage((HWAVEOUT
)wod
,DRV_QUERYDSOUNDGUID
,(DWORD
)&temp
,0));
323 if (IsEqualGUID( &guid
, &temp
) ) {
324 err
= mmErr(waveOutMessage((HWAVEOUT
)wod
,DRV_QUERYDSOUNDDESC
,(DWORD
)&desc
,0));
326 TRACE("calling lpDSEnumCallback(%s,\"%s\",\"%s\",%p)\n",
327 debugstr_guid(&DSDEVID_DefaultPlayback
),"Primary Sound Driver",desc
.szDrvName
,lpContext
);
328 if (lpDSEnumCallback((LPGUID
)&DSDEVID_DefaultPlayback
, "Primary Sound Driver", desc
.szDrvName
, lpContext
) == FALSE
)
337 for (wod
= 0; wod
< devs
; ++wod
) {
338 err
= mmErr(waveOutMessage((HWAVEOUT
)wod
,DRV_QUERYDSOUNDDESC
,(DWORD
)&desc
,0));
340 err
= mmErr(waveOutMessage((HWAVEOUT
)wod
,DRV_QUERYDSOUNDGUID
,(DWORD
)&guid
,0));
342 TRACE("calling lpDSEnumCallback(%s,\"%s\",\"%s\",%p)\n",
343 debugstr_guid(&guid
),desc
.szDesc
,desc
.szDrvName
,lpContext
);
344 if (lpDSEnumCallback(&guid
, desc
.szDesc
, desc
.szDrvName
, lpContext
) == FALSE
)
352 /***************************************************************************
353 * DirectSoundEnumerateW [DSOUND.3]
355 * Enumerate all DirectSound drivers installed in the system
358 * lpDSEnumCallback [I] Address of callback function.
359 * lpContext [I] Address of user defined context passed to callback function.
363 * Failure: DSERR_INVALIDPARAM
365 HRESULT WINAPI
DirectSoundEnumerateW(
366 LPDSENUMCALLBACKW lpDSEnumCallback
,
373 WCHAR wDesc
[MAXPNAMELEN
];
374 WCHAR wName
[MAXPNAMELEN
];
376 TRACE("lpDSEnumCallback = %p, lpContext = %p\n",
377 lpDSEnumCallback
, lpContext
);
379 if (lpDSEnumCallback
== NULL
) {
380 WARN("invalid parameter: lpDSEnumCallback == NULL\n");
381 return DSERR_INVALIDPARAM
;
384 devs
= waveOutGetNumDevs();
386 if (GetDeviceID(&DSDEVID_DefaultPlayback
, &guid
) == DS_OK
) {
388 for (wod
= 0; wod
< devs
; ++wod
) {
389 err
= mmErr(waveOutMessage((HWAVEOUT
)wod
,DRV_QUERYDSOUNDGUID
,(DWORD
)&temp
,0));
391 if (IsEqualGUID( &guid
, &temp
) ) {
392 err
= mmErr(waveOutMessage((HWAVEOUT
)wod
,DRV_QUERYDSOUNDDESC
,(DWORD
)&desc
,0));
394 TRACE("calling lpDSEnumCallback(%s,\"%s\",\"%s\",%p)\n",
395 debugstr_guid(&DSDEVID_DefaultPlayback
),"Primary Sound Driver",desc
.szDrvName
,lpContext
);
396 MultiByteToWideChar( CP_ACP
, 0, "Primary Sound Driver", -1,
397 wDesc
, sizeof(wDesc
)/sizeof(WCHAR
) );
398 MultiByteToWideChar( CP_ACP
, 0, desc
.szDrvName
, -1,
399 wName
, sizeof(wName
)/sizeof(WCHAR
) );
400 if (lpDSEnumCallback((LPGUID
)&DSDEVID_DefaultPlayback
, wDesc
, wName
, lpContext
) == FALSE
)
409 for (wod
= 0; wod
< devs
; ++wod
) {
410 err
= mmErr(waveOutMessage((HWAVEOUT
)wod
,DRV_QUERYDSOUNDDESC
,(DWORD
)&desc
,0));
412 err
= mmErr(waveOutMessage((HWAVEOUT
)wod
,DRV_QUERYDSOUNDGUID
,(DWORD
)&guid
,0));
414 TRACE("calling lpDSEnumCallback(%s,\"%s\",\"%s\",%p)\n",
415 debugstr_guid(&guid
),desc
.szDesc
,desc
.szDrvName
,lpContext
);
416 MultiByteToWideChar( CP_ACP
, 0, desc
.szDesc
, -1,
417 wDesc
, sizeof(wDesc
)/sizeof(WCHAR
) );
418 MultiByteToWideChar( CP_ACP
, 0, desc
.szDrvName
, -1,
419 wName
, sizeof(wName
)/sizeof(WCHAR
) );
420 if (lpDSEnumCallback(&guid
, wDesc
, wName
, lpContext
) == FALSE
)
429 static void _dump_DSBCAPS(DWORD xmask
) {
434 #define FE(x) { x, #x },
435 FE(DSBCAPS_PRIMARYBUFFER
)
437 FE(DSBCAPS_LOCHARDWARE
)
438 FE(DSBCAPS_LOCSOFTWARE
)
440 FE(DSBCAPS_CTRLFREQUENCY
)
442 FE(DSBCAPS_CTRLVOLUME
)
443 FE(DSBCAPS_CTRLPOSITIONNOTIFY
)
444 FE(DSBCAPS_CTRLDEFAULT
)
446 FE(DSBCAPS_STICKYFOCUS
)
447 FE(DSBCAPS_GLOBALFOCUS
)
448 FE(DSBCAPS_GETCURRENTPOSITION2
)
449 FE(DSBCAPS_MUTE3DATMAXDISTANCE
)
454 for (i
=0;i
<sizeof(flags
)/sizeof(flags
[0]);i
++)
455 if ((flags
[i
].mask
& xmask
) == flags
[i
].mask
)
456 DPRINTF("%s ",flags
[i
].name
);
459 /*******************************************************************************
463 static HRESULT WINAPI
IDirectSoundImpl_SetCooperativeLevel(
464 LPDIRECTSOUND8 iface
,HWND hwnd
,DWORD level
466 ICOM_THIS(IDirectSoundImpl
,iface
);
468 FIXME("(%p,%08lx,%ld):stub\n",This
,(DWORD
)hwnd
,level
);
470 This
->priolevel
= level
;
475 static HRESULT WINAPI
IDirectSoundImpl_CreateSoundBuffer(
476 LPDIRECTSOUND8 iface
,LPDSBUFFERDESC dsbd
,LPLPDIRECTSOUNDBUFFER8 ppdsb
,LPUNKNOWN lpunk
478 ICOM_THIS(IDirectSoundImpl
,iface
);
481 TRACE("(%p,%p,%p,%p)\n",This
,dsbd
,ppdsb
,lpunk
);
483 if ((This
== NULL
) || (dsbd
== NULL
) || (ppdsb
== NULL
))
484 return DSERR_INVALIDPARAM
;
486 if (TRACE_ON(dsound
)) {
487 TRACE("(structsize=%ld)\n",dsbd
->dwSize
);
488 TRACE("(flags=0x%08lx:\n",dsbd
->dwFlags
);
489 _dump_DSBCAPS(dsbd
->dwFlags
);
491 TRACE("(bufferbytes=%ld)\n",dsbd
->dwBufferBytes
);
492 TRACE("(lpwfxFormat=%p)\n",dsbd
->lpwfxFormat
);
495 wfex
= dsbd
->lpwfxFormat
;
498 TRACE("(formattag=0x%04x,chans=%d,samplerate=%ld,"
499 "bytespersec=%ld,blockalign=%d,bitspersamp=%d,cbSize=%d)\n",
500 wfex
->wFormatTag
, wfex
->nChannels
, wfex
->nSamplesPerSec
,
501 wfex
->nAvgBytesPerSec
, wfex
->nBlockAlign
,
502 wfex
->wBitsPerSample
, wfex
->cbSize
);
504 if (dsbd
->dwFlags
& DSBCAPS_PRIMARYBUFFER
)
505 return PrimaryBuffer_Create(This
, (PrimaryBufferImpl
**)ppdsb
, dsbd
);
507 return SecondaryBuffer_Create(This
, (IDirectSoundBufferImpl
**)ppdsb
, dsbd
);
510 static HRESULT WINAPI
IDirectSoundImpl_DuplicateSoundBuffer(
511 LPDIRECTSOUND8 iface
,LPDIRECTSOUNDBUFFER8 pdsb
,LPLPDIRECTSOUNDBUFFER8 ppdsb
513 ICOM_THIS(IDirectSoundImpl
,iface
);
514 IDirectSoundBufferImpl
* ipdsb
=(IDirectSoundBufferImpl
*)pdsb
;
515 IDirectSoundBufferImpl
** ippdsb
=(IDirectSoundBufferImpl
**)ppdsb
;
516 TRACE("(%p,%p,%p)\n",This
,ipdsb
,ippdsb
);
518 if (ipdsb
->dsbd
.dwFlags
& DSBCAPS_PRIMARYBUFFER
) {
519 ERR("trying to duplicate primary buffer\n");
520 return DSERR_INVALIDCALL
;
524 FIXME("need to duplicate hardware buffer\n");
527 if (ipdsb
->dsbd
.dwFlags
& DSBCAPS_CTRL3D
) {
528 FIXME("need to duplicate 3D buffer\n");
531 *ippdsb
= (IDirectSoundBufferImpl
*)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY
,sizeof(IDirectSoundBufferImpl
));
533 IDirectSoundBuffer8_AddRef(pdsb
);
534 memcpy(*ippdsb
, ipdsb
, sizeof(IDirectSoundBufferImpl
));
536 (*ippdsb
)->state
= STATE_STOPPED
;
537 (*ippdsb
)->playpos
= 0;
538 (*ippdsb
)->buf_mixpos
= 0;
539 (*ippdsb
)->dsound
= This
;
540 (*ippdsb
)->parent
= ipdsb
;
541 (*ippdsb
)->hwbuf
= NULL
;
542 (*ippdsb
)->ds3db
= NULL
; /* FIXME? */
543 (*ippdsb
)->iks
= NULL
; /* FIXME? */
544 memcpy(&((*ippdsb
)->wfx
), &(ipdsb
->wfx
), sizeof((*ippdsb
)->wfx
));
545 InitializeCriticalSection(&(*ippdsb
)->lock
);
546 /* register buffer */
547 RtlAcquireResourceExclusive(&(This
->lock
), TRUE
);
549 IDirectSoundBufferImpl
**newbuffers
= (IDirectSoundBufferImpl
**)HeapReAlloc(GetProcessHeap(),0,This
->buffers
,sizeof(IDirectSoundBufferImpl
**)*(This
->nrofbuffers
+1));
551 This
->buffers
= newbuffers
;
552 This
->buffers
[This
->nrofbuffers
] = *ippdsb
;
554 TRACE("buffer count is now %d\n", This
->nrofbuffers
);
556 ERR("out of memory for buffer list! Current buffer count is %d\n", This
->nrofbuffers
);
557 /* FIXME: release buffer */
560 RtlReleaseResource(&(This
->lock
));
561 IDirectSound_AddRef(iface
);
566 static HRESULT WINAPI
IDirectSoundImpl_GetCaps(LPDIRECTSOUND8 iface
,LPDSCAPS caps
) {
567 ICOM_THIS(IDirectSoundImpl
,iface
);
568 TRACE("(%p,%p)\n",This
,caps
);
570 if (caps
== NULL
|| caps
->dwSize
!=sizeof(*caps
))
571 return DSERR_INVALIDPARAM
;
573 caps
->dwFlags
= This
->drvcaps
.dwFlags
;
574 TRACE("(flags=0x%08lx)\n",caps
->dwFlags
);
576 /* FIXME: copy caps from This->drvcaps */
577 caps
->dwMinSecondarySampleRate
= DSBFREQUENCY_MIN
;
578 caps
->dwMaxSecondarySampleRate
= DSBFREQUENCY_MAX
;
580 caps
->dwPrimaryBuffers
= 1;
582 caps
->dwMaxHwMixingAllBuffers
= 0;
583 caps
->dwMaxHwMixingStaticBuffers
= 0;
584 caps
->dwMaxHwMixingStreamingBuffers
= 0;
586 caps
->dwFreeHwMixingAllBuffers
= 0;
587 caps
->dwFreeHwMixingStaticBuffers
= 0;
588 caps
->dwFreeHwMixingStreamingBuffers
= 0;
590 caps
->dwMaxHw3DAllBuffers
= 0;
591 caps
->dwMaxHw3DStaticBuffers
= 0;
592 caps
->dwMaxHw3DStreamingBuffers
= 0;
594 caps
->dwFreeHw3DAllBuffers
= 0;
595 caps
->dwFreeHw3DStaticBuffers
= 0;
596 caps
->dwFreeHw3DStreamingBuffers
= 0;
598 caps
->dwTotalHwMemBytes
= 0;
600 caps
->dwFreeHwMemBytes
= 0;
602 caps
->dwMaxContigFreeHwMemBytes
= 0;
604 caps
->dwUnlockTransferRateHwBuffers
= 4096; /* But we have none... */
606 caps
->dwPlayCpuOverheadSwBuffers
= 1; /* 1% */
611 static ULONG WINAPI
IDirectSoundImpl_AddRef(LPDIRECTSOUND8 iface
) {
612 ICOM_THIS(IDirectSoundImpl
,iface
);
613 return ++(This
->ref
);
616 static ULONG WINAPI
IDirectSoundImpl_Release(LPDIRECTSOUND8 iface
) {
617 ICOM_THIS(IDirectSoundImpl
,iface
);
618 TRACE("(%p), ref was %ld\n",This
,This
->ref
);
619 if (!--(This
->ref
)) {
622 timeKillEvent(This
->timerID
);
623 timeEndPeriod(DS_TIME_RES
);
626 for( i
=0;i
<This
->nrofbuffers
;i
++)
627 IDirectSoundBuffer8_Release((LPDIRECTSOUNDBUFFER8
)This
->buffers
[i
]);
630 DSOUND_PrimaryDestroy(This
);
632 RtlDeleteResource(&This
->lock
);
633 DeleteCriticalSection(&This
->mixlock
);
635 IDsDriver_Close(This
->driver
);
637 if (This
->drvdesc
.dwFlags
& DSDDESC_DOMMSYSTEMOPEN
) {
638 waveOutClose(This
->hwo
);
641 IDsDriver_Release(This
->driver
);
643 HeapFree(GetProcessHeap(),0,This
);
650 static HRESULT WINAPI
IDirectSoundImpl_SetSpeakerConfig(
651 LPDIRECTSOUND8 iface
,DWORD config
653 ICOM_THIS(IDirectSoundImpl
,iface
);
654 FIXME("(%p,0x%08lx):stub\n",This
,config
);
658 static HRESULT WINAPI
IDirectSoundImpl_QueryInterface(
659 LPDIRECTSOUND8 iface
,REFIID riid
,LPVOID
*ppobj
661 ICOM_THIS(IDirectSoundImpl
,iface
);
663 if ( IsEqualGUID( &IID_IDirectSound3DListener
, riid
) ) {
664 ERR("app requested IDirectSound3DListener on dsound object\n");
669 FIXME("(%p,%s,%p)\n",This
,debugstr_guid(riid
),ppobj
);
670 return E_NOINTERFACE
;
673 static HRESULT WINAPI
IDirectSoundImpl_Compact(
674 LPDIRECTSOUND8 iface
)
676 ICOM_THIS(IDirectSoundImpl
,iface
);
677 TRACE("(%p)\n", This
);
681 static HRESULT WINAPI
IDirectSoundImpl_GetSpeakerConfig(
682 LPDIRECTSOUND8 iface
,
683 LPDWORD lpdwSpeakerConfig
)
685 ICOM_THIS(IDirectSoundImpl
,iface
);
686 TRACE("(%p, %p)\n", This
, lpdwSpeakerConfig
);
687 *lpdwSpeakerConfig
= DSSPEAKER_STEREO
| (DSSPEAKER_GEOMETRY_NARROW
<< 16);
691 static HRESULT WINAPI
IDirectSoundImpl_Initialize(
692 LPDIRECTSOUND8 iface
,
695 ICOM_THIS(IDirectSoundImpl
,iface
);
696 TRACE("(%p, %p)\n", This
, lpcGuid
);
700 static HRESULT WINAPI
IDirectSoundImpl_VerifyCertification(
701 LPDIRECTSOUND8 iface
,
702 LPDWORD pdwCertified
)
704 ICOM_THIS(IDirectSoundImpl
,iface
);
705 TRACE("(%p, %p)\n", This
, pdwCertified
);
706 *pdwCertified
= DS_CERTIFIED
;
710 static ICOM_VTABLE(IDirectSound8
) dsvt
=
712 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
713 IDirectSoundImpl_QueryInterface
,
714 IDirectSoundImpl_AddRef
,
715 IDirectSoundImpl_Release
,
716 IDirectSoundImpl_CreateSoundBuffer
,
717 IDirectSoundImpl_GetCaps
,
718 IDirectSoundImpl_DuplicateSoundBuffer
,
719 IDirectSoundImpl_SetCooperativeLevel
,
720 IDirectSoundImpl_Compact
,
721 IDirectSoundImpl_GetSpeakerConfig
,
722 IDirectSoundImpl_SetSpeakerConfig
,
723 IDirectSoundImpl_Initialize
,
724 IDirectSoundImpl_VerifyCertification
728 /*******************************************************************************
729 * DirectSoundCreate (DSOUND.1)
731 * Creates and initializes a DirectSound interface.
734 * lpcGUID [I] Address of the GUID that identifies the sound device.
735 * ppDS [O] Address of a variable to receive the interface pointer.
736 * pUnkOuter [I] Must be NULL.
740 * Failure: DSERR_ALLOCATED, DSERR_INVALIDPARAM, DSERR_NOAGGREGATION,
741 * DSERR_NODRIVER, DSERR_OUTOFMEMORY
743 HRESULT WINAPI
DirectSoundCreate8(LPCGUID lpcGUID
,LPDIRECTSOUND8
*ppDS
,IUnknown
*pUnkOuter
)
745 IDirectSoundImpl
** ippDS
=(IDirectSoundImpl
**)ppDS
;
746 PIDSDRIVER drv
= NULL
;
748 HRESULT err
= DSERR_INVALIDPARAM
;
750 BOOLEAN found
= FALSE
;
752 TRACE("(%s,%p,%p)\n",debugstr_guid(lpcGUID
),ippDS
,pUnkOuter
);
755 WARN("invalid parameter\n");
756 return DSERR_INVALIDPARAM
;
759 /* Get dsound configuration */
760 setup_dsound_options();
762 /* Default device? */
763 if (!lpcGUID
|| IsEqualGUID(lpcGUID
, &GUID_NULL
))
764 lpcGUID
= &DSDEVID_DefaultPlayback
;
766 if (GetDeviceID(lpcGUID
, &devGuid
) != DS_OK
) {
767 WARN("invalid parameter\n");
768 return DSERR_INVALIDPARAM
;
772 if (IsEqualGUID(&devGuid
, &dsound
->guid
) ) {
773 ERR("dsound already opened\n");
774 IDirectSound_AddRef((LPDIRECTSOUND
)dsound
);
778 ERR("different dsound already opened\n");
782 /* Enumerate WINMM audio devices and find the one we want */
783 wodn
= waveOutGetNumDevs();
784 if (!wodn
) return DSERR_NODRIVER
;
786 TRACE(" expecting GUID %s.\n", debugstr_guid(&devGuid
));
788 for (wod
=0; wod
<wodn
; wod
++) {
790 err
= mmErr(waveOutMessage((HWAVEOUT
)wod
,DRV_QUERYDSOUNDGUID
,(DWORD
)(&guid
),0));
792 WARN("waveOutMessage failed; err=%lx\n",err
);
795 TRACE("got GUID %s for wod %d.\n", debugstr_guid(&guid
), wod
);
796 if (IsEqualGUID( &devGuid
, &guid
) ) {
804 WARN("invalid parameter\n");
805 return DSERR_INVALIDPARAM
;
808 if (found
== FALSE
) {
809 WARN("No device found matching given ID - trying with default one !\n");
810 wod
= ds_default_playback
;
813 /* DRV_QUERYDSOUNDIFACE is a "Wine extension" to get the DSound interface */
814 waveOutMessage((HWAVEOUT
)wod
, DRV_QUERYDSOUNDIFACE
, (DWORD
)&drv
, 0);
816 /* Disable the direct sound driver to force emulation if requested. */
817 if (ds_hw_accel
== DS_HW_ACCEL_EMULATION
)
820 /* Allocate memory */
821 *ippDS
= (IDirectSoundImpl
*)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY
,sizeof(IDirectSoundImpl
));
823 return DSERR_OUTOFMEMORY
;
825 (*ippDS
)->lpVtbl
= &dsvt
;
828 (*ippDS
)->driver
= drv
;
829 (*ippDS
)->priolevel
= DSSCL_NORMAL
;
830 (*ippDS
)->fraglen
= 0;
831 (*ippDS
)->hwbuf
= NULL
;
832 (*ippDS
)->buffer
= NULL
;
833 (*ippDS
)->buflen
= 0;
834 (*ippDS
)->writelead
= 0;
835 (*ippDS
)->state
= STATE_STOPPED
;
836 (*ippDS
)->nrofbuffers
= 0;
837 (*ippDS
)->buffers
= NULL
;
838 (*ippDS
)->listener
= NULL
;
840 (*ippDS
)->prebuf
= ds_snd_queue_max
;
841 (*ippDS
)->guid
= devGuid
;
843 /* Get driver description */
845 IDsDriver_GetDriverDesc(drv
,&((*ippDS
)->drvdesc
));
847 /* if no DirectSound interface available, use WINMM API instead */
848 (*ippDS
)->drvdesc
.dwFlags
= DSDDESC_DOMMSYSTEMOPEN
| DSDDESC_DOMMSYSTEMSETFORMAT
;
851 (*ippDS
)->drvdesc
.dnDevNode
= wod
;
853 /* Set default wave format (may need it for waveOutOpen) */
854 (*ippDS
)->wfx
.wFormatTag
= WAVE_FORMAT_PCM
;
855 /* We rely on the sound driver to return the actual sound format of
856 * the device if it does not support 22050x8x2 and is given the
857 * WAVE_DIRECTSOUND flag.
859 (*ippDS
)->wfx
.nSamplesPerSec
= 22050;
860 (*ippDS
)->wfx
.wBitsPerSample
= 8;
861 (*ippDS
)->wfx
.nChannels
= 2;
862 (*ippDS
)->wfx
.nBlockAlign
= (*ippDS
)->wfx
.wBitsPerSample
* (*ippDS
)->wfx
.nChannels
/ 8;
863 (*ippDS
)->wfx
.nAvgBytesPerSec
= (*ippDS
)->wfx
.nSamplesPerSec
* (*ippDS
)->wfx
.nBlockAlign
;
864 (*ippDS
)->wfx
.cbSize
= 0;
866 /* If the driver requests being opened through MMSYSTEM
867 * (which is recommended by the DDK), it is supposed to happen
868 * before the DirectSound interface is opened */
869 if ((*ippDS
)->drvdesc
.dwFlags
& DSDDESC_DOMMSYSTEMOPEN
)
871 DWORD flags
= CALLBACK_FUNCTION
;
873 /* disable direct sound if requested */
874 if (ds_hw_accel
!= DS_HW_ACCEL_EMULATION
)
875 flags
|= WAVE_DIRECTSOUND
;
877 err
= mmErr(waveOutOpen(&((*ippDS
)->hwo
),
878 (*ippDS
)->drvdesc
.dnDevNode
, &((*ippDS
)->wfx
),
879 (DWORD
)DSOUND_callback
, (DWORD
)(*ippDS
),
883 if (drv
&& (err
== DS_OK
))
884 err
= IDsDriver_Open(drv
);
886 /* FIXME: do we want to handle a temporarily busy device? */
888 HeapFree(GetProcessHeap(),0,*ippDS
);
893 /* the driver is now open, so it's now allowed to call GetCaps */
895 IDsDriver_GetCaps(drv
,&((*ippDS
)->drvcaps
));
897 /* FIXME: We should check the device capabilities */
898 (*ippDS
)->drvcaps
.dwFlags
=
899 DSCAPS_PRIMARY16BIT
| DSCAPS_PRIMARYSTEREO
;
901 (*ippDS
)->drvcaps
.dwFlags
|= DSCAPS_EMULDRIVER
;
904 DSOUND_RecalcVolPan(&((*ippDS
)->volpan
));
906 InitializeCriticalSection(&((*ippDS
)->mixlock
));
907 RtlInitializeResource(&((*ippDS
)->lock
));
911 DSOUND_PrimaryCreate(dsound
);
912 timeBeginPeriod(DS_TIME_RES
);
913 dsound
->timerID
= timeSetEvent(DS_TIME_DEL
, DS_TIME_RES
, DSOUND_timer
,
914 (DWORD
)dsound
, TIME_PERIODIC
| TIME_CALLBACK_FUNCTION
);
920 /*******************************************************************************
921 * DirectSound ClassFactory
925 /* IUnknown fields */
926 ICOM_VFIELD(IClassFactory
);
930 static HRESULT WINAPI
931 DSCF_QueryInterface(LPCLASSFACTORY iface
,REFIID riid
,LPVOID
*ppobj
) {
932 ICOM_THIS(IClassFactoryImpl
,iface
);
934 FIXME("(%p)->(%s,%p),stub!\n",This
,debugstr_guid(riid
),ppobj
);
935 return E_NOINTERFACE
;
939 DSCF_AddRef(LPCLASSFACTORY iface
) {
940 ICOM_THIS(IClassFactoryImpl
,iface
);
941 return ++(This
->ref
);
944 static ULONG WINAPI
DSCF_Release(LPCLASSFACTORY iface
) {
945 ICOM_THIS(IClassFactoryImpl
,iface
);
946 /* static class, won't be freed */
947 return --(This
->ref
);
950 static HRESULT WINAPI
DSCF_CreateInstance(
951 LPCLASSFACTORY iface
,LPUNKNOWN pOuter
,REFIID riid
,LPVOID
*ppobj
953 ICOM_THIS(IClassFactoryImpl
,iface
);
955 TRACE("(%p)->(%p,%s,%p)\n",This
,pOuter
,debugstr_guid(riid
),ppobj
);
956 if ( IsEqualGUID( &IID_IDirectSound
, riid
) ||
957 IsEqualGUID( &IID_IDirectSound8
, riid
) ) {
958 /* FIXME: reuse already created dsound if present? */
959 return DirectSoundCreate8(0,(LPDIRECTSOUND8
*)ppobj
,pOuter
);
961 if ( IsEqualGUID( &IID_IDirectSoundCapture
, riid
) ||
962 IsEqualGUID( &IID_IDirectSoundCapture8
, riid
) ) {
963 return DirectSoundCaptureCreate8(0,(LPDIRECTSOUNDCAPTURE8
*)ppobj
,pOuter
);
965 if ( IsEqualGUID( &IID_IKsPropertySet
, riid
) ) {
966 return IKsPropertySetImpl_Create(0,(IKsPropertySetImpl
**)ppobj
);
969 FIXME("(%p,%p,%s,%p) Interface not found!\n",This
,pOuter
,debugstr_guid(riid
),ppobj
);
970 return E_NOINTERFACE
;
973 static HRESULT WINAPI
DSCF_LockServer(LPCLASSFACTORY iface
,BOOL dolock
) {
974 ICOM_THIS(IClassFactoryImpl
,iface
);
975 FIXME("(%p)->(%d),stub!\n",This
,dolock
);
979 static ICOM_VTABLE(IClassFactory
) DSCF_Vtbl
= {
980 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
987 static IClassFactoryImpl DSOUND_CF
= {&DSCF_Vtbl
, 1 };
989 /*******************************************************************************
990 * DllGetClassObject [DSOUND.5]
991 * Retrieves class object from a DLL object
994 * Docs say returns STDAPI
997 * rclsid [I] CLSID for the class object
998 * riid [I] Reference to identifier of interface for class object
999 * ppv [O] Address of variable to receive interface pointer for riid
1003 * Failure: CLASS_E_CLASSNOTAVAILABLE, E_OUTOFMEMORY, E_INVALIDARG,
1006 DWORD WINAPI
DSOUND_DllGetClassObject(REFCLSID rclsid
,REFIID riid
,LPVOID
*ppv
)
1008 TRACE("(%s,%s,%p)\n", debugstr_guid(rclsid
), debugstr_guid(riid
), ppv
);
1009 if ( IsEqualCLSID( &IID_IClassFactory
, riid
) ) {
1010 *ppv
= (LPVOID
)&DSOUND_CF
;
1011 IClassFactory_AddRef((IClassFactory
*)*ppv
);
1015 FIXME("(%s,%s,%p): no interface found.\n", debugstr_guid(rclsid
), debugstr_guid(riid
), ppv
);
1016 return CLASS_E_CLASSNOTAVAILABLE
;
1020 /*******************************************************************************
1021 * DllCanUnloadNow [DSOUND.4]
1022 * Determines whether the DLL is in use.
1028 DWORD WINAPI
DSOUND_DllCanUnloadNow(void)
1030 FIXME("(void): stub\n");