2 * Copyright 2009 Maarten Lankhorst
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
23 #define NONAMELESSUNION
29 #include "wine/debug.h"
30 #include "wine/list.h"
31 #include "wine/unicode.h"
35 #include "mmdeviceapi.h"
38 #include "audioclient.h"
39 #include "endpointvolume.h"
40 #include "audiopolicy.h"
45 WINE_DEFAULT_DEBUG_CHANNEL(mmdevapi);
47 static const WCHAR software_mmdevapi[] =
48 { 'S','o','f','t','w','a','r','e','\\',
49 'M','i','c','r','o','s','o','f','t','\\',
50 'W','i','n','d','o','w','s','\\',
51 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
52 'M','M','D','e','v','i','c','e','s','\\',
53 'A','u','d','i','o',0};
54 static const WCHAR reg_render[] =
55 { 'R','e','n','d','e','r',0 };
56 static const WCHAR reg_capture[] =
57 { 'C','a','p','t','u','r','e',0 };
58 static const WCHAR reg_devicestate[] =
59 { 'D','e','v','i','c','e','S','t','a','t','e',0 };
60 static const WCHAR reg_properties[] =
61 { 'P','r','o','p','e','r','t','i','e','s',0 };
62 static const WCHAR slashW[] = {'\\',0};
63 static const WCHAR reg_out_nameW[] = {'D','e','f','a','u','l','t','O','u','t','p','u','t',0};
64 static const WCHAR reg_vout_nameW[] = {'D','e','f','a','u','l','t','V','o','i','c','e','O','u','t','p','u','t',0};
65 static const WCHAR reg_in_nameW[] = {'D','e','f','a','u','l','t','I','n','p','u','t',0};
66 static const WCHAR reg_vin_nameW[] = {'D','e','f','a','u','l','t','V','o','i','c','e','I','n','p','u','t',0};
68 static HKEY key_render;
69 static HKEY key_capture;
71 typedef struct MMDevPropStoreImpl
73 IPropertyStore IPropertyStore_iface;
79 typedef struct MMDevEnumImpl
81 IMMDeviceEnumerator IMMDeviceEnumerator_iface;
85 static MMDevEnumImpl *MMDevEnumerator;
86 static MMDevice **MMDevice_head;
87 static MMDevice *MMDevice_def_rec, *MMDevice_def_play;
88 static DWORD MMDevice_count;
89 static const IMMDeviceEnumeratorVtbl MMDevEnumVtbl;
90 static const IMMDeviceCollectionVtbl MMDevColVtbl;
91 static const IMMDeviceVtbl MMDeviceVtbl;
92 static const IPropertyStoreVtbl MMDevPropVtbl;
93 static const IMMEndpointVtbl MMEndpointVtbl;
95 static IMMDevice info_device;
97 typedef struct MMDevColImpl
99 IMMDeviceCollection IMMDeviceCollection_iface;
105 typedef struct IPropertyBagImpl {
106 IPropertyBag IPropertyBag_iface;
110 static const IPropertyBagVtbl PB_Vtbl;
112 static HRESULT MMDevPropStore_Create(MMDevice *This, DWORD access, IPropertyStore **ppv);
114 static inline MMDevPropStore *impl_from_IPropertyStore(IPropertyStore *iface)
116 return CONTAINING_RECORD(iface, MMDevPropStore, IPropertyStore_iface);
119 static inline MMDevEnumImpl *impl_from_IMMDeviceEnumerator(IMMDeviceEnumerator *iface)
121 return CONTAINING_RECORD(iface, MMDevEnumImpl, IMMDeviceEnumerator_iface);
124 static inline MMDevColImpl *impl_from_IMMDeviceCollection(IMMDeviceCollection *iface)
126 return CONTAINING_RECORD(iface, MMDevColImpl, IMMDeviceCollection_iface);
129 static inline IPropertyBagImpl *impl_from_IPropertyBag(IPropertyBag *iface)
131 return CONTAINING_RECORD(iface, IPropertyBagImpl, IPropertyBag_iface);
134 static const WCHAR propkey_formatW[] = {
135 '{','%','0','8','X','-','%','0','4','X','-',
136 '%','0','4','X','-','%','0','2','X','%','0','2','X','-',
137 '%','0','2','X','%','0','2','X','%','0','2','X','%','0','2','X',
138 '%','0','2','X','%','0','2','X','}',',','%','d',0 };
140 static HRESULT MMDevPropStore_OpenPropKey(const GUID *guid, DWORD flow, HKEY *propkey)
145 StringFromGUID2(guid, buffer, 39);
146 if ((ret = RegOpenKeyExW(flow == eRender ? key_render : key_capture, buffer, 0, KEY_READ|KEY_WRITE, &key)) != ERROR_SUCCESS)
148 WARN("Opening key %s failed with %u\n", debugstr_w(buffer), ret);
151 ret = RegOpenKeyExW(key, reg_properties, 0, KEY_READ|KEY_WRITE, propkey);
153 if (ret != ERROR_SUCCESS)
155 WARN("Opening key %s failed with %u\n", debugstr_w(reg_properties), ret);
161 static HRESULT MMDevice_GetPropValue(const GUID *devguid, DWORD flow, REFPROPERTYKEY key, PROPVARIANT *pv)
164 const GUID *id = &key->fmtid;
170 hr = MMDevPropStore_OpenPropKey(devguid, flow, ®key);
173 wsprintfW( buffer, propkey_formatW, id->Data1, id->Data2, id->Data3,
174 id->Data4[0], id->Data4[1], id->Data4[2], id->Data4[3],
175 id->Data4[4], id->Data4[5], id->Data4[6], id->Data4[7], key->pid );
176 ret = RegGetValueW(regkey, NULL, buffer, RRF_RT_ANY, &type, NULL, &size);
177 if (ret != ERROR_SUCCESS)
179 WARN("Reading %s returned %d\n", debugstr_w(buffer), ret);
181 PropVariantClear(pv);
190 pv->u.pwszVal = CoTaskMemAlloc(size);
194 RegGetValueW(regkey, NULL, buffer, RRF_RT_REG_SZ, NULL, (BYTE*)pv->u.pwszVal, &size);
200 RegGetValueW(regkey, NULL, buffer, RRF_RT_REG_DWORD, NULL, (BYTE*)&pv->u.ulVal, &size);
206 pv->u.blob.cbSize = size;
207 pv->u.blob.pBlobData = CoTaskMemAlloc(size);
208 if (!pv->u.blob.pBlobData)
211 RegGetValueW(regkey, NULL, buffer, RRF_RT_REG_BINARY, NULL, (BYTE*)pv->u.blob.pBlobData, &size);
215 ERR("Unknown/unhandled type: %u\n", type);
216 PropVariantClear(pv);
223 static HRESULT MMDevice_SetPropValue(const GUID *devguid, DWORD flow, REFPROPERTYKEY key, REFPROPVARIANT pv)
226 const GUID *id = &key->fmtid;
231 hr = MMDevPropStore_OpenPropKey(devguid, flow, ®key);
234 wsprintfW( buffer, propkey_formatW, id->Data1, id->Data2, id->Data3,
235 id->Data4[0], id->Data4[1], id->Data4[2], id->Data4[3],
236 id->Data4[4], id->Data4[5], id->Data4[6], id->Data4[7], key->pid );
241 ret = RegSetValueExW(regkey, buffer, 0, REG_DWORD, (const BYTE*)&pv->u.ulVal, sizeof(DWORD));
246 ret = RegSetValueExW(regkey, buffer, 0, REG_BINARY, pv->u.blob.pBlobData, pv->u.blob.cbSize);
247 TRACE("Blob %p %u\n", pv->u.blob.pBlobData, pv->u.blob.cbSize);
253 ret = RegSetValueExW(regkey, buffer, 0, REG_SZ, (const BYTE*)pv->u.pwszVal, sizeof(WCHAR)*(1+lstrlenW(pv->u.pwszVal)));
258 FIXME("Unhandled type %u\n", pv->vt);
263 TRACE("Writing %s returned %u\n", debugstr_w(buffer), ret);
267 /* Creates or updates the state of a device
268 * If GUID is null, a random guid will be assigned
269 * and the device will be created
271 static MMDevice *MMDevice_Create(WCHAR *name, GUID *id, EDataFlow flow, DWORD state, BOOL setdefault)
274 MMDevice *cur = NULL;
278 static const PROPERTYKEY deviceinterface_key = {
279 {0x233164c8, 0x1b2c, 0x4c7d, {0xbc, 0x68, 0xb6, 0x71, 0x68, 0x7a, 0x25, 0x67}}, 1
282 for (i = 0; i < MMDevice_count; ++i)
284 MMDevice *device = MMDevice_head[i];
285 if (device->flow == flow && IsEqualGUID(&device->devguid, id)){
292 /* No device found, allocate new one */
293 cur = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*cur));
297 cur->IMMDevice_iface.lpVtbl = &MMDeviceVtbl;
298 cur->IMMEndpoint_iface.lpVtbl = &MMEndpointVtbl;
300 InitializeCriticalSection(&cur->crst);
301 cur->crst.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": MMDevice.crst");
304 MMDevice_head = HeapAlloc(GetProcessHeap(), 0, sizeof(*MMDevice_head));
306 MMDevice_head = HeapReAlloc(GetProcessHeap(), 0, MMDevice_head, sizeof(*MMDevice_head)*(1+MMDevice_count));
307 MMDevice_head[MMDevice_count++] = cur;
308 }else if(cur->ref > 0)
309 WARN("Modifying an MMDevice with postitive reference count!\n");
311 HeapFree(GetProcessHeap(), 0, cur->drv_id);
318 StringFromGUID2(&cur->devguid, guidstr, sizeof(guidstr)/sizeof(*guidstr));
325 if (RegCreateKeyExW(root, guidstr, 0, NULL, 0, KEY_WRITE|KEY_READ, NULL, &key, NULL) == ERROR_SUCCESS)
328 RegSetValueExW(key, reg_devicestate, 0, REG_DWORD, (const BYTE*)&state, sizeof(DWORD));
329 if (!RegCreateKeyExW(key, reg_properties, 0, NULL, 0, KEY_WRITE|KEY_READ, NULL, &keyprop, NULL))
335 MMDevice_SetPropValue(id, flow, (const PROPERTYKEY*)&DEVPKEY_Device_FriendlyName, &pv);
336 MMDevice_SetPropValue(id, flow, (const PROPERTYKEY*)&DEVPKEY_Device_DeviceDesc, &pv);
338 pv.u.pwszVal = guidstr;
339 MMDevice_SetPropValue(id, flow, &deviceinterface_key, &pv);
341 RegCloseKey(keyprop);
349 MMDevice_def_play = cur;
351 MMDevice_def_rec = cur;
356 static HRESULT load_devices_from_reg(void)
363 ret = RegCreateKeyExW(HKEY_LOCAL_MACHINE, software_mmdevapi, 0, NULL, 0, KEY_WRITE|KEY_READ, NULL, &root, NULL);
364 if (ret == ERROR_SUCCESS)
365 ret = RegCreateKeyExW(root, reg_capture, 0, NULL, 0, KEY_READ|KEY_WRITE, NULL, &key_capture, NULL);
366 if (ret == ERROR_SUCCESS)
367 ret = RegCreateKeyExW(root, reg_render, 0, NULL, 0, KEY_READ|KEY_WRITE, NULL, &key_render, NULL);
371 if (ret != ERROR_SUCCESS)
373 RegCloseKey(key_capture);
374 key_render = key_capture = NULL;
375 WARN("Couldn't create key: %u\n", ret);
383 PROPVARIANT pv = { VT_EMPTY };
385 len = sizeof(guidvalue)/sizeof(guidvalue[0]);
386 ret = RegEnumKeyExW(cur, i++, guidvalue, &len, NULL, NULL, NULL, NULL);
387 if (ret == ERROR_NO_MORE_ITEMS)
389 if (cur == key_capture)
398 if (ret != ERROR_SUCCESS)
400 if (SUCCEEDED(CLSIDFromString(guidvalue, &guid))
401 && SUCCEEDED(MMDevice_GetPropValue(&guid, curflow, (const PROPERTYKEY*)&DEVPKEY_Device_FriendlyName, &pv))
402 && pv.vt == VT_LPWSTR)
404 DWORD size_bytes = (strlenW(pv.u.pwszVal) + 1) * sizeof(WCHAR);
405 WCHAR *name = HeapAlloc(GetProcessHeap(), 0, size_bytes);
406 memcpy(name, pv.u.pwszVal, size_bytes);
407 MMDevice_Create(name, &guid, curflow,
408 DEVICE_STATE_NOTPRESENT, FALSE);
409 CoTaskMemFree(pv.u.pwszVal);
416 static HRESULT set_format(MMDevice *dev)
419 IAudioClient *client;
421 PROPVARIANT pv = { VT_EMPTY };
423 hr = drvs.pGetAudioEndpoint(&dev->devguid, &dev->IMMDevice_iface, &client);
427 hr = IAudioClient_GetMixFormat(client, &fmt);
429 IAudioClient_Release(client);
433 IAudioClient_Release(client);
436 pv.u.blob.cbSize = sizeof(WAVEFORMATEX) + fmt->cbSize;
437 pv.u.blob.pBlobData = (BYTE*)fmt;
438 MMDevice_SetPropValue(&dev->devguid, dev->flow,
439 &PKEY_AudioEngine_DeviceFormat, &pv);
440 MMDevice_SetPropValue(&dev->devguid, dev->flow,
441 &PKEY_AudioEngine_OEMFormat, &pv);
446 static HRESULT load_driver_devices(EDataFlow flow)
453 if(!drvs.pGetEndpointIDs)
456 hr = drvs.pGetEndpointIDs(flow, &ids, &guids, &num, &def);
460 for(i = 0; i < num; ++i){
462 dev = MMDevice_Create(ids[i], &guids[i], flow, DEVICE_STATE_ACTIVE,
467 HeapFree(GetProcessHeap(), 0, guids);
468 HeapFree(GetProcessHeap(), 0, ids);
473 static void MMDevice_Destroy(MMDevice *This)
476 TRACE("Freeing %s\n", debugstr_w(This->drv_id));
477 /* Since this function is called at destruction time, reordering of the list is unimportant */
478 for (i = 0; i < MMDevice_count; ++i)
480 if (MMDevice_head[i] == This)
482 MMDevice_head[i] = MMDevice_head[--MMDevice_count];
486 This->crst.DebugInfo->Spare[0] = 0;
487 DeleteCriticalSection(&This->crst);
488 HeapFree(GetProcessHeap(), 0, This->drv_id);
489 HeapFree(GetProcessHeap(), 0, This);
492 static inline MMDevice *impl_from_IMMDevice(IMMDevice *iface)
494 return CONTAINING_RECORD(iface, MMDevice, IMMDevice_iface);
497 static HRESULT WINAPI MMDevice_QueryInterface(IMMDevice *iface, REFIID riid, void **ppv)
499 MMDevice *This = impl_from_IMMDevice(iface);
500 TRACE("(%p)->(%s,%p)\n", iface, debugstr_guid(riid), ppv);
505 if (IsEqualIID(riid, &IID_IUnknown)
506 || IsEqualIID(riid, &IID_IMMDevice))
508 else if (IsEqualIID(riid, &IID_IMMEndpoint))
509 *ppv = &This->IMMEndpoint_iface;
512 IUnknown_AddRef((IUnknown*)*ppv);
515 WARN("Unknown interface %s\n", debugstr_guid(riid));
516 return E_NOINTERFACE;
519 static ULONG WINAPI MMDevice_AddRef(IMMDevice *iface)
521 MMDevice *This = impl_from_IMMDevice(iface);
524 ref = InterlockedIncrement(&This->ref);
525 TRACE("Refcount now %i\n", ref);
529 static ULONG WINAPI MMDevice_Release(IMMDevice *iface)
531 MMDevice *This = impl_from_IMMDevice(iface);
534 ref = InterlockedDecrement(&This->ref);
535 TRACE("Refcount now %i\n", ref);
539 static HRESULT WINAPI MMDevice_Activate(IMMDevice *iface, REFIID riid, DWORD clsctx, PROPVARIANT *params, void **ppv)
541 HRESULT hr = E_NOINTERFACE;
542 MMDevice *This = impl_from_IMMDevice(iface);
544 TRACE("(%p)->(%p,%x,%p,%p)\n", iface, riid, clsctx, params, ppv);
549 if (IsEqualIID(riid, &IID_IAudioClient)){
550 hr = drvs.pGetAudioEndpoint(&This->devguid, iface, (IAudioClient**)ppv);
551 }else if (IsEqualIID(riid, &IID_IAudioEndpointVolume))
552 hr = AudioEndpointVolume_Create(This, (IAudioEndpointVolume**)ppv);
553 else if (IsEqualIID(riid, &IID_IAudioSessionManager)
554 || IsEqualIID(riid, &IID_IAudioSessionManager2))
556 hr = drvs.pGetAudioSessionManager(iface, (IAudioSessionManager2**)ppv);
558 else if (IsEqualIID(riid, &IID_IBaseFilter))
560 if (This->flow == eRender)
561 hr = CoCreateInstance(&CLSID_DSoundRender, NULL, clsctx, riid, ppv);
563 ERR("Not supported for recording?\n");
566 IPersistPropertyBag *ppb;
567 hr = IUnknown_QueryInterface((IUnknown*)*ppv, &IID_IPersistPropertyBag, (void*)&ppb);
570 /* ::Load cannot assume the interface stays alive after the function returns,
571 * so just create the interface on the stack, saves a lot of complicated code */
572 IPropertyBagImpl bag = { { &PB_Vtbl }, This->devguid };
573 hr = IPersistPropertyBag_Load(ppb, &bag.IPropertyBag_iface, NULL);
574 IPersistPropertyBag_Release(ppb);
576 IBaseFilter_Release((IBaseFilter*)*ppv);
580 FIXME("Wine doesn't support IPersistPropertyBag on DSoundRender yet, ignoring..\n");
585 else if (IsEqualIID(riid, &IID_IDeviceTopology))
587 FIXME("IID_IDeviceTopology unsupported\n");
589 else if (IsEqualIID(riid, &IID_IDirectSound)
590 || IsEqualIID(riid, &IID_IDirectSound8))
592 if (This->flow == eRender)
593 hr = CoCreateInstance(&CLSID_DirectSound8, NULL, clsctx, riid, ppv);
596 hr = IDirectSound_Initialize((IDirectSound*)*ppv, &This->devguid);
598 IDirectSound_Release((IDirectSound*)*ppv);
601 else if (IsEqualIID(riid, &IID_IDirectSoundCapture)
602 || IsEqualIID(riid, &IID_IDirectSoundCapture8))
604 if (This->flow == eCapture)
605 hr = CoCreateInstance(&CLSID_DirectSoundCapture8, NULL, clsctx, riid, ppv);
608 hr = IDirectSoundCapture_Initialize((IDirectSoundCapture*)*ppv, &This->devguid);
610 IDirectSoundCapture_Release((IDirectSoundCapture*)*ppv);
614 ERR("Invalid/unknown iid %s\n", debugstr_guid(riid));
619 TRACE("Returning %08x\n", hr);
623 static HRESULT WINAPI MMDevice_OpenPropertyStore(IMMDevice *iface, DWORD access, IPropertyStore **ppv)
625 MMDevice *This = impl_from_IMMDevice(iface);
626 TRACE("(%p)->(%x,%p)\n", This, access, ppv);
630 return MMDevPropStore_Create(This, access, ppv);
633 static HRESULT WINAPI MMDevice_GetId(IMMDevice *iface, WCHAR **itemid)
635 MMDevice *This = impl_from_IMMDevice(iface);
637 GUID *id = &This->devguid;
638 static const WCHAR formatW[] = { '{','0','.','0','.','%','u','.','0','0','0','0','0','0','0','0','}','.',
639 '{','%','0','8','X','-','%','0','4','X','-',
640 '%','0','4','X','-','%','0','2','X','%','0','2','X','-',
641 '%','0','2','X','%','0','2','X','%','0','2','X','%','0','2','X',
642 '%','0','2','X','%','0','2','X','}',0 };
644 TRACE("(%p)->(%p)\n", This, itemid);
647 *itemid = str = CoTaskMemAlloc(56 * sizeof(WCHAR));
649 return E_OUTOFMEMORY;
650 wsprintfW( str, formatW, This->flow, id->Data1, id->Data2, id->Data3,
651 id->Data4[0], id->Data4[1], id->Data4[2], id->Data4[3],
652 id->Data4[4], id->Data4[5], id->Data4[6], id->Data4[7] );
653 TRACE("returning %s\n", wine_dbgstr_w(str));
657 static HRESULT WINAPI MMDevice_GetState(IMMDevice *iface, DWORD *state)
659 MMDevice *This = impl_from_IMMDevice(iface);
660 TRACE("(%p)->(%p)\n", iface, state);
664 *state = This->state;
668 static const IMMDeviceVtbl MMDeviceVtbl =
670 MMDevice_QueryInterface,
674 MMDevice_OpenPropertyStore,
679 static inline MMDevice *impl_from_IMMEndpoint(IMMEndpoint *iface)
681 return CONTAINING_RECORD(iface, MMDevice, IMMEndpoint_iface);
684 static HRESULT WINAPI MMEndpoint_QueryInterface(IMMEndpoint *iface, REFIID riid, void **ppv)
686 MMDevice *This = impl_from_IMMEndpoint(iface);
687 TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(riid), ppv);
688 return IMMDevice_QueryInterface(&This->IMMDevice_iface, riid, ppv);
691 static ULONG WINAPI MMEndpoint_AddRef(IMMEndpoint *iface)
693 MMDevice *This = impl_from_IMMEndpoint(iface);
694 TRACE("(%p)\n", This);
695 return IMMDevice_AddRef(&This->IMMDevice_iface);
698 static ULONG WINAPI MMEndpoint_Release(IMMEndpoint *iface)
700 MMDevice *This = impl_from_IMMEndpoint(iface);
701 TRACE("(%p)\n", This);
702 return IMMDevice_Release(&This->IMMDevice_iface);
705 static HRESULT WINAPI MMEndpoint_GetDataFlow(IMMEndpoint *iface, EDataFlow *flow)
707 MMDevice *This = impl_from_IMMEndpoint(iface);
708 TRACE("(%p)->(%p)\n", This, flow);
715 static const IMMEndpointVtbl MMEndpointVtbl =
717 MMEndpoint_QueryInterface,
720 MMEndpoint_GetDataFlow
723 static HRESULT MMDevCol_Create(IMMDeviceCollection **ppv, EDataFlow flow, DWORD state)
727 This = HeapAlloc(GetProcessHeap(), 0, sizeof(*This));
730 return E_OUTOFMEMORY;
731 This->IMMDeviceCollection_iface.lpVtbl = &MMDevColVtbl;
735 *ppv = &This->IMMDeviceCollection_iface;
739 static void MMDevCol_Destroy(MMDevColImpl *This)
741 HeapFree(GetProcessHeap(), 0, This);
744 static HRESULT WINAPI MMDevCol_QueryInterface(IMMDeviceCollection *iface, REFIID riid, void **ppv)
746 MMDevColImpl *This = impl_from_IMMDeviceCollection(iface);
747 TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(riid), ppv);
751 if (IsEqualIID(riid, &IID_IUnknown)
752 || IsEqualIID(riid, &IID_IMMDeviceCollection))
757 return E_NOINTERFACE;
758 IUnknown_AddRef((IUnknown*)*ppv);
762 static ULONG WINAPI MMDevCol_AddRef(IMMDeviceCollection *iface)
764 MMDevColImpl *This = impl_from_IMMDeviceCollection(iface);
765 LONG ref = InterlockedIncrement(&This->ref);
766 TRACE("Refcount now %i\n", ref);
770 static ULONG WINAPI MMDevCol_Release(IMMDeviceCollection *iface)
772 MMDevColImpl *This = impl_from_IMMDeviceCollection(iface);
773 LONG ref = InterlockedDecrement(&This->ref);
774 TRACE("Refcount now %i\n", ref);
776 MMDevCol_Destroy(This);
780 static HRESULT WINAPI MMDevCol_GetCount(IMMDeviceCollection *iface, UINT *numdevs)
782 MMDevColImpl *This = impl_from_IMMDeviceCollection(iface);
785 TRACE("(%p)->(%p)\n", This, numdevs);
790 for (i = 0; i < MMDevice_count; ++i)
792 MMDevice *cur = MMDevice_head[i];
793 if ((cur->flow == This->flow || This->flow == eAll)
794 && (cur->state & This->state))
800 static HRESULT WINAPI MMDevCol_Item(IMMDeviceCollection *iface, UINT n, IMMDevice **dev)
802 MMDevColImpl *This = impl_from_IMMDeviceCollection(iface);
805 TRACE("(%p)->(%u, %p)\n", This, n, dev);
809 for (j = 0; j < MMDevice_count; ++j)
811 MMDevice *cur = MMDevice_head[j];
812 if ((cur->flow == This->flow || This->flow == eAll)
813 && (cur->state & This->state)
816 *dev = &cur->IMMDevice_iface;
817 IMMDevice_AddRef(*dev);
821 WARN("Could not obtain item %u\n", n);
826 static const IMMDeviceCollectionVtbl MMDevColVtbl =
828 MMDevCol_QueryInterface,
835 HRESULT MMDevEnum_Create(REFIID riid, void **ppv)
837 MMDevEnumImpl *This = MMDevEnumerator;
841 This = HeapAlloc(GetProcessHeap(), 0, sizeof(*This));
844 return E_OUTOFMEMORY;
846 This->IMMDeviceEnumerator_iface.lpVtbl = &MMDevEnumVtbl;
847 MMDevEnumerator = This;
849 load_devices_from_reg();
850 load_driver_devices(eRender);
851 load_driver_devices(eCapture);
853 return IUnknown_QueryInterface((IUnknown*)This, riid, ppv);
856 void MMDevEnum_Free(void)
858 while (MMDevice_count)
859 MMDevice_Destroy(MMDevice_head[0]);
860 RegCloseKey(key_render);
861 RegCloseKey(key_capture);
862 key_render = key_capture = NULL;
863 HeapFree(GetProcessHeap(), 0, MMDevEnumerator);
864 MMDevEnumerator = NULL;
867 static HRESULT WINAPI MMDevEnum_QueryInterface(IMMDeviceEnumerator *iface, REFIID riid, void **ppv)
869 MMDevEnumImpl *This = impl_from_IMMDeviceEnumerator(iface);
870 TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(riid), ppv);
874 if (IsEqualIID(riid, &IID_IUnknown)
875 || IsEqualIID(riid, &IID_IMMDeviceEnumerator))
880 return E_NOINTERFACE;
881 IUnknown_AddRef((IUnknown*)*ppv);
885 static ULONG WINAPI MMDevEnum_AddRef(IMMDeviceEnumerator *iface)
887 MMDevEnumImpl *This = impl_from_IMMDeviceEnumerator(iface);
888 LONG ref = InterlockedIncrement(&This->ref);
889 TRACE("Refcount now %i\n", ref);
893 static ULONG WINAPI MMDevEnum_Release(IMMDeviceEnumerator *iface)
895 MMDevEnumImpl *This = impl_from_IMMDeviceEnumerator(iface);
896 LONG ref = InterlockedDecrement(&This->ref);
899 TRACE("Refcount now %i\n", ref);
903 static HRESULT WINAPI MMDevEnum_EnumAudioEndpoints(IMMDeviceEnumerator *iface, EDataFlow flow, DWORD mask, IMMDeviceCollection **devices)
905 MMDevEnumImpl *This = impl_from_IMMDeviceEnumerator(iface);
906 TRACE("(%p)->(%u,%u,%p)\n", This, flow, mask, devices);
910 if (flow >= EDataFlow_enum_count)
912 if (mask & ~DEVICE_STATEMASK_ALL)
914 return MMDevCol_Create(devices, flow, mask);
917 static HRESULT WINAPI MMDevEnum_GetDefaultAudioEndpoint(IMMDeviceEnumerator *iface, EDataFlow flow, ERole role, IMMDevice **device)
919 MMDevEnumImpl *This = impl_from_IMMDeviceEnumerator(iface);
924 TRACE("(%p)->(%u,%u,%p)\n", This, flow, role, device);
929 if((flow != eRender && flow != eCapture) ||
930 (role != eConsole && role != eMultimedia && role != eCommunications)){
931 WARN("Unknown flow (%u) or role (%u)\n", flow, role);
937 if(!drvs.module_name[0])
940 lstrcpyW(reg_key, drv_keyW);
941 lstrcatW(reg_key, slashW);
942 lstrcatW(reg_key, drvs.module_name);
944 if(RegOpenKeyW(HKEY_CURRENT_USER, reg_key, &key) == ERROR_SUCCESS){
945 const WCHAR *reg_x_name, *reg_vx_name;
947 DWORD size = sizeof(def_id), state;
950 reg_x_name = reg_out_nameW;
951 reg_vx_name = reg_vout_nameW;
953 reg_x_name = reg_in_nameW;
954 reg_vx_name = reg_vin_nameW;
957 if(role == eCommunications &&
958 RegQueryValueExW(key, reg_vx_name, 0, NULL,
959 (BYTE*)def_id, &size) == ERROR_SUCCESS){
960 hr = IMMDeviceEnumerator_GetDevice(iface, def_id, device);
962 if(SUCCEEDED(IMMDevice_GetState(*device, &state)) &&
963 state == DEVICE_STATE_ACTIVE){
969 TRACE("Unable to find voice device %s\n", wine_dbgstr_w(def_id));
972 if(RegQueryValueExW(key, reg_x_name, 0, NULL,
973 (BYTE*)def_id, &size) == ERROR_SUCCESS){
974 hr = IMMDeviceEnumerator_GetDevice(iface, def_id, device);
976 if(SUCCEEDED(IMMDevice_GetState(*device, &state)) &&
977 state == DEVICE_STATE_ACTIVE){
983 TRACE("Unable to find device %s\n", wine_dbgstr_w(def_id));
990 *device = &MMDevice_def_play->IMMDevice_iface;
992 *device = &MMDevice_def_rec->IMMDevice_iface;
996 IMMDevice_AddRef(*device);
1000 static HRESULT WINAPI MMDevEnum_GetDevice(IMMDeviceEnumerator *iface, const WCHAR *name, IMMDevice **device)
1002 MMDevEnumImpl *This = impl_from_IMMDeviceEnumerator(iface);
1004 IMMDevice *dev = NULL;
1006 static const WCHAR wine_info_deviceW[] = {'W','i','n','e',' ',
1007 'i','n','f','o',' ','d','e','v','i','c','e',0};
1009 TRACE("(%p)->(%s,%p)\n", This, debugstr_w(name), device);
1011 if(!name || !device)
1014 if(!lstrcmpW(name, wine_info_deviceW)){
1015 *device = &info_device;
1019 for (i = 0; i < MMDevice_count; ++i)
1022 dev = &MMDevice_head[i]->IMMDevice_iface;
1023 IMMDevice_GetId(dev, &str);
1025 if (str && !lstrcmpW(str, name))
1028 IMMDevice_AddRef(dev);
1034 TRACE("Could not find device %s\n", debugstr_w(name));
1035 return E_INVALIDARG;
1038 struct NotificationClientWrapper {
1039 IMMNotificationClient *client;
1043 static struct list g_notif_clients = LIST_INIT(g_notif_clients);
1044 static HANDLE g_notif_thread;
1046 static CRITICAL_SECTION g_notif_lock;
1047 static CRITICAL_SECTION_DEBUG g_notif_lock_debug =
1049 0, 0, &g_notif_lock,
1050 { &g_notif_lock_debug.ProcessLocksList, &g_notif_lock_debug.ProcessLocksList },
1051 0, 0, { (DWORD_PTR)(__FILE__ ": g_notif_lock") }
1053 static CRITICAL_SECTION g_notif_lock = { &g_notif_lock_debug, -1, 0, 0, 0, 0 };
1055 static void notify_clients(EDataFlow flow, ERole role, const WCHAR *id)
1057 struct NotificationClientWrapper *wrapper;
1058 LIST_FOR_EACH_ENTRY(wrapper, &g_notif_clients,
1059 struct NotificationClientWrapper, entry)
1060 IMMNotificationClient_OnDefaultDeviceChanged(wrapper->client, flow,
1063 /* Windows 7 treats changes to eConsole as changes to eMultimedia */
1064 if(role == eConsole)
1065 notify_clients(flow, eMultimedia, id);
1068 static int notify_if_changed(EDataFlow flow, ERole role, HKEY key,
1069 const WCHAR *val_name, WCHAR *old_val, IMMDevice *def_dev)
1071 WCHAR new_val[64], *id;
1075 size = sizeof(new_val);
1076 if(RegQueryValueExW(key, val_name, 0, NULL,
1077 (BYTE*)new_val, &size) != ERROR_SUCCESS){
1078 if(old_val[0] != 0){
1079 /* set by user -> system default */
1081 hr = IMMDevice_GetId(def_dev, &id);
1083 ERR("GetId failed: %08x\n", hr);
1089 notify_clients(flow, role, id);
1096 /* system default -> system default, noop */
1100 if(!lstrcmpW(old_val, new_val)){
1101 /* set by user -> same value */
1105 if(new_val[0] != 0){
1106 /* set by user -> different value */
1107 notify_clients(flow, role, new_val);
1108 memcpy(old_val, new_val, sizeof(new_val));
1112 /* set by user -> system default */
1114 hr = IMMDevice_GetId(def_dev, &id);
1116 ERR("GetId failed: %08x\n", hr);
1122 notify_clients(flow, role, id);
1129 static DWORD WINAPI notif_thread_proc(void *user)
1133 WCHAR out_name[64], vout_name[64], in_name[64], vin_name[64];
1136 lstrcpyW(reg_key, drv_keyW);
1137 lstrcatW(reg_key, slashW);
1138 lstrcatW(reg_key, drvs.module_name);
1140 if(RegCreateKeyExW(HKEY_CURRENT_USER, reg_key, 0, NULL, 0,
1141 MAXIMUM_ALLOWED, NULL, &key, NULL) != ERROR_SUCCESS){
1142 ERR("RegCreateKeyEx failed: %u\n", GetLastError());
1146 size = sizeof(out_name);
1147 if(RegQueryValueExW(key, reg_out_nameW, 0, NULL,
1148 (BYTE*)out_name, &size) != ERROR_SUCCESS)
1151 size = sizeof(vout_name);
1152 if(RegQueryValueExW(key, reg_vout_nameW, 0, NULL,
1153 (BYTE*)vout_name, &size) != ERROR_SUCCESS)
1156 size = sizeof(in_name);
1157 if(RegQueryValueExW(key, reg_in_nameW, 0, NULL,
1158 (BYTE*)in_name, &size) != ERROR_SUCCESS)
1161 size = sizeof(vin_name);
1162 if(RegQueryValueExW(key, reg_vin_nameW, 0, NULL,
1163 (BYTE*)vin_name, &size) != ERROR_SUCCESS)
1167 if(RegNotifyChangeKeyValue(key, FALSE, REG_NOTIFY_CHANGE_LAST_SET,
1168 NULL, FALSE) != ERROR_SUCCESS){
1169 ERR("RegNotifyChangeKeyValue failed: %u\n", GetLastError());
1171 g_notif_thread = NULL;
1175 EnterCriticalSection(&g_notif_lock);
1177 notify_if_changed(eRender, eConsole, key, reg_out_nameW,
1178 out_name, &MMDevice_def_play->IMMDevice_iface);
1179 notify_if_changed(eRender, eCommunications, key, reg_vout_nameW,
1180 vout_name, &MMDevice_def_play->IMMDevice_iface);
1181 notify_if_changed(eCapture, eConsole, key, reg_in_nameW,
1182 in_name, &MMDevice_def_rec->IMMDevice_iface);
1183 notify_if_changed(eCapture, eCommunications, key, reg_vin_nameW,
1184 vin_name, &MMDevice_def_rec->IMMDevice_iface);
1186 LeaveCriticalSection(&g_notif_lock);
1191 g_notif_thread = NULL;
1196 static HRESULT WINAPI MMDevEnum_RegisterEndpointNotificationCallback(IMMDeviceEnumerator *iface, IMMNotificationClient *client)
1198 MMDevEnumImpl *This = impl_from_IMMDeviceEnumerator(iface);
1199 struct NotificationClientWrapper *wrapper;
1201 TRACE("(%p)->(%p)\n", This, client);
1206 wrapper = HeapAlloc(GetProcessHeap(), 0, sizeof(*wrapper));
1208 return E_OUTOFMEMORY;
1210 wrapper->client = client;
1212 EnterCriticalSection(&g_notif_lock);
1214 list_add_tail(&g_notif_clients, &wrapper->entry);
1216 if(!g_notif_thread){
1217 g_notif_thread = CreateThread(NULL, 0, notif_thread_proc, NULL, 0, NULL);
1219 ERR("CreateThread failed: %u\n", GetLastError());
1222 LeaveCriticalSection(&g_notif_lock);
1227 static HRESULT WINAPI MMDevEnum_UnregisterEndpointNotificationCallback(IMMDeviceEnumerator *iface, IMMNotificationClient *client)
1229 MMDevEnumImpl *This = impl_from_IMMDeviceEnumerator(iface);
1230 struct NotificationClientWrapper *wrapper, *wrapper2;
1232 TRACE("(%p)->(%p)\n", This, client);
1237 EnterCriticalSection(&g_notif_lock);
1239 LIST_FOR_EACH_ENTRY_SAFE(wrapper, wrapper2, &g_notif_clients,
1240 struct NotificationClientWrapper, entry){
1241 if(wrapper->client == client){
1242 list_remove(&wrapper->entry);
1243 HeapFree(GetProcessHeap(), 0, wrapper);
1244 LeaveCriticalSection(&g_notif_lock);
1249 LeaveCriticalSection(&g_notif_lock);
1254 static const IMMDeviceEnumeratorVtbl MMDevEnumVtbl =
1256 MMDevEnum_QueryInterface,
1259 MMDevEnum_EnumAudioEndpoints,
1260 MMDevEnum_GetDefaultAudioEndpoint,
1261 MMDevEnum_GetDevice,
1262 MMDevEnum_RegisterEndpointNotificationCallback,
1263 MMDevEnum_UnregisterEndpointNotificationCallback
1266 static HRESULT MMDevPropStore_Create(MMDevice *parent, DWORD access, IPropertyStore **ppv)
1268 MMDevPropStore *This;
1269 if (access != STGM_READ
1270 && access != STGM_WRITE
1271 && access != STGM_READWRITE)
1273 WARN("Invalid access %08x\n", access);
1274 return E_INVALIDARG;
1276 This = HeapAlloc(GetProcessHeap(), 0, sizeof(*This));
1277 *ppv = &This->IPropertyStore_iface;
1279 return E_OUTOFMEMORY;
1280 This->IPropertyStore_iface.lpVtbl = &MMDevPropVtbl;
1282 This->parent = parent;
1283 This->access = access;
1287 static void MMDevPropStore_Destroy(MMDevPropStore *This)
1289 HeapFree(GetProcessHeap(), 0, This);
1292 static HRESULT WINAPI MMDevPropStore_QueryInterface(IPropertyStore *iface, REFIID riid, void **ppv)
1294 MMDevPropStore *This = impl_from_IPropertyStore(iface);
1295 TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(riid), ppv);
1299 if (IsEqualIID(riid, &IID_IUnknown)
1300 || IsEqualIID(riid, &IID_IPropertyStore))
1305 return E_NOINTERFACE;
1306 IUnknown_AddRef((IUnknown*)*ppv);
1310 static ULONG WINAPI MMDevPropStore_AddRef(IPropertyStore *iface)
1312 MMDevPropStore *This = impl_from_IPropertyStore(iface);
1313 LONG ref = InterlockedIncrement(&This->ref);
1314 TRACE("Refcount now %i\n", ref);
1318 static ULONG WINAPI MMDevPropStore_Release(IPropertyStore *iface)
1320 MMDevPropStore *This = impl_from_IPropertyStore(iface);
1321 LONG ref = InterlockedDecrement(&This->ref);
1322 TRACE("Refcount now %i\n", ref);
1324 MMDevPropStore_Destroy(This);
1328 static HRESULT WINAPI MMDevPropStore_GetCount(IPropertyStore *iface, DWORD *nprops)
1330 MMDevPropStore *This = impl_from_IPropertyStore(iface);
1336 TRACE("(%p)->(%p)\n", iface, nprops);
1339 hr = MMDevPropStore_OpenPropKey(&This->parent->devguid, This->parent->flow, &propkey);
1344 DWORD len = sizeof(buffer)/sizeof(*buffer);
1345 if (RegEnumKeyExW(propkey, i, buffer, &len, NULL, NULL, NULL, NULL) != ERROR_SUCCESS)
1349 RegCloseKey(propkey);
1350 TRACE("Returning %i\n", i);
1355 static HRESULT WINAPI MMDevPropStore_GetAt(IPropertyStore *iface, DWORD prop, PROPERTYKEY *key)
1357 MMDevPropStore *This = impl_from_IPropertyStore(iface);
1359 DWORD len = sizeof(buffer)/sizeof(*buffer);
1363 TRACE("(%p)->(%u,%p)\n", iface, prop, key);
1367 hr = MMDevPropStore_OpenPropKey(&This->parent->devguid, This->parent->flow, &propkey);
1371 if (RegEnumKeyExW(propkey, prop, buffer, &len, NULL, NULL, NULL, NULL) != ERROR_SUCCESS
1374 WARN("GetAt %u failed\n", prop);
1375 return E_INVALIDARG;
1377 RegCloseKey(propkey);
1379 CLSIDFromString(buffer, &key->fmtid);
1380 key->pid = atoiW(&buffer[40]);
1384 static HRESULT WINAPI MMDevPropStore_GetValue(IPropertyStore *iface, REFPROPERTYKEY key, PROPVARIANT *pv)
1386 MMDevPropStore *This = impl_from_IPropertyStore(iface);
1387 TRACE("(%p)->(\"%s,%u\", %p)\n", This, key ? debugstr_guid(&key->fmtid) : NULL, key ? key->pid : 0, pv);
1391 if (This->access != STGM_READ
1392 && This->access != STGM_READWRITE)
1393 return STG_E_ACCESSDENIED;
1396 if (IsEqualPropertyKey(*key, PKEY_AudioEndpoint_GUID))
1399 pv->u.pwszVal = CoTaskMemAlloc(39 * sizeof(WCHAR));
1401 return E_OUTOFMEMORY;
1402 StringFromGUID2(&This->parent->devguid, pv->u.pwszVal, 39);
1406 return MMDevice_GetPropValue(&This->parent->devguid, This->parent->flow, key, pv);
1409 static HRESULT WINAPI MMDevPropStore_SetValue(IPropertyStore *iface, REFPROPERTYKEY key, REFPROPVARIANT pv)
1411 MMDevPropStore *This = impl_from_IPropertyStore(iface);
1412 TRACE("(%p)->(\"%s,%u\", %p)\n", This, key ? debugstr_guid(&key->fmtid) : NULL, key ? key->pid : 0, pv);
1417 if (This->access != STGM_WRITE
1418 && This->access != STGM_READWRITE)
1419 return STG_E_ACCESSDENIED;
1420 return MMDevice_SetPropValue(&This->parent->devguid, This->parent->flow, key, pv);
1423 static HRESULT WINAPI MMDevPropStore_Commit(IPropertyStore *iface)
1429 static const IPropertyStoreVtbl MMDevPropVtbl =
1431 MMDevPropStore_QueryInterface,
1432 MMDevPropStore_AddRef,
1433 MMDevPropStore_Release,
1434 MMDevPropStore_GetCount,
1435 MMDevPropStore_GetAt,
1436 MMDevPropStore_GetValue,
1437 MMDevPropStore_SetValue,
1438 MMDevPropStore_Commit
1442 /* Property bag for IBaseFilter activation */
1443 static HRESULT WINAPI PB_QueryInterface(IPropertyBag *iface, REFIID riid, void **ppv)
1445 ERR("Should not be called\n");
1447 return E_NOINTERFACE;
1450 static ULONG WINAPI PB_AddRef(IPropertyBag *iface)
1452 ERR("Should not be called\n");
1456 static ULONG WINAPI PB_Release(IPropertyBag *iface)
1458 ERR("Should not be called\n");
1462 static HRESULT WINAPI PB_Read(IPropertyBag *iface, LPCOLESTR name, VARIANT *var, IErrorLog *log)
1464 static const WCHAR dsguid[] = { 'D','S','G','u','i','d', 0 };
1465 IPropertyBagImpl *This = impl_from_IPropertyBag(iface);
1466 TRACE("Trying to read %s, type %u\n", debugstr_w(name), var->n1.n2.vt);
1467 if (!lstrcmpW(name, dsguid))
1470 StringFromGUID2(&This->devguid, guidstr,sizeof(guidstr)/sizeof(*guidstr));
1471 var->n1.n2.vt = VT_BSTR;
1472 var->n1.n2.n3.bstrVal = SysAllocString(guidstr);
1475 ERR("Unknown property '%s' queried\n", debugstr_w(name));
1479 static HRESULT WINAPI PB_Write(IPropertyBag *iface, LPCOLESTR name, VARIANT *var)
1481 ERR("Should not be called\n");
1485 static const IPropertyBagVtbl PB_Vtbl =
1494 static ULONG WINAPI info_device_ps_AddRef(IPropertyStore *iface)
1499 static ULONG WINAPI info_device_ps_Release(IPropertyStore *iface)
1504 static HRESULT WINAPI info_device_ps_GetValue(IPropertyStore *iface,
1505 REFPROPERTYKEY key, PROPVARIANT *pv)
1507 TRACE("(static)->(\"%s,%u\", %p)\n", debugstr_guid(&key->fmtid), key ? key->pid : 0, pv);
1512 if (IsEqualPropertyKey(*key, DEVPKEY_Device_Driver))
1514 INT size = (lstrlenW(drvs.module_name) + 1) * sizeof(WCHAR);
1516 pv->u.pwszVal = CoTaskMemAlloc(size);
1518 return E_OUTOFMEMORY;
1519 memcpy(pv->u.pwszVal, drvs.module_name, size);
1523 return E_INVALIDARG;
1526 static const IPropertyStoreVtbl info_device_ps_Vtbl =
1529 info_device_ps_AddRef,
1530 info_device_ps_Release,
1533 info_device_ps_GetValue,
1538 static IPropertyStore info_device_ps = {
1539 &info_device_ps_Vtbl
1542 static ULONG WINAPI info_device_AddRef(IMMDevice *iface)
1547 static ULONG WINAPI info_device_Release(IMMDevice *iface)
1552 static HRESULT WINAPI info_device_OpenPropertyStore(IMMDevice *iface,
1553 DWORD access, IPropertyStore **ppv)
1555 TRACE("(static)->(%x, %p)\n", access, ppv);
1556 *ppv = &info_device_ps;
1560 static const IMMDeviceVtbl info_device_Vtbl =
1564 info_device_Release,
1566 info_device_OpenPropertyStore,
1571 static IMMDevice info_device = {