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
31 #include "wine/debug.h"
32 #include "wine/unicode.h"
33 #include "wine/list.h"
36 #include "mmdeviceapi.h"
42 #include "endpointvolume.h"
43 #include "audioclient.h"
44 #include "audiopolicy.h"
46 #include <alsa/asoundlib.h>
48 WINE_DEFAULT_DEBUG_CHANNEL(alsa);
49 WINE_DECLARE_DEBUG_CHANNEL(winediag);
51 #define NULL_PTR_ERR MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, RPC_X_NULL_REF_POINTER)
53 static const REFERENCE_TIME DefaultPeriod = 100000;
54 static const REFERENCE_TIME MinimumPeriod = 50000;
55 #define EXTRA_SAFE_RT 40000
58 typedef struct ACImpl ACImpl;
60 typedef struct _AudioSession {
71 CRITICAL_SECTION lock;
76 typedef struct _AudioSessionWrapper {
77 IAudioSessionControl2 IAudioSessionControl2_iface;
78 IChannelAudioVolume IChannelAudioVolume_iface;
79 ISimpleAudioVolume ISimpleAudioVolume_iface;
84 AudioSession *session;
85 } AudioSessionWrapper;
88 IAudioClient IAudioClient_iface;
89 IAudioRenderClient IAudioRenderClient_iface;
90 IAudioCaptureClient IAudioCaptureClient_iface;
91 IAudioClock IAudioClock_iface;
92 IAudioClock2 IAudioClock2_iface;
93 IAudioStreamVolume IAudioStreamVolume_iface;
97 snd_pcm_t *pcm_handle;
98 snd_pcm_uframes_t alsa_bufsize_frames, alsa_period_frames;
99 snd_pcm_hw_params_t *hw_params; /* does not hold state between calls */
100 snd_pcm_format_t alsa_format;
107 AUDCLNT_SHAREMODE share;
113 int alsa_channel_map[32];
115 BOOL initted, started;
116 REFERENCE_TIME mmdev_period_rt;
117 UINT64 written_frames, last_pos_frames;
118 UINT32 bufsize_frames, held_frames, tmp_buffer_frames, mmdev_period_frames;
119 snd_pcm_uframes_t remapping_buf_frames;
120 UINT32 lcl_offs_frames; /* offs into local_buffer where valid data starts */
121 UINT32 wri_offs_frames; /* where to write fresh data in local_buffer */
122 UINT32 hidden_frames; /* ALSA reserve to ensure continuous rendering */
125 BYTE *local_buffer, *tmp_buffer, *remapping_buf;
126 LONG32 getbuf_last; /* <0 when using tmp_buffer */
128 CRITICAL_SECTION lock;
130 AudioSession *session;
131 AudioSessionWrapper *session_wrapper;
136 typedef struct _SessionMgr {
137 IAudioSessionManager2 IAudioSessionManager2_iface;
144 static HANDLE g_timer_q;
146 static CRITICAL_SECTION g_sessions_lock;
147 static CRITICAL_SECTION_DEBUG g_sessions_lock_debug =
149 0, 0, &g_sessions_lock,
150 { &g_sessions_lock_debug.ProcessLocksList, &g_sessions_lock_debug.ProcessLocksList },
151 0, 0, { (DWORD_PTR)(__FILE__ ": g_sessions_lock") }
153 static CRITICAL_SECTION g_sessions_lock = { &g_sessions_lock_debug, -1, 0, 0, 0, 0 };
154 static struct list g_sessions = LIST_INIT(g_sessions);
156 static const WCHAR defaultW[] = {'d','e','f','a','u','l','t',0};
157 static const char defname[] = "default";
159 static const WCHAR drv_keyW[] = {'S','o','f','t','w','a','r','e','\\',
160 'W','i','n','e','\\','D','r','i','v','e','r','s','\\',
161 'w','i','n','e','a','l','s','a','.','d','r','v',0};
162 static const WCHAR drv_key_devicesW[] = {'S','o','f','t','w','a','r','e','\\',
163 'W','i','n','e','\\','D','r','i','v','e','r','s','\\',
164 'w','i','n','e','a','l','s','a','.','d','r','v','\\','d','e','v','i','c','e','s',0};
165 static const WCHAR guidW[] = {'g','u','i','d',0};
167 static const IAudioClientVtbl AudioClient_Vtbl;
168 static const IAudioRenderClientVtbl AudioRenderClient_Vtbl;
169 static const IAudioCaptureClientVtbl AudioCaptureClient_Vtbl;
170 static const IAudioSessionControl2Vtbl AudioSessionControl2_Vtbl;
171 static const ISimpleAudioVolumeVtbl SimpleAudioVolume_Vtbl;
172 static const IAudioClockVtbl AudioClock_Vtbl;
173 static const IAudioClock2Vtbl AudioClock2_Vtbl;
174 static const IAudioStreamVolumeVtbl AudioStreamVolume_Vtbl;
175 static const IChannelAudioVolumeVtbl ChannelAudioVolume_Vtbl;
176 static const IAudioSessionManager2Vtbl AudioSessionManager2_Vtbl;
178 static AudioSessionWrapper *AudioSessionWrapper_Create(ACImpl *client);
180 static inline ACImpl *impl_from_IAudioClient(IAudioClient *iface)
182 return CONTAINING_RECORD(iface, ACImpl, IAudioClient_iface);
185 static inline ACImpl *impl_from_IAudioRenderClient(IAudioRenderClient *iface)
187 return CONTAINING_RECORD(iface, ACImpl, IAudioRenderClient_iface);
190 static inline ACImpl *impl_from_IAudioCaptureClient(IAudioCaptureClient *iface)
192 return CONTAINING_RECORD(iface, ACImpl, IAudioCaptureClient_iface);
195 static inline AudioSessionWrapper *impl_from_IAudioSessionControl2(IAudioSessionControl2 *iface)
197 return CONTAINING_RECORD(iface, AudioSessionWrapper, IAudioSessionControl2_iface);
200 static inline AudioSessionWrapper *impl_from_ISimpleAudioVolume(ISimpleAudioVolume *iface)
202 return CONTAINING_RECORD(iface, AudioSessionWrapper, ISimpleAudioVolume_iface);
205 static inline AudioSessionWrapper *impl_from_IChannelAudioVolume(IChannelAudioVolume *iface)
207 return CONTAINING_RECORD(iface, AudioSessionWrapper, IChannelAudioVolume_iface);
210 static inline ACImpl *impl_from_IAudioClock(IAudioClock *iface)
212 return CONTAINING_RECORD(iface, ACImpl, IAudioClock_iface);
215 static inline ACImpl *impl_from_IAudioClock2(IAudioClock2 *iface)
217 return CONTAINING_RECORD(iface, ACImpl, IAudioClock2_iface);
220 static inline ACImpl *impl_from_IAudioStreamVolume(IAudioStreamVolume *iface)
222 return CONTAINING_RECORD(iface, ACImpl, IAudioStreamVolume_iface);
225 static inline SessionMgr *impl_from_IAudioSessionManager2(IAudioSessionManager2 *iface)
227 return CONTAINING_RECORD(iface, SessionMgr, IAudioSessionManager2_iface);
230 BOOL WINAPI DllMain(HINSTANCE dll, DWORD reason, void *reserved)
234 case DLL_PROCESS_ATTACH:
235 g_timer_q = CreateTimerQueue();
240 case DLL_PROCESS_DETACH:
241 DeleteCriticalSection(&g_sessions_lock);
247 /* From <dlls/mmdevapi/mmdevapi.h> */
248 enum DriverPriority {
249 Priority_Unavailable = 0,
255 int WINAPI AUDDRV_GetPriority(void)
257 return Priority_Neutral;
260 static void set_device_guid(EDataFlow flow, HKEY drv_key, const WCHAR *key_name,
268 lr = RegCreateKeyExW(HKEY_CURRENT_USER, drv_key_devicesW, 0, NULL, 0, KEY_WRITE,
269 NULL, &drv_key, NULL);
270 if(lr != ERROR_SUCCESS){
271 ERR("RegCreateKeyEx(drv_key) failed: %u\n", lr);
277 lr = RegCreateKeyExW(drv_key, key_name, 0, NULL, 0, KEY_WRITE,
279 if(lr != ERROR_SUCCESS){
280 ERR("RegCreateKeyEx(%s) failed: %u\n", wine_dbgstr_w(key_name), lr);
284 lr = RegSetValueExW(key, guidW, 0, REG_BINARY, (BYTE*)guid,
286 if(lr != ERROR_SUCCESS)
287 ERR("RegSetValueEx(%s\\guid) failed: %u\n", wine_dbgstr_w(key_name), lr);
292 RegCloseKey(drv_key);
295 static void get_device_guid(EDataFlow flow, const char *device, GUID *guid)
297 HKEY key = NULL, dev_key;
298 DWORD type, size = sizeof(*guid);
306 MultiByteToWideChar(CP_UNIXCP, 0, device, -1, key_name + 2,
307 (sizeof(key_name) / sizeof(*key_name)) - 2);
309 if(RegOpenKeyExW(HKEY_CURRENT_USER, drv_key_devicesW, 0, KEY_WRITE|KEY_READ, &key) == ERROR_SUCCESS){
310 if(RegOpenKeyExW(key, key_name, 0, KEY_READ, &dev_key) == ERROR_SUCCESS){
311 if(RegQueryValueExW(dev_key, guidW, 0, &type,
312 (BYTE*)guid, &size) == ERROR_SUCCESS){
313 if(type == REG_BINARY){
314 RegCloseKey(dev_key);
318 ERR("Invalid type for device %s GUID: %u; ignoring and overwriting\n",
319 wine_dbgstr_w(key_name), type);
321 RegCloseKey(dev_key);
327 set_device_guid(flow, key, key_name, guid);
333 static BOOL alsa_try_open(const char *devnode, snd_pcm_stream_t stream)
338 TRACE("devnode: %s, stream: %d\n", devnode, stream);
340 if((err = snd_pcm_open(&handle, devnode, stream, SND_PCM_NONBLOCK)) < 0){
341 WARN("The device \"%s\" failed to open: %d (%s).\n",
342 devnode, err, snd_strerror(err));
346 snd_pcm_close(handle);
350 static WCHAR *construct_device_id(EDataFlow flow, const WCHAR *chunk1, const char *chunk2)
354 DWORD len_wchars = 0, chunk1_len, copied = 0, prefix_len;
356 static const WCHAR dashW[] = {' ','-',' ',0};
357 static const size_t dashW_len = (sizeof(dashW) / sizeof(*dashW)) - 1;
358 static const WCHAR outW[] = {'O','u','t',':',' ',0};
359 static const WCHAR inW[] = {'I','n',':',' ',0};
363 prefix_len = (sizeof(outW) / sizeof(*outW)) - 1;
364 len_wchars += prefix_len;
367 prefix_len = (sizeof(inW) / sizeof(*inW)) - 1;
368 len_wchars += prefix_len;
371 chunk1_len = strlenW(chunk1);
372 len_wchars += chunk1_len;
375 len_wchars += dashW_len;
377 len_wchars += MultiByteToWideChar(CP_UNIXCP, 0, chunk2, -1, NULL, 0) - 1;
378 len_wchars += 1; /* NULL byte */
380 ret = HeapAlloc(GetProcessHeap(), 0, len_wchars * sizeof(WCHAR));
382 memcpy(ret, prefix, prefix_len * sizeof(WCHAR));
383 copied += prefix_len;
385 memcpy(ret + copied, chunk1, chunk1_len * sizeof(WCHAR));
386 copied += chunk1_len;
388 if(chunk1 && chunk2){
389 memcpy(ret + copied, dashW, dashW_len * sizeof(WCHAR));
393 MultiByteToWideChar(CP_UNIXCP, 0, chunk2, -1, ret + copied, len_wchars - copied);
397 TRACE("Enumerated device: %s\n", wine_dbgstr_w(ret));
402 static HRESULT alsa_get_card_devices(EDataFlow flow, snd_pcm_stream_t stream,
403 WCHAR ***ids, GUID **guids, UINT *num, snd_ctl_t *ctl, int card,
404 const WCHAR *cardnameW)
407 snd_pcm_info_t *info;
409 info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, snd_pcm_info_sizeof());
411 return E_OUTOFMEMORY;
413 snd_pcm_info_set_subdevice(info, 0);
414 snd_pcm_info_set_stream(info, stream);
417 for(err = snd_ctl_pcm_next_device(ctl, &device); device != -1 && err >= 0;
418 err = snd_ctl_pcm_next_device(ctl, &device)){
422 snd_pcm_info_set_device(info, device);
424 if((err = snd_ctl_pcm_info(ctl, info)) < 0){
426 /* This device doesn't have the right stream direction */
429 WARN("Failed to get info for card %d, device %d: %d (%s)\n",
430 card, device, err, snd_strerror(err));
434 sprintf(devnode, "plughw:%d,%d", card, device);
435 if(!alsa_try_open(devnode, stream))
439 *ids = HeapReAlloc(GetProcessHeap(), 0, *ids, sizeof(WCHAR *) * (*num + 1));
440 *guids = HeapReAlloc(GetProcessHeap(), 0, *guids, sizeof(GUID) * (*num + 1));
442 *ids = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR *));
443 *guids = HeapAlloc(GetProcessHeap(), 0, sizeof(GUID));
446 devname = snd_pcm_info_get_name(info);
448 WARN("Unable to get device name for card %d, device %d\n", card,
453 (*ids)[*num] = construct_device_id(flow, cardnameW, devname);
454 get_device_guid(flow, devnode, &(*guids)[*num]);
459 HeapFree(GetProcessHeap(), 0, info);
462 WARN("Got a failure during device enumeration on card %d: %d (%s)\n",
463 card, err, snd_strerror(err));
468 static void get_reg_devices(EDataFlow flow, snd_pcm_stream_t stream, WCHAR ***ids,
469 GUID **guids, UINT *num)
471 static const WCHAR ALSAOutputDevices[] = {'A','L','S','A','O','u','t','p','u','t','D','e','v','i','c','e','s',0};
472 static const WCHAR ALSAInputDevices[] = {'A','L','S','A','I','n','p','u','t','D','e','v','i','c','e','s',0};
474 WCHAR reg_devices[256];
475 DWORD size = sizeof(reg_devices), type;
476 const WCHAR *value_name = (stream == SND_PCM_STREAM_PLAYBACK) ? ALSAOutputDevices : ALSAInputDevices;
478 /* @@ Wine registry key: HKCU\Software\Wine\Drivers\winealsa.drv */
479 if(RegOpenKeyW(HKEY_CURRENT_USER, drv_keyW, &key) == ERROR_SUCCESS){
480 if(RegQueryValueExW(key, value_name, 0, &type,
481 (BYTE*)reg_devices, &size) == ERROR_SUCCESS){
482 WCHAR *p = reg_devices;
484 if(type != REG_MULTI_SZ){
485 ERR("Registry ALSA device list value type must be REG_MULTI_SZ\n");
493 WideCharToMultiByte(CP_UNIXCP, 0, p, -1, devname, sizeof(devname), NULL, NULL);
495 if(alsa_try_open(devname, stream)){
497 *ids = HeapReAlloc(GetProcessHeap(), 0, *ids, sizeof(WCHAR *) * (*num + 1));
498 *guids = HeapReAlloc(GetProcessHeap(), 0, *guids, sizeof(GUID) * (*num + 1));
500 *ids = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR *));
501 *guids = HeapAlloc(GetProcessHeap(), 0, sizeof(GUID));
503 (*ids)[*num] = construct_device_id(flow, p, NULL);
504 get_device_guid(flow, devname, &(*guids)[*num]);
508 p += lstrlenW(p) + 1;
516 static HRESULT alsa_enum_devices(EDataFlow flow, WCHAR ***ids, GUID **guids,
519 snd_pcm_stream_t stream = (flow == eRender ? SND_PCM_STREAM_PLAYBACK :
520 SND_PCM_STREAM_CAPTURE);
526 if(alsa_try_open(defname, stream)){
527 *ids = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR *));
528 (*ids)[0] = construct_device_id(flow, defaultW, NULL);
529 *guids = HeapAlloc(GetProcessHeap(), 0, sizeof(GUID));
530 get_device_guid(flow, defname, &(*guids)[0]);
534 get_reg_devices(flow, stream, ids, guids, num);
536 for(err = snd_card_next(&card); card != -1 && err >= 0;
537 err = snd_card_next(&card)){
544 sprintf(cardpath, "hw:%u", card);
546 if((err = snd_ctl_open(&ctl, cardpath, 0)) < 0){
547 WARN("Unable to open ctl for ALSA device %s: %d (%s)\n", cardpath,
548 err, snd_strerror(err));
552 if(snd_card_get_name(card, &cardname) < 0) {
553 /* FIXME: Should be localized */
554 static const WCHAR nameW[] = {'U','n','k','n','o','w','n',' ','s','o','u','n','d','c','a','r','d',0};
555 WARN("Unable to get card name for ALSA device %s: %d (%s)\n",
556 cardpath, err, snd_strerror(err));
557 alsa_get_card_devices(flow, stream, ids, guids, num, ctl, card, nameW);
559 len = MultiByteToWideChar(CP_UNIXCP, 0, cardname, -1, NULL, 0);
560 cardnameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
565 return E_OUTOFMEMORY;
567 MultiByteToWideChar(CP_UNIXCP, 0, cardname, -1, cardnameW, len);
569 alsa_get_card_devices(flow, stream, ids, guids, num, ctl, card, cardnameW);
571 HeapFree(GetProcessHeap(), 0, cardnameW);
579 WARN("Got a failure during card enumeration: %d (%s)\n",
580 err, snd_strerror(err));
585 HRESULT WINAPI AUDDRV_GetEndpointIDs(EDataFlow flow, WCHAR ***ids, GUID **guids,
586 UINT *num, UINT *def_index)
590 TRACE("%d %p %p %p %p\n", flow, ids, guids, num, def_index);
595 hr = alsa_enum_devices(flow, ids, guids, num);
598 for(i = 0; i < *num; ++i)
599 HeapFree(GetProcessHeap(), 0, (*ids)[i]);
600 HeapFree(GetProcessHeap(), 0, *ids);
601 HeapFree(GetProcessHeap(), 0, *guids);
602 return E_OUTOFMEMORY;
605 TRACE("Enumerated %u devices\n", *num);
608 HeapFree(GetProcessHeap(), 0, *ids);
610 HeapFree(GetProcessHeap(), 0, *guids);
619 /* Using the pulse PCM device from alsa-plugins 1.0.24 triggers a bug
620 * which causes audio to cease playing after a few minutes of playback.
621 * Setting handle_underrun=1 on pulse-backed ALSA devices seems to work
622 * around this issue. */
623 static snd_config_t *make_handle_underrun_config(const char *name)
625 snd_config_t *lconf, *dev_node, *hu_node, *type_node;
626 char dev_node_name[64];
627 const char *type_str;
632 if((err = snd_config_copy(&lconf, snd_config)) < 0){
633 WARN("snd_config_copy failed: %d (%s)\n", err, snd_strerror(err));
637 sprintf(dev_node_name, "pcm.%s", name);
638 err = snd_config_search(lconf, dev_node_name, &dev_node);
640 snd_config_delete(lconf);
644 snd_config_delete(lconf);
645 WARN("snd_config_search failed: %d (%s)\n", err, snd_strerror(err));
649 /* ALSA is extremely fragile. If it runs into a config setting it doesn't
650 * recognize, it tends to fail or assert. So we only want to inject
651 * handle_underrun=1 on devices that we know will recognize it. */
652 err = snd_config_search(dev_node, "type", &type_node);
654 snd_config_delete(lconf);
658 snd_config_delete(lconf);
659 WARN("snd_config_search failed: %d (%s)\n", err, snd_strerror(err));
663 if((err = snd_config_get_string(type_node, &type_str)) < 0){
664 snd_config_delete(lconf);
668 if(strcmp(type_str, "pulse") != 0){
669 snd_config_delete(lconf);
673 err = snd_config_search(dev_node, "handle_underrun", &hu_node);
675 /* user already has an explicit handle_underrun setting, so don't
676 * use a local config */
677 snd_config_delete(lconf);
681 snd_config_delete(lconf);
682 WARN("snd_config_search failed: %d (%s)\n", err, snd_strerror(err));
686 if((err = snd_config_imake_integer(&hu_node, "handle_underrun", 1)) < 0){
687 snd_config_delete(lconf);
688 WARN("snd_config_imake_integer failed: %d (%s)\n", err,
693 if((err = snd_config_add(dev_node, hu_node)) < 0){
694 snd_config_delete(lconf);
695 WARN("snd_config_add failed: %d (%s)\n", err, snd_strerror(err));
702 static BOOL get_alsa_name_by_guid(GUID *guid, char *name, DWORD name_size, EDataFlow *flow)
709 if(RegOpenKeyExW(HKEY_CURRENT_USER, drv_key_devicesW, 0, KEY_READ, &devices_key) != ERROR_SUCCESS){
710 ERR("No devices found in registry?\n");
719 key_name_size = sizeof(key_name)/sizeof(WCHAR);
720 if(RegEnumKeyExW(devices_key, i, key_name, &key_name_size, NULL,
721 NULL, NULL, NULL) != ERROR_SUCCESS)
724 if(RegOpenKeyExW(devices_key, key_name, 0, KEY_READ, &key) != ERROR_SUCCESS){
725 WARN("Couldn't open key: %s\n", wine_dbgstr_w(key_name));
729 size = sizeof(reg_guid);
730 if(RegQueryValueExW(key, guidW, 0, &type,
731 (BYTE*)®_guid, &size) == ERROR_SUCCESS){
732 if(IsEqualGUID(®_guid, guid)){
734 RegCloseKey(devices_key);
736 TRACE("Found matching device key: %s\n", wine_dbgstr_w(key_name));
738 if(key_name[0] == '0')
740 else if(key_name[0] == '1')
743 ERR("Unknown device type: %c\n", key_name[0]);
747 WideCharToMultiByte(CP_UNIXCP, 0, key_name + 2, -1, name, name_size, NULL, NULL);
758 RegCloseKey(devices_key);
760 WARN("No matching device in registry for GUID %s\n", debugstr_guid(guid));
765 HRESULT WINAPI AUDDRV_GetAudioEndpoint(GUID *guid, IMMDevice *dev, IAudioClient **out)
769 snd_pcm_stream_t stream;
771 static int handle_underrun = 1;
775 TRACE("%s %p %p\n", debugstr_guid(guid), dev, out);
777 if(!get_alsa_name_by_guid(guid, alsa_name, sizeof(alsa_name), &dataflow))
778 return AUDCLNT_E_DEVICE_INVALIDATED;
780 This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(ACImpl));
782 return E_OUTOFMEMORY;
784 This->IAudioClient_iface.lpVtbl = &AudioClient_Vtbl;
785 This->IAudioRenderClient_iface.lpVtbl = &AudioRenderClient_Vtbl;
786 This->IAudioCaptureClient_iface.lpVtbl = &AudioCaptureClient_Vtbl;
787 This->IAudioClock_iface.lpVtbl = &AudioClock_Vtbl;
788 This->IAudioClock2_iface.lpVtbl = &AudioClock2_Vtbl;
789 This->IAudioStreamVolume_iface.lpVtbl = &AudioStreamVolume_Vtbl;
791 if(dataflow == eRender)
792 stream = SND_PCM_STREAM_PLAYBACK;
793 else if(dataflow == eCapture)
794 stream = SND_PCM_STREAM_CAPTURE;
796 HeapFree(GetProcessHeap(), 0, This);
800 This->dataflow = dataflow;
801 if(handle_underrun && ((lconf = make_handle_underrun_config(alsa_name)))){
802 err = snd_pcm_open_lconf(&This->pcm_handle, alsa_name, stream, SND_PCM_NONBLOCK, lconf);
803 TRACE("Opening PCM device \"%s\" with handle_underrun: %d\n", alsa_name, err);
804 snd_config_delete(lconf);
805 /* Pulse <= 2010 returns EINVAL, it does not know handle_underrun. */
807 ERR_(winediag)("PulseAudio \"%s\" %d without handle_underrun. Audio may hang."
808 " Please upgrade to alsa_plugins >= 1.0.24\n", alsa_name, err);
814 err = snd_pcm_open(&This->pcm_handle, alsa_name, stream, SND_PCM_NONBLOCK);
817 HeapFree(GetProcessHeap(), 0, This);
818 WARN("Unable to open PCM \"%s\": %d (%s)\n", alsa_name, err, snd_strerror(err));
821 return AUDCLNT_E_DEVICE_IN_USE;
823 return AUDCLNT_E_ENDPOINT_CREATE_FAILED;
827 This->hw_params = HeapAlloc(GetProcessHeap(), 0,
828 snd_pcm_hw_params_sizeof());
829 if(!This->hw_params){
830 snd_pcm_close(This->pcm_handle);
831 HeapFree(GetProcessHeap(), 0, This);
832 return E_OUTOFMEMORY;
835 InitializeCriticalSection(&This->lock);
836 This->lock.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": ACImpl.lock");
839 IMMDevice_AddRef(This->parent);
841 *out = &This->IAudioClient_iface;
842 IAudioClient_AddRef(&This->IAudioClient_iface);
847 static HRESULT WINAPI AudioClient_QueryInterface(IAudioClient *iface,
848 REFIID riid, void **ppv)
850 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
855 if(IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IAudioClient))
858 IUnknown_AddRef((IUnknown*)*ppv);
861 WARN("Unknown interface %s\n", debugstr_guid(riid));
862 return E_NOINTERFACE;
865 static ULONG WINAPI AudioClient_AddRef(IAudioClient *iface)
867 ACImpl *This = impl_from_IAudioClient(iface);
869 ref = InterlockedIncrement(&This->ref);
870 TRACE("(%p) Refcount now %u\n", This, ref);
874 static ULONG WINAPI AudioClient_Release(IAudioClient *iface)
876 ACImpl *This = impl_from_IAudioClient(iface);
878 ref = InterlockedDecrement(&This->ref);
879 TRACE("(%p) Refcount now %u\n", This, ref);
881 IAudioClient_Stop(iface);
882 IMMDevice_Release(This->parent);
883 This->lock.DebugInfo->Spare[0] = 0;
884 DeleteCriticalSection(&This->lock);
885 snd_pcm_drop(This->pcm_handle);
886 snd_pcm_close(This->pcm_handle);
888 EnterCriticalSection(&g_sessions_lock);
889 list_remove(&This->entry);
890 LeaveCriticalSection(&g_sessions_lock);
892 HeapFree(GetProcessHeap(), 0, This->vols);
893 HeapFree(GetProcessHeap(), 0, This->local_buffer);
894 HeapFree(GetProcessHeap(), 0, This->remapping_buf);
895 HeapFree(GetProcessHeap(), 0, This->tmp_buffer);
896 HeapFree(GetProcessHeap(), 0, This->hw_params);
897 CoTaskMemFree(This->fmt);
898 HeapFree(GetProcessHeap(), 0, This);
903 static void dump_fmt(const WAVEFORMATEX *fmt)
905 TRACE("wFormatTag: 0x%x (", fmt->wFormatTag);
906 switch(fmt->wFormatTag){
907 case WAVE_FORMAT_PCM:
908 TRACE("WAVE_FORMAT_PCM");
910 case WAVE_FORMAT_IEEE_FLOAT:
911 TRACE("WAVE_FORMAT_IEEE_FLOAT");
913 case WAVE_FORMAT_EXTENSIBLE:
914 TRACE("WAVE_FORMAT_EXTENSIBLE");
922 TRACE("nChannels: %u\n", fmt->nChannels);
923 TRACE("nSamplesPerSec: %u\n", fmt->nSamplesPerSec);
924 TRACE("nAvgBytesPerSec: %u\n", fmt->nAvgBytesPerSec);
925 TRACE("nBlockAlign: %u\n", fmt->nBlockAlign);
926 TRACE("wBitsPerSample: %u\n", fmt->wBitsPerSample);
927 TRACE("cbSize: %u\n", fmt->cbSize);
929 if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE){
930 WAVEFORMATEXTENSIBLE *fmtex = (void*)fmt;
931 TRACE("dwChannelMask: %08x\n", fmtex->dwChannelMask);
932 TRACE("Samples: %04x\n", fmtex->Samples.wReserved);
933 TRACE("SubFormat: %s\n", wine_dbgstr_guid(&fmtex->SubFormat));
937 static WAVEFORMATEX *clone_format(const WAVEFORMATEX *fmt)
942 if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
943 size = sizeof(WAVEFORMATEXTENSIBLE);
945 size = sizeof(WAVEFORMATEX);
947 ret = CoTaskMemAlloc(size);
951 memcpy(ret, fmt, size);
953 ret->cbSize = size - sizeof(WAVEFORMATEX);
958 static snd_pcm_format_t alsa_format(const WAVEFORMATEX *fmt)
960 snd_pcm_format_t format = SND_PCM_FORMAT_UNKNOWN;
961 const WAVEFORMATEXTENSIBLE *fmtex = (const WAVEFORMATEXTENSIBLE *)fmt;
963 if(fmt->wFormatTag == WAVE_FORMAT_PCM ||
964 (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
965 IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))){
966 if(fmt->wBitsPerSample == 8)
967 format = SND_PCM_FORMAT_U8;
968 else if(fmt->wBitsPerSample == 16)
969 format = SND_PCM_FORMAT_S16_LE;
970 else if(fmt->wBitsPerSample == 24)
971 format = SND_PCM_FORMAT_S24_3LE;
972 else if(fmt->wBitsPerSample == 32)
973 format = SND_PCM_FORMAT_S32_LE;
975 WARN("Unsupported bit depth: %u\n", fmt->wBitsPerSample);
976 if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
977 fmt->wBitsPerSample != fmtex->Samples.wValidBitsPerSample){
978 if(fmtex->Samples.wValidBitsPerSample == 20 && fmt->wBitsPerSample == 24)
979 format = SND_PCM_FORMAT_S20_3LE;
981 WARN("Unsupported ValidBits: %u\n", fmtex->Samples.wValidBitsPerSample);
983 }else if(fmt->wFormatTag == WAVE_FORMAT_IEEE_FLOAT ||
984 (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
985 IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT))){
986 if(fmt->wBitsPerSample == 32)
987 format = SND_PCM_FORMAT_FLOAT_LE;
988 else if(fmt->wBitsPerSample == 64)
989 format = SND_PCM_FORMAT_FLOAT64_LE;
991 WARN("Unsupported float size: %u\n", fmt->wBitsPerSample);
993 WARN("Unknown wave format: %04x\n", fmt->wFormatTag);
997 static void session_init_vols(AudioSession *session, UINT channels)
999 if(session->channel_count < channels){
1002 if(session->channel_vols)
1003 session->channel_vols = HeapReAlloc(GetProcessHeap(), 0,
1004 session->channel_vols, sizeof(float) * channels);
1006 session->channel_vols = HeapAlloc(GetProcessHeap(), 0,
1007 sizeof(float) * channels);
1008 if(!session->channel_vols)
1011 for(i = session->channel_count; i < channels; ++i)
1012 session->channel_vols[i] = 1.f;
1014 session->channel_count = channels;
1018 static AudioSession *create_session(const GUID *guid, IMMDevice *device,
1023 ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(AudioSession));
1027 memcpy(&ret->guid, guid, sizeof(GUID));
1029 ret->device = device;
1031 list_init(&ret->clients);
1033 list_add_head(&g_sessions, &ret->entry);
1035 InitializeCriticalSection(&ret->lock);
1036 ret->lock.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": AudioSession.lock");
1038 session_init_vols(ret, num_channels);
1040 ret->master_vol = 1.f;
1045 /* if channels == 0, then this will return or create a session with
1046 * matching dataflow and GUID. otherwise, channels must also match */
1047 static HRESULT get_audio_session(const GUID *sessionguid,
1048 IMMDevice *device, UINT channels, AudioSession **out)
1050 AudioSession *session;
1052 if(!sessionguid || IsEqualGUID(sessionguid, &GUID_NULL)){
1053 *out = create_session(&GUID_NULL, device, channels);
1055 return E_OUTOFMEMORY;
1061 LIST_FOR_EACH_ENTRY(session, &g_sessions, AudioSession, entry){
1062 if(session->device == device &&
1063 IsEqualGUID(sessionguid, &session->guid)){
1064 session_init_vols(session, channels);
1071 *out = create_session(sessionguid, device, channels);
1073 return E_OUTOFMEMORY;
1079 static int alsa_channel_index(DWORD flag)
1082 case SPEAKER_FRONT_LEFT:
1084 case SPEAKER_FRONT_RIGHT:
1086 case SPEAKER_BACK_LEFT:
1088 case SPEAKER_BACK_RIGHT:
1090 case SPEAKER_FRONT_CENTER:
1092 case SPEAKER_LOW_FREQUENCY:
1094 case SPEAKER_SIDE_LEFT:
1096 case SPEAKER_SIDE_RIGHT:
1102 static BOOL need_remapping(ACImpl *This, const WAVEFORMATEX *fmt)
1105 for(i = 0; i < fmt->nChannels; ++i){
1106 if(This->alsa_channel_map[i] != i)
1112 static DWORD get_channel_mask(unsigned int channels)
1118 return KSAUDIO_SPEAKER_MONO;
1120 return KSAUDIO_SPEAKER_STEREO;
1122 return KSAUDIO_SPEAKER_STEREO | SPEAKER_LOW_FREQUENCY;
1124 return KSAUDIO_SPEAKER_QUAD; /* not _SURROUND */
1126 return KSAUDIO_SPEAKER_QUAD | SPEAKER_LOW_FREQUENCY;
1128 return KSAUDIO_SPEAKER_5POINT1; /* not 5POINT1_SURROUND */
1130 return KSAUDIO_SPEAKER_5POINT1 | SPEAKER_BACK_CENTER;
1132 return KSAUDIO_SPEAKER_7POINT1_SURROUND; /* Vista deprecates 7POINT1 */
1134 FIXME("Unknown speaker configuration: %u\n", channels);
1138 static HRESULT map_channels(ACImpl *This, const WAVEFORMATEX *fmt)
1140 if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE || fmt->nChannels > 2){
1141 WAVEFORMATEXTENSIBLE *fmtex = (void*)fmt;
1142 DWORD mask, flag = SPEAKER_FRONT_LEFT;
1145 if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
1146 fmtex->dwChannelMask != 0)
1147 mask = fmtex->dwChannelMask;
1149 mask = get_channel_mask(fmt->nChannels);
1151 This->alsa_channels = 0;
1153 while(i < fmt->nChannels && !(flag & SPEAKER_RESERVED)){
1155 This->alsa_channel_map[i] = alsa_channel_index(flag);
1156 TRACE("Mapping mmdevapi channel %u (0x%x) to ALSA channel %d\n",
1157 i, flag, This->alsa_channel_map[i]);
1158 if(This->alsa_channel_map[i] >= This->alsa_channels)
1159 This->alsa_channels = This->alsa_channel_map[i] + 1;
1165 while(i < fmt->nChannels){
1166 This->alsa_channel_map[i] = This->alsa_channels;
1167 TRACE("Mapping mmdevapi channel %u to ALSA channel %d\n",
1168 i, This->alsa_channel_map[i]);
1169 ++This->alsa_channels;
1173 for(i = 0; i < fmt->nChannels; ++i){
1174 if(This->alsa_channel_map[i] == -1){
1175 This->alsa_channel_map[i] = This->alsa_channels;
1176 ++This->alsa_channels;
1177 TRACE("Remapping mmdevapi channel %u to ALSA channel %d\n",
1178 i, This->alsa_channel_map[i]);
1182 This->need_remapping = need_remapping(This, fmt);
1184 TRACE("need_remapping: %u, alsa_channels: %d\n", This->need_remapping, This->alsa_channels);
1186 This->need_remapping = FALSE;
1187 This->alsa_channels = fmt->nChannels;
1188 TRACE("need_remapping: %u, alsa_channels: %d\n", This->need_remapping, This->alsa_channels);
1194 static HRESULT WINAPI AudioClient_Initialize(IAudioClient *iface,
1195 AUDCLNT_SHAREMODE mode, DWORD flags, REFERENCE_TIME duration,
1196 REFERENCE_TIME period, const WAVEFORMATEX *fmt,
1197 const GUID *sessionguid)
1199 ACImpl *This = impl_from_IAudioClient(iface);
1200 snd_pcm_sw_params_t *sw_params = NULL;
1201 snd_pcm_format_t format;
1202 unsigned int rate, alsa_period_us;
1206 TRACE("(%p)->(%x, %x, %s, %s, %p, %s)\n", This, mode, flags,
1207 wine_dbgstr_longlong(duration), wine_dbgstr_longlong(period), fmt, debugstr_guid(sessionguid));
1212 if(mode != AUDCLNT_SHAREMODE_SHARED && mode != AUDCLNT_SHAREMODE_EXCLUSIVE)
1213 return AUDCLNT_E_NOT_INITIALIZED;
1215 if(flags & ~(AUDCLNT_STREAMFLAGS_CROSSPROCESS |
1216 AUDCLNT_STREAMFLAGS_LOOPBACK |
1217 AUDCLNT_STREAMFLAGS_EVENTCALLBACK |
1218 AUDCLNT_STREAMFLAGS_NOPERSIST |
1219 AUDCLNT_STREAMFLAGS_RATEADJUST |
1220 AUDCLNT_SESSIONFLAGS_EXPIREWHENUNOWNED |
1221 AUDCLNT_SESSIONFLAGS_DISPLAY_HIDE |
1222 AUDCLNT_SESSIONFLAGS_DISPLAY_HIDEWHENEXPIRED)){
1223 TRACE("Unknown flags: %08x\n", flags);
1224 return E_INVALIDARG;
1227 if(mode == AUDCLNT_SHAREMODE_SHARED){
1228 period = DefaultPeriod;
1229 if( duration < 3 * period)
1230 duration = 3 * period;
1232 if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE){
1233 if(((WAVEFORMATEXTENSIBLE*)fmt)->dwChannelMask == 0 ||
1234 ((WAVEFORMATEXTENSIBLE*)fmt)->dwChannelMask & SPEAKER_RESERVED)
1235 return AUDCLNT_E_UNSUPPORTED_FORMAT;
1239 period = DefaultPeriod; /* not minimum */
1240 if(period < MinimumPeriod || period > 5000000)
1241 return AUDCLNT_E_INVALID_DEVICE_PERIOD;
1242 if(duration > 20000000) /* the smaller the period, the lower this limit */
1243 return AUDCLNT_E_BUFFER_SIZE_ERROR;
1244 if(flags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK){
1245 if(duration != period)
1246 return AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL;
1247 FIXME("EXCLUSIVE mode with EVENTCALLBACK\n");
1248 return AUDCLNT_E_DEVICE_IN_USE;
1250 if( duration < 8 * period)
1251 duration = 8 * period; /* may grow above 2s */
1255 EnterCriticalSection(&This->lock);
1258 LeaveCriticalSection(&This->lock);
1259 return AUDCLNT_E_ALREADY_INITIALIZED;
1264 if(FAILED(map_channels(This, fmt))){
1265 WARN("map_channels failed\n");
1266 hr = AUDCLNT_E_ENDPOINT_CREATE_FAILED;
1270 if((err = snd_pcm_hw_params_any(This->pcm_handle, This->hw_params)) < 0){
1271 WARN("Unable to get hw_params: %d (%s)\n", err, snd_strerror(err));
1272 hr = AUDCLNT_E_ENDPOINT_CREATE_FAILED;
1276 if((err = snd_pcm_hw_params_set_access(This->pcm_handle, This->hw_params,
1277 SND_PCM_ACCESS_RW_INTERLEAVED)) < 0){
1278 WARN("Unable to set access: %d (%s)\n", err, snd_strerror(err));
1279 hr = AUDCLNT_E_ENDPOINT_CREATE_FAILED;
1283 format = alsa_format(fmt);
1284 if (format == SND_PCM_FORMAT_UNKNOWN){
1285 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
1289 if((err = snd_pcm_hw_params_set_format(This->pcm_handle, This->hw_params,
1291 WARN("Unable to set ALSA format to %u: %d (%s)\n", format, err,
1293 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
1297 This->alsa_format = format;
1299 rate = fmt->nSamplesPerSec;
1300 if((err = snd_pcm_hw_params_set_rate_near(This->pcm_handle, This->hw_params,
1302 WARN("Unable to set rate to %u: %d (%s)\n", rate, err,
1304 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
1308 if((err = snd_pcm_hw_params_set_channels(This->pcm_handle, This->hw_params,
1309 This->alsa_channels)) < 0){
1310 WARN("Unable to set channels to %u: %d (%s)\n", fmt->nChannels, err,
1312 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
1316 This->mmdev_period_rt = period;
1317 alsa_period_us = This->mmdev_period_rt / 10;
1318 if((err = snd_pcm_hw_params_set_period_time_near(This->pcm_handle,
1319 This->hw_params, &alsa_period_us, NULL)) < 0)
1320 WARN("Unable to set period time near %u: %d (%s)\n", alsa_period_us,
1321 err, snd_strerror(err));
1322 /* ALSA updates the output variable alsa_period_us */
1324 This->mmdev_period_frames = MulDiv(fmt->nSamplesPerSec,
1325 This->mmdev_period_rt, 10000000);
1327 /* Buffer 4 ALSA periods if large enough, else 4 mmdevapi periods */
1328 This->alsa_bufsize_frames = This->mmdev_period_frames * 4;
1329 if(err < 0 || alsa_period_us < period / 10)
1330 err = snd_pcm_hw_params_set_buffer_size_near(This->pcm_handle,
1331 This->hw_params, &This->alsa_bufsize_frames);
1333 unsigned int periods = 4;
1334 err = snd_pcm_hw_params_set_periods_near(This->pcm_handle, This->hw_params, &periods, NULL);
1337 WARN("Unable to set buffer size: %d (%s)\n", err, snd_strerror(err));
1339 if((err = snd_pcm_hw_params(This->pcm_handle, This->hw_params)) < 0){
1340 WARN("Unable to set hw params: %d (%s)\n", err, snd_strerror(err));
1341 hr = AUDCLNT_E_ENDPOINT_CREATE_FAILED;
1345 if((err = snd_pcm_hw_params_get_period_size(This->hw_params,
1346 &This->alsa_period_frames, NULL)) < 0){
1347 WARN("Unable to get period size: %d (%s)\n", err, snd_strerror(err));
1348 hr = AUDCLNT_E_ENDPOINT_CREATE_FAILED;
1352 if((err = snd_pcm_hw_params_get_buffer_size(This->hw_params,
1353 &This->alsa_bufsize_frames)) < 0){
1354 WARN("Unable to get buffer size: %d (%s)\n", err, snd_strerror(err));
1355 hr = AUDCLNT_E_ENDPOINT_CREATE_FAILED;
1359 sw_params = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, snd_pcm_sw_params_sizeof());
1365 if((err = snd_pcm_sw_params_current(This->pcm_handle, sw_params)) < 0){
1366 WARN("Unable to get sw_params: %d (%s)\n", err, snd_strerror(err));
1367 hr = AUDCLNT_E_ENDPOINT_CREATE_FAILED;
1371 if((err = snd_pcm_sw_params_set_start_threshold(This->pcm_handle,
1372 sw_params, 1)) < 0){
1373 WARN("Unable set start threshold to 0: %d (%s)\n", err, snd_strerror(err));
1374 hr = AUDCLNT_E_ENDPOINT_CREATE_FAILED;
1378 if((err = snd_pcm_sw_params_set_stop_threshold(This->pcm_handle,
1379 sw_params, This->alsa_bufsize_frames)) < 0){
1380 WARN("Unable set stop threshold to %lu: %d (%s)\n",
1381 This->alsa_bufsize_frames, err, snd_strerror(err));
1382 hr = AUDCLNT_E_ENDPOINT_CREATE_FAILED;
1386 if((err = snd_pcm_sw_params(This->pcm_handle, sw_params)) < 0){
1387 WARN("Unable to set sw params: %d (%s)\n", err, snd_strerror(err));
1388 hr = AUDCLNT_E_ENDPOINT_CREATE_FAILED;
1392 if((err = snd_pcm_prepare(This->pcm_handle)) < 0){
1393 WARN("Unable to prepare device: %d (%s)\n", err, snd_strerror(err));
1394 hr = AUDCLNT_E_ENDPOINT_CREATE_FAILED;
1398 /* Bear in mind weird situations where
1399 * ALSA period (50ms) > mmdevapi buffer (3x10ms)
1400 * or surprising rounding as seen with 22050x8x1 with Pulse:
1401 * ALSA period 220 vs. 221 frames in mmdevapi and
1402 * buffer 883 vs. 2205 frames in mmdevapi! */
1403 This->bufsize_frames = MulDiv(duration, fmt->nSamplesPerSec, 10000000);
1404 This->hidden_frames = This->alsa_period_frames + This->mmdev_period_frames +
1405 MulDiv(fmt->nSamplesPerSec, EXTRA_SAFE_RT, 10000000);
1407 /* Check if the ALSA buffer is so small that it will run out before
1408 * the next MMDevAPI period tick occurs. Allow a little wiggle room
1409 * with 120% of the period time. */
1410 if(This->alsa_bufsize_frames < 1.2 * This->mmdev_period_frames)
1411 FIXME("ALSA buffer time is too small. Expect underruns. (%lu < %u * 1.2)\n",
1412 This->alsa_bufsize_frames, This->mmdev_period_frames);
1414 This->local_buffer = HeapAlloc(GetProcessHeap(), 0,
1415 This->bufsize_frames * fmt->nBlockAlign);
1416 if(!This->local_buffer){
1420 if (fmt->wBitsPerSample == 8)
1421 memset(This->local_buffer, 128, This->bufsize_frames * fmt->nBlockAlign);
1423 memset(This->local_buffer, 0, This->bufsize_frames * fmt->nBlockAlign);
1425 This->fmt = clone_format(fmt);
1431 This->vols = HeapAlloc(GetProcessHeap(), 0, fmt->nChannels * sizeof(float));
1437 for(i = 0; i < fmt->nChannels; ++i)
1438 This->vols[i] = 1.f;
1441 This->flags = flags;
1443 EnterCriticalSection(&g_sessions_lock);
1445 hr = get_audio_session(sessionguid, This->parent, fmt->nChannels,
1448 LeaveCriticalSection(&g_sessions_lock);
1452 list_add_tail(&This->session->clients, &This->entry);
1454 LeaveCriticalSection(&g_sessions_lock);
1456 This->initted = TRUE;
1458 TRACE("ALSA period: %lu frames\n", This->alsa_period_frames);
1459 TRACE("ALSA buffer: %lu frames\n", This->alsa_bufsize_frames);
1460 TRACE("MMDevice period: %u frames\n", This->mmdev_period_frames);
1461 TRACE("MMDevice buffer: %u frames\n", This->bufsize_frames);
1464 HeapFree(GetProcessHeap(), 0, sw_params);
1466 HeapFree(GetProcessHeap(), 0, This->local_buffer);
1467 This->local_buffer = NULL;
1468 CoTaskMemFree(This->fmt);
1470 HeapFree(GetProcessHeap(), 0, This->vols);
1474 LeaveCriticalSection(&This->lock);
1479 static HRESULT WINAPI AudioClient_GetBufferSize(IAudioClient *iface,
1482 ACImpl *This = impl_from_IAudioClient(iface);
1484 TRACE("(%p)->(%p)\n", This, out);
1489 EnterCriticalSection(&This->lock);
1492 LeaveCriticalSection(&This->lock);
1493 return AUDCLNT_E_NOT_INITIALIZED;
1496 *out = This->bufsize_frames;
1498 LeaveCriticalSection(&This->lock);
1503 static HRESULT WINAPI AudioClient_GetStreamLatency(IAudioClient *iface,
1504 REFERENCE_TIME *latency)
1506 ACImpl *This = impl_from_IAudioClient(iface);
1508 TRACE("(%p)->(%p)\n", This, latency);
1513 EnterCriticalSection(&This->lock);
1516 LeaveCriticalSection(&This->lock);
1517 return AUDCLNT_E_NOT_INITIALIZED;
1520 /* Hide some frames in the ALSA buffer. Allows us to return GetCurrentPadding=0
1521 * yet have enough data left to play (as if it were in native's mixer). Add:
1522 * + mmdevapi_period such that at the end of it, ALSA still has data;
1523 * + EXTRA_SAFE (~4ms) to allow for late callback invocation / fluctuation;
1524 * + alsa_period such that ALSA always has at least one period to play. */
1525 if(This->dataflow == eRender)
1526 *latency = MulDiv(This->hidden_frames, 10000000, This->fmt->nSamplesPerSec);
1528 *latency = MulDiv(This->alsa_period_frames, 10000000, This->fmt->nSamplesPerSec)
1529 + This->mmdev_period_rt;
1531 LeaveCriticalSection(&This->lock);
1536 static HRESULT WINAPI AudioClient_GetCurrentPadding(IAudioClient *iface,
1539 ACImpl *This = impl_from_IAudioClient(iface);
1541 TRACE("(%p)->(%p)\n", This, out);
1546 EnterCriticalSection(&This->lock);
1549 LeaveCriticalSection(&This->lock);
1550 return AUDCLNT_E_NOT_INITIALIZED;
1553 /* padding is solely updated at callback time in shared mode */
1554 *out = This->held_frames;
1556 LeaveCriticalSection(&This->lock);
1558 TRACE("pad: %u\n", *out);
1563 static HRESULT WINAPI AudioClient_IsFormatSupported(IAudioClient *iface,
1564 AUDCLNT_SHAREMODE mode, const WAVEFORMATEX *fmt,
1567 ACImpl *This = impl_from_IAudioClient(iface);
1568 snd_pcm_format_mask_t *formats = NULL;
1569 snd_pcm_format_t format;
1571 WAVEFORMATEX *closest = NULL;
1572 unsigned int max = 0, min = 0;
1575 TRACE("(%p)->(%x, %p, %p)\n", This, mode, fmt, out);
1577 if(!fmt || (mode == AUDCLNT_SHAREMODE_SHARED && !out))
1580 if(mode != AUDCLNT_SHAREMODE_SHARED && mode != AUDCLNT_SHAREMODE_EXCLUSIVE)
1581 return E_INVALIDARG;
1583 if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
1584 fmt->cbSize < sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX))
1585 return E_INVALIDARG;
1591 if(mode != AUDCLNT_SHAREMODE_SHARED)
1595 if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
1596 (fmt->nAvgBytesPerSec == 0 ||
1597 fmt->nBlockAlign == 0 ||
1598 ((WAVEFORMATEXTENSIBLE*)fmt)->Samples.wValidBitsPerSample > fmt->wBitsPerSample))
1599 return E_INVALIDARG;
1601 if(fmt->nChannels == 0)
1602 return AUDCLNT_E_UNSUPPORTED_FORMAT;
1604 EnterCriticalSection(&This->lock);
1606 if((err = snd_pcm_hw_params_any(This->pcm_handle, This->hw_params)) < 0){
1607 hr = AUDCLNT_E_DEVICE_INVALIDATED;
1611 formats = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
1612 snd_pcm_format_mask_sizeof());
1618 snd_pcm_hw_params_get_format_mask(This->hw_params, formats);
1619 format = alsa_format(fmt);
1620 if (format == SND_PCM_FORMAT_UNKNOWN ||
1621 !snd_pcm_format_mask_test(formats, format)){
1622 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
1626 closest = clone_format(fmt);
1632 if((err = snd_pcm_hw_params_get_rate_min(This->hw_params, &min, NULL)) < 0){
1633 hr = AUDCLNT_E_DEVICE_INVALIDATED;
1634 WARN("Unable to get min rate: %d (%s)\n", err, snd_strerror(err));
1638 if((err = snd_pcm_hw_params_get_rate_max(This->hw_params, &max, NULL)) < 0){
1639 hr = AUDCLNT_E_DEVICE_INVALIDATED;
1640 WARN("Unable to get max rate: %d (%s)\n", err, snd_strerror(err));
1644 if(fmt->nSamplesPerSec < min || fmt->nSamplesPerSec > max){
1645 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
1649 if((err = snd_pcm_hw_params_get_channels_min(This->hw_params, &min)) < 0){
1650 hr = AUDCLNT_E_DEVICE_INVALIDATED;
1651 WARN("Unable to get min channels: %d (%s)\n", err, snd_strerror(err));
1655 if((err = snd_pcm_hw_params_get_channels_max(This->hw_params, &max)) < 0){
1656 hr = AUDCLNT_E_DEVICE_INVALIDATED;
1657 WARN("Unable to get max channels: %d (%s)\n", err, snd_strerror(err));
1660 if(fmt->nChannels > max){
1662 closest->nChannels = max;
1663 }else if(fmt->nChannels < min){
1665 closest->nChannels = min;
1668 if(FAILED(map_channels(This, fmt))){
1669 hr = AUDCLNT_E_DEVICE_INVALIDATED;
1670 WARN("map_channels failed\n");
1673 if(This->alsa_channels > max){
1675 closest->nChannels = max;
1678 if(closest->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
1679 ((WAVEFORMATEXTENSIBLE*)closest)->dwChannelMask = get_channel_mask(closest->nChannels);
1681 if(fmt->nBlockAlign != fmt->nChannels * fmt->wBitsPerSample / 8 ||
1682 fmt->nAvgBytesPerSec != fmt->nBlockAlign * fmt->nSamplesPerSec ||
1683 (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
1684 ((WAVEFORMATEXTENSIBLE*)fmt)->Samples.wValidBitsPerSample < fmt->wBitsPerSample))
1687 if(mode == AUDCLNT_SHAREMODE_EXCLUSIVE &&
1688 fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE){
1689 if(((WAVEFORMATEXTENSIBLE*)fmt)->dwChannelMask == 0 ||
1690 ((WAVEFORMATEXTENSIBLE*)fmt)->dwChannelMask & SPEAKER_RESERVED)
1695 LeaveCriticalSection(&This->lock);
1696 HeapFree(GetProcessHeap(), 0, formats);
1698 if(hr == S_FALSE && !out)
1699 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
1701 if(hr == S_FALSE && out) {
1702 closest->nBlockAlign =
1703 closest->nChannels * closest->wBitsPerSample / 8;
1704 closest->nAvgBytesPerSec =
1705 closest->nBlockAlign * closest->nSamplesPerSec;
1706 if(closest->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
1707 ((WAVEFORMATEXTENSIBLE*)closest)->Samples.wValidBitsPerSample = closest->wBitsPerSample;
1710 CoTaskMemFree(closest);
1712 TRACE("returning: %08x\n", hr);
1716 static HRESULT WINAPI AudioClient_GetMixFormat(IAudioClient *iface,
1717 WAVEFORMATEX **pwfx)
1719 ACImpl *This = impl_from_IAudioClient(iface);
1720 WAVEFORMATEXTENSIBLE *fmt;
1721 snd_pcm_format_mask_t *formats;
1722 unsigned int max_rate, max_channels;
1726 TRACE("(%p)->(%p)\n", This, pwfx);
1732 fmt = CoTaskMemAlloc(sizeof(WAVEFORMATEXTENSIBLE));
1734 return E_OUTOFMEMORY;
1736 formats = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, snd_pcm_format_mask_sizeof());
1739 return E_OUTOFMEMORY;
1742 EnterCriticalSection(&This->lock);
1744 if((err = snd_pcm_hw_params_any(This->pcm_handle, This->hw_params)) < 0){
1745 WARN("Unable to get hw_params: %d (%s)\n", err, snd_strerror(err));
1746 hr = AUDCLNT_E_DEVICE_INVALIDATED;
1750 snd_pcm_hw_params_get_format_mask(This->hw_params, formats);
1752 fmt->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
1753 if(snd_pcm_format_mask_test(formats, SND_PCM_FORMAT_FLOAT_LE)){
1754 fmt->Format.wBitsPerSample = 32;
1755 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
1756 }else if(snd_pcm_format_mask_test(formats, SND_PCM_FORMAT_S16_LE)){
1757 fmt->Format.wBitsPerSample = 16;
1758 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1759 }else if(snd_pcm_format_mask_test(formats, SND_PCM_FORMAT_U8)){
1760 fmt->Format.wBitsPerSample = 8;
1761 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1762 }else if(snd_pcm_format_mask_test(formats, SND_PCM_FORMAT_S32_LE)){
1763 fmt->Format.wBitsPerSample = 32;
1764 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1765 }else if(snd_pcm_format_mask_test(formats, SND_PCM_FORMAT_S24_3LE)){
1766 fmt->Format.wBitsPerSample = 24;
1767 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1769 ERR("Didn't recognize any available ALSA formats\n");
1770 hr = AUDCLNT_E_DEVICE_INVALIDATED;
1774 if((err = snd_pcm_hw_params_get_channels_max(This->hw_params,
1775 &max_channels)) < 0){
1776 WARN("Unable to get max channels: %d (%s)\n", err, snd_strerror(err));
1777 hr = AUDCLNT_E_DEVICE_INVALIDATED;
1781 if(max_channels > 2)
1782 fmt->Format.nChannels = 2;
1784 fmt->Format.nChannels = max_channels;
1786 fmt->dwChannelMask = get_channel_mask(fmt->Format.nChannels);
1788 if((err = snd_pcm_hw_params_get_rate_max(This->hw_params, &max_rate,
1790 WARN("Unable to get max rate: %d (%s)\n", err, snd_strerror(err));
1791 hr = AUDCLNT_E_DEVICE_INVALIDATED;
1795 if(max_rate >= 48000)
1796 fmt->Format.nSamplesPerSec = 48000;
1797 else if(max_rate >= 44100)
1798 fmt->Format.nSamplesPerSec = 44100;
1799 else if(max_rate >= 22050)
1800 fmt->Format.nSamplesPerSec = 22050;
1801 else if(max_rate >= 11025)
1802 fmt->Format.nSamplesPerSec = 11025;
1803 else if(max_rate >= 8000)
1804 fmt->Format.nSamplesPerSec = 8000;
1806 ERR("Unknown max rate: %u\n", max_rate);
1807 hr = AUDCLNT_E_DEVICE_INVALIDATED;
1811 fmt->Format.nBlockAlign = (fmt->Format.wBitsPerSample *
1812 fmt->Format.nChannels) / 8;
1813 fmt->Format.nAvgBytesPerSec = fmt->Format.nSamplesPerSec *
1814 fmt->Format.nBlockAlign;
1816 fmt->Samples.wValidBitsPerSample = fmt->Format.wBitsPerSample;
1817 fmt->Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
1819 dump_fmt((WAVEFORMATEX*)fmt);
1820 *pwfx = (WAVEFORMATEX*)fmt;
1823 LeaveCriticalSection(&This->lock);
1826 HeapFree(GetProcessHeap(), 0, formats);
1831 static HRESULT WINAPI AudioClient_GetDevicePeriod(IAudioClient *iface,
1832 REFERENCE_TIME *defperiod, REFERENCE_TIME *minperiod)
1834 ACImpl *This = impl_from_IAudioClient(iface);
1836 TRACE("(%p)->(%p, %p)\n", This, defperiod, minperiod);
1838 if(!defperiod && !minperiod)
1842 *defperiod = DefaultPeriod;
1844 *minperiod = MinimumPeriod;
1849 static BYTE *remap_channels(ACImpl *This, BYTE *buf, snd_pcm_uframes_t frames)
1851 snd_pcm_uframes_t i;
1853 UINT bytes_per_sample = This->fmt->wBitsPerSample / 8;
1855 if(!This->need_remapping)
1858 if(!This->remapping_buf){
1859 This->remapping_buf = HeapAlloc(GetProcessHeap(), 0,
1860 bytes_per_sample * This->alsa_channels * frames);
1861 This->remapping_buf_frames = frames;
1862 }else if(This->remapping_buf_frames < frames){
1863 This->remapping_buf = HeapReAlloc(GetProcessHeap(), 0, This->remapping_buf,
1864 bytes_per_sample * This->alsa_channels * frames);
1865 This->remapping_buf_frames = frames;
1868 snd_pcm_format_set_silence(This->alsa_format, This->remapping_buf,
1869 frames * This->alsa_channels);
1871 switch(This->fmt->wBitsPerSample){
1873 UINT8 *tgt_buf, *src_buf;
1874 tgt_buf = This->remapping_buf;
1876 for(i = 0; i < frames; ++i){
1877 for(c = 0; c < This->fmt->nChannels; ++c)
1878 tgt_buf[This->alsa_channel_map[c]] = src_buf[c];
1879 tgt_buf += This->alsa_channels;
1880 src_buf += This->fmt->nChannels;
1885 UINT16 *tgt_buf, *src_buf;
1886 tgt_buf = (UINT16*)This->remapping_buf;
1887 src_buf = (UINT16*)buf;
1888 for(i = 0; i < frames; ++i){
1889 for(c = 0; c < This->fmt->nChannels; ++c)
1890 tgt_buf[This->alsa_channel_map[c]] = src_buf[c];
1891 tgt_buf += This->alsa_channels;
1892 src_buf += This->fmt->nChannels;
1897 UINT32 *tgt_buf, *src_buf;
1898 tgt_buf = (UINT32*)This->remapping_buf;
1899 src_buf = (UINT32*)buf;
1900 for(i = 0; i < frames; ++i){
1901 for(c = 0; c < This->fmt->nChannels; ++c)
1902 tgt_buf[This->alsa_channel_map[c]] = src_buf[c];
1903 tgt_buf += This->alsa_channels;
1904 src_buf += This->fmt->nChannels;
1909 BYTE *tgt_buf, *src_buf;
1910 tgt_buf = This->remapping_buf;
1912 for(i = 0; i < frames; ++i){
1913 for(c = 0; c < This->fmt->nChannels; ++c)
1914 memcpy(&tgt_buf[This->alsa_channel_map[c] * bytes_per_sample],
1915 &src_buf[c * bytes_per_sample], bytes_per_sample);
1916 tgt_buf += This->alsa_channels * bytes_per_sample;
1917 src_buf += This->fmt->nChannels * bytes_per_sample;
1923 return This->remapping_buf;
1926 static snd_pcm_sframes_t alsa_write_best_effort(snd_pcm_t *handle, BYTE *buf,
1927 snd_pcm_uframes_t frames, ACImpl *This, BOOL mute)
1929 snd_pcm_sframes_t written;
1933 if((err = snd_pcm_format_set_silence(This->alsa_format, buf,
1934 frames * This->fmt->nChannels)) < 0)
1935 WARN("Setting buffer to silence failed: %d (%s)\n", err,
1939 buf = remap_channels(This, buf, frames);
1941 written = snd_pcm_writei(handle, buf, frames);
1945 if(written == -EAGAIN)
1949 WARN("writei failed, recovering: %ld (%s)\n", written,
1950 snd_strerror(written));
1952 ret = snd_pcm_recover(handle, written, 0);
1954 WARN("Could not recover: %d (%s)\n", ret, snd_strerror(ret));
1958 written = snd_pcm_writei(handle, buf, frames);
1964 /* The callback and mmdevapi API functions execute concurrently.
1965 * Shared state & life time after Start:
1966 * This constant until _Release
1967 *->pcm_handle likewise
1969 *->alsa_format, hidden_frames likewise
1970 *->local_buffer, bufsize_frames, alsa_bufsize_frames likewise
1971 *->event Read Only, even constant until _Release(!)
1972 *->started Read Only from cb POV, constant if _Stop kills the cb
1974 *->held_frames is the only R/W object.
1975 *->lcl_offs_frames/wri_offs_frames are written by one side exclusively:
1976 * lcl_offs_frames by CaptureClient & write callback
1977 * wri_offs_frames by read callback & RenderClient
1979 static void alsa_write_data(ACImpl *This)
1981 snd_pcm_sframes_t written, in_alsa;
1982 snd_pcm_uframes_t to_write, avail, write_limit, max_period;
1985 This->local_buffer + This->lcl_offs_frames * This->fmt->nBlockAlign;
1987 /* this call seems to be required to get an accurate snd_pcm_state() */
1988 avail = snd_pcm_avail_update(This->pcm_handle);
1990 if(snd_pcm_state(This->pcm_handle) == SND_PCM_STATE_XRUN ||
1991 avail > This->alsa_bufsize_frames){
1992 TRACE("XRun state avail %ld, recovering\n", avail);
1994 avail = This->alsa_bufsize_frames;
1996 if((err = snd_pcm_recover(This->pcm_handle, -EPIPE, 1)) < 0)
1997 WARN("snd_pcm_recover failed: %d (%s)\n", err, snd_strerror(err));
1999 if((err = snd_pcm_reset(This->pcm_handle)) < 0)
2000 WARN("snd_pcm_reset failed: %d (%s)\n", err, snd_strerror(err));
2002 if((err = snd_pcm_prepare(This->pcm_handle)) < 0)
2003 WARN("snd_pcm_prepare failed: %d (%s)\n", err, snd_strerror(err));
2005 TRACE("pad: %ld\n", This->alsa_bufsize_frames - avail);
2007 if(This->held_frames == 0)
2010 if(This->lcl_offs_frames + This->held_frames > This->bufsize_frames)
2011 to_write = This->bufsize_frames - This->lcl_offs_frames;
2013 to_write = This->held_frames;
2015 max_period = max(This->mmdev_period_frames, This->alsa_period_frames);
2017 /* try to keep 3 ALSA periods or 3 MMDevAPI periods in the ALSA buffer and
2020 in_alsa = This->alsa_bufsize_frames - avail;
2021 while(in_alsa + write_limit < max_period * 3)
2022 write_limit += max_period;
2023 if(write_limit == 0)
2026 to_write = min(to_write, write_limit);
2028 /* Add a lead-in when starting with too few frames to ensure
2029 * continuous rendering. Additional benefit: Force ALSA to start.
2030 * GetPosition continues to reflect the speaker position because
2031 * snd_pcm_delay includes buffered frames in its total delay
2032 * and last_pos_frames prevents moving backwards. */
2033 if(!in_alsa && This->held_frames < This->hidden_frames){
2034 UINT32 s_frames = This->hidden_frames - This->held_frames;
2035 BYTE *silence = HeapAlloc(GetProcessHeap(), 0,
2036 s_frames * This->fmt->nBlockAlign);
2039 in_alsa = alsa_write_best_effort(This->pcm_handle,
2040 silence, s_frames, This, TRUE);
2041 TRACE("lead-in %ld\n", in_alsa);
2042 HeapFree(GetProcessHeap(), 0, silence);
2046 WARN("Couldn't allocate lead-in, expect underrun\n");
2049 written = alsa_write_best_effort(This->pcm_handle, buf, to_write, This,
2050 This->session->mute);
2052 WARN("Couldn't write: %ld (%s)\n", written, snd_strerror(written));
2056 This->lcl_offs_frames += written;
2057 This->lcl_offs_frames %= This->bufsize_frames;
2058 This->held_frames -= written;
2060 if(written < to_write){
2061 /* ALSA buffer probably full */
2065 if(This->held_frames && (written < write_limit)){
2066 /* wrapped and have some data back at the start to write */
2067 written = alsa_write_best_effort(This->pcm_handle, This->local_buffer,
2068 min(This->held_frames, write_limit - written), This,
2069 This->session->mute);
2071 WARN("Couldn't write: %ld (%s)\n", written, snd_strerror(written));
2075 This->lcl_offs_frames += written;
2076 This->lcl_offs_frames %= This->bufsize_frames;
2077 This->held_frames -= written;
2081 static void alsa_read_data(ACImpl *This)
2083 snd_pcm_sframes_t nread;
2084 UINT32 pos = This->wri_offs_frames, limit = This->held_frames;
2086 /* FIXME: Detect overrun and signal DATA_DISCONTINUITY
2087 * How to count overrun frames and report them as position increase? */
2088 limit = This->bufsize_frames - max(limit, pos);
2090 nread = snd_pcm_readi(This->pcm_handle,
2091 This->local_buffer + pos * This->fmt->nBlockAlign, limit);
2092 TRACE("read %ld from %u limit %u\n", nread, pos, limit);
2096 if(nread == -EAGAIN) /* no data yet */
2099 WARN("read failed, recovering: %ld (%s)\n", nread, snd_strerror(nread));
2101 ret = snd_pcm_recover(This->pcm_handle, nread, 0);
2103 WARN("Recover failed: %d (%s)\n", ret, snd_strerror(ret));
2107 nread = snd_pcm_readi(This->pcm_handle,
2108 This->local_buffer + pos * This->fmt->nBlockAlign, limit);
2110 WARN("read failed: %ld (%s)\n", nread, snd_strerror(nread));
2115 if(This->session->mute){
2117 if((err = snd_pcm_format_set_silence(This->alsa_format,
2118 This->local_buffer + pos * This->fmt->nBlockAlign,
2120 WARN("Setting buffer to silence failed: %d (%s)\n", err,
2124 This->wri_offs_frames += nread;
2125 This->wri_offs_frames %= This->bufsize_frames;
2126 This->held_frames += nread;
2129 static void CALLBACK alsa_push_buffer_data(void *user, BOOLEAN timer)
2131 ACImpl *This = user;
2133 EnterCriticalSection(&This->lock);
2136 if(This->dataflow == eRender)
2137 alsa_write_data(This);
2138 else if(This->dataflow == eCapture)
2139 alsa_read_data(This);
2142 LeaveCriticalSection(&This->lock);
2145 SetEvent(This->event);
2148 static HRESULT WINAPI AudioClient_Start(IAudioClient *iface)
2150 ACImpl *This = impl_from_IAudioClient(iface);
2152 TRACE("(%p)\n", This);
2154 EnterCriticalSection(&This->lock);
2157 LeaveCriticalSection(&This->lock);
2158 return AUDCLNT_E_NOT_INITIALIZED;
2161 if((This->flags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK) && !This->event){
2162 LeaveCriticalSection(&This->lock);
2163 return AUDCLNT_E_EVENTHANDLE_NOT_SET;
2167 LeaveCriticalSection(&This->lock);
2168 return AUDCLNT_E_NOT_STOPPED;
2171 if(This->dataflow == eCapture){
2172 /* dump any data that might be leftover in the ALSA capture buffer */
2173 snd_pcm_readi(This->pcm_handle, This->local_buffer,
2174 This->bufsize_frames);
2177 if(!CreateTimerQueueTimer(&This->timer, g_timer_q, alsa_push_buffer_data,
2178 This, 0, This->mmdev_period_rt / 10000, WT_EXECUTEINTIMERTHREAD)){
2179 LeaveCriticalSection(&This->lock);
2180 WARN("Unable to create timer: %u\n", GetLastError());
2181 return E_OUTOFMEMORY;
2184 This->started = TRUE;
2186 LeaveCriticalSection(&This->lock);
2191 static HRESULT WINAPI AudioClient_Stop(IAudioClient *iface)
2193 ACImpl *This = impl_from_IAudioClient(iface);
2197 TRACE("(%p)\n", This);
2199 EnterCriticalSection(&This->lock);
2202 LeaveCriticalSection(&This->lock);
2203 return AUDCLNT_E_NOT_INITIALIZED;
2207 LeaveCriticalSection(&This->lock);
2211 /* Stop without losing written frames or position.
2212 * snd_pcm_pause would be appropriate but is unsupported by dmix.
2213 * snd_pcm_drain yields EAGAIN in NONBLOCK mode, except with Pulse. */
2215 event = CreateEventW(NULL, TRUE, FALSE, NULL);
2216 wait = !DeleteTimerQueueTimer(g_timer_q, This->timer, event);
2218 WARN("DeleteTimerQueueTimer error %u\n", GetLastError());
2219 wait = wait && GetLastError() == ERROR_IO_PENDING;
2221 This->started = FALSE;
2223 LeaveCriticalSection(&This->lock);
2226 WaitForSingleObject(event, INFINITE);
2232 static HRESULT WINAPI AudioClient_Reset(IAudioClient *iface)
2234 ACImpl *This = impl_from_IAudioClient(iface);
2236 TRACE("(%p)\n", This);
2238 EnterCriticalSection(&This->lock);
2241 LeaveCriticalSection(&This->lock);
2242 return AUDCLNT_E_NOT_INITIALIZED;
2246 LeaveCriticalSection(&This->lock);
2247 return AUDCLNT_E_NOT_STOPPED;
2250 if(This->getbuf_last){
2251 LeaveCriticalSection(&This->lock);
2252 return AUDCLNT_E_BUFFER_OPERATION_PENDING;
2255 if(snd_pcm_drop(This->pcm_handle) < 0)
2256 WARN("snd_pcm_drop failed\n");
2258 if(snd_pcm_reset(This->pcm_handle) < 0)
2259 WARN("snd_pcm_reset failed\n");
2261 if(snd_pcm_prepare(This->pcm_handle) < 0)
2262 WARN("snd_pcm_prepare failed\n");
2264 if(This->dataflow == eRender){
2265 This->written_frames = 0;
2266 This->last_pos_frames = 0;
2268 This->written_frames += This->held_frames;
2270 This->held_frames = 0;
2271 This->lcl_offs_frames = 0;
2272 This->wri_offs_frames = 0;
2274 LeaveCriticalSection(&This->lock);
2279 static HRESULT WINAPI AudioClient_SetEventHandle(IAudioClient *iface,
2282 ACImpl *This = impl_from_IAudioClient(iface);
2284 TRACE("(%p)->(%p)\n", This, event);
2287 return E_INVALIDARG;
2289 EnterCriticalSection(&This->lock);
2292 LeaveCriticalSection(&This->lock);
2293 return AUDCLNT_E_NOT_INITIALIZED;
2296 if(!(This->flags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK)){
2297 LeaveCriticalSection(&This->lock);
2298 return AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED;
2302 LeaveCriticalSection(&This->lock);
2303 FIXME("called twice\n");
2304 return HRESULT_FROM_WIN32(ERROR_INVALID_NAME);
2307 This->event = event;
2309 LeaveCriticalSection(&This->lock);
2314 static HRESULT WINAPI AudioClient_GetService(IAudioClient *iface, REFIID riid,
2317 ACImpl *This = impl_from_IAudioClient(iface);
2319 TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(riid), ppv);
2325 EnterCriticalSection(&This->lock);
2328 LeaveCriticalSection(&This->lock);
2329 return AUDCLNT_E_NOT_INITIALIZED;
2332 if(IsEqualIID(riid, &IID_IAudioRenderClient)){
2333 if(This->dataflow != eRender){
2334 LeaveCriticalSection(&This->lock);
2335 return AUDCLNT_E_WRONG_ENDPOINT_TYPE;
2337 IAudioRenderClient_AddRef(&This->IAudioRenderClient_iface);
2338 *ppv = &This->IAudioRenderClient_iface;
2339 }else if(IsEqualIID(riid, &IID_IAudioCaptureClient)){
2340 if(This->dataflow != eCapture){
2341 LeaveCriticalSection(&This->lock);
2342 return AUDCLNT_E_WRONG_ENDPOINT_TYPE;
2344 IAudioCaptureClient_AddRef(&This->IAudioCaptureClient_iface);
2345 *ppv = &This->IAudioCaptureClient_iface;
2346 }else if(IsEqualIID(riid, &IID_IAudioClock)){
2347 IAudioClock_AddRef(&This->IAudioClock_iface);
2348 *ppv = &This->IAudioClock_iface;
2349 }else if(IsEqualIID(riid, &IID_IAudioStreamVolume)){
2350 IAudioStreamVolume_AddRef(&This->IAudioStreamVolume_iface);
2351 *ppv = &This->IAudioStreamVolume_iface;
2352 }else if(IsEqualIID(riid, &IID_IAudioSessionControl)){
2353 if(!This->session_wrapper){
2354 This->session_wrapper = AudioSessionWrapper_Create(This);
2355 if(!This->session_wrapper){
2356 LeaveCriticalSection(&This->lock);
2357 return E_OUTOFMEMORY;
2360 IAudioSessionControl2_AddRef(&This->session_wrapper->IAudioSessionControl2_iface);
2362 *ppv = &This->session_wrapper->IAudioSessionControl2_iface;
2363 }else if(IsEqualIID(riid, &IID_IChannelAudioVolume)){
2364 if(!This->session_wrapper){
2365 This->session_wrapper = AudioSessionWrapper_Create(This);
2366 if(!This->session_wrapper){
2367 LeaveCriticalSection(&This->lock);
2368 return E_OUTOFMEMORY;
2371 IChannelAudioVolume_AddRef(&This->session_wrapper->IChannelAudioVolume_iface);
2373 *ppv = &This->session_wrapper->IChannelAudioVolume_iface;
2374 }else if(IsEqualIID(riid, &IID_ISimpleAudioVolume)){
2375 if(!This->session_wrapper){
2376 This->session_wrapper = AudioSessionWrapper_Create(This);
2377 if(!This->session_wrapper){
2378 LeaveCriticalSection(&This->lock);
2379 return E_OUTOFMEMORY;
2382 ISimpleAudioVolume_AddRef(&This->session_wrapper->ISimpleAudioVolume_iface);
2384 *ppv = &This->session_wrapper->ISimpleAudioVolume_iface;
2388 LeaveCriticalSection(&This->lock);
2392 LeaveCriticalSection(&This->lock);
2394 FIXME("stub %s\n", debugstr_guid(riid));
2395 return E_NOINTERFACE;
2398 static const IAudioClientVtbl AudioClient_Vtbl =
2400 AudioClient_QueryInterface,
2402 AudioClient_Release,
2403 AudioClient_Initialize,
2404 AudioClient_GetBufferSize,
2405 AudioClient_GetStreamLatency,
2406 AudioClient_GetCurrentPadding,
2407 AudioClient_IsFormatSupported,
2408 AudioClient_GetMixFormat,
2409 AudioClient_GetDevicePeriod,
2413 AudioClient_SetEventHandle,
2414 AudioClient_GetService
2417 static HRESULT WINAPI AudioRenderClient_QueryInterface(
2418 IAudioRenderClient *iface, REFIID riid, void **ppv)
2420 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2426 if(IsEqualIID(riid, &IID_IUnknown) ||
2427 IsEqualIID(riid, &IID_IAudioRenderClient))
2430 IUnknown_AddRef((IUnknown*)*ppv);
2434 WARN("Unknown interface %s\n", debugstr_guid(riid));
2435 return E_NOINTERFACE;
2438 static ULONG WINAPI AudioRenderClient_AddRef(IAudioRenderClient *iface)
2440 ACImpl *This = impl_from_IAudioRenderClient(iface);
2441 return AudioClient_AddRef(&This->IAudioClient_iface);
2444 static ULONG WINAPI AudioRenderClient_Release(IAudioRenderClient *iface)
2446 ACImpl *This = impl_from_IAudioRenderClient(iface);
2447 return AudioClient_Release(&This->IAudioClient_iface);
2450 static HRESULT WINAPI AudioRenderClient_GetBuffer(IAudioRenderClient *iface,
2451 UINT32 frames, BYTE **data)
2453 ACImpl *This = impl_from_IAudioRenderClient(iface);
2456 TRACE("(%p)->(%u, %p)\n", This, frames, data);
2462 EnterCriticalSection(&This->lock);
2464 if(This->getbuf_last){
2465 LeaveCriticalSection(&This->lock);
2466 return AUDCLNT_E_OUT_OF_ORDER;
2470 LeaveCriticalSection(&This->lock);
2474 /* held_frames == GetCurrentPadding_nolock(); */
2475 if(This->held_frames + frames > This->bufsize_frames){
2476 LeaveCriticalSection(&This->lock);
2477 return AUDCLNT_E_BUFFER_TOO_LARGE;
2480 write_pos = This->wri_offs_frames;
2481 if(write_pos + frames > This->bufsize_frames){
2482 if(This->tmp_buffer_frames < frames){
2483 HeapFree(GetProcessHeap(), 0, This->tmp_buffer);
2484 This->tmp_buffer = HeapAlloc(GetProcessHeap(), 0,
2485 frames * This->fmt->nBlockAlign);
2486 if(!This->tmp_buffer){
2487 LeaveCriticalSection(&This->lock);
2488 return E_OUTOFMEMORY;
2490 This->tmp_buffer_frames = frames;
2492 *data = This->tmp_buffer;
2493 This->getbuf_last = -frames;
2495 *data = This->local_buffer + write_pos * This->fmt->nBlockAlign;
2496 This->getbuf_last = frames;
2499 LeaveCriticalSection(&This->lock);
2504 static void alsa_wrap_buffer(ACImpl *This, BYTE *buffer, UINT32 written_frames)
2506 snd_pcm_uframes_t write_offs_frames = This->wri_offs_frames;
2507 UINT32 write_offs_bytes = write_offs_frames * This->fmt->nBlockAlign;
2508 snd_pcm_uframes_t chunk_frames = This->bufsize_frames - write_offs_frames;
2509 UINT32 chunk_bytes = chunk_frames * This->fmt->nBlockAlign;
2510 UINT32 written_bytes = written_frames * This->fmt->nBlockAlign;
2512 if(written_bytes <= chunk_bytes){
2513 memcpy(This->local_buffer + write_offs_bytes, buffer, written_bytes);
2515 memcpy(This->local_buffer + write_offs_bytes, buffer, chunk_bytes);
2516 memcpy(This->local_buffer, buffer + chunk_bytes,
2517 written_bytes - chunk_bytes);
2521 static HRESULT WINAPI AudioRenderClient_ReleaseBuffer(
2522 IAudioRenderClient *iface, UINT32 written_frames, DWORD flags)
2524 ACImpl *This = impl_from_IAudioRenderClient(iface);
2527 TRACE("(%p)->(%u, %x)\n", This, written_frames, flags);
2529 EnterCriticalSection(&This->lock);
2531 if(!written_frames){
2532 This->getbuf_last = 0;
2533 LeaveCriticalSection(&This->lock);
2537 if(!This->getbuf_last){
2538 LeaveCriticalSection(&This->lock);
2539 return AUDCLNT_E_OUT_OF_ORDER;
2542 if(written_frames > (This->getbuf_last >= 0 ? This->getbuf_last : -This->getbuf_last)){
2543 LeaveCriticalSection(&This->lock);
2544 return AUDCLNT_E_INVALID_SIZE;
2547 if(This->getbuf_last >= 0)
2548 buffer = This->local_buffer + This->wri_offs_frames * This->fmt->nBlockAlign;
2550 buffer = This->tmp_buffer;
2552 if(flags & AUDCLNT_BUFFERFLAGS_SILENT){
2553 if(This->fmt->wBitsPerSample == 8)
2554 memset(buffer, 128, written_frames * This->fmt->nBlockAlign);
2556 memset(buffer, 0, written_frames * This->fmt->nBlockAlign);
2559 if(This->getbuf_last < 0)
2560 alsa_wrap_buffer(This, buffer, written_frames);
2562 This->wri_offs_frames += written_frames;
2563 This->wri_offs_frames %= This->bufsize_frames;
2564 This->held_frames += written_frames;
2565 This->written_frames += written_frames;
2566 This->getbuf_last = 0;
2568 LeaveCriticalSection(&This->lock);
2573 static const IAudioRenderClientVtbl AudioRenderClient_Vtbl = {
2574 AudioRenderClient_QueryInterface,
2575 AudioRenderClient_AddRef,
2576 AudioRenderClient_Release,
2577 AudioRenderClient_GetBuffer,
2578 AudioRenderClient_ReleaseBuffer
2581 static HRESULT WINAPI AudioCaptureClient_QueryInterface(
2582 IAudioCaptureClient *iface, REFIID riid, void **ppv)
2584 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2590 if(IsEqualIID(riid, &IID_IUnknown) ||
2591 IsEqualIID(riid, &IID_IAudioCaptureClient))
2594 IUnknown_AddRef((IUnknown*)*ppv);
2598 WARN("Unknown interface %s\n", debugstr_guid(riid));
2599 return E_NOINTERFACE;
2602 static ULONG WINAPI AudioCaptureClient_AddRef(IAudioCaptureClient *iface)
2604 ACImpl *This = impl_from_IAudioCaptureClient(iface);
2605 return IAudioClient_AddRef(&This->IAudioClient_iface);
2608 static ULONG WINAPI AudioCaptureClient_Release(IAudioCaptureClient *iface)
2610 ACImpl *This = impl_from_IAudioCaptureClient(iface);
2611 return IAudioClient_Release(&This->IAudioClient_iface);
2614 static HRESULT WINAPI AudioCaptureClient_GetBuffer(IAudioCaptureClient *iface,
2615 BYTE **data, UINT32 *frames, DWORD *flags, UINT64 *devpos,
2618 ACImpl *This = impl_from_IAudioCaptureClient(iface);
2620 TRACE("(%p)->(%p, %p, %p, %p, %p)\n", This, data, frames, flags,
2623 if(!data || !frames || !flags)
2626 EnterCriticalSection(&This->lock);
2628 if(This->getbuf_last){
2629 LeaveCriticalSection(&This->lock);
2630 return AUDCLNT_E_OUT_OF_ORDER;
2633 /* hr = GetNextPacketSize(iface, frames); */
2634 if(This->held_frames < This->mmdev_period_frames){
2636 LeaveCriticalSection(&This->lock);
2637 return AUDCLNT_S_BUFFER_EMPTY;
2639 *frames = This->mmdev_period_frames;
2641 if(This->lcl_offs_frames + *frames > This->bufsize_frames){
2642 UINT32 chunk_bytes, offs_bytes, frames_bytes;
2643 if(This->tmp_buffer_frames < *frames){
2644 HeapFree(GetProcessHeap(), 0, This->tmp_buffer);
2645 This->tmp_buffer = HeapAlloc(GetProcessHeap(), 0,
2646 *frames * This->fmt->nBlockAlign);
2647 if(!This->tmp_buffer){
2648 LeaveCriticalSection(&This->lock);
2649 return E_OUTOFMEMORY;
2651 This->tmp_buffer_frames = *frames;
2654 *data = This->tmp_buffer;
2655 chunk_bytes = (This->bufsize_frames - This->lcl_offs_frames) *
2656 This->fmt->nBlockAlign;
2657 offs_bytes = This->lcl_offs_frames * This->fmt->nBlockAlign;
2658 frames_bytes = *frames * This->fmt->nBlockAlign;
2659 memcpy(This->tmp_buffer, This->local_buffer + offs_bytes, chunk_bytes);
2660 memcpy(This->tmp_buffer + chunk_bytes, This->local_buffer,
2661 frames_bytes - chunk_bytes);
2663 *data = This->local_buffer +
2664 This->lcl_offs_frames * This->fmt->nBlockAlign;
2666 This->getbuf_last = *frames;
2670 *devpos = This->written_frames;
2671 if(qpcpos){ /* fixme: qpc of recording time */
2672 LARGE_INTEGER stamp, freq;
2673 QueryPerformanceCounter(&stamp);
2674 QueryPerformanceFrequency(&freq);
2675 *qpcpos = (stamp.QuadPart * (INT64)10000000) / freq.QuadPart;
2678 LeaveCriticalSection(&This->lock);
2680 return *frames ? S_OK : AUDCLNT_S_BUFFER_EMPTY;
2683 static HRESULT WINAPI AudioCaptureClient_ReleaseBuffer(
2684 IAudioCaptureClient *iface, UINT32 done)
2686 ACImpl *This = impl_from_IAudioCaptureClient(iface);
2688 TRACE("(%p)->(%u)\n", This, done);
2690 EnterCriticalSection(&This->lock);
2693 This->getbuf_last = 0;
2694 LeaveCriticalSection(&This->lock);
2698 if(!This->getbuf_last){
2699 LeaveCriticalSection(&This->lock);
2700 return AUDCLNT_E_OUT_OF_ORDER;
2703 if(This->getbuf_last != done){
2704 LeaveCriticalSection(&This->lock);
2705 return AUDCLNT_E_INVALID_SIZE;
2708 This->written_frames += done;
2709 This->held_frames -= done;
2710 This->lcl_offs_frames += done;
2711 This->lcl_offs_frames %= This->bufsize_frames;
2712 This->getbuf_last = 0;
2714 LeaveCriticalSection(&This->lock);
2719 static HRESULT WINAPI AudioCaptureClient_GetNextPacketSize(
2720 IAudioCaptureClient *iface, UINT32 *frames)
2722 ACImpl *This = impl_from_IAudioCaptureClient(iface);
2724 TRACE("(%p)->(%p)\n", This, frames);
2729 EnterCriticalSection(&This->lock);
2731 *frames = This->held_frames < This->mmdev_period_frames ? 0 : This->mmdev_period_frames;
2733 LeaveCriticalSection(&This->lock);
2738 static const IAudioCaptureClientVtbl AudioCaptureClient_Vtbl =
2740 AudioCaptureClient_QueryInterface,
2741 AudioCaptureClient_AddRef,
2742 AudioCaptureClient_Release,
2743 AudioCaptureClient_GetBuffer,
2744 AudioCaptureClient_ReleaseBuffer,
2745 AudioCaptureClient_GetNextPacketSize
2748 static HRESULT WINAPI AudioClock_QueryInterface(IAudioClock *iface,
2749 REFIID riid, void **ppv)
2751 ACImpl *This = impl_from_IAudioClock(iface);
2753 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2759 if(IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IAudioClock))
2761 else if(IsEqualIID(riid, &IID_IAudioClock2))
2762 *ppv = &This->IAudioClock2_iface;
2764 IUnknown_AddRef((IUnknown*)*ppv);
2768 WARN("Unknown interface %s\n", debugstr_guid(riid));
2769 return E_NOINTERFACE;
2772 static ULONG WINAPI AudioClock_AddRef(IAudioClock *iface)
2774 ACImpl *This = impl_from_IAudioClock(iface);
2775 return IAudioClient_AddRef(&This->IAudioClient_iface);
2778 static ULONG WINAPI AudioClock_Release(IAudioClock *iface)
2780 ACImpl *This = impl_from_IAudioClock(iface);
2781 return IAudioClient_Release(&This->IAudioClient_iface);
2784 static HRESULT WINAPI AudioClock_GetFrequency(IAudioClock *iface, UINT64 *freq)
2786 ACImpl *This = impl_from_IAudioClock(iface);
2788 TRACE("(%p)->(%p)\n", This, freq);
2790 *freq = This->fmt->nSamplesPerSec;
2795 static HRESULT WINAPI AudioClock_GetPosition(IAudioClock *iface, UINT64 *pos,
2798 ACImpl *This = impl_from_IAudioClock(iface);
2799 UINT64 written_frames, position;
2802 snd_pcm_state_t alsa_state;
2803 snd_pcm_uframes_t avail_frames;
2804 snd_pcm_sframes_t delay_frames;
2806 TRACE("(%p)->(%p, %p)\n", This, pos, qpctime);
2811 EnterCriticalSection(&This->lock);
2813 /* call required to get accurate snd_pcm_state() */
2814 avail_frames = snd_pcm_avail_update(This->pcm_handle);
2815 alsa_state = snd_pcm_state(This->pcm_handle);
2816 written_frames = This->written_frames;
2817 held_frames = This->held_frames;
2819 err = snd_pcm_delay(This->pcm_handle, &delay_frames);
2821 /* old Pulse, shortly after start */
2822 WARN("snd_pcm_delay failed in state %u: %d (%s)\n", alsa_state, err, snd_strerror(err));
2825 if(This->dataflow == eRender){
2826 position = written_frames - held_frames; /* maximum */
2827 if(!This->started || alsa_state > SND_PCM_STATE_RUNNING)
2828 ; /* mmdevapi stopped or ALSA underrun: pretend everything was played */
2829 else if(err<0 || delay_frames > position - This->last_pos_frames)
2830 /* Pulse bug: past underrun, despite recovery, avail_frames & delay
2831 * may be larger than alsa_bufsize_frames, as if cumulating frames. */
2832 /* Pulse bug: EIO(-5) shortly after starting: nothing played */
2833 position = This->last_pos_frames;
2834 else if(delay_frames > 0)
2835 position -= delay_frames;
2837 position = written_frames + held_frames;
2839 /* ensure monotic growth */
2840 This->last_pos_frames = position;
2842 LeaveCriticalSection(&This->lock);
2844 TRACE("frames written: %u, held: %u, avail: %ld, delay: %ld state %d, pos: %u\n",
2845 (UINT32)(written_frames%1000000000), held_frames,
2846 avail_frames, delay_frames, alsa_state, (UINT32)(position%1000000000));
2850 LARGE_INTEGER stamp, freq;
2851 QueryPerformanceCounter(&stamp);
2852 QueryPerformanceFrequency(&freq);
2853 *qpctime = (stamp.QuadPart * (INT64)10000000) / freq.QuadPart;
2859 static HRESULT WINAPI AudioClock_GetCharacteristics(IAudioClock *iface,
2862 ACImpl *This = impl_from_IAudioClock(iface);
2864 TRACE("(%p)->(%p)\n", This, chars);
2869 *chars = AUDIOCLOCK_CHARACTERISTIC_FIXED_FREQ;
2874 static const IAudioClockVtbl AudioClock_Vtbl =
2876 AudioClock_QueryInterface,
2879 AudioClock_GetFrequency,
2880 AudioClock_GetPosition,
2881 AudioClock_GetCharacteristics
2884 static HRESULT WINAPI AudioClock2_QueryInterface(IAudioClock2 *iface,
2885 REFIID riid, void **ppv)
2887 ACImpl *This = impl_from_IAudioClock2(iface);
2888 return IAudioClock_QueryInterface(&This->IAudioClock_iface, riid, ppv);
2891 static ULONG WINAPI AudioClock2_AddRef(IAudioClock2 *iface)
2893 ACImpl *This = impl_from_IAudioClock2(iface);
2894 return IAudioClient_AddRef(&This->IAudioClient_iface);
2897 static ULONG WINAPI AudioClock2_Release(IAudioClock2 *iface)
2899 ACImpl *This = impl_from_IAudioClock2(iface);
2900 return IAudioClient_Release(&This->IAudioClient_iface);
2903 static HRESULT WINAPI AudioClock2_GetDevicePosition(IAudioClock2 *iface,
2904 UINT64 *pos, UINT64 *qpctime)
2906 ACImpl *This = impl_from_IAudioClock2(iface);
2908 FIXME("(%p)->(%p, %p)\n", This, pos, qpctime);
2913 static const IAudioClock2Vtbl AudioClock2_Vtbl =
2915 AudioClock2_QueryInterface,
2917 AudioClock2_Release,
2918 AudioClock2_GetDevicePosition
2921 static AudioSessionWrapper *AudioSessionWrapper_Create(ACImpl *client)
2923 AudioSessionWrapper *ret;
2925 ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
2926 sizeof(AudioSessionWrapper));
2930 ret->IAudioSessionControl2_iface.lpVtbl = &AudioSessionControl2_Vtbl;
2931 ret->ISimpleAudioVolume_iface.lpVtbl = &SimpleAudioVolume_Vtbl;
2932 ret->IChannelAudioVolume_iface.lpVtbl = &ChannelAudioVolume_Vtbl;
2936 ret->client = client;
2938 ret->session = client->session;
2939 AudioClient_AddRef(&client->IAudioClient_iface);
2945 static HRESULT WINAPI AudioSessionControl_QueryInterface(
2946 IAudioSessionControl2 *iface, REFIID riid, void **ppv)
2948 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2954 if(IsEqualIID(riid, &IID_IUnknown) ||
2955 IsEqualIID(riid, &IID_IAudioSessionControl) ||
2956 IsEqualIID(riid, &IID_IAudioSessionControl2))
2959 IUnknown_AddRef((IUnknown*)*ppv);
2963 WARN("Unknown interface %s\n", debugstr_guid(riid));
2964 return E_NOINTERFACE;
2967 static ULONG WINAPI AudioSessionControl_AddRef(IAudioSessionControl2 *iface)
2969 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2971 ref = InterlockedIncrement(&This->ref);
2972 TRACE("(%p) Refcount now %u\n", This, ref);
2976 static ULONG WINAPI AudioSessionControl_Release(IAudioSessionControl2 *iface)
2978 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2980 ref = InterlockedDecrement(&This->ref);
2981 TRACE("(%p) Refcount now %u\n", This, ref);
2984 EnterCriticalSection(&This->client->lock);
2985 This->client->session_wrapper = NULL;
2986 LeaveCriticalSection(&This->client->lock);
2987 AudioClient_Release(&This->client->IAudioClient_iface);
2989 HeapFree(GetProcessHeap(), 0, This);
2994 static HRESULT WINAPI AudioSessionControl_GetState(IAudioSessionControl2 *iface,
2995 AudioSessionState *state)
2997 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
3000 TRACE("(%p)->(%p)\n", This, state);
3003 return NULL_PTR_ERR;
3005 EnterCriticalSection(&g_sessions_lock);
3007 if(list_empty(&This->session->clients)){
3008 *state = AudioSessionStateExpired;
3009 LeaveCriticalSection(&g_sessions_lock);
3013 LIST_FOR_EACH_ENTRY(client, &This->session->clients, ACImpl, entry){
3014 EnterCriticalSection(&client->lock);
3015 if(client->started){
3016 *state = AudioSessionStateActive;
3017 LeaveCriticalSection(&client->lock);
3018 LeaveCriticalSection(&g_sessions_lock);
3021 LeaveCriticalSection(&client->lock);
3024 LeaveCriticalSection(&g_sessions_lock);
3026 *state = AudioSessionStateInactive;
3031 static HRESULT WINAPI AudioSessionControl_GetDisplayName(
3032 IAudioSessionControl2 *iface, WCHAR **name)
3034 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
3036 FIXME("(%p)->(%p) - stub\n", This, name);
3041 static HRESULT WINAPI AudioSessionControl_SetDisplayName(
3042 IAudioSessionControl2 *iface, const WCHAR *name, const GUID *session)
3044 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
3046 FIXME("(%p)->(%p, %s) - stub\n", This, name, debugstr_guid(session));
3051 static HRESULT WINAPI AudioSessionControl_GetIconPath(
3052 IAudioSessionControl2 *iface, WCHAR **path)
3054 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
3056 FIXME("(%p)->(%p) - stub\n", This, path);
3061 static HRESULT WINAPI AudioSessionControl_SetIconPath(
3062 IAudioSessionControl2 *iface, const WCHAR *path, const GUID *session)
3064 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
3066 FIXME("(%p)->(%p, %s) - stub\n", This, path, debugstr_guid(session));
3071 static HRESULT WINAPI AudioSessionControl_GetGroupingParam(
3072 IAudioSessionControl2 *iface, GUID *group)
3074 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
3076 FIXME("(%p)->(%p) - stub\n", This, group);
3081 static HRESULT WINAPI AudioSessionControl_SetGroupingParam(
3082 IAudioSessionControl2 *iface, const GUID *group, const GUID *session)
3084 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
3086 FIXME("(%p)->(%s, %s) - stub\n", This, debugstr_guid(group),
3087 debugstr_guid(session));
3092 static HRESULT WINAPI AudioSessionControl_RegisterAudioSessionNotification(
3093 IAudioSessionControl2 *iface, IAudioSessionEvents *events)
3095 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
3097 FIXME("(%p)->(%p) - stub\n", This, events);
3102 static HRESULT WINAPI AudioSessionControl_UnregisterAudioSessionNotification(
3103 IAudioSessionControl2 *iface, IAudioSessionEvents *events)
3105 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
3107 FIXME("(%p)->(%p) - stub\n", This, events);
3112 static HRESULT WINAPI AudioSessionControl_GetSessionIdentifier(
3113 IAudioSessionControl2 *iface, WCHAR **id)
3115 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
3117 FIXME("(%p)->(%p) - stub\n", This, id);
3122 static HRESULT WINAPI AudioSessionControl_GetSessionInstanceIdentifier(
3123 IAudioSessionControl2 *iface, WCHAR **id)
3125 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
3127 FIXME("(%p)->(%p) - stub\n", This, id);
3132 static HRESULT WINAPI AudioSessionControl_GetProcessId(
3133 IAudioSessionControl2 *iface, DWORD *pid)
3135 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
3137 TRACE("(%p)->(%p)\n", This, pid);
3142 *pid = GetCurrentProcessId();
3147 static HRESULT WINAPI AudioSessionControl_IsSystemSoundsSession(
3148 IAudioSessionControl2 *iface)
3150 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
3152 TRACE("(%p)\n", This);
3157 static HRESULT WINAPI AudioSessionControl_SetDuckingPreference(
3158 IAudioSessionControl2 *iface, BOOL optout)
3160 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
3162 TRACE("(%p)->(%d)\n", This, optout);
3167 static const IAudioSessionControl2Vtbl AudioSessionControl2_Vtbl =
3169 AudioSessionControl_QueryInterface,
3170 AudioSessionControl_AddRef,
3171 AudioSessionControl_Release,
3172 AudioSessionControl_GetState,
3173 AudioSessionControl_GetDisplayName,
3174 AudioSessionControl_SetDisplayName,
3175 AudioSessionControl_GetIconPath,
3176 AudioSessionControl_SetIconPath,
3177 AudioSessionControl_GetGroupingParam,
3178 AudioSessionControl_SetGroupingParam,
3179 AudioSessionControl_RegisterAudioSessionNotification,
3180 AudioSessionControl_UnregisterAudioSessionNotification,
3181 AudioSessionControl_GetSessionIdentifier,
3182 AudioSessionControl_GetSessionInstanceIdentifier,
3183 AudioSessionControl_GetProcessId,
3184 AudioSessionControl_IsSystemSoundsSession,
3185 AudioSessionControl_SetDuckingPreference
3188 static HRESULT WINAPI SimpleAudioVolume_QueryInterface(
3189 ISimpleAudioVolume *iface, REFIID riid, void **ppv)
3191 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
3197 if(IsEqualIID(riid, &IID_IUnknown) ||
3198 IsEqualIID(riid, &IID_ISimpleAudioVolume))
3201 IUnknown_AddRef((IUnknown*)*ppv);
3205 WARN("Unknown interface %s\n", debugstr_guid(riid));
3206 return E_NOINTERFACE;
3209 static ULONG WINAPI SimpleAudioVolume_AddRef(ISimpleAudioVolume *iface)
3211 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
3212 return AudioSessionControl_AddRef(&This->IAudioSessionControl2_iface);
3215 static ULONG WINAPI SimpleAudioVolume_Release(ISimpleAudioVolume *iface)
3217 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
3218 return AudioSessionControl_Release(&This->IAudioSessionControl2_iface);
3221 static HRESULT WINAPI SimpleAudioVolume_SetMasterVolume(
3222 ISimpleAudioVolume *iface, float level, const GUID *context)
3224 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
3225 AudioSession *session = This->session;
3227 TRACE("(%p)->(%f, %s)\n", session, level, wine_dbgstr_guid(context));
3229 if(level < 0.f || level > 1.f)
3230 return E_INVALIDARG;
3233 FIXME("Notifications not supported yet\n");
3235 TRACE("ALSA does not support volume control\n");
3237 EnterCriticalSection(&session->lock);
3239 session->master_vol = level;
3241 LeaveCriticalSection(&session->lock);
3246 static HRESULT WINAPI SimpleAudioVolume_GetMasterVolume(
3247 ISimpleAudioVolume *iface, float *level)
3249 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
3250 AudioSession *session = This->session;
3252 TRACE("(%p)->(%p)\n", session, level);
3255 return NULL_PTR_ERR;
3257 *level = session->master_vol;
3262 static HRESULT WINAPI SimpleAudioVolume_SetMute(ISimpleAudioVolume *iface,
3263 BOOL mute, const GUID *context)
3265 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
3266 AudioSession *session = This->session;
3268 TRACE("(%p)->(%u, %p)\n", session, mute, context);
3271 FIXME("Notifications not supported yet\n");
3273 session->mute = mute;
3278 static HRESULT WINAPI SimpleAudioVolume_GetMute(ISimpleAudioVolume *iface,
3281 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
3282 AudioSession *session = This->session;
3284 TRACE("(%p)->(%p)\n", session, mute);
3287 return NULL_PTR_ERR;
3289 *mute = session->mute;
3294 static const ISimpleAudioVolumeVtbl SimpleAudioVolume_Vtbl =
3296 SimpleAudioVolume_QueryInterface,
3297 SimpleAudioVolume_AddRef,
3298 SimpleAudioVolume_Release,
3299 SimpleAudioVolume_SetMasterVolume,
3300 SimpleAudioVolume_GetMasterVolume,
3301 SimpleAudioVolume_SetMute,
3302 SimpleAudioVolume_GetMute
3305 static HRESULT WINAPI AudioStreamVolume_QueryInterface(
3306 IAudioStreamVolume *iface, REFIID riid, void **ppv)
3308 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
3314 if(IsEqualIID(riid, &IID_IUnknown) ||
3315 IsEqualIID(riid, &IID_IAudioStreamVolume))
3318 IUnknown_AddRef((IUnknown*)*ppv);
3322 WARN("Unknown interface %s\n", debugstr_guid(riid));
3323 return E_NOINTERFACE;
3326 static ULONG WINAPI AudioStreamVolume_AddRef(IAudioStreamVolume *iface)
3328 ACImpl *This = impl_from_IAudioStreamVolume(iface);
3329 return IAudioClient_AddRef(&This->IAudioClient_iface);
3332 static ULONG WINAPI AudioStreamVolume_Release(IAudioStreamVolume *iface)
3334 ACImpl *This = impl_from_IAudioStreamVolume(iface);
3335 return IAudioClient_Release(&This->IAudioClient_iface);
3338 static HRESULT WINAPI AudioStreamVolume_GetChannelCount(
3339 IAudioStreamVolume *iface, UINT32 *out)
3341 ACImpl *This = impl_from_IAudioStreamVolume(iface);
3343 TRACE("(%p)->(%p)\n", This, out);
3348 *out = This->fmt->nChannels;
3353 static HRESULT WINAPI AudioStreamVolume_SetChannelVolume(
3354 IAudioStreamVolume *iface, UINT32 index, float level)
3356 ACImpl *This = impl_from_IAudioStreamVolume(iface);
3358 TRACE("(%p)->(%d, %f)\n", This, index, level);
3360 if(level < 0.f || level > 1.f)
3361 return E_INVALIDARG;
3363 if(index >= This->fmt->nChannels)
3364 return E_INVALIDARG;
3366 TRACE("ALSA does not support volume control\n");
3368 EnterCriticalSection(&This->lock);
3370 This->vols[index] = level;
3372 LeaveCriticalSection(&This->lock);
3377 static HRESULT WINAPI AudioStreamVolume_GetChannelVolume(
3378 IAudioStreamVolume *iface, UINT32 index, float *level)
3380 ACImpl *This = impl_from_IAudioStreamVolume(iface);
3382 TRACE("(%p)->(%d, %p)\n", This, index, level);
3387 if(index >= This->fmt->nChannels)
3388 return E_INVALIDARG;
3390 *level = This->vols[index];
3395 static HRESULT WINAPI AudioStreamVolume_SetAllVolumes(
3396 IAudioStreamVolume *iface, UINT32 count, const float *levels)
3398 ACImpl *This = impl_from_IAudioStreamVolume(iface);
3401 TRACE("(%p)->(%d, %p)\n", This, count, levels);
3406 if(count != This->fmt->nChannels)
3407 return E_INVALIDARG;
3409 TRACE("ALSA does not support volume control\n");
3411 EnterCriticalSection(&This->lock);
3413 for(i = 0; i < count; ++i)
3414 This->vols[i] = levels[i];
3416 LeaveCriticalSection(&This->lock);
3421 static HRESULT WINAPI AudioStreamVolume_GetAllVolumes(
3422 IAudioStreamVolume *iface, UINT32 count, float *levels)
3424 ACImpl *This = impl_from_IAudioStreamVolume(iface);
3427 TRACE("(%p)->(%d, %p)\n", This, count, levels);
3432 if(count != This->fmt->nChannels)
3433 return E_INVALIDARG;
3435 EnterCriticalSection(&This->lock);
3437 for(i = 0; i < count; ++i)
3438 levels[i] = This->vols[i];
3440 LeaveCriticalSection(&This->lock);
3445 static const IAudioStreamVolumeVtbl AudioStreamVolume_Vtbl =
3447 AudioStreamVolume_QueryInterface,
3448 AudioStreamVolume_AddRef,
3449 AudioStreamVolume_Release,
3450 AudioStreamVolume_GetChannelCount,
3451 AudioStreamVolume_SetChannelVolume,
3452 AudioStreamVolume_GetChannelVolume,
3453 AudioStreamVolume_SetAllVolumes,
3454 AudioStreamVolume_GetAllVolumes
3457 static HRESULT WINAPI ChannelAudioVolume_QueryInterface(
3458 IChannelAudioVolume *iface, REFIID riid, void **ppv)
3460 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
3466 if(IsEqualIID(riid, &IID_IUnknown) ||
3467 IsEqualIID(riid, &IID_IChannelAudioVolume))
3470 IUnknown_AddRef((IUnknown*)*ppv);
3474 WARN("Unknown interface %s\n", debugstr_guid(riid));
3475 return E_NOINTERFACE;
3478 static ULONG WINAPI ChannelAudioVolume_AddRef(IChannelAudioVolume *iface)
3480 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
3481 return AudioSessionControl_AddRef(&This->IAudioSessionControl2_iface);
3484 static ULONG WINAPI ChannelAudioVolume_Release(IChannelAudioVolume *iface)
3486 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
3487 return AudioSessionControl_Release(&This->IAudioSessionControl2_iface);
3490 static HRESULT WINAPI ChannelAudioVolume_GetChannelCount(
3491 IChannelAudioVolume *iface, UINT32 *out)
3493 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
3494 AudioSession *session = This->session;
3496 TRACE("(%p)->(%p)\n", session, out);
3499 return NULL_PTR_ERR;
3501 *out = session->channel_count;
3506 static HRESULT WINAPI ChannelAudioVolume_SetChannelVolume(
3507 IChannelAudioVolume *iface, UINT32 index, float level,
3508 const GUID *context)
3510 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
3511 AudioSession *session = This->session;
3513 TRACE("(%p)->(%d, %f, %s)\n", session, index, level,
3514 wine_dbgstr_guid(context));
3516 if(level < 0.f || level > 1.f)
3517 return E_INVALIDARG;
3519 if(index >= session->channel_count)
3520 return E_INVALIDARG;
3523 FIXME("Notifications not supported yet\n");
3525 TRACE("ALSA does not support volume control\n");
3527 EnterCriticalSection(&session->lock);
3529 session->channel_vols[index] = level;
3531 LeaveCriticalSection(&session->lock);
3536 static HRESULT WINAPI ChannelAudioVolume_GetChannelVolume(
3537 IChannelAudioVolume *iface, UINT32 index, float *level)
3539 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
3540 AudioSession *session = This->session;
3542 TRACE("(%p)->(%d, %p)\n", session, index, level);
3545 return NULL_PTR_ERR;
3547 if(index >= session->channel_count)
3548 return E_INVALIDARG;
3550 *level = session->channel_vols[index];
3555 static HRESULT WINAPI ChannelAudioVolume_SetAllVolumes(
3556 IChannelAudioVolume *iface, UINT32 count, const float *levels,
3557 const GUID *context)
3559 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
3560 AudioSession *session = This->session;
3563 TRACE("(%p)->(%d, %p, %s)\n", session, count, levels,
3564 wine_dbgstr_guid(context));
3567 return NULL_PTR_ERR;
3569 if(count != session->channel_count)
3570 return E_INVALIDARG;
3573 FIXME("Notifications not supported yet\n");
3575 TRACE("ALSA does not support volume control\n");
3577 EnterCriticalSection(&session->lock);
3579 for(i = 0; i < count; ++i)
3580 session->channel_vols[i] = levels[i];
3582 LeaveCriticalSection(&session->lock);
3587 static HRESULT WINAPI ChannelAudioVolume_GetAllVolumes(
3588 IChannelAudioVolume *iface, UINT32 count, float *levels)
3590 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
3591 AudioSession *session = This->session;
3594 TRACE("(%p)->(%d, %p)\n", session, count, levels);
3597 return NULL_PTR_ERR;
3599 if(count != session->channel_count)
3600 return E_INVALIDARG;
3602 for(i = 0; i < count; ++i)
3603 levels[i] = session->channel_vols[i];
3608 static const IChannelAudioVolumeVtbl ChannelAudioVolume_Vtbl =
3610 ChannelAudioVolume_QueryInterface,
3611 ChannelAudioVolume_AddRef,
3612 ChannelAudioVolume_Release,
3613 ChannelAudioVolume_GetChannelCount,
3614 ChannelAudioVolume_SetChannelVolume,
3615 ChannelAudioVolume_GetChannelVolume,
3616 ChannelAudioVolume_SetAllVolumes,
3617 ChannelAudioVolume_GetAllVolumes
3620 static HRESULT WINAPI AudioSessionManager_QueryInterface(IAudioSessionManager2 *iface,
3621 REFIID riid, void **ppv)
3623 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
3629 if(IsEqualIID(riid, &IID_IUnknown) ||
3630 IsEqualIID(riid, &IID_IAudioSessionManager) ||
3631 IsEqualIID(riid, &IID_IAudioSessionManager2))
3634 IUnknown_AddRef((IUnknown*)*ppv);
3638 WARN("Unknown interface %s\n", debugstr_guid(riid));
3639 return E_NOINTERFACE;
3642 static ULONG WINAPI AudioSessionManager_AddRef(IAudioSessionManager2 *iface)
3644 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3646 ref = InterlockedIncrement(&This->ref);
3647 TRACE("(%p) Refcount now %u\n", This, ref);
3651 static ULONG WINAPI AudioSessionManager_Release(IAudioSessionManager2 *iface)
3653 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3655 ref = InterlockedDecrement(&This->ref);
3656 TRACE("(%p) Refcount now %u\n", This, ref);
3658 HeapFree(GetProcessHeap(), 0, This);
3662 static HRESULT WINAPI AudioSessionManager_GetAudioSessionControl(
3663 IAudioSessionManager2 *iface, const GUID *session_guid, DWORD flags,
3664 IAudioSessionControl **out)
3666 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3667 AudioSession *session;
3668 AudioSessionWrapper *wrapper;
3671 TRACE("(%p)->(%s, %x, %p)\n", This, debugstr_guid(session_guid),
3674 hr = get_audio_session(session_guid, This->device, 0, &session);
3678 wrapper = AudioSessionWrapper_Create(NULL);
3680 return E_OUTOFMEMORY;
3682 wrapper->session = session;
3684 *out = (IAudioSessionControl*)&wrapper->IAudioSessionControl2_iface;
3689 static HRESULT WINAPI AudioSessionManager_GetSimpleAudioVolume(
3690 IAudioSessionManager2 *iface, const GUID *session_guid, DWORD flags,
3691 ISimpleAudioVolume **out)
3693 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3694 AudioSession *session;
3695 AudioSessionWrapper *wrapper;
3698 TRACE("(%p)->(%s, %x, %p)\n", This, debugstr_guid(session_guid),
3701 hr = get_audio_session(session_guid, This->device, 0, &session);
3705 wrapper = AudioSessionWrapper_Create(NULL);
3707 return E_OUTOFMEMORY;
3709 wrapper->session = session;
3711 *out = &wrapper->ISimpleAudioVolume_iface;
3716 static HRESULT WINAPI AudioSessionManager_GetSessionEnumerator(
3717 IAudioSessionManager2 *iface, IAudioSessionEnumerator **out)
3719 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3720 FIXME("(%p)->(%p) - stub\n", This, out);
3724 static HRESULT WINAPI AudioSessionManager_RegisterSessionNotification(
3725 IAudioSessionManager2 *iface, IAudioSessionNotification *notification)
3727 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3728 FIXME("(%p)->(%p) - stub\n", This, notification);
3732 static HRESULT WINAPI AudioSessionManager_UnregisterSessionNotification(
3733 IAudioSessionManager2 *iface, IAudioSessionNotification *notification)
3735 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3736 FIXME("(%p)->(%p) - stub\n", This, notification);
3740 static HRESULT WINAPI AudioSessionManager_RegisterDuckNotification(
3741 IAudioSessionManager2 *iface, const WCHAR *session_id,
3742 IAudioVolumeDuckNotification *notification)
3744 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3745 FIXME("(%p)->(%p) - stub\n", This, notification);
3749 static HRESULT WINAPI AudioSessionManager_UnregisterDuckNotification(
3750 IAudioSessionManager2 *iface,
3751 IAudioVolumeDuckNotification *notification)
3753 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3754 FIXME("(%p)->(%p) - stub\n", This, notification);
3758 static const IAudioSessionManager2Vtbl AudioSessionManager2_Vtbl =
3760 AudioSessionManager_QueryInterface,
3761 AudioSessionManager_AddRef,
3762 AudioSessionManager_Release,
3763 AudioSessionManager_GetAudioSessionControl,
3764 AudioSessionManager_GetSimpleAudioVolume,
3765 AudioSessionManager_GetSessionEnumerator,
3766 AudioSessionManager_RegisterSessionNotification,
3767 AudioSessionManager_UnregisterSessionNotification,
3768 AudioSessionManager_RegisterDuckNotification,
3769 AudioSessionManager_UnregisterDuckNotification
3772 HRESULT WINAPI AUDDRV_GetAudioSessionManager(IMMDevice *device,
3773 IAudioSessionManager2 **out)
3777 This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(SessionMgr));
3779 return E_OUTOFMEMORY;
3781 This->IAudioSessionManager2_iface.lpVtbl = &AudioSessionManager2_Vtbl;
3782 This->device = device;
3785 *out = &This->IAudioSessionManager2_iface;