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"
45 #include <sys/types.h>
46 #include <sys/fcntl.h>
54 #define NONAMELESSSTRUCT
55 #define NONAMELESSUNION
66 #include "wine/windef16.h"
67 #include "wine/winbase16.h"
68 #include "wine/debug.h"
71 #include "dsound_private.h"
74 WINE_DEFAULT_DEBUG_CHANNEL(dsound);
76 /* these are eligible for tuning... they must be high on slow machines... */
77 /* some stuff may get more responsive with lower values though... */
78 #define DS_EMULDRIVER 0 /* some games (Quake 2, UT) refuse to accept
79 emulated dsound devices. set to 0 ! */
80 #define DS_HEL_MARGIN 5 /* HEL only: number of waveOut fragments ahead to mix in new buffers
81 * (keep this close or equal to DS_HEL_QUEUE for best results) */
82 #define DS_HEL_QUEUE 5 /* HEL only: number of waveOut fragments ahead to queue to driver
83 * (this will affect HEL sound reliability and latency) */
85 #define DS_SND_QUEUE_MAX 28 /* max number of fragments to prebuffer */
86 #define DS_SND_QUEUE_MIN 12 /* min number of fragments to prebuffer */
88 IDirectSoundImpl* dsound = NULL;
89 static GUID device_guids[MAXWAVEDRIVERS];
91 HRESULT mmErr(UINT err)
94 case MMSYSERR_NOERROR:
96 case MMSYSERR_ALLOCATED:
97 return DSERR_ALLOCATED;
99 case MMSYSERR_INVALHANDLE:
100 case WAVERR_STILLPLAYING:
101 return DSERR_GENERIC; /* FIXME */
102 case MMSYSERR_NODRIVER:
103 return DSERR_NODRIVER;
105 return DSERR_OUTOFMEMORY;
106 case MMSYSERR_INVALPARAM:
107 case WAVERR_BADFORMAT:
108 case WAVERR_UNPREPARED:
109 return DSERR_INVALIDPARAM;
110 case MMSYSERR_NOTSUPPORTED:
111 return DSERR_UNSUPPORTED;
113 FIXME("Unknown MMSYS error %d\n",err);
114 return DSERR_GENERIC;
118 int ds_emuldriver = DS_EMULDRIVER;
119 int ds_hel_margin = DS_HEL_MARGIN;
120 int ds_hel_queue = DS_HEL_QUEUE;
121 int ds_snd_queue_max = DS_SND_QUEUE_MAX;
122 int ds_snd_queue_min = DS_SND_QUEUE_MIN;
123 int ds_hw_accel = DS_HW_ACCEL_FULL;
124 int ds_default_playback = 0;
125 int ds_default_capture = 0;
128 * Get a config key from either the app-specific or the default config
131 inline static DWORD get_config_key( HKEY defkey, HKEY appkey, const char *name,
132 char *buffer, DWORD size )
134 if (appkey && !RegQueryValueExA( appkey, name, 0, NULL, buffer, &size )) return 0;
135 return RegQueryValueExA( defkey, name, 0, NULL, buffer, &size );
140 * Setup the dsound options.
143 void setup_dsound_options(void)
145 char buffer[MAX_PATH+1];
146 HKEY hkey, appkey = 0;
149 buffer[MAX_PATH]='\0';
151 if (RegCreateKeyExA( HKEY_LOCAL_MACHINE, "Software\\Wine\\Wine\\Config\\dsound", 0, NULL,
152 REG_OPTION_VOLATILE, KEY_ALL_ACCESS, NULL, &hkey, NULL ))
154 ERR("Cannot create config registry key\n" );
158 len = GetModuleFileNameA( 0, buffer, MAX_PATH );
159 if (len && len < MAX_PATH)
163 if (!RegOpenKeyA( HKEY_LOCAL_MACHINE, "Software\\Wine\\Wine\\Config\\AppDefaults", &tmpkey ))
165 char appname[MAX_PATH+16];
166 char *p = strrchr( buffer, '\\' );
168 appname[MAX_PATH]='\0';
169 strncpy(appname,p+1,MAX_PATH);
170 strcat(appname,"\\dsound");
171 TRACE("appname = [%s] \n",appname);
172 if (RegOpenKeyA( tmpkey, appname, &appkey )) appkey = 0;
173 RegCloseKey( tmpkey );
180 if (!get_config_key( hkey, appkey, "EmulDriver", buffer, MAX_PATH ))
181 ds_emuldriver = strcmp(buffer, "N");
183 if (!get_config_key( hkey, appkey, "HELmargin", buffer, MAX_PATH ))
184 ds_hel_margin = atoi(buffer);
186 if (!get_config_key( hkey, appkey, "HELqueue", buffer, MAX_PATH ))
187 ds_hel_queue = atoi(buffer);
189 if (!get_config_key( hkey, appkey, "SndQueueMax", buffer, MAX_PATH ))
190 ds_snd_queue_max = atoi(buffer);
192 if (!get_config_key( hkey, appkey, "SndQueueMin", buffer, MAX_PATH ))
193 ds_snd_queue_min = atoi(buffer);
195 if (!get_config_key( hkey, appkey, "HardwareAcceleration", buffer, MAX_PATH )) {
196 if (strcmp(buffer, "Full") == 0)
197 ds_hw_accel = DS_HW_ACCEL_FULL;
198 else if (strcmp(buffer, "Standard") == 0)
199 ds_hw_accel = DS_HW_ACCEL_STANDARD;
200 else if (strcmp(buffer, "Basic") == 0)
201 ds_hw_accel = DS_HW_ACCEL_BASIC;
202 else if (strcmp(buffer, "Emulation") == 0)
203 ds_hw_accel = DS_HW_ACCEL_EMULATION;
206 if (!get_config_key( hkey, appkey, "DefaultPlayback", buffer, MAX_PATH ))
207 ds_default_playback = atoi(buffer);
209 if (!get_config_key( hkey, appkey, "DefaultCapture", buffer, MAX_PATH ))
210 ds_default_capture = atoi(buffer);
212 if (appkey) RegCloseKey( appkey );
215 if (ds_emuldriver != DS_EMULDRIVER )
216 WARN("ds_emuldriver = %d (default=%d)\n",ds_emuldriver, DS_EMULDRIVER);
217 if (ds_hel_margin != DS_HEL_MARGIN )
218 WARN("ds_hel_margin = %d (default=%d)\n",ds_hel_margin, DS_HEL_MARGIN );
219 if (ds_hel_queue != DS_HEL_QUEUE )
220 WARN("ds_hel_queue = %d (default=%d)\n",ds_hel_queue, DS_HEL_QUEUE );
221 if (ds_snd_queue_max != DS_SND_QUEUE_MAX)
222 WARN("ds_snd_queue_max = %d (default=%d)\n",ds_snd_queue_max ,DS_SND_QUEUE_MAX);
223 if (ds_snd_queue_min != DS_SND_QUEUE_MIN)
224 WARN("ds_snd_queue_min = %d (default=%d)\n",ds_snd_queue_min ,DS_SND_QUEUE_MIN);
225 if (ds_hw_accel != DS_HW_ACCEL_FULL)
226 WARN("ds_hw_accel = %s (default=Full)\n",
227 ds_hw_accel==DS_HW_ACCEL_FULL ? "Full" :
228 ds_hw_accel==DS_HW_ACCEL_STANDARD ? "Standard" :
229 ds_hw_accel==DS_HW_ACCEL_BASIC ? "Basic" :
230 ds_hw_accel==DS_HW_ACCEL_EMULATION ? "Emulation" :
232 if (ds_default_playback != 0)
233 WARN("ds_default_playback = %d (default=0)\n",ds_default_playback);
234 if (ds_default_capture != 0)
235 WARN("ds_default_capture = %d (default=0)\n",ds_default_playback);
238 const char * get_device_id(LPCGUID pGuid)
240 if (IsEqualGUID(&DSDEVID_DefaultPlayback, pGuid))
241 return "DSDEVID_DefaultPlayback";
242 else if (IsEqualGUID(&DSDEVID_DefaultVoicePlayback, pGuid))
243 return "DSDEVID_DefaultVoicePlayback";
244 else if (IsEqualGUID(&DSDEVID_DefaultCapture, pGuid))
245 return "DSDEVID_DefaultCapture";
246 else if (IsEqualGUID(&DSDEVID_DefaultVoiceCapture, pGuid))
247 return "DSDEVID_DefaultVoiceCapture";
248 return debugstr_guid(pGuid);
251 /***************************************************************************
252 * GetDeviceID [DSOUND.9]
254 * Retrieves unique identifier of default device specified
257 * pGuidSrc [I] Address of device GUID.
258 * pGuidDest [O] Address to receive unique device GUID.
262 * Failure: DSERR_INVALIDPARAM
265 * pGuidSrc is a valid device GUID or DSDEVID_DefaultPlayback,
266 * DSDEVID_DefaultCapture, DSDEVID_DefaultVoicePlayback, or
267 * DSDEVID_DefaultVoiceCapture.
268 * Returns pGuidSrc if pGuidSrc is a valid device or the device
269 * GUID for the specified constants.
271 HRESULT WINAPI GetDeviceID(LPCGUID pGuidSrc, LPGUID pGuidDest)
273 TRACE("(%s,%p)\n", get_device_id(pGuidSrc),pGuidDest);
275 if ( pGuidSrc == NULL) {
276 WARN("invalid parameter: pGuidSrc == NULL\n");
277 return DSERR_INVALIDPARAM;
280 if ( pGuidDest == NULL ) {
281 WARN("invalid parameter: pGuidDest == NULL\n");
282 return DSERR_INVALIDPARAM;
285 if ( IsEqualGUID( &DSDEVID_DefaultPlayback, pGuidSrc ) ||
286 IsEqualGUID( &DSDEVID_DefaultVoicePlayback, pGuidSrc ) ) {
288 int err = mmErr(waveOutMessage((HWAVEOUT)ds_default_playback,DRV_QUERYDSOUNDGUID,(DWORD)&guid,0));
290 memcpy(pGuidDest, &guid, sizeof(GUID));
291 TRACE("returns %s\n", get_device_id(pGuidDest));
296 if ( IsEqualGUID( &DSDEVID_DefaultCapture, pGuidSrc ) ||
297 IsEqualGUID( &DSDEVID_DefaultVoiceCapture, pGuidSrc ) ) {
299 int err = mmErr(waveInMessage((HWAVEIN)ds_default_capture,DRV_QUERYDSOUNDGUID,(DWORD)&guid,0));
301 memcpy(pGuidDest, &guid, sizeof(GUID));
302 TRACE("returns %s\n", get_device_id(pGuidDest));
307 memcpy(pGuidDest, pGuidSrc, sizeof(GUID));
308 TRACE("returns %s\n", get_device_id(pGuidDest));
314 /***************************************************************************
315 * DirectSoundEnumerateA [DSOUND.2]
317 * Enumerate all DirectSound drivers installed in the system
320 * lpDSEnumCallback [I] Address of callback function.
321 * lpContext [I] Address of user defined context passed to callback function.
325 * Failure: DSERR_INVALIDPARAM
327 HRESULT WINAPI DirectSoundEnumerateA(
328 LPDSENUMCALLBACKA lpDSEnumCallback,
336 TRACE("lpDSEnumCallback = %p, lpContext = %p\n",
337 lpDSEnumCallback, lpContext);
339 if (lpDSEnumCallback == NULL) {
340 WARN("invalid parameter: lpDSEnumCallback == NULL\n");
341 return DSERR_INVALIDPARAM;
344 devs = waveOutGetNumDevs();
346 if (GetDeviceID(&DSDEVID_DefaultPlayback, &guid) == DS_OK) {
348 for (wod = 0; wod < devs; ++wod) {
349 err = mmErr(waveOutMessage((HWAVEOUT)wod,DRV_QUERYDSOUNDGUID,(DWORD)&temp,0));
351 if (IsEqualGUID( &guid, &temp ) ) {
352 err = mmErr(waveOutMessage((HWAVEOUT)wod,DRV_QUERYDSOUNDDESC,(DWORD)&desc,0));
354 TRACE("calling lpDSEnumCallback(NULL,\"%s\",\"%s\",%p)\n",
355 "Primary Sound Driver",desc.szDrvName,lpContext);
356 if (lpDSEnumCallback(NULL, "Primary Sound Driver", desc.szDrvName, lpContext) == FALSE)
365 for (wod = 0; wod < devs; ++wod) {
366 err = mmErr(waveOutMessage((HWAVEOUT)wod,DRV_QUERYDSOUNDDESC,(DWORD)&desc,0));
368 err = mmErr(waveOutMessage((HWAVEOUT)wod,DRV_QUERYDSOUNDGUID,(DWORD)&device_guids[wod],0));
370 TRACE("calling lpDSEnumCallback(%s,\"%s\",\"%s\",%p)\n",
371 debugstr_guid(&device_guids[wod]),desc.szDesc,desc.szDrvName,lpContext);
372 if (lpDSEnumCallback(&device_guids[wod], desc.szDesc, desc.szDrvName, lpContext) == FALSE)
380 /***************************************************************************
381 * DirectSoundEnumerateW [DSOUND.3]
383 * Enumerate all DirectSound drivers installed in the system
386 * lpDSEnumCallback [I] Address of callback function.
387 * lpContext [I] Address of user defined context passed to callback function.
391 * Failure: DSERR_INVALIDPARAM
393 HRESULT WINAPI DirectSoundEnumerateW(
394 LPDSENUMCALLBACKW lpDSEnumCallback,
401 WCHAR wDesc[MAXPNAMELEN];
402 WCHAR wName[MAXPNAMELEN];
404 TRACE("lpDSEnumCallback = %p, lpContext = %p\n",
405 lpDSEnumCallback, lpContext);
407 if (lpDSEnumCallback == NULL) {
408 WARN("invalid parameter: lpDSEnumCallback == NULL\n");
409 return DSERR_INVALIDPARAM;
412 devs = waveOutGetNumDevs();
414 if (GetDeviceID(&DSDEVID_DefaultPlayback, &guid) == DS_OK) {
416 for (wod = 0; wod < devs; ++wod) {
417 err = mmErr(waveOutMessage((HWAVEOUT)wod,DRV_QUERYDSOUNDGUID,(DWORD)&temp,0));
419 if (IsEqualGUID( &guid, &temp ) ) {
420 err = mmErr(waveOutMessage((HWAVEOUT)wod,DRV_QUERYDSOUNDDESC,(DWORD)&desc,0));
422 TRACE("calling lpDSEnumCallback(NULL,\"%s\",\"%s\",%p)\n",
423 "Primary Sound Driver",desc.szDrvName,lpContext);
424 MultiByteToWideChar( CP_ACP, 0, "Primary Sound Driver", -1,
425 wDesc, sizeof(wDesc)/sizeof(WCHAR) );
426 MultiByteToWideChar( CP_ACP, 0, desc.szDrvName, -1,
427 wName, sizeof(wName)/sizeof(WCHAR) );
428 if (lpDSEnumCallback(NULL, wDesc, wName, lpContext) == FALSE)
437 for (wod = 0; wod < devs; ++wod) {
438 err = mmErr(waveOutMessage((HWAVEOUT)wod,DRV_QUERYDSOUNDDESC,(DWORD)&desc,0));
440 err = mmErr(waveOutMessage((HWAVEOUT)wod,DRV_QUERYDSOUNDGUID,(DWORD)&device_guids[wod],0));
442 TRACE("calling lpDSEnumCallback(%s,\"%s\",\"%s\",%p)\n",
443 debugstr_guid(&device_guids[wod]),desc.szDesc,desc.szDrvName,lpContext);
444 MultiByteToWideChar( CP_ACP, 0, desc.szDesc, -1,
445 wDesc, sizeof(wDesc)/sizeof(WCHAR) );
446 MultiByteToWideChar( CP_ACP, 0, desc.szDrvName, -1,
447 wName, sizeof(wName)/sizeof(WCHAR) );
448 if (lpDSEnumCallback(&device_guids[wod], wDesc, wName, lpContext) == FALSE)
456 /*******************************************************************************
457 * DirectSound ClassFactory
460 static HRESULT WINAPI
461 DSCF_QueryInterface(LPCLASSFACTORY iface,REFIID riid,LPVOID *ppobj) {
462 ICOM_THIS(IClassFactoryImpl,iface);
464 FIXME("(%p)->(%s,%p),stub!\n",This,debugstr_guid(riid),ppobj);
465 return E_NOINTERFACE;
469 DSCF_AddRef(LPCLASSFACTORY iface) {
470 ICOM_THIS(IClassFactoryImpl,iface);
471 TRACE("(%p) ref was %ld\n", This, This->ref);
472 return ++(This->ref);
475 static ULONG WINAPI DSCF_Release(LPCLASSFACTORY iface) {
476 ICOM_THIS(IClassFactoryImpl,iface);
477 /* static class, won't be freed */
478 TRACE("(%p) ref was %ld\n", This, This->ref);
479 return --(This->ref);
482 static HRESULT WINAPI DSCF_CreateInstance(
483 LPCLASSFACTORY iface,LPUNKNOWN pOuter,REFIID riid,LPVOID *ppobj
485 ICOM_THIS(IClassFactoryImpl,iface);
486 TRACE("(%p)->(%p,%s,%p)\n",This,pOuter,debugstr_guid(riid),ppobj);
489 WARN("invalid parameter\n");
490 return DSERR_INVALIDPARAM;
495 if ( IsEqualIID( &IID_IDirectSound, riid ) )
496 return DSOUND_Create(0,(LPDIRECTSOUND*)ppobj,pOuter);
498 if ( IsEqualIID( &IID_IDirectSound8, riid ) )
499 return DSOUND_Create8(0,(LPDIRECTSOUND8*)ppobj,pOuter);
501 WARN("(%p,%p,%s,%p) Interface not found!\n",This,pOuter,debugstr_guid(riid),ppobj);
502 return E_NOINTERFACE;
505 static HRESULT WINAPI DSCF_LockServer(LPCLASSFACTORY iface,BOOL dolock) {
506 ICOM_THIS(IClassFactoryImpl,iface);
507 FIXME("(%p)->(%d),stub!\n",This,dolock);
511 static ICOM_VTABLE(IClassFactory) DSCF_Vtbl = {
512 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
520 static IClassFactoryImpl DSOUND_CF = { &DSCF_Vtbl, 1 };
522 /*******************************************************************************
523 * DirectSoundPrivate ClassFactory
526 static HRESULT WINAPI
527 DSPCF_QueryInterface(LPCLASSFACTORY iface,REFIID riid,LPVOID *ppobj) {
528 ICOM_THIS(IClassFactoryImpl,iface);
530 FIXME("(%p)->(%s,%p),stub!\n",This,debugstr_guid(riid),ppobj);
531 return E_NOINTERFACE;
535 DSPCF_AddRef(LPCLASSFACTORY iface) {
536 ICOM_THIS(IClassFactoryImpl,iface);
537 TRACE("(%p) ref was %ld\n", This, This->ref);
538 return ++(This->ref);
542 DSPCF_Release(LPCLASSFACTORY iface) {
543 ICOM_THIS(IClassFactoryImpl,iface);
544 /* static class, won't be freed */
545 TRACE("(%p) ref was %ld\n", This, This->ref);
546 return --(This->ref);
549 static HRESULT WINAPI
550 DSPCF_CreateInstance(
551 LPCLASSFACTORY iface,LPUNKNOWN pOuter,REFIID riid,LPVOID *ppobj
553 ICOM_THIS(IClassFactoryImpl,iface);
554 TRACE("(%p)->(%p,%s,%p)\n",This,pOuter,debugstr_guid(riid),ppobj);
557 WARN("invalid parameter\n");
558 return DSERR_INVALIDPARAM;
563 if ( IsEqualGUID( &IID_IKsPropertySet, riid ) ) {
564 return IKsPrivatePropertySetImpl_Create((IKsPrivatePropertySetImpl**)ppobj);
567 WARN("(%p,%p,%s,%p) Interface not found!\n",This,pOuter,debugstr_guid(riid),ppobj);
568 return E_NOINTERFACE;
571 static HRESULT WINAPI
572 DSPCF_LockServer(LPCLASSFACTORY iface,BOOL dolock) {
573 ICOM_THIS(IClassFactoryImpl,iface);
574 FIXME("(%p)->(%d),stub!\n",This,dolock);
578 static ICOM_VTABLE(IClassFactory) DSPCF_Vtbl = {
579 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
580 DSPCF_QueryInterface,
583 DSPCF_CreateInstance,
587 static IClassFactoryImpl DSOUND_PRIVATE_CF = { &DSPCF_Vtbl, 1 };
589 /*******************************************************************************
590 * DllGetClassObject [DSOUND.5]
591 * Retrieves class object from a DLL object
594 * Docs say returns STDAPI
597 * rclsid [I] CLSID for the class object
598 * riid [I] Reference to identifier of interface for class object
599 * ppv [O] Address of variable to receive interface pointer for riid
603 * Failure: CLASS_E_CLASSNOTAVAILABLE, E_OUTOFMEMORY, E_INVALIDARG,
606 DWORD WINAPI DSOUND_DllGetClassObject(REFCLSID rclsid,REFIID riid,LPVOID *ppv)
608 TRACE("(%s,%s,%p)\n", debugstr_guid(rclsid), debugstr_guid(riid), ppv);
611 WARN("invalid parameter\n");
617 if ( IsEqualCLSID( &CLSID_DirectSound, rclsid ) ||
618 IsEqualCLSID( &CLSID_DirectSound8, rclsid ) ) {
619 if ( IsEqualCLSID( &IID_IClassFactory, riid ) ) {
620 *ppv = (LPVOID)&DSOUND_CF;
621 IClassFactory_AddRef((IClassFactory*)*ppv);
624 WARN("(%s,%s,%p): no interface found.\n",
625 debugstr_guid(rclsid), debugstr_guid(riid), ppv);
629 if ( IsEqualCLSID( &CLSID_DirectSoundCapture, rclsid ) ||
630 IsEqualCLSID( &CLSID_DirectSoundCapture8, rclsid ) ) {
631 if ( IsEqualCLSID( &IID_IClassFactory, riid ) ) {
632 *ppv = (LPVOID)&DSOUND_CAPTURE_CF;
633 IClassFactory_AddRef((IClassFactory*)*ppv);
636 WARN("(%s,%s,%p): no interface found.\n",
637 debugstr_guid(rclsid), debugstr_guid(riid), ppv);
641 if ( IsEqualCLSID( &CLSID_DirectSoundFullDuplex, rclsid ) ) {
642 if ( IsEqualCLSID( &IID_IClassFactory, riid ) ) {
643 *ppv = (LPVOID)&DSOUND_FULLDUPLEX_CF;
644 IClassFactory_AddRef((IClassFactory*)*ppv);
647 WARN("(%s,%s,%p): no interface found.\n",
648 debugstr_guid(rclsid), debugstr_guid(riid), ppv);
652 if ( IsEqualCLSID( &CLSID_DirectSoundPrivate, rclsid ) ) {
653 if ( IsEqualCLSID( &IID_IClassFactory, riid ) ) {
654 *ppv = (LPVOID)&DSOUND_PRIVATE_CF;
655 IClassFactory_AddRef((IClassFactory*)*ppv);
658 WARN("(%s,%s,%p): no interface found.\n",
659 debugstr_guid(rclsid), debugstr_guid(riid), ppv);
663 WARN("(%s,%s,%p): no class found.\n", debugstr_guid(rclsid), debugstr_guid(riid), ppv);
664 return CLASS_E_CLASSNOTAVAILABLE;
668 /*******************************************************************************
669 * DllCanUnloadNow [DSOUND.4]
670 * Determines whether the DLL is in use.
676 DWORD WINAPI DSOUND_DllCanUnloadNow(void)
678 FIXME("(void): stub\n");