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
26 #elif defined(HAVE_OPENAL_AL_H)
27 #include <OpenAL/al.h>
28 #include <OpenAL/alc.h>
31 #define NONAMELESSUNION
37 #include "wine/debug.h"
38 #include "wine/unicode.h"
41 #include "mmdeviceapi.h"
44 #include "audioclient.h"
45 #include "endpointvolume.h"
46 #include "audiopolicy.h"
51 WINE_DEFAULT_DEBUG_CHANNEL(mmdevapi);
53 static const WCHAR software_mmdevapi[] =
54 { 'S','o','f','t','w','a','r','e','\\',
55 'M','i','c','r','o','s','o','f','t','\\',
56 'W','i','n','d','o','w','s','\\',
57 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
58 'M','M','D','e','v','i','c','e','s','\\',
59 'A','u','d','i','o',0};
60 static const WCHAR reg_render[] =
61 { 'R','e','n','d','e','r',0 };
62 static const WCHAR reg_capture[] =
63 { 'C','a','p','t','u','r','e',0 };
64 static const WCHAR reg_devicestate[] =
65 { 'D','e','v','i','c','e','S','t','a','t','e',0 };
66 static const WCHAR reg_properties[] =
67 { 'P','r','o','p','e','r','t','i','e','s',0 };
69 static HKEY key_render;
70 static HKEY key_capture;
72 typedef struct MMDevPropStoreImpl
74 IPropertyStore IPropertyStore_iface;
80 typedef struct MMDevEnumImpl
82 IMMDeviceEnumerator IMMDeviceEnumerator_iface;
86 static MMDevEnumImpl *MMDevEnumerator;
87 static MMDevice **MMDevice_head;
88 static MMDevice *MMDevice_def_rec, *MMDevice_def_play;
89 static DWORD MMDevice_count;
90 static const IMMDeviceEnumeratorVtbl MMDevEnumVtbl;
91 static const IMMDeviceCollectionVtbl MMDevColVtbl;
92 static const IMMDeviceVtbl MMDeviceVtbl;
93 static const IPropertyStoreVtbl MMDevPropVtbl;
94 static const IMMEndpointVtbl MMEndpointVtbl;
96 typedef struct MMDevColImpl
98 IMMDeviceCollection IMMDeviceCollection_iface;
104 typedef struct IPropertyBagImpl {
105 IPropertyBag IPropertyBag_iface;
109 static const IPropertyBagVtbl PB_Vtbl;
111 static HRESULT MMDevPropStore_Create(MMDevice *This, DWORD access, IPropertyStore **ppv);
113 static inline MMDevPropStore *impl_from_IPropertyStore(IPropertyStore *iface)
115 return CONTAINING_RECORD(iface, MMDevPropStore, IPropertyStore_iface);
118 static inline MMDevEnumImpl *impl_from_IMMDeviceEnumerator(IMMDeviceEnumerator *iface)
120 return CONTAINING_RECORD(iface, MMDevEnumImpl, IMMDeviceEnumerator_iface);
123 static inline MMDevColImpl *impl_from_IMMDeviceCollection(IMMDeviceCollection *iface)
125 return CONTAINING_RECORD(iface, MMDevColImpl, IMMDeviceCollection_iface);
128 static inline IPropertyBagImpl *impl_from_IPropertyBag(IPropertyBag *iface)
130 return CONTAINING_RECORD(iface, IPropertyBagImpl, IPropertyBag_iface);
133 /* Creates or updates the state of a device
134 * If GUID is null, a random guid will be assigned
135 * and the device will be created
137 static void MMDevice_Create(MMDevice **dev, WCHAR *name, GUID *id, EDataFlow flow, DWORD state, BOOL setdefault)
144 for (i = 0; i < MMDevice_count; ++i)
146 cur = MMDevice_head[i];
147 if (cur->flow == flow && !lstrcmpW(cur->alname, name))
150 /* Same device, update state */
152 StringFromGUID2(&cur->devguid, guidstr, sizeof(guidstr)/sizeof(*guidstr));
153 ret = RegOpenKeyExW(flow == eRender ? key_render : key_capture, guidstr, 0, KEY_WRITE, &key);
154 if (ret == ERROR_SUCCESS)
156 RegSetValueExW(key, reg_devicestate, 0, REG_DWORD, (const BYTE*)&state, sizeof(DWORD));
163 /* No device found, allocate new one */
164 cur = HeapAlloc(GetProcessHeap(), 0, sizeof(*cur));
167 cur->alname = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(name)+1)*sizeof(WCHAR));
170 HeapFree(GetProcessHeap(), 0, cur);
173 lstrcpyW(cur->alname, name);
174 cur->lpVtbl = &MMDeviceVtbl;
175 cur->lpEndpointVtbl = &MMEndpointVtbl;
177 InitializeCriticalSection(&cur->crst);
178 cur->crst.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": MMDevice.crst");
188 StringFromGUID2(id, guidstr, sizeof(guidstr)/sizeof(*guidstr));
193 if (!RegCreateKeyExW(root, guidstr, 0, NULL, 0, KEY_WRITE|KEY_READ, NULL, &key, NULL))
196 RegSetValueExW(key, reg_devicestate, 0, REG_DWORD, (const BYTE*)&state, sizeof(DWORD));
197 if (!RegCreateKeyExW(key, reg_properties, 0, NULL, 0, KEY_WRITE|KEY_READ, NULL, &keyprop, NULL))
202 MMDevice_SetPropValue(id, flow, (const PROPERTYKEY*)&DEVPKEY_Device_FriendlyName, &pv);
203 MMDevice_SetPropValue(id, flow, (const PROPERTYKEY*)&DEVPKEY_Device_DeviceDesc, &pv);
204 RegCloseKey(keyprop);
209 MMDevice_head = HeapAlloc(GetProcessHeap(), 0, sizeof(*MMDevice_head));
211 MMDevice_head = HeapReAlloc(GetProcessHeap(), 0, MMDevice_head, sizeof(*MMDevice_head)*(1+MMDevice_count));
212 MMDevice_head[MMDevice_count++] = cur;
218 MMDevice_def_play = cur;
220 MMDevice_def_rec = cur;
226 static void MMDevice_Destroy(MMDevice *This)
229 TRACE("Freeing %s\n", debugstr_w(This->alname));
230 /* Since this function is called at destruction time, reordering of the list is unimportant */
231 for (i = 0; i < MMDevice_count; ++i)
233 if (MMDevice_head[i] == This)
235 MMDevice_head[i] = MMDevice_head[--MMDevice_count];
241 palcCloseDevice(This->device);
243 This->crst.DebugInfo->Spare[0] = 0;
244 DeleteCriticalSection(&This->crst);
245 HeapFree(GetProcessHeap(), 0, This->alname);
246 HeapFree(GetProcessHeap(), 0, This);
249 static HRESULT WINAPI MMDevice_QueryInterface(IMMDevice *iface, REFIID riid, void **ppv)
251 MMDevice *This = (MMDevice *)iface;
252 TRACE("(%p)->(%s,%p)\n", iface, debugstr_guid(riid), ppv);
257 if (IsEqualIID(riid, &IID_IUnknown)
258 || IsEqualIID(riid, &IID_IMMDevice))
260 else if (IsEqualIID(riid, &IID_IMMEndpoint))
261 *ppv = &This->lpEndpointVtbl;
264 IUnknown_AddRef((IUnknown*)*ppv);
267 WARN("Unknown interface %s\n", debugstr_guid(riid));
268 return E_NOINTERFACE;
271 static ULONG WINAPI MMDevice_AddRef(IMMDevice *iface)
273 MMDevice *This = (MMDevice *)iface;
276 ref = InterlockedIncrement(&This->ref);
277 TRACE("Refcount now %i\n", ref);
281 static ULONG WINAPI MMDevice_Release(IMMDevice *iface)
283 MMDevice *This = (MMDevice *)iface;
286 ref = InterlockedDecrement(&This->ref);
287 TRACE("Refcount now %i\n", ref);
291 static HRESULT WINAPI MMDevice_Activate(IMMDevice *iface, REFIID riid, DWORD clsctx, PROPVARIANT *params, void **ppv)
293 HRESULT hr = E_NOINTERFACE;
296 MMDevice *This = (MMDevice *)iface;
298 TRACE("(%p)->(%p,%x,%p,%p)\n", iface, riid, clsctx, params, ppv);
305 WARN("OpenAL is still not loaded\n");
306 hr = AUDCLNT_E_SERVICE_NOT_RUNNING;
308 else if (IsEqualIID(riid, &IID_IAudioClient))
309 hr = AudioClient_Create(This, (IAudioClient**)ppv);
310 else if (IsEqualIID(riid, &IID_IAudioEndpointVolume))
311 hr = AudioEndpointVolume_Create(This, (IAudioEndpointVolume**)ppv);
312 else if (IsEqualIID(riid, &IID_IAudioSessionManager)
313 || IsEqualIID(riid, &IID_IAudioSessionManager2))
315 FIXME("IID_IAudioSessionManager unsupported\n");
317 else if (IsEqualIID(riid, &IID_IBaseFilter))
319 if (This->flow == eRender)
320 hr = CoCreateInstance(&CLSID_DSoundRender, NULL, clsctx, riid, ppv);
322 ERR("Not supported for recording?\n");
325 IPersistPropertyBag *ppb;
326 hr = IUnknown_QueryInterface((IUnknown*)*ppv, &IID_IPersistPropertyBag, (void*)&ppb);
329 /* ::Load cannot assume the interface stays alive after the function returns,
330 * so just create the interface on the stack, saves a lot of complicated code */
331 IPropertyBagImpl bag = { { &PB_Vtbl }, This->devguid };
332 hr = IPersistPropertyBag_Load(ppb, &bag.IPropertyBag_iface, NULL);
333 IPersistPropertyBag_Release(ppb);
335 IBaseFilter_Release((IBaseFilter*)*ppv);
339 FIXME("Wine doesn't support IPersistPropertyBag on DSoundRender yet, ignoring..\n");
344 else if (IsEqualIID(riid, &IID_IDeviceTopology))
346 FIXME("IID_IDeviceTopology unsupported\n");
348 else if (IsEqualIID(riid, &IID_IDirectSound)
349 || IsEqualIID(riid, &IID_IDirectSound8))
351 if (This->flow == eRender)
352 hr = CoCreateInstance(&CLSID_DirectSound8, NULL, clsctx, riid, ppv);
355 hr = IDirectSound_Initialize((IDirectSound*)*ppv, &This->devguid);
357 IDirectSound_Release((IDirectSound*)*ppv);
360 else if (IsEqualIID(riid, &IID_IDirectSoundCapture)
361 || IsEqualIID(riid, &IID_IDirectSoundCapture8))
363 if (This->flow == eCapture)
364 hr = CoCreateInstance(&CLSID_DirectSoundCapture8, NULL, clsctx, riid, ppv);
367 hr = IDirectSoundCapture_Initialize((IDirectSoundCapture*)*ppv, &This->devguid);
369 IDirectSoundCapture_Release((IDirectSoundCapture*)*ppv);
373 ERR("Invalid/unknown iid %s\n", debugstr_guid(riid));
375 if (!ppv) return E_POINTER;
376 hr = AUDCLNT_E_SERVICE_NOT_RUNNING;
382 TRACE("Returning %08x\n", hr);
386 static HRESULT WINAPI MMDevice_OpenPropertyStore(IMMDevice *iface, DWORD access, IPropertyStore **ppv)
388 MMDevice *This = (MMDevice *)iface;
389 TRACE("(%p)->(%x,%p)\n", This, access, ppv);
393 return MMDevPropStore_Create(This, access, ppv);
396 static HRESULT WINAPI MMDevice_GetId(IMMDevice *iface, WCHAR **itemid)
398 MMDevice *This = (MMDevice *)iface;
400 GUID *id = &This->devguid;
401 static const WCHAR formatW[] = { '{','0','.','0','.','0','.','0','0','0','0','0','0','0','0','}','.',
402 '{','%','0','8','X','-','%','0','4','X','-',
403 '%','0','4','X','-','%','0','2','X','%','0','2','X','-',
404 '%','0','2','X','%','0','2','X','%','0','2','X','%','0','2','X',
405 '%','0','2','X','%','0','2','X','}',0 };
407 TRACE("(%p)->(%p)\n", This, itemid);
410 *itemid = str = CoTaskMemAlloc(56 * sizeof(WCHAR));
412 return E_OUTOFMEMORY;
413 wsprintfW( str, formatW, id->Data1, id->Data2, id->Data3,
414 id->Data4[0], id->Data4[1], id->Data4[2], id->Data4[3],
415 id->Data4[4], id->Data4[5], id->Data4[6], id->Data4[7] );
419 static HRESULT WINAPI MMDevice_GetState(IMMDevice *iface, DWORD *state)
421 MMDevice *This = (MMDevice *)iface;
422 TRACE("(%p)->(%p)\n", iface, state);
426 *state = This->state;
430 static const IMMDeviceVtbl MMDeviceVtbl =
432 MMDevice_QueryInterface,
436 MMDevice_OpenPropertyStore,
441 static MMDevice *get_this_from_endpoint(IMMEndpoint *iface)
443 return (MMDevice*)((char*)iface - offsetof(MMDevice,lpEndpointVtbl));
446 static HRESULT WINAPI MMEndpoint_QueryInterface(IMMEndpoint *iface, REFIID riid, void **ppv)
448 MMDevice *This = get_this_from_endpoint(iface);
449 return IMMDevice_QueryInterface((IMMDevice*)This, riid, ppv);
452 static ULONG WINAPI MMEndpoint_AddRef(IMMEndpoint *iface)
454 MMDevice *This = get_this_from_endpoint(iface);
455 return IMMDevice_AddRef((IMMDevice*)This);
458 static ULONG WINAPI MMEndpoint_Release(IMMEndpoint *iface)
460 MMDevice *This = get_this_from_endpoint(iface);
461 return IMMDevice_Release((IMMDevice*)This);
464 static HRESULT WINAPI MMEndpoint_GetDataFlow(IMMEndpoint *iface, EDataFlow *flow)
466 MMDevice *This = get_this_from_endpoint(iface);
473 static const IMMEndpointVtbl MMEndpointVtbl =
475 MMEndpoint_QueryInterface,
478 MMEndpoint_GetDataFlow
481 static HRESULT MMDevCol_Create(IMMDeviceCollection **ppv, EDataFlow flow, DWORD state)
485 This = HeapAlloc(GetProcessHeap(), 0, sizeof(*This));
488 return E_OUTOFMEMORY;
489 This->IMMDeviceCollection_iface.lpVtbl = &MMDevColVtbl;
493 *ppv = &This->IMMDeviceCollection_iface;
497 static void MMDevCol_Destroy(MMDevColImpl *This)
499 HeapFree(GetProcessHeap(), 0, This);
502 static HRESULT WINAPI MMDevCol_QueryInterface(IMMDeviceCollection *iface, REFIID riid, void **ppv)
504 MMDevColImpl *This = impl_from_IMMDeviceCollection(iface);
508 if (IsEqualIID(riid, &IID_IUnknown)
509 || IsEqualIID(riid, &IID_IMMDeviceCollection))
514 return E_NOINTERFACE;
515 IUnknown_AddRef((IUnknown*)*ppv);
519 static ULONG WINAPI MMDevCol_AddRef(IMMDeviceCollection *iface)
521 MMDevColImpl *This = impl_from_IMMDeviceCollection(iface);
522 LONG ref = InterlockedIncrement(&This->ref);
523 TRACE("Refcount now %i\n", ref);
527 static ULONG WINAPI MMDevCol_Release(IMMDeviceCollection *iface)
529 MMDevColImpl *This = impl_from_IMMDeviceCollection(iface);
530 LONG ref = InterlockedDecrement(&This->ref);
531 TRACE("Refcount now %i\n", ref);
533 MMDevCol_Destroy(This);
537 static HRESULT WINAPI MMDevCol_GetCount(IMMDeviceCollection *iface, UINT *numdevs)
539 MMDevColImpl *This = impl_from_IMMDeviceCollection(iface);
542 TRACE("(%p)->(%p)\n", This, numdevs);
547 for (i = 0; i < MMDevice_count; ++i)
549 MMDevice *cur = MMDevice_head[i];
550 if ((cur->flow == This->flow || This->flow == eAll)
551 && (cur->state & This->state))
557 static HRESULT WINAPI MMDevCol_Item(IMMDeviceCollection *iface, UINT n, IMMDevice **dev)
559 MMDevColImpl *This = impl_from_IMMDeviceCollection(iface);
562 TRACE("(%p)->(%u, %p)\n", This, n, dev);
566 for (j = 0; j < MMDevice_count; ++j)
568 MMDevice *cur = MMDevice_head[j];
569 if ((cur->flow == This->flow || This->flow == eAll)
570 && (cur->state & This->state)
573 *dev = (IMMDevice *)cur;
574 IMMDevice_AddRef(*dev);
578 WARN("Could not obtain item %u\n", n);
583 static const IMMDeviceCollectionVtbl MMDevColVtbl =
585 MMDevCol_QueryInterface,
592 static const WCHAR propkey_formatW[] = {
593 '{','%','0','8','X','-','%','0','4','X','-',
594 '%','0','4','X','-','%','0','2','X','%','0','2','X','-',
595 '%','0','2','X','%','0','2','X','%','0','2','X','%','0','2','X',
596 '%','0','2','X','%','0','2','X','}',',','%','d',0 };
598 static HRESULT MMDevPropStore_OpenPropKey(const GUID *guid, DWORD flow, HKEY *propkey)
603 StringFromGUID2(guid, buffer, 39);
604 if ((ret = RegOpenKeyExW(flow == eRender ? key_render : key_capture, buffer, 0, KEY_READ|KEY_WRITE, &key)) != ERROR_SUCCESS)
606 WARN("Opening key %s failed with %u\n", debugstr_w(buffer), ret);
609 ret = RegOpenKeyExW(key, reg_properties, 0, KEY_READ|KEY_WRITE, propkey);
611 if (ret != ERROR_SUCCESS)
613 WARN("Opening key %s failed with %u\n", debugstr_w(reg_properties), ret);
619 HRESULT MMDevice_GetPropValue(const GUID *devguid, DWORD flow, REFPROPERTYKEY key, PROPVARIANT *pv)
622 const GUID *id = &key->fmtid;
628 hr = MMDevPropStore_OpenPropKey(devguid, flow, ®key);
631 wsprintfW( buffer, propkey_formatW, id->Data1, id->Data2, id->Data3,
632 id->Data4[0], id->Data4[1], id->Data4[2], id->Data4[3],
633 id->Data4[4], id->Data4[5], id->Data4[6], id->Data4[7], key->pid );
634 ret = RegGetValueW(regkey, NULL, buffer, RRF_RT_ANY, &type, NULL, &size);
635 if (ret != ERROR_SUCCESS)
637 WARN("Reading %s returned %d\n", debugstr_w(buffer), ret);
639 PropVariantClear(pv);
648 pv->u.pwszVal = CoTaskMemAlloc(size);
652 RegGetValueW(regkey, NULL, buffer, RRF_RT_REG_SZ, NULL, (BYTE*)pv->u.pwszVal, &size);
658 RegGetValueW(regkey, NULL, buffer, RRF_RT_REG_DWORD, NULL, (BYTE*)&pv->u.ulVal, &size);
664 pv->u.blob.cbSize = size;
665 pv->u.blob.pBlobData = CoTaskMemAlloc(size);
666 if (!pv->u.blob.pBlobData)
669 RegGetValueW(regkey, NULL, buffer, RRF_RT_REG_BINARY, NULL, (BYTE*)pv->u.blob.pBlobData, &size);
673 ERR("Unknown/unhandled type: %u\n", type);
674 PropVariantClear(pv);
681 HRESULT MMDevice_SetPropValue(const GUID *devguid, DWORD flow, REFPROPERTYKEY key, REFPROPVARIANT pv)
684 const GUID *id = &key->fmtid;
689 hr = MMDevPropStore_OpenPropKey(devguid, flow, ®key);
692 wsprintfW( buffer, propkey_formatW, id->Data1, id->Data2, id->Data3,
693 id->Data4[0], id->Data4[1], id->Data4[2], id->Data4[3],
694 id->Data4[4], id->Data4[5], id->Data4[6], id->Data4[7], key->pid );
699 ret = RegSetValueExW(regkey, buffer, 0, REG_DWORD, (const BYTE*)&pv->u.ulVal, sizeof(DWORD));
704 ret = RegSetValueExW(regkey, buffer, 0, REG_BINARY, pv->u.blob.pBlobData, pv->u.blob.cbSize);
705 TRACE("Blob %p %u\n", pv->u.blob.pBlobData, pv->u.blob.cbSize);
711 ret = RegSetValueExW(regkey, buffer, 0, REG_SZ, (const BYTE*)pv->u.pwszVal, sizeof(WCHAR)*(1+lstrlenW(pv->u.pwszVal)));
716 FIXME("Unhandled type %u\n", pv->vt);
721 TRACE("Writing %s returned %u\n", debugstr_w(buffer), ret);
727 static void openal_setformat(MMDevice *This, DWORD freq)
730 PROPVARIANT pv = { VT_EMPTY };
732 hr = MMDevice_GetPropValue(&This->devguid, This->flow, &PKEY_AudioEngine_DeviceFormat, &pv);
733 if (SUCCEEDED(hr) && pv.vt == VT_BLOB)
736 pwfx = (WAVEFORMATEX*)pv.u.blob.pBlobData;
737 if (pwfx->nSamplesPerSec != freq)
739 pwfx->nSamplesPerSec = freq;
740 pwfx->nAvgBytesPerSec = freq * pwfx->nBlockAlign;
741 MMDevice_SetPropValue(&This->devguid, This->flow, &PKEY_AudioEngine_DeviceFormat, &pv);
747 WAVEFORMATEXTENSIBLE wfxe;
749 wfxe.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
750 wfxe.Format.nChannels = 2;
751 wfxe.Format.wBitsPerSample = 32;
752 wfxe.Format.nBlockAlign = wfxe.Format.nChannels * wfxe.Format.wBitsPerSample/8;
753 wfxe.Format.nSamplesPerSec = freq;
754 wfxe.Format.nAvgBytesPerSec = wfxe.Format.nSamplesPerSec * wfxe.Format.nBlockAlign;
755 wfxe.Format.cbSize = sizeof(wfxe)-sizeof(WAVEFORMATEX);
756 wfxe.Samples.wValidBitsPerSample = 32;
757 wfxe.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
758 wfxe.dwChannelMask = SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT;
761 pv.u.blob.cbSize = sizeof(wfxe);
762 pv.u.blob.pBlobData = (BYTE*)&wfxe;
763 MMDevice_SetPropValue(&This->devguid, This->flow, &PKEY_AudioEngine_DeviceFormat, &pv);
764 MMDevice_SetPropValue(&This->devguid, This->flow, &PKEY_AudioEngine_OEMFormat, &pv);
768 static int blacklist_pulse;
770 static int blacklist(const char *dev) {
772 if (!strncmp(dev, "OSS ", 4))
775 if (blacklist_pulse && !strncmp(dev, "PulseAudio ", 11))
777 if (!strncmp(dev, "ALSA ", 5) && strstr(dev, "hw:"))
779 if (!strncmp(dev, "PortAudio ", 10))
784 static void pulse_fixup(const char *devstr, const char **defstr, int render) {
788 if (render && !blacklist_pulse && !local_contexts)
791 if (!blacklist_pulse || !devstr || !*devstr)
794 default_pulse = !strncmp(*defstr, "PulseAudio ", 11);
796 while (*devstr && !strncmp(devstr, "PulseAudio ", 11))
797 devstr += strlen(devstr) + 1;
799 /* Could still be a newer version, so check for 1.11 if more devices are enabled */
800 if (render && *devstr) {
801 ALCdevice *dev = palcOpenDevice(devstr);
802 ALCcontext *ctx = palcCreateContext(dev, NULL);
807 ver = palGetString(AL_VERSION);
809 palcDestroyContext(ctx);
811 if (!strcmp(ver, "1.1 ALSOFT 1.11.753")) {
813 palcCloseDevice(dev);
818 palcCloseDevice(dev);
822 ERR("Disabling pulseaudio because of old openal version\n");
823 ERR("Please upgrade to openal-soft v1.12 or newer\n");
825 TRACE("New default: %s\n", devstr);
830 static void openal_scanrender(void)
832 WCHAR name[MAX_PATH];
834 const ALCchar *devstr, *defaultstr;
836 EnterCriticalSection(&openal_crst);
837 if (palcIsExtensionPresent(NULL, "ALC_ENUMERATE_ALL_EXT")) {
838 defaultstr = palcGetString(NULL, ALC_DEFAULT_ALL_DEVICES_SPECIFIER);
839 devstr = palcGetString(NULL, ALC_ALL_DEVICES_SPECIFIER);
841 defaultstr = palcGetString(NULL, ALC_DEFAULT_DEVICE_SPECIFIER);
842 devstr = palcGetString(NULL, ALC_DEVICE_SPECIFIER);
844 pulse_fixup(devstr, &defaultstr, 1);
845 defblacklisted = blacklist(defaultstr);
847 WARN("Disabling blacklist because %s is blacklisted\n", defaultstr);
849 for (; *devstr; devstr += strlen(devstr)+1) {
851 MultiByteToWideChar( CP_UNIXCP, 0, devstr, -1,
852 name, sizeof(name)/sizeof(*name)-1 );
853 name[sizeof(name)/sizeof(*name)-1] = 0;
854 /* Only enable blacklist if the default device isn't blacklisted */
855 if (!defblacklisted && blacklist(devstr)) {
856 WARN("Not adding %s: device is blacklisted\n", devstr);
859 TRACE("Adding %s\n", devstr);
860 dev = palcOpenDevice(devstr);
861 MMDevice_Create(&mmdev, name, NULL, eRender, dev ? DEVICE_STATE_ACTIVE : DEVICE_STATE_NOTPRESENT, !strcmp(devstr, defaultstr));
865 palcGetIntegerv(dev, ALC_FREQUENCY, 1, &freq);
866 openal_setformat(mmdev, freq);
867 palcCloseDevice(dev);
870 WARN("Could not open device: %04x\n", palcGetError(NULL));
872 LeaveCriticalSection(&openal_crst);
875 static void openal_scancapture(void)
877 WCHAR name[MAX_PATH];
879 const ALCchar *devstr, *defaultstr;
882 EnterCriticalSection(&openal_crst);
883 devstr = palcGetString(NULL, ALC_CAPTURE_DEVICE_SPECIFIER);
884 defaultstr = palcGetString(NULL, ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER);
885 pulse_fixup(devstr, &defaultstr, 0);
886 defblacklisted = blacklist(defaultstr);
888 WARN("Disabling blacklist because %s is blacklisted\n", defaultstr);
889 if (devstr && *devstr)
890 for (; *devstr; devstr += strlen(devstr)+1) {
893 MultiByteToWideChar( CP_UNIXCP, 0, devstr, -1,
894 name, sizeof(name)/sizeof(*name)-1 );
895 name[sizeof(name)/sizeof(*name)-1] = 0;
896 if (!defblacklisted && blacklist(devstr)) {
897 WARN("Not adding %s: device is blacklisted\n", devstr);
900 TRACE("Adding %s\n", devstr);
901 dev = palcCaptureOpenDevice(devstr, freq, AL_FORMAT_MONO16, 65536);
902 MMDevice_Create(&mmdev, name, NULL, eCapture, dev ? DEVICE_STATE_ACTIVE : DEVICE_STATE_NOTPRESENT, !strcmp(devstr, defaultstr));
904 openal_setformat(mmdev, freq);
905 palcCaptureCloseDevice(dev);
907 WARN("Could not open device: %04x\n", palcGetError(NULL));
909 LeaveCriticalSection(&openal_crst);
911 #endif /*HAVE_OPENAL*/
913 HRESULT MMDevEnum_Create(REFIID riid, void **ppv)
915 MMDevEnumImpl *This = MMDevEnumerator;
924 This = HeapAlloc(GetProcessHeap(), 0, sizeof(*This));
927 return E_OUTOFMEMORY;
929 This->IMMDeviceEnumerator_iface.lpVtbl = &MMDevEnumVtbl;
930 MMDevEnumerator = This;
932 ret = RegCreateKeyExW(HKEY_LOCAL_MACHINE, software_mmdevapi, 0, NULL, 0, KEY_WRITE|KEY_READ, NULL, &root, NULL);
933 if (ret == ERROR_SUCCESS)
934 ret = RegCreateKeyExW(root, reg_capture, 0, NULL, 0, KEY_READ|KEY_WRITE, NULL, &key_capture, NULL);
935 if (ret == ERROR_SUCCESS)
936 ret = RegCreateKeyExW(root, reg_render, 0, NULL, 0, KEY_READ|KEY_WRITE, NULL, &key_render, NULL);
940 if (ret != ERROR_SUCCESS)
942 RegCloseKey(key_capture);
943 key_render = key_capture = NULL;
944 WARN("Couldn't create key: %u\n", ret);
951 PROPVARIANT pv = { VT_EMPTY };
953 len = sizeof(guidvalue)/sizeof(guidvalue[0]);
954 ret = RegEnumKeyExW(cur, i++, guidvalue, &len, NULL, NULL, NULL, NULL);
955 if (ret == ERROR_NO_MORE_ITEMS)
957 if (cur == key_capture)
966 if (ret != ERROR_SUCCESS)
968 if (SUCCEEDED(CLSIDFromString(guidvalue, &guid))
969 && SUCCEEDED(MMDevice_GetPropValue(&guid, curflow, (const PROPERTYKEY*)&DEVPKEY_Device_FriendlyName, &pv))
970 && pv.vt == VT_LPWSTR)
972 MMDevice_Create(NULL, pv.u.pwszVal, &guid, curflow,
973 DEVICE_STATE_NOTPRESENT, FALSE);
974 CoTaskMemFree(pv.u.pwszVal);
981 openal_scancapture();
984 FIXME("OpenAL support not enabled, application will not find sound devices\n");
986 ERR("OpenAL support not compiled in, application will not find sound devices\n");
987 #endif /*HAVE_OPENAL*/
989 return IUnknown_QueryInterface((IUnknown*)This, riid, ppv);
992 void MMDevEnum_Free(void)
994 while (MMDevice_count)
995 MMDevice_Destroy(MMDevice_head[0]);
996 RegCloseKey(key_render);
997 RegCloseKey(key_capture);
998 key_render = key_capture = NULL;
999 HeapFree(GetProcessHeap(), 0, MMDevEnumerator);
1000 MMDevEnumerator = NULL;
1003 static HRESULT WINAPI MMDevEnum_QueryInterface(IMMDeviceEnumerator *iface, REFIID riid, void **ppv)
1005 MMDevEnumImpl *This = impl_from_IMMDeviceEnumerator(iface);
1009 if (IsEqualIID(riid, &IID_IUnknown)
1010 || IsEqualIID(riid, &IID_IMMDeviceEnumerator))
1015 return E_NOINTERFACE;
1016 IUnknown_AddRef((IUnknown*)*ppv);
1020 static ULONG WINAPI MMDevEnum_AddRef(IMMDeviceEnumerator *iface)
1022 MMDevEnumImpl *This = impl_from_IMMDeviceEnumerator(iface);
1023 LONG ref = InterlockedIncrement(&This->ref);
1024 TRACE("Refcount now %i\n", ref);
1028 static ULONG WINAPI MMDevEnum_Release(IMMDeviceEnumerator *iface)
1030 MMDevEnumImpl *This = impl_from_IMMDeviceEnumerator(iface);
1031 LONG ref = InterlockedDecrement(&This->ref);
1034 TRACE("Refcount now %i\n", ref);
1038 static HRESULT WINAPI MMDevEnum_EnumAudioEndpoints(IMMDeviceEnumerator *iface, EDataFlow flow, DWORD mask, IMMDeviceCollection **devices)
1040 MMDevEnumImpl *This = impl_from_IMMDeviceEnumerator(iface);
1041 TRACE("(%p)->(%u,%u,%p)\n", This, flow, mask, devices);
1045 if (flow >= EDataFlow_enum_count)
1046 return E_INVALIDARG;
1047 if (mask & ~DEVICE_STATEMASK_ALL)
1048 return E_INVALIDARG;
1049 return MMDevCol_Create(devices, flow, mask);
1052 static HRESULT WINAPI MMDevEnum_GetDefaultAudioEndpoint(IMMDeviceEnumerator *iface, EDataFlow flow, ERole role, IMMDevice **device)
1054 MMDevEnumImpl *This = impl_from_IMMDeviceEnumerator(iface);
1055 TRACE("(%p)->(%u,%u,%p)\n", This, flow, role, device);
1061 if (flow == eRender)
1062 *device = (IMMDevice*)MMDevice_def_play;
1063 else if (flow == eCapture)
1064 *device = (IMMDevice*)MMDevice_def_rec;
1067 WARN("Unknown flow %u\n", flow);
1068 return E_INVALIDARG;
1073 IMMDevice_AddRef(*device);
1077 static HRESULT WINAPI MMDevEnum_GetDevice(IMMDeviceEnumerator *iface, const WCHAR *name, IMMDevice **device)
1079 MMDevEnumImpl *This = impl_from_IMMDeviceEnumerator(iface);
1081 IMMDevice *dev = NULL;
1083 TRACE("(%p)->(%s,%p)\n", This, debugstr_w(name), device);
1084 for (i = 0; i < MMDevice_count; ++i)
1087 dev = (IMMDevice*)MMDevice_head[i];
1088 IMMDevice_GetId(dev, &str);
1090 if (str && !lstrcmpW(str, name))
1099 IUnknown_AddRef(dev);
1103 WARN("Could not find device %s\n", debugstr_w(name));
1107 static HRESULT WINAPI MMDevEnum_RegisterEndpointNotificationCallback(IMMDeviceEnumerator *iface, IMMNotificationClient *client)
1109 MMDevEnumImpl *This = impl_from_IMMDeviceEnumerator(iface);
1110 TRACE("(%p)->(%p)\n", This, client);
1115 static HRESULT WINAPI MMDevEnum_UnregisterEndpointNotificationCallback(IMMDeviceEnumerator *iface, IMMNotificationClient *client)
1117 MMDevEnumImpl *This = impl_from_IMMDeviceEnumerator(iface);
1118 TRACE("(%p)->(%p)\n", This, client);
1123 static const IMMDeviceEnumeratorVtbl MMDevEnumVtbl =
1125 MMDevEnum_QueryInterface,
1128 MMDevEnum_EnumAudioEndpoints,
1129 MMDevEnum_GetDefaultAudioEndpoint,
1130 MMDevEnum_GetDevice,
1131 MMDevEnum_RegisterEndpointNotificationCallback,
1132 MMDevEnum_UnregisterEndpointNotificationCallback
1135 static HRESULT MMDevPropStore_Create(MMDevice *parent, DWORD access, IPropertyStore **ppv)
1137 MMDevPropStore *This;
1138 if (access != STGM_READ
1139 && access != STGM_WRITE
1140 && access != STGM_READWRITE)
1142 WARN("Invalid access %08x\n", access);
1143 return E_INVALIDARG;
1145 This = HeapAlloc(GetProcessHeap(), 0, sizeof(*This));
1146 *ppv = &This->IPropertyStore_iface;
1148 return E_OUTOFMEMORY;
1149 This->IPropertyStore_iface.lpVtbl = &MMDevPropVtbl;
1151 This->parent = parent;
1152 This->access = access;
1156 static void MMDevPropStore_Destroy(MMDevPropStore *This)
1158 HeapFree(GetProcessHeap(), 0, This);
1161 static HRESULT WINAPI MMDevPropStore_QueryInterface(IPropertyStore *iface, REFIID riid, void **ppv)
1163 MMDevPropStore *This = impl_from_IPropertyStore(iface);
1167 if (IsEqualIID(riid, &IID_IUnknown)
1168 || IsEqualIID(riid, &IID_IPropertyStore))
1173 return E_NOINTERFACE;
1174 IUnknown_AddRef((IUnknown*)*ppv);
1178 static ULONG WINAPI MMDevPropStore_AddRef(IPropertyStore *iface)
1180 MMDevPropStore *This = impl_from_IPropertyStore(iface);
1181 LONG ref = InterlockedIncrement(&This->ref);
1182 TRACE("Refcount now %i\n", ref);
1186 static ULONG WINAPI MMDevPropStore_Release(IPropertyStore *iface)
1188 MMDevPropStore *This = impl_from_IPropertyStore(iface);
1189 LONG ref = InterlockedDecrement(&This->ref);
1190 TRACE("Refcount now %i\n", ref);
1192 MMDevPropStore_Destroy(This);
1196 static HRESULT WINAPI MMDevPropStore_GetCount(IPropertyStore *iface, DWORD *nprops)
1198 MMDevPropStore *This = impl_from_IPropertyStore(iface);
1204 TRACE("(%p)->(%p)\n", iface, nprops);
1207 hr = MMDevPropStore_OpenPropKey(&This->parent->devguid, This->parent->flow, &propkey);
1212 DWORD len = sizeof(buffer)/sizeof(*buffer);
1213 if (RegEnumKeyExW(propkey, i, buffer, &len, NULL, NULL, NULL, NULL) != ERROR_SUCCESS)
1217 RegCloseKey(propkey);
1218 TRACE("Returning %i\n", i);
1223 static HRESULT WINAPI MMDevPropStore_GetAt(IPropertyStore *iface, DWORD prop, PROPERTYKEY *key)
1225 MMDevPropStore *This = impl_from_IPropertyStore(iface);
1227 DWORD len = sizeof(buffer)/sizeof(*buffer);
1231 TRACE("(%p)->(%u,%p)\n", iface, prop, key);
1235 hr = MMDevPropStore_OpenPropKey(&This->parent->devguid, This->parent->flow, &propkey);
1239 if (RegEnumKeyExW(propkey, prop, buffer, &len, NULL, NULL, NULL, NULL) != ERROR_SUCCESS
1242 WARN("GetAt %u failed\n", prop);
1243 return E_INVALIDARG;
1245 RegCloseKey(propkey);
1247 CLSIDFromString(buffer, &key->fmtid);
1248 key->pid = atoiW(&buffer[40]);
1252 static HRESULT WINAPI MMDevPropStore_GetValue(IPropertyStore *iface, REFPROPERTYKEY key, PROPVARIANT *pv)
1254 MMDevPropStore *This = impl_from_IPropertyStore(iface);
1255 TRACE("(%p)->(\"%s,%u\", %p\n", This, debugstr_guid(&key->fmtid), key ? key->pid : 0, pv);
1259 if (This->access != STGM_READ
1260 && This->access != STGM_READWRITE)
1261 return STG_E_ACCESSDENIED;
1264 if (IsEqualPropertyKey(*key, PKEY_AudioEndpoint_GUID))
1267 pv->u.pwszVal = CoTaskMemAlloc(39 * sizeof(WCHAR));
1269 return E_OUTOFMEMORY;
1270 StringFromGUID2(&This->parent->devguid, pv->u.pwszVal, 39);
1274 return MMDevice_GetPropValue(&This->parent->devguid, This->parent->flow, key, pv);
1277 static HRESULT WINAPI MMDevPropStore_SetValue(IPropertyStore *iface, REFPROPERTYKEY key, REFPROPVARIANT pv)
1279 MMDevPropStore *This = impl_from_IPropertyStore(iface);
1284 if (This->access != STGM_WRITE
1285 && This->access != STGM_READWRITE)
1286 return STG_E_ACCESSDENIED;
1287 return MMDevice_SetPropValue(&This->parent->devguid, This->parent->flow, key, pv);
1290 static HRESULT WINAPI MMDevPropStore_Commit(IPropertyStore *iface)
1296 static const IPropertyStoreVtbl MMDevPropVtbl =
1298 MMDevPropStore_QueryInterface,
1299 MMDevPropStore_AddRef,
1300 MMDevPropStore_Release,
1301 MMDevPropStore_GetCount,
1302 MMDevPropStore_GetAt,
1303 MMDevPropStore_GetValue,
1304 MMDevPropStore_SetValue,
1305 MMDevPropStore_Commit
1309 /* Property bag for IBaseFilter activation */
1310 static HRESULT WINAPI PB_QueryInterface(IPropertyBag *iface, REFIID riid, void **ppv)
1312 ERR("Should not be called\n");
1314 return E_NOINTERFACE;
1317 static ULONG WINAPI PB_AddRef(IPropertyBag *iface)
1319 ERR("Should not be called\n");
1323 static ULONG WINAPI PB_Release(IPropertyBag *iface)
1325 ERR("Should not be called\n");
1329 static HRESULT WINAPI PB_Read(IPropertyBag *iface, LPCOLESTR name, VARIANT *var, IErrorLog *log)
1331 static const WCHAR dsguid[] = { 'D','S','G','u','i','d', 0 };
1332 IPropertyBagImpl *This = impl_from_IPropertyBag(iface);
1333 TRACE("Trying to read %s, type %u\n", debugstr_w(name), var->n1.n2.vt);
1334 if (!lstrcmpW(name, dsguid))
1337 StringFromGUID2(&This->devguid, guidstr,sizeof(guidstr)/sizeof(*guidstr));
1338 var->n1.n2.vt = VT_BSTR;
1339 var->n1.n2.n3.bstrVal = SysAllocString(guidstr);
1342 ERR("Unknown property '%s' queried\n", debugstr_w(name));
1346 static HRESULT WINAPI PB_Write(IPropertyBag *iface, LPCOLESTR name, VARIANT *var)
1348 ERR("Should not be called\n");
1352 static const IPropertyBagVtbl PB_Vtbl =