wined3d: Enforce a stable texture units mapping.
[wine.git] / dlls / winealsa.drv / mmdevdrv.c
blob14eac39b20f0b142716851ec54937dfd4765cda3
1 /*
2 * Copyright 2010 Maarten Lankhorst for CodeWeavers
3 * Copyright 2011 Andrew Eikum for CodeWeavers
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20 #define NONAMELESSUNION
21 #define COBJMACROS
22 #include "config.h"
24 #include <stdarg.h>
25 #include <math.h>
27 #include "windef.h"
28 #include "winbase.h"
29 #include "winnls.h"
30 #include "winreg.h"
31 #include "wine/debug.h"
32 #include "wine/unicode.h"
33 #include "wine/list.h"
35 #include "propsys.h"
36 #include "initguid.h"
37 #include "ole2.h"
38 #include "propkey.h"
39 #include "mmdeviceapi.h"
40 #include "devpkey.h"
41 #include "mmsystem.h"
42 #include "dsound.h"
44 #include "initguid.h"
45 #include "endpointvolume.h"
46 #include "audioclient.h"
47 #include "audiopolicy.h"
49 #include <alsa/asoundlib.h>
51 WINE_DEFAULT_DEBUG_CHANNEL(alsa);
52 WINE_DECLARE_DEBUG_CHANNEL(winediag);
54 #define NULL_PTR_ERR MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, RPC_X_NULL_REF_POINTER)
56 static const REFERENCE_TIME DefaultPeriod = 100000;
57 static const REFERENCE_TIME MinimumPeriod = 50000;
58 #define EXTRA_SAFE_RT 40000
60 struct ACImpl;
61 typedef struct ACImpl ACImpl;
63 typedef struct _AudioSession {
64 GUID guid;
65 struct list clients;
67 IMMDevice *device;
69 float master_vol;
70 UINT32 channel_count;
71 float *channel_vols;
72 BOOL mute;
74 CRITICAL_SECTION lock;
76 struct list entry;
77 } AudioSession;
79 typedef struct _AudioSessionWrapper {
80 IAudioSessionControl2 IAudioSessionControl2_iface;
81 IChannelAudioVolume IChannelAudioVolume_iface;
82 ISimpleAudioVolume ISimpleAudioVolume_iface;
84 LONG ref;
86 ACImpl *client;
87 AudioSession *session;
88 } AudioSessionWrapper;
90 struct ACImpl {
91 IAudioClient IAudioClient_iface;
92 IAudioRenderClient IAudioRenderClient_iface;
93 IAudioCaptureClient IAudioCaptureClient_iface;
94 IAudioClock IAudioClock_iface;
95 IAudioClock2 IAudioClock2_iface;
96 IAudioStreamVolume IAudioStreamVolume_iface;
98 LONG ref;
100 snd_pcm_t *pcm_handle;
101 snd_pcm_uframes_t alsa_bufsize_frames, alsa_period_frames, safe_rewind_frames;
102 snd_pcm_hw_params_t *hw_params; /* does not hold state between calls */
103 snd_pcm_format_t alsa_format;
105 LARGE_INTEGER last_period_time;
107 IMMDevice *parent;
108 IUnknown *pUnkFTMarshal;
110 EDataFlow dataflow;
111 WAVEFORMATEX *fmt;
112 DWORD flags;
113 AUDCLNT_SHAREMODE share;
114 HANDLE event;
115 float *vols;
117 BOOL need_remapping;
118 int alsa_channels;
119 int alsa_channel_map[32];
121 BOOL initted, started;
122 REFERENCE_TIME mmdev_period_rt;
123 UINT64 written_frames, last_pos_frames;
124 UINT32 bufsize_frames, held_frames, tmp_buffer_frames, mmdev_period_frames;
125 snd_pcm_uframes_t remapping_buf_frames;
126 UINT32 lcl_offs_frames; /* offs into local_buffer where valid data starts */
127 UINT32 wri_offs_frames; /* where to write fresh data in local_buffer */
128 UINT32 hidden_frames; /* ALSA reserve to ensure continuous rendering */
129 UINT32 data_in_alsa_frames;
131 HANDLE timer;
132 BYTE *local_buffer, *tmp_buffer, *remapping_buf, *silence_buf;
133 LONG32 getbuf_last; /* <0 when using tmp_buffer */
135 CRITICAL_SECTION lock;
137 AudioSession *session;
138 AudioSessionWrapper *session_wrapper;
140 struct list entry;
143 typedef struct _SessionMgr {
144 IAudioSessionManager2 IAudioSessionManager2_iface;
146 LONG ref;
148 IMMDevice *device;
149 } SessionMgr;
151 static HANDLE g_timer_q;
153 static CRITICAL_SECTION g_sessions_lock;
154 static CRITICAL_SECTION_DEBUG g_sessions_lock_debug =
156 0, 0, &g_sessions_lock,
157 { &g_sessions_lock_debug.ProcessLocksList, &g_sessions_lock_debug.ProcessLocksList },
158 0, 0, { (DWORD_PTR)(__FILE__ ": g_sessions_lock") }
160 static CRITICAL_SECTION g_sessions_lock = { &g_sessions_lock_debug, -1, 0, 0, 0, 0 };
161 static struct list g_sessions = LIST_INIT(g_sessions);
163 static const WCHAR defaultW[] = {'d','e','f','a','u','l','t',0};
164 static const char defname[] = "default";
166 static const WCHAR drv_keyW[] = {'S','o','f','t','w','a','r','e','\\',
167 'W','i','n','e','\\','D','r','i','v','e','r','s','\\',
168 'w','i','n','e','a','l','s','a','.','d','r','v',0};
169 static const WCHAR drv_key_devicesW[] = {'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','\\','d','e','v','i','c','e','s',0};
172 static const WCHAR guidW[] = {'g','u','i','d',0};
174 static const IAudioClientVtbl AudioClient_Vtbl;
175 static const IAudioRenderClientVtbl AudioRenderClient_Vtbl;
176 static const IAudioCaptureClientVtbl AudioCaptureClient_Vtbl;
177 static const IAudioSessionControl2Vtbl AudioSessionControl2_Vtbl;
178 static const ISimpleAudioVolumeVtbl SimpleAudioVolume_Vtbl;
179 static const IAudioClockVtbl AudioClock_Vtbl;
180 static const IAudioClock2Vtbl AudioClock2_Vtbl;
181 static const IAudioStreamVolumeVtbl AudioStreamVolume_Vtbl;
182 static const IChannelAudioVolumeVtbl ChannelAudioVolume_Vtbl;
183 static const IAudioSessionManager2Vtbl AudioSessionManager2_Vtbl;
185 static AudioSessionWrapper *AudioSessionWrapper_Create(ACImpl *client);
187 static inline ACImpl *impl_from_IAudioClient(IAudioClient *iface)
189 return CONTAINING_RECORD(iface, ACImpl, IAudioClient_iface);
192 static inline ACImpl *impl_from_IAudioRenderClient(IAudioRenderClient *iface)
194 return CONTAINING_RECORD(iface, ACImpl, IAudioRenderClient_iface);
197 static inline ACImpl *impl_from_IAudioCaptureClient(IAudioCaptureClient *iface)
199 return CONTAINING_RECORD(iface, ACImpl, IAudioCaptureClient_iface);
202 static inline AudioSessionWrapper *impl_from_IAudioSessionControl2(IAudioSessionControl2 *iface)
204 return CONTAINING_RECORD(iface, AudioSessionWrapper, IAudioSessionControl2_iface);
207 static inline AudioSessionWrapper *impl_from_ISimpleAudioVolume(ISimpleAudioVolume *iface)
209 return CONTAINING_RECORD(iface, AudioSessionWrapper, ISimpleAudioVolume_iface);
212 static inline AudioSessionWrapper *impl_from_IChannelAudioVolume(IChannelAudioVolume *iface)
214 return CONTAINING_RECORD(iface, AudioSessionWrapper, IChannelAudioVolume_iface);
217 static inline ACImpl *impl_from_IAudioClock(IAudioClock *iface)
219 return CONTAINING_RECORD(iface, ACImpl, IAudioClock_iface);
222 static inline ACImpl *impl_from_IAudioClock2(IAudioClock2 *iface)
224 return CONTAINING_RECORD(iface, ACImpl, IAudioClock2_iface);
227 static inline ACImpl *impl_from_IAudioStreamVolume(IAudioStreamVolume *iface)
229 return CONTAINING_RECORD(iface, ACImpl, IAudioStreamVolume_iface);
232 static inline SessionMgr *impl_from_IAudioSessionManager2(IAudioSessionManager2 *iface)
234 return CONTAINING_RECORD(iface, SessionMgr, IAudioSessionManager2_iface);
237 BOOL WINAPI DllMain(HINSTANCE dll, DWORD reason, void *reserved)
239 switch (reason)
241 case DLL_PROCESS_ATTACH:
242 g_timer_q = CreateTimerQueue();
243 if(!g_timer_q)
244 return FALSE;
245 break;
247 case DLL_PROCESS_DETACH:
248 if (reserved) break;
249 DeleteCriticalSection(&g_sessions_lock);
250 break;
252 return TRUE;
255 /* From <dlls/mmdevapi/mmdevapi.h> */
256 enum DriverPriority {
257 Priority_Unavailable = 0,
258 Priority_Low,
259 Priority_Neutral,
260 Priority_Preferred
263 int WINAPI AUDDRV_GetPriority(void)
265 return Priority_Neutral;
268 static void set_device_guid(EDataFlow flow, HKEY drv_key, const WCHAR *key_name,
269 GUID *guid)
271 HKEY key;
272 BOOL opened = FALSE;
273 LONG lr;
275 if(!drv_key){
276 lr = RegCreateKeyExW(HKEY_CURRENT_USER, drv_key_devicesW, 0, NULL, 0, KEY_WRITE,
277 NULL, &drv_key, NULL);
278 if(lr != ERROR_SUCCESS){
279 ERR("RegCreateKeyEx(drv_key) failed: %u\n", lr);
280 return;
282 opened = TRUE;
285 lr = RegCreateKeyExW(drv_key, key_name, 0, NULL, 0, KEY_WRITE,
286 NULL, &key, NULL);
287 if(lr != ERROR_SUCCESS){
288 ERR("RegCreateKeyEx(%s) failed: %u\n", wine_dbgstr_w(key_name), lr);
289 goto exit;
292 lr = RegSetValueExW(key, guidW, 0, REG_BINARY, (BYTE*)guid,
293 sizeof(GUID));
294 if(lr != ERROR_SUCCESS)
295 ERR("RegSetValueEx(%s\\guid) failed: %u\n", wine_dbgstr_w(key_name), lr);
297 RegCloseKey(key);
298 exit:
299 if(opened)
300 RegCloseKey(drv_key);
303 static void get_device_guid(EDataFlow flow, const char *device, GUID *guid)
305 HKEY key = NULL, dev_key;
306 DWORD type, size = sizeof(*guid);
307 WCHAR key_name[256];
309 if(flow == eCapture)
310 key_name[0] = '1';
311 else
312 key_name[0] = '0';
313 key_name[1] = ',';
314 MultiByteToWideChar(CP_UNIXCP, 0, device, -1, key_name + 2,
315 (sizeof(key_name) / sizeof(*key_name)) - 2);
317 if(RegOpenKeyExW(HKEY_CURRENT_USER, drv_key_devicesW, 0, KEY_WRITE|KEY_READ, &key) == ERROR_SUCCESS){
318 if(RegOpenKeyExW(key, key_name, 0, KEY_READ, &dev_key) == ERROR_SUCCESS){
319 if(RegQueryValueExW(dev_key, guidW, 0, &type,
320 (BYTE*)guid, &size) == ERROR_SUCCESS){
321 if(type == REG_BINARY){
322 RegCloseKey(dev_key);
323 RegCloseKey(key);
324 return;
326 ERR("Invalid type for device %s GUID: %u; ignoring and overwriting\n",
327 wine_dbgstr_w(key_name), type);
329 RegCloseKey(dev_key);
333 CoCreateGuid(guid);
335 set_device_guid(flow, key, key_name, guid);
337 if(key)
338 RegCloseKey(key);
341 static BOOL alsa_try_open(const char *devnode, snd_pcm_stream_t stream)
343 snd_pcm_t *handle;
344 int err;
346 TRACE("devnode: %s, stream: %d\n", devnode, stream);
348 if((err = snd_pcm_open(&handle, devnode, stream, SND_PCM_NONBLOCK)) < 0){
349 WARN("The device \"%s\" failed to open: %d (%s).\n",
350 devnode, err, snd_strerror(err));
351 return FALSE;
354 snd_pcm_close(handle);
355 return TRUE;
358 static WCHAR *construct_device_id(EDataFlow flow, const WCHAR *chunk1, const char *chunk2)
360 WCHAR *ret;
361 const WCHAR *prefix;
362 DWORD len_wchars = 0, chunk1_len, copied = 0, prefix_len;
364 static const WCHAR dashW[] = {' ','-',' ',0};
365 static const size_t dashW_len = (sizeof(dashW) / sizeof(*dashW)) - 1;
366 static const WCHAR outW[] = {'O','u','t',':',' ',0};
367 static const WCHAR inW[] = {'I','n',':',' ',0};
369 if(flow == eRender){
370 prefix = outW;
371 prefix_len = (sizeof(outW) / sizeof(*outW)) - 1;
372 len_wchars += prefix_len;
373 }else{
374 prefix = inW;
375 prefix_len = (sizeof(inW) / sizeof(*inW)) - 1;
376 len_wchars += prefix_len;
378 if(chunk1){
379 chunk1_len = strlenW(chunk1);
380 len_wchars += chunk1_len;
382 if(chunk1 && chunk2)
383 len_wchars += dashW_len;
384 if(chunk2)
385 len_wchars += MultiByteToWideChar(CP_UNIXCP, 0, chunk2, -1, NULL, 0) - 1;
386 len_wchars += 1; /* NULL byte */
388 ret = HeapAlloc(GetProcessHeap(), 0, len_wchars * sizeof(WCHAR));
390 memcpy(ret, prefix, prefix_len * sizeof(WCHAR));
391 copied += prefix_len;
392 if(chunk1){
393 memcpy(ret + copied, chunk1, chunk1_len * sizeof(WCHAR));
394 copied += chunk1_len;
396 if(chunk1 && chunk2){
397 memcpy(ret + copied, dashW, dashW_len * sizeof(WCHAR));
398 copied += dashW_len;
400 if(chunk2){
401 MultiByteToWideChar(CP_UNIXCP, 0, chunk2, -1, ret + copied, len_wchars - copied);
402 }else
403 ret[copied] = 0;
405 TRACE("Enumerated device: %s\n", wine_dbgstr_w(ret));
407 return ret;
410 static HRESULT alsa_get_card_devices(EDataFlow flow, snd_pcm_stream_t stream,
411 WCHAR ***ids, GUID **guids, UINT *num, snd_ctl_t *ctl, int card,
412 const WCHAR *cardnameW)
414 int err, device;
415 snd_pcm_info_t *info;
417 info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, snd_pcm_info_sizeof());
418 if(!info)
419 return E_OUTOFMEMORY;
421 snd_pcm_info_set_subdevice(info, 0);
422 snd_pcm_info_set_stream(info, stream);
424 device = -1;
425 for(err = snd_ctl_pcm_next_device(ctl, &device); device != -1 && err >= 0;
426 err = snd_ctl_pcm_next_device(ctl, &device)){
427 const char *devname;
428 char devnode[32];
430 snd_pcm_info_set_device(info, device);
432 if((err = snd_ctl_pcm_info(ctl, info)) < 0){
433 if(err == -ENOENT)
434 /* This device doesn't have the right stream direction */
435 continue;
437 WARN("Failed to get info for card %d, device %d: %d (%s)\n",
438 card, device, err, snd_strerror(err));
439 continue;
442 sprintf(devnode, "plughw:%d,%d", card, device);
443 if(!alsa_try_open(devnode, stream))
444 continue;
446 if(*num){
447 *ids = HeapReAlloc(GetProcessHeap(), 0, *ids, sizeof(WCHAR *) * (*num + 1));
448 *guids = HeapReAlloc(GetProcessHeap(), 0, *guids, sizeof(GUID) * (*num + 1));
449 }else{
450 *ids = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR *));
451 *guids = HeapAlloc(GetProcessHeap(), 0, sizeof(GUID));
454 devname = snd_pcm_info_get_name(info);
455 if(!devname){
456 WARN("Unable to get device name for card %d, device %d\n", card,
457 device);
458 continue;
461 (*ids)[*num] = construct_device_id(flow, cardnameW, devname);
462 get_device_guid(flow, devnode, &(*guids)[*num]);
464 ++(*num);
467 HeapFree(GetProcessHeap(), 0, info);
469 if(err != 0)
470 WARN("Got a failure during device enumeration on card %d: %d (%s)\n",
471 card, err, snd_strerror(err));
473 return S_OK;
476 static void get_reg_devices(EDataFlow flow, snd_pcm_stream_t stream, WCHAR ***ids,
477 GUID **guids, UINT *num)
479 static const WCHAR ALSAOutputDevices[] = {'A','L','S','A','O','u','t','p','u','t','D','e','v','i','c','e','s',0};
480 static const WCHAR ALSAInputDevices[] = {'A','L','S','A','I','n','p','u','t','D','e','v','i','c','e','s',0};
481 HKEY key;
482 WCHAR reg_devices[256];
483 DWORD size = sizeof(reg_devices), type;
484 const WCHAR *value_name = (stream == SND_PCM_STREAM_PLAYBACK) ? ALSAOutputDevices : ALSAInputDevices;
486 /* @@ Wine registry key: HKCU\Software\Wine\Drivers\winealsa.drv */
487 if(RegOpenKeyW(HKEY_CURRENT_USER, drv_keyW, &key) == ERROR_SUCCESS){
488 if(RegQueryValueExW(key, value_name, 0, &type,
489 (BYTE*)reg_devices, &size) == ERROR_SUCCESS){
490 WCHAR *p = reg_devices;
492 if(type != REG_MULTI_SZ){
493 ERR("Registry ALSA device list value type must be REG_MULTI_SZ\n");
494 RegCloseKey(key);
495 return;
498 while(*p){
499 char devname[64];
501 WideCharToMultiByte(CP_UNIXCP, 0, p, -1, devname, sizeof(devname), NULL, NULL);
503 if(alsa_try_open(devname, stream)){
504 if(*num){
505 *ids = HeapReAlloc(GetProcessHeap(), 0, *ids, sizeof(WCHAR *) * (*num + 1));
506 *guids = HeapReAlloc(GetProcessHeap(), 0, *guids, sizeof(GUID) * (*num + 1));
507 }else{
508 *ids = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR *));
509 *guids = HeapAlloc(GetProcessHeap(), 0, sizeof(GUID));
511 (*ids)[*num] = construct_device_id(flow, p, NULL);
512 get_device_guid(flow, devname, &(*guids)[*num]);
513 ++*num;
516 p += lstrlenW(p) + 1;
520 RegCloseKey(key);
524 static HRESULT alsa_enum_devices(EDataFlow flow, WCHAR ***ids, GUID **guids,
525 UINT *num)
527 snd_pcm_stream_t stream = (flow == eRender ? SND_PCM_STREAM_PLAYBACK :
528 SND_PCM_STREAM_CAPTURE);
529 int err, card;
531 card = -1;
532 *num = 0;
534 if(alsa_try_open(defname, stream)){
535 *ids = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR *));
536 (*ids)[0] = construct_device_id(flow, defaultW, NULL);
537 *guids = HeapAlloc(GetProcessHeap(), 0, sizeof(GUID));
538 get_device_guid(flow, defname, &(*guids)[0]);
539 ++*num;
542 get_reg_devices(flow, stream, ids, guids, num);
544 for(err = snd_card_next(&card); card != -1 && err >= 0;
545 err = snd_card_next(&card)){
546 char cardpath[64];
547 char *cardname;
548 WCHAR *cardnameW;
549 snd_ctl_t *ctl;
550 DWORD len;
552 sprintf(cardpath, "hw:%u", card);
554 if((err = snd_ctl_open(&ctl, cardpath, 0)) < 0){
555 WARN("Unable to open ctl for ALSA device %s: %d (%s)\n", cardpath,
556 err, snd_strerror(err));
557 continue;
560 if(snd_card_get_name(card, &cardname) < 0) {
561 /* FIXME: Should be localized */
562 static const WCHAR nameW[] = {'U','n','k','n','o','w','n',' ','s','o','u','n','d','c','a','r','d',0};
563 WARN("Unable to get card name for ALSA device %s: %d (%s)\n",
564 cardpath, err, snd_strerror(err));
565 alsa_get_card_devices(flow, stream, ids, guids, num, ctl, card, nameW);
566 }else{
567 len = MultiByteToWideChar(CP_UNIXCP, 0, cardname, -1, NULL, 0);
568 cardnameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
570 if(!cardnameW){
571 free(cardname);
572 snd_ctl_close(ctl);
573 return E_OUTOFMEMORY;
575 MultiByteToWideChar(CP_UNIXCP, 0, cardname, -1, cardnameW, len);
577 alsa_get_card_devices(flow, stream, ids, guids, num, ctl, card, cardnameW);
579 HeapFree(GetProcessHeap(), 0, cardnameW);
580 free(cardname);
583 snd_ctl_close(ctl);
586 if(err != 0)
587 WARN("Got a failure during card enumeration: %d (%s)\n",
588 err, snd_strerror(err));
590 return S_OK;
593 HRESULT WINAPI AUDDRV_GetEndpointIDs(EDataFlow flow, WCHAR ***ids, GUID **guids,
594 UINT *num, UINT *def_index)
596 HRESULT hr;
598 TRACE("%d %p %p %p %p\n", flow, ids, guids, num, def_index);
600 *ids = NULL;
601 *guids = NULL;
603 hr = alsa_enum_devices(flow, ids, guids, num);
604 if(FAILED(hr)){
605 UINT i;
606 for(i = 0; i < *num; ++i)
607 HeapFree(GetProcessHeap(), 0, (*ids)[i]);
608 HeapFree(GetProcessHeap(), 0, *ids);
609 HeapFree(GetProcessHeap(), 0, *guids);
610 return E_OUTOFMEMORY;
613 TRACE("Enumerated %u devices\n", *num);
615 if(*num == 0){
616 HeapFree(GetProcessHeap(), 0, *ids);
617 *ids = NULL;
618 HeapFree(GetProcessHeap(), 0, *guids);
619 *guids = NULL;
622 *def_index = 0;
624 return S_OK;
627 /* Using the pulse PCM device from alsa-plugins 1.0.24 triggers a bug
628 * which causes audio to cease playing after a few minutes of playback.
629 * Setting handle_underrun=1 on pulse-backed ALSA devices seems to work
630 * around this issue. */
631 static snd_config_t *make_handle_underrun_config(const char *name)
633 snd_config_t *lconf, *dev_node, *hu_node, *type_node;
634 char dev_node_name[64];
635 const char *type_str;
636 int err;
638 snd_config_update();
640 if((err = snd_config_copy(&lconf, snd_config)) < 0){
641 WARN("snd_config_copy failed: %d (%s)\n", err, snd_strerror(err));
642 return NULL;
645 sprintf(dev_node_name, "pcm.%s", name);
646 err = snd_config_search(lconf, dev_node_name, &dev_node);
647 if(err == -ENOENT){
648 snd_config_delete(lconf);
649 return NULL;
651 if(err < 0){
652 snd_config_delete(lconf);
653 WARN("snd_config_search failed: %d (%s)\n", err, snd_strerror(err));
654 return NULL;
657 /* ALSA is extremely fragile. If it runs into a config setting it doesn't
658 * recognize, it tends to fail or assert. So we only want to inject
659 * handle_underrun=1 on devices that we know will recognize it. */
660 err = snd_config_search(dev_node, "type", &type_node);
661 if(err == -ENOENT){
662 snd_config_delete(lconf);
663 return NULL;
665 if(err < 0){
666 snd_config_delete(lconf);
667 WARN("snd_config_search failed: %d (%s)\n", err, snd_strerror(err));
668 return NULL;
671 if((err = snd_config_get_string(type_node, &type_str)) < 0){
672 snd_config_delete(lconf);
673 return NULL;
676 if(strcmp(type_str, "pulse") != 0){
677 snd_config_delete(lconf);
678 return NULL;
681 err = snd_config_search(dev_node, "handle_underrun", &hu_node);
682 if(err >= 0){
683 /* user already has an explicit handle_underrun setting, so don't
684 * use a local config */
685 snd_config_delete(lconf);
686 return NULL;
688 if(err != -ENOENT){
689 snd_config_delete(lconf);
690 WARN("snd_config_search failed: %d (%s)\n", err, snd_strerror(err));
691 return NULL;
694 if((err = snd_config_imake_integer(&hu_node, "handle_underrun", 1)) < 0){
695 snd_config_delete(lconf);
696 WARN("snd_config_imake_integer failed: %d (%s)\n", err,
697 snd_strerror(err));
698 return NULL;
701 if((err = snd_config_add(dev_node, hu_node)) < 0){
702 snd_config_delete(lconf);
703 WARN("snd_config_add failed: %d (%s)\n", err, snd_strerror(err));
704 return NULL;
707 return lconf;
710 static BOOL get_alsa_name_by_guid(GUID *guid, char *name, DWORD name_size, EDataFlow *flow)
712 HKEY devices_key;
713 UINT i = 0;
714 WCHAR key_name[256];
715 DWORD key_name_size;
717 if(RegOpenKeyExW(HKEY_CURRENT_USER, drv_key_devicesW, 0, KEY_READ, &devices_key) != ERROR_SUCCESS){
718 ERR("No devices found in registry?\n");
719 return FALSE;
722 while(1){
723 HKEY key;
724 DWORD size, type;
725 GUID reg_guid;
727 key_name_size = sizeof(key_name)/sizeof(WCHAR);
728 if(RegEnumKeyExW(devices_key, i++, key_name, &key_name_size, NULL,
729 NULL, NULL, NULL) != ERROR_SUCCESS)
730 break;
732 if(RegOpenKeyExW(devices_key, key_name, 0, KEY_READ, &key) != ERROR_SUCCESS){
733 WARN("Couldn't open key: %s\n", wine_dbgstr_w(key_name));
734 continue;
737 size = sizeof(reg_guid);
738 if(RegQueryValueExW(key, guidW, 0, &type,
739 (BYTE*)&reg_guid, &size) == ERROR_SUCCESS){
740 if(IsEqualGUID(&reg_guid, guid)){
741 RegCloseKey(key);
742 RegCloseKey(devices_key);
744 TRACE("Found matching device key: %s\n", wine_dbgstr_w(key_name));
746 if(key_name[0] == '0')
747 *flow = eRender;
748 else if(key_name[0] == '1')
749 *flow = eCapture;
750 else{
751 ERR("Unknown device type: %c\n", key_name[0]);
752 return FALSE;
755 WideCharToMultiByte(CP_UNIXCP, 0, key_name + 2, -1, name, name_size, NULL, NULL);
757 return TRUE;
761 RegCloseKey(key);
764 RegCloseKey(devices_key);
766 WARN("No matching device in registry for GUID %s\n", debugstr_guid(guid));
768 return FALSE;
771 HRESULT WINAPI AUDDRV_GetAudioEndpoint(GUID *guid, IMMDevice *dev, IAudioClient **out)
773 ACImpl *This;
774 int err;
775 snd_pcm_stream_t stream;
776 snd_config_t *lconf;
777 static BOOL handle_underrun = TRUE;
778 char alsa_name[256];
779 EDataFlow dataflow;
780 HRESULT hr;
782 TRACE("%s %p %p\n", debugstr_guid(guid), dev, out);
784 if(!get_alsa_name_by_guid(guid, alsa_name, sizeof(alsa_name), &dataflow))
785 return AUDCLNT_E_DEVICE_INVALIDATED;
787 This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(ACImpl));
788 if(!This)
789 return E_OUTOFMEMORY;
791 This->IAudioClient_iface.lpVtbl = &AudioClient_Vtbl;
792 This->IAudioRenderClient_iface.lpVtbl = &AudioRenderClient_Vtbl;
793 This->IAudioCaptureClient_iface.lpVtbl = &AudioCaptureClient_Vtbl;
794 This->IAudioClock_iface.lpVtbl = &AudioClock_Vtbl;
795 This->IAudioClock2_iface.lpVtbl = &AudioClock2_Vtbl;
796 This->IAudioStreamVolume_iface.lpVtbl = &AudioStreamVolume_Vtbl;
798 if(dataflow == eRender)
799 stream = SND_PCM_STREAM_PLAYBACK;
800 else if(dataflow == eCapture)
801 stream = SND_PCM_STREAM_CAPTURE;
802 else{
803 HeapFree(GetProcessHeap(), 0, This);
804 return E_UNEXPECTED;
807 hr = CoCreateFreeThreadedMarshaler((IUnknown *)&This->IAudioClient_iface,
808 (IUnknown **)&This->pUnkFTMarshal);
809 if (FAILED(hr)) {
810 HeapFree(GetProcessHeap(), 0, This);
811 return hr;
814 This->dataflow = dataflow;
815 if(handle_underrun && ((lconf = make_handle_underrun_config(alsa_name)))){
816 err = snd_pcm_open_lconf(&This->pcm_handle, alsa_name, stream, SND_PCM_NONBLOCK, lconf);
817 TRACE("Opening PCM device \"%s\" with handle_underrun: %d\n", alsa_name, err);
818 snd_config_delete(lconf);
819 /* Pulse <= 2010 returns EINVAL, it does not know handle_underrun. */
820 if(err == -EINVAL){
821 ERR_(winediag)("PulseAudio \"%s\" %d without handle_underrun. Audio may hang."
822 " Please upgrade to alsa_plugins >= 1.0.24\n", alsa_name, err);
823 handle_underrun = FALSE;
825 }else
826 err = -EINVAL;
827 if(err == -EINVAL){
828 err = snd_pcm_open(&This->pcm_handle, alsa_name, stream, SND_PCM_NONBLOCK);
830 if(err < 0){
831 HeapFree(GetProcessHeap(), 0, This);
832 WARN("Unable to open PCM \"%s\": %d (%s)\n", alsa_name, err, snd_strerror(err));
833 switch(err){
834 case -EBUSY:
835 return AUDCLNT_E_DEVICE_IN_USE;
836 default:
837 return AUDCLNT_E_ENDPOINT_CREATE_FAILED;
841 This->hw_params = HeapAlloc(GetProcessHeap(), 0,
842 snd_pcm_hw_params_sizeof());
843 if(!This->hw_params){
844 snd_pcm_close(This->pcm_handle);
845 HeapFree(GetProcessHeap(), 0, This);
846 return E_OUTOFMEMORY;
849 InitializeCriticalSection(&This->lock);
850 This->lock.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": ACImpl.lock");
852 This->parent = dev;
853 IMMDevice_AddRef(This->parent);
855 *out = &This->IAudioClient_iface;
856 IAudioClient_AddRef(&This->IAudioClient_iface);
858 return S_OK;
861 static HRESULT WINAPI AudioClient_QueryInterface(IAudioClient *iface,
862 REFIID riid, void **ppv)
864 ACImpl *This = impl_from_IAudioClient(iface);
865 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
867 if(!ppv)
868 return E_POINTER;
869 *ppv = NULL;
870 if(IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IAudioClient))
871 *ppv = iface;
872 else if(IsEqualIID(riid, &IID_IMarshal))
873 return IUnknown_QueryInterface(This->pUnkFTMarshal, riid, ppv);
875 if(*ppv){
876 IUnknown_AddRef((IUnknown*)*ppv);
877 return S_OK;
879 WARN("Unknown interface %s\n", debugstr_guid(riid));
880 return E_NOINTERFACE;
883 static ULONG WINAPI AudioClient_AddRef(IAudioClient *iface)
885 ACImpl *This = impl_from_IAudioClient(iface);
886 ULONG ref;
887 ref = InterlockedIncrement(&This->ref);
888 TRACE("(%p) Refcount now %u\n", This, ref);
889 return ref;
892 static ULONG WINAPI AudioClient_Release(IAudioClient *iface)
894 ACImpl *This = impl_from_IAudioClient(iface);
895 ULONG ref;
897 ref = InterlockedDecrement(&This->ref);
898 TRACE("(%p) Refcount now %u\n", This, ref);
899 if(!ref){
900 if(This->timer){
901 HANDLE event;
902 DWORD wait;
903 event = CreateEventW(NULL, TRUE, FALSE, NULL);
904 wait = !DeleteTimerQueueTimer(g_timer_q, This->timer, event);
905 wait = wait && GetLastError() == ERROR_IO_PENDING;
906 if(event && wait)
907 WaitForSingleObject(event, INFINITE);
908 CloseHandle(event);
911 IAudioClient_Stop(iface);
912 IMMDevice_Release(This->parent);
913 IUnknown_Release(This->pUnkFTMarshal);
914 This->lock.DebugInfo->Spare[0] = 0;
915 DeleteCriticalSection(&This->lock);
916 snd_pcm_drop(This->pcm_handle);
917 snd_pcm_close(This->pcm_handle);
918 if(This->initted){
919 EnterCriticalSection(&g_sessions_lock);
920 list_remove(&This->entry);
921 LeaveCriticalSection(&g_sessions_lock);
923 HeapFree(GetProcessHeap(), 0, This->vols);
924 HeapFree(GetProcessHeap(), 0, This->local_buffer);
925 HeapFree(GetProcessHeap(), 0, This->remapping_buf);
926 HeapFree(GetProcessHeap(), 0, This->silence_buf);
927 HeapFree(GetProcessHeap(), 0, This->tmp_buffer);
928 HeapFree(GetProcessHeap(), 0, This->hw_params);
929 CoTaskMemFree(This->fmt);
930 HeapFree(GetProcessHeap(), 0, This);
932 return ref;
935 static void dump_fmt(const WAVEFORMATEX *fmt)
937 TRACE("wFormatTag: 0x%x (", fmt->wFormatTag);
938 switch(fmt->wFormatTag){
939 case WAVE_FORMAT_PCM:
940 TRACE("WAVE_FORMAT_PCM");
941 break;
942 case WAVE_FORMAT_IEEE_FLOAT:
943 TRACE("WAVE_FORMAT_IEEE_FLOAT");
944 break;
945 case WAVE_FORMAT_EXTENSIBLE:
946 TRACE("WAVE_FORMAT_EXTENSIBLE");
947 break;
948 default:
949 TRACE("Unknown");
950 break;
952 TRACE(")\n");
954 TRACE("nChannels: %u\n", fmt->nChannels);
955 TRACE("nSamplesPerSec: %u\n", fmt->nSamplesPerSec);
956 TRACE("nAvgBytesPerSec: %u\n", fmt->nAvgBytesPerSec);
957 TRACE("nBlockAlign: %u\n", fmt->nBlockAlign);
958 TRACE("wBitsPerSample: %u\n", fmt->wBitsPerSample);
959 TRACE("cbSize: %u\n", fmt->cbSize);
961 if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE){
962 WAVEFORMATEXTENSIBLE *fmtex = (void*)fmt;
963 TRACE("dwChannelMask: %08x\n", fmtex->dwChannelMask);
964 TRACE("Samples: %04x\n", fmtex->Samples.wReserved);
965 TRACE("SubFormat: %s\n", wine_dbgstr_guid(&fmtex->SubFormat));
969 static WAVEFORMATEX *clone_format(const WAVEFORMATEX *fmt)
971 WAVEFORMATEX *ret;
972 size_t size;
974 if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
975 size = sizeof(WAVEFORMATEXTENSIBLE);
976 else
977 size = sizeof(WAVEFORMATEX);
979 ret = CoTaskMemAlloc(size);
980 if(!ret)
981 return NULL;
983 memcpy(ret, fmt, size);
985 ret->cbSize = size - sizeof(WAVEFORMATEX);
987 return ret;
990 static snd_pcm_format_t alsa_format(const WAVEFORMATEX *fmt)
992 snd_pcm_format_t format = SND_PCM_FORMAT_UNKNOWN;
993 const WAVEFORMATEXTENSIBLE *fmtex = (const WAVEFORMATEXTENSIBLE *)fmt;
995 if(fmt->wFormatTag == WAVE_FORMAT_PCM ||
996 (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
997 IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))){
998 if(fmt->wBitsPerSample == 8)
999 format = SND_PCM_FORMAT_U8;
1000 else if(fmt->wBitsPerSample == 16)
1001 format = SND_PCM_FORMAT_S16_LE;
1002 else if(fmt->wBitsPerSample == 24)
1003 format = SND_PCM_FORMAT_S24_3LE;
1004 else if(fmt->wBitsPerSample == 32)
1005 format = SND_PCM_FORMAT_S32_LE;
1006 else
1007 WARN("Unsupported bit depth: %u\n", fmt->wBitsPerSample);
1008 if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
1009 fmt->wBitsPerSample != fmtex->Samples.wValidBitsPerSample){
1010 if(fmtex->Samples.wValidBitsPerSample == 20 && fmt->wBitsPerSample == 24)
1011 format = SND_PCM_FORMAT_S20_3LE;
1012 else
1013 WARN("Unsupported ValidBits: %u\n", fmtex->Samples.wValidBitsPerSample);
1015 }else if(fmt->wFormatTag == WAVE_FORMAT_IEEE_FLOAT ||
1016 (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
1017 IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT))){
1018 if(fmt->wBitsPerSample == 32)
1019 format = SND_PCM_FORMAT_FLOAT_LE;
1020 else if(fmt->wBitsPerSample == 64)
1021 format = SND_PCM_FORMAT_FLOAT64_LE;
1022 else
1023 WARN("Unsupported float size: %u\n", fmt->wBitsPerSample);
1024 }else
1025 WARN("Unknown wave format: %04x\n", fmt->wFormatTag);
1026 return format;
1029 static void session_init_vols(AudioSession *session, UINT channels)
1031 if(session->channel_count < channels){
1032 UINT i;
1034 if(session->channel_vols)
1035 session->channel_vols = HeapReAlloc(GetProcessHeap(), 0,
1036 session->channel_vols, sizeof(float) * channels);
1037 else
1038 session->channel_vols = HeapAlloc(GetProcessHeap(), 0,
1039 sizeof(float) * channels);
1040 if(!session->channel_vols)
1041 return;
1043 for(i = session->channel_count; i < channels; ++i)
1044 session->channel_vols[i] = 1.f;
1046 session->channel_count = channels;
1050 static AudioSession *create_session(const GUID *guid, IMMDevice *device,
1051 UINT num_channels)
1053 AudioSession *ret;
1055 ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(AudioSession));
1056 if(!ret)
1057 return NULL;
1059 memcpy(&ret->guid, guid, sizeof(GUID));
1061 ret->device = device;
1063 list_init(&ret->clients);
1065 list_add_head(&g_sessions, &ret->entry);
1067 InitializeCriticalSection(&ret->lock);
1068 ret->lock.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": AudioSession.lock");
1070 session_init_vols(ret, num_channels);
1072 ret->master_vol = 1.f;
1074 return ret;
1077 /* if channels == 0, then this will return or create a session with
1078 * matching dataflow and GUID. otherwise, channels must also match */
1079 static HRESULT get_audio_session(const GUID *sessionguid,
1080 IMMDevice *device, UINT channels, AudioSession **out)
1082 AudioSession *session;
1084 if(!sessionguid || IsEqualGUID(sessionguid, &GUID_NULL)){
1085 *out = create_session(&GUID_NULL, device, channels);
1086 if(!*out)
1087 return E_OUTOFMEMORY;
1089 return S_OK;
1092 *out = NULL;
1093 LIST_FOR_EACH_ENTRY(session, &g_sessions, AudioSession, entry){
1094 if(session->device == device &&
1095 IsEqualGUID(sessionguid, &session->guid)){
1096 session_init_vols(session, channels);
1097 *out = session;
1098 break;
1102 if(!*out){
1103 *out = create_session(sessionguid, device, channels);
1104 if(!*out)
1105 return E_OUTOFMEMORY;
1108 return S_OK;
1111 static int alsa_channel_index(DWORD flag)
1113 switch(flag){
1114 case SPEAKER_FRONT_LEFT:
1115 return 0;
1116 case SPEAKER_FRONT_RIGHT:
1117 return 1;
1118 case SPEAKER_BACK_LEFT:
1119 return 2;
1120 case SPEAKER_BACK_RIGHT:
1121 return 3;
1122 case SPEAKER_FRONT_CENTER:
1123 return 4;
1124 case SPEAKER_LOW_FREQUENCY:
1125 return 5;
1126 case SPEAKER_SIDE_LEFT:
1127 return 6;
1128 case SPEAKER_SIDE_RIGHT:
1129 return 7;
1131 return -1;
1134 static BOOL need_remapping(ACImpl *This, const WAVEFORMATEX *fmt, int *map)
1136 unsigned int i;
1137 for(i = 0; i < fmt->nChannels; ++i){
1138 if(map[i] != i)
1139 return TRUE;
1141 return FALSE;
1144 static DWORD get_channel_mask(unsigned int channels)
1146 switch(channels){
1147 case 0:
1148 return 0;
1149 case 1:
1150 return KSAUDIO_SPEAKER_MONO;
1151 case 2:
1152 return KSAUDIO_SPEAKER_STEREO;
1153 case 3:
1154 return KSAUDIO_SPEAKER_STEREO | SPEAKER_LOW_FREQUENCY;
1155 case 4:
1156 return KSAUDIO_SPEAKER_QUAD; /* not _SURROUND */
1157 case 5:
1158 return KSAUDIO_SPEAKER_QUAD | SPEAKER_LOW_FREQUENCY;
1159 case 6:
1160 return KSAUDIO_SPEAKER_5POINT1; /* not 5POINT1_SURROUND */
1161 case 7:
1162 return KSAUDIO_SPEAKER_5POINT1 | SPEAKER_BACK_CENTER;
1163 case 8:
1164 return KSAUDIO_SPEAKER_7POINT1_SURROUND; /* Vista deprecates 7POINT1 */
1166 FIXME("Unknown speaker configuration: %u\n", channels);
1167 return 0;
1170 static HRESULT map_channels(ACImpl *This, const WAVEFORMATEX *fmt, int *alsa_channels, int *map)
1172 BOOL need_remap;
1174 if(This->dataflow != eCapture && (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE || fmt->nChannels > 2) ){
1175 WAVEFORMATEXTENSIBLE *fmtex = (void*)fmt;
1176 DWORD mask, flag = SPEAKER_FRONT_LEFT;
1177 UINT i = 0;
1179 if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
1180 fmtex->dwChannelMask != 0)
1181 mask = fmtex->dwChannelMask;
1182 else
1183 mask = get_channel_mask(fmt->nChannels);
1185 *alsa_channels = 0;
1187 while(i < fmt->nChannels && !(flag & SPEAKER_RESERVED)){
1188 if(mask & flag){
1189 map[i] = alsa_channel_index(flag);
1190 TRACE("Mapping mmdevapi channel %u (0x%x) to ALSA channel %d\n",
1191 i, flag, map[i]);
1192 if(map[i] >= *alsa_channels)
1193 *alsa_channels = map[i] + 1;
1194 ++i;
1196 flag <<= 1;
1199 while(i < fmt->nChannels){
1200 map[i] = *alsa_channels;
1201 TRACE("Mapping mmdevapi channel %u to ALSA channel %d\n",
1202 i, map[i]);
1203 ++*alsa_channels;
1204 ++i;
1207 for(i = 0; i < fmt->nChannels; ++i){
1208 if(map[i] == -1){
1209 map[i] = *alsa_channels;
1210 ++*alsa_channels;
1211 TRACE("Remapping mmdevapi channel %u to ALSA channel %d\n",
1212 i, map[i]);
1216 need_remap = need_remapping(This, fmt, map);
1217 }else{
1218 *alsa_channels = fmt->nChannels;
1220 need_remap = FALSE;
1223 TRACE("need_remapping: %u, alsa_channels: %d\n", need_remap, *alsa_channels);
1225 return need_remap ? S_OK : S_FALSE;
1228 static void silence_buffer(ACImpl *This, BYTE *buffer, UINT32 frames)
1230 WAVEFORMATEXTENSIBLE *fmtex = (WAVEFORMATEXTENSIBLE*)This->fmt;
1231 if((This->fmt->wFormatTag == WAVE_FORMAT_PCM ||
1232 (This->fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
1233 IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))) &&
1234 This->fmt->wBitsPerSample == 8)
1235 memset(buffer, 128, frames * This->fmt->nBlockAlign);
1236 else
1237 memset(buffer, 0, frames * This->fmt->nBlockAlign);
1240 static HRESULT WINAPI AudioClient_Initialize(IAudioClient *iface,
1241 AUDCLNT_SHAREMODE mode, DWORD flags, REFERENCE_TIME duration,
1242 REFERENCE_TIME period, const WAVEFORMATEX *fmt,
1243 const GUID *sessionguid)
1245 ACImpl *This = impl_from_IAudioClient(iface);
1246 snd_pcm_sw_params_t *sw_params = NULL;
1247 snd_pcm_format_t format;
1248 unsigned int rate, alsa_period_us;
1249 int err, i;
1250 HRESULT hr = S_OK;
1252 TRACE("(%p)->(%x, %x, %s, %s, %p, %s)\n", This, mode, flags,
1253 wine_dbgstr_longlong(duration), wine_dbgstr_longlong(period), fmt, debugstr_guid(sessionguid));
1255 if(!fmt)
1256 return E_POINTER;
1258 if(mode != AUDCLNT_SHAREMODE_SHARED && mode != AUDCLNT_SHAREMODE_EXCLUSIVE)
1259 return AUDCLNT_E_NOT_INITIALIZED;
1261 if(flags & ~(AUDCLNT_STREAMFLAGS_CROSSPROCESS |
1262 AUDCLNT_STREAMFLAGS_LOOPBACK |
1263 AUDCLNT_STREAMFLAGS_EVENTCALLBACK |
1264 AUDCLNT_STREAMFLAGS_NOPERSIST |
1265 AUDCLNT_STREAMFLAGS_RATEADJUST |
1266 AUDCLNT_SESSIONFLAGS_EXPIREWHENUNOWNED |
1267 AUDCLNT_SESSIONFLAGS_DISPLAY_HIDE |
1268 AUDCLNT_SESSIONFLAGS_DISPLAY_HIDEWHENEXPIRED)){
1269 TRACE("Unknown flags: %08x\n", flags);
1270 return E_INVALIDARG;
1273 if(mode == AUDCLNT_SHAREMODE_SHARED){
1274 period = DefaultPeriod;
1275 if( duration < 3 * period)
1276 duration = 3 * period;
1277 }else{
1278 if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE){
1279 if(((WAVEFORMATEXTENSIBLE*)fmt)->dwChannelMask == 0 ||
1280 ((WAVEFORMATEXTENSIBLE*)fmt)->dwChannelMask & SPEAKER_RESERVED)
1281 return AUDCLNT_E_UNSUPPORTED_FORMAT;
1284 if(!period)
1285 period = DefaultPeriod; /* not minimum */
1286 if(period < MinimumPeriod || period > 5000000)
1287 return AUDCLNT_E_INVALID_DEVICE_PERIOD;
1288 if(duration > 20000000) /* the smaller the period, the lower this limit */
1289 return AUDCLNT_E_BUFFER_SIZE_ERROR;
1290 if(flags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK){
1291 if(duration != period)
1292 return AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL;
1293 FIXME("EXCLUSIVE mode with EVENTCALLBACK\n");
1294 return AUDCLNT_E_DEVICE_IN_USE;
1295 }else{
1296 if( duration < 8 * period)
1297 duration = 8 * period; /* may grow above 2s */
1301 EnterCriticalSection(&This->lock);
1303 if(This->initted){
1304 LeaveCriticalSection(&This->lock);
1305 return AUDCLNT_E_ALREADY_INITIALIZED;
1308 dump_fmt(fmt);
1310 This->need_remapping = map_channels(This, fmt, &This->alsa_channels, This->alsa_channel_map) == S_OK;
1312 if((err = snd_pcm_hw_params_any(This->pcm_handle, This->hw_params)) < 0){
1313 WARN("Unable to get hw_params: %d (%s)\n", err, snd_strerror(err));
1314 hr = AUDCLNT_E_ENDPOINT_CREATE_FAILED;
1315 goto exit;
1318 if((err = snd_pcm_hw_params_set_access(This->pcm_handle, This->hw_params,
1319 SND_PCM_ACCESS_RW_INTERLEAVED)) < 0){
1320 WARN("Unable to set access: %d (%s)\n", err, snd_strerror(err));
1321 hr = AUDCLNT_E_ENDPOINT_CREATE_FAILED;
1322 goto exit;
1325 format = alsa_format(fmt);
1326 if (format == SND_PCM_FORMAT_UNKNOWN){
1327 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
1328 goto exit;
1331 if((err = snd_pcm_hw_params_set_format(This->pcm_handle, This->hw_params,
1332 format)) < 0){
1333 WARN("Unable to set ALSA format to %u: %d (%s)\n", format, err,
1334 snd_strerror(err));
1335 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
1336 goto exit;
1339 This->alsa_format = format;
1341 rate = fmt->nSamplesPerSec;
1342 if((err = snd_pcm_hw_params_set_rate_near(This->pcm_handle, This->hw_params,
1343 &rate, NULL)) < 0){
1344 WARN("Unable to set rate to %u: %d (%s)\n", rate, err,
1345 snd_strerror(err));
1346 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
1347 goto exit;
1350 if((err = snd_pcm_hw_params_set_channels(This->pcm_handle, This->hw_params,
1351 This->alsa_channels)) < 0){
1352 WARN("Unable to set channels to %u: %d (%s)\n", fmt->nChannels, err,
1353 snd_strerror(err));
1354 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
1355 goto exit;
1358 This->mmdev_period_rt = period;
1359 alsa_period_us = This->mmdev_period_rt / 10;
1360 if((err = snd_pcm_hw_params_set_period_time_near(This->pcm_handle,
1361 This->hw_params, &alsa_period_us, NULL)) < 0)
1362 WARN("Unable to set period time near %u: %d (%s)\n", alsa_period_us,
1363 err, snd_strerror(err));
1364 /* ALSA updates the output variable alsa_period_us */
1366 This->mmdev_period_frames = MulDiv(fmt->nSamplesPerSec,
1367 This->mmdev_period_rt, 10000000);
1369 /* Buffer 4 ALSA periods if large enough, else 4 mmdevapi periods */
1370 This->alsa_bufsize_frames = This->mmdev_period_frames * 4;
1371 if(err < 0 || alsa_period_us < period / 10)
1372 err = snd_pcm_hw_params_set_buffer_size_near(This->pcm_handle,
1373 This->hw_params, &This->alsa_bufsize_frames);
1374 else{
1375 unsigned int periods = 4;
1376 err = snd_pcm_hw_params_set_periods_near(This->pcm_handle, This->hw_params, &periods, NULL);
1378 if(err < 0)
1379 WARN("Unable to set buffer size: %d (%s)\n", err, snd_strerror(err));
1381 if((err = snd_pcm_hw_params(This->pcm_handle, This->hw_params)) < 0){
1382 WARN("Unable to set hw params: %d (%s)\n", err, snd_strerror(err));
1383 hr = AUDCLNT_E_ENDPOINT_CREATE_FAILED;
1384 goto exit;
1387 if((err = snd_pcm_hw_params_get_period_size(This->hw_params,
1388 &This->alsa_period_frames, NULL)) < 0){
1389 WARN("Unable to get period size: %d (%s)\n", err, snd_strerror(err));
1390 hr = AUDCLNT_E_ENDPOINT_CREATE_FAILED;
1391 goto exit;
1394 if((err = snd_pcm_hw_params_get_buffer_size(This->hw_params,
1395 &This->alsa_bufsize_frames)) < 0){
1396 WARN("Unable to get buffer size: %d (%s)\n", err, snd_strerror(err));
1397 hr = AUDCLNT_E_ENDPOINT_CREATE_FAILED;
1398 goto exit;
1401 sw_params = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, snd_pcm_sw_params_sizeof());
1402 if(!sw_params){
1403 hr = E_OUTOFMEMORY;
1404 goto exit;
1407 if((err = snd_pcm_sw_params_current(This->pcm_handle, sw_params)) < 0){
1408 WARN("Unable to get sw_params: %d (%s)\n", err, snd_strerror(err));
1409 hr = AUDCLNT_E_ENDPOINT_CREATE_FAILED;
1410 goto exit;
1413 if((err = snd_pcm_sw_params_set_start_threshold(This->pcm_handle,
1414 sw_params, 1)) < 0){
1415 WARN("Unable set start threshold to 1: %d (%s)\n", err, snd_strerror(err));
1416 hr = AUDCLNT_E_ENDPOINT_CREATE_FAILED;
1417 goto exit;
1420 if((err = snd_pcm_sw_params_set_stop_threshold(This->pcm_handle,
1421 sw_params, This->alsa_bufsize_frames)) < 0){
1422 WARN("Unable set stop threshold to %lu: %d (%s)\n",
1423 This->alsa_bufsize_frames, err, snd_strerror(err));
1424 hr = AUDCLNT_E_ENDPOINT_CREATE_FAILED;
1425 goto exit;
1428 if((err = snd_pcm_sw_params(This->pcm_handle, sw_params)) < 0){
1429 WARN("Unable to set sw params: %d (%s)\n", err, snd_strerror(err));
1430 hr = AUDCLNT_E_ENDPOINT_CREATE_FAILED;
1431 goto exit;
1434 if((err = snd_pcm_prepare(This->pcm_handle)) < 0){
1435 WARN("Unable to prepare device: %d (%s)\n", err, snd_strerror(err));
1436 hr = AUDCLNT_E_ENDPOINT_CREATE_FAILED;
1437 goto exit;
1440 /* Bear in mind weird situations where
1441 * ALSA period (50ms) > mmdevapi buffer (3x10ms)
1442 * or surprising rounding as seen with 22050x8x1 with Pulse:
1443 * ALSA period 220 vs. 221 frames in mmdevapi and
1444 * buffer 883 vs. 2205 frames in mmdevapi! */
1445 This->bufsize_frames = MulDiv(duration, fmt->nSamplesPerSec, 10000000);
1446 if(mode == AUDCLNT_SHAREMODE_EXCLUSIVE)
1447 This->bufsize_frames -= This->bufsize_frames % This->mmdev_period_frames;
1448 This->hidden_frames = This->alsa_period_frames + This->mmdev_period_frames +
1449 MulDiv(fmt->nSamplesPerSec, EXTRA_SAFE_RT, 10000000);
1450 /* leave no less than about 1.33ms or 256 bytes of data after a rewind */
1451 This->safe_rewind_frames = max(256 / fmt->nBlockAlign, MulDiv(133, fmt->nSamplesPerSec, 100000));
1453 /* Check if the ALSA buffer is so small that it will run out before
1454 * the next MMDevAPI period tick occurs. Allow a little wiggle room
1455 * with 120% of the period time. */
1456 if(This->alsa_bufsize_frames < 1.2 * This->mmdev_period_frames)
1457 FIXME("ALSA buffer time is too small. Expect underruns. (%lu < %u * 1.2)\n",
1458 This->alsa_bufsize_frames, This->mmdev_period_frames);
1460 This->fmt = clone_format(fmt);
1461 if(!This->fmt){
1462 hr = E_OUTOFMEMORY;
1463 goto exit;
1466 This->local_buffer = HeapAlloc(GetProcessHeap(), 0,
1467 This->bufsize_frames * fmt->nBlockAlign);
1468 if(!This->local_buffer){
1469 hr = E_OUTOFMEMORY;
1470 goto exit;
1472 silence_buffer(This, This->local_buffer, This->bufsize_frames);
1474 This->silence_buf = HeapAlloc(GetProcessHeap(), 0,
1475 This->alsa_period_frames * This->fmt->nBlockAlign);
1476 if(!This->silence_buf){
1477 hr = E_OUTOFMEMORY;
1478 goto exit;
1480 silence_buffer(This, This->silence_buf, This->alsa_period_frames);
1482 This->vols = HeapAlloc(GetProcessHeap(), 0, fmt->nChannels * sizeof(float));
1483 if(!This->vols){
1484 hr = E_OUTOFMEMORY;
1485 goto exit;
1488 for(i = 0; i < fmt->nChannels; ++i)
1489 This->vols[i] = 1.f;
1491 This->share = mode;
1492 This->flags = flags;
1494 EnterCriticalSection(&g_sessions_lock);
1496 hr = get_audio_session(sessionguid, This->parent, fmt->nChannels,
1497 &This->session);
1498 if(FAILED(hr)){
1499 LeaveCriticalSection(&g_sessions_lock);
1500 goto exit;
1503 list_add_tail(&This->session->clients, &This->entry);
1505 LeaveCriticalSection(&g_sessions_lock);
1507 This->initted = TRUE;
1509 TRACE("ALSA period: %lu frames\n", This->alsa_period_frames);
1510 TRACE("ALSA buffer: %lu frames\n", This->alsa_bufsize_frames);
1511 TRACE("MMDevice period: %u frames\n", This->mmdev_period_frames);
1512 TRACE("MMDevice buffer: %u frames\n", This->bufsize_frames);
1514 exit:
1515 HeapFree(GetProcessHeap(), 0, sw_params);
1516 if(FAILED(hr)){
1517 HeapFree(GetProcessHeap(), 0, This->local_buffer);
1518 This->local_buffer = NULL;
1519 CoTaskMemFree(This->fmt);
1520 This->fmt = NULL;
1521 HeapFree(GetProcessHeap(), 0, This->vols);
1522 This->vols = NULL;
1525 LeaveCriticalSection(&This->lock);
1527 return hr;
1530 static HRESULT WINAPI AudioClient_GetBufferSize(IAudioClient *iface,
1531 UINT32 *out)
1533 ACImpl *This = impl_from_IAudioClient(iface);
1535 TRACE("(%p)->(%p)\n", This, out);
1537 if(!out)
1538 return E_POINTER;
1540 EnterCriticalSection(&This->lock);
1542 if(!This->initted){
1543 LeaveCriticalSection(&This->lock);
1544 return AUDCLNT_E_NOT_INITIALIZED;
1547 *out = This->bufsize_frames;
1549 LeaveCriticalSection(&This->lock);
1551 return S_OK;
1554 static HRESULT WINAPI AudioClient_GetStreamLatency(IAudioClient *iface,
1555 REFERENCE_TIME *latency)
1557 ACImpl *This = impl_from_IAudioClient(iface);
1559 TRACE("(%p)->(%p)\n", This, latency);
1561 if(!latency)
1562 return E_POINTER;
1564 EnterCriticalSection(&This->lock);
1566 if(!This->initted){
1567 LeaveCriticalSection(&This->lock);
1568 return AUDCLNT_E_NOT_INITIALIZED;
1571 /* Hide some frames in the ALSA buffer. Allows us to return GetCurrentPadding=0
1572 * yet have enough data left to play (as if it were in native's mixer). Add:
1573 * + mmdevapi_period such that at the end of it, ALSA still has data;
1574 * + EXTRA_SAFE (~4ms) to allow for late callback invocation / fluctuation;
1575 * + alsa_period such that ALSA always has at least one period to play. */
1576 if(This->dataflow == eRender)
1577 *latency = MulDiv(This->hidden_frames, 10000000, This->fmt->nSamplesPerSec);
1578 else
1579 *latency = MulDiv(This->alsa_period_frames, 10000000, This->fmt->nSamplesPerSec)
1580 + This->mmdev_period_rt;
1582 LeaveCriticalSection(&This->lock);
1584 return S_OK;
1587 static HRESULT WINAPI AudioClient_GetCurrentPadding(IAudioClient *iface,
1588 UINT32 *out)
1590 ACImpl *This = impl_from_IAudioClient(iface);
1592 TRACE("(%p)->(%p)\n", This, out);
1594 if(!out)
1595 return E_POINTER;
1597 EnterCriticalSection(&This->lock);
1599 if(!This->initted){
1600 LeaveCriticalSection(&This->lock);
1601 return AUDCLNT_E_NOT_INITIALIZED;
1604 /* padding is solely updated at callback time in shared mode */
1605 *out = This->held_frames;
1607 LeaveCriticalSection(&This->lock);
1609 TRACE("pad: %u\n", *out);
1611 return S_OK;
1614 static HRESULT WINAPI AudioClient_IsFormatSupported(IAudioClient *iface,
1615 AUDCLNT_SHAREMODE mode, const WAVEFORMATEX *fmt,
1616 WAVEFORMATEX **out)
1618 ACImpl *This = impl_from_IAudioClient(iface);
1619 snd_pcm_format_mask_t *formats = NULL;
1620 snd_pcm_format_t format;
1621 HRESULT hr = S_OK;
1622 WAVEFORMATEX *closest = NULL;
1623 unsigned int max = 0, min = 0;
1624 int err;
1625 int alsa_channels, alsa_channel_map[32];
1627 TRACE("(%p)->(%x, %p, %p)\n", This, mode, fmt, out);
1629 if(!fmt || (mode == AUDCLNT_SHAREMODE_SHARED && !out))
1630 return E_POINTER;
1632 if(mode != AUDCLNT_SHAREMODE_SHARED && mode != AUDCLNT_SHAREMODE_EXCLUSIVE)
1633 return E_INVALIDARG;
1635 if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
1636 fmt->cbSize < sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX))
1637 return E_INVALIDARG;
1639 dump_fmt(fmt);
1641 if(out){
1642 *out = NULL;
1643 if(mode != AUDCLNT_SHAREMODE_SHARED)
1644 out = NULL;
1647 if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
1648 (fmt->nAvgBytesPerSec == 0 ||
1649 fmt->nBlockAlign == 0 ||
1650 ((WAVEFORMATEXTENSIBLE*)fmt)->Samples.wValidBitsPerSample > fmt->wBitsPerSample))
1651 return E_INVALIDARG;
1653 if(fmt->nChannels == 0)
1654 return AUDCLNT_E_UNSUPPORTED_FORMAT;
1656 EnterCriticalSection(&This->lock);
1658 if((err = snd_pcm_hw_params_any(This->pcm_handle, This->hw_params)) < 0){
1659 hr = AUDCLNT_E_DEVICE_INVALIDATED;
1660 goto exit;
1663 formats = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
1664 snd_pcm_format_mask_sizeof());
1665 if(!formats){
1666 hr = E_OUTOFMEMORY;
1667 goto exit;
1670 snd_pcm_hw_params_get_format_mask(This->hw_params, formats);
1671 format = alsa_format(fmt);
1672 if (format == SND_PCM_FORMAT_UNKNOWN ||
1673 !snd_pcm_format_mask_test(formats, format)){
1674 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
1675 goto exit;
1678 closest = clone_format(fmt);
1679 if(!closest){
1680 hr = E_OUTOFMEMORY;
1681 goto exit;
1684 if((err = snd_pcm_hw_params_get_rate_min(This->hw_params, &min, NULL)) < 0){
1685 hr = AUDCLNT_E_DEVICE_INVALIDATED;
1686 WARN("Unable to get min rate: %d (%s)\n", err, snd_strerror(err));
1687 goto exit;
1690 if((err = snd_pcm_hw_params_get_rate_max(This->hw_params, &max, NULL)) < 0){
1691 hr = AUDCLNT_E_DEVICE_INVALIDATED;
1692 WARN("Unable to get max rate: %d (%s)\n", err, snd_strerror(err));
1693 goto exit;
1696 if(fmt->nSamplesPerSec < min || fmt->nSamplesPerSec > max){
1697 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
1698 goto exit;
1701 if((err = snd_pcm_hw_params_get_channels_min(This->hw_params, &min)) < 0){
1702 hr = AUDCLNT_E_DEVICE_INVALIDATED;
1703 WARN("Unable to get min channels: %d (%s)\n", err, snd_strerror(err));
1704 goto exit;
1707 if((err = snd_pcm_hw_params_get_channels_max(This->hw_params, &max)) < 0){
1708 hr = AUDCLNT_E_DEVICE_INVALIDATED;
1709 WARN("Unable to get max channels: %d (%s)\n", err, snd_strerror(err));
1710 goto exit;
1712 if(fmt->nChannels > max){
1713 hr = S_FALSE;
1714 closest->nChannels = max;
1715 }else if(fmt->nChannels < min){
1716 hr = S_FALSE;
1717 closest->nChannels = min;
1720 map_channels(This, fmt, &alsa_channels, alsa_channel_map);
1722 if(alsa_channels > max){
1723 hr = S_FALSE;
1724 closest->nChannels = max;
1727 if(closest->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
1728 ((WAVEFORMATEXTENSIBLE*)closest)->dwChannelMask = get_channel_mask(closest->nChannels);
1730 if(fmt->nBlockAlign != fmt->nChannels * fmt->wBitsPerSample / 8 ||
1731 fmt->nAvgBytesPerSec != fmt->nBlockAlign * fmt->nSamplesPerSec ||
1732 (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
1733 ((WAVEFORMATEXTENSIBLE*)fmt)->Samples.wValidBitsPerSample < fmt->wBitsPerSample))
1734 hr = S_FALSE;
1736 if(mode == AUDCLNT_SHAREMODE_EXCLUSIVE &&
1737 fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE){
1738 if(((WAVEFORMATEXTENSIBLE*)fmt)->dwChannelMask == 0 ||
1739 ((WAVEFORMATEXTENSIBLE*)fmt)->dwChannelMask & SPEAKER_RESERVED)
1740 hr = S_FALSE;
1743 exit:
1744 LeaveCriticalSection(&This->lock);
1745 HeapFree(GetProcessHeap(), 0, formats);
1747 if(hr == S_FALSE && !out)
1748 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
1750 if(hr == S_FALSE && out) {
1751 closest->nBlockAlign =
1752 closest->nChannels * closest->wBitsPerSample / 8;
1753 closest->nAvgBytesPerSec =
1754 closest->nBlockAlign * closest->nSamplesPerSec;
1755 if(closest->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
1756 ((WAVEFORMATEXTENSIBLE*)closest)->Samples.wValidBitsPerSample = closest->wBitsPerSample;
1757 *out = closest;
1758 } else
1759 CoTaskMemFree(closest);
1761 TRACE("returning: %08x\n", hr);
1762 return hr;
1765 static HRESULT WINAPI AudioClient_GetMixFormat(IAudioClient *iface,
1766 WAVEFORMATEX **pwfx)
1768 ACImpl *This = impl_from_IAudioClient(iface);
1769 WAVEFORMATEXTENSIBLE *fmt;
1770 snd_pcm_format_mask_t *formats;
1771 unsigned int max_rate, max_channels;
1772 int err;
1773 HRESULT hr = S_OK;
1775 TRACE("(%p)->(%p)\n", This, pwfx);
1777 if(!pwfx)
1778 return E_POINTER;
1779 *pwfx = NULL;
1781 fmt = CoTaskMemAlloc(sizeof(WAVEFORMATEXTENSIBLE));
1782 if(!fmt)
1783 return E_OUTOFMEMORY;
1785 formats = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, snd_pcm_format_mask_sizeof());
1786 if(!formats){
1787 CoTaskMemFree(fmt);
1788 return E_OUTOFMEMORY;
1791 EnterCriticalSection(&This->lock);
1793 if((err = snd_pcm_hw_params_any(This->pcm_handle, This->hw_params)) < 0){
1794 WARN("Unable to get hw_params: %d (%s)\n", err, snd_strerror(err));
1795 hr = AUDCLNT_E_DEVICE_INVALIDATED;
1796 goto exit;
1799 snd_pcm_hw_params_get_format_mask(This->hw_params, formats);
1801 fmt->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
1802 if(snd_pcm_format_mask_test(formats, SND_PCM_FORMAT_FLOAT_LE)){
1803 fmt->Format.wBitsPerSample = 32;
1804 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
1805 }else if(snd_pcm_format_mask_test(formats, SND_PCM_FORMAT_S16_LE)){
1806 fmt->Format.wBitsPerSample = 16;
1807 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1808 }else if(snd_pcm_format_mask_test(formats, SND_PCM_FORMAT_U8)){
1809 fmt->Format.wBitsPerSample = 8;
1810 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1811 }else if(snd_pcm_format_mask_test(formats, SND_PCM_FORMAT_S32_LE)){
1812 fmt->Format.wBitsPerSample = 32;
1813 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1814 }else if(snd_pcm_format_mask_test(formats, SND_PCM_FORMAT_S24_3LE)){
1815 fmt->Format.wBitsPerSample = 24;
1816 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1817 }else{
1818 ERR("Didn't recognize any available ALSA formats\n");
1819 hr = AUDCLNT_E_DEVICE_INVALIDATED;
1820 goto exit;
1823 if((err = snd_pcm_hw_params_get_channels_max(This->hw_params,
1824 &max_channels)) < 0){
1825 WARN("Unable to get max channels: %d (%s)\n", err, snd_strerror(err));
1826 hr = AUDCLNT_E_DEVICE_INVALIDATED;
1827 goto exit;
1830 if(max_channels > 6)
1831 fmt->Format.nChannels = 2;
1832 else
1833 fmt->Format.nChannels = max_channels;
1835 fmt->dwChannelMask = get_channel_mask(fmt->Format.nChannels);
1837 if((err = snd_pcm_hw_params_get_rate_max(This->hw_params, &max_rate,
1838 NULL)) < 0){
1839 WARN("Unable to get max rate: %d (%s)\n", err, snd_strerror(err));
1840 hr = AUDCLNT_E_DEVICE_INVALIDATED;
1841 goto exit;
1844 if(max_rate >= 48000)
1845 fmt->Format.nSamplesPerSec = 48000;
1846 else if(max_rate >= 44100)
1847 fmt->Format.nSamplesPerSec = 44100;
1848 else if(max_rate >= 22050)
1849 fmt->Format.nSamplesPerSec = 22050;
1850 else if(max_rate >= 11025)
1851 fmt->Format.nSamplesPerSec = 11025;
1852 else if(max_rate >= 8000)
1853 fmt->Format.nSamplesPerSec = 8000;
1854 else{
1855 ERR("Unknown max rate: %u\n", max_rate);
1856 hr = AUDCLNT_E_DEVICE_INVALIDATED;
1857 goto exit;
1860 fmt->Format.nBlockAlign = (fmt->Format.wBitsPerSample *
1861 fmt->Format.nChannels) / 8;
1862 fmt->Format.nAvgBytesPerSec = fmt->Format.nSamplesPerSec *
1863 fmt->Format.nBlockAlign;
1865 fmt->Samples.wValidBitsPerSample = fmt->Format.wBitsPerSample;
1866 fmt->Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
1868 dump_fmt((WAVEFORMATEX*)fmt);
1869 *pwfx = (WAVEFORMATEX*)fmt;
1871 exit:
1872 LeaveCriticalSection(&This->lock);
1873 if(FAILED(hr))
1874 CoTaskMemFree(fmt);
1875 HeapFree(GetProcessHeap(), 0, formats);
1877 return hr;
1880 static HRESULT WINAPI AudioClient_GetDevicePeriod(IAudioClient *iface,
1881 REFERENCE_TIME *defperiod, REFERENCE_TIME *minperiod)
1883 ACImpl *This = impl_from_IAudioClient(iface);
1885 TRACE("(%p)->(%p, %p)\n", This, defperiod, minperiod);
1887 if(!defperiod && !minperiod)
1888 return E_POINTER;
1890 if(defperiod)
1891 *defperiod = DefaultPeriod;
1892 if(minperiod)
1893 *minperiod = DefaultPeriod;
1895 return S_OK;
1898 static BYTE *remap_channels(ACImpl *This, BYTE *buf, snd_pcm_uframes_t frames)
1900 snd_pcm_uframes_t i;
1901 UINT c;
1902 UINT bytes_per_sample = This->fmt->wBitsPerSample / 8;
1904 if(!This->need_remapping)
1905 return buf;
1907 if(!This->remapping_buf){
1908 This->remapping_buf = HeapAlloc(GetProcessHeap(), 0,
1909 bytes_per_sample * This->alsa_channels * frames);
1910 This->remapping_buf_frames = frames;
1911 }else if(This->remapping_buf_frames < frames){
1912 This->remapping_buf = HeapReAlloc(GetProcessHeap(), 0, This->remapping_buf,
1913 bytes_per_sample * This->alsa_channels * frames);
1914 This->remapping_buf_frames = frames;
1917 snd_pcm_format_set_silence(This->alsa_format, This->remapping_buf,
1918 frames * This->alsa_channels);
1920 switch(This->fmt->wBitsPerSample){
1921 case 8: {
1922 UINT8 *tgt_buf, *src_buf;
1923 tgt_buf = This->remapping_buf;
1924 src_buf = buf;
1925 for(i = 0; i < frames; ++i){
1926 for(c = 0; c < This->fmt->nChannels; ++c)
1927 tgt_buf[This->alsa_channel_map[c]] = src_buf[c];
1928 tgt_buf += This->alsa_channels;
1929 src_buf += This->fmt->nChannels;
1931 break;
1933 case 16: {
1934 UINT16 *tgt_buf, *src_buf;
1935 tgt_buf = (UINT16*)This->remapping_buf;
1936 src_buf = (UINT16*)buf;
1937 for(i = 0; i < frames; ++i){
1938 for(c = 0; c < This->fmt->nChannels; ++c)
1939 tgt_buf[This->alsa_channel_map[c]] = src_buf[c];
1940 tgt_buf += This->alsa_channels;
1941 src_buf += This->fmt->nChannels;
1944 break;
1945 case 32: {
1946 UINT32 *tgt_buf, *src_buf;
1947 tgt_buf = (UINT32*)This->remapping_buf;
1948 src_buf = (UINT32*)buf;
1949 for(i = 0; i < frames; ++i){
1950 for(c = 0; c < This->fmt->nChannels; ++c)
1951 tgt_buf[This->alsa_channel_map[c]] = src_buf[c];
1952 tgt_buf += This->alsa_channels;
1953 src_buf += This->fmt->nChannels;
1956 break;
1957 default: {
1958 BYTE *tgt_buf, *src_buf;
1959 tgt_buf = This->remapping_buf;
1960 src_buf = buf;
1961 for(i = 0; i < frames; ++i){
1962 for(c = 0; c < This->fmt->nChannels; ++c)
1963 memcpy(&tgt_buf[This->alsa_channel_map[c] * bytes_per_sample],
1964 &src_buf[c * bytes_per_sample], bytes_per_sample);
1965 tgt_buf += This->alsa_channels * bytes_per_sample;
1966 src_buf += This->fmt->nChannels * bytes_per_sample;
1969 break;
1972 return This->remapping_buf;
1975 static snd_pcm_sframes_t alsa_write_best_effort(ACImpl *This, BYTE *buf,
1976 snd_pcm_uframes_t frames, BOOL mute)
1978 snd_pcm_sframes_t written;
1980 if(mute){
1981 int err;
1982 if((err = snd_pcm_format_set_silence(This->alsa_format, buf,
1983 frames * This->fmt->nChannels)) < 0)
1984 WARN("Setting buffer to silence failed: %d (%s)\n", err,
1985 snd_strerror(err));
1988 buf = remap_channels(This, buf, frames);
1990 written = snd_pcm_writei(This->pcm_handle, buf, frames);
1991 if(written < 0){
1992 int ret;
1994 if(written == -EAGAIN)
1995 /* buffer full */
1996 return 0;
1998 WARN("writei failed, recovering: %ld (%s)\n", written,
1999 snd_strerror(written));
2001 ret = snd_pcm_recover(This->pcm_handle, written, 0);
2002 if(ret < 0){
2003 WARN("Could not recover: %d (%s)\n", ret, snd_strerror(ret));
2004 return ret;
2007 written = snd_pcm_writei(This->pcm_handle, buf, frames);
2010 return written;
2013 static snd_pcm_sframes_t alsa_write_buffer_wrap(ACImpl *This, BYTE *buf,
2014 snd_pcm_uframes_t buflen, snd_pcm_uframes_t offs,
2015 snd_pcm_uframes_t to_write)
2017 snd_pcm_sframes_t ret = 0;
2019 while(to_write){
2020 snd_pcm_uframes_t chunk;
2021 snd_pcm_sframes_t tmp;
2023 if(offs + to_write > buflen)
2024 chunk = buflen - offs;
2025 else
2026 chunk = to_write;
2028 tmp = alsa_write_best_effort(This, buf + offs * This->fmt->nBlockAlign, chunk, This->session->mute);
2029 if(tmp < 0)
2030 return ret;
2031 if(!tmp)
2032 break;
2034 ret += tmp;
2035 to_write -= tmp;
2036 offs += tmp;
2037 offs %= buflen;
2040 return ret;
2043 static UINT buf_ptr_diff(UINT left, UINT right, UINT bufsize)
2045 if(left <= right)
2046 return right - left;
2047 return bufsize - (left - right);
2050 static UINT data_not_in_alsa(ACImpl *This)
2052 UINT32 diff;
2054 diff = buf_ptr_diff(This->lcl_offs_frames, This->wri_offs_frames, This->bufsize_frames);
2055 if(diff)
2056 return diff;
2058 return This->held_frames - This->data_in_alsa_frames;
2060 /* Here's the buffer setup:
2062 * vvvvvvvv sent to HW already
2063 * vvvvvvvv in ALSA buffer but rewindable
2064 * [dddddddddddddddd] ALSA buffer
2065 * [dddddddddddddddd--------] mmdevapi buffer
2066 * ^^^^^^^^ data_in_alsa_frames
2067 * ^^^^^^^^^^^^^^^^ held_frames
2068 * ^ lcl_offs_frames
2069 * ^ wri_offs_frames
2071 * GetCurrentPadding is held_frames
2073 * During period callback, we decrement held_frames, fill ALSA buffer, and move
2074 * lcl_offs forward
2076 * During Stop, we rewind the ALSA buffer
2078 static void alsa_write_data(ACImpl *This)
2080 snd_pcm_sframes_t written;
2081 snd_pcm_uframes_t avail, max_copy_frames, data_frames_played;
2082 int err;
2084 /* this call seems to be required to get an accurate snd_pcm_state() */
2085 avail = snd_pcm_avail_update(This->pcm_handle);
2087 if(snd_pcm_state(This->pcm_handle) == SND_PCM_STATE_XRUN){
2088 TRACE("XRun state, recovering\n");
2090 avail = This->alsa_bufsize_frames;
2092 if((err = snd_pcm_recover(This->pcm_handle, -EPIPE, 1)) < 0)
2093 WARN("snd_pcm_recover failed: %d (%s)\n", err, snd_strerror(err));
2095 if((err = snd_pcm_reset(This->pcm_handle)) < 0)
2096 WARN("snd_pcm_reset failed: %d (%s)\n", err, snd_strerror(err));
2098 if((err = snd_pcm_prepare(This->pcm_handle)) < 0)
2099 WARN("snd_pcm_prepare failed: %d (%s)\n", err, snd_strerror(err));
2102 TRACE("avail: %ld\n", avail);
2104 /* Add a lead-in when starting with too few frames to ensure
2105 * continuous rendering. Additional benefit: Force ALSA to start. */
2106 if(This->data_in_alsa_frames == 0 && This->held_frames < This->alsa_period_frames)
2107 alsa_write_best_effort(This, This->silence_buf, This->alsa_period_frames - This->held_frames, FALSE);
2109 if(This->started)
2110 max_copy_frames = data_not_in_alsa(This);
2111 else
2112 max_copy_frames = 0;
2114 data_frames_played = min(This->data_in_alsa_frames, avail);
2115 This->data_in_alsa_frames -= data_frames_played;
2117 if(This->held_frames > data_frames_played){
2118 if(This->started)
2119 This->held_frames -= data_frames_played;
2120 }else
2121 This->held_frames = 0;
2123 while(avail && max_copy_frames){
2124 snd_pcm_uframes_t to_write;
2126 to_write = min(avail, max_copy_frames);
2128 written = alsa_write_buffer_wrap(This, This->local_buffer,
2129 This->bufsize_frames, This->lcl_offs_frames, to_write);
2130 if(written <= 0)
2131 break;
2133 avail -= written;
2134 This->lcl_offs_frames += written;
2135 This->lcl_offs_frames %= This->bufsize_frames;
2136 This->data_in_alsa_frames += written;
2137 max_copy_frames -= written;
2140 if(This->event)
2141 SetEvent(This->event);
2144 static void alsa_read_data(ACImpl *This)
2146 snd_pcm_sframes_t nread;
2147 UINT32 pos = This->wri_offs_frames, limit = This->held_frames;
2149 if(!This->started)
2150 goto exit;
2152 /* FIXME: Detect overrun and signal DATA_DISCONTINUITY
2153 * How to count overrun frames and report them as position increase? */
2154 limit = This->bufsize_frames - max(limit, pos);
2156 nread = snd_pcm_readi(This->pcm_handle,
2157 This->local_buffer + pos * This->fmt->nBlockAlign, limit);
2158 TRACE("read %ld from %u limit %u\n", nread, pos, limit);
2159 if(nread < 0){
2160 int ret;
2162 if(nread == -EAGAIN) /* no data yet */
2163 return;
2165 WARN("read failed, recovering: %ld (%s)\n", nread, snd_strerror(nread));
2167 ret = snd_pcm_recover(This->pcm_handle, nread, 0);
2168 if(ret < 0){
2169 WARN("Recover failed: %d (%s)\n", ret, snd_strerror(ret));
2170 return;
2173 nread = snd_pcm_readi(This->pcm_handle,
2174 This->local_buffer + pos * This->fmt->nBlockAlign, limit);
2175 if(nread < 0){
2176 WARN("read failed: %ld (%s)\n", nread, snd_strerror(nread));
2177 return;
2181 if(This->session->mute){
2182 int err;
2183 if((err = snd_pcm_format_set_silence(This->alsa_format,
2184 This->local_buffer + pos * This->fmt->nBlockAlign,
2185 nread)) < 0)
2186 WARN("Setting buffer to silence failed: %d (%s)\n", err,
2187 snd_strerror(err));
2190 This->wri_offs_frames += nread;
2191 This->wri_offs_frames %= This->bufsize_frames;
2192 This->held_frames += nread;
2194 exit:
2195 if(This->event)
2196 SetEvent(This->event);
2199 static void CALLBACK alsa_push_buffer_data(void *user, BOOLEAN timer)
2201 ACImpl *This = user;
2203 EnterCriticalSection(&This->lock);
2205 QueryPerformanceCounter(&This->last_period_time);
2207 if(This->dataflow == eRender)
2208 alsa_write_data(This);
2209 else if(This->dataflow == eCapture)
2210 alsa_read_data(This);
2212 LeaveCriticalSection(&This->lock);
2215 static snd_pcm_uframes_t interp_elapsed_frames(ACImpl *This)
2217 LARGE_INTEGER time_freq, current_time, time_diff;
2218 QueryPerformanceFrequency(&time_freq);
2219 QueryPerformanceCounter(&current_time);
2220 time_diff.QuadPart = current_time.QuadPart - This->last_period_time.QuadPart;
2221 return MulDiv(time_diff.QuadPart, This->fmt->nSamplesPerSec, time_freq.QuadPart);
2224 static int alsa_rewind_best_effort(ACImpl *This)
2226 snd_pcm_uframes_t len, leave;
2228 /* we can't use snd_pcm_rewindable, some PCM devices crash. so follow
2229 * PulseAudio's example and rewind as much data as we believe is in the
2230 * buffer, minus 1.33ms for safety. */
2232 /* amount of data to leave in ALSA buffer */
2233 leave = interp_elapsed_frames(This) + This->safe_rewind_frames;
2235 if(This->held_frames < leave)
2236 This->held_frames = 0;
2237 else
2238 This->held_frames -= leave;
2240 if(This->data_in_alsa_frames < leave)
2241 len = 0;
2242 else
2243 len = This->data_in_alsa_frames - leave;
2245 TRACE("rewinding %lu frames, now held %u\n", len, This->held_frames);
2247 if(len)
2248 /* snd_pcm_rewind return value is often broken, assume it succeeded */
2249 snd_pcm_rewind(This->pcm_handle, len);
2251 This->data_in_alsa_frames = 0;
2253 return len;
2256 static HRESULT WINAPI AudioClient_Start(IAudioClient *iface)
2258 ACImpl *This = impl_from_IAudioClient(iface);
2260 TRACE("(%p)\n", This);
2262 EnterCriticalSection(&This->lock);
2264 if(!This->initted){
2265 LeaveCriticalSection(&This->lock);
2266 return AUDCLNT_E_NOT_INITIALIZED;
2269 if((This->flags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK) && !This->event){
2270 LeaveCriticalSection(&This->lock);
2271 return AUDCLNT_E_EVENTHANDLE_NOT_SET;
2274 if(This->started){
2275 LeaveCriticalSection(&This->lock);
2276 return AUDCLNT_E_NOT_STOPPED;
2279 if(This->dataflow == eCapture){
2280 /* dump any data that might be leftover in the ALSA capture buffer */
2281 snd_pcm_readi(This->pcm_handle, This->local_buffer,
2282 This->bufsize_frames);
2283 }else{
2284 snd_pcm_sframes_t avail, written;
2285 snd_pcm_uframes_t offs;
2287 avail = snd_pcm_avail_update(This->pcm_handle);
2288 avail = min(avail, This->held_frames);
2290 if(This->wri_offs_frames < This->held_frames)
2291 offs = This->bufsize_frames - This->held_frames + This->wri_offs_frames;
2292 else
2293 offs = This->wri_offs_frames - This->held_frames;
2295 /* fill it with data */
2296 written = alsa_write_buffer_wrap(This, This->local_buffer,
2297 This->bufsize_frames, offs, avail);
2299 if(written > 0){
2300 This->lcl_offs_frames = (offs + written) % This->bufsize_frames;
2301 This->data_in_alsa_frames = written;
2302 }else{
2303 This->lcl_offs_frames = offs;
2304 This->data_in_alsa_frames = 0;
2308 if(!This->timer){
2309 if(!CreateTimerQueueTimer(&This->timer, g_timer_q, alsa_push_buffer_data,
2310 This, 0, This->mmdev_period_rt / 10000, WT_EXECUTEINTIMERTHREAD)){
2311 LeaveCriticalSection(&This->lock);
2312 WARN("Unable to create timer: %u\n", GetLastError());
2313 return E_OUTOFMEMORY;
2317 This->started = TRUE;
2319 LeaveCriticalSection(&This->lock);
2321 return S_OK;
2324 static HRESULT WINAPI AudioClient_Stop(IAudioClient *iface)
2326 ACImpl *This = impl_from_IAudioClient(iface);
2328 TRACE("(%p)\n", This);
2330 EnterCriticalSection(&This->lock);
2332 if(!This->initted){
2333 LeaveCriticalSection(&This->lock);
2334 return AUDCLNT_E_NOT_INITIALIZED;
2337 if(!This->started){
2338 LeaveCriticalSection(&This->lock);
2339 return S_FALSE;
2342 if(This->dataflow == eRender)
2343 alsa_rewind_best_effort(This);
2345 This->started = FALSE;
2347 LeaveCriticalSection(&This->lock);
2349 return S_OK;
2352 static HRESULT WINAPI AudioClient_Reset(IAudioClient *iface)
2354 ACImpl *This = impl_from_IAudioClient(iface);
2356 TRACE("(%p)\n", This);
2358 EnterCriticalSection(&This->lock);
2360 if(!This->initted){
2361 LeaveCriticalSection(&This->lock);
2362 return AUDCLNT_E_NOT_INITIALIZED;
2365 if(This->started){
2366 LeaveCriticalSection(&This->lock);
2367 return AUDCLNT_E_NOT_STOPPED;
2370 if(This->getbuf_last){
2371 LeaveCriticalSection(&This->lock);
2372 return AUDCLNT_E_BUFFER_OPERATION_PENDING;
2375 if(snd_pcm_drop(This->pcm_handle) < 0)
2376 WARN("snd_pcm_drop failed\n");
2378 if(snd_pcm_reset(This->pcm_handle) < 0)
2379 WARN("snd_pcm_reset failed\n");
2381 if(snd_pcm_prepare(This->pcm_handle) < 0)
2382 WARN("snd_pcm_prepare failed\n");
2384 if(This->dataflow == eRender){
2385 This->written_frames = 0;
2386 This->last_pos_frames = 0;
2387 }else{
2388 This->written_frames += This->held_frames;
2390 This->held_frames = 0;
2391 This->lcl_offs_frames = 0;
2392 This->wri_offs_frames = 0;
2394 LeaveCriticalSection(&This->lock);
2396 return S_OK;
2399 static HRESULT WINAPI AudioClient_SetEventHandle(IAudioClient *iface,
2400 HANDLE event)
2402 ACImpl *This = impl_from_IAudioClient(iface);
2404 TRACE("(%p)->(%p)\n", This, event);
2406 if(!event)
2407 return E_INVALIDARG;
2409 EnterCriticalSection(&This->lock);
2411 if(!This->initted){
2412 LeaveCriticalSection(&This->lock);
2413 return AUDCLNT_E_NOT_INITIALIZED;
2416 if(!(This->flags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK)){
2417 LeaveCriticalSection(&This->lock);
2418 return AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED;
2421 if (This->event){
2422 LeaveCriticalSection(&This->lock);
2423 FIXME("called twice\n");
2424 return HRESULT_FROM_WIN32(ERROR_INVALID_NAME);
2427 This->event = event;
2429 LeaveCriticalSection(&This->lock);
2431 return S_OK;
2434 static HRESULT WINAPI AudioClient_GetService(IAudioClient *iface, REFIID riid,
2435 void **ppv)
2437 ACImpl *This = impl_from_IAudioClient(iface);
2439 TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(riid), ppv);
2441 if(!ppv)
2442 return E_POINTER;
2443 *ppv = NULL;
2445 EnterCriticalSection(&This->lock);
2447 if(!This->initted){
2448 LeaveCriticalSection(&This->lock);
2449 return AUDCLNT_E_NOT_INITIALIZED;
2452 if(IsEqualIID(riid, &IID_IAudioRenderClient)){
2453 if(This->dataflow != eRender){
2454 LeaveCriticalSection(&This->lock);
2455 return AUDCLNT_E_WRONG_ENDPOINT_TYPE;
2457 IAudioRenderClient_AddRef(&This->IAudioRenderClient_iface);
2458 *ppv = &This->IAudioRenderClient_iface;
2459 }else if(IsEqualIID(riid, &IID_IAudioCaptureClient)){
2460 if(This->dataflow != eCapture){
2461 LeaveCriticalSection(&This->lock);
2462 return AUDCLNT_E_WRONG_ENDPOINT_TYPE;
2464 IAudioCaptureClient_AddRef(&This->IAudioCaptureClient_iface);
2465 *ppv = &This->IAudioCaptureClient_iface;
2466 }else if(IsEqualIID(riid, &IID_IAudioClock)){
2467 IAudioClock_AddRef(&This->IAudioClock_iface);
2468 *ppv = &This->IAudioClock_iface;
2469 }else if(IsEqualIID(riid, &IID_IAudioStreamVolume)){
2470 IAudioStreamVolume_AddRef(&This->IAudioStreamVolume_iface);
2471 *ppv = &This->IAudioStreamVolume_iface;
2472 }else if(IsEqualIID(riid, &IID_IAudioSessionControl)){
2473 if(!This->session_wrapper){
2474 This->session_wrapper = AudioSessionWrapper_Create(This);
2475 if(!This->session_wrapper){
2476 LeaveCriticalSection(&This->lock);
2477 return E_OUTOFMEMORY;
2479 }else
2480 IAudioSessionControl2_AddRef(&This->session_wrapper->IAudioSessionControl2_iface);
2482 *ppv = &This->session_wrapper->IAudioSessionControl2_iface;
2483 }else if(IsEqualIID(riid, &IID_IChannelAudioVolume)){
2484 if(!This->session_wrapper){
2485 This->session_wrapper = AudioSessionWrapper_Create(This);
2486 if(!This->session_wrapper){
2487 LeaveCriticalSection(&This->lock);
2488 return E_OUTOFMEMORY;
2490 }else
2491 IChannelAudioVolume_AddRef(&This->session_wrapper->IChannelAudioVolume_iface);
2493 *ppv = &This->session_wrapper->IChannelAudioVolume_iface;
2494 }else if(IsEqualIID(riid, &IID_ISimpleAudioVolume)){
2495 if(!This->session_wrapper){
2496 This->session_wrapper = AudioSessionWrapper_Create(This);
2497 if(!This->session_wrapper){
2498 LeaveCriticalSection(&This->lock);
2499 return E_OUTOFMEMORY;
2501 }else
2502 ISimpleAudioVolume_AddRef(&This->session_wrapper->ISimpleAudioVolume_iface);
2504 *ppv = &This->session_wrapper->ISimpleAudioVolume_iface;
2507 if(*ppv){
2508 LeaveCriticalSection(&This->lock);
2509 return S_OK;
2512 LeaveCriticalSection(&This->lock);
2514 FIXME("stub %s\n", debugstr_guid(riid));
2515 return E_NOINTERFACE;
2518 static const IAudioClientVtbl AudioClient_Vtbl =
2520 AudioClient_QueryInterface,
2521 AudioClient_AddRef,
2522 AudioClient_Release,
2523 AudioClient_Initialize,
2524 AudioClient_GetBufferSize,
2525 AudioClient_GetStreamLatency,
2526 AudioClient_GetCurrentPadding,
2527 AudioClient_IsFormatSupported,
2528 AudioClient_GetMixFormat,
2529 AudioClient_GetDevicePeriod,
2530 AudioClient_Start,
2531 AudioClient_Stop,
2532 AudioClient_Reset,
2533 AudioClient_SetEventHandle,
2534 AudioClient_GetService
2537 static HRESULT WINAPI AudioRenderClient_QueryInterface(
2538 IAudioRenderClient *iface, REFIID riid, void **ppv)
2540 ACImpl *This = impl_from_IAudioRenderClient(iface);
2541 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2543 if(!ppv)
2544 return E_POINTER;
2545 *ppv = NULL;
2547 if(IsEqualIID(riid, &IID_IUnknown) ||
2548 IsEqualIID(riid, &IID_IAudioRenderClient))
2549 *ppv = iface;
2550 else if(IsEqualIID(riid, &IID_IMarshal))
2551 return IUnknown_QueryInterface(This->pUnkFTMarshal, riid, ppv);
2553 if(*ppv){
2554 IUnknown_AddRef((IUnknown*)*ppv);
2555 return S_OK;
2558 WARN("Unknown interface %s\n", debugstr_guid(riid));
2559 return E_NOINTERFACE;
2562 static ULONG WINAPI AudioRenderClient_AddRef(IAudioRenderClient *iface)
2564 ACImpl *This = impl_from_IAudioRenderClient(iface);
2565 return AudioClient_AddRef(&This->IAudioClient_iface);
2568 static ULONG WINAPI AudioRenderClient_Release(IAudioRenderClient *iface)
2570 ACImpl *This = impl_from_IAudioRenderClient(iface);
2571 return AudioClient_Release(&This->IAudioClient_iface);
2574 static HRESULT WINAPI AudioRenderClient_GetBuffer(IAudioRenderClient *iface,
2575 UINT32 frames, BYTE **data)
2577 ACImpl *This = impl_from_IAudioRenderClient(iface);
2578 UINT32 write_pos;
2580 TRACE("(%p)->(%u, %p)\n", This, frames, data);
2582 if(!data)
2583 return E_POINTER;
2584 *data = NULL;
2586 EnterCriticalSection(&This->lock);
2588 if(This->getbuf_last){
2589 LeaveCriticalSection(&This->lock);
2590 return AUDCLNT_E_OUT_OF_ORDER;
2593 if(!frames){
2594 LeaveCriticalSection(&This->lock);
2595 return S_OK;
2598 /* held_frames == GetCurrentPadding_nolock(); */
2599 if(This->held_frames + frames > This->bufsize_frames){
2600 LeaveCriticalSection(&This->lock);
2601 return AUDCLNT_E_BUFFER_TOO_LARGE;
2604 write_pos = This->wri_offs_frames;
2605 if(write_pos + frames > This->bufsize_frames){
2606 if(This->tmp_buffer_frames < frames){
2607 HeapFree(GetProcessHeap(), 0, This->tmp_buffer);
2608 This->tmp_buffer = HeapAlloc(GetProcessHeap(), 0,
2609 frames * This->fmt->nBlockAlign);
2610 if(!This->tmp_buffer){
2611 LeaveCriticalSection(&This->lock);
2612 return E_OUTOFMEMORY;
2614 This->tmp_buffer_frames = frames;
2616 *data = This->tmp_buffer;
2617 This->getbuf_last = -frames;
2618 }else{
2619 *data = This->local_buffer + write_pos * This->fmt->nBlockAlign;
2620 This->getbuf_last = frames;
2623 silence_buffer(This, *data, frames);
2625 LeaveCriticalSection(&This->lock);
2627 return S_OK;
2630 static void alsa_wrap_buffer(ACImpl *This, BYTE *buffer, UINT32 written_frames)
2632 snd_pcm_uframes_t write_offs_frames = This->wri_offs_frames;
2633 UINT32 write_offs_bytes = write_offs_frames * This->fmt->nBlockAlign;
2634 snd_pcm_uframes_t chunk_frames = This->bufsize_frames - write_offs_frames;
2635 UINT32 chunk_bytes = chunk_frames * This->fmt->nBlockAlign;
2636 UINT32 written_bytes = written_frames * This->fmt->nBlockAlign;
2638 if(written_bytes <= chunk_bytes){
2639 memcpy(This->local_buffer + write_offs_bytes, buffer, written_bytes);
2640 }else{
2641 memcpy(This->local_buffer + write_offs_bytes, buffer, chunk_bytes);
2642 memcpy(This->local_buffer, buffer + chunk_bytes,
2643 written_bytes - chunk_bytes);
2647 static HRESULT WINAPI AudioRenderClient_ReleaseBuffer(
2648 IAudioRenderClient *iface, UINT32 written_frames, DWORD flags)
2650 ACImpl *This = impl_from_IAudioRenderClient(iface);
2651 BYTE *buffer;
2653 TRACE("(%p)->(%u, %x)\n", This, written_frames, flags);
2655 EnterCriticalSection(&This->lock);
2657 if(!written_frames){
2658 This->getbuf_last = 0;
2659 LeaveCriticalSection(&This->lock);
2660 return S_OK;
2663 if(!This->getbuf_last){
2664 LeaveCriticalSection(&This->lock);
2665 return AUDCLNT_E_OUT_OF_ORDER;
2668 if(written_frames > (This->getbuf_last >= 0 ? This->getbuf_last : -This->getbuf_last)){
2669 LeaveCriticalSection(&This->lock);
2670 return AUDCLNT_E_INVALID_SIZE;
2673 if(This->getbuf_last >= 0)
2674 buffer = This->local_buffer + This->wri_offs_frames * This->fmt->nBlockAlign;
2675 else
2676 buffer = This->tmp_buffer;
2678 if(flags & AUDCLNT_BUFFERFLAGS_SILENT)
2679 silence_buffer(This, buffer, written_frames);
2681 if(This->getbuf_last < 0)
2682 alsa_wrap_buffer(This, buffer, written_frames);
2684 This->wri_offs_frames += written_frames;
2685 This->wri_offs_frames %= This->bufsize_frames;
2686 This->held_frames += written_frames;
2687 This->written_frames += written_frames;
2688 This->getbuf_last = 0;
2690 LeaveCriticalSection(&This->lock);
2692 return S_OK;
2695 static const IAudioRenderClientVtbl AudioRenderClient_Vtbl = {
2696 AudioRenderClient_QueryInterface,
2697 AudioRenderClient_AddRef,
2698 AudioRenderClient_Release,
2699 AudioRenderClient_GetBuffer,
2700 AudioRenderClient_ReleaseBuffer
2703 static HRESULT WINAPI AudioCaptureClient_QueryInterface(
2704 IAudioCaptureClient *iface, REFIID riid, void **ppv)
2706 ACImpl *This = impl_from_IAudioCaptureClient(iface);
2707 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2709 if(!ppv)
2710 return E_POINTER;
2711 *ppv = NULL;
2713 if(IsEqualIID(riid, &IID_IUnknown) ||
2714 IsEqualIID(riid, &IID_IAudioCaptureClient))
2715 *ppv = iface;
2716 else if(IsEqualIID(riid, &IID_IMarshal))
2717 return IUnknown_QueryInterface(This->pUnkFTMarshal, riid, ppv);
2719 if(*ppv){
2720 IUnknown_AddRef((IUnknown*)*ppv);
2721 return S_OK;
2724 WARN("Unknown interface %s\n", debugstr_guid(riid));
2725 return E_NOINTERFACE;
2728 static ULONG WINAPI AudioCaptureClient_AddRef(IAudioCaptureClient *iface)
2730 ACImpl *This = impl_from_IAudioCaptureClient(iface);
2731 return IAudioClient_AddRef(&This->IAudioClient_iface);
2734 static ULONG WINAPI AudioCaptureClient_Release(IAudioCaptureClient *iface)
2736 ACImpl *This = impl_from_IAudioCaptureClient(iface);
2737 return IAudioClient_Release(&This->IAudioClient_iface);
2740 static HRESULT WINAPI AudioCaptureClient_GetBuffer(IAudioCaptureClient *iface,
2741 BYTE **data, UINT32 *frames, DWORD *flags, UINT64 *devpos,
2742 UINT64 *qpcpos)
2744 ACImpl *This = impl_from_IAudioCaptureClient(iface);
2746 TRACE("(%p)->(%p, %p, %p, %p, %p)\n", This, data, frames, flags,
2747 devpos, qpcpos);
2749 if(!data || !frames || !flags)
2750 return E_POINTER;
2752 EnterCriticalSection(&This->lock);
2754 if(This->getbuf_last){
2755 LeaveCriticalSection(&This->lock);
2756 return AUDCLNT_E_OUT_OF_ORDER;
2759 /* hr = GetNextPacketSize(iface, frames); */
2760 if(This->held_frames < This->mmdev_period_frames){
2761 *frames = 0;
2762 LeaveCriticalSection(&This->lock);
2763 return AUDCLNT_S_BUFFER_EMPTY;
2765 *frames = This->mmdev_period_frames;
2767 if(This->lcl_offs_frames + *frames > This->bufsize_frames){
2768 UINT32 chunk_bytes, offs_bytes, frames_bytes;
2769 if(This->tmp_buffer_frames < *frames){
2770 HeapFree(GetProcessHeap(), 0, This->tmp_buffer);
2771 This->tmp_buffer = HeapAlloc(GetProcessHeap(), 0,
2772 *frames * This->fmt->nBlockAlign);
2773 if(!This->tmp_buffer){
2774 LeaveCriticalSection(&This->lock);
2775 return E_OUTOFMEMORY;
2777 This->tmp_buffer_frames = *frames;
2780 *data = This->tmp_buffer;
2781 chunk_bytes = (This->bufsize_frames - This->lcl_offs_frames) *
2782 This->fmt->nBlockAlign;
2783 offs_bytes = This->lcl_offs_frames * This->fmt->nBlockAlign;
2784 frames_bytes = *frames * This->fmt->nBlockAlign;
2785 memcpy(This->tmp_buffer, This->local_buffer + offs_bytes, chunk_bytes);
2786 memcpy(This->tmp_buffer + chunk_bytes, This->local_buffer,
2787 frames_bytes - chunk_bytes);
2788 }else
2789 *data = This->local_buffer +
2790 This->lcl_offs_frames * This->fmt->nBlockAlign;
2792 This->getbuf_last = *frames;
2793 *flags = 0;
2795 if(devpos)
2796 *devpos = This->written_frames;
2797 if(qpcpos){ /* fixme: qpc of recording time */
2798 LARGE_INTEGER stamp, freq;
2799 QueryPerformanceCounter(&stamp);
2800 QueryPerformanceFrequency(&freq);
2801 *qpcpos = (stamp.QuadPart * (INT64)10000000) / freq.QuadPart;
2804 LeaveCriticalSection(&This->lock);
2806 return *frames ? S_OK : AUDCLNT_S_BUFFER_EMPTY;
2809 static HRESULT WINAPI AudioCaptureClient_ReleaseBuffer(
2810 IAudioCaptureClient *iface, UINT32 done)
2812 ACImpl *This = impl_from_IAudioCaptureClient(iface);
2814 TRACE("(%p)->(%u)\n", This, done);
2816 EnterCriticalSection(&This->lock);
2818 if(!done){
2819 This->getbuf_last = 0;
2820 LeaveCriticalSection(&This->lock);
2821 return S_OK;
2824 if(!This->getbuf_last){
2825 LeaveCriticalSection(&This->lock);
2826 return AUDCLNT_E_OUT_OF_ORDER;
2829 if(This->getbuf_last != done){
2830 LeaveCriticalSection(&This->lock);
2831 return AUDCLNT_E_INVALID_SIZE;
2834 This->written_frames += done;
2835 This->held_frames -= done;
2836 This->lcl_offs_frames += done;
2837 This->lcl_offs_frames %= This->bufsize_frames;
2838 This->getbuf_last = 0;
2840 LeaveCriticalSection(&This->lock);
2842 return S_OK;
2845 static HRESULT WINAPI AudioCaptureClient_GetNextPacketSize(
2846 IAudioCaptureClient *iface, UINT32 *frames)
2848 ACImpl *This = impl_from_IAudioCaptureClient(iface);
2850 TRACE("(%p)->(%p)\n", This, frames);
2852 if(!frames)
2853 return E_POINTER;
2855 EnterCriticalSection(&This->lock);
2857 *frames = This->held_frames < This->mmdev_period_frames ? 0 : This->mmdev_period_frames;
2859 LeaveCriticalSection(&This->lock);
2861 return S_OK;
2864 static const IAudioCaptureClientVtbl AudioCaptureClient_Vtbl =
2866 AudioCaptureClient_QueryInterface,
2867 AudioCaptureClient_AddRef,
2868 AudioCaptureClient_Release,
2869 AudioCaptureClient_GetBuffer,
2870 AudioCaptureClient_ReleaseBuffer,
2871 AudioCaptureClient_GetNextPacketSize
2874 static HRESULT WINAPI AudioClock_QueryInterface(IAudioClock *iface,
2875 REFIID riid, void **ppv)
2877 ACImpl *This = impl_from_IAudioClock(iface);
2879 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2881 if(!ppv)
2882 return E_POINTER;
2883 *ppv = NULL;
2885 if(IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IAudioClock))
2886 *ppv = iface;
2887 else if(IsEqualIID(riid, &IID_IAudioClock2))
2888 *ppv = &This->IAudioClock2_iface;
2889 if(*ppv){
2890 IUnknown_AddRef((IUnknown*)*ppv);
2891 return S_OK;
2894 WARN("Unknown interface %s\n", debugstr_guid(riid));
2895 return E_NOINTERFACE;
2898 static ULONG WINAPI AudioClock_AddRef(IAudioClock *iface)
2900 ACImpl *This = impl_from_IAudioClock(iface);
2901 return IAudioClient_AddRef(&This->IAudioClient_iface);
2904 static ULONG WINAPI AudioClock_Release(IAudioClock *iface)
2906 ACImpl *This = impl_from_IAudioClock(iface);
2907 return IAudioClient_Release(&This->IAudioClient_iface);
2910 static HRESULT WINAPI AudioClock_GetFrequency(IAudioClock *iface, UINT64 *freq)
2912 ACImpl *This = impl_from_IAudioClock(iface);
2914 TRACE("(%p)->(%p)\n", This, freq);
2916 if(This->share == AUDCLNT_SHAREMODE_SHARED)
2917 *freq = (UINT64)This->fmt->nSamplesPerSec * This->fmt->nBlockAlign;
2918 else
2919 *freq = This->fmt->nSamplesPerSec;
2921 return S_OK;
2924 static HRESULT WINAPI AudioClock_GetPosition(IAudioClock *iface, UINT64 *pos,
2925 UINT64 *qpctime)
2927 ACImpl *This = impl_from_IAudioClock(iface);
2928 UINT64 position;
2929 snd_pcm_state_t alsa_state;
2931 TRACE("(%p)->(%p, %p)\n", This, pos, qpctime);
2933 if(!pos)
2934 return E_POINTER;
2936 EnterCriticalSection(&This->lock);
2938 /* avail_update required to get accurate snd_pcm_state() */
2939 snd_pcm_avail_update(This->pcm_handle);
2940 alsa_state = snd_pcm_state(This->pcm_handle);
2942 if(This->dataflow == eRender){
2943 position = This->written_frames - This->held_frames;
2945 if(This->started && alsa_state == SND_PCM_STATE_RUNNING && This->held_frames)
2946 /* we should be using snd_pcm_delay here, but it is broken
2947 * especially during ALSA device underrun. instead, let's just
2948 * interpolate between periods with the system timer. */
2949 position += interp_elapsed_frames(This);
2951 position = min(position, This->written_frames - This->held_frames + This->mmdev_period_frames);
2953 position = min(position, This->written_frames);
2954 }else
2955 position = This->written_frames + This->held_frames;
2957 /* ensure monotic growth */
2958 if(position < This->last_pos_frames)
2959 position = This->last_pos_frames;
2960 else
2961 This->last_pos_frames = position;
2963 TRACE("frames written: %u, held: %u, state: 0x%x, position: %u\n",
2964 (UINT32)(This->written_frames%1000000000), This->held_frames,
2965 alsa_state, (UINT32)(position%1000000000));
2967 LeaveCriticalSection(&This->lock);
2969 if(This->share == AUDCLNT_SHAREMODE_SHARED)
2970 *pos = position * This->fmt->nBlockAlign;
2971 else
2972 *pos = position;
2974 if(qpctime){
2975 LARGE_INTEGER stamp, freq;
2976 QueryPerformanceCounter(&stamp);
2977 QueryPerformanceFrequency(&freq);
2978 *qpctime = (stamp.QuadPart * (INT64)10000000) / freq.QuadPart;
2981 return S_OK;
2984 static HRESULT WINAPI AudioClock_GetCharacteristics(IAudioClock *iface,
2985 DWORD *chars)
2987 ACImpl *This = impl_from_IAudioClock(iface);
2989 TRACE("(%p)->(%p)\n", This, chars);
2991 if(!chars)
2992 return E_POINTER;
2994 *chars = AUDIOCLOCK_CHARACTERISTIC_FIXED_FREQ;
2996 return S_OK;
2999 static const IAudioClockVtbl AudioClock_Vtbl =
3001 AudioClock_QueryInterface,
3002 AudioClock_AddRef,
3003 AudioClock_Release,
3004 AudioClock_GetFrequency,
3005 AudioClock_GetPosition,
3006 AudioClock_GetCharacteristics
3009 static HRESULT WINAPI AudioClock2_QueryInterface(IAudioClock2 *iface,
3010 REFIID riid, void **ppv)
3012 ACImpl *This = impl_from_IAudioClock2(iface);
3013 return IAudioClock_QueryInterface(&This->IAudioClock_iface, riid, ppv);
3016 static ULONG WINAPI AudioClock2_AddRef(IAudioClock2 *iface)
3018 ACImpl *This = impl_from_IAudioClock2(iface);
3019 return IAudioClient_AddRef(&This->IAudioClient_iface);
3022 static ULONG WINAPI AudioClock2_Release(IAudioClock2 *iface)
3024 ACImpl *This = impl_from_IAudioClock2(iface);
3025 return IAudioClient_Release(&This->IAudioClient_iface);
3028 static HRESULT WINAPI AudioClock2_GetDevicePosition(IAudioClock2 *iface,
3029 UINT64 *pos, UINT64 *qpctime)
3031 ACImpl *This = impl_from_IAudioClock2(iface);
3033 FIXME("(%p)->(%p, %p)\n", This, pos, qpctime);
3035 return E_NOTIMPL;
3038 static const IAudioClock2Vtbl AudioClock2_Vtbl =
3040 AudioClock2_QueryInterface,
3041 AudioClock2_AddRef,
3042 AudioClock2_Release,
3043 AudioClock2_GetDevicePosition
3046 static AudioSessionWrapper *AudioSessionWrapper_Create(ACImpl *client)
3048 AudioSessionWrapper *ret;
3050 ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
3051 sizeof(AudioSessionWrapper));
3052 if(!ret)
3053 return NULL;
3055 ret->IAudioSessionControl2_iface.lpVtbl = &AudioSessionControl2_Vtbl;
3056 ret->ISimpleAudioVolume_iface.lpVtbl = &SimpleAudioVolume_Vtbl;
3057 ret->IChannelAudioVolume_iface.lpVtbl = &ChannelAudioVolume_Vtbl;
3059 ret->ref = 1;
3061 ret->client = client;
3062 if(client){
3063 ret->session = client->session;
3064 AudioClient_AddRef(&client->IAudioClient_iface);
3067 return ret;
3070 static HRESULT WINAPI AudioSessionControl_QueryInterface(
3071 IAudioSessionControl2 *iface, REFIID riid, void **ppv)
3073 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
3075 if(!ppv)
3076 return E_POINTER;
3077 *ppv = NULL;
3079 if(IsEqualIID(riid, &IID_IUnknown) ||
3080 IsEqualIID(riid, &IID_IAudioSessionControl) ||
3081 IsEqualIID(riid, &IID_IAudioSessionControl2))
3082 *ppv = iface;
3083 if(*ppv){
3084 IUnknown_AddRef((IUnknown*)*ppv);
3085 return S_OK;
3088 WARN("Unknown interface %s\n", debugstr_guid(riid));
3089 return E_NOINTERFACE;
3092 static ULONG WINAPI AudioSessionControl_AddRef(IAudioSessionControl2 *iface)
3094 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
3095 ULONG ref;
3096 ref = InterlockedIncrement(&This->ref);
3097 TRACE("(%p) Refcount now %u\n", This, ref);
3098 return ref;
3101 static ULONG WINAPI AudioSessionControl_Release(IAudioSessionControl2 *iface)
3103 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
3104 ULONG ref;
3105 ref = InterlockedDecrement(&This->ref);
3106 TRACE("(%p) Refcount now %u\n", This, ref);
3107 if(!ref){
3108 if(This->client){
3109 EnterCriticalSection(&This->client->lock);
3110 This->client->session_wrapper = NULL;
3111 LeaveCriticalSection(&This->client->lock);
3112 AudioClient_Release(&This->client->IAudioClient_iface);
3114 HeapFree(GetProcessHeap(), 0, This);
3116 return ref;
3119 static HRESULT WINAPI AudioSessionControl_GetState(IAudioSessionControl2 *iface,
3120 AudioSessionState *state)
3122 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
3123 ACImpl *client;
3125 TRACE("(%p)->(%p)\n", This, state);
3127 if(!state)
3128 return NULL_PTR_ERR;
3130 EnterCriticalSection(&g_sessions_lock);
3132 if(list_empty(&This->session->clients)){
3133 *state = AudioSessionStateExpired;
3134 LeaveCriticalSection(&g_sessions_lock);
3135 return S_OK;
3138 LIST_FOR_EACH_ENTRY(client, &This->session->clients, ACImpl, entry){
3139 EnterCriticalSection(&client->lock);
3140 if(client->started){
3141 *state = AudioSessionStateActive;
3142 LeaveCriticalSection(&client->lock);
3143 LeaveCriticalSection(&g_sessions_lock);
3144 return S_OK;
3146 LeaveCriticalSection(&client->lock);
3149 LeaveCriticalSection(&g_sessions_lock);
3151 *state = AudioSessionStateInactive;
3153 return S_OK;
3156 static HRESULT WINAPI AudioSessionControl_GetDisplayName(
3157 IAudioSessionControl2 *iface, WCHAR **name)
3159 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
3161 FIXME("(%p)->(%p) - stub\n", This, name);
3163 return E_NOTIMPL;
3166 static HRESULT WINAPI AudioSessionControl_SetDisplayName(
3167 IAudioSessionControl2 *iface, const WCHAR *name, const GUID *session)
3169 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
3171 FIXME("(%p)->(%p, %s) - stub\n", This, name, debugstr_guid(session));
3173 return E_NOTIMPL;
3176 static HRESULT WINAPI AudioSessionControl_GetIconPath(
3177 IAudioSessionControl2 *iface, WCHAR **path)
3179 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
3181 FIXME("(%p)->(%p) - stub\n", This, path);
3183 return E_NOTIMPL;
3186 static HRESULT WINAPI AudioSessionControl_SetIconPath(
3187 IAudioSessionControl2 *iface, const WCHAR *path, const GUID *session)
3189 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
3191 FIXME("(%p)->(%p, %s) - stub\n", This, path, debugstr_guid(session));
3193 return E_NOTIMPL;
3196 static HRESULT WINAPI AudioSessionControl_GetGroupingParam(
3197 IAudioSessionControl2 *iface, GUID *group)
3199 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
3201 FIXME("(%p)->(%p) - stub\n", This, group);
3203 return E_NOTIMPL;
3206 static HRESULT WINAPI AudioSessionControl_SetGroupingParam(
3207 IAudioSessionControl2 *iface, const GUID *group, const GUID *session)
3209 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
3211 FIXME("(%p)->(%s, %s) - stub\n", This, debugstr_guid(group),
3212 debugstr_guid(session));
3214 return E_NOTIMPL;
3217 static HRESULT WINAPI AudioSessionControl_RegisterAudioSessionNotification(
3218 IAudioSessionControl2 *iface, IAudioSessionEvents *events)
3220 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
3222 FIXME("(%p)->(%p) - stub\n", This, events);
3224 return S_OK;
3227 static HRESULT WINAPI AudioSessionControl_UnregisterAudioSessionNotification(
3228 IAudioSessionControl2 *iface, IAudioSessionEvents *events)
3230 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
3232 FIXME("(%p)->(%p) - stub\n", This, events);
3234 return S_OK;
3237 static HRESULT WINAPI AudioSessionControl_GetSessionIdentifier(
3238 IAudioSessionControl2 *iface, WCHAR **id)
3240 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
3242 FIXME("(%p)->(%p) - stub\n", This, id);
3244 return E_NOTIMPL;
3247 static HRESULT WINAPI AudioSessionControl_GetSessionInstanceIdentifier(
3248 IAudioSessionControl2 *iface, WCHAR **id)
3250 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
3252 FIXME("(%p)->(%p) - stub\n", This, id);
3254 return E_NOTIMPL;
3257 static HRESULT WINAPI AudioSessionControl_GetProcessId(
3258 IAudioSessionControl2 *iface, DWORD *pid)
3260 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
3262 TRACE("(%p)->(%p)\n", This, pid);
3264 if(!pid)
3265 return E_POINTER;
3267 *pid = GetCurrentProcessId();
3269 return S_OK;
3272 static HRESULT WINAPI AudioSessionControl_IsSystemSoundsSession(
3273 IAudioSessionControl2 *iface)
3275 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
3277 TRACE("(%p)\n", This);
3279 return S_FALSE;
3282 static HRESULT WINAPI AudioSessionControl_SetDuckingPreference(
3283 IAudioSessionControl2 *iface, BOOL optout)
3285 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
3287 TRACE("(%p)->(%d)\n", This, optout);
3289 return S_OK;
3292 static const IAudioSessionControl2Vtbl AudioSessionControl2_Vtbl =
3294 AudioSessionControl_QueryInterface,
3295 AudioSessionControl_AddRef,
3296 AudioSessionControl_Release,
3297 AudioSessionControl_GetState,
3298 AudioSessionControl_GetDisplayName,
3299 AudioSessionControl_SetDisplayName,
3300 AudioSessionControl_GetIconPath,
3301 AudioSessionControl_SetIconPath,
3302 AudioSessionControl_GetGroupingParam,
3303 AudioSessionControl_SetGroupingParam,
3304 AudioSessionControl_RegisterAudioSessionNotification,
3305 AudioSessionControl_UnregisterAudioSessionNotification,
3306 AudioSessionControl_GetSessionIdentifier,
3307 AudioSessionControl_GetSessionInstanceIdentifier,
3308 AudioSessionControl_GetProcessId,
3309 AudioSessionControl_IsSystemSoundsSession,
3310 AudioSessionControl_SetDuckingPreference
3313 static HRESULT WINAPI SimpleAudioVolume_QueryInterface(
3314 ISimpleAudioVolume *iface, REFIID riid, void **ppv)
3316 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
3318 if(!ppv)
3319 return E_POINTER;
3320 *ppv = NULL;
3322 if(IsEqualIID(riid, &IID_IUnknown) ||
3323 IsEqualIID(riid, &IID_ISimpleAudioVolume))
3324 *ppv = iface;
3325 if(*ppv){
3326 IUnknown_AddRef((IUnknown*)*ppv);
3327 return S_OK;
3330 WARN("Unknown interface %s\n", debugstr_guid(riid));
3331 return E_NOINTERFACE;
3334 static ULONG WINAPI SimpleAudioVolume_AddRef(ISimpleAudioVolume *iface)
3336 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
3337 return AudioSessionControl_AddRef(&This->IAudioSessionControl2_iface);
3340 static ULONG WINAPI SimpleAudioVolume_Release(ISimpleAudioVolume *iface)
3342 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
3343 return AudioSessionControl_Release(&This->IAudioSessionControl2_iface);
3346 static HRESULT WINAPI SimpleAudioVolume_SetMasterVolume(
3347 ISimpleAudioVolume *iface, float level, const GUID *context)
3349 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
3350 AudioSession *session = This->session;
3352 TRACE("(%p)->(%f, %s)\n", session, level, wine_dbgstr_guid(context));
3354 if(level < 0.f || level > 1.f)
3355 return E_INVALIDARG;
3357 if(context)
3358 FIXME("Notifications not supported yet\n");
3360 TRACE("ALSA does not support volume control\n");
3362 EnterCriticalSection(&session->lock);
3364 session->master_vol = level;
3366 LeaveCriticalSection(&session->lock);
3368 return S_OK;
3371 static HRESULT WINAPI SimpleAudioVolume_GetMasterVolume(
3372 ISimpleAudioVolume *iface, float *level)
3374 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
3375 AudioSession *session = This->session;
3377 TRACE("(%p)->(%p)\n", session, level);
3379 if(!level)
3380 return NULL_PTR_ERR;
3382 *level = session->master_vol;
3384 return S_OK;
3387 static HRESULT WINAPI SimpleAudioVolume_SetMute(ISimpleAudioVolume *iface,
3388 BOOL mute, const GUID *context)
3390 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
3391 AudioSession *session = This->session;
3393 TRACE("(%p)->(%u, %p)\n", session, mute, context);
3395 if(context)
3396 FIXME("Notifications not supported yet\n");
3398 session->mute = mute;
3400 return S_OK;
3403 static HRESULT WINAPI SimpleAudioVolume_GetMute(ISimpleAudioVolume *iface,
3404 BOOL *mute)
3406 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
3407 AudioSession *session = This->session;
3409 TRACE("(%p)->(%p)\n", session, mute);
3411 if(!mute)
3412 return NULL_PTR_ERR;
3414 *mute = session->mute;
3416 return S_OK;
3419 static const ISimpleAudioVolumeVtbl SimpleAudioVolume_Vtbl =
3421 SimpleAudioVolume_QueryInterface,
3422 SimpleAudioVolume_AddRef,
3423 SimpleAudioVolume_Release,
3424 SimpleAudioVolume_SetMasterVolume,
3425 SimpleAudioVolume_GetMasterVolume,
3426 SimpleAudioVolume_SetMute,
3427 SimpleAudioVolume_GetMute
3430 static HRESULT WINAPI AudioStreamVolume_QueryInterface(
3431 IAudioStreamVolume *iface, REFIID riid, void **ppv)
3433 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
3435 if(!ppv)
3436 return E_POINTER;
3437 *ppv = NULL;
3439 if(IsEqualIID(riid, &IID_IUnknown) ||
3440 IsEqualIID(riid, &IID_IAudioStreamVolume))
3441 *ppv = iface;
3442 if(*ppv){
3443 IUnknown_AddRef((IUnknown*)*ppv);
3444 return S_OK;
3447 WARN("Unknown interface %s\n", debugstr_guid(riid));
3448 return E_NOINTERFACE;
3451 static ULONG WINAPI AudioStreamVolume_AddRef(IAudioStreamVolume *iface)
3453 ACImpl *This = impl_from_IAudioStreamVolume(iface);
3454 return IAudioClient_AddRef(&This->IAudioClient_iface);
3457 static ULONG WINAPI AudioStreamVolume_Release(IAudioStreamVolume *iface)
3459 ACImpl *This = impl_from_IAudioStreamVolume(iface);
3460 return IAudioClient_Release(&This->IAudioClient_iface);
3463 static HRESULT WINAPI AudioStreamVolume_GetChannelCount(
3464 IAudioStreamVolume *iface, UINT32 *out)
3466 ACImpl *This = impl_from_IAudioStreamVolume(iface);
3468 TRACE("(%p)->(%p)\n", This, out);
3470 if(!out)
3471 return E_POINTER;
3473 *out = This->fmt->nChannels;
3475 return S_OK;
3478 static HRESULT WINAPI AudioStreamVolume_SetChannelVolume(
3479 IAudioStreamVolume *iface, UINT32 index, float level)
3481 ACImpl *This = impl_from_IAudioStreamVolume(iface);
3483 TRACE("(%p)->(%d, %f)\n", This, index, level);
3485 if(level < 0.f || level > 1.f)
3486 return E_INVALIDARG;
3488 if(index >= This->fmt->nChannels)
3489 return E_INVALIDARG;
3491 TRACE("ALSA does not support volume control\n");
3493 EnterCriticalSection(&This->lock);
3495 This->vols[index] = level;
3497 LeaveCriticalSection(&This->lock);
3499 return S_OK;
3502 static HRESULT WINAPI AudioStreamVolume_GetChannelVolume(
3503 IAudioStreamVolume *iface, UINT32 index, float *level)
3505 ACImpl *This = impl_from_IAudioStreamVolume(iface);
3507 TRACE("(%p)->(%d, %p)\n", This, index, level);
3509 if(!level)
3510 return E_POINTER;
3512 if(index >= This->fmt->nChannels)
3513 return E_INVALIDARG;
3515 *level = This->vols[index];
3517 return S_OK;
3520 static HRESULT WINAPI AudioStreamVolume_SetAllVolumes(
3521 IAudioStreamVolume *iface, UINT32 count, const float *levels)
3523 ACImpl *This = impl_from_IAudioStreamVolume(iface);
3524 unsigned int i;
3526 TRACE("(%p)->(%d, %p)\n", This, count, levels);
3528 if(!levels)
3529 return E_POINTER;
3531 if(count != This->fmt->nChannels)
3532 return E_INVALIDARG;
3534 TRACE("ALSA does not support volume control\n");
3536 EnterCriticalSection(&This->lock);
3538 for(i = 0; i < count; ++i)
3539 This->vols[i] = levels[i];
3541 LeaveCriticalSection(&This->lock);
3543 return S_OK;
3546 static HRESULT WINAPI AudioStreamVolume_GetAllVolumes(
3547 IAudioStreamVolume *iface, UINT32 count, float *levels)
3549 ACImpl *This = impl_from_IAudioStreamVolume(iface);
3550 unsigned int i;
3552 TRACE("(%p)->(%d, %p)\n", This, count, levels);
3554 if(!levels)
3555 return E_POINTER;
3557 if(count != This->fmt->nChannels)
3558 return E_INVALIDARG;
3560 EnterCriticalSection(&This->lock);
3562 for(i = 0; i < count; ++i)
3563 levels[i] = This->vols[i];
3565 LeaveCriticalSection(&This->lock);
3567 return S_OK;
3570 static const IAudioStreamVolumeVtbl AudioStreamVolume_Vtbl =
3572 AudioStreamVolume_QueryInterface,
3573 AudioStreamVolume_AddRef,
3574 AudioStreamVolume_Release,
3575 AudioStreamVolume_GetChannelCount,
3576 AudioStreamVolume_SetChannelVolume,
3577 AudioStreamVolume_GetChannelVolume,
3578 AudioStreamVolume_SetAllVolumes,
3579 AudioStreamVolume_GetAllVolumes
3582 static HRESULT WINAPI ChannelAudioVolume_QueryInterface(
3583 IChannelAudioVolume *iface, REFIID riid, void **ppv)
3585 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
3587 if(!ppv)
3588 return E_POINTER;
3589 *ppv = NULL;
3591 if(IsEqualIID(riid, &IID_IUnknown) ||
3592 IsEqualIID(riid, &IID_IChannelAudioVolume))
3593 *ppv = iface;
3594 if(*ppv){
3595 IUnknown_AddRef((IUnknown*)*ppv);
3596 return S_OK;
3599 WARN("Unknown interface %s\n", debugstr_guid(riid));
3600 return E_NOINTERFACE;
3603 static ULONG WINAPI ChannelAudioVolume_AddRef(IChannelAudioVolume *iface)
3605 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
3606 return AudioSessionControl_AddRef(&This->IAudioSessionControl2_iface);
3609 static ULONG WINAPI ChannelAudioVolume_Release(IChannelAudioVolume *iface)
3611 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
3612 return AudioSessionControl_Release(&This->IAudioSessionControl2_iface);
3615 static HRESULT WINAPI ChannelAudioVolume_GetChannelCount(
3616 IChannelAudioVolume *iface, UINT32 *out)
3618 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
3619 AudioSession *session = This->session;
3621 TRACE("(%p)->(%p)\n", session, out);
3623 if(!out)
3624 return NULL_PTR_ERR;
3626 *out = session->channel_count;
3628 return S_OK;
3631 static HRESULT WINAPI ChannelAudioVolume_SetChannelVolume(
3632 IChannelAudioVolume *iface, UINT32 index, float level,
3633 const GUID *context)
3635 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
3636 AudioSession *session = This->session;
3638 TRACE("(%p)->(%d, %f, %s)\n", session, index, level,
3639 wine_dbgstr_guid(context));
3641 if(level < 0.f || level > 1.f)
3642 return E_INVALIDARG;
3644 if(index >= session->channel_count)
3645 return E_INVALIDARG;
3647 if(context)
3648 FIXME("Notifications not supported yet\n");
3650 TRACE("ALSA does not support volume control\n");
3652 EnterCriticalSection(&session->lock);
3654 session->channel_vols[index] = level;
3656 LeaveCriticalSection(&session->lock);
3658 return S_OK;
3661 static HRESULT WINAPI ChannelAudioVolume_GetChannelVolume(
3662 IChannelAudioVolume *iface, UINT32 index, float *level)
3664 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
3665 AudioSession *session = This->session;
3667 TRACE("(%p)->(%d, %p)\n", session, index, level);
3669 if(!level)
3670 return NULL_PTR_ERR;
3672 if(index >= session->channel_count)
3673 return E_INVALIDARG;
3675 *level = session->channel_vols[index];
3677 return S_OK;
3680 static HRESULT WINAPI ChannelAudioVolume_SetAllVolumes(
3681 IChannelAudioVolume *iface, UINT32 count, const float *levels,
3682 const GUID *context)
3684 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
3685 AudioSession *session = This->session;
3686 unsigned int i;
3688 TRACE("(%p)->(%d, %p, %s)\n", session, count, levels,
3689 wine_dbgstr_guid(context));
3691 if(!levels)
3692 return NULL_PTR_ERR;
3694 if(count != session->channel_count)
3695 return E_INVALIDARG;
3697 if(context)
3698 FIXME("Notifications not supported yet\n");
3700 TRACE("ALSA does not support volume control\n");
3702 EnterCriticalSection(&session->lock);
3704 for(i = 0; i < count; ++i)
3705 session->channel_vols[i] = levels[i];
3707 LeaveCriticalSection(&session->lock);
3709 return S_OK;
3712 static HRESULT WINAPI ChannelAudioVolume_GetAllVolumes(
3713 IChannelAudioVolume *iface, UINT32 count, float *levels)
3715 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
3716 AudioSession *session = This->session;
3717 unsigned int i;
3719 TRACE("(%p)->(%d, %p)\n", session, count, levels);
3721 if(!levels)
3722 return NULL_PTR_ERR;
3724 if(count != session->channel_count)
3725 return E_INVALIDARG;
3727 for(i = 0; i < count; ++i)
3728 levels[i] = session->channel_vols[i];
3730 return S_OK;
3733 static const IChannelAudioVolumeVtbl ChannelAudioVolume_Vtbl =
3735 ChannelAudioVolume_QueryInterface,
3736 ChannelAudioVolume_AddRef,
3737 ChannelAudioVolume_Release,
3738 ChannelAudioVolume_GetChannelCount,
3739 ChannelAudioVolume_SetChannelVolume,
3740 ChannelAudioVolume_GetChannelVolume,
3741 ChannelAudioVolume_SetAllVolumes,
3742 ChannelAudioVolume_GetAllVolumes
3745 static HRESULT WINAPI AudioSessionManager_QueryInterface(IAudioSessionManager2 *iface,
3746 REFIID riid, void **ppv)
3748 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
3750 if(!ppv)
3751 return E_POINTER;
3752 *ppv = NULL;
3754 if(IsEqualIID(riid, &IID_IUnknown) ||
3755 IsEqualIID(riid, &IID_IAudioSessionManager) ||
3756 IsEqualIID(riid, &IID_IAudioSessionManager2))
3757 *ppv = iface;
3758 if(*ppv){
3759 IUnknown_AddRef((IUnknown*)*ppv);
3760 return S_OK;
3763 WARN("Unknown interface %s\n", debugstr_guid(riid));
3764 return E_NOINTERFACE;
3767 static ULONG WINAPI AudioSessionManager_AddRef(IAudioSessionManager2 *iface)
3769 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3770 ULONG ref;
3771 ref = InterlockedIncrement(&This->ref);
3772 TRACE("(%p) Refcount now %u\n", This, ref);
3773 return ref;
3776 static ULONG WINAPI AudioSessionManager_Release(IAudioSessionManager2 *iface)
3778 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3779 ULONG ref;
3780 ref = InterlockedDecrement(&This->ref);
3781 TRACE("(%p) Refcount now %u\n", This, ref);
3782 if(!ref)
3783 HeapFree(GetProcessHeap(), 0, This);
3784 return ref;
3787 static HRESULT WINAPI AudioSessionManager_GetAudioSessionControl(
3788 IAudioSessionManager2 *iface, const GUID *session_guid, DWORD flags,
3789 IAudioSessionControl **out)
3791 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3792 AudioSession *session;
3793 AudioSessionWrapper *wrapper;
3794 HRESULT hr;
3796 TRACE("(%p)->(%s, %x, %p)\n", This, debugstr_guid(session_guid),
3797 flags, out);
3799 hr = get_audio_session(session_guid, This->device, 0, &session);
3800 if(FAILED(hr))
3801 return hr;
3803 wrapper = AudioSessionWrapper_Create(NULL);
3804 if(!wrapper)
3805 return E_OUTOFMEMORY;
3807 wrapper->session = session;
3809 *out = (IAudioSessionControl*)&wrapper->IAudioSessionControl2_iface;
3811 return S_OK;
3814 static HRESULT WINAPI AudioSessionManager_GetSimpleAudioVolume(
3815 IAudioSessionManager2 *iface, const GUID *session_guid, DWORD flags,
3816 ISimpleAudioVolume **out)
3818 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3819 AudioSession *session;
3820 AudioSessionWrapper *wrapper;
3821 HRESULT hr;
3823 TRACE("(%p)->(%s, %x, %p)\n", This, debugstr_guid(session_guid),
3824 flags, out);
3826 hr = get_audio_session(session_guid, This->device, 0, &session);
3827 if(FAILED(hr))
3828 return hr;
3830 wrapper = AudioSessionWrapper_Create(NULL);
3831 if(!wrapper)
3832 return E_OUTOFMEMORY;
3834 wrapper->session = session;
3836 *out = &wrapper->ISimpleAudioVolume_iface;
3838 return S_OK;
3841 static HRESULT WINAPI AudioSessionManager_GetSessionEnumerator(
3842 IAudioSessionManager2 *iface, IAudioSessionEnumerator **out)
3844 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3845 FIXME("(%p)->(%p) - stub\n", This, out);
3846 return E_NOTIMPL;
3849 static HRESULT WINAPI AudioSessionManager_RegisterSessionNotification(
3850 IAudioSessionManager2 *iface, IAudioSessionNotification *notification)
3852 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3853 FIXME("(%p)->(%p) - stub\n", This, notification);
3854 return E_NOTIMPL;
3857 static HRESULT WINAPI AudioSessionManager_UnregisterSessionNotification(
3858 IAudioSessionManager2 *iface, IAudioSessionNotification *notification)
3860 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3861 FIXME("(%p)->(%p) - stub\n", This, notification);
3862 return E_NOTIMPL;
3865 static HRESULT WINAPI AudioSessionManager_RegisterDuckNotification(
3866 IAudioSessionManager2 *iface, const WCHAR *session_id,
3867 IAudioVolumeDuckNotification *notification)
3869 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3870 FIXME("(%p)->(%p) - stub\n", This, notification);
3871 return E_NOTIMPL;
3874 static HRESULT WINAPI AudioSessionManager_UnregisterDuckNotification(
3875 IAudioSessionManager2 *iface,
3876 IAudioVolumeDuckNotification *notification)
3878 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3879 FIXME("(%p)->(%p) - stub\n", This, notification);
3880 return E_NOTIMPL;
3883 static const IAudioSessionManager2Vtbl AudioSessionManager2_Vtbl =
3885 AudioSessionManager_QueryInterface,
3886 AudioSessionManager_AddRef,
3887 AudioSessionManager_Release,
3888 AudioSessionManager_GetAudioSessionControl,
3889 AudioSessionManager_GetSimpleAudioVolume,
3890 AudioSessionManager_GetSessionEnumerator,
3891 AudioSessionManager_RegisterSessionNotification,
3892 AudioSessionManager_UnregisterSessionNotification,
3893 AudioSessionManager_RegisterDuckNotification,
3894 AudioSessionManager_UnregisterDuckNotification
3897 HRESULT WINAPI AUDDRV_GetAudioSessionManager(IMMDevice *device,
3898 IAudioSessionManager2 **out)
3900 SessionMgr *This;
3902 This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(SessionMgr));
3903 if(!This)
3904 return E_OUTOFMEMORY;
3906 This->IAudioSessionManager2_iface.lpVtbl = &AudioSessionManager2_Vtbl;
3907 This->device = device;
3908 This->ref = 1;
3910 *out = &This->IAudioSessionManager2_iface;
3912 return S_OK;
3915 static unsigned int alsa_probe_num_speakers(char *name) {
3916 snd_pcm_t *handle;
3917 snd_pcm_hw_params_t *params;
3918 int err;
3919 unsigned int max_channels = 0;
3921 if ((err = snd_pcm_open(&handle, name, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK)) < 0) {
3922 WARN("The device \"%s\" failed to open: %d (%s).\n",
3923 name, err, snd_strerror(err));
3924 return 0;
3927 params = HeapAlloc(GetProcessHeap(), 0, snd_pcm_hw_params_sizeof());
3928 if (!params) {
3929 WARN("Out of memory.\n");
3930 snd_pcm_close(handle);
3931 return 0;
3934 if ((err = snd_pcm_hw_params_any(handle, params)) < 0) {
3935 WARN("snd_pcm_hw_params_any failed for \"%s\": %d (%s).\n",
3936 name, err, snd_strerror(err));
3937 goto exit;
3940 if ((err = snd_pcm_hw_params_get_channels_max(params,
3941 &max_channels)) < 0){
3942 WARN("Unable to get max channels: %d (%s)\n", err, snd_strerror(err));
3943 goto exit;
3946 exit:
3947 HeapFree(GetProcessHeap(), 0, params);
3948 snd_pcm_close(handle);
3950 return max_channels;
3953 enum AudioDeviceConnectionType {
3954 AudioDeviceConnectionType_Unknown = 0,
3955 AudioDeviceConnectionType_PCI,
3956 AudioDeviceConnectionType_USB
3959 HRESULT WINAPI AUDDRV_GetPropValue(GUID *guid, const PROPERTYKEY *prop, PROPVARIANT *out)
3961 char name[256];
3962 EDataFlow flow;
3964 static const PROPERTYKEY devicepath_key = { /* undocumented? - {b3f8fa53-0004-438e-9003-51a46e139bfc},2 */
3965 {0xb3f8fa53, 0x0004, 0x438e, {0x90, 0x03, 0x51, 0xa4, 0x6e, 0x13, 0x9b, 0xfc}}, 2
3968 TRACE("%s, (%s,%u), %p\n", wine_dbgstr_guid(guid), wine_dbgstr_guid(&prop->fmtid), prop->pid, out);
3970 if(!get_alsa_name_by_guid(guid, name, sizeof(name), &flow))
3972 WARN("Unknown interface %s\n", debugstr_guid(guid));
3973 return E_NOINTERFACE;
3976 if(IsEqualPropertyKey(*prop, devicepath_key))
3978 char uevent[MAX_PATH];
3979 FILE *fuevent;
3980 int card, device;
3982 /* only implemented for identifiable devices, i.e. not "default" */
3983 if(!sscanf(name, "plughw:%u,%u", &card, &device))
3984 return E_NOTIMPL;
3986 sprintf(uevent, "/sys/class/sound/card%u/device/uevent", card);
3987 fuevent = fopen(uevent, "r");
3989 if(fuevent){
3990 enum AudioDeviceConnectionType connection = AudioDeviceConnectionType_Unknown;
3991 USHORT vendor_id = 0, product_id = 0;
3992 char line[256];
3994 while (fgets(line, sizeof(line), fuevent)) {
3995 char *val;
3996 size_t val_len;
3998 if((val = strchr(line, '='))) {
3999 val[0] = 0;
4000 val++;
4002 val_len = strlen(val);
4003 if(val_len > 0 && val[val_len - 1] == '\n') { val[val_len - 1] = 0; }
4005 if(!strcmp(line, "PCI_ID")){
4006 connection = AudioDeviceConnectionType_PCI;
4007 if(sscanf(val, "%hX:%hX", &vendor_id, &product_id)<2){
4008 WARN("Unexpected input when reading PCI_ID in uevent file.\n");
4009 connection = AudioDeviceConnectionType_Unknown;
4010 break;
4012 }else if(!strcmp(line, "DEVTYPE") && !strcmp(val,"usb_interface"))
4013 connection = AudioDeviceConnectionType_USB;
4014 else if(!strcmp(line, "PRODUCT"))
4015 if(sscanf(val, "%hx/%hx/", &vendor_id, &product_id)<2){
4016 WARN("Unexpected input when reading PRODUCT in uevent file.\n");
4017 connection = AudioDeviceConnectionType_Unknown;
4018 break;
4023 fclose(fuevent);
4025 if(connection == AudioDeviceConnectionType_USB || connection == AudioDeviceConnectionType_PCI){
4026 static const WCHAR usbformatW[] = { '{','1','}','.','U','S','B','\\','V','I','D','_',
4027 '%','0','4','X','&','P','I','D','_','%','0','4','X','\\',
4028 '%','u','&','%','0','8','X',0 }; /* "{1}.USB\VID_%04X&PID_%04X\%u&%08X" */
4029 static const WCHAR pciformatW[] = { '{','1','}','.','H','D','A','U','D','I','O','\\','F','U','N','C','_','0','1','&',
4030 'V','E','N','_','%','0','4','X','&','D','E','V','_',
4031 '%','0','4','X','\\','%','u','&','%','0','8','X',0 }; /* "{1}.HDAUDIO\FUNC_01&VEN_%04X&DEV_%04X\%u&%08X" */
4032 UINT serial_number;
4034 /* As hardly any audio devices have serial numbers, Windows instead
4035 appears to use a persistent random number. We emulate this here
4036 by instead using the last 8 hex digits of the GUID. */
4037 serial_number = (guid->Data4[4] << 24) | (guid->Data4[5] << 16) | (guid->Data4[6] << 8) | guid->Data4[7];
4039 out->vt = VT_LPWSTR;
4040 out->u.pwszVal = CoTaskMemAlloc(128 * sizeof(WCHAR));
4042 if(!out->u.pwszVal)
4043 return E_OUTOFMEMORY;
4045 if(connection == AudioDeviceConnectionType_USB)
4046 sprintfW( out->u.pwszVal, usbformatW, vendor_id, product_id, device, serial_number);
4047 else if(connection == AudioDeviceConnectionType_PCI)
4048 sprintfW( out->u.pwszVal, pciformatW, vendor_id, product_id, device, serial_number);
4050 return S_OK;
4052 }else{
4053 WARN("Could not open %s for reading\n", uevent);
4054 return E_NOTIMPL;
4056 } else if (flow != eCapture && IsEqualPropertyKey(*prop, PKEY_AudioEndpoint_PhysicalSpeakers)) {
4057 unsigned int num_speakers, card, device;
4058 char hwname[255];
4060 if (sscanf(name, "plughw:%u,%u", &card, &device))
4061 sprintf(hwname, "hw:%u,%u", card, device); /* must be hw rather than plughw to work */
4062 else
4063 strcpy(hwname, name);
4065 num_speakers = alsa_probe_num_speakers(hwname);
4066 if (num_speakers == 0)
4067 return E_FAIL;
4069 out->vt = VT_UI4;
4071 if (num_speakers > 6)
4072 out->u.ulVal = KSAUDIO_SPEAKER_STEREO;
4073 else if (num_speakers == 6)
4074 out->u.ulVal = KSAUDIO_SPEAKER_5POINT1;
4075 else if (num_speakers >= 4)
4076 out->u.ulVal = KSAUDIO_SPEAKER_QUAD;
4077 else if (num_speakers >= 2)
4078 out->u.ulVal = KSAUDIO_SPEAKER_STEREO;
4079 else if (num_speakers == 1)
4080 out->u.ulVal = KSAUDIO_SPEAKER_MONO;
4082 return S_OK;
4085 TRACE("Unimplemented property %s,%u\n", wine_dbgstr_guid(&prop->fmtid), prop->pid);
4087 return E_NOTIMPL;