msxml3: Skip leading space characters when loading from BSTR.
[wine] / dlls / dsound / dsound_main.c
1 /*                      DirectSound
2  *
3  * Copyright 1998 Marcus Meissner
4  * Copyright 1998 Rob Riggs
5  * Copyright 2000-2002 TransGaming Technologies, Inc.
6  *
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.
11  *
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.
16  *
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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20  *
21  * Most thread locking is complete. There may be a few race
22  * conditions still lurking.
23  *
24  * TODO:
25  *      Implement SetCooperativeLevel properly (need to address focus issues)
26  *      Implement DirectSound3DBuffers (stubs in place)
27  *      Use hardware 3D support if available
28  *      Add critical section locking inside Release and AddRef methods
29  *      Handle static buffers - put those in hardware, non-static not in hardware
30  *      Hardware DuplicateSoundBuffer
31  *      Proper volume calculation for 3d buffers
32  *      Remove DS_HEL_FRAGS and use mixer fragment length for it
33  */
34
35 #include <stdarg.h>
36
37 #define COBJMACROS
38 #define NONAMELESSSTRUCT
39 #define NONAMELESSUNION
40 #include "windef.h"
41 #include "winbase.h"
42 #include "winuser.h"
43 #include "winnls.h"
44 #include "winreg.h"
45 #include "mmsystem.h"
46 #include "winternl.h"
47 #include "mmddk.h"
48 #include "wine/debug.h"
49 #include "dsound.h"
50 #include "dsconf.h"
51 #include "ks.h"
52 #include "rpcproxy.h"
53 #include "rpc.h"
54 #include "rpcndr.h"
55 #include "unknwn.h"
56 #include "oleidl.h"
57 #include "shobjidl.h"
58
59 #include "initguid.h"
60 #include "ksmedia.h"
61 #include "propkey.h"
62 #include "devpkey.h"
63
64 #include "dsound_private.h"
65
66 WINE_DEFAULT_DEBUG_CHANNEL(dsound);
67
68 struct list DSOUND_renderers = LIST_INIT(DSOUND_renderers);
69 CRITICAL_SECTION DSOUND_renderers_lock;
70 static CRITICAL_SECTION_DEBUG DSOUND_renderers_lock_debug =
71 {
72     0, 0, &DSOUND_renderers_lock,
73     { &DSOUND_renderers_lock_debug.ProcessLocksList, &DSOUND_renderers_lock_debug.ProcessLocksList },
74       0, 0, { (DWORD_PTR)(__FILE__ ": DSOUND_renderers_lock") }
75 };
76 CRITICAL_SECTION DSOUND_renderers_lock = { &DSOUND_renderers_lock_debug, -1, 0, 0, 0, 0 };
77
78 struct list DSOUND_capturers = LIST_INIT(DSOUND_capturers);
79 CRITICAL_SECTION DSOUND_capturers_lock;
80 static CRITICAL_SECTION_DEBUG DSOUND_capturers_lock_debug =
81 {
82     0, 0, &DSOUND_capturers_lock,
83     { &DSOUND_capturers_lock_debug.ProcessLocksList, &DSOUND_capturers_lock_debug.ProcessLocksList },
84       0, 0, { (DWORD_PTR)(__FILE__ ": DSOUND_capturers_lock") }
85 };
86 CRITICAL_SECTION DSOUND_capturers_lock = { &DSOUND_capturers_lock_debug, -1, 0, 0, 0, 0 };
87
88 GUID                    DSOUND_renderer_guids[MAXWAVEDRIVERS];
89 GUID                    DSOUND_capture_guids[MAXWAVEDRIVERS];
90
91 static IMMDeviceEnumerator *g_devenum;
92 static CRITICAL_SECTION g_devenum_lock;
93 static CRITICAL_SECTION_DEBUG g_devenum_lock_debug =
94 {
95     0, 0, &g_devenum_lock,
96     { &g_devenum_lock_debug.ProcessLocksList, &g_devenum_lock_debug.ProcessLocksList },
97       0, 0, { (DWORD_PTR)(__FILE__ ": g_devenum_lock") }
98 };
99 static CRITICAL_SECTION g_devenum_lock = { &g_devenum_lock_debug, -1, 0, 0, 0, 0 };
100 static HANDLE g_devenum_thread;
101
102 WCHAR wine_vxd_drv[] = { 'w','i','n','e','m','m','.','v','x','d', 0 };
103
104 /* All default settings, you most likely don't want to touch these, see wiki on UsefulRegistryKeys */
105 int ds_hel_buflen = 32768 * 2;
106 int ds_snd_queue_max = 10;
107 int ds_snd_shadow_maxsize = 2;
108 int ds_default_sample_rate = 44100;
109 int ds_default_bits_per_sample = 16;
110 static HINSTANCE instance;
111
112 /*
113  * Get a config key from either the app-specific or the default config
114  */
115
116 static inline DWORD get_config_key( HKEY defkey, HKEY appkey, const char *name,
117                                     char *buffer, DWORD size )
118 {
119     if (appkey && !RegQueryValueExA( appkey, name, 0, NULL, (LPBYTE)buffer, &size )) return 0;
120     if (defkey && !RegQueryValueExA( defkey, name, 0, NULL, (LPBYTE)buffer, &size )) return 0;
121     return ERROR_FILE_NOT_FOUND;
122 }
123
124
125 /*
126  * Setup the dsound options.
127  */
128
129 void setup_dsound_options(void)
130 {
131     char buffer[MAX_PATH+16];
132     HKEY hkey, appkey = 0;
133     DWORD len;
134
135     buffer[MAX_PATH]='\0';
136
137     /* @@ Wine registry key: HKCU\Software\Wine\DirectSound */
138     if (RegOpenKeyA( HKEY_CURRENT_USER, "Software\\Wine\\DirectSound", &hkey )) hkey = 0;
139
140     len = GetModuleFileNameA( 0, buffer, MAX_PATH );
141     if (len && len < MAX_PATH)
142     {
143         HKEY tmpkey;
144         /* @@ Wine registry key: HKCU\Software\Wine\AppDefaults\app.exe\DirectSound */
145         if (!RegOpenKeyA( HKEY_CURRENT_USER, "Software\\Wine\\AppDefaults", &tmpkey ))
146         {
147             char *p, *appname = buffer;
148             if ((p = strrchr( appname, '/' ))) appname = p + 1;
149             if ((p = strrchr( appname, '\\' ))) appname = p + 1;
150             strcat( appname, "\\DirectSound" );
151             TRACE("appname = [%s]\n", appname);
152             if (RegOpenKeyA( tmpkey, appname, &appkey )) appkey = 0;
153             RegCloseKey( tmpkey );
154         }
155     }
156
157     /* get options */
158
159     if (!get_config_key( hkey, appkey, "HelBuflen", buffer, MAX_PATH ))
160         ds_hel_buflen = atoi(buffer);
161
162     if (!get_config_key( hkey, appkey, "SndQueueMax", buffer, MAX_PATH ))
163         ds_snd_queue_max = atoi(buffer);
164
165     if (!get_config_key( hkey, appkey, "MaxShadowSize", buffer, MAX_PATH ))
166         ds_snd_shadow_maxsize = atoi(buffer);
167
168     if (!get_config_key( hkey, appkey, "DefaultSampleRate", buffer, MAX_PATH ))
169         ds_default_sample_rate = atoi(buffer);
170
171     if (!get_config_key( hkey, appkey, "DefaultBitsPerSample", buffer, MAX_PATH ))
172         ds_default_bits_per_sample = atoi(buffer);
173
174     if (appkey) RegCloseKey( appkey );
175     if (hkey) RegCloseKey( hkey );
176
177     TRACE("ds_hel_buflen = %d\n", ds_hel_buflen);
178     TRACE("ds_snd_queue_max = %d\n", ds_snd_queue_max);
179     TRACE("ds_default_sample_rate = %d\n", ds_default_sample_rate);
180     TRACE("ds_default_bits_per_sample = %d\n", ds_default_bits_per_sample);
181     TRACE("ds_snd_shadow_maxsize = %d\n", ds_snd_shadow_maxsize);
182 }
183
184 static const char * get_device_id(LPCGUID pGuid)
185 {
186     if (IsEqualGUID(&DSDEVID_DefaultPlayback, pGuid))
187         return "DSDEVID_DefaultPlayback";
188     else if (IsEqualGUID(&DSDEVID_DefaultVoicePlayback, pGuid))
189         return "DSDEVID_DefaultVoicePlayback";
190     else if (IsEqualGUID(&DSDEVID_DefaultCapture, pGuid))
191         return "DSDEVID_DefaultCapture";
192     else if (IsEqualGUID(&DSDEVID_DefaultVoiceCapture, pGuid))
193         return "DSDEVID_DefaultVoiceCapture";
194     return debugstr_guid(pGuid);
195 }
196
197 /* The MMDeviceEnumerator object has to be created & destroyed
198  * from the same thread. */
199 static DWORD WINAPI devenum_thread_proc(void *arg)
200 {
201     HANDLE evt = arg;
202     HRESULT hr;
203     MSG msg;
204
205     hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
206     if(FAILED(hr)){
207          ERR("CoInitializeEx failed: %08x\n", hr);
208          return 1;
209     }
210
211     hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL,
212             CLSCTX_INPROC_SERVER, &IID_IMMDeviceEnumerator, (void**)&g_devenum);
213     if(FAILED(hr)){
214         ERR("CoCreateInstance failed: %08x\n", hr);
215         CoUninitialize();
216         return 1;
217     }
218
219     SetEvent(evt);
220
221     PeekMessageW(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE);
222
223     while(GetMessageW(&msg, NULL, 0, 0)){
224         if(msg.hwnd)
225             DispatchMessageW(&msg);
226         else
227             ERR("Unknown message: %04x\n", msg.message);
228     }
229
230     IMMDeviceEnumerator_Release(g_devenum);
231     g_devenum = NULL;
232     CoUninitialize();
233
234     return 0;
235 }
236
237 static IMMDeviceEnumerator *get_mmdevenum(void)
238 {
239     HANDLE events[2];
240     DWORD wait;
241
242     EnterCriticalSection(&g_devenum_lock);
243
244     if(g_devenum){
245         LeaveCriticalSection(&g_devenum_lock);
246         return g_devenum;
247     }
248
249     events[0] = CreateEventW(NULL, FALSE, FALSE, NULL);
250
251     g_devenum_thread = CreateThread(NULL, 0, devenum_thread_proc,
252             events[0], 0, NULL);
253     if(!g_devenum_thread){
254         LeaveCriticalSection(&g_devenum_lock);
255         CloseHandle(events[0]);
256         return NULL;
257     }
258
259     events[1] = g_devenum_thread;
260     wait = WaitForMultipleObjects(2, events, FALSE, INFINITE);
261     CloseHandle(events[0]);
262     if(wait != WAIT_OBJECT_0){
263         if(wait == 1 + WAIT_OBJECT_0){
264             CloseHandle(g_devenum_thread);
265             g_devenum_thread = NULL;
266         }
267         LeaveCriticalSection(&g_devenum_lock);
268         return NULL;
269     }
270
271     LeaveCriticalSection(&g_devenum_lock);
272
273     return g_devenum;
274 }
275
276 static HRESULT get_mmdevice_guid(IMMDevice *device, IPropertyStore *ps,
277         GUID *guid)
278 {
279     PROPVARIANT pv;
280     HRESULT hr;
281
282     if(!ps){
283         hr = IMMDevice_OpenPropertyStore(device, STGM_READ, &ps);
284         if(FAILED(hr)){
285             WARN("OpenPropertyStore failed: %08x\n", hr);
286             return hr;
287         }
288     }else
289         IPropertyStore_AddRef(ps);
290
291     PropVariantInit(&pv);
292
293     hr = IPropertyStore_GetValue(ps, &PKEY_AudioEndpoint_GUID, &pv);
294     if(FAILED(hr)){
295         IPropertyStore_Release(ps);
296         WARN("GetValue(GUID) failed: %08x\n", hr);
297         return hr;
298     }
299
300     CLSIDFromString(pv.u.pwszVal, guid);
301
302     PropVariantClear(&pv);
303     IPropertyStore_Release(ps);
304
305     return S_OK;
306 }
307
308 /***************************************************************************
309  * GetDeviceID  [DSOUND.9]
310  *
311  * Retrieves unique identifier of default device specified
312  *
313  * PARAMS
314  *    pGuidSrc  [I] Address of device GUID.
315  *    pGuidDest [O] Address to receive unique device GUID.
316  *
317  * RETURNS
318  *    Success: DS_OK
319  *    Failure: DSERR_INVALIDPARAM
320  *
321  * NOTES
322  *    pGuidSrc is a valid device GUID or DSDEVID_DefaultPlayback,
323  *    DSDEVID_DefaultCapture, DSDEVID_DefaultVoicePlayback, or
324  *    DSDEVID_DefaultVoiceCapture.
325  *    Returns pGuidSrc if pGuidSrc is a valid device or the device
326  *    GUID for the specified constants.
327  */
328 HRESULT WINAPI GetDeviceID(LPCGUID pGuidSrc, LPGUID pGuidDest)
329 {
330     IMMDeviceEnumerator *devenum;
331     EDataFlow flow = (EDataFlow)-1;
332     ERole role = (ERole)-1;
333     HRESULT hr;
334
335     TRACE("(%s,%p)\n", get_device_id(pGuidSrc),pGuidDest);
336
337     if(!pGuidSrc || !pGuidDest)
338         return DSERR_INVALIDPARAM;
339
340     devenum = get_mmdevenum();
341     if(!devenum)
342         return DSERR_GENERIC;
343
344     if(IsEqualGUID(&DSDEVID_DefaultPlayback, pGuidSrc)){
345         role = eMultimedia;
346         flow = eRender;
347     }else if(IsEqualGUID(&DSDEVID_DefaultVoicePlayback, pGuidSrc)){
348         role = eCommunications;
349         flow = eRender;
350     }else if(IsEqualGUID(&DSDEVID_DefaultCapture, pGuidSrc)){
351         role = eMultimedia;
352         flow = eCapture;
353     }else if(IsEqualGUID(&DSDEVID_DefaultVoiceCapture, pGuidSrc)){
354         role = eCommunications;
355         flow = eCapture;
356     }
357
358     if(role != (ERole)-1 && flow != (EDataFlow)-1){
359         IMMDevice *device;
360
361         hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(devenum,
362                 flow, role, &device);
363         if(FAILED(hr)){
364             WARN("GetDefaultAudioEndpoint failed: %08x\n", hr);
365             return DSERR_NODRIVER;
366         }
367
368         hr = get_mmdevice_guid(device, NULL, pGuidDest);
369         IMMDevice_Release(device);
370
371         return (hr == S_OK) ? DS_OK : hr;
372     }
373
374     *pGuidDest = *pGuidSrc;
375
376     return DS_OK;
377 }
378
379 struct morecontext
380 {
381     LPDSENUMCALLBACKA callA;
382     LPVOID data;
383 };
384
385 static BOOL CALLBACK a_to_w_callback(LPGUID guid, LPCWSTR descW, LPCWSTR modW, LPVOID data)
386 {
387     struct morecontext *context = data;
388     char descA[MAXPNAMELEN], modA[MAXPNAMELEN];
389
390     WideCharToMultiByte(CP_ACP, 0, descW, -1, descA, sizeof(descA), NULL, NULL);
391     WideCharToMultiByte(CP_ACP, 0, modW, -1, modA, sizeof(modA), NULL, NULL);
392
393     return context->callA(guid, descA, modA, context->data);
394 }
395
396 /***************************************************************************
397  * DirectSoundEnumerateA [DSOUND.2]
398  *
399  * Enumerate all DirectSound drivers installed in the system
400  *
401  * PARAMS
402  *    lpDSEnumCallback  [I] Address of callback function.
403  *    lpContext         [I] Address of user defined context passed to callback function.
404  *
405  * RETURNS
406  *    Success: DS_OK
407  *    Failure: DSERR_INVALIDPARAM
408  */
409 HRESULT WINAPI DirectSoundEnumerateA(
410     LPDSENUMCALLBACKA lpDSEnumCallback,
411     LPVOID lpContext)
412 {
413     struct morecontext context;
414
415     if (lpDSEnumCallback == NULL) {
416         WARN("invalid parameter: lpDSEnumCallback == NULL\n");
417         return DSERR_INVALIDPARAM;
418     }
419
420     context.callA = lpDSEnumCallback;
421     context.data = lpContext;
422
423     return DirectSoundEnumerateW(a_to_w_callback, &context);
424 }
425
426 HRESULT get_mmdevice(EDataFlow flow, const GUID *tgt, IMMDevice **device)
427 {
428     IMMDeviceEnumerator *devenum;
429     IMMDeviceCollection *coll;
430     UINT count, i;
431     HRESULT hr;
432
433     devenum = get_mmdevenum();
434     if(!devenum)
435         return DSERR_GENERIC;
436
437     hr = IMMDeviceEnumerator_EnumAudioEndpoints(devenum, flow,
438             DEVICE_STATE_ACTIVE, &coll);
439     if(FAILED(hr)){
440         WARN("EnumAudioEndpoints failed: %08x\n", hr);
441         return hr;
442     }
443
444     hr = IMMDeviceCollection_GetCount(coll, &count);
445     if(FAILED(hr)){
446         IMMDeviceCollection_Release(coll);
447         WARN("GetCount failed: %08x\n", hr);
448         return hr;
449     }
450
451     for(i = 0; i < count; ++i){
452         GUID guid;
453
454         hr = IMMDeviceCollection_Item(coll, i, device);
455         if(FAILED(hr))
456             continue;
457
458         hr = get_mmdevice_guid(*device, NULL, &guid);
459         if(FAILED(hr)){
460             IMMDevice_Release(*device);
461             continue;
462         }
463
464         if(IsEqualGUID(&guid, tgt))
465             return DS_OK;
466
467         IMMDevice_Release(*device);
468     }
469
470     WARN("No device with GUID %s found!\n", wine_dbgstr_guid(tgt));
471
472     return DSERR_INVALIDPARAM;
473 }
474
475 static BOOL send_device(IMMDevice *device, GUID *guid,
476         LPDSENUMCALLBACKW cb, void *user)
477 {
478     IPropertyStore *ps;
479     PROPVARIANT pv;
480     BOOL keep_going;
481     HRESULT hr;
482
483     PropVariantInit(&pv);
484
485     hr = IMMDevice_OpenPropertyStore(device, STGM_READ, &ps);
486     if(FAILED(hr)){
487         WARN("OpenPropertyStore failed: %08x\n", hr);
488         return TRUE;
489     }
490
491     hr = get_mmdevice_guid(device, ps, guid);
492     if(FAILED(hr)){
493         IPropertyStore_Release(ps);
494         return TRUE;
495     }
496
497     hr = IPropertyStore_GetValue(ps,
498             (const PROPERTYKEY *)&DEVPKEY_Device_FriendlyName, &pv);
499     if(FAILED(hr)){
500         IPropertyStore_Release(ps);
501         WARN("GetValue(FriendlyName) failed: %08x\n", hr);
502         return TRUE;
503     }
504
505     TRACE("Calling back with %s (%s)\n", wine_dbgstr_guid(guid),
506             wine_dbgstr_w(pv.u.pwszVal));
507
508     keep_going = cb(guid, pv.u.pwszVal, wine_vxd_drv, user);
509
510     PropVariantClear(&pv);
511     IPropertyStore_Release(ps);
512
513     return keep_going;
514 }
515
516 /* S_FALSE means the callback returned FALSE at some point
517  * S_OK means the callback always returned TRUE */
518 HRESULT enumerate_mmdevices(EDataFlow flow, GUID *guids,
519         LPDSENUMCALLBACKW cb, void *user)
520 {
521     IMMDeviceEnumerator *devenum;
522     IMMDeviceCollection *coll;
523     IMMDevice *defdev = NULL;
524     UINT count, i, n;
525     BOOL keep_going;
526     HRESULT hr;
527
528     static const WCHAR primary_desc[] = {'P','r','i','m','a','r','y',' ',
529         'S','o','u','n','d',' ','D','r','i','v','e','r',0};
530     static const WCHAR empty_drv[] = {0};
531
532     devenum = get_mmdevenum();
533     if(!devenum)
534         return DS_OK;
535
536     hr = IMMDeviceEnumerator_EnumAudioEndpoints(g_devenum, flow,
537             DEVICE_STATE_ACTIVE, &coll);
538     if(FAILED(hr)){
539         WARN("EnumAudioEndpoints failed: %08x\n", hr);
540         return DS_OK;
541     }
542
543     hr = IMMDeviceCollection_GetCount(coll, &count);
544     if(FAILED(hr)){
545         IMMDeviceCollection_Release(coll);
546         WARN("GetCount failed: %08x\n", hr);
547         return DS_OK;
548     }
549
550     if(count == 0)
551         return DS_OK;
552
553     TRACE("Calling back with NULL (%s)\n", wine_dbgstr_w(primary_desc));
554     keep_going = cb(NULL, primary_desc, empty_drv, user);
555
556     /* always send the default device first */
557     if(keep_going){
558         hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(devenum, flow,
559                 eMultimedia, &defdev);
560         if(FAILED(hr)){
561             defdev = NULL;
562             n = 0;
563         }else{
564             keep_going = send_device(defdev, &guids[0], cb, user);
565             n = 1;
566         }
567     }
568
569     for(i = 0; keep_going && i < count; ++i){
570         IMMDevice *device;
571
572         hr = IMMDeviceCollection_Item(coll, i, &device);
573         if(FAILED(hr)){
574             WARN("Item failed: %08x\n", hr);
575             continue;
576         }
577
578         if(device != defdev){
579             send_device(device, &guids[n], cb, user);
580             ++n;
581         }
582
583         IMMDevice_Release(device);
584     }
585
586     if(defdev)
587         IMMDevice_Release(defdev);
588     IMMDeviceCollection_Release(coll);
589
590     return (keep_going == TRUE) ? S_OK : S_FALSE;
591 }
592
593 /***************************************************************************
594  * DirectSoundEnumerateW [DSOUND.3]
595  *
596  * Enumerate all DirectSound drivers installed in the system
597  *
598  * PARAMS
599  *    lpDSEnumCallback  [I] Address of callback function.
600  *    lpContext         [I] Address of user defined context passed to callback function.
601  *
602  * RETURNS
603  *    Success: DS_OK
604  *    Failure: DSERR_INVALIDPARAM
605  */
606 HRESULT WINAPI DirectSoundEnumerateW(
607         LPDSENUMCALLBACKW lpDSEnumCallback,
608         LPVOID lpContext )
609 {
610     HRESULT hr;
611
612     TRACE("(%p,%p)\n", lpDSEnumCallback, lpContext);
613
614     if (lpDSEnumCallback == NULL) {
615         WARN("invalid parameter: lpDSEnumCallback == NULL\n");
616         return DSERR_INVALIDPARAM;
617     }
618
619     setup_dsound_options();
620
621     hr = enumerate_mmdevices(eRender, DSOUND_renderer_guids,
622             lpDSEnumCallback, lpContext);
623     return SUCCEEDED(hr) ? DS_OK : hr;
624 }
625
626 /***************************************************************************
627  * DirectSoundCaptureEnumerateA [DSOUND.7]
628  *
629  * Enumerate all DirectSound drivers installed in the system.
630  *
631  * PARAMS
632  *    lpDSEnumCallback  [I] Address of callback function.
633  *    lpContext         [I] Address of user defined context passed to callback function.
634  *
635  * RETURNS
636  *    Success: DS_OK
637  *    Failure: DSERR_INVALIDPARAM
638  */
639 HRESULT WINAPI DirectSoundCaptureEnumerateA(
640     LPDSENUMCALLBACKA lpDSEnumCallback,
641     LPVOID lpContext)
642 {
643     struct morecontext context;
644
645     if (lpDSEnumCallback == NULL) {
646         WARN("invalid parameter: lpDSEnumCallback == NULL\n");
647         return DSERR_INVALIDPARAM;
648     }
649
650     context.callA = lpDSEnumCallback;
651     context.data = lpContext;
652
653     return DirectSoundCaptureEnumerateW(a_to_w_callback, &context);
654 }
655
656 /***************************************************************************
657  * DirectSoundCaptureEnumerateW [DSOUND.8]
658  *
659  * Enumerate all DirectSound drivers installed in the system.
660  *
661  * PARAMS
662  *    lpDSEnumCallback  [I] Address of callback function.
663  *    lpContext         [I] Address of user defined context passed to callback function.
664  *
665  * RETURNS
666  *    Success: DS_OK
667  *    Failure: DSERR_INVALIDPARAM
668  */
669 HRESULT WINAPI
670 DirectSoundCaptureEnumerateW(
671     LPDSENUMCALLBACKW lpDSEnumCallback,
672     LPVOID lpContext)
673 {
674     HRESULT hr;
675
676     TRACE("(%p,%p)\n", lpDSEnumCallback, lpContext );
677
678     if (lpDSEnumCallback == NULL) {
679         WARN("invalid parameter: lpDSEnumCallback == NULL\n");
680         return DSERR_INVALIDPARAM;
681     }
682
683     setup_dsound_options();
684
685     hr = enumerate_mmdevices(eCapture, DSOUND_capture_guids,
686             lpDSEnumCallback, lpContext);
687     return SUCCEEDED(hr) ? DS_OK : hr;
688 }
689
690 /*******************************************************************************
691  * DirectSound ClassFactory
692  */
693
694 typedef  HRESULT (*FnCreateInstance)(REFIID riid, LPVOID *ppobj);
695  
696 typedef struct {
697     IClassFactory IClassFactory_iface;
698     REFCLSID rclsid;
699     FnCreateInstance pfnCreateInstance;
700 } IClassFactoryImpl;
701
702 static inline IClassFactoryImpl *impl_from_IClassFactory(IClassFactory *iface)
703 {
704     return CONTAINING_RECORD(iface, IClassFactoryImpl, IClassFactory_iface);
705 }
706
707 static HRESULT WINAPI
708 DSCF_QueryInterface(LPCLASSFACTORY iface, REFIID riid, LPVOID *ppobj)
709 {
710     IClassFactoryImpl *This = impl_from_IClassFactory(iface);
711     TRACE("(%p, %s, %p)\n", This, debugstr_guid(riid), ppobj);
712     if (ppobj == NULL)
713         return E_POINTER;
714     if (IsEqualIID(riid, &IID_IUnknown) ||
715         IsEqualIID(riid, &IID_IClassFactory))
716     {
717         *ppobj = iface;
718         IUnknown_AddRef(iface);
719         return S_OK;
720     }
721     *ppobj = NULL;
722     return E_NOINTERFACE;
723 }
724
725 static ULONG WINAPI DSCF_AddRef(LPCLASSFACTORY iface)
726 {
727     return 2;
728 }
729
730 static ULONG WINAPI DSCF_Release(LPCLASSFACTORY iface)
731 {
732     /* static class, won't be freed */
733     return 1;
734 }
735
736 static HRESULT WINAPI DSCF_CreateInstance(
737     LPCLASSFACTORY iface,
738     LPUNKNOWN pOuter,
739     REFIID riid,
740     LPVOID *ppobj)
741 {
742     IClassFactoryImpl *This = impl_from_IClassFactory(iface);
743     TRACE("(%p, %p, %s, %p)\n", This, pOuter, debugstr_guid(riid), ppobj);
744
745     if (pOuter)
746         return CLASS_E_NOAGGREGATION;
747
748     if (ppobj == NULL) {
749         WARN("invalid parameter\n");
750         return DSERR_INVALIDPARAM;
751     }
752     *ppobj = NULL;
753     return This->pfnCreateInstance(riid, ppobj);
754 }
755  
756 static HRESULT WINAPI DSCF_LockServer(LPCLASSFACTORY iface, BOOL dolock)
757 {
758     IClassFactoryImpl *This = impl_from_IClassFactory(iface);
759     FIXME("(%p, %d) stub!\n", This, dolock);
760     return S_OK;
761 }
762
763 static const IClassFactoryVtbl DSCF_Vtbl = {
764     DSCF_QueryInterface,
765     DSCF_AddRef,
766     DSCF_Release,
767     DSCF_CreateInstance,
768     DSCF_LockServer
769 };
770
771 static IClassFactoryImpl DSOUND_CF[] = {
772     { { &DSCF_Vtbl }, &CLSID_DirectSound, (FnCreateInstance)DSOUND_Create },
773     { { &DSCF_Vtbl }, &CLSID_DirectSound8, (FnCreateInstance)DSOUND_Create8 },
774     { { &DSCF_Vtbl }, &CLSID_DirectSoundCapture, (FnCreateInstance)DSOUND_CaptureCreate },
775     { { &DSCF_Vtbl }, &CLSID_DirectSoundCapture8, (FnCreateInstance)DSOUND_CaptureCreate8 },
776     { { &DSCF_Vtbl }, &CLSID_DirectSoundFullDuplex, (FnCreateInstance)DSOUND_FullDuplexCreate },
777     { { &DSCF_Vtbl }, &CLSID_DirectSoundPrivate, (FnCreateInstance)IKsPrivatePropertySetImpl_Create },
778     { { NULL }, NULL, NULL }
779 };
780
781 /*******************************************************************************
782  * DllGetClassObject [DSOUND.@]
783  * Retrieves class object from a DLL object
784  *
785  * NOTES
786  *    Docs say returns STDAPI
787  *
788  * PARAMS
789  *    rclsid [I] CLSID for the class object
790  *    riid   [I] Reference to identifier of interface for class object
791  *    ppv    [O] Address of variable to receive interface pointer for riid
792  *
793  * RETURNS
794  *    Success: S_OK
795  *    Failure: CLASS_E_CLASSNOTAVAILABLE, E_OUTOFMEMORY, E_INVALIDARG,
796  *             E_UNEXPECTED
797  */
798 HRESULT WINAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID *ppv)
799 {
800     int i = 0;
801     TRACE("(%s, %s, %p)\n", debugstr_guid(rclsid), debugstr_guid(riid), ppv);
802
803     if (ppv == NULL) {
804         WARN("invalid parameter\n");
805         return E_INVALIDARG;
806     }
807
808     *ppv = NULL;
809
810     if (!IsEqualIID(riid, &IID_IClassFactory) &&
811         !IsEqualIID(riid, &IID_IUnknown)) {
812         WARN("no interface for %s\n", debugstr_guid(riid));
813         return E_NOINTERFACE;
814     }
815
816     while (NULL != DSOUND_CF[i].rclsid) {
817         if (IsEqualGUID(rclsid, DSOUND_CF[i].rclsid)) {
818             DSCF_AddRef(&DSOUND_CF[i].IClassFactory_iface);
819             *ppv = &DSOUND_CF[i];
820             return S_OK;
821         }
822         i++;
823     }
824
825     WARN("(%s, %s, %p): no class found.\n", debugstr_guid(rclsid),
826          debugstr_guid(riid), ppv);
827     return CLASS_E_CLASSNOTAVAILABLE;
828 }
829
830
831 /*******************************************************************************
832  * DllCanUnloadNow [DSOUND.4]
833  * Determines whether the DLL is in use.
834  *
835  * RETURNS
836  *    Can unload now: S_OK
837  *    Cannot unload now (the DLL is still active): S_FALSE
838  */
839 HRESULT WINAPI DllCanUnloadNow(void)
840 {
841     return S_FALSE;
842 }
843
844 #define INIT_GUID(guid, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8)      \
845         guid.Data1 = l; guid.Data2 = w1; guid.Data3 = w2;               \
846         guid.Data4[0] = b1; guid.Data4[1] = b2; guid.Data4[2] = b3;     \
847         guid.Data4[3] = b4; guid.Data4[4] = b5; guid.Data4[5] = b6;     \
848         guid.Data4[6] = b7; guid.Data4[7] = b8;
849
850 /***********************************************************************
851  *           DllMain (DSOUND.init)
852  */
853 BOOL WINAPI DllMain(HINSTANCE hInstDLL, DWORD fdwReason, LPVOID lpvReserved)
854 {
855     TRACE("(%p %d %p)\n", hInstDLL, fdwReason, lpvReserved);
856
857     switch (fdwReason) {
858     case DLL_PROCESS_ATTACH:
859         TRACE("DLL_PROCESS_ATTACH\n");
860         instance = hInstDLL;
861         DisableThreadLibraryCalls(hInstDLL);
862         /* Increase refcount on dsound by 1 */
863         GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (LPCWSTR)hInstDLL, &hInstDLL);
864         break;
865     case DLL_PROCESS_DETACH:
866         TRACE("DLL_PROCESS_DETACH\n");
867         DeleteCriticalSection(&DSOUND_renderers_lock);
868         DeleteCriticalSection(&DSOUND_capturers_lock);
869         DeleteCriticalSection(&g_devenum_lock);
870         break;
871     default:
872         TRACE("UNKNOWN REASON\n");
873         break;
874     }
875     return TRUE;
876 }
877
878 /***********************************************************************
879  *              DllRegisterServer (DSOUND.@)
880  */
881 HRESULT WINAPI DllRegisterServer(void)
882 {
883     return __wine_register_resources( instance );
884 }
885
886 /***********************************************************************
887  *              DllUnregisterServer (DSOUND.@)
888  */
889 HRESULT WINAPI DllUnregisterServer(void)
890 {
891     return __wine_unregister_resources( instance );
892 }