msvcp60: Avoid FALSE:TRUE conditional expressions.
[wine/multimedia.git] / dlls / winealsa.drv / mmdevdrv.c
blob93e2b8dc66b86f782578bef4eee631ecf66acde5
1 /*
2 * Copyright 2010 Maarten Lankhorst for CodeWeavers
3 * Copyright 2011 Andrew Eikum for CodeWeavers
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 NONAMELESSUNION
21 #define COBJMACROS
22 #include "config.h"
24 #include <stdarg.h>
25 #include <math.h>
27 #include "windef.h"
28 #include "winbase.h"
29 #include "winnls.h"
30 #include "winreg.h"
31 #include "wine/debug.h"
32 #include "wine/unicode.h"
33 #include "wine/list.h"
35 #include "ole2.h"
36 #include "mmdeviceapi.h"
37 #include "devpkey.h"
38 #include "dshow.h"
39 #include "dsound.h"
41 #include "initguid.h"
42 #include "endpointvolume.h"
43 #include "audioclient.h"
44 #include "audiopolicy.h"
46 #include <alsa/asoundlib.h>
48 WINE_DEFAULT_DEBUG_CHANNEL(alsa);
49 WINE_DECLARE_DEBUG_CHANNEL(winediag);
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;
55 #define EXTRA_SAFE_RT 40000
57 struct ACImpl;
58 typedef struct ACImpl ACImpl;
60 typedef struct _AudioSession {
61 GUID guid;
62 struct list clients;
64 IMMDevice *device;
66 float master_vol;
67 UINT32 channel_count;
68 float *channel_vols;
69 BOOL mute;
71 CRITICAL_SECTION lock;
73 struct list entry;
74 } AudioSession;
76 typedef struct _AudioSessionWrapper {
77 IAudioSessionControl2 IAudioSessionControl2_iface;
78 IChannelAudioVolume IChannelAudioVolume_iface;
79 ISimpleAudioVolume ISimpleAudioVolume_iface;
81 LONG ref;
83 ACImpl *client;
84 AudioSession *session;
85 } AudioSessionWrapper;
87 struct ACImpl {
88 IAudioClient IAudioClient_iface;
89 IAudioRenderClient IAudioRenderClient_iface;
90 IAudioCaptureClient IAudioCaptureClient_iface;
91 IAudioClock IAudioClock_iface;
92 IAudioClock2 IAudioClock2_iface;
93 IAudioStreamVolume IAudioStreamVolume_iface;
95 LONG ref;
97 snd_pcm_t *pcm_handle;
98 snd_pcm_uframes_t alsa_bufsize_frames, alsa_period_frames;
99 snd_pcm_hw_params_t *hw_params; /* does not hold state between calls */
100 snd_pcm_format_t alsa_format;
102 IMMDevice *parent;
104 EDataFlow dataflow;
105 WAVEFORMATEX *fmt;
106 DWORD flags;
107 AUDCLNT_SHAREMODE share;
108 HANDLE event;
109 float *vols;
111 BOOL initted, started;
112 REFERENCE_TIME mmdev_period_rt;
113 UINT64 written_frames, last_pos_frames;
114 UINT32 bufsize_frames, held_frames, tmp_buffer_frames, mmdev_period_frames;
115 UINT32 lcl_offs_frames; /* offs into local_buffer where valid data starts */
116 UINT32 hidden_frames; /* ALSA reserve to ensure continuous rendering */
118 HANDLE timer;
119 BYTE *local_buffer, *tmp_buffer;
120 LONG32 getbuf_last; /* <0 when using tmp_buffer */
122 CRITICAL_SECTION lock;
124 AudioSession *session;
125 AudioSessionWrapper *session_wrapper;
127 struct list entry;
130 typedef struct _SessionMgr {
131 IAudioSessionManager2 IAudioSessionManager2_iface;
133 LONG ref;
135 IMMDevice *device;
136 } SessionMgr;
138 static HANDLE g_timer_q;
140 static CRITICAL_SECTION g_sessions_lock;
141 static CRITICAL_SECTION_DEBUG g_sessions_lock_debug =
143 0, 0, &g_sessions_lock,
144 { &g_sessions_lock_debug.ProcessLocksList, &g_sessions_lock_debug.ProcessLocksList },
145 0, 0, { (DWORD_PTR)(__FILE__ ": g_sessions_lock") }
147 static CRITICAL_SECTION g_sessions_lock = { &g_sessions_lock_debug, -1, 0, 0, 0, 0 };
148 static struct list g_sessions = LIST_INIT(g_sessions);
150 static const WCHAR defaultW[] = {'d','e','f','a','u','l','t',0};
151 static const char defname[] = "default";
153 static const WCHAR drv_keyW[] = {'S','o','f','t','w','a','r','e','\\',
154 'W','i','n','e','\\','D','r','i','v','e','r','s','\\',
155 'w','i','n','e','a','l','s','a','.','d','r','v',0};
156 static const WCHAR drv_key_devicesW[] = {'S','o','f','t','w','a','r','e','\\',
157 'W','i','n','e','\\','D','r','i','v','e','r','s','\\',
158 'w','i','n','e','a','l','s','a','.','d','r','v','\\','d','e','v','i','c','e','s',0};
159 static const WCHAR guidW[] = {'g','u','i','d',0};
161 static const IAudioClientVtbl AudioClient_Vtbl;
162 static const IAudioRenderClientVtbl AudioRenderClient_Vtbl;
163 static const IAudioCaptureClientVtbl AudioCaptureClient_Vtbl;
164 static const IAudioSessionControl2Vtbl AudioSessionControl2_Vtbl;
165 static const ISimpleAudioVolumeVtbl SimpleAudioVolume_Vtbl;
166 static const IAudioClockVtbl AudioClock_Vtbl;
167 static const IAudioClock2Vtbl AudioClock2_Vtbl;
168 static const IAudioStreamVolumeVtbl AudioStreamVolume_Vtbl;
169 static const IChannelAudioVolumeVtbl ChannelAudioVolume_Vtbl;
170 static const IAudioSessionManager2Vtbl AudioSessionManager2_Vtbl;
172 static AudioSessionWrapper *AudioSessionWrapper_Create(ACImpl *client);
174 static inline ACImpl *impl_from_IAudioClient(IAudioClient *iface)
176 return CONTAINING_RECORD(iface, ACImpl, IAudioClient_iface);
179 static inline ACImpl *impl_from_IAudioRenderClient(IAudioRenderClient *iface)
181 return CONTAINING_RECORD(iface, ACImpl, IAudioRenderClient_iface);
184 static inline ACImpl *impl_from_IAudioCaptureClient(IAudioCaptureClient *iface)
186 return CONTAINING_RECORD(iface, ACImpl, IAudioCaptureClient_iface);
189 static inline AudioSessionWrapper *impl_from_IAudioSessionControl2(IAudioSessionControl2 *iface)
191 return CONTAINING_RECORD(iface, AudioSessionWrapper, IAudioSessionControl2_iface);
194 static inline AudioSessionWrapper *impl_from_ISimpleAudioVolume(ISimpleAudioVolume *iface)
196 return CONTAINING_RECORD(iface, AudioSessionWrapper, ISimpleAudioVolume_iface);
199 static inline AudioSessionWrapper *impl_from_IChannelAudioVolume(IChannelAudioVolume *iface)
201 return CONTAINING_RECORD(iface, AudioSessionWrapper, IChannelAudioVolume_iface);
204 static inline ACImpl *impl_from_IAudioClock(IAudioClock *iface)
206 return CONTAINING_RECORD(iface, ACImpl, IAudioClock_iface);
209 static inline ACImpl *impl_from_IAudioClock2(IAudioClock2 *iface)
211 return CONTAINING_RECORD(iface, ACImpl, IAudioClock2_iface);
214 static inline ACImpl *impl_from_IAudioStreamVolume(IAudioStreamVolume *iface)
216 return CONTAINING_RECORD(iface, ACImpl, IAudioStreamVolume_iface);
219 static inline SessionMgr *impl_from_IAudioSessionManager2(IAudioSessionManager2 *iface)
221 return CONTAINING_RECORD(iface, SessionMgr, IAudioSessionManager2_iface);
224 BOOL WINAPI DllMain(HINSTANCE dll, DWORD reason, void *reserved)
226 switch (reason)
228 case DLL_PROCESS_ATTACH:
229 g_timer_q = CreateTimerQueue();
230 if(!g_timer_q)
231 return FALSE;
232 break;
234 case DLL_PROCESS_DETACH:
235 DeleteCriticalSection(&g_sessions_lock);
236 break;
238 return TRUE;
241 /* From <dlls/mmdevapi/mmdevapi.h> */
242 enum DriverPriority {
243 Priority_Unavailable = 0,
244 Priority_Low,
245 Priority_Neutral,
246 Priority_Preferred
249 int WINAPI AUDDRV_GetPriority(void)
251 return Priority_Neutral;
254 static void set_device_guid(EDataFlow flow, HKEY drv_key, const WCHAR *key_name,
255 GUID *guid)
257 HKEY key;
258 BOOL opened = FALSE;
259 LONG lr;
261 if(!drv_key){
262 lr = RegCreateKeyExW(HKEY_CURRENT_USER, drv_key_devicesW, 0, NULL, 0, KEY_WRITE,
263 NULL, &drv_key, NULL);
264 if(lr != ERROR_SUCCESS){
265 ERR("RegCreateKeyEx(drv_key) failed: %u\n", lr);
266 return;
268 opened = TRUE;
271 lr = RegCreateKeyExW(drv_key, key_name, 0, NULL, 0, KEY_WRITE,
272 NULL, &key, NULL);
273 if(lr != ERROR_SUCCESS){
274 ERR("RegCreateKeyEx(%s) failed: %u\n", wine_dbgstr_w(key_name), lr);
275 goto exit;
278 lr = RegSetValueExW(key, guidW, 0, REG_BINARY, (BYTE*)guid,
279 sizeof(GUID));
280 if(lr != ERROR_SUCCESS)
281 ERR("RegSetValueEx(%s\\guid) failed: %u\n", wine_dbgstr_w(key_name), lr);
283 RegCloseKey(key);
284 exit:
285 if(opened)
286 RegCloseKey(drv_key);
289 static void get_device_guid(EDataFlow flow, const char *device, GUID *guid)
291 HKEY key = NULL, dev_key;
292 DWORD type, size = sizeof(*guid);
293 WCHAR key_name[256];
295 if(flow == eCapture)
296 key_name[0] = '1';
297 else
298 key_name[0] = '0';
299 key_name[1] = ',';
300 MultiByteToWideChar(CP_UNIXCP, 0, device, -1, key_name + 2,
301 (sizeof(key_name) / sizeof(*key_name)) - 2);
303 if(RegOpenKeyExW(HKEY_CURRENT_USER, drv_key_devicesW, 0, KEY_WRITE|KEY_READ, &key) == ERROR_SUCCESS){
304 if(RegOpenKeyExW(key, key_name, 0, KEY_READ, &dev_key) == ERROR_SUCCESS){
305 if(RegQueryValueExW(dev_key, guidW, 0, &type,
306 (BYTE*)guid, &size) == ERROR_SUCCESS){
307 if(type == REG_BINARY){
308 RegCloseKey(dev_key);
309 RegCloseKey(key);
310 return;
312 ERR("Invalid type for device %s GUID: %u; ignoring and overwriting\n",
313 wine_dbgstr_w(key_name), type);
315 RegCloseKey(dev_key);
319 CoCreateGuid(guid);
321 set_device_guid(flow, key, key_name, guid);
323 if(key)
324 RegCloseKey(key);
327 static BOOL alsa_try_open(const char *devnode, snd_pcm_stream_t stream)
329 snd_pcm_t *handle;
330 int err;
332 TRACE("devnode: %s, stream: %d\n", devnode, stream);
334 if((err = snd_pcm_open(&handle, devnode, stream, SND_PCM_NONBLOCK)) < 0){
335 WARN("The device \"%s\" failed to open: %d (%s).\n",
336 devnode, err, snd_strerror(err));
337 return FALSE;
340 snd_pcm_close(handle);
341 return TRUE;
344 static WCHAR *construct_device_id(EDataFlow flow, const WCHAR *chunk1, const char *chunk2)
346 WCHAR *ret;
347 const WCHAR *prefix;
348 DWORD len_wchars = 0, chunk1_len, copied = 0, prefix_len;
350 static const WCHAR dashW[] = {' ','-',' ',0};
351 static const size_t dashW_len = (sizeof(dashW) / sizeof(*dashW)) - 1;
352 static const WCHAR outW[] = {'O','u','t',':',' ',0};
353 static const WCHAR inW[] = {'I','n',':',' ',0};
355 if(flow == eRender){
356 prefix = outW;
357 prefix_len = (sizeof(outW) / sizeof(*outW)) - 1;
358 len_wchars += prefix_len;
359 }else{
360 prefix = inW;
361 prefix_len = (sizeof(inW) / sizeof(*inW)) - 1;
362 len_wchars += prefix_len;
364 if(chunk1){
365 chunk1_len = strlenW(chunk1);
366 len_wchars += chunk1_len;
368 if(chunk1 && chunk2)
369 len_wchars += dashW_len;
370 if(chunk2)
371 len_wchars += MultiByteToWideChar(CP_UNIXCP, 0, chunk2, -1, NULL, 0) - 1;
372 len_wchars += 1; /* NULL byte */
374 ret = HeapAlloc(GetProcessHeap(), 0, len_wchars * sizeof(WCHAR));
376 memcpy(ret, prefix, prefix_len * sizeof(WCHAR));
377 copied += prefix_len;
378 if(chunk1){
379 memcpy(ret + copied, chunk1, chunk1_len * sizeof(WCHAR));
380 copied += chunk1_len;
382 if(chunk1 && chunk2){
383 memcpy(ret + copied, dashW, dashW_len * sizeof(WCHAR));
384 copied += dashW_len;
386 if(chunk2){
387 MultiByteToWideChar(CP_UNIXCP, 0, chunk2, -1, ret + copied, len_wchars - copied);
388 }else
389 ret[copied] = 0;
391 TRACE("Enumerated device: %s\n", wine_dbgstr_w(ret));
393 return ret;
396 static HRESULT alsa_get_card_devices(EDataFlow flow, snd_pcm_stream_t stream,
397 WCHAR ***ids, GUID **guids, UINT *num, snd_ctl_t *ctl, int card,
398 const WCHAR *cardnameW)
400 int err, device;
401 snd_pcm_info_t *info;
403 info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, snd_pcm_info_sizeof());
404 if(!info)
405 return E_OUTOFMEMORY;
407 snd_pcm_info_set_subdevice(info, 0);
408 snd_pcm_info_set_stream(info, stream);
410 device = -1;
411 for(err = snd_ctl_pcm_next_device(ctl, &device); device != -1 && err >= 0;
412 err = snd_ctl_pcm_next_device(ctl, &device)){
413 const char *devname;
414 char devnode[32];
416 snd_pcm_info_set_device(info, device);
418 if((err = snd_ctl_pcm_info(ctl, info)) < 0){
419 if(err == -ENOENT)
420 /* This device doesn't have the right stream direction */
421 continue;
423 WARN("Failed to get info for card %d, device %d: %d (%s)\n",
424 card, device, err, snd_strerror(err));
425 continue;
428 sprintf(devnode, "plughw:%d,%d", card, device);
429 if(!alsa_try_open(devnode, stream))
430 continue;
432 if(*num){
433 *ids = HeapReAlloc(GetProcessHeap(), 0, *ids, sizeof(WCHAR *) * (*num + 1));
434 *guids = HeapReAlloc(GetProcessHeap(), 0, *guids, sizeof(GUID) * (*num + 1));
435 }else{
436 *ids = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR *));
437 *guids = HeapAlloc(GetProcessHeap(), 0, sizeof(GUID));
440 devname = snd_pcm_info_get_name(info);
441 if(!devname){
442 WARN("Unable to get device name for card %d, device %d\n", card,
443 device);
444 continue;
447 (*ids)[*num] = construct_device_id(flow, cardnameW, devname);
448 get_device_guid(flow, devnode, &(*guids)[*num]);
450 ++(*num);
453 HeapFree(GetProcessHeap(), 0, info);
455 if(err != 0)
456 WARN("Got a failure during device enumeration on card %d: %d (%s)\n",
457 card, err, snd_strerror(err));
459 return S_OK;
462 static void get_reg_devices(EDataFlow flow, snd_pcm_stream_t stream, WCHAR ***ids,
463 GUID **guids, UINT *num)
465 static const WCHAR ALSAOutputDevices[] = {'A','L','S','A','O','u','t','p','u','t','D','e','v','i','c','e','s',0};
466 static const WCHAR ALSAInputDevices[] = {'A','L','S','A','I','n','p','u','t','D','e','v','i','c','e','s',0};
467 HKEY key;
468 WCHAR reg_devices[256];
469 DWORD size = sizeof(reg_devices), type;
470 const WCHAR *value_name = (stream == SND_PCM_STREAM_PLAYBACK) ? ALSAOutputDevices : ALSAInputDevices;
472 /* @@ Wine registry key: HKCU\Software\Wine\Drivers\winealsa.drv */
473 if(RegOpenKeyW(HKEY_CURRENT_USER, drv_keyW, &key) == ERROR_SUCCESS){
474 if(RegQueryValueExW(key, value_name, 0, &type,
475 (BYTE*)reg_devices, &size) == ERROR_SUCCESS){
476 WCHAR *p = reg_devices;
478 if(type != REG_MULTI_SZ){
479 ERR("Registry ALSA device list value type must be REG_MULTI_SZ\n");
480 RegCloseKey(key);
481 return;
484 while(*p){
485 char devname[64];
487 WideCharToMultiByte(CP_UNIXCP, 0, p, -1, devname, sizeof(devname), NULL, NULL);
489 if(alsa_try_open(devname, stream)){
490 if(*num){
491 *ids = HeapReAlloc(GetProcessHeap(), 0, *ids, sizeof(WCHAR *) * (*num + 1));
492 *guids = HeapReAlloc(GetProcessHeap(), 0, *guids, sizeof(GUID) * (*num + 1));
493 }else{
494 *ids = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR *));
495 *guids = HeapAlloc(GetProcessHeap(), 0, sizeof(GUID));
497 (*ids)[*num] = construct_device_id(flow, p, NULL);
498 get_device_guid(flow, devname, &(*guids)[*num]);
499 ++*num;
502 p += lstrlenW(p) + 1;
506 RegCloseKey(key);
510 static HRESULT alsa_enum_devices(EDataFlow flow, WCHAR ***ids, GUID **guids,
511 UINT *num)
513 snd_pcm_stream_t stream = (flow == eRender ? SND_PCM_STREAM_PLAYBACK :
514 SND_PCM_STREAM_CAPTURE);
515 int err, card;
517 card = -1;
518 *num = 0;
520 if(alsa_try_open(defname, stream)){
521 *ids = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR *));
522 (*ids)[0] = construct_device_id(flow, defaultW, NULL);
523 *guids = HeapAlloc(GetProcessHeap(), 0, sizeof(GUID));
524 get_device_guid(flow, defname, &(*guids)[0]);
525 ++*num;
528 get_reg_devices(flow, stream, ids, guids, num);
530 for(err = snd_card_next(&card); card != -1 && err >= 0;
531 err = snd_card_next(&card)){
532 char cardpath[64];
533 char *cardname;
534 WCHAR *cardnameW;
535 snd_ctl_t *ctl;
536 DWORD len;
538 sprintf(cardpath, "hw:%u", card);
540 if((err = snd_ctl_open(&ctl, cardpath, 0)) < 0){
541 WARN("Unable to open ctl for ALSA device %s: %d (%s)\n", cardpath,
542 err, snd_strerror(err));
543 continue;
546 if(snd_card_get_name(card, &cardname) < 0) {
547 /* FIXME: Should be localized */
548 static const WCHAR nameW[] = {'U','n','k','n','o','w','n',' ','s','o','u','n','d','c','a','r','d',0};
549 WARN("Unable to get card name for ALSA device %s: %d (%s)\n",
550 cardpath, err, snd_strerror(err));
551 alsa_get_card_devices(flow, stream, ids, guids, num, ctl, card, nameW);
552 }else{
553 len = MultiByteToWideChar(CP_UNIXCP, 0, cardname, -1, NULL, 0);
554 cardnameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
556 if(!cardnameW){
557 free(cardname);
558 snd_ctl_close(ctl);
559 return E_OUTOFMEMORY;
561 MultiByteToWideChar(CP_UNIXCP, 0, cardname, -1, cardnameW, len);
563 alsa_get_card_devices(flow, stream, ids, guids, num, ctl, card, cardnameW);
565 HeapFree(GetProcessHeap(), 0, cardnameW);
566 free(cardname);
569 snd_ctl_close(ctl);
572 if(err != 0)
573 WARN("Got a failure during card enumeration: %d (%s)\n",
574 err, snd_strerror(err));
576 return S_OK;
579 HRESULT WINAPI AUDDRV_GetEndpointIDs(EDataFlow flow, WCHAR ***ids, GUID **guids,
580 UINT *num, UINT *def_index)
582 HRESULT hr;
584 TRACE("%d %p %p %p %p\n", flow, ids, guids, num, def_index);
586 *ids = NULL;
587 *guids = NULL;
589 hr = alsa_enum_devices(flow, ids, guids, num);
590 if(FAILED(hr)){
591 UINT i;
592 for(i = 0; i < *num; ++i)
593 HeapFree(GetProcessHeap(), 0, (*ids)[i]);
594 HeapFree(GetProcessHeap(), 0, *ids);
595 HeapFree(GetProcessHeap(), 0, *guids);
596 return E_OUTOFMEMORY;
599 TRACE("Enumerated %u devices\n", *num);
601 if(*num == 0){
602 HeapFree(GetProcessHeap(), 0, *ids);
603 *ids = NULL;
604 HeapFree(GetProcessHeap(), 0, *guids);
605 *guids = NULL;
608 *def_index = 0;
610 return S_OK;
613 /* Using the pulse PCM device from alsa-plugins 1.0.24 triggers a bug
614 * which causes audio to cease playing after a few minutes of playback.
615 * Setting handle_underrun=1 on pulse-backed ALSA devices seems to work
616 * around this issue. */
617 static snd_config_t *make_handle_underrun_config(const char *name)
619 snd_config_t *lconf, *dev_node, *hu_node, *type_node;
620 char dev_node_name[64];
621 const char *type_str;
622 int err;
624 snd_config_update();
626 if((err = snd_config_copy(&lconf, snd_config)) < 0){
627 WARN("snd_config_copy failed: %d (%s)\n", err, snd_strerror(err));
628 return NULL;
631 sprintf(dev_node_name, "pcm.%s", name);
632 err = snd_config_search(lconf, dev_node_name, &dev_node);
633 if(err == -ENOENT){
634 snd_config_delete(lconf);
635 return NULL;
637 if(err < 0){
638 snd_config_delete(lconf);
639 WARN("snd_config_search failed: %d (%s)\n", err, snd_strerror(err));
640 return NULL;
643 /* ALSA is extremely fragile. If it runs into a config setting it doesn't
644 * recognize, it tends to fail or assert. So we only want to inject
645 * handle_underrun=1 on devices that we know will recognize it. */
646 err = snd_config_search(dev_node, "type", &type_node);
647 if(err == -ENOENT){
648 snd_config_delete(lconf);
649 return NULL;
651 if(err < 0){
652 snd_config_delete(lconf);
653 WARN("snd_config_search failed: %d (%s)\n", err, snd_strerror(err));
654 return NULL;
657 if((err = snd_config_get_string(type_node, &type_str)) < 0){
658 snd_config_delete(lconf);
659 return NULL;
662 if(strcmp(type_str, "pulse") != 0){
663 snd_config_delete(lconf);
664 return NULL;
667 err = snd_config_search(dev_node, "handle_underrun", &hu_node);
668 if(err >= 0){
669 /* user already has an explicit handle_underrun setting, so don't
670 * use a local config */
671 snd_config_delete(lconf);
672 return NULL;
674 if(err != -ENOENT){
675 snd_config_delete(lconf);
676 WARN("snd_config_search failed: %d (%s)\n", err, snd_strerror(err));
677 return NULL;
680 if((err = snd_config_imake_integer(&hu_node, "handle_underrun", 1)) < 0){
681 snd_config_delete(lconf);
682 WARN("snd_config_imake_integer failed: %d (%s)\n", err,
683 snd_strerror(err));
684 return NULL;
687 if((err = snd_config_add(dev_node, hu_node)) < 0){
688 snd_config_delete(lconf);
689 WARN("snd_config_add failed: %d (%s)\n", err, snd_strerror(err));
690 return NULL;
693 return lconf;
696 static BOOL get_alsa_name_by_guid(GUID *guid, char *name, DWORD name_size, EDataFlow *flow)
698 HKEY devices_key;
699 UINT i = 0;
700 WCHAR key_name[256];
701 DWORD key_name_size;
703 if(RegOpenKeyExW(HKEY_CURRENT_USER, drv_key_devicesW, 0, KEY_READ, &devices_key) != ERROR_SUCCESS){
704 ERR("No devices found in registry?\n");
705 return FALSE;
708 while(1){
709 HKEY key;
710 DWORD size, type;
711 GUID reg_guid;
713 key_name_size = sizeof(key_name)/sizeof(WCHAR);
714 if(RegEnumKeyExW(devices_key, i, key_name, &key_name_size, NULL,
715 NULL, NULL, NULL) != ERROR_SUCCESS)
716 break;
718 if(RegOpenKeyExW(devices_key, key_name, 0, KEY_READ, &key) != ERROR_SUCCESS){
719 WARN("Couldn't open key: %s\n", wine_dbgstr_w(key_name));
720 continue;
723 size = sizeof(reg_guid);
724 if(RegQueryValueExW(key, guidW, 0, &type,
725 (BYTE*)&reg_guid, &size) == ERROR_SUCCESS){
726 if(IsEqualGUID(&reg_guid, guid)){
727 RegCloseKey(key);
728 RegCloseKey(devices_key);
730 TRACE("Found matching device key: %s\n", wine_dbgstr_w(key_name));
732 if(key_name[0] == '0')
733 *flow = eRender;
734 else if(key_name[0] == '1')
735 *flow = eCapture;
736 else{
737 ERR("Unknown device type: %c\n", key_name[0]);
738 return FALSE;
741 WideCharToMultiByte(CP_UNIXCP, 0, key_name + 2, -1, name, name_size, NULL, NULL);
743 return TRUE;
747 RegCloseKey(key);
749 ++i;
752 RegCloseKey(devices_key);
754 WARN("No matching device in registry for GUID %s\n", debugstr_guid(guid));
756 return FALSE;
759 HRESULT WINAPI AUDDRV_GetAudioEndpoint(GUID *guid, IMMDevice *dev, IAudioClient **out)
761 ACImpl *This;
762 int err;
763 snd_pcm_stream_t stream;
764 snd_config_t *lconf;
765 static int handle_underrun = 1;
766 char alsa_name[256];
767 EDataFlow dataflow;
769 TRACE("%s %p %p\n", debugstr_guid(guid), dev, out);
771 if(!get_alsa_name_by_guid(guid, alsa_name, sizeof(alsa_name), &dataflow))
772 return AUDCLNT_E_DEVICE_INVALIDATED;
774 This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(ACImpl));
775 if(!This)
776 return E_OUTOFMEMORY;
778 This->IAudioClient_iface.lpVtbl = &AudioClient_Vtbl;
779 This->IAudioRenderClient_iface.lpVtbl = &AudioRenderClient_Vtbl;
780 This->IAudioCaptureClient_iface.lpVtbl = &AudioCaptureClient_Vtbl;
781 This->IAudioClock_iface.lpVtbl = &AudioClock_Vtbl;
782 This->IAudioClock2_iface.lpVtbl = &AudioClock2_Vtbl;
783 This->IAudioStreamVolume_iface.lpVtbl = &AudioStreamVolume_Vtbl;
785 if(dataflow == eRender)
786 stream = SND_PCM_STREAM_PLAYBACK;
787 else if(dataflow == eCapture)
788 stream = SND_PCM_STREAM_CAPTURE;
789 else{
790 HeapFree(GetProcessHeap(), 0, This);
791 return E_UNEXPECTED;
794 This->dataflow = dataflow;
795 if(handle_underrun && ((lconf = make_handle_underrun_config(alsa_name)))){
796 err = snd_pcm_open_lconf(&This->pcm_handle, alsa_name, stream, SND_PCM_NONBLOCK, lconf);
797 TRACE("Opening PCM device \"%s\" with handle_underrun: %d\n", alsa_name, err);
798 snd_config_delete(lconf);
799 /* Pulse <= 2010 returns EINVAL, it does not know handle_underrun. */
800 if(err == -EINVAL){
801 ERR_(winediag)("PulseAudio \"%s\" %d without handle_underrun. Audio may hang."
802 " Please upgrade to alsa_plugins >= 1.0.24\n", alsa_name, err);
803 handle_underrun = 0;
805 }else
806 err = -EINVAL;
807 if(err == -EINVAL){
808 err = snd_pcm_open(&This->pcm_handle, alsa_name, stream, SND_PCM_NONBLOCK);
810 if(err < 0){
811 HeapFree(GetProcessHeap(), 0, This);
812 WARN("Unable to open PCM \"%s\": %d (%s)\n", alsa_name, err, snd_strerror(err));
813 switch(err){
814 case -EBUSY:
815 return AUDCLNT_E_DEVICE_IN_USE;
816 default:
817 return AUDCLNT_E_ENDPOINT_CREATE_FAILED;
821 This->hw_params = HeapAlloc(GetProcessHeap(), 0,
822 snd_pcm_hw_params_sizeof());
823 if(!This->hw_params){
824 snd_pcm_close(This->pcm_handle);
825 HeapFree(GetProcessHeap(), 0, This);
826 return E_OUTOFMEMORY;
829 InitializeCriticalSection(&This->lock);
830 This->lock.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": ACImpl.lock");
832 This->parent = dev;
833 IMMDevice_AddRef(This->parent);
835 *out = &This->IAudioClient_iface;
836 IAudioClient_AddRef(&This->IAudioClient_iface);
838 return S_OK;
841 static HRESULT WINAPI AudioClient_QueryInterface(IAudioClient *iface,
842 REFIID riid, void **ppv)
844 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
846 if(!ppv)
847 return E_POINTER;
848 *ppv = NULL;
849 if(IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IAudioClient))
850 *ppv = iface;
851 if(*ppv){
852 IUnknown_AddRef((IUnknown*)*ppv);
853 return S_OK;
855 WARN("Unknown interface %s\n", debugstr_guid(riid));
856 return E_NOINTERFACE;
859 static ULONG WINAPI AudioClient_AddRef(IAudioClient *iface)
861 ACImpl *This = impl_from_IAudioClient(iface);
862 ULONG ref;
863 ref = InterlockedIncrement(&This->ref);
864 TRACE("(%p) Refcount now %u\n", This, ref);
865 return ref;
868 static ULONG WINAPI AudioClient_Release(IAudioClient *iface)
870 ACImpl *This = impl_from_IAudioClient(iface);
871 ULONG ref;
872 ref = InterlockedDecrement(&This->ref);
873 TRACE("(%p) Refcount now %u\n", This, ref);
874 if(!ref){
875 IAudioClient_Stop(iface);
876 IMMDevice_Release(This->parent);
877 This->lock.DebugInfo->Spare[0] = 0;
878 DeleteCriticalSection(&This->lock);
879 snd_pcm_drop(This->pcm_handle);
880 snd_pcm_close(This->pcm_handle);
881 if(This->initted){
882 EnterCriticalSection(&g_sessions_lock);
883 list_remove(&This->entry);
884 LeaveCriticalSection(&g_sessions_lock);
886 HeapFree(GetProcessHeap(), 0, This->vols);
887 HeapFree(GetProcessHeap(), 0, This->local_buffer);
888 HeapFree(GetProcessHeap(), 0, This->tmp_buffer);
889 HeapFree(GetProcessHeap(), 0, This->hw_params);
890 CoTaskMemFree(This->fmt);
891 HeapFree(GetProcessHeap(), 0, This);
893 return ref;
896 static void dump_fmt(const WAVEFORMATEX *fmt)
898 TRACE("wFormatTag: 0x%x (", fmt->wFormatTag);
899 switch(fmt->wFormatTag){
900 case WAVE_FORMAT_PCM:
901 TRACE("WAVE_FORMAT_PCM");
902 break;
903 case WAVE_FORMAT_IEEE_FLOAT:
904 TRACE("WAVE_FORMAT_IEEE_FLOAT");
905 break;
906 case WAVE_FORMAT_EXTENSIBLE:
907 TRACE("WAVE_FORMAT_EXTENSIBLE");
908 break;
909 default:
910 TRACE("Unknown");
911 break;
913 TRACE(")\n");
915 TRACE("nChannels: %u\n", fmt->nChannels);
916 TRACE("nSamplesPerSec: %u\n", fmt->nSamplesPerSec);
917 TRACE("nAvgBytesPerSec: %u\n", fmt->nAvgBytesPerSec);
918 TRACE("nBlockAlign: %u\n", fmt->nBlockAlign);
919 TRACE("wBitsPerSample: %u\n", fmt->wBitsPerSample);
920 TRACE("cbSize: %u\n", fmt->cbSize);
922 if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE){
923 WAVEFORMATEXTENSIBLE *fmtex = (void*)fmt;
924 TRACE("dwChannelMask: %08x\n", fmtex->dwChannelMask);
925 TRACE("Samples: %04x\n", fmtex->Samples.wReserved);
926 TRACE("SubFormat: %s\n", wine_dbgstr_guid(&fmtex->SubFormat));
930 static WAVEFORMATEX *clone_format(const WAVEFORMATEX *fmt)
932 WAVEFORMATEX *ret;
933 size_t size;
935 if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
936 size = sizeof(WAVEFORMATEXTENSIBLE);
937 else
938 size = sizeof(WAVEFORMATEX);
940 ret = CoTaskMemAlloc(size);
941 if(!ret)
942 return NULL;
944 memcpy(ret, fmt, size);
946 ret->cbSize = size - sizeof(WAVEFORMATEX);
948 return ret;
951 static snd_pcm_format_t alsa_format(const WAVEFORMATEX *fmt)
953 snd_pcm_format_t format = SND_PCM_FORMAT_UNKNOWN;
954 const WAVEFORMATEXTENSIBLE *fmtex = (const WAVEFORMATEXTENSIBLE *)fmt;
956 if(fmt->wFormatTag == WAVE_FORMAT_PCM ||
957 (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
958 IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))){
959 if(fmt->wBitsPerSample == 8)
960 format = SND_PCM_FORMAT_U8;
961 else if(fmt->wBitsPerSample == 16)
962 format = SND_PCM_FORMAT_S16_LE;
963 else if(fmt->wBitsPerSample == 24)
964 format = SND_PCM_FORMAT_S24_3LE;
965 else if(fmt->wBitsPerSample == 32)
966 format = SND_PCM_FORMAT_S32_LE;
967 else
968 WARN("Unsupported bit depth: %u\n", fmt->wBitsPerSample);
969 if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
970 fmt->wBitsPerSample != fmtex->Samples.wValidBitsPerSample){
971 if(fmtex->Samples.wValidBitsPerSample == 20 && fmt->wBitsPerSample == 24)
972 format = SND_PCM_FORMAT_S20_3LE;
973 else{
974 WARN("Unsupported ValidBits: %u\n", fmtex->Samples.wValidBitsPerSample);
975 format = SND_PCM_FORMAT_UNKNOWN;
978 }else if(fmt->wFormatTag == WAVE_FORMAT_IEEE_FLOAT ||
979 (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
980 IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT))){
981 if(fmt->wBitsPerSample == 32)
982 format = SND_PCM_FORMAT_FLOAT_LE;
983 else if(fmt->wBitsPerSample == 64)
984 format = SND_PCM_FORMAT_FLOAT64_LE;
985 else
986 WARN("Unsupported float size: %u\n", fmt->wBitsPerSample);
987 }else
988 WARN("Unknown wave format: %04x\n", fmt->wFormatTag);
989 return format;
992 static void session_init_vols(AudioSession *session, UINT channels)
994 if(session->channel_count < channels){
995 UINT i;
997 if(session->channel_vols)
998 session->channel_vols = HeapReAlloc(GetProcessHeap(), 0,
999 session->channel_vols, sizeof(float) * channels);
1000 else
1001 session->channel_vols = HeapAlloc(GetProcessHeap(), 0,
1002 sizeof(float) * channels);
1003 if(!session->channel_vols)
1004 return;
1006 for(i = session->channel_count; i < channels; ++i)
1007 session->channel_vols[i] = 1.f;
1009 session->channel_count = channels;
1013 static AudioSession *create_session(const GUID *guid, IMMDevice *device,
1014 UINT num_channels)
1016 AudioSession *ret;
1018 ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(AudioSession));
1019 if(!ret)
1020 return NULL;
1022 memcpy(&ret->guid, guid, sizeof(GUID));
1024 ret->device = device;
1026 list_init(&ret->clients);
1028 list_add_head(&g_sessions, &ret->entry);
1030 InitializeCriticalSection(&ret->lock);
1031 ret->lock.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": AudioSession.lock");
1033 session_init_vols(ret, num_channels);
1035 ret->master_vol = 1.f;
1037 return ret;
1040 /* if channels == 0, then this will return or create a session with
1041 * matching dataflow and GUID. otherwise, channels must also match */
1042 static HRESULT get_audio_session(const GUID *sessionguid,
1043 IMMDevice *device, UINT channels, AudioSession **out)
1045 AudioSession *session;
1047 if(!sessionguid || IsEqualGUID(sessionguid, &GUID_NULL)){
1048 *out = create_session(&GUID_NULL, device, channels);
1049 if(!*out)
1050 return E_OUTOFMEMORY;
1052 return S_OK;
1055 *out = NULL;
1056 LIST_FOR_EACH_ENTRY(session, &g_sessions, AudioSession, entry){
1057 if(session->device == device &&
1058 IsEqualGUID(sessionguid, &session->guid)){
1059 session_init_vols(session, channels);
1060 *out = session;
1061 break;
1065 if(!*out){
1066 *out = create_session(sessionguid, device, channels);
1067 if(!*out)
1068 return E_OUTOFMEMORY;
1071 return S_OK;
1074 static HRESULT WINAPI AudioClient_Initialize(IAudioClient *iface,
1075 AUDCLNT_SHAREMODE mode, DWORD flags, REFERENCE_TIME duration,
1076 REFERENCE_TIME period, const WAVEFORMATEX *fmt,
1077 const GUID *sessionguid)
1079 ACImpl *This = impl_from_IAudioClient(iface);
1080 snd_pcm_sw_params_t *sw_params = NULL;
1081 snd_pcm_format_t format;
1082 unsigned int rate, alsa_period_us;
1083 int err, i;
1084 HRESULT hr = S_OK;
1086 TRACE("(%p)->(%x, %x, %s, %s, %p, %s)\n", This, mode, flags,
1087 wine_dbgstr_longlong(duration), wine_dbgstr_longlong(period), fmt, debugstr_guid(sessionguid));
1089 if(!fmt)
1090 return E_POINTER;
1092 if(mode != AUDCLNT_SHAREMODE_SHARED && mode != AUDCLNT_SHAREMODE_EXCLUSIVE)
1093 return AUDCLNT_E_NOT_INITIALIZED;
1095 if(flags & ~(AUDCLNT_STREAMFLAGS_CROSSPROCESS |
1096 AUDCLNT_STREAMFLAGS_LOOPBACK |
1097 AUDCLNT_STREAMFLAGS_EVENTCALLBACK |
1098 AUDCLNT_STREAMFLAGS_NOPERSIST |
1099 AUDCLNT_STREAMFLAGS_RATEADJUST |
1100 AUDCLNT_SESSIONFLAGS_EXPIREWHENUNOWNED |
1101 AUDCLNT_SESSIONFLAGS_DISPLAY_HIDE |
1102 AUDCLNT_SESSIONFLAGS_DISPLAY_HIDEWHENEXPIRED)){
1103 TRACE("Unknown flags: %08x\n", flags);
1104 return E_INVALIDARG;
1107 if(mode == AUDCLNT_SHAREMODE_SHARED){
1108 period = DefaultPeriod;
1109 if( duration < 3 * period)
1110 duration = 3 * period;
1111 }else{
1112 if(!period)
1113 period = DefaultPeriod; /* not minimum */
1114 if(period < MinimumPeriod || period > 5000000)
1115 return AUDCLNT_E_INVALID_DEVICE_PERIOD;
1116 if(duration > 20000000) /* the smaller the period, the lower this limit */
1117 return AUDCLNT_E_BUFFER_SIZE_ERROR;
1118 if(flags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK){
1119 if(duration != period)
1120 return AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL;
1121 FIXME("EXCLUSIVE mode with EVENTCALLBACK\n");
1122 return AUDCLNT_E_DEVICE_IN_USE;
1123 }else{
1124 if( duration < 8 * period)
1125 duration = 8 * period; /* may grow above 2s */
1129 EnterCriticalSection(&This->lock);
1131 if(This->initted){
1132 LeaveCriticalSection(&This->lock);
1133 return AUDCLNT_E_ALREADY_INITIALIZED;
1136 dump_fmt(fmt);
1138 if((err = snd_pcm_hw_params_any(This->pcm_handle, This->hw_params)) < 0){
1139 WARN("Unable to get hw_params: %d (%s)\n", err, snd_strerror(err));
1140 hr = AUDCLNT_E_ENDPOINT_CREATE_FAILED;
1141 goto exit;
1144 if((err = snd_pcm_hw_params_set_access(This->pcm_handle, This->hw_params,
1145 SND_PCM_ACCESS_RW_INTERLEAVED)) < 0){
1146 WARN("Unable to set access: %d (%s)\n", err, snd_strerror(err));
1147 hr = AUDCLNT_E_ENDPOINT_CREATE_FAILED;
1148 goto exit;
1151 format = alsa_format(fmt);
1152 if (format == SND_PCM_FORMAT_UNKNOWN){
1153 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
1154 goto exit;
1157 if((err = snd_pcm_hw_params_set_format(This->pcm_handle, This->hw_params,
1158 format)) < 0){
1159 WARN("Unable to set ALSA format to %u: %d (%s)\n", format, err,
1160 snd_strerror(err));
1161 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
1162 goto exit;
1165 This->alsa_format = format;
1167 rate = fmt->nSamplesPerSec;
1168 if((err = snd_pcm_hw_params_set_rate_near(This->pcm_handle, This->hw_params,
1169 &rate, NULL)) < 0){
1170 WARN("Unable to set rate to %u: %d (%s)\n", rate, err,
1171 snd_strerror(err));
1172 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
1173 goto exit;
1176 if((err = snd_pcm_hw_params_set_channels(This->pcm_handle, This->hw_params,
1177 fmt->nChannels)) < 0){
1178 WARN("Unable to set channels to %u: %d (%s)\n", fmt->nChannels, err,
1179 snd_strerror(err));
1180 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
1181 goto exit;
1184 This->mmdev_period_rt = period;
1185 alsa_period_us = This->mmdev_period_rt / 10;
1186 if((err = snd_pcm_hw_params_set_period_time_near(This->pcm_handle,
1187 This->hw_params, &alsa_period_us, NULL)) < 0)
1188 WARN("Unable to set period time near %u: %d (%s)\n", alsa_period_us,
1189 err, snd_strerror(err));
1190 /* ALSA updates the output variable alsa_period_us */
1192 This->mmdev_period_frames = MulDiv(fmt->nSamplesPerSec,
1193 This->mmdev_period_rt, 10000000);
1195 /* Buffer 4 ALSA periods if large enough, else 4 mmdevapi periods */
1196 This->alsa_bufsize_frames = This->mmdev_period_frames * 4;
1197 if(err < 0 || alsa_period_us < period / 10)
1198 err = snd_pcm_hw_params_set_buffer_size_near(This->pcm_handle,
1199 This->hw_params, &This->alsa_bufsize_frames);
1200 else{
1201 unsigned int periods = 4;
1202 err = snd_pcm_hw_params_set_periods_near(This->pcm_handle, This->hw_params, &periods, NULL);
1204 if(err < 0)
1205 WARN("Unable to set buffer size: %d (%s)\n", err, snd_strerror(err));
1207 if((err = snd_pcm_hw_params(This->pcm_handle, This->hw_params)) < 0){
1208 WARN("Unable to set hw params: %d (%s)\n", err, snd_strerror(err));
1209 hr = AUDCLNT_E_ENDPOINT_CREATE_FAILED;
1210 goto exit;
1213 if((err = snd_pcm_hw_params_get_period_size(This->hw_params,
1214 &This->alsa_period_frames, NULL)) < 0){
1215 WARN("Unable to get period size: %d (%s)\n", err, snd_strerror(err));
1216 hr = AUDCLNT_E_ENDPOINT_CREATE_FAILED;
1217 goto exit;
1220 if((err = snd_pcm_hw_params_get_buffer_size(This->hw_params,
1221 &This->alsa_bufsize_frames)) < 0){
1222 WARN("Unable to get buffer size: %d (%s)\n", err, snd_strerror(err));
1223 hr = AUDCLNT_E_ENDPOINT_CREATE_FAILED;
1224 goto exit;
1227 sw_params = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, snd_pcm_sw_params_sizeof());
1228 if(!sw_params){
1229 hr = E_OUTOFMEMORY;
1230 goto exit;
1233 if((err = snd_pcm_sw_params_current(This->pcm_handle, sw_params)) < 0){
1234 WARN("Unable to get sw_params: %d (%s)\n", err, snd_strerror(err));
1235 hr = AUDCLNT_E_ENDPOINT_CREATE_FAILED;
1236 goto exit;
1239 if((err = snd_pcm_sw_params_set_start_threshold(This->pcm_handle,
1240 sw_params, 1)) < 0){
1241 WARN("Unable set start threshold to 0: %d (%s)\n", err, snd_strerror(err));
1242 hr = AUDCLNT_E_ENDPOINT_CREATE_FAILED;
1243 goto exit;
1246 if((err = snd_pcm_sw_params_set_stop_threshold(This->pcm_handle,
1247 sw_params, This->alsa_bufsize_frames)) < 0){
1248 WARN("Unable set stop threshold to %lu: %d (%s)\n",
1249 This->alsa_bufsize_frames, err, snd_strerror(err));
1250 hr = AUDCLNT_E_ENDPOINT_CREATE_FAILED;
1251 goto exit;
1254 if((err = snd_pcm_sw_params(This->pcm_handle, sw_params)) < 0){
1255 WARN("Unable to set sw params: %d (%s)\n", err, snd_strerror(err));
1256 hr = AUDCLNT_E_ENDPOINT_CREATE_FAILED;
1257 goto exit;
1260 if((err = snd_pcm_prepare(This->pcm_handle)) < 0){
1261 WARN("Unable to prepare device: %d (%s)\n", err, snd_strerror(err));
1262 hr = AUDCLNT_E_ENDPOINT_CREATE_FAILED;
1263 goto exit;
1266 /* Bear in mind weird situations where
1267 * ALSA period (50ms) > mmdevapi buffer (3x10ms)
1268 * or surprising rounding as seen with 22050x8x1 with Pulse:
1269 * ALSA period 220 vs. 221 frames in mmdevapi and
1270 * buffer 883 vs. 2205 frames in mmdevapi! */
1271 This->bufsize_frames = MulDiv(duration, fmt->nSamplesPerSec, 10000000);
1272 This->hidden_frames = This->alsa_period_frames + This->mmdev_period_frames +
1273 MulDiv(fmt->nSamplesPerSec, EXTRA_SAFE_RT, 10000000);
1275 /* Check if the ALSA buffer is so small that it will run out before
1276 * the next MMDevAPI period tick occurs. Allow a little wiggle room
1277 * with 120% of the period time. */
1278 if(This->alsa_bufsize_frames < 1.2 * This->mmdev_period_frames)
1279 FIXME("ALSA buffer time is too small. Expect underruns. (%lu < %u * 1.2)\n",
1280 This->alsa_bufsize_frames, This->mmdev_period_frames);
1282 This->local_buffer = HeapAlloc(GetProcessHeap(), 0,
1283 This->bufsize_frames * fmt->nBlockAlign);
1284 if(!This->local_buffer){
1285 hr = E_OUTOFMEMORY;
1286 goto exit;
1288 if (fmt->wBitsPerSample == 8)
1289 memset(This->local_buffer, 128, This->bufsize_frames * fmt->nBlockAlign);
1290 else
1291 memset(This->local_buffer, 0, This->bufsize_frames * fmt->nBlockAlign);
1293 This->fmt = clone_format(fmt);
1294 if(!This->fmt){
1295 hr = E_OUTOFMEMORY;
1296 goto exit;
1299 This->vols = HeapAlloc(GetProcessHeap(), 0, fmt->nChannels * sizeof(float));
1300 if(!This->vols){
1301 hr = E_OUTOFMEMORY;
1302 goto exit;
1305 for(i = 0; i < fmt->nChannels; ++i)
1306 This->vols[i] = 1.f;
1308 This->share = mode;
1309 This->flags = flags;
1311 EnterCriticalSection(&g_sessions_lock);
1313 hr = get_audio_session(sessionguid, This->parent, fmt->nChannels,
1314 &This->session);
1315 if(FAILED(hr)){
1316 LeaveCriticalSection(&g_sessions_lock);
1317 goto exit;
1320 list_add_tail(&This->session->clients, &This->entry);
1322 LeaveCriticalSection(&g_sessions_lock);
1324 This->initted = TRUE;
1326 TRACE("ALSA period: %lu frames\n", This->alsa_period_frames);
1327 TRACE("ALSA buffer: %lu frames\n", This->alsa_bufsize_frames);
1328 TRACE("MMDevice period: %u frames\n", This->mmdev_period_frames);
1329 TRACE("MMDevice buffer: %u frames\n", This->bufsize_frames);
1331 exit:
1332 HeapFree(GetProcessHeap(), 0, sw_params);
1333 if(FAILED(hr)){
1334 HeapFree(GetProcessHeap(), 0, This->local_buffer);
1335 This->local_buffer = NULL;
1336 CoTaskMemFree(This->fmt);
1337 This->fmt = NULL;
1338 HeapFree(GetProcessHeap(), 0, This->vols);
1339 This->vols = NULL;
1342 LeaveCriticalSection(&This->lock);
1344 return hr;
1347 static HRESULT WINAPI AudioClient_GetBufferSize(IAudioClient *iface,
1348 UINT32 *out)
1350 ACImpl *This = impl_from_IAudioClient(iface);
1352 TRACE("(%p)->(%p)\n", This, out);
1354 if(!out)
1355 return E_POINTER;
1357 EnterCriticalSection(&This->lock);
1359 if(!This->initted){
1360 LeaveCriticalSection(&This->lock);
1361 return AUDCLNT_E_NOT_INITIALIZED;
1364 *out = This->bufsize_frames;
1366 LeaveCriticalSection(&This->lock);
1368 return S_OK;
1371 static HRESULT WINAPI AudioClient_GetStreamLatency(IAudioClient *iface,
1372 REFERENCE_TIME *latency)
1374 ACImpl *This = impl_from_IAudioClient(iface);
1376 TRACE("(%p)->(%p)\n", This, latency);
1378 if(!latency)
1379 return E_POINTER;
1381 EnterCriticalSection(&This->lock);
1383 if(!This->initted){
1384 LeaveCriticalSection(&This->lock);
1385 return AUDCLNT_E_NOT_INITIALIZED;
1388 /* Hide some frames in the ALSA buffer. Allows us to return GetCurrentPadding=0
1389 * yet have enough data left to play (as if it were in native's mixer). Add:
1390 * + mmdevapi_period such that at the end of it, ALSA still has data;
1391 * + EXTRA_SAFE (~4ms) to allow for late callback invocation / fluctuation;
1392 * + alsa_period such that ALSA always has at least one period to play. */
1393 if(This->dataflow == eRender)
1394 *latency = MulDiv(This->hidden_frames, 10000000, This->fmt->nSamplesPerSec);
1395 else
1396 *latency = MulDiv(This->alsa_period_frames, 10000000, This->fmt->nSamplesPerSec)
1397 + This->mmdev_period_rt;
1399 LeaveCriticalSection(&This->lock);
1401 return S_OK;
1404 static HRESULT WINAPI AudioClient_GetCurrentPadding(IAudioClient *iface,
1405 UINT32 *out)
1407 ACImpl *This = impl_from_IAudioClient(iface);
1409 TRACE("(%p)->(%p)\n", This, out);
1411 if(!out)
1412 return E_POINTER;
1414 EnterCriticalSection(&This->lock);
1416 if(!This->initted){
1417 LeaveCriticalSection(&This->lock);
1418 return AUDCLNT_E_NOT_INITIALIZED;
1421 /* padding is solely updated at callback time in shared mode */
1422 *out = This->held_frames;
1424 LeaveCriticalSection(&This->lock);
1426 TRACE("pad: %u\n", *out);
1428 return S_OK;
1431 static DWORD get_channel_mask(unsigned int channels)
1433 switch(channels){
1434 case 0:
1435 return 0;
1436 case 1:
1437 return KSAUDIO_SPEAKER_MONO;
1438 case 2:
1439 return KSAUDIO_SPEAKER_STEREO;
1440 case 3:
1441 return KSAUDIO_SPEAKER_STEREO | SPEAKER_LOW_FREQUENCY;
1442 case 4:
1443 return KSAUDIO_SPEAKER_QUAD; /* not _SURROUND */
1444 case 5:
1445 return KSAUDIO_SPEAKER_QUAD | SPEAKER_LOW_FREQUENCY;
1446 case 6:
1447 return KSAUDIO_SPEAKER_5POINT1; /* not 5POINT1_SURROUND */
1448 case 7:
1449 return KSAUDIO_SPEAKER_5POINT1 | SPEAKER_BACK_CENTER;
1450 case 8:
1451 return KSAUDIO_SPEAKER_7POINT1_SURROUND; /* Vista deprecates 7POINT1 */
1453 FIXME("Unknown speaker configuration: %u\n", channels);
1454 return 0;
1457 static HRESULT WINAPI AudioClient_IsFormatSupported(IAudioClient *iface,
1458 AUDCLNT_SHAREMODE mode, const WAVEFORMATEX *fmt,
1459 WAVEFORMATEX **out)
1461 ACImpl *This = impl_from_IAudioClient(iface);
1462 snd_pcm_format_mask_t *formats = NULL;
1463 snd_pcm_format_t format;
1464 HRESULT hr = S_OK;
1465 WAVEFORMATEX *closest = NULL;
1466 const WAVEFORMATEXTENSIBLE *fmtex = (const WAVEFORMATEXTENSIBLE *)fmt;
1467 unsigned int max = 0, min = 0;
1468 int err;
1470 TRACE("(%p)->(%x, %p, %p)\n", This, mode, fmt, out);
1472 if(!fmt || (mode == AUDCLNT_SHAREMODE_SHARED && !out))
1473 return E_POINTER;
1475 if(mode != AUDCLNT_SHAREMODE_SHARED && mode != AUDCLNT_SHAREMODE_EXCLUSIVE)
1476 return E_INVALIDARG;
1478 if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
1479 fmt->cbSize < sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX))
1480 return E_INVALIDARG;
1482 dump_fmt(fmt);
1484 if(out){
1485 *out = NULL;
1486 if(mode != AUDCLNT_SHAREMODE_SHARED)
1487 out = NULL;
1490 EnterCriticalSection(&This->lock);
1492 if((err = snd_pcm_hw_params_any(This->pcm_handle, This->hw_params)) < 0){
1493 hr = AUDCLNT_E_DEVICE_INVALIDATED;
1494 goto exit;
1497 formats = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
1498 snd_pcm_format_mask_sizeof());
1499 if(!formats){
1500 hr = E_OUTOFMEMORY;
1501 goto exit;
1504 snd_pcm_hw_params_get_format_mask(This->hw_params, formats);
1505 format = alsa_format(fmt);
1506 if (format == SND_PCM_FORMAT_UNKNOWN ||
1507 !snd_pcm_format_mask_test(formats, format)){
1508 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
1509 goto exit;
1512 closest = clone_format(fmt);
1513 if(!closest){
1514 hr = E_OUTOFMEMORY;
1515 goto exit;
1518 if((err = snd_pcm_hw_params_get_rate_min(This->hw_params, &min, NULL)) < 0){
1519 hr = AUDCLNT_E_DEVICE_INVALIDATED;
1520 WARN("Unable to get min rate: %d (%s)\n", err, snd_strerror(err));
1521 goto exit;
1524 if((err = snd_pcm_hw_params_get_rate_max(This->hw_params, &max, NULL)) < 0){
1525 hr = AUDCLNT_E_DEVICE_INVALIDATED;
1526 WARN("Unable to get max rate: %d (%s)\n", err, snd_strerror(err));
1527 goto exit;
1530 if(fmt->nSamplesPerSec < min || fmt->nSamplesPerSec > max){
1531 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
1532 goto exit;
1535 if((err = snd_pcm_hw_params_get_channels_min(This->hw_params, &min)) < 0){
1536 hr = AUDCLNT_E_DEVICE_INVALIDATED;
1537 WARN("Unable to get min channels: %d (%s)\n", err, snd_strerror(err));
1538 goto exit;
1541 if((err = snd_pcm_hw_params_get_channels_max(This->hw_params, &max)) < 0){
1542 hr = AUDCLNT_E_DEVICE_INVALIDATED;
1543 WARN("Unable to get max channels: %d (%s)\n", err, snd_strerror(err));
1544 goto exit;
1546 if(max > 8)
1547 max = 2;
1548 if(fmt->nChannels > max){
1549 hr = S_FALSE;
1550 closest->nChannels = max;
1551 }else if(fmt->nChannels < min){
1552 hr = S_FALSE;
1553 closest->nChannels = min;
1556 if(closest->wFormatTag == WAVE_FORMAT_EXTENSIBLE){
1557 DWORD mask = get_channel_mask(closest->nChannels);
1559 ((WAVEFORMATEXTENSIBLE*)closest)->dwChannelMask = mask;
1561 if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
1562 fmtex->dwChannelMask != 0 &&
1563 fmtex->dwChannelMask != mask)
1564 hr = S_FALSE;
1567 exit:
1568 LeaveCriticalSection(&This->lock);
1569 HeapFree(GetProcessHeap(), 0, formats);
1571 if(hr == S_FALSE && !out)
1572 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
1574 if(hr == S_FALSE && out) {
1575 closest->nBlockAlign =
1576 closest->nChannels * closest->wBitsPerSample / 8;
1577 closest->nAvgBytesPerSec =
1578 closest->nBlockAlign * closest->nSamplesPerSec;
1579 *out = closest;
1580 } else
1581 CoTaskMemFree(closest);
1583 TRACE("returning: %08x\n", hr);
1584 return hr;
1587 static HRESULT WINAPI AudioClient_GetMixFormat(IAudioClient *iface,
1588 WAVEFORMATEX **pwfx)
1590 ACImpl *This = impl_from_IAudioClient(iface);
1591 WAVEFORMATEXTENSIBLE *fmt;
1592 snd_pcm_format_mask_t *formats;
1593 unsigned int max_rate, max_channels;
1594 int err;
1595 HRESULT hr = S_OK;
1597 TRACE("(%p)->(%p)\n", This, pwfx);
1599 if(!pwfx)
1600 return E_POINTER;
1601 *pwfx = NULL;
1603 fmt = CoTaskMemAlloc(sizeof(WAVEFORMATEXTENSIBLE));
1604 if(!fmt)
1605 return E_OUTOFMEMORY;
1607 formats = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, snd_pcm_format_mask_sizeof());
1608 if(!formats){
1609 CoTaskMemFree(fmt);
1610 return E_OUTOFMEMORY;
1613 EnterCriticalSection(&This->lock);
1615 if((err = snd_pcm_hw_params_any(This->pcm_handle, This->hw_params)) < 0){
1616 WARN("Unable to get hw_params: %d (%s)\n", err, snd_strerror(err));
1617 hr = AUDCLNT_E_DEVICE_INVALIDATED;
1618 goto exit;
1621 snd_pcm_hw_params_get_format_mask(This->hw_params, formats);
1623 fmt->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
1624 if(snd_pcm_format_mask_test(formats, SND_PCM_FORMAT_FLOAT_LE)){
1625 fmt->Format.wBitsPerSample = 32;
1626 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
1627 }else if(snd_pcm_format_mask_test(formats, SND_PCM_FORMAT_S16_LE)){
1628 fmt->Format.wBitsPerSample = 16;
1629 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1630 }else if(snd_pcm_format_mask_test(formats, SND_PCM_FORMAT_U8)){
1631 fmt->Format.wBitsPerSample = 8;
1632 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1633 }else if(snd_pcm_format_mask_test(formats, SND_PCM_FORMAT_S32_LE)){
1634 fmt->Format.wBitsPerSample = 32;
1635 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1636 }else if(snd_pcm_format_mask_test(formats, SND_PCM_FORMAT_S24_3LE)){
1637 fmt->Format.wBitsPerSample = 24;
1638 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1639 }else{
1640 ERR("Didn't recognize any available ALSA formats\n");
1641 hr = AUDCLNT_E_DEVICE_INVALIDATED;
1642 goto exit;
1645 if((err = snd_pcm_hw_params_get_channels_max(This->hw_params,
1646 &max_channels)) < 0){
1647 WARN("Unable to get max channels: %d (%s)\n", err, snd_strerror(err));
1648 hr = AUDCLNT_E_DEVICE_INVALIDATED;
1649 goto exit;
1652 if(max_channels > 2)
1653 fmt->Format.nChannels = 2;
1654 else
1655 fmt->Format.nChannels = max_channels;
1657 fmt->dwChannelMask = get_channel_mask(fmt->Format.nChannels);
1659 if((err = snd_pcm_hw_params_get_rate_max(This->hw_params, &max_rate,
1660 NULL)) < 0){
1661 WARN("Unable to get max rate: %d (%s)\n", err, snd_strerror(err));
1662 hr = AUDCLNT_E_DEVICE_INVALIDATED;
1663 goto exit;
1666 if(max_rate >= 48000)
1667 fmt->Format.nSamplesPerSec = 48000;
1668 else if(max_rate >= 44100)
1669 fmt->Format.nSamplesPerSec = 44100;
1670 else if(max_rate >= 22050)
1671 fmt->Format.nSamplesPerSec = 22050;
1672 else if(max_rate >= 11025)
1673 fmt->Format.nSamplesPerSec = 11025;
1674 else if(max_rate >= 8000)
1675 fmt->Format.nSamplesPerSec = 8000;
1676 else{
1677 ERR("Unknown max rate: %u\n", max_rate);
1678 hr = AUDCLNT_E_DEVICE_INVALIDATED;
1679 goto exit;
1682 fmt->Format.nBlockAlign = (fmt->Format.wBitsPerSample *
1683 fmt->Format.nChannels) / 8;
1684 fmt->Format.nAvgBytesPerSec = fmt->Format.nSamplesPerSec *
1685 fmt->Format.nBlockAlign;
1687 fmt->Samples.wValidBitsPerSample = fmt->Format.wBitsPerSample;
1688 fmt->Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
1690 dump_fmt((WAVEFORMATEX*)fmt);
1691 *pwfx = (WAVEFORMATEX*)fmt;
1693 exit:
1694 LeaveCriticalSection(&This->lock);
1695 if(FAILED(hr))
1696 CoTaskMemFree(fmt);
1697 HeapFree(GetProcessHeap(), 0, formats);
1699 return hr;
1702 static HRESULT WINAPI AudioClient_GetDevicePeriod(IAudioClient *iface,
1703 REFERENCE_TIME *defperiod, REFERENCE_TIME *minperiod)
1705 ACImpl *This = impl_from_IAudioClient(iface);
1707 TRACE("(%p)->(%p, %p)\n", This, defperiod, minperiod);
1709 if(!defperiod && !minperiod)
1710 return E_POINTER;
1712 if(defperiod)
1713 *defperiod = DefaultPeriod;
1714 if(minperiod)
1715 *minperiod = MinimumPeriod;
1717 return S_OK;
1720 static snd_pcm_sframes_t alsa_write_best_effort(snd_pcm_t *handle, BYTE *buf,
1721 snd_pcm_uframes_t frames, ACImpl *This, BOOL mute)
1723 snd_pcm_sframes_t written;
1725 if(mute){
1726 int err;
1727 if((err = snd_pcm_format_set_silence(This->alsa_format, buf,
1728 frames * This->fmt->nChannels)) < 0)
1729 WARN("Setting buffer to silence failed: %d (%s)\n", err,
1730 snd_strerror(err));
1733 written = snd_pcm_writei(handle, buf, frames);
1734 if(written < 0){
1735 int ret;
1737 if(written == -EAGAIN)
1738 /* buffer full */
1739 return 0;
1741 WARN("writei failed, recovering: %ld (%s)\n", written,
1742 snd_strerror(written));
1744 ret = snd_pcm_recover(handle, written, 0);
1745 if(ret < 0){
1746 WARN("Could not recover: %d (%s)\n", ret, snd_strerror(ret));
1747 return ret;
1750 written = snd_pcm_writei(handle, buf, frames);
1753 return written;
1756 static void alsa_write_data(ACImpl *This)
1758 snd_pcm_sframes_t written, in_alsa;
1759 snd_pcm_uframes_t to_write, avail, write_limit, max_period;
1760 int err;
1761 BYTE *buf =
1762 This->local_buffer + (This->lcl_offs_frames * This->fmt->nBlockAlign);
1764 /* this call seems to be required to get an accurate snd_pcm_state() */
1765 avail = snd_pcm_avail_update(This->pcm_handle);
1767 if(snd_pcm_state(This->pcm_handle) == SND_PCM_STATE_XRUN ||
1768 avail > This->alsa_bufsize_frames){
1769 TRACE("XRun state avail %ld, recovering\n", avail);
1771 avail = This->alsa_bufsize_frames;
1773 if((err = snd_pcm_recover(This->pcm_handle, -EPIPE, 1)) < 0)
1774 WARN("snd_pcm_recover failed: %d (%s)\n", err, snd_strerror(err));
1776 if((err = snd_pcm_reset(This->pcm_handle)) < 0)
1777 WARN("snd_pcm_reset failed: %d (%s)\n", err, snd_strerror(err));
1779 if((err = snd_pcm_prepare(This->pcm_handle)) < 0)
1780 WARN("snd_pcm_prepare failed: %d (%s)\n", err, snd_strerror(err));
1781 }else
1782 TRACE("pad: %ld\n", This->alsa_bufsize_frames - avail);
1784 if(This->held_frames == 0)
1785 return;
1787 if(This->lcl_offs_frames + This->held_frames > This->bufsize_frames)
1788 to_write = This->bufsize_frames - This->lcl_offs_frames;
1789 else
1790 to_write = This->held_frames;
1792 max_period = max(This->mmdev_period_frames, This->alsa_period_frames);
1794 /* try to keep 3 ALSA periods or 3 MMDevAPI periods in the ALSA buffer and
1795 * no more */
1796 write_limit = 0;
1797 in_alsa = This->alsa_bufsize_frames - avail;
1798 while(in_alsa + write_limit < max_period * 3)
1799 write_limit += max_period;
1800 if(write_limit == 0)
1801 return;
1803 to_write = min(to_write, write_limit);
1805 /* Add a lead-in when starting with too few frames to ensure
1806 * continuous rendering. Additional benefit: Force ALSA to start.
1807 * GetPosition continues to reflect the speaker position because
1808 * snd_pcm_delay includes buffered frames in its total delay
1809 * and last_pos_frames prevents moving backwards. */
1810 if(!in_alsa && This->held_frames < This->hidden_frames){
1811 UINT32 s_frames = This->hidden_frames - This->held_frames;
1812 BYTE *silence = HeapAlloc(GetProcessHeap(), 0,
1813 s_frames * This->fmt->nBlockAlign);
1815 if(silence){
1816 in_alsa = alsa_write_best_effort(This->pcm_handle,
1817 silence, s_frames, This, TRUE);
1818 TRACE("lead-in %ld\n", in_alsa);
1819 HeapFree(GetProcessHeap(), 0, silence);
1820 if(in_alsa <= 0)
1821 return;
1822 }else
1823 WARN("Couldn't allocate lead-in, expect underrun\n");
1826 written = alsa_write_best_effort(This->pcm_handle, buf, to_write, This,
1827 This->session->mute);
1828 if(written < 0){
1829 WARN("Couldn't write: %ld (%s)\n", written, snd_strerror(written));
1830 return;
1833 This->lcl_offs_frames += written;
1834 This->lcl_offs_frames %= This->bufsize_frames;
1835 This->held_frames -= written;
1837 if(written < to_write){
1838 /* ALSA buffer probably full */
1839 return;
1842 if(This->held_frames && (written < write_limit)){
1843 /* wrapped and have some data back at the start to write */
1844 written = alsa_write_best_effort(This->pcm_handle, This->local_buffer,
1845 min(This->held_frames, write_limit - written), This,
1846 This->session->mute);
1847 if(written < 0){
1848 WARN("Couldn't write: %ld (%s)\n", written, snd_strerror(written));
1849 return;
1852 This->lcl_offs_frames += written;
1853 This->lcl_offs_frames %= This->bufsize_frames;
1854 This->held_frames -= written;
1858 static void alsa_read_data(ACImpl *This)
1860 snd_pcm_sframes_t pos, readable, nread;
1862 pos = (This->held_frames + This->lcl_offs_frames) % This->bufsize_frames;
1863 readable = This->bufsize_frames - pos;
1865 nread = snd_pcm_readi(This->pcm_handle,
1866 This->local_buffer + pos * This->fmt->nBlockAlign, readable);
1867 if(nread < 0){
1868 int ret;
1870 WARN("read failed, recovering: %ld (%s)\n", nread, snd_strerror(nread));
1872 ret = snd_pcm_recover(This->pcm_handle, nread, 0);
1873 if(ret < 0){
1874 WARN("Recover failed: %d (%s)\n", ret, snd_strerror(ret));
1875 return;
1878 nread = snd_pcm_readi(This->pcm_handle,
1879 This->local_buffer + pos * This->fmt->nBlockAlign, readable);
1880 if(nread < 0){
1881 WARN("read failed: %ld (%s)\n", nread, snd_strerror(nread));
1882 return;
1886 if(This->session->mute){
1887 int err;
1888 if((err = snd_pcm_format_set_silence(This->alsa_format,
1889 This->local_buffer + pos * This->fmt->nBlockAlign,
1890 nread)) < 0)
1891 WARN("Setting buffer to silence failed: %d (%s)\n", err,
1892 snd_strerror(err));
1895 This->held_frames += nread;
1897 if(This->held_frames > This->bufsize_frames){
1898 WARN("Overflow of unread data\n");
1899 This->lcl_offs_frames += This->held_frames;
1900 This->lcl_offs_frames %= This->bufsize_frames;
1901 This->held_frames = This->bufsize_frames;
1905 static void CALLBACK alsa_push_buffer_data(void *user, BOOLEAN timer)
1907 ACImpl *This = user;
1909 EnterCriticalSection(&This->lock);
1911 if(This->started){
1912 if(This->dataflow == eRender)
1913 alsa_write_data(This);
1914 else if(This->dataflow == eCapture)
1915 alsa_read_data(This);
1917 if(This->event)
1918 SetEvent(This->event);
1921 LeaveCriticalSection(&This->lock);
1924 static HRESULT WINAPI AudioClient_Start(IAudioClient *iface)
1926 ACImpl *This = impl_from_IAudioClient(iface);
1928 TRACE("(%p)\n", This);
1930 EnterCriticalSection(&This->lock);
1932 if(!This->initted){
1933 LeaveCriticalSection(&This->lock);
1934 return AUDCLNT_E_NOT_INITIALIZED;
1937 if((This->flags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK) && !This->event){
1938 LeaveCriticalSection(&This->lock);
1939 return AUDCLNT_E_EVENTHANDLE_NOT_SET;
1942 if(This->started){
1943 LeaveCriticalSection(&This->lock);
1944 return AUDCLNT_E_NOT_STOPPED;
1947 if(This->dataflow == eCapture){
1948 /* dump any data that might be leftover in the ALSA capture buffer */
1949 snd_pcm_readi(This->pcm_handle, This->local_buffer,
1950 This->bufsize_frames);
1953 if(!CreateTimerQueueTimer(&This->timer, g_timer_q, alsa_push_buffer_data,
1954 This, 0, This->mmdev_period_rt / 10000, WT_EXECUTEINTIMERTHREAD)){
1955 LeaveCriticalSection(&This->lock);
1956 WARN("Unable to create timer: %u\n", GetLastError());
1957 return E_OUTOFMEMORY;
1960 This->started = TRUE;
1962 LeaveCriticalSection(&This->lock);
1964 return S_OK;
1967 static HRESULT WINAPI AudioClient_Stop(IAudioClient *iface)
1969 ACImpl *This = impl_from_IAudioClient(iface);
1970 HANDLE event;
1971 BOOL wait;
1973 TRACE("(%p)\n", This);
1975 EnterCriticalSection(&This->lock);
1977 if(!This->initted){
1978 LeaveCriticalSection(&This->lock);
1979 return AUDCLNT_E_NOT_INITIALIZED;
1982 if(!This->started){
1983 LeaveCriticalSection(&This->lock);
1984 return S_FALSE;
1987 /* Stop without losing written frames or position.
1988 * snd_pcm_pause would be appropriate but is unsupported by dmix.
1989 * snd_pcm_drain yields EAGAIN in NONBLOCK mode, except with Pulse. */
1991 event = CreateEventW(NULL, TRUE, FALSE, NULL);
1992 wait = !DeleteTimerQueueTimer(g_timer_q, This->timer, event);
1993 if(wait)
1994 WARN("DeleteTimerQueueTimer error %u\n", GetLastError());
1995 wait = wait && GetLastError() == ERROR_IO_PENDING;
1997 This->started = FALSE;
1999 LeaveCriticalSection(&This->lock);
2001 if(event && wait)
2002 WaitForSingleObject(event, INFINITE);
2003 CloseHandle(event);
2005 return S_OK;
2008 static HRESULT WINAPI AudioClient_Reset(IAudioClient *iface)
2010 ACImpl *This = impl_from_IAudioClient(iface);
2012 TRACE("(%p)\n", This);
2014 EnterCriticalSection(&This->lock);
2016 if(!This->initted){
2017 LeaveCriticalSection(&This->lock);
2018 return AUDCLNT_E_NOT_INITIALIZED;
2021 if(This->started){
2022 LeaveCriticalSection(&This->lock);
2023 return AUDCLNT_E_NOT_STOPPED;
2026 if(This->getbuf_last){
2027 LeaveCriticalSection(&This->lock);
2028 return AUDCLNT_E_BUFFER_OPERATION_PENDING;
2031 if(snd_pcm_drop(This->pcm_handle) < 0)
2032 WARN("snd_pcm_drop failed\n");
2034 if(snd_pcm_reset(This->pcm_handle) < 0)
2035 WARN("snd_pcm_reset failed\n");
2037 if(snd_pcm_prepare(This->pcm_handle) < 0)
2038 WARN("snd_pcm_prepare failed\n");
2040 if(This->dataflow == eRender){
2041 This->written_frames = 0;
2042 This->last_pos_frames = 0;
2043 }else{
2044 This->written_frames += This->held_frames;
2046 This->held_frames = 0;
2047 This->lcl_offs_frames = 0;
2049 LeaveCriticalSection(&This->lock);
2051 return S_OK;
2054 static HRESULT WINAPI AudioClient_SetEventHandle(IAudioClient *iface,
2055 HANDLE event)
2057 ACImpl *This = impl_from_IAudioClient(iface);
2059 TRACE("(%p)->(%p)\n", This, event);
2061 if(!event)
2062 return E_INVALIDARG;
2064 EnterCriticalSection(&This->lock);
2066 if(!This->initted){
2067 LeaveCriticalSection(&This->lock);
2068 return AUDCLNT_E_NOT_INITIALIZED;
2071 if(!(This->flags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK)){
2072 LeaveCriticalSection(&This->lock);
2073 return AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED;
2076 This->event = event;
2078 LeaveCriticalSection(&This->lock);
2080 return S_OK;
2083 static HRESULT WINAPI AudioClient_GetService(IAudioClient *iface, REFIID riid,
2084 void **ppv)
2086 ACImpl *This = impl_from_IAudioClient(iface);
2088 TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(riid), ppv);
2090 if(!ppv)
2091 return E_POINTER;
2092 *ppv = NULL;
2094 EnterCriticalSection(&This->lock);
2096 if(!This->initted){
2097 LeaveCriticalSection(&This->lock);
2098 return AUDCLNT_E_NOT_INITIALIZED;
2101 if(IsEqualIID(riid, &IID_IAudioRenderClient)){
2102 if(This->dataflow != eRender){
2103 LeaveCriticalSection(&This->lock);
2104 return AUDCLNT_E_WRONG_ENDPOINT_TYPE;
2106 IAudioRenderClient_AddRef(&This->IAudioRenderClient_iface);
2107 *ppv = &This->IAudioRenderClient_iface;
2108 }else if(IsEqualIID(riid, &IID_IAudioCaptureClient)){
2109 if(This->dataflow != eCapture){
2110 LeaveCriticalSection(&This->lock);
2111 return AUDCLNT_E_WRONG_ENDPOINT_TYPE;
2113 IAudioCaptureClient_AddRef(&This->IAudioCaptureClient_iface);
2114 *ppv = &This->IAudioCaptureClient_iface;
2115 }else if(IsEqualIID(riid, &IID_IAudioClock)){
2116 IAudioClock_AddRef(&This->IAudioClock_iface);
2117 *ppv = &This->IAudioClock_iface;
2118 }else if(IsEqualIID(riid, &IID_IAudioStreamVolume)){
2119 IAudioStreamVolume_AddRef(&This->IAudioStreamVolume_iface);
2120 *ppv = &This->IAudioStreamVolume_iface;
2121 }else if(IsEqualIID(riid, &IID_IAudioSessionControl)){
2122 if(!This->session_wrapper){
2123 This->session_wrapper = AudioSessionWrapper_Create(This);
2124 if(!This->session_wrapper){
2125 LeaveCriticalSection(&This->lock);
2126 return E_OUTOFMEMORY;
2128 }else
2129 IAudioSessionControl2_AddRef(&This->session_wrapper->IAudioSessionControl2_iface);
2131 *ppv = &This->session_wrapper->IAudioSessionControl2_iface;
2132 }else if(IsEqualIID(riid, &IID_IChannelAudioVolume)){
2133 if(!This->session_wrapper){
2134 This->session_wrapper = AudioSessionWrapper_Create(This);
2135 if(!This->session_wrapper){
2136 LeaveCriticalSection(&This->lock);
2137 return E_OUTOFMEMORY;
2139 }else
2140 IChannelAudioVolume_AddRef(&This->session_wrapper->IChannelAudioVolume_iface);
2142 *ppv = &This->session_wrapper->IChannelAudioVolume_iface;
2143 }else if(IsEqualIID(riid, &IID_ISimpleAudioVolume)){
2144 if(!This->session_wrapper){
2145 This->session_wrapper = AudioSessionWrapper_Create(This);
2146 if(!This->session_wrapper){
2147 LeaveCriticalSection(&This->lock);
2148 return E_OUTOFMEMORY;
2150 }else
2151 ISimpleAudioVolume_AddRef(&This->session_wrapper->ISimpleAudioVolume_iface);
2153 *ppv = &This->session_wrapper->ISimpleAudioVolume_iface;
2156 if(*ppv){
2157 LeaveCriticalSection(&This->lock);
2158 return S_OK;
2161 LeaveCriticalSection(&This->lock);
2163 FIXME("stub %s\n", debugstr_guid(riid));
2164 return E_NOINTERFACE;
2167 static const IAudioClientVtbl AudioClient_Vtbl =
2169 AudioClient_QueryInterface,
2170 AudioClient_AddRef,
2171 AudioClient_Release,
2172 AudioClient_Initialize,
2173 AudioClient_GetBufferSize,
2174 AudioClient_GetStreamLatency,
2175 AudioClient_GetCurrentPadding,
2176 AudioClient_IsFormatSupported,
2177 AudioClient_GetMixFormat,
2178 AudioClient_GetDevicePeriod,
2179 AudioClient_Start,
2180 AudioClient_Stop,
2181 AudioClient_Reset,
2182 AudioClient_SetEventHandle,
2183 AudioClient_GetService
2186 static HRESULT WINAPI AudioRenderClient_QueryInterface(
2187 IAudioRenderClient *iface, REFIID riid, void **ppv)
2189 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2191 if(!ppv)
2192 return E_POINTER;
2193 *ppv = NULL;
2195 if(IsEqualIID(riid, &IID_IUnknown) ||
2196 IsEqualIID(riid, &IID_IAudioRenderClient))
2197 *ppv = iface;
2198 if(*ppv){
2199 IUnknown_AddRef((IUnknown*)*ppv);
2200 return S_OK;
2203 WARN("Unknown interface %s\n", debugstr_guid(riid));
2204 return E_NOINTERFACE;
2207 static ULONG WINAPI AudioRenderClient_AddRef(IAudioRenderClient *iface)
2209 ACImpl *This = impl_from_IAudioRenderClient(iface);
2210 return AudioClient_AddRef(&This->IAudioClient_iface);
2213 static ULONG WINAPI AudioRenderClient_Release(IAudioRenderClient *iface)
2215 ACImpl *This = impl_from_IAudioRenderClient(iface);
2216 return AudioClient_Release(&This->IAudioClient_iface);
2219 static HRESULT WINAPI AudioRenderClient_GetBuffer(IAudioRenderClient *iface,
2220 UINT32 frames, BYTE **data)
2222 ACImpl *This = impl_from_IAudioRenderClient(iface);
2223 UINT32 write_pos;
2225 TRACE("(%p)->(%u, %p)\n", This, frames, data);
2227 if(!data)
2228 return E_POINTER;
2229 *data = NULL;
2231 EnterCriticalSection(&This->lock);
2233 if(This->getbuf_last){
2234 LeaveCriticalSection(&This->lock);
2235 return AUDCLNT_E_OUT_OF_ORDER;
2238 if(!frames){
2239 LeaveCriticalSection(&This->lock);
2240 return S_OK;
2243 /* held_frames == GetCurrentPadding_nolock(); */
2244 if(This->held_frames + frames > This->bufsize_frames){
2245 LeaveCriticalSection(&This->lock);
2246 return AUDCLNT_E_BUFFER_TOO_LARGE;
2249 write_pos =
2250 (This->lcl_offs_frames + This->held_frames) % This->bufsize_frames;
2251 if(write_pos + frames > This->bufsize_frames){
2252 if(This->tmp_buffer_frames < frames){
2253 HeapFree(GetProcessHeap(), 0, This->tmp_buffer);
2254 This->tmp_buffer = HeapAlloc(GetProcessHeap(), 0,
2255 frames * This->fmt->nBlockAlign);
2256 if(!This->tmp_buffer){
2257 LeaveCriticalSection(&This->lock);
2258 return E_OUTOFMEMORY;
2260 This->tmp_buffer_frames = frames;
2262 *data = This->tmp_buffer;
2263 This->getbuf_last = -frames;
2264 }else{
2265 *data = This->local_buffer + write_pos * This->fmt->nBlockAlign;
2266 This->getbuf_last = frames;
2269 LeaveCriticalSection(&This->lock);
2271 return S_OK;
2274 static void alsa_wrap_buffer(ACImpl *This, BYTE *buffer, UINT32 written_frames)
2276 snd_pcm_uframes_t write_offs_frames =
2277 (This->lcl_offs_frames + This->held_frames) % This->bufsize_frames;
2278 UINT32 write_offs_bytes = write_offs_frames * This->fmt->nBlockAlign;
2279 snd_pcm_uframes_t chunk_frames = This->bufsize_frames - write_offs_frames;
2280 UINT32 chunk_bytes = chunk_frames * This->fmt->nBlockAlign;
2281 UINT32 written_bytes = written_frames * This->fmt->nBlockAlign;
2283 if(written_bytes <= chunk_bytes){
2284 memcpy(This->local_buffer + write_offs_bytes, buffer, written_bytes);
2285 }else{
2286 memcpy(This->local_buffer + write_offs_bytes, buffer, chunk_bytes);
2287 memcpy(This->local_buffer, buffer + chunk_bytes,
2288 written_bytes - chunk_bytes);
2292 static HRESULT WINAPI AudioRenderClient_ReleaseBuffer(
2293 IAudioRenderClient *iface, UINT32 written_frames, DWORD flags)
2295 ACImpl *This = impl_from_IAudioRenderClient(iface);
2296 BYTE *buffer;
2298 TRACE("(%p)->(%u, %x)\n", This, written_frames, flags);
2300 EnterCriticalSection(&This->lock);
2302 if(!written_frames){
2303 This->getbuf_last = 0;
2304 LeaveCriticalSection(&This->lock);
2305 return S_OK;
2308 if(!This->getbuf_last){
2309 LeaveCriticalSection(&This->lock);
2310 return AUDCLNT_E_OUT_OF_ORDER;
2313 if(written_frames > (This->getbuf_last >= 0 ? This->getbuf_last : -This->getbuf_last)){
2314 LeaveCriticalSection(&This->lock);
2315 return AUDCLNT_E_INVALID_SIZE;
2318 if(This->getbuf_last >= 0)
2319 buffer = This->local_buffer + This->fmt->nBlockAlign *
2320 ((This->lcl_offs_frames + This->held_frames) % This->bufsize_frames);
2321 else
2322 buffer = This->tmp_buffer;
2324 if(flags & AUDCLNT_BUFFERFLAGS_SILENT){
2325 if(This->fmt->wBitsPerSample == 8)
2326 memset(buffer, 128, written_frames * This->fmt->nBlockAlign);
2327 else
2328 memset(buffer, 0, written_frames * This->fmt->nBlockAlign);
2331 if(This->getbuf_last < 0)
2332 alsa_wrap_buffer(This, buffer, written_frames);
2334 This->held_frames += written_frames;
2335 This->written_frames += written_frames;
2336 This->getbuf_last = 0;
2338 LeaveCriticalSection(&This->lock);
2340 return S_OK;
2343 static const IAudioRenderClientVtbl AudioRenderClient_Vtbl = {
2344 AudioRenderClient_QueryInterface,
2345 AudioRenderClient_AddRef,
2346 AudioRenderClient_Release,
2347 AudioRenderClient_GetBuffer,
2348 AudioRenderClient_ReleaseBuffer
2351 static HRESULT WINAPI AudioCaptureClient_QueryInterface(
2352 IAudioCaptureClient *iface, REFIID riid, void **ppv)
2354 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2356 if(!ppv)
2357 return E_POINTER;
2358 *ppv = NULL;
2360 if(IsEqualIID(riid, &IID_IUnknown) ||
2361 IsEqualIID(riid, &IID_IAudioCaptureClient))
2362 *ppv = iface;
2363 if(*ppv){
2364 IUnknown_AddRef((IUnknown*)*ppv);
2365 return S_OK;
2368 WARN("Unknown interface %s\n", debugstr_guid(riid));
2369 return E_NOINTERFACE;
2372 static ULONG WINAPI AudioCaptureClient_AddRef(IAudioCaptureClient *iface)
2374 ACImpl *This = impl_from_IAudioCaptureClient(iface);
2375 return IAudioClient_AddRef(&This->IAudioClient_iface);
2378 static ULONG WINAPI AudioCaptureClient_Release(IAudioCaptureClient *iface)
2380 ACImpl *This = impl_from_IAudioCaptureClient(iface);
2381 return IAudioClient_Release(&This->IAudioClient_iface);
2384 static HRESULT WINAPI AudioCaptureClient_GetBuffer(IAudioCaptureClient *iface,
2385 BYTE **data, UINT32 *frames, DWORD *flags, UINT64 *devpos,
2386 UINT64 *qpcpos)
2388 ACImpl *This = impl_from_IAudioCaptureClient(iface);
2390 TRACE("(%p)->(%p, %p, %p, %p, %p)\n", This, data, frames, flags,
2391 devpos, qpcpos);
2393 if(!data || !frames || !flags)
2394 return E_POINTER;
2396 EnterCriticalSection(&This->lock);
2398 if(This->getbuf_last){
2399 LeaveCriticalSection(&This->lock);
2400 return AUDCLNT_E_OUT_OF_ORDER;
2403 /* hr = GetNextPacketSize(iface, frames); */
2404 if(This->held_frames < This->mmdev_period_frames){
2405 *frames = 0;
2406 LeaveCriticalSection(&This->lock);
2407 return AUDCLNT_S_BUFFER_EMPTY;
2409 *frames = This->mmdev_period_frames;
2411 if(This->lcl_offs_frames + *frames > This->bufsize_frames){
2412 UINT32 chunk_bytes, offs_bytes, frames_bytes;
2413 if(This->tmp_buffer_frames < *frames){
2414 HeapFree(GetProcessHeap(), 0, This->tmp_buffer);
2415 This->tmp_buffer = HeapAlloc(GetProcessHeap(), 0,
2416 *frames * This->fmt->nBlockAlign);
2417 if(!This->tmp_buffer){
2418 LeaveCriticalSection(&This->lock);
2419 return E_OUTOFMEMORY;
2421 This->tmp_buffer_frames = *frames;
2424 *data = This->tmp_buffer;
2425 chunk_bytes = (This->bufsize_frames - This->lcl_offs_frames) *
2426 This->fmt->nBlockAlign;
2427 offs_bytes = This->lcl_offs_frames * This->fmt->nBlockAlign;
2428 frames_bytes = *frames * This->fmt->nBlockAlign;
2429 memcpy(This->tmp_buffer, This->local_buffer + offs_bytes, chunk_bytes);
2430 memcpy(This->tmp_buffer + chunk_bytes, This->local_buffer,
2431 frames_bytes - chunk_bytes);
2432 }else
2433 *data = This->local_buffer +
2434 This->lcl_offs_frames * This->fmt->nBlockAlign;
2436 This->getbuf_last = *frames;
2437 *flags = 0;
2439 if(devpos)
2440 *devpos = This->written_frames;
2441 if(qpcpos){ /* fixme: qpc of recording time */
2442 LARGE_INTEGER stamp, freq;
2443 QueryPerformanceCounter(&stamp);
2444 QueryPerformanceFrequency(&freq);
2445 *qpcpos = (stamp.QuadPart * (INT64)10000000) / freq.QuadPart;
2448 LeaveCriticalSection(&This->lock);
2450 return *frames ? S_OK : AUDCLNT_S_BUFFER_EMPTY;
2453 static HRESULT WINAPI AudioCaptureClient_ReleaseBuffer(
2454 IAudioCaptureClient *iface, UINT32 done)
2456 ACImpl *This = impl_from_IAudioCaptureClient(iface);
2458 TRACE("(%p)->(%u)\n", This, done);
2460 EnterCriticalSection(&This->lock);
2462 if(!done){
2463 This->getbuf_last = 0;
2464 LeaveCriticalSection(&This->lock);
2465 return S_OK;
2468 if(!This->getbuf_last){
2469 LeaveCriticalSection(&This->lock);
2470 return AUDCLNT_E_OUT_OF_ORDER;
2473 if(This->getbuf_last != done){
2474 LeaveCriticalSection(&This->lock);
2475 return AUDCLNT_E_INVALID_SIZE;
2478 This->written_frames += done;
2479 This->held_frames -= done;
2480 This->lcl_offs_frames += done;
2481 This->lcl_offs_frames %= This->bufsize_frames;
2482 This->getbuf_last = 0;
2484 LeaveCriticalSection(&This->lock);
2486 return S_OK;
2489 static HRESULT WINAPI AudioCaptureClient_GetNextPacketSize(
2490 IAudioCaptureClient *iface, UINT32 *frames)
2492 ACImpl *This = impl_from_IAudioCaptureClient(iface);
2494 TRACE("(%p)->(%p)\n", This, frames);
2496 if(!frames)
2497 return E_POINTER;
2499 EnterCriticalSection(&This->lock);
2501 *frames = This->held_frames < This->mmdev_period_frames ? 0 : This->mmdev_period_frames;
2503 LeaveCriticalSection(&This->lock);
2505 return S_OK;
2508 static const IAudioCaptureClientVtbl AudioCaptureClient_Vtbl =
2510 AudioCaptureClient_QueryInterface,
2511 AudioCaptureClient_AddRef,
2512 AudioCaptureClient_Release,
2513 AudioCaptureClient_GetBuffer,
2514 AudioCaptureClient_ReleaseBuffer,
2515 AudioCaptureClient_GetNextPacketSize
2518 static HRESULT WINAPI AudioClock_QueryInterface(IAudioClock *iface,
2519 REFIID riid, void **ppv)
2521 ACImpl *This = impl_from_IAudioClock(iface);
2523 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2525 if(!ppv)
2526 return E_POINTER;
2527 *ppv = NULL;
2529 if(IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IAudioClock))
2530 *ppv = iface;
2531 else if(IsEqualIID(riid, &IID_IAudioClock2))
2532 *ppv = &This->IAudioClock2_iface;
2533 if(*ppv){
2534 IUnknown_AddRef((IUnknown*)*ppv);
2535 return S_OK;
2538 WARN("Unknown interface %s\n", debugstr_guid(riid));
2539 return E_NOINTERFACE;
2542 static ULONG WINAPI AudioClock_AddRef(IAudioClock *iface)
2544 ACImpl *This = impl_from_IAudioClock(iface);
2545 return IAudioClient_AddRef(&This->IAudioClient_iface);
2548 static ULONG WINAPI AudioClock_Release(IAudioClock *iface)
2550 ACImpl *This = impl_from_IAudioClock(iface);
2551 return IAudioClient_Release(&This->IAudioClient_iface);
2554 static HRESULT WINAPI AudioClock_GetFrequency(IAudioClock *iface, UINT64 *freq)
2556 ACImpl *This = impl_from_IAudioClock(iface);
2558 TRACE("(%p)->(%p)\n", This, freq);
2560 *freq = This->fmt->nSamplesPerSec;
2562 return S_OK;
2565 static HRESULT WINAPI AudioClock_GetPosition(IAudioClock *iface, UINT64 *pos,
2566 UINT64 *qpctime)
2568 ACImpl *This = impl_from_IAudioClock(iface);
2569 UINT64 written_frames, position;
2570 UINT32 held_frames;
2571 int err;
2572 snd_pcm_state_t alsa_state;
2573 snd_pcm_uframes_t avail_frames;
2574 snd_pcm_sframes_t delay_frames;
2576 TRACE("(%p)->(%p, %p)\n", This, pos, qpctime);
2578 if(!pos)
2579 return E_POINTER;
2581 EnterCriticalSection(&This->lock);
2583 /* call required to get accurate snd_pcm_state() */
2584 avail_frames = snd_pcm_avail_update(This->pcm_handle);
2585 alsa_state = snd_pcm_state(This->pcm_handle);
2586 written_frames = This->written_frames;
2587 held_frames = This->held_frames;
2589 err = snd_pcm_delay(This->pcm_handle, &delay_frames);
2590 if(err < 0){
2591 /* old Pulse, shortly after start */
2592 WARN("snd_pcm_delay failed in state %u: %d (%s)\n", alsa_state, err, snd_strerror(err));
2595 if(This->dataflow == eRender){
2596 position = written_frames - held_frames; /* maximum */
2597 if(!This->started || alsa_state > SND_PCM_STATE_RUNNING)
2598 ; /* mmdevapi stopped or ALSA underrun: pretend everything was played */
2599 else if(err<0 || delay_frames > position - This->last_pos_frames)
2600 /* Pulse bug: past underrun, despite recovery, avail_frames & delay
2601 * may be larger than alsa_bufsize_frames, as if cumulating frames. */
2602 /* Pulse bug: EIO(-5) shortly after starting: nothing played */
2603 position = This->last_pos_frames;
2604 else if(delay_frames > 0)
2605 position -= delay_frames;
2606 }else
2607 position = written_frames + held_frames;
2609 /* ensure monotic growth */
2610 This->last_pos_frames = position;
2612 LeaveCriticalSection(&This->lock);
2614 TRACE("frames written: %u, held: %u, avail: %ld, delay: %ld state %d, pos: %u\n",
2615 (UINT32)(written_frames%1000000000), held_frames,
2616 avail_frames, delay_frames, alsa_state, (UINT32)(position%1000000000));
2617 *pos = position;
2619 if(qpctime){
2620 LARGE_INTEGER stamp, freq;
2621 QueryPerformanceCounter(&stamp);
2622 QueryPerformanceFrequency(&freq);
2623 *qpctime = (stamp.QuadPart * (INT64)10000000) / freq.QuadPart;
2626 return S_OK;
2629 static HRESULT WINAPI AudioClock_GetCharacteristics(IAudioClock *iface,
2630 DWORD *chars)
2632 ACImpl *This = impl_from_IAudioClock(iface);
2634 TRACE("(%p)->(%p)\n", This, chars);
2636 if(!chars)
2637 return E_POINTER;
2639 *chars = AUDIOCLOCK_CHARACTERISTIC_FIXED_FREQ;
2641 return S_OK;
2644 static const IAudioClockVtbl AudioClock_Vtbl =
2646 AudioClock_QueryInterface,
2647 AudioClock_AddRef,
2648 AudioClock_Release,
2649 AudioClock_GetFrequency,
2650 AudioClock_GetPosition,
2651 AudioClock_GetCharacteristics
2654 static HRESULT WINAPI AudioClock2_QueryInterface(IAudioClock2 *iface,
2655 REFIID riid, void **ppv)
2657 ACImpl *This = impl_from_IAudioClock2(iface);
2658 return IAudioClock_QueryInterface(&This->IAudioClock_iface, riid, ppv);
2661 static ULONG WINAPI AudioClock2_AddRef(IAudioClock2 *iface)
2663 ACImpl *This = impl_from_IAudioClock2(iface);
2664 return IAudioClient_AddRef(&This->IAudioClient_iface);
2667 static ULONG WINAPI AudioClock2_Release(IAudioClock2 *iface)
2669 ACImpl *This = impl_from_IAudioClock2(iface);
2670 return IAudioClient_Release(&This->IAudioClient_iface);
2673 static HRESULT WINAPI AudioClock2_GetDevicePosition(IAudioClock2 *iface,
2674 UINT64 *pos, UINT64 *qpctime)
2676 ACImpl *This = impl_from_IAudioClock2(iface);
2678 FIXME("(%p)->(%p, %p)\n", This, pos, qpctime);
2680 return E_NOTIMPL;
2683 static const IAudioClock2Vtbl AudioClock2_Vtbl =
2685 AudioClock2_QueryInterface,
2686 AudioClock2_AddRef,
2687 AudioClock2_Release,
2688 AudioClock2_GetDevicePosition
2691 static AudioSessionWrapper *AudioSessionWrapper_Create(ACImpl *client)
2693 AudioSessionWrapper *ret;
2695 ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
2696 sizeof(AudioSessionWrapper));
2697 if(!ret)
2698 return NULL;
2700 ret->IAudioSessionControl2_iface.lpVtbl = &AudioSessionControl2_Vtbl;
2701 ret->ISimpleAudioVolume_iface.lpVtbl = &SimpleAudioVolume_Vtbl;
2702 ret->IChannelAudioVolume_iface.lpVtbl = &ChannelAudioVolume_Vtbl;
2704 ret->ref = 1;
2706 ret->client = client;
2707 if(client){
2708 ret->session = client->session;
2709 AudioClient_AddRef(&client->IAudioClient_iface);
2712 return ret;
2715 static HRESULT WINAPI AudioSessionControl_QueryInterface(
2716 IAudioSessionControl2 *iface, REFIID riid, void **ppv)
2718 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2720 if(!ppv)
2721 return E_POINTER;
2722 *ppv = NULL;
2724 if(IsEqualIID(riid, &IID_IUnknown) ||
2725 IsEqualIID(riid, &IID_IAudioSessionControl) ||
2726 IsEqualIID(riid, &IID_IAudioSessionControl2))
2727 *ppv = iface;
2728 if(*ppv){
2729 IUnknown_AddRef((IUnknown*)*ppv);
2730 return S_OK;
2733 WARN("Unknown interface %s\n", debugstr_guid(riid));
2734 return E_NOINTERFACE;
2737 static ULONG WINAPI AudioSessionControl_AddRef(IAudioSessionControl2 *iface)
2739 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2740 ULONG ref;
2741 ref = InterlockedIncrement(&This->ref);
2742 TRACE("(%p) Refcount now %u\n", This, ref);
2743 return ref;
2746 static ULONG WINAPI AudioSessionControl_Release(IAudioSessionControl2 *iface)
2748 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2749 ULONG ref;
2750 ref = InterlockedDecrement(&This->ref);
2751 TRACE("(%p) Refcount now %u\n", This, ref);
2752 if(!ref){
2753 if(This->client){
2754 EnterCriticalSection(&This->client->lock);
2755 This->client->session_wrapper = NULL;
2756 LeaveCriticalSection(&This->client->lock);
2757 AudioClient_Release(&This->client->IAudioClient_iface);
2759 HeapFree(GetProcessHeap(), 0, This);
2761 return ref;
2764 static HRESULT WINAPI AudioSessionControl_GetState(IAudioSessionControl2 *iface,
2765 AudioSessionState *state)
2767 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2768 ACImpl *client;
2770 TRACE("(%p)->(%p)\n", This, state);
2772 if(!state)
2773 return NULL_PTR_ERR;
2775 EnterCriticalSection(&g_sessions_lock);
2777 if(list_empty(&This->session->clients)){
2778 *state = AudioSessionStateExpired;
2779 LeaveCriticalSection(&g_sessions_lock);
2780 return S_OK;
2783 LIST_FOR_EACH_ENTRY(client, &This->session->clients, ACImpl, entry){
2784 EnterCriticalSection(&client->lock);
2785 if(client->started){
2786 *state = AudioSessionStateActive;
2787 LeaveCriticalSection(&client->lock);
2788 LeaveCriticalSection(&g_sessions_lock);
2789 return S_OK;
2791 LeaveCriticalSection(&client->lock);
2794 LeaveCriticalSection(&g_sessions_lock);
2796 *state = AudioSessionStateInactive;
2798 return S_OK;
2801 static HRESULT WINAPI AudioSessionControl_GetDisplayName(
2802 IAudioSessionControl2 *iface, WCHAR **name)
2804 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2806 FIXME("(%p)->(%p) - stub\n", This, name);
2808 return E_NOTIMPL;
2811 static HRESULT WINAPI AudioSessionControl_SetDisplayName(
2812 IAudioSessionControl2 *iface, const WCHAR *name, const GUID *session)
2814 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2816 FIXME("(%p)->(%p, %s) - stub\n", This, name, debugstr_guid(session));
2818 return E_NOTIMPL;
2821 static HRESULT WINAPI AudioSessionControl_GetIconPath(
2822 IAudioSessionControl2 *iface, WCHAR **path)
2824 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2826 FIXME("(%p)->(%p) - stub\n", This, path);
2828 return E_NOTIMPL;
2831 static HRESULT WINAPI AudioSessionControl_SetIconPath(
2832 IAudioSessionControl2 *iface, const WCHAR *path, const GUID *session)
2834 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2836 FIXME("(%p)->(%p, %s) - stub\n", This, path, debugstr_guid(session));
2838 return E_NOTIMPL;
2841 static HRESULT WINAPI AudioSessionControl_GetGroupingParam(
2842 IAudioSessionControl2 *iface, GUID *group)
2844 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2846 FIXME("(%p)->(%p) - stub\n", This, group);
2848 return E_NOTIMPL;
2851 static HRESULT WINAPI AudioSessionControl_SetGroupingParam(
2852 IAudioSessionControl2 *iface, const GUID *group, const GUID *session)
2854 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2856 FIXME("(%p)->(%s, %s) - stub\n", This, debugstr_guid(group),
2857 debugstr_guid(session));
2859 return E_NOTIMPL;
2862 static HRESULT WINAPI AudioSessionControl_RegisterAudioSessionNotification(
2863 IAudioSessionControl2 *iface, IAudioSessionEvents *events)
2865 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2867 FIXME("(%p)->(%p) - stub\n", This, events);
2869 return S_OK;
2872 static HRESULT WINAPI AudioSessionControl_UnregisterAudioSessionNotification(
2873 IAudioSessionControl2 *iface, IAudioSessionEvents *events)
2875 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2877 FIXME("(%p)->(%p) - stub\n", This, events);
2879 return S_OK;
2882 static HRESULT WINAPI AudioSessionControl_GetSessionIdentifier(
2883 IAudioSessionControl2 *iface, WCHAR **id)
2885 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2887 FIXME("(%p)->(%p) - stub\n", This, id);
2889 return E_NOTIMPL;
2892 static HRESULT WINAPI AudioSessionControl_GetSessionInstanceIdentifier(
2893 IAudioSessionControl2 *iface, WCHAR **id)
2895 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2897 FIXME("(%p)->(%p) - stub\n", This, id);
2899 return E_NOTIMPL;
2902 static HRESULT WINAPI AudioSessionControl_GetProcessId(
2903 IAudioSessionControl2 *iface, DWORD *pid)
2905 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2907 TRACE("(%p)->(%p)\n", This, pid);
2909 if(!pid)
2910 return E_POINTER;
2912 *pid = GetCurrentProcessId();
2914 return S_OK;
2917 static HRESULT WINAPI AudioSessionControl_IsSystemSoundsSession(
2918 IAudioSessionControl2 *iface)
2920 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2922 TRACE("(%p)\n", This);
2924 return S_FALSE;
2927 static HRESULT WINAPI AudioSessionControl_SetDuckingPreference(
2928 IAudioSessionControl2 *iface, BOOL optout)
2930 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2932 TRACE("(%p)->(%d)\n", This, optout);
2934 return S_OK;
2937 static const IAudioSessionControl2Vtbl AudioSessionControl2_Vtbl =
2939 AudioSessionControl_QueryInterface,
2940 AudioSessionControl_AddRef,
2941 AudioSessionControl_Release,
2942 AudioSessionControl_GetState,
2943 AudioSessionControl_GetDisplayName,
2944 AudioSessionControl_SetDisplayName,
2945 AudioSessionControl_GetIconPath,
2946 AudioSessionControl_SetIconPath,
2947 AudioSessionControl_GetGroupingParam,
2948 AudioSessionControl_SetGroupingParam,
2949 AudioSessionControl_RegisterAudioSessionNotification,
2950 AudioSessionControl_UnregisterAudioSessionNotification,
2951 AudioSessionControl_GetSessionIdentifier,
2952 AudioSessionControl_GetSessionInstanceIdentifier,
2953 AudioSessionControl_GetProcessId,
2954 AudioSessionControl_IsSystemSoundsSession,
2955 AudioSessionControl_SetDuckingPreference
2958 static HRESULT WINAPI SimpleAudioVolume_QueryInterface(
2959 ISimpleAudioVolume *iface, REFIID riid, void **ppv)
2961 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2963 if(!ppv)
2964 return E_POINTER;
2965 *ppv = NULL;
2967 if(IsEqualIID(riid, &IID_IUnknown) ||
2968 IsEqualIID(riid, &IID_ISimpleAudioVolume))
2969 *ppv = iface;
2970 if(*ppv){
2971 IUnknown_AddRef((IUnknown*)*ppv);
2972 return S_OK;
2975 WARN("Unknown interface %s\n", debugstr_guid(riid));
2976 return E_NOINTERFACE;
2979 static ULONG WINAPI SimpleAudioVolume_AddRef(ISimpleAudioVolume *iface)
2981 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2982 return AudioSessionControl_AddRef(&This->IAudioSessionControl2_iface);
2985 static ULONG WINAPI SimpleAudioVolume_Release(ISimpleAudioVolume *iface)
2987 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2988 return AudioSessionControl_Release(&This->IAudioSessionControl2_iface);
2991 static HRESULT WINAPI SimpleAudioVolume_SetMasterVolume(
2992 ISimpleAudioVolume *iface, float level, const GUID *context)
2994 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2995 AudioSession *session = This->session;
2997 TRACE("(%p)->(%f, %s)\n", session, level, wine_dbgstr_guid(context));
2999 if(level < 0.f || level > 1.f)
3000 return E_INVALIDARG;
3002 if(context)
3003 FIXME("Notifications not supported yet\n");
3005 TRACE("ALSA does not support volume control\n");
3007 EnterCriticalSection(&session->lock);
3009 session->master_vol = level;
3011 LeaveCriticalSection(&session->lock);
3013 return S_OK;
3016 static HRESULT WINAPI SimpleAudioVolume_GetMasterVolume(
3017 ISimpleAudioVolume *iface, float *level)
3019 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
3020 AudioSession *session = This->session;
3022 TRACE("(%p)->(%p)\n", session, level);
3024 if(!level)
3025 return NULL_PTR_ERR;
3027 *level = session->master_vol;
3029 return S_OK;
3032 static HRESULT WINAPI SimpleAudioVolume_SetMute(ISimpleAudioVolume *iface,
3033 BOOL mute, const GUID *context)
3035 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
3036 AudioSession *session = This->session;
3038 TRACE("(%p)->(%u, %p)\n", session, mute, context);
3040 if(context)
3041 FIXME("Notifications not supported yet\n");
3043 session->mute = mute;
3045 return S_OK;
3048 static HRESULT WINAPI SimpleAudioVolume_GetMute(ISimpleAudioVolume *iface,
3049 BOOL *mute)
3051 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
3052 AudioSession *session = This->session;
3054 TRACE("(%p)->(%p)\n", session, mute);
3056 if(!mute)
3057 return NULL_PTR_ERR;
3059 *mute = session->mute;
3061 return S_OK;
3064 static const ISimpleAudioVolumeVtbl SimpleAudioVolume_Vtbl =
3066 SimpleAudioVolume_QueryInterface,
3067 SimpleAudioVolume_AddRef,
3068 SimpleAudioVolume_Release,
3069 SimpleAudioVolume_SetMasterVolume,
3070 SimpleAudioVolume_GetMasterVolume,
3071 SimpleAudioVolume_SetMute,
3072 SimpleAudioVolume_GetMute
3075 static HRESULT WINAPI AudioStreamVolume_QueryInterface(
3076 IAudioStreamVolume *iface, REFIID riid, void **ppv)
3078 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
3080 if(!ppv)
3081 return E_POINTER;
3082 *ppv = NULL;
3084 if(IsEqualIID(riid, &IID_IUnknown) ||
3085 IsEqualIID(riid, &IID_IAudioStreamVolume))
3086 *ppv = iface;
3087 if(*ppv){
3088 IUnknown_AddRef((IUnknown*)*ppv);
3089 return S_OK;
3092 WARN("Unknown interface %s\n", debugstr_guid(riid));
3093 return E_NOINTERFACE;
3096 static ULONG WINAPI AudioStreamVolume_AddRef(IAudioStreamVolume *iface)
3098 ACImpl *This = impl_from_IAudioStreamVolume(iface);
3099 return IAudioClient_AddRef(&This->IAudioClient_iface);
3102 static ULONG WINAPI AudioStreamVolume_Release(IAudioStreamVolume *iface)
3104 ACImpl *This = impl_from_IAudioStreamVolume(iface);
3105 return IAudioClient_Release(&This->IAudioClient_iface);
3108 static HRESULT WINAPI AudioStreamVolume_GetChannelCount(
3109 IAudioStreamVolume *iface, UINT32 *out)
3111 ACImpl *This = impl_from_IAudioStreamVolume(iface);
3113 TRACE("(%p)->(%p)\n", This, out);
3115 if(!out)
3116 return E_POINTER;
3118 *out = This->fmt->nChannels;
3120 return S_OK;
3123 static HRESULT WINAPI AudioStreamVolume_SetChannelVolume(
3124 IAudioStreamVolume *iface, UINT32 index, float level)
3126 ACImpl *This = impl_from_IAudioStreamVolume(iface);
3128 TRACE("(%p)->(%d, %f)\n", This, index, level);
3130 if(level < 0.f || level > 1.f)
3131 return E_INVALIDARG;
3133 if(index >= This->fmt->nChannels)
3134 return E_INVALIDARG;
3136 TRACE("ALSA does not support volume control\n");
3138 EnterCriticalSection(&This->lock);
3140 This->vols[index] = level;
3142 LeaveCriticalSection(&This->lock);
3144 return S_OK;
3147 static HRESULT WINAPI AudioStreamVolume_GetChannelVolume(
3148 IAudioStreamVolume *iface, UINT32 index, float *level)
3150 ACImpl *This = impl_from_IAudioStreamVolume(iface);
3152 TRACE("(%p)->(%d, %p)\n", This, index, level);
3154 if(!level)
3155 return E_POINTER;
3157 if(index >= This->fmt->nChannels)
3158 return E_INVALIDARG;
3160 *level = This->vols[index];
3162 return S_OK;
3165 static HRESULT WINAPI AudioStreamVolume_SetAllVolumes(
3166 IAudioStreamVolume *iface, UINT32 count, const float *levels)
3168 ACImpl *This = impl_from_IAudioStreamVolume(iface);
3169 int i;
3171 TRACE("(%p)->(%d, %p)\n", This, count, levels);
3173 if(!levels)
3174 return E_POINTER;
3176 if(count != This->fmt->nChannels)
3177 return E_INVALIDARG;
3179 TRACE("ALSA does not support volume control\n");
3181 EnterCriticalSection(&This->lock);
3183 for(i = 0; i < count; ++i)
3184 This->vols[i] = levels[i];
3186 LeaveCriticalSection(&This->lock);
3188 return S_OK;
3191 static HRESULT WINAPI AudioStreamVolume_GetAllVolumes(
3192 IAudioStreamVolume *iface, UINT32 count, float *levels)
3194 ACImpl *This = impl_from_IAudioStreamVolume(iface);
3195 int i;
3197 TRACE("(%p)->(%d, %p)\n", This, count, levels);
3199 if(!levels)
3200 return E_POINTER;
3202 if(count != This->fmt->nChannels)
3203 return E_INVALIDARG;
3205 EnterCriticalSection(&This->lock);
3207 for(i = 0; i < count; ++i)
3208 levels[i] = This->vols[i];
3210 LeaveCriticalSection(&This->lock);
3212 return S_OK;
3215 static const IAudioStreamVolumeVtbl AudioStreamVolume_Vtbl =
3217 AudioStreamVolume_QueryInterface,
3218 AudioStreamVolume_AddRef,
3219 AudioStreamVolume_Release,
3220 AudioStreamVolume_GetChannelCount,
3221 AudioStreamVolume_SetChannelVolume,
3222 AudioStreamVolume_GetChannelVolume,
3223 AudioStreamVolume_SetAllVolumes,
3224 AudioStreamVolume_GetAllVolumes
3227 static HRESULT WINAPI ChannelAudioVolume_QueryInterface(
3228 IChannelAudioVolume *iface, REFIID riid, void **ppv)
3230 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
3232 if(!ppv)
3233 return E_POINTER;
3234 *ppv = NULL;
3236 if(IsEqualIID(riid, &IID_IUnknown) ||
3237 IsEqualIID(riid, &IID_IChannelAudioVolume))
3238 *ppv = iface;
3239 if(*ppv){
3240 IUnknown_AddRef((IUnknown*)*ppv);
3241 return S_OK;
3244 WARN("Unknown interface %s\n", debugstr_guid(riid));
3245 return E_NOINTERFACE;
3248 static ULONG WINAPI ChannelAudioVolume_AddRef(IChannelAudioVolume *iface)
3250 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
3251 return AudioSessionControl_AddRef(&This->IAudioSessionControl2_iface);
3254 static ULONG WINAPI ChannelAudioVolume_Release(IChannelAudioVolume *iface)
3256 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
3257 return AudioSessionControl_Release(&This->IAudioSessionControl2_iface);
3260 static HRESULT WINAPI ChannelAudioVolume_GetChannelCount(
3261 IChannelAudioVolume *iface, UINT32 *out)
3263 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
3264 AudioSession *session = This->session;
3266 TRACE("(%p)->(%p)\n", session, out);
3268 if(!out)
3269 return NULL_PTR_ERR;
3271 *out = session->channel_count;
3273 return S_OK;
3276 static HRESULT WINAPI ChannelAudioVolume_SetChannelVolume(
3277 IChannelAudioVolume *iface, UINT32 index, float level,
3278 const GUID *context)
3280 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
3281 AudioSession *session = This->session;
3283 TRACE("(%p)->(%d, %f, %s)\n", session, index, level,
3284 wine_dbgstr_guid(context));
3286 if(level < 0.f || level > 1.f)
3287 return E_INVALIDARG;
3289 if(index >= session->channel_count)
3290 return E_INVALIDARG;
3292 if(context)
3293 FIXME("Notifications not supported yet\n");
3295 TRACE("ALSA does not support volume control\n");
3297 EnterCriticalSection(&session->lock);
3299 session->channel_vols[index] = level;
3301 LeaveCriticalSection(&session->lock);
3303 return S_OK;
3306 static HRESULT WINAPI ChannelAudioVolume_GetChannelVolume(
3307 IChannelAudioVolume *iface, UINT32 index, float *level)
3309 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
3310 AudioSession *session = This->session;
3312 TRACE("(%p)->(%d, %p)\n", session, index, level);
3314 if(!level)
3315 return NULL_PTR_ERR;
3317 if(index >= session->channel_count)
3318 return E_INVALIDARG;
3320 *level = session->channel_vols[index];
3322 return S_OK;
3325 static HRESULT WINAPI ChannelAudioVolume_SetAllVolumes(
3326 IChannelAudioVolume *iface, UINT32 count, const float *levels,
3327 const GUID *context)
3329 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
3330 AudioSession *session = This->session;
3331 int i;
3333 TRACE("(%p)->(%d, %p, %s)\n", session, count, levels,
3334 wine_dbgstr_guid(context));
3336 if(!levels)
3337 return NULL_PTR_ERR;
3339 if(count != session->channel_count)
3340 return E_INVALIDARG;
3342 if(context)
3343 FIXME("Notifications not supported yet\n");
3345 TRACE("ALSA does not support volume control\n");
3347 EnterCriticalSection(&session->lock);
3349 for(i = 0; i < count; ++i)
3350 session->channel_vols[i] = levels[i];
3352 LeaveCriticalSection(&session->lock);
3354 return S_OK;
3357 static HRESULT WINAPI ChannelAudioVolume_GetAllVolumes(
3358 IChannelAudioVolume *iface, UINT32 count, float *levels)
3360 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
3361 AudioSession *session = This->session;
3362 int i;
3364 TRACE("(%p)->(%d, %p)\n", session, count, levels);
3366 if(!levels)
3367 return NULL_PTR_ERR;
3369 if(count != session->channel_count)
3370 return E_INVALIDARG;
3372 for(i = 0; i < count; ++i)
3373 levels[i] = session->channel_vols[i];
3375 return S_OK;
3378 static const IChannelAudioVolumeVtbl ChannelAudioVolume_Vtbl =
3380 ChannelAudioVolume_QueryInterface,
3381 ChannelAudioVolume_AddRef,
3382 ChannelAudioVolume_Release,
3383 ChannelAudioVolume_GetChannelCount,
3384 ChannelAudioVolume_SetChannelVolume,
3385 ChannelAudioVolume_GetChannelVolume,
3386 ChannelAudioVolume_SetAllVolumes,
3387 ChannelAudioVolume_GetAllVolumes
3390 static HRESULT WINAPI AudioSessionManager_QueryInterface(IAudioSessionManager2 *iface,
3391 REFIID riid, void **ppv)
3393 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
3395 if(!ppv)
3396 return E_POINTER;
3397 *ppv = NULL;
3399 if(IsEqualIID(riid, &IID_IUnknown) ||
3400 IsEqualIID(riid, &IID_IAudioSessionManager) ||
3401 IsEqualIID(riid, &IID_IAudioSessionManager2))
3402 *ppv = iface;
3403 if(*ppv){
3404 IUnknown_AddRef((IUnknown*)*ppv);
3405 return S_OK;
3408 WARN("Unknown interface %s\n", debugstr_guid(riid));
3409 return E_NOINTERFACE;
3412 static ULONG WINAPI AudioSessionManager_AddRef(IAudioSessionManager2 *iface)
3414 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3415 ULONG ref;
3416 ref = InterlockedIncrement(&This->ref);
3417 TRACE("(%p) Refcount now %u\n", This, ref);
3418 return ref;
3421 static ULONG WINAPI AudioSessionManager_Release(IAudioSessionManager2 *iface)
3423 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3424 ULONG ref;
3425 ref = InterlockedDecrement(&This->ref);
3426 TRACE("(%p) Refcount now %u\n", This, ref);
3427 if(!ref)
3428 HeapFree(GetProcessHeap(), 0, This);
3429 return ref;
3432 static HRESULT WINAPI AudioSessionManager_GetAudioSessionControl(
3433 IAudioSessionManager2 *iface, const GUID *session_guid, DWORD flags,
3434 IAudioSessionControl **out)
3436 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3437 AudioSession *session;
3438 AudioSessionWrapper *wrapper;
3439 HRESULT hr;
3441 TRACE("(%p)->(%s, %x, %p)\n", This, debugstr_guid(session_guid),
3442 flags, out);
3444 hr = get_audio_session(session_guid, This->device, 0, &session);
3445 if(FAILED(hr))
3446 return hr;
3448 wrapper = AudioSessionWrapper_Create(NULL);
3449 if(!wrapper)
3450 return E_OUTOFMEMORY;
3452 wrapper->session = session;
3454 *out = (IAudioSessionControl*)&wrapper->IAudioSessionControl2_iface;
3456 return S_OK;
3459 static HRESULT WINAPI AudioSessionManager_GetSimpleAudioVolume(
3460 IAudioSessionManager2 *iface, const GUID *session_guid, DWORD flags,
3461 ISimpleAudioVolume **out)
3463 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3464 AudioSession *session;
3465 AudioSessionWrapper *wrapper;
3466 HRESULT hr;
3468 TRACE("(%p)->(%s, %x, %p)\n", This, debugstr_guid(session_guid),
3469 flags, out);
3471 hr = get_audio_session(session_guid, This->device, 0, &session);
3472 if(FAILED(hr))
3473 return hr;
3475 wrapper = AudioSessionWrapper_Create(NULL);
3476 if(!wrapper)
3477 return E_OUTOFMEMORY;
3479 wrapper->session = session;
3481 *out = &wrapper->ISimpleAudioVolume_iface;
3483 return S_OK;
3486 static HRESULT WINAPI AudioSessionManager_GetSessionEnumerator(
3487 IAudioSessionManager2 *iface, IAudioSessionEnumerator **out)
3489 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3490 FIXME("(%p)->(%p) - stub\n", This, out);
3491 return E_NOTIMPL;
3494 static HRESULT WINAPI AudioSessionManager_RegisterSessionNotification(
3495 IAudioSessionManager2 *iface, IAudioSessionNotification *notification)
3497 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3498 FIXME("(%p)->(%p) - stub\n", This, notification);
3499 return E_NOTIMPL;
3502 static HRESULT WINAPI AudioSessionManager_UnregisterSessionNotification(
3503 IAudioSessionManager2 *iface, IAudioSessionNotification *notification)
3505 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3506 FIXME("(%p)->(%p) - stub\n", This, notification);
3507 return E_NOTIMPL;
3510 static HRESULT WINAPI AudioSessionManager_RegisterDuckNotification(
3511 IAudioSessionManager2 *iface, const WCHAR *session_id,
3512 IAudioVolumeDuckNotification *notification)
3514 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3515 FIXME("(%p)->(%p) - stub\n", This, notification);
3516 return E_NOTIMPL;
3519 static HRESULT WINAPI AudioSessionManager_UnregisterDuckNotification(
3520 IAudioSessionManager2 *iface,
3521 IAudioVolumeDuckNotification *notification)
3523 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3524 FIXME("(%p)->(%p) - stub\n", This, notification);
3525 return E_NOTIMPL;
3528 static const IAudioSessionManager2Vtbl AudioSessionManager2_Vtbl =
3530 AudioSessionManager_QueryInterface,
3531 AudioSessionManager_AddRef,
3532 AudioSessionManager_Release,
3533 AudioSessionManager_GetAudioSessionControl,
3534 AudioSessionManager_GetSimpleAudioVolume,
3535 AudioSessionManager_GetSessionEnumerator,
3536 AudioSessionManager_RegisterSessionNotification,
3537 AudioSessionManager_UnregisterSessionNotification,
3538 AudioSessionManager_RegisterDuckNotification,
3539 AudioSessionManager_UnregisterDuckNotification
3542 HRESULT WINAPI AUDDRV_GetAudioSessionManager(IMMDevice *device,
3543 IAudioSessionManager2 **out)
3545 SessionMgr *This;
3547 This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(SessionMgr));
3548 if(!This)
3549 return E_OUTOFMEMORY;
3551 This->IAudioSessionManager2_iface.lpVtbl = &AudioSessionManager2_Vtbl;
3552 This->device = device;
3553 This->ref = 1;
3555 *out = &This->IAudioSessionManager2_iface;
3557 return S_OK;