Release 20030408.
[wine/gsoc-2012-control.git] / dlls / dsound / dsound_main.c
blob5cd20a00e76de4d0088716509dadd683e7278f37
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.2]
236 * Retrieves unique identifier of default device specified
238 * RETURNS
239 * Success: DS_OK
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 ) ) {
251 GUID guid;
252 int err = mmErr(waveOutMessage((HWAVEOUT)ds_default_playback,DRV_QUERYDSOUNDGUID,(DWORD)&guid,0));
253 if (err == DS_OK) {
254 memcpy(pGuidDest, &guid, sizeof(GUID));
255 return DS_OK;
259 if ( IsEqualGUID( &DSDEVID_DefaultCapture, pGuidSrc ) ||
260 IsEqualGUID( &DSDEVID_DefaultVoiceCapture, pGuidSrc ) ) {
261 GUID guid;
262 int err = mmErr(waveInMessage((HWAVEIN)ds_default_capture,DRV_QUERYDSOUNDGUID,(DWORD)&guid,0));
263 if (err == DS_OK) {
264 memcpy(pGuidDest, &guid, sizeof(GUID));
265 return DS_OK;
269 memcpy(pGuidDest, pGuidSrc, sizeof(GUID));
271 return DS_OK;
275 /***************************************************************************
276 * DirectSoundEnumerateA [DSOUND.2]
278 * Enumerate all DirectSound drivers installed in the system
280 * RETURNS
281 * Success: DS_OK
282 * Failure: DSERR_INVALIDPARAM
284 HRESULT WINAPI DirectSoundEnumerateA(
285 LPDSENUMCALLBACKA lpDSEnumCallback,
286 LPVOID lpContext)
288 unsigned devs, wod;
289 DSDRIVERDESC desc;
290 GUID guid;
291 int err;
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();
302 if (devs > 0) {
303 if (GetDeviceID(&DSDEVID_DefaultPlayback, &guid) == DS_OK) {
304 GUID temp;
305 for (wod = 0; wod < devs; ++wod) {
306 err = mmErr(waveOutMessage((HWAVEOUT)wod,DRV_QUERYDSOUNDGUID,(DWORD)&temp,0));
307 if (err == DS_OK) {
308 if (IsEqualGUID( &guid, &temp ) ) {
309 err = mmErr(waveOutMessage((HWAVEOUT)wod,DRV_QUERYDSOUNDDESC,(DWORD)&desc,0));
310 if (err == DS_OK) {
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)
314 return DS_OK;
322 for (wod = 0; wod < devs; ++wod) {
323 err = mmErr(waveOutMessage((HWAVEOUT)wod,DRV_QUERYDSOUNDDESC,(DWORD)&desc,0));
324 if (err == DS_OK) {
325 err = mmErr(waveOutMessage((HWAVEOUT)wod,DRV_QUERYDSOUNDGUID,(DWORD)&guid,0));
326 if (err == DS_OK) {
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)
330 return DS_OK;
334 return DS_OK;
337 /***************************************************************************
338 * DirectSoundEnumerateW [DSOUND.3]
340 * Enumerate all DirectSound drivers installed in the system
342 * RETURNS
343 * Success: DS_OK
344 * Failure: DSERR_INVALIDPARAM
346 HRESULT WINAPI DirectSoundEnumerateW(
347 LPDSENUMCALLBACKW lpDSEnumCallback,
348 LPVOID lpContext )
350 unsigned devs, wod;
351 DSDRIVERDESC desc;
352 GUID guid;
353 int err;
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();
366 if (devs > 0) {
367 if (GetDeviceID(&DSDEVID_DefaultPlayback, &guid) == DS_OK) {
368 GUID temp;
369 for (wod = 0; wod < devs; ++wod) {
370 err = mmErr(waveOutMessage((HWAVEOUT)wod,DRV_QUERYDSOUNDGUID,(DWORD)&temp,0));
371 if (err == DS_OK) {
372 if (IsEqualGUID( &guid, &temp ) ) {
373 err = mmErr(waveOutMessage((HWAVEOUT)wod,DRV_QUERYDSOUNDDESC,(DWORD)&desc,0));
374 if (err == DS_OK) {
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)
382 return DS_OK;
390 for (wod = 0; wod < devs; ++wod) {
391 err = mmErr(waveOutMessage((HWAVEOUT)wod,DRV_QUERYDSOUNDDESC,(DWORD)&desc,0));
392 if (err == DS_OK) {
393 err = mmErr(waveOutMessage((HWAVEOUT)wod,DRV_QUERYDSOUNDGUID,(DWORD)&guid,0));
394 if (err == DS_OK) {
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)
402 return DS_OK;
406 return DS_OK;
410 static void _dump_DSBCAPS(DWORD xmask) {
411 struct {
412 DWORD mask;
413 char *name;
414 } flags[] = {
415 #define FE(x) { x, #x },
416 FE(DSBCAPS_PRIMARYBUFFER)
417 FE(DSBCAPS_STATIC)
418 FE(DSBCAPS_LOCHARDWARE)
419 FE(DSBCAPS_LOCSOFTWARE)
420 FE(DSBCAPS_CTRL3D)
421 FE(DSBCAPS_CTRLFREQUENCY)
422 FE(DSBCAPS_CTRLPAN)
423 FE(DSBCAPS_CTRLVOLUME)
424 FE(DSBCAPS_CTRLPOSITIONNOTIFY)
425 FE(DSBCAPS_CTRLDEFAULT)
426 FE(DSBCAPS_CTRLALL)
427 FE(DSBCAPS_STICKYFOCUS)
428 FE(DSBCAPS_GLOBALFOCUS)
429 FE(DSBCAPS_GETCURRENTPOSITION2)
430 FE(DSBCAPS_MUTE3DATMAXDISTANCE)
431 #undef FE
433 int i;
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 /*******************************************************************************
441 * IDirectSound
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;
453 return DS_OK;
456 static HRESULT WINAPI IDirectSoundImpl_CreateSoundBuffer(
457 LPDIRECTSOUND8 iface,LPDSBUFFERDESC dsbd,LPLPDIRECTSOUNDBUFFER8 ppdsb,LPUNKNOWN lpunk
459 ICOM_THIS(IDirectSoundImpl,iface);
460 LPWAVEFORMATEX wfex;
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);
471 DPRINTF(")\n");
472 TRACE("(bufferbytes=%ld)\n",dsbd->dwBufferBytes);
473 TRACE("(lpwfxFormat=%p)\n",dsbd->lpwfxFormat);
476 wfex = dsbd->lpwfxFormat;
478 if (wfex)
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);
487 else
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;
504 if (ipdsb->hwbuf) {
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));
516 (*ippdsb)->ref = 1;
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));
531 if (newbuffers) {
532 This->buffers = newbuffers;
533 This->buffers[This->nrofbuffers] = *ippdsb;
534 This->nrofbuffers++;
535 TRACE("buffer count is now %d\n", This->nrofbuffers);
536 } else {
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);
543 return DS_OK;
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% */
589 return DS_OK;
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)) {
601 UINT i;
603 timeKillEvent(This->timerID);
604 timeEndPeriod(DS_TIME_RES);
606 if (This->buffers) {
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);
615 if (This->driver) {
616 IDsDriver_Close(This->driver);
618 if (This->drvdesc.dwFlags & DSDDESC_DOMMSYSTEMOPEN) {
619 waveOutClose(This->hwo);
621 if (This->driver)
622 IDsDriver_Release(This->driver);
624 HeapFree(GetProcessHeap(),0,This);
625 dsound = NULL;
626 return 0;
628 return This->ref;
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);
636 return DS_OK;
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");
646 *ppobj = NULL;
647 return E_FAIL;
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);
659 return DS_OK;
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);
669 return DS_OK;
672 static HRESULT WINAPI IDirectSoundImpl_Initialize(
673 LPDIRECTSOUND8 iface,
674 LPCGUID lpcGuid)
676 ICOM_THIS(IDirectSoundImpl,iface);
677 TRACE("(%p, %p)\n", This, lpcGuid);
678 return DS_OK;
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;
688 return DS_OK;
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;
716 unsigned wod, wodn;
717 HRESULT err = DSERR_INVALIDPARAM;
718 GUID devGuid;
719 TRACE("(%p,%p,%p)\n",lpcGUID,ippDS,pUnkOuter);
721 if (ippDS == NULL) {
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;
738 if (dsound) {
739 if (IsEqualGUID(&devGuid, &dsound->guid) ) {
740 ERR("dsound already opened\n");
741 IDirectSound_AddRef((LPDIRECTSOUND)dsound);
742 *ippDS = dsound;
743 return DS_OK;
744 } else {
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++) {
754 GUID guid;
755 err = mmErr(waveOutMessage((HWAVEOUT)wod,DRV_QUERYDSOUNDGUID,(DWORD)(&guid),0));
756 if (err != DS_OK) {
757 WARN("waveOutMessage failed; err=%lx\n",err);
758 return err;
760 if (IsEqualGUID( &devGuid, &guid) ) {
761 err = DS_OK;
762 break;
766 if (err != DS_OK) {
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)
776 drv = NULL;
778 /* Allocate memory */
779 *ippDS = (IDirectSoundImpl*)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(IDirectSoundImpl));
780 if (*ippDS == NULL)
781 return DSERR_OUTOFMEMORY;
783 ICOM_VTBL(*ippDS) = &dsvt;
784 (*ippDS)->ref = 1;
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 */
802 if (drv) {
803 IDsDriver_GetDriverDesc(drv,&((*ippDS)->drvdesc));
804 } else {
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),
838 flags));
841 if (drv && (err == DS_OK))
842 err = IDsDriver_Open(drv);
844 /* FIXME: do we want to handle a temporarily busy device? */
845 if (err != DS_OK) {
846 HeapFree(GetProcessHeap(),0,*ippDS);
847 *ippDS = NULL;
848 return err;
851 /* the driver is now open, so it's now allowed to call GetCaps */
852 if (drv) {
853 IDsDriver_GetCaps(drv,&((*ippDS)->drvcaps));
854 } else {
855 /* FIXME: We should check the device capabilities */
856 (*ippDS)->drvcaps.dwFlags =
857 DSCAPS_PRIMARY16BIT | DSCAPS_PRIMARYSTEREO;
858 if (ds_emuldriver)
859 (*ippDS)->drvcaps.dwFlags |= DSCAPS_EMULDRIVER;
862 DSOUND_RecalcVolPan(&((*ippDS)->volpan));
864 InitializeCriticalSection(&((*ippDS)->mixlock));
865 RtlInitializeResource(&((*ippDS)->lock));
867 if (!dsound) {
868 dsound = (*ippDS);
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);
874 return DS_OK;
878 /*******************************************************************************
879 * DirectSound ClassFactory
881 typedef struct
883 /* IUnknown fields */
884 ICOM_VFIELD(IClassFactory);
885 DWORD ref;
886 } IClassFactoryImpl;
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;
896 static ULONG WINAPI
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? */
928 #if 0
929 return IKsPropertySetImpl_Create(0,(IKsPropertySetImpl**)ppobj);
930 #else
931 return E_NOINTERFACE;
932 #endif
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);
942 return S_OK;
945 static ICOM_VTABLE(IClassFactory) DSCF_Vtbl = {
946 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
947 DSCF_QueryInterface,
948 DSCF_AddRef,
949 DSCF_Release,
950 DSCF_CreateInstance,
951 DSCF_LockServer
953 static IClassFactoryImpl DSOUND_CF = {&DSCF_Vtbl, 1 };
955 /*******************************************************************************
956 * DllGetClassObject [DSOUND.5]
957 * Retrieves class object from a DLL object
959 * NOTES
960 * Docs say returns STDAPI
962 * PARAMS
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
967 * RETURNS
968 * Success: S_OK
969 * Failure: CLASS_E_CLASSNOTAVAILABLE, E_OUTOFMEMORY, E_INVALIDARG,
970 * E_UNEXPECTED
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);
978 return S_OK;
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.
989 * RETURNS
990 * Success: S_OK
991 * Failure: S_FALSE
993 DWORD WINAPI DSOUND_DllCanUnloadNow(void)
995 FIXME("(void): stub\n");
996 return S_FALSE;