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
=