2 * Copyright 2009 Maarten Lankhorst
3 * Copyright 2011 Andrew Eikum for CodeWeavers
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
25 #define WIN32_NO_STATUS
34 #include "propkeydef.h"
35 #include "mmdeviceapi.h"
38 #include "audioclient.h"
39 #include "endpointvolume.h"
40 #include "audiopolicy.h"
43 #include "spatialaudioclient.h"
45 #include "mmdevapi_private.h"
46 #include "wine/debug.h"
48 WINE_DEFAULT_DEBUG_CHANNEL(mmdevapi
);
52 const WCHAR drv_keyW
[] = L
"Software\\Wine\\Drivers";
54 static const char *get_priority_string(int prio
)
57 case Priority_Unavailable
:
61 case Priority_Neutral
:
63 case Priority_Preferred
:
69 static BOOL
load_driver(const WCHAR
*name
, DriverFuncs
*driver
)
72 WCHAR driver_module
[264], path
[MAX_PATH
];
73 struct test_connect_params params
;
75 lstrcpyW(driver_module
, L
"wine");
76 lstrcatW(driver_module
, name
);
77 lstrcatW(driver_module
, L
".drv");
79 TRACE("Attempting to load %s\n", wine_dbgstr_w(driver_module
));
81 driver
->module
= LoadLibraryW(driver_module
);
83 TRACE("Unable to load %s: %lu\n", wine_dbgstr_w(driver_module
),
88 if ((status
= NtQueryVirtualMemory(GetCurrentProcess(), driver
->module
, MemoryWineUnixFuncs
,
89 &driver
->module_unixlib
, sizeof(driver
->module_unixlib
), NULL
))) {
90 ERR("Unable to load UNIX functions: %lx\n", status
);
94 if ((status
= __wine_unix_call(driver
->module_unixlib
, process_attach
, NULL
))) {
95 ERR("Unable to initialize library: %lx\n", status
);
99 #define LDFC(n) do { driver->p##n = (void*)GetProcAddress(driver->module, #n);\
100 if(!driver->p##n) { goto fail; } } while(0)
101 LDFC(get_device_guid
);
102 LDFC(get_device_name_from_guid
);
105 GetModuleFileNameW(NULL
, path
, ARRAY_SIZE(path
));
106 params
.name
= wcsrchr(path
, '\\');
107 params
.name
= params
.name
? params
.name
+ 1 : path
;
108 params
.priority
= Priority_Neutral
;
110 if ((status
= __wine_unix_call(driver
->module_unixlib
, test_connect
, ¶ms
))) {
111 ERR("Unable to retrieve driver priority: %lx\n", status
);
115 driver
->priority
= params
.priority
;
117 lstrcpyW(driver
->module_name
, driver_module
);
119 TRACE("Successfully loaded %s with priority %s\n",
120 wine_dbgstr_w(driver_module
), get_priority_string(driver
->priority
));
124 FreeLibrary(driver
->module
);
128 static BOOL WINAPI
init_driver(INIT_ONCE
*once
, void *param
, void **context
)
130 static WCHAR default_list
[] = L
"pulse,alsa,oss,coreaudio";
133 WCHAR reg_list
[256], *p
, *next
, *driver_list
= default_list
;
135 if(RegOpenKeyW(HKEY_CURRENT_USER
, drv_keyW
, &key
) == ERROR_SUCCESS
){
136 DWORD size
= sizeof(reg_list
);
138 if(RegQueryValueExW(key
, L
"Audio", 0, NULL
, (BYTE
*)reg_list
, &size
) == ERROR_SUCCESS
){
139 if(reg_list
[0] == '\0'){
140 TRACE("User explicitly chose no driver\n");
145 driver_list
= reg_list
;
151 TRACE("Loading driver list %s\n", wine_dbgstr_w(driver_list
));
152 for(next
= p
= driver_list
; next
; p
= next
+ 1){
153 next
= wcschr(p
, ',');
157 driver
.priority
= Priority_Unavailable
;
158 if(load_driver(p
, &driver
)){
159 if(driver
.priority
== Priority_Unavailable
)
160 FreeLibrary(driver
.module
);
161 else if(!drvs
.module
|| driver
.priority
> drvs
.priority
){
162 TRACE("Selecting driver %s with priority %s\n",
163 wine_dbgstr_w(p
), get_priority_string(driver
.priority
));
165 FreeLibrary(drvs
.module
);
168 FreeLibrary(driver
.module
);
170 TRACE("Failed to load driver %s\n", wine_dbgstr_w(p
));
176 if (drvs
.module
!= 0){
177 load_devices_from_reg();
178 load_driver_devices(eRender
);
179 load_driver_devices(eCapture
);
182 return drvs
.module
!= 0;
185 BOOL WINAPI
DllMain(HINSTANCE hinstDLL
, DWORD fdwReason
, LPVOID lpvReserved
)
187 TRACE("(0x%p, %ld, %p)\n", hinstDLL
, fdwReason
, lpvReserved
);
191 case DLL_PROCESS_ATTACH
:
192 DisableThreadLibraryCalls(hinstDLL
);
194 case DLL_PROCESS_DETACH
:
195 if (drvs
.module_unixlib
) {
196 const NTSTATUS status
= __wine_unix_call(drvs
.module_unixlib
, process_detach
, NULL
);
198 WARN("Unable to deinitialize library: %lx\n", status
);
211 typedef HRESULT (*FnCreateInstance
)(REFIID riid
, LPVOID
*ppobj
);
214 IClassFactory IClassFactory_iface
;
216 FnCreateInstance pfnCreateInstance
;
219 static inline IClassFactoryImpl
*impl_from_IClassFactory(IClassFactory
*iface
)
221 return CONTAINING_RECORD(iface
, IClassFactoryImpl
, IClassFactory_iface
);
224 static HRESULT WINAPI
225 MMCF_QueryInterface(IClassFactory
*iface
, REFIID riid
, void **ppobj
)
227 IClassFactoryImpl
*This
= impl_from_IClassFactory(iface
);
228 TRACE("(%p, %s, %p)\n", This
, debugstr_guid(riid
), ppobj
);
231 if (IsEqualIID(riid
, &IID_IUnknown
) ||
232 IsEqualIID(riid
, &IID_IClassFactory
))
235 IClassFactory_AddRef(iface
);
239 return E_NOINTERFACE
;
242 static ULONG WINAPI
MMCF_AddRef(LPCLASSFACTORY iface
)
247 static ULONG WINAPI
MMCF_Release(LPCLASSFACTORY iface
)
249 /* static class, won't be freed */
253 static HRESULT WINAPI
MMCF_CreateInstance(
254 LPCLASSFACTORY iface
,
259 IClassFactoryImpl
*This
= impl_from_IClassFactory(iface
);
260 TRACE("(%p, %p, %s, %p)\n", This
, pOuter
, debugstr_guid(riid
), ppobj
);
263 return CLASS_E_NOAGGREGATION
;
266 WARN("invalid parameter\n");
270 return This
->pfnCreateInstance(riid
, ppobj
);
273 static HRESULT WINAPI
MMCF_LockServer(LPCLASSFACTORY iface
, BOOL dolock
)
275 IClassFactoryImpl
*This
= impl_from_IClassFactory(iface
);
276 FIXME("(%p, %d) stub!\n", This
, dolock
);
280 static const IClassFactoryVtbl MMCF_Vtbl
= {
288 static IClassFactoryImpl MMDEVAPI_CF
[] = {
289 { { &MMCF_Vtbl
}, &CLSID_MMDeviceEnumerator
, (FnCreateInstance
)MMDevEnum_Create
}
292 HRESULT WINAPI
DllGetClassObject(REFCLSID rclsid
, REFIID riid
, LPVOID
*ppv
)
294 static INIT_ONCE init_once
= INIT_ONCE_STATIC_INIT
;
296 TRACE("(%s, %s, %p)\n", debugstr_guid(rclsid
), debugstr_guid(riid
), ppv
);
298 if(!InitOnceExecuteOnce(&init_once
, init_driver
, NULL
, NULL
)) {
299 ERR("Driver initialization failed\n");
304 WARN("invalid parameter\n");
310 if (!IsEqualIID(riid
, &IID_IClassFactory
) &&
311 !IsEqualIID(riid
, &IID_IUnknown
)) {
312 WARN("no interface for %s\n", debugstr_guid(riid
));
313 return E_NOINTERFACE
;
316 for (i
= 0; i
< ARRAY_SIZE(MMDEVAPI_CF
); ++i
)
318 if (IsEqualGUID(rclsid
, MMDEVAPI_CF
[i
].rclsid
)) {
319 IClassFactory_AddRef(&MMDEVAPI_CF
[i
].IClassFactory_iface
);
320 *ppv
= &MMDEVAPI_CF
[i
];
325 WARN("(%s, %s, %p): no class found.\n", debugstr_guid(rclsid
),
326 debugstr_guid(riid
), ppv
);
327 return CLASS_E_CLASSNOTAVAILABLE
;
330 struct activate_async_op
{
331 IActivateAudioInterfaceAsyncOperation IActivateAudioInterfaceAsyncOperation_iface
;
334 IActivateAudioInterfaceCompletionHandler
*callback
;
336 IUnknown
*result_iface
;
339 static struct activate_async_op
*impl_from_IActivateAudioInterfaceAsyncOperation(IActivateAudioInterfaceAsyncOperation
*iface
)
341 return CONTAINING_RECORD(iface
, struct activate_async_op
, IActivateAudioInterfaceAsyncOperation_iface
);
344 static HRESULT WINAPI
activate_async_op_QueryInterface(IActivateAudioInterfaceAsyncOperation
*iface
,
345 REFIID riid
, void **ppv
)
347 struct activate_async_op
*This
= impl_from_IActivateAudioInterfaceAsyncOperation(iface
);
349 TRACE("(%p)->(%s, %p)\n", This
, debugstr_guid(riid
), ppv
);
354 if (IsEqualIID(riid
, &IID_IUnknown
) ||
355 IsEqualIID(riid
, &IID_IActivateAudioInterfaceAsyncOperation
)) {
356 *ppv
= &This
->IActivateAudioInterfaceAsyncOperation_iface
;
359 return E_NOINTERFACE
;
362 IUnknown_AddRef((IUnknown
*)*ppv
);
366 static ULONG WINAPI
activate_async_op_AddRef(IActivateAudioInterfaceAsyncOperation
*iface
)
368 struct activate_async_op
*This
= impl_from_IActivateAudioInterfaceAsyncOperation(iface
);
369 LONG ref
= InterlockedIncrement(&This
->ref
);
370 TRACE("(%p) refcount now %li\n", This
, ref
);
374 static ULONG WINAPI
activate_async_op_Release(IActivateAudioInterfaceAsyncOperation
*iface
)
376 struct activate_async_op
*This
= impl_from_IActivateAudioInterfaceAsyncOperation(iface
);
377 LONG ref
= InterlockedDecrement(&This
->ref
);
378 TRACE("(%p) refcount now %li\n", This
, ref
);
380 if(This
->result_iface
)
381 IUnknown_Release(This
->result_iface
);
382 IActivateAudioInterfaceCompletionHandler_Release(This
->callback
);
388 static HRESULT WINAPI
activate_async_op_GetActivateResult(IActivateAudioInterfaceAsyncOperation
*iface
,
389 HRESULT
*result_hr
, IUnknown
**result_iface
)
391 struct activate_async_op
*This
= impl_from_IActivateAudioInterfaceAsyncOperation(iface
);
393 TRACE("(%p)->(%p, %p)\n", This
, result_hr
, result_iface
);
395 *result_hr
= This
->result_hr
;
397 if(This
->result_hr
== S_OK
){
398 *result_iface
= This
->result_iface
;
399 IUnknown_AddRef(*result_iface
);
405 static IActivateAudioInterfaceAsyncOperationVtbl IActivateAudioInterfaceAsyncOperation_vtbl
= {
406 activate_async_op_QueryInterface
,
407 activate_async_op_AddRef
,
408 activate_async_op_Release
,
409 activate_async_op_GetActivateResult
,
412 static DWORD WINAPI
activate_async_threadproc(void *user
)
414 struct activate_async_op
*op
= user
;
416 SetThreadDescription(GetCurrentThread(), L
"wine_mmdevapi_activate_async");
418 IActivateAudioInterfaceCompletionHandler_ActivateCompleted(op
->callback
, &op
->IActivateAudioInterfaceAsyncOperation_iface
);
420 IActivateAudioInterfaceAsyncOperation_Release(&op
->IActivateAudioInterfaceAsyncOperation_iface
);
425 static HRESULT
get_mmdevice_by_activatepath(const WCHAR
*path
, IMMDevice
**mmdev
)
427 IMMDeviceEnumerator
*devenum
;
430 static const WCHAR DEVINTERFACE_AUDIO_RENDER_WSTR
[] = L
"{E6327CAD-DCEC-4949-AE8A-991E976A79D2}";
431 static const WCHAR DEVINTERFACE_AUDIO_CAPTURE_WSTR
[] = L
"{2EEF81BE-33FA-4800-9670-1CD474972C3F}";
432 static const WCHAR MMDEV_PATH_PREFIX
[] = L
"\\\\?\\SWD#MMDEVAPI#";
434 hr
= MMDevEnum_Create(&IID_IMMDeviceEnumerator
, (void**)&devenum
);
436 WARN("Failed to create MMDeviceEnumerator: %08lx\n", hr
);
440 if (!lstrcmpiW(path
, DEVINTERFACE_AUDIO_RENDER_WSTR
)){
441 hr
= IMMDeviceEnumerator_GetDefaultAudioEndpoint(devenum
, eRender
, eMultimedia
, mmdev
);
442 } else if (!lstrcmpiW(path
, DEVINTERFACE_AUDIO_CAPTURE_WSTR
)){
443 hr
= IMMDeviceEnumerator_GetDefaultAudioEndpoint(devenum
, eCapture
, eMultimedia
, mmdev
);
444 } else if (!memcmp(path
, MMDEV_PATH_PREFIX
, sizeof(MMDEV_PATH_PREFIX
) - sizeof(WCHAR
))) {
445 WCHAR device_id
[56]; /* == strlen("{0.0.1.00000000}.{fd47d9cc-4218-4135-9ce2-0c195c87405b}") + 1 */
447 lstrcpynW(device_id
, path
+ (ARRAY_SIZE(MMDEV_PATH_PREFIX
) - 1), ARRAY_SIZE(device_id
));
449 hr
= IMMDeviceEnumerator_GetDevice(devenum
, device_id
, mmdev
);
451 FIXME("Unrecognized device id format: %s\n", debugstr_w(path
));
452 hr
= HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND
);
456 WARN("Failed to get requested device (%s): %08lx\n", debugstr_w(path
), hr
);
458 hr
= HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND
);
461 IMMDeviceEnumerator_Release(devenum
);
466 /***********************************************************************
467 * ActivateAudioInterfaceAsync (MMDEVAPI.17)
469 HRESULT WINAPI
ActivateAudioInterfaceAsync(const WCHAR
*path
, REFIID riid
,
470 PROPVARIANT
*params
, IActivateAudioInterfaceCompletionHandler
*done_handler
,
471 IActivateAudioInterfaceAsyncOperation
**op_out
)
473 struct activate_async_op
*op
;
477 TRACE("(%s, %s, %p, %p, %p)\n", debugstr_w(path
), debugstr_guid(riid
),
478 params
, done_handler
, op_out
);
480 op
= malloc(sizeof(*op
));
482 return E_OUTOFMEMORY
;
484 op
->ref
= 2; /* returned ref and threadproc ref */
485 op
->IActivateAudioInterfaceAsyncOperation_iface
.lpVtbl
= &IActivateAudioInterfaceAsyncOperation_vtbl
;
486 op
->callback
= done_handler
;
487 IActivateAudioInterfaceCompletionHandler_AddRef(done_handler
);
489 op
->result_hr
= get_mmdevice_by_activatepath(path
, &mmdev
);
490 if (SUCCEEDED(op
->result_hr
)) {
491 op
->result_hr
= IMMDevice_Activate(mmdev
, riid
, CLSCTX_INPROC_SERVER
, params
, (void**)&op
->result_iface
);
492 IMMDevice_Release(mmdev
);
494 op
->result_iface
= NULL
;
496 ht
= CreateThread(NULL
, 0, &activate_async_threadproc
, op
, 0, NULL
);
499 *op_out
= &op
->IActivateAudioInterfaceAsyncOperation_iface
;