Give the right ids to the DirectSoundCreate8 function (thanks to
[wine/multimedia.git] / dlls / dsound / dsound_main.c
blob09a9db392c1b7cccfc0819a41a1d84cae90f4b7a
1 /* DirectSound
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+.
28 * TODO:
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
39 #include "config.h"
40 #include "wine/port.h"
42 #include <assert.h>
43 #include <stdio.h>
44 #include <sys/types.h>
45 #include <sys/fcntl.h>
46 #ifdef HAVE_UNISTD_H
47 # include <unistd.h>
48 #endif
49 #include <stdlib.h>
50 #include <string.h>
51 #include <math.h> /* Insomnia - pow() function */
53 #include "windef.h"
54 #include "winbase.h"
55 #include "winreg.h"
56 #include "winuser.h"
57 #include "wingdi.h"
58 #include "winuser.h"
59 #include "winerror.h"
60 #include "mmsystem.h"
61 #include "winternl.h"
62 #include "mmddk.h"
63 #include "wine/windef16.h"
64 #include "wine/winbase16.h"
65 #include "wine/debug.h"
66 #include "dsound.h"
67 #include "dsdriver.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)
88 switch(err) {
89 case MMSYSERR_NOERROR:
90 return DS_OK;
91 case MMSYSERR_ALLOCATED:
92 return DSERR_ALLOCATED;
93 case MMSYSERR_ERROR:
94 case MMSYSERR_INVALHANDLE:
95 case WAVERR_STILLPLAYING:
96 return DSERR_GENERIC; /* FIXME */
97 case MMSYSERR_NODRIVER:
98 return DSERR_NODRIVER;
99 case MMSYSERR_NOMEM:
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;
107 default:
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" );
149 ExitProcess(1);
152 if (GetModuleFileNameA( 0, buffer, MAX_PATH ))
154 HKEY tmpkey;
156 if (!RegOpenKeyA( HKEY_LOCAL_MACHINE, "Software\\Wine\\Wine\\Config\\AppDefaults", &tmpkey ))
158 char appname[MAX_PATH+16];
159 char *p = strrchr( buffer, '\\' );
160 if (p!=NULL) {
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 );
171 /* get options */
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 );
206 RegCloseKey( hkey );
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" :
224 "Unknown");
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
238 * PARAMS
239 * pGuidSrc [I] Address of device GUID.
240 * pGuidDest [O] Address to receive unique device GUID.
242 * RETURNS
243 * Success: DS_OK
244 * Failure: DSERR_INVALIDPARAM
246 * NOTES
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 ) ) {
262 GUID guid;
263 int err = mmErr(waveOutMessage((HWAVEOUT)ds_default_playback,DRV_QUERYDSOUNDGUID,(DWORD)&guid,0));
264 if (err == DS_OK) {
265 memcpy(pGuidDest, &guid, sizeof(GUID));
266 return DS_OK;
270 if ( IsEqualGUID( &DSDEVID_DefaultCapture, pGuidSrc ) ||
271 IsEqualGUID( &DSDEVID_DefaultVoiceCapture, pGuidSrc ) ) {
272 GUID guid;
273 int err = mmErr(waveInMessage((HWAVEIN)ds_default_capture,DRV_QUERYDSOUNDGUID,(DWORD)&guid,0));
274 if (err == DS_OK) {
275 memcpy(pGuidDest, &guid, sizeof(GUID));
276 return DS_OK;
280 memcpy(pGuidDest, pGuidSrc, sizeof(GUID));
282 return DS_OK;
286 /***************************************************************************
287 * DirectSoundEnumerateA [DSOUND.2]
289 * Enumerate all DirectSound drivers installed in the system
291 * PARAMS
292 * lpDSEnumCallback [I] Address of callback function.
293 * lpContext [I] Address of user defined context passed to callback function.
295 * RETURNS
296 * Success: DS_OK
297 * Failure: DSERR_INVALIDPARAM
299 HRESULT WINAPI DirectSoundEnumerateA(
300 LPDSENUMCALLBACKA lpDSEnumCallback,
301 LPVOID lpContext)
303 unsigned devs, wod;
304 DSDRIVERDESC desc;
305 GUID guid;
306 int err;
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();
317 if (devs > 0) {
318 if (GetDeviceID(&DSDEVID_DefaultPlayback, &guid) == DS_OK) {
319 GUID temp;
320 for (wod = 0; wod < devs; ++wod) {
321 err = mmErr(waveOutMessage((HWAVEOUT)wod,DRV_QUERYDSOUNDGUID,(DWORD)&temp,0));
322 if (err == DS_OK) {
323 if (IsEqualGUID( &guid, &temp ) ) {
324 err = mmErr(waveOutMessage((HWAVEOUT)wod,DRV_QUERYDSOUNDDESC,(DWORD)&desc,0));
325 if (err == DS_OK) {
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)
329 return DS_OK;
337 for (wod = 0; wod < devs; ++wod) {
338 err = mmErr(waveOutMessage((HWAVEOUT)wod,DRV_QUERYDSOUNDDESC,(DWORD)&desc,0));
339 if (err == DS_OK) {
340 err = mmErr(waveOutMessage((HWAVEOUT)wod,DRV_QUERYDSOUNDGUID,(DWORD)&guid,0));
341 if (err == DS_OK) {
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)
345 return DS_OK;
349 return DS_OK;
352 /***************************************************************************
353 * DirectSoundEnumerateW [DSOUND.3]
355 * Enumerate all DirectSound drivers installed in the system
357 * PARAMS
358 * lpDSEnumCallback [I] Address of callback function.
359 * lpContext [I] Address of user defined context passed to callback function.
361 * RETURNS
362 * Success: DS_OK
363 * Failure: DSERR_INVALIDPARAM
365 HRESULT WINAPI DirectSoundEnumerateW(
366 LPDSENUMCALLBACKW lpDSEnumCallback,
367 LPVOID lpContext )
369 unsigned devs, wod;
370 DSDRIVERDESC desc;
371 GUID guid;
372 int err;
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();
385 if (devs > 0) {
386 if (GetDeviceID(&DSDEVID_DefaultPlayback, &guid) == DS_OK) {
387 GUID temp;
388 for (wod = 0; wod < devs; ++wod) {
389 err = mmErr(waveOutMessage((HWAVEOUT)wod,DRV_QUERYDSOUNDGUID,(DWORD)&temp,0));
390 if (err == DS_OK) {
391 if (IsEqualGUID( &guid, &temp ) ) {
392 err = mmErr(waveOutMessage((HWAVEOUT)wod,DRV_QUERYDSOUNDDESC,(DWORD)&desc,0));
393 if (err == DS_OK) {
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)
401 return DS_OK;
409 for (wod = 0; wod < devs; ++wod) {
410 err = mmErr(waveOutMessage((HWAVEOUT)wod,DRV_QUERYDSOUNDDESC,(DWORD)&desc,0));
411 if (err == DS_OK) {
412 err = mmErr(waveOutMessage((HWAVEOUT)wod,DRV_QUERYDSOUNDGUID,(DWORD)&guid,0));
413 if (err == DS_OK) {
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)
421 return DS_OK;
425 return DS_OK;
429 static void _dump_DSBCAPS(DWORD xmask) {
430 struct {
431 DWORD mask;
432 char *name;
433 } flags[] = {
434 #define FE(x) { x, #x },
435 FE(DSBCAPS_PRIMARYBUFFER)
436 FE(DSBCAPS_STATIC)
437 FE(DSBCAPS_LOCHARDWARE)
438 FE(DSBCAPS_LOCSOFTWARE)
439 FE(DSBCAPS_CTRL3D)
440 FE(DSBCAPS_CTRLFREQUENCY)
441 FE(DSBCAPS_CTRLPAN)
442 FE(DSBCAPS_CTRLVOLUME)
443 FE(DSBCAPS_CTRLPOSITIONNOTIFY)
444 FE(DSBCAPS_CTRLDEFAULT)
445 FE(DSBCAPS_CTRLALL)
446 FE(DSBCAPS_STICKYFOCUS)
447 FE(DSBCAPS_GLOBALFOCUS)
448 FE(DSBCAPS_GETCURRENTPOSITION2)
449 FE(DSBCAPS_MUTE3DATMAXDISTANCE)
450 #undef FE
452 int i;
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 /*******************************************************************************
460 * IDirectSound
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;
472 return DS_OK;
475 static HRESULT WINAPI IDirectSoundImpl_CreateSoundBuffer(
476 LPDIRECTSOUND8 iface,LPDSBUFFERDESC dsbd,LPLPDIRECTSOUNDBUFFER8 ppdsb,LPUNKNOWN lpunk
478 ICOM_THIS(IDirectSoundImpl,iface);
479 LPWAVEFORMATEX wfex;
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);
490 DPRINTF(")\n");
491 TRACE("(bufferbytes=%ld)\n",dsbd->dwBufferBytes);
492 TRACE("(lpwfxFormat=%p)\n",dsbd->lpwfxFormat);
495 wfex = dsbd->lpwfxFormat;
497 if (wfex)
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);
506 else
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;
523 if (ipdsb->hwbuf) {
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));
535 (*ippdsb)->ref = 1;
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));
550 if (newbuffers) {
551 This->buffers = newbuffers;
552 This->buffers[This->nrofbuffers] = *ippdsb;
553 This->nrofbuffers++;
554 TRACE("buffer count is now %d\n", This->nrofbuffers);
555 } else {
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);
562 return DS_OK;
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% */
608 return DS_OK;
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)) {
620 UINT i;
622 timeKillEvent(This->timerID);
623 timeEndPeriod(DS_TIME_RES);
625 if (This->buffers) {
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);
634 if (This->driver) {
635 IDsDriver_Close(This->driver);
637 if (This->drvdesc.dwFlags & DSDDESC_DOMMSYSTEMOPEN) {
638 waveOutClose(This->hwo);
640 if (This->driver)
641 IDsDriver_Release(This->driver);
643 HeapFree(GetProcessHeap(),0,This);
644 dsound = NULL;
645 return 0;
647 return This->ref;
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);
655 return DS_OK;
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");
665 *ppobj = NULL;
666 return E_FAIL;
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);
678 return DS_OK;
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);
688 return DS_OK;
691 static HRESULT WINAPI IDirectSoundImpl_Initialize(
692 LPDIRECTSOUND8 iface,
693 LPCGUID lpcGuid)
695 ICOM_THIS(IDirectSoundImpl,iface);
696 TRACE("(%p, %p)\n", This, lpcGuid);
697 return DS_OK;
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;
707 return DS_OK;
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.
733 * PARAMS
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.
738 * RETURNS
739 * Success: DS_OK
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;
747 unsigned wod, wodn;
748 HRESULT err = DSERR_INVALIDPARAM;
749 GUID devGuid;
750 BOOLEAN found = FALSE;
752 TRACE("(%s,%p,%p)\n",debugstr_guid(lpcGUID),ippDS,pUnkOuter);
754 if (ippDS == NULL) {
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;
771 if (dsound) {
772 if (IsEqualGUID(&devGuid, &dsound->guid) ) {
773 ERR("dsound already opened\n");
774 IDirectSound_AddRef((LPDIRECTSOUND)dsound);
775 *ippDS = dsound;
776 return DS_OK;
777 } else {
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++) {
789 GUID guid;
790 err = mmErr(waveOutMessage((HWAVEOUT)wod,DRV_QUERYDSOUNDGUID,(DWORD)(&guid),0));
791 if (err != DS_OK) {
792 WARN("waveOutMessage failed; err=%lx\n",err);
793 return err;
795 TRACE("got GUID %s for wod %d.\n", debugstr_guid(&guid), wod);
796 if (IsEqualGUID( &devGuid, &guid) ) {
797 err = DS_OK;
798 found = TRUE;
799 break;
803 if (err != DS_OK) {
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)
818 drv = NULL;
820 /* Allocate memory */
821 *ippDS = (IDirectSoundImpl*)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(IDirectSoundImpl));
822 if (*ippDS == NULL)
823 return DSERR_OUTOFMEMORY;
825 (*ippDS)->lpVtbl = &dsvt;
826 (*ippDS)->ref = 1;
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 */
844 if (drv) {
845 IDsDriver_GetDriverDesc(drv,&((*ippDS)->drvdesc));
846 } else {
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),
880 flags));
883 if (drv && (err == DS_OK))
884 err = IDsDriver_Open(drv);
886 /* FIXME: do we want to handle a temporarily busy device? */
887 if (err != DS_OK) {
888 HeapFree(GetProcessHeap(),0,*ippDS);
889 *ippDS = NULL;
890 return err;
893 /* the driver is now open, so it's now allowed to call GetCaps */
894 if (drv) {
895 IDsDriver_GetCaps(drv,&((*ippDS)->drvcaps));
896 } else {
897 /* FIXME: We should check the device capabilities */
898 (*ippDS)->drvcaps.dwFlags =
899 DSCAPS_PRIMARY16BIT | DSCAPS_PRIMARYSTEREO;
900 if (ds_emuldriver)
901 (*ippDS)->drvcaps.dwFlags |= DSCAPS_EMULDRIVER;
904 DSOUND_RecalcVolPan(&((*ippDS)->volpan));
906 InitializeCriticalSection(&((*ippDS)->mixlock));
907 RtlInitializeResource(&((*ippDS)->lock));
909 if (!dsound) {
910 dsound = (*ippDS);
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);
916 return DS_OK;
920 /*******************************************************************************
921 * DirectSound ClassFactory
923 typedef struct
925 /* IUnknown fields */
926 ICOM_VFIELD(IClassFactory);
927 DWORD ref;
928 } IClassFactoryImpl;
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;
938 static ULONG WINAPI
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);
976 return S_OK;
979 static ICOM_VTABLE(IClassFactory) DSCF_Vtbl = {
980 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
981 DSCF_QueryInterface,
982 DSCF_AddRef,
983 DSCF_Release,
984 DSCF_CreateInstance,
985 DSCF_LockServer
987 static IClassFactoryImpl DSOUND_CF = {&DSCF_Vtbl, 1 };
989 /*******************************************************************************
990 * DllGetClassObject [DSOUND.5]
991 * Retrieves class object from a DLL object
993 * NOTES
994 * Docs say returns STDAPI
996 * PARAMS
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
1001 * RETURNS
1002 * Success: S_OK
1003 * Failure: CLASS_E_CLASSNOTAVAILABLE, E_OUTOFMEMORY, E_INVALIDARG,
1004 * E_UNEXPECTED
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);
1012 return S_OK;
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.
1024 * RETURNS
1025 * Success: S_OK
1026 * Failure: S_FALSE
1028 DWORD WINAPI DSOUND_DllCanUnloadNow(void)
1030 FIXME("(void): stub\n");
1031 return S_FALSE;