winecoreaudio: Use mmdevapi's AudioClient's GetService.
[wine.git] / dlls / winecoreaudio.drv / mmdevdrv.c
blobdcd712c7c7a3d39ff6a5dbdd533c3ebecc89d58b
1 /*
2 * Copyright 2011 Andrew Eikum for CodeWeavers
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
18 #define COBJMACROS
20 #include <stdarg.h>
21 #include <wchar.h>
23 #include "windef.h"
24 #include "winbase.h"
25 #include "winternl.h"
26 #include "winnls.h"
27 #include "winreg.h"
28 #include "wine/debug.h"
29 #include "wine/heap.h"
30 #include "wine/list.h"
31 #include "wine/unixlib.h"
33 #include "ole2.h"
34 #include "mmdeviceapi.h"
35 #include "devpkey.h"
36 #include "dshow.h"
37 #include "dsound.h"
39 #include "initguid.h"
40 #include "endpointvolume.h"
41 #include "audioclient.h"
42 #include "audiopolicy.h"
43 #include "unixlib.h"
45 #include "../mmdevapi/mmdevdrv.h"
47 WINE_DEFAULT_DEBUG_CHANNEL(coreaudio);
49 #define NULL_PTR_ERR MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, RPC_X_NULL_REF_POINTER)
51 static const IAudioClient3Vtbl AudioClient3_Vtbl;
52 extern const IAudioRenderClientVtbl AudioRenderClient_Vtbl;
53 extern const IAudioCaptureClientVtbl AudioCaptureClient_Vtbl;
54 extern const IAudioSessionControl2Vtbl AudioSessionControl2_Vtbl;
55 extern const ISimpleAudioVolumeVtbl SimpleAudioVolume_Vtbl;
56 extern const IAudioClockVtbl AudioClock_Vtbl;
57 extern const IAudioClock2Vtbl AudioClock2_Vtbl;
58 extern const IAudioStreamVolumeVtbl AudioStreamVolume_Vtbl;
59 extern const IChannelAudioVolumeVtbl ChannelAudioVolume_Vtbl;
61 static WCHAR drv_key_devicesW[256];
63 static CRITICAL_SECTION g_sessions_lock;
64 static CRITICAL_SECTION_DEBUG g_sessions_lock_debug =
66 0, 0, &g_sessions_lock,
67 { &g_sessions_lock_debug.ProcessLocksList, &g_sessions_lock_debug.ProcessLocksList },
68 0, 0, { (DWORD_PTR)(__FILE__ ": g_sessions_lock") }
70 static CRITICAL_SECTION g_sessions_lock = { &g_sessions_lock_debug, -1, 0, 0, 0, 0 };
71 static struct list g_sessions = LIST_INIT(g_sessions);
73 extern struct audio_session_wrapper *session_wrapper_create(
74 struct audio_client *client) DECLSPEC_HIDDEN;
76 void DECLSPEC_HIDDEN sessions_lock(void)
78 EnterCriticalSection(&g_sessions_lock);
81 void DECLSPEC_HIDDEN sessions_unlock(void)
83 LeaveCriticalSection(&g_sessions_lock);
86 static inline ACImpl *impl_from_IAudioClient3(IAudioClient3 *iface)
88 return CONTAINING_RECORD(iface, ACImpl, IAudioClient3_iface);
91 BOOL WINAPI DllMain(HINSTANCE dll, DWORD reason, void *reserved)
93 switch (reason)
95 case DLL_PROCESS_ATTACH:
97 WCHAR buf[MAX_PATH];
98 WCHAR *filename;
100 DisableThreadLibraryCalls(dll);
101 if (__wine_init_unix_call())
102 return FALSE;
104 GetModuleFileNameW(dll, buf, ARRAY_SIZE(buf));
106 filename = wcsrchr(buf, '\\');
107 filename = filename ? filename + 1 : buf;
109 swprintf(drv_key_devicesW, ARRAY_SIZE(drv_key_devicesW),
110 L"Software\\Wine\\Drivers\\%s\\devices", filename);
112 break;
114 case DLL_PROCESS_DETACH:
115 if (reserved) break;
116 DeleteCriticalSection(&g_sessions_lock);
117 break;
119 return TRUE;
122 static void set_device_guid(EDataFlow flow, HKEY drv_key, const WCHAR *key_name,
123 GUID *guid)
125 HKEY key;
126 BOOL opened = FALSE;
127 LONG lr;
129 if(!drv_key){
130 lr = RegCreateKeyExW(HKEY_CURRENT_USER, drv_key_devicesW, 0, NULL, 0, KEY_WRITE,
131 NULL, &drv_key, NULL);
132 if(lr != ERROR_SUCCESS){
133 ERR("RegCreateKeyEx(drv_key) failed: %lu\n", lr);
134 return;
136 opened = TRUE;
139 lr = RegCreateKeyExW(drv_key, key_name, 0, NULL, 0, KEY_WRITE,
140 NULL, &key, NULL);
141 if(lr != ERROR_SUCCESS){
142 ERR("RegCreateKeyEx(%s) failed: %lu\n", wine_dbgstr_w(key_name), lr);
143 goto exit;
146 lr = RegSetValueExW(key, L"guid", 0, REG_BINARY, (BYTE*)guid,
147 sizeof(GUID));
148 if(lr != ERROR_SUCCESS)
149 ERR("RegSetValueEx(%s\\guid) failed: %lu\n", wine_dbgstr_w(key_name), lr);
151 RegCloseKey(key);
152 exit:
153 if(opened)
154 RegCloseKey(drv_key);
157 static void get_device_guid(EDataFlow flow, const char *dev, GUID *guid)
159 HKEY key = NULL, dev_key;
160 DWORD type, size = sizeof(*guid);
161 WCHAR key_name[256];
163 if(flow == eCapture)
164 key_name[0] = '1';
165 else
166 key_name[0] = '0';
167 key_name[1] = ',';
169 MultiByteToWideChar(CP_UNIXCP, 0, dev, -1, key_name + 2, ARRAY_SIZE(key_name) - 2);
171 if(RegOpenKeyExW(HKEY_CURRENT_USER, drv_key_devicesW, 0, KEY_WRITE|KEY_READ, &key) == ERROR_SUCCESS){
172 if(RegOpenKeyExW(key, key_name, 0, KEY_READ, &dev_key) == ERROR_SUCCESS){
173 if(RegQueryValueExW(dev_key, L"guid", 0, &type,
174 (BYTE*)guid, &size) == ERROR_SUCCESS){
175 if(type == REG_BINARY){
176 RegCloseKey(dev_key);
177 RegCloseKey(key);
178 return;
180 ERR("Invalid type for device %s GUID: %lu; ignoring and overwriting\n",
181 wine_dbgstr_w(key_name), type);
183 RegCloseKey(dev_key);
187 CoCreateGuid(guid);
189 set_device_guid(flow, key, key_name, guid);
191 if(key)
192 RegCloseKey(key);
195 static void set_stream_volumes(ACImpl *This)
197 struct set_volumes_params params;
199 params.stream = This->stream;
200 params.master_volume = This->session->mute ? 0.0f : This->session->master_vol;
201 params.volumes = This->vols;
202 params.session_volumes = This->session->channel_vols;
204 UNIX_CALL(set_volumes, &params);
207 HRESULT WINAPI AUDDRV_GetEndpointIDs(EDataFlow flow, WCHAR ***ids_out,
208 GUID **guids_out, UINT *num, UINT *def_index)
210 struct get_endpoint_ids_params params;
211 unsigned int i;
212 GUID *guids = NULL;
213 WCHAR **ids = NULL;
215 TRACE("%d %p %p %p\n", flow, ids_out, num, def_index);
217 params.flow = flow;
218 params.size = 1000;
219 params.endpoints = NULL;
221 heap_free(params.endpoints);
222 params.endpoints = heap_alloc(params.size);
223 UNIX_CALL(get_endpoint_ids, &params);
224 }while(params.result == HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER));
226 if(FAILED(params.result)) goto end;
228 ids = heap_alloc_zero(params.num * sizeof(*ids));
229 guids = heap_alloc(params.num * sizeof(*guids));
230 if(!ids || !guids){
231 params.result = E_OUTOFMEMORY;
232 goto end;
235 for(i = 0; i < params.num; i++){
236 const WCHAR *name = (WCHAR *)((char *)params.endpoints + params.endpoints[i].name);
237 const char *device = (char *)params.endpoints + params.endpoints[i].device;
238 const unsigned int size = (wcslen(name) + 1) * sizeof(WCHAR);
240 ids[i] = heap_alloc(size);
241 if(!ids[i]){
242 params.result = E_OUTOFMEMORY;
243 goto end;
245 memcpy(ids[i], name, size);
246 get_device_guid(flow, device, guids + i);
248 *def_index = params.default_idx;
250 end:
251 heap_free(params.endpoints);
252 if(FAILED(params.result)){
253 heap_free(guids);
254 if(ids){
255 for(i = 0; i < params.num; i++) heap_free(ids[i]);
256 heap_free(ids);
258 }else{
259 *ids_out = ids;
260 *guids_out = guids;
261 *num = params.num;
264 return params.result;
267 static BOOL get_device_name_by_guid(const GUID *guid, char *name, const SIZE_T name_size, EDataFlow *flow)
269 HKEY devices_key;
270 UINT i = 0;
271 WCHAR key_name[256];
272 DWORD key_name_size;
274 if(RegOpenKeyExW(HKEY_CURRENT_USER, drv_key_devicesW, 0, KEY_READ, &devices_key) != ERROR_SUCCESS){
275 ERR("No devices in registry?\n");
276 return FALSE;
279 while(1){
280 HKEY key;
281 DWORD size, type;
282 GUID reg_guid;
284 key_name_size = ARRAY_SIZE(key_name);
285 if(RegEnumKeyExW(devices_key, i++, key_name, &key_name_size, NULL,
286 NULL, NULL, NULL) != ERROR_SUCCESS)
287 break;
289 if(RegOpenKeyExW(devices_key, key_name, 0, KEY_READ, &key) != ERROR_SUCCESS){
290 WARN("Couldn't open key: %s\n", wine_dbgstr_w(key_name));
291 continue;
294 size = sizeof(reg_guid);
295 if(RegQueryValueExW(key, L"guid", 0, &type,
296 (BYTE*)&reg_guid, &size) == ERROR_SUCCESS){
297 if(IsEqualGUID(&reg_guid, guid)){
298 RegCloseKey(key);
299 RegCloseKey(devices_key);
301 TRACE("Found matching device key: %s\n", wine_dbgstr_w(key_name));
303 if(key_name[0] == '0')
304 *flow = eRender;
305 else if(key_name[0] == '1')
306 *flow = eCapture;
307 else{
308 ERR("Unknown device type: %c\n", key_name[0]);
309 return FALSE;
312 WideCharToMultiByte(CP_UNIXCP, 0, key_name + 2, -1, name, name_size, NULL, NULL);
314 return TRUE;
318 RegCloseKey(key);
321 RegCloseKey(devices_key);
323 WARN("No matching device in registry for GUID %s\n", debugstr_guid(guid));
325 return FALSE;
328 HRESULT WINAPI AUDDRV_GetAudioEndpoint(GUID *guid, IMMDevice *dev, IAudioClient **out)
330 ACImpl *This;
331 char name[256];
332 SIZE_T name_len;
333 EDataFlow dataflow;
334 HRESULT hr;
336 TRACE("%s %p %p\n", debugstr_guid(guid), dev, out);
338 if(!get_device_name_by_guid(guid, name, sizeof(name), &dataflow))
339 return AUDCLNT_E_DEVICE_INVALIDATED;
341 if(dataflow != eRender && dataflow != eCapture)
342 return E_INVALIDARG;
344 name_len = strlen(name);
345 This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, offsetof(ACImpl, device_name[name_len + 1]));
346 if(!This)
347 return E_OUTOFMEMORY;
349 This->IAudioClient3_iface.lpVtbl = &AudioClient3_Vtbl;
350 This->IAudioRenderClient_iface.lpVtbl = &AudioRenderClient_Vtbl;
351 This->IAudioCaptureClient_iface.lpVtbl = &AudioCaptureClient_Vtbl;
352 This->IAudioClock_iface.lpVtbl = &AudioClock_Vtbl;
353 This->IAudioClock2_iface.lpVtbl = &AudioClock2_Vtbl;
354 This->IAudioStreamVolume_iface.lpVtbl = &AudioStreamVolume_Vtbl;
356 This->dataflow = dataflow;
357 memcpy(This->device_name, name, name_len + 1);
359 hr = CoCreateFreeThreadedMarshaler((IUnknown *)&This->IAudioClient3_iface, &This->marshal);
360 if (FAILED(hr)) {
361 HeapFree(GetProcessHeap(), 0, This);
362 return hr;
365 This->parent = dev;
366 IMMDevice_AddRef(This->parent);
368 *out = (IAudioClient *)&This->IAudioClient3_iface;
369 IAudioClient3_AddRef(&This->IAudioClient3_iface);
371 return S_OK;
374 static HRESULT WINAPI AudioClient_QueryInterface(IAudioClient3 *iface,
375 REFIID riid, void **ppv)
377 ACImpl *This = impl_from_IAudioClient3(iface);
378 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
380 if(!ppv)
381 return E_POINTER;
382 *ppv = NULL;
383 if(IsEqualIID(riid, &IID_IUnknown) ||
384 IsEqualIID(riid, &IID_IAudioClient) ||
385 IsEqualIID(riid, &IID_IAudioClient2) ||
386 IsEqualIID(riid, &IID_IAudioClient3))
387 *ppv = iface;
388 else if(IsEqualIID(riid, &IID_IMarshal))
389 return IUnknown_QueryInterface(This->marshal, riid, ppv);
391 if(*ppv){
392 IUnknown_AddRef((IUnknown*)*ppv);
393 return S_OK;
395 WARN("Unknown interface %s\n", debugstr_guid(riid));
396 return E_NOINTERFACE;
399 static ULONG WINAPI AudioClient_AddRef(IAudioClient3 *iface)
401 ACImpl *This = impl_from_IAudioClient3(iface);
402 ULONG ref;
403 ref = InterlockedIncrement(&This->ref);
404 TRACE("(%p) Refcount now %lu\n", This, ref);
405 return ref;
408 static ULONG WINAPI AudioClient_Release(IAudioClient3 *iface)
410 ACImpl *This = impl_from_IAudioClient3(iface);
411 ULONG ref;
412 ref = InterlockedDecrement(&This->ref);
413 TRACE("(%p) Refcount now %lu\n", This, ref);
414 if(!ref){
415 if(This->stream){
416 struct release_stream_params params;
417 params.stream = This->stream;
418 params.timer_thread = This->timer_thread;
419 UNIX_CALL(release_stream, &params);
420 This->stream = 0;
422 sessions_lock();
423 list_remove(&This->entry);
424 sessions_unlock();
426 HeapFree(GetProcessHeap(), 0, This->vols);
427 IMMDevice_Release(This->parent);
428 IUnknown_Release(This->marshal);
429 HeapFree(GetProcessHeap(), 0, This);
431 return ref;
434 static void dump_fmt(const WAVEFORMATEX *fmt)
436 TRACE("wFormatTag: 0x%x (", fmt->wFormatTag);
437 switch(fmt->wFormatTag){
438 case WAVE_FORMAT_PCM:
439 TRACE("WAVE_FORMAT_PCM");
440 break;
441 case WAVE_FORMAT_IEEE_FLOAT:
442 TRACE("WAVE_FORMAT_IEEE_FLOAT");
443 break;
444 case WAVE_FORMAT_EXTENSIBLE:
445 TRACE("WAVE_FORMAT_EXTENSIBLE");
446 break;
447 default:
448 TRACE("Unknown");
449 break;
451 TRACE(")\n");
453 TRACE("nChannels: %u\n", fmt->nChannels);
454 TRACE("nSamplesPerSec: %lu\n", fmt->nSamplesPerSec);
455 TRACE("nAvgBytesPerSec: %lu\n", fmt->nAvgBytesPerSec);
456 TRACE("nBlockAlign: %u\n", fmt->nBlockAlign);
457 TRACE("wBitsPerSample: %u\n", fmt->wBitsPerSample);
458 TRACE("cbSize: %u\n", fmt->cbSize);
460 if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE){
461 WAVEFORMATEXTENSIBLE *fmtex = (void*)fmt;
462 TRACE("dwChannelMask: %08lx\n", fmtex->dwChannelMask);
463 TRACE("Samples: %04x\n", fmtex->Samples.wReserved);
464 TRACE("SubFormat: %s\n", wine_dbgstr_guid(&fmtex->SubFormat));
468 static void session_init_vols(AudioSession *session, UINT channels)
470 if(session->channel_count < channels){
471 UINT i;
473 if(session->channel_vols)
474 session->channel_vols = HeapReAlloc(GetProcessHeap(), 0,
475 session->channel_vols, sizeof(float) * channels);
476 else
477 session->channel_vols = HeapAlloc(GetProcessHeap(), 0,
478 sizeof(float) * channels);
479 if(!session->channel_vols)
480 return;
482 for(i = session->channel_count; i < channels; ++i)
483 session->channel_vols[i] = 1.f;
485 session->channel_count = channels;
489 static AudioSession *create_session(const GUID *guid, IMMDevice *device,
490 UINT num_channels)
492 AudioSession *ret;
494 ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(AudioSession));
495 if(!ret)
496 return NULL;
498 memcpy(&ret->guid, guid, sizeof(GUID));
500 ret->device = device;
502 list_init(&ret->clients);
504 list_add_head(&g_sessions, &ret->entry);
506 session_init_vols(ret, num_channels);
508 ret->master_vol = 1.f;
510 return ret;
513 /* if channels == 0, then this will return or create a session with
514 * matching dataflow and GUID. otherwise, channels must also match */
515 static HRESULT get_audio_session(const GUID *sessionguid,
516 IMMDevice *device, UINT channels, AudioSession **out)
518 AudioSession *session;
520 if(!sessionguid || IsEqualGUID(sessionguid, &GUID_NULL)){
521 *out = create_session(&GUID_NULL, device, channels);
522 if(!*out)
523 return E_OUTOFMEMORY;
525 return S_OK;
528 *out = NULL;
529 LIST_FOR_EACH_ENTRY(session, &g_sessions, AudioSession, entry){
530 if(session->device == device &&
531 IsEqualGUID(sessionguid, &session->guid)){
532 session_init_vols(session, channels);
533 *out = session;
534 break;
538 if(!*out){
539 *out = create_session(sessionguid, device, channels);
540 if(!*out)
541 return E_OUTOFMEMORY;
544 return S_OK;
547 static HRESULT WINAPI AudioClient_Initialize(IAudioClient3 *iface,
548 AUDCLNT_SHAREMODE mode, DWORD flags, REFERENCE_TIME duration,
549 REFERENCE_TIME period, const WAVEFORMATEX *fmt,
550 const GUID *sessionguid)
552 ACImpl *This = impl_from_IAudioClient3(iface);
553 struct release_stream_params release_params;
554 struct create_stream_params params;
555 stream_handle stream;
556 UINT32 i;
558 TRACE("(%p)->(%x, %lx, %s, %s, %p, %s)\n", This, mode, flags,
559 wine_dbgstr_longlong(duration), wine_dbgstr_longlong(period), fmt, debugstr_guid(sessionguid));
561 if(!fmt)
562 return E_POINTER;
564 dump_fmt(fmt);
566 if(mode != AUDCLNT_SHAREMODE_SHARED && mode != AUDCLNT_SHAREMODE_EXCLUSIVE)
567 return E_INVALIDARG;
569 if(flags & ~(AUDCLNT_STREAMFLAGS_CROSSPROCESS |
570 AUDCLNT_STREAMFLAGS_LOOPBACK |
571 AUDCLNT_STREAMFLAGS_EVENTCALLBACK |
572 AUDCLNT_STREAMFLAGS_NOPERSIST |
573 AUDCLNT_STREAMFLAGS_RATEADJUST |
574 AUDCLNT_SESSIONFLAGS_EXPIREWHENUNOWNED |
575 AUDCLNT_SESSIONFLAGS_DISPLAY_HIDE |
576 AUDCLNT_SESSIONFLAGS_DISPLAY_HIDEWHENEXPIRED |
577 AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY |
578 AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM)){
579 FIXME("Unknown flags: %08lx\n", flags);
580 return E_INVALIDARG;
583 sessions_lock();
585 if(This->stream){
586 sessions_unlock();
587 return AUDCLNT_E_ALREADY_INITIALIZED;
590 params.name = NULL;
591 params.device = This->device_name;
592 params.flow = This->dataflow;
593 params.share = mode;
594 params.flags = flags;
595 params.duration = duration;
596 params.period = period;
597 params.fmt = fmt;
598 params.channel_count = NULL;
599 params.stream = &stream;
601 UNIX_CALL(create_stream, &params);
602 if(FAILED(params.result)){
603 sessions_unlock();
604 return params.result;
607 This->channel_count = fmt->nChannels;
609 This->vols = HeapAlloc(GetProcessHeap(), 0, This->channel_count * sizeof(float));
610 if(!This->vols){
611 params.result = E_OUTOFMEMORY;
612 goto end;
615 for(i = 0; i < This->channel_count; ++i)
616 This->vols[i] = 1.f;
618 params.result = get_audio_session(sessionguid, This->parent, fmt->nChannels, &This->session);
619 if(FAILED(params.result)) goto end;
621 list_add_tail(&This->session->clients, &This->entry);
623 end:
624 if(FAILED(params.result)){
625 release_params.stream = stream;
626 UNIX_CALL(release_stream, &release_params);
627 HeapFree(GetProcessHeap(), 0, This->vols);
628 This->vols = NULL;
629 }else{
630 This->stream = stream;
631 set_stream_volumes(This);
634 sessions_unlock();
636 return params.result;
639 static HRESULT WINAPI AudioClient_GetBufferSize(IAudioClient3 *iface,
640 UINT32 *frames)
642 ACImpl *This = impl_from_IAudioClient3(iface);
643 struct get_buffer_size_params params;
645 TRACE("(%p)->(%p)\n", This, frames);
647 if(!frames)
648 return E_POINTER;
650 if(!This->stream)
651 return AUDCLNT_E_NOT_INITIALIZED;
653 params.stream = This->stream;
654 params.frames = frames;
655 UNIX_CALL(get_buffer_size, &params);
656 return params.result;
659 static HRESULT WINAPI AudioClient_GetStreamLatency(IAudioClient3 *iface,
660 REFERENCE_TIME *out)
662 ACImpl *This = impl_from_IAudioClient3(iface);
663 struct get_latency_params params;
665 TRACE("(%p)->(%p)\n", This, out);
667 if(!out)
668 return E_POINTER;
670 if(!This->stream)
671 return AUDCLNT_E_NOT_INITIALIZED;
673 params.stream = This->stream;
674 params.latency = out;
675 UNIX_CALL(get_latency, &params);
676 return params.result;
679 static HRESULT WINAPI AudioClient_GetCurrentPadding(IAudioClient3 *iface,
680 UINT32 *numpad)
682 ACImpl *This = impl_from_IAudioClient3(iface);
683 struct get_current_padding_params params;
685 TRACE("(%p)->(%p)\n", This, numpad);
687 if(!numpad)
688 return E_POINTER;
690 if(!This->stream)
691 return AUDCLNT_E_NOT_INITIALIZED;
693 params.stream = This->stream;
694 params.padding = numpad;
695 UNIX_CALL(get_current_padding, &params);
696 return params.result;
699 static HRESULT WINAPI AudioClient_IsFormatSupported(IAudioClient3 *iface,
700 AUDCLNT_SHAREMODE mode, const WAVEFORMATEX *pwfx,
701 WAVEFORMATEX **outpwfx)
703 ACImpl *This = impl_from_IAudioClient3(iface);
704 struct is_format_supported_params params;
706 TRACE("(%p)->(%x, %p, %p)\n", This, mode, pwfx, outpwfx);
707 if(pwfx) dump_fmt(pwfx);
709 params.device = This->device_name;
710 params.flow = This->dataflow;
711 params.share = mode;
712 params.fmt_in = pwfx;
713 params.fmt_out = NULL;
715 if(outpwfx){
716 *outpwfx = NULL;
717 if(mode == AUDCLNT_SHAREMODE_SHARED)
718 params.fmt_out = CoTaskMemAlloc(sizeof(*params.fmt_out));
720 UNIX_CALL(is_format_supported, &params);
722 if(params.result == S_FALSE)
723 *outpwfx = &params.fmt_out->Format;
724 else
725 CoTaskMemFree(params.fmt_out);
727 return params.result;
730 static HRESULT WINAPI AudioClient_GetMixFormat(IAudioClient3 *iface,
731 WAVEFORMATEX **pwfx)
733 ACImpl *This = impl_from_IAudioClient3(iface);
734 struct get_mix_format_params params;
736 TRACE("(%p)->(%p)\n", This, pwfx);
738 if(!pwfx)
739 return E_POINTER;
740 *pwfx = NULL;
742 params.device = This->device_name;
743 params.flow = This->dataflow;
744 params.fmt = CoTaskMemAlloc(sizeof(WAVEFORMATEXTENSIBLE));
745 if(!params.fmt)
746 return E_OUTOFMEMORY;
748 UNIX_CALL(get_mix_format, &params);
750 if(SUCCEEDED(params.result)){
751 *pwfx = &params.fmt->Format;
752 dump_fmt(*pwfx);
753 }else
754 CoTaskMemFree(params.fmt);
756 return params.result;
759 static HRESULT WINAPI AudioClient_GetDevicePeriod(IAudioClient3 *iface,
760 REFERENCE_TIME *defperiod, REFERENCE_TIME *minperiod)
762 ACImpl *This = impl_from_IAudioClient3(iface);
763 struct get_device_period_params params;
765 TRACE("(%p)->(%p, %p)\n", This, defperiod, minperiod);
767 if (!defperiod && !minperiod)
768 return E_POINTER;
770 params.device = This->device_name;
771 params.flow = This->dataflow;
772 params.def_period = defperiod;
773 params.min_period = minperiod;
775 UNIX_CALL(get_device_period, &params);
777 return params.result;
780 static DWORD WINAPI ca_timer_thread(void *user)
782 struct timer_loop_params params;
783 ACImpl *This = user;
784 params.stream = This->stream;
785 SetThreadDescription(GetCurrentThread(), L"winecoreaudio_timer_loop");
786 UNIX_CALL(timer_loop, &params);
787 return 0;
790 static HRESULT WINAPI AudioClient_Start(IAudioClient3 *iface)
792 ACImpl *This = impl_from_IAudioClient3(iface);
793 struct start_params params;
794 HRESULT hr;
796 TRACE("(%p)\n", This);
798 if(!This->stream)
799 return AUDCLNT_E_NOT_INITIALIZED;
801 params.stream = This->stream;
802 UNIX_CALL(start, &params);
803 if(FAILED(hr = params.result))
804 return hr;
806 if(!This->timer_thread) {
807 This->timer_thread = CreateThread(NULL, 0, ca_timer_thread, This, 0, NULL);
808 SetThreadPriority(This->timer_thread, THREAD_PRIORITY_TIME_CRITICAL);
811 return S_OK;
814 static HRESULT WINAPI AudioClient_Stop(IAudioClient3 *iface)
816 ACImpl *This = impl_from_IAudioClient3(iface);
817 struct stop_params params;
819 TRACE("(%p)\n", This);
821 if(!This->stream)
822 return AUDCLNT_E_NOT_INITIALIZED;
824 params.stream = This->stream;
825 UNIX_CALL(stop, &params);
826 return params.result;
829 static HRESULT WINAPI AudioClient_Reset(IAudioClient3 *iface)
831 ACImpl *This = impl_from_IAudioClient3(iface);
832 struct reset_params params;
834 TRACE("(%p)\n", This);
836 if(!This->stream)
837 return AUDCLNT_E_NOT_INITIALIZED;
839 params.stream = This->stream;
840 UNIX_CALL(reset, &params);
841 return params.result;
844 static HRESULT WINAPI AudioClient_SetEventHandle(IAudioClient3 *iface,
845 HANDLE event)
847 ACImpl *This = impl_from_IAudioClient3(iface);
848 struct set_event_handle_params params;
850 TRACE("(%p)->(%p)\n", This, event);
852 if(!event)
853 return E_INVALIDARG;
855 if(!This->stream)
856 return AUDCLNT_E_NOT_INITIALIZED;
858 params.stream = This->stream;
859 params.event = event;
860 UNIX_CALL(set_event_handle, &params);
861 return params.result;
864 extern HRESULT WINAPI client_GetService(IAudioClient3 *iface, REFIID riid,
865 void **ppv);
867 extern HRESULT WINAPI client_IsOffloadCapable(IAudioClient3 *iface,
868 AUDIO_STREAM_CATEGORY category, BOOL *offload_capable);
870 extern HRESULT WINAPI client_SetClientProperties(IAudioClient3 *iface,
871 const AudioClientProperties *prop);
873 extern HRESULT WINAPI client_GetBufferSizeLimits(IAudioClient3 *iface,
874 const WAVEFORMATEX *format, BOOL event_driven, REFERENCE_TIME *min_duration,
875 REFERENCE_TIME *max_duration);
877 extern HRESULT WINAPI client_GetSharedModeEnginePeriod(IAudioClient3 *iface,
878 const WAVEFORMATEX *format, UINT32 *default_period_frames, UINT32 *unit_period_frames,
879 UINT32 *min_period_frames, UINT32 *max_period_frames);
881 extern HRESULT WINAPI client_GetCurrentSharedModeEnginePeriod(IAudioClient3 *iface,
882 WAVEFORMATEX **cur_format, UINT32 *cur_period_frames);
884 extern HRESULT WINAPI client_InitializeSharedAudioStream(IAudioClient3 *iface,
885 DWORD flags, UINT32 period_frames, const WAVEFORMATEX *format,
886 const GUID *session_guid);
888 static const IAudioClient3Vtbl AudioClient3_Vtbl =
890 AudioClient_QueryInterface,
891 AudioClient_AddRef,
892 AudioClient_Release,
893 AudioClient_Initialize,
894 AudioClient_GetBufferSize,
895 AudioClient_GetStreamLatency,
896 AudioClient_GetCurrentPadding,
897 AudioClient_IsFormatSupported,
898 AudioClient_GetMixFormat,
899 AudioClient_GetDevicePeriod,
900 AudioClient_Start,
901 AudioClient_Stop,
902 AudioClient_Reset,
903 AudioClient_SetEventHandle,
904 client_GetService,
905 client_IsOffloadCapable,
906 client_SetClientProperties,
907 client_GetBufferSizeLimits,
908 client_GetSharedModeEnginePeriod,
909 client_GetCurrentSharedModeEnginePeriod,
910 client_InitializeSharedAudioStream,
913 HRESULT WINAPI AUDDRV_GetAudioSessionWrapper(const GUID *guid, IMMDevice *device,
914 AudioSessionWrapper **out)
916 AudioSession *session;
918 HRESULT hr = get_audio_session(guid, device, 0, &session);
919 if(FAILED(hr))
920 return hr;
922 *out = session_wrapper_create(NULL);
923 if(!*out)
924 return E_OUTOFMEMORY;
926 (*out)->session = session;
928 return S_OK;