combase: Add CoDecodeProxy stub.
[wine.git] / dlls / winealsa.drv / mmdevdrv.c
blob5f7d277ce9a354194123dbf3b01ef04f1c31db2d
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 #define _GNU_SOURCE
26 #include <stdio.h>
27 #include <math.h>
29 #include "windef.h"
30 #include "winbase.h"
31 #include "winnls.h"
32 #include "winreg.h"
33 #include "wine/debug.h"
34 #include "wine/unicode.h"
35 #include "wine/list.h"
37 #include "propsys.h"
38 #include "initguid.h"
39 #include "ole2.h"
40 #include "propkey.h"
41 #include "mmdeviceapi.h"
42 #include "devpkey.h"
43 #include "mmsystem.h"
44 #include "dsound.h"
46 #include "initguid.h"
47 #include "endpointvolume.h"
48 #include "audioclient.h"
49 #include "audiopolicy.h"
51 #include <alsa/asoundlib.h>
53 WINE_DEFAULT_DEBUG_CHANNEL(alsa);
54 WINE_DECLARE_DEBUG_CHANNEL(winediag);
56 #define NULL_PTR_ERR MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, RPC_X_NULL_REF_POINTER)
58 static const REFERENCE_TIME DefaultPeriod = 100000;
59 static const REFERENCE_TIME MinimumPeriod = 50000;
60 #define EXTRA_SAFE_RT 40000
62 struct ACImpl;
63 typedef struct ACImpl ACImpl;
65 typedef struct _AudioSession {
66 GUID guid;
67 struct list clients;
69 IMMDevice *device;
71 float master_vol;
72 UINT32 channel_count;
73 float *channel_vols;
74 BOOL mute;
76 CRITICAL_SECTION lock;
78 struct list entry;
79 } AudioSession;
81 typedef struct _AudioSessionWrapper {
82 IAudioSessionControl2 IAudioSessionControl2_iface;
83 IChannelAudioVolume IChannelAudioVolume_iface;
84 ISimpleAudioVolume ISimpleAudioVolume_iface;
86 LONG ref;
88 ACImpl *client;
89 AudioSession *session;
90 } AudioSessionWrapper;
92 struct ACImpl {
93 IAudioClient3 IAudioClient3_iface;
94 IAudioRenderClient IAudioRenderClient_iface;
95 IAudioCaptureClient IAudioCaptureClient_iface;
96 IAudioClock IAudioClock_iface;
97 IAudioClock2 IAudioClock2_iface;
98 IAudioStreamVolume IAudioStreamVolume_iface;
100 LONG ref;
102 snd_pcm_t *pcm_handle;
103 snd_pcm_uframes_t alsa_bufsize_frames, alsa_period_frames, safe_rewind_frames;
104 snd_pcm_hw_params_t *hw_params; /* does not hold state between calls */
105 snd_pcm_format_t alsa_format;
107 LARGE_INTEGER last_period_time;
109 IMMDevice *parent;
110 IUnknown *pUnkFTMarshal;
112 EDataFlow dataflow;
113 WAVEFORMATEX *fmt;
114 DWORD flags;
115 AUDCLNT_SHAREMODE share;
116 HANDLE event;
117 float *vols;
119 BOOL need_remapping;
120 int alsa_channels;
121 int alsa_channel_map[32];
123 BOOL initted, started;
124 REFERENCE_TIME mmdev_period_rt;
125 UINT64 written_frames, last_pos_frames;
126 UINT32 bufsize_frames, held_frames, tmp_buffer_frames, mmdev_period_frames;
127 snd_pcm_uframes_t remapping_buf_frames;
128 UINT32 lcl_offs_frames; /* offs into local_buffer where valid data starts */
129 UINT32 wri_offs_frames; /* where to write fresh data in local_buffer */
130 UINT32 hidden_frames; /* ALSA reserve to ensure continuous rendering */
131 UINT32 vol_adjusted_frames; /* Frames we've already adjusted the volume of but didn't write yet */
132 UINT32 data_in_alsa_frames;
134 HANDLE timer;
135 BYTE *local_buffer, *tmp_buffer, *remapping_buf, *silence_buf;
136 LONG32 getbuf_last; /* <0 when using tmp_buffer */
138 CRITICAL_SECTION lock;
140 AudioSession *session;
141 AudioSessionWrapper *session_wrapper;
143 struct list entry;
146 typedef struct _SessionMgr {
147 IAudioSessionManager2 IAudioSessionManager2_iface;
149 LONG ref;
151 IMMDevice *device;
152 } SessionMgr;
154 static HANDLE g_timer_q;
156 static CRITICAL_SECTION g_sessions_lock;
157 static CRITICAL_SECTION_DEBUG g_sessions_lock_debug =
159 0, 0, &g_sessions_lock,
160 { &g_sessions_lock_debug.ProcessLocksList, &g_sessions_lock_debug.ProcessLocksList },
161 0, 0, { (DWORD_PTR)(__FILE__ ": g_sessions_lock") }
163 static CRITICAL_SECTION g_sessions_lock = { &g_sessions_lock_debug, -1, 0, 0, 0, 0 };
164 static struct list g_sessions = LIST_INIT(g_sessions);
166 static const WCHAR defaultW[] = {'d','e','f','a','u','l','t',0};
167 static const char defname[] = "default";
169 static const WCHAR drv_keyW[] = {'S','o','f','t','w','a','r','e','\\',
170 'W','i','n','e','\\','D','r','i','v','e','r','s','\\',
171 'w','i','n','e','a','l','s','a','.','d','r','v',0};
172 static const WCHAR drv_key_devicesW[] = {'S','o','f','t','w','a','r','e','\\',
173 'W','i','n','e','\\','D','r','i','v','e','r','s','\\',
174 'w','i','n','e','a','l','s','a','.','d','r','v','\\','d','e','v','i','c','e','s',0};
175 static const WCHAR guidW[] = {'g','u','i','d',0};
177 static const IAudioClient3Vtbl AudioClient3_Vtbl;
178 static const IAudioRenderClientVtbl AudioRenderClient_Vtbl;
179 static const IAudioCaptureClientVtbl AudioCaptureClient_Vtbl;
180 static const IAudioSessionControl2Vtbl AudioSessionControl2_Vtbl;
181 static const ISimpleAudioVolumeVtbl SimpleAudioVolume_Vtbl;
182 static const IAudioClockVtbl AudioClock_Vtbl;
183 static const IAudioClock2Vtbl AudioClock2_Vtbl;
184 static const IAudioStreamVolumeVtbl AudioStreamVolume_Vtbl;
185 static const IChannelAudioVolumeVtbl ChannelAudioVolume_Vtbl;
186 static const IAudioSessionManager2Vtbl AudioSessionManager2_Vtbl;
188 static AudioSessionWrapper *AudioSessionWrapper_Create(ACImpl *client);
190 static inline ACImpl *impl_from_IAudioClient3(IAudioClient3 *iface)
192 return CONTAINING_RECORD(iface, ACImpl, IAudioClient3_iface);
195 static inline ACImpl *impl_from_IAudioRenderClient(IAudioRenderClient *iface)
197 return CONTAINING_RECORD(iface, ACImpl, IAudioRenderClient_iface);
200 static inline ACImpl *impl_from_IAudioCaptureClient(IAudioCaptureClient *iface)
202 return CONTAINING_RECORD(iface, ACImpl, IAudioCaptureClient_iface);
205 static inline AudioSessionWrapper *impl_from_IAudioSessionControl2(IAudioSessionControl2 *iface)
207 return CONTAINING_RECORD(iface, AudioSessionWrapper, IAudioSessionControl2_iface);
210 static inline AudioSessionWrapper *impl_from_ISimpleAudioVolume(ISimpleAudioVolume *iface)
212 return CONTAINING_RECORD(iface, AudioSessionWrapper, ISimpleAudioVolume_iface);
215 static inline AudioSessionWrapper *impl_from_IChannelAudioVolume(IChannelAudioVolume *iface)
217 return CONTAINING_RECORD(iface, AudioSessionWrapper, IChannelAudioVolume_iface);
220 static inline ACImpl *impl_from_IAudioClock(IAudioClock *iface)
222 return CONTAINING_RECORD(iface, ACImpl, IAudioClock_iface);
225 static inline ACImpl *impl_from_IAudioClock2(IAudioClock2 *iface)
227 return CONTAINING_RECORD(iface, ACImpl, IAudioClock2_iface);
230 static inline ACImpl *impl_from_IAudioStreamVolume(IAudioStreamVolume *iface)
232 return CONTAINING_RECORD(iface, ACImpl, IAudioStreamVolume_iface);
235 static inline SessionMgr *impl_from_IAudioSessionManager2(IAudioSessionManager2 *iface)
237 return CONTAINING_RECORD(iface, SessionMgr, IAudioSessionManager2_iface);
240 BOOL WINAPI DllMain(HINSTANCE dll, DWORD reason, void *reserved)
242 switch (reason)
244 case DLL_PROCESS_ATTACH:
245 g_timer_q = CreateTimerQueue();
246 if(!g_timer_q)
247 return FALSE;
248 break;
250 case DLL_PROCESS_DETACH:
251 if (reserved) break;
252 DeleteCriticalSection(&g_sessions_lock);
253 break;
255 return TRUE;
258 /* From <dlls/mmdevapi/mmdevapi.h> */
259 enum DriverPriority {
260 Priority_Unavailable = 0,
261 Priority_Low,
262 Priority_Neutral,
263 Priority_Preferred
266 int WINAPI AUDDRV_GetPriority(void)
268 return Priority_Neutral;
271 static void set_device_guid(EDataFlow flow, HKEY drv_key, const WCHAR *key_name,
272 GUID *guid)
274 HKEY key;
275 BOOL opened = FALSE;
276 LONG lr;
278 if(!drv_key){
279 lr = RegCreateKeyExW(HKEY_CURRENT_USER, drv_key_devicesW, 0, NULL, 0, KEY_WRITE,
280 NULL, &drv_key, NULL);
281 if(lr != ERROR_SUCCESS){
282 ERR("RegCreateKeyEx(drv_key) failed: %u\n", lr);
283 return;
285 opened = TRUE;
288 lr = RegCreateKeyExW(drv_key, key_name, 0, NULL, 0, KEY_WRITE,
289 NULL, &key, NULL);
290 if(lr != ERROR_SUCCESS){
291 ERR("RegCreateKeyEx(%s) failed: %u\n", wine_dbgstr_w(key_name), lr);
292 goto exit;
295 lr = RegSetValueExW(key, guidW, 0, REG_BINARY, (BYTE*)guid,
296 sizeof(GUID));
297 if(lr != ERROR_SUCCESS)
298 ERR("RegSetValueEx(%s\\guid) failed: %u\n", wine_dbgstr_w(key_name), lr);
300 RegCloseKey(key);
301 exit:
302 if(opened)
303 RegCloseKey(drv_key);
306 static void get_device_guid(EDataFlow flow, const char *device, GUID *guid)
308 HKEY key = NULL, dev_key;
309 DWORD type, size = sizeof(*guid);
310 WCHAR key_name[256];
312 if(flow == eCapture)
313 key_name[0] = '1';
314 else
315 key_name[0] = '0';
316 key_name[1] = ',';
317 MultiByteToWideChar(CP_UNIXCP, 0, device, -1, key_name + 2, ARRAY_SIZE(key_name) - 2);
319 if(RegOpenKeyExW(HKEY_CURRENT_USER, drv_key_devicesW, 0, KEY_WRITE|KEY_READ, &key) == ERROR_SUCCESS){
320 if(RegOpenKeyExW(key, key_name, 0, KEY_READ, &dev_key) == ERROR_SUCCESS){
321 if(RegQueryValueExW(dev_key, guidW, 0, &type,
322 (BYTE*)guid, &size) == ERROR_SUCCESS){
323 if(type == REG_BINARY){
324 RegCloseKey(dev_key);
325 RegCloseKey(key);
326 return;
328 ERR("Invalid type for device %s GUID: %u; ignoring and overwriting\n",
329 wine_dbgstr_w(key_name), type);
331 RegCloseKey(dev_key);
335 CoCreateGuid(guid);
337 set_device_guid(flow, key, key_name, guid);
339 if(key)
340 RegCloseKey(key);
343 static BOOL alsa_try_open(const char *devnode, snd_pcm_stream_t stream)
345 snd_pcm_t *handle;
346 int err;
348 TRACE("devnode: %s, stream: %d\n", devnode, stream);
350 if((err = snd_pcm_open(&handle, devnode, stream, SND_PCM_NONBLOCK)) < 0){
351 WARN("The device \"%s\" failed to open: %d (%s).\n",
352 devnode, err, snd_strerror(err));
353 return FALSE;
356 snd_pcm_close(handle);
357 return TRUE;
360 static WCHAR *construct_device_id(EDataFlow flow, const WCHAR *chunk1, const char *chunk2)
362 WCHAR *ret;
363 const WCHAR *prefix;
364 DWORD len_wchars = 0, chunk1_len = 0, copied = 0, prefix_len;
366 static const WCHAR dashW[] = {' ','-',' ',0};
367 static const size_t dashW_len = ARRAY_SIZE(dashW) - 1;
368 static const WCHAR outW[] = {'O','u','t',':',' ',0};
369 static const WCHAR inW[] = {'I','n',':',' ',0};
371 if(flow == eRender){
372 prefix = outW;
373 prefix_len = ARRAY_SIZE(outW) - 1;
374 len_wchars += prefix_len;
375 }else{
376 prefix = inW;
377 prefix_len = ARRAY_SIZE(inW) - 1;
378 len_wchars += prefix_len;
380 if(chunk1){
381 chunk1_len = strlenW(chunk1);
382 len_wchars += chunk1_len;
384 if(chunk1 && chunk2)
385 len_wchars += dashW_len;
386 if(chunk2)
387 len_wchars += MultiByteToWideChar(CP_UNIXCP, 0, chunk2, -1, NULL, 0) - 1;
388 len_wchars += 1; /* NULL byte */
390 ret = HeapAlloc(GetProcessHeap(), 0, len_wchars * sizeof(WCHAR));
392 memcpy(ret, prefix, prefix_len * sizeof(WCHAR));
393 copied += prefix_len;
394 if(chunk1){
395 memcpy(ret + copied, chunk1, chunk1_len * sizeof(WCHAR));
396 copied += chunk1_len;
398 if(chunk1 && chunk2){
399 memcpy(ret + copied, dashW, dashW_len * sizeof(WCHAR));
400 copied += dashW_len;
402 if(chunk2){
403 MultiByteToWideChar(CP_UNIXCP, 0, chunk2, -1, ret + copied, len_wchars - copied);
404 }else
405 ret[copied] = 0;
407 TRACE("Enumerated device: %s\n", wine_dbgstr_w(ret));
409 return ret;
412 static HRESULT alsa_get_card_devices(EDataFlow flow, snd_pcm_stream_t stream,
413 WCHAR ***ids, GUID **guids, UINT *num, snd_ctl_t *ctl, int card,
414 const WCHAR *cardnameW)
416 int err, device;
417 snd_pcm_info_t *info;
419 info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, snd_pcm_info_sizeof());
420 if(!info)
421 return E_OUTOFMEMORY;
423 snd_pcm_info_set_subdevice(info, 0);
424 snd_pcm_info_set_stream(info, stream);
426 device = -1;
427 for(err = snd_ctl_pcm_next_device(ctl, &device); device != -1 && err >= 0;
428 err = snd_ctl_pcm_next_device(ctl, &device)){
429 const char *devname;
430 char devnode[32];
432 snd_pcm_info_set_device(info, device);
434 if((err = snd_ctl_pcm_info(ctl, info)) < 0){
435 if(err == -ENOENT)
436 /* This device doesn't have the right stream direction */
437 continue;
439 WARN("Failed to get info for card %d, device %d: %d (%s)\n",
440 card, device, err, snd_strerror(err));
441 continue;
444 sprintf(devnode, "plughw:%d,%d", card, device);
445 if(!alsa_try_open(devnode, stream))
446 continue;
448 if(*num){
449 *ids = HeapReAlloc(GetProcessHeap(), 0, *ids, sizeof(WCHAR *) * (*num + 1));
450 *guids = HeapReAlloc(GetProcessHeap(), 0, *guids, sizeof(GUID) * (*num + 1));
451 }else{
452 *ids = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR *));
453 *guids = HeapAlloc(GetProcessHeap(), 0, sizeof(GUID));
456 devname = snd_pcm_info_get_name(info);
457 if(!devname){
458 WARN("Unable to get device name for card %d, device %d\n", card,
459 device);
460 continue;
463 (*ids)[*num] = construct_device_id(flow, cardnameW, devname);
464 get_device_guid(flow, devnode, &(*guids)[*num]);
466 ++(*num);
469 HeapFree(GetProcessHeap(), 0, info);
471 if(err != 0)
472 WARN("Got a failure during device enumeration on card %d: %d (%s)\n",
473 card, err, snd_strerror(err));
475 return S_OK;
478 static void get_reg_devices(EDataFlow flow, snd_pcm_stream_t stream, WCHAR ***ids,
479 GUID **guids, UINT *num)
481 static const WCHAR ALSAOutputDevices[] = {'A','L','S','A','O','u','t','p','u','t','D','e','v','i','c','e','s',0};
482 static const WCHAR ALSAInputDevices[] = {'A','L','S','A','I','n','p','u','t','D','e','v','i','c','e','s',0};
483 HKEY key;
484 WCHAR reg_devices[256];
485 DWORD size = sizeof(reg_devices), type;
486 const WCHAR *value_name = (stream == SND_PCM_STREAM_PLAYBACK) ? ALSAOutputDevices : ALSAInputDevices;
488 /* @@ Wine registry key: HKCU\Software\Wine\Drivers\winealsa.drv */
489 if(RegOpenKeyW(HKEY_CURRENT_USER, drv_keyW, &key) == ERROR_SUCCESS){
490 if(RegQueryValueExW(key, value_name, 0, &type,
491 (BYTE*)reg_devices, &size) == ERROR_SUCCESS){
492 WCHAR *p = reg_devices;
494 if(type != REG_MULTI_SZ){
495 ERR("Registry ALSA device list value type must be REG_MULTI_SZ\n");
496 RegCloseKey(key);
497 return;
500 while(*p){
501 char devname[64];
503 WideCharToMultiByte(CP_UNIXCP, 0, p, -1, devname, sizeof(devname), NULL, NULL);
505 if(alsa_try_open(devname, stream)){
506 if(*num){
507 *ids = HeapReAlloc(GetProcessHeap(), 0, *ids, sizeof(WCHAR *) * (*num + 1));
508 *guids = HeapReAlloc(GetProcessHeap(), 0, *guids, sizeof(GUID) * (*num + 1));
509 }else{
510 *ids = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR *));
511 *guids = HeapAlloc(GetProcessHeap(), 0, sizeof(GUID));
513 (*ids)[*num] = construct_device_id(flow, p, NULL);
514 get_device_guid(flow, devname, &(*guids)[*num]);
515 ++*num;
518 p += lstrlenW(p) + 1;
522 RegCloseKey(key);
526 struct card_type {
527 struct list entry;
528 int first_card_number;
529 char string[1];
532 static struct list card_types = LIST_INIT(card_types);
534 static BOOL need_card_number(int card, const char *string)
536 struct card_type *cptr;
538 LIST_FOR_EACH_ENTRY(cptr, &card_types, struct card_type, entry)
540 if(!strcmp(string, cptr->string))
541 return card != cptr->first_card_number;
544 /* this is the first instance of string */
545 cptr = HeapAlloc(GetProcessHeap(), 0, sizeof(struct card_type) + strlen(string));
546 if(!cptr)
547 /* Default to displaying card number if we can't track cards */
548 return TRUE;
550 cptr->first_card_number = card;
551 strcpy(cptr->string, string);
552 list_add_head(&card_types, &cptr->entry);
553 return FALSE;
556 static HRESULT alsa_enum_devices(EDataFlow flow, WCHAR ***ids, GUID **guids,
557 UINT *num)
559 snd_pcm_stream_t stream = (flow == eRender ? SND_PCM_STREAM_PLAYBACK :
560 SND_PCM_STREAM_CAPTURE);
561 int err, card;
563 card = -1;
564 *num = 0;
566 if(alsa_try_open(defname, stream)){
567 *ids = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR *));
568 (*ids)[0] = construct_device_id(flow, defaultW, NULL);
569 *guids = HeapAlloc(GetProcessHeap(), 0, sizeof(GUID));
570 get_device_guid(flow, defname, &(*guids)[0]);
571 ++*num;
574 get_reg_devices(flow, stream, ids, guids, num);
576 for(err = snd_card_next(&card); card != -1 && err >= 0;
577 err = snd_card_next(&card)){
578 char cardpath[64];
579 char *cardname;
580 WCHAR *cardnameW;
581 snd_ctl_t *ctl;
582 DWORD len;
584 sprintf(cardpath, "hw:%u", card);
586 if((err = snd_ctl_open(&ctl, cardpath, 0)) < 0){
587 WARN("Unable to open ctl for ALSA device %s: %d (%s)\n", cardpath,
588 err, snd_strerror(err));
589 continue;
592 if(snd_card_get_name(card, &cardname) < 0) {
593 /* FIXME: Should be localized */
594 static const WCHAR nameW[] = {'U','n','k','n','o','w','n',' ','s','o','u','n','d','c','a','r','d',0};
595 WARN("Unable to get card name for ALSA device %s: %d (%s)\n",
596 cardpath, err, snd_strerror(err));
597 alsa_get_card_devices(flow, stream, ids, guids, num, ctl, card, nameW);
598 }else{
599 if(need_card_number(card, cardname)){
600 char *cardnameN;
602 * For identical card names, second and subsequent instances get
603 * card number prefix to distinguish them (like Windows).
605 if(asprintf(&cardnameN, "%u-%s", card, cardname) > 0){
606 free(cardname);
607 cardname = cardnameN;
610 len = MultiByteToWideChar(CP_UNIXCP, 0, cardname, -1, NULL, 0);
611 cardnameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
613 if(!cardnameW){
614 free(cardname);
615 snd_ctl_close(ctl);
616 return E_OUTOFMEMORY;
618 MultiByteToWideChar(CP_UNIXCP, 0, cardname, -1, cardnameW, len);
620 alsa_get_card_devices(flow, stream, ids, guids, num, ctl, card, cardnameW);
622 HeapFree(GetProcessHeap(), 0, cardnameW);
623 free(cardname);
626 snd_ctl_close(ctl);
629 if(err != 0)
630 WARN("Got a failure during card enumeration: %d (%s)\n",
631 err, snd_strerror(err));
633 return S_OK;
636 HRESULT WINAPI AUDDRV_GetEndpointIDs(EDataFlow flow, WCHAR ***ids, GUID **guids,
637 UINT *num, UINT *def_index)
639 HRESULT hr;
641 TRACE("%d %p %p %p %p\n", flow, ids, guids, num, def_index);
643 *ids = NULL;
644 *guids = NULL;
646 hr = alsa_enum_devices(flow, ids, guids, num);
647 if(FAILED(hr)){
648 UINT i;
649 for(i = 0; i < *num; ++i)
650 HeapFree(GetProcessHeap(), 0, (*ids)[i]);
651 HeapFree(GetProcessHeap(), 0, *ids);
652 HeapFree(GetProcessHeap(), 0, *guids);
653 return E_OUTOFMEMORY;
656 TRACE("Enumerated %u devices\n", *num);
658 if(*num == 0){
659 HeapFree(GetProcessHeap(), 0, *ids);
660 *ids = NULL;
661 HeapFree(GetProcessHeap(), 0, *guids);
662 *guids = NULL;
665 *def_index = 0;
667 return S_OK;
670 /* Using the pulse PCM device from alsa-plugins 1.0.24 triggers a bug
671 * which causes audio to cease playing after a few minutes of playback.
672 * Setting handle_underrun=1 on pulse-backed ALSA devices seems to work
673 * around this issue. */
674 static snd_config_t *make_handle_underrun_config(const char *name)
676 snd_config_t *lconf, *dev_node, *hu_node, *type_node;
677 char dev_node_name[260];
678 const char *type_str;
679 int err;
681 snd_config_update();
683 if((err = snd_config_copy(&lconf, snd_config)) < 0){
684 WARN("snd_config_copy failed: %d (%s)\n", err, snd_strerror(err));
685 return NULL;
688 sprintf(dev_node_name, "pcm.%s", name);
689 err = snd_config_search(lconf, dev_node_name, &dev_node);
690 if(err == -ENOENT){
691 snd_config_delete(lconf);
692 return NULL;
694 if(err < 0){
695 snd_config_delete(lconf);
696 WARN("snd_config_search failed: %d (%s)\n", err, snd_strerror(err));
697 return NULL;
700 /* ALSA is extremely fragile. If it runs into a config setting it doesn't
701 * recognize, it tends to fail or assert. So we only want to inject
702 * handle_underrun=1 on devices that we know will recognize it. */
703 err = snd_config_search(dev_node, "type", &type_node);
704 if(err == -ENOENT){
705 snd_config_delete(lconf);
706 return NULL;
708 if(err < 0){
709 snd_config_delete(lconf);
710 WARN("snd_config_search failed: %d (%s)\n", err, snd_strerror(err));
711 return NULL;
714 if((err = snd_config_get_string(type_node, &type_str)) < 0){
715 snd_config_delete(lconf);
716 return NULL;
719 if(strcmp(type_str, "pulse") != 0){
720 snd_config_delete(lconf);
721 return NULL;
724 err = snd_config_search(dev_node, "handle_underrun", &hu_node);
725 if(err >= 0){
726 /* user already has an explicit handle_underrun setting, so don't
727 * use a local config */
728 snd_config_delete(lconf);
729 return NULL;
731 if(err != -ENOENT){
732 snd_config_delete(lconf);
733 WARN("snd_config_search failed: %d (%s)\n", err, snd_strerror(err));
734 return NULL;
737 if((err = snd_config_imake_integer(&hu_node, "handle_underrun", 1)) < 0){
738 snd_config_delete(lconf);
739 WARN("snd_config_imake_integer failed: %d (%s)\n", err,
740 snd_strerror(err));
741 return NULL;
744 if((err = snd_config_add(dev_node, hu_node)) < 0){
745 snd_config_delete(lconf);
746 WARN("snd_config_add failed: %d (%s)\n", err, snd_strerror(err));
747 return NULL;
750 return lconf;
753 static BOOL get_alsa_name_by_guid(GUID *guid, char *name, DWORD name_size, EDataFlow *flow)
755 HKEY devices_key;
756 UINT i = 0;
757 WCHAR key_name[256];
758 DWORD key_name_size;
760 if(RegOpenKeyExW(HKEY_CURRENT_USER, drv_key_devicesW, 0, KEY_READ, &devices_key) != ERROR_SUCCESS){
761 ERR("No devices found in registry?\n");
762 return FALSE;
765 while(1){
766 HKEY key;
767 DWORD size, type;
768 GUID reg_guid;
770 key_name_size = ARRAY_SIZE(key_name);
771 if(RegEnumKeyExW(devices_key, i++, key_name, &key_name_size, NULL,
772 NULL, NULL, NULL) != ERROR_SUCCESS)
773 break;
775 if(RegOpenKeyExW(devices_key, key_name, 0, KEY_READ, &key) != ERROR_SUCCESS){
776 WARN("Couldn't open key: %s\n", wine_dbgstr_w(key_name));
777 continue;
780 size = sizeof(reg_guid);
781 if(RegQueryValueExW(key, guidW, 0, &type,
782 (BYTE*)&reg_guid, &size) == ERROR_SUCCESS){
783 if(IsEqualGUID(&reg_guid, guid)){
784 RegCloseKey(key);
785 RegCloseKey(devices_key);
787 TRACE("Found matching device key: %s\n", wine_dbgstr_w(key_name));
789 if(key_name[0] == '0')
790 *flow = eRender;
791 else if(key_name[0] == '1')
792 *flow = eCapture;
793 else{
794 ERR("Unknown device type: %c\n", key_name[0]);
795 return FALSE;
798 WideCharToMultiByte(CP_UNIXCP, 0, key_name + 2, -1, name, name_size, NULL, NULL);
800 return TRUE;
804 RegCloseKey(key);
807 RegCloseKey(devices_key);
809 WARN("No matching device in registry for GUID %s\n", debugstr_guid(guid));
811 return FALSE;
814 HRESULT WINAPI AUDDRV_GetAudioEndpoint(GUID *guid, IMMDevice *dev, IAudioClient **out)
816 ACImpl *This;
817 int err;
818 snd_pcm_stream_t stream;
819 snd_config_t *lconf;
820 static BOOL handle_underrun = TRUE;
821 char alsa_name[256];
822 EDataFlow dataflow;
823 HRESULT hr;
825 TRACE("%s %p %p\n", debugstr_guid(guid), dev, out);
827 if(!get_alsa_name_by_guid(guid, alsa_name, sizeof(alsa_name), &dataflow))
828 return AUDCLNT_E_DEVICE_INVALIDATED;
830 This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(ACImpl));
831 if(!This)
832 return E_OUTOFMEMORY;
834 This->IAudioClient3_iface.lpVtbl = &AudioClient3_Vtbl;
835 This->IAudioRenderClient_iface.lpVtbl = &AudioRenderClient_Vtbl;
836 This->IAudioCaptureClient_iface.lpVtbl = &AudioCaptureClient_Vtbl;
837 This->IAudioClock_iface.lpVtbl = &AudioClock_Vtbl;
838 This->IAudioClock2_iface.lpVtbl = &AudioClock2_Vtbl;
839 This->IAudioStreamVolume_iface.lpVtbl = &AudioStreamVolume_Vtbl;
841 if(dataflow == eRender)
842 stream = SND_PCM_STREAM_PLAYBACK;
843 else if(dataflow == eCapture)
844 stream = SND_PCM_STREAM_CAPTURE;
845 else{
846 HeapFree(GetProcessHeap(), 0, This);
847 return E_UNEXPECTED;
850 hr = CoCreateFreeThreadedMarshaler((IUnknown *)&This->IAudioClient3_iface, &This->pUnkFTMarshal);
851 if (FAILED(hr)) {
852 HeapFree(GetProcessHeap(), 0, This);
853 return hr;
856 This->dataflow = dataflow;
857 if(handle_underrun && ((lconf = make_handle_underrun_config(alsa_name)))){
858 err = snd_pcm_open_lconf(&This->pcm_handle, alsa_name, stream, SND_PCM_NONBLOCK, lconf);
859 TRACE("Opening PCM device \"%s\" with handle_underrun: %d\n", alsa_name, err);
860 snd_config_delete(lconf);
861 /* Pulse <= 2010 returns EINVAL, it does not know handle_underrun. */
862 if(err == -EINVAL){
863 ERR_(winediag)("PulseAudio \"%s\" %d without handle_underrun. Audio may hang."
864 " Please upgrade to alsa_plugins >= 1.0.24\n", alsa_name, err);
865 handle_underrun = FALSE;
867 }else
868 err = -EINVAL;
869 if(err == -EINVAL){
870 err = snd_pcm_open(&This->pcm_handle, alsa_name, stream, SND_PCM_NONBLOCK);
872 if(err < 0){
873 HeapFree(GetProcessHeap(), 0, This);
874 WARN("Unable to open PCM \"%s\": %d (%s)\n", alsa_name, err, snd_strerror(err));
875 switch(err){
876 case -EBUSY:
877 return AUDCLNT_E_DEVICE_IN_USE;
878 default:
879 return AUDCLNT_E_ENDPOINT_CREATE_FAILED;
883 This->hw_params = HeapAlloc(GetProcessHeap(), 0,
884 snd_pcm_hw_params_sizeof());
885 if(!This->hw_params){
886 snd_pcm_close(This->pcm_handle);
887 HeapFree(GetProcessHeap(), 0, This);
888 return E_OUTOFMEMORY;
891 InitializeCriticalSection(&This->lock);
892 This->lock.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": ACImpl.lock");
894 This->parent = dev;
895 IMMDevice_AddRef(This->parent);
897 *out = (IAudioClient *)&This->IAudioClient3_iface;
898 IAudioClient3_AddRef(&This->IAudioClient3_iface);
900 return S_OK;
903 static HRESULT WINAPI AudioClient_QueryInterface(IAudioClient3 *iface,
904 REFIID riid, void **ppv)
906 ACImpl *This = impl_from_IAudioClient3(iface);
907 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
909 if(!ppv)
910 return E_POINTER;
911 *ppv = NULL;
912 if(IsEqualIID(riid, &IID_IUnknown) ||
913 IsEqualIID(riid, &IID_IAudioClient) ||
914 IsEqualIID(riid, &IID_IAudioClient2) ||
915 IsEqualIID(riid, &IID_IAudioClient3))
916 *ppv = iface;
917 else if(IsEqualIID(riid, &IID_IMarshal))
918 return IUnknown_QueryInterface(This->pUnkFTMarshal, riid, ppv);
920 if(*ppv){
921 IUnknown_AddRef((IUnknown*)*ppv);
922 return S_OK;
924 WARN("Unknown interface %s\n", debugstr_guid(riid));
925 return E_NOINTERFACE;
928 static ULONG WINAPI AudioClient_AddRef(IAudioClient3 *iface)
930 ACImpl *This = impl_from_IAudioClient3(iface);
931 ULONG ref;
932 ref = InterlockedIncrement(&This->ref);
933 TRACE("(%p) Refcount now %u\n", This, ref);
934 return ref;
937 static ULONG WINAPI AudioClient_Release(IAudioClient3 *iface)
939 ACImpl *This = impl_from_IAudioClient3(iface);
940 ULONG ref;
942 ref = InterlockedDecrement(&This->ref);
943 TRACE("(%p) Refcount now %u\n", This, ref);
944 if(!ref){
945 if(This->timer){
946 HANDLE event;
947 DWORD wait;
948 event = CreateEventW(NULL, TRUE, FALSE, NULL);
949 wait = !DeleteTimerQueueTimer(g_timer_q, This->timer, event);
950 wait = wait && GetLastError() == ERROR_IO_PENDING;
951 if(event && wait)
952 WaitForSingleObject(event, INFINITE);
953 CloseHandle(event);
956 IAudioClient3_Stop(iface);
957 IMMDevice_Release(This->parent);
958 IUnknown_Release(This->pUnkFTMarshal);
959 This->lock.DebugInfo->Spare[0] = 0;
960 DeleteCriticalSection(&This->lock);
961 snd_pcm_drop(This->pcm_handle);
962 snd_pcm_close(This->pcm_handle);
963 if(This->initted){
964 EnterCriticalSection(&g_sessions_lock);
965 list_remove(&This->entry);
966 LeaveCriticalSection(&g_sessions_lock);
968 HeapFree(GetProcessHeap(), 0, This->vols);
969 HeapFree(GetProcessHeap(), 0, This->local_buffer);
970 HeapFree(GetProcessHeap(), 0, This->remapping_buf);
971 HeapFree(GetProcessHeap(), 0, This->silence_buf);
972 HeapFree(GetProcessHeap(), 0, This->tmp_buffer);
973 HeapFree(GetProcessHeap(), 0, This->hw_params);
974 CoTaskMemFree(This->fmt);
975 HeapFree(GetProcessHeap(), 0, This);
977 return ref;
980 static void dump_fmt(const WAVEFORMATEX *fmt)
982 TRACE("wFormatTag: 0x%x (", fmt->wFormatTag);
983 switch(fmt->wFormatTag){
984 case WAVE_FORMAT_PCM:
985 TRACE("WAVE_FORMAT_PCM");
986 break;
987 case WAVE_FORMAT_IEEE_FLOAT:
988 TRACE("WAVE_FORMAT_IEEE_FLOAT");
989 break;
990 case WAVE_FORMAT_EXTENSIBLE:
991 TRACE("WAVE_FORMAT_EXTENSIBLE");
992 break;
993 default:
994 TRACE("Unknown");
995 break;
997 TRACE(")\n");
999 TRACE("nChannels: %u\n", fmt->nChannels);
1000 TRACE("nSamplesPerSec: %u\n", fmt->nSamplesPerSec);
1001 TRACE("nAvgBytesPerSec: %u\n", fmt->nAvgBytesPerSec);
1002 TRACE("nBlockAlign: %u\n", fmt->nBlockAlign);
1003 TRACE("wBitsPerSample: %u\n", fmt->wBitsPerSample);
1004 TRACE("cbSize: %u\n", fmt->cbSize);
1006 if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE){
1007 WAVEFORMATEXTENSIBLE *fmtex = (void*)fmt;
1008 TRACE("dwChannelMask: %08x\n", fmtex->dwChannelMask);
1009 TRACE("Samples: %04x\n", fmtex->Samples.wReserved);
1010 TRACE("SubFormat: %s\n", wine_dbgstr_guid(&fmtex->SubFormat));
1014 static WAVEFORMATEX *clone_format(const WAVEFORMATEX *fmt)
1016 WAVEFORMATEX *ret;
1017 size_t size;
1019 if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
1020 size = sizeof(WAVEFORMATEXTENSIBLE);
1021 else
1022 size = sizeof(WAVEFORMATEX);
1024 ret = CoTaskMemAlloc(size);
1025 if(!ret)
1026 return NULL;
1028 memcpy(ret, fmt, size);
1030 ret->cbSize = size - sizeof(WAVEFORMATEX);
1032 return ret;
1035 static snd_pcm_format_t alsa_format(const WAVEFORMATEX *fmt)
1037 snd_pcm_format_t format = SND_PCM_FORMAT_UNKNOWN;
1038 const WAVEFORMATEXTENSIBLE *fmtex = (const WAVEFORMATEXTENSIBLE *)fmt;
1040 if(fmt->wFormatTag == WAVE_FORMAT_PCM ||
1041 (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
1042 IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))){
1043 if(fmt->wBitsPerSample == 8)
1044 format = SND_PCM_FORMAT_U8;
1045 else if(fmt->wBitsPerSample == 16)
1046 format = SND_PCM_FORMAT_S16_LE;
1047 else if(fmt->wBitsPerSample == 24)
1048 format = SND_PCM_FORMAT_S24_3LE;
1049 else if(fmt->wBitsPerSample == 32)
1050 format = SND_PCM_FORMAT_S32_LE;
1051 else
1052 WARN("Unsupported bit depth: %u\n", fmt->wBitsPerSample);
1053 if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
1054 fmt->wBitsPerSample != fmtex->Samples.wValidBitsPerSample){
1055 if(fmtex->Samples.wValidBitsPerSample == 20 && fmt->wBitsPerSample == 24)
1056 format = SND_PCM_FORMAT_S20_3LE;
1057 else
1058 WARN("Unsupported ValidBits: %u\n", fmtex->Samples.wValidBitsPerSample);
1060 }else if(fmt->wFormatTag == WAVE_FORMAT_IEEE_FLOAT ||
1061 (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
1062 IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT))){
1063 if(fmt->wBitsPerSample == 32)
1064 format = SND_PCM_FORMAT_FLOAT_LE;
1065 else if(fmt->wBitsPerSample == 64)
1066 format = SND_PCM_FORMAT_FLOAT64_LE;
1067 else
1068 WARN("Unsupported float size: %u\n", fmt->wBitsPerSample);
1069 }else
1070 WARN("Unknown wave format: %04x\n", fmt->wFormatTag);
1071 return format;
1074 static void session_init_vols(AudioSession *session, UINT channels)
1076 if(session->channel_count < channels){
1077 UINT i;
1079 if(session->channel_vols)
1080 session->channel_vols = HeapReAlloc(GetProcessHeap(), 0,
1081 session->channel_vols, sizeof(float) * channels);
1082 else
1083 session->channel_vols = HeapAlloc(GetProcessHeap(), 0,
1084 sizeof(float) * channels);
1085 if(!session->channel_vols)
1086 return;
1088 for(i = session->channel_count; i < channels; ++i)
1089 session->channel_vols[i] = 1.f;
1091 session->channel_count = channels;
1095 static AudioSession *create_session(const GUID *guid, IMMDevice *device,
1096 UINT num_channels)
1098 AudioSession *ret;
1100 ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(AudioSession));
1101 if(!ret)
1102 return NULL;
1104 memcpy(&ret->guid, guid, sizeof(GUID));
1106 ret->device = device;
1108 list_init(&ret->clients);
1110 list_add_head(&g_sessions, &ret->entry);
1112 InitializeCriticalSection(&ret->lock);
1113 ret->lock.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": AudioSession.lock");
1115 session_init_vols(ret, num_channels);
1117 ret->master_vol = 1.f;
1119 return ret;
1122 /* if channels == 0, then this will return or create a session with
1123 * matching dataflow and GUID. otherwise, channels must also match */
1124 static HRESULT get_audio_session(const GUID *sessionguid,
1125 IMMDevice *device, UINT channels, AudioSession **out)
1127 AudioSession *session;
1129 if(!sessionguid || IsEqualGUID(sessionguid, &GUID_NULL)){
1130 *out = create_session(&GUID_NULL, device, channels);
1131 if(!*out)
1132 return E_OUTOFMEMORY;
1134 return S_OK;
1137 *out = NULL;
1138 LIST_FOR_EACH_ENTRY(session, &g_sessions, AudioSession, entry){
1139 if(session->device == device &&
1140 IsEqualGUID(sessionguid, &session->guid)){
1141 session_init_vols(session, channels);
1142 *out = session;
1143 break;
1147 if(!*out){
1148 *out = create_session(sessionguid, device, channels);
1149 if(!*out)
1150 return E_OUTOFMEMORY;
1153 return S_OK;
1156 static int alsa_channel_index(DWORD flag)
1158 switch(flag){
1159 case SPEAKER_FRONT_LEFT:
1160 return 0;
1161 case SPEAKER_FRONT_RIGHT:
1162 return 1;
1163 case SPEAKER_BACK_LEFT:
1164 return 2;
1165 case SPEAKER_BACK_RIGHT:
1166 return 3;
1167 case SPEAKER_FRONT_CENTER:
1168 return 4;
1169 case SPEAKER_LOW_FREQUENCY:
1170 return 5;
1171 case SPEAKER_SIDE_LEFT:
1172 return 6;
1173 case SPEAKER_SIDE_RIGHT:
1174 return 7;
1176 return -1;
1179 static BOOL need_remapping(ACImpl *This, const WAVEFORMATEX *fmt, int *map)
1181 unsigned int i;
1182 for(i = 0; i < fmt->nChannels; ++i){
1183 if(map[i] != i)
1184 return TRUE;
1186 return FALSE;
1189 static DWORD get_channel_mask(unsigned int channels)
1191 switch(channels){
1192 case 0:
1193 return 0;
1194 case 1:
1195 return KSAUDIO_SPEAKER_MONO;
1196 case 2:
1197 return KSAUDIO_SPEAKER_STEREO;
1198 case 3:
1199 return KSAUDIO_SPEAKER_STEREO | SPEAKER_LOW_FREQUENCY;
1200 case 4:
1201 return KSAUDIO_SPEAKER_QUAD; /* not _SURROUND */
1202 case 5:
1203 return KSAUDIO_SPEAKER_QUAD | SPEAKER_LOW_FREQUENCY;
1204 case 6:
1205 return KSAUDIO_SPEAKER_5POINT1; /* not 5POINT1_SURROUND */
1206 case 7:
1207 return KSAUDIO_SPEAKER_5POINT1 | SPEAKER_BACK_CENTER;
1208 case 8:
1209 return KSAUDIO_SPEAKER_7POINT1_SURROUND; /* Vista deprecates 7POINT1 */
1211 FIXME("Unknown speaker configuration: %u\n", channels);
1212 return 0;
1215 static HRESULT map_channels(ACImpl *This, const WAVEFORMATEX *fmt, int *alsa_channels, int *map)
1217 BOOL need_remap;
1219 if(This->dataflow != eCapture && (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE || fmt->nChannels > 2) ){
1220 WAVEFORMATEXTENSIBLE *fmtex = (void*)fmt;
1221 DWORD mask, flag = SPEAKER_FRONT_LEFT;
1222 UINT i = 0;
1224 if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
1225 fmtex->dwChannelMask != 0)
1226 mask = fmtex->dwChannelMask;
1227 else
1228 mask = get_channel_mask(fmt->nChannels);
1230 *alsa_channels = 0;
1232 while(i < fmt->nChannels && !(flag & SPEAKER_RESERVED)){
1233 if(mask & flag){
1234 map[i] = alsa_channel_index(flag);
1235 TRACE("Mapping mmdevapi channel %u (0x%x) to ALSA channel %d\n",
1236 i, flag, map[i]);
1237 if(map[i] >= *alsa_channels)
1238 *alsa_channels = map[i] + 1;
1239 ++i;
1241 flag <<= 1;
1244 while(i < fmt->nChannels){
1245 map[i] = *alsa_channels;
1246 TRACE("Mapping mmdevapi channel %u to ALSA channel %d\n",
1247 i, map[i]);
1248 ++*alsa_channels;
1249 ++i;
1252 for(i = 0; i < fmt->nChannels; ++i){
1253 if(map[i] == -1){
1254 map[i] = *alsa_channels;
1255 ++*alsa_channels;
1256 TRACE("Remapping mmdevapi channel %u to ALSA channel %d\n",
1257 i, map[i]);
1261 need_remap = need_remapping(This, fmt, map);
1262 }else{
1263 *alsa_channels = fmt->nChannels;
1265 need_remap = FALSE;
1268 TRACE("need_remapping: %u, alsa_channels: %d\n", need_remap, *alsa_channels);
1270 return need_remap ? S_OK : S_FALSE;
1273 static void silence_buffer(ACImpl *This, BYTE *buffer, UINT32 frames)
1275 WAVEFORMATEXTENSIBLE *fmtex = (WAVEFORMATEXTENSIBLE*)This->fmt;
1276 if((This->fmt->wFormatTag == WAVE_FORMAT_PCM ||
1277 (This->fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
1278 IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))) &&
1279 This->fmt->wBitsPerSample == 8)
1280 memset(buffer, 128, frames * This->fmt->nBlockAlign);
1281 else
1282 memset(buffer, 0, frames * This->fmt->nBlockAlign);
1285 static HRESULT WINAPI AudioClient_Initialize(IAudioClient3 *iface,
1286 AUDCLNT_SHAREMODE mode, DWORD flags, REFERENCE_TIME duration,
1287 REFERENCE_TIME period, const WAVEFORMATEX *fmt,
1288 const GUID *sessionguid)
1290 ACImpl *This = impl_from_IAudioClient3(iface);
1291 snd_pcm_sw_params_t *sw_params = NULL;
1292 snd_pcm_format_t format;
1293 unsigned int rate, alsa_period_us;
1294 int err, i;
1295 HRESULT hr = S_OK;
1297 TRACE("(%p)->(%x, %x, %s, %s, %p, %s)\n", This, mode, flags,
1298 wine_dbgstr_longlong(duration), wine_dbgstr_longlong(period), fmt, debugstr_guid(sessionguid));
1300 if(!fmt)
1301 return E_POINTER;
1303 if(mode != AUDCLNT_SHAREMODE_SHARED && mode != AUDCLNT_SHAREMODE_EXCLUSIVE)
1304 return E_INVALIDARG;
1306 if(flags & ~(AUDCLNT_STREAMFLAGS_CROSSPROCESS |
1307 AUDCLNT_STREAMFLAGS_LOOPBACK |
1308 AUDCLNT_STREAMFLAGS_EVENTCALLBACK |
1309 AUDCLNT_STREAMFLAGS_NOPERSIST |
1310 AUDCLNT_STREAMFLAGS_RATEADJUST |
1311 AUDCLNT_SESSIONFLAGS_EXPIREWHENUNOWNED |
1312 AUDCLNT_SESSIONFLAGS_DISPLAY_HIDE |
1313 AUDCLNT_SESSIONFLAGS_DISPLAY_HIDEWHENEXPIRED |
1314 AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY |
1315 AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM)){
1316 FIXME("Unknown flags: %08x\n", flags);
1317 return E_INVALIDARG;
1320 if(mode == AUDCLNT_SHAREMODE_SHARED){
1321 period = DefaultPeriod;
1322 if( duration < 3 * period)
1323 duration = 3 * period;
1324 }else{
1325 if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE){
1326 if(((WAVEFORMATEXTENSIBLE*)fmt)->dwChannelMask == 0 ||
1327 ((WAVEFORMATEXTENSIBLE*)fmt)->dwChannelMask & SPEAKER_RESERVED)
1328 return AUDCLNT_E_UNSUPPORTED_FORMAT;
1331 if(!period)
1332 period = DefaultPeriod; /* not minimum */
1333 if(period < MinimumPeriod || period > 5000000)
1334 return AUDCLNT_E_INVALID_DEVICE_PERIOD;
1335 if(duration > 20000000) /* the smaller the period, the lower this limit */
1336 return AUDCLNT_E_BUFFER_SIZE_ERROR;
1337 if(flags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK){
1338 if(duration != period)
1339 return AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL;
1340 FIXME("EXCLUSIVE mode with EVENTCALLBACK\n");
1341 return AUDCLNT_E_DEVICE_IN_USE;
1342 }else{
1343 if( duration < 8 * period)
1344 duration = 8 * period; /* may grow above 2s */
1348 EnterCriticalSection(&This->lock);
1350 if(This->initted){
1351 LeaveCriticalSection(&This->lock);
1352 return AUDCLNT_E_ALREADY_INITIALIZED;
1355 dump_fmt(fmt);
1357 This->need_remapping = map_channels(This, fmt, &This->alsa_channels, This->alsa_channel_map) == S_OK;
1359 if((err = snd_pcm_hw_params_any(This->pcm_handle, This->hw_params)) < 0){
1360 WARN("Unable to get hw_params: %d (%s)\n", err, snd_strerror(err));
1361 hr = AUDCLNT_E_ENDPOINT_CREATE_FAILED;
1362 goto exit;
1365 if((err = snd_pcm_hw_params_set_access(This->pcm_handle, This->hw_params,
1366 SND_PCM_ACCESS_RW_INTERLEAVED)) < 0){
1367 WARN("Unable to set access: %d (%s)\n", err, snd_strerror(err));
1368 hr = AUDCLNT_E_ENDPOINT_CREATE_FAILED;
1369 goto exit;
1372 format = alsa_format(fmt);
1373 if (format == SND_PCM_FORMAT_UNKNOWN){
1374 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
1375 goto exit;
1378 if((err = snd_pcm_hw_params_set_format(This->pcm_handle, This->hw_params,
1379 format)) < 0){
1380 WARN("Unable to set ALSA format to %u: %d (%s)\n", format, err,
1381 snd_strerror(err));
1382 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
1383 goto exit;
1386 This->alsa_format = format;
1388 rate = fmt->nSamplesPerSec;
1389 if((err = snd_pcm_hw_params_set_rate_near(This->pcm_handle, This->hw_params,
1390 &rate, NULL)) < 0){
1391 WARN("Unable to set rate to %u: %d (%s)\n", rate, err,
1392 snd_strerror(err));
1393 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
1394 goto exit;
1397 if((err = snd_pcm_hw_params_set_channels(This->pcm_handle, This->hw_params,
1398 This->alsa_channels)) < 0){
1399 WARN("Unable to set channels to %u: %d (%s)\n", fmt->nChannels, err,
1400 snd_strerror(err));
1401 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
1402 goto exit;
1405 This->mmdev_period_rt = period;
1406 alsa_period_us = This->mmdev_period_rt / 10;
1407 if((err = snd_pcm_hw_params_set_period_time_near(This->pcm_handle,
1408 This->hw_params, &alsa_period_us, NULL)) < 0)
1409 WARN("Unable to set period time near %u: %d (%s)\n", alsa_period_us,
1410 err, snd_strerror(err));
1411 /* ALSA updates the output variable alsa_period_us */
1413 This->mmdev_period_frames = MulDiv(fmt->nSamplesPerSec,
1414 This->mmdev_period_rt, 10000000);
1416 /* Buffer 4 ALSA periods if large enough, else 4 mmdevapi periods */
1417 This->alsa_bufsize_frames = This->mmdev_period_frames * 4;
1418 if(err < 0 || alsa_period_us < period / 10)
1419 err = snd_pcm_hw_params_set_buffer_size_near(This->pcm_handle,
1420 This->hw_params, &This->alsa_bufsize_frames);
1421 else{
1422 unsigned int periods = 4;
1423 err = snd_pcm_hw_params_set_periods_near(This->pcm_handle, This->hw_params, &periods, NULL);
1425 if(err < 0)
1426 WARN("Unable to set buffer size: %d (%s)\n", err, snd_strerror(err));
1428 if((err = snd_pcm_hw_params(This->pcm_handle, This->hw_params)) < 0){
1429 WARN("Unable to set hw params: %d (%s)\n", err, snd_strerror(err));
1430 hr = AUDCLNT_E_ENDPOINT_CREATE_FAILED;
1431 goto exit;
1434 if((err = snd_pcm_hw_params_get_period_size(This->hw_params,
1435 &This->alsa_period_frames, NULL)) < 0){
1436 WARN("Unable to get period size: %d (%s)\n", err, snd_strerror(err));
1437 hr = AUDCLNT_E_ENDPOINT_CREATE_FAILED;
1438 goto exit;
1441 if((err = snd_pcm_hw_params_get_buffer_size(This->hw_params,
1442 &This->alsa_bufsize_frames)) < 0){
1443 WARN("Unable to get buffer size: %d (%s)\n", err, snd_strerror(err));
1444 hr = AUDCLNT_E_ENDPOINT_CREATE_FAILED;
1445 goto exit;
1448 sw_params = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, snd_pcm_sw_params_sizeof());
1449 if(!sw_params){
1450 hr = E_OUTOFMEMORY;
1451 goto exit;
1454 if((err = snd_pcm_sw_params_current(This->pcm_handle, sw_params)) < 0){
1455 WARN("Unable to get sw_params: %d (%s)\n", err, snd_strerror(err));
1456 hr = AUDCLNT_E_ENDPOINT_CREATE_FAILED;
1457 goto exit;
1460 if((err = snd_pcm_sw_params_set_start_threshold(This->pcm_handle,
1461 sw_params, 1)) < 0){
1462 WARN("Unable set start threshold to 1: %d (%s)\n", err, snd_strerror(err));
1463 hr = AUDCLNT_E_ENDPOINT_CREATE_FAILED;
1464 goto exit;
1467 if((err = snd_pcm_sw_params_set_stop_threshold(This->pcm_handle,
1468 sw_params, This->alsa_bufsize_frames)) < 0){
1469 WARN("Unable set stop threshold to %lu: %d (%s)\n",
1470 This->alsa_bufsize_frames, err, snd_strerror(err));
1471 hr = AUDCLNT_E_ENDPOINT_CREATE_FAILED;
1472 goto exit;
1475 if((err = snd_pcm_sw_params(This->pcm_handle, sw_params)) < 0){
1476 WARN("Unable to set sw params: %d (%s)\n", err, snd_strerror(err));
1477 hr = AUDCLNT_E_ENDPOINT_CREATE_FAILED;
1478 goto exit;
1481 if((err = snd_pcm_prepare(This->pcm_handle)) < 0){
1482 WARN("Unable to prepare device: %d (%s)\n", err, snd_strerror(err));
1483 hr = AUDCLNT_E_ENDPOINT_CREATE_FAILED;
1484 goto exit;
1487 /* Bear in mind weird situations where
1488 * ALSA period (50ms) > mmdevapi buffer (3x10ms)
1489 * or surprising rounding as seen with 22050x8x1 with Pulse:
1490 * ALSA period 220 vs. 221 frames in mmdevapi and
1491 * buffer 883 vs. 2205 frames in mmdevapi! */
1492 This->bufsize_frames = MulDiv(duration, fmt->nSamplesPerSec, 10000000);
1493 if(mode == AUDCLNT_SHAREMODE_EXCLUSIVE)
1494 This->bufsize_frames -= This->bufsize_frames % This->mmdev_period_frames;
1495 This->hidden_frames = This->alsa_period_frames + This->mmdev_period_frames +
1496 MulDiv(fmt->nSamplesPerSec, EXTRA_SAFE_RT, 10000000);
1497 /* leave no less than about 1.33ms or 256 bytes of data after a rewind */
1498 This->safe_rewind_frames = max(256 / fmt->nBlockAlign, MulDiv(133, fmt->nSamplesPerSec, 100000));
1500 /* Check if the ALSA buffer is so small that it will run out before
1501 * the next MMDevAPI period tick occurs. Allow a little wiggle room
1502 * with 120% of the period time. */
1503 if(This->alsa_bufsize_frames < 1.2 * This->mmdev_period_frames)
1504 FIXME("ALSA buffer time is too small. Expect underruns. (%lu < %u * 1.2)\n",
1505 This->alsa_bufsize_frames, This->mmdev_period_frames);
1507 This->fmt = clone_format(fmt);
1508 if(!This->fmt){
1509 hr = E_OUTOFMEMORY;
1510 goto exit;
1513 This->local_buffer = HeapAlloc(GetProcessHeap(), 0,
1514 This->bufsize_frames * fmt->nBlockAlign);
1515 if(!This->local_buffer){
1516 hr = E_OUTOFMEMORY;
1517 goto exit;
1519 silence_buffer(This, This->local_buffer, This->bufsize_frames);
1521 This->silence_buf = HeapAlloc(GetProcessHeap(), 0,
1522 This->alsa_period_frames * This->fmt->nBlockAlign);
1523 if(!This->silence_buf){
1524 hr = E_OUTOFMEMORY;
1525 goto exit;
1527 silence_buffer(This, This->silence_buf, This->alsa_period_frames);
1529 This->vols = HeapAlloc(GetProcessHeap(), 0, fmt->nChannels * sizeof(float));
1530 if(!This->vols){
1531 hr = E_OUTOFMEMORY;
1532 goto exit;
1535 for(i = 0; i < fmt->nChannels; ++i)
1536 This->vols[i] = 1.f;
1538 This->share = mode;
1539 This->flags = flags;
1541 EnterCriticalSection(&g_sessions_lock);
1543 hr = get_audio_session(sessionguid, This->parent, fmt->nChannels,
1544 &This->session);
1545 if(FAILED(hr)){
1546 LeaveCriticalSection(&g_sessions_lock);
1547 goto exit;
1550 list_add_tail(&This->session->clients, &This->entry);
1552 LeaveCriticalSection(&g_sessions_lock);
1554 This->initted = TRUE;
1556 TRACE("ALSA period: %lu frames\n", This->alsa_period_frames);
1557 TRACE("ALSA buffer: %lu frames\n", This->alsa_bufsize_frames);
1558 TRACE("MMDevice period: %u frames\n", This->mmdev_period_frames);
1559 TRACE("MMDevice buffer: %u frames\n", This->bufsize_frames);
1561 exit:
1562 HeapFree(GetProcessHeap(), 0, sw_params);
1563 if(FAILED(hr)){
1564 HeapFree(GetProcessHeap(), 0, This->local_buffer);
1565 This->local_buffer = NULL;
1566 CoTaskMemFree(This->fmt);
1567 This->fmt = NULL;
1568 HeapFree(GetProcessHeap(), 0, This->vols);
1569 This->vols = NULL;
1572 LeaveCriticalSection(&This->lock);
1574 return hr;
1577 static HRESULT WINAPI AudioClient_GetBufferSize(IAudioClient3 *iface,
1578 UINT32 *out)
1580 ACImpl *This = impl_from_IAudioClient3(iface);
1582 TRACE("(%p)->(%p)\n", This, out);
1584 if(!out)
1585 return E_POINTER;
1587 EnterCriticalSection(&This->lock);
1589 if(!This->initted){
1590 LeaveCriticalSection(&This->lock);
1591 return AUDCLNT_E_NOT_INITIALIZED;
1594 *out = This->bufsize_frames;
1596 LeaveCriticalSection(&This->lock);
1598 return S_OK;
1601 static HRESULT WINAPI AudioClient_GetStreamLatency(IAudioClient3 *iface,
1602 REFERENCE_TIME *latency)
1604 ACImpl *This = impl_from_IAudioClient3(iface);
1606 TRACE("(%p)->(%p)\n", This, latency);
1608 if(!latency)
1609 return E_POINTER;
1611 EnterCriticalSection(&This->lock);
1613 if(!This->initted){
1614 LeaveCriticalSection(&This->lock);
1615 return AUDCLNT_E_NOT_INITIALIZED;
1618 /* Hide some frames in the ALSA buffer. Allows us to return GetCurrentPadding=0
1619 * yet have enough data left to play (as if it were in native's mixer). Add:
1620 * + mmdevapi_period such that at the end of it, ALSA still has data;
1621 * + EXTRA_SAFE (~4ms) to allow for late callback invocation / fluctuation;
1622 * + alsa_period such that ALSA always has at least one period to play. */
1623 if(This->dataflow == eRender)
1624 *latency = MulDiv(This->hidden_frames, 10000000, This->fmt->nSamplesPerSec);
1625 else
1626 *latency = MulDiv(This->alsa_period_frames, 10000000, This->fmt->nSamplesPerSec)
1627 + This->mmdev_period_rt;
1629 LeaveCriticalSection(&This->lock);
1631 return S_OK;
1634 static HRESULT WINAPI AudioClient_GetCurrentPadding(IAudioClient3 *iface,
1635 UINT32 *out)
1637 ACImpl *This = impl_from_IAudioClient3(iface);
1639 TRACE("(%p)->(%p)\n", This, out);
1641 if(!out)
1642 return E_POINTER;
1644 EnterCriticalSection(&This->lock);
1646 if(!This->initted){
1647 LeaveCriticalSection(&This->lock);
1648 return AUDCLNT_E_NOT_INITIALIZED;
1651 /* padding is solely updated at callback time in shared mode */
1652 *out = This->held_frames;
1654 LeaveCriticalSection(&This->lock);
1656 TRACE("pad: %u\n", *out);
1658 return S_OK;
1661 static HRESULT WINAPI AudioClient_IsFormatSupported(IAudioClient3 *iface,
1662 AUDCLNT_SHAREMODE mode, const WAVEFORMATEX *fmt,
1663 WAVEFORMATEX **out)
1665 ACImpl *This = impl_from_IAudioClient3(iface);
1666 snd_pcm_format_mask_t *formats = NULL;
1667 snd_pcm_format_t format;
1668 HRESULT hr = S_OK;
1669 WAVEFORMATEX *closest = NULL;
1670 unsigned int max = 0, min = 0;
1671 int err;
1672 int alsa_channels, alsa_channel_map[32];
1674 TRACE("(%p)->(%x, %p, %p)\n", This, mode, fmt, out);
1676 if(!fmt || (mode == AUDCLNT_SHAREMODE_SHARED && !out))
1677 return E_POINTER;
1679 if(mode != AUDCLNT_SHAREMODE_SHARED && mode != AUDCLNT_SHAREMODE_EXCLUSIVE)
1680 return E_INVALIDARG;
1682 if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
1683 fmt->cbSize < sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX))
1684 return E_INVALIDARG;
1686 dump_fmt(fmt);
1688 if(out){
1689 *out = NULL;
1690 if(mode != AUDCLNT_SHAREMODE_SHARED)
1691 out = NULL;
1694 if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
1695 (fmt->nAvgBytesPerSec == 0 ||
1696 fmt->nBlockAlign == 0 ||
1697 ((WAVEFORMATEXTENSIBLE*)fmt)->Samples.wValidBitsPerSample > fmt->wBitsPerSample))
1698 return E_INVALIDARG;
1700 if(fmt->nChannels == 0)
1701 return AUDCLNT_E_UNSUPPORTED_FORMAT;
1703 EnterCriticalSection(&This->lock);
1705 if((err = snd_pcm_hw_params_any(This->pcm_handle, This->hw_params)) < 0){
1706 hr = AUDCLNT_E_DEVICE_INVALIDATED;
1707 goto exit;
1710 formats = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
1711 snd_pcm_format_mask_sizeof());
1712 if(!formats){
1713 hr = E_OUTOFMEMORY;
1714 goto exit;
1717 snd_pcm_hw_params_get_format_mask(This->hw_params, formats);
1718 format = alsa_format(fmt);
1719 if (format == SND_PCM_FORMAT_UNKNOWN ||
1720 !snd_pcm_format_mask_test(formats, format)){
1721 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
1722 goto exit;
1725 closest = clone_format(fmt);
1726 if(!closest){
1727 hr = E_OUTOFMEMORY;
1728 goto exit;
1731 if((err = snd_pcm_hw_params_get_rate_min(This->hw_params, &min, NULL)) < 0){
1732 hr = AUDCLNT_E_DEVICE_INVALIDATED;
1733 WARN("Unable to get min rate: %d (%s)\n", err, snd_strerror(err));
1734 goto exit;
1737 if((err = snd_pcm_hw_params_get_rate_max(This->hw_params, &max, NULL)) < 0){
1738 hr = AUDCLNT_E_DEVICE_INVALIDATED;
1739 WARN("Unable to get max rate: %d (%s)\n", err, snd_strerror(err));
1740 goto exit;
1743 if(fmt->nSamplesPerSec < min || fmt->nSamplesPerSec > max){
1744 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
1745 goto exit;
1748 if((err = snd_pcm_hw_params_get_channels_min(This->hw_params, &min)) < 0){
1749 hr = AUDCLNT_E_DEVICE_INVALIDATED;
1750 WARN("Unable to get min channels: %d (%s)\n", err, snd_strerror(err));
1751 goto exit;
1754 if((err = snd_pcm_hw_params_get_channels_max(This->hw_params, &max)) < 0){
1755 hr = AUDCLNT_E_DEVICE_INVALIDATED;
1756 WARN("Unable to get max channels: %d (%s)\n", err, snd_strerror(err));
1757 goto exit;
1759 if(fmt->nChannels > max){
1760 hr = S_FALSE;
1761 closest->nChannels = max;
1762 }else if(fmt->nChannels < min){
1763 hr = S_FALSE;
1764 closest->nChannels = min;
1767 map_channels(This, fmt, &alsa_channels, alsa_channel_map);
1769 if(alsa_channels > max){
1770 hr = S_FALSE;
1771 closest->nChannels = max;
1774 if(closest->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
1775 ((WAVEFORMATEXTENSIBLE*)closest)->dwChannelMask = get_channel_mask(closest->nChannels);
1777 if(fmt->nBlockAlign != fmt->nChannels * fmt->wBitsPerSample / 8 ||
1778 fmt->nAvgBytesPerSec != fmt->nBlockAlign * fmt->nSamplesPerSec ||
1779 (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
1780 ((WAVEFORMATEXTENSIBLE*)fmt)->Samples.wValidBitsPerSample < fmt->wBitsPerSample))
1781 hr = S_FALSE;
1783 if(mode == AUDCLNT_SHAREMODE_EXCLUSIVE &&
1784 fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE){
1785 if(((WAVEFORMATEXTENSIBLE*)fmt)->dwChannelMask == 0 ||
1786 ((WAVEFORMATEXTENSIBLE*)fmt)->dwChannelMask & SPEAKER_RESERVED)
1787 hr = S_FALSE;
1790 exit:
1791 LeaveCriticalSection(&This->lock);
1792 HeapFree(GetProcessHeap(), 0, formats);
1794 if(hr == S_FALSE && !out)
1795 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
1797 if(hr == S_FALSE && out) {
1798 closest->nBlockAlign =
1799 closest->nChannels * closest->wBitsPerSample / 8;
1800 closest->nAvgBytesPerSec =
1801 closest->nBlockAlign * closest->nSamplesPerSec;
1802 if(closest->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
1803 ((WAVEFORMATEXTENSIBLE*)closest)->Samples.wValidBitsPerSample = closest->wBitsPerSample;
1804 *out = closest;
1805 } else
1806 CoTaskMemFree(closest);
1808 TRACE("returning: %08x\n", hr);
1809 return hr;
1812 static HRESULT WINAPI AudioClient_GetMixFormat(IAudioClient3 *iface,
1813 WAVEFORMATEX **pwfx)
1815 ACImpl *This = impl_from_IAudioClient3(iface);
1816 WAVEFORMATEXTENSIBLE *fmt;
1817 snd_pcm_format_mask_t *formats;
1818 unsigned int max_rate, max_channels;
1819 int err;
1820 HRESULT hr = S_OK;
1822 TRACE("(%p)->(%p)\n", This, pwfx);
1824 if(!pwfx)
1825 return E_POINTER;
1826 *pwfx = NULL;
1828 fmt = CoTaskMemAlloc(sizeof(WAVEFORMATEXTENSIBLE));
1829 if(!fmt)
1830 return E_OUTOFMEMORY;
1832 formats = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, snd_pcm_format_mask_sizeof());
1833 if(!formats){
1834 CoTaskMemFree(fmt);
1835 return E_OUTOFMEMORY;
1838 EnterCriticalSection(&This->lock);
1840 if((err = snd_pcm_hw_params_any(This->pcm_handle, This->hw_params)) < 0){
1841 WARN("Unable to get hw_params: %d (%s)\n", err, snd_strerror(err));
1842 hr = AUDCLNT_E_DEVICE_INVALIDATED;
1843 goto exit;
1846 snd_pcm_hw_params_get_format_mask(This->hw_params, formats);
1848 fmt->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
1849 if(snd_pcm_format_mask_test(formats, SND_PCM_FORMAT_FLOAT_LE)){
1850 fmt->Format.wBitsPerSample = 32;
1851 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
1852 }else if(snd_pcm_format_mask_test(formats, SND_PCM_FORMAT_S16_LE)){
1853 fmt->Format.wBitsPerSample = 16;
1854 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1855 }else if(snd_pcm_format_mask_test(formats, SND_PCM_FORMAT_U8)){
1856 fmt->Format.wBitsPerSample = 8;
1857 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1858 }else if(snd_pcm_format_mask_test(formats, SND_PCM_FORMAT_S32_LE)){
1859 fmt->Format.wBitsPerSample = 32;
1860 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1861 }else if(snd_pcm_format_mask_test(formats, SND_PCM_FORMAT_S24_3LE)){
1862 fmt->Format.wBitsPerSample = 24;
1863 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1864 }else{
1865 ERR("Didn't recognize any available ALSA formats\n");
1866 hr = AUDCLNT_E_DEVICE_INVALIDATED;
1867 goto exit;
1870 if((err = snd_pcm_hw_params_get_channels_max(This->hw_params,
1871 &max_channels)) < 0){
1872 WARN("Unable to get max channels: %d (%s)\n", err, snd_strerror(err));
1873 hr = AUDCLNT_E_DEVICE_INVALIDATED;
1874 goto exit;
1877 if(max_channels > 6)
1878 fmt->Format.nChannels = 2;
1879 else
1880 fmt->Format.nChannels = max_channels;
1882 if(fmt->Format.nChannels > 1 && (fmt->Format.nChannels & 0x1)){
1883 /* For most hardware on Windows, users must choose a configuration with an even
1884 * number of channels (stereo, quad, 5.1, 7.1). Users can then disable
1885 * channels, but those channels are still reported to applications from
1886 * GetMixFormat! Some applications behave badly if given an odd number of
1887 * channels (e.g. 2.1). */
1889 if(fmt->Format.nChannels < max_channels)
1890 fmt->Format.nChannels += 1;
1891 else
1892 /* We could "fake" more channels and downmix the emulated channels,
1893 * but at that point you really ought to tweak your ALSA setup or
1894 * just use PulseAudio. */
1895 WARN("Some Windows applications behave badly with an odd number of channels (%u)!\n", fmt->Format.nChannels);
1898 fmt->dwChannelMask = get_channel_mask(fmt->Format.nChannels);
1900 if((err = snd_pcm_hw_params_get_rate_max(This->hw_params, &max_rate,
1901 NULL)) < 0){
1902 WARN("Unable to get max rate: %d (%s)\n", err, snd_strerror(err));
1903 hr = AUDCLNT_E_DEVICE_INVALIDATED;
1904 goto exit;
1907 if(max_rate >= 48000)
1908 fmt->Format.nSamplesPerSec = 48000;
1909 else if(max_rate >= 44100)
1910 fmt->Format.nSamplesPerSec = 44100;
1911 else if(max_rate >= 22050)
1912 fmt->Format.nSamplesPerSec = 22050;
1913 else if(max_rate >= 11025)
1914 fmt->Format.nSamplesPerSec = 11025;
1915 else if(max_rate >= 8000)
1916 fmt->Format.nSamplesPerSec = 8000;
1917 else{
1918 ERR("Unknown max rate: %u\n", max_rate);
1919 hr = AUDCLNT_E_DEVICE_INVALIDATED;
1920 goto exit;
1923 fmt->Format.nBlockAlign = (fmt->Format.wBitsPerSample *
1924 fmt->Format.nChannels) / 8;
1925 fmt->Format.nAvgBytesPerSec = fmt->Format.nSamplesPerSec *
1926 fmt->Format.nBlockAlign;
1928 fmt->Samples.wValidBitsPerSample = fmt->Format.wBitsPerSample;
1929 fmt->Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
1931 dump_fmt((WAVEFORMATEX*)fmt);
1932 *pwfx = (WAVEFORMATEX*)fmt;
1934 exit:
1935 LeaveCriticalSection(&This->lock);
1936 if(FAILED(hr))
1937 CoTaskMemFree(fmt);
1938 HeapFree(GetProcessHeap(), 0, formats);
1940 return hr;
1943 static HRESULT WINAPI AudioClient_GetDevicePeriod(IAudioClient3 *iface,
1944 REFERENCE_TIME *defperiod, REFERENCE_TIME *minperiod)
1946 ACImpl *This = impl_from_IAudioClient3(iface);
1948 TRACE("(%p)->(%p, %p)\n", This, defperiod, minperiod);
1950 if(!defperiod && !minperiod)
1951 return E_POINTER;
1953 if(defperiod)
1954 *defperiod = DefaultPeriod;
1955 if(minperiod)
1956 *minperiod = DefaultPeriod;
1958 return S_OK;
1961 static BYTE *remap_channels(ACImpl *This, BYTE *buf, snd_pcm_uframes_t frames)
1963 snd_pcm_uframes_t i;
1964 UINT c;
1965 UINT bytes_per_sample = This->fmt->wBitsPerSample / 8;
1967 if(!This->need_remapping)
1968 return buf;
1970 if(!This->remapping_buf){
1971 This->remapping_buf = HeapAlloc(GetProcessHeap(), 0,
1972 bytes_per_sample * This->alsa_channels * frames);
1973 This->remapping_buf_frames = frames;
1974 }else if(This->remapping_buf_frames < frames){
1975 This->remapping_buf = HeapReAlloc(GetProcessHeap(), 0, This->remapping_buf,
1976 bytes_per_sample * This->alsa_channels * frames);
1977 This->remapping_buf_frames = frames;
1980 snd_pcm_format_set_silence(This->alsa_format, This->remapping_buf,
1981 frames * This->alsa_channels);
1983 switch(This->fmt->wBitsPerSample){
1984 case 8: {
1985 UINT8 *tgt_buf, *src_buf;
1986 tgt_buf = This->remapping_buf;
1987 src_buf = buf;
1988 for(i = 0; i < frames; ++i){
1989 for(c = 0; c < This->fmt->nChannels; ++c)
1990 tgt_buf[This->alsa_channel_map[c]] = src_buf[c];
1991 tgt_buf += This->alsa_channels;
1992 src_buf += This->fmt->nChannels;
1994 break;
1996 case 16: {
1997 UINT16 *tgt_buf, *src_buf;
1998 tgt_buf = (UINT16*)This->remapping_buf;
1999 src_buf = (UINT16*)buf;
2000 for(i = 0; i < frames; ++i){
2001 for(c = 0; c < This->fmt->nChannels; ++c)
2002 tgt_buf[This->alsa_channel_map[c]] = src_buf[c];
2003 tgt_buf += This->alsa_channels;
2004 src_buf += This->fmt->nChannels;
2007 break;
2008 case 32: {
2009 UINT32 *tgt_buf, *src_buf;
2010 tgt_buf = (UINT32*)This->remapping_buf;
2011 src_buf = (UINT32*)buf;
2012 for(i = 0; i < frames; ++i){
2013 for(c = 0; c < This->fmt->nChannels; ++c)
2014 tgt_buf[This->alsa_channel_map[c]] = src_buf[c];
2015 tgt_buf += This->alsa_channels;
2016 src_buf += This->fmt->nChannels;
2019 break;
2020 default: {
2021 BYTE *tgt_buf, *src_buf;
2022 tgt_buf = This->remapping_buf;
2023 src_buf = buf;
2024 for(i = 0; i < frames; ++i){
2025 for(c = 0; c < This->fmt->nChannels; ++c)
2026 memcpy(&tgt_buf[This->alsa_channel_map[c] * bytes_per_sample],
2027 &src_buf[c * bytes_per_sample], bytes_per_sample);
2028 tgt_buf += This->alsa_channels * bytes_per_sample;
2029 src_buf += This->fmt->nChannels * bytes_per_sample;
2032 break;
2035 return This->remapping_buf;
2038 static void adjust_buffer_volume(const ACImpl *This, BYTE *buf, snd_pcm_uframes_t frames, BOOL mute)
2040 float vol[ARRAY_SIZE(This->alsa_channel_map)];
2041 BOOL adjust = FALSE;
2042 UINT32 i, channels;
2043 BYTE *end;
2045 if (This->vol_adjusted_frames >= frames)
2046 return;
2047 channels = This->fmt->nChannels;
2049 if (mute)
2051 int err = snd_pcm_format_set_silence(This->alsa_format, buf, frames * channels);
2052 if (err < 0)
2053 WARN("Setting buffer to silence failed: %d (%s)\n", err, snd_strerror(err));
2054 return;
2057 /* Adjust the buffer based on the volume for each channel */
2058 for (i = 0; i < channels; i++)
2059 vol[i] = This->vols[i] * This->session->master_vol;
2060 for (i = 0; i < min(channels, This->session->channel_count); i++)
2062 vol[i] *= This->session->channel_vols[i];
2063 adjust |= vol[i] != 1.0f;
2065 while (i < channels) adjust |= vol[i++] != 1.0f;
2066 if (!adjust) return;
2068 /* Skip the frames we've already adjusted before */
2069 end = buf + frames * This->fmt->nBlockAlign;
2070 buf += This->vol_adjusted_frames * This->fmt->nBlockAlign;
2072 switch (This->alsa_format)
2074 #ifndef WORDS_BIGENDIAN
2075 #define PROCESS_BUFFER(type) do \
2077 type *p = (type*)buf; \
2078 do \
2080 for (i = 0; i < channels; i++) \
2081 p[i] = p[i] * vol[i]; \
2082 p += i; \
2083 } while ((BYTE*)p != end); \
2084 } while (0)
2085 case SND_PCM_FORMAT_S16_LE:
2086 PROCESS_BUFFER(INT16);
2087 break;
2088 case SND_PCM_FORMAT_S32_LE:
2089 PROCESS_BUFFER(INT32);
2090 break;
2091 case SND_PCM_FORMAT_FLOAT_LE:
2092 PROCESS_BUFFER(float);
2093 break;
2094 case SND_PCM_FORMAT_FLOAT64_LE:
2095 PROCESS_BUFFER(double);
2096 break;
2097 #undef PROCESS_BUFFER
2098 case SND_PCM_FORMAT_S20_3LE:
2099 case SND_PCM_FORMAT_S24_3LE:
2101 /* Do it 12 bytes at a time until it is no longer possible */
2102 UINT32 *q = (UINT32*)buf, mask = ~0xff;
2103 BYTE *p;
2105 /* After we adjust the volume, we need to mask out low bits */
2106 if (This->alsa_format == SND_PCM_FORMAT_S20_3LE)
2107 mask = ~0x0fff;
2109 i = 0;
2110 while (end - (BYTE*)q >= 12)
2112 UINT32 v[4], k;
2113 v[0] = q[0] << 8;
2114 v[1] = q[1] << 16 | (q[0] >> 16 & ~0xff);
2115 v[2] = q[2] << 24 | (q[1] >> 8 & ~0xff);
2116 v[3] = q[2] & ~0xff;
2117 for (k = 0; k < 4; k++)
2119 v[k] = (INT32)((INT32)v[k] * vol[i]);
2120 v[k] &= mask;
2121 if (++i == channels) i = 0;
2123 *q++ = v[0] >> 8 | v[1] << 16;
2124 *q++ = v[1] >> 16 | v[2] << 8;
2125 *q++ = v[2] >> 24 | v[3];
2127 p = (BYTE*)q;
2128 while (p != end)
2130 UINT32 v = (INT32)((INT32)(p[0] << 8 | p[1] << 16 | p[2] << 24) * vol[i]);
2131 v &= mask;
2132 *p++ = v >> 8 & 0xff;
2133 *p++ = v >> 16 & 0xff;
2134 *p++ = v >> 24;
2135 if (++i == channels) i = 0;
2137 break;
2139 #endif
2140 case SND_PCM_FORMAT_U8:
2142 UINT8 *p = (UINT8*)buf;
2145 for (i = 0; i < channels; i++)
2146 p[i] = (int)((p[i] - 128) * vol[i]) + 128;
2147 p += i;
2148 } while ((BYTE*)p != end);
2149 break;
2151 default:
2152 TRACE("Unhandled format %i, not adjusting volume.\n", This->alsa_format);
2153 break;
2157 static snd_pcm_sframes_t alsa_write_best_effort(ACImpl *This, BYTE *buf,
2158 snd_pcm_uframes_t frames, BOOL mute)
2160 snd_pcm_sframes_t written;
2162 adjust_buffer_volume(This, buf, frames, mute);
2164 /* Mark the frames we've already adjusted */
2165 if (This->vol_adjusted_frames < frames)
2166 This->vol_adjusted_frames = frames;
2168 buf = remap_channels(This, buf, frames);
2170 written = snd_pcm_writei(This->pcm_handle, buf, frames);
2171 if(written < 0){
2172 int ret;
2174 if(written == -EAGAIN)
2175 /* buffer full */
2176 return 0;
2178 WARN("writei failed, recovering: %ld (%s)\n", written,
2179 snd_strerror(written));
2181 ret = snd_pcm_recover(This->pcm_handle, written, 0);
2182 if(ret < 0){
2183 WARN("Could not recover: %d (%s)\n", ret, snd_strerror(ret));
2184 return ret;
2187 written = snd_pcm_writei(This->pcm_handle, buf, frames);
2190 if (written > 0)
2191 This->vol_adjusted_frames -= written;
2192 return written;
2195 static snd_pcm_sframes_t alsa_write_buffer_wrap(ACImpl *This, BYTE *buf,
2196 snd_pcm_uframes_t buflen, snd_pcm_uframes_t offs,
2197 snd_pcm_uframes_t to_write)
2199 snd_pcm_sframes_t ret = 0;
2201 while(to_write){
2202 snd_pcm_uframes_t chunk;
2203 snd_pcm_sframes_t tmp;
2205 if(offs + to_write > buflen)
2206 chunk = buflen - offs;
2207 else
2208 chunk = to_write;
2210 tmp = alsa_write_best_effort(This, buf + offs * This->fmt->nBlockAlign, chunk, This->session->mute);
2211 if(tmp < 0)
2212 return ret;
2213 if(!tmp)
2214 break;
2216 ret += tmp;
2217 to_write -= tmp;
2218 offs += tmp;
2219 offs %= buflen;
2222 return ret;
2225 static UINT buf_ptr_diff(UINT left, UINT right, UINT bufsize)
2227 if(left <= right)
2228 return right - left;
2229 return bufsize - (left - right);
2232 static UINT data_not_in_alsa(ACImpl *This)
2234 UINT32 diff;
2236 diff = buf_ptr_diff(This->lcl_offs_frames, This->wri_offs_frames, This->bufsize_frames);
2237 if(diff)
2238 return diff;
2240 return This->held_frames - This->data_in_alsa_frames;
2242 /* Here's the buffer setup:
2244 * vvvvvvvv sent to HW already
2245 * vvvvvvvv in ALSA buffer but rewindable
2246 * [dddddddddddddddd] ALSA buffer
2247 * [dddddddddddddddd--------] mmdevapi buffer
2248 * ^^^^^^^^ data_in_alsa_frames
2249 * ^^^^^^^^^^^^^^^^ held_frames
2250 * ^ lcl_offs_frames
2251 * ^ wri_offs_frames
2253 * GetCurrentPadding is held_frames
2255 * During period callback, we decrement held_frames, fill ALSA buffer, and move
2256 * lcl_offs forward
2258 * During Stop, we rewind the ALSA buffer
2260 static void alsa_write_data(ACImpl *This)
2262 snd_pcm_sframes_t written;
2263 snd_pcm_uframes_t avail, max_copy_frames, data_frames_played;
2264 int err;
2266 /* this call seems to be required to get an accurate snd_pcm_state() */
2267 avail = snd_pcm_avail_update(This->pcm_handle);
2269 if(snd_pcm_state(This->pcm_handle) == SND_PCM_STATE_XRUN){
2270 TRACE("XRun state, recovering\n");
2272 avail = This->alsa_bufsize_frames;
2274 if((err = snd_pcm_recover(This->pcm_handle, -EPIPE, 1)) < 0)
2275 WARN("snd_pcm_recover failed: %d (%s)\n", err, snd_strerror(err));
2277 if((err = snd_pcm_reset(This->pcm_handle)) < 0)
2278 WARN("snd_pcm_reset failed: %d (%s)\n", err, snd_strerror(err));
2280 if((err = snd_pcm_prepare(This->pcm_handle)) < 0)
2281 WARN("snd_pcm_prepare failed: %d (%s)\n", err, snd_strerror(err));
2284 TRACE("avail: %ld\n", avail);
2286 /* Add a lead-in when starting with too few frames to ensure
2287 * continuous rendering. Additional benefit: Force ALSA to start. */
2288 if(This->data_in_alsa_frames == 0 && This->held_frames < This->alsa_period_frames)
2290 alsa_write_best_effort(This, This->silence_buf, This->alsa_period_frames - This->held_frames, FALSE);
2291 This->vol_adjusted_frames = 0;
2294 if(This->started)
2295 max_copy_frames = data_not_in_alsa(This);
2296 else
2297 max_copy_frames = 0;
2299 data_frames_played = min(This->data_in_alsa_frames, avail);
2300 This->data_in_alsa_frames -= data_frames_played;
2302 if(This->held_frames > data_frames_played){
2303 if(This->started)
2304 This->held_frames -= data_frames_played;
2305 }else
2306 This->held_frames = 0;
2308 while(avail && max_copy_frames){
2309 snd_pcm_uframes_t to_write;
2311 to_write = min(avail, max_copy_frames);
2313 written = alsa_write_buffer_wrap(This, This->local_buffer,
2314 This->bufsize_frames, This->lcl_offs_frames, to_write);
2315 if(written <= 0)
2316 break;
2318 avail -= written;
2319 This->lcl_offs_frames += written;
2320 This->lcl_offs_frames %= This->bufsize_frames;
2321 This->data_in_alsa_frames += written;
2322 max_copy_frames -= written;
2325 if(This->event)
2326 SetEvent(This->event);
2329 static void alsa_read_data(ACImpl *This)
2331 snd_pcm_sframes_t nread;
2332 UINT32 pos = This->wri_offs_frames, limit = This->held_frames;
2334 if(!This->started)
2335 goto exit;
2337 /* FIXME: Detect overrun and signal DATA_DISCONTINUITY
2338 * How to count overrun frames and report them as position increase? */
2339 limit = This->bufsize_frames - max(limit, pos);
2341 nread = snd_pcm_readi(This->pcm_handle,
2342 This->local_buffer + pos * This->fmt->nBlockAlign, limit);
2343 TRACE("read %ld from %u limit %u\n", nread, pos, limit);
2344 if(nread < 0){
2345 int ret;
2347 if(nread == -EAGAIN) /* no data yet */
2348 return;
2350 WARN("read failed, recovering: %ld (%s)\n", nread, snd_strerror(nread));
2352 ret = snd_pcm_recover(This->pcm_handle, nread, 0);
2353 if(ret < 0){
2354 WARN("Recover failed: %d (%s)\n", ret, snd_strerror(ret));
2355 return;
2358 nread = snd_pcm_readi(This->pcm_handle,
2359 This->local_buffer + pos * This->fmt->nBlockAlign, limit);
2360 if(nread < 0){
2361 WARN("read failed: %ld (%s)\n", nread, snd_strerror(nread));
2362 return;
2366 if(This->session->mute){
2367 int err;
2368 if((err = snd_pcm_format_set_silence(This->alsa_format,
2369 This->local_buffer + pos * This->fmt->nBlockAlign,
2370 nread)) < 0)
2371 WARN("Setting buffer to silence failed: %d (%s)\n", err,
2372 snd_strerror(err));
2375 This->wri_offs_frames += nread;
2376 This->wri_offs_frames %= This->bufsize_frames;
2377 This->held_frames += nread;
2379 exit:
2380 if(This->event)
2381 SetEvent(This->event);
2384 static void CALLBACK alsa_push_buffer_data(void *user, BOOLEAN timer)
2386 ACImpl *This = user;
2388 EnterCriticalSection(&This->lock);
2390 QueryPerformanceCounter(&This->last_period_time);
2392 if(This->dataflow == eRender)
2393 alsa_write_data(This);
2394 else if(This->dataflow == eCapture)
2395 alsa_read_data(This);
2397 LeaveCriticalSection(&This->lock);
2400 static snd_pcm_uframes_t interp_elapsed_frames(ACImpl *This)
2402 LARGE_INTEGER time_freq, current_time, time_diff;
2403 QueryPerformanceFrequency(&time_freq);
2404 QueryPerformanceCounter(&current_time);
2405 time_diff.QuadPart = current_time.QuadPart - This->last_period_time.QuadPart;
2406 return MulDiv(time_diff.QuadPart, This->fmt->nSamplesPerSec, time_freq.QuadPart);
2409 static int alsa_rewind_best_effort(ACImpl *This)
2411 snd_pcm_uframes_t len, leave;
2413 /* we can't use snd_pcm_rewindable, some PCM devices crash. so follow
2414 * PulseAudio's example and rewind as much data as we believe is in the
2415 * buffer, minus 1.33ms for safety. */
2417 /* amount of data to leave in ALSA buffer */
2418 leave = interp_elapsed_frames(This) + This->safe_rewind_frames;
2420 if(This->held_frames < leave)
2421 This->held_frames = 0;
2422 else
2423 This->held_frames -= leave;
2425 if(This->data_in_alsa_frames < leave)
2426 len = 0;
2427 else
2428 len = This->data_in_alsa_frames - leave;
2430 TRACE("rewinding %lu frames, now held %u\n", len, This->held_frames);
2432 if(len)
2433 /* snd_pcm_rewind return value is often broken, assume it succeeded */
2434 snd_pcm_rewind(This->pcm_handle, len);
2436 This->data_in_alsa_frames = 0;
2438 return len;
2441 static HRESULT WINAPI AudioClient_Start(IAudioClient3 *iface)
2443 ACImpl *This = impl_from_IAudioClient3(iface);
2445 TRACE("(%p)\n", This);
2447 EnterCriticalSection(&This->lock);
2449 if(!This->initted){
2450 LeaveCriticalSection(&This->lock);
2451 return AUDCLNT_E_NOT_INITIALIZED;
2454 if((This->flags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK) && !This->event){
2455 LeaveCriticalSection(&This->lock);
2456 return AUDCLNT_E_EVENTHANDLE_NOT_SET;
2459 if(This->started){
2460 LeaveCriticalSection(&This->lock);
2461 return AUDCLNT_E_NOT_STOPPED;
2464 if(This->dataflow == eCapture){
2465 /* dump any data that might be leftover in the ALSA capture buffer */
2466 snd_pcm_readi(This->pcm_handle, This->local_buffer,
2467 This->bufsize_frames);
2468 }else{
2469 snd_pcm_sframes_t avail, written;
2470 snd_pcm_uframes_t offs;
2472 avail = snd_pcm_avail_update(This->pcm_handle);
2473 avail = min(avail, This->held_frames);
2475 if(This->wri_offs_frames < This->held_frames)
2476 offs = This->bufsize_frames - This->held_frames + This->wri_offs_frames;
2477 else
2478 offs = This->wri_offs_frames - This->held_frames;
2480 /* fill it with data */
2481 written = alsa_write_buffer_wrap(This, This->local_buffer,
2482 This->bufsize_frames, offs, avail);
2484 if(written > 0){
2485 This->lcl_offs_frames = (offs + written) % This->bufsize_frames;
2486 This->data_in_alsa_frames = written;
2487 }else{
2488 This->lcl_offs_frames = offs;
2489 This->data_in_alsa_frames = 0;
2493 if(!This->timer){
2494 if(!CreateTimerQueueTimer(&This->timer, g_timer_q, alsa_push_buffer_data,
2495 This, 0, This->mmdev_period_rt / 10000, WT_EXECUTEINTIMERTHREAD)){
2496 LeaveCriticalSection(&This->lock);
2497 WARN("Unable to create timer: %u\n", GetLastError());
2498 return E_OUTOFMEMORY;
2502 This->started = TRUE;
2504 LeaveCriticalSection(&This->lock);
2506 return S_OK;
2509 static HRESULT WINAPI AudioClient_Stop(IAudioClient3 *iface)
2511 ACImpl *This = impl_from_IAudioClient3(iface);
2513 TRACE("(%p)\n", This);
2515 EnterCriticalSection(&This->lock);
2517 if(!This->initted){
2518 LeaveCriticalSection(&This->lock);
2519 return AUDCLNT_E_NOT_INITIALIZED;
2522 if(!This->started){
2523 LeaveCriticalSection(&This->lock);
2524 return S_FALSE;
2527 if(This->dataflow == eRender)
2528 alsa_rewind_best_effort(This);
2530 This->started = FALSE;
2532 LeaveCriticalSection(&This->lock);
2534 return S_OK;
2537 static HRESULT WINAPI AudioClient_Reset(IAudioClient3 *iface)
2539 ACImpl *This = impl_from_IAudioClient3(iface);
2541 TRACE("(%p)\n", This);
2543 EnterCriticalSection(&This->lock);
2545 if(!This->initted){
2546 LeaveCriticalSection(&This->lock);
2547 return AUDCLNT_E_NOT_INITIALIZED;
2550 if(This->started){
2551 LeaveCriticalSection(&This->lock);
2552 return AUDCLNT_E_NOT_STOPPED;
2555 if(This->getbuf_last){
2556 LeaveCriticalSection(&This->lock);
2557 return AUDCLNT_E_BUFFER_OPERATION_PENDING;
2560 if(snd_pcm_drop(This->pcm_handle) < 0)
2561 WARN("snd_pcm_drop failed\n");
2563 if(snd_pcm_reset(This->pcm_handle) < 0)
2564 WARN("snd_pcm_reset failed\n");
2566 if(snd_pcm_prepare(This->pcm_handle) < 0)
2567 WARN("snd_pcm_prepare failed\n");
2569 if(This->dataflow == eRender){
2570 This->written_frames = 0;
2571 This->last_pos_frames = 0;
2572 }else{
2573 This->written_frames += This->held_frames;
2575 This->held_frames = 0;
2576 This->lcl_offs_frames = 0;
2577 This->wri_offs_frames = 0;
2579 LeaveCriticalSection(&This->lock);
2581 return S_OK;
2584 static HRESULT WINAPI AudioClient_SetEventHandle(IAudioClient3 *iface,
2585 HANDLE event)
2587 ACImpl *This = impl_from_IAudioClient3(iface);
2589 TRACE("(%p)->(%p)\n", This, event);
2591 if(!event)
2592 return E_INVALIDARG;
2594 EnterCriticalSection(&This->lock);
2596 if(!This->initted){
2597 LeaveCriticalSection(&This->lock);
2598 return AUDCLNT_E_NOT_INITIALIZED;
2601 if(!(This->flags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK)){
2602 LeaveCriticalSection(&This->lock);
2603 return AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED;
2606 if (This->event){
2607 LeaveCriticalSection(&This->lock);
2608 FIXME("called twice\n");
2609 return HRESULT_FROM_WIN32(ERROR_INVALID_NAME);
2612 This->event = event;
2614 LeaveCriticalSection(&This->lock);
2616 return S_OK;
2619 static HRESULT WINAPI AudioClient_GetService(IAudioClient3 *iface, REFIID riid,
2620 void **ppv)
2622 ACImpl *This = impl_from_IAudioClient3(iface);
2624 TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(riid), ppv);
2626 if(!ppv)
2627 return E_POINTER;
2628 *ppv = NULL;
2630 EnterCriticalSection(&This->lock);
2632 if(!This->initted){
2633 LeaveCriticalSection(&This->lock);
2634 return AUDCLNT_E_NOT_INITIALIZED;
2637 if(IsEqualIID(riid, &IID_IAudioRenderClient)){
2638 if(This->dataflow != eRender){
2639 LeaveCriticalSection(&This->lock);
2640 return AUDCLNT_E_WRONG_ENDPOINT_TYPE;
2642 IAudioRenderClient_AddRef(&This->IAudioRenderClient_iface);
2643 *ppv = &This->IAudioRenderClient_iface;
2644 }else if(IsEqualIID(riid, &IID_IAudioCaptureClient)){
2645 if(This->dataflow != eCapture){
2646 LeaveCriticalSection(&This->lock);
2647 return AUDCLNT_E_WRONG_ENDPOINT_TYPE;
2649 IAudioCaptureClient_AddRef(&This->IAudioCaptureClient_iface);
2650 *ppv = &This->IAudioCaptureClient_iface;
2651 }else if(IsEqualIID(riid, &IID_IAudioClock)){
2652 IAudioClock_AddRef(&This->IAudioClock_iface);
2653 *ppv = &This->IAudioClock_iface;
2654 }else if(IsEqualIID(riid, &IID_IAudioStreamVolume)){
2655 IAudioStreamVolume_AddRef(&This->IAudioStreamVolume_iface);
2656 *ppv = &This->IAudioStreamVolume_iface;
2657 }else if(IsEqualIID(riid, &IID_IAudioSessionControl)){
2658 if(!This->session_wrapper){
2659 This->session_wrapper = AudioSessionWrapper_Create(This);
2660 if(!This->session_wrapper){
2661 LeaveCriticalSection(&This->lock);
2662 return E_OUTOFMEMORY;
2664 }else
2665 IAudioSessionControl2_AddRef(&This->session_wrapper->IAudioSessionControl2_iface);
2667 *ppv = &This->session_wrapper->IAudioSessionControl2_iface;
2668 }else if(IsEqualIID(riid, &IID_IChannelAudioVolume)){
2669 if(!This->session_wrapper){
2670 This->session_wrapper = AudioSessionWrapper_Create(This);
2671 if(!This->session_wrapper){
2672 LeaveCriticalSection(&This->lock);
2673 return E_OUTOFMEMORY;
2675 }else
2676 IChannelAudioVolume_AddRef(&This->session_wrapper->IChannelAudioVolume_iface);
2678 *ppv = &This->session_wrapper->IChannelAudioVolume_iface;
2679 }else if(IsEqualIID(riid, &IID_ISimpleAudioVolume)){
2680 if(!This->session_wrapper){
2681 This->session_wrapper = AudioSessionWrapper_Create(This);
2682 if(!This->session_wrapper){
2683 LeaveCriticalSection(&This->lock);
2684 return E_OUTOFMEMORY;
2686 }else
2687 ISimpleAudioVolume_AddRef(&This->session_wrapper->ISimpleAudioVolume_iface);
2689 *ppv = &This->session_wrapper->ISimpleAudioVolume_iface;
2692 if(*ppv){
2693 LeaveCriticalSection(&This->lock);
2694 return S_OK;
2697 LeaveCriticalSection(&This->lock);
2699 FIXME("stub %s\n", debugstr_guid(riid));
2700 return E_NOINTERFACE;
2703 static HRESULT WINAPI AudioClient_IsOffloadCapable(IAudioClient3 *iface,
2704 AUDIO_STREAM_CATEGORY category, BOOL *offload_capable)
2706 ACImpl *This = impl_from_IAudioClient3(iface);
2708 TRACE("(%p)->(0x%x, %p)\n", This, category, offload_capable);
2710 if(!offload_capable)
2711 return E_INVALIDARG;
2713 *offload_capable = FALSE;
2715 return S_OK;
2718 static HRESULT WINAPI AudioClient_SetClientProperties(IAudioClient3 *iface,
2719 const AudioClientProperties *prop)
2721 ACImpl *This = impl_from_IAudioClient3(iface);
2722 const Win8AudioClientProperties *legacy_prop = (const Win8AudioClientProperties *)prop;
2724 TRACE("(%p)->(%p)\n", This, prop);
2726 if(!legacy_prop)
2727 return E_POINTER;
2729 if(legacy_prop->cbSize == sizeof(AudioClientProperties)){
2730 TRACE("{ bIsOffload: %u, eCategory: 0x%x, Options: 0x%x }\n",
2731 legacy_prop->bIsOffload,
2732 legacy_prop->eCategory,
2733 prop->Options);
2734 }else if(legacy_prop->cbSize == sizeof(Win8AudioClientProperties)){
2735 TRACE("{ bIsOffload: %u, eCategory: 0x%x }\n",
2736 legacy_prop->bIsOffload,
2737 legacy_prop->eCategory);
2738 }else{
2739 WARN("Unsupported Size = %d\n", legacy_prop->cbSize);
2740 return E_INVALIDARG;
2744 if(legacy_prop->bIsOffload)
2745 return AUDCLNT_E_ENDPOINT_OFFLOAD_NOT_CAPABLE;
2747 return S_OK;
2750 static HRESULT WINAPI AudioClient_GetBufferSizeLimits(IAudioClient3 *iface,
2751 const WAVEFORMATEX *format, BOOL event_driven, REFERENCE_TIME *min_duration,
2752 REFERENCE_TIME *max_duration)
2754 ACImpl *This = impl_from_IAudioClient3(iface);
2756 FIXME("(%p)->(%p, %u, %p, %p)\n", This, format, event_driven, min_duration, max_duration);
2758 return E_NOTIMPL;
2761 static HRESULT WINAPI AudioClient_GetSharedModeEnginePeriod(IAudioClient3 *iface,
2762 const WAVEFORMATEX *format, UINT32 *default_period_frames, UINT32 *unit_period_frames,
2763 UINT32 *min_period_frames, UINT32 *max_period_frames)
2765 ACImpl *This = impl_from_IAudioClient3(iface);
2767 FIXME("(%p)->(%p, %p, %p, %p, %p)\n", This, format, default_period_frames, unit_period_frames,
2768 min_period_frames, max_period_frames);
2770 return E_NOTIMPL;
2773 static HRESULT WINAPI AudioClient_GetCurrentSharedModeEnginePeriod(IAudioClient3 *iface,
2774 WAVEFORMATEX **cur_format, UINT32 *cur_period_frames)
2776 ACImpl *This = impl_from_IAudioClient3(iface);
2778 FIXME("(%p)->(%p, %p)\n", This, cur_format, cur_period_frames);
2780 return E_NOTIMPL;
2783 static HRESULT WINAPI AudioClient_InitializeSharedAudioStream(IAudioClient3 *iface,
2784 DWORD flags, UINT32 period_frames, const WAVEFORMATEX *format,
2785 const GUID *session_guid)
2787 ACImpl *This = impl_from_IAudioClient3(iface);
2789 FIXME("(%p)->(0x%x, %u, %p, %s)\n", This, flags, period_frames, format, debugstr_guid(session_guid));
2791 return E_NOTIMPL;
2794 static const IAudioClient3Vtbl AudioClient3_Vtbl =
2796 AudioClient_QueryInterface,
2797 AudioClient_AddRef,
2798 AudioClient_Release,
2799 AudioClient_Initialize,
2800 AudioClient_GetBufferSize,
2801 AudioClient_GetStreamLatency,
2802 AudioClient_GetCurrentPadding,
2803 AudioClient_IsFormatSupported,
2804 AudioClient_GetMixFormat,
2805 AudioClient_GetDevicePeriod,
2806 AudioClient_Start,
2807 AudioClient_Stop,
2808 AudioClient_Reset,
2809 AudioClient_SetEventHandle,
2810 AudioClient_GetService,
2811 AudioClient_IsOffloadCapable,
2812 AudioClient_SetClientProperties,
2813 AudioClient_GetBufferSizeLimits,
2814 AudioClient_GetSharedModeEnginePeriod,
2815 AudioClient_GetCurrentSharedModeEnginePeriod,
2816 AudioClient_InitializeSharedAudioStream,
2819 static HRESULT WINAPI AudioRenderClient_QueryInterface(
2820 IAudioRenderClient *iface, REFIID riid, void **ppv)
2822 ACImpl *This = impl_from_IAudioRenderClient(iface);
2823 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2825 if(!ppv)
2826 return E_POINTER;
2827 *ppv = NULL;
2829 if(IsEqualIID(riid, &IID_IUnknown) ||
2830 IsEqualIID(riid, &IID_IAudioRenderClient))
2831 *ppv = iface;
2832 else if(IsEqualIID(riid, &IID_IMarshal))
2833 return IUnknown_QueryInterface(This->pUnkFTMarshal, riid, ppv);
2835 if(*ppv){
2836 IUnknown_AddRef((IUnknown*)*ppv);
2837 return S_OK;
2840 WARN("Unknown interface %s\n", debugstr_guid(riid));
2841 return E_NOINTERFACE;
2844 static ULONG WINAPI AudioRenderClient_AddRef(IAudioRenderClient *iface)
2846 ACImpl *This = impl_from_IAudioRenderClient(iface);
2847 return AudioClient_AddRef(&This->IAudioClient3_iface);
2850 static ULONG WINAPI AudioRenderClient_Release(IAudioRenderClient *iface)
2852 ACImpl *This = impl_from_IAudioRenderClient(iface);
2853 return AudioClient_Release(&This->IAudioClient3_iface);
2856 static HRESULT WINAPI AudioRenderClient_GetBuffer(IAudioRenderClient *iface,
2857 UINT32 frames, BYTE **data)
2859 ACImpl *This = impl_from_IAudioRenderClient(iface);
2860 UINT32 write_pos;
2862 TRACE("(%p)->(%u, %p)\n", This, frames, data);
2864 if(!data)
2865 return E_POINTER;
2866 *data = NULL;
2868 EnterCriticalSection(&This->lock);
2870 if(This->getbuf_last){
2871 LeaveCriticalSection(&This->lock);
2872 return AUDCLNT_E_OUT_OF_ORDER;
2875 if(!frames){
2876 LeaveCriticalSection(&This->lock);
2877 return S_OK;
2880 /* held_frames == GetCurrentPadding_nolock(); */
2881 if(This->held_frames + frames > This->bufsize_frames){
2882 LeaveCriticalSection(&This->lock);
2883 return AUDCLNT_E_BUFFER_TOO_LARGE;
2886 write_pos = This->wri_offs_frames;
2887 if(write_pos + frames > This->bufsize_frames){
2888 if(This->tmp_buffer_frames < frames){
2889 HeapFree(GetProcessHeap(), 0, This->tmp_buffer);
2890 This->tmp_buffer = HeapAlloc(GetProcessHeap(), 0,
2891 frames * This->fmt->nBlockAlign);
2892 if(!This->tmp_buffer){
2893 LeaveCriticalSection(&This->lock);
2894 return E_OUTOFMEMORY;
2896 This->tmp_buffer_frames = frames;
2898 *data = This->tmp_buffer;
2899 This->getbuf_last = -frames;
2900 }else{
2901 *data = This->local_buffer + write_pos * This->fmt->nBlockAlign;
2902 This->getbuf_last = frames;
2905 silence_buffer(This, *data, frames);
2907 LeaveCriticalSection(&This->lock);
2909 return S_OK;
2912 static void alsa_wrap_buffer(ACImpl *This, BYTE *buffer, UINT32 written_frames)
2914 snd_pcm_uframes_t write_offs_frames = This->wri_offs_frames;
2915 UINT32 write_offs_bytes = write_offs_frames * This->fmt->nBlockAlign;
2916 snd_pcm_uframes_t chunk_frames = This->bufsize_frames - write_offs_frames;
2917 UINT32 chunk_bytes = chunk_frames * This->fmt->nBlockAlign;
2918 UINT32 written_bytes = written_frames * This->fmt->nBlockAlign;
2920 if(written_bytes <= chunk_bytes){
2921 memcpy(This->local_buffer + write_offs_bytes, buffer, written_bytes);
2922 }else{
2923 memcpy(This->local_buffer + write_offs_bytes, buffer, chunk_bytes);
2924 memcpy(This->local_buffer, buffer + chunk_bytes,
2925 written_bytes - chunk_bytes);
2929 static HRESULT WINAPI AudioRenderClient_ReleaseBuffer(
2930 IAudioRenderClient *iface, UINT32 written_frames, DWORD flags)
2932 ACImpl *This = impl_from_IAudioRenderClient(iface);
2933 BYTE *buffer;
2935 TRACE("(%p)->(%u, %x)\n", This, written_frames, flags);
2937 EnterCriticalSection(&This->lock);
2939 if(!written_frames){
2940 This->getbuf_last = 0;
2941 LeaveCriticalSection(&This->lock);
2942 return S_OK;
2945 if(!This->getbuf_last){
2946 LeaveCriticalSection(&This->lock);
2947 return AUDCLNT_E_OUT_OF_ORDER;
2950 if(written_frames > (This->getbuf_last >= 0 ? This->getbuf_last : -This->getbuf_last)){
2951 LeaveCriticalSection(&This->lock);
2952 return AUDCLNT_E_INVALID_SIZE;
2955 if(This->getbuf_last >= 0)
2956 buffer = This->local_buffer + This->wri_offs_frames * This->fmt->nBlockAlign;
2957 else
2958 buffer = This->tmp_buffer;
2960 if(flags & AUDCLNT_BUFFERFLAGS_SILENT)
2961 silence_buffer(This, buffer, written_frames);
2963 if(This->getbuf_last < 0)
2964 alsa_wrap_buffer(This, buffer, written_frames);
2966 This->wri_offs_frames += written_frames;
2967 This->wri_offs_frames %= This->bufsize_frames;
2968 This->held_frames += written_frames;
2969 This->written_frames += written_frames;
2970 This->getbuf_last = 0;
2972 LeaveCriticalSection(&This->lock);
2974 return S_OK;
2977 static const IAudioRenderClientVtbl AudioRenderClient_Vtbl = {
2978 AudioRenderClient_QueryInterface,
2979 AudioRenderClient_AddRef,
2980 AudioRenderClient_Release,
2981 AudioRenderClient_GetBuffer,
2982 AudioRenderClient_ReleaseBuffer
2985 static HRESULT WINAPI AudioCaptureClient_QueryInterface(
2986 IAudioCaptureClient *iface, REFIID riid, void **ppv)
2988 ACImpl *This = impl_from_IAudioCaptureClient(iface);
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_IAudioCaptureClient))
2997 *ppv = iface;
2998 else if(IsEqualIID(riid, &IID_IMarshal))
2999 return IUnknown_QueryInterface(This->pUnkFTMarshal, riid, ppv);
3001 if(*ppv){
3002 IUnknown_AddRef((IUnknown*)*ppv);
3003 return S_OK;
3006 WARN("Unknown interface %s\n", debugstr_guid(riid));
3007 return E_NOINTERFACE;
3010 static ULONG WINAPI AudioCaptureClient_AddRef(IAudioCaptureClient *iface)
3012 ACImpl *This = impl_from_IAudioCaptureClient(iface);
3013 return IAudioClient3_AddRef(&This->IAudioClient3_iface);
3016 static ULONG WINAPI AudioCaptureClient_Release(IAudioCaptureClient *iface)
3018 ACImpl *This = impl_from_IAudioCaptureClient(iface);
3019 return IAudioClient3_Release(&This->IAudioClient3_iface);
3022 static HRESULT WINAPI AudioCaptureClient_GetBuffer(IAudioCaptureClient *iface,
3023 BYTE **data, UINT32 *frames, DWORD *flags, UINT64 *devpos,
3024 UINT64 *qpcpos)
3026 ACImpl *This = impl_from_IAudioCaptureClient(iface);
3028 TRACE("(%p)->(%p, %p, %p, %p, %p)\n", This, data, frames, flags,
3029 devpos, qpcpos);
3031 if(!data)
3032 return E_POINTER;
3034 *data = NULL;
3036 if(!frames || !flags)
3037 return E_POINTER;
3039 EnterCriticalSection(&This->lock);
3041 if(This->getbuf_last){
3042 LeaveCriticalSection(&This->lock);
3043 return AUDCLNT_E_OUT_OF_ORDER;
3046 /* hr = GetNextPacketSize(iface, frames); */
3047 if(This->held_frames < This->mmdev_period_frames){
3048 *frames = 0;
3049 LeaveCriticalSection(&This->lock);
3050 return AUDCLNT_S_BUFFER_EMPTY;
3052 *frames = This->mmdev_period_frames;
3054 if(This->lcl_offs_frames + *frames > This->bufsize_frames){
3055 UINT32 chunk_bytes, offs_bytes, frames_bytes;
3056 if(This->tmp_buffer_frames < *frames){
3057 HeapFree(GetProcessHeap(), 0, This->tmp_buffer);
3058 This->tmp_buffer = HeapAlloc(GetProcessHeap(), 0,
3059 *frames * This->fmt->nBlockAlign);
3060 if(!This->tmp_buffer){
3061 LeaveCriticalSection(&This->lock);
3062 return E_OUTOFMEMORY;
3064 This->tmp_buffer_frames = *frames;
3067 *data = This->tmp_buffer;
3068 chunk_bytes = (This->bufsize_frames - This->lcl_offs_frames) *
3069 This->fmt->nBlockAlign;
3070 offs_bytes = This->lcl_offs_frames * This->fmt->nBlockAlign;
3071 frames_bytes = *frames * This->fmt->nBlockAlign;
3072 memcpy(This->tmp_buffer, This->local_buffer + offs_bytes, chunk_bytes);
3073 memcpy(This->tmp_buffer + chunk_bytes, This->local_buffer,
3074 frames_bytes - chunk_bytes);
3075 }else
3076 *data = This->local_buffer +
3077 This->lcl_offs_frames * This->fmt->nBlockAlign;
3079 This->getbuf_last = *frames;
3080 *flags = 0;
3082 if(devpos)
3083 *devpos = This->written_frames;
3084 if(qpcpos){ /* fixme: qpc of recording time */
3085 LARGE_INTEGER stamp, freq;
3086 QueryPerformanceCounter(&stamp);
3087 QueryPerformanceFrequency(&freq);
3088 *qpcpos = (stamp.QuadPart * (INT64)10000000) / freq.QuadPart;
3091 LeaveCriticalSection(&This->lock);
3093 return *frames ? S_OK : AUDCLNT_S_BUFFER_EMPTY;
3096 static HRESULT WINAPI AudioCaptureClient_ReleaseBuffer(
3097 IAudioCaptureClient *iface, UINT32 done)
3099 ACImpl *This = impl_from_IAudioCaptureClient(iface);
3101 TRACE("(%p)->(%u)\n", This, done);
3103 EnterCriticalSection(&This->lock);
3105 if(!done){
3106 This->getbuf_last = 0;
3107 LeaveCriticalSection(&This->lock);
3108 return S_OK;
3111 if(!This->getbuf_last){
3112 LeaveCriticalSection(&This->lock);
3113 return AUDCLNT_E_OUT_OF_ORDER;
3116 if(This->getbuf_last != done){
3117 LeaveCriticalSection(&This->lock);
3118 return AUDCLNT_E_INVALID_SIZE;
3121 This->written_frames += done;
3122 This->held_frames -= done;
3123 This->lcl_offs_frames += done;
3124 This->lcl_offs_frames %= This->bufsize_frames;
3125 This->getbuf_last = 0;
3127 LeaveCriticalSection(&This->lock);
3129 return S_OK;
3132 static HRESULT WINAPI AudioCaptureClient_GetNextPacketSize(
3133 IAudioCaptureClient *iface, UINT32 *frames)
3135 ACImpl *This = impl_from_IAudioCaptureClient(iface);
3137 TRACE("(%p)->(%p)\n", This, frames);
3139 if(!frames)
3140 return E_POINTER;
3142 EnterCriticalSection(&This->lock);
3144 *frames = This->held_frames < This->mmdev_period_frames ? 0 : This->mmdev_period_frames;
3146 LeaveCriticalSection(&This->lock);
3148 return S_OK;
3151 static const IAudioCaptureClientVtbl AudioCaptureClient_Vtbl =
3153 AudioCaptureClient_QueryInterface,
3154 AudioCaptureClient_AddRef,
3155 AudioCaptureClient_Release,
3156 AudioCaptureClient_GetBuffer,
3157 AudioCaptureClient_ReleaseBuffer,
3158 AudioCaptureClient_GetNextPacketSize
3161 static HRESULT WINAPI AudioClock_QueryInterface(IAudioClock *iface,
3162 REFIID riid, void **ppv)
3164 ACImpl *This = impl_from_IAudioClock(iface);
3166 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
3168 if(!ppv)
3169 return E_POINTER;
3170 *ppv = NULL;
3172 if(IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IAudioClock))
3173 *ppv = iface;
3174 else if(IsEqualIID(riid, &IID_IAudioClock2))
3175 *ppv = &This->IAudioClock2_iface;
3176 if(*ppv){
3177 IUnknown_AddRef((IUnknown*)*ppv);
3178 return S_OK;
3181 WARN("Unknown interface %s\n", debugstr_guid(riid));
3182 return E_NOINTERFACE;
3185 static ULONG WINAPI AudioClock_AddRef(IAudioClock *iface)
3187 ACImpl *This = impl_from_IAudioClock(iface);
3188 return IAudioClient3_AddRef(&This->IAudioClient3_iface);
3191 static ULONG WINAPI AudioClock_Release(IAudioClock *iface)
3193 ACImpl *This = impl_from_IAudioClock(iface);
3194 return IAudioClient3_Release(&This->IAudioClient3_iface);
3197 static HRESULT WINAPI AudioClock_GetFrequency(IAudioClock *iface, UINT64 *freq)
3199 ACImpl *This = impl_from_IAudioClock(iface);
3201 TRACE("(%p)->(%p)\n", This, freq);
3203 if(This->share == AUDCLNT_SHAREMODE_SHARED)
3204 *freq = (UINT64)This->fmt->nSamplesPerSec * This->fmt->nBlockAlign;
3205 else
3206 *freq = This->fmt->nSamplesPerSec;
3208 return S_OK;
3211 static HRESULT WINAPI AudioClock_GetPosition(IAudioClock *iface, UINT64 *pos,
3212 UINT64 *qpctime)
3214 ACImpl *This = impl_from_IAudioClock(iface);
3215 UINT64 position;
3216 snd_pcm_state_t alsa_state;
3218 TRACE("(%p)->(%p, %p)\n", This, pos, qpctime);
3220 if(!pos)
3221 return E_POINTER;
3223 EnterCriticalSection(&This->lock);
3225 /* avail_update required to get accurate snd_pcm_state() */
3226 snd_pcm_avail_update(This->pcm_handle);
3227 alsa_state = snd_pcm_state(This->pcm_handle);
3229 if(This->dataflow == eRender){
3230 position = This->written_frames - This->held_frames;
3232 if(This->started && alsa_state == SND_PCM_STATE_RUNNING && This->held_frames)
3233 /* we should be using snd_pcm_delay here, but it is broken
3234 * especially during ALSA device underrun. instead, let's just
3235 * interpolate between periods with the system timer. */
3236 position += interp_elapsed_frames(This);
3238 position = min(position, This->written_frames - This->held_frames + This->mmdev_period_frames);
3240 position = min(position, This->written_frames);
3241 }else
3242 position = This->written_frames + This->held_frames;
3244 /* ensure monotic growth */
3245 if(position < This->last_pos_frames)
3246 position = This->last_pos_frames;
3247 else
3248 This->last_pos_frames = position;
3250 TRACE("frames written: %u, held: %u, state: 0x%x, position: %u\n",
3251 (UINT32)(This->written_frames%1000000000), This->held_frames,
3252 alsa_state, (UINT32)(position%1000000000));
3254 LeaveCriticalSection(&This->lock);
3256 if(This->share == AUDCLNT_SHAREMODE_SHARED)
3257 *pos = position * This->fmt->nBlockAlign;
3258 else
3259 *pos = position;
3261 if(qpctime){
3262 LARGE_INTEGER stamp, freq;
3263 QueryPerformanceCounter(&stamp);
3264 QueryPerformanceFrequency(&freq);
3265 *qpctime = (stamp.QuadPart * (INT64)10000000) / freq.QuadPart;
3268 return S_OK;
3271 static HRESULT WINAPI AudioClock_GetCharacteristics(IAudioClock *iface,
3272 DWORD *chars)
3274 ACImpl *This = impl_from_IAudioClock(iface);
3276 TRACE("(%p)->(%p)\n", This, chars);
3278 if(!chars)
3279 return E_POINTER;
3281 *chars = AUDIOCLOCK_CHARACTERISTIC_FIXED_FREQ;
3283 return S_OK;
3286 static const IAudioClockVtbl AudioClock_Vtbl =
3288 AudioClock_QueryInterface,
3289 AudioClock_AddRef,
3290 AudioClock_Release,
3291 AudioClock_GetFrequency,
3292 AudioClock_GetPosition,
3293 AudioClock_GetCharacteristics
3296 static HRESULT WINAPI AudioClock2_QueryInterface(IAudioClock2 *iface,
3297 REFIID riid, void **ppv)
3299 ACImpl *This = impl_from_IAudioClock2(iface);
3300 return IAudioClock_QueryInterface(&This->IAudioClock_iface, riid, ppv);
3303 static ULONG WINAPI AudioClock2_AddRef(IAudioClock2 *iface)
3305 ACImpl *This = impl_from_IAudioClock2(iface);
3306 return IAudioClient3_AddRef(&This->IAudioClient3_iface);
3309 static ULONG WINAPI AudioClock2_Release(IAudioClock2 *iface)
3311 ACImpl *This = impl_from_IAudioClock2(iface);
3312 return IAudioClient3_Release(&This->IAudioClient3_iface);
3315 static HRESULT WINAPI AudioClock2_GetDevicePosition(IAudioClock2 *iface,
3316 UINT64 *pos, UINT64 *qpctime)
3318 ACImpl *This = impl_from_IAudioClock2(iface);
3320 FIXME("(%p)->(%p, %p)\n", This, pos, qpctime);
3322 return E_NOTIMPL;
3325 static const IAudioClock2Vtbl AudioClock2_Vtbl =
3327 AudioClock2_QueryInterface,
3328 AudioClock2_AddRef,
3329 AudioClock2_Release,
3330 AudioClock2_GetDevicePosition
3333 static AudioSessionWrapper *AudioSessionWrapper_Create(ACImpl *client)
3335 AudioSessionWrapper *ret;
3337 ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
3338 sizeof(AudioSessionWrapper));
3339 if(!ret)
3340 return NULL;
3342 ret->IAudioSessionControl2_iface.lpVtbl = &AudioSessionControl2_Vtbl;
3343 ret->ISimpleAudioVolume_iface.lpVtbl = &SimpleAudioVolume_Vtbl;
3344 ret->IChannelAudioVolume_iface.lpVtbl = &ChannelAudioVolume_Vtbl;
3346 ret->ref = 1;
3348 ret->client = client;
3349 if(client){
3350 ret->session = client->session;
3351 AudioClient_AddRef(&client->IAudioClient3_iface);
3354 return ret;
3357 static HRESULT WINAPI AudioSessionControl_QueryInterface(
3358 IAudioSessionControl2 *iface, REFIID riid, void **ppv)
3360 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
3362 if(!ppv)
3363 return E_POINTER;
3364 *ppv = NULL;
3366 if(IsEqualIID(riid, &IID_IUnknown) ||
3367 IsEqualIID(riid, &IID_IAudioSessionControl) ||
3368 IsEqualIID(riid, &IID_IAudioSessionControl2))
3369 *ppv = iface;
3370 if(*ppv){
3371 IUnknown_AddRef((IUnknown*)*ppv);
3372 return S_OK;
3375 WARN("Unknown interface %s\n", debugstr_guid(riid));
3376 return E_NOINTERFACE;
3379 static ULONG WINAPI AudioSessionControl_AddRef(IAudioSessionControl2 *iface)
3381 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
3382 ULONG ref;
3383 ref = InterlockedIncrement(&This->ref);
3384 TRACE("(%p) Refcount now %u\n", This, ref);
3385 return ref;
3388 static ULONG WINAPI AudioSessionControl_Release(IAudioSessionControl2 *iface)
3390 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
3391 ULONG ref;
3392 ref = InterlockedDecrement(&This->ref);
3393 TRACE("(%p) Refcount now %u\n", This, ref);
3394 if(!ref){
3395 if(This->client){
3396 EnterCriticalSection(&This->client->lock);
3397 This->client->session_wrapper = NULL;
3398 LeaveCriticalSection(&This->client->lock);
3399 AudioClient_Release(&This->client->IAudioClient3_iface);
3401 HeapFree(GetProcessHeap(), 0, This);
3403 return ref;
3406 static HRESULT WINAPI AudioSessionControl_GetState(IAudioSessionControl2 *iface,
3407 AudioSessionState *state)
3409 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
3410 ACImpl *client;
3412 TRACE("(%p)->(%p)\n", This, state);
3414 if(!state)
3415 return NULL_PTR_ERR;
3417 EnterCriticalSection(&g_sessions_lock);
3419 if(list_empty(&This->session->clients)){
3420 *state = AudioSessionStateExpired;
3421 LeaveCriticalSection(&g_sessions_lock);
3422 return S_OK;
3425 LIST_FOR_EACH_ENTRY(client, &This->session->clients, ACImpl, entry){
3426 EnterCriticalSection(&client->lock);
3427 if(client->started){
3428 *state = AudioSessionStateActive;
3429 LeaveCriticalSection(&client->lock);
3430 LeaveCriticalSection(&g_sessions_lock);
3431 return S_OK;
3433 LeaveCriticalSection(&client->lock);
3436 LeaveCriticalSection(&g_sessions_lock);
3438 *state = AudioSessionStateInactive;
3440 return S_OK;
3443 static HRESULT WINAPI AudioSessionControl_GetDisplayName(
3444 IAudioSessionControl2 *iface, WCHAR **name)
3446 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
3448 FIXME("(%p)->(%p) - stub\n", This, name);
3450 return E_NOTIMPL;
3453 static HRESULT WINAPI AudioSessionControl_SetDisplayName(
3454 IAudioSessionControl2 *iface, const WCHAR *name, const GUID *session)
3456 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
3458 FIXME("(%p)->(%p, %s) - stub\n", This, name, debugstr_guid(session));
3460 return E_NOTIMPL;
3463 static HRESULT WINAPI AudioSessionControl_GetIconPath(
3464 IAudioSessionControl2 *iface, WCHAR **path)
3466 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
3468 FIXME("(%p)->(%p) - stub\n", This, path);
3470 return E_NOTIMPL;
3473 static HRESULT WINAPI AudioSessionControl_SetIconPath(
3474 IAudioSessionControl2 *iface, const WCHAR *path, const GUID *session)
3476 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
3478 FIXME("(%p)->(%p, %s) - stub\n", This, path, debugstr_guid(session));
3480 return E_NOTIMPL;
3483 static HRESULT WINAPI AudioSessionControl_GetGroupingParam(
3484 IAudioSessionControl2 *iface, GUID *group)
3486 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
3488 FIXME("(%p)->(%p) - stub\n", This, group);
3490 return E_NOTIMPL;
3493 static HRESULT WINAPI AudioSessionControl_SetGroupingParam(
3494 IAudioSessionControl2 *iface, const GUID *group, const GUID *session)
3496 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
3498 FIXME("(%p)->(%s, %s) - stub\n", This, debugstr_guid(group),
3499 debugstr_guid(session));
3501 return E_NOTIMPL;
3504 static HRESULT WINAPI AudioSessionControl_RegisterAudioSessionNotification(
3505 IAudioSessionControl2 *iface, IAudioSessionEvents *events)
3507 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
3509 FIXME("(%p)->(%p) - stub\n", This, events);
3511 return S_OK;
3514 static HRESULT WINAPI AudioSessionControl_UnregisterAudioSessionNotification(
3515 IAudioSessionControl2 *iface, IAudioSessionEvents *events)
3517 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
3519 FIXME("(%p)->(%p) - stub\n", This, events);
3521 return S_OK;
3524 static HRESULT WINAPI AudioSessionControl_GetSessionIdentifier(
3525 IAudioSessionControl2 *iface, WCHAR **id)
3527 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
3529 FIXME("(%p)->(%p) - stub\n", This, id);
3531 return E_NOTIMPL;
3534 static HRESULT WINAPI AudioSessionControl_GetSessionInstanceIdentifier(
3535 IAudioSessionControl2 *iface, WCHAR **id)
3537 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
3539 FIXME("(%p)->(%p) - stub\n", This, id);
3541 return E_NOTIMPL;
3544 static HRESULT WINAPI AudioSessionControl_GetProcessId(
3545 IAudioSessionControl2 *iface, DWORD *pid)
3547 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
3549 TRACE("(%p)->(%p)\n", This, pid);
3551 if(!pid)
3552 return E_POINTER;
3554 *pid = GetCurrentProcessId();
3556 return S_OK;
3559 static HRESULT WINAPI AudioSessionControl_IsSystemSoundsSession(
3560 IAudioSessionControl2 *iface)
3562 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
3564 TRACE("(%p)\n", This);
3566 return S_FALSE;
3569 static HRESULT WINAPI AudioSessionControl_SetDuckingPreference(
3570 IAudioSessionControl2 *iface, BOOL optout)
3572 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
3574 TRACE("(%p)->(%d)\n", This, optout);
3576 return S_OK;
3579 static const IAudioSessionControl2Vtbl AudioSessionControl2_Vtbl =
3581 AudioSessionControl_QueryInterface,
3582 AudioSessionControl_AddRef,
3583 AudioSessionControl_Release,
3584 AudioSessionControl_GetState,
3585 AudioSessionControl_GetDisplayName,
3586 AudioSessionControl_SetDisplayName,
3587 AudioSessionControl_GetIconPath,
3588 AudioSessionControl_SetIconPath,
3589 AudioSessionControl_GetGroupingParam,
3590 AudioSessionControl_SetGroupingParam,
3591 AudioSessionControl_RegisterAudioSessionNotification,
3592 AudioSessionControl_UnregisterAudioSessionNotification,
3593 AudioSessionControl_GetSessionIdentifier,
3594 AudioSessionControl_GetSessionInstanceIdentifier,
3595 AudioSessionControl_GetProcessId,
3596 AudioSessionControl_IsSystemSoundsSession,
3597 AudioSessionControl_SetDuckingPreference
3600 static HRESULT WINAPI SimpleAudioVolume_QueryInterface(
3601 ISimpleAudioVolume *iface, REFIID riid, void **ppv)
3603 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
3605 if(!ppv)
3606 return E_POINTER;
3607 *ppv = NULL;
3609 if(IsEqualIID(riid, &IID_IUnknown) ||
3610 IsEqualIID(riid, &IID_ISimpleAudioVolume))
3611 *ppv = iface;
3612 if(*ppv){
3613 IUnknown_AddRef((IUnknown*)*ppv);
3614 return S_OK;
3617 WARN("Unknown interface %s\n", debugstr_guid(riid));
3618 return E_NOINTERFACE;
3621 static ULONG WINAPI SimpleAudioVolume_AddRef(ISimpleAudioVolume *iface)
3623 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
3624 return AudioSessionControl_AddRef(&This->IAudioSessionControl2_iface);
3627 static ULONG WINAPI SimpleAudioVolume_Release(ISimpleAudioVolume *iface)
3629 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
3630 return AudioSessionControl_Release(&This->IAudioSessionControl2_iface);
3633 static HRESULT WINAPI SimpleAudioVolume_SetMasterVolume(
3634 ISimpleAudioVolume *iface, float level, const GUID *context)
3636 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
3637 AudioSession *session = This->session;
3639 TRACE("(%p)->(%f, %s)\n", session, level, wine_dbgstr_guid(context));
3641 if(level < 0.f || level > 1.f)
3642 return E_INVALIDARG;
3644 if(context)
3645 FIXME("Notifications not supported yet\n");
3647 TRACE("ALSA does not support volume control\n");
3649 EnterCriticalSection(&session->lock);
3651 session->master_vol = level;
3653 LeaveCriticalSection(&session->lock);
3655 return S_OK;
3658 static HRESULT WINAPI SimpleAudioVolume_GetMasterVolume(
3659 ISimpleAudioVolume *iface, float *level)
3661 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
3662 AudioSession *session = This->session;
3664 TRACE("(%p)->(%p)\n", session, level);
3666 if(!level)
3667 return NULL_PTR_ERR;
3669 *level = session->master_vol;
3671 return S_OK;
3674 static HRESULT WINAPI SimpleAudioVolume_SetMute(ISimpleAudioVolume *iface,
3675 BOOL mute, const GUID *context)
3677 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
3678 AudioSession *session = This->session;
3680 TRACE("(%p)->(%u, %s)\n", session, mute, debugstr_guid(context));
3682 if(context)
3683 FIXME("Notifications not supported yet\n");
3685 session->mute = mute;
3687 return S_OK;
3690 static HRESULT WINAPI SimpleAudioVolume_GetMute(ISimpleAudioVolume *iface,
3691 BOOL *mute)
3693 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
3694 AudioSession *session = This->session;
3696 TRACE("(%p)->(%p)\n", session, mute);
3698 if(!mute)
3699 return NULL_PTR_ERR;
3701 *mute = session->mute;
3703 return S_OK;
3706 static const ISimpleAudioVolumeVtbl SimpleAudioVolume_Vtbl =
3708 SimpleAudioVolume_QueryInterface,
3709 SimpleAudioVolume_AddRef,
3710 SimpleAudioVolume_Release,
3711 SimpleAudioVolume_SetMasterVolume,
3712 SimpleAudioVolume_GetMasterVolume,
3713 SimpleAudioVolume_SetMute,
3714 SimpleAudioVolume_GetMute
3717 static HRESULT WINAPI AudioStreamVolume_QueryInterface(
3718 IAudioStreamVolume *iface, REFIID riid, void **ppv)
3720 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
3722 if(!ppv)
3723 return E_POINTER;
3724 *ppv = NULL;
3726 if(IsEqualIID(riid, &IID_IUnknown) ||
3727 IsEqualIID(riid, &IID_IAudioStreamVolume))
3728 *ppv = iface;
3729 if(*ppv){
3730 IUnknown_AddRef((IUnknown*)*ppv);
3731 return S_OK;
3734 WARN("Unknown interface %s\n", debugstr_guid(riid));
3735 return E_NOINTERFACE;
3738 static ULONG WINAPI AudioStreamVolume_AddRef(IAudioStreamVolume *iface)
3740 ACImpl *This = impl_from_IAudioStreamVolume(iface);
3741 return IAudioClient3_AddRef(&This->IAudioClient3_iface);
3744 static ULONG WINAPI AudioStreamVolume_Release(IAudioStreamVolume *iface)
3746 ACImpl *This = impl_from_IAudioStreamVolume(iface);
3747 return IAudioClient3_Release(&This->IAudioClient3_iface);
3750 static HRESULT WINAPI AudioStreamVolume_GetChannelCount(
3751 IAudioStreamVolume *iface, UINT32 *out)
3753 ACImpl *This = impl_from_IAudioStreamVolume(iface);
3755 TRACE("(%p)->(%p)\n", This, out);
3757 if(!out)
3758 return E_POINTER;
3760 *out = This->fmt->nChannels;
3762 return S_OK;
3765 static HRESULT WINAPI AudioStreamVolume_SetChannelVolume(
3766 IAudioStreamVolume *iface, UINT32 index, float level)
3768 ACImpl *This = impl_from_IAudioStreamVolume(iface);
3770 TRACE("(%p)->(%d, %f)\n", This, index, level);
3772 if(level < 0.f || level > 1.f)
3773 return E_INVALIDARG;
3775 if(index >= This->fmt->nChannels)
3776 return E_INVALIDARG;
3778 TRACE("ALSA does not support volume control\n");
3780 EnterCriticalSection(&This->lock);
3782 This->vols[index] = level;
3784 LeaveCriticalSection(&This->lock);
3786 return S_OK;
3789 static HRESULT WINAPI AudioStreamVolume_GetChannelVolume(
3790 IAudioStreamVolume *iface, UINT32 index, float *level)
3792 ACImpl *This = impl_from_IAudioStreamVolume(iface);
3794 TRACE("(%p)->(%d, %p)\n", This, index, level);
3796 if(!level)
3797 return E_POINTER;
3799 if(index >= This->fmt->nChannels)
3800 return E_INVALIDARG;
3802 *level = This->vols[index];
3804 return S_OK;
3807 static HRESULT WINAPI AudioStreamVolume_SetAllVolumes(
3808 IAudioStreamVolume *iface, UINT32 count, const float *levels)
3810 ACImpl *This = impl_from_IAudioStreamVolume(iface);
3811 unsigned int i;
3813 TRACE("(%p)->(%d, %p)\n", This, count, levels);
3815 if(!levels)
3816 return E_POINTER;
3818 if(count != This->fmt->nChannels)
3819 return E_INVALIDARG;
3821 TRACE("ALSA does not support volume control\n");
3823 EnterCriticalSection(&This->lock);
3825 for(i = 0; i < count; ++i)
3826 This->vols[i] = levels[i];
3828 LeaveCriticalSection(&This->lock);
3830 return S_OK;
3833 static HRESULT WINAPI AudioStreamVolume_GetAllVolumes(
3834 IAudioStreamVolume *iface, UINT32 count, float *levels)
3836 ACImpl *This = impl_from_IAudioStreamVolume(iface);
3837 unsigned int i;
3839 TRACE("(%p)->(%d, %p)\n", This, count, levels);
3841 if(!levels)
3842 return E_POINTER;
3844 if(count != This->fmt->nChannels)
3845 return E_INVALIDARG;
3847 EnterCriticalSection(&This->lock);
3849 for(i = 0; i < count; ++i)
3850 levels[i] = This->vols[i];
3852 LeaveCriticalSection(&This->lock);
3854 return S_OK;
3857 static const IAudioStreamVolumeVtbl AudioStreamVolume_Vtbl =
3859 AudioStreamVolume_QueryInterface,
3860 AudioStreamVolume_AddRef,
3861 AudioStreamVolume_Release,
3862 AudioStreamVolume_GetChannelCount,
3863 AudioStreamVolume_SetChannelVolume,
3864 AudioStreamVolume_GetChannelVolume,
3865 AudioStreamVolume_SetAllVolumes,
3866 AudioStreamVolume_GetAllVolumes
3869 static HRESULT WINAPI ChannelAudioVolume_QueryInterface(
3870 IChannelAudioVolume *iface, REFIID riid, void **ppv)
3872 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
3874 if(!ppv)
3875 return E_POINTER;
3876 *ppv = NULL;
3878 if(IsEqualIID(riid, &IID_IUnknown) ||
3879 IsEqualIID(riid, &IID_IChannelAudioVolume))
3880 *ppv = iface;
3881 if(*ppv){
3882 IUnknown_AddRef((IUnknown*)*ppv);
3883 return S_OK;
3886 WARN("Unknown interface %s\n", debugstr_guid(riid));
3887 return E_NOINTERFACE;
3890 static ULONG WINAPI ChannelAudioVolume_AddRef(IChannelAudioVolume *iface)
3892 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
3893 return AudioSessionControl_AddRef(&This->IAudioSessionControl2_iface);
3896 static ULONG WINAPI ChannelAudioVolume_Release(IChannelAudioVolume *iface)
3898 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
3899 return AudioSessionControl_Release(&This->IAudioSessionControl2_iface);
3902 static HRESULT WINAPI ChannelAudioVolume_GetChannelCount(
3903 IChannelAudioVolume *iface, UINT32 *out)
3905 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
3906 AudioSession *session = This->session;
3908 TRACE("(%p)->(%p)\n", session, out);
3910 if(!out)
3911 return NULL_PTR_ERR;
3913 *out = session->channel_count;
3915 return S_OK;
3918 static HRESULT WINAPI ChannelAudioVolume_SetChannelVolume(
3919 IChannelAudioVolume *iface, UINT32 index, float level,
3920 const GUID *context)
3922 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
3923 AudioSession *session = This->session;
3925 TRACE("(%p)->(%d, %f, %s)\n", session, index, level,
3926 wine_dbgstr_guid(context));
3928 if(level < 0.f || level > 1.f)
3929 return E_INVALIDARG;
3931 if(index >= session->channel_count)
3932 return E_INVALIDARG;
3934 if(context)
3935 FIXME("Notifications not supported yet\n");
3937 TRACE("ALSA does not support volume control\n");
3939 EnterCriticalSection(&session->lock);
3941 session->channel_vols[index] = level;
3943 LeaveCriticalSection(&session->lock);
3945 return S_OK;
3948 static HRESULT WINAPI ChannelAudioVolume_GetChannelVolume(
3949 IChannelAudioVolume *iface, UINT32 index, float *level)
3951 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
3952 AudioSession *session = This->session;
3954 TRACE("(%p)->(%d, %p)\n", session, index, level);
3956 if(!level)
3957 return NULL_PTR_ERR;
3959 if(index >= session->channel_count)
3960 return E_INVALIDARG;
3962 *level = session->channel_vols[index];
3964 return S_OK;
3967 static HRESULT WINAPI ChannelAudioVolume_SetAllVolumes(
3968 IChannelAudioVolume *iface, UINT32 count, const float *levels,
3969 const GUID *context)
3971 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
3972 AudioSession *session = This->session;
3973 unsigned int i;
3975 TRACE("(%p)->(%d, %p, %s)\n", session, count, levels,
3976 wine_dbgstr_guid(context));
3978 if(!levels)
3979 return NULL_PTR_ERR;
3981 if(count != session->channel_count)
3982 return E_INVALIDARG;
3984 if(context)
3985 FIXME("Notifications not supported yet\n");
3987 TRACE("ALSA does not support volume control\n");
3989 EnterCriticalSection(&session->lock);
3991 for(i = 0; i < count; ++i)
3992 session->channel_vols[i] = levels[i];
3994 LeaveCriticalSection(&session->lock);
3996 return S_OK;
3999 static HRESULT WINAPI ChannelAudioVolume_GetAllVolumes(
4000 IChannelAudioVolume *iface, UINT32 count, float *levels)
4002 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
4003 AudioSession *session = This->session;
4004 unsigned int i;
4006 TRACE("(%p)->(%d, %p)\n", session, count, levels);
4008 if(!levels)
4009 return NULL_PTR_ERR;
4011 if(count != session->channel_count)
4012 return E_INVALIDARG;
4014 for(i = 0; i < count; ++i)
4015 levels[i] = session->channel_vols[i];
4017 return S_OK;
4020 static const IChannelAudioVolumeVtbl ChannelAudioVolume_Vtbl =
4022 ChannelAudioVolume_QueryInterface,
4023 ChannelAudioVolume_AddRef,
4024 ChannelAudioVolume_Release,
4025 ChannelAudioVolume_GetChannelCount,
4026 ChannelAudioVolume_SetChannelVolume,
4027 ChannelAudioVolume_GetChannelVolume,
4028 ChannelAudioVolume_SetAllVolumes,
4029 ChannelAudioVolume_GetAllVolumes
4032 static HRESULT WINAPI AudioSessionManager_QueryInterface(IAudioSessionManager2 *iface,
4033 REFIID riid, void **ppv)
4035 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
4037 if(!ppv)
4038 return E_POINTER;
4039 *ppv = NULL;
4041 if(IsEqualIID(riid, &IID_IUnknown) ||
4042 IsEqualIID(riid, &IID_IAudioSessionManager) ||
4043 IsEqualIID(riid, &IID_IAudioSessionManager2))
4044 *ppv = iface;
4045 if(*ppv){
4046 IUnknown_AddRef((IUnknown*)*ppv);
4047 return S_OK;
4050 WARN("Unknown interface %s\n", debugstr_guid(riid));
4051 return E_NOINTERFACE;
4054 static ULONG WINAPI AudioSessionManager_AddRef(IAudioSessionManager2 *iface)
4056 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
4057 ULONG ref;
4058 ref = InterlockedIncrement(&This->ref);
4059 TRACE("(%p) Refcount now %u\n", This, ref);
4060 return ref;
4063 static ULONG WINAPI AudioSessionManager_Release(IAudioSessionManager2 *iface)
4065 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
4066 ULONG ref;
4067 ref = InterlockedDecrement(&This->ref);
4068 TRACE("(%p) Refcount now %u\n", This, ref);
4069 if(!ref)
4070 HeapFree(GetProcessHeap(), 0, This);
4071 return ref;
4074 static HRESULT WINAPI AudioSessionManager_GetAudioSessionControl(
4075 IAudioSessionManager2 *iface, const GUID *session_guid, DWORD flags,
4076 IAudioSessionControl **out)
4078 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
4079 AudioSession *session;
4080 AudioSessionWrapper *wrapper;
4081 HRESULT hr;
4083 TRACE("(%p)->(%s, %x, %p)\n", This, debugstr_guid(session_guid),
4084 flags, out);
4086 hr = get_audio_session(session_guid, This->device, 0, &session);
4087 if(FAILED(hr))
4088 return hr;
4090 wrapper = AudioSessionWrapper_Create(NULL);
4091 if(!wrapper)
4092 return E_OUTOFMEMORY;
4094 wrapper->session = session;
4096 *out = (IAudioSessionControl*)&wrapper->IAudioSessionControl2_iface;
4098 return S_OK;
4101 static HRESULT WINAPI AudioSessionManager_GetSimpleAudioVolume(
4102 IAudioSessionManager2 *iface, const GUID *session_guid, DWORD flags,
4103 ISimpleAudioVolume **out)
4105 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
4106 AudioSession *session;
4107 AudioSessionWrapper *wrapper;
4108 HRESULT hr;
4110 TRACE("(%p)->(%s, %x, %p)\n", This, debugstr_guid(session_guid),
4111 flags, out);
4113 hr = get_audio_session(session_guid, This->device, 0, &session);
4114 if(FAILED(hr))
4115 return hr;
4117 wrapper = AudioSessionWrapper_Create(NULL);
4118 if(!wrapper)
4119 return E_OUTOFMEMORY;
4121 wrapper->session = session;
4123 *out = &wrapper->ISimpleAudioVolume_iface;
4125 return S_OK;
4128 static HRESULT WINAPI AudioSessionManager_GetSessionEnumerator(
4129 IAudioSessionManager2 *iface, IAudioSessionEnumerator **out)
4131 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
4132 FIXME("(%p)->(%p) - stub\n", This, out);
4133 return E_NOTIMPL;
4136 static HRESULT WINAPI AudioSessionManager_RegisterSessionNotification(
4137 IAudioSessionManager2 *iface, IAudioSessionNotification *notification)
4139 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
4140 FIXME("(%p)->(%p) - stub\n", This, notification);
4141 return E_NOTIMPL;
4144 static HRESULT WINAPI AudioSessionManager_UnregisterSessionNotification(
4145 IAudioSessionManager2 *iface, IAudioSessionNotification *notification)
4147 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
4148 FIXME("(%p)->(%p) - stub\n", This, notification);
4149 return E_NOTIMPL;
4152 static HRESULT WINAPI AudioSessionManager_RegisterDuckNotification(
4153 IAudioSessionManager2 *iface, const WCHAR *session_id,
4154 IAudioVolumeDuckNotification *notification)
4156 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
4157 FIXME("(%p)->(%p) - stub\n", This, notification);
4158 return E_NOTIMPL;
4161 static HRESULT WINAPI AudioSessionManager_UnregisterDuckNotification(
4162 IAudioSessionManager2 *iface,
4163 IAudioVolumeDuckNotification *notification)
4165 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
4166 FIXME("(%p)->(%p) - stub\n", This, notification);
4167 return E_NOTIMPL;
4170 static const IAudioSessionManager2Vtbl AudioSessionManager2_Vtbl =
4172 AudioSessionManager_QueryInterface,
4173 AudioSessionManager_AddRef,
4174 AudioSessionManager_Release,
4175 AudioSessionManager_GetAudioSessionControl,
4176 AudioSessionManager_GetSimpleAudioVolume,
4177 AudioSessionManager_GetSessionEnumerator,
4178 AudioSessionManager_RegisterSessionNotification,
4179 AudioSessionManager_UnregisterSessionNotification,
4180 AudioSessionManager_RegisterDuckNotification,
4181 AudioSessionManager_UnregisterDuckNotification
4184 HRESULT WINAPI AUDDRV_GetAudioSessionManager(IMMDevice *device,
4185 IAudioSessionManager2 **out)
4187 SessionMgr *This;
4189 This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(SessionMgr));
4190 if(!This)
4191 return E_OUTOFMEMORY;
4193 This->IAudioSessionManager2_iface.lpVtbl = &AudioSessionManager2_Vtbl;
4194 This->device = device;
4195 This->ref = 1;
4197 *out = &This->IAudioSessionManager2_iface;
4199 return S_OK;
4202 static unsigned int alsa_probe_num_speakers(char *name) {
4203 snd_pcm_t *handle;
4204 snd_pcm_hw_params_t *params;
4205 int err;
4206 unsigned int max_channels = 0;
4208 if ((err = snd_pcm_open(&handle, name, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK)) < 0) {
4209 WARN("The device \"%s\" failed to open: %d (%s).\n",
4210 name, err, snd_strerror(err));
4211 return 0;
4214 params = HeapAlloc(GetProcessHeap(), 0, snd_pcm_hw_params_sizeof());
4215 if (!params) {
4216 WARN("Out of memory.\n");
4217 snd_pcm_close(handle);
4218 return 0;
4221 if ((err = snd_pcm_hw_params_any(handle, params)) < 0) {
4222 WARN("snd_pcm_hw_params_any failed for \"%s\": %d (%s).\n",
4223 name, err, snd_strerror(err));
4224 goto exit;
4227 if ((err = snd_pcm_hw_params_get_channels_max(params,
4228 &max_channels)) < 0){
4229 WARN("Unable to get max channels: %d (%s)\n", err, snd_strerror(err));
4230 goto exit;
4233 exit:
4234 HeapFree(GetProcessHeap(), 0, params);
4235 snd_pcm_close(handle);
4237 return max_channels;
4240 enum AudioDeviceConnectionType {
4241 AudioDeviceConnectionType_Unknown = 0,
4242 AudioDeviceConnectionType_PCI,
4243 AudioDeviceConnectionType_USB
4246 HRESULT WINAPI AUDDRV_GetPropValue(GUID *guid, const PROPERTYKEY *prop, PROPVARIANT *out)
4248 char name[256];
4249 EDataFlow flow;
4251 static const PROPERTYKEY devicepath_key = { /* undocumented? - {b3f8fa53-0004-438e-9003-51a46e139bfc},2 */
4252 {0xb3f8fa53, 0x0004, 0x438e, {0x90, 0x03, 0x51, 0xa4, 0x6e, 0x13, 0x9b, 0xfc}}, 2
4255 TRACE("%s, (%s,%u), %p\n", wine_dbgstr_guid(guid), wine_dbgstr_guid(&prop->fmtid), prop->pid, out);
4257 if(!get_alsa_name_by_guid(guid, name, sizeof(name), &flow))
4259 WARN("Unknown interface %s\n", debugstr_guid(guid));
4260 return E_NOINTERFACE;
4263 if(IsEqualPropertyKey(*prop, devicepath_key))
4265 char uevent[MAX_PATH];
4266 FILE *fuevent;
4267 int card, device;
4269 /* only implemented for identifiable devices, i.e. not "default" */
4270 if(!sscanf(name, "plughw:%u,%u", &card, &device))
4271 return E_NOTIMPL;
4273 sprintf(uevent, "/sys/class/sound/card%u/device/uevent", card);
4274 fuevent = fopen(uevent, "r");
4276 if(fuevent){
4277 enum AudioDeviceConnectionType connection = AudioDeviceConnectionType_Unknown;
4278 USHORT vendor_id = 0, product_id = 0;
4279 char line[256];
4281 while (fgets(line, sizeof(line), fuevent)) {
4282 char *val;
4283 size_t val_len;
4285 if((val = strchr(line, '='))) {
4286 val[0] = 0;
4287 val++;
4289 val_len = strlen(val);
4290 if(val_len > 0 && val[val_len - 1] == '\n') { val[val_len - 1] = 0; }
4292 if(!strcmp(line, "PCI_ID")){
4293 connection = AudioDeviceConnectionType_PCI;
4294 if(sscanf(val, "%hX:%hX", &vendor_id, &product_id)<2){
4295 WARN("Unexpected input when reading PCI_ID in uevent file.\n");
4296 connection = AudioDeviceConnectionType_Unknown;
4297 break;
4299 }else if(!strcmp(line, "DEVTYPE") && !strcmp(val,"usb_interface"))
4300 connection = AudioDeviceConnectionType_USB;
4301 else if(!strcmp(line, "PRODUCT"))
4302 if(sscanf(val, "%hx/%hx/", &vendor_id, &product_id)<2){
4303 WARN("Unexpected input when reading PRODUCT in uevent file.\n");
4304 connection = AudioDeviceConnectionType_Unknown;
4305 break;
4310 fclose(fuevent);
4312 if(connection == AudioDeviceConnectionType_USB || connection == AudioDeviceConnectionType_PCI){
4313 static const WCHAR usbformatW[] = { '{','1','}','.','U','S','B','\\','V','I','D','_',
4314 '%','0','4','X','&','P','I','D','_','%','0','4','X','\\',
4315 '%','u','&','%','0','8','X',0 }; /* "{1}.USB\VID_%04X&PID_%04X\%u&%08X" */
4316 static const WCHAR pciformatW[] = { '{','1','}','.','H','D','A','U','D','I','O','\\','F','U','N','C','_','0','1','&',
4317 'V','E','N','_','%','0','4','X','&','D','E','V','_',
4318 '%','0','4','X','\\','%','u','&','%','0','8','X',0 }; /* "{1}.HDAUDIO\FUNC_01&VEN_%04X&DEV_%04X\%u&%08X" */
4319 UINT serial_number;
4321 /* As hardly any audio devices have serial numbers, Windows instead
4322 appears to use a persistent random number. We emulate this here
4323 by instead using the last 8 hex digits of the GUID. */
4324 serial_number = (guid->Data4[4] << 24) | (guid->Data4[5] << 16) | (guid->Data4[6] << 8) | guid->Data4[7];
4326 out->vt = VT_LPWSTR;
4327 out->pwszVal = CoTaskMemAlloc(128 * sizeof(WCHAR));
4329 if(!out->pwszVal)
4330 return E_OUTOFMEMORY;
4332 if(connection == AudioDeviceConnectionType_USB)
4333 sprintfW( out->pwszVal, usbformatW, vendor_id, product_id, device, serial_number);
4334 else if(connection == AudioDeviceConnectionType_PCI)
4335 sprintfW( out->pwszVal, pciformatW, vendor_id, product_id, device, serial_number);
4337 return S_OK;
4339 }else{
4340 WARN("Could not open %s for reading\n", uevent);
4341 return E_NOTIMPL;
4343 } else if (flow != eCapture && IsEqualPropertyKey(*prop, PKEY_AudioEndpoint_PhysicalSpeakers)) {
4344 unsigned int num_speakers, card, device;
4345 char hwname[255];
4347 if (sscanf(name, "plughw:%u,%u", &card, &device))
4348 sprintf(hwname, "hw:%u,%u", card, device); /* must be hw rather than plughw to work */
4349 else
4350 strcpy(hwname, name);
4352 num_speakers = alsa_probe_num_speakers(hwname);
4353 if (num_speakers == 0)
4354 return E_FAIL;
4356 out->vt = VT_UI4;
4358 if (num_speakers > 6)
4359 out->ulVal = KSAUDIO_SPEAKER_STEREO;
4360 else if (num_speakers == 6)
4361 out->ulVal = KSAUDIO_SPEAKER_5POINT1;
4362 else if (num_speakers >= 4)
4363 out->ulVal = KSAUDIO_SPEAKER_QUAD;
4364 else if (num_speakers >= 2)
4365 out->ulVal = KSAUDIO_SPEAKER_STEREO;
4366 else if (num_speakers == 1)
4367 out->ulVal = KSAUDIO_SPEAKER_MONO;
4369 return S_OK;
4372 TRACE("Unimplemented property %s,%u\n", wine_dbgstr_guid(&prop->fmtid), prop->pid);
4374 return E_NOTIMPL;