dcomp: Add DCompositionCreateDevice() stub.
[wine.git] / dlls / winealsa.drv / mmdevdrv.c
blob318350471dbe18bda1f941d145312d52b12c21a1
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 IAudioClient3 IAudioClient3_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 vol_adjusted_frames; /* Frames we've already adjusted the volume of but didn't write yet */
130 UINT32 data_in_alsa_frames;
132 HANDLE timer;
133 BYTE *local_buffer, *tmp_buffer, *remapping_buf, *silence_buf;
134 LONG32 getbuf_last; /* <0 when using tmp_buffer */
136 CRITICAL_SECTION lock;
138 AudioSession *session;
139 AudioSessionWrapper *session_wrapper;
141 struct list entry;
144 typedef struct _SessionMgr {
145 IAudioSessionManager2 IAudioSessionManager2_iface;
147 LONG ref;
149 IMMDevice *device;
150 } SessionMgr;
152 static HANDLE g_timer_q;
154 static CRITICAL_SECTION g_sessions_lock;
155 static CRITICAL_SECTION_DEBUG g_sessions_lock_debug =
157 0, 0, &g_sessions_lock,
158 { &g_sessions_lock_debug.ProcessLocksList, &g_sessions_lock_debug.ProcessLocksList },
159 0, 0, { (DWORD_PTR)(__FILE__ ": g_sessions_lock") }
161 static CRITICAL_SECTION g_sessions_lock = { &g_sessions_lock_debug, -1, 0, 0, 0, 0 };
162 static struct list g_sessions = LIST_INIT(g_sessions);
164 static const WCHAR defaultW[] = {'d','e','f','a','u','l','t',0};
165 static const char defname[] = "default";
167 static const WCHAR drv_keyW[] = {'S','o','f','t','w','a','r','e','\\',
168 'W','i','n','e','\\','D','r','i','v','e','r','s','\\',
169 'w','i','n','e','a','l','s','a','.','d','r','v',0};
170 static const WCHAR drv_key_devicesW[] = {'S','o','f','t','w','a','r','e','\\',
171 'W','i','n','e','\\','D','r','i','v','e','r','s','\\',
172 'w','i','n','e','a','l','s','a','.','d','r','v','\\','d','e','v','i','c','e','s',0};
173 static const WCHAR guidW[] = {'g','u','i','d',0};
175 static const IAudioClient3Vtbl AudioClient3_Vtbl;
176 static const IAudioRenderClientVtbl AudioRenderClient_Vtbl;
177 static const IAudioCaptureClientVtbl AudioCaptureClient_Vtbl;
178 static const IAudioSessionControl2Vtbl AudioSessionControl2_Vtbl;
179 static const ISimpleAudioVolumeVtbl SimpleAudioVolume_Vtbl;
180 static const IAudioClockVtbl AudioClock_Vtbl;
181 static const IAudioClock2Vtbl AudioClock2_Vtbl;
182 static const IAudioStreamVolumeVtbl AudioStreamVolume_Vtbl;
183 static const IChannelAudioVolumeVtbl ChannelAudioVolume_Vtbl;
184 static const IAudioSessionManager2Vtbl AudioSessionManager2_Vtbl;
186 static AudioSessionWrapper *AudioSessionWrapper_Create(ACImpl *client);
188 static inline ACImpl *impl_from_IAudioClient3(IAudioClient3 *iface)
190 return CONTAINING_RECORD(iface, ACImpl, IAudioClient3_iface);
193 static inline ACImpl *impl_from_IAudioRenderClient(IAudioRenderClient *iface)
195 return CONTAINING_RECORD(iface, ACImpl, IAudioRenderClient_iface);
198 static inline ACImpl *impl_from_IAudioCaptureClient(IAudioCaptureClient *iface)
200 return CONTAINING_RECORD(iface, ACImpl, IAudioCaptureClient_iface);
203 static inline AudioSessionWrapper *impl_from_IAudioSessionControl2(IAudioSessionControl2 *iface)
205 return CONTAINING_RECORD(iface, AudioSessionWrapper, IAudioSessionControl2_iface);
208 static inline AudioSessionWrapper *impl_from_ISimpleAudioVolume(ISimpleAudioVolume *iface)
210 return CONTAINING_RECORD(iface, AudioSessionWrapper, ISimpleAudioVolume_iface);
213 static inline AudioSessionWrapper *impl_from_IChannelAudioVolume(IChannelAudioVolume *iface)
215 return CONTAINING_RECORD(iface, AudioSessionWrapper, IChannelAudioVolume_iface);
218 static inline ACImpl *impl_from_IAudioClock(IAudioClock *iface)
220 return CONTAINING_RECORD(iface, ACImpl, IAudioClock_iface);
223 static inline ACImpl *impl_from_IAudioClock2(IAudioClock2 *iface)
225 return CONTAINING_RECORD(iface, ACImpl, IAudioClock2_iface);
228 static inline ACImpl *impl_from_IAudioStreamVolume(IAudioStreamVolume *iface)
230 return CONTAINING_RECORD(iface, ACImpl, IAudioStreamVolume_iface);
233 static inline SessionMgr *impl_from_IAudioSessionManager2(IAudioSessionManager2 *iface)
235 return CONTAINING_RECORD(iface, SessionMgr, IAudioSessionManager2_iface);
238 BOOL WINAPI DllMain(HINSTANCE dll, DWORD reason, void *reserved)
240 switch (reason)
242 case DLL_PROCESS_ATTACH:
243 g_timer_q = CreateTimerQueue();
244 if(!g_timer_q)
245 return FALSE;
246 break;
248 case DLL_PROCESS_DETACH:
249 if (reserved) break;
250 DeleteCriticalSection(&g_sessions_lock);
251 break;
253 return TRUE;
256 /* From <dlls/mmdevapi/mmdevapi.h> */
257 enum DriverPriority {
258 Priority_Unavailable = 0,
259 Priority_Low,
260 Priority_Neutral,
261 Priority_Preferred
264 int WINAPI AUDDRV_GetPriority(void)
266 return Priority_Neutral;
269 static void set_device_guid(EDataFlow flow, HKEY drv_key, const WCHAR *key_name,
270 GUID *guid)
272 HKEY key;
273 BOOL opened = FALSE;
274 LONG lr;
276 if(!drv_key){
277 lr = RegCreateKeyExW(HKEY_CURRENT_USER, drv_key_devicesW, 0, NULL, 0, KEY_WRITE,
278 NULL, &drv_key, NULL);
279 if(lr != ERROR_SUCCESS){
280 ERR("RegCreateKeyEx(drv_key) failed: %u\n", lr);
281 return;
283 opened = TRUE;
286 lr = RegCreateKeyExW(drv_key, key_name, 0, NULL, 0, KEY_WRITE,
287 NULL, &key, NULL);
288 if(lr != ERROR_SUCCESS){
289 ERR("RegCreateKeyEx(%s) failed: %u\n", wine_dbgstr_w(key_name), lr);
290 goto exit;
293 lr = RegSetValueExW(key, guidW, 0, REG_BINARY, (BYTE*)guid,
294 sizeof(GUID));
295 if(lr != ERROR_SUCCESS)
296 ERR("RegSetValueEx(%s\\guid) failed: %u\n", wine_dbgstr_w(key_name), lr);
298 RegCloseKey(key);
299 exit:
300 if(opened)
301 RegCloseKey(drv_key);
304 static void get_device_guid(EDataFlow flow, const char *device, GUID *guid)
306 HKEY key = NULL, dev_key;
307 DWORD type, size = sizeof(*guid);
308 WCHAR key_name[256];
310 if(flow == eCapture)
311 key_name[0] = '1';
312 else
313 key_name[0] = '0';
314 key_name[1] = ',';
315 MultiByteToWideChar(CP_UNIXCP, 0, device, -1, key_name + 2, ARRAY_SIZE(key_name) - 2);
317 if(RegOpenKeyExW(HKEY_CURRENT_USER, drv_key_devicesW, 0, KEY_WRITE|KEY_READ, &key) == ERROR_SUCCESS){
318 if(RegOpenKeyExW(key, key_name, 0, KEY_READ, &dev_key) == ERROR_SUCCESS){
319 if(RegQueryValueExW(dev_key, guidW, 0, &type,
320 (BYTE*)guid, &size) == ERROR_SUCCESS){
321 if(type == REG_BINARY){
322 RegCloseKey(dev_key);
323 RegCloseKey(key);
324 return;
326 ERR("Invalid type for device %s GUID: %u; ignoring and overwriting\n",
327 wine_dbgstr_w(key_name), type);
329 RegCloseKey(dev_key);
333 CoCreateGuid(guid);
335 set_device_guid(flow, key, key_name, guid);
337 if(key)
338 RegCloseKey(key);
341 static BOOL alsa_try_open(const char *devnode, snd_pcm_stream_t stream)
343 snd_pcm_t *handle;
344 int err;
346 TRACE("devnode: %s, stream: %d\n", devnode, stream);
348 if((err = snd_pcm_open(&handle, devnode, stream, SND_PCM_NONBLOCK)) < 0){
349 WARN("The device \"%s\" failed to open: %d (%s).\n",
350 devnode, err, snd_strerror(err));
351 return FALSE;
354 snd_pcm_close(handle);
355 return TRUE;
358 static WCHAR *construct_device_id(EDataFlow flow, const WCHAR *chunk1, const char *chunk2)
360 WCHAR *ret;
361 const WCHAR *prefix;
362 DWORD len_wchars = 0, chunk1_len = 0, copied = 0, prefix_len;
364 static const WCHAR dashW[] = {' ','-',' ',0};
365 static const size_t dashW_len = ARRAY_SIZE(dashW) - 1;
366 static const WCHAR outW[] = {'O','u','t',':',' ',0};
367 static const WCHAR inW[] = {'I','n',':',' ',0};
369 if(flow == eRender){
370 prefix = outW;
371 prefix_len = ARRAY_SIZE(outW) - 1;
372 len_wchars += prefix_len;
373 }else{
374 prefix = inW;
375 prefix_len = ARRAY_SIZE(inW) - 1;
376 len_wchars += prefix_len;
378 if(chunk1){
379 chunk1_len = strlenW(chunk1);
380 len_wchars += chunk1_len;
382 if(chunk1 && chunk2)
383 len_wchars += dashW_len;
384 if(chunk2)
385 len_wchars += MultiByteToWideChar(CP_UNIXCP, 0, chunk2, -1, NULL, 0) - 1;
386 len_wchars += 1; /* NULL byte */
388 ret = HeapAlloc(GetProcessHeap(), 0, len_wchars * sizeof(WCHAR));
390 memcpy(ret, prefix, prefix_len * sizeof(WCHAR));
391 copied += prefix_len;
392 if(chunk1){
393 memcpy(ret + copied, chunk1, chunk1_len * sizeof(WCHAR));
394 copied += chunk1_len;
396 if(chunk1 && chunk2){
397 memcpy(ret + copied, dashW, dashW_len * sizeof(WCHAR));
398 copied += dashW_len;
400 if(chunk2){
401 MultiByteToWideChar(CP_UNIXCP, 0, chunk2, -1, ret + copied, len_wchars - copied);
402 }else
403 ret[copied] = 0;
405 TRACE("Enumerated device: %s\n", wine_dbgstr_w(ret));
407 return ret;
410 static HRESULT alsa_get_card_devices(EDataFlow flow, snd_pcm_stream_t stream,
411 WCHAR ***ids, GUID **guids, UINT *num, snd_ctl_t *ctl, int card,
412 const WCHAR *cardnameW)
414 int err, device;
415 snd_pcm_info_t *info;
417 info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, snd_pcm_info_sizeof());
418 if(!info)
419 return E_OUTOFMEMORY;
421 snd_pcm_info_set_subdevice(info, 0);
422 snd_pcm_info_set_stream(info, stream);
424 device = -1;
425 for(err = snd_ctl_pcm_next_device(ctl, &device); device != -1 && err >= 0;
426 err = snd_ctl_pcm_next_device(ctl, &device)){
427 const char *devname;
428 char devnode[32];
430 snd_pcm_info_set_device(info, device);
432 if((err = snd_ctl_pcm_info(ctl, info)) < 0){
433 if(err == -ENOENT)
434 /* This device doesn't have the right stream direction */
435 continue;
437 WARN("Failed to get info for card %d, device %d: %d (%s)\n",
438 card, device, err, snd_strerror(err));
439 continue;
442 sprintf(devnode, "plughw:%d,%d", card, device);
443 if(!alsa_try_open(devnode, stream))
444 continue;
446 if(*num){
447 *ids = HeapReAlloc(GetProcessHeap(), 0, *ids, sizeof(WCHAR *) * (*num + 1));
448 *guids = HeapReAlloc(GetProcessHeap(), 0, *guids, sizeof(GUID) * (*num + 1));
449 }else{
450 *ids = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR *));
451 *guids = HeapAlloc(GetProcessHeap(), 0, sizeof(GUID));
454 devname = snd_pcm_info_get_name(info);
455 if(!devname){
456 WARN("Unable to get device name for card %d, device %d\n", card,
457 device);
458 continue;
461 (*ids)[*num] = construct_device_id(flow, cardnameW, devname);
462 get_device_guid(flow, devnode, &(*guids)[*num]);
464 ++(*num);
467 HeapFree(GetProcessHeap(), 0, info);
469 if(err != 0)
470 WARN("Got a failure during device enumeration on card %d: %d (%s)\n",
471 card, err, snd_strerror(err));
473 return S_OK;
476 static void get_reg_devices(EDataFlow flow, snd_pcm_stream_t stream, WCHAR ***ids,
477 GUID **guids, UINT *num)
479 static const WCHAR ALSAOutputDevices[] = {'A','L','S','A','O','u','t','p','u','t','D','e','v','i','c','e','s',0};
480 static const WCHAR ALSAInputDevices[] = {'A','L','S','A','I','n','p','u','t','D','e','v','i','c','e','s',0};
481 HKEY key;
482 WCHAR reg_devices[256];
483 DWORD size = sizeof(reg_devices), type;
484 const WCHAR *value_name = (stream == SND_PCM_STREAM_PLAYBACK) ? ALSAOutputDevices : ALSAInputDevices;
486 /* @@ Wine registry key: HKCU\Software\Wine\Drivers\winealsa.drv */
487 if(RegOpenKeyW(HKEY_CURRENT_USER, drv_keyW, &key) == ERROR_SUCCESS){
488 if(RegQueryValueExW(key, value_name, 0, &type,
489 (BYTE*)reg_devices, &size) == ERROR_SUCCESS){
490 WCHAR *p = reg_devices;
492 if(type != REG_MULTI_SZ){
493 ERR("Registry ALSA device list value type must be REG_MULTI_SZ\n");
494 RegCloseKey(key);
495 return;
498 while(*p){
499 char devname[64];
501 WideCharToMultiByte(CP_UNIXCP, 0, p, -1, devname, sizeof(devname), NULL, NULL);
503 if(alsa_try_open(devname, stream)){
504 if(*num){
505 *ids = HeapReAlloc(GetProcessHeap(), 0, *ids, sizeof(WCHAR *) * (*num + 1));
506 *guids = HeapReAlloc(GetProcessHeap(), 0, *guids, sizeof(GUID) * (*num + 1));
507 }else{
508 *ids = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR *));
509 *guids = HeapAlloc(GetProcessHeap(), 0, sizeof(GUID));
511 (*ids)[*num] = construct_device_id(flow, p, NULL);
512 get_device_guid(flow, devname, &(*guids)[*num]);
513 ++*num;
516 p += lstrlenW(p) + 1;
520 RegCloseKey(key);
524 static HRESULT alsa_enum_devices(EDataFlow flow, WCHAR ***ids, GUID **guids,
525 UINT *num)
527 snd_pcm_stream_t stream = (flow == eRender ? SND_PCM_STREAM_PLAYBACK :
528 SND_PCM_STREAM_CAPTURE);
529 int err, card;
531 card = -1;
532 *num = 0;
534 if(alsa_try_open(defname, stream)){
535 *ids = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR *));
536 (*ids)[0] = construct_device_id(flow, defaultW, NULL);
537 *guids = HeapAlloc(GetProcessHeap(), 0, sizeof(GUID));
538 get_device_guid(flow, defname, &(*guids)[0]);
539 ++*num;
542 get_reg_devices(flow, stream, ids, guids, num);
544 for(err = snd_card_next(&card); card != -1 && err >= 0;
545 err = snd_card_next(&card)){
546 char cardpath[64];
547 char *cardname;
548 WCHAR *cardnameW;
549 snd_ctl_t *ctl;
550 DWORD len;
552 sprintf(cardpath, "hw:%u", card);
554 if((err = snd_ctl_open(&ctl, cardpath, 0)) < 0){
555 WARN("Unable to open ctl for ALSA device %s: %d (%s)\n", cardpath,
556 err, snd_strerror(err));
557 continue;
560 if(snd_card_get_name(card, &cardname) < 0) {
561 /* FIXME: Should be localized */
562 static const WCHAR nameW[] = {'U','n','k','n','o','w','n',' ','s','o','u','n','d','c','a','r','d',0};
563 WARN("Unable to get card name for ALSA device %s: %d (%s)\n",
564 cardpath, err, snd_strerror(err));
565 alsa_get_card_devices(flow, stream, ids, guids, num, ctl, card, nameW);
566 }else{
567 len = MultiByteToWideChar(CP_UNIXCP, 0, cardname, -1, NULL, 0);
568 cardnameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
570 if(!cardnameW){
571 free(cardname);
572 snd_ctl_close(ctl);
573 return E_OUTOFMEMORY;
575 MultiByteToWideChar(CP_UNIXCP, 0, cardname, -1, cardnameW, len);
577 alsa_get_card_devices(flow, stream, ids, guids, num, ctl, card, cardnameW);
579 HeapFree(GetProcessHeap(), 0, cardnameW);
580 free(cardname);
583 snd_ctl_close(ctl);
586 if(err != 0)
587 WARN("Got a failure during card enumeration: %d (%s)\n",
588 err, snd_strerror(err));
590 return S_OK;
593 HRESULT WINAPI AUDDRV_GetEndpointIDs(EDataFlow flow, WCHAR ***ids, GUID **guids,
594 UINT *num, UINT *def_index)
596 HRESULT hr;
598 TRACE("%d %p %p %p %p\n", flow, ids, guids, num, def_index);
600 *ids = NULL;
601 *guids = NULL;
603 hr = alsa_enum_devices(flow, ids, guids, num);
604 if(FAILED(hr)){
605 UINT i;
606 for(i = 0; i < *num; ++i)
607 HeapFree(GetProcessHeap(), 0, (*ids)[i]);
608 HeapFree(GetProcessHeap(), 0, *ids);
609 HeapFree(GetProcessHeap(), 0, *guids);
610 return E_OUTOFMEMORY;
613 TRACE("Enumerated %u devices\n", *num);
615 if(*num == 0){
616 HeapFree(GetProcessHeap(), 0, *ids);
617 *ids = NULL;
618 HeapFree(GetProcessHeap(), 0, *guids);
619 *guids = NULL;
622 *def_index = 0;
624 return S_OK;
627 /* Using the pulse PCM device from alsa-plugins 1.0.24 triggers a bug
628 * which causes audio to cease playing after a few minutes of playback.
629 * Setting handle_underrun=1 on pulse-backed ALSA devices seems to work
630 * around this issue. */
631 static snd_config_t *make_handle_underrun_config(const char *name)
633 snd_config_t *lconf, *dev_node, *hu_node, *type_node;
634 char dev_node_name[260];
635 const char *type_str;
636 int err;
638 snd_config_update();
640 if((err = snd_config_copy(&lconf, snd_config)) < 0){
641 WARN("snd_config_copy failed: %d (%s)\n", err, snd_strerror(err));
642 return NULL;
645 sprintf(dev_node_name, "pcm.%s", name);
646 err = snd_config_search(lconf, dev_node_name, &dev_node);
647 if(err == -ENOENT){
648 snd_config_delete(lconf);
649 return NULL;
651 if(err < 0){
652 snd_config_delete(lconf);
653 WARN("snd_config_search failed: %d (%s)\n", err, snd_strerror(err));
654 return NULL;
657 /* ALSA is extremely fragile. If it runs into a config setting it doesn't
658 * recognize, it tends to fail or assert. So we only want to inject
659 * handle_underrun=1 on devices that we know will recognize it. */
660 err = snd_config_search(dev_node, "type", &type_node);
661 if(err == -ENOENT){
662 snd_config_delete(lconf);
663 return NULL;
665 if(err < 0){
666 snd_config_delete(lconf);
667 WARN("snd_config_search failed: %d (%s)\n", err, snd_strerror(err));
668 return NULL;
671 if((err = snd_config_get_string(type_node, &type_str)) < 0){
672 snd_config_delete(lconf);
673 return NULL;
676 if(strcmp(type_str, "pulse") != 0){
677 snd_config_delete(lconf);
678 return NULL;
681 err = snd_config_search(dev_node, "handle_underrun", &hu_node);
682 if(err >= 0){
683 /* user already has an explicit handle_underrun setting, so don't
684 * use a local config */
685 snd_config_delete(lconf);
686 return NULL;
688 if(err != -ENOENT){
689 snd_config_delete(lconf);
690 WARN("snd_config_search failed: %d (%s)\n", err, snd_strerror(err));
691 return NULL;
694 if((err = snd_config_imake_integer(&hu_node, "handle_underrun", 1)) < 0){
695 snd_config_delete(lconf);
696 WARN("snd_config_imake_integer failed: %d (%s)\n", err,
697 snd_strerror(err));
698 return NULL;
701 if((err = snd_config_add(dev_node, hu_node)) < 0){
702 snd_config_delete(lconf);
703 WARN("snd_config_add failed: %d (%s)\n", err, snd_strerror(err));
704 return NULL;
707 return lconf;
710 static BOOL get_alsa_name_by_guid(GUID *guid, char *name, DWORD name_size, EDataFlow *flow)
712 HKEY devices_key;
713 UINT i = 0;
714 WCHAR key_name[256];
715 DWORD key_name_size;
717 if(RegOpenKeyExW(HKEY_CURRENT_USER, drv_key_devicesW, 0, KEY_READ, &devices_key) != ERROR_SUCCESS){
718 ERR("No devices found in registry?\n");
719 return FALSE;
722 while(1){
723 HKEY key;
724 DWORD size, type;
725 GUID reg_guid;
727 key_name_size = ARRAY_SIZE(key_name);
728 if(RegEnumKeyExW(devices_key, i++, key_name, &key_name_size, NULL,
729 NULL, NULL, NULL) != ERROR_SUCCESS)
730 break;
732 if(RegOpenKeyExW(devices_key, key_name, 0, KEY_READ, &key) != ERROR_SUCCESS){
733 WARN("Couldn't open key: %s\n", wine_dbgstr_w(key_name));
734 continue;
737 size = sizeof(reg_guid);
738 if(RegQueryValueExW(key, guidW, 0, &type,
739 (BYTE*)&reg_guid, &size) == ERROR_SUCCESS){
740 if(IsEqualGUID(&reg_guid, guid)){
741 RegCloseKey(key);
742 RegCloseKey(devices_key);
744 TRACE("Found matching device key: %s\n", wine_dbgstr_w(key_name));
746 if(key_name[0] == '0')
747 *flow = eRender;
748 else if(key_name[0] == '1')
749 *flow = eCapture;
750 else{
751 ERR("Unknown device type: %c\n", key_name[0]);
752 return FALSE;
755 WideCharToMultiByte(CP_UNIXCP, 0, key_name + 2, -1, name, name_size, NULL, NULL);
757 return TRUE;
761 RegCloseKey(key);
764 RegCloseKey(devices_key);
766 WARN("No matching device in registry for GUID %s\n", debugstr_guid(guid));
768 return FALSE;
771 HRESULT WINAPI AUDDRV_GetAudioEndpoint(GUID *guid, IMMDevice *dev, IAudioClient **out)
773 ACImpl *This;
774 int err;
775 snd_pcm_stream_t stream;
776 snd_config_t *lconf;
777 static BOOL handle_underrun = TRUE;
778 char alsa_name[256];
779 EDataFlow dataflow;
780 HRESULT hr;
782 TRACE("%s %p %p\n", debugstr_guid(guid), dev, out);
784 if(!get_alsa_name_by_guid(guid, alsa_name, sizeof(alsa_name), &dataflow))
785 return AUDCLNT_E_DEVICE_INVALIDATED;
787 This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(ACImpl));
788 if(!This)
789 return E_OUTOFMEMORY;
791 This->IAudioClient3_iface.lpVtbl = &AudioClient3_Vtbl;
792 This->IAudioRenderClient_iface.lpVtbl = &AudioRenderClient_Vtbl;
793 This->IAudioCaptureClient_iface.lpVtbl = &AudioCaptureClient_Vtbl;
794 This->IAudioClock_iface.lpVtbl = &AudioClock_Vtbl;
795 This->IAudioClock2_iface.lpVtbl = &AudioClock2_Vtbl;
796 This->IAudioStreamVolume_iface.lpVtbl = &AudioStreamVolume_Vtbl;
798 if(dataflow == eRender)
799 stream = SND_PCM_STREAM_PLAYBACK;
800 else if(dataflow == eCapture)
801 stream = SND_PCM_STREAM_CAPTURE;
802 else{
803 HeapFree(GetProcessHeap(), 0, This);
804 return E_UNEXPECTED;
807 hr = CoCreateFreeThreadedMarshaler((IUnknown *)&This->IAudioClient3_iface, &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 = (IAudioClient *)&This->IAudioClient3_iface;
855 IAudioClient3_AddRef(&This->IAudioClient3_iface);
857 return S_OK;
860 static HRESULT WINAPI AudioClient_QueryInterface(IAudioClient3 *iface,
861 REFIID riid, void **ppv)
863 ACImpl *This = impl_from_IAudioClient3(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) ||
870 IsEqualIID(riid, &IID_IAudioClient) ||
871 IsEqualIID(riid, &IID_IAudioClient2) ||
872 IsEqualIID(riid, &IID_IAudioClient3))
873 *ppv = iface;
874 else if(IsEqualIID(riid, &IID_IMarshal))
875 return IUnknown_QueryInterface(This->pUnkFTMarshal, riid, ppv);
877 if(*ppv){
878 IUnknown_AddRef((IUnknown*)*ppv);
879 return S_OK;
881 WARN("Unknown interface %s\n", debugstr_guid(riid));
882 return E_NOINTERFACE;
885 static ULONG WINAPI AudioClient_AddRef(IAudioClient3 *iface)
887 ACImpl *This = impl_from_IAudioClient3(iface);
888 ULONG ref;
889 ref = InterlockedIncrement(&This->ref);
890 TRACE("(%p) Refcount now %u\n", This, ref);
891 return ref;
894 static ULONG WINAPI AudioClient_Release(IAudioClient3 *iface)
896 ACImpl *This = impl_from_IAudioClient3(iface);
897 ULONG ref;
899 ref = InterlockedDecrement(&This->ref);
900 TRACE("(%p) Refcount now %u\n", This, ref);
901 if(!ref){
902 if(This->timer){
903 HANDLE event;
904 DWORD wait;
905 event = CreateEventW(NULL, TRUE, FALSE, NULL);
906 wait = !DeleteTimerQueueTimer(g_timer_q, This->timer, event);
907 wait = wait && GetLastError() == ERROR_IO_PENDING;
908 if(event && wait)
909 WaitForSingleObject(event, INFINITE);
910 CloseHandle(event);
913 IAudioClient3_Stop(iface);
914 IMMDevice_Release(This->parent);
915 IUnknown_Release(This->pUnkFTMarshal);
916 This->lock.DebugInfo->Spare[0] = 0;
917 DeleteCriticalSection(&This->lock);
918 snd_pcm_drop(This->pcm_handle);
919 snd_pcm_close(This->pcm_handle);
920 if(This->initted){
921 EnterCriticalSection(&g_sessions_lock);
922 list_remove(&This->entry);
923 LeaveCriticalSection(&g_sessions_lock);
925 HeapFree(GetProcessHeap(), 0, This->vols);
926 HeapFree(GetProcessHeap(), 0, This->local_buffer);
927 HeapFree(GetProcessHeap(), 0, This->remapping_buf);
928 HeapFree(GetProcessHeap(), 0, This->silence_buf);
929 HeapFree(GetProcessHeap(), 0, This->tmp_buffer);
930 HeapFree(GetProcessHeap(), 0, This->hw_params);
931 CoTaskMemFree(This->fmt);
932 HeapFree(GetProcessHeap(), 0, This);
934 return ref;
937 static void dump_fmt(const WAVEFORMATEX *fmt)
939 TRACE("wFormatTag: 0x%x (", fmt->wFormatTag);
940 switch(fmt->wFormatTag){
941 case WAVE_FORMAT_PCM:
942 TRACE("WAVE_FORMAT_PCM");
943 break;
944 case WAVE_FORMAT_IEEE_FLOAT:
945 TRACE("WAVE_FORMAT_IEEE_FLOAT");
946 break;
947 case WAVE_FORMAT_EXTENSIBLE:
948 TRACE("WAVE_FORMAT_EXTENSIBLE");
949 break;
950 default:
951 TRACE("Unknown");
952 break;
954 TRACE(")\n");
956 TRACE("nChannels: %u\n", fmt->nChannels);
957 TRACE("nSamplesPerSec: %u\n", fmt->nSamplesPerSec);
958 TRACE("nAvgBytesPerSec: %u\n", fmt->nAvgBytesPerSec);
959 TRACE("nBlockAlign: %u\n", fmt->nBlockAlign);
960 TRACE("wBitsPerSample: %u\n", fmt->wBitsPerSample);
961 TRACE("cbSize: %u\n", fmt->cbSize);
963 if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE){
964 WAVEFORMATEXTENSIBLE *fmtex = (void*)fmt;
965 TRACE("dwChannelMask: %08x\n", fmtex->dwChannelMask);
966 TRACE("Samples: %04x\n", fmtex->Samples.wReserved);
967 TRACE("SubFormat: %s\n", wine_dbgstr_guid(&fmtex->SubFormat));
971 static WAVEFORMATEX *clone_format(const WAVEFORMATEX *fmt)
973 WAVEFORMATEX *ret;
974 size_t size;
976 if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
977 size = sizeof(WAVEFORMATEXTENSIBLE);
978 else
979 size = sizeof(WAVEFORMATEX);
981 ret = CoTaskMemAlloc(size);
982 if(!ret)
983 return NULL;
985 memcpy(ret, fmt, size);
987 ret->cbSize = size - sizeof(WAVEFORMATEX);
989 return ret;
992 static snd_pcm_format_t alsa_format(const WAVEFORMATEX *fmt)
994 snd_pcm_format_t format = SND_PCM_FORMAT_UNKNOWN;
995 const WAVEFORMATEXTENSIBLE *fmtex = (const WAVEFORMATEXTENSIBLE *)fmt;
997 if(fmt->wFormatTag == WAVE_FORMAT_PCM ||
998 (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
999 IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))){
1000 if(fmt->wBitsPerSample == 8)
1001 format = SND_PCM_FORMAT_U8;
1002 else if(fmt->wBitsPerSample == 16)
1003 format = SND_PCM_FORMAT_S16_LE;
1004 else if(fmt->wBitsPerSample == 24)
1005 format = SND_PCM_FORMAT_S24_3LE;
1006 else if(fmt->wBitsPerSample == 32)
1007 format = SND_PCM_FORMAT_S32_LE;
1008 else
1009 WARN("Unsupported bit depth: %u\n", fmt->wBitsPerSample);
1010 if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
1011 fmt->wBitsPerSample != fmtex->Samples.wValidBitsPerSample){
1012 if(fmtex->Samples.wValidBitsPerSample == 20 && fmt->wBitsPerSample == 24)
1013 format = SND_PCM_FORMAT_S20_3LE;
1014 else
1015 WARN("Unsupported ValidBits: %u\n", fmtex->Samples.wValidBitsPerSample);
1017 }else if(fmt->wFormatTag == WAVE_FORMAT_IEEE_FLOAT ||
1018 (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
1019 IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT))){
1020 if(fmt->wBitsPerSample == 32)
1021 format = SND_PCM_FORMAT_FLOAT_LE;
1022 else if(fmt->wBitsPerSample == 64)
1023 format = SND_PCM_FORMAT_FLOAT64_LE;
1024 else
1025 WARN("Unsupported float size: %u\n", fmt->wBitsPerSample);
1026 }else
1027 WARN("Unknown wave format: %04x\n", fmt->wFormatTag);
1028 return format;
1031 static void session_init_vols(AudioSession *session, UINT channels)
1033 if(session->channel_count < channels){
1034 UINT i;
1036 if(session->channel_vols)
1037 session->channel_vols = HeapReAlloc(GetProcessHeap(), 0,
1038 session->channel_vols, sizeof(float) * channels);
1039 else
1040 session->channel_vols = HeapAlloc(GetProcessHeap(), 0,
1041 sizeof(float) * channels);
1042 if(!session->channel_vols)
1043 return;
1045 for(i = session->channel_count; i < channels; ++i)
1046 session->channel_vols[i] = 1.f;
1048 session->channel_count = channels;
1052 static AudioSession *create_session(const GUID *guid, IMMDevice *device,
1053 UINT num_channels)
1055 AudioSession *ret;
1057 ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(AudioSession));
1058 if(!ret)
1059 return NULL;
1061 memcpy(&ret->guid, guid, sizeof(GUID));
1063 ret->device = device;
1065 list_init(&ret->clients);
1067 list_add_head(&g_sessions, &ret->entry);
1069 InitializeCriticalSection(&ret->lock);
1070 ret->lock.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": AudioSession.lock");
1072 session_init_vols(ret, num_channels);
1074 ret->master_vol = 1.f;
1076 return ret;
1079 /* if channels == 0, then this will return or create a session with
1080 * matching dataflow and GUID. otherwise, channels must also match */
1081 static HRESULT get_audio_session(const GUID *sessionguid,
1082 IMMDevice *device, UINT channels, AudioSession **out)
1084 AudioSession *session;
1086 if(!sessionguid || IsEqualGUID(sessionguid, &GUID_NULL)){
1087 *out = create_session(&GUID_NULL, device, channels);
1088 if(!*out)
1089 return E_OUTOFMEMORY;
1091 return S_OK;
1094 *out = NULL;
1095 LIST_FOR_EACH_ENTRY(session, &g_sessions, AudioSession, entry){
1096 if(session->device == device &&
1097 IsEqualGUID(sessionguid, &session->guid)){
1098 session_init_vols(session, channels);
1099 *out = session;
1100 break;
1104 if(!*out){
1105 *out = create_session(sessionguid, device, channels);
1106 if(!*out)
1107 return E_OUTOFMEMORY;
1110 return S_OK;
1113 static int alsa_channel_index(DWORD flag)
1115 switch(flag){
1116 case SPEAKER_FRONT_LEFT:
1117 return 0;
1118 case SPEAKER_FRONT_RIGHT:
1119 return 1;
1120 case SPEAKER_BACK_LEFT:
1121 return 2;
1122 case SPEAKER_BACK_RIGHT:
1123 return 3;
1124 case SPEAKER_FRONT_CENTER:
1125 return 4;
1126 case SPEAKER_LOW_FREQUENCY:
1127 return 5;
1128 case SPEAKER_SIDE_LEFT:
1129 return 6;
1130 case SPEAKER_SIDE_RIGHT:
1131 return 7;
1133 return -1;
1136 static BOOL need_remapping(ACImpl *This, const WAVEFORMATEX *fmt, int *map)
1138 unsigned int i;
1139 for(i = 0; i < fmt->nChannels; ++i){
1140 if(map[i] != i)
1141 return TRUE;
1143 return FALSE;
1146 static DWORD get_channel_mask(unsigned int channels)
1148 switch(channels){
1149 case 0:
1150 return 0;
1151 case 1:
1152 return KSAUDIO_SPEAKER_MONO;
1153 case 2:
1154 return KSAUDIO_SPEAKER_STEREO;
1155 case 3:
1156 return KSAUDIO_SPEAKER_STEREO | SPEAKER_LOW_FREQUENCY;
1157 case 4:
1158 return KSAUDIO_SPEAKER_QUAD; /* not _SURROUND */
1159 case 5:
1160 return KSAUDIO_SPEAKER_QUAD | SPEAKER_LOW_FREQUENCY;
1161 case 6:
1162 return KSAUDIO_SPEAKER_5POINT1; /* not 5POINT1_SURROUND */
1163 case 7:
1164 return KSAUDIO_SPEAKER_5POINT1 | SPEAKER_BACK_CENTER;
1165 case 8:
1166 return KSAUDIO_SPEAKER_7POINT1_SURROUND; /* Vista deprecates 7POINT1 */
1168 FIXME("Unknown speaker configuration: %u\n", channels);
1169 return 0;
1172 static HRESULT map_channels(ACImpl *This, const WAVEFORMATEX *fmt, int *alsa_channels, int *map)
1174 BOOL need_remap;
1176 if(This->dataflow != eCapture && (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE || fmt->nChannels > 2) ){
1177 WAVEFORMATEXTENSIBLE *fmtex = (void*)fmt;
1178 DWORD mask, flag = SPEAKER_FRONT_LEFT;
1179 UINT i = 0;
1181 if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
1182 fmtex->dwChannelMask != 0)
1183 mask = fmtex->dwChannelMask;
1184 else
1185 mask = get_channel_mask(fmt->nChannels);
1187 *alsa_channels = 0;
1189 while(i < fmt->nChannels && !(flag & SPEAKER_RESERVED)){
1190 if(mask & flag){
1191 map[i] = alsa_channel_index(flag);
1192 TRACE("Mapping mmdevapi channel %u (0x%x) to ALSA channel %d\n",
1193 i, flag, map[i]);
1194 if(map[i] >= *alsa_channels)
1195 *alsa_channels = map[i] + 1;
1196 ++i;
1198 flag <<= 1;
1201 while(i < fmt->nChannels){
1202 map[i] = *alsa_channels;
1203 TRACE("Mapping mmdevapi channel %u to ALSA channel %d\n",
1204 i, map[i]);
1205 ++*alsa_channels;
1206 ++i;
1209 for(i = 0; i < fmt->nChannels; ++i){
1210 if(map[i] == -1){
1211 map[i] = *alsa_channels;
1212 ++*alsa_channels;
1213 TRACE("Remapping mmdevapi channel %u to ALSA channel %d\n",
1214 i, map[i]);
1218 need_remap = need_remapping(This, fmt, map);
1219 }else{
1220 *alsa_channels = fmt->nChannels;
1222 need_remap = FALSE;
1225 TRACE("need_remapping: %u, alsa_channels: %d\n", need_remap, *alsa_channels);
1227 return need_remap ? S_OK : S_FALSE;
1230 static void silence_buffer(ACImpl *This, BYTE *buffer, UINT32 frames)
1232 WAVEFORMATEXTENSIBLE *fmtex = (WAVEFORMATEXTENSIBLE*)This->fmt;
1233 if((This->fmt->wFormatTag == WAVE_FORMAT_PCM ||
1234 (This->fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
1235 IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))) &&
1236 This->fmt->wBitsPerSample == 8)
1237 memset(buffer, 128, frames * This->fmt->nBlockAlign);
1238 else
1239 memset(buffer, 0, frames * This->fmt->nBlockAlign);
1242 static HRESULT WINAPI AudioClient_Initialize(IAudioClient3 *iface,
1243 AUDCLNT_SHAREMODE mode, DWORD flags, REFERENCE_TIME duration,
1244 REFERENCE_TIME period, const WAVEFORMATEX *fmt,
1245 const GUID *sessionguid)
1247 ACImpl *This = impl_from_IAudioClient3(iface);
1248 snd_pcm_sw_params_t *sw_params = NULL;
1249 snd_pcm_format_t format;
1250 unsigned int rate, alsa_period_us;
1251 int err, i;
1252 HRESULT hr = S_OK;
1254 TRACE("(%p)->(%x, %x, %s, %s, %p, %s)\n", This, mode, flags,
1255 wine_dbgstr_longlong(duration), wine_dbgstr_longlong(period), fmt, debugstr_guid(sessionguid));
1257 if(!fmt)
1258 return E_POINTER;
1260 if(mode != AUDCLNT_SHAREMODE_SHARED && mode != AUDCLNT_SHAREMODE_EXCLUSIVE)
1261 return E_INVALIDARG;
1263 if(flags & ~(AUDCLNT_STREAMFLAGS_CROSSPROCESS |
1264 AUDCLNT_STREAMFLAGS_LOOPBACK |
1265 AUDCLNT_STREAMFLAGS_EVENTCALLBACK |
1266 AUDCLNT_STREAMFLAGS_NOPERSIST |
1267 AUDCLNT_STREAMFLAGS_RATEADJUST |
1268 AUDCLNT_SESSIONFLAGS_EXPIREWHENUNOWNED |
1269 AUDCLNT_SESSIONFLAGS_DISPLAY_HIDE |
1270 AUDCLNT_SESSIONFLAGS_DISPLAY_HIDEWHENEXPIRED |
1271 AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY |
1272 AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM)){
1273 FIXME("Unknown flags: %08x\n", flags);
1274 return E_INVALIDARG;
1277 if(mode == AUDCLNT_SHAREMODE_SHARED){
1278 period = DefaultPeriod;
1279 if( duration < 3 * period)
1280 duration = 3 * period;
1281 }else{
1282 if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE){
1283 if(((WAVEFORMATEXTENSIBLE*)fmt)->dwChannelMask == 0 ||
1284 ((WAVEFORMATEXTENSIBLE*)fmt)->dwChannelMask & SPEAKER_RESERVED)
1285 return AUDCLNT_E_UNSUPPORTED_FORMAT;
1288 if(!period)
1289 period = DefaultPeriod; /* not minimum */
1290 if(period < MinimumPeriod || period > 5000000)
1291 return AUDCLNT_E_INVALID_DEVICE_PERIOD;
1292 if(duration > 20000000) /* the smaller the period, the lower this limit */
1293 return AUDCLNT_E_BUFFER_SIZE_ERROR;
1294 if(flags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK){
1295 if(duration != period)
1296 return AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL;
1297 FIXME("EXCLUSIVE mode with EVENTCALLBACK\n");
1298 return AUDCLNT_E_DEVICE_IN_USE;
1299 }else{
1300 if( duration < 8 * period)
1301 duration = 8 * period; /* may grow above 2s */
1305 EnterCriticalSection(&This->lock);
1307 if(This->initted){
1308 LeaveCriticalSection(&This->lock);
1309 return AUDCLNT_E_ALREADY_INITIALIZED;
1312 dump_fmt(fmt);
1314 This->need_remapping = map_channels(This, fmt, &This->alsa_channels, This->alsa_channel_map) == S_OK;
1316 if((err = snd_pcm_hw_params_any(This->pcm_handle, This->hw_params)) < 0){
1317 WARN("Unable to get hw_params: %d (%s)\n", err, snd_strerror(err));
1318 hr = AUDCLNT_E_ENDPOINT_CREATE_FAILED;
1319 goto exit;
1322 if((err = snd_pcm_hw_params_set_access(This->pcm_handle, This->hw_params,
1323 SND_PCM_ACCESS_RW_INTERLEAVED)) < 0){
1324 WARN("Unable to set access: %d (%s)\n", err, snd_strerror(err));
1325 hr = AUDCLNT_E_ENDPOINT_CREATE_FAILED;
1326 goto exit;
1329 format = alsa_format(fmt);
1330 if (format == SND_PCM_FORMAT_UNKNOWN){
1331 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
1332 goto exit;
1335 if((err = snd_pcm_hw_params_set_format(This->pcm_handle, This->hw_params,
1336 format)) < 0){
1337 WARN("Unable to set ALSA format to %u: %d (%s)\n", format, err,
1338 snd_strerror(err));
1339 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
1340 goto exit;
1343 This->alsa_format = format;
1345 rate = fmt->nSamplesPerSec;
1346 if((err = snd_pcm_hw_params_set_rate_near(This->pcm_handle, This->hw_params,
1347 &rate, NULL)) < 0){
1348 WARN("Unable to set rate to %u: %d (%s)\n", rate, err,
1349 snd_strerror(err));
1350 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
1351 goto exit;
1354 if((err = snd_pcm_hw_params_set_channels(This->pcm_handle, This->hw_params,
1355 This->alsa_channels)) < 0){
1356 WARN("Unable to set channels to %u: %d (%s)\n", fmt->nChannels, err,
1357 snd_strerror(err));
1358 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
1359 goto exit;
1362 This->mmdev_period_rt = period;
1363 alsa_period_us = This->mmdev_period_rt / 10;
1364 if((err = snd_pcm_hw_params_set_period_time_near(This->pcm_handle,
1365 This->hw_params, &alsa_period_us, NULL)) < 0)
1366 WARN("Unable to set period time near %u: %d (%s)\n", alsa_period_us,
1367 err, snd_strerror(err));
1368 /* ALSA updates the output variable alsa_period_us */
1370 This->mmdev_period_frames = MulDiv(fmt->nSamplesPerSec,
1371 This->mmdev_period_rt, 10000000);
1373 /* Buffer 4 ALSA periods if large enough, else 4 mmdevapi periods */
1374 This->alsa_bufsize_frames = This->mmdev_period_frames * 4;
1375 if(err < 0 || alsa_period_us < period / 10)
1376 err = snd_pcm_hw_params_set_buffer_size_near(This->pcm_handle,
1377 This->hw_params, &This->alsa_bufsize_frames);
1378 else{
1379 unsigned int periods = 4;
1380 err = snd_pcm_hw_params_set_periods_near(This->pcm_handle, This->hw_params, &periods, NULL);
1382 if(err < 0)
1383 WARN("Unable to set buffer size: %d (%s)\n", err, snd_strerror(err));
1385 if((err = snd_pcm_hw_params(This->pcm_handle, This->hw_params)) < 0){
1386 WARN("Unable to set hw params: %d (%s)\n", err, snd_strerror(err));
1387 hr = AUDCLNT_E_ENDPOINT_CREATE_FAILED;
1388 goto exit;
1391 if((err = snd_pcm_hw_params_get_period_size(This->hw_params,
1392 &This->alsa_period_frames, NULL)) < 0){
1393 WARN("Unable to get period size: %d (%s)\n", err, snd_strerror(err));
1394 hr = AUDCLNT_E_ENDPOINT_CREATE_FAILED;
1395 goto exit;
1398 if((err = snd_pcm_hw_params_get_buffer_size(This->hw_params,
1399 &This->alsa_bufsize_frames)) < 0){
1400 WARN("Unable to get buffer size: %d (%s)\n", err, snd_strerror(err));
1401 hr = AUDCLNT_E_ENDPOINT_CREATE_FAILED;
1402 goto exit;
1405 sw_params = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, snd_pcm_sw_params_sizeof());
1406 if(!sw_params){
1407 hr = E_OUTOFMEMORY;
1408 goto exit;
1411 if((err = snd_pcm_sw_params_current(This->pcm_handle, sw_params)) < 0){
1412 WARN("Unable to get sw_params: %d (%s)\n", err, snd_strerror(err));
1413 hr = AUDCLNT_E_ENDPOINT_CREATE_FAILED;
1414 goto exit;
1417 if((err = snd_pcm_sw_params_set_start_threshold(This->pcm_handle,
1418 sw_params, 1)) < 0){
1419 WARN("Unable set start threshold to 1: %d (%s)\n", err, snd_strerror(err));
1420 hr = AUDCLNT_E_ENDPOINT_CREATE_FAILED;
1421 goto exit;
1424 if((err = snd_pcm_sw_params_set_stop_threshold(This->pcm_handle,
1425 sw_params, This->alsa_bufsize_frames)) < 0){
1426 WARN("Unable set stop threshold to %lu: %d (%s)\n",
1427 This->alsa_bufsize_frames, err, snd_strerror(err));
1428 hr = AUDCLNT_E_ENDPOINT_CREATE_FAILED;
1429 goto exit;
1432 if((err = snd_pcm_sw_params(This->pcm_handle, sw_params)) < 0){
1433 WARN("Unable to set sw params: %d (%s)\n", err, snd_strerror(err));
1434 hr = AUDCLNT_E_ENDPOINT_CREATE_FAILED;
1435 goto exit;
1438 if((err = snd_pcm_prepare(This->pcm_handle)) < 0){
1439 WARN("Unable to prepare device: %d (%s)\n", err, snd_strerror(err));
1440 hr = AUDCLNT_E_ENDPOINT_CREATE_FAILED;
1441 goto exit;
1444 /* Bear in mind weird situations where
1445 * ALSA period (50ms) > mmdevapi buffer (3x10ms)
1446 * or surprising rounding as seen with 22050x8x1 with Pulse:
1447 * ALSA period 220 vs. 221 frames in mmdevapi and
1448 * buffer 883 vs. 2205 frames in mmdevapi! */
1449 This->bufsize_frames = MulDiv(duration, fmt->nSamplesPerSec, 10000000);
1450 if(mode == AUDCLNT_SHAREMODE_EXCLUSIVE)
1451 This->bufsize_frames -= This->bufsize_frames % This->mmdev_period_frames;
1452 This->hidden_frames = This->alsa_period_frames + This->mmdev_period_frames +
1453 MulDiv(fmt->nSamplesPerSec, EXTRA_SAFE_RT, 10000000);
1454 /* leave no less than about 1.33ms or 256 bytes of data after a rewind */
1455 This->safe_rewind_frames = max(256 / fmt->nBlockAlign, MulDiv(133, fmt->nSamplesPerSec, 100000));
1457 /* Check if the ALSA buffer is so small that it will run out before
1458 * the next MMDevAPI period tick occurs. Allow a little wiggle room
1459 * with 120% of the period time. */
1460 if(This->alsa_bufsize_frames < 1.2 * This->mmdev_period_frames)
1461 FIXME("ALSA buffer time is too small. Expect underruns. (%lu < %u * 1.2)\n",
1462 This->alsa_bufsize_frames, This->mmdev_period_frames);
1464 This->fmt = clone_format(fmt);
1465 if(!This->fmt){
1466 hr = E_OUTOFMEMORY;
1467 goto exit;
1470 This->local_buffer = HeapAlloc(GetProcessHeap(), 0,
1471 This->bufsize_frames * fmt->nBlockAlign);
1472 if(!This->local_buffer){
1473 hr = E_OUTOFMEMORY;
1474 goto exit;
1476 silence_buffer(This, This->local_buffer, This->bufsize_frames);
1478 This->silence_buf = HeapAlloc(GetProcessHeap(), 0,
1479 This->alsa_period_frames * This->fmt->nBlockAlign);
1480 if(!This->silence_buf){
1481 hr = E_OUTOFMEMORY;
1482 goto exit;
1484 silence_buffer(This, This->silence_buf, This->alsa_period_frames);
1486 This->vols = HeapAlloc(GetProcessHeap(), 0, fmt->nChannels * sizeof(float));
1487 if(!This->vols){
1488 hr = E_OUTOFMEMORY;
1489 goto exit;
1492 for(i = 0; i < fmt->nChannels; ++i)
1493 This->vols[i] = 1.f;
1495 This->share = mode;
1496 This->flags = flags;
1498 EnterCriticalSection(&g_sessions_lock);
1500 hr = get_audio_session(sessionguid, This->parent, fmt->nChannels,
1501 &This->session);
1502 if(FAILED(hr)){
1503 LeaveCriticalSection(&g_sessions_lock);
1504 goto exit;
1507 list_add_tail(&This->session->clients, &This->entry);
1509 LeaveCriticalSection(&g_sessions_lock);
1511 This->initted = TRUE;
1513 TRACE("ALSA period: %lu frames\n", This->alsa_period_frames);
1514 TRACE("ALSA buffer: %lu frames\n", This->alsa_bufsize_frames);
1515 TRACE("MMDevice period: %u frames\n", This->mmdev_period_frames);
1516 TRACE("MMDevice buffer: %u frames\n", This->bufsize_frames);
1518 exit:
1519 HeapFree(GetProcessHeap(), 0, sw_params);
1520 if(FAILED(hr)){
1521 HeapFree(GetProcessHeap(), 0, This->local_buffer);
1522 This->local_buffer = NULL;
1523 CoTaskMemFree(This->fmt);
1524 This->fmt = NULL;
1525 HeapFree(GetProcessHeap(), 0, This->vols);
1526 This->vols = NULL;
1529 LeaveCriticalSection(&This->lock);
1531 return hr;
1534 static HRESULT WINAPI AudioClient_GetBufferSize(IAudioClient3 *iface,
1535 UINT32 *out)
1537 ACImpl *This = impl_from_IAudioClient3(iface);
1539 TRACE("(%p)->(%p)\n", This, out);
1541 if(!out)
1542 return E_POINTER;
1544 EnterCriticalSection(&This->lock);
1546 if(!This->initted){
1547 LeaveCriticalSection(&This->lock);
1548 return AUDCLNT_E_NOT_INITIALIZED;
1551 *out = This->bufsize_frames;
1553 LeaveCriticalSection(&This->lock);
1555 return S_OK;
1558 static HRESULT WINAPI AudioClient_GetStreamLatency(IAudioClient3 *iface,
1559 REFERENCE_TIME *latency)
1561 ACImpl *This = impl_from_IAudioClient3(iface);
1563 TRACE("(%p)->(%p)\n", This, latency);
1565 if(!latency)
1566 return E_POINTER;
1568 EnterCriticalSection(&This->lock);
1570 if(!This->initted){
1571 LeaveCriticalSection(&This->lock);
1572 return AUDCLNT_E_NOT_INITIALIZED;
1575 /* Hide some frames in the ALSA buffer. Allows us to return GetCurrentPadding=0
1576 * yet have enough data left to play (as if it were in native's mixer). Add:
1577 * + mmdevapi_period such that at the end of it, ALSA still has data;
1578 * + EXTRA_SAFE (~4ms) to allow for late callback invocation / fluctuation;
1579 * + alsa_period such that ALSA always has at least one period to play. */
1580 if(This->dataflow == eRender)
1581 *latency = MulDiv(This->hidden_frames, 10000000, This->fmt->nSamplesPerSec);
1582 else
1583 *latency = MulDiv(This->alsa_period_frames, 10000000, This->fmt->nSamplesPerSec)
1584 + This->mmdev_period_rt;
1586 LeaveCriticalSection(&This->lock);
1588 return S_OK;
1591 static HRESULT WINAPI AudioClient_GetCurrentPadding(IAudioClient3 *iface,
1592 UINT32 *out)
1594 ACImpl *This = impl_from_IAudioClient3(iface);
1596 TRACE("(%p)->(%p)\n", This, out);
1598 if(!out)
1599 return E_POINTER;
1601 EnterCriticalSection(&This->lock);
1603 if(!This->initted){
1604 LeaveCriticalSection(&This->lock);
1605 return AUDCLNT_E_NOT_INITIALIZED;
1608 /* padding is solely updated at callback time in shared mode */
1609 *out = This->held_frames;
1611 LeaveCriticalSection(&This->lock);
1613 TRACE("pad: %u\n", *out);
1615 return S_OK;
1618 static HRESULT WINAPI AudioClient_IsFormatSupported(IAudioClient3 *iface,
1619 AUDCLNT_SHAREMODE mode, const WAVEFORMATEX *fmt,
1620 WAVEFORMATEX **out)
1622 ACImpl *This = impl_from_IAudioClient3(iface);
1623 snd_pcm_format_mask_t *formats = NULL;
1624 snd_pcm_format_t format;
1625 HRESULT hr = S_OK;
1626 WAVEFORMATEX *closest = NULL;
1627 unsigned int max = 0, min = 0;
1628 int err;
1629 int alsa_channels, alsa_channel_map[32];
1631 TRACE("(%p)->(%x, %p, %p)\n", This, mode, fmt, out);
1633 if(!fmt || (mode == AUDCLNT_SHAREMODE_SHARED && !out))
1634 return E_POINTER;
1636 if(mode != AUDCLNT_SHAREMODE_SHARED && mode != AUDCLNT_SHAREMODE_EXCLUSIVE)
1637 return E_INVALIDARG;
1639 if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
1640 fmt->cbSize < sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX))
1641 return E_INVALIDARG;
1643 dump_fmt(fmt);
1645 if(out){
1646 *out = NULL;
1647 if(mode != AUDCLNT_SHAREMODE_SHARED)
1648 out = NULL;
1651 if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
1652 (fmt->nAvgBytesPerSec == 0 ||
1653 fmt->nBlockAlign == 0 ||
1654 ((WAVEFORMATEXTENSIBLE*)fmt)->Samples.wValidBitsPerSample > fmt->wBitsPerSample))
1655 return E_INVALIDARG;
1657 if(fmt->nChannels == 0)
1658 return AUDCLNT_E_UNSUPPORTED_FORMAT;
1660 EnterCriticalSection(&This->lock);
1662 if((err = snd_pcm_hw_params_any(This->pcm_handle, This->hw_params)) < 0){
1663 hr = AUDCLNT_E_DEVICE_INVALIDATED;
1664 goto exit;
1667 formats = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
1668 snd_pcm_format_mask_sizeof());
1669 if(!formats){
1670 hr = E_OUTOFMEMORY;
1671 goto exit;
1674 snd_pcm_hw_params_get_format_mask(This->hw_params, formats);
1675 format = alsa_format(fmt);
1676 if (format == SND_PCM_FORMAT_UNKNOWN ||
1677 !snd_pcm_format_mask_test(formats, format)){
1678 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
1679 goto exit;
1682 closest = clone_format(fmt);
1683 if(!closest){
1684 hr = E_OUTOFMEMORY;
1685 goto exit;
1688 if((err = snd_pcm_hw_params_get_rate_min(This->hw_params, &min, NULL)) < 0){
1689 hr = AUDCLNT_E_DEVICE_INVALIDATED;
1690 WARN("Unable to get min rate: %d (%s)\n", err, snd_strerror(err));
1691 goto exit;
1694 if((err = snd_pcm_hw_params_get_rate_max(This->hw_params, &max, NULL)) < 0){
1695 hr = AUDCLNT_E_DEVICE_INVALIDATED;
1696 WARN("Unable to get max rate: %d (%s)\n", err, snd_strerror(err));
1697 goto exit;
1700 if(fmt->nSamplesPerSec < min || fmt->nSamplesPerSec > max){
1701 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
1702 goto exit;
1705 if((err = snd_pcm_hw_params_get_channels_min(This->hw_params, &min)) < 0){
1706 hr = AUDCLNT_E_DEVICE_INVALIDATED;
1707 WARN("Unable to get min channels: %d (%s)\n", err, snd_strerror(err));
1708 goto exit;
1711 if((err = snd_pcm_hw_params_get_channels_max(This->hw_params, &max)) < 0){
1712 hr = AUDCLNT_E_DEVICE_INVALIDATED;
1713 WARN("Unable to get max channels: %d (%s)\n", err, snd_strerror(err));
1714 goto exit;
1716 if(fmt->nChannels > max){
1717 hr = S_FALSE;
1718 closest->nChannels = max;
1719 }else if(fmt->nChannels < min){
1720 hr = S_FALSE;
1721 closest->nChannels = min;
1724 map_channels(This, fmt, &alsa_channels, alsa_channel_map);
1726 if(alsa_channels > max){
1727 hr = S_FALSE;
1728 closest->nChannels = max;
1731 if(closest->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
1732 ((WAVEFORMATEXTENSIBLE*)closest)->dwChannelMask = get_channel_mask(closest->nChannels);
1734 if(fmt->nBlockAlign != fmt->nChannels * fmt->wBitsPerSample / 8 ||
1735 fmt->nAvgBytesPerSec != fmt->nBlockAlign * fmt->nSamplesPerSec ||
1736 (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
1737 ((WAVEFORMATEXTENSIBLE*)fmt)->Samples.wValidBitsPerSample < fmt->wBitsPerSample))
1738 hr = S_FALSE;
1740 if(mode == AUDCLNT_SHAREMODE_EXCLUSIVE &&
1741 fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE){
1742 if(((WAVEFORMATEXTENSIBLE*)fmt)->dwChannelMask == 0 ||
1743 ((WAVEFORMATEXTENSIBLE*)fmt)->dwChannelMask & SPEAKER_RESERVED)
1744 hr = S_FALSE;
1747 exit:
1748 LeaveCriticalSection(&This->lock);
1749 HeapFree(GetProcessHeap(), 0, formats);
1751 if(hr == S_FALSE && !out)
1752 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
1754 if(hr == S_FALSE && out) {
1755 closest->nBlockAlign =
1756 closest->nChannels * closest->wBitsPerSample / 8;
1757 closest->nAvgBytesPerSec =
1758 closest->nBlockAlign * closest->nSamplesPerSec;
1759 if(closest->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
1760 ((WAVEFORMATEXTENSIBLE*)closest)->Samples.wValidBitsPerSample = closest->wBitsPerSample;
1761 *out = closest;
1762 } else
1763 CoTaskMemFree(closest);
1765 TRACE("returning: %08x\n", hr);
1766 return hr;
1769 static HRESULT WINAPI AudioClient_GetMixFormat(IAudioClient3 *iface,
1770 WAVEFORMATEX **pwfx)
1772 ACImpl *This = impl_from_IAudioClient3(iface);
1773 WAVEFORMATEXTENSIBLE *fmt;
1774 snd_pcm_format_mask_t *formats;
1775 unsigned int max_rate, max_channels;
1776 int err;
1777 HRESULT hr = S_OK;
1779 TRACE("(%p)->(%p)\n", This, pwfx);
1781 if(!pwfx)
1782 return E_POINTER;
1783 *pwfx = NULL;
1785 fmt = CoTaskMemAlloc(sizeof(WAVEFORMATEXTENSIBLE));
1786 if(!fmt)
1787 return E_OUTOFMEMORY;
1789 formats = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, snd_pcm_format_mask_sizeof());
1790 if(!formats){
1791 CoTaskMemFree(fmt);
1792 return E_OUTOFMEMORY;
1795 EnterCriticalSection(&This->lock);
1797 if((err = snd_pcm_hw_params_any(This->pcm_handle, This->hw_params)) < 0){
1798 WARN("Unable to get hw_params: %d (%s)\n", err, snd_strerror(err));
1799 hr = AUDCLNT_E_DEVICE_INVALIDATED;
1800 goto exit;
1803 snd_pcm_hw_params_get_format_mask(This->hw_params, formats);
1805 fmt->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
1806 if(snd_pcm_format_mask_test(formats, SND_PCM_FORMAT_FLOAT_LE)){
1807 fmt->Format.wBitsPerSample = 32;
1808 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
1809 }else if(snd_pcm_format_mask_test(formats, SND_PCM_FORMAT_S16_LE)){
1810 fmt->Format.wBitsPerSample = 16;
1811 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1812 }else if(snd_pcm_format_mask_test(formats, SND_PCM_FORMAT_U8)){
1813 fmt->Format.wBitsPerSample = 8;
1814 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1815 }else if(snd_pcm_format_mask_test(formats, SND_PCM_FORMAT_S32_LE)){
1816 fmt->Format.wBitsPerSample = 32;
1817 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1818 }else if(snd_pcm_format_mask_test(formats, SND_PCM_FORMAT_S24_3LE)){
1819 fmt->Format.wBitsPerSample = 24;
1820 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1821 }else{
1822 ERR("Didn't recognize any available ALSA formats\n");
1823 hr = AUDCLNT_E_DEVICE_INVALIDATED;
1824 goto exit;
1827 if((err = snd_pcm_hw_params_get_channels_max(This->hw_params,
1828 &max_channels)) < 0){
1829 WARN("Unable to get max channels: %d (%s)\n", err, snd_strerror(err));
1830 hr = AUDCLNT_E_DEVICE_INVALIDATED;
1831 goto exit;
1834 if(max_channels > 6)
1835 fmt->Format.nChannels = 2;
1836 else
1837 fmt->Format.nChannels = max_channels;
1839 if(fmt->Format.nChannels > 1 && (fmt->Format.nChannels & 0x1)){
1840 /* For most hardware on Windows, users must choose a configuration with an even
1841 * number of channels (stereo, quad, 5.1, 7.1). Users can then disable
1842 * channels, but those channels are still reported to applications from
1843 * GetMixFormat! Some applications behave badly if given an odd number of
1844 * channels (e.g. 2.1). */
1846 if(fmt->Format.nChannels < max_channels)
1847 fmt->Format.nChannels += 1;
1848 else
1849 /* We could "fake" more channels and downmix the emulated channels,
1850 * but at that point you really ought to tweak your ALSA setup or
1851 * just use PulseAudio. */
1852 WARN("Some Windows applications behave badly with an odd number of channels (%u)!\n", fmt->Format.nChannels);
1855 fmt->dwChannelMask = get_channel_mask(fmt->Format.nChannels);
1857 if((err = snd_pcm_hw_params_get_rate_max(This->hw_params, &max_rate,
1858 NULL)) < 0){
1859 WARN("Unable to get max rate: %d (%s)\n", err, snd_strerror(err));
1860 hr = AUDCLNT_E_DEVICE_INVALIDATED;
1861 goto exit;
1864 if(max_rate >= 48000)
1865 fmt->Format.nSamplesPerSec = 48000;
1866 else if(max_rate >= 44100)
1867 fmt->Format.nSamplesPerSec = 44100;
1868 else if(max_rate >= 22050)
1869 fmt->Format.nSamplesPerSec = 22050;
1870 else if(max_rate >= 11025)
1871 fmt->Format.nSamplesPerSec = 11025;
1872 else if(max_rate >= 8000)
1873 fmt->Format.nSamplesPerSec = 8000;
1874 else{
1875 ERR("Unknown max rate: %u\n", max_rate);
1876 hr = AUDCLNT_E_DEVICE_INVALIDATED;
1877 goto exit;
1880 fmt->Format.nBlockAlign = (fmt->Format.wBitsPerSample *
1881 fmt->Format.nChannels) / 8;
1882 fmt->Format.nAvgBytesPerSec = fmt->Format.nSamplesPerSec *
1883 fmt->Format.nBlockAlign;
1885 fmt->Samples.wValidBitsPerSample = fmt->Format.wBitsPerSample;
1886 fmt->Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
1888 dump_fmt((WAVEFORMATEX*)fmt);
1889 *pwfx = (WAVEFORMATEX*)fmt;
1891 exit:
1892 LeaveCriticalSection(&This->lock);
1893 if(FAILED(hr))
1894 CoTaskMemFree(fmt);
1895 HeapFree(GetProcessHeap(), 0, formats);
1897 return hr;
1900 static HRESULT WINAPI AudioClient_GetDevicePeriod(IAudioClient3 *iface,
1901 REFERENCE_TIME *defperiod, REFERENCE_TIME *minperiod)
1903 ACImpl *This = impl_from_IAudioClient3(iface);
1905 TRACE("(%p)->(%p, %p)\n", This, defperiod, minperiod);
1907 if(!defperiod && !minperiod)
1908 return E_POINTER;
1910 if(defperiod)
1911 *defperiod = DefaultPeriod;
1912 if(minperiod)
1913 *minperiod = DefaultPeriod;
1915 return S_OK;
1918 static BYTE *remap_channels(ACImpl *This, BYTE *buf, snd_pcm_uframes_t frames)
1920 snd_pcm_uframes_t i;
1921 UINT c;
1922 UINT bytes_per_sample = This->fmt->wBitsPerSample / 8;
1924 if(!This->need_remapping)
1925 return buf;
1927 if(!This->remapping_buf){
1928 This->remapping_buf = HeapAlloc(GetProcessHeap(), 0,
1929 bytes_per_sample * This->alsa_channels * frames);
1930 This->remapping_buf_frames = frames;
1931 }else if(This->remapping_buf_frames < frames){
1932 This->remapping_buf = HeapReAlloc(GetProcessHeap(), 0, This->remapping_buf,
1933 bytes_per_sample * This->alsa_channels * frames);
1934 This->remapping_buf_frames = frames;
1937 snd_pcm_format_set_silence(This->alsa_format, This->remapping_buf,
1938 frames * This->alsa_channels);
1940 switch(This->fmt->wBitsPerSample){
1941 case 8: {
1942 UINT8 *tgt_buf, *src_buf;
1943 tgt_buf = This->remapping_buf;
1944 src_buf = buf;
1945 for(i = 0; i < frames; ++i){
1946 for(c = 0; c < This->fmt->nChannels; ++c)
1947 tgt_buf[This->alsa_channel_map[c]] = src_buf[c];
1948 tgt_buf += This->alsa_channels;
1949 src_buf += This->fmt->nChannels;
1951 break;
1953 case 16: {
1954 UINT16 *tgt_buf, *src_buf;
1955 tgt_buf = (UINT16*)This->remapping_buf;
1956 src_buf = (UINT16*)buf;
1957 for(i = 0; i < frames; ++i){
1958 for(c = 0; c < This->fmt->nChannels; ++c)
1959 tgt_buf[This->alsa_channel_map[c]] = src_buf[c];
1960 tgt_buf += This->alsa_channels;
1961 src_buf += This->fmt->nChannels;
1964 break;
1965 case 32: {
1966 UINT32 *tgt_buf, *src_buf;
1967 tgt_buf = (UINT32*)This->remapping_buf;
1968 src_buf = (UINT32*)buf;
1969 for(i = 0; i < frames; ++i){
1970 for(c = 0; c < This->fmt->nChannels; ++c)
1971 tgt_buf[This->alsa_channel_map[c]] = src_buf[c];
1972 tgt_buf += This->alsa_channels;
1973 src_buf += This->fmt->nChannels;
1976 break;
1977 default: {
1978 BYTE *tgt_buf, *src_buf;
1979 tgt_buf = This->remapping_buf;
1980 src_buf = buf;
1981 for(i = 0; i < frames; ++i){
1982 for(c = 0; c < This->fmt->nChannels; ++c)
1983 memcpy(&tgt_buf[This->alsa_channel_map[c] * bytes_per_sample],
1984 &src_buf[c * bytes_per_sample], bytes_per_sample);
1985 tgt_buf += This->alsa_channels * bytes_per_sample;
1986 src_buf += This->fmt->nChannels * bytes_per_sample;
1989 break;
1992 return This->remapping_buf;
1995 static void adjust_buffer_volume(const ACImpl *This, BYTE *buf, snd_pcm_uframes_t frames, BOOL mute)
1997 float vol[ARRAY_SIZE(This->alsa_channel_map)];
1998 BOOL adjust = FALSE;
1999 UINT32 i, channels;
2000 BYTE *end;
2002 if (This->vol_adjusted_frames >= frames)
2003 return;
2004 channels = This->fmt->nChannels;
2006 if (mute)
2008 int err = snd_pcm_format_set_silence(This->alsa_format, buf, frames * channels);
2009 if (err < 0)
2010 WARN("Setting buffer to silence failed: %d (%s)\n", err, snd_strerror(err));
2011 return;
2014 /* Adjust the buffer based on the volume for each channel */
2015 for (i = 0; i < channels; i++)
2016 vol[i] = This->vols[i] * This->session->master_vol;
2017 for (i = 0; i < min(channels, This->session->channel_count); i++)
2019 vol[i] *= This->session->channel_vols[i];
2020 adjust |= vol[i] != 1.0f;
2022 while (i < channels) adjust |= vol[i++] != 1.0f;
2023 if (!adjust) return;
2025 /* Skip the frames we've already adjusted before */
2026 end = buf + frames * This->fmt->nBlockAlign;
2027 buf += This->vol_adjusted_frames * This->fmt->nBlockAlign;
2029 switch (This->alsa_format)
2031 #ifndef WORDS_BIGENDIAN
2032 #define PROCESS_BUFFER(type) do \
2034 type *p = (type*)buf; \
2035 do \
2037 for (i = 0; i < channels; i++) \
2038 p[i] = p[i] * vol[i]; \
2039 p += i; \
2040 } while ((BYTE*)p != end); \
2041 } while (0)
2042 case SND_PCM_FORMAT_S16_LE:
2043 PROCESS_BUFFER(INT16);
2044 break;
2045 case SND_PCM_FORMAT_S32_LE:
2046 PROCESS_BUFFER(INT32);
2047 break;
2048 case SND_PCM_FORMAT_FLOAT_LE:
2049 PROCESS_BUFFER(float);
2050 break;
2051 case SND_PCM_FORMAT_FLOAT64_LE:
2052 PROCESS_BUFFER(double);
2053 break;
2054 #undef PROCESS_BUFFER
2055 case SND_PCM_FORMAT_S20_3LE:
2056 case SND_PCM_FORMAT_S24_3LE:
2058 /* Do it 12 bytes at a time until it is no longer possible */
2059 UINT32 *q = (UINT32*)buf, mask = ~0xff;
2060 BYTE *p;
2062 /* After we adjust the volume, we need to mask out low bits */
2063 if (This->alsa_format == SND_PCM_FORMAT_S20_3LE)
2064 mask = ~0x0fff;
2066 i = 0;
2067 while (end - (BYTE*)q >= 12)
2069 UINT32 v[4], k;
2070 v[0] = q[0] << 8;
2071 v[1] = q[1] << 16 | (q[0] >> 16 & ~0xff);
2072 v[2] = q[2] << 24 | (q[1] >> 8 & ~0xff);
2073 v[3] = q[2] & ~0xff;
2074 for (k = 0; k < 4; k++)
2076 v[k] = (INT32)((INT32)v[k] * vol[i]);
2077 v[k] &= mask;
2078 if (++i == channels) i = 0;
2080 *q++ = v[0] >> 8 | v[1] << 16;
2081 *q++ = v[1] >> 16 | v[2] << 8;
2082 *q++ = v[2] >> 24 | v[3];
2084 p = (BYTE*)q;
2085 while (p != end)
2087 UINT32 v = (INT32)((INT32)(p[0] << 8 | p[1] << 16 | p[2] << 24) * vol[i]);
2088 v &= mask;
2089 *p++ = v >> 8 & 0xff;
2090 *p++ = v >> 16 & 0xff;
2091 *p++ = v >> 24;
2092 if (++i == channels) i = 0;
2094 break;
2096 #endif
2097 case SND_PCM_FORMAT_U8:
2099 UINT8 *p = (UINT8*)buf;
2102 for (i = 0; i < channels; i++)
2103 p[i] = (int)((p[i] - 128) * vol[i]) + 128;
2104 p += i;
2105 } while ((BYTE*)p != end);
2106 break;
2108 default:
2109 TRACE("Unhandled format %i, not adjusting volume.\n", This->alsa_format);
2110 break;
2114 static snd_pcm_sframes_t alsa_write_best_effort(ACImpl *This, BYTE *buf,
2115 snd_pcm_uframes_t frames, BOOL mute)
2117 snd_pcm_sframes_t written;
2119 adjust_buffer_volume(This, buf, frames, mute);
2121 /* Mark the frames we've already adjusted */
2122 if (This->vol_adjusted_frames < frames)
2123 This->vol_adjusted_frames = frames;
2125 buf = remap_channels(This, buf, frames);
2127 written = snd_pcm_writei(This->pcm_handle, buf, frames);
2128 if(written < 0){
2129 int ret;
2131 if(written == -EAGAIN)
2132 /* buffer full */
2133 return 0;
2135 WARN("writei failed, recovering: %ld (%s)\n", written,
2136 snd_strerror(written));
2138 ret = snd_pcm_recover(This->pcm_handle, written, 0);
2139 if(ret < 0){
2140 WARN("Could not recover: %d (%s)\n", ret, snd_strerror(ret));
2141 return ret;
2144 written = snd_pcm_writei(This->pcm_handle, buf, frames);
2147 if (written > 0)
2148 This->vol_adjusted_frames -= written;
2149 return written;
2152 static snd_pcm_sframes_t alsa_write_buffer_wrap(ACImpl *This, BYTE *buf,
2153 snd_pcm_uframes_t buflen, snd_pcm_uframes_t offs,
2154 snd_pcm_uframes_t to_write)
2156 snd_pcm_sframes_t ret = 0;
2158 while(to_write){
2159 snd_pcm_uframes_t chunk;
2160 snd_pcm_sframes_t tmp;
2162 if(offs + to_write > buflen)
2163 chunk = buflen - offs;
2164 else
2165 chunk = to_write;
2167 tmp = alsa_write_best_effort(This, buf + offs * This->fmt->nBlockAlign, chunk, This->session->mute);
2168 if(tmp < 0)
2169 return ret;
2170 if(!tmp)
2171 break;
2173 ret += tmp;
2174 to_write -= tmp;
2175 offs += tmp;
2176 offs %= buflen;
2179 return ret;
2182 static UINT buf_ptr_diff(UINT left, UINT right, UINT bufsize)
2184 if(left <= right)
2185 return right - left;
2186 return bufsize - (left - right);
2189 static UINT data_not_in_alsa(ACImpl *This)
2191 UINT32 diff;
2193 diff = buf_ptr_diff(This->lcl_offs_frames, This->wri_offs_frames, This->bufsize_frames);
2194 if(diff)
2195 return diff;
2197 return This->held_frames - This->data_in_alsa_frames;
2199 /* Here's the buffer setup:
2201 * vvvvvvvv sent to HW already
2202 * vvvvvvvv in ALSA buffer but rewindable
2203 * [dddddddddddddddd] ALSA buffer
2204 * [dddddddddddddddd--------] mmdevapi buffer
2205 * ^^^^^^^^ data_in_alsa_frames
2206 * ^^^^^^^^^^^^^^^^ held_frames
2207 * ^ lcl_offs_frames
2208 * ^ wri_offs_frames
2210 * GetCurrentPadding is held_frames
2212 * During period callback, we decrement held_frames, fill ALSA buffer, and move
2213 * lcl_offs forward
2215 * During Stop, we rewind the ALSA buffer
2217 static void alsa_write_data(ACImpl *This)
2219 snd_pcm_sframes_t written;
2220 snd_pcm_uframes_t avail, max_copy_frames, data_frames_played;
2221 int err;
2223 /* this call seems to be required to get an accurate snd_pcm_state() */
2224 avail = snd_pcm_avail_update(This->pcm_handle);
2226 if(snd_pcm_state(This->pcm_handle) == SND_PCM_STATE_XRUN){
2227 TRACE("XRun state, recovering\n");
2229 avail = This->alsa_bufsize_frames;
2231 if((err = snd_pcm_recover(This->pcm_handle, -EPIPE, 1)) < 0)
2232 WARN("snd_pcm_recover failed: %d (%s)\n", err, snd_strerror(err));
2234 if((err = snd_pcm_reset(This->pcm_handle)) < 0)
2235 WARN("snd_pcm_reset failed: %d (%s)\n", err, snd_strerror(err));
2237 if((err = snd_pcm_prepare(This->pcm_handle)) < 0)
2238 WARN("snd_pcm_prepare failed: %d (%s)\n", err, snd_strerror(err));
2241 TRACE("avail: %ld\n", avail);
2243 /* Add a lead-in when starting with too few frames to ensure
2244 * continuous rendering. Additional benefit: Force ALSA to start. */
2245 if(This->data_in_alsa_frames == 0 && This->held_frames < This->alsa_period_frames)
2247 alsa_write_best_effort(This, This->silence_buf, This->alsa_period_frames - This->held_frames, FALSE);
2248 This->vol_adjusted_frames = 0;
2251 if(This->started)
2252 max_copy_frames = data_not_in_alsa(This);
2253 else
2254 max_copy_frames = 0;
2256 data_frames_played = min(This->data_in_alsa_frames, avail);
2257 This->data_in_alsa_frames -= data_frames_played;
2259 if(This->held_frames > data_frames_played){
2260 if(This->started)
2261 This->held_frames -= data_frames_played;
2262 }else
2263 This->held_frames = 0;
2265 while(avail && max_copy_frames){
2266 snd_pcm_uframes_t to_write;
2268 to_write = min(avail, max_copy_frames);
2270 written = alsa_write_buffer_wrap(This, This->local_buffer,
2271 This->bufsize_frames, This->lcl_offs_frames, to_write);
2272 if(written <= 0)
2273 break;
2275 avail -= written;
2276 This->lcl_offs_frames += written;
2277 This->lcl_offs_frames %= This->bufsize_frames;
2278 This->data_in_alsa_frames += written;
2279 max_copy_frames -= written;
2282 if(This->event)
2283 SetEvent(This->event);
2286 static void alsa_read_data(ACImpl *This)
2288 snd_pcm_sframes_t nread;
2289 UINT32 pos = This->wri_offs_frames, limit = This->held_frames;
2291 if(!This->started)
2292 goto exit;
2294 /* FIXME: Detect overrun and signal DATA_DISCONTINUITY
2295 * How to count overrun frames and report them as position increase? */
2296 limit = This->bufsize_frames - max(limit, pos);
2298 nread = snd_pcm_readi(This->pcm_handle,
2299 This->local_buffer + pos * This->fmt->nBlockAlign, limit);
2300 TRACE("read %ld from %u limit %u\n", nread, pos, limit);
2301 if(nread < 0){
2302 int ret;
2304 if(nread == -EAGAIN) /* no data yet */
2305 return;
2307 WARN("read failed, recovering: %ld (%s)\n", nread, snd_strerror(nread));
2309 ret = snd_pcm_recover(This->pcm_handle, nread, 0);
2310 if(ret < 0){
2311 WARN("Recover failed: %d (%s)\n", ret, snd_strerror(ret));
2312 return;
2315 nread = snd_pcm_readi(This->pcm_handle,
2316 This->local_buffer + pos * This->fmt->nBlockAlign, limit);
2317 if(nread < 0){
2318 WARN("read failed: %ld (%s)\n", nread, snd_strerror(nread));
2319 return;
2323 if(This->session->mute){
2324 int err;
2325 if((err = snd_pcm_format_set_silence(This->alsa_format,
2326 This->local_buffer + pos * This->fmt->nBlockAlign,
2327 nread)) < 0)
2328 WARN("Setting buffer to silence failed: %d (%s)\n", err,
2329 snd_strerror(err));
2332 This->wri_offs_frames += nread;
2333 This->wri_offs_frames %= This->bufsize_frames;
2334 This->held_frames += nread;
2336 exit:
2337 if(This->event)
2338 SetEvent(This->event);
2341 static void CALLBACK alsa_push_buffer_data(void *user, BOOLEAN timer)
2343 ACImpl *This = user;
2345 EnterCriticalSection(&This->lock);
2347 QueryPerformanceCounter(&This->last_period_time);
2349 if(This->dataflow == eRender)
2350 alsa_write_data(This);
2351 else if(This->dataflow == eCapture)
2352 alsa_read_data(This);
2354 LeaveCriticalSection(&This->lock);
2357 static snd_pcm_uframes_t interp_elapsed_frames(ACImpl *This)
2359 LARGE_INTEGER time_freq, current_time, time_diff;
2360 QueryPerformanceFrequency(&time_freq);
2361 QueryPerformanceCounter(&current_time);
2362 time_diff.QuadPart = current_time.QuadPart - This->last_period_time.QuadPart;
2363 return MulDiv(time_diff.QuadPart, This->fmt->nSamplesPerSec, time_freq.QuadPart);
2366 static int alsa_rewind_best_effort(ACImpl *This)
2368 snd_pcm_uframes_t len, leave;
2370 /* we can't use snd_pcm_rewindable, some PCM devices crash. so follow
2371 * PulseAudio's example and rewind as much data as we believe is in the
2372 * buffer, minus 1.33ms for safety. */
2374 /* amount of data to leave in ALSA buffer */
2375 leave = interp_elapsed_frames(This) + This->safe_rewind_frames;
2377 if(This->held_frames < leave)
2378 This->held_frames = 0;
2379 else
2380 This->held_frames -= leave;
2382 if(This->data_in_alsa_frames < leave)
2383 len = 0;
2384 else
2385 len = This->data_in_alsa_frames - leave;
2387 TRACE("rewinding %lu frames, now held %u\n", len, This->held_frames);
2389 if(len)
2390 /* snd_pcm_rewind return value is often broken, assume it succeeded */
2391 snd_pcm_rewind(This->pcm_handle, len);
2393 This->data_in_alsa_frames = 0;
2395 return len;
2398 static HRESULT WINAPI AudioClient_Start(IAudioClient3 *iface)
2400 ACImpl *This = impl_from_IAudioClient3(iface);
2402 TRACE("(%p)\n", This);
2404 EnterCriticalSection(&This->lock);
2406 if(!This->initted){
2407 LeaveCriticalSection(&This->lock);
2408 return AUDCLNT_E_NOT_INITIALIZED;
2411 if((This->flags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK) && !This->event){
2412 LeaveCriticalSection(&This->lock);
2413 return AUDCLNT_E_EVENTHANDLE_NOT_SET;
2416 if(This->started){
2417 LeaveCriticalSection(&This->lock);
2418 return AUDCLNT_E_NOT_STOPPED;
2421 if(This->dataflow == eCapture){
2422 /* dump any data that might be leftover in the ALSA capture buffer */
2423 snd_pcm_readi(This->pcm_handle, This->local_buffer,
2424 This->bufsize_frames);
2425 }else{
2426 snd_pcm_sframes_t avail, written;
2427 snd_pcm_uframes_t offs;
2429 avail = snd_pcm_avail_update(This->pcm_handle);
2430 avail = min(avail, This->held_frames);
2432 if(This->wri_offs_frames < This->held_frames)
2433 offs = This->bufsize_frames - This->held_frames + This->wri_offs_frames;
2434 else
2435 offs = This->wri_offs_frames - This->held_frames;
2437 /* fill it with data */
2438 written = alsa_write_buffer_wrap(This, This->local_buffer,
2439 This->bufsize_frames, offs, avail);
2441 if(written > 0){
2442 This->lcl_offs_frames = (offs + written) % This->bufsize_frames;
2443 This->data_in_alsa_frames = written;
2444 }else{
2445 This->lcl_offs_frames = offs;
2446 This->data_in_alsa_frames = 0;
2450 if(!This->timer){
2451 if(!CreateTimerQueueTimer(&This->timer, g_timer_q, alsa_push_buffer_data,
2452 This, 0, This->mmdev_period_rt / 10000, WT_EXECUTEINTIMERTHREAD)){
2453 LeaveCriticalSection(&This->lock);
2454 WARN("Unable to create timer: %u\n", GetLastError());
2455 return E_OUTOFMEMORY;
2459 This->started = TRUE;
2461 LeaveCriticalSection(&This->lock);
2463 return S_OK;
2466 static HRESULT WINAPI AudioClient_Stop(IAudioClient3 *iface)
2468 ACImpl *This = impl_from_IAudioClient3(iface);
2470 TRACE("(%p)\n", This);
2472 EnterCriticalSection(&This->lock);
2474 if(!This->initted){
2475 LeaveCriticalSection(&This->lock);
2476 return AUDCLNT_E_NOT_INITIALIZED;
2479 if(!This->started){
2480 LeaveCriticalSection(&This->lock);
2481 return S_FALSE;
2484 if(This->dataflow == eRender)
2485 alsa_rewind_best_effort(This);
2487 This->started = FALSE;
2489 LeaveCriticalSection(&This->lock);
2491 return S_OK;
2494 static HRESULT WINAPI AudioClient_Reset(IAudioClient3 *iface)
2496 ACImpl *This = impl_from_IAudioClient3(iface);
2498 TRACE("(%p)\n", This);
2500 EnterCriticalSection(&This->lock);
2502 if(!This->initted){
2503 LeaveCriticalSection(&This->lock);
2504 return AUDCLNT_E_NOT_INITIALIZED;
2507 if(This->started){
2508 LeaveCriticalSection(&This->lock);
2509 return AUDCLNT_E_NOT_STOPPED;
2512 if(This->getbuf_last){
2513 LeaveCriticalSection(&This->lock);
2514 return AUDCLNT_E_BUFFER_OPERATION_PENDING;
2517 if(snd_pcm_drop(This->pcm_handle) < 0)
2518 WARN("snd_pcm_drop failed\n");
2520 if(snd_pcm_reset(This->pcm_handle) < 0)
2521 WARN("snd_pcm_reset failed\n");
2523 if(snd_pcm_prepare(This->pcm_handle) < 0)
2524 WARN("snd_pcm_prepare failed\n");
2526 if(This->dataflow == eRender){
2527 This->written_frames = 0;
2528 This->last_pos_frames = 0;
2529 }else{
2530 This->written_frames += This->held_frames;
2532 This->held_frames = 0;
2533 This->lcl_offs_frames = 0;
2534 This->wri_offs_frames = 0;
2536 LeaveCriticalSection(&This->lock);
2538 return S_OK;
2541 static HRESULT WINAPI AudioClient_SetEventHandle(IAudioClient3 *iface,
2542 HANDLE event)
2544 ACImpl *This = impl_from_IAudioClient3(iface);
2546 TRACE("(%p)->(%p)\n", This, event);
2548 if(!event)
2549 return E_INVALIDARG;
2551 EnterCriticalSection(&This->lock);
2553 if(!This->initted){
2554 LeaveCriticalSection(&This->lock);
2555 return AUDCLNT_E_NOT_INITIALIZED;
2558 if(!(This->flags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK)){
2559 LeaveCriticalSection(&This->lock);
2560 return AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED;
2563 if (This->event){
2564 LeaveCriticalSection(&This->lock);
2565 FIXME("called twice\n");
2566 return HRESULT_FROM_WIN32(ERROR_INVALID_NAME);
2569 This->event = event;
2571 LeaveCriticalSection(&This->lock);
2573 return S_OK;
2576 static HRESULT WINAPI AudioClient_GetService(IAudioClient3 *iface, REFIID riid,
2577 void **ppv)
2579 ACImpl *This = impl_from_IAudioClient3(iface);
2581 TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(riid), ppv);
2583 if(!ppv)
2584 return E_POINTER;
2585 *ppv = NULL;
2587 EnterCriticalSection(&This->lock);
2589 if(!This->initted){
2590 LeaveCriticalSection(&This->lock);
2591 return AUDCLNT_E_NOT_INITIALIZED;
2594 if(IsEqualIID(riid, &IID_IAudioRenderClient)){
2595 if(This->dataflow != eRender){
2596 LeaveCriticalSection(&This->lock);
2597 return AUDCLNT_E_WRONG_ENDPOINT_TYPE;
2599 IAudioRenderClient_AddRef(&This->IAudioRenderClient_iface);
2600 *ppv = &This->IAudioRenderClient_iface;
2601 }else if(IsEqualIID(riid, &IID_IAudioCaptureClient)){
2602 if(This->dataflow != eCapture){
2603 LeaveCriticalSection(&This->lock);
2604 return AUDCLNT_E_WRONG_ENDPOINT_TYPE;
2606 IAudioCaptureClient_AddRef(&This->IAudioCaptureClient_iface);
2607 *ppv = &This->IAudioCaptureClient_iface;
2608 }else if(IsEqualIID(riid, &IID_IAudioClock)){
2609 IAudioClock_AddRef(&This->IAudioClock_iface);
2610 *ppv = &This->IAudioClock_iface;
2611 }else if(IsEqualIID(riid, &IID_IAudioStreamVolume)){
2612 IAudioStreamVolume_AddRef(&This->IAudioStreamVolume_iface);
2613 *ppv = &This->IAudioStreamVolume_iface;
2614 }else if(IsEqualIID(riid, &IID_IAudioSessionControl)){
2615 if(!This->session_wrapper){
2616 This->session_wrapper = AudioSessionWrapper_Create(This);
2617 if(!This->session_wrapper){
2618 LeaveCriticalSection(&This->lock);
2619 return E_OUTOFMEMORY;
2621 }else
2622 IAudioSessionControl2_AddRef(&This->session_wrapper->IAudioSessionControl2_iface);
2624 *ppv = &This->session_wrapper->IAudioSessionControl2_iface;
2625 }else if(IsEqualIID(riid, &IID_IChannelAudioVolume)){
2626 if(!This->session_wrapper){
2627 This->session_wrapper = AudioSessionWrapper_Create(This);
2628 if(!This->session_wrapper){
2629 LeaveCriticalSection(&This->lock);
2630 return E_OUTOFMEMORY;
2632 }else
2633 IChannelAudioVolume_AddRef(&This->session_wrapper->IChannelAudioVolume_iface);
2635 *ppv = &This->session_wrapper->IChannelAudioVolume_iface;
2636 }else if(IsEqualIID(riid, &IID_ISimpleAudioVolume)){
2637 if(!This->session_wrapper){
2638 This->session_wrapper = AudioSessionWrapper_Create(This);
2639 if(!This->session_wrapper){
2640 LeaveCriticalSection(&This->lock);
2641 return E_OUTOFMEMORY;
2643 }else
2644 ISimpleAudioVolume_AddRef(&This->session_wrapper->ISimpleAudioVolume_iface);
2646 *ppv = &This->session_wrapper->ISimpleAudioVolume_iface;
2649 if(*ppv){
2650 LeaveCriticalSection(&This->lock);
2651 return S_OK;
2654 LeaveCriticalSection(&This->lock);
2656 FIXME("stub %s\n", debugstr_guid(riid));
2657 return E_NOINTERFACE;
2660 static HRESULT WINAPI AudioClient_IsOffloadCapable(IAudioClient3 *iface,
2661 AUDIO_STREAM_CATEGORY category, BOOL *offload_capable)
2663 ACImpl *This = impl_from_IAudioClient3(iface);
2665 TRACE("(%p)->(0x%x, %p)\n", This, category, offload_capable);
2667 if(!offload_capable)
2668 return E_INVALIDARG;
2670 *offload_capable = FALSE;
2672 return S_OK;
2675 static HRESULT WINAPI AudioClient_SetClientProperties(IAudioClient3 *iface,
2676 const AudioClientProperties *prop)
2678 ACImpl *This = impl_from_IAudioClient3(iface);
2679 const Win8AudioClientProperties *legacy_prop = (const Win8AudioClientProperties *)prop;
2681 TRACE("(%p)->(%p)\n", This, prop);
2683 if(!legacy_prop)
2684 return E_POINTER;
2686 if(legacy_prop->cbSize == sizeof(AudioClientProperties)){
2687 TRACE("{ bIsOffload: %u, eCategory: 0x%x, Options: 0x%x }\n",
2688 legacy_prop->bIsOffload,
2689 legacy_prop->eCategory,
2690 prop->Options);
2691 }else if(legacy_prop->cbSize == sizeof(Win8AudioClientProperties)){
2692 TRACE("{ bIsOffload: %u, eCategory: 0x%x }\n",
2693 legacy_prop->bIsOffload,
2694 legacy_prop->eCategory);
2695 }else{
2696 WARN("Unsupported Size = %d\n", legacy_prop->cbSize);
2697 return E_INVALIDARG;
2701 if(legacy_prop->bIsOffload)
2702 return AUDCLNT_E_ENDPOINT_OFFLOAD_NOT_CAPABLE;
2704 return S_OK;
2707 static HRESULT WINAPI AudioClient_GetBufferSizeLimits(IAudioClient3 *iface,
2708 const WAVEFORMATEX *format, BOOL event_driven, REFERENCE_TIME *min_duration,
2709 REFERENCE_TIME *max_duration)
2711 ACImpl *This = impl_from_IAudioClient3(iface);
2713 FIXME("(%p)->(%p, %u, %p, %p)\n", This, format, event_driven, min_duration, max_duration);
2715 return E_NOTIMPL;
2718 static HRESULT WINAPI AudioClient_GetSharedModeEnginePeriod(IAudioClient3 *iface,
2719 const WAVEFORMATEX *format, UINT32 *default_period_frames, UINT32 *unit_period_frames,
2720 UINT32 *min_period_frames, UINT32 *max_period_frames)
2722 ACImpl *This = impl_from_IAudioClient3(iface);
2724 FIXME("(%p)->(%p, %p, %p, %p, %p)\n", This, format, default_period_frames, unit_period_frames,
2725 min_period_frames, max_period_frames);
2727 return E_NOTIMPL;
2730 static HRESULT WINAPI AudioClient_GetCurrentSharedModeEnginePeriod(IAudioClient3 *iface,
2731 WAVEFORMATEX **cur_format, UINT32 *cur_period_frames)
2733 ACImpl *This = impl_from_IAudioClient3(iface);
2735 FIXME("(%p)->(%p, %p)\n", This, cur_format, cur_period_frames);
2737 return E_NOTIMPL;
2740 static HRESULT WINAPI AudioClient_InitializeSharedAudioStream(IAudioClient3 *iface,
2741 DWORD flags, UINT32 period_frames, const WAVEFORMATEX *format,
2742 const GUID *session_guid)
2744 ACImpl *This = impl_from_IAudioClient3(iface);
2746 FIXME("(%p)->(0x%x, %u, %p, %s)\n", This, flags, period_frames, format, debugstr_guid(session_guid));
2748 return E_NOTIMPL;
2751 static const IAudioClient3Vtbl AudioClient3_Vtbl =
2753 AudioClient_QueryInterface,
2754 AudioClient_AddRef,
2755 AudioClient_Release,
2756 AudioClient_Initialize,
2757 AudioClient_GetBufferSize,
2758 AudioClient_GetStreamLatency,
2759 AudioClient_GetCurrentPadding,
2760 AudioClient_IsFormatSupported,
2761 AudioClient_GetMixFormat,
2762 AudioClient_GetDevicePeriod,
2763 AudioClient_Start,
2764 AudioClient_Stop,
2765 AudioClient_Reset,
2766 AudioClient_SetEventHandle,
2767 AudioClient_GetService,
2768 AudioClient_IsOffloadCapable,
2769 AudioClient_SetClientProperties,
2770 AudioClient_GetBufferSizeLimits,
2771 AudioClient_GetSharedModeEnginePeriod,
2772 AudioClient_GetCurrentSharedModeEnginePeriod,
2773 AudioClient_InitializeSharedAudioStream,
2776 static HRESULT WINAPI AudioRenderClient_QueryInterface(
2777 IAudioRenderClient *iface, REFIID riid, void **ppv)
2779 ACImpl *This = impl_from_IAudioRenderClient(iface);
2780 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2782 if(!ppv)
2783 return E_POINTER;
2784 *ppv = NULL;
2786 if(IsEqualIID(riid, &IID_IUnknown) ||
2787 IsEqualIID(riid, &IID_IAudioRenderClient))
2788 *ppv = iface;
2789 else if(IsEqualIID(riid, &IID_IMarshal))
2790 return IUnknown_QueryInterface(This->pUnkFTMarshal, riid, ppv);
2792 if(*ppv){
2793 IUnknown_AddRef((IUnknown*)*ppv);
2794 return S_OK;
2797 WARN("Unknown interface %s\n", debugstr_guid(riid));
2798 return E_NOINTERFACE;
2801 static ULONG WINAPI AudioRenderClient_AddRef(IAudioRenderClient *iface)
2803 ACImpl *This = impl_from_IAudioRenderClient(iface);
2804 return AudioClient_AddRef(&This->IAudioClient3_iface);
2807 static ULONG WINAPI AudioRenderClient_Release(IAudioRenderClient *iface)
2809 ACImpl *This = impl_from_IAudioRenderClient(iface);
2810 return AudioClient_Release(&This->IAudioClient3_iface);
2813 static HRESULT WINAPI AudioRenderClient_GetBuffer(IAudioRenderClient *iface,
2814 UINT32 frames, BYTE **data)
2816 ACImpl *This = impl_from_IAudioRenderClient(iface);
2817 UINT32 write_pos;
2819 TRACE("(%p)->(%u, %p)\n", This, frames, data);
2821 if(!data)
2822 return E_POINTER;
2823 *data = NULL;
2825 EnterCriticalSection(&This->lock);
2827 if(This->getbuf_last){
2828 LeaveCriticalSection(&This->lock);
2829 return AUDCLNT_E_OUT_OF_ORDER;
2832 if(!frames){
2833 LeaveCriticalSection(&This->lock);
2834 return S_OK;
2837 /* held_frames == GetCurrentPadding_nolock(); */
2838 if(This->held_frames + frames > This->bufsize_frames){
2839 LeaveCriticalSection(&This->lock);
2840 return AUDCLNT_E_BUFFER_TOO_LARGE;
2843 write_pos = This->wri_offs_frames;
2844 if(write_pos + frames > This->bufsize_frames){
2845 if(This->tmp_buffer_frames < frames){
2846 HeapFree(GetProcessHeap(), 0, This->tmp_buffer);
2847 This->tmp_buffer = HeapAlloc(GetProcessHeap(), 0,
2848 frames * This->fmt->nBlockAlign);
2849 if(!This->tmp_buffer){
2850 LeaveCriticalSection(&This->lock);
2851 return E_OUTOFMEMORY;
2853 This->tmp_buffer_frames = frames;
2855 *data = This->tmp_buffer;
2856 This->getbuf_last = -frames;
2857 }else{
2858 *data = This->local_buffer + write_pos * This->fmt->nBlockAlign;
2859 This->getbuf_last = frames;
2862 silence_buffer(This, *data, frames);
2864 LeaveCriticalSection(&This->lock);
2866 return S_OK;
2869 static void alsa_wrap_buffer(ACImpl *This, BYTE *buffer, UINT32 written_frames)
2871 snd_pcm_uframes_t write_offs_frames = This->wri_offs_frames;
2872 UINT32 write_offs_bytes = write_offs_frames * This->fmt->nBlockAlign;
2873 snd_pcm_uframes_t chunk_frames = This->bufsize_frames - write_offs_frames;
2874 UINT32 chunk_bytes = chunk_frames * This->fmt->nBlockAlign;
2875 UINT32 written_bytes = written_frames * This->fmt->nBlockAlign;
2877 if(written_bytes <= chunk_bytes){
2878 memcpy(This->local_buffer + write_offs_bytes, buffer, written_bytes);
2879 }else{
2880 memcpy(This->local_buffer + write_offs_bytes, buffer, chunk_bytes);
2881 memcpy(This->local_buffer, buffer + chunk_bytes,
2882 written_bytes - chunk_bytes);
2886 static HRESULT WINAPI AudioRenderClient_ReleaseBuffer(
2887 IAudioRenderClient *iface, UINT32 written_frames, DWORD flags)
2889 ACImpl *This = impl_from_IAudioRenderClient(iface);
2890 BYTE *buffer;
2892 TRACE("(%p)->(%u, %x)\n", This, written_frames, flags);
2894 EnterCriticalSection(&This->lock);
2896 if(!written_frames){
2897 This->getbuf_last = 0;
2898 LeaveCriticalSection(&This->lock);
2899 return S_OK;
2902 if(!This->getbuf_last){
2903 LeaveCriticalSection(&This->lock);
2904 return AUDCLNT_E_OUT_OF_ORDER;
2907 if(written_frames > (This->getbuf_last >= 0 ? This->getbuf_last : -This->getbuf_last)){
2908 LeaveCriticalSection(&This->lock);
2909 return AUDCLNT_E_INVALID_SIZE;
2912 if(This->getbuf_last >= 0)
2913 buffer = This->local_buffer + This->wri_offs_frames * This->fmt->nBlockAlign;
2914 else
2915 buffer = This->tmp_buffer;
2917 if(flags & AUDCLNT_BUFFERFLAGS_SILENT)
2918 silence_buffer(This, buffer, written_frames);
2920 if(This->getbuf_last < 0)
2921 alsa_wrap_buffer(This, buffer, written_frames);
2923 This->wri_offs_frames += written_frames;
2924 This->wri_offs_frames %= This->bufsize_frames;
2925 This->held_frames += written_frames;
2926 This->written_frames += written_frames;
2927 This->getbuf_last = 0;
2929 LeaveCriticalSection(&This->lock);
2931 return S_OK;
2934 static const IAudioRenderClientVtbl AudioRenderClient_Vtbl = {
2935 AudioRenderClient_QueryInterface,
2936 AudioRenderClient_AddRef,
2937 AudioRenderClient_Release,
2938 AudioRenderClient_GetBuffer,
2939 AudioRenderClient_ReleaseBuffer
2942 static HRESULT WINAPI AudioCaptureClient_QueryInterface(
2943 IAudioCaptureClient *iface, REFIID riid, void **ppv)
2945 ACImpl *This = impl_from_IAudioCaptureClient(iface);
2946 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2948 if(!ppv)
2949 return E_POINTER;
2950 *ppv = NULL;
2952 if(IsEqualIID(riid, &IID_IUnknown) ||
2953 IsEqualIID(riid, &IID_IAudioCaptureClient))
2954 *ppv = iface;
2955 else if(IsEqualIID(riid, &IID_IMarshal))
2956 return IUnknown_QueryInterface(This->pUnkFTMarshal, riid, ppv);
2958 if(*ppv){
2959 IUnknown_AddRef((IUnknown*)*ppv);
2960 return S_OK;
2963 WARN("Unknown interface %s\n", debugstr_guid(riid));
2964 return E_NOINTERFACE;
2967 static ULONG WINAPI AudioCaptureClient_AddRef(IAudioCaptureClient *iface)
2969 ACImpl *This = impl_from_IAudioCaptureClient(iface);
2970 return IAudioClient3_AddRef(&This->IAudioClient3_iface);
2973 static ULONG WINAPI AudioCaptureClient_Release(IAudioCaptureClient *iface)
2975 ACImpl *This = impl_from_IAudioCaptureClient(iface);
2976 return IAudioClient3_Release(&This->IAudioClient3_iface);
2979 static HRESULT WINAPI AudioCaptureClient_GetBuffer(IAudioCaptureClient *iface,
2980 BYTE **data, UINT32 *frames, DWORD *flags, UINT64 *devpos,
2981 UINT64 *qpcpos)
2983 ACImpl *This = impl_from_IAudioCaptureClient(iface);
2985 TRACE("(%p)->(%p, %p, %p, %p, %p)\n", This, data, frames, flags,
2986 devpos, qpcpos);
2988 if(!data)
2989 return E_POINTER;
2991 *data = NULL;
2993 if(!frames || !flags)
2994 return E_POINTER;
2996 EnterCriticalSection(&This->lock);
2998 if(This->getbuf_last){
2999 LeaveCriticalSection(&This->lock);
3000 return AUDCLNT_E_OUT_OF_ORDER;
3003 /* hr = GetNextPacketSize(iface, frames); */
3004 if(This->held_frames < This->mmdev_period_frames){
3005 *frames = 0;
3006 LeaveCriticalSection(&This->lock);
3007 return AUDCLNT_S_BUFFER_EMPTY;
3009 *frames = This->mmdev_period_frames;
3011 if(This->lcl_offs_frames + *frames > This->bufsize_frames){
3012 UINT32 chunk_bytes, offs_bytes, frames_bytes;
3013 if(This->tmp_buffer_frames < *frames){
3014 HeapFree(GetProcessHeap(), 0, This->tmp_buffer);
3015 This->tmp_buffer = HeapAlloc(GetProcessHeap(), 0,
3016 *frames * This->fmt->nBlockAlign);
3017 if(!This->tmp_buffer){
3018 LeaveCriticalSection(&This->lock);
3019 return E_OUTOFMEMORY;
3021 This->tmp_buffer_frames = *frames;
3024 *data = This->tmp_buffer;
3025 chunk_bytes = (This->bufsize_frames - This->lcl_offs_frames) *
3026 This->fmt->nBlockAlign;
3027 offs_bytes = This->lcl_offs_frames * This->fmt->nBlockAlign;
3028 frames_bytes = *frames * This->fmt->nBlockAlign;
3029 memcpy(This->tmp_buffer, This->local_buffer + offs_bytes, chunk_bytes);
3030 memcpy(This->tmp_buffer + chunk_bytes, This->local_buffer,
3031 frames_bytes - chunk_bytes);
3032 }else
3033 *data = This->local_buffer +
3034 This->lcl_offs_frames * This->fmt->nBlockAlign;
3036 This->getbuf_last = *frames;
3037 *flags = 0;
3039 if(devpos)
3040 *devpos = This->written_frames;
3041 if(qpcpos){ /* fixme: qpc of recording time */
3042 LARGE_INTEGER stamp, freq;
3043 QueryPerformanceCounter(&stamp);
3044 QueryPerformanceFrequency(&freq);
3045 *qpcpos = (stamp.QuadPart * (INT64)10000000) / freq.QuadPart;
3048 LeaveCriticalSection(&This->lock);
3050 return *frames ? S_OK : AUDCLNT_S_BUFFER_EMPTY;
3053 static HRESULT WINAPI AudioCaptureClient_ReleaseBuffer(
3054 IAudioCaptureClient *iface, UINT32 done)
3056 ACImpl *This = impl_from_IAudioCaptureClient(iface);
3058 TRACE("(%p)->(%u)\n", This, done);
3060 EnterCriticalSection(&This->lock);
3062 if(!done){
3063 This->getbuf_last = 0;
3064 LeaveCriticalSection(&This->lock);
3065 return S_OK;
3068 if(!This->getbuf_last){
3069 LeaveCriticalSection(&This->lock);
3070 return AUDCLNT_E_OUT_OF_ORDER;
3073 if(This->getbuf_last != done){
3074 LeaveCriticalSection(&This->lock);
3075 return AUDCLNT_E_INVALID_SIZE;
3078 This->written_frames += done;
3079 This->held_frames -= done;
3080 This->lcl_offs_frames += done;
3081 This->lcl_offs_frames %= This->bufsize_frames;
3082 This->getbuf_last = 0;
3084 LeaveCriticalSection(&This->lock);
3086 return S_OK;
3089 static HRESULT WINAPI AudioCaptureClient_GetNextPacketSize(
3090 IAudioCaptureClient *iface, UINT32 *frames)
3092 ACImpl *This = impl_from_IAudioCaptureClient(iface);
3094 TRACE("(%p)->(%p)\n", This, frames);
3096 if(!frames)
3097 return E_POINTER;
3099 EnterCriticalSection(&This->lock);
3101 *frames = This->held_frames < This->mmdev_period_frames ? 0 : This->mmdev_period_frames;
3103 LeaveCriticalSection(&This->lock);
3105 return S_OK;
3108 static const IAudioCaptureClientVtbl AudioCaptureClient_Vtbl =
3110 AudioCaptureClient_QueryInterface,
3111 AudioCaptureClient_AddRef,
3112 AudioCaptureClient_Release,
3113 AudioCaptureClient_GetBuffer,
3114 AudioCaptureClient_ReleaseBuffer,
3115 AudioCaptureClient_GetNextPacketSize
3118 static HRESULT WINAPI AudioClock_QueryInterface(IAudioClock *iface,
3119 REFIID riid, void **ppv)
3121 ACImpl *This = impl_from_IAudioClock(iface);
3123 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
3125 if(!ppv)
3126 return E_POINTER;
3127 *ppv = NULL;
3129 if(IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IAudioClock))
3130 *ppv = iface;
3131 else if(IsEqualIID(riid, &IID_IAudioClock2))
3132 *ppv = &This->IAudioClock2_iface;
3133 if(*ppv){
3134 IUnknown_AddRef((IUnknown*)*ppv);
3135 return S_OK;
3138 WARN("Unknown interface %s\n", debugstr_guid(riid));
3139 return E_NOINTERFACE;
3142 static ULONG WINAPI AudioClock_AddRef(IAudioClock *iface)
3144 ACImpl *This = impl_from_IAudioClock(iface);
3145 return IAudioClient3_AddRef(&This->IAudioClient3_iface);
3148 static ULONG WINAPI AudioClock_Release(IAudioClock *iface)
3150 ACImpl *This = impl_from_IAudioClock(iface);
3151 return IAudioClient3_Release(&This->IAudioClient3_iface);
3154 static HRESULT WINAPI AudioClock_GetFrequency(IAudioClock *iface, UINT64 *freq)
3156 ACImpl *This = impl_from_IAudioClock(iface);
3158 TRACE("(%p)->(%p)\n", This, freq);
3160 if(This->share == AUDCLNT_SHAREMODE_SHARED)
3161 *freq = (UINT64)This->fmt->nSamplesPerSec * This->fmt->nBlockAlign;
3162 else
3163 *freq = This->fmt->nSamplesPerSec;
3165 return S_OK;
3168 static HRESULT WINAPI AudioClock_GetPosition(IAudioClock *iface, UINT64 *pos,
3169 UINT64 *qpctime)
3171 ACImpl *This = impl_from_IAudioClock(iface);
3172 UINT64 position;
3173 snd_pcm_state_t alsa_state;
3175 TRACE("(%p)->(%p, %p)\n", This, pos, qpctime);
3177 if(!pos)
3178 return E_POINTER;
3180 EnterCriticalSection(&This->lock);
3182 /* avail_update required to get accurate snd_pcm_state() */
3183 snd_pcm_avail_update(This->pcm_handle);
3184 alsa_state = snd_pcm_state(This->pcm_handle);
3186 if(This->dataflow == eRender){
3187 position = This->written_frames - This->held_frames;
3189 if(This->started && alsa_state == SND_PCM_STATE_RUNNING && This->held_frames)
3190 /* we should be using snd_pcm_delay here, but it is broken
3191 * especially during ALSA device underrun. instead, let's just
3192 * interpolate between periods with the system timer. */
3193 position += interp_elapsed_frames(This);
3195 position = min(position, This->written_frames - This->held_frames + This->mmdev_period_frames);
3197 position = min(position, This->written_frames);
3198 }else
3199 position = This->written_frames + This->held_frames;
3201 /* ensure monotic growth */
3202 if(position < This->last_pos_frames)
3203 position = This->last_pos_frames;
3204 else
3205 This->last_pos_frames = position;
3207 TRACE("frames written: %u, held: %u, state: 0x%x, position: %u\n",
3208 (UINT32)(This->written_frames%1000000000), This->held_frames,
3209 alsa_state, (UINT32)(position%1000000000));
3211 LeaveCriticalSection(&This->lock);
3213 if(This->share == AUDCLNT_SHAREMODE_SHARED)
3214 *pos = position * This->fmt->nBlockAlign;
3215 else
3216 *pos = position;
3218 if(qpctime){
3219 LARGE_INTEGER stamp, freq;
3220 QueryPerformanceCounter(&stamp);
3221 QueryPerformanceFrequency(&freq);
3222 *qpctime = (stamp.QuadPart * (INT64)10000000) / freq.QuadPart;
3225 return S_OK;
3228 static HRESULT WINAPI AudioClock_GetCharacteristics(IAudioClock *iface,
3229 DWORD *chars)
3231 ACImpl *This = impl_from_IAudioClock(iface);
3233 TRACE("(%p)->(%p)\n", This, chars);
3235 if(!chars)
3236 return E_POINTER;
3238 *chars = AUDIOCLOCK_CHARACTERISTIC_FIXED_FREQ;
3240 return S_OK;
3243 static const IAudioClockVtbl AudioClock_Vtbl =
3245 AudioClock_QueryInterface,
3246 AudioClock_AddRef,
3247 AudioClock_Release,
3248 AudioClock_GetFrequency,
3249 AudioClock_GetPosition,
3250 AudioClock_GetCharacteristics
3253 static HRESULT WINAPI AudioClock2_QueryInterface(IAudioClock2 *iface,
3254 REFIID riid, void **ppv)
3256 ACImpl *This = impl_from_IAudioClock2(iface);
3257 return IAudioClock_QueryInterface(&This->IAudioClock_iface, riid, ppv);
3260 static ULONG WINAPI AudioClock2_AddRef(IAudioClock2 *iface)
3262 ACImpl *This = impl_from_IAudioClock2(iface);
3263 return IAudioClient3_AddRef(&This->IAudioClient3_iface);
3266 static ULONG WINAPI AudioClock2_Release(IAudioClock2 *iface)
3268 ACImpl *This = impl_from_IAudioClock2(iface);
3269 return IAudioClient3_Release(&This->IAudioClient3_iface);
3272 static HRESULT WINAPI AudioClock2_GetDevicePosition(IAudioClock2 *iface,
3273 UINT64 *pos, UINT64 *qpctime)
3275 ACImpl *This = impl_from_IAudioClock2(iface);
3277 FIXME("(%p)->(%p, %p)\n", This, pos, qpctime);
3279 return E_NOTIMPL;
3282 static const IAudioClock2Vtbl AudioClock2_Vtbl =
3284 AudioClock2_QueryInterface,
3285 AudioClock2_AddRef,
3286 AudioClock2_Release,
3287 AudioClock2_GetDevicePosition
3290 static AudioSessionWrapper *AudioSessionWrapper_Create(ACImpl *client)
3292 AudioSessionWrapper *ret;
3294 ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
3295 sizeof(AudioSessionWrapper));
3296 if(!ret)
3297 return NULL;
3299 ret->IAudioSessionControl2_iface.lpVtbl = &AudioSessionControl2_Vtbl;
3300 ret->ISimpleAudioVolume_iface.lpVtbl = &SimpleAudioVolume_Vtbl;
3301 ret->IChannelAudioVolume_iface.lpVtbl = &ChannelAudioVolume_Vtbl;
3303 ret->ref = 1;
3305 ret->client = client;
3306 if(client){
3307 ret->session = client->session;
3308 AudioClient_AddRef(&client->IAudioClient3_iface);
3311 return ret;
3314 static HRESULT WINAPI AudioSessionControl_QueryInterface(
3315 IAudioSessionControl2 *iface, REFIID riid, void **ppv)
3317 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
3319 if(!ppv)
3320 return E_POINTER;
3321 *ppv = NULL;
3323 if(IsEqualIID(riid, &IID_IUnknown) ||
3324 IsEqualIID(riid, &IID_IAudioSessionControl) ||
3325 IsEqualIID(riid, &IID_IAudioSessionControl2))
3326 *ppv = iface;
3327 if(*ppv){
3328 IUnknown_AddRef((IUnknown*)*ppv);
3329 return S_OK;
3332 WARN("Unknown interface %s\n", debugstr_guid(riid));
3333 return E_NOINTERFACE;
3336 static ULONG WINAPI AudioSessionControl_AddRef(IAudioSessionControl2 *iface)
3338 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
3339 ULONG ref;
3340 ref = InterlockedIncrement(&This->ref);
3341 TRACE("(%p) Refcount now %u\n", This, ref);
3342 return ref;
3345 static ULONG WINAPI AudioSessionControl_Release(IAudioSessionControl2 *iface)
3347 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
3348 ULONG ref;
3349 ref = InterlockedDecrement(&This->ref);
3350 TRACE("(%p) Refcount now %u\n", This, ref);
3351 if(!ref){
3352 if(This->client){
3353 EnterCriticalSection(&This->client->lock);
3354 This->client->session_wrapper = NULL;
3355 LeaveCriticalSection(&This->client->lock);
3356 AudioClient_Release(&This->client->IAudioClient3_iface);
3358 HeapFree(GetProcessHeap(), 0, This);
3360 return ref;
3363 static HRESULT WINAPI AudioSessionControl_GetState(IAudioSessionControl2 *iface,
3364 AudioSessionState *state)
3366 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
3367 ACImpl *client;
3369 TRACE("(%p)->(%p)\n", This, state);
3371 if(!state)
3372 return NULL_PTR_ERR;
3374 EnterCriticalSection(&g_sessions_lock);
3376 if(list_empty(&This->session->clients)){
3377 *state = AudioSessionStateExpired;
3378 LeaveCriticalSection(&g_sessions_lock);
3379 return S_OK;
3382 LIST_FOR_EACH_ENTRY(client, &This->session->clients, ACImpl, entry){
3383 EnterCriticalSection(&client->lock);
3384 if(client->started){
3385 *state = AudioSessionStateActive;
3386 LeaveCriticalSection(&client->lock);
3387 LeaveCriticalSection(&g_sessions_lock);
3388 return S_OK;
3390 LeaveCriticalSection(&client->lock);
3393 LeaveCriticalSection(&g_sessions_lock);
3395 *state = AudioSessionStateInactive;
3397 return S_OK;
3400 static HRESULT WINAPI AudioSessionControl_GetDisplayName(
3401 IAudioSessionControl2 *iface, WCHAR **name)
3403 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
3405 FIXME("(%p)->(%p) - stub\n", This, name);
3407 return E_NOTIMPL;
3410 static HRESULT WINAPI AudioSessionControl_SetDisplayName(
3411 IAudioSessionControl2 *iface, const WCHAR *name, const GUID *session)
3413 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
3415 FIXME("(%p)->(%p, %s) - stub\n", This, name, debugstr_guid(session));
3417 return E_NOTIMPL;
3420 static HRESULT WINAPI AudioSessionControl_GetIconPath(
3421 IAudioSessionControl2 *iface, WCHAR **path)
3423 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
3425 FIXME("(%p)->(%p) - stub\n", This, path);
3427 return E_NOTIMPL;
3430 static HRESULT WINAPI AudioSessionControl_SetIconPath(
3431 IAudioSessionControl2 *iface, const WCHAR *path, const GUID *session)
3433 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
3435 FIXME("(%p)->(%p, %s) - stub\n", This, path, debugstr_guid(session));
3437 return E_NOTIMPL;
3440 static HRESULT WINAPI AudioSessionControl_GetGroupingParam(
3441 IAudioSessionControl2 *iface, GUID *group)
3443 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
3445 FIXME("(%p)->(%p) - stub\n", This, group);
3447 return E_NOTIMPL;
3450 static HRESULT WINAPI AudioSessionControl_SetGroupingParam(
3451 IAudioSessionControl2 *iface, const GUID *group, const GUID *session)
3453 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
3455 FIXME("(%p)->(%s, %s) - stub\n", This, debugstr_guid(group),
3456 debugstr_guid(session));
3458 return E_NOTIMPL;
3461 static HRESULT WINAPI AudioSessionControl_RegisterAudioSessionNotification(
3462 IAudioSessionControl2 *iface, IAudioSessionEvents *events)
3464 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
3466 FIXME("(%p)->(%p) - stub\n", This, events);
3468 return S_OK;
3471 static HRESULT WINAPI AudioSessionControl_UnregisterAudioSessionNotification(
3472 IAudioSessionControl2 *iface, IAudioSessionEvents *events)
3474 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
3476 FIXME("(%p)->(%p) - stub\n", This, events);
3478 return S_OK;
3481 static HRESULT WINAPI AudioSessionControl_GetSessionIdentifier(
3482 IAudioSessionControl2 *iface, WCHAR **id)
3484 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
3486 FIXME("(%p)->(%p) - stub\n", This, id);
3488 return E_NOTIMPL;
3491 static HRESULT WINAPI AudioSessionControl_GetSessionInstanceIdentifier(
3492 IAudioSessionControl2 *iface, WCHAR **id)
3494 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
3496 FIXME("(%p)->(%p) - stub\n", This, id);
3498 return E_NOTIMPL;
3501 static HRESULT WINAPI AudioSessionControl_GetProcessId(
3502 IAudioSessionControl2 *iface, DWORD *pid)
3504 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
3506 TRACE("(%p)->(%p)\n", This, pid);
3508 if(!pid)
3509 return E_POINTER;
3511 *pid = GetCurrentProcessId();
3513 return S_OK;
3516 static HRESULT WINAPI AudioSessionControl_IsSystemSoundsSession(
3517 IAudioSessionControl2 *iface)
3519 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
3521 TRACE("(%p)\n", This);
3523 return S_FALSE;
3526 static HRESULT WINAPI AudioSessionControl_SetDuckingPreference(
3527 IAudioSessionControl2 *iface, BOOL optout)
3529 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
3531 TRACE("(%p)->(%d)\n", This, optout);
3533 return S_OK;
3536 static const IAudioSessionControl2Vtbl AudioSessionControl2_Vtbl =
3538 AudioSessionControl_QueryInterface,
3539 AudioSessionControl_AddRef,
3540 AudioSessionControl_Release,
3541 AudioSessionControl_GetState,
3542 AudioSessionControl_GetDisplayName,
3543 AudioSessionControl_SetDisplayName,
3544 AudioSessionControl_GetIconPath,
3545 AudioSessionControl_SetIconPath,
3546 AudioSessionControl_GetGroupingParam,
3547 AudioSessionControl_SetGroupingParam,
3548 AudioSessionControl_RegisterAudioSessionNotification,
3549 AudioSessionControl_UnregisterAudioSessionNotification,
3550 AudioSessionControl_GetSessionIdentifier,
3551 AudioSessionControl_GetSessionInstanceIdentifier,
3552 AudioSessionControl_GetProcessId,
3553 AudioSessionControl_IsSystemSoundsSession,
3554 AudioSessionControl_SetDuckingPreference
3557 static HRESULT WINAPI SimpleAudioVolume_QueryInterface(
3558 ISimpleAudioVolume *iface, REFIID riid, void **ppv)
3560 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
3562 if(!ppv)
3563 return E_POINTER;
3564 *ppv = NULL;
3566 if(IsEqualIID(riid, &IID_IUnknown) ||
3567 IsEqualIID(riid, &IID_ISimpleAudioVolume))
3568 *ppv = iface;
3569 if(*ppv){
3570 IUnknown_AddRef((IUnknown*)*ppv);
3571 return S_OK;
3574 WARN("Unknown interface %s\n", debugstr_guid(riid));
3575 return E_NOINTERFACE;
3578 static ULONG WINAPI SimpleAudioVolume_AddRef(ISimpleAudioVolume *iface)
3580 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
3581 return AudioSessionControl_AddRef(&This->IAudioSessionControl2_iface);
3584 static ULONG WINAPI SimpleAudioVolume_Release(ISimpleAudioVolume *iface)
3586 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
3587 return AudioSessionControl_Release(&This->IAudioSessionControl2_iface);
3590 static HRESULT WINAPI SimpleAudioVolume_SetMasterVolume(
3591 ISimpleAudioVolume *iface, float level, const GUID *context)
3593 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
3594 AudioSession *session = This->session;
3596 TRACE("(%p)->(%f, %s)\n", session, level, wine_dbgstr_guid(context));
3598 if(level < 0.f || level > 1.f)
3599 return E_INVALIDARG;
3601 if(context)
3602 FIXME("Notifications not supported yet\n");
3604 TRACE("ALSA does not support volume control\n");
3606 EnterCriticalSection(&session->lock);
3608 session->master_vol = level;
3610 LeaveCriticalSection(&session->lock);
3612 return S_OK;
3615 static HRESULT WINAPI SimpleAudioVolume_GetMasterVolume(
3616 ISimpleAudioVolume *iface, float *level)
3618 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
3619 AudioSession *session = This->session;
3621 TRACE("(%p)->(%p)\n", session, level);
3623 if(!level)
3624 return NULL_PTR_ERR;
3626 *level = session->master_vol;
3628 return S_OK;
3631 static HRESULT WINAPI SimpleAudioVolume_SetMute(ISimpleAudioVolume *iface,
3632 BOOL mute, const GUID *context)
3634 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
3635 AudioSession *session = This->session;
3637 TRACE("(%p)->(%u, %s)\n", session, mute, debugstr_guid(context));
3639 if(context)
3640 FIXME("Notifications not supported yet\n");
3642 session->mute = mute;
3644 return S_OK;
3647 static HRESULT WINAPI SimpleAudioVolume_GetMute(ISimpleAudioVolume *iface,
3648 BOOL *mute)
3650 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
3651 AudioSession *session = This->session;
3653 TRACE("(%p)->(%p)\n", session, mute);
3655 if(!mute)
3656 return NULL_PTR_ERR;
3658 *mute = session->mute;
3660 return S_OK;
3663 static const ISimpleAudioVolumeVtbl SimpleAudioVolume_Vtbl =
3665 SimpleAudioVolume_QueryInterface,
3666 SimpleAudioVolume_AddRef,
3667 SimpleAudioVolume_Release,
3668 SimpleAudioVolume_SetMasterVolume,
3669 SimpleAudioVolume_GetMasterVolume,
3670 SimpleAudioVolume_SetMute,
3671 SimpleAudioVolume_GetMute
3674 static HRESULT WINAPI AudioStreamVolume_QueryInterface(
3675 IAudioStreamVolume *iface, REFIID riid, void **ppv)
3677 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
3679 if(!ppv)
3680 return E_POINTER;
3681 *ppv = NULL;
3683 if(IsEqualIID(riid, &IID_IUnknown) ||
3684 IsEqualIID(riid, &IID_IAudioStreamVolume))
3685 *ppv = iface;
3686 if(*ppv){
3687 IUnknown_AddRef((IUnknown*)*ppv);
3688 return S_OK;
3691 WARN("Unknown interface %s\n", debugstr_guid(riid));
3692 return E_NOINTERFACE;
3695 static ULONG WINAPI AudioStreamVolume_AddRef(IAudioStreamVolume *iface)
3697 ACImpl *This = impl_from_IAudioStreamVolume(iface);
3698 return IAudioClient3_AddRef(&This->IAudioClient3_iface);
3701 static ULONG WINAPI AudioStreamVolume_Release(IAudioStreamVolume *iface)
3703 ACImpl *This = impl_from_IAudioStreamVolume(iface);
3704 return IAudioClient3_Release(&This->IAudioClient3_iface);
3707 static HRESULT WINAPI AudioStreamVolume_GetChannelCount(
3708 IAudioStreamVolume *iface, UINT32 *out)
3710 ACImpl *This = impl_from_IAudioStreamVolume(iface);
3712 TRACE("(%p)->(%p)\n", This, out);
3714 if(!out)
3715 return E_POINTER;
3717 *out = This->fmt->nChannels;
3719 return S_OK;
3722 static HRESULT WINAPI AudioStreamVolume_SetChannelVolume(
3723 IAudioStreamVolume *iface, UINT32 index, float level)
3725 ACImpl *This = impl_from_IAudioStreamVolume(iface);
3727 TRACE("(%p)->(%d, %f)\n", This, index, level);
3729 if(level < 0.f || level > 1.f)
3730 return E_INVALIDARG;
3732 if(index >= This->fmt->nChannels)
3733 return E_INVALIDARG;
3735 TRACE("ALSA does not support volume control\n");
3737 EnterCriticalSection(&This->lock);
3739 This->vols[index] = level;
3741 LeaveCriticalSection(&This->lock);
3743 return S_OK;
3746 static HRESULT WINAPI AudioStreamVolume_GetChannelVolume(
3747 IAudioStreamVolume *iface, UINT32 index, float *level)
3749 ACImpl *This = impl_from_IAudioStreamVolume(iface);
3751 TRACE("(%p)->(%d, %p)\n", This, index, level);
3753 if(!level)
3754 return E_POINTER;
3756 if(index >= This->fmt->nChannels)
3757 return E_INVALIDARG;
3759 *level = This->vols[index];
3761 return S_OK;
3764 static HRESULT WINAPI AudioStreamVolume_SetAllVolumes(
3765 IAudioStreamVolume *iface, UINT32 count, const float *levels)
3767 ACImpl *This = impl_from_IAudioStreamVolume(iface);
3768 unsigned int i;
3770 TRACE("(%p)->(%d, %p)\n", This, count, levels);
3772 if(!levels)
3773 return E_POINTER;
3775 if(count != This->fmt->nChannels)
3776 return E_INVALIDARG;
3778 TRACE("ALSA does not support volume control\n");
3780 EnterCriticalSection(&This->lock);
3782 for(i = 0; i < count; ++i)
3783 This->vols[i] = levels[i];
3785 LeaveCriticalSection(&This->lock);
3787 return S_OK;
3790 static HRESULT WINAPI AudioStreamVolume_GetAllVolumes(
3791 IAudioStreamVolume *iface, UINT32 count, float *levels)
3793 ACImpl *This = impl_from_IAudioStreamVolume(iface);
3794 unsigned int i;
3796 TRACE("(%p)->(%d, %p)\n", This, count, levels);
3798 if(!levels)
3799 return E_POINTER;
3801 if(count != This->fmt->nChannels)
3802 return E_INVALIDARG;
3804 EnterCriticalSection(&This->lock);
3806 for(i = 0; i < count; ++i)
3807 levels[i] = This->vols[i];
3809 LeaveCriticalSection(&This->lock);
3811 return S_OK;
3814 static const IAudioStreamVolumeVtbl AudioStreamVolume_Vtbl =
3816 AudioStreamVolume_QueryInterface,
3817 AudioStreamVolume_AddRef,
3818 AudioStreamVolume_Release,
3819 AudioStreamVolume_GetChannelCount,
3820 AudioStreamVolume_SetChannelVolume,
3821 AudioStreamVolume_GetChannelVolume,
3822 AudioStreamVolume_SetAllVolumes,
3823 AudioStreamVolume_GetAllVolumes
3826 static HRESULT WINAPI ChannelAudioVolume_QueryInterface(
3827 IChannelAudioVolume *iface, REFIID riid, void **ppv)
3829 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
3831 if(!ppv)
3832 return E_POINTER;
3833 *ppv = NULL;
3835 if(IsEqualIID(riid, &IID_IUnknown) ||
3836 IsEqualIID(riid, &IID_IChannelAudioVolume))
3837 *ppv = iface;
3838 if(*ppv){
3839 IUnknown_AddRef((IUnknown*)*ppv);
3840 return S_OK;
3843 WARN("Unknown interface %s\n", debugstr_guid(riid));
3844 return E_NOINTERFACE;
3847 static ULONG WINAPI ChannelAudioVolume_AddRef(IChannelAudioVolume *iface)
3849 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
3850 return AudioSessionControl_AddRef(&This->IAudioSessionControl2_iface);
3853 static ULONG WINAPI ChannelAudioVolume_Release(IChannelAudioVolume *iface)
3855 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
3856 return AudioSessionControl_Release(&This->IAudioSessionControl2_iface);
3859 static HRESULT WINAPI ChannelAudioVolume_GetChannelCount(
3860 IChannelAudioVolume *iface, UINT32 *out)
3862 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
3863 AudioSession *session = This->session;
3865 TRACE("(%p)->(%p)\n", session, out);
3867 if(!out)
3868 return NULL_PTR_ERR;
3870 *out = session->channel_count;
3872 return S_OK;
3875 static HRESULT WINAPI ChannelAudioVolume_SetChannelVolume(
3876 IChannelAudioVolume *iface, UINT32 index, float level,
3877 const GUID *context)
3879 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
3880 AudioSession *session = This->session;
3882 TRACE("(%p)->(%d, %f, %s)\n", session, index, level,
3883 wine_dbgstr_guid(context));
3885 if(level < 0.f || level > 1.f)
3886 return E_INVALIDARG;
3888 if(index >= session->channel_count)
3889 return E_INVALIDARG;
3891 if(context)
3892 FIXME("Notifications not supported yet\n");
3894 TRACE("ALSA does not support volume control\n");
3896 EnterCriticalSection(&session->lock);
3898 session->channel_vols[index] = level;
3900 LeaveCriticalSection(&session->lock);
3902 return S_OK;
3905 static HRESULT WINAPI ChannelAudioVolume_GetChannelVolume(
3906 IChannelAudioVolume *iface, UINT32 index, float *level)
3908 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
3909 AudioSession *session = This->session;
3911 TRACE("(%p)->(%d, %p)\n", session, index, level);
3913 if(!level)
3914 return NULL_PTR_ERR;
3916 if(index >= session->channel_count)
3917 return E_INVALIDARG;
3919 *level = session->channel_vols[index];
3921 return S_OK;
3924 static HRESULT WINAPI ChannelAudioVolume_SetAllVolumes(
3925 IChannelAudioVolume *iface, UINT32 count, const float *levels,
3926 const GUID *context)
3928 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
3929 AudioSession *session = This->session;
3930 unsigned int i;
3932 TRACE("(%p)->(%d, %p, %s)\n", session, count, levels,
3933 wine_dbgstr_guid(context));
3935 if(!levels)
3936 return NULL_PTR_ERR;
3938 if(count != session->channel_count)
3939 return E_INVALIDARG;
3941 if(context)
3942 FIXME("Notifications not supported yet\n");
3944 TRACE("ALSA does not support volume control\n");
3946 EnterCriticalSection(&session->lock);
3948 for(i = 0; i < count; ++i)
3949 session->channel_vols[i] = levels[i];
3951 LeaveCriticalSection(&session->lock);
3953 return S_OK;
3956 static HRESULT WINAPI ChannelAudioVolume_GetAllVolumes(
3957 IChannelAudioVolume *iface, UINT32 count, float *levels)
3959 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
3960 AudioSession *session = This->session;
3961 unsigned int i;
3963 TRACE("(%p)->(%d, %p)\n", session, count, levels);
3965 if(!levels)
3966 return NULL_PTR_ERR;
3968 if(count != session->channel_count)
3969 return E_INVALIDARG;
3971 for(i = 0; i < count; ++i)
3972 levels[i] = session->channel_vols[i];
3974 return S_OK;
3977 static const IChannelAudioVolumeVtbl ChannelAudioVolume_Vtbl =
3979 ChannelAudioVolume_QueryInterface,
3980 ChannelAudioVolume_AddRef,
3981 ChannelAudioVolume_Release,
3982 ChannelAudioVolume_GetChannelCount,
3983 ChannelAudioVolume_SetChannelVolume,
3984 ChannelAudioVolume_GetChannelVolume,
3985 ChannelAudioVolume_SetAllVolumes,
3986 ChannelAudioVolume_GetAllVolumes
3989 static HRESULT WINAPI AudioSessionManager_QueryInterface(IAudioSessionManager2 *iface,
3990 REFIID riid, void **ppv)
3992 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
3994 if(!ppv)
3995 return E_POINTER;
3996 *ppv = NULL;
3998 if(IsEqualIID(riid, &IID_IUnknown) ||
3999 IsEqualIID(riid, &IID_IAudioSessionManager) ||
4000 IsEqualIID(riid, &IID_IAudioSessionManager2))
4001 *ppv = iface;
4002 if(*ppv){
4003 IUnknown_AddRef((IUnknown*)*ppv);
4004 return S_OK;
4007 WARN("Unknown interface %s\n", debugstr_guid(riid));
4008 return E_NOINTERFACE;
4011 static ULONG WINAPI AudioSessionManager_AddRef(IAudioSessionManager2 *iface)
4013 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
4014 ULONG ref;
4015 ref = InterlockedIncrement(&This->ref);
4016 TRACE("(%p) Refcount now %u\n", This, ref);
4017 return ref;
4020 static ULONG WINAPI AudioSessionManager_Release(IAudioSessionManager2 *iface)
4022 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
4023 ULONG ref;
4024 ref = InterlockedDecrement(&This->ref);
4025 TRACE("(%p) Refcount now %u\n", This, ref);
4026 if(!ref)
4027 HeapFree(GetProcessHeap(), 0, This);
4028 return ref;
4031 static HRESULT WINAPI AudioSessionManager_GetAudioSessionControl(
4032 IAudioSessionManager2 *iface, const GUID *session_guid, DWORD flags,
4033 IAudioSessionControl **out)
4035 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
4036 AudioSession *session;
4037 AudioSessionWrapper *wrapper;
4038 HRESULT hr;
4040 TRACE("(%p)->(%s, %x, %p)\n", This, debugstr_guid(session_guid),
4041 flags, out);
4043 hr = get_audio_session(session_guid, This->device, 0, &session);
4044 if(FAILED(hr))
4045 return hr;
4047 wrapper = AudioSessionWrapper_Create(NULL);
4048 if(!wrapper)
4049 return E_OUTOFMEMORY;
4051 wrapper->session = session;
4053 *out = (IAudioSessionControl*)&wrapper->IAudioSessionControl2_iface;
4055 return S_OK;
4058 static HRESULT WINAPI AudioSessionManager_GetSimpleAudioVolume(
4059 IAudioSessionManager2 *iface, const GUID *session_guid, DWORD flags,
4060 ISimpleAudioVolume **out)
4062 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
4063 AudioSession *session;
4064 AudioSessionWrapper *wrapper;
4065 HRESULT hr;
4067 TRACE("(%p)->(%s, %x, %p)\n", This, debugstr_guid(session_guid),
4068 flags, out);
4070 hr = get_audio_session(session_guid, This->device, 0, &session);
4071 if(FAILED(hr))
4072 return hr;
4074 wrapper = AudioSessionWrapper_Create(NULL);
4075 if(!wrapper)
4076 return E_OUTOFMEMORY;
4078 wrapper->session = session;
4080 *out = &wrapper->ISimpleAudioVolume_iface;
4082 return S_OK;
4085 static HRESULT WINAPI AudioSessionManager_GetSessionEnumerator(
4086 IAudioSessionManager2 *iface, IAudioSessionEnumerator **out)
4088 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
4089 FIXME("(%p)->(%p) - stub\n", This, out);
4090 return E_NOTIMPL;
4093 static HRESULT WINAPI AudioSessionManager_RegisterSessionNotification(
4094 IAudioSessionManager2 *iface, IAudioSessionNotification *notification)
4096 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
4097 FIXME("(%p)->(%p) - stub\n", This, notification);
4098 return E_NOTIMPL;
4101 static HRESULT WINAPI AudioSessionManager_UnregisterSessionNotification(
4102 IAudioSessionManager2 *iface, IAudioSessionNotification *notification)
4104 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
4105 FIXME("(%p)->(%p) - stub\n", This, notification);
4106 return E_NOTIMPL;
4109 static HRESULT WINAPI AudioSessionManager_RegisterDuckNotification(
4110 IAudioSessionManager2 *iface, const WCHAR *session_id,
4111 IAudioVolumeDuckNotification *notification)
4113 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
4114 FIXME("(%p)->(%p) - stub\n", This, notification);
4115 return E_NOTIMPL;
4118 static HRESULT WINAPI AudioSessionManager_UnregisterDuckNotification(
4119 IAudioSessionManager2 *iface,
4120 IAudioVolumeDuckNotification *notification)
4122 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
4123 FIXME("(%p)->(%p) - stub\n", This, notification);
4124 return E_NOTIMPL;
4127 static const IAudioSessionManager2Vtbl AudioSessionManager2_Vtbl =
4129 AudioSessionManager_QueryInterface,
4130 AudioSessionManager_AddRef,
4131 AudioSessionManager_Release,
4132 AudioSessionManager_GetAudioSessionControl,
4133 AudioSessionManager_GetSimpleAudioVolume,
4134 AudioSessionManager_GetSessionEnumerator,
4135 AudioSessionManager_RegisterSessionNotification,
4136 AudioSessionManager_UnregisterSessionNotification,
4137 AudioSessionManager_RegisterDuckNotification,
4138 AudioSessionManager_UnregisterDuckNotification
4141 HRESULT WINAPI AUDDRV_GetAudioSessionManager(IMMDevice *device,
4142 IAudioSessionManager2 **out)
4144 SessionMgr *This;
4146 This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(SessionMgr));
4147 if(!This)
4148 return E_OUTOFMEMORY;
4150 This->IAudioSessionManager2_iface.lpVtbl = &AudioSessionManager2_Vtbl;
4151 This->device = device;
4152 This->ref = 1;
4154 *out = &This->IAudioSessionManager2_iface;
4156 return S_OK;
4159 static unsigned int alsa_probe_num_speakers(char *name) {
4160 snd_pcm_t *handle;
4161 snd_pcm_hw_params_t *params;
4162 int err;
4163 unsigned int max_channels = 0;
4165 if ((err = snd_pcm_open(&handle, name, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK)) < 0) {
4166 WARN("The device \"%s\" failed to open: %d (%s).\n",
4167 name, err, snd_strerror(err));
4168 return 0;
4171 params = HeapAlloc(GetProcessHeap(), 0, snd_pcm_hw_params_sizeof());
4172 if (!params) {
4173 WARN("Out of memory.\n");
4174 snd_pcm_close(handle);
4175 return 0;
4178 if ((err = snd_pcm_hw_params_any(handle, params)) < 0) {
4179 WARN("snd_pcm_hw_params_any failed for \"%s\": %d (%s).\n",
4180 name, err, snd_strerror(err));
4181 goto exit;
4184 if ((err = snd_pcm_hw_params_get_channels_max(params,
4185 &max_channels)) < 0){
4186 WARN("Unable to get max channels: %d (%s)\n", err, snd_strerror(err));
4187 goto exit;
4190 exit:
4191 HeapFree(GetProcessHeap(), 0, params);
4192 snd_pcm_close(handle);
4194 return max_channels;
4197 enum AudioDeviceConnectionType {
4198 AudioDeviceConnectionType_Unknown = 0,
4199 AudioDeviceConnectionType_PCI,
4200 AudioDeviceConnectionType_USB
4203 HRESULT WINAPI AUDDRV_GetPropValue(GUID *guid, const PROPERTYKEY *prop, PROPVARIANT *out)
4205 char name[256];
4206 EDataFlow flow;
4208 static const PROPERTYKEY devicepath_key = { /* undocumented? - {b3f8fa53-0004-438e-9003-51a46e139bfc},2 */
4209 {0xb3f8fa53, 0x0004, 0x438e, {0x90, 0x03, 0x51, 0xa4, 0x6e, 0x13, 0x9b, 0xfc}}, 2
4212 TRACE("%s, (%s,%u), %p\n", wine_dbgstr_guid(guid), wine_dbgstr_guid(&prop->fmtid), prop->pid, out);
4214 if(!get_alsa_name_by_guid(guid, name, sizeof(name), &flow))
4216 WARN("Unknown interface %s\n", debugstr_guid(guid));
4217 return E_NOINTERFACE;
4220 if(IsEqualPropertyKey(*prop, devicepath_key))
4222 char uevent[MAX_PATH];
4223 FILE *fuevent;
4224 int card, device;
4226 /* only implemented for identifiable devices, i.e. not "default" */
4227 if(!sscanf(name, "plughw:%u,%u", &card, &device))
4228 return E_NOTIMPL;
4230 sprintf(uevent, "/sys/class/sound/card%u/device/uevent", card);
4231 fuevent = fopen(uevent, "r");
4233 if(fuevent){
4234 enum AudioDeviceConnectionType connection = AudioDeviceConnectionType_Unknown;
4235 USHORT vendor_id = 0, product_id = 0;
4236 char line[256];
4238 while (fgets(line, sizeof(line), fuevent)) {
4239 char *val;
4240 size_t val_len;
4242 if((val = strchr(line, '='))) {
4243 val[0] = 0;
4244 val++;
4246 val_len = strlen(val);
4247 if(val_len > 0 && val[val_len - 1] == '\n') { val[val_len - 1] = 0; }
4249 if(!strcmp(line, "PCI_ID")){
4250 connection = AudioDeviceConnectionType_PCI;
4251 if(sscanf(val, "%hX:%hX", &vendor_id, &product_id)<2){
4252 WARN("Unexpected input when reading PCI_ID in uevent file.\n");
4253 connection = AudioDeviceConnectionType_Unknown;
4254 break;
4256 }else if(!strcmp(line, "DEVTYPE") && !strcmp(val,"usb_interface"))
4257 connection = AudioDeviceConnectionType_USB;
4258 else if(!strcmp(line, "PRODUCT"))
4259 if(sscanf(val, "%hx/%hx/", &vendor_id, &product_id)<2){
4260 WARN("Unexpected input when reading PRODUCT in uevent file.\n");
4261 connection = AudioDeviceConnectionType_Unknown;
4262 break;
4267 fclose(fuevent);
4269 if(connection == AudioDeviceConnectionType_USB || connection == AudioDeviceConnectionType_PCI){
4270 static const WCHAR usbformatW[] = { '{','1','}','.','U','S','B','\\','V','I','D','_',
4271 '%','0','4','X','&','P','I','D','_','%','0','4','X','\\',
4272 '%','u','&','%','0','8','X',0 }; /* "{1}.USB\VID_%04X&PID_%04X\%u&%08X" */
4273 static const WCHAR pciformatW[] = { '{','1','}','.','H','D','A','U','D','I','O','\\','F','U','N','C','_','0','1','&',
4274 'V','E','N','_','%','0','4','X','&','D','E','V','_',
4275 '%','0','4','X','\\','%','u','&','%','0','8','X',0 }; /* "{1}.HDAUDIO\FUNC_01&VEN_%04X&DEV_%04X\%u&%08X" */
4276 UINT serial_number;
4278 /* As hardly any audio devices have serial numbers, Windows instead
4279 appears to use a persistent random number. We emulate this here
4280 by instead using the last 8 hex digits of the GUID. */
4281 serial_number = (guid->Data4[4] << 24) | (guid->Data4[5] << 16) | (guid->Data4[6] << 8) | guid->Data4[7];
4283 out->vt = VT_LPWSTR;
4284 out->u.pwszVal = CoTaskMemAlloc(128 * sizeof(WCHAR));
4286 if(!out->u.pwszVal)
4287 return E_OUTOFMEMORY;
4289 if(connection == AudioDeviceConnectionType_USB)
4290 sprintfW( out->u.pwszVal, usbformatW, vendor_id, product_id, device, serial_number);
4291 else if(connection == AudioDeviceConnectionType_PCI)
4292 sprintfW( out->u.pwszVal, pciformatW, vendor_id, product_id, device, serial_number);
4294 return S_OK;
4296 }else{
4297 WARN("Could not open %s for reading\n", uevent);
4298 return E_NOTIMPL;
4300 } else if (flow != eCapture && IsEqualPropertyKey(*prop, PKEY_AudioEndpoint_PhysicalSpeakers)) {
4301 unsigned int num_speakers, card, device;
4302 char hwname[255];
4304 if (sscanf(name, "plughw:%u,%u", &card, &device))
4305 sprintf(hwname, "hw:%u,%u", card, device); /* must be hw rather than plughw to work */
4306 else
4307 strcpy(hwname, name);
4309 num_speakers = alsa_probe_num_speakers(hwname);
4310 if (num_speakers == 0)
4311 return E_FAIL;
4313 out->vt = VT_UI4;
4315 if (num_speakers > 6)
4316 out->u.ulVal = KSAUDIO_SPEAKER_STEREO;
4317 else if (num_speakers == 6)
4318 out->u.ulVal = KSAUDIO_SPEAKER_5POINT1;
4319 else if (num_speakers >= 4)
4320 out->u.ulVal = KSAUDIO_SPEAKER_QUAD;
4321 else if (num_speakers >= 2)
4322 out->u.ulVal = KSAUDIO_SPEAKER_STEREO;
4323 else if (num_speakers == 1)
4324 out->u.ulVal = KSAUDIO_SPEAKER_MONO;
4326 return S_OK;
4329 TRACE("Unimplemented property %s,%u\n", wine_dbgstr_guid(&prop->fmtid), prop->pid);
4331 return E_NOTIMPL;