wined3d: Get rid of the swapchain destroy callback.
[wine] / dlls / mmdevapi / devenum.c
1 /*
2  * Copyright 2009 Maarten Lankhorst
3  *
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.
8  *
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.
13  *
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
17  */
18
19 #include "config.h"
20
21 #include <stdarg.h>
22
23 #ifdef HAVE_AL_AL_H
24 #include <AL/al.h>
25 #include <AL/alc.h>
26 #elif defined(HAVE_OPENAL_AL_H)
27 #include <OpenAL/al.h>
28 #include <OpenAL/alc.h>
29 #endif
30
31 #define NONAMELESSUNION
32 #define COBJMACROS
33 #include "windef.h"
34 #include "winbase.h"
35 #include "winnls.h"
36 #include "winreg.h"
37 #include "wine/debug.h"
38 #include "wine/unicode.h"
39
40 #include "ole2.h"
41 #include "mmdeviceapi.h"
42 #include "dshow.h"
43 #include "dsound.h"
44 #include "audioclient.h"
45 #include "endpointvolume.h"
46 #include "audiopolicy.h"
47
48 #include "mmdevapi.h"
49 #include "devpkey.h"
50
51 WINE_DEFAULT_DEBUG_CHANNEL(mmdevapi);
52
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 };
68
69 static HKEY key_render;
70 static HKEY key_capture;
71
72 typedef struct MMDevPropStoreImpl
73 {
74     IPropertyStore IPropertyStore_iface;
75     LONG ref;
76     MMDevice *parent;
77     DWORD access;
78 } MMDevPropStore;
79
80 typedef struct MMDevEnumImpl
81 {
82     IMMDeviceEnumerator IMMDeviceEnumerator_iface;
83     LONG ref;
84 } MMDevEnumImpl;
85
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;
95
96 typedef struct MMDevColImpl
97 {
98     IMMDeviceCollection IMMDeviceCollection_iface;
99     LONG ref;
100     EDataFlow flow;
101     DWORD state;
102 } MMDevColImpl;
103
104 typedef struct IPropertyBagImpl {
105     IPropertyBag IPropertyBag_iface;
106     GUID devguid;
107 } IPropertyBagImpl;
108
109 static const IPropertyBagVtbl PB_Vtbl;
110
111 static HRESULT MMDevPropStore_Create(MMDevice *This, DWORD access, IPropertyStore **ppv);
112
113 static inline MMDevPropStore *impl_from_IPropertyStore(IPropertyStore *iface)
114 {
115     return CONTAINING_RECORD(iface, MMDevPropStore, IPropertyStore_iface);
116 }
117
118 static inline MMDevEnumImpl *impl_from_IMMDeviceEnumerator(IMMDeviceEnumerator *iface)
119 {
120     return CONTAINING_RECORD(iface, MMDevEnumImpl, IMMDeviceEnumerator_iface);
121 }
122
123 static inline MMDevColImpl *impl_from_IMMDeviceCollection(IMMDeviceCollection *iface)
124 {
125     return CONTAINING_RECORD(iface, MMDevColImpl, IMMDeviceCollection_iface);
126 }
127
128 static inline IPropertyBagImpl *impl_from_IPropertyBag(IPropertyBag *iface)
129 {
130     return CONTAINING_RECORD(iface, IPropertyBagImpl, IPropertyBag_iface);
131 }
132
133 static const WCHAR propkey_formatW[] = {
134     '{','%','0','8','X','-','%','0','4','X','-',
135     '%','0','4','X','-','%','0','2','X','%','0','2','X','-',
136     '%','0','2','X','%','0','2','X','%','0','2','X','%','0','2','X',
137     '%','0','2','X','%','0','2','X','}',',','%','d',0 };
138
139 static HRESULT MMDevPropStore_OpenPropKey(const GUID *guid, DWORD flow, HKEY *propkey)
140 {
141     WCHAR buffer[39];
142     LONG ret;
143     HKEY key;
144     StringFromGUID2(guid, buffer, 39);
145     if ((ret = RegOpenKeyExW(flow == eRender ? key_render : key_capture, buffer, 0, KEY_READ|KEY_WRITE, &key)) != ERROR_SUCCESS)
146     {
147         WARN("Opening key %s failed with %u\n", debugstr_w(buffer), ret);
148         return E_FAIL;
149     }
150     ret = RegOpenKeyExW(key, reg_properties, 0, KEY_READ|KEY_WRITE, propkey);
151     RegCloseKey(key);
152     if (ret != ERROR_SUCCESS)
153     {
154         WARN("Opening key %s failed with %u\n", debugstr_w(reg_properties), ret);
155         return E_FAIL;
156     }
157     return S_OK;
158 }
159
160 HRESULT MMDevice_GetPropValue(const GUID *devguid, DWORD flow, REFPROPERTYKEY key, PROPVARIANT *pv)
161 {
162     WCHAR buffer[80];
163     const GUID *id = &key->fmtid;
164     DWORD type, size;
165     HRESULT hr = S_OK;
166     HKEY regkey;
167     LONG ret;
168
169     hr = MMDevPropStore_OpenPropKey(devguid, flow, &regkey);
170     if (FAILED(hr))
171         return hr;
172     wsprintfW( buffer, propkey_formatW, id->Data1, id->Data2, id->Data3,
173                id->Data4[0], id->Data4[1], id->Data4[2], id->Data4[3],
174                id->Data4[4], id->Data4[5], id->Data4[6], id->Data4[7], key->pid );
175     ret = RegGetValueW(regkey, NULL, buffer, RRF_RT_ANY, &type, NULL, &size);
176     if (ret != ERROR_SUCCESS)
177     {
178         WARN("Reading %s returned %d\n", debugstr_w(buffer), ret);
179         RegCloseKey(regkey);
180         PropVariantClear(pv);
181         return S_OK;
182     }
183
184     switch (type)
185     {
186         case REG_SZ:
187         {
188             pv->vt = VT_LPWSTR;
189             pv->u.pwszVal = CoTaskMemAlloc(size);
190             if (!pv->u.pwszVal)
191                 hr = E_OUTOFMEMORY;
192             else
193                 RegGetValueW(regkey, NULL, buffer, RRF_RT_REG_SZ, NULL, (BYTE*)pv->u.pwszVal, &size);
194             break;
195         }
196         case REG_DWORD:
197         {
198             pv->vt = VT_UI4;
199             RegGetValueW(regkey, NULL, buffer, RRF_RT_REG_DWORD, NULL, (BYTE*)&pv->u.ulVal, &size);
200             break;
201         }
202         case REG_BINARY:
203         {
204             pv->vt = VT_BLOB;
205             pv->u.blob.cbSize = size;
206             pv->u.blob.pBlobData = CoTaskMemAlloc(size);
207             if (!pv->u.blob.pBlobData)
208                 hr = E_OUTOFMEMORY;
209             else
210                 RegGetValueW(regkey, NULL, buffer, RRF_RT_REG_BINARY, NULL, (BYTE*)pv->u.blob.pBlobData, &size);
211             break;
212         }
213         default:
214             ERR("Unknown/unhandled type: %u\n", type);
215             PropVariantClear(pv);
216             break;
217     }
218     RegCloseKey(regkey);
219     return hr;
220 }
221
222 static HRESULT MMDevice_SetPropValue(const GUID *devguid, DWORD flow, REFPROPERTYKEY key, REFPROPVARIANT pv)
223 {
224     WCHAR buffer[80];
225     const GUID *id = &key->fmtid;
226     HRESULT hr;
227     HKEY regkey;
228     LONG ret;
229
230     hr = MMDevPropStore_OpenPropKey(devguid, flow, &regkey);
231     if (FAILED(hr))
232         return hr;
233     wsprintfW( buffer, propkey_formatW, id->Data1, id->Data2, id->Data3,
234                id->Data4[0], id->Data4[1], id->Data4[2], id->Data4[3],
235                id->Data4[4], id->Data4[5], id->Data4[6], id->Data4[7], key->pid );
236     switch (pv->vt)
237     {
238         case VT_UI4:
239         {
240             ret = RegSetValueExW(regkey, buffer, 0, REG_DWORD, (const BYTE*)&pv->u.ulVal, sizeof(DWORD));
241             break;
242         }
243         case VT_BLOB:
244         {
245             ret = RegSetValueExW(regkey, buffer, 0, REG_BINARY, pv->u.blob.pBlobData, pv->u.blob.cbSize);
246             TRACE("Blob %p %u\n", pv->u.blob.pBlobData, pv->u.blob.cbSize);
247
248             break;
249         }
250         case VT_LPWSTR:
251         {
252             ret = RegSetValueExW(regkey, buffer, 0, REG_SZ, (const BYTE*)pv->u.pwszVal, sizeof(WCHAR)*(1+lstrlenW(pv->u.pwszVal)));
253             break;
254         }
255         default:
256             ret = 0;
257             FIXME("Unhandled type %u\n", pv->vt);
258             hr = E_INVALIDARG;
259             break;
260     }
261     RegCloseKey(regkey);
262     TRACE("Writing %s returned %u\n", debugstr_w(buffer), ret);
263     return hr;
264 }
265
266 /* Creates or updates the state of a device
267  * If GUID is null, a random guid will be assigned
268  * and the device will be created
269  */
270 static void MMDevice_Create(MMDevice **dev, WCHAR *name, GUID *id, EDataFlow flow, DWORD state, BOOL setdefault)
271 {
272     HKEY key, root;
273     MMDevice *cur;
274     WCHAR guidstr[39];
275     DWORD i;
276
277     for (i = 0; i < MMDevice_count; ++i)
278     {
279         cur = MMDevice_head[i];
280         if (cur->flow == flow && !lstrcmpW(cur->alname, name))
281         {
282             LONG ret;
283             /* Same device, update state */
284             cur->state = state;
285             StringFromGUID2(&cur->devguid, guidstr, sizeof(guidstr)/sizeof(*guidstr));
286             ret = RegOpenKeyExW(flow == eRender ? key_render : key_capture, guidstr, 0, KEY_WRITE, &key);
287             if (ret == ERROR_SUCCESS)
288             {
289                 RegSetValueExW(key, reg_devicestate, 0, REG_DWORD, (const BYTE*)&state, sizeof(DWORD));
290                 RegCloseKey(key);
291             }
292             goto done;
293         }
294     }
295
296     /* No device found, allocate new one */
297     cur = HeapAlloc(GetProcessHeap(), 0, sizeof(*cur));
298     if (!cur)
299         return;
300     cur->alname = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(name)+1)*sizeof(WCHAR));
301     if (!cur->alname)
302     {
303         HeapFree(GetProcessHeap(), 0, cur);
304         return;
305     }
306     lstrcpyW(cur->alname, name);
307     cur->IMMDevice_iface.lpVtbl = &MMDeviceVtbl;
308     cur->IMMEndpoint_iface.lpVtbl = &MMEndpointVtbl;
309     cur->ref = 0;
310     InitializeCriticalSection(&cur->crst);
311     cur->crst.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": MMDevice.crst");
312     cur->flow = flow;
313     cur->state = state;
314     cur->device = NULL;
315     if (!id)
316     {
317         id = &cur->devguid;
318         CoCreateGuid(id);
319     }
320     cur->devguid = *id;
321     StringFromGUID2(id, guidstr, sizeof(guidstr)/sizeof(*guidstr));
322     if (flow == eRender)
323         root = key_render;
324     else
325         root = key_capture;
326     if (!RegCreateKeyExW(root, guidstr, 0, NULL, 0, KEY_WRITE|KEY_READ, NULL, &key, NULL))
327     {
328         HKEY keyprop;
329         RegSetValueExW(key, reg_devicestate, 0, REG_DWORD, (const BYTE*)&state, sizeof(DWORD));
330         if (!RegCreateKeyExW(key, reg_properties, 0, NULL, 0, KEY_WRITE|KEY_READ, NULL, &keyprop, NULL))
331         {
332             PROPVARIANT pv;
333             pv.vt = VT_LPWSTR;
334             pv.u.pwszVal = name;
335             MMDevice_SetPropValue(id, flow, (const PROPERTYKEY*)&DEVPKEY_Device_FriendlyName, &pv);
336             MMDevice_SetPropValue(id, flow, (const PROPERTYKEY*)&DEVPKEY_Device_DeviceDesc, &pv);
337             RegCloseKey(keyprop);
338         }
339         RegCloseKey(key);
340     }
341     if (!MMDevice_head)
342         MMDevice_head = HeapAlloc(GetProcessHeap(), 0, sizeof(*MMDevice_head));
343     else
344         MMDevice_head = HeapReAlloc(GetProcessHeap(), 0, MMDevice_head, sizeof(*MMDevice_head)*(1+MMDevice_count));
345     MMDevice_head[MMDevice_count++] = cur;
346
347 done:
348     if (setdefault)
349     {
350         if (flow == eRender)
351             MMDevice_def_play = cur;
352         else
353             MMDevice_def_rec = cur;
354     }
355     if (dev)
356         *dev = cur;
357 }
358
359 static void MMDevice_Destroy(MMDevice *This)
360 {
361     DWORD i;
362     TRACE("Freeing %s\n", debugstr_w(This->alname));
363     /* Since this function is called at destruction time, reordering of the list is unimportant */
364     for (i = 0; i < MMDevice_count; ++i)
365     {
366         if (MMDevice_head[i] == This)
367         {
368             MMDevice_head[i] = MMDevice_head[--MMDevice_count];
369             break;
370         }
371     }
372 #ifdef HAVE_OPENAL
373     if (This->device)
374         palcCloseDevice(This->device);
375 #endif
376     This->crst.DebugInfo->Spare[0] = 0;
377     DeleteCriticalSection(&This->crst);
378     HeapFree(GetProcessHeap(), 0, This->alname);
379     HeapFree(GetProcessHeap(), 0, This);
380 }
381
382 static inline MMDevice *impl_from_IMMDevice(IMMDevice *iface)
383 {
384     return CONTAINING_RECORD(iface, MMDevice, IMMDevice_iface);
385 }
386
387 static HRESULT WINAPI MMDevice_QueryInterface(IMMDevice *iface, REFIID riid, void **ppv)
388 {
389     MMDevice *This = impl_from_IMMDevice(iface);
390     TRACE("(%p)->(%s,%p)\n", iface, debugstr_guid(riid), ppv);
391
392     if (!ppv)
393         return E_POINTER;
394     *ppv = NULL;
395     if (IsEqualIID(riid, &IID_IUnknown)
396         || IsEqualIID(riid, &IID_IMMDevice))
397         *ppv = This;
398     else if (IsEqualIID(riid, &IID_IMMEndpoint))
399         *ppv = &This->IMMEndpoint_iface;
400     if (*ppv)
401     {
402         IUnknown_AddRef((IUnknown*)*ppv);
403         return S_OK;
404     }
405     WARN("Unknown interface %s\n", debugstr_guid(riid));
406     return E_NOINTERFACE;
407 }
408
409 static ULONG WINAPI MMDevice_AddRef(IMMDevice *iface)
410 {
411     MMDevice *This = impl_from_IMMDevice(iface);
412     LONG ref;
413
414     ref = InterlockedIncrement(&This->ref);
415     TRACE("Refcount now %i\n", ref);
416     return ref;
417 }
418
419 static ULONG WINAPI MMDevice_Release(IMMDevice *iface)
420 {
421     MMDevice *This = impl_from_IMMDevice(iface);
422     LONG ref;
423
424     ref = InterlockedDecrement(&This->ref);
425     TRACE("Refcount now %i\n", ref);
426     return ref;
427 }
428
429 static HRESULT WINAPI MMDevice_Activate(IMMDevice *iface, REFIID riid, DWORD clsctx, PROPVARIANT *params, void **ppv)
430 {
431     HRESULT hr = E_NOINTERFACE;
432
433 #ifdef HAVE_OPENAL
434     MMDevice *This = impl_from_IMMDevice(iface);
435
436     TRACE("(%p)->(%p,%x,%p,%p)\n", iface, riid, clsctx, params, ppv);
437
438     if (!ppv)
439         return E_POINTER;
440
441     if (!openal_loaded)
442     {
443         WARN("OpenAL is still not loaded\n");
444         hr = AUDCLNT_E_SERVICE_NOT_RUNNING;
445     }
446     else if (IsEqualIID(riid, &IID_IAudioClient))
447         hr = AudioClient_Create(This, (IAudioClient**)ppv);
448     else if (IsEqualIID(riid, &IID_IAudioEndpointVolume))
449         hr = AudioEndpointVolume_Create(This, (IAudioEndpointVolume**)ppv);
450     else if (IsEqualIID(riid, &IID_IAudioSessionManager)
451              || IsEqualIID(riid, &IID_IAudioSessionManager2))
452     {
453         FIXME("IID_IAudioSessionManager unsupported\n");
454     }
455     else if (IsEqualIID(riid, &IID_IBaseFilter))
456     {
457         if (This->flow == eRender)
458             hr = CoCreateInstance(&CLSID_DSoundRender, NULL, clsctx, riid, ppv);
459         else
460             ERR("Not supported for recording?\n");
461         if (SUCCEEDED(hr))
462         {
463             IPersistPropertyBag *ppb;
464             hr = IUnknown_QueryInterface((IUnknown*)*ppv, &IID_IPersistPropertyBag, (void*)&ppb);
465             if (SUCCEEDED(hr))
466             {
467                 /* ::Load cannot assume the interface stays alive after the function returns,
468                  * so just create the interface on the stack, saves a lot of complicated code */
469                 IPropertyBagImpl bag = { { &PB_Vtbl }, This->devguid };
470                 hr = IPersistPropertyBag_Load(ppb, &bag.IPropertyBag_iface, NULL);
471                 IPersistPropertyBag_Release(ppb);
472                 if (FAILED(hr))
473                     IBaseFilter_Release((IBaseFilter*)*ppv);
474             }
475             else
476             {
477                 FIXME("Wine doesn't support IPersistPropertyBag on DSoundRender yet, ignoring..\n");
478                 hr = S_OK;
479             }
480         }
481     }
482     else if (IsEqualIID(riid, &IID_IDeviceTopology))
483     {
484         FIXME("IID_IDeviceTopology unsupported\n");
485     }
486     else if (IsEqualIID(riid, &IID_IDirectSound)
487              || IsEqualIID(riid, &IID_IDirectSound8))
488     {
489         if (This->flow == eRender)
490             hr = CoCreateInstance(&CLSID_DirectSound8, NULL, clsctx, riid, ppv);
491         if (SUCCEEDED(hr))
492         {
493             hr = IDirectSound_Initialize((IDirectSound*)*ppv, &This->devguid);
494             if (FAILED(hr))
495                 IDirectSound_Release((IDirectSound*)*ppv);
496         }
497     }
498     else if (IsEqualIID(riid, &IID_IDirectSoundCapture)
499              || IsEqualIID(riid, &IID_IDirectSoundCapture8))
500     {
501         if (This->flow == eCapture)
502             hr = CoCreateInstance(&CLSID_DirectSoundCapture8, NULL, clsctx, riid, ppv);
503         if (SUCCEEDED(hr))
504         {
505             hr = IDirectSoundCapture_Initialize((IDirectSoundCapture*)*ppv, &This->devguid);
506             if (FAILED(hr))
507                 IDirectSoundCapture_Release((IDirectSoundCapture*)*ppv);
508         }
509     }
510     else
511         ERR("Invalid/unknown iid %s\n", debugstr_guid(riid));
512 #else
513     if (!ppv) return E_POINTER;
514     hr = AUDCLNT_E_SERVICE_NOT_RUNNING;
515 #endif
516
517     if (FAILED(hr))
518         *ppv = NULL;
519
520     TRACE("Returning %08x\n", hr);
521     return hr;
522 }
523
524 static HRESULT WINAPI MMDevice_OpenPropertyStore(IMMDevice *iface, DWORD access, IPropertyStore **ppv)
525 {
526     MMDevice *This = impl_from_IMMDevice(iface);
527     TRACE("(%p)->(%x,%p)\n", This, access, ppv);
528
529     if (!ppv)
530         return E_POINTER;
531     return MMDevPropStore_Create(This, access, ppv);
532 }
533
534 static HRESULT WINAPI MMDevice_GetId(IMMDevice *iface, WCHAR **itemid)
535 {
536     MMDevice *This = impl_from_IMMDevice(iface);
537     WCHAR *str;
538     GUID *id = &This->devguid;
539     static const WCHAR formatW[] = { '{','0','.','0','.','0','.','0','0','0','0','0','0','0','0','}','.',
540                                      '{','%','0','8','X','-','%','0','4','X','-',
541                                      '%','0','4','X','-','%','0','2','X','%','0','2','X','-',
542                                      '%','0','2','X','%','0','2','X','%','0','2','X','%','0','2','X',
543                                      '%','0','2','X','%','0','2','X','}',0 };
544
545     TRACE("(%p)->(%p)\n", This, itemid);
546     if (!itemid)
547         return E_POINTER;
548     *itemid = str = CoTaskMemAlloc(56 * sizeof(WCHAR));
549     if (!str)
550         return E_OUTOFMEMORY;
551     wsprintfW( str, formatW, id->Data1, id->Data2, id->Data3,
552                id->Data4[0], id->Data4[1], id->Data4[2], id->Data4[3],
553                id->Data4[4], id->Data4[5], id->Data4[6], id->Data4[7] );
554     return S_OK;
555 }
556
557 static HRESULT WINAPI MMDevice_GetState(IMMDevice *iface, DWORD *state)
558 {
559     MMDevice *This = impl_from_IMMDevice(iface);
560     TRACE("(%p)->(%p)\n", iface, state);
561
562     if (!state)
563         return E_POINTER;
564     *state = This->state;
565     return S_OK;
566 }
567
568 static const IMMDeviceVtbl MMDeviceVtbl =
569 {
570     MMDevice_QueryInterface,
571     MMDevice_AddRef,
572     MMDevice_Release,
573     MMDevice_Activate,
574     MMDevice_OpenPropertyStore,
575     MMDevice_GetId,
576     MMDevice_GetState
577 };
578
579 static inline MMDevice *impl_from_IMMEndpoint(IMMEndpoint *iface)
580 {
581     return CONTAINING_RECORD(iface, MMDevice, IMMEndpoint_iface);
582 }
583
584 static HRESULT WINAPI MMEndpoint_QueryInterface(IMMEndpoint *iface, REFIID riid, void **ppv)
585 {
586     MMDevice *This = impl_from_IMMEndpoint(iface);
587     return IMMDevice_QueryInterface(&This->IMMDevice_iface, riid, ppv);
588 }
589
590 static ULONG WINAPI MMEndpoint_AddRef(IMMEndpoint *iface)
591 {
592     MMDevice *This = impl_from_IMMEndpoint(iface);
593     return IMMDevice_AddRef(&This->IMMDevice_iface);
594 }
595
596 static ULONG WINAPI MMEndpoint_Release(IMMEndpoint *iface)
597 {
598     MMDevice *This = impl_from_IMMEndpoint(iface);
599     return IMMDevice_Release(&This->IMMDevice_iface);
600 }
601
602 static HRESULT WINAPI MMEndpoint_GetDataFlow(IMMEndpoint *iface, EDataFlow *flow)
603 {
604     MMDevice *This = impl_from_IMMEndpoint(iface);
605     if (!flow)
606         return E_POINTER;
607     *flow = This->flow;
608     return S_OK;
609 }
610
611 static const IMMEndpointVtbl MMEndpointVtbl =
612 {
613     MMEndpoint_QueryInterface,
614     MMEndpoint_AddRef,
615     MMEndpoint_Release,
616     MMEndpoint_GetDataFlow
617 };
618
619 static HRESULT MMDevCol_Create(IMMDeviceCollection **ppv, EDataFlow flow, DWORD state)
620 {
621     MMDevColImpl *This;
622
623     This = HeapAlloc(GetProcessHeap(), 0, sizeof(*This));
624     *ppv = NULL;
625     if (!This)
626         return E_OUTOFMEMORY;
627     This->IMMDeviceCollection_iface.lpVtbl = &MMDevColVtbl;
628     This->ref = 1;
629     This->flow = flow;
630     This->state = state;
631     *ppv = &This->IMMDeviceCollection_iface;
632     return S_OK;
633 }
634
635 static void MMDevCol_Destroy(MMDevColImpl *This)
636 {
637     HeapFree(GetProcessHeap(), 0, This);
638 }
639
640 static HRESULT WINAPI MMDevCol_QueryInterface(IMMDeviceCollection *iface, REFIID riid, void **ppv)
641 {
642     MMDevColImpl *This = impl_from_IMMDeviceCollection(iface);
643
644     if (!ppv)
645         return E_POINTER;
646     if (IsEqualIID(riid, &IID_IUnknown)
647         || IsEqualIID(riid, &IID_IMMDeviceCollection))
648         *ppv = This;
649     else
650         *ppv = NULL;
651     if (!*ppv)
652         return E_NOINTERFACE;
653     IUnknown_AddRef((IUnknown*)*ppv);
654     return S_OK;
655 }
656
657 static ULONG WINAPI MMDevCol_AddRef(IMMDeviceCollection *iface)
658 {
659     MMDevColImpl *This = impl_from_IMMDeviceCollection(iface);
660     LONG ref = InterlockedIncrement(&This->ref);
661     TRACE("Refcount now %i\n", ref);
662     return ref;
663 }
664
665 static ULONG WINAPI MMDevCol_Release(IMMDeviceCollection *iface)
666 {
667     MMDevColImpl *This = impl_from_IMMDeviceCollection(iface);
668     LONG ref = InterlockedDecrement(&This->ref);
669     TRACE("Refcount now %i\n", ref);
670     if (!ref)
671         MMDevCol_Destroy(This);
672     return ref;
673 }
674
675 static HRESULT WINAPI MMDevCol_GetCount(IMMDeviceCollection *iface, UINT *numdevs)
676 {
677     MMDevColImpl *This = impl_from_IMMDeviceCollection(iface);
678     DWORD i;
679
680     TRACE("(%p)->(%p)\n", This, numdevs);
681     if (!numdevs)
682         return E_POINTER;
683
684     *numdevs = 0;
685     for (i = 0; i < MMDevice_count; ++i)
686     {
687         MMDevice *cur = MMDevice_head[i];
688         if ((cur->flow == This->flow || This->flow == eAll)
689             && (cur->state & This->state))
690             ++(*numdevs);
691     }
692     return S_OK;
693 }
694
695 static HRESULT WINAPI MMDevCol_Item(IMMDeviceCollection *iface, UINT n, IMMDevice **dev)
696 {
697     MMDevColImpl *This = impl_from_IMMDeviceCollection(iface);
698     DWORD i = 0, j = 0;
699
700     TRACE("(%p)->(%u, %p)\n", This, n, dev);
701     if (!dev)
702         return E_POINTER;
703
704     for (j = 0; j < MMDevice_count; ++j)
705     {
706         MMDevice *cur = MMDevice_head[j];
707         if ((cur->flow == This->flow || This->flow == eAll)
708             && (cur->state & This->state)
709             && i++ == n)
710         {
711             *dev = &cur->IMMDevice_iface;
712             IMMDevice_AddRef(*dev);
713             return S_OK;
714         }
715     }
716     WARN("Could not obtain item %u\n", n);
717     *dev = NULL;
718     return E_INVALIDARG;
719 }
720
721 static const IMMDeviceCollectionVtbl MMDevColVtbl =
722 {
723     MMDevCol_QueryInterface,
724     MMDevCol_AddRef,
725     MMDevCol_Release,
726     MMDevCol_GetCount,
727     MMDevCol_Item
728 };
729
730 #ifdef HAVE_OPENAL
731
732 static void openal_setformat(MMDevice *This, DWORD freq)
733 {
734     HRESULT hr;
735     PROPVARIANT pv = { VT_EMPTY };
736
737     hr = MMDevice_GetPropValue(&This->devguid, This->flow, &PKEY_AudioEngine_DeviceFormat, &pv);
738     if (SUCCEEDED(hr) && pv.vt == VT_BLOB)
739     {
740         WAVEFORMATEX *pwfx;
741         pwfx = (WAVEFORMATEX*)pv.u.blob.pBlobData;
742         if (pwfx->nSamplesPerSec != freq)
743         {
744             pwfx->nSamplesPerSec = freq;
745             pwfx->nAvgBytesPerSec = freq * pwfx->nBlockAlign;
746             MMDevice_SetPropValue(&This->devguid, This->flow, &PKEY_AudioEngine_DeviceFormat, &pv);
747         }
748         CoTaskMemFree(pwfx);
749     }
750     else
751     {
752         WAVEFORMATEXTENSIBLE wfxe;
753
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;
764
765         pv.vt = VT_BLOB;
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);
770     }
771 }
772
773 static int blacklist_pulse;
774
775 static int blacklist(const char *dev) {
776 #ifdef __linux__
777     if (!strncmp(dev, "OSS ", 4))
778         return 1;
779 #endif
780     if (blacklist_pulse && !strncmp(dev, "PulseAudio ", 11))
781         return 1;
782     if (!strncmp(dev, "ALSA ", 5) && strstr(dev, "hw:"))
783         return 1;
784     if (!strncmp(dev, "PortAudio ", 10))
785         return 1;
786     return 0;
787 }
788
789 static void pulse_fixup(const char *devstr, const char **defstr, int render) {
790     static int warned;
791     int default_pulse;
792
793     if (render && !blacklist_pulse && !local_contexts)
794         blacklist_pulse = 1;
795
796     if (!blacklist_pulse || !devstr || !*devstr)
797         return;
798
799     default_pulse = !strncmp(*defstr, "PulseAudio ", 11);
800
801     while (*devstr && !strncmp(devstr, "PulseAudio ", 11))
802         devstr += strlen(devstr) + 1;
803
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);
808         if (ctx) {
809             const char *ver;
810
811             setALContext(ctx);
812             ver = palGetString(AL_VERSION);
813             popALContext();
814             palcDestroyContext(ctx);
815
816             if (!strcmp(ver, "1.1 ALSOFT 1.11.753")) {
817                 blacklist_pulse = 0;
818                 palcCloseDevice(dev);
819                 return;
820             }
821         }
822         if (dev)
823             palcCloseDevice(dev);
824     }
825
826     if (!warned++) {
827         ERR("Disabling pulseaudio because of old openal version\n");
828         ERR("Please upgrade to openal-soft v1.12 or newer\n");
829     }
830     TRACE("New default: %s\n", devstr);
831     if (default_pulse)
832         *defstr = devstr;
833 }
834
835 static void openal_scanrender(void)
836 {
837     WCHAR name[MAX_PATH];
838     ALCdevice *dev;
839     const ALCchar *devstr, *defaultstr;
840     int defblacklisted;
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);
845     } else {
846         defaultstr = palcGetString(NULL, ALC_DEFAULT_DEVICE_SPECIFIER);
847         devstr = palcGetString(NULL, ALC_DEVICE_SPECIFIER);
848     }
849     pulse_fixup(devstr, &defaultstr, 1);
850     defblacklisted = blacklist(defaultstr);
851     if (defblacklisted)
852         WARN("Disabling blacklist because %s is blacklisted\n", defaultstr);
853     if (devstr)
854         for (; *devstr; devstr += strlen(devstr)+1) {
855             MMDevice *mmdev;
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);
862                 continue;
863             }
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));
867             if (dev)
868             {
869                 ALint freq = 44100;
870                 palcGetIntegerv(dev, ALC_FREQUENCY, 1, &freq);
871                 openal_setformat(mmdev, freq);
872                 palcCloseDevice(dev);
873             }
874             else
875                 WARN("Could not open device: %04x\n", palcGetError(NULL));
876         }
877     LeaveCriticalSection(&openal_crst);
878 }
879
880 static void openal_scancapture(void)
881 {
882     WCHAR name[MAX_PATH];
883     ALCdevice *dev;
884     const ALCchar *devstr, *defaultstr;
885     int defblacklisted;
886
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);
892     if (defblacklisted)
893         WARN("Disabling blacklist because %s is blacklisted\n", defaultstr);
894     if (devstr && *devstr)
895         for (; *devstr; devstr += strlen(devstr)+1) {
896             MMDevice *mmdev;
897             ALint freq = 44100;
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);
903                 continue;
904             }
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));
908             if (dev) {
909                 openal_setformat(mmdev, freq);
910                 palcCaptureCloseDevice(dev);
911             } else
912                 WARN("Could not open device: %04x\n", palcGetError(NULL));
913         }
914     LeaveCriticalSection(&openal_crst);
915 }
916 #endif /*HAVE_OPENAL*/
917
918 HRESULT MMDevEnum_Create(REFIID riid, void **ppv)
919 {
920     MMDevEnumImpl *This = MMDevEnumerator;
921
922     if (!This)
923     {
924         DWORD i = 0;
925         HKEY root, cur;
926         LONG ret;
927         DWORD curflow;
928
929         This = HeapAlloc(GetProcessHeap(), 0, sizeof(*This));
930         *ppv = NULL;
931         if (!This)
932             return E_OUTOFMEMORY;
933         This->ref = 1;
934         This->IMMDeviceEnumerator_iface.lpVtbl = &MMDevEnumVtbl;
935         MMDevEnumerator = This;
936
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);
942         RegCloseKey(root);
943         cur = key_capture;
944         curflow = eCapture;
945         if (ret != ERROR_SUCCESS)
946         {
947             RegCloseKey(key_capture);
948             key_render = key_capture = NULL;
949             WARN("Couldn't create key: %u\n", ret);
950             return E_FAIL;
951         }
952         else do {
953             WCHAR guidvalue[39];
954             GUID guid;
955             DWORD len;
956             PROPVARIANT pv = { VT_EMPTY };
957
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)
961             {
962                 if (cur == key_capture)
963                 {
964                     cur = key_render;
965                     curflow = eRender;
966                     i = 0;
967                     continue;
968                 }
969                 break;
970             }
971             if (ret != ERROR_SUCCESS)
972                 continue;
973             if (SUCCEEDED(CLSIDFromString(guidvalue, &guid))
974                 && SUCCEEDED(MMDevice_GetPropValue(&guid, curflow, (const PROPERTYKEY*)&DEVPKEY_Device_FriendlyName, &pv))
975                 && pv.vt == VT_LPWSTR)
976             {
977                 MMDevice_Create(NULL, pv.u.pwszVal, &guid, curflow,
978                                 DEVICE_STATE_NOTPRESENT, FALSE);
979                 CoTaskMemFree(pv.u.pwszVal);
980             }
981         } while (1);
982 #ifdef HAVE_OPENAL
983         if (openal_loaded)
984         {
985             openal_scanrender();
986             openal_scancapture();
987         }
988         else
989             FIXME("OpenAL support not enabled, application will not find sound devices\n");
990 #else
991         ERR("OpenAL support not compiled in, application will not find sound devices\n");
992 #endif /*HAVE_OPENAL*/
993     }
994     return IUnknown_QueryInterface((IUnknown*)This, riid, ppv);
995 }
996
997 void MMDevEnum_Free(void)
998 {
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;
1006 }
1007
1008 static HRESULT WINAPI MMDevEnum_QueryInterface(IMMDeviceEnumerator *iface, REFIID riid, void **ppv)
1009 {
1010     MMDevEnumImpl *This = impl_from_IMMDeviceEnumerator(iface);
1011
1012     if (!ppv)
1013         return E_POINTER;
1014     if (IsEqualIID(riid, &IID_IUnknown)
1015         || IsEqualIID(riid, &IID_IMMDeviceEnumerator))
1016         *ppv = This;
1017     else
1018         *ppv = NULL;
1019     if (!*ppv)
1020         return E_NOINTERFACE;
1021     IUnknown_AddRef((IUnknown*)*ppv);
1022     return S_OK;
1023 }
1024
1025 static ULONG WINAPI MMDevEnum_AddRef(IMMDeviceEnumerator *iface)
1026 {
1027     MMDevEnumImpl *This = impl_from_IMMDeviceEnumerator(iface);
1028     LONG ref = InterlockedIncrement(&This->ref);
1029     TRACE("Refcount now %i\n", ref);
1030     return ref;
1031 }
1032
1033 static ULONG WINAPI MMDevEnum_Release(IMMDeviceEnumerator *iface)
1034 {
1035     MMDevEnumImpl *This = impl_from_IMMDeviceEnumerator(iface);
1036     LONG ref = InterlockedDecrement(&This->ref);
1037     if (!ref)
1038         MMDevEnum_Free();
1039     TRACE("Refcount now %i\n", ref);
1040     return ref;
1041 }
1042
1043 static HRESULT WINAPI MMDevEnum_EnumAudioEndpoints(IMMDeviceEnumerator *iface, EDataFlow flow, DWORD mask, IMMDeviceCollection **devices)
1044 {
1045     MMDevEnumImpl *This = impl_from_IMMDeviceEnumerator(iface);
1046     TRACE("(%p)->(%u,%u,%p)\n", This, flow, mask, devices);
1047     if (!devices)
1048         return E_POINTER;
1049     *devices = NULL;
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);
1055 }
1056
1057 static HRESULT WINAPI MMDevEnum_GetDefaultAudioEndpoint(IMMDeviceEnumerator *iface, EDataFlow flow, ERole role, IMMDevice **device)
1058 {
1059     MMDevEnumImpl *This = impl_from_IMMDeviceEnumerator(iface);
1060     TRACE("(%p)->(%u,%u,%p)\n", This, flow, role, device);
1061
1062     if (!device)
1063         return E_POINTER;
1064     *device = NULL;
1065
1066     if (flow == eRender)
1067         *device = &MMDevice_def_play->IMMDevice_iface;
1068     else if (flow == eCapture)
1069         *device = &MMDevice_def_rec->IMMDevice_iface;
1070     else
1071     {
1072         WARN("Unknown flow %u\n", flow);
1073         return E_INVALIDARG;
1074     }
1075
1076     if (!*device)
1077         return E_NOTFOUND;
1078     IMMDevice_AddRef(*device);
1079     return S_OK;
1080 }
1081
1082 static HRESULT WINAPI MMDevEnum_GetDevice(IMMDeviceEnumerator *iface, const WCHAR *name, IMMDevice **device)
1083 {
1084     MMDevEnumImpl *This = impl_from_IMMDeviceEnumerator(iface);
1085     DWORD i=0;
1086     IMMDevice *dev = NULL;
1087
1088     TRACE("(%p)->(%s,%p)\n", This, debugstr_w(name), device);
1089     for (i = 0; i < MMDevice_count; ++i)
1090     {
1091         WCHAR *str;
1092         dev = &MMDevice_head[i]->IMMDevice_iface;
1093         IMMDevice_GetId(dev, &str);
1094
1095         if (str && !lstrcmpW(str, name))
1096         {
1097             CoTaskMemFree(str);
1098             break;
1099         }
1100         CoTaskMemFree(str);
1101     }
1102     if (dev)
1103     {
1104         IUnknown_AddRef(dev);
1105         *device = dev;
1106         return S_OK;
1107     }
1108     WARN("Could not find device %s\n", debugstr_w(name));
1109     return E_NOTFOUND;
1110 }
1111
1112 static HRESULT WINAPI MMDevEnum_RegisterEndpointNotificationCallback(IMMDeviceEnumerator *iface, IMMNotificationClient *client)
1113 {
1114     MMDevEnumImpl *This = impl_from_IMMDeviceEnumerator(iface);
1115     TRACE("(%p)->(%p)\n", This, client);
1116     FIXME("stub\n");
1117     return S_OK;
1118 }
1119
1120 static HRESULT WINAPI MMDevEnum_UnregisterEndpointNotificationCallback(IMMDeviceEnumerator *iface, IMMNotificationClient *client)
1121 {
1122     MMDevEnumImpl *This = impl_from_IMMDeviceEnumerator(iface);
1123     TRACE("(%p)->(%p)\n", This, client);
1124     FIXME("stub\n");
1125     return S_OK;
1126 }
1127
1128 static const IMMDeviceEnumeratorVtbl MMDevEnumVtbl =
1129 {
1130     MMDevEnum_QueryInterface,
1131     MMDevEnum_AddRef,
1132     MMDevEnum_Release,
1133     MMDevEnum_EnumAudioEndpoints,
1134     MMDevEnum_GetDefaultAudioEndpoint,
1135     MMDevEnum_GetDevice,
1136     MMDevEnum_RegisterEndpointNotificationCallback,
1137     MMDevEnum_UnregisterEndpointNotificationCallback
1138 };
1139
1140 static HRESULT MMDevPropStore_Create(MMDevice *parent, DWORD access, IPropertyStore **ppv)
1141 {
1142     MMDevPropStore *This;
1143     if (access != STGM_READ
1144         && access != STGM_WRITE
1145         && access != STGM_READWRITE)
1146     {
1147         WARN("Invalid access %08x\n", access);
1148         return E_INVALIDARG;
1149     }
1150     This = HeapAlloc(GetProcessHeap(), 0, sizeof(*This));
1151     *ppv = &This->IPropertyStore_iface;
1152     if (!This)
1153         return E_OUTOFMEMORY;
1154     This->IPropertyStore_iface.lpVtbl = &MMDevPropVtbl;
1155     This->ref = 1;
1156     This->parent = parent;
1157     This->access = access;
1158     return S_OK;
1159 }
1160
1161 static void MMDevPropStore_Destroy(MMDevPropStore *This)
1162 {
1163     HeapFree(GetProcessHeap(), 0, This);
1164 }
1165
1166 static HRESULT WINAPI MMDevPropStore_QueryInterface(IPropertyStore *iface, REFIID riid, void **ppv)
1167 {
1168     MMDevPropStore *This = impl_from_IPropertyStore(iface);
1169
1170     if (!ppv)
1171         return E_POINTER;
1172     if (IsEqualIID(riid, &IID_IUnknown)
1173         || IsEqualIID(riid, &IID_IPropertyStore))
1174         *ppv = This;
1175     else
1176         *ppv = NULL;
1177     if (!*ppv)
1178         return E_NOINTERFACE;
1179     IUnknown_AddRef((IUnknown*)*ppv);
1180     return S_OK;
1181 }
1182
1183 static ULONG WINAPI MMDevPropStore_AddRef(IPropertyStore *iface)
1184 {
1185     MMDevPropStore *This = impl_from_IPropertyStore(iface);
1186     LONG ref = InterlockedIncrement(&This->ref);
1187     TRACE("Refcount now %i\n", ref);
1188     return ref;
1189 }
1190
1191 static ULONG WINAPI MMDevPropStore_Release(IPropertyStore *iface)
1192 {
1193     MMDevPropStore *This = impl_from_IPropertyStore(iface);
1194     LONG ref = InterlockedDecrement(&This->ref);
1195     TRACE("Refcount now %i\n", ref);
1196     if (!ref)
1197         MMDevPropStore_Destroy(This);
1198     return ref;
1199 }
1200
1201 static HRESULT WINAPI MMDevPropStore_GetCount(IPropertyStore *iface, DWORD *nprops)
1202 {
1203     MMDevPropStore *This = impl_from_IPropertyStore(iface);
1204     WCHAR buffer[50];
1205     DWORD i = 0;
1206     HKEY propkey;
1207     HRESULT hr;
1208
1209     TRACE("(%p)->(%p)\n", iface, nprops);
1210     if (!nprops)
1211         return E_POINTER;
1212     hr = MMDevPropStore_OpenPropKey(&This->parent->devguid, This->parent->flow, &propkey);
1213     if (FAILED(hr))
1214         return hr;
1215     *nprops = 0;
1216     do {
1217         DWORD len = sizeof(buffer)/sizeof(*buffer);
1218         if (RegEnumKeyExW(propkey, i, buffer, &len, NULL, NULL, NULL, NULL) != ERROR_SUCCESS)
1219             break;
1220         i++;
1221     } while (0);
1222     RegCloseKey(propkey);
1223     TRACE("Returning %i\n", i);
1224     *nprops = i;
1225     return S_OK;
1226 }
1227
1228 static HRESULT WINAPI MMDevPropStore_GetAt(IPropertyStore *iface, DWORD prop, PROPERTYKEY *key)
1229 {
1230     MMDevPropStore *This = impl_from_IPropertyStore(iface);
1231     WCHAR buffer[50];
1232     DWORD len = sizeof(buffer)/sizeof(*buffer);
1233     HRESULT hr;
1234     HKEY propkey;
1235
1236     TRACE("(%p)->(%u,%p)\n", iface, prop, key);
1237     if (!key)
1238         return E_POINTER;
1239
1240     hr = MMDevPropStore_OpenPropKey(&This->parent->devguid, This->parent->flow, &propkey);
1241     if (FAILED(hr))
1242         return hr;
1243
1244     if (RegEnumKeyExW(propkey, prop, buffer, &len, NULL, NULL, NULL, NULL) != ERROR_SUCCESS
1245         || len <= 40)
1246     {
1247         WARN("GetAt %u failed\n", prop);
1248         return E_INVALIDARG;
1249     }
1250     RegCloseKey(propkey);
1251     buffer[39] = 0;
1252     CLSIDFromString(buffer, &key->fmtid);
1253     key->pid = atoiW(&buffer[40]);
1254     return S_OK;
1255 }
1256
1257 static HRESULT WINAPI MMDevPropStore_GetValue(IPropertyStore *iface, REFPROPERTYKEY key, PROPVARIANT *pv)
1258 {
1259     MMDevPropStore *This = impl_from_IPropertyStore(iface);
1260     TRACE("(%p)->(\"%s,%u\", %p\n", This, debugstr_guid(&key->fmtid), key ? key->pid : 0, pv);
1261
1262     if (!key || !pv)
1263         return E_POINTER;
1264     if (This->access != STGM_READ
1265         && This->access != STGM_READWRITE)
1266         return STG_E_ACCESSDENIED;
1267
1268     /* Special case */
1269     if (IsEqualPropertyKey(*key, PKEY_AudioEndpoint_GUID))
1270     {
1271         pv->vt = VT_LPWSTR;
1272         pv->u.pwszVal = CoTaskMemAlloc(39 * sizeof(WCHAR));
1273         if (!pv->u.pwszVal)
1274             return E_OUTOFMEMORY;
1275         StringFromGUID2(&This->parent->devguid, pv->u.pwszVal, 39);
1276         return S_OK;
1277     }
1278
1279     return MMDevice_GetPropValue(&This->parent->devguid, This->parent->flow, key, pv);
1280 }
1281
1282 static HRESULT WINAPI MMDevPropStore_SetValue(IPropertyStore *iface, REFPROPERTYKEY key, REFPROPVARIANT pv)
1283 {
1284     MMDevPropStore *This = impl_from_IPropertyStore(iface);
1285
1286     if (!key || !pv)
1287         return E_POINTER;
1288
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);
1293 }
1294
1295 static HRESULT WINAPI MMDevPropStore_Commit(IPropertyStore *iface)
1296 {
1297     FIXME("stub\n");
1298     return E_NOTIMPL;
1299 }
1300
1301 static const IPropertyStoreVtbl MMDevPropVtbl =
1302 {
1303     MMDevPropStore_QueryInterface,
1304     MMDevPropStore_AddRef,
1305     MMDevPropStore_Release,
1306     MMDevPropStore_GetCount,
1307     MMDevPropStore_GetAt,
1308     MMDevPropStore_GetValue,
1309     MMDevPropStore_SetValue,
1310     MMDevPropStore_Commit
1311 };
1312
1313
1314 /* Property bag for IBaseFilter activation */
1315 static HRESULT WINAPI PB_QueryInterface(IPropertyBag *iface, REFIID riid, void **ppv)
1316 {
1317     ERR("Should not be called\n");
1318     *ppv = NULL;
1319     return E_NOINTERFACE;
1320 }
1321
1322 static ULONG WINAPI PB_AddRef(IPropertyBag *iface)
1323 {
1324     ERR("Should not be called\n");
1325     return 2;
1326 }
1327
1328 static ULONG WINAPI PB_Release(IPropertyBag *iface)
1329 {
1330     ERR("Should not be called\n");
1331     return 1;
1332 }
1333
1334 static HRESULT WINAPI PB_Read(IPropertyBag *iface, LPCOLESTR name, VARIANT *var, IErrorLog *log)
1335 {
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))
1340     {
1341         WCHAR guidstr[39];
1342         StringFromGUID2(&This->devguid, guidstr,sizeof(guidstr)/sizeof(*guidstr));
1343         var->n1.n2.vt = VT_BSTR;
1344         var->n1.n2.n3.bstrVal = SysAllocString(guidstr);
1345         return S_OK;
1346     }
1347     ERR("Unknown property '%s' queried\n", debugstr_w(name));
1348     return E_FAIL;
1349 }
1350
1351 static HRESULT WINAPI PB_Write(IPropertyBag *iface, LPCOLESTR name, VARIANT *var)
1352 {
1353     ERR("Should not be called\n");
1354     return E_FAIL;
1355 }
1356
1357 static const IPropertyBagVtbl PB_Vtbl =
1358 {
1359     PB_QueryInterface,
1360     PB_AddRef,
1361     PB_Release,
1362     PB_Read,
1363     PB_Write
1364 };