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"
39 #include "mmdeviceapi.h"
45 #include "endpointvolume.h"
46 #include "audioclient.h"
47 #include "audiopolicy.h"
49 #include <alsa/asoundlib.h>
51 WINE_DEFAULT_DEBUG_CHANNEL(alsa
);
52 WINE_DECLARE_DEBUG_CHANNEL(winediag
);
54 #define NULL_PTR_ERR MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, RPC_X_NULL_REF_POINTER)
56 static const REFERENCE_TIME DefaultPeriod
= 100000;
57 static const REFERENCE_TIME MinimumPeriod
= 50000;
58 #define EXTRA_SAFE_RT 40000
61 typedef struct ACImpl ACImpl
;
63 typedef struct _AudioSession
{
74 CRITICAL_SECTION lock
;
79 typedef struct _AudioSessionWrapper
{
80 IAudioSessionControl2 IAudioSessionControl2_iface
;
81 IChannelAudioVolume IChannelAudioVolume_iface
;
82 ISimpleAudioVolume ISimpleAudioVolume_iface
;
87 AudioSession
*session
;
88 } AudioSessionWrapper
;
91 IAudioClient IAudioClient_iface
;
92 IAudioRenderClient IAudioRenderClient_iface
;
93 IAudioCaptureClient IAudioCaptureClient_iface
;
94 IAudioClock IAudioClock_iface
;
95 IAudioClock2 IAudioClock2_iface
;
96 IAudioStreamVolume IAudioStreamVolume_iface
;
100 snd_pcm_t
*pcm_handle
;
101 snd_pcm_uframes_t alsa_bufsize_frames
, alsa_period_frames
, safe_rewind_frames
;
102 snd_pcm_hw_params_t
*hw_params
; /* does not hold state between calls */
103 snd_pcm_format_t alsa_format
;
105 LARGE_INTEGER last_period_time
;
108 IUnknown
*pUnkFTMarshal
;
113 AUDCLNT_SHAREMODE share
;
119 int alsa_channel_map
[32];
121 BOOL initted
, started
;
122 REFERENCE_TIME mmdev_period_rt
;
123 UINT64 written_frames
, last_pos_frames
;
124 UINT32 bufsize_frames
, held_frames
, tmp_buffer_frames
, mmdev_period_frames
;
125 snd_pcm_uframes_t remapping_buf_frames
;
126 UINT32 lcl_offs_frames
; /* offs into local_buffer where valid data starts */
127 UINT32 wri_offs_frames
; /* where to write fresh data in local_buffer */
128 UINT32 hidden_frames
; /* ALSA reserve to ensure continuous rendering */
129 UINT32 data_in_alsa_frames
;
132 BYTE
*local_buffer
, *tmp_buffer
, *remapping_buf
, *silence_buf
;
133 LONG32 getbuf_last
; /* <0 when using tmp_buffer */
135 CRITICAL_SECTION lock
;
137 AudioSession
*session
;
138 AudioSessionWrapper
*session_wrapper
;
143 typedef struct _SessionMgr
{
144 IAudioSessionManager2 IAudioSessionManager2_iface
;
151 static HANDLE g_timer_q
;
153 static CRITICAL_SECTION g_sessions_lock
;
154 static CRITICAL_SECTION_DEBUG g_sessions_lock_debug
=
156 0, 0, &g_sessions_lock
,
157 { &g_sessions_lock_debug
.ProcessLocksList
, &g_sessions_lock_debug
.ProcessLocksList
},
158 0, 0, { (DWORD_PTR
)(__FILE__
": g_sessions_lock") }
160 static CRITICAL_SECTION g_sessions_lock
= { &g_sessions_lock_debug
, -1, 0, 0, 0, 0 };
161 static struct list g_sessions
= LIST_INIT(g_sessions
);
163 static const WCHAR defaultW
[] = {'d','e','f','a','u','l','t',0};
164 static const char defname
[] = "default";
166 static const WCHAR drv_keyW
[] = {'S','o','f','t','w','a','r','e','\\',
167 'W','i','n','e','\\','D','r','i','v','e','r','s','\\',
168 'w','i','n','e','a','l','s','a','.','d','r','v',0};
169 static const WCHAR drv_key_devicesW
[] = {'S','o','f','t','w','a','r','e','\\',
170 'W','i','n','e','\\','D','r','i','v','e','r','s','\\',
171 'w','i','n','e','a','l','s','a','.','d','r','v','\\','d','e','v','i','c','e','s',0};
172 static const WCHAR guidW
[] = {'g','u','i','d',0};
174 static const IAudioClientVtbl AudioClient_Vtbl
;
175 static const IAudioRenderClientVtbl AudioRenderClient_Vtbl
;
176 static const IAudioCaptureClientVtbl AudioCaptureClient_Vtbl
;
177 static const IAudioSessionControl2Vtbl AudioSessionControl2_Vtbl
;
178 static const ISimpleAudioVolumeVtbl SimpleAudioVolume_Vtbl
;
179 static const IAudioClockVtbl AudioClock_Vtbl
;
180 static const IAudioClock2Vtbl AudioClock2_Vtbl
;
181 static const IAudioStreamVolumeVtbl AudioStreamVolume_Vtbl
;
182 static const IChannelAudioVolumeVtbl ChannelAudioVolume_Vtbl
;
183 static const IAudioSessionManager2Vtbl AudioSessionManager2_Vtbl
;
185 static AudioSessionWrapper
*AudioSessionWrapper_Create(ACImpl
*client
);
187 static inline ACImpl
*impl_from_IAudioClient(IAudioClient
*iface
)
189 return CONTAINING_RECORD(iface
, ACImpl
, IAudioClient_iface
);
192 static inline ACImpl
*impl_from_IAudioRenderClient(IAudioRenderClient
*iface
)
194 return CONTAINING_RECORD(iface
, ACImpl
, IAudioRenderClient_iface
);
197 static inline ACImpl
*impl_from_IAudioCaptureClient(IAudioCaptureClient
*iface
)
199 return CONTAINING_RECORD(iface
, ACImpl
, IAudioCaptureClient_iface
);
202 static inline AudioSessionWrapper
*impl_from_IAudioSessionControl2(IAudioSessionControl2
*iface
)
204 return CONTAINING_RECORD(iface
, AudioSessionWrapper
, IAudioSessionControl2_iface
);
207 static inline AudioSessionWrapper
*impl_from_ISimpleAudioVolume(ISimpleAudioVolume
*iface
)
209 return CONTAINING_RECORD(iface
, AudioSessionWrapper
, ISimpleAudioVolume_iface
);
212 static inline AudioSessionWrapper
*impl_from_IChannelAudioVolume(IChannelAudioVolume
*iface
)
214 return CONTAINING_RECORD(iface
, AudioSessionWrapper
, IChannelAudioVolume_iface
);
217 static inline ACImpl
*impl_from_IAudioClock(IAudioClock
*iface
)
219 return CONTAINING_RECORD(iface
, ACImpl
, IAudioClock_iface
);
222 static inline ACImpl
*impl_from_IAudioClock2(IAudioClock2
*iface
)
224 return CONTAINING_RECORD(iface
, ACImpl
, IAudioClock2_iface
);
227 static inline ACImpl
*impl_from_IAudioStreamVolume(IAudioStreamVolume
*iface
)
229 return CONTAINING_RECORD(iface
, ACImpl
, IAudioStreamVolume_iface
);
232 static inline SessionMgr
*impl_from_IAudioSessionManager2(IAudioSessionManager2
*iface
)
234 return CONTAINING_RECORD(iface
, SessionMgr
, IAudioSessionManager2_iface
);
237 BOOL WINAPI
DllMain(HINSTANCE dll
, DWORD reason
, void *reserved
)
241 case DLL_PROCESS_ATTACH
:
242 g_timer_q
= CreateTimerQueue();
247 case DLL_PROCESS_DETACH
:
249 DeleteCriticalSection(&g_sessions_lock
);
255 /* From <dlls/mmdevapi/mmdevapi.h> */
256 enum DriverPriority
{
257 Priority_Unavailable
= 0,
263 int WINAPI
AUDDRV_GetPriority(void)
265 return Priority_Neutral
;
268 static void set_device_guid(EDataFlow flow
, HKEY drv_key
, const WCHAR
*key_name
,
276 lr
= RegCreateKeyExW(HKEY_CURRENT_USER
, drv_key_devicesW
, 0, NULL
, 0, KEY_WRITE
,
277 NULL
, &drv_key
, NULL
);
278 if(lr
!= ERROR_SUCCESS
){
279 ERR("RegCreateKeyEx(drv_key) failed: %u\n", lr
);
285 lr
= RegCreateKeyExW(drv_key
, key_name
, 0, NULL
, 0, KEY_WRITE
,
287 if(lr
!= ERROR_SUCCESS
){
288 ERR("RegCreateKeyEx(%s) failed: %u\n", wine_dbgstr_w(key_name
), lr
);
292 lr
= RegSetValueExW(key
, guidW
, 0, REG_BINARY
, (BYTE
*)guid
,
294 if(lr
!= ERROR_SUCCESS
)
295 ERR("RegSetValueEx(%s\\guid) failed: %u\n", wine_dbgstr_w(key_name
), lr
);
300 RegCloseKey(drv_key
);
303 static void get_device_guid(EDataFlow flow
, const char *device
, GUID
*guid
)
305 HKEY key
= NULL
, dev_key
;
306 DWORD type
, size
= sizeof(*guid
);
314 MultiByteToWideChar(CP_UNIXCP
, 0, device
, -1, key_name
+ 2,
315 (sizeof(key_name
) / sizeof(*key_name
)) - 2);
317 if(RegOpenKeyExW(HKEY_CURRENT_USER
, drv_key_devicesW
, 0, KEY_WRITE
|KEY_READ
, &key
) == ERROR_SUCCESS
){
318 if(RegOpenKeyExW(key
, key_name
, 0, KEY_READ
, &dev_key
) == ERROR_SUCCESS
){
319 if(RegQueryValueExW(dev_key
, guidW
, 0, &type
,
320 (BYTE
*)guid
, &size
) == ERROR_SUCCESS
){
321 if(type
== REG_BINARY
){
322 RegCloseKey(dev_key
);
326 ERR("Invalid type for device %s GUID: %u; ignoring and overwriting\n",
327 wine_dbgstr_w(key_name
), type
);
329 RegCloseKey(dev_key
);
335 set_device_guid(flow
, key
, key_name
, guid
);
341 static BOOL
alsa_try_open(const char *devnode
, snd_pcm_stream_t stream
)
346 TRACE("devnode: %s, stream: %d\n", devnode
, stream
);
348 if((err
= snd_pcm_open(&handle
, devnode
, stream
, SND_PCM_NONBLOCK
)) < 0){
349 WARN("The device \"%s\" failed to open: %d (%s).\n",
350 devnode
, err
, snd_strerror(err
));
354 snd_pcm_close(handle
);
358 static WCHAR
*construct_device_id(EDataFlow flow
, const WCHAR
*chunk1
, const char *chunk2
)
362 DWORD len_wchars
= 0, chunk1_len
, copied
= 0, prefix_len
;
364 static const WCHAR dashW
[] = {' ','-',' ',0};
365 static const size_t dashW_len
= (sizeof(dashW
) / sizeof(*dashW
)) - 1;
366 static const WCHAR outW
[] = {'O','u','t',':',' ',0};
367 static const WCHAR inW
[] = {'I','n',':',' ',0};
371 prefix_len
= (sizeof(outW
) / sizeof(*outW
)) - 1;
372 len_wchars
+= prefix_len
;
375 prefix_len
= (sizeof(inW
) / sizeof(*inW
)) - 1;
376 len_wchars
+= prefix_len
;
379 chunk1_len
= strlenW(chunk1
);
380 len_wchars
+= chunk1_len
;
383 len_wchars
+= dashW_len
;
385 len_wchars
+= MultiByteToWideChar(CP_UNIXCP
, 0, chunk2
, -1, NULL
, 0) - 1;
386 len_wchars
+= 1; /* NULL byte */
388 ret
= HeapAlloc(GetProcessHeap(), 0, len_wchars
* sizeof(WCHAR
));
390 memcpy(ret
, prefix
, prefix_len
* sizeof(WCHAR
));
391 copied
+= prefix_len
;
393 memcpy(ret
+ copied
, chunk1
, chunk1_len
* sizeof(WCHAR
));
394 copied
+= chunk1_len
;
396 if(chunk1
&& chunk2
){
397 memcpy(ret
+ copied
, dashW
, dashW_len
* sizeof(WCHAR
));
401 MultiByteToWideChar(CP_UNIXCP
, 0, chunk2
, -1, ret
+ copied
, len_wchars
- copied
);
405 TRACE("Enumerated device: %s\n", wine_dbgstr_w(ret
));
410 static HRESULT
alsa_get_card_devices(EDataFlow flow
, snd_pcm_stream_t stream
,
411 WCHAR
***ids
, GUID
**guids
, UINT
*num
, snd_ctl_t
*ctl
, int card
,
412 const WCHAR
*cardnameW
)
415 snd_pcm_info_t
*info
;
417 info
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, snd_pcm_info_sizeof());
419 return E_OUTOFMEMORY
;
421 snd_pcm_info_set_subdevice(info
, 0);
422 snd_pcm_info_set_stream(info
, stream
);
425 for(err
= snd_ctl_pcm_next_device(ctl
, &device
); device
!= -1 && err
>= 0;
426 err
= snd_ctl_pcm_next_device(ctl
, &device
)){
430 snd_pcm_info_set_device(info
, device
);
432 if((err
= snd_ctl_pcm_info(ctl
, info
)) < 0){
434 /* This device doesn't have the right stream direction */
437 WARN("Failed to get info for card %d, device %d: %d (%s)\n",
438 card
, device
, err
, snd_strerror(err
));
442 sprintf(devnode
, "plughw:%d,%d", card
, device
);
443 if(!alsa_try_open(devnode
, stream
))
447 *ids
= HeapReAlloc(GetProcessHeap(), 0, *ids
, sizeof(WCHAR
*) * (*num
+ 1));
448 *guids
= HeapReAlloc(GetProcessHeap(), 0, *guids
, sizeof(GUID
) * (*num
+ 1));
450 *ids
= HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR
*));
451 *guids
= HeapAlloc(GetProcessHeap(), 0, sizeof(GUID
));
454 devname
= snd_pcm_info_get_name(info
);
456 WARN("Unable to get device name for card %d, device %d\n", card
,
461 (*ids
)[*num
] = construct_device_id(flow
, cardnameW
, devname
);
462 get_device_guid(flow
, devnode
, &(*guids
)[*num
]);
467 HeapFree(GetProcessHeap(), 0, info
);
470 WARN("Got a failure during device enumeration on card %d: %d (%s)\n",
471 card
, err
, snd_strerror(err
));
476 static void get_reg_devices(EDataFlow flow
, snd_pcm_stream_t stream
, WCHAR
***ids
,
477 GUID
**guids
, UINT
*num
)
479 static const WCHAR ALSAOutputDevices
[] = {'A','L','S','A','O','u','t','p','u','t','D','e','v','i','c','e','s',0};
480 static const WCHAR ALSAInputDevices
[] = {'A','L','S','A','I','n','p','u','t','D','e','v','i','c','e','s',0};
482 WCHAR reg_devices
[256];
483 DWORD size
= sizeof(reg_devices
), type
;
484 const WCHAR
*value_name
= (stream
== SND_PCM_STREAM_PLAYBACK
) ? ALSAOutputDevices
: ALSAInputDevices
;
486 /* @@ Wine registry key: HKCU\Software\Wine\Drivers\winealsa.drv */
487 if(RegOpenKeyW(HKEY_CURRENT_USER
, drv_keyW
, &key
) == ERROR_SUCCESS
){
488 if(RegQueryValueExW(key
, value_name
, 0, &type
,
489 (BYTE
*)reg_devices
, &size
) == ERROR_SUCCESS
){
490 WCHAR
*p
= reg_devices
;
492 if(type
!= REG_MULTI_SZ
){
493 ERR("Registry ALSA device list value type must be REG_MULTI_SZ\n");
501 WideCharToMultiByte(CP_UNIXCP
, 0, p
, -1, devname
, sizeof(devname
), NULL
, NULL
);
503 if(alsa_try_open(devname
, stream
)){
505 *ids
= HeapReAlloc(GetProcessHeap(), 0, *ids
, sizeof(WCHAR
*) * (*num
+ 1));
506 *guids
= HeapReAlloc(GetProcessHeap(), 0, *guids
, sizeof(GUID
) * (*num
+ 1));
508 *ids
= HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR
*));
509 *guids
= HeapAlloc(GetProcessHeap(), 0, sizeof(GUID
));
511 (*ids
)[*num
] = construct_device_id(flow
, p
, NULL
);
512 get_device_guid(flow
, devname
, &(*guids
)[*num
]);
516 p
+= lstrlenW(p
) + 1;
524 static HRESULT
alsa_enum_devices(EDataFlow flow
, WCHAR
***ids
, GUID
**guids
,
527 snd_pcm_stream_t stream
= (flow
== eRender
? SND_PCM_STREAM_PLAYBACK
:
528 SND_PCM_STREAM_CAPTURE
);
534 if(alsa_try_open(defname
, stream
)){
535 *ids
= HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR
*));
536 (*ids
)[0] = construct_device_id(flow
, defaultW
, NULL
);
537 *guids
= HeapAlloc(GetProcessHeap(), 0, sizeof(GUID
));
538 get_device_guid(flow
, defname
, &(*guids
)[0]);
542 get_reg_devices(flow
, stream
, ids
, guids
, num
);
544 for(err
= snd_card_next(&card
); card
!= -1 && err
>= 0;
545 err
= snd_card_next(&card
)){
552 sprintf(cardpath
, "hw:%u", card
);
554 if((err
= snd_ctl_open(&ctl
, cardpath
, 0)) < 0){
555 WARN("Unable to open ctl for ALSA device %s: %d (%s)\n", cardpath
,
556 err
, snd_strerror(err
));
560 if(snd_card_get_name(card
, &cardname
) < 0) {
561 /* FIXME: Should be localized */
562 static const WCHAR nameW
[] = {'U','n','k','n','o','w','n',' ','s','o','u','n','d','c','a','r','d',0};
563 WARN("Unable to get card name for ALSA device %s: %d (%s)\n",
564 cardpath
, err
, snd_strerror(err
));
565 alsa_get_card_devices(flow
, stream
, ids
, guids
, num
, ctl
, card
, nameW
);
567 len
= MultiByteToWideChar(CP_UNIXCP
, 0, cardname
, -1, NULL
, 0);
568 cardnameW
= HeapAlloc(GetProcessHeap(), 0, len
* sizeof(WCHAR
));
573 return E_OUTOFMEMORY
;
575 MultiByteToWideChar(CP_UNIXCP
, 0, cardname
, -1, cardnameW
, len
);
577 alsa_get_card_devices(flow
, stream
, ids
, guids
, num
, ctl
, card
, cardnameW
);
579 HeapFree(GetProcessHeap(), 0, cardnameW
);
587 WARN("Got a failure during card enumeration: %d (%s)\n",
588 err
, snd_strerror(err
));
593 HRESULT WINAPI
AUDDRV_GetEndpointIDs(EDataFlow flow
, WCHAR
***ids
, GUID
**guids
,
594 UINT
*num
, UINT
*def_index
)
598 TRACE("%d %p %p %p %p\n", flow
, ids
, guids
, num
, def_index
);
603 hr
= alsa_enum_devices(flow
, ids
, guids
, num
);
606 for(i
= 0; i
< *num
; ++i
)
607 HeapFree(GetProcessHeap(), 0, (*ids
)[i
]);
608 HeapFree(GetProcessHeap(), 0, *ids
);
609 HeapFree(GetProcessHeap(), 0, *guids
);
610 return E_OUTOFMEMORY
;
613 TRACE("Enumerated %u devices\n", *num
);
616 HeapFree(GetProcessHeap(), 0, *ids
);
618 HeapFree(GetProcessHeap(), 0, *guids
);
627 /* Using the pulse PCM device from alsa-plugins 1.0.24 triggers a bug
628 * which causes audio to cease playing after a few minutes of playback.
629 * Setting handle_underrun=1 on pulse-backed ALSA devices seems to work
630 * around this issue. */
631 static snd_config_t
*make_handle_underrun_config(const char *name
)
633 snd_config_t
*lconf
, *dev_node
, *hu_node
, *type_node
;
634 char dev_node_name
[64];
635 const char *type_str
;
640 if((err
= snd_config_copy(&lconf
, snd_config
)) < 0){
641 WARN("snd_config_copy failed: %d (%s)\n", err
, snd_strerror(err
));
645 sprintf(dev_node_name
, "pcm.%s", name
);
646 err
= snd_config_search(lconf
, dev_node_name
, &dev_node
);
648 snd_config_delete(lconf
);
652 snd_config_delete(lconf
);
653 WARN("snd_config_search failed: %d (%s)\n", err
, snd_strerror(err
));
657 /* ALSA is extremely fragile. If it runs into a config setting it doesn't
658 * recognize, it tends to fail or assert. So we only want to inject
659 * handle_underrun=1 on devices that we know will recognize it. */
660 err
= snd_config_search(dev_node
, "type", &type_node
);
662 snd_config_delete(lconf
);
666 snd_config_delete(lconf
);
667 WARN("snd_config_search failed: %d (%s)\n", err
, snd_strerror(err
));
671 if((err
= snd_config_get_string(type_node
, &type_str
)) < 0){
672 snd_config_delete(lconf
);
676 if(strcmp(type_str
, "pulse") != 0){
677 snd_config_delete(lconf
);
681 err
= snd_config_search(dev_node
, "handle_underrun", &hu_node
);
683 /* user already has an explicit handle_underrun setting, so don't
684 * use a local config */
685 snd_config_delete(lconf
);
689 snd_config_delete(lconf
);
690 WARN("snd_config_search failed: %d (%s)\n", err
, snd_strerror(err
));
694 if((err
= snd_config_imake_integer(&hu_node
, "handle_underrun", 1)) < 0){
695 snd_config_delete(lconf
);
696 WARN("snd_config_imake_integer failed: %d (%s)\n", err
,
701 if((err
= snd_config_add(dev_node
, hu_node
)) < 0){
702 snd_config_delete(lconf
);
703 WARN("snd_config_add failed: %d (%s)\n", err
, snd_strerror(err
));
710 static BOOL
get_alsa_name_by_guid(GUID
*guid
, char *name
, DWORD name_size
, EDataFlow
*flow
)
717 if(RegOpenKeyExW(HKEY_CURRENT_USER
, drv_key_devicesW
, 0, KEY_READ
, &devices_key
) != ERROR_SUCCESS
){
718 ERR("No devices found in registry?\n");
727 key_name_size
= sizeof(key_name
)/sizeof(WCHAR
);
728 if(RegEnumKeyExW(devices_key
, i
++, key_name
, &key_name_size
, NULL
,
729 NULL
, NULL
, NULL
) != ERROR_SUCCESS
)
732 if(RegOpenKeyExW(devices_key
, key_name
, 0, KEY_READ
, &key
) != ERROR_SUCCESS
){
733 WARN("Couldn't open key: %s\n", wine_dbgstr_w(key_name
));
737 size
= sizeof(reg_guid
);
738 if(RegQueryValueExW(key
, guidW
, 0, &type
,
739 (BYTE
*)®_guid
, &size
) == ERROR_SUCCESS
){
740 if(IsEqualGUID(®_guid
, guid
)){
742 RegCloseKey(devices_key
);
744 TRACE("Found matching device key: %s\n", wine_dbgstr_w(key_name
));
746 if(key_name
[0] == '0')
748 else if(key_name
[0] == '1')
751 ERR("Unknown device type: %c\n", key_name
[0]);
755 WideCharToMultiByte(CP_UNIXCP
, 0, key_name
+ 2, -1, name
, name_size
, NULL
, NULL
);
764 RegCloseKey(devices_key
);
766 WARN("No matching device in registry for GUID %s\n", debugstr_guid(guid
));
771 HRESULT WINAPI
AUDDRV_GetAudioEndpoint(GUID
*guid
, IMMDevice
*dev
, IAudioClient
**out
)
775 snd_pcm_stream_t stream
;
777 static BOOL handle_underrun
= TRUE
;
782 TRACE("%s %p %p\n", debugstr_guid(guid
), dev
, out
);
784 if(!get_alsa_name_by_guid(guid
, alsa_name
, sizeof(alsa_name
), &dataflow
))
785 return AUDCLNT_E_DEVICE_INVALIDATED
;
787 This
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(ACImpl
));
789 return E_OUTOFMEMORY
;
791 This
->IAudioClient_iface
.lpVtbl
= &AudioClient_Vtbl
;
792 This
->IAudioRenderClient_iface
.lpVtbl
= &AudioRenderClient_Vtbl
;
793 This
->IAudioCaptureClient_iface
.lpVtbl
= &AudioCaptureClient_Vtbl
;
794 This
->IAudioClock_iface
.lpVtbl
= &AudioClock_Vtbl
;
795 This
->IAudioClock2_iface
.lpVtbl
= &AudioClock2_Vtbl
;
796 This
->IAudioStreamVolume_iface
.lpVtbl
= &AudioStreamVolume_Vtbl
;
798 if(dataflow
== eRender
)
799 stream
= SND_PCM_STREAM_PLAYBACK
;
800 else if(dataflow
== eCapture
)
801 stream
= SND_PCM_STREAM_CAPTURE
;
803 HeapFree(GetProcessHeap(), 0, This
);
807 hr
= CoCreateFreeThreadedMarshaler((IUnknown
*)&This
->IAudioClient_iface
,
808 (IUnknown
**)&This
->pUnkFTMarshal
);
810 HeapFree(GetProcessHeap(), 0, This
);
814 This
->dataflow
= dataflow
;
815 if(handle_underrun
&& ((lconf
= make_handle_underrun_config(alsa_name
)))){
816 err
= snd_pcm_open_lconf(&This
->pcm_handle
, alsa_name
, stream
, SND_PCM_NONBLOCK
, lconf
);
817 TRACE("Opening PCM device \"%s\" with handle_underrun: %d\n", alsa_name
, err
);
818 snd_config_delete(lconf
);
819 /* Pulse <= 2010 returns EINVAL, it does not know handle_underrun. */
821 ERR_(winediag
)("PulseAudio \"%s\" %d without handle_underrun. Audio may hang."
822 " Please upgrade to alsa_plugins >= 1.0.24\n", alsa_name
, err
);
823 handle_underrun
= FALSE
;
828 err
= snd_pcm_open(&This
->pcm_handle
, alsa_name
, stream
, SND_PCM_NONBLOCK
);
831 HeapFree(GetProcessHeap(), 0, This
);
832 WARN("Unable to open PCM \"%s\": %d (%s)\n", alsa_name
, err
, snd_strerror(err
));
835 return AUDCLNT_E_DEVICE_IN_USE
;
837 return AUDCLNT_E_ENDPOINT_CREATE_FAILED
;
841 This
->hw_params
= HeapAlloc(GetProcessHeap(), 0,
842 snd_pcm_hw_params_sizeof());
843 if(!This
->hw_params
){
844 snd_pcm_close(This
->pcm_handle
);
845 HeapFree(GetProcessHeap(), 0, This
);
846 return E_OUTOFMEMORY
;
849 InitializeCriticalSection(&This
->lock
);
850 This
->lock
.DebugInfo
->Spare
[0] = (DWORD_PTR
)(__FILE__
": ACImpl.lock");
853 IMMDevice_AddRef(This
->parent
);
855 *out
= &This
->IAudioClient_iface
;
856 IAudioClient_AddRef(&This
->IAudioClient_iface
);
861 static HRESULT WINAPI
AudioClient_QueryInterface(IAudioClient
*iface
,
862 REFIID riid
, void **ppv
)
864 ACImpl
*This
= impl_from_IAudioClient(iface
);
865 TRACE("(%p)->(%s, %p)\n", iface
, debugstr_guid(riid
), ppv
);
870 if(IsEqualIID(riid
, &IID_IUnknown
) || IsEqualIID(riid
, &IID_IAudioClient
))
872 else if(IsEqualIID(riid
, &IID_IMarshal
))
873 return IUnknown_QueryInterface(This
->pUnkFTMarshal
, riid
, ppv
);
876 IUnknown_AddRef((IUnknown
*)*ppv
);
879 WARN("Unknown interface %s\n", debugstr_guid(riid
));
880 return E_NOINTERFACE
;
883 static ULONG WINAPI
AudioClient_AddRef(IAudioClient
*iface
)
885 ACImpl
*This
= impl_from_IAudioClient(iface
);
887 ref
= InterlockedIncrement(&This
->ref
);
888 TRACE("(%p) Refcount now %u\n", This
, ref
);
892 static ULONG WINAPI
AudioClient_Release(IAudioClient
*iface
)
894 ACImpl
*This
= impl_from_IAudioClient(iface
);
897 ref
= InterlockedDecrement(&This
->ref
);
898 TRACE("(%p) Refcount now %u\n", This
, ref
);
903 event
= CreateEventW(NULL
, TRUE
, FALSE
, NULL
);
904 wait
= !DeleteTimerQueueTimer(g_timer_q
, This
->timer
, event
);
905 wait
= wait
&& GetLastError() == ERROR_IO_PENDING
;
907 WaitForSingleObject(event
, INFINITE
);
911 IAudioClient_Stop(iface
);
912 IMMDevice_Release(This
->parent
);
913 IUnknown_Release(This
->pUnkFTMarshal
);
914 This
->lock
.DebugInfo
->Spare
[0] = 0;
915 DeleteCriticalSection(&This
->lock
);
916 snd_pcm_drop(This
->pcm_handle
);
917 snd_pcm_close(This
->pcm_handle
);
919 EnterCriticalSection(&g_sessions_lock
);
920 list_remove(&This
->entry
);
921 LeaveCriticalSection(&g_sessions_lock
);
923 HeapFree(GetProcessHeap(), 0, This
->vols
);
924 HeapFree(GetProcessHeap(), 0, This
->local_buffer
);
925 HeapFree(GetProcessHeap(), 0, This
->remapping_buf
);
926 HeapFree(GetProcessHeap(), 0, This
->silence_buf
);
927 HeapFree(GetProcessHeap(), 0, This
->tmp_buffer
);
928 HeapFree(GetProcessHeap(), 0, This
->hw_params
);
929 CoTaskMemFree(This
->fmt
);
930 HeapFree(GetProcessHeap(), 0, This
);
935 static void dump_fmt(const WAVEFORMATEX
*fmt
)
937 TRACE("wFormatTag: 0x%x (", fmt
->wFormatTag
);
938 switch(fmt
->wFormatTag
){
939 case WAVE_FORMAT_PCM
:
940 TRACE("WAVE_FORMAT_PCM");
942 case WAVE_FORMAT_IEEE_FLOAT
:
943 TRACE("WAVE_FORMAT_IEEE_FLOAT");
945 case WAVE_FORMAT_EXTENSIBLE
:
946 TRACE("WAVE_FORMAT_EXTENSIBLE");
954 TRACE("nChannels: %u\n", fmt
->nChannels
);
955 TRACE("nSamplesPerSec: %u\n", fmt
->nSamplesPerSec
);
956 TRACE("nAvgBytesPerSec: %u\n", fmt
->nAvgBytesPerSec
);
957 TRACE("nBlockAlign: %u\n", fmt
->nBlockAlign
);
958 TRACE("wBitsPerSample: %u\n", fmt
->wBitsPerSample
);
959 TRACE("cbSize: %u\n", fmt
->cbSize
);
961 if(fmt
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
){
962 WAVEFORMATEXTENSIBLE
*fmtex
= (void*)fmt
;
963 TRACE("dwChannelMask: %08x\n", fmtex
->dwChannelMask
);
964 TRACE("Samples: %04x\n", fmtex
->Samples
.wReserved
);
965 TRACE("SubFormat: %s\n", wine_dbgstr_guid(&fmtex
->SubFormat
));
969 static WAVEFORMATEX
*clone_format(const WAVEFORMATEX
*fmt
)
974 if(fmt
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
)
975 size
= sizeof(WAVEFORMATEXTENSIBLE
);
977 size
= sizeof(WAVEFORMATEX
);
979 ret
= CoTaskMemAlloc(size
);
983 memcpy(ret
, fmt
, size
);
985 ret
->cbSize
= size
- sizeof(WAVEFORMATEX
);
990 static snd_pcm_format_t
alsa_format(const WAVEFORMATEX
*fmt
)
992 snd_pcm_format_t format
= SND_PCM_FORMAT_UNKNOWN
;
993 const WAVEFORMATEXTENSIBLE
*fmtex
= (const WAVEFORMATEXTENSIBLE
*)fmt
;
995 if(fmt
->wFormatTag
== WAVE_FORMAT_PCM
||
996 (fmt
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
&&
997 IsEqualGUID(&fmtex
->SubFormat
, &KSDATAFORMAT_SUBTYPE_PCM
))){
998 if(fmt
->wBitsPerSample
== 8)
999 format
= SND_PCM_FORMAT_U8
;
1000 else if(fmt
->wBitsPerSample
== 16)
1001 format
= SND_PCM_FORMAT_S16_LE
;
1002 else if(fmt
->wBitsPerSample
== 24)
1003 format
= SND_PCM_FORMAT_S24_3LE
;
1004 else if(fmt
->wBitsPerSample
== 32)
1005 format
= SND_PCM_FORMAT_S32_LE
;
1007 WARN("Unsupported bit depth: %u\n", fmt
->wBitsPerSample
);
1008 if(fmt
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
&&
1009 fmt
->wBitsPerSample
!= fmtex
->Samples
.wValidBitsPerSample
){
1010 if(fmtex
->Samples
.wValidBitsPerSample
== 20 && fmt
->wBitsPerSample
== 24)
1011 format
= SND_PCM_FORMAT_S20_3LE
;
1013 WARN("Unsupported ValidBits: %u\n", fmtex
->Samples
.wValidBitsPerSample
);
1015 }else if(fmt
->wFormatTag
== WAVE_FORMAT_IEEE_FLOAT
||
1016 (fmt
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
&&
1017 IsEqualGUID(&fmtex
->SubFormat
, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
))){
1018 if(fmt
->wBitsPerSample
== 32)
1019 format
= SND_PCM_FORMAT_FLOAT_LE
;
1020 else if(fmt
->wBitsPerSample
== 64)
1021 format
= SND_PCM_FORMAT_FLOAT64_LE
;
1023 WARN("Unsupported float size: %u\n", fmt
->wBitsPerSample
);
1025 WARN("Unknown wave format: %04x\n", fmt
->wFormatTag
);
1029 static void session_init_vols(AudioSession
*session
, UINT channels
)
1031 if(session
->channel_count
< channels
){
1034 if(session
->channel_vols
)
1035 session
->channel_vols
= HeapReAlloc(GetProcessHeap(), 0,
1036 session
->channel_vols
, sizeof(float) * channels
);
1038 session
->channel_vols
= HeapAlloc(GetProcessHeap(), 0,
1039 sizeof(float) * channels
);
1040 if(!session
->channel_vols
)
1043 for(i
= session
->channel_count
; i
< channels
; ++i
)
1044 session
->channel_vols
[i
] = 1.f
;
1046 session
->channel_count
= channels
;
1050 static AudioSession
*create_session(const GUID
*guid
, IMMDevice
*device
,
1055 ret
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(AudioSession
));
1059 memcpy(&ret
->guid
, guid
, sizeof(GUID
));
1061 ret
->device
= device
;
1063 list_init(&ret
->clients
);
1065 list_add_head(&g_sessions
, &ret
->entry
);
1067 InitializeCriticalSection(&ret
->lock
);
1068 ret
->lock
.DebugInfo
->Spare
[0] = (DWORD_PTR
)(__FILE__
": AudioSession.lock");
1070 session_init_vols(ret
, num_channels
);
1072 ret
->master_vol
= 1.f
;
1077 /* if channels == 0, then this will return or create a session with
1078 * matching dataflow and GUID. otherwise, channels must also match */
1079 static HRESULT
get_audio_session(const GUID
*sessionguid
,
1080 IMMDevice
*device
, UINT channels
, AudioSession
**out
)
1082 AudioSession
*session
;
1084 if(!sessionguid
|| IsEqualGUID(sessionguid
, &GUID_NULL
)){
1085 *out
= create_session(&GUID_NULL
, device
, channels
);
1087 return E_OUTOFMEMORY
;
1093 LIST_FOR_EACH_ENTRY(session
, &g_sessions
, AudioSession
, entry
){
1094 if(session
->device
== device
&&
1095 IsEqualGUID(sessionguid
, &session
->guid
)){
1096 session_init_vols(session
, channels
);
1103 *out
= create_session(sessionguid
, device
, channels
);
1105 return E_OUTOFMEMORY
;
1111 static int alsa_channel_index(DWORD flag
)
1114 case SPEAKER_FRONT_LEFT
:
1116 case SPEAKER_FRONT_RIGHT
:
1118 case SPEAKER_BACK_LEFT
:
1120 case SPEAKER_BACK_RIGHT
:
1122 case SPEAKER_FRONT_CENTER
:
1124 case SPEAKER_LOW_FREQUENCY
:
1126 case SPEAKER_SIDE_LEFT
:
1128 case SPEAKER_SIDE_RIGHT
:
1134 static BOOL
need_remapping(ACImpl
*This
, const WAVEFORMATEX
*fmt
, int *map
)
1137 for(i
= 0; i
< fmt
->nChannels
; ++i
){
1144 static DWORD
get_channel_mask(unsigned int channels
)
1150 return KSAUDIO_SPEAKER_MONO
;
1152 return KSAUDIO_SPEAKER_STEREO
;
1154 return KSAUDIO_SPEAKER_STEREO
| SPEAKER_LOW_FREQUENCY
;
1156 return KSAUDIO_SPEAKER_QUAD
; /* not _SURROUND */
1158 return KSAUDIO_SPEAKER_QUAD
| SPEAKER_LOW_FREQUENCY
;
1160 return KSAUDIO_SPEAKER_5POINT1
; /* not 5POINT1_SURROUND */
1162 return KSAUDIO_SPEAKER_5POINT1
| SPEAKER_BACK_CENTER
;
1164 return KSAUDIO_SPEAKER_7POINT1_SURROUND
; /* Vista deprecates 7POINT1 */
1166 FIXME("Unknown speaker configuration: %u\n", channels
);
1170 static HRESULT
map_channels(ACImpl
*This
, const WAVEFORMATEX
*fmt
, int *alsa_channels
, int *map
)
1174 if(This
->dataflow
!= eCapture
&& (fmt
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
|| fmt
->nChannels
> 2) ){
1175 WAVEFORMATEXTENSIBLE
*fmtex
= (void*)fmt
;
1176 DWORD mask
, flag
= SPEAKER_FRONT_LEFT
;
1179 if(fmt
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
&&
1180 fmtex
->dwChannelMask
!= 0)
1181 mask
= fmtex
->dwChannelMask
;
1183 mask
= get_channel_mask(fmt
->nChannels
);
1187 while(i
< fmt
->nChannels
&& !(flag
& SPEAKER_RESERVED
)){
1189 map
[i
] = alsa_channel_index(flag
);
1190 TRACE("Mapping mmdevapi channel %u (0x%x) to ALSA channel %d\n",
1192 if(map
[i
] >= *alsa_channels
)
1193 *alsa_channels
= map
[i
] + 1;
1199 while(i
< fmt
->nChannels
){
1200 map
[i
] = *alsa_channels
;
1201 TRACE("Mapping mmdevapi channel %u to ALSA channel %d\n",
1207 for(i
= 0; i
< fmt
->nChannels
; ++i
){
1209 map
[i
] = *alsa_channels
;
1211 TRACE("Remapping mmdevapi channel %u to ALSA channel %d\n",
1216 need_remap
= need_remapping(This
, fmt
, map
);
1218 *alsa_channels
= fmt
->nChannels
;
1223 TRACE("need_remapping: %u, alsa_channels: %d\n", need_remap
, *alsa_channels
);
1225 return need_remap
? S_OK
: S_FALSE
;
1228 static void silence_buffer(ACImpl
*This
, BYTE
*buffer
, UINT32 frames
)
1230 WAVEFORMATEXTENSIBLE
*fmtex
= (WAVEFORMATEXTENSIBLE
*)This
->fmt
;
1231 if((This
->fmt
->wFormatTag
== WAVE_FORMAT_PCM
||
1232 (This
->fmt
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
&&
1233 IsEqualGUID(&fmtex
->SubFormat
, &KSDATAFORMAT_SUBTYPE_PCM
))) &&
1234 This
->fmt
->wBitsPerSample
== 8)
1235 memset(buffer
, 128, frames
* This
->fmt
->nBlockAlign
);
1237 memset(buffer
, 0, frames
* This
->fmt
->nBlockAlign
);
1240 static HRESULT WINAPI
AudioClient_Initialize(IAudioClient
*iface
,
1241 AUDCLNT_SHAREMODE mode
, DWORD flags
, REFERENCE_TIME duration
,
1242 REFERENCE_TIME period
, const WAVEFORMATEX
*fmt
,
1243 const GUID
*sessionguid
)
1245 ACImpl
*This
= impl_from_IAudioClient(iface
);
1246 snd_pcm_sw_params_t
*sw_params
= NULL
;
1247 snd_pcm_format_t format
;
1248 unsigned int rate
, alsa_period_us
;
1252 TRACE("(%p)->(%x, %x, %s, %s, %p, %s)\n", This
, mode
, flags
,
1253 wine_dbgstr_longlong(duration
), wine_dbgstr_longlong(period
), fmt
, debugstr_guid(sessionguid
));
1258 if(mode
!= AUDCLNT_SHAREMODE_SHARED
&& mode
!= AUDCLNT_SHAREMODE_EXCLUSIVE
)
1259 return AUDCLNT_E_NOT_INITIALIZED
;
1261 if(flags
& ~(AUDCLNT_STREAMFLAGS_CROSSPROCESS
|
1262 AUDCLNT_STREAMFLAGS_LOOPBACK
|
1263 AUDCLNT_STREAMFLAGS_EVENTCALLBACK
|
1264 AUDCLNT_STREAMFLAGS_NOPERSIST
|
1265 AUDCLNT_STREAMFLAGS_RATEADJUST
|
1266 AUDCLNT_SESSIONFLAGS_EXPIREWHENUNOWNED
|
1267 AUDCLNT_SESSIONFLAGS_DISPLAY_HIDE
|
1268 AUDCLNT_SESSIONFLAGS_DISPLAY_HIDEWHENEXPIRED
)){
1269 TRACE("Unknown flags: %08x\n", flags
);
1270 return E_INVALIDARG
;
1273 if(mode
== AUDCLNT_SHAREMODE_SHARED
){
1274 period
= DefaultPeriod
;
1275 if( duration
< 3 * period
)
1276 duration
= 3 * period
;
1278 if(fmt
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
){
1279 if(((WAVEFORMATEXTENSIBLE
*)fmt
)->dwChannelMask
== 0 ||
1280 ((WAVEFORMATEXTENSIBLE
*)fmt
)->dwChannelMask
& SPEAKER_RESERVED
)
1281 return AUDCLNT_E_UNSUPPORTED_FORMAT
;
1285 period
= DefaultPeriod
; /* not minimum */
1286 if(period
< MinimumPeriod
|| period
> 5000000)
1287 return AUDCLNT_E_INVALID_DEVICE_PERIOD
;
1288 if(duration
> 20000000) /* the smaller the period, the lower this limit */
1289 return AUDCLNT_E_BUFFER_SIZE_ERROR
;
1290 if(flags
& AUDCLNT_STREAMFLAGS_EVENTCALLBACK
){
1291 if(duration
!= period
)
1292 return AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL
;
1293 FIXME("EXCLUSIVE mode with EVENTCALLBACK\n");
1294 return AUDCLNT_E_DEVICE_IN_USE
;
1296 if( duration
< 8 * period
)
1297 duration
= 8 * period
; /* may grow above 2s */
1301 EnterCriticalSection(&This
->lock
);
1304 LeaveCriticalSection(&This
->lock
);
1305 return AUDCLNT_E_ALREADY_INITIALIZED
;
1310 This
->need_remapping
= map_channels(This
, fmt
, &This
->alsa_channels
, This
->alsa_channel_map
) == S_OK
;
1312 if((err
= snd_pcm_hw_params_any(This
->pcm_handle
, This
->hw_params
)) < 0){
1313 WARN("Unable to get hw_params: %d (%s)\n", err
, snd_strerror(err
));
1314 hr
= AUDCLNT_E_ENDPOINT_CREATE_FAILED
;
1318 if((err
= snd_pcm_hw_params_set_access(This
->pcm_handle
, This
->hw_params
,
1319 SND_PCM_ACCESS_RW_INTERLEAVED
)) < 0){
1320 WARN("Unable to set access: %d (%s)\n", err
, snd_strerror(err
));
1321 hr
= AUDCLNT_E_ENDPOINT_CREATE_FAILED
;
1325 format
= alsa_format(fmt
);
1326 if (format
== SND_PCM_FORMAT_UNKNOWN
){
1327 hr
= AUDCLNT_E_UNSUPPORTED_FORMAT
;
1331 if((err
= snd_pcm_hw_params_set_format(This
->pcm_handle
, This
->hw_params
,
1333 WARN("Unable to set ALSA format to %u: %d (%s)\n", format
, err
,
1335 hr
= AUDCLNT_E_UNSUPPORTED_FORMAT
;
1339 This
->alsa_format
= format
;
1341 rate
= fmt
->nSamplesPerSec
;
1342 if((err
= snd_pcm_hw_params_set_rate_near(This
->pcm_handle
, This
->hw_params
,
1344 WARN("Unable to set rate to %u: %d (%s)\n", rate
, err
,
1346 hr
= AUDCLNT_E_UNSUPPORTED_FORMAT
;
1350 if((err
= snd_pcm_hw_params_set_channels(This
->pcm_handle
, This
->hw_params
,
1351 This
->alsa_channels
)) < 0){
1352 WARN("Unable to set channels to %u: %d (%s)\n", fmt
->nChannels
, err
,
1354 hr
= AUDCLNT_E_UNSUPPORTED_FORMAT
;
1358 This
->mmdev_period_rt
= period
;
1359 alsa_period_us
= This
->mmdev_period_rt
/ 10;
1360 if((err
= snd_pcm_hw_params_set_period_time_near(This
->pcm_handle
,
1361 This
->hw_params
, &alsa_period_us
, NULL
)) < 0)
1362 WARN("Unable to set period time near %u: %d (%s)\n", alsa_period_us
,
1363 err
, snd_strerror(err
));
1364 /* ALSA updates the output variable alsa_period_us */
1366 This
->mmdev_period_frames
= MulDiv(fmt
->nSamplesPerSec
,
1367 This
->mmdev_period_rt
, 10000000);
1369 /* Buffer 4 ALSA periods if large enough, else 4 mmdevapi periods */
1370 This
->alsa_bufsize_frames
= This
->mmdev_period_frames
* 4;
1371 if(err
< 0 || alsa_period_us
< period
/ 10)
1372 err
= snd_pcm_hw_params_set_buffer_size_near(This
->pcm_handle
,
1373 This
->hw_params
, &This
->alsa_bufsize_frames
);
1375 unsigned int periods
= 4;
1376 err
= snd_pcm_hw_params_set_periods_near(This
->pcm_handle
, This
->hw_params
, &periods
, NULL
);
1379 WARN("Unable to set buffer size: %d (%s)\n", err
, snd_strerror(err
));
1381 if((err
= snd_pcm_hw_params(This
->pcm_handle
, This
->hw_params
)) < 0){
1382 WARN("Unable to set hw params: %d (%s)\n", err
, snd_strerror(err
));
1383 hr
= AUDCLNT_E_ENDPOINT_CREATE_FAILED
;
1387 if((err
= snd_pcm_hw_params_get_period_size(This
->hw_params
,
1388 &This
->alsa_period_frames
, NULL
)) < 0){
1389 WARN("Unable to get period size: %d (%s)\n", err
, snd_strerror(err
));
1390 hr
= AUDCLNT_E_ENDPOINT_CREATE_FAILED
;
1394 if((err
= snd_pcm_hw_params_get_buffer_size(This
->hw_params
,
1395 &This
->alsa_bufsize_frames
)) < 0){
1396 WARN("Unable to get buffer size: %d (%s)\n", err
, snd_strerror(err
));
1397 hr
= AUDCLNT_E_ENDPOINT_CREATE_FAILED
;
1401 sw_params
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, snd_pcm_sw_params_sizeof());
1407 if((err
= snd_pcm_sw_params_current(This
->pcm_handle
, sw_params
)) < 0){
1408 WARN("Unable to get sw_params: %d (%s)\n", err
, snd_strerror(err
));
1409 hr
= AUDCLNT_E_ENDPOINT_CREATE_FAILED
;
1413 if((err
= snd_pcm_sw_params_set_start_threshold(This
->pcm_handle
,
1414 sw_params
, 1)) < 0){
1415 WARN("Unable set start threshold to 1: %d (%s)\n", err
, snd_strerror(err
));
1416 hr
= AUDCLNT_E_ENDPOINT_CREATE_FAILED
;
1420 if((err
= snd_pcm_sw_params_set_stop_threshold(This
->pcm_handle
,
1421 sw_params
, This
->alsa_bufsize_frames
)) < 0){
1422 WARN("Unable set stop threshold to %lu: %d (%s)\n",
1423 This
->alsa_bufsize_frames
, err
, snd_strerror(err
));
1424 hr
= AUDCLNT_E_ENDPOINT_CREATE_FAILED
;
1428 if((err
= snd_pcm_sw_params(This
->pcm_handle
, sw_params
)) < 0){
1429 WARN("Unable to set sw params: %d (%s)\n", err
, snd_strerror(err
));
1430 hr
= AUDCLNT_E_ENDPOINT_CREATE_FAILED
;
1434 if((err
= snd_pcm_prepare(This
->pcm_handle
)) < 0){
1435 WARN("Unable to prepare device: %d (%s)\n", err
, snd_strerror(err
));
1436 hr
= AUDCLNT_E_ENDPOINT_CREATE_FAILED
;
1440 /* Bear in mind weird situations where
1441 * ALSA period (50ms) > mmdevapi buffer (3x10ms)
1442 * or surprising rounding as seen with 22050x8x1 with Pulse:
1443 * ALSA period 220 vs. 221 frames in mmdevapi and
1444 * buffer 883 vs. 2205 frames in mmdevapi! */
1445 This
->bufsize_frames
= MulDiv(duration
, fmt
->nSamplesPerSec
, 10000000);
1446 if(mode
== AUDCLNT_SHAREMODE_EXCLUSIVE
)
1447 This
->bufsize_frames
-= This
->bufsize_frames
% This
->mmdev_period_frames
;
1448 This
->hidden_frames
= This
->alsa_period_frames
+ This
->mmdev_period_frames
+
1449 MulDiv(fmt
->nSamplesPerSec
, EXTRA_SAFE_RT
, 10000000);
1450 /* leave no less than about 1.33ms or 256 bytes of data after a rewind */
1451 This
->safe_rewind_frames
= max(256 / fmt
->nBlockAlign
, MulDiv(133, fmt
->nSamplesPerSec
, 100000));
1453 /* Check if the ALSA buffer is so small that it will run out before
1454 * the next MMDevAPI period tick occurs. Allow a little wiggle room
1455 * with 120% of the period time. */
1456 if(This
->alsa_bufsize_frames
< 1.2 * This
->mmdev_period_frames
)
1457 FIXME("ALSA buffer time is too small. Expect underruns. (%lu < %u * 1.2)\n",
1458 This
->alsa_bufsize_frames
, This
->mmdev_period_frames
);
1460 This
->fmt
= clone_format(fmt
);
1466 This
->local_buffer
= HeapAlloc(GetProcessHeap(), 0,
1467 This
->bufsize_frames
* fmt
->nBlockAlign
);
1468 if(!This
->local_buffer
){
1472 silence_buffer(This
, This
->local_buffer
, This
->bufsize_frames
);
1474 This
->silence_buf
= HeapAlloc(GetProcessHeap(), 0,
1475 This
->alsa_period_frames
* This
->fmt
->nBlockAlign
);
1476 if(!This
->silence_buf
){
1480 silence_buffer(This
, This
->silence_buf
, This
->alsa_period_frames
);
1482 This
->vols
= HeapAlloc(GetProcessHeap(), 0, fmt
->nChannels
* sizeof(float));
1488 for(i
= 0; i
< fmt
->nChannels
; ++i
)
1489 This
->vols
[i
] = 1.f
;
1492 This
->flags
= flags
;
1494 EnterCriticalSection(&g_sessions_lock
);
1496 hr
= get_audio_session(sessionguid
, This
->parent
, fmt
->nChannels
,
1499 LeaveCriticalSection(&g_sessions_lock
);
1503 list_add_tail(&This
->session
->clients
, &This
->entry
);
1505 LeaveCriticalSection(&g_sessions_lock
);
1507 This
->initted
= TRUE
;
1509 TRACE("ALSA period: %lu frames\n", This
->alsa_period_frames
);
1510 TRACE("ALSA buffer: %lu frames\n", This
->alsa_bufsize_frames
);
1511 TRACE("MMDevice period: %u frames\n", This
->mmdev_period_frames
);
1512 TRACE("MMDevice buffer: %u frames\n", This
->bufsize_frames
);
1515 HeapFree(GetProcessHeap(), 0, sw_params
);
1517 HeapFree(GetProcessHeap(), 0, This
->local_buffer
);
1518 This
->local_buffer
= NULL
;
1519 CoTaskMemFree(This
->fmt
);
1521 HeapFree(GetProcessHeap(), 0, This
->vols
);
1525 LeaveCriticalSection(&This
->lock
);
1530 static HRESULT WINAPI
AudioClient_GetBufferSize(IAudioClient
*iface
,
1533 ACImpl
*This
= impl_from_IAudioClient(iface
);
1535 TRACE("(%p)->(%p)\n", This
, out
);
1540 EnterCriticalSection(&This
->lock
);
1543 LeaveCriticalSection(&This
->lock
);
1544 return AUDCLNT_E_NOT_INITIALIZED
;
1547 *out
= This
->bufsize_frames
;
1549 LeaveCriticalSection(&This
->lock
);
1554 static HRESULT WINAPI
AudioClient_GetStreamLatency(IAudioClient
*iface
,
1555 REFERENCE_TIME
*latency
)
1557 ACImpl
*This
= impl_from_IAudioClient(iface
);
1559 TRACE("(%p)->(%p)\n", This
, latency
);
1564 EnterCriticalSection(&This
->lock
);
1567 LeaveCriticalSection(&This
->lock
);
1568 return AUDCLNT_E_NOT_INITIALIZED
;
1571 /* Hide some frames in the ALSA buffer. Allows us to return GetCurrentPadding=0
1572 * yet have enough data left to play (as if it were in native's mixer). Add:
1573 * + mmdevapi_period such that at the end of it, ALSA still has data;
1574 * + EXTRA_SAFE (~4ms) to allow for late callback invocation / fluctuation;
1575 * + alsa_period such that ALSA always has at least one period to play. */
1576 if(This
->dataflow
== eRender
)
1577 *latency
= MulDiv(This
->hidden_frames
, 10000000, This
->fmt
->nSamplesPerSec
);
1579 *latency
= MulDiv(This
->alsa_period_frames
, 10000000, This
->fmt
->nSamplesPerSec
)
1580 + This
->mmdev_period_rt
;
1582 LeaveCriticalSection(&This
->lock
);
1587 static HRESULT WINAPI
AudioClient_GetCurrentPadding(IAudioClient
*iface
,
1590 ACImpl
*This
= impl_from_IAudioClient(iface
);
1592 TRACE("(%p)->(%p)\n", This
, out
);
1597 EnterCriticalSection(&This
->lock
);
1600 LeaveCriticalSection(&This
->lock
);
1601 return AUDCLNT_E_NOT_INITIALIZED
;
1604 /* padding is solely updated at callback time in shared mode */
1605 *out
= This
->held_frames
;
1607 LeaveCriticalSection(&This
->lock
);
1609 TRACE("pad: %u\n", *out
);
1614 static HRESULT WINAPI
AudioClient_IsFormatSupported(IAudioClient
*iface
,
1615 AUDCLNT_SHAREMODE mode
, const WAVEFORMATEX
*fmt
,
1618 ACImpl
*This
= impl_from_IAudioClient(iface
);
1619 snd_pcm_format_mask_t
*formats
= NULL
;
1620 snd_pcm_format_t format
;
1622 WAVEFORMATEX
*closest
= NULL
;
1623 unsigned int max
= 0, min
= 0;
1625 int alsa_channels
, alsa_channel_map
[32];
1627 TRACE("(%p)->(%x, %p, %p)\n", This
, mode
, fmt
, out
);
1629 if(!fmt
|| (mode
== AUDCLNT_SHAREMODE_SHARED
&& !out
))
1632 if(mode
!= AUDCLNT_SHAREMODE_SHARED
&& mode
!= AUDCLNT_SHAREMODE_EXCLUSIVE
)
1633 return E_INVALIDARG
;
1635 if(fmt
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
&&
1636 fmt
->cbSize
< sizeof(WAVEFORMATEXTENSIBLE
) - sizeof(WAVEFORMATEX
))
1637 return E_INVALIDARG
;
1643 if(mode
!= AUDCLNT_SHAREMODE_SHARED
)
1647 if(fmt
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
&&
1648 (fmt
->nAvgBytesPerSec
== 0 ||
1649 fmt
->nBlockAlign
== 0 ||
1650 ((WAVEFORMATEXTENSIBLE
*)fmt
)->Samples
.wValidBitsPerSample
> fmt
->wBitsPerSample
))
1651 return E_INVALIDARG
;
1653 if(fmt
->nChannels
== 0)
1654 return AUDCLNT_E_UNSUPPORTED_FORMAT
;
1656 EnterCriticalSection(&This
->lock
);
1658 if((err
= snd_pcm_hw_params_any(This
->pcm_handle
, This
->hw_params
)) < 0){
1659 hr
= AUDCLNT_E_DEVICE_INVALIDATED
;
1663 formats
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
,
1664 snd_pcm_format_mask_sizeof());
1670 snd_pcm_hw_params_get_format_mask(This
->hw_params
, formats
);
1671 format
= alsa_format(fmt
);
1672 if (format
== SND_PCM_FORMAT_UNKNOWN
||
1673 !snd_pcm_format_mask_test(formats
, format
)){
1674 hr
= AUDCLNT_E_UNSUPPORTED_FORMAT
;
1678 closest
= clone_format(fmt
);
1684 if((err
= snd_pcm_hw_params_get_rate_min(This
->hw_params
, &min
, NULL
)) < 0){
1685 hr
= AUDCLNT_E_DEVICE_INVALIDATED
;
1686 WARN("Unable to get min rate: %d (%s)\n", err
, snd_strerror(err
));
1690 if((err
= snd_pcm_hw_params_get_rate_max(This
->hw_params
, &max
, NULL
)) < 0){
1691 hr
= AUDCLNT_E_DEVICE_INVALIDATED
;
1692 WARN("Unable to get max rate: %d (%s)\n", err
, snd_strerror(err
));
1696 if(fmt
->nSamplesPerSec
< min
|| fmt
->nSamplesPerSec
> max
){
1697 hr
= AUDCLNT_E_UNSUPPORTED_FORMAT
;
1701 if((err
= snd_pcm_hw_params_get_channels_min(This
->hw_params
, &min
)) < 0){
1702 hr
= AUDCLNT_E_DEVICE_INVALIDATED
;
1703 WARN("Unable to get min channels: %d (%s)\n", err
, snd_strerror(err
));
1707 if((err
= snd_pcm_hw_params_get_channels_max(This
->hw_params
, &max
)) < 0){
1708 hr
= AUDCLNT_E_DEVICE_INVALIDATED
;
1709 WARN("Unable to get max channels: %d (%s)\n", err
, snd_strerror(err
));
1712 if(fmt
->nChannels
> max
){
1714 closest
->nChannels
= max
;
1715 }else if(fmt
->nChannels
< min
){
1717 closest
->nChannels
= min
;
1720 map_channels(This
, fmt
, &alsa_channels
, alsa_channel_map
);
1722 if(alsa_channels
> max
){
1724 closest
->nChannels
= max
;
1727 if(closest
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
)
1728 ((WAVEFORMATEXTENSIBLE
*)closest
)->dwChannelMask
= get_channel_mask(closest
->nChannels
);
1730 if(fmt
->nBlockAlign
!= fmt
->nChannels
* fmt
->wBitsPerSample
/ 8 ||
1731 fmt
->nAvgBytesPerSec
!= fmt
->nBlockAlign
* fmt
->nSamplesPerSec
||
1732 (fmt
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
&&
1733 ((WAVEFORMATEXTENSIBLE
*)fmt
)->Samples
.wValidBitsPerSample
< fmt
->wBitsPerSample
))
1736 if(mode
== AUDCLNT_SHAREMODE_EXCLUSIVE
&&
1737 fmt
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
){
1738 if(((WAVEFORMATEXTENSIBLE
*)fmt
)->dwChannelMask
== 0 ||
1739 ((WAVEFORMATEXTENSIBLE
*)fmt
)->dwChannelMask
& SPEAKER_RESERVED
)
1744 LeaveCriticalSection(&This
->lock
);
1745 HeapFree(GetProcessHeap(), 0, formats
);
1747 if(hr
== S_FALSE
&& !out
)
1748 hr
= AUDCLNT_E_UNSUPPORTED_FORMAT
;
1750 if(hr
== S_FALSE
&& out
) {
1751 closest
->nBlockAlign
=
1752 closest
->nChannels
* closest
->wBitsPerSample
/ 8;
1753 closest
->nAvgBytesPerSec
=
1754 closest
->nBlockAlign
* closest
->nSamplesPerSec
;
1755 if(closest
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
)
1756 ((WAVEFORMATEXTENSIBLE
*)closest
)->Samples
.wValidBitsPerSample
= closest
->wBitsPerSample
;
1759 CoTaskMemFree(closest
);
1761 TRACE("returning: %08x\n", hr
);
1765 static HRESULT WINAPI
AudioClient_GetMixFormat(IAudioClient
*iface
,
1766 WAVEFORMATEX
**pwfx
)
1768 ACImpl
*This
= impl_from_IAudioClient(iface
);
1769 WAVEFORMATEXTENSIBLE
*fmt
;
1770 snd_pcm_format_mask_t
*formats
;
1771 unsigned int max_rate
, max_channels
;
1775 TRACE("(%p)->(%p)\n", This
, pwfx
);
1781 fmt
= CoTaskMemAlloc(sizeof(WAVEFORMATEXTENSIBLE
));
1783 return E_OUTOFMEMORY
;
1785 formats
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, snd_pcm_format_mask_sizeof());
1788 return E_OUTOFMEMORY
;
1791 EnterCriticalSection(&This
->lock
);
1793 if((err
= snd_pcm_hw_params_any(This
->pcm_handle
, This
->hw_params
)) < 0){
1794 WARN("Unable to get hw_params: %d (%s)\n", err
, snd_strerror(err
));
1795 hr
= AUDCLNT_E_DEVICE_INVALIDATED
;
1799 snd_pcm_hw_params_get_format_mask(This
->hw_params
, formats
);
1801 fmt
->Format
.wFormatTag
= WAVE_FORMAT_EXTENSIBLE
;
1802 if(snd_pcm_format_mask_test(formats
, SND_PCM_FORMAT_FLOAT_LE
)){
1803 fmt
->Format
.wBitsPerSample
= 32;
1804 fmt
->SubFormat
= KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
;
1805 }else if(snd_pcm_format_mask_test(formats
, SND_PCM_FORMAT_S16_LE
)){
1806 fmt
->Format
.wBitsPerSample
= 16;
1807 fmt
->SubFormat
= KSDATAFORMAT_SUBTYPE_PCM
;
1808 }else if(snd_pcm_format_mask_test(formats
, SND_PCM_FORMAT_U8
)){
1809 fmt
->Format
.wBitsPerSample
= 8;
1810 fmt
->SubFormat
= KSDATAFORMAT_SUBTYPE_PCM
;
1811 }else if(snd_pcm_format_mask_test(formats
, SND_PCM_FORMAT_S32_LE
)){
1812 fmt
->Format
.wBitsPerSample
= 32;
1813 fmt
->SubFormat
= KSDATAFORMAT_SUBTYPE_PCM
;
1814 }else if(snd_pcm_format_mask_test(formats
, SND_PCM_FORMAT_S24_3LE
)){
1815 fmt
->Format
.wBitsPerSample
= 24;
1816 fmt
->SubFormat
= KSDATAFORMAT_SUBTYPE_PCM
;
1818 ERR("Didn't recognize any available ALSA formats\n");
1819 hr
= AUDCLNT_E_DEVICE_INVALIDATED
;
1823 if((err
= snd_pcm_hw_params_get_channels_max(This
->hw_params
,
1824 &max_channels
)) < 0){
1825 WARN("Unable to get max channels: %d (%s)\n", err
, snd_strerror(err
));
1826 hr
= AUDCLNT_E_DEVICE_INVALIDATED
;
1830 if(max_channels
> 6)
1831 fmt
->Format
.nChannels
= 2;
1833 fmt
->Format
.nChannels
= max_channels
;
1835 fmt
->dwChannelMask
= get_channel_mask(fmt
->Format
.nChannels
);
1837 if((err
= snd_pcm_hw_params_get_rate_max(This
->hw_params
, &max_rate
,
1839 WARN("Unable to get max rate: %d (%s)\n", err
, snd_strerror(err
));
1840 hr
= AUDCLNT_E_DEVICE_INVALIDATED
;
1844 if(max_rate
>= 48000)
1845 fmt
->Format
.nSamplesPerSec
= 48000;
1846 else if(max_rate
>= 44100)
1847 fmt
->Format
.nSamplesPerSec
= 44100;
1848 else if(max_rate
>= 22050)
1849 fmt
->Format
.nSamplesPerSec
= 22050;
1850 else if(max_rate
>= 11025)
1851 fmt
->Format
.nSamplesPerSec
= 11025;
1852 else if(max_rate
>= 8000)
1853 fmt
->Format
.nSamplesPerSec
= 8000;
1855 ERR("Unknown max rate: %u\n", max_rate
);
1856 hr
= AUDCLNT_E_DEVICE_INVALIDATED
;
1860 fmt
->Format
.nBlockAlign
= (fmt
->Format
.wBitsPerSample
*
1861 fmt
->Format
.nChannels
) / 8;
1862 fmt
->Format
.nAvgBytesPerSec
= fmt
->Format
.nSamplesPerSec
*
1863 fmt
->Format
.nBlockAlign
;
1865 fmt
->Samples
.wValidBitsPerSample
= fmt
->Format
.wBitsPerSample
;
1866 fmt
->Format
.cbSize
= sizeof(WAVEFORMATEXTENSIBLE
) - sizeof(WAVEFORMATEX
);
1868 dump_fmt((WAVEFORMATEX
*)fmt
);
1869 *pwfx
= (WAVEFORMATEX
*)fmt
;
1872 LeaveCriticalSection(&This
->lock
);
1875 HeapFree(GetProcessHeap(), 0, formats
);
1880 static HRESULT WINAPI
AudioClient_GetDevicePeriod(IAudioClient
*iface
,
1881 REFERENCE_TIME
*defperiod
, REFERENCE_TIME
*minperiod
)
1883 ACImpl
*This
= impl_from_IAudioClient(iface
);
1885 TRACE("(%p)->(%p, %p)\n", This
, defperiod
, minperiod
);
1887 if(!defperiod
&& !minperiod
)
1891 *defperiod
= DefaultPeriod
;
1893 *minperiod
= DefaultPeriod
;
1898 static BYTE
*remap_channels(ACImpl
*This
, BYTE
*buf
, snd_pcm_uframes_t frames
)
1900 snd_pcm_uframes_t i
;
1902 UINT bytes_per_sample
= This
->fmt
->wBitsPerSample
/ 8;
1904 if(!This
->need_remapping
)
1907 if(!This
->remapping_buf
){
1908 This
->remapping_buf
= HeapAlloc(GetProcessHeap(), 0,
1909 bytes_per_sample
* This
->alsa_channels
* frames
);
1910 This
->remapping_buf_frames
= frames
;
1911 }else if(This
->remapping_buf_frames
< frames
){
1912 This
->remapping_buf
= HeapReAlloc(GetProcessHeap(), 0, This
->remapping_buf
,
1913 bytes_per_sample
* This
->alsa_channels
* frames
);
1914 This
->remapping_buf_frames
= frames
;
1917 snd_pcm_format_set_silence(This
->alsa_format
, This
->remapping_buf
,
1918 frames
* This
->alsa_channels
);
1920 switch(This
->fmt
->wBitsPerSample
){
1922 UINT8
*tgt_buf
, *src_buf
;
1923 tgt_buf
= This
->remapping_buf
;
1925 for(i
= 0; i
< frames
; ++i
){
1926 for(c
= 0; c
< This
->fmt
->nChannels
; ++c
)
1927 tgt_buf
[This
->alsa_channel_map
[c
]] = src_buf
[c
];
1928 tgt_buf
+= This
->alsa_channels
;
1929 src_buf
+= This
->fmt
->nChannels
;
1934 UINT16
*tgt_buf
, *src_buf
;
1935 tgt_buf
= (UINT16
*)This
->remapping_buf
;
1936 src_buf
= (UINT16
*)buf
;
1937 for(i
= 0; i
< frames
; ++i
){
1938 for(c
= 0; c
< This
->fmt
->nChannels
; ++c
)
1939 tgt_buf
[This
->alsa_channel_map
[c
]] = src_buf
[c
];
1940 tgt_buf
+= This
->alsa_channels
;
1941 src_buf
+= This
->fmt
->nChannels
;
1946 UINT32
*tgt_buf
, *src_buf
;
1947 tgt_buf
= (UINT32
*)This
->remapping_buf
;
1948 src_buf
= (UINT32
*)buf
;
1949 for(i
= 0; i
< frames
; ++i
){
1950 for(c
= 0; c
< This
->fmt
->nChannels
; ++c
)
1951 tgt_buf
[This
->alsa_channel_map
[c
]] = src_buf
[c
];
1952 tgt_buf
+= This
->alsa_channels
;
1953 src_buf
+= This
->fmt
->nChannels
;
1958 BYTE
*tgt_buf
, *src_buf
;
1959 tgt_buf
= This
->remapping_buf
;
1961 for(i
= 0; i
< frames
; ++i
){
1962 for(c
= 0; c
< This
->fmt
->nChannels
; ++c
)
1963 memcpy(&tgt_buf
[This
->alsa_channel_map
[c
] * bytes_per_sample
],
1964 &src_buf
[c
* bytes_per_sample
], bytes_per_sample
);
1965 tgt_buf
+= This
->alsa_channels
* bytes_per_sample
;
1966 src_buf
+= This
->fmt
->nChannels
* bytes_per_sample
;
1972 return This
->remapping_buf
;
1975 static snd_pcm_sframes_t
alsa_write_best_effort(ACImpl
*This
, BYTE
*buf
,
1976 snd_pcm_uframes_t frames
, BOOL mute
)
1978 snd_pcm_sframes_t written
;
1982 if((err
= snd_pcm_format_set_silence(This
->alsa_format
, buf
,
1983 frames
* This
->fmt
->nChannels
)) < 0)
1984 WARN("Setting buffer to silence failed: %d (%s)\n", err
,
1988 buf
= remap_channels(This
, buf
, frames
);
1990 written
= snd_pcm_writei(This
->pcm_handle
, buf
, frames
);
1994 if(written
== -EAGAIN
)
1998 WARN("writei failed, recovering: %ld (%s)\n", written
,
1999 snd_strerror(written
));
2001 ret
= snd_pcm_recover(This
->pcm_handle
, written
, 0);
2003 WARN("Could not recover: %d (%s)\n", ret
, snd_strerror(ret
));
2007 written
= snd_pcm_writei(This
->pcm_handle
, buf
, frames
);
2013 static snd_pcm_sframes_t
alsa_write_buffer_wrap(ACImpl
*This
, BYTE
*buf
,
2014 snd_pcm_uframes_t buflen
, snd_pcm_uframes_t offs
,
2015 snd_pcm_uframes_t to_write
)
2017 snd_pcm_sframes_t ret
= 0;
2020 snd_pcm_uframes_t chunk
;
2021 snd_pcm_sframes_t tmp
;
2023 if(offs
+ to_write
> buflen
)
2024 chunk
= buflen
- offs
;
2028 tmp
= alsa_write_best_effort(This
, buf
+ offs
* This
->fmt
->nBlockAlign
, chunk
, This
->session
->mute
);
2043 static UINT
buf_ptr_diff(UINT left
, UINT right
, UINT bufsize
)
2046 return right
- left
;
2047 return bufsize
- (left
- right
);
2050 static UINT
data_not_in_alsa(ACImpl
*This
)
2054 diff
= buf_ptr_diff(This
->lcl_offs_frames
, This
->wri_offs_frames
, This
->bufsize_frames
);
2058 return This
->held_frames
- This
->data_in_alsa_frames
;
2060 /* Here's the buffer setup:
2062 * vvvvvvvv sent to HW already
2063 * vvvvvvvv in ALSA buffer but rewindable
2064 * [dddddddddddddddd] ALSA buffer
2065 * [dddddddddddddddd--------] mmdevapi buffer
2066 * ^^^^^^^^ data_in_alsa_frames
2067 * ^^^^^^^^^^^^^^^^ held_frames
2071 * GetCurrentPadding is held_frames
2073 * During period callback, we decrement held_frames, fill ALSA buffer, and move
2076 * During Stop, we rewind the ALSA buffer
2078 static void alsa_write_data(ACImpl
*This
)
2080 snd_pcm_sframes_t written
;
2081 snd_pcm_uframes_t avail
, max_copy_frames
, data_frames_played
;
2084 /* this call seems to be required to get an accurate snd_pcm_state() */
2085 avail
= snd_pcm_avail_update(This
->pcm_handle
);
2087 if(snd_pcm_state(This
->pcm_handle
) == SND_PCM_STATE_XRUN
){
2088 TRACE("XRun state, recovering\n");
2090 avail
= This
->alsa_bufsize_frames
;
2092 if((err
= snd_pcm_recover(This
->pcm_handle
, -EPIPE
, 1)) < 0)
2093 WARN("snd_pcm_recover failed: %d (%s)\n", err
, snd_strerror(err
));
2095 if((err
= snd_pcm_reset(This
->pcm_handle
)) < 0)
2096 WARN("snd_pcm_reset failed: %d (%s)\n", err
, snd_strerror(err
));
2098 if((err
= snd_pcm_prepare(This
->pcm_handle
)) < 0)
2099 WARN("snd_pcm_prepare failed: %d (%s)\n", err
, snd_strerror(err
));
2102 TRACE("avail: %ld\n", avail
);
2104 /* Add a lead-in when starting with too few frames to ensure
2105 * continuous rendering. Additional benefit: Force ALSA to start. */
2106 if(This
->data_in_alsa_frames
== 0 && This
->held_frames
< This
->alsa_period_frames
)
2107 alsa_write_best_effort(This
, This
->silence_buf
, This
->alsa_period_frames
- This
->held_frames
, FALSE
);
2110 max_copy_frames
= data_not_in_alsa(This
);
2112 max_copy_frames
= 0;
2114 data_frames_played
= min(This
->data_in_alsa_frames
, avail
);
2115 This
->data_in_alsa_frames
-= data_frames_played
;
2117 if(This
->held_frames
> data_frames_played
){
2119 This
->held_frames
-= data_frames_played
;
2121 This
->held_frames
= 0;
2123 while(avail
&& max_copy_frames
){
2124 snd_pcm_uframes_t to_write
;
2126 to_write
= min(avail
, max_copy_frames
);
2128 written
= alsa_write_buffer_wrap(This
, This
->local_buffer
,
2129 This
->bufsize_frames
, This
->lcl_offs_frames
, to_write
);
2134 This
->lcl_offs_frames
+= written
;
2135 This
->lcl_offs_frames
%= This
->bufsize_frames
;
2136 This
->data_in_alsa_frames
+= written
;
2137 max_copy_frames
-= written
;
2141 SetEvent(This
->event
);
2144 static void alsa_read_data(ACImpl
*This
)
2146 snd_pcm_sframes_t nread
;
2147 UINT32 pos
= This
->wri_offs_frames
, limit
= This
->held_frames
;
2152 /* FIXME: Detect overrun and signal DATA_DISCONTINUITY
2153 * How to count overrun frames and report them as position increase? */
2154 limit
= This
->bufsize_frames
- max(limit
, pos
);
2156 nread
= snd_pcm_readi(This
->pcm_handle
,
2157 This
->local_buffer
+ pos
* This
->fmt
->nBlockAlign
, limit
);
2158 TRACE("read %ld from %u limit %u\n", nread
, pos
, limit
);
2162 if(nread
== -EAGAIN
) /* no data yet */
2165 WARN("read failed, recovering: %ld (%s)\n", nread
, snd_strerror(nread
));
2167 ret
= snd_pcm_recover(This
->pcm_handle
, nread
, 0);
2169 WARN("Recover failed: %d (%s)\n", ret
, snd_strerror(ret
));
2173 nread
= snd_pcm_readi(This
->pcm_handle
,
2174 This
->local_buffer
+ pos
* This
->fmt
->nBlockAlign
, limit
);
2176 WARN("read failed: %ld (%s)\n", nread
, snd_strerror(nread
));
2181 if(This
->session
->mute
){
2183 if((err
= snd_pcm_format_set_silence(This
->alsa_format
,
2184 This
->local_buffer
+ pos
* This
->fmt
->nBlockAlign
,
2186 WARN("Setting buffer to silence failed: %d (%s)\n", err
,
2190 This
->wri_offs_frames
+= nread
;
2191 This
->wri_offs_frames
%= This
->bufsize_frames
;
2192 This
->held_frames
+= nread
;
2196 SetEvent(This
->event
);
2199 static void CALLBACK
alsa_push_buffer_data(void *user
, BOOLEAN timer
)
2201 ACImpl
*This
= user
;
2203 EnterCriticalSection(&This
->lock
);
2205 QueryPerformanceCounter(&This
->last_period_time
);
2207 if(This
->dataflow
== eRender
)
2208 alsa_write_data(This
);
2209 else if(This
->dataflow
== eCapture
)
2210 alsa_read_data(This
);
2212 LeaveCriticalSection(&This
->lock
);
2215 static snd_pcm_uframes_t
interp_elapsed_frames(ACImpl
*This
)
2217 LARGE_INTEGER time_freq
, current_time
, time_diff
;
2218 QueryPerformanceFrequency(&time_freq
);
2219 QueryPerformanceCounter(¤t_time
);
2220 time_diff
.QuadPart
= current_time
.QuadPart
- This
->last_period_time
.QuadPart
;
2221 return MulDiv(time_diff
.QuadPart
, This
->fmt
->nSamplesPerSec
, time_freq
.QuadPart
);
2224 static int alsa_rewind_best_effort(ACImpl
*This
)
2226 snd_pcm_uframes_t len
, leave
;
2228 /* we can't use snd_pcm_rewindable, some PCM devices crash. so follow
2229 * PulseAudio's example and rewind as much data as we believe is in the
2230 * buffer, minus 1.33ms for safety. */
2232 /* amount of data to leave in ALSA buffer */
2233 leave
= interp_elapsed_frames(This
) + This
->safe_rewind_frames
;
2235 if(This
->held_frames
< leave
)
2236 This
->held_frames
= 0;
2238 This
->held_frames
-= leave
;
2240 if(This
->data_in_alsa_frames
< leave
)
2243 len
= This
->data_in_alsa_frames
- leave
;
2245 TRACE("rewinding %lu frames, now held %u\n", len
, This
->held_frames
);
2248 /* snd_pcm_rewind return value is often broken, assume it succeeded */
2249 snd_pcm_rewind(This
->pcm_handle
, len
);
2251 This
->data_in_alsa_frames
= 0;
2256 static HRESULT WINAPI
AudioClient_Start(IAudioClient
*iface
)
2258 ACImpl
*This
= impl_from_IAudioClient(iface
);
2260 TRACE("(%p)\n", This
);
2262 EnterCriticalSection(&This
->lock
);
2265 LeaveCriticalSection(&This
->lock
);
2266 return AUDCLNT_E_NOT_INITIALIZED
;
2269 if((This
->flags
& AUDCLNT_STREAMFLAGS_EVENTCALLBACK
) && !This
->event
){
2270 LeaveCriticalSection(&This
->lock
);
2271 return AUDCLNT_E_EVENTHANDLE_NOT_SET
;
2275 LeaveCriticalSection(&This
->lock
);
2276 return AUDCLNT_E_NOT_STOPPED
;
2279 if(This
->dataflow
== eCapture
){
2280 /* dump any data that might be leftover in the ALSA capture buffer */
2281 snd_pcm_readi(This
->pcm_handle
, This
->local_buffer
,
2282 This
->bufsize_frames
);
2284 snd_pcm_sframes_t avail
, written
;
2285 snd_pcm_uframes_t offs
;
2287 avail
= snd_pcm_avail_update(This
->pcm_handle
);
2288 avail
= min(avail
, This
->held_frames
);
2290 if(This
->wri_offs_frames
< This
->held_frames
)
2291 offs
= This
->bufsize_frames
- This
->held_frames
+ This
->wri_offs_frames
;
2293 offs
= This
->wri_offs_frames
- This
->held_frames
;
2295 /* fill it with data */
2296 written
= alsa_write_buffer_wrap(This
, This
->local_buffer
,
2297 This
->bufsize_frames
, offs
, avail
);
2300 This
->lcl_offs_frames
= (offs
+ written
) % This
->bufsize_frames
;
2301 This
->data_in_alsa_frames
= written
;
2303 This
->lcl_offs_frames
= offs
;
2304 This
->data_in_alsa_frames
= 0;
2309 if(!CreateTimerQueueTimer(&This
->timer
, g_timer_q
, alsa_push_buffer_data
,
2310 This
, 0, This
->mmdev_period_rt
/ 10000, WT_EXECUTEINTIMERTHREAD
)){
2311 LeaveCriticalSection(&This
->lock
);
2312 WARN("Unable to create timer: %u\n", GetLastError());
2313 return E_OUTOFMEMORY
;
2317 This
->started
= TRUE
;
2319 LeaveCriticalSection(&This
->lock
);
2324 static HRESULT WINAPI
AudioClient_Stop(IAudioClient
*iface
)
2326 ACImpl
*This
= impl_from_IAudioClient(iface
);
2328 TRACE("(%p)\n", This
);
2330 EnterCriticalSection(&This
->lock
);
2333 LeaveCriticalSection(&This
->lock
);
2334 return AUDCLNT_E_NOT_INITIALIZED
;
2338 LeaveCriticalSection(&This
->lock
);
2342 if(This
->dataflow
== eRender
)
2343 alsa_rewind_best_effort(This
);
2345 This
->started
= FALSE
;
2347 LeaveCriticalSection(&This
->lock
);
2352 static HRESULT WINAPI
AudioClient_Reset(IAudioClient
*iface
)
2354 ACImpl
*This
= impl_from_IAudioClient(iface
);
2356 TRACE("(%p)\n", This
);
2358 EnterCriticalSection(&This
->lock
);
2361 LeaveCriticalSection(&This
->lock
);
2362 return AUDCLNT_E_NOT_INITIALIZED
;
2366 LeaveCriticalSection(&This
->lock
);
2367 return AUDCLNT_E_NOT_STOPPED
;
2370 if(This
->getbuf_last
){
2371 LeaveCriticalSection(&This
->lock
);
2372 return AUDCLNT_E_BUFFER_OPERATION_PENDING
;
2375 if(snd_pcm_drop(This
->pcm_handle
) < 0)
2376 WARN("snd_pcm_drop failed\n");
2378 if(snd_pcm_reset(This
->pcm_handle
) < 0)
2379 WARN("snd_pcm_reset failed\n");
2381 if(snd_pcm_prepare(This
->pcm_handle
) < 0)
2382 WARN("snd_pcm_prepare failed\n");
2384 if(This
->dataflow
== eRender
){
2385 This
->written_frames
= 0;
2386 This
->last_pos_frames
= 0;
2388 This
->written_frames
+= This
->held_frames
;
2390 This
->held_frames
= 0;
2391 This
->lcl_offs_frames
= 0;
2392 This
->wri_offs_frames
= 0;
2394 LeaveCriticalSection(&This
->lock
);
2399 static HRESULT WINAPI
AudioClient_SetEventHandle(IAudioClient
*iface
,
2402 ACImpl
*This
= impl_from_IAudioClient(iface
);
2404 TRACE("(%p)->(%p)\n", This
, event
);
2407 return E_INVALIDARG
;
2409 EnterCriticalSection(&This
->lock
);
2412 LeaveCriticalSection(&This
->lock
);
2413 return AUDCLNT_E_NOT_INITIALIZED
;
2416 if(!(This
->flags
& AUDCLNT_STREAMFLAGS_EVENTCALLBACK
)){
2417 LeaveCriticalSection(&This
->lock
);
2418 return AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED
;
2422 LeaveCriticalSection(&This
->lock
);
2423 FIXME("called twice\n");
2424 return HRESULT_FROM_WIN32(ERROR_INVALID_NAME
);
2427 This
->event
= event
;
2429 LeaveCriticalSection(&This
->lock
);
2434 static HRESULT WINAPI
AudioClient_GetService(IAudioClient
*iface
, REFIID riid
,
2437 ACImpl
*This
= impl_from_IAudioClient(iface
);
2439 TRACE("(%p)->(%s, %p)\n", This
, debugstr_guid(riid
), ppv
);
2445 EnterCriticalSection(&This
->lock
);
2448 LeaveCriticalSection(&This
->lock
);
2449 return AUDCLNT_E_NOT_INITIALIZED
;
2452 if(IsEqualIID(riid
, &IID_IAudioRenderClient
)){
2453 if(This
->dataflow
!= eRender
){
2454 LeaveCriticalSection(&This
->lock
);
2455 return AUDCLNT_E_WRONG_ENDPOINT_TYPE
;
2457 IAudioRenderClient_AddRef(&This
->IAudioRenderClient_iface
);
2458 *ppv
= &This
->IAudioRenderClient_iface
;
2459 }else if(IsEqualIID(riid
, &IID_IAudioCaptureClient
)){
2460 if(This
->dataflow
!= eCapture
){
2461 LeaveCriticalSection(&This
->lock
);
2462 return AUDCLNT_E_WRONG_ENDPOINT_TYPE
;
2464 IAudioCaptureClient_AddRef(&This
->IAudioCaptureClient_iface
);
2465 *ppv
= &This
->IAudioCaptureClient_iface
;
2466 }else if(IsEqualIID(riid
, &IID_IAudioClock
)){
2467 IAudioClock_AddRef(&This
->IAudioClock_iface
);
2468 *ppv
= &This
->IAudioClock_iface
;
2469 }else if(IsEqualIID(riid
, &IID_IAudioStreamVolume
)){
2470 IAudioStreamVolume_AddRef(&This
->IAudioStreamVolume_iface
);
2471 *ppv
= &This
->IAudioStreamVolume_iface
;
2472 }else if(IsEqualIID(riid
, &IID_IAudioSessionControl
)){
2473 if(!This
->session_wrapper
){
2474 This
->session_wrapper
= AudioSessionWrapper_Create(This
);
2475 if(!This
->session_wrapper
){
2476 LeaveCriticalSection(&This
->lock
);
2477 return E_OUTOFMEMORY
;
2480 IAudioSessionControl2_AddRef(&This
->session_wrapper
->IAudioSessionControl2_iface
);
2482 *ppv
= &This
->session_wrapper
->IAudioSessionControl2_iface
;
2483 }else if(IsEqualIID(riid
, &IID_IChannelAudioVolume
)){
2484 if(!This
->session_wrapper
){
2485 This
->session_wrapper
= AudioSessionWrapper_Create(This
);
2486 if(!This
->session_wrapper
){
2487 LeaveCriticalSection(&This
->lock
);
2488 return E_OUTOFMEMORY
;
2491 IChannelAudioVolume_AddRef(&This
->session_wrapper
->IChannelAudioVolume_iface
);
2493 *ppv
= &This
->session_wrapper
->IChannelAudioVolume_iface
;
2494 }else if(IsEqualIID(riid
, &IID_ISimpleAudioVolume
)){
2495 if(!This
->session_wrapper
){
2496 This
->session_wrapper
= AudioSessionWrapper_Create(This
);
2497 if(!This
->session_wrapper
){
2498 LeaveCriticalSection(&This
->lock
);
2499 return E_OUTOFMEMORY
;
2502 ISimpleAudioVolume_AddRef(&This
->session_wrapper
->ISimpleAudioVolume_iface
);
2504 *ppv
= &This
->session_wrapper
->ISimpleAudioVolume_iface
;
2508 LeaveCriticalSection(&This
->lock
);
2512 LeaveCriticalSection(&This
->lock
);
2514 FIXME("stub %s\n", debugstr_guid(riid
));
2515 return E_NOINTERFACE
;
2518 static const IAudioClientVtbl AudioClient_Vtbl
=
2520 AudioClient_QueryInterface
,
2522 AudioClient_Release
,
2523 AudioClient_Initialize
,
2524 AudioClient_GetBufferSize
,
2525 AudioClient_GetStreamLatency
,
2526 AudioClient_GetCurrentPadding
,
2527 AudioClient_IsFormatSupported
,
2528 AudioClient_GetMixFormat
,
2529 AudioClient_GetDevicePeriod
,
2533 AudioClient_SetEventHandle
,
2534 AudioClient_GetService
2537 static HRESULT WINAPI
AudioRenderClient_QueryInterface(
2538 IAudioRenderClient
*iface
, REFIID riid
, void **ppv
)
2540 ACImpl
*This
= impl_from_IAudioRenderClient(iface
);
2541 TRACE("(%p)->(%s, %p)\n", iface
, debugstr_guid(riid
), ppv
);
2547 if(IsEqualIID(riid
, &IID_IUnknown
) ||
2548 IsEqualIID(riid
, &IID_IAudioRenderClient
))
2550 else if(IsEqualIID(riid
, &IID_IMarshal
))
2551 return IUnknown_QueryInterface(This
->pUnkFTMarshal
, riid
, ppv
);
2554 IUnknown_AddRef((IUnknown
*)*ppv
);
2558 WARN("Unknown interface %s\n", debugstr_guid(riid
));
2559 return E_NOINTERFACE
;
2562 static ULONG WINAPI
AudioRenderClient_AddRef(IAudioRenderClient
*iface
)
2564 ACImpl
*This
= impl_from_IAudioRenderClient(iface
);
2565 return AudioClient_AddRef(&This
->IAudioClient_iface
);
2568 static ULONG WINAPI
AudioRenderClient_Release(IAudioRenderClient
*iface
)
2570 ACImpl
*This
= impl_from_IAudioRenderClient(iface
);
2571 return AudioClient_Release(&This
->IAudioClient_iface
);
2574 static HRESULT WINAPI
AudioRenderClient_GetBuffer(IAudioRenderClient
*iface
,
2575 UINT32 frames
, BYTE
**data
)
2577 ACImpl
*This
= impl_from_IAudioRenderClient(iface
);
2580 TRACE("(%p)->(%u, %p)\n", This
, frames
, data
);
2586 EnterCriticalSection(&This
->lock
);
2588 if(This
->getbuf_last
){
2589 LeaveCriticalSection(&This
->lock
);
2590 return AUDCLNT_E_OUT_OF_ORDER
;
2594 LeaveCriticalSection(&This
->lock
);
2598 /* held_frames == GetCurrentPadding_nolock(); */
2599 if(This
->held_frames
+ frames
> This
->bufsize_frames
){
2600 LeaveCriticalSection(&This
->lock
);
2601 return AUDCLNT_E_BUFFER_TOO_LARGE
;
2604 write_pos
= This
->wri_offs_frames
;
2605 if(write_pos
+ frames
> This
->bufsize_frames
){
2606 if(This
->tmp_buffer_frames
< frames
){
2607 HeapFree(GetProcessHeap(), 0, This
->tmp_buffer
);
2608 This
->tmp_buffer
= HeapAlloc(GetProcessHeap(), 0,
2609 frames
* This
->fmt
->nBlockAlign
);
2610 if(!This
->tmp_buffer
){
2611 LeaveCriticalSection(&This
->lock
);
2612 return E_OUTOFMEMORY
;
2614 This
->tmp_buffer_frames
= frames
;
2616 *data
= This
->tmp_buffer
;
2617 This
->getbuf_last
= -frames
;
2619 *data
= This
->local_buffer
+ write_pos
* This
->fmt
->nBlockAlign
;
2620 This
->getbuf_last
= frames
;
2623 silence_buffer(This
, *data
, frames
);
2625 LeaveCriticalSection(&This
->lock
);
2630 static void alsa_wrap_buffer(ACImpl
*This
, BYTE
*buffer
, UINT32 written_frames
)
2632 snd_pcm_uframes_t write_offs_frames
= This
->wri_offs_frames
;
2633 UINT32 write_offs_bytes
= write_offs_frames
* This
->fmt
->nBlockAlign
;
2634 snd_pcm_uframes_t chunk_frames
= This
->bufsize_frames
- write_offs_frames
;
2635 UINT32 chunk_bytes
= chunk_frames
* This
->fmt
->nBlockAlign
;
2636 UINT32 written_bytes
= written_frames
* This
->fmt
->nBlockAlign
;
2638 if(written_bytes
<= chunk_bytes
){
2639 memcpy(This
->local_buffer
+ write_offs_bytes
, buffer
, written_bytes
);
2641 memcpy(This
->local_buffer
+ write_offs_bytes
, buffer
, chunk_bytes
);
2642 memcpy(This
->local_buffer
, buffer
+ chunk_bytes
,
2643 written_bytes
- chunk_bytes
);
2647 static HRESULT WINAPI
AudioRenderClient_ReleaseBuffer(
2648 IAudioRenderClient
*iface
, UINT32 written_frames
, DWORD flags
)
2650 ACImpl
*This
= impl_from_IAudioRenderClient(iface
);
2653 TRACE("(%p)->(%u, %x)\n", This
, written_frames
, flags
);
2655 EnterCriticalSection(&This
->lock
);
2657 if(!written_frames
){
2658 This
->getbuf_last
= 0;
2659 LeaveCriticalSection(&This
->lock
);
2663 if(!This
->getbuf_last
){
2664 LeaveCriticalSection(&This
->lock
);
2665 return AUDCLNT_E_OUT_OF_ORDER
;
2668 if(written_frames
> (This
->getbuf_last
>= 0 ? This
->getbuf_last
: -This
->getbuf_last
)){
2669 LeaveCriticalSection(&This
->lock
);
2670 return AUDCLNT_E_INVALID_SIZE
;
2673 if(This
->getbuf_last
>= 0)
2674 buffer
= This
->local_buffer
+ This
->wri_offs_frames
* This
->fmt
->nBlockAlign
;
2676 buffer
= This
->tmp_buffer
;
2678 if(flags
& AUDCLNT_BUFFERFLAGS_SILENT
)
2679 silence_buffer(This
, buffer
, written_frames
);
2681 if(This
->getbuf_last
< 0)
2682 alsa_wrap_buffer(This
, buffer
, written_frames
);
2684 This
->wri_offs_frames
+= written_frames
;
2685 This
->wri_offs_frames
%= This
->bufsize_frames
;
2686 This
->held_frames
+= written_frames
;
2687 This
->written_frames
+= written_frames
;
2688 This
->getbuf_last
= 0;
2690 LeaveCriticalSection(&This
->lock
);
2695 static const IAudioRenderClientVtbl AudioRenderClient_Vtbl
= {
2696 AudioRenderClient_QueryInterface
,
2697 AudioRenderClient_AddRef
,
2698 AudioRenderClient_Release
,
2699 AudioRenderClient_GetBuffer
,
2700 AudioRenderClient_ReleaseBuffer
2703 static HRESULT WINAPI
AudioCaptureClient_QueryInterface(
2704 IAudioCaptureClient
*iface
, REFIID riid
, void **ppv
)
2706 ACImpl
*This
= impl_from_IAudioCaptureClient(iface
);
2707 TRACE("(%p)->(%s, %p)\n", iface
, debugstr_guid(riid
), ppv
);
2713 if(IsEqualIID(riid
, &IID_IUnknown
) ||
2714 IsEqualIID(riid
, &IID_IAudioCaptureClient
))
2716 else if(IsEqualIID(riid
, &IID_IMarshal
))
2717 return IUnknown_QueryInterface(This
->pUnkFTMarshal
, riid
, ppv
);
2720 IUnknown_AddRef((IUnknown
*)*ppv
);
2724 WARN("Unknown interface %s\n", debugstr_guid(riid
));
2725 return E_NOINTERFACE
;
2728 static ULONG WINAPI
AudioCaptureClient_AddRef(IAudioCaptureClient
*iface
)
2730 ACImpl
*This
= impl_from_IAudioCaptureClient(iface
);
2731 return IAudioClient_AddRef(&This
->IAudioClient_iface
);
2734 static ULONG WINAPI
AudioCaptureClient_Release(IAudioCaptureClient
*iface
)
2736 ACImpl
*This
= impl_from_IAudioCaptureClient(iface
);
2737 return IAudioClient_Release(&This
->IAudioClient_iface
);
2740 static HRESULT WINAPI
AudioCaptureClient_GetBuffer(IAudioCaptureClient
*iface
,
2741 BYTE
**data
, UINT32
*frames
, DWORD
*flags
, UINT64
*devpos
,
2744 ACImpl
*This
= impl_from_IAudioCaptureClient(iface
);
2746 TRACE("(%p)->(%p, %p, %p, %p, %p)\n", This
, data
, frames
, flags
,
2749 if(!data
|| !frames
|| !flags
)
2752 EnterCriticalSection(&This
->lock
);
2754 if(This
->getbuf_last
){
2755 LeaveCriticalSection(&This
->lock
);
2756 return AUDCLNT_E_OUT_OF_ORDER
;
2759 /* hr = GetNextPacketSize(iface, frames); */
2760 if(This
->held_frames
< This
->mmdev_period_frames
){
2762 LeaveCriticalSection(&This
->lock
);
2763 return AUDCLNT_S_BUFFER_EMPTY
;
2765 *frames
= This
->mmdev_period_frames
;
2767 if(This
->lcl_offs_frames
+ *frames
> This
->bufsize_frames
){
2768 UINT32 chunk_bytes
, offs_bytes
, frames_bytes
;
2769 if(This
->tmp_buffer_frames
< *frames
){
2770 HeapFree(GetProcessHeap(), 0, This
->tmp_buffer
);
2771 This
->tmp_buffer
= HeapAlloc(GetProcessHeap(), 0,
2772 *frames
* This
->fmt
->nBlockAlign
);
2773 if(!This
->tmp_buffer
){
2774 LeaveCriticalSection(&This
->lock
);
2775 return E_OUTOFMEMORY
;
2777 This
->tmp_buffer_frames
= *frames
;
2780 *data
= This
->tmp_buffer
;
2781 chunk_bytes
= (This
->bufsize_frames
- This
->lcl_offs_frames
) *
2782 This
->fmt
->nBlockAlign
;
2783 offs_bytes
= This
->lcl_offs_frames
* This
->fmt
->nBlockAlign
;
2784 frames_bytes
= *frames
* This
->fmt
->nBlockAlign
;
2785 memcpy(This
->tmp_buffer
, This
->local_buffer
+ offs_bytes
, chunk_bytes
);
2786 memcpy(This
->tmp_buffer
+ chunk_bytes
, This
->local_buffer
,
2787 frames_bytes
- chunk_bytes
);
2789 *data
= This
->local_buffer
+
2790 This
->lcl_offs_frames
* This
->fmt
->nBlockAlign
;
2792 This
->getbuf_last
= *frames
;
2796 *devpos
= This
->written_frames
;
2797 if(qpcpos
){ /* fixme: qpc of recording time */
2798 LARGE_INTEGER stamp
, freq
;
2799 QueryPerformanceCounter(&stamp
);
2800 QueryPerformanceFrequency(&freq
);
2801 *qpcpos
= (stamp
.QuadPart
* (INT64
)10000000) / freq
.QuadPart
;
2804 LeaveCriticalSection(&This
->lock
);
2806 return *frames
? S_OK
: AUDCLNT_S_BUFFER_EMPTY
;
2809 static HRESULT WINAPI
AudioCaptureClient_ReleaseBuffer(
2810 IAudioCaptureClient
*iface
, UINT32 done
)
2812 ACImpl
*This
= impl_from_IAudioCaptureClient(iface
);
2814 TRACE("(%p)->(%u)\n", This
, done
);
2816 EnterCriticalSection(&This
->lock
);
2819 This
->getbuf_last
= 0;
2820 LeaveCriticalSection(&This
->lock
);
2824 if(!This
->getbuf_last
){
2825 LeaveCriticalSection(&This
->lock
);
2826 return AUDCLNT_E_OUT_OF_ORDER
;
2829 if(This
->getbuf_last
!= done
){
2830 LeaveCriticalSection(&This
->lock
);
2831 return AUDCLNT_E_INVALID_SIZE
;
2834 This
->written_frames
+= done
;
2835 This
->held_frames
-= done
;
2836 This
->lcl_offs_frames
+= done
;
2837 This
->lcl_offs_frames
%= This
->bufsize_frames
;
2838 This
->getbuf_last
= 0;
2840 LeaveCriticalSection(&This
->lock
);
2845 static HRESULT WINAPI
AudioCaptureClient_GetNextPacketSize(
2846 IAudioCaptureClient
*iface
, UINT32
*frames
)
2848 ACImpl
*This
= impl_from_IAudioCaptureClient(iface
);
2850 TRACE("(%p)->(%p)\n", This
, frames
);
2855 EnterCriticalSection(&This
->lock
);
2857 *frames
= This
->held_frames
< This
->mmdev_period_frames
? 0 : This
->mmdev_period_frames
;
2859 LeaveCriticalSection(&This
->lock
);
2864 static const IAudioCaptureClientVtbl AudioCaptureClient_Vtbl
=
2866 AudioCaptureClient_QueryInterface
,
2867 AudioCaptureClient_AddRef
,
2868 AudioCaptureClient_Release
,
2869 AudioCaptureClient_GetBuffer
,
2870 AudioCaptureClient_ReleaseBuffer
,
2871 AudioCaptureClient_GetNextPacketSize
2874 static HRESULT WINAPI
AudioClock_QueryInterface(IAudioClock
*iface
,
2875 REFIID riid
, void **ppv
)
2877 ACImpl
*This
= impl_from_IAudioClock(iface
);
2879 TRACE("(%p)->(%s, %p)\n", iface
, debugstr_guid(riid
), ppv
);
2885 if(IsEqualIID(riid
, &IID_IUnknown
) || IsEqualIID(riid
, &IID_IAudioClock
))
2887 else if(IsEqualIID(riid
, &IID_IAudioClock2
))
2888 *ppv
= &This
->IAudioClock2_iface
;
2890 IUnknown_AddRef((IUnknown
*)*ppv
);
2894 WARN("Unknown interface %s\n", debugstr_guid(riid
));
2895 return E_NOINTERFACE
;
2898 static ULONG WINAPI
AudioClock_AddRef(IAudioClock
*iface
)
2900 ACImpl
*This
= impl_from_IAudioClock(iface
);
2901 return IAudioClient_AddRef(&This
->IAudioClient_iface
);
2904 static ULONG WINAPI
AudioClock_Release(IAudioClock
*iface
)
2906 ACImpl
*This
= impl_from_IAudioClock(iface
);
2907 return IAudioClient_Release(&This
->IAudioClient_iface
);
2910 static HRESULT WINAPI
AudioClock_GetFrequency(IAudioClock
*iface
, UINT64
*freq
)
2912 ACImpl
*This
= impl_from_IAudioClock(iface
);
2914 TRACE("(%p)->(%p)\n", This
, freq
);
2916 if(This
->share
== AUDCLNT_SHAREMODE_SHARED
)
2917 *freq
= (UINT64
)This
->fmt
->nSamplesPerSec
* This
->fmt
->nBlockAlign
;
2919 *freq
= This
->fmt
->nSamplesPerSec
;
2924 static HRESULT WINAPI
AudioClock_GetPosition(IAudioClock
*iface
, UINT64
*pos
,
2927 ACImpl
*This
= impl_from_IAudioClock(iface
);
2929 snd_pcm_state_t alsa_state
;
2931 TRACE("(%p)->(%p, %p)\n", This
, pos
, qpctime
);
2936 EnterCriticalSection(&This
->lock
);
2938 /* avail_update required to get accurate snd_pcm_state() */
2939 snd_pcm_avail_update(This
->pcm_handle
);
2940 alsa_state
= snd_pcm_state(This
->pcm_handle
);
2942 if(This
->dataflow
== eRender
){
2943 position
= This
->written_frames
- This
->held_frames
;
2945 if(This
->started
&& alsa_state
== SND_PCM_STATE_RUNNING
&& This
->held_frames
)
2946 /* we should be using snd_pcm_delay here, but it is broken
2947 * especially during ALSA device underrun. instead, let's just
2948 * interpolate between periods with the system timer. */
2949 position
+= interp_elapsed_frames(This
);
2951 position
= min(position
, This
->written_frames
- This
->held_frames
+ This
->mmdev_period_frames
);
2953 position
= min(position
, This
->written_frames
);
2955 position
= This
->written_frames
+ This
->held_frames
;
2957 /* ensure monotic growth */
2958 if(position
< This
->last_pos_frames
)
2959 position
= This
->last_pos_frames
;
2961 This
->last_pos_frames
= position
;
2963 TRACE("frames written: %u, held: %u, state: 0x%x, position: %u\n",
2964 (UINT32
)(This
->written_frames
%1000000000), This
->held_frames
,
2965 alsa_state
, (UINT32
)(position
%1000000000));
2967 LeaveCriticalSection(&This
->lock
);
2969 if(This
->share
== AUDCLNT_SHAREMODE_SHARED
)
2970 *pos
= position
* This
->fmt
->nBlockAlign
;
2975 LARGE_INTEGER stamp
, freq
;
2976 QueryPerformanceCounter(&stamp
);
2977 QueryPerformanceFrequency(&freq
);
2978 *qpctime
= (stamp
.QuadPart
* (INT64
)10000000) / freq
.QuadPart
;
2984 static HRESULT WINAPI
AudioClock_GetCharacteristics(IAudioClock
*iface
,
2987 ACImpl
*This
= impl_from_IAudioClock(iface
);
2989 TRACE("(%p)->(%p)\n", This
, chars
);
2994 *chars
= AUDIOCLOCK_CHARACTERISTIC_FIXED_FREQ
;
2999 static const IAudioClockVtbl AudioClock_Vtbl
=
3001 AudioClock_QueryInterface
,
3004 AudioClock_GetFrequency
,
3005 AudioClock_GetPosition
,
3006 AudioClock_GetCharacteristics
3009 static HRESULT WINAPI
AudioClock2_QueryInterface(IAudioClock2
*iface
,
3010 REFIID riid
, void **ppv
)
3012 ACImpl
*This
= impl_from_IAudioClock2(iface
);
3013 return IAudioClock_QueryInterface(&This
->IAudioClock_iface
, riid
, ppv
);
3016 static ULONG WINAPI
AudioClock2_AddRef(IAudioClock2
*iface
)
3018 ACImpl
*This
= impl_from_IAudioClock2(iface
);
3019 return IAudioClient_AddRef(&This
->IAudioClient_iface
);
3022 static ULONG WINAPI
AudioClock2_Release(IAudioClock2
*iface
)
3024 ACImpl
*This
= impl_from_IAudioClock2(iface
);
3025 return IAudioClient_Release(&This
->IAudioClient_iface
);
3028 static HRESULT WINAPI
AudioClock2_GetDevicePosition(IAudioClock2
*iface
,
3029 UINT64
*pos
, UINT64
*qpctime
)
3031 ACImpl
*This
= impl_from_IAudioClock2(iface
);
3033 FIXME("(%p)->(%p, %p)\n", This
, pos
, qpctime
);
3038 static const IAudioClock2Vtbl AudioClock2_Vtbl
=
3040 AudioClock2_QueryInterface
,
3042 AudioClock2_Release
,
3043 AudioClock2_GetDevicePosition
3046 static AudioSessionWrapper
*AudioSessionWrapper_Create(ACImpl
*client
)
3048 AudioSessionWrapper
*ret
;
3050 ret
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
,
3051 sizeof(AudioSessionWrapper
));
3055 ret
->IAudioSessionControl2_iface
.lpVtbl
= &AudioSessionControl2_Vtbl
;
3056 ret
->ISimpleAudioVolume_iface
.lpVtbl
= &SimpleAudioVolume_Vtbl
;
3057 ret
->IChannelAudioVolume_iface
.lpVtbl
= &ChannelAudioVolume_Vtbl
;
3061 ret
->client
= client
;
3063 ret
->session
= client
->session
;
3064 AudioClient_AddRef(&client
->IAudioClient_iface
);
3070 static HRESULT WINAPI
AudioSessionControl_QueryInterface(
3071 IAudioSessionControl2
*iface
, REFIID riid
, void **ppv
)
3073 TRACE("(%p)->(%s, %p)\n", iface
, debugstr_guid(riid
), ppv
);
3079 if(IsEqualIID(riid
, &IID_IUnknown
) ||
3080 IsEqualIID(riid
, &IID_IAudioSessionControl
) ||
3081 IsEqualIID(riid
, &IID_IAudioSessionControl2
))
3084 IUnknown_AddRef((IUnknown
*)*ppv
);
3088 WARN("Unknown interface %s\n", debugstr_guid(riid
));
3089 return E_NOINTERFACE
;
3092 static ULONG WINAPI
AudioSessionControl_AddRef(IAudioSessionControl2
*iface
)
3094 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
3096 ref
= InterlockedIncrement(&This
->ref
);
3097 TRACE("(%p) Refcount now %u\n", This
, ref
);
3101 static ULONG WINAPI
AudioSessionControl_Release(IAudioSessionControl2
*iface
)
3103 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
3105 ref
= InterlockedDecrement(&This
->ref
);
3106 TRACE("(%p) Refcount now %u\n", This
, ref
);
3109 EnterCriticalSection(&This
->client
->lock
);
3110 This
->client
->session_wrapper
= NULL
;
3111 LeaveCriticalSection(&This
->client
->lock
);
3112 AudioClient_Release(&This
->client
->IAudioClient_iface
);
3114 HeapFree(GetProcessHeap(), 0, This
);
3119 static HRESULT WINAPI
AudioSessionControl_GetState(IAudioSessionControl2
*iface
,
3120 AudioSessionState
*state
)
3122 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
3125 TRACE("(%p)->(%p)\n", This
, state
);
3128 return NULL_PTR_ERR
;
3130 EnterCriticalSection(&g_sessions_lock
);
3132 if(list_empty(&This
->session
->clients
)){
3133 *state
= AudioSessionStateExpired
;
3134 LeaveCriticalSection(&g_sessions_lock
);
3138 LIST_FOR_EACH_ENTRY(client
, &This
->session
->clients
, ACImpl
, entry
){
3139 EnterCriticalSection(&client
->lock
);
3140 if(client
->started
){
3141 *state
= AudioSessionStateActive
;
3142 LeaveCriticalSection(&client
->lock
);
3143 LeaveCriticalSection(&g_sessions_lock
);
3146 LeaveCriticalSection(&client
->lock
);
3149 LeaveCriticalSection(&g_sessions_lock
);
3151 *state
= AudioSessionStateInactive
;
3156 static HRESULT WINAPI
AudioSessionControl_GetDisplayName(
3157 IAudioSessionControl2
*iface
, WCHAR
**name
)
3159 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
3161 FIXME("(%p)->(%p) - stub\n", This
, name
);
3166 static HRESULT WINAPI
AudioSessionControl_SetDisplayName(
3167 IAudioSessionControl2
*iface
, const WCHAR
*name
, const GUID
*session
)
3169 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
3171 FIXME("(%p)->(%p, %s) - stub\n", This
, name
, debugstr_guid(session
));
3176 static HRESULT WINAPI
AudioSessionControl_GetIconPath(
3177 IAudioSessionControl2
*iface
, WCHAR
**path
)
3179 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
3181 FIXME("(%p)->(%p) - stub\n", This
, path
);
3186 static HRESULT WINAPI
AudioSessionControl_SetIconPath(
3187 IAudioSessionControl2
*iface
, const WCHAR
*path
, const GUID
*session
)
3189 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
3191 FIXME("(%p)->(%p, %s) - stub\n", This
, path
, debugstr_guid(session
));
3196 static HRESULT WINAPI
AudioSessionControl_GetGroupingParam(
3197 IAudioSessionControl2
*iface
, GUID
*group
)
3199 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
3201 FIXME("(%p)->(%p) - stub\n", This
, group
);
3206 static HRESULT WINAPI
AudioSessionControl_SetGroupingParam(
3207 IAudioSessionControl2
*iface
, const GUID
*group
, const GUID
*session
)
3209 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
3211 FIXME("(%p)->(%s, %s) - stub\n", This
, debugstr_guid(group
),
3212 debugstr_guid(session
));
3217 static HRESULT WINAPI
AudioSessionControl_RegisterAudioSessionNotification(
3218 IAudioSessionControl2
*iface
, IAudioSessionEvents
*events
)
3220 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
3222 FIXME("(%p)->(%p) - stub\n", This
, events
);
3227 static HRESULT WINAPI
AudioSessionControl_UnregisterAudioSessionNotification(
3228 IAudioSessionControl2
*iface
, IAudioSessionEvents
*events
)
3230 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
3232 FIXME("(%p)->(%p) - stub\n", This
, events
);
3237 static HRESULT WINAPI
AudioSessionControl_GetSessionIdentifier(
3238 IAudioSessionControl2
*iface
, WCHAR
**id
)
3240 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
3242 FIXME("(%p)->(%p) - stub\n", This
, id
);
3247 static HRESULT WINAPI
AudioSessionControl_GetSessionInstanceIdentifier(
3248 IAudioSessionControl2
*iface
, WCHAR
**id
)
3250 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
3252 FIXME("(%p)->(%p) - stub\n", This
, id
);
3257 static HRESULT WINAPI
AudioSessionControl_GetProcessId(
3258 IAudioSessionControl2
*iface
, DWORD
*pid
)
3260 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
3262 TRACE("(%p)->(%p)\n", This
, pid
);
3267 *pid
= GetCurrentProcessId();
3272 static HRESULT WINAPI
AudioSessionControl_IsSystemSoundsSession(
3273 IAudioSessionControl2
*iface
)
3275 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
3277 TRACE("(%p)\n", This
);
3282 static HRESULT WINAPI
AudioSessionControl_SetDuckingPreference(
3283 IAudioSessionControl2
*iface
, BOOL optout
)
3285 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
3287 TRACE("(%p)->(%d)\n", This
, optout
);
3292 static const IAudioSessionControl2Vtbl AudioSessionControl2_Vtbl
=
3294 AudioSessionControl_QueryInterface
,
3295 AudioSessionControl_AddRef
,
3296 AudioSessionControl_Release
,
3297 AudioSessionControl_GetState
,
3298 AudioSessionControl_GetDisplayName
,
3299 AudioSessionControl_SetDisplayName
,
3300 AudioSessionControl_GetIconPath
,
3301 AudioSessionControl_SetIconPath
,
3302 AudioSessionControl_GetGroupingParam
,
3303 AudioSessionControl_SetGroupingParam
,
3304 AudioSessionControl_RegisterAudioSessionNotification
,
3305 AudioSessionControl_UnregisterAudioSessionNotification
,
3306 AudioSessionControl_GetSessionIdentifier
,
3307 AudioSessionControl_GetSessionInstanceIdentifier
,
3308 AudioSessionControl_GetProcessId
,
3309 AudioSessionControl_IsSystemSoundsSession
,
3310 AudioSessionControl_SetDuckingPreference
3313 static HRESULT WINAPI
SimpleAudioVolume_QueryInterface(
3314 ISimpleAudioVolume
*iface
, REFIID riid
, void **ppv
)
3316 TRACE("(%p)->(%s, %p)\n", iface
, debugstr_guid(riid
), ppv
);
3322 if(IsEqualIID(riid
, &IID_IUnknown
) ||
3323 IsEqualIID(riid
, &IID_ISimpleAudioVolume
))
3326 IUnknown_AddRef((IUnknown
*)*ppv
);
3330 WARN("Unknown interface %s\n", debugstr_guid(riid
));
3331 return E_NOINTERFACE
;
3334 static ULONG WINAPI
SimpleAudioVolume_AddRef(ISimpleAudioVolume
*iface
)
3336 AudioSessionWrapper
*This
= impl_from_ISimpleAudioVolume(iface
);
3337 return AudioSessionControl_AddRef(&This
->IAudioSessionControl2_iface
);
3340 static ULONG WINAPI
SimpleAudioVolume_Release(ISimpleAudioVolume
*iface
)
3342 AudioSessionWrapper
*This
= impl_from_ISimpleAudioVolume(iface
);
3343 return AudioSessionControl_Release(&This
->IAudioSessionControl2_iface
);
3346 static HRESULT WINAPI
SimpleAudioVolume_SetMasterVolume(
3347 ISimpleAudioVolume
*iface
, float level
, const GUID
*context
)
3349 AudioSessionWrapper
*This
= impl_from_ISimpleAudioVolume(iface
);
3350 AudioSession
*session
= This
->session
;
3352 TRACE("(%p)->(%f, %s)\n", session
, level
, wine_dbgstr_guid(context
));
3354 if(level
< 0.f
|| level
> 1.f
)
3355 return E_INVALIDARG
;
3358 FIXME("Notifications not supported yet\n");
3360 TRACE("ALSA does not support volume control\n");
3362 EnterCriticalSection(&session
->lock
);
3364 session
->master_vol
= level
;
3366 LeaveCriticalSection(&session
->lock
);
3371 static HRESULT WINAPI
SimpleAudioVolume_GetMasterVolume(
3372 ISimpleAudioVolume
*iface
, float *level
)
3374 AudioSessionWrapper
*This
= impl_from_ISimpleAudioVolume(iface
);
3375 AudioSession
*session
= This
->session
;
3377 TRACE("(%p)->(%p)\n", session
, level
);
3380 return NULL_PTR_ERR
;
3382 *level
= session
->master_vol
;
3387 static HRESULT WINAPI
SimpleAudioVolume_SetMute(ISimpleAudioVolume
*iface
,
3388 BOOL mute
, const GUID
*context
)
3390 AudioSessionWrapper
*This
= impl_from_ISimpleAudioVolume(iface
);
3391 AudioSession
*session
= This
->session
;
3393 TRACE("(%p)->(%u, %p)\n", session
, mute
, context
);
3396 FIXME("Notifications not supported yet\n");
3398 session
->mute
= mute
;
3403 static HRESULT WINAPI
SimpleAudioVolume_GetMute(ISimpleAudioVolume
*iface
,
3406 AudioSessionWrapper
*This
= impl_from_ISimpleAudioVolume(iface
);
3407 AudioSession
*session
= This
->session
;
3409 TRACE("(%p)->(%p)\n", session
, mute
);
3412 return NULL_PTR_ERR
;
3414 *mute
= session
->mute
;
3419 static const ISimpleAudioVolumeVtbl SimpleAudioVolume_Vtbl
=
3421 SimpleAudioVolume_QueryInterface
,
3422 SimpleAudioVolume_AddRef
,
3423 SimpleAudioVolume_Release
,
3424 SimpleAudioVolume_SetMasterVolume
,
3425 SimpleAudioVolume_GetMasterVolume
,
3426 SimpleAudioVolume_SetMute
,
3427 SimpleAudioVolume_GetMute
3430 static HRESULT WINAPI
AudioStreamVolume_QueryInterface(
3431 IAudioStreamVolume
*iface
, REFIID riid
, void **ppv
)
3433 TRACE("(%p)->(%s, %p)\n", iface
, debugstr_guid(riid
), ppv
);
3439 if(IsEqualIID(riid
, &IID_IUnknown
) ||
3440 IsEqualIID(riid
, &IID_IAudioStreamVolume
))
3443 IUnknown_AddRef((IUnknown
*)*ppv
);
3447 WARN("Unknown interface %s\n", debugstr_guid(riid
));
3448 return E_NOINTERFACE
;
3451 static ULONG WINAPI
AudioStreamVolume_AddRef(IAudioStreamVolume
*iface
)
3453 ACImpl
*This
= impl_from_IAudioStreamVolume(iface
);
3454 return IAudioClient_AddRef(&This
->IAudioClient_iface
);
3457 static ULONG WINAPI
AudioStreamVolume_Release(IAudioStreamVolume
*iface
)
3459 ACImpl
*This
= impl_from_IAudioStreamVolume(iface
);
3460 return IAudioClient_Release(&This
->IAudioClient_iface
);
3463 static HRESULT WINAPI
AudioStreamVolume_GetChannelCount(
3464 IAudioStreamVolume
*iface
, UINT32
*out
)
3466 ACImpl
*This
= impl_from_IAudioStreamVolume(iface
);
3468 TRACE("(%p)->(%p)\n", This
, out
);
3473 *out
= This
->fmt
->nChannels
;
3478 static HRESULT WINAPI
AudioStreamVolume_SetChannelVolume(
3479 IAudioStreamVolume
*iface
, UINT32 index
, float level
)
3481 ACImpl
*This
= impl_from_IAudioStreamVolume(iface
);
3483 TRACE("(%p)->(%d, %f)\n", This
, index
, level
);
3485 if(level
< 0.f
|| level
> 1.f
)
3486 return E_INVALIDARG
;
3488 if(index
>= This
->fmt
->nChannels
)
3489 return E_INVALIDARG
;
3491 TRACE("ALSA does not support volume control\n");
3493 EnterCriticalSection(&This
->lock
);
3495 This
->vols
[index
] = level
;
3497 LeaveCriticalSection(&This
->lock
);
3502 static HRESULT WINAPI
AudioStreamVolume_GetChannelVolume(
3503 IAudioStreamVolume
*iface
, UINT32 index
, float *level
)
3505 ACImpl
*This
= impl_from_IAudioStreamVolume(iface
);
3507 TRACE("(%p)->(%d, %p)\n", This
, index
, level
);
3512 if(index
>= This
->fmt
->nChannels
)
3513 return E_INVALIDARG
;
3515 *level
= This
->vols
[index
];
3520 static HRESULT WINAPI
AudioStreamVolume_SetAllVolumes(
3521 IAudioStreamVolume
*iface
, UINT32 count
, const float *levels
)
3523 ACImpl
*This
= impl_from_IAudioStreamVolume(iface
);
3526 TRACE("(%p)->(%d, %p)\n", This
, count
, levels
);
3531 if(count
!= This
->fmt
->nChannels
)
3532 return E_INVALIDARG
;
3534 TRACE("ALSA does not support volume control\n");
3536 EnterCriticalSection(&This
->lock
);
3538 for(i
= 0; i
< count
; ++i
)
3539 This
->vols
[i
] = levels
[i
];
3541 LeaveCriticalSection(&This
->lock
);
3546 static HRESULT WINAPI
AudioStreamVolume_GetAllVolumes(
3547 IAudioStreamVolume
*iface
, UINT32 count
, float *levels
)
3549 ACImpl
*This
= impl_from_IAudioStreamVolume(iface
);
3552 TRACE("(%p)->(%d, %p)\n", This
, count
, levels
);
3557 if(count
!= This
->fmt
->nChannels
)
3558 return E_INVALIDARG
;
3560 EnterCriticalSection(&This
->lock
);
3562 for(i
= 0; i
< count
; ++i
)
3563 levels
[i
] = This
->vols
[i
];
3565 LeaveCriticalSection(&This
->lock
);
3570 static const IAudioStreamVolumeVtbl AudioStreamVolume_Vtbl
=
3572 AudioStreamVolume_QueryInterface
,
3573 AudioStreamVolume_AddRef
,
3574 AudioStreamVolume_Release
,
3575 AudioStreamVolume_GetChannelCount
,
3576 AudioStreamVolume_SetChannelVolume
,
3577 AudioStreamVolume_GetChannelVolume
,
3578 AudioStreamVolume_SetAllVolumes
,
3579 AudioStreamVolume_GetAllVolumes
3582 static HRESULT WINAPI
ChannelAudioVolume_QueryInterface(
3583 IChannelAudioVolume
*iface
, REFIID riid
, void **ppv
)
3585 TRACE("(%p)->(%s, %p)\n", iface
, debugstr_guid(riid
), ppv
);
3591 if(IsEqualIID(riid
, &IID_IUnknown
) ||
3592 IsEqualIID(riid
, &IID_IChannelAudioVolume
))
3595 IUnknown_AddRef((IUnknown
*)*ppv
);
3599 WARN("Unknown interface %s\n", debugstr_guid(riid
));
3600 return E_NOINTERFACE
;
3603 static ULONG WINAPI
ChannelAudioVolume_AddRef(IChannelAudioVolume
*iface
)
3605 AudioSessionWrapper
*This
= impl_from_IChannelAudioVolume(iface
);
3606 return AudioSessionControl_AddRef(&This
->IAudioSessionControl2_iface
);
3609 static ULONG WINAPI
ChannelAudioVolume_Release(IChannelAudioVolume
*iface
)
3611 AudioSessionWrapper
*This
= impl_from_IChannelAudioVolume(iface
);
3612 return AudioSessionControl_Release(&This
->IAudioSessionControl2_iface
);
3615 static HRESULT WINAPI
ChannelAudioVolume_GetChannelCount(
3616 IChannelAudioVolume
*iface
, UINT32
*out
)
3618 AudioSessionWrapper
*This
= impl_from_IChannelAudioVolume(iface
);
3619 AudioSession
*session
= This
->session
;
3621 TRACE("(%p)->(%p)\n", session
, out
);
3624 return NULL_PTR_ERR
;
3626 *out
= session
->channel_count
;
3631 static HRESULT WINAPI
ChannelAudioVolume_SetChannelVolume(
3632 IChannelAudioVolume
*iface
, UINT32 index
, float level
,
3633 const GUID
*context
)
3635 AudioSessionWrapper
*This
= impl_from_IChannelAudioVolume(iface
);
3636 AudioSession
*session
= This
->session
;
3638 TRACE("(%p)->(%d, %f, %s)\n", session
, index
, level
,
3639 wine_dbgstr_guid(context
));
3641 if(level
< 0.f
|| level
> 1.f
)
3642 return E_INVALIDARG
;
3644 if(index
>= session
->channel_count
)
3645 return E_INVALIDARG
;
3648 FIXME("Notifications not supported yet\n");
3650 TRACE("ALSA does not support volume control\n");
3652 EnterCriticalSection(&session
->lock
);
3654 session
->channel_vols
[index
] = level
;
3656 LeaveCriticalSection(&session
->lock
);
3661 static HRESULT WINAPI
ChannelAudioVolume_GetChannelVolume(
3662 IChannelAudioVolume
*iface
, UINT32 index
, float *level
)
3664 AudioSessionWrapper
*This
= impl_from_IChannelAudioVolume(iface
);
3665 AudioSession
*session
= This
->session
;
3667 TRACE("(%p)->(%d, %p)\n", session
, index
, level
);
3670 return NULL_PTR_ERR
;
3672 if(index
>= session
->channel_count
)
3673 return E_INVALIDARG
;
3675 *level
= session
->channel_vols
[index
];
3680 static HRESULT WINAPI
ChannelAudioVolume_SetAllVolumes(
3681 IChannelAudioVolume
*iface
, UINT32 count
, const float *levels
,
3682 const GUID
*context
)
3684 AudioSessionWrapper
*This
= impl_from_IChannelAudioVolume(iface
);
3685 AudioSession
*session
= This
->session
;
3688 TRACE("(%p)->(%d, %p, %s)\n", session
, count
, levels
,
3689 wine_dbgstr_guid(context
));
3692 return NULL_PTR_ERR
;
3694 if(count
!= session
->channel_count
)
3695 return E_INVALIDARG
;
3698 FIXME("Notifications not supported yet\n");
3700 TRACE("ALSA does not support volume control\n");
3702 EnterCriticalSection(&session
->lock
);
3704 for(i
= 0; i
< count
; ++i
)
3705 session
->channel_vols
[i
] = levels
[i
];
3707 LeaveCriticalSection(&session
->lock
);
3712 static HRESULT WINAPI
ChannelAudioVolume_GetAllVolumes(
3713 IChannelAudioVolume
*iface
, UINT32 count
, float *levels
)
3715 AudioSessionWrapper
*This
= impl_from_IChannelAudioVolume(iface
);
3716 AudioSession
*session
= This
->session
;
3719 TRACE("(%p)->(%d, %p)\n", session
, count
, levels
);
3722 return NULL_PTR_ERR
;
3724 if(count
!= session
->channel_count
)
3725 return E_INVALIDARG
;
3727 for(i
= 0; i
< count
; ++i
)
3728 levels
[i
] = session
->channel_vols
[i
];
3733 static const IChannelAudioVolumeVtbl ChannelAudioVolume_Vtbl
=
3735 ChannelAudioVolume_QueryInterface
,
3736 ChannelAudioVolume_AddRef
,
3737 ChannelAudioVolume_Release
,
3738 ChannelAudioVolume_GetChannelCount
,
3739 ChannelAudioVolume_SetChannelVolume
,
3740 ChannelAudioVolume_GetChannelVolume
,
3741 ChannelAudioVolume_SetAllVolumes
,
3742 ChannelAudioVolume_GetAllVolumes
3745 static HRESULT WINAPI
AudioSessionManager_QueryInterface(IAudioSessionManager2
*iface
,
3746 REFIID riid
, void **ppv
)
3748 TRACE("(%p)->(%s, %p)\n", iface
, debugstr_guid(riid
), ppv
);
3754 if(IsEqualIID(riid
, &IID_IUnknown
) ||
3755 IsEqualIID(riid
, &IID_IAudioSessionManager
) ||
3756 IsEqualIID(riid
, &IID_IAudioSessionManager2
))
3759 IUnknown_AddRef((IUnknown
*)*ppv
);
3763 WARN("Unknown interface %s\n", debugstr_guid(riid
));
3764 return E_NOINTERFACE
;
3767 static ULONG WINAPI
AudioSessionManager_AddRef(IAudioSessionManager2
*iface
)
3769 SessionMgr
*This
= impl_from_IAudioSessionManager2(iface
);
3771 ref
= InterlockedIncrement(&This
->ref
);
3772 TRACE("(%p) Refcount now %u\n", This
, ref
);
3776 static ULONG WINAPI
AudioSessionManager_Release(IAudioSessionManager2
*iface
)
3778 SessionMgr
*This
= impl_from_IAudioSessionManager2(iface
);
3780 ref
= InterlockedDecrement(&This
->ref
);
3781 TRACE("(%p) Refcount now %u\n", This
, ref
);
3783 HeapFree(GetProcessHeap(), 0, This
);
3787 static HRESULT WINAPI
AudioSessionManager_GetAudioSessionControl(
3788 IAudioSessionManager2
*iface
, const GUID
*session_guid
, DWORD flags
,
3789 IAudioSessionControl
**out
)
3791 SessionMgr
*This
= impl_from_IAudioSessionManager2(iface
);
3792 AudioSession
*session
;
3793 AudioSessionWrapper
*wrapper
;
3796 TRACE("(%p)->(%s, %x, %p)\n", This
, debugstr_guid(session_guid
),
3799 hr
= get_audio_session(session_guid
, This
->device
, 0, &session
);
3803 wrapper
= AudioSessionWrapper_Create(NULL
);
3805 return E_OUTOFMEMORY
;
3807 wrapper
->session
= session
;
3809 *out
= (IAudioSessionControl
*)&wrapper
->IAudioSessionControl2_iface
;
3814 static HRESULT WINAPI
AudioSessionManager_GetSimpleAudioVolume(
3815 IAudioSessionManager2
*iface
, const GUID
*session_guid
, DWORD flags
,
3816 ISimpleAudioVolume
**out
)
3818 SessionMgr
*This
= impl_from_IAudioSessionManager2(iface
);
3819 AudioSession
*session
;
3820 AudioSessionWrapper
*wrapper
;
3823 TRACE("(%p)->(%s, %x, %p)\n", This
, debugstr_guid(session_guid
),
3826 hr
= get_audio_session(session_guid
, This
->device
, 0, &session
);
3830 wrapper
= AudioSessionWrapper_Create(NULL
);
3832 return E_OUTOFMEMORY
;
3834 wrapper
->session
= session
;
3836 *out
= &wrapper
->ISimpleAudioVolume_iface
;
3841 static HRESULT WINAPI
AudioSessionManager_GetSessionEnumerator(
3842 IAudioSessionManager2
*iface
, IAudioSessionEnumerator
**out
)
3844 SessionMgr
*This
= impl_from_IAudioSessionManager2(iface
);
3845 FIXME("(%p)->(%p) - stub\n", This
, out
);
3849 static HRESULT WINAPI
AudioSessionManager_RegisterSessionNotification(
3850 IAudioSessionManager2
*iface
, IAudioSessionNotification
*notification
)
3852 SessionMgr
*This
= impl_from_IAudioSessionManager2(iface
);
3853 FIXME("(%p)->(%p) - stub\n", This
, notification
);
3857 static HRESULT WINAPI
AudioSessionManager_UnregisterSessionNotification(
3858 IAudioSessionManager2
*iface
, IAudioSessionNotification
*notification
)
3860 SessionMgr
*This
= impl_from_IAudioSessionManager2(iface
);
3861 FIXME("(%p)->(%p) - stub\n", This
, notification
);
3865 static HRESULT WINAPI
AudioSessionManager_RegisterDuckNotification(
3866 IAudioSessionManager2
*iface
, const WCHAR
*session_id
,
3867 IAudioVolumeDuckNotification
*notification
)
3869 SessionMgr
*This
= impl_from_IAudioSessionManager2(iface
);
3870 FIXME("(%p)->(%p) - stub\n", This
, notification
);
3874 static HRESULT WINAPI
AudioSessionManager_UnregisterDuckNotification(
3875 IAudioSessionManager2
*iface
,
3876 IAudioVolumeDuckNotification
*notification
)
3878 SessionMgr
*This
= impl_from_IAudioSessionManager2(iface
);
3879 FIXME("(%p)->(%p) - stub\n", This
, notification
);
3883 static const IAudioSessionManager2Vtbl AudioSessionManager2_Vtbl
=
3885 AudioSessionManager_QueryInterface
,
3886 AudioSessionManager_AddRef
,
3887 AudioSessionManager_Release
,
3888 AudioSessionManager_GetAudioSessionControl
,
3889 AudioSessionManager_GetSimpleAudioVolume
,
3890 AudioSessionManager_GetSessionEnumerator
,
3891 AudioSessionManager_RegisterSessionNotification
,
3892 AudioSessionManager_UnregisterSessionNotification
,
3893 AudioSessionManager_RegisterDuckNotification
,
3894 AudioSessionManager_UnregisterDuckNotification
3897 HRESULT WINAPI
AUDDRV_GetAudioSessionManager(IMMDevice
*device
,
3898 IAudioSessionManager2
**out
)
3902 This
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(SessionMgr
));
3904 return E_OUTOFMEMORY
;
3906 This
->IAudioSessionManager2_iface
.lpVtbl
= &AudioSessionManager2_Vtbl
;
3907 This
->device
= device
;
3910 *out
= &This
->IAudioSessionManager2_iface
;
3915 static unsigned int alsa_probe_num_speakers(char *name
) {
3917 snd_pcm_hw_params_t
*params
;
3919 unsigned int max_channels
= 0;
3921 if ((err
= snd_pcm_open(&handle
, name
, SND_PCM_STREAM_PLAYBACK
, SND_PCM_NONBLOCK
)) < 0) {
3922 WARN("The device \"%s\" failed to open: %d (%s).\n",
3923 name
, err
, snd_strerror(err
));
3927 params
= HeapAlloc(GetProcessHeap(), 0, snd_pcm_hw_params_sizeof());
3929 WARN("Out of memory.\n");
3930 snd_pcm_close(handle
);
3934 if ((err
= snd_pcm_hw_params_any(handle
, params
)) < 0) {
3935 WARN("snd_pcm_hw_params_any failed for \"%s\": %d (%s).\n",
3936 name
, err
, snd_strerror(err
));
3940 if ((err
= snd_pcm_hw_params_get_channels_max(params
,
3941 &max_channels
)) < 0){
3942 WARN("Unable to get max channels: %d (%s)\n", err
, snd_strerror(err
));
3947 HeapFree(GetProcessHeap(), 0, params
);
3948 snd_pcm_close(handle
);
3950 return max_channels
;
3953 enum AudioDeviceConnectionType
{
3954 AudioDeviceConnectionType_Unknown
= 0,
3955 AudioDeviceConnectionType_PCI
,
3956 AudioDeviceConnectionType_USB
3959 HRESULT WINAPI
AUDDRV_GetPropValue(GUID
*guid
, const PROPERTYKEY
*prop
, PROPVARIANT
*out
)
3964 static const PROPERTYKEY devicepath_key
= { /* undocumented? - {b3f8fa53-0004-438e-9003-51a46e139bfc},2 */
3965 {0xb3f8fa53, 0x0004, 0x438e, {0x90, 0x03, 0x51, 0xa4, 0x6e, 0x13, 0x9b, 0xfc}}, 2
3968 TRACE("%s, (%s,%u), %p\n", wine_dbgstr_guid(guid
), wine_dbgstr_guid(&prop
->fmtid
), prop
->pid
, out
);
3970 if(!get_alsa_name_by_guid(guid
, name
, sizeof(name
), &flow
))
3972 WARN("Unknown interface %s\n", debugstr_guid(guid
));
3973 return E_NOINTERFACE
;
3976 if(IsEqualPropertyKey(*prop
, devicepath_key
))
3978 char uevent
[MAX_PATH
];
3982 /* only implemented for identifiable devices, i.e. not "default" */
3983 if(!sscanf(name
, "plughw:%u,%u", &card
, &device
))
3986 sprintf(uevent
, "/sys/class/sound/card%u/device/uevent", card
);
3987 fuevent
= fopen(uevent
, "r");
3990 enum AudioDeviceConnectionType connection
= AudioDeviceConnectionType_Unknown
;
3991 USHORT vendor_id
= 0, product_id
= 0;
3994 while (fgets(line
, sizeof(line
), fuevent
)) {
3998 if((val
= strchr(line
, '='))) {
4002 val_len
= strlen(val
);
4003 if(val_len
> 0 && val
[val_len
- 1] == '\n') { val
[val_len
- 1] = 0; }
4005 if(!strcmp(line
, "PCI_ID")){
4006 connection
= AudioDeviceConnectionType_PCI
;
4007 if(sscanf(val
, "%hX:%hX", &vendor_id
, &product_id
)<2){
4008 WARN("Unexpected input when reading PCI_ID in uevent file.\n");
4009 connection
= AudioDeviceConnectionType_Unknown
;
4012 }else if(!strcmp(line
, "DEVTYPE") && !strcmp(val
,"usb_interface"))
4013 connection
= AudioDeviceConnectionType_USB
;
4014 else if(!strcmp(line
, "PRODUCT"))
4015 if(sscanf(val
, "%hx/%hx/", &vendor_id
, &product_id
)<2){
4016 WARN("Unexpected input when reading PRODUCT in uevent file.\n");
4017 connection
= AudioDeviceConnectionType_Unknown
;
4025 if(connection
== AudioDeviceConnectionType_USB
|| connection
== AudioDeviceConnectionType_PCI
){
4026 static const WCHAR usbformatW
[] = { '{','1','}','.','U','S','B','\\','V','I','D','_',
4027 '%','0','4','X','&','P','I','D','_','%','0','4','X','\\',
4028 '%','u','&','%','0','8','X',0 }; /* "{1}.USB\VID_%04X&PID_%04X\%u&%08X" */
4029 static const WCHAR pciformatW
[] = { '{','1','}','.','H','D','A','U','D','I','O','\\','F','U','N','C','_','0','1','&',
4030 'V','E','N','_','%','0','4','X','&','D','E','V','_',
4031 '%','0','4','X','\\','%','u','&','%','0','8','X',0 }; /* "{1}.HDAUDIO\FUNC_01&VEN_%04X&DEV_%04X\%u&%08X" */
4034 /* As hardly any audio devices have serial numbers, Windows instead
4035 appears to use a persistent random number. We emulate this here
4036 by instead using the last 8 hex digits of the GUID. */
4037 serial_number
= (guid
->Data4
[4] << 24) | (guid
->Data4
[5] << 16) | (guid
->Data4
[6] << 8) | guid
->Data4
[7];
4039 out
->vt
= VT_LPWSTR
;
4040 out
->u
.pwszVal
= CoTaskMemAlloc(128 * sizeof(WCHAR
));
4043 return E_OUTOFMEMORY
;
4045 if(connection
== AudioDeviceConnectionType_USB
)
4046 sprintfW( out
->u
.pwszVal
, usbformatW
, vendor_id
, product_id
, device
, serial_number
);
4047 else if(connection
== AudioDeviceConnectionType_PCI
)
4048 sprintfW( out
->u
.pwszVal
, pciformatW
, vendor_id
, product_id
, device
, serial_number
);
4053 WARN("Could not open %s for reading\n", uevent
);
4056 } else if (flow
!= eCapture
&& IsEqualPropertyKey(*prop
, PKEY_AudioEndpoint_PhysicalSpeakers
)) {
4057 unsigned int num_speakers
, card
, device
;
4060 if (sscanf(name
, "plughw:%u,%u", &card
, &device
))
4061 sprintf(hwname
, "hw:%u,%u", card
, device
); /* must be hw rather than plughw to work */
4063 strcpy(hwname
, name
);
4065 num_speakers
= alsa_probe_num_speakers(hwname
);
4066 if (num_speakers
== 0)
4071 if (num_speakers
> 6)
4072 out
->u
.ulVal
= KSAUDIO_SPEAKER_STEREO
;
4073 else if (num_speakers
== 6)
4074 out
->u
.ulVal
= KSAUDIO_SPEAKER_5POINT1
;
4075 else if (num_speakers
>= 4)
4076 out
->u
.ulVal
= KSAUDIO_SPEAKER_QUAD
;
4077 else if (num_speakers
>= 2)
4078 out
->u
.ulVal
= KSAUDIO_SPEAKER_STEREO
;
4079 else if (num_speakers
== 1)
4080 out
->u
.ulVal
= KSAUDIO_SPEAKER_MONO
;
4085 TRACE("Unimplemented property %s,%u\n", wine_dbgstr_guid(&prop
->fmtid
), prop
->pid
);