wineoss: Use mmdevapi's AudioRenderClient.
[wine.git] / dlls / wineoss.drv / mmdevdrv.c
blobdac5790e384592cce723f040ad64c27a5412a412
1 /*
2 * Copyright 2011 Andrew Eikum for CodeWeavers
3 * 2022 Huw Davies
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20 #define COBJMACROS
21 #include <stdarg.h>
22 #include <wchar.h>
24 #include "windef.h"
25 #include "winbase.h"
26 #include "winternl.h"
27 #include "winnls.h"
28 #include "winreg.h"
30 #include "ole2.h"
31 #include "mmdeviceapi.h"
32 #include "devpkey.h"
33 #include "dshow.h"
34 #include "dsound.h"
36 #include "initguid.h"
37 #include "endpointvolume.h"
38 #include "audiopolicy.h"
39 #include "audioclient.h"
41 #include "wine/debug.h"
42 #include "wine/list.h"
43 #include "wine/unixlib.h"
45 #include "unixlib.h"
47 #include "../mmdevapi/mmdevdrv.h"
49 WINE_DEFAULT_DEBUG_CHANNEL(oss);
51 #define NULL_PTR_ERR MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, RPC_X_NULL_REF_POINTER)
53 static const REFERENCE_TIME DefaultPeriod = 100000;
54 static const REFERENCE_TIME MinimumPeriod = 50000;
56 typedef struct _OSSDevice {
57 struct list entry;
58 EDataFlow flow;
59 GUID guid;
60 char devnode[0];
61 } OSSDevice;
63 static struct list g_devices = LIST_INIT(g_devices);
65 static WCHAR drv_key_devicesW[256];
66 static const WCHAR guidW[] = {'g','u','i','d',0};
68 static CRITICAL_SECTION g_sessions_lock;
69 static CRITICAL_SECTION_DEBUG g_sessions_lock_debug =
71 0, 0, &g_sessions_lock,
72 { &g_sessions_lock_debug.ProcessLocksList, &g_sessions_lock_debug.ProcessLocksList },
73 0, 0, { (DWORD_PTR)(__FILE__ ": g_sessions_lock") }
75 static CRITICAL_SECTION g_sessions_lock = { &g_sessions_lock_debug, -1, 0, 0, 0, 0 };
76 static struct list g_sessions = LIST_INIT(g_sessions);
78 static AudioSessionWrapper *AudioSessionWrapper_Create(ACImpl *client);
80 static const IAudioClient3Vtbl AudioClient3_Vtbl;
81 extern const IAudioRenderClientVtbl AudioRenderClient_Vtbl;
82 extern const IAudioCaptureClientVtbl AudioCaptureClient_Vtbl;
83 extern const IAudioSessionControl2Vtbl AudioSessionControl2_Vtbl;
84 extern const ISimpleAudioVolumeVtbl SimpleAudioVolume_Vtbl;
85 extern const IAudioClockVtbl AudioClock_Vtbl;
86 extern const IAudioClock2Vtbl AudioClock2_Vtbl;
87 extern const IAudioStreamVolumeVtbl AudioStreamVolume_Vtbl;
88 extern const IChannelAudioVolumeVtbl ChannelAudioVolume_Vtbl;
90 void DECLSPEC_HIDDEN sessions_lock(void)
92 EnterCriticalSection(&g_sessions_lock);
95 void DECLSPEC_HIDDEN sessions_unlock(void)
97 LeaveCriticalSection(&g_sessions_lock);
100 static inline ACImpl *impl_from_IAudioClient3(IAudioClient3 *iface)
102 return CONTAINING_RECORD(iface, ACImpl, IAudioClient3_iface);
105 BOOL WINAPI DllMain(HINSTANCE dll, DWORD reason, void *reserved)
107 switch (reason)
109 case DLL_PROCESS_ATTACH:
111 WCHAR buf[MAX_PATH];
112 WCHAR *filename;
114 if(__wine_init_unix_call()) return FALSE;
116 GetModuleFileNameW(dll, buf, ARRAY_SIZE(buf));
118 filename = wcsrchr(buf, '\\');
119 filename = filename ? filename + 1 : buf;
121 swprintf(drv_key_devicesW, ARRAY_SIZE(drv_key_devicesW),
122 L"Software\\Wine\\Drivers\\%s\\devices", filename);
124 break;
126 case DLL_PROCESS_DETACH:
127 if (!reserved)
129 OSSDevice *iter, *iter2;
131 DeleteCriticalSection(&g_sessions_lock);
133 LIST_FOR_EACH_ENTRY_SAFE(iter, iter2, &g_devices, OSSDevice, entry){
134 HeapFree(GetProcessHeap(), 0, iter);
137 break;
139 return TRUE;
142 static HRESULT stream_release(stream_handle stream, HANDLE timer_thread)
144 struct release_stream_params params;
146 params.stream = stream;
147 params.timer_thread = timer_thread;
148 OSS_CALL(release_stream, &params);
150 return params.result;
153 static DWORD WINAPI timer_thread(void *user)
155 struct timer_loop_params params;
156 ACImpl *This = user;
158 params.stream = This->stream;
159 OSS_CALL(timer_loop, &params);
161 return 0;
164 static void set_device_guid(EDataFlow flow, HKEY drv_key, const WCHAR *key_name,
165 GUID *guid)
167 HKEY key;
168 BOOL opened = FALSE;
169 LONG lr;
171 if(!drv_key){
172 lr = RegCreateKeyExW(HKEY_CURRENT_USER, drv_key_devicesW, 0, NULL, 0, KEY_WRITE,
173 NULL, &drv_key, NULL);
174 if(lr != ERROR_SUCCESS){
175 ERR("RegCreateKeyEx(drv_key) failed: %lu\n", lr);
176 return;
178 opened = TRUE;
181 lr = RegCreateKeyExW(drv_key, key_name, 0, NULL, 0, KEY_WRITE,
182 NULL, &key, NULL);
183 if(lr != ERROR_SUCCESS){
184 ERR("RegCreateKeyEx(%s) failed: %lu\n", wine_dbgstr_w(key_name), lr);
185 goto exit;
188 lr = RegSetValueExW(key, guidW, 0, REG_BINARY, (BYTE*)guid,
189 sizeof(GUID));
190 if(lr != ERROR_SUCCESS)
191 ERR("RegSetValueEx(%s\\guid) failed: %lu\n", wine_dbgstr_w(key_name), lr);
193 RegCloseKey(key);
194 exit:
195 if(opened)
196 RegCloseKey(drv_key);
199 static void get_device_guid(EDataFlow flow, const char *device, GUID *guid)
201 HKEY key = NULL, dev_key;
202 DWORD type, size = sizeof(*guid);
203 WCHAR key_name[256];
205 if(flow == eCapture)
206 key_name[0] = '1';
207 else
208 key_name[0] = '0';
209 key_name[1] = ',';
210 MultiByteToWideChar(CP_UNIXCP, 0, device, -1, key_name + 2, ARRAY_SIZE(key_name) - 2);
212 if(RegOpenKeyExW(HKEY_CURRENT_USER, drv_key_devicesW, 0, KEY_WRITE|KEY_READ, &key) == ERROR_SUCCESS){
213 if(RegOpenKeyExW(key, key_name, 0, KEY_READ, &dev_key) == ERROR_SUCCESS){
214 if(RegQueryValueExW(dev_key, guidW, 0, &type,
215 (BYTE*)guid, &size) == ERROR_SUCCESS){
216 if(type == REG_BINARY){
217 RegCloseKey(dev_key);
218 RegCloseKey(key);
219 return;
221 ERR("Invalid type for device %s GUID: %lu; ignoring and overwriting\n",
222 wine_dbgstr_w(key_name), type);
224 RegCloseKey(dev_key);
228 CoCreateGuid(guid);
230 set_device_guid(flow, key, key_name, guid);
232 if(key)
233 RegCloseKey(key);
236 static void set_stream_volumes(ACImpl *This)
238 struct set_volumes_params params;
240 params.stream = This->stream;
241 params.master_volume = (This->session->mute ? 0.0f : This->session->master_vol);
242 params.volumes = This->vols;
243 params.session_volumes = This->session->channel_vols;
244 OSS_CALL(set_volumes, &params);
247 static const OSSDevice *get_ossdevice_from_guid(const GUID *guid)
249 OSSDevice *dev_item;
250 LIST_FOR_EACH_ENTRY(dev_item, &g_devices, OSSDevice, entry)
251 if(IsEqualGUID(guid, &dev_item->guid))
252 return dev_item;
253 return NULL;
256 static void device_add(OSSDevice *oss_dev)
258 if(get_ossdevice_from_guid(&oss_dev->guid)) /* already in list */
259 HeapFree(GetProcessHeap(), 0, oss_dev);
260 else
261 list_add_tail(&g_devices, &oss_dev->entry);
264 HRESULT WINAPI AUDDRV_GetEndpointIDs(EDataFlow flow, WCHAR ***ids_out, GUID **guids_out,
265 UINT *num, UINT *def_index)
267 struct get_endpoint_ids_params params;
268 GUID *guids = NULL;
269 WCHAR **ids = NULL;
270 unsigned int i;
272 TRACE("%d %p %p %p %p\n", flow, ids, guids, num, def_index);
274 params.flow = flow;
275 params.size = 1000;
276 params.endpoints = NULL;
278 HeapFree(GetProcessHeap(), 0, params.endpoints);
279 params.endpoints = HeapAlloc(GetProcessHeap(), 0, params.size);
280 OSS_CALL(get_endpoint_ids, &params);
281 }while(params.result == HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER));
283 if(FAILED(params.result)) goto end;
285 ids = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, params.num * sizeof(*ids));
286 guids = HeapAlloc(GetProcessHeap(), 0, params.num * sizeof(*guids));
287 if(!ids || !guids){
288 params.result = E_OUTOFMEMORY;
289 goto end;
292 for(i = 0; i < params.num; i++){
293 WCHAR *name = (WCHAR *)((char *)params.endpoints + params.endpoints[i].name);
294 char *device = (char *)params.endpoints + params.endpoints[i].device;
295 unsigned int name_size = (wcslen(name) + 1) * sizeof(WCHAR);
296 unsigned int dev_size = strlen(device) + 1;
297 OSSDevice *oss_dev;
299 ids[i] = HeapAlloc(GetProcessHeap(), 0, name_size);
300 oss_dev = HeapAlloc(GetProcessHeap(), 0, offsetof(OSSDevice, devnode[dev_size]));
301 if(!ids[i] || !oss_dev){
302 HeapFree(GetProcessHeap(), 0, oss_dev);
303 params.result = E_OUTOFMEMORY;
304 goto end;
306 memcpy(ids[i], name, name_size);
307 get_device_guid(flow, device, guids + i);
309 oss_dev->flow = flow;
310 oss_dev->guid = guids[i];
311 memcpy(oss_dev->devnode, device, dev_size);
312 device_add(oss_dev);
314 *def_index = params.default_idx;
316 end:
317 HeapFree(GetProcessHeap(), 0, params.endpoints);
318 if(FAILED(params.result)){
319 HeapFree(GetProcessHeap(), 0, guids);
320 if(ids){
321 for(i = 0; i < params.num; i++)
322 HeapFree(GetProcessHeap(), 0, ids[i]);
323 HeapFree(GetProcessHeap(), 0, ids);
325 }else{
326 *ids_out = ids;
327 *guids_out = guids;
328 *num = params.num;
331 return params.result;
334 HRESULT WINAPI AUDDRV_GetAudioEndpoint(GUID *guid, IMMDevice *dev,
335 IAudioClient **out)
337 ACImpl *This;
338 const OSSDevice *oss_dev;
339 HRESULT hr;
340 int len;
342 TRACE("%s %p %p\n", debugstr_guid(guid), dev, out);
344 oss_dev = get_ossdevice_from_guid(guid);
345 if(!oss_dev){
346 WARN("Unknown GUID: %s\n", debugstr_guid(guid));
347 return AUDCLNT_E_DEVICE_INVALIDATED;
349 len = strlen(oss_dev->devnode);
350 This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, offsetof(ACImpl, device_name[len + 1]));
351 if(!This)
352 return E_OUTOFMEMORY;
354 hr = CoCreateFreeThreadedMarshaler((IUnknown *)&This->IAudioClient3_iface, &This->marshal);
355 if (FAILED(hr)) {
356 HeapFree(GetProcessHeap(), 0, This);
357 return hr;
360 This->dataflow = oss_dev->flow;
361 strcpy(This->device_name, oss_dev->devnode);
363 This->IAudioClient3_iface.lpVtbl = &AudioClient3_Vtbl;
364 This->IAudioRenderClient_iface.lpVtbl = &AudioRenderClient_Vtbl;
365 This->IAudioCaptureClient_iface.lpVtbl = &AudioCaptureClient_Vtbl;
366 This->IAudioClock_iface.lpVtbl = &AudioClock_Vtbl;
367 This->IAudioClock2_iface.lpVtbl = &AudioClock2_Vtbl;
368 This->IAudioStreamVolume_iface.lpVtbl = &AudioStreamVolume_Vtbl;
370 This->parent = dev;
371 IMMDevice_AddRef(This->parent);
373 *out = (IAudioClient *)&This->IAudioClient3_iface;
374 IAudioClient3_AddRef(&This->IAudioClient3_iface);
376 return S_OK;
379 static HRESULT WINAPI AudioClient_QueryInterface(IAudioClient3 *iface,
380 REFIID riid, void **ppv)
382 ACImpl *This = impl_from_IAudioClient3(iface);
383 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
385 if(!ppv)
386 return E_POINTER;
387 *ppv = NULL;
388 if(IsEqualIID(riid, &IID_IUnknown) ||
389 IsEqualIID(riid, &IID_IAudioClient) ||
390 IsEqualIID(riid, &IID_IAudioClient2) ||
391 IsEqualIID(riid, &IID_IAudioClient3))
392 *ppv = iface;
393 else if(IsEqualIID(riid, &IID_IMarshal))
394 return IUnknown_QueryInterface(This->marshal, riid, ppv);
395 if(*ppv){
396 IUnknown_AddRef((IUnknown*)*ppv);
397 return S_OK;
399 WARN("Unknown interface %s\n", debugstr_guid(riid));
400 return E_NOINTERFACE;
403 static ULONG WINAPI AudioClient_AddRef(IAudioClient3 *iface)
405 ACImpl *This = impl_from_IAudioClient3(iface);
406 ULONG ref;
407 ref = InterlockedIncrement(&This->ref);
408 TRACE("(%p) Refcount now %lu\n", This, ref);
409 return ref;
412 static ULONG WINAPI AudioClient_Release(IAudioClient3 *iface)
414 ACImpl *This = impl_from_IAudioClient3(iface);
415 ULONG ref;
417 ref = InterlockedDecrement(&This->ref);
418 TRACE("(%p) Refcount now %lu\n", This, ref);
419 if(!ref){
420 IAudioClient3_Stop(iface);
421 IMMDevice_Release(This->parent);
422 IUnknown_Release(This->marshal);
423 if(This->session){
424 sessions_lock();
425 list_remove(&This->entry);
426 sessions_unlock();
428 HeapFree(GetProcessHeap(), 0, This->vols);
429 if(This->stream)
430 stream_release(This->stream, This->timer_thread);
431 HeapFree(GetProcessHeap(), 0, This);
433 return ref;
436 static void dump_fmt(const WAVEFORMATEX *fmt)
438 TRACE("wFormatTag: 0x%x (", fmt->wFormatTag);
439 switch(fmt->wFormatTag){
440 case WAVE_FORMAT_PCM:
441 TRACE("WAVE_FORMAT_PCM");
442 break;
443 case WAVE_FORMAT_IEEE_FLOAT:
444 TRACE("WAVE_FORMAT_IEEE_FLOAT");
445 break;
446 case WAVE_FORMAT_EXTENSIBLE:
447 TRACE("WAVE_FORMAT_EXTENSIBLE");
448 break;
449 default:
450 TRACE("Unknown");
451 break;
453 TRACE(")\n");
455 TRACE("nChannels: %u\n", fmt->nChannels);
456 TRACE("nSamplesPerSec: %lu\n", fmt->nSamplesPerSec);
457 TRACE("nAvgBytesPerSec: %lu\n", fmt->nAvgBytesPerSec);
458 TRACE("nBlockAlign: %u\n", fmt->nBlockAlign);
459 TRACE("wBitsPerSample: %u\n", fmt->wBitsPerSample);
460 TRACE("cbSize: %u\n", fmt->cbSize);
462 if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE){
463 WAVEFORMATEXTENSIBLE *fmtex = (void*)fmt;
464 TRACE("dwChannelMask: %08lx\n", fmtex->dwChannelMask);
465 TRACE("Samples: %04x\n", fmtex->Samples.wReserved);
466 TRACE("SubFormat: %s\n", wine_dbgstr_guid(&fmtex->SubFormat));
470 static void session_init_vols(AudioSession *session, UINT channels)
472 if(session->channel_count < channels){
473 UINT i;
475 if(session->channel_vols)
476 session->channel_vols = HeapReAlloc(GetProcessHeap(), 0,
477 session->channel_vols, sizeof(float) * channels);
478 else
479 session->channel_vols = HeapAlloc(GetProcessHeap(), 0,
480 sizeof(float) * channels);
481 if(!session->channel_vols)
482 return;
484 for(i = session->channel_count; i < channels; ++i)
485 session->channel_vols[i] = 1.f;
487 session->channel_count = channels;
491 static AudioSession *create_session(const GUID *guid, IMMDevice *device,
492 UINT num_channels)
494 AudioSession *ret;
496 ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(AudioSession));
497 if(!ret)
498 return NULL;
500 memcpy(&ret->guid, guid, sizeof(GUID));
502 ret->device = device;
504 list_init(&ret->clients);
506 list_add_head(&g_sessions, &ret->entry);
508 session_init_vols(ret, num_channels);
510 ret->master_vol = 1.f;
512 return ret;
515 /* if channels == 0, then this will return or create a session with
516 * matching dataflow and GUID. otherwise, channels must also match */
517 static HRESULT get_audio_session(const GUID *sessionguid,
518 IMMDevice *device, UINT channels, AudioSession **out)
520 AudioSession *session;
522 if(!sessionguid || IsEqualGUID(sessionguid, &GUID_NULL)){
523 *out = create_session(&GUID_NULL, device, channels);
524 if(!*out)
525 return E_OUTOFMEMORY;
527 return S_OK;
530 *out = NULL;
531 LIST_FOR_EACH_ENTRY(session, &g_sessions, AudioSession, entry){
532 if(session->device == device &&
533 IsEqualGUID(sessionguid, &session->guid)){
534 session_init_vols(session, channels);
535 *out = session;
536 break;
540 if(!*out){
541 *out = create_session(sessionguid, device, channels);
542 if(!*out)
543 return E_OUTOFMEMORY;
546 return S_OK;
549 static HRESULT WINAPI AudioClient_Initialize(IAudioClient3 *iface,
550 AUDCLNT_SHAREMODE mode, DWORD flags, REFERENCE_TIME duration,
551 REFERENCE_TIME period, const WAVEFORMATEX *fmt,
552 const GUID *sessionguid)
554 ACImpl *This = impl_from_IAudioClient3(iface);
555 struct create_stream_params params;
556 stream_handle stream;
557 unsigned int i;
559 TRACE("(%p)->(%x, %lx, %s, %s, %p, %s)\n", This, mode, flags,
560 wine_dbgstr_longlong(duration), wine_dbgstr_longlong(period), fmt, debugstr_guid(sessionguid));
562 if(!fmt)
563 return E_POINTER;
565 dump_fmt(fmt);
567 if(mode != AUDCLNT_SHAREMODE_SHARED && mode != AUDCLNT_SHAREMODE_EXCLUSIVE)
568 return E_INVALIDARG;
570 if(flags & ~(AUDCLNT_STREAMFLAGS_CROSSPROCESS |
571 AUDCLNT_STREAMFLAGS_LOOPBACK |
572 AUDCLNT_STREAMFLAGS_EVENTCALLBACK |
573 AUDCLNT_STREAMFLAGS_NOPERSIST |
574 AUDCLNT_STREAMFLAGS_RATEADJUST |
575 AUDCLNT_SESSIONFLAGS_EXPIREWHENUNOWNED |
576 AUDCLNT_SESSIONFLAGS_DISPLAY_HIDE |
577 AUDCLNT_SESSIONFLAGS_DISPLAY_HIDEWHENEXPIRED |
578 AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY |
579 AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM)){
580 FIXME("Unknown flags: %08lx\n", flags);
581 return E_INVALIDARG;
584 if(mode == AUDCLNT_SHAREMODE_SHARED){
585 period = DefaultPeriod;
586 if( duration < 3 * period)
587 duration = 3 * period;
588 }else{
589 if(!period)
590 period = DefaultPeriod; /* not minimum */
591 if(period < MinimumPeriod || period > 5000000)
592 return AUDCLNT_E_INVALID_DEVICE_PERIOD;
593 if(duration > 20000000) /* the smaller the period, the lower this limit */
594 return AUDCLNT_E_BUFFER_SIZE_ERROR;
595 if(flags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK){
596 if(duration != period)
597 return AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL;
598 FIXME("EXCLUSIVE mode with EVENTCALLBACK\n");
599 return AUDCLNT_E_DEVICE_IN_USE;
600 }else{
601 if( duration < 8 * period)
602 duration = 8 * period; /* may grow above 2s */
606 sessions_lock();
608 if(This->stream){
609 sessions_unlock();
610 return AUDCLNT_E_ALREADY_INITIALIZED;
613 params.name = NULL;
614 params.device = This->device_name;
615 params.flow = This->dataflow;
616 params.share = mode;
617 params.flags = flags;
618 params.duration = duration;
619 params.period = period;
620 params.fmt = fmt;
621 params.channel_count = NULL;
622 params.stream = &stream;
624 OSS_CALL(create_stream, &params);
625 if(FAILED(params.result)){
626 sessions_unlock();
627 return params.result;
630 This->channel_count = fmt->nChannels;
631 This->vols = HeapAlloc(GetProcessHeap(), 0, This->channel_count * sizeof(float));
632 if(!This->vols){
633 params.result = E_OUTOFMEMORY;
634 goto exit;
636 for(i = 0; i < This->channel_count; ++i)
637 This->vols[i] = 1.f;
639 params.result = get_audio_session(sessionguid, This->parent, This->channel_count,
640 &This->session);
642 exit:
643 if(FAILED(params.result)){
644 stream_release(stream, NULL);
645 HeapFree(GetProcessHeap(), 0, This->vols);
646 This->vols = NULL;
647 } else {
648 list_add_tail(&This->session->clients, &This->entry);
649 This->stream = stream;
650 set_stream_volumes(This);
653 sessions_unlock();
655 return params.result;
658 static HRESULT WINAPI AudioClient_GetBufferSize(IAudioClient3 *iface,
659 UINT32 *frames)
661 ACImpl *This = impl_from_IAudioClient3(iface);
662 struct get_buffer_size_params params;
664 TRACE("(%p)->(%p)\n", This, frames);
666 if(!frames)
667 return E_POINTER;
669 if(!This->stream)
670 return AUDCLNT_E_NOT_INITIALIZED;
672 params.stream = This->stream;
673 params.frames = frames;
675 OSS_CALL(get_buffer_size, &params);
676 TRACE("buffer size: %u\n", *frames);
678 return params.result;
681 static HRESULT WINAPI AudioClient_GetStreamLatency(IAudioClient3 *iface,
682 REFERENCE_TIME *latency)
684 ACImpl *This = impl_from_IAudioClient3(iface);
685 struct get_latency_params params;
687 TRACE("(%p)->(%p)\n", This, latency);
689 if(!latency)
690 return E_POINTER;
692 if(!This->stream)
693 return AUDCLNT_E_NOT_INITIALIZED;
695 params.stream = This->stream;
696 params.latency = latency;
697 OSS_CALL(get_latency, &params);
699 return params.result;
702 static HRESULT WINAPI AudioClient_GetCurrentPadding(IAudioClient3 *iface,
703 UINT32 *numpad)
705 ACImpl *This = impl_from_IAudioClient3(iface);
706 struct get_current_padding_params params;
708 TRACE("(%p)->(%p)\n", This, numpad);
710 if(!numpad)
711 return E_POINTER;
713 if(!This->stream)
714 return AUDCLNT_E_NOT_INITIALIZED;
716 params.stream = This->stream;
717 params.padding = numpad;
718 OSS_CALL(get_current_padding, &params);
719 TRACE("padding: %u\n", *numpad);
721 return params.result;
724 static HRESULT WINAPI AudioClient_IsFormatSupported(IAudioClient3 *iface,
725 AUDCLNT_SHAREMODE mode, const WAVEFORMATEX *fmt,
726 WAVEFORMATEX **out)
728 ACImpl *This = impl_from_IAudioClient3(iface);
729 struct is_format_supported_params params;
731 TRACE("(%p)->(%x, %p, %p)\n", This, mode, fmt, out);
732 if(fmt) dump_fmt(fmt);
734 params.device = This->device_name;
735 params.flow = This->dataflow;
736 params.share = mode;
737 params.fmt_in = fmt;
738 params.fmt_out = NULL;
740 if(out){
741 *out = NULL;
742 if(mode == AUDCLNT_SHAREMODE_SHARED)
743 params.fmt_out = CoTaskMemAlloc(sizeof(*params.fmt_out));
745 OSS_CALL(is_format_supported, &params);
747 if(params.result == S_FALSE)
748 *out = &params.fmt_out->Format;
749 else
750 CoTaskMemFree(params.fmt_out);
752 return params.result;
755 static HRESULT WINAPI AudioClient_GetMixFormat(IAudioClient3 *iface,
756 WAVEFORMATEX **pwfx)
758 ACImpl *This = impl_from_IAudioClient3(iface);
759 struct get_mix_format_params params;
761 TRACE("(%p)->(%p)\n", This, pwfx);
763 if(!pwfx)
764 return E_POINTER;
765 *pwfx = NULL;
767 params.device = This->device_name;
768 params.flow = This->dataflow;
769 params.fmt = CoTaskMemAlloc(sizeof(WAVEFORMATEXTENSIBLE));
770 if(!params.fmt)
771 return E_OUTOFMEMORY;
773 OSS_CALL(get_mix_format, &params);
775 if(SUCCEEDED(params.result)){
776 *pwfx = &params.fmt->Format;
777 dump_fmt(*pwfx);
778 } else
779 CoTaskMemFree(params.fmt);
781 return params.result;
784 static HRESULT WINAPI AudioClient_GetDevicePeriod(IAudioClient3 *iface,
785 REFERENCE_TIME *defperiod, REFERENCE_TIME *minperiod)
787 ACImpl *This = impl_from_IAudioClient3(iface);
789 TRACE("(%p)->(%p, %p)\n", This, defperiod, minperiod);
791 if(!defperiod && !minperiod)
792 return E_POINTER;
794 if(defperiod)
795 *defperiod = DefaultPeriod;
796 if(minperiod)
797 *minperiod = MinimumPeriod;
799 return S_OK;
802 static HRESULT WINAPI AudioClient_Start(IAudioClient3 *iface)
804 ACImpl *This = impl_from_IAudioClient3(iface);
805 struct start_params params;
807 TRACE("(%p)\n", This);
809 sessions_lock();
811 if(!This->stream){
812 sessions_unlock();
813 return AUDCLNT_E_NOT_INITIALIZED;
816 params.stream = This->stream;
817 OSS_CALL(start, &params);
819 if(SUCCEEDED(params.result) && !This->timer_thread){
820 This->timer_thread = CreateThread(NULL, 0, timer_thread, This, 0, NULL);
821 SetThreadPriority(This->timer_thread, THREAD_PRIORITY_TIME_CRITICAL);
824 sessions_unlock();
826 return params.result;
829 static HRESULT WINAPI AudioClient_Stop(IAudioClient3 *iface)
831 ACImpl *This = impl_from_IAudioClient3(iface);
832 struct stop_params params;
834 TRACE("(%p)\n", This);
836 if(!This->stream)
837 return AUDCLNT_E_NOT_INITIALIZED;
839 params.stream = This->stream;
840 OSS_CALL(stop, &params);
842 return params.result;
845 static HRESULT WINAPI AudioClient_Reset(IAudioClient3 *iface)
847 ACImpl *This = impl_from_IAudioClient3(iface);
848 struct reset_params params;
850 TRACE("(%p)\n", This);
852 if(!This->stream)
853 return AUDCLNT_E_NOT_INITIALIZED;
855 params.stream = This->stream;
856 OSS_CALL(reset, &params);
858 return params.result;
861 static HRESULT WINAPI AudioClient_SetEventHandle(IAudioClient3 *iface,
862 HANDLE event)
864 ACImpl *This = impl_from_IAudioClient3(iface);
865 struct set_event_handle_params params;
867 TRACE("(%p)->(%p)\n", This, event);
869 if(!event)
870 return E_INVALIDARG;
872 if(!This->stream)
873 return AUDCLNT_E_NOT_INITIALIZED;
875 params.stream = This->stream;
876 params.event = event;
877 OSS_CALL(set_event_handle, &params);
879 return params.result;
882 static HRESULT WINAPI AudioClient_GetService(IAudioClient3 *iface, REFIID riid,
883 void **ppv)
885 ACImpl *This = impl_from_IAudioClient3(iface);
887 TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(riid), ppv);
889 if(!ppv)
890 return E_POINTER;
891 *ppv = NULL;
893 sessions_lock();
895 if(!This->stream){
896 sessions_unlock();
897 return AUDCLNT_E_NOT_INITIALIZED;
900 if(IsEqualIID(riid, &IID_IAudioRenderClient)){
901 if(This->dataflow != eRender){
902 sessions_unlock();
903 return AUDCLNT_E_WRONG_ENDPOINT_TYPE;
905 IAudioRenderClient_AddRef(&This->IAudioRenderClient_iface);
906 *ppv = &This->IAudioRenderClient_iface;
907 }else if(IsEqualIID(riid, &IID_IAudioCaptureClient)){
908 if(This->dataflow != eCapture){
909 sessions_unlock();
910 return AUDCLNT_E_WRONG_ENDPOINT_TYPE;
912 IAudioCaptureClient_AddRef(&This->IAudioCaptureClient_iface);
913 *ppv = &This->IAudioCaptureClient_iface;
914 }else if(IsEqualIID(riid, &IID_IAudioClock)){
915 IAudioClock_AddRef(&This->IAudioClock_iface);
916 *ppv = &This->IAudioClock_iface;
917 }else if(IsEqualIID(riid, &IID_IAudioStreamVolume)){
918 IAudioStreamVolume_AddRef(&This->IAudioStreamVolume_iface);
919 *ppv = &This->IAudioStreamVolume_iface;
920 }else if(IsEqualIID(riid, &IID_IAudioSessionControl)){
921 if(!This->session_wrapper){
922 This->session_wrapper = AudioSessionWrapper_Create(This);
923 if(!This->session_wrapper){
924 sessions_unlock();
925 return E_OUTOFMEMORY;
927 }else
928 IAudioSessionControl2_AddRef(&This->session_wrapper->IAudioSessionControl2_iface);
930 *ppv = &This->session_wrapper->IAudioSessionControl2_iface;
931 }else if(IsEqualIID(riid, &IID_IChannelAudioVolume)){
932 if(!This->session_wrapper){
933 This->session_wrapper = AudioSessionWrapper_Create(This);
934 if(!This->session_wrapper){
935 sessions_unlock();
936 return E_OUTOFMEMORY;
938 }else
939 IChannelAudioVolume_AddRef(&This->session_wrapper->IChannelAudioVolume_iface);
941 *ppv = &This->session_wrapper->IChannelAudioVolume_iface;
942 }else if(IsEqualIID(riid, &IID_ISimpleAudioVolume)){
943 if(!This->session_wrapper){
944 This->session_wrapper = AudioSessionWrapper_Create(This);
945 if(!This->session_wrapper){
946 sessions_unlock();
947 return E_OUTOFMEMORY;
949 }else
950 ISimpleAudioVolume_AddRef(&This->session_wrapper->ISimpleAudioVolume_iface);
952 *ppv = &This->session_wrapper->ISimpleAudioVolume_iface;
955 if(*ppv){
956 sessions_unlock();
957 return S_OK;
960 sessions_unlock();
962 FIXME("stub %s\n", debugstr_guid(riid));
963 return E_NOINTERFACE;
966 static HRESULT WINAPI AudioClient_IsOffloadCapable(IAudioClient3 *iface,
967 AUDIO_STREAM_CATEGORY category, BOOL *offload_capable)
969 ACImpl *This = impl_from_IAudioClient3(iface);
971 TRACE("(%p)->(0x%x, %p)\n", This, category, offload_capable);
973 if(!offload_capable)
974 return E_INVALIDARG;
976 *offload_capable = FALSE;
978 return S_OK;
981 static HRESULT WINAPI AudioClient_SetClientProperties(IAudioClient3 *iface,
982 const AudioClientProperties *prop)
984 ACImpl *This = impl_from_IAudioClient3(iface);
985 const Win8AudioClientProperties *legacy_prop = (const Win8AudioClientProperties *)prop;
987 TRACE("(%p)->(%p)\n", This, prop);
989 if(!legacy_prop)
990 return E_POINTER;
992 if(legacy_prop->cbSize == sizeof(AudioClientProperties)){
993 TRACE("{ bIsOffload: %u, eCategory: 0x%x, Options: 0x%x }\n",
994 legacy_prop->bIsOffload,
995 legacy_prop->eCategory,
996 prop->Options);
997 }else if(legacy_prop->cbSize == sizeof(Win8AudioClientProperties)){
998 TRACE("{ bIsOffload: %u, eCategory: 0x%x }\n",
999 legacy_prop->bIsOffload,
1000 legacy_prop->eCategory);
1001 }else{
1002 WARN("Unsupported Size = %d\n", legacy_prop->cbSize);
1003 return E_INVALIDARG;
1007 if(legacy_prop->bIsOffload)
1008 return AUDCLNT_E_ENDPOINT_OFFLOAD_NOT_CAPABLE;
1010 return S_OK;
1013 static HRESULT WINAPI AudioClient_GetBufferSizeLimits(IAudioClient3 *iface,
1014 const WAVEFORMATEX *format, BOOL event_driven, REFERENCE_TIME *min_duration,
1015 REFERENCE_TIME *max_duration)
1017 ACImpl *This = impl_from_IAudioClient3(iface);
1019 FIXME("(%p)->(%p, %u, %p, %p)\n", This, format, event_driven, min_duration, max_duration);
1021 return E_NOTIMPL;
1024 static HRESULT WINAPI AudioClient_GetSharedModeEnginePeriod(IAudioClient3 *iface,
1025 const WAVEFORMATEX *format, UINT32 *default_period_frames, UINT32 *unit_period_frames,
1026 UINT32 *min_period_frames, UINT32 *max_period_frames)
1028 ACImpl *This = impl_from_IAudioClient3(iface);
1030 FIXME("(%p)->(%p, %p, %p, %p, %p)\n", This, format, default_period_frames, unit_period_frames,
1031 min_period_frames, max_period_frames);
1033 return E_NOTIMPL;
1036 static HRESULT WINAPI AudioClient_GetCurrentSharedModeEnginePeriod(IAudioClient3 *iface,
1037 WAVEFORMATEX **cur_format, UINT32 *cur_period_frames)
1039 ACImpl *This = impl_from_IAudioClient3(iface);
1041 FIXME("(%p)->(%p, %p)\n", This, cur_format, cur_period_frames);
1043 return E_NOTIMPL;
1046 static HRESULT WINAPI AudioClient_InitializeSharedAudioStream(IAudioClient3 *iface,
1047 DWORD flags, UINT32 period_frames, const WAVEFORMATEX *format,
1048 const GUID *session_guid)
1050 ACImpl *This = impl_from_IAudioClient3(iface);
1052 FIXME("(%p)->(0x%lx, %u, %p, %s)\n", This, flags, period_frames, format, debugstr_guid(session_guid));
1054 return E_NOTIMPL;
1057 static const IAudioClient3Vtbl AudioClient3_Vtbl =
1059 AudioClient_QueryInterface,
1060 AudioClient_AddRef,
1061 AudioClient_Release,
1062 AudioClient_Initialize,
1063 AudioClient_GetBufferSize,
1064 AudioClient_GetStreamLatency,
1065 AudioClient_GetCurrentPadding,
1066 AudioClient_IsFormatSupported,
1067 AudioClient_GetMixFormat,
1068 AudioClient_GetDevicePeriod,
1069 AudioClient_Start,
1070 AudioClient_Stop,
1071 AudioClient_Reset,
1072 AudioClient_SetEventHandle,
1073 AudioClient_GetService,
1074 AudioClient_IsOffloadCapable,
1075 AudioClient_SetClientProperties,
1076 AudioClient_GetBufferSizeLimits,
1077 AudioClient_GetSharedModeEnginePeriod,
1078 AudioClient_GetCurrentSharedModeEnginePeriod,
1079 AudioClient_InitializeSharedAudioStream,
1082 static AudioSessionWrapper *AudioSessionWrapper_Create(ACImpl *client)
1084 AudioSessionWrapper *ret;
1086 ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
1087 sizeof(AudioSessionWrapper));
1088 if(!ret)
1089 return NULL;
1091 ret->IAudioSessionControl2_iface.lpVtbl = &AudioSessionControl2_Vtbl;
1092 ret->ISimpleAudioVolume_iface.lpVtbl = &SimpleAudioVolume_Vtbl;
1093 ret->IChannelAudioVolume_iface.lpVtbl = &ChannelAudioVolume_Vtbl;
1095 ret->ref = 1;
1097 ret->client = client;
1098 if(client){
1099 ret->session = client->session;
1100 IAudioClient3_AddRef(&client->IAudioClient3_iface);
1103 return ret;
1106 HRESULT WINAPI AUDDRV_GetAudioSessionWrapper(const GUID *guid, IMMDevice *device,
1107 AudioSessionWrapper **out)
1109 AudioSession *session;
1111 HRESULT hr = get_audio_session(guid, device, 0, &session);
1112 if(FAILED(hr))
1113 return hr;
1115 *out = AudioSessionWrapper_Create(NULL);
1116 if(!*out)
1117 return E_OUTOFMEMORY;
1119 (*out)->session = session;
1121 return S_OK;