comctl32/tests: Avoid a structure initialization warning.
[wine.git] / dlls / winealsa.drv / mmdevdrv.c
blobde89fd05c5882a63d45cacd2858885060c8b41ee
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, ARRAY_SIZE(key_name) - 2);
316 if(RegOpenKeyExW(HKEY_CURRENT_USER, drv_key_devicesW, 0, KEY_WRITE|KEY_READ, &key) == ERROR_SUCCESS){
317 if(RegOpenKeyExW(key, key_name, 0, KEY_READ, &dev_key) == ERROR_SUCCESS){
318 if(RegQueryValueExW(dev_key, guidW, 0, &type,
319 (BYTE*)guid, &size) == ERROR_SUCCESS){
320 if(type == REG_BINARY){
321 RegCloseKey(dev_key);
322 RegCloseKey(key);
323 return;
325 ERR("Invalid type for device %s GUID: %u; ignoring and overwriting\n",
326 wine_dbgstr_w(key_name), type);
328 RegCloseKey(dev_key);
332 CoCreateGuid(guid);
334 set_device_guid(flow, key, key_name, guid);
336 if(key)
337 RegCloseKey(key);
340 static BOOL alsa_try_open(const char *devnode, snd_pcm_stream_t stream)
342 snd_pcm_t *handle;
343 int err;
345 TRACE("devnode: %s, stream: %d\n", devnode, stream);
347 if((err = snd_pcm_open(&handle, devnode, stream, SND_PCM_NONBLOCK)) < 0){
348 WARN("The device \"%s\" failed to open: %d (%s).\n",
349 devnode, err, snd_strerror(err));
350 return FALSE;
353 snd_pcm_close(handle);
354 return TRUE;
357 static WCHAR *construct_device_id(EDataFlow flow, const WCHAR *chunk1, const char *chunk2)
359 WCHAR *ret;
360 const WCHAR *prefix;
361 DWORD len_wchars = 0, chunk1_len = 0, copied = 0, prefix_len;
363 static const WCHAR dashW[] = {' ','-',' ',0};
364 static const size_t dashW_len = ARRAY_SIZE(dashW) - 1;
365 static const WCHAR outW[] = {'O','u','t',':',' ',0};
366 static const WCHAR inW[] = {'I','n',':',' ',0};
368 if(flow == eRender){
369 prefix = outW;
370 prefix_len = ARRAY_SIZE(outW) - 1;
371 len_wchars += prefix_len;
372 }else{
373 prefix = inW;
374 prefix_len = ARRAY_SIZE(inW) - 1;
375 len_wchars += prefix_len;
377 if(chunk1){
378 chunk1_len = strlenW(chunk1);
379 len_wchars += chunk1_len;
381 if(chunk1 && chunk2)
382 len_wchars += dashW_len;
383 if(chunk2)
384 len_wchars += MultiByteToWideChar(CP_UNIXCP, 0, chunk2, -1, NULL, 0) - 1;
385 len_wchars += 1; /* NULL byte */
387 ret = HeapAlloc(GetProcessHeap(), 0, len_wchars * sizeof(WCHAR));
389 memcpy(ret, prefix, prefix_len * sizeof(WCHAR));
390 copied += prefix_len;
391 if(chunk1){
392 memcpy(ret + copied, chunk1, chunk1_len * sizeof(WCHAR));
393 copied += chunk1_len;
395 if(chunk1 && chunk2){
396 memcpy(ret + copied, dashW, dashW_len * sizeof(WCHAR));
397 copied += dashW_len;
399 if(chunk2){
400 MultiByteToWideChar(CP_UNIXCP, 0, chunk2, -1, ret + copied, len_wchars - copied);
401 }else
402 ret[copied] = 0;
404 TRACE("Enumerated device: %s\n", wine_dbgstr_w(ret));
406 return ret;
409 static HRESULT alsa_get_card_devices(EDataFlow flow, snd_pcm_stream_t stream,
410 WCHAR ***ids, GUID **guids, UINT *num, snd_ctl_t *ctl, int card,
411 const WCHAR *cardnameW)
413 int err, device;
414 snd_pcm_info_t *info;
416 info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, snd_pcm_info_sizeof());
417 if(!info)
418 return E_OUTOFMEMORY;
420 snd_pcm_info_set_subdevice(info, 0);
421 snd_pcm_info_set_stream(info, stream);
423 device = -1;
424 for(err = snd_ctl_pcm_next_device(ctl, &device); device != -1 && err >= 0;
425 err = snd_ctl_pcm_next_device(ctl, &device)){
426 const char *devname;
427 char devnode[32];
429 snd_pcm_info_set_device(info, device);
431 if((err = snd_ctl_pcm_info(ctl, info)) < 0){
432 if(err == -ENOENT)
433 /* This device doesn't have the right stream direction */
434 continue;
436 WARN("Failed to get info for card %d, device %d: %d (%s)\n",
437 card, device, err, snd_strerror(err));
438 continue;
441 sprintf(devnode, "plughw:%d,%d", card, device);
442 if(!alsa_try_open(devnode, stream))
443 continue;
445 if(*num){
446 *ids = HeapReAlloc(GetProcessHeap(), 0, *ids, sizeof(WCHAR *) * (*num + 1));
447 *guids = HeapReAlloc(GetProcessHeap(), 0, *guids, sizeof(GUID) * (*num + 1));
448 }else{
449 *ids = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR *));
450 *guids = HeapAlloc(GetProcessHeap(), 0, sizeof(GUID));
453 devname = snd_pcm_info_get_name(info);
454 if(!devname){
455 WARN("Unable to get device name for card %d, device %d\n", card,
456 device);
457 continue;
460 (*ids)[*num] = construct_device_id(flow, cardnameW, devname);
461 get_device_guid(flow, devnode, &(*guids)[*num]);
463 ++(*num);
466 HeapFree(GetProcessHeap(), 0, info);
468 if(err != 0)
469 WARN("Got a failure during device enumeration on card %d: %d (%s)\n",
470 card, err, snd_strerror(err));
472 return S_OK;
475 static void get_reg_devices(EDataFlow flow, snd_pcm_stream_t stream, WCHAR ***ids,
476 GUID **guids, UINT *num)
478 static const WCHAR ALSAOutputDevices[] = {'A','L','S','A','O','u','t','p','u','t','D','e','v','i','c','e','s',0};
479 static const WCHAR ALSAInputDevices[] = {'A','L','S','A','I','n','p','u','t','D','e','v','i','c','e','s',0};
480 HKEY key;
481 WCHAR reg_devices[256];
482 DWORD size = sizeof(reg_devices), type;
483 const WCHAR *value_name = (stream == SND_PCM_STREAM_PLAYBACK) ? ALSAOutputDevices : ALSAInputDevices;
485 /* @@ Wine registry key: HKCU\Software\Wine\Drivers\winealsa.drv */
486 if(RegOpenKeyW(HKEY_CURRENT_USER, drv_keyW, &key) == ERROR_SUCCESS){
487 if(RegQueryValueExW(key, value_name, 0, &type,
488 (BYTE*)reg_devices, &size) == ERROR_SUCCESS){
489 WCHAR *p = reg_devices;
491 if(type != REG_MULTI_SZ){
492 ERR("Registry ALSA device list value type must be REG_MULTI_SZ\n");
493 RegCloseKey(key);
494 return;
497 while(*p){
498 char devname[64];
500 WideCharToMultiByte(CP_UNIXCP, 0, p, -1, devname, sizeof(devname), NULL, NULL);
502 if(alsa_try_open(devname, stream)){
503 if(*num){
504 *ids = HeapReAlloc(GetProcessHeap(), 0, *ids, sizeof(WCHAR *) * (*num + 1));
505 *guids = HeapReAlloc(GetProcessHeap(), 0, *guids, sizeof(GUID) * (*num + 1));
506 }else{
507 *ids = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR *));
508 *guids = HeapAlloc(GetProcessHeap(), 0, sizeof(GUID));
510 (*ids)[*num] = construct_device_id(flow, p, NULL);
511 get_device_guid(flow, devname, &(*guids)[*num]);
512 ++*num;
515 p += lstrlenW(p) + 1;
519 RegCloseKey(key);
523 static HRESULT alsa_enum_devices(EDataFlow flow, WCHAR ***ids, GUID **guids,
524 UINT *num)
526 snd_pcm_stream_t stream = (flow == eRender ? SND_PCM_STREAM_PLAYBACK :
527 SND_PCM_STREAM_CAPTURE);
528 int err, card;
530 card = -1;
531 *num = 0;
533 if(alsa_try_open(defname, stream)){
534 *ids = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR *));
535 (*ids)[0] = construct_device_id(flow, defaultW, NULL);
536 *guids = HeapAlloc(GetProcessHeap(), 0, sizeof(GUID));
537 get_device_guid(flow, defname, &(*guids)[0]);
538 ++*num;
541 get_reg_devices(flow, stream, ids, guids, num);
543 for(err = snd_card_next(&card); card != -1 && err >= 0;
544 err = snd_card_next(&card)){
545 char cardpath[64];
546 char *cardname;
547 WCHAR *cardnameW;
548 snd_ctl_t *ctl;
549 DWORD len;
551 sprintf(cardpath, "hw:%u", card);
553 if((err = snd_ctl_open(&ctl, cardpath, 0)) < 0){
554 WARN("Unable to open ctl for ALSA device %s: %d (%s)\n", cardpath,
555 err, snd_strerror(err));
556 continue;
559 if(snd_card_get_name(card, &cardname) < 0) {
560 /* FIXME: Should be localized */
561 static const WCHAR nameW[] = {'U','n','k','n','o','w','n',' ','s','o','u','n','d','c','a','r','d',0};
562 WARN("Unable to get card name for ALSA device %s: %d (%s)\n",
563 cardpath, err, snd_strerror(err));
564 alsa_get_card_devices(flow, stream, ids, guids, num, ctl, card, nameW);
565 }else{
566 len = MultiByteToWideChar(CP_UNIXCP, 0, cardname, -1, NULL, 0);
567 cardnameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
569 if(!cardnameW){
570 free(cardname);
571 snd_ctl_close(ctl);
572 return E_OUTOFMEMORY;
574 MultiByteToWideChar(CP_UNIXCP, 0, cardname, -1, cardnameW, len);
576 alsa_get_card_devices(flow, stream, ids, guids, num, ctl, card, cardnameW);
578 HeapFree(GetProcessHeap(), 0, cardnameW);
579 free(cardname);
582 snd_ctl_close(ctl);
585 if(err != 0)
586 WARN("Got a failure during card enumeration: %d (%s)\n",
587 err, snd_strerror(err));
589 return S_OK;
592 HRESULT WINAPI AUDDRV_GetEndpointIDs(EDataFlow flow, WCHAR ***ids, GUID **guids,
593 UINT *num, UINT *def_index)
595 HRESULT hr;
597 TRACE("%d %p %p %p %p\n", flow, ids, guids, num, def_index);
599 *ids = NULL;
600 *guids = NULL;
602 hr = alsa_enum_devices(flow, ids, guids, num);
603 if(FAILED(hr)){
604 UINT i;
605 for(i = 0; i < *num; ++i)
606 HeapFree(GetProcessHeap(), 0, (*ids)[i]);
607 HeapFree(GetProcessHeap(), 0, *ids);
608 HeapFree(GetProcessHeap(), 0, *guids);
609 return E_OUTOFMEMORY;
612 TRACE("Enumerated %u devices\n", *num);
614 if(*num == 0){
615 HeapFree(GetProcessHeap(), 0, *ids);
616 *ids = NULL;
617 HeapFree(GetProcessHeap(), 0, *guids);
618 *guids = NULL;
621 *def_index = 0;
623 return S_OK;
626 /* Using the pulse PCM device from alsa-plugins 1.0.24 triggers a bug
627 * which causes audio to cease playing after a few minutes of playback.
628 * Setting handle_underrun=1 on pulse-backed ALSA devices seems to work
629 * around this issue. */
630 static snd_config_t *make_handle_underrun_config(const char *name)
632 snd_config_t *lconf, *dev_node, *hu_node, *type_node;
633 char dev_node_name[64];
634 const char *type_str;
635 int err;
637 snd_config_update();
639 if((err = snd_config_copy(&lconf, snd_config)) < 0){
640 WARN("snd_config_copy failed: %d (%s)\n", err, snd_strerror(err));
641 return NULL;
644 sprintf(dev_node_name, "pcm.%s", name);
645 err = snd_config_search(lconf, dev_node_name, &dev_node);
646 if(err == -ENOENT){
647 snd_config_delete(lconf);
648 return NULL;
650 if(err < 0){
651 snd_config_delete(lconf);
652 WARN("snd_config_search failed: %d (%s)\n", err, snd_strerror(err));
653 return NULL;
656 /* ALSA is extremely fragile. If it runs into a config setting it doesn't
657 * recognize, it tends to fail or assert. So we only want to inject
658 * handle_underrun=1 on devices that we know will recognize it. */
659 err = snd_config_search(dev_node, "type", &type_node);
660 if(err == -ENOENT){
661 snd_config_delete(lconf);
662 return NULL;
664 if(err < 0){
665 snd_config_delete(lconf);
666 WARN("snd_config_search failed: %d (%s)\n", err, snd_strerror(err));
667 return NULL;
670 if((err = snd_config_get_string(type_node, &type_str)) < 0){
671 snd_config_delete(lconf);
672 return NULL;
675 if(strcmp(type_str, "pulse") != 0){
676 snd_config_delete(lconf);
677 return NULL;
680 err = snd_config_search(dev_node, "handle_underrun", &hu_node);
681 if(err >= 0){
682 /* user already has an explicit handle_underrun setting, so don't
683 * use a local config */
684 snd_config_delete(lconf);
685 return NULL;
687 if(err != -ENOENT){
688 snd_config_delete(lconf);
689 WARN("snd_config_search failed: %d (%s)\n", err, snd_strerror(err));
690 return NULL;
693 if((err = snd_config_imake_integer(&hu_node, "handle_underrun", 1)) < 0){
694 snd_config_delete(lconf);
695 WARN("snd_config_imake_integer failed: %d (%s)\n", err,
696 snd_strerror(err));
697 return NULL;
700 if((err = snd_config_add(dev_node, hu_node)) < 0){
701 snd_config_delete(lconf);
702 WARN("snd_config_add failed: %d (%s)\n", err, snd_strerror(err));
703 return NULL;
706 return lconf;
709 static BOOL get_alsa_name_by_guid(GUID *guid, char *name, DWORD name_size, EDataFlow *flow)
711 HKEY devices_key;
712 UINT i = 0;
713 WCHAR key_name[256];
714 DWORD key_name_size;
716 if(RegOpenKeyExW(HKEY_CURRENT_USER, drv_key_devicesW, 0, KEY_READ, &devices_key) != ERROR_SUCCESS){
717 ERR("No devices found in registry?\n");
718 return FALSE;
721 while(1){
722 HKEY key;
723 DWORD size, type;
724 GUID reg_guid;
726 key_name_size = ARRAY_SIZE(key_name);
727 if(RegEnumKeyExW(devices_key, i++, key_name, &key_name_size, NULL,
728 NULL, NULL, NULL) != ERROR_SUCCESS)
729 break;
731 if(RegOpenKeyExW(devices_key, key_name, 0, KEY_READ, &key) != ERROR_SUCCESS){
732 WARN("Couldn't open key: %s\n", wine_dbgstr_w(key_name));
733 continue;
736 size = sizeof(reg_guid);
737 if(RegQueryValueExW(key, guidW, 0, &type,
738 (BYTE*)&reg_guid, &size) == ERROR_SUCCESS){
739 if(IsEqualGUID(&reg_guid, guid)){
740 RegCloseKey(key);
741 RegCloseKey(devices_key);
743 TRACE("Found matching device key: %s\n", wine_dbgstr_w(key_name));
745 if(key_name[0] == '0')
746 *flow = eRender;
747 else if(key_name[0] == '1')
748 *flow = eCapture;
749 else{
750 ERR("Unknown device type: %c\n", key_name[0]);
751 return FALSE;
754 WideCharToMultiByte(CP_UNIXCP, 0, key_name + 2, -1, name, name_size, NULL, NULL);
756 return TRUE;
760 RegCloseKey(key);
763 RegCloseKey(devices_key);
765 WARN("No matching device in registry for GUID %s\n", debugstr_guid(guid));
767 return FALSE;
770 HRESULT WINAPI AUDDRV_GetAudioEndpoint(GUID *guid, IMMDevice *dev, IAudioClient **out)
772 ACImpl *This;
773 int err;
774 snd_pcm_stream_t stream;
775 snd_config_t *lconf;
776 static BOOL handle_underrun = TRUE;
777 char alsa_name[256];
778 EDataFlow dataflow;
779 HRESULT hr;
781 TRACE("%s %p %p\n", debugstr_guid(guid), dev, out);
783 if(!get_alsa_name_by_guid(guid, alsa_name, sizeof(alsa_name), &dataflow))
784 return AUDCLNT_E_DEVICE_INVALIDATED;
786 This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(ACImpl));
787 if(!This)
788 return E_OUTOFMEMORY;
790 This->IAudioClient_iface.lpVtbl = &AudioClient_Vtbl;
791 This->IAudioRenderClient_iface.lpVtbl = &AudioRenderClient_Vtbl;
792 This->IAudioCaptureClient_iface.lpVtbl = &AudioCaptureClient_Vtbl;
793 This->IAudioClock_iface.lpVtbl = &AudioClock_Vtbl;
794 This->IAudioClock2_iface.lpVtbl = &AudioClock2_Vtbl;
795 This->IAudioStreamVolume_iface.lpVtbl = &AudioStreamVolume_Vtbl;
797 if(dataflow == eRender)
798 stream = SND_PCM_STREAM_PLAYBACK;
799 else if(dataflow == eCapture)
800 stream = SND_PCM_STREAM_CAPTURE;
801 else{
802 HeapFree(GetProcessHeap(), 0, This);
803 return E_UNEXPECTED;
806 hr = CoCreateFreeThreadedMarshaler((IUnknown *)&This->IAudioClient_iface,
807 (IUnknown **)&This->pUnkFTMarshal);
808 if (FAILED(hr)) {
809 HeapFree(GetProcessHeap(), 0, This);
810 return hr;
813 This->dataflow = dataflow;
814 if(handle_underrun && ((lconf = make_handle_underrun_config(alsa_name)))){
815 err = snd_pcm_open_lconf(&This->pcm_handle, alsa_name, stream, SND_PCM_NONBLOCK, lconf);
816 TRACE("Opening PCM device \"%s\" with handle_underrun: %d\n", alsa_name, err);
817 snd_config_delete(lconf);
818 /* Pulse <= 2010 returns EINVAL, it does not know handle_underrun. */
819 if(err == -EINVAL){
820 ERR_(winediag)("PulseAudio \"%s\" %d without handle_underrun. Audio may hang."
821 " Please upgrade to alsa_plugins >= 1.0.24\n", alsa_name, err);
822 handle_underrun = FALSE;
824 }else
825 err = -EINVAL;
826 if(err == -EINVAL){
827 err = snd_pcm_open(&This->pcm_handle, alsa_name, stream, SND_PCM_NONBLOCK);
829 if(err < 0){
830 HeapFree(GetProcessHeap(), 0, This);
831 WARN("Unable to open PCM \"%s\": %d (%s)\n", alsa_name, err, snd_strerror(err));
832 switch(err){
833 case -EBUSY:
834 return AUDCLNT_E_DEVICE_IN_USE;
835 default:
836 return AUDCLNT_E_ENDPOINT_CREATE_FAILED;
840 This->hw_params = HeapAlloc(GetProcessHeap(), 0,
841 snd_pcm_hw_params_sizeof());
842 if(!This->hw_params){
843 snd_pcm_close(This->pcm_handle);
844 HeapFree(GetProcessHeap(), 0, This);
845 return E_OUTOFMEMORY;
848 InitializeCriticalSection(&This->lock);
849 This->lock.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": ACImpl.lock");
851 This->parent = dev;
852 IMMDevice_AddRef(This->parent);
854 *out = &This->IAudioClient_iface;
855 IAudioClient_AddRef(&This->IAudioClient_iface);
857 return S_OK;
860 static HRESULT WINAPI AudioClient_QueryInterface(IAudioClient *iface,
861 REFIID riid, void **ppv)
863 ACImpl *This = impl_from_IAudioClient(iface);
864 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
866 if(!ppv)
867 return E_POINTER;
868 *ppv = NULL;
869 if(IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IAudioClient))
870 *ppv = iface;
871 else if(IsEqualIID(riid, &IID_IMarshal))
872 return IUnknown_QueryInterface(This->pUnkFTMarshal, riid, ppv);
874 if(*ppv){
875 IUnknown_AddRef((IUnknown*)*ppv);
876 return S_OK;
878 WARN("Unknown interface %s\n", debugstr_guid(riid));
879 return E_NOINTERFACE;
882 static ULONG WINAPI AudioClient_AddRef(IAudioClient *iface)
884 ACImpl *This = impl_from_IAudioClient(iface);
885 ULONG ref;
886 ref = InterlockedIncrement(&This->ref);
887 TRACE("(%p) Refcount now %u\n", This, ref);
888 return ref;
891 static ULONG WINAPI AudioClient_Release(IAudioClient *iface)
893 ACImpl *This = impl_from_IAudioClient(iface);
894 ULONG ref;
896 ref = InterlockedDecrement(&This->ref);
897 TRACE("(%p) Refcount now %u\n", This, ref);
898 if(!ref){
899 if(This->timer){
900 HANDLE event;
901 DWORD wait;
902 event = CreateEventW(NULL, TRUE, FALSE, NULL);
903 wait = !DeleteTimerQueueTimer(g_timer_q, This->timer, event);
904 wait = wait && GetLastError() == ERROR_IO_PENDING;
905 if(event && wait)
906 WaitForSingleObject(event, INFINITE);
907 CloseHandle(event);
910 IAudioClient_Stop(iface);
911 IMMDevice_Release(This->parent);
912 IUnknown_Release(This->pUnkFTMarshal);
913 This->lock.DebugInfo->Spare[0] = 0;
914 DeleteCriticalSection(&This->lock);
915 snd_pcm_drop(This->pcm_handle);
916 snd_pcm_close(This->pcm_handle);
917 if(This->initted){
918 EnterCriticalSection(&g_sessions_lock);
919 list_remove(&This->entry);
920 LeaveCriticalSection(&g_sessions_lock);
922 HeapFree(GetProcessHeap(), 0, This->vols);
923 HeapFree(GetProcessHeap(), 0, This->local_buffer);
924 HeapFree(GetProcessHeap(), 0, This->remapping_buf);
925 HeapFree(GetProcessHeap(), 0, This->silence_buf);
926 HeapFree(GetProcessHeap(), 0, This->tmp_buffer);
927 HeapFree(GetProcessHeap(), 0, This->hw_params);
928 CoTaskMemFree(This->fmt);
929 HeapFree(GetProcessHeap(), 0, This);
931 return ref;
934 static void dump_fmt(const WAVEFORMATEX *fmt)
936 TRACE("wFormatTag: 0x%x (", fmt->wFormatTag);
937 switch(fmt->wFormatTag){
938 case WAVE_FORMAT_PCM:
939 TRACE("WAVE_FORMAT_PCM");
940 break;
941 case WAVE_FORMAT_IEEE_FLOAT:
942 TRACE("WAVE_FORMAT_IEEE_FLOAT");
943 break;
944 case WAVE_FORMAT_EXTENSIBLE:
945 TRACE("WAVE_FORMAT_EXTENSIBLE");
946 break;
947 default:
948 TRACE("Unknown");
949 break;
951 TRACE(")\n");
953 TRACE("nChannels: %u\n", fmt->nChannels);
954 TRACE("nSamplesPerSec: %u\n", fmt->nSamplesPerSec);
955 TRACE("nAvgBytesPerSec: %u\n", fmt->nAvgBytesPerSec);
956 TRACE("nBlockAlign: %u\n", fmt->nBlockAlign);
957 TRACE("wBitsPerSample: %u\n", fmt->wBitsPerSample);
958 TRACE("cbSize: %u\n", fmt->cbSize);
960 if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE){
961 WAVEFORMATEXTENSIBLE *fmtex = (void*)fmt;
962 TRACE("dwChannelMask: %08x\n", fmtex->dwChannelMask);
963 TRACE("Samples: %04x\n", fmtex->Samples.wReserved);
964 TRACE("SubFormat: %s\n", wine_dbgstr_guid(&fmtex->SubFormat));
968 static WAVEFORMATEX *clone_format(const WAVEFORMATEX *fmt)
970 WAVEFORMATEX *ret;
971 size_t size;
973 if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
974 size = sizeof(WAVEFORMATEXTENSIBLE);
975 else
976 size = sizeof(WAVEFORMATEX);
978 ret = CoTaskMemAlloc(size);
979 if(!ret)
980 return NULL;
982 memcpy(ret, fmt, size);
984 ret->cbSize = size - sizeof(WAVEFORMATEX);
986 return ret;
989 static snd_pcm_format_t alsa_format(const WAVEFORMATEX *fmt)
991 snd_pcm_format_t format = SND_PCM_FORMAT_UNKNOWN;
992 const WAVEFORMATEXTENSIBLE *fmtex = (const WAVEFORMATEXTENSIBLE *)fmt;
994 if(fmt->wFormatTag == WAVE_FORMAT_PCM ||
995 (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
996 IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))){
997 if(fmt->wBitsPerSample == 8)
998 format = SND_PCM_FORMAT_U8;
999 else if(fmt->wBitsPerSample == 16)
1000 format = SND_PCM_FORMAT_S16_LE;
1001 else if(fmt->wBitsPerSample == 24)
1002 format = SND_PCM_FORMAT_S24_3LE;
1003 else if(fmt->wBitsPerSample == 32)
1004 format = SND_PCM_FORMAT_S32_LE;
1005 else
1006 WARN("Unsupported bit depth: %u\n", fmt->wBitsPerSample);
1007 if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
1008 fmt->wBitsPerSample != fmtex->Samples.wValidBitsPerSample){
1009 if(fmtex->Samples.wValidBitsPerSample == 20 && fmt->wBitsPerSample == 24)
1010 format = SND_PCM_FORMAT_S20_3LE;
1011 else
1012 WARN("Unsupported ValidBits: %u\n", fmtex->Samples.wValidBitsPerSample);
1014 }else if(fmt->wFormatTag == WAVE_FORMAT_IEEE_FLOAT ||
1015 (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
1016 IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT))){
1017 if(fmt->wBitsPerSample == 32)
1018 format = SND_PCM_FORMAT_FLOAT_LE;
1019 else if(fmt->wBitsPerSample == 64)
1020 format = SND_PCM_FORMAT_FLOAT64_LE;
1021 else
1022 WARN("Unsupported float size: %u\n", fmt->wBitsPerSample);
1023 }else
1024 WARN("Unknown wave format: %04x\n", fmt->wFormatTag);
1025 return format;
1028 static void session_init_vols(AudioSession *session, UINT channels)
1030 if(session->channel_count < channels){
1031 UINT i;
1033 if(session->channel_vols)
1034 session->channel_vols = HeapReAlloc(GetProcessHeap(), 0,
1035 session->channel_vols, sizeof(float) * channels);
1036 else
1037 session->channel_vols = HeapAlloc(GetProcessHeap(), 0,
1038 sizeof(float) * channels);
1039 if(!session->channel_vols)
1040 return;
1042 for(i = session->channel_count; i < channels; ++i)
1043 session->channel_vols[i] = 1.f;
1045 session->channel_count = channels;
1049 static AudioSession *create_session(const GUID *guid, IMMDevice *device,
1050 UINT num_channels)
1052 AudioSession *ret;
1054 ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(AudioSession));
1055 if(!ret)
1056 return NULL;
1058 memcpy(&ret->guid, guid, sizeof(GUID));
1060 ret->device = device;
1062 list_init(&ret->clients);
1064 list_add_head(&g_sessions, &ret->entry);
1066 InitializeCriticalSection(&ret->lock);
1067 ret->lock.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": AudioSession.lock");
1069 session_init_vols(ret, num_channels);
1071 ret->master_vol = 1.f;
1073 return ret;
1076 /* if channels == 0, then this will return or create a session with
1077 * matching dataflow and GUID. otherwise, channels must also match */
1078 static HRESULT get_audio_session(const GUID *sessionguid,
1079 IMMDevice *device, UINT channels, AudioSession **out)
1081 AudioSession *session;
1083 if(!sessionguid || IsEqualGUID(sessionguid, &GUID_NULL)){
1084 *out = create_session(&GUID_NULL, device, channels);
1085 if(!*out)
1086 return E_OUTOFMEMORY;
1088 return S_OK;
1091 *out = NULL;
1092 LIST_FOR_EACH_ENTRY(session, &g_sessions, AudioSession, entry){
1093 if(session->device == device &&
1094 IsEqualGUID(sessionguid, &session->guid)){
1095 session_init_vols(session, channels);
1096 *out = session;
1097 break;
1101 if(!*out){
1102 *out = create_session(sessionguid, device, channels);
1103 if(!*out)
1104 return E_OUTOFMEMORY;
1107 return S_OK;
1110 static int alsa_channel_index(DWORD flag)
1112 switch(flag){
1113 case SPEAKER_FRONT_LEFT:
1114 return 0;
1115 case SPEAKER_FRONT_RIGHT:
1116 return 1;
1117 case SPEAKER_BACK_LEFT:
1118 return 2;
1119 case SPEAKER_BACK_RIGHT:
1120 return 3;
1121 case SPEAKER_FRONT_CENTER:
1122 return 4;
1123 case SPEAKER_LOW_FREQUENCY:
1124 return 5;
1125 case SPEAKER_SIDE_LEFT:
1126 return 6;
1127 case SPEAKER_SIDE_RIGHT:
1128 return 7;
1130 return -1;
1133 static BOOL need_remapping(ACImpl *This, const WAVEFORMATEX *fmt, int *map)
1135 unsigned int i;
1136 for(i = 0; i < fmt->nChannels; ++i){
1137 if(map[i] != i)
1138 return TRUE;
1140 return FALSE;
1143 static DWORD get_channel_mask(unsigned int channels)
1145 switch(channels){
1146 case 0:
1147 return 0;
1148 case 1:
1149 return KSAUDIO_SPEAKER_MONO;
1150 case 2:
1151 return KSAUDIO_SPEAKER_STEREO;
1152 case 3:
1153 return KSAUDIO_SPEAKER_STEREO | SPEAKER_LOW_FREQUENCY;
1154 case 4:
1155 return KSAUDIO_SPEAKER_QUAD; /* not _SURROUND */
1156 case 5:
1157 return KSAUDIO_SPEAKER_QUAD | SPEAKER_LOW_FREQUENCY;
1158 case 6:
1159 return KSAUDIO_SPEAKER_5POINT1; /* not 5POINT1_SURROUND */
1160 case 7:
1161 return KSAUDIO_SPEAKER_5POINT1 | SPEAKER_BACK_CENTER;
1162 case 8:
1163 return KSAUDIO_SPEAKER_7POINT1_SURROUND; /* Vista deprecates 7POINT1 */
1165 FIXME("Unknown speaker configuration: %u\n", channels);
1166 return 0;
1169 static HRESULT map_channels(ACImpl *This, const WAVEFORMATEX *fmt, int *alsa_channels, int *map)
1171 BOOL need_remap;
1173 if(This->dataflow != eCapture && (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE || fmt->nChannels > 2) ){
1174 WAVEFORMATEXTENSIBLE *fmtex = (void*)fmt;
1175 DWORD mask, flag = SPEAKER_FRONT_LEFT;
1176 UINT i = 0;
1178 if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
1179 fmtex->dwChannelMask != 0)
1180 mask = fmtex->dwChannelMask;
1181 else
1182 mask = get_channel_mask(fmt->nChannels);
1184 *alsa_channels = 0;
1186 while(i < fmt->nChannels && !(flag & SPEAKER_RESERVED)){
1187 if(mask & flag){
1188 map[i] = alsa_channel_index(flag);
1189 TRACE("Mapping mmdevapi channel %u (0x%x) to ALSA channel %d\n",
1190 i, flag, map[i]);
1191 if(map[i] >= *alsa_channels)
1192 *alsa_channels = map[i] + 1;
1193 ++i;
1195 flag <<= 1;
1198 while(i < fmt->nChannels){
1199 map[i] = *alsa_channels;
1200 TRACE("Mapping mmdevapi channel %u to ALSA channel %d\n",
1201 i, map[i]);
1202 ++*alsa_channels;
1203 ++i;
1206 for(i = 0; i < fmt->nChannels; ++i){
1207 if(map[i] == -1){
1208 map[i] = *alsa_channels;
1209 ++*alsa_channels;
1210 TRACE("Remapping mmdevapi channel %u to ALSA channel %d\n",
1211 i, map[i]);
1215 need_remap = need_remapping(This, fmt, map);
1216 }else{
1217 *alsa_channels = fmt->nChannels;
1219 need_remap = FALSE;
1222 TRACE("need_remapping: %u, alsa_channels: %d\n", need_remap, *alsa_channels);
1224 return need_remap ? S_OK : S_FALSE;
1227 static void silence_buffer(ACImpl *This, BYTE *buffer, UINT32 frames)
1229 WAVEFORMATEXTENSIBLE *fmtex = (WAVEFORMATEXTENSIBLE*)This->fmt;
1230 if((This->fmt->wFormatTag == WAVE_FORMAT_PCM ||
1231 (This->fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
1232 IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))) &&
1233 This->fmt->wBitsPerSample == 8)
1234 memset(buffer, 128, frames * This->fmt->nBlockAlign);
1235 else
1236 memset(buffer, 0, frames * This->fmt->nBlockAlign);
1239 static HRESULT WINAPI AudioClient_Initialize(IAudioClient *iface,
1240 AUDCLNT_SHAREMODE mode, DWORD flags, REFERENCE_TIME duration,
1241 REFERENCE_TIME period, const WAVEFORMATEX *fmt,
1242 const GUID *sessionguid)
1244 ACImpl *This = impl_from_IAudioClient(iface);
1245 snd_pcm_sw_params_t *sw_params = NULL;
1246 snd_pcm_format_t format;
1247 unsigned int rate, alsa_period_us;
1248 int err, i;
1249 HRESULT hr = S_OK;
1251 TRACE("(%p)->(%x, %x, %s, %s, %p, %s)\n", This, mode, flags,
1252 wine_dbgstr_longlong(duration), wine_dbgstr_longlong(period), fmt, debugstr_guid(sessionguid));
1254 if(!fmt)
1255 return E_POINTER;
1257 if(mode != AUDCLNT_SHAREMODE_SHARED && mode != AUDCLNT_SHAREMODE_EXCLUSIVE)
1258 return AUDCLNT_E_NOT_INITIALIZED;
1260 if(flags & ~(AUDCLNT_STREAMFLAGS_CROSSPROCESS |
1261 AUDCLNT_STREAMFLAGS_LOOPBACK |
1262 AUDCLNT_STREAMFLAGS_EVENTCALLBACK |
1263 AUDCLNT_STREAMFLAGS_NOPERSIST |
1264 AUDCLNT_STREAMFLAGS_RATEADJUST |
1265 AUDCLNT_SESSIONFLAGS_EXPIREWHENUNOWNED |
1266 AUDCLNT_SESSIONFLAGS_DISPLAY_HIDE |
1267 AUDCLNT_SESSIONFLAGS_DISPLAY_HIDEWHENEXPIRED)){
1268 TRACE("Unknown flags: %08x\n", flags);
1269 return E_INVALIDARG;
1272 if(mode == AUDCLNT_SHAREMODE_SHARED){
1273 period = DefaultPeriod;
1274 if( duration < 3 * period)
1275 duration = 3 * period;
1276 }else{
1277 if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE){
1278 if(((WAVEFORMATEXTENSIBLE*)fmt)->dwChannelMask == 0 ||
1279 ((WAVEFORMATEXTENSIBLE*)fmt)->dwChannelMask & SPEAKER_RESERVED)
1280 return AUDCLNT_E_UNSUPPORTED_FORMAT;
1283 if(!period)
1284 period = DefaultPeriod; /* not minimum */
1285 if(period < MinimumPeriod || period > 5000000)
1286 return AUDCLNT_E_INVALID_DEVICE_PERIOD;
1287 if(duration > 20000000) /* the smaller the period, the lower this limit */
1288 return AUDCLNT_E_BUFFER_SIZE_ERROR;
1289 if(flags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK){
1290 if(duration != period)
1291 return AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL;
1292 FIXME("EXCLUSIVE mode with EVENTCALLBACK\n");
1293 return AUDCLNT_E_DEVICE_IN_USE;
1294 }else{
1295 if( duration < 8 * period)
1296 duration = 8 * period; /* may grow above 2s */
1300 EnterCriticalSection(&This->lock);
1302 if(This->initted){
1303 LeaveCriticalSection(&This->lock);
1304 return AUDCLNT_E_ALREADY_INITIALIZED;
1307 dump_fmt(fmt);
1309 This->need_remapping = map_channels(This, fmt, &This->alsa_channels, This->alsa_channel_map) == S_OK;
1311 if((err = snd_pcm_hw_params_any(This->pcm_handle, This->hw_params)) < 0){
1312 WARN("Unable to get hw_params: %d (%s)\n", err, snd_strerror(err));
1313 hr = AUDCLNT_E_ENDPOINT_CREATE_FAILED;
1314 goto exit;
1317 if((err = snd_pcm_hw_params_set_access(This->pcm_handle, This->hw_params,
1318 SND_PCM_ACCESS_RW_INTERLEAVED)) < 0){
1319 WARN("Unable to set access: %d (%s)\n", err, snd_strerror(err));
1320 hr = AUDCLNT_E_ENDPOINT_CREATE_FAILED;
1321 goto exit;
1324 format = alsa_format(fmt);
1325 if (format == SND_PCM_FORMAT_UNKNOWN){
1326 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
1327 goto exit;
1330 if((err = snd_pcm_hw_params_set_format(This->pcm_handle, This->hw_params,
1331 format)) < 0){
1332 WARN("Unable to set ALSA format to %u: %d (%s)\n", format, err,
1333 snd_strerror(err));
1334 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
1335 goto exit;
1338 This->alsa_format = format;
1340 rate = fmt->nSamplesPerSec;
1341 if((err = snd_pcm_hw_params_set_rate_near(This->pcm_handle, This->hw_params,
1342 &rate, NULL)) < 0){
1343 WARN("Unable to set rate to %u: %d (%s)\n", rate, err,
1344 snd_strerror(err));
1345 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
1346 goto exit;
1349 if((err = snd_pcm_hw_params_set_channels(This->pcm_handle, This->hw_params,
1350 This->alsa_channels)) < 0){
1351 WARN("Unable to set channels to %u: %d (%s)\n", fmt->nChannels, err,
1352 snd_strerror(err));
1353 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
1354 goto exit;
1357 This->mmdev_period_rt = period;
1358 alsa_period_us = This->mmdev_period_rt / 10;
1359 if((err = snd_pcm_hw_params_set_period_time_near(This->pcm_handle,
1360 This->hw_params, &alsa_period_us, NULL)) < 0)
1361 WARN("Unable to set period time near %u: %d (%s)\n", alsa_period_us,
1362 err, snd_strerror(err));
1363 /* ALSA updates the output variable alsa_period_us */
1365 This->mmdev_period_frames = MulDiv(fmt->nSamplesPerSec,
1366 This->mmdev_period_rt, 10000000);
1368 /* Buffer 4 ALSA periods if large enough, else 4 mmdevapi periods */
1369 This->alsa_bufsize_frames = This->mmdev_period_frames * 4;
1370 if(err < 0 || alsa_period_us < period / 10)
1371 err = snd_pcm_hw_params_set_buffer_size_near(This->pcm_handle,
1372 This->hw_params, &This->alsa_bufsize_frames);
1373 else{
1374 unsigned int periods = 4;
1375 err = snd_pcm_hw_params_set_periods_near(This->pcm_handle, This->hw_params, &periods, NULL);
1377 if(err < 0)
1378 WARN("Unable to set buffer size: %d (%s)\n", err, snd_strerror(err));
1380 if((err = snd_pcm_hw_params(This->pcm_handle, This->hw_params)) < 0){
1381 WARN("Unable to set hw params: %d (%s)\n", err, snd_strerror(err));
1382 hr = AUDCLNT_E_ENDPOINT_CREATE_FAILED;
1383 goto exit;
1386 if((err = snd_pcm_hw_params_get_period_size(This->hw_params,
1387 &This->alsa_period_frames, NULL)) < 0){
1388 WARN("Unable to get period size: %d (%s)\n", err, snd_strerror(err));
1389 hr = AUDCLNT_E_ENDPOINT_CREATE_FAILED;
1390 goto exit;
1393 if((err = snd_pcm_hw_params_get_buffer_size(This->hw_params,
1394 &This->alsa_bufsize_frames)) < 0){
1395 WARN("Unable to get buffer size: %d (%s)\n", err, snd_strerror(err));
1396 hr = AUDCLNT_E_ENDPOINT_CREATE_FAILED;
1397 goto exit;
1400 sw_params = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, snd_pcm_sw_params_sizeof());
1401 if(!sw_params){
1402 hr = E_OUTOFMEMORY;
1403 goto exit;
1406 if((err = snd_pcm_sw_params_current(This->pcm_handle, sw_params)) < 0){
1407 WARN("Unable to get sw_params: %d (%s)\n", err, snd_strerror(err));
1408 hr = AUDCLNT_E_ENDPOINT_CREATE_FAILED;
1409 goto exit;
1412 if((err = snd_pcm_sw_params_set_start_threshold(This->pcm_handle,
1413 sw_params, 1)) < 0){
1414 WARN("Unable set start threshold to 1: %d (%s)\n", err, snd_strerror(err));
1415 hr = AUDCLNT_E_ENDPOINT_CREATE_FAILED;
1416 goto exit;
1419 if((err = snd_pcm_sw_params_set_stop_threshold(This->pcm_handle,
1420 sw_params, This->alsa_bufsize_frames)) < 0){
1421 WARN("Unable set stop threshold to %lu: %d (%s)\n",
1422 This->alsa_bufsize_frames, err, snd_strerror(err));
1423 hr = AUDCLNT_E_ENDPOINT_CREATE_FAILED;
1424 goto exit;
1427 if((err = snd_pcm_sw_params(This->pcm_handle, sw_params)) < 0){
1428 WARN("Unable to set sw params: %d (%s)\n", err, snd_strerror(err));
1429 hr = AUDCLNT_E_ENDPOINT_CREATE_FAILED;
1430 goto exit;
1433 if((err = snd_pcm_prepare(This->pcm_handle)) < 0){
1434 WARN("Unable to prepare device: %d (%s)\n", err, snd_strerror(err));
1435 hr = AUDCLNT_E_ENDPOINT_CREATE_FAILED;
1436 goto exit;
1439 /* Bear in mind weird situations where
1440 * ALSA period (50ms) > mmdevapi buffer (3x10ms)
1441 * or surprising rounding as seen with 22050x8x1 with Pulse:
1442 * ALSA period 220 vs. 221 frames in mmdevapi and
1443 * buffer 883 vs. 2205 frames in mmdevapi! */
1444 This->bufsize_frames = MulDiv(duration, fmt->nSamplesPerSec, 10000000);
1445 if(mode == AUDCLNT_SHAREMODE_EXCLUSIVE)
1446 This->bufsize_frames -= This->bufsize_frames % This->mmdev_period_frames;
1447 This->hidden_frames = This->alsa_period_frames + This->mmdev_period_frames +
1448 MulDiv(fmt->nSamplesPerSec, EXTRA_SAFE_RT, 10000000);
1449 /* leave no less than about 1.33ms or 256 bytes of data after a rewind */
1450 This->safe_rewind_frames = max(256 / fmt->nBlockAlign, MulDiv(133, fmt->nSamplesPerSec, 100000));
1452 /* Check if the ALSA buffer is so small that it will run out before
1453 * the next MMDevAPI period tick occurs. Allow a little wiggle room
1454 * with 120% of the period time. */
1455 if(This->alsa_bufsize_frames < 1.2 * This->mmdev_period_frames)
1456 FIXME("ALSA buffer time is too small. Expect underruns. (%lu < %u * 1.2)\n",
1457 This->alsa_bufsize_frames, This->mmdev_period_frames);
1459 This->fmt = clone_format(fmt);
1460 if(!This->fmt){
1461 hr = E_OUTOFMEMORY;
1462 goto exit;
1465 This->local_buffer = HeapAlloc(GetProcessHeap(), 0,
1466 This->bufsize_frames * fmt->nBlockAlign);
1467 if(!This->local_buffer){
1468 hr = E_OUTOFMEMORY;
1469 goto exit;
1471 silence_buffer(This, This->local_buffer, This->bufsize_frames);
1473 This->silence_buf = HeapAlloc(GetProcessHeap(), 0,
1474 This->alsa_period_frames * This->fmt->nBlockAlign);
1475 if(!This->silence_buf){
1476 hr = E_OUTOFMEMORY;
1477 goto exit;
1479 silence_buffer(This, This->silence_buf, This->alsa_period_frames);
1481 This->vols = HeapAlloc(GetProcessHeap(), 0, fmt->nChannels * sizeof(float));
1482 if(!This->vols){
1483 hr = E_OUTOFMEMORY;
1484 goto exit;
1487 for(i = 0; i < fmt->nChannels; ++i)
1488 This->vols[i] = 1.f;
1490 This->share = mode;
1491 This->flags = flags;
1493 EnterCriticalSection(&g_sessions_lock);
1495 hr = get_audio_session(sessionguid, This->parent, fmt->nChannels,
1496 &This->session);
1497 if(FAILED(hr)){
1498 LeaveCriticalSection(&g_sessions_lock);
1499 goto exit;
1502 list_add_tail(&This->session->clients, &This->entry);
1504 LeaveCriticalSection(&g_sessions_lock);
1506 This->initted = TRUE;
1508 TRACE("ALSA period: %lu frames\n", This->alsa_period_frames);
1509 TRACE("ALSA buffer: %lu frames\n", This->alsa_bufsize_frames);
1510 TRACE("MMDevice period: %u frames\n", This->mmdev_period_frames);
1511 TRACE("MMDevice buffer: %u frames\n", This->bufsize_frames);
1513 exit:
1514 HeapFree(GetProcessHeap(), 0, sw_params);
1515 if(FAILED(hr)){
1516 HeapFree(GetProcessHeap(), 0, This->local_buffer);
1517 This->local_buffer = NULL;
1518 CoTaskMemFree(This->fmt);
1519 This->fmt = NULL;
1520 HeapFree(GetProcessHeap(), 0, This->vols);
1521 This->vols = NULL;
1524 LeaveCriticalSection(&This->lock);
1526 return hr;
1529 static HRESULT WINAPI AudioClient_GetBufferSize(IAudioClient *iface,
1530 UINT32 *out)
1532 ACImpl *This = impl_from_IAudioClient(iface);
1534 TRACE("(%p)->(%p)\n", This, out);
1536 if(!out)
1537 return E_POINTER;
1539 EnterCriticalSection(&This->lock);
1541 if(!This->initted){
1542 LeaveCriticalSection(&This->lock);
1543 return AUDCLNT_E_NOT_INITIALIZED;
1546 *out = This->bufsize_frames;
1548 LeaveCriticalSection(&This->lock);
1550 return S_OK;
1553 static HRESULT WINAPI AudioClient_GetStreamLatency(IAudioClient *iface,
1554 REFERENCE_TIME *latency)
1556 ACImpl *This = impl_from_IAudioClient(iface);
1558 TRACE("(%p)->(%p)\n", This, latency);
1560 if(!latency)
1561 return E_POINTER;
1563 EnterCriticalSection(&This->lock);
1565 if(!This->initted){
1566 LeaveCriticalSection(&This->lock);
1567 return AUDCLNT_E_NOT_INITIALIZED;
1570 /* Hide some frames in the ALSA buffer. Allows us to return GetCurrentPadding=0
1571 * yet have enough data left to play (as if it were in native's mixer). Add:
1572 * + mmdevapi_period such that at the end of it, ALSA still has data;
1573 * + EXTRA_SAFE (~4ms) to allow for late callback invocation / fluctuation;
1574 * + alsa_period such that ALSA always has at least one period to play. */
1575 if(This->dataflow == eRender)
1576 *latency = MulDiv(This->hidden_frames, 10000000, This->fmt->nSamplesPerSec);
1577 else
1578 *latency = MulDiv(This->alsa_period_frames, 10000000, This->fmt->nSamplesPerSec)
1579 + This->mmdev_period_rt;
1581 LeaveCriticalSection(&This->lock);
1583 return S_OK;
1586 static HRESULT WINAPI AudioClient_GetCurrentPadding(IAudioClient *iface,
1587 UINT32 *out)
1589 ACImpl *This = impl_from_IAudioClient(iface);
1591 TRACE("(%p)->(%p)\n", This, out);
1593 if(!out)
1594 return E_POINTER;
1596 EnterCriticalSection(&This->lock);
1598 if(!This->initted){
1599 LeaveCriticalSection(&This->lock);
1600 return AUDCLNT_E_NOT_INITIALIZED;
1603 /* padding is solely updated at callback time in shared mode */
1604 *out = This->held_frames;
1606 LeaveCriticalSection(&This->lock);
1608 TRACE("pad: %u\n", *out);
1610 return S_OK;
1613 static HRESULT WINAPI AudioClient_IsFormatSupported(IAudioClient *iface,
1614 AUDCLNT_SHAREMODE mode, const WAVEFORMATEX *fmt,
1615 WAVEFORMATEX **out)
1617 ACImpl *This = impl_from_IAudioClient(iface);
1618 snd_pcm_format_mask_t *formats = NULL;
1619 snd_pcm_format_t format;
1620 HRESULT hr = S_OK;
1621 WAVEFORMATEX *closest = NULL;
1622 unsigned int max = 0, min = 0;
1623 int err;
1624 int alsa_channels, alsa_channel_map[32];
1626 TRACE("(%p)->(%x, %p, %p)\n", This, mode, fmt, out);
1628 if(!fmt || (mode == AUDCLNT_SHAREMODE_SHARED && !out))
1629 return E_POINTER;
1631 if(mode != AUDCLNT_SHAREMODE_SHARED && mode != AUDCLNT_SHAREMODE_EXCLUSIVE)
1632 return E_INVALIDARG;
1634 if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
1635 fmt->cbSize < sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX))
1636 return E_INVALIDARG;
1638 dump_fmt(fmt);
1640 if(out){
1641 *out = NULL;
1642 if(mode != AUDCLNT_SHAREMODE_SHARED)
1643 out = NULL;
1646 if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
1647 (fmt->nAvgBytesPerSec == 0 ||
1648 fmt->nBlockAlign == 0 ||
1649 ((WAVEFORMATEXTENSIBLE*)fmt)->Samples.wValidBitsPerSample > fmt->wBitsPerSample))
1650 return E_INVALIDARG;
1652 if(fmt->nChannels == 0)
1653 return AUDCLNT_E_UNSUPPORTED_FORMAT;
1655 EnterCriticalSection(&This->lock);
1657 if((err = snd_pcm_hw_params_any(This->pcm_handle, This->hw_params)) < 0){
1658 hr = AUDCLNT_E_DEVICE_INVALIDATED;
1659 goto exit;
1662 formats = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
1663 snd_pcm_format_mask_sizeof());
1664 if(!formats){
1665 hr = E_OUTOFMEMORY;
1666 goto exit;
1669 snd_pcm_hw_params_get_format_mask(This->hw_params, formats);
1670 format = alsa_format(fmt);
1671 if (format == SND_PCM_FORMAT_UNKNOWN ||
1672 !snd_pcm_format_mask_test(formats, format)){
1673 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
1674 goto exit;
1677 closest = clone_format(fmt);
1678 if(!closest){
1679 hr = E_OUTOFMEMORY;
1680 goto exit;
1683 if((err = snd_pcm_hw_params_get_rate_min(This->hw_params, &min, NULL)) < 0){
1684 hr = AUDCLNT_E_DEVICE_INVALIDATED;
1685 WARN("Unable to get min rate: %d (%s)\n", err, snd_strerror(err));
1686 goto exit;
1689 if((err = snd_pcm_hw_params_get_rate_max(This->hw_params, &max, NULL)) < 0){
1690 hr = AUDCLNT_E_DEVICE_INVALIDATED;
1691 WARN("Unable to get max rate: %d (%s)\n", err, snd_strerror(err));
1692 goto exit;
1695 if(fmt->nSamplesPerSec < min || fmt->nSamplesPerSec > max){
1696 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
1697 goto exit;
1700 if((err = snd_pcm_hw_params_get_channels_min(This->hw_params, &min)) < 0){
1701 hr = AUDCLNT_E_DEVICE_INVALIDATED;
1702 WARN("Unable to get min channels: %d (%s)\n", err, snd_strerror(err));
1703 goto exit;
1706 if((err = snd_pcm_hw_params_get_channels_max(This->hw_params, &max)) < 0){
1707 hr = AUDCLNT_E_DEVICE_INVALIDATED;
1708 WARN("Unable to get max channels: %d (%s)\n", err, snd_strerror(err));
1709 goto exit;
1711 if(fmt->nChannels > max){
1712 hr = S_FALSE;
1713 closest->nChannels = max;
1714 }else if(fmt->nChannels < min){
1715 hr = S_FALSE;
1716 closest->nChannels = min;
1719 map_channels(This, fmt, &alsa_channels, alsa_channel_map);
1721 if(alsa_channels > max){
1722 hr = S_FALSE;
1723 closest->nChannels = max;
1726 if(closest->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
1727 ((WAVEFORMATEXTENSIBLE*)closest)->dwChannelMask = get_channel_mask(closest->nChannels);
1729 if(fmt->nBlockAlign != fmt->nChannels * fmt->wBitsPerSample / 8 ||
1730 fmt->nAvgBytesPerSec != fmt->nBlockAlign * fmt->nSamplesPerSec ||
1731 (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
1732 ((WAVEFORMATEXTENSIBLE*)fmt)->Samples.wValidBitsPerSample < fmt->wBitsPerSample))
1733 hr = S_FALSE;
1735 if(mode == AUDCLNT_SHAREMODE_EXCLUSIVE &&
1736 fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE){
1737 if(((WAVEFORMATEXTENSIBLE*)fmt)->dwChannelMask == 0 ||
1738 ((WAVEFORMATEXTENSIBLE*)fmt)->dwChannelMask & SPEAKER_RESERVED)
1739 hr = S_FALSE;
1742 exit:
1743 LeaveCriticalSection(&This->lock);
1744 HeapFree(GetProcessHeap(), 0, formats);
1746 if(hr == S_FALSE && !out)
1747 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
1749 if(hr == S_FALSE && out) {
1750 closest->nBlockAlign =
1751 closest->nChannels * closest->wBitsPerSample / 8;
1752 closest->nAvgBytesPerSec =
1753 closest->nBlockAlign * closest->nSamplesPerSec;
1754 if(closest->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
1755 ((WAVEFORMATEXTENSIBLE*)closest)->Samples.wValidBitsPerSample = closest->wBitsPerSample;
1756 *out = closest;
1757 } else
1758 CoTaskMemFree(closest);
1760 TRACE("returning: %08x\n", hr);
1761 return hr;
1764 static HRESULT WINAPI AudioClient_GetMixFormat(IAudioClient *iface,
1765 WAVEFORMATEX **pwfx)
1767 ACImpl *This = impl_from_IAudioClient(iface);
1768 WAVEFORMATEXTENSIBLE *fmt;
1769 snd_pcm_format_mask_t *formats;
1770 unsigned int max_rate, max_channels;
1771 int err;
1772 HRESULT hr = S_OK;
1774 TRACE("(%p)->(%p)\n", This, pwfx);
1776 if(!pwfx)
1777 return E_POINTER;
1778 *pwfx = NULL;
1780 fmt = CoTaskMemAlloc(sizeof(WAVEFORMATEXTENSIBLE));
1781 if(!fmt)
1782 return E_OUTOFMEMORY;
1784 formats = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, snd_pcm_format_mask_sizeof());
1785 if(!formats){
1786 CoTaskMemFree(fmt);
1787 return E_OUTOFMEMORY;
1790 EnterCriticalSection(&This->lock);
1792 if((err = snd_pcm_hw_params_any(This->pcm_handle, This->hw_params)) < 0){
1793 WARN("Unable to get hw_params: %d (%s)\n", err, snd_strerror(err));
1794 hr = AUDCLNT_E_DEVICE_INVALIDATED;
1795 goto exit;
1798 snd_pcm_hw_params_get_format_mask(This->hw_params, formats);
1800 fmt->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
1801 if(snd_pcm_format_mask_test(formats, SND_PCM_FORMAT_FLOAT_LE)){
1802 fmt->Format.wBitsPerSample = 32;
1803 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
1804 }else if(snd_pcm_format_mask_test(formats, SND_PCM_FORMAT_S16_LE)){
1805 fmt->Format.wBitsPerSample = 16;
1806 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1807 }else if(snd_pcm_format_mask_test(formats, SND_PCM_FORMAT_U8)){
1808 fmt->Format.wBitsPerSample = 8;
1809 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1810 }else if(snd_pcm_format_mask_test(formats, SND_PCM_FORMAT_S32_LE)){
1811 fmt->Format.wBitsPerSample = 32;
1812 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1813 }else if(snd_pcm_format_mask_test(formats, SND_PCM_FORMAT_S24_3LE)){
1814 fmt->Format.wBitsPerSample = 24;
1815 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1816 }else{
1817 ERR("Didn't recognize any available ALSA formats\n");
1818 hr = AUDCLNT_E_DEVICE_INVALIDATED;
1819 goto exit;
1822 if((err = snd_pcm_hw_params_get_channels_max(This->hw_params,
1823 &max_channels)) < 0){
1824 WARN("Unable to get max channels: %d (%s)\n", err, snd_strerror(err));
1825 hr = AUDCLNT_E_DEVICE_INVALIDATED;
1826 goto exit;
1829 if(max_channels > 6)
1830 fmt->Format.nChannels = 2;
1831 else
1832 fmt->Format.nChannels = max_channels;
1834 fmt->dwChannelMask = get_channel_mask(fmt->Format.nChannels);
1836 if((err = snd_pcm_hw_params_get_rate_max(This->hw_params, &max_rate,
1837 NULL)) < 0){
1838 WARN("Unable to get max rate: %d (%s)\n", err, snd_strerror(err));
1839 hr = AUDCLNT_E_DEVICE_INVALIDATED;
1840 goto exit;
1843 if(max_rate >= 48000)
1844 fmt->Format.nSamplesPerSec = 48000;
1845 else if(max_rate >= 44100)
1846 fmt->Format.nSamplesPerSec = 44100;
1847 else if(max_rate >= 22050)
1848 fmt->Format.nSamplesPerSec = 22050;
1849 else if(max_rate >= 11025)
1850 fmt->Format.nSamplesPerSec = 11025;
1851 else if(max_rate >= 8000)
1852 fmt->Format.nSamplesPerSec = 8000;
1853 else{
1854 ERR("Unknown max rate: %u\n", max_rate);
1855 hr = AUDCLNT_E_DEVICE_INVALIDATED;
1856 goto exit;
1859 fmt->Format.nBlockAlign = (fmt->Format.wBitsPerSample *
1860 fmt->Format.nChannels) / 8;
1861 fmt->Format.nAvgBytesPerSec = fmt->Format.nSamplesPerSec *
1862 fmt->Format.nBlockAlign;
1864 fmt->Samples.wValidBitsPerSample = fmt->Format.wBitsPerSample;
1865 fmt->Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
1867 dump_fmt((WAVEFORMATEX*)fmt);
1868 *pwfx = (WAVEFORMATEX*)fmt;
1870 exit:
1871 LeaveCriticalSection(&This->lock);
1872 if(FAILED(hr))
1873 CoTaskMemFree(fmt);
1874 HeapFree(GetProcessHeap(), 0, formats);
1876 return hr;
1879 static HRESULT WINAPI AudioClient_GetDevicePeriod(IAudioClient *iface,
1880 REFERENCE_TIME *defperiod, REFERENCE_TIME *minperiod)
1882 ACImpl *This = impl_from_IAudioClient(iface);
1884 TRACE("(%p)->(%p, %p)\n", This, defperiod, minperiod);
1886 if(!defperiod && !minperiod)
1887 return E_POINTER;
1889 if(defperiod)
1890 *defperiod = DefaultPeriod;
1891 if(minperiod)
1892 *minperiod = DefaultPeriod;
1894 return S_OK;
1897 static BYTE *remap_channels(ACImpl *This, BYTE *buf, snd_pcm_uframes_t frames)
1899 snd_pcm_uframes_t i;
1900 UINT c;
1901 UINT bytes_per_sample = This->fmt->wBitsPerSample / 8;
1903 if(!This->need_remapping)
1904 return buf;
1906 if(!This->remapping_buf){
1907 This->remapping_buf = HeapAlloc(GetProcessHeap(), 0,
1908 bytes_per_sample * This->alsa_channels * frames);
1909 This->remapping_buf_frames = frames;
1910 }else if(This->remapping_buf_frames < frames){
1911 This->remapping_buf = HeapReAlloc(GetProcessHeap(), 0, This->remapping_buf,
1912 bytes_per_sample * This->alsa_channels * frames);
1913 This->remapping_buf_frames = frames;
1916 snd_pcm_format_set_silence(This->alsa_format, This->remapping_buf,
1917 frames * This->alsa_channels);
1919 switch(This->fmt->wBitsPerSample){
1920 case 8: {
1921 UINT8 *tgt_buf, *src_buf;
1922 tgt_buf = This->remapping_buf;
1923 src_buf = buf;
1924 for(i = 0; i < frames; ++i){
1925 for(c = 0; c < This->fmt->nChannels; ++c)
1926 tgt_buf[This->alsa_channel_map[c]] = src_buf[c];
1927 tgt_buf += This->alsa_channels;
1928 src_buf += This->fmt->nChannels;
1930 break;
1932 case 16: {
1933 UINT16 *tgt_buf, *src_buf;
1934 tgt_buf = (UINT16*)This->remapping_buf;
1935 src_buf = (UINT16*)buf;
1936 for(i = 0; i < frames; ++i){
1937 for(c = 0; c < This->fmt->nChannels; ++c)
1938 tgt_buf[This->alsa_channel_map[c]] = src_buf[c];
1939 tgt_buf += This->alsa_channels;
1940 src_buf += This->fmt->nChannels;
1943 break;
1944 case 32: {
1945 UINT32 *tgt_buf, *src_buf;
1946 tgt_buf = (UINT32*)This->remapping_buf;
1947 src_buf = (UINT32*)buf;
1948 for(i = 0; i < frames; ++i){
1949 for(c = 0; c < This->fmt->nChannels; ++c)
1950 tgt_buf[This->alsa_channel_map[c]] = src_buf[c];
1951 tgt_buf += This->alsa_channels;
1952 src_buf += This->fmt->nChannels;
1955 break;
1956 default: {
1957 BYTE *tgt_buf, *src_buf;
1958 tgt_buf = This->remapping_buf;
1959 src_buf = buf;
1960 for(i = 0; i < frames; ++i){
1961 for(c = 0; c < This->fmt->nChannels; ++c)
1962 memcpy(&tgt_buf[This->alsa_channel_map[c] * bytes_per_sample],
1963 &src_buf[c * bytes_per_sample], bytes_per_sample);
1964 tgt_buf += This->alsa_channels * bytes_per_sample;
1965 src_buf += This->fmt->nChannels * bytes_per_sample;
1968 break;
1971 return This->remapping_buf;
1974 static snd_pcm_sframes_t alsa_write_best_effort(ACImpl *This, BYTE *buf,
1975 snd_pcm_uframes_t frames, BOOL mute)
1977 snd_pcm_sframes_t written;
1979 if(mute){
1980 int err;
1981 if((err = snd_pcm_format_set_silence(This->alsa_format, buf,
1982 frames * This->fmt->nChannels)) < 0)
1983 WARN("Setting buffer to silence failed: %d (%s)\n", err,
1984 snd_strerror(err));
1987 buf = remap_channels(This, buf, frames);
1989 written = snd_pcm_writei(This->pcm_handle, buf, frames);
1990 if(written < 0){
1991 int ret;
1993 if(written == -EAGAIN)
1994 /* buffer full */
1995 return 0;
1997 WARN("writei failed, recovering: %ld (%s)\n", written,
1998 snd_strerror(written));
2000 ret = snd_pcm_recover(This->pcm_handle, written, 0);
2001 if(ret < 0){
2002 WARN("Could not recover: %d (%s)\n", ret, snd_strerror(ret));
2003 return ret;
2006 written = snd_pcm_writei(This->pcm_handle, buf, frames);
2009 return written;
2012 static snd_pcm_sframes_t alsa_write_buffer_wrap(ACImpl *This, BYTE *buf,
2013 snd_pcm_uframes_t buflen, snd_pcm_uframes_t offs,
2014 snd_pcm_uframes_t to_write)
2016 snd_pcm_sframes_t ret = 0;
2018 while(to_write){
2019 snd_pcm_uframes_t chunk;
2020 snd_pcm_sframes_t tmp;
2022 if(offs + to_write > buflen)
2023 chunk = buflen - offs;
2024 else
2025 chunk = to_write;
2027 tmp = alsa_write_best_effort(This, buf + offs * This->fmt->nBlockAlign, chunk, This->session->mute);
2028 if(tmp < 0)
2029 return ret;
2030 if(!tmp)
2031 break;
2033 ret += tmp;
2034 to_write -= tmp;
2035 offs += tmp;
2036 offs %= buflen;
2039 return ret;
2042 static UINT buf_ptr_diff(UINT left, UINT right, UINT bufsize)
2044 if(left <= right)
2045 return right - left;
2046 return bufsize - (left - right);
2049 static UINT data_not_in_alsa(ACImpl *This)
2051 UINT32 diff;
2053 diff = buf_ptr_diff(This->lcl_offs_frames, This->wri_offs_frames, This->bufsize_frames);
2054 if(diff)
2055 return diff;
2057 return This->held_frames - This->data_in_alsa_frames;
2059 /* Here's the buffer setup:
2061 * vvvvvvvv sent to HW already
2062 * vvvvvvvv in ALSA buffer but rewindable
2063 * [dddddddddddddddd] ALSA buffer
2064 * [dddddddddddddddd--------] mmdevapi buffer
2065 * ^^^^^^^^ data_in_alsa_frames
2066 * ^^^^^^^^^^^^^^^^ held_frames
2067 * ^ lcl_offs_frames
2068 * ^ wri_offs_frames
2070 * GetCurrentPadding is held_frames
2072 * During period callback, we decrement held_frames, fill ALSA buffer, and move
2073 * lcl_offs forward
2075 * During Stop, we rewind the ALSA buffer
2077 static void alsa_write_data(ACImpl *This)
2079 snd_pcm_sframes_t written;
2080 snd_pcm_uframes_t avail, max_copy_frames, data_frames_played;
2081 int err;
2083 /* this call seems to be required to get an accurate snd_pcm_state() */
2084 avail = snd_pcm_avail_update(This->pcm_handle);
2086 if(snd_pcm_state(This->pcm_handle) == SND_PCM_STATE_XRUN){
2087 TRACE("XRun state, recovering\n");
2089 avail = This->alsa_bufsize_frames;
2091 if((err = snd_pcm_recover(This->pcm_handle, -EPIPE, 1)) < 0)
2092 WARN("snd_pcm_recover failed: %d (%s)\n", err, snd_strerror(err));
2094 if((err = snd_pcm_reset(This->pcm_handle)) < 0)
2095 WARN("snd_pcm_reset failed: %d (%s)\n", err, snd_strerror(err));
2097 if((err = snd_pcm_prepare(This->pcm_handle)) < 0)
2098 WARN("snd_pcm_prepare failed: %d (%s)\n", err, snd_strerror(err));
2101 TRACE("avail: %ld\n", avail);
2103 /* Add a lead-in when starting with too few frames to ensure
2104 * continuous rendering. Additional benefit: Force ALSA to start. */
2105 if(This->data_in_alsa_frames == 0 && This->held_frames < This->alsa_period_frames)
2106 alsa_write_best_effort(This, This->silence_buf, This->alsa_period_frames - This->held_frames, FALSE);
2108 if(This->started)
2109 max_copy_frames = data_not_in_alsa(This);
2110 else
2111 max_copy_frames = 0;
2113 data_frames_played = min(This->data_in_alsa_frames, avail);
2114 This->data_in_alsa_frames -= data_frames_played;
2116 if(This->held_frames > data_frames_played){
2117 if(This->started)
2118 This->held_frames -= data_frames_played;
2119 }else
2120 This->held_frames = 0;
2122 while(avail && max_copy_frames){
2123 snd_pcm_uframes_t to_write;
2125 to_write = min(avail, max_copy_frames);
2127 written = alsa_write_buffer_wrap(This, This->local_buffer,
2128 This->bufsize_frames, This->lcl_offs_frames, to_write);
2129 if(written <= 0)
2130 break;
2132 avail -= written;
2133 This->lcl_offs_frames += written;
2134 This->lcl_offs_frames %= This->bufsize_frames;
2135 This->data_in_alsa_frames += written;
2136 max_copy_frames -= written;
2139 if(This->event)
2140 SetEvent(This->event);
2143 static void alsa_read_data(ACImpl *This)
2145 snd_pcm_sframes_t nread;
2146 UINT32 pos = This->wri_offs_frames, limit = This->held_frames;
2148 if(!This->started)
2149 goto exit;
2151 /* FIXME: Detect overrun and signal DATA_DISCONTINUITY
2152 * How to count overrun frames and report them as position increase? */
2153 limit = This->bufsize_frames - max(limit, pos);
2155 nread = snd_pcm_readi(This->pcm_handle,
2156 This->local_buffer + pos * This->fmt->nBlockAlign, limit);
2157 TRACE("read %ld from %u limit %u\n", nread, pos, limit);
2158 if(nread < 0){
2159 int ret;
2161 if(nread == -EAGAIN) /* no data yet */
2162 return;
2164 WARN("read failed, recovering: %ld (%s)\n", nread, snd_strerror(nread));
2166 ret = snd_pcm_recover(This->pcm_handle, nread, 0);
2167 if(ret < 0){
2168 WARN("Recover failed: %d (%s)\n", ret, snd_strerror(ret));
2169 return;
2172 nread = snd_pcm_readi(This->pcm_handle,
2173 This->local_buffer + pos * This->fmt->nBlockAlign, limit);
2174 if(nread < 0){
2175 WARN("read failed: %ld (%s)\n", nread, snd_strerror(nread));
2176 return;
2180 if(This->session->mute){
2181 int err;
2182 if((err = snd_pcm_format_set_silence(This->alsa_format,
2183 This->local_buffer + pos * This->fmt->nBlockAlign,
2184 nread)) < 0)
2185 WARN("Setting buffer to silence failed: %d (%s)\n", err,
2186 snd_strerror(err));
2189 This->wri_offs_frames += nread;
2190 This->wri_offs_frames %= This->bufsize_frames;
2191 This->held_frames += nread;
2193 exit:
2194 if(This->event)
2195 SetEvent(This->event);
2198 static void CALLBACK alsa_push_buffer_data(void *user, BOOLEAN timer)
2200 ACImpl *This = user;
2202 EnterCriticalSection(&This->lock);
2204 QueryPerformanceCounter(&This->last_period_time);
2206 if(This->dataflow == eRender)
2207 alsa_write_data(This);
2208 else if(This->dataflow == eCapture)
2209 alsa_read_data(This);
2211 LeaveCriticalSection(&This->lock);
2214 static snd_pcm_uframes_t interp_elapsed_frames(ACImpl *This)
2216 LARGE_INTEGER time_freq, current_time, time_diff;
2217 QueryPerformanceFrequency(&time_freq);
2218 QueryPerformanceCounter(&current_time);
2219 time_diff.QuadPart = current_time.QuadPart - This->last_period_time.QuadPart;
2220 return MulDiv(time_diff.QuadPart, This->fmt->nSamplesPerSec, time_freq.QuadPart);
2223 static int alsa_rewind_best_effort(ACImpl *This)
2225 snd_pcm_uframes_t len, leave;
2227 /* we can't use snd_pcm_rewindable, some PCM devices crash. so follow
2228 * PulseAudio's example and rewind as much data as we believe is in the
2229 * buffer, minus 1.33ms for safety. */
2231 /* amount of data to leave in ALSA buffer */
2232 leave = interp_elapsed_frames(This) + This->safe_rewind_frames;
2234 if(This->held_frames < leave)
2235 This->held_frames = 0;
2236 else
2237 This->held_frames -= leave;
2239 if(This->data_in_alsa_frames < leave)
2240 len = 0;
2241 else
2242 len = This->data_in_alsa_frames - leave;
2244 TRACE("rewinding %lu frames, now held %u\n", len, This->held_frames);
2246 if(len)
2247 /* snd_pcm_rewind return value is often broken, assume it succeeded */
2248 snd_pcm_rewind(This->pcm_handle, len);
2250 This->data_in_alsa_frames = 0;
2252 return len;
2255 static HRESULT WINAPI AudioClient_Start(IAudioClient *iface)
2257 ACImpl *This = impl_from_IAudioClient(iface);
2259 TRACE("(%p)\n", This);
2261 EnterCriticalSection(&This->lock);
2263 if(!This->initted){
2264 LeaveCriticalSection(&This->lock);
2265 return AUDCLNT_E_NOT_INITIALIZED;
2268 if((This->flags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK) && !This->event){
2269 LeaveCriticalSection(&This->lock);
2270 return AUDCLNT_E_EVENTHANDLE_NOT_SET;
2273 if(This->started){
2274 LeaveCriticalSection(&This->lock);
2275 return AUDCLNT_E_NOT_STOPPED;
2278 if(This->dataflow == eCapture){
2279 /* dump any data that might be leftover in the ALSA capture buffer */
2280 snd_pcm_readi(This->pcm_handle, This->local_buffer,
2281 This->bufsize_frames);
2282 }else{
2283 snd_pcm_sframes_t avail, written;
2284 snd_pcm_uframes_t offs;
2286 avail = snd_pcm_avail_update(This->pcm_handle);
2287 avail = min(avail, This->held_frames);
2289 if(This->wri_offs_frames < This->held_frames)
2290 offs = This->bufsize_frames - This->held_frames + This->wri_offs_frames;
2291 else
2292 offs = This->wri_offs_frames - This->held_frames;
2294 /* fill it with data */
2295 written = alsa_write_buffer_wrap(This, This->local_buffer,
2296 This->bufsize_frames, offs, avail);
2298 if(written > 0){
2299 This->lcl_offs_frames = (offs + written) % This->bufsize_frames;
2300 This->data_in_alsa_frames = written;
2301 }else{
2302 This->lcl_offs_frames = offs;
2303 This->data_in_alsa_frames = 0;
2307 if(!This->timer){
2308 if(!CreateTimerQueueTimer(&This->timer, g_timer_q, alsa_push_buffer_data,
2309 This, 0, This->mmdev_period_rt / 10000, WT_EXECUTEINTIMERTHREAD)){
2310 LeaveCriticalSection(&This->lock);
2311 WARN("Unable to create timer: %u\n", GetLastError());
2312 return E_OUTOFMEMORY;
2316 This->started = TRUE;
2318 LeaveCriticalSection(&This->lock);
2320 return S_OK;
2323 static HRESULT WINAPI AudioClient_Stop(IAudioClient *iface)
2325 ACImpl *This = impl_from_IAudioClient(iface);
2327 TRACE("(%p)\n", This);
2329 EnterCriticalSection(&This->lock);
2331 if(!This->initted){
2332 LeaveCriticalSection(&This->lock);
2333 return AUDCLNT_E_NOT_INITIALIZED;
2336 if(!This->started){
2337 LeaveCriticalSection(&This->lock);
2338 return S_FALSE;
2341 if(This->dataflow == eRender)
2342 alsa_rewind_best_effort(This);
2344 This->started = FALSE;
2346 LeaveCriticalSection(&This->lock);
2348 return S_OK;
2351 static HRESULT WINAPI AudioClient_Reset(IAudioClient *iface)
2353 ACImpl *This = impl_from_IAudioClient(iface);
2355 TRACE("(%p)\n", This);
2357 EnterCriticalSection(&This->lock);
2359 if(!This->initted){
2360 LeaveCriticalSection(&This->lock);
2361 return AUDCLNT_E_NOT_INITIALIZED;
2364 if(This->started){
2365 LeaveCriticalSection(&This->lock);
2366 return AUDCLNT_E_NOT_STOPPED;
2369 if(This->getbuf_last){
2370 LeaveCriticalSection(&This->lock);
2371 return AUDCLNT_E_BUFFER_OPERATION_PENDING;
2374 if(snd_pcm_drop(This->pcm_handle) < 0)
2375 WARN("snd_pcm_drop failed\n");
2377 if(snd_pcm_reset(This->pcm_handle) < 0)
2378 WARN("snd_pcm_reset failed\n");
2380 if(snd_pcm_prepare(This->pcm_handle) < 0)
2381 WARN("snd_pcm_prepare failed\n");
2383 if(This->dataflow == eRender){
2384 This->written_frames = 0;
2385 This->last_pos_frames = 0;
2386 }else{
2387 This->written_frames += This->held_frames;
2389 This->held_frames = 0;
2390 This->lcl_offs_frames = 0;
2391 This->wri_offs_frames = 0;
2393 LeaveCriticalSection(&This->lock);
2395 return S_OK;
2398 static HRESULT WINAPI AudioClient_SetEventHandle(IAudioClient *iface,
2399 HANDLE event)
2401 ACImpl *This = impl_from_IAudioClient(iface);
2403 TRACE("(%p)->(%p)\n", This, event);
2405 if(!event)
2406 return E_INVALIDARG;
2408 EnterCriticalSection(&This->lock);
2410 if(!This->initted){
2411 LeaveCriticalSection(&This->lock);
2412 return AUDCLNT_E_NOT_INITIALIZED;
2415 if(!(This->flags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK)){
2416 LeaveCriticalSection(&This->lock);
2417 return AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED;
2420 if (This->event){
2421 LeaveCriticalSection(&This->lock);
2422 FIXME("called twice\n");
2423 return HRESULT_FROM_WIN32(ERROR_INVALID_NAME);
2426 This->event = event;
2428 LeaveCriticalSection(&This->lock);
2430 return S_OK;
2433 static HRESULT WINAPI AudioClient_GetService(IAudioClient *iface, REFIID riid,
2434 void **ppv)
2436 ACImpl *This = impl_from_IAudioClient(iface);
2438 TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(riid), ppv);
2440 if(!ppv)
2441 return E_POINTER;
2442 *ppv = NULL;
2444 EnterCriticalSection(&This->lock);
2446 if(!This->initted){
2447 LeaveCriticalSection(&This->lock);
2448 return AUDCLNT_E_NOT_INITIALIZED;
2451 if(IsEqualIID(riid, &IID_IAudioRenderClient)){
2452 if(This->dataflow != eRender){
2453 LeaveCriticalSection(&This->lock);
2454 return AUDCLNT_E_WRONG_ENDPOINT_TYPE;
2456 IAudioRenderClient_AddRef(&This->IAudioRenderClient_iface);
2457 *ppv = &This->IAudioRenderClient_iface;
2458 }else if(IsEqualIID(riid, &IID_IAudioCaptureClient)){
2459 if(This->dataflow != eCapture){
2460 LeaveCriticalSection(&This->lock);
2461 return AUDCLNT_E_WRONG_ENDPOINT_TYPE;
2463 IAudioCaptureClient_AddRef(&This->IAudioCaptureClient_iface);
2464 *ppv = &This->IAudioCaptureClient_iface;
2465 }else if(IsEqualIID(riid, &IID_IAudioClock)){
2466 IAudioClock_AddRef(&This->IAudioClock_iface);
2467 *ppv = &This->IAudioClock_iface;
2468 }else if(IsEqualIID(riid, &IID_IAudioStreamVolume)){
2469 IAudioStreamVolume_AddRef(&This->IAudioStreamVolume_iface);
2470 *ppv = &This->IAudioStreamVolume_iface;
2471 }else if(IsEqualIID(riid, &IID_IAudioSessionControl)){
2472 if(!This->session_wrapper){
2473 This->session_wrapper = AudioSessionWrapper_Create(This);
2474 if(!This->session_wrapper){
2475 LeaveCriticalSection(&This->lock);
2476 return E_OUTOFMEMORY;
2478 }else
2479 IAudioSessionControl2_AddRef(&This->session_wrapper->IAudioSessionControl2_iface);
2481 *ppv = &This->session_wrapper->IAudioSessionControl2_iface;
2482 }else if(IsEqualIID(riid, &IID_IChannelAudioVolume)){
2483 if(!This->session_wrapper){
2484 This->session_wrapper = AudioSessionWrapper_Create(This);
2485 if(!This->session_wrapper){
2486 LeaveCriticalSection(&This->lock);
2487 return E_OUTOFMEMORY;
2489 }else
2490 IChannelAudioVolume_AddRef(&This->session_wrapper->IChannelAudioVolume_iface);
2492 *ppv = &This->session_wrapper->IChannelAudioVolume_iface;
2493 }else if(IsEqualIID(riid, &IID_ISimpleAudioVolume)){
2494 if(!This->session_wrapper){
2495 This->session_wrapper = AudioSessionWrapper_Create(This);
2496 if(!This->session_wrapper){
2497 LeaveCriticalSection(&This->lock);
2498 return E_OUTOFMEMORY;
2500 }else
2501 ISimpleAudioVolume_AddRef(&This->session_wrapper->ISimpleAudioVolume_iface);
2503 *ppv = &This->session_wrapper->ISimpleAudioVolume_iface;
2506 if(*ppv){
2507 LeaveCriticalSection(&This->lock);
2508 return S_OK;
2511 LeaveCriticalSection(&This->lock);
2513 FIXME("stub %s\n", debugstr_guid(riid));
2514 return E_NOINTERFACE;
2517 static const IAudioClientVtbl AudioClient_Vtbl =
2519 AudioClient_QueryInterface,
2520 AudioClient_AddRef,
2521 AudioClient_Release,
2522 AudioClient_Initialize,
2523 AudioClient_GetBufferSize,
2524 AudioClient_GetStreamLatency,
2525 AudioClient_GetCurrentPadding,
2526 AudioClient_IsFormatSupported,
2527 AudioClient_GetMixFormat,
2528 AudioClient_GetDevicePeriod,
2529 AudioClient_Start,
2530 AudioClient_Stop,
2531 AudioClient_Reset,
2532 AudioClient_SetEventHandle,
2533 AudioClient_GetService
2536 static HRESULT WINAPI AudioRenderClient_QueryInterface(
2537 IAudioRenderClient *iface, REFIID riid, void **ppv)
2539 ACImpl *This = impl_from_IAudioRenderClient(iface);
2540 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2542 if(!ppv)
2543 return E_POINTER;
2544 *ppv = NULL;
2546 if(IsEqualIID(riid, &IID_IUnknown) ||
2547 IsEqualIID(riid, &IID_IAudioRenderClient))
2548 *ppv = iface;
2549 else if(IsEqualIID(riid, &IID_IMarshal))
2550 return IUnknown_QueryInterface(This->pUnkFTMarshal, riid, ppv);
2552 if(*ppv){
2553 IUnknown_AddRef((IUnknown*)*ppv);
2554 return S_OK;
2557 WARN("Unknown interface %s\n", debugstr_guid(riid));
2558 return E_NOINTERFACE;
2561 static ULONG WINAPI AudioRenderClient_AddRef(IAudioRenderClient *iface)
2563 ACImpl *This = impl_from_IAudioRenderClient(iface);
2564 return AudioClient_AddRef(&This->IAudioClient_iface);
2567 static ULONG WINAPI AudioRenderClient_Release(IAudioRenderClient *iface)
2569 ACImpl *This = impl_from_IAudioRenderClient(iface);
2570 return AudioClient_Release(&This->IAudioClient_iface);
2573 static HRESULT WINAPI AudioRenderClient_GetBuffer(IAudioRenderClient *iface,
2574 UINT32 frames, BYTE **data)
2576 ACImpl *This = impl_from_IAudioRenderClient(iface);
2577 UINT32 write_pos;
2579 TRACE("(%p)->(%u, %p)\n", This, frames, data);
2581 if(!data)
2582 return E_POINTER;
2583 *data = NULL;
2585 EnterCriticalSection(&This->lock);
2587 if(This->getbuf_last){
2588 LeaveCriticalSection(&This->lock);
2589 return AUDCLNT_E_OUT_OF_ORDER;
2592 if(!frames){
2593 LeaveCriticalSection(&This->lock);
2594 return S_OK;
2597 /* held_frames == GetCurrentPadding_nolock(); */
2598 if(This->held_frames + frames > This->bufsize_frames){
2599 LeaveCriticalSection(&This->lock);
2600 return AUDCLNT_E_BUFFER_TOO_LARGE;
2603 write_pos = This->wri_offs_frames;
2604 if(write_pos + frames > This->bufsize_frames){
2605 if(This->tmp_buffer_frames < frames){
2606 HeapFree(GetProcessHeap(), 0, This->tmp_buffer);
2607 This->tmp_buffer = HeapAlloc(GetProcessHeap(), 0,
2608 frames * This->fmt->nBlockAlign);
2609 if(!This->tmp_buffer){
2610 LeaveCriticalSection(&This->lock);
2611 return E_OUTOFMEMORY;
2613 This->tmp_buffer_frames = frames;
2615 *data = This->tmp_buffer;
2616 This->getbuf_last = -frames;
2617 }else{
2618 *data = This->local_buffer + write_pos * This->fmt->nBlockAlign;
2619 This->getbuf_last = frames;
2622 silence_buffer(This, *data, frames);
2624 LeaveCriticalSection(&This->lock);
2626 return S_OK;
2629 static void alsa_wrap_buffer(ACImpl *This, BYTE *buffer, UINT32 written_frames)
2631 snd_pcm_uframes_t write_offs_frames = This->wri_offs_frames;
2632 UINT32 write_offs_bytes = write_offs_frames * This->fmt->nBlockAlign;
2633 snd_pcm_uframes_t chunk_frames = This->bufsize_frames - write_offs_frames;
2634 UINT32 chunk_bytes = chunk_frames * This->fmt->nBlockAlign;
2635 UINT32 written_bytes = written_frames * This->fmt->nBlockAlign;
2637 if(written_bytes <= chunk_bytes){
2638 memcpy(This->local_buffer + write_offs_bytes, buffer, written_bytes);
2639 }else{
2640 memcpy(This->local_buffer + write_offs_bytes, buffer, chunk_bytes);
2641 memcpy(This->local_buffer, buffer + chunk_bytes,
2642 written_bytes - chunk_bytes);
2646 static HRESULT WINAPI AudioRenderClient_ReleaseBuffer(
2647 IAudioRenderClient *iface, UINT32 written_frames, DWORD flags)
2649 ACImpl *This = impl_from_IAudioRenderClient(iface);
2650 BYTE *buffer;
2652 TRACE("(%p)->(%u, %x)\n", This, written_frames, flags);
2654 EnterCriticalSection(&This->lock);
2656 if(!written_frames){
2657 This->getbuf_last = 0;
2658 LeaveCriticalSection(&This->lock);
2659 return S_OK;
2662 if(!This->getbuf_last){
2663 LeaveCriticalSection(&This->lock);
2664 return AUDCLNT_E_OUT_OF_ORDER;
2667 if(written_frames > (This->getbuf_last >= 0 ? This->getbuf_last : -This->getbuf_last)){
2668 LeaveCriticalSection(&This->lock);
2669 return AUDCLNT_E_INVALID_SIZE;
2672 if(This->getbuf_last >= 0)
2673 buffer = This->local_buffer + This->wri_offs_frames * This->fmt->nBlockAlign;
2674 else
2675 buffer = This->tmp_buffer;
2677 if(flags & AUDCLNT_BUFFERFLAGS_SILENT)
2678 silence_buffer(This, buffer, written_frames);
2680 if(This->getbuf_last < 0)
2681 alsa_wrap_buffer(This, buffer, written_frames);
2683 This->wri_offs_frames += written_frames;
2684 This->wri_offs_frames %= This->bufsize_frames;
2685 This->held_frames += written_frames;
2686 This->written_frames += written_frames;
2687 This->getbuf_last = 0;
2689 LeaveCriticalSection(&This->lock);
2691 return S_OK;
2694 static const IAudioRenderClientVtbl AudioRenderClient_Vtbl = {
2695 AudioRenderClient_QueryInterface,
2696 AudioRenderClient_AddRef,
2697 AudioRenderClient_Release,
2698 AudioRenderClient_GetBuffer,
2699 AudioRenderClient_ReleaseBuffer
2702 static HRESULT WINAPI AudioCaptureClient_QueryInterface(
2703 IAudioCaptureClient *iface, REFIID riid, void **ppv)
2705 ACImpl *This = impl_from_IAudioCaptureClient(iface);
2706 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2708 if(!ppv)
2709 return E_POINTER;
2710 *ppv = NULL;
2712 if(IsEqualIID(riid, &IID_IUnknown) ||
2713 IsEqualIID(riid, &IID_IAudioCaptureClient))
2714 *ppv = iface;
2715 else if(IsEqualIID(riid, &IID_IMarshal))
2716 return IUnknown_QueryInterface(This->pUnkFTMarshal, riid, ppv);
2718 if(*ppv){
2719 IUnknown_AddRef((IUnknown*)*ppv);
2720 return S_OK;
2723 WARN("Unknown interface %s\n", debugstr_guid(riid));
2724 return E_NOINTERFACE;
2727 static ULONG WINAPI AudioCaptureClient_AddRef(IAudioCaptureClient *iface)
2729 ACImpl *This = impl_from_IAudioCaptureClient(iface);
2730 return IAudioClient_AddRef(&This->IAudioClient_iface);
2733 static ULONG WINAPI AudioCaptureClient_Release(IAudioCaptureClient *iface)
2735 ACImpl *This = impl_from_IAudioCaptureClient(iface);
2736 return IAudioClient_Release(&This->IAudioClient_iface);
2739 static HRESULT WINAPI AudioCaptureClient_GetBuffer(IAudioCaptureClient *iface,
2740 BYTE **data, UINT32 *frames, DWORD *flags, UINT64 *devpos,
2741 UINT64 *qpcpos)
2743 ACImpl *This = impl_from_IAudioCaptureClient(iface);
2745 TRACE("(%p)->(%p, %p, %p, %p, %p)\n", This, data, frames, flags,
2746 devpos, qpcpos);
2748 if(!data || !frames || !flags)
2749 return E_POINTER;
2751 EnterCriticalSection(&This->lock);
2753 if(This->getbuf_last){
2754 LeaveCriticalSection(&This->lock);
2755 return AUDCLNT_E_OUT_OF_ORDER;
2758 /* hr = GetNextPacketSize(iface, frames); */
2759 if(This->held_frames < This->mmdev_period_frames){
2760 *frames = 0;
2761 LeaveCriticalSection(&This->lock);
2762 return AUDCLNT_S_BUFFER_EMPTY;
2764 *frames = This->mmdev_period_frames;
2766 if(This->lcl_offs_frames + *frames > This->bufsize_frames){
2767 UINT32 chunk_bytes, offs_bytes, frames_bytes;
2768 if(This->tmp_buffer_frames < *frames){
2769 HeapFree(GetProcessHeap(), 0, This->tmp_buffer);
2770 This->tmp_buffer = HeapAlloc(GetProcessHeap(), 0,
2771 *frames * This->fmt->nBlockAlign);
2772 if(!This->tmp_buffer){
2773 LeaveCriticalSection(&This->lock);
2774 return E_OUTOFMEMORY;
2776 This->tmp_buffer_frames = *frames;
2779 *data = This->tmp_buffer;
2780 chunk_bytes = (This->bufsize_frames - This->lcl_offs_frames) *
2781 This->fmt->nBlockAlign;
2782 offs_bytes = This->lcl_offs_frames * This->fmt->nBlockAlign;
2783 frames_bytes = *frames * This->fmt->nBlockAlign;
2784 memcpy(This->tmp_buffer, This->local_buffer + offs_bytes, chunk_bytes);
2785 memcpy(This->tmp_buffer + chunk_bytes, This->local_buffer,
2786 frames_bytes - chunk_bytes);
2787 }else
2788 *data = This->local_buffer +
2789 This->lcl_offs_frames * This->fmt->nBlockAlign;
2791 This->getbuf_last = *frames;
2792 *flags = 0;
2794 if(devpos)
2795 *devpos = This->written_frames;
2796 if(qpcpos){ /* fixme: qpc of recording time */
2797 LARGE_INTEGER stamp, freq;
2798 QueryPerformanceCounter(&stamp);
2799 QueryPerformanceFrequency(&freq);
2800 *qpcpos = (stamp.QuadPart * (INT64)10000000) / freq.QuadPart;
2803 LeaveCriticalSection(&This->lock);
2805 return *frames ? S_OK : AUDCLNT_S_BUFFER_EMPTY;
2808 static HRESULT WINAPI AudioCaptureClient_ReleaseBuffer(
2809 IAudioCaptureClient *iface, UINT32 done)
2811 ACImpl *This = impl_from_IAudioCaptureClient(iface);
2813 TRACE("(%p)->(%u)\n", This, done);
2815 EnterCriticalSection(&This->lock);
2817 if(!done){
2818 This->getbuf_last = 0;
2819 LeaveCriticalSection(&This->lock);
2820 return S_OK;
2823 if(!This->getbuf_last){
2824 LeaveCriticalSection(&This->lock);
2825 return AUDCLNT_E_OUT_OF_ORDER;
2828 if(This->getbuf_last != done){
2829 LeaveCriticalSection(&This->lock);
2830 return AUDCLNT_E_INVALID_SIZE;
2833 This->written_frames += done;
2834 This->held_frames -= done;
2835 This->lcl_offs_frames += done;
2836 This->lcl_offs_frames %= This->bufsize_frames;
2837 This->getbuf_last = 0;
2839 LeaveCriticalSection(&This->lock);
2841 return S_OK;
2844 static HRESULT WINAPI AudioCaptureClient_GetNextPacketSize(
2845 IAudioCaptureClient *iface, UINT32 *frames)
2847 ACImpl *This = impl_from_IAudioCaptureClient(iface);
2849 TRACE("(%p)->(%p)\n", This, frames);
2851 if(!frames)
2852 return E_POINTER;
2854 EnterCriticalSection(&This->lock);
2856 *frames = This->held_frames < This->mmdev_period_frames ? 0 : This->mmdev_period_frames;
2858 LeaveCriticalSection(&This->lock);
2860 return S_OK;
2863 static const IAudioCaptureClientVtbl AudioCaptureClient_Vtbl =
2865 AudioCaptureClient_QueryInterface,
2866 AudioCaptureClient_AddRef,
2867 AudioCaptureClient_Release,
2868 AudioCaptureClient_GetBuffer,
2869 AudioCaptureClient_ReleaseBuffer,
2870 AudioCaptureClient_GetNextPacketSize
2873 static HRESULT WINAPI AudioClock_QueryInterface(IAudioClock *iface,
2874 REFIID riid, void **ppv)
2876 ACImpl *This = impl_from_IAudioClock(iface);
2878 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2880 if(!ppv)
2881 return E_POINTER;
2882 *ppv = NULL;
2884 if(IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IAudioClock))
2885 *ppv = iface;
2886 else if(IsEqualIID(riid, &IID_IAudioClock2))
2887 *ppv = &This->IAudioClock2_iface;
2888 if(*ppv){
2889 IUnknown_AddRef((IUnknown*)*ppv);
2890 return S_OK;
2893 WARN("Unknown interface %s\n", debugstr_guid(riid));
2894 return E_NOINTERFACE;
2897 static ULONG WINAPI AudioClock_AddRef(IAudioClock *iface)
2899 ACImpl *This = impl_from_IAudioClock(iface);
2900 return IAudioClient_AddRef(&This->IAudioClient_iface);
2903 static ULONG WINAPI AudioClock_Release(IAudioClock *iface)
2905 ACImpl *This = impl_from_IAudioClock(iface);
2906 return IAudioClient_Release(&This->IAudioClient_iface);
2909 static HRESULT WINAPI AudioClock_GetFrequency(IAudioClock *iface, UINT64 *freq)
2911 ACImpl *This = impl_from_IAudioClock(iface);
2913 TRACE("(%p)->(%p)\n", This, freq);
2915 if(This->share == AUDCLNT_SHAREMODE_SHARED)
2916 *freq = (UINT64)This->fmt->nSamplesPerSec * This->fmt->nBlockAlign;
2917 else
2918 *freq = This->fmt->nSamplesPerSec;
2920 return S_OK;
2923 static HRESULT WINAPI AudioClock_GetPosition(IAudioClock *iface, UINT64 *pos,
2924 UINT64 *qpctime)
2926 ACImpl *This = impl_from_IAudioClock(iface);
2927 UINT64 position;
2928 snd_pcm_state_t alsa_state;
2930 TRACE("(%p)->(%p, %p)\n", This, pos, qpctime);
2932 if(!pos)
2933 return E_POINTER;
2935 EnterCriticalSection(&This->lock);
2937 /* avail_update required to get accurate snd_pcm_state() */
2938 snd_pcm_avail_update(This->pcm_handle);
2939 alsa_state = snd_pcm_state(This->pcm_handle);
2941 if(This->dataflow == eRender){
2942 position = This->written_frames - This->held_frames;
2944 if(This->started && alsa_state == SND_PCM_STATE_RUNNING && This->held_frames)
2945 /* we should be using snd_pcm_delay here, but it is broken
2946 * especially during ALSA device underrun. instead, let's just
2947 * interpolate between periods with the system timer. */
2948 position += interp_elapsed_frames(This);
2950 position = min(position, This->written_frames - This->held_frames + This->mmdev_period_frames);
2952 position = min(position, This->written_frames);
2953 }else
2954 position = This->written_frames + This->held_frames;
2956 /* ensure monotic growth */
2957 if(position < This->last_pos_frames)
2958 position = This->last_pos_frames;
2959 else
2960 This->last_pos_frames = position;
2962 TRACE("frames written: %u, held: %u, state: 0x%x, position: %u\n",
2963 (UINT32)(This->written_frames%1000000000), This->held_frames,
2964 alsa_state, (UINT32)(position%1000000000));
2966 LeaveCriticalSection(&This->lock);
2968 if(This->share == AUDCLNT_SHAREMODE_SHARED)
2969 *pos = position * This->fmt->nBlockAlign;
2970 else
2971 *pos = position;
2973 if(qpctime){
2974 LARGE_INTEGER stamp, freq;
2975 QueryPerformanceCounter(&stamp);
2976 QueryPerformanceFrequency(&freq);
2977 *qpctime = (stamp.QuadPart * (INT64)10000000) / freq.QuadPart;
2980 return S_OK;
2983 static HRESULT WINAPI AudioClock_GetCharacteristics(IAudioClock *iface,
2984 DWORD *chars)
2986 ACImpl *This = impl_from_IAudioClock(iface);
2988 TRACE("(%p)->(%p)\n", This, chars);
2990 if(!chars)
2991 return E_POINTER;
2993 *chars = AUDIOCLOCK_CHARACTERISTIC_FIXED_FREQ;
2995 return S_OK;
2998 static const IAudioClockVtbl AudioClock_Vtbl =
3000 AudioClock_QueryInterface,
3001 AudioClock_AddRef,
3002 AudioClock_Release,
3003 AudioClock_GetFrequency,
3004 AudioClock_GetPosition,
3005 AudioClock_GetCharacteristics
3008 static HRESULT WINAPI AudioClock2_QueryInterface(IAudioClock2 *iface,
3009 REFIID riid, void **ppv)
3011 ACImpl *This = impl_from_IAudioClock2(iface);
3012 return IAudioClock_QueryInterface(&This->IAudioClock_iface, riid, ppv);
3015 static ULONG WINAPI AudioClock2_AddRef(IAudioClock2 *iface)
3017 ACImpl *This = impl_from_IAudioClock2(iface);
3018 return IAudioClient_AddRef(&This->IAudioClient_iface);
3021 static ULONG WINAPI AudioClock2_Release(IAudioClock2 *iface)
3023 ACImpl *This = impl_from_IAudioClock2(iface);
3024 return IAudioClient_Release(&This->IAudioClient_iface);
3027 static HRESULT WINAPI AudioClock2_GetDevicePosition(IAudioClock2 *iface,
3028 UINT64 *pos, UINT64 *qpctime)
3030 ACImpl *This = impl_from_IAudioClock2(iface);
3032 FIXME("(%p)->(%p, %p)\n", This, pos, qpctime);
3034 return E_NOTIMPL;
3037 static const IAudioClock2Vtbl AudioClock2_Vtbl =
3039 AudioClock2_QueryInterface,
3040 AudioClock2_AddRef,
3041 AudioClock2_Release,
3042 AudioClock2_GetDevicePosition
3045 static AudioSessionWrapper *AudioSessionWrapper_Create(ACImpl *client)
3047 AudioSessionWrapper *ret;
3049 ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
3050 sizeof(AudioSessionWrapper));
3051 if(!ret)
3052 return NULL;
3054 ret->IAudioSessionControl2_iface.lpVtbl = &AudioSessionControl2_Vtbl;
3055 ret->ISimpleAudioVolume_iface.lpVtbl = &SimpleAudioVolume_Vtbl;
3056 ret->IChannelAudioVolume_iface.lpVtbl = &ChannelAudioVolume_Vtbl;
3058 ret->ref = 1;
3060 ret->client = client;
3061 if(client){
3062 ret->session = client->session;
3063 AudioClient_AddRef(&client->IAudioClient_iface);
3066 return ret;
3069 static HRESULT WINAPI AudioSessionControl_QueryInterface(
3070 IAudioSessionControl2 *iface, REFIID riid, void **ppv)
3072 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
3074 if(!ppv)
3075 return E_POINTER;
3076 *ppv = NULL;
3078 if(IsEqualIID(riid, &IID_IUnknown) ||
3079 IsEqualIID(riid, &IID_IAudioSessionControl) ||
3080 IsEqualIID(riid, &IID_IAudioSessionControl2))
3081 *ppv = iface;
3082 if(*ppv){
3083 IUnknown_AddRef((IUnknown*)*ppv);
3084 return S_OK;
3087 WARN("Unknown interface %s\n", debugstr_guid(riid));
3088 return E_NOINTERFACE;
3091 static ULONG WINAPI AudioSessionControl_AddRef(IAudioSessionControl2 *iface)
3093 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
3094 ULONG ref;
3095 ref = InterlockedIncrement(&This->ref);
3096 TRACE("(%p) Refcount now %u\n", This, ref);
3097 return ref;
3100 static ULONG WINAPI AudioSessionControl_Release(IAudioSessionControl2 *iface)
3102 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
3103 ULONG ref;
3104 ref = InterlockedDecrement(&This->ref);
3105 TRACE("(%p) Refcount now %u\n", This, ref);
3106 if(!ref){
3107 if(This->client){
3108 EnterCriticalSection(&This->client->lock);
3109 This->client->session_wrapper = NULL;
3110 LeaveCriticalSection(&This->client->lock);
3111 AudioClient_Release(&This->client->IAudioClient_iface);
3113 HeapFree(GetProcessHeap(), 0, This);
3115 return ref;
3118 static HRESULT WINAPI AudioSessionControl_GetState(IAudioSessionControl2 *iface,
3119 AudioSessionState *state)
3121 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
3122 ACImpl *client;
3124 TRACE("(%p)->(%p)\n", This, state);
3126 if(!state)
3127 return NULL_PTR_ERR;
3129 EnterCriticalSection(&g_sessions_lock);
3131 if(list_empty(&This->session->clients)){
3132 *state = AudioSessionStateExpired;
3133 LeaveCriticalSection(&g_sessions_lock);
3134 return S_OK;
3137 LIST_FOR_EACH_ENTRY(client, &This->session->clients, ACImpl, entry){
3138 EnterCriticalSection(&client->lock);
3139 if(client->started){
3140 *state = AudioSessionStateActive;
3141 LeaveCriticalSection(&client->lock);
3142 LeaveCriticalSection(&g_sessions_lock);
3143 return S_OK;
3145 LeaveCriticalSection(&client->lock);
3148 LeaveCriticalSection(&g_sessions_lock);
3150 *state = AudioSessionStateInactive;
3152 return S_OK;
3155 static HRESULT WINAPI AudioSessionControl_GetDisplayName(
3156 IAudioSessionControl2 *iface, WCHAR **name)
3158 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
3160 FIXME("(%p)->(%p) - stub\n", This, name);
3162 return E_NOTIMPL;
3165 static HRESULT WINAPI AudioSessionControl_SetDisplayName(
3166 IAudioSessionControl2 *iface, const WCHAR *name, const GUID *session)
3168 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
3170 FIXME("(%p)->(%p, %s) - stub\n", This, name, debugstr_guid(session));
3172 return E_NOTIMPL;
3175 static HRESULT WINAPI AudioSessionControl_GetIconPath(
3176 IAudioSessionControl2 *iface, WCHAR **path)
3178 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
3180 FIXME("(%p)->(%p) - stub\n", This, path);
3182 return E_NOTIMPL;
3185 static HRESULT WINAPI AudioSessionControl_SetIconPath(
3186 IAudioSessionControl2 *iface, const WCHAR *path, const GUID *session)
3188 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
3190 FIXME("(%p)->(%p, %s) - stub\n", This, path, debugstr_guid(session));
3192 return E_NOTIMPL;
3195 static HRESULT WINAPI AudioSessionControl_GetGroupingParam(
3196 IAudioSessionControl2 *iface, GUID *group)
3198 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
3200 FIXME("(%p)->(%p) - stub\n", This, group);
3202 return E_NOTIMPL;
3205 static HRESULT WINAPI AudioSessionControl_SetGroupingParam(
3206 IAudioSessionControl2 *iface, const GUID *group, const GUID *session)
3208 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
3210 FIXME("(%p)->(%s, %s) - stub\n", This, debugstr_guid(group),
3211 debugstr_guid(session));
3213 return E_NOTIMPL;
3216 static HRESULT WINAPI AudioSessionControl_RegisterAudioSessionNotification(
3217 IAudioSessionControl2 *iface, IAudioSessionEvents *events)
3219 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
3221 FIXME("(%p)->(%p) - stub\n", This, events);
3223 return S_OK;
3226 static HRESULT WINAPI AudioSessionControl_UnregisterAudioSessionNotification(
3227 IAudioSessionControl2 *iface, IAudioSessionEvents *events)
3229 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
3231 FIXME("(%p)->(%p) - stub\n", This, events);
3233 return S_OK;
3236 static HRESULT WINAPI AudioSessionControl_GetSessionIdentifier(
3237 IAudioSessionControl2 *iface, WCHAR **id)
3239 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
3241 FIXME("(%p)->(%p) - stub\n", This, id);
3243 return E_NOTIMPL;
3246 static HRESULT WINAPI AudioSessionControl_GetSessionInstanceIdentifier(
3247 IAudioSessionControl2 *iface, WCHAR **id)
3249 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
3251 FIXME("(%p)->(%p) - stub\n", This, id);
3253 return E_NOTIMPL;
3256 static HRESULT WINAPI AudioSessionControl_GetProcessId(
3257 IAudioSessionControl2 *iface, DWORD *pid)
3259 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
3261 TRACE("(%p)->(%p)\n", This, pid);
3263 if(!pid)
3264 return E_POINTER;
3266 *pid = GetCurrentProcessId();
3268 return S_OK;
3271 static HRESULT WINAPI AudioSessionControl_IsSystemSoundsSession(
3272 IAudioSessionControl2 *iface)
3274 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
3276 TRACE("(%p)\n", This);
3278 return S_FALSE;
3281 static HRESULT WINAPI AudioSessionControl_SetDuckingPreference(
3282 IAudioSessionControl2 *iface, BOOL optout)
3284 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
3286 TRACE("(%p)->(%d)\n", This, optout);
3288 return S_OK;
3291 static const IAudioSessionControl2Vtbl AudioSessionControl2_Vtbl =
3293 AudioSessionControl_QueryInterface,
3294 AudioSessionControl_AddRef,
3295 AudioSessionControl_Release,
3296 AudioSessionControl_GetState,
3297 AudioSessionControl_GetDisplayName,
3298 AudioSessionControl_SetDisplayName,
3299 AudioSessionControl_GetIconPath,
3300 AudioSessionControl_SetIconPath,
3301 AudioSessionControl_GetGroupingParam,
3302 AudioSessionControl_SetGroupingParam,
3303 AudioSessionControl_RegisterAudioSessionNotification,
3304 AudioSessionControl_UnregisterAudioSessionNotification,
3305 AudioSessionControl_GetSessionIdentifier,
3306 AudioSessionControl_GetSessionInstanceIdentifier,
3307 AudioSessionControl_GetProcessId,
3308 AudioSessionControl_IsSystemSoundsSession,
3309 AudioSessionControl_SetDuckingPreference
3312 static HRESULT WINAPI SimpleAudioVolume_QueryInterface(
3313 ISimpleAudioVolume *iface, REFIID riid, void **ppv)
3315 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
3317 if(!ppv)
3318 return E_POINTER;
3319 *ppv = NULL;
3321 if(IsEqualIID(riid, &IID_IUnknown) ||
3322 IsEqualIID(riid, &IID_ISimpleAudioVolume))
3323 *ppv = iface;
3324 if(*ppv){
3325 IUnknown_AddRef((IUnknown*)*ppv);
3326 return S_OK;
3329 WARN("Unknown interface %s\n", debugstr_guid(riid));
3330 return E_NOINTERFACE;
3333 static ULONG WINAPI SimpleAudioVolume_AddRef(ISimpleAudioVolume *iface)
3335 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
3336 return AudioSessionControl_AddRef(&This->IAudioSessionControl2_iface);
3339 static ULONG WINAPI SimpleAudioVolume_Release(ISimpleAudioVolume *iface)
3341 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
3342 return AudioSessionControl_Release(&This->IAudioSessionControl2_iface);
3345 static HRESULT WINAPI SimpleAudioVolume_SetMasterVolume(
3346 ISimpleAudioVolume *iface, float level, const GUID *context)
3348 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
3349 AudioSession *session = This->session;
3351 TRACE("(%p)->(%f, %s)\n", session, level, wine_dbgstr_guid(context));
3353 if(level < 0.f || level > 1.f)
3354 return E_INVALIDARG;
3356 if(context)
3357 FIXME("Notifications not supported yet\n");
3359 TRACE("ALSA does not support volume control\n");
3361 EnterCriticalSection(&session->lock);
3363 session->master_vol = level;
3365 LeaveCriticalSection(&session->lock);
3367 return S_OK;
3370 static HRESULT WINAPI SimpleAudioVolume_GetMasterVolume(
3371 ISimpleAudioVolume *iface, float *level)
3373 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
3374 AudioSession *session = This->session;
3376 TRACE("(%p)->(%p)\n", session, level);
3378 if(!level)
3379 return NULL_PTR_ERR;
3381 *level = session->master_vol;
3383 return S_OK;
3386 static HRESULT WINAPI SimpleAudioVolume_SetMute(ISimpleAudioVolume *iface,
3387 BOOL mute, const GUID *context)
3389 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
3390 AudioSession *session = This->session;
3392 TRACE("(%p)->(%u, %s)\n", session, mute, debugstr_guid(context));
3394 if(context)
3395 FIXME("Notifications not supported yet\n");
3397 session->mute = mute;
3399 return S_OK;
3402 static HRESULT WINAPI SimpleAudioVolume_GetMute(ISimpleAudioVolume *iface,
3403 BOOL *mute)
3405 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
3406 AudioSession *session = This->session;
3408 TRACE("(%p)->(%p)\n", session, mute);
3410 if(!mute)
3411 return NULL_PTR_ERR;
3413 *mute = session->mute;
3415 return S_OK;
3418 static const ISimpleAudioVolumeVtbl SimpleAudioVolume_Vtbl =
3420 SimpleAudioVolume_QueryInterface,
3421 SimpleAudioVolume_AddRef,
3422 SimpleAudioVolume_Release,
3423 SimpleAudioVolume_SetMasterVolume,
3424 SimpleAudioVolume_GetMasterVolume,
3425 SimpleAudioVolume_SetMute,
3426 SimpleAudioVolume_GetMute
3429 static HRESULT WINAPI AudioStreamVolume_QueryInterface(
3430 IAudioStreamVolume *iface, REFIID riid, void **ppv)
3432 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
3434 if(!ppv)
3435 return E_POINTER;
3436 *ppv = NULL;
3438 if(IsEqualIID(riid, &IID_IUnknown) ||
3439 IsEqualIID(riid, &IID_IAudioStreamVolume))
3440 *ppv = iface;
3441 if(*ppv){
3442 IUnknown_AddRef((IUnknown*)*ppv);
3443 return S_OK;
3446 WARN("Unknown interface %s\n", debugstr_guid(riid));
3447 return E_NOINTERFACE;
3450 static ULONG WINAPI AudioStreamVolume_AddRef(IAudioStreamVolume *iface)
3452 ACImpl *This = impl_from_IAudioStreamVolume(iface);
3453 return IAudioClient_AddRef(&This->IAudioClient_iface);
3456 static ULONG WINAPI AudioStreamVolume_Release(IAudioStreamVolume *iface)
3458 ACImpl *This = impl_from_IAudioStreamVolume(iface);
3459 return IAudioClient_Release(&This->IAudioClient_iface);
3462 static HRESULT WINAPI AudioStreamVolume_GetChannelCount(
3463 IAudioStreamVolume *iface, UINT32 *out)
3465 ACImpl *This = impl_from_IAudioStreamVolume(iface);
3467 TRACE("(%p)->(%p)\n", This, out);
3469 if(!out)
3470 return E_POINTER;
3472 *out = This->fmt->nChannels;
3474 return S_OK;
3477 static HRESULT WINAPI AudioStreamVolume_SetChannelVolume(
3478 IAudioStreamVolume *iface, UINT32 index, float level)
3480 ACImpl *This = impl_from_IAudioStreamVolume(iface);
3482 TRACE("(%p)->(%d, %f)\n", This, index, level);
3484 if(level < 0.f || level > 1.f)
3485 return E_INVALIDARG;
3487 if(index >= This->fmt->nChannels)
3488 return E_INVALIDARG;
3490 TRACE("ALSA does not support volume control\n");
3492 EnterCriticalSection(&This->lock);
3494 This->vols[index] = level;
3496 LeaveCriticalSection(&This->lock);
3498 return S_OK;
3501 static HRESULT WINAPI AudioStreamVolume_GetChannelVolume(
3502 IAudioStreamVolume *iface, UINT32 index, float *level)
3504 ACImpl *This = impl_from_IAudioStreamVolume(iface);
3506 TRACE("(%p)->(%d, %p)\n", This, index, level);
3508 if(!level)
3509 return E_POINTER;
3511 if(index >= This->fmt->nChannels)
3512 return E_INVALIDARG;
3514 *level = This->vols[index];
3516 return S_OK;
3519 static HRESULT WINAPI AudioStreamVolume_SetAllVolumes(
3520 IAudioStreamVolume *iface, UINT32 count, const float *levels)
3522 ACImpl *This = impl_from_IAudioStreamVolume(iface);
3523 unsigned int i;
3525 TRACE("(%p)->(%d, %p)\n", This, count, levels);
3527 if(!levels)
3528 return E_POINTER;
3530 if(count != This->fmt->nChannels)
3531 return E_INVALIDARG;
3533 TRACE("ALSA does not support volume control\n");
3535 EnterCriticalSection(&This->lock);
3537 for(i = 0; i < count; ++i)
3538 This->vols[i] = levels[i];
3540 LeaveCriticalSection(&This->lock);
3542 return S_OK;
3545 static HRESULT WINAPI AudioStreamVolume_GetAllVolumes(
3546 IAudioStreamVolume *iface, UINT32 count, float *levels)
3548 ACImpl *This = impl_from_IAudioStreamVolume(iface);
3549 unsigned int i;
3551 TRACE("(%p)->(%d, %p)\n", This, count, levels);
3553 if(!levels)
3554 return E_POINTER;
3556 if(count != This->fmt->nChannels)
3557 return E_INVALIDARG;
3559 EnterCriticalSection(&This->lock);
3561 for(i = 0; i < count; ++i)
3562 levels[i] = This->vols[i];
3564 LeaveCriticalSection(&This->lock);
3566 return S_OK;
3569 static const IAudioStreamVolumeVtbl AudioStreamVolume_Vtbl =
3571 AudioStreamVolume_QueryInterface,
3572 AudioStreamVolume_AddRef,
3573 AudioStreamVolume_Release,
3574 AudioStreamVolume_GetChannelCount,
3575 AudioStreamVolume_SetChannelVolume,
3576 AudioStreamVolume_GetChannelVolume,
3577 AudioStreamVolume_SetAllVolumes,
3578 AudioStreamVolume_GetAllVolumes
3581 static HRESULT WINAPI ChannelAudioVolume_QueryInterface(
3582 IChannelAudioVolume *iface, REFIID riid, void **ppv)
3584 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
3586 if(!ppv)
3587 return E_POINTER;
3588 *ppv = NULL;
3590 if(IsEqualIID(riid, &IID_IUnknown) ||
3591 IsEqualIID(riid, &IID_IChannelAudioVolume))
3592 *ppv = iface;
3593 if(*ppv){
3594 IUnknown_AddRef((IUnknown*)*ppv);
3595 return S_OK;
3598 WARN("Unknown interface %s\n", debugstr_guid(riid));
3599 return E_NOINTERFACE;
3602 static ULONG WINAPI ChannelAudioVolume_AddRef(IChannelAudioVolume *iface)
3604 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
3605 return AudioSessionControl_AddRef(&This->IAudioSessionControl2_iface);
3608 static ULONG WINAPI ChannelAudioVolume_Release(IChannelAudioVolume *iface)
3610 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
3611 return AudioSessionControl_Release(&This->IAudioSessionControl2_iface);
3614 static HRESULT WINAPI ChannelAudioVolume_GetChannelCount(
3615 IChannelAudioVolume *iface, UINT32 *out)
3617 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
3618 AudioSession *session = This->session;
3620 TRACE("(%p)->(%p)\n", session, out);
3622 if(!out)
3623 return NULL_PTR_ERR;
3625 *out = session->channel_count;
3627 return S_OK;
3630 static HRESULT WINAPI ChannelAudioVolume_SetChannelVolume(
3631 IChannelAudioVolume *iface, UINT32 index, float level,
3632 const GUID *context)
3634 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
3635 AudioSession *session = This->session;
3637 TRACE("(%p)->(%d, %f, %s)\n", session, index, level,
3638 wine_dbgstr_guid(context));
3640 if(level < 0.f || level > 1.f)
3641 return E_INVALIDARG;
3643 if(index >= session->channel_count)
3644 return E_INVALIDARG;
3646 if(context)
3647 FIXME("Notifications not supported yet\n");
3649 TRACE("ALSA does not support volume control\n");
3651 EnterCriticalSection(&session->lock);
3653 session->channel_vols[index] = level;
3655 LeaveCriticalSection(&session->lock);
3657 return S_OK;
3660 static HRESULT WINAPI ChannelAudioVolume_GetChannelVolume(
3661 IChannelAudioVolume *iface, UINT32 index, float *level)
3663 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
3664 AudioSession *session = This->session;
3666 TRACE("(%p)->(%d, %p)\n", session, index, level);
3668 if(!level)
3669 return NULL_PTR_ERR;
3671 if(index >= session->channel_count)
3672 return E_INVALIDARG;
3674 *level = session->channel_vols[index];
3676 return S_OK;
3679 static HRESULT WINAPI ChannelAudioVolume_SetAllVolumes(
3680 IChannelAudioVolume *iface, UINT32 count, const float *levels,
3681 const GUID *context)
3683 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
3684 AudioSession *session = This->session;
3685 unsigned int i;
3687 TRACE("(%p)->(%d, %p, %s)\n", session, count, levels,
3688 wine_dbgstr_guid(context));
3690 if(!levels)
3691 return NULL_PTR_ERR;
3693 if(count != session->channel_count)
3694 return E_INVALIDARG;
3696 if(context)
3697 FIXME("Notifications not supported yet\n");
3699 TRACE("ALSA does not support volume control\n");
3701 EnterCriticalSection(&session->lock);
3703 for(i = 0; i < count; ++i)
3704 session->channel_vols[i] = levels[i];
3706 LeaveCriticalSection(&session->lock);
3708 return S_OK;
3711 static HRESULT WINAPI ChannelAudioVolume_GetAllVolumes(
3712 IChannelAudioVolume *iface, UINT32 count, float *levels)
3714 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
3715 AudioSession *session = This->session;
3716 unsigned int i;
3718 TRACE("(%p)->(%d, %p)\n", session, count, levels);
3720 if(!levels)
3721 return NULL_PTR_ERR;
3723 if(count != session->channel_count)
3724 return E_INVALIDARG;
3726 for(i = 0; i < count; ++i)
3727 levels[i] = session->channel_vols[i];
3729 return S_OK;
3732 static const IChannelAudioVolumeVtbl ChannelAudioVolume_Vtbl =
3734 ChannelAudioVolume_QueryInterface,
3735 ChannelAudioVolume_AddRef,
3736 ChannelAudioVolume_Release,
3737 ChannelAudioVolume_GetChannelCount,
3738 ChannelAudioVolume_SetChannelVolume,
3739 ChannelAudioVolume_GetChannelVolume,
3740 ChannelAudioVolume_SetAllVolumes,
3741 ChannelAudioVolume_GetAllVolumes
3744 static HRESULT WINAPI AudioSessionManager_QueryInterface(IAudioSessionManager2 *iface,
3745 REFIID riid, void **ppv)
3747 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
3749 if(!ppv)
3750 return E_POINTER;
3751 *ppv = NULL;
3753 if(IsEqualIID(riid, &IID_IUnknown) ||
3754 IsEqualIID(riid, &IID_IAudioSessionManager) ||
3755 IsEqualIID(riid, &IID_IAudioSessionManager2))
3756 *ppv = iface;
3757 if(*ppv){
3758 IUnknown_AddRef((IUnknown*)*ppv);
3759 return S_OK;
3762 WARN("Unknown interface %s\n", debugstr_guid(riid));
3763 return E_NOINTERFACE;
3766 static ULONG WINAPI AudioSessionManager_AddRef(IAudioSessionManager2 *iface)
3768 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3769 ULONG ref;
3770 ref = InterlockedIncrement(&This->ref);
3771 TRACE("(%p) Refcount now %u\n", This, ref);
3772 return ref;
3775 static ULONG WINAPI AudioSessionManager_Release(IAudioSessionManager2 *iface)
3777 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3778 ULONG ref;
3779 ref = InterlockedDecrement(&This->ref);
3780 TRACE("(%p) Refcount now %u\n", This, ref);
3781 if(!ref)
3782 HeapFree(GetProcessHeap(), 0, This);
3783 return ref;
3786 static HRESULT WINAPI AudioSessionManager_GetAudioSessionControl(
3787 IAudioSessionManager2 *iface, const GUID *session_guid, DWORD flags,
3788 IAudioSessionControl **out)
3790 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3791 AudioSession *session;
3792 AudioSessionWrapper *wrapper;
3793 HRESULT hr;
3795 TRACE("(%p)->(%s, %x, %p)\n", This, debugstr_guid(session_guid),
3796 flags, out);
3798 hr = get_audio_session(session_guid, This->device, 0, &session);
3799 if(FAILED(hr))
3800 return hr;
3802 wrapper = AudioSessionWrapper_Create(NULL);
3803 if(!wrapper)
3804 return E_OUTOFMEMORY;
3806 wrapper->session = session;
3808 *out = (IAudioSessionControl*)&wrapper->IAudioSessionControl2_iface;
3810 return S_OK;
3813 static HRESULT WINAPI AudioSessionManager_GetSimpleAudioVolume(
3814 IAudioSessionManager2 *iface, const GUID *session_guid, DWORD flags,
3815 ISimpleAudioVolume **out)
3817 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3818 AudioSession *session;
3819 AudioSessionWrapper *wrapper;
3820 HRESULT hr;
3822 TRACE("(%p)->(%s, %x, %p)\n", This, debugstr_guid(session_guid),
3823 flags, out);
3825 hr = get_audio_session(session_guid, This->device, 0, &session);
3826 if(FAILED(hr))
3827 return hr;
3829 wrapper = AudioSessionWrapper_Create(NULL);
3830 if(!wrapper)
3831 return E_OUTOFMEMORY;
3833 wrapper->session = session;
3835 *out = &wrapper->ISimpleAudioVolume_iface;
3837 return S_OK;
3840 static HRESULT WINAPI AudioSessionManager_GetSessionEnumerator(
3841 IAudioSessionManager2 *iface, IAudioSessionEnumerator **out)
3843 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3844 FIXME("(%p)->(%p) - stub\n", This, out);
3845 return E_NOTIMPL;
3848 static HRESULT WINAPI AudioSessionManager_RegisterSessionNotification(
3849 IAudioSessionManager2 *iface, IAudioSessionNotification *notification)
3851 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3852 FIXME("(%p)->(%p) - stub\n", This, notification);
3853 return E_NOTIMPL;
3856 static HRESULT WINAPI AudioSessionManager_UnregisterSessionNotification(
3857 IAudioSessionManager2 *iface, IAudioSessionNotification *notification)
3859 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3860 FIXME("(%p)->(%p) - stub\n", This, notification);
3861 return E_NOTIMPL;
3864 static HRESULT WINAPI AudioSessionManager_RegisterDuckNotification(
3865 IAudioSessionManager2 *iface, const WCHAR *session_id,
3866 IAudioVolumeDuckNotification *notification)
3868 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3869 FIXME("(%p)->(%p) - stub\n", This, notification);
3870 return E_NOTIMPL;
3873 static HRESULT WINAPI AudioSessionManager_UnregisterDuckNotification(
3874 IAudioSessionManager2 *iface,
3875 IAudioVolumeDuckNotification *notification)
3877 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3878 FIXME("(%p)->(%p) - stub\n", This, notification);
3879 return E_NOTIMPL;
3882 static const IAudioSessionManager2Vtbl AudioSessionManager2_Vtbl =
3884 AudioSessionManager_QueryInterface,
3885 AudioSessionManager_AddRef,
3886 AudioSessionManager_Release,
3887 AudioSessionManager_GetAudioSessionControl,
3888 AudioSessionManager_GetSimpleAudioVolume,
3889 AudioSessionManager_GetSessionEnumerator,
3890 AudioSessionManager_RegisterSessionNotification,
3891 AudioSessionManager_UnregisterSessionNotification,
3892 AudioSessionManager_RegisterDuckNotification,
3893 AudioSessionManager_UnregisterDuckNotification
3896 HRESULT WINAPI AUDDRV_GetAudioSessionManager(IMMDevice *device,
3897 IAudioSessionManager2 **out)
3899 SessionMgr *This;
3901 This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(SessionMgr));
3902 if(!This)
3903 return E_OUTOFMEMORY;
3905 This->IAudioSessionManager2_iface.lpVtbl = &AudioSessionManager2_Vtbl;
3906 This->device = device;
3907 This->ref = 1;
3909 *out = &This->IAudioSessionManager2_iface;
3911 return S_OK;
3914 static unsigned int alsa_probe_num_speakers(char *name) {
3915 snd_pcm_t *handle;
3916 snd_pcm_hw_params_t *params;
3917 int err;
3918 unsigned int max_channels = 0;
3920 if ((err = snd_pcm_open(&handle, name, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK)) < 0) {
3921 WARN("The device \"%s\" failed to open: %d (%s).\n",
3922 name, err, snd_strerror(err));
3923 return 0;
3926 params = HeapAlloc(GetProcessHeap(), 0, snd_pcm_hw_params_sizeof());
3927 if (!params) {
3928 WARN("Out of memory.\n");
3929 snd_pcm_close(handle);
3930 return 0;
3933 if ((err = snd_pcm_hw_params_any(handle, params)) < 0) {
3934 WARN("snd_pcm_hw_params_any failed for \"%s\": %d (%s).\n",
3935 name, err, snd_strerror(err));
3936 goto exit;
3939 if ((err = snd_pcm_hw_params_get_channels_max(params,
3940 &max_channels)) < 0){
3941 WARN("Unable to get max channels: %d (%s)\n", err, snd_strerror(err));
3942 goto exit;
3945 exit:
3946 HeapFree(GetProcessHeap(), 0, params);
3947 snd_pcm_close(handle);
3949 return max_channels;
3952 enum AudioDeviceConnectionType {
3953 AudioDeviceConnectionType_Unknown = 0,
3954 AudioDeviceConnectionType_PCI,
3955 AudioDeviceConnectionType_USB
3958 HRESULT WINAPI AUDDRV_GetPropValue(GUID *guid, const PROPERTYKEY *prop, PROPVARIANT *out)
3960 char name[256];
3961 EDataFlow flow;
3963 static const PROPERTYKEY devicepath_key = { /* undocumented? - {b3f8fa53-0004-438e-9003-51a46e139bfc},2 */
3964 {0xb3f8fa53, 0x0004, 0x438e, {0x90, 0x03, 0x51, 0xa4, 0x6e, 0x13, 0x9b, 0xfc}}, 2
3967 TRACE("%s, (%s,%u), %p\n", wine_dbgstr_guid(guid), wine_dbgstr_guid(&prop->fmtid), prop->pid, out);
3969 if(!get_alsa_name_by_guid(guid, name, sizeof(name), &flow))
3971 WARN("Unknown interface %s\n", debugstr_guid(guid));
3972 return E_NOINTERFACE;
3975 if(IsEqualPropertyKey(*prop, devicepath_key))
3977 char uevent[MAX_PATH];
3978 FILE *fuevent;
3979 int card, device;
3981 /* only implemented for identifiable devices, i.e. not "default" */
3982 if(!sscanf(name, "plughw:%u,%u", &card, &device))
3983 return E_NOTIMPL;
3985 sprintf(uevent, "/sys/class/sound/card%u/device/uevent", card);
3986 fuevent = fopen(uevent, "r");
3988 if(fuevent){
3989 enum AudioDeviceConnectionType connection = AudioDeviceConnectionType_Unknown;
3990 USHORT vendor_id = 0, product_id = 0;
3991 char line[256];
3993 while (fgets(line, sizeof(line), fuevent)) {
3994 char *val;
3995 size_t val_len;
3997 if((val = strchr(line, '='))) {
3998 val[0] = 0;
3999 val++;
4001 val_len = strlen(val);
4002 if(val_len > 0 && val[val_len - 1] == '\n') { val[val_len - 1] = 0; }
4004 if(!strcmp(line, "PCI_ID")){
4005 connection = AudioDeviceConnectionType_PCI;
4006 if(sscanf(val, "%hX:%hX", &vendor_id, &product_id)<2){
4007 WARN("Unexpected input when reading PCI_ID in uevent file.\n");
4008 connection = AudioDeviceConnectionType_Unknown;
4009 break;
4011 }else if(!strcmp(line, "DEVTYPE") && !strcmp(val,"usb_interface"))
4012 connection = AudioDeviceConnectionType_USB;
4013 else if(!strcmp(line, "PRODUCT"))
4014 if(sscanf(val, "%hx/%hx/", &vendor_id, &product_id)<2){
4015 WARN("Unexpected input when reading PRODUCT in uevent file.\n");
4016 connection = AudioDeviceConnectionType_Unknown;
4017 break;
4022 fclose(fuevent);
4024 if(connection == AudioDeviceConnectionType_USB || connection == AudioDeviceConnectionType_PCI){
4025 static const WCHAR usbformatW[] = { '{','1','}','.','U','S','B','\\','V','I','D','_',
4026 '%','0','4','X','&','P','I','D','_','%','0','4','X','\\',
4027 '%','u','&','%','0','8','X',0 }; /* "{1}.USB\VID_%04X&PID_%04X\%u&%08X" */
4028 static const WCHAR pciformatW[] = { '{','1','}','.','H','D','A','U','D','I','O','\\','F','U','N','C','_','0','1','&',
4029 'V','E','N','_','%','0','4','X','&','D','E','V','_',
4030 '%','0','4','X','\\','%','u','&','%','0','8','X',0 }; /* "{1}.HDAUDIO\FUNC_01&VEN_%04X&DEV_%04X\%u&%08X" */
4031 UINT serial_number;
4033 /* As hardly any audio devices have serial numbers, Windows instead
4034 appears to use a persistent random number. We emulate this here
4035 by instead using the last 8 hex digits of the GUID. */
4036 serial_number = (guid->Data4[4] << 24) | (guid->Data4[5] << 16) | (guid->Data4[6] << 8) | guid->Data4[7];
4038 out->vt = VT_LPWSTR;
4039 out->u.pwszVal = CoTaskMemAlloc(128 * sizeof(WCHAR));
4041 if(!out->u.pwszVal)
4042 return E_OUTOFMEMORY;
4044 if(connection == AudioDeviceConnectionType_USB)
4045 sprintfW( out->u.pwszVal, usbformatW, vendor_id, product_id, device, serial_number);
4046 else if(connection == AudioDeviceConnectionType_PCI)
4047 sprintfW( out->u.pwszVal, pciformatW, vendor_id, product_id, device, serial_number);
4049 return S_OK;
4051 }else{
4052 WARN("Could not open %s for reading\n", uevent);
4053 return E_NOTIMPL;
4055 } else if (flow != eCapture && IsEqualPropertyKey(*prop, PKEY_AudioEndpoint_PhysicalSpeakers)) {
4056 unsigned int num_speakers, card, device;
4057 char hwname[255];
4059 if (sscanf(name, "plughw:%u,%u", &card, &device))
4060 sprintf(hwname, "hw:%u,%u", card, device); /* must be hw rather than plughw to work */
4061 else
4062 strcpy(hwname, name);
4064 num_speakers = alsa_probe_num_speakers(hwname);
4065 if (num_speakers == 0)
4066 return E_FAIL;
4068 out->vt = VT_UI4;
4070 if (num_speakers > 6)
4071 out->u.ulVal = KSAUDIO_SPEAKER_STEREO;
4072 else if (num_speakers == 6)
4073 out->u.ulVal = KSAUDIO_SPEAKER_5POINT1;
4074 else if (num_speakers >= 4)
4075 out->u.ulVal = KSAUDIO_SPEAKER_QUAD;
4076 else if (num_speakers >= 2)
4077 out->u.ulVal = KSAUDIO_SPEAKER_STEREO;
4078 else if (num_speakers == 1)
4079 out->u.ulVal = KSAUDIO_SPEAKER_MONO;
4081 return S_OK;
4084 TRACE("Unimplemented property %s,%u\n", wine_dbgstr_guid(&prop->fmtid), prop->pid);
4086 return E_NOTIMPL;