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
);
981 format
= SND_PCM_FORMAT_UNKNOWN
;
984 }else if(fmt
->wFormatTag
== WAVE_FORMAT_IEEE_FLOAT
||
985 (fmt
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
&&
986 IsEqualGUID(&fmtex
->SubFormat
, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
))){
987 if(fmt
->wBitsPerSample
== 32)
988 format
= SND_PCM_FORMAT_FLOAT_LE
;
989 else if(fmt
->wBitsPerSample
== 64)
990 format
= SND_PCM_FORMAT_FLOAT64_LE
;
992 WARN("Unsupported float size: %u\n", fmt
->wBitsPerSample
);
994 WARN("Unknown wave format: %04x\n", fmt
->wFormatTag
);
998 static void session_init_vols(AudioSession
*session
, UINT channels
)
1000 if(session
->channel_count
< channels
){
1003 if(session
->channel_vols
)
1004 session
->channel_vols
= HeapReAlloc(GetProcessHeap(), 0,
1005 session
->channel_vols
, sizeof(float) * channels
);
1007 session
->channel_vols
= HeapAlloc(GetProcessHeap(), 0,
1008 sizeof(float) * channels
);
1009 if(!session
->channel_vols
)
1012 for(i
= session
->channel_count
; i
< channels
; ++i
)
1013 session
->channel_vols
[i
] = 1.f
;
1015 session
->channel_count
= channels
;
1019 static AudioSession
*create_session(const GUID
*guid
, IMMDevice
*device
,
1024 ret
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(AudioSession
));
1028 memcpy(&ret
->guid
, guid
, sizeof(GUID
));
1030 ret
->device
= device
;
1032 list_init(&ret
->clients
);
1034 list_add_head(&g_sessions
, &ret
->entry
);
1036 InitializeCriticalSection(&ret
->lock
);
1037 ret
->lock
.DebugInfo
->Spare
[0] = (DWORD_PTR
)(__FILE__
": AudioSession.lock");
1039 session_init_vols(ret
, num_channels
);
1041 ret
->master_vol
= 1.f
;
1046 /* if channels == 0, then this will return or create a session with
1047 * matching dataflow and GUID. otherwise, channels must also match */
1048 static HRESULT
get_audio_session(const GUID
*sessionguid
,
1049 IMMDevice
*device
, UINT channels
, AudioSession
**out
)
1051 AudioSession
*session
;
1053 if(!sessionguid
|| IsEqualGUID(sessionguid
, &GUID_NULL
)){
1054 *out
= create_session(&GUID_NULL
, device
, channels
);
1056 return E_OUTOFMEMORY
;
1062 LIST_FOR_EACH_ENTRY(session
, &g_sessions
, AudioSession
, entry
){
1063 if(session
->device
== device
&&
1064 IsEqualGUID(sessionguid
, &session
->guid
)){
1065 session_init_vols(session
, channels
);
1072 *out
= create_session(sessionguid
, device
, channels
);
1074 return E_OUTOFMEMORY
;
1080 static int alsa_channel_index(DWORD flag
)
1083 case SPEAKER_FRONT_LEFT
:
1085 case SPEAKER_FRONT_RIGHT
:
1087 case SPEAKER_BACK_LEFT
:
1089 case SPEAKER_BACK_RIGHT
:
1091 case SPEAKER_FRONT_CENTER
:
1093 case SPEAKER_LOW_FREQUENCY
:
1095 case SPEAKER_SIDE_LEFT
:
1097 case SPEAKER_SIDE_RIGHT
:
1103 static BOOL
need_remapping(ACImpl
*This
, const WAVEFORMATEX
*fmt
)
1106 for(i
= 0; i
< fmt
->nChannels
; ++i
){
1107 if(This
->alsa_channel_map
[i
] != i
)
1113 static DWORD
get_channel_mask(unsigned int channels
)
1119 return KSAUDIO_SPEAKER_MONO
;
1121 return KSAUDIO_SPEAKER_STEREO
;
1123 return KSAUDIO_SPEAKER_STEREO
| SPEAKER_LOW_FREQUENCY
;
1125 return KSAUDIO_SPEAKER_QUAD
; /* not _SURROUND */
1127 return KSAUDIO_SPEAKER_QUAD
| SPEAKER_LOW_FREQUENCY
;
1129 return KSAUDIO_SPEAKER_5POINT1
; /* not 5POINT1_SURROUND */
1131 return KSAUDIO_SPEAKER_5POINT1
| SPEAKER_BACK_CENTER
;
1133 return KSAUDIO_SPEAKER_7POINT1_SURROUND
; /* Vista deprecates 7POINT1 */
1135 FIXME("Unknown speaker configuration: %u\n", channels
);
1139 static HRESULT
map_channels(ACImpl
*This
, const WAVEFORMATEX
*fmt
)
1141 if(fmt
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
|| fmt
->nChannels
> 2){
1142 WAVEFORMATEXTENSIBLE
*fmtex
= (void*)fmt
;
1143 DWORD mask
, flag
= SPEAKER_FRONT_LEFT
;
1146 if(fmt
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
&&
1147 fmtex
->dwChannelMask
!= 0)
1148 mask
= fmtex
->dwChannelMask
;
1150 mask
= get_channel_mask(fmt
->nChannels
);
1152 This
->alsa_channels
= 0;
1154 while(i
< fmt
->nChannels
&& !(flag
& SPEAKER_RESERVED
)){
1156 This
->alsa_channel_map
[i
] = alsa_channel_index(flag
);
1157 TRACE("Mapping mmdevapi channel %u (0x%x) to ALSA channel %d\n",
1158 i
, flag
, This
->alsa_channel_map
[i
]);
1159 if(This
->alsa_channel_map
[i
] >= This
->alsa_channels
)
1160 This
->alsa_channels
= This
->alsa_channel_map
[i
] + 1;
1166 while(i
< fmt
->nChannels
){
1167 This
->alsa_channel_map
[i
] = This
->alsa_channels
;
1168 TRACE("Mapping mmdevapi channel %u to ALSA channel %d\n",
1169 i
, This
->alsa_channel_map
[i
]);
1170 ++This
->alsa_channels
;
1174 for(i
= 0; i
< fmt
->nChannels
; ++i
){
1175 if(This
->alsa_channel_map
[i
] == -1){
1176 This
->alsa_channel_map
[i
] = This
->alsa_channels
;
1177 ++This
->alsa_channels
;
1178 TRACE("Remapping mmdevapi channel %u to ALSA channel %d\n",
1179 i
, This
->alsa_channel_map
[i
]);
1183 This
->need_remapping
= need_remapping(This
, fmt
);
1185 TRACE("need_remapping: %u, alsa_channels: %d\n", This
->need_remapping
, This
->alsa_channels
);
1187 This
->need_remapping
= FALSE
;
1188 This
->alsa_channels
= fmt
->nChannels
;
1189 TRACE("need_remapping: %u, alsa_channels: %d\n", This
->need_remapping
, This
->alsa_channels
);
1195 static HRESULT WINAPI
AudioClient_Initialize(IAudioClient
*iface
,
1196 AUDCLNT_SHAREMODE mode
, DWORD flags
, REFERENCE_TIME duration
,
1197 REFERENCE_TIME period
, const WAVEFORMATEX
*fmt
,
1198 const GUID
*sessionguid
)
1200 ACImpl
*This
= impl_from_IAudioClient(iface
);
1201 snd_pcm_sw_params_t
*sw_params
= NULL
;
1202 snd_pcm_format_t format
;
1203 unsigned int rate
, alsa_period_us
;
1207 TRACE("(%p)->(%x, %x, %s, %s, %p, %s)\n", This
, mode
, flags
,
1208 wine_dbgstr_longlong(duration
), wine_dbgstr_longlong(period
), fmt
, debugstr_guid(sessionguid
));
1213 if(mode
!= AUDCLNT_SHAREMODE_SHARED
&& mode
!= AUDCLNT_SHAREMODE_EXCLUSIVE
)
1214 return AUDCLNT_E_NOT_INITIALIZED
;
1216 if(flags
& ~(AUDCLNT_STREAMFLAGS_CROSSPROCESS
|
1217 AUDCLNT_STREAMFLAGS_LOOPBACK
|
1218 AUDCLNT_STREAMFLAGS_EVENTCALLBACK
|
1219 AUDCLNT_STREAMFLAGS_NOPERSIST
|
1220 AUDCLNT_STREAMFLAGS_RATEADJUST
|
1221 AUDCLNT_SESSIONFLAGS_EXPIREWHENUNOWNED
|
1222 AUDCLNT_SESSIONFLAGS_DISPLAY_HIDE
|
1223 AUDCLNT_SESSIONFLAGS_DISPLAY_HIDEWHENEXPIRED
)){
1224 TRACE("Unknown flags: %08x\n", flags
);
1225 return E_INVALIDARG
;
1228 if(mode
== AUDCLNT_SHAREMODE_SHARED
){
1229 period
= DefaultPeriod
;
1230 if( duration
< 3 * period
)
1231 duration
= 3 * period
;
1234 period
= DefaultPeriod
; /* not minimum */
1235 if(period
< MinimumPeriod
|| period
> 5000000)
1236 return AUDCLNT_E_INVALID_DEVICE_PERIOD
;
1237 if(duration
> 20000000) /* the smaller the period, the lower this limit */
1238 return AUDCLNT_E_BUFFER_SIZE_ERROR
;
1239 if(flags
& AUDCLNT_STREAMFLAGS_EVENTCALLBACK
){
1240 if(duration
!= period
)
1241 return AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL
;
1242 FIXME("EXCLUSIVE mode with EVENTCALLBACK\n");
1243 return AUDCLNT_E_DEVICE_IN_USE
;
1245 if( duration
< 8 * period
)
1246 duration
= 8 * period
; /* may grow above 2s */
1250 EnterCriticalSection(&This
->lock
);
1253 LeaveCriticalSection(&This
->lock
);
1254 return AUDCLNT_E_ALREADY_INITIALIZED
;
1259 if(FAILED(map_channels(This
, fmt
))){
1260 WARN("map_channels failed\n");
1261 hr
= AUDCLNT_E_ENDPOINT_CREATE_FAILED
;
1265 if((err
= snd_pcm_hw_params_any(This
->pcm_handle
, This
->hw_params
)) < 0){
1266 WARN("Unable to get hw_params: %d (%s)\n", err
, snd_strerror(err
));
1267 hr
= AUDCLNT_E_ENDPOINT_CREATE_FAILED
;
1271 if((err
= snd_pcm_hw_params_set_access(This
->pcm_handle
, This
->hw_params
,
1272 SND_PCM_ACCESS_RW_INTERLEAVED
)) < 0){
1273 WARN("Unable to set access: %d (%s)\n", err
, snd_strerror(err
));
1274 hr
= AUDCLNT_E_ENDPOINT_CREATE_FAILED
;
1278 format
= alsa_format(fmt
);
1279 if (format
== SND_PCM_FORMAT_UNKNOWN
){
1280 hr
= AUDCLNT_E_UNSUPPORTED_FORMAT
;
1284 if((err
= snd_pcm_hw_params_set_format(This
->pcm_handle
, This
->hw_params
,
1286 WARN("Unable to set ALSA format to %u: %d (%s)\n", format
, err
,
1288 hr
= AUDCLNT_E_UNSUPPORTED_FORMAT
;
1292 This
->alsa_format
= format
;
1294 rate
= fmt
->nSamplesPerSec
;
1295 if((err
= snd_pcm_hw_params_set_rate_near(This
->pcm_handle
, This
->hw_params
,
1297 WARN("Unable to set rate to %u: %d (%s)\n", rate
, err
,
1299 hr
= AUDCLNT_E_UNSUPPORTED_FORMAT
;
1303 if((err
= snd_pcm_hw_params_set_channels(This
->pcm_handle
, This
->hw_params
,
1304 This
->alsa_channels
)) < 0){
1305 WARN("Unable to set channels to %u: %d (%s)\n", fmt
->nChannels
, err
,
1307 hr
= AUDCLNT_E_UNSUPPORTED_FORMAT
;
1311 This
->mmdev_period_rt
= period
;
1312 alsa_period_us
= This
->mmdev_period_rt
/ 10;
1313 if((err
= snd_pcm_hw_params_set_period_time_near(This
->pcm_handle
,
1314 This
->hw_params
, &alsa_period_us
, NULL
)) < 0)
1315 WARN("Unable to set period time near %u: %d (%s)\n", alsa_period_us
,
1316 err
, snd_strerror(err
));
1317 /* ALSA updates the output variable alsa_period_us */
1319 This
->mmdev_period_frames
= MulDiv(fmt
->nSamplesPerSec
,
1320 This
->mmdev_period_rt
, 10000000);
1322 /* Buffer 4 ALSA periods if large enough, else 4 mmdevapi periods */
1323 This
->alsa_bufsize_frames
= This
->mmdev_period_frames
* 4;
1324 if(err
< 0 || alsa_period_us
< period
/ 10)
1325 err
= snd_pcm_hw_params_set_buffer_size_near(This
->pcm_handle
,
1326 This
->hw_params
, &This
->alsa_bufsize_frames
);
1328 unsigned int periods
= 4;
1329 err
= snd_pcm_hw_params_set_periods_near(This
->pcm_handle
, This
->hw_params
, &periods
, NULL
);
1332 WARN("Unable to set buffer size: %d (%s)\n", err
, snd_strerror(err
));
1334 if((err
= snd_pcm_hw_params(This
->pcm_handle
, This
->hw_params
)) < 0){
1335 WARN("Unable to set hw params: %d (%s)\n", err
, snd_strerror(err
));
1336 hr
= AUDCLNT_E_ENDPOINT_CREATE_FAILED
;
1340 if((err
= snd_pcm_hw_params_get_period_size(This
->hw_params
,
1341 &This
->alsa_period_frames
, NULL
)) < 0){
1342 WARN("Unable to get period size: %d (%s)\n", err
, snd_strerror(err
));
1343 hr
= AUDCLNT_E_ENDPOINT_CREATE_FAILED
;
1347 if((err
= snd_pcm_hw_params_get_buffer_size(This
->hw_params
,
1348 &This
->alsa_bufsize_frames
)) < 0){
1349 WARN("Unable to get buffer size: %d (%s)\n", err
, snd_strerror(err
));
1350 hr
= AUDCLNT_E_ENDPOINT_CREATE_FAILED
;
1354 sw_params
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, snd_pcm_sw_params_sizeof());
1360 if((err
= snd_pcm_sw_params_current(This
->pcm_handle
, sw_params
)) < 0){
1361 WARN("Unable to get sw_params: %d (%s)\n", err
, snd_strerror(err
));
1362 hr
= AUDCLNT_E_ENDPOINT_CREATE_FAILED
;
1366 if((err
= snd_pcm_sw_params_set_start_threshold(This
->pcm_handle
,
1367 sw_params
, 1)) < 0){
1368 WARN("Unable set start threshold to 0: %d (%s)\n", err
, snd_strerror(err
));
1369 hr
= AUDCLNT_E_ENDPOINT_CREATE_FAILED
;
1373 if((err
= snd_pcm_sw_params_set_stop_threshold(This
->pcm_handle
,
1374 sw_params
, This
->alsa_bufsize_frames
)) < 0){
1375 WARN("Unable set stop threshold to %lu: %d (%s)\n",
1376 This
->alsa_bufsize_frames
, err
, snd_strerror(err
));
1377 hr
= AUDCLNT_E_ENDPOINT_CREATE_FAILED
;
1381 if((err
= snd_pcm_sw_params(This
->pcm_handle
, sw_params
)) < 0){
1382 WARN("Unable to set sw params: %d (%s)\n", err
, snd_strerror(err
));
1383 hr
= AUDCLNT_E_ENDPOINT_CREATE_FAILED
;
1387 if((err
= snd_pcm_prepare(This
->pcm_handle
)) < 0){
1388 WARN("Unable to prepare device: %d (%s)\n", err
, snd_strerror(err
));
1389 hr
= AUDCLNT_E_ENDPOINT_CREATE_FAILED
;
1393 /* Bear in mind weird situations where
1394 * ALSA period (50ms) > mmdevapi buffer (3x10ms)
1395 * or surprising rounding as seen with 22050x8x1 with Pulse:
1396 * ALSA period 220 vs. 221 frames in mmdevapi and
1397 * buffer 883 vs. 2205 frames in mmdevapi! */
1398 This
->bufsize_frames
= MulDiv(duration
, fmt
->nSamplesPerSec
, 10000000);
1399 This
->hidden_frames
= This
->alsa_period_frames
+ This
->mmdev_period_frames
+
1400 MulDiv(fmt
->nSamplesPerSec
, EXTRA_SAFE_RT
, 10000000);
1402 /* Check if the ALSA buffer is so small that it will run out before
1403 * the next MMDevAPI period tick occurs. Allow a little wiggle room
1404 * with 120% of the period time. */
1405 if(This
->alsa_bufsize_frames
< 1.2 * This
->mmdev_period_frames
)
1406 FIXME("ALSA buffer time is too small. Expect underruns. (%lu < %u * 1.2)\n",
1407 This
->alsa_bufsize_frames
, This
->mmdev_period_frames
);
1409 This
->local_buffer
= HeapAlloc(GetProcessHeap(), 0,
1410 This
->bufsize_frames
* fmt
->nBlockAlign
);
1411 if(!This
->local_buffer
){
1415 if (fmt
->wBitsPerSample
== 8)
1416 memset(This
->local_buffer
, 128, This
->bufsize_frames
* fmt
->nBlockAlign
);
1418 memset(This
->local_buffer
, 0, This
->bufsize_frames
* fmt
->nBlockAlign
);
1420 This
->fmt
= clone_format(fmt
);
1426 This
->vols
= HeapAlloc(GetProcessHeap(), 0, fmt
->nChannels
* sizeof(float));
1432 for(i
= 0; i
< fmt
->nChannels
; ++i
)
1433 This
->vols
[i
] = 1.f
;
1436 This
->flags
= flags
;
1438 EnterCriticalSection(&g_sessions_lock
);
1440 hr
= get_audio_session(sessionguid
, This
->parent
, fmt
->nChannels
,
1443 LeaveCriticalSection(&g_sessions_lock
);
1447 list_add_tail(&This
->session
->clients
, &This
->entry
);
1449 LeaveCriticalSection(&g_sessions_lock
);
1451 This
->initted
= TRUE
;
1453 TRACE("ALSA period: %lu frames\n", This
->alsa_period_frames
);
1454 TRACE("ALSA buffer: %lu frames\n", This
->alsa_bufsize_frames
);
1455 TRACE("MMDevice period: %u frames\n", This
->mmdev_period_frames
);
1456 TRACE("MMDevice buffer: %u frames\n", This
->bufsize_frames
);
1459 HeapFree(GetProcessHeap(), 0, sw_params
);
1461 HeapFree(GetProcessHeap(), 0, This
->local_buffer
);
1462 This
->local_buffer
= NULL
;
1463 CoTaskMemFree(This
->fmt
);
1465 HeapFree(GetProcessHeap(), 0, This
->vols
);
1469 LeaveCriticalSection(&This
->lock
);
1474 static HRESULT WINAPI
AudioClient_GetBufferSize(IAudioClient
*iface
,
1477 ACImpl
*This
= impl_from_IAudioClient(iface
);
1479 TRACE("(%p)->(%p)\n", This
, out
);
1484 EnterCriticalSection(&This
->lock
);
1487 LeaveCriticalSection(&This
->lock
);
1488 return AUDCLNT_E_NOT_INITIALIZED
;
1491 *out
= This
->bufsize_frames
;
1493 LeaveCriticalSection(&This
->lock
);
1498 static HRESULT WINAPI
AudioClient_GetStreamLatency(IAudioClient
*iface
,
1499 REFERENCE_TIME
*latency
)
1501 ACImpl
*This
= impl_from_IAudioClient(iface
);
1503 TRACE("(%p)->(%p)\n", This
, latency
);
1508 EnterCriticalSection(&This
->lock
);
1511 LeaveCriticalSection(&This
->lock
);
1512 return AUDCLNT_E_NOT_INITIALIZED
;
1515 /* Hide some frames in the ALSA buffer. Allows us to return GetCurrentPadding=0
1516 * yet have enough data left to play (as if it were in native's mixer). Add:
1517 * + mmdevapi_period such that at the end of it, ALSA still has data;
1518 * + EXTRA_SAFE (~4ms) to allow for late callback invocation / fluctuation;
1519 * + alsa_period such that ALSA always has at least one period to play. */
1520 if(This
->dataflow
== eRender
)
1521 *latency
= MulDiv(This
->hidden_frames
, 10000000, This
->fmt
->nSamplesPerSec
);
1523 *latency
= MulDiv(This
->alsa_period_frames
, 10000000, This
->fmt
->nSamplesPerSec
)
1524 + This
->mmdev_period_rt
;
1526 LeaveCriticalSection(&This
->lock
);
1531 static HRESULT WINAPI
AudioClient_GetCurrentPadding(IAudioClient
*iface
,
1534 ACImpl
*This
= impl_from_IAudioClient(iface
);
1536 TRACE("(%p)->(%p)\n", This
, out
);
1541 EnterCriticalSection(&This
->lock
);
1544 LeaveCriticalSection(&This
->lock
);
1545 return AUDCLNT_E_NOT_INITIALIZED
;
1548 /* padding is solely updated at callback time in shared mode */
1549 *out
= This
->held_frames
;
1551 LeaveCriticalSection(&This
->lock
);
1553 TRACE("pad: %u\n", *out
);
1558 static HRESULT WINAPI
AudioClient_IsFormatSupported(IAudioClient
*iface
,
1559 AUDCLNT_SHAREMODE mode
, const WAVEFORMATEX
*fmt
,
1562 ACImpl
*This
= impl_from_IAudioClient(iface
);
1563 snd_pcm_format_mask_t
*formats
= NULL
;
1564 snd_pcm_format_t format
;
1566 WAVEFORMATEX
*closest
= NULL
;
1567 unsigned int max
= 0, min
= 0;
1570 TRACE("(%p)->(%x, %p, %p)\n", This
, mode
, fmt
, out
);
1572 if(!fmt
|| (mode
== AUDCLNT_SHAREMODE_SHARED
&& !out
))
1575 if(mode
!= AUDCLNT_SHAREMODE_SHARED
&& mode
!= AUDCLNT_SHAREMODE_EXCLUSIVE
)
1576 return E_INVALIDARG
;
1578 if(fmt
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
&&
1579 fmt
->cbSize
< sizeof(WAVEFORMATEXTENSIBLE
) - sizeof(WAVEFORMATEX
))
1580 return E_INVALIDARG
;
1586 if(mode
!= AUDCLNT_SHAREMODE_SHARED
)
1590 EnterCriticalSection(&This
->lock
);
1592 if((err
= snd_pcm_hw_params_any(This
->pcm_handle
, This
->hw_params
)) < 0){
1593 hr
= AUDCLNT_E_DEVICE_INVALIDATED
;
1597 formats
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
,
1598 snd_pcm_format_mask_sizeof());
1604 snd_pcm_hw_params_get_format_mask(This
->hw_params
, formats
);
1605 format
= alsa_format(fmt
);
1606 if (format
== SND_PCM_FORMAT_UNKNOWN
||
1607 !snd_pcm_format_mask_test(formats
, format
)){
1608 hr
= AUDCLNT_E_UNSUPPORTED_FORMAT
;
1612 closest
= clone_format(fmt
);
1618 if((err
= snd_pcm_hw_params_get_rate_min(This
->hw_params
, &min
, NULL
)) < 0){
1619 hr
= AUDCLNT_E_DEVICE_INVALIDATED
;
1620 WARN("Unable to get min rate: %d (%s)\n", err
, snd_strerror(err
));
1624 if((err
= snd_pcm_hw_params_get_rate_max(This
->hw_params
, &max
, NULL
)) < 0){
1625 hr
= AUDCLNT_E_DEVICE_INVALIDATED
;
1626 WARN("Unable to get max rate: %d (%s)\n", err
, snd_strerror(err
));
1630 if(fmt
->nSamplesPerSec
< min
|| fmt
->nSamplesPerSec
> max
){
1631 hr
= AUDCLNT_E_UNSUPPORTED_FORMAT
;
1635 if((err
= snd_pcm_hw_params_get_channels_min(This
->hw_params
, &min
)) < 0){
1636 hr
= AUDCLNT_E_DEVICE_INVALIDATED
;
1637 WARN("Unable to get min channels: %d (%s)\n", err
, snd_strerror(err
));
1641 if((err
= snd_pcm_hw_params_get_channels_max(This
->hw_params
, &max
)) < 0){
1642 hr
= AUDCLNT_E_DEVICE_INVALIDATED
;
1643 WARN("Unable to get max channels: %d (%s)\n", err
, snd_strerror(err
));
1646 if(fmt
->nChannels
> max
){
1648 closest
->nChannels
= max
;
1649 }else if(fmt
->nChannels
< min
){
1651 closest
->nChannels
= min
;
1654 if(FAILED(map_channels(This
, fmt
))){
1655 hr
= AUDCLNT_E_DEVICE_INVALIDATED
;
1656 WARN("map_channels failed\n");
1659 if(This
->alsa_channels
> max
){
1661 closest
->nChannels
= max
;
1664 if(closest
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
)
1665 ((WAVEFORMATEXTENSIBLE
*)closest
)->dwChannelMask
= get_channel_mask(closest
->nChannels
);
1668 LeaveCriticalSection(&This
->lock
);
1669 HeapFree(GetProcessHeap(), 0, formats
);
1671 if(hr
== S_FALSE
&& !out
)
1672 hr
= AUDCLNT_E_UNSUPPORTED_FORMAT
;
1674 if(hr
== S_FALSE
&& out
) {
1675 closest
->nBlockAlign
=
1676 closest
->nChannels
* closest
->wBitsPerSample
/ 8;
1677 closest
->nAvgBytesPerSec
=
1678 closest
->nBlockAlign
* closest
->nSamplesPerSec
;
1681 CoTaskMemFree(closest
);
1683 TRACE("returning: %08x\n", hr
);
1687 static HRESULT WINAPI
AudioClient_GetMixFormat(IAudioClient
*iface
,
1688 WAVEFORMATEX
**pwfx
)
1690 ACImpl
*This
= impl_from_IAudioClient(iface
);
1691 WAVEFORMATEXTENSIBLE
*fmt
;
1692 snd_pcm_format_mask_t
*formats
;
1693 unsigned int max_rate
, max_channels
;
1697 TRACE("(%p)->(%p)\n", This
, pwfx
);
1703 fmt
= CoTaskMemAlloc(sizeof(WAVEFORMATEXTENSIBLE
));
1705 return E_OUTOFMEMORY
;
1707 formats
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, snd_pcm_format_mask_sizeof());
1710 return E_OUTOFMEMORY
;
1713 EnterCriticalSection(&This
->lock
);
1715 if((err
= snd_pcm_hw_params_any(This
->pcm_handle
, This
->hw_params
)) < 0){
1716 WARN("Unable to get hw_params: %d (%s)\n", err
, snd_strerror(err
));
1717 hr
= AUDCLNT_E_DEVICE_INVALIDATED
;
1721 snd_pcm_hw_params_get_format_mask(This
->hw_params
, formats
);
1723 fmt
->Format
.wFormatTag
= WAVE_FORMAT_EXTENSIBLE
;
1724 if(snd_pcm_format_mask_test(formats
, SND_PCM_FORMAT_FLOAT_LE
)){
1725 fmt
->Format
.wBitsPerSample
= 32;
1726 fmt
->SubFormat
= KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
;
1727 }else if(snd_pcm_format_mask_test(formats
, SND_PCM_FORMAT_S16_LE
)){
1728 fmt
->Format
.wBitsPerSample
= 16;
1729 fmt
->SubFormat
= KSDATAFORMAT_SUBTYPE_PCM
;
1730 }else if(snd_pcm_format_mask_test(formats
, SND_PCM_FORMAT_U8
)){
1731 fmt
->Format
.wBitsPerSample
= 8;
1732 fmt
->SubFormat
= KSDATAFORMAT_SUBTYPE_PCM
;
1733 }else if(snd_pcm_format_mask_test(formats
, SND_PCM_FORMAT_S32_LE
)){
1734 fmt
->Format
.wBitsPerSample
= 32;
1735 fmt
->SubFormat
= KSDATAFORMAT_SUBTYPE_PCM
;
1736 }else if(snd_pcm_format_mask_test(formats
, SND_PCM_FORMAT_S24_3LE
)){
1737 fmt
->Format
.wBitsPerSample
= 24;
1738 fmt
->SubFormat
= KSDATAFORMAT_SUBTYPE_PCM
;
1740 ERR("Didn't recognize any available ALSA formats\n");
1741 hr
= AUDCLNT_E_DEVICE_INVALIDATED
;
1745 if((err
= snd_pcm_hw_params_get_channels_max(This
->hw_params
,
1746 &max_channels
)) < 0){
1747 WARN("Unable to get max channels: %d (%s)\n", err
, snd_strerror(err
));
1748 hr
= AUDCLNT_E_DEVICE_INVALIDATED
;
1752 if(max_channels
> 2)
1753 fmt
->Format
.nChannels
= 2;
1755 fmt
->Format
.nChannels
= max_channels
;
1757 fmt
->dwChannelMask
= get_channel_mask(fmt
->Format
.nChannels
);
1759 if((err
= snd_pcm_hw_params_get_rate_max(This
->hw_params
, &max_rate
,
1761 WARN("Unable to get max rate: %d (%s)\n", err
, snd_strerror(err
));
1762 hr
= AUDCLNT_E_DEVICE_INVALIDATED
;
1766 if(max_rate
>= 48000)
1767 fmt
->Format
.nSamplesPerSec
= 48000;
1768 else if(max_rate
>= 44100)
1769 fmt
->Format
.nSamplesPerSec
= 44100;
1770 else if(max_rate
>= 22050)
1771 fmt
->Format
.nSamplesPerSec
= 22050;
1772 else if(max_rate
>= 11025)
1773 fmt
->Format
.nSamplesPerSec
= 11025;
1774 else if(max_rate
>= 8000)
1775 fmt
->Format
.nSamplesPerSec
= 8000;
1777 ERR("Unknown max rate: %u\n", max_rate
);
1778 hr
= AUDCLNT_E_DEVICE_INVALIDATED
;
1782 fmt
->Format
.nBlockAlign
= (fmt
->Format
.wBitsPerSample
*
1783 fmt
->Format
.nChannels
) / 8;
1784 fmt
->Format
.nAvgBytesPerSec
= fmt
->Format
.nSamplesPerSec
*
1785 fmt
->Format
.nBlockAlign
;
1787 fmt
->Samples
.wValidBitsPerSample
= fmt
->Format
.wBitsPerSample
;
1788 fmt
->Format
.cbSize
= sizeof(WAVEFORMATEXTENSIBLE
) - sizeof(WAVEFORMATEX
);
1790 dump_fmt((WAVEFORMATEX
*)fmt
);
1791 *pwfx
= (WAVEFORMATEX
*)fmt
;
1794 LeaveCriticalSection(&This
->lock
);
1797 HeapFree(GetProcessHeap(), 0, formats
);
1802 static HRESULT WINAPI
AudioClient_GetDevicePeriod(IAudioClient
*iface
,
1803 REFERENCE_TIME
*defperiod
, REFERENCE_TIME
*minperiod
)
1805 ACImpl
*This
= impl_from_IAudioClient(iface
);
1807 TRACE("(%p)->(%p, %p)\n", This
, defperiod
, minperiod
);
1809 if(!defperiod
&& !minperiod
)
1813 *defperiod
= DefaultPeriod
;
1815 *minperiod
= MinimumPeriod
;
1820 static BYTE
*remap_channels(ACImpl
*This
, BYTE
*buf
, snd_pcm_uframes_t frames
)
1822 snd_pcm_uframes_t i
;
1824 UINT bytes_per_sample
= This
->fmt
->wBitsPerSample
/ 8;
1826 if(!This
->need_remapping
)
1829 if(!This
->remapping_buf
){
1830 This
->remapping_buf
= HeapAlloc(GetProcessHeap(), 0,
1831 bytes_per_sample
* This
->alsa_channels
* frames
);
1832 This
->remapping_buf_frames
= frames
;
1833 }else if(This
->remapping_buf_frames
< frames
){
1834 This
->remapping_buf
= HeapReAlloc(GetProcessHeap(), 0, This
->remapping_buf
,
1835 bytes_per_sample
* This
->alsa_channels
* frames
);
1836 This
->remapping_buf_frames
= frames
;
1839 snd_pcm_format_set_silence(This
->alsa_format
, This
->remapping_buf
,
1840 frames
* This
->alsa_channels
);
1842 switch(This
->fmt
->wBitsPerSample
){
1844 UINT8
*tgt_buf
, *src_buf
;
1845 tgt_buf
= This
->remapping_buf
;
1847 for(i
= 0; i
< frames
; ++i
){
1848 for(c
= 0; c
< This
->fmt
->nChannels
; ++c
)
1849 tgt_buf
[This
->alsa_channel_map
[c
]] = src_buf
[c
];
1850 tgt_buf
+= This
->alsa_channels
;
1851 src_buf
+= This
->fmt
->nChannels
;
1856 UINT16
*tgt_buf
, *src_buf
;
1857 tgt_buf
= (UINT16
*)This
->remapping_buf
;
1858 src_buf
= (UINT16
*)buf
;
1859 for(i
= 0; i
< frames
; ++i
){
1860 for(c
= 0; c
< This
->fmt
->nChannels
; ++c
)
1861 tgt_buf
[This
->alsa_channel_map
[c
]] = src_buf
[c
];
1862 tgt_buf
+= This
->alsa_channels
;
1863 src_buf
+= This
->fmt
->nChannels
;
1868 UINT32
*tgt_buf
, *src_buf
;
1869 tgt_buf
= (UINT32
*)This
->remapping_buf
;
1870 src_buf
= (UINT32
*)buf
;
1871 for(i
= 0; i
< frames
; ++i
){
1872 for(c
= 0; c
< This
->fmt
->nChannels
; ++c
)
1873 tgt_buf
[This
->alsa_channel_map
[c
]] = src_buf
[c
];
1874 tgt_buf
+= This
->alsa_channels
;
1875 src_buf
+= This
->fmt
->nChannels
;
1880 BYTE
*tgt_buf
, *src_buf
;
1881 tgt_buf
= This
->remapping_buf
;
1883 for(i
= 0; i
< frames
; ++i
){
1884 for(c
= 0; c
< This
->fmt
->nChannels
; ++c
)
1885 memcpy(&tgt_buf
[This
->alsa_channel_map
[c
] * bytes_per_sample
],
1886 &src_buf
[c
* bytes_per_sample
], bytes_per_sample
);
1887 tgt_buf
+= This
->alsa_channels
* bytes_per_sample
;
1888 src_buf
+= This
->fmt
->nChannels
* bytes_per_sample
;
1894 return This
->remapping_buf
;
1897 static snd_pcm_sframes_t
alsa_write_best_effort(snd_pcm_t
*handle
, BYTE
*buf
,
1898 snd_pcm_uframes_t frames
, ACImpl
*This
, BOOL mute
)
1900 snd_pcm_sframes_t written
;
1904 if((err
= snd_pcm_format_set_silence(This
->alsa_format
, buf
,
1905 frames
* This
->fmt
->nChannels
)) < 0)
1906 WARN("Setting buffer to silence failed: %d (%s)\n", err
,
1910 buf
= remap_channels(This
, buf
, frames
);
1912 written
= snd_pcm_writei(handle
, buf
, frames
);
1916 if(written
== -EAGAIN
)
1920 WARN("writei failed, recovering: %ld (%s)\n", written
,
1921 snd_strerror(written
));
1923 ret
= snd_pcm_recover(handle
, written
, 0);
1925 WARN("Could not recover: %d (%s)\n", ret
, snd_strerror(ret
));
1929 written
= snd_pcm_writei(handle
, buf
, frames
);
1935 static void alsa_write_data(ACImpl
*This
)
1937 snd_pcm_sframes_t written
, in_alsa
;
1938 snd_pcm_uframes_t to_write
, avail
, write_limit
, max_period
;
1941 This
->local_buffer
+ (This
->lcl_offs_frames
* This
->fmt
->nBlockAlign
);
1943 /* this call seems to be required to get an accurate snd_pcm_state() */
1944 avail
= snd_pcm_avail_update(This
->pcm_handle
);
1946 if(snd_pcm_state(This
->pcm_handle
) == SND_PCM_STATE_XRUN
||
1947 avail
> This
->alsa_bufsize_frames
){
1948 TRACE("XRun state avail %ld, recovering\n", avail
);
1950 avail
= This
->alsa_bufsize_frames
;
1952 if((err
= snd_pcm_recover(This
->pcm_handle
, -EPIPE
, 1)) < 0)
1953 WARN("snd_pcm_recover failed: %d (%s)\n", err
, snd_strerror(err
));
1955 if((err
= snd_pcm_reset(This
->pcm_handle
)) < 0)
1956 WARN("snd_pcm_reset failed: %d (%s)\n", err
, snd_strerror(err
));
1958 if((err
= snd_pcm_prepare(This
->pcm_handle
)) < 0)
1959 WARN("snd_pcm_prepare failed: %d (%s)\n", err
, snd_strerror(err
));
1961 TRACE("pad: %ld\n", This
->alsa_bufsize_frames
- avail
);
1963 if(This
->held_frames
== 0)
1966 if(This
->lcl_offs_frames
+ This
->held_frames
> This
->bufsize_frames
)
1967 to_write
= This
->bufsize_frames
- This
->lcl_offs_frames
;
1969 to_write
= This
->held_frames
;
1971 max_period
= max(This
->mmdev_period_frames
, This
->alsa_period_frames
);
1973 /* try to keep 3 ALSA periods or 3 MMDevAPI periods in the ALSA buffer and
1976 in_alsa
= This
->alsa_bufsize_frames
- avail
;
1977 while(in_alsa
+ write_limit
< max_period
* 3)
1978 write_limit
+= max_period
;
1979 if(write_limit
== 0)
1982 to_write
= min(to_write
, write_limit
);
1984 /* Add a lead-in when starting with too few frames to ensure
1985 * continuous rendering. Additional benefit: Force ALSA to start.
1986 * GetPosition continues to reflect the speaker position because
1987 * snd_pcm_delay includes buffered frames in its total delay
1988 * and last_pos_frames prevents moving backwards. */
1989 if(!in_alsa
&& This
->held_frames
< This
->hidden_frames
){
1990 UINT32 s_frames
= This
->hidden_frames
- This
->held_frames
;
1991 BYTE
*silence
= HeapAlloc(GetProcessHeap(), 0,
1992 s_frames
* This
->fmt
->nBlockAlign
);
1995 in_alsa
= alsa_write_best_effort(This
->pcm_handle
,
1996 silence
, s_frames
, This
, TRUE
);
1997 TRACE("lead-in %ld\n", in_alsa
);
1998 HeapFree(GetProcessHeap(), 0, silence
);
2002 WARN("Couldn't allocate lead-in, expect underrun\n");
2005 written
= alsa_write_best_effort(This
->pcm_handle
, buf
, to_write
, This
,
2006 This
->session
->mute
);
2008 WARN("Couldn't write: %ld (%s)\n", written
, snd_strerror(written
));
2012 This
->lcl_offs_frames
+= written
;
2013 This
->lcl_offs_frames
%= This
->bufsize_frames
;
2014 This
->held_frames
-= written
;
2016 if(written
< to_write
){
2017 /* ALSA buffer probably full */
2021 if(This
->held_frames
&& (written
< write_limit
)){
2022 /* wrapped and have some data back at the start to write */
2023 written
= alsa_write_best_effort(This
->pcm_handle
, This
->local_buffer
,
2024 min(This
->held_frames
, write_limit
- written
), This
,
2025 This
->session
->mute
);
2027 WARN("Couldn't write: %ld (%s)\n", written
, snd_strerror(written
));
2031 This
->lcl_offs_frames
+= written
;
2032 This
->lcl_offs_frames
%= This
->bufsize_frames
;
2033 This
->held_frames
-= written
;
2037 static void alsa_read_data(ACImpl
*This
)
2039 snd_pcm_sframes_t pos
, readable
, nread
;
2041 pos
= (This
->held_frames
+ This
->lcl_offs_frames
) % This
->bufsize_frames
;
2042 readable
= This
->bufsize_frames
- pos
;
2044 nread
= snd_pcm_readi(This
->pcm_handle
,
2045 This
->local_buffer
+ pos
* This
->fmt
->nBlockAlign
, readable
);
2049 WARN("read failed, recovering: %ld (%s)\n", nread
, snd_strerror(nread
));
2051 ret
= snd_pcm_recover(This
->pcm_handle
, nread
, 0);
2053 WARN("Recover failed: %d (%s)\n", ret
, snd_strerror(ret
));
2057 nread
= snd_pcm_readi(This
->pcm_handle
,
2058 This
->local_buffer
+ pos
* This
->fmt
->nBlockAlign
, readable
);
2060 WARN("read failed: %ld (%s)\n", nread
, snd_strerror(nread
));
2065 if(This
->session
->mute
){
2067 if((err
= snd_pcm_format_set_silence(This
->alsa_format
,
2068 This
->local_buffer
+ pos
* This
->fmt
->nBlockAlign
,
2070 WARN("Setting buffer to silence failed: %d (%s)\n", err
,
2074 This
->held_frames
+= nread
;
2076 if(This
->held_frames
> This
->bufsize_frames
){
2077 WARN("Overflow of unread data\n");
2078 This
->lcl_offs_frames
+= This
->held_frames
;
2079 This
->lcl_offs_frames
%= This
->bufsize_frames
;
2080 This
->held_frames
= This
->bufsize_frames
;
2084 static void CALLBACK
alsa_push_buffer_data(void *user
, BOOLEAN timer
)
2086 ACImpl
*This
= user
;
2088 EnterCriticalSection(&This
->lock
);
2091 if(This
->dataflow
== eRender
)
2092 alsa_write_data(This
);
2093 else if(This
->dataflow
== eCapture
)
2094 alsa_read_data(This
);
2097 SetEvent(This
->event
);
2100 LeaveCriticalSection(&This
->lock
);
2103 static HRESULT WINAPI
AudioClient_Start(IAudioClient
*iface
)
2105 ACImpl
*This
= impl_from_IAudioClient(iface
);
2107 TRACE("(%p)\n", This
);
2109 EnterCriticalSection(&This
->lock
);
2112 LeaveCriticalSection(&This
->lock
);
2113 return AUDCLNT_E_NOT_INITIALIZED
;
2116 if((This
->flags
& AUDCLNT_STREAMFLAGS_EVENTCALLBACK
) && !This
->event
){
2117 LeaveCriticalSection(&This
->lock
);
2118 return AUDCLNT_E_EVENTHANDLE_NOT_SET
;
2122 LeaveCriticalSection(&This
->lock
);
2123 return AUDCLNT_E_NOT_STOPPED
;
2126 if(This
->dataflow
== eCapture
){
2127 /* dump any data that might be leftover in the ALSA capture buffer */
2128 snd_pcm_readi(This
->pcm_handle
, This
->local_buffer
,
2129 This
->bufsize_frames
);
2132 if(!CreateTimerQueueTimer(&This
->timer
, g_timer_q
, alsa_push_buffer_data
,
2133 This
, 0, This
->mmdev_period_rt
/ 10000, WT_EXECUTEINTIMERTHREAD
)){
2134 LeaveCriticalSection(&This
->lock
);
2135 WARN("Unable to create timer: %u\n", GetLastError());
2136 return E_OUTOFMEMORY
;
2139 This
->started
= TRUE
;
2141 LeaveCriticalSection(&This
->lock
);
2146 static HRESULT WINAPI
AudioClient_Stop(IAudioClient
*iface
)
2148 ACImpl
*This
= impl_from_IAudioClient(iface
);
2152 TRACE("(%p)\n", This
);
2154 EnterCriticalSection(&This
->lock
);
2157 LeaveCriticalSection(&This
->lock
);
2158 return AUDCLNT_E_NOT_INITIALIZED
;
2162 LeaveCriticalSection(&This
->lock
);
2166 /* Stop without losing written frames or position.
2167 * snd_pcm_pause would be appropriate but is unsupported by dmix.
2168 * snd_pcm_drain yields EAGAIN in NONBLOCK mode, except with Pulse. */
2170 event
= CreateEventW(NULL
, TRUE
, FALSE
, NULL
);
2171 wait
= !DeleteTimerQueueTimer(g_timer_q
, This
->timer
, event
);
2173 WARN("DeleteTimerQueueTimer error %u\n", GetLastError());
2174 wait
= wait
&& GetLastError() == ERROR_IO_PENDING
;
2176 This
->started
= FALSE
;
2178 LeaveCriticalSection(&This
->lock
);
2181 WaitForSingleObject(event
, INFINITE
);
2187 static HRESULT WINAPI
AudioClient_Reset(IAudioClient
*iface
)
2189 ACImpl
*This
= impl_from_IAudioClient(iface
);
2191 TRACE("(%p)\n", This
);
2193 EnterCriticalSection(&This
->lock
);
2196 LeaveCriticalSection(&This
->lock
);
2197 return AUDCLNT_E_NOT_INITIALIZED
;
2201 LeaveCriticalSection(&This
->lock
);
2202 return AUDCLNT_E_NOT_STOPPED
;
2205 if(This
->getbuf_last
){
2206 LeaveCriticalSection(&This
->lock
);
2207 return AUDCLNT_E_BUFFER_OPERATION_PENDING
;
2210 if(snd_pcm_drop(This
->pcm_handle
) < 0)
2211 WARN("snd_pcm_drop failed\n");
2213 if(snd_pcm_reset(This
->pcm_handle
) < 0)
2214 WARN("snd_pcm_reset failed\n");
2216 if(snd_pcm_prepare(This
->pcm_handle
) < 0)
2217 WARN("snd_pcm_prepare failed\n");
2219 if(This
->dataflow
== eRender
){
2220 This
->written_frames
= 0;
2221 This
->last_pos_frames
= 0;
2223 This
->written_frames
+= This
->held_frames
;
2225 This
->held_frames
= 0;
2226 This
->lcl_offs_frames
= 0;
2228 LeaveCriticalSection(&This
->lock
);
2233 static HRESULT WINAPI
AudioClient_SetEventHandle(IAudioClient
*iface
,
2236 ACImpl
*This
= impl_from_IAudioClient(iface
);
2238 TRACE("(%p)->(%p)\n", This
, event
);
2241 return E_INVALIDARG
;
2243 EnterCriticalSection(&This
->lock
);
2246 LeaveCriticalSection(&This
->lock
);
2247 return AUDCLNT_E_NOT_INITIALIZED
;
2250 if(!(This
->flags
& AUDCLNT_STREAMFLAGS_EVENTCALLBACK
)){
2251 LeaveCriticalSection(&This
->lock
);
2252 return AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED
;
2255 This
->event
= event
;
2257 LeaveCriticalSection(&This
->lock
);
2262 static HRESULT WINAPI
AudioClient_GetService(IAudioClient
*iface
, REFIID riid
,
2265 ACImpl
*This
= impl_from_IAudioClient(iface
);
2267 TRACE("(%p)->(%s, %p)\n", This
, debugstr_guid(riid
), ppv
);
2273 EnterCriticalSection(&This
->lock
);
2276 LeaveCriticalSection(&This
->lock
);
2277 return AUDCLNT_E_NOT_INITIALIZED
;
2280 if(IsEqualIID(riid
, &IID_IAudioRenderClient
)){
2281 if(This
->dataflow
!= eRender
){
2282 LeaveCriticalSection(&This
->lock
);
2283 return AUDCLNT_E_WRONG_ENDPOINT_TYPE
;
2285 IAudioRenderClient_AddRef(&This
->IAudioRenderClient_iface
);
2286 *ppv
= &This
->IAudioRenderClient_iface
;
2287 }else if(IsEqualIID(riid
, &IID_IAudioCaptureClient
)){
2288 if(This
->dataflow
!= eCapture
){
2289 LeaveCriticalSection(&This
->lock
);
2290 return AUDCLNT_E_WRONG_ENDPOINT_TYPE
;
2292 IAudioCaptureClient_AddRef(&This
->IAudioCaptureClient_iface
);
2293 *ppv
= &This
->IAudioCaptureClient_iface
;
2294 }else if(IsEqualIID(riid
, &IID_IAudioClock
)){
2295 IAudioClock_AddRef(&This
->IAudioClock_iface
);
2296 *ppv
= &This
->IAudioClock_iface
;
2297 }else if(IsEqualIID(riid
, &IID_IAudioStreamVolume
)){
2298 IAudioStreamVolume_AddRef(&This
->IAudioStreamVolume_iface
);
2299 *ppv
= &This
->IAudioStreamVolume_iface
;
2300 }else if(IsEqualIID(riid
, &IID_IAudioSessionControl
)){
2301 if(!This
->session_wrapper
){
2302 This
->session_wrapper
= AudioSessionWrapper_Create(This
);
2303 if(!This
->session_wrapper
){
2304 LeaveCriticalSection(&This
->lock
);
2305 return E_OUTOFMEMORY
;
2308 IAudioSessionControl2_AddRef(&This
->session_wrapper
->IAudioSessionControl2_iface
);
2310 *ppv
= &This
->session_wrapper
->IAudioSessionControl2_iface
;
2311 }else if(IsEqualIID(riid
, &IID_IChannelAudioVolume
)){
2312 if(!This
->session_wrapper
){
2313 This
->session_wrapper
= AudioSessionWrapper_Create(This
);
2314 if(!This
->session_wrapper
){
2315 LeaveCriticalSection(&This
->lock
);
2316 return E_OUTOFMEMORY
;
2319 IChannelAudioVolume_AddRef(&This
->session_wrapper
->IChannelAudioVolume_iface
);
2321 *ppv
= &This
->session_wrapper
->IChannelAudioVolume_iface
;
2322 }else if(IsEqualIID(riid
, &IID_ISimpleAudioVolume
)){
2323 if(!This
->session_wrapper
){
2324 This
->session_wrapper
= AudioSessionWrapper_Create(This
);
2325 if(!This
->session_wrapper
){
2326 LeaveCriticalSection(&This
->lock
);
2327 return E_OUTOFMEMORY
;
2330 ISimpleAudioVolume_AddRef(&This
->session_wrapper
->ISimpleAudioVolume_iface
);
2332 *ppv
= &This
->session_wrapper
->ISimpleAudioVolume_iface
;
2336 LeaveCriticalSection(&This
->lock
);
2340 LeaveCriticalSection(&This
->lock
);
2342 FIXME("stub %s\n", debugstr_guid(riid
));
2343 return E_NOINTERFACE
;
2346 static const IAudioClientVtbl AudioClient_Vtbl
=
2348 AudioClient_QueryInterface
,
2350 AudioClient_Release
,
2351 AudioClient_Initialize
,
2352 AudioClient_GetBufferSize
,
2353 AudioClient_GetStreamLatency
,
2354 AudioClient_GetCurrentPadding
,
2355 AudioClient_IsFormatSupported
,
2356 AudioClient_GetMixFormat
,
2357 AudioClient_GetDevicePeriod
,
2361 AudioClient_SetEventHandle
,
2362 AudioClient_GetService
2365 static HRESULT WINAPI
AudioRenderClient_QueryInterface(
2366 IAudioRenderClient
*iface
, REFIID riid
, void **ppv
)
2368 TRACE("(%p)->(%s, %p)\n", iface
, debugstr_guid(riid
), ppv
);
2374 if(IsEqualIID(riid
, &IID_IUnknown
) ||
2375 IsEqualIID(riid
, &IID_IAudioRenderClient
))
2378 IUnknown_AddRef((IUnknown
*)*ppv
);
2382 WARN("Unknown interface %s\n", debugstr_guid(riid
));
2383 return E_NOINTERFACE
;
2386 static ULONG WINAPI
AudioRenderClient_AddRef(IAudioRenderClient
*iface
)
2388 ACImpl
*This
= impl_from_IAudioRenderClient(iface
);
2389 return AudioClient_AddRef(&This
->IAudioClient_iface
);
2392 static ULONG WINAPI
AudioRenderClient_Release(IAudioRenderClient
*iface
)
2394 ACImpl
*This
= impl_from_IAudioRenderClient(iface
);
2395 return AudioClient_Release(&This
->IAudioClient_iface
);
2398 static HRESULT WINAPI
AudioRenderClient_GetBuffer(IAudioRenderClient
*iface
,
2399 UINT32 frames
, BYTE
**data
)
2401 ACImpl
*This
= impl_from_IAudioRenderClient(iface
);
2404 TRACE("(%p)->(%u, %p)\n", This
, frames
, data
);
2410 EnterCriticalSection(&This
->lock
);
2412 if(This
->getbuf_last
){
2413 LeaveCriticalSection(&This
->lock
);
2414 return AUDCLNT_E_OUT_OF_ORDER
;
2418 LeaveCriticalSection(&This
->lock
);
2422 /* held_frames == GetCurrentPadding_nolock(); */
2423 if(This
->held_frames
+ frames
> This
->bufsize_frames
){
2424 LeaveCriticalSection(&This
->lock
);
2425 return AUDCLNT_E_BUFFER_TOO_LARGE
;
2429 (This
->lcl_offs_frames
+ This
->held_frames
) % This
->bufsize_frames
;
2430 if(write_pos
+ frames
> This
->bufsize_frames
){
2431 if(This
->tmp_buffer_frames
< frames
){
2432 HeapFree(GetProcessHeap(), 0, This
->tmp_buffer
);
2433 This
->tmp_buffer
= HeapAlloc(GetProcessHeap(), 0,
2434 frames
* This
->fmt
->nBlockAlign
);
2435 if(!This
->tmp_buffer
){
2436 LeaveCriticalSection(&This
->lock
);
2437 return E_OUTOFMEMORY
;
2439 This
->tmp_buffer_frames
= frames
;
2441 *data
= This
->tmp_buffer
;
2442 This
->getbuf_last
= -frames
;
2444 *data
= This
->local_buffer
+ write_pos
* This
->fmt
->nBlockAlign
;
2445 This
->getbuf_last
= frames
;
2448 LeaveCriticalSection(&This
->lock
);
2453 static void alsa_wrap_buffer(ACImpl
*This
, BYTE
*buffer
, UINT32 written_frames
)
2455 snd_pcm_uframes_t write_offs_frames
=
2456 (This
->lcl_offs_frames
+ This
->held_frames
) % This
->bufsize_frames
;
2457 UINT32 write_offs_bytes
= write_offs_frames
* This
->fmt
->nBlockAlign
;
2458 snd_pcm_uframes_t chunk_frames
= This
->bufsize_frames
- write_offs_frames
;
2459 UINT32 chunk_bytes
= chunk_frames
* This
->fmt
->nBlockAlign
;
2460 UINT32 written_bytes
= written_frames
* This
->fmt
->nBlockAlign
;
2462 if(written_bytes
<= chunk_bytes
){
2463 memcpy(This
->local_buffer
+ write_offs_bytes
, buffer
, written_bytes
);
2465 memcpy(This
->local_buffer
+ write_offs_bytes
, buffer
, chunk_bytes
);
2466 memcpy(This
->local_buffer
, buffer
+ chunk_bytes
,
2467 written_bytes
- chunk_bytes
);
2471 static HRESULT WINAPI
AudioRenderClient_ReleaseBuffer(
2472 IAudioRenderClient
*iface
, UINT32 written_frames
, DWORD flags
)
2474 ACImpl
*This
= impl_from_IAudioRenderClient(iface
);
2477 TRACE("(%p)->(%u, %x)\n", This
, written_frames
, flags
);
2479 EnterCriticalSection(&This
->lock
);
2481 if(!written_frames
){
2482 This
->getbuf_last
= 0;
2483 LeaveCriticalSection(&This
->lock
);
2487 if(!This
->getbuf_last
){
2488 LeaveCriticalSection(&This
->lock
);
2489 return AUDCLNT_E_OUT_OF_ORDER
;
2492 if(written_frames
> (This
->getbuf_last
>= 0 ? This
->getbuf_last
: -This
->getbuf_last
)){
2493 LeaveCriticalSection(&This
->lock
);
2494 return AUDCLNT_E_INVALID_SIZE
;
2497 if(This
->getbuf_last
>= 0)
2498 buffer
= This
->local_buffer
+ This
->fmt
->nBlockAlign
*
2499 ((This
->lcl_offs_frames
+ This
->held_frames
) % This
->bufsize_frames
);
2501 buffer
= This
->tmp_buffer
;
2503 if(flags
& AUDCLNT_BUFFERFLAGS_SILENT
){
2504 if(This
->fmt
->wBitsPerSample
== 8)
2505 memset(buffer
, 128, written_frames
* This
->fmt
->nBlockAlign
);
2507 memset(buffer
, 0, written_frames
* This
->fmt
->nBlockAlign
);
2510 if(This
->getbuf_last
< 0)
2511 alsa_wrap_buffer(This
, buffer
, written_frames
);
2513 This
->held_frames
+= written_frames
;
2514 This
->written_frames
+= written_frames
;
2515 This
->getbuf_last
= 0;
2517 LeaveCriticalSection(&This
->lock
);
2522 static const IAudioRenderClientVtbl AudioRenderClient_Vtbl
= {
2523 AudioRenderClient_QueryInterface
,
2524 AudioRenderClient_AddRef
,
2525 AudioRenderClient_Release
,
2526 AudioRenderClient_GetBuffer
,
2527 AudioRenderClient_ReleaseBuffer
2530 static HRESULT WINAPI
AudioCaptureClient_QueryInterface(
2531 IAudioCaptureClient
*iface
, REFIID riid
, void **ppv
)
2533 TRACE("(%p)->(%s, %p)\n", iface
, debugstr_guid(riid
), ppv
);
2539 if(IsEqualIID(riid
, &IID_IUnknown
) ||
2540 IsEqualIID(riid
, &IID_IAudioCaptureClient
))
2543 IUnknown_AddRef((IUnknown
*)*ppv
);
2547 WARN("Unknown interface %s\n", debugstr_guid(riid
));
2548 return E_NOINTERFACE
;
2551 static ULONG WINAPI
AudioCaptureClient_AddRef(IAudioCaptureClient
*iface
)
2553 ACImpl
*This
= impl_from_IAudioCaptureClient(iface
);
2554 return IAudioClient_AddRef(&This
->IAudioClient_iface
);
2557 static ULONG WINAPI
AudioCaptureClient_Release(IAudioCaptureClient
*iface
)
2559 ACImpl
*This
= impl_from_IAudioCaptureClient(iface
);
2560 return IAudioClient_Release(&This
->IAudioClient_iface
);
2563 static HRESULT WINAPI
AudioCaptureClient_GetBuffer(IAudioCaptureClient
*iface
,
2564 BYTE
**data
, UINT32
*frames
, DWORD
*flags
, UINT64
*devpos
,
2567 ACImpl
*This
= impl_from_IAudioCaptureClient(iface
);
2569 TRACE("(%p)->(%p, %p, %p, %p, %p)\n", This
, data
, frames
, flags
,
2572 if(!data
|| !frames
|| !flags
)
2575 EnterCriticalSection(&This
->lock
);
2577 if(This
->getbuf_last
){
2578 LeaveCriticalSection(&This
->lock
);
2579 return AUDCLNT_E_OUT_OF_ORDER
;
2582 /* hr = GetNextPacketSize(iface, frames); */
2583 if(This
->held_frames
< This
->mmdev_period_frames
){
2585 LeaveCriticalSection(&This
->lock
);
2586 return AUDCLNT_S_BUFFER_EMPTY
;
2588 *frames
= This
->mmdev_period_frames
;
2590 if(This
->lcl_offs_frames
+ *frames
> This
->bufsize_frames
){
2591 UINT32 chunk_bytes
, offs_bytes
, frames_bytes
;
2592 if(This
->tmp_buffer_frames
< *frames
){
2593 HeapFree(GetProcessHeap(), 0, This
->tmp_buffer
);
2594 This
->tmp_buffer
= HeapAlloc(GetProcessHeap(), 0,
2595 *frames
* This
->fmt
->nBlockAlign
);
2596 if(!This
->tmp_buffer
){
2597 LeaveCriticalSection(&This
->lock
);
2598 return E_OUTOFMEMORY
;
2600 This
->tmp_buffer_frames
= *frames
;
2603 *data
= This
->tmp_buffer
;
2604 chunk_bytes
= (This
->bufsize_frames
- This
->lcl_offs_frames
) *
2605 This
->fmt
->nBlockAlign
;
2606 offs_bytes
= This
->lcl_offs_frames
* This
->fmt
->nBlockAlign
;
2607 frames_bytes
= *frames
* This
->fmt
->nBlockAlign
;
2608 memcpy(This
->tmp_buffer
, This
->local_buffer
+ offs_bytes
, chunk_bytes
);
2609 memcpy(This
->tmp_buffer
+ chunk_bytes
, This
->local_buffer
,
2610 frames_bytes
- chunk_bytes
);
2612 *data
= This
->local_buffer
+
2613 This
->lcl_offs_frames
* This
->fmt
->nBlockAlign
;
2615 This
->getbuf_last
= *frames
;
2619 *devpos
= This
->written_frames
;
2620 if(qpcpos
){ /* fixme: qpc of recording time */
2621 LARGE_INTEGER stamp
, freq
;
2622 QueryPerformanceCounter(&stamp
);
2623 QueryPerformanceFrequency(&freq
);
2624 *qpcpos
= (stamp
.QuadPart
* (INT64
)10000000) / freq
.QuadPart
;
2627 LeaveCriticalSection(&This
->lock
);
2629 return *frames
? S_OK
: AUDCLNT_S_BUFFER_EMPTY
;
2632 static HRESULT WINAPI
AudioCaptureClient_ReleaseBuffer(
2633 IAudioCaptureClient
*iface
, UINT32 done
)
2635 ACImpl
*This
= impl_from_IAudioCaptureClient(iface
);
2637 TRACE("(%p)->(%u)\n", This
, done
);
2639 EnterCriticalSection(&This
->lock
);
2642 This
->getbuf_last
= 0;
2643 LeaveCriticalSection(&This
->lock
);
2647 if(!This
->getbuf_last
){
2648 LeaveCriticalSection(&This
->lock
);
2649 return AUDCLNT_E_OUT_OF_ORDER
;
2652 if(This
->getbuf_last
!= done
){
2653 LeaveCriticalSection(&This
->lock
);
2654 return AUDCLNT_E_INVALID_SIZE
;
2657 This
->written_frames
+= done
;
2658 This
->held_frames
-= done
;
2659 This
->lcl_offs_frames
+= done
;
2660 This
->lcl_offs_frames
%= This
->bufsize_frames
;
2661 This
->getbuf_last
= 0;
2663 LeaveCriticalSection(&This
->lock
);
2668 static HRESULT WINAPI
AudioCaptureClient_GetNextPacketSize(
2669 IAudioCaptureClient
*iface
, UINT32
*frames
)
2671 ACImpl
*This
= impl_from_IAudioCaptureClient(iface
);
2673 TRACE("(%p)->(%p)\n", This
, frames
);
2678 EnterCriticalSection(&This
->lock
);
2680 *frames
= This
->held_frames
< This
->mmdev_period_frames
? 0 : This
->mmdev_period_frames
;
2682 LeaveCriticalSection(&This
->lock
);
2687 static const IAudioCaptureClientVtbl AudioCaptureClient_Vtbl
=
2689 AudioCaptureClient_QueryInterface
,
2690 AudioCaptureClient_AddRef
,
2691 AudioCaptureClient_Release
,
2692 AudioCaptureClient_GetBuffer
,
2693 AudioCaptureClient_ReleaseBuffer
,
2694 AudioCaptureClient_GetNextPacketSize
2697 static HRESULT WINAPI
AudioClock_QueryInterface(IAudioClock
*iface
,
2698 REFIID riid
, void **ppv
)
2700 ACImpl
*This
= impl_from_IAudioClock(iface
);
2702 TRACE("(%p)->(%s, %p)\n", iface
, debugstr_guid(riid
), ppv
);
2708 if(IsEqualIID(riid
, &IID_IUnknown
) || IsEqualIID(riid
, &IID_IAudioClock
))
2710 else if(IsEqualIID(riid
, &IID_IAudioClock2
))
2711 *ppv
= &This
->IAudioClock2_iface
;
2713 IUnknown_AddRef((IUnknown
*)*ppv
);
2717 WARN("Unknown interface %s\n", debugstr_guid(riid
));
2718 return E_NOINTERFACE
;
2721 static ULONG WINAPI
AudioClock_AddRef(IAudioClock
*iface
)
2723 ACImpl
*This
= impl_from_IAudioClock(iface
);
2724 return IAudioClient_AddRef(&This
->IAudioClient_iface
);
2727 static ULONG WINAPI
AudioClock_Release(IAudioClock
*iface
)
2729 ACImpl
*This
= impl_from_IAudioClock(iface
);
2730 return IAudioClient_Release(&This
->IAudioClient_iface
);
2733 static HRESULT WINAPI
AudioClock_GetFrequency(IAudioClock
*iface
, UINT64
*freq
)
2735 ACImpl
*This
= impl_from_IAudioClock(iface
);
2737 TRACE("(%p)->(%p)\n", This
, freq
);
2739 *freq
= This
->fmt
->nSamplesPerSec
;
2744 static HRESULT WINAPI
AudioClock_GetPosition(IAudioClock
*iface
, UINT64
*pos
,
2747 ACImpl
*This
= impl_from_IAudioClock(iface
);
2748 UINT64 written_frames
, position
;
2751 snd_pcm_state_t alsa_state
;
2752 snd_pcm_uframes_t avail_frames
;
2753 snd_pcm_sframes_t delay_frames
;
2755 TRACE("(%p)->(%p, %p)\n", This
, pos
, qpctime
);
2760 EnterCriticalSection(&This
->lock
);
2762 /* call required to get accurate snd_pcm_state() */
2763 avail_frames
= snd_pcm_avail_update(This
->pcm_handle
);
2764 alsa_state
= snd_pcm_state(This
->pcm_handle
);
2765 written_frames
= This
->written_frames
;
2766 held_frames
= This
->held_frames
;
2768 err
= snd_pcm_delay(This
->pcm_handle
, &delay_frames
);
2770 /* old Pulse, shortly after start */
2771 WARN("snd_pcm_delay failed in state %u: %d (%s)\n", alsa_state
, err
, snd_strerror(err
));
2774 if(This
->dataflow
== eRender
){
2775 position
= written_frames
- held_frames
; /* maximum */
2776 if(!This
->started
|| alsa_state
> SND_PCM_STATE_RUNNING
)
2777 ; /* mmdevapi stopped or ALSA underrun: pretend everything was played */
2778 else if(err
<0 || delay_frames
> position
- This
->last_pos_frames
)
2779 /* Pulse bug: past underrun, despite recovery, avail_frames & delay
2780 * may be larger than alsa_bufsize_frames, as if cumulating frames. */
2781 /* Pulse bug: EIO(-5) shortly after starting: nothing played */
2782 position
= This
->last_pos_frames
;
2783 else if(delay_frames
> 0)
2784 position
-= delay_frames
;
2786 position
= written_frames
+ held_frames
;
2788 /* ensure monotic growth */
2789 This
->last_pos_frames
= position
;
2791 LeaveCriticalSection(&This
->lock
);
2793 TRACE("frames written: %u, held: %u, avail: %ld, delay: %ld state %d, pos: %u\n",
2794 (UINT32
)(written_frames
%1000000000), held_frames
,
2795 avail_frames
, delay_frames
, alsa_state
, (UINT32
)(position
%1000000000));
2799 LARGE_INTEGER stamp
, freq
;
2800 QueryPerformanceCounter(&stamp
);
2801 QueryPerformanceFrequency(&freq
);
2802 *qpctime
= (stamp
.QuadPart
* (INT64
)10000000) / freq
.QuadPart
;
2808 static HRESULT WINAPI
AudioClock_GetCharacteristics(IAudioClock
*iface
,
2811 ACImpl
*This
= impl_from_IAudioClock(iface
);
2813 TRACE("(%p)->(%p)\n", This
, chars
);
2818 *chars
= AUDIOCLOCK_CHARACTERISTIC_FIXED_FREQ
;
2823 static const IAudioClockVtbl AudioClock_Vtbl
=
2825 AudioClock_QueryInterface
,
2828 AudioClock_GetFrequency
,
2829 AudioClock_GetPosition
,
2830 AudioClock_GetCharacteristics
2833 static HRESULT WINAPI
AudioClock2_QueryInterface(IAudioClock2
*iface
,
2834 REFIID riid
, void **ppv
)
2836 ACImpl
*This
= impl_from_IAudioClock2(iface
);
2837 return IAudioClock_QueryInterface(&This
->IAudioClock_iface
, riid
, ppv
);
2840 static ULONG WINAPI
AudioClock2_AddRef(IAudioClock2
*iface
)
2842 ACImpl
*This
= impl_from_IAudioClock2(iface
);
2843 return IAudioClient_AddRef(&This
->IAudioClient_iface
);
2846 static ULONG WINAPI
AudioClock2_Release(IAudioClock2
*iface
)
2848 ACImpl
*This
= impl_from_IAudioClock2(iface
);
2849 return IAudioClient_Release(&This
->IAudioClient_iface
);
2852 static HRESULT WINAPI
AudioClock2_GetDevicePosition(IAudioClock2
*iface
,
2853 UINT64
*pos
, UINT64
*qpctime
)
2855 ACImpl
*This
= impl_from_IAudioClock2(iface
);
2857 FIXME("(%p)->(%p, %p)\n", This
, pos
, qpctime
);
2862 static const IAudioClock2Vtbl AudioClock2_Vtbl
=
2864 AudioClock2_QueryInterface
,
2866 AudioClock2_Release
,
2867 AudioClock2_GetDevicePosition
2870 static AudioSessionWrapper
*AudioSessionWrapper_Create(ACImpl
*client
)
2872 AudioSessionWrapper
*ret
;
2874 ret
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
,
2875 sizeof(AudioSessionWrapper
));
2879 ret
->IAudioSessionControl2_iface
.lpVtbl
= &AudioSessionControl2_Vtbl
;
2880 ret
->ISimpleAudioVolume_iface
.lpVtbl
= &SimpleAudioVolume_Vtbl
;
2881 ret
->IChannelAudioVolume_iface
.lpVtbl
= &ChannelAudioVolume_Vtbl
;
2885 ret
->client
= client
;
2887 ret
->session
= client
->session
;
2888 AudioClient_AddRef(&client
->IAudioClient_iface
);
2894 static HRESULT WINAPI
AudioSessionControl_QueryInterface(
2895 IAudioSessionControl2
*iface
, REFIID riid
, void **ppv
)
2897 TRACE("(%p)->(%s, %p)\n", iface
, debugstr_guid(riid
), ppv
);
2903 if(IsEqualIID(riid
, &IID_IUnknown
) ||
2904 IsEqualIID(riid
, &IID_IAudioSessionControl
) ||
2905 IsEqualIID(riid
, &IID_IAudioSessionControl2
))
2908 IUnknown_AddRef((IUnknown
*)*ppv
);
2912 WARN("Unknown interface %s\n", debugstr_guid(riid
));
2913 return E_NOINTERFACE
;
2916 static ULONG WINAPI
AudioSessionControl_AddRef(IAudioSessionControl2
*iface
)
2918 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
2920 ref
= InterlockedIncrement(&This
->ref
);
2921 TRACE("(%p) Refcount now %u\n", This
, ref
);
2925 static ULONG WINAPI
AudioSessionControl_Release(IAudioSessionControl2
*iface
)
2927 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
2929 ref
= InterlockedDecrement(&This
->ref
);
2930 TRACE("(%p) Refcount now %u\n", This
, ref
);
2933 EnterCriticalSection(&This
->client
->lock
);
2934 This
->client
->session_wrapper
= NULL
;
2935 LeaveCriticalSection(&This
->client
->lock
);
2936 AudioClient_Release(&This
->client
->IAudioClient_iface
);
2938 HeapFree(GetProcessHeap(), 0, This
);
2943 static HRESULT WINAPI
AudioSessionControl_GetState(IAudioSessionControl2
*iface
,
2944 AudioSessionState
*state
)
2946 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
2949 TRACE("(%p)->(%p)\n", This
, state
);
2952 return NULL_PTR_ERR
;
2954 EnterCriticalSection(&g_sessions_lock
);
2956 if(list_empty(&This
->session
->clients
)){
2957 *state
= AudioSessionStateExpired
;
2958 LeaveCriticalSection(&g_sessions_lock
);
2962 LIST_FOR_EACH_ENTRY(client
, &This
->session
->clients
, ACImpl
, entry
){
2963 EnterCriticalSection(&client
->lock
);
2964 if(client
->started
){
2965 *state
= AudioSessionStateActive
;
2966 LeaveCriticalSection(&client
->lock
);
2967 LeaveCriticalSection(&g_sessions_lock
);
2970 LeaveCriticalSection(&client
->lock
);
2973 LeaveCriticalSection(&g_sessions_lock
);
2975 *state
= AudioSessionStateInactive
;
2980 static HRESULT WINAPI
AudioSessionControl_GetDisplayName(
2981 IAudioSessionControl2
*iface
, WCHAR
**name
)
2983 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
2985 FIXME("(%p)->(%p) - stub\n", This
, name
);
2990 static HRESULT WINAPI
AudioSessionControl_SetDisplayName(
2991 IAudioSessionControl2
*iface
, const WCHAR
*name
, const GUID
*session
)
2993 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
2995 FIXME("(%p)->(%p, %s) - stub\n", This
, name
, debugstr_guid(session
));
3000 static HRESULT WINAPI
AudioSessionControl_GetIconPath(
3001 IAudioSessionControl2
*iface
, WCHAR
**path
)
3003 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
3005 FIXME("(%p)->(%p) - stub\n", This
, path
);
3010 static HRESULT WINAPI
AudioSessionControl_SetIconPath(
3011 IAudioSessionControl2
*iface
, const WCHAR
*path
, const GUID
*session
)
3013 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
3015 FIXME("(%p)->(%p, %s) - stub\n", This
, path
, debugstr_guid(session
));
3020 static HRESULT WINAPI
AudioSessionControl_GetGroupingParam(
3021 IAudioSessionControl2
*iface
, GUID
*group
)
3023 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
3025 FIXME("(%p)->(%p) - stub\n", This
, group
);
3030 static HRESULT WINAPI
AudioSessionControl_SetGroupingParam(
3031 IAudioSessionControl2
*iface
, const GUID
*group
, const GUID
*session
)
3033 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
3035 FIXME("(%p)->(%s, %s) - stub\n", This
, debugstr_guid(group
),
3036 debugstr_guid(session
));
3041 static HRESULT WINAPI
AudioSessionControl_RegisterAudioSessionNotification(
3042 IAudioSessionControl2
*iface
, IAudioSessionEvents
*events
)
3044 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
3046 FIXME("(%p)->(%p) - stub\n", This
, events
);
3051 static HRESULT WINAPI
AudioSessionControl_UnregisterAudioSessionNotification(
3052 IAudioSessionControl2
*iface
, IAudioSessionEvents
*events
)
3054 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
3056 FIXME("(%p)->(%p) - stub\n", This
, events
);
3061 static HRESULT WINAPI
AudioSessionControl_GetSessionIdentifier(
3062 IAudioSessionControl2
*iface
, WCHAR
**id
)
3064 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
3066 FIXME("(%p)->(%p) - stub\n", This
, id
);
3071 static HRESULT WINAPI
AudioSessionControl_GetSessionInstanceIdentifier(
3072 IAudioSessionControl2
*iface
, WCHAR
**id
)
3074 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
3076 FIXME("(%p)->(%p) - stub\n", This
, id
);
3081 static HRESULT WINAPI
AudioSessionControl_GetProcessId(
3082 IAudioSessionControl2
*iface
, DWORD
*pid
)
3084 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
3086 TRACE("(%p)->(%p)\n", This
, pid
);
3091 *pid
= GetCurrentProcessId();
3096 static HRESULT WINAPI
AudioSessionControl_IsSystemSoundsSession(
3097 IAudioSessionControl2
*iface
)
3099 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
3101 TRACE("(%p)\n", This
);
3106 static HRESULT WINAPI
AudioSessionControl_SetDuckingPreference(
3107 IAudioSessionControl2
*iface
, BOOL optout
)
3109 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
3111 TRACE("(%p)->(%d)\n", This
, optout
);
3116 static const IAudioSessionControl2Vtbl AudioSessionControl2_Vtbl
=
3118 AudioSessionControl_QueryInterface
,
3119 AudioSessionControl_AddRef
,
3120 AudioSessionControl_Release
,
3121 AudioSessionControl_GetState
,
3122 AudioSessionControl_GetDisplayName
,
3123 AudioSessionControl_SetDisplayName
,
3124 AudioSessionControl_GetIconPath
,
3125 AudioSessionControl_SetIconPath
,
3126 AudioSessionControl_GetGroupingParam
,
3127 AudioSessionControl_SetGroupingParam
,
3128 AudioSessionControl_RegisterAudioSessionNotification
,
3129 AudioSessionControl_UnregisterAudioSessionNotification
,
3130 AudioSessionControl_GetSessionIdentifier
,
3131 AudioSessionControl_GetSessionInstanceIdentifier
,
3132 AudioSessionControl_GetProcessId
,
3133 AudioSessionControl_IsSystemSoundsSession
,
3134 AudioSessionControl_SetDuckingPreference
3137 static HRESULT WINAPI
SimpleAudioVolume_QueryInterface(
3138 ISimpleAudioVolume
*iface
, REFIID riid
, void **ppv
)
3140 TRACE("(%p)->(%s, %p)\n", iface
, debugstr_guid(riid
), ppv
);
3146 if(IsEqualIID(riid
, &IID_IUnknown
) ||
3147 IsEqualIID(riid
, &IID_ISimpleAudioVolume
))
3150 IUnknown_AddRef((IUnknown
*)*ppv
);
3154 WARN("Unknown interface %s\n", debugstr_guid(riid
));
3155 return E_NOINTERFACE
;
3158 static ULONG WINAPI
SimpleAudioVolume_AddRef(ISimpleAudioVolume
*iface
)
3160 AudioSessionWrapper
*This
= impl_from_ISimpleAudioVolume(iface
);
3161 return AudioSessionControl_AddRef(&This
->IAudioSessionControl2_iface
);
3164 static ULONG WINAPI
SimpleAudioVolume_Release(ISimpleAudioVolume
*iface
)
3166 AudioSessionWrapper
*This
= impl_from_ISimpleAudioVolume(iface
);
3167 return AudioSessionControl_Release(&This
->IAudioSessionControl2_iface
);
3170 static HRESULT WINAPI
SimpleAudioVolume_SetMasterVolume(
3171 ISimpleAudioVolume
*iface
, float level
, const GUID
*context
)
3173 AudioSessionWrapper
*This
= impl_from_ISimpleAudioVolume(iface
);
3174 AudioSession
*session
= This
->session
;
3176 TRACE("(%p)->(%f, %s)\n", session
, level
, wine_dbgstr_guid(context
));
3178 if(level
< 0.f
|| level
> 1.f
)
3179 return E_INVALIDARG
;
3182 FIXME("Notifications not supported yet\n");
3184 TRACE("ALSA does not support volume control\n");
3186 EnterCriticalSection(&session
->lock
);
3188 session
->master_vol
= level
;
3190 LeaveCriticalSection(&session
->lock
);
3195 static HRESULT WINAPI
SimpleAudioVolume_GetMasterVolume(
3196 ISimpleAudioVolume
*iface
, float *level
)
3198 AudioSessionWrapper
*This
= impl_from_ISimpleAudioVolume(iface
);
3199 AudioSession
*session
= This
->session
;
3201 TRACE("(%p)->(%p)\n", session
, level
);
3204 return NULL_PTR_ERR
;
3206 *level
= session
->master_vol
;
3211 static HRESULT WINAPI
SimpleAudioVolume_SetMute(ISimpleAudioVolume
*iface
,
3212 BOOL mute
, const GUID
*context
)
3214 AudioSessionWrapper
*This
= impl_from_ISimpleAudioVolume(iface
);
3215 AudioSession
*session
= This
->session
;
3217 TRACE("(%p)->(%u, %p)\n", session
, mute
, context
);
3220 FIXME("Notifications not supported yet\n");
3222 session
->mute
= mute
;
3227 static HRESULT WINAPI
SimpleAudioVolume_GetMute(ISimpleAudioVolume
*iface
,
3230 AudioSessionWrapper
*This
= impl_from_ISimpleAudioVolume(iface
);
3231 AudioSession
*session
= This
->session
;
3233 TRACE("(%p)->(%p)\n", session
, mute
);
3236 return NULL_PTR_ERR
;
3238 *mute
= session
->mute
;
3243 static const ISimpleAudioVolumeVtbl SimpleAudioVolume_Vtbl
=
3245 SimpleAudioVolume_QueryInterface
,
3246 SimpleAudioVolume_AddRef
,
3247 SimpleAudioVolume_Release
,
3248 SimpleAudioVolume_SetMasterVolume
,
3249 SimpleAudioVolume_GetMasterVolume
,
3250 SimpleAudioVolume_SetMute
,
3251 SimpleAudioVolume_GetMute
3254 static HRESULT WINAPI
AudioStreamVolume_QueryInterface(
3255 IAudioStreamVolume
*iface
, REFIID riid
, void **ppv
)
3257 TRACE("(%p)->(%s, %p)\n", iface
, debugstr_guid(riid
), ppv
);
3263 if(IsEqualIID(riid
, &IID_IUnknown
) ||
3264 IsEqualIID(riid
, &IID_IAudioStreamVolume
))
3267 IUnknown_AddRef((IUnknown
*)*ppv
);
3271 WARN("Unknown interface %s\n", debugstr_guid(riid
));
3272 return E_NOINTERFACE
;
3275 static ULONG WINAPI
AudioStreamVolume_AddRef(IAudioStreamVolume
*iface
)
3277 ACImpl
*This
= impl_from_IAudioStreamVolume(iface
);
3278 return IAudioClient_AddRef(&This
->IAudioClient_iface
);
3281 static ULONG WINAPI
AudioStreamVolume_Release(IAudioStreamVolume
*iface
)
3283 ACImpl
*This
= impl_from_IAudioStreamVolume(iface
);
3284 return IAudioClient_Release(&This
->IAudioClient_iface
);
3287 static HRESULT WINAPI
AudioStreamVolume_GetChannelCount(
3288 IAudioStreamVolume
*iface
, UINT32
*out
)
3290 ACImpl
*This
= impl_from_IAudioStreamVolume(iface
);
3292 TRACE("(%p)->(%p)\n", This
, out
);
3297 *out
= This
->fmt
->nChannels
;
3302 static HRESULT WINAPI
AudioStreamVolume_SetChannelVolume(
3303 IAudioStreamVolume
*iface
, UINT32 index
, float level
)
3305 ACImpl
*This
= impl_from_IAudioStreamVolume(iface
);
3307 TRACE("(%p)->(%d, %f)\n", This
, index
, level
);
3309 if(level
< 0.f
|| level
> 1.f
)
3310 return E_INVALIDARG
;
3312 if(index
>= This
->fmt
->nChannels
)
3313 return E_INVALIDARG
;
3315 TRACE("ALSA does not support volume control\n");
3317 EnterCriticalSection(&This
->lock
);
3319 This
->vols
[index
] = level
;
3321 LeaveCriticalSection(&This
->lock
);
3326 static HRESULT WINAPI
AudioStreamVolume_GetChannelVolume(
3327 IAudioStreamVolume
*iface
, UINT32 index
, float *level
)
3329 ACImpl
*This
= impl_from_IAudioStreamVolume(iface
);
3331 TRACE("(%p)->(%d, %p)\n", This
, index
, level
);
3336 if(index
>= This
->fmt
->nChannels
)
3337 return E_INVALIDARG
;
3339 *level
= This
->vols
[index
];
3344 static HRESULT WINAPI
AudioStreamVolume_SetAllVolumes(
3345 IAudioStreamVolume
*iface
, UINT32 count
, const float *levels
)
3347 ACImpl
*This
= impl_from_IAudioStreamVolume(iface
);
3350 TRACE("(%p)->(%d, %p)\n", This
, count
, levels
);
3355 if(count
!= This
->fmt
->nChannels
)
3356 return E_INVALIDARG
;
3358 TRACE("ALSA does not support volume control\n");
3360 EnterCriticalSection(&This
->lock
);
3362 for(i
= 0; i
< count
; ++i
)
3363 This
->vols
[i
] = levels
[i
];
3365 LeaveCriticalSection(&This
->lock
);
3370 static HRESULT WINAPI
AudioStreamVolume_GetAllVolumes(
3371 IAudioStreamVolume
*iface
, UINT32 count
, float *levels
)
3373 ACImpl
*This
= impl_from_IAudioStreamVolume(iface
);
3376 TRACE("(%p)->(%d, %p)\n", This
, count
, levels
);
3381 if(count
!= This
->fmt
->nChannels
)
3382 return E_INVALIDARG
;
3384 EnterCriticalSection(&This
->lock
);
3386 for(i
= 0; i
< count
; ++i
)
3387 levels
[i
] = This
->vols
[i
];
3389 LeaveCriticalSection(&This
->lock
);
3394 static const IAudioStreamVolumeVtbl AudioStreamVolume_Vtbl
=
3396 AudioStreamVolume_QueryInterface
,
3397 AudioStreamVolume_AddRef
,
3398 AudioStreamVolume_Release
,
3399 AudioStreamVolume_GetChannelCount
,
3400 AudioStreamVolume_SetChannelVolume
,
3401 AudioStreamVolume_GetChannelVolume
,
3402 AudioStreamVolume_SetAllVolumes
,
3403 AudioStreamVolume_GetAllVolumes
3406 static HRESULT WINAPI
ChannelAudioVolume_QueryInterface(
3407 IChannelAudioVolume
*iface
, REFIID riid
, void **ppv
)
3409 TRACE("(%p)->(%s, %p)\n", iface
, debugstr_guid(riid
), ppv
);
3415 if(IsEqualIID(riid
, &IID_IUnknown
) ||
3416 IsEqualIID(riid
, &IID_IChannelAudioVolume
))
3419 IUnknown_AddRef((IUnknown
*)*ppv
);
3423 WARN("Unknown interface %s\n", debugstr_guid(riid
));
3424 return E_NOINTERFACE
;
3427 static ULONG WINAPI
ChannelAudioVolume_AddRef(IChannelAudioVolume
*iface
)
3429 AudioSessionWrapper
*This
= impl_from_IChannelAudioVolume(iface
);
3430 return AudioSessionControl_AddRef(&This
->IAudioSessionControl2_iface
);
3433 static ULONG WINAPI
ChannelAudioVolume_Release(IChannelAudioVolume
*iface
)
3435 AudioSessionWrapper
*This
= impl_from_IChannelAudioVolume(iface
);
3436 return AudioSessionControl_Release(&This
->IAudioSessionControl2_iface
);
3439 static HRESULT WINAPI
ChannelAudioVolume_GetChannelCount(
3440 IChannelAudioVolume
*iface
, UINT32
*out
)
3442 AudioSessionWrapper
*This
= impl_from_IChannelAudioVolume(iface
);
3443 AudioSession
*session
= This
->session
;
3445 TRACE("(%p)->(%p)\n", session
, out
);
3448 return NULL_PTR_ERR
;
3450 *out
= session
->channel_count
;
3455 static HRESULT WINAPI
ChannelAudioVolume_SetChannelVolume(
3456 IChannelAudioVolume
*iface
, UINT32 index
, float level
,
3457 const GUID
*context
)
3459 AudioSessionWrapper
*This
= impl_from_IChannelAudioVolume(iface
);
3460 AudioSession
*session
= This
->session
;
3462 TRACE("(%p)->(%d, %f, %s)\n", session
, index
, level
,
3463 wine_dbgstr_guid(context
));
3465 if(level
< 0.f
|| level
> 1.f
)
3466 return E_INVALIDARG
;
3468 if(index
>= session
->channel_count
)
3469 return E_INVALIDARG
;
3472 FIXME("Notifications not supported yet\n");
3474 TRACE("ALSA does not support volume control\n");
3476 EnterCriticalSection(&session
->lock
);
3478 session
->channel_vols
[index
] = level
;
3480 LeaveCriticalSection(&session
->lock
);
3485 static HRESULT WINAPI
ChannelAudioVolume_GetChannelVolume(
3486 IChannelAudioVolume
*iface
, UINT32 index
, float *level
)
3488 AudioSessionWrapper
*This
= impl_from_IChannelAudioVolume(iface
);
3489 AudioSession
*session
= This
->session
;
3491 TRACE("(%p)->(%d, %p)\n", session
, index
, level
);
3494 return NULL_PTR_ERR
;
3496 if(index
>= session
->channel_count
)
3497 return E_INVALIDARG
;
3499 *level
= session
->channel_vols
[index
];
3504 static HRESULT WINAPI
ChannelAudioVolume_SetAllVolumes(
3505 IChannelAudioVolume
*iface
, UINT32 count
, const float *levels
,
3506 const GUID
*context
)
3508 AudioSessionWrapper
*This
= impl_from_IChannelAudioVolume(iface
);
3509 AudioSession
*session
= This
->session
;
3512 TRACE("(%p)->(%d, %p, %s)\n", session
, count
, levels
,
3513 wine_dbgstr_guid(context
));
3516 return NULL_PTR_ERR
;
3518 if(count
!= session
->channel_count
)
3519 return E_INVALIDARG
;
3522 FIXME("Notifications not supported yet\n");
3524 TRACE("ALSA does not support volume control\n");
3526 EnterCriticalSection(&session
->lock
);
3528 for(i
= 0; i
< count
; ++i
)
3529 session
->channel_vols
[i
] = levels
[i
];
3531 LeaveCriticalSection(&session
->lock
);
3536 static HRESULT WINAPI
ChannelAudioVolume_GetAllVolumes(
3537 IChannelAudioVolume
*iface
, UINT32 count
, float *levels
)
3539 AudioSessionWrapper
*This
= impl_from_IChannelAudioVolume(iface
);
3540 AudioSession
*session
= This
->session
;
3543 TRACE("(%p)->(%d, %p)\n", session
, count
, levels
);
3546 return NULL_PTR_ERR
;
3548 if(count
!= session
->channel_count
)
3549 return E_INVALIDARG
;
3551 for(i
= 0; i
< count
; ++i
)
3552 levels
[i
] = session
->channel_vols
[i
];
3557 static const IChannelAudioVolumeVtbl ChannelAudioVolume_Vtbl
=
3559 ChannelAudioVolume_QueryInterface
,
3560 ChannelAudioVolume_AddRef
,
3561 ChannelAudioVolume_Release
,
3562 ChannelAudioVolume_GetChannelCount
,
3563 ChannelAudioVolume_SetChannelVolume
,
3564 ChannelAudioVolume_GetChannelVolume
,
3565 ChannelAudioVolume_SetAllVolumes
,
3566 ChannelAudioVolume_GetAllVolumes
3569 static HRESULT WINAPI
AudioSessionManager_QueryInterface(IAudioSessionManager2
*iface
,
3570 REFIID riid
, void **ppv
)
3572 TRACE("(%p)->(%s, %p)\n", iface
, debugstr_guid(riid
), ppv
);
3578 if(IsEqualIID(riid
, &IID_IUnknown
) ||
3579 IsEqualIID(riid
, &IID_IAudioSessionManager
) ||
3580 IsEqualIID(riid
, &IID_IAudioSessionManager2
))
3583 IUnknown_AddRef((IUnknown
*)*ppv
);
3587 WARN("Unknown interface %s\n", debugstr_guid(riid
));
3588 return E_NOINTERFACE
;
3591 static ULONG WINAPI
AudioSessionManager_AddRef(IAudioSessionManager2
*iface
)
3593 SessionMgr
*This
= impl_from_IAudioSessionManager2(iface
);
3595 ref
= InterlockedIncrement(&This
->ref
);
3596 TRACE("(%p) Refcount now %u\n", This
, ref
);
3600 static ULONG WINAPI
AudioSessionManager_Release(IAudioSessionManager2
*iface
)
3602 SessionMgr
*This
= impl_from_IAudioSessionManager2(iface
);
3604 ref
= InterlockedDecrement(&This
->ref
);
3605 TRACE("(%p) Refcount now %u\n", This
, ref
);
3607 HeapFree(GetProcessHeap(), 0, This
);
3611 static HRESULT WINAPI
AudioSessionManager_GetAudioSessionControl(
3612 IAudioSessionManager2
*iface
, const GUID
*session_guid
, DWORD flags
,
3613 IAudioSessionControl
**out
)
3615 SessionMgr
*This
= impl_from_IAudioSessionManager2(iface
);
3616 AudioSession
*session
;
3617 AudioSessionWrapper
*wrapper
;
3620 TRACE("(%p)->(%s, %x, %p)\n", This
, debugstr_guid(session_guid
),
3623 hr
= get_audio_session(session_guid
, This
->device
, 0, &session
);
3627 wrapper
= AudioSessionWrapper_Create(NULL
);
3629 return E_OUTOFMEMORY
;
3631 wrapper
->session
= session
;
3633 *out
= (IAudioSessionControl
*)&wrapper
->IAudioSessionControl2_iface
;
3638 static HRESULT WINAPI
AudioSessionManager_GetSimpleAudioVolume(
3639 IAudioSessionManager2
*iface
, const GUID
*session_guid
, DWORD flags
,
3640 ISimpleAudioVolume
**out
)
3642 SessionMgr
*This
= impl_from_IAudioSessionManager2(iface
);
3643 AudioSession
*session
;
3644 AudioSessionWrapper
*wrapper
;
3647 TRACE("(%p)->(%s, %x, %p)\n", This
, debugstr_guid(session_guid
),
3650 hr
= get_audio_session(session_guid
, This
->device
, 0, &session
);
3654 wrapper
= AudioSessionWrapper_Create(NULL
);
3656 return E_OUTOFMEMORY
;
3658 wrapper
->session
= session
;
3660 *out
= &wrapper
->ISimpleAudioVolume_iface
;
3665 static HRESULT WINAPI
AudioSessionManager_GetSessionEnumerator(
3666 IAudioSessionManager2
*iface
, IAudioSessionEnumerator
**out
)
3668 SessionMgr
*This
= impl_from_IAudioSessionManager2(iface
);
3669 FIXME("(%p)->(%p) - stub\n", This
, out
);
3673 static HRESULT WINAPI
AudioSessionManager_RegisterSessionNotification(
3674 IAudioSessionManager2
*iface
, IAudioSessionNotification
*notification
)
3676 SessionMgr
*This
= impl_from_IAudioSessionManager2(iface
);
3677 FIXME("(%p)->(%p) - stub\n", This
, notification
);
3681 static HRESULT WINAPI
AudioSessionManager_UnregisterSessionNotification(
3682 IAudioSessionManager2
*iface
, IAudioSessionNotification
*notification
)
3684 SessionMgr
*This
= impl_from_IAudioSessionManager2(iface
);
3685 FIXME("(%p)->(%p) - stub\n", This
, notification
);
3689 static HRESULT WINAPI
AudioSessionManager_RegisterDuckNotification(
3690 IAudioSessionManager2
*iface
, const WCHAR
*session_id
,
3691 IAudioVolumeDuckNotification
*notification
)
3693 SessionMgr
*This
= impl_from_IAudioSessionManager2(iface
);
3694 FIXME("(%p)->(%p) - stub\n", This
, notification
);
3698 static HRESULT WINAPI
AudioSessionManager_UnregisterDuckNotification(
3699 IAudioSessionManager2
*iface
,
3700 IAudioVolumeDuckNotification
*notification
)
3702 SessionMgr
*This
= impl_from_IAudioSessionManager2(iface
);
3703 FIXME("(%p)->(%p) - stub\n", This
, notification
);
3707 static const IAudioSessionManager2Vtbl AudioSessionManager2_Vtbl
=
3709 AudioSessionManager_QueryInterface
,
3710 AudioSessionManager_AddRef
,
3711 AudioSessionManager_Release
,
3712 AudioSessionManager_GetAudioSessionControl
,
3713 AudioSessionManager_GetSimpleAudioVolume
,
3714 AudioSessionManager_GetSessionEnumerator
,
3715 AudioSessionManager_RegisterSessionNotification
,
3716 AudioSessionManager_UnregisterSessionNotification
,
3717 AudioSessionManager_RegisterDuckNotification
,
3718 AudioSessionManager_UnregisterDuckNotification
3721 HRESULT WINAPI
AUDDRV_GetAudioSessionManager(IMMDevice
*device
,
3722 IAudioSessionManager2
**out
)
3726 This
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(SessionMgr
));
3728 return E_OUTOFMEMORY
;
3730 This
->IAudioSessionManager2_iface
.lpVtbl
= &AudioSessionManager2_Vtbl
;
3731 This
->device
= device
;
3734 *out
= &This
->IAudioSessionManager2_iface
;