cmd: Space does not delimit environment variable names.
[wine.git] / dlls / winealsa.drv / mmdevdrv.c
blob6b2bed5b67357b3f5fcdd5d96e73d6f1399475ae
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 need_remapping;
112 int alsa_channels;
113 int alsa_channel_map[32];
115 BOOL initted, started;
116 REFERENCE_TIME mmdev_period_rt;
117 UINT64 written_frames, last_pos_frames;
118 UINT32 bufsize_frames, held_frames, tmp_buffer_frames, mmdev_period_frames;
119 snd_pcm_uframes_t remapping_buf_frames;
120 UINT32 lcl_offs_frames; /* offs into local_buffer where valid data starts */
121 UINT32 hidden_frames; /* ALSA reserve to ensure continuous rendering */
123 HANDLE timer;
124 BYTE *local_buffer, *tmp_buffer, *remapping_buf;
125 LONG32 getbuf_last; /* <0 when using tmp_buffer */
127 CRITICAL_SECTION lock;
129 AudioSession *session;
130 AudioSessionWrapper *session_wrapper;
132 struct list entry;
135 typedef struct _SessionMgr {
136 IAudioSessionManager2 IAudioSessionManager2_iface;
138 LONG ref;
140 IMMDevice *device;
141 } SessionMgr;
143 static HANDLE g_timer_q;
145 static CRITICAL_SECTION g_sessions_lock;
146 static CRITICAL_SECTION_DEBUG g_sessions_lock_debug =
148 0, 0, &g_sessions_lock,
149 { &g_sessions_lock_debug.ProcessLocksList, &g_sessions_lock_debug.ProcessLocksList },
150 0, 0, { (DWORD_PTR)(__FILE__ ": g_sessions_lock") }
152 static CRITICAL_SECTION g_sessions_lock = { &g_sessions_lock_debug, -1, 0, 0, 0, 0 };
153 static struct list g_sessions = LIST_INIT(g_sessions);
155 static const WCHAR defaultW[] = {'d','e','f','a','u','l','t',0};
156 static const char defname[] = "default";
158 static const WCHAR drv_keyW[] = {'S','o','f','t','w','a','r','e','\\',
159 'W','i','n','e','\\','D','r','i','v','e','r','s','\\',
160 'w','i','n','e','a','l','s','a','.','d','r','v',0};
161 static const WCHAR drv_key_devicesW[] = {'S','o','f','t','w','a','r','e','\\',
162 'W','i','n','e','\\','D','r','i','v','e','r','s','\\',
163 'w','i','n','e','a','l','s','a','.','d','r','v','\\','d','e','v','i','c','e','s',0};
164 static const WCHAR guidW[] = {'g','u','i','d',0};
166 static const IAudioClientVtbl AudioClient_Vtbl;
167 static const IAudioRenderClientVtbl AudioRenderClient_Vtbl;
168 static const IAudioCaptureClientVtbl AudioCaptureClient_Vtbl;
169 static const IAudioSessionControl2Vtbl AudioSessionControl2_Vtbl;
170 static const ISimpleAudioVolumeVtbl SimpleAudioVolume_Vtbl;
171 static const IAudioClockVtbl AudioClock_Vtbl;
172 static const IAudioClock2Vtbl AudioClock2_Vtbl;
173 static const IAudioStreamVolumeVtbl AudioStreamVolume_Vtbl;
174 static const IChannelAudioVolumeVtbl ChannelAudioVolume_Vtbl;
175 static const IAudioSessionManager2Vtbl AudioSessionManager2_Vtbl;
177 static AudioSessionWrapper *AudioSessionWrapper_Create(ACImpl *client);
179 static inline ACImpl *impl_from_IAudioClient(IAudioClient *iface)
181 return CONTAINING_RECORD(iface, ACImpl, IAudioClient_iface);
184 static inline ACImpl *impl_from_IAudioRenderClient(IAudioRenderClient *iface)
186 return CONTAINING_RECORD(iface, ACImpl, IAudioRenderClient_iface);
189 static inline ACImpl *impl_from_IAudioCaptureClient(IAudioCaptureClient *iface)
191 return CONTAINING_RECORD(iface, ACImpl, IAudioCaptureClient_iface);
194 static inline AudioSessionWrapper *impl_from_IAudioSessionControl2(IAudioSessionControl2 *iface)
196 return CONTAINING_RECORD(iface, AudioSessionWrapper, IAudioSessionControl2_iface);
199 static inline AudioSessionWrapper *impl_from_ISimpleAudioVolume(ISimpleAudioVolume *iface)
201 return CONTAINING_RECORD(iface, AudioSessionWrapper, ISimpleAudioVolume_iface);
204 static inline AudioSessionWrapper *impl_from_IChannelAudioVolume(IChannelAudioVolume *iface)
206 return CONTAINING_RECORD(iface, AudioSessionWrapper, IChannelAudioVolume_iface);
209 static inline ACImpl *impl_from_IAudioClock(IAudioClock *iface)
211 return CONTAINING_RECORD(iface, ACImpl, IAudioClock_iface);
214 static inline ACImpl *impl_from_IAudioClock2(IAudioClock2 *iface)
216 return CONTAINING_RECORD(iface, ACImpl, IAudioClock2_iface);
219 static inline ACImpl *impl_from_IAudioStreamVolume(IAudioStreamVolume *iface)
221 return CONTAINING_RECORD(iface, ACImpl, IAudioStreamVolume_iface);
224 static inline SessionMgr *impl_from_IAudioSessionManager2(IAudioSessionManager2 *iface)
226 return CONTAINING_RECORD(iface, SessionMgr, IAudioSessionManager2_iface);
229 BOOL WINAPI DllMain(HINSTANCE dll, DWORD reason, void *reserved)
231 switch (reason)
233 case DLL_PROCESS_ATTACH:
234 g_timer_q = CreateTimerQueue();
235 if(!g_timer_q)
236 return FALSE;
237 break;
239 case DLL_PROCESS_DETACH:
240 DeleteCriticalSection(&g_sessions_lock);
241 break;
243 return TRUE;
246 /* From <dlls/mmdevapi/mmdevapi.h> */
247 enum DriverPriority {
248 Priority_Unavailable = 0,
249 Priority_Low,
250 Priority_Neutral,
251 Priority_Preferred
254 int WINAPI AUDDRV_GetPriority(void)
256 return Priority_Neutral;
259 static void set_device_guid(EDataFlow flow, HKEY drv_key, const WCHAR *key_name,
260 GUID *guid)
262 HKEY key;
263 BOOL opened = FALSE;
264 LONG lr;
266 if(!drv_key){
267 lr = RegCreateKeyExW(HKEY_CURRENT_USER, drv_key_devicesW, 0, NULL, 0, KEY_WRITE,
268 NULL, &drv_key, NULL);
269 if(lr != ERROR_SUCCESS){
270 ERR("RegCreateKeyEx(drv_key) failed: %u\n", lr);
271 return;
273 opened = TRUE;
276 lr = RegCreateKeyExW(drv_key, key_name, 0, NULL, 0, KEY_WRITE,
277 NULL, &key, NULL);
278 if(lr != ERROR_SUCCESS){
279 ERR("RegCreateKeyEx(%s) failed: %u\n", wine_dbgstr_w(key_name), lr);
280 goto exit;
283 lr = RegSetValueExW(key, guidW, 0, REG_BINARY, (BYTE*)guid,
284 sizeof(GUID));
285 if(lr != ERROR_SUCCESS)
286 ERR("RegSetValueEx(%s\\guid) failed: %u\n", wine_dbgstr_w(key_name), lr);
288 RegCloseKey(key);
289 exit:
290 if(opened)
291 RegCloseKey(drv_key);
294 static void get_device_guid(EDataFlow flow, const char *device, GUID *guid)
296 HKEY key = NULL, dev_key;
297 DWORD type, size = sizeof(*guid);
298 WCHAR key_name[256];
300 if(flow == eCapture)
301 key_name[0] = '1';
302 else
303 key_name[0] = '0';
304 key_name[1] = ',';
305 MultiByteToWideChar(CP_UNIXCP, 0, device, -1, key_name + 2,
306 (sizeof(key_name) / sizeof(*key_name)) - 2);
308 if(RegOpenKeyExW(HKEY_CURRENT_USER, drv_key_devicesW, 0, KEY_WRITE|KEY_READ, &key) == ERROR_SUCCESS){
309 if(RegOpenKeyExW(key, key_name, 0, KEY_READ, &dev_key) == ERROR_SUCCESS){
310 if(RegQueryValueExW(dev_key, guidW, 0, &type,
311 (BYTE*)guid, &size) == ERROR_SUCCESS){
312 if(type == REG_BINARY){
313 RegCloseKey(dev_key);
314 RegCloseKey(key);
315 return;
317 ERR("Invalid type for device %s GUID: %u; ignoring and overwriting\n",
318 wine_dbgstr_w(key_name), type);
320 RegCloseKey(dev_key);
324 CoCreateGuid(guid);
326 set_device_guid(flow, key, key_name, guid);
328 if(key)
329 RegCloseKey(key);
332 static BOOL alsa_try_open(const char *devnode, snd_pcm_stream_t stream)
334 snd_pcm_t *handle;
335 int err;
337 TRACE("devnode: %s, stream: %d\n", devnode, stream);
339 if((err = snd_pcm_open(&handle, devnode, stream, SND_PCM_NONBLOCK)) < 0){
340 WARN("The device \"%s\" failed to open: %d (%s).\n",
341 devnode, err, snd_strerror(err));
342 return FALSE;
345 snd_pcm_close(handle);
346 return TRUE;
349 static WCHAR *construct_device_id(EDataFlow flow, const WCHAR *chunk1, const char *chunk2)
351 WCHAR *ret;
352 const WCHAR *prefix;
353 DWORD len_wchars = 0, chunk1_len, copied = 0, prefix_len;
355 static const WCHAR dashW[] = {' ','-',' ',0};
356 static const size_t dashW_len = (sizeof(dashW) / sizeof(*dashW)) - 1;
357 static const WCHAR outW[] = {'O','u','t',':',' ',0};
358 static const WCHAR inW[] = {'I','n',':',' ',0};
360 if(flow == eRender){
361 prefix = outW;
362 prefix_len = (sizeof(outW) / sizeof(*outW)) - 1;
363 len_wchars += prefix_len;
364 }else{
365 prefix = inW;
366 prefix_len = (sizeof(inW) / sizeof(*inW)) - 1;
367 len_wchars += prefix_len;
369 if(chunk1){
370 chunk1_len = strlenW(chunk1);
371 len_wchars += chunk1_len;
373 if(chunk1 && chunk2)
374 len_wchars += dashW_len;
375 if(chunk2)
376 len_wchars += MultiByteToWideChar(CP_UNIXCP, 0, chunk2, -1, NULL, 0) - 1;
377 len_wchars += 1; /* NULL byte */
379 ret = HeapAlloc(GetProcessHeap(), 0, len_wchars * sizeof(WCHAR));
381 memcpy(ret, prefix, prefix_len * sizeof(WCHAR));
382 copied += prefix_len;
383 if(chunk1){
384 memcpy(ret + copied, chunk1, chunk1_len * sizeof(WCHAR));
385 copied += chunk1_len;
387 if(chunk1 && chunk2){
388 memcpy(ret + copied, dashW, dashW_len * sizeof(WCHAR));
389 copied += dashW_len;
391 if(chunk2){
392 MultiByteToWideChar(CP_UNIXCP, 0, chunk2, -1, ret + copied, len_wchars - copied);
393 }else
394 ret[copied] = 0;
396 TRACE("Enumerated device: %s\n", wine_dbgstr_w(ret));
398 return ret;
401 static HRESULT alsa_get_card_devices(EDataFlow flow, snd_pcm_stream_t stream,
402 WCHAR ***ids, GUID **guids, UINT *num, snd_ctl_t *ctl, int card,
403 const WCHAR *cardnameW)
405 int err, device;
406 snd_pcm_info_t *info;
408 info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, snd_pcm_info_sizeof());
409 if(!info)
410 return E_OUTOFMEMORY;
412 snd_pcm_info_set_subdevice(info, 0);
413 snd_pcm_info_set_stream(info, stream);
415 device = -1;
416 for(err = snd_ctl_pcm_next_device(ctl, &device); device != -1 && err >= 0;
417 err = snd_ctl_pcm_next_device(ctl, &device)){
418 const char *devname;
419 char devnode[32];
421 snd_pcm_info_set_device(info, device);
423 if((err = snd_ctl_pcm_info(ctl, info)) < 0){
424 if(err == -ENOENT)
425 /* This device doesn't have the right stream direction */
426 continue;
428 WARN("Failed to get info for card %d, device %d: %d (%s)\n",
429 card, device, err, snd_strerror(err));
430 continue;
433 sprintf(devnode, "plughw:%d,%d", card, device);
434 if(!alsa_try_open(devnode, stream))
435 continue;
437 if(*num){
438 *ids = HeapReAlloc(GetProcessHeap(), 0, *ids, sizeof(WCHAR *) * (*num + 1));
439 *guids = HeapReAlloc(GetProcessHeap(), 0, *guids, sizeof(GUID) * (*num + 1));
440 }else{
441 *ids = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR *));
442 *guids = HeapAlloc(GetProcessHeap(), 0, sizeof(GUID));
445 devname = snd_pcm_info_get_name(info);
446 if(!devname){
447 WARN("Unable to get device name for card %d, device %d\n", card,
448 device);
449 continue;
452 (*ids)[*num] = construct_device_id(flow, cardnameW, devname);
453 get_device_guid(flow, devnode, &(*guids)[*num]);
455 ++(*num);
458 HeapFree(GetProcessHeap(), 0, info);
460 if(err != 0)
461 WARN("Got a failure during device enumeration on card %d: %d (%s)\n",
462 card, err, snd_strerror(err));
464 return S_OK;
467 static void get_reg_devices(EDataFlow flow, snd_pcm_stream_t stream, WCHAR ***ids,
468 GUID **guids, UINT *num)
470 static const WCHAR ALSAOutputDevices[] = {'A','L','S','A','O','u','t','p','u','t','D','e','v','i','c','e','s',0};
471 static const WCHAR ALSAInputDevices[] = {'A','L','S','A','I','n','p','u','t','D','e','v','i','c','e','s',0};
472 HKEY key;
473 WCHAR reg_devices[256];
474 DWORD size = sizeof(reg_devices), type;
475 const WCHAR *value_name = (stream == SND_PCM_STREAM_PLAYBACK) ? ALSAOutputDevices : ALSAInputDevices;
477 /* @@ Wine registry key: HKCU\Software\Wine\Drivers\winealsa.drv */
478 if(RegOpenKeyW(HKEY_CURRENT_USER, drv_keyW, &key) == ERROR_SUCCESS){
479 if(RegQueryValueExW(key, value_name, 0, &type,
480 (BYTE*)reg_devices, &size) == ERROR_SUCCESS){
481 WCHAR *p = reg_devices;
483 if(type != REG_MULTI_SZ){
484 ERR("Registry ALSA device list value type must be REG_MULTI_SZ\n");
485 RegCloseKey(key);
486 return;
489 while(*p){
490 char devname[64];
492 WideCharToMultiByte(CP_UNIXCP, 0, p, -1, devname, sizeof(devname), NULL, NULL);
494 if(alsa_try_open(devname, stream)){
495 if(*num){
496 *ids = HeapReAlloc(GetProcessHeap(), 0, *ids, sizeof(WCHAR *) * (*num + 1));
497 *guids = HeapReAlloc(GetProcessHeap(), 0, *guids, sizeof(GUID) * (*num + 1));
498 }else{
499 *ids = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR *));
500 *guids = HeapAlloc(GetProcessHeap(), 0, sizeof(GUID));
502 (*ids)[*num] = construct_device_id(flow, p, NULL);
503 get_device_guid(flow, devname, &(*guids)[*num]);
504 ++*num;
507 p += lstrlenW(p) + 1;
511 RegCloseKey(key);
515 static HRESULT alsa_enum_devices(EDataFlow flow, WCHAR ***ids, GUID **guids,
516 UINT *num)
518 snd_pcm_stream_t stream = (flow == eRender ? SND_PCM_STREAM_PLAYBACK :
519 SND_PCM_STREAM_CAPTURE);
520 int err, card;
522 card = -1;
523 *num = 0;
525 if(alsa_try_open(defname, stream)){
526 *ids = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR *));
527 (*ids)[0] = construct_device_id(flow, defaultW, NULL);
528 *guids = HeapAlloc(GetProcessHeap(), 0, sizeof(GUID));
529 get_device_guid(flow, defname, &(*guids)[0]);
530 ++*num;
533 get_reg_devices(flow, stream, ids, guids, num);
535 for(err = snd_card_next(&card); card != -1 && err >= 0;
536 err = snd_card_next(&card)){
537 char cardpath[64];
538 char *cardname;
539 WCHAR *cardnameW;
540 snd_ctl_t *ctl;
541 DWORD len;
543 sprintf(cardpath, "hw:%u", card);
545 if((err = snd_ctl_open(&ctl, cardpath, 0)) < 0){
546 WARN("Unable to open ctl for ALSA device %s: %d (%s)\n", cardpath,
547 err, snd_strerror(err));
548 continue;
551 if(snd_card_get_name(card, &cardname) < 0) {
552 /* FIXME: Should be localized */
553 static const WCHAR nameW[] = {'U','n','k','n','o','w','n',' ','s','o','u','n','d','c','a','r','d',0};
554 WARN("Unable to get card name for ALSA device %s: %d (%s)\n",
555 cardpath, err, snd_strerror(err));
556 alsa_get_card_devices(flow, stream, ids, guids, num, ctl, card, nameW);
557 }else{
558 len = MultiByteToWideChar(CP_UNIXCP, 0, cardname, -1, NULL, 0);
559 cardnameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
561 if(!cardnameW){
562 free(cardname);
563 snd_ctl_close(ctl);
564 return E_OUTOFMEMORY;
566 MultiByteToWideChar(CP_UNIXCP, 0, cardname, -1, cardnameW, len);
568 alsa_get_card_devices(flow, stream, ids, guids, num, ctl, card, cardnameW);
570 HeapFree(GetProcessHeap(), 0, cardnameW);
571 free(cardname);
574 snd_ctl_close(ctl);
577 if(err != 0)
578 WARN("Got a failure during card enumeration: %d (%s)\n",
579 err, snd_strerror(err));
581 return S_OK;
584 HRESULT WINAPI AUDDRV_GetEndpointIDs(EDataFlow flow, WCHAR ***ids, GUID **guids,
585 UINT *num, UINT *def_index)
587 HRESULT hr;
589 TRACE("%d %p %p %p %p\n", flow, ids, guids, num, def_index);
591 *ids = NULL;
592 *guids = NULL;
594 hr = alsa_enum_devices(flow, ids, guids, num);
595 if(FAILED(hr)){
596 UINT i;
597 for(i = 0; i < *num; ++i)
598 HeapFree(GetProcessHeap(), 0, (*ids)[i]);
599 HeapFree(GetProcessHeap(), 0, *ids);
600 HeapFree(GetProcessHeap(), 0, *guids);
601 return E_OUTOFMEMORY;
604 TRACE("Enumerated %u devices\n", *num);
606 if(*num == 0){
607 HeapFree(GetProcessHeap(), 0, *ids);
608 *ids = NULL;
609 HeapFree(GetProcessHeap(), 0, *guids);
610 *guids = NULL;
613 *def_index = 0;
615 return S_OK;
618 /* Using the pulse PCM device from alsa-plugins 1.0.24 triggers a bug
619 * which causes audio to cease playing after a few minutes of playback.
620 * Setting handle_underrun=1 on pulse-backed ALSA devices seems to work
621 * around this issue. */
622 static snd_config_t *make_handle_underrun_config(const char *name)
624 snd_config_t *lconf, *dev_node, *hu_node, *type_node;
625 char dev_node_name[64];
626 const char *type_str;
627 int err;
629 snd_config_update();
631 if((err = snd_config_copy(&lconf, snd_config)) < 0){
632 WARN("snd_config_copy failed: %d (%s)\n", err, snd_strerror(err));
633 return NULL;
636 sprintf(dev_node_name, "pcm.%s", name);
637 err = snd_config_search(lconf, dev_node_name, &dev_node);
638 if(err == -ENOENT){
639 snd_config_delete(lconf);
640 return NULL;
642 if(err < 0){
643 snd_config_delete(lconf);
644 WARN("snd_config_search failed: %d (%s)\n", err, snd_strerror(err));
645 return NULL;
648 /* ALSA is extremely fragile. If it runs into a config setting it doesn't
649 * recognize, it tends to fail or assert. So we only want to inject
650 * handle_underrun=1 on devices that we know will recognize it. */
651 err = snd_config_search(dev_node, "type", &type_node);
652 if(err == -ENOENT){
653 snd_config_delete(lconf);
654 return NULL;
656 if(err < 0){
657 snd_config_delete(lconf);
658 WARN("snd_config_search failed: %d (%s)\n", err, snd_strerror(err));
659 return NULL;
662 if((err = snd_config_get_string(type_node, &type_str)) < 0){
663 snd_config_delete(lconf);
664 return NULL;
667 if(strcmp(type_str, "pulse") != 0){
668 snd_config_delete(lconf);
669 return NULL;
672 err = snd_config_search(dev_node, "handle_underrun", &hu_node);
673 if(err >= 0){
674 /* user already has an explicit handle_underrun setting, so don't
675 * use a local config */
676 snd_config_delete(lconf);
677 return NULL;
679 if(err != -ENOENT){
680 snd_config_delete(lconf);
681 WARN("snd_config_search failed: %d (%s)\n", err, snd_strerror(err));
682 return NULL;
685 if((err = snd_config_imake_integer(&hu_node, "handle_underrun", 1)) < 0){
686 snd_config_delete(lconf);
687 WARN("snd_config_imake_integer failed: %d (%s)\n", err,
688 snd_strerror(err));
689 return NULL;
692 if((err = snd_config_add(dev_node, hu_node)) < 0){
693 snd_config_delete(lconf);
694 WARN("snd_config_add failed: %d (%s)\n", err, snd_strerror(err));
695 return NULL;
698 return lconf;
701 static BOOL get_alsa_name_by_guid(GUID *guid, char *name, DWORD name_size, EDataFlow *flow)
703 HKEY devices_key;
704 UINT i = 0;
705 WCHAR key_name[256];
706 DWORD key_name_size;
708 if(RegOpenKeyExW(HKEY_CURRENT_USER, drv_key_devicesW, 0, KEY_READ, &devices_key) != ERROR_SUCCESS){
709 ERR("No devices found in registry?\n");
710 return FALSE;
713 while(1){
714 HKEY key;
715 DWORD size, type;
716 GUID reg_guid;
718 key_name_size = sizeof(key_name)/sizeof(WCHAR);
719 if(RegEnumKeyExW(devices_key, i, key_name, &key_name_size, NULL,
720 NULL, NULL, NULL) != ERROR_SUCCESS)
721 break;
723 if(RegOpenKeyExW(devices_key, key_name, 0, KEY_READ, &key) != ERROR_SUCCESS){
724 WARN("Couldn't open key: %s\n", wine_dbgstr_w(key_name));
725 continue;
728 size = sizeof(reg_guid);
729 if(RegQueryValueExW(key, guidW, 0, &type,
730 (BYTE*)&reg_guid, &size) == ERROR_SUCCESS){
731 if(IsEqualGUID(&reg_guid, guid)){
732 RegCloseKey(key);
733 RegCloseKey(devices_key);
735 TRACE("Found matching device key: %s\n", wine_dbgstr_w(key_name));
737 if(key_name[0] == '0')
738 *flow = eRender;
739 else if(key_name[0] == '1')
740 *flow = eCapture;
741 else{
742 ERR("Unknown device type: %c\n", key_name[0]);
743 return FALSE;
746 WideCharToMultiByte(CP_UNIXCP, 0, key_name + 2, -1, name, name_size, NULL, NULL);
748 return TRUE;
752 RegCloseKey(key);
754 ++i;
757 RegCloseKey(devices_key);
759 WARN("No matching device in registry for GUID %s\n", debugstr_guid(guid));
761 return FALSE;
764 HRESULT WINAPI AUDDRV_GetAudioEndpoint(GUID *guid, IMMDevice *dev, IAudioClient **out)
766 ACImpl *This;
767 int err;
768 snd_pcm_stream_t stream;
769 snd_config_t *lconf;
770 static int handle_underrun = 1;
771 char alsa_name[256];
772 EDataFlow dataflow;
774 TRACE("%s %p %p\n", debugstr_guid(guid), dev, out);
776 if(!get_alsa_name_by_guid(guid, alsa_name, sizeof(alsa_name), &dataflow))
777 return AUDCLNT_E_DEVICE_INVALIDATED;
779 This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(ACImpl));
780 if(!This)
781 return E_OUTOFMEMORY;
783 This->IAudioClient_iface.lpVtbl = &AudioClient_Vtbl;
784 This->IAudioRenderClient_iface.lpVtbl = &AudioRenderClient_Vtbl;
785 This->IAudioCaptureClient_iface.lpVtbl = &AudioCaptureClient_Vtbl;
786 This->IAudioClock_iface.lpVtbl = &AudioClock_Vtbl;
787 This->IAudioClock2_iface.lpVtbl = &AudioClock2_Vtbl;
788 This->IAudioStreamVolume_iface.lpVtbl = &AudioStreamVolume_Vtbl;
790 if(dataflow == eRender)
791 stream = SND_PCM_STREAM_PLAYBACK;
792 else if(dataflow == eCapture)
793 stream = SND_PCM_STREAM_CAPTURE;
794 else{
795 HeapFree(GetProcessHeap(), 0, This);
796 return E_UNEXPECTED;
799 This->dataflow = dataflow;
800 if(handle_underrun && ((lconf = make_handle_underrun_config(alsa_name)))){
801 err = snd_pcm_open_lconf(&This->pcm_handle, alsa_name, stream, SND_PCM_NONBLOCK, lconf);
802 TRACE("Opening PCM device \"%s\" with handle_underrun: %d\n", alsa_name, err);
803 snd_config_delete(lconf);
804 /* Pulse <= 2010 returns EINVAL, it does not know handle_underrun. */
805 if(err == -EINVAL){
806 ERR_(winediag)("PulseAudio \"%s\" %d without handle_underrun. Audio may hang."
807 " Please upgrade to alsa_plugins >= 1.0.24\n", alsa_name, err);
808 handle_underrun = 0;
810 }else
811 err = -EINVAL;
812 if(err == -EINVAL){
813 err = snd_pcm_open(&This->pcm_handle, alsa_name, stream, SND_PCM_NONBLOCK);
815 if(err < 0){
816 HeapFree(GetProcessHeap(), 0, This);
817 WARN("Unable to open PCM \"%s\": %d (%s)\n", alsa_name, err, snd_strerror(err));
818 switch(err){
819 case -EBUSY:
820 return AUDCLNT_E_DEVICE_IN_USE;
821 default:
822 return AUDCLNT_E_ENDPOINT_CREATE_FAILED;
826 This->hw_params = HeapAlloc(GetProcessHeap(), 0,
827 snd_pcm_hw_params_sizeof());
828 if(!This->hw_params){
829 snd_pcm_close(This->pcm_handle);
830 HeapFree(GetProcessHeap(), 0, This);
831 return E_OUTOFMEMORY;
834 InitializeCriticalSection(&This->lock);
835 This->lock.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": ACImpl.lock");
837 This->parent = dev;
838 IMMDevice_AddRef(This->parent);
840 *out = &This->IAudioClient_iface;
841 IAudioClient_AddRef(&This->IAudioClient_iface);
843 return S_OK;
846 static HRESULT WINAPI AudioClient_QueryInterface(IAudioClient *iface,
847 REFIID riid, void **ppv)
849 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
851 if(!ppv)
852 return E_POINTER;
853 *ppv = NULL;
854 if(IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IAudioClient))
855 *ppv = iface;
856 if(*ppv){
857 IUnknown_AddRef((IUnknown*)*ppv);
858 return S_OK;
860 WARN("Unknown interface %s\n", debugstr_guid(riid));
861 return E_NOINTERFACE;
864 static ULONG WINAPI AudioClient_AddRef(IAudioClient *iface)
866 ACImpl *This = impl_from_IAudioClient(iface);
867 ULONG ref;
868 ref = InterlockedIncrement(&This->ref);
869 TRACE("(%p) Refcount now %u\n", This, ref);
870 return ref;
873 static ULONG WINAPI AudioClient_Release(IAudioClient *iface)
875 ACImpl *This = impl_from_IAudioClient(iface);
876 ULONG ref;
877 ref = InterlockedDecrement(&This->ref);
878 TRACE("(%p) Refcount now %u\n", This, ref);
879 if(!ref){
880 IAudioClient_Stop(iface);
881 IMMDevice_Release(This->parent);
882 This->lock.DebugInfo->Spare[0] = 0;
883 DeleteCriticalSection(&This->lock);
884 snd_pcm_drop(This->pcm_handle);
885 snd_pcm_close(This->pcm_handle);
886 if(This->initted){
887 EnterCriticalSection(&g_sessions_lock);
888 list_remove(&This->entry);
889 LeaveCriticalSection(&g_sessions_lock);
891 HeapFree(GetProcessHeap(), 0, This->vols);
892 HeapFree(GetProcessHeap(), 0, This->local_buffer);
893 HeapFree(GetProcessHeap(), 0, This->remapping_buf);
894 HeapFree(GetProcessHeap(), 0, This->tmp_buffer);
895 HeapFree(GetProcessHeap(), 0, This->hw_params);
896 CoTaskMemFree(This->fmt);
897 HeapFree(GetProcessHeap(), 0, This);
899 return ref;
902 static void dump_fmt(const WAVEFORMATEX *fmt)
904 TRACE("wFormatTag: 0x%x (", fmt->wFormatTag);
905 switch(fmt->wFormatTag){
906 case WAVE_FORMAT_PCM:
907 TRACE("WAVE_FORMAT_PCM");
908 break;
909 case WAVE_FORMAT_IEEE_FLOAT:
910 TRACE("WAVE_FORMAT_IEEE_FLOAT");
911 break;
912 case WAVE_FORMAT_EXTENSIBLE:
913 TRACE("WAVE_FORMAT_EXTENSIBLE");
914 break;
915 default:
916 TRACE("Unknown");
917 break;
919 TRACE(")\n");
921 TRACE("nChannels: %u\n", fmt->nChannels);
922 TRACE("nSamplesPerSec: %u\n", fmt->nSamplesPerSec);
923 TRACE("nAvgBytesPerSec: %u\n", fmt->nAvgBytesPerSec);
924 TRACE("nBlockAlign: %u\n", fmt->nBlockAlign);
925 TRACE("wBitsPerSample: %u\n", fmt->wBitsPerSample);
926 TRACE("cbSize: %u\n", fmt->cbSize);
928 if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE){
929 WAVEFORMATEXTENSIBLE *fmtex = (void*)fmt;
930 TRACE("dwChannelMask: %08x\n", fmtex->dwChannelMask);
931 TRACE("Samples: %04x\n", fmtex->Samples.wReserved);
932 TRACE("SubFormat: %s\n", wine_dbgstr_guid(&fmtex->SubFormat));
936 static WAVEFORMATEX *clone_format(const WAVEFORMATEX *fmt)
938 WAVEFORMATEX *ret;
939 size_t size;
941 if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
942 size = sizeof(WAVEFORMATEXTENSIBLE);
943 else
944 size = sizeof(WAVEFORMATEX);
946 ret = CoTaskMemAlloc(size);
947 if(!ret)
948 return NULL;
950 memcpy(ret, fmt, size);
952 ret->cbSize = size - sizeof(WAVEFORMATEX);
954 return ret;
957 static snd_pcm_format_t alsa_format(const WAVEFORMATEX *fmt)
959 snd_pcm_format_t format = SND_PCM_FORMAT_UNKNOWN;
960 const WAVEFORMATEXTENSIBLE *fmtex = (const WAVEFORMATEXTENSIBLE *)fmt;
962 if(fmt->wFormatTag == WAVE_FORMAT_PCM ||
963 (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
964 IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))){
965 if(fmt->wBitsPerSample == 8)
966 format = SND_PCM_FORMAT_U8;
967 else if(fmt->wBitsPerSample == 16)
968 format = SND_PCM_FORMAT_S16_LE;
969 else if(fmt->wBitsPerSample == 24)
970 format = SND_PCM_FORMAT_S24_3LE;
971 else if(fmt->wBitsPerSample == 32)
972 format = SND_PCM_FORMAT_S32_LE;
973 else
974 WARN("Unsupported bit depth: %u\n", fmt->wBitsPerSample);
975 if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
976 fmt->wBitsPerSample != fmtex->Samples.wValidBitsPerSample){
977 if(fmtex->Samples.wValidBitsPerSample == 20 && fmt->wBitsPerSample == 24)
978 format = SND_PCM_FORMAT_S20_3LE;
979 else{
980 WARN("Unsupported ValidBits: %u\n", fmtex->Samples.wValidBitsPerSample);
981 format = SND_PCM_FORMAT_UNKNOWN;
984 }else if(fmt->wFormatTag == WAVE_FORMAT_IEEE_FLOAT ||
985 (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
986 IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT))){
987 if(fmt->wBitsPerSample == 32)
988 format = SND_PCM_FORMAT_FLOAT_LE;
989 else if(fmt->wBitsPerSample == 64)
990 format = SND_PCM_FORMAT_FLOAT64_LE;
991 else
992 WARN("Unsupported float size: %u\n", fmt->wBitsPerSample);
993 }else
994 WARN("Unknown wave format: %04x\n", fmt->wFormatTag);
995 return format;
998 static void session_init_vols(AudioSession *session, UINT channels)
1000 if(session->channel_count < channels){
1001 UINT i;
1003 if(session->channel_vols)
1004 session->channel_vols = HeapReAlloc(GetProcessHeap(), 0,
1005 session->channel_vols, sizeof(float) * channels);
1006 else
1007 session->channel_vols = HeapAlloc(GetProcessHeap(), 0,
1008 sizeof(float) * channels);
1009 if(!session->channel_vols)
1010 return;
1012 for(i = session->channel_count; i < channels; ++i)
1013 session->channel_vols[i] = 1.f;
1015 session->channel_count = channels;
1019 static AudioSession *create_session(const GUID *guid, IMMDevice *device,
1020 UINT num_channels)
1022 AudioSession *ret;
1024 ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(AudioSession));
1025 if(!ret)
1026 return NULL;
1028 memcpy(&ret->guid, guid, sizeof(GUID));
1030 ret->device = device;
1032 list_init(&ret->clients);
1034 list_add_head(&g_sessions, &ret->entry);
1036 InitializeCriticalSection(&ret->lock);
1037 ret->lock.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": AudioSession.lock");
1039 session_init_vols(ret, num_channels);
1041 ret->master_vol = 1.f;
1043 return ret;
1046 /* if channels == 0, then this will return or create a session with
1047 * matching dataflow and GUID. otherwise, channels must also match */
1048 static HRESULT get_audio_session(const GUID *sessionguid,
1049 IMMDevice *device, UINT channels, AudioSession **out)
1051 AudioSession *session;
1053 if(!sessionguid || IsEqualGUID(sessionguid, &GUID_NULL)){
1054 *out = create_session(&GUID_NULL, device, channels);
1055 if(!*out)
1056 return E_OUTOFMEMORY;
1058 return S_OK;
1061 *out = NULL;
1062 LIST_FOR_EACH_ENTRY(session, &g_sessions, AudioSession, entry){
1063 if(session->device == device &&
1064 IsEqualGUID(sessionguid, &session->guid)){
1065 session_init_vols(session, channels);
1066 *out = session;
1067 break;
1071 if(!*out){
1072 *out = create_session(sessionguid, device, channels);
1073 if(!*out)
1074 return E_OUTOFMEMORY;
1077 return S_OK;
1080 static int alsa_channel_index(DWORD flag)
1082 switch(flag){
1083 case SPEAKER_FRONT_LEFT:
1084 return 0;
1085 case SPEAKER_FRONT_RIGHT:
1086 return 1;
1087 case SPEAKER_BACK_LEFT:
1088 return 2;
1089 case SPEAKER_BACK_RIGHT:
1090 return 3;
1091 case SPEAKER_FRONT_CENTER:
1092 return 4;
1093 case SPEAKER_LOW_FREQUENCY:
1094 return 5;
1095 case SPEAKER_SIDE_LEFT:
1096 return 6;
1097 case SPEAKER_SIDE_RIGHT:
1098 return 7;
1100 return -1;
1103 static BOOL need_remapping(ACImpl *This, const WAVEFORMATEX *fmt)
1105 unsigned int i;
1106 for(i = 0; i < fmt->nChannels; ++i){
1107 if(This->alsa_channel_map[i] != i)
1108 return TRUE;
1110 return FALSE;
1113 static DWORD get_channel_mask(unsigned int channels)
1115 switch(channels){
1116 case 0:
1117 return 0;
1118 case 1:
1119 return KSAUDIO_SPEAKER_MONO;
1120 case 2:
1121 return KSAUDIO_SPEAKER_STEREO;
1122 case 3:
1123 return KSAUDIO_SPEAKER_STEREO | SPEAKER_LOW_FREQUENCY;
1124 case 4:
1125 return KSAUDIO_SPEAKER_QUAD; /* not _SURROUND */
1126 case 5:
1127 return KSAUDIO_SPEAKER_QUAD | SPEAKER_LOW_FREQUENCY;
1128 case 6:
1129 return KSAUDIO_SPEAKER_5POINT1; /* not 5POINT1_SURROUND */
1130 case 7:
1131 return KSAUDIO_SPEAKER_5POINT1 | SPEAKER_BACK_CENTER;
1132 case 8:
1133 return KSAUDIO_SPEAKER_7POINT1_SURROUND; /* Vista deprecates 7POINT1 */
1135 FIXME("Unknown speaker configuration: %u\n", channels);
1136 return 0;
1139 static HRESULT map_channels(ACImpl *This, const WAVEFORMATEX *fmt)
1141 if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE || fmt->nChannels > 2){
1142 WAVEFORMATEXTENSIBLE *fmtex = (void*)fmt;
1143 DWORD mask, flag = SPEAKER_FRONT_LEFT;
1144 UINT i = 0;
1146 if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
1147 fmtex->dwChannelMask != 0)
1148 mask = fmtex->dwChannelMask;
1149 else
1150 mask = get_channel_mask(fmt->nChannels);
1152 This->alsa_channels = 0;
1154 while(i < fmt->nChannels && !(flag & SPEAKER_RESERVED)){
1155 if(mask & flag){
1156 This->alsa_channel_map[i] = alsa_channel_index(flag);
1157 TRACE("Mapping mmdevapi channel %u (0x%x) to ALSA channel %d\n",
1158 i, flag, This->alsa_channel_map[i]);
1159 if(This->alsa_channel_map[i] >= This->alsa_channels)
1160 This->alsa_channels = This->alsa_channel_map[i] + 1;
1161 ++i;
1163 flag <<= 1;
1166 while(i < fmt->nChannels){
1167 This->alsa_channel_map[i] = This->alsa_channels;
1168 TRACE("Mapping mmdevapi channel %u to ALSA channel %d\n",
1169 i, This->alsa_channel_map[i]);
1170 ++This->alsa_channels;
1171 ++i;
1174 for(i = 0; i < fmt->nChannels; ++i){
1175 if(This->alsa_channel_map[i] == -1){
1176 This->alsa_channel_map[i] = This->alsa_channels;
1177 ++This->alsa_channels;
1178 TRACE("Remapping mmdevapi channel %u to ALSA channel %d\n",
1179 i, This->alsa_channel_map[i]);
1183 This->need_remapping = need_remapping(This, fmt);
1185 TRACE("need_remapping: %u, alsa_channels: %d\n", This->need_remapping, This->alsa_channels);
1186 }else{
1187 This->need_remapping = FALSE;
1188 This->alsa_channels = fmt->nChannels;
1189 TRACE("need_remapping: %u, alsa_channels: %d\n", This->need_remapping, This->alsa_channels);
1192 return S_OK;
1195 static HRESULT WINAPI AudioClient_Initialize(IAudioClient *iface,
1196 AUDCLNT_SHAREMODE mode, DWORD flags, REFERENCE_TIME duration,
1197 REFERENCE_TIME period, const WAVEFORMATEX *fmt,
1198 const GUID *sessionguid)
1200 ACImpl *This = impl_from_IAudioClient(iface);
1201 snd_pcm_sw_params_t *sw_params = NULL;
1202 snd_pcm_format_t format;
1203 unsigned int rate, alsa_period_us;
1204 int err, i;
1205 HRESULT hr = S_OK;
1207 TRACE("(%p)->(%x, %x, %s, %s, %p, %s)\n", This, mode, flags,
1208 wine_dbgstr_longlong(duration), wine_dbgstr_longlong(period), fmt, debugstr_guid(sessionguid));
1210 if(!fmt)
1211 return E_POINTER;
1213 if(mode != AUDCLNT_SHAREMODE_SHARED && mode != AUDCLNT_SHAREMODE_EXCLUSIVE)
1214 return AUDCLNT_E_NOT_INITIALIZED;
1216 if(flags & ~(AUDCLNT_STREAMFLAGS_CROSSPROCESS |
1217 AUDCLNT_STREAMFLAGS_LOOPBACK |
1218 AUDCLNT_STREAMFLAGS_EVENTCALLBACK |
1219 AUDCLNT_STREAMFLAGS_NOPERSIST |
1220 AUDCLNT_STREAMFLAGS_RATEADJUST |
1221 AUDCLNT_SESSIONFLAGS_EXPIREWHENUNOWNED |
1222 AUDCLNT_SESSIONFLAGS_DISPLAY_HIDE |
1223 AUDCLNT_SESSIONFLAGS_DISPLAY_HIDEWHENEXPIRED)){
1224 TRACE("Unknown flags: %08x\n", flags);
1225 return E_INVALIDARG;
1228 if(mode == AUDCLNT_SHAREMODE_SHARED){
1229 period = DefaultPeriod;
1230 if( duration < 3 * period)
1231 duration = 3 * period;
1232 }else{
1233 if(!period)
1234 period = DefaultPeriod; /* not minimum */
1235 if(period < MinimumPeriod || period > 5000000)
1236 return AUDCLNT_E_INVALID_DEVICE_PERIOD;
1237 if(duration > 20000000) /* the smaller the period, the lower this limit */
1238 return AUDCLNT_E_BUFFER_SIZE_ERROR;
1239 if(flags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK){
1240 if(duration != period)
1241 return AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL;
1242 FIXME("EXCLUSIVE mode with EVENTCALLBACK\n");
1243 return AUDCLNT_E_DEVICE_IN_USE;
1244 }else{
1245 if( duration < 8 * period)
1246 duration = 8 * period; /* may grow above 2s */
1250 EnterCriticalSection(&This->lock);
1252 if(This->initted){
1253 LeaveCriticalSection(&This->lock);
1254 return AUDCLNT_E_ALREADY_INITIALIZED;
1257 dump_fmt(fmt);
1259 if(FAILED(map_channels(This, fmt))){
1260 WARN("map_channels failed\n");
1261 hr = AUDCLNT_E_ENDPOINT_CREATE_FAILED;
1262 goto exit;
1265 if((err = snd_pcm_hw_params_any(This->pcm_handle, This->hw_params)) < 0){
1266 WARN("Unable to get hw_params: %d (%s)\n", err, snd_strerror(err));
1267 hr = AUDCLNT_E_ENDPOINT_CREATE_FAILED;
1268 goto exit;
1271 if((err = snd_pcm_hw_params_set_access(This->pcm_handle, This->hw_params,
1272 SND_PCM_ACCESS_RW_INTERLEAVED)) < 0){
1273 WARN("Unable to set access: %d (%s)\n", err, snd_strerror(err));
1274 hr = AUDCLNT_E_ENDPOINT_CREATE_FAILED;
1275 goto exit;
1278 format = alsa_format(fmt);
1279 if (format == SND_PCM_FORMAT_UNKNOWN){
1280 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
1281 goto exit;
1284 if((err = snd_pcm_hw_params_set_format(This->pcm_handle, This->hw_params,
1285 format)) < 0){
1286 WARN("Unable to set ALSA format to %u: %d (%s)\n", format, err,
1287 snd_strerror(err));
1288 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
1289 goto exit;
1292 This->alsa_format = format;
1294 rate = fmt->nSamplesPerSec;
1295 if((err = snd_pcm_hw_params_set_rate_near(This->pcm_handle, This->hw_params,
1296 &rate, NULL)) < 0){
1297 WARN("Unable to set rate to %u: %d (%s)\n", rate, err,
1298 snd_strerror(err));
1299 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
1300 goto exit;
1303 if((err = snd_pcm_hw_params_set_channels(This->pcm_handle, This->hw_params,
1304 This->alsa_channels)) < 0){
1305 WARN("Unable to set channels to %u: %d (%s)\n", fmt->nChannels, err,
1306 snd_strerror(err));
1307 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
1308 goto exit;
1311 This->mmdev_period_rt = period;
1312 alsa_period_us = This->mmdev_period_rt / 10;
1313 if((err = snd_pcm_hw_params_set_period_time_near(This->pcm_handle,
1314 This->hw_params, &alsa_period_us, NULL)) < 0)
1315 WARN("Unable to set period time near %u: %d (%s)\n", alsa_period_us,
1316 err, snd_strerror(err));
1317 /* ALSA updates the output variable alsa_period_us */
1319 This->mmdev_period_frames = MulDiv(fmt->nSamplesPerSec,
1320 This->mmdev_period_rt, 10000000);
1322 /* Buffer 4 ALSA periods if large enough, else 4 mmdevapi periods */
1323 This->alsa_bufsize_frames = This->mmdev_period_frames * 4;
1324 if(err < 0 || alsa_period_us < period / 10)
1325 err = snd_pcm_hw_params_set_buffer_size_near(This->pcm_handle,
1326 This->hw_params, &This->alsa_bufsize_frames);
1327 else{
1328 unsigned int periods = 4;
1329 err = snd_pcm_hw_params_set_periods_near(This->pcm_handle, This->hw_params, &periods, NULL);
1331 if(err < 0)
1332 WARN("Unable to set buffer size: %d (%s)\n", err, snd_strerror(err));
1334 if((err = snd_pcm_hw_params(This->pcm_handle, This->hw_params)) < 0){
1335 WARN("Unable to set hw params: %d (%s)\n", err, snd_strerror(err));
1336 hr = AUDCLNT_E_ENDPOINT_CREATE_FAILED;
1337 goto exit;
1340 if((err = snd_pcm_hw_params_get_period_size(This->hw_params,
1341 &This->alsa_period_frames, NULL)) < 0){
1342 WARN("Unable to get period size: %d (%s)\n", err, snd_strerror(err));
1343 hr = AUDCLNT_E_ENDPOINT_CREATE_FAILED;
1344 goto exit;
1347 if((err = snd_pcm_hw_params_get_buffer_size(This->hw_params,
1348 &This->alsa_bufsize_frames)) < 0){
1349 WARN("Unable to get buffer size: %d (%s)\n", err, snd_strerror(err));
1350 hr = AUDCLNT_E_ENDPOINT_CREATE_FAILED;
1351 goto exit;
1354 sw_params = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, snd_pcm_sw_params_sizeof());
1355 if(!sw_params){
1356 hr = E_OUTOFMEMORY;
1357 goto exit;
1360 if((err = snd_pcm_sw_params_current(This->pcm_handle, sw_params)) < 0){
1361 WARN("Unable to get sw_params: %d (%s)\n", err, snd_strerror(err));
1362 hr = AUDCLNT_E_ENDPOINT_CREATE_FAILED;
1363 goto exit;
1366 if((err = snd_pcm_sw_params_set_start_threshold(This->pcm_handle,
1367 sw_params, 1)) < 0){
1368 WARN("Unable set start threshold to 0: %d (%s)\n", err, snd_strerror(err));
1369 hr = AUDCLNT_E_ENDPOINT_CREATE_FAILED;
1370 goto exit;
1373 if((err = snd_pcm_sw_params_set_stop_threshold(This->pcm_handle,
1374 sw_params, This->alsa_bufsize_frames)) < 0){
1375 WARN("Unable set stop threshold to %lu: %d (%s)\n",
1376 This->alsa_bufsize_frames, err, snd_strerror(err));
1377 hr = AUDCLNT_E_ENDPOINT_CREATE_FAILED;
1378 goto exit;
1381 if((err = snd_pcm_sw_params(This->pcm_handle, sw_params)) < 0){
1382 WARN("Unable to set sw params: %d (%s)\n", err, snd_strerror(err));
1383 hr = AUDCLNT_E_ENDPOINT_CREATE_FAILED;
1384 goto exit;
1387 if((err = snd_pcm_prepare(This->pcm_handle)) < 0){
1388 WARN("Unable to prepare device: %d (%s)\n", err, snd_strerror(err));
1389 hr = AUDCLNT_E_ENDPOINT_CREATE_FAILED;
1390 goto exit;
1393 /* Bear in mind weird situations where
1394 * ALSA period (50ms) > mmdevapi buffer (3x10ms)
1395 * or surprising rounding as seen with 22050x8x1 with Pulse:
1396 * ALSA period 220 vs. 221 frames in mmdevapi and
1397 * buffer 883 vs. 2205 frames in mmdevapi! */
1398 This->bufsize_frames = MulDiv(duration, fmt->nSamplesPerSec, 10000000);
1399 This->hidden_frames = This->alsa_period_frames + This->mmdev_period_frames +
1400 MulDiv(fmt->nSamplesPerSec, EXTRA_SAFE_RT, 10000000);
1402 /* Check if the ALSA buffer is so small that it will run out before
1403 * the next MMDevAPI period tick occurs. Allow a little wiggle room
1404 * with 120% of the period time. */
1405 if(This->alsa_bufsize_frames < 1.2 * This->mmdev_period_frames)
1406 FIXME("ALSA buffer time is too small. Expect underruns. (%lu < %u * 1.2)\n",
1407 This->alsa_bufsize_frames, This->mmdev_period_frames);
1409 This->local_buffer = HeapAlloc(GetProcessHeap(), 0,
1410 This->bufsize_frames * fmt->nBlockAlign);
1411 if(!This->local_buffer){
1412 hr = E_OUTOFMEMORY;
1413 goto exit;
1415 if (fmt->wBitsPerSample == 8)
1416 memset(This->local_buffer, 128, This->bufsize_frames * fmt->nBlockAlign);
1417 else
1418 memset(This->local_buffer, 0, This->bufsize_frames * fmt->nBlockAlign);
1420 This->fmt = clone_format(fmt);
1421 if(!This->fmt){
1422 hr = E_OUTOFMEMORY;
1423 goto exit;
1426 This->vols = HeapAlloc(GetProcessHeap(), 0, fmt->nChannels * sizeof(float));
1427 if(!This->vols){
1428 hr = E_OUTOFMEMORY;
1429 goto exit;
1432 for(i = 0; i < fmt->nChannels; ++i)
1433 This->vols[i] = 1.f;
1435 This->share = mode;
1436 This->flags = flags;
1438 EnterCriticalSection(&g_sessions_lock);
1440 hr = get_audio_session(sessionguid, This->parent, fmt->nChannels,
1441 &This->session);
1442 if(FAILED(hr)){
1443 LeaveCriticalSection(&g_sessions_lock);
1444 goto exit;
1447 list_add_tail(&This->session->clients, &This->entry);
1449 LeaveCriticalSection(&g_sessions_lock);
1451 This->initted = TRUE;
1453 TRACE("ALSA period: %lu frames\n", This->alsa_period_frames);
1454 TRACE("ALSA buffer: %lu frames\n", This->alsa_bufsize_frames);
1455 TRACE("MMDevice period: %u frames\n", This->mmdev_period_frames);
1456 TRACE("MMDevice buffer: %u frames\n", This->bufsize_frames);
1458 exit:
1459 HeapFree(GetProcessHeap(), 0, sw_params);
1460 if(FAILED(hr)){
1461 HeapFree(GetProcessHeap(), 0, This->local_buffer);
1462 This->local_buffer = NULL;
1463 CoTaskMemFree(This->fmt);
1464 This->fmt = NULL;
1465 HeapFree(GetProcessHeap(), 0, This->vols);
1466 This->vols = NULL;
1469 LeaveCriticalSection(&This->lock);
1471 return hr;
1474 static HRESULT WINAPI AudioClient_GetBufferSize(IAudioClient *iface,
1475 UINT32 *out)
1477 ACImpl *This = impl_from_IAudioClient(iface);
1479 TRACE("(%p)->(%p)\n", This, out);
1481 if(!out)
1482 return E_POINTER;
1484 EnterCriticalSection(&This->lock);
1486 if(!This->initted){
1487 LeaveCriticalSection(&This->lock);
1488 return AUDCLNT_E_NOT_INITIALIZED;
1491 *out = This->bufsize_frames;
1493 LeaveCriticalSection(&This->lock);
1495 return S_OK;
1498 static HRESULT WINAPI AudioClient_GetStreamLatency(IAudioClient *iface,
1499 REFERENCE_TIME *latency)
1501 ACImpl *This = impl_from_IAudioClient(iface);
1503 TRACE("(%p)->(%p)\n", This, latency);
1505 if(!latency)
1506 return E_POINTER;
1508 EnterCriticalSection(&This->lock);
1510 if(!This->initted){
1511 LeaveCriticalSection(&This->lock);
1512 return AUDCLNT_E_NOT_INITIALIZED;
1515 /* Hide some frames in the ALSA buffer. Allows us to return GetCurrentPadding=0
1516 * yet have enough data left to play (as if it were in native's mixer). Add:
1517 * + mmdevapi_period such that at the end of it, ALSA still has data;
1518 * + EXTRA_SAFE (~4ms) to allow for late callback invocation / fluctuation;
1519 * + alsa_period such that ALSA always has at least one period to play. */
1520 if(This->dataflow == eRender)
1521 *latency = MulDiv(This->hidden_frames, 10000000, This->fmt->nSamplesPerSec);
1522 else
1523 *latency = MulDiv(This->alsa_period_frames, 10000000, This->fmt->nSamplesPerSec)
1524 + This->mmdev_period_rt;
1526 LeaveCriticalSection(&This->lock);
1528 return S_OK;
1531 static HRESULT WINAPI AudioClient_GetCurrentPadding(IAudioClient *iface,
1532 UINT32 *out)
1534 ACImpl *This = impl_from_IAudioClient(iface);
1536 TRACE("(%p)->(%p)\n", This, out);
1538 if(!out)
1539 return E_POINTER;
1541 EnterCriticalSection(&This->lock);
1543 if(!This->initted){
1544 LeaveCriticalSection(&This->lock);
1545 return AUDCLNT_E_NOT_INITIALIZED;
1548 /* padding is solely updated at callback time in shared mode */
1549 *out = This->held_frames;
1551 LeaveCriticalSection(&This->lock);
1553 TRACE("pad: %u\n", *out);
1555 return S_OK;
1558 static HRESULT WINAPI AudioClient_IsFormatSupported(IAudioClient *iface,
1559 AUDCLNT_SHAREMODE mode, const WAVEFORMATEX *fmt,
1560 WAVEFORMATEX **out)
1562 ACImpl *This = impl_from_IAudioClient(iface);
1563 snd_pcm_format_mask_t *formats = NULL;
1564 snd_pcm_format_t format;
1565 HRESULT hr = S_OK;
1566 WAVEFORMATEX *closest = NULL;
1567 unsigned int max = 0, min = 0;
1568 int err;
1570 TRACE("(%p)->(%x, %p, %p)\n", This, mode, fmt, out);
1572 if(!fmt || (mode == AUDCLNT_SHAREMODE_SHARED && !out))
1573 return E_POINTER;
1575 if(mode != AUDCLNT_SHAREMODE_SHARED && mode != AUDCLNT_SHAREMODE_EXCLUSIVE)
1576 return E_INVALIDARG;
1578 if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
1579 fmt->cbSize < sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX))
1580 return E_INVALIDARG;
1582 dump_fmt(fmt);
1584 if(out){
1585 *out = NULL;
1586 if(mode != AUDCLNT_SHAREMODE_SHARED)
1587 out = NULL;
1590 EnterCriticalSection(&This->lock);
1592 if((err = snd_pcm_hw_params_any(This->pcm_handle, This->hw_params)) < 0){
1593 hr = AUDCLNT_E_DEVICE_INVALIDATED;
1594 goto exit;
1597 formats = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
1598 snd_pcm_format_mask_sizeof());
1599 if(!formats){
1600 hr = E_OUTOFMEMORY;
1601 goto exit;
1604 snd_pcm_hw_params_get_format_mask(This->hw_params, formats);
1605 format = alsa_format(fmt);
1606 if (format == SND_PCM_FORMAT_UNKNOWN ||
1607 !snd_pcm_format_mask_test(formats, format)){
1608 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
1609 goto exit;
1612 closest = clone_format(fmt);
1613 if(!closest){
1614 hr = E_OUTOFMEMORY;
1615 goto exit;
1618 if((err = snd_pcm_hw_params_get_rate_min(This->hw_params, &min, NULL)) < 0){
1619 hr = AUDCLNT_E_DEVICE_INVALIDATED;
1620 WARN("Unable to get min rate: %d (%s)\n", err, snd_strerror(err));
1621 goto exit;
1624 if((err = snd_pcm_hw_params_get_rate_max(This->hw_params, &max, NULL)) < 0){
1625 hr = AUDCLNT_E_DEVICE_INVALIDATED;
1626 WARN("Unable to get max rate: %d (%s)\n", err, snd_strerror(err));
1627 goto exit;
1630 if(fmt->nSamplesPerSec < min || fmt->nSamplesPerSec > max){
1631 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
1632 goto exit;
1635 if((err = snd_pcm_hw_params_get_channels_min(This->hw_params, &min)) < 0){
1636 hr = AUDCLNT_E_DEVICE_INVALIDATED;
1637 WARN("Unable to get min channels: %d (%s)\n", err, snd_strerror(err));
1638 goto exit;
1641 if((err = snd_pcm_hw_params_get_channels_max(This->hw_params, &max)) < 0){
1642 hr = AUDCLNT_E_DEVICE_INVALIDATED;
1643 WARN("Unable to get max channels: %d (%s)\n", err, snd_strerror(err));
1644 goto exit;
1646 if(fmt->nChannels > max){
1647 hr = S_FALSE;
1648 closest->nChannels = max;
1649 }else if(fmt->nChannels < min){
1650 hr = S_FALSE;
1651 closest->nChannels = min;
1654 if(FAILED(map_channels(This, fmt))){
1655 hr = AUDCLNT_E_DEVICE_INVALIDATED;
1656 WARN("map_channels failed\n");
1657 goto exit;
1659 if(This->alsa_channels > max){
1660 hr = S_FALSE;
1661 closest->nChannels = max;
1664 if(closest->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
1665 ((WAVEFORMATEXTENSIBLE*)closest)->dwChannelMask = get_channel_mask(closest->nChannels);
1667 exit:
1668 LeaveCriticalSection(&This->lock);
1669 HeapFree(GetProcessHeap(), 0, formats);
1671 if(hr == S_FALSE && !out)
1672 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
1674 if(hr == S_FALSE && out) {
1675 closest->nBlockAlign =
1676 closest->nChannels * closest->wBitsPerSample / 8;
1677 closest->nAvgBytesPerSec =
1678 closest->nBlockAlign * closest->nSamplesPerSec;
1679 *out = closest;
1680 } else
1681 CoTaskMemFree(closest);
1683 TRACE("returning: %08x\n", hr);
1684 return hr;
1687 static HRESULT WINAPI AudioClient_GetMixFormat(IAudioClient *iface,
1688 WAVEFORMATEX **pwfx)
1690 ACImpl *This = impl_from_IAudioClient(iface);
1691 WAVEFORMATEXTENSIBLE *fmt;
1692 snd_pcm_format_mask_t *formats;
1693 unsigned int max_rate, max_channels;
1694 int err;
1695 HRESULT hr = S_OK;
1697 TRACE("(%p)->(%p)\n", This, pwfx);
1699 if(!pwfx)
1700 return E_POINTER;
1701 *pwfx = NULL;
1703 fmt = CoTaskMemAlloc(sizeof(WAVEFORMATEXTENSIBLE));
1704 if(!fmt)
1705 return E_OUTOFMEMORY;
1707 formats = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, snd_pcm_format_mask_sizeof());
1708 if(!formats){
1709 CoTaskMemFree(fmt);
1710 return E_OUTOFMEMORY;
1713 EnterCriticalSection(&This->lock);
1715 if((err = snd_pcm_hw_params_any(This->pcm_handle, This->hw_params)) < 0){
1716 WARN("Unable to get hw_params: %d (%s)\n", err, snd_strerror(err));
1717 hr = AUDCLNT_E_DEVICE_INVALIDATED;
1718 goto exit;
1721 snd_pcm_hw_params_get_format_mask(This->hw_params, formats);
1723 fmt->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
1724 if(snd_pcm_format_mask_test(formats, SND_PCM_FORMAT_FLOAT_LE)){
1725 fmt->Format.wBitsPerSample = 32;
1726 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
1727 }else if(snd_pcm_format_mask_test(formats, SND_PCM_FORMAT_S16_LE)){
1728 fmt->Format.wBitsPerSample = 16;
1729 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1730 }else if(snd_pcm_format_mask_test(formats, SND_PCM_FORMAT_U8)){
1731 fmt->Format.wBitsPerSample = 8;
1732 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1733 }else if(snd_pcm_format_mask_test(formats, SND_PCM_FORMAT_S32_LE)){
1734 fmt->Format.wBitsPerSample = 32;
1735 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1736 }else if(snd_pcm_format_mask_test(formats, SND_PCM_FORMAT_S24_3LE)){
1737 fmt->Format.wBitsPerSample = 24;
1738 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1739 }else{
1740 ERR("Didn't recognize any available ALSA formats\n");
1741 hr = AUDCLNT_E_DEVICE_INVALIDATED;
1742 goto exit;
1745 if((err = snd_pcm_hw_params_get_channels_max(This->hw_params,
1746 &max_channels)) < 0){
1747 WARN("Unable to get max channels: %d (%s)\n", err, snd_strerror(err));
1748 hr = AUDCLNT_E_DEVICE_INVALIDATED;
1749 goto exit;
1752 if(max_channels > 2)
1753 fmt->Format.nChannels = 2;
1754 else
1755 fmt->Format.nChannels = max_channels;
1757 fmt->dwChannelMask = get_channel_mask(fmt->Format.nChannels);
1759 if((err = snd_pcm_hw_params_get_rate_max(This->hw_params, &max_rate,
1760 NULL)) < 0){
1761 WARN("Unable to get max rate: %d (%s)\n", err, snd_strerror(err));
1762 hr = AUDCLNT_E_DEVICE_INVALIDATED;
1763 goto exit;
1766 if(max_rate >= 48000)
1767 fmt->Format.nSamplesPerSec = 48000;
1768 else if(max_rate >= 44100)
1769 fmt->Format.nSamplesPerSec = 44100;
1770 else if(max_rate >= 22050)
1771 fmt->Format.nSamplesPerSec = 22050;
1772 else if(max_rate >= 11025)
1773 fmt->Format.nSamplesPerSec = 11025;
1774 else if(max_rate >= 8000)
1775 fmt->Format.nSamplesPerSec = 8000;
1776 else{
1777 ERR("Unknown max rate: %u\n", max_rate);
1778 hr = AUDCLNT_E_DEVICE_INVALIDATED;
1779 goto exit;
1782 fmt->Format.nBlockAlign = (fmt->Format.wBitsPerSample *
1783 fmt->Format.nChannels) / 8;
1784 fmt->Format.nAvgBytesPerSec = fmt->Format.nSamplesPerSec *
1785 fmt->Format.nBlockAlign;
1787 fmt->Samples.wValidBitsPerSample = fmt->Format.wBitsPerSample;
1788 fmt->Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
1790 dump_fmt((WAVEFORMATEX*)fmt);
1791 *pwfx = (WAVEFORMATEX*)fmt;
1793 exit:
1794 LeaveCriticalSection(&This->lock);
1795 if(FAILED(hr))
1796 CoTaskMemFree(fmt);
1797 HeapFree(GetProcessHeap(), 0, formats);
1799 return hr;
1802 static HRESULT WINAPI AudioClient_GetDevicePeriod(IAudioClient *iface,
1803 REFERENCE_TIME *defperiod, REFERENCE_TIME *minperiod)
1805 ACImpl *This = impl_from_IAudioClient(iface);
1807 TRACE("(%p)->(%p, %p)\n", This, defperiod, minperiod);
1809 if(!defperiod && !minperiod)
1810 return E_POINTER;
1812 if(defperiod)
1813 *defperiod = DefaultPeriod;
1814 if(minperiod)
1815 *minperiod = MinimumPeriod;
1817 return S_OK;
1820 static BYTE *remap_channels(ACImpl *This, BYTE *buf, snd_pcm_uframes_t frames)
1822 snd_pcm_uframes_t i;
1823 UINT c;
1824 UINT bytes_per_sample = This->fmt->wBitsPerSample / 8;
1826 if(!This->need_remapping)
1827 return buf;
1829 if(!This->remapping_buf){
1830 This->remapping_buf = HeapAlloc(GetProcessHeap(), 0,
1831 bytes_per_sample * This->alsa_channels * frames);
1832 This->remapping_buf_frames = frames;
1833 }else if(This->remapping_buf_frames < frames){
1834 This->remapping_buf = HeapReAlloc(GetProcessHeap(), 0, This->remapping_buf,
1835 bytes_per_sample * This->alsa_channels * frames);
1836 This->remapping_buf_frames = frames;
1839 snd_pcm_format_set_silence(This->alsa_format, This->remapping_buf,
1840 frames * This->alsa_channels);
1842 switch(This->fmt->wBitsPerSample){
1843 case 8: {
1844 UINT8 *tgt_buf, *src_buf;
1845 tgt_buf = This->remapping_buf;
1846 src_buf = buf;
1847 for(i = 0; i < frames; ++i){
1848 for(c = 0; c < This->fmt->nChannels; ++c)
1849 tgt_buf[This->alsa_channel_map[c]] = src_buf[c];
1850 tgt_buf += This->alsa_channels;
1851 src_buf += This->fmt->nChannels;
1853 break;
1855 case 16: {
1856 UINT16 *tgt_buf, *src_buf;
1857 tgt_buf = (UINT16*)This->remapping_buf;
1858 src_buf = (UINT16*)buf;
1859 for(i = 0; i < frames; ++i){
1860 for(c = 0; c < This->fmt->nChannels; ++c)
1861 tgt_buf[This->alsa_channel_map[c]] = src_buf[c];
1862 tgt_buf += This->alsa_channels;
1863 src_buf += This->fmt->nChannels;
1866 break;
1867 case 32: {
1868 UINT32 *tgt_buf, *src_buf;
1869 tgt_buf = (UINT32*)This->remapping_buf;
1870 src_buf = (UINT32*)buf;
1871 for(i = 0; i < frames; ++i){
1872 for(c = 0; c < This->fmt->nChannels; ++c)
1873 tgt_buf[This->alsa_channel_map[c]] = src_buf[c];
1874 tgt_buf += This->alsa_channels;
1875 src_buf += This->fmt->nChannels;
1878 break;
1879 default: {
1880 BYTE *tgt_buf, *src_buf;
1881 tgt_buf = This->remapping_buf;
1882 src_buf = buf;
1883 for(i = 0; i < frames; ++i){
1884 for(c = 0; c < This->fmt->nChannels; ++c)
1885 memcpy(&tgt_buf[This->alsa_channel_map[c] * bytes_per_sample],
1886 &src_buf[c * bytes_per_sample], bytes_per_sample);
1887 tgt_buf += This->alsa_channels * bytes_per_sample;
1888 src_buf += This->fmt->nChannels * bytes_per_sample;
1891 break;
1894 return This->remapping_buf;
1897 static snd_pcm_sframes_t alsa_write_best_effort(snd_pcm_t *handle, BYTE *buf,
1898 snd_pcm_uframes_t frames, ACImpl *This, BOOL mute)
1900 snd_pcm_sframes_t written;
1902 if(mute){
1903 int err;
1904 if((err = snd_pcm_format_set_silence(This->alsa_format, buf,
1905 frames * This->fmt->nChannels)) < 0)
1906 WARN("Setting buffer to silence failed: %d (%s)\n", err,
1907 snd_strerror(err));
1910 buf = remap_channels(This, buf, frames);
1912 written = snd_pcm_writei(handle, buf, frames);
1913 if(written < 0){
1914 int ret;
1916 if(written == -EAGAIN)
1917 /* buffer full */
1918 return 0;
1920 WARN("writei failed, recovering: %ld (%s)\n", written,
1921 snd_strerror(written));
1923 ret = snd_pcm_recover(handle, written, 0);
1924 if(ret < 0){
1925 WARN("Could not recover: %d (%s)\n", ret, snd_strerror(ret));
1926 return ret;
1929 written = snd_pcm_writei(handle, buf, frames);
1932 return written;
1935 static void alsa_write_data(ACImpl *This)
1937 snd_pcm_sframes_t written, in_alsa;
1938 snd_pcm_uframes_t to_write, avail, write_limit, max_period;
1939 int err;
1940 BYTE *buf =
1941 This->local_buffer + (This->lcl_offs_frames * This->fmt->nBlockAlign);
1943 /* this call seems to be required to get an accurate snd_pcm_state() */
1944 avail = snd_pcm_avail_update(This->pcm_handle);
1946 if(snd_pcm_state(This->pcm_handle) == SND_PCM_STATE_XRUN ||
1947 avail > This->alsa_bufsize_frames){
1948 TRACE("XRun state avail %ld, recovering\n", avail);
1950 avail = This->alsa_bufsize_frames;
1952 if((err = snd_pcm_recover(This->pcm_handle, -EPIPE, 1)) < 0)
1953 WARN("snd_pcm_recover failed: %d (%s)\n", err, snd_strerror(err));
1955 if((err = snd_pcm_reset(This->pcm_handle)) < 0)
1956 WARN("snd_pcm_reset failed: %d (%s)\n", err, snd_strerror(err));
1958 if((err = snd_pcm_prepare(This->pcm_handle)) < 0)
1959 WARN("snd_pcm_prepare failed: %d (%s)\n", err, snd_strerror(err));
1960 }else
1961 TRACE("pad: %ld\n", This->alsa_bufsize_frames - avail);
1963 if(This->held_frames == 0)
1964 return;
1966 if(This->lcl_offs_frames + This->held_frames > This->bufsize_frames)
1967 to_write = This->bufsize_frames - This->lcl_offs_frames;
1968 else
1969 to_write = This->held_frames;
1971 max_period = max(This->mmdev_period_frames, This->alsa_period_frames);
1973 /* try to keep 3 ALSA periods or 3 MMDevAPI periods in the ALSA buffer and
1974 * no more */
1975 write_limit = 0;
1976 in_alsa = This->alsa_bufsize_frames - avail;
1977 while(in_alsa + write_limit < max_period * 3)
1978 write_limit += max_period;
1979 if(write_limit == 0)
1980 return;
1982 to_write = min(to_write, write_limit);
1984 /* Add a lead-in when starting with too few frames to ensure
1985 * continuous rendering. Additional benefit: Force ALSA to start.
1986 * GetPosition continues to reflect the speaker position because
1987 * snd_pcm_delay includes buffered frames in its total delay
1988 * and last_pos_frames prevents moving backwards. */
1989 if(!in_alsa && This->held_frames < This->hidden_frames){
1990 UINT32 s_frames = This->hidden_frames - This->held_frames;
1991 BYTE *silence = HeapAlloc(GetProcessHeap(), 0,
1992 s_frames * This->fmt->nBlockAlign);
1994 if(silence){
1995 in_alsa = alsa_write_best_effort(This->pcm_handle,
1996 silence, s_frames, This, TRUE);
1997 TRACE("lead-in %ld\n", in_alsa);
1998 HeapFree(GetProcessHeap(), 0, silence);
1999 if(in_alsa <= 0)
2000 return;
2001 }else
2002 WARN("Couldn't allocate lead-in, expect underrun\n");
2005 written = alsa_write_best_effort(This->pcm_handle, buf, to_write, This,
2006 This->session->mute);
2007 if(written < 0){
2008 WARN("Couldn't write: %ld (%s)\n", written, snd_strerror(written));
2009 return;
2012 This->lcl_offs_frames += written;
2013 This->lcl_offs_frames %= This->bufsize_frames;
2014 This->held_frames -= written;
2016 if(written < to_write){
2017 /* ALSA buffer probably full */
2018 return;
2021 if(This->held_frames && (written < write_limit)){
2022 /* wrapped and have some data back at the start to write */
2023 written = alsa_write_best_effort(This->pcm_handle, This->local_buffer,
2024 min(This->held_frames, write_limit - written), This,
2025 This->session->mute);
2026 if(written < 0){
2027 WARN("Couldn't write: %ld (%s)\n", written, snd_strerror(written));
2028 return;
2031 This->lcl_offs_frames += written;
2032 This->lcl_offs_frames %= This->bufsize_frames;
2033 This->held_frames -= written;
2037 static void alsa_read_data(ACImpl *This)
2039 snd_pcm_sframes_t pos, readable, nread;
2041 pos = (This->held_frames + This->lcl_offs_frames) % This->bufsize_frames;
2042 readable = This->bufsize_frames - pos;
2044 nread = snd_pcm_readi(This->pcm_handle,
2045 This->local_buffer + pos * This->fmt->nBlockAlign, readable);
2046 if(nread < 0){
2047 int ret;
2049 WARN("read failed, recovering: %ld (%s)\n", nread, snd_strerror(nread));
2051 ret = snd_pcm_recover(This->pcm_handle, nread, 0);
2052 if(ret < 0){
2053 WARN("Recover failed: %d (%s)\n", ret, snd_strerror(ret));
2054 return;
2057 nread = snd_pcm_readi(This->pcm_handle,
2058 This->local_buffer + pos * This->fmt->nBlockAlign, readable);
2059 if(nread < 0){
2060 WARN("read failed: %ld (%s)\n", nread, snd_strerror(nread));
2061 return;
2065 if(This->session->mute){
2066 int err;
2067 if((err = snd_pcm_format_set_silence(This->alsa_format,
2068 This->local_buffer + pos * This->fmt->nBlockAlign,
2069 nread)) < 0)
2070 WARN("Setting buffer to silence failed: %d (%s)\n", err,
2071 snd_strerror(err));
2074 This->held_frames += nread;
2076 if(This->held_frames > This->bufsize_frames){
2077 WARN("Overflow of unread data\n");
2078 This->lcl_offs_frames += This->held_frames;
2079 This->lcl_offs_frames %= This->bufsize_frames;
2080 This->held_frames = This->bufsize_frames;
2084 static void CALLBACK alsa_push_buffer_data(void *user, BOOLEAN timer)
2086 ACImpl *This = user;
2088 EnterCriticalSection(&This->lock);
2090 if(This->started){
2091 if(This->dataflow == eRender)
2092 alsa_write_data(This);
2093 else if(This->dataflow == eCapture)
2094 alsa_read_data(This);
2096 if(This->event)
2097 SetEvent(This->event);
2100 LeaveCriticalSection(&This->lock);
2103 static HRESULT WINAPI AudioClient_Start(IAudioClient *iface)
2105 ACImpl *This = impl_from_IAudioClient(iface);
2107 TRACE("(%p)\n", This);
2109 EnterCriticalSection(&This->lock);
2111 if(!This->initted){
2112 LeaveCriticalSection(&This->lock);
2113 return AUDCLNT_E_NOT_INITIALIZED;
2116 if((This->flags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK) && !This->event){
2117 LeaveCriticalSection(&This->lock);
2118 return AUDCLNT_E_EVENTHANDLE_NOT_SET;
2121 if(This->started){
2122 LeaveCriticalSection(&This->lock);
2123 return AUDCLNT_E_NOT_STOPPED;
2126 if(This->dataflow == eCapture){
2127 /* dump any data that might be leftover in the ALSA capture buffer */
2128 snd_pcm_readi(This->pcm_handle, This->local_buffer,
2129 This->bufsize_frames);
2132 if(!CreateTimerQueueTimer(&This->timer, g_timer_q, alsa_push_buffer_data,
2133 This, 0, This->mmdev_period_rt / 10000, WT_EXECUTEINTIMERTHREAD)){
2134 LeaveCriticalSection(&This->lock);
2135 WARN("Unable to create timer: %u\n", GetLastError());
2136 return E_OUTOFMEMORY;
2139 This->started = TRUE;
2141 LeaveCriticalSection(&This->lock);
2143 return S_OK;
2146 static HRESULT WINAPI AudioClient_Stop(IAudioClient *iface)
2148 ACImpl *This = impl_from_IAudioClient(iface);
2149 HANDLE event;
2150 BOOL wait;
2152 TRACE("(%p)\n", This);
2154 EnterCriticalSection(&This->lock);
2156 if(!This->initted){
2157 LeaveCriticalSection(&This->lock);
2158 return AUDCLNT_E_NOT_INITIALIZED;
2161 if(!This->started){
2162 LeaveCriticalSection(&This->lock);
2163 return S_FALSE;
2166 /* Stop without losing written frames or position.
2167 * snd_pcm_pause would be appropriate but is unsupported by dmix.
2168 * snd_pcm_drain yields EAGAIN in NONBLOCK mode, except with Pulse. */
2170 event = CreateEventW(NULL, TRUE, FALSE, NULL);
2171 wait = !DeleteTimerQueueTimer(g_timer_q, This->timer, event);
2172 if(wait)
2173 WARN("DeleteTimerQueueTimer error %u\n", GetLastError());
2174 wait = wait && GetLastError() == ERROR_IO_PENDING;
2176 This->started = FALSE;
2178 LeaveCriticalSection(&This->lock);
2180 if(event && wait)
2181 WaitForSingleObject(event, INFINITE);
2182 CloseHandle(event);
2184 return S_OK;
2187 static HRESULT WINAPI AudioClient_Reset(IAudioClient *iface)
2189 ACImpl *This = impl_from_IAudioClient(iface);
2191 TRACE("(%p)\n", This);
2193 EnterCriticalSection(&This->lock);
2195 if(!This->initted){
2196 LeaveCriticalSection(&This->lock);
2197 return AUDCLNT_E_NOT_INITIALIZED;
2200 if(This->started){
2201 LeaveCriticalSection(&This->lock);
2202 return AUDCLNT_E_NOT_STOPPED;
2205 if(This->getbuf_last){
2206 LeaveCriticalSection(&This->lock);
2207 return AUDCLNT_E_BUFFER_OPERATION_PENDING;
2210 if(snd_pcm_drop(This->pcm_handle) < 0)
2211 WARN("snd_pcm_drop failed\n");
2213 if(snd_pcm_reset(This->pcm_handle) < 0)
2214 WARN("snd_pcm_reset failed\n");
2216 if(snd_pcm_prepare(This->pcm_handle) < 0)
2217 WARN("snd_pcm_prepare failed\n");
2219 if(This->dataflow == eRender){
2220 This->written_frames = 0;
2221 This->last_pos_frames = 0;
2222 }else{
2223 This->written_frames += This->held_frames;
2225 This->held_frames = 0;
2226 This->lcl_offs_frames = 0;
2228 LeaveCriticalSection(&This->lock);
2230 return S_OK;
2233 static HRESULT WINAPI AudioClient_SetEventHandle(IAudioClient *iface,
2234 HANDLE event)
2236 ACImpl *This = impl_from_IAudioClient(iface);
2238 TRACE("(%p)->(%p)\n", This, event);
2240 if(!event)
2241 return E_INVALIDARG;
2243 EnterCriticalSection(&This->lock);
2245 if(!This->initted){
2246 LeaveCriticalSection(&This->lock);
2247 return AUDCLNT_E_NOT_INITIALIZED;
2250 if(!(This->flags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK)){
2251 LeaveCriticalSection(&This->lock);
2252 return AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED;
2255 This->event = event;
2257 LeaveCriticalSection(&This->lock);
2259 return S_OK;
2262 static HRESULT WINAPI AudioClient_GetService(IAudioClient *iface, REFIID riid,
2263 void **ppv)
2265 ACImpl *This = impl_from_IAudioClient(iface);
2267 TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(riid), ppv);
2269 if(!ppv)
2270 return E_POINTER;
2271 *ppv = NULL;
2273 EnterCriticalSection(&This->lock);
2275 if(!This->initted){
2276 LeaveCriticalSection(&This->lock);
2277 return AUDCLNT_E_NOT_INITIALIZED;
2280 if(IsEqualIID(riid, &IID_IAudioRenderClient)){
2281 if(This->dataflow != eRender){
2282 LeaveCriticalSection(&This->lock);
2283 return AUDCLNT_E_WRONG_ENDPOINT_TYPE;
2285 IAudioRenderClient_AddRef(&This->IAudioRenderClient_iface);
2286 *ppv = &This->IAudioRenderClient_iface;
2287 }else if(IsEqualIID(riid, &IID_IAudioCaptureClient)){
2288 if(This->dataflow != eCapture){
2289 LeaveCriticalSection(&This->lock);
2290 return AUDCLNT_E_WRONG_ENDPOINT_TYPE;
2292 IAudioCaptureClient_AddRef(&This->IAudioCaptureClient_iface);
2293 *ppv = &This->IAudioCaptureClient_iface;
2294 }else if(IsEqualIID(riid, &IID_IAudioClock)){
2295 IAudioClock_AddRef(&This->IAudioClock_iface);
2296 *ppv = &This->IAudioClock_iface;
2297 }else if(IsEqualIID(riid, &IID_IAudioStreamVolume)){
2298 IAudioStreamVolume_AddRef(&This->IAudioStreamVolume_iface);
2299 *ppv = &This->IAudioStreamVolume_iface;
2300 }else if(IsEqualIID(riid, &IID_IAudioSessionControl)){
2301 if(!This->session_wrapper){
2302 This->session_wrapper = AudioSessionWrapper_Create(This);
2303 if(!This->session_wrapper){
2304 LeaveCriticalSection(&This->lock);
2305 return E_OUTOFMEMORY;
2307 }else
2308 IAudioSessionControl2_AddRef(&This->session_wrapper->IAudioSessionControl2_iface);
2310 *ppv = &This->session_wrapper->IAudioSessionControl2_iface;
2311 }else if(IsEqualIID(riid, &IID_IChannelAudioVolume)){
2312 if(!This->session_wrapper){
2313 This->session_wrapper = AudioSessionWrapper_Create(This);
2314 if(!This->session_wrapper){
2315 LeaveCriticalSection(&This->lock);
2316 return E_OUTOFMEMORY;
2318 }else
2319 IChannelAudioVolume_AddRef(&This->session_wrapper->IChannelAudioVolume_iface);
2321 *ppv = &This->session_wrapper->IChannelAudioVolume_iface;
2322 }else if(IsEqualIID(riid, &IID_ISimpleAudioVolume)){
2323 if(!This->session_wrapper){
2324 This->session_wrapper = AudioSessionWrapper_Create(This);
2325 if(!This->session_wrapper){
2326 LeaveCriticalSection(&This->lock);
2327 return E_OUTOFMEMORY;
2329 }else
2330 ISimpleAudioVolume_AddRef(&This->session_wrapper->ISimpleAudioVolume_iface);
2332 *ppv = &This->session_wrapper->ISimpleAudioVolume_iface;
2335 if(*ppv){
2336 LeaveCriticalSection(&This->lock);
2337 return S_OK;
2340 LeaveCriticalSection(&This->lock);
2342 FIXME("stub %s\n", debugstr_guid(riid));
2343 return E_NOINTERFACE;
2346 static const IAudioClientVtbl AudioClient_Vtbl =
2348 AudioClient_QueryInterface,
2349 AudioClient_AddRef,
2350 AudioClient_Release,
2351 AudioClient_Initialize,
2352 AudioClient_GetBufferSize,
2353 AudioClient_GetStreamLatency,
2354 AudioClient_GetCurrentPadding,
2355 AudioClient_IsFormatSupported,
2356 AudioClient_GetMixFormat,
2357 AudioClient_GetDevicePeriod,
2358 AudioClient_Start,
2359 AudioClient_Stop,
2360 AudioClient_Reset,
2361 AudioClient_SetEventHandle,
2362 AudioClient_GetService
2365 static HRESULT WINAPI AudioRenderClient_QueryInterface(
2366 IAudioRenderClient *iface, REFIID riid, void **ppv)
2368 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2370 if(!ppv)
2371 return E_POINTER;
2372 *ppv = NULL;
2374 if(IsEqualIID(riid, &IID_IUnknown) ||
2375 IsEqualIID(riid, &IID_IAudioRenderClient))
2376 *ppv = iface;
2377 if(*ppv){
2378 IUnknown_AddRef((IUnknown*)*ppv);
2379 return S_OK;
2382 WARN("Unknown interface %s\n", debugstr_guid(riid));
2383 return E_NOINTERFACE;
2386 static ULONG WINAPI AudioRenderClient_AddRef(IAudioRenderClient *iface)
2388 ACImpl *This = impl_from_IAudioRenderClient(iface);
2389 return AudioClient_AddRef(&This->IAudioClient_iface);
2392 static ULONG WINAPI AudioRenderClient_Release(IAudioRenderClient *iface)
2394 ACImpl *This = impl_from_IAudioRenderClient(iface);
2395 return AudioClient_Release(&This->IAudioClient_iface);
2398 static HRESULT WINAPI AudioRenderClient_GetBuffer(IAudioRenderClient *iface,
2399 UINT32 frames, BYTE **data)
2401 ACImpl *This = impl_from_IAudioRenderClient(iface);
2402 UINT32 write_pos;
2404 TRACE("(%p)->(%u, %p)\n", This, frames, data);
2406 if(!data)
2407 return E_POINTER;
2408 *data = NULL;
2410 EnterCriticalSection(&This->lock);
2412 if(This->getbuf_last){
2413 LeaveCriticalSection(&This->lock);
2414 return AUDCLNT_E_OUT_OF_ORDER;
2417 if(!frames){
2418 LeaveCriticalSection(&This->lock);
2419 return S_OK;
2422 /* held_frames == GetCurrentPadding_nolock(); */
2423 if(This->held_frames + frames > This->bufsize_frames){
2424 LeaveCriticalSection(&This->lock);
2425 return AUDCLNT_E_BUFFER_TOO_LARGE;
2428 write_pos =
2429 (This->lcl_offs_frames + This->held_frames) % This->bufsize_frames;
2430 if(write_pos + frames > This->bufsize_frames){
2431 if(This->tmp_buffer_frames < frames){
2432 HeapFree(GetProcessHeap(), 0, This->tmp_buffer);
2433 This->tmp_buffer = HeapAlloc(GetProcessHeap(), 0,
2434 frames * This->fmt->nBlockAlign);
2435 if(!This->tmp_buffer){
2436 LeaveCriticalSection(&This->lock);
2437 return E_OUTOFMEMORY;
2439 This->tmp_buffer_frames = frames;
2441 *data = This->tmp_buffer;
2442 This->getbuf_last = -frames;
2443 }else{
2444 *data = This->local_buffer + write_pos * This->fmt->nBlockAlign;
2445 This->getbuf_last = frames;
2448 LeaveCriticalSection(&This->lock);
2450 return S_OK;
2453 static void alsa_wrap_buffer(ACImpl *This, BYTE *buffer, UINT32 written_frames)
2455 snd_pcm_uframes_t write_offs_frames =
2456 (This->lcl_offs_frames + This->held_frames) % This->bufsize_frames;
2457 UINT32 write_offs_bytes = write_offs_frames * This->fmt->nBlockAlign;
2458 snd_pcm_uframes_t chunk_frames = This->bufsize_frames - write_offs_frames;
2459 UINT32 chunk_bytes = chunk_frames * This->fmt->nBlockAlign;
2460 UINT32 written_bytes = written_frames * This->fmt->nBlockAlign;
2462 if(written_bytes <= chunk_bytes){
2463 memcpy(This->local_buffer + write_offs_bytes, buffer, written_bytes);
2464 }else{
2465 memcpy(This->local_buffer + write_offs_bytes, buffer, chunk_bytes);
2466 memcpy(This->local_buffer, buffer + chunk_bytes,
2467 written_bytes - chunk_bytes);
2471 static HRESULT WINAPI AudioRenderClient_ReleaseBuffer(
2472 IAudioRenderClient *iface, UINT32 written_frames, DWORD flags)
2474 ACImpl *This = impl_from_IAudioRenderClient(iface);
2475 BYTE *buffer;
2477 TRACE("(%p)->(%u, %x)\n", This, written_frames, flags);
2479 EnterCriticalSection(&This->lock);
2481 if(!written_frames){
2482 This->getbuf_last = 0;
2483 LeaveCriticalSection(&This->lock);
2484 return S_OK;
2487 if(!This->getbuf_last){
2488 LeaveCriticalSection(&This->lock);
2489 return AUDCLNT_E_OUT_OF_ORDER;
2492 if(written_frames > (This->getbuf_last >= 0 ? This->getbuf_last : -This->getbuf_last)){
2493 LeaveCriticalSection(&This->lock);
2494 return AUDCLNT_E_INVALID_SIZE;
2497 if(This->getbuf_last >= 0)
2498 buffer = This->local_buffer + This->fmt->nBlockAlign *
2499 ((This->lcl_offs_frames + This->held_frames) % This->bufsize_frames);
2500 else
2501 buffer = This->tmp_buffer;
2503 if(flags & AUDCLNT_BUFFERFLAGS_SILENT){
2504 if(This->fmt->wBitsPerSample == 8)
2505 memset(buffer, 128, written_frames * This->fmt->nBlockAlign);
2506 else
2507 memset(buffer, 0, written_frames * This->fmt->nBlockAlign);
2510 if(This->getbuf_last < 0)
2511 alsa_wrap_buffer(This, buffer, written_frames);
2513 This->held_frames += written_frames;
2514 This->written_frames += written_frames;
2515 This->getbuf_last = 0;
2517 LeaveCriticalSection(&This->lock);
2519 return S_OK;
2522 static const IAudioRenderClientVtbl AudioRenderClient_Vtbl = {
2523 AudioRenderClient_QueryInterface,
2524 AudioRenderClient_AddRef,
2525 AudioRenderClient_Release,
2526 AudioRenderClient_GetBuffer,
2527 AudioRenderClient_ReleaseBuffer
2530 static HRESULT WINAPI AudioCaptureClient_QueryInterface(
2531 IAudioCaptureClient *iface, REFIID riid, void **ppv)
2533 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2535 if(!ppv)
2536 return E_POINTER;
2537 *ppv = NULL;
2539 if(IsEqualIID(riid, &IID_IUnknown) ||
2540 IsEqualIID(riid, &IID_IAudioCaptureClient))
2541 *ppv = iface;
2542 if(*ppv){
2543 IUnknown_AddRef((IUnknown*)*ppv);
2544 return S_OK;
2547 WARN("Unknown interface %s\n", debugstr_guid(riid));
2548 return E_NOINTERFACE;
2551 static ULONG WINAPI AudioCaptureClient_AddRef(IAudioCaptureClient *iface)
2553 ACImpl *This = impl_from_IAudioCaptureClient(iface);
2554 return IAudioClient_AddRef(&This->IAudioClient_iface);
2557 static ULONG WINAPI AudioCaptureClient_Release(IAudioCaptureClient *iface)
2559 ACImpl *This = impl_from_IAudioCaptureClient(iface);
2560 return IAudioClient_Release(&This->IAudioClient_iface);
2563 static HRESULT WINAPI AudioCaptureClient_GetBuffer(IAudioCaptureClient *iface,
2564 BYTE **data, UINT32 *frames, DWORD *flags, UINT64 *devpos,
2565 UINT64 *qpcpos)
2567 ACImpl *This = impl_from_IAudioCaptureClient(iface);
2569 TRACE("(%p)->(%p, %p, %p, %p, %p)\n", This, data, frames, flags,
2570 devpos, qpcpos);
2572 if(!data || !frames || !flags)
2573 return E_POINTER;
2575 EnterCriticalSection(&This->lock);
2577 if(This->getbuf_last){
2578 LeaveCriticalSection(&This->lock);
2579 return AUDCLNT_E_OUT_OF_ORDER;
2582 /* hr = GetNextPacketSize(iface, frames); */
2583 if(This->held_frames < This->mmdev_period_frames){
2584 *frames = 0;
2585 LeaveCriticalSection(&This->lock);
2586 return AUDCLNT_S_BUFFER_EMPTY;
2588 *frames = This->mmdev_period_frames;
2590 if(This->lcl_offs_frames + *frames > This->bufsize_frames){
2591 UINT32 chunk_bytes, offs_bytes, frames_bytes;
2592 if(This->tmp_buffer_frames < *frames){
2593 HeapFree(GetProcessHeap(), 0, This->tmp_buffer);
2594 This->tmp_buffer = HeapAlloc(GetProcessHeap(), 0,
2595 *frames * This->fmt->nBlockAlign);
2596 if(!This->tmp_buffer){
2597 LeaveCriticalSection(&This->lock);
2598 return E_OUTOFMEMORY;
2600 This->tmp_buffer_frames = *frames;
2603 *data = This->tmp_buffer;
2604 chunk_bytes = (This->bufsize_frames - This->lcl_offs_frames) *
2605 This->fmt->nBlockAlign;
2606 offs_bytes = This->lcl_offs_frames * This->fmt->nBlockAlign;
2607 frames_bytes = *frames * This->fmt->nBlockAlign;
2608 memcpy(This->tmp_buffer, This->local_buffer + offs_bytes, chunk_bytes);
2609 memcpy(This->tmp_buffer + chunk_bytes, This->local_buffer,
2610 frames_bytes - chunk_bytes);
2611 }else
2612 *data = This->local_buffer +
2613 This->lcl_offs_frames * This->fmt->nBlockAlign;
2615 This->getbuf_last = *frames;
2616 *flags = 0;
2618 if(devpos)
2619 *devpos = This->written_frames;
2620 if(qpcpos){ /* fixme: qpc of recording time */
2621 LARGE_INTEGER stamp, freq;
2622 QueryPerformanceCounter(&stamp);
2623 QueryPerformanceFrequency(&freq);
2624 *qpcpos = (stamp.QuadPart * (INT64)10000000) / freq.QuadPart;
2627 LeaveCriticalSection(&This->lock);
2629 return *frames ? S_OK : AUDCLNT_S_BUFFER_EMPTY;
2632 static HRESULT WINAPI AudioCaptureClient_ReleaseBuffer(
2633 IAudioCaptureClient *iface, UINT32 done)
2635 ACImpl *This = impl_from_IAudioCaptureClient(iface);
2637 TRACE("(%p)->(%u)\n", This, done);
2639 EnterCriticalSection(&This->lock);
2641 if(!done){
2642 This->getbuf_last = 0;
2643 LeaveCriticalSection(&This->lock);
2644 return S_OK;
2647 if(!This->getbuf_last){
2648 LeaveCriticalSection(&This->lock);
2649 return AUDCLNT_E_OUT_OF_ORDER;
2652 if(This->getbuf_last != done){
2653 LeaveCriticalSection(&This->lock);
2654 return AUDCLNT_E_INVALID_SIZE;
2657 This->written_frames += done;
2658 This->held_frames -= done;
2659 This->lcl_offs_frames += done;
2660 This->lcl_offs_frames %= This->bufsize_frames;
2661 This->getbuf_last = 0;
2663 LeaveCriticalSection(&This->lock);
2665 return S_OK;
2668 static HRESULT WINAPI AudioCaptureClient_GetNextPacketSize(
2669 IAudioCaptureClient *iface, UINT32 *frames)
2671 ACImpl *This = impl_from_IAudioCaptureClient(iface);
2673 TRACE("(%p)->(%p)\n", This, frames);
2675 if(!frames)
2676 return E_POINTER;
2678 EnterCriticalSection(&This->lock);
2680 *frames = This->held_frames < This->mmdev_period_frames ? 0 : This->mmdev_period_frames;
2682 LeaveCriticalSection(&This->lock);
2684 return S_OK;
2687 static const IAudioCaptureClientVtbl AudioCaptureClient_Vtbl =
2689 AudioCaptureClient_QueryInterface,
2690 AudioCaptureClient_AddRef,
2691 AudioCaptureClient_Release,
2692 AudioCaptureClient_GetBuffer,
2693 AudioCaptureClient_ReleaseBuffer,
2694 AudioCaptureClient_GetNextPacketSize
2697 static HRESULT WINAPI AudioClock_QueryInterface(IAudioClock *iface,
2698 REFIID riid, void **ppv)
2700 ACImpl *This = impl_from_IAudioClock(iface);
2702 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2704 if(!ppv)
2705 return E_POINTER;
2706 *ppv = NULL;
2708 if(IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IAudioClock))
2709 *ppv = iface;
2710 else if(IsEqualIID(riid, &IID_IAudioClock2))
2711 *ppv = &This->IAudioClock2_iface;
2712 if(*ppv){
2713 IUnknown_AddRef((IUnknown*)*ppv);
2714 return S_OK;
2717 WARN("Unknown interface %s\n", debugstr_guid(riid));
2718 return E_NOINTERFACE;
2721 static ULONG WINAPI AudioClock_AddRef(IAudioClock *iface)
2723 ACImpl *This = impl_from_IAudioClock(iface);
2724 return IAudioClient_AddRef(&This->IAudioClient_iface);
2727 static ULONG WINAPI AudioClock_Release(IAudioClock *iface)
2729 ACImpl *This = impl_from_IAudioClock(iface);
2730 return IAudioClient_Release(&This->IAudioClient_iface);
2733 static HRESULT WINAPI AudioClock_GetFrequency(IAudioClock *iface, UINT64 *freq)
2735 ACImpl *This = impl_from_IAudioClock(iface);
2737 TRACE("(%p)->(%p)\n", This, freq);
2739 *freq = This->fmt->nSamplesPerSec;
2741 return S_OK;
2744 static HRESULT WINAPI AudioClock_GetPosition(IAudioClock *iface, UINT64 *pos,
2745 UINT64 *qpctime)
2747 ACImpl *This = impl_from_IAudioClock(iface);
2748 UINT64 written_frames, position;
2749 UINT32 held_frames;
2750 int err;
2751 snd_pcm_state_t alsa_state;
2752 snd_pcm_uframes_t avail_frames;
2753 snd_pcm_sframes_t delay_frames;
2755 TRACE("(%p)->(%p, %p)\n", This, pos, qpctime);
2757 if(!pos)
2758 return E_POINTER;
2760 EnterCriticalSection(&This->lock);
2762 /* call required to get accurate snd_pcm_state() */
2763 avail_frames = snd_pcm_avail_update(This->pcm_handle);
2764 alsa_state = snd_pcm_state(This->pcm_handle);
2765 written_frames = This->written_frames;
2766 held_frames = This->held_frames;
2768 err = snd_pcm_delay(This->pcm_handle, &delay_frames);
2769 if(err < 0){
2770 /* old Pulse, shortly after start */
2771 WARN("snd_pcm_delay failed in state %u: %d (%s)\n", alsa_state, err, snd_strerror(err));
2774 if(This->dataflow == eRender){
2775 position = written_frames - held_frames; /* maximum */
2776 if(!This->started || alsa_state > SND_PCM_STATE_RUNNING)
2777 ; /* mmdevapi stopped or ALSA underrun: pretend everything was played */
2778 else if(err<0 || delay_frames > position - This->last_pos_frames)
2779 /* Pulse bug: past underrun, despite recovery, avail_frames & delay
2780 * may be larger than alsa_bufsize_frames, as if cumulating frames. */
2781 /* Pulse bug: EIO(-5) shortly after starting: nothing played */
2782 position = This->last_pos_frames;
2783 else if(delay_frames > 0)
2784 position -= delay_frames;
2785 }else
2786 position = written_frames + held_frames;
2788 /* ensure monotic growth */
2789 This->last_pos_frames = position;
2791 LeaveCriticalSection(&This->lock);
2793 TRACE("frames written: %u, held: %u, avail: %ld, delay: %ld state %d, pos: %u\n",
2794 (UINT32)(written_frames%1000000000), held_frames,
2795 avail_frames, delay_frames, alsa_state, (UINT32)(position%1000000000));
2796 *pos = position;
2798 if(qpctime){
2799 LARGE_INTEGER stamp, freq;
2800 QueryPerformanceCounter(&stamp);
2801 QueryPerformanceFrequency(&freq);
2802 *qpctime = (stamp.QuadPart * (INT64)10000000) / freq.QuadPart;
2805 return S_OK;
2808 static HRESULT WINAPI AudioClock_GetCharacteristics(IAudioClock *iface,
2809 DWORD *chars)
2811 ACImpl *This = impl_from_IAudioClock(iface);
2813 TRACE("(%p)->(%p)\n", This, chars);
2815 if(!chars)
2816 return E_POINTER;
2818 *chars = AUDIOCLOCK_CHARACTERISTIC_FIXED_FREQ;
2820 return S_OK;
2823 static const IAudioClockVtbl AudioClock_Vtbl =
2825 AudioClock_QueryInterface,
2826 AudioClock_AddRef,
2827 AudioClock_Release,
2828 AudioClock_GetFrequency,
2829 AudioClock_GetPosition,
2830 AudioClock_GetCharacteristics
2833 static HRESULT WINAPI AudioClock2_QueryInterface(IAudioClock2 *iface,
2834 REFIID riid, void **ppv)
2836 ACImpl *This = impl_from_IAudioClock2(iface);
2837 return IAudioClock_QueryInterface(&This->IAudioClock_iface, riid, ppv);
2840 static ULONG WINAPI AudioClock2_AddRef(IAudioClock2 *iface)
2842 ACImpl *This = impl_from_IAudioClock2(iface);
2843 return IAudioClient_AddRef(&This->IAudioClient_iface);
2846 static ULONG WINAPI AudioClock2_Release(IAudioClock2 *iface)
2848 ACImpl *This = impl_from_IAudioClock2(iface);
2849 return IAudioClient_Release(&This->IAudioClient_iface);
2852 static HRESULT WINAPI AudioClock2_GetDevicePosition(IAudioClock2 *iface,
2853 UINT64 *pos, UINT64 *qpctime)
2855 ACImpl *This = impl_from_IAudioClock2(iface);
2857 FIXME("(%p)->(%p, %p)\n", This, pos, qpctime);
2859 return E_NOTIMPL;
2862 static const IAudioClock2Vtbl AudioClock2_Vtbl =
2864 AudioClock2_QueryInterface,
2865 AudioClock2_AddRef,
2866 AudioClock2_Release,
2867 AudioClock2_GetDevicePosition
2870 static AudioSessionWrapper *AudioSessionWrapper_Create(ACImpl *client)
2872 AudioSessionWrapper *ret;
2874 ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
2875 sizeof(AudioSessionWrapper));
2876 if(!ret)
2877 return NULL;
2879 ret->IAudioSessionControl2_iface.lpVtbl = &AudioSessionControl2_Vtbl;
2880 ret->ISimpleAudioVolume_iface.lpVtbl = &SimpleAudioVolume_Vtbl;
2881 ret->IChannelAudioVolume_iface.lpVtbl = &ChannelAudioVolume_Vtbl;
2883 ret->ref = 1;
2885 ret->client = client;
2886 if(client){
2887 ret->session = client->session;
2888 AudioClient_AddRef(&client->IAudioClient_iface);
2891 return ret;
2894 static HRESULT WINAPI AudioSessionControl_QueryInterface(
2895 IAudioSessionControl2 *iface, REFIID riid, void **ppv)
2897 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2899 if(!ppv)
2900 return E_POINTER;
2901 *ppv = NULL;
2903 if(IsEqualIID(riid, &IID_IUnknown) ||
2904 IsEqualIID(riid, &IID_IAudioSessionControl) ||
2905 IsEqualIID(riid, &IID_IAudioSessionControl2))
2906 *ppv = iface;
2907 if(*ppv){
2908 IUnknown_AddRef((IUnknown*)*ppv);
2909 return S_OK;
2912 WARN("Unknown interface %s\n", debugstr_guid(riid));
2913 return E_NOINTERFACE;
2916 static ULONG WINAPI AudioSessionControl_AddRef(IAudioSessionControl2 *iface)
2918 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2919 ULONG ref;
2920 ref = InterlockedIncrement(&This->ref);
2921 TRACE("(%p) Refcount now %u\n", This, ref);
2922 return ref;
2925 static ULONG WINAPI AudioSessionControl_Release(IAudioSessionControl2 *iface)
2927 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2928 ULONG ref;
2929 ref = InterlockedDecrement(&This->ref);
2930 TRACE("(%p) Refcount now %u\n", This, ref);
2931 if(!ref){
2932 if(This->client){
2933 EnterCriticalSection(&This->client->lock);
2934 This->client->session_wrapper = NULL;
2935 LeaveCriticalSection(&This->client->lock);
2936 AudioClient_Release(&This->client->IAudioClient_iface);
2938 HeapFree(GetProcessHeap(), 0, This);
2940 return ref;
2943 static HRESULT WINAPI AudioSessionControl_GetState(IAudioSessionControl2 *iface,
2944 AudioSessionState *state)
2946 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2947 ACImpl *client;
2949 TRACE("(%p)->(%p)\n", This, state);
2951 if(!state)
2952 return NULL_PTR_ERR;
2954 EnterCriticalSection(&g_sessions_lock);
2956 if(list_empty(&This->session->clients)){
2957 *state = AudioSessionStateExpired;
2958 LeaveCriticalSection(&g_sessions_lock);
2959 return S_OK;
2962 LIST_FOR_EACH_ENTRY(client, &This->session->clients, ACImpl, entry){
2963 EnterCriticalSection(&client->lock);
2964 if(client->started){
2965 *state = AudioSessionStateActive;
2966 LeaveCriticalSection(&client->lock);
2967 LeaveCriticalSection(&g_sessions_lock);
2968 return S_OK;
2970 LeaveCriticalSection(&client->lock);
2973 LeaveCriticalSection(&g_sessions_lock);
2975 *state = AudioSessionStateInactive;
2977 return S_OK;
2980 static HRESULT WINAPI AudioSessionControl_GetDisplayName(
2981 IAudioSessionControl2 *iface, WCHAR **name)
2983 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2985 FIXME("(%p)->(%p) - stub\n", This, name);
2987 return E_NOTIMPL;
2990 static HRESULT WINAPI AudioSessionControl_SetDisplayName(
2991 IAudioSessionControl2 *iface, const WCHAR *name, const GUID *session)
2993 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2995 FIXME("(%p)->(%p, %s) - stub\n", This, name, debugstr_guid(session));
2997 return E_NOTIMPL;
3000 static HRESULT WINAPI AudioSessionControl_GetIconPath(
3001 IAudioSessionControl2 *iface, WCHAR **path)
3003 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
3005 FIXME("(%p)->(%p) - stub\n", This, path);
3007 return E_NOTIMPL;
3010 static HRESULT WINAPI AudioSessionControl_SetIconPath(
3011 IAudioSessionControl2 *iface, const WCHAR *path, const GUID *session)
3013 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
3015 FIXME("(%p)->(%p, %s) - stub\n", This, path, debugstr_guid(session));
3017 return E_NOTIMPL;
3020 static HRESULT WINAPI AudioSessionControl_GetGroupingParam(
3021 IAudioSessionControl2 *iface, GUID *group)
3023 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
3025 FIXME("(%p)->(%p) - stub\n", This, group);
3027 return E_NOTIMPL;
3030 static HRESULT WINAPI AudioSessionControl_SetGroupingParam(
3031 IAudioSessionControl2 *iface, const GUID *group, const GUID *session)
3033 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
3035 FIXME("(%p)->(%s, %s) - stub\n", This, debugstr_guid(group),
3036 debugstr_guid(session));
3038 return E_NOTIMPL;
3041 static HRESULT WINAPI AudioSessionControl_RegisterAudioSessionNotification(
3042 IAudioSessionControl2 *iface, IAudioSessionEvents *events)
3044 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
3046 FIXME("(%p)->(%p) - stub\n", This, events);
3048 return S_OK;
3051 static HRESULT WINAPI AudioSessionControl_UnregisterAudioSessionNotification(
3052 IAudioSessionControl2 *iface, IAudioSessionEvents *events)
3054 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
3056 FIXME("(%p)->(%p) - stub\n", This, events);
3058 return S_OK;
3061 static HRESULT WINAPI AudioSessionControl_GetSessionIdentifier(
3062 IAudioSessionControl2 *iface, WCHAR **id)
3064 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
3066 FIXME("(%p)->(%p) - stub\n", This, id);
3068 return E_NOTIMPL;
3071 static HRESULT WINAPI AudioSessionControl_GetSessionInstanceIdentifier(
3072 IAudioSessionControl2 *iface, WCHAR **id)
3074 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
3076 FIXME("(%p)->(%p) - stub\n", This, id);
3078 return E_NOTIMPL;
3081 static HRESULT WINAPI AudioSessionControl_GetProcessId(
3082 IAudioSessionControl2 *iface, DWORD *pid)
3084 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
3086 TRACE("(%p)->(%p)\n", This, pid);
3088 if(!pid)
3089 return E_POINTER;
3091 *pid = GetCurrentProcessId();
3093 return S_OK;
3096 static HRESULT WINAPI AudioSessionControl_IsSystemSoundsSession(
3097 IAudioSessionControl2 *iface)
3099 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
3101 TRACE("(%p)\n", This);
3103 return S_FALSE;
3106 static HRESULT WINAPI AudioSessionControl_SetDuckingPreference(
3107 IAudioSessionControl2 *iface, BOOL optout)
3109 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
3111 TRACE("(%p)->(%d)\n", This, optout);
3113 return S_OK;
3116 static const IAudioSessionControl2Vtbl AudioSessionControl2_Vtbl =
3118 AudioSessionControl_QueryInterface,
3119 AudioSessionControl_AddRef,
3120 AudioSessionControl_Release,
3121 AudioSessionControl_GetState,
3122 AudioSessionControl_GetDisplayName,
3123 AudioSessionControl_SetDisplayName,
3124 AudioSessionControl_GetIconPath,
3125 AudioSessionControl_SetIconPath,
3126 AudioSessionControl_GetGroupingParam,
3127 AudioSessionControl_SetGroupingParam,
3128 AudioSessionControl_RegisterAudioSessionNotification,
3129 AudioSessionControl_UnregisterAudioSessionNotification,
3130 AudioSessionControl_GetSessionIdentifier,
3131 AudioSessionControl_GetSessionInstanceIdentifier,
3132 AudioSessionControl_GetProcessId,
3133 AudioSessionControl_IsSystemSoundsSession,
3134 AudioSessionControl_SetDuckingPreference
3137 static HRESULT WINAPI SimpleAudioVolume_QueryInterface(
3138 ISimpleAudioVolume *iface, REFIID riid, void **ppv)
3140 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
3142 if(!ppv)
3143 return E_POINTER;
3144 *ppv = NULL;
3146 if(IsEqualIID(riid, &IID_IUnknown) ||
3147 IsEqualIID(riid, &IID_ISimpleAudioVolume))
3148 *ppv = iface;
3149 if(*ppv){
3150 IUnknown_AddRef((IUnknown*)*ppv);
3151 return S_OK;
3154 WARN("Unknown interface %s\n", debugstr_guid(riid));
3155 return E_NOINTERFACE;
3158 static ULONG WINAPI SimpleAudioVolume_AddRef(ISimpleAudioVolume *iface)
3160 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
3161 return AudioSessionControl_AddRef(&This->IAudioSessionControl2_iface);
3164 static ULONG WINAPI SimpleAudioVolume_Release(ISimpleAudioVolume *iface)
3166 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
3167 return AudioSessionControl_Release(&This->IAudioSessionControl2_iface);
3170 static HRESULT WINAPI SimpleAudioVolume_SetMasterVolume(
3171 ISimpleAudioVolume *iface, float level, const GUID *context)
3173 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
3174 AudioSession *session = This->session;
3176 TRACE("(%p)->(%f, %s)\n", session, level, wine_dbgstr_guid(context));
3178 if(level < 0.f || level > 1.f)
3179 return E_INVALIDARG;
3181 if(context)
3182 FIXME("Notifications not supported yet\n");
3184 TRACE("ALSA does not support volume control\n");
3186 EnterCriticalSection(&session->lock);
3188 session->master_vol = level;
3190 LeaveCriticalSection(&session->lock);
3192 return S_OK;
3195 static HRESULT WINAPI SimpleAudioVolume_GetMasterVolume(
3196 ISimpleAudioVolume *iface, float *level)
3198 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
3199 AudioSession *session = This->session;
3201 TRACE("(%p)->(%p)\n", session, level);
3203 if(!level)
3204 return NULL_PTR_ERR;
3206 *level = session->master_vol;
3208 return S_OK;
3211 static HRESULT WINAPI SimpleAudioVolume_SetMute(ISimpleAudioVolume *iface,
3212 BOOL mute, const GUID *context)
3214 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
3215 AudioSession *session = This->session;
3217 TRACE("(%p)->(%u, %p)\n", session, mute, context);
3219 if(context)
3220 FIXME("Notifications not supported yet\n");
3222 session->mute = mute;
3224 return S_OK;
3227 static HRESULT WINAPI SimpleAudioVolume_GetMute(ISimpleAudioVolume *iface,
3228 BOOL *mute)
3230 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
3231 AudioSession *session = This->session;
3233 TRACE("(%p)->(%p)\n", session, mute);
3235 if(!mute)
3236 return NULL_PTR_ERR;
3238 *mute = session->mute;
3240 return S_OK;
3243 static const ISimpleAudioVolumeVtbl SimpleAudioVolume_Vtbl =
3245 SimpleAudioVolume_QueryInterface,
3246 SimpleAudioVolume_AddRef,
3247 SimpleAudioVolume_Release,
3248 SimpleAudioVolume_SetMasterVolume,
3249 SimpleAudioVolume_GetMasterVolume,
3250 SimpleAudioVolume_SetMute,
3251 SimpleAudioVolume_GetMute
3254 static HRESULT WINAPI AudioStreamVolume_QueryInterface(
3255 IAudioStreamVolume *iface, REFIID riid, void **ppv)
3257 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
3259 if(!ppv)
3260 return E_POINTER;
3261 *ppv = NULL;
3263 if(IsEqualIID(riid, &IID_IUnknown) ||
3264 IsEqualIID(riid, &IID_IAudioStreamVolume))
3265 *ppv = iface;
3266 if(*ppv){
3267 IUnknown_AddRef((IUnknown*)*ppv);
3268 return S_OK;
3271 WARN("Unknown interface %s\n", debugstr_guid(riid));
3272 return E_NOINTERFACE;
3275 static ULONG WINAPI AudioStreamVolume_AddRef(IAudioStreamVolume *iface)
3277 ACImpl *This = impl_from_IAudioStreamVolume(iface);
3278 return IAudioClient_AddRef(&This->IAudioClient_iface);
3281 static ULONG WINAPI AudioStreamVolume_Release(IAudioStreamVolume *iface)
3283 ACImpl *This = impl_from_IAudioStreamVolume(iface);
3284 return IAudioClient_Release(&This->IAudioClient_iface);
3287 static HRESULT WINAPI AudioStreamVolume_GetChannelCount(
3288 IAudioStreamVolume *iface, UINT32 *out)
3290 ACImpl *This = impl_from_IAudioStreamVolume(iface);
3292 TRACE("(%p)->(%p)\n", This, out);
3294 if(!out)
3295 return E_POINTER;
3297 *out = This->fmt->nChannels;
3299 return S_OK;
3302 static HRESULT WINAPI AudioStreamVolume_SetChannelVolume(
3303 IAudioStreamVolume *iface, UINT32 index, float level)
3305 ACImpl *This = impl_from_IAudioStreamVolume(iface);
3307 TRACE("(%p)->(%d, %f)\n", This, index, level);
3309 if(level < 0.f || level > 1.f)
3310 return E_INVALIDARG;
3312 if(index >= This->fmt->nChannels)
3313 return E_INVALIDARG;
3315 TRACE("ALSA does not support volume control\n");
3317 EnterCriticalSection(&This->lock);
3319 This->vols[index] = level;
3321 LeaveCriticalSection(&This->lock);
3323 return S_OK;
3326 static HRESULT WINAPI AudioStreamVolume_GetChannelVolume(
3327 IAudioStreamVolume *iface, UINT32 index, float *level)
3329 ACImpl *This = impl_from_IAudioStreamVolume(iface);
3331 TRACE("(%p)->(%d, %p)\n", This, index, level);
3333 if(!level)
3334 return E_POINTER;
3336 if(index >= This->fmt->nChannels)
3337 return E_INVALIDARG;
3339 *level = This->vols[index];
3341 return S_OK;
3344 static HRESULT WINAPI AudioStreamVolume_SetAllVolumes(
3345 IAudioStreamVolume *iface, UINT32 count, const float *levels)
3347 ACImpl *This = impl_from_IAudioStreamVolume(iface);
3348 int i;
3350 TRACE("(%p)->(%d, %p)\n", This, count, levels);
3352 if(!levels)
3353 return E_POINTER;
3355 if(count != This->fmt->nChannels)
3356 return E_INVALIDARG;
3358 TRACE("ALSA does not support volume control\n");
3360 EnterCriticalSection(&This->lock);
3362 for(i = 0; i < count; ++i)
3363 This->vols[i] = levels[i];
3365 LeaveCriticalSection(&This->lock);
3367 return S_OK;
3370 static HRESULT WINAPI AudioStreamVolume_GetAllVolumes(
3371 IAudioStreamVolume *iface, UINT32 count, float *levels)
3373 ACImpl *This = impl_from_IAudioStreamVolume(iface);
3374 int i;
3376 TRACE("(%p)->(%d, %p)\n", This, count, levels);
3378 if(!levels)
3379 return E_POINTER;
3381 if(count != This->fmt->nChannels)
3382 return E_INVALIDARG;
3384 EnterCriticalSection(&This->lock);
3386 for(i = 0; i < count; ++i)
3387 levels[i] = This->vols[i];
3389 LeaveCriticalSection(&This->lock);
3391 return S_OK;
3394 static const IAudioStreamVolumeVtbl AudioStreamVolume_Vtbl =
3396 AudioStreamVolume_QueryInterface,
3397 AudioStreamVolume_AddRef,
3398 AudioStreamVolume_Release,
3399 AudioStreamVolume_GetChannelCount,
3400 AudioStreamVolume_SetChannelVolume,
3401 AudioStreamVolume_GetChannelVolume,
3402 AudioStreamVolume_SetAllVolumes,
3403 AudioStreamVolume_GetAllVolumes
3406 static HRESULT WINAPI ChannelAudioVolume_QueryInterface(
3407 IChannelAudioVolume *iface, REFIID riid, void **ppv)
3409 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
3411 if(!ppv)
3412 return E_POINTER;
3413 *ppv = NULL;
3415 if(IsEqualIID(riid, &IID_IUnknown) ||
3416 IsEqualIID(riid, &IID_IChannelAudioVolume))
3417 *ppv = iface;
3418 if(*ppv){
3419 IUnknown_AddRef((IUnknown*)*ppv);
3420 return S_OK;
3423 WARN("Unknown interface %s\n", debugstr_guid(riid));
3424 return E_NOINTERFACE;
3427 static ULONG WINAPI ChannelAudioVolume_AddRef(IChannelAudioVolume *iface)
3429 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
3430 return AudioSessionControl_AddRef(&This->IAudioSessionControl2_iface);
3433 static ULONG WINAPI ChannelAudioVolume_Release(IChannelAudioVolume *iface)
3435 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
3436 return AudioSessionControl_Release(&This->IAudioSessionControl2_iface);
3439 static HRESULT WINAPI ChannelAudioVolume_GetChannelCount(
3440 IChannelAudioVolume *iface, UINT32 *out)
3442 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
3443 AudioSession *session = This->session;
3445 TRACE("(%p)->(%p)\n", session, out);
3447 if(!out)
3448 return NULL_PTR_ERR;
3450 *out = session->channel_count;
3452 return S_OK;
3455 static HRESULT WINAPI ChannelAudioVolume_SetChannelVolume(
3456 IChannelAudioVolume *iface, UINT32 index, float level,
3457 const GUID *context)
3459 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
3460 AudioSession *session = This->session;
3462 TRACE("(%p)->(%d, %f, %s)\n", session, index, level,
3463 wine_dbgstr_guid(context));
3465 if(level < 0.f || level > 1.f)
3466 return E_INVALIDARG;
3468 if(index >= session->channel_count)
3469 return E_INVALIDARG;
3471 if(context)
3472 FIXME("Notifications not supported yet\n");
3474 TRACE("ALSA does not support volume control\n");
3476 EnterCriticalSection(&session->lock);
3478 session->channel_vols[index] = level;
3480 LeaveCriticalSection(&session->lock);
3482 return S_OK;
3485 static HRESULT WINAPI ChannelAudioVolume_GetChannelVolume(
3486 IChannelAudioVolume *iface, UINT32 index, float *level)
3488 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
3489 AudioSession *session = This->session;
3491 TRACE("(%p)->(%d, %p)\n", session, index, level);
3493 if(!level)
3494 return NULL_PTR_ERR;
3496 if(index >= session->channel_count)
3497 return E_INVALIDARG;
3499 *level = session->channel_vols[index];
3501 return S_OK;
3504 static HRESULT WINAPI ChannelAudioVolume_SetAllVolumes(
3505 IChannelAudioVolume *iface, UINT32 count, const float *levels,
3506 const GUID *context)
3508 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
3509 AudioSession *session = This->session;
3510 int i;
3512 TRACE("(%p)->(%d, %p, %s)\n", session, count, levels,
3513 wine_dbgstr_guid(context));
3515 if(!levels)
3516 return NULL_PTR_ERR;
3518 if(count != session->channel_count)
3519 return E_INVALIDARG;
3521 if(context)
3522 FIXME("Notifications not supported yet\n");
3524 TRACE("ALSA does not support volume control\n");
3526 EnterCriticalSection(&session->lock);
3528 for(i = 0; i < count; ++i)
3529 session->channel_vols[i] = levels[i];
3531 LeaveCriticalSection(&session->lock);
3533 return S_OK;
3536 static HRESULT WINAPI ChannelAudioVolume_GetAllVolumes(
3537 IChannelAudioVolume *iface, UINT32 count, float *levels)
3539 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
3540 AudioSession *session = This->session;
3541 int i;
3543 TRACE("(%p)->(%d, %p)\n", session, count, levels);
3545 if(!levels)
3546 return NULL_PTR_ERR;
3548 if(count != session->channel_count)
3549 return E_INVALIDARG;
3551 for(i = 0; i < count; ++i)
3552 levels[i] = session->channel_vols[i];
3554 return S_OK;
3557 static const IChannelAudioVolumeVtbl ChannelAudioVolume_Vtbl =
3559 ChannelAudioVolume_QueryInterface,
3560 ChannelAudioVolume_AddRef,
3561 ChannelAudioVolume_Release,
3562 ChannelAudioVolume_GetChannelCount,
3563 ChannelAudioVolume_SetChannelVolume,
3564 ChannelAudioVolume_GetChannelVolume,
3565 ChannelAudioVolume_SetAllVolumes,
3566 ChannelAudioVolume_GetAllVolumes
3569 static HRESULT WINAPI AudioSessionManager_QueryInterface(IAudioSessionManager2 *iface,
3570 REFIID riid, void **ppv)
3572 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
3574 if(!ppv)
3575 return E_POINTER;
3576 *ppv = NULL;
3578 if(IsEqualIID(riid, &IID_IUnknown) ||
3579 IsEqualIID(riid, &IID_IAudioSessionManager) ||
3580 IsEqualIID(riid, &IID_IAudioSessionManager2))
3581 *ppv = iface;
3582 if(*ppv){
3583 IUnknown_AddRef((IUnknown*)*ppv);
3584 return S_OK;
3587 WARN("Unknown interface %s\n", debugstr_guid(riid));
3588 return E_NOINTERFACE;
3591 static ULONG WINAPI AudioSessionManager_AddRef(IAudioSessionManager2 *iface)
3593 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3594 ULONG ref;
3595 ref = InterlockedIncrement(&This->ref);
3596 TRACE("(%p) Refcount now %u\n", This, ref);
3597 return ref;
3600 static ULONG WINAPI AudioSessionManager_Release(IAudioSessionManager2 *iface)
3602 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3603 ULONG ref;
3604 ref = InterlockedDecrement(&This->ref);
3605 TRACE("(%p) Refcount now %u\n", This, ref);
3606 if(!ref)
3607 HeapFree(GetProcessHeap(), 0, This);
3608 return ref;
3611 static HRESULT WINAPI AudioSessionManager_GetAudioSessionControl(
3612 IAudioSessionManager2 *iface, const GUID *session_guid, DWORD flags,
3613 IAudioSessionControl **out)
3615 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3616 AudioSession *session;
3617 AudioSessionWrapper *wrapper;
3618 HRESULT hr;
3620 TRACE("(%p)->(%s, %x, %p)\n", This, debugstr_guid(session_guid),
3621 flags, out);
3623 hr = get_audio_session(session_guid, This->device, 0, &session);
3624 if(FAILED(hr))
3625 return hr;
3627 wrapper = AudioSessionWrapper_Create(NULL);
3628 if(!wrapper)
3629 return E_OUTOFMEMORY;
3631 wrapper->session = session;
3633 *out = (IAudioSessionControl*)&wrapper->IAudioSessionControl2_iface;
3635 return S_OK;
3638 static HRESULT WINAPI AudioSessionManager_GetSimpleAudioVolume(
3639 IAudioSessionManager2 *iface, const GUID *session_guid, DWORD flags,
3640 ISimpleAudioVolume **out)
3642 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3643 AudioSession *session;
3644 AudioSessionWrapper *wrapper;
3645 HRESULT hr;
3647 TRACE("(%p)->(%s, %x, %p)\n", This, debugstr_guid(session_guid),
3648 flags, out);
3650 hr = get_audio_session(session_guid, This->device, 0, &session);
3651 if(FAILED(hr))
3652 return hr;
3654 wrapper = AudioSessionWrapper_Create(NULL);
3655 if(!wrapper)
3656 return E_OUTOFMEMORY;
3658 wrapper->session = session;
3660 *out = &wrapper->ISimpleAudioVolume_iface;
3662 return S_OK;
3665 static HRESULT WINAPI AudioSessionManager_GetSessionEnumerator(
3666 IAudioSessionManager2 *iface, IAudioSessionEnumerator **out)
3668 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3669 FIXME("(%p)->(%p) - stub\n", This, out);
3670 return E_NOTIMPL;
3673 static HRESULT WINAPI AudioSessionManager_RegisterSessionNotification(
3674 IAudioSessionManager2 *iface, IAudioSessionNotification *notification)
3676 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3677 FIXME("(%p)->(%p) - stub\n", This, notification);
3678 return E_NOTIMPL;
3681 static HRESULT WINAPI AudioSessionManager_UnregisterSessionNotification(
3682 IAudioSessionManager2 *iface, IAudioSessionNotification *notification)
3684 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3685 FIXME("(%p)->(%p) - stub\n", This, notification);
3686 return E_NOTIMPL;
3689 static HRESULT WINAPI AudioSessionManager_RegisterDuckNotification(
3690 IAudioSessionManager2 *iface, const WCHAR *session_id,
3691 IAudioVolumeDuckNotification *notification)
3693 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3694 FIXME("(%p)->(%p) - stub\n", This, notification);
3695 return E_NOTIMPL;
3698 static HRESULT WINAPI AudioSessionManager_UnregisterDuckNotification(
3699 IAudioSessionManager2 *iface,
3700 IAudioVolumeDuckNotification *notification)
3702 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3703 FIXME("(%p)->(%p) - stub\n", This, notification);
3704 return E_NOTIMPL;
3707 static const IAudioSessionManager2Vtbl AudioSessionManager2_Vtbl =
3709 AudioSessionManager_QueryInterface,
3710 AudioSessionManager_AddRef,
3711 AudioSessionManager_Release,
3712 AudioSessionManager_GetAudioSessionControl,
3713 AudioSessionManager_GetSimpleAudioVolume,
3714 AudioSessionManager_GetSessionEnumerator,
3715 AudioSessionManager_RegisterSessionNotification,
3716 AudioSessionManager_UnregisterSessionNotification,
3717 AudioSessionManager_RegisterDuckNotification,
3718 AudioSessionManager_UnregisterDuckNotification
3721 HRESULT WINAPI AUDDRV_GetAudioSessionManager(IMMDevice *device,
3722 IAudioSessionManager2 **out)
3724 SessionMgr *This;
3726 This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(SessionMgr));
3727 if(!This)
3728 return E_OUTOFMEMORY;
3730 This->IAudioSessionManager2_iface.lpVtbl = &AudioSessionManager2_Vtbl;
3731 This->device = device;
3732 This->ref = 1;
3734 *out = &This->IAudioSessionManager2_iface;
3736 return S_OK;