wininet/tests: Fix a couple of test failures on Internet Explorer 11.
[wine/wine-gecko.git] / dlls / winealsa.drv / mmdevdrv.c
blob5e40d6e01360ee656e85ff4f4e42eff48dccbad5
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 "mmsystem.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;
103 IUnknown *pUnkFTMarshal;
105 EDataFlow dataflow;
106 WAVEFORMATEX *fmt;
107 DWORD flags;
108 AUDCLNT_SHAREMODE share;
109 HANDLE event;
110 float *vols;
112 BOOL need_remapping;
113 int alsa_channels;
114 int alsa_channel_map[32];
116 BOOL initted, started;
117 REFERENCE_TIME mmdev_period_rt;
118 UINT64 written_frames, last_pos_frames;
119 UINT32 bufsize_frames, held_frames, tmp_buffer_frames, mmdev_period_frames;
120 snd_pcm_uframes_t remapping_buf_frames;
121 UINT32 lcl_offs_frames; /* offs into local_buffer where valid data starts */
122 UINT32 wri_offs_frames; /* where to write fresh data in local_buffer */
123 UINT32 hidden_frames; /* ALSA reserve to ensure continuous rendering */
125 HANDLE timer;
126 BYTE *local_buffer, *tmp_buffer, *remapping_buf;
127 LONG32 getbuf_last; /* <0 when using tmp_buffer */
129 CRITICAL_SECTION lock;
131 AudioSession *session;
132 AudioSessionWrapper *session_wrapper;
134 struct list entry;
137 typedef struct _SessionMgr {
138 IAudioSessionManager2 IAudioSessionManager2_iface;
140 LONG ref;
142 IMMDevice *device;
143 } SessionMgr;
145 static HANDLE g_timer_q;
147 static CRITICAL_SECTION g_sessions_lock;
148 static CRITICAL_SECTION_DEBUG g_sessions_lock_debug =
150 0, 0, &g_sessions_lock,
151 { &g_sessions_lock_debug.ProcessLocksList, &g_sessions_lock_debug.ProcessLocksList },
152 0, 0, { (DWORD_PTR)(__FILE__ ": g_sessions_lock") }
154 static CRITICAL_SECTION g_sessions_lock = { &g_sessions_lock_debug, -1, 0, 0, 0, 0 };
155 static struct list g_sessions = LIST_INIT(g_sessions);
157 static const WCHAR defaultW[] = {'d','e','f','a','u','l','t',0};
158 static const char defname[] = "default";
160 static const WCHAR drv_keyW[] = {'S','o','f','t','w','a','r','e','\\',
161 'W','i','n','e','\\','D','r','i','v','e','r','s','\\',
162 'w','i','n','e','a','l','s','a','.','d','r','v',0};
163 static const WCHAR drv_key_devicesW[] = {'S','o','f','t','w','a','r','e','\\',
164 'W','i','n','e','\\','D','r','i','v','e','r','s','\\',
165 'w','i','n','e','a','l','s','a','.','d','r','v','\\','d','e','v','i','c','e','s',0};
166 static const WCHAR guidW[] = {'g','u','i','d',0};
168 static const IAudioClientVtbl AudioClient_Vtbl;
169 static const IAudioRenderClientVtbl AudioRenderClient_Vtbl;
170 static const IAudioCaptureClientVtbl AudioCaptureClient_Vtbl;
171 static const IAudioSessionControl2Vtbl AudioSessionControl2_Vtbl;
172 static const ISimpleAudioVolumeVtbl SimpleAudioVolume_Vtbl;
173 static const IAudioClockVtbl AudioClock_Vtbl;
174 static const IAudioClock2Vtbl AudioClock2_Vtbl;
175 static const IAudioStreamVolumeVtbl AudioStreamVolume_Vtbl;
176 static const IChannelAudioVolumeVtbl ChannelAudioVolume_Vtbl;
177 static const IAudioSessionManager2Vtbl AudioSessionManager2_Vtbl;
179 static AudioSessionWrapper *AudioSessionWrapper_Create(ACImpl *client);
181 static inline ACImpl *impl_from_IAudioClient(IAudioClient *iface)
183 return CONTAINING_RECORD(iface, ACImpl, IAudioClient_iface);
186 static inline ACImpl *impl_from_IAudioRenderClient(IAudioRenderClient *iface)
188 return CONTAINING_RECORD(iface, ACImpl, IAudioRenderClient_iface);
191 static inline ACImpl *impl_from_IAudioCaptureClient(IAudioCaptureClient *iface)
193 return CONTAINING_RECORD(iface, ACImpl, IAudioCaptureClient_iface);
196 static inline AudioSessionWrapper *impl_from_IAudioSessionControl2(IAudioSessionControl2 *iface)
198 return CONTAINING_RECORD(iface, AudioSessionWrapper, IAudioSessionControl2_iface);
201 static inline AudioSessionWrapper *impl_from_ISimpleAudioVolume(ISimpleAudioVolume *iface)
203 return CONTAINING_RECORD(iface, AudioSessionWrapper, ISimpleAudioVolume_iface);
206 static inline AudioSessionWrapper *impl_from_IChannelAudioVolume(IChannelAudioVolume *iface)
208 return CONTAINING_RECORD(iface, AudioSessionWrapper, IChannelAudioVolume_iface);
211 static inline ACImpl *impl_from_IAudioClock(IAudioClock *iface)
213 return CONTAINING_RECORD(iface, ACImpl, IAudioClock_iface);
216 static inline ACImpl *impl_from_IAudioClock2(IAudioClock2 *iface)
218 return CONTAINING_RECORD(iface, ACImpl, IAudioClock2_iface);
221 static inline ACImpl *impl_from_IAudioStreamVolume(IAudioStreamVolume *iface)
223 return CONTAINING_RECORD(iface, ACImpl, IAudioStreamVolume_iface);
226 static inline SessionMgr *impl_from_IAudioSessionManager2(IAudioSessionManager2 *iface)
228 return CONTAINING_RECORD(iface, SessionMgr, IAudioSessionManager2_iface);
231 BOOL WINAPI DllMain(HINSTANCE dll, DWORD reason, void *reserved)
233 switch (reason)
235 case DLL_PROCESS_ATTACH:
236 g_timer_q = CreateTimerQueue();
237 if(!g_timer_q)
238 return FALSE;
239 break;
241 case DLL_PROCESS_DETACH:
242 if (reserved) break;
243 DeleteCriticalSection(&g_sessions_lock);
244 break;
246 return TRUE;
249 /* From <dlls/mmdevapi/mmdevapi.h> */
250 enum DriverPriority {
251 Priority_Unavailable = 0,
252 Priority_Low,
253 Priority_Neutral,
254 Priority_Preferred
257 int WINAPI AUDDRV_GetPriority(void)
259 return Priority_Neutral;
262 static void set_device_guid(EDataFlow flow, HKEY drv_key, const WCHAR *key_name,
263 GUID *guid)
265 HKEY key;
266 BOOL opened = FALSE;
267 LONG lr;
269 if(!drv_key){
270 lr = RegCreateKeyExW(HKEY_CURRENT_USER, drv_key_devicesW, 0, NULL, 0, KEY_WRITE,
271 NULL, &drv_key, NULL);
272 if(lr != ERROR_SUCCESS){
273 ERR("RegCreateKeyEx(drv_key) failed: %u\n", lr);
274 return;
276 opened = TRUE;
279 lr = RegCreateKeyExW(drv_key, key_name, 0, NULL, 0, KEY_WRITE,
280 NULL, &key, NULL);
281 if(lr != ERROR_SUCCESS){
282 ERR("RegCreateKeyEx(%s) failed: %u\n", wine_dbgstr_w(key_name), lr);
283 goto exit;
286 lr = RegSetValueExW(key, guidW, 0, REG_BINARY, (BYTE*)guid,
287 sizeof(GUID));
288 if(lr != ERROR_SUCCESS)
289 ERR("RegSetValueEx(%s\\guid) failed: %u\n", wine_dbgstr_w(key_name), lr);
291 RegCloseKey(key);
292 exit:
293 if(opened)
294 RegCloseKey(drv_key);
297 static void get_device_guid(EDataFlow flow, const char *device, GUID *guid)
299 HKEY key = NULL, dev_key;
300 DWORD type, size = sizeof(*guid);
301 WCHAR key_name[256];
303 if(flow == eCapture)
304 key_name[0] = '1';
305 else
306 key_name[0] = '0';
307 key_name[1] = ',';
308 MultiByteToWideChar(CP_UNIXCP, 0, device, -1, key_name + 2,
309 (sizeof(key_name) / sizeof(*key_name)) - 2);
311 if(RegOpenKeyExW(HKEY_CURRENT_USER, drv_key_devicesW, 0, KEY_WRITE|KEY_READ, &key) == ERROR_SUCCESS){
312 if(RegOpenKeyExW(key, key_name, 0, KEY_READ, &dev_key) == ERROR_SUCCESS){
313 if(RegQueryValueExW(dev_key, guidW, 0, &type,
314 (BYTE*)guid, &size) == ERROR_SUCCESS){
315 if(type == REG_BINARY){
316 RegCloseKey(dev_key);
317 RegCloseKey(key);
318 return;
320 ERR("Invalid type for device %s GUID: %u; ignoring and overwriting\n",
321 wine_dbgstr_w(key_name), type);
323 RegCloseKey(dev_key);
327 CoCreateGuid(guid);
329 set_device_guid(flow, key, key_name, guid);
331 if(key)
332 RegCloseKey(key);
335 static BOOL alsa_try_open(const char *devnode, snd_pcm_stream_t stream)
337 snd_pcm_t *handle;
338 int err;
340 TRACE("devnode: %s, stream: %d\n", devnode, stream);
342 if((err = snd_pcm_open(&handle, devnode, stream, SND_PCM_NONBLOCK)) < 0){
343 WARN("The device \"%s\" failed to open: %d (%s).\n",
344 devnode, err, snd_strerror(err));
345 return FALSE;
348 snd_pcm_close(handle);
349 return TRUE;
352 static WCHAR *construct_device_id(EDataFlow flow, const WCHAR *chunk1, const char *chunk2)
354 WCHAR *ret;
355 const WCHAR *prefix;
356 DWORD len_wchars = 0, chunk1_len, copied = 0, prefix_len;
358 static const WCHAR dashW[] = {' ','-',' ',0};
359 static const size_t dashW_len = (sizeof(dashW) / sizeof(*dashW)) - 1;
360 static const WCHAR outW[] = {'O','u','t',':',' ',0};
361 static const WCHAR inW[] = {'I','n',':',' ',0};
363 if(flow == eRender){
364 prefix = outW;
365 prefix_len = (sizeof(outW) / sizeof(*outW)) - 1;
366 len_wchars += prefix_len;
367 }else{
368 prefix = inW;
369 prefix_len = (sizeof(inW) / sizeof(*inW)) - 1;
370 len_wchars += prefix_len;
372 if(chunk1){
373 chunk1_len = strlenW(chunk1);
374 len_wchars += chunk1_len;
376 if(chunk1 && chunk2)
377 len_wchars += dashW_len;
378 if(chunk2)
379 len_wchars += MultiByteToWideChar(CP_UNIXCP, 0, chunk2, -1, NULL, 0) - 1;
380 len_wchars += 1; /* NULL byte */
382 ret = HeapAlloc(GetProcessHeap(), 0, len_wchars * sizeof(WCHAR));
384 memcpy(ret, prefix, prefix_len * sizeof(WCHAR));
385 copied += prefix_len;
386 if(chunk1){
387 memcpy(ret + copied, chunk1, chunk1_len * sizeof(WCHAR));
388 copied += chunk1_len;
390 if(chunk1 && chunk2){
391 memcpy(ret + copied, dashW, dashW_len * sizeof(WCHAR));
392 copied += dashW_len;
394 if(chunk2){
395 MultiByteToWideChar(CP_UNIXCP, 0, chunk2, -1, ret + copied, len_wchars - copied);
396 }else
397 ret[copied] = 0;
399 TRACE("Enumerated device: %s\n", wine_dbgstr_w(ret));
401 return ret;
404 static HRESULT alsa_get_card_devices(EDataFlow flow, snd_pcm_stream_t stream,
405 WCHAR ***ids, GUID **guids, UINT *num, snd_ctl_t *ctl, int card,
406 const WCHAR *cardnameW)
408 int err, device;
409 snd_pcm_info_t *info;
411 info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, snd_pcm_info_sizeof());
412 if(!info)
413 return E_OUTOFMEMORY;
415 snd_pcm_info_set_subdevice(info, 0);
416 snd_pcm_info_set_stream(info, stream);
418 device = -1;
419 for(err = snd_ctl_pcm_next_device(ctl, &device); device != -1 && err >= 0;
420 err = snd_ctl_pcm_next_device(ctl, &device)){
421 const char *devname;
422 char devnode[32];
424 snd_pcm_info_set_device(info, device);
426 if((err = snd_ctl_pcm_info(ctl, info)) < 0){
427 if(err == -ENOENT)
428 /* This device doesn't have the right stream direction */
429 continue;
431 WARN("Failed to get info for card %d, device %d: %d (%s)\n",
432 card, device, err, snd_strerror(err));
433 continue;
436 sprintf(devnode, "plughw:%d,%d", card, device);
437 if(!alsa_try_open(devnode, stream))
438 continue;
440 if(*num){
441 *ids = HeapReAlloc(GetProcessHeap(), 0, *ids, sizeof(WCHAR *) * (*num + 1));
442 *guids = HeapReAlloc(GetProcessHeap(), 0, *guids, sizeof(GUID) * (*num + 1));
443 }else{
444 *ids = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR *));
445 *guids = HeapAlloc(GetProcessHeap(), 0, sizeof(GUID));
448 devname = snd_pcm_info_get_name(info);
449 if(!devname){
450 WARN("Unable to get device name for card %d, device %d\n", card,
451 device);
452 continue;
455 (*ids)[*num] = construct_device_id(flow, cardnameW, devname);
456 get_device_guid(flow, devnode, &(*guids)[*num]);
458 ++(*num);
461 HeapFree(GetProcessHeap(), 0, info);
463 if(err != 0)
464 WARN("Got a failure during device enumeration on card %d: %d (%s)\n",
465 card, err, snd_strerror(err));
467 return S_OK;
470 static void get_reg_devices(EDataFlow flow, snd_pcm_stream_t stream, WCHAR ***ids,
471 GUID **guids, UINT *num)
473 static const WCHAR ALSAOutputDevices[] = {'A','L','S','A','O','u','t','p','u','t','D','e','v','i','c','e','s',0};
474 static const WCHAR ALSAInputDevices[] = {'A','L','S','A','I','n','p','u','t','D','e','v','i','c','e','s',0};
475 HKEY key;
476 WCHAR reg_devices[256];
477 DWORD size = sizeof(reg_devices), type;
478 const WCHAR *value_name = (stream == SND_PCM_STREAM_PLAYBACK) ? ALSAOutputDevices : ALSAInputDevices;
480 /* @@ Wine registry key: HKCU\Software\Wine\Drivers\winealsa.drv */
481 if(RegOpenKeyW(HKEY_CURRENT_USER, drv_keyW, &key) == ERROR_SUCCESS){
482 if(RegQueryValueExW(key, value_name, 0, &type,
483 (BYTE*)reg_devices, &size) == ERROR_SUCCESS){
484 WCHAR *p = reg_devices;
486 if(type != REG_MULTI_SZ){
487 ERR("Registry ALSA device list value type must be REG_MULTI_SZ\n");
488 RegCloseKey(key);
489 return;
492 while(*p){
493 char devname[64];
495 WideCharToMultiByte(CP_UNIXCP, 0, p, -1, devname, sizeof(devname), NULL, NULL);
497 if(alsa_try_open(devname, stream)){
498 if(*num){
499 *ids = HeapReAlloc(GetProcessHeap(), 0, *ids, sizeof(WCHAR *) * (*num + 1));
500 *guids = HeapReAlloc(GetProcessHeap(), 0, *guids, sizeof(GUID) * (*num + 1));
501 }else{
502 *ids = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR *));
503 *guids = HeapAlloc(GetProcessHeap(), 0, sizeof(GUID));
505 (*ids)[*num] = construct_device_id(flow, p, NULL);
506 get_device_guid(flow, devname, &(*guids)[*num]);
507 ++*num;
510 p += lstrlenW(p) + 1;
514 RegCloseKey(key);
518 static HRESULT alsa_enum_devices(EDataFlow flow, WCHAR ***ids, GUID **guids,
519 UINT *num)
521 snd_pcm_stream_t stream = (flow == eRender ? SND_PCM_STREAM_PLAYBACK :
522 SND_PCM_STREAM_CAPTURE);
523 int err, card;
525 card = -1;
526 *num = 0;
528 if(alsa_try_open(defname, stream)){
529 *ids = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR *));
530 (*ids)[0] = construct_device_id(flow, defaultW, NULL);
531 *guids = HeapAlloc(GetProcessHeap(), 0, sizeof(GUID));
532 get_device_guid(flow, defname, &(*guids)[0]);
533 ++*num;
536 get_reg_devices(flow, stream, ids, guids, num);
538 for(err = snd_card_next(&card); card != -1 && err >= 0;
539 err = snd_card_next(&card)){
540 char cardpath[64];
541 char *cardname;
542 WCHAR *cardnameW;
543 snd_ctl_t *ctl;
544 DWORD len;
546 sprintf(cardpath, "hw:%u", card);
548 if((err = snd_ctl_open(&ctl, cardpath, 0)) < 0){
549 WARN("Unable to open ctl for ALSA device %s: %d (%s)\n", cardpath,
550 err, snd_strerror(err));
551 continue;
554 if(snd_card_get_name(card, &cardname) < 0) {
555 /* FIXME: Should be localized */
556 static const WCHAR nameW[] = {'U','n','k','n','o','w','n',' ','s','o','u','n','d','c','a','r','d',0};
557 WARN("Unable to get card name for ALSA device %s: %d (%s)\n",
558 cardpath, err, snd_strerror(err));
559 alsa_get_card_devices(flow, stream, ids, guids, num, ctl, card, nameW);
560 }else{
561 len = MultiByteToWideChar(CP_UNIXCP, 0, cardname, -1, NULL, 0);
562 cardnameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
564 if(!cardnameW){
565 free(cardname);
566 snd_ctl_close(ctl);
567 return E_OUTOFMEMORY;
569 MultiByteToWideChar(CP_UNIXCP, 0, cardname, -1, cardnameW, len);
571 alsa_get_card_devices(flow, stream, ids, guids, num, ctl, card, cardnameW);
573 HeapFree(GetProcessHeap(), 0, cardnameW);
574 free(cardname);
577 snd_ctl_close(ctl);
580 if(err != 0)
581 WARN("Got a failure during card enumeration: %d (%s)\n",
582 err, snd_strerror(err));
584 return S_OK;
587 HRESULT WINAPI AUDDRV_GetEndpointIDs(EDataFlow flow, WCHAR ***ids, GUID **guids,
588 UINT *num, UINT *def_index)
590 HRESULT hr;
592 TRACE("%d %p %p %p %p\n", flow, ids, guids, num, def_index);
594 *ids = NULL;
595 *guids = NULL;
597 hr = alsa_enum_devices(flow, ids, guids, num);
598 if(FAILED(hr)){
599 UINT i;
600 for(i = 0; i < *num; ++i)
601 HeapFree(GetProcessHeap(), 0, (*ids)[i]);
602 HeapFree(GetProcessHeap(), 0, *ids);
603 HeapFree(GetProcessHeap(), 0, *guids);
604 return E_OUTOFMEMORY;
607 TRACE("Enumerated %u devices\n", *num);
609 if(*num == 0){
610 HeapFree(GetProcessHeap(), 0, *ids);
611 *ids = NULL;
612 HeapFree(GetProcessHeap(), 0, *guids);
613 *guids = NULL;
616 *def_index = 0;
618 return S_OK;
621 /* Using the pulse PCM device from alsa-plugins 1.0.24 triggers a bug
622 * which causes audio to cease playing after a few minutes of playback.
623 * Setting handle_underrun=1 on pulse-backed ALSA devices seems to work
624 * around this issue. */
625 static snd_config_t *make_handle_underrun_config(const char *name)
627 snd_config_t *lconf, *dev_node, *hu_node, *type_node;
628 char dev_node_name[64];
629 const char *type_str;
630 int err;
632 snd_config_update();
634 if((err = snd_config_copy(&lconf, snd_config)) < 0){
635 WARN("snd_config_copy failed: %d (%s)\n", err, snd_strerror(err));
636 return NULL;
639 sprintf(dev_node_name, "pcm.%s", name);
640 err = snd_config_search(lconf, dev_node_name, &dev_node);
641 if(err == -ENOENT){
642 snd_config_delete(lconf);
643 return NULL;
645 if(err < 0){
646 snd_config_delete(lconf);
647 WARN("snd_config_search failed: %d (%s)\n", err, snd_strerror(err));
648 return NULL;
651 /* ALSA is extremely fragile. If it runs into a config setting it doesn't
652 * recognize, it tends to fail or assert. So we only want to inject
653 * handle_underrun=1 on devices that we know will recognize it. */
654 err = snd_config_search(dev_node, "type", &type_node);
655 if(err == -ENOENT){
656 snd_config_delete(lconf);
657 return NULL;
659 if(err < 0){
660 snd_config_delete(lconf);
661 WARN("snd_config_search failed: %d (%s)\n", err, snd_strerror(err));
662 return NULL;
665 if((err = snd_config_get_string(type_node, &type_str)) < 0){
666 snd_config_delete(lconf);
667 return NULL;
670 if(strcmp(type_str, "pulse") != 0){
671 snd_config_delete(lconf);
672 return NULL;
675 err = snd_config_search(dev_node, "handle_underrun", &hu_node);
676 if(err >= 0){
677 /* user already has an explicit handle_underrun setting, so don't
678 * use a local config */
679 snd_config_delete(lconf);
680 return NULL;
682 if(err != -ENOENT){
683 snd_config_delete(lconf);
684 WARN("snd_config_search failed: %d (%s)\n", err, snd_strerror(err));
685 return NULL;
688 if((err = snd_config_imake_integer(&hu_node, "handle_underrun", 1)) < 0){
689 snd_config_delete(lconf);
690 WARN("snd_config_imake_integer failed: %d (%s)\n", err,
691 snd_strerror(err));
692 return NULL;
695 if((err = snd_config_add(dev_node, hu_node)) < 0){
696 snd_config_delete(lconf);
697 WARN("snd_config_add failed: %d (%s)\n", err, snd_strerror(err));
698 return NULL;
701 return lconf;
704 static BOOL get_alsa_name_by_guid(GUID *guid, char *name, DWORD name_size, EDataFlow *flow)
706 HKEY devices_key;
707 UINT i = 0;
708 WCHAR key_name[256];
709 DWORD key_name_size;
711 if(RegOpenKeyExW(HKEY_CURRENT_USER, drv_key_devicesW, 0, KEY_READ, &devices_key) != ERROR_SUCCESS){
712 ERR("No devices found in registry?\n");
713 return FALSE;
716 while(1){
717 HKEY key;
718 DWORD size, type;
719 GUID reg_guid;
721 key_name_size = sizeof(key_name)/sizeof(WCHAR);
722 if(RegEnumKeyExW(devices_key, i, key_name, &key_name_size, NULL,
723 NULL, NULL, NULL) != ERROR_SUCCESS)
724 break;
726 if(RegOpenKeyExW(devices_key, key_name, 0, KEY_READ, &key) != ERROR_SUCCESS){
727 WARN("Couldn't open key: %s\n", wine_dbgstr_w(key_name));
728 continue;
731 size = sizeof(reg_guid);
732 if(RegQueryValueExW(key, guidW, 0, &type,
733 (BYTE*)&reg_guid, &size) == ERROR_SUCCESS){
734 if(IsEqualGUID(&reg_guid, guid)){
735 RegCloseKey(key);
736 RegCloseKey(devices_key);
738 TRACE("Found matching device key: %s\n", wine_dbgstr_w(key_name));
740 if(key_name[0] == '0')
741 *flow = eRender;
742 else if(key_name[0] == '1')
743 *flow = eCapture;
744 else{
745 ERR("Unknown device type: %c\n", key_name[0]);
746 return FALSE;
749 WideCharToMultiByte(CP_UNIXCP, 0, key_name + 2, -1, name, name_size, NULL, NULL);
751 return TRUE;
755 RegCloseKey(key);
757 ++i;
760 RegCloseKey(devices_key);
762 WARN("No matching device in registry for GUID %s\n", debugstr_guid(guid));
764 return FALSE;
767 HRESULT WINAPI AUDDRV_GetAudioEndpoint(GUID *guid, IMMDevice *dev, IAudioClient **out)
769 ACImpl *This;
770 int err;
771 snd_pcm_stream_t stream;
772 snd_config_t *lconf;
773 static BOOL handle_underrun = TRUE;
774 char alsa_name[256];
775 EDataFlow dataflow;
776 HRESULT hr;
778 TRACE("%s %p %p\n", debugstr_guid(guid), dev, out);
780 if(!get_alsa_name_by_guid(guid, alsa_name, sizeof(alsa_name), &dataflow))
781 return AUDCLNT_E_DEVICE_INVALIDATED;
783 This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(ACImpl));
784 if(!This)
785 return E_OUTOFMEMORY;
787 This->IAudioClient_iface.lpVtbl = &AudioClient_Vtbl;
788 This->IAudioRenderClient_iface.lpVtbl = &AudioRenderClient_Vtbl;
789 This->IAudioCaptureClient_iface.lpVtbl = &AudioCaptureClient_Vtbl;
790 This->IAudioClock_iface.lpVtbl = &AudioClock_Vtbl;
791 This->IAudioClock2_iface.lpVtbl = &AudioClock2_Vtbl;
792 This->IAudioStreamVolume_iface.lpVtbl = &AudioStreamVolume_Vtbl;
794 if(dataflow == eRender)
795 stream = SND_PCM_STREAM_PLAYBACK;
796 else if(dataflow == eCapture)
797 stream = SND_PCM_STREAM_CAPTURE;
798 else{
799 HeapFree(GetProcessHeap(), 0, This);
800 return E_UNEXPECTED;
803 hr = CoCreateFreeThreadedMarshaler((IUnknown *)&This->IAudioClient_iface,
804 (IUnknown **)&This->pUnkFTMarshal);
805 if (FAILED(hr)) {
806 HeapFree(GetProcessHeap(), 0, This);
807 return hr;
810 This->dataflow = dataflow;
811 if(handle_underrun && ((lconf = make_handle_underrun_config(alsa_name)))){
812 err = snd_pcm_open_lconf(&This->pcm_handle, alsa_name, stream, SND_PCM_NONBLOCK, lconf);
813 TRACE("Opening PCM device \"%s\" with handle_underrun: %d\n", alsa_name, err);
814 snd_config_delete(lconf);
815 /* Pulse <= 2010 returns EINVAL, it does not know handle_underrun. */
816 if(err == -EINVAL){
817 ERR_(winediag)("PulseAudio \"%s\" %d without handle_underrun. Audio may hang."
818 " Please upgrade to alsa_plugins >= 1.0.24\n", alsa_name, err);
819 handle_underrun = FALSE;
821 }else
822 err = -EINVAL;
823 if(err == -EINVAL){
824 err = snd_pcm_open(&This->pcm_handle, alsa_name, stream, SND_PCM_NONBLOCK);
826 if(err < 0){
827 HeapFree(GetProcessHeap(), 0, This);
828 WARN("Unable to open PCM \"%s\": %d (%s)\n", alsa_name, err, snd_strerror(err));
829 switch(err){
830 case -EBUSY:
831 return AUDCLNT_E_DEVICE_IN_USE;
832 default:
833 return AUDCLNT_E_ENDPOINT_CREATE_FAILED;
837 This->hw_params = HeapAlloc(GetProcessHeap(), 0,
838 snd_pcm_hw_params_sizeof());
839 if(!This->hw_params){
840 snd_pcm_close(This->pcm_handle);
841 HeapFree(GetProcessHeap(), 0, This);
842 return E_OUTOFMEMORY;
845 InitializeCriticalSection(&This->lock);
846 This->lock.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": ACImpl.lock");
848 This->parent = dev;
849 IMMDevice_AddRef(This->parent);
851 *out = &This->IAudioClient_iface;
852 IAudioClient_AddRef(&This->IAudioClient_iface);
854 return S_OK;
857 static HRESULT WINAPI AudioClient_QueryInterface(IAudioClient *iface,
858 REFIID riid, void **ppv)
860 ACImpl *This = impl_from_IAudioClient(iface);
861 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
863 if(!ppv)
864 return E_POINTER;
865 *ppv = NULL;
866 if(IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IAudioClient))
867 *ppv = iface;
868 else if(IsEqualIID(riid, &IID_IMarshal))
869 return IUnknown_QueryInterface(This->pUnkFTMarshal, riid, ppv);
871 if(*ppv){
872 IUnknown_AddRef((IUnknown*)*ppv);
873 return S_OK;
875 WARN("Unknown interface %s\n", debugstr_guid(riid));
876 return E_NOINTERFACE;
879 static ULONG WINAPI AudioClient_AddRef(IAudioClient *iface)
881 ACImpl *This = impl_from_IAudioClient(iface);
882 ULONG ref;
883 ref = InterlockedIncrement(&This->ref);
884 TRACE("(%p) Refcount now %u\n", This, ref);
885 return ref;
888 static ULONG WINAPI AudioClient_Release(IAudioClient *iface)
890 ACImpl *This = impl_from_IAudioClient(iface);
891 ULONG ref;
892 ref = InterlockedDecrement(&This->ref);
893 TRACE("(%p) Refcount now %u\n", This, ref);
894 if(!ref){
895 IAudioClient_Stop(iface);
896 IMMDevice_Release(This->parent);
897 IUnknown_Release(This->pUnkFTMarshal);
898 This->lock.DebugInfo->Spare[0] = 0;
899 DeleteCriticalSection(&This->lock);
900 snd_pcm_drop(This->pcm_handle);
901 snd_pcm_close(This->pcm_handle);
902 if(This->initted){
903 EnterCriticalSection(&g_sessions_lock);
904 list_remove(&This->entry);
905 LeaveCriticalSection(&g_sessions_lock);
907 HeapFree(GetProcessHeap(), 0, This->vols);
908 HeapFree(GetProcessHeap(), 0, This->local_buffer);
909 HeapFree(GetProcessHeap(), 0, This->remapping_buf);
910 HeapFree(GetProcessHeap(), 0, This->tmp_buffer);
911 HeapFree(GetProcessHeap(), 0, This->hw_params);
912 CoTaskMemFree(This->fmt);
913 HeapFree(GetProcessHeap(), 0, This);
915 return ref;
918 static void dump_fmt(const WAVEFORMATEX *fmt)
920 TRACE("wFormatTag: 0x%x (", fmt->wFormatTag);
921 switch(fmt->wFormatTag){
922 case WAVE_FORMAT_PCM:
923 TRACE("WAVE_FORMAT_PCM");
924 break;
925 case WAVE_FORMAT_IEEE_FLOAT:
926 TRACE("WAVE_FORMAT_IEEE_FLOAT");
927 break;
928 case WAVE_FORMAT_EXTENSIBLE:
929 TRACE("WAVE_FORMAT_EXTENSIBLE");
930 break;
931 default:
932 TRACE("Unknown");
933 break;
935 TRACE(")\n");
937 TRACE("nChannels: %u\n", fmt->nChannels);
938 TRACE("nSamplesPerSec: %u\n", fmt->nSamplesPerSec);
939 TRACE("nAvgBytesPerSec: %u\n", fmt->nAvgBytesPerSec);
940 TRACE("nBlockAlign: %u\n", fmt->nBlockAlign);
941 TRACE("wBitsPerSample: %u\n", fmt->wBitsPerSample);
942 TRACE("cbSize: %u\n", fmt->cbSize);
944 if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE){
945 WAVEFORMATEXTENSIBLE *fmtex = (void*)fmt;
946 TRACE("dwChannelMask: %08x\n", fmtex->dwChannelMask);
947 TRACE("Samples: %04x\n", fmtex->Samples.wReserved);
948 TRACE("SubFormat: %s\n", wine_dbgstr_guid(&fmtex->SubFormat));
952 static WAVEFORMATEX *clone_format(const WAVEFORMATEX *fmt)
954 WAVEFORMATEX *ret;
955 size_t size;
957 if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
958 size = sizeof(WAVEFORMATEXTENSIBLE);
959 else
960 size = sizeof(WAVEFORMATEX);
962 ret = CoTaskMemAlloc(size);
963 if(!ret)
964 return NULL;
966 memcpy(ret, fmt, size);
968 ret->cbSize = size - sizeof(WAVEFORMATEX);
970 return ret;
973 static snd_pcm_format_t alsa_format(const WAVEFORMATEX *fmt)
975 snd_pcm_format_t format = SND_PCM_FORMAT_UNKNOWN;
976 const WAVEFORMATEXTENSIBLE *fmtex = (const WAVEFORMATEXTENSIBLE *)fmt;
978 if(fmt->wFormatTag == WAVE_FORMAT_PCM ||
979 (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
980 IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))){
981 if(fmt->wBitsPerSample == 8)
982 format = SND_PCM_FORMAT_U8;
983 else if(fmt->wBitsPerSample == 16)
984 format = SND_PCM_FORMAT_S16_LE;
985 else if(fmt->wBitsPerSample == 24)
986 format = SND_PCM_FORMAT_S24_3LE;
987 else if(fmt->wBitsPerSample == 32)
988 format = SND_PCM_FORMAT_S32_LE;
989 else
990 WARN("Unsupported bit depth: %u\n", fmt->wBitsPerSample);
991 if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
992 fmt->wBitsPerSample != fmtex->Samples.wValidBitsPerSample){
993 if(fmtex->Samples.wValidBitsPerSample == 20 && fmt->wBitsPerSample == 24)
994 format = SND_PCM_FORMAT_S20_3LE;
995 else
996 WARN("Unsupported ValidBits: %u\n", fmtex->Samples.wValidBitsPerSample);
998 }else if(fmt->wFormatTag == WAVE_FORMAT_IEEE_FLOAT ||
999 (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
1000 IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT))){
1001 if(fmt->wBitsPerSample == 32)
1002 format = SND_PCM_FORMAT_FLOAT_LE;
1003 else if(fmt->wBitsPerSample == 64)
1004 format = SND_PCM_FORMAT_FLOAT64_LE;
1005 else
1006 WARN("Unsupported float size: %u\n", fmt->wBitsPerSample);
1007 }else
1008 WARN("Unknown wave format: %04x\n", fmt->wFormatTag);
1009 return format;
1012 static void session_init_vols(AudioSession *session, UINT channels)
1014 if(session->channel_count < channels){
1015 UINT i;
1017 if(session->channel_vols)
1018 session->channel_vols = HeapReAlloc(GetProcessHeap(), 0,
1019 session->channel_vols, sizeof(float) * channels);
1020 else
1021 session->channel_vols = HeapAlloc(GetProcessHeap(), 0,
1022 sizeof(float) * channels);
1023 if(!session->channel_vols)
1024 return;
1026 for(i = session->channel_count; i < channels; ++i)
1027 session->channel_vols[i] = 1.f;
1029 session->channel_count = channels;
1033 static AudioSession *create_session(const GUID *guid, IMMDevice *device,
1034 UINT num_channels)
1036 AudioSession *ret;
1038 ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(AudioSession));
1039 if(!ret)
1040 return NULL;
1042 memcpy(&ret->guid, guid, sizeof(GUID));
1044 ret->device = device;
1046 list_init(&ret->clients);
1048 list_add_head(&g_sessions, &ret->entry);
1050 InitializeCriticalSection(&ret->lock);
1051 ret->lock.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": AudioSession.lock");
1053 session_init_vols(ret, num_channels);
1055 ret->master_vol = 1.f;
1057 return ret;
1060 /* if channels == 0, then this will return or create a session with
1061 * matching dataflow and GUID. otherwise, channels must also match */
1062 static HRESULT get_audio_session(const GUID *sessionguid,
1063 IMMDevice *device, UINT channels, AudioSession **out)
1065 AudioSession *session;
1067 if(!sessionguid || IsEqualGUID(sessionguid, &GUID_NULL)){
1068 *out = create_session(&GUID_NULL, device, channels);
1069 if(!*out)
1070 return E_OUTOFMEMORY;
1072 return S_OK;
1075 *out = NULL;
1076 LIST_FOR_EACH_ENTRY(session, &g_sessions, AudioSession, entry){
1077 if(session->device == device &&
1078 IsEqualGUID(sessionguid, &session->guid)){
1079 session_init_vols(session, channels);
1080 *out = session;
1081 break;
1085 if(!*out){
1086 *out = create_session(sessionguid, device, channels);
1087 if(!*out)
1088 return E_OUTOFMEMORY;
1091 return S_OK;
1094 static int alsa_channel_index(DWORD flag)
1096 switch(flag){
1097 case SPEAKER_FRONT_LEFT:
1098 return 0;
1099 case SPEAKER_FRONT_RIGHT:
1100 return 1;
1101 case SPEAKER_BACK_LEFT:
1102 return 2;
1103 case SPEAKER_BACK_RIGHT:
1104 return 3;
1105 case SPEAKER_FRONT_CENTER:
1106 return 4;
1107 case SPEAKER_LOW_FREQUENCY:
1108 return 5;
1109 case SPEAKER_SIDE_LEFT:
1110 return 6;
1111 case SPEAKER_SIDE_RIGHT:
1112 return 7;
1114 return -1;
1117 static BOOL need_remapping(ACImpl *This, const WAVEFORMATEX *fmt)
1119 unsigned int i;
1120 for(i = 0; i < fmt->nChannels; ++i){
1121 if(This->alsa_channel_map[i] != i)
1122 return TRUE;
1124 return FALSE;
1127 static DWORD get_channel_mask(unsigned int channels)
1129 switch(channels){
1130 case 0:
1131 return 0;
1132 case 1:
1133 return KSAUDIO_SPEAKER_MONO;
1134 case 2:
1135 return KSAUDIO_SPEAKER_STEREO;
1136 case 3:
1137 return KSAUDIO_SPEAKER_STEREO | SPEAKER_LOW_FREQUENCY;
1138 case 4:
1139 return KSAUDIO_SPEAKER_QUAD; /* not _SURROUND */
1140 case 5:
1141 return KSAUDIO_SPEAKER_QUAD | SPEAKER_LOW_FREQUENCY;
1142 case 6:
1143 return KSAUDIO_SPEAKER_5POINT1; /* not 5POINT1_SURROUND */
1144 case 7:
1145 return KSAUDIO_SPEAKER_5POINT1 | SPEAKER_BACK_CENTER;
1146 case 8:
1147 return KSAUDIO_SPEAKER_7POINT1_SURROUND; /* Vista deprecates 7POINT1 */
1149 FIXME("Unknown speaker configuration: %u\n", channels);
1150 return 0;
1153 static HRESULT map_channels(ACImpl *This, const WAVEFORMATEX *fmt)
1155 if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE || fmt->nChannels > 2){
1156 WAVEFORMATEXTENSIBLE *fmtex = (void*)fmt;
1157 DWORD mask, flag = SPEAKER_FRONT_LEFT;
1158 UINT i = 0;
1160 if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
1161 fmtex->dwChannelMask != 0)
1162 mask = fmtex->dwChannelMask;
1163 else
1164 mask = get_channel_mask(fmt->nChannels);
1166 This->alsa_channels = 0;
1168 while(i < fmt->nChannels && !(flag & SPEAKER_RESERVED)){
1169 if(mask & flag){
1170 This->alsa_channel_map[i] = alsa_channel_index(flag);
1171 TRACE("Mapping mmdevapi channel %u (0x%x) to ALSA channel %d\n",
1172 i, flag, This->alsa_channel_map[i]);
1173 if(This->alsa_channel_map[i] >= This->alsa_channels)
1174 This->alsa_channels = This->alsa_channel_map[i] + 1;
1175 ++i;
1177 flag <<= 1;
1180 while(i < fmt->nChannels){
1181 This->alsa_channel_map[i] = This->alsa_channels;
1182 TRACE("Mapping mmdevapi channel %u to ALSA channel %d\n",
1183 i, This->alsa_channel_map[i]);
1184 ++This->alsa_channels;
1185 ++i;
1188 for(i = 0; i < fmt->nChannels; ++i){
1189 if(This->alsa_channel_map[i] == -1){
1190 This->alsa_channel_map[i] = This->alsa_channels;
1191 ++This->alsa_channels;
1192 TRACE("Remapping mmdevapi channel %u to ALSA channel %d\n",
1193 i, This->alsa_channel_map[i]);
1197 This->need_remapping = need_remapping(This, fmt);
1199 TRACE("need_remapping: %u, alsa_channels: %d\n", This->need_remapping, This->alsa_channels);
1200 }else{
1201 This->need_remapping = FALSE;
1202 This->alsa_channels = fmt->nChannels;
1203 TRACE("need_remapping: %u, alsa_channels: %d\n", This->need_remapping, This->alsa_channels);
1206 return S_OK;
1209 static HRESULT WINAPI AudioClient_Initialize(IAudioClient *iface,
1210 AUDCLNT_SHAREMODE mode, DWORD flags, REFERENCE_TIME duration,
1211 REFERENCE_TIME period, const WAVEFORMATEX *fmt,
1212 const GUID *sessionguid)
1214 ACImpl *This = impl_from_IAudioClient(iface);
1215 snd_pcm_sw_params_t *sw_params = NULL;
1216 snd_pcm_format_t format;
1217 unsigned int rate, alsa_period_us;
1218 int err, i;
1219 HRESULT hr = S_OK;
1221 TRACE("(%p)->(%x, %x, %s, %s, %p, %s)\n", This, mode, flags,
1222 wine_dbgstr_longlong(duration), wine_dbgstr_longlong(period), fmt, debugstr_guid(sessionguid));
1224 if(!fmt)
1225 return E_POINTER;
1227 if(mode != AUDCLNT_SHAREMODE_SHARED && mode != AUDCLNT_SHAREMODE_EXCLUSIVE)
1228 return AUDCLNT_E_NOT_INITIALIZED;
1230 if(flags & ~(AUDCLNT_STREAMFLAGS_CROSSPROCESS |
1231 AUDCLNT_STREAMFLAGS_LOOPBACK |
1232 AUDCLNT_STREAMFLAGS_EVENTCALLBACK |
1233 AUDCLNT_STREAMFLAGS_NOPERSIST |
1234 AUDCLNT_STREAMFLAGS_RATEADJUST |
1235 AUDCLNT_SESSIONFLAGS_EXPIREWHENUNOWNED |
1236 AUDCLNT_SESSIONFLAGS_DISPLAY_HIDE |
1237 AUDCLNT_SESSIONFLAGS_DISPLAY_HIDEWHENEXPIRED)){
1238 TRACE("Unknown flags: %08x\n", flags);
1239 return E_INVALIDARG;
1242 if(mode == AUDCLNT_SHAREMODE_SHARED){
1243 period = DefaultPeriod;
1244 if( duration < 3 * period)
1245 duration = 3 * period;
1246 }else{
1247 if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE){
1248 if(((WAVEFORMATEXTENSIBLE*)fmt)->dwChannelMask == 0 ||
1249 ((WAVEFORMATEXTENSIBLE*)fmt)->dwChannelMask & SPEAKER_RESERVED)
1250 return AUDCLNT_E_UNSUPPORTED_FORMAT;
1253 if(!period)
1254 period = DefaultPeriod; /* not minimum */
1255 if(period < MinimumPeriod || period > 5000000)
1256 return AUDCLNT_E_INVALID_DEVICE_PERIOD;
1257 if(duration > 20000000) /* the smaller the period, the lower this limit */
1258 return AUDCLNT_E_BUFFER_SIZE_ERROR;
1259 if(flags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK){
1260 if(duration != period)
1261 return AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL;
1262 FIXME("EXCLUSIVE mode with EVENTCALLBACK\n");
1263 return AUDCLNT_E_DEVICE_IN_USE;
1264 }else{
1265 if( duration < 8 * period)
1266 duration = 8 * period; /* may grow above 2s */
1270 EnterCriticalSection(&This->lock);
1272 if(This->initted){
1273 LeaveCriticalSection(&This->lock);
1274 return AUDCLNT_E_ALREADY_INITIALIZED;
1277 dump_fmt(fmt);
1279 if(FAILED(map_channels(This, fmt))){
1280 WARN("map_channels failed\n");
1281 hr = AUDCLNT_E_ENDPOINT_CREATE_FAILED;
1282 goto exit;
1285 if((err = snd_pcm_hw_params_any(This->pcm_handle, This->hw_params)) < 0){
1286 WARN("Unable to get hw_params: %d (%s)\n", err, snd_strerror(err));
1287 hr = AUDCLNT_E_ENDPOINT_CREATE_FAILED;
1288 goto exit;
1291 if((err = snd_pcm_hw_params_set_access(This->pcm_handle, This->hw_params,
1292 SND_PCM_ACCESS_RW_INTERLEAVED)) < 0){
1293 WARN("Unable to set access: %d (%s)\n", err, snd_strerror(err));
1294 hr = AUDCLNT_E_ENDPOINT_CREATE_FAILED;
1295 goto exit;
1298 format = alsa_format(fmt);
1299 if (format == SND_PCM_FORMAT_UNKNOWN){
1300 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
1301 goto exit;
1304 if((err = snd_pcm_hw_params_set_format(This->pcm_handle, This->hw_params,
1305 format)) < 0){
1306 WARN("Unable to set ALSA format to %u: %d (%s)\n", format, err,
1307 snd_strerror(err));
1308 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
1309 goto exit;
1312 This->alsa_format = format;
1314 rate = fmt->nSamplesPerSec;
1315 if((err = snd_pcm_hw_params_set_rate_near(This->pcm_handle, This->hw_params,
1316 &rate, NULL)) < 0){
1317 WARN("Unable to set rate to %u: %d (%s)\n", rate, err,
1318 snd_strerror(err));
1319 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
1320 goto exit;
1323 if((err = snd_pcm_hw_params_set_channels(This->pcm_handle, This->hw_params,
1324 This->alsa_channels)) < 0){
1325 WARN("Unable to set channels to %u: %d (%s)\n", fmt->nChannels, err,
1326 snd_strerror(err));
1327 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
1328 goto exit;
1331 This->mmdev_period_rt = period;
1332 alsa_period_us = This->mmdev_period_rt / 10;
1333 if((err = snd_pcm_hw_params_set_period_time_near(This->pcm_handle,
1334 This->hw_params, &alsa_period_us, NULL)) < 0)
1335 WARN("Unable to set period time near %u: %d (%s)\n", alsa_period_us,
1336 err, snd_strerror(err));
1337 /* ALSA updates the output variable alsa_period_us */
1339 This->mmdev_period_frames = MulDiv(fmt->nSamplesPerSec,
1340 This->mmdev_period_rt, 10000000);
1342 /* Buffer 4 ALSA periods if large enough, else 4 mmdevapi periods */
1343 This->alsa_bufsize_frames = This->mmdev_period_frames * 4;
1344 if(err < 0 || alsa_period_us < period / 10)
1345 err = snd_pcm_hw_params_set_buffer_size_near(This->pcm_handle,
1346 This->hw_params, &This->alsa_bufsize_frames);
1347 else{
1348 unsigned int periods = 4;
1349 err = snd_pcm_hw_params_set_periods_near(This->pcm_handle, This->hw_params, &periods, NULL);
1351 if(err < 0)
1352 WARN("Unable to set buffer size: %d (%s)\n", err, snd_strerror(err));
1354 if((err = snd_pcm_hw_params(This->pcm_handle, This->hw_params)) < 0){
1355 WARN("Unable to set hw params: %d (%s)\n", err, snd_strerror(err));
1356 hr = AUDCLNT_E_ENDPOINT_CREATE_FAILED;
1357 goto exit;
1360 if((err = snd_pcm_hw_params_get_period_size(This->hw_params,
1361 &This->alsa_period_frames, NULL)) < 0){
1362 WARN("Unable to get period size: %d (%s)\n", err, snd_strerror(err));
1363 hr = AUDCLNT_E_ENDPOINT_CREATE_FAILED;
1364 goto exit;
1367 if((err = snd_pcm_hw_params_get_buffer_size(This->hw_params,
1368 &This->alsa_bufsize_frames)) < 0){
1369 WARN("Unable to get buffer size: %d (%s)\n", err, snd_strerror(err));
1370 hr = AUDCLNT_E_ENDPOINT_CREATE_FAILED;
1371 goto exit;
1374 sw_params = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, snd_pcm_sw_params_sizeof());
1375 if(!sw_params){
1376 hr = E_OUTOFMEMORY;
1377 goto exit;
1380 if((err = snd_pcm_sw_params_current(This->pcm_handle, sw_params)) < 0){
1381 WARN("Unable to get sw_params: %d (%s)\n", err, snd_strerror(err));
1382 hr = AUDCLNT_E_ENDPOINT_CREATE_FAILED;
1383 goto exit;
1386 if((err = snd_pcm_sw_params_set_start_threshold(This->pcm_handle,
1387 sw_params, 1)) < 0){
1388 WARN("Unable set start threshold to 0: %d (%s)\n", err, snd_strerror(err));
1389 hr = AUDCLNT_E_ENDPOINT_CREATE_FAILED;
1390 goto exit;
1393 if((err = snd_pcm_sw_params_set_stop_threshold(This->pcm_handle,
1394 sw_params, This->alsa_bufsize_frames)) < 0){
1395 WARN("Unable set stop threshold to %lu: %d (%s)\n",
1396 This->alsa_bufsize_frames, err, snd_strerror(err));
1397 hr = AUDCLNT_E_ENDPOINT_CREATE_FAILED;
1398 goto exit;
1401 if((err = snd_pcm_sw_params(This->pcm_handle, sw_params)) < 0){
1402 WARN("Unable to set sw params: %d (%s)\n", err, snd_strerror(err));
1403 hr = AUDCLNT_E_ENDPOINT_CREATE_FAILED;
1404 goto exit;
1407 if((err = snd_pcm_prepare(This->pcm_handle)) < 0){
1408 WARN("Unable to prepare device: %d (%s)\n", err, snd_strerror(err));
1409 hr = AUDCLNT_E_ENDPOINT_CREATE_FAILED;
1410 goto exit;
1413 /* Bear in mind weird situations where
1414 * ALSA period (50ms) > mmdevapi buffer (3x10ms)
1415 * or surprising rounding as seen with 22050x8x1 with Pulse:
1416 * ALSA period 220 vs. 221 frames in mmdevapi and
1417 * buffer 883 vs. 2205 frames in mmdevapi! */
1418 This->bufsize_frames = MulDiv(duration, fmt->nSamplesPerSec, 10000000);
1419 if(mode == AUDCLNT_SHAREMODE_EXCLUSIVE)
1420 This->bufsize_frames -= This->bufsize_frames % This->mmdev_period_frames;
1421 This->hidden_frames = This->alsa_period_frames + This->mmdev_period_frames +
1422 MulDiv(fmt->nSamplesPerSec, EXTRA_SAFE_RT, 10000000);
1424 /* Check if the ALSA buffer is so small that it will run out before
1425 * the next MMDevAPI period tick occurs. Allow a little wiggle room
1426 * with 120% of the period time. */
1427 if(This->alsa_bufsize_frames < 1.2 * This->mmdev_period_frames)
1428 FIXME("ALSA buffer time is too small. Expect underruns. (%lu < %u * 1.2)\n",
1429 This->alsa_bufsize_frames, This->mmdev_period_frames);
1431 This->local_buffer = HeapAlloc(GetProcessHeap(), 0,
1432 This->bufsize_frames * fmt->nBlockAlign);
1433 if(!This->local_buffer){
1434 hr = E_OUTOFMEMORY;
1435 goto exit;
1437 if (fmt->wBitsPerSample == 8)
1438 memset(This->local_buffer, 128, This->bufsize_frames * fmt->nBlockAlign);
1439 else
1440 memset(This->local_buffer, 0, This->bufsize_frames * fmt->nBlockAlign);
1442 This->fmt = clone_format(fmt);
1443 if(!This->fmt){
1444 hr = E_OUTOFMEMORY;
1445 goto exit;
1448 This->vols = HeapAlloc(GetProcessHeap(), 0, fmt->nChannels * sizeof(float));
1449 if(!This->vols){
1450 hr = E_OUTOFMEMORY;
1451 goto exit;
1454 for(i = 0; i < fmt->nChannels; ++i)
1455 This->vols[i] = 1.f;
1457 This->share = mode;
1458 This->flags = flags;
1460 EnterCriticalSection(&g_sessions_lock);
1462 hr = get_audio_session(sessionguid, This->parent, fmt->nChannels,
1463 &This->session);
1464 if(FAILED(hr)){
1465 LeaveCriticalSection(&g_sessions_lock);
1466 goto exit;
1469 list_add_tail(&This->session->clients, &This->entry);
1471 LeaveCriticalSection(&g_sessions_lock);
1473 This->initted = TRUE;
1475 TRACE("ALSA period: %lu frames\n", This->alsa_period_frames);
1476 TRACE("ALSA buffer: %lu frames\n", This->alsa_bufsize_frames);
1477 TRACE("MMDevice period: %u frames\n", This->mmdev_period_frames);
1478 TRACE("MMDevice buffer: %u frames\n", This->bufsize_frames);
1480 exit:
1481 HeapFree(GetProcessHeap(), 0, sw_params);
1482 if(FAILED(hr)){
1483 HeapFree(GetProcessHeap(), 0, This->local_buffer);
1484 This->local_buffer = NULL;
1485 CoTaskMemFree(This->fmt);
1486 This->fmt = NULL;
1487 HeapFree(GetProcessHeap(), 0, This->vols);
1488 This->vols = NULL;
1491 LeaveCriticalSection(&This->lock);
1493 return hr;
1496 static HRESULT WINAPI AudioClient_GetBufferSize(IAudioClient *iface,
1497 UINT32 *out)
1499 ACImpl *This = impl_from_IAudioClient(iface);
1501 TRACE("(%p)->(%p)\n", This, out);
1503 if(!out)
1504 return E_POINTER;
1506 EnterCriticalSection(&This->lock);
1508 if(!This->initted){
1509 LeaveCriticalSection(&This->lock);
1510 return AUDCLNT_E_NOT_INITIALIZED;
1513 *out = This->bufsize_frames;
1515 LeaveCriticalSection(&This->lock);
1517 return S_OK;
1520 static HRESULT WINAPI AudioClient_GetStreamLatency(IAudioClient *iface,
1521 REFERENCE_TIME *latency)
1523 ACImpl *This = impl_from_IAudioClient(iface);
1525 TRACE("(%p)->(%p)\n", This, latency);
1527 if(!latency)
1528 return E_POINTER;
1530 EnterCriticalSection(&This->lock);
1532 if(!This->initted){
1533 LeaveCriticalSection(&This->lock);
1534 return AUDCLNT_E_NOT_INITIALIZED;
1537 /* Hide some frames in the ALSA buffer. Allows us to return GetCurrentPadding=0
1538 * yet have enough data left to play (as if it were in native's mixer). Add:
1539 * + mmdevapi_period such that at the end of it, ALSA still has data;
1540 * + EXTRA_SAFE (~4ms) to allow for late callback invocation / fluctuation;
1541 * + alsa_period such that ALSA always has at least one period to play. */
1542 if(This->dataflow == eRender)
1543 *latency = MulDiv(This->hidden_frames, 10000000, This->fmt->nSamplesPerSec);
1544 else
1545 *latency = MulDiv(This->alsa_period_frames, 10000000, This->fmt->nSamplesPerSec)
1546 + This->mmdev_period_rt;
1548 LeaveCriticalSection(&This->lock);
1550 return S_OK;
1553 static HRESULT WINAPI AudioClient_GetCurrentPadding(IAudioClient *iface,
1554 UINT32 *out)
1556 ACImpl *This = impl_from_IAudioClient(iface);
1558 TRACE("(%p)->(%p)\n", This, out);
1560 if(!out)
1561 return E_POINTER;
1563 EnterCriticalSection(&This->lock);
1565 if(!This->initted){
1566 LeaveCriticalSection(&This->lock);
1567 return AUDCLNT_E_NOT_INITIALIZED;
1570 /* padding is solely updated at callback time in shared mode */
1571 *out = This->held_frames;
1573 LeaveCriticalSection(&This->lock);
1575 TRACE("pad: %u\n", *out);
1577 return S_OK;
1580 static HRESULT WINAPI AudioClient_IsFormatSupported(IAudioClient *iface,
1581 AUDCLNT_SHAREMODE mode, const WAVEFORMATEX *fmt,
1582 WAVEFORMATEX **out)
1584 ACImpl *This = impl_from_IAudioClient(iface);
1585 snd_pcm_format_mask_t *formats = NULL;
1586 snd_pcm_format_t format;
1587 HRESULT hr = S_OK;
1588 WAVEFORMATEX *closest = NULL;
1589 unsigned int max = 0, min = 0;
1590 int err;
1592 TRACE("(%p)->(%x, %p, %p)\n", This, mode, fmt, out);
1594 if(!fmt || (mode == AUDCLNT_SHAREMODE_SHARED && !out))
1595 return E_POINTER;
1597 if(mode != AUDCLNT_SHAREMODE_SHARED && mode != AUDCLNT_SHAREMODE_EXCLUSIVE)
1598 return E_INVALIDARG;
1600 if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
1601 fmt->cbSize < sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX))
1602 return E_INVALIDARG;
1604 dump_fmt(fmt);
1606 if(out){
1607 *out = NULL;
1608 if(mode != AUDCLNT_SHAREMODE_SHARED)
1609 out = NULL;
1612 if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
1613 (fmt->nAvgBytesPerSec == 0 ||
1614 fmt->nBlockAlign == 0 ||
1615 ((WAVEFORMATEXTENSIBLE*)fmt)->Samples.wValidBitsPerSample > fmt->wBitsPerSample))
1616 return E_INVALIDARG;
1618 if(fmt->nChannels == 0)
1619 return AUDCLNT_E_UNSUPPORTED_FORMAT;
1621 EnterCriticalSection(&This->lock);
1623 if((err = snd_pcm_hw_params_any(This->pcm_handle, This->hw_params)) < 0){
1624 hr = AUDCLNT_E_DEVICE_INVALIDATED;
1625 goto exit;
1628 formats = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
1629 snd_pcm_format_mask_sizeof());
1630 if(!formats){
1631 hr = E_OUTOFMEMORY;
1632 goto exit;
1635 snd_pcm_hw_params_get_format_mask(This->hw_params, formats);
1636 format = alsa_format(fmt);
1637 if (format == SND_PCM_FORMAT_UNKNOWN ||
1638 !snd_pcm_format_mask_test(formats, format)){
1639 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
1640 goto exit;
1643 closest = clone_format(fmt);
1644 if(!closest){
1645 hr = E_OUTOFMEMORY;
1646 goto exit;
1649 if((err = snd_pcm_hw_params_get_rate_min(This->hw_params, &min, NULL)) < 0){
1650 hr = AUDCLNT_E_DEVICE_INVALIDATED;
1651 WARN("Unable to get min rate: %d (%s)\n", err, snd_strerror(err));
1652 goto exit;
1655 if((err = snd_pcm_hw_params_get_rate_max(This->hw_params, &max, NULL)) < 0){
1656 hr = AUDCLNT_E_DEVICE_INVALIDATED;
1657 WARN("Unable to get max rate: %d (%s)\n", err, snd_strerror(err));
1658 goto exit;
1661 if(fmt->nSamplesPerSec < min || fmt->nSamplesPerSec > max){
1662 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
1663 goto exit;
1666 if((err = snd_pcm_hw_params_get_channels_min(This->hw_params, &min)) < 0){
1667 hr = AUDCLNT_E_DEVICE_INVALIDATED;
1668 WARN("Unable to get min channels: %d (%s)\n", err, snd_strerror(err));
1669 goto exit;
1672 if((err = snd_pcm_hw_params_get_channels_max(This->hw_params, &max)) < 0){
1673 hr = AUDCLNT_E_DEVICE_INVALIDATED;
1674 WARN("Unable to get max channels: %d (%s)\n", err, snd_strerror(err));
1675 goto exit;
1677 if(fmt->nChannels > max){
1678 hr = S_FALSE;
1679 closest->nChannels = max;
1680 }else if(fmt->nChannels < min){
1681 hr = S_FALSE;
1682 closest->nChannels = min;
1685 if(FAILED(map_channels(This, fmt))){
1686 hr = AUDCLNT_E_DEVICE_INVALIDATED;
1687 WARN("map_channels failed\n");
1688 goto exit;
1690 if(This->alsa_channels > max){
1691 hr = S_FALSE;
1692 closest->nChannels = max;
1695 if(closest->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
1696 ((WAVEFORMATEXTENSIBLE*)closest)->dwChannelMask = get_channel_mask(closest->nChannels);
1698 if(fmt->nBlockAlign != fmt->nChannels * fmt->wBitsPerSample / 8 ||
1699 fmt->nAvgBytesPerSec != fmt->nBlockAlign * fmt->nSamplesPerSec ||
1700 (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
1701 ((WAVEFORMATEXTENSIBLE*)fmt)->Samples.wValidBitsPerSample < fmt->wBitsPerSample))
1702 hr = S_FALSE;
1704 if(mode == AUDCLNT_SHAREMODE_EXCLUSIVE &&
1705 fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE){
1706 if(((WAVEFORMATEXTENSIBLE*)fmt)->dwChannelMask == 0 ||
1707 ((WAVEFORMATEXTENSIBLE*)fmt)->dwChannelMask & SPEAKER_RESERVED)
1708 hr = S_FALSE;
1711 exit:
1712 LeaveCriticalSection(&This->lock);
1713 HeapFree(GetProcessHeap(), 0, formats);
1715 if(hr == S_FALSE && !out)
1716 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
1718 if(hr == S_FALSE && out) {
1719 closest->nBlockAlign =
1720 closest->nChannels * closest->wBitsPerSample / 8;
1721 closest->nAvgBytesPerSec =
1722 closest->nBlockAlign * closest->nSamplesPerSec;
1723 if(closest->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
1724 ((WAVEFORMATEXTENSIBLE*)closest)->Samples.wValidBitsPerSample = closest->wBitsPerSample;
1725 *out = closest;
1726 } else
1727 CoTaskMemFree(closest);
1729 TRACE("returning: %08x\n", hr);
1730 return hr;
1733 static HRESULT WINAPI AudioClient_GetMixFormat(IAudioClient *iface,
1734 WAVEFORMATEX **pwfx)
1736 ACImpl *This = impl_from_IAudioClient(iface);
1737 WAVEFORMATEXTENSIBLE *fmt;
1738 snd_pcm_format_mask_t *formats;
1739 unsigned int max_rate, max_channels;
1740 int err;
1741 HRESULT hr = S_OK;
1743 TRACE("(%p)->(%p)\n", This, pwfx);
1745 if(!pwfx)
1746 return E_POINTER;
1747 *pwfx = NULL;
1749 fmt = CoTaskMemAlloc(sizeof(WAVEFORMATEXTENSIBLE));
1750 if(!fmt)
1751 return E_OUTOFMEMORY;
1753 formats = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, snd_pcm_format_mask_sizeof());
1754 if(!formats){
1755 CoTaskMemFree(fmt);
1756 return E_OUTOFMEMORY;
1759 EnterCriticalSection(&This->lock);
1761 if((err = snd_pcm_hw_params_any(This->pcm_handle, This->hw_params)) < 0){
1762 WARN("Unable to get hw_params: %d (%s)\n", err, snd_strerror(err));
1763 hr = AUDCLNT_E_DEVICE_INVALIDATED;
1764 goto exit;
1767 snd_pcm_hw_params_get_format_mask(This->hw_params, formats);
1769 fmt->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
1770 if(snd_pcm_format_mask_test(formats, SND_PCM_FORMAT_FLOAT_LE)){
1771 fmt->Format.wBitsPerSample = 32;
1772 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
1773 }else if(snd_pcm_format_mask_test(formats, SND_PCM_FORMAT_S16_LE)){
1774 fmt->Format.wBitsPerSample = 16;
1775 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1776 }else if(snd_pcm_format_mask_test(formats, SND_PCM_FORMAT_U8)){
1777 fmt->Format.wBitsPerSample = 8;
1778 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1779 }else if(snd_pcm_format_mask_test(formats, SND_PCM_FORMAT_S32_LE)){
1780 fmt->Format.wBitsPerSample = 32;
1781 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1782 }else if(snd_pcm_format_mask_test(formats, SND_PCM_FORMAT_S24_3LE)){
1783 fmt->Format.wBitsPerSample = 24;
1784 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1785 }else{
1786 ERR("Didn't recognize any available ALSA formats\n");
1787 hr = AUDCLNT_E_DEVICE_INVALIDATED;
1788 goto exit;
1791 if((err = snd_pcm_hw_params_get_channels_max(This->hw_params,
1792 &max_channels)) < 0){
1793 WARN("Unable to get max channels: %d (%s)\n", err, snd_strerror(err));
1794 hr = AUDCLNT_E_DEVICE_INVALIDATED;
1795 goto exit;
1798 if(max_channels > 2)
1799 fmt->Format.nChannels = 2;
1800 else
1801 fmt->Format.nChannels = max_channels;
1803 fmt->dwChannelMask = get_channel_mask(fmt->Format.nChannels);
1805 if((err = snd_pcm_hw_params_get_rate_max(This->hw_params, &max_rate,
1806 NULL)) < 0){
1807 WARN("Unable to get max rate: %d (%s)\n", err, snd_strerror(err));
1808 hr = AUDCLNT_E_DEVICE_INVALIDATED;
1809 goto exit;
1812 if(max_rate >= 48000)
1813 fmt->Format.nSamplesPerSec = 48000;
1814 else if(max_rate >= 44100)
1815 fmt->Format.nSamplesPerSec = 44100;
1816 else if(max_rate >= 22050)
1817 fmt->Format.nSamplesPerSec = 22050;
1818 else if(max_rate >= 11025)
1819 fmt->Format.nSamplesPerSec = 11025;
1820 else if(max_rate >= 8000)
1821 fmt->Format.nSamplesPerSec = 8000;
1822 else{
1823 ERR("Unknown max rate: %u\n", max_rate);
1824 hr = AUDCLNT_E_DEVICE_INVALIDATED;
1825 goto exit;
1828 fmt->Format.nBlockAlign = (fmt->Format.wBitsPerSample *
1829 fmt->Format.nChannels) / 8;
1830 fmt->Format.nAvgBytesPerSec = fmt->Format.nSamplesPerSec *
1831 fmt->Format.nBlockAlign;
1833 fmt->Samples.wValidBitsPerSample = fmt->Format.wBitsPerSample;
1834 fmt->Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
1836 dump_fmt((WAVEFORMATEX*)fmt);
1837 *pwfx = (WAVEFORMATEX*)fmt;
1839 exit:
1840 LeaveCriticalSection(&This->lock);
1841 if(FAILED(hr))
1842 CoTaskMemFree(fmt);
1843 HeapFree(GetProcessHeap(), 0, formats);
1845 return hr;
1848 static HRESULT WINAPI AudioClient_GetDevicePeriod(IAudioClient *iface,
1849 REFERENCE_TIME *defperiod, REFERENCE_TIME *minperiod)
1851 ACImpl *This = impl_from_IAudioClient(iface);
1853 TRACE("(%p)->(%p, %p)\n", This, defperiod, minperiod);
1855 if(!defperiod && !minperiod)
1856 return E_POINTER;
1858 if(defperiod)
1859 *defperiod = DefaultPeriod;
1860 if(minperiod)
1861 *minperiod = MinimumPeriod;
1863 return S_OK;
1866 static BYTE *remap_channels(ACImpl *This, BYTE *buf, snd_pcm_uframes_t frames)
1868 snd_pcm_uframes_t i;
1869 UINT c;
1870 UINT bytes_per_sample = This->fmt->wBitsPerSample / 8;
1872 if(!This->need_remapping)
1873 return buf;
1875 if(!This->remapping_buf){
1876 This->remapping_buf = HeapAlloc(GetProcessHeap(), 0,
1877 bytes_per_sample * This->alsa_channels * frames);
1878 This->remapping_buf_frames = frames;
1879 }else if(This->remapping_buf_frames < frames){
1880 This->remapping_buf = HeapReAlloc(GetProcessHeap(), 0, This->remapping_buf,
1881 bytes_per_sample * This->alsa_channels * frames);
1882 This->remapping_buf_frames = frames;
1885 snd_pcm_format_set_silence(This->alsa_format, This->remapping_buf,
1886 frames * This->alsa_channels);
1888 switch(This->fmt->wBitsPerSample){
1889 case 8: {
1890 UINT8 *tgt_buf, *src_buf;
1891 tgt_buf = This->remapping_buf;
1892 src_buf = buf;
1893 for(i = 0; i < frames; ++i){
1894 for(c = 0; c < This->fmt->nChannels; ++c)
1895 tgt_buf[This->alsa_channel_map[c]] = src_buf[c];
1896 tgt_buf += This->alsa_channels;
1897 src_buf += This->fmt->nChannels;
1899 break;
1901 case 16: {
1902 UINT16 *tgt_buf, *src_buf;
1903 tgt_buf = (UINT16*)This->remapping_buf;
1904 src_buf = (UINT16*)buf;
1905 for(i = 0; i < frames; ++i){
1906 for(c = 0; c < This->fmt->nChannels; ++c)
1907 tgt_buf[This->alsa_channel_map[c]] = src_buf[c];
1908 tgt_buf += This->alsa_channels;
1909 src_buf += This->fmt->nChannels;
1912 break;
1913 case 32: {
1914 UINT32 *tgt_buf, *src_buf;
1915 tgt_buf = (UINT32*)This->remapping_buf;
1916 src_buf = (UINT32*)buf;
1917 for(i = 0; i < frames; ++i){
1918 for(c = 0; c < This->fmt->nChannels; ++c)
1919 tgt_buf[This->alsa_channel_map[c]] = src_buf[c];
1920 tgt_buf += This->alsa_channels;
1921 src_buf += This->fmt->nChannels;
1924 break;
1925 default: {
1926 BYTE *tgt_buf, *src_buf;
1927 tgt_buf = This->remapping_buf;
1928 src_buf = buf;
1929 for(i = 0; i < frames; ++i){
1930 for(c = 0; c < This->fmt->nChannels; ++c)
1931 memcpy(&tgt_buf[This->alsa_channel_map[c] * bytes_per_sample],
1932 &src_buf[c * bytes_per_sample], bytes_per_sample);
1933 tgt_buf += This->alsa_channels * bytes_per_sample;
1934 src_buf += This->fmt->nChannels * bytes_per_sample;
1937 break;
1940 return This->remapping_buf;
1943 static snd_pcm_sframes_t alsa_write_best_effort(snd_pcm_t *handle, BYTE *buf,
1944 snd_pcm_uframes_t frames, ACImpl *This, BOOL mute)
1946 snd_pcm_sframes_t written;
1948 if(mute){
1949 int err;
1950 if((err = snd_pcm_format_set_silence(This->alsa_format, buf,
1951 frames * This->fmt->nChannels)) < 0)
1952 WARN("Setting buffer to silence failed: %d (%s)\n", err,
1953 snd_strerror(err));
1956 buf = remap_channels(This, buf, frames);
1958 written = snd_pcm_writei(handle, buf, frames);
1959 if(written < 0){
1960 int ret;
1962 if(written == -EAGAIN)
1963 /* buffer full */
1964 return 0;
1966 WARN("writei failed, recovering: %ld (%s)\n", written,
1967 snd_strerror(written));
1969 ret = snd_pcm_recover(handle, written, 0);
1970 if(ret < 0){
1971 WARN("Could not recover: %d (%s)\n", ret, snd_strerror(ret));
1972 return ret;
1975 written = snd_pcm_writei(handle, buf, frames);
1978 return written;
1981 /* The callback and mmdevapi API functions execute concurrently.
1982 * Shared state & life time after Start:
1983 * This constant until _Release
1984 *->pcm_handle likewise
1985 *->fmt likewise
1986 *->alsa_format, hidden_frames likewise
1987 *->local_buffer, bufsize_frames, alsa_bufsize_frames likewise
1988 *->event Read Only, even constant until _Release(!)
1989 *->started Read Only from cb POV, constant if _Stop kills the cb
1991 *->held_frames is the only R/W object.
1992 *->lcl_offs_frames/wri_offs_frames are written by one side exclusively:
1993 * lcl_offs_frames by CaptureClient & write callback
1994 * wri_offs_frames by read callback & RenderClient
1996 static void alsa_write_data(ACImpl *This)
1998 snd_pcm_sframes_t written, in_alsa;
1999 snd_pcm_uframes_t to_write, avail, write_limit, max_period;
2000 int err;
2001 BYTE *buf =
2002 This->local_buffer + This->lcl_offs_frames * This->fmt->nBlockAlign;
2004 /* this call seems to be required to get an accurate snd_pcm_state() */
2005 avail = snd_pcm_avail_update(This->pcm_handle);
2007 if(snd_pcm_state(This->pcm_handle) == SND_PCM_STATE_XRUN ||
2008 avail > This->alsa_bufsize_frames){
2009 TRACE("XRun state avail %ld, recovering\n", avail);
2011 avail = This->alsa_bufsize_frames;
2013 if((err = snd_pcm_recover(This->pcm_handle, -EPIPE, 1)) < 0)
2014 WARN("snd_pcm_recover failed: %d (%s)\n", err, snd_strerror(err));
2016 if((err = snd_pcm_reset(This->pcm_handle)) < 0)
2017 WARN("snd_pcm_reset failed: %d (%s)\n", err, snd_strerror(err));
2019 if((err = snd_pcm_prepare(This->pcm_handle)) < 0)
2020 WARN("snd_pcm_prepare failed: %d (%s)\n", err, snd_strerror(err));
2021 }else
2022 TRACE("pad: %ld\n", This->alsa_bufsize_frames - avail);
2024 if(This->held_frames == 0)
2025 return;
2027 if(This->lcl_offs_frames + This->held_frames > This->bufsize_frames)
2028 to_write = This->bufsize_frames - This->lcl_offs_frames;
2029 else
2030 to_write = This->held_frames;
2032 max_period = max(This->mmdev_period_frames, This->alsa_period_frames);
2034 /* try to keep 3 ALSA periods or 3 MMDevAPI periods in the ALSA buffer and
2035 * no more */
2036 write_limit = 0;
2037 in_alsa = This->alsa_bufsize_frames - avail;
2038 while(in_alsa + write_limit < max_period * 3)
2039 write_limit += max_period;
2040 if(write_limit == 0)
2041 return;
2043 to_write = min(to_write, write_limit);
2045 /* Add a lead-in when starting with too few frames to ensure
2046 * continuous rendering. Additional benefit: Force ALSA to start.
2047 * GetPosition continues to reflect the speaker position because
2048 * snd_pcm_delay includes buffered frames in its total delay
2049 * and last_pos_frames prevents moving backwards. */
2050 if(!in_alsa && This->held_frames < This->hidden_frames){
2051 UINT32 s_frames = This->hidden_frames - This->held_frames;
2052 BYTE *silence = HeapAlloc(GetProcessHeap(), 0,
2053 s_frames * This->fmt->nBlockAlign);
2055 if(silence){
2056 in_alsa = alsa_write_best_effort(This->pcm_handle,
2057 silence, s_frames, This, TRUE);
2058 TRACE("lead-in %ld\n", in_alsa);
2059 HeapFree(GetProcessHeap(), 0, silence);
2060 if(in_alsa <= 0)
2061 return;
2062 }else
2063 WARN("Couldn't allocate lead-in, expect underrun\n");
2066 written = alsa_write_best_effort(This->pcm_handle, buf, to_write, This,
2067 This->session->mute);
2068 if(written < 0){
2069 WARN("Couldn't write: %ld (%s)\n", written, snd_strerror(written));
2070 return;
2073 This->lcl_offs_frames += written;
2074 This->lcl_offs_frames %= This->bufsize_frames;
2075 This->held_frames -= written;
2077 if(written < to_write){
2078 /* ALSA buffer probably full */
2079 return;
2082 if(This->held_frames && (written < write_limit)){
2083 /* wrapped and have some data back at the start to write */
2084 written = alsa_write_best_effort(This->pcm_handle, This->local_buffer,
2085 min(This->held_frames, write_limit - written), This,
2086 This->session->mute);
2087 if(written < 0){
2088 WARN("Couldn't write: %ld (%s)\n", written, snd_strerror(written));
2089 return;
2092 This->lcl_offs_frames += written;
2093 This->lcl_offs_frames %= This->bufsize_frames;
2094 This->held_frames -= written;
2098 static void alsa_read_data(ACImpl *This)
2100 snd_pcm_sframes_t nread;
2101 UINT32 pos = This->wri_offs_frames, limit = This->held_frames;
2103 /* FIXME: Detect overrun and signal DATA_DISCONTINUITY
2104 * How to count overrun frames and report them as position increase? */
2105 limit = This->bufsize_frames - max(limit, pos);
2107 nread = snd_pcm_readi(This->pcm_handle,
2108 This->local_buffer + pos * This->fmt->nBlockAlign, limit);
2109 TRACE("read %ld from %u limit %u\n", nread, pos, limit);
2110 if(nread < 0){
2111 int ret;
2113 if(nread == -EAGAIN) /* no data yet */
2114 return;
2116 WARN("read failed, recovering: %ld (%s)\n", nread, snd_strerror(nread));
2118 ret = snd_pcm_recover(This->pcm_handle, nread, 0);
2119 if(ret < 0){
2120 WARN("Recover failed: %d (%s)\n", ret, snd_strerror(ret));
2121 return;
2124 nread = snd_pcm_readi(This->pcm_handle,
2125 This->local_buffer + pos * This->fmt->nBlockAlign, limit);
2126 if(nread < 0){
2127 WARN("read failed: %ld (%s)\n", nread, snd_strerror(nread));
2128 return;
2132 if(This->session->mute){
2133 int err;
2134 if((err = snd_pcm_format_set_silence(This->alsa_format,
2135 This->local_buffer + pos * This->fmt->nBlockAlign,
2136 nread)) < 0)
2137 WARN("Setting buffer to silence failed: %d (%s)\n", err,
2138 snd_strerror(err));
2141 This->wri_offs_frames += nread;
2142 This->wri_offs_frames %= This->bufsize_frames;
2143 This->held_frames += nread;
2146 static void CALLBACK alsa_push_buffer_data(void *user, BOOLEAN timer)
2148 ACImpl *This = user;
2150 EnterCriticalSection(&This->lock);
2152 if(This->started){
2153 if(This->dataflow == eRender)
2154 alsa_write_data(This);
2155 else if(This->dataflow == eCapture)
2156 alsa_read_data(This);
2159 LeaveCriticalSection(&This->lock);
2161 if(This->event)
2162 SetEvent(This->event);
2165 static HRESULT WINAPI AudioClient_Start(IAudioClient *iface)
2167 ACImpl *This = impl_from_IAudioClient(iface);
2169 TRACE("(%p)\n", This);
2171 EnterCriticalSection(&This->lock);
2173 if(!This->initted){
2174 LeaveCriticalSection(&This->lock);
2175 return AUDCLNT_E_NOT_INITIALIZED;
2178 if((This->flags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK) && !This->event){
2179 LeaveCriticalSection(&This->lock);
2180 return AUDCLNT_E_EVENTHANDLE_NOT_SET;
2183 if(This->started){
2184 LeaveCriticalSection(&This->lock);
2185 return AUDCLNT_E_NOT_STOPPED;
2188 if(This->dataflow == eCapture){
2189 /* dump any data that might be leftover in the ALSA capture buffer */
2190 snd_pcm_readi(This->pcm_handle, This->local_buffer,
2191 This->bufsize_frames);
2194 if(!CreateTimerQueueTimer(&This->timer, g_timer_q, alsa_push_buffer_data,
2195 This, 0, This->mmdev_period_rt / 10000, WT_EXECUTEINTIMERTHREAD)){
2196 LeaveCriticalSection(&This->lock);
2197 WARN("Unable to create timer: %u\n", GetLastError());
2198 return E_OUTOFMEMORY;
2201 This->started = TRUE;
2203 LeaveCriticalSection(&This->lock);
2205 return S_OK;
2208 static HRESULT WINAPI AudioClient_Stop(IAudioClient *iface)
2210 ACImpl *This = impl_from_IAudioClient(iface);
2211 HANDLE event;
2212 BOOL wait;
2214 TRACE("(%p)\n", This);
2216 EnterCriticalSection(&This->lock);
2218 if(!This->initted){
2219 LeaveCriticalSection(&This->lock);
2220 return AUDCLNT_E_NOT_INITIALIZED;
2223 if(!This->started){
2224 LeaveCriticalSection(&This->lock);
2225 return S_FALSE;
2228 /* Stop without losing written frames or position.
2229 * snd_pcm_pause would be appropriate but is unsupported by dmix.
2230 * snd_pcm_drain yields EAGAIN in NONBLOCK mode, except with Pulse. */
2232 event = CreateEventW(NULL, TRUE, FALSE, NULL);
2233 wait = !DeleteTimerQueueTimer(g_timer_q, This->timer, event);
2234 if(wait)
2235 WARN("DeleteTimerQueueTimer error %u\n", GetLastError());
2236 wait = wait && GetLastError() == ERROR_IO_PENDING;
2238 This->started = FALSE;
2240 LeaveCriticalSection(&This->lock);
2242 if(event && wait)
2243 WaitForSingleObject(event, INFINITE);
2244 CloseHandle(event);
2246 return S_OK;
2249 static HRESULT WINAPI AudioClient_Reset(IAudioClient *iface)
2251 ACImpl *This = impl_from_IAudioClient(iface);
2253 TRACE("(%p)\n", This);
2255 EnterCriticalSection(&This->lock);
2257 if(!This->initted){
2258 LeaveCriticalSection(&This->lock);
2259 return AUDCLNT_E_NOT_INITIALIZED;
2262 if(This->started){
2263 LeaveCriticalSection(&This->lock);
2264 return AUDCLNT_E_NOT_STOPPED;
2267 if(This->getbuf_last){
2268 LeaveCriticalSection(&This->lock);
2269 return AUDCLNT_E_BUFFER_OPERATION_PENDING;
2272 if(snd_pcm_drop(This->pcm_handle) < 0)
2273 WARN("snd_pcm_drop failed\n");
2275 if(snd_pcm_reset(This->pcm_handle) < 0)
2276 WARN("snd_pcm_reset failed\n");
2278 if(snd_pcm_prepare(This->pcm_handle) < 0)
2279 WARN("snd_pcm_prepare failed\n");
2281 if(This->dataflow == eRender){
2282 This->written_frames = 0;
2283 This->last_pos_frames = 0;
2284 }else{
2285 This->written_frames += This->held_frames;
2287 This->held_frames = 0;
2288 This->lcl_offs_frames = 0;
2289 This->wri_offs_frames = 0;
2291 LeaveCriticalSection(&This->lock);
2293 return S_OK;
2296 static HRESULT WINAPI AudioClient_SetEventHandle(IAudioClient *iface,
2297 HANDLE event)
2299 ACImpl *This = impl_from_IAudioClient(iface);
2301 TRACE("(%p)->(%p)\n", This, event);
2303 if(!event)
2304 return E_INVALIDARG;
2306 EnterCriticalSection(&This->lock);
2308 if(!This->initted){
2309 LeaveCriticalSection(&This->lock);
2310 return AUDCLNT_E_NOT_INITIALIZED;
2313 if(!(This->flags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK)){
2314 LeaveCriticalSection(&This->lock);
2315 return AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED;
2318 if (This->event){
2319 LeaveCriticalSection(&This->lock);
2320 FIXME("called twice\n");
2321 return HRESULT_FROM_WIN32(ERROR_INVALID_NAME);
2324 This->event = event;
2326 LeaveCriticalSection(&This->lock);
2328 return S_OK;
2331 static HRESULT WINAPI AudioClient_GetService(IAudioClient *iface, REFIID riid,
2332 void **ppv)
2334 ACImpl *This = impl_from_IAudioClient(iface);
2336 TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(riid), ppv);
2338 if(!ppv)
2339 return E_POINTER;
2340 *ppv = NULL;
2342 EnterCriticalSection(&This->lock);
2344 if(!This->initted){
2345 LeaveCriticalSection(&This->lock);
2346 return AUDCLNT_E_NOT_INITIALIZED;
2349 if(IsEqualIID(riid, &IID_IAudioRenderClient)){
2350 if(This->dataflow != eRender){
2351 LeaveCriticalSection(&This->lock);
2352 return AUDCLNT_E_WRONG_ENDPOINT_TYPE;
2354 IAudioRenderClient_AddRef(&This->IAudioRenderClient_iface);
2355 *ppv = &This->IAudioRenderClient_iface;
2356 }else if(IsEqualIID(riid, &IID_IAudioCaptureClient)){
2357 if(This->dataflow != eCapture){
2358 LeaveCriticalSection(&This->lock);
2359 return AUDCLNT_E_WRONG_ENDPOINT_TYPE;
2361 IAudioCaptureClient_AddRef(&This->IAudioCaptureClient_iface);
2362 *ppv = &This->IAudioCaptureClient_iface;
2363 }else if(IsEqualIID(riid, &IID_IAudioClock)){
2364 IAudioClock_AddRef(&This->IAudioClock_iface);
2365 *ppv = &This->IAudioClock_iface;
2366 }else if(IsEqualIID(riid, &IID_IAudioStreamVolume)){
2367 IAudioStreamVolume_AddRef(&This->IAudioStreamVolume_iface);
2368 *ppv = &This->IAudioStreamVolume_iface;
2369 }else if(IsEqualIID(riid, &IID_IAudioSessionControl)){
2370 if(!This->session_wrapper){
2371 This->session_wrapper = AudioSessionWrapper_Create(This);
2372 if(!This->session_wrapper){
2373 LeaveCriticalSection(&This->lock);
2374 return E_OUTOFMEMORY;
2376 }else
2377 IAudioSessionControl2_AddRef(&This->session_wrapper->IAudioSessionControl2_iface);
2379 *ppv = &This->session_wrapper->IAudioSessionControl2_iface;
2380 }else if(IsEqualIID(riid, &IID_IChannelAudioVolume)){
2381 if(!This->session_wrapper){
2382 This->session_wrapper = AudioSessionWrapper_Create(This);
2383 if(!This->session_wrapper){
2384 LeaveCriticalSection(&This->lock);
2385 return E_OUTOFMEMORY;
2387 }else
2388 IChannelAudioVolume_AddRef(&This->session_wrapper->IChannelAudioVolume_iface);
2390 *ppv = &This->session_wrapper->IChannelAudioVolume_iface;
2391 }else if(IsEqualIID(riid, &IID_ISimpleAudioVolume)){
2392 if(!This->session_wrapper){
2393 This->session_wrapper = AudioSessionWrapper_Create(This);
2394 if(!This->session_wrapper){
2395 LeaveCriticalSection(&This->lock);
2396 return E_OUTOFMEMORY;
2398 }else
2399 ISimpleAudioVolume_AddRef(&This->session_wrapper->ISimpleAudioVolume_iface);
2401 *ppv = &This->session_wrapper->ISimpleAudioVolume_iface;
2404 if(*ppv){
2405 LeaveCriticalSection(&This->lock);
2406 return S_OK;
2409 LeaveCriticalSection(&This->lock);
2411 FIXME("stub %s\n", debugstr_guid(riid));
2412 return E_NOINTERFACE;
2415 static const IAudioClientVtbl AudioClient_Vtbl =
2417 AudioClient_QueryInterface,
2418 AudioClient_AddRef,
2419 AudioClient_Release,
2420 AudioClient_Initialize,
2421 AudioClient_GetBufferSize,
2422 AudioClient_GetStreamLatency,
2423 AudioClient_GetCurrentPadding,
2424 AudioClient_IsFormatSupported,
2425 AudioClient_GetMixFormat,
2426 AudioClient_GetDevicePeriod,
2427 AudioClient_Start,
2428 AudioClient_Stop,
2429 AudioClient_Reset,
2430 AudioClient_SetEventHandle,
2431 AudioClient_GetService
2434 static HRESULT WINAPI AudioRenderClient_QueryInterface(
2435 IAudioRenderClient *iface, REFIID riid, void **ppv)
2437 ACImpl *This = impl_from_IAudioRenderClient(iface);
2438 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2440 if(!ppv)
2441 return E_POINTER;
2442 *ppv = NULL;
2444 if(IsEqualIID(riid, &IID_IUnknown) ||
2445 IsEqualIID(riid, &IID_IAudioRenderClient))
2446 *ppv = iface;
2447 else if(IsEqualIID(riid, &IID_IMarshal))
2448 return IUnknown_QueryInterface(This->pUnkFTMarshal, riid, ppv);
2450 if(*ppv){
2451 IUnknown_AddRef((IUnknown*)*ppv);
2452 return S_OK;
2455 WARN("Unknown interface %s\n", debugstr_guid(riid));
2456 return E_NOINTERFACE;
2459 static ULONG WINAPI AudioRenderClient_AddRef(IAudioRenderClient *iface)
2461 ACImpl *This = impl_from_IAudioRenderClient(iface);
2462 return AudioClient_AddRef(&This->IAudioClient_iface);
2465 static ULONG WINAPI AudioRenderClient_Release(IAudioRenderClient *iface)
2467 ACImpl *This = impl_from_IAudioRenderClient(iface);
2468 return AudioClient_Release(&This->IAudioClient_iface);
2471 static void silence_buffer(ACImpl *This, BYTE *buffer, UINT32 frames)
2473 WAVEFORMATEXTENSIBLE *fmtex = (WAVEFORMATEXTENSIBLE*)This->fmt;
2474 if((This->fmt->wFormatTag == WAVE_FORMAT_PCM ||
2475 (This->fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
2476 IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))) &&
2477 This->fmt->wBitsPerSample == 8)
2478 memset(buffer, 128, frames * This->fmt->nBlockAlign);
2479 else
2480 memset(buffer, 0, frames * This->fmt->nBlockAlign);
2483 static HRESULT WINAPI AudioRenderClient_GetBuffer(IAudioRenderClient *iface,
2484 UINT32 frames, BYTE **data)
2486 ACImpl *This = impl_from_IAudioRenderClient(iface);
2487 UINT32 write_pos;
2489 TRACE("(%p)->(%u, %p)\n", This, frames, data);
2491 if(!data)
2492 return E_POINTER;
2493 *data = NULL;
2495 EnterCriticalSection(&This->lock);
2497 if(This->getbuf_last){
2498 LeaveCriticalSection(&This->lock);
2499 return AUDCLNT_E_OUT_OF_ORDER;
2502 if(!frames){
2503 LeaveCriticalSection(&This->lock);
2504 return S_OK;
2507 /* held_frames == GetCurrentPadding_nolock(); */
2508 if(This->held_frames + frames > This->bufsize_frames){
2509 LeaveCriticalSection(&This->lock);
2510 return AUDCLNT_E_BUFFER_TOO_LARGE;
2513 write_pos = This->wri_offs_frames;
2514 if(write_pos + frames > This->bufsize_frames){
2515 if(This->tmp_buffer_frames < frames){
2516 HeapFree(GetProcessHeap(), 0, This->tmp_buffer);
2517 This->tmp_buffer = HeapAlloc(GetProcessHeap(), 0,
2518 frames * This->fmt->nBlockAlign);
2519 if(!This->tmp_buffer){
2520 LeaveCriticalSection(&This->lock);
2521 return E_OUTOFMEMORY;
2523 This->tmp_buffer_frames = frames;
2525 *data = This->tmp_buffer;
2526 This->getbuf_last = -frames;
2527 }else{
2528 *data = This->local_buffer + write_pos * This->fmt->nBlockAlign;
2529 This->getbuf_last = frames;
2532 silence_buffer(This, *data, frames);
2534 LeaveCriticalSection(&This->lock);
2536 return S_OK;
2539 static void alsa_wrap_buffer(ACImpl *This, BYTE *buffer, UINT32 written_frames)
2541 snd_pcm_uframes_t write_offs_frames = This->wri_offs_frames;
2542 UINT32 write_offs_bytes = write_offs_frames * This->fmt->nBlockAlign;
2543 snd_pcm_uframes_t chunk_frames = This->bufsize_frames - write_offs_frames;
2544 UINT32 chunk_bytes = chunk_frames * This->fmt->nBlockAlign;
2545 UINT32 written_bytes = written_frames * This->fmt->nBlockAlign;
2547 if(written_bytes <= chunk_bytes){
2548 memcpy(This->local_buffer + write_offs_bytes, buffer, written_bytes);
2549 }else{
2550 memcpy(This->local_buffer + write_offs_bytes, buffer, chunk_bytes);
2551 memcpy(This->local_buffer, buffer + chunk_bytes,
2552 written_bytes - chunk_bytes);
2556 static HRESULT WINAPI AudioRenderClient_ReleaseBuffer(
2557 IAudioRenderClient *iface, UINT32 written_frames, DWORD flags)
2559 ACImpl *This = impl_from_IAudioRenderClient(iface);
2560 BYTE *buffer;
2562 TRACE("(%p)->(%u, %x)\n", This, written_frames, flags);
2564 EnterCriticalSection(&This->lock);
2566 if(!written_frames){
2567 This->getbuf_last = 0;
2568 LeaveCriticalSection(&This->lock);
2569 return S_OK;
2572 if(!This->getbuf_last){
2573 LeaveCriticalSection(&This->lock);
2574 return AUDCLNT_E_OUT_OF_ORDER;
2577 if(written_frames > (This->getbuf_last >= 0 ? This->getbuf_last : -This->getbuf_last)){
2578 LeaveCriticalSection(&This->lock);
2579 return AUDCLNT_E_INVALID_SIZE;
2582 if(This->getbuf_last >= 0)
2583 buffer = This->local_buffer + This->wri_offs_frames * This->fmt->nBlockAlign;
2584 else
2585 buffer = This->tmp_buffer;
2587 if(flags & AUDCLNT_BUFFERFLAGS_SILENT)
2588 silence_buffer(This, buffer, written_frames);
2590 if(This->getbuf_last < 0)
2591 alsa_wrap_buffer(This, buffer, written_frames);
2593 This->wri_offs_frames += written_frames;
2594 This->wri_offs_frames %= This->bufsize_frames;
2595 This->held_frames += written_frames;
2596 This->written_frames += written_frames;
2597 This->getbuf_last = 0;
2599 LeaveCriticalSection(&This->lock);
2601 return S_OK;
2604 static const IAudioRenderClientVtbl AudioRenderClient_Vtbl = {
2605 AudioRenderClient_QueryInterface,
2606 AudioRenderClient_AddRef,
2607 AudioRenderClient_Release,
2608 AudioRenderClient_GetBuffer,
2609 AudioRenderClient_ReleaseBuffer
2612 static HRESULT WINAPI AudioCaptureClient_QueryInterface(
2613 IAudioCaptureClient *iface, REFIID riid, void **ppv)
2615 ACImpl *This = impl_from_IAudioCaptureClient(iface);
2616 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2618 if(!ppv)
2619 return E_POINTER;
2620 *ppv = NULL;
2622 if(IsEqualIID(riid, &IID_IUnknown) ||
2623 IsEqualIID(riid, &IID_IAudioCaptureClient))
2624 *ppv = iface;
2625 else if(IsEqualIID(riid, &IID_IMarshal))
2626 return IUnknown_QueryInterface(This->pUnkFTMarshal, riid, ppv);
2628 if(*ppv){
2629 IUnknown_AddRef((IUnknown*)*ppv);
2630 return S_OK;
2633 WARN("Unknown interface %s\n", debugstr_guid(riid));
2634 return E_NOINTERFACE;
2637 static ULONG WINAPI AudioCaptureClient_AddRef(IAudioCaptureClient *iface)
2639 ACImpl *This = impl_from_IAudioCaptureClient(iface);
2640 return IAudioClient_AddRef(&This->IAudioClient_iface);
2643 static ULONG WINAPI AudioCaptureClient_Release(IAudioCaptureClient *iface)
2645 ACImpl *This = impl_from_IAudioCaptureClient(iface);
2646 return IAudioClient_Release(&This->IAudioClient_iface);
2649 static HRESULT WINAPI AudioCaptureClient_GetBuffer(IAudioCaptureClient *iface,
2650 BYTE **data, UINT32 *frames, DWORD *flags, UINT64 *devpos,
2651 UINT64 *qpcpos)
2653 ACImpl *This = impl_from_IAudioCaptureClient(iface);
2655 TRACE("(%p)->(%p, %p, %p, %p, %p)\n", This, data, frames, flags,
2656 devpos, qpcpos);
2658 if(!data || !frames || !flags)
2659 return E_POINTER;
2661 EnterCriticalSection(&This->lock);
2663 if(This->getbuf_last){
2664 LeaveCriticalSection(&This->lock);
2665 return AUDCLNT_E_OUT_OF_ORDER;
2668 /* hr = GetNextPacketSize(iface, frames); */
2669 if(This->held_frames < This->mmdev_period_frames){
2670 *frames = 0;
2671 LeaveCriticalSection(&This->lock);
2672 return AUDCLNT_S_BUFFER_EMPTY;
2674 *frames = This->mmdev_period_frames;
2676 if(This->lcl_offs_frames + *frames > This->bufsize_frames){
2677 UINT32 chunk_bytes, offs_bytes, frames_bytes;
2678 if(This->tmp_buffer_frames < *frames){
2679 HeapFree(GetProcessHeap(), 0, This->tmp_buffer);
2680 This->tmp_buffer = HeapAlloc(GetProcessHeap(), 0,
2681 *frames * This->fmt->nBlockAlign);
2682 if(!This->tmp_buffer){
2683 LeaveCriticalSection(&This->lock);
2684 return E_OUTOFMEMORY;
2686 This->tmp_buffer_frames = *frames;
2689 *data = This->tmp_buffer;
2690 chunk_bytes = (This->bufsize_frames - This->lcl_offs_frames) *
2691 This->fmt->nBlockAlign;
2692 offs_bytes = This->lcl_offs_frames * This->fmt->nBlockAlign;
2693 frames_bytes = *frames * This->fmt->nBlockAlign;
2694 memcpy(This->tmp_buffer, This->local_buffer + offs_bytes, chunk_bytes);
2695 memcpy(This->tmp_buffer + chunk_bytes, This->local_buffer,
2696 frames_bytes - chunk_bytes);
2697 }else
2698 *data = This->local_buffer +
2699 This->lcl_offs_frames * This->fmt->nBlockAlign;
2701 This->getbuf_last = *frames;
2702 *flags = 0;
2704 if(devpos)
2705 *devpos = This->written_frames;
2706 if(qpcpos){ /* fixme: qpc of recording time */
2707 LARGE_INTEGER stamp, freq;
2708 QueryPerformanceCounter(&stamp);
2709 QueryPerformanceFrequency(&freq);
2710 *qpcpos = (stamp.QuadPart * (INT64)10000000) / freq.QuadPart;
2713 LeaveCriticalSection(&This->lock);
2715 return *frames ? S_OK : AUDCLNT_S_BUFFER_EMPTY;
2718 static HRESULT WINAPI AudioCaptureClient_ReleaseBuffer(
2719 IAudioCaptureClient *iface, UINT32 done)
2721 ACImpl *This = impl_from_IAudioCaptureClient(iface);
2723 TRACE("(%p)->(%u)\n", This, done);
2725 EnterCriticalSection(&This->lock);
2727 if(!done){
2728 This->getbuf_last = 0;
2729 LeaveCriticalSection(&This->lock);
2730 return S_OK;
2733 if(!This->getbuf_last){
2734 LeaveCriticalSection(&This->lock);
2735 return AUDCLNT_E_OUT_OF_ORDER;
2738 if(This->getbuf_last != done){
2739 LeaveCriticalSection(&This->lock);
2740 return AUDCLNT_E_INVALID_SIZE;
2743 This->written_frames += done;
2744 This->held_frames -= done;
2745 This->lcl_offs_frames += done;
2746 This->lcl_offs_frames %= This->bufsize_frames;
2747 This->getbuf_last = 0;
2749 LeaveCriticalSection(&This->lock);
2751 return S_OK;
2754 static HRESULT WINAPI AudioCaptureClient_GetNextPacketSize(
2755 IAudioCaptureClient *iface, UINT32 *frames)
2757 ACImpl *This = impl_from_IAudioCaptureClient(iface);
2759 TRACE("(%p)->(%p)\n", This, frames);
2761 if(!frames)
2762 return E_POINTER;
2764 EnterCriticalSection(&This->lock);
2766 *frames = This->held_frames < This->mmdev_period_frames ? 0 : This->mmdev_period_frames;
2768 LeaveCriticalSection(&This->lock);
2770 return S_OK;
2773 static const IAudioCaptureClientVtbl AudioCaptureClient_Vtbl =
2775 AudioCaptureClient_QueryInterface,
2776 AudioCaptureClient_AddRef,
2777 AudioCaptureClient_Release,
2778 AudioCaptureClient_GetBuffer,
2779 AudioCaptureClient_ReleaseBuffer,
2780 AudioCaptureClient_GetNextPacketSize
2783 static HRESULT WINAPI AudioClock_QueryInterface(IAudioClock *iface,
2784 REFIID riid, void **ppv)
2786 ACImpl *This = impl_from_IAudioClock(iface);
2788 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2790 if(!ppv)
2791 return E_POINTER;
2792 *ppv = NULL;
2794 if(IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IAudioClock))
2795 *ppv = iface;
2796 else if(IsEqualIID(riid, &IID_IAudioClock2))
2797 *ppv = &This->IAudioClock2_iface;
2798 if(*ppv){
2799 IUnknown_AddRef((IUnknown*)*ppv);
2800 return S_OK;
2803 WARN("Unknown interface %s\n", debugstr_guid(riid));
2804 return E_NOINTERFACE;
2807 static ULONG WINAPI AudioClock_AddRef(IAudioClock *iface)
2809 ACImpl *This = impl_from_IAudioClock(iface);
2810 return IAudioClient_AddRef(&This->IAudioClient_iface);
2813 static ULONG WINAPI AudioClock_Release(IAudioClock *iface)
2815 ACImpl *This = impl_from_IAudioClock(iface);
2816 return IAudioClient_Release(&This->IAudioClient_iface);
2819 static HRESULT WINAPI AudioClock_GetFrequency(IAudioClock *iface, UINT64 *freq)
2821 ACImpl *This = impl_from_IAudioClock(iface);
2823 TRACE("(%p)->(%p)\n", This, freq);
2825 if(This->share == AUDCLNT_SHAREMODE_SHARED)
2826 *freq = This->fmt->nSamplesPerSec * This->fmt->nBlockAlign;
2827 else
2828 *freq = This->fmt->nSamplesPerSec;
2830 return S_OK;
2833 static HRESULT WINAPI AudioClock_GetPosition(IAudioClock *iface, UINT64 *pos,
2834 UINT64 *qpctime)
2836 ACImpl *This = impl_from_IAudioClock(iface);
2837 UINT64 written_frames, position;
2838 UINT32 held_frames;
2839 int err;
2840 snd_pcm_state_t alsa_state;
2841 snd_pcm_uframes_t avail_frames;
2842 snd_pcm_sframes_t delay_frames;
2844 TRACE("(%p)->(%p, %p)\n", This, pos, qpctime);
2846 if(!pos)
2847 return E_POINTER;
2849 EnterCriticalSection(&This->lock);
2851 /* call required to get accurate snd_pcm_state() */
2852 avail_frames = snd_pcm_avail_update(This->pcm_handle);
2853 alsa_state = snd_pcm_state(This->pcm_handle);
2854 written_frames = This->written_frames;
2855 held_frames = This->held_frames;
2857 err = snd_pcm_delay(This->pcm_handle, &delay_frames);
2858 if(err < 0){
2859 /* old Pulse, shortly after start */
2860 WARN("snd_pcm_delay failed in state %u: %d (%s)\n", alsa_state, err, snd_strerror(err));
2863 if(This->dataflow == eRender){
2864 position = written_frames - held_frames; /* maximum */
2865 if(!This->started || alsa_state > SND_PCM_STATE_RUNNING)
2866 ; /* mmdevapi stopped or ALSA underrun: pretend everything was played */
2867 else if(err<0 || delay_frames > position - This->last_pos_frames)
2868 /* Pulse bug: past underrun, despite recovery, avail_frames & delay
2869 * may be larger than alsa_bufsize_frames, as if cumulating frames. */
2870 /* Pulse bug: EIO(-5) shortly after starting: nothing played */
2871 position = This->last_pos_frames;
2872 else if(delay_frames > 0)
2873 position -= delay_frames;
2874 }else
2875 position = written_frames + held_frames;
2877 /* ensure monotic growth */
2878 This->last_pos_frames = position;
2880 LeaveCriticalSection(&This->lock);
2882 TRACE("frames written: %u, held: %u, avail: %ld, delay: %ld state %d, pos: %u\n",
2883 (UINT32)(written_frames%1000000000), held_frames,
2884 avail_frames, delay_frames, alsa_state, (UINT32)(position%1000000000));
2885 if(This->share == AUDCLNT_SHAREMODE_SHARED)
2886 *pos = position * This->fmt->nBlockAlign;
2887 else
2888 *pos = position;
2890 if(qpctime){
2891 LARGE_INTEGER stamp, freq;
2892 QueryPerformanceCounter(&stamp);
2893 QueryPerformanceFrequency(&freq);
2894 *qpctime = (stamp.QuadPart * (INT64)10000000) / freq.QuadPart;
2897 return S_OK;
2900 static HRESULT WINAPI AudioClock_GetCharacteristics(IAudioClock *iface,
2901 DWORD *chars)
2903 ACImpl *This = impl_from_IAudioClock(iface);
2905 TRACE("(%p)->(%p)\n", This, chars);
2907 if(!chars)
2908 return E_POINTER;
2910 *chars = AUDIOCLOCK_CHARACTERISTIC_FIXED_FREQ;
2912 return S_OK;
2915 static const IAudioClockVtbl AudioClock_Vtbl =
2917 AudioClock_QueryInterface,
2918 AudioClock_AddRef,
2919 AudioClock_Release,
2920 AudioClock_GetFrequency,
2921 AudioClock_GetPosition,
2922 AudioClock_GetCharacteristics
2925 static HRESULT WINAPI AudioClock2_QueryInterface(IAudioClock2 *iface,
2926 REFIID riid, void **ppv)
2928 ACImpl *This = impl_from_IAudioClock2(iface);
2929 return IAudioClock_QueryInterface(&This->IAudioClock_iface, riid, ppv);
2932 static ULONG WINAPI AudioClock2_AddRef(IAudioClock2 *iface)
2934 ACImpl *This = impl_from_IAudioClock2(iface);
2935 return IAudioClient_AddRef(&This->IAudioClient_iface);
2938 static ULONG WINAPI AudioClock2_Release(IAudioClock2 *iface)
2940 ACImpl *This = impl_from_IAudioClock2(iface);
2941 return IAudioClient_Release(&This->IAudioClient_iface);
2944 static HRESULT WINAPI AudioClock2_GetDevicePosition(IAudioClock2 *iface,
2945 UINT64 *pos, UINT64 *qpctime)
2947 ACImpl *This = impl_from_IAudioClock2(iface);
2949 FIXME("(%p)->(%p, %p)\n", This, pos, qpctime);
2951 return E_NOTIMPL;
2954 static const IAudioClock2Vtbl AudioClock2_Vtbl =
2956 AudioClock2_QueryInterface,
2957 AudioClock2_AddRef,
2958 AudioClock2_Release,
2959 AudioClock2_GetDevicePosition
2962 static AudioSessionWrapper *AudioSessionWrapper_Create(ACImpl *client)
2964 AudioSessionWrapper *ret;
2966 ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
2967 sizeof(AudioSessionWrapper));
2968 if(!ret)
2969 return NULL;
2971 ret->IAudioSessionControl2_iface.lpVtbl = &AudioSessionControl2_Vtbl;
2972 ret->ISimpleAudioVolume_iface.lpVtbl = &SimpleAudioVolume_Vtbl;
2973 ret->IChannelAudioVolume_iface.lpVtbl = &ChannelAudioVolume_Vtbl;
2975 ret->ref = 1;
2977 ret->client = client;
2978 if(client){
2979 ret->session = client->session;
2980 AudioClient_AddRef(&client->IAudioClient_iface);
2983 return ret;
2986 static HRESULT WINAPI AudioSessionControl_QueryInterface(
2987 IAudioSessionControl2 *iface, REFIID riid, void **ppv)
2989 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2991 if(!ppv)
2992 return E_POINTER;
2993 *ppv = NULL;
2995 if(IsEqualIID(riid, &IID_IUnknown) ||
2996 IsEqualIID(riid, &IID_IAudioSessionControl) ||
2997 IsEqualIID(riid, &IID_IAudioSessionControl2))
2998 *ppv = iface;
2999 if(*ppv){
3000 IUnknown_AddRef((IUnknown*)*ppv);
3001 return S_OK;
3004 WARN("Unknown interface %s\n", debugstr_guid(riid));
3005 return E_NOINTERFACE;
3008 static ULONG WINAPI AudioSessionControl_AddRef(IAudioSessionControl2 *iface)
3010 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
3011 ULONG ref;
3012 ref = InterlockedIncrement(&This->ref);
3013 TRACE("(%p) Refcount now %u\n", This, ref);
3014 return ref;
3017 static ULONG WINAPI AudioSessionControl_Release(IAudioSessionControl2 *iface)
3019 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
3020 ULONG ref;
3021 ref = InterlockedDecrement(&This->ref);
3022 TRACE("(%p) Refcount now %u\n", This, ref);
3023 if(!ref){
3024 if(This->client){
3025 EnterCriticalSection(&This->client->lock);
3026 This->client->session_wrapper = NULL;
3027 LeaveCriticalSection(&This->client->lock);
3028 AudioClient_Release(&This->client->IAudioClient_iface);
3030 HeapFree(GetProcessHeap(), 0, This);
3032 return ref;
3035 static HRESULT WINAPI AudioSessionControl_GetState(IAudioSessionControl2 *iface,
3036 AudioSessionState *state)
3038 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
3039 ACImpl *client;
3041 TRACE("(%p)->(%p)\n", This, state);
3043 if(!state)
3044 return NULL_PTR_ERR;
3046 EnterCriticalSection(&g_sessions_lock);
3048 if(list_empty(&This->session->clients)){
3049 *state = AudioSessionStateExpired;
3050 LeaveCriticalSection(&g_sessions_lock);
3051 return S_OK;
3054 LIST_FOR_EACH_ENTRY(client, &This->session->clients, ACImpl, entry){
3055 EnterCriticalSection(&client->lock);
3056 if(client->started){
3057 *state = AudioSessionStateActive;
3058 LeaveCriticalSection(&client->lock);
3059 LeaveCriticalSection(&g_sessions_lock);
3060 return S_OK;
3062 LeaveCriticalSection(&client->lock);
3065 LeaveCriticalSection(&g_sessions_lock);
3067 *state = AudioSessionStateInactive;
3069 return S_OK;
3072 static HRESULT WINAPI AudioSessionControl_GetDisplayName(
3073 IAudioSessionControl2 *iface, WCHAR **name)
3075 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
3077 FIXME("(%p)->(%p) - stub\n", This, name);
3079 return E_NOTIMPL;
3082 static HRESULT WINAPI AudioSessionControl_SetDisplayName(
3083 IAudioSessionControl2 *iface, const WCHAR *name, const GUID *session)
3085 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
3087 FIXME("(%p)->(%p, %s) - stub\n", This, name, debugstr_guid(session));
3089 return E_NOTIMPL;
3092 static HRESULT WINAPI AudioSessionControl_GetIconPath(
3093 IAudioSessionControl2 *iface, WCHAR **path)
3095 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
3097 FIXME("(%p)->(%p) - stub\n", This, path);
3099 return E_NOTIMPL;
3102 static HRESULT WINAPI AudioSessionControl_SetIconPath(
3103 IAudioSessionControl2 *iface, const WCHAR *path, const GUID *session)
3105 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
3107 FIXME("(%p)->(%p, %s) - stub\n", This, path, debugstr_guid(session));
3109 return E_NOTIMPL;
3112 static HRESULT WINAPI AudioSessionControl_GetGroupingParam(
3113 IAudioSessionControl2 *iface, GUID *group)
3115 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
3117 FIXME("(%p)->(%p) - stub\n", This, group);
3119 return E_NOTIMPL;
3122 static HRESULT WINAPI AudioSessionControl_SetGroupingParam(
3123 IAudioSessionControl2 *iface, const GUID *group, const GUID *session)
3125 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
3127 FIXME("(%p)->(%s, %s) - stub\n", This, debugstr_guid(group),
3128 debugstr_guid(session));
3130 return E_NOTIMPL;
3133 static HRESULT WINAPI AudioSessionControl_RegisterAudioSessionNotification(
3134 IAudioSessionControl2 *iface, IAudioSessionEvents *events)
3136 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
3138 FIXME("(%p)->(%p) - stub\n", This, events);
3140 return S_OK;
3143 static HRESULT WINAPI AudioSessionControl_UnregisterAudioSessionNotification(
3144 IAudioSessionControl2 *iface, IAudioSessionEvents *events)
3146 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
3148 FIXME("(%p)->(%p) - stub\n", This, events);
3150 return S_OK;
3153 static HRESULT WINAPI AudioSessionControl_GetSessionIdentifier(
3154 IAudioSessionControl2 *iface, WCHAR **id)
3156 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
3158 FIXME("(%p)->(%p) - stub\n", This, id);
3160 return E_NOTIMPL;
3163 static HRESULT WINAPI AudioSessionControl_GetSessionInstanceIdentifier(
3164 IAudioSessionControl2 *iface, WCHAR **id)
3166 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
3168 FIXME("(%p)->(%p) - stub\n", This, id);
3170 return E_NOTIMPL;
3173 static HRESULT WINAPI AudioSessionControl_GetProcessId(
3174 IAudioSessionControl2 *iface, DWORD *pid)
3176 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
3178 TRACE("(%p)->(%p)\n", This, pid);
3180 if(!pid)
3181 return E_POINTER;
3183 *pid = GetCurrentProcessId();
3185 return S_OK;
3188 static HRESULT WINAPI AudioSessionControl_IsSystemSoundsSession(
3189 IAudioSessionControl2 *iface)
3191 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
3193 TRACE("(%p)\n", This);
3195 return S_FALSE;
3198 static HRESULT WINAPI AudioSessionControl_SetDuckingPreference(
3199 IAudioSessionControl2 *iface, BOOL optout)
3201 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
3203 TRACE("(%p)->(%d)\n", This, optout);
3205 return S_OK;
3208 static const IAudioSessionControl2Vtbl AudioSessionControl2_Vtbl =
3210 AudioSessionControl_QueryInterface,
3211 AudioSessionControl_AddRef,
3212 AudioSessionControl_Release,
3213 AudioSessionControl_GetState,
3214 AudioSessionControl_GetDisplayName,
3215 AudioSessionControl_SetDisplayName,
3216 AudioSessionControl_GetIconPath,
3217 AudioSessionControl_SetIconPath,
3218 AudioSessionControl_GetGroupingParam,
3219 AudioSessionControl_SetGroupingParam,
3220 AudioSessionControl_RegisterAudioSessionNotification,
3221 AudioSessionControl_UnregisterAudioSessionNotification,
3222 AudioSessionControl_GetSessionIdentifier,
3223 AudioSessionControl_GetSessionInstanceIdentifier,
3224 AudioSessionControl_GetProcessId,
3225 AudioSessionControl_IsSystemSoundsSession,
3226 AudioSessionControl_SetDuckingPreference
3229 static HRESULT WINAPI SimpleAudioVolume_QueryInterface(
3230 ISimpleAudioVolume *iface, REFIID riid, void **ppv)
3232 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
3234 if(!ppv)
3235 return E_POINTER;
3236 *ppv = NULL;
3238 if(IsEqualIID(riid, &IID_IUnknown) ||
3239 IsEqualIID(riid, &IID_ISimpleAudioVolume))
3240 *ppv = iface;
3241 if(*ppv){
3242 IUnknown_AddRef((IUnknown*)*ppv);
3243 return S_OK;
3246 WARN("Unknown interface %s\n", debugstr_guid(riid));
3247 return E_NOINTERFACE;
3250 static ULONG WINAPI SimpleAudioVolume_AddRef(ISimpleAudioVolume *iface)
3252 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
3253 return AudioSessionControl_AddRef(&This->IAudioSessionControl2_iface);
3256 static ULONG WINAPI SimpleAudioVolume_Release(ISimpleAudioVolume *iface)
3258 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
3259 return AudioSessionControl_Release(&This->IAudioSessionControl2_iface);
3262 static HRESULT WINAPI SimpleAudioVolume_SetMasterVolume(
3263 ISimpleAudioVolume *iface, float level, const GUID *context)
3265 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
3266 AudioSession *session = This->session;
3268 TRACE("(%p)->(%f, %s)\n", session, level, wine_dbgstr_guid(context));
3270 if(level < 0.f || level > 1.f)
3271 return E_INVALIDARG;
3273 if(context)
3274 FIXME("Notifications not supported yet\n");
3276 TRACE("ALSA does not support volume control\n");
3278 EnterCriticalSection(&session->lock);
3280 session->master_vol = level;
3282 LeaveCriticalSection(&session->lock);
3284 return S_OK;
3287 static HRESULT WINAPI SimpleAudioVolume_GetMasterVolume(
3288 ISimpleAudioVolume *iface, float *level)
3290 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
3291 AudioSession *session = This->session;
3293 TRACE("(%p)->(%p)\n", session, level);
3295 if(!level)
3296 return NULL_PTR_ERR;
3298 *level = session->master_vol;
3300 return S_OK;
3303 static HRESULT WINAPI SimpleAudioVolume_SetMute(ISimpleAudioVolume *iface,
3304 BOOL mute, const GUID *context)
3306 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
3307 AudioSession *session = This->session;
3309 TRACE("(%p)->(%u, %p)\n", session, mute, context);
3311 if(context)
3312 FIXME("Notifications not supported yet\n");
3314 session->mute = mute;
3316 return S_OK;
3319 static HRESULT WINAPI SimpleAudioVolume_GetMute(ISimpleAudioVolume *iface,
3320 BOOL *mute)
3322 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
3323 AudioSession *session = This->session;
3325 TRACE("(%p)->(%p)\n", session, mute);
3327 if(!mute)
3328 return NULL_PTR_ERR;
3330 *mute = session->mute;
3332 return S_OK;
3335 static const ISimpleAudioVolumeVtbl SimpleAudioVolume_Vtbl =
3337 SimpleAudioVolume_QueryInterface,
3338 SimpleAudioVolume_AddRef,
3339 SimpleAudioVolume_Release,
3340 SimpleAudioVolume_SetMasterVolume,
3341 SimpleAudioVolume_GetMasterVolume,
3342 SimpleAudioVolume_SetMute,
3343 SimpleAudioVolume_GetMute
3346 static HRESULT WINAPI AudioStreamVolume_QueryInterface(
3347 IAudioStreamVolume *iface, REFIID riid, void **ppv)
3349 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
3351 if(!ppv)
3352 return E_POINTER;
3353 *ppv = NULL;
3355 if(IsEqualIID(riid, &IID_IUnknown) ||
3356 IsEqualIID(riid, &IID_IAudioStreamVolume))
3357 *ppv = iface;
3358 if(*ppv){
3359 IUnknown_AddRef((IUnknown*)*ppv);
3360 return S_OK;
3363 WARN("Unknown interface %s\n", debugstr_guid(riid));
3364 return E_NOINTERFACE;
3367 static ULONG WINAPI AudioStreamVolume_AddRef(IAudioStreamVolume *iface)
3369 ACImpl *This = impl_from_IAudioStreamVolume(iface);
3370 return IAudioClient_AddRef(&This->IAudioClient_iface);
3373 static ULONG WINAPI AudioStreamVolume_Release(IAudioStreamVolume *iface)
3375 ACImpl *This = impl_from_IAudioStreamVolume(iface);
3376 return IAudioClient_Release(&This->IAudioClient_iface);
3379 static HRESULT WINAPI AudioStreamVolume_GetChannelCount(
3380 IAudioStreamVolume *iface, UINT32 *out)
3382 ACImpl *This = impl_from_IAudioStreamVolume(iface);
3384 TRACE("(%p)->(%p)\n", This, out);
3386 if(!out)
3387 return E_POINTER;
3389 *out = This->fmt->nChannels;
3391 return S_OK;
3394 static HRESULT WINAPI AudioStreamVolume_SetChannelVolume(
3395 IAudioStreamVolume *iface, UINT32 index, float level)
3397 ACImpl *This = impl_from_IAudioStreamVolume(iface);
3399 TRACE("(%p)->(%d, %f)\n", This, index, level);
3401 if(level < 0.f || level > 1.f)
3402 return E_INVALIDARG;
3404 if(index >= This->fmt->nChannels)
3405 return E_INVALIDARG;
3407 TRACE("ALSA does not support volume control\n");
3409 EnterCriticalSection(&This->lock);
3411 This->vols[index] = level;
3413 LeaveCriticalSection(&This->lock);
3415 return S_OK;
3418 static HRESULT WINAPI AudioStreamVolume_GetChannelVolume(
3419 IAudioStreamVolume *iface, UINT32 index, float *level)
3421 ACImpl *This = impl_from_IAudioStreamVolume(iface);
3423 TRACE("(%p)->(%d, %p)\n", This, index, level);
3425 if(!level)
3426 return E_POINTER;
3428 if(index >= This->fmt->nChannels)
3429 return E_INVALIDARG;
3431 *level = This->vols[index];
3433 return S_OK;
3436 static HRESULT WINAPI AudioStreamVolume_SetAllVolumes(
3437 IAudioStreamVolume *iface, UINT32 count, const float *levels)
3439 ACImpl *This = impl_from_IAudioStreamVolume(iface);
3440 unsigned int i;
3442 TRACE("(%p)->(%d, %p)\n", This, count, levels);
3444 if(!levels)
3445 return E_POINTER;
3447 if(count != This->fmt->nChannels)
3448 return E_INVALIDARG;
3450 TRACE("ALSA does not support volume control\n");
3452 EnterCriticalSection(&This->lock);
3454 for(i = 0; i < count; ++i)
3455 This->vols[i] = levels[i];
3457 LeaveCriticalSection(&This->lock);
3459 return S_OK;
3462 static HRESULT WINAPI AudioStreamVolume_GetAllVolumes(
3463 IAudioStreamVolume *iface, UINT32 count, float *levels)
3465 ACImpl *This = impl_from_IAudioStreamVolume(iface);
3466 unsigned int i;
3468 TRACE("(%p)->(%d, %p)\n", This, count, levels);
3470 if(!levels)
3471 return E_POINTER;
3473 if(count != This->fmt->nChannels)
3474 return E_INVALIDARG;
3476 EnterCriticalSection(&This->lock);
3478 for(i = 0; i < count; ++i)
3479 levels[i] = This->vols[i];
3481 LeaveCriticalSection(&This->lock);
3483 return S_OK;
3486 static const IAudioStreamVolumeVtbl AudioStreamVolume_Vtbl =
3488 AudioStreamVolume_QueryInterface,
3489 AudioStreamVolume_AddRef,
3490 AudioStreamVolume_Release,
3491 AudioStreamVolume_GetChannelCount,
3492 AudioStreamVolume_SetChannelVolume,
3493 AudioStreamVolume_GetChannelVolume,
3494 AudioStreamVolume_SetAllVolumes,
3495 AudioStreamVolume_GetAllVolumes
3498 static HRESULT WINAPI ChannelAudioVolume_QueryInterface(
3499 IChannelAudioVolume *iface, REFIID riid, void **ppv)
3501 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
3503 if(!ppv)
3504 return E_POINTER;
3505 *ppv = NULL;
3507 if(IsEqualIID(riid, &IID_IUnknown) ||
3508 IsEqualIID(riid, &IID_IChannelAudioVolume))
3509 *ppv = iface;
3510 if(*ppv){
3511 IUnknown_AddRef((IUnknown*)*ppv);
3512 return S_OK;
3515 WARN("Unknown interface %s\n", debugstr_guid(riid));
3516 return E_NOINTERFACE;
3519 static ULONG WINAPI ChannelAudioVolume_AddRef(IChannelAudioVolume *iface)
3521 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
3522 return AudioSessionControl_AddRef(&This->IAudioSessionControl2_iface);
3525 static ULONG WINAPI ChannelAudioVolume_Release(IChannelAudioVolume *iface)
3527 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
3528 return AudioSessionControl_Release(&This->IAudioSessionControl2_iface);
3531 static HRESULT WINAPI ChannelAudioVolume_GetChannelCount(
3532 IChannelAudioVolume *iface, UINT32 *out)
3534 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
3535 AudioSession *session = This->session;
3537 TRACE("(%p)->(%p)\n", session, out);
3539 if(!out)
3540 return NULL_PTR_ERR;
3542 *out = session->channel_count;
3544 return S_OK;
3547 static HRESULT WINAPI ChannelAudioVolume_SetChannelVolume(
3548 IChannelAudioVolume *iface, UINT32 index, float level,
3549 const GUID *context)
3551 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
3552 AudioSession *session = This->session;
3554 TRACE("(%p)->(%d, %f, %s)\n", session, index, level,
3555 wine_dbgstr_guid(context));
3557 if(level < 0.f || level > 1.f)
3558 return E_INVALIDARG;
3560 if(index >= session->channel_count)
3561 return E_INVALIDARG;
3563 if(context)
3564 FIXME("Notifications not supported yet\n");
3566 TRACE("ALSA does not support volume control\n");
3568 EnterCriticalSection(&session->lock);
3570 session->channel_vols[index] = level;
3572 LeaveCriticalSection(&session->lock);
3574 return S_OK;
3577 static HRESULT WINAPI ChannelAudioVolume_GetChannelVolume(
3578 IChannelAudioVolume *iface, UINT32 index, float *level)
3580 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
3581 AudioSession *session = This->session;
3583 TRACE("(%p)->(%d, %p)\n", session, index, level);
3585 if(!level)
3586 return NULL_PTR_ERR;
3588 if(index >= session->channel_count)
3589 return E_INVALIDARG;
3591 *level = session->channel_vols[index];
3593 return S_OK;
3596 static HRESULT WINAPI ChannelAudioVolume_SetAllVolumes(
3597 IChannelAudioVolume *iface, UINT32 count, const float *levels,
3598 const GUID *context)
3600 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
3601 AudioSession *session = This->session;
3602 unsigned int i;
3604 TRACE("(%p)->(%d, %p, %s)\n", session, count, levels,
3605 wine_dbgstr_guid(context));
3607 if(!levels)
3608 return NULL_PTR_ERR;
3610 if(count != session->channel_count)
3611 return E_INVALIDARG;
3613 if(context)
3614 FIXME("Notifications not supported yet\n");
3616 TRACE("ALSA does not support volume control\n");
3618 EnterCriticalSection(&session->lock);
3620 for(i = 0; i < count; ++i)
3621 session->channel_vols[i] = levels[i];
3623 LeaveCriticalSection(&session->lock);
3625 return S_OK;
3628 static HRESULT WINAPI ChannelAudioVolume_GetAllVolumes(
3629 IChannelAudioVolume *iface, UINT32 count, float *levels)
3631 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
3632 AudioSession *session = This->session;
3633 unsigned int i;
3635 TRACE("(%p)->(%d, %p)\n", session, count, levels);
3637 if(!levels)
3638 return NULL_PTR_ERR;
3640 if(count != session->channel_count)
3641 return E_INVALIDARG;
3643 for(i = 0; i < count; ++i)
3644 levels[i] = session->channel_vols[i];
3646 return S_OK;
3649 static const IChannelAudioVolumeVtbl ChannelAudioVolume_Vtbl =
3651 ChannelAudioVolume_QueryInterface,
3652 ChannelAudioVolume_AddRef,
3653 ChannelAudioVolume_Release,
3654 ChannelAudioVolume_GetChannelCount,
3655 ChannelAudioVolume_SetChannelVolume,
3656 ChannelAudioVolume_GetChannelVolume,
3657 ChannelAudioVolume_SetAllVolumes,
3658 ChannelAudioVolume_GetAllVolumes
3661 static HRESULT WINAPI AudioSessionManager_QueryInterface(IAudioSessionManager2 *iface,
3662 REFIID riid, void **ppv)
3664 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
3666 if(!ppv)
3667 return E_POINTER;
3668 *ppv = NULL;
3670 if(IsEqualIID(riid, &IID_IUnknown) ||
3671 IsEqualIID(riid, &IID_IAudioSessionManager) ||
3672 IsEqualIID(riid, &IID_IAudioSessionManager2))
3673 *ppv = iface;
3674 if(*ppv){
3675 IUnknown_AddRef((IUnknown*)*ppv);
3676 return S_OK;
3679 WARN("Unknown interface %s\n", debugstr_guid(riid));
3680 return E_NOINTERFACE;
3683 static ULONG WINAPI AudioSessionManager_AddRef(IAudioSessionManager2 *iface)
3685 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3686 ULONG ref;
3687 ref = InterlockedIncrement(&This->ref);
3688 TRACE("(%p) Refcount now %u\n", This, ref);
3689 return ref;
3692 static ULONG WINAPI AudioSessionManager_Release(IAudioSessionManager2 *iface)
3694 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3695 ULONG ref;
3696 ref = InterlockedDecrement(&This->ref);
3697 TRACE("(%p) Refcount now %u\n", This, ref);
3698 if(!ref)
3699 HeapFree(GetProcessHeap(), 0, This);
3700 return ref;
3703 static HRESULT WINAPI AudioSessionManager_GetAudioSessionControl(
3704 IAudioSessionManager2 *iface, const GUID *session_guid, DWORD flags,
3705 IAudioSessionControl **out)
3707 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3708 AudioSession *session;
3709 AudioSessionWrapper *wrapper;
3710 HRESULT hr;
3712 TRACE("(%p)->(%s, %x, %p)\n", This, debugstr_guid(session_guid),
3713 flags, out);
3715 hr = get_audio_session(session_guid, This->device, 0, &session);
3716 if(FAILED(hr))
3717 return hr;
3719 wrapper = AudioSessionWrapper_Create(NULL);
3720 if(!wrapper)
3721 return E_OUTOFMEMORY;
3723 wrapper->session = session;
3725 *out = (IAudioSessionControl*)&wrapper->IAudioSessionControl2_iface;
3727 return S_OK;
3730 static HRESULT WINAPI AudioSessionManager_GetSimpleAudioVolume(
3731 IAudioSessionManager2 *iface, const GUID *session_guid, DWORD flags,
3732 ISimpleAudioVolume **out)
3734 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3735 AudioSession *session;
3736 AudioSessionWrapper *wrapper;
3737 HRESULT hr;
3739 TRACE("(%p)->(%s, %x, %p)\n", This, debugstr_guid(session_guid),
3740 flags, out);
3742 hr = get_audio_session(session_guid, This->device, 0, &session);
3743 if(FAILED(hr))
3744 return hr;
3746 wrapper = AudioSessionWrapper_Create(NULL);
3747 if(!wrapper)
3748 return E_OUTOFMEMORY;
3750 wrapper->session = session;
3752 *out = &wrapper->ISimpleAudioVolume_iface;
3754 return S_OK;
3757 static HRESULT WINAPI AudioSessionManager_GetSessionEnumerator(
3758 IAudioSessionManager2 *iface, IAudioSessionEnumerator **out)
3760 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3761 FIXME("(%p)->(%p) - stub\n", This, out);
3762 return E_NOTIMPL;
3765 static HRESULT WINAPI AudioSessionManager_RegisterSessionNotification(
3766 IAudioSessionManager2 *iface, IAudioSessionNotification *notification)
3768 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3769 FIXME("(%p)->(%p) - stub\n", This, notification);
3770 return E_NOTIMPL;
3773 static HRESULT WINAPI AudioSessionManager_UnregisterSessionNotification(
3774 IAudioSessionManager2 *iface, IAudioSessionNotification *notification)
3776 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3777 FIXME("(%p)->(%p) - stub\n", This, notification);
3778 return E_NOTIMPL;
3781 static HRESULT WINAPI AudioSessionManager_RegisterDuckNotification(
3782 IAudioSessionManager2 *iface, const WCHAR *session_id,
3783 IAudioVolumeDuckNotification *notification)
3785 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3786 FIXME("(%p)->(%p) - stub\n", This, notification);
3787 return E_NOTIMPL;
3790 static HRESULT WINAPI AudioSessionManager_UnregisterDuckNotification(
3791 IAudioSessionManager2 *iface,
3792 IAudioVolumeDuckNotification *notification)
3794 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3795 FIXME("(%p)->(%p) - stub\n", This, notification);
3796 return E_NOTIMPL;
3799 static const IAudioSessionManager2Vtbl AudioSessionManager2_Vtbl =
3801 AudioSessionManager_QueryInterface,
3802 AudioSessionManager_AddRef,
3803 AudioSessionManager_Release,
3804 AudioSessionManager_GetAudioSessionControl,
3805 AudioSessionManager_GetSimpleAudioVolume,
3806 AudioSessionManager_GetSessionEnumerator,
3807 AudioSessionManager_RegisterSessionNotification,
3808 AudioSessionManager_UnregisterSessionNotification,
3809 AudioSessionManager_RegisterDuckNotification,
3810 AudioSessionManager_UnregisterDuckNotification
3813 HRESULT WINAPI AUDDRV_GetAudioSessionManager(IMMDevice *device,
3814 IAudioSessionManager2 **out)
3816 SessionMgr *This;
3818 This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(SessionMgr));
3819 if(!This)
3820 return E_OUTOFMEMORY;
3822 This->IAudioSessionManager2_iface.lpVtbl = &AudioSessionManager2_Vtbl;
3823 This->device = device;
3824 This->ref = 1;
3826 *out = &This->IAudioSessionManager2_iface;
3828 return S_OK;