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 TRACE("(%p,%p)\n",pGuidSrc,pGuidDest);
257 if ( pGuidSrc == NULL) {
258 WARN("invalid parameter: pGuidSrc == NULL\n");
259 return DSERR_INVALIDPARAM;
262 if ( pGuidDest == NULL ) {
263 WARN("invalid parameter: pGuidDest == NULL\n");
264 return DSERR_INVALIDPARAM;
267 if ( IsEqualGUID( &DSDEVID_DefaultPlayback, pGuidSrc ) ||
268 IsEqualGUID( &DSDEVID_DefaultVoicePlayback, pGuidSrc ) ) {
270 int err = mmErr(waveOutMessage((HWAVEOUT)ds_default_playback,DRV_QUERYDSOUNDGUID,(DWORD)&guid,0));
272 memcpy(pGuidDest, &guid, sizeof(GUID));
277 if ( IsEqualGUID( &DSDEVID_DefaultCapture, pGuidSrc ) ||
278 IsEqualGUID( &DSDEVID_DefaultVoiceCapture, pGuidSrc ) ) {
280 int err = mmErr(waveInMessage((HWAVEIN)ds_default_capture,DRV_QUERYDSOUNDGUID,(DWORD)&guid,0));
282 memcpy(pGuidDest, &guid, sizeof(GUID));
287 memcpy(pGuidDest, pGuidSrc, sizeof(GUID));
293 /***************************************************************************
294 * DirectSoundEnumerateA [DSOUND.2]
296 * Enumerate all DirectSound drivers installed in the system
299 * lpDSEnumCallback [I] Address of callback function.
300 * lpContext [I] Address of user defined context passed to callback function.
304 * Failure: DSERR_INVALIDPARAM
306 HRESULT WINAPI DirectSoundEnumerateA(
307 LPDSENUMCALLBACKA lpDSEnumCallback,
315 TRACE("lpDSEnumCallback = %p, lpContext = %p\n",
316 lpDSEnumCallback, lpContext);
318 if (lpDSEnumCallback == NULL) {
319 WARN("invalid parameter: lpDSEnumCallback == NULL\n");
320 return DSERR_INVALIDPARAM;
323 devs = waveOutGetNumDevs();
325 if (GetDeviceID(&DSDEVID_DefaultPlayback, &guid) == DS_OK) {
327 for (wod = 0; wod < devs; ++wod) {
328 err = mmErr(waveOutMessage((HWAVEOUT)wod,DRV_QUERYDSOUNDGUID,(DWORD)&temp,0));
330 if (IsEqualGUID( &guid, &temp ) ) {
331 err = mmErr(waveOutMessage((HWAVEOUT)wod,DRV_QUERYDSOUNDDESC,(DWORD)&desc,0));
333 TRACE("calling lpDSEnumCallback(%s,\"%s\",\"%s\",%p)\n",
334 debugstr_guid(&DSDEVID_DefaultPlayback),"Primary Sound Driver",desc.szDrvName,lpContext);
335 if (lpDSEnumCallback((LPGUID)&DSDEVID_DefaultPlayback, "Primary Sound Driver", desc.szDrvName, lpContext) == FALSE)
344 for (wod = 0; wod < devs; ++wod) {
345 err = mmErr(waveOutMessage((HWAVEOUT)wod,DRV_QUERYDSOUNDDESC,(DWORD)&desc,0));
347 err = mmErr(waveOutMessage((HWAVEOUT)wod,DRV_QUERYDSOUNDGUID,(DWORD)&guid,0));
349 TRACE("calling lpDSEnumCallback(%s,\"%s\",\"%s\",%p)\n",
350 debugstr_guid(&guid),desc.szDesc,desc.szDrvName,lpContext);
351 if (lpDSEnumCallback(&guid, desc.szDesc, desc.szDrvName, lpContext) == FALSE)
359 /***************************************************************************
360 * DirectSoundEnumerateW [DSOUND.3]
362 * Enumerate all DirectSound drivers installed in the system
365 * lpDSEnumCallback [I] Address of callback function.
366 * lpContext [I] Address of user defined context passed to callback function.
370 * Failure: DSERR_INVALIDPARAM
372 HRESULT WINAPI DirectSoundEnumerateW(
373 LPDSENUMCALLBACKW lpDSEnumCallback,
380 WCHAR wDesc[MAXPNAMELEN];
381 WCHAR wName[MAXPNAMELEN];
383 TRACE("lpDSEnumCallback = %p, lpContext = %p\n",
384 lpDSEnumCallback, lpContext);
386 if (lpDSEnumCallback == NULL) {
387 WARN("invalid parameter: lpDSEnumCallback == NULL\n");
388 return DSERR_INVALIDPARAM;
391 devs = waveOutGetNumDevs();
393 if (GetDeviceID(&DSDEVID_DefaultPlayback, &guid) == DS_OK) {
395 for (wod = 0; wod < devs; ++wod) {
396 err = mmErr(waveOutMessage((HWAVEOUT)wod,DRV_QUERYDSOUNDGUID,(DWORD)&temp,0));
398 if (IsEqualGUID( &guid, &temp ) ) {
399 err = mmErr(waveOutMessage((HWAVEOUT)wod,DRV_QUERYDSOUNDDESC,(DWORD)&desc,0));
401 TRACE("calling lpDSEnumCallback(%s,\"%s\",\"%s\",%p)\n",
402 debugstr_guid(&DSDEVID_DefaultPlayback),"Primary Sound Driver",desc.szDrvName,lpContext);
403 MultiByteToWideChar( CP_ACP, 0, "Primary Sound Driver", -1,
404 wDesc, sizeof(wDesc)/sizeof(WCHAR) );
405 MultiByteToWideChar( CP_ACP, 0, desc.szDrvName, -1,
406 wName, sizeof(wName)/sizeof(WCHAR) );
407 if (lpDSEnumCallback((LPGUID)&DSDEVID_DefaultPlayback, wDesc, wName, lpContext) == FALSE)
416 for (wod = 0; wod < devs; ++wod) {
417 err = mmErr(waveOutMessage((HWAVEOUT)wod,DRV_QUERYDSOUNDDESC,(DWORD)&desc,0));
419 err = mmErr(waveOutMessage((HWAVEOUT)wod,DRV_QUERYDSOUNDGUID,(DWORD)&guid,0));
421 TRACE("calling lpDSEnumCallback(%s,\"%s\",\"%s\",%p)\n",
422 debugstr_guid(&guid),desc.szDesc,desc.szDrvName,lpContext);
423 MultiByteToWideChar( CP_ACP, 0, desc.szDesc, -1,
424 wDesc, sizeof(wDesc)/sizeof(WCHAR) );
425 MultiByteToWideChar( CP_ACP, 0, desc.szDrvName, -1,
426 wName, sizeof(wName)/sizeof(WCHAR) );
427 if (lpDSEnumCallback(&guid, wDesc, wName, lpContext) == FALSE)
436 static void _dump_DSBCAPS(DWORD xmask) {
441 #define FE(x) { x, #x },
442 FE(DSBCAPS_PRIMARYBUFFER)
444 FE(DSBCAPS_LOCHARDWARE)
445 FE(DSBCAPS_LOCSOFTWARE)
447 FE(DSBCAPS_CTRLFREQUENCY)
449 FE(DSBCAPS_CTRLVOLUME)
450 FE(DSBCAPS_CTRLPOSITIONNOTIFY)
451 FE(DSBCAPS_CTRLDEFAULT)
453 FE(DSBCAPS_STICKYFOCUS)
454 FE(DSBCAPS_GLOBALFOCUS)
455 FE(DSBCAPS_GETCURRENTPOSITION2)
456 FE(DSBCAPS_MUTE3DATMAXDISTANCE)
461 for (i=0;i<sizeof(flags)/sizeof(flags[0]);i++)
462 if ((flags[i].mask & xmask) == flags[i].mask)
463 DPRINTF("%s ",flags[i].name);
466 /*******************************************************************************
470 static HRESULT WINAPI IDirectSoundImpl_SetCooperativeLevel(
471 LPDIRECTSOUND8 iface,HWND hwnd,DWORD level
473 ICOM_THIS(IDirectSoundImpl,iface);
475 FIXME("(%p,%08lx,%ld):stub\n",This,(DWORD)hwnd,level);
477 This->priolevel = level;
482 static HRESULT WINAPI IDirectSoundImpl_CreateSoundBuffer(
483 LPDIRECTSOUND8 iface,LPDSBUFFERDESC dsbd,LPLPDIRECTSOUNDBUFFER8 ppdsb,LPUNKNOWN lpunk
485 ICOM_THIS(IDirectSoundImpl,iface);
489 TRACE("(%p,%p,%p,%p)\n",This,dsbd,ppdsb,lpunk);
492 WARN("invalid parameter: This == NULL\n");
493 return DSERR_INVALIDPARAM;
497 WARN("invalid parameter: dsbd == NULL\n");
498 return DSERR_INVALIDPARAM;
502 WARN("invalid parameter: ppdsb == NULL\n");
503 return DSERR_INVALIDPARAM;
506 if (TRACE_ON(dsound)) {
507 TRACE("(structsize=%ld)\n",dsbd->dwSize);
508 TRACE("(flags=0x%08lx:\n",dsbd->dwFlags);
509 _dump_DSBCAPS(dsbd->dwFlags);
511 TRACE("(bufferbytes=%ld)\n",dsbd->dwBufferBytes);
512 TRACE("(lpwfxFormat=%p)\n",dsbd->lpwfxFormat);
515 wfex = dsbd->lpwfxFormat;
518 TRACE("(formattag=0x%04x,chans=%d,samplerate=%ld,"
519 "bytespersec=%ld,blockalign=%d,bitspersamp=%d,cbSize=%d)\n",
520 wfex->wFormatTag, wfex->nChannels, wfex->nSamplesPerSec,
521 wfex->nAvgBytesPerSec, wfex->nBlockAlign,
522 wfex->wBitsPerSample, wfex->cbSize);
524 if (dsbd->dwFlags & DSBCAPS_PRIMARYBUFFER) {
525 hres = PrimaryBuffer_Create(This, (PrimaryBufferImpl**)ppdsb, dsbd);
527 WARN("PrimaryBuffer_Create failed\n");
529 hres = SecondaryBuffer_Create(This, (IDirectSoundBufferImpl**)ppdsb, dsbd);
531 WARN("SecondaryBuffer_Create failed\n");
537 static HRESULT WINAPI IDirectSoundImpl_DuplicateSoundBuffer(
538 LPDIRECTSOUND8 iface,LPDIRECTSOUNDBUFFER8 pdsb,LPLPDIRECTSOUNDBUFFER8 ppdsb
540 ICOM_THIS(IDirectSoundImpl,iface);
541 IDirectSoundBufferImpl* ipdsb=(IDirectSoundBufferImpl*)pdsb;
542 IDirectSoundBufferImpl* dsb;
543 TRACE("(%p,%p,%p)\n",This,pdsb,ppdsb);
546 WARN("invalid parameter: This == NULL\n");
547 return DSERR_INVALIDPARAM;
551 WARN("invalid parameter: pdsb == NULL\n");
552 return DSERR_INVALIDPARAM;
556 WARN("invalid parameter: ppdsb == NULL\n");
557 return DSERR_INVALIDPARAM;
560 if (ipdsb->dsbd.dwFlags & DSBCAPS_PRIMARYBUFFER) {
561 ERR("trying to duplicate primary buffer\n");
563 return DSERR_INVALIDCALL;
567 FIXME("need to duplicate hardware buffer\n");
569 return DSERR_INVALIDCALL;
572 dsb = (IDirectSoundBufferImpl*)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(*dsb));
575 WARN("out of memory\n");
577 return DSERR_OUTOFMEMORY;
580 IDirectSoundBuffer8_AddRef(pdsb);
581 memcpy(dsb, ipdsb, sizeof(IDirectSoundBufferImpl));
583 dsb->state = STATE_STOPPED;
589 if (ipdsb->ds3db != NULL) {
591 hres = IDirectSound3DBufferImpl_Create(dsb, &(dsb->ds3db));
593 WARN("IDirectSound3DBufferImpl_Create failed\n");
595 IDirectSound3DBuffer_AddRef((LPDIRECTSOUND3DBUFFER)(dsb->ds3db));
596 dsb->dsbd.dwFlags |= DSBCAPS_CTRL3D;
597 memcpy(&(dsb->ds3db->ds3db), &(ipdsb->ds3db->ds3db), sizeof(DS3DBUFFER));
602 dsb->iks = NULL; /* FIXME? */
603 memcpy(&(dsb->wfx), &(ipdsb->wfx), sizeof(dsb->wfx));
604 InitializeCriticalSection(&(dsb->lock));
605 /* register buffer */
606 RtlAcquireResourceExclusive(&(This->lock), TRUE);
608 IDirectSoundBufferImpl **newbuffers = (IDirectSoundBufferImpl**)HeapReAlloc(GetProcessHeap(),0,This->buffers,sizeof(IDirectSoundBufferImpl**)*(This->nrofbuffers+1));
610 This->buffers = newbuffers;
611 This->buffers[This->nrofbuffers] = dsb;
613 TRACE("buffer count is now %d\n", This->nrofbuffers);
615 ERR("out of memory for buffer list! Current buffer count is %d\n", This->nrofbuffers);
616 IDirectSoundBuffer8_Release(pdsb);
617 DeleteCriticalSection(&(dsb->lock));
618 RtlReleaseResource(&(This->lock));
619 HeapFree(GetProcessHeap(),0,dsb);
621 return DSERR_OUTOFMEMORY;
624 RtlReleaseResource(&(This->lock));
625 IDirectSound_AddRef(iface);
626 *ppdsb = (LPDIRECTSOUNDBUFFER8)dsb;
631 static HRESULT WINAPI IDirectSoundImpl_GetCaps(LPDIRECTSOUND8 iface,LPDSCAPS lpDSCaps) {
632 ICOM_THIS(IDirectSoundImpl,iface);
633 TRACE("(%p,%p)\n",This,lpDSCaps);
636 WARN("invalid parameter: This == NULL\n");
637 return DSERR_INVALIDPARAM;
640 if (lpDSCaps == NULL) {
641 WARN("invalid parameter: lpDSCaps = NULL\n");
642 return DSERR_INVALIDPARAM;
645 /* check is there is enough room */
646 if (lpDSCaps->dwSize < sizeof(*lpDSCaps)) {
647 WARN("invalid parameter: lpDSCaps->dwSize = %ld < %d\n",
648 lpDSCaps->dwSize, sizeof(*lpDSCaps));
649 return DSERR_INVALIDPARAM;
652 lpDSCaps->dwFlags = This->drvcaps.dwFlags;
653 TRACE("(flags=0x%08lx)\n",lpDSCaps->dwFlags);
655 /* FIXME: copy caps from This->drv */
656 lpDSCaps->dwMinSecondarySampleRate = DSBFREQUENCY_MIN;
657 lpDSCaps->dwMaxSecondarySampleRate = DSBFREQUENCY_MAX;
659 lpDSCaps->dwPrimaryBuffers = 1;
661 lpDSCaps->dwMaxHwMixingAllBuffers = 0;
662 lpDSCaps->dwMaxHwMixingStaticBuffers = 0;
663 lpDSCaps->dwMaxHwMixingStreamingBuffers = 0;
665 lpDSCaps->dwFreeHwMixingAllBuffers = 0;
666 lpDSCaps->dwFreeHwMixingStaticBuffers = 0;
667 lpDSCaps->dwFreeHwMixingStreamingBuffers = 0;
669 lpDSCaps->dwMaxHw3DAllBuffers = 0;
670 lpDSCaps->dwMaxHw3DStaticBuffers = 0;
671 lpDSCaps->dwMaxHw3DStreamingBuffers = 0;
673 lpDSCaps->dwFreeHw3DAllBuffers = 0;
674 lpDSCaps->dwFreeHw3DStaticBuffers = 0;
675 lpDSCaps->dwFreeHw3DStreamingBuffers = 0;
677 lpDSCaps->dwTotalHwMemBytes = 0;
679 lpDSCaps->dwFreeHwMemBytes = 0;
681 lpDSCaps->dwMaxContigFreeHwMemBytes = 0;
683 lpDSCaps->dwUnlockTransferRateHwBuffers = 4096; /* But we have none... */
685 lpDSCaps->dwPlayCpuOverheadSwBuffers = 1; /* 1% */
690 static ULONG WINAPI IDirectSoundImpl_AddRef(LPDIRECTSOUND8 iface) {
691 ICOM_THIS(IDirectSoundImpl,iface);
692 TRACE("(%p) ref was %ld\n", This, This->ref);
693 return ++(This->ref);
696 static ULONG WINAPI IDirectSoundImpl_Release(LPDIRECTSOUND8 iface) {
698 ICOM_THIS(IDirectSoundImpl,iface);
699 TRACE("(%p), ref was %ld\n",This,This->ref);
700 if (!--(This->ref)) {
703 timeKillEvent(This->timerID);
704 timeEndPeriod(DS_TIME_RES);
707 for( i=0;i<This->nrofbuffers;i++)
708 IDirectSoundBuffer8_Release((LPDIRECTSOUNDBUFFER8)This->buffers[i]);
711 hres = DSOUND_PrimaryDestroy(This);
713 WARN("DSOUND_PrimaryDestroy failed\n");
715 RtlDeleteResource(&This->lock);
716 DeleteCriticalSection(&This->mixlock);
718 IDsDriver_Close(This->driver);
720 if (This->drvdesc.dwFlags & DSDDESC_DOMMSYSTEMOPEN) {
721 waveOutClose(This->hwo);
724 IDsDriver_Release(This->driver);
726 HeapFree(GetProcessHeap(),0,This);
733 static HRESULT WINAPI IDirectSoundImpl_SetSpeakerConfig(
734 LPDIRECTSOUND8 iface,DWORD config
736 ICOM_THIS(IDirectSoundImpl,iface);
737 FIXME("(%p,0x%08lx):stub\n",This,config);
741 static HRESULT WINAPI IDirectSoundImpl_QueryInterface(
742 LPDIRECTSOUND8 iface,REFIID riid,LPVOID *ppobj
744 ICOM_THIS(IDirectSoundImpl,iface);
746 if ( IsEqualGUID( &IID_IDirectSound3DListener, riid ) ) {
747 ERR("app requested IDirectSound3DListener on dsound object\n");
752 FIXME("(%p,%s,%p)\n",This,debugstr_guid(riid),ppobj);
753 return E_NOINTERFACE;
756 static HRESULT WINAPI IDirectSoundImpl_Compact(
757 LPDIRECTSOUND8 iface)
759 ICOM_THIS(IDirectSoundImpl,iface);
760 TRACE("(%p)\n", This);
764 static HRESULT WINAPI IDirectSoundImpl_GetSpeakerConfig(
765 LPDIRECTSOUND8 iface,
766 LPDWORD lpdwSpeakerConfig)
768 ICOM_THIS(IDirectSoundImpl,iface);
769 TRACE("(%p, %p)\n", This, lpdwSpeakerConfig);
770 *lpdwSpeakerConfig = DSSPEAKER_STEREO | (DSSPEAKER_GEOMETRY_NARROW << 16);
774 static HRESULT WINAPI IDirectSoundImpl_Initialize(
775 LPDIRECTSOUND8 iface,
778 ICOM_THIS(IDirectSoundImpl,iface);
779 TRACE("(%p, %s)\n", This, debugstr_guid(lpcGuid));
783 static HRESULT WINAPI IDirectSoundImpl_VerifyCertification(
784 LPDIRECTSOUND8 iface,
785 LPDWORD pdwCertified)
787 ICOM_THIS(IDirectSoundImpl,iface);
788 TRACE("(%p, %p)\n", This, pdwCertified);
789 *pdwCertified = DS_CERTIFIED;
793 static ICOM_VTABLE(IDirectSound8) dsvt =
795 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
796 IDirectSoundImpl_QueryInterface,
797 IDirectSoundImpl_AddRef,
798 IDirectSoundImpl_Release,
799 IDirectSoundImpl_CreateSoundBuffer,
800 IDirectSoundImpl_GetCaps,
801 IDirectSoundImpl_DuplicateSoundBuffer,
802 IDirectSoundImpl_SetCooperativeLevel,
803 IDirectSoundImpl_Compact,
804 IDirectSoundImpl_GetSpeakerConfig,
805 IDirectSoundImpl_SetSpeakerConfig,
806 IDirectSoundImpl_Initialize,
807 IDirectSoundImpl_VerifyCertification
811 /*******************************************************************************
812 * DirectSoundCreate (DSOUND.1)
814 * Creates and initializes a DirectSound interface.
817 * lpcGUID [I] Address of the GUID that identifies the sound device.
818 * ppDS [O] Address of a variable to receive the interface pointer.
819 * pUnkOuter [I] Must be NULL.
823 * Failure: DSERR_ALLOCATED, DSERR_INVALIDPARAM, DSERR_NOAGGREGATION,
824 * DSERR_NODRIVER, DSERR_OUTOFMEMORY
826 HRESULT WINAPI DirectSoundCreate8(LPCGUID lpcGUID,LPDIRECTSOUND8 *ppDS,IUnknown *pUnkOuter )
828 IDirectSoundImpl** ippDS=(IDirectSoundImpl**)ppDS;
829 PIDSDRIVER drv = NULL;
831 HRESULT err = DSERR_INVALIDPARAM;
833 BOOLEAN found = FALSE;
835 TRACE("(%s,%p,%p)\n",debugstr_guid(lpcGUID),ippDS,pUnkOuter);
838 WARN("invalid parameter: ippDS == NULL\n");
839 return DSERR_INVALIDPARAM;
842 /* Get dsound configuration */
843 setup_dsound_options();
845 /* Default device? */
846 if (!lpcGUID || IsEqualGUID(lpcGUID, &GUID_NULL))
847 lpcGUID = &DSDEVID_DefaultPlayback;
849 if (GetDeviceID(lpcGUID, &devGuid) != DS_OK) {
850 WARN("invalid parameter: lpcGUID\n");
852 return DSERR_INVALIDPARAM;
856 if (IsEqualGUID(&devGuid, &dsound->guid) ) {
857 ERR("dsound already opened\n");
858 IDirectSound_AddRef((LPDIRECTSOUND)dsound);
862 ERR("different dsound already opened\n");
866 /* Enumerate WINMM audio devices and find the one we want */
867 wodn = waveOutGetNumDevs();
871 return DSERR_NODRIVER;
874 TRACE(" expecting GUID %s.\n", debugstr_guid(&devGuid));
876 for (wod=0; wod<wodn; wod++) {
878 err = mmErr(waveOutMessage((HWAVEOUT)wod,DRV_QUERYDSOUNDGUID,(DWORD)(&guid),0));
880 WARN("waveOutMessage failed; err=%lx\n",err);
884 TRACE("got GUID %s for wod %d.\n", debugstr_guid(&guid), wod);
885 if (IsEqualGUID( &devGuid, &guid) ) {
893 WARN("invalid parameter\n");
895 return DSERR_INVALIDPARAM;
898 if (found == FALSE) {
899 WARN("No device found matching given ID - trying with default one !\n");
900 wod = ds_default_playback;
903 /* DRV_QUERYDSOUNDIFACE is a "Wine extension" to get the DSound interface */
904 waveOutMessage((HWAVEOUT)wod, DRV_QUERYDSOUNDIFACE, (DWORD)&drv, 0);
906 /* Disable the direct sound driver to force emulation if requested. */
907 if (ds_hw_accel == DS_HW_ACCEL_EMULATION)
910 /* Allocate memory */
911 *ippDS = (IDirectSoundImpl*)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(IDirectSoundImpl));
912 if (*ippDS == NULL) {
913 WARN("out of memory\n");
914 return DSERR_OUTOFMEMORY;
917 (*ippDS)->lpVtbl = &dsvt;
920 (*ippDS)->driver = drv;
921 (*ippDS)->priolevel = DSSCL_NORMAL;
922 (*ippDS)->fraglen = 0;
923 (*ippDS)->hwbuf = NULL;
924 (*ippDS)->buffer = NULL;
925 (*ippDS)->buflen = 0;
926 (*ippDS)->writelead = 0;
927 (*ippDS)->state = STATE_STOPPED;
928 (*ippDS)->nrofbuffers = 0;
929 (*ippDS)->buffers = NULL;
930 (*ippDS)->listener = NULL;
932 (*ippDS)->prebuf = ds_snd_queue_max;
933 (*ippDS)->guid = devGuid;
935 /* Get driver description */
937 err = IDsDriver_GetDriverDesc(drv,&((*ippDS)->drvdesc));
939 WARN("IDsDriver_GetDriverDesc failed\n");
940 HeapFree(GetProcessHeap(),0,*ippDS);
945 /* if no DirectSound interface available, use WINMM API instead */
946 (*ippDS)->drvdesc.dwFlags = DSDDESC_DOMMSYSTEMOPEN | DSDDESC_DOMMSYSTEMSETFORMAT;
949 (*ippDS)->drvdesc.dnDevNode = wod;
951 /* Set default wave format (may need it for waveOutOpen) */
952 (*ippDS)->wfx.wFormatTag = WAVE_FORMAT_PCM;
953 /* We rely on the sound driver to return the actual sound format of
954 * the device if it does not support 22050x8x2 and is given the
955 * WAVE_DIRECTSOUND flag.
957 (*ippDS)->wfx.nSamplesPerSec = 22050;
958 (*ippDS)->wfx.wBitsPerSample = 8;
959 (*ippDS)->wfx.nChannels = 2;
960 (*ippDS)->wfx.nBlockAlign = (*ippDS)->wfx.wBitsPerSample * (*ippDS)->wfx.nChannels / 8;
961 (*ippDS)->wfx.nAvgBytesPerSec = (*ippDS)->wfx.nSamplesPerSec * (*ippDS)->wfx.nBlockAlign;
962 (*ippDS)->wfx.cbSize = 0;
964 /* If the driver requests being opened through MMSYSTEM
965 * (which is recommended by the DDK), it is supposed to happen
966 * before the DirectSound interface is opened */
967 if ((*ippDS)->drvdesc.dwFlags & DSDDESC_DOMMSYSTEMOPEN)
969 DWORD flags = CALLBACK_FUNCTION;
971 /* disable direct sound if requested */
972 if (ds_hw_accel != DS_HW_ACCEL_EMULATION)
973 flags |= WAVE_DIRECTSOUND;
975 err = mmErr(waveOutOpen(&((*ippDS)->hwo),
976 (*ippDS)->drvdesc.dnDevNode, &((*ippDS)->wfx),
977 (DWORD)DSOUND_callback, (DWORD)(*ippDS),
980 WARN("waveOutOpen failed\n");
981 HeapFree(GetProcessHeap(),0,*ippDS);
988 err = IDsDriver_Open(drv);
990 WARN("IDsDriver_Open failed\n");
991 HeapFree(GetProcessHeap(),0,*ippDS);
996 /* the driver is now open, so it's now allowed to call GetCaps */
997 err = IDsDriver_GetCaps(drv,&((*ippDS)->drvcaps));
999 WARN("IDsDriver_GetCaps failed\n");
1000 HeapFree(GetProcessHeap(),0,*ippDS);
1006 err = mmErr(waveOutGetDevCapsA((*ippDS)->drvdesc.dnDevNode, &woc, sizeof(woc)));
1008 WARN("waveOutGetDevCaps failed\n");
1009 HeapFree(GetProcessHeap(),0,*ippDS);
1013 (*ippDS)->drvcaps.dwFlags = 0;
1014 if ((woc.dwFormats & WAVE_FORMAT_1M08) ||
1015 (woc.dwFormats & WAVE_FORMAT_2M08) ||
1016 (woc.dwFormats & WAVE_FORMAT_4M08) ||
1017 (woc.dwFormats & WAVE_FORMAT_48M08) ||
1018 (woc.dwFormats & WAVE_FORMAT_96M08)) {
1019 (*ippDS)->drvcaps.dwFlags |= DSCAPS_PRIMARY8BIT;
1020 (*ippDS)->drvcaps.dwFlags |= DSCAPS_PRIMARYMONO;
1022 if ((woc.dwFormats & WAVE_FORMAT_1M16) ||
1023 (woc.dwFormats & WAVE_FORMAT_2M16) ||
1024 (woc.dwFormats & WAVE_FORMAT_4M16) ||
1025 (woc.dwFormats & WAVE_FORMAT_48M16) ||
1026 (woc.dwFormats & WAVE_FORMAT_96M16)) {
1027 (*ippDS)->drvcaps.dwFlags |= DSCAPS_PRIMARY16BIT;
1028 (*ippDS)->drvcaps.dwFlags |= DSCAPS_PRIMARYMONO;
1030 if ((woc.dwFormats & WAVE_FORMAT_1S08) ||
1031 (woc.dwFormats & WAVE_FORMAT_2S08) ||
1032 (woc.dwFormats & WAVE_FORMAT_4S08) ||
1033 (woc.dwFormats & WAVE_FORMAT_48S08) ||
1034 (woc.dwFormats & WAVE_FORMAT_96S08)) {
1035 (*ippDS)->drvcaps.dwFlags |= DSCAPS_PRIMARY8BIT;
1036 (*ippDS)->drvcaps.dwFlags |= DSCAPS_PRIMARYSTEREO;
1038 if ((woc.dwFormats & WAVE_FORMAT_1S16) ||
1039 (woc.dwFormats & WAVE_FORMAT_2S16) ||
1040 (woc.dwFormats & WAVE_FORMAT_4S16) ||
1041 (woc.dwFormats & WAVE_FORMAT_48S16) ||
1042 (woc.dwFormats & WAVE_FORMAT_96S16)) {
1043 (*ippDS)->drvcaps.dwFlags |= DSCAPS_PRIMARY16BIT;
1044 (*ippDS)->drvcaps.dwFlags |= DSCAPS_PRIMARYSTEREO;
1047 (*ippDS)->drvcaps.dwFlags |= DSCAPS_EMULDRIVER;
1050 DSOUND_RecalcVolPan(&((*ippDS)->volpan));
1052 InitializeCriticalSection(&((*ippDS)->mixlock));
1053 RtlInitializeResource(&((*ippDS)->lock));
1058 hres = DSOUND_PrimaryCreate(dsound);
1059 if (hres != DS_OK) {
1060 WARN("DSOUND_PrimaryCreate failed\n");
1063 timeBeginPeriod(DS_TIME_RES);
1064 dsound->timerID = timeSetEvent(DS_TIME_DEL, DS_TIME_RES, DSOUND_timer,
1065 (DWORD)dsound, TIME_PERIODIC | TIME_CALLBACK_FUNCTION);
1071 /*******************************************************************************
1072 * DirectSound ClassFactory
1076 /* IUnknown fields */
1077 ICOM_VFIELD(IClassFactory);
1079 } IClassFactoryImpl;
1081 static HRESULT WINAPI
1082 DSCF_QueryInterface(LPCLASSFACTORY iface,REFIID riid,LPVOID *ppobj) {
1083 ICOM_THIS(IClassFactoryImpl,iface);
1085 FIXME("(%p)->(%s,%p),stub!\n",This,debugstr_guid(riid),ppobj);
1086 return E_NOINTERFACE;
1090 DSCF_AddRef(LPCLASSFACTORY iface) {
1091 ICOM_THIS(IClassFactoryImpl,iface);
1092 TRACE("(%p) ref was %ld\n", This, This->ref);
1093 return ++(This->ref);
1096 static ULONG WINAPI DSCF_Release(LPCLASSFACTORY iface) {
1097 ICOM_THIS(IClassFactoryImpl,iface);
1098 /* static class, won't be freed */
1099 TRACE("(%p) ref was %ld\n", This, This->ref);
1100 return --(This->ref);
1103 static HRESULT WINAPI DSCF_CreateInstance(
1104 LPCLASSFACTORY iface,LPUNKNOWN pOuter,REFIID riid,LPVOID *ppobj
1106 ICOM_THIS(IClassFactoryImpl,iface);
1108 TRACE("(%p)->(%p,%s,%p)\n",This,pOuter,debugstr_guid(riid),ppobj);
1109 if ( IsEqualGUID( &IID_IDirectSound, riid ) ||
1110 IsEqualGUID( &IID_IDirectSound8, riid ) ) {
1111 /* FIXME: reuse already created dsound if present? */
1112 return DirectSoundCreate8(0,(LPDIRECTSOUND8*)ppobj,pOuter);
1114 if ( IsEqualGUID( &IID_IDirectSoundCapture, riid ) ||
1115 IsEqualGUID( &IID_IDirectSoundCapture8, riid ) ) {
1116 return DirectSoundCaptureCreate8(0,(LPDIRECTSOUNDCAPTURE8*)ppobj,pOuter);
1118 if ( IsEqualGUID( &IID_IKsPropertySet, riid ) ) {
1119 return IKsPropertySetImpl_Create(0,(IKsPropertySetImpl**)ppobj);
1122 FIXME("(%p,%p,%s,%p) Interface not found!\n",This,pOuter,debugstr_guid(riid),ppobj);
1123 return E_NOINTERFACE;
1126 static HRESULT WINAPI DSCF_LockServer(LPCLASSFACTORY iface,BOOL dolock) {
1127 ICOM_THIS(IClassFactoryImpl,iface);
1128 FIXME("(%p)->(%d),stub!\n",This,dolock);
1132 static ICOM_VTABLE(IClassFactory) DSCF_Vtbl = {
1133 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
1134 DSCF_QueryInterface,
1137 DSCF_CreateInstance,
1140 static IClassFactoryImpl DSOUND_CF = {&DSCF_Vtbl, 1 };
1142 /*******************************************************************************
1143 * DllGetClassObject [DSOUND.5]
1144 * Retrieves class object from a DLL object
1147 * Docs say returns STDAPI
1150 * rclsid [I] CLSID for the class object
1151 * riid [I] Reference to identifier of interface for class object
1152 * ppv [O] Address of variable to receive interface pointer for riid
1156 * Failure: CLASS_E_CLASSNOTAVAILABLE, E_OUTOFMEMORY, E_INVALIDARG,
1159 DWORD WINAPI DSOUND_DllGetClassObject(REFCLSID rclsid,REFIID riid,LPVOID *ppv)
1161 TRACE("(%s,%s,%p)\n", debugstr_guid(rclsid), debugstr_guid(riid), ppv);
1162 if ( IsEqualCLSID( &IID_IClassFactory, riid ) ) {
1163 *ppv = (LPVOID)&DSOUND_CF;
1164 IClassFactory_AddRef((IClassFactory*)*ppv);
1168 FIXME("(%s,%s,%p): no interface found.\n", debugstr_guid(rclsid), debugstr_guid(riid), ppv);
1169 return CLASS_E_CLASSNOTAVAILABLE;
1173 /*******************************************************************************
1174 * DllCanUnloadNow [DSOUND.4]
1175 * Determines whether the DLL is in use.
1181 DWORD WINAPI DSOUND_DllCanUnloadNow(void)
1183 FIXME("(void): stub\n");