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 hidden_frames; /* ALSA reserve to ensure continuous rendering */
124 BYTE *local_buffer, *tmp_buffer, *remapping_buf;
125 LONG32 getbuf_last; /* <0 when using tmp_buffer */
127 CRITICAL_SECTION lock;
129 AudioSession *session;
130 AudioSessionWrapper *session_wrapper;
135 typedef struct _SessionMgr {
136 IAudioSessionManager2 IAudioSessionManager2_iface;
143 static HANDLE g_timer_q;
145 static CRITICAL_SECTION g_sessions_lock;
146 static CRITICAL_SECTION_DEBUG g_sessions_lock_debug =
148 0, 0, &g_sessions_lock,
149 { &g_sessions_lock_debug.ProcessLocksList, &g_sessions_lock_debug.ProcessLocksList },
150 0, 0, { (DWORD_PTR)(__FILE__ ": g_sessions_lock") }
152 static CRITICAL_SECTION g_sessions_lock = { &g_sessions_lock_debug, -1, 0, 0, 0, 0 };
153 static struct list g_sessions = LIST_INIT(g_sessions);
155 static const WCHAR defaultW[] = {'d','e','f','a','u','l','t',0};
156 static const char defname[] = "default";
158 static const WCHAR drv_keyW[] = {'S','o','f','t','w','a','r','e','\\',
159 'W','i','n','e','\\','D','r','i','v','e','r','s','\\',
160 'w','i','n','e','a','l','s','a','.','d','r','v',0};
161 static const WCHAR drv_key_devicesW[] = {'S','o','f','t','w','a','r','e','\\',
162 'W','i','n','e','\\','D','r','i','v','e','r','s','\\',
163 'w','i','n','e','a','l','s','a','.','d','r','v','\\','d','e','v','i','c','e','s',0};
164 static const WCHAR guidW[] = {'g','u','i','d',0};
166 static const IAudioClientVtbl AudioClient_Vtbl;
167 static const IAudioRenderClientVtbl AudioRenderClient_Vtbl;
168 static const IAudioCaptureClientVtbl AudioCaptureClient_Vtbl;
169 static const IAudioSessionControl2Vtbl AudioSessionControl2_Vtbl;
170 static const ISimpleAudioVolumeVtbl SimpleAudioVolume_Vtbl;
171 static const IAudioClockVtbl AudioClock_Vtbl;
172 static const IAudioClock2Vtbl AudioClock2_Vtbl;
173 static const IAudioStreamVolumeVtbl AudioStreamVolume_Vtbl;
174 static const IChannelAudioVolumeVtbl ChannelAudioVolume_Vtbl;
175 static const IAudioSessionManager2Vtbl AudioSessionManager2_Vtbl;
177 static AudioSessionWrapper *AudioSessionWrapper_Create(ACImpl *client);
179 static inline ACImpl *impl_from_IAudioClient(IAudioClient *iface)
181 return CONTAINING_RECORD(iface, ACImpl, IAudioClient_iface);
184 static inline ACImpl *impl_from_IAudioRenderClient(IAudioRenderClient *iface)
186 return CONTAINING_RECORD(iface, ACImpl, IAudioRenderClient_iface);
189 static inline ACImpl *impl_from_IAudioCaptureClient(IAudioCaptureClient *iface)
191 return CONTAINING_RECORD(iface, ACImpl, IAudioCaptureClient_iface);
194 static inline AudioSessionWrapper *impl_from_IAudioSessionControl2(IAudioSessionControl2 *iface)
196 return CONTAINING_RECORD(iface, AudioSessionWrapper, IAudioSessionControl2_iface);
199 static inline AudioSessionWrapper *impl_from_ISimpleAudioVolume(ISimpleAudioVolume *iface)
201 return CONTAINING_RECORD(iface, AudioSessionWrapper, ISimpleAudioVolume_iface);
204 static inline AudioSessionWrapper *impl_from_IChannelAudioVolume(IChannelAudioVolume *iface)
206 return CONTAINING_RECORD(iface, AudioSessionWrapper, IChannelAudioVolume_iface);
209 static inline ACImpl *impl_from_IAudioClock(IAudioClock *iface)
211 return CONTAINING_RECORD(iface, ACImpl, IAudioClock_iface);
214 static inline ACImpl *impl_from_IAudioClock2(IAudioClock2 *iface)
216 return CONTAINING_RECORD(iface, ACImpl, IAudioClock2_iface);
219 static inline ACImpl *impl_from_IAudioStreamVolume(IAudioStreamVolume *iface)
221 return CONTAINING_RECORD(iface, ACImpl, IAudioStreamVolume_iface);
224 static inline SessionMgr *impl_from_IAudioSessionManager2(IAudioSessionManager2 *iface)
226 return CONTAINING_RECORD(iface, SessionMgr, IAudioSessionManager2_iface);
229 BOOL WINAPI DllMain(HINSTANCE dll, DWORD reason, void *reserved)
233 case DLL_PROCESS_ATTACH:
234 g_timer_q = CreateTimerQueue();
239 case DLL_PROCESS_DETACH:
240 DeleteCriticalSection(&g_sessions_lock);
246 /* From <dlls/mmdevapi/mmdevapi.h> */
247 enum DriverPriority {
248 Priority_Unavailable = 0,
254 int WINAPI AUDDRV_GetPriority(void)
256 return Priority_Neutral;
259 static void set_device_guid(EDataFlow flow, HKEY drv_key, const WCHAR *key_name,
267 lr = RegCreateKeyExW(HKEY_CURRENT_USER, drv_key_devicesW, 0, NULL, 0, KEY_WRITE,
268 NULL, &drv_key, NULL);
269 if(lr != ERROR_SUCCESS){
270 ERR("RegCreateKeyEx(drv_key) failed: %u\n", lr);
276 lr = RegCreateKeyExW(drv_key, key_name, 0, NULL, 0, KEY_WRITE,
278 if(lr != ERROR_SUCCESS){
279 ERR("RegCreateKeyEx(%s) failed: %u\n", wine_dbgstr_w(key_name), lr);
283 lr = RegSetValueExW(key, guidW, 0, REG_BINARY, (BYTE*)guid,
285 if(lr != ERROR_SUCCESS)
286 ERR("RegSetValueEx(%s\\guid) failed: %u\n", wine_dbgstr_w(key_name), lr);
291 RegCloseKey(drv_key);
294 static void get_device_guid(EDataFlow flow, const char *device, GUID *guid)
296 HKEY key = NULL, dev_key;
297 DWORD type, size = sizeof(*guid);
305 MultiByteToWideChar(CP_UNIXCP, 0, device, -1, key_name + 2,
306 (sizeof(key_name) / sizeof(*key_name)) - 2);
308 if(RegOpenKeyExW(HKEY_CURRENT_USER, drv_key_devicesW, 0, KEY_WRITE|KEY_READ, &key) == ERROR_SUCCESS){
309 if(RegOpenKeyExW(key, key_name, 0, KEY_READ, &dev_key) == ERROR_SUCCESS){
310 if(RegQueryValueExW(dev_key, guidW, 0, &type,
311 (BYTE*)guid, &size) == ERROR_SUCCESS){
312 if(type == REG_BINARY){
313 RegCloseKey(dev_key);
317 ERR("Invalid type for device %s GUID: %u; ignoring and overwriting\n",
318 wine_dbgstr_w(key_name), type);
320 RegCloseKey(dev_key);
326 set_device_guid(flow, key, key_name, guid);
332 static BOOL alsa_try_open(const char *devnode, snd_pcm_stream_t stream)
337 TRACE("devnode: %s, stream: %d\n", devnode, stream);
339 if((err = snd_pcm_open(&handle, devnode, stream, SND_PCM_NONBLOCK)) < 0){
340 WARN("The device \"%s\" failed to open: %d (%s).\n",
341 devnode, err, snd_strerror(err));
345 snd_pcm_close(handle);
349 static WCHAR *construct_device_id(EDataFlow flow, const WCHAR *chunk1, const char *chunk2)
353 DWORD len_wchars = 0, chunk1_len, copied = 0, prefix_len;
355 static const WCHAR dashW[] = {' ','-',' ',0};
356 static const size_t dashW_len = (sizeof(dashW) / sizeof(*dashW)) - 1;
357 static const WCHAR outW[] = {'O','u','t',':',' ',0};
358 static const WCHAR inW[] = {'I','n',':',' ',0};
362 prefix_len = (sizeof(outW) / sizeof(*outW)) - 1;
363 len_wchars += prefix_len;
366 prefix_len = (sizeof(inW) / sizeof(*inW)) - 1;
367 len_wchars += prefix_len;
370 chunk1_len = strlenW(chunk1);
371 len_wchars += chunk1_len;
374 len_wchars += dashW_len;
376 len_wchars += MultiByteToWideChar(CP_UNIXCP, 0, chunk2, -1, NULL, 0) - 1;
377 len_wchars += 1; /* NULL byte */
379 ret = HeapAlloc(GetProcessHeap(), 0, len_wchars * sizeof(WCHAR));
381 memcpy(ret, prefix, prefix_len * sizeof(WCHAR));
382 copied += prefix_len;
384 memcpy(ret + copied, chunk1, chunk1_len * sizeof(WCHAR));
385 copied += chunk1_len;
387 if(chunk1 && chunk2){
388 memcpy(ret + copied, dashW, dashW_len * sizeof(WCHAR));
392 MultiByteToWideChar(CP_UNIXCP, 0, chunk2, -1, ret + copied, len_wchars - copied);
396 TRACE("Enumerated device: %s\n", wine_dbgstr_w(ret));
401 static HRESULT alsa_get_card_devices(EDataFlow flow, snd_pcm_stream_t stream,
402 WCHAR ***ids, GUID **guids, UINT *num, snd_ctl_t *ctl, int card,
403 const WCHAR *cardnameW)
406 snd_pcm_info_t *info;
408 info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, snd_pcm_info_sizeof());
410 return E_OUTOFMEMORY;
412 snd_pcm_info_set_subdevice(info, 0);
413 snd_pcm_info_set_stream(info, stream);
416 for(err = snd_ctl_pcm_next_device(ctl, &device); device != -1 && err >= 0;
417 err = snd_ctl_pcm_next_device(ctl, &device)){
421 snd_pcm_info_set_device(info, device);
423 if((err = snd_ctl_pcm_info(ctl, info)) < 0){
425 /* This device doesn't have the right stream direction */
428 WARN("Failed to get info for card %d, device %d: %d (%s)\n",
429 card, device, err, snd_strerror(err));
433 sprintf(devnode, "plughw:%d,%d", card, device);
434 if(!alsa_try_open(devnode, stream))
438 *ids = HeapReAlloc(GetProcessHeap(), 0, *ids, sizeof(WCHAR *) * (*num + 1));
439 *guids = HeapReAlloc(GetProcessHeap(), 0, *guids, sizeof(GUID) * (*num + 1));
441 *ids = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR *));
442 *guids = HeapAlloc(GetProcessHeap(), 0, sizeof(GUID));
445 devname = snd_pcm_info_get_name(info);
447 WARN("Unable to get device name for card %d, device %d\n", card,
452 (*ids)[*num] = construct_device_id(flow, cardnameW, devname);
453 get_device_guid(flow, devnode, &(*guids)[*num]);
458 HeapFree(GetProcessHeap(), 0, info);
461 WARN("Got a failure during device enumeration on card %d: %d (%s)\n",
462 card, err, snd_strerror(err));
467 static void get_reg_devices(EDataFlow flow, snd_pcm_stream_t stream, WCHAR ***ids,
468 GUID **guids, UINT *num)
470 static const WCHAR ALSAOutputDevices[] = {'A','L','S','A','O','u','t','p','u','t','D','e','v','i','c','e','s',0};
471 static const WCHAR ALSAInputDevices[] = {'A','L','S','A','I','n','p','u','t','D','e','v','i','c','e','s',0};
473 WCHAR reg_devices[256];
474 DWORD size = sizeof(reg_devices), type;
475 const WCHAR *value_name = (stream == SND_PCM_STREAM_PLAYBACK) ? ALSAOutputDevices : ALSAInputDevices;
477 /* @@ Wine registry key: HKCU\Software\Wine\Drivers\winealsa.drv */
478 if(RegOpenKeyW(HKEY_CURRENT_USER, drv_keyW, &key) == ERROR_SUCCESS){
479 if(RegQueryValueExW(key, value_name, 0, &type,
480 (BYTE*)reg_devices, &size) == ERROR_SUCCESS){
481 WCHAR *p = reg_devices;
483 if(type != REG_MULTI_SZ){
484 ERR("Registry ALSA device list value type must be REG_MULTI_SZ\n");
492 WideCharToMultiByte(CP_UNIXCP, 0, p, -1, devname, sizeof(devname), NULL, NULL);
494 if(alsa_try_open(devname, stream)){
496 *ids = HeapReAlloc(GetProcessHeap(), 0, *ids, sizeof(WCHAR *) * (*num + 1));
497 *guids = HeapReAlloc(GetProcessHeap(), 0, *guids, sizeof(GUID) * (*num + 1));
499 *ids = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR *));
500 *guids = HeapAlloc(GetProcessHeap(), 0, sizeof(GUID));
502 (*ids)[*num] = construct_device_id(flow, p, NULL);
503 get_device_guid(flow, devname, &(*guids)[*num]);
507 p += lstrlenW(p) + 1;
515 static HRESULT alsa_enum_devices(EDataFlow flow, WCHAR ***ids, GUID **guids,
518 snd_pcm_stream_t stream = (flow == eRender ? SND_PCM_STREAM_PLAYBACK :
519 SND_PCM_STREAM_CAPTURE);
525 if(alsa_try_open(defname, stream)){
526 *ids = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR *));
527 (*ids)[0] = construct_device_id(flow, defaultW, NULL);
528 *guids = HeapAlloc(GetProcessHeap(), 0, sizeof(GUID));
529 get_device_guid(flow, defname, &(*guids)[0]);
533 get_reg_devices(flow, stream, ids, guids, num);
535 for(err = snd_card_next(&card); card != -1 && err >= 0;
536 err = snd_card_next(&card)){
543 sprintf(cardpath, "hw:%u", card);
545 if((err = snd_ctl_open(&ctl, cardpath, 0)) < 0){
546 WARN("Unable to open ctl for ALSA device %s: %d (%s)\n", cardpath,
547 err, snd_strerror(err));
551 if(snd_card_get_name(card, &cardname) < 0) {
552 /* FIXME: Should be localized */
553 static const WCHAR nameW[] = {'U','n','k','n','o','w','n',' ','s','o','u','n','d','c','a','r','d',0};
554 WARN("Unable to get card name for ALSA device %s: %d (%s)\n",
555 cardpath, err, snd_strerror(err));
556 alsa_get_card_devices(flow, stream, ids, guids, num, ctl, card, nameW);
558 len = MultiByteToWideChar(CP_UNIXCP, 0, cardname, -1, NULL, 0);
559 cardnameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
564 return E_OUTOFMEMORY;
566 MultiByteToWideChar(CP_UNIXCP, 0, cardname, -1, cardnameW, len);
568 alsa_get_card_devices(flow, stream, ids, guids, num, ctl, card, cardnameW);
570 HeapFree(GetProcessHeap(), 0, cardnameW);
578 WARN("Got a failure during card enumeration: %d (%s)\n",
579 err, snd_strerror(err));
584 HRESULT WINAPI AUDDRV_GetEndpointIDs(EDataFlow flow, WCHAR ***ids, GUID **guids,
585 UINT *num, UINT *def_index)
589 TRACE("%d %p %p %p %p\n", flow, ids, guids, num, def_index);
594 hr = alsa_enum_devices(flow, ids, guids, num);
597 for(i = 0; i < *num; ++i)
598 HeapFree(GetProcessHeap(), 0, (*ids)[i]);
599 HeapFree(GetProcessHeap(), 0, *ids);
600 HeapFree(GetProcessHeap(), 0, *guids);
601 return E_OUTOFMEMORY;
604 TRACE("Enumerated %u devices\n", *num);
607 HeapFree(GetProcessHeap(), 0, *ids);
609 HeapFree(GetProcessHeap(), 0, *guids);
618 /* Using the pulse PCM device from alsa-plugins 1.0.24 triggers a bug
619 * which causes audio to cease playing after a few minutes of playback.
620 * Setting handle_underrun=1 on pulse-backed ALSA devices seems to work
621 * around this issue. */
622 static snd_config_t *make_handle_underrun_config(const char *name)
624 snd_config_t *lconf, *dev_node, *hu_node, *type_node;
625 char dev_node_name[64];
626 const char *type_str;
631 if((err = snd_config_copy(&lconf, snd_config)) < 0){
632 WARN("snd_config_copy failed: %d (%s)\n", err, snd_strerror(err));
636 sprintf(dev_node_name, "pcm.%s", name);
637 err = snd_config_search(lconf, dev_node_name, &dev_node);
639 snd_config_delete(lconf);
643 snd_config_delete(lconf);
644 WARN("snd_config_search failed: %d (%s)\n", err, snd_strerror(err));
648 /* ALSA is extremely fragile. If it runs into a config setting it doesn't
649 * recognize, it tends to fail or assert. So we only want to inject
650 * handle_underrun=1 on devices that we know will recognize it. */
651 err = snd_config_search(dev_node, "type", &type_node);
653 snd_config_delete(lconf);
657 snd_config_delete(lconf);
658 WARN("snd_config_search failed: %d (%s)\n", err, snd_strerror(err));
662 if((err = snd_config_get_string(type_node, &type_str)) < 0){
663 snd_config_delete(lconf);
667 if(strcmp(type_str, "pulse") != 0){
668 snd_config_delete(lconf);
672 err = snd_config_search(dev_node, "handle_underrun", &hu_node);
674 /* user already has an explicit handle_underrun setting, so don't
675 * use a local config */
676 snd_config_delete(lconf);
680 snd_config_delete(lconf);
681 WARN("snd_config_search failed: %d (%s)\n", err, snd_strerror(err));
685 if((err = snd_config_imake_integer(&hu_node, "handle_underrun", 1)) < 0){
686 snd_config_delete(lconf);
687 WARN("snd_config_imake_integer failed: %d (%s)\n", err,
692 if((err = snd_config_add(dev_node, hu_node)) < 0){
693 snd_config_delete(lconf);
694 WARN("snd_config_add failed: %d (%s)\n", err, snd_strerror(err));
701 static BOOL get_alsa_name_by_guid(GUID *guid, char *name, DWORD name_size, EDataFlow *flow)
708 if(RegOpenKeyExW(HKEY_CURRENT_USER, drv_key_devicesW, 0, KEY_READ, &devices_key) != ERROR_SUCCESS){
709 ERR("No devices found in registry?\n");
718 key_name_size = sizeof(key_name)/sizeof(WCHAR);
719 if(RegEnumKeyExW(devices_key, i, key_name, &key_name_size, NULL,
720 NULL, NULL, NULL) != ERROR_SUCCESS)
723 if(RegOpenKeyExW(devices_key, key_name, 0, KEY_READ, &key) != ERROR_SUCCESS){
724 WARN("Couldn't open key: %s\n", wine_dbgstr_w(key_name));
728 size = sizeof(reg_guid);
729 if(RegQueryValueExW(key, guidW, 0, &type,
730 (BYTE*)®_guid, &size) == ERROR_SUCCESS){
731 if(IsEqualGUID(®_guid, guid)){
733 RegCloseKey(devices_key);
735 TRACE("Found matching device key: %s\n", wine_dbgstr_w(key_name));
737 if(key_name[0] == '0')
739 else if(key_name[0] == '1')
742 ERR("Unknown device type: %c\n", key_name[0]);
746 WideCharToMultiByte(CP_UNIXCP, 0, key_name + 2, -1, name, name_size, NULL, NULL);
757 RegCloseKey(devices_key);
759 WARN("No matching device in registry for GUID %s\n", debugstr_guid(guid));
764 HRESULT WINAPI AUDDRV_GetAudioEndpoint(GUID *guid, IMMDevice *dev, IAudioClient **out)
768 snd_pcm_stream_t stream;
770 static int handle_underrun = 1;
774 TRACE("%s %p %p\n", debugstr_guid(guid), dev, out);
776 if(!get_alsa_name_by_guid(guid, alsa_name, sizeof(alsa_name), &dataflow))
777 return AUDCLNT_E_DEVICE_INVALIDATED;
779 This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(ACImpl));
781 return E_OUTOFMEMORY;
783 This->IAudioClient_iface.lpVtbl = &AudioClient_Vtbl;
784 This->IAudioRenderClient_iface.lpVtbl = &AudioRenderClient_Vtbl;
785 This->IAudioCaptureClient_iface.lpVtbl = &AudioCaptureClient_Vtbl;
786 This->IAudioClock_iface.lpVtbl = &AudioClock_Vtbl;
787 This->IAudioClock2_iface.lpVtbl = &AudioClock2_Vtbl;
788 This->IAudioStreamVolume_iface.lpVtbl = &AudioStreamVolume_Vtbl;
790 if(dataflow == eRender)
791 stream = SND_PCM_STREAM_PLAYBACK;
792 else if(dataflow == eCapture)
793 stream = SND_PCM_STREAM_CAPTURE;
795 HeapFree(GetProcessHeap(), 0, This);
799 This->dataflow = dataflow;
800 if(handle_underrun && ((lconf = make_handle_underrun_config(alsa_name)))){
801 err = snd_pcm_open_lconf(&This->pcm_handle, alsa_name, stream, SND_PCM_NONBLOCK, lconf);
802 TRACE("Opening PCM device \"%s\" with handle_underrun: %d\n", alsa_name, err);
803 snd_config_delete(lconf);
804 /* Pulse <= 2010 returns EINVAL, it does not know handle_underrun. */
806 ERR_(winediag)("PulseAudio \"%s\" %d without handle_underrun. Audio may hang."
807 " Please upgrade to alsa_plugins >= 1.0.24\n", alsa_name, err);
813 err = snd_pcm_open(&This->pcm_handle, alsa_name, stream, SND_PCM_NONBLOCK);
816 HeapFree(GetProcessHeap(), 0, This);
817 WARN("Unable to open PCM \"%s\": %d (%s)\n", alsa_name, err, snd_strerror(err));
820 return AUDCLNT_E_DEVICE_IN_USE;
822 return AUDCLNT_E_ENDPOINT_CREATE_FAILED;
826 This->hw_params = HeapAlloc(GetProcessHeap(), 0,
827 snd_pcm_hw_params_sizeof());
828 if(!This->hw_params){
829 snd_pcm_close(This->pcm_handle);
830 HeapFree(GetProcessHeap(), 0, This);
831 return E_OUTOFMEMORY;
834 InitializeCriticalSection(&This->lock);
835 This->lock.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": ACImpl.lock");
838 IMMDevice_AddRef(This->parent);
840 *out = &This->IAudioClient_iface;
841 IAudioClient_AddRef(&This->IAudioClient_iface);
846 static HRESULT WINAPI AudioClient_QueryInterface(IAudioClient *iface,
847 REFIID riid, void **ppv)
849 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
854 if(IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IAudioClient))
857 IUnknown_AddRef((IUnknown*)*ppv);
860 WARN("Unknown interface %s\n", debugstr_guid(riid));
861 return E_NOINTERFACE;
864 static ULONG WINAPI AudioClient_AddRef(IAudioClient *iface)
866 ACImpl *This = impl_from_IAudioClient(iface);
868 ref = InterlockedIncrement(&This->ref);
869 TRACE("(%p) Refcount now %u\n", This, ref);
873 static ULONG WINAPI AudioClient_Release(IAudioClient *iface)
875 ACImpl *This = impl_from_IAudioClient(iface);
877 ref = InterlockedDecrement(&This->ref);
878 TRACE("(%p) Refcount now %u\n", This, ref);
880 IAudioClient_Stop(iface);
881 IMMDevice_Release(This->parent);
882 This->lock.DebugInfo->Spare[0] = 0;
883 DeleteCriticalSection(&This->lock);
884 snd_pcm_drop(This->pcm_handle);
885 snd_pcm_close(This->pcm_handle);
887 EnterCriticalSection(&g_sessions_lock);
888 list_remove(&This->entry);
889 LeaveCriticalSection(&g_sessions_lock);
891 HeapFree(GetProcessHeap(), 0, This->vols);
892 HeapFree(GetProcessHeap(), 0, This->local_buffer);
893 HeapFree(GetProcessHeap(), 0, This->remapping_buf);
894 HeapFree(GetProcessHeap(), 0, This->tmp_buffer);
895 HeapFree(GetProcessHeap(), 0, This->hw_params);
896 CoTaskMemFree(This->fmt);
897 HeapFree(GetProcessHeap(), 0, This);
902 static void dump_fmt(const WAVEFORMATEX *fmt)
904 TRACE("wFormatTag: 0x%x (", fmt->wFormatTag);
905 switch(fmt->wFormatTag){
906 case WAVE_FORMAT_PCM:
907 TRACE("WAVE_FORMAT_PCM");
909 case WAVE_FORMAT_IEEE_FLOAT:
910 TRACE("WAVE_FORMAT_IEEE_FLOAT");
912 case WAVE_FORMAT_EXTENSIBLE:
913 TRACE("WAVE_FORMAT_EXTENSIBLE");
921 TRACE("nChannels: %u\n", fmt->nChannels);
922 TRACE("nSamplesPerSec: %u\n", fmt->nSamplesPerSec);
923 TRACE("nAvgBytesPerSec: %u\n", fmt->nAvgBytesPerSec);
924 TRACE("nBlockAlign: %u\n", fmt->nBlockAlign);
925 TRACE("wBitsPerSample: %u\n", fmt->wBitsPerSample);
926 TRACE("cbSize: %u\n", fmt->cbSize);
928 if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE){
929 WAVEFORMATEXTENSIBLE *fmtex = (void*)fmt;
930 TRACE("dwChannelMask: %08x\n", fmtex->dwChannelMask);
931 TRACE("Samples: %04x\n", fmtex->Samples.wReserved);
932 TRACE("SubFormat: %s\n", wine_dbgstr_guid(&fmtex->SubFormat));
936 static WAVEFORMATEX *clone_format(const WAVEFORMATEX *fmt)
941 if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
942 size = sizeof(WAVEFORMATEXTENSIBLE);
944 size = sizeof(WAVEFORMATEX);
946 ret = CoTaskMemAlloc(size);
950 memcpy(ret, fmt, size);
952 ret->cbSize = size - sizeof(WAVEFORMATEX);
957 static snd_pcm_format_t alsa_format(const WAVEFORMATEX *fmt)
959 snd_pcm_format_t format = SND_PCM_FORMAT_UNKNOWN;
960 const WAVEFORMATEXTENSIBLE *fmtex = (const WAVEFORMATEXTENSIBLE *)fmt;
962 if(fmt->wFormatTag == WAVE_FORMAT_PCM ||
963 (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
964 IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))){
965 if(fmt->wBitsPerSample == 8)
966 format = SND_PCM_FORMAT_U8;
967 else if(fmt->wBitsPerSample == 16)
968 format = SND_PCM_FORMAT_S16_LE;
969 else if(fmt->wBitsPerSample == 24)
970 format = SND_PCM_FORMAT_S24_3LE;
971 else if(fmt->wBitsPerSample == 32)
972 format = SND_PCM_FORMAT_S32_LE;
974 WARN("Unsupported bit depth: %u\n", fmt->wBitsPerSample);
975 if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
976 fmt->wBitsPerSample != fmtex->Samples.wValidBitsPerSample){
977 if(fmtex->Samples.wValidBitsPerSample == 20 && fmt->wBitsPerSample == 24)
978 format = SND_PCM_FORMAT_S20_3LE;
980 WARN("Unsupported ValidBits: %u\n", fmtex->Samples.wValidBitsPerSample);
982 }else if(fmt->wFormatTag == WAVE_FORMAT_IEEE_FLOAT ||
983 (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
984 IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT))){
985 if(fmt->wBitsPerSample == 32)
986 format = SND_PCM_FORMAT_FLOAT_LE;
987 else if(fmt->wBitsPerSample == 64)
988 format = SND_PCM_FORMAT_FLOAT64_LE;
990 WARN("Unsupported float size: %u\n", fmt->wBitsPerSample);
992 WARN("Unknown wave format: %04x\n", fmt->wFormatTag);
996 static void session_init_vols(AudioSession *session, UINT channels)
998 if(session->channel_count < channels){
1001 if(session->channel_vols)
1002 session->channel_vols = HeapReAlloc(GetProcessHeap(), 0,
1003 session->channel_vols, sizeof(float) * channels);
1005 session->channel_vols = HeapAlloc(GetProcessHeap(), 0,
1006 sizeof(float) * channels);
1007 if(!session->channel_vols)
1010 for(i = session->channel_count; i < channels; ++i)
1011 session->channel_vols[i] = 1.f;
1013 session->channel_count = channels;
1017 static AudioSession *create_session(const GUID *guid, IMMDevice *device,
1022 ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(AudioSession));
1026 memcpy(&ret->guid, guid, sizeof(GUID));
1028 ret->device = device;
1030 list_init(&ret->clients);
1032 list_add_head(&g_sessions, &ret->entry);
1034 InitializeCriticalSection(&ret->lock);
1035 ret->lock.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": AudioSession.lock");
1037 session_init_vols(ret, num_channels);
1039 ret->master_vol = 1.f;
1044 /* if channels == 0, then this will return or create a session with
1045 * matching dataflow and GUID. otherwise, channels must also match */
1046 static HRESULT get_audio_session(const GUID *sessionguid,
1047 IMMDevice *device, UINT channels, AudioSession **out)
1049 AudioSession *session;
1051 if(!sessionguid || IsEqualGUID(sessionguid, &GUID_NULL)){
1052 *out = create_session(&GUID_NULL, device, channels);
1054 return E_OUTOFMEMORY;
1060 LIST_FOR_EACH_ENTRY(session, &g_sessions, AudioSession, entry){
1061 if(session->device == device &&
1062 IsEqualGUID(sessionguid, &session->guid)){
1063 session_init_vols(session, channels);
1070 *out = create_session(sessionguid, device, channels);
1072 return E_OUTOFMEMORY;
1078 static int alsa_channel_index(DWORD flag)
1081 case SPEAKER_FRONT_LEFT:
1083 case SPEAKER_FRONT_RIGHT:
1085 case SPEAKER_BACK_LEFT:
1087 case SPEAKER_BACK_RIGHT:
1089 case SPEAKER_FRONT_CENTER:
1091 case SPEAKER_LOW_FREQUENCY:
1093 case SPEAKER_SIDE_LEFT:
1095 case SPEAKER_SIDE_RIGHT:
1101 static BOOL need_remapping(ACImpl *This, const WAVEFORMATEX *fmt)
1104 for(i = 0; i < fmt->nChannels; ++i){
1105 if(This->alsa_channel_map[i] != i)
1111 static DWORD get_channel_mask(unsigned int channels)
1117 return KSAUDIO_SPEAKER_MONO;
1119 return KSAUDIO_SPEAKER_STEREO;
1121 return KSAUDIO_SPEAKER_STEREO | SPEAKER_LOW_FREQUENCY;
1123 return KSAUDIO_SPEAKER_QUAD; /* not _SURROUND */
1125 return KSAUDIO_SPEAKER_QUAD | SPEAKER_LOW_FREQUENCY;
1127 return KSAUDIO_SPEAKER_5POINT1; /* not 5POINT1_SURROUND */
1129 return KSAUDIO_SPEAKER_5POINT1 | SPEAKER_BACK_CENTER;
1131 return KSAUDIO_SPEAKER_7POINT1_SURROUND; /* Vista deprecates 7POINT1 */
1133 FIXME("Unknown speaker configuration: %u\n", channels);
1137 static HRESULT map_channels(ACImpl *This, const WAVEFORMATEX *fmt)
1139 if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE || fmt->nChannels > 2){
1140 WAVEFORMATEXTENSIBLE *fmtex = (void*)fmt;
1141 DWORD mask, flag = SPEAKER_FRONT_LEFT;
1144 if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
1145 fmtex->dwChannelMask != 0)
1146 mask = fmtex->dwChannelMask;
1148 mask = get_channel_mask(fmt->nChannels);
1150 This->alsa_channels = 0;
1152 while(i < fmt->nChannels && !(flag & SPEAKER_RESERVED)){
1154 This->alsa_channel_map[i] = alsa_channel_index(flag);
1155 TRACE("Mapping mmdevapi channel %u (0x%x) to ALSA channel %d\n",
1156 i, flag, This->alsa_channel_map[i]);
1157 if(This->alsa_channel_map[i] >= This->alsa_channels)
1158 This->alsa_channels = This->alsa_channel_map[i] + 1;
1164 while(i < fmt->nChannels){
1165 This->alsa_channel_map[i] = This->alsa_channels;
1166 TRACE("Mapping mmdevapi channel %u to ALSA channel %d\n",
1167 i, This->alsa_channel_map[i]);
1168 ++This->alsa_channels;
1172 for(i = 0; i < fmt->nChannels; ++i){
1173 if(This->alsa_channel_map[i] == -1){
1174 This->alsa_channel_map[i] = This->alsa_channels;
1175 ++This->alsa_channels;
1176 TRACE("Remapping mmdevapi channel %u to ALSA channel %d\n",
1177 i, This->alsa_channel_map[i]);
1181 This->need_remapping = need_remapping(This, fmt);
1183 TRACE("need_remapping: %u, alsa_channels: %d\n", This->need_remapping, This->alsa_channels);
1185 This->need_remapping = FALSE;
1186 This->alsa_channels = fmt->nChannels;
1187 TRACE("need_remapping: %u, alsa_channels: %d\n", This->need_remapping, This->alsa_channels);
1193 static HRESULT WINAPI AudioClient_Initialize(IAudioClient *iface,
1194 AUDCLNT_SHAREMODE mode, DWORD flags, REFERENCE_TIME duration,
1195 REFERENCE_TIME period, const WAVEFORMATEX *fmt,
1196 const GUID *sessionguid)
1198 ACImpl *This = impl_from_IAudioClient(iface);
1199 snd_pcm_sw_params_t *sw_params = NULL;
1200 snd_pcm_format_t format;
1201 unsigned int rate, alsa_period_us;
1205 TRACE("(%p)->(%x, %x, %s, %s, %p, %s)\n", This, mode, flags,
1206 wine_dbgstr_longlong(duration), wine_dbgstr_longlong(period), fmt, debugstr_guid(sessionguid));
1211 if(mode != AUDCLNT_SHAREMODE_SHARED && mode != AUDCLNT_SHAREMODE_EXCLUSIVE)
1212 return AUDCLNT_E_NOT_INITIALIZED;
1214 if(flags & ~(AUDCLNT_STREAMFLAGS_CROSSPROCESS |
1215 AUDCLNT_STREAMFLAGS_LOOPBACK |
1216 AUDCLNT_STREAMFLAGS_EVENTCALLBACK |
1217 AUDCLNT_STREAMFLAGS_NOPERSIST |
1218 AUDCLNT_STREAMFLAGS_RATEADJUST |
1219 AUDCLNT_SESSIONFLAGS_EXPIREWHENUNOWNED |
1220 AUDCLNT_SESSIONFLAGS_DISPLAY_HIDE |
1221 AUDCLNT_SESSIONFLAGS_DISPLAY_HIDEWHENEXPIRED)){
1222 TRACE("Unknown flags: %08x\n", flags);
1223 return E_INVALIDARG;
1226 if(mode == AUDCLNT_SHAREMODE_SHARED){
1227 period = DefaultPeriod;
1228 if( duration < 3 * period)
1229 duration = 3 * period;
1231 if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE){
1232 if(((WAVEFORMATEXTENSIBLE*)fmt)->dwChannelMask == 0 ||
1233 ((WAVEFORMATEXTENSIBLE*)fmt)->dwChannelMask & SPEAKER_RESERVED)
1234 return AUDCLNT_E_UNSUPPORTED_FORMAT;
1238 period = DefaultPeriod; /* not minimum */
1239 if(period < MinimumPeriod || period > 5000000)
1240 return AUDCLNT_E_INVALID_DEVICE_PERIOD;
1241 if(duration > 20000000) /* the smaller the period, the lower this limit */
1242 return AUDCLNT_E_BUFFER_SIZE_ERROR;
1243 if(flags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK){
1244 if(duration != period)
1245 return AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL;
1246 FIXME("EXCLUSIVE mode with EVENTCALLBACK\n");
1247 return AUDCLNT_E_DEVICE_IN_USE;
1249 if( duration < 8 * period)
1250 duration = 8 * period; /* may grow above 2s */
1254 EnterCriticalSection(&This->lock);
1257 LeaveCriticalSection(&This->lock);
1258 return AUDCLNT_E_ALREADY_INITIALIZED;
1263 if(FAILED(map_channels(This, fmt))){
1264 WARN("map_channels failed\n");
1265 hr = AUDCLNT_E_ENDPOINT_CREATE_FAILED;
1269 if((err = snd_pcm_hw_params_any(This->pcm_handle, This->hw_params)) < 0){
1270 WARN("Unable to get hw_params: %d (%s)\n", err, snd_strerror(err));
1271 hr = AUDCLNT_E_ENDPOINT_CREATE_FAILED;
1275 if((err = snd_pcm_hw_params_set_access(This->pcm_handle, This->hw_params,
1276 SND_PCM_ACCESS_RW_INTERLEAVED)) < 0){
1277 WARN("Unable to set access: %d (%s)\n", err, snd_strerror(err));
1278 hr = AUDCLNT_E_ENDPOINT_CREATE_FAILED;
1282 format = alsa_format(fmt);
1283 if (format == SND_PCM_FORMAT_UNKNOWN){
1284 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
1288 if((err = snd_pcm_hw_params_set_format(This->pcm_handle, This->hw_params,
1290 WARN("Unable to set ALSA format to %u: %d (%s)\n", format, err,
1292 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
1296 This->alsa_format = format;
1298 rate = fmt->nSamplesPerSec;
1299 if((err = snd_pcm_hw_params_set_rate_near(This->pcm_handle, This->hw_params,
1301 WARN("Unable to set rate to %u: %d (%s)\n", rate, err,
1303 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
1307 if((err = snd_pcm_hw_params_set_channels(This->pcm_handle, This->hw_params,
1308 This->alsa_channels)) < 0){
1309 WARN("Unable to set channels to %u: %d (%s)\n", fmt->nChannels, err,
1311 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
1315 This->mmdev_period_rt = period;
1316 alsa_period_us = This->mmdev_period_rt / 10;
1317 if((err = snd_pcm_hw_params_set_period_time_near(This->pcm_handle,
1318 This->hw_params, &alsa_period_us, NULL)) < 0)
1319 WARN("Unable to set period time near %u: %d (%s)\n", alsa_period_us,
1320 err, snd_strerror(err));
1321 /* ALSA updates the output variable alsa_period_us */
1323 This->mmdev_period_frames = MulDiv(fmt->nSamplesPerSec,
1324 This->mmdev_period_rt, 10000000);
1326 /* Buffer 4 ALSA periods if large enough, else 4 mmdevapi periods */
1327 This->alsa_bufsize_frames = This->mmdev_period_frames * 4;
1328 if(err < 0 || alsa_period_us < period / 10)
1329 err = snd_pcm_hw_params_set_buffer_size_near(This->pcm_handle,
1330 This->hw_params, &This->alsa_bufsize_frames);
1332 unsigned int periods = 4;
1333 err = snd_pcm_hw_params_set_periods_near(This->pcm_handle, This->hw_params, &periods, NULL);
1336 WARN("Unable to set buffer size: %d (%s)\n", err, snd_strerror(err));
1338 if((err = snd_pcm_hw_params(This->pcm_handle, This->hw_params)) < 0){
1339 WARN("Unable to set hw params: %d (%s)\n", err, snd_strerror(err));
1340 hr = AUDCLNT_E_ENDPOINT_CREATE_FAILED;
1344 if((err = snd_pcm_hw_params_get_period_size(This->hw_params,
1345 &This->alsa_period_frames, NULL)) < 0){
1346 WARN("Unable to get period size: %d (%s)\n", err, snd_strerror(err));
1347 hr = AUDCLNT_E_ENDPOINT_CREATE_FAILED;
1351 if((err = snd_pcm_hw_params_get_buffer_size(This->hw_params,
1352 &This->alsa_bufsize_frames)) < 0){
1353 WARN("Unable to get buffer size: %d (%s)\n", err, snd_strerror(err));
1354 hr = AUDCLNT_E_ENDPOINT_CREATE_FAILED;
1358 sw_params = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, snd_pcm_sw_params_sizeof());
1364 if((err = snd_pcm_sw_params_current(This->pcm_handle, sw_params)) < 0){
1365 WARN("Unable to get sw_params: %d (%s)\n", err, snd_strerror(err));
1366 hr = AUDCLNT_E_ENDPOINT_CREATE_FAILED;
1370 if((err = snd_pcm_sw_params_set_start_threshold(This->pcm_handle,
1371 sw_params, 1)) < 0){
1372 WARN("Unable set start threshold to 0: %d (%s)\n", err, snd_strerror(err));
1373 hr = AUDCLNT_E_ENDPOINT_CREATE_FAILED;
1377 if((err = snd_pcm_sw_params_set_stop_threshold(This->pcm_handle,
1378 sw_params, This->alsa_bufsize_frames)) < 0){
1379 WARN("Unable set stop threshold to %lu: %d (%s)\n",
1380 This->alsa_bufsize_frames, err, snd_strerror(err));
1381 hr = AUDCLNT_E_ENDPOINT_CREATE_FAILED;
1385 if((err = snd_pcm_sw_params(This->pcm_handle, sw_params)) < 0){
1386 WARN("Unable to set sw params: %d (%s)\n", err, snd_strerror(err));
1387 hr = AUDCLNT_E_ENDPOINT_CREATE_FAILED;
1391 if((err = snd_pcm_prepare(This->pcm_handle)) < 0){
1392 WARN("Unable to prepare device: %d (%s)\n", err, snd_strerror(err));
1393 hr = AUDCLNT_E_ENDPOINT_CREATE_FAILED;
1397 /* Bear in mind weird situations where
1398 * ALSA period (50ms) > mmdevapi buffer (3x10ms)
1399 * or surprising rounding as seen with 22050x8x1 with Pulse:
1400 * ALSA period 220 vs. 221 frames in mmdevapi and
1401 * buffer 883 vs. 2205 frames in mmdevapi! */
1402 This->bufsize_frames = MulDiv(duration, fmt->nSamplesPerSec, 10000000);
1403 This->hidden_frames = This->alsa_period_frames + This->mmdev_period_frames +
1404 MulDiv(fmt->nSamplesPerSec, EXTRA_SAFE_RT, 10000000);
1406 /* Check if the ALSA buffer is so small that it will run out before
1407 * the next MMDevAPI period tick occurs. Allow a little wiggle room
1408 * with 120% of the period time. */
1409 if(This->alsa_bufsize_frames < 1.2 * This->mmdev_period_frames)
1410 FIXME("ALSA buffer time is too small. Expect underruns. (%lu < %u * 1.2)\n",
1411 This->alsa_bufsize_frames, This->mmdev_period_frames);
1413 This->local_buffer = HeapAlloc(GetProcessHeap(), 0,
1414 This->bufsize_frames * fmt->nBlockAlign);
1415 if(!This->local_buffer){
1419 if (fmt->wBitsPerSample == 8)
1420 memset(This->local_buffer, 128, This->bufsize_frames * fmt->nBlockAlign);
1422 memset(This->local_buffer, 0, This->bufsize_frames * fmt->nBlockAlign);
1424 This->fmt = clone_format(fmt);
1430 This->vols = HeapAlloc(GetProcessHeap(), 0, fmt->nChannels * sizeof(float));
1436 for(i = 0; i < fmt->nChannels; ++i)
1437 This->vols[i] = 1.f;
1440 This->flags = flags;
1442 EnterCriticalSection(&g_sessions_lock);
1444 hr = get_audio_session(sessionguid, This->parent, fmt->nChannels,
1447 LeaveCriticalSection(&g_sessions_lock);
1451 list_add_tail(&This->session->clients, &This->entry);
1453 LeaveCriticalSection(&g_sessions_lock);
1455 This->initted = TRUE;
1457 TRACE("ALSA period: %lu frames\n", This->alsa_period_frames);
1458 TRACE("ALSA buffer: %lu frames\n", This->alsa_bufsize_frames);
1459 TRACE("MMDevice period: %u frames\n", This->mmdev_period_frames);
1460 TRACE("MMDevice buffer: %u frames\n", This->bufsize_frames);
1463 HeapFree(GetProcessHeap(), 0, sw_params);
1465 HeapFree(GetProcessHeap(), 0, This->local_buffer);
1466 This->local_buffer = NULL;
1467 CoTaskMemFree(This->fmt);
1469 HeapFree(GetProcessHeap(), 0, This->vols);
1473 LeaveCriticalSection(&This->lock);
1478 static HRESULT WINAPI AudioClient_GetBufferSize(IAudioClient *iface,
1481 ACImpl *This = impl_from_IAudioClient(iface);
1483 TRACE("(%p)->(%p)\n", This, out);
1488 EnterCriticalSection(&This->lock);
1491 LeaveCriticalSection(&This->lock);
1492 return AUDCLNT_E_NOT_INITIALIZED;
1495 *out = This->bufsize_frames;
1497 LeaveCriticalSection(&This->lock);
1502 static HRESULT WINAPI AudioClient_GetStreamLatency(IAudioClient *iface,
1503 REFERENCE_TIME *latency)
1505 ACImpl *This = impl_from_IAudioClient(iface);
1507 TRACE("(%p)->(%p)\n", This, latency);
1512 EnterCriticalSection(&This->lock);
1515 LeaveCriticalSection(&This->lock);
1516 return AUDCLNT_E_NOT_INITIALIZED;
1519 /* Hide some frames in the ALSA buffer. Allows us to return GetCurrentPadding=0
1520 * yet have enough data left to play (as if it were in native's mixer). Add:
1521 * + mmdevapi_period such that at the end of it, ALSA still has data;
1522 * + EXTRA_SAFE (~4ms) to allow for late callback invocation / fluctuation;
1523 * + alsa_period such that ALSA always has at least one period to play. */
1524 if(This->dataflow == eRender)
1525 *latency = MulDiv(This->hidden_frames, 10000000, This->fmt->nSamplesPerSec);
1527 *latency = MulDiv(This->alsa_period_frames, 10000000, This->fmt->nSamplesPerSec)
1528 + This->mmdev_period_rt;
1530 LeaveCriticalSection(&This->lock);
1535 static HRESULT WINAPI AudioClient_GetCurrentPadding(IAudioClient *iface,
1538 ACImpl *This = impl_from_IAudioClient(iface);
1540 TRACE("(%p)->(%p)\n", This, out);
1545 EnterCriticalSection(&This->lock);
1548 LeaveCriticalSection(&This->lock);
1549 return AUDCLNT_E_NOT_INITIALIZED;
1552 /* padding is solely updated at callback time in shared mode */
1553 *out = This->held_frames;
1555 LeaveCriticalSection(&This->lock);
1557 TRACE("pad: %u\n", *out);
1562 static HRESULT WINAPI AudioClient_IsFormatSupported(IAudioClient *iface,
1563 AUDCLNT_SHAREMODE mode, const WAVEFORMATEX *fmt,
1566 ACImpl *This = impl_from_IAudioClient(iface);
1567 snd_pcm_format_mask_t *formats = NULL;
1568 snd_pcm_format_t format;
1570 WAVEFORMATEX *closest = NULL;
1571 unsigned int max = 0, min = 0;
1574 TRACE("(%p)->(%x, %p, %p)\n", This, mode, fmt, out);
1576 if(!fmt || (mode == AUDCLNT_SHAREMODE_SHARED && !out))
1579 if(mode != AUDCLNT_SHAREMODE_SHARED && mode != AUDCLNT_SHAREMODE_EXCLUSIVE)
1580 return E_INVALIDARG;
1582 if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
1583 fmt->cbSize < sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX))
1584 return E_INVALIDARG;
1590 if(mode != AUDCLNT_SHAREMODE_SHARED)
1594 if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
1595 (fmt->nAvgBytesPerSec == 0 ||
1596 fmt->nBlockAlign == 0 ||
1597 ((WAVEFORMATEXTENSIBLE*)fmt)->Samples.wValidBitsPerSample > fmt->wBitsPerSample))
1598 return E_INVALIDARG;
1600 if(fmt->nChannels == 0)
1601 return AUDCLNT_E_UNSUPPORTED_FORMAT;
1603 EnterCriticalSection(&This->lock);
1605 if((err = snd_pcm_hw_params_any(This->pcm_handle, This->hw_params)) < 0){
1606 hr = AUDCLNT_E_DEVICE_INVALIDATED;
1610 formats = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
1611 snd_pcm_format_mask_sizeof());
1617 snd_pcm_hw_params_get_format_mask(This->hw_params, formats);
1618 format = alsa_format(fmt);
1619 if (format == SND_PCM_FORMAT_UNKNOWN ||
1620 !snd_pcm_format_mask_test(formats, format)){
1621 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
1625 closest = clone_format(fmt);
1631 if((err = snd_pcm_hw_params_get_rate_min(This->hw_params, &min, NULL)) < 0){
1632 hr = AUDCLNT_E_DEVICE_INVALIDATED;
1633 WARN("Unable to get min rate: %d (%s)\n", err, snd_strerror(err));
1637 if((err = snd_pcm_hw_params_get_rate_max(This->hw_params, &max, NULL)) < 0){
1638 hr = AUDCLNT_E_DEVICE_INVALIDATED;
1639 WARN("Unable to get max rate: %d (%s)\n", err, snd_strerror(err));
1643 if(fmt->nSamplesPerSec < min || fmt->nSamplesPerSec > max){
1644 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
1648 if((err = snd_pcm_hw_params_get_channels_min(This->hw_params, &min)) < 0){
1649 hr = AUDCLNT_E_DEVICE_INVALIDATED;
1650 WARN("Unable to get min channels: %d (%s)\n", err, snd_strerror(err));
1654 if((err = snd_pcm_hw_params_get_channels_max(This->hw_params, &max)) < 0){
1655 hr = AUDCLNT_E_DEVICE_INVALIDATED;
1656 WARN("Unable to get max channels: %d (%s)\n", err, snd_strerror(err));
1659 if(fmt->nChannels > max){
1661 closest->nChannels = max;
1662 }else if(fmt->nChannels < min){
1664 closest->nChannels = min;
1667 if(FAILED(map_channels(This, fmt))){
1668 hr = AUDCLNT_E_DEVICE_INVALIDATED;
1669 WARN("map_channels failed\n");
1672 if(This->alsa_channels > max){
1674 closest->nChannels = max;
1677 if(closest->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
1678 ((WAVEFORMATEXTENSIBLE*)closest)->dwChannelMask = get_channel_mask(closest->nChannels);
1680 if(fmt->nBlockAlign != fmt->nChannels * fmt->wBitsPerSample / 8 ||
1681 fmt->nAvgBytesPerSec != fmt->nBlockAlign * fmt->nSamplesPerSec ||
1682 (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
1683 ((WAVEFORMATEXTENSIBLE*)fmt)->Samples.wValidBitsPerSample < fmt->wBitsPerSample))
1686 if(mode == AUDCLNT_SHAREMODE_EXCLUSIVE &&
1687 fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE){
1688 if(((WAVEFORMATEXTENSIBLE*)fmt)->dwChannelMask == 0 ||
1689 ((WAVEFORMATEXTENSIBLE*)fmt)->dwChannelMask & SPEAKER_RESERVED)
1694 LeaveCriticalSection(&This->lock);
1695 HeapFree(GetProcessHeap(), 0, formats);
1697 if(hr == S_FALSE && !out)
1698 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
1700 if(hr == S_FALSE && out) {
1701 closest->nBlockAlign =
1702 closest->nChannels * closest->wBitsPerSample / 8;
1703 closest->nAvgBytesPerSec =
1704 closest->nBlockAlign * closest->nSamplesPerSec;
1705 if(closest->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
1706 ((WAVEFORMATEXTENSIBLE*)closest)->Samples.wValidBitsPerSample = closest->wBitsPerSample;
1709 CoTaskMemFree(closest);
1711 TRACE("returning: %08x\n", hr);
1715 static HRESULT WINAPI AudioClient_GetMixFormat(IAudioClient *iface,
1716 WAVEFORMATEX **pwfx)
1718 ACImpl *This = impl_from_IAudioClient(iface);
1719 WAVEFORMATEXTENSIBLE *fmt;
1720 snd_pcm_format_mask_t *formats;
1721 unsigned int max_rate, max_channels;
1725 TRACE("(%p)->(%p)\n", This, pwfx);
1731 fmt = CoTaskMemAlloc(sizeof(WAVEFORMATEXTENSIBLE));
1733 return E_OUTOFMEMORY;
1735 formats = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, snd_pcm_format_mask_sizeof());
1738 return E_OUTOFMEMORY;
1741 EnterCriticalSection(&This->lock);
1743 if((err = snd_pcm_hw_params_any(This->pcm_handle, This->hw_params)) < 0){
1744 WARN("Unable to get hw_params: %d (%s)\n", err, snd_strerror(err));
1745 hr = AUDCLNT_E_DEVICE_INVALIDATED;
1749 snd_pcm_hw_params_get_format_mask(This->hw_params, formats);
1751 fmt->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
1752 if(snd_pcm_format_mask_test(formats, SND_PCM_FORMAT_FLOAT_LE)){
1753 fmt->Format.wBitsPerSample = 32;
1754 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
1755 }else if(snd_pcm_format_mask_test(formats, SND_PCM_FORMAT_S16_LE)){
1756 fmt->Format.wBitsPerSample = 16;
1757 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1758 }else if(snd_pcm_format_mask_test(formats, SND_PCM_FORMAT_U8)){
1759 fmt->Format.wBitsPerSample = 8;
1760 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1761 }else if(snd_pcm_format_mask_test(formats, SND_PCM_FORMAT_S32_LE)){
1762 fmt->Format.wBitsPerSample = 32;
1763 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1764 }else if(snd_pcm_format_mask_test(formats, SND_PCM_FORMAT_S24_3LE)){
1765 fmt->Format.wBitsPerSample = 24;
1766 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1768 ERR("Didn't recognize any available ALSA formats\n");
1769 hr = AUDCLNT_E_DEVICE_INVALIDATED;
1773 if((err = snd_pcm_hw_params_get_channels_max(This->hw_params,
1774 &max_channels)) < 0){
1775 WARN("Unable to get max channels: %d (%s)\n", err, snd_strerror(err));
1776 hr = AUDCLNT_E_DEVICE_INVALIDATED;
1780 if(max_channels > 2)
1781 fmt->Format.nChannels = 2;
1783 fmt->Format.nChannels = max_channels;
1785 fmt->dwChannelMask = get_channel_mask(fmt->Format.nChannels);
1787 if((err = snd_pcm_hw_params_get_rate_max(This->hw_params, &max_rate,
1789 WARN("Unable to get max rate: %d (%s)\n", err, snd_strerror(err));
1790 hr = AUDCLNT_E_DEVICE_INVALIDATED;
1794 if(max_rate >= 48000)
1795 fmt->Format.nSamplesPerSec = 48000;
1796 else if(max_rate >= 44100)
1797 fmt->Format.nSamplesPerSec = 44100;
1798 else if(max_rate >= 22050)
1799 fmt->Format.nSamplesPerSec = 22050;
1800 else if(max_rate >= 11025)
1801 fmt->Format.nSamplesPerSec = 11025;
1802 else if(max_rate >= 8000)
1803 fmt->Format.nSamplesPerSec = 8000;
1805 ERR("Unknown max rate: %u\n", max_rate);
1806 hr = AUDCLNT_E_DEVICE_INVALIDATED;
1810 fmt->Format.nBlockAlign = (fmt->Format.wBitsPerSample *
1811 fmt->Format.nChannels) / 8;
1812 fmt->Format.nAvgBytesPerSec = fmt->Format.nSamplesPerSec *
1813 fmt->Format.nBlockAlign;
1815 fmt->Samples.wValidBitsPerSample = fmt->Format.wBitsPerSample;
1816 fmt->Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
1818 dump_fmt((WAVEFORMATEX*)fmt);
1819 *pwfx = (WAVEFORMATEX*)fmt;
1822 LeaveCriticalSection(&This->lock);
1825 HeapFree(GetProcessHeap(), 0, formats);
1830 static HRESULT WINAPI AudioClient_GetDevicePeriod(IAudioClient *iface,
1831 REFERENCE_TIME *defperiod, REFERENCE_TIME *minperiod)
1833 ACImpl *This = impl_from_IAudioClient(iface);
1835 TRACE("(%p)->(%p, %p)\n", This, defperiod, minperiod);
1837 if(!defperiod && !minperiod)
1841 *defperiod = DefaultPeriod;
1843 *minperiod = MinimumPeriod;
1848 static BYTE *remap_channels(ACImpl *This, BYTE *buf, snd_pcm_uframes_t frames)
1850 snd_pcm_uframes_t i;
1852 UINT bytes_per_sample = This->fmt->wBitsPerSample / 8;
1854 if(!This->need_remapping)
1857 if(!This->remapping_buf){
1858 This->remapping_buf = HeapAlloc(GetProcessHeap(), 0,
1859 bytes_per_sample * This->alsa_channels * frames);
1860 This->remapping_buf_frames = frames;
1861 }else if(This->remapping_buf_frames < frames){
1862 This->remapping_buf = HeapReAlloc(GetProcessHeap(), 0, This->remapping_buf,
1863 bytes_per_sample * This->alsa_channels * frames);
1864 This->remapping_buf_frames = frames;
1867 snd_pcm_format_set_silence(This->alsa_format, This->remapping_buf,
1868 frames * This->alsa_channels);
1870 switch(This->fmt->wBitsPerSample){
1872 UINT8 *tgt_buf, *src_buf;
1873 tgt_buf = This->remapping_buf;
1875 for(i = 0; i < frames; ++i){
1876 for(c = 0; c < This->fmt->nChannels; ++c)
1877 tgt_buf[This->alsa_channel_map[c]] = src_buf[c];
1878 tgt_buf += This->alsa_channels;
1879 src_buf += This->fmt->nChannels;
1884 UINT16 *tgt_buf, *src_buf;
1885 tgt_buf = (UINT16*)This->remapping_buf;
1886 src_buf = (UINT16*)buf;
1887 for(i = 0; i < frames; ++i){
1888 for(c = 0; c < This->fmt->nChannels; ++c)
1889 tgt_buf[This->alsa_channel_map[c]] = src_buf[c];
1890 tgt_buf += This->alsa_channels;
1891 src_buf += This->fmt->nChannels;
1896 UINT32 *tgt_buf, *src_buf;
1897 tgt_buf = (UINT32*)This->remapping_buf;
1898 src_buf = (UINT32*)buf;
1899 for(i = 0; i < frames; ++i){
1900 for(c = 0; c < This->fmt->nChannels; ++c)
1901 tgt_buf[This->alsa_channel_map[c]] = src_buf[c];
1902 tgt_buf += This->alsa_channels;
1903 src_buf += This->fmt->nChannels;
1908 BYTE *tgt_buf, *src_buf;
1909 tgt_buf = This->remapping_buf;
1911 for(i = 0; i < frames; ++i){
1912 for(c = 0; c < This->fmt->nChannels; ++c)
1913 memcpy(&tgt_buf[This->alsa_channel_map[c] * bytes_per_sample],
1914 &src_buf[c * bytes_per_sample], bytes_per_sample);
1915 tgt_buf += This->alsa_channels * bytes_per_sample;
1916 src_buf += This->fmt->nChannels * bytes_per_sample;
1922 return This->remapping_buf;
1925 static snd_pcm_sframes_t alsa_write_best_effort(snd_pcm_t *handle, BYTE *buf,
1926 snd_pcm_uframes_t frames, ACImpl *This, BOOL mute)
1928 snd_pcm_sframes_t written;
1932 if((err = snd_pcm_format_set_silence(This->alsa_format, buf,
1933 frames * This->fmt->nChannels)) < 0)
1934 WARN("Setting buffer to silence failed: %d (%s)\n", err,
1938 buf = remap_channels(This, buf, frames);
1940 written = snd_pcm_writei(handle, buf, frames);
1944 if(written == -EAGAIN)
1948 WARN("writei failed, recovering: %ld (%s)\n", written,
1949 snd_strerror(written));
1951 ret = snd_pcm_recover(handle, written, 0);
1953 WARN("Could not recover: %d (%s)\n", ret, snd_strerror(ret));
1957 written = snd_pcm_writei(handle, buf, frames);
1963 static void alsa_write_data(ACImpl *This)
1965 snd_pcm_sframes_t written, in_alsa;
1966 snd_pcm_uframes_t to_write, avail, write_limit, max_period;
1969 This->local_buffer + (This->lcl_offs_frames * This->fmt->nBlockAlign);
1971 /* this call seems to be required to get an accurate snd_pcm_state() */
1972 avail = snd_pcm_avail_update(This->pcm_handle);
1974 if(snd_pcm_state(This->pcm_handle) == SND_PCM_STATE_XRUN ||
1975 avail > This->alsa_bufsize_frames){
1976 TRACE("XRun state avail %ld, recovering\n", avail);
1978 avail = This->alsa_bufsize_frames;
1980 if((err = snd_pcm_recover(This->pcm_handle, -EPIPE, 1)) < 0)
1981 WARN("snd_pcm_recover failed: %d (%s)\n", err, snd_strerror(err));
1983 if((err = snd_pcm_reset(This->pcm_handle)) < 0)
1984 WARN("snd_pcm_reset failed: %d (%s)\n", err, snd_strerror(err));
1986 if((err = snd_pcm_prepare(This->pcm_handle)) < 0)
1987 WARN("snd_pcm_prepare failed: %d (%s)\n", err, snd_strerror(err));
1989 TRACE("pad: %ld\n", This->alsa_bufsize_frames - avail);
1991 if(This->held_frames == 0)
1994 if(This->lcl_offs_frames + This->held_frames > This->bufsize_frames)
1995 to_write = This->bufsize_frames - This->lcl_offs_frames;
1997 to_write = This->held_frames;
1999 max_period = max(This->mmdev_period_frames, This->alsa_period_frames);
2001 /* try to keep 3 ALSA periods or 3 MMDevAPI periods in the ALSA buffer and
2004 in_alsa = This->alsa_bufsize_frames - avail;
2005 while(in_alsa + write_limit < max_period * 3)
2006 write_limit += max_period;
2007 if(write_limit == 0)
2010 to_write = min(to_write, write_limit);
2012 /* Add a lead-in when starting with too few frames to ensure
2013 * continuous rendering. Additional benefit: Force ALSA to start.
2014 * GetPosition continues to reflect the speaker position because
2015 * snd_pcm_delay includes buffered frames in its total delay
2016 * and last_pos_frames prevents moving backwards. */
2017 if(!in_alsa && This->held_frames < This->hidden_frames){
2018 UINT32 s_frames = This->hidden_frames - This->held_frames;
2019 BYTE *silence = HeapAlloc(GetProcessHeap(), 0,
2020 s_frames * This->fmt->nBlockAlign);
2023 in_alsa = alsa_write_best_effort(This->pcm_handle,
2024 silence, s_frames, This, TRUE);
2025 TRACE("lead-in %ld\n", in_alsa);
2026 HeapFree(GetProcessHeap(), 0, silence);
2030 WARN("Couldn't allocate lead-in, expect underrun\n");
2033 written = alsa_write_best_effort(This->pcm_handle, buf, to_write, This,
2034 This->session->mute);
2036 WARN("Couldn't write: %ld (%s)\n", written, snd_strerror(written));
2040 This->lcl_offs_frames += written;
2041 This->lcl_offs_frames %= This->bufsize_frames;
2042 This->held_frames -= written;
2044 if(written < to_write){
2045 /* ALSA buffer probably full */
2049 if(This->held_frames && (written < write_limit)){
2050 /* wrapped and have some data back at the start to write */
2051 written = alsa_write_best_effort(This->pcm_handle, This->local_buffer,
2052 min(This->held_frames, write_limit - written), This,
2053 This->session->mute);
2055 WARN("Couldn't write: %ld (%s)\n", written, snd_strerror(written));
2059 This->lcl_offs_frames += written;
2060 This->lcl_offs_frames %= This->bufsize_frames;
2061 This->held_frames -= written;
2065 static void alsa_read_data(ACImpl *This)
2067 snd_pcm_sframes_t pos, readable, nread;
2069 pos = (This->held_frames + This->lcl_offs_frames) % This->bufsize_frames;
2070 readable = This->bufsize_frames - pos;
2072 nread = snd_pcm_readi(This->pcm_handle,
2073 This->local_buffer + pos * This->fmt->nBlockAlign, readable);
2074 TRACE("read %ld from %u limit %lu\n", nread, This->held_frames + This->lcl_offs_frames, readable);
2078 if(nread == -EAGAIN) /* no data yet */
2081 WARN("read failed, recovering: %ld (%s)\n", nread, snd_strerror(nread));
2083 ret = snd_pcm_recover(This->pcm_handle, nread, 0);
2085 WARN("Recover failed: %d (%s)\n", ret, snd_strerror(ret));
2089 nread = snd_pcm_readi(This->pcm_handle,
2090 This->local_buffer + pos * This->fmt->nBlockAlign, readable);
2092 WARN("read failed: %ld (%s)\n", nread, snd_strerror(nread));
2097 if(This->session->mute){
2099 if((err = snd_pcm_format_set_silence(This->alsa_format,
2100 This->local_buffer + pos * This->fmt->nBlockAlign,
2102 WARN("Setting buffer to silence failed: %d (%s)\n", err,
2106 This->held_frames += nread;
2108 if(This->held_frames > This->bufsize_frames){
2109 WARN("Overflow of unread data\n");
2110 This->lcl_offs_frames += This->held_frames;
2111 This->lcl_offs_frames %= This->bufsize_frames;
2112 This->held_frames = This->bufsize_frames;
2116 static void CALLBACK alsa_push_buffer_data(void *user, BOOLEAN timer)
2118 ACImpl *This = user;
2120 EnterCriticalSection(&This->lock);
2123 if(This->dataflow == eRender)
2124 alsa_write_data(This);
2125 else if(This->dataflow == eCapture)
2126 alsa_read_data(This);
2129 SetEvent(This->event);
2132 LeaveCriticalSection(&This->lock);
2135 static HRESULT WINAPI AudioClient_Start(IAudioClient *iface)
2137 ACImpl *This = impl_from_IAudioClient(iface);
2139 TRACE("(%p)\n", This);
2141 EnterCriticalSection(&This->lock);
2144 LeaveCriticalSection(&This->lock);
2145 return AUDCLNT_E_NOT_INITIALIZED;
2148 if((This->flags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK) && !This->event){
2149 LeaveCriticalSection(&This->lock);
2150 return AUDCLNT_E_EVENTHANDLE_NOT_SET;
2154 LeaveCriticalSection(&This->lock);
2155 return AUDCLNT_E_NOT_STOPPED;
2158 if(This->dataflow == eCapture){
2159 /* dump any data that might be leftover in the ALSA capture buffer */
2160 snd_pcm_readi(This->pcm_handle, This->local_buffer,
2161 This->bufsize_frames);
2164 if(!CreateTimerQueueTimer(&This->timer, g_timer_q, alsa_push_buffer_data,
2165 This, 0, This->mmdev_period_rt / 10000, WT_EXECUTEINTIMERTHREAD)){
2166 LeaveCriticalSection(&This->lock);
2167 WARN("Unable to create timer: %u\n", GetLastError());
2168 return E_OUTOFMEMORY;
2171 This->started = TRUE;
2173 LeaveCriticalSection(&This->lock);
2178 static HRESULT WINAPI AudioClient_Stop(IAudioClient *iface)
2180 ACImpl *This = impl_from_IAudioClient(iface);
2184 TRACE("(%p)\n", This);
2186 EnterCriticalSection(&This->lock);
2189 LeaveCriticalSection(&This->lock);
2190 return AUDCLNT_E_NOT_INITIALIZED;
2194 LeaveCriticalSection(&This->lock);
2198 /* Stop without losing written frames or position.
2199 * snd_pcm_pause would be appropriate but is unsupported by dmix.
2200 * snd_pcm_drain yields EAGAIN in NONBLOCK mode, except with Pulse. */
2202 event = CreateEventW(NULL, TRUE, FALSE, NULL);
2203 wait = !DeleteTimerQueueTimer(g_timer_q, This->timer, event);
2205 WARN("DeleteTimerQueueTimer error %u\n", GetLastError());
2206 wait = wait && GetLastError() == ERROR_IO_PENDING;
2208 This->started = FALSE;
2210 LeaveCriticalSection(&This->lock);
2213 WaitForSingleObject(event, INFINITE);
2219 static HRESULT WINAPI AudioClient_Reset(IAudioClient *iface)
2221 ACImpl *This = impl_from_IAudioClient(iface);
2223 TRACE("(%p)\n", This);
2225 EnterCriticalSection(&This->lock);
2228 LeaveCriticalSection(&This->lock);
2229 return AUDCLNT_E_NOT_INITIALIZED;
2233 LeaveCriticalSection(&This->lock);
2234 return AUDCLNT_E_NOT_STOPPED;
2237 if(This->getbuf_last){
2238 LeaveCriticalSection(&This->lock);
2239 return AUDCLNT_E_BUFFER_OPERATION_PENDING;
2242 if(snd_pcm_drop(This->pcm_handle) < 0)
2243 WARN("snd_pcm_drop failed\n");
2245 if(snd_pcm_reset(This->pcm_handle) < 0)
2246 WARN("snd_pcm_reset failed\n");
2248 if(snd_pcm_prepare(This->pcm_handle) < 0)
2249 WARN("snd_pcm_prepare failed\n");
2251 if(This->dataflow == eRender){
2252 This->written_frames = 0;
2253 This->last_pos_frames = 0;
2255 This->written_frames += This->held_frames;
2257 This->held_frames = 0;
2258 This->lcl_offs_frames = 0;
2260 LeaveCriticalSection(&This->lock);
2265 static HRESULT WINAPI AudioClient_SetEventHandle(IAudioClient *iface,
2268 ACImpl *This = impl_from_IAudioClient(iface);
2270 TRACE("(%p)->(%p)\n", This, event);
2273 return E_INVALIDARG;
2275 EnterCriticalSection(&This->lock);
2278 LeaveCriticalSection(&This->lock);
2279 return AUDCLNT_E_NOT_INITIALIZED;
2282 if(!(This->flags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK)){
2283 LeaveCriticalSection(&This->lock);
2284 return AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED;
2287 This->event = event;
2289 LeaveCriticalSection(&This->lock);
2294 static HRESULT WINAPI AudioClient_GetService(IAudioClient *iface, REFIID riid,
2297 ACImpl *This = impl_from_IAudioClient(iface);
2299 TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(riid), ppv);
2305 EnterCriticalSection(&This->lock);
2308 LeaveCriticalSection(&This->lock);
2309 return AUDCLNT_E_NOT_INITIALIZED;
2312 if(IsEqualIID(riid, &IID_IAudioRenderClient)){
2313 if(This->dataflow != eRender){
2314 LeaveCriticalSection(&This->lock);
2315 return AUDCLNT_E_WRONG_ENDPOINT_TYPE;
2317 IAudioRenderClient_AddRef(&This->IAudioRenderClient_iface);
2318 *ppv = &This->IAudioRenderClient_iface;
2319 }else if(IsEqualIID(riid, &IID_IAudioCaptureClient)){
2320 if(This->dataflow != eCapture){
2321 LeaveCriticalSection(&This->lock);
2322 return AUDCLNT_E_WRONG_ENDPOINT_TYPE;
2324 IAudioCaptureClient_AddRef(&This->IAudioCaptureClient_iface);
2325 *ppv = &This->IAudioCaptureClient_iface;
2326 }else if(IsEqualIID(riid, &IID_IAudioClock)){
2327 IAudioClock_AddRef(&This->IAudioClock_iface);
2328 *ppv = &This->IAudioClock_iface;
2329 }else if(IsEqualIID(riid, &IID_IAudioStreamVolume)){
2330 IAudioStreamVolume_AddRef(&This->IAudioStreamVolume_iface);
2331 *ppv = &This->IAudioStreamVolume_iface;
2332 }else if(IsEqualIID(riid, &IID_IAudioSessionControl)){
2333 if(!This->session_wrapper){
2334 This->session_wrapper = AudioSessionWrapper_Create(This);
2335 if(!This->session_wrapper){
2336 LeaveCriticalSection(&This->lock);
2337 return E_OUTOFMEMORY;
2340 IAudioSessionControl2_AddRef(&This->session_wrapper->IAudioSessionControl2_iface);
2342 *ppv = &This->session_wrapper->IAudioSessionControl2_iface;
2343 }else if(IsEqualIID(riid, &IID_IChannelAudioVolume)){
2344 if(!This->session_wrapper){
2345 This->session_wrapper = AudioSessionWrapper_Create(This);
2346 if(!This->session_wrapper){
2347 LeaveCriticalSection(&This->lock);
2348 return E_OUTOFMEMORY;
2351 IChannelAudioVolume_AddRef(&This->session_wrapper->IChannelAudioVolume_iface);
2353 *ppv = &This->session_wrapper->IChannelAudioVolume_iface;
2354 }else if(IsEqualIID(riid, &IID_ISimpleAudioVolume)){
2355 if(!This->session_wrapper){
2356 This->session_wrapper = AudioSessionWrapper_Create(This);
2357 if(!This->session_wrapper){
2358 LeaveCriticalSection(&This->lock);
2359 return E_OUTOFMEMORY;
2362 ISimpleAudioVolume_AddRef(&This->session_wrapper->ISimpleAudioVolume_iface);
2364 *ppv = &This->session_wrapper->ISimpleAudioVolume_iface;
2368 LeaveCriticalSection(&This->lock);
2372 LeaveCriticalSection(&This->lock);
2374 FIXME("stub %s\n", debugstr_guid(riid));
2375 return E_NOINTERFACE;
2378 static const IAudioClientVtbl AudioClient_Vtbl =
2380 AudioClient_QueryInterface,
2382 AudioClient_Release,
2383 AudioClient_Initialize,
2384 AudioClient_GetBufferSize,
2385 AudioClient_GetStreamLatency,
2386 AudioClient_GetCurrentPadding,
2387 AudioClient_IsFormatSupported,
2388 AudioClient_GetMixFormat,
2389 AudioClient_GetDevicePeriod,
2393 AudioClient_SetEventHandle,
2394 AudioClient_GetService
2397 static HRESULT WINAPI AudioRenderClient_QueryInterface(
2398 IAudioRenderClient *iface, REFIID riid, void **ppv)
2400 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2406 if(IsEqualIID(riid, &IID_IUnknown) ||
2407 IsEqualIID(riid, &IID_IAudioRenderClient))
2410 IUnknown_AddRef((IUnknown*)*ppv);
2414 WARN("Unknown interface %s\n", debugstr_guid(riid));
2415 return E_NOINTERFACE;
2418 static ULONG WINAPI AudioRenderClient_AddRef(IAudioRenderClient *iface)
2420 ACImpl *This = impl_from_IAudioRenderClient(iface);
2421 return AudioClient_AddRef(&This->IAudioClient_iface);
2424 static ULONG WINAPI AudioRenderClient_Release(IAudioRenderClient *iface)
2426 ACImpl *This = impl_from_IAudioRenderClient(iface);
2427 return AudioClient_Release(&This->IAudioClient_iface);
2430 static HRESULT WINAPI AudioRenderClient_GetBuffer(IAudioRenderClient *iface,
2431 UINT32 frames, BYTE **data)
2433 ACImpl *This = impl_from_IAudioRenderClient(iface);
2436 TRACE("(%p)->(%u, %p)\n", This, frames, data);
2442 EnterCriticalSection(&This->lock);
2444 if(This->getbuf_last){
2445 LeaveCriticalSection(&This->lock);
2446 return AUDCLNT_E_OUT_OF_ORDER;
2450 LeaveCriticalSection(&This->lock);
2454 /* held_frames == GetCurrentPadding_nolock(); */
2455 if(This->held_frames + frames > This->bufsize_frames){
2456 LeaveCriticalSection(&This->lock);
2457 return AUDCLNT_E_BUFFER_TOO_LARGE;
2461 (This->lcl_offs_frames + This->held_frames) % This->bufsize_frames;
2462 if(write_pos + frames > This->bufsize_frames){
2463 if(This->tmp_buffer_frames < frames){
2464 HeapFree(GetProcessHeap(), 0, This->tmp_buffer);
2465 This->tmp_buffer = HeapAlloc(GetProcessHeap(), 0,
2466 frames * This->fmt->nBlockAlign);
2467 if(!This->tmp_buffer){
2468 LeaveCriticalSection(&This->lock);
2469 return E_OUTOFMEMORY;
2471 This->tmp_buffer_frames = frames;
2473 *data = This->tmp_buffer;
2474 This->getbuf_last = -frames;
2476 *data = This->local_buffer + write_pos * This->fmt->nBlockAlign;
2477 This->getbuf_last = frames;
2480 LeaveCriticalSection(&This->lock);
2485 static void alsa_wrap_buffer(ACImpl *This, BYTE *buffer, UINT32 written_frames)
2487 snd_pcm_uframes_t write_offs_frames =
2488 (This->lcl_offs_frames + This->held_frames) % This->bufsize_frames;
2489 UINT32 write_offs_bytes = write_offs_frames * This->fmt->nBlockAlign;
2490 snd_pcm_uframes_t chunk_frames = This->bufsize_frames - write_offs_frames;
2491 UINT32 chunk_bytes = chunk_frames * This->fmt->nBlockAlign;
2492 UINT32 written_bytes = written_frames * This->fmt->nBlockAlign;
2494 if(written_bytes <= chunk_bytes){
2495 memcpy(This->local_buffer + write_offs_bytes, buffer, written_bytes);
2497 memcpy(This->local_buffer + write_offs_bytes, buffer, chunk_bytes);
2498 memcpy(This->local_buffer, buffer + chunk_bytes,
2499 written_bytes - chunk_bytes);
2503 static HRESULT WINAPI AudioRenderClient_ReleaseBuffer(
2504 IAudioRenderClient *iface, UINT32 written_frames, DWORD flags)
2506 ACImpl *This = impl_from_IAudioRenderClient(iface);
2509 TRACE("(%p)->(%u, %x)\n", This, written_frames, flags);
2511 EnterCriticalSection(&This->lock);
2513 if(!written_frames){
2514 This->getbuf_last = 0;
2515 LeaveCriticalSection(&This->lock);
2519 if(!This->getbuf_last){
2520 LeaveCriticalSection(&This->lock);
2521 return AUDCLNT_E_OUT_OF_ORDER;
2524 if(written_frames > (This->getbuf_last >= 0 ? This->getbuf_last : -This->getbuf_last)){
2525 LeaveCriticalSection(&This->lock);
2526 return AUDCLNT_E_INVALID_SIZE;
2529 if(This->getbuf_last >= 0)
2530 buffer = This->local_buffer + This->fmt->nBlockAlign *
2531 ((This->lcl_offs_frames + This->held_frames) % This->bufsize_frames);
2533 buffer = This->tmp_buffer;
2535 if(flags & AUDCLNT_BUFFERFLAGS_SILENT){
2536 if(This->fmt->wBitsPerSample == 8)
2537 memset(buffer, 128, written_frames * This->fmt->nBlockAlign);
2539 memset(buffer, 0, written_frames * This->fmt->nBlockAlign);
2542 if(This->getbuf_last < 0)
2543 alsa_wrap_buffer(This, buffer, written_frames);
2545 This->held_frames += written_frames;
2546 This->written_frames += written_frames;
2547 This->getbuf_last = 0;
2549 LeaveCriticalSection(&This->lock);
2554 static const IAudioRenderClientVtbl AudioRenderClient_Vtbl = {
2555 AudioRenderClient_QueryInterface,
2556 AudioRenderClient_AddRef,
2557 AudioRenderClient_Release,
2558 AudioRenderClient_GetBuffer,
2559 AudioRenderClient_ReleaseBuffer
2562 static HRESULT WINAPI AudioCaptureClient_QueryInterface(
2563 IAudioCaptureClient *iface, REFIID riid, void **ppv)
2565 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2571 if(IsEqualIID(riid, &IID_IUnknown) ||
2572 IsEqualIID(riid, &IID_IAudioCaptureClient))
2575 IUnknown_AddRef((IUnknown*)*ppv);
2579 WARN("Unknown interface %s\n", debugstr_guid(riid));
2580 return E_NOINTERFACE;
2583 static ULONG WINAPI AudioCaptureClient_AddRef(IAudioCaptureClient *iface)
2585 ACImpl *This = impl_from_IAudioCaptureClient(iface);
2586 return IAudioClient_AddRef(&This->IAudioClient_iface);
2589 static ULONG WINAPI AudioCaptureClient_Release(IAudioCaptureClient *iface)
2591 ACImpl *This = impl_from_IAudioCaptureClient(iface);
2592 return IAudioClient_Release(&This->IAudioClient_iface);
2595 static HRESULT WINAPI AudioCaptureClient_GetBuffer(IAudioCaptureClient *iface,
2596 BYTE **data, UINT32 *frames, DWORD *flags, UINT64 *devpos,
2599 ACImpl *This = impl_from_IAudioCaptureClient(iface);
2601 TRACE("(%p)->(%p, %p, %p, %p, %p)\n", This, data, frames, flags,
2604 if(!data || !frames || !flags)
2607 EnterCriticalSection(&This->lock);
2609 if(This->getbuf_last){
2610 LeaveCriticalSection(&This->lock);
2611 return AUDCLNT_E_OUT_OF_ORDER;
2614 /* hr = GetNextPacketSize(iface, frames); */
2615 if(This->held_frames < This->mmdev_period_frames){
2617 LeaveCriticalSection(&This->lock);
2618 return AUDCLNT_S_BUFFER_EMPTY;
2620 *frames = This->mmdev_period_frames;
2622 if(This->lcl_offs_frames + *frames > This->bufsize_frames){
2623 UINT32 chunk_bytes, offs_bytes, frames_bytes;
2624 if(This->tmp_buffer_frames < *frames){
2625 HeapFree(GetProcessHeap(), 0, This->tmp_buffer);
2626 This->tmp_buffer = HeapAlloc(GetProcessHeap(), 0,
2627 *frames * This->fmt->nBlockAlign);
2628 if(!This->tmp_buffer){
2629 LeaveCriticalSection(&This->lock);
2630 return E_OUTOFMEMORY;
2632 This->tmp_buffer_frames = *frames;
2635 *data = This->tmp_buffer;
2636 chunk_bytes = (This->bufsize_frames - This->lcl_offs_frames) *
2637 This->fmt->nBlockAlign;
2638 offs_bytes = This->lcl_offs_frames * This->fmt->nBlockAlign;
2639 frames_bytes = *frames * This->fmt->nBlockAlign;
2640 memcpy(This->tmp_buffer, This->local_buffer + offs_bytes, chunk_bytes);
2641 memcpy(This->tmp_buffer + chunk_bytes, This->local_buffer,
2642 frames_bytes - chunk_bytes);
2644 *data = This->local_buffer +
2645 This->lcl_offs_frames * This->fmt->nBlockAlign;
2647 This->getbuf_last = *frames;
2651 *devpos = This->written_frames;
2652 if(qpcpos){ /* fixme: qpc of recording time */
2653 LARGE_INTEGER stamp, freq;
2654 QueryPerformanceCounter(&stamp);
2655 QueryPerformanceFrequency(&freq);
2656 *qpcpos = (stamp.QuadPart * (INT64)10000000) / freq.QuadPart;
2659 LeaveCriticalSection(&This->lock);
2661 return *frames ? S_OK : AUDCLNT_S_BUFFER_EMPTY;
2664 static HRESULT WINAPI AudioCaptureClient_ReleaseBuffer(
2665 IAudioCaptureClient *iface, UINT32 done)
2667 ACImpl *This = impl_from_IAudioCaptureClient(iface);
2669 TRACE("(%p)->(%u)\n", This, done);
2671 EnterCriticalSection(&This->lock);
2674 This->getbuf_last = 0;
2675 LeaveCriticalSection(&This->lock);
2679 if(!This->getbuf_last){
2680 LeaveCriticalSection(&This->lock);
2681 return AUDCLNT_E_OUT_OF_ORDER;
2684 if(This->getbuf_last != done){
2685 LeaveCriticalSection(&This->lock);
2686 return AUDCLNT_E_INVALID_SIZE;
2689 This->written_frames += done;
2690 This->held_frames -= done;
2691 This->lcl_offs_frames += done;
2692 This->lcl_offs_frames %= This->bufsize_frames;
2693 This->getbuf_last = 0;
2695 LeaveCriticalSection(&This->lock);
2700 static HRESULT WINAPI AudioCaptureClient_GetNextPacketSize(
2701 IAudioCaptureClient *iface, UINT32 *frames)
2703 ACImpl *This = impl_from_IAudioCaptureClient(iface);
2705 TRACE("(%p)->(%p)\n", This, frames);
2710 EnterCriticalSection(&This->lock);
2712 *frames = This->held_frames < This->mmdev_period_frames ? 0 : This->mmdev_period_frames;
2714 LeaveCriticalSection(&This->lock);
2719 static const IAudioCaptureClientVtbl AudioCaptureClient_Vtbl =
2721 AudioCaptureClient_QueryInterface,
2722 AudioCaptureClient_AddRef,
2723 AudioCaptureClient_Release,
2724 AudioCaptureClient_GetBuffer,
2725 AudioCaptureClient_ReleaseBuffer,
2726 AudioCaptureClient_GetNextPacketSize
2729 static HRESULT WINAPI AudioClock_QueryInterface(IAudioClock *iface,
2730 REFIID riid, void **ppv)
2732 ACImpl *This = impl_from_IAudioClock(iface);
2734 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2740 if(IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IAudioClock))
2742 else if(IsEqualIID(riid, &IID_IAudioClock2))
2743 *ppv = &This->IAudioClock2_iface;
2745 IUnknown_AddRef((IUnknown*)*ppv);
2749 WARN("Unknown interface %s\n", debugstr_guid(riid));
2750 return E_NOINTERFACE;
2753 static ULONG WINAPI AudioClock_AddRef(IAudioClock *iface)
2755 ACImpl *This = impl_from_IAudioClock(iface);
2756 return IAudioClient_AddRef(&This->IAudioClient_iface);
2759 static ULONG WINAPI AudioClock_Release(IAudioClock *iface)
2761 ACImpl *This = impl_from_IAudioClock(iface);
2762 return IAudioClient_Release(&This->IAudioClient_iface);
2765 static HRESULT WINAPI AudioClock_GetFrequency(IAudioClock *iface, UINT64 *freq)
2767 ACImpl *This = impl_from_IAudioClock(iface);
2769 TRACE("(%p)->(%p)\n", This, freq);
2771 *freq = This->fmt->nSamplesPerSec;
2776 static HRESULT WINAPI AudioClock_GetPosition(IAudioClock *iface, UINT64 *pos,
2779 ACImpl *This = impl_from_IAudioClock(iface);
2780 UINT64 written_frames, position;
2783 snd_pcm_state_t alsa_state;
2784 snd_pcm_uframes_t avail_frames;
2785 snd_pcm_sframes_t delay_frames;
2787 TRACE("(%p)->(%p, %p)\n", This, pos, qpctime);
2792 EnterCriticalSection(&This->lock);
2794 /* call required to get accurate snd_pcm_state() */
2795 avail_frames = snd_pcm_avail_update(This->pcm_handle);
2796 alsa_state = snd_pcm_state(This->pcm_handle);
2797 written_frames = This->written_frames;
2798 held_frames = This->held_frames;
2800 err = snd_pcm_delay(This->pcm_handle, &delay_frames);
2802 /* old Pulse, shortly after start */
2803 WARN("snd_pcm_delay failed in state %u: %d (%s)\n", alsa_state, err, snd_strerror(err));
2806 if(This->dataflow == eRender){
2807 position = written_frames - held_frames; /* maximum */
2808 if(!This->started || alsa_state > SND_PCM_STATE_RUNNING)
2809 ; /* mmdevapi stopped or ALSA underrun: pretend everything was played */
2810 else if(err<0 || delay_frames > position - This->last_pos_frames)
2811 /* Pulse bug: past underrun, despite recovery, avail_frames & delay
2812 * may be larger than alsa_bufsize_frames, as if cumulating frames. */
2813 /* Pulse bug: EIO(-5) shortly after starting: nothing played */
2814 position = This->last_pos_frames;
2815 else if(delay_frames > 0)
2816 position -= delay_frames;
2818 position = written_frames + held_frames;
2820 /* ensure monotic growth */
2821 This->last_pos_frames = position;
2823 LeaveCriticalSection(&This->lock);
2825 TRACE("frames written: %u, held: %u, avail: %ld, delay: %ld state %d, pos: %u\n",
2826 (UINT32)(written_frames%1000000000), held_frames,
2827 avail_frames, delay_frames, alsa_state, (UINT32)(position%1000000000));
2831 LARGE_INTEGER stamp, freq;
2832 QueryPerformanceCounter(&stamp);
2833 QueryPerformanceFrequency(&freq);
2834 *qpctime = (stamp.QuadPart * (INT64)10000000) / freq.QuadPart;
2840 static HRESULT WINAPI AudioClock_GetCharacteristics(IAudioClock *iface,
2843 ACImpl *This = impl_from_IAudioClock(iface);
2845 TRACE("(%p)->(%p)\n", This, chars);
2850 *chars = AUDIOCLOCK_CHARACTERISTIC_FIXED_FREQ;
2855 static const IAudioClockVtbl AudioClock_Vtbl =
2857 AudioClock_QueryInterface,
2860 AudioClock_GetFrequency,
2861 AudioClock_GetPosition,
2862 AudioClock_GetCharacteristics
2865 static HRESULT WINAPI AudioClock2_QueryInterface(IAudioClock2 *iface,
2866 REFIID riid, void **ppv)
2868 ACImpl *This = impl_from_IAudioClock2(iface);
2869 return IAudioClock_QueryInterface(&This->IAudioClock_iface, riid, ppv);
2872 static ULONG WINAPI AudioClock2_AddRef(IAudioClock2 *iface)
2874 ACImpl *This = impl_from_IAudioClock2(iface);
2875 return IAudioClient_AddRef(&This->IAudioClient_iface);
2878 static ULONG WINAPI AudioClock2_Release(IAudioClock2 *iface)
2880 ACImpl *This = impl_from_IAudioClock2(iface);
2881 return IAudioClient_Release(&This->IAudioClient_iface);
2884 static HRESULT WINAPI AudioClock2_GetDevicePosition(IAudioClock2 *iface,
2885 UINT64 *pos, UINT64 *qpctime)
2887 ACImpl *This = impl_from_IAudioClock2(iface);
2889 FIXME("(%p)->(%p, %p)\n", This, pos, qpctime);
2894 static const IAudioClock2Vtbl AudioClock2_Vtbl =
2896 AudioClock2_QueryInterface,
2898 AudioClock2_Release,
2899 AudioClock2_GetDevicePosition
2902 static AudioSessionWrapper *AudioSessionWrapper_Create(ACImpl *client)
2904 AudioSessionWrapper *ret;
2906 ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
2907 sizeof(AudioSessionWrapper));
2911 ret->IAudioSessionControl2_iface.lpVtbl = &AudioSessionControl2_Vtbl;
2912 ret->ISimpleAudioVolume_iface.lpVtbl = &SimpleAudioVolume_Vtbl;
2913 ret->IChannelAudioVolume_iface.lpVtbl = &ChannelAudioVolume_Vtbl;
2917 ret->client = client;
2919 ret->session = client->session;
2920 AudioClient_AddRef(&client->IAudioClient_iface);
2926 static HRESULT WINAPI AudioSessionControl_QueryInterface(
2927 IAudioSessionControl2 *iface, REFIID riid, void **ppv)
2929 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2935 if(IsEqualIID(riid, &IID_IUnknown) ||
2936 IsEqualIID(riid, &IID_IAudioSessionControl) ||
2937 IsEqualIID(riid, &IID_IAudioSessionControl2))
2940 IUnknown_AddRef((IUnknown*)*ppv);
2944 WARN("Unknown interface %s\n", debugstr_guid(riid));
2945 return E_NOINTERFACE;
2948 static ULONG WINAPI AudioSessionControl_AddRef(IAudioSessionControl2 *iface)
2950 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2952 ref = InterlockedIncrement(&This->ref);
2953 TRACE("(%p) Refcount now %u\n", This, ref);
2957 static ULONG WINAPI AudioSessionControl_Release(IAudioSessionControl2 *iface)
2959 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2961 ref = InterlockedDecrement(&This->ref);
2962 TRACE("(%p) Refcount now %u\n", This, ref);
2965 EnterCriticalSection(&This->client->lock);
2966 This->client->session_wrapper = NULL;
2967 LeaveCriticalSection(&This->client->lock);
2968 AudioClient_Release(&This->client->IAudioClient_iface);
2970 HeapFree(GetProcessHeap(), 0, This);
2975 static HRESULT WINAPI AudioSessionControl_GetState(IAudioSessionControl2 *iface,
2976 AudioSessionState *state)
2978 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2981 TRACE("(%p)->(%p)\n", This, state);
2984 return NULL_PTR_ERR;
2986 EnterCriticalSection(&g_sessions_lock);
2988 if(list_empty(&This->session->clients)){
2989 *state = AudioSessionStateExpired;
2990 LeaveCriticalSection(&g_sessions_lock);
2994 LIST_FOR_EACH_ENTRY(client, &This->session->clients, ACImpl, entry){
2995 EnterCriticalSection(&client->lock);
2996 if(client->started){
2997 *state = AudioSessionStateActive;
2998 LeaveCriticalSection(&client->lock);
2999 LeaveCriticalSection(&g_sessions_lock);
3002 LeaveCriticalSection(&client->lock);
3005 LeaveCriticalSection(&g_sessions_lock);
3007 *state = AudioSessionStateInactive;
3012 static HRESULT WINAPI AudioSessionControl_GetDisplayName(
3013 IAudioSessionControl2 *iface, WCHAR **name)
3015 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
3017 FIXME("(%p)->(%p) - stub\n", This, name);
3022 static HRESULT WINAPI AudioSessionControl_SetDisplayName(
3023 IAudioSessionControl2 *iface, const WCHAR *name, const GUID *session)
3025 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
3027 FIXME("(%p)->(%p, %s) - stub\n", This, name, debugstr_guid(session));
3032 static HRESULT WINAPI AudioSessionControl_GetIconPath(
3033 IAudioSessionControl2 *iface, WCHAR **path)
3035 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
3037 FIXME("(%p)->(%p) - stub\n", This, path);
3042 static HRESULT WINAPI AudioSessionControl_SetIconPath(
3043 IAudioSessionControl2 *iface, const WCHAR *path, const GUID *session)
3045 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
3047 FIXME("(%p)->(%p, %s) - stub\n", This, path, debugstr_guid(session));
3052 static HRESULT WINAPI AudioSessionControl_GetGroupingParam(
3053 IAudioSessionControl2 *iface, GUID *group)
3055 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
3057 FIXME("(%p)->(%p) - stub\n", This, group);
3062 static HRESULT WINAPI AudioSessionControl_SetGroupingParam(
3063 IAudioSessionControl2 *iface, const GUID *group, const GUID *session)
3065 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
3067 FIXME("(%p)->(%s, %s) - stub\n", This, debugstr_guid(group),
3068 debugstr_guid(session));
3073 static HRESULT WINAPI AudioSessionControl_RegisterAudioSessionNotification(
3074 IAudioSessionControl2 *iface, IAudioSessionEvents *events)
3076 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
3078 FIXME("(%p)->(%p) - stub\n", This, events);
3083 static HRESULT WINAPI AudioSessionControl_UnregisterAudioSessionNotification(
3084 IAudioSessionControl2 *iface, IAudioSessionEvents *events)
3086 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
3088 FIXME("(%p)->(%p) - stub\n", This, events);
3093 static HRESULT WINAPI AudioSessionControl_GetSessionIdentifier(
3094 IAudioSessionControl2 *iface, WCHAR **id)
3096 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
3098 FIXME("(%p)->(%p) - stub\n", This, id);
3103 static HRESULT WINAPI AudioSessionControl_GetSessionInstanceIdentifier(
3104 IAudioSessionControl2 *iface, WCHAR **id)
3106 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
3108 FIXME("(%p)->(%p) - stub\n", This, id);
3113 static HRESULT WINAPI AudioSessionControl_GetProcessId(
3114 IAudioSessionControl2 *iface, DWORD *pid)
3116 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
3118 TRACE("(%p)->(%p)\n", This, pid);
3123 *pid = GetCurrentProcessId();
3128 static HRESULT WINAPI AudioSessionControl_IsSystemSoundsSession(
3129 IAudioSessionControl2 *iface)
3131 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
3133 TRACE("(%p)\n", This);
3138 static HRESULT WINAPI AudioSessionControl_SetDuckingPreference(
3139 IAudioSessionControl2 *iface, BOOL optout)
3141 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
3143 TRACE("(%p)->(%d)\n", This, optout);
3148 static const IAudioSessionControl2Vtbl AudioSessionControl2_Vtbl =
3150 AudioSessionControl_QueryInterface,
3151 AudioSessionControl_AddRef,
3152 AudioSessionControl_Release,
3153 AudioSessionControl_GetState,
3154 AudioSessionControl_GetDisplayName,
3155 AudioSessionControl_SetDisplayName,
3156 AudioSessionControl_GetIconPath,
3157 AudioSessionControl_SetIconPath,
3158 AudioSessionControl_GetGroupingParam,
3159 AudioSessionControl_SetGroupingParam,
3160 AudioSessionControl_RegisterAudioSessionNotification,
3161 AudioSessionControl_UnregisterAudioSessionNotification,
3162 AudioSessionControl_GetSessionIdentifier,
3163 AudioSessionControl_GetSessionInstanceIdentifier,
3164 AudioSessionControl_GetProcessId,
3165 AudioSessionControl_IsSystemSoundsSession,
3166 AudioSessionControl_SetDuckingPreference
3169 static HRESULT WINAPI SimpleAudioVolume_QueryInterface(
3170 ISimpleAudioVolume *iface, REFIID riid, void **ppv)
3172 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
3178 if(IsEqualIID(riid, &IID_IUnknown) ||
3179 IsEqualIID(riid, &IID_ISimpleAudioVolume))
3182 IUnknown_AddRef((IUnknown*)*ppv);
3186 WARN("Unknown interface %s\n", debugstr_guid(riid));
3187 return E_NOINTERFACE;
3190 static ULONG WINAPI SimpleAudioVolume_AddRef(ISimpleAudioVolume *iface)
3192 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
3193 return AudioSessionControl_AddRef(&This->IAudioSessionControl2_iface);
3196 static ULONG WINAPI SimpleAudioVolume_Release(ISimpleAudioVolume *iface)
3198 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
3199 return AudioSessionControl_Release(&This->IAudioSessionControl2_iface);
3202 static HRESULT WINAPI SimpleAudioVolume_SetMasterVolume(
3203 ISimpleAudioVolume *iface, float level, const GUID *context)
3205 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
3206 AudioSession *session = This->session;
3208 TRACE("(%p)->(%f, %s)\n", session, level, wine_dbgstr_guid(context));
3210 if(level < 0.f || level > 1.f)
3211 return E_INVALIDARG;
3214 FIXME("Notifications not supported yet\n");
3216 TRACE("ALSA does not support volume control\n");
3218 EnterCriticalSection(&session->lock);
3220 session->master_vol = level;
3222 LeaveCriticalSection(&session->lock);
3227 static HRESULT WINAPI SimpleAudioVolume_GetMasterVolume(
3228 ISimpleAudioVolume *iface, float *level)
3230 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
3231 AudioSession *session = This->session;
3233 TRACE("(%p)->(%p)\n", session, level);
3236 return NULL_PTR_ERR;
3238 *level = session->master_vol;
3243 static HRESULT WINAPI SimpleAudioVolume_SetMute(ISimpleAudioVolume *iface,
3244 BOOL mute, const GUID *context)
3246 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
3247 AudioSession *session = This->session;
3249 TRACE("(%p)->(%u, %p)\n", session, mute, context);
3252 FIXME("Notifications not supported yet\n");
3254 session->mute = mute;
3259 static HRESULT WINAPI SimpleAudioVolume_GetMute(ISimpleAudioVolume *iface,
3262 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
3263 AudioSession *session = This->session;
3265 TRACE("(%p)->(%p)\n", session, mute);
3268 return NULL_PTR_ERR;
3270 *mute = session->mute;
3275 static const ISimpleAudioVolumeVtbl SimpleAudioVolume_Vtbl =
3277 SimpleAudioVolume_QueryInterface,
3278 SimpleAudioVolume_AddRef,
3279 SimpleAudioVolume_Release,
3280 SimpleAudioVolume_SetMasterVolume,
3281 SimpleAudioVolume_GetMasterVolume,
3282 SimpleAudioVolume_SetMute,
3283 SimpleAudioVolume_GetMute
3286 static HRESULT WINAPI AudioStreamVolume_QueryInterface(
3287 IAudioStreamVolume *iface, REFIID riid, void **ppv)
3289 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
3295 if(IsEqualIID(riid, &IID_IUnknown) ||
3296 IsEqualIID(riid, &IID_IAudioStreamVolume))
3299 IUnknown_AddRef((IUnknown*)*ppv);
3303 WARN("Unknown interface %s\n", debugstr_guid(riid));
3304 return E_NOINTERFACE;
3307 static ULONG WINAPI AudioStreamVolume_AddRef(IAudioStreamVolume *iface)
3309 ACImpl *This = impl_from_IAudioStreamVolume(iface);
3310 return IAudioClient_AddRef(&This->IAudioClient_iface);
3313 static ULONG WINAPI AudioStreamVolume_Release(IAudioStreamVolume *iface)
3315 ACImpl *This = impl_from_IAudioStreamVolume(iface);
3316 return IAudioClient_Release(&This->IAudioClient_iface);
3319 static HRESULT WINAPI AudioStreamVolume_GetChannelCount(
3320 IAudioStreamVolume *iface, UINT32 *out)
3322 ACImpl *This = impl_from_IAudioStreamVolume(iface);
3324 TRACE("(%p)->(%p)\n", This, out);
3329 *out = This->fmt->nChannels;
3334 static HRESULT WINAPI AudioStreamVolume_SetChannelVolume(
3335 IAudioStreamVolume *iface, UINT32 index, float level)
3337 ACImpl *This = impl_from_IAudioStreamVolume(iface);
3339 TRACE("(%p)->(%d, %f)\n", This, index, level);
3341 if(level < 0.f || level > 1.f)
3342 return E_INVALIDARG;
3344 if(index >= This->fmt->nChannels)
3345 return E_INVALIDARG;
3347 TRACE("ALSA does not support volume control\n");
3349 EnterCriticalSection(&This->lock);
3351 This->vols[index] = level;
3353 LeaveCriticalSection(&This->lock);
3358 static HRESULT WINAPI AudioStreamVolume_GetChannelVolume(
3359 IAudioStreamVolume *iface, UINT32 index, float *level)
3361 ACImpl *This = impl_from_IAudioStreamVolume(iface);
3363 TRACE("(%p)->(%d, %p)\n", This, index, level);
3368 if(index >= This->fmt->nChannels)
3369 return E_INVALIDARG;
3371 *level = This->vols[index];
3376 static HRESULT WINAPI AudioStreamVolume_SetAllVolumes(
3377 IAudioStreamVolume *iface, UINT32 count, const float *levels)
3379 ACImpl *This = impl_from_IAudioStreamVolume(iface);
3382 TRACE("(%p)->(%d, %p)\n", This, count, levels);
3387 if(count != This->fmt->nChannels)
3388 return E_INVALIDARG;
3390 TRACE("ALSA does not support volume control\n");
3392 EnterCriticalSection(&This->lock);
3394 for(i = 0; i < count; ++i)
3395 This->vols[i] = levels[i];
3397 LeaveCriticalSection(&This->lock);
3402 static HRESULT WINAPI AudioStreamVolume_GetAllVolumes(
3403 IAudioStreamVolume *iface, UINT32 count, float *levels)
3405 ACImpl *This = impl_from_IAudioStreamVolume(iface);
3408 TRACE("(%p)->(%d, %p)\n", This, count, levels);
3413 if(count != This->fmt->nChannels)
3414 return E_INVALIDARG;
3416 EnterCriticalSection(&This->lock);
3418 for(i = 0; i < count; ++i)
3419 levels[i] = This->vols[i];
3421 LeaveCriticalSection(&This->lock);
3426 static const IAudioStreamVolumeVtbl AudioStreamVolume_Vtbl =
3428 AudioStreamVolume_QueryInterface,
3429 AudioStreamVolume_AddRef,
3430 AudioStreamVolume_Release,
3431 AudioStreamVolume_GetChannelCount,
3432 AudioStreamVolume_SetChannelVolume,
3433 AudioStreamVolume_GetChannelVolume,
3434 AudioStreamVolume_SetAllVolumes,
3435 AudioStreamVolume_GetAllVolumes
3438 static HRESULT WINAPI ChannelAudioVolume_QueryInterface(
3439 IChannelAudioVolume *iface, REFIID riid, void **ppv)
3441 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
3447 if(IsEqualIID(riid, &IID_IUnknown) ||
3448 IsEqualIID(riid, &IID_IChannelAudioVolume))
3451 IUnknown_AddRef((IUnknown*)*ppv);
3455 WARN("Unknown interface %s\n", debugstr_guid(riid));
3456 return E_NOINTERFACE;
3459 static ULONG WINAPI ChannelAudioVolume_AddRef(IChannelAudioVolume *iface)
3461 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
3462 return AudioSessionControl_AddRef(&This->IAudioSessionControl2_iface);
3465 static ULONG WINAPI ChannelAudioVolume_Release(IChannelAudioVolume *iface)
3467 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
3468 return AudioSessionControl_Release(&This->IAudioSessionControl2_iface);
3471 static HRESULT WINAPI ChannelAudioVolume_GetChannelCount(
3472 IChannelAudioVolume *iface, UINT32 *out)
3474 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
3475 AudioSession *session = This->session;
3477 TRACE("(%p)->(%p)\n", session, out);
3480 return NULL_PTR_ERR;
3482 *out = session->channel_count;
3487 static HRESULT WINAPI ChannelAudioVolume_SetChannelVolume(
3488 IChannelAudioVolume *iface, UINT32 index, float level,
3489 const GUID *context)
3491 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
3492 AudioSession *session = This->session;
3494 TRACE("(%p)->(%d, %f, %s)\n", session, index, level,
3495 wine_dbgstr_guid(context));
3497 if(level < 0.f || level > 1.f)
3498 return E_INVALIDARG;
3500 if(index >= session->channel_count)
3501 return E_INVALIDARG;
3504 FIXME("Notifications not supported yet\n");
3506 TRACE("ALSA does not support volume control\n");
3508 EnterCriticalSection(&session->lock);
3510 session->channel_vols[index] = level;
3512 LeaveCriticalSection(&session->lock);
3517 static HRESULT WINAPI ChannelAudioVolume_GetChannelVolume(
3518 IChannelAudioVolume *iface, UINT32 index, float *level)
3520 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
3521 AudioSession *session = This->session;
3523 TRACE("(%p)->(%d, %p)\n", session, index, level);
3526 return NULL_PTR_ERR;
3528 if(index >= session->channel_count)
3529 return E_INVALIDARG;
3531 *level = session->channel_vols[index];
3536 static HRESULT WINAPI ChannelAudioVolume_SetAllVolumes(
3537 IChannelAudioVolume *iface, UINT32 count, const float *levels,
3538 const GUID *context)
3540 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
3541 AudioSession *session = This->session;
3544 TRACE("(%p)->(%d, %p, %s)\n", session, count, levels,
3545 wine_dbgstr_guid(context));
3548 return NULL_PTR_ERR;
3550 if(count != session->channel_count)
3551 return E_INVALIDARG;
3554 FIXME("Notifications not supported yet\n");
3556 TRACE("ALSA does not support volume control\n");
3558 EnterCriticalSection(&session->lock);
3560 for(i = 0; i < count; ++i)
3561 session->channel_vols[i] = levels[i];
3563 LeaveCriticalSection(&session->lock);
3568 static HRESULT WINAPI ChannelAudioVolume_GetAllVolumes(
3569 IChannelAudioVolume *iface, UINT32 count, float *levels)
3571 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
3572 AudioSession *session = This->session;
3575 TRACE("(%p)->(%d, %p)\n", session, count, levels);
3578 return NULL_PTR_ERR;
3580 if(count != session->channel_count)
3581 return E_INVALIDARG;
3583 for(i = 0; i < count; ++i)
3584 levels[i] = session->channel_vols[i];
3589 static const IChannelAudioVolumeVtbl ChannelAudioVolume_Vtbl =
3591 ChannelAudioVolume_QueryInterface,
3592 ChannelAudioVolume_AddRef,
3593 ChannelAudioVolume_Release,
3594 ChannelAudioVolume_GetChannelCount,
3595 ChannelAudioVolume_SetChannelVolume,
3596 ChannelAudioVolume_GetChannelVolume,
3597 ChannelAudioVolume_SetAllVolumes,
3598 ChannelAudioVolume_GetAllVolumes
3601 static HRESULT WINAPI AudioSessionManager_QueryInterface(IAudioSessionManager2 *iface,
3602 REFIID riid, void **ppv)
3604 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
3610 if(IsEqualIID(riid, &IID_IUnknown) ||
3611 IsEqualIID(riid, &IID_IAudioSessionManager) ||
3612 IsEqualIID(riid, &IID_IAudioSessionManager2))
3615 IUnknown_AddRef((IUnknown*)*ppv);
3619 WARN("Unknown interface %s\n", debugstr_guid(riid));
3620 return E_NOINTERFACE;
3623 static ULONG WINAPI AudioSessionManager_AddRef(IAudioSessionManager2 *iface)
3625 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3627 ref = InterlockedIncrement(&This->ref);
3628 TRACE("(%p) Refcount now %u\n", This, ref);
3632 static ULONG WINAPI AudioSessionManager_Release(IAudioSessionManager2 *iface)
3634 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3636 ref = InterlockedDecrement(&This->ref);
3637 TRACE("(%p) Refcount now %u\n", This, ref);
3639 HeapFree(GetProcessHeap(), 0, This);
3643 static HRESULT WINAPI AudioSessionManager_GetAudioSessionControl(
3644 IAudioSessionManager2 *iface, const GUID *session_guid, DWORD flags,
3645 IAudioSessionControl **out)
3647 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3648 AudioSession *session;
3649 AudioSessionWrapper *wrapper;
3652 TRACE("(%p)->(%s, %x, %p)\n", This, debugstr_guid(session_guid),
3655 hr = get_audio_session(session_guid, This->device, 0, &session);
3659 wrapper = AudioSessionWrapper_Create(NULL);
3661 return E_OUTOFMEMORY;
3663 wrapper->session = session;
3665 *out = (IAudioSessionControl*)&wrapper->IAudioSessionControl2_iface;
3670 static HRESULT WINAPI AudioSessionManager_GetSimpleAudioVolume(
3671 IAudioSessionManager2 *iface, const GUID *session_guid, DWORD flags,
3672 ISimpleAudioVolume **out)
3674 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3675 AudioSession *session;
3676 AudioSessionWrapper *wrapper;
3679 TRACE("(%p)->(%s, %x, %p)\n", This, debugstr_guid(session_guid),
3682 hr = get_audio_session(session_guid, This->device, 0, &session);
3686 wrapper = AudioSessionWrapper_Create(NULL);
3688 return E_OUTOFMEMORY;
3690 wrapper->session = session;
3692 *out = &wrapper->ISimpleAudioVolume_iface;
3697 static HRESULT WINAPI AudioSessionManager_GetSessionEnumerator(
3698 IAudioSessionManager2 *iface, IAudioSessionEnumerator **out)
3700 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3701 FIXME("(%p)->(%p) - stub\n", This, out);
3705 static HRESULT WINAPI AudioSessionManager_RegisterSessionNotification(
3706 IAudioSessionManager2 *iface, IAudioSessionNotification *notification)
3708 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3709 FIXME("(%p)->(%p) - stub\n", This, notification);
3713 static HRESULT WINAPI AudioSessionManager_UnregisterSessionNotification(
3714 IAudioSessionManager2 *iface, IAudioSessionNotification *notification)
3716 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3717 FIXME("(%p)->(%p) - stub\n", This, notification);
3721 static HRESULT WINAPI AudioSessionManager_RegisterDuckNotification(
3722 IAudioSessionManager2 *iface, const WCHAR *session_id,
3723 IAudioVolumeDuckNotification *notification)
3725 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3726 FIXME("(%p)->(%p) - stub\n", This, notification);
3730 static HRESULT WINAPI AudioSessionManager_UnregisterDuckNotification(
3731 IAudioSessionManager2 *iface,
3732 IAudioVolumeDuckNotification *notification)
3734 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3735 FIXME("(%p)->(%p) - stub\n", This, notification);
3739 static const IAudioSessionManager2Vtbl AudioSessionManager2_Vtbl =
3741 AudioSessionManager_QueryInterface,
3742 AudioSessionManager_AddRef,
3743 AudioSessionManager_Release,
3744 AudioSessionManager_GetAudioSessionControl,
3745 AudioSessionManager_GetSimpleAudioVolume,
3746 AudioSessionManager_GetSessionEnumerator,
3747 AudioSessionManager_RegisterSessionNotification,
3748 AudioSessionManager_UnregisterSessionNotification,
3749 AudioSessionManager_RegisterDuckNotification,
3750 AudioSessionManager_UnregisterDuckNotification
3753 HRESULT WINAPI AUDDRV_GetAudioSessionManager(IMMDevice *device,
3754 IAudioSessionManager2 **out)
3758 This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(SessionMgr));
3760 return E_OUTOFMEMORY;
3762 This->IAudioSessionManager2_iface.lpVtbl = &AudioSessionManager2_Vtbl;
3763 This->device = device;
3766 *out = &This->IAudioSessionManager2_iface;