winealsa: Use mmdevapi's AudioClock.
[wine.git] / dlls / winealsa.drv / mmdevdrv.c
blob2a3ecd68b7a57787ea90fb7a3b8f72d4de540c73
1 /*
2 * Copyright 2010 Maarten Lankhorst for CodeWeavers
3 * Copyright 2011 Andrew Eikum for CodeWeavers
4 * Copyright 2022 Huw Davies
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 #define COBJMACROS
23 #include <stdarg.h>
24 #include <wchar.h>
26 #include "windef.h"
27 #include "winbase.h"
28 #include "winnls.h"
29 #include "winreg.h"
30 #include "winternl.h"
31 #include "propsys.h"
32 #include "propkey.h"
33 #include "initguid.h"
34 #include "ole2.h"
35 #include "mmdeviceapi.h"
36 #include "devpkey.h"
37 #include "mmsystem.h"
38 #include "dsound.h"
40 #include "endpointvolume.h"
41 #include "audioclient.h"
42 #include "audiopolicy.h"
44 #include "wine/debug.h"
45 #include "wine/list.h"
46 #include "wine/unixlib.h"
48 #include "unixlib.h"
50 #include "../mmdevapi/mmdevdrv.h"
52 WINE_DEFAULT_DEBUG_CHANNEL(alsa);
54 #define NULL_PTR_ERR MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, RPC_X_NULL_REF_POINTER)
56 static const REFERENCE_TIME DefaultPeriod = 100000;
57 static const REFERENCE_TIME MinimumPeriod = 50000;
59 static CRITICAL_SECTION g_sessions_lock;
60 static CRITICAL_SECTION_DEBUG g_sessions_lock_debug =
62 0, 0, &g_sessions_lock,
63 { &g_sessions_lock_debug.ProcessLocksList, &g_sessions_lock_debug.ProcessLocksList },
64 0, 0, { (DWORD_PTR)(__FILE__ ": g_sessions_lock") }
66 static CRITICAL_SECTION g_sessions_lock = { &g_sessions_lock_debug, -1, 0, 0, 0, 0 };
67 static struct list g_sessions = LIST_INIT(g_sessions);
69 static WCHAR drv_key_devicesW[256];
70 static const WCHAR guidW[] = {'g','u','i','d',0};
72 static const IAudioClient3Vtbl AudioClient3_Vtbl;
73 static const IAudioRenderClientVtbl AudioRenderClient_Vtbl;
74 static const IAudioCaptureClientVtbl AudioCaptureClient_Vtbl;
75 extern const IAudioSessionControl2Vtbl AudioSessionControl2_Vtbl;
76 extern const ISimpleAudioVolumeVtbl SimpleAudioVolume_Vtbl;
77 extern const IAudioClockVtbl AudioClock_Vtbl;
78 extern const IAudioClock2Vtbl AudioClock2_Vtbl;
79 extern const IAudioStreamVolumeVtbl AudioStreamVolume_Vtbl;
80 extern const IChannelAudioVolumeVtbl ChannelAudioVolume_Vtbl;
82 static AudioSessionWrapper *AudioSessionWrapper_Create(ACImpl *client);
84 void DECLSPEC_HIDDEN sessions_lock(void)
86 EnterCriticalSection(&g_sessions_lock);
89 void DECLSPEC_HIDDEN sessions_unlock(void)
91 LeaveCriticalSection(&g_sessions_lock);
94 static inline ACImpl *impl_from_IAudioClient3(IAudioClient3 *iface)
96 return CONTAINING_RECORD(iface, ACImpl, IAudioClient3_iface);
99 static inline ACImpl *impl_from_IAudioRenderClient(IAudioRenderClient *iface)
101 return CONTAINING_RECORD(iface, ACImpl, IAudioRenderClient_iface);
104 static inline ACImpl *impl_from_IAudioCaptureClient(IAudioCaptureClient *iface)
106 return CONTAINING_RECORD(iface, ACImpl, IAudioCaptureClient_iface);
109 BOOL WINAPI DllMain(HINSTANCE dll, DWORD reason, void *reserved)
111 switch (reason)
113 case DLL_PROCESS_ATTACH:
115 WCHAR buf[MAX_PATH];
116 WCHAR *filename;
118 if(__wine_init_unix_call()) return FALSE;
120 GetModuleFileNameW(dll, buf, ARRAY_SIZE(buf));
122 filename = wcsrchr(buf, '\\');
123 filename = filename ? filename + 1 : buf;
125 swprintf(drv_key_devicesW, ARRAY_SIZE(drv_key_devicesW),
126 L"Software\\Wine\\Drivers\\%s\\devices", filename);
128 break;
130 case DLL_PROCESS_DETACH:
131 if (reserved) break;
132 DeleteCriticalSection(&g_sessions_lock);
133 break;
135 return TRUE;
138 static HRESULT alsa_stream_release(stream_handle stream, HANDLE timer_thread)
140 struct release_stream_params params;
142 params.stream = stream;
143 params.timer_thread = timer_thread;
145 ALSA_CALL(release_stream, &params);
147 return params.result;
150 static DWORD WINAPI alsa_timer_thread(void *user)
152 struct timer_loop_params params;
153 ACImpl *This = user;
155 SetThreadDescription(GetCurrentThread(), L"winealsa_timer");
157 params.stream = This->stream;
159 ALSA_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;
245 ALSA_CALL(set_volumes, &params);
248 HRESULT WINAPI AUDDRV_GetEndpointIDs(EDataFlow flow, WCHAR ***ids_out, GUID **guids_out,
249 UINT *num, UINT *def_index)
251 struct get_endpoint_ids_params params;
252 unsigned int i;
253 GUID *guids = NULL;
254 WCHAR **ids = NULL;
256 TRACE("%d %p %p %p %p\n", flow, ids, guids, num, def_index);
258 params.flow = flow;
259 params.size = 1000;
260 params.endpoints = NULL;
262 HeapFree(GetProcessHeap(), 0, params.endpoints);
263 params.endpoints = HeapAlloc(GetProcessHeap(), 0, params.size);
264 ALSA_CALL(get_endpoint_ids, &params);
265 }while(params.result == HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER));
267 if(FAILED(params.result)) goto end;
269 ids = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, params.num * sizeof(*ids));
270 guids = HeapAlloc(GetProcessHeap(), 0, params.num * sizeof(*guids));
271 if(!ids || !guids){
272 params.result = E_OUTOFMEMORY;
273 goto end;
276 for(i = 0; i < params.num; i++){
277 WCHAR *name = (WCHAR *)((char *)params.endpoints + params.endpoints[i].name);
278 char *device = (char *)params.endpoints + params.endpoints[i].device;
279 unsigned int size = (wcslen(name) + 1) * sizeof(WCHAR);
281 ids[i] = HeapAlloc(GetProcessHeap(), 0, size);
282 if(!ids[i]){
283 params.result = E_OUTOFMEMORY;
284 goto end;
286 memcpy(ids[i], name, size);
287 get_device_guid(flow, device, guids + i);
289 *def_index = params.default_idx;
291 end:
292 HeapFree(GetProcessHeap(), 0, params.endpoints);
293 if(FAILED(params.result)){
294 HeapFree(GetProcessHeap(), 0, guids);
295 if(ids){
296 for(i = 0; i < params.num; i++)
297 HeapFree(GetProcessHeap(), 0, ids[i]);
298 HeapFree(GetProcessHeap(), 0, ids);
300 }else{
301 *ids_out = ids;
302 *guids_out = guids;
303 *num = params.num;
306 return params.result;
309 static BOOL get_alsa_name_by_guid(GUID *guid, char *name, DWORD name_size, EDataFlow *flow)
311 HKEY devices_key;
312 UINT i = 0;
313 WCHAR key_name[256];
314 DWORD key_name_size;
316 if(RegOpenKeyExW(HKEY_CURRENT_USER, drv_key_devicesW, 0, KEY_READ, &devices_key) != ERROR_SUCCESS){
317 ERR("No devices found in registry?\n");
318 return FALSE;
321 while(1){
322 HKEY key;
323 DWORD size, type;
324 GUID reg_guid;
326 key_name_size = ARRAY_SIZE(key_name);
327 if(RegEnumKeyExW(devices_key, i++, key_name, &key_name_size, NULL,
328 NULL, NULL, NULL) != ERROR_SUCCESS)
329 break;
331 if(RegOpenKeyExW(devices_key, key_name, 0, KEY_READ, &key) != ERROR_SUCCESS){
332 WARN("Couldn't open key: %s\n", wine_dbgstr_w(key_name));
333 continue;
336 size = sizeof(reg_guid);
337 if(RegQueryValueExW(key, guidW, 0, &type,
338 (BYTE*)&reg_guid, &size) == ERROR_SUCCESS){
339 if(IsEqualGUID(&reg_guid, guid)){
340 RegCloseKey(key);
341 RegCloseKey(devices_key);
343 TRACE("Found matching device key: %s\n", wine_dbgstr_w(key_name));
345 if(key_name[0] == '0')
346 *flow = eRender;
347 else if(key_name[0] == '1')
348 *flow = eCapture;
349 else{
350 ERR("Unknown device type: %c\n", key_name[0]);
351 return FALSE;
354 WideCharToMultiByte(CP_UNIXCP, 0, key_name + 2, -1, name, name_size, NULL, NULL);
356 return TRUE;
360 RegCloseKey(key);
363 RegCloseKey(devices_key);
365 WARN("No matching device in registry for GUID %s\n", debugstr_guid(guid));
367 return FALSE;
370 HRESULT WINAPI AUDDRV_GetAudioEndpoint(GUID *guid, IMMDevice *dev, IAudioClient **out)
372 ACImpl *This;
373 char alsa_name[256];
374 EDataFlow dataflow;
375 HRESULT hr;
376 int len;
378 TRACE("%s %p %p\n", debugstr_guid(guid), dev, out);
380 if(!get_alsa_name_by_guid(guid, alsa_name, sizeof(alsa_name), &dataflow))
381 return AUDCLNT_E_DEVICE_INVALIDATED;
383 if(dataflow != eRender && dataflow != eCapture)
384 return E_UNEXPECTED;
386 len = strlen(alsa_name);
387 This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, offsetof(ACImpl, device_name[len + 1]));
388 if(!This)
389 return E_OUTOFMEMORY;
391 This->IAudioClient3_iface.lpVtbl = &AudioClient3_Vtbl;
392 This->IAudioRenderClient_iface.lpVtbl = &AudioRenderClient_Vtbl;
393 This->IAudioCaptureClient_iface.lpVtbl = &AudioCaptureClient_Vtbl;
394 This->IAudioClock_iface.lpVtbl = &AudioClock_Vtbl;
395 This->IAudioClock2_iface.lpVtbl = &AudioClock2_Vtbl;
396 This->IAudioStreamVolume_iface.lpVtbl = &AudioStreamVolume_Vtbl;
398 hr = CoCreateFreeThreadedMarshaler((IUnknown *)&This->IAudioClient3_iface, &This->marshal);
399 if (FAILED(hr)) {
400 HeapFree(GetProcessHeap(), 0, This);
401 return hr;
404 This->dataflow = dataflow;
405 memcpy(This->device_name, alsa_name, len + 1);
407 This->parent = dev;
408 IMMDevice_AddRef(This->parent);
410 *out = (IAudioClient *)&This->IAudioClient3_iface;
411 IAudioClient3_AddRef(&This->IAudioClient3_iface);
413 return S_OK;
416 static HRESULT WINAPI AudioClient_QueryInterface(IAudioClient3 *iface,
417 REFIID riid, void **ppv)
419 ACImpl *This = impl_from_IAudioClient3(iface);
420 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
422 if(!ppv)
423 return E_POINTER;
424 *ppv = NULL;
425 if(IsEqualIID(riid, &IID_IUnknown) ||
426 IsEqualIID(riid, &IID_IAudioClient) ||
427 IsEqualIID(riid, &IID_IAudioClient2) ||
428 IsEqualIID(riid, &IID_IAudioClient3))
429 *ppv = iface;
430 else if(IsEqualIID(riid, &IID_IMarshal))
431 return IUnknown_QueryInterface(This->marshal, riid, ppv);
433 if(*ppv){
434 IUnknown_AddRef((IUnknown*)*ppv);
435 return S_OK;
437 WARN("Unknown interface %s\n", debugstr_guid(riid));
438 return E_NOINTERFACE;
441 static ULONG WINAPI AudioClient_AddRef(IAudioClient3 *iface)
443 ACImpl *This = impl_from_IAudioClient3(iface);
444 ULONG ref;
445 ref = InterlockedIncrement(&This->ref);
446 TRACE("(%p) Refcount now %lu\n", This, ref);
447 return ref;
450 static ULONG WINAPI AudioClient_Release(IAudioClient3 *iface)
452 ACImpl *This = impl_from_IAudioClient3(iface);
453 ULONG ref;
455 ref = InterlockedDecrement(&This->ref);
456 TRACE("(%p) Refcount now %lu\n", This, ref);
457 if(!ref){
458 IAudioClient3_Stop(iface);
459 IMMDevice_Release(This->parent);
460 IUnknown_Release(This->marshal);
461 if(This->session){
462 sessions_lock();
463 list_remove(&This->entry);
464 sessions_unlock();
466 HeapFree(GetProcessHeap(), 0, This->vols);
467 if (This->stream)
468 alsa_stream_release(This->stream, This->timer_thread);
469 HeapFree(GetProcessHeap(), 0, This);
471 return ref;
474 static void dump_fmt(const WAVEFORMATEX *fmt)
476 TRACE("wFormatTag: 0x%x (", fmt->wFormatTag);
477 switch(fmt->wFormatTag){
478 case WAVE_FORMAT_PCM:
479 TRACE("WAVE_FORMAT_PCM");
480 break;
481 case WAVE_FORMAT_IEEE_FLOAT:
482 TRACE("WAVE_FORMAT_IEEE_FLOAT");
483 break;
484 case WAVE_FORMAT_EXTENSIBLE:
485 TRACE("WAVE_FORMAT_EXTENSIBLE");
486 break;
487 default:
488 TRACE("Unknown");
489 break;
491 TRACE(")\n");
493 TRACE("nChannels: %u\n", fmt->nChannels);
494 TRACE("nSamplesPerSec: %lu\n", fmt->nSamplesPerSec);
495 TRACE("nAvgBytesPerSec: %lu\n", fmt->nAvgBytesPerSec);
496 TRACE("nBlockAlign: %u\n", fmt->nBlockAlign);
497 TRACE("wBitsPerSample: %u\n", fmt->wBitsPerSample);
498 TRACE("cbSize: %u\n", fmt->cbSize);
500 if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE){
501 WAVEFORMATEXTENSIBLE *fmtex = (void*)fmt;
502 TRACE("dwChannelMask: %08lx\n", fmtex->dwChannelMask);
503 TRACE("Samples: %04x\n", fmtex->Samples.wReserved);
504 TRACE("SubFormat: %s\n", wine_dbgstr_guid(&fmtex->SubFormat));
508 static void session_init_vols(AudioSession *session, UINT channels)
510 if(session->channel_count < channels){
511 UINT i;
513 if(session->channel_vols)
514 session->channel_vols = HeapReAlloc(GetProcessHeap(), 0,
515 session->channel_vols, sizeof(float) * channels);
516 else
517 session->channel_vols = HeapAlloc(GetProcessHeap(), 0,
518 sizeof(float) * channels);
519 if(!session->channel_vols)
520 return;
522 for(i = session->channel_count; i < channels; ++i)
523 session->channel_vols[i] = 1.f;
525 session->channel_count = channels;
529 static AudioSession *create_session(const GUID *guid, IMMDevice *device,
530 UINT num_channels)
532 AudioSession *ret;
534 ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(AudioSession));
535 if(!ret)
536 return NULL;
538 memcpy(&ret->guid, guid, sizeof(GUID));
540 ret->device = device;
542 list_init(&ret->clients);
544 list_add_head(&g_sessions, &ret->entry);
546 session_init_vols(ret, num_channels);
548 ret->master_vol = 1.f;
550 return ret;
553 /* if channels == 0, then this will return or create a session with
554 * matching dataflow and GUID. otherwise, channels must also match */
555 static HRESULT get_audio_session(const GUID *sessionguid,
556 IMMDevice *device, UINT channels, AudioSession **out)
558 AudioSession *session;
560 if(!sessionguid || IsEqualGUID(sessionguid, &GUID_NULL)){
561 *out = create_session(&GUID_NULL, device, channels);
562 if(!*out)
563 return E_OUTOFMEMORY;
565 return S_OK;
568 *out = NULL;
569 LIST_FOR_EACH_ENTRY(session, &g_sessions, AudioSession, entry){
570 if(session->device == device &&
571 IsEqualGUID(sessionguid, &session->guid)){
572 session_init_vols(session, channels);
573 *out = session;
574 break;
578 if(!*out){
579 *out = create_session(sessionguid, device, channels);
580 if(!*out)
581 return E_OUTOFMEMORY;
584 return S_OK;
587 static HRESULT WINAPI AudioClient_Initialize(IAudioClient3 *iface,
588 AUDCLNT_SHAREMODE mode, DWORD flags, REFERENCE_TIME duration,
589 REFERENCE_TIME period, const WAVEFORMATEX *fmt,
590 const GUID *sessionguid)
592 ACImpl *This = impl_from_IAudioClient3(iface);
593 struct create_stream_params params;
594 stream_handle stream;
595 unsigned int i;
597 TRACE("(%p)->(%x, %lx, %s, %s, %p, %s)\n", This, mode, flags,
598 wine_dbgstr_longlong(duration), wine_dbgstr_longlong(period), fmt, debugstr_guid(sessionguid));
600 if(!fmt)
601 return E_POINTER;
603 if(mode != AUDCLNT_SHAREMODE_SHARED && mode != AUDCLNT_SHAREMODE_EXCLUSIVE)
604 return E_INVALIDARG;
606 if(flags & ~(AUDCLNT_STREAMFLAGS_CROSSPROCESS |
607 AUDCLNT_STREAMFLAGS_LOOPBACK |
608 AUDCLNT_STREAMFLAGS_EVENTCALLBACK |
609 AUDCLNT_STREAMFLAGS_NOPERSIST |
610 AUDCLNT_STREAMFLAGS_RATEADJUST |
611 AUDCLNT_SESSIONFLAGS_EXPIREWHENUNOWNED |
612 AUDCLNT_SESSIONFLAGS_DISPLAY_HIDE |
613 AUDCLNT_SESSIONFLAGS_DISPLAY_HIDEWHENEXPIRED |
614 AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY |
615 AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM)){
616 FIXME("Unknown flags: %08lx\n", flags);
617 return E_INVALIDARG;
620 if(mode == AUDCLNT_SHAREMODE_SHARED){
621 period = DefaultPeriod;
622 if( duration < 3 * period)
623 duration = 3 * period;
624 }else{
625 if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE){
626 if(((WAVEFORMATEXTENSIBLE*)fmt)->dwChannelMask == 0 ||
627 ((WAVEFORMATEXTENSIBLE*)fmt)->dwChannelMask & SPEAKER_RESERVED)
628 return AUDCLNT_E_UNSUPPORTED_FORMAT;
631 if(!period)
632 period = DefaultPeriod; /* not minimum */
633 if(period < MinimumPeriod || period > 5000000)
634 return AUDCLNT_E_INVALID_DEVICE_PERIOD;
635 if(duration > 20000000) /* the smaller the period, the lower this limit */
636 return AUDCLNT_E_BUFFER_SIZE_ERROR;
637 if(flags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK){
638 if(duration != period)
639 return AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL;
640 FIXME("EXCLUSIVE mode with EVENTCALLBACK\n");
641 return AUDCLNT_E_DEVICE_IN_USE;
642 }else{
643 if( duration < 8 * period)
644 duration = 8 * period; /* may grow above 2s */
648 sessions_lock();
650 if(This->stream){
651 sessions_unlock();
652 return AUDCLNT_E_ALREADY_INITIALIZED;
655 dump_fmt(fmt);
657 params.name = NULL;
658 params.device = This->device_name;
659 params.flow = This->dataflow;
660 params.share = mode;
661 params.flags = flags;
662 params.duration = duration;
663 params.period = period;
664 params.fmt = fmt;
665 params.channel_count = NULL;
666 params.stream = &stream;
668 ALSA_CALL(create_stream, &params);
669 if(FAILED(params.result)){
670 sessions_unlock();
671 return params.result;
674 This->channel_count = fmt->nChannels;
675 This->vols = HeapAlloc(GetProcessHeap(), 0, This->channel_count * sizeof(float));
676 if(!This->vols){
677 params.result = E_OUTOFMEMORY;
678 goto exit;
680 for(i = 0; i < This->channel_count; ++i)
681 This->vols[i] = 1.f;
683 params.result = get_audio_session(sessionguid, This->parent, This->channel_count,
684 &This->session);
685 if(FAILED(params.result))
686 goto exit;
688 list_add_tail(&This->session->clients, &This->entry);
690 exit:
691 if(FAILED(params.result)){
692 alsa_stream_release(stream, NULL);
693 HeapFree(GetProcessHeap(), 0, This->vols);
694 This->vols = NULL;
695 }else{
696 This->stream = stream;
697 set_stream_volumes(This);
700 sessions_unlock();
702 return params.result;
705 static HRESULT WINAPI AudioClient_GetBufferSize(IAudioClient3 *iface,
706 UINT32 *out)
708 ACImpl *This = impl_from_IAudioClient3(iface);
709 struct get_buffer_size_params params;
711 TRACE("(%p)->(%p)\n", This, out);
713 if(!out)
714 return E_POINTER;
716 if(!This->stream)
717 return AUDCLNT_E_NOT_INITIALIZED;
719 params.stream = This->stream;
720 params.frames = out;
722 ALSA_CALL(get_buffer_size, &params);
724 return params.result;
727 static HRESULT WINAPI AudioClient_GetStreamLatency(IAudioClient3 *iface,
728 REFERENCE_TIME *latency)
730 ACImpl *This = impl_from_IAudioClient3(iface);
731 struct get_latency_params params;
733 TRACE("(%p)->(%p)\n", This, latency);
735 if(!latency)
736 return E_POINTER;
738 if(!This->stream)
739 return AUDCLNT_E_NOT_INITIALIZED;
741 params.stream = This->stream;
742 params.latency = latency;
744 ALSA_CALL(get_latency, &params);
746 return params.result;
749 static HRESULT WINAPI AudioClient_GetCurrentPadding(IAudioClient3 *iface,
750 UINT32 *out)
752 ACImpl *This = impl_from_IAudioClient3(iface);
753 struct get_current_padding_params params;
755 TRACE("(%p)->(%p)\n", This, out);
757 if(!out)
758 return E_POINTER;
760 if(!This->stream)
761 return AUDCLNT_E_NOT_INITIALIZED;
763 params.stream = This->stream;
764 params.padding = out;
766 ALSA_CALL(get_current_padding, &params);
768 TRACE("pad: %u\n", *out);
770 return params.result;
773 static HRESULT WINAPI AudioClient_IsFormatSupported(IAudioClient3 *iface,
774 AUDCLNT_SHAREMODE mode, const WAVEFORMATEX *fmt,
775 WAVEFORMATEX **out)
777 ACImpl *This = impl_from_IAudioClient3(iface);
778 struct is_format_supported_params params;
780 TRACE("(%p)->(%x, %p, %p)\n", This, mode, fmt, out);
781 if(fmt) dump_fmt(fmt);
783 params.device = This->device_name;
784 params.flow = This->dataflow;
785 params.share = mode;
786 params.fmt_in = fmt;
787 params.fmt_out = NULL;
789 if(out){
790 *out = NULL;
791 if(mode == AUDCLNT_SHAREMODE_SHARED)
792 params.fmt_out = CoTaskMemAlloc(sizeof(*params.fmt_out));
794 ALSA_CALL(is_format_supported, &params);
796 if(params.result == S_FALSE)
797 *out = &params.fmt_out->Format;
798 else
799 CoTaskMemFree(params.fmt_out);
801 return params.result;
804 static HRESULT WINAPI AudioClient_GetMixFormat(IAudioClient3 *iface,
805 WAVEFORMATEX **pwfx)
807 ACImpl *This = impl_from_IAudioClient3(iface);
808 struct get_mix_format_params params;
810 TRACE("(%p)->(%p)\n", This, pwfx);
812 if(!pwfx)
813 return E_POINTER;
814 *pwfx = NULL;
816 params.device = This->device_name;
817 params.flow = This->dataflow;
818 params.fmt = CoTaskMemAlloc(sizeof(WAVEFORMATEXTENSIBLE));
819 if(!params.fmt)
820 return E_OUTOFMEMORY;
822 ALSA_CALL(get_mix_format, &params);
824 if(SUCCEEDED(params.result)){
825 *pwfx = &params.fmt->Format;
826 dump_fmt(*pwfx);
827 } else
828 CoTaskMemFree(params.fmt);
830 return params.result;
833 static HRESULT WINAPI AudioClient_GetDevicePeriod(IAudioClient3 *iface,
834 REFERENCE_TIME *defperiod, REFERENCE_TIME *minperiod)
836 ACImpl *This = impl_from_IAudioClient3(iface);
838 TRACE("(%p)->(%p, %p)\n", This, defperiod, minperiod);
840 if(!defperiod && !minperiod)
841 return E_POINTER;
843 if(defperiod)
844 *defperiod = DefaultPeriod;
845 if(minperiod)
846 *minperiod = DefaultPeriod;
848 return S_OK;
851 static HRESULT WINAPI AudioClient_Start(IAudioClient3 *iface)
853 ACImpl *This = impl_from_IAudioClient3(iface);
854 struct start_params params;
856 TRACE("(%p)\n", This);
858 sessions_lock();
860 if(!This->stream){
861 sessions_unlock();
862 return AUDCLNT_E_NOT_INITIALIZED;
865 params.stream = This->stream;
867 ALSA_CALL(start, &params);
869 if(SUCCEEDED(params.result) && !This->timer_thread){
870 This->timer_thread = CreateThread(NULL, 0, alsa_timer_thread, This, 0, NULL);
871 SetThreadPriority(This->timer_thread, THREAD_PRIORITY_TIME_CRITICAL);
874 sessions_unlock();
876 return params.result;
879 static HRESULT WINAPI AudioClient_Stop(IAudioClient3 *iface)
881 ACImpl *This = impl_from_IAudioClient3(iface);
882 struct stop_params params;
884 TRACE("(%p)\n", This);
886 if(!This->stream)
887 return AUDCLNT_E_NOT_INITIALIZED;
889 params.stream = This->stream;
891 ALSA_CALL(stop, &params);
893 return params.result;
896 static HRESULT WINAPI AudioClient_Reset(IAudioClient3 *iface)
898 ACImpl *This = impl_from_IAudioClient3(iface);
899 struct reset_params params;
901 TRACE("(%p)\n", This);
903 if(!This->stream)
904 return AUDCLNT_E_NOT_INITIALIZED;
906 params.stream = This->stream;
908 ALSA_CALL(reset, &params);
910 return params.result;
913 static HRESULT WINAPI AudioClient_SetEventHandle(IAudioClient3 *iface,
914 HANDLE event)
916 ACImpl *This = impl_from_IAudioClient3(iface);
917 struct set_event_handle_params params;
919 TRACE("(%p)->(%p)\n", This, event);
921 if(!event)
922 return E_INVALIDARG;
924 if(!This->stream)
925 return AUDCLNT_E_NOT_INITIALIZED;
927 params.stream = This->stream;
928 params.event = event;
930 ALSA_CALL(set_event_handle, &params);
932 return params.result;
935 static HRESULT WINAPI AudioClient_GetService(IAudioClient3 *iface, REFIID riid,
936 void **ppv)
938 ACImpl *This = impl_from_IAudioClient3(iface);
940 TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(riid), ppv);
942 if(!ppv)
943 return E_POINTER;
944 *ppv = NULL;
946 sessions_lock();
948 if(!This->stream){
949 sessions_unlock();
950 return AUDCLNT_E_NOT_INITIALIZED;
953 if(IsEqualIID(riid, &IID_IAudioRenderClient)){
954 if(This->dataflow != eRender){
955 sessions_unlock();
956 return AUDCLNT_E_WRONG_ENDPOINT_TYPE;
958 IAudioRenderClient_AddRef(&This->IAudioRenderClient_iface);
959 *ppv = &This->IAudioRenderClient_iface;
960 }else if(IsEqualIID(riid, &IID_IAudioCaptureClient)){
961 if(This->dataflow != eCapture){
962 sessions_unlock();
963 return AUDCLNT_E_WRONG_ENDPOINT_TYPE;
965 IAudioCaptureClient_AddRef(&This->IAudioCaptureClient_iface);
966 *ppv = &This->IAudioCaptureClient_iface;
967 }else if(IsEqualIID(riid, &IID_IAudioClock)){
968 IAudioClock_AddRef(&This->IAudioClock_iface);
969 *ppv = &This->IAudioClock_iface;
970 }else if(IsEqualIID(riid, &IID_IAudioStreamVolume)){
971 IAudioStreamVolume_AddRef(&This->IAudioStreamVolume_iface);
972 *ppv = &This->IAudioStreamVolume_iface;
973 }else if(IsEqualIID(riid, &IID_IAudioSessionControl)){
974 if(!This->session_wrapper){
975 This->session_wrapper = AudioSessionWrapper_Create(This);
976 if(!This->session_wrapper){
977 sessions_unlock();
978 return E_OUTOFMEMORY;
980 }else
981 IAudioSessionControl2_AddRef(&This->session_wrapper->IAudioSessionControl2_iface);
983 *ppv = &This->session_wrapper->IAudioSessionControl2_iface;
984 }else if(IsEqualIID(riid, &IID_IChannelAudioVolume)){
985 if(!This->session_wrapper){
986 This->session_wrapper = AudioSessionWrapper_Create(This);
987 if(!This->session_wrapper){
988 sessions_unlock();
989 return E_OUTOFMEMORY;
991 }else
992 IChannelAudioVolume_AddRef(&This->session_wrapper->IChannelAudioVolume_iface);
994 *ppv = &This->session_wrapper->IChannelAudioVolume_iface;
995 }else if(IsEqualIID(riid, &IID_ISimpleAudioVolume)){
996 if(!This->session_wrapper){
997 This->session_wrapper = AudioSessionWrapper_Create(This);
998 if(!This->session_wrapper){
999 sessions_unlock();
1000 return E_OUTOFMEMORY;
1002 }else
1003 ISimpleAudioVolume_AddRef(&This->session_wrapper->ISimpleAudioVolume_iface);
1005 *ppv = &This->session_wrapper->ISimpleAudioVolume_iface;
1008 if(*ppv){
1009 sessions_unlock();
1010 return S_OK;
1013 sessions_unlock();
1015 FIXME("stub %s\n", debugstr_guid(riid));
1016 return E_NOINTERFACE;
1019 static HRESULT WINAPI AudioClient_IsOffloadCapable(IAudioClient3 *iface,
1020 AUDIO_STREAM_CATEGORY category, BOOL *offload_capable)
1022 ACImpl *This = impl_from_IAudioClient3(iface);
1024 TRACE("(%p)->(0x%x, %p)\n", This, category, offload_capable);
1026 if(!offload_capable)
1027 return E_INVALIDARG;
1029 *offload_capable = FALSE;
1031 return S_OK;
1034 static HRESULT WINAPI AudioClient_SetClientProperties(IAudioClient3 *iface,
1035 const AudioClientProperties *prop)
1037 ACImpl *This = impl_from_IAudioClient3(iface);
1038 const Win8AudioClientProperties *legacy_prop = (const Win8AudioClientProperties *)prop;
1040 TRACE("(%p)->(%p)\n", This, prop);
1042 if(!legacy_prop)
1043 return E_POINTER;
1045 if(legacy_prop->cbSize == sizeof(AudioClientProperties)){
1046 TRACE("{ bIsOffload: %u, eCategory: 0x%x, Options: 0x%x }\n",
1047 legacy_prop->bIsOffload,
1048 legacy_prop->eCategory,
1049 prop->Options);
1050 }else if(legacy_prop->cbSize == sizeof(Win8AudioClientProperties)){
1051 TRACE("{ bIsOffload: %u, eCategory: 0x%x }\n",
1052 legacy_prop->bIsOffload,
1053 legacy_prop->eCategory);
1054 }else{
1055 WARN("Unsupported Size = %d\n", legacy_prop->cbSize);
1056 return E_INVALIDARG;
1060 if(legacy_prop->bIsOffload)
1061 return AUDCLNT_E_ENDPOINT_OFFLOAD_NOT_CAPABLE;
1063 return S_OK;
1066 static HRESULT WINAPI AudioClient_GetBufferSizeLimits(IAudioClient3 *iface,
1067 const WAVEFORMATEX *format, BOOL event_driven, REFERENCE_TIME *min_duration,
1068 REFERENCE_TIME *max_duration)
1070 ACImpl *This = impl_from_IAudioClient3(iface);
1072 FIXME("(%p)->(%p, %u, %p, %p)\n", This, format, event_driven, min_duration, max_duration);
1074 return E_NOTIMPL;
1077 static HRESULT WINAPI AudioClient_GetSharedModeEnginePeriod(IAudioClient3 *iface,
1078 const WAVEFORMATEX *format, UINT32 *default_period_frames, UINT32 *unit_period_frames,
1079 UINT32 *min_period_frames, UINT32 *max_period_frames)
1081 ACImpl *This = impl_from_IAudioClient3(iface);
1083 FIXME("(%p)->(%p, %p, %p, %p, %p)\n", This, format, default_period_frames, unit_period_frames,
1084 min_period_frames, max_period_frames);
1086 return E_NOTIMPL;
1089 static HRESULT WINAPI AudioClient_GetCurrentSharedModeEnginePeriod(IAudioClient3 *iface,
1090 WAVEFORMATEX **cur_format, UINT32 *cur_period_frames)
1092 ACImpl *This = impl_from_IAudioClient3(iface);
1094 FIXME("(%p)->(%p, %p)\n", This, cur_format, cur_period_frames);
1096 return E_NOTIMPL;
1099 static HRESULT WINAPI AudioClient_InitializeSharedAudioStream(IAudioClient3 *iface,
1100 DWORD flags, UINT32 period_frames, const WAVEFORMATEX *format,
1101 const GUID *session_guid)
1103 ACImpl *This = impl_from_IAudioClient3(iface);
1105 FIXME("(%p)->(0x%lx, %u, %p, %s)\n", This, flags, period_frames, format, debugstr_guid(session_guid));
1107 return E_NOTIMPL;
1110 static const IAudioClient3Vtbl AudioClient3_Vtbl =
1112 AudioClient_QueryInterface,
1113 AudioClient_AddRef,
1114 AudioClient_Release,
1115 AudioClient_Initialize,
1116 AudioClient_GetBufferSize,
1117 AudioClient_GetStreamLatency,
1118 AudioClient_GetCurrentPadding,
1119 AudioClient_IsFormatSupported,
1120 AudioClient_GetMixFormat,
1121 AudioClient_GetDevicePeriod,
1122 AudioClient_Start,
1123 AudioClient_Stop,
1124 AudioClient_Reset,
1125 AudioClient_SetEventHandle,
1126 AudioClient_GetService,
1127 AudioClient_IsOffloadCapable,
1128 AudioClient_SetClientProperties,
1129 AudioClient_GetBufferSizeLimits,
1130 AudioClient_GetSharedModeEnginePeriod,
1131 AudioClient_GetCurrentSharedModeEnginePeriod,
1132 AudioClient_InitializeSharedAudioStream,
1135 static HRESULT WINAPI AudioRenderClient_QueryInterface(
1136 IAudioRenderClient *iface, REFIID riid, void **ppv)
1138 ACImpl *This = impl_from_IAudioRenderClient(iface);
1139 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
1141 if(!ppv)
1142 return E_POINTER;
1143 *ppv = NULL;
1145 if(IsEqualIID(riid, &IID_IUnknown) ||
1146 IsEqualIID(riid, &IID_IAudioRenderClient))
1147 *ppv = iface;
1148 else if(IsEqualIID(riid, &IID_IMarshal))
1149 return IUnknown_QueryInterface(This->marshal, riid, ppv);
1151 if(*ppv){
1152 IUnknown_AddRef((IUnknown*)*ppv);
1153 return S_OK;
1156 WARN("Unknown interface %s\n", debugstr_guid(riid));
1157 return E_NOINTERFACE;
1160 static ULONG WINAPI AudioRenderClient_AddRef(IAudioRenderClient *iface)
1162 ACImpl *This = impl_from_IAudioRenderClient(iface);
1163 return IAudioClient3_AddRef(&This->IAudioClient3_iface);
1166 static ULONG WINAPI AudioRenderClient_Release(IAudioRenderClient *iface)
1168 ACImpl *This = impl_from_IAudioRenderClient(iface);
1169 return IAudioClient3_Release(&This->IAudioClient3_iface);
1172 static HRESULT WINAPI AudioRenderClient_GetBuffer(IAudioRenderClient *iface,
1173 UINT32 frames, BYTE **data)
1175 ACImpl *This = impl_from_IAudioRenderClient(iface);
1176 struct get_render_buffer_params params;
1178 TRACE("(%p)->(%u, %p)\n", This, frames, data);
1180 if(!data)
1181 return E_POINTER;
1182 *data = NULL;
1184 params.stream = This->stream;
1185 params.frames = frames;
1186 params.data = data;
1188 ALSA_CALL(get_render_buffer, &params);
1190 return params.result;
1193 static HRESULT WINAPI AudioRenderClient_ReleaseBuffer(
1194 IAudioRenderClient *iface, UINT32 written_frames, DWORD flags)
1196 ACImpl *This = impl_from_IAudioRenderClient(iface);
1197 struct release_render_buffer_params params;
1199 TRACE("(%p)->(%u, %lx)\n", This, written_frames, flags);
1201 params.stream = This->stream;
1202 params.written_frames = written_frames;
1203 params.flags = flags;
1205 ALSA_CALL(release_render_buffer, &params);
1207 return params.result;
1210 static const IAudioRenderClientVtbl AudioRenderClient_Vtbl = {
1211 AudioRenderClient_QueryInterface,
1212 AudioRenderClient_AddRef,
1213 AudioRenderClient_Release,
1214 AudioRenderClient_GetBuffer,
1215 AudioRenderClient_ReleaseBuffer
1218 static HRESULT WINAPI AudioCaptureClient_QueryInterface(
1219 IAudioCaptureClient *iface, REFIID riid, void **ppv)
1221 ACImpl *This = impl_from_IAudioCaptureClient(iface);
1222 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
1224 if(!ppv)
1225 return E_POINTER;
1226 *ppv = NULL;
1228 if(IsEqualIID(riid, &IID_IUnknown) ||
1229 IsEqualIID(riid, &IID_IAudioCaptureClient))
1230 *ppv = iface;
1231 else if(IsEqualIID(riid, &IID_IMarshal))
1232 return IUnknown_QueryInterface(This->marshal, riid, ppv);
1234 if(*ppv){
1235 IUnknown_AddRef((IUnknown*)*ppv);
1236 return S_OK;
1239 WARN("Unknown interface %s\n", debugstr_guid(riid));
1240 return E_NOINTERFACE;
1243 static ULONG WINAPI AudioCaptureClient_AddRef(IAudioCaptureClient *iface)
1245 ACImpl *This = impl_from_IAudioCaptureClient(iface);
1246 return IAudioClient3_AddRef(&This->IAudioClient3_iface);
1249 static ULONG WINAPI AudioCaptureClient_Release(IAudioCaptureClient *iface)
1251 ACImpl *This = impl_from_IAudioCaptureClient(iface);
1252 return IAudioClient3_Release(&This->IAudioClient3_iface);
1255 static HRESULT WINAPI AudioCaptureClient_GetBuffer(IAudioCaptureClient *iface,
1256 BYTE **data, UINT32 *frames, DWORD *flags, UINT64 *devpos,
1257 UINT64 *qpcpos)
1259 ACImpl *This = impl_from_IAudioCaptureClient(iface);
1260 struct get_capture_buffer_params params;
1262 TRACE("(%p)->(%p, %p, %p, %p, %p)\n", This, data, frames, flags,
1263 devpos, qpcpos);
1265 if(!data)
1266 return E_POINTER;
1268 *data = NULL;
1270 if(!frames || !flags)
1271 return E_POINTER;
1273 params.stream = This->stream;
1274 params.data = data;
1275 params.frames = frames;
1276 params.flags = (UINT*)flags;
1277 params.devpos = devpos;
1278 params.qpcpos = qpcpos;
1280 ALSA_CALL(get_capture_buffer, &params);
1282 return params.result;
1285 static HRESULT WINAPI AudioCaptureClient_ReleaseBuffer(
1286 IAudioCaptureClient *iface, UINT32 done)
1288 ACImpl *This = impl_from_IAudioCaptureClient(iface);
1289 struct release_capture_buffer_params params;
1291 TRACE("(%p)->(%u)\n", This, done);
1293 params.stream = This->stream;
1294 params.done = done;
1296 ALSA_CALL(release_capture_buffer, &params);
1298 return params.result;
1301 static HRESULT WINAPI AudioCaptureClient_GetNextPacketSize(
1302 IAudioCaptureClient *iface, UINT32 *frames)
1304 ACImpl *This = impl_from_IAudioCaptureClient(iface);
1305 struct get_next_packet_size_params params;
1307 TRACE("(%p)->(%p)\n", This, frames);
1309 if(!frames)
1310 return E_POINTER;
1312 params.stream = This->stream;
1313 params.frames = frames;
1315 ALSA_CALL(get_next_packet_size, &params);
1317 return params.result;
1320 static const IAudioCaptureClientVtbl AudioCaptureClient_Vtbl =
1322 AudioCaptureClient_QueryInterface,
1323 AudioCaptureClient_AddRef,
1324 AudioCaptureClient_Release,
1325 AudioCaptureClient_GetBuffer,
1326 AudioCaptureClient_ReleaseBuffer,
1327 AudioCaptureClient_GetNextPacketSize
1330 static AudioSessionWrapper *AudioSessionWrapper_Create(ACImpl *client)
1332 AudioSessionWrapper *ret;
1334 ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
1335 sizeof(AudioSessionWrapper));
1336 if(!ret)
1337 return NULL;
1339 ret->IAudioSessionControl2_iface.lpVtbl = &AudioSessionControl2_Vtbl;
1340 ret->ISimpleAudioVolume_iface.lpVtbl = &SimpleAudioVolume_Vtbl;
1341 ret->IChannelAudioVolume_iface.lpVtbl = &ChannelAudioVolume_Vtbl;
1343 ret->ref = 1;
1345 ret->client = client;
1346 if(client){
1347 ret->session = client->session;
1348 IAudioClient3_AddRef(&client->IAudioClient3_iface);
1351 return ret;
1354 HRESULT WINAPI AUDDRV_GetAudioSessionWrapper(const GUID *guid, IMMDevice *device,
1355 AudioSessionWrapper **out)
1357 AudioSession *session;
1359 HRESULT hr = get_audio_session(guid, device, 0, &session);
1360 if(FAILED(hr))
1361 return hr;
1363 *out = AudioSessionWrapper_Create(NULL);
1364 if(!*out)
1365 return E_OUTOFMEMORY;
1367 (*out)->session = session;
1369 return S_OK;
1372 HRESULT WINAPI AUDDRV_GetPropValue(GUID *guid, const PROPERTYKEY *prop, PROPVARIANT *out)
1374 struct get_prop_value_params params;
1375 char name[256];
1376 EDataFlow flow;
1377 unsigned int size = 0;
1379 TRACE("%s, (%s,%lu), %p\n", wine_dbgstr_guid(guid), wine_dbgstr_guid(&prop->fmtid), prop->pid, out);
1381 if(!get_alsa_name_by_guid(guid, name, sizeof(name), &flow))
1383 WARN("Unknown interface %s\n", debugstr_guid(guid));
1384 return E_NOINTERFACE;
1387 params.device = name;
1388 params.flow = flow;
1389 params.guid = guid;
1390 params.prop = prop;
1391 params.value = out;
1392 params.buffer = NULL;
1393 params.buffer_size = &size;
1395 while(1) {
1396 ALSA_CALL(get_prop_value, &params);
1398 if(params.result != E_NOT_SUFFICIENT_BUFFER)
1399 break;
1401 CoTaskMemFree(params.buffer);
1402 params.buffer = CoTaskMemAlloc(*params.buffer_size);
1403 if(!params.buffer)
1404 return E_OUTOFMEMORY;
1406 if(FAILED(params.result))
1407 CoTaskMemFree(params.buffer);
1409 return params.result;