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 IAudioClient3 IAudioClient3_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 vol_adjusted_frames
; /* Frames we've already adjusted the volume of but didn't write yet */
130 UINT32 data_in_alsa_frames
;
133 BYTE
*local_buffer
, *tmp_buffer
, *remapping_buf
, *silence_buf
;
134 LONG32 getbuf_last
; /* <0 when using tmp_buffer */
136 CRITICAL_SECTION lock
;
138 AudioSession
*session
;
139 AudioSessionWrapper
*session_wrapper
;
144 typedef struct _SessionMgr
{
145 IAudioSessionManager2 IAudioSessionManager2_iface
;
152 static HANDLE g_timer_q
;
154 static CRITICAL_SECTION g_sessions_lock
;
155 static CRITICAL_SECTION_DEBUG g_sessions_lock_debug
=
157 0, 0, &g_sessions_lock
,
158 { &g_sessions_lock_debug
.ProcessLocksList
, &g_sessions_lock_debug
.ProcessLocksList
},
159 0, 0, { (DWORD_PTR
)(__FILE__
": g_sessions_lock") }
161 static CRITICAL_SECTION g_sessions_lock
= { &g_sessions_lock_debug
, -1, 0, 0, 0, 0 };
162 static struct list g_sessions
= LIST_INIT(g_sessions
);
164 static const WCHAR defaultW
[] = {'d','e','f','a','u','l','t',0};
165 static const char defname
[] = "default";
167 static const WCHAR drv_keyW
[] = {'S','o','f','t','w','a','r','e','\\',
168 'W','i','n','e','\\','D','r','i','v','e','r','s','\\',
169 'w','i','n','e','a','l','s','a','.','d','r','v',0};
170 static const WCHAR drv_key_devicesW
[] = {'S','o','f','t','w','a','r','e','\\',
171 'W','i','n','e','\\','D','r','i','v','e','r','s','\\',
172 'w','i','n','e','a','l','s','a','.','d','r','v','\\','d','e','v','i','c','e','s',0};
173 static const WCHAR guidW
[] = {'g','u','i','d',0};
175 static const IAudioClient3Vtbl AudioClient3_Vtbl
;
176 static const IAudioRenderClientVtbl AudioRenderClient_Vtbl
;
177 static const IAudioCaptureClientVtbl AudioCaptureClient_Vtbl
;
178 static const IAudioSessionControl2Vtbl AudioSessionControl2_Vtbl
;
179 static const ISimpleAudioVolumeVtbl SimpleAudioVolume_Vtbl
;
180 static const IAudioClockVtbl AudioClock_Vtbl
;
181 static const IAudioClock2Vtbl AudioClock2_Vtbl
;
182 static const IAudioStreamVolumeVtbl AudioStreamVolume_Vtbl
;
183 static const IChannelAudioVolumeVtbl ChannelAudioVolume_Vtbl
;
184 static const IAudioSessionManager2Vtbl AudioSessionManager2_Vtbl
;
186 static AudioSessionWrapper
*AudioSessionWrapper_Create(ACImpl
*client
);
188 static inline ACImpl
*impl_from_IAudioClient3(IAudioClient3
*iface
)
190 return CONTAINING_RECORD(iface
, ACImpl
, IAudioClient3_iface
);
193 static inline ACImpl
*impl_from_IAudioRenderClient(IAudioRenderClient
*iface
)
195 return CONTAINING_RECORD(iface
, ACImpl
, IAudioRenderClient_iface
);
198 static inline ACImpl
*impl_from_IAudioCaptureClient(IAudioCaptureClient
*iface
)
200 return CONTAINING_RECORD(iface
, ACImpl
, IAudioCaptureClient_iface
);
203 static inline AudioSessionWrapper
*impl_from_IAudioSessionControl2(IAudioSessionControl2
*iface
)
205 return CONTAINING_RECORD(iface
, AudioSessionWrapper
, IAudioSessionControl2_iface
);
208 static inline AudioSessionWrapper
*impl_from_ISimpleAudioVolume(ISimpleAudioVolume
*iface
)
210 return CONTAINING_RECORD(iface
, AudioSessionWrapper
, ISimpleAudioVolume_iface
);
213 static inline AudioSessionWrapper
*impl_from_IChannelAudioVolume(IChannelAudioVolume
*iface
)
215 return CONTAINING_RECORD(iface
, AudioSessionWrapper
, IChannelAudioVolume_iface
);
218 static inline ACImpl
*impl_from_IAudioClock(IAudioClock
*iface
)
220 return CONTAINING_RECORD(iface
, ACImpl
, IAudioClock_iface
);
223 static inline ACImpl
*impl_from_IAudioClock2(IAudioClock2
*iface
)
225 return CONTAINING_RECORD(iface
, ACImpl
, IAudioClock2_iface
);
228 static inline ACImpl
*impl_from_IAudioStreamVolume(IAudioStreamVolume
*iface
)
230 return CONTAINING_RECORD(iface
, ACImpl
, IAudioStreamVolume_iface
);
233 static inline SessionMgr
*impl_from_IAudioSessionManager2(IAudioSessionManager2
*iface
)
235 return CONTAINING_RECORD(iface
, SessionMgr
, IAudioSessionManager2_iface
);
238 BOOL WINAPI
DllMain(HINSTANCE dll
, DWORD reason
, void *reserved
)
242 case DLL_PROCESS_ATTACH
:
243 g_timer_q
= CreateTimerQueue();
248 case DLL_PROCESS_DETACH
:
250 DeleteCriticalSection(&g_sessions_lock
);
256 /* From <dlls/mmdevapi/mmdevapi.h> */
257 enum DriverPriority
{
258 Priority_Unavailable
= 0,
264 int WINAPI
AUDDRV_GetPriority(void)
266 return Priority_Neutral
;
269 static void set_device_guid(EDataFlow flow
, HKEY drv_key
, const WCHAR
*key_name
,
277 lr
= RegCreateKeyExW(HKEY_CURRENT_USER
, drv_key_devicesW
, 0, NULL
, 0, KEY_WRITE
,
278 NULL
, &drv_key
, NULL
);
279 if(lr
!= ERROR_SUCCESS
){
280 ERR("RegCreateKeyEx(drv_key) failed: %u\n", lr
);
286 lr
= RegCreateKeyExW(drv_key
, key_name
, 0, NULL
, 0, KEY_WRITE
,
288 if(lr
!= ERROR_SUCCESS
){
289 ERR("RegCreateKeyEx(%s) failed: %u\n", wine_dbgstr_w(key_name
), lr
);
293 lr
= RegSetValueExW(key
, guidW
, 0, REG_BINARY
, (BYTE
*)guid
,
295 if(lr
!= ERROR_SUCCESS
)
296 ERR("RegSetValueEx(%s\\guid) failed: %u\n", wine_dbgstr_w(key_name
), lr
);
301 RegCloseKey(drv_key
);
304 static void get_device_guid(EDataFlow flow
, const char *device
, GUID
*guid
)
306 HKEY key
= NULL
, dev_key
;
307 DWORD type
, size
= sizeof(*guid
);
315 MultiByteToWideChar(CP_UNIXCP
, 0, device
, -1, key_name
+ 2, ARRAY_SIZE(key_name
) - 2);
317 if(RegOpenKeyExW(HKEY_CURRENT_USER
, drv_key_devicesW
, 0, KEY_WRITE
|KEY_READ
, &key
) == ERROR_SUCCESS
){
318 if(RegOpenKeyExW(key
, key_name
, 0, KEY_READ
, &dev_key
) == ERROR_SUCCESS
){
319 if(RegQueryValueExW(dev_key
, guidW
, 0, &type
,
320 (BYTE
*)guid
, &size
) == ERROR_SUCCESS
){
321 if(type
== REG_BINARY
){
322 RegCloseKey(dev_key
);
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
= 0, copied
= 0, prefix_len
;
364 static const WCHAR dashW
[] = {' ','-',' ',0};
365 static const size_t dashW_len
= ARRAY_SIZE(dashW
) - 1;
366 static const WCHAR outW
[] = {'O','u','t',':',' ',0};
367 static const WCHAR inW
[] = {'I','n',':',' ',0};
371 prefix_len
= ARRAY_SIZE(outW
) - 1;
372 len_wchars
+= prefix_len
;
375 prefix_len
= ARRAY_SIZE(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
[260];
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
= ARRAY_SIZE(key_name
);
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
->IAudioClient3_iface
.lpVtbl
= &AudioClient3_Vtbl
;
792 This
->IAudioRenderClient_iface
.lpVtbl
= &AudioRenderClient_Vtbl
;
793 This
->IAudioCaptureClient_iface
.lpVtbl
= &AudioCaptureClient_Vtbl
;
794 This
->IAudioClock_iface
.lpVtbl
= &AudioClock_Vtbl
;
795 This
->IAudioClock2_iface
.lpVtbl
= &AudioClock2_Vtbl
;
796 This
->IAudioStreamVolume_iface
.lpVtbl
= &AudioStreamVolume_Vtbl
;
798 if(dataflow
== eRender
)
799 stream
= SND_PCM_STREAM_PLAYBACK
;
800 else if(dataflow
== eCapture
)
801 stream
= SND_PCM_STREAM_CAPTURE
;
803 HeapFree(GetProcessHeap(), 0, This
);
807 hr
= CoCreateFreeThreadedMarshaler((IUnknown
*)&This
->IAudioClient3_iface
, &This
->pUnkFTMarshal
);
809 HeapFree(GetProcessHeap(), 0, This
);
813 This
->dataflow
= dataflow
;
814 if(handle_underrun
&& ((lconf
= make_handle_underrun_config(alsa_name
)))){
815 err
= snd_pcm_open_lconf(&This
->pcm_handle
, alsa_name
, stream
, SND_PCM_NONBLOCK
, lconf
);
816 TRACE("Opening PCM device \"%s\" with handle_underrun: %d\n", alsa_name
, err
);
817 snd_config_delete(lconf
);
818 /* Pulse <= 2010 returns EINVAL, it does not know handle_underrun. */
820 ERR_(winediag
)("PulseAudio \"%s\" %d without handle_underrun. Audio may hang."
821 " Please upgrade to alsa_plugins >= 1.0.24\n", alsa_name
, err
);
822 handle_underrun
= FALSE
;
827 err
= snd_pcm_open(&This
->pcm_handle
, alsa_name
, stream
, SND_PCM_NONBLOCK
);
830 HeapFree(GetProcessHeap(), 0, This
);
831 WARN("Unable to open PCM \"%s\": %d (%s)\n", alsa_name
, err
, snd_strerror(err
));
834 return AUDCLNT_E_DEVICE_IN_USE
;
836 return AUDCLNT_E_ENDPOINT_CREATE_FAILED
;
840 This
->hw_params
= HeapAlloc(GetProcessHeap(), 0,
841 snd_pcm_hw_params_sizeof());
842 if(!This
->hw_params
){
843 snd_pcm_close(This
->pcm_handle
);
844 HeapFree(GetProcessHeap(), 0, This
);
845 return E_OUTOFMEMORY
;
848 InitializeCriticalSection(&This
->lock
);
849 This
->lock
.DebugInfo
->Spare
[0] = (DWORD_PTR
)(__FILE__
": ACImpl.lock");
852 IMMDevice_AddRef(This
->parent
);
854 *out
= (IAudioClient
*)&This
->IAudioClient3_iface
;
855 IAudioClient3_AddRef(&This
->IAudioClient3_iface
);
860 static HRESULT WINAPI
AudioClient_QueryInterface(IAudioClient3
*iface
,
861 REFIID riid
, void **ppv
)
863 ACImpl
*This
= impl_from_IAudioClient3(iface
);
864 TRACE("(%p)->(%s, %p)\n", iface
, debugstr_guid(riid
), ppv
);
869 if(IsEqualIID(riid
, &IID_IUnknown
) ||
870 IsEqualIID(riid
, &IID_IAudioClient
) ||
871 IsEqualIID(riid
, &IID_IAudioClient2
) ||
872 IsEqualIID(riid
, &IID_IAudioClient3
))
874 else if(IsEqualIID(riid
, &IID_IMarshal
))
875 return IUnknown_QueryInterface(This
->pUnkFTMarshal
, riid
, ppv
);
878 IUnknown_AddRef((IUnknown
*)*ppv
);
881 WARN("Unknown interface %s\n", debugstr_guid(riid
));
882 return E_NOINTERFACE
;
885 static ULONG WINAPI
AudioClient_AddRef(IAudioClient3
*iface
)
887 ACImpl
*This
= impl_from_IAudioClient3(iface
);
889 ref
= InterlockedIncrement(&This
->ref
);
890 TRACE("(%p) Refcount now %u\n", This
, ref
);
894 static ULONG WINAPI
AudioClient_Release(IAudioClient3
*iface
)
896 ACImpl
*This
= impl_from_IAudioClient3(iface
);
899 ref
= InterlockedDecrement(&This
->ref
);
900 TRACE("(%p) Refcount now %u\n", This
, ref
);
905 event
= CreateEventW(NULL
, TRUE
, FALSE
, NULL
);
906 wait
= !DeleteTimerQueueTimer(g_timer_q
, This
->timer
, event
);
907 wait
= wait
&& GetLastError() == ERROR_IO_PENDING
;
909 WaitForSingleObject(event
, INFINITE
);
913 IAudioClient3_Stop(iface
);
914 IMMDevice_Release(This
->parent
);
915 IUnknown_Release(This
->pUnkFTMarshal
);
916 This
->lock
.DebugInfo
->Spare
[0] = 0;
917 DeleteCriticalSection(&This
->lock
);
918 snd_pcm_drop(This
->pcm_handle
);
919 snd_pcm_close(This
->pcm_handle
);
921 EnterCriticalSection(&g_sessions_lock
);
922 list_remove(&This
->entry
);
923 LeaveCriticalSection(&g_sessions_lock
);
925 HeapFree(GetProcessHeap(), 0, This
->vols
);
926 HeapFree(GetProcessHeap(), 0, This
->local_buffer
);
927 HeapFree(GetProcessHeap(), 0, This
->remapping_buf
);
928 HeapFree(GetProcessHeap(), 0, This
->silence_buf
);
929 HeapFree(GetProcessHeap(), 0, This
->tmp_buffer
);
930 HeapFree(GetProcessHeap(), 0, This
->hw_params
);
931 CoTaskMemFree(This
->fmt
);
932 HeapFree(GetProcessHeap(), 0, This
);
937 static void dump_fmt(const WAVEFORMATEX
*fmt
)
939 TRACE("wFormatTag: 0x%x (", fmt
->wFormatTag
);
940 switch(fmt
->wFormatTag
){
941 case WAVE_FORMAT_PCM
:
942 TRACE("WAVE_FORMAT_PCM");
944 case WAVE_FORMAT_IEEE_FLOAT
:
945 TRACE("WAVE_FORMAT_IEEE_FLOAT");
947 case WAVE_FORMAT_EXTENSIBLE
:
948 TRACE("WAVE_FORMAT_EXTENSIBLE");
956 TRACE("nChannels: %u\n", fmt
->nChannels
);
957 TRACE("nSamplesPerSec: %u\n", fmt
->nSamplesPerSec
);
958 TRACE("nAvgBytesPerSec: %u\n", fmt
->nAvgBytesPerSec
);
959 TRACE("nBlockAlign: %u\n", fmt
->nBlockAlign
);
960 TRACE("wBitsPerSample: %u\n", fmt
->wBitsPerSample
);
961 TRACE("cbSize: %u\n", fmt
->cbSize
);
963 if(fmt
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
){
964 WAVEFORMATEXTENSIBLE
*fmtex
= (void*)fmt
;
965 TRACE("dwChannelMask: %08x\n", fmtex
->dwChannelMask
);
966 TRACE("Samples: %04x\n", fmtex
->Samples
.wReserved
);
967 TRACE("SubFormat: %s\n", wine_dbgstr_guid(&fmtex
->SubFormat
));
971 static WAVEFORMATEX
*clone_format(const WAVEFORMATEX
*fmt
)
976 if(fmt
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
)
977 size
= sizeof(WAVEFORMATEXTENSIBLE
);
979 size
= sizeof(WAVEFORMATEX
);
981 ret
= CoTaskMemAlloc(size
);
985 memcpy(ret
, fmt
, size
);
987 ret
->cbSize
= size
- sizeof(WAVEFORMATEX
);
992 static snd_pcm_format_t
alsa_format(const WAVEFORMATEX
*fmt
)
994 snd_pcm_format_t format
= SND_PCM_FORMAT_UNKNOWN
;
995 const WAVEFORMATEXTENSIBLE
*fmtex
= (const WAVEFORMATEXTENSIBLE
*)fmt
;
997 if(fmt
->wFormatTag
== WAVE_FORMAT_PCM
||
998 (fmt
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
&&
999 IsEqualGUID(&fmtex
->SubFormat
, &KSDATAFORMAT_SUBTYPE_PCM
))){
1000 if(fmt
->wBitsPerSample
== 8)
1001 format
= SND_PCM_FORMAT_U8
;
1002 else if(fmt
->wBitsPerSample
== 16)
1003 format
= SND_PCM_FORMAT_S16_LE
;
1004 else if(fmt
->wBitsPerSample
== 24)
1005 format
= SND_PCM_FORMAT_S24_3LE
;
1006 else if(fmt
->wBitsPerSample
== 32)
1007 format
= SND_PCM_FORMAT_S32_LE
;
1009 WARN("Unsupported bit depth: %u\n", fmt
->wBitsPerSample
);
1010 if(fmt
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
&&
1011 fmt
->wBitsPerSample
!= fmtex
->Samples
.wValidBitsPerSample
){
1012 if(fmtex
->Samples
.wValidBitsPerSample
== 20 && fmt
->wBitsPerSample
== 24)
1013 format
= SND_PCM_FORMAT_S20_3LE
;
1015 WARN("Unsupported ValidBits: %u\n", fmtex
->Samples
.wValidBitsPerSample
);
1017 }else if(fmt
->wFormatTag
== WAVE_FORMAT_IEEE_FLOAT
||
1018 (fmt
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
&&
1019 IsEqualGUID(&fmtex
->SubFormat
, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
))){
1020 if(fmt
->wBitsPerSample
== 32)
1021 format
= SND_PCM_FORMAT_FLOAT_LE
;
1022 else if(fmt
->wBitsPerSample
== 64)
1023 format
= SND_PCM_FORMAT_FLOAT64_LE
;
1025 WARN("Unsupported float size: %u\n", fmt
->wBitsPerSample
);
1027 WARN("Unknown wave format: %04x\n", fmt
->wFormatTag
);
1031 static void session_init_vols(AudioSession
*session
, UINT channels
)
1033 if(session
->channel_count
< channels
){
1036 if(session
->channel_vols
)
1037 session
->channel_vols
= HeapReAlloc(GetProcessHeap(), 0,
1038 session
->channel_vols
, sizeof(float) * channels
);
1040 session
->channel_vols
= HeapAlloc(GetProcessHeap(), 0,
1041 sizeof(float) * channels
);
1042 if(!session
->channel_vols
)
1045 for(i
= session
->channel_count
; i
< channels
; ++i
)
1046 session
->channel_vols
[i
] = 1.f
;
1048 session
->channel_count
= channels
;
1052 static AudioSession
*create_session(const GUID
*guid
, IMMDevice
*device
,
1057 ret
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(AudioSession
));
1061 memcpy(&ret
->guid
, guid
, sizeof(GUID
));
1063 ret
->device
= device
;
1065 list_init(&ret
->clients
);
1067 list_add_head(&g_sessions
, &ret
->entry
);
1069 InitializeCriticalSection(&ret
->lock
);
1070 ret
->lock
.DebugInfo
->Spare
[0] = (DWORD_PTR
)(__FILE__
": AudioSession.lock");
1072 session_init_vols(ret
, num_channels
);
1074 ret
->master_vol
= 1.f
;
1079 /* if channels == 0, then this will return or create a session with
1080 * matching dataflow and GUID. otherwise, channels must also match */
1081 static HRESULT
get_audio_session(const GUID
*sessionguid
,
1082 IMMDevice
*device
, UINT channels
, AudioSession
**out
)
1084 AudioSession
*session
;
1086 if(!sessionguid
|| IsEqualGUID(sessionguid
, &GUID_NULL
)){
1087 *out
= create_session(&GUID_NULL
, device
, channels
);
1089 return E_OUTOFMEMORY
;
1095 LIST_FOR_EACH_ENTRY(session
, &g_sessions
, AudioSession
, entry
){
1096 if(session
->device
== device
&&
1097 IsEqualGUID(sessionguid
, &session
->guid
)){
1098 session_init_vols(session
, channels
);
1105 *out
= create_session(sessionguid
, device
, channels
);
1107 return E_OUTOFMEMORY
;
1113 static int alsa_channel_index(DWORD flag
)
1116 case SPEAKER_FRONT_LEFT
:
1118 case SPEAKER_FRONT_RIGHT
:
1120 case SPEAKER_BACK_LEFT
:
1122 case SPEAKER_BACK_RIGHT
:
1124 case SPEAKER_FRONT_CENTER
:
1126 case SPEAKER_LOW_FREQUENCY
:
1128 case SPEAKER_SIDE_LEFT
:
1130 case SPEAKER_SIDE_RIGHT
:
1136 static BOOL
need_remapping(ACImpl
*This
, const WAVEFORMATEX
*fmt
, int *map
)
1139 for(i
= 0; i
< fmt
->nChannels
; ++i
){
1146 static DWORD
get_channel_mask(unsigned int channels
)
1152 return KSAUDIO_SPEAKER_MONO
;
1154 return KSAUDIO_SPEAKER_STEREO
;
1156 return KSAUDIO_SPEAKER_STEREO
| SPEAKER_LOW_FREQUENCY
;
1158 return KSAUDIO_SPEAKER_QUAD
; /* not _SURROUND */
1160 return KSAUDIO_SPEAKER_QUAD
| SPEAKER_LOW_FREQUENCY
;
1162 return KSAUDIO_SPEAKER_5POINT1
; /* not 5POINT1_SURROUND */
1164 return KSAUDIO_SPEAKER_5POINT1
| SPEAKER_BACK_CENTER
;
1166 return KSAUDIO_SPEAKER_7POINT1_SURROUND
; /* Vista deprecates 7POINT1 */
1168 FIXME("Unknown speaker configuration: %u\n", channels
);
1172 static HRESULT
map_channels(ACImpl
*This
, const WAVEFORMATEX
*fmt
, int *alsa_channels
, int *map
)
1176 if(This
->dataflow
!= eCapture
&& (fmt
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
|| fmt
->nChannels
> 2) ){
1177 WAVEFORMATEXTENSIBLE
*fmtex
= (void*)fmt
;
1178 DWORD mask
, flag
= SPEAKER_FRONT_LEFT
;
1181 if(fmt
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
&&
1182 fmtex
->dwChannelMask
!= 0)
1183 mask
= fmtex
->dwChannelMask
;
1185 mask
= get_channel_mask(fmt
->nChannels
);
1189 while(i
< fmt
->nChannels
&& !(flag
& SPEAKER_RESERVED
)){
1191 map
[i
] = alsa_channel_index(flag
);
1192 TRACE("Mapping mmdevapi channel %u (0x%x) to ALSA channel %d\n",
1194 if(map
[i
] >= *alsa_channels
)
1195 *alsa_channels
= map
[i
] + 1;
1201 while(i
< fmt
->nChannels
){
1202 map
[i
] = *alsa_channels
;
1203 TRACE("Mapping mmdevapi channel %u to ALSA channel %d\n",
1209 for(i
= 0; i
< fmt
->nChannels
; ++i
){
1211 map
[i
] = *alsa_channels
;
1213 TRACE("Remapping mmdevapi channel %u to ALSA channel %d\n",
1218 need_remap
= need_remapping(This
, fmt
, map
);
1220 *alsa_channels
= fmt
->nChannels
;
1225 TRACE("need_remapping: %u, alsa_channels: %d\n", need_remap
, *alsa_channels
);
1227 return need_remap
? S_OK
: S_FALSE
;
1230 static void silence_buffer(ACImpl
*This
, BYTE
*buffer
, UINT32 frames
)
1232 WAVEFORMATEXTENSIBLE
*fmtex
= (WAVEFORMATEXTENSIBLE
*)This
->fmt
;
1233 if((This
->fmt
->wFormatTag
== WAVE_FORMAT_PCM
||
1234 (This
->fmt
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
&&
1235 IsEqualGUID(&fmtex
->SubFormat
, &KSDATAFORMAT_SUBTYPE_PCM
))) &&
1236 This
->fmt
->wBitsPerSample
== 8)
1237 memset(buffer
, 128, frames
* This
->fmt
->nBlockAlign
);
1239 memset(buffer
, 0, frames
* This
->fmt
->nBlockAlign
);
1242 static HRESULT WINAPI
AudioClient_Initialize(IAudioClient3
*iface
,
1243 AUDCLNT_SHAREMODE mode
, DWORD flags
, REFERENCE_TIME duration
,
1244 REFERENCE_TIME period
, const WAVEFORMATEX
*fmt
,
1245 const GUID
*sessionguid
)
1247 ACImpl
*This
= impl_from_IAudioClient3(iface
);
1248 snd_pcm_sw_params_t
*sw_params
= NULL
;
1249 snd_pcm_format_t format
;
1250 unsigned int rate
, alsa_period_us
;
1254 TRACE("(%p)->(%x, %x, %s, %s, %p, %s)\n", This
, mode
, flags
,
1255 wine_dbgstr_longlong(duration
), wine_dbgstr_longlong(period
), fmt
, debugstr_guid(sessionguid
));
1260 if(mode
!= AUDCLNT_SHAREMODE_SHARED
&& mode
!= AUDCLNT_SHAREMODE_EXCLUSIVE
)
1261 return E_INVALIDARG
;
1263 if(flags
& ~(AUDCLNT_STREAMFLAGS_CROSSPROCESS
|
1264 AUDCLNT_STREAMFLAGS_LOOPBACK
|
1265 AUDCLNT_STREAMFLAGS_EVENTCALLBACK
|
1266 AUDCLNT_STREAMFLAGS_NOPERSIST
|
1267 AUDCLNT_STREAMFLAGS_RATEADJUST
|
1268 AUDCLNT_SESSIONFLAGS_EXPIREWHENUNOWNED
|
1269 AUDCLNT_SESSIONFLAGS_DISPLAY_HIDE
|
1270 AUDCLNT_SESSIONFLAGS_DISPLAY_HIDEWHENEXPIRED
|
1271 AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY
|
1272 AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM
)){
1273 FIXME("Unknown flags: %08x\n", flags
);
1274 return E_INVALIDARG
;
1277 if(mode
== AUDCLNT_SHAREMODE_SHARED
){
1278 period
= DefaultPeriod
;
1279 if( duration
< 3 * period
)
1280 duration
= 3 * period
;
1282 if(fmt
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
){
1283 if(((WAVEFORMATEXTENSIBLE
*)fmt
)->dwChannelMask
== 0 ||
1284 ((WAVEFORMATEXTENSIBLE
*)fmt
)->dwChannelMask
& SPEAKER_RESERVED
)
1285 return AUDCLNT_E_UNSUPPORTED_FORMAT
;
1289 period
= DefaultPeriod
; /* not minimum */
1290 if(period
< MinimumPeriod
|| period
> 5000000)
1291 return AUDCLNT_E_INVALID_DEVICE_PERIOD
;
1292 if(duration
> 20000000) /* the smaller the period, the lower this limit */
1293 return AUDCLNT_E_BUFFER_SIZE_ERROR
;
1294 if(flags
& AUDCLNT_STREAMFLAGS_EVENTCALLBACK
){
1295 if(duration
!= period
)
1296 return AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL
;
1297 FIXME("EXCLUSIVE mode with EVENTCALLBACK\n");
1298 return AUDCLNT_E_DEVICE_IN_USE
;
1300 if( duration
< 8 * period
)
1301 duration
= 8 * period
; /* may grow above 2s */
1305 EnterCriticalSection(&This
->lock
);
1308 LeaveCriticalSection(&This
->lock
);
1309 return AUDCLNT_E_ALREADY_INITIALIZED
;
1314 This
->need_remapping
= map_channels(This
, fmt
, &This
->alsa_channels
, This
->alsa_channel_map
) == S_OK
;
1316 if((err
= snd_pcm_hw_params_any(This
->pcm_handle
, This
->hw_params
)) < 0){
1317 WARN("Unable to get hw_params: %d (%s)\n", err
, snd_strerror(err
));
1318 hr
= AUDCLNT_E_ENDPOINT_CREATE_FAILED
;
1322 if((err
= snd_pcm_hw_params_set_access(This
->pcm_handle
, This
->hw_params
,
1323 SND_PCM_ACCESS_RW_INTERLEAVED
)) < 0){
1324 WARN("Unable to set access: %d (%s)\n", err
, snd_strerror(err
));
1325 hr
= AUDCLNT_E_ENDPOINT_CREATE_FAILED
;
1329 format
= alsa_format(fmt
);
1330 if (format
== SND_PCM_FORMAT_UNKNOWN
){
1331 hr
= AUDCLNT_E_UNSUPPORTED_FORMAT
;
1335 if((err
= snd_pcm_hw_params_set_format(This
->pcm_handle
, This
->hw_params
,
1337 WARN("Unable to set ALSA format to %u: %d (%s)\n", format
, err
,
1339 hr
= AUDCLNT_E_UNSUPPORTED_FORMAT
;
1343 This
->alsa_format
= format
;
1345 rate
= fmt
->nSamplesPerSec
;
1346 if((err
= snd_pcm_hw_params_set_rate_near(This
->pcm_handle
, This
->hw_params
,
1348 WARN("Unable to set rate to %u: %d (%s)\n", rate
, err
,
1350 hr
= AUDCLNT_E_UNSUPPORTED_FORMAT
;
1354 if((err
= snd_pcm_hw_params_set_channels(This
->pcm_handle
, This
->hw_params
,
1355 This
->alsa_channels
)) < 0){
1356 WARN("Unable to set channels to %u: %d (%s)\n", fmt
->nChannels
, err
,
1358 hr
= AUDCLNT_E_UNSUPPORTED_FORMAT
;
1362 This
->mmdev_period_rt
= period
;
1363 alsa_period_us
= This
->mmdev_period_rt
/ 10;
1364 if((err
= snd_pcm_hw_params_set_period_time_near(This
->pcm_handle
,
1365 This
->hw_params
, &alsa_period_us
, NULL
)) < 0)
1366 WARN("Unable to set period time near %u: %d (%s)\n", alsa_period_us
,
1367 err
, snd_strerror(err
));
1368 /* ALSA updates the output variable alsa_period_us */
1370 This
->mmdev_period_frames
= MulDiv(fmt
->nSamplesPerSec
,
1371 This
->mmdev_period_rt
, 10000000);
1373 /* Buffer 4 ALSA periods if large enough, else 4 mmdevapi periods */
1374 This
->alsa_bufsize_frames
= This
->mmdev_period_frames
* 4;
1375 if(err
< 0 || alsa_period_us
< period
/ 10)
1376 err
= snd_pcm_hw_params_set_buffer_size_near(This
->pcm_handle
,
1377 This
->hw_params
, &This
->alsa_bufsize_frames
);
1379 unsigned int periods
= 4;
1380 err
= snd_pcm_hw_params_set_periods_near(This
->pcm_handle
, This
->hw_params
, &periods
, NULL
);
1383 WARN("Unable to set buffer size: %d (%s)\n", err
, snd_strerror(err
));
1385 if((err
= snd_pcm_hw_params(This
->pcm_handle
, This
->hw_params
)) < 0){
1386 WARN("Unable to set hw params: %d (%s)\n", err
, snd_strerror(err
));
1387 hr
= AUDCLNT_E_ENDPOINT_CREATE_FAILED
;
1391 if((err
= snd_pcm_hw_params_get_period_size(This
->hw_params
,
1392 &This
->alsa_period_frames
, NULL
)) < 0){
1393 WARN("Unable to get period size: %d (%s)\n", err
, snd_strerror(err
));
1394 hr
= AUDCLNT_E_ENDPOINT_CREATE_FAILED
;
1398 if((err
= snd_pcm_hw_params_get_buffer_size(This
->hw_params
,
1399 &This
->alsa_bufsize_frames
)) < 0){
1400 WARN("Unable to get buffer size: %d (%s)\n", err
, snd_strerror(err
));
1401 hr
= AUDCLNT_E_ENDPOINT_CREATE_FAILED
;
1405 sw_params
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, snd_pcm_sw_params_sizeof());
1411 if((err
= snd_pcm_sw_params_current(This
->pcm_handle
, sw_params
)) < 0){
1412 WARN("Unable to get sw_params: %d (%s)\n", err
, snd_strerror(err
));
1413 hr
= AUDCLNT_E_ENDPOINT_CREATE_FAILED
;
1417 if((err
= snd_pcm_sw_params_set_start_threshold(This
->pcm_handle
,
1418 sw_params
, 1)) < 0){
1419 WARN("Unable set start threshold to 1: %d (%s)\n", err
, snd_strerror(err
));
1420 hr
= AUDCLNT_E_ENDPOINT_CREATE_FAILED
;
1424 if((err
= snd_pcm_sw_params_set_stop_threshold(This
->pcm_handle
,
1425 sw_params
, This
->alsa_bufsize_frames
)) < 0){
1426 WARN("Unable set stop threshold to %lu: %d (%s)\n",
1427 This
->alsa_bufsize_frames
, err
, snd_strerror(err
));
1428 hr
= AUDCLNT_E_ENDPOINT_CREATE_FAILED
;
1432 if((err
= snd_pcm_sw_params(This
->pcm_handle
, sw_params
)) < 0){
1433 WARN("Unable to set sw params: %d (%s)\n", err
, snd_strerror(err
));
1434 hr
= AUDCLNT_E_ENDPOINT_CREATE_FAILED
;
1438 if((err
= snd_pcm_prepare(This
->pcm_handle
)) < 0){
1439 WARN("Unable to prepare device: %d (%s)\n", err
, snd_strerror(err
));
1440 hr
= AUDCLNT_E_ENDPOINT_CREATE_FAILED
;
1444 /* Bear in mind weird situations where
1445 * ALSA period (50ms) > mmdevapi buffer (3x10ms)
1446 * or surprising rounding as seen with 22050x8x1 with Pulse:
1447 * ALSA period 220 vs. 221 frames in mmdevapi and
1448 * buffer 883 vs. 2205 frames in mmdevapi! */
1449 This
->bufsize_frames
= MulDiv(duration
, fmt
->nSamplesPerSec
, 10000000);
1450 if(mode
== AUDCLNT_SHAREMODE_EXCLUSIVE
)
1451 This
->bufsize_frames
-= This
->bufsize_frames
% This
->mmdev_period_frames
;
1452 This
->hidden_frames
= This
->alsa_period_frames
+ This
->mmdev_period_frames
+
1453 MulDiv(fmt
->nSamplesPerSec
, EXTRA_SAFE_RT
, 10000000);
1454 /* leave no less than about 1.33ms or 256 bytes of data after a rewind */
1455 This
->safe_rewind_frames
= max(256 / fmt
->nBlockAlign
, MulDiv(133, fmt
->nSamplesPerSec
, 100000));
1457 /* Check if the ALSA buffer is so small that it will run out before
1458 * the next MMDevAPI period tick occurs. Allow a little wiggle room
1459 * with 120% of the period time. */
1460 if(This
->alsa_bufsize_frames
< 1.2 * This
->mmdev_period_frames
)
1461 FIXME("ALSA buffer time is too small. Expect underruns. (%lu < %u * 1.2)\n",
1462 This
->alsa_bufsize_frames
, This
->mmdev_period_frames
);
1464 This
->fmt
= clone_format(fmt
);
1470 This
->local_buffer
= HeapAlloc(GetProcessHeap(), 0,
1471 This
->bufsize_frames
* fmt
->nBlockAlign
);
1472 if(!This
->local_buffer
){
1476 silence_buffer(This
, This
->local_buffer
, This
->bufsize_frames
);
1478 This
->silence_buf
= HeapAlloc(GetProcessHeap(), 0,
1479 This
->alsa_period_frames
* This
->fmt
->nBlockAlign
);
1480 if(!This
->silence_buf
){
1484 silence_buffer(This
, This
->silence_buf
, This
->alsa_period_frames
);
1486 This
->vols
= HeapAlloc(GetProcessHeap(), 0, fmt
->nChannels
* sizeof(float));
1492 for(i
= 0; i
< fmt
->nChannels
; ++i
)
1493 This
->vols
[i
] = 1.f
;
1496 This
->flags
= flags
;
1498 EnterCriticalSection(&g_sessions_lock
);
1500 hr
= get_audio_session(sessionguid
, This
->parent
, fmt
->nChannels
,
1503 LeaveCriticalSection(&g_sessions_lock
);
1507 list_add_tail(&This
->session
->clients
, &This
->entry
);
1509 LeaveCriticalSection(&g_sessions_lock
);
1511 This
->initted
= TRUE
;
1513 TRACE("ALSA period: %lu frames\n", This
->alsa_period_frames
);
1514 TRACE("ALSA buffer: %lu frames\n", This
->alsa_bufsize_frames
);
1515 TRACE("MMDevice period: %u frames\n", This
->mmdev_period_frames
);
1516 TRACE("MMDevice buffer: %u frames\n", This
->bufsize_frames
);
1519 HeapFree(GetProcessHeap(), 0, sw_params
);
1521 HeapFree(GetProcessHeap(), 0, This
->local_buffer
);
1522 This
->local_buffer
= NULL
;
1523 CoTaskMemFree(This
->fmt
);
1525 HeapFree(GetProcessHeap(), 0, This
->vols
);
1529 LeaveCriticalSection(&This
->lock
);
1534 static HRESULT WINAPI
AudioClient_GetBufferSize(IAudioClient3
*iface
,
1537 ACImpl
*This
= impl_from_IAudioClient3(iface
);
1539 TRACE("(%p)->(%p)\n", This
, out
);
1544 EnterCriticalSection(&This
->lock
);
1547 LeaveCriticalSection(&This
->lock
);
1548 return AUDCLNT_E_NOT_INITIALIZED
;
1551 *out
= This
->bufsize_frames
;
1553 LeaveCriticalSection(&This
->lock
);
1558 static HRESULT WINAPI
AudioClient_GetStreamLatency(IAudioClient3
*iface
,
1559 REFERENCE_TIME
*latency
)
1561 ACImpl
*This
= impl_from_IAudioClient3(iface
);
1563 TRACE("(%p)->(%p)\n", This
, latency
);
1568 EnterCriticalSection(&This
->lock
);
1571 LeaveCriticalSection(&This
->lock
);
1572 return AUDCLNT_E_NOT_INITIALIZED
;
1575 /* Hide some frames in the ALSA buffer. Allows us to return GetCurrentPadding=0
1576 * yet have enough data left to play (as if it were in native's mixer). Add:
1577 * + mmdevapi_period such that at the end of it, ALSA still has data;
1578 * + EXTRA_SAFE (~4ms) to allow for late callback invocation / fluctuation;
1579 * + alsa_period such that ALSA always has at least one period to play. */
1580 if(This
->dataflow
== eRender
)
1581 *latency
= MulDiv(This
->hidden_frames
, 10000000, This
->fmt
->nSamplesPerSec
);
1583 *latency
= MulDiv(This
->alsa_period_frames
, 10000000, This
->fmt
->nSamplesPerSec
)
1584 + This
->mmdev_period_rt
;
1586 LeaveCriticalSection(&This
->lock
);
1591 static HRESULT WINAPI
AudioClient_GetCurrentPadding(IAudioClient3
*iface
,
1594 ACImpl
*This
= impl_from_IAudioClient3(iface
);
1596 TRACE("(%p)->(%p)\n", This
, out
);
1601 EnterCriticalSection(&This
->lock
);
1604 LeaveCriticalSection(&This
->lock
);
1605 return AUDCLNT_E_NOT_INITIALIZED
;
1608 /* padding is solely updated at callback time in shared mode */
1609 *out
= This
->held_frames
;
1611 LeaveCriticalSection(&This
->lock
);
1613 TRACE("pad: %u\n", *out
);
1618 static HRESULT WINAPI
AudioClient_IsFormatSupported(IAudioClient3
*iface
,
1619 AUDCLNT_SHAREMODE mode
, const WAVEFORMATEX
*fmt
,
1622 ACImpl
*This
= impl_from_IAudioClient3(iface
);
1623 snd_pcm_format_mask_t
*formats
= NULL
;
1624 snd_pcm_format_t format
;
1626 WAVEFORMATEX
*closest
= NULL
;
1627 unsigned int max
= 0, min
= 0;
1629 int alsa_channels
, alsa_channel_map
[32];
1631 TRACE("(%p)->(%x, %p, %p)\n", This
, mode
, fmt
, out
);
1633 if(!fmt
|| (mode
== AUDCLNT_SHAREMODE_SHARED
&& !out
))
1636 if(mode
!= AUDCLNT_SHAREMODE_SHARED
&& mode
!= AUDCLNT_SHAREMODE_EXCLUSIVE
)
1637 return E_INVALIDARG
;
1639 if(fmt
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
&&
1640 fmt
->cbSize
< sizeof(WAVEFORMATEXTENSIBLE
) - sizeof(WAVEFORMATEX
))
1641 return E_INVALIDARG
;
1647 if(mode
!= AUDCLNT_SHAREMODE_SHARED
)
1651 if(fmt
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
&&
1652 (fmt
->nAvgBytesPerSec
== 0 ||
1653 fmt
->nBlockAlign
== 0 ||
1654 ((WAVEFORMATEXTENSIBLE
*)fmt
)->Samples
.wValidBitsPerSample
> fmt
->wBitsPerSample
))
1655 return E_INVALIDARG
;
1657 if(fmt
->nChannels
== 0)
1658 return AUDCLNT_E_UNSUPPORTED_FORMAT
;
1660 EnterCriticalSection(&This
->lock
);
1662 if((err
= snd_pcm_hw_params_any(This
->pcm_handle
, This
->hw_params
)) < 0){
1663 hr
= AUDCLNT_E_DEVICE_INVALIDATED
;
1667 formats
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
,
1668 snd_pcm_format_mask_sizeof());
1674 snd_pcm_hw_params_get_format_mask(This
->hw_params
, formats
);
1675 format
= alsa_format(fmt
);
1676 if (format
== SND_PCM_FORMAT_UNKNOWN
||
1677 !snd_pcm_format_mask_test(formats
, format
)){
1678 hr
= AUDCLNT_E_UNSUPPORTED_FORMAT
;
1682 closest
= clone_format(fmt
);
1688 if((err
= snd_pcm_hw_params_get_rate_min(This
->hw_params
, &min
, NULL
)) < 0){
1689 hr
= AUDCLNT_E_DEVICE_INVALIDATED
;
1690 WARN("Unable to get min rate: %d (%s)\n", err
, snd_strerror(err
));
1694 if((err
= snd_pcm_hw_params_get_rate_max(This
->hw_params
, &max
, NULL
)) < 0){
1695 hr
= AUDCLNT_E_DEVICE_INVALIDATED
;
1696 WARN("Unable to get max rate: %d (%s)\n", err
, snd_strerror(err
));
1700 if(fmt
->nSamplesPerSec
< min
|| fmt
->nSamplesPerSec
> max
){
1701 hr
= AUDCLNT_E_UNSUPPORTED_FORMAT
;
1705 if((err
= snd_pcm_hw_params_get_channels_min(This
->hw_params
, &min
)) < 0){
1706 hr
= AUDCLNT_E_DEVICE_INVALIDATED
;
1707 WARN("Unable to get min channels: %d (%s)\n", err
, snd_strerror(err
));
1711 if((err
= snd_pcm_hw_params_get_channels_max(This
->hw_params
, &max
)) < 0){
1712 hr
= AUDCLNT_E_DEVICE_INVALIDATED
;
1713 WARN("Unable to get max channels: %d (%s)\n", err
, snd_strerror(err
));
1716 if(fmt
->nChannels
> max
){
1718 closest
->nChannels
= max
;
1719 }else if(fmt
->nChannels
< min
){
1721 closest
->nChannels
= min
;
1724 map_channels(This
, fmt
, &alsa_channels
, alsa_channel_map
);
1726 if(alsa_channels
> max
){
1728 closest
->nChannels
= max
;
1731 if(closest
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
)
1732 ((WAVEFORMATEXTENSIBLE
*)closest
)->dwChannelMask
= get_channel_mask(closest
->nChannels
);
1734 if(fmt
->nBlockAlign
!= fmt
->nChannels
* fmt
->wBitsPerSample
/ 8 ||
1735 fmt
->nAvgBytesPerSec
!= fmt
->nBlockAlign
* fmt
->nSamplesPerSec
||
1736 (fmt
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
&&
1737 ((WAVEFORMATEXTENSIBLE
*)fmt
)->Samples
.wValidBitsPerSample
< fmt
->wBitsPerSample
))
1740 if(mode
== AUDCLNT_SHAREMODE_EXCLUSIVE
&&
1741 fmt
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
){
1742 if(((WAVEFORMATEXTENSIBLE
*)fmt
)->dwChannelMask
== 0 ||
1743 ((WAVEFORMATEXTENSIBLE
*)fmt
)->dwChannelMask
& SPEAKER_RESERVED
)
1748 LeaveCriticalSection(&This
->lock
);
1749 HeapFree(GetProcessHeap(), 0, formats
);
1751 if(hr
== S_FALSE
&& !out
)
1752 hr
= AUDCLNT_E_UNSUPPORTED_FORMAT
;
1754 if(hr
== S_FALSE
&& out
) {
1755 closest
->nBlockAlign
=
1756 closest
->nChannels
* closest
->wBitsPerSample
/ 8;
1757 closest
->nAvgBytesPerSec
=
1758 closest
->nBlockAlign
* closest
->nSamplesPerSec
;
1759 if(closest
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
)
1760 ((WAVEFORMATEXTENSIBLE
*)closest
)->Samples
.wValidBitsPerSample
= closest
->wBitsPerSample
;
1763 CoTaskMemFree(closest
);
1765 TRACE("returning: %08x\n", hr
);
1769 static HRESULT WINAPI
AudioClient_GetMixFormat(IAudioClient3
*iface
,
1770 WAVEFORMATEX
**pwfx
)
1772 ACImpl
*This
= impl_from_IAudioClient3(iface
);
1773 WAVEFORMATEXTENSIBLE
*fmt
;
1774 snd_pcm_format_mask_t
*formats
;
1775 unsigned int max_rate
, max_channels
;
1779 TRACE("(%p)->(%p)\n", This
, pwfx
);
1785 fmt
= CoTaskMemAlloc(sizeof(WAVEFORMATEXTENSIBLE
));
1787 return E_OUTOFMEMORY
;
1789 formats
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, snd_pcm_format_mask_sizeof());
1792 return E_OUTOFMEMORY
;
1795 EnterCriticalSection(&This
->lock
);
1797 if((err
= snd_pcm_hw_params_any(This
->pcm_handle
, This
->hw_params
)) < 0){
1798 WARN("Unable to get hw_params: %d (%s)\n", err
, snd_strerror(err
));
1799 hr
= AUDCLNT_E_DEVICE_INVALIDATED
;
1803 snd_pcm_hw_params_get_format_mask(This
->hw_params
, formats
);
1805 fmt
->Format
.wFormatTag
= WAVE_FORMAT_EXTENSIBLE
;
1806 if(snd_pcm_format_mask_test(formats
, SND_PCM_FORMAT_FLOAT_LE
)){
1807 fmt
->Format
.wBitsPerSample
= 32;
1808 fmt
->SubFormat
= KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
;
1809 }else if(snd_pcm_format_mask_test(formats
, SND_PCM_FORMAT_S16_LE
)){
1810 fmt
->Format
.wBitsPerSample
= 16;
1811 fmt
->SubFormat
= KSDATAFORMAT_SUBTYPE_PCM
;
1812 }else if(snd_pcm_format_mask_test(formats
, SND_PCM_FORMAT_U8
)){
1813 fmt
->Format
.wBitsPerSample
= 8;
1814 fmt
->SubFormat
= KSDATAFORMAT_SUBTYPE_PCM
;
1815 }else if(snd_pcm_format_mask_test(formats
, SND_PCM_FORMAT_S32_LE
)){
1816 fmt
->Format
.wBitsPerSample
= 32;
1817 fmt
->SubFormat
= KSDATAFORMAT_SUBTYPE_PCM
;
1818 }else if(snd_pcm_format_mask_test(formats
, SND_PCM_FORMAT_S24_3LE
)){
1819 fmt
->Format
.wBitsPerSample
= 24;
1820 fmt
->SubFormat
= KSDATAFORMAT_SUBTYPE_PCM
;
1822 ERR("Didn't recognize any available ALSA formats\n");
1823 hr
= AUDCLNT_E_DEVICE_INVALIDATED
;
1827 if((err
= snd_pcm_hw_params_get_channels_max(This
->hw_params
,
1828 &max_channels
)) < 0){
1829 WARN("Unable to get max channels: %d (%s)\n", err
, snd_strerror(err
));
1830 hr
= AUDCLNT_E_DEVICE_INVALIDATED
;
1834 if(max_channels
> 6)
1835 fmt
->Format
.nChannels
= 2;
1837 fmt
->Format
.nChannels
= max_channels
;
1839 if(fmt
->Format
.nChannels
> 1 && (fmt
->Format
.nChannels
& 0x1)){
1840 /* For most hardware on Windows, users must choose a configuration with an even
1841 * number of channels (stereo, quad, 5.1, 7.1). Users can then disable
1842 * channels, but those channels are still reported to applications from
1843 * GetMixFormat! Some applications behave badly if given an odd number of
1844 * channels (e.g. 2.1). */
1846 if(fmt
->Format
.nChannels
< max_channels
)
1847 fmt
->Format
.nChannels
+= 1;
1849 /* We could "fake" more channels and downmix the emulated channels,
1850 * but at that point you really ought to tweak your ALSA setup or
1851 * just use PulseAudio. */
1852 WARN("Some Windows applications behave badly with an odd number of channels (%u)!\n", fmt
->Format
.nChannels
);
1855 fmt
->dwChannelMask
= get_channel_mask(fmt
->Format
.nChannels
);
1857 if((err
= snd_pcm_hw_params_get_rate_max(This
->hw_params
, &max_rate
,
1859 WARN("Unable to get max rate: %d (%s)\n", err
, snd_strerror(err
));
1860 hr
= AUDCLNT_E_DEVICE_INVALIDATED
;
1864 if(max_rate
>= 48000)
1865 fmt
->Format
.nSamplesPerSec
= 48000;
1866 else if(max_rate
>= 44100)
1867 fmt
->Format
.nSamplesPerSec
= 44100;
1868 else if(max_rate
>= 22050)
1869 fmt
->Format
.nSamplesPerSec
= 22050;
1870 else if(max_rate
>= 11025)
1871 fmt
->Format
.nSamplesPerSec
= 11025;
1872 else if(max_rate
>= 8000)
1873 fmt
->Format
.nSamplesPerSec
= 8000;
1875 ERR("Unknown max rate: %u\n", max_rate
);
1876 hr
= AUDCLNT_E_DEVICE_INVALIDATED
;
1880 fmt
->Format
.nBlockAlign
= (fmt
->Format
.wBitsPerSample
*
1881 fmt
->Format
.nChannels
) / 8;
1882 fmt
->Format
.nAvgBytesPerSec
= fmt
->Format
.nSamplesPerSec
*
1883 fmt
->Format
.nBlockAlign
;
1885 fmt
->Samples
.wValidBitsPerSample
= fmt
->Format
.wBitsPerSample
;
1886 fmt
->Format
.cbSize
= sizeof(WAVEFORMATEXTENSIBLE
) - sizeof(WAVEFORMATEX
);
1888 dump_fmt((WAVEFORMATEX
*)fmt
);
1889 *pwfx
= (WAVEFORMATEX
*)fmt
;
1892 LeaveCriticalSection(&This
->lock
);
1895 HeapFree(GetProcessHeap(), 0, formats
);
1900 static HRESULT WINAPI
AudioClient_GetDevicePeriod(IAudioClient3
*iface
,
1901 REFERENCE_TIME
*defperiod
, REFERENCE_TIME
*minperiod
)
1903 ACImpl
*This
= impl_from_IAudioClient3(iface
);
1905 TRACE("(%p)->(%p, %p)\n", This
, defperiod
, minperiod
);
1907 if(!defperiod
&& !minperiod
)
1911 *defperiod
= DefaultPeriod
;
1913 *minperiod
= DefaultPeriod
;
1918 static BYTE
*remap_channels(ACImpl
*This
, BYTE
*buf
, snd_pcm_uframes_t frames
)
1920 snd_pcm_uframes_t i
;
1922 UINT bytes_per_sample
= This
->fmt
->wBitsPerSample
/ 8;
1924 if(!This
->need_remapping
)
1927 if(!This
->remapping_buf
){
1928 This
->remapping_buf
= HeapAlloc(GetProcessHeap(), 0,
1929 bytes_per_sample
* This
->alsa_channels
* frames
);
1930 This
->remapping_buf_frames
= frames
;
1931 }else if(This
->remapping_buf_frames
< frames
){
1932 This
->remapping_buf
= HeapReAlloc(GetProcessHeap(), 0, This
->remapping_buf
,
1933 bytes_per_sample
* This
->alsa_channels
* frames
);
1934 This
->remapping_buf_frames
= frames
;
1937 snd_pcm_format_set_silence(This
->alsa_format
, This
->remapping_buf
,
1938 frames
* This
->alsa_channels
);
1940 switch(This
->fmt
->wBitsPerSample
){
1942 UINT8
*tgt_buf
, *src_buf
;
1943 tgt_buf
= This
->remapping_buf
;
1945 for(i
= 0; i
< frames
; ++i
){
1946 for(c
= 0; c
< This
->fmt
->nChannels
; ++c
)
1947 tgt_buf
[This
->alsa_channel_map
[c
]] = src_buf
[c
];
1948 tgt_buf
+= This
->alsa_channels
;
1949 src_buf
+= This
->fmt
->nChannels
;
1954 UINT16
*tgt_buf
, *src_buf
;
1955 tgt_buf
= (UINT16
*)This
->remapping_buf
;
1956 src_buf
= (UINT16
*)buf
;
1957 for(i
= 0; i
< frames
; ++i
){
1958 for(c
= 0; c
< This
->fmt
->nChannels
; ++c
)
1959 tgt_buf
[This
->alsa_channel_map
[c
]] = src_buf
[c
];
1960 tgt_buf
+= This
->alsa_channels
;
1961 src_buf
+= This
->fmt
->nChannels
;
1966 UINT32
*tgt_buf
, *src_buf
;
1967 tgt_buf
= (UINT32
*)This
->remapping_buf
;
1968 src_buf
= (UINT32
*)buf
;
1969 for(i
= 0; i
< frames
; ++i
){
1970 for(c
= 0; c
< This
->fmt
->nChannels
; ++c
)
1971 tgt_buf
[This
->alsa_channel_map
[c
]] = src_buf
[c
];
1972 tgt_buf
+= This
->alsa_channels
;
1973 src_buf
+= This
->fmt
->nChannels
;
1978 BYTE
*tgt_buf
, *src_buf
;
1979 tgt_buf
= This
->remapping_buf
;
1981 for(i
= 0; i
< frames
; ++i
){
1982 for(c
= 0; c
< This
->fmt
->nChannels
; ++c
)
1983 memcpy(&tgt_buf
[This
->alsa_channel_map
[c
] * bytes_per_sample
],
1984 &src_buf
[c
* bytes_per_sample
], bytes_per_sample
);
1985 tgt_buf
+= This
->alsa_channels
* bytes_per_sample
;
1986 src_buf
+= This
->fmt
->nChannels
* bytes_per_sample
;
1992 return This
->remapping_buf
;
1995 static void adjust_buffer_volume(const ACImpl
*This
, BYTE
*buf
, snd_pcm_uframes_t frames
, BOOL mute
)
1997 float vol
[ARRAY_SIZE(This
->alsa_channel_map
)];
1998 BOOL adjust
= FALSE
;
2002 if (This
->vol_adjusted_frames
>= frames
)
2004 channels
= This
->fmt
->nChannels
;
2008 int err
= snd_pcm_format_set_silence(This
->alsa_format
, buf
, frames
* channels
);
2010 WARN("Setting buffer to silence failed: %d (%s)\n", err
, snd_strerror(err
));
2014 /* Adjust the buffer based on the volume for each channel */
2015 for (i
= 0; i
< channels
; i
++)
2016 vol
[i
] = This
->vols
[i
] * This
->session
->master_vol
;
2017 for (i
= 0; i
< min(channels
, This
->session
->channel_count
); i
++)
2019 vol
[i
] *= This
->session
->channel_vols
[i
];
2020 adjust
|= vol
[i
] != 1.0f
;
2022 while (i
< channels
) adjust
|= vol
[i
++] != 1.0f
;
2023 if (!adjust
) return;
2025 /* Skip the frames we've already adjusted before */
2026 end
= buf
+ frames
* This
->fmt
->nBlockAlign
;
2027 buf
+= This
->vol_adjusted_frames
* This
->fmt
->nBlockAlign
;
2029 switch (This
->alsa_format
)
2031 #ifndef WORDS_BIGENDIAN
2032 #define PROCESS_BUFFER(type) do \
2034 type *p = (type*)buf; \
2037 for (i = 0; i < channels; i++) \
2038 p[i] = p[i] * vol[i]; \
2040 } while ((BYTE*)p != end); \
2042 case SND_PCM_FORMAT_S16_LE
:
2043 PROCESS_BUFFER(INT16
);
2045 case SND_PCM_FORMAT_S32_LE
:
2046 PROCESS_BUFFER(INT32
);
2048 case SND_PCM_FORMAT_FLOAT_LE
:
2049 PROCESS_BUFFER(float);
2051 case SND_PCM_FORMAT_FLOAT64_LE
:
2052 PROCESS_BUFFER(double);
2054 #undef PROCESS_BUFFER
2055 case SND_PCM_FORMAT_S20_3LE
:
2056 case SND_PCM_FORMAT_S24_3LE
:
2058 /* Do it 12 bytes at a time until it is no longer possible */
2059 UINT32
*q
= (UINT32
*)buf
, mask
= ~0xff;
2062 /* After we adjust the volume, we need to mask out low bits */
2063 if (This
->alsa_format
== SND_PCM_FORMAT_S20_3LE
)
2067 while (end
- (BYTE
*)q
>= 12)
2071 v
[1] = q
[1] << 16 | (q
[0] >> 16 & ~0xff);
2072 v
[2] = q
[2] << 24 | (q
[1] >> 8 & ~0xff);
2073 v
[3] = q
[2] & ~0xff;
2074 for (k
= 0; k
< 4; k
++)
2076 v
[k
] = (INT32
)((INT32
)v
[k
] * vol
[i
]);
2078 if (++i
== channels
) i
= 0;
2080 *q
++ = v
[0] >> 8 | v
[1] << 16;
2081 *q
++ = v
[1] >> 16 | v
[2] << 8;
2082 *q
++ = v
[2] >> 24 | v
[3];
2087 UINT32 v
= (INT32
)((INT32
)(p
[0] << 8 | p
[1] << 16 | p
[2] << 24) * vol
[i
]);
2089 *p
++ = v
>> 8 & 0xff;
2090 *p
++ = v
>> 16 & 0xff;
2092 if (++i
== channels
) i
= 0;
2097 case SND_PCM_FORMAT_U8
:
2099 UINT8
*p
= (UINT8
*)buf
;
2102 for (i
= 0; i
< channels
; i
++)
2103 p
[i
] = (int)((p
[i
] - 128) * vol
[i
]) + 128;
2105 } while ((BYTE
*)p
!= end
);
2109 TRACE("Unhandled format %i, not adjusting volume.\n", This
->alsa_format
);
2114 static snd_pcm_sframes_t
alsa_write_best_effort(ACImpl
*This
, BYTE
*buf
,
2115 snd_pcm_uframes_t frames
, BOOL mute
)
2117 snd_pcm_sframes_t written
;
2119 adjust_buffer_volume(This
, buf
, frames
, mute
);
2121 /* Mark the frames we've already adjusted */
2122 if (This
->vol_adjusted_frames
< frames
)
2123 This
->vol_adjusted_frames
= frames
;
2125 buf
= remap_channels(This
, buf
, frames
);
2127 written
= snd_pcm_writei(This
->pcm_handle
, buf
, frames
);
2131 if(written
== -EAGAIN
)
2135 WARN("writei failed, recovering: %ld (%s)\n", written
,
2136 snd_strerror(written
));
2138 ret
= snd_pcm_recover(This
->pcm_handle
, written
, 0);
2140 WARN("Could not recover: %d (%s)\n", ret
, snd_strerror(ret
));
2144 written
= snd_pcm_writei(This
->pcm_handle
, buf
, frames
);
2148 This
->vol_adjusted_frames
-= written
;
2152 static snd_pcm_sframes_t
alsa_write_buffer_wrap(ACImpl
*This
, BYTE
*buf
,
2153 snd_pcm_uframes_t buflen
, snd_pcm_uframes_t offs
,
2154 snd_pcm_uframes_t to_write
)
2156 snd_pcm_sframes_t ret
= 0;
2159 snd_pcm_uframes_t chunk
;
2160 snd_pcm_sframes_t tmp
;
2162 if(offs
+ to_write
> buflen
)
2163 chunk
= buflen
- offs
;
2167 tmp
= alsa_write_best_effort(This
, buf
+ offs
* This
->fmt
->nBlockAlign
, chunk
, This
->session
->mute
);
2182 static UINT
buf_ptr_diff(UINT left
, UINT right
, UINT bufsize
)
2185 return right
- left
;
2186 return bufsize
- (left
- right
);
2189 static UINT
data_not_in_alsa(ACImpl
*This
)
2193 diff
= buf_ptr_diff(This
->lcl_offs_frames
, This
->wri_offs_frames
, This
->bufsize_frames
);
2197 return This
->held_frames
- This
->data_in_alsa_frames
;
2199 /* Here's the buffer setup:
2201 * vvvvvvvv sent to HW already
2202 * vvvvvvvv in ALSA buffer but rewindable
2203 * [dddddddddddddddd] ALSA buffer
2204 * [dddddddddddddddd--------] mmdevapi buffer
2205 * ^^^^^^^^ data_in_alsa_frames
2206 * ^^^^^^^^^^^^^^^^ held_frames
2210 * GetCurrentPadding is held_frames
2212 * During period callback, we decrement held_frames, fill ALSA buffer, and move
2215 * During Stop, we rewind the ALSA buffer
2217 static void alsa_write_data(ACImpl
*This
)
2219 snd_pcm_sframes_t written
;
2220 snd_pcm_uframes_t avail
, max_copy_frames
, data_frames_played
;
2223 /* this call seems to be required to get an accurate snd_pcm_state() */
2224 avail
= snd_pcm_avail_update(This
->pcm_handle
);
2226 if(snd_pcm_state(This
->pcm_handle
) == SND_PCM_STATE_XRUN
){
2227 TRACE("XRun state, recovering\n");
2229 avail
= This
->alsa_bufsize_frames
;
2231 if((err
= snd_pcm_recover(This
->pcm_handle
, -EPIPE
, 1)) < 0)
2232 WARN("snd_pcm_recover failed: %d (%s)\n", err
, snd_strerror(err
));
2234 if((err
= snd_pcm_reset(This
->pcm_handle
)) < 0)
2235 WARN("snd_pcm_reset failed: %d (%s)\n", err
, snd_strerror(err
));
2237 if((err
= snd_pcm_prepare(This
->pcm_handle
)) < 0)
2238 WARN("snd_pcm_prepare failed: %d (%s)\n", err
, snd_strerror(err
));
2241 TRACE("avail: %ld\n", avail
);
2243 /* Add a lead-in when starting with too few frames to ensure
2244 * continuous rendering. Additional benefit: Force ALSA to start. */
2245 if(This
->data_in_alsa_frames
== 0 && This
->held_frames
< This
->alsa_period_frames
)
2247 alsa_write_best_effort(This
, This
->silence_buf
, This
->alsa_period_frames
- This
->held_frames
, FALSE
);
2248 This
->vol_adjusted_frames
= 0;
2252 max_copy_frames
= data_not_in_alsa(This
);
2254 max_copy_frames
= 0;
2256 data_frames_played
= min(This
->data_in_alsa_frames
, avail
);
2257 This
->data_in_alsa_frames
-= data_frames_played
;
2259 if(This
->held_frames
> data_frames_played
){
2261 This
->held_frames
-= data_frames_played
;
2263 This
->held_frames
= 0;
2265 while(avail
&& max_copy_frames
){
2266 snd_pcm_uframes_t to_write
;
2268 to_write
= min(avail
, max_copy_frames
);
2270 written
= alsa_write_buffer_wrap(This
, This
->local_buffer
,
2271 This
->bufsize_frames
, This
->lcl_offs_frames
, to_write
);
2276 This
->lcl_offs_frames
+= written
;
2277 This
->lcl_offs_frames
%= This
->bufsize_frames
;
2278 This
->data_in_alsa_frames
+= written
;
2279 max_copy_frames
-= written
;
2283 SetEvent(This
->event
);
2286 static void alsa_read_data(ACImpl
*This
)
2288 snd_pcm_sframes_t nread
;
2289 UINT32 pos
= This
->wri_offs_frames
, limit
= This
->held_frames
;
2294 /* FIXME: Detect overrun and signal DATA_DISCONTINUITY
2295 * How to count overrun frames and report them as position increase? */
2296 limit
= This
->bufsize_frames
- max(limit
, pos
);
2298 nread
= snd_pcm_readi(This
->pcm_handle
,
2299 This
->local_buffer
+ pos
* This
->fmt
->nBlockAlign
, limit
);
2300 TRACE("read %ld from %u limit %u\n", nread
, pos
, limit
);
2304 if(nread
== -EAGAIN
) /* no data yet */
2307 WARN("read failed, recovering: %ld (%s)\n", nread
, snd_strerror(nread
));
2309 ret
= snd_pcm_recover(This
->pcm_handle
, nread
, 0);
2311 WARN("Recover failed: %d (%s)\n", ret
, snd_strerror(ret
));
2315 nread
= snd_pcm_readi(This
->pcm_handle
,
2316 This
->local_buffer
+ pos
* This
->fmt
->nBlockAlign
, limit
);
2318 WARN("read failed: %ld (%s)\n", nread
, snd_strerror(nread
));
2323 if(This
->session
->mute
){
2325 if((err
= snd_pcm_format_set_silence(This
->alsa_format
,
2326 This
->local_buffer
+ pos
* This
->fmt
->nBlockAlign
,
2328 WARN("Setting buffer to silence failed: %d (%s)\n", err
,
2332 This
->wri_offs_frames
+= nread
;
2333 This
->wri_offs_frames
%= This
->bufsize_frames
;
2334 This
->held_frames
+= nread
;
2338 SetEvent(This
->event
);
2341 static void CALLBACK
alsa_push_buffer_data(void *user
, BOOLEAN timer
)
2343 ACImpl
*This
= user
;
2345 EnterCriticalSection(&This
->lock
);
2347 QueryPerformanceCounter(&This
->last_period_time
);
2349 if(This
->dataflow
== eRender
)
2350 alsa_write_data(This
);
2351 else if(This
->dataflow
== eCapture
)
2352 alsa_read_data(This
);
2354 LeaveCriticalSection(&This
->lock
);
2357 static snd_pcm_uframes_t
interp_elapsed_frames(ACImpl
*This
)
2359 LARGE_INTEGER time_freq
, current_time
, time_diff
;
2360 QueryPerformanceFrequency(&time_freq
);
2361 QueryPerformanceCounter(¤t_time
);
2362 time_diff
.QuadPart
= current_time
.QuadPart
- This
->last_period_time
.QuadPart
;
2363 return MulDiv(time_diff
.QuadPart
, This
->fmt
->nSamplesPerSec
, time_freq
.QuadPart
);
2366 static int alsa_rewind_best_effort(ACImpl
*This
)
2368 snd_pcm_uframes_t len
, leave
;
2370 /* we can't use snd_pcm_rewindable, some PCM devices crash. so follow
2371 * PulseAudio's example and rewind as much data as we believe is in the
2372 * buffer, minus 1.33ms for safety. */
2374 /* amount of data to leave in ALSA buffer */
2375 leave
= interp_elapsed_frames(This
) + This
->safe_rewind_frames
;
2377 if(This
->held_frames
< leave
)
2378 This
->held_frames
= 0;
2380 This
->held_frames
-= leave
;
2382 if(This
->data_in_alsa_frames
< leave
)
2385 len
= This
->data_in_alsa_frames
- leave
;
2387 TRACE("rewinding %lu frames, now held %u\n", len
, This
->held_frames
);
2390 /* snd_pcm_rewind return value is often broken, assume it succeeded */
2391 snd_pcm_rewind(This
->pcm_handle
, len
);
2393 This
->data_in_alsa_frames
= 0;
2398 static HRESULT WINAPI
AudioClient_Start(IAudioClient3
*iface
)
2400 ACImpl
*This
= impl_from_IAudioClient3(iface
);
2402 TRACE("(%p)\n", This
);
2404 EnterCriticalSection(&This
->lock
);
2407 LeaveCriticalSection(&This
->lock
);
2408 return AUDCLNT_E_NOT_INITIALIZED
;
2411 if((This
->flags
& AUDCLNT_STREAMFLAGS_EVENTCALLBACK
) && !This
->event
){
2412 LeaveCriticalSection(&This
->lock
);
2413 return AUDCLNT_E_EVENTHANDLE_NOT_SET
;
2417 LeaveCriticalSection(&This
->lock
);
2418 return AUDCLNT_E_NOT_STOPPED
;
2421 if(This
->dataflow
== eCapture
){
2422 /* dump any data that might be leftover in the ALSA capture buffer */
2423 snd_pcm_readi(This
->pcm_handle
, This
->local_buffer
,
2424 This
->bufsize_frames
);
2426 snd_pcm_sframes_t avail
, written
;
2427 snd_pcm_uframes_t offs
;
2429 avail
= snd_pcm_avail_update(This
->pcm_handle
);
2430 avail
= min(avail
, This
->held_frames
);
2432 if(This
->wri_offs_frames
< This
->held_frames
)
2433 offs
= This
->bufsize_frames
- This
->held_frames
+ This
->wri_offs_frames
;
2435 offs
= This
->wri_offs_frames
- This
->held_frames
;
2437 /* fill it with data */
2438 written
= alsa_write_buffer_wrap(This
, This
->local_buffer
,
2439 This
->bufsize_frames
, offs
, avail
);
2442 This
->lcl_offs_frames
= (offs
+ written
) % This
->bufsize_frames
;
2443 This
->data_in_alsa_frames
= written
;
2445 This
->lcl_offs_frames
= offs
;
2446 This
->data_in_alsa_frames
= 0;
2451 if(!CreateTimerQueueTimer(&This
->timer
, g_timer_q
, alsa_push_buffer_data
,
2452 This
, 0, This
->mmdev_period_rt
/ 10000, WT_EXECUTEINTIMERTHREAD
)){
2453 LeaveCriticalSection(&This
->lock
);
2454 WARN("Unable to create timer: %u\n", GetLastError());
2455 return E_OUTOFMEMORY
;
2459 This
->started
= TRUE
;
2461 LeaveCriticalSection(&This
->lock
);
2466 static HRESULT WINAPI
AudioClient_Stop(IAudioClient3
*iface
)
2468 ACImpl
*This
= impl_from_IAudioClient3(iface
);
2470 TRACE("(%p)\n", This
);
2472 EnterCriticalSection(&This
->lock
);
2475 LeaveCriticalSection(&This
->lock
);
2476 return AUDCLNT_E_NOT_INITIALIZED
;
2480 LeaveCriticalSection(&This
->lock
);
2484 if(This
->dataflow
== eRender
)
2485 alsa_rewind_best_effort(This
);
2487 This
->started
= FALSE
;
2489 LeaveCriticalSection(&This
->lock
);
2494 static HRESULT WINAPI
AudioClient_Reset(IAudioClient3
*iface
)
2496 ACImpl
*This
= impl_from_IAudioClient3(iface
);
2498 TRACE("(%p)\n", This
);
2500 EnterCriticalSection(&This
->lock
);
2503 LeaveCriticalSection(&This
->lock
);
2504 return AUDCLNT_E_NOT_INITIALIZED
;
2508 LeaveCriticalSection(&This
->lock
);
2509 return AUDCLNT_E_NOT_STOPPED
;
2512 if(This
->getbuf_last
){
2513 LeaveCriticalSection(&This
->lock
);
2514 return AUDCLNT_E_BUFFER_OPERATION_PENDING
;
2517 if(snd_pcm_drop(This
->pcm_handle
) < 0)
2518 WARN("snd_pcm_drop failed\n");
2520 if(snd_pcm_reset(This
->pcm_handle
) < 0)
2521 WARN("snd_pcm_reset failed\n");
2523 if(snd_pcm_prepare(This
->pcm_handle
) < 0)
2524 WARN("snd_pcm_prepare failed\n");
2526 if(This
->dataflow
== eRender
){
2527 This
->written_frames
= 0;
2528 This
->last_pos_frames
= 0;
2530 This
->written_frames
+= This
->held_frames
;
2532 This
->held_frames
= 0;
2533 This
->lcl_offs_frames
= 0;
2534 This
->wri_offs_frames
= 0;
2536 LeaveCriticalSection(&This
->lock
);
2541 static HRESULT WINAPI
AudioClient_SetEventHandle(IAudioClient3
*iface
,
2544 ACImpl
*This
= impl_from_IAudioClient3(iface
);
2546 TRACE("(%p)->(%p)\n", This
, event
);
2549 return E_INVALIDARG
;
2551 EnterCriticalSection(&This
->lock
);
2554 LeaveCriticalSection(&This
->lock
);
2555 return AUDCLNT_E_NOT_INITIALIZED
;
2558 if(!(This
->flags
& AUDCLNT_STREAMFLAGS_EVENTCALLBACK
)){
2559 LeaveCriticalSection(&This
->lock
);
2560 return AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED
;
2564 LeaveCriticalSection(&This
->lock
);
2565 FIXME("called twice\n");
2566 return HRESULT_FROM_WIN32(ERROR_INVALID_NAME
);
2569 This
->event
= event
;
2571 LeaveCriticalSection(&This
->lock
);
2576 static HRESULT WINAPI
AudioClient_GetService(IAudioClient3
*iface
, REFIID riid
,
2579 ACImpl
*This
= impl_from_IAudioClient3(iface
);
2581 TRACE("(%p)->(%s, %p)\n", This
, debugstr_guid(riid
), ppv
);
2587 EnterCriticalSection(&This
->lock
);
2590 LeaveCriticalSection(&This
->lock
);
2591 return AUDCLNT_E_NOT_INITIALIZED
;
2594 if(IsEqualIID(riid
, &IID_IAudioRenderClient
)){
2595 if(This
->dataflow
!= eRender
){
2596 LeaveCriticalSection(&This
->lock
);
2597 return AUDCLNT_E_WRONG_ENDPOINT_TYPE
;
2599 IAudioRenderClient_AddRef(&This
->IAudioRenderClient_iface
);
2600 *ppv
= &This
->IAudioRenderClient_iface
;
2601 }else if(IsEqualIID(riid
, &IID_IAudioCaptureClient
)){
2602 if(This
->dataflow
!= eCapture
){
2603 LeaveCriticalSection(&This
->lock
);
2604 return AUDCLNT_E_WRONG_ENDPOINT_TYPE
;
2606 IAudioCaptureClient_AddRef(&This
->IAudioCaptureClient_iface
);
2607 *ppv
= &This
->IAudioCaptureClient_iface
;
2608 }else if(IsEqualIID(riid
, &IID_IAudioClock
)){
2609 IAudioClock_AddRef(&This
->IAudioClock_iface
);
2610 *ppv
= &This
->IAudioClock_iface
;
2611 }else if(IsEqualIID(riid
, &IID_IAudioStreamVolume
)){
2612 IAudioStreamVolume_AddRef(&This
->IAudioStreamVolume_iface
);
2613 *ppv
= &This
->IAudioStreamVolume_iface
;
2614 }else if(IsEqualIID(riid
, &IID_IAudioSessionControl
)){
2615 if(!This
->session_wrapper
){
2616 This
->session_wrapper
= AudioSessionWrapper_Create(This
);
2617 if(!This
->session_wrapper
){
2618 LeaveCriticalSection(&This
->lock
);
2619 return E_OUTOFMEMORY
;
2622 IAudioSessionControl2_AddRef(&This
->session_wrapper
->IAudioSessionControl2_iface
);
2624 *ppv
= &This
->session_wrapper
->IAudioSessionControl2_iface
;
2625 }else if(IsEqualIID(riid
, &IID_IChannelAudioVolume
)){
2626 if(!This
->session_wrapper
){
2627 This
->session_wrapper
= AudioSessionWrapper_Create(This
);
2628 if(!This
->session_wrapper
){
2629 LeaveCriticalSection(&This
->lock
);
2630 return E_OUTOFMEMORY
;
2633 IChannelAudioVolume_AddRef(&This
->session_wrapper
->IChannelAudioVolume_iface
);
2635 *ppv
= &This
->session_wrapper
->IChannelAudioVolume_iface
;
2636 }else if(IsEqualIID(riid
, &IID_ISimpleAudioVolume
)){
2637 if(!This
->session_wrapper
){
2638 This
->session_wrapper
= AudioSessionWrapper_Create(This
);
2639 if(!This
->session_wrapper
){
2640 LeaveCriticalSection(&This
->lock
);
2641 return E_OUTOFMEMORY
;
2644 ISimpleAudioVolume_AddRef(&This
->session_wrapper
->ISimpleAudioVolume_iface
);
2646 *ppv
= &This
->session_wrapper
->ISimpleAudioVolume_iface
;
2650 LeaveCriticalSection(&This
->lock
);
2654 LeaveCriticalSection(&This
->lock
);
2656 FIXME("stub %s\n", debugstr_guid(riid
));
2657 return E_NOINTERFACE
;
2660 static HRESULT WINAPI
AudioClient_IsOffloadCapable(IAudioClient3
*iface
,
2661 AUDIO_STREAM_CATEGORY category
, BOOL
*offload_capable
)
2663 ACImpl
*This
= impl_from_IAudioClient3(iface
);
2665 TRACE("(%p)->(0x%x, %p)\n", This
, category
, offload_capable
);
2667 if(!offload_capable
)
2668 return E_INVALIDARG
;
2670 *offload_capable
= FALSE
;
2675 static HRESULT WINAPI
AudioClient_SetClientProperties(IAudioClient3
*iface
,
2676 const AudioClientProperties
*prop
)
2678 ACImpl
*This
= impl_from_IAudioClient3(iface
);
2680 TRACE("(%p)->(%p)\n", This
, prop
);
2685 if(prop
->cbSize
!= sizeof(*prop
))
2686 return E_INVALIDARG
;
2688 TRACE("{ bIsOffload: %u, eCategory: 0x%x, Options: 0x%x }\n",
2693 if(prop
->bIsOffload
)
2694 return AUDCLNT_E_ENDPOINT_OFFLOAD_NOT_CAPABLE
;
2699 static HRESULT WINAPI
AudioClient_GetBufferSizeLimits(IAudioClient3
*iface
,
2700 const WAVEFORMATEX
*format
, BOOL event_driven
, REFERENCE_TIME
*min_duration
,
2701 REFERENCE_TIME
*max_duration
)
2703 ACImpl
*This
= impl_from_IAudioClient3(iface
);
2705 FIXME("(%p)->(%p, %u, %p, %p)\n", This
, format
, event_driven
, min_duration
, max_duration
);
2710 static HRESULT WINAPI
AudioClient_GetSharedModeEnginePeriod(IAudioClient3
*iface
,
2711 const WAVEFORMATEX
*format
, UINT32
*default_period_frames
, UINT32
*unit_period_frames
,
2712 UINT32
*min_period_frames
, UINT32
*max_period_frames
)
2714 ACImpl
*This
= impl_from_IAudioClient3(iface
);
2716 FIXME("(%p)->(%p, %p, %p, %p, %p)\n", This
, format
, default_period_frames
, unit_period_frames
,
2717 min_period_frames
, max_period_frames
);
2722 static HRESULT WINAPI
AudioClient_GetCurrentSharedModeEnginePeriod(IAudioClient3
*iface
,
2723 WAVEFORMATEX
**cur_format
, UINT32
*cur_period_frames
)
2725 ACImpl
*This
= impl_from_IAudioClient3(iface
);
2727 FIXME("(%p)->(%p, %p)\n", This
, cur_format
, cur_period_frames
);
2732 static HRESULT WINAPI
AudioClient_InitializeSharedAudioStream(IAudioClient3
*iface
,
2733 DWORD flags
, UINT32 period_frames
, const WAVEFORMATEX
*format
,
2734 const GUID
*session_guid
)
2736 ACImpl
*This
= impl_from_IAudioClient3(iface
);
2738 FIXME("(%p)->(0x%x, %u, %p, %s)\n", This
, flags
, period_frames
, format
, debugstr_guid(session_guid
));
2743 static const IAudioClient3Vtbl AudioClient3_Vtbl
=
2745 AudioClient_QueryInterface
,
2747 AudioClient_Release
,
2748 AudioClient_Initialize
,
2749 AudioClient_GetBufferSize
,
2750 AudioClient_GetStreamLatency
,
2751 AudioClient_GetCurrentPadding
,
2752 AudioClient_IsFormatSupported
,
2753 AudioClient_GetMixFormat
,
2754 AudioClient_GetDevicePeriod
,
2758 AudioClient_SetEventHandle
,
2759 AudioClient_GetService
,
2760 AudioClient_IsOffloadCapable
,
2761 AudioClient_SetClientProperties
,
2762 AudioClient_GetBufferSizeLimits
,
2763 AudioClient_GetSharedModeEnginePeriod
,
2764 AudioClient_GetCurrentSharedModeEnginePeriod
,
2765 AudioClient_InitializeSharedAudioStream
,
2768 static HRESULT WINAPI
AudioRenderClient_QueryInterface(
2769 IAudioRenderClient
*iface
, REFIID riid
, void **ppv
)
2771 ACImpl
*This
= impl_from_IAudioRenderClient(iface
);
2772 TRACE("(%p)->(%s, %p)\n", iface
, debugstr_guid(riid
), ppv
);
2778 if(IsEqualIID(riid
, &IID_IUnknown
) ||
2779 IsEqualIID(riid
, &IID_IAudioRenderClient
))
2781 else if(IsEqualIID(riid
, &IID_IMarshal
))
2782 return IUnknown_QueryInterface(This
->pUnkFTMarshal
, riid
, ppv
);
2785 IUnknown_AddRef((IUnknown
*)*ppv
);
2789 WARN("Unknown interface %s\n", debugstr_guid(riid
));
2790 return E_NOINTERFACE
;
2793 static ULONG WINAPI
AudioRenderClient_AddRef(IAudioRenderClient
*iface
)
2795 ACImpl
*This
= impl_from_IAudioRenderClient(iface
);
2796 return AudioClient_AddRef(&This
->IAudioClient3_iface
);
2799 static ULONG WINAPI
AudioRenderClient_Release(IAudioRenderClient
*iface
)
2801 ACImpl
*This
= impl_from_IAudioRenderClient(iface
);
2802 return AudioClient_Release(&This
->IAudioClient3_iface
);
2805 static HRESULT WINAPI
AudioRenderClient_GetBuffer(IAudioRenderClient
*iface
,
2806 UINT32 frames
, BYTE
**data
)
2808 ACImpl
*This
= impl_from_IAudioRenderClient(iface
);
2811 TRACE("(%p)->(%u, %p)\n", This
, frames
, data
);
2817 EnterCriticalSection(&This
->lock
);
2819 if(This
->getbuf_last
){
2820 LeaveCriticalSection(&This
->lock
);
2821 return AUDCLNT_E_OUT_OF_ORDER
;
2825 LeaveCriticalSection(&This
->lock
);
2829 /* held_frames == GetCurrentPadding_nolock(); */
2830 if(This
->held_frames
+ frames
> This
->bufsize_frames
){
2831 LeaveCriticalSection(&This
->lock
);
2832 return AUDCLNT_E_BUFFER_TOO_LARGE
;
2835 write_pos
= This
->wri_offs_frames
;
2836 if(write_pos
+ frames
> This
->bufsize_frames
){
2837 if(This
->tmp_buffer_frames
< frames
){
2838 HeapFree(GetProcessHeap(), 0, This
->tmp_buffer
);
2839 This
->tmp_buffer
= HeapAlloc(GetProcessHeap(), 0,
2840 frames
* This
->fmt
->nBlockAlign
);
2841 if(!This
->tmp_buffer
){
2842 LeaveCriticalSection(&This
->lock
);
2843 return E_OUTOFMEMORY
;
2845 This
->tmp_buffer_frames
= frames
;
2847 *data
= This
->tmp_buffer
;
2848 This
->getbuf_last
= -frames
;
2850 *data
= This
->local_buffer
+ write_pos
* This
->fmt
->nBlockAlign
;
2851 This
->getbuf_last
= frames
;
2854 silence_buffer(This
, *data
, frames
);
2856 LeaveCriticalSection(&This
->lock
);
2861 static void alsa_wrap_buffer(ACImpl
*This
, BYTE
*buffer
, UINT32 written_frames
)
2863 snd_pcm_uframes_t write_offs_frames
= This
->wri_offs_frames
;
2864 UINT32 write_offs_bytes
= write_offs_frames
* This
->fmt
->nBlockAlign
;
2865 snd_pcm_uframes_t chunk_frames
= This
->bufsize_frames
- write_offs_frames
;
2866 UINT32 chunk_bytes
= chunk_frames
* This
->fmt
->nBlockAlign
;
2867 UINT32 written_bytes
= written_frames
* This
->fmt
->nBlockAlign
;
2869 if(written_bytes
<= chunk_bytes
){
2870 memcpy(This
->local_buffer
+ write_offs_bytes
, buffer
, written_bytes
);
2872 memcpy(This
->local_buffer
+ write_offs_bytes
, buffer
, chunk_bytes
);
2873 memcpy(This
->local_buffer
, buffer
+ chunk_bytes
,
2874 written_bytes
- chunk_bytes
);
2878 static HRESULT WINAPI
AudioRenderClient_ReleaseBuffer(
2879 IAudioRenderClient
*iface
, UINT32 written_frames
, DWORD flags
)
2881 ACImpl
*This
= impl_from_IAudioRenderClient(iface
);
2884 TRACE("(%p)->(%u, %x)\n", This
, written_frames
, flags
);
2886 EnterCriticalSection(&This
->lock
);
2888 if(!written_frames
){
2889 This
->getbuf_last
= 0;
2890 LeaveCriticalSection(&This
->lock
);
2894 if(!This
->getbuf_last
){
2895 LeaveCriticalSection(&This
->lock
);
2896 return AUDCLNT_E_OUT_OF_ORDER
;
2899 if(written_frames
> (This
->getbuf_last
>= 0 ? This
->getbuf_last
: -This
->getbuf_last
)){
2900 LeaveCriticalSection(&This
->lock
);
2901 return AUDCLNT_E_INVALID_SIZE
;
2904 if(This
->getbuf_last
>= 0)
2905 buffer
= This
->local_buffer
+ This
->wri_offs_frames
* This
->fmt
->nBlockAlign
;
2907 buffer
= This
->tmp_buffer
;
2909 if(flags
& AUDCLNT_BUFFERFLAGS_SILENT
)
2910 silence_buffer(This
, buffer
, written_frames
);
2912 if(This
->getbuf_last
< 0)
2913 alsa_wrap_buffer(This
, buffer
, written_frames
);
2915 This
->wri_offs_frames
+= written_frames
;
2916 This
->wri_offs_frames
%= This
->bufsize_frames
;
2917 This
->held_frames
+= written_frames
;
2918 This
->written_frames
+= written_frames
;
2919 This
->getbuf_last
= 0;
2921 LeaveCriticalSection(&This
->lock
);
2926 static const IAudioRenderClientVtbl AudioRenderClient_Vtbl
= {
2927 AudioRenderClient_QueryInterface
,
2928 AudioRenderClient_AddRef
,
2929 AudioRenderClient_Release
,
2930 AudioRenderClient_GetBuffer
,
2931 AudioRenderClient_ReleaseBuffer
2934 static HRESULT WINAPI
AudioCaptureClient_QueryInterface(
2935 IAudioCaptureClient
*iface
, REFIID riid
, void **ppv
)
2937 ACImpl
*This
= impl_from_IAudioCaptureClient(iface
);
2938 TRACE("(%p)->(%s, %p)\n", iface
, debugstr_guid(riid
), ppv
);
2944 if(IsEqualIID(riid
, &IID_IUnknown
) ||
2945 IsEqualIID(riid
, &IID_IAudioCaptureClient
))
2947 else if(IsEqualIID(riid
, &IID_IMarshal
))
2948 return IUnknown_QueryInterface(This
->pUnkFTMarshal
, riid
, ppv
);
2951 IUnknown_AddRef((IUnknown
*)*ppv
);
2955 WARN("Unknown interface %s\n", debugstr_guid(riid
));
2956 return E_NOINTERFACE
;
2959 static ULONG WINAPI
AudioCaptureClient_AddRef(IAudioCaptureClient
*iface
)
2961 ACImpl
*This
= impl_from_IAudioCaptureClient(iface
);
2962 return IAudioClient3_AddRef(&This
->IAudioClient3_iface
);
2965 static ULONG WINAPI
AudioCaptureClient_Release(IAudioCaptureClient
*iface
)
2967 ACImpl
*This
= impl_from_IAudioCaptureClient(iface
);
2968 return IAudioClient3_Release(&This
->IAudioClient3_iface
);
2971 static HRESULT WINAPI
AudioCaptureClient_GetBuffer(IAudioCaptureClient
*iface
,
2972 BYTE
**data
, UINT32
*frames
, DWORD
*flags
, UINT64
*devpos
,
2975 ACImpl
*This
= impl_from_IAudioCaptureClient(iface
);
2977 TRACE("(%p)->(%p, %p, %p, %p, %p)\n", This
, data
, frames
, flags
,
2985 if(!frames
|| !flags
)
2988 EnterCriticalSection(&This
->lock
);
2990 if(This
->getbuf_last
){
2991 LeaveCriticalSection(&This
->lock
);
2992 return AUDCLNT_E_OUT_OF_ORDER
;
2995 /* hr = GetNextPacketSize(iface, frames); */
2996 if(This
->held_frames
< This
->mmdev_period_frames
){
2998 LeaveCriticalSection(&This
->lock
);
2999 return AUDCLNT_S_BUFFER_EMPTY
;
3001 *frames
= This
->mmdev_period_frames
;
3003 if(This
->lcl_offs_frames
+ *frames
> This
->bufsize_frames
){
3004 UINT32 chunk_bytes
, offs_bytes
, frames_bytes
;
3005 if(This
->tmp_buffer_frames
< *frames
){
3006 HeapFree(GetProcessHeap(), 0, This
->tmp_buffer
);
3007 This
->tmp_buffer
= HeapAlloc(GetProcessHeap(), 0,
3008 *frames
* This
->fmt
->nBlockAlign
);
3009 if(!This
->tmp_buffer
){
3010 LeaveCriticalSection(&This
->lock
);
3011 return E_OUTOFMEMORY
;
3013 This
->tmp_buffer_frames
= *frames
;
3016 *data
= This
->tmp_buffer
;
3017 chunk_bytes
= (This
->bufsize_frames
- This
->lcl_offs_frames
) *
3018 This
->fmt
->nBlockAlign
;
3019 offs_bytes
= This
->lcl_offs_frames
* This
->fmt
->nBlockAlign
;
3020 frames_bytes
= *frames
* This
->fmt
->nBlockAlign
;
3021 memcpy(This
->tmp_buffer
, This
->local_buffer
+ offs_bytes
, chunk_bytes
);
3022 memcpy(This
->tmp_buffer
+ chunk_bytes
, This
->local_buffer
,
3023 frames_bytes
- chunk_bytes
);
3025 *data
= This
->local_buffer
+
3026 This
->lcl_offs_frames
* This
->fmt
->nBlockAlign
;
3028 This
->getbuf_last
= *frames
;
3032 *devpos
= This
->written_frames
;
3033 if(qpcpos
){ /* fixme: qpc of recording time */
3034 LARGE_INTEGER stamp
, freq
;
3035 QueryPerformanceCounter(&stamp
);
3036 QueryPerformanceFrequency(&freq
);
3037 *qpcpos
= (stamp
.QuadPart
* (INT64
)10000000) / freq
.QuadPart
;
3040 LeaveCriticalSection(&This
->lock
);
3042 return *frames
? S_OK
: AUDCLNT_S_BUFFER_EMPTY
;
3045 static HRESULT WINAPI
AudioCaptureClient_ReleaseBuffer(
3046 IAudioCaptureClient
*iface
, UINT32 done
)
3048 ACImpl
*This
= impl_from_IAudioCaptureClient(iface
);
3050 TRACE("(%p)->(%u)\n", This
, done
);
3052 EnterCriticalSection(&This
->lock
);
3055 This
->getbuf_last
= 0;
3056 LeaveCriticalSection(&This
->lock
);
3060 if(!This
->getbuf_last
){
3061 LeaveCriticalSection(&This
->lock
);
3062 return AUDCLNT_E_OUT_OF_ORDER
;
3065 if(This
->getbuf_last
!= done
){
3066 LeaveCriticalSection(&This
->lock
);
3067 return AUDCLNT_E_INVALID_SIZE
;
3070 This
->written_frames
+= done
;
3071 This
->held_frames
-= done
;
3072 This
->lcl_offs_frames
+= done
;
3073 This
->lcl_offs_frames
%= This
->bufsize_frames
;
3074 This
->getbuf_last
= 0;
3076 LeaveCriticalSection(&This
->lock
);
3081 static HRESULT WINAPI
AudioCaptureClient_GetNextPacketSize(
3082 IAudioCaptureClient
*iface
, UINT32
*frames
)
3084 ACImpl
*This
= impl_from_IAudioCaptureClient(iface
);
3086 TRACE("(%p)->(%p)\n", This
, frames
);
3091 EnterCriticalSection(&This
->lock
);
3093 *frames
= This
->held_frames
< This
->mmdev_period_frames
? 0 : This
->mmdev_period_frames
;
3095 LeaveCriticalSection(&This
->lock
);
3100 static const IAudioCaptureClientVtbl AudioCaptureClient_Vtbl
=
3102 AudioCaptureClient_QueryInterface
,
3103 AudioCaptureClient_AddRef
,
3104 AudioCaptureClient_Release
,
3105 AudioCaptureClient_GetBuffer
,
3106 AudioCaptureClient_ReleaseBuffer
,
3107 AudioCaptureClient_GetNextPacketSize
3110 static HRESULT WINAPI
AudioClock_QueryInterface(IAudioClock
*iface
,
3111 REFIID riid
, void **ppv
)
3113 ACImpl
*This
= impl_from_IAudioClock(iface
);
3115 TRACE("(%p)->(%s, %p)\n", iface
, debugstr_guid(riid
), ppv
);
3121 if(IsEqualIID(riid
, &IID_IUnknown
) || IsEqualIID(riid
, &IID_IAudioClock
))
3123 else if(IsEqualIID(riid
, &IID_IAudioClock2
))
3124 *ppv
= &This
->IAudioClock2_iface
;
3126 IUnknown_AddRef((IUnknown
*)*ppv
);
3130 WARN("Unknown interface %s\n", debugstr_guid(riid
));
3131 return E_NOINTERFACE
;
3134 static ULONG WINAPI
AudioClock_AddRef(IAudioClock
*iface
)
3136 ACImpl
*This
= impl_from_IAudioClock(iface
);
3137 return IAudioClient3_AddRef(&This
->IAudioClient3_iface
);
3140 static ULONG WINAPI
AudioClock_Release(IAudioClock
*iface
)
3142 ACImpl
*This
= impl_from_IAudioClock(iface
);
3143 return IAudioClient3_Release(&This
->IAudioClient3_iface
);
3146 static HRESULT WINAPI
AudioClock_GetFrequency(IAudioClock
*iface
, UINT64
*freq
)
3148 ACImpl
*This
= impl_from_IAudioClock(iface
);
3150 TRACE("(%p)->(%p)\n", This
, freq
);
3152 if(This
->share
== AUDCLNT_SHAREMODE_SHARED
)
3153 *freq
= (UINT64
)This
->fmt
->nSamplesPerSec
* This
->fmt
->nBlockAlign
;
3155 *freq
= This
->fmt
->nSamplesPerSec
;
3160 static HRESULT WINAPI
AudioClock_GetPosition(IAudioClock
*iface
, UINT64
*pos
,
3163 ACImpl
*This
= impl_from_IAudioClock(iface
);
3165 snd_pcm_state_t alsa_state
;
3167 TRACE("(%p)->(%p, %p)\n", This
, pos
, qpctime
);
3172 EnterCriticalSection(&This
->lock
);
3174 /* avail_update required to get accurate snd_pcm_state() */
3175 snd_pcm_avail_update(This
->pcm_handle
);
3176 alsa_state
= snd_pcm_state(This
->pcm_handle
);
3178 if(This
->dataflow
== eRender
){
3179 position
= This
->written_frames
- This
->held_frames
;
3181 if(This
->started
&& alsa_state
== SND_PCM_STATE_RUNNING
&& This
->held_frames
)
3182 /* we should be using snd_pcm_delay here, but it is broken
3183 * especially during ALSA device underrun. instead, let's just
3184 * interpolate between periods with the system timer. */
3185 position
+= interp_elapsed_frames(This
);
3187 position
= min(position
, This
->written_frames
- This
->held_frames
+ This
->mmdev_period_frames
);
3189 position
= min(position
, This
->written_frames
);
3191 position
= This
->written_frames
+ This
->held_frames
;
3193 /* ensure monotic growth */
3194 if(position
< This
->last_pos_frames
)
3195 position
= This
->last_pos_frames
;
3197 This
->last_pos_frames
= position
;
3199 TRACE("frames written: %u, held: %u, state: 0x%x, position: %u\n",
3200 (UINT32
)(This
->written_frames
%1000000000), This
->held_frames
,
3201 alsa_state
, (UINT32
)(position
%1000000000));
3203 LeaveCriticalSection(&This
->lock
);
3205 if(This
->share
== AUDCLNT_SHAREMODE_SHARED
)
3206 *pos
= position
* This
->fmt
->nBlockAlign
;
3211 LARGE_INTEGER stamp
, freq
;
3212 QueryPerformanceCounter(&stamp
);
3213 QueryPerformanceFrequency(&freq
);
3214 *qpctime
= (stamp
.QuadPart
* (INT64
)10000000) / freq
.QuadPart
;
3220 static HRESULT WINAPI
AudioClock_GetCharacteristics(IAudioClock
*iface
,
3223 ACImpl
*This
= impl_from_IAudioClock(iface
);
3225 TRACE("(%p)->(%p)\n", This
, chars
);
3230 *chars
= AUDIOCLOCK_CHARACTERISTIC_FIXED_FREQ
;
3235 static const IAudioClockVtbl AudioClock_Vtbl
=
3237 AudioClock_QueryInterface
,
3240 AudioClock_GetFrequency
,
3241 AudioClock_GetPosition
,
3242 AudioClock_GetCharacteristics
3245 static HRESULT WINAPI
AudioClock2_QueryInterface(IAudioClock2
*iface
,
3246 REFIID riid
, void **ppv
)
3248 ACImpl
*This
= impl_from_IAudioClock2(iface
);
3249 return IAudioClock_QueryInterface(&This
->IAudioClock_iface
, riid
, ppv
);
3252 static ULONG WINAPI
AudioClock2_AddRef(IAudioClock2
*iface
)
3254 ACImpl
*This
= impl_from_IAudioClock2(iface
);
3255 return IAudioClient3_AddRef(&This
->IAudioClient3_iface
);
3258 static ULONG WINAPI
AudioClock2_Release(IAudioClock2
*iface
)
3260 ACImpl
*This
= impl_from_IAudioClock2(iface
);
3261 return IAudioClient3_Release(&This
->IAudioClient3_iface
);
3264 static HRESULT WINAPI
AudioClock2_GetDevicePosition(IAudioClock2
*iface
,
3265 UINT64
*pos
, UINT64
*qpctime
)
3267 ACImpl
*This
= impl_from_IAudioClock2(iface
);
3269 FIXME("(%p)->(%p, %p)\n", This
, pos
, qpctime
);
3274 static const IAudioClock2Vtbl AudioClock2_Vtbl
=
3276 AudioClock2_QueryInterface
,
3278 AudioClock2_Release
,
3279 AudioClock2_GetDevicePosition
3282 static AudioSessionWrapper
*AudioSessionWrapper_Create(ACImpl
*client
)
3284 AudioSessionWrapper
*ret
;
3286 ret
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
,
3287 sizeof(AudioSessionWrapper
));
3291 ret
->IAudioSessionControl2_iface
.lpVtbl
= &AudioSessionControl2_Vtbl
;
3292 ret
->ISimpleAudioVolume_iface
.lpVtbl
= &SimpleAudioVolume_Vtbl
;
3293 ret
->IChannelAudioVolume_iface
.lpVtbl
= &ChannelAudioVolume_Vtbl
;
3297 ret
->client
= client
;
3299 ret
->session
= client
->session
;
3300 AudioClient_AddRef(&client
->IAudioClient3_iface
);
3306 static HRESULT WINAPI
AudioSessionControl_QueryInterface(
3307 IAudioSessionControl2
*iface
, REFIID riid
, void **ppv
)
3309 TRACE("(%p)->(%s, %p)\n", iface
, debugstr_guid(riid
), ppv
);
3315 if(IsEqualIID(riid
, &IID_IUnknown
) ||
3316 IsEqualIID(riid
, &IID_IAudioSessionControl
) ||
3317 IsEqualIID(riid
, &IID_IAudioSessionControl2
))
3320 IUnknown_AddRef((IUnknown
*)*ppv
);
3324 WARN("Unknown interface %s\n", debugstr_guid(riid
));
3325 return E_NOINTERFACE
;
3328 static ULONG WINAPI
AudioSessionControl_AddRef(IAudioSessionControl2
*iface
)
3330 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
3332 ref
= InterlockedIncrement(&This
->ref
);
3333 TRACE("(%p) Refcount now %u\n", This
, ref
);
3337 static ULONG WINAPI
AudioSessionControl_Release(IAudioSessionControl2
*iface
)
3339 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
3341 ref
= InterlockedDecrement(&This
->ref
);
3342 TRACE("(%p) Refcount now %u\n", This
, ref
);
3345 EnterCriticalSection(&This
->client
->lock
);
3346 This
->client
->session_wrapper
= NULL
;
3347 LeaveCriticalSection(&This
->client
->lock
);
3348 AudioClient_Release(&This
->client
->IAudioClient3_iface
);
3350 HeapFree(GetProcessHeap(), 0, This
);
3355 static HRESULT WINAPI
AudioSessionControl_GetState(IAudioSessionControl2
*iface
,
3356 AudioSessionState
*state
)
3358 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
3361 TRACE("(%p)->(%p)\n", This
, state
);
3364 return NULL_PTR_ERR
;
3366 EnterCriticalSection(&g_sessions_lock
);
3368 if(list_empty(&This
->session
->clients
)){
3369 *state
= AudioSessionStateExpired
;
3370 LeaveCriticalSection(&g_sessions_lock
);
3374 LIST_FOR_EACH_ENTRY(client
, &This
->session
->clients
, ACImpl
, entry
){
3375 EnterCriticalSection(&client
->lock
);
3376 if(client
->started
){
3377 *state
= AudioSessionStateActive
;
3378 LeaveCriticalSection(&client
->lock
);
3379 LeaveCriticalSection(&g_sessions_lock
);
3382 LeaveCriticalSection(&client
->lock
);
3385 LeaveCriticalSection(&g_sessions_lock
);
3387 *state
= AudioSessionStateInactive
;
3392 static HRESULT WINAPI
AudioSessionControl_GetDisplayName(
3393 IAudioSessionControl2
*iface
, WCHAR
**name
)
3395 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
3397 FIXME("(%p)->(%p) - stub\n", This
, name
);
3402 static HRESULT WINAPI
AudioSessionControl_SetDisplayName(
3403 IAudioSessionControl2
*iface
, const WCHAR
*name
, const GUID
*session
)
3405 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
3407 FIXME("(%p)->(%p, %s) - stub\n", This
, name
, debugstr_guid(session
));
3412 static HRESULT WINAPI
AudioSessionControl_GetIconPath(
3413 IAudioSessionControl2
*iface
, WCHAR
**path
)
3415 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
3417 FIXME("(%p)->(%p) - stub\n", This
, path
);
3422 static HRESULT WINAPI
AudioSessionControl_SetIconPath(
3423 IAudioSessionControl2
*iface
, const WCHAR
*path
, const GUID
*session
)
3425 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
3427 FIXME("(%p)->(%p, %s) - stub\n", This
, path
, debugstr_guid(session
));
3432 static HRESULT WINAPI
AudioSessionControl_GetGroupingParam(
3433 IAudioSessionControl2
*iface
, GUID
*group
)
3435 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
3437 FIXME("(%p)->(%p) - stub\n", This
, group
);
3442 static HRESULT WINAPI
AudioSessionControl_SetGroupingParam(
3443 IAudioSessionControl2
*iface
, const GUID
*group
, const GUID
*session
)
3445 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
3447 FIXME("(%p)->(%s, %s) - stub\n", This
, debugstr_guid(group
),
3448 debugstr_guid(session
));
3453 static HRESULT WINAPI
AudioSessionControl_RegisterAudioSessionNotification(
3454 IAudioSessionControl2
*iface
, IAudioSessionEvents
*events
)
3456 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
3458 FIXME("(%p)->(%p) - stub\n", This
, events
);
3463 static HRESULT WINAPI
AudioSessionControl_UnregisterAudioSessionNotification(
3464 IAudioSessionControl2
*iface
, IAudioSessionEvents
*events
)
3466 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
3468 FIXME("(%p)->(%p) - stub\n", This
, events
);
3473 static HRESULT WINAPI
AudioSessionControl_GetSessionIdentifier(
3474 IAudioSessionControl2
*iface
, WCHAR
**id
)
3476 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
3478 FIXME("(%p)->(%p) - stub\n", This
, id
);
3483 static HRESULT WINAPI
AudioSessionControl_GetSessionInstanceIdentifier(
3484 IAudioSessionControl2
*iface
, WCHAR
**id
)
3486 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
3488 FIXME("(%p)->(%p) - stub\n", This
, id
);
3493 static HRESULT WINAPI
AudioSessionControl_GetProcessId(
3494 IAudioSessionControl2
*iface
, DWORD
*pid
)
3496 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
3498 TRACE("(%p)->(%p)\n", This
, pid
);
3503 *pid
= GetCurrentProcessId();
3508 static HRESULT WINAPI
AudioSessionControl_IsSystemSoundsSession(
3509 IAudioSessionControl2
*iface
)
3511 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
3513 TRACE("(%p)\n", This
);
3518 static HRESULT WINAPI
AudioSessionControl_SetDuckingPreference(
3519 IAudioSessionControl2
*iface
, BOOL optout
)
3521 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
3523 TRACE("(%p)->(%d)\n", This
, optout
);
3528 static const IAudioSessionControl2Vtbl AudioSessionControl2_Vtbl
=
3530 AudioSessionControl_QueryInterface
,
3531 AudioSessionControl_AddRef
,
3532 AudioSessionControl_Release
,
3533 AudioSessionControl_GetState
,
3534 AudioSessionControl_GetDisplayName
,
3535 AudioSessionControl_SetDisplayName
,
3536 AudioSessionControl_GetIconPath
,
3537 AudioSessionControl_SetIconPath
,
3538 AudioSessionControl_GetGroupingParam
,
3539 AudioSessionControl_SetGroupingParam
,
3540 AudioSessionControl_RegisterAudioSessionNotification
,
3541 AudioSessionControl_UnregisterAudioSessionNotification
,
3542 AudioSessionControl_GetSessionIdentifier
,
3543 AudioSessionControl_GetSessionInstanceIdentifier
,
3544 AudioSessionControl_GetProcessId
,
3545 AudioSessionControl_IsSystemSoundsSession
,
3546 AudioSessionControl_SetDuckingPreference
3549 static HRESULT WINAPI
SimpleAudioVolume_QueryInterface(
3550 ISimpleAudioVolume
*iface
, REFIID riid
, void **ppv
)
3552 TRACE("(%p)->(%s, %p)\n", iface
, debugstr_guid(riid
), ppv
);
3558 if(IsEqualIID(riid
, &IID_IUnknown
) ||
3559 IsEqualIID(riid
, &IID_ISimpleAudioVolume
))
3562 IUnknown_AddRef((IUnknown
*)*ppv
);
3566 WARN("Unknown interface %s\n", debugstr_guid(riid
));
3567 return E_NOINTERFACE
;
3570 static ULONG WINAPI
SimpleAudioVolume_AddRef(ISimpleAudioVolume
*iface
)
3572 AudioSessionWrapper
*This
= impl_from_ISimpleAudioVolume(iface
);
3573 return AudioSessionControl_AddRef(&This
->IAudioSessionControl2_iface
);
3576 static ULONG WINAPI
SimpleAudioVolume_Release(ISimpleAudioVolume
*iface
)
3578 AudioSessionWrapper
*This
= impl_from_ISimpleAudioVolume(iface
);
3579 return AudioSessionControl_Release(&This
->IAudioSessionControl2_iface
);
3582 static HRESULT WINAPI
SimpleAudioVolume_SetMasterVolume(
3583 ISimpleAudioVolume
*iface
, float level
, const GUID
*context
)
3585 AudioSessionWrapper
*This
= impl_from_ISimpleAudioVolume(iface
);
3586 AudioSession
*session
= This
->session
;
3588 TRACE("(%p)->(%f, %s)\n", session
, level
, wine_dbgstr_guid(context
));
3590 if(level
< 0.f
|| level
> 1.f
)
3591 return E_INVALIDARG
;
3594 FIXME("Notifications not supported yet\n");
3596 TRACE("ALSA does not support volume control\n");
3598 EnterCriticalSection(&session
->lock
);
3600 session
->master_vol
= level
;
3602 LeaveCriticalSection(&session
->lock
);
3607 static HRESULT WINAPI
SimpleAudioVolume_GetMasterVolume(
3608 ISimpleAudioVolume
*iface
, float *level
)
3610 AudioSessionWrapper
*This
= impl_from_ISimpleAudioVolume(iface
);
3611 AudioSession
*session
= This
->session
;
3613 TRACE("(%p)->(%p)\n", session
, level
);
3616 return NULL_PTR_ERR
;
3618 *level
= session
->master_vol
;
3623 static HRESULT WINAPI
SimpleAudioVolume_SetMute(ISimpleAudioVolume
*iface
,
3624 BOOL mute
, const GUID
*context
)
3626 AudioSessionWrapper
*This
= impl_from_ISimpleAudioVolume(iface
);
3627 AudioSession
*session
= This
->session
;
3629 TRACE("(%p)->(%u, %s)\n", session
, mute
, debugstr_guid(context
));
3632 FIXME("Notifications not supported yet\n");
3634 session
->mute
= mute
;
3639 static HRESULT WINAPI
SimpleAudioVolume_GetMute(ISimpleAudioVolume
*iface
,
3642 AudioSessionWrapper
*This
= impl_from_ISimpleAudioVolume(iface
);
3643 AudioSession
*session
= This
->session
;
3645 TRACE("(%p)->(%p)\n", session
, mute
);
3648 return NULL_PTR_ERR
;
3650 *mute
= session
->mute
;
3655 static const ISimpleAudioVolumeVtbl SimpleAudioVolume_Vtbl
=
3657 SimpleAudioVolume_QueryInterface
,
3658 SimpleAudioVolume_AddRef
,
3659 SimpleAudioVolume_Release
,
3660 SimpleAudioVolume_SetMasterVolume
,
3661 SimpleAudioVolume_GetMasterVolume
,
3662 SimpleAudioVolume_SetMute
,
3663 SimpleAudioVolume_GetMute
3666 static HRESULT WINAPI
AudioStreamVolume_QueryInterface(
3667 IAudioStreamVolume
*iface
, REFIID riid
, void **ppv
)
3669 TRACE("(%p)->(%s, %p)\n", iface
, debugstr_guid(riid
), ppv
);
3675 if(IsEqualIID(riid
, &IID_IUnknown
) ||
3676 IsEqualIID(riid
, &IID_IAudioStreamVolume
))
3679 IUnknown_AddRef((IUnknown
*)*ppv
);
3683 WARN("Unknown interface %s\n", debugstr_guid(riid
));
3684 return E_NOINTERFACE
;
3687 static ULONG WINAPI
AudioStreamVolume_AddRef(IAudioStreamVolume
*iface
)
3689 ACImpl
*This
= impl_from_IAudioStreamVolume(iface
);
3690 return IAudioClient3_AddRef(&This
->IAudioClient3_iface
);
3693 static ULONG WINAPI
AudioStreamVolume_Release(IAudioStreamVolume
*iface
)
3695 ACImpl
*This
= impl_from_IAudioStreamVolume(iface
);
3696 return IAudioClient3_Release(&This
->IAudioClient3_iface
);
3699 static HRESULT WINAPI
AudioStreamVolume_GetChannelCount(
3700 IAudioStreamVolume
*iface
, UINT32
*out
)
3702 ACImpl
*This
= impl_from_IAudioStreamVolume(iface
);
3704 TRACE("(%p)->(%p)\n", This
, out
);
3709 *out
= This
->fmt
->nChannels
;
3714 static HRESULT WINAPI
AudioStreamVolume_SetChannelVolume(
3715 IAudioStreamVolume
*iface
, UINT32 index
, float level
)
3717 ACImpl
*This
= impl_from_IAudioStreamVolume(iface
);
3719 TRACE("(%p)->(%d, %f)\n", This
, index
, level
);
3721 if(level
< 0.f
|| level
> 1.f
)
3722 return E_INVALIDARG
;
3724 if(index
>= This
->fmt
->nChannels
)
3725 return E_INVALIDARG
;
3727 TRACE("ALSA does not support volume control\n");
3729 EnterCriticalSection(&This
->lock
);
3731 This
->vols
[index
] = level
;
3733 LeaveCriticalSection(&This
->lock
);
3738 static HRESULT WINAPI
AudioStreamVolume_GetChannelVolume(
3739 IAudioStreamVolume
*iface
, UINT32 index
, float *level
)
3741 ACImpl
*This
= impl_from_IAudioStreamVolume(iface
);
3743 TRACE("(%p)->(%d, %p)\n", This
, index
, level
);
3748 if(index
>= This
->fmt
->nChannels
)
3749 return E_INVALIDARG
;
3751 *level
= This
->vols
[index
];
3756 static HRESULT WINAPI
AudioStreamVolume_SetAllVolumes(
3757 IAudioStreamVolume
*iface
, UINT32 count
, const float *levels
)
3759 ACImpl
*This
= impl_from_IAudioStreamVolume(iface
);
3762 TRACE("(%p)->(%d, %p)\n", This
, count
, levels
);
3767 if(count
!= This
->fmt
->nChannels
)
3768 return E_INVALIDARG
;
3770 TRACE("ALSA does not support volume control\n");
3772 EnterCriticalSection(&This
->lock
);
3774 for(i
= 0; i
< count
; ++i
)
3775 This
->vols
[i
] = levels
[i
];
3777 LeaveCriticalSection(&This
->lock
);
3782 static HRESULT WINAPI
AudioStreamVolume_GetAllVolumes(
3783 IAudioStreamVolume
*iface
, UINT32 count
, float *levels
)
3785 ACImpl
*This
= impl_from_IAudioStreamVolume(iface
);
3788 TRACE("(%p)->(%d, %p)\n", This
, count
, levels
);
3793 if(count
!= This
->fmt
->nChannels
)
3794 return E_INVALIDARG
;
3796 EnterCriticalSection(&This
->lock
);
3798 for(i
= 0; i
< count
; ++i
)
3799 levels
[i
] = This
->vols
[i
];
3801 LeaveCriticalSection(&This
->lock
);
3806 static const IAudioStreamVolumeVtbl AudioStreamVolume_Vtbl
=
3808 AudioStreamVolume_QueryInterface
,
3809 AudioStreamVolume_AddRef
,
3810 AudioStreamVolume_Release
,
3811 AudioStreamVolume_GetChannelCount
,
3812 AudioStreamVolume_SetChannelVolume
,
3813 AudioStreamVolume_GetChannelVolume
,
3814 AudioStreamVolume_SetAllVolumes
,
3815 AudioStreamVolume_GetAllVolumes
3818 static HRESULT WINAPI
ChannelAudioVolume_QueryInterface(
3819 IChannelAudioVolume
*iface
, REFIID riid
, void **ppv
)
3821 TRACE("(%p)->(%s, %p)\n", iface
, debugstr_guid(riid
), ppv
);
3827 if(IsEqualIID(riid
, &IID_IUnknown
) ||
3828 IsEqualIID(riid
, &IID_IChannelAudioVolume
))
3831 IUnknown_AddRef((IUnknown
*)*ppv
);
3835 WARN("Unknown interface %s\n", debugstr_guid(riid
));
3836 return E_NOINTERFACE
;
3839 static ULONG WINAPI
ChannelAudioVolume_AddRef(IChannelAudioVolume
*iface
)
3841 AudioSessionWrapper
*This
= impl_from_IChannelAudioVolume(iface
);
3842 return AudioSessionControl_AddRef(&This
->IAudioSessionControl2_iface
);
3845 static ULONG WINAPI
ChannelAudioVolume_Release(IChannelAudioVolume
*iface
)
3847 AudioSessionWrapper
*This
= impl_from_IChannelAudioVolume(iface
);
3848 return AudioSessionControl_Release(&This
->IAudioSessionControl2_iface
);
3851 static HRESULT WINAPI
ChannelAudioVolume_GetChannelCount(
3852 IChannelAudioVolume
*iface
, UINT32
*out
)
3854 AudioSessionWrapper
*This
= impl_from_IChannelAudioVolume(iface
);
3855 AudioSession
*session
= This
->session
;
3857 TRACE("(%p)->(%p)\n", session
, out
);
3860 return NULL_PTR_ERR
;
3862 *out
= session
->channel_count
;
3867 static HRESULT WINAPI
ChannelAudioVolume_SetChannelVolume(
3868 IChannelAudioVolume
*iface
, UINT32 index
, float level
,
3869 const GUID
*context
)
3871 AudioSessionWrapper
*This
= impl_from_IChannelAudioVolume(iface
);
3872 AudioSession
*session
= This
->session
;
3874 TRACE("(%p)->(%d, %f, %s)\n", session
, index
, level
,
3875 wine_dbgstr_guid(context
));
3877 if(level
< 0.f
|| level
> 1.f
)
3878 return E_INVALIDARG
;
3880 if(index
>= session
->channel_count
)
3881 return E_INVALIDARG
;
3884 FIXME("Notifications not supported yet\n");
3886 TRACE("ALSA does not support volume control\n");
3888 EnterCriticalSection(&session
->lock
);
3890 session
->channel_vols
[index
] = level
;
3892 LeaveCriticalSection(&session
->lock
);
3897 static HRESULT WINAPI
ChannelAudioVolume_GetChannelVolume(
3898 IChannelAudioVolume
*iface
, UINT32 index
, float *level
)
3900 AudioSessionWrapper
*This
= impl_from_IChannelAudioVolume(iface
);
3901 AudioSession
*session
= This
->session
;
3903 TRACE("(%p)->(%d, %p)\n", session
, index
, level
);
3906 return NULL_PTR_ERR
;
3908 if(index
>= session
->channel_count
)
3909 return E_INVALIDARG
;
3911 *level
= session
->channel_vols
[index
];
3916 static HRESULT WINAPI
ChannelAudioVolume_SetAllVolumes(
3917 IChannelAudioVolume
*iface
, UINT32 count
, const float *levels
,
3918 const GUID
*context
)
3920 AudioSessionWrapper
*This
= impl_from_IChannelAudioVolume(iface
);
3921 AudioSession
*session
= This
->session
;
3924 TRACE("(%p)->(%d, %p, %s)\n", session
, count
, levels
,
3925 wine_dbgstr_guid(context
));
3928 return NULL_PTR_ERR
;
3930 if(count
!= session
->channel_count
)
3931 return E_INVALIDARG
;
3934 FIXME("Notifications not supported yet\n");
3936 TRACE("ALSA does not support volume control\n");
3938 EnterCriticalSection(&session
->lock
);
3940 for(i
= 0; i
< count
; ++i
)
3941 session
->channel_vols
[i
] = levels
[i
];
3943 LeaveCriticalSection(&session
->lock
);
3948 static HRESULT WINAPI
ChannelAudioVolume_GetAllVolumes(
3949 IChannelAudioVolume
*iface
, UINT32 count
, float *levels
)
3951 AudioSessionWrapper
*This
= impl_from_IChannelAudioVolume(iface
);
3952 AudioSession
*session
= This
->session
;
3955 TRACE("(%p)->(%d, %p)\n", session
, count
, levels
);
3958 return NULL_PTR_ERR
;
3960 if(count
!= session
->channel_count
)
3961 return E_INVALIDARG
;
3963 for(i
= 0; i
< count
; ++i
)
3964 levels
[i
] = session
->channel_vols
[i
];
3969 static const IChannelAudioVolumeVtbl ChannelAudioVolume_Vtbl
=
3971 ChannelAudioVolume_QueryInterface
,
3972 ChannelAudioVolume_AddRef
,
3973 ChannelAudioVolume_Release
,
3974 ChannelAudioVolume_GetChannelCount
,
3975 ChannelAudioVolume_SetChannelVolume
,
3976 ChannelAudioVolume_GetChannelVolume
,
3977 ChannelAudioVolume_SetAllVolumes
,
3978 ChannelAudioVolume_GetAllVolumes
3981 static HRESULT WINAPI
AudioSessionManager_QueryInterface(IAudioSessionManager2
*iface
,
3982 REFIID riid
, void **ppv
)
3984 TRACE("(%p)->(%s, %p)\n", iface
, debugstr_guid(riid
), ppv
);
3990 if(IsEqualIID(riid
, &IID_IUnknown
) ||
3991 IsEqualIID(riid
, &IID_IAudioSessionManager
) ||
3992 IsEqualIID(riid
, &IID_IAudioSessionManager2
))
3995 IUnknown_AddRef((IUnknown
*)*ppv
);
3999 WARN("Unknown interface %s\n", debugstr_guid(riid
));
4000 return E_NOINTERFACE
;
4003 static ULONG WINAPI
AudioSessionManager_AddRef(IAudioSessionManager2
*iface
)
4005 SessionMgr
*This
= impl_from_IAudioSessionManager2(iface
);
4007 ref
= InterlockedIncrement(&This
->ref
);
4008 TRACE("(%p) Refcount now %u\n", This
, ref
);
4012 static ULONG WINAPI
AudioSessionManager_Release(IAudioSessionManager2
*iface
)
4014 SessionMgr
*This
= impl_from_IAudioSessionManager2(iface
);
4016 ref
= InterlockedDecrement(&This
->ref
);
4017 TRACE("(%p) Refcount now %u\n", This
, ref
);
4019 HeapFree(GetProcessHeap(), 0, This
);
4023 static HRESULT WINAPI
AudioSessionManager_GetAudioSessionControl(
4024 IAudioSessionManager2
*iface
, const GUID
*session_guid
, DWORD flags
,
4025 IAudioSessionControl
**out
)
4027 SessionMgr
*This
= impl_from_IAudioSessionManager2(iface
);
4028 AudioSession
*session
;
4029 AudioSessionWrapper
*wrapper
;
4032 TRACE("(%p)->(%s, %x, %p)\n", This
, debugstr_guid(session_guid
),
4035 hr
= get_audio_session(session_guid
, This
->device
, 0, &session
);
4039 wrapper
= AudioSessionWrapper_Create(NULL
);
4041 return E_OUTOFMEMORY
;
4043 wrapper
->session
= session
;
4045 *out
= (IAudioSessionControl
*)&wrapper
->IAudioSessionControl2_iface
;
4050 static HRESULT WINAPI
AudioSessionManager_GetSimpleAudioVolume(
4051 IAudioSessionManager2
*iface
, const GUID
*session_guid
, DWORD flags
,
4052 ISimpleAudioVolume
**out
)
4054 SessionMgr
*This
= impl_from_IAudioSessionManager2(iface
);
4055 AudioSession
*session
;
4056 AudioSessionWrapper
*wrapper
;
4059 TRACE("(%p)->(%s, %x, %p)\n", This
, debugstr_guid(session_guid
),
4062 hr
= get_audio_session(session_guid
, This
->device
, 0, &session
);
4066 wrapper
= AudioSessionWrapper_Create(NULL
);
4068 return E_OUTOFMEMORY
;
4070 wrapper
->session
= session
;
4072 *out
= &wrapper
->ISimpleAudioVolume_iface
;
4077 static HRESULT WINAPI
AudioSessionManager_GetSessionEnumerator(
4078 IAudioSessionManager2
*iface
, IAudioSessionEnumerator
**out
)
4080 SessionMgr
*This
= impl_from_IAudioSessionManager2(iface
);
4081 FIXME("(%p)->(%p) - stub\n", This
, out
);
4085 static HRESULT WINAPI
AudioSessionManager_RegisterSessionNotification(
4086 IAudioSessionManager2
*iface
, IAudioSessionNotification
*notification
)
4088 SessionMgr
*This
= impl_from_IAudioSessionManager2(iface
);
4089 FIXME("(%p)->(%p) - stub\n", This
, notification
);
4093 static HRESULT WINAPI
AudioSessionManager_UnregisterSessionNotification(
4094 IAudioSessionManager2
*iface
, IAudioSessionNotification
*notification
)
4096 SessionMgr
*This
= impl_from_IAudioSessionManager2(iface
);
4097 FIXME("(%p)->(%p) - stub\n", This
, notification
);
4101 static HRESULT WINAPI
AudioSessionManager_RegisterDuckNotification(
4102 IAudioSessionManager2
*iface
, const WCHAR
*session_id
,
4103 IAudioVolumeDuckNotification
*notification
)
4105 SessionMgr
*This
= impl_from_IAudioSessionManager2(iface
);
4106 FIXME("(%p)->(%p) - stub\n", This
, notification
);
4110 static HRESULT WINAPI
AudioSessionManager_UnregisterDuckNotification(
4111 IAudioSessionManager2
*iface
,
4112 IAudioVolumeDuckNotification
*notification
)
4114 SessionMgr
*This
= impl_from_IAudioSessionManager2(iface
);
4115 FIXME("(%p)->(%p) - stub\n", This
, notification
);
4119 static const IAudioSessionManager2Vtbl AudioSessionManager2_Vtbl
=
4121 AudioSessionManager_QueryInterface
,
4122 AudioSessionManager_AddRef
,
4123 AudioSessionManager_Release
,
4124 AudioSessionManager_GetAudioSessionControl
,
4125 AudioSessionManager_GetSimpleAudioVolume
,
4126 AudioSessionManager_GetSessionEnumerator
,
4127 AudioSessionManager_RegisterSessionNotification
,
4128 AudioSessionManager_UnregisterSessionNotification
,
4129 AudioSessionManager_RegisterDuckNotification
,
4130 AudioSessionManager_UnregisterDuckNotification
4133 HRESULT WINAPI
AUDDRV_GetAudioSessionManager(IMMDevice
*device
,
4134 IAudioSessionManager2
**out
)
4138 This
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(SessionMgr
));
4140 return E_OUTOFMEMORY
;
4142 This
->IAudioSessionManager2_iface
.lpVtbl
= &AudioSessionManager2_Vtbl
;
4143 This
->device
= device
;
4146 *out
= &This
->IAudioSessionManager2_iface
;
4151 static unsigned int alsa_probe_num_speakers(char *name
) {
4153 snd_pcm_hw_params_t
*params
;
4155 unsigned int max_channels
= 0;
4157 if ((err
= snd_pcm_open(&handle
, name
, SND_PCM_STREAM_PLAYBACK
, SND_PCM_NONBLOCK
)) < 0) {
4158 WARN("The device \"%s\" failed to open: %d (%s).\n",
4159 name
, err
, snd_strerror(err
));
4163 params
= HeapAlloc(GetProcessHeap(), 0, snd_pcm_hw_params_sizeof());
4165 WARN("Out of memory.\n");
4166 snd_pcm_close(handle
);
4170 if ((err
= snd_pcm_hw_params_any(handle
, params
)) < 0) {
4171 WARN("snd_pcm_hw_params_any failed for \"%s\": %d (%s).\n",
4172 name
, err
, snd_strerror(err
));
4176 if ((err
= snd_pcm_hw_params_get_channels_max(params
,
4177 &max_channels
)) < 0){
4178 WARN("Unable to get max channels: %d (%s)\n", err
, snd_strerror(err
));
4183 HeapFree(GetProcessHeap(), 0, params
);
4184 snd_pcm_close(handle
);
4186 return max_channels
;
4189 enum AudioDeviceConnectionType
{
4190 AudioDeviceConnectionType_Unknown
= 0,
4191 AudioDeviceConnectionType_PCI
,
4192 AudioDeviceConnectionType_USB
4195 HRESULT WINAPI
AUDDRV_GetPropValue(GUID
*guid
, const PROPERTYKEY
*prop
, PROPVARIANT
*out
)
4200 static const PROPERTYKEY devicepath_key
= { /* undocumented? - {b3f8fa53-0004-438e-9003-51a46e139bfc},2 */
4201 {0xb3f8fa53, 0x0004, 0x438e, {0x90, 0x03, 0x51, 0xa4, 0x6e, 0x13, 0x9b, 0xfc}}, 2
4204 TRACE("%s, (%s,%u), %p\n", wine_dbgstr_guid(guid
), wine_dbgstr_guid(&prop
->fmtid
), prop
->pid
, out
);
4206 if(!get_alsa_name_by_guid(guid
, name
, sizeof(name
), &flow
))
4208 WARN("Unknown interface %s\n", debugstr_guid(guid
));
4209 return E_NOINTERFACE
;
4212 if(IsEqualPropertyKey(*prop
, devicepath_key
))
4214 char uevent
[MAX_PATH
];
4218 /* only implemented for identifiable devices, i.e. not "default" */
4219 if(!sscanf(name
, "plughw:%u,%u", &card
, &device
))
4222 sprintf(uevent
, "/sys/class/sound/card%u/device/uevent", card
);
4223 fuevent
= fopen(uevent
, "r");
4226 enum AudioDeviceConnectionType connection
= AudioDeviceConnectionType_Unknown
;
4227 USHORT vendor_id
= 0, product_id
= 0;
4230 while (fgets(line
, sizeof(line
), fuevent
)) {
4234 if((val
= strchr(line
, '='))) {
4238 val_len
= strlen(val
);
4239 if(val_len
> 0 && val
[val_len
- 1] == '\n') { val
[val_len
- 1] = 0; }
4241 if(!strcmp(line
, "PCI_ID")){
4242 connection
= AudioDeviceConnectionType_PCI
;
4243 if(sscanf(val
, "%hX:%hX", &vendor_id
, &product_id
)<2){
4244 WARN("Unexpected input when reading PCI_ID in uevent file.\n");
4245 connection
= AudioDeviceConnectionType_Unknown
;
4248 }else if(!strcmp(line
, "DEVTYPE") && !strcmp(val
,"usb_interface"))
4249 connection
= AudioDeviceConnectionType_USB
;
4250 else if(!strcmp(line
, "PRODUCT"))
4251 if(sscanf(val
, "%hx/%hx/", &vendor_id
, &product_id
)<2){
4252 WARN("Unexpected input when reading PRODUCT in uevent file.\n");
4253 connection
= AudioDeviceConnectionType_Unknown
;
4261 if(connection
== AudioDeviceConnectionType_USB
|| connection
== AudioDeviceConnectionType_PCI
){
4262 static const WCHAR usbformatW
[] = { '{','1','}','.','U','S','B','\\','V','I','D','_',
4263 '%','0','4','X','&','P','I','D','_','%','0','4','X','\\',
4264 '%','u','&','%','0','8','X',0 }; /* "{1}.USB\VID_%04X&PID_%04X\%u&%08X" */
4265 static const WCHAR pciformatW
[] = { '{','1','}','.','H','D','A','U','D','I','O','\\','F','U','N','C','_','0','1','&',
4266 'V','E','N','_','%','0','4','X','&','D','E','V','_',
4267 '%','0','4','X','\\','%','u','&','%','0','8','X',0 }; /* "{1}.HDAUDIO\FUNC_01&VEN_%04X&DEV_%04X\%u&%08X" */
4270 /* As hardly any audio devices have serial numbers, Windows instead
4271 appears to use a persistent random number. We emulate this here
4272 by instead using the last 8 hex digits of the GUID. */
4273 serial_number
= (guid
->Data4
[4] << 24) | (guid
->Data4
[5] << 16) | (guid
->Data4
[6] << 8) | guid
->Data4
[7];
4275 out
->vt
= VT_LPWSTR
;
4276 out
->u
.pwszVal
= CoTaskMemAlloc(128 * sizeof(WCHAR
));
4279 return E_OUTOFMEMORY
;
4281 if(connection
== AudioDeviceConnectionType_USB
)
4282 sprintfW( out
->u
.pwszVal
, usbformatW
, vendor_id
, product_id
, device
, serial_number
);
4283 else if(connection
== AudioDeviceConnectionType_PCI
)
4284 sprintfW( out
->u
.pwszVal
, pciformatW
, vendor_id
, product_id
, device
, serial_number
);
4289 WARN("Could not open %s for reading\n", uevent
);
4292 } else if (flow
!= eCapture
&& IsEqualPropertyKey(*prop
, PKEY_AudioEndpoint_PhysicalSpeakers
)) {
4293 unsigned int num_speakers
, card
, device
;
4296 if (sscanf(name
, "plughw:%u,%u", &card
, &device
))
4297 sprintf(hwname
, "hw:%u,%u", card
, device
); /* must be hw rather than plughw to work */
4299 strcpy(hwname
, name
);
4301 num_speakers
= alsa_probe_num_speakers(hwname
);
4302 if (num_speakers
== 0)
4307 if (num_speakers
> 6)
4308 out
->u
.ulVal
= KSAUDIO_SPEAKER_STEREO
;
4309 else if (num_speakers
== 6)
4310 out
->u
.ulVal
= KSAUDIO_SPEAKER_5POINT1
;
4311 else if (num_speakers
>= 4)
4312 out
->u
.ulVal
= KSAUDIO_SPEAKER_QUAD
;
4313 else if (num_speakers
>= 2)
4314 out
->u
.ulVal
= KSAUDIO_SPEAKER_STEREO
;
4315 else if (num_speakers
== 1)
4316 out
->u
.ulVal
= KSAUDIO_SPEAKER_MONO
;
4321 TRACE("Unimplemented property %s,%u\n", wine_dbgstr_guid(&prop
->fmtid
), prop
->pid
);