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