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
33 #include "wine/debug.h"
34 #include "wine/unicode.h"
35 #include "wine/list.h"
41 #include "mmdeviceapi.h"
47 #include "endpointvolume.h"
48 #include "audioclient.h"
49 #include "audiopolicy.h"
51 #include <alsa/asoundlib.h>
53 WINE_DEFAULT_DEBUG_CHANNEL(alsa
);
54 WINE_DECLARE_DEBUG_CHANNEL(winediag
);
56 #define NULL_PTR_ERR MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, RPC_X_NULL_REF_POINTER)
58 static const REFERENCE_TIME DefaultPeriod
= 100000;
59 static const REFERENCE_TIME MinimumPeriod
= 50000;
60 #define EXTRA_SAFE_RT 40000
63 typedef struct ACImpl ACImpl
;
65 typedef struct _AudioSession
{
76 CRITICAL_SECTION lock
;
81 typedef struct _AudioSessionWrapper
{
82 IAudioSessionControl2 IAudioSessionControl2_iface
;
83 IChannelAudioVolume IChannelAudioVolume_iface
;
84 ISimpleAudioVolume ISimpleAudioVolume_iface
;
89 AudioSession
*session
;
90 } AudioSessionWrapper
;
93 IAudioClient3 IAudioClient3_iface
;
94 IAudioRenderClient IAudioRenderClient_iface
;
95 IAudioCaptureClient IAudioCaptureClient_iface
;
96 IAudioClock IAudioClock_iface
;
97 IAudioClock2 IAudioClock2_iface
;
98 IAudioStreamVolume IAudioStreamVolume_iface
;
102 snd_pcm_t
*pcm_handle
;
103 snd_pcm_uframes_t alsa_bufsize_frames
, alsa_period_frames
, safe_rewind_frames
;
104 snd_pcm_hw_params_t
*hw_params
; /* does not hold state between calls */
105 snd_pcm_format_t alsa_format
;
107 LARGE_INTEGER last_period_time
;
110 IUnknown
*pUnkFTMarshal
;
115 AUDCLNT_SHAREMODE share
;
121 int alsa_channel_map
[32];
123 BOOL initted
, started
;
124 REFERENCE_TIME mmdev_period_rt
;
125 UINT64 written_frames
, last_pos_frames
;
126 UINT32 bufsize_frames
, held_frames
, tmp_buffer_frames
, mmdev_period_frames
;
127 snd_pcm_uframes_t remapping_buf_frames
;
128 UINT32 lcl_offs_frames
; /* offs into local_buffer where valid data starts */
129 UINT32 wri_offs_frames
; /* where to write fresh data in local_buffer */
130 UINT32 hidden_frames
; /* ALSA reserve to ensure continuous rendering */
131 UINT32 vol_adjusted_frames
; /* Frames we've already adjusted the volume of but didn't write yet */
132 UINT32 data_in_alsa_frames
;
135 BYTE
*local_buffer
, *tmp_buffer
, *remapping_buf
, *silence_buf
;
136 LONG32 getbuf_last
; /* <0 when using tmp_buffer */
138 CRITICAL_SECTION lock
;
140 AudioSession
*session
;
141 AudioSessionWrapper
*session_wrapper
;
146 typedef struct _SessionMgr
{
147 IAudioSessionManager2 IAudioSessionManager2_iface
;
154 static HANDLE g_timer_q
;
156 static CRITICAL_SECTION g_sessions_lock
;
157 static CRITICAL_SECTION_DEBUG g_sessions_lock_debug
=
159 0, 0, &g_sessions_lock
,
160 { &g_sessions_lock_debug
.ProcessLocksList
, &g_sessions_lock_debug
.ProcessLocksList
},
161 0, 0, { (DWORD_PTR
)(__FILE__
": g_sessions_lock") }
163 static CRITICAL_SECTION g_sessions_lock
= { &g_sessions_lock_debug
, -1, 0, 0, 0, 0 };
164 static struct list g_sessions
= LIST_INIT(g_sessions
);
166 static const WCHAR defaultW
[] = {'d','e','f','a','u','l','t',0};
167 static const char defname
[] = "default";
169 static const WCHAR drv_keyW
[] = {'S','o','f','t','w','a','r','e','\\',
170 'W','i','n','e','\\','D','r','i','v','e','r','s','\\',
171 'w','i','n','e','a','l','s','a','.','d','r','v',0};
172 static const WCHAR drv_key_devicesW
[] = {'S','o','f','t','w','a','r','e','\\',
173 'W','i','n','e','\\','D','r','i','v','e','r','s','\\',
174 'w','i','n','e','a','l','s','a','.','d','r','v','\\','d','e','v','i','c','e','s',0};
175 static const WCHAR guidW
[] = {'g','u','i','d',0};
177 static const IAudioClient3Vtbl AudioClient3_Vtbl
;
178 static const IAudioRenderClientVtbl AudioRenderClient_Vtbl
;
179 static const IAudioCaptureClientVtbl AudioCaptureClient_Vtbl
;
180 static const IAudioSessionControl2Vtbl AudioSessionControl2_Vtbl
;
181 static const ISimpleAudioVolumeVtbl SimpleAudioVolume_Vtbl
;
182 static const IAudioClockVtbl AudioClock_Vtbl
;
183 static const IAudioClock2Vtbl AudioClock2_Vtbl
;
184 static const IAudioStreamVolumeVtbl AudioStreamVolume_Vtbl
;
185 static const IChannelAudioVolumeVtbl ChannelAudioVolume_Vtbl
;
186 static const IAudioSessionManager2Vtbl AudioSessionManager2_Vtbl
;
188 static AudioSessionWrapper
*AudioSessionWrapper_Create(ACImpl
*client
);
190 static inline ACImpl
*impl_from_IAudioClient3(IAudioClient3
*iface
)
192 return CONTAINING_RECORD(iface
, ACImpl
, IAudioClient3_iface
);
195 static inline ACImpl
*impl_from_IAudioRenderClient(IAudioRenderClient
*iface
)
197 return CONTAINING_RECORD(iface
, ACImpl
, IAudioRenderClient_iface
);
200 static inline ACImpl
*impl_from_IAudioCaptureClient(IAudioCaptureClient
*iface
)
202 return CONTAINING_RECORD(iface
, ACImpl
, IAudioCaptureClient_iface
);
205 static inline AudioSessionWrapper
*impl_from_IAudioSessionControl2(IAudioSessionControl2
*iface
)
207 return CONTAINING_RECORD(iface
, AudioSessionWrapper
, IAudioSessionControl2_iface
);
210 static inline AudioSessionWrapper
*impl_from_ISimpleAudioVolume(ISimpleAudioVolume
*iface
)
212 return CONTAINING_RECORD(iface
, AudioSessionWrapper
, ISimpleAudioVolume_iface
);
215 static inline AudioSessionWrapper
*impl_from_IChannelAudioVolume(IChannelAudioVolume
*iface
)
217 return CONTAINING_RECORD(iface
, AudioSessionWrapper
, IChannelAudioVolume_iface
);
220 static inline ACImpl
*impl_from_IAudioClock(IAudioClock
*iface
)
222 return CONTAINING_RECORD(iface
, ACImpl
, IAudioClock_iface
);
225 static inline ACImpl
*impl_from_IAudioClock2(IAudioClock2
*iface
)
227 return CONTAINING_RECORD(iface
, ACImpl
, IAudioClock2_iface
);
230 static inline ACImpl
*impl_from_IAudioStreamVolume(IAudioStreamVolume
*iface
)
232 return CONTAINING_RECORD(iface
, ACImpl
, IAudioStreamVolume_iface
);
235 static inline SessionMgr
*impl_from_IAudioSessionManager2(IAudioSessionManager2
*iface
)
237 return CONTAINING_RECORD(iface
, SessionMgr
, IAudioSessionManager2_iface
);
240 BOOL WINAPI
DllMain(HINSTANCE dll
, DWORD reason
, void *reserved
)
244 case DLL_PROCESS_ATTACH
:
245 g_timer_q
= CreateTimerQueue();
250 case DLL_PROCESS_DETACH
:
252 DeleteCriticalSection(&g_sessions_lock
);
258 /* From <dlls/mmdevapi/mmdevapi.h> */
259 enum DriverPriority
{
260 Priority_Unavailable
= 0,
266 int WINAPI
AUDDRV_GetPriority(void)
268 return Priority_Neutral
;
271 static void set_device_guid(EDataFlow flow
, HKEY drv_key
, const WCHAR
*key_name
,
279 lr
= RegCreateKeyExW(HKEY_CURRENT_USER
, drv_key_devicesW
, 0, NULL
, 0, KEY_WRITE
,
280 NULL
, &drv_key
, NULL
);
281 if(lr
!= ERROR_SUCCESS
){
282 ERR("RegCreateKeyEx(drv_key) failed: %u\n", lr
);
288 lr
= RegCreateKeyExW(drv_key
, key_name
, 0, NULL
, 0, KEY_WRITE
,
290 if(lr
!= ERROR_SUCCESS
){
291 ERR("RegCreateKeyEx(%s) failed: %u\n", wine_dbgstr_w(key_name
), lr
);
295 lr
= RegSetValueExW(key
, guidW
, 0, REG_BINARY
, (BYTE
*)guid
,
297 if(lr
!= ERROR_SUCCESS
)
298 ERR("RegSetValueEx(%s\\guid) failed: %u\n", wine_dbgstr_w(key_name
), lr
);
303 RegCloseKey(drv_key
);
306 static void get_device_guid(EDataFlow flow
, const char *device
, GUID
*guid
)
308 HKEY key
= NULL
, dev_key
;
309 DWORD type
, size
= sizeof(*guid
);
317 MultiByteToWideChar(CP_UNIXCP
, 0, device
, -1, key_name
+ 2, ARRAY_SIZE(key_name
) - 2);
319 if(RegOpenKeyExW(HKEY_CURRENT_USER
, drv_key_devicesW
, 0, KEY_WRITE
|KEY_READ
, &key
) == ERROR_SUCCESS
){
320 if(RegOpenKeyExW(key
, key_name
, 0, KEY_READ
, &dev_key
) == ERROR_SUCCESS
){
321 if(RegQueryValueExW(dev_key
, guidW
, 0, &type
,
322 (BYTE
*)guid
, &size
) == ERROR_SUCCESS
){
323 if(type
== REG_BINARY
){
324 RegCloseKey(dev_key
);
328 ERR("Invalid type for device %s GUID: %u; ignoring and overwriting\n",
329 wine_dbgstr_w(key_name
), type
);
331 RegCloseKey(dev_key
);
337 set_device_guid(flow
, key
, key_name
, guid
);
343 static BOOL
alsa_try_open(const char *devnode
, snd_pcm_stream_t stream
)
348 TRACE("devnode: %s, stream: %d\n", devnode
, stream
);
350 if((err
= snd_pcm_open(&handle
, devnode
, stream
, SND_PCM_NONBLOCK
)) < 0){
351 WARN("The device \"%s\" failed to open: %d (%s).\n",
352 devnode
, err
, snd_strerror(err
));
356 snd_pcm_close(handle
);
360 static WCHAR
*construct_device_id(EDataFlow flow
, const WCHAR
*chunk1
, const char *chunk2
)
364 DWORD len_wchars
= 0, chunk1_len
= 0, copied
= 0, prefix_len
;
366 static const WCHAR dashW
[] = {' ','-',' ',0};
367 static const size_t dashW_len
= ARRAY_SIZE(dashW
) - 1;
368 static const WCHAR outW
[] = {'O','u','t',':',' ',0};
369 static const WCHAR inW
[] = {'I','n',':',' ',0};
373 prefix_len
= ARRAY_SIZE(outW
) - 1;
374 len_wchars
+= prefix_len
;
377 prefix_len
= ARRAY_SIZE(inW
) - 1;
378 len_wchars
+= prefix_len
;
381 chunk1_len
= strlenW(chunk1
);
382 len_wchars
+= chunk1_len
;
385 len_wchars
+= dashW_len
;
387 len_wchars
+= MultiByteToWideChar(CP_UNIXCP
, 0, chunk2
, -1, NULL
, 0) - 1;
388 len_wchars
+= 1; /* NULL byte */
390 ret
= HeapAlloc(GetProcessHeap(), 0, len_wchars
* sizeof(WCHAR
));
392 memcpy(ret
, prefix
, prefix_len
* sizeof(WCHAR
));
393 copied
+= prefix_len
;
395 memcpy(ret
+ copied
, chunk1
, chunk1_len
* sizeof(WCHAR
));
396 copied
+= chunk1_len
;
398 if(chunk1
&& chunk2
){
399 memcpy(ret
+ copied
, dashW
, dashW_len
* sizeof(WCHAR
));
403 MultiByteToWideChar(CP_UNIXCP
, 0, chunk2
, -1, ret
+ copied
, len_wchars
- copied
);
407 TRACE("Enumerated device: %s\n", wine_dbgstr_w(ret
));
412 static HRESULT
alsa_get_card_devices(EDataFlow flow
, snd_pcm_stream_t stream
,
413 WCHAR
***ids
, GUID
**guids
, UINT
*num
, snd_ctl_t
*ctl
, int card
,
414 const WCHAR
*cardnameW
)
417 snd_pcm_info_t
*info
;
419 info
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, snd_pcm_info_sizeof());
421 return E_OUTOFMEMORY
;
423 snd_pcm_info_set_subdevice(info
, 0);
424 snd_pcm_info_set_stream(info
, stream
);
427 for(err
= snd_ctl_pcm_next_device(ctl
, &device
); device
!= -1 && err
>= 0;
428 err
= snd_ctl_pcm_next_device(ctl
, &device
)){
432 snd_pcm_info_set_device(info
, device
);
434 if((err
= snd_ctl_pcm_info(ctl
, info
)) < 0){
436 /* This device doesn't have the right stream direction */
439 WARN("Failed to get info for card %d, device %d: %d (%s)\n",
440 card
, device
, err
, snd_strerror(err
));
444 sprintf(devnode
, "plughw:%d,%d", card
, device
);
445 if(!alsa_try_open(devnode
, stream
))
449 *ids
= HeapReAlloc(GetProcessHeap(), 0, *ids
, sizeof(WCHAR
*) * (*num
+ 1));
450 *guids
= HeapReAlloc(GetProcessHeap(), 0, *guids
, sizeof(GUID
) * (*num
+ 1));
452 *ids
= HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR
*));
453 *guids
= HeapAlloc(GetProcessHeap(), 0, sizeof(GUID
));
456 devname
= snd_pcm_info_get_name(info
);
458 WARN("Unable to get device name for card %d, device %d\n", card
,
463 (*ids
)[*num
] = construct_device_id(flow
, cardnameW
, devname
);
464 get_device_guid(flow
, devnode
, &(*guids
)[*num
]);
469 HeapFree(GetProcessHeap(), 0, info
);
472 WARN("Got a failure during device enumeration on card %d: %d (%s)\n",
473 card
, err
, snd_strerror(err
));
478 static void get_reg_devices(EDataFlow flow
, snd_pcm_stream_t stream
, WCHAR
***ids
,
479 GUID
**guids
, UINT
*num
)
481 static const WCHAR ALSAOutputDevices
[] = {'A','L','S','A','O','u','t','p','u','t','D','e','v','i','c','e','s',0};
482 static const WCHAR ALSAInputDevices
[] = {'A','L','S','A','I','n','p','u','t','D','e','v','i','c','e','s',0};
484 WCHAR reg_devices
[256];
485 DWORD size
= sizeof(reg_devices
), type
;
486 const WCHAR
*value_name
= (stream
== SND_PCM_STREAM_PLAYBACK
) ? ALSAOutputDevices
: ALSAInputDevices
;
488 /* @@ Wine registry key: HKCU\Software\Wine\Drivers\winealsa.drv */
489 if(RegOpenKeyW(HKEY_CURRENT_USER
, drv_keyW
, &key
) == ERROR_SUCCESS
){
490 if(RegQueryValueExW(key
, value_name
, 0, &type
,
491 (BYTE
*)reg_devices
, &size
) == ERROR_SUCCESS
){
492 WCHAR
*p
= reg_devices
;
494 if(type
!= REG_MULTI_SZ
){
495 ERR("Registry ALSA device list value type must be REG_MULTI_SZ\n");
503 WideCharToMultiByte(CP_UNIXCP
, 0, p
, -1, devname
, sizeof(devname
), NULL
, NULL
);
505 if(alsa_try_open(devname
, stream
)){
507 *ids
= HeapReAlloc(GetProcessHeap(), 0, *ids
, sizeof(WCHAR
*) * (*num
+ 1));
508 *guids
= HeapReAlloc(GetProcessHeap(), 0, *guids
, sizeof(GUID
) * (*num
+ 1));
510 *ids
= HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR
*));
511 *guids
= HeapAlloc(GetProcessHeap(), 0, sizeof(GUID
));
513 (*ids
)[*num
] = construct_device_id(flow
, p
, NULL
);
514 get_device_guid(flow
, devname
, &(*guids
)[*num
]);
518 p
+= lstrlenW(p
) + 1;
528 int first_card_number
;
532 static struct list card_types
= LIST_INIT(card_types
);
534 static BOOL
need_card_number(int card
, const char *string
)
536 struct card_type
*cptr
;
538 LIST_FOR_EACH_ENTRY(cptr
, &card_types
, struct card_type
, entry
)
540 if(!strcmp(string
, cptr
->string
))
541 return card
!= cptr
->first_card_number
;
544 /* this is the first instance of string */
545 cptr
= HeapAlloc(GetProcessHeap(), 0, sizeof(struct card_type
) + strlen(string
));
547 /* Default to displaying card number if we can't track cards */
550 cptr
->first_card_number
= card
;
551 strcpy(cptr
->string
, string
);
552 list_add_head(&card_types
, &cptr
->entry
);
556 static HRESULT
alsa_enum_devices(EDataFlow flow
, WCHAR
***ids
, GUID
**guids
,
559 snd_pcm_stream_t stream
= (flow
== eRender
? SND_PCM_STREAM_PLAYBACK
:
560 SND_PCM_STREAM_CAPTURE
);
566 if(alsa_try_open(defname
, stream
)){
567 *ids
= HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR
*));
568 (*ids
)[0] = construct_device_id(flow
, defaultW
, NULL
);
569 *guids
= HeapAlloc(GetProcessHeap(), 0, sizeof(GUID
));
570 get_device_guid(flow
, defname
, &(*guids
)[0]);
574 get_reg_devices(flow
, stream
, ids
, guids
, num
);
576 for(err
= snd_card_next(&card
); card
!= -1 && err
>= 0;
577 err
= snd_card_next(&card
)){
584 sprintf(cardpath
, "hw:%u", card
);
586 if((err
= snd_ctl_open(&ctl
, cardpath
, 0)) < 0){
587 WARN("Unable to open ctl for ALSA device %s: %d (%s)\n", cardpath
,
588 err
, snd_strerror(err
));
592 if(snd_card_get_name(card
, &cardname
) < 0) {
593 /* FIXME: Should be localized */
594 static const WCHAR nameW
[] = {'U','n','k','n','o','w','n',' ','s','o','u','n','d','c','a','r','d',0};
595 WARN("Unable to get card name for ALSA device %s: %d (%s)\n",
596 cardpath
, err
, snd_strerror(err
));
597 alsa_get_card_devices(flow
, stream
, ids
, guids
, num
, ctl
, card
, nameW
);
599 if(need_card_number(card
, cardname
)){
602 * For identical card names, second and subsequent instances get
603 * card number prefix to distinguish them (like Windows).
605 if(asprintf(&cardnameN
, "%u-%s", card
, cardname
) > 0){
607 cardname
= cardnameN
;
610 len
= MultiByteToWideChar(CP_UNIXCP
, 0, cardname
, -1, NULL
, 0);
611 cardnameW
= HeapAlloc(GetProcessHeap(), 0, len
* sizeof(WCHAR
));
616 return E_OUTOFMEMORY
;
618 MultiByteToWideChar(CP_UNIXCP
, 0, cardname
, -1, cardnameW
, len
);
620 alsa_get_card_devices(flow
, stream
, ids
, guids
, num
, ctl
, card
, cardnameW
);
622 HeapFree(GetProcessHeap(), 0, cardnameW
);
630 WARN("Got a failure during card enumeration: %d (%s)\n",
631 err
, snd_strerror(err
));
636 HRESULT WINAPI
AUDDRV_GetEndpointIDs(EDataFlow flow
, WCHAR
***ids
, GUID
**guids
,
637 UINT
*num
, UINT
*def_index
)
641 TRACE("%d %p %p %p %p\n", flow
, ids
, guids
, num
, def_index
);
646 hr
= alsa_enum_devices(flow
, ids
, guids
, num
);
649 for(i
= 0; i
< *num
; ++i
)
650 HeapFree(GetProcessHeap(), 0, (*ids
)[i
]);
651 HeapFree(GetProcessHeap(), 0, *ids
);
652 HeapFree(GetProcessHeap(), 0, *guids
);
653 return E_OUTOFMEMORY
;
656 TRACE("Enumerated %u devices\n", *num
);
659 HeapFree(GetProcessHeap(), 0, *ids
);
661 HeapFree(GetProcessHeap(), 0, *guids
);
670 /* Using the pulse PCM device from alsa-plugins 1.0.24 triggers a bug
671 * which causes audio to cease playing after a few minutes of playback.
672 * Setting handle_underrun=1 on pulse-backed ALSA devices seems to work
673 * around this issue. */
674 static snd_config_t
*make_handle_underrun_config(const char *name
)
676 snd_config_t
*lconf
, *dev_node
, *hu_node
, *type_node
;
677 char dev_node_name
[260];
678 const char *type_str
;
683 if((err
= snd_config_copy(&lconf
, snd_config
)) < 0){
684 WARN("snd_config_copy failed: %d (%s)\n", err
, snd_strerror(err
));
688 sprintf(dev_node_name
, "pcm.%s", name
);
689 err
= snd_config_search(lconf
, dev_node_name
, &dev_node
);
691 snd_config_delete(lconf
);
695 snd_config_delete(lconf
);
696 WARN("snd_config_search failed: %d (%s)\n", err
, snd_strerror(err
));
700 /* ALSA is extremely fragile. If it runs into a config setting it doesn't
701 * recognize, it tends to fail or assert. So we only want to inject
702 * handle_underrun=1 on devices that we know will recognize it. */
703 err
= snd_config_search(dev_node
, "type", &type_node
);
705 snd_config_delete(lconf
);
709 snd_config_delete(lconf
);
710 WARN("snd_config_search failed: %d (%s)\n", err
, snd_strerror(err
));
714 if((err
= snd_config_get_string(type_node
, &type_str
)) < 0){
715 snd_config_delete(lconf
);
719 if(strcmp(type_str
, "pulse") != 0){
720 snd_config_delete(lconf
);
724 err
= snd_config_search(dev_node
, "handle_underrun", &hu_node
);
726 /* user already has an explicit handle_underrun setting, so don't
727 * use a local config */
728 snd_config_delete(lconf
);
732 snd_config_delete(lconf
);
733 WARN("snd_config_search failed: %d (%s)\n", err
, snd_strerror(err
));
737 if((err
= snd_config_imake_integer(&hu_node
, "handle_underrun", 1)) < 0){
738 snd_config_delete(lconf
);
739 WARN("snd_config_imake_integer failed: %d (%s)\n", err
,
744 if((err
= snd_config_add(dev_node
, hu_node
)) < 0){
745 snd_config_delete(lconf
);
746 WARN("snd_config_add failed: %d (%s)\n", err
, snd_strerror(err
));
753 static BOOL
get_alsa_name_by_guid(GUID
*guid
, char *name
, DWORD name_size
, EDataFlow
*flow
)
760 if(RegOpenKeyExW(HKEY_CURRENT_USER
, drv_key_devicesW
, 0, KEY_READ
, &devices_key
) != ERROR_SUCCESS
){
761 ERR("No devices found in registry?\n");
770 key_name_size
= ARRAY_SIZE(key_name
);
771 if(RegEnumKeyExW(devices_key
, i
++, key_name
, &key_name_size
, NULL
,
772 NULL
, NULL
, NULL
) != ERROR_SUCCESS
)
775 if(RegOpenKeyExW(devices_key
, key_name
, 0, KEY_READ
, &key
) != ERROR_SUCCESS
){
776 WARN("Couldn't open key: %s\n", wine_dbgstr_w(key_name
));
780 size
= sizeof(reg_guid
);
781 if(RegQueryValueExW(key
, guidW
, 0, &type
,
782 (BYTE
*)®_guid
, &size
) == ERROR_SUCCESS
){
783 if(IsEqualGUID(®_guid
, guid
)){
785 RegCloseKey(devices_key
);
787 TRACE("Found matching device key: %s\n", wine_dbgstr_w(key_name
));
789 if(key_name
[0] == '0')
791 else if(key_name
[0] == '1')
794 ERR("Unknown device type: %c\n", key_name
[0]);
798 WideCharToMultiByte(CP_UNIXCP
, 0, key_name
+ 2, -1, name
, name_size
, NULL
, NULL
);
807 RegCloseKey(devices_key
);
809 WARN("No matching device in registry for GUID %s\n", debugstr_guid(guid
));
814 HRESULT WINAPI
AUDDRV_GetAudioEndpoint(GUID
*guid
, IMMDevice
*dev
, IAudioClient
**out
)
818 snd_pcm_stream_t stream
;
820 static BOOL handle_underrun
= TRUE
;
825 TRACE("%s %p %p\n", debugstr_guid(guid
), dev
, out
);
827 if(!get_alsa_name_by_guid(guid
, alsa_name
, sizeof(alsa_name
), &dataflow
))
828 return AUDCLNT_E_DEVICE_INVALIDATED
;
830 This
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(ACImpl
));
832 return E_OUTOFMEMORY
;
834 This
->IAudioClient3_iface
.lpVtbl
= &AudioClient3_Vtbl
;
835 This
->IAudioRenderClient_iface
.lpVtbl
= &AudioRenderClient_Vtbl
;
836 This
->IAudioCaptureClient_iface
.lpVtbl
= &AudioCaptureClient_Vtbl
;
837 This
->IAudioClock_iface
.lpVtbl
= &AudioClock_Vtbl
;
838 This
->IAudioClock2_iface
.lpVtbl
= &AudioClock2_Vtbl
;
839 This
->IAudioStreamVolume_iface
.lpVtbl
= &AudioStreamVolume_Vtbl
;
841 if(dataflow
== eRender
)
842 stream
= SND_PCM_STREAM_PLAYBACK
;
843 else if(dataflow
== eCapture
)
844 stream
= SND_PCM_STREAM_CAPTURE
;
846 HeapFree(GetProcessHeap(), 0, This
);
850 hr
= CoCreateFreeThreadedMarshaler((IUnknown
*)&This
->IAudioClient3_iface
, &This
->pUnkFTMarshal
);
852 HeapFree(GetProcessHeap(), 0, This
);
856 This
->dataflow
= dataflow
;
857 if(handle_underrun
&& ((lconf
= make_handle_underrun_config(alsa_name
)))){
858 err
= snd_pcm_open_lconf(&This
->pcm_handle
, alsa_name
, stream
, SND_PCM_NONBLOCK
, lconf
);
859 TRACE("Opening PCM device \"%s\" with handle_underrun: %d\n", alsa_name
, err
);
860 snd_config_delete(lconf
);
861 /* Pulse <= 2010 returns EINVAL, it does not know handle_underrun. */
863 ERR_(winediag
)("PulseAudio \"%s\" %d without handle_underrun. Audio may hang."
864 " Please upgrade to alsa_plugins >= 1.0.24\n", alsa_name
, err
);
865 handle_underrun
= FALSE
;
870 err
= snd_pcm_open(&This
->pcm_handle
, alsa_name
, stream
, SND_PCM_NONBLOCK
);
873 HeapFree(GetProcessHeap(), 0, This
);
874 WARN("Unable to open PCM \"%s\": %d (%s)\n", alsa_name
, err
, snd_strerror(err
));
877 return AUDCLNT_E_DEVICE_IN_USE
;
879 return AUDCLNT_E_ENDPOINT_CREATE_FAILED
;
883 This
->hw_params
= HeapAlloc(GetProcessHeap(), 0,
884 snd_pcm_hw_params_sizeof());
885 if(!This
->hw_params
){
886 snd_pcm_close(This
->pcm_handle
);
887 HeapFree(GetProcessHeap(), 0, This
);
888 return E_OUTOFMEMORY
;
891 InitializeCriticalSection(&This
->lock
);
892 This
->lock
.DebugInfo
->Spare
[0] = (DWORD_PTR
)(__FILE__
": ACImpl.lock");
895 IMMDevice_AddRef(This
->parent
);
897 *out
= (IAudioClient
*)&This
->IAudioClient3_iface
;
898 IAudioClient3_AddRef(&This
->IAudioClient3_iface
);
903 static HRESULT WINAPI
AudioClient_QueryInterface(IAudioClient3
*iface
,
904 REFIID riid
, void **ppv
)
906 ACImpl
*This
= impl_from_IAudioClient3(iface
);
907 TRACE("(%p)->(%s, %p)\n", iface
, debugstr_guid(riid
), ppv
);
912 if(IsEqualIID(riid
, &IID_IUnknown
) ||
913 IsEqualIID(riid
, &IID_IAudioClient
) ||
914 IsEqualIID(riid
, &IID_IAudioClient2
) ||
915 IsEqualIID(riid
, &IID_IAudioClient3
))
917 else if(IsEqualIID(riid
, &IID_IMarshal
))
918 return IUnknown_QueryInterface(This
->pUnkFTMarshal
, riid
, ppv
);
921 IUnknown_AddRef((IUnknown
*)*ppv
);
924 WARN("Unknown interface %s\n", debugstr_guid(riid
));
925 return E_NOINTERFACE
;
928 static ULONG WINAPI
AudioClient_AddRef(IAudioClient3
*iface
)
930 ACImpl
*This
= impl_from_IAudioClient3(iface
);
932 ref
= InterlockedIncrement(&This
->ref
);
933 TRACE("(%p) Refcount now %u\n", This
, ref
);
937 static ULONG WINAPI
AudioClient_Release(IAudioClient3
*iface
)
939 ACImpl
*This
= impl_from_IAudioClient3(iface
);
942 ref
= InterlockedDecrement(&This
->ref
);
943 TRACE("(%p) Refcount now %u\n", This
, ref
);
948 event
= CreateEventW(NULL
, TRUE
, FALSE
, NULL
);
949 wait
= !DeleteTimerQueueTimer(g_timer_q
, This
->timer
, event
);
950 wait
= wait
&& GetLastError() == ERROR_IO_PENDING
;
952 WaitForSingleObject(event
, INFINITE
);
956 IAudioClient3_Stop(iface
);
957 IMMDevice_Release(This
->parent
);
958 IUnknown_Release(This
->pUnkFTMarshal
);
959 This
->lock
.DebugInfo
->Spare
[0] = 0;
960 DeleteCriticalSection(&This
->lock
);
961 snd_pcm_drop(This
->pcm_handle
);
962 snd_pcm_close(This
->pcm_handle
);
964 EnterCriticalSection(&g_sessions_lock
);
965 list_remove(&This
->entry
);
966 LeaveCriticalSection(&g_sessions_lock
);
968 HeapFree(GetProcessHeap(), 0, This
->vols
);
969 HeapFree(GetProcessHeap(), 0, This
->local_buffer
);
970 HeapFree(GetProcessHeap(), 0, This
->remapping_buf
);
971 HeapFree(GetProcessHeap(), 0, This
->silence_buf
);
972 HeapFree(GetProcessHeap(), 0, This
->tmp_buffer
);
973 HeapFree(GetProcessHeap(), 0, This
->hw_params
);
974 CoTaskMemFree(This
->fmt
);
975 HeapFree(GetProcessHeap(), 0, This
);
980 static void dump_fmt(const WAVEFORMATEX
*fmt
)
982 TRACE("wFormatTag: 0x%x (", fmt
->wFormatTag
);
983 switch(fmt
->wFormatTag
){
984 case WAVE_FORMAT_PCM
:
985 TRACE("WAVE_FORMAT_PCM");
987 case WAVE_FORMAT_IEEE_FLOAT
:
988 TRACE("WAVE_FORMAT_IEEE_FLOAT");
990 case WAVE_FORMAT_EXTENSIBLE
:
991 TRACE("WAVE_FORMAT_EXTENSIBLE");
999 TRACE("nChannels: %u\n", fmt
->nChannels
);
1000 TRACE("nSamplesPerSec: %u\n", fmt
->nSamplesPerSec
);
1001 TRACE("nAvgBytesPerSec: %u\n", fmt
->nAvgBytesPerSec
);
1002 TRACE("nBlockAlign: %u\n", fmt
->nBlockAlign
);
1003 TRACE("wBitsPerSample: %u\n", fmt
->wBitsPerSample
);
1004 TRACE("cbSize: %u\n", fmt
->cbSize
);
1006 if(fmt
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
){
1007 WAVEFORMATEXTENSIBLE
*fmtex
= (void*)fmt
;
1008 TRACE("dwChannelMask: %08x\n", fmtex
->dwChannelMask
);
1009 TRACE("Samples: %04x\n", fmtex
->Samples
.wReserved
);
1010 TRACE("SubFormat: %s\n", wine_dbgstr_guid(&fmtex
->SubFormat
));
1014 static WAVEFORMATEX
*clone_format(const WAVEFORMATEX
*fmt
)
1019 if(fmt
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
)
1020 size
= sizeof(WAVEFORMATEXTENSIBLE
);
1022 size
= sizeof(WAVEFORMATEX
);
1024 ret
= CoTaskMemAlloc(size
);
1028 memcpy(ret
, fmt
, size
);
1030 ret
->cbSize
= size
- sizeof(WAVEFORMATEX
);
1035 static snd_pcm_format_t
alsa_format(const WAVEFORMATEX
*fmt
)
1037 snd_pcm_format_t format
= SND_PCM_FORMAT_UNKNOWN
;
1038 const WAVEFORMATEXTENSIBLE
*fmtex
= (const WAVEFORMATEXTENSIBLE
*)fmt
;
1040 if(fmt
->wFormatTag
== WAVE_FORMAT_PCM
||
1041 (fmt
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
&&
1042 IsEqualGUID(&fmtex
->SubFormat
, &KSDATAFORMAT_SUBTYPE_PCM
))){
1043 if(fmt
->wBitsPerSample
== 8)
1044 format
= SND_PCM_FORMAT_U8
;
1045 else if(fmt
->wBitsPerSample
== 16)
1046 format
= SND_PCM_FORMAT_S16_LE
;
1047 else if(fmt
->wBitsPerSample
== 24)
1048 format
= SND_PCM_FORMAT_S24_3LE
;
1049 else if(fmt
->wBitsPerSample
== 32)
1050 format
= SND_PCM_FORMAT_S32_LE
;
1052 WARN("Unsupported bit depth: %u\n", fmt
->wBitsPerSample
);
1053 if(fmt
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
&&
1054 fmt
->wBitsPerSample
!= fmtex
->Samples
.wValidBitsPerSample
){
1055 if(fmtex
->Samples
.wValidBitsPerSample
== 20 && fmt
->wBitsPerSample
== 24)
1056 format
= SND_PCM_FORMAT_S20_3LE
;
1058 WARN("Unsupported ValidBits: %u\n", fmtex
->Samples
.wValidBitsPerSample
);
1060 }else if(fmt
->wFormatTag
== WAVE_FORMAT_IEEE_FLOAT
||
1061 (fmt
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
&&
1062 IsEqualGUID(&fmtex
->SubFormat
, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
))){
1063 if(fmt
->wBitsPerSample
== 32)
1064 format
= SND_PCM_FORMAT_FLOAT_LE
;
1065 else if(fmt
->wBitsPerSample
== 64)
1066 format
= SND_PCM_FORMAT_FLOAT64_LE
;
1068 WARN("Unsupported float size: %u\n", fmt
->wBitsPerSample
);
1070 WARN("Unknown wave format: %04x\n", fmt
->wFormatTag
);
1074 static void session_init_vols(AudioSession
*session
, UINT channels
)
1076 if(session
->channel_count
< channels
){
1079 if(session
->channel_vols
)
1080 session
->channel_vols
= HeapReAlloc(GetProcessHeap(), 0,
1081 session
->channel_vols
, sizeof(float) * channels
);
1083 session
->channel_vols
= HeapAlloc(GetProcessHeap(), 0,
1084 sizeof(float) * channels
);
1085 if(!session
->channel_vols
)
1088 for(i
= session
->channel_count
; i
< channels
; ++i
)
1089 session
->channel_vols
[i
] = 1.f
;
1091 session
->channel_count
= channels
;
1095 static AudioSession
*create_session(const GUID
*guid
, IMMDevice
*device
,
1100 ret
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(AudioSession
));
1104 memcpy(&ret
->guid
, guid
, sizeof(GUID
));
1106 ret
->device
= device
;
1108 list_init(&ret
->clients
);
1110 list_add_head(&g_sessions
, &ret
->entry
);
1112 InitializeCriticalSection(&ret
->lock
);
1113 ret
->lock
.DebugInfo
->Spare
[0] = (DWORD_PTR
)(__FILE__
": AudioSession.lock");
1115 session_init_vols(ret
, num_channels
);
1117 ret
->master_vol
= 1.f
;
1122 /* if channels == 0, then this will return or create a session with
1123 * matching dataflow and GUID. otherwise, channels must also match */
1124 static HRESULT
get_audio_session(const GUID
*sessionguid
,
1125 IMMDevice
*device
, UINT channels
, AudioSession
**out
)
1127 AudioSession
*session
;
1129 if(!sessionguid
|| IsEqualGUID(sessionguid
, &GUID_NULL
)){
1130 *out
= create_session(&GUID_NULL
, device
, channels
);
1132 return E_OUTOFMEMORY
;
1138 LIST_FOR_EACH_ENTRY(session
, &g_sessions
, AudioSession
, entry
){
1139 if(session
->device
== device
&&
1140 IsEqualGUID(sessionguid
, &session
->guid
)){
1141 session_init_vols(session
, channels
);
1148 *out
= create_session(sessionguid
, device
, channels
);
1150 return E_OUTOFMEMORY
;
1156 static int alsa_channel_index(DWORD flag
)
1159 case SPEAKER_FRONT_LEFT
:
1161 case SPEAKER_FRONT_RIGHT
:
1163 case SPEAKER_BACK_LEFT
:
1165 case SPEAKER_BACK_RIGHT
:
1167 case SPEAKER_FRONT_CENTER
:
1169 case SPEAKER_LOW_FREQUENCY
:
1171 case SPEAKER_SIDE_LEFT
:
1173 case SPEAKER_SIDE_RIGHT
:
1179 static BOOL
need_remapping(ACImpl
*This
, const WAVEFORMATEX
*fmt
, int *map
)
1182 for(i
= 0; i
< fmt
->nChannels
; ++i
){
1189 static DWORD
get_channel_mask(unsigned int channels
)
1195 return KSAUDIO_SPEAKER_MONO
;
1197 return KSAUDIO_SPEAKER_STEREO
;
1199 return KSAUDIO_SPEAKER_STEREO
| SPEAKER_LOW_FREQUENCY
;
1201 return KSAUDIO_SPEAKER_QUAD
; /* not _SURROUND */
1203 return KSAUDIO_SPEAKER_QUAD
| SPEAKER_LOW_FREQUENCY
;
1205 return KSAUDIO_SPEAKER_5POINT1
; /* not 5POINT1_SURROUND */
1207 return KSAUDIO_SPEAKER_5POINT1
| SPEAKER_BACK_CENTER
;
1209 return KSAUDIO_SPEAKER_7POINT1_SURROUND
; /* Vista deprecates 7POINT1 */
1211 FIXME("Unknown speaker configuration: %u\n", channels
);
1215 static HRESULT
map_channels(ACImpl
*This
, const WAVEFORMATEX
*fmt
, int *alsa_channels
, int *map
)
1219 if(This
->dataflow
!= eCapture
&& (fmt
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
|| fmt
->nChannels
> 2) ){
1220 WAVEFORMATEXTENSIBLE
*fmtex
= (void*)fmt
;
1221 DWORD mask
, flag
= SPEAKER_FRONT_LEFT
;
1224 if(fmt
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
&&
1225 fmtex
->dwChannelMask
!= 0)
1226 mask
= fmtex
->dwChannelMask
;
1228 mask
= get_channel_mask(fmt
->nChannels
);
1232 while(i
< fmt
->nChannels
&& !(flag
& SPEAKER_RESERVED
)){
1234 map
[i
] = alsa_channel_index(flag
);
1235 TRACE("Mapping mmdevapi channel %u (0x%x) to ALSA channel %d\n",
1237 if(map
[i
] >= *alsa_channels
)
1238 *alsa_channels
= map
[i
] + 1;
1244 while(i
< fmt
->nChannels
){
1245 map
[i
] = *alsa_channels
;
1246 TRACE("Mapping mmdevapi channel %u to ALSA channel %d\n",
1252 for(i
= 0; i
< fmt
->nChannels
; ++i
){
1254 map
[i
] = *alsa_channels
;
1256 TRACE("Remapping mmdevapi channel %u to ALSA channel %d\n",
1261 need_remap
= need_remapping(This
, fmt
, map
);
1263 *alsa_channels
= fmt
->nChannels
;
1268 TRACE("need_remapping: %u, alsa_channels: %d\n", need_remap
, *alsa_channels
);
1270 return need_remap
? S_OK
: S_FALSE
;
1273 static void silence_buffer(ACImpl
*This
, BYTE
*buffer
, UINT32 frames
)
1275 WAVEFORMATEXTENSIBLE
*fmtex
= (WAVEFORMATEXTENSIBLE
*)This
->fmt
;
1276 if((This
->fmt
->wFormatTag
== WAVE_FORMAT_PCM
||
1277 (This
->fmt
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
&&
1278 IsEqualGUID(&fmtex
->SubFormat
, &KSDATAFORMAT_SUBTYPE_PCM
))) &&
1279 This
->fmt
->wBitsPerSample
== 8)
1280 memset(buffer
, 128, frames
* This
->fmt
->nBlockAlign
);
1282 memset(buffer
, 0, frames
* This
->fmt
->nBlockAlign
);
1285 static HRESULT WINAPI
AudioClient_Initialize(IAudioClient3
*iface
,
1286 AUDCLNT_SHAREMODE mode
, DWORD flags
, REFERENCE_TIME duration
,
1287 REFERENCE_TIME period
, const WAVEFORMATEX
*fmt
,
1288 const GUID
*sessionguid
)
1290 ACImpl
*This
= impl_from_IAudioClient3(iface
);
1291 snd_pcm_sw_params_t
*sw_params
= NULL
;
1292 snd_pcm_format_t format
;
1293 unsigned int rate
, alsa_period_us
;
1297 TRACE("(%p)->(%x, %x, %s, %s, %p, %s)\n", This
, mode
, flags
,
1298 wine_dbgstr_longlong(duration
), wine_dbgstr_longlong(period
), fmt
, debugstr_guid(sessionguid
));
1303 if(mode
!= AUDCLNT_SHAREMODE_SHARED
&& mode
!= AUDCLNT_SHAREMODE_EXCLUSIVE
)
1304 return E_INVALIDARG
;
1306 if(flags
& ~(AUDCLNT_STREAMFLAGS_CROSSPROCESS
|
1307 AUDCLNT_STREAMFLAGS_LOOPBACK
|
1308 AUDCLNT_STREAMFLAGS_EVENTCALLBACK
|
1309 AUDCLNT_STREAMFLAGS_NOPERSIST
|
1310 AUDCLNT_STREAMFLAGS_RATEADJUST
|
1311 AUDCLNT_SESSIONFLAGS_EXPIREWHENUNOWNED
|
1312 AUDCLNT_SESSIONFLAGS_DISPLAY_HIDE
|
1313 AUDCLNT_SESSIONFLAGS_DISPLAY_HIDEWHENEXPIRED
|
1314 AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY
|
1315 AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM
)){
1316 FIXME("Unknown flags: %08x\n", flags
);
1317 return E_INVALIDARG
;
1320 if(mode
== AUDCLNT_SHAREMODE_SHARED
){
1321 period
= DefaultPeriod
;
1322 if( duration
< 3 * period
)
1323 duration
= 3 * period
;
1325 if(fmt
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
){
1326 if(((WAVEFORMATEXTENSIBLE
*)fmt
)->dwChannelMask
== 0 ||
1327 ((WAVEFORMATEXTENSIBLE
*)fmt
)->dwChannelMask
& SPEAKER_RESERVED
)
1328 return AUDCLNT_E_UNSUPPORTED_FORMAT
;
1332 period
= DefaultPeriod
; /* not minimum */
1333 if(period
< MinimumPeriod
|| period
> 5000000)
1334 return AUDCLNT_E_INVALID_DEVICE_PERIOD
;
1335 if(duration
> 20000000) /* the smaller the period, the lower this limit */
1336 return AUDCLNT_E_BUFFER_SIZE_ERROR
;
1337 if(flags
& AUDCLNT_STREAMFLAGS_EVENTCALLBACK
){
1338 if(duration
!= period
)
1339 return AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL
;
1340 FIXME("EXCLUSIVE mode with EVENTCALLBACK\n");
1341 return AUDCLNT_E_DEVICE_IN_USE
;
1343 if( duration
< 8 * period
)
1344 duration
= 8 * period
; /* may grow above 2s */
1348 EnterCriticalSection(&This
->lock
);
1351 LeaveCriticalSection(&This
->lock
);
1352 return AUDCLNT_E_ALREADY_INITIALIZED
;
1357 This
->need_remapping
= map_channels(This
, fmt
, &This
->alsa_channels
, This
->alsa_channel_map
) == S_OK
;
1359 if((err
= snd_pcm_hw_params_any(This
->pcm_handle
, This
->hw_params
)) < 0){
1360 WARN("Unable to get hw_params: %d (%s)\n", err
, snd_strerror(err
));
1361 hr
= AUDCLNT_E_ENDPOINT_CREATE_FAILED
;
1365 if((err
= snd_pcm_hw_params_set_access(This
->pcm_handle
, This
->hw_params
,
1366 SND_PCM_ACCESS_RW_INTERLEAVED
)) < 0){
1367 WARN("Unable to set access: %d (%s)\n", err
, snd_strerror(err
));
1368 hr
= AUDCLNT_E_ENDPOINT_CREATE_FAILED
;
1372 format
= alsa_format(fmt
);
1373 if (format
== SND_PCM_FORMAT_UNKNOWN
){
1374 hr
= AUDCLNT_E_UNSUPPORTED_FORMAT
;
1378 if((err
= snd_pcm_hw_params_set_format(This
->pcm_handle
, This
->hw_params
,
1380 WARN("Unable to set ALSA format to %u: %d (%s)\n", format
, err
,
1382 hr
= AUDCLNT_E_UNSUPPORTED_FORMAT
;
1386 This
->alsa_format
= format
;
1388 rate
= fmt
->nSamplesPerSec
;
1389 if((err
= snd_pcm_hw_params_set_rate_near(This
->pcm_handle
, This
->hw_params
,
1391 WARN("Unable to set rate to %u: %d (%s)\n", rate
, err
,
1393 hr
= AUDCLNT_E_UNSUPPORTED_FORMAT
;
1397 if((err
= snd_pcm_hw_params_set_channels(This
->pcm_handle
, This
->hw_params
,
1398 This
->alsa_channels
)) < 0){
1399 WARN("Unable to set channels to %u: %d (%s)\n", fmt
->nChannels
, err
,
1401 hr
= AUDCLNT_E_UNSUPPORTED_FORMAT
;
1405 This
->mmdev_period_rt
= period
;
1406 alsa_period_us
= This
->mmdev_period_rt
/ 10;
1407 if((err
= snd_pcm_hw_params_set_period_time_near(This
->pcm_handle
,
1408 This
->hw_params
, &alsa_period_us
, NULL
)) < 0)
1409 WARN("Unable to set period time near %u: %d (%s)\n", alsa_period_us
,
1410 err
, snd_strerror(err
));
1411 /* ALSA updates the output variable alsa_period_us */
1413 This
->mmdev_period_frames
= MulDiv(fmt
->nSamplesPerSec
,
1414 This
->mmdev_period_rt
, 10000000);
1416 /* Buffer 4 ALSA periods if large enough, else 4 mmdevapi periods */
1417 This
->alsa_bufsize_frames
= This
->mmdev_period_frames
* 4;
1418 if(err
< 0 || alsa_period_us
< period
/ 10)
1419 err
= snd_pcm_hw_params_set_buffer_size_near(This
->pcm_handle
,
1420 This
->hw_params
, &This
->alsa_bufsize_frames
);
1422 unsigned int periods
= 4;
1423 err
= snd_pcm_hw_params_set_periods_near(This
->pcm_handle
, This
->hw_params
, &periods
, NULL
);
1426 WARN("Unable to set buffer size: %d (%s)\n", err
, snd_strerror(err
));
1428 if((err
= snd_pcm_hw_params(This
->pcm_handle
, This
->hw_params
)) < 0){
1429 WARN("Unable to set hw params: %d (%s)\n", err
, snd_strerror(err
));
1430 hr
= AUDCLNT_E_ENDPOINT_CREATE_FAILED
;
1434 if((err
= snd_pcm_hw_params_get_period_size(This
->hw_params
,
1435 &This
->alsa_period_frames
, NULL
)) < 0){
1436 WARN("Unable to get period size: %d (%s)\n", err
, snd_strerror(err
));
1437 hr
= AUDCLNT_E_ENDPOINT_CREATE_FAILED
;
1441 if((err
= snd_pcm_hw_params_get_buffer_size(This
->hw_params
,
1442 &This
->alsa_bufsize_frames
)) < 0){
1443 WARN("Unable to get buffer size: %d (%s)\n", err
, snd_strerror(err
));
1444 hr
= AUDCLNT_E_ENDPOINT_CREATE_FAILED
;
1448 sw_params
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, snd_pcm_sw_params_sizeof());
1454 if((err
= snd_pcm_sw_params_current(This
->pcm_handle
, sw_params
)) < 0){
1455 WARN("Unable to get sw_params: %d (%s)\n", err
, snd_strerror(err
));
1456 hr
= AUDCLNT_E_ENDPOINT_CREATE_FAILED
;
1460 if((err
= snd_pcm_sw_params_set_start_threshold(This
->pcm_handle
,
1461 sw_params
, 1)) < 0){
1462 WARN("Unable set start threshold to 1: %d (%s)\n", err
, snd_strerror(err
));
1463 hr
= AUDCLNT_E_ENDPOINT_CREATE_FAILED
;
1467 if((err
= snd_pcm_sw_params_set_stop_threshold(This
->pcm_handle
,
1468 sw_params
, This
->alsa_bufsize_frames
)) < 0){
1469 WARN("Unable set stop threshold to %lu: %d (%s)\n",
1470 This
->alsa_bufsize_frames
, err
, snd_strerror(err
));
1471 hr
= AUDCLNT_E_ENDPOINT_CREATE_FAILED
;
1475 if((err
= snd_pcm_sw_params(This
->pcm_handle
, sw_params
)) < 0){
1476 WARN("Unable to set sw params: %d (%s)\n", err
, snd_strerror(err
));
1477 hr
= AUDCLNT_E_ENDPOINT_CREATE_FAILED
;
1481 if((err
= snd_pcm_prepare(This
->pcm_handle
)) < 0){
1482 WARN("Unable to prepare device: %d (%s)\n", err
, snd_strerror(err
));
1483 hr
= AUDCLNT_E_ENDPOINT_CREATE_FAILED
;
1487 /* Bear in mind weird situations where
1488 * ALSA period (50ms) > mmdevapi buffer (3x10ms)
1489 * or surprising rounding as seen with 22050x8x1 with Pulse:
1490 * ALSA period 220 vs. 221 frames in mmdevapi and
1491 * buffer 883 vs. 2205 frames in mmdevapi! */
1492 This
->bufsize_frames
= MulDiv(duration
, fmt
->nSamplesPerSec
, 10000000);
1493 if(mode
== AUDCLNT_SHAREMODE_EXCLUSIVE
)
1494 This
->bufsize_frames
-= This
->bufsize_frames
% This
->mmdev_period_frames
;
1495 This
->hidden_frames
= This
->alsa_period_frames
+ This
->mmdev_period_frames
+
1496 MulDiv(fmt
->nSamplesPerSec
, EXTRA_SAFE_RT
, 10000000);
1497 /* leave no less than about 1.33ms or 256 bytes of data after a rewind */
1498 This
->safe_rewind_frames
= max(256 / fmt
->nBlockAlign
, MulDiv(133, fmt
->nSamplesPerSec
, 100000));
1500 /* Check if the ALSA buffer is so small that it will run out before
1501 * the next MMDevAPI period tick occurs. Allow a little wiggle room
1502 * with 120% of the period time. */
1503 if(This
->alsa_bufsize_frames
< 1.2 * This
->mmdev_period_frames
)
1504 FIXME("ALSA buffer time is too small. Expect underruns. (%lu < %u * 1.2)\n",
1505 This
->alsa_bufsize_frames
, This
->mmdev_period_frames
);
1507 This
->fmt
= clone_format(fmt
);
1513 This
->local_buffer
= HeapAlloc(GetProcessHeap(), 0,
1514 This
->bufsize_frames
* fmt
->nBlockAlign
);
1515 if(!This
->local_buffer
){
1519 silence_buffer(This
, This
->local_buffer
, This
->bufsize_frames
);
1521 This
->silence_buf
= HeapAlloc(GetProcessHeap(), 0,
1522 This
->alsa_period_frames
* This
->fmt
->nBlockAlign
);
1523 if(!This
->silence_buf
){
1527 silence_buffer(This
, This
->silence_buf
, This
->alsa_period_frames
);
1529 This
->vols
= HeapAlloc(GetProcessHeap(), 0, fmt
->nChannels
* sizeof(float));
1535 for(i
= 0; i
< fmt
->nChannels
; ++i
)
1536 This
->vols
[i
] = 1.f
;
1539 This
->flags
= flags
;
1541 EnterCriticalSection(&g_sessions_lock
);
1543 hr
= get_audio_session(sessionguid
, This
->parent
, fmt
->nChannels
,
1546 LeaveCriticalSection(&g_sessions_lock
);
1550 list_add_tail(&This
->session
->clients
, &This
->entry
);
1552 LeaveCriticalSection(&g_sessions_lock
);
1554 This
->initted
= TRUE
;
1556 TRACE("ALSA period: %lu frames\n", This
->alsa_period_frames
);
1557 TRACE("ALSA buffer: %lu frames\n", This
->alsa_bufsize_frames
);
1558 TRACE("MMDevice period: %u frames\n", This
->mmdev_period_frames
);
1559 TRACE("MMDevice buffer: %u frames\n", This
->bufsize_frames
);
1562 HeapFree(GetProcessHeap(), 0, sw_params
);
1564 HeapFree(GetProcessHeap(), 0, This
->local_buffer
);
1565 This
->local_buffer
= NULL
;
1566 CoTaskMemFree(This
->fmt
);
1568 HeapFree(GetProcessHeap(), 0, This
->vols
);
1572 LeaveCriticalSection(&This
->lock
);
1577 static HRESULT WINAPI
AudioClient_GetBufferSize(IAudioClient3
*iface
,
1580 ACImpl
*This
= impl_from_IAudioClient3(iface
);
1582 TRACE("(%p)->(%p)\n", This
, out
);
1587 EnterCriticalSection(&This
->lock
);
1590 LeaveCriticalSection(&This
->lock
);
1591 return AUDCLNT_E_NOT_INITIALIZED
;
1594 *out
= This
->bufsize_frames
;
1596 LeaveCriticalSection(&This
->lock
);
1601 static HRESULT WINAPI
AudioClient_GetStreamLatency(IAudioClient3
*iface
,
1602 REFERENCE_TIME
*latency
)
1604 ACImpl
*This
= impl_from_IAudioClient3(iface
);
1606 TRACE("(%p)->(%p)\n", This
, latency
);
1611 EnterCriticalSection(&This
->lock
);
1614 LeaveCriticalSection(&This
->lock
);
1615 return AUDCLNT_E_NOT_INITIALIZED
;
1618 /* Hide some frames in the ALSA buffer. Allows us to return GetCurrentPadding=0
1619 * yet have enough data left to play (as if it were in native's mixer). Add:
1620 * + mmdevapi_period such that at the end of it, ALSA still has data;
1621 * + EXTRA_SAFE (~4ms) to allow for late callback invocation / fluctuation;
1622 * + alsa_period such that ALSA always has at least one period to play. */
1623 if(This
->dataflow
== eRender
)
1624 *latency
= MulDiv(This
->hidden_frames
, 10000000, This
->fmt
->nSamplesPerSec
);
1626 *latency
= MulDiv(This
->alsa_period_frames
, 10000000, This
->fmt
->nSamplesPerSec
)
1627 + This
->mmdev_period_rt
;
1629 LeaveCriticalSection(&This
->lock
);
1634 static HRESULT WINAPI
AudioClient_GetCurrentPadding(IAudioClient3
*iface
,
1637 ACImpl
*This
= impl_from_IAudioClient3(iface
);
1639 TRACE("(%p)->(%p)\n", This
, out
);
1644 EnterCriticalSection(&This
->lock
);
1647 LeaveCriticalSection(&This
->lock
);
1648 return AUDCLNT_E_NOT_INITIALIZED
;
1651 /* padding is solely updated at callback time in shared mode */
1652 *out
= This
->held_frames
;
1654 LeaveCriticalSection(&This
->lock
);
1656 TRACE("pad: %u\n", *out
);
1661 static HRESULT WINAPI
AudioClient_IsFormatSupported(IAudioClient3
*iface
,
1662 AUDCLNT_SHAREMODE mode
, const WAVEFORMATEX
*fmt
,
1665 ACImpl
*This
= impl_from_IAudioClient3(iface
);
1666 snd_pcm_format_mask_t
*formats
= NULL
;
1667 snd_pcm_format_t format
;
1669 WAVEFORMATEX
*closest
= NULL
;
1670 unsigned int max
= 0, min
= 0;
1672 int alsa_channels
, alsa_channel_map
[32];
1674 TRACE("(%p)->(%x, %p, %p)\n", This
, mode
, fmt
, out
);
1676 if(!fmt
|| (mode
== AUDCLNT_SHAREMODE_SHARED
&& !out
))
1679 if(mode
!= AUDCLNT_SHAREMODE_SHARED
&& mode
!= AUDCLNT_SHAREMODE_EXCLUSIVE
)
1680 return E_INVALIDARG
;
1682 if(fmt
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
&&
1683 fmt
->cbSize
< sizeof(WAVEFORMATEXTENSIBLE
) - sizeof(WAVEFORMATEX
))
1684 return E_INVALIDARG
;
1690 if(mode
!= AUDCLNT_SHAREMODE_SHARED
)
1694 if(fmt
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
&&
1695 (fmt
->nAvgBytesPerSec
== 0 ||
1696 fmt
->nBlockAlign
== 0 ||
1697 ((WAVEFORMATEXTENSIBLE
*)fmt
)->Samples
.wValidBitsPerSample
> fmt
->wBitsPerSample
))
1698 return E_INVALIDARG
;
1700 if(fmt
->nChannels
== 0)
1701 return AUDCLNT_E_UNSUPPORTED_FORMAT
;
1703 EnterCriticalSection(&This
->lock
);
1705 if((err
= snd_pcm_hw_params_any(This
->pcm_handle
, This
->hw_params
)) < 0){
1706 hr
= AUDCLNT_E_DEVICE_INVALIDATED
;
1710 formats
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
,
1711 snd_pcm_format_mask_sizeof());
1717 snd_pcm_hw_params_get_format_mask(This
->hw_params
, formats
);
1718 format
= alsa_format(fmt
);
1719 if (format
== SND_PCM_FORMAT_UNKNOWN
||
1720 !snd_pcm_format_mask_test(formats
, format
)){
1721 hr
= AUDCLNT_E_UNSUPPORTED_FORMAT
;
1725 closest
= clone_format(fmt
);
1731 if((err
= snd_pcm_hw_params_get_rate_min(This
->hw_params
, &min
, NULL
)) < 0){
1732 hr
= AUDCLNT_E_DEVICE_INVALIDATED
;
1733 WARN("Unable to get min rate: %d (%s)\n", err
, snd_strerror(err
));
1737 if((err
= snd_pcm_hw_params_get_rate_max(This
->hw_params
, &max
, NULL
)) < 0){
1738 hr
= AUDCLNT_E_DEVICE_INVALIDATED
;
1739 WARN("Unable to get max rate: %d (%s)\n", err
, snd_strerror(err
));
1743 if(fmt
->nSamplesPerSec
< min
|| fmt
->nSamplesPerSec
> max
){
1744 hr
= AUDCLNT_E_UNSUPPORTED_FORMAT
;
1748 if((err
= snd_pcm_hw_params_get_channels_min(This
->hw_params
, &min
)) < 0){
1749 hr
= AUDCLNT_E_DEVICE_INVALIDATED
;
1750 WARN("Unable to get min channels: %d (%s)\n", err
, snd_strerror(err
));
1754 if((err
= snd_pcm_hw_params_get_channels_max(This
->hw_params
, &max
)) < 0){
1755 hr
= AUDCLNT_E_DEVICE_INVALIDATED
;
1756 WARN("Unable to get max channels: %d (%s)\n", err
, snd_strerror(err
));
1759 if(fmt
->nChannels
> max
){
1761 closest
->nChannels
= max
;
1762 }else if(fmt
->nChannels
< min
){
1764 closest
->nChannels
= min
;
1767 map_channels(This
, fmt
, &alsa_channels
, alsa_channel_map
);
1769 if(alsa_channels
> max
){
1771 closest
->nChannels
= max
;
1774 if(closest
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
)
1775 ((WAVEFORMATEXTENSIBLE
*)closest
)->dwChannelMask
= get_channel_mask(closest
->nChannels
);
1777 if(fmt
->nBlockAlign
!= fmt
->nChannels
* fmt
->wBitsPerSample
/ 8 ||
1778 fmt
->nAvgBytesPerSec
!= fmt
->nBlockAlign
* fmt
->nSamplesPerSec
||
1779 (fmt
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
&&
1780 ((WAVEFORMATEXTENSIBLE
*)fmt
)->Samples
.wValidBitsPerSample
< fmt
->wBitsPerSample
))
1783 if(mode
== AUDCLNT_SHAREMODE_EXCLUSIVE
&&
1784 fmt
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
){
1785 if(((WAVEFORMATEXTENSIBLE
*)fmt
)->dwChannelMask
== 0 ||
1786 ((WAVEFORMATEXTENSIBLE
*)fmt
)->dwChannelMask
& SPEAKER_RESERVED
)
1791 LeaveCriticalSection(&This
->lock
);
1792 HeapFree(GetProcessHeap(), 0, formats
);
1794 if(hr
== S_FALSE
&& !out
)
1795 hr
= AUDCLNT_E_UNSUPPORTED_FORMAT
;
1797 if(hr
== S_FALSE
&& out
) {
1798 closest
->nBlockAlign
=
1799 closest
->nChannels
* closest
->wBitsPerSample
/ 8;
1800 closest
->nAvgBytesPerSec
=
1801 closest
->nBlockAlign
* closest
->nSamplesPerSec
;
1802 if(closest
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
)
1803 ((WAVEFORMATEXTENSIBLE
*)closest
)->Samples
.wValidBitsPerSample
= closest
->wBitsPerSample
;
1806 CoTaskMemFree(closest
);
1808 TRACE("returning: %08x\n", hr
);
1812 static HRESULT WINAPI
AudioClient_GetMixFormat(IAudioClient3
*iface
,
1813 WAVEFORMATEX
**pwfx
)
1815 ACImpl
*This
= impl_from_IAudioClient3(iface
);
1816 WAVEFORMATEXTENSIBLE
*fmt
;
1817 snd_pcm_format_mask_t
*formats
;
1818 unsigned int max_rate
, max_channels
;
1822 TRACE("(%p)->(%p)\n", This
, pwfx
);
1828 fmt
= CoTaskMemAlloc(sizeof(WAVEFORMATEXTENSIBLE
));
1830 return E_OUTOFMEMORY
;
1832 formats
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, snd_pcm_format_mask_sizeof());
1835 return E_OUTOFMEMORY
;
1838 EnterCriticalSection(&This
->lock
);
1840 if((err
= snd_pcm_hw_params_any(This
->pcm_handle
, This
->hw_params
)) < 0){
1841 WARN("Unable to get hw_params: %d (%s)\n", err
, snd_strerror(err
));
1842 hr
= AUDCLNT_E_DEVICE_INVALIDATED
;
1846 snd_pcm_hw_params_get_format_mask(This
->hw_params
, formats
);
1848 fmt
->Format
.wFormatTag
= WAVE_FORMAT_EXTENSIBLE
;
1849 if(snd_pcm_format_mask_test(formats
, SND_PCM_FORMAT_FLOAT_LE
)){
1850 fmt
->Format
.wBitsPerSample
= 32;
1851 fmt
->SubFormat
= KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
;
1852 }else if(snd_pcm_format_mask_test(formats
, SND_PCM_FORMAT_S16_LE
)){
1853 fmt
->Format
.wBitsPerSample
= 16;
1854 fmt
->SubFormat
= KSDATAFORMAT_SUBTYPE_PCM
;
1855 }else if(snd_pcm_format_mask_test(formats
, SND_PCM_FORMAT_U8
)){
1856 fmt
->Format
.wBitsPerSample
= 8;
1857 fmt
->SubFormat
= KSDATAFORMAT_SUBTYPE_PCM
;
1858 }else if(snd_pcm_format_mask_test(formats
, SND_PCM_FORMAT_S32_LE
)){
1859 fmt
->Format
.wBitsPerSample
= 32;
1860 fmt
->SubFormat
= KSDATAFORMAT_SUBTYPE_PCM
;
1861 }else if(snd_pcm_format_mask_test(formats
, SND_PCM_FORMAT_S24_3LE
)){
1862 fmt
->Format
.wBitsPerSample
= 24;
1863 fmt
->SubFormat
= KSDATAFORMAT_SUBTYPE_PCM
;
1865 ERR("Didn't recognize any available ALSA formats\n");
1866 hr
= AUDCLNT_E_DEVICE_INVALIDATED
;
1870 if((err
= snd_pcm_hw_params_get_channels_max(This
->hw_params
,
1871 &max_channels
)) < 0){
1872 WARN("Unable to get max channels: %d (%s)\n", err
, snd_strerror(err
));
1873 hr
= AUDCLNT_E_DEVICE_INVALIDATED
;
1877 if(max_channels
> 6)
1878 fmt
->Format
.nChannels
= 2;
1880 fmt
->Format
.nChannels
= max_channels
;
1882 if(fmt
->Format
.nChannels
> 1 && (fmt
->Format
.nChannels
& 0x1)){
1883 /* For most hardware on Windows, users must choose a configuration with an even
1884 * number of channels (stereo, quad, 5.1, 7.1). Users can then disable
1885 * channels, but those channels are still reported to applications from
1886 * GetMixFormat! Some applications behave badly if given an odd number of
1887 * channels (e.g. 2.1). */
1889 if(fmt
->Format
.nChannels
< max_channels
)
1890 fmt
->Format
.nChannels
+= 1;
1892 /* We could "fake" more channels and downmix the emulated channels,
1893 * but at that point you really ought to tweak your ALSA setup or
1894 * just use PulseAudio. */
1895 WARN("Some Windows applications behave badly with an odd number of channels (%u)!\n", fmt
->Format
.nChannels
);
1898 fmt
->dwChannelMask
= get_channel_mask(fmt
->Format
.nChannels
);
1900 if((err
= snd_pcm_hw_params_get_rate_max(This
->hw_params
, &max_rate
,
1902 WARN("Unable to get max rate: %d (%s)\n", err
, snd_strerror(err
));
1903 hr
= AUDCLNT_E_DEVICE_INVALIDATED
;
1907 if(max_rate
>= 48000)
1908 fmt
->Format
.nSamplesPerSec
= 48000;
1909 else if(max_rate
>= 44100)
1910 fmt
->Format
.nSamplesPerSec
= 44100;
1911 else if(max_rate
>= 22050)
1912 fmt
->Format
.nSamplesPerSec
= 22050;
1913 else if(max_rate
>= 11025)
1914 fmt
->Format
.nSamplesPerSec
= 11025;
1915 else if(max_rate
>= 8000)
1916 fmt
->Format
.nSamplesPerSec
= 8000;
1918 ERR("Unknown max rate: %u\n", max_rate
);
1919 hr
= AUDCLNT_E_DEVICE_INVALIDATED
;
1923 fmt
->Format
.nBlockAlign
= (fmt
->Format
.wBitsPerSample
*
1924 fmt
->Format
.nChannels
) / 8;
1925 fmt
->Format
.nAvgBytesPerSec
= fmt
->Format
.nSamplesPerSec
*
1926 fmt
->Format
.nBlockAlign
;
1928 fmt
->Samples
.wValidBitsPerSample
= fmt
->Format
.wBitsPerSample
;
1929 fmt
->Format
.cbSize
= sizeof(WAVEFORMATEXTENSIBLE
) - sizeof(WAVEFORMATEX
);
1931 dump_fmt((WAVEFORMATEX
*)fmt
);
1932 *pwfx
= (WAVEFORMATEX
*)fmt
;
1935 LeaveCriticalSection(&This
->lock
);
1938 HeapFree(GetProcessHeap(), 0, formats
);
1943 static HRESULT WINAPI
AudioClient_GetDevicePeriod(IAudioClient3
*iface
,
1944 REFERENCE_TIME
*defperiod
, REFERENCE_TIME
*minperiod
)
1946 ACImpl
*This
= impl_from_IAudioClient3(iface
);
1948 TRACE("(%p)->(%p, %p)\n", This
, defperiod
, minperiod
);
1950 if(!defperiod
&& !minperiod
)
1954 *defperiod
= DefaultPeriod
;
1956 *minperiod
= DefaultPeriod
;
1961 static BYTE
*remap_channels(ACImpl
*This
, BYTE
*buf
, snd_pcm_uframes_t frames
)
1963 snd_pcm_uframes_t i
;
1965 UINT bytes_per_sample
= This
->fmt
->wBitsPerSample
/ 8;
1967 if(!This
->need_remapping
)
1970 if(!This
->remapping_buf
){
1971 This
->remapping_buf
= HeapAlloc(GetProcessHeap(), 0,
1972 bytes_per_sample
* This
->alsa_channels
* frames
);
1973 This
->remapping_buf_frames
= frames
;
1974 }else if(This
->remapping_buf_frames
< frames
){
1975 This
->remapping_buf
= HeapReAlloc(GetProcessHeap(), 0, This
->remapping_buf
,
1976 bytes_per_sample
* This
->alsa_channels
* frames
);
1977 This
->remapping_buf_frames
= frames
;
1980 snd_pcm_format_set_silence(This
->alsa_format
, This
->remapping_buf
,
1981 frames
* This
->alsa_channels
);
1983 switch(This
->fmt
->wBitsPerSample
){
1985 UINT8
*tgt_buf
, *src_buf
;
1986 tgt_buf
= This
->remapping_buf
;
1988 for(i
= 0; i
< frames
; ++i
){
1989 for(c
= 0; c
< This
->fmt
->nChannels
; ++c
)
1990 tgt_buf
[This
->alsa_channel_map
[c
]] = src_buf
[c
];
1991 tgt_buf
+= This
->alsa_channels
;
1992 src_buf
+= This
->fmt
->nChannels
;
1997 UINT16
*tgt_buf
, *src_buf
;
1998 tgt_buf
= (UINT16
*)This
->remapping_buf
;
1999 src_buf
= (UINT16
*)buf
;
2000 for(i
= 0; i
< frames
; ++i
){
2001 for(c
= 0; c
< This
->fmt
->nChannels
; ++c
)
2002 tgt_buf
[This
->alsa_channel_map
[c
]] = src_buf
[c
];
2003 tgt_buf
+= This
->alsa_channels
;
2004 src_buf
+= This
->fmt
->nChannels
;
2009 UINT32
*tgt_buf
, *src_buf
;
2010 tgt_buf
= (UINT32
*)This
->remapping_buf
;
2011 src_buf
= (UINT32
*)buf
;
2012 for(i
= 0; i
< frames
; ++i
){
2013 for(c
= 0; c
< This
->fmt
->nChannels
; ++c
)
2014 tgt_buf
[This
->alsa_channel_map
[c
]] = src_buf
[c
];
2015 tgt_buf
+= This
->alsa_channels
;
2016 src_buf
+= This
->fmt
->nChannels
;
2021 BYTE
*tgt_buf
, *src_buf
;
2022 tgt_buf
= This
->remapping_buf
;
2024 for(i
= 0; i
< frames
; ++i
){
2025 for(c
= 0; c
< This
->fmt
->nChannels
; ++c
)
2026 memcpy(&tgt_buf
[This
->alsa_channel_map
[c
] * bytes_per_sample
],
2027 &src_buf
[c
* bytes_per_sample
], bytes_per_sample
);
2028 tgt_buf
+= This
->alsa_channels
* bytes_per_sample
;
2029 src_buf
+= This
->fmt
->nChannels
* bytes_per_sample
;
2035 return This
->remapping_buf
;
2038 static void adjust_buffer_volume(const ACImpl
*This
, BYTE
*buf
, snd_pcm_uframes_t frames
, BOOL mute
)
2040 float vol
[ARRAY_SIZE(This
->alsa_channel_map
)];
2041 BOOL adjust
= FALSE
;
2045 if (This
->vol_adjusted_frames
>= frames
)
2047 channels
= This
->fmt
->nChannels
;
2051 int err
= snd_pcm_format_set_silence(This
->alsa_format
, buf
, frames
* channels
);
2053 WARN("Setting buffer to silence failed: %d (%s)\n", err
, snd_strerror(err
));
2057 /* Adjust the buffer based on the volume for each channel */
2058 for (i
= 0; i
< channels
; i
++)
2059 vol
[i
] = This
->vols
[i
] * This
->session
->master_vol
;
2060 for (i
= 0; i
< min(channels
, This
->session
->channel_count
); i
++)
2062 vol
[i
] *= This
->session
->channel_vols
[i
];
2063 adjust
|= vol
[i
] != 1.0f
;
2065 while (i
< channels
) adjust
|= vol
[i
++] != 1.0f
;
2066 if (!adjust
) return;
2068 /* Skip the frames we've already adjusted before */
2069 end
= buf
+ frames
* This
->fmt
->nBlockAlign
;
2070 buf
+= This
->vol_adjusted_frames
* This
->fmt
->nBlockAlign
;
2072 switch (This
->alsa_format
)
2074 #ifndef WORDS_BIGENDIAN
2075 #define PROCESS_BUFFER(type) do \
2077 type *p = (type*)buf; \
2080 for (i = 0; i < channels; i++) \
2081 p[i] = p[i] * vol[i]; \
2083 } while ((BYTE*)p != end); \
2085 case SND_PCM_FORMAT_S16_LE
:
2086 PROCESS_BUFFER(INT16
);
2088 case SND_PCM_FORMAT_S32_LE
:
2089 PROCESS_BUFFER(INT32
);
2091 case SND_PCM_FORMAT_FLOAT_LE
:
2092 PROCESS_BUFFER(float);
2094 case SND_PCM_FORMAT_FLOAT64_LE
:
2095 PROCESS_BUFFER(double);
2097 #undef PROCESS_BUFFER
2098 case SND_PCM_FORMAT_S20_3LE
:
2099 case SND_PCM_FORMAT_S24_3LE
:
2101 /* Do it 12 bytes at a time until it is no longer possible */
2102 UINT32
*q
= (UINT32
*)buf
, mask
= ~0xff;
2105 /* After we adjust the volume, we need to mask out low bits */
2106 if (This
->alsa_format
== SND_PCM_FORMAT_S20_3LE
)
2110 while (end
- (BYTE
*)q
>= 12)
2114 v
[1] = q
[1] << 16 | (q
[0] >> 16 & ~0xff);
2115 v
[2] = q
[2] << 24 | (q
[1] >> 8 & ~0xff);
2116 v
[3] = q
[2] & ~0xff;
2117 for (k
= 0; k
< 4; k
++)
2119 v
[k
] = (INT32
)((INT32
)v
[k
] * vol
[i
]);
2121 if (++i
== channels
) i
= 0;
2123 *q
++ = v
[0] >> 8 | v
[1] << 16;
2124 *q
++ = v
[1] >> 16 | v
[2] << 8;
2125 *q
++ = v
[2] >> 24 | v
[3];
2130 UINT32 v
= (INT32
)((INT32
)(p
[0] << 8 | p
[1] << 16 | p
[2] << 24) * vol
[i
]);
2132 *p
++ = v
>> 8 & 0xff;
2133 *p
++ = v
>> 16 & 0xff;
2135 if (++i
== channels
) i
= 0;
2140 case SND_PCM_FORMAT_U8
:
2142 UINT8
*p
= (UINT8
*)buf
;
2145 for (i
= 0; i
< channels
; i
++)
2146 p
[i
] = (int)((p
[i
] - 128) * vol
[i
]) + 128;
2148 } while ((BYTE
*)p
!= end
);
2152 TRACE("Unhandled format %i, not adjusting volume.\n", This
->alsa_format
);
2157 static snd_pcm_sframes_t
alsa_write_best_effort(ACImpl
*This
, BYTE
*buf
,
2158 snd_pcm_uframes_t frames
, BOOL mute
)
2160 snd_pcm_sframes_t written
;
2162 adjust_buffer_volume(This
, buf
, frames
, mute
);
2164 /* Mark the frames we've already adjusted */
2165 if (This
->vol_adjusted_frames
< frames
)
2166 This
->vol_adjusted_frames
= frames
;
2168 buf
= remap_channels(This
, buf
, frames
);
2170 written
= snd_pcm_writei(This
->pcm_handle
, buf
, frames
);
2174 if(written
== -EAGAIN
)
2178 WARN("writei failed, recovering: %ld (%s)\n", written
,
2179 snd_strerror(written
));
2181 ret
= snd_pcm_recover(This
->pcm_handle
, written
, 0);
2183 WARN("Could not recover: %d (%s)\n", ret
, snd_strerror(ret
));
2187 written
= snd_pcm_writei(This
->pcm_handle
, buf
, frames
);
2191 This
->vol_adjusted_frames
-= written
;
2195 static snd_pcm_sframes_t
alsa_write_buffer_wrap(ACImpl
*This
, BYTE
*buf
,
2196 snd_pcm_uframes_t buflen
, snd_pcm_uframes_t offs
,
2197 snd_pcm_uframes_t to_write
)
2199 snd_pcm_sframes_t ret
= 0;
2202 snd_pcm_uframes_t chunk
;
2203 snd_pcm_sframes_t tmp
;
2205 if(offs
+ to_write
> buflen
)
2206 chunk
= buflen
- offs
;
2210 tmp
= alsa_write_best_effort(This
, buf
+ offs
* This
->fmt
->nBlockAlign
, chunk
, This
->session
->mute
);
2225 static UINT
buf_ptr_diff(UINT left
, UINT right
, UINT bufsize
)
2228 return right
- left
;
2229 return bufsize
- (left
- right
);
2232 static UINT
data_not_in_alsa(ACImpl
*This
)
2236 diff
= buf_ptr_diff(This
->lcl_offs_frames
, This
->wri_offs_frames
, This
->bufsize_frames
);
2240 return This
->held_frames
- This
->data_in_alsa_frames
;
2242 /* Here's the buffer setup:
2244 * vvvvvvvv sent to HW already
2245 * vvvvvvvv in ALSA buffer but rewindable
2246 * [dddddddddddddddd] ALSA buffer
2247 * [dddddddddddddddd--------] mmdevapi buffer
2248 * ^^^^^^^^ data_in_alsa_frames
2249 * ^^^^^^^^^^^^^^^^ held_frames
2253 * GetCurrentPadding is held_frames
2255 * During period callback, we decrement held_frames, fill ALSA buffer, and move
2258 * During Stop, we rewind the ALSA buffer
2260 static void alsa_write_data(ACImpl
*This
)
2262 snd_pcm_sframes_t written
;
2263 snd_pcm_uframes_t avail
, max_copy_frames
, data_frames_played
;
2266 /* this call seems to be required to get an accurate snd_pcm_state() */
2267 avail
= snd_pcm_avail_update(This
->pcm_handle
);
2269 if(snd_pcm_state(This
->pcm_handle
) == SND_PCM_STATE_XRUN
){
2270 TRACE("XRun state, recovering\n");
2272 avail
= This
->alsa_bufsize_frames
;
2274 if((err
= snd_pcm_recover(This
->pcm_handle
, -EPIPE
, 1)) < 0)
2275 WARN("snd_pcm_recover failed: %d (%s)\n", err
, snd_strerror(err
));
2277 if((err
= snd_pcm_reset(This
->pcm_handle
)) < 0)
2278 WARN("snd_pcm_reset failed: %d (%s)\n", err
, snd_strerror(err
));
2280 if((err
= snd_pcm_prepare(This
->pcm_handle
)) < 0)
2281 WARN("snd_pcm_prepare failed: %d (%s)\n", err
, snd_strerror(err
));
2284 TRACE("avail: %ld\n", avail
);
2286 /* Add a lead-in when starting with too few frames to ensure
2287 * continuous rendering. Additional benefit: Force ALSA to start. */
2288 if(This
->data_in_alsa_frames
== 0 && This
->held_frames
< This
->alsa_period_frames
)
2290 alsa_write_best_effort(This
, This
->silence_buf
, This
->alsa_period_frames
- This
->held_frames
, FALSE
);
2291 This
->vol_adjusted_frames
= 0;
2295 max_copy_frames
= data_not_in_alsa(This
);
2297 max_copy_frames
= 0;
2299 data_frames_played
= min(This
->data_in_alsa_frames
, avail
);
2300 This
->data_in_alsa_frames
-= data_frames_played
;
2302 if(This
->held_frames
> data_frames_played
){
2304 This
->held_frames
-= data_frames_played
;
2306 This
->held_frames
= 0;
2308 while(avail
&& max_copy_frames
){
2309 snd_pcm_uframes_t to_write
;
2311 to_write
= min(avail
, max_copy_frames
);
2313 written
= alsa_write_buffer_wrap(This
, This
->local_buffer
,
2314 This
->bufsize_frames
, This
->lcl_offs_frames
, to_write
);
2319 This
->lcl_offs_frames
+= written
;
2320 This
->lcl_offs_frames
%= This
->bufsize_frames
;
2321 This
->data_in_alsa_frames
+= written
;
2322 max_copy_frames
-= written
;
2326 SetEvent(This
->event
);
2329 static void alsa_read_data(ACImpl
*This
)
2331 snd_pcm_sframes_t nread
;
2332 UINT32 pos
= This
->wri_offs_frames
, limit
= This
->held_frames
;
2337 /* FIXME: Detect overrun and signal DATA_DISCONTINUITY
2338 * How to count overrun frames and report them as position increase? */
2339 limit
= This
->bufsize_frames
- max(limit
, pos
);
2341 nread
= snd_pcm_readi(This
->pcm_handle
,
2342 This
->local_buffer
+ pos
* This
->fmt
->nBlockAlign
, limit
);
2343 TRACE("read %ld from %u limit %u\n", nread
, pos
, limit
);
2347 if(nread
== -EAGAIN
) /* no data yet */
2350 WARN("read failed, recovering: %ld (%s)\n", nread
, snd_strerror(nread
));
2352 ret
= snd_pcm_recover(This
->pcm_handle
, nread
, 0);
2354 WARN("Recover failed: %d (%s)\n", ret
, snd_strerror(ret
));
2358 nread
= snd_pcm_readi(This
->pcm_handle
,
2359 This
->local_buffer
+ pos
* This
->fmt
->nBlockAlign
, limit
);
2361 WARN("read failed: %ld (%s)\n", nread
, snd_strerror(nread
));
2366 if(This
->session
->mute
){
2368 if((err
= snd_pcm_format_set_silence(This
->alsa_format
,
2369 This
->local_buffer
+ pos
* This
->fmt
->nBlockAlign
,
2371 WARN("Setting buffer to silence failed: %d (%s)\n", err
,
2375 This
->wri_offs_frames
+= nread
;
2376 This
->wri_offs_frames
%= This
->bufsize_frames
;
2377 This
->held_frames
+= nread
;
2381 SetEvent(This
->event
);
2384 static void CALLBACK
alsa_push_buffer_data(void *user
, BOOLEAN timer
)
2386 ACImpl
*This
= user
;
2388 EnterCriticalSection(&This
->lock
);
2390 QueryPerformanceCounter(&This
->last_period_time
);
2392 if(This
->dataflow
== eRender
)
2393 alsa_write_data(This
);
2394 else if(This
->dataflow
== eCapture
)
2395 alsa_read_data(This
);
2397 LeaveCriticalSection(&This
->lock
);
2400 static snd_pcm_uframes_t
interp_elapsed_frames(ACImpl
*This
)
2402 LARGE_INTEGER time_freq
, current_time
, time_diff
;
2403 QueryPerformanceFrequency(&time_freq
);
2404 QueryPerformanceCounter(¤t_time
);
2405 time_diff
.QuadPart
= current_time
.QuadPart
- This
->last_period_time
.QuadPart
;
2406 return MulDiv(time_diff
.QuadPart
, This
->fmt
->nSamplesPerSec
, time_freq
.QuadPart
);
2409 static int alsa_rewind_best_effort(ACImpl
*This
)
2411 snd_pcm_uframes_t len
, leave
;
2413 /* we can't use snd_pcm_rewindable, some PCM devices crash. so follow
2414 * PulseAudio's example and rewind as much data as we believe is in the
2415 * buffer, minus 1.33ms for safety. */
2417 /* amount of data to leave in ALSA buffer */
2418 leave
= interp_elapsed_frames(This
) + This
->safe_rewind_frames
;
2420 if(This
->held_frames
< leave
)
2421 This
->held_frames
= 0;
2423 This
->held_frames
-= leave
;
2425 if(This
->data_in_alsa_frames
< leave
)
2428 len
= This
->data_in_alsa_frames
- leave
;
2430 TRACE("rewinding %lu frames, now held %u\n", len
, This
->held_frames
);
2433 /* snd_pcm_rewind return value is often broken, assume it succeeded */
2434 snd_pcm_rewind(This
->pcm_handle
, len
);
2436 This
->data_in_alsa_frames
= 0;
2441 static HRESULT WINAPI
AudioClient_Start(IAudioClient3
*iface
)
2443 ACImpl
*This
= impl_from_IAudioClient3(iface
);
2445 TRACE("(%p)\n", This
);
2447 EnterCriticalSection(&This
->lock
);
2450 LeaveCriticalSection(&This
->lock
);
2451 return AUDCLNT_E_NOT_INITIALIZED
;
2454 if((This
->flags
& AUDCLNT_STREAMFLAGS_EVENTCALLBACK
) && !This
->event
){
2455 LeaveCriticalSection(&This
->lock
);
2456 return AUDCLNT_E_EVENTHANDLE_NOT_SET
;
2460 LeaveCriticalSection(&This
->lock
);
2461 return AUDCLNT_E_NOT_STOPPED
;
2464 if(This
->dataflow
== eCapture
){
2465 /* dump any data that might be leftover in the ALSA capture buffer */
2466 snd_pcm_readi(This
->pcm_handle
, This
->local_buffer
,
2467 This
->bufsize_frames
);
2469 snd_pcm_sframes_t avail
, written
;
2470 snd_pcm_uframes_t offs
;
2472 avail
= snd_pcm_avail_update(This
->pcm_handle
);
2473 avail
= min(avail
, This
->held_frames
);
2475 if(This
->wri_offs_frames
< This
->held_frames
)
2476 offs
= This
->bufsize_frames
- This
->held_frames
+ This
->wri_offs_frames
;
2478 offs
= This
->wri_offs_frames
- This
->held_frames
;
2480 /* fill it with data */
2481 written
= alsa_write_buffer_wrap(This
, This
->local_buffer
,
2482 This
->bufsize_frames
, offs
, avail
);
2485 This
->lcl_offs_frames
= (offs
+ written
) % This
->bufsize_frames
;
2486 This
->data_in_alsa_frames
= written
;
2488 This
->lcl_offs_frames
= offs
;
2489 This
->data_in_alsa_frames
= 0;
2494 if(!CreateTimerQueueTimer(&This
->timer
, g_timer_q
, alsa_push_buffer_data
,
2495 This
, 0, This
->mmdev_period_rt
/ 10000, WT_EXECUTEINTIMERTHREAD
)){
2496 LeaveCriticalSection(&This
->lock
);
2497 WARN("Unable to create timer: %u\n", GetLastError());
2498 return E_OUTOFMEMORY
;
2502 This
->started
= TRUE
;
2504 LeaveCriticalSection(&This
->lock
);
2509 static HRESULT WINAPI
AudioClient_Stop(IAudioClient3
*iface
)
2511 ACImpl
*This
= impl_from_IAudioClient3(iface
);
2513 TRACE("(%p)\n", This
);
2515 EnterCriticalSection(&This
->lock
);
2518 LeaveCriticalSection(&This
->lock
);
2519 return AUDCLNT_E_NOT_INITIALIZED
;
2523 LeaveCriticalSection(&This
->lock
);
2527 if(This
->dataflow
== eRender
)
2528 alsa_rewind_best_effort(This
);
2530 This
->started
= FALSE
;
2532 LeaveCriticalSection(&This
->lock
);
2537 static HRESULT WINAPI
AudioClient_Reset(IAudioClient3
*iface
)
2539 ACImpl
*This
= impl_from_IAudioClient3(iface
);
2541 TRACE("(%p)\n", This
);
2543 EnterCriticalSection(&This
->lock
);
2546 LeaveCriticalSection(&This
->lock
);
2547 return AUDCLNT_E_NOT_INITIALIZED
;
2551 LeaveCriticalSection(&This
->lock
);
2552 return AUDCLNT_E_NOT_STOPPED
;
2555 if(This
->getbuf_last
){
2556 LeaveCriticalSection(&This
->lock
);
2557 return AUDCLNT_E_BUFFER_OPERATION_PENDING
;
2560 if(snd_pcm_drop(This
->pcm_handle
) < 0)
2561 WARN("snd_pcm_drop failed\n");
2563 if(snd_pcm_reset(This
->pcm_handle
) < 0)
2564 WARN("snd_pcm_reset failed\n");
2566 if(snd_pcm_prepare(This
->pcm_handle
) < 0)
2567 WARN("snd_pcm_prepare failed\n");
2569 if(This
->dataflow
== eRender
){
2570 This
->written_frames
= 0;
2571 This
->last_pos_frames
= 0;
2573 This
->written_frames
+= This
->held_frames
;
2575 This
->held_frames
= 0;
2576 This
->lcl_offs_frames
= 0;
2577 This
->wri_offs_frames
= 0;
2579 LeaveCriticalSection(&This
->lock
);
2584 static HRESULT WINAPI
AudioClient_SetEventHandle(IAudioClient3
*iface
,
2587 ACImpl
*This
= impl_from_IAudioClient3(iface
);
2589 TRACE("(%p)->(%p)\n", This
, event
);
2592 return E_INVALIDARG
;
2594 EnterCriticalSection(&This
->lock
);
2597 LeaveCriticalSection(&This
->lock
);
2598 return AUDCLNT_E_NOT_INITIALIZED
;
2601 if(!(This
->flags
& AUDCLNT_STREAMFLAGS_EVENTCALLBACK
)){
2602 LeaveCriticalSection(&This
->lock
);
2603 return AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED
;
2607 LeaveCriticalSection(&This
->lock
);
2608 FIXME("called twice\n");
2609 return HRESULT_FROM_WIN32(ERROR_INVALID_NAME
);
2612 This
->event
= event
;
2614 LeaveCriticalSection(&This
->lock
);
2619 static HRESULT WINAPI
AudioClient_GetService(IAudioClient3
*iface
, REFIID riid
,
2622 ACImpl
*This
= impl_from_IAudioClient3(iface
);
2624 TRACE("(%p)->(%s, %p)\n", This
, debugstr_guid(riid
), ppv
);
2630 EnterCriticalSection(&This
->lock
);
2633 LeaveCriticalSection(&This
->lock
);
2634 return AUDCLNT_E_NOT_INITIALIZED
;
2637 if(IsEqualIID(riid
, &IID_IAudioRenderClient
)){
2638 if(This
->dataflow
!= eRender
){
2639 LeaveCriticalSection(&This
->lock
);
2640 return AUDCLNT_E_WRONG_ENDPOINT_TYPE
;
2642 IAudioRenderClient_AddRef(&This
->IAudioRenderClient_iface
);
2643 *ppv
= &This
->IAudioRenderClient_iface
;
2644 }else if(IsEqualIID(riid
, &IID_IAudioCaptureClient
)){
2645 if(This
->dataflow
!= eCapture
){
2646 LeaveCriticalSection(&This
->lock
);
2647 return AUDCLNT_E_WRONG_ENDPOINT_TYPE
;
2649 IAudioCaptureClient_AddRef(&This
->IAudioCaptureClient_iface
);
2650 *ppv
= &This
->IAudioCaptureClient_iface
;
2651 }else if(IsEqualIID(riid
, &IID_IAudioClock
)){
2652 IAudioClock_AddRef(&This
->IAudioClock_iface
);
2653 *ppv
= &This
->IAudioClock_iface
;
2654 }else if(IsEqualIID(riid
, &IID_IAudioStreamVolume
)){
2655 IAudioStreamVolume_AddRef(&This
->IAudioStreamVolume_iface
);
2656 *ppv
= &This
->IAudioStreamVolume_iface
;
2657 }else if(IsEqualIID(riid
, &IID_IAudioSessionControl
)){
2658 if(!This
->session_wrapper
){
2659 This
->session_wrapper
= AudioSessionWrapper_Create(This
);
2660 if(!This
->session_wrapper
){
2661 LeaveCriticalSection(&This
->lock
);
2662 return E_OUTOFMEMORY
;
2665 IAudioSessionControl2_AddRef(&This
->session_wrapper
->IAudioSessionControl2_iface
);
2667 *ppv
= &This
->session_wrapper
->IAudioSessionControl2_iface
;
2668 }else if(IsEqualIID(riid
, &IID_IChannelAudioVolume
)){
2669 if(!This
->session_wrapper
){
2670 This
->session_wrapper
= AudioSessionWrapper_Create(This
);
2671 if(!This
->session_wrapper
){
2672 LeaveCriticalSection(&This
->lock
);
2673 return E_OUTOFMEMORY
;
2676 IChannelAudioVolume_AddRef(&This
->session_wrapper
->IChannelAudioVolume_iface
);
2678 *ppv
= &This
->session_wrapper
->IChannelAudioVolume_iface
;
2679 }else if(IsEqualIID(riid
, &IID_ISimpleAudioVolume
)){
2680 if(!This
->session_wrapper
){
2681 This
->session_wrapper
= AudioSessionWrapper_Create(This
);
2682 if(!This
->session_wrapper
){
2683 LeaveCriticalSection(&This
->lock
);
2684 return E_OUTOFMEMORY
;
2687 ISimpleAudioVolume_AddRef(&This
->session_wrapper
->ISimpleAudioVolume_iface
);
2689 *ppv
= &This
->session_wrapper
->ISimpleAudioVolume_iface
;
2693 LeaveCriticalSection(&This
->lock
);
2697 LeaveCriticalSection(&This
->lock
);
2699 FIXME("stub %s\n", debugstr_guid(riid
));
2700 return E_NOINTERFACE
;
2703 static HRESULT WINAPI
AudioClient_IsOffloadCapable(IAudioClient3
*iface
,
2704 AUDIO_STREAM_CATEGORY category
, BOOL
*offload_capable
)
2706 ACImpl
*This
= impl_from_IAudioClient3(iface
);
2708 TRACE("(%p)->(0x%x, %p)\n", This
, category
, offload_capable
);
2710 if(!offload_capable
)
2711 return E_INVALIDARG
;
2713 *offload_capable
= FALSE
;
2718 static HRESULT WINAPI
AudioClient_SetClientProperties(IAudioClient3
*iface
,
2719 const AudioClientProperties
*prop
)
2721 ACImpl
*This
= impl_from_IAudioClient3(iface
);
2722 const Win8AudioClientProperties
*legacy_prop
= (const Win8AudioClientProperties
*)prop
;
2724 TRACE("(%p)->(%p)\n", This
, prop
);
2729 if(legacy_prop
->cbSize
== sizeof(AudioClientProperties
)){
2730 TRACE("{ bIsOffload: %u, eCategory: 0x%x, Options: 0x%x }\n",
2731 legacy_prop
->bIsOffload
,
2732 legacy_prop
->eCategory
,
2734 }else if(legacy_prop
->cbSize
== sizeof(Win8AudioClientProperties
)){
2735 TRACE("{ bIsOffload: %u, eCategory: 0x%x }\n",
2736 legacy_prop
->bIsOffload
,
2737 legacy_prop
->eCategory
);
2739 WARN("Unsupported Size = %d\n", legacy_prop
->cbSize
);
2740 return E_INVALIDARG
;
2744 if(legacy_prop
->bIsOffload
)
2745 return AUDCLNT_E_ENDPOINT_OFFLOAD_NOT_CAPABLE
;
2750 static HRESULT WINAPI
AudioClient_GetBufferSizeLimits(IAudioClient3
*iface
,
2751 const WAVEFORMATEX
*format
, BOOL event_driven
, REFERENCE_TIME
*min_duration
,
2752 REFERENCE_TIME
*max_duration
)
2754 ACImpl
*This
= impl_from_IAudioClient3(iface
);
2756 FIXME("(%p)->(%p, %u, %p, %p)\n", This
, format
, event_driven
, min_duration
, max_duration
);
2761 static HRESULT WINAPI
AudioClient_GetSharedModeEnginePeriod(IAudioClient3
*iface
,
2762 const WAVEFORMATEX
*format
, UINT32
*default_period_frames
, UINT32
*unit_period_frames
,
2763 UINT32
*min_period_frames
, UINT32
*max_period_frames
)
2765 ACImpl
*This
= impl_from_IAudioClient3(iface
);
2767 FIXME("(%p)->(%p, %p, %p, %p, %p)\n", This
, format
, default_period_frames
, unit_period_frames
,
2768 min_period_frames
, max_period_frames
);
2773 static HRESULT WINAPI
AudioClient_GetCurrentSharedModeEnginePeriod(IAudioClient3
*iface
,
2774 WAVEFORMATEX
**cur_format
, UINT32
*cur_period_frames
)
2776 ACImpl
*This
= impl_from_IAudioClient3(iface
);
2778 FIXME("(%p)->(%p, %p)\n", This
, cur_format
, cur_period_frames
);
2783 static HRESULT WINAPI
AudioClient_InitializeSharedAudioStream(IAudioClient3
*iface
,
2784 DWORD flags
, UINT32 period_frames
, const WAVEFORMATEX
*format
,
2785 const GUID
*session_guid
)
2787 ACImpl
*This
= impl_from_IAudioClient3(iface
);
2789 FIXME("(%p)->(0x%x, %u, %p, %s)\n", This
, flags
, period_frames
, format
, debugstr_guid(session_guid
));
2794 static const IAudioClient3Vtbl AudioClient3_Vtbl
=
2796 AudioClient_QueryInterface
,
2798 AudioClient_Release
,
2799 AudioClient_Initialize
,
2800 AudioClient_GetBufferSize
,
2801 AudioClient_GetStreamLatency
,
2802 AudioClient_GetCurrentPadding
,
2803 AudioClient_IsFormatSupported
,
2804 AudioClient_GetMixFormat
,
2805 AudioClient_GetDevicePeriod
,
2809 AudioClient_SetEventHandle
,
2810 AudioClient_GetService
,
2811 AudioClient_IsOffloadCapable
,
2812 AudioClient_SetClientProperties
,
2813 AudioClient_GetBufferSizeLimits
,
2814 AudioClient_GetSharedModeEnginePeriod
,
2815 AudioClient_GetCurrentSharedModeEnginePeriod
,
2816 AudioClient_InitializeSharedAudioStream
,
2819 static HRESULT WINAPI
AudioRenderClient_QueryInterface(
2820 IAudioRenderClient
*iface
, REFIID riid
, void **ppv
)
2822 ACImpl
*This
= impl_from_IAudioRenderClient(iface
);
2823 TRACE("(%p)->(%s, %p)\n", iface
, debugstr_guid(riid
), ppv
);
2829 if(IsEqualIID(riid
, &IID_IUnknown
) ||
2830 IsEqualIID(riid
, &IID_IAudioRenderClient
))
2832 else if(IsEqualIID(riid
, &IID_IMarshal
))
2833 return IUnknown_QueryInterface(This
->pUnkFTMarshal
, riid
, ppv
);
2836 IUnknown_AddRef((IUnknown
*)*ppv
);
2840 WARN("Unknown interface %s\n", debugstr_guid(riid
));
2841 return E_NOINTERFACE
;
2844 static ULONG WINAPI
AudioRenderClient_AddRef(IAudioRenderClient
*iface
)
2846 ACImpl
*This
= impl_from_IAudioRenderClient(iface
);
2847 return AudioClient_AddRef(&This
->IAudioClient3_iface
);
2850 static ULONG WINAPI
AudioRenderClient_Release(IAudioRenderClient
*iface
)
2852 ACImpl
*This
= impl_from_IAudioRenderClient(iface
);
2853 return AudioClient_Release(&This
->IAudioClient3_iface
);
2856 static HRESULT WINAPI
AudioRenderClient_GetBuffer(IAudioRenderClient
*iface
,
2857 UINT32 frames
, BYTE
**data
)
2859 ACImpl
*This
= impl_from_IAudioRenderClient(iface
);
2862 TRACE("(%p)->(%u, %p)\n", This
, frames
, data
);
2868 EnterCriticalSection(&This
->lock
);
2870 if(This
->getbuf_last
){
2871 LeaveCriticalSection(&This
->lock
);
2872 return AUDCLNT_E_OUT_OF_ORDER
;
2876 LeaveCriticalSection(&This
->lock
);
2880 /* held_frames == GetCurrentPadding_nolock(); */
2881 if(This
->held_frames
+ frames
> This
->bufsize_frames
){
2882 LeaveCriticalSection(&This
->lock
);
2883 return AUDCLNT_E_BUFFER_TOO_LARGE
;
2886 write_pos
= This
->wri_offs_frames
;
2887 if(write_pos
+ frames
> This
->bufsize_frames
){
2888 if(This
->tmp_buffer_frames
< frames
){
2889 HeapFree(GetProcessHeap(), 0, This
->tmp_buffer
);
2890 This
->tmp_buffer
= HeapAlloc(GetProcessHeap(), 0,
2891 frames
* This
->fmt
->nBlockAlign
);
2892 if(!This
->tmp_buffer
){
2893 LeaveCriticalSection(&This
->lock
);
2894 return E_OUTOFMEMORY
;
2896 This
->tmp_buffer_frames
= frames
;
2898 *data
= This
->tmp_buffer
;
2899 This
->getbuf_last
= -frames
;
2901 *data
= This
->local_buffer
+ write_pos
* This
->fmt
->nBlockAlign
;
2902 This
->getbuf_last
= frames
;
2905 silence_buffer(This
, *data
, frames
);
2907 LeaveCriticalSection(&This
->lock
);
2912 static void alsa_wrap_buffer(ACImpl
*This
, BYTE
*buffer
, UINT32 written_frames
)
2914 snd_pcm_uframes_t write_offs_frames
= This
->wri_offs_frames
;
2915 UINT32 write_offs_bytes
= write_offs_frames
* This
->fmt
->nBlockAlign
;
2916 snd_pcm_uframes_t chunk_frames
= This
->bufsize_frames
- write_offs_frames
;
2917 UINT32 chunk_bytes
= chunk_frames
* This
->fmt
->nBlockAlign
;
2918 UINT32 written_bytes
= written_frames
* This
->fmt
->nBlockAlign
;
2920 if(written_bytes
<= chunk_bytes
){
2921 memcpy(This
->local_buffer
+ write_offs_bytes
, buffer
, written_bytes
);
2923 memcpy(This
->local_buffer
+ write_offs_bytes
, buffer
, chunk_bytes
);
2924 memcpy(This
->local_buffer
, buffer
+ chunk_bytes
,
2925 written_bytes
- chunk_bytes
);
2929 static HRESULT WINAPI
AudioRenderClient_ReleaseBuffer(
2930 IAudioRenderClient
*iface
, UINT32 written_frames
, DWORD flags
)
2932 ACImpl
*This
= impl_from_IAudioRenderClient(iface
);
2935 TRACE("(%p)->(%u, %x)\n", This
, written_frames
, flags
);
2937 EnterCriticalSection(&This
->lock
);
2939 if(!written_frames
){
2940 This
->getbuf_last
= 0;
2941 LeaveCriticalSection(&This
->lock
);
2945 if(!This
->getbuf_last
){
2946 LeaveCriticalSection(&This
->lock
);
2947 return AUDCLNT_E_OUT_OF_ORDER
;
2950 if(written_frames
> (This
->getbuf_last
>= 0 ? This
->getbuf_last
: -This
->getbuf_last
)){
2951 LeaveCriticalSection(&This
->lock
);
2952 return AUDCLNT_E_INVALID_SIZE
;
2955 if(This
->getbuf_last
>= 0)
2956 buffer
= This
->local_buffer
+ This
->wri_offs_frames
* This
->fmt
->nBlockAlign
;
2958 buffer
= This
->tmp_buffer
;
2960 if(flags
& AUDCLNT_BUFFERFLAGS_SILENT
)
2961 silence_buffer(This
, buffer
, written_frames
);
2963 if(This
->getbuf_last
< 0)
2964 alsa_wrap_buffer(This
, buffer
, written_frames
);
2966 This
->wri_offs_frames
+= written_frames
;
2967 This
->wri_offs_frames
%= This
->bufsize_frames
;
2968 This
->held_frames
+= written_frames
;
2969 This
->written_frames
+= written_frames
;
2970 This
->getbuf_last
= 0;
2972 LeaveCriticalSection(&This
->lock
);
2977 static const IAudioRenderClientVtbl AudioRenderClient_Vtbl
= {
2978 AudioRenderClient_QueryInterface
,
2979 AudioRenderClient_AddRef
,
2980 AudioRenderClient_Release
,
2981 AudioRenderClient_GetBuffer
,
2982 AudioRenderClient_ReleaseBuffer
2985 static HRESULT WINAPI
AudioCaptureClient_QueryInterface(
2986 IAudioCaptureClient
*iface
, REFIID riid
, void **ppv
)
2988 ACImpl
*This
= impl_from_IAudioCaptureClient(iface
);
2989 TRACE("(%p)->(%s, %p)\n", iface
, debugstr_guid(riid
), ppv
);
2995 if(IsEqualIID(riid
, &IID_IUnknown
) ||
2996 IsEqualIID(riid
, &IID_IAudioCaptureClient
))
2998 else if(IsEqualIID(riid
, &IID_IMarshal
))
2999 return IUnknown_QueryInterface(This
->pUnkFTMarshal
, riid
, ppv
);
3002 IUnknown_AddRef((IUnknown
*)*ppv
);
3006 WARN("Unknown interface %s\n", debugstr_guid(riid
));
3007 return E_NOINTERFACE
;
3010 static ULONG WINAPI
AudioCaptureClient_AddRef(IAudioCaptureClient
*iface
)
3012 ACImpl
*This
= impl_from_IAudioCaptureClient(iface
);
3013 return IAudioClient3_AddRef(&This
->IAudioClient3_iface
);
3016 static ULONG WINAPI
AudioCaptureClient_Release(IAudioCaptureClient
*iface
)
3018 ACImpl
*This
= impl_from_IAudioCaptureClient(iface
);
3019 return IAudioClient3_Release(&This
->IAudioClient3_iface
);
3022 static HRESULT WINAPI
AudioCaptureClient_GetBuffer(IAudioCaptureClient
*iface
,
3023 BYTE
**data
, UINT32
*frames
, DWORD
*flags
, UINT64
*devpos
,
3026 ACImpl
*This
= impl_from_IAudioCaptureClient(iface
);
3028 TRACE("(%p)->(%p, %p, %p, %p, %p)\n", This
, data
, frames
, flags
,
3036 if(!frames
|| !flags
)
3039 EnterCriticalSection(&This
->lock
);
3041 if(This
->getbuf_last
){
3042 LeaveCriticalSection(&This
->lock
);
3043 return AUDCLNT_E_OUT_OF_ORDER
;
3046 /* hr = GetNextPacketSize(iface, frames); */
3047 if(This
->held_frames
< This
->mmdev_period_frames
){
3049 LeaveCriticalSection(&This
->lock
);
3050 return AUDCLNT_S_BUFFER_EMPTY
;
3052 *frames
= This
->mmdev_period_frames
;
3054 if(This
->lcl_offs_frames
+ *frames
> This
->bufsize_frames
){
3055 UINT32 chunk_bytes
, offs_bytes
, frames_bytes
;
3056 if(This
->tmp_buffer_frames
< *frames
){
3057 HeapFree(GetProcessHeap(), 0, This
->tmp_buffer
);
3058 This
->tmp_buffer
= HeapAlloc(GetProcessHeap(), 0,
3059 *frames
* This
->fmt
->nBlockAlign
);
3060 if(!This
->tmp_buffer
){
3061 LeaveCriticalSection(&This
->lock
);
3062 return E_OUTOFMEMORY
;
3064 This
->tmp_buffer_frames
= *frames
;
3067 *data
= This
->tmp_buffer
;
3068 chunk_bytes
= (This
->bufsize_frames
- This
->lcl_offs_frames
) *
3069 This
->fmt
->nBlockAlign
;
3070 offs_bytes
= This
->lcl_offs_frames
* This
->fmt
->nBlockAlign
;
3071 frames_bytes
= *frames
* This
->fmt
->nBlockAlign
;
3072 memcpy(This
->tmp_buffer
, This
->local_buffer
+ offs_bytes
, chunk_bytes
);
3073 memcpy(This
->tmp_buffer
+ chunk_bytes
, This
->local_buffer
,
3074 frames_bytes
- chunk_bytes
);
3076 *data
= This
->local_buffer
+
3077 This
->lcl_offs_frames
* This
->fmt
->nBlockAlign
;
3079 This
->getbuf_last
= *frames
;
3083 *devpos
= This
->written_frames
;
3084 if(qpcpos
){ /* fixme: qpc of recording time */
3085 LARGE_INTEGER stamp
, freq
;
3086 QueryPerformanceCounter(&stamp
);
3087 QueryPerformanceFrequency(&freq
);
3088 *qpcpos
= (stamp
.QuadPart
* (INT64
)10000000) / freq
.QuadPart
;
3091 LeaveCriticalSection(&This
->lock
);
3093 return *frames
? S_OK
: AUDCLNT_S_BUFFER_EMPTY
;
3096 static HRESULT WINAPI
AudioCaptureClient_ReleaseBuffer(
3097 IAudioCaptureClient
*iface
, UINT32 done
)
3099 ACImpl
*This
= impl_from_IAudioCaptureClient(iface
);
3101 TRACE("(%p)->(%u)\n", This
, done
);
3103 EnterCriticalSection(&This
->lock
);
3106 This
->getbuf_last
= 0;
3107 LeaveCriticalSection(&This
->lock
);
3111 if(!This
->getbuf_last
){
3112 LeaveCriticalSection(&This
->lock
);
3113 return AUDCLNT_E_OUT_OF_ORDER
;
3116 if(This
->getbuf_last
!= done
){
3117 LeaveCriticalSection(&This
->lock
);
3118 return AUDCLNT_E_INVALID_SIZE
;
3121 This
->written_frames
+= done
;
3122 This
->held_frames
-= done
;
3123 This
->lcl_offs_frames
+= done
;
3124 This
->lcl_offs_frames
%= This
->bufsize_frames
;
3125 This
->getbuf_last
= 0;
3127 LeaveCriticalSection(&This
->lock
);
3132 static HRESULT WINAPI
AudioCaptureClient_GetNextPacketSize(
3133 IAudioCaptureClient
*iface
, UINT32
*frames
)
3135 ACImpl
*This
= impl_from_IAudioCaptureClient(iface
);
3137 TRACE("(%p)->(%p)\n", This
, frames
);
3142 EnterCriticalSection(&This
->lock
);
3144 *frames
= This
->held_frames
< This
->mmdev_period_frames
? 0 : This
->mmdev_period_frames
;
3146 LeaveCriticalSection(&This
->lock
);
3151 static const IAudioCaptureClientVtbl AudioCaptureClient_Vtbl
=
3153 AudioCaptureClient_QueryInterface
,
3154 AudioCaptureClient_AddRef
,
3155 AudioCaptureClient_Release
,
3156 AudioCaptureClient_GetBuffer
,
3157 AudioCaptureClient_ReleaseBuffer
,
3158 AudioCaptureClient_GetNextPacketSize
3161 static HRESULT WINAPI
AudioClock_QueryInterface(IAudioClock
*iface
,
3162 REFIID riid
, void **ppv
)
3164 ACImpl
*This
= impl_from_IAudioClock(iface
);
3166 TRACE("(%p)->(%s, %p)\n", iface
, debugstr_guid(riid
), ppv
);
3172 if(IsEqualIID(riid
, &IID_IUnknown
) || IsEqualIID(riid
, &IID_IAudioClock
))
3174 else if(IsEqualIID(riid
, &IID_IAudioClock2
))
3175 *ppv
= &This
->IAudioClock2_iface
;
3177 IUnknown_AddRef((IUnknown
*)*ppv
);
3181 WARN("Unknown interface %s\n", debugstr_guid(riid
));
3182 return E_NOINTERFACE
;
3185 static ULONG WINAPI
AudioClock_AddRef(IAudioClock
*iface
)
3187 ACImpl
*This
= impl_from_IAudioClock(iface
);
3188 return IAudioClient3_AddRef(&This
->IAudioClient3_iface
);
3191 static ULONG WINAPI
AudioClock_Release(IAudioClock
*iface
)
3193 ACImpl
*This
= impl_from_IAudioClock(iface
);
3194 return IAudioClient3_Release(&This
->IAudioClient3_iface
);
3197 static HRESULT WINAPI
AudioClock_GetFrequency(IAudioClock
*iface
, UINT64
*freq
)
3199 ACImpl
*This
= impl_from_IAudioClock(iface
);
3201 TRACE("(%p)->(%p)\n", This
, freq
);
3203 if(This
->share
== AUDCLNT_SHAREMODE_SHARED
)
3204 *freq
= (UINT64
)This
->fmt
->nSamplesPerSec
* This
->fmt
->nBlockAlign
;
3206 *freq
= This
->fmt
->nSamplesPerSec
;
3211 static HRESULT WINAPI
AudioClock_GetPosition(IAudioClock
*iface
, UINT64
*pos
,
3214 ACImpl
*This
= impl_from_IAudioClock(iface
);
3216 snd_pcm_state_t alsa_state
;
3218 TRACE("(%p)->(%p, %p)\n", This
, pos
, qpctime
);
3223 EnterCriticalSection(&This
->lock
);
3225 /* avail_update required to get accurate snd_pcm_state() */
3226 snd_pcm_avail_update(This
->pcm_handle
);
3227 alsa_state
= snd_pcm_state(This
->pcm_handle
);
3229 if(This
->dataflow
== eRender
){
3230 position
= This
->written_frames
- This
->held_frames
;
3232 if(This
->started
&& alsa_state
== SND_PCM_STATE_RUNNING
&& This
->held_frames
)
3233 /* we should be using snd_pcm_delay here, but it is broken
3234 * especially during ALSA device underrun. instead, let's just
3235 * interpolate between periods with the system timer. */
3236 position
+= interp_elapsed_frames(This
);
3238 position
= min(position
, This
->written_frames
- This
->held_frames
+ This
->mmdev_period_frames
);
3240 position
= min(position
, This
->written_frames
);
3242 position
= This
->written_frames
+ This
->held_frames
;
3244 /* ensure monotic growth */
3245 if(position
< This
->last_pos_frames
)
3246 position
= This
->last_pos_frames
;
3248 This
->last_pos_frames
= position
;
3250 TRACE("frames written: %u, held: %u, state: 0x%x, position: %u\n",
3251 (UINT32
)(This
->written_frames
%1000000000), This
->held_frames
,
3252 alsa_state
, (UINT32
)(position
%1000000000));
3254 LeaveCriticalSection(&This
->lock
);
3256 if(This
->share
== AUDCLNT_SHAREMODE_SHARED
)
3257 *pos
= position
* This
->fmt
->nBlockAlign
;
3262 LARGE_INTEGER stamp
, freq
;
3263 QueryPerformanceCounter(&stamp
);
3264 QueryPerformanceFrequency(&freq
);
3265 *qpctime
= (stamp
.QuadPart
* (INT64
)10000000) / freq
.QuadPart
;
3271 static HRESULT WINAPI
AudioClock_GetCharacteristics(IAudioClock
*iface
,
3274 ACImpl
*This
= impl_from_IAudioClock(iface
);
3276 TRACE("(%p)->(%p)\n", This
, chars
);
3281 *chars
= AUDIOCLOCK_CHARACTERISTIC_FIXED_FREQ
;
3286 static const IAudioClockVtbl AudioClock_Vtbl
=
3288 AudioClock_QueryInterface
,
3291 AudioClock_GetFrequency
,
3292 AudioClock_GetPosition
,
3293 AudioClock_GetCharacteristics
3296 static HRESULT WINAPI
AudioClock2_QueryInterface(IAudioClock2
*iface
,
3297 REFIID riid
, void **ppv
)
3299 ACImpl
*This
= impl_from_IAudioClock2(iface
);
3300 return IAudioClock_QueryInterface(&This
->IAudioClock_iface
, riid
, ppv
);
3303 static ULONG WINAPI
AudioClock2_AddRef(IAudioClock2
*iface
)
3305 ACImpl
*This
= impl_from_IAudioClock2(iface
);
3306 return IAudioClient3_AddRef(&This
->IAudioClient3_iface
);
3309 static ULONG WINAPI
AudioClock2_Release(IAudioClock2
*iface
)
3311 ACImpl
*This
= impl_from_IAudioClock2(iface
);
3312 return IAudioClient3_Release(&This
->IAudioClient3_iface
);
3315 static HRESULT WINAPI
AudioClock2_GetDevicePosition(IAudioClock2
*iface
,
3316 UINT64
*pos
, UINT64
*qpctime
)
3318 ACImpl
*This
= impl_from_IAudioClock2(iface
);
3320 FIXME("(%p)->(%p, %p)\n", This
, pos
, qpctime
);
3325 static const IAudioClock2Vtbl AudioClock2_Vtbl
=
3327 AudioClock2_QueryInterface
,
3329 AudioClock2_Release
,
3330 AudioClock2_GetDevicePosition
3333 static AudioSessionWrapper
*AudioSessionWrapper_Create(ACImpl
*client
)
3335 AudioSessionWrapper
*ret
;
3337 ret
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
,
3338 sizeof(AudioSessionWrapper
));
3342 ret
->IAudioSessionControl2_iface
.lpVtbl
= &AudioSessionControl2_Vtbl
;
3343 ret
->ISimpleAudioVolume_iface
.lpVtbl
= &SimpleAudioVolume_Vtbl
;
3344 ret
->IChannelAudioVolume_iface
.lpVtbl
= &ChannelAudioVolume_Vtbl
;
3348 ret
->client
= client
;
3350 ret
->session
= client
->session
;
3351 AudioClient_AddRef(&client
->IAudioClient3_iface
);
3357 static HRESULT WINAPI
AudioSessionControl_QueryInterface(
3358 IAudioSessionControl2
*iface
, REFIID riid
, void **ppv
)
3360 TRACE("(%p)->(%s, %p)\n", iface
, debugstr_guid(riid
), ppv
);
3366 if(IsEqualIID(riid
, &IID_IUnknown
) ||
3367 IsEqualIID(riid
, &IID_IAudioSessionControl
) ||
3368 IsEqualIID(riid
, &IID_IAudioSessionControl2
))
3371 IUnknown_AddRef((IUnknown
*)*ppv
);
3375 WARN("Unknown interface %s\n", debugstr_guid(riid
));
3376 return E_NOINTERFACE
;
3379 static ULONG WINAPI
AudioSessionControl_AddRef(IAudioSessionControl2
*iface
)
3381 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
3383 ref
= InterlockedIncrement(&This
->ref
);
3384 TRACE("(%p) Refcount now %u\n", This
, ref
);
3388 static ULONG WINAPI
AudioSessionControl_Release(IAudioSessionControl2
*iface
)
3390 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
3392 ref
= InterlockedDecrement(&This
->ref
);
3393 TRACE("(%p) Refcount now %u\n", This
, ref
);
3396 EnterCriticalSection(&This
->client
->lock
);
3397 This
->client
->session_wrapper
= NULL
;
3398 LeaveCriticalSection(&This
->client
->lock
);
3399 AudioClient_Release(&This
->client
->IAudioClient3_iface
);
3401 HeapFree(GetProcessHeap(), 0, This
);
3406 static HRESULT WINAPI
AudioSessionControl_GetState(IAudioSessionControl2
*iface
,
3407 AudioSessionState
*state
)
3409 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
3412 TRACE("(%p)->(%p)\n", This
, state
);
3415 return NULL_PTR_ERR
;
3417 EnterCriticalSection(&g_sessions_lock
);
3419 if(list_empty(&This
->session
->clients
)){
3420 *state
= AudioSessionStateExpired
;
3421 LeaveCriticalSection(&g_sessions_lock
);
3425 LIST_FOR_EACH_ENTRY(client
, &This
->session
->clients
, ACImpl
, entry
){
3426 EnterCriticalSection(&client
->lock
);
3427 if(client
->started
){
3428 *state
= AudioSessionStateActive
;
3429 LeaveCriticalSection(&client
->lock
);
3430 LeaveCriticalSection(&g_sessions_lock
);
3433 LeaveCriticalSection(&client
->lock
);
3436 LeaveCriticalSection(&g_sessions_lock
);
3438 *state
= AudioSessionStateInactive
;
3443 static HRESULT WINAPI
AudioSessionControl_GetDisplayName(
3444 IAudioSessionControl2
*iface
, WCHAR
**name
)
3446 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
3448 FIXME("(%p)->(%p) - stub\n", This
, name
);
3453 static HRESULT WINAPI
AudioSessionControl_SetDisplayName(
3454 IAudioSessionControl2
*iface
, const WCHAR
*name
, const GUID
*session
)
3456 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
3458 FIXME("(%p)->(%p, %s) - stub\n", This
, name
, debugstr_guid(session
));
3463 static HRESULT WINAPI
AudioSessionControl_GetIconPath(
3464 IAudioSessionControl2
*iface
, WCHAR
**path
)
3466 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
3468 FIXME("(%p)->(%p) - stub\n", This
, path
);
3473 static HRESULT WINAPI
AudioSessionControl_SetIconPath(
3474 IAudioSessionControl2
*iface
, const WCHAR
*path
, const GUID
*session
)
3476 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
3478 FIXME("(%p)->(%p, %s) - stub\n", This
, path
, debugstr_guid(session
));
3483 static HRESULT WINAPI
AudioSessionControl_GetGroupingParam(
3484 IAudioSessionControl2
*iface
, GUID
*group
)
3486 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
3488 FIXME("(%p)->(%p) - stub\n", This
, group
);
3493 static HRESULT WINAPI
AudioSessionControl_SetGroupingParam(
3494 IAudioSessionControl2
*iface
, const GUID
*group
, const GUID
*session
)
3496 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
3498 FIXME("(%p)->(%s, %s) - stub\n", This
, debugstr_guid(group
),
3499 debugstr_guid(session
));
3504 static HRESULT WINAPI
AudioSessionControl_RegisterAudioSessionNotification(
3505 IAudioSessionControl2
*iface
, IAudioSessionEvents
*events
)
3507 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
3509 FIXME("(%p)->(%p) - stub\n", This
, events
);
3514 static HRESULT WINAPI
AudioSessionControl_UnregisterAudioSessionNotification(
3515 IAudioSessionControl2
*iface
, IAudioSessionEvents
*events
)
3517 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
3519 FIXME("(%p)->(%p) - stub\n", This
, events
);
3524 static HRESULT WINAPI
AudioSessionControl_GetSessionIdentifier(
3525 IAudioSessionControl2
*iface
, WCHAR
**id
)
3527 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
3529 FIXME("(%p)->(%p) - stub\n", This
, id
);
3534 static HRESULT WINAPI
AudioSessionControl_GetSessionInstanceIdentifier(
3535 IAudioSessionControl2
*iface
, WCHAR
**id
)
3537 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
3539 FIXME("(%p)->(%p) - stub\n", This
, id
);
3544 static HRESULT WINAPI
AudioSessionControl_GetProcessId(
3545 IAudioSessionControl2
*iface
, DWORD
*pid
)
3547 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
3549 TRACE("(%p)->(%p)\n", This
, pid
);
3554 *pid
= GetCurrentProcessId();
3559 static HRESULT WINAPI
AudioSessionControl_IsSystemSoundsSession(
3560 IAudioSessionControl2
*iface
)
3562 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
3564 TRACE("(%p)\n", This
);
3569 static HRESULT WINAPI
AudioSessionControl_SetDuckingPreference(
3570 IAudioSessionControl2
*iface
, BOOL optout
)
3572 AudioSessionWrapper
*This
= impl_from_IAudioSessionControl2(iface
);
3574 TRACE("(%p)->(%d)\n", This
, optout
);
3579 static const IAudioSessionControl2Vtbl AudioSessionControl2_Vtbl
=
3581 AudioSessionControl_QueryInterface
,
3582 AudioSessionControl_AddRef
,
3583 AudioSessionControl_Release
,
3584 AudioSessionControl_GetState
,
3585 AudioSessionControl_GetDisplayName
,
3586 AudioSessionControl_SetDisplayName
,
3587 AudioSessionControl_GetIconPath
,
3588 AudioSessionControl_SetIconPath
,
3589 AudioSessionControl_GetGroupingParam
,
3590 AudioSessionControl_SetGroupingParam
,
3591 AudioSessionControl_RegisterAudioSessionNotification
,
3592 AudioSessionControl_UnregisterAudioSessionNotification
,
3593 AudioSessionControl_GetSessionIdentifier
,
3594 AudioSessionControl_GetSessionInstanceIdentifier
,
3595 AudioSessionControl_GetProcessId
,
3596 AudioSessionControl_IsSystemSoundsSession
,
3597 AudioSessionControl_SetDuckingPreference
3600 static HRESULT WINAPI
SimpleAudioVolume_QueryInterface(
3601 ISimpleAudioVolume
*iface
, REFIID riid
, void **ppv
)
3603 TRACE("(%p)->(%s, %p)\n", iface
, debugstr_guid(riid
), ppv
);
3609 if(IsEqualIID(riid
, &IID_IUnknown
) ||
3610 IsEqualIID(riid
, &IID_ISimpleAudioVolume
))
3613 IUnknown_AddRef((IUnknown
*)*ppv
);
3617 WARN("Unknown interface %s\n", debugstr_guid(riid
));
3618 return E_NOINTERFACE
;
3621 static ULONG WINAPI
SimpleAudioVolume_AddRef(ISimpleAudioVolume
*iface
)
3623 AudioSessionWrapper
*This
= impl_from_ISimpleAudioVolume(iface
);
3624 return AudioSessionControl_AddRef(&This
->IAudioSessionControl2_iface
);
3627 static ULONG WINAPI
SimpleAudioVolume_Release(ISimpleAudioVolume
*iface
)
3629 AudioSessionWrapper
*This
= impl_from_ISimpleAudioVolume(iface
);
3630 return AudioSessionControl_Release(&This
->IAudioSessionControl2_iface
);
3633 static HRESULT WINAPI
SimpleAudioVolume_SetMasterVolume(
3634 ISimpleAudioVolume
*iface
, float level
, const GUID
*context
)
3636 AudioSessionWrapper
*This
= impl_from_ISimpleAudioVolume(iface
);
3637 AudioSession
*session
= This
->session
;
3639 TRACE("(%p)->(%f, %s)\n", session
, level
, wine_dbgstr_guid(context
));
3641 if(level
< 0.f
|| level
> 1.f
)
3642 return E_INVALIDARG
;
3645 FIXME("Notifications not supported yet\n");
3647 TRACE("ALSA does not support volume control\n");
3649 EnterCriticalSection(&session
->lock
);
3651 session
->master_vol
= level
;
3653 LeaveCriticalSection(&session
->lock
);
3658 static HRESULT WINAPI
SimpleAudioVolume_GetMasterVolume(
3659 ISimpleAudioVolume
*iface
, float *level
)
3661 AudioSessionWrapper
*This
= impl_from_ISimpleAudioVolume(iface
);
3662 AudioSession
*session
= This
->session
;
3664 TRACE("(%p)->(%p)\n", session
, level
);
3667 return NULL_PTR_ERR
;
3669 *level
= session
->master_vol
;
3674 static HRESULT WINAPI
SimpleAudioVolume_SetMute(ISimpleAudioVolume
*iface
,
3675 BOOL mute
, const GUID
*context
)
3677 AudioSessionWrapper
*This
= impl_from_ISimpleAudioVolume(iface
);
3678 AudioSession
*session
= This
->session
;
3680 TRACE("(%p)->(%u, %s)\n", session
, mute
, debugstr_guid(context
));
3683 FIXME("Notifications not supported yet\n");
3685 session
->mute
= mute
;
3690 static HRESULT WINAPI
SimpleAudioVolume_GetMute(ISimpleAudioVolume
*iface
,
3693 AudioSessionWrapper
*This
= impl_from_ISimpleAudioVolume(iface
);
3694 AudioSession
*session
= This
->session
;
3696 TRACE("(%p)->(%p)\n", session
, mute
);
3699 return NULL_PTR_ERR
;
3701 *mute
= session
->mute
;
3706 static const ISimpleAudioVolumeVtbl SimpleAudioVolume_Vtbl
=
3708 SimpleAudioVolume_QueryInterface
,
3709 SimpleAudioVolume_AddRef
,
3710 SimpleAudioVolume_Release
,
3711 SimpleAudioVolume_SetMasterVolume
,
3712 SimpleAudioVolume_GetMasterVolume
,
3713 SimpleAudioVolume_SetMute
,
3714 SimpleAudioVolume_GetMute
3717 static HRESULT WINAPI
AudioStreamVolume_QueryInterface(
3718 IAudioStreamVolume
*iface
, REFIID riid
, void **ppv
)
3720 TRACE("(%p)->(%s, %p)\n", iface
, debugstr_guid(riid
), ppv
);
3726 if(IsEqualIID(riid
, &IID_IUnknown
) ||
3727 IsEqualIID(riid
, &IID_IAudioStreamVolume
))
3730 IUnknown_AddRef((IUnknown
*)*ppv
);
3734 WARN("Unknown interface %s\n", debugstr_guid(riid
));
3735 return E_NOINTERFACE
;
3738 static ULONG WINAPI
AudioStreamVolume_AddRef(IAudioStreamVolume
*iface
)
3740 ACImpl
*This
= impl_from_IAudioStreamVolume(iface
);
3741 return IAudioClient3_AddRef(&This
->IAudioClient3_iface
);
3744 static ULONG WINAPI
AudioStreamVolume_Release(IAudioStreamVolume
*iface
)
3746 ACImpl
*This
= impl_from_IAudioStreamVolume(iface
);
3747 return IAudioClient3_Release(&This
->IAudioClient3_iface
);
3750 static HRESULT WINAPI
AudioStreamVolume_GetChannelCount(
3751 IAudioStreamVolume
*iface
, UINT32
*out
)
3753 ACImpl
*This
= impl_from_IAudioStreamVolume(iface
);
3755 TRACE("(%p)->(%p)\n", This
, out
);
3760 *out
= This
->fmt
->nChannels
;
3765 static HRESULT WINAPI
AudioStreamVolume_SetChannelVolume(
3766 IAudioStreamVolume
*iface
, UINT32 index
, float level
)
3768 ACImpl
*This
= impl_from_IAudioStreamVolume(iface
);
3770 TRACE("(%p)->(%d, %f)\n", This
, index
, level
);
3772 if(level
< 0.f
|| level
> 1.f
)
3773 return E_INVALIDARG
;
3775 if(index
>= This
->fmt
->nChannels
)
3776 return E_INVALIDARG
;
3778 TRACE("ALSA does not support volume control\n");
3780 EnterCriticalSection(&This
->lock
);
3782 This
->vols
[index
] = level
;
3784 LeaveCriticalSection(&This
->lock
);
3789 static HRESULT WINAPI
AudioStreamVolume_GetChannelVolume(
3790 IAudioStreamVolume
*iface
, UINT32 index
, float *level
)
3792 ACImpl
*This
= impl_from_IAudioStreamVolume(iface
);
3794 TRACE("(%p)->(%d, %p)\n", This
, index
, level
);
3799 if(index
>= This
->fmt
->nChannels
)
3800 return E_INVALIDARG
;
3802 *level
= This
->vols
[index
];
3807 static HRESULT WINAPI
AudioStreamVolume_SetAllVolumes(
3808 IAudioStreamVolume
*iface
, UINT32 count
, const float *levels
)
3810 ACImpl
*This
= impl_from_IAudioStreamVolume(iface
);
3813 TRACE("(%p)->(%d, %p)\n", This
, count
, levels
);
3818 if(count
!= This
->fmt
->nChannels
)
3819 return E_INVALIDARG
;
3821 TRACE("ALSA does not support volume control\n");
3823 EnterCriticalSection(&This
->lock
);
3825 for(i
= 0; i
< count
; ++i
)
3826 This
->vols
[i
] = levels
[i
];
3828 LeaveCriticalSection(&This
->lock
);
3833 static HRESULT WINAPI
AudioStreamVolume_GetAllVolumes(
3834 IAudioStreamVolume
*iface
, UINT32 count
, float *levels
)
3836 ACImpl
*This
= impl_from_IAudioStreamVolume(iface
);
3839 TRACE("(%p)->(%d, %p)\n", This
, count
, levels
);
3844 if(count
!= This
->fmt
->nChannels
)
3845 return E_INVALIDARG
;
3847 EnterCriticalSection(&This
->lock
);
3849 for(i
= 0; i
< count
; ++i
)
3850 levels
[i
] = This
->vols
[i
];
3852 LeaveCriticalSection(&This
->lock
);
3857 static const IAudioStreamVolumeVtbl AudioStreamVolume_Vtbl
=
3859 AudioStreamVolume_QueryInterface
,
3860 AudioStreamVolume_AddRef
,
3861 AudioStreamVolume_Release
,
3862 AudioStreamVolume_GetChannelCount
,
3863 AudioStreamVolume_SetChannelVolume
,
3864 AudioStreamVolume_GetChannelVolume
,
3865 AudioStreamVolume_SetAllVolumes
,
3866 AudioStreamVolume_GetAllVolumes
3869 static HRESULT WINAPI
ChannelAudioVolume_QueryInterface(
3870 IChannelAudioVolume
*iface
, REFIID riid
, void **ppv
)
3872 TRACE("(%p)->(%s, %p)\n", iface
, debugstr_guid(riid
), ppv
);
3878 if(IsEqualIID(riid
, &IID_IUnknown
) ||
3879 IsEqualIID(riid
, &IID_IChannelAudioVolume
))
3882 IUnknown_AddRef((IUnknown
*)*ppv
);
3886 WARN("Unknown interface %s\n", debugstr_guid(riid
));
3887 return E_NOINTERFACE
;
3890 static ULONG WINAPI
ChannelAudioVolume_AddRef(IChannelAudioVolume
*iface
)
3892 AudioSessionWrapper
*This
= impl_from_IChannelAudioVolume(iface
);
3893 return AudioSessionControl_AddRef(&This
->IAudioSessionControl2_iface
);
3896 static ULONG WINAPI
ChannelAudioVolume_Release(IChannelAudioVolume
*iface
)
3898 AudioSessionWrapper
*This
= impl_from_IChannelAudioVolume(iface
);
3899 return AudioSessionControl_Release(&This
->IAudioSessionControl2_iface
);
3902 static HRESULT WINAPI
ChannelAudioVolume_GetChannelCount(
3903 IChannelAudioVolume
*iface
, UINT32
*out
)
3905 AudioSessionWrapper
*This
= impl_from_IChannelAudioVolume(iface
);
3906 AudioSession
*session
= This
->session
;
3908 TRACE("(%p)->(%p)\n", session
, out
);
3911 return NULL_PTR_ERR
;
3913 *out
= session
->channel_count
;
3918 static HRESULT WINAPI
ChannelAudioVolume_SetChannelVolume(
3919 IChannelAudioVolume
*iface
, UINT32 index
, float level
,
3920 const GUID
*context
)
3922 AudioSessionWrapper
*This
= impl_from_IChannelAudioVolume(iface
);
3923 AudioSession
*session
= This
->session
;
3925 TRACE("(%p)->(%d, %f, %s)\n", session
, index
, level
,
3926 wine_dbgstr_guid(context
));
3928 if(level
< 0.f
|| level
> 1.f
)
3929 return E_INVALIDARG
;
3931 if(index
>= session
->channel_count
)
3932 return E_INVALIDARG
;
3935 FIXME("Notifications not supported yet\n");
3937 TRACE("ALSA does not support volume control\n");
3939 EnterCriticalSection(&session
->lock
);
3941 session
->channel_vols
[index
] = level
;
3943 LeaveCriticalSection(&session
->lock
);
3948 static HRESULT WINAPI
ChannelAudioVolume_GetChannelVolume(
3949 IChannelAudioVolume
*iface
, UINT32 index
, float *level
)
3951 AudioSessionWrapper
*This
= impl_from_IChannelAudioVolume(iface
);
3952 AudioSession
*session
= This
->session
;
3954 TRACE("(%p)->(%d, %p)\n", session
, index
, level
);
3957 return NULL_PTR_ERR
;
3959 if(index
>= session
->channel_count
)
3960 return E_INVALIDARG
;
3962 *level
= session
->channel_vols
[index
];
3967 static HRESULT WINAPI
ChannelAudioVolume_SetAllVolumes(
3968 IChannelAudioVolume
*iface
, UINT32 count
, const float *levels
,
3969 const GUID
*context
)
3971 AudioSessionWrapper
*This
= impl_from_IChannelAudioVolume(iface
);
3972 AudioSession
*session
= This
->session
;
3975 TRACE("(%p)->(%d, %p, %s)\n", session
, count
, levels
,
3976 wine_dbgstr_guid(context
));
3979 return NULL_PTR_ERR
;
3981 if(count
!= session
->channel_count
)
3982 return E_INVALIDARG
;
3985 FIXME("Notifications not supported yet\n");
3987 TRACE("ALSA does not support volume control\n");
3989 EnterCriticalSection(&session
->lock
);
3991 for(i
= 0; i
< count
; ++i
)
3992 session
->channel_vols
[i
] = levels
[i
];
3994 LeaveCriticalSection(&session
->lock
);
3999 static HRESULT WINAPI
ChannelAudioVolume_GetAllVolumes(
4000 IChannelAudioVolume
*iface
, UINT32 count
, float *levels
)
4002 AudioSessionWrapper
*This
= impl_from_IChannelAudioVolume(iface
);
4003 AudioSession
*session
= This
->session
;
4006 TRACE("(%p)->(%d, %p)\n", session
, count
, levels
);
4009 return NULL_PTR_ERR
;
4011 if(count
!= session
->channel_count
)
4012 return E_INVALIDARG
;
4014 for(i
= 0; i
< count
; ++i
)
4015 levels
[i
] = session
->channel_vols
[i
];
4020 static const IChannelAudioVolumeVtbl ChannelAudioVolume_Vtbl
=
4022 ChannelAudioVolume_QueryInterface
,
4023 ChannelAudioVolume_AddRef
,
4024 ChannelAudioVolume_Release
,
4025 ChannelAudioVolume_GetChannelCount
,
4026 ChannelAudioVolume_SetChannelVolume
,
4027 ChannelAudioVolume_GetChannelVolume
,
4028 ChannelAudioVolume_SetAllVolumes
,
4029 ChannelAudioVolume_GetAllVolumes
4032 static HRESULT WINAPI
AudioSessionManager_QueryInterface(IAudioSessionManager2
*iface
,
4033 REFIID riid
, void **ppv
)
4035 TRACE("(%p)->(%s, %p)\n", iface
, debugstr_guid(riid
), ppv
);
4041 if(IsEqualIID(riid
, &IID_IUnknown
) ||
4042 IsEqualIID(riid
, &IID_IAudioSessionManager
) ||
4043 IsEqualIID(riid
, &IID_IAudioSessionManager2
))
4046 IUnknown_AddRef((IUnknown
*)*ppv
);
4050 WARN("Unknown interface %s\n", debugstr_guid(riid
));
4051 return E_NOINTERFACE
;
4054 static ULONG WINAPI
AudioSessionManager_AddRef(IAudioSessionManager2
*iface
)
4056 SessionMgr
*This
= impl_from_IAudioSessionManager2(iface
);
4058 ref
= InterlockedIncrement(&This
->ref
);
4059 TRACE("(%p) Refcount now %u\n", This
, ref
);
4063 static ULONG WINAPI
AudioSessionManager_Release(IAudioSessionManager2
*iface
)
4065 SessionMgr
*This
= impl_from_IAudioSessionManager2(iface
);
4067 ref
= InterlockedDecrement(&This
->ref
);
4068 TRACE("(%p) Refcount now %u\n", This
, ref
);
4070 HeapFree(GetProcessHeap(), 0, This
);
4074 static HRESULT WINAPI
AudioSessionManager_GetAudioSessionControl(
4075 IAudioSessionManager2
*iface
, const GUID
*session_guid
, DWORD flags
,
4076 IAudioSessionControl
**out
)
4078 SessionMgr
*This
= impl_from_IAudioSessionManager2(iface
);
4079 AudioSession
*session
;
4080 AudioSessionWrapper
*wrapper
;
4083 TRACE("(%p)->(%s, %x, %p)\n", This
, debugstr_guid(session_guid
),
4086 hr
= get_audio_session(session_guid
, This
->device
, 0, &session
);
4090 wrapper
= AudioSessionWrapper_Create(NULL
);
4092 return E_OUTOFMEMORY
;
4094 wrapper
->session
= session
;
4096 *out
= (IAudioSessionControl
*)&wrapper
->IAudioSessionControl2_iface
;
4101 static HRESULT WINAPI
AudioSessionManager_GetSimpleAudioVolume(
4102 IAudioSessionManager2
*iface
, const GUID
*session_guid
, DWORD flags
,
4103 ISimpleAudioVolume
**out
)
4105 SessionMgr
*This
= impl_from_IAudioSessionManager2(iface
);
4106 AudioSession
*session
;
4107 AudioSessionWrapper
*wrapper
;
4110 TRACE("(%p)->(%s, %x, %p)\n", This
, debugstr_guid(session_guid
),
4113 hr
= get_audio_session(session_guid
, This
->device
, 0, &session
);
4117 wrapper
= AudioSessionWrapper_Create(NULL
);
4119 return E_OUTOFMEMORY
;
4121 wrapper
->session
= session
;
4123 *out
= &wrapper
->ISimpleAudioVolume_iface
;
4128 static HRESULT WINAPI
AudioSessionManager_GetSessionEnumerator(
4129 IAudioSessionManager2
*iface
, IAudioSessionEnumerator
**out
)
4131 SessionMgr
*This
= impl_from_IAudioSessionManager2(iface
);
4132 FIXME("(%p)->(%p) - stub\n", This
, out
);
4136 static HRESULT WINAPI
AudioSessionManager_RegisterSessionNotification(
4137 IAudioSessionManager2
*iface
, IAudioSessionNotification
*notification
)
4139 SessionMgr
*This
= impl_from_IAudioSessionManager2(iface
);
4140 FIXME("(%p)->(%p) - stub\n", This
, notification
);
4144 static HRESULT WINAPI
AudioSessionManager_UnregisterSessionNotification(
4145 IAudioSessionManager2
*iface
, IAudioSessionNotification
*notification
)
4147 SessionMgr
*This
= impl_from_IAudioSessionManager2(iface
);
4148 FIXME("(%p)->(%p) - stub\n", This
, notification
);
4152 static HRESULT WINAPI
AudioSessionManager_RegisterDuckNotification(
4153 IAudioSessionManager2
*iface
, const WCHAR
*session_id
,
4154 IAudioVolumeDuckNotification
*notification
)
4156 SessionMgr
*This
= impl_from_IAudioSessionManager2(iface
);
4157 FIXME("(%p)->(%p) - stub\n", This
, notification
);
4161 static HRESULT WINAPI
AudioSessionManager_UnregisterDuckNotification(
4162 IAudioSessionManager2
*iface
,
4163 IAudioVolumeDuckNotification
*notification
)
4165 SessionMgr
*This
= impl_from_IAudioSessionManager2(iface
);
4166 FIXME("(%p)->(%p) - stub\n", This
, notification
);
4170 static const IAudioSessionManager2Vtbl AudioSessionManager2_Vtbl
=
4172 AudioSessionManager_QueryInterface
,
4173 AudioSessionManager_AddRef
,
4174 AudioSessionManager_Release
,
4175 AudioSessionManager_GetAudioSessionControl
,
4176 AudioSessionManager_GetSimpleAudioVolume
,
4177 AudioSessionManager_GetSessionEnumerator
,
4178 AudioSessionManager_RegisterSessionNotification
,
4179 AudioSessionManager_UnregisterSessionNotification
,
4180 AudioSessionManager_RegisterDuckNotification
,
4181 AudioSessionManager_UnregisterDuckNotification
4184 HRESULT WINAPI
AUDDRV_GetAudioSessionManager(IMMDevice
*device
,
4185 IAudioSessionManager2
**out
)
4189 This
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(SessionMgr
));
4191 return E_OUTOFMEMORY
;
4193 This
->IAudioSessionManager2_iface
.lpVtbl
= &AudioSessionManager2_Vtbl
;
4194 This
->device
= device
;
4197 *out
= &This
->IAudioSessionManager2_iface
;
4202 static unsigned int alsa_probe_num_speakers(char *name
) {
4204 snd_pcm_hw_params_t
*params
;
4206 unsigned int max_channels
= 0;
4208 if ((err
= snd_pcm_open(&handle
, name
, SND_PCM_STREAM_PLAYBACK
, SND_PCM_NONBLOCK
)) < 0) {
4209 WARN("The device \"%s\" failed to open: %d (%s).\n",
4210 name
, err
, snd_strerror(err
));
4214 params
= HeapAlloc(GetProcessHeap(), 0, snd_pcm_hw_params_sizeof());
4216 WARN("Out of memory.\n");
4217 snd_pcm_close(handle
);
4221 if ((err
= snd_pcm_hw_params_any(handle
, params
)) < 0) {
4222 WARN("snd_pcm_hw_params_any failed for \"%s\": %d (%s).\n",
4223 name
, err
, snd_strerror(err
));
4227 if ((err
= snd_pcm_hw_params_get_channels_max(params
,
4228 &max_channels
)) < 0){
4229 WARN("Unable to get max channels: %d (%s)\n", err
, snd_strerror(err
));
4234 HeapFree(GetProcessHeap(), 0, params
);
4235 snd_pcm_close(handle
);
4237 return max_channels
;
4240 enum AudioDeviceConnectionType
{
4241 AudioDeviceConnectionType_Unknown
= 0,
4242 AudioDeviceConnectionType_PCI
,
4243 AudioDeviceConnectionType_USB
4246 HRESULT WINAPI
AUDDRV_GetPropValue(GUID
*guid
, const PROPERTYKEY
*prop
, PROPVARIANT
*out
)
4251 static const PROPERTYKEY devicepath_key
= { /* undocumented? - {b3f8fa53-0004-438e-9003-51a46e139bfc},2 */
4252 {0xb3f8fa53, 0x0004, 0x438e, {0x90, 0x03, 0x51, 0xa4, 0x6e, 0x13, 0x9b, 0xfc}}, 2
4255 TRACE("%s, (%s,%u), %p\n", wine_dbgstr_guid(guid
), wine_dbgstr_guid(&prop
->fmtid
), prop
->pid
, out
);
4257 if(!get_alsa_name_by_guid(guid
, name
, sizeof(name
), &flow
))
4259 WARN("Unknown interface %s\n", debugstr_guid(guid
));
4260 return E_NOINTERFACE
;
4263 if(IsEqualPropertyKey(*prop
, devicepath_key
))
4265 char uevent
[MAX_PATH
];
4269 /* only implemented for identifiable devices, i.e. not "default" */
4270 if(!sscanf(name
, "plughw:%u,%u", &card
, &device
))
4273 sprintf(uevent
, "/sys/class/sound/card%u/device/uevent", card
);
4274 fuevent
= fopen(uevent
, "r");
4277 enum AudioDeviceConnectionType connection
= AudioDeviceConnectionType_Unknown
;
4278 USHORT vendor_id
= 0, product_id
= 0;
4281 while (fgets(line
, sizeof(line
), fuevent
)) {
4285 if((val
= strchr(line
, '='))) {
4289 val_len
= strlen(val
);
4290 if(val_len
> 0 && val
[val_len
- 1] == '\n') { val
[val_len
- 1] = 0; }
4292 if(!strcmp(line
, "PCI_ID")){
4293 connection
= AudioDeviceConnectionType_PCI
;
4294 if(sscanf(val
, "%hX:%hX", &vendor_id
, &product_id
)<2){
4295 WARN("Unexpected input when reading PCI_ID in uevent file.\n");
4296 connection
= AudioDeviceConnectionType_Unknown
;
4299 }else if(!strcmp(line
, "DEVTYPE") && !strcmp(val
,"usb_interface"))
4300 connection
= AudioDeviceConnectionType_USB
;
4301 else if(!strcmp(line
, "PRODUCT"))
4302 if(sscanf(val
, "%hx/%hx/", &vendor_id
, &product_id
)<2){
4303 WARN("Unexpected input when reading PRODUCT in uevent file.\n");
4304 connection
= AudioDeviceConnectionType_Unknown
;
4312 if(connection
== AudioDeviceConnectionType_USB
|| connection
== AudioDeviceConnectionType_PCI
){
4313 static const WCHAR usbformatW
[] = { '{','1','}','.','U','S','B','\\','V','I','D','_',
4314 '%','0','4','X','&','P','I','D','_','%','0','4','X','\\',
4315 '%','u','&','%','0','8','X',0 }; /* "{1}.USB\VID_%04X&PID_%04X\%u&%08X" */
4316 static const WCHAR pciformatW
[] = { '{','1','}','.','H','D','A','U','D','I','O','\\','F','U','N','C','_','0','1','&',
4317 'V','E','N','_','%','0','4','X','&','D','E','V','_',
4318 '%','0','4','X','\\','%','u','&','%','0','8','X',0 }; /* "{1}.HDAUDIO\FUNC_01&VEN_%04X&DEV_%04X\%u&%08X" */
4321 /* As hardly any audio devices have serial numbers, Windows instead
4322 appears to use a persistent random number. We emulate this here
4323 by instead using the last 8 hex digits of the GUID. */
4324 serial_number
= (guid
->Data4
[4] << 24) | (guid
->Data4
[5] << 16) | (guid
->Data4
[6] << 8) | guid
->Data4
[7];
4326 out
->vt
= VT_LPWSTR
;
4327 out
->pwszVal
= CoTaskMemAlloc(128 * sizeof(WCHAR
));
4330 return E_OUTOFMEMORY
;
4332 if(connection
== AudioDeviceConnectionType_USB
)
4333 sprintfW( out
->pwszVal
, usbformatW
, vendor_id
, product_id
, device
, serial_number
);
4334 else if(connection
== AudioDeviceConnectionType_PCI
)
4335 sprintfW( out
->pwszVal
, pciformatW
, vendor_id
, product_id
, device
, serial_number
);
4340 WARN("Could not open %s for reading\n", uevent
);
4343 } else if (flow
!= eCapture
&& IsEqualPropertyKey(*prop
, PKEY_AudioEndpoint_PhysicalSpeakers
)) {
4344 unsigned int num_speakers
, card
, device
;
4347 if (sscanf(name
, "plughw:%u,%u", &card
, &device
))
4348 sprintf(hwname
, "hw:%u,%u", card
, device
); /* must be hw rather than plughw to work */
4350 strcpy(hwname
, name
);
4352 num_speakers
= alsa_probe_num_speakers(hwname
);
4353 if (num_speakers
== 0)
4358 if (num_speakers
> 6)
4359 out
->ulVal
= KSAUDIO_SPEAKER_STEREO
;
4360 else if (num_speakers
== 6)
4361 out
->ulVal
= KSAUDIO_SPEAKER_5POINT1
;
4362 else if (num_speakers
>= 4)
4363 out
->ulVal
= KSAUDIO_SPEAKER_QUAD
;
4364 else if (num_speakers
>= 2)
4365 out
->ulVal
= KSAUDIO_SPEAKER_STEREO
;
4366 else if (num_speakers
== 1)
4367 out
->ulVal
= KSAUDIO_SPEAKER_MONO
;
4372 TRACE("Unimplemented property %s,%u\n", wine_dbgstr_guid(&prop
->fmtid
), prop
->pid
);