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.2]
236 * Retrieves unique identifier of default device specified
240 * Failure: DSERR_INVALIDPARAM
242 HRESULT WINAPI
GetDeviceID(LPCGUID pGuidSrc
, LPGUID pGuidDest
)
244 if ( ( pGuidSrc
== NULL
) || (pGuidDest
== NULL
) ) {
245 WARN("invalid parameter\n");
246 return DSERR_INVALIDPARAM
;
249 if ( IsEqualGUID( &DSDEVID_DefaultPlayback
, pGuidSrc
) ||
250 IsEqualGUID( &DSDEVID_DefaultVoicePlayback
, pGuidSrc
) ) {
252 int err
= mmErr(waveOutMessage((HWAVEOUT
)ds_default_playback
,DRV_QUERYDSOUNDGUID
,(DWORD
)&guid
,0));
254 memcpy(pGuidDest
, &guid
, sizeof(GUID
));
259 if ( IsEqualGUID( &DSDEVID_DefaultCapture
, pGuidSrc
) ||
260 IsEqualGUID( &DSDEVID_DefaultVoiceCapture
, pGuidSrc
) ) {
262 int err
= mmErr(waveInMessage((HWAVEIN
)ds_default_capture
,DRV_QUERYDSOUNDGUID
,(DWORD
)&guid
,0));
264 memcpy(pGuidDest
, &guid
, sizeof(GUID
));
269 memcpy(pGuidDest
, pGuidSrc
, sizeof(GUID
));
275 /***************************************************************************
276 * DirectSoundEnumerateA [DSOUND.2]
278 * Enumerate all DirectSound drivers installed in the system
282 * Failure: DSERR_INVALIDPARAM
284 HRESULT WINAPI
DirectSoundEnumerateA(
285 LPDSENUMCALLBACKA lpDSEnumCallback
,
293 TRACE("lpDSEnumCallback = %p, lpContext = %p\n",
294 lpDSEnumCallback
, lpContext
);
296 if (lpDSEnumCallback
== NULL
) {
297 WARN("invalid parameter\n");
298 return DSERR_INVALIDPARAM
;
301 devs
= waveOutGetNumDevs();
303 if (GetDeviceID(&DSDEVID_DefaultPlayback
, &guid
) == DS_OK
) {
305 for (wod
= 0; wod
< devs
; ++wod
) {
306 err
= mmErr(waveOutMessage((HWAVEOUT
)wod
,DRV_QUERYDSOUNDGUID
,(DWORD
)&temp
,0));
308 if (IsEqualGUID( &guid
, &temp
) ) {
309 err
= mmErr(waveOutMessage((HWAVEOUT
)wod
,DRV_QUERYDSOUNDDESC
,(DWORD
)&desc
,0));
311 TRACE("calling lpDSEnumCallback(%s,\"%s\",\"%s\",%p)\n",
312 debugstr_guid(&DSDEVID_DefaultPlayback
),"Primary Sound Driver",desc
.szDrvName
,lpContext
);
313 if (lpDSEnumCallback((LPGUID
)&DSDEVID_DefaultPlayback
, "Primary Sound Driver", desc
.szDrvName
, lpContext
) == FALSE
)
322 for (wod
= 0; wod
< devs
; ++wod
) {
323 err
= mmErr(waveOutMessage((HWAVEOUT
)wod
,DRV_QUERYDSOUNDDESC
,(DWORD
)&desc
,0));
325 err
= mmErr(waveOutMessage((HWAVEOUT
)wod
,DRV_QUERYDSOUNDGUID
,(DWORD
)&guid
,0));
327 TRACE("calling lpDSEnumCallback(%s,\"%s\",\"%s\",%p)\n",
328 debugstr_guid(&guid
),desc
.szDesc
,desc
.szDrvName
,lpContext
);
329 if (lpDSEnumCallback(&guid
, desc
.szDesc
, desc
.szDrvName
, lpContext
) == FALSE
)
337 /***************************************************************************
338 * DirectSoundEnumerateW [DSOUND.3]
340 * Enumerate all DirectSound drivers installed in the system
344 * Failure: DSERR_INVALIDPARAM
346 HRESULT WINAPI
DirectSoundEnumerateW(
347 LPDSENUMCALLBACKW lpDSEnumCallback
,
354 WCHAR wDesc
[MAXPNAMELEN
];
355 WCHAR wName
[MAXPNAMELEN
];
357 TRACE("lpDSEnumCallback = %p, lpContext = %p\n",
358 lpDSEnumCallback
, lpContext
);
360 if (lpDSEnumCallback
== NULL
) {
361 WARN("invalid parameter\n");
362 return DSERR_INVALIDPARAM
;
365 devs
= waveOutGetNumDevs();
367 if (GetDeviceID(&DSDEVID_DefaultPlayback
, &guid
) == DS_OK
) {
369 for (wod
= 0; wod
< devs
; ++wod
) {
370 err
= mmErr(waveOutMessage((HWAVEOUT
)wod
,DRV_QUERYDSOUNDGUID
,(DWORD
)&temp
,0));
372 if (IsEqualGUID( &guid
, &temp
) ) {
373 err
= mmErr(waveOutMessage((HWAVEOUT
)wod
,DRV_QUERYDSOUNDDESC
,(DWORD
)&desc
,0));
375 TRACE("calling lpDSEnumCallback(%s,\"%s\",\"%s\",%p)\n",
376 debugstr_guid(&DSDEVID_DefaultPlayback
),"Primary Sound Driver",desc
.szDrvName
,lpContext
);
377 MultiByteToWideChar( CP_ACP
, 0, "Primary Sound Driver", -1,
378 wDesc
, sizeof(wDesc
)/sizeof(WCHAR
) );
379 MultiByteToWideChar( CP_ACP
, 0, desc
.szDrvName
, -1,
380 wName
, sizeof(wName
)/sizeof(WCHAR
) );
381 if (lpDSEnumCallback((LPGUID
)&DSDEVID_DefaultPlayback
, wDesc
, wName
, lpContext
) == FALSE
)
390 for (wod
= 0; wod
< devs
; ++wod
) {
391 err
= mmErr(waveOutMessage((HWAVEOUT
)wod
,DRV_QUERYDSOUNDDESC
,(DWORD
)&desc
,0));
393 err
= mmErr(waveOutMessage((HWAVEOUT
)wod
,DRV_QUERYDSOUNDGUID
,(DWORD
)&guid
,0));
395 TRACE("calling lpDSEnumCallback(%s,\"%s\",\"%s\",%p)\n",
396 debugstr_guid(&guid
),desc
.szDesc
,desc
.szDrvName
,lpContext
);
397 MultiByteToWideChar( CP_ACP
, 0, desc
.szDesc
, -1,
398 wDesc
, sizeof(wDesc
)/sizeof(WCHAR
) );
399 MultiByteToWideChar( CP_ACP
, 0, desc
.szDrvName
, -1,
400 wName
, sizeof(wName
)/sizeof(WCHAR
) );
401 if (lpDSEnumCallback(&guid
, wDesc
, wName
, lpContext
) == FALSE
)
410 static void _dump_DSBCAPS(DWORD xmask
) {
415 #define FE(x) { x, #x },
416 FE(DSBCAPS_PRIMARYBUFFER
)
418 FE(DSBCAPS_LOCHARDWARE
)
419 FE(DSBCAPS_LOCSOFTWARE
)
421 FE(DSBCAPS_CTRLFREQUENCY
)
423 FE(DSBCAPS_CTRLVOLUME
)
424 FE(DSBCAPS_CTRLPOSITIONNOTIFY
)
425 FE(DSBCAPS_CTRLDEFAULT
)
427 FE(DSBCAPS_STICKYFOCUS
)
428 FE(DSBCAPS_GLOBALFOCUS
)
429 FE(DSBCAPS_GETCURRENTPOSITION2
)
430 FE(DSBCAPS_MUTE3DATMAXDISTANCE
)
435 for (i
=0;i
<sizeof(flags
)/sizeof(flags
[0]);i
++)
436 if ((flags
[i
].mask
& xmask
) == flags
[i
].mask
)
437 DPRINTF("%s ",flags
[i
].name
);
440 /*******************************************************************************
444 static HRESULT WINAPI
IDirectSoundImpl_SetCooperativeLevel(
445 LPDIRECTSOUND8 iface
,HWND hwnd
,DWORD level
447 ICOM_THIS(IDirectSoundImpl
,iface
);
449 FIXME("(%p,%08lx,%ld):stub\n",This
,(DWORD
)hwnd
,level
);
451 This
->priolevel
= level
;
456 static HRESULT WINAPI
IDirectSoundImpl_CreateSoundBuffer(
457 LPDIRECTSOUND8 iface
,LPDSBUFFERDESC dsbd
,LPLPDIRECTSOUNDBUFFER8 ppdsb
,LPUNKNOWN lpunk
459 ICOM_THIS(IDirectSoundImpl
,iface
);
462 TRACE("(%p,%p,%p,%p)\n",This
,dsbd
,ppdsb
,lpunk
);
464 if ((This
== NULL
) || (dsbd
== NULL
) || (ppdsb
== NULL
))
465 return DSERR_INVALIDPARAM
;
467 if (TRACE_ON(dsound
)) {
468 TRACE("(structsize=%ld)\n",dsbd
->dwSize
);
469 TRACE("(flags=0x%08lx:\n",dsbd
->dwFlags
);
470 _dump_DSBCAPS(dsbd
->dwFlags
);
472 TRACE("(bufferbytes=%ld)\n",dsbd
->dwBufferBytes
);
473 TRACE("(lpwfxFormat=%p)\n",dsbd
->lpwfxFormat
);
476 wfex
= dsbd
->lpwfxFormat
;
479 TRACE("(formattag=0x%04x,chans=%d,samplerate=%ld,"
480 "bytespersec=%ld,blockalign=%d,bitspersamp=%d,cbSize=%d)\n",
481 wfex
->wFormatTag
, wfex
->nChannels
, wfex
->nSamplesPerSec
,
482 wfex
->nAvgBytesPerSec
, wfex
->nBlockAlign
,
483 wfex
->wBitsPerSample
, wfex
->cbSize
);
485 if (dsbd
->dwFlags
& DSBCAPS_PRIMARYBUFFER
)
486 return PrimaryBuffer_Create(This
, (PrimaryBufferImpl
**)ppdsb
, dsbd
);
488 return SecondaryBuffer_Create(This
, (IDirectSoundBufferImpl
**)ppdsb
, dsbd
);
491 static HRESULT WINAPI
IDirectSoundImpl_DuplicateSoundBuffer(
492 LPDIRECTSOUND8 iface
,LPDIRECTSOUNDBUFFER8 pdsb
,LPLPDIRECTSOUNDBUFFER8 ppdsb
494 ICOM_THIS(IDirectSoundImpl
,iface
);
495 IDirectSoundBufferImpl
* ipdsb
=(IDirectSoundBufferImpl
*)pdsb
;
496 IDirectSoundBufferImpl
** ippdsb
=(IDirectSoundBufferImpl
**)ppdsb
;
497 TRACE("(%p,%p,%p)\n",This
,ipdsb
,ippdsb
);
499 if (ipdsb
->dsbd
.dwFlags
& DSBCAPS_PRIMARYBUFFER
) {
500 ERR("trying to duplicate primary buffer\n");
501 return DSERR_INVALIDCALL
;
505 FIXME("need to duplicate hardware buffer\n");
508 if (ipdsb
->dsbd
.dwFlags
& DSBCAPS_CTRL3D
) {
509 FIXME("need to duplicate 3D buffer\n");
512 *ippdsb
= (IDirectSoundBufferImpl
*)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY
,sizeof(IDirectSoundBufferImpl
));
514 IDirectSoundBuffer8_AddRef(pdsb
);
515 memcpy(*ippdsb
, ipdsb
, sizeof(IDirectSoundBufferImpl
));
517 (*ippdsb
)->state
= STATE_STOPPED
;
518 (*ippdsb
)->playpos
= 0;
519 (*ippdsb
)->buf_mixpos
= 0;
520 (*ippdsb
)->dsound
= This
;
521 (*ippdsb
)->parent
= ipdsb
;
522 (*ippdsb
)->hwbuf
= NULL
;
523 (*ippdsb
)->ds3db
= NULL
; /* FIXME? */
524 (*ippdsb
)->iks
= NULL
; /* FIXME? */
525 memcpy(&((*ippdsb
)->wfx
), &(ipdsb
->wfx
), sizeof((*ippdsb
)->wfx
));
526 InitializeCriticalSection(&(*ippdsb
)->lock
);
527 /* register buffer */
528 RtlAcquireResourceExclusive(&(This
->lock
), TRUE
);
530 IDirectSoundBufferImpl
**newbuffers
= (IDirectSoundBufferImpl
**)HeapReAlloc(GetProcessHeap(),0,This
->buffers
,sizeof(IDirectSoundBufferImpl
**)*(This
->nrofbuffers
+1));
532 This
->buffers
= newbuffers
;
533 This
->buffers
[This
->nrofbuffers
] = *ippdsb
;
535 TRACE("buffer count is now %d\n", This
->nrofbuffers
);
537 ERR("out of memory for buffer list! Current buffer count is %d\n", This
->nrofbuffers
);
538 /* FIXME: release buffer */
541 RtlReleaseResource(&(This
->lock
));
542 IDirectSound_AddRef(iface
);
547 static HRESULT WINAPI
IDirectSoundImpl_GetCaps(LPDIRECTSOUND8 iface
,LPDSCAPS caps
) {
548 ICOM_THIS(IDirectSoundImpl
,iface
);
549 TRACE("(%p,%p)\n",This
,caps
);
551 if (caps
== NULL
|| caps
->dwSize
!=sizeof(*caps
))
552 return DSERR_INVALIDPARAM
;
554 caps
->dwFlags
= This
->drvcaps
.dwFlags
;
555 TRACE("(flags=0x%08lx)\n",caps
->dwFlags
);
557 /* FIXME: copy caps from This->drvcaps */
558 caps
->dwMinSecondarySampleRate
= DSBFREQUENCY_MIN
;
559 caps
->dwMaxSecondarySampleRate
= DSBFREQUENCY_MAX
;
561 caps
->dwPrimaryBuffers
= 1;
563 caps
->dwMaxHwMixingAllBuffers
= 0;
564 caps
->dwMaxHwMixingStaticBuffers
= 0;
565 caps
->dwMaxHwMixingStreamingBuffers
= 0;
567 caps
->dwFreeHwMixingAllBuffers
= 0;
568 caps
->dwFreeHwMixingStaticBuffers
= 0;
569 caps
->dwFreeHwMixingStreamingBuffers
= 0;
571 caps
->dwMaxHw3DAllBuffers
= 0;
572 caps
->dwMaxHw3DStaticBuffers
= 0;
573 caps
->dwMaxHw3DStreamingBuffers
= 0;
575 caps
->dwFreeHw3DAllBuffers
= 0;
576 caps
->dwFreeHw3DStaticBuffers
= 0;
577 caps
->dwFreeHw3DStreamingBuffers
= 0;
579 caps
->dwTotalHwMemBytes
= 0;
581 caps
->dwFreeHwMemBytes
= 0;
583 caps
->dwMaxContigFreeHwMemBytes
= 0;
585 caps
->dwUnlockTransferRateHwBuffers
= 4096; /* But we have none... */
587 caps
->dwPlayCpuOverheadSwBuffers
= 1; /* 1% */
592 static ULONG WINAPI
IDirectSoundImpl_AddRef(LPDIRECTSOUND8 iface
) {
593 ICOM_THIS(IDirectSoundImpl
,iface
);
594 return ++(This
->ref
);
597 static ULONG WINAPI
IDirectSoundImpl_Release(LPDIRECTSOUND8 iface
) {
598 ICOM_THIS(IDirectSoundImpl
,iface
);
599 TRACE("(%p), ref was %ld\n",This
,This
->ref
);
600 if (!--(This
->ref
)) {
603 timeKillEvent(This
->timerID
);
604 timeEndPeriod(DS_TIME_RES
);
607 for( i
=0;i
<This
->nrofbuffers
;i
++)
608 IDirectSoundBuffer8_Release((LPDIRECTSOUNDBUFFER8
)This
->buffers
[i
]);
611 DSOUND_PrimaryDestroy(This
);
613 RtlDeleteResource(&This
->lock
);
614 DeleteCriticalSection(&This
->mixlock
);
616 IDsDriver_Close(This
->driver
);
618 if (This
->drvdesc
.dwFlags
& DSDDESC_DOMMSYSTEMOPEN
) {
619 waveOutClose(This
->hwo
);
622 IDsDriver_Release(This
->driver
);
624 HeapFree(GetProcessHeap(),0,This
);
631 static HRESULT WINAPI
IDirectSoundImpl_SetSpeakerConfig(
632 LPDIRECTSOUND8 iface
,DWORD config
634 ICOM_THIS(IDirectSoundImpl
,iface
);
635 FIXME("(%p,0x%08lx):stub\n",This
,config
);
639 static HRESULT WINAPI
IDirectSoundImpl_QueryInterface(
640 LPDIRECTSOUND8 iface
,REFIID riid
,LPVOID
*ppobj
642 ICOM_THIS(IDirectSoundImpl
,iface
);
644 if ( IsEqualGUID( &IID_IDirectSound3DListener
, riid
) ) {
645 ERR("app requested IDirectSound3DListener on dsound object\n");
650 FIXME("(%p,%s,%p)\n",This
,debugstr_guid(riid
),ppobj
);
651 return E_NOINTERFACE
;
654 static HRESULT WINAPI
IDirectSoundImpl_Compact(
655 LPDIRECTSOUND8 iface
)
657 ICOM_THIS(IDirectSoundImpl
,iface
);
658 TRACE("(%p)\n", This
);
662 static HRESULT WINAPI
IDirectSoundImpl_GetSpeakerConfig(
663 LPDIRECTSOUND8 iface
,
664 LPDWORD lpdwSpeakerConfig
)
666 ICOM_THIS(IDirectSoundImpl
,iface
);
667 TRACE("(%p, %p)\n", This
, lpdwSpeakerConfig
);
668 *lpdwSpeakerConfig
= DSSPEAKER_STEREO
| (DSSPEAKER_GEOMETRY_NARROW
<< 16);
672 static HRESULT WINAPI
IDirectSoundImpl_Initialize(
673 LPDIRECTSOUND8 iface
,
676 ICOM_THIS(IDirectSoundImpl
,iface
);
677 TRACE("(%p, %p)\n", This
, lpcGuid
);
681 static HRESULT WINAPI
IDirectSoundImpl_VerifyCertification(
682 LPDIRECTSOUND8 iface
,
683 LPDWORD pdwCertified
)
685 ICOM_THIS(IDirectSoundImpl
,iface
);
686 TRACE("(%p, %p)\n", This
, pdwCertified
);
687 *pdwCertified
= DS_CERTIFIED
;
691 static ICOM_VTABLE(IDirectSound8
) dsvt
=
693 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
694 IDirectSoundImpl_QueryInterface
,
695 IDirectSoundImpl_AddRef
,
696 IDirectSoundImpl_Release
,
697 IDirectSoundImpl_CreateSoundBuffer
,
698 IDirectSoundImpl_GetCaps
,
699 IDirectSoundImpl_DuplicateSoundBuffer
,
700 IDirectSoundImpl_SetCooperativeLevel
,
701 IDirectSoundImpl_Compact
,
702 IDirectSoundImpl_GetSpeakerConfig
,
703 IDirectSoundImpl_SetSpeakerConfig
,
704 IDirectSoundImpl_Initialize
,
705 IDirectSoundImpl_VerifyCertification
709 /*******************************************************************************
710 * DirectSoundCreate (DSOUND.1)
712 HRESULT WINAPI
DirectSoundCreate8(LPCGUID lpcGUID
,LPDIRECTSOUND8
*ppDS
,IUnknown
*pUnkOuter
)
714 IDirectSoundImpl
** ippDS
=(IDirectSoundImpl
**)ppDS
;
715 PIDSDRIVER drv
= NULL
;
717 HRESULT err
= DSERR_INVALIDPARAM
;
719 TRACE("(%p,%p,%p)\n",lpcGUID
,ippDS
,pUnkOuter
);
722 WARN("invalid parameter\n");
723 return DSERR_INVALIDPARAM
;
726 /* Get dsound configuration */
727 setup_dsound_options();
729 /* Default device? */
730 if ( !lpcGUID
|| IsEqualGUID(lpcGUID
, &GUID_NULL
) )
731 lpcGUID
= &DSDEVID_DefaultPlayback
;
733 if (GetDeviceID(lpcGUID
, &devGuid
) != DS_OK
) {
734 WARN("invalid parameter\n");
735 return DSERR_INVALIDPARAM
;
739 if (IsEqualGUID(&devGuid
, &dsound
->guid
) ) {
740 ERR("dsound already opened\n");
741 IDirectSound_AddRef((LPDIRECTSOUND
)dsound
);
745 ERR("different dsound already opened\n");
749 /* Enumerate WINMM audio devices and find the one we want */
750 wodn
= waveOutGetNumDevs();
751 if (!wodn
) return DSERR_NODRIVER
;
753 for (wod
=0; wod
<wodn
; wod
++) {
755 err
= mmErr(waveOutMessage((HWAVEOUT
)wod
,DRV_QUERYDSOUNDGUID
,(DWORD
)(&guid
),0));
757 WARN("waveOutMessage failed; err=%lx\n",err
);
760 if (IsEqualGUID( &devGuid
, &guid
) ) {
767 WARN("invalid parameter\n");
768 return DSERR_INVALIDPARAM
;
771 /* DRV_QUERYDSOUNDIFACE is a "Wine extension" to get the DSound interface */
772 waveOutMessage((HWAVEOUT
)wod
, DRV_QUERYDSOUNDIFACE
, (DWORD
)&drv
, 0);
774 /* Disable the direct sound driver to force emulation if requested. */
775 if (ds_hw_accel
== DS_HW_ACCEL_EMULATION
)
778 /* Allocate memory */
779 *ippDS
= (IDirectSoundImpl
*)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY
,sizeof(IDirectSoundImpl
));
781 return DSERR_OUTOFMEMORY
;
783 ICOM_VTBL(*ippDS
) = &dsvt
;
786 (*ippDS
)->driver
= drv
;
787 (*ippDS
)->priolevel
= DSSCL_NORMAL
;
788 (*ippDS
)->fraglen
= 0;
789 (*ippDS
)->hwbuf
= NULL
;
790 (*ippDS
)->buffer
= NULL
;
791 (*ippDS
)->buflen
= 0;
792 (*ippDS
)->writelead
= 0;
793 (*ippDS
)->state
= STATE_STOPPED
;
794 (*ippDS
)->nrofbuffers
= 0;
795 (*ippDS
)->buffers
= NULL
;
796 (*ippDS
)->listener
= NULL
;
798 (*ippDS
)->prebuf
= ds_snd_queue_max
;
799 (*ippDS
)->guid
= devGuid
;
801 /* Get driver description */
803 IDsDriver_GetDriverDesc(drv
,&((*ippDS
)->drvdesc
));
805 /* if no DirectSound interface available, use WINMM API instead */
806 (*ippDS
)->drvdesc
.dwFlags
= DSDDESC_DOMMSYSTEMOPEN
| DSDDESC_DOMMSYSTEMSETFORMAT
;
809 (*ippDS
)->drvdesc
.dnDevNode
= wod
;
811 /* Set default wave format (may need it for waveOutOpen) */
812 (*ippDS
)->wfx
.wFormatTag
= WAVE_FORMAT_PCM
;
813 /* We rely on the sound driver to return the actual sound format of
814 * the device if it does not support 22050x8x2 and is given the
815 * WAVE_DIRECTSOUND flag.
817 (*ippDS
)->wfx
.nSamplesPerSec
= 22050;
818 (*ippDS
)->wfx
.wBitsPerSample
= 8;
819 (*ippDS
)->wfx
.nChannels
= 2;
820 (*ippDS
)->wfx
.nBlockAlign
= (*ippDS
)->wfx
.wBitsPerSample
* (*ippDS
)->wfx
.nChannels
/ 8;
821 (*ippDS
)->wfx
.nAvgBytesPerSec
= (*ippDS
)->wfx
.nSamplesPerSec
* (*ippDS
)->wfx
.nBlockAlign
;
822 (*ippDS
)->wfx
.cbSize
= 0;
824 /* If the driver requests being opened through MMSYSTEM
825 * (which is recommended by the DDK), it is supposed to happen
826 * before the DirectSound interface is opened */
827 if ((*ippDS
)->drvdesc
.dwFlags
& DSDDESC_DOMMSYSTEMOPEN
)
829 DWORD flags
= CALLBACK_FUNCTION
;
831 /* disable direct sound if requested */
832 if (ds_hw_accel
!= DS_HW_ACCEL_EMULATION
)
833 flags
|= WAVE_DIRECTSOUND
;
835 err
= mmErr(waveOutOpen(&((*ippDS
)->hwo
),
836 (*ippDS
)->drvdesc
.dnDevNode
, &((*ippDS
)->wfx
),
837 (DWORD
)DSOUND_callback
, (DWORD
)(*ippDS
),
841 if (drv
&& (err
== DS_OK
))
842 err
= IDsDriver_Open(drv
);
844 /* FIXME: do we want to handle a temporarily busy device? */
846 HeapFree(GetProcessHeap(),0,*ippDS
);
851 /* the driver is now open, so it's now allowed to call GetCaps */
853 IDsDriver_GetCaps(drv
,&((*ippDS
)->drvcaps
));
855 /* FIXME: We should check the device capabilities */
856 (*ippDS
)->drvcaps
.dwFlags
=
857 DSCAPS_PRIMARY16BIT
| DSCAPS_PRIMARYSTEREO
;
859 (*ippDS
)->drvcaps
.dwFlags
|= DSCAPS_EMULDRIVER
;
862 DSOUND_RecalcVolPan(&((*ippDS
)->volpan
));
864 InitializeCriticalSection(&((*ippDS
)->mixlock
));
865 RtlInitializeResource(&((*ippDS
)->lock
));
869 DSOUND_PrimaryCreate(dsound
);
870 timeBeginPeriod(DS_TIME_RES
);
871 dsound
->timerID
= timeSetEvent(DS_TIME_DEL
, DS_TIME_RES
, DSOUND_timer
,
872 (DWORD
)dsound
, TIME_PERIODIC
| TIME_CALLBACK_FUNCTION
);
878 /*******************************************************************************
879 * DirectSound ClassFactory
883 /* IUnknown fields */
884 ICOM_VFIELD(IClassFactory
);
888 static HRESULT WINAPI
889 DSCF_QueryInterface(LPCLASSFACTORY iface
,REFIID riid
,LPVOID
*ppobj
) {
890 ICOM_THIS(IClassFactoryImpl
,iface
);
892 FIXME("(%p)->(%s,%p),stub!\n",This
,debugstr_guid(riid
),ppobj
);
893 return E_NOINTERFACE
;
897 DSCF_AddRef(LPCLASSFACTORY iface
) {
898 ICOM_THIS(IClassFactoryImpl
,iface
);
899 return ++(This
->ref
);
902 static ULONG WINAPI
DSCF_Release(LPCLASSFACTORY iface
) {
903 ICOM_THIS(IClassFactoryImpl
,iface
);
904 /* static class, won't be freed */
905 return --(This
->ref
);
908 static HRESULT WINAPI
DSCF_CreateInstance(
909 LPCLASSFACTORY iface
,LPUNKNOWN pOuter
,REFIID riid
,LPVOID
*ppobj
911 ICOM_THIS(IClassFactoryImpl
,iface
);
913 TRACE("(%p)->(%p,%s,%p)\n",This
,pOuter
,debugstr_guid(riid
),ppobj
);
914 if ( IsEqualGUID( &IID_IDirectSound
, riid
) ||
915 IsEqualGUID( &IID_IDirectSound8
, riid
) ) {
916 /* FIXME: reuse already created dsound if present? */
917 return DirectSoundCreate8(riid
,(LPDIRECTSOUND8
*)ppobj
,pOuter
);
919 if ( IsEqualGUID( &IID_IDirectSoundCapture
, riid
) ||
920 IsEqualGUID( &IID_IDirectSoundCapture8
, riid
) ) {
921 return DirectSoundCaptureCreate8(0,(LPDIRECTSOUNDCAPTURE8
*)ppobj
,pOuter
);
923 /* DxDiag tries to create this */
924 if ( IsEqualGUID( &IID_IKsPropertySet
, riid
) ) {
925 FIXME("(%p,%p,%s,%p) IID_IKsPropertySet not implemented\n",
926 This
,pOuter
,debugstr_guid(riid
),ppobj
);
927 /* FIXME: what is DxDiag looking for here? */
929 return IKsPropertySetImpl_Create(0,(IKsPropertySetImpl
**)ppobj
);
931 return E_NOINTERFACE
;
935 FIXME("(%p,%p,%s,%p) Interface not found!\n",This
,pOuter
,debugstr_guid(riid
),ppobj
);
936 return E_NOINTERFACE
;
939 static HRESULT WINAPI
DSCF_LockServer(LPCLASSFACTORY iface
,BOOL dolock
) {
940 ICOM_THIS(IClassFactoryImpl
,iface
);
941 FIXME("(%p)->(%d),stub!\n",This
,dolock
);
945 static ICOM_VTABLE(IClassFactory
) DSCF_Vtbl
= {
946 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
953 static IClassFactoryImpl DSOUND_CF
= {&DSCF_Vtbl
, 1 };
955 /*******************************************************************************
956 * DllGetClassObject [DSOUND.5]
957 * Retrieves class object from a DLL object
960 * Docs say returns STDAPI
963 * rclsid [I] CLSID for the class object
964 * riid [I] Reference to identifier of interface for class object
965 * ppv [O] Address of variable to receive interface pointer for riid
969 * Failure: CLASS_E_CLASSNOTAVAILABLE, E_OUTOFMEMORY, E_INVALIDARG,
972 DWORD WINAPI
DSOUND_DllGetClassObject(REFCLSID rclsid
,REFIID riid
,LPVOID
*ppv
)
974 TRACE("(%p,%p,%p)\n", debugstr_guid(rclsid
), debugstr_guid(riid
), ppv
);
975 if ( IsEqualCLSID( &IID_IClassFactory
, riid
) ) {
976 *ppv
= (LPVOID
)&DSOUND_CF
;
977 IClassFactory_AddRef((IClassFactory
*)*ppv
);
981 FIXME("(%p,%p,%p): no interface found.\n", debugstr_guid(rclsid
), debugstr_guid(riid
), ppv
);
982 return CLASS_E_CLASSNOTAVAILABLE
;
986 /*******************************************************************************
987 * DllCanUnloadNow [DSOUND.4] Determines whether the DLL is in use.
993 DWORD WINAPI
DSOUND_DllCanUnloadNow(void)
995 FIXME("(void): stub\n");