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->IMMDevice_iface.lpVtbl = &MMDeviceVtbl;
175 cur->IMMEndpoint_iface.lpVtbl = &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 inline MMDevice *impl_from_IMMDevice(IMMDevice *iface)
251 return CONTAINING_RECORD(iface, MMDevice, IMMDevice_iface);
254 static HRESULT WINAPI MMDevice_QueryInterface(IMMDevice *iface, REFIID riid, void **ppv)
256 MMDevice *This = impl_from_IMMDevice(iface);
257 TRACE("(%p)->(%s,%p)\n", iface, debugstr_guid(riid), ppv);
262 if (IsEqualIID(riid, &IID_IUnknown)
263 || IsEqualIID(riid, &IID_IMMDevice))
265 else if (IsEqualIID(riid, &IID_IMMEndpoint))
266 *ppv = &This->IMMEndpoint_iface;
269 IUnknown_AddRef((IUnknown*)*ppv);
272 WARN("Unknown interface %s\n", debugstr_guid(riid));
273 return E_NOINTERFACE;
276 static ULONG WINAPI MMDevice_AddRef(IMMDevice *iface)
278 MMDevice *This = impl_from_IMMDevice(iface);
281 ref = InterlockedIncrement(&This->ref);
282 TRACE("Refcount now %i\n", ref);
286 static ULONG WINAPI MMDevice_Release(IMMDevice *iface)
288 MMDevice *This = impl_from_IMMDevice(iface);
291 ref = InterlockedDecrement(&This->ref);
292 TRACE("Refcount now %i\n", ref);
296 static HRESULT WINAPI MMDevice_Activate(IMMDevice *iface, REFIID riid, DWORD clsctx, PROPVARIANT *params, void **ppv)
298 HRESULT hr = E_NOINTERFACE;
301 MMDevice *This = impl_from_IMMDevice(iface);
303 TRACE("(%p)->(%p,%x,%p,%p)\n", iface, riid, clsctx, params, ppv);
310 WARN("OpenAL is still not loaded\n");
311 hr = AUDCLNT_E_SERVICE_NOT_RUNNING;
313 else if (IsEqualIID(riid, &IID_IAudioClient))
314 hr = AudioClient_Create(This, (IAudioClient**)ppv);
315 else if (IsEqualIID(riid, &IID_IAudioEndpointVolume))
316 hr = AudioEndpointVolume_Create(This, (IAudioEndpointVolume**)ppv);
317 else if (IsEqualIID(riid, &IID_IAudioSessionManager)
318 || IsEqualIID(riid, &IID_IAudioSessionManager2))
320 FIXME("IID_IAudioSessionManager unsupported\n");
322 else if (IsEqualIID(riid, &IID_IBaseFilter))
324 if (This->flow == eRender)
325 hr = CoCreateInstance(&CLSID_DSoundRender, NULL, clsctx, riid, ppv);
327 ERR("Not supported for recording?\n");
330 IPersistPropertyBag *ppb;
331 hr = IUnknown_QueryInterface((IUnknown*)*ppv, &IID_IPersistPropertyBag, (void*)&ppb);
334 /* ::Load cannot assume the interface stays alive after the function returns,
335 * so just create the interface on the stack, saves a lot of complicated code */
336 IPropertyBagImpl bag = { { &PB_Vtbl }, This->devguid };
337 hr = IPersistPropertyBag_Load(ppb, &bag.IPropertyBag_iface, NULL);
338 IPersistPropertyBag_Release(ppb);
340 IBaseFilter_Release((IBaseFilter*)*ppv);
344 FIXME("Wine doesn't support IPersistPropertyBag on DSoundRender yet, ignoring..\n");
349 else if (IsEqualIID(riid, &IID_IDeviceTopology))
351 FIXME("IID_IDeviceTopology unsupported\n");
353 else if (IsEqualIID(riid, &IID_IDirectSound)
354 || IsEqualIID(riid, &IID_IDirectSound8))
356 if (This->flow == eRender)
357 hr = CoCreateInstance(&CLSID_DirectSound8, NULL, clsctx, riid, ppv);
360 hr = IDirectSound_Initialize((IDirectSound*)*ppv, &This->devguid);
362 IDirectSound_Release((IDirectSound*)*ppv);
365 else if (IsEqualIID(riid, &IID_IDirectSoundCapture)
366 || IsEqualIID(riid, &IID_IDirectSoundCapture8))
368 if (This->flow == eCapture)
369 hr = CoCreateInstance(&CLSID_DirectSoundCapture8, NULL, clsctx, riid, ppv);
372 hr = IDirectSoundCapture_Initialize((IDirectSoundCapture*)*ppv, &This->devguid);
374 IDirectSoundCapture_Release((IDirectSoundCapture*)*ppv);
378 ERR("Invalid/unknown iid %s\n", debugstr_guid(riid));
380 if (!ppv) return E_POINTER;
381 hr = AUDCLNT_E_SERVICE_NOT_RUNNING;
387 TRACE("Returning %08x\n", hr);
391 static HRESULT WINAPI MMDevice_OpenPropertyStore(IMMDevice *iface, DWORD access, IPropertyStore **ppv)
393 MMDevice *This = impl_from_IMMDevice(iface);
394 TRACE("(%p)->(%x,%p)\n", This, access, ppv);
398 return MMDevPropStore_Create(This, access, ppv);
401 static HRESULT WINAPI MMDevice_GetId(IMMDevice *iface, WCHAR **itemid)
403 MMDevice *This = impl_from_IMMDevice(iface);
405 GUID *id = &This->devguid;
406 static const WCHAR formatW[] = { '{','0','.','0','.','0','.','0','0','0','0','0','0','0','0','}','.',
407 '{','%','0','8','X','-','%','0','4','X','-',
408 '%','0','4','X','-','%','0','2','X','%','0','2','X','-',
409 '%','0','2','X','%','0','2','X','%','0','2','X','%','0','2','X',
410 '%','0','2','X','%','0','2','X','}',0 };
412 TRACE("(%p)->(%p)\n", This, itemid);
415 *itemid = str = CoTaskMemAlloc(56 * sizeof(WCHAR));
417 return E_OUTOFMEMORY;
418 wsprintfW( str, formatW, id->Data1, id->Data2, id->Data3,
419 id->Data4[0], id->Data4[1], id->Data4[2], id->Data4[3],
420 id->Data4[4], id->Data4[5], id->Data4[6], id->Data4[7] );
424 static HRESULT WINAPI MMDevice_GetState(IMMDevice *iface, DWORD *state)
426 MMDevice *This = impl_from_IMMDevice(iface);
427 TRACE("(%p)->(%p)\n", iface, state);
431 *state = This->state;
435 static const IMMDeviceVtbl MMDeviceVtbl =
437 MMDevice_QueryInterface,
441 MMDevice_OpenPropertyStore,
446 static inline MMDevice *impl_from_IMMEndpoint(IMMEndpoint *iface)
448 return CONTAINING_RECORD(iface, MMDevice, IMMEndpoint_iface);
451 static HRESULT WINAPI MMEndpoint_QueryInterface(IMMEndpoint *iface, REFIID riid, void **ppv)
453 MMDevice *This = impl_from_IMMEndpoint(iface);
454 return IMMDevice_QueryInterface(&This->IMMDevice_iface, riid, ppv);
457 static ULONG WINAPI MMEndpoint_AddRef(IMMEndpoint *iface)
459 MMDevice *This = impl_from_IMMEndpoint(iface);
460 return IMMDevice_AddRef(&This->IMMDevice_iface);
463 static ULONG WINAPI MMEndpoint_Release(IMMEndpoint *iface)
465 MMDevice *This = impl_from_IMMEndpoint(iface);
466 return IMMDevice_Release(&This->IMMDevice_iface);
469 static HRESULT WINAPI MMEndpoint_GetDataFlow(IMMEndpoint *iface, EDataFlow *flow)
471 MMDevice *This = impl_from_IMMEndpoint(iface);
478 static const IMMEndpointVtbl MMEndpointVtbl =
480 MMEndpoint_QueryInterface,
483 MMEndpoint_GetDataFlow
486 static HRESULT MMDevCol_Create(IMMDeviceCollection **ppv, EDataFlow flow, DWORD state)
490 This = HeapAlloc(GetProcessHeap(), 0, sizeof(*This));
493 return E_OUTOFMEMORY;
494 This->IMMDeviceCollection_iface.lpVtbl = &MMDevColVtbl;
498 *ppv = &This->IMMDeviceCollection_iface;
502 static void MMDevCol_Destroy(MMDevColImpl *This)
504 HeapFree(GetProcessHeap(), 0, This);
507 static HRESULT WINAPI MMDevCol_QueryInterface(IMMDeviceCollection *iface, REFIID riid, void **ppv)
509 MMDevColImpl *This = impl_from_IMMDeviceCollection(iface);
513 if (IsEqualIID(riid, &IID_IUnknown)
514 || IsEqualIID(riid, &IID_IMMDeviceCollection))
519 return E_NOINTERFACE;
520 IUnknown_AddRef((IUnknown*)*ppv);
524 static ULONG WINAPI MMDevCol_AddRef(IMMDeviceCollection *iface)
526 MMDevColImpl *This = impl_from_IMMDeviceCollection(iface);
527 LONG ref = InterlockedIncrement(&This->ref);
528 TRACE("Refcount now %i\n", ref);
532 static ULONG WINAPI MMDevCol_Release(IMMDeviceCollection *iface)
534 MMDevColImpl *This = impl_from_IMMDeviceCollection(iface);
535 LONG ref = InterlockedDecrement(&This->ref);
536 TRACE("Refcount now %i\n", ref);
538 MMDevCol_Destroy(This);
542 static HRESULT WINAPI MMDevCol_GetCount(IMMDeviceCollection *iface, UINT *numdevs)
544 MMDevColImpl *This = impl_from_IMMDeviceCollection(iface);
547 TRACE("(%p)->(%p)\n", This, numdevs);
552 for (i = 0; i < MMDevice_count; ++i)
554 MMDevice *cur = MMDevice_head[i];
555 if ((cur->flow == This->flow || This->flow == eAll)
556 && (cur->state & This->state))
562 static HRESULT WINAPI MMDevCol_Item(IMMDeviceCollection *iface, UINT n, IMMDevice **dev)
564 MMDevColImpl *This = impl_from_IMMDeviceCollection(iface);
567 TRACE("(%p)->(%u, %p)\n", This, n, dev);
571 for (j = 0; j < MMDevice_count; ++j)
573 MMDevice *cur = MMDevice_head[j];
574 if ((cur->flow == This->flow || This->flow == eAll)
575 && (cur->state & This->state)
578 *dev = &cur->IMMDevice_iface;
579 IMMDevice_AddRef(*dev);
583 WARN("Could not obtain item %u\n", n);
588 static const IMMDeviceCollectionVtbl MMDevColVtbl =
590 MMDevCol_QueryInterface,
597 static const WCHAR propkey_formatW[] = {
598 '{','%','0','8','X','-','%','0','4','X','-',
599 '%','0','4','X','-','%','0','2','X','%','0','2','X','-',
600 '%','0','2','X','%','0','2','X','%','0','2','X','%','0','2','X',
601 '%','0','2','X','%','0','2','X','}',',','%','d',0 };
603 static HRESULT MMDevPropStore_OpenPropKey(const GUID *guid, DWORD flow, HKEY *propkey)
608 StringFromGUID2(guid, buffer, 39);
609 if ((ret = RegOpenKeyExW(flow == eRender ? key_render : key_capture, buffer, 0, KEY_READ|KEY_WRITE, &key)) != ERROR_SUCCESS)
611 WARN("Opening key %s failed with %u\n", debugstr_w(buffer), ret);
614 ret = RegOpenKeyExW(key, reg_properties, 0, KEY_READ|KEY_WRITE, propkey);
616 if (ret != ERROR_SUCCESS)
618 WARN("Opening key %s failed with %u\n", debugstr_w(reg_properties), ret);
624 HRESULT MMDevice_GetPropValue(const GUID *devguid, DWORD flow, REFPROPERTYKEY key, PROPVARIANT *pv)
627 const GUID *id = &key->fmtid;
633 hr = MMDevPropStore_OpenPropKey(devguid, flow, ®key);
636 wsprintfW( buffer, propkey_formatW, id->Data1, id->Data2, id->Data3,
637 id->Data4[0], id->Data4[1], id->Data4[2], id->Data4[3],
638 id->Data4[4], id->Data4[5], id->Data4[6], id->Data4[7], key->pid );
639 ret = RegGetValueW(regkey, NULL, buffer, RRF_RT_ANY, &type, NULL, &size);
640 if (ret != ERROR_SUCCESS)
642 WARN("Reading %s returned %d\n", debugstr_w(buffer), ret);
644 PropVariantClear(pv);
653 pv->u.pwszVal = CoTaskMemAlloc(size);
657 RegGetValueW(regkey, NULL, buffer, RRF_RT_REG_SZ, NULL, (BYTE*)pv->u.pwszVal, &size);
663 RegGetValueW(regkey, NULL, buffer, RRF_RT_REG_DWORD, NULL, (BYTE*)&pv->u.ulVal, &size);
669 pv->u.blob.cbSize = size;
670 pv->u.blob.pBlobData = CoTaskMemAlloc(size);
671 if (!pv->u.blob.pBlobData)
674 RegGetValueW(regkey, NULL, buffer, RRF_RT_REG_BINARY, NULL, (BYTE*)pv->u.blob.pBlobData, &size);
678 ERR("Unknown/unhandled type: %u\n", type);
679 PropVariantClear(pv);
686 HRESULT MMDevice_SetPropValue(const GUID *devguid, DWORD flow, REFPROPERTYKEY key, REFPROPVARIANT pv)
689 const GUID *id = &key->fmtid;
694 hr = MMDevPropStore_OpenPropKey(devguid, flow, ®key);
697 wsprintfW( buffer, propkey_formatW, id->Data1, id->Data2, id->Data3,
698 id->Data4[0], id->Data4[1], id->Data4[2], id->Data4[3],
699 id->Data4[4], id->Data4[5], id->Data4[6], id->Data4[7], key->pid );
704 ret = RegSetValueExW(regkey, buffer, 0, REG_DWORD, (const BYTE*)&pv->u.ulVal, sizeof(DWORD));
709 ret = RegSetValueExW(regkey, buffer, 0, REG_BINARY, pv->u.blob.pBlobData, pv->u.blob.cbSize);
710 TRACE("Blob %p %u\n", pv->u.blob.pBlobData, pv->u.blob.cbSize);
716 ret = RegSetValueExW(regkey, buffer, 0, REG_SZ, (const BYTE*)pv->u.pwszVal, sizeof(WCHAR)*(1+lstrlenW(pv->u.pwszVal)));
721 FIXME("Unhandled type %u\n", pv->vt);
726 TRACE("Writing %s returned %u\n", debugstr_w(buffer), ret);
732 static void openal_setformat(MMDevice *This, DWORD freq)
735 PROPVARIANT pv = { VT_EMPTY };
737 hr = MMDevice_GetPropValue(&This->devguid, This->flow, &PKEY_AudioEngine_DeviceFormat, &pv);
738 if (SUCCEEDED(hr) && pv.vt == VT_BLOB)
741 pwfx = (WAVEFORMATEX*)pv.u.blob.pBlobData;
742 if (pwfx->nSamplesPerSec != freq)
744 pwfx->nSamplesPerSec = freq;
745 pwfx->nAvgBytesPerSec = freq * pwfx->nBlockAlign;
746 MMDevice_SetPropValue(&This->devguid, This->flow, &PKEY_AudioEngine_DeviceFormat, &pv);
752 WAVEFORMATEXTENSIBLE wfxe;
754 wfxe.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
755 wfxe.Format.nChannels = 2;
756 wfxe.Format.wBitsPerSample = 32;
757 wfxe.Format.nBlockAlign = wfxe.Format.nChannels * wfxe.Format.wBitsPerSample/8;
758 wfxe.Format.nSamplesPerSec = freq;
759 wfxe.Format.nAvgBytesPerSec = wfxe.Format.nSamplesPerSec * wfxe.Format.nBlockAlign;
760 wfxe.Format.cbSize = sizeof(wfxe)-sizeof(WAVEFORMATEX);
761 wfxe.Samples.wValidBitsPerSample = 32;
762 wfxe.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
763 wfxe.dwChannelMask = SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT;
766 pv.u.blob.cbSize = sizeof(wfxe);
767 pv.u.blob.pBlobData = (BYTE*)&wfxe;
768 MMDevice_SetPropValue(&This->devguid, This->flow, &PKEY_AudioEngine_DeviceFormat, &pv);
769 MMDevice_SetPropValue(&This->devguid, This->flow, &PKEY_AudioEngine_OEMFormat, &pv);
773 static int blacklist_pulse;
775 static int blacklist(const char *dev) {
777 if (!strncmp(dev, "OSS ", 4))
780 if (blacklist_pulse && !strncmp(dev, "PulseAudio ", 11))
782 if (!strncmp(dev, "ALSA ", 5) && strstr(dev, "hw:"))
784 if (!strncmp(dev, "PortAudio ", 10))
789 static void pulse_fixup(const char *devstr, const char **defstr, int render) {
793 if (render && !blacklist_pulse && !local_contexts)
796 if (!blacklist_pulse || !devstr || !*devstr)
799 default_pulse = !strncmp(*defstr, "PulseAudio ", 11);
801 while (*devstr && !strncmp(devstr, "PulseAudio ", 11))
802 devstr += strlen(devstr) + 1;
804 /* Could still be a newer version, so check for 1.11 if more devices are enabled */
805 if (render && *devstr) {
806 ALCdevice *dev = palcOpenDevice(devstr);
807 ALCcontext *ctx = palcCreateContext(dev, NULL);
812 ver = palGetString(AL_VERSION);
814 palcDestroyContext(ctx);
816 if (!strcmp(ver, "1.1 ALSOFT 1.11.753")) {
818 palcCloseDevice(dev);
823 palcCloseDevice(dev);
827 ERR("Disabling pulseaudio because of old openal version\n");
828 ERR("Please upgrade to openal-soft v1.12 or newer\n");
830 TRACE("New default: %s\n", devstr);
835 static void openal_scanrender(void)
837 WCHAR name[MAX_PATH];
839 const ALCchar *devstr, *defaultstr;
841 EnterCriticalSection(&openal_crst);
842 if (palcIsExtensionPresent(NULL, "ALC_ENUMERATE_ALL_EXT")) {
843 defaultstr = palcGetString(NULL, ALC_DEFAULT_ALL_DEVICES_SPECIFIER);
844 devstr = palcGetString(NULL, ALC_ALL_DEVICES_SPECIFIER);
846 defaultstr = palcGetString(NULL, ALC_DEFAULT_DEVICE_SPECIFIER);
847 devstr = palcGetString(NULL, ALC_DEVICE_SPECIFIER);
849 pulse_fixup(devstr, &defaultstr, 1);
850 defblacklisted = blacklist(defaultstr);
852 WARN("Disabling blacklist because %s is blacklisted\n", defaultstr);
854 for (; *devstr; devstr += strlen(devstr)+1) {
856 MultiByteToWideChar( CP_UNIXCP, 0, devstr, -1,
857 name, sizeof(name)/sizeof(*name)-1 );
858 name[sizeof(name)/sizeof(*name)-1] = 0;
859 /* Only enable blacklist if the default device isn't blacklisted */
860 if (!defblacklisted && blacklist(devstr)) {
861 WARN("Not adding %s: device is blacklisted\n", devstr);
864 TRACE("Adding %s\n", devstr);
865 dev = palcOpenDevice(devstr);
866 MMDevice_Create(&mmdev, name, NULL, eRender, dev ? DEVICE_STATE_ACTIVE : DEVICE_STATE_NOTPRESENT, !strcmp(devstr, defaultstr));
870 palcGetIntegerv(dev, ALC_FREQUENCY, 1, &freq);
871 openal_setformat(mmdev, freq);
872 palcCloseDevice(dev);
875 WARN("Could not open device: %04x\n", palcGetError(NULL));
877 LeaveCriticalSection(&openal_crst);
880 static void openal_scancapture(void)
882 WCHAR name[MAX_PATH];
884 const ALCchar *devstr, *defaultstr;
887 EnterCriticalSection(&openal_crst);
888 devstr = palcGetString(NULL, ALC_CAPTURE_DEVICE_SPECIFIER);
889 defaultstr = palcGetString(NULL, ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER);
890 pulse_fixup(devstr, &defaultstr, 0);
891 defblacklisted = blacklist(defaultstr);
893 WARN("Disabling blacklist because %s is blacklisted\n", defaultstr);
894 if (devstr && *devstr)
895 for (; *devstr; devstr += strlen(devstr)+1) {
898 MultiByteToWideChar( CP_UNIXCP, 0, devstr, -1,
899 name, sizeof(name)/sizeof(*name)-1 );
900 name[sizeof(name)/sizeof(*name)-1] = 0;
901 if (!defblacklisted && blacklist(devstr)) {
902 WARN("Not adding %s: device is blacklisted\n", devstr);
905 TRACE("Adding %s\n", devstr);
906 dev = palcCaptureOpenDevice(devstr, freq, AL_FORMAT_MONO16, 65536);
907 MMDevice_Create(&mmdev, name, NULL, eCapture, dev ? DEVICE_STATE_ACTIVE : DEVICE_STATE_NOTPRESENT, !strcmp(devstr, defaultstr));
909 openal_setformat(mmdev, freq);
910 palcCaptureCloseDevice(dev);
912 WARN("Could not open device: %04x\n", palcGetError(NULL));
914 LeaveCriticalSection(&openal_crst);
916 #endif /*HAVE_OPENAL*/
918 HRESULT MMDevEnum_Create(REFIID riid, void **ppv)
920 MMDevEnumImpl *This = MMDevEnumerator;
929 This = HeapAlloc(GetProcessHeap(), 0, sizeof(*This));
932 return E_OUTOFMEMORY;
934 This->IMMDeviceEnumerator_iface.lpVtbl = &MMDevEnumVtbl;
935 MMDevEnumerator = This;
937 ret = RegCreateKeyExW(HKEY_LOCAL_MACHINE, software_mmdevapi, 0, NULL, 0, KEY_WRITE|KEY_READ, NULL, &root, NULL);
938 if (ret == ERROR_SUCCESS)
939 ret = RegCreateKeyExW(root, reg_capture, 0, NULL, 0, KEY_READ|KEY_WRITE, NULL, &key_capture, NULL);
940 if (ret == ERROR_SUCCESS)
941 ret = RegCreateKeyExW(root, reg_render, 0, NULL, 0, KEY_READ|KEY_WRITE, NULL, &key_render, NULL);
945 if (ret != ERROR_SUCCESS)
947 RegCloseKey(key_capture);
948 key_render = key_capture = NULL;
949 WARN("Couldn't create key: %u\n", ret);
956 PROPVARIANT pv = { VT_EMPTY };
958 len = sizeof(guidvalue)/sizeof(guidvalue[0]);
959 ret = RegEnumKeyExW(cur, i++, guidvalue, &len, NULL, NULL, NULL, NULL);
960 if (ret == ERROR_NO_MORE_ITEMS)
962 if (cur == key_capture)
971 if (ret != ERROR_SUCCESS)
973 if (SUCCEEDED(CLSIDFromString(guidvalue, &guid))
974 && SUCCEEDED(MMDevice_GetPropValue(&guid, curflow, (const PROPERTYKEY*)&DEVPKEY_Device_FriendlyName, &pv))
975 && pv.vt == VT_LPWSTR)
977 MMDevice_Create(NULL, pv.u.pwszVal, &guid, curflow,
978 DEVICE_STATE_NOTPRESENT, FALSE);
979 CoTaskMemFree(pv.u.pwszVal);
986 openal_scancapture();
989 FIXME("OpenAL support not enabled, application will not find sound devices\n");
991 ERR("OpenAL support not compiled in, application will not find sound devices\n");
992 #endif /*HAVE_OPENAL*/
994 return IUnknown_QueryInterface((IUnknown*)This, riid, ppv);
997 void MMDevEnum_Free(void)
999 while (MMDevice_count)
1000 MMDevice_Destroy(MMDevice_head[0]);
1001 RegCloseKey(key_render);
1002 RegCloseKey(key_capture);
1003 key_render = key_capture = NULL;
1004 HeapFree(GetProcessHeap(), 0, MMDevEnumerator);
1005 MMDevEnumerator = NULL;
1008 static HRESULT WINAPI MMDevEnum_QueryInterface(IMMDeviceEnumerator *iface, REFIID riid, void **ppv)
1010 MMDevEnumImpl *This = impl_from_IMMDeviceEnumerator(iface);
1014 if (IsEqualIID(riid, &IID_IUnknown)
1015 || IsEqualIID(riid, &IID_IMMDeviceEnumerator))
1020 return E_NOINTERFACE;
1021 IUnknown_AddRef((IUnknown*)*ppv);
1025 static ULONG WINAPI MMDevEnum_AddRef(IMMDeviceEnumerator *iface)
1027 MMDevEnumImpl *This = impl_from_IMMDeviceEnumerator(iface);
1028 LONG ref = InterlockedIncrement(&This->ref);
1029 TRACE("Refcount now %i\n", ref);
1033 static ULONG WINAPI MMDevEnum_Release(IMMDeviceEnumerator *iface)
1035 MMDevEnumImpl *This = impl_from_IMMDeviceEnumerator(iface);
1036 LONG ref = InterlockedDecrement(&This->ref);
1039 TRACE("Refcount now %i\n", ref);
1043 static HRESULT WINAPI MMDevEnum_EnumAudioEndpoints(IMMDeviceEnumerator *iface, EDataFlow flow, DWORD mask, IMMDeviceCollection **devices)
1045 MMDevEnumImpl *This = impl_from_IMMDeviceEnumerator(iface);
1046 TRACE("(%p)->(%u,%u,%p)\n", This, flow, mask, devices);
1050 if (flow >= EDataFlow_enum_count)
1051 return E_INVALIDARG;
1052 if (mask & ~DEVICE_STATEMASK_ALL)
1053 return E_INVALIDARG;
1054 return MMDevCol_Create(devices, flow, mask);
1057 static HRESULT WINAPI MMDevEnum_GetDefaultAudioEndpoint(IMMDeviceEnumerator *iface, EDataFlow flow, ERole role, IMMDevice **device)
1059 MMDevEnumImpl *This = impl_from_IMMDeviceEnumerator(iface);
1060 TRACE("(%p)->(%u,%u,%p)\n", This, flow, role, device);
1066 if (flow == eRender)
1067 *device = &MMDevice_def_play->IMMDevice_iface;
1068 else if (flow == eCapture)
1069 *device = &MMDevice_def_rec->IMMDevice_iface;
1072 WARN("Unknown flow %u\n", flow);
1073 return E_INVALIDARG;
1078 IMMDevice_AddRef(*device);
1082 static HRESULT WINAPI MMDevEnum_GetDevice(IMMDeviceEnumerator *iface, const WCHAR *name, IMMDevice **device)
1084 MMDevEnumImpl *This = impl_from_IMMDeviceEnumerator(iface);
1086 IMMDevice *dev = NULL;
1088 TRACE("(%p)->(%s,%p)\n", This, debugstr_w(name), device);
1089 for (i = 0; i < MMDevice_count; ++i)
1092 dev = &MMDevice_head[i]->IMMDevice_iface;
1093 IMMDevice_GetId(dev, &str);
1095 if (str && !lstrcmpW(str, name))
1104 IUnknown_AddRef(dev);
1108 WARN("Could not find device %s\n", debugstr_w(name));
1112 static HRESULT WINAPI MMDevEnum_RegisterEndpointNotificationCallback(IMMDeviceEnumerator *iface, IMMNotificationClient *client)
1114 MMDevEnumImpl *This = impl_from_IMMDeviceEnumerator(iface);
1115 TRACE("(%p)->(%p)\n", This, client);
1120 static HRESULT WINAPI MMDevEnum_UnregisterEndpointNotificationCallback(IMMDeviceEnumerator *iface, IMMNotificationClient *client)
1122 MMDevEnumImpl *This = impl_from_IMMDeviceEnumerator(iface);
1123 TRACE("(%p)->(%p)\n", This, client);
1128 static const IMMDeviceEnumeratorVtbl MMDevEnumVtbl =
1130 MMDevEnum_QueryInterface,
1133 MMDevEnum_EnumAudioEndpoints,
1134 MMDevEnum_GetDefaultAudioEndpoint,
1135 MMDevEnum_GetDevice,
1136 MMDevEnum_RegisterEndpointNotificationCallback,
1137 MMDevEnum_UnregisterEndpointNotificationCallback
1140 static HRESULT MMDevPropStore_Create(MMDevice *parent, DWORD access, IPropertyStore **ppv)
1142 MMDevPropStore *This;
1143 if (access != STGM_READ
1144 && access != STGM_WRITE
1145 && access != STGM_READWRITE)
1147 WARN("Invalid access %08x\n", access);
1148 return E_INVALIDARG;
1150 This = HeapAlloc(GetProcessHeap(), 0, sizeof(*This));
1151 *ppv = &This->IPropertyStore_iface;
1153 return E_OUTOFMEMORY;
1154 This->IPropertyStore_iface.lpVtbl = &MMDevPropVtbl;
1156 This->parent = parent;
1157 This->access = access;
1161 static void MMDevPropStore_Destroy(MMDevPropStore *This)
1163 HeapFree(GetProcessHeap(), 0, This);
1166 static HRESULT WINAPI MMDevPropStore_QueryInterface(IPropertyStore *iface, REFIID riid, void **ppv)
1168 MMDevPropStore *This = impl_from_IPropertyStore(iface);
1172 if (IsEqualIID(riid, &IID_IUnknown)
1173 || IsEqualIID(riid, &IID_IPropertyStore))
1178 return E_NOINTERFACE;
1179 IUnknown_AddRef((IUnknown*)*ppv);
1183 static ULONG WINAPI MMDevPropStore_AddRef(IPropertyStore *iface)
1185 MMDevPropStore *This = impl_from_IPropertyStore(iface);
1186 LONG ref = InterlockedIncrement(&This->ref);
1187 TRACE("Refcount now %i\n", ref);
1191 static ULONG WINAPI MMDevPropStore_Release(IPropertyStore *iface)
1193 MMDevPropStore *This = impl_from_IPropertyStore(iface);
1194 LONG ref = InterlockedDecrement(&This->ref);
1195 TRACE("Refcount now %i\n", ref);
1197 MMDevPropStore_Destroy(This);
1201 static HRESULT WINAPI MMDevPropStore_GetCount(IPropertyStore *iface, DWORD *nprops)
1203 MMDevPropStore *This = impl_from_IPropertyStore(iface);
1209 TRACE("(%p)->(%p)\n", iface, nprops);
1212 hr = MMDevPropStore_OpenPropKey(&This->parent->devguid, This->parent->flow, &propkey);
1217 DWORD len = sizeof(buffer)/sizeof(*buffer);
1218 if (RegEnumKeyExW(propkey, i, buffer, &len, NULL, NULL, NULL, NULL) != ERROR_SUCCESS)
1222 RegCloseKey(propkey);
1223 TRACE("Returning %i\n", i);
1228 static HRESULT WINAPI MMDevPropStore_GetAt(IPropertyStore *iface, DWORD prop, PROPERTYKEY *key)
1230 MMDevPropStore *This = impl_from_IPropertyStore(iface);
1232 DWORD len = sizeof(buffer)/sizeof(*buffer);
1236 TRACE("(%p)->(%u,%p)\n", iface, prop, key);
1240 hr = MMDevPropStore_OpenPropKey(&This->parent->devguid, This->parent->flow, &propkey);
1244 if (RegEnumKeyExW(propkey, prop, buffer, &len, NULL, NULL, NULL, NULL) != ERROR_SUCCESS
1247 WARN("GetAt %u failed\n", prop);
1248 return E_INVALIDARG;
1250 RegCloseKey(propkey);
1252 CLSIDFromString(buffer, &key->fmtid);
1253 key->pid = atoiW(&buffer[40]);
1257 static HRESULT WINAPI MMDevPropStore_GetValue(IPropertyStore *iface, REFPROPERTYKEY key, PROPVARIANT *pv)
1259 MMDevPropStore *This = impl_from_IPropertyStore(iface);
1260 TRACE("(%p)->(\"%s,%u\", %p\n", This, debugstr_guid(&key->fmtid), key ? key->pid : 0, pv);
1264 if (This->access != STGM_READ
1265 && This->access != STGM_READWRITE)
1266 return STG_E_ACCESSDENIED;
1269 if (IsEqualPropertyKey(*key, PKEY_AudioEndpoint_GUID))
1272 pv->u.pwszVal = CoTaskMemAlloc(39 * sizeof(WCHAR));
1274 return E_OUTOFMEMORY;
1275 StringFromGUID2(&This->parent->devguid, pv->u.pwszVal, 39);
1279 return MMDevice_GetPropValue(&This->parent->devguid, This->parent->flow, key, pv);
1282 static HRESULT WINAPI MMDevPropStore_SetValue(IPropertyStore *iface, REFPROPERTYKEY key, REFPROPVARIANT pv)
1284 MMDevPropStore *This = impl_from_IPropertyStore(iface);
1289 if (This->access != STGM_WRITE
1290 && This->access != STGM_READWRITE)
1291 return STG_E_ACCESSDENIED;
1292 return MMDevice_SetPropValue(&This->parent->devguid, This->parent->flow, key, pv);
1295 static HRESULT WINAPI MMDevPropStore_Commit(IPropertyStore *iface)
1301 static const IPropertyStoreVtbl MMDevPropVtbl =
1303 MMDevPropStore_QueryInterface,
1304 MMDevPropStore_AddRef,
1305 MMDevPropStore_Release,
1306 MMDevPropStore_GetCount,
1307 MMDevPropStore_GetAt,
1308 MMDevPropStore_GetValue,
1309 MMDevPropStore_SetValue,
1310 MMDevPropStore_Commit
1314 /* Property bag for IBaseFilter activation */
1315 static HRESULT WINAPI PB_QueryInterface(IPropertyBag *iface, REFIID riid, void **ppv)
1317 ERR("Should not be called\n");
1319 return E_NOINTERFACE;
1322 static ULONG WINAPI PB_AddRef(IPropertyBag *iface)
1324 ERR("Should not be called\n");
1328 static ULONG WINAPI PB_Release(IPropertyBag *iface)
1330 ERR("Should not be called\n");
1334 static HRESULT WINAPI PB_Read(IPropertyBag *iface, LPCOLESTR name, VARIANT *var, IErrorLog *log)
1336 static const WCHAR dsguid[] = { 'D','S','G','u','i','d', 0 };
1337 IPropertyBagImpl *This = impl_from_IPropertyBag(iface);
1338 TRACE("Trying to read %s, type %u\n", debugstr_w(name), var->n1.n2.vt);
1339 if (!lstrcmpW(name, dsguid))
1342 StringFromGUID2(&This->devguid, guidstr,sizeof(guidstr)/sizeof(*guidstr));
1343 var->n1.n2.vt = VT_BSTR;
1344 var->n1.n2.n3.bstrVal = SysAllocString(guidstr);
1347 ERR("Unknown property '%s' queried\n", debugstr_w(name));
1351 static HRESULT WINAPI PB_Write(IPropertyBag *iface, LPCOLESTR name, VARIANT *var)
1353 ERR("Should not be called\n");
1357 static const IPropertyBagVtbl PB_Vtbl =